00001 /* 00002 * PROPRIETARY INFORMATION. This software is proprietary to 00003 * Side Effects Software Inc., and is not to be reproduced, 00004 * transmitted, or disclosed in any way without written permission. 00005 * 00006 * Produced by: 00007 * 00008 * David Pritchard 00009 * Side Effects Software Inc. 00010 * 123 Front Street West, Suite 1401 00011 * Toronto, Ontario 00012 * Canada M5J 2M2 00013 * 416-504-9876 00014 * 00015 * NAME: Delaunay triangulation tool 00016 * 00017 */ 00018 00019 #ifndef __GQ_PolyDelaunay_h__ 00020 #define __GQ_PolyDelaunay_h__ 00021 00022 #include "GQ_API.h" 00023 #include <GB/GB_Edge.h> 00024 #include <UT/UT_LinkListT.h> 00025 #include <UT/UT_Matrix4.h> 00026 #include <UT/UT_Pair.h> 00027 #include <UT/UT_RefArray.h> 00028 #include <UT/UT_String.h> 00029 #include <UT/UT_Vector3.h> 00030 00031 class GU_Detail; 00032 class GQ_Detail; 00033 class GQ_Point; 00034 class GQ_Edge; 00035 class GQ_Face; 00036 class GEO_Point; 00037 class GB_EdgeGroup; 00038 class GB_PrimitiveGroup; 00039 class GB_PointGroup; 00040 00041 class UT_Vector2; 00042 class UT_Vector4; 00043 00044 class gqTriangleSplitNode; 00045 class gqFaceQueue; 00046 00047 /// Delaunay triangulation (and refinement) of a set of 2D points. 00048 /// 00049 /// The algorithm used is a randomized incremental algorithm, as presented in 00050 /// de Berg, van Kreveld, Overmars and Schwarzkopf. "Computational Geometry", 00051 /// 2nd edition, 1999. 00052 /// 00053 /// It's adapted from: 00054 /// Guibas, Knuth and Sharir. "Randomized incremental construction of 00055 /// Delaunay and Voronoi diagrams." Algorithmica, 7:381-413, 1992. 00056 /// 00057 /// Most of it follows the earlier paper: 00058 /// Guibas and Stolfi. "Primitives for the Manipulation of General 00059 /// Subdivisions and the Computation of Voronoi Diagrams", ACM Transactions 00060 /// on Graphics, 4(2):74-123, April 1985. 00061 /// except for the data structure for determining which triangle a point is 00062 /// in. 00063 /// 00064 /// 00065 /// After triangulation is complete, an optional Delaunay refinement 00066 /// process is applied. This follows 00067 /// Shewchuk. "Delaunay Refinement Algorithms for Triangular Mesh 00068 /// Generation". Computational Geometry: Theory and Applications 00069 /// 22(1-3):21-74, May 2002. 00070 /// http://www.cs.berkeley.edu/~jrs/papers/2dj.ps 00071 /// 00072 /// Shewchuk describes a number of algorithms; I've implemented the 00073 /// pseudocode from pages 47-48. I used the Terminator algorithm, using 00074 /// concentric circles instead of diametral lenses. I haven't implemented 00075 /// one part of his algorithm: the key "terminator" innovation, his 00076 /// splitPermitted routine. 00077 /// 00078 /// A rough overview of the algorithm can be found at: 00079 /// http://www-2.cs.cmu.edu/~quake/tripaper/triangle3.html 00080 /// 00081 /// Shewchuk's main inspiration is this paper: 00082 /// 00083 /// Ruppert. "A Delaunay Refinement Algorithm for Quality 2-Dimensional 00084 /// Mesh Generation", Journal of Algorithms 18(3):548-585, May 1995. 00085 /// 00086 /// 00087 /// 00088 /// TODO: this could be extended to work with 3D points under a 2D 00089 /// parameterization (i.e., use texture co-ordinates instead of positions). 00090 /// I'm not sure how we would handle u-wrap or v-wrap, though... 00091 /// There's some discussion of this problem in 00092 /// Chew, L. Paul. Guaranteed-Quality Mesh Generation for Curved 00093 /// Surfaces. Proceedings of the 9th Annual Symposium on Computational 00094 /// Geometry, pages 274-280. ACM, May 1993. 00095 /// 00096 /// Better yet, we could do what Shewchuk describes in section 7.1 of his 00097 /// paper: triangulate a set of 3D primitives independently, but keeping 00098 /// track of insertions on the boundaries that are shared by multiple 00099 /// primitives. Apparently, it would require a way of calculating geodesic 00100 /// distance between points on different primitives. I'm sure other changes 00101 /// to my algorithm would be required too. 00102 /// 00103 /// 00104 /// Notes: 00105 /// * We use the GB_Edge class unconventionally here. Constraint edges 00106 /// are stored as GB_Edges in a GB_EdgeGroup. The endpoint order matters: 00107 /// the region to the right of p0->p1 is in the "interior" of the 00108 /// constraint. The optional "primitive" member of the GB_Edge points to 00109 /// the constraint primitive. However, the primitive member will be null 00110 /// for constraints that have no holes (i.e., have no bridge edges). This 00111 /// signals that there is no need to remove triangles from the inside of 00112 /// the hole. 00113 00114 class GQ_API GQ_PolyDelaunay 00115 { 00116 public: 00117 explicit GQ_PolyDelaunay(GU_Detail *gdp); 00118 virtual ~GQ_PolyDelaunay(); 00119 00120 /// Set the plane where triangulation happens. 00121 void setPlane(const UT_Vector3 &normal, fpreal distance); 00122 /// Fit a plane to the input points. Returns true if it succeeded. 00123 bool fitPlane(const GB_PointGroup *pointGroup = 0, 00124 UT_Vector3 *outNormal = 0, 00125 fpreal *outDistance = 0); 00126 00127 void setRemoveDuplicatePoints(bool value) 00128 { myRemoveDuplicatePoints = value; } 00129 00130 /// This one is mostly here for debugging - if enabled, we don't 00131 /// delete the bounding triangle that's added during construction. 00132 void setKeepBoundingTriangle(bool value) 00133 { myKeepBoundingTriangle = value; } 00134 /// If enabled, delete all edges that are outside the constrained 00135 /// edges. 00136 void setRemoveOutsideConstraints(bool value) 00137 { myRemoveOutsideConstraints = value; } 00138 /// Allow new points - e.g., constraint edges are split to maintain 00139 /// the Delaunay property. 00140 void enableNewConstraintPoints(bool enabled) 00141 { myNewConstraintPoints = enabled; } 00142 void setConstraintNewPointGroupName(const UT_String &name) 00143 { myConstraintNewPointGroupName.harden(name); } 00144 /// Allow refinement. 00145 void enableRefinement(bool enabled) 00146 { myRefinement = enabled; } 00147 void setMaxNewPoints(int maxNewPoints) 00148 { myMaxNewPoints = maxNewPoints; } 00149 /// Set refinement criteria. Any triangle that fails the maximum area 00150 /// test will be refined by inserting a new Steiner point. 00151 /// To disable this test, set to <= 0. 00152 void setMaxArea(fpreal maxArea) 00153 { myMaxArea = maxArea; } 00154 /// Set refinement criteria. Any triangle that fails the minimum angle 00155 /// test will be refined by inserting a new Steiner point. 00156 /// To disable this test, set to <= 0. 00157 /// 00158 /// Warning: high values can cause this to halt. Shewchuk says that 00159 /// there's a proof that it works for below 21 degrees, and he finds it 00160 /// works below 33, but he's had problems with higher values. 00161 void setMinAngle(fpreal minAngle) 00162 { myMinAngle = minAngle; } 00163 00164 void triangulate(const GB_PointGroup *pointGroup, 00165 const GB_PrimitiveGroup *constrainedPrimGroup); 00166 void buildGeometry(bool updatePointNormals = true); 00167 00168 /// Project 3D point onto plane. 00169 UT_Vector2 make2D(const UT_Vector4 &pos) const; 00170 UT_Vector4 make3D(const UT_Vector2 &pos) const; 00171 00172 private: 00173 typedef UT_LinkListT<GB_Edge> gqEdgeQueue; 00174 enum gqInsertStatus 00175 { 00176 GQ_Success, 00177 GQ_DuplicateVertex, 00178 GQ_OutsideBounding 00179 }; 00180 00181 /// A journal of events during a point insertion. It is used to allow 00182 /// the point insertion to be undone. 00183 struct gqJournal { 00184 typedef UT_Pair<int,int> gqEdgePointIds; 00185 typedef UT_Pair<gqEdgePointIds, GB_Element *> gqConstraint; 00186 00187 /// The sequence of edge flips performed, from first to last. 00188 /// The point ids are from after the flip. 00189 UT_RefArray<gqEdgePointIds> myEdgeFlips; 00190 /// The constraints that have been added and removed. 00191 UT_RefArray<gqConstraint> myConstraintsAdded; 00192 UT_RefArray<gqConstraint> myConstraintsRemoved; 00193 00194 /// If this point was inserted on an existing edge, these are the 00195 /// point ids of that original edge. 00196 gqEdgePointIds myOnEdge; 00197 }; 00198 00199 /// Set up the bounding triangle, myGqd, and the locating DAG. 00200 void setup(); 00201 /// Set up my*Transform member variables from the plane 00202 /// normal/distance. 00203 void calcTransforms(const UT_Vector3 &normal, fpreal distance); 00204 /// Check the constraint edges to see if any of their endpoints match 00205 /// an old point, and edit them to reference the new point if they 00206 /// do. The "old point" can be defined either by its position (with 00207 /// *no* tolerance), or by its id. 00208 /// 00209 /// Very inefficient. 00210 enum gq_ChangeConstraintType 00211 { 00212 gq_ByPos, 00213 gq_ByPointId 00214 }; 00215 void changeConstraints(gq_ChangeConstraintType type, 00216 const UT_Vector2 &oldPos, 00217 int oldPointId, 00218 GEO_Point &newPoint, 00219 GB_EdgeGroup &constrainedEdges, 00220 gqJournal *journal) const; 00221 /// Part of the core triangulation algorithm. Insert a new point into 00222 /// the existing triangulation. 00223 /// If the point is intended to lie on an existing edge (e.g., 00224 /// splitting an existing edge), the "onEdge" parameter can be used to 00225 /// indicate that. 00226 gqInsertStatus insertPoint(int pid, GQ_Edge *onEdge, 00227 GB_EdgeGroup &constrainedEdges, 00228 gqJournal *journal); 00229 /// Undo an insertion. 00230 void undoInsertPoint(int pid, GB_EdgeGroup &constrainedEdges, 00231 const gqJournal &journal); 00232 /// Flip a single edge. Does not update the point location data 00233 /// structure. 00234 void flipEdge(GQ_Edge &edge, bool updateDAG); 00235 /// Part of the core triangulation algorithm. Parameter point has just 00236 /// been inserted, and edge is being tested to see if it's legal 00237 /// according to the Delaunay criterion; if not, the edge is flipped 00238 /// and we recurse. 00239 void legalizeEdge(const GQ_Point &point, GQ_Edge &edge, 00240 GB_EdgeGroup &constrainedEdges, 00241 gqJournal *journal); 00242 /// After triangulation, ensure that all constraints are met. 00243 /// Returns false if constraints are contradictory and cannot be 00244 /// enforced. 00245 bool enforceConstraints(GB_EdgeGroup &constrainedEdges); 00246 /// Remove the bounding triangle 00247 void removeBoundingTriangle(); 00248 /// Remove outside edges that aren't in the given group. 00249 void removeOutsideEdges(const GB_EdgeGroup &constrainedEdges); 00250 /// Remove edges that are inside holes. 00251 void removeHoles(const GB_EdgeGroup &constrainedEdges); 00252 /// Destroy all edges in queue, and recursively destroy any 00253 /// neighbouring edges that are not constraints. This is Shewchuk's 00254 /// "triangle eating virus" used to remove edges outside the boundary 00255 /// and inside holes. 00256 /// For holes, it will leave one large non-triangular face after 00257 /// completion. 00258 void eatEdges(gqEdgeQueue &queue, 00259 const GB_EdgeGroup &constrainedEdges); 00260 /// Insert new points to satisfy refinement criteria. 00261 /// Returns true if the insertion ran successfully. False otherwise. 00262 bool refine(GB_EdgeGroup &constrainedEdges); 00263 /// Calculate stats related to refinement of a face. 00264 void calcFaceStats(const GQ_Face &face, 00265 fpreal &area, 00266 const GB_EdgeGroup *constrainedEdges, 00267 fpreal *minAngle) const; 00268 bool isFaceTooLarge(const GQ_Face &face) const; 00269 /// Test if a given face is "bad" - i.e., excessive area, or too small 00270 /// an angle. 00271 bool isFaceBad(const GQ_Face &face, 00272 const GB_EdgeGroup &constrainedEdges) const; 00273 /// Test if the given edge is "encroached" - i.e., if the triangles on 00274 /// side have an obtuse angle opposite to the edge. 00275 bool isEncroached(const GQ_Edge &edge, 00276 fpreal tol = 0.05f) const; 00277 /// Test if the given edge is encroached to its left side only. 00278 bool isEncroachedLeft(const GQ_Edge &edge, 00279 fpreal tol = 0.05f) const; 00280 /// Insert a point to split an non/existing edge at the given fraction 00281 /// along its length. 00282 GQ_Point *insertSplitPoint(GEO_Point &p0, GEO_Point &p1, 00283 fpreal fraction, 00284 GB_EdgeGroup &constrainedEdges, 00285 GQ_Edge *onEdge, 00286 bool isConstraintEdge); 00287 /// Split an existing edge at somewhere-near-the-midpoint. 00288 GQ_Point *splitEdge(GQ_Edge &edge, 00289 GB_EdgeGroup &constrainedEdges); 00290 /// Split the given existing constraint edge using the given point. 00291 /// Just updates the constraintEdges group. 00292 void splitConstraint(GB_EdgeGroup &constrainedEdges, 00293 const GB_Edge &gbEdge, 00294 GEO_Point &newPoint, 00295 gqJournal *journal) const; 00296 bool splitEncroachedEdges(GB_EdgeGroup &constrainedEdges, 00297 gqEdgeQueue &edgeQueue, 00298 gqFaceQueue *faceQueuePtr); 00299 void updateQueuesAfterNewPoint(const GQ_Point &newPoint, 00300 const GB_EdgeGroup &constrainedEdges, 00301 gqEdgeQueue &edgeQueue, 00302 gqFaceQueue *faceQueue); 00303 void deletePoint(int pid, GB_EdgeGroup &constrainedEdges, 00304 gqEdgeQueue *edgeQueue, 00305 gqFaceQueue *faceQueue); 00306 00307 /// Verify that myGqd and the locating DAG make sense. 00308 void verify(); 00309 00310 /// Transform from 3D space to plane 00311 UT_Matrix4 my3to2Transform; 00312 /// Transform from plane to 3D space 00313 UT_Matrix4 my2to3Transform; 00314 bool myNewConstraintPoints; 00315 int myMaxNewPoints; 00316 int myNumNewPoints; 00317 bool myRefinement; 00318 bool myRemoveDuplicatePoints; 00319 bool myKeepBoundingTriangle; 00320 bool myRemoveOutsideConstraints; 00321 fpreal myMaxArea, myMinAngle; 00322 bool myHasBoundingTriangle; 00323 UT_String myConstraintNewPointGroupName; 00324 00325 GU_Detail *myGdp; 00326 GQ_Detail *myGqd; 00327 GQ_Point *myOutsideP[3]; 00328 GB_PointGroup *myConstraintNewPointGroup; 00329 /// Root of DAG for doing point->triangle lookups 00330 gqTriangleSplitNode 00331 *myTriSplitRoot; 00332 }; 00333 00334 #endif
1.5.9