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.
nmWTAI-Platform/3rd/VTK7.1/source/VTK-7.1.1/Common/DataModel/vtkPolyhedron.cxx

3586 lines
103 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkPolyhedron.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 "vtkPolyhedron.h"
#include "vtkCellArray.h"
#include "vtkIdTypeArray.h"
#include "vtkDoubleArray.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkOrderedTriangulator.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkTetra.h"
#include "vtkTriangle.h"
#include "vtkQuad.h"
#include "vtkPolygon.h"
#include "vtkLine.h"
#include "vtkEdgeTable.h"
#include "vtkPolyData.h"
#include "vtkCellLocator.h"
#include "vtkGenericCell.h"
#include "vtkPointLocator.h"
#include "vtkMeanValueCoordinatesInterpolator.h"
#include "vtkSmartPointer.h"
#include "vtkMergePoints.h"
#include "vtkCellData.h"
#include "vtkDataArray.h"
#include "vtkType.h"
#include <map>
#include <vector>
#include <set>
#include <list>
#include <limits>
vtkStandardNewMacro(vtkPolyhedron);
// Special typedef
typedef std::vector<vtkIdType> vtkIdVectorType;
class vtkPointIdMap : public std::map<vtkIdType,vtkIdType>{};
class vtkIdToIdMapType : public std::map<vtkIdType, vtkIdType>{};
class vtkIdToIdVectorMapType : public std::map<vtkIdType, vtkIdVectorType>{};
typedef std::map<vtkIdType,vtkIdType*>::iterator PointIdMapIterator;
typedef vtkIdToIdVectorMapType::iterator vtkIdToIdVectorMapIteratorType;
typedef std::pair<vtkIdType, vtkIdVectorType> vtkIdToIdVectorPairType;
typedef std::pair<vtkIdType, vtkIdType> vtkIdToIdPairType;
typedef std::set<vtkIdType> vtkIdSetType;
// Special class for iterating through polyhedron faces
//----------------------------------------------------------------------------
class vtkPolyhedronFaceIterator
{
public:
vtkIdType CurrentPolygonSize;
vtkIdType *Polygon;
vtkIdType *Current;
vtkIdType NumberOfPolygons;
vtkIdType Id;
vtkPolyhedronFaceIterator(vtkIdType numFaces, vtkIdType *t)
{
this->CurrentPolygonSize = t[0];
this->Polygon = t;
this->Current = t+1;
this->NumberOfPolygons = numFaces;
this->Id = 0;
}
vtkIdType* operator++()
{
this->Current += this->CurrentPolygonSize + 1;
this->Polygon = this->Current - 1;
this->Id++;
if (this->Id < this->NumberOfPolygons)
{
this->CurrentPolygonSize = this->Polygon[0];
}
else
{
this->CurrentPolygonSize = VTK_ID_MAX;
}
return this->Current;
}
};
// Special class for iterating through vertices on a polygon face
//----------------------------------------------------------------------------
class vtkPolygonVertexIterator
{
public:
vtkIdType *Current;
vtkIdType NumberOfVertices;
vtkIdType Id;
// 1 or 0 for iterating along its original direction or reverse
vtkIdType IterDirection;
vtkPolygonVertexIterator(vtkIdType numVertices, vtkIdType startVertex,
vtkIdType *startVertexPointer, vtkIdType nextVertex)
{
this->Current = startVertexPointer;
this->NumberOfVertices = numVertices;
this->Id = startVertex;
this->IterDirection = 1;
vtkIdType nextId = this->Id + 1;
vtkIdType *next = this->Current + 1;
if (nextId == this->NumberOfVertices)
{
next -= this->NumberOfVertices;
}
if (*next != nextVertex)
{
this->IterDirection = 0;
}
}
vtkIdType* operator++()
{
if (this->IterDirection)
{
this->Id++;
this->Current++;
if (this->Id == this->NumberOfVertices)
{
this->Id = 0;
this->Current -= this->NumberOfVertices;
}
}
else
{
this->Id--;
this->Current--;
if (this->Id == -1)
{
this->Id = this->NumberOfVertices - 1;
this->Current += this->NumberOfVertices;
}
}
return this->Current;
}
};
//----------------------------------------------------------------------------
class vtkPolyhedron::vtkInternal
{
public:
vtkIdTypeArray * FacesBackup;
vtkEdgeTable * EdgeTableBackup;
vtkInternal()
{
this->FacesBackup = NULL;
this->EdgeTableBackup = NULL;
}
~vtkInternal()
{
this->FacesBackup = NULL;
this->EdgeTableBackup = NULL;
}
//----------------------------------------------------------------------------
// Here we use a point merger to try to prevent the problem of duplicated
// points in the input.
void RemoveDuplicatedPointsFromFaceArrayAndEdgeTable(vtkPoints * points,
vtkIdTypeArray * & faces,
vtkEdgeTable * & edgeTable,
double *bounds)
{
double coordTolerance = 0.000001; // initial value
if (bounds)
{
// scale coordTolerance with length of the bounds diagonal if < 1.0
double bbDiag = sqrt((bounds[1] - bounds[0])*(bounds[1] - bounds[0]) +
(bounds[3] - bounds[2])*(bounds[3] - bounds[2]) +
(bounds[5] - bounds[4])*(bounds[5] - bounds[4]));
coordTolerance = std::min(coordTolerance, coordTolerance*bbDiag);
}
vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkPointLocator> merge = vtkSmartPointer<vtkPointLocator>::New();
merge->SetTolerance(coordTolerance);
merge->InitPointInsertion(newPoints, bounds);
bool foundDupPoint = false;
vtkIdType pid = -1;
vtkIdToIdMapType pidMap0;
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); i++)
{
if (!merge->InsertUniquePoint(points->GetPoint(i), pid))
{
foundDupPoint = true;
}
if (pidMap0.find(pid) == pidMap0.end())
{
pidMap0.insert(vtkIdToIdPairType(pid,i));
}
}
// update face array and edge table if necessary.
if (foundDupPoint)
{
vtkIdToIdMapType pidMap;
for (vtkIdType i = 0; i < points->GetNumberOfPoints(); i++)
{
pid = merge->IsInsertedPoint(points->GetPoint(i));
pidMap.insert(vtkIdToIdPairType(i, pidMap0.find(pid)->second));
}
this->FacesBackup = faces;
this->EdgeTableBackup = edgeTable;
vtkIdType nfaces = 0;
vtkIdType insertId = 0;
faces = vtkIdTypeArray::New();
faces->SetNumberOfTuples(points->GetNumberOfPoints()*10);
faces->InsertComponent(insertId++, 0, 0); // allocate space for nfaces
edgeTable = vtkEdgeTable::New();
edgeTable->InitEdgeInsertion(points->GetNumberOfPoints());
vtkPolyhedronFaceIterator
faceIter(this->FacesBackup->GetValue(0), this->FacesBackup->GetPointer(1));
while (faceIter.Id < faceIter.NumberOfPolygons)
{
vtkIdVectorType vVector;
for (vtkIdType i = 0; i < faceIter.CurrentPolygonSize; i++)
{
pid = pidMap.find(faceIter.Current[i])->second;
vVector.push_back(pid);
}
bool dupPointRemoved = true;
while (dupPointRemoved && vVector.size() > 2)
{
dupPointRemoved = false;
if (vVector[0] == vVector[vVector.size()-1])
{
vVector.erase(vVector.begin()+vVector.size()-1);
dupPointRemoved = true;
}
for (size_t i = 1; i < vVector.size(); i++)
{
if (vVector[i] == vVector[i-1])
{
vVector.erase(vVector.begin()+i);
dupPointRemoved = true;
}
}
}
if (vVector.size() < 3)
{
++faceIter;
continue;
}
nfaces++;
faces->InsertComponent(insertId++, 0, vVector.size());
for (size_t i = 0; i < vVector.size(); i++)
{
faces->InsertComponent(insertId++, 0, vVector[i]);
}
if (edgeTable->IsEdge(vVector[0],vVector[vVector.size()-1]) == (-1))
{
edgeTable->InsertEdge(vVector[0],vVector[vVector.size()-1]);
}
for (size_t i = 1; i < vVector.size(); i++)
{
if (edgeTable->IsEdge(vVector[i],vVector[i-1]) == (-1))
{
edgeTable->InsertEdge(vVector[i],vVector[i-1]);
}
}
++faceIter;
}
faces->SetComponent(0,0,nfaces);
}
else
{
this->FacesBackup = NULL;
this->EdgeTableBackup = NULL;
}
}
//----------------------------------------------------------------------------
// Here we use a point merger to try to prevent the problem of duplicated
// points in the input.
void RestoreFaceArrayAndEdgeTable(vtkIdTypeArray * & faces,
vtkEdgeTable * & edgeTable)
{
if (this->FacesBackup)
{
faces->Delete();
faces = this->FacesBackup;
}
if (this->EdgeTableBackup)
{
edgeTable->Delete();
edgeTable = this->EdgeTableBackup;
}
}
//----------------------------------------------------------------------------
// insert new id element in between two existing adjacent id elements.
// this is a convenient function. no check whether the input elements
// exist in the vector. no check for element adjacency.
int InsertNewIdToIdVector(vtkIdVectorType & idVector, vtkIdType id,
vtkIdType id0, vtkIdType id1)
{
if (idVector.size() < 2)
{
return 0;
}
size_t num = idVector.size();
if ((idVector[0] == id0 && idVector[num-1] == id1)
||(idVector[0] == id1 && idVector[num-1] == id0))
{
idVector.push_back(id);
return 1;
}
vtkIdVectorType::iterator iter = idVector.begin();
for (; iter != idVector.end(); ++iter)
{
if (*iter == id0 || *iter == id1)
{
++iter;
idVector.insert(iter, id);
return 1;
}
}
return 0;
};
// Convinient function used by clip. The id is the vector index of the positive
// point, id0 is the vector index of the start point, and id1 is the vector index
// of the end point.
//----------------------------------------------------------------------------
int EraseSegmentFromIdVector(vtkIdVectorType & idVector, vtkIdType id,
vtkIdType id0, vtkIdType id1)
{
// three possible cases
// first case: 0 -- id0 -- id -- id1 -- size-1
if (id0 < id && id < id1)
{
idVector.erase(idVector.begin() + id0 + 1, idVector.begin() + id1);
}
// second case: 0 -- id1 -- id0 -- id -- size-1
// third case: 0 -- id -- id1 -- id0 -- size-1
else if (id1 < id0 && (id0 < id || id < id1))
{
idVector.erase(idVector.begin() + id0 + 1, idVector.end());
idVector.erase(idVector.begin(), idVector.begin() + id1);
}
else
{
// we should never get here.
return 0;
}
return 1;
};
// convert the point ids from map.first to map.second
//----------------------------------------------------------------------------
int ConvertPointIds(vtkIdType npts, vtkIdType * pts,
vtkIdToIdMapType & map, vtkIdType reverse = 0)
{
for (vtkIdType i = 0; i < npts; i++)
{
vtkIdType id = reverse ? npts-1-i : i;
vtkIdToIdMapType::iterator iter = map.find(pts[id]);
if (iter == map.end())
{
return 0;
}
pts[id] = iter->second;
}
return 1;
};
//----------------------------------------------------------------------------
// The connected contour points are found by (1) locating the current
// contour point in the face loop, (2) looping through face point:
// meet a positive point, keep going.
// meet a contour point, store it and stop marching in this direction.
// meet a negative point, stop marching in this direction.
// meet the same point from both directions, stop.
// This loop may find zero, one or two connected contour points.
void FindConnectedContourPointsOnFace(vtkIdVectorType & facePtsVector,
vtkIdVectorType & faceContourPtsVec,
vtkIdType currContourPoint,
vtkIdVectorType & pointLabelVec,
vtkIdSetType & connectedContourPtsSet,
vtkIdSetType & unConnectedContourPtsSet)
{
vtkIdType numFacePoints = static_cast<vtkIdType>(facePtsVector.size());
if (numFacePoints < 3)
{
return;
}
if (faceContourPtsVec.size() < 2)
{
return;
}
// locate the id of the startContourPt inside the face loop
vtkIdType startPt = -1;
for (vtkIdType i = 0; i < numFacePoints; i++)
{
if (currContourPoint == facePtsVector[i])
{
startPt = i;
break;
}
}
if (startPt < 0 || startPt >= numFacePoints)
{
return;
}
vtkIdType leftEndPt = -1; // face loop index
vtkIdType rightEndPt = -1; // face loop index
vtkIdType leftEndPoint = -1; // point id
vtkIdType rightEndPoint = -1; // point id
vtkIdType leftEndPassPositivePoint = 0;
vtkIdType rightEndPassPositivePoint = 0;
// search in one direction.
vtkIdType endPt = startPt - 1;
for (; endPt != startPt; endPt--)
{
if (endPt < 0)
{
endPt = numFacePoints - 1;
if (endPt == startPt)
{
break;
}
}
if (pointLabelVec[facePtsVector[endPt]] == -1)//negative point reached. stop
{
break;
}
else if (pointLabelVec[facePtsVector[endPt]] == 0)//contour pt reached. stop
{
leftEndPt = endPt;
leftEndPoint = facePtsVector[endPt];
break;
}
else
{
leftEndPassPositivePoint = 1;
}
// positive pt reached. continue.
}
// check if already loop through the entire face
if (endPt != startPt)
{
vtkIdType prevEndPt = endPt;
// search in the other direction
for (endPt = startPt + 1; endPt != prevEndPt; endPt++)
{
if (endPt > numFacePoints - 1)
{
endPt = 0;
if (endPt == prevEndPt)
{
break;
}
if (endPt == startPt)
{
break;
}
}
if (pointLabelVec[facePtsVector[endPt]] == -1)//negative point reached. stop
{
break;
}
else if (pointLabelVec[facePtsVector[endPt]] == 0)//contour pt reached. stop
{
rightEndPt = endPt;
rightEndPoint = facePtsVector[endPt];
break;
}
else
{
rightEndPassPositivePoint = 1;
}
}
}
// need to check a special case where startPt, leftEndPoint and rightEndPoint
// are directly connected or connected by a series of other contour points,
// and startPt is at one end of the contour strip. We can check this situation
// using leftEndPassPositivePoint and leftEndPassPositivePoint. If both are
// 1, then the three points are not on a contour strip. If both are 0, then
// startPt is not at one end of the contour strip.
if (leftEndPoint >= 0 && rightEndPoint >=0 && leftEndPoint != rightEndPoint)
{
if (leftEndPassPositivePoint != rightEndPassPositivePoint)
{
bool foundNonContourPoint = false;
for (endPt = leftEndPt - 1; endPt != rightEndPt; endPt--)
{
if (endPt < 0)
{
endPt = numFacePoints - 1;
if (endPt == rightEndPt)
{
break;
}
}
if (pointLabelVec[facePtsVector[endPt]] != 0)
{
foundNonContourPoint = true;
break;
}
}
if (!foundNonContourPoint)// startPt on one end of the contour strip
{
if (leftEndPassPositivePoint)
{
leftEndPoint = -1;
}
else
{
rightEndPoint = -1;
}
}
}
}
if (leftEndPoint >= 0)
{
connectedContourPtsSet.insert(leftEndPoint);
}
if (rightEndPoint >= 0)
{
connectedContourPtsSet.insert(rightEndPoint);
}
for (size_t i = 0; i < faceContourPtsVec.size(); i++)
{
if (faceContourPtsVec[i] != leftEndPoint &&
faceContourPtsVec[i] != rightEndPoint &&
faceContourPtsVec[i] != currContourPoint)
{
unConnectedContourPtsSet.insert(faceContourPtsVec[i]);
}
}
};
//----------------------------------------------------------------------------
void RemoveIdFromIdToIdVectorMap(vtkIdToIdVectorMapType & map, vtkIdType id)
{
vtkIdToIdVectorMapIteratorType mit = map.begin();
for (; mit != map.end(); ++mit)
{
vtkIdVectorType::iterator vit = mit->second.begin();
for (; vit != mit->second.end(); ++vit)
{
if ((*vit) == id)
{
mit->second.erase(vit);
break;
}
}
}
};
//----------------------------------------------------------------------------
// For each contour point, extract its adjacent faces, then extract other
// contour points on the same face that can be connected to the current
// points.
// The connected contour points are found by (1) locating the current
// contour point in the face loop, (2) looping through face point:
// meet a positive point, keep going.
// meet a contour point, store it and stop marching in this direction.
// meet a negative point, stop marching in this direction.
// meet the same point from both directions, stop.
// This loop may find zero, one or two connected contour points.
int ExtractContourConnectivities(
vtkIdToIdVectorMapType & ceMap,
vtkIdSetType & cpSet,
vtkIdVectorType & pointLabelVector,
vtkIdToIdVectorMapType & pointToFacesMap,
vtkIdToIdVectorMapType & faceToPointsMap,
vtkIdToIdVectorMapType & faceToContourPointsMap)
{
int maxConnectivity = 0;
if (cpSet.empty())
{
return 0;
}
vtkIdSetType contourBranchesSet;
vtkIdSetType nonContourBranchesSet;
vtkIdVectorType contourBranchesVector;
vtkIdSetType::iterator cpSetIt;
vtkIdToIdVectorMapType::iterator fcpMapIt, fvMapIt, ceMapIt, ceMapIt1;
for (cpSetIt = cpSet.begin(); cpSetIt != cpSet.end(); /*manual increment*/)
{
contourBranchesSet.clear();
nonContourBranchesSet.clear();
contourBranchesVector.clear();
vtkIdType pid = *cpSetIt;
vtkIdVectorType fVector = pointToFacesMap.find(pid)->second;
for (size_t i = 0; i < fVector.size(); i++)
{
// find adjacent faces that contain contour points
fcpMapIt = faceToContourPointsMap.find(fVector[i]);
if (fcpMapIt == faceToContourPointsMap.end())
{
continue;
}
fvMapIt = faceToPointsMap.find(fVector[i]);
if (fvMapIt == faceToPointsMap.end())
{
cout << "Cannot find point ids of a face. We should never get "
"here. Contouring aborted." << endl;
return 0;
}
// find connected contour points and store them in the set. Notice that
// some weird topology will classify a point as a connected contour point
// in one face and a non-connected contour point in some other face. we
// will extract the union.
FindConnectedContourPointsOnFace(
fvMapIt->second, fcpMapIt->second, pid,
pointLabelVector, contourBranchesSet, nonContourBranchesSet);
}
if (!contourBranchesSet.empty())
{
vtkIdSetType::iterator ccpSetIt = contourBranchesSet.begin();
for (; ccpSetIt != contourBranchesSet.end(); ++ccpSetIt)
{
if (nonContourBranchesSet.find(*ccpSetIt) == nonContourBranchesSet.end())
{
contourBranchesVector.push_back(*ccpSetIt);
}
}
}
if (contourBranchesVector.size() >= 2)
{
ceMap.insert(
vtkIdToIdVectorPairType(pid, contourBranchesVector));
++cpSetIt;
}
else // throw away point contour or edge contour.
{
if (cpSetIt != cpSet.begin())
{
vtkIdSetType::iterator tempIt = cpSetIt;
--cpSetIt;
cpSet.erase(tempIt);
++cpSetIt;
}
else
{
cpSet.erase(cpSetIt);
cpSetIt = cpSet.begin();
}
}
}
// sanity check, all edges should be listed twice
for (ceMapIt = ceMap.begin(); ceMapIt != ceMap.end(); ++ceMapIt)
{
vtkIdVectorType edges = ceMapIt->second;
for (size_t i = 0; i < edges.size(); i++)
{
bool foundMatch = false;
ceMapIt1 = ceMap.find(edges[i]);
if (ceMapIt1 != ceMap.end())
{
for (size_t j = 0; j < ceMapIt1->second.size(); j++)
{
if (ceMapIt->first == ceMapIt1->second[j])
{
foundMatch = true;
break;
}
}
}
if (!foundMatch)
{
edges.erase(edges.begin()+i);
i--;
}
}
ceMapIt->second = edges;
}
// clean 0 or 1-connected contour from ceMap
for (ceMapIt = ceMap.begin(); ceMapIt != ceMap.end(); /*manual increment*/)
{
if (ceMapIt->second.size() >= 2)
{
++ceMapIt;
continue;
}
cpSetIt = cpSet.find(ceMapIt->first);
if (cpSetIt != cpSet.end())
{
cpSet.erase(cpSetIt);
}
if (ceMapIt != ceMap.begin())
{
vtkIdToIdVectorMapType::iterator tempIt = ceMapIt;
--ceMapIt;
ceMap.erase(tempIt);
++ceMapIt;
}
else
{
ceMap.erase(ceMapIt);
ceMapIt = ceMap.begin();
}
}
// set maxConnectivity.
for (ceMapIt = ceMap.begin(); ceMapIt != ceMap.end(); ++ceMapIt)
{
if (static_cast<int>(ceMapIt->second.size()) > maxConnectivity)
{
maxConnectivity = static_cast<int>(ceMapIt->second.size());
}
}
return maxConnectivity;
};
//----------------------------------------------------------------------------
// Use eigenvalues to determine the dimension of the input contour points.
// This chunk of code is mostly copied from vtkOBBTree::ComputeOBB()
// Function returns 0 if input is a single point, 1 if co-linear,
// 2 if co-planar, 3 if 3D. It also returns the center as well as the normal
// (the eigenvector with the smallest eigenvalue) of the input contour pointset.
static int CheckContourDimensions(vtkPoints* points, vtkIdType npts,
const vtkIdType * ptIds,
double * normal, double * center)
{
static const double eigenvalueRatioThresh = 1e-12; // was 0.001, but 1e-12 seems better: https://gitlab.kitware.com/paraview/paraview/issues/13490
if (npts < 3)
{
// Defensively return zeros here for normal and center.
if (normal)
{
normal[0] = 0.0;
normal[1] = 0.0;
normal[2] = 0.0;
}
if (center)
{
center[0] = 0.0;
center[1] = 0.0;
center[2] = 0.0;
}
return npts - 1;
}
vtkIdType i, j;
double x[3], mean[3], xp[3], *v[3], v0[3], v1[3], v2[3];
double *a[3], a0[3], a1[3], a2[3], eigValue[3];
// Compute mean
mean[0] = mean[1] = mean[2] = 0.0;
for (i=0; i < npts; i++ )
{
points->GetPoint(ptIds[i], x);
mean[0] += x[0];
mean[1] += x[1];
mean[2] += x[2];
}
for (i=0; i < 3; i++)
{
mean[i] /= npts;
}
// 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 (j = 0; j < npts; j++ )
{
points->GetPoint(ptIds[j], 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] /= npts;
a1[i] /= npts;
a2[i] /= npts;
}
// Extract axes (i.e., eigenvectors) from covariance matrix.
v[0] = v0; v[1] = v1; v[2] = v2;
vtkMath::Jacobi(a,eigValue,v);
int ret = 3;
if ((eigValue[2] / eigValue[0]) < eigenvalueRatioThresh)
{
ret--;
}
if ((eigValue[1] / eigValue[0]) < eigenvalueRatioThresh)
{
ret--;
}
if (normal)
{
for (i =0; i < 3; i++)
{
double norm = vtkMath::Norm(a[i], 3);
if (norm > 0.000001)
{
break;
}
}
if (i < 3)
{
normal[0] = v2[0];
normal[1] = v2[1];
normal[2] = v2[2];
}
else
{
points->GetPoint(ptIds[0], v0);
points->GetPoint(ptIds[1], v1);
v0[0] = v0[0] - mean[0];
v0[1] = v0[1] - mean[1];
v0[2] = v0[2] - mean[2];
v1[0] = v1[0] - mean[0];
v1[1] = v1[1] - mean[1];
v1[2] = v1[2] - mean[2];
vtkMath::Normalize(v0);
vtkMath::Normalize(v1);
vtkMath::Cross(v0, v1, normal);
vtkMath::Normalize(normal);
}
}
if (center)
{
center[0] = mean[0];
center[1] = mean[1];
center[2] = mean[2];
}
return ret;
};
//----------------------------------------------------------------------------
// For each contour point, compute the normal (pointing to the positive side),
// then sort the other contour points connected to it, such that the connecting
// edges are ordered contour-clockwise when viewed from the normal direction.
// Input ceMap shows that a contour point (map->first) is connected to a number
// of other contour points (map->second). It does not distinguish boundary
// edges from internal edges. The following function also update ceMap such that
// a boundary edge a-->b (assuming traversing from the counter-clockwise
// direction) is only stored once ({a, [b, ...]}). an internal edge a<-->b is
// stored twice ({a, [b, ...] and {b, [a, ...]}}.
// Current implementation of this function assumes planar contours, we only
// compute normal once and reuse it for all other contour points.
// TODO: for non-planar cut, need to compute normal for each contour point. We
// then project edges onto a tangent plane and sort them.
static void OrderMultiConnectedContourPoints(vtkIdToIdVectorMapType & cpMap,
vtkIdToIdVectorMapType & cpBackupMap,
vtkIdSetType & cpSet,
vtkPoints * points)
{
double p[3], x0[3], x1[3], e0[3], e1[3], nn[3];
vtkIdSetType::iterator setIt;
vtkIdVectorType pids;
for (setIt = cpSet.begin(); setIt != cpSet.end(); ++setIt)
{
pids.push_back(*setIt);
}
// return if the input contour points are 1D. Note: the function also
// computes normal n and center o.
double o[3], n[3];
if (CheckContourDimensions(
points, static_cast<vtkIdType>(pids.size()), &(pids[0]), n, o) < 2)
{
return;
}
vtkMath::Normalize(n);
// locate an extreme point in a direction normal to the normal. this
// extreme point is a convex vertex.
vtkIdToIdVectorMapType::iterator mapIt = cpMap.begin();
points->GetPoint(mapIt->first, p);
e0[0] = p[0] - o[0];
e0[1] = p[1] - o[1];
e0[2] = p[2] - o[2];
vtkMath::Normalize(e0);
vtkMath::Cross(e0, n, nn);
vtkMath::Normalize(nn);
double maxDistance = VTK_DOUBLE_MIN;
vtkIdType maxPid = -1;
for (; mapIt != cpMap.end(); ++mapIt)
{
points->GetPoint(mapIt->first, p);
e0[0] = p[0] - o[0];
e0[1] = p[1] - o[1];
e0[2] = p[2] - o[2];
double distance = vtkMath::Dot(nn, e0);
if (distance > maxDistance)
{
maxDistance = distance;
maxPid = mapIt->first;
}
}
// Order edges of the contour point contour-clockwise. Note that a boundary
// point has two boundary edges. We will remove the incoming boundary edge
// and store the outgoing boundary edge at the end (after all internal edges).
// incoming and outgoing boudnary edges are defined when they are traversed
// counter-clockwisely.
std::vector<double> extremePointAngles; // record the angles of extreme point
vtkIdVectorType edges;
size_t edgesSize = 0;
const double angleTolerance = 0.0000001;
for (mapIt = cpMap.begin(); mapIt != cpMap.end(); ++mapIt)
{
edges = mapIt->second;
edgesSize = edges.size();
// If the contour point is 2-connected we don't need to order them.
if (edgesSize >=3 || mapIt->first == maxPid)
{
// get the current first edge
points->GetPoint(mapIt->first, p);
points->GetPoint(edges[0], x0);
e0[0] = x0[0] - p[0];
e0[1] = x0[1] - p[1];
e0[2] = x0[2] - p[2];
vtkMath::Normalize(e0);
vtkMath::Cross(e0, n, x0);
vtkMath::Cross(n, x0, e0);
vtkMath::Normalize(e0);
// compute the angles from other edges to the first edge
std::vector<double> angles;
angles.push_back(0);
const double maxDotProduct = 0.95;
for (size_t i = 1; i < edgesSize; i++)
{
points->GetPoint(edges[i], x1);
e1[0] = x1[0] - p[0];
e1[1] = x1[1] - p[1];
e1[2] = x1[2] - p[2];
vtkMath::Normalize(e1);
vtkMath::Cross(e1, n, x1);
vtkMath::Cross(n, x1, e1);
vtkMath::Normalize(e1);
double dotproduct = vtkMath::Dot(e0, e1);
double angle = acos(dotproduct);
if (dotproduct < maxDotProduct && dotproduct > -maxDotProduct)
{
vtkMath::Cross(e0, e1, nn);
if (vtkMath::Dot(n, nn) < 0)
{
angle = 2.0*vtkMath::Pi() - angle;
}
}
else if (dotproduct > maxDotProduct)
{
vtkMath::Cross(e0, n, nn);
angle = acos(vtkMath::Dot(nn, e1)) - vtkMath::Pi()/2.0;
}
else if (dotproduct < -maxDotProduct)
{
vtkMath::Cross(n, e0, nn);
angle = acos(vtkMath::Dot(nn, e1)) + vtkMath::Pi()/2.0;
}
if (angle < -angleTolerance)
{
angle += 2.0*vtkMath::Pi();
}
if (angle > 2.0*vtkMath::Pi()+angleTolerance)
{
angle -= 2.0*vtkMath::Pi();
}
angles.push_back(angle);
}
// sort edges
for (size_t i = 1; i < edgesSize-1; i++)
{
for (size_t j = i+1; j < edgesSize; j++)
{
if (angles[i] > angles[j])
{
vtkIdType temp = edges[i];
edges[i] = edges[j];
edges[j] = temp;
double angle = angles[i];
angles[i] = angles[j];
angles[j] = angle;
}
}
}
mapIt->second = edges;
if (mapIt->first == maxPid)
{
extremePointAngles = angles;
}
}
}
// store the sorted map.
cpBackupMap = cpMap;
// find the incoming and outgoing boundary edges of the extreme point. we use
// the observation: if the outgoing boundary edge is chosen as the reference
// edge. the angle between all other edges and the outgoing boundary edges
// will be in [0, pi]. the incoming boundary edge will be the one that is
// previous to the outgoing boundary edge.
mapIt = cpMap.find(maxPid);
edges = mapIt->second;
edgesSize = edges.size();
if (extremePointAngles.size() != edgesSize)
{
cout << "The size of the edge array does not match the size of the "
"angle array. We should never get here." << endl;
return;
}
vtkIdType outBoundary = -1;
vtkIdType inBoundary = -1;
for (size_t i = 0; i < edgesSize; i++)
{
double angle0 = extremePointAngles[i];
size_t j = 0;
for (; j < edgesSize; j++)
{
double angle = extremePointAngles[j] - angle0;
if (angle < 0)
{
angle = angle + 2.0*vtkMath::Pi();
}
if (angle > vtkMath::Pi())
{
break;
}
}
if (j == edgesSize)
{
outBoundary = static_cast<vtkIdType>(i);
inBoundary = outBoundary - 1 < 0 ?
static_cast<vtkIdType>(edgesSize) - 1 : outBoundary - 1;
break;
}
}
vtkIdType prevPid = maxPid;
vtkIdType currPid = edges[outBoundary];
// remove incoming boundary edge.
edges.erase(edges.begin() + inBoundary);
cpMap.find(maxPid)->second = edges;
// traverse the contour graph to remove all incoming boundary edges.
while (currPid != maxPid)
{
edges = cpMap.find(currPid)->second;
edgesSize = edges.size();
size_t i;
bool foundPrevPid = false;
for (i = 0; i < edgesSize; i++)
{
if (edges[i] == prevPid)
{
inBoundary = static_cast<vtkIdType>(i);
outBoundary = inBoundary + 1 >= static_cast<vtkIdType>(edgesSize) ?
0 : inBoundary + 1;
foundPrevPid = true;
break;
}
}
if (!foundPrevPid) // traversing failed.
{
return;
}
prevPid = currPid;
currPid = edges[outBoundary];
edges.erase(edges.begin() + inBoundary);
cpMap.find(prevPid)->second = edges;
}
};
//-----------------------------------------------------------------------------
void OrderTwoConnectedContourPoints(vtkIdToIdVectorMapType & cpMap,
vtkIdToIdVectorMapType & cpBackupMap)
{
// backup the map.
cpBackupMap = cpMap;
// traverse edges
vtkIdToIdVectorMapType::iterator mapIt = cpMap.begin();
vtkIdVectorType edges = mapIt->second;
vtkIdType startPid = mapIt->first;
// choose one as incoming edge and one as outgoing edge
vtkIdType outBoundary = 0;
vtkIdType inBoundary = 1;
// find next point
vtkIdType prevPid = mapIt->first;
vtkIdType currPid = edges[outBoundary];
// remove incoming boundary edge.
edges.erase(edges.begin() + inBoundary);
cpMap.find(startPid)->second = edges;
// traverse the edge graph to remove all incoming boundary edges.
while (currPid != startPid)
{
mapIt = cpMap.find(currPid);
if (mapIt == cpMap.end())
{
cout << "Find an unexpected case. The input polyhedron cell may not be a "
<< "water tight or the polygonal faces may not be planar. Contouring "
<< "will continue, but this cell may not be processed correctly." << endl;
break;
}
edges = mapIt->second;
if (edges[0] == prevPid)
{
inBoundary = 0;
outBoundary = 1;
}
else
{
inBoundary = 1;
outBoundary = 0;
}
prevPid = currPid;
currPid = edges[outBoundary];
edges.erase(edges.begin() + inBoundary);
cpMap.find(prevPid)->second = edges;
}
};
//----------------------------------------------------------------------------
// This function is called when InternalContour() finds an unexpected case
// (typically caused by a non-watertight cell). In this case, we will ignore
// the existing edges between contours. Instead, simply order them as a polygon
// around the center point.
static int OrderDisconnectedContourPoints(vtkIdSetType & cpSet,
vtkPoints * points,
vtkIdVectorType & pointLabelVector,
vtkIdVectorType & polygon)
{
polygon.clear();
if (cpSet.empty())
{
return 0;
}
double x[3], e0[3], e[3], nn[3];
vtkIdSetType::iterator setIt;
for (setIt = cpSet.begin(); setIt != cpSet.end(); ++setIt)
{
polygon.push_back(*setIt);
}
// return if the input contour points are 1D. Note: the function also
// computes normal n and center o.
double o[3], n[3];
if (CheckContourDimensions(
points, static_cast<vtkIdType>(polygon.size()), &(polygon[0]), n, o) < 2)
{
return 0;
}
// make sure normal n points to the positive side
vtkIdType numPoints = static_cast<vtkIdType>(pointLabelVector.size());
for (vtkIdType i = 0; i < numPoints; i++)
{
if (pointLabelVector[i] == 1)
{
points->GetPoint(i, x);
e[0] = x[0] - o[0];
e[1] = x[1] - o[1];
e[2] = x[2] - o[2];
if (vtkMath::Dot(e, n) < 0)
{
n[0] = -n[0];
n[1] = -n[1];
n[2] = -n[2];
}
break;
}
else if (pointLabelVector[i] == -1)
{
points->GetPoint(i, x);
e[0] = x[0] - o[0];
e[1] = x[1] - o[1];
e[2] = x[2] - o[2];
if (vtkMath::Dot(e, n) > 0)
{
n[0] = -n[0];
n[1] = -n[1];
n[2] = -n[2];
}
break;
}
}
// now loop over contour points to order them.
std::vector<double> angles;
angles.push_back(0.0);
// choose to start from the first point
points->GetPoint(polygon[0], x);
e0[0] = x[0] - o[0];
e0[1] = x[1] - o[1];
e0[2] = x[2] - o[2];
vtkMath::Cross(e0, n, nn);
vtkMath::Cross(n, nn, e0);
vtkMath::Normalize(e0);
// compute the angles from other edges to the first edge
for (size_t i = 1; i < polygon.size(); i++)
{
points->GetPoint(polygon[i], x);
e[0] = x[0] - o[0];
e[1] = x[1] - o[1];
e[2] = x[2] - o[2];
vtkMath::Cross(e, n, nn);
vtkMath::Cross(n, nn, e);
vtkMath::Normalize(e);
const double maxDotProduct = 0.95;
double dotproduct = vtkMath::Dot(e0, e);
double angle = acos(dotproduct);
if (dotproduct < maxDotProduct && dotproduct > -maxDotProduct)
{
vtkMath::Cross(e0, e, nn);
if (vtkMath::Dot(n, nn) < 0)
{
angle += vtkMath::Pi();
}
}
else if (dotproduct > maxDotProduct)
{
vtkMath::Cross(e0, n, nn);
angle = acos(vtkMath::Dot(nn, e)) - vtkMath::Pi()/2.0;
}
else
{
vtkMath::Cross(n, e0, nn);
angle = acos(vtkMath::Dot(nn, e)) + vtkMath::Pi()/2.0;
}
angles.push_back(angle);
}
// sort contour points
for (size_t i = 1; i < polygon.size(); i++)
{
for (size_t j = i+1; j < polygon.size(); j++)
{
if (angles[i] > angles[j])
{
vtkIdType temp = polygon[i];
polygon[i] = polygon[j];
polygon[j] = temp;
double angle = angles[i];
angles[i] = angles[j];
angles[j] = angle;
}
}
}
return 1;
}
//----------------------------------------------------------------------------
// Note: the triangulation results are inserted into the input cellArray, which
// does not need to be empty.
void Triangulate3DContour(vtkIdType npts, vtkIdType * pts,
vtkCellArray *cellArray)
{
vtkIdType start = 0;
vtkIdType end = npts-1;
vtkIdType ids[3];
while (start < end)
{
ids[0] = pts[start++];
ids[1] = pts[start];
ids[2] = pts[end];
cellArray->InsertNextCell(3, ids);
if (start >= end - 1)
{
return;
}
ids[0] = pts[end];
ids[1] = pts[start];
ids[2] = pts[--end];
cellArray->InsertNextCell(3, ids);
}
};
}; //end vtkInternal class
//----------------------------------------------------------------------------
// Construct the hexahedron with eight points.
vtkPolyhedron::vtkPolyhedron()
{
this->Line = vtkLine::New();
this->Triangle = vtkTriangle::New();
this->Quad = vtkQuad::New();
this->Polygon = vtkPolygon::New();
this->Tetra = vtkTetra::New();
this->GlobalFaces = vtkIdTypeArray::New();
this->FaceLocations = vtkIdTypeArray::New();
this->PointIdMap = new vtkPointIdMap;
this->EdgesGenerated = 0;
this->EdgeTable = vtkEdgeTable::New();
this->Edges = vtkIdTypeArray::New();
this->Edges->SetNumberOfComponents(2);
this->EdgeFaces = vtkIdTypeArray::New();
this->EdgeFaces->SetNumberOfComponents(2);
this->FacesGenerated = 0;
this->Faces = vtkIdTypeArray::New();
this->BoundsComputed = 0;
this->PolyDataConstructed = 0;
this->PolyData = vtkPolyData::New();
this->Polys = vtkCellArray::New();
//this->Polys->Register(this);
//this->Polys->Delete();
this->PolyConnectivity = vtkIdTypeArray::New();
this->LocatorConstructed = 0;
this->CellLocator = vtkCellLocator::New();
this->CellIds = vtkIdList::New();
this->Cell = vtkGenericCell::New();
this->Internal = new vtkInternal();
}
//----------------------------------------------------------------------------
vtkPolyhedron::~vtkPolyhedron()
{
this->Line->Delete();
this->Triangle->Delete();
this->Quad->Delete();
this->Polygon->Delete();
this->Tetra->Delete();
this->GlobalFaces->Delete();
this->FaceLocations->Delete();
delete this->PointIdMap;
this->EdgeTable->Delete();
this->Edges->Delete();
this->EdgeFaces->Delete();
this->Faces->Delete();
this->PolyData->Delete();
this->Polys->Delete();
this->PolyConnectivity->Delete();
this->CellLocator->Delete();
this->CellIds->Delete();
this->Cell->Delete();
delete this->Internal;
}
//----------------------------------------------------------------------------
void vtkPolyhedron::ComputeBounds()
{
if ( this->BoundsComputed )
{
return;
}
this->Superclass::GetBounds(); //stored in this->Bounds
this->BoundsComputed = 1;
}
//----------------------------------------------------------------------------
void vtkPolyhedron::ConstructPolyData()
{
if (this->PolyDataConstructed)
{
return;
}
// Here's a trick, we're going to use the Faces array as the connectivity
// array. Note that the Faces have an added nfaces value at the beginning
// of the array. Other than that,it's a vtkCellArray. So we play games
// with the pointers.
this->GenerateFaces();
if (this->Faces->GetNumberOfTuples() == 0)
{
return;
}
this->PolyConnectivity->SetNumberOfTuples(this->Faces->GetMaxId()-1);
this->PolyConnectivity->
SetArray(this->Faces->GetPointer(1), this->Faces->GetMaxId()-1, 1);
this->Polys->SetNumberOfCells(*(this->Faces->GetPointer(0)));
this->Polys->
SetCells(*(this->Faces->GetPointer(0)), this->PolyConnectivity);
// Standard setup
this->PolyData->Initialize();
this->PolyData->SetPoints(this->Points);
this->PolyData->SetPolys(this->Polys);
this->PolyDataConstructed = 1;
}
vtkPolyData* vtkPolyhedron::GetPolyData()
{
if (!this->PolyDataConstructed)
{
this->ConstructPolyData();
}
return this->PolyData;
}
//----------------------------------------------------------------------------
void vtkPolyhedron::ConstructLocator()
{
if (this->LocatorConstructed)
{
return;
}
this->ConstructPolyData();
// With the polydata set up, we can assign it to the locator
this->CellLocator->Initialize();
this->CellLocator->SetDataSet(this->PolyData);
this->CellLocator->BuildLocator();
this->LocatorConstructed = 1;
}
//----------------------------------------------------------------------------
void vtkPolyhedron::ComputeParametricCoordinate(double x[3], double pc[3])
{
this->ComputeBounds();
double *bounds = this->Bounds;
pc[0] = (x[0] - bounds[0]) / (bounds[1] - bounds[0]);
pc[1] = (x[1] - bounds[2]) / (bounds[3] - bounds[2]);
pc[2] = (x[2] - bounds[4]) / (bounds[5] - bounds[4]);
}
//----------------------------------------------------------------------------
void vtkPolyhedron::
ComputePositionFromParametricCoordinate(double pc[3], double x[3])
{
this->ComputeBounds();
double *bounds = this->Bounds;
x[0] = ( 1 - pc[0] )* bounds[0] + pc[0] * bounds[1];
x[1] = ( 1 - pc[1] )* bounds[2] + pc[1] * bounds[3];
x[2] = ( 1 - pc[2] )* bounds[4] + pc[2] * bounds[5];
}
//----------------------------------------------------------------------------
// Should be called by GetCell() prior to any other method invocation and after the
// points, point ids, and faces have been loaded.
void vtkPolyhedron::Initialize()
{
// Clear out any remaining memory.
this->PointIdMap->clear();
// We need to create a reverse map from the point ids to their canonical cell
// ids. This is a fancy way of saying that we have to be able to rapidly go
// from a PointId[i] to the location i in the cell.
vtkIdType i, id, numPointIds = this->PointIds->GetNumberOfIds();
for (i=0; i < numPointIds; ++i)
{
id = this->PointIds->GetId(i);
(*this->PointIdMap)[id] = i;
}
// Edges have to be reset
this->EdgesGenerated = 0;
this->EdgeTable->Reset();
this->Edges->Reset();
this->EdgeFaces->Reset();
this->Faces->Reset();
// Polys have to be reset
this->Polys->Reset();
this->PolyConnectivity->Reset();
// Faces may need renumbering later. This means converting the face ids from
// global ids to local, canonical ids.
this->FacesGenerated = 0;
// No bounds have been computed as of yet.
this->BoundsComputed = 0;
// No supplemental geometric stuff created
this->PolyDataConstructed = 0;
this->LocatorConstructed = 0;
}
//----------------------------------------------------------------------------
int vtkPolyhedron::GetNumberOfEdges()
{
// Make sure edges have been generated.
if ( ! this->EdgesGenerated )
{
this->GenerateEdges();
}
return static_cast<int>(this->Edges->GetNumberOfTuples());
}
//----------------------------------------------------------------------------
// This method requires that GenerateEdges() is invoked beforehand.
vtkCell *vtkPolyhedron::GetEdge(int edgeId)
{
// Make sure edges have been generated.
if ( ! this->EdgesGenerated )
{
this->GenerateEdges();
}
// Make sure requested edge is within range
vtkIdType numEdges = this->Edges->GetNumberOfTuples();
if ( edgeId < 0 || edgeId >= numEdges )
{
return NULL;
}
// Return the requested edge
vtkIdType edge[2];
this->Edges->GetTypedTuple(edgeId,edge);
// Recall that edge tuples are stored in canonical numbering
for (int i=0; i<2; i++)
{
this->Line->PointIds->SetId(i,this->PointIds->GetId(edge[i]));
this->Line->Points->SetPoint(i,this->Points->GetPoint(edge[i]));
}
return this->Line;
}
//----------------------------------------------------------------------------
int vtkPolyhedron::GenerateEdges()
{
if ( this->EdgesGenerated )
{
return this->Edges->GetNumberOfTuples();
}
//check the number of faces and return if there aren't any
if ( this->GlobalFaces->GetNumberOfTuples() == 0 ||
this->GlobalFaces->GetValue(0) <= 0 )
{
return 0;
}
// Loop over all faces, inserting edges into the table
vtkIdType *faces = this->GlobalFaces->GetPointer(0);
vtkIdType nfaces = faces[0];
vtkIdType *face = faces + 1;
vtkIdType fid, i, edge[2], npts, edgeFaces[2], edgeId;
edgeFaces[1] = -1;
this->EdgeTable->InitEdgeInsertion(this->Points->GetNumberOfPoints(),1);
for (fid=0; fid < nfaces; ++fid)
{
npts = face[0];
for (i=1; i <= npts; ++i)
{
edge[0] = (*this->PointIdMap)[face[i]];
edge[1] = (*this->PointIdMap)[(i != npts ? face[i+1] : face[1])];
edgeFaces[0] = fid;
if ( (edgeId = this->EdgeTable->IsEdge(edge[0],edge[1])) == (-1) )
{
edgeId = this->EdgeTable->InsertEdge(edge[0],edge[1]);
this->Edges->InsertNextTypedTuple(edge);
this->EdgeFaces->InsertTypedTuple(edgeId,edgeFaces);
}
else
{
this->EdgeFaces->SetComponent(edgeId,1,fid);
}
}
face += face[0] + 1;
} //for all faces
// Okay all done
this->EdgesGenerated = 1;
return this->Edges->GetNumberOfTuples();
}
//----------------------------------------------------------------------------
int vtkPolyhedron::GetNumberOfFaces()
{
// Make sure faces have been generated.
if ( ! this->FacesGenerated )
{
this->GenerateFaces();
}
if (this->GlobalFaces->GetNumberOfTuples() == 0)
{
return 0;
}
return static_cast<int>(this->GlobalFaces->GetValue(0));
}
//----------------------------------------------------------------------------
void vtkPolyhedron::GenerateFaces()
{
if ( this->FacesGenerated )
{
return;
}
if (this->GlobalFaces->GetNumberOfTuples() == 0)
{
return;
}
// Basically we just run through the faces and change the global ids to the
// canonical ids using the PointIdMap.
this->Faces->SetNumberOfTuples(this->GlobalFaces->GetNumberOfTuples());
vtkIdType *gFaces = this->GlobalFaces->GetPointer(0);
vtkIdType *faces = this->Faces->GetPointer(0);
vtkIdType nfaces = gFaces[0]; faces[0] = nfaces;
vtkIdType *gFace = gFaces + 1;
vtkIdType *face = faces + 1;
vtkIdType fid, i, id, npts;
for (fid=0; fid < nfaces; ++fid)
{
npts = gFace[0];
face[0] = npts;
for (i=1; i <= npts; ++i)
{
id = (*this->PointIdMap)[gFace[i]];
face[i] = id;
}
gFace += gFace[0] + 1;
face += face[0] + 1;
} //for all faces
// Okay we've done the deed
this->FacesGenerated = 1;
}
//----------------------------------------------------------------------------
vtkCell *vtkPolyhedron::GetFace(int faceId)
{
if ( faceId < 0 || faceId >= this->GlobalFaces->GetValue(0) )
{
return NULL;
}
this->GenerateFaces();
// Okay load up the polygon
vtkIdType i, p, loc = this->FaceLocations->GetValue(faceId);
vtkIdType *face = this->GlobalFaces->GetPointer(loc);
this->Polygon->PointIds->SetNumberOfIds(face[0]);
this->Polygon->Points->SetNumberOfPoints(face[0]);
// grab faces in global id space
for (i=0; i < face[0]; ++i)
{
this->Polygon->PointIds->SetId(i,face[i+1]);
p = (*this->PointIdMap)[face[i+1]];
this->Polygon->Points->SetPoint(i,this->Points->GetPoint(p));
}
return this->Polygon;
}
//----------------------------------------------------------------------------
// Specify the faces for this cell.
void vtkPolyhedron::SetFaces(vtkIdType *faces)
{
// Set up face structure
this->GlobalFaces->Reset();
this->FaceLocations->Reset();
if (!faces)
{
return;
}
vtkIdType nfaces = faces[0];
this->FaceLocations->SetNumberOfValues(nfaces);
this->GlobalFaces->InsertNextValue(nfaces);
vtkIdType *face = faces + 1;
vtkIdType faceLoc = 1;
vtkIdType i, fid, npts;
for (fid=0; fid < nfaces; ++fid)
{
npts = face[0];
this->GlobalFaces->InsertNextValue(npts);
for (i=1; i<=npts; ++i)
{
this->GlobalFaces->InsertNextValue(face[i]);
}
this->FaceLocations->SetValue(fid,faceLoc);
faceLoc += face[0] + 1;
face = faces + faceLoc;
} //for all faces
}
//----------------------------------------------------------------------------
// Return the list of faces for this cell.
vtkIdType *vtkPolyhedron::GetFaces()
{
if (!this->GlobalFaces->GetNumberOfTuples())
{
return NULL;
}
return this->GlobalFaces->GetPointer(0);
}
//----------------------------------------------------------------------------
int vtkPolyhedron::IntersectWithLine(double p1[3], double p2[3], double tol,
double& tMin, double xMin[3],
double pc[3], int& subId)
{
// It's easiest if this is done in canonical space
this->GenerateFaces();
// Loop over all the faces, intersecting them in turn.
vtkIdType *face = this->Faces->GetPointer(0);
vtkIdType nfaces = *face++;
vtkIdType npts, i, fid, numHits=0;
double t=VTK_FLOAT_MAX;
double x[3];
tMin=VTK_FLOAT_MAX;
for (fid=0; fid < nfaces; ++fid)
{
npts = face[0];
vtkIdType hit = 0;
switch (npts)
{
case 3: //triangle
for (i=0; i<3; i++)
{
this->Triangle->Points->SetPoint(i,this->Points->GetPoint(face[i+1]));
this->Triangle->PointIds->SetId(i,face[i+1]);
}
hit = this->Triangle->IntersectWithLine(p1,p2,tol,t,x,pc,subId);
break;
case 4: //quad
for (i=0; i<4; i++)
{
this->Quad->Points->SetPoint(i,this->Points->GetPoint(face[i+1]));
this->Quad->PointIds->SetId(i,face[i+1]);
}
hit = this->Quad->IntersectWithLine(p1,p2,tol,t,x,pc,subId);
break;
default: //general polygon
this->Polygon->GetPoints()->SetNumberOfPoints(npts);
this->Polygon->GetPointIds()->SetNumberOfIds(npts);
for (i=0; i<npts; i++)
{
this->Polygon->Points->SetPoint(i,this->Points->GetPoint(face[i+1]));
this->Polygon->PointIds->SetId(i,face[i+1]);
}
hit = this->Polygon->IntersectWithLine(p1,p2,tol,t,x,pc,subId);
break;
}
// Update minimum hit
if ( hit )
{
numHits++;
if ( t < tMin )
{
tMin = t;
xMin[0] = x[0]; xMin[1] = x[1]; xMin[2] = x[2];
}
}
face += face[0] + 1;
}//for all faces
// Compute parametric coordinates
this->ComputeParametricCoordinate(xMin,pc);
return numHits;
}
#define VTK_MAX_ITER 10 //Maximum iterations for ray-firing
#define VTK_VOTE_THRESHOLD 3
//----------------------------------------------------------------------------
// Shoot random rays and count the number of intersections
int vtkPolyhedron::IsInside(double x[3], double tolerance)
{
// do a quick bounds check
this->ComputeBounds();
double *bounds = this->Bounds;
if ( x[0] < bounds[0] || x[0] > bounds[1] ||
x[1] < bounds[2] || x[1] > bounds[3] ||
x[2] < bounds[4] || x[2] > bounds[5])
{
return 0;
}
// It's easiest if these computations are done in canonical space
this->GenerateFaces();
// This algorithm is adaptive; if there are enough faces in this
// polyhedron, a cell locator is built to accelerate intersections.
// Otherwise brute force looping over cells is used.
vtkIdType *faceArray = this->Faces->GetPointer(0);
vtkIdType nfaces = *faceArray++;
if ( nfaces > 25 )
{
this->ConstructLocator();
}
// We need a length to normalize the computations
double length = sqrt( this->Superclass::GetLength2() );
// Perform in/out by shooting random rays. Multiple rays are fired
// to improve accuracy of the result.
//
// The variable iterNumber counts the number of rays fired and is
// limited by the defined variable VTK_MAX_ITER.
//
// The variable deltaVotes keeps track of the number of votes for
// "in" versus "out" of the surface. When deltaVotes > 0, more votes
// have counted for "in" than "out". When deltaVotes < 0, more votes
// have counted for "out" than "in". When the delta_vote exceeds or
// equals the defined variable VTK_VOTE_THRESHOLD, then the
// appropriate "in" or "out" status is returned.
//
double rayMag, ray[3], xray[3], t, pcoords[3], xint[3];
int i, numInts, iterNumber, deltaVotes, subId;
vtkIdType idx, numCells;
double tol = tolerance * length;
for (deltaVotes = 0, iterNumber = 1;
(iterNumber < VTK_MAX_ITER) && (abs(deltaVotes) < VTK_VOTE_THRESHOLD);
iterNumber++)
{
// Define a random ray to fire.
do
{
for (i=0; i<3; i++)
{
ray[i] = vtkMath::Random(-1.0,1.0);
}
rayMag = vtkMath::Norm(ray);
}
while (rayMag == 0.0);
// The ray must be appropriately sized wrt the bounding box. (It has to go
// all the way through the bounding box.)
for (i=0; i<3; i++)
{
xray[i] = x[i] + (length/rayMag)*ray[i];
}
// Intersect the line with each of the candidate cells
numInts = 0;
if ( this->LocatorConstructed )
{
// Retrieve the candidate cells from the locator
this->CellLocator->FindCellsAlongLine(x,xray,tol,this->CellIds);
numCells = this->CellIds->GetNumberOfIds();
for ( idx=0; idx < numCells; idx++ )
{
this->PolyData->GetCell(this->CellIds->GetId(idx), this->Cell);
if ( this->Cell->IntersectWithLine(x, xray, tol, t, xint, pcoords, subId) )
{
// Check for vertex, edge or face intersections
// count the number of 0 or 1 pcoords
int pcount = 0;
for (int p = 0; p < 3; ++p)
{
if (pcoords[p] == 0.0 || pcoords[p] == 1.0)
{
pcount++;
}
}
// pcount = 1, exact face intersection
// pcount = 2, exact edge intersection
// pcount = 3, exact vertex intersection
if (pcount == 0)
{
numInts++;
}
}
} //for all candidate cells
}
else
{
numCells = nfaces;
this->ConstructPolyData();
for ( idx=0; idx < numCells; idx++ )
{
this->PolyData->GetCell(idx, this->Cell);
if ( this->Cell->IntersectWithLine(x, xray, tol, t, xint, pcoords, subId) )
{
// Check for vertex, edge or face intersections
// count the number of 0 or 1 pcoords
int pcount = 0;
for (int p = 0; p < 3; ++p)
{
if (pcoords[p] == 0.0 || pcoords[p] == 1.0)
{
pcount++;
}
}
// pcount = 1, exact face intersection
// pcount = 2, exact edge intersection
// pcount = 3, exact vertex intersection
if (pcount == 0)
{
numInts++;
}
}
} //for all candidate cells
}
// Count the result
if ( numInts != 0 && (numInts % 2) == 0)
{
--deltaVotes;
}
else
{
++deltaVotes;
}
} //try another ray
// If the number of votes is positive, the point is inside
//
return ( deltaVotes < 0 ? 0 : 1 );
}
#undef VTK_MAX_ITER
#undef VTK_VOTE_THRESHOLD
//----------------------------------------------------------------------------
// Determine whether or not a polyhedron is convex. This method is adapted
// from Devillers et al., "Checking the Convexity of Polytopes and the
// Planarity of Subdivisions", Computational Geometry, Volume 11, Issues 3 – 4,
// December 1998, Pages 187 – 208.
bool vtkPolyhedron::IsConvex()
{
double x[2][3], n[3], c[3], c0[3], c1[3], c0p[3], c1p[3], n0[3], n1[3];
double n0p[3], n1p[3], np[3], tmp0, tmp1;
vtkIdType i, w[2], p0, p1, edgeId, edgeFaces[2], loc, v, *face, r=0;
const double eps = FLT_EPSILON;
std::vector<double> p(this->PointIds->GetNumberOfIds());
vtkIdVectorType d(this->PointIds->GetNumberOfIds(),0);
// initialization
this->GenerateEdges();
this->GenerateFaces();
this->ConstructPolyData();
this->ComputeBounds();
// loop over all edges in the polyhedron
this->EdgeTable->InitTraversal();
while ((edgeId = this->EdgeTable->GetNextEdge(w[0], w[1])) >= 0)
{
// get the global point ids
p0 = this->PointIds->GetId(w[0]);
p1 = this->PointIds->GetId(w[1]);
// get the edge points
this->Points->GetPoint(p0, x[0]);
this->Points->GetPoint(p1, x[1]);
// get the local face ids
this->EdgeFaces->GetTypedTuple(edgeId,edgeFaces);
// get the face vertex ids for the first face
loc = this->FaceLocations->GetValue(edgeFaces[0]);
face = this->GlobalFaces->GetPointer(loc);
// compute the centroid and normal for the first face
vtkPolygon::ComputeCentroid(this->Points, face[0], face + 1, c0);
vtkPolygon::ComputeNormal(this->Points, face[0], face + 1, n0);
// get the face vertex ids for the second face
loc = this->FaceLocations->GetValue(edgeFaces[1]);
face = this->GlobalFaces->GetPointer(loc);
// compute the centroid and normal for the second face
vtkPolygon::ComputeCentroid(this->Points, face[0], face + 1, c1);
vtkPolygon::ComputeNormal(this->Points, face[0], face + 1, n1);
// check for local convexity (the average of the two centroids must be
// "below" both faces, as defined by their outward normals).
for (i=0;i<3;i++)
{
c[i] = (c1[i] + c0[i])*.5;
c0p[i] = c[i] - c0[i];
c1p[i] = c[i] - c1[i];
}
if (vtkMath::Dot(n0,c0p) > 0. || vtkMath::Dot(n1,c1p) > 0.)
{
return false;
}
// check if the edge is a seam edge
// 1. the edge must not be vertical
// 2. the two faces must lie on the same side of a vertical plane
// 3. the upper face must not be vertical
// 1. simply check that the unit normal along the seam has x or y
// components
for (i=0;i<3;i++)
{
n[i] = x[1][i] - x[0][i];
}
vtkMath::Normalize(n);
if (std::abs(n[0]) < eps && std::abs(n[1]) < eps)
{
continue;
}
// 2. we need a plane through the seam and through a vector parallel to the
// z axis (or, more accurately, we need a vector perpendicular to this
// plane). So, we take a vector pointing from the centroid of the seam
// to the centroid of "higher" plane and remove the seam- and
// z-components from it.
for (i=0;i<3;i++)
{
c[i] = (x[1][i] + x[0][i])*.5;
n0p[i] = c0[i] - c[i];
n1p[i] = c1[i] - c[i];
}
vtkMath::Normalize(n0p);
vtkMath::Normalize(n1p);
memcpy(np, (n0p[2] > n1p[2] ? n0p : n1p), sizeof(np));
tmp0 = vtkMath::Dot(np,n);
np[0] -= n[0]*tmp0;
np[1] -= n[1]*tmp0;
np[2] = 0.;
// 3. if np has zero magnitude, then condition 3 is violated. Otherwise,
// we can use it to ensure condition 2.
if (std::abs(np[0]) < eps && std::abs(np[1]) < eps)
{
continue;
}
// if the vectors from the seam centroid to the face centroid are in the
// same direction relative to the plane, then condition 2 is satisfied.
tmp0 = vtkMath::Dot(np,n0p);
tmp1 = vtkMath::Dot(np,n1p);
if ((tmp0 < 0.) != (tmp1 < 0.))
{
continue;
}
// at this point, we know we have a seam edge. We now look at each vertex
// in the seam and determine whether or not it is a right-2-seam vertex. A
// convex polytope has exactly one right-2-seam vertex.
for (i = 0; i < 2; i++)
{
v = w[i];
// are there already 2 seams associated with this vertex? If so, then the
// projection of the polytope onto the x-y plane would have multiple seams
// emanating from the vertex => non-convex.
if (d[v] == 2)
{
return false;
}
// is this the first time that this vertex has been associated with a
// seam? If so, increment its seam count and record the x-coordinate of
// the adjacent edge vertex.
if (d[v] == 0)
{
d[v]++;
p[v] = x[(i+1)%2][0];
}
else
{
d[v]++;
// is v a right-2-seam vertex (i.e. is the x-value of v larger than the
// x-values of both u and p[v])?
if (x[i][0] > x[(i+1)%2][0] && x[i][0] > p[v])
{
// is this the first right-2-seam vertex?
if (r == 0)
{
r++;
}
else
{
return false;
}
}
}
}
}
return true;
}
//----------------------------------------------------------------------------
int vtkPolyhedron::CellBoundary(int vtkNotUsed(subId), double pcoords[3],
vtkIdList *pts)
{
double x[3], n[3], o[3], v[3];
double dist, minDist = VTK_DOUBLE_MAX;
vtkIdType numFacePts = -1;
vtkIdType * facePts = 0;
// compute coordinates
this->ComputePositionFromParametricCoordinate(pcoords, x);
vtkPolyhedronFaceIterator
faceIter(this->GetNumberOfFaces(), this->Faces->GetPointer(1));
while (faceIter.Id < faceIter.NumberOfPolygons)
{
if (faceIter.CurrentPolygonSize < 3)
{
vtkErrorMacro("Find a face with " << faceIter.CurrentPolygonSize <<
" vertices. Cannot return CellBoundary due to this degenerate case.");
break;
}
vtkPolygon::ComputeNormal(this->Points, faceIter.CurrentPolygonSize,
faceIter.Current, n);
vtkMath::Normalize(n);
this->Points->GetPoint(faceIter.Current[0], o);
v[0] = x[0] - o[0];
v[1] = x[1] - o[1];
v[2] = x[2] - o[2];
dist = fabs(vtkMath::Dot(v, n));
if (dist < minDist)
{
minDist = dist;
numFacePts = faceIter.CurrentPolygonSize;
facePts = faceIter.Current;
}
++faceIter;
}
pts->Reset();
if (numFacePts > 0)
{
for (vtkIdType i = 0; i < numFacePts; i++)
{
pts->InsertNextId(this->PointIds->GetId(facePts[i]));
}
}
// determine whether point is inside of polygon
if ( pcoords[0] >= 0.0 && pcoords[0] <= 1.0 &&
pcoords[1] >= 0.0 && pcoords[1] <= 1.0 &&
pcoords[2] >= 0.0 && pcoords[2] <= 1.0 &&
(this->IsInside(x, std::numeric_limits<double>::infinity())) )
{
return 1;
}
else
{
return 0;
}
}
//----------------------------------------------------------------------------
int vtkPolyhedron::EvaluatePosition( double x[3], double * closestPoint,
int & vtkNotUsed(subId), double pcoords[3],
double & minDist2, double * weights )
{
// compute parametric coordinates
this->ComputeParametricCoordinate(x, pcoords);
// construct polydata, the result is stored in this->PolyData,
// the cell array is stored in this->Polys
this->ConstructPolyData();
// Construct cell locator
this->ConstructLocator();
// find closest point and store the squared distance
vtkIdType cellId;
int id;
double cp[3];
this->Cell->Initialize();
this->CellLocator->FindClosestPoint(
x, cp, this->Cell, cellId, id, minDist2 );
if (closestPoint)
{
closestPoint[0] = cp[0];
closestPoint[1] = cp[1];
closestPoint[2] = cp[2];
}
// get the MVC weights
this->InterpolateFunctions(x, weights);
// set distance to be zero, if point is inside
int isInside = this->IsInside(x, std::numeric_limits<double>::infinity());
if (isInside)
{
minDist2 = 0.0;
}
return isInside;
}
//----------------------------------------------------------------------------
void vtkPolyhedron::EvaluateLocation( int & vtkNotUsed(subId), double pcoords[3],
double x[3], double * weights )
{
this->ComputePositionFromParametricCoordinate(pcoords, x);
this->InterpolateFunctions(x, weights);
}
//----------------------------------------------------------------------------
void vtkPolyhedron::Derivatives(int vtkNotUsed(subId), double pcoords[3],
double *values, int dim, double *derivs)
{
int i, j, k, idx;
for ( j = 0; j < dim; j++ )
{
for ( i = 0; i < 3; i++ )
{
derivs[j*dim + i] = 0.0;
}
}
static const double Sample_Offset_In_Parameter_Space = 0.01;
double x[4][3];
double coord[3];
//compute positions of point and three offset sample points
coord[0] = pcoords[0];
coord[1] = pcoords[1];
coord[2] = pcoords[2];
this->ComputePositionFromParametricCoordinate(coord, x[0]);
coord[0] += Sample_Offset_In_Parameter_Space;
this->ComputePositionFromParametricCoordinate(coord, x[1]);
coord[0] = pcoords[0];
coord[1] += Sample_Offset_In_Parameter_Space;
this->ComputePositionFromParametricCoordinate(coord, x[2]);
coord[1] = pcoords[1];
coord[2] += Sample_Offset_In_Parameter_Space;
this->ComputePositionFromParametricCoordinate(coord, x[3]);
coord[2] = pcoords[2];
this->ConstructPolyData();
int numVerts = this->PolyData->GetNumberOfPoints();
double *weights = new double[numVerts];
double *sample = new double[dim*4];
//for each sample point, sample data values
for ( idx = 0, k = 0; k < 4; k++ ) //loop over three sample points
{
this->InterpolateFunctions(x[k],weights);
for ( j = 0; j < dim; j++, idx++) //over number of derivates requested
{
sample[idx] = 0.0;
for ( i = 0; i < numVerts; i++ )
{
sample[idx] += weights[i] * values[j + i*dim];
}
}
}
double v1[3], v2[3], v3[3];
double l1, l2, l3;
//compute differences along the two axes
for ( i = 0; i < 3; i++ )
{
v1[i] = x[1][i] - x[0][i];
v2[i] = x[2][i] - x[0][i];
v3[i] = x[3][i] - x[0][i];
}
l1 = vtkMath::Normalize(v1);
l2 = vtkMath::Normalize(v2);
l3 = vtkMath::Normalize(v3);
//compute derivatives along x-y-z axes
double ddx, ddy, ddz;
for ( j = 0; j < dim; j++ )
{
ddx = (sample[ dim+j] - sample[j]) / l1;
ddy = (sample[2*dim+j] - sample[j]) / l2;
ddz = (sample[3*dim+j] - sample[j]) / l3;
//project onto global x-y-z axes
derivs[3*j] = ddx*v1[0] + ddy*v2[0] + ddz*v3[0];
derivs[3*j + 1] = ddx*v1[1] + ddy*v2[1] + ddz*v3[1];
derivs[3*j + 2] = ddx*v1[2] + ddy*v2[2] + ddz*v3[2];
}
delete [] weights;
delete [] sample;
}
//----------------------------------------------------------------------------
double *vtkPolyhedron::GetParametricCoords()
{
return NULL;
}
//----------------------------------------------------------------------------
void vtkPolyhedron::InterpolateFunctions(double x[3], double *sf)
{
// construct polydata, the result is stored in this->PolyData,
// the cell array is stored in this->Polys
this->ConstructPolyData();
// compute the weights
if (!this->PolyData->GetPoints())
{
return;
}
vtkMeanValueCoordinatesInterpolator::ComputeInterpolationWeights(
x, this->PolyData->GetPoints(), this->Polys, sf);
}
//----------------------------------------------------------------------------
void vtkPolyhedron::InterpolateDerivs(double x[3], double *derivs)
{
(void)x;
(void)derivs;
}
//----------------------------------------------------------------------------
int vtkPolyhedron::Triangulate(int vtkNotUsed(index), vtkIdList *ptIds,
vtkPoints *pts)
{
ptIds->Reset();
pts->Reset();
if (!this->GetPoints() || !this->GetNumberOfPoints())
{
return 0;
}
this->ComputeBounds();
// use ordered triangulator to triangulate the polyhedron.
vtkSmartPointer<vtkOrderedTriangulator> triangulator =
vtkSmartPointer<vtkOrderedTriangulator>::New();
triangulator->InitTriangulation(this->Bounds, this->GetNumberOfPoints());
triangulator->PreSortedOff();
double point[3];
for (vtkIdType i = 0; i < this->GetNumberOfPoints(); i++)
{
this->GetPoints()->GetPoint(i, point);
triangulator->InsertPoint(i, point, point, 0);
}
triangulator->Triangulate();
triangulator->AddTetras(0, ptIds, pts);
// convert to global Ids
vtkIdType* ids = ptIds->GetPointer(0);
for (vtkIdType i = 0; i < ptIds->GetNumberOfIds(); i++)
{
ids[i] = this->PointIds->GetId(ids[i]);
}
return 1;
}
//----------------------------------------------------------------------------
int vtkPolyhedron::IntersectWithContour(double value,
int insideOut,
vtkDataArray *inScalars)
{
bool allPositive = true;
bool allNegative = true;
// make the valueTolerance dependent on the range of
// values that is being contoured
double vMin(DBL_MAX), vMax(-DBL_MAX);
for (vtkIdType pid = 0; pid < this->Points->GetNumberOfPoints(); ++pid)
{
double v = inScalars->GetComponent(pid,0);
vMin = std::min(vMin, v);
vMax = std::max(vMax, v);
}
double valueTolerance = std::min(1e-6, 1e-6*(vMax - vMin));
for (vtkIdType pid = 0; pid < this->Points->GetNumberOfPoints(); ++pid)
{
double v = inScalars->GetComponent(pid,0);
if (v < value + valueTolerance)
{
allPositive = false;
}
else if (v > value - valueTolerance)
{
allNegative = false;
}
}
if ((allPositive && insideOut) || (allNegative && !insideOut))
{
return 2;
}
if (allPositive || allNegative)
{
return 1;
}
return 0;
}
//----------------------------------------------------------------------------
// Internal implementation of contouring algorithm
// NOTE: inScalars are in canonoical id space. while inPd are in global id space.
int vtkPolyhedron::InternalContour(double value,
int insideOut,
vtkIncrementalPointLocator *locator,
vtkDataArray *inScalars,
vtkDataArray *outScalars,
vtkPointData *inPd,
vtkPointData *outPd,
vtkCellArray *contourPolys,
vtkIdToIdVectorMapType & faceToPointsMap,
vtkIdToIdVectorMapType & pointToFacesMap,
vtkIdToIdMapType & pointIdMap)
{
// make the valueTolerance dependent on the range of
// values that is being contoured
double vMin(DBL_MAX), vMax(-DBL_MAX);
for (vtkIdType pid = 0; pid < this->Points->GetNumberOfPoints(); ++pid)
{
double v = inScalars->GetComponent(pid,0);
vMin = std::min(vMin, v);
vMax = std::max(vMax, v);
}
double valueTolerance = std::min(1e-6, 1e-6*(vMax - vMin));
double x0[3], x1[3], x[3];
double v0, v1, v, t;
vtkIdType p0, p1, pid, fid, outPid, globalP0, globalP1;
void * ptr = NULL;
pointToFacesMap.clear();
faceToPointsMap.clear();
pointIdMap.clear();
vtkIdVectorType pointLabelVector;
for (pid = 0; pid < this->Points->GetNumberOfPoints(); pid++)
{
v = inScalars->GetComponent(pid,0);
if (v < value + valueTolerance)
{
if (v > value - valueTolerance)
{
pointLabelVector.push_back(0);
}
else
{
pointLabelVector.push_back(-1);
}
}
else if (v > value - valueTolerance)
{
if (v < value + valueTolerance)
{
pointLabelVector.push_back(0);
}
else
{
pointLabelVector.push_back(1);
}
}
}
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points->DeepCopy(this->Points);
if (outScalars)
{
for (vtkIdType i = 0; i < inScalars->GetNumberOfTuples(); i++)
{
outScalars->InsertNextTuple1(inScalars->GetTuple1(i));
}
}
// construct a face to contour points map
vtkIdToIdVectorMapType faceToContourPointsMap;
vtkIdToIdVectorMapIteratorType vfMapIt, vfMapIt0, vfMapIt1;
vtkIdToIdVectorMapIteratorType fvMapIt, fcpMapIt, fcpMapItTemp;
// loop through all faces to construct PointToFacesMap and FaceToPointsMap
vtkPolyhedronFaceIterator
faceIter(this->Faces->GetValue(0), this->Faces->GetPointer(1));
while (faceIter.Id < faceIter.NumberOfPolygons)
{
// the rest code of this function assumes that a face contains at least
// three vertices. return if find a single-vertex or double-vertex face.
if (faceIter.CurrentPolygonSize < 3)
{
vtkErrorMacro("Find a face with " << faceIter.CurrentPolygonSize <<
" vertices. Contouring aborted due to this degenrate case.");
return -1;
}
fid = faceIter.Id;
vtkIdVectorType vVector;
for (vtkIdType i = 0; i < faceIter.CurrentPolygonSize; i++)
{
pid = faceIter.Current[i];
vfMapIt = pointToFacesMap.find(pid);
if (vfMapIt != pointToFacesMap.end())
{
vfMapIt->second.push_back(fid);
}
else
{
vtkIdVectorType fVector;
fVector.push_back(fid);
pointToFacesMap.insert(vtkIdToIdVectorPairType(pid, fVector));
}
vVector.push_back(pid);
}
faceToPointsMap.insert(vtkIdToIdVectorPairType(fid, vVector));
++faceIter;
}
// loop through all edges to find contour points and store them in the point
// locator. if the contour points are new (not overlap with any of original
// vertex), update PointToFacesMap, FaceToPointsMap and FaceToContourPointsMap.
vtkIdSetType cpSet; // contour point set
this->EdgeTable->InitTraversal();
while (this->EdgeTable->GetNextEdge(p0, p1, ptr))
{
// If both vertices are positive or negative, we do nothing and continue;
if ((pointLabelVector[p0] == 1 && pointLabelVector[p1] == 1) ||
(pointLabelVector[p0] == -1 && pointLabelVector[p1] == -1))
{
continue;
}
globalP0 = this->PointIds->GetId(p0);
globalP1 = this->PointIds->GetId(p1);
v0 = inScalars->GetComponent(p0,0);
v1 = inScalars->GetComponent(p1,0);
points->GetPoint(p0, x0);
points->GetPoint(p1, x1);
// If one or two of the vertices are contour points, we maintain the face
// to contour point map then continue
if (!pointLabelVector[p0] || !pointLabelVector[p1])
{
vtkIdType contourVertexIds[2];
contourVertexIds[0] = -1;
contourVertexIds[1] = -1;
if (pointLabelVector[p0] == 0)
{
if (cpSet.insert(p0).second) // check if the point already exist in set
{
if (locator->InsertUniquePoint(x0, outPid))
{
outPd->CopyData(inPd, globalP0, outPid);
}
pointIdMap.insert(vtkIdToIdPairType(p0, outPid));
contourVertexIds[0] = p0;
}
}
if (pointLabelVector[p1] == 0)
{
if (cpSet.insert(p1).second) // check if the point already exist in set
{
if (locator->InsertUniquePoint(x1, outPid))
{
outPd->CopyData(inPd, globalP1, outPid);
}
pointIdMap.insert(vtkIdToIdPairType(p1, outPid));
contourVertexIds[1] = p1;
}
}
for (int i = 0; i < 2; i++)
{
if (contourVertexIds[i] < 0)
{
continue;
}
vfMapIt = pointToFacesMap.find(contourVertexIds[i]);
if (vfMapIt == pointToFacesMap.end())
{
vtkErrorMacro("Cannot locate adjacent faces of a vertex. We should "
"never get here. Contouring continue but result maybe wrong.");
continue;
}
for (size_t k = 0; k < vfMapIt->second.size(); k++)
{
vtkIdType contourFaceId = vfMapIt->second[k];
fcpMapIt = faceToContourPointsMap.find(contourFaceId);
if (fcpMapIt != faceToContourPointsMap.end())
{
fcpMapIt->second.push_back(contourVertexIds[i]);
}
else
{
vtkIdVectorType contourPointVector;
contourPointVector.push_back(contourVertexIds[i]);
faceToContourPointsMap.insert(
vtkIdToIdVectorPairType(contourFaceId, contourPointVector));
}
}
}
continue;
}
// If two edge vertices are one positive and one negative. We need to
// insert new contour points on this edge.
t = (value - v0)/(v1 - v0);
x[0] = (1 - t) * x0[0] + t * x1[0];
x[1] = (1 - t) * x0[1] + t * x1[1];
x[2] = (1 - t) * x0[2] + t * x1[2];
pid = points->InsertNextPoint(x);
// update pointLabelVector: we know the pid will be the number of existing
// point (original verices plus previously inserted contour points)
pointLabelVector.push_back(0);
// update PointToFacesMap: there should be two and only two faces adjacent
// to the newly inserted contour point.
vfMapIt0 = pointToFacesMap.find(p0);
vfMapIt1 = pointToFacesMap.find(p1);
vtkIdVectorType fVector;
vtkIdVectorType fVector0 = vfMapIt0->second;
vtkIdVectorType fVector1 = vfMapIt1->second;
for (size_t i = 0; i < fVector0.size(); i++)
{
for (size_t j = 0; j < fVector1.size(); j++)
{
if (fVector0[i] == fVector1[j])
{
fVector.push_back(fVector0[i]);
}
}
}
if (fVector.size() != 2)
{
continue;
}
pointToFacesMap.insert(vtkIdToIdVectorPairType(pid, fVector));
// update FaceToPointsMap: insert the new point to the adjacent faces,
// but still need to keep the order
for (int k = 0; k < 2; k++)
{
fvMapIt = faceToPointsMap.find(fVector[k]);
this->Internal->InsertNewIdToIdVector(fvMapIt->second, pid, p0, p1);
}
// update FaceToContourPointsMap: insert the new point to the adjacent faces
for (int k = 0; k < 2; k++)
{
fcpMapIt = faceToContourPointsMap.find(fVector[k]);
if (fcpMapIt != faceToContourPointsMap.end())
{
fcpMapIt->second.push_back(pid);
}
else
{
vtkIdVectorType contourPointVector;
contourPointVector.push_back(pid);
faceToContourPointsMap.insert(
vtkIdToIdVectorPairType(fVector[k], contourPointVector));
}
}
// Maintain point data. only add to locator when it has never been added
// as contour point of previous processed cells.
if (locator->InsertUniquePoint(x, outPid) && outPd)
{
outPd->InterpolateEdge(inPd,outPid,globalP0,globalP1,t);
}
// A point unique to merge may not be unique to locator, since it may have
// been inserted to locator as contour point of previous processed cells.
if (outScalars)
{
outScalars->InsertTuple1(pid, value);
}
pointIdMap.insert(vtkIdToIdPairType(pid, outPid));
cpSet.insert(pid);
}
// Extract valid edges between contour points. We store edge information in a
// edge map ceMap. The key (first field) of ceMap is contour point Pd. The
// second field of ceMap is a vector of Ids of connected contour points. This
// process may remove point from cpSet if that point is only connected to one
// other contour point and therefore form a edge face.
vtkIdToIdVectorMapType ceMap; // edge map
int maxConnectivity = this->Internal->ExtractContourConnectivities(
ceMap, cpSet, pointLabelVector, pointToFacesMap,
faceToPointsMap, faceToContourPointsMap);
// special handling of point or line cases.
if (cpSet.size() < 3 || ceMap.size() < 3)
{
for (size_t i = 0; i < pointLabelVector.size(); i++)
{
if (pointLabelVector[i] == 1)
{
return insideOut ? 2 : 1;
}
if (pointLabelVector[i] == -1)
{
return insideOut ? 1 : 2;
}
}
return -1;
}
// The following process needs to know whether a contour point is boundary
// point and therefore contain two boundary edges.
// This information is important. As we traverse the edges to extract polygons
// contour edges only need to be traversed once, while internal edges need to
// be traversed twice.
// Note that in the simple case, where all contour points are 2-connected and
// all contour edges are boundary edges. The result contour only contains one
// single polygon. Otherwise, there are both boundary points (connected to two
// boundary edges and zero or one or multiple internal edges) and internal
// points (only connected to internal edges). The result contour contains
// multiple polygons. In the latter case, we will need to distinguish boundary
// contour points and interior contour points.
// ceMap only shows that a contour point (map->first) is connected to a number
// of other contour points (map->second). the following function computes
// the normal of the contour point (map->first) and then sorts the connected
// contour points (map->second) such that the connected edges are ordered
// counter-clockwise. the sorted edge graph is stored in ceBackupMap.
// the following function also distinguishes boundary edges from internal ones
// a boundary edge a-->b (assuming traversing from the counter-clockwise
// direction) is only stored once ({a, [b, ...]}). an internal edge a<-->b is
// stored twice ({a, [b, ...] and {b, [a, ...]}}. this graph is stored in
// the updated ceMap.
vtkIdToIdVectorMapType ceBackupMap;
if (maxConnectivity > 2)
{
this->Internal->OrderMultiConnectedContourPoints(ceMap, ceBackupMap,
cpSet, points);
}
else
{
this->Internal->OrderTwoConnectedContourPoints(ceMap, ceBackupMap);
}
// cpSet and ceMap defines the contour graph. We now need to travel through
// the graph to extract non-overlapping polygons. The polygons can share
// edges but none of them is a subset of another one.
// Here we use the order of the edges. Specifically, when a contour point
// is visited, we will choose the outgoing edge to be the edge previous to the
// incoming edge in the ceBackupMap.
std::vector<vtkIdVectorType> polygonVector;
vtkIdToIdVectorMapType::iterator ceMapIt, ceBackupMapIt;
vtkIdSetType::iterator cpSetIt = cpSet.end();
// backup ceMap. During graph travasal, we will remove edges from contour point
// which can mess up the ordering.
vtkIdSetType cpBackupSet = cpSet;
bool unexpectedCell = false;
while (!cpSet.empty())
{
vtkIdType startPid = *(cpSet.begin());
// check if the point still have untravelled outgoing edges.
ceMapIt = ceMap.find(startPid);
if (ceMapIt == ceMap.end())
{
cpSet.erase(cpSetIt);
continue;
}
vtkIdType currPid = startPid;
vtkIdType prevPid = -1;
vtkIdType nextPid = -1;
// vector to record points on a contour polygon
vtkIdVectorType cpLoop;
// continue to find the next contour point.
while (!cpLoop.empty() || prevPid == -1)
{
// when back to the start point, break the loop.
if (!cpLoop.empty() && currPid == startPid)
{
break;
}
cpSetIt = cpSet.find(currPid);
ceMapIt = ceMap.find(currPid);
// we should never arrive to a deadend.
if (ceMapIt == ceMap.end() || cpSetIt == cpSet.end())
{
unexpectedCell = true;
break;
}
// add current point to the polygon loop
cpLoop.push_back(currPid);
// get the current available outgoing edges
vtkIdVectorType edges = ceMapIt->second;
// choose the next point to travel. the outgoing edge is chosen to be the
// one previous to the incoming edge.
if (prevPid == -1)
{
nextPid = edges[0];
}
else
{
if (edges.size() == 1)
{
nextPid = edges[0];
}
else if (edges.size() == 2)
{
nextPid = edges[0] == prevPid ? edges[1] : edges[0];
}
else
{
vtkIdVectorType backupEdges = ceBackupMap.find(currPid)->second;
for (size_t i = 0; i < backupEdges.size(); i++)
{
if (backupEdges[i] == prevPid)
{
if (i == 0)
{
nextPid = backupEdges[backupEdges.size() - 1];
}
else
{
nextPid = backupEdges[i-1];
}
break;
}
}
}
}
// remove the outgoing edge
bool foundEdge = false;
for (size_t i = 0; i < edges.size(); i++)
{
if (edges[i] == nextPid)
{
foundEdge = true;
edges.erase(edges.begin()+i);
}
}
// the next edge shouldn't have been travelled and thus been removed
if (!foundEdge)
{
unexpectedCell = true;
break;
}
// removing point from ceMap and cpSet if all its edges have been visited.
if (edges.empty())
{
ceMap.erase(ceMapIt);
cpSet.erase(cpSetIt);
}
else
{
ceMapIt->second = edges;
}
// move on
prevPid = currPid;
currPid = nextPid;
nextPid = -1;
}// end_inner_while_loop
if (unexpectedCell)
{
//vtkWarningMacro("Find an unexpected case. The input polyhedron cell may "
//"not be a water tight cell. Or the contouring function is non-planar and "
//"intersects more than two edges and/or vertices on one face of the input "
//"polyhedron cell. Contouring will continue, but this cell will be not be "
//"processed.");
polygonVector.clear();
vtkIdVectorType polygon;
if (this->Internal->OrderDisconnectedContourPoints(cpBackupSet,
points, pointLabelVector, polygon))
{
polygonVector.push_back(polygon);
}
break;
}
if (!cpLoop.empty())
{
// record polygon loop.
polygonVector.push_back(cpLoop);
}
} // end_outer_while_loop
//
// Finally, add contour polygons to the output
for (size_t i = 0; i < polygonVector.size(); i++)
{
vtkIdVectorType polygon = polygonVector[i];
vtkIdType npts = static_cast<vtkIdType>(polygon.size());
vtkIdType *pts = &(polygon[0]);
if (npts < 3) // skip point or line contour
{
continue;
}
// check the dimensionality of the contour
int ret = this->Internal->
CheckContourDimensions(points, npts, pts, NULL, NULL);
if (ret <= 1) // skip single point or co-linear points
{
}
else if (ret == 2) // planar polygon, add directly
{
contourPolys->InsertNextCell(npts, pts);
}
else // 3D points, need to triangulate the original polygon
{
this->Internal->Triangulate3DContour(npts, pts, contourPolys);
}
}
return 0;
}
//----------------------------------------------------------------------------
void vtkPolyhedron::Contour(double value,
vtkDataArray *pointScalars,
vtkIncrementalPointLocator *locator,
vtkCellArray *verts,
vtkCellArray *lines,
vtkCellArray *polys,
vtkPointData *inPd, vtkPointData *outPd,
vtkCellData *inCd, vtkIdType cellId,
vtkCellData *outCd)
{
vtkIdToIdVectorMapType faceToPointsMap;
vtkIdToIdVectorMapType pointToFacesMap;
vtkIdToIdMapType pointIdMap; //local one, not this->PointIdMap
vtkIdType offset = 0;
if (verts)
{
offset += verts->GetNumberOfCells();
}
if (lines)
{
offset += lines->GetNumberOfCells();
}
// initialization
this->GenerateEdges();
this->GenerateFaces();
this->ConstructPolyData();
this->ComputeBounds();
if (this->IntersectWithContour(value, 0, pointScalars))
{
return;
}
this->Internal->RemoveDuplicatedPointsFromFaceArrayAndEdgeTable(
this->Points, this->Faces, this->EdgeTable, this->Bounds);
vtkSmartPointer<vtkCellArray> contourPolys =
vtkSmartPointer<vtkCellArray>::New();
int ret = this->InternalContour(value, 0, locator, pointScalars,
NULL, inPd, outPd, contourPolys,
faceToPointsMap, pointToFacesMap, pointIdMap);
if (ret != 0)
{
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
vtkIdType npts = 0;
vtkIdType *pts = 0;
contourPolys->InitTraversal();
while (contourPolys->GetNextCell(npts, pts))
{
if (!this->Internal->ConvertPointIds(npts, pts, pointIdMap))
{
vtkErrorMacro("Cannot find the id of an output point. We should never "
"get here. Contouring aborted.");
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
vtkIdType newCellId = offset + polys->InsertNextCell(npts, pts);
outCd->CopyData(inCd, cellId, newCellId);
}
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
}
//----------------------------------------------------------------------------
void vtkPolyhedron::Clip(double value,
vtkDataArray *pointScalars,
vtkIncrementalPointLocator *locator,
vtkCellArray *connectivity,
vtkPointData *inPd, vtkPointData *outPd,
vtkCellData *inCd, vtkIdType cellId,
vtkCellData *outCd, int insideOut)
{
vtkIdToIdVectorMapType faceToPointsMap;
vtkIdToIdVectorMapType pointToFacesMap;
vtkIdToIdMapType pointIdMap; //local one, not this->PointIdMap
vtkIdType newPid, newCellId;
vtkIdType npts = 0;
vtkIdType *pts = 0;
// initialization
this->GenerateEdges();
this->GenerateFaces();
this->ConstructPolyData();
this->ComputeBounds();
// vector to store cell connectivity
vtkIdVectorType cellVector;
// check if polyhedron is all in
if (this->IntersectWithContour(value, insideOut, pointScalars) == 1)
{
cellVector.push_back(this->Faces->GetValue(0));
// loop through all faces to add them into cellVector
vtkPolyhedronFaceIterator
faceIter(this->Faces->GetValue(0), this->Faces->GetPointer(1));
while (faceIter.Id < faceIter.NumberOfPolygons)
{
vtkIdVectorType pids;
for (vtkIdType i = 0; i < faceIter.CurrentPolygonSize; i++)
{
vtkIdType pid = faceIter.Current[i];
if (locator->InsertUniquePoint(this->Points->GetPoint(pid), newPid))
{
vtkIdType globalPid = this->PointIds->GetId(pid);
outPd->CopyData(inPd, globalPid, newPid);
}
pids.push_back(pid);
pointIdMap.insert(vtkIdToIdPairType(pid, newPid));
}
npts = static_cast<vtkIdType>(pids.size());
if (npts == 0)
{
++faceIter;
continue;
}
pts = &(pids[0]);
if (!this->Internal->ConvertPointIds(npts, pts, pointIdMap))
{
vtkErrorMacro("Cannot find the id of an output point. We should never "
"get here. Clipping aborted.");
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
cellVector.push_back(npts);
cellVector.insert(cellVector.end(), pts, pts+npts);
++faceIter;
}
if (!cellVector.empty())
{
newCellId = connectivity->InsertNextCell(
static_cast<vtkIdType>(cellVector.size()), &(cellVector[0]));
outCd->CopyData(inCd, cellId, newCellId);
}
return;
}
this->Internal->RemoveDuplicatedPointsFromFaceArrayAndEdgeTable(
this->Points, this->Faces, this->EdgeTable, this->Bounds);
vtkSmartPointer<vtkDoubleArray> contourScalars =
vtkSmartPointer<vtkDoubleArray>::New();
vtkSmartPointer<vtkCellArray> contourPolys =
vtkSmartPointer<vtkCellArray>::New();
int ret = this->InternalContour(value, insideOut, locator, pointScalars,
contourScalars, inPd, outPd, contourPolys,
faceToPointsMap, pointToFacesMap, pointIdMap);
// error occurs
if (ret == -1)
{
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
// polyhedron is all outside
if (ret == 2)
{
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
// polyhedron is all inside
// FIXME: Documentation needed:
// 1. How this can ever happen given the IntersectWithContour call above?
// 2. If it can happen, how+why is it different than the code above that
// copies the cell to the output?
if (ret == 1)
{
cellVector.push_back(this->Faces->GetValue(0));
// loop through all faces to add them into cellVector
vtkPolyhedronFaceIterator
faceIter(this->Faces->GetValue(0), this->Faces->GetPointer(1));
while (faceIter.Id < faceIter.NumberOfPolygons)
{
vtkIdVectorType pids;
for (vtkIdType i = 0; i < faceIter.CurrentPolygonSize; i++)
{
vtkIdType pid = faceIter.Current[i];
if (locator->InsertUniquePoint(this->Points->GetPoint(pid), newPid))
{
vtkIdType globalPid = this->PointIds->GetId(pid);
outPd->CopyData(inPd, globalPid, newPid);
}
pids.push_back(pid);
pointIdMap.insert(vtkIdToIdPairType(pid, newPid));
}
npts = static_cast<vtkIdType>(pids.size());
if (npts == 0)
{
if (faceIter.Id < faceIter.NumberOfPolygons - 1)
{
++faceIter;
continue;
}
else
{
break;
}
}
pts = &(pids[0]);
if (!this->Internal->ConvertPointIds(npts, pts, pointIdMap))
{
vtkErrorMacro("Cannot find the id of an output point. We should never "
"get here. Clipping aborted.");
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
cellVector.push_back(npts);
cellVector.insert(cellVector.end(), pts, pts+npts);
++faceIter;
}
if (!cellVector.empty())
{
newCellId = connectivity->InsertNextCell(
static_cast<vtkIdType>(cellVector.size()), &(cellVector[0]));
outCd->CopyData(inCd, cellId, newCellId);
}
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
// prepare visited array for all faces
bool* visited = new bool [this->Faces->GetValue(0)];
for (int i = 0; i < this->Faces->GetValue(0); i++)
{
visited[i] = false;
}
// make the valueTolerance dependent on the range of
// values that is being contoured
double vMin(DBL_MAX), vMax(-DBL_MAX);
for (vtkIdType pid = 0; pid < this->Points->GetNumberOfPoints(); ++pid)
{
double v = contourScalars->GetComponent(pid,0);
vMin = std::min(vMin, v);
vMax = std::max(vMax, v);
}
double valueTolerance = std::min(1e-6, 1e-6*(vMax - vMin));
// Main algorithm: go through all positive points (points on the right side
// of the contour). These do not include contour points.
// For each point on the right side, find all of its adjacent faces. There
// maybe two types of faces, (1) faces with all positive points, or
// (2) faces with positive negative and contour points. For case (1), we will
// keep the original face and add it into the result polyhedron. For case (2),
// we will subdivide the original face, and add the subface that includes
// positive points into the result polyhedron.
std::vector<vtkIdVectorType> faces;
vtkIdToIdVectorMapIteratorType pfMapIt, fpMapIt;
for (vtkIdType pid = 0; pid < this->Points->GetNumberOfPoints(); pid++)
{
// find if a point is a positive point
double v = contourScalars->GetComponent(pid,0);
if ( (insideOut && (v > value-valueTolerance)) || ((!insideOut) && (v < value+valueTolerance)) )
{
continue;
}
// find adjacent faces of the positive point
pfMapIt = pointToFacesMap.find(pid);
if (pfMapIt == pointToFacesMap.end())
{
continue;
}
vtkIdVectorType fids = pfMapIt->second;
// for each adjacent face
for (size_t i = 0; i < fids.size(); i++)
{
vtkIdType fid = fids[i];
if (visited[fid])
{
continue;
}
fpMapIt = faceToPointsMap.find(fid);
if (fpMapIt == faceToPointsMap.end())
{
vtkErrorMacro("Cannot locate points on a face. We should "
"never get here. Clipping continues but may generate wrong result.");
continue;
}
vtkIdVectorType pids = fpMapIt->second;
vtkIdType numFacePoints = static_cast<vtkIdType>(pids.size());
// locate the positive point inside the id vector.
vtkIdType positivePt = -1;
for (vtkIdType j = 0; j < numFacePoints; j++)
{
if (pid == pids[j])
{
positivePt = j;
break;
}
}
// positive point not found: this can happen when the current face
// has been partially visited before, and some points have been removed from
// its point vector.
if (positivePt < 0 || positivePt >= numFacePoints)
{
continue;
}
// a new id vector to hold ids of points on new surface patch
vtkIdVectorType newpids;
newpids.push_back(pid);
// step through the ajacent points on both sides of the positive point.
// stop when a contour point or a negative point is hit.
bool startFound = false;
bool endFound = false;
vtkIdType startPt = positivePt - 1;
vtkIdType endPt = positivePt + 1;
for (vtkIdType k = 0; k < numFacePoints; k++)
{
if (startFound && endFound)
{
break;
}
if (!startFound)
{
if (startPt < 0)
{
startPt = numFacePoints - 1;
}
newpids.insert(newpids.begin(), pids[startPt]);
v = contourScalars->GetComponent(pids[startPt],0);
if ((insideOut && (v > value-valueTolerance)) || ((!insideOut) && (v < value+valueTolerance)))
{
startFound = true;
if ((insideOut && (v > value+valueTolerance)) || ((!insideOut) && (v < value-valueTolerance)))
{
vtkWarningMacro("A positive point is directly connected to a "
"negative point with no contour point in between. We should "
"never get here.");
if (startPt == numFacePoints-1)
{
startPt = 0;
}
else
{
startPt++;
}
newpids.erase(newpids.begin());
}
}
else
{
startPt--;
}
}
if (!endFound)
{
if (endPt > numFacePoints - 1)
{
endPt = 0;
}
newpids.push_back(pids[endPt]);
v = contourScalars->GetComponent(pids[endPt],0);
if ((insideOut && (v > value-valueTolerance)) || ((!insideOut) && (v < value+valueTolerance)))
{
endFound = true;
if ((insideOut && (v > value+valueTolerance)) || ((!insideOut) && (v < value-valueTolerance)))
{
vtkWarningMacro("A positive point is directly connected to a "
"negative point with no contour point in between. We should "
"never get here.");
if (endPt == 0)
{
endPt = numFacePoints-1;
}
else
{
endPt--;
}
newpids.pop_back();
}
}
else
{
endPt++;
}
}
}// end inner for loop for finding start and end points
// if face are entirely positive, add it directly into the face list
if (!startFound && !endFound)
{
visited[fid] = true;
faces.push_back(pids);
}
// if face contain contour points
else if (startFound && endFound)
{
// a point or a line
if (newpids.size() < 3)
{
visited[fid] = true;
}
// if face only contains one contour point, this is a special case that
// may only happen when one of the original vertex is a contour point.
// we will add this face to the result polyhedron.
else if (startPt == endPt)
{
visited[fid] = true;
faces.push_back(pids);
}
// Face contain at least two contour points. In this case, we will create
// a new face patch whose close boundary is start point -->contour point
// --> end point --> start point. Notice that the face may contain other
// positive points and contour points. So we will not label the face as
// visited. Instead, we will erase the chunk from start point to end
// point from the point id vector of the face. So that the other part
// can still be visited in the future.
else
{
if (!this->Internal->EraseSegmentFromIdVector(
pids, positivePt, startPt, endPt))
{
vtkErrorMacro("Erase segment from Id vector failed. We should "
"never get here.");
visited[fid] = true;
continue;
}
if (pids.size()<=2) // all but two contour points are left
{
pids.clear();
visited[fid] = true;
}
fpMapIt->second = pids;
faces.push_back(newpids);
}
}
// only find start or only find end. this should never happen
else
{
visited[fid] = true;
vtkErrorMacro("We should never get here. Locating contour points failed. "
"Clipping continues but may generate wrong result.");
}
} // end for each face
} // end for_pid
delete [] visited;
// not a valid output when the clip plane passes through the cell boundary
// faces.
if (faces.empty())
{
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
vtkIdType numAllFaces = contourPolys->GetNumberOfCells() +
static_cast<vtkIdType>(faces.size());
cellVector.push_back(numAllFaces);
// add contour faces
contourPolys->InitTraversal();
while (contourPolys->GetNextCell(npts, pts))
{
if (!this->Internal->ConvertPointIds(npts, pts, pointIdMap, insideOut))
{
vtkErrorMacro("Cannot find the id of an output point. We should never "
"get here. Clipping aborted.");
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
cellVector.push_back(npts);
cellVector.insert(cellVector.end(), pts, pts+npts);
}
// add other faces
for (size_t i = 0; i < faces.size(); i++)
{
vtkIdVectorType pids = faces[i];
for (size_t j = 0; j < pids.size(); j++)
{
vtkIdType pid = pids[j];
vtkIdToIdMapType::iterator iter = pointIdMap.find(pid);
if (iter == pointIdMap.end()) // must be original points
{
if (locator->InsertUniquePoint(this->Points->GetPoint(pid), newPid))
{
vtkIdType globalPid = this->PointIds->GetId(pid);
outPd->CopyData(inPd, globalPid, newPid);
}
pointIdMap.insert(vtkIdToIdPairType(pid, newPid));
}
}
npts = static_cast<vtkIdType>(pids.size());
pts = &(pids[0]);
if (!this->Internal->ConvertPointIds(npts, pts, pointIdMap))
{
vtkErrorMacro("Cannot find the id of an output point. We should never "
"get here. Clipping aborted.");
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
return;
}
cellVector.push_back(npts);
cellVector.insert(cellVector.end(), pts, pts+npts);
}
newCellId = connectivity->InsertNextCell(
static_cast<vtkIdType>(cellVector.size()), &(cellVector[0]));
outCd->CopyData(inCd, cellId, newCellId);
this->Internal->RestoreFaceArrayAndEdgeTable(this->Faces, this->EdgeTable);
}
//----------------------------------------------------------------------------
void vtkPolyhedron::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "Triangle:\n";
this->Triangle->PrintSelf(os,indent.GetNextIndent());
os << indent << "Polygon:\n";
this->Polygon->PrintSelf(os,indent.GetNextIndent());
os << indent << "Tetra:\n";
this->Tetra->PrintSelf(os,indent.GetNextIndent());
os << indent << "Faces:\n";
this->GlobalFaces->PrintSelf(os,indent.GetNextIndent());
}