Cloned library of VTK-5.0.0 with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1933 lines
55 KiB

/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkOBBTree.cxx,v $
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 "vtkOBBTree.h"
#include "vtkCellArray.h"
#include "vtkGenericCell.h"
#include "vtkLine.h"
#include "vtkMath.h"
#include "vtkMatrix4x4.h"
#include "vtkObjectFactory.h"
#include "vtkPlane.h"
#include "vtkPolyData.h"
#include "vtkPolygon.h"
#include "vtkTriangle.h"
#include "vtkUnstructuredGrid.h"
vtkCxxRevisionMacro(vtkOBBTree, "$Revision: 1.63.14.1 $");
vtkStandardNewMacro(vtkOBBTree);
#define vtkCELLTRIANGLES(CELLPTIDS, TYPE, IDX, PTID0, PTID1, PTID2) \
{ switch( TYPE ) \
{ \
case VTK_TRIANGLE: \
case VTK_POLYGON: \
case VTK_QUAD: \
PTID0 = CELLPTIDS[0]; \
PTID1 = CELLPTIDS[(IDX)+1]; \
PTID2 = CELLPTIDS[(IDX)+2]; \
break; \
case VTK_TRIANGLE_STRIP: \
PTID0 = CELLPTIDS[IDX]; \
PTID1 = CELLPTIDS[(IDX)+1+((IDX)&1)]; \
PTID2 = CELLPTIDS[(IDX)+2-((IDX)&1)]; \
break; \
default: \
PTID0 = PTID1 = PTID2 = -1; \
} }
vtkOBBNode::vtkOBBNode()
{
this->Cells = NULL;
this->Parent = NULL;
this->Kids = NULL;
}
vtkOBBNode::~vtkOBBNode()
{
if (this->Kids)
{
delete [] this->Kids;
}
if (this->Cells)
{
this->Cells->Delete();
}
}
// Construct with automatic computation of divisions, averaging
// 25 cells per octant.
vtkOBBTree::vtkOBBTree()
{
this->DataSet = NULL;
this->Level = 4;
this->MaxLevel = 12;
this->Automatic = 1;
this->Tolerance = 0.01;
this->Tree = NULL;
this->PointsList = NULL;
this->InsertedPoints = NULL;
this->OBBCount = this->DeepestLevel = 0;
}
vtkOBBTree::~vtkOBBTree()
{
this->FreeSearchStructure();
}
void vtkOBBTree::FreeSearchStructure()
{
if ( this->Tree )
{
this->DeleteTree(this->Tree);
delete this->Tree;
this->Tree = NULL;
}
}
void vtkOBBTree::DeleteTree(vtkOBBNode *OBBptr)
{
if ( OBBptr->Kids != NULL )
{
this->DeleteTree(OBBptr->Kids[0]);
this->DeleteTree(OBBptr->Kids[1]);
delete OBBptr->Kids[0];
delete OBBptr->Kids[1];
}
}
// Compute an OBB from the list of points given. Return the corner point
// and the three axes defining the orientation of the OBB. Also return
// a sorted list of relative "sizes" of axes for comparison purposes.
void vtkOBBTree::ComputeOBB(vtkPoints *pts, double corner[3], double max[3],
double mid[3], double min[3], double size[3])
{
int i;
vtkIdType numPts, pointId;
double x[3], mean[3], xp[3], *v[3], v0[3], v1[3], v2[3];
double *a[3], a0[3], a1[3], a2[3];
double tMin[3], tMax[3], closest[3], t;
//
// Compute mean
//
numPts = pts->GetNumberOfPoints();
mean[0] = mean[1] = mean[2] = 0.0;
for (pointId=0; pointId < numPts; pointId++ )
{
pts->GetPoint(pointId, x);
for (i=0; i < 3; i++)
{
mean[i] += x[i];
}
}
for (i=0; i < 3; i++)
{
mean[i] /= numPts;
}
//
// Compute covariance matrix
//
a[0] = a0; a[1] = a1; a[2] = a2;
for (i=0; i < 3; i++)
{
a0[i] = a1[i] = a2[i] = 0.0;
}
for (pointId=0; pointId < numPts; pointId++ )
{
pts->GetPoint(pointId, x);
xp[0] = x[0] - mean[0]; xp[1] = x[1] - mean[1]; xp[2] = x[2] - mean[2];
for (i=0; i < 3; i++)
{
a0[i] += xp[0] * xp[i];
a1[i] += xp[1] * xp[i];
a2[i] += xp[2] * xp[i];
}
}//for all points
for (i=0; i < 3; i++)
{
a0[i] /= numPts;
a1[i] /= numPts;
a2[i] /= numPts;
}
//
// Extract axes (i.e., eigenvectors) from covariance matrix.
//
v[0] = v0; v[1] = v1; v[2] = v2;
vtkMath::Jacobi(a,size,v);
max[0] = v[0][0]; max[1] = v[1][0]; max[2] = v[2][0];
mid[0] = v[0][1]; mid[1] = v[1][1]; mid[2] = v[2][1];
min[0] = v[0][2]; min[1] = v[1][2]; min[2] = v[2][2];
for (i=0; i < 3; i++)
{
a[0][i] = mean[i] + max[i];
a[1][i] = mean[i] + mid[i];
a[2][i] = mean[i] + min[i];
}
//
// Create oriented bounding box by projecting points onto eigenvectors.
//
tMin[0] = tMin[1] = tMin[2] = VTK_DOUBLE_MAX;
tMax[0] = tMax[1] = tMax[2] = -VTK_DOUBLE_MAX;
for (pointId=0; pointId < numPts; pointId++ )
{
pts->GetPoint(pointId, x);
for (i=0; i < 3; i++)
{
vtkLine::DistanceToLine(x, mean, a[i], t, closest);
if ( t < tMin[i] )
{
tMin[i] = t;
}
if ( t > tMax[i] )
{
tMax[i] = t;
}
}
}//for all points
for (i=0; i < 3; i++)
{
corner[i] = mean[i] + tMin[0]*max[i] + tMin[1]*mid[i] + tMin[2]*min[i];
max[i] = (tMax[0] - tMin[0]) * max[i];
mid[i] = (tMax[1] - tMin[1]) * mid[i];
min[i] = (tMax[2] - tMin[2]) * min[i];
}
}
// a method to compute the OBB of a dataset without having to go through the
// Execute method; It does set
void vtkOBBTree::ComputeOBB(vtkDataSet *input, double corner[3], double max[3],
double mid[3], double min[3], double size[3])
{
vtkIdType numPts, numCells, i;
vtkIdList *cellList;
vtkDataSet *origDataSet;
vtkDebugMacro(<<"Computing OBB");
if ( input == NULL || (numPts = input->GetNumberOfPoints()) < 1 ||
(input->GetNumberOfCells()) < 1 )
{
vtkErrorMacro(<<"Can't compute OBB - no data available!");
return;
}
numCells = input->GetNumberOfCells();
// save previous value of DataSet and reset after calling ComputeOBB because
// computeOBB used this->DataSet internally
origDataSet = this->DataSet;
this->DataSet = input;
// these are other member variables that ComputeOBB requires
this->OBBCount = 0;
this->InsertedPoints = new int[numPts];
for (i=0; i < numPts; i++)
{
this->InsertedPoints[i] = 0;
}
this->PointsList = vtkPoints::New();
this->PointsList->Allocate(numPts);
cellList = vtkIdList::New();
cellList->Allocate(numCells);
for (i=0; i < numCells; i++)
{
cellList->InsertId(i,i);
}
this->ComputeOBB(cellList, corner, max, mid, min, size);
this->DataSet = origDataSet;
delete [] this->InsertedPoints;
this->PointsList->Delete();
cellList->Delete();
}
// Compute an OBB from the list of cells given. Return the corner point
// and the three axes defining the orientation of the OBB. Also return
// a sorted list of relative "sizes" of axes for comparison purposes.
void vtkOBBTree::ComputeOBB(vtkIdList *cells, double corner[3], double max[3],
double mid[3], double min[3], double size[3])
{
vtkIdType numCells, i, j, cellId, ptId, pId, qId, rId;
int k, type;
vtkIdType numPts = 0;
vtkIdType *ptIds = 0;
double p[3], q[3], r[3], mean[3], xp[3], *v[3], v0[3], v1[3], v2[3];
double *a[3], a0[3], a1[3], a2[3];
double tMin[3], tMax[3], closest[3], t;
double dp0[3], dp1[3], tri_mass, tot_mass, c[3];
this->OBBCount++;
this->PointsList->Reset();
//
// Compute mean & moments
//
numCells = cells->GetNumberOfIds();
mean[0] = mean[1] = mean[2] = 0.0;
tot_mass = 0.0;
a[0] = a0; a[1] = a1; a[2] = a2;
for ( i=0; i<3; i++ )
{
a0[i] = a1[i] = a2[i] = 0.0;
}
for ( i=0; i < numCells; i++ )
{
cellId = cells->GetId( i );
type = this->DataSet->GetCellType( cellId );
switch (this->DataSet->GetDataObjectType())
{
case VTK_POLY_DATA:
((vtkPolyData *)this->DataSet)->GetCellPoints( cellId, numPts, ptIds );
break;
case VTK_UNSTRUCTURED_GRID:
((vtkUnstructuredGrid *)this->DataSet)->GetCellPoints( cellId, numPts, ptIds );
break;
default:
vtkErrorMacro( <<"DataSet " << this->DataSet->GetClassName() <<
" not supported." );
break;
}
for ( j=0; j<numPts-2; j++ )
{
vtkCELLTRIANGLES( ptIds, type, j, pId, qId, rId );
if ( pId < 0 )
{
continue;
}
this->DataSet->GetPoint(pId, p);
this->DataSet->GetPoint(qId, q);
this->DataSet->GetPoint(rId, r);
// p, q, and r are the oriented triangle points.
// Compute the components of the moment of inertia tensor.
for ( k=0; k<3; k++ )
{
// two edge vectors
dp0[k] = q[k] - p[k];
dp1[k] = r[k] - p[k];
// centroid
c[k] = (p[k] + q[k] + r[k])/3;
}
vtkMath::Cross( dp0, dp1, xp );
tri_mass = 0.5*vtkMath::Norm( xp );
tot_mass += tri_mass;
for ( k=0; k<3; k++ )
{
mean[k] += tri_mass*c[k];
}
// on-diagonal terms
a0[0] += tri_mass*(9*c[0]*c[0] + p[0]*p[0] + q[0]*q[0] + r[0]*r[0])/12;
a1[1] += tri_mass*(9*c[1]*c[1] + p[1]*p[1] + q[1]*q[1] + r[1]*r[1])/12;
a2[2] += tri_mass*(9*c[2]*c[2] + p[2]*p[2] + q[2]*q[2] + r[2]*r[2])/12;
// off-diagonal terms
a0[1] += tri_mass*(9*c[0]*c[1] + p[0]*p[1] + q[0]*q[1] + r[0]*r[1])/12;
a0[2] += tri_mass*(9*c[0]*c[2] + p[0]*p[2] + q[0]*q[2] + r[0]*r[2])/12;
a1[2] += tri_mass*(9*c[1]*c[2] + p[1]*p[2] + q[1]*q[2] + r[1]*r[2])/12;
} // end foreach triangle
// While computing cell moments, gather all the cell's
// point coordinates into a single list.
//
for ( j=0; j < numPts; j++ )
{
if ( this->InsertedPoints[ptIds[j]] != this->OBBCount )
{
this->InsertedPoints[ptIds[j]] = this->OBBCount;
this->PointsList->InsertNextPoint(this->DataSet->GetPoint(ptIds[j]));
}
}//for all points of this cell
} // end foreach cell
// normalize data
for ( i=0; i<3; i++ )
{
mean[i] = mean[i]/tot_mass;
}
// matrix is symmetric
a1[0] = a0[1];
a2[0] = a0[2];
a2[1] = a1[2];
// get covariance from moments
for ( i=0; i<3; i++ )
{
for ( j=0; j<3; j++ )
{
a[i][j] = a[i][j]/tot_mass - mean[i]*mean[j];
}
}
//
// Extract axes (i.e., eigenvectors) from covariance matrix.
//
v[0] = v0; v[1] = v1; v[2] = v2;
vtkMath::Jacobi(a,size,v);
max[0] = v[0][0]; max[1] = v[1][0]; max[2] = v[2][0];
mid[0] = v[0][1]; mid[1] = v[1][1]; mid[2] = v[2][1];
min[0] = v[0][2]; min[1] = v[1][2]; min[2] = v[2][2];
for (i=0; i < 3; i++)
{
a[0][i] = mean[i] + max[i];
a[1][i] = mean[i] + mid[i];
a[2][i] = mean[i] + min[i];
}
//
// Create oriented bounding box by projecting points onto eigenvectors.
//
tMin[0] = tMin[1] = tMin[2] = VTK_DOUBLE_MAX;
tMax[0] = tMax[1] = tMax[2] = -VTK_DOUBLE_MAX;
numPts = this->PointsList->GetNumberOfPoints();
for (ptId=0; ptId < numPts; ptId++ )
{
this->PointsList->GetPoint(ptId, p);
for (i=0; i < 3; i++)
{
vtkLine::DistanceToLine(p, mean, a[i], t, closest);
if ( t < tMin[i] )
{
tMin[i] = t;
}
if ( t > tMax[i] )
{
tMax[i] = t;
}
}
}//for all points
for (i=0; i < 3; i++)
{
corner[i] = mean[i] + tMin[0]*max[i] + tMin[1]*mid[i] + tMin[2]*min[i];
max[i] = (tMax[0] - tMin[0]) * max[i];
mid[i] = (tMax[1] - tMin[1]) * mid[i];
min[i] = (tMax[2] - tMin[2]) * min[i];
}
}
// Efficient check for whether a line p1,p2 intersects with triangle
// pt1,pt2,pt3 to within specified tolerance. This is included here
// because vtkTriangle doesn't have an equivalently efficient method.
// The intersection point is returned, along with the parametric
// coordinate t and the sense of the intersection (+1 if entering
// or -1 if exiting, according to normal of triangle)
// The function return value is 1 if an intersection was found.
static inline
int vtkOBBTreeLineIntersectsTriangle(double p1[3], double p2[3],
double pt1[3], double pt2[3], double pt3[3],
double tolerance, double point[3],
double &t, int &sense)
{
double normal[3];
vtkTriangle::ComputeNormal(pt1, pt2, pt3, normal);
// vector from p1 to p2
double v12[3];
v12[0] = p2[0] - p1[0];
v12[1] = p2[1] - p1[1];
v12[2] = p2[2] - p1[2];
// vector from p1 to triangle
double v1t[3];
v1t[0] = pt1[0] - p1[0];
v1t[1] = pt1[1] - p1[1];
v1t[2] = pt1[2] - p1[2];
// compute numerator/denominator of parametric distance
double numerator = vtkMath::Dot(normal, v1t);
double denominator = vtkMath::Dot(normal, v12);
if (denominator == 0)
{
return 0;
}
// If denominator less than the tolerance, then the
// line and plane are considered parallel.
double fabsden = denominator;
sense = -1;
if (fabsden < 0.0)
{
sense = 1;
fabsden = -fabsden;
}
if (fabsden > 1e-6 + tolerance)
{
// calculate the distance to the intersection along the line
t = numerator/denominator;
if (t < 0.0 || t > 1.0)
{
return 0;
}
// intersection point
point[0] = p1[0] + t*v12[0];
point[1] = p1[1] + t*v12[1];
point[2] = p1[2] + t*v12[2];
// find axis permutation to allow us to do the rest of the
// math in 2D (much more efficient than doing the math in 3D)
int xi = 0, yi = 1, zi = 2;
if (normal[0]*normal[0] < normal[1]*normal[1])
{
xi = 1; yi = 2; zi = 0;
}
if (normal[xi]*normal[xi] < normal[2]*normal[2])
{
xi = 2; yi = 0; zi = 1;
}
// calculate vector from triangle corner to point
double u0 = point[yi] - pt1[yi];
double v0 = point[zi] - pt1[zi];
// calculate edge vectors for triangle
double u1 = pt2[yi] - pt1[yi];
double v1 = pt2[zi] - pt1[zi];
double u2 = pt3[yi] - pt1[yi];
double v2 = pt3[zi] - pt1[zi];
// area of projected triangle (multiplied by 2) via cross product
double area = (v2*u1 - u2*v1);
// sub-areas that must sum to less than the total area
double alpha = (v2*u0 - u2*v0);
double beta = (v0*u1 - u0*v1);
double gamma = area - alpha - beta;
if (area < 0)
{
area = -area;
alpha = -alpha;
beta = -beta;
gamma = -gamma;
}
if (alpha > 0 && beta > 0 && gamma > 0)
{ // inside of polygon
return 1;
}
}
// if zero tolerance, nothing more that we can do!
if (tolerance == 0)
{
return 0;
}
// check the edges of the triangle (because triangles share edges,
// this check should be identical for adjacent triangles which is
// a 'good thing'
double tolsquared = tolerance*tolerance;
// make sure that order of points in each line segment is the
// same for faces pointed in opposite directions
double *tpoints[4];
if (sense > 0)
{
tpoints[0] = pt1;
tpoints[1] = pt2;
tpoints[2] = pt3;
tpoints[3] = pt1;
}
else
{
tpoints[0] = pt3;
tpoints[1] = pt2;
tpoints[2] = pt1;
tpoints[3] = pt3;
}
double vec[3];
double v;
for (int i = 0; i < 3; i++)
{
pt1 = tpoints[i];
pt2 = tpoints[i+1];
if (vtkLine::Intersection(p1,p2, pt1,pt2, t,v) == 2)
{
vec[0] = (p1[0] + v12[0]*t) - (pt1[0] + (pt2[0] - pt1[0])*v);
vec[1] = (p1[1] + v12[1]*t) - (pt1[1] + (pt2[1] - pt1[1])*v);
vec[2] = (p1[2] + v12[2]*t) - (pt1[2] + (pt2[2] - pt1[2])*v);
if (vtkMath::Dot(vec,vec) < tolsquared)
{
return 1;
}
}
}
return 0;
}
// just check whether a point lies inside or outside the DataSet,
// assuming that the data is a closed vtkPolyData surface.
int vtkOBBTree::InsideOrOutside(const double point[3])
{
// no points!
// shoot a ray that is guaranteed to hit one of the cells and use
// that as our inside/outside check
vtkIdType numCells = this->DataSet->GetNumberOfCells();
for (vtkIdType i = 0; i < numCells; i++)
{
vtkIdType numPts;
vtkIdType *ptIds;
int cellType = this->DataSet->GetCellType(i);
((vtkPolyData *)this->DataSet)->GetCellPoints(i, numPts, ptIds);
// break the cell into triangles
for (vtkIdType j = 0; j < numPts-2; j++)
{
vtkIdType pt1Id, pt2Id, pt3Id;
vtkCELLTRIANGLES(ptIds, cellType, j, pt1Id, pt2Id, pt3Id);
if (pt1Id < 0)
{ // cell wasn't a polygon, triangle, quad, or triangle strip
continue;
}
// create a point that is guaranteed to be inside the cell
double pt1[3], pt2[3], pt3[3];
this->DataSet->GetPoint(pt1Id, pt1);
this->DataSet->GetPoint(pt2Id, pt2);
this->DataSet->GetPoint(pt3Id, pt3);
double x[3];
x[0] = (pt1[0] + pt2[0] + pt3[0])/3;
x[1] = (pt1[1] + pt2[1] + pt3[1])/3;
x[2] = (pt1[2] + pt2[2] + pt3[2])/3;
// make a line guaranteed to pass through the cell's first triangle
x[0] += x[0] - point[0];
x[1] += x[1] - point[1];
x[2] += x[2] - point[2];
// calculate vector
double v12[3];
v12[0] = x[0] - point[0];
v12[1] = x[1] - point[1];
v12[2] = x[2] - point[2];
// get triangle normal, we need a triangle for whose face is
// not parallel to the line
double normal[3];
vtkTriangle::ComputeNormal(pt1, pt2, pt3, normal);
double dotProd = vtkMath::Dot(normal, v12);
if (dotProd < 0)
{
dotProd = -dotProd;
}
if (dotProd >= this->Tolerance + 1e-6)
{
return this->IntersectWithLine(point, x, NULL, NULL);
}
// otherwise go on to next triangle
}
}
return 0;
}
// Take the passed line segment and intersect it with the OBB cells.
// This method assumes that the data set is a vtkPolyData that describes
// a closed surface, and the intersection points that are returned in
// 'points' alternate between entrance points and exit points.
// The return value of the function is 0 if no intersection was found,
// 1 if point 'p1' lies inside the polydata surface, or -1 if point 'p1'
// lies outside the polydata surface.
int vtkOBBTree::IntersectWithLine(const double p1[3], const double p2[3],
vtkPoints *points, vtkIdList *cellIds)
{
if (this->DataSet == NULL)
{
if (points)
{
points->SetNumberOfPoints(0);
}
if (cellIds)
{
cellIds->SetNumberOfIds(0);
}
return 0;
}
if (!this->DataSet->IsA("vtkPolyData"))
{
vtkErrorMacro("IntersectWithLine: this method requires a vtkPolyData");
return 0;
}
int rval = 0; // return value for function
vtkIdList *cells;
// temporary list used to sort intersections
int listSize = 0;
int listMaxSize = 10;
double *distanceList = new double[listMaxSize];
vtkIdType *cellList = new vtkIdType[listMaxSize];
char *senseList = new char[listMaxSize];
double point[3];
double distance = 0;
int sense = 0;
vtkIdType cellId;
// compute line vector from p1 to p2
double v12[3];
v12[0] = p2[0] - p1[0];
v12[1] = p2[1] - p1[1];
v12[2] = p2[2] - p1[2];
vtkOBBNode **OBBstack = new vtkOBBNode *[this->GetLevel()+1];
OBBstack[0] = this->Tree;
// depth counter for stack
int depth = 1;
while(depth > 0)
{ // simulate recursion without the overhead or limitations
vtkOBBNode *node = OBBstack[--depth];
// check for intersection with node
if (this->LineIntersectsNode(node, (double *)p1, (double *)p2))
{
if (node->Kids == NULL)
{ // then this is a leaf node...get Cells
cells = node->Cells;
vtkIdType numCells = cells->GetNumberOfIds();
for (vtkIdType i = 0; i < numCells; i++)
{
// get the current cell
cellId = cells->GetId(i);
int cellType = this->DataSet->GetCellType(cellId);
vtkIdType numPts;
vtkIdType *ptIds;
((vtkPolyData *)this->DataSet)->GetCellPoints(cellId, numPts, ptIds);
// break the cell into triangles
for (vtkIdType j = 0; j < numPts-2; j++)
{
vtkIdType pt1Id, pt2Id, pt3Id;
vtkCELLTRIANGLES(ptIds, cellType, j, pt1Id, pt2Id, pt3Id);
if (pt1Id < 0)
{ // cell wasn't a polygon, triangle, quad, or triangle strip
continue;
}
// get the points for this triangle
double pt1[3], pt2[3], pt3[3];
this->DataSet->GetPoint(pt1Id, pt1);
this->DataSet->GetPoint(pt2Id, pt2);
this->DataSet->GetPoint(pt3Id, pt3);
if (vtkOBBTreeLineIntersectsTriangle((double *)p1, (double *)p2,
pt1, pt2, pt3,
this->Tolerance, point,
distance, sense) <= 0)
{ // no intersection with triangle
continue;
}
// we made it! we have a hit!
if (listSize >= listMaxSize)
{ // have to grow the distanceList
listMaxSize *= 2;
double *tmpDistanceList = new double[listMaxSize];
vtkIdType *tmpCellList = new vtkIdType[listMaxSize];
char *tmpSenseList = new char[listMaxSize];
for (int k = 0; k < listSize; k++)
{
tmpDistanceList[k] = distanceList[k];
tmpCellList[k] = cellList[k];
tmpSenseList[k] = senseList[k];
}
delete [] distanceList;
distanceList = tmpDistanceList;
delete [] cellList;
cellList = tmpCellList;
delete [] senseList;
senseList = tmpSenseList;
}
// store in the distanceList
distanceList[listSize] = distance;
cellList[listSize] = cellId;
senseList[listSize++] = sense;
// if cell is planar (i.e. not a triangle strip) then proceed
// immediately to the next cell, otherwise go to next triangle
if (cellType != VTK_TRIANGLE_STRIP)
{
break;
}
}
}
}
else
{ // push kids onto stack
OBBstack[depth] = node->Kids[0];
OBBstack[depth+1] = node->Kids[1];
depth += 2;
}
}
} // end while
if (listSize != 0)
{
// Look at the distanceList and return the intersection point
// sorted according to their distance from p1.
if (points)
{
points->SetNumberOfPoints(listSize);
}
if (cellIds)
{
cellIds->SetNumberOfIds(0);
}
double ptol = this->Tolerance/sqrt(vtkMath::Dot(v12,v12));
double lastDistance = 0.0;
int lastSense = 0;
int nPoints = 0;
int listRemainder = listSize;
while (listRemainder)
{
int minIdx = 0;
for (int j = 1; j < listRemainder; j++)
{ // check for closest intersection of the correct sense
if (senseList[j] != lastSense &&
distanceList[j] < distanceList[minIdx])
{
minIdx = j;
}
}
distance = distanceList[minIdx];
cellId = cellList[minIdx];
sense = senseList[minIdx];
listRemainder--;
distanceList[minIdx] = distanceList[listRemainder];
cellList[minIdx] = cellList[listRemainder];
senseList[minIdx] = senseList[listRemainder];
// only use point if it moves us forward,
// or it moves us backward by less than tol
if (distance > lastDistance - ptol && sense != lastSense)
{
if (points)
{
point[0] = p1[0] + distance*v12[0];
point[1] = p1[1] + distance*v12[1];
point[2] = p1[2] + distance*v12[2];
points->SetPoint(nPoints, point);
}
if (cellIds)
{
cellIds->InsertNextId(cellId);
}
nPoints++;
// set return value according to sense of first intersection
if (rval == 0)
{
rval = sense;
}
// remember the last point
lastDistance = distance;
lastSense = sense;
}
}
// shrink points array if not all points were used
if (nPoints < listSize)
{
if (points)
{
points->GetData()->Resize(nPoints);
}
}
// done!
}
else
{
if (points)
{
points->SetNumberOfPoints(0);
}
if (cellIds)
{
cellIds->SetNumberOfIds(0);
}
}
delete [] senseList;
delete [] cellList;
delete [] distanceList;
delete [] OBBstack;
// return 1 if p1 is inside, 0 is p1 is outside
return rval;
}
// Return intersection point of line defined by two points (a0,a1) in dataset
// coordinate system; returning cellId (or -1 if no intersection). The
// argument list returns the intersection parametric coordinate, t, along
// the line; the coordinate of intersection, x[3]; the cell parametric
// coordinates, pcoords[3]; and subId of the cell. (Not yet implemented.)
int vtkOBBTree::IntersectWithLine(double a0[3], double a1[3], double tol,
double& t, double x[3], double pcoords[3],
int &subId)
{
vtkIdType cellId = -1;
return this->IntersectWithLine( a0, a1, tol, t, x, pcoords,
subId, cellId );
}
// Return intersection point of line defined by two points (a0,a1) in dataset
// coordinate system; returning cellId (or -1 if no intersection). The
// argument list returns the intersection parametric coordinate, t, along
// the line; the coordinate of intersection, x[3]; the cell parametric
// coordinates, pcoords[3]; and subId of the cell. (Not yet implemented.)
int vtkOBBTree::IntersectWithLine(double a0[3], double a1[3], double tol,
double& t, double x[3], double pcoords[3],
int &subId, vtkIdType &cellId)
{
vtkGenericCell *cell=vtkGenericCell::New();
int returnVal;
returnVal = this->IntersectWithLine( a0, a1, tol, t, x, pcoords, subId,
cellId, cell);
cell->Delete();
return returnVal;
}
// Return intersection point (if any) AND the cell which was intersected by
// finite line
int vtkOBBTree::IntersectWithLine(double a0[3], double a1[3], double tol,
double& t, double x[3], double pcoords[3],
int &subId, vtkIdType &cellId,
vtkGenericCell *cell)
{
vtkOBBNode **OBBstack, *node;
vtkIdList *cells;
int depth, ii, foundIntersection = 0, bestIntersection = 0;
double tBest = VTK_DOUBLE_MAX, xBest[3], pcoordsBest[3];
int subIdBest = -1;
vtkIdType thisId, cellIdBest = -1;
OBBstack = new vtkOBBNode *[this->GetLevel()+1];
OBBstack[0] = this->Tree;
depth = 1;
while( depth > 0 )
{ // simulate recursion without the overhead or limitations
depth--;
node = OBBstack[depth];
if ( this->LineIntersectsNode( node, a0, a1 ) )
{
if ( node->Kids == NULL )
{ // then this is a leaf node...get Cells
cells = node->Cells;
for ( ii=0; ii<cells->GetNumberOfIds(); ii++ )
{
thisId = cells->GetId(ii);
this->DataSet->GetCell( thisId, cell);
if ( cell->IntersectWithLine( a0, a1, tol, t, x,
pcoords, subId ) )
{ // line intersects cell, but is it the best one?
foundIntersection++;
if ( t < tBest )
{ // Yes, it's the best.
bestIntersection = foundIntersection;
tBest = t;
xBest[0] = x[0]; xBest[1] = x[1]; xBest[2] = x[2];
pcoordsBest[0] = pcoords[0]; pcoordsBest[1] = pcoords[1];
pcoordsBest[2] = pcoords[2];
subIdBest = subId;
cellIdBest = thisId;
}
}
}
}
else
{ // push kids onto stack
OBBstack[depth] = node->Kids[0];
OBBstack[depth+1] = node->Kids[1];
depth += 2;
}
}
} // end while
if ( foundIntersection != bestIntersection )
{
t = tBest;
x[0] = xBest[0]; x[1] = xBest[1]; x[2] = xBest[2];
pcoords[0] = pcoordsBest[0]; pcoords[1] = pcoordsBest[1];
pcoords[2] = pcoordsBest[2];
subId= subIdBest ;
}
delete [] OBBstack;
if ( cellIdBest < 0 )
{
return 0;
}
else
{
cellId = cellIdBest;
return 1;
}
}
void vtkOBBNode::DebugPrintTree( int level, double *leaf_vol,
int *minCells, int *maxCells )
{
double xp[3], volume, c[3];
int i;
vtkIdType nCells;
if ( this->Cells != NULL )
{
nCells = this->Cells->GetNumberOfIds();
}
else
{
nCells = 0;
}
vtkMath::Cross( this->Axes[0], this->Axes[1], xp );
volume = fabs( vtkMath::Dot( xp, this->Axes[2] ) );
for ( i=0; i<3; i++ )
{
c[i] = this->Corner[i] + 0.5*this->Axes[0][i] + 0.5*this->Axes[1][i]
+ 0.5*this->Axes[2][i];
}
for ( i=0; i<level; i++ )
{
cout<<" ";
}
cout <<level<<" # Cells: "<<nCells<<", Volume: "<<volume<<"\n";
for ( i=0; i<level; i++ )
{
cout<<" ";
}
cout << " " << vtkMath::Norm( this->Axes[0] ) << " X " <<
vtkMath::Norm( this->Axes[1] ) << " X " <<
vtkMath::Norm( this->Axes[2] ) << "\n";
for ( i=0; i<level; i++ )
{
cout<<" ";
}
cout << " Center: " << c[0] << " " << c[1] << " " << c[2] << "\n";
if ( nCells != 0 )
{
*leaf_vol += volume;
if ( nCells < *minCells )
{
*minCells = nCells;
}
if ( nCells > *maxCells )
{
*maxCells = nCells;
}
}
if ( this->Kids != NULL )
{
this->Kids[0]->DebugPrintTree( level+1, leaf_vol, minCells, maxCells );
this->Kids[1]->DebugPrintTree( level+1, leaf_vol, minCells, maxCells );
}
}
//
// Method to form subdivision of space based on the cells provided and
// subject to the constraints of levels and NumberOfCellsInOctant.
// The result is directly addressable and of uniform subdivision.
//
void vtkOBBTree::BuildLocator()
{
vtkIdType numPts, numCells, i;
vtkIdList *cellList;
vtkDebugMacro(<<"Building OBB tree");
if ( (this->Tree != NULL) && (this->BuildTime > this->MTime)
&& (this->BuildTime > this->DataSet->GetMTime()) )
{
return;
}
numPts = this->DataSet->GetNumberOfPoints();
numCells = this->DataSet->GetNumberOfCells();
if ( this->DataSet == NULL || numPts < 1 || numCells < 1 )
{
vtkErrorMacro(<<"Can't build OBB tree - no data available!");
return;
}
this->OBBCount = 0;
this->InsertedPoints = new int[numPts];
for (i=0; i < numPts; i++)
{
this->InsertedPoints[i] = 0;
}
this->PointsList = vtkPoints::New();
this->PointsList->Allocate(numPts);
//
// Begin recursively creating OBB's
//
cellList = vtkIdList::New();
cellList->Allocate(numCells);
for (i=0; i < numCells; i++)
{
cellList->InsertId(i,i);
}
if ( this->Tree )
{
this->DeleteTree(this->Tree);
delete this->Tree;
}
this->Tree = new vtkOBBNode;
this->DeepestLevel = 0;
this->BuildTree(cellList,this->Tree,0);
this->Level = this->DeepestLevel;
vtkDebugMacro(<<"# Cells: " << numCells << ", Deepest tree level: " <<
this->DeepestLevel <<", Created: " << this->OBBCount << " OBB nodes");
if ( this->GetDebug() > 1 )
{ // print tree
double volume = 0.0;
int minCells = 65535, maxCells = 0;
this->Tree->DebugPrintTree( 0, &volume, &minCells, &maxCells );
cout<<"Total leafnode volume = "<<volume<<"\n";
cout<<"Min leaf cells: "<<minCells<<", Max leaf cells: "
<<maxCells<<"\n";
cout.flush();
}
//
// Clean up
//
delete [] this->InsertedPoints;
this->PointsList->Delete();
this->BuildTime.Modified();
}
// NOTE: for better memory usage this recursive method
// frees its first argument
void vtkOBBTree::BuildTree(vtkIdList *cells, vtkOBBNode *OBBptr, int level)
{
vtkIdType i, j, numCells=cells->GetNumberOfIds();
vtkIdType cellId;
int ptId;
vtkIdList *cellPts = vtkIdList::New();
double size[3];
if ( level > this->DeepestLevel )
{
this->DeepestLevel = level;
}
//
// Now compute the OBB
//
this->ComputeOBB(cells, OBBptr->Corner, OBBptr->Axes[0],
OBBptr->Axes[1], OBBptr->Axes[2], size);
//
// Check whether to continue recursing; if so, create two children and
// assign cells to appropriate child.
//
if ( level < this->MaxLevel && numCells > this->NumberOfCellsPerBucket )
{
vtkIdList *LHlist = vtkIdList::New();
LHlist->Allocate(cells->GetNumberOfIds()/2);
vtkIdList *RHlist = vtkIdList::New();
RHlist->Allocate(cells->GetNumberOfIds()/2);
double n[3], p[3], c[3], x[3], val, ratio, bestRatio;
int negative, positive, splitAcceptable, splitPlane;
int foundBestSplit, bestPlane=0, numPts;
int numInLHnode, numInRHnode;
//loop over three split planes to find acceptable one
for (i=0; i < 3; i++) //compute split point
{
p[i] = OBBptr->Corner[i] + OBBptr->Axes[0][i]/2.0 +
OBBptr->Axes[1][i]/2.0 + OBBptr->Axes[2][i]/2.0;
}
bestRatio = 1.0; // worst case ratio
foundBestSplit = 0;
for (splitPlane=0,splitAcceptable=0; !splitAcceptable && splitPlane < 3; )
{
// compute split normal
for (i=0 ; i < 3; i++)
{
n[i] = OBBptr->Axes[splitPlane][i];
}
vtkMath::Normalize(n);
//traverse cells, assigning to appropriate child list as necessary
for ( i=0; i < numCells; i++ )
{
cellId = cells->GetId(i);
this->DataSet->GetCellPoints(cellId, cellPts);
c[0] = c[1] = c[2] = 0.0;
numPts = cellPts->GetNumberOfIds();
for ( negative=positive=j=0; j < numPts; j++ )
{
ptId = cellPts->GetId(j);
this->DataSet->GetPoint(ptId, x);
val = n[0]*(x[0]-p[0]) + n[1]*(x[1]-p[1]) + n[2]*(x[2]-p[2]);
c[0] += x[0];
c[1] += x[1];
c[2] += x[2];
if ( val < 0.0 )
{
negative = 1;
}
else
{
positive = 1;
}
}
if ( negative && positive )
{ // Use centroid to decide straddle cases
c[0] /= numPts;
c[1] /= numPts;
c[2] /= numPts;
if ( n[0]*(c[0]-p[0])+n[1]*(c[1]-p[1])+n[2]*(c[2]-p[2]) < 0.0 )
{
LHlist->InsertNextId(cellId);
}
else
{
RHlist->InsertNextId(cellId);
}
}
else
{
if ( negative )
{
LHlist->InsertNextId(cellId);
}
else
{
RHlist->InsertNextId(cellId);
}
}
}//for all cells
//evaluate this split
numInLHnode = LHlist->GetNumberOfIds();
numInRHnode = RHlist->GetNumberOfIds();
ratio = fabs(((double)numInRHnode-numInLHnode)/numCells);
//see whether we've found acceptable split plane
if ( ratio < 0.6 || foundBestSplit ) //accept right off the bat
{
splitAcceptable = 1;
}
else
{ //not a great split try another
LHlist->Reset();
RHlist->Reset();
if ( ratio < bestRatio )
{
bestRatio = ratio;
bestPlane = splitPlane;
}
if ( ++splitPlane == 3 && bestRatio < 0.95 )
{ //at closing time, even the ugly ones look good
splitPlane = bestPlane;
foundBestSplit = 1;
}
} //try another split
}//for each split
if ( splitAcceptable ) //otherwise recursion terminates
{
vtkOBBNode *LHnode= new vtkOBBNode;
vtkOBBNode *RHnode= new vtkOBBNode;
OBBptr->Kids = new vtkOBBNode *[2];
OBBptr->Kids[0] = LHnode;
OBBptr->Kids[1] = RHnode;
LHnode->Parent = OBBptr;
RHnode->Parent = OBBptr;
cells->Delete(); cells = NULL; //don't need to keep anymore
this->BuildTree(LHlist, LHnode, level+1);
this->BuildTree(RHlist, RHnode, level+1);
}
else
{
// free up local objects
LHlist->Delete();
RHlist->Delete();
}
}//if should build tree
if ( cells && this->RetainCellLists )
{
cells->Squeeze();
OBBptr->Cells = cells;
}
else if ( cells )
{
cells->Delete();
}
cellPts->Delete();
}
// Create polygonal representation for OBB tree at specified level. If
// level < 0, then the leaf OBB nodes will be gathered. The aspect ratio (ar)
// and line diameter (d) are used to control the building of the
// representation. If a OBB node edge ratio's are greater than ar, then the
// dimension of the OBB is collapsed (OBB->plane->line). A "line" OBB will be
// represented either as two crossed polygons, or as a line, depending on
// the relative diameter of the OBB compared to the diameter (d).
void vtkOBBTree::GenerateRepresentation(int level, vtkPolyData *pd)
{
vtkPoints *pts;
vtkCellArray *polys;
if ( this->Tree == NULL )
{
vtkErrorMacro(<<"No tree to generate representation from");
return;
}
pts = vtkPoints::New();
pts->Allocate(5000);
polys = vtkCellArray::New();
polys->Allocate(10000);
this->GeneratePolygons(this->Tree,0,level,pts,polys);
pd->SetPoints(pts);
pts->Delete();
pd->SetPolys(polys);
polys->Delete();
pd->Squeeze();
}
void vtkOBBTree::GeneratePolygons(vtkOBBNode *OBBptr, int level, int repLevel,
vtkPoints *pts, vtkCellArray *polys)
{
if ( level == repLevel || (repLevel < 0 && OBBptr->Kids == NULL) )
{
double x[3];
vtkIdType cubeIds[8];
vtkIdType ptIds[4];
x[0] = OBBptr->Corner[0];
x[1] = OBBptr->Corner[1];
x[2] = OBBptr->Corner[2];
cubeIds[0] = pts->InsertNextPoint(x);
x[0] = OBBptr->Corner[0] + OBBptr->Axes[0][0];
x[1] = OBBptr->Corner[1] + OBBptr->Axes[0][1];
x[2] = OBBptr->Corner[2] + OBBptr->Axes[0][2];
cubeIds[1] = pts->InsertNextPoint(x);
x[0] = OBBptr->Corner[0] + OBBptr->Axes[1][0];
x[1] = OBBptr->Corner[1] + OBBptr->Axes[1][1];
x[2] = OBBptr->Corner[2] + OBBptr->Axes[1][2];
cubeIds[2] = pts->InsertNextPoint(x);
x[0] = OBBptr->Corner[0] + OBBptr->Axes[0][0] + OBBptr->Axes[1][0];
x[1] = OBBptr->Corner[1] + OBBptr->Axes[0][1] + OBBptr->Axes[1][1];
x[2] = OBBptr->Corner[2] + OBBptr->Axes[0][2] + OBBptr->Axes[1][2];
cubeIds[3] = pts->InsertNextPoint(x);
x[0] = OBBptr->Corner[0] + OBBptr->Axes[2][0];
x[1] = OBBptr->Corner[1] + OBBptr->Axes[2][1];
x[2] = OBBptr->Corner[2] + OBBptr->Axes[2][2];
cubeIds[4] = pts->InsertNextPoint(x);
x[0] = OBBptr->Corner[0] + OBBptr->Axes[0][0] + OBBptr->Axes[2][0];
x[1] = OBBptr->Corner[1] + OBBptr->Axes[0][1] + OBBptr->Axes[2][1];
x[2] = OBBptr->Corner[2] + OBBptr->Axes[0][2] + OBBptr->Axes[2][2];
cubeIds[5] = pts->InsertNextPoint(x);
x[0] = OBBptr->Corner[0] + OBBptr->Axes[1][0] + OBBptr->Axes[2][0];
x[1] = OBBptr->Corner[1] + OBBptr->Axes[1][1] + OBBptr->Axes[2][1];
x[2] = OBBptr->Corner[2] + OBBptr->Axes[1][2] + OBBptr->Axes[2][2];
cubeIds[6] = pts->InsertNextPoint(x);
x[0] = OBBptr->Corner[0] + OBBptr->Axes[0][0] + OBBptr->Axes[1][0]
+ OBBptr->Axes[2][0];
x[1] = OBBptr->Corner[1] + OBBptr->Axes[0][1] + OBBptr->Axes[1][1]
+ OBBptr->Axes[2][1];
x[2] = OBBptr->Corner[2] + OBBptr->Axes[0][2] + OBBptr->Axes[1][2]
+ OBBptr->Axes[2][2];
cubeIds[7] = pts->InsertNextPoint(x);
ptIds[0] = cubeIds[0]; ptIds[1] = cubeIds[2];
ptIds[2] = cubeIds[3]; ptIds[3] = cubeIds[1];
polys->InsertNextCell(4,ptIds);
ptIds[0] = cubeIds[0]; ptIds[1] = cubeIds[1];
ptIds[2] = cubeIds[5]; ptIds[3] = cubeIds[4];
polys->InsertNextCell(4,ptIds);
ptIds[0] = cubeIds[0]; ptIds[1] = cubeIds[4];
ptIds[2] = cubeIds[6]; ptIds[3] = cubeIds[2];
polys->InsertNextCell(4,ptIds);
ptIds[0] = cubeIds[1]; ptIds[1] = cubeIds[3];
ptIds[2] = cubeIds[7]; ptIds[3] = cubeIds[5];
polys->InsertNextCell(4,ptIds);
ptIds[0] = cubeIds[4]; ptIds[1] = cubeIds[5];
ptIds[2] = cubeIds[7]; ptIds[3] = cubeIds[6];
polys->InsertNextCell(4,ptIds);
ptIds[0] = cubeIds[2]; ptIds[1] = cubeIds[6];
ptIds[2] = cubeIds[7]; ptIds[3] = cubeIds[3];
polys->InsertNextCell(4,ptIds);
}
else if ( (level < repLevel || repLevel < 0) && OBBptr->Kids != NULL )
{
this->GeneratePolygons(OBBptr->Kids[0],level+1,repLevel,pts,polys);
this->GeneratePolygons(OBBptr->Kids[1],level+1,repLevel,pts,polys);
}
}
int vtkOBBTree::DisjointOBBNodes( vtkOBBNode *nodeA,
vtkOBBNode *nodeB,
vtkMatrix4x4 *XformBtoA )
{
vtkOBBNode nodeBxformed, *pB, *pA;
double centerA[3], centerB[3], AtoB[3], in[4], out[4];
double rangeAmin, rangeAmax, rangeBmin, rangeBmax, dotA, dotB,
dotAB[3][3];
double eps;
int ii, jj, kk;
eps = this->Tolerance;
pA = nodeA;
if ( XformBtoA != NULL )
{ // Here we assume that XformBtoA is an orthogonal matrix
pB = &nodeBxformed;
in[0] = nodeB->Corner[0]; in[1] = nodeB->Corner[1] ;
in[2] = nodeB->Corner[2]; in[3] = 1.0;
XformBtoA->MultiplyPoint( in, out );
pB->Corner[0] = out[0]/out[3];
pB->Corner[1] = out[1]/out[3];
pB->Corner[2] = out[2]/out[3];
// Clean this up when the bug in MultiplyVectors is fixed!
for ( ii=0; ii<3; ii++ )
{
pB->Axes[0][ii] = nodeB->Corner[ii] + nodeB->Axes[0][ii];
pB->Axes[1][ii] = nodeB->Corner[ii] + nodeB->Axes[1][ii];
pB->Axes[2][ii] = nodeB->Corner[ii] + nodeB->Axes[2][ii];
}
for ( ii=0; ii<3; ii++ )
{
in[0] = pB->Axes[ii][0]; in[1] = pB->Axes[ii][1];
in[2] = pB->Axes[ii][2]; in[3] = 1.0;
XformBtoA->MultiplyPoint( in, out );
pB->Axes[ii][0] = out[0]/out[3];
pB->Axes[ii][1] = out[1]/out[3];
pB->Axes[ii][2] = out[2]/out[3];
}
for ( ii=0; ii<3; ii++ )
{
pB->Axes[0][ii] = pB->Axes[0][ii] - pB->Corner[ii];
pB->Axes[1][ii] = pB->Axes[1][ii] - pB->Corner[ii];
pB->Axes[2][ii] = pB->Axes[2][ii] - pB->Corner[ii];
}
}
else
{
pB = nodeB;
}
for ( ii=0; ii<3; ii++ )
{
centerA[ii] = pA->Corner[ii] +
0.5*(pA->Axes[0][ii] + pA->Axes[1][ii] + pA->Axes[2][ii]);
centerB[ii] = pB->Corner[ii] +
0.5*(pB->Axes[0][ii] + pB->Axes[1][ii] + pB->Axes[2][ii]);
AtoB[ii] = centerB[ii] - centerA[ii];
}
// Project maximal and minimal corners onto line between centers
rangeAmin = rangeAmax = vtkMath::Dot( pA->Corner, AtoB );
rangeBmin = rangeBmax = vtkMath::Dot( pB->Corner, AtoB );
for ( ii=0; ii<3; ii++ )
{
// compute A range
dotA = vtkMath::Dot( pA->Axes[ii], AtoB );
if ( dotA > 0 )
{
rangeAmax += dotA;
}
else
{
rangeAmin += dotA;
}
// compute B range
dotB = vtkMath::Dot( pB->Axes[ii], AtoB );
if ( dotB > 0 )
{
rangeBmax += dotB;
}
else
{
rangeBmin += dotB;
}
}
if ( (rangeAmax+eps < rangeBmin) || (rangeBmax+eps < rangeAmin) )
{
return( 1 ); // A and B are Disjoint by the 1st test.
}
// now check for a separation plane parallel to the faces of B
for ( ii=0; ii<3; ii++ )
{ // plane is normal to pB->Axes[ii]
// computing B range is easy...
rangeBmin = rangeBmax = vtkMath::Dot( pB->Corner, pB->Axes[ii] );
rangeBmax += vtkMath::Dot( pB->Axes[ii], pB->Axes[ii] );
// compute A range...
rangeAmin = rangeAmax = vtkMath::Dot( pA->Corner, pB->Axes[ii] );
for ( jj=0; jj<3; jj++ )
{
// (note: we are saving all 9 dotproducts for future use)
dotA = dotAB[ii][jj] = vtkMath::Dot( pB->Axes[ii], pA->Axes[jj] );
if ( dotA > 0 )
{
rangeAmax += dotA;
}
else
{
rangeAmin += dotA;
}
}
if ( (rangeAmax+eps < rangeBmin) || (rangeBmax+eps < rangeAmin) )
{
return( 2 ); // A and B are Disjoint by the 3rd test.
}
}
// now check for a separation plane parallel to the faces of A
for ( ii=0; ii<3; ii++ )
{ // plane is normal to pA->Axes[ii]
// computing A range is easy...
rangeAmin = rangeAmax = vtkMath::Dot( pA->Corner, pA->Axes[ii] );
rangeAmax += vtkMath::Dot( pA->Axes[ii], pA->Axes[ii] );
// compute B range...
rangeBmin = rangeBmax = vtkMath::Dot( pB->Corner, pA->Axes[ii] );
for ( jj=0; jj<3; jj++ )
{
// (note: we are using the 9 dotproducts computed earlier)
dotB = dotAB[jj][ii];
if ( dotB > 0 )
{
rangeBmax += dotB;
}
else
{
rangeBmin += dotB;
}
}
if ( (rangeAmax+eps < rangeBmin) || (rangeBmax+eps < rangeAmin) )
{
return( 3 ); // A and B are Disjoint by the 2nd test.
}
}
// Bad luck: now we must look for a separation plane parallel
// to one edge from A and one edge from B.
for ( ii=0; ii<3; ii++ )
{
for ( jj=0; jj<3; jj++ )
{
// the plane is normal to pA->Axes[ii] X pB->Axes[jj]
vtkMath::Cross( pA->Axes[ii], pB->Axes[jj], AtoB );
rangeAmin = rangeAmax = vtkMath::Dot( pA->Corner, AtoB );
rangeBmin = rangeBmax = vtkMath::Dot( pB->Corner, AtoB );
for ( kk=0; kk<3; kk++ )
{
// compute A range
dotA = vtkMath::Dot( pA->Axes[kk], AtoB );
if ( dotA > 0 )
{
rangeAmax += dotA;
}
else
{
rangeAmin += dotA;
}
// compute B range
dotB = vtkMath::Dot( pB->Axes[kk], AtoB );
if ( dotB > 0 )
{
rangeBmax += dotB;
}
else
{
rangeBmin += dotB;
}
}
if ( (rangeAmax+eps < rangeBmin) || (rangeBmax+eps < rangeAmin) )
{
return( 4 ); // A and B are Disjoint by the 4th test.
}
}
}
// if we fall through to here, the OBB's overlap
return( 0 );
}
int vtkOBBTree::TriangleIntersectsNode( vtkOBBNode *nodeA,
double p0[3], double p1[3], double p2[3],
vtkMatrix4x4 *XformBtoA )
{
vtkOBBNode *pA;
double p0Xformed[3], p1Xformed[3], p2Xformed[3];
double *pB[3], in[4], out[4], v0[3], v1[3], AtoB[3], xprod[3];
double rangeAmin, rangeAmax, rangeBmin, rangeBmax, dotA, dotB;
double eps;
int ii, jj, kk;
eps = this->Tolerance;
pA = nodeA;
if ( XformBtoA != NULL )
{ // Here we assume that XformBtoA is an orthogonal matrix
pB[0] = p0Xformed; pB[1] = p1Xformed; pB[2] = p2Xformed;
for ( ii=0; ii<3; ii++ )
{
p0Xformed[ii] = p0[ii];
p1Xformed[ii] = p1[ii];
p2Xformed[ii] = p2[ii];
}
for ( ii=0; ii<3; ii++ )
{
in[0] = pB[ii][0]; in[1] = pB[ii][1] ; in[2] = pB[ii][2]; in[3] = 1.0;
XformBtoA->MultiplyPoint( in, out );
pB[ii][0] = out[0]/out[3];
pB[ii][1] = out[1]/out[3];
pB[ii][2] = out[2]/out[3];
}
}
else
{
pB[0] = p0; pB[1] = p1; pB[2] = p2;
}
// now check for a separation plane parallel to the triangle
for ( ii=0; ii<3; ii++ )
{ // plane is normal to the triangle
v0[ii] = pB[1][ii] - pB[0][ii];
v1[ii] = pB[2][ii] - pB[0][ii];
}
vtkMath::Cross( v0, v1, xprod );
// computing B range is easy...
rangeBmin = rangeBmax = vtkMath::Dot( pB[0], xprod );
// compute A range...
rangeAmin = rangeAmax = vtkMath::Dot( pA->Corner, xprod );
for ( jj=0; jj<3; jj++ )
{
dotA = vtkMath::Dot( xprod, pA->Axes[jj] );
if ( dotA > 0 )
{
rangeAmax += dotA;
}
else
{
rangeAmin += dotA;
}
}
if ( (rangeAmax+eps < rangeBmin) || (rangeBmax+eps < rangeAmin) )
{
return( 0 ); // A and B are Disjoint by the 1st test.
}
// now check for a separation plane parallel to the faces of A
for ( ii=0; ii<3; ii++ )
{ // plane is normal to pA->Axes[ii]
// computing A range is easy...
rangeAmin = rangeAmax = vtkMath::Dot( pA->Corner, pA->Axes[ii] );
rangeAmax += vtkMath::Dot( pA->Axes[ii], pA->Axes[ii] );
// compute B range...
rangeBmin = rangeBmax = vtkMath::Dot( pB[0], pA->Axes[ii] );
dotB = vtkMath::Dot( pB[1], pA->Axes[ii] );
if ( dotB > rangeBmax )
{
rangeBmax = dotB;
}
else
{
rangeBmin = dotB;
}
dotB = vtkMath::Dot( pB[2], pA->Axes[ii] );
if ( dotB > rangeBmax )
{
rangeBmax = dotB;
}
else if ( dotB < rangeBmin )
{
rangeBmin = dotB;
}
if ( (rangeAmax+eps < rangeBmin) || (rangeBmax+eps < rangeAmin) )
{
return( 0 ); // A and B are Disjoint by the 2nd test.
}
}
// Bad luck: now we must look for a separation plane parallel
// to one edge from A and one edge from B.
for ( ii=0; ii<3; ii++ )
{
for ( jj=0; jj<3; jj++ )
{
// the plane is normal to pA->Axes[ii] X (pB[jj+1]-pB[jj])
v0[0] = pB[(jj+1)%3][0] - pB[jj][0];
v0[1] = pB[(jj+1)%3][1] - pB[jj][1];
v0[2] = pB[(jj+1)%3][2] - pB[jj][2];
vtkMath::Cross( pA->Axes[ii], v0, AtoB );
rangeAmin = rangeAmax = vtkMath::Dot( pA->Corner, AtoB );
rangeBmin = rangeBmax = vtkMath::Dot( pB[jj], AtoB );
for ( kk=0; kk<3; kk++ )
{
// compute A range
dotA = vtkMath::Dot( pA->Axes[kk], AtoB );
if ( dotA > 0 )
{
rangeAmax += dotA;
}
else
{
rangeAmin += dotA;
}
}
// compute B range
dotB = vtkMath::Dot( pB[(jj+2)%3], AtoB );
if ( dotB > rangeBmax )
{
rangeBmax = dotB;
}
else
{
rangeBmin = dotB;
}
if ( (rangeAmax+eps < rangeBmin) || (rangeBmax+eps < rangeAmin) )
{
return( 0 ); // A and B are Disjoint by the 3rd test.
}
}
}
// if we fall through to here, the OBB overlaps the triangle.
return( 1 );
}
// check if a line intersects the node: the line doesn't have to actually
// pass all the way through the node, but at least some portion of the line
// must lie within the node.
int vtkOBBTree::LineIntersectsNode( vtkOBBNode *pA,
double b0[3], double b1[3] )
{
double rangeAmin, rangeAmax, rangeBmin, rangeBmax, dotB;
double eps;
int ii;
for ( ii = 0; ii < 3; ii++ )
{
// computing A range is easy...
rangeAmin = vtkMath::Dot( pA->Corner, pA->Axes[ii] );
rangeAmax = rangeAmin + vtkMath::Dot( pA->Axes[ii], pA->Axes[ii] );
// compute B range...
rangeBmin = vtkMath::Dot( b0, pA->Axes[ii] );
rangeBmax = rangeBmin;
dotB = vtkMath::Dot( b1, pA->Axes[ii] );
if ( dotB < rangeBmin )
{
rangeBmin = dotB;
}
else
{
rangeBmax = dotB;
}
eps = this->Tolerance;
if ( eps != 0 )
{ // avoid sqrt call if tolerance check isn't being done
eps *= sqrt(fabs(rangeAmax - rangeAmin));
}
if ( (rangeAmax+eps < rangeBmin) || (rangeBmax+eps < rangeAmin) )
{
return ( 0 );
}
}
// if we fall through to here, the OBB overlaps the line segment.
return ( 1 );
}
// Intersect this OBBTree with OBBTreeB (as transformed) and
// call processing function for each intersecting leaf node pair.
// If the processing function returns a negative integer, terminate.
int vtkOBBTree::IntersectWithOBBTree( vtkOBBTree *OBBTreeB,
vtkMatrix4x4 *XformBtoA,
int(*function)( vtkOBBNode *nodeA,
vtkOBBNode *nodeB,
vtkMatrix4x4 *Xform,
void *arg ),
void *data_arg )
{
int maxdepth, mindepth, depth, returnValue = 0, count = 0, maxStackDepth;
vtkOBBNode **OBBstackA, **OBBstackB, *nodeA, *nodeB;
// Intersect OBBs and process intersecting leaf nodes.
maxdepth = this->GetLevel();
if ( (mindepth = OBBTreeB->GetLevel()) > maxdepth )
{
mindepth = maxdepth;
maxdepth = OBBTreeB->GetLevel();
}
// Compute maximum theoretical recursion depth.
maxStackDepth = 3*mindepth + 2*(maxdepth-mindepth) + 1;
OBBstackA = new vtkOBBNode *[maxStackDepth];
OBBstackB = new vtkOBBNode *[maxStackDepth];
OBBstackA[0] = this->Tree;
OBBstackB[0] = OBBTreeB->Tree;
depth = 1;
while( depth > 0 && returnValue > -1 )
{ // simulate recursion without overhead of real recursion.
depth--;
nodeA = OBBstackA[depth];
nodeB = OBBstackB[depth];
if ( !this->DisjointOBBNodes( nodeA, nodeB, XformBtoA ) )
{
if ( nodeA->Kids == NULL )
{
if ( nodeB->Kids == NULL )
{ // then this is a pair of intersecting leaf nodes to process
returnValue = (*function)( nodeA, nodeB, XformBtoA, data_arg );
if ( returnValue >= 0 )
{
count += returnValue;
}
else
{
count = returnValue;
}
}
else
{ // A is a leaf, but B goes deeper.
OBBstackA[depth] = nodeA;
OBBstackB[depth] = nodeB->Kids[0];
OBBstackA[depth+1] = nodeA;
OBBstackB[depth+1] = nodeB->Kids[1];
depth += 2;
}
}
else
{
if ( nodeB->Kids == NULL )
{ // B is a leaf, but A goes deeper.
OBBstackB[depth] = nodeB;
OBBstackA[depth] = nodeA->Kids[0];
OBBstackB[depth+1] = nodeB;
OBBstackA[depth+1] = nodeA->Kids[1];
depth += 2;
}
else
{ // neither A nor B are leaves. Go to the next level.
OBBstackA[depth] = nodeA->Kids[0];
OBBstackB[depth] = nodeB->Kids[0];
OBBstackA[depth+1] = nodeA->Kids[1];
OBBstackB[depth+1] = nodeB->Kids[0];
OBBstackA[depth+2] = nodeA->Kids[0];
OBBstackB[depth+2] = nodeB->Kids[1];
OBBstackA[depth+3] = nodeA->Kids[1];
OBBstackB[depth+3] = nodeB->Kids[1];
depth += 4;
}
}
}
}
// cleanup
delete OBBstackA;
delete OBBstackB;
return( count );
}
void vtkOBBTree::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
if ( this->Tree )
{
os << indent << "Tree " << this->Tree << "\n";
}
else
{
os << indent << "Tree: (null)\n";
}
if ( this->PointsList )
{
os << indent << "PointsList " << this->PointsList << "\n";
}
else
{
os << indent << "PointsList: (null)\n";
}
if( this->InsertedPoints )
{
os << indent << "InsertedPoints " << this->InsertedPoints << "\n";
}
else
{
os << indent << "InsertedPoints: (null)\n";
}
os << indent << "OBBCount " << this->OBBCount << "\n";
os << indent << "DeepestLevel " << this->DeepestLevel << "\n";
}