You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
12 KiB
C++
379 lines
12 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkClosedSurfacePointPlacer.cxx
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkClosedSurfacePointPlacer.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkPlane.h"
|
|
#include "vtkPlanes.h"
|
|
#include "vtkPlaneCollection.h"
|
|
#include "vtkRenderer.h"
|
|
#include "vtkInteractorObserver.h"
|
|
#include "vtkLine.h"
|
|
#include "vtkCamera.h"
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
vtkStandardNewMacro(vtkClosedSurfacePointPlacer);
|
|
vtkCxxSetObjectMacro(vtkClosedSurfacePointPlacer, BoundingPlanes,vtkPlaneCollection);
|
|
|
|
//----------------------------------------------------------------------
|
|
// Place holder structure to find the two planes that would best cut
|
|
// a line with a plane. We do this freaky stuff because we cannot use
|
|
// absolute tolerances. Sometimes a point may be intersected by two planes
|
|
// when it is on a corner etc... Believe me, I found this necessary.
|
|
//
|
|
// Plane : The plane that we found had intersected the line in question
|
|
// p : The intersection point of the line and the plane.
|
|
// Distance: Distance of the point "p" from the object. Negative distances
|
|
// mean that it is outside.
|
|
struct vtkClosedSurfacePointPlacerNode
|
|
{
|
|
typedef vtkClosedSurfacePointPlacerNode Self;
|
|
mutable vtkPlane * Plane;
|
|
double Distance;
|
|
double p[3];
|
|
static bool Sort( const Self &a, const Self &b )
|
|
{ return a.Distance > b.Distance; }
|
|
bool operator==(const Self &a) const { return a.Plane == this->Plane; }
|
|
bool operator!=(const Self &a) const { return a.Plane != this->Plane; }
|
|
vtkClosedSurfacePointPlacerNode()
|
|
{ Plane = NULL; Distance = VTK_DOUBLE_MIN; }
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
vtkClosedSurfacePointPlacer::vtkClosedSurfacePointPlacer()
|
|
{
|
|
this->BoundingPlanes = NULL;
|
|
this->MinimumDistance = 0.0;
|
|
this->InnerBoundingPlanes = vtkPlaneCollection::New();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
vtkClosedSurfacePointPlacer::~vtkClosedSurfacePointPlacer()
|
|
{
|
|
this->RemoveAllBoundingPlanes();
|
|
if (this->BoundingPlanes)
|
|
{
|
|
this->BoundingPlanes->UnRegister(this);
|
|
}
|
|
this->InnerBoundingPlanes->Delete();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void vtkClosedSurfacePointPlacer::AddBoundingPlane(vtkPlane *plane)
|
|
{
|
|
if (this->BoundingPlanes == NULL)
|
|
{
|
|
this->BoundingPlanes = vtkPlaneCollection::New();
|
|
this->BoundingPlanes->Register(this);
|
|
this->BoundingPlanes->Delete();
|
|
}
|
|
|
|
this->BoundingPlanes->AddItem(plane);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void vtkClosedSurfacePointPlacer::RemoveBoundingPlane(vtkPlane *plane)
|
|
{
|
|
if (this->BoundingPlanes )
|
|
{
|
|
this->BoundingPlanes->RemoveItem(plane);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void vtkClosedSurfacePointPlacer::RemoveAllBoundingPlanes()
|
|
{
|
|
if ( this->BoundingPlanes )
|
|
{
|
|
this->BoundingPlanes->RemoveAllItems();
|
|
this->BoundingPlanes->Delete();
|
|
this->BoundingPlanes = NULL;
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------
|
|
|
|
void vtkClosedSurfacePointPlacer::SetBoundingPlanes(vtkPlanes *planes)
|
|
{
|
|
if (!planes)
|
|
{
|
|
return;
|
|
}
|
|
|
|
vtkPlane *plane;
|
|
int numPlanes = planes->GetNumberOfPlanes();
|
|
|
|
this->RemoveAllBoundingPlanes();
|
|
for (int i=0; i<numPlanes ; i++)
|
|
{
|
|
plane = vtkPlane::New();
|
|
planes->GetPlane(i, plane);
|
|
this->AddBoundingPlane(plane);
|
|
plane->Delete();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void vtkClosedSurfacePointPlacer::BuildPlanes()
|
|
{
|
|
if (this->InnerBoundingPlanes->GetMTime() > this->GetMTime() &&
|
|
this->InnerBoundingPlanes->GetMTime() > this->BoundingPlanes->GetMTime())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Need to build planes.. Bring them all in front by MinimumDistance.
|
|
// Find the Inner bounding planes.
|
|
|
|
this->InnerBoundingPlanes->RemoveAllItems();
|
|
|
|
double normal[3], origin[3];
|
|
vtkPlane *p;
|
|
for (this->BoundingPlanes->InitTraversal();
|
|
(p = this->BoundingPlanes->GetNextItem()); )
|
|
{
|
|
p->GetNormal(normal);
|
|
p->GetOrigin(origin);
|
|
for (int i = 0; i<3; i++)
|
|
{
|
|
origin[i] += this->MinimumDistance * normal[i];
|
|
}
|
|
|
|
vtkPlane * plane = vtkPlane::New();
|
|
plane->SetOrigin(origin);
|
|
plane->SetNormal(normal);
|
|
this->InnerBoundingPlanes->AddItem(plane);
|
|
plane->Delete();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Given a renderer, a display position and a reference position, "worldPos"
|
|
// is calculated as :
|
|
// Consider the line "L" that passes through the supplied "displayPos" and
|
|
// is parallel to the direction of projection of the camera. Clip this line
|
|
// segment with the parallelopiped, let's call it "L_segment". The computed
|
|
// world position, "worldPos" will be the point on "L_segment" that is closest
|
|
// to refWorldPos.
|
|
int vtkClosedSurfacePointPlacer::ComputeWorldPosition(
|
|
vtkRenderer * ren,
|
|
double displayPos[2],
|
|
double refWorldPos[3],
|
|
double worldPos[3],
|
|
double * vtkNotUsed(worldOrient) )
|
|
{
|
|
this->BuildPlanes();
|
|
|
|
if (!this->BoundingPlanes)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
double directionOfProjection[3], t, d[3],
|
|
currentWorldPos[4], ls[2][3], fp[4];
|
|
|
|
vtkInteractorObserver::ComputeWorldToDisplay( ren,
|
|
refWorldPos[0], refWorldPos[1], refWorldPos[2], fp );
|
|
|
|
ren->GetActiveCamera()->
|
|
GetDirectionOfProjection(directionOfProjection);
|
|
vtkInteractorObserver::ComputeDisplayToWorld( ren,
|
|
displayPos[0], displayPos[1], fp[2], currentWorldPos);
|
|
|
|
// The line "L" defined by two points, l0 and l1. The line-segment
|
|
// end-points will be defined by points ls[2][3].
|
|
double l0[3] = {currentWorldPos[0] - directionOfProjection[0],
|
|
currentWorldPos[1] - directionOfProjection[1],
|
|
currentWorldPos[2] - directionOfProjection[2] };
|
|
double l1[3] = {currentWorldPos[0] + directionOfProjection[0],
|
|
currentWorldPos[1] + directionOfProjection[1],
|
|
currentWorldPos[2] + directionOfProjection[2] };
|
|
|
|
// Traverse all the planes to clip the line.
|
|
|
|
vtkPlaneCollection *pc = this->InnerBoundingPlanes;
|
|
|
|
// Stores candidate intersections with the parallelopiped. This was found
|
|
// necessary instead of a simple two point intersection test because of
|
|
// tolerances in vtkPlane::EvaluatePosition when the handle was very close
|
|
// to an edge.
|
|
std::vector< vtkClosedSurfacePointPlacerNode > intersections;
|
|
|
|
const int nPlanes = pc->GetNumberOfItems();
|
|
|
|
// Loop over each plane.
|
|
for ( int n = 0; n < nPlanes; n++ )
|
|
{
|
|
vtkPlane * plane = static_cast< vtkPlane * >(pc->GetItemAsObject(n));
|
|
vtkClosedSurfacePointPlacerNode node;
|
|
|
|
vtkPlane::IntersectWithLine( l0, l1,
|
|
plane->GetNormal(), plane->GetOrigin(), t, node.p );
|
|
|
|
// The IF below insures that the line and the plane aren't parallel.
|
|
if (t != VTK_DOUBLE_MAX)
|
|
{
|
|
node.Plane = plane;
|
|
node.Distance = this->GetDistanceFromObject(
|
|
node.p, this->InnerBoundingPlanes, d);
|
|
intersections.push_back(node);
|
|
vtkDebugMacro( << "We aren't parallel to plane with normal: ("
|
|
<< plane->GetNormal()[0] << "," << plane->GetNormal()[1] << ","
|
|
<< plane->GetNormal()[2] << ")" );
|
|
vtkDebugMacro( << "Size of inersections = " << intersections.size()
|
|
<< " Distance: " << node.Distance << " Plane: " << plane );
|
|
}
|
|
}
|
|
|
|
std::sort( intersections.begin(),
|
|
intersections.end(),
|
|
vtkClosedSurfacePointPlacerNode::Sort);
|
|
|
|
// Now pick the top two candidates, insuring that the line at least intersects
|
|
// with the object. If we have fewer than 2 in the queue, or if the
|
|
// top candidate is outsude, we have failed to intersect the object.
|
|
|
|
std::vector< vtkClosedSurfacePointPlacerNode >
|
|
::const_iterator it = intersections.begin();
|
|
if ( intersections.size() < 2 ||
|
|
it ->Distance < (-1.0 * this->WorldTolerance) ||
|
|
(++it)->Distance < (-1.0 * this->WorldTolerance))
|
|
{
|
|
// The display point points to a location outside the object. Just
|
|
// return 0. In actuality, I'd like to return the closest point in the
|
|
// object. For this I require an algorithm that can, given a point "p" and
|
|
// an object "O", defined by a set of bounding planes, find the point on
|
|
// "O" that is closest to "p"
|
|
|
|
return 0;
|
|
}
|
|
|
|
it = intersections.begin();
|
|
for (int i = 0; i < 2; i++, ++it)
|
|
{
|
|
ls[i][0] = it->p[0];
|
|
ls[i][1] = it->p[1];
|
|
ls[i][2] = it->p[2];
|
|
}
|
|
|
|
vtkLine::DistanceToLine( refWorldPos, ls[0], ls[1], t, worldPos );
|
|
t = (t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t));
|
|
|
|
// the point "worldPos", now lies within the object and on the line from
|
|
// the eye along the direction of projection.
|
|
|
|
worldPos[0] = ls[0][0] * (1.0-t) + ls[1][0] * t;
|
|
worldPos[1] = ls[0][1] * (1.0-t) + ls[1][1] * t;
|
|
worldPos[2] = ls[0][2] * (1.0-t) + ls[1][2] * t;
|
|
|
|
vtkDebugMacro( << "Reference Pos: (" << refWorldPos[0] << ","
|
|
<< refWorldPos[1] << "," << refWorldPos[2] << ") Line segment from "
|
|
<< "the eye along the direction of projection, clipped by the object [("
|
|
<< ls[0][0] << "," << ls[0][1] << "," << ls[0][2] << ") - (" << ls[1][0]
|
|
<< "," << ls[1][1] << "," << ls[1][2] << ")] Computed position (that is "
|
|
<< "the closest point on this segment to ReferencePos: (" << worldPos[0]
|
|
<< "," << worldPos[1] << "," << worldPos[2] << ")" );
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int vtkClosedSurfacePointPlacer
|
|
::ComputeWorldPosition( vtkRenderer *,
|
|
double vtkNotUsed(displayPos)[2],
|
|
double vtkNotUsed(worldPos)[3],
|
|
double vtkNotUsed(worldOrient)[9] )
|
|
{
|
|
vtkErrorMacro( << "This placer needs a reference world position.");
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int vtkClosedSurfacePointPlacer::ValidateWorldPosition( double worldPos[3],
|
|
double* vtkNotUsed(worldOrient) )
|
|
{
|
|
return this->ValidateWorldPosition( worldPos );
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int vtkClosedSurfacePointPlacer::ValidateWorldPosition( double worldPos[3] )
|
|
{
|
|
this->BuildPlanes();
|
|
|
|
// Now check against the bounding planes
|
|
if ( this->InnerBoundingPlanes )
|
|
{
|
|
vtkPlane *p;
|
|
|
|
this->InnerBoundingPlanes->InitTraversal();
|
|
|
|
while ( (p = this->InnerBoundingPlanes->GetNextItem()) )
|
|
{
|
|
if ( p->EvaluateFunction( worldPos ) < this->WorldTolerance )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Calculate the distance of a point from the Object. Negative
|
|
// values imply that the point is outside. Positive values imply that it is
|
|
// inside. The closest point to the object is returned in closestPt.
|
|
double vtkClosedSurfacePointPlacer
|
|
::GetDistanceFromObject( double pos[3],
|
|
vtkPlaneCollection * pc,
|
|
double closestPt[3])
|
|
{
|
|
vtkPlane *minPlane = NULL;
|
|
double minD = VTK_DOUBLE_MAX;
|
|
|
|
pc->InitTraversal();
|
|
while ( vtkPlane * p = pc->GetNextItem() )
|
|
{
|
|
const double d = p->EvaluateFunction( pos );
|
|
if (d < minD)
|
|
{
|
|
minD = d;
|
|
minPlane = p;
|
|
}
|
|
}
|
|
|
|
vtkPlane::ProjectPoint( pos, minPlane->GetOrigin(),
|
|
minPlane->GetNormal(), closestPt );
|
|
return minD;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void vtkClosedSurfacePointPlacer::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
os << indent << "Bounding Planes:\n";
|
|
if ( this->BoundingPlanes )
|
|
{
|
|
this->BoundingPlanes->PrintSelf(os,indent.GetNextIndent());
|
|
}
|
|
else
|
|
{
|
|
os << " (none)\n";
|
|
}
|
|
|
|
os << indent << "Minimum Distance: " << this->MinimumDistance << "\n";
|
|
}
|