/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkOrderedTriangulator.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 "vtkOrderedTriangulator.h" #include "vtkCellArray.h" #include "vtkEdgeTable.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkTetra.h" #include "vtkUnstructuredGrid.h" #include "vtkHeap.h" #include "vtkDataArray.h" #include "vtkDoubleArray.h" #include "vtkPointLocator.h" #include "vtkPointData.h" #include "vtkCellData.h" #include #include #include #include #include vtkCxxRevisionMacro(vtkOrderedTriangulator, "$Revision: 1.2.4.1 $"); vtkStandardNewMacro(vtkOrderedTriangulator); #ifdef _WIN32_WCE # ifndef __PLACEMENT_NEW_INLINE # define __PLACEMENT_NEW_INLINE inline void *__cdecl operator new(size_t, void *_P) { return (_P); } # if _MSC_VER >= 1200 inline void __cdecl operator delete(void *, void *) { return; } # endif # endif #else # ifdef VTK_USE_ANSI_STDLIB # include # else # include # endif #endif // Old HP compiler does not support operator delete that is called // when a constructor called by operator new throws. // Might be supported in newer versions && (__HP_aCC <= 012100) #if defined(__HP_aCC) # define VTK_NO_PLACEMENT_DELETE #endif // SGI compiler does not support placement delete that is called when // a constructor called by placement new throws. #if defined(__sgi) && !defined(__GNUC__) # define VTK_NO_PLACEMENT_DELETE #endif // Classes are used to represent points, faces, and tetras------------------- // This data structure consists of points and tetras, with the face used // temporarily as a place holder during triangulation. struct OTPoint; struct OTFace; struct OTTetra; //---Class represents a point (and related typedefs)-------------------------- // Note that the points has two sets of coordinates: the first the actual // position X[3] and the second the coordinate used for performing // triangulation (usually a parametric coordinate P[3]). struct OTPoint { OTPoint() : Type(Inside), Id(0), SortId(0), SortId2(0), OriginalId(0), InsertionId(0) { this->X[0] = this->X[1] = this->X[2] = 0.0; this->P[0] = this->P[1] = this->P[2] = 0.0; } enum PointClassification {Inside=0,Outside=1,Boundary=2,Added=3,NoInsert=4}; PointClassification Type; double X[3]; //Actual position of point double P[3]; //Triangulation coordinate (typically parametric coordinate) //Id of originating point vtkIdType Id; //Id used to sort points prior to triangulation vtkIdType SortId; //Second id used to sort in triangulation //This can be used in situations where one id is not enough //(for example, when the id is related to an edge which // is described by two points) vtkIdType SortId2; //Id based on order seen in InsertPoint() vtkIdType OriginalId; //Id after sorting the points (i.e. order inserted into mesh) vtkIdType InsertionId; }; struct PointListType : public vtkstd::vector { PointListType() : vtkstd::vector() {} OTPoint* GetPointer(int ptId) {return &( *(this->begin()+ptId) ); } }; typedef PointListType::iterator PointListIterator; //---Class represents a face (and related typedefs)-------------------------- struct OTFace //used during tetra construction { void *operator new(size_t size, vtkHeap *heap) {return heap->AllocateMemory(size);} #if !defined(VTK_NO_PLACEMENT_DELETE) void operator delete(void*,vtkHeap*) {} #endif OTPoint *Points[3]; //the three points of the face OTTetra *Neighbor; double Normal[3]; double N2; void ComputePseudoNormal() { double v20[3], v10[3]; v20[0] = this->Points[2]->P[0] - this->Points[0]->P[0]; v20[1] = this->Points[2]->P[1] - this->Points[0]->P[1]; v20[2] = this->Points[2]->P[2] - this->Points[0]->P[2]; v10[0] = this->Points[1]->P[0] - this->Points[0]->P[0]; v10[1] = this->Points[1]->P[1] - this->Points[0]->P[1]; v10[2] = this->Points[1]->P[2] - this->Points[0]->P[2]; vtkMath::Cross(v10,v20,this->Normal); this->N2 = vtkMath::Dot(this->Normal,this->Normal); } int IsValidCavityFace(double X[3],double tol2) { double vp[3], d; vp[0] = X[0] - this->Points[0]->P[0]; vp[1] = X[1] - this->Points[0]->P[1]; vp[2] = X[2] - this->Points[0]->P[2]; d = vtkMath::Dot(vp,this->Normal); return ( (d > 0.0L && (d*d) > (tol2*this->N2)) ? 1 : 0 ); } }; typedef vtkstd::vector FaceListType; typedef vtkstd::vector::iterator FaceListIterator; //---Class represents a tetrahedron (and related typedefs)-------------------- typedef vtkstd::list TetraListType; typedef vtkstd::list::iterator TetraListIterator; struct TetraStackType : public vtkstd::stack { TetraStackType() : vtkstd::stack() {} void clear() {while (!this->empty()) this->pop();} }; typedef vtkstd::vector TetraQueueType; typedef vtkstd::vector::iterator TetraQueueIterator; struct OTTetra { void *operator new(size_t size, vtkHeap *heap) {return heap->AllocateMemory(size);} #if !defined(VTK_NO_PLACEMENT_DELETE) void operator delete(void*,vtkHeap*) {} #endif OTTetra() : Radius2(0.0L), CurrentPointId(-1), Type(OutsideCavity) { this->Center[0] = this->Center[1] = this->Center[2] = 0.0L; this->Points[0] = this->Points[1] = this->Points[2] = this->Points[3] = 0; this->Neighbors[0] = this->Neighbors[1] = this->Neighbors[2] = this->Neighbors[3] = 0; } // Center and radius squared of circumsphere of this tetra double Radius2; double Center[3]; // Note: there is a direct correlation between the points and the faces // i.e., the ordering of the points and face neighbors. OTTetra *Neighbors[4]; //the four face neighbors OTPoint *Points[4]; //the four points // The following are used during point insertion int CurrentPointId; //indicated current point being inserted enum TetraClassification {Inside=0,Outside=1,All=2,InCavity=3,OutsideCavity=4,Exterior=5}; TetraClassification Type; // Supporting triangulation operators void GetFacePoints(int i, OTFace *face); int InCircumSphere(double x[3]); TetraClassification DetermineType(); //inside, outside TetraListIterator ListIterator; //points to the list of tetras }; //---Class represents the Delaunay triangulation using points and tetras. // Additional support for the Delaunay triangulation process. struct vtkOTMesh { vtkOTMesh(vtkHeap *heap) : NumberOfTetrasClassifiedInside(0), NumberOfTemplates(0) { this->EdgeTable = vtkEdgeTable::New(); this->Heap = heap; } ~vtkOTMesh() { this->EdgeTable->Delete(); } PointListType Points; //Points in the mesh TetraListType Tetras; //Tetrahedra in the mesh FaceListType CavityFaces; //Faces forming an insertion cavity TetraQueueType VisitedTetras; //Those tetra already visited during insertion TetraStackType TetraStack; //Stack of tetra visited during point insertion TetraQueueType DegenerateQueue; //Tetra involved in degenerate triangulation vtkEdgeTable *EdgeTable; //Edges used to create triangulation of cavity double Tolerance2; //Used to control error vtkHeap *Heap; //Many allocations occur in efficent heap int NumberOfTetrasClassifiedInside; int NumberOfTemplates; TetraListIterator CurrentTetra; void Reset() { this->Points.clear(); this->Tetras.clear(); this->CavityFaces.clear(); this->VisitedTetras.clear(); this->TetraStack.clear(); this->DegenerateQueue.clear(); this->EdgeTable->Reset(); } OTTetra *CreateTetra(OTPoint *p, OTFace *face); OTTetra *WalkToTetra(OTTetra *t,double x[3],int depth,double bc[4]); int CreateInsertionCavity(OTPoint* p, OTTetra *tetra, double bc[4]); int ClassifyTetras(); void DumpInsertionCavity(double x[3]); }; //---Classes and typedefs used to support triangulation templates. // Triangulation templates are used instead of Delaunay triangulation // because they are so much faster. Because there are so many possible // triangulations/templates possible, triangulation templates are // computed on the fly and then cached. // // Two lists are kept. The first is a list of lists of templates for // each cell type. The second is a list of templates for each cell. // // A specific template. The number of tetras and the tetra connectivity. struct OTTemplate { vtkIdType NumberOfTetras; vtkIdType *Tetras; OTTemplate(vtkIdType numberOfTetras, vtkHeap *heap) { this->NumberOfTetras = numberOfTetras; this->Tetras = static_cast( heap->AllocateMemory(sizeof(vtkIdType)*numberOfTetras*4) ); } void *operator new(size_t size, vtkHeap *heap) {return heap->AllocateMemory(size);} #if !defined(VTK_NO_PLACEMENT_DELETE) void operator delete(void*,vtkHeap*) {} #endif }; // Typedefs for a list of templates for a particular cell. Key is the // template index. typedef vtkstd::map TemplateList; typedef vtkstd::map::iterator TemplateListIterator; // // Typedefs for a list of lists of templates keyed on cell type struct vtkOTTemplates : public vtkstd::map {}; typedef vtkstd::map::iterator TemplatesIterator; //------------------------------------------------------------------------ vtkOrderedTriangulator::vtkOrderedTriangulator() { // In place news (using allocators) are done here this->Heap = vtkHeap::New(); this->Heap->SetBlockSize(500000); this->Mesh = new vtkOTMesh(this->Heap); this->NumberOfPoints = 0; this->PreSorted = 0; this->UseTwoSortIds = 0; this->UseTemplates = 0; this->NumberOfCellPoints = 0; this->NumberOfCellEdges = 0; this->Templates = new vtkOTTemplates; this->TemplateHeap = vtkHeap::New(); this->TemplateHeap->SetBlockSize(250000); } //------------------------------------------------------------------------ vtkOrderedTriangulator::~vtkOrderedTriangulator() { delete this->Mesh; this->Heap->Delete(); TemplatesIterator titer; for (titer=this->Templates->begin(); titer != this->Templates->end(); ++titer) { delete (*titer).second; } delete this->Templates; this->TemplateHeap->Delete(); } //------------------------------------------------------------------------ void vtkOrderedTriangulator::InitTriangulation(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, int numPts) { double bounds[6]; bounds[0] = xmin; bounds[1] = xmax; bounds[2] = ymin; bounds[3] = ymax; bounds[4] = zmin; bounds[5] = zmax; this->InitTriangulation(bounds,numPts); // The templates remain valid and are reused. } //------------------------------------------------------------------------ void vtkOrderedTriangulator::InitTriangulation(double bounds[6], int numPts) { this->Heap->Reset(); this->Mesh->Reset(); this->NumberOfPoints = 0; this->MaximumNumberOfPoints = numPts; this->Mesh->Points.resize(numPts+6); for (int i=0; i<6; i++) { this->Bounds[i] = bounds[i]; } } //------------------------------------------------------------------------ // Create an initial bounding Delaunay triangulation consisting of four // tetras arranged in an octahedron. void vtkOrderedTriangulator::Initialize() { double length; double center[3]; double radius2; // Set up the internal data structures. Space for six extra points // is allocated for the bounding triangulation. int numPts = this->MaximumNumberOfPoints; double *bounds=this->Bounds; // Create the initial Delaunay triangulation which is a // bounding octahedron: 6 points & 4 tetra. center[0] = (bounds[0]+bounds[1])/2.0; center[1] = (bounds[2]+bounds[3])/2.0; center[2] = (bounds[4]+bounds[5])/2.0; length = 2.0 * sqrt( (radius2 = (bounds[1]-bounds[0])*(bounds[1]-bounds[0]) + (bounds[3]-bounds[2])*(bounds[3]-bounds[2]) + (bounds[5]-bounds[4])*(bounds[5]-bounds[4])) ); radius2 /= 2.0; this->Mesh->Tolerance2 = length*length*1.0e-10; // Define the points (-x,+x,-y,+y,-z,+z). Theses added points are // used to create a bounding octahedron. this->Mesh->Points[numPts].P[0] = center[0] - length; this->Mesh->Points[numPts].P[1] = center[1]; this->Mesh->Points[numPts].P[2] = center[2]; this->Mesh->Points[numPts].Id = numPts; this->Mesh->Points[numPts].InsertionId = numPts; this->Mesh->Points[numPts].Type = OTPoint::Added; this->Mesh->Points[numPts+1].P[0] = center[0] + length; this->Mesh->Points[numPts+1].P[1] = center[1]; this->Mesh->Points[numPts+1].P[2] = center[2]; this->Mesh->Points[numPts+1].Id = numPts + 1; this->Mesh->Points[numPts+1].InsertionId = numPts + 1; this->Mesh->Points[numPts+1].Type = OTPoint::Added; this->Mesh->Points[numPts+2].P[0] = center[0]; this->Mesh->Points[numPts+2].P[1] = center[1] - length; this->Mesh->Points[numPts+2].P[2] = center[2]; this->Mesh->Points[numPts+2].Id = numPts + 2; this->Mesh->Points[numPts+2].InsertionId = numPts + 2; this->Mesh->Points[numPts+2].Type = OTPoint::Added; this->Mesh->Points[numPts+3].P[0] = center[0]; this->Mesh->Points[numPts+3].P[1] = center[1] + length; this->Mesh->Points[numPts+3].P[2] = center[2]; this->Mesh->Points[numPts+3].Id = numPts + 3; this->Mesh->Points[numPts+3].InsertionId = numPts + 3; this->Mesh->Points[numPts+3].Type = OTPoint::Added; this->Mesh->Points[numPts+4].P[0] = center[0]; this->Mesh->Points[numPts+4].P[1] = center[1]; this->Mesh->Points[numPts+4].P[2] = center[2] - length; this->Mesh->Points[numPts+4].Id = numPts + 4; this->Mesh->Points[numPts+4].InsertionId = numPts + 4; this->Mesh->Points[numPts+4].Type = OTPoint::Added; this->Mesh->Points[numPts+5].P[0] = center[0]; this->Mesh->Points[numPts+5].P[1] = center[1]; this->Mesh->Points[numPts+5].P[2] = center[2] + length; this->Mesh->Points[numPts+5].Id = numPts + 5; this->Mesh->Points[numPts+5].InsertionId = numPts + 5; this->Mesh->Points[numPts+5].Type = OTPoint::Added; // Create bounding tetras (there are four) as well as the associated faces // They all share the same center and radius OTTetra *tetras[4]; for (int i=0; i<4; ++i) { tetras[i] = new(this->Heap) OTTetra(); this->Mesh->Tetras.push_front(tetras[i]); tetras[i]->ListIterator = this->Mesh->Tetras.begin(); tetras[i]->Center[0] = center[0]; tetras[i]->Center[1] = center[1]; tetras[i]->Center[2] = center[2]; tetras[i]->Radius2 = radius2; } //Okay now set up the points and neighbors in the tetras tetras[0]->Points[0] = this->Mesh->Points.GetPointer(numPts + 0); tetras[0]->Points[1] = this->Mesh->Points.GetPointer(numPts + 2); tetras[0]->Points[2] = this->Mesh->Points.GetPointer(numPts + 4); tetras[0]->Points[3] = this->Mesh->Points.GetPointer(numPts + 5); tetras[0]->Neighbors[0] = 0; //outside tetras[0]->Neighbors[1] = tetras[1]; tetras[0]->Neighbors[2] = tetras[3]; tetras[0]->Neighbors[3] = 0; tetras[1]->Points[0] = this->Mesh->Points.GetPointer(numPts + 2); tetras[1]->Points[1] = this->Mesh->Points.GetPointer(numPts + 1); tetras[1]->Points[2] = this->Mesh->Points.GetPointer(numPts + 4); tetras[1]->Points[3] = this->Mesh->Points.GetPointer(numPts + 5); tetras[1]->Neighbors[0] = 0; tetras[1]->Neighbors[1] = tetras[2]; tetras[1]->Neighbors[2] = tetras[0]; tetras[1]->Neighbors[3] = 0; tetras[2]->Points[0] = this->Mesh->Points.GetPointer(numPts + 1); tetras[2]->Points[1] = this->Mesh->Points.GetPointer(numPts + 3); tetras[2]->Points[2] = this->Mesh->Points.GetPointer(numPts + 4); tetras[2]->Points[3] = this->Mesh->Points.GetPointer(numPts + 5); tetras[2]->Neighbors[0] = 0; tetras[2]->Neighbors[1] = tetras[3]; tetras[2]->Neighbors[2] = tetras[1]; tetras[2]->Neighbors[3] = 0; tetras[3]->Points[0] = this->Mesh->Points.GetPointer(numPts + 3); tetras[3]->Points[1] = this->Mesh->Points.GetPointer(numPts + 0); tetras[3]->Points[2] = this->Mesh->Points.GetPointer(numPts + 4); tetras[3]->Points[3] = this->Mesh->Points.GetPointer(numPts + 5); tetras[3]->Neighbors[0] = 0; tetras[3]->Neighbors[1] = tetras[0]; tetras[3]->Neighbors[2] = tetras[2]; tetras[3]->Neighbors[3] = 0; } //------------------------------------------------------------------------ // Add a point to the list of points to be triangulated. vtkIdType vtkOrderedTriangulator::InsertPoint(vtkIdType id, double x[3], double p[3], int type) { vtkIdType idx = this->NumberOfPoints++; if ( idx >= this->MaximumNumberOfPoints ) { vtkErrorMacro(<< "Trying to insert more points than specified max="<MaximumNumberOfPoints<<" idx="<Mesh->Points[idx].Id = id; this->Mesh->Points[idx].SortId = id; this->Mesh->Points[idx].SortId2 = -1; this->Mesh->Points[idx].OriginalId = idx; this->Mesh->Points[idx].InsertionId = -1; //dummy value until inserted this->Mesh->Points[idx].X[0] = x[0]; this->Mesh->Points[idx].X[1] = x[1]; this->Mesh->Points[idx].X[2] = x[2]; this->Mesh->Points[idx].P[0] = p[0]; this->Mesh->Points[idx].P[1] = p[1]; this->Mesh->Points[idx].P[2] = p[2]; this->Mesh->Points[idx].Type = (OTPoint::PointClassification) type; return idx; } //------------------------------------------------------------------------ // Add a point to the list of points to be triangulated. vtkIdType vtkOrderedTriangulator::InsertPoint(vtkIdType id, vtkIdType sortid, double x[3], double p[3], int type) { vtkIdType idx = this->NumberOfPoints++; if ( idx >= this->MaximumNumberOfPoints ) { vtkErrorMacro(<< "Trying to insert more points than specified"); return idx; } this->Mesh->Points[idx].Id = id; this->Mesh->Points[idx].SortId = sortid; this->Mesh->Points[idx].SortId2 = -1; this->Mesh->Points[idx].OriginalId = idx; this->Mesh->Points[idx].InsertionId = -1; //dummy value until inserted this->Mesh->Points[idx].X[0] = x[0]; this->Mesh->Points[idx].X[1] = x[1]; this->Mesh->Points[idx].X[2] = x[2]; this->Mesh->Points[idx].P[0] = p[0]; this->Mesh->Points[idx].P[1] = p[1]; this->Mesh->Points[idx].P[2] = p[2]; this->Mesh->Points[idx].Type = (OTPoint::PointClassification) type; return idx; } //------------------------------------------------------------------------ // Add a point to the list of points to be triangulated. vtkIdType vtkOrderedTriangulator::InsertPoint(vtkIdType id, vtkIdType sortid, vtkIdType sortid2, double x[3], double p[3], int type) { vtkIdType idx = this->NumberOfPoints++; if ( idx >= this->MaximumNumberOfPoints ) { vtkErrorMacro(<< "Trying to insert more points than specified"); return idx; } this->Mesh->Points[idx].Id = id; this->Mesh->Points[idx].SortId = sortid; this->Mesh->Points[idx].SortId2 = sortid2; this->Mesh->Points[idx].OriginalId = idx; this->Mesh->Points[idx].InsertionId = -1; //dummy value until inserted this->Mesh->Points[idx].X[0] = x[0]; this->Mesh->Points[idx].X[1] = x[1]; this->Mesh->Points[idx].X[2] = x[2]; this->Mesh->Points[idx].P[0] = p[0]; this->Mesh->Points[idx].P[1] = p[1]; this->Mesh->Points[idx].P[2] = p[2]; this->Mesh->Points[idx].Type = (OTPoint::PointClassification) type; return idx; } //------------------------------------------------------------------------ // Used when an already inserted point must have its classification changed // (e.g., an intersection point is very near another point). void vtkOrderedTriangulator::UpdatePointType(vtkIdType internalId, int type) { assert("pre: valid_range" && internalId>=0 && internalIdNumberOfPoints); this->Mesh->Points[internalId].Type = (OTPoint::PointClassification) type; } //------------------------------------------------------------------------ double *vtkOrderedTriangulator::GetPointPosition(vtkIdType internalId) { assert("pre: valid_range" && internalId>=0 && internalIdNumberOfPoints); return this->Mesh->Points[internalId].P; } //------------------------------------------------------------------------ double *vtkOrderedTriangulator::GetPointLocation(vtkIdType internalId) { assert("pre: valid_range" && internalId>=0 && internalIdNumberOfPoints); return this->Mesh->Points[internalId].X; } //------------------------------------------------------------------------ vtkIdType vtkOrderedTriangulator::GetPointId(vtkIdType internalId) { assert("pre: valid_range" && internalId>=0 && internalIdNumberOfPoints); return this->Mesh->Points[internalId].Id; } //------------------------------------------------------------------------ // For a particular tetra and given a face id, return the three points // defining the face. void OTTetra::GetFacePoints(int i, OTFace *face) { // The order is carefully choosen to produce a tetrahedron // that is not inside out; i.e., the ordering produces a positive // Jacobian (computed from first three points points to fourth). switch (i) { case 0: face->Points[0] = this->Points[0]; face->Points[1] = this->Points[3]; face->Points[2] = this->Points[1]; break; case 1: face->Points[0] = this->Points[1]; face->Points[1] = this->Points[3]; face->Points[2] = this->Points[2]; break; case 2: face->Points[0] = this->Points[0]; face->Points[1] = this->Points[2]; face->Points[2] = this->Points[3]; break; case 3: face->Points[0] = this->Points[0]; face->Points[1] = this->Points[1]; face->Points[2] = this->Points[2]; break; } face->ComputePseudoNormal(); } //------------------------------------------------------------------------ // Routines used to sort the points based on id. extern "C" { #ifdef _WIN32_WCE int __cdecl vtkSortOnIds(const void *val1, const void *val2) #else int vtkSortOnIds(const void *val1, const void *val2) #endif { if (((OTPoint *)val1)->SortId < ((OTPoint *)val2)->SortId) { return (-1); } else if (((OTPoint *)val1)->SortId > ((OTPoint *)val2)->SortId) { return (1); } else { return (0); } } } extern "C" { #ifdef _WIN32_WCE int __cdecl vtkSortOnTwoIds(const void *val1, const void *val2) #else int vtkSortOnTwoIds(const void *val1, const void *val2) #endif { if (((OTPoint *)val1)->SortId2 < ((OTPoint *)val2)->SortId2) { return (-1); } else if (((OTPoint *)val1)->SortId2 > ((OTPoint *)val2)->SortId2) { return (1); } if (((OTPoint *)val1)->SortId < ((OTPoint *)val2)->SortId) { return (-1); } else if (((OTPoint *)val1)->SortId > ((OTPoint *)val2)->SortId) { return (1); } else { return (0); } } } //------------------------------------------------------------------------ // See whether point is in sphere of tetrahedron. int OTTetra::InCircumSphere(double x[3]) { double dist2; // check if inside/outside circumsphere dist2 = (x[0] - this->Center[0]) * (x[0] - this->Center[0]) + (x[1] - this->Center[1]) * (x[1] - this->Center[1]) + (x[2] - this->Center[2]) * (x[2] - this->Center[2]); return (dist2 < (0.999999L * this->Radius2) ? 1 : 0); } //------------------------------------------------------------------------ // Determine the classification of a tetra based on point types. inline OTTetra::TetraClassification OTTetra::DetermineType() { if ( (this->Points[0]->Type == OTPoint::Inside || this->Points[0]->Type == OTPoint::Boundary ) && (this->Points[1]->Type == OTPoint::Inside || this->Points[1]->Type == OTPoint::Boundary ) && (this->Points[2]->Type == OTPoint::Inside || this->Points[2]->Type == OTPoint::Boundary ) && (this->Points[3]->Type == OTPoint::Inside || this->Points[3]->Type == OTPoint::Boundary ) ) { this->Type = OTTetra::Inside; return OTTetra::Inside; } else if ( (this->Points[0]->Type == OTPoint::Outside || this->Points[0]->Type == OTPoint::Boundary ) && (this->Points[1]->Type == OTPoint::Outside || this->Points[1]->Type == OTPoint::Boundary ) && (this->Points[2]->Type == OTPoint::Outside || this->Points[2]->Type == OTPoint::Boundary ) && (this->Points[3]->Type == OTPoint::Outside || this->Points[3]->Type == OTPoint::Boundary ) ) { this->Type = OTTetra::Outside; return OTTetra::Outside; } else { this->Type = OTTetra::Exterior; return OTTetra::Exterior; } } //------------------------------------------------------------------------ // Determine whether the point is used by a specified tetra. inline static int IsAPoint(OTTetra *t, vtkIdType id) { if ( id == t->Points[0]->InsertionId || id == t->Points[1]->InsertionId || id == t->Points[2]->InsertionId || id == t->Points[3]->InsertionId ) { return 1; } else { return 0; } } //------------------------------------------------------------------------ // Given two tetra face neighbors, assign the neighbor pointers to each tetra. static void AssignNeighbors(OTTetra* t1, OTTetra* t2) { static int CASE_MASK[4] = {1,2,4,8}; int i, index; for (i=0, index=0; i<4; ++i) { if (IsAPoint(t2,t1->Points[i]->InsertionId) ) { index |= CASE_MASK[i]; } } switch (index) { case 11: t1->Neighbors[0] = t2; break; case 14: t1->Neighbors[1] = t2; break; case 13: t1->Neighbors[2] = t2; break; case 7: t1->Neighbors[3] = t2; break; default: vtkGenericWarningMacro(<<"Really bad"); } for (i=0, index=0; i<4; ++i) { if (IsAPoint(t1,t2->Points[i]->InsertionId) ) { index |= CASE_MASK[i]; } } switch (index) { case 11: t2->Neighbors[0] = t1; break; case 14: t2->Neighbors[1] = t1; break; case 13: t2->Neighbors[2] = t1; break; case 7: t2->Neighbors[3] = t1; break; default: vtkGenericWarningMacro(<<"Really bad"); } } //------------------------------------------------------------------------ // Instantiate and initialize a tetra. OTTetra *vtkOTMesh::CreateTetra(OTPoint *p, OTFace *face) { OTTetra *tetra = new(this->Heap) OTTetra; this->Tetras.push_front(tetra); tetra->ListIterator = this->Tetras.begin(); tetra->Radius2 = vtkTetra::Circumsphere(p->P, face->Points[0]->P, face->Points[1]->P, face->Points[2]->P, tetra->Center); // the order is carefully choosen to produce a tetrahedron // that is not inside out; i.e., the ordering produces a positive // jacobian (normal computed from first three points points to fourth). tetra->Points[0] = face->Points[0]; tetra->Points[1] = face->Points[1]; tetra->Points[2] = face->Points[2]; tetra->Points[3] = p; if ( face->Neighbor ) { AssignNeighbors(tetra,face->Neighbor); } return tetra; } //------------------------------------------------------------------------ // We start with a point that is inside a tetrahedron. We find face // neighbors of the tetrahedron that also contain the point. The // process continues recursively until no more tetrahedron are found. // Faces that lie between a tetrahedron that is in the cavity and one // that is not form the cavity boundary, these are kept track of in // a list. Eventually the point and boundary faces form new tetrahedra. int vtkOTMesh::CreateInsertionCavity(OTPoint* p, OTTetra *initialTet, double [4]) { // Prepare to insert deleted tetras and cavity faces // this->CavityFaces.clear(); //cavity face boundary this->VisitedTetras.clear(); //tetras involved in creating cavity this->TetraStack.clear(); //queue of tetras being processed this->DegenerateQueue.clear(); //queue of tetras that have degenerate faces this->TetraStack.push(initialTet); initialTet->Type = OTTetra::InCavity; //the seed of the cavity initialTet->CurrentPointId = p->InsertionId; //mark visited this->VisitedTetras.push_back(initialTet); // Process queue of tetras until exhausted // int i, valid; int somethingNotValid=0; OTTetra *nei, *tetra; TetraQueueIterator t; for ( int numCycles=0; !this->TetraStack.empty(); numCycles++) { tetra = this->TetraStack.top(); this->TetraStack.pop(); //for each face, see whether the neighbors are in the cavity for (valid=1, i=0; i<4 && valid; ++i) { nei = tetra->Neighbors[i]; // If a mesh boundary face, the face is added to the // list of insertion cavity faces if ( nei == 0 ) { OTFace *face = new(this->Heap) OTFace; tetra->GetFacePoints(i,face); face->Neighbor = 0; this->CavityFaces.push_back(face); valid = face->IsValidCavityFace(p->P,this->Tolerance2); } // Neighbor tetra has not been visited, check for possible face boundary else if ( nei->CurrentPointId != p->InsertionId ) { this->VisitedTetras.push_back(nei); nei->CurrentPointId = p->InsertionId; //mark visited if ( nei->InCircumSphere(p->P) ) { nei->Type = OTTetra::InCavity; this->TetraStack.push(nei); } else //a cavity boundary { nei->Type = OTTetra::OutsideCavity; OTFace *face = new(this->Heap) OTFace; tetra->GetFacePoints(i,face); face->Neighbor = nei; this->CavityFaces.push_back(face); valid = face->IsValidCavityFace(p->P,this->Tolerance2); } }//if a not-visited face neighbor // Visited before, add this face as a boundary else if ( nei->Type == OTTetra::OutsideCavity ) { OTFace *face = new(this->Heap) OTFace; tetra->GetFacePoints(i,face); face->Neighbor = nei; this->CavityFaces.push_back(face); valid = face->IsValidCavityFace(p->P,this->Tolerance2); } }//for each of the four tetra faces //check for validity if ( !valid ) //broke out due to invalid face { somethingNotValid++; //add this tetra to queue this->DegenerateQueue.push_back(tetra); //mark all current tetras unvisited for (t = this->VisitedTetras.begin(); t != this->VisitedTetras.end(); ++t) { (*t)->CurrentPointId = -1; } //mark degenerate tetras visited and outside cavity TetraQueueIterator titer; for ( titer=this->DegenerateQueue.begin(); titer != this->DegenerateQueue.end(); ++titer) { (*titer)->CurrentPointId = p->InsertionId; (*titer)->Type = OTTetra::OutsideCavity; } //reinitialize queue this->CavityFaces.clear(); //cavity face boundary this->VisitedTetras.clear(); //tetras visited during cavity creation this->TetraStack.clear(); //reprocess this->TetraStack.push(initialTet); initialTet->CurrentPointId = p->InsertionId; initialTet->Type = OTTetra::InCavity; this->VisitedTetras.push_back(initialTet); } if ( numCycles > 1000 ) return 0; }//while queue not empty // Make final pass and delete tetras inside the cavity for (t = this->VisitedTetras.begin(); t != this->VisitedTetras.end(); ++t) { tetra = *t; if ( tetra->CurrentPointId == p->InsertionId && tetra->Type == OTTetra::InCavity ) { this->Tetras.erase(tetra->ListIterator); } } #if 0 //please leave this for debugging purposes if ( somethingNotValid ) { this->DumpInsertionCavity(p->P); // exit(1); } #endif return 1; } //------------------------------------------------------------------------ // Returns the number of tetras classified inside; a side effect is that // all tetra are classified. int vtkOTMesh::ClassifyTetras() { TetraListIterator t; vtkIdType numInsideTetras=0; // loop over all tetras getting the ones with the classification requested for (t=this->Tetras.begin(); t != this->Tetras.end(); ++t) { if ( (*t)->DetermineType() == OTTetra::Inside ) { numInsideTetras++; } }//for all tetras return numInsideTetras; } //------------------------------------------------------------------------ // Used to debug (writes a VTK file representing the current insertion cavity). void vtkOTMesh::DumpInsertionCavity(double x[3]) { OTFace *face; FaceListIterator fptr; cout << "# vtk DataFile Version 3.0\n"; cout << "ordered triangulator output\n"; cout << "ASCII\n"; cout << "DATASET POLYDATA\n"; //write out points int numFaces = (int)this->CavityFaces.size(); cout << "POINTS " << 3*numFaces+1 << " double\n"; for (fptr=this->CavityFaces.begin(); fptr != this->CavityFaces.end(); ++fptr) { face = *fptr; cout << face->Points[0]->P[0] << " " << face->Points[0]->P[1] << " " << face->Points[0]->P[2] << " " << face->Points[1]->P[0] << " " << face->Points[1]->P[1] << " " << face->Points[1]->P[2] << " " << face->Points[2]->P[0] << " " << face->Points[2]->P[1] << " " << face->Points[2]->P[2] << "\n"; } //write out point insertion vertex cout << x[0] << " " << x[1] << " " << x[2] << "\n\n"; cout << "VERTICES 1 2 \n"; cout << "1 " << 3*numFaces << "\n\n"; //write out triangles cout << "POLYGONS " << numFaces << " " <<4*numFaces << "\n"; int idx=0; for (fptr=this->CavityFaces.begin(); fptr != this->CavityFaces.end(); ++fptr, idx+=3) { cout << 3 << " " << idx << " " << idx+1 << " " << idx+2 << "\n"; } } //------------------------------------------------------------------------ // Walk to the tetra tha contains this point. Walking is done by moving // in the direction of the most negative barycentric coordinate (i.e., // into the face neighbor). OTTetra* vtkOTMesh::WalkToTetra(OTTetra *tetra, double x[3], int depth, double bc[4]) { int neg = 0; int j, numNeg; double negValue; // prevent aimless wandering and death by recursion if ( depth > 200 ) { return 0; } vtkTetra::BarycentricCoords(x, tetra->Points[0]->P, tetra->Points[1]->P, tetra->Points[2]->P, tetra->Points[3]->P, bc); // find the most negative face for ( negValue=VTK_DOUBLE_MAX, numNeg=j=0; j<4; j++ ) { if ( bc[j] < -0.000001 ) //if close enough that's okay { numNeg++; if ( bc[j] < negValue ) { negValue = bc[j]; neg = j; } } } // if no negatives, then inside this tetra if ( numNeg <= 0 ) { return tetra; } // okay, march towards the most negative direction switch (neg) { case 0: tetra = tetra->Neighbors[1]; break; case 1: tetra = tetra->Neighbors[2]; break; case 2: tetra = tetra->Neighbors[0]; break; case 3: tetra = tetra->Neighbors[3]; break; } if ( tetra ) { return this->WalkToTetra(tetra, x, ++depth, bc); } else { return 0; } } //------------------------------------------------------------------------ // Use an ordered insertion process in combination with a consistent // degenerate resolution process to generate a unique Delaunay triangulation. void vtkOrderedTriangulator::Triangulate() { OTPoint *p; int i; vtkIdType ptId; // Sort the points according to id. The last six points are left // where they are (at the end of the list). if ( ! this->PreSorted ) { if (this->UseTwoSortIds) { qsort((void *)this->Mesh->Points.GetPointer(0), this->NumberOfPoints, sizeof(OTPoint), vtkSortOnTwoIds); } else { qsort((void *)this->Mesh->Points.GetPointer(0), this->NumberOfPoints, sizeof(OTPoint), vtkSortOnIds); } } // Prepare the data structures (e.g., mesh) for an ordererd triangulation. this->Initialize(); // Insert each point into the triangulation. Assign internal ids // as we progress. for (ptId=0, p=this->Mesh->Points.GetPointer(0); ptId < this->NumberOfPoints; ++p, ++ptId) { if ( p->Type == OTPoint::NoInsert ) { continue; //skip this point } p->InsertionId = ptId; // Walk to a tetrahedron (start with first one on list) double bc[4]; OTTetra *tetra = this->Mesh->WalkToTetra(*(this->Mesh->Tetras.begin()),p->P,0,bc); if ( tetra == 0 || !this->Mesh->CreateInsertionCavity(p, tetra, bc) ) { vtkDebugMacro(<<"Point not in tetrahedron"); continue; } // For each face on the boundary of the cavity, create a new // tetrahedron with the face and point. We've also got to set // up tetrahedron face neighbors, so we'll use an edge table // to keep track of the tetrahedron that generated the face as // a result of sweeping an edge. vtkIdType v1, v2; this->Mesh->EdgeTable->InitEdgeInsertion(this->MaximumNumberOfPoints+6,2); this->Mesh->TetraStack.clear(); FaceListIterator fptr; void *tptr; OTTetra *neiTetra; OTFace *face; for (fptr=this->Mesh->CavityFaces.begin(); fptr != this->Mesh->CavityFaces.end(); ++fptr) { face = *fptr; //create a tetra (it's added to the list of tetras as a side effect) tetra = this->Mesh->CreateTetra(p,face); for (i=0; i<3; ++i) { v1 = face->Points[i%3]->InsertionId; v2 = face->Points[(i+1)%3]->InsertionId; this->Mesh->EdgeTable->IsEdge(v1,v2,tptr); if ( ! tptr ) { this->Mesh->EdgeTable->InsertEdge(v1,v2,tetra); } else { neiTetra = static_cast(tptr); AssignNeighbors(tetra, neiTetra); } }//for three edges }//for each face on the insertion cavity }//for all points to be inserted // Final classification this->Mesh->NumberOfTetrasClassifiedInside = this->Mesh->ClassifyTetras(); } //------------------------------------------------------------------------ // Perform triangulation using templates (when possible). void vtkOrderedTriangulator::TemplateTriangulate(int cellType, int numPts, int numEdges) { this->CellType = cellType; if ( ! this->UseTemplates ) { this->Triangulate(); return; } this->NumberOfCellPoints = numPts; this->NumberOfCellEdges = numEdges; // Sort the points according to id. if ( ! this->PreSorted ) { if (this->UseTwoSortIds) { qsort((void *)this->Mesh->Points.GetPointer(0), this->NumberOfPoints, sizeof(OTPoint), vtkSortOnTwoIds); } else { qsort((void *)this->Mesh->Points.GetPointer(0), this->NumberOfPoints, sizeof(OTPoint), vtkSortOnIds); } } if ( ! this->TemplateTriangulation() ) {//template triangulation didn't work, triangulate it and add to template cache int preSorted = this->PreSorted; //prevents resorting this->PreSorted = 1; this->Triangulate(); this->AddTemplate(); this->PreSorted = preSorted; } } //------------------------------------------------------------------------ // Add the tetras classified as specified to an unstructured grid. vtkIdType vtkOrderedTriangulator::GetTetras(int classification, vtkUnstructuredGrid *ugrid) { // Create the points // int i; vtkIdType numTetras=0; PointListIterator p; vtkPoints *points = vtkPoints::New(); points->SetNumberOfPoints(this->NumberOfPoints); for ( i=0, p=this->Mesh->Points.begin(); iNumberOfPoints; ++i, ++p) { points->SetPoint(p->InsertionId,p->X); } ugrid->SetPoints(points); points->Delete(); ugrid->Allocate(1000); TetraListIterator t; OTTetra *tetra; // loop over all tetras getting the ones with the classification requested vtkIdType pts[4]; for (t=this->Mesh->Tetras.begin(); t != this->Mesh->Tetras.end(); ++t) { tetra = *t; if ( tetra->Type == classification || classification == OTTetra::All) { numTetras++; pts[0] = tetra->Points[0]->Id; pts[1] = tetra->Points[1]->Id; pts[2] = tetra->Points[2]->Id; pts[3] = tetra->Points[3]->Id; ugrid->InsertNextCell(VTK_TETRA,4,pts); } }//for all tetras return numTetras; } //------------------------------------------------------------------------ // Add the tetras classified as specified to an unstructured grid vtkIdType vtkOrderedTriangulator::AddTetras(int classification, vtkCellArray *outConnectivity) { TetraListIterator t; OTTetra *tetra; vtkIdType numTetras=0; // loop over all tetras getting the ones with the classification requested for (t=this->Mesh->Tetras.begin(); t != this->Mesh->Tetras.end(); ++t) { tetra = *t; if ( tetra->Type == classification || classification == OTTetra::All) { numTetras++; outConnectivity->InsertNextCell(4); outConnectivity->InsertCellPoint(tetra->Points[0]->Id); outConnectivity->InsertCellPoint(tetra->Points[1]->Id); outConnectivity->InsertCellPoint(tetra->Points[2]->Id); outConnectivity->InsertCellPoint(tetra->Points[3]->Id); } }//for all tetras return numTetras; } //----------------------------------------------------------------------------- // Assuming that all the inserted points come from a cell `cellId' to // triangulate, get the tetrahedra in outConnectivity, the points in locator // and copy point data and cell data. Return the number of added tetras. // \pre locator_exists: locator!=0 // \pre outConnectivity: outConnectivity!=0 // \pre inPD_exists: inPD!=0 // \pre outPD_exists: outPD!=0 // \pre inCD_exists: inCD!=0 // \pre outCD_exists: outCD!=0 vtkIdType vtkOrderedTriangulator::AddTetras(int classification, vtkPointLocator *locator, vtkCellArray *outConnectivity, vtkPointData *inPD, vtkPointData *outPD, vtkCellData *inCD, vtkIdType cellId, vtkCellData *outCD) { assert("pre: locator_exists" && locator!=0); assert("pre: outConnectivity" && outConnectivity!=0); assert("inPD_exists" && inPD!=0); assert("pre: outPD_exists" && outPD!=0); assert("inCD_exists" && inCD!=0); assert("pre: outCD_exists" && outCD!=0); TetraListIterator t; OTTetra *tetra; vtkIdType result=0; // loop over all tetras getting the ones with the classification requested for (t=this->Mesh->Tetras.begin(); t != this->Mesh->Tetras.end(); ++t) { tetra = *t; if ( tetra->Type == classification || classification == OTTetra::All) { // Insert the points vtkIdType pts[4]; int i=0; while(i<4) { if(locator->InsertUniquePoint(tetra->Points[i]->X,pts[i])) { outPD->CopyData(inPD,tetra->Points[i]->Id,pts[i]); } ++i; } // Insert the connectivity result++; vtkIdType newCellId = outConnectivity->InsertNextCell(4,pts); outCD->CopyData(inCD,cellId,newCellId); } }//for all tetras return result; } //------------------------------------------------------------------------ // Initialize tetra traversal. Used in conjunction with GetNextTetra(). void vtkOrderedTriangulator::InitTetraTraversal() { this->Mesh->CurrentTetra = this->Mesh->Tetras.begin(); } //------------------------------------------------------------------------ // Retrieve a single tetra. Used in conjunction with InitTetraTraversal(). // Returns 0 when the list is exhausted. int vtkOrderedTriangulator::GetNextTetra(int classification, vtkTetra *tet, vtkDataArray *cellScalars, vtkDoubleArray *tetScalars) { OTTetra *tetra; int i; // Find the next tetra with the right classification while ( this->Mesh->CurrentTetra != this->Mesh->Tetras.end() && (*this->Mesh->CurrentTetra)->Type != classification && (*this->Mesh->CurrentTetra)->Type != OTTetra::All ) { tetra = *(this->Mesh->CurrentTetra); ++this->Mesh->CurrentTetra; } if ( this->Mesh->CurrentTetra != this->Mesh->Tetras.end() ) { tetra = *(this->Mesh->CurrentTetra); for (i=0; i<4; i++) { tet->PointIds->SetId(i,tetra->Points[i]->Id); tet->Points->SetPoint(i,tetra->Points[i]->X); tetScalars->SetTuple(i, cellScalars->GetTuple( tetra->Points[i]->OriginalId)); } ++this->Mesh->CurrentTetra; return 1; } else { return 0; } } //------------------------------------------------------------------------ // Add the tetras classified as specified to a list of point ids and // point coordinates. vtkIdType vtkOrderedTriangulator::AddTetras(int classification, vtkIdList *ptIds, vtkPoints *pts) { TetraListIterator t; OTTetra *tetra; vtkIdType numTetras=0; int i; // loop over all tetras getting the ones with the classification requested for (t=this->Mesh->Tetras.begin(); t != this->Mesh->Tetras.end(); ++t) { tetra = *t; if ( tetra->Type == classification || classification == OTTetra::All) { numTetras++; for (i=0; i<4; i++) { ptIds->InsertNextId(tetra->Points[i]->Id); pts->InsertNextPoint(tetra->Points[i]->X); } } }//for all tetras return numTetras; } //------------------------------------------------------------------------ // Add the tetras classified as specified to an unstructured grid vtkIdType vtkOrderedTriangulator::AddTetras(int classification, vtkUnstructuredGrid *ugrid) { vtkIdType numTetras=0; TetraListIterator t; OTTetra *tetra; // loop over all tetras getting the ones with the classification requested vtkIdType pts[4]; for (t=this->Mesh->Tetras.begin(); t != this->Mesh->Tetras.end(); ++t) { tetra = *t; if ( tetra->Type == classification || classification == OTTetra::All) { numTetras++; pts[0] = tetra->Points[0]->Id; pts[1] = tetra->Points[1]->Id; pts[2] = tetra->Points[2]->Id; pts[3] = tetra->Points[3]->Id; ugrid->InsertNextCell(VTK_TETRA,4,pts); } }//for all tetras return numTetras; } //------------------------------------------------------------------------ // Add the tetras classified as specified to a call array (connectivity list) vtkIdType vtkOrderedTriangulator::AddTriangles(vtkCellArray *tris) { vtkIdType numTris=0; int i; // Loop over all tetras examining each unvisited face. Faces whose // points are all classified "boundary" are added to the list of // faces. TetraListIterator t; OTTetra *tetra; OTFace *face = new(this->Heap) OTFace; // loop over all tetras getting the faces classified on the boundary for (t=this->Mesh->Tetras.begin(); t != this->Mesh->Tetras.end(); ++t) { tetra = *t; tetra->CurrentPointId = VTK_LARGE_INTEGER; //mark visited for (i=0; i<4; i++) { if ( tetra->Neighbors[i] && tetra->Neighbors[i]->CurrentPointId != VTK_LARGE_INTEGER && tetra->Type != tetra->Neighbors[i]->Type ) {//face not yet visited tetra->GetFacePoints(i,face); numTris++; tris->InsertNextCell(3); tris->InsertCellPoint(face->Points[0]->Id); tris->InsertCellPoint(face->Points[1]->Id); tris->InsertCellPoint(face->Points[2]->Id); } } }//for all tetras return numTris; } //------------------------------------------------------------------------ // Add faces classified on the boundary to a cell array (connectivity list) vtkIdType vtkOrderedTriangulator::AddTriangles(vtkIdType id, vtkCellArray *tris) { vtkIdType numTris=0; int i; // Loop over all tetras examining each unvisited face. Faces whose // points are all classified "boundary" are added to the list of // faces. TetraListIterator t; OTTetra *tetra; OTFace *face = new(this->Heap) OTFace; // loop over all tetras getting the faces classified on the boundary for (t=this->Mesh->Tetras.begin(); t != this->Mesh->Tetras.end(); ++t) { tetra = *t; tetra->CurrentPointId = VTK_LARGE_INTEGER; //mark visited for (i=0; i<4; i++) { if ( tetra->Neighbors[i] && tetra->Neighbors[i]->CurrentPointId != VTK_LARGE_INTEGER && tetra->Type != tetra->Neighbors[i]->Type ) {//face not yet visited tetra->GetFacePoints(i,face); if ( face->Points[0]->Id == id || face->Points[1]->Id == id || face->Points[2]->Id == id ) { numTris++; tris->InsertNextCell(3); tris->InsertCellPoint(face->Points[0]->Id); tris->InsertCellPoint(face->Points[1]->Id); tris->InsertCellPoint(face->Points[2]->Id); } } } }//for all tetras return numTris; } //---The following code supports templates---------------------------------- // Rather than predefining templates for the many possible triangulations, the // ordered triangulator is used to generate the template which is then cached // for later use. The key is that templates are uniquely characterized by a // template id---a number representing a permutation of the sort of the // original points. //---Define template id type. Note: type must be 32 bits-------------------- // Currently the templates are set up for a maximum of eight point ids per // cell maximum (this is due to the use of the vtkHexahedron). Any point can // be exchanged with any of the other ids during the sort operation, so each // exchange is represented with four bits as follows: // // +----+----+----+----+----+----+----+----+ // | p0 | p1 | p2 | p3 | p4 | p5 | p6 | p7 | // +----+----+----+----+----+----+----+----+ // //------------------------------------------------------------------------ // Given the results of the sorting, compute an index used to specify // a template id. inline TemplateIDType vtkOrderedTriangulator::ComputeTemplateIndex() { static TemplateIDType mask[8]={0xF0000000,0x0F000000,0x00F00000,0x000F0000, 0x0000F000,0x00000F00,0x000000F0,0x0000000F}; int i; PointListIterator p; TemplateIDType templateID=0; for (p=this->Mesh->Points.begin(), i=0; iNumberOfCellPoints; ++i, ++p) { templateID |= ((templateID & mask[i]) | (p->OriginalId << (32-4*(i+1)))); } return templateID; } //------------------------------------------------------------------------ // If a template is missing, add it to the list of templates. void vtkOrderedTriangulator::AddTemplate() { // Find the template list for the given cell type int templateMayBeAvailable; TemplateList *tlist; TemplatesIterator titer = this->Templates->find(this->CellType); if ( titer != this->Templates->end() ) //something found { templateMayBeAvailable = 1; tlist = (*titer).second; } else //nothing found, have to create an entry for this cell type { templateMayBeAvailable = 0; tlist = new TemplateList; (*this->Templates)[this->CellType] = tlist; } // Create the template: its index and connectivity list TemplateIDType index = this->ComputeTemplateIndex(); // Make sure template has not been created before TemplateListIterator tplate = tlist->find(index); if ( templateMayBeAvailable && tplate != tlist->end() ) { vtkGenericWarningMacro(<<"Template found when it should not have been"); } else { this->Mesh->NumberOfTemplates++; // The tetras have been classified previously. So allocate space // and add it as a template list. OTTemplate *otplate = new(this->TemplateHeap) OTTemplate(this->Mesh->NumberOfTetrasClassifiedInside,this->TemplateHeap); (*tlist)[index] = otplate; // Now fill in the connectivity list int i; TetraListIterator t; OTTetra *tetra; vtkIdType *clist=otplate->Tetras; for (t=this->Mesh->Tetras.begin(); t != this->Mesh->Tetras.end(); ++t) { if ( (*t)->Type == OTTetra::Inside ) { tetra = *t; for (i=0; i<4; i++) { *clist++ = tetra->Points[i]->InsertionId; } } }//for all tetras } } //------------------------------------------------------------------------ // Use a template to create the triangulation. Return 0 if a template // could not be used. int vtkOrderedTriangulator::TemplateTriangulation() { TemplatesIterator titer = this->Templates->find(this->CellType); if ( titer != this->Templates->end() ) //something found { TemplateIDType index = this->ComputeTemplateIndex(); TemplateList *tlist = (*titer).second; TemplateListIterator tlistIter=tlist->find(index); if ( tlistIter != tlist->end() ) //something found { int i, j; OTTemplate *tets = (*tlistIter).second; vtkIdType numTets = tets->NumberOfTetras; vtkIdType *clist = tets->Tetras; OTTetra *tetra; for (i=0; iHeap) OTTetra(); this->Mesh->Tetras.push_front(tetra); tetra->Type = OTTetra::Inside; for (j=0; j<4; j++) { tetra->Points[j] = this->Mesh->Points.GetPointer(*clist++); } }//for all tetras in template return 1; }//if a template found }//if a template list for this cell type found return 0; } //------------------------------------------------------------------------ void vtkOrderedTriangulator::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "PreSorted: " << (this->PreSorted ? "On\n" : "Off\n"); os << indent << "UseTwoSortIds: " << (this->UseTwoSortIds ? "On\n" : "Off\n"); os << indent << "UseTemplates: " << (this->UseTemplates ? "On\n" : "Off\n"); os << indent << "NumberOfPoints: " << this->NumberOfPoints << endl; }