diff --git a/Geo/ChainComplex.h b/Geo/ChainComplex.h index 0ec42e3d6a9d37b40150d19c2797b95a9e7dccc0..558cc50cd5f41253c8b86c5d18bc6ee27c5ee136 100644 --- a/Geo/ChainComplex.h +++ b/Geo/ChainComplex.h @@ -116,8 +116,6 @@ class ChainComplex{ virtual void matrixTest(); }; -#endif - // A class representing a chain. // Used to visualize generators of the homology groups. class Chain{ @@ -165,3 +163,5 @@ class Chain{ }; #endif + +#endif diff --git a/Geo/Makefile b/Geo/Makefile index dbb7036a8223db67b97ca26761effcb3805fed5d..b8c6c75bca84136ad0407212be324ceafb0c31d0 100644 --- a/Geo/Makefile +++ b/Geo/Makefile @@ -40,9 +40,7 @@ SRC = GEntity.cpp STensor3.cpp\ MLine.cpp MTriangle.cpp MQuadrangle.cpp MTetrahedron.cpp\ MHexahedron.cpp MPrism.cpp MPyramid.cpp\ MZone.cpp MZoneBoundary.cpp\ - CellComplex.cpp ChainComplex.cpp Homology.cpp\ - STensor3.cpp - + CellComplex.cpp ChainComplex.cpp Homology.cpp OBJ = ${SRC:.cpp=${OBJEXT}} diff --git a/Mesh/meshGRegion.cpp b/Mesh/meshGRegion.cpp index 627a8776c5790d6272067c3c92ca607af858adb2..5f7557cb8d726b61b3e2da63c4f9cc7209f00ca6 100644 --- a/Mesh/meshGRegion.cpp +++ b/Mesh/meshGRegion.cpp @@ -158,7 +158,10 @@ void TransferTetgenMesh(GRegion *gr, tetgenio &in, tetgenio &out, double guess[2] = {0,0}; int Count = 0; for(int j = 0; j < 3; j++){ - if(v[j]->onWhat()->dim() == 2){ + if(!v[j]->onWhat()){ + Msg::Error("Uncategorized vertex %d", v[j]->getNum()); + } + else if(v[j]->onWhat()->dim() == 2){ double uu,vv; v[j]->getParameter(0,uu); v[j]->getParameter(1,vv); diff --git a/contrib/Tetgen/Makefile b/contrib/Tetgen/Makefile index 525e7e56bee6fb55606c09325815d1291517a779..e2296d4dfa15f7073e43534eeab75ed58c7ae377 100644 --- a/contrib/Tetgen/Makefile +++ b/contrib/Tetgen/Makefile @@ -21,6 +21,7 @@ SRC = behavior.cxx\ meshio.cxx\ meshstat.cxx\ predicates.cxx\ + refine.cxx\ surface.cxx OBJ = ${SRC:.cxx=${OBJEXT}} diff --git a/contrib/Tetgen/behavior.cxx b/contrib/Tetgen/behavior.cxx index db89a9a53edc7e2e7632a967c282b70c4488b2d1..e25f7e772cc7286f3daf0d8dd1cfeb4449e96ae7 100644 --- a/contrib/Tetgen/behavior.cxx +++ b/contrib/Tetgen/behavior.cxx @@ -50,7 +50,7 @@ tetgenbehavior::tetgenbehavior() nofacewritten = 0; noiterationnum = 0; nobisect = 0; - steiner = -1; + steinerleft = -1l; nomerge = 0; docheck = 0; quiet = 0; @@ -360,7 +360,7 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - steiner = (int) strtol(workstring, (char **) NULL, 0); + steinerleft = (int) strtol(workstring, (char **) NULL, 0); } } else if (argv[i][j] == 'D') { conformdel++; diff --git a/contrib/Tetgen/constrain.cxx b/contrib/Tetgen/constrain.cxx index 377ded1e02394c3173a112c45816ce797db28f00..b2e175540f0d656e3cd9f10ff8a88fdeec2a0e69 100644 --- a/contrib/Tetgen/constrain.cxx +++ b/contrib/Tetgen/constrain.cxx @@ -357,16 +357,14 @@ enum tetgenmesh::intersection tetgenmesh::finddirection(triface* searchtet, // scoutsegment() Look for a given segment in the tetrahedralization T. // // // // Search an edge in the tetrahedralization that matches the given segmment. // -// If such an edge is found, the segment is 'locked' at the edge. // -// // -// If 'searchtet' != NULL, it's origin must be the origin of 'sseg'. It is // -// used as the starting tet for searching the edge. // +// If such an edge exists, the segment is 'locked' at the edge. 'searchtet' // +// returns this (constrained) edge. Otherwise, the segment is missing. // // // // The returned value indicates one of the following cases: // -// - SHAREVERT, the segment exists and is inserted in T; // -// - ACROSSVERT, a vertex ('refpt') lies on the segment; // -// - ACROSSEDGE, the segment is missing; // -// - ACROSSFACE, the segment is missing; // +// - SHAREEDGE, the segment exists and is inserted in T; // +// - ACROSSVERT, the segment intersects a vertex ('refpt'). // +// - ACROSSEDGE, the segment intersects an edge (in 'searchtet'). // +// - ACROSSFACE, the segment crosses a face (in 'searchtet'). // // // // If the returned value is ACROSSEDGE or ACROSSFACE, i.e., the segment is // // missing, 'refpt' returns the reference point for splitting thus segment, // @@ -385,44 +383,22 @@ enum tetgenmesh::intersection tetgenmesh::scoutsegment(face* sseg, enum intersection dir; REAL angmax, ang; long facecount; - bool orgflag; int types[2], poss[4]; - int shver, pos, i; + int pos, i; tetrahedron ptr; shellface sptr; int *iptr, tver; + startpt = sorg(*sseg); + endpt = sdest(*sseg); + // Is 'searchtet' a valid handle? if (searchtet->tet == NULL) { - orgflag = false; - // Search a tet whose origin is one of the endpoints of 'sseg'. - for (shver = 0; shver < 2 && !orgflag; shver++) { - startpt = (point) sseg->sh[shver + 3]; - decode(point2tet(startpt), *searchtet); - if ((searchtet->tet != NULL) && (searchtet->tet[4] != NULL)) { - // Check if this tet contains pa. - for (i = 4; i < 8 && !orgflag; i++) { - if ((point) searchtet->tet[i] == startpt) { - // Found. Set pa as its origin. - switch (i) { - case 4: searchtet->loc = 0; searchtet->ver = 0; break; - case 5: searchtet->loc = 0; searchtet->ver = 2; break; - case 6: searchtet->loc = 0; searchtet->ver = 4; break; - case 7: searchtet->loc = 1; searchtet->ver = 2; break; - } - sseg->shver = shver; - orgflag = true; - } - } - } - } - assert(orgflag); // SELF_CHECK + point2tetorg(startpt, *searchtet); } else { - startpt = sorg(*sseg); assert(org(*searchtet) == startpt); // SELF_CHECK } - endpt = sdest(*sseg); if (b->verbose > 1) { printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt)); @@ -436,6 +412,8 @@ enum tetgenmesh::intersection tetgenmesh::scoutsegment(face* sseg, // Found! Insert the segment. tsspivot(*searchtet, checkseg); // SELF_CHECK if (checkseg.sh == NULL) { + // Let the segment remember an adjacent tet. + sstbond(*sseg, *searchtet); neightet = *searchtet; do { tssbond1(neightet, *sseg); @@ -447,7 +425,7 @@ enum tetgenmesh::intersection tetgenmesh::scoutsegment(face* sseg, assert(checkseg.sh == sseg->sh); // SELF_CHECK } // The job is done. - return SHAREVERT; + return SHAREEDGE; } else { // A point is on the path. *refpt = pd; @@ -773,6 +751,216 @@ void tetgenmesh::getsegmentsplitpoint(face* sseg, point refpt, REAL* vt) } } +// This function only use Rule-1 to split the segment. refpt may be NULL. +// Added 2009-05-20. + +void tetgenmesh::getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt) +{ + point ei, ej; + REAL split, L, d, d1, d2; + int i; + + ei = sorg(*sseg); + ej = sdest(*sseg); + + if (b->verbose > 1) { + printf(" Split seg(%d, %d) ref(%d).\n", pointmark(ei), pointmark(ej), + refpt ? pointmark(refpt) : -1); + } + + if (refpt != NULL) { + L = DIST(ei, ej); + d1 = DIST(ei, refpt); + d2 = DIST(ej, refpt); + if (d1 < d2) { + // Choose ei as center. + if (d1 < 0.5 * L) { + split = d1 / L; + // Adjust split if it is close to middle. (2009-02-01) + // if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ei[i] + split * (ej[i] - ei[i]); + } + } else { + // Choose ej as center. + if (d2 < 0.5 * L) { + split = d2 / L; + // Adjust split if it is close to middle. (2009-02-01) + // if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ej[i] + split * (ei[i] - ej[i]); + } + } + } else { + split = 0.5; + for (i = 0; i < 3; i++) { + vt[i] = ei[i] + split * (ej[i] - ei[i]); + } + } + r1count++; +} + +/////////////////////////////////////////////////////////////////////////////// +// getsegmentsplitpoint3() Calculate a split point in the given segment. +// This routine does not check far origin and far dest. + +void tetgenmesh::getsegmentsplitpoint3(face* sseg, point refpt, REAL* vt) +{ + point ei, ej, ek; + REAL split, L, d, d1, d2, d3; + int stype, sign; + int i; + + // Decide the type of this segment. + sign = 1; + ei = sorg(*sseg); + ej = sdest(*sseg); + ek = NULL; + + // ei can be an ACUTEVERTEX, or a RIDGEVERTEX, or a STEINERVERTEX. + if (getpointtype(ei) == ACUTEVERTEX) { + if (getpointtype(ej) == ACUTEVERTEX) { + // ej is an ACUTEVERTEX. + stype = 0; + } else { + // ej is either a RIDGEVERTEX or a STEINERVERTEX. + stype = 1; + ek = ei; + } + } else { + if (getpointtype(ei) == RIDGEVERTEX) { + if (getpointtype(ej) == ACUTEVERTEX) { + // ej is an ACUTEVERTEX. + stype = 1; sign = -1; + ek = ej; + } else { + if (getpointtype(ej) == RIDGEVERTEX) { + // ej is a RIDGEVERTEX. + stype = 0; + } else { + // ej is a STEINERVETEX. + stype = 0; + /* ek = farsdest(*sseg); + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + stype = 0; + }*/ + } + } + } else { + // ei is a STEINERVERTEX. + if (getpointtype(ej) == ACUTEVERTEX) { + stype = 1; sign = -1; + ek = ej; + } else { + // ej is either a RIDGEVERTEX or STEINERVERTEX. + stype = 0; + /*ek = farsorg(*sseg); + if (getpointtype(ej) == RIDGEVERTEX) { + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; + } else { + stype = 0; + } + } else { + // Both ei and ej are STEINERVETEXs. ei has priority. + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; + } else { + ek = farsdest(*sseg); + if (getpointtype(ek) == ACUTEVERTEX) { + stype = 1; sign = -1; + } else { + stype = 0; + } + } + }*/ + } + } + } + + // Adjust the endpoints: ei, ej. + if (sign == -1) { + sesymself(*sseg); + ei = sorg(*sseg); + ej = sdest(*sseg); + } + + if (b->verbose > 1) { + printf(" Split a type-%d seg(%d, %d) ref(%d).\n", stype, + pointmark(ei), pointmark(ej), pointmark(refpt)); + } + + // Calculate the split point. + if (stype == 0) { + // Use rule-1. + L = DIST(ei, ej); + d1 = DIST(ei, refpt); + d2 = DIST(ej, refpt); + if (d1 < d2) { + // Choose ei as center. + if (d1 < 0.5 * L) { + split = d1 / L; + // Adjust split if it is close to middle. (2009-02-01) + if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ei[i] + split * (ej[i] - ei[i]); + } + } else { + // Choose ej as center. + if (d2 < 0.5 * L) { + split = d2 / L; + // Adjust split if it is close to middle. (2009-02-01) + if ((split > 0.4) || (split < 0.6)) split = 0.5; + } else { + split = 0.5; + } + for (i = 0; i < 3; i++) { + vt[i] = ej[i] + split * (ei[i] - ej[i]); + } + } + r1count++; + } else { + // Use rule-2. + // ek = farsorg(*sseg); + L = DIST(ek, ej); + d = DIST(ek, refpt); + split = d / L; + for (i = 0; i < 3; i++) { + vt[i] = ek[i] + split * (ej[i] - ek[i]); + } + d1 = DIST(vt, refpt); + d2 = DIST(vt, ej); + if (d1 > d2) { + // Use rule-3. + d3 = DIST(ei, refpt); + if (d1 < 0.5 * d3) { + split = (d - d1) / L; + } else { + split = (d - 0.5 * d3) / L; + } + for (i = 0; i < 3; i++) { + vt[i] = ek[i] + split * (ej[i] - ek[i]); + } + } + d1 > d2 ? r3count++ : r2count++; + } + + if (b->verbose > 1) { + printf(" split (%g), vt (%g, %g, %g).\n", split, vt[0], vt[1], vt[2]); + } +} + /////////////////////////////////////////////////////////////////////////////// // // // delaunizesegments() Recover segments in a Delaunay tetrahedralization. // @@ -788,10 +976,6 @@ void tetgenmesh::delaunizesegments() enum intersection dir; bool visflag; - if (b->verbose) { - printf(" Delaunizing segments.\n"); - } - // Loop until 'subsegstack' is empty. while (subsegstack->objects > 0l) { // seglist is used as a stack. @@ -806,7 +990,7 @@ void tetgenmesh::delaunizesegments() searchtet.tet = NULL; dir = scoutsegment(&sseg, &searchtet, &refpt); - if (dir != SHAREVERT) { + if (dir != SHAREEDGE) { // The segment is missing, split it. spivot(sseg, splitsh); if (dir != ACROSSVERT) { @@ -814,6 +998,10 @@ void tetgenmesh::delaunizesegments() makepoint(&newpt); getsegmentsplitpoint(&sseg, refpt, newpt); setpointtype(newpt, STEINERVERTEX); + // DEBUG + if (pointmark(newpt) == 4157) { + printf("debug point\n"); + } // Split the segment by newpt. sinsertvertex(newpt, &splitsh, &sseg, true, false); // Insert newpt into the DT. If 'checksubfaces == 1' the current @@ -836,10 +1024,6 @@ void tetgenmesh::delaunizesegments() } } } - - if (b->verbose) { - printf(" %d protecting points.\n", r1count + r2count + r3count); - } } /////////////////////////////////////////////////////////////////////////////// @@ -1618,6 +1802,7 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, { triface *parytet, searchtet, neightet, spintet, *parytet1; face checksh, tmpsh, *parysh; + face checkseg; point pa, pb, pc, pd, pt[3], *parypt; // badface *newflipface; enum intersection dir; @@ -1673,18 +1858,18 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, pt[0] = * (point *) fastlookup(cavpoints, i); assert(pt[0] != dummypoint); // SELF_CHECK if (!pinfected(pt[0])) { - pinfect(pt[0]); // Mark it as inserted. + // pinfect(pt[0]); // Mark it as inserted. searchtet = recenttet; insertvertex(pt[0], &searchtet, true, false, false, false); } else { - // puninfect(pt[0]); // It is already inserted. + puninfect(pt[0]); // It is already inserted. } } - // Comment: All vertices of the cavity are marked. + // Comment: All vertices of the cavity are NOT marked. while (1) { - // Indentify boundary faces. Mark interior tets. Save missing faces. + // Identify boundary faces. Mark interior tets. Save missing faces. for (i = 0; i < cavfaces->objects; i++) { parytet = (triface *) fastlookup(cavfaces, i); // Skip an interior face (due to the enlargement of the cavity). @@ -1713,10 +1898,11 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, tsbond(neightet, tmpsh); } assert(dest(neightet) == pt[0]); // SELF_CHECK - // Mark neightet as interior. - if (!infected(neightet)) { - infect(neightet); - } + // The following step is now done in fillcavity(), 2009-04-24. + // // Mark neightet as interior. + // if (!infected(neightet)) { + // infect(neightet); + // } } else if (dir == COLLISIONFACE) { // A subface is already inserted (see fig/dum-cavity-case6). assert(oppo(*parytet) == NULL); // It must be a faked tet. @@ -1777,22 +1963,17 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, shellfacedealloc(subfacepool, parysh->sh); } cavshells->restart(); + + // Infect the points which are of the cavity. + for (i = 0; i < cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + pinfect(pt[0]); // Mark it as inserted. + } + // Enlarge the cavity. for (i = 0; i < misfaces->objects; i++) { // Get a missing face. parytet = (triface *) fastlookup(misfaces, i); - // Check for a missing subface. - tspivot(*parytet, checksh); - if (checksh.sh != NULL) { - if (b->verbose > 1) { - printf(" Queue a subface x%lx (%d, %d, %d).\n", - (unsigned long) checksh.sh, pointmark(sorg(checksh)), - pointmark(sdest(checksh)), pointmark(sapex(checksh))); - } - stdissolve(checksh); - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - } if (!infected(*parytet)) { // Put it into crossing tet list. infect(*parytet); @@ -1810,6 +1991,18 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, searchtet = recenttet; insertvertex(pd, &searchtet, true, false, false, false); } + // Check for a missing subface. + tspivot(*parytet, checksh); + if (checksh.sh != NULL) { + if (b->verbose > 1) { + printf(" Queue a subface x%lx (%d, %d, %d).\n", + (unsigned long) checksh.sh, pointmark(sorg(checksh)), + pointmark(sdest(checksh)), pointmark(sapex(checksh))); + } + stdissolve(checksh); + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } // Add three opposite faces into the boundary list. for (j = 0; j < 3; j++) { enext0fnext(*parytet, neightet); @@ -1840,6 +2033,13 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, } // j } // if (!infected(parytet)) } + + // Uninfect the points which are of the cavity. + for (i = 0; i < cavpoints->objects; i++) { + pt[0] = * (point *) fastlookup(cavpoints, i); + puninfect(pt[0]); + } + misfaces->restart(); cavityexpcount++; continue; @@ -1865,11 +2065,6 @@ bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, } } - // Uninfect all points of the DT. - for (i = 0; i < cavpoints->objects; i++) { - parypt = (point *) fastlookup(cavpoints, i); - puninfect(*parypt); - } cavpoints->restart(); // Comment: Now no vertex is marked. cavfaces->restart(); @@ -1909,50 +2104,59 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, // Connect newtets to tets outside the cavity. for (k = 0; k < 2; k++) { cavshells = (k == 0 ? topshells : botshells); - for (i = 0; i < cavshells->objects; i++) { - // Get a temp subface. - parysh = (face *) fastlookup(cavshells, i); - // Get the boundary tet outsode the cavity. - decode(parysh->sh[0], bdrytet); - pa = org(bdrytet); - pb = dest(bdrytet); - pc = apex(bdrytet); - // Get the adjacent new tet. - stpivot(*parysh, neightet); - assert(org(neightet) == pb); // SELF_CHECK - assert(dest(neightet) == pa); // SELF_CHECK - if (oppo(bdrytet) != NULL) { - // Bond the two tets. - bond(bdrytet, neightet); // Also cleared the pointer to tmpsh. - } - // Bond a subface (if it exists). - tspivot(bdrytet, checksh); - if (checksh.sh != NULL) { - tsbond(neightet, checksh); // Also cleared the pointer to tmpsh. - } else { - tsdissolve(neightet); // No subface, clear the pointer to tmpsh. - } - // Update the point-to-tets map. - point2tet(pa) = encode(neightet); - point2tet(pb) = encode(neightet); - point2tet(pc) = encode(neightet); - // Delete the temp subface. - // shellfacedealloc(subfacepool, parysh->sh); - if (oppo(bdrytet) == NULL) { - // Delete a faked tet. - tetrahedrondealloc(bdrytet.tet); + if (cavshells != NULL) { + for (i = 0; i < cavshells->objects; i++) { + // Get a temp subface. + parysh = (face *) fastlookup(cavshells, i); + // Get the boundary tet outsode the cavity. + decode(parysh->sh[0], bdrytet); + pa = org(bdrytet); + pb = dest(bdrytet); + pc = apex(bdrytet); + // Get the adjacent new tet. + stpivot(*parysh, neightet); + assert(org(neightet) == pb); // SELF_CHECK + assert(dest(neightet) == pa); // SELF_CHECK + // Mark neightet as an interior tet of this cavity, 2009-04-24. + // Comment: We know neightet is an interior tet. + if (!infected(neightet)) { + infect(neightet); + } + if (oppo(bdrytet) != NULL) { + // Bond the two tets. + bond(bdrytet, neightet); // Also cleared the pointer to tmpsh. + } + // Bond a subface (if it exists). + tspivot(bdrytet, checksh); + if (checksh.sh != NULL) { + tsbond(neightet, checksh); // Also cleared the pointer to tmpsh. + } else { + tsdissolve(neightet); // No subface, clear the pointer to tmpsh. + } + // Update the point-to-tets map. + point2tet(pa) = encode(neightet); + point2tet(pb) = encode(neightet); + point2tet(pc) = encode(neightet); + // Delete the temp subface. + // shellfacedealloc(subfacepool, parysh->sh); + if (oppo(bdrytet) == NULL) { + // Delete a faked tet. + tetrahedrondealloc(bdrytet.tet); + } } - } + } // if (cavshells != NULL) } + mflag = true; // Initialize it. + + if (midfaces != NULL) { + // Mark all facet vertices for finding middle subfaces. for (i = 0; i < facpoints->objects; i++) { pf = * (point *) fastlookup(facpoints, i); pinfect(pf); } - mflag = true; // Initialize it. - // The first pair of top and bottom tets share the same edge [a, b]. // toptet = * (triface *) fastlookup(topfaces, 0); if (infected(firsttopface)) { @@ -2086,81 +2290,90 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, } // j } // i + } // if (midfaces != NULL) if (mflag) { - if (b->verbose > 1) { - printf(" Found %ld middle subfaces.\n", midfaces->objects); - } - if (midfaces->objects > maxregionsize) { - maxregionsize = midfaces->objects; - } - // Unmark middle faces. - for (i = 0; i < midfaces->objects; i++) { - // Get a matched middle face [a, b, c] - midface = * (triface *) fastlookup(midfaces, i); - assert(facemarked(midface)); // SELF_CHECK - unmarkface(midface); + if (midfaces != NULL) { + if (b->verbose > 1) { + printf(" Found %ld middle subfaces.\n", midfaces->objects); + } + if (midfaces->objects > maxregionsize) { + maxregionsize = midfaces->objects; + } + // Unmark middle faces. + for (i = 0; i < midfaces->objects; i++) { + // Get a matched middle face [a, b, c] + midface = * (triface *) fastlookup(midfaces, i); + assert(facemarked(midface)); // SELF_CHECK + unmarkface(midface); + } } // Bond subsegments to new tets. // Comment: *** The following code does redundant job. Should be // re-placed in the future. for (k = 0; k < 2; k++) { cavshells = (k == 0 ? topshells : botshells); - for (i = 0; i < cavshells->objects; i++) { - parysh = (face *) fastlookup(cavshells, i); - decode(parysh->sh[0], bdrytet); - if (bdrytet.tet[4] != NULL) { - // Not a faked tet. Bond a subsegment (if it exists). - for (j = 0; j < 3; j++) { - tsspivot(bdrytet, checkseg); - if (checkseg.sh != NULL) { - symedge(bdrytet, neightet); - assert(marktested(neightet)); // SELF_CHECK - while (1) { - tssbond1(neightet, checkseg); - fnextself(neightet); - if (!marktested(neightet)) break; + if (cavshells != NULL) { + for (i = 0; i < cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + decode(parysh->sh[0], bdrytet); + if (bdrytet.tet[4] != NULL) { + // Not a faked tet. Bond a subsegment (if it exists). + for (j = 0; j < 3; j++) { + tsspivot(bdrytet, checkseg); + if (checkseg.sh != NULL) { + symedge(bdrytet, neightet); + assert(marktested(neightet)); // SELF_CHECK + // Let the segment remember an adjacent tet. + sstbond(checkseg, neightet); + while (1) { + tssbond1(neightet, checkseg); + fnextself(neightet); + if (!marktested(neightet)) break; + } } + enextself(bdrytet); } - enextself(bdrytet); - } - } else { - // A faked tet. There is an interior subface. Use it. - // See fig/dump-cavity-case19. - stpivot(*parysh, neightet); - assert(marktested(neightet)); // SELF_CHECK - tspivot(neightet, checksh); - assert(checksh.sh != NULL); // SELF_CHECK - assert(checksh.sh != parysh->sh); // // SELF_CHECK - // Align them at the same directed edge. - pa = org(neightet); - pb = dest(neightet); - for (j = 0; j < 3; j++) { - if (sorg(checksh) == pa) break; - senextself(checksh); - } - assert(j < 3); // SELF_CHECK - if (sdest(checksh) != pb) { - senext2self(checksh); - sesymself(checksh); - } - assert(sdest(checksh) == pb); // SELF_CHECK - // Bond a subsegment (if it exists). - for (j = 0; j < 3; j++) { - sspivot(checksh, checkseg); - if (checkseg.sh != NULL) { - toptet = neightet; - while (1) { - tssbond1(toptet, checkseg); - fnextself(toptet); - if (apex(toptet) == apex(neightet)) break; + } else { + // A faked tet. There is an interior subface. Use it. + // See fig/dump-cavity-case19. + stpivot(*parysh, neightet); + assert(marktested(neightet)); // SELF_CHECK + tspivot(neightet, checksh); + assert(checksh.sh != NULL); // SELF_CHECK + assert(checksh.sh != parysh->sh); // // SELF_CHECK + // Align them at the same directed edge. + pa = org(neightet); + pb = dest(neightet); + for (j = 0; j < 3; j++) { + if (sorg(checksh) == pa) break; + senextself(checksh); + } + assert(j < 3); // SELF_CHECK + if (sdest(checksh) != pb) { + senext2self(checksh); + sesymself(checksh); + } + assert(sdest(checksh) == pb); // SELF_CHECK + // Bond a subsegment (if it exists). + for (j = 0; j < 3; j++) { + sspivot(checksh, checkseg); + if (checkseg.sh != NULL) { + // Let the segment remember an adjacent tet. + sstbond(checkseg, neightet); + toptet = neightet; + while (1) { + tssbond1(toptet, checkseg); + fnextself(toptet); + if (apex(toptet) == apex(neightet)) break; + } } + senextself(checksh); + enextself(neightet); } - senextself(checksh); - enextself(neightet); } } - } + } // if (cavshells != NULL) } } else { // Faces at top and bottom are not matched. There exists non-Delaunay @@ -2224,22 +2437,33 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, // dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; ndelaunayedgecount++; } - // Unmark all facet vertices. - for (i = 0; i < facpoints->objects; i++) { - pf = * (point *) fastlookup(facpoints, i); - puninfect(pf); + + if (facpoints != NULL) { + // Unmark all facet vertices. + for (i = 0; i < facpoints->objects; i++) { + pf = * (point *) fastlookup(facpoints, i); + puninfect(pf); + } } + // Delete the temp subfaces. for (k = 0; k < 2; k++) { cavshells = (k == 0 ? topshells : botshells); - for (i = 0; i < cavshells->objects; i++) { - parysh = (face *) fastlookup(cavshells, i); - shellfacedealloc(subfacepool, parysh->sh); + if (cavshells != NULL) { + for (i = 0; i < cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + shellfacedealloc(subfacepool, parysh->sh); + } } } + topshells->restart(); - botshells->restart(); - midfaces->restart(); + if (botshells != NULL) { + botshells->restart(); + } + if (midfaces != NULL) { + midfaces->restart(); + } // Comment: Now no vertex is marked. return mflag; @@ -2261,6 +2485,8 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // NOTE: Some subsegments may contained inside the cavity. They must be // queued for recovery. See fig/dump-cavity-case20. + // Comment: This check should be avoided in the future. Do the check in + // routine delaunizecavity() is NOT enough. (2009-04-24). for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); assert(infected(*parytet)); // SELF_CHECK @@ -2283,6 +2509,8 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, printf(" Queue a missing segment (%d, %d).\n", pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); } + // Clean the seg-to-tet pointer. + stdissolve(checkseg); sinfect(checkseg); subsegstack->newindex((void **) &parysh); *parysh = checkseg; @@ -2303,17 +2531,22 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Collect infected new tets in cavity. for (k = 0; k < 2; k++) { newtets = (k == 0 ? topnewtets : botnewtets); - for (i = 0; i < newtets->objects; i++) { - parytet = (triface *) fastlookup(newtets, i); - if (infected(*parytet)) { - crosstets->newindex((void **) &pnewtet); - *pnewtet = *parytet; + if (newtets != NULL) { + for (i = 0; i < newtets->objects; i++) { + parytet = (triface *) fastlookup(newtets, i); + if (infected(*parytet)) { + crosstets->newindex((void **) &pnewtet); + *pnewtet = *parytet; + } } } } // Collect all new tets in cavity. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); + if (i == 0) { + recenttet = *parytet; // Remember a live handle. + } for (j = 0; j < 4; j++) { decode(parytet->tet[j], neightet); if (marktested(neightet)) { // Is it a new tet? @@ -2331,22 +2564,26 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Delete outer new tets. for (k = 0; k < 2; k++) { newtets = (k == 0 ? topnewtets : botnewtets); - for (i = 0; i < newtets->objects; i++) { - parytet = (triface *) fastlookup(newtets, i); - if (infected(*parytet)) { - // This is an interior tet. - uninfect(*parytet); - unmarktest(*parytet); - } else { - // An outer tet. Delete it. - tetrahedrondealloc(parytet->tet); + if (newtets != NULL) { + for (i = 0; i < newtets->objects; i++) { + parytet = (triface *) fastlookup(newtets, i); + if (infected(*parytet)) { + // This is an interior tet. + uninfect(*parytet); + unmarktest(*parytet); + } else { + // An outer tet. Delete it. + tetrahedrondealloc(parytet->tet); + } } } } crosstets->restart(); topnewtets->restart(); - botnewtets->restart(); + if (botnewtets != NULL) { + botnewtets->restart(); + } } /////////////////////////////////////////////////////////////////////////////// @@ -2367,6 +2604,9 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); assert(infected(*parytet)); // SELF_CHECK + if (i == 0) { + recenttet = *parytet; // Remember a live handle. + } parytet->ver = 0; for (parytet->loc = 0; parytet->loc < 4; parytet->loc++) { symedge(*parytet, neightet); @@ -2398,14 +2638,18 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, tetrahedrondealloc(parytet->tet); } - for (i = 0; i < botnewtets->objects; i++) { - parytet = (triface *) fastlookup(botnewtets, i); - tetrahedrondealloc(parytet->tet); + if (botnewtets != NULL) { + for (i = 0; i < botnewtets->objects; i++) { + parytet = (triface *) fastlookup(botnewtets, i); + tetrahedrondealloc(parytet->tet); + } } crosstets->restart(); topnewtets->restart(); - botnewtets->restart(); + if (botnewtets != NULL) { + botnewtets->restart(); + } } /////////////////////////////////////////////////////////////////////////////// @@ -2478,27 +2722,23 @@ void tetgenmesh::splitsubedge(point newpt, face *searchsh, arraypool *facfaces, void tetgenmesh::constrainedfacets() { + /* arraypool *crosstets, *topnewtets, *botnewtets; arraypool *topfaces, *botfaces, *midfaces; arraypool *topshells, *botshells, *facfaces; arraypool *toppoints, *botpoints, *facpoints; + */ triface *parytet, searchtet, neightet; face *pssub, ssub, neighsh; face checkseg; point *ppt, pt, newpt; enum intersection dir; bool success, delaunayflag; - long bakflip22count; - long cavitycount; int facetcount; int bakhullsize; int s, i, j; - if (b->verbose) { - printf(" Constraining facets.\n"); - } - - // Initialize arrays. + /* // Initialize arrays. crosstets = new arraypool(sizeof(triface), 10); topnewtets = new arraypool(sizeof(triface), 10); botnewtets = new arraypool(sizeof(triface), 10); @@ -2511,9 +2751,8 @@ void tetgenmesh::constrainedfacets() facfaces = new arraypool(sizeof(face), 10); topshells = new arraypool(sizeof(face), 10); botshells = new arraypool(sizeof(face), 10); + */ - bakflip22count = flip22count; - cavitycount = 0; facetcount = 0; // Loop until 'subfacstack' is empty. @@ -2528,11 +2767,11 @@ void tetgenmesh::constrainedfacets() if (neightet.tet == NULL) { // Find an unrecovered subface. smarktest(ssub); - facfaces->newindex((void **) &pssub); + tg_facfaces->newindex((void **) &pssub); *pssub = ssub; // Get all subfaces and vertices of the same facet. - for (i = 0; i < facfaces->objects; i++) { - ssub = * (face *) fastlookup(facfaces, i); + for (i = 0; i < tg_facfaces->objects; i++) { + ssub = * (face *) fastlookup(tg_facfaces, i); for (j = 0; j < 3; j++) { sspivot(ssub, checkseg); if (checkseg.sh == NULL) { @@ -2543,7 +2782,7 @@ void tetgenmesh::constrainedfacets() stpivot(neighsh, neightet); if (neightet.tet == NULL) { smarktest(neighsh); - facfaces->newindex((void **) &pssub); + tg_facfaces->newindex((void **) &pssub); *pssub = neighsh; } } @@ -2551,32 +2790,32 @@ void tetgenmesh::constrainedfacets() pt = sorg(ssub); if (!pinfected(pt)) { pinfect(pt); - facpoints->newindex((void **) &ppt); + tg_facpoints->newindex((void **) &ppt); *ppt = pt; } senextself(ssub); } // j } // i // Have found all facet subfaces (vertices). Uninfect them. - for (i = 0; i < facfaces->objects; i++) { - pssub = (face *) fastlookup(facfaces, i); + for (i = 0; i < tg_facfaces->objects; i++) { + pssub = (face *) fastlookup(tg_facfaces, i); sunmarktest(*pssub); } - for (i = 0; i < facpoints->objects; i++) { - ppt = (point *) fastlookup(facpoints, i); + for (i = 0; i < tg_facpoints->objects; i++) { + ppt = (point *) fastlookup(tg_facpoints, i); puninfect(*ppt); } if (b->verbose > 1) { printf(" Recover facet #%d: %ld subfaces, %ld vertices.\n", - facetcount + 1, facfaces->objects, facpoints->objects); + facetcount + 1, tg_facfaces->objects, tg_facpoints->objects); } facetcount++; - // Loop until 'facfaces' is empty. - while (facfaces->objects > 0l) { + // Loop until 'tg_facfaces' is empty. + while (tg_facfaces->objects > 0l) { // Get the last subface of this array. - facfaces->objects--; - pssub = (face *) fastlookup(facfaces, facfaces->objects); + tg_facfaces->objects--; + pssub = (face *) fastlookup(tg_facfaces, tg_facfaces->objects); ssub = *pssub; stpivot(ssub, neightet); @@ -2589,40 +2828,41 @@ void tetgenmesh::constrainedfacets() assert(dir != COLLISIONFACE); // SELF_CHECK // Not exist. Push the subface back into stack. - s = randomnation(facfaces->objects + 1); - facfaces->newindex((void **) &pssub); - *pssub = * (face *) fastlookup(facfaces, s); - * (face *) fastlookup(facfaces, s) = ssub; + s = randomnation(tg_facfaces->objects + 1); + tg_facfaces->newindex((void **) &pssub); + *pssub = * (face *) fastlookup(tg_facfaces, s); + * (face *) fastlookup(tg_facfaces, s) = ssub; if (dir == EDGETRIINT) continue; // All three edges are missing. // Search for a crossing tet. - dir = scoutcrosstet(&ssub, &searchtet, facpoints); + dir = scoutcrosstet(&ssub, &searchtet, tg_facpoints); if (dir == ACROSSTET) { // Recover subfaces by local retetrahedralization. cavitycount++; bakhullsize = hullsize; checksubsegs = checksubfaces = 0; - crosstets->newindex((void **) &parytet); + tg_crosstets->newindex((void **) &parytet); *parytet = searchtet; // Form a cavity of crossing tets. - formcavity(&ssub, crosstets, topfaces, botfaces, toppoints, - botpoints, facpoints); + formcavity(&ssub, tg_crosstets, tg_topfaces, tg_botfaces, + tg_toppoints, tg_botpoints, tg_facpoints); delaunayflag = true; - // Tetrahedralize the top part. Re-use 'midfaces'. - success = delaunizecavity(toppoints, topfaces, topshells, - topnewtets, crosstets, midfaces); + // Tetrahedralize the top part. Re-use 'tg_midfaces'. + success = delaunizecavity(tg_toppoints, tg_topfaces, tg_topshells, + tg_topnewtets, tg_crosstets, tg_midfaces); if (success) { - // Tetrahedralize the bottom part. Re-use 'midfaces'. - success = delaunizecavity(botpoints, botfaces, botshells, - botnewtets, crosstets, midfaces); + // Tetrahedralize the bottom part. Re-use 'tg_midfaces'. + success = delaunizecavity(tg_botpoints, tg_botfaces, tg_botshells, + tg_botnewtets, tg_crosstets, tg_midfaces); if (success) { // Fill the cavity with new tets. - success = fillcavity(topshells, botshells, midfaces, facpoints); + success = fillcavity(tg_topshells, tg_botshells, tg_midfaces, + tg_facpoints); if (success) { // Delete old tets and outer new tets. - carvecavity(crosstets, topnewtets, botnewtets); + carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); } } else { delaunayflag = false; @@ -2632,16 +2872,16 @@ void tetgenmesh::constrainedfacets() } if (!success) { // Restore old tets and delete new tets. - restorecavity(crosstets, topnewtets, botnewtets); + restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); } /*if (!delaunayflag) { dump_facetof(&ssub, "facet1.lua"); while (futureflip != NULL) { - formedgecavity(futureflip->forg, futureflip->fdest, crosstets, - topfaces, toppoints); - crosstets->restart(); - topfaces->restart(); - toppoints->restart(); + formedgecavity(futureflip->forg, futureflip->fdest, tg_crosstets, + tg_topfaces, tg_toppoints); + tg_crosstets->restart(); + tg_topfaces->restart(); + tg_toppoints->restart(); futureflip = futureflip->nextitem; } flippool->restart(); @@ -2654,7 +2894,7 @@ void tetgenmesh::constrainedfacets() checksubsegs = checksubfaces = 1; } else if (dir == ACROSSFACE) { // Recover subfaces by flipping edges in surface mesh. - recoversubfacebyflips(&ssub, &searchtet, facfaces); + recoversubfacebyflips(&ssub, &searchtet, tg_facfaces); success = true; } else { // dir == TOUCHFACE assert(0); @@ -2662,7 +2902,7 @@ void tetgenmesh::constrainedfacets() if (!success) break; } // while - if (facfaces->objects > 0l) { + if (tg_facfaces->objects > 0l) { // Found a non-Delaunay edge, split it (or a segment close to it). // Create a new point at the middle of this edge, its coordinates // were saved in dummypoint in 'fillcavity()'. @@ -2671,28 +2911,21 @@ void tetgenmesh::constrainedfacets() setpointtype(newpt, STEINERVERTEX); dummypoint[0] = dummypoint[1] = dummypoint[2] = 0; // Insert the new point. Starting search it from 'ssub'. - splitsubedge(newpt, &ssub, facfaces, facpoints); - facfaces->restart(); + splitsubedge(newpt, &ssub, tg_facfaces, tg_facpoints); + tg_facfaces->restart(); } // Clear the list of facet vertices. - facpoints->restart(); + tg_facpoints->restart(); // Some subsegments may be queued, recover them. if (subsegstack->objects > 0l) { - b->verbose--; // Suppress the message output. delaunizesegments(); - b->verbose++; } // Now the mesh should be constrained Delaunay. } // if (neightet.tet == NULL) } - if (b->verbose) { - printf(" %ld subedge flips.\n", flip22count - bakflip22count); - printf(" %ld cavities remeshed.\n", cavitycount); - } - - // Delete arrays. + /* // Delete arrays. delete crosstets; delete topnewtets; delete botnewtets; @@ -2705,26 +2938,37 @@ void tetgenmesh::constrainedfacets() delete facfaces; delete topshells; delete botshells; + */ } /////////////////////////////////////////////////////////////////////////////// // // -// scoutedge() Search an edge in current tetrahedralization. // +// scoutsegment2() Search a segment in tetrahedralization. // +// // +// Search an edge in tetrahedralization that matches the given segmment. If // +// such an edge exists, the segment is 'locked' at that edge. 'searchtet' // +// returns this (constrained) edge. Otherwise, the segment is missing. // // // -// The edge [a, b] will be searched. If the edge exists, return a handle in // -// the tetrahedralization ('searchtet') referring to this edge. If the edge // -// is missing, and the array 'crosstets' is not a NULL, return the cavity // -// of all corssing tets of this edge. // +// If the segment is missing, and the array 'crosstets' is given, it returns // +// all crossing tetrahedra by this segment. // +// // +// The returned value indicates one of the following cases: // +// - SHAREEDGE, the segment exists and is inserted in T; // +// - ACROSSVERT, the segment passes a vertex (the origin of 'searchtet'). // +// - ACROSSEDGE, the segment intersects an edge (in 'searchtet'). // +// - ACROSSFACE, the segment crosses a face (in 'searchtet'). // +// - ACROSSSUBSEG, the segment intersects a segment (in 'searchtet'). // +// - ACROSSSUBFACE, the segment crosses a subface (in 'searchtet'). // +// - ACROSSTET, the segment is missing and the cavity has formed. // // // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, - triface* searchtet, arraypool* crosstets, arraypool* cavfaces, - arraypool* cavpoints) +enum tetgenmesh::intersection tetgenmesh::scoutsegment2(face* sseg, + triface* searchtet, arraypool* crosstets) { triface neightet, spintet, *parytet; face checksh, checkseg; - point pc, pd, pe, pf, *ppt, *parypt; + point pa, pb, pc, pd, pe, pf;// *ppt, *parypt; enum intersection dir; int types[2], poss[4]; int pos, i; @@ -2732,8 +2976,11 @@ enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, tetrahedron ptr; int *iptr, tver; + pa = sorg(*sseg); + pb = sdest(*sseg); + if (b->verbose > 1) { - printf(" Search edge (%d, %d).\n", pointmark(pa), pointmark(pb)); + printf(" Search edge (%d, %d).\n", pointmark(pa), pointmark(pb)); } // Search a tet whose origin is pa. @@ -2743,15 +2990,32 @@ enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, dir = finddirection(searchtet, pb); if (dir == ACROSSVERT) { if (dest(*searchtet) == pb) { - // Found! Does there already exist a segment. + // Found! Insert the segment. + tsspivot(*searchtet, checkseg); // SELF_CHECK + if (checkseg.sh == NULL) { + // Let the segment remember an adjacent tet. + sstbond(*sseg, *searchtet); + neightet = *searchtet; + do { + tssbond1(neightet, *sseg); + fnextself(neightet); + } while (neightet.tet != searchtet->tet); + } else { + // Collision! This can happy during facet recovery. + // See fig/dump-cavity-case19, -case20. + assert(checkseg.sh == sseg->sh); // SELF_CHECK + } return SHAREEDGE; // The edge is not missing. } else { + enextself(*searchtet); return ACROSSVERT; // The edge intersects a vertex. } } // The edge is missing, shall we form the edge cavity? - if ((crosstets == NULL) && (cavfaces == NULL) && (cavpoints == NULL)) { + if (crosstets == NULL) { + // Go to the face/edge it crosses. + enextfnextself(*searchtet); return dir; // ACROSSFACE and ACROSSEDGE } @@ -2762,13 +3026,13 @@ enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, infect(*searchtet); crosstets->newindex((void **) &parytet); *parytet = *searchtet; - // Add all vertices of this tet into list. + /*// Add all vertices of this tet into list. ppt = (point *) &(searchtet->tet[4]); for (i = 0; i < 4; i++) { pinfect(ppt[i]); cavpoints->newindex((void **) &parypt); *parypt = ppt[i]; - } + }*/ // Collect all crossing tets of the edge [a, b]. while (1) { @@ -2783,11 +3047,11 @@ enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, crosstets->newindex((void **) &parytet); *parytet = *searchtet; } - if (!pinfected(pf)) { // Add the opposite point into list. + /*if (!pinfected(pf)) { // Add the opposite point into list. pinfect(pf); cavpoints->newindex((void **) &parypt); *parypt = pf; - } + }*/ tspivot(*searchtet, checksh); // Check if a subface is crossed. if (checksh.sh != NULL) { // The edge intersect a subface. @@ -2806,16 +3070,17 @@ enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, *parytet = spintet; } pd = oppo(spintet); - if (!pinfected(pd)) { // Add the opposite point into list. + /*if (!pinfected(pd)) { // Add the opposite point into list. pinfect(pd); cavpoints->newindex((void **) &parypt); *parypt = pd; - } + }*/ if (apex(spintet) == pc) break; } tsspivot(spintet, checkseg); // Check if a segment is crossed. if (checkseg.sh != NULL) { // The edge intersects a subsegment. + *searchtet = spintet; dir = ACROSSSUBSEG; break; } @@ -2900,16 +3165,16 @@ enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, parytet = (triface *) fastlookup(crosstets, i); uninfect(*parytet); } - for (i = 0; i < cavpoints->objects; i++) { + /*for (i = 0; i < cavpoints->objects; i++) { parypt = (point *) fastlookup(cavpoints, i); puninfect(*parypt); - } + }*/ crosstets->restart(); - cavpoints->restart(); + // cavpoints->restart(); return dir; } - //We can form the edge cavity. + /*//We can form the edge cavity. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); *searchtet = *parytet; @@ -2922,11 +3187,55 @@ enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, } } } + // Uninfect the vertices. + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + puninfect(*parypt); + } + // Comment: All crossing tets are infected. + */ + + if (b->verbose > 1) { + printf(" Formed edge cavity: %ld tets.\n", crosstets->objects); + } + + return ACROSSTET; +} - // Uninfect the collected crossing tets and vertices. +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrasegcavity() Tetrahedralize a cavity for recovering a segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::tetrasegcavity(face* sseg, arraypool* crosstets, + arraypool* cavpoints, arraypool* cavfaces, arraypool* cavshells, + arraypool* newtets, arraypool* misfaces, arraypool* missegs, + arraypool* fixedseglist) +{ + triface searchtet, neightet, spintet, *parytet, *parytet1; + face tmpsh, *parysh; + face checkseg, *paryseg; + point pt[4], pswap, *parypt; + enum intersection dir; + REAL ori; + bool success; + int i, j; + + tetrahedron ptr; + int *iptr, tver; + + // cavpoints are collected. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); - uninfect(*parytet); + for (j = 0; j < 4; j++) { + pt[0] = (point) parytet->tet[4 + j]; + if (!pinfected(pt[0])) { + pinfect(pt[0]); + cavpoints->newindex((void **) &parypt); + *parypt = pt[0]; + } + } } for (i = 0; i < cavpoints->objects; i++) { parypt = (point *) fastlookup(cavpoints, i); @@ -2934,11 +3243,533 @@ enum tetgenmesh::intersection tetgenmesh::scoutedge(point pa, point pb, } if (b->verbose > 1) { - printf(" Formed edge cavity: %ld tets, %ld faces, %ld vertices.\n", - crosstets->objects, cavfaces->objects, cavpoints->objects); + printf(" Tetrahedralizing segment cavity: %ld points.\n", + cavpoints->objects, cavfaces->objects); } - return ACROSSFACE; // or ACROSSEDGE + // Form an initial constrained tetrahedralization (CT) which has only one + // tetrahedron containing the given segment. + + // The first two points are the endpoints of the segment. + pt[0] = sorg(*sseg); + pt[1] = sdest(*sseg); + pinfect(pt[0]); + pinfect(pt[1]); + + // Get the third and fourth points. + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + if (!pinfected(*parypt)) { + pt[2] = *parypt; + i++; + for (; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + if (!pinfected(*parypt)) { + ori = orient3d(pt[0], pt[1], pt[2], *parypt); + if (ori != 0) { + pt[3] = *parypt; + if (ori > 0) { // Swap pa and pb. + pswap = pt[0]; pt[0] = pt[1]; pt[1] = pswap; + } + break; + } + } + } // i + break; + } + } // i + assert(i < cavpoints->objects); // SELF_CHECK + pinfect(pt[2]); + pinfect(pt[3]); + + // Create an initial DT. + initialDT(pt[0], pt[1], pt[2], pt[3]); + + // Insert the segment (turns a DT into a CT). + scoutsegment2(sseg, &searchtet, NULL); + + // Insert the other points into the CT (the segment is respected). + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + if (!pinfected(*parypt)) { + // pinfect(*parypt); + searchtet = recenttet; // No random samples. + // Insert the point by flips. Set 'flipflag' = 3, do not flip a segment. + flipinsertvertex(*parypt, &searchtet, 3); + } else { + puninfect(*parypt); + } + } + // Comment: All vertices of the cavity are NOT marked. + + success = true; + + while (1) { + + // cavfaces are collected. + for (i = 0; i < crosstets->objects; i++) { + parytet = (triface *) fastlookup(crosstets, i); + searchtet = *parytet; + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, neightet); + if (!infected(neightet)) { + // A cavity bounday face. + cavfaces->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + + // Identify boundary faces. Mark interior tets. Save missing faces. + for (i = 0; i < cavfaces->objects; i++) { + parytet = (triface *) fastlookup(cavfaces, i); + // The tet of this face is exteriorly adjacent to the cavity. + assert(!infected(*parytet)); // SELF_CHECK + // This face may contain dummypoint (See fig/dump-cavity-case2). + // If so, dummypoint must be its apex. + parytet->ver = 4; + pt[0] = org(*parytet); + pt[1] = dest(*parytet); + pt[2] = apex(*parytet); + // Create a temp subface. + makeshellface(subfacepool, &tmpsh); + setshvertices(tmpsh, pt[0], pt[1], pt[2]); + // Insert tmpsh in CT. + searchtet.tet = NULL; + dir = scoutsubface(&tmpsh, &searchtet); + if (dir == SHAREFACE) { + // Identify the inter and outer tets at tempsh. + stpivot(tmpsh, neightet); + // neightet and tmpsh refer to the same edge [pt[0], pt[1]]. + // Moreover, neightet is in 0th edge ring (see decode()). + if (org(neightet) != pt[1]) { + symedgeself(neightet); + assert(org(neightet) == pt[1]); // SELF_CHECK + // Make sure that tmpsh is connected with an interior tet. + tsbond(neightet, tmpsh); + } + assert(dest(neightet) == pt[0]); // SELF_CHECK + } else if (dir == COLLISIONFACE) { + // A subface is already inserted. + assert(0); // Not handled yet. + } else { + if (b->verbose > 1) { + printf(" p:draw_subface(%d, %d, %d) -- %d is missing\n", + pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i); + } + shellfacedealloc(subfacepool, tmpsh.sh); + // Save this face in list. + misfaces->newindex((void **) &parytet1); + *parytet1 = *parytet; + continue; + } + // Remember tmpsh (use the adjacent tet slot). + // parytet->tet[parytet->loc] = (tetrahedron) sencode(tmpsh); + tmpsh.sh[0] = (shellface) encode(*parytet); + // Save this subface. + cavshells->newindex((void **) &parysh); + *parysh = tmpsh; + } // for (i) + + if (misfaces->objects > 0) { + // Removing tempoaray subfaces. + for (i = 0; i < cavshells->objects; i++) { + parysh = (face *) fastlookup(cavshells, i); + stpivot(*parysh, neightet); + uninfect(neightet); + tsdissolve(neightet); // Detach it from adj. tets. + symself(neightet); + tsdissolve(neightet); + shellfacedealloc(subfacepool, parysh->sh); + } + cavshells->restart(); + + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + pinfect(*parypt); + } + + // Enlarge the cavity. + for (i = 0; i < misfaces->objects; i++) { + // Get a missing face. + parytet = (triface *) fastlookup(misfaces, i); + // For this routine we do not check subface(s). + if (!infected(*parytet)) { + // This face should not be on the hull. + assert((point) parytet->tet[7] != dummypoint); // SELF_CHECK + // Check if we can enclose this tet into our cavity. + infect(*parytet); + // Check its six edges for enclosed segments. + neightet.tet = parytet->tet; + for (j = 0; j < 6; j++) { + neightet.loc = edge2locver[j][0]; + neightet.ver = edge2locver[j][1]; + tsspivot(neightet, checkseg); + if (checkseg.sh != NULL) { + // Check if this segment is inside our cavity. + spintet = neightet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) break; // Not inside. + if (apex(spintet) == apex(neightet)) { + if (b->verbose > 1) { + printf(" p:draw_subseg(%d, %d) -- is inside.\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + if (!smarktested(checkseg)) { + missegs->newindex((void **) &parysh); + *parysh = checkseg; + } else { + if (b->verbose > 1) { + printf(" !! A fixed segment.\n"); + } + success = false; + } + break; + } + } // while (1) + if (!success) break; + } + } // j + if (success) { + // We can enlarge the cavity. + if (b->verbose > 1) { + printf(" Add a crosstet (%d, %d, %d, %d).\n", + pointmark(org(*parytet)), pointmark(dest(*parytet)), + pointmark(apex(*parytet)), pointmark(oppo(*parytet))); + } + crosstets->newindex((void **) &parytet1); + *parytet1 = *parytet; + // Insert the opposite point if it is not in CT. + pt[0] = oppo(*parytet); + if (!pinfected(pt[0])) { + if (b->verbose > 1) { + printf(" Insert oppo-point %d.\n", pointmark(pt[0])); + } + pinfect(pt[0]); + cavpoints->newindex((void **) &parypt); + *parypt = pt[0]; + searchtet = recenttet; + flipinsertvertex(pt[0], &searchtet, 3); + } + /*// Add three opposite faces into the boundary list. + for (j = 0; j < 3; j++) { + enext0fnext(*parytet, neightet); + symself(neightet); + if (!infected(neightet)) { + if (b->verbose > 1) { + printf(" Add a cavface (%d, %d, %d).\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet))); + } + cavfaces->newindex((void **) &parytet1); + *parytet1 = neightet; + } else { + // It is an interior face, we do not check subface + // for this routine. + } + enextself(*parytet); + } // j */ + } else { + // Do not enlarge it due to an existing segment. + uninfect(*parytet); + } + } // if (!infected(*parytet)) + if (!success) break; + } // i + + for (i = 0; i < cavpoints->objects; i++) { + parypt = (point *) fastlookup(cavpoints, i); + puninfect(*parypt); + } + + misfaces->restart(); + cavfaces->restart(); + + if (success && (missegs->objects > 0l)) { + // The cavity has been enlarged, but some segments are enclosed. + if (!smarktested(*sseg)) { + smarktest(*sseg); + fixedseglist->newindex((void **) &paryseg); + *paryseg = *sseg; + } + // SELF_CHECK + assert(cavshells->objects == 0l); + assert(newtets->objects == 0l); + // Recover the missing segments inside the enlarged cavity. + success = recoversegments(fixedseglist, missegs, cavfaces, cavshells, + misfaces, newtets); + // SELF_CHECK + assert(missegs->objects == 0l); + assert(cavshells->objects == 0l); + assert(newtets->objects == 0l); + assert(cavfaces->objects == 0l); + } + + if (success) { + // Cavity has enlarged. Continue to recover the segment. + cavityexpcount++; + continue; + } + } // if (misfaces->objects > 0) + + break; // Leave the loop. + + } // while (1) + + // Collect all tets of the DT. + marktest(recenttet); + newtets->newindex((void **) &parytet); + *parytet = recenttet; + for (i = 0; i < newtets->objects; i++) { + searchtet = * (triface *) fastlookup(newtets, i); + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, neightet); + if (!marktested(neightet)) { + marktest(neightet); + newtets->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + // Comment: All new tets are marktested. + + cavpoints->restart(); // Comment: Now no vertex is marked. + cavfaces->restart(); + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversegments() Recover segments by local remeshing. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::recoversegments(arraypool* fixedseglist, arraypool *recosegs, + arraypool* cavfaces, arraypool* cavshells, arraypool* misfaces, + arraypool* newtets) +{ + arraypool *crosstets; + arraypool *missegs; + arraypool *cavpoints; + triface searchtet; + face *psseg, sseg; + enum intersection dir; + bool success; + long bakhullsize; + long cavitycount; + int i; + + if (b->verbose > 1) { + printf(" Recovering %ld segments (%ld fixed segments).\n", + recosegs->objects, fixedseglist->objects); + } + + // Initialize arrays. + crosstets = new arraypool(sizeof(triface), 10); + missegs = new arraypool(sizeof(face), 8); + cavpoints = new arraypool(sizeof(point), 8); + + success = true; + + // Loop until 'recosegs' is empty. + while (recosegs->objects > 0l) { + // seglist is used as a stack. + recosegs->objects--; + psseg = (face *) fastlookup(recosegs, recosegs->objects); + sseg = *psseg; + + // It is not a global missing segment. + assert(!sinfected(sseg)); + + // Form the segment cavity. + searchtet.tet = NULL; + dir = scoutsegment2(&sseg, &searchtet, crosstets); + + // The segment is missing. + assert(dir != SHAREEDGE); // SELF_CHECK + + if (dir == ACROSSTET) { + // Recover the segment by local re-meshing. + bakhullsize = hullsize; + success = tetrasegcavity(&sseg, crosstets, cavpoints, cavfaces, + cavshells, newtets, misfaces, missegs, fixedseglist); + // Do not clean 'fixedseglist'. + hullsize = bakhullsize; + if (success) { + // The segment is recovered. + fillcavity(cavshells, NULL, NULL, NULL); + carvecavity(crosstets, newtets, NULL); + } else { + // Unable to recover the segment. + restorecavity(crosstets, newtets, NULL); + break; + } + } else { + // Unexpected return type. + assert(0); // Not handled yet. + } + } + + if (recosegs->objects > 0l) { + recosegs->restart(); + } + + delete crosstets; + delete missegs; + delete cavpoints; + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// constrainedsegments() Recover segments in a constrained // +// tetrahedralization. // +// // +// 'recoverseglist' contains a list of recovering segments, 'fixedseglist' // +// is an accumulated list of segments which are inside current tetrahedrali- // +// zation and must "fix" at their places (do not remove them). All segments // +// in 'fixedseglist' are smarktested. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::constrainedsegments() +{ + arraypool *crosstets, *newtets, *misfaces; + arraypool *cavfaces; + arraypool *cavpoints; + arraypool *cavshells; + arraypool *fixedseglist, *missegs; + triface searchtet; + face splitsh; + face *psseg, sseg; + point newpt, pa, pb; + enum intersection dir; + bool success; + long bakhullsize; + long cavitycount; + long steinptcount; + int i; + + if (b->verbose) { + printf(" Recovering %ld segments.\n", subsegstack->objects); + } + + // Initialize arrays. + crosstets = new arraypool(sizeof(triface), 10); + newtets = new arraypool(sizeof(triface), 10); + misfaces = new arraypool(sizeof(triface), 10); + cavfaces = new arraypool(sizeof(triface), 10); + cavshells = new arraypool(sizeof(face), 10); + cavpoints = new arraypool(sizeof(point), 8); + fixedseglist = new arraypool(sizeof(face), 8); + missegs = new arraypool(sizeof(face), 8); + + cavitycount = 0l; + steinptcount = 0l; + + // Loop until 'subsegstack' is empty. + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + psseg = (face *) fastlookup(subsegstack, subsegstack->objects); + sseg = *psseg; + + if (!sinfected(sseg)) continue; // Not a missing segment. + suninfect(sseg); + + // Insert the segment. + searchtet.tet = NULL; + dir = scoutsegment2(&sseg, &searchtet, crosstets); + + if (dir != SHAREEDGE) { + // The segment is missing. + if (dir == ACROSSTET) { + // Recover the segment by local re-meshing. + bakhullsize = hullsize; + success = tetrasegcavity(&sseg, crosstets, cavpoints, cavfaces, + cavshells, newtets, misfaces, missegs, fixedseglist); + hullsize = bakhullsize; + // Unmark the testmarked segments. + for (i = 0; i < fixedseglist->objects; i++) { + psseg = (face *) fastlookup(fixedseglist, i); + assert(smarktested(*psseg)); + sunmarktest(*psseg); + } + fixedseglist->restart(); // Clear this list. + if (success) { + // The segment is recovered. + fillcavity(cavshells, NULL, NULL, NULL); + carvecavity(crosstets, newtets, NULL); + cavitycount++; + } else { + // Unable to recover the segment. + restorecavity(crosstets, newtets, NULL); + /*// Split the segment at its middle. + makepoint(&newpt); + pa = sorg(sseg); + pb = sdest(sseg); + for (i = 0; i < 3; i++) { + newpt[i] = 0.5 * (pa[i] + pb[i]); + } + setpointtype(newpt, STEINERVERTEX); + // Insert the point into the surface mesh. + spivot(sseg, splitsh); + // Two subsegments are queued in 'subsegstack' for recovery. + sinsertvertex(newpt, &splitsh, &sseg, true, false); + // Insert the point into the CT. + point2tetorg(pa, searchtet); + // Set 'flipflag' = 3, do not flip a segment. + flipinsertvertex(newpt, &searchtet, 3); + */ + steinptcount++; + } + } else if (dir == ACROSSVERT) { + printf("Error: Invalid PLC! A point and a segment intersect.\n"); + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" Point: %d. Segment: (%d, %d).\n", pointmark(org(searchtet)), + pointmark(pa), pointmark(pb)); + terminatetetgen(1); + } else if (dir == ACROSSSUBSEG) { + printf("Error: Invalid PLC! Two segments intersect.\n"); + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" 1st: (%d, %d)", pointmark(pa), pointmark(pb)); + tsspivot(searchtet, sseg); + assert(sseg.sh != NULL); + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" 2nd: (%d, %d).\n", pointmark(pa), pointmark(pb)); + terminatetetgen(1); + } else if (dir == ACROSSSUBFACE) { + printf("Error: Invalid PLC! A segment and a subface intersect.\n"); + pa = farsorg(sseg); + pb = farsdest(sseg); + printf(" Segment: (%d, %d)", pointmark(pa), pointmark(pb)); + tspivot(searchtet, splitsh); + printf(" Subface: (%d, %d, %d)", pointmark(sorg(splitsh)), + pointmark(sdest(splitsh)), pointmark(sapex(splitsh))); + terminatetetgen(1); + } + } + } + + if (b->verbose) { + printf(" %ld cavities remeshed.\n", cavitycount); + printf(" %ld Steiner points inserted.\n", steinptcount); + } + + delete crosstets; + delete newtets; + delete misfaces; + delete cavfaces; + delete cavshells; + delete cavpoints; + delete fixedseglist; + delete missegs; } /////////////////////////////////////////////////////////////////////////////// @@ -2953,6 +3784,8 @@ void tetgenmesh::formskeleton() { face *pssub, ssub; REAL bakeps; + long bakflip22count; + long bakcavitycount; int s, i; if (!b->quiet) { @@ -2991,8 +3824,22 @@ void tetgenmesh::formskeleton() // Segments will be introduced. checksubsegs = 1; + // Recover segments. - delaunizesegments(); + if (b->nobisect == 0) { + if (b->verbose) { + printf(" Delaunizing segments.\n"); + } + + delaunizesegments(); + + if (b->verbose) { + printf(" %d protecting points.\n", r1count + r2count + r3count); + } + } else { + // -Y option, constrained recover. + constrainedsegments(); + } // Randomly order the subfaces. subfacepool->traversalinit(); @@ -3009,9 +3856,49 @@ void tetgenmesh::formskeleton() // Subfaces will be introduced. checksubfaces = 1; + bakflip22count = flip22count; + bakcavitycount = cavitycount; + + if (b->verbose) { + printf(" Constraining facets.\n"); + } + + // Initialize arrays. + tg_crosstets = new arraypool(sizeof(triface), 10); + tg_topnewtets = new arraypool(sizeof(triface), 10); + tg_botnewtets = new arraypool(sizeof(triface), 10); + tg_topfaces = new arraypool(sizeof(triface), 10); + tg_botfaces = new arraypool(sizeof(triface), 10); + tg_midfaces = new arraypool(sizeof(triface), 10); + tg_toppoints = new arraypool(sizeof(point), 8); + tg_botpoints = new arraypool(sizeof(point), 8); + tg_facpoints = new arraypool(sizeof(point), 8); + tg_facfaces = new arraypool(sizeof(face), 10); + tg_topshells = new arraypool(sizeof(face), 10); + tg_botshells = new arraypool(sizeof(face), 10); + // Recover facets. constrainedfacets(); + // Delete arrays. + delete tg_crosstets; + delete tg_topnewtets; + delete tg_botnewtets; + delete tg_topfaces; + delete tg_botfaces; + delete tg_midfaces; + delete tg_toppoints; + delete tg_botpoints; + delete tg_facpoints; + delete tg_facfaces; + delete tg_topshells; + delete tg_botshells; + + if (b->verbose) { + printf(" %ld subedge flips.\n", flip22count - bakflip22count); + printf(" %ld cavities remeshed.\n", cavitycount - bakcavitycount); + } + // checksubsegs = 0; b->epsilon = bakeps; } @@ -3108,7 +3995,7 @@ void tetgenmesh::carveholes() } } - // Find and infect all exterior tets. + // Find and infect all exterior tets (in concave place and in holes). for (i = 0; i < tetarray->objects; i++) { parytet = (triface *) fastlookup(tetarray, i); tetloop = *parytet; @@ -3177,7 +4064,8 @@ void tetgenmesh::carveholes() tetarray->restart(); // Re-use it for new hull tets. - // Create new hull faces and update the point-to-tet map. + // Create new hull tets. + // Update point-to-tet map, segment-to-tet map, and subface-to-tet map. tetrahedronpool->traversalinit(); tetloop.ver = 0; tetloop.tet = tetrahedrontraverse(); @@ -3193,7 +4081,18 @@ void tetgenmesh::carveholes() pc = apex(tetloop); setvertices(hulltet, pb, pa, pc, dummypoint); bond(tetloop, hulltet); + // Update subface-to-tet map. tsbond(hulltet, checksh); + // Update segment-to-tet map. + for (i = 0; i < 3; i++) { + tsspivot(tetloop, checkseg); + if (checkseg.sh != NULL) { + tssbond1(hulltet, checkseg); + sstbond(checkseg, hulltet); + } + enextself(tetloop); + enext2self(hulltet); + } // Save this hull tet in list. tetarray->newindex((void **) &parytet); *parytet = hulltet; @@ -3234,6 +4133,10 @@ void tetgenmesh::carveholes() ////////////////////////////////////////////////////////////////////// // Peel off "flat" tetrahedra at boundary. + // + // A tet is flat if it contains two subfaces of the same facet. + // Flat tets are possible when a facet is defined by non-exactly + // coplanar vertices. tetarray->restart(); // Re-use this array. flatcount = 0; diff --git a/contrib/Tetgen/delaunay.cxx b/contrib/Tetgen/delaunay.cxx index d97a0101e8b9387e356a2789ce69d7bdfeba71d6..a023c94b201a1c2bc4c9e64930f56ab40ed8475d 100644 --- a/contrib/Tetgen/delaunay.cxx +++ b/contrib/Tetgen/delaunay.cxx @@ -517,9 +517,9 @@ void tetgenmesh::initialDT(point pa, point pb, point pc, point pd) // If 'visflag' is TRUE, force to check the visibility of the boundary faces // // of cavity. This is needed when T is not Delaunay. // // // -// If 'noencflag' is TRUE, only insert the new point p if it does not cause // -// any existing (sub)segment be non-Delaunay. This option only is checked // -// when the global variable 'checksubsegs' is set. // +// If 'noencsegflag' is TRUE, only insert the point if it does not encroach // +// on any existing segment of the mesh. Otherwise, do not insert the point, // +// and all encroaching segments are returned in subsegstack. // // // /////////////////////////////////////////////////////////////////////////////// @@ -763,8 +763,9 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, maxbowatcavsize = cavebdrylist->objects; } - if (checksubsegs || noencsegflag) { - // Check if some (sub)segments are inside the cavity. + if (checksubsegs) { + // Check if some (sub)segments are inside the cavity. Such segments + // are queued in 'subsegstack'. for (i = 0; i < caveoldtetlist->objects; i++) { cavetet = (triface *) fastlookup(caveoldtetlist, i); for (j = 0; j < 6; j++) { @@ -788,6 +789,9 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, printf(" Queue a missing segment (%d, %d).\n", pointmark(sorg(sseg)), pointmark(sdest(sseg))); } + // All tets containing this segment will be dead, clean the + // seg-to-tet pointer. + stdissolve(sseg); sinfect(sseg); // Only save it once. subsegstack->newindex((void **) &psseg); *psseg = sseg; @@ -795,6 +799,35 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, } } } + if (noencsegflag) { + // Check for encroaching segment on the boundary of the cavity. + // Encroached segments are queued in 'subsegstack'. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // 'cavetet' is an exterior tet adjacent to the cavity. + assert(cavetet->ver == 4); // SELF_CHECK + for (j = 0; j < 3; j++) { + tsspivot(*cavetet, sseg); + if (sseg.sh != NULL) { + // Found a segment. Check it if it is not queued yet. + if (!sinfected(sseg)) { + if (checkedge4encroach(sseg, insertpt, 0)) { + if (b->verbose > 1) { + printf(" Queue an encroaching segment (%d, %d).\n", + pointmark(sorg(sseg)), pointmark(sdest(sseg))); + } + // This segment will still be connected to a tet after the + // insertion. + sinfect(sseg); // Only save it once. + subsegstack->newindex((void **) &psseg); + *psseg = sseg; + } + } + } + enextself(*cavetet); + } + } + } } if (noencsegflag && (subsegstack->objects > 0)) { @@ -818,8 +851,9 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, return ENCSEGMENT; } - if (checksubfaces || noencsubflag) { - // Check if some subfaces are inside the cavity. + if (checksubfaces) { + // Check if some subfaces are inside the cavity. Such subfaces + // are queued in 'subfacstack'. for (i = 0; i < caveoldtetlist->objects; i++) { cavetet = (triface *) fastlookup(caveoldtetlist, i); neightet.tet = cavetet->tet; @@ -843,6 +877,19 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, } } } + if (noencsubflag) { + // Check for encroaching subface on the boundary of the cavity. + // Encroached subfaces are queued in 'subfacstack'. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // 'cavetet' is an exterior tet adjacent to the cavity. + assert(cavetet->ver == 4); // SELF_CHECK + tspivot(*cavetet, checksh); + if (checksh.sh != NULL) { + // checkface4encroach(); + } + } + } } if (noencsubflag && (subfacstack->objects > 0)) { @@ -869,6 +916,8 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, if (visflag) { // If T is not a Delaunay triangulation, the formed cavity may not be // star-shaped (fig/dump-cavity-case8). Validation is needed. + // Comment: The validation is done by removing tets from the cavity + // until the cavity is star-shaped. cavetetlist->restart(); // Re-use it. for (i = 0; i < cavebdrylist->objects; i++) { cavetet = (triface *) fastlookup(cavebdrylist, i); @@ -881,7 +930,7 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, pb = dest(*cavetet); pc = apex(*cavetet); ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; - assert(ori != 0.0); // SELF_CHECK + // assert(ori != 0.0); // SELF_CHECK enqflag = (ori > 0.0); } else { enqflag = true; // A hull face. @@ -996,6 +1045,12 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, suninfect(sseg); // Dequeue a non-missing segment. } tssbond1(newtet, sseg); + sstbond(sseg, newtet); + // Do we need to care about encroached segments? + if ((badsegpool != NULL) && !smarktested(sseg)) { + // Queue the subsegment if it is encroached by the new point. + checkedge4encroach(sseg, insertpt, 1); + } } enextself(neightet); enext2self(newtet); @@ -1005,6 +1060,11 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, tspivot(neightet, checksh); if (checksh.sh != NULL) { tsbond(newtet, checksh); // Also disconnect the old bond. + // Do we need to care about encroached segments? + if ((badsubpool != NULL) && !smarktested(checksh)) { + // Queue the subface if it is encroached by the new point. + // checkface4encroach(checksh, insertpt, 1); + } } } if (updatecount > 0l) { @@ -1165,7 +1225,13 @@ enum tetgenmesh::location tetgenmesh::insertvertex(point insertpt, futureflip = flippush(futureflip, parytet, pa); } // Recover Delaunay faces. - // Set 'flipflag' = 2, s.t. all faces are checked for flipping. + // Set 'flipflag' = 2, s.t. all non-flippable faces are not ignoring, + // they are queued in the flip queue for the future flips. One key + // is that we also flip non-Delaunay segments and subfaces. This way + // we are able to recover all non-Delaunay edges/faces (no proof yet), + // i.e., the flip will terminate. + // The flipped segments and subfaces are queued in 'subsegstack' and + // 'subfacstack' for recovery. lawsonflip3d(2); } diff --git a/contrib/Tetgen/flip.cxx b/contrib/Tetgen/flip.cxx index 12b0ba76c15c49c4df0e35bf9c0018049feadc47..d32dd83fc9a3d961d3470c536a13e086ac060a29 100644 --- a/contrib/Tetgen/flip.cxx +++ b/contrib/Tetgen/flip.cxx @@ -573,18 +573,22 @@ void tetgenmesh::flip14(point newpt, triface* splittet, int flipflag) if (checkseg.sh != NULL) { tssbond1(*splittet, checkseg); tssbond1(newface, checkseg); + // Let the segment remember an adjacent tet. + sstbond(checkseg, *splittet); } enextself(newface); // [b,d], [c,d], [a,d] enext(castets[i], casface); tsspivot(casface, checkseg); if (checkseg.sh != NULL) { tssbond1(newface, checkseg); + sstbond(checkseg, newface); } enextself(newface); // [d,a], [d,b], [d,c] enext2(castets[i], casface); tsspivot(casface, checkseg); if (checkseg.sh != NULL) { tssbond1(newface, checkseg); + sstbond(checkseg, newface); } enextself(*splittet); } @@ -769,6 +773,7 @@ void tetgenmesh::flip26(point newpt, triface* splitface, int flipflag) tsspivot(casface, checkseg); if (checkseg.sh != NULL) { tssbond1(newface, checkseg); + sstbond(checkseg, newface); } enextself(casface); enextself(newface); @@ -786,6 +791,7 @@ void tetgenmesh::flip26(point newpt, triface* splitface, int flipflag) tsspivot(casface, checkseg); if (checkseg.sh != NULL) { tssbond1(newface, checkseg); + sstbond(checkseg, newface); } enextself(casface); enextself(newface); @@ -993,6 +999,7 @@ void tetgenmesh::flipn2n(point newpt, triface* splitedge, int flipflag) tsspivot(casface, checkseg); if (checkseg.sh != NULL) { tssbond1(newface, checkseg); + sstbond(checkseg, newface); } enextself(casface); enextself(newface); @@ -1005,6 +1012,7 @@ void tetgenmesh::flipn2n(point newpt, triface* splitedge, int flipflag) tsspivot(casface, checkseg); if (checkseg.sh != NULL) { tssbond1(newface, checkseg); + sstbond(checkseg, newface); } enextself(casface); enextself(newface); @@ -1257,6 +1265,7 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag) enextfnext(fliptets[i], newface); enextself(newface); tssbond1(newface, checkseg); + sstbond(checkseg, newface); } } // The top three: da, db, dc. Each edge belongs to two tets. @@ -1266,9 +1275,11 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag) if (checkseg.sh != NULL) { enext(fliptets[i], newface); tssbond1(newface, checkseg); + sstbond(checkseg, newface); enext0fnext(fliptets[(i + 2) % 3], newface); enextself(newface); tssbond1(newface, checkseg); + sstbond(checkseg, newface); } } // The bot three: ae, be, ce. Each edge belongs to two tets. @@ -1278,9 +1289,11 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag) if (checkseg.sh != NULL) { enext2(fliptets[i], newface); tssbond1(newface, checkseg); + sstbond(checkseg, newface); enext0fnext(fliptets[(i + 2) % 3], newface); enext2self(newface); tssbond1(newface, checkseg); + sstbond(checkseg, newface); } } } @@ -1455,15 +1468,19 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag) if (checksubsegs) { // Check if the flip edge is subsegment. tsspivot(fliptets[0], checkseg); - if ((checkseg.sh != NULL) && !sinfected(checkseg)) { - // This subsegment will be flipped. Queue it. - if (b->verbose > 1) { - printf(" Queue a flipped segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + if ((checkseg.sh != NULL)) { + if (!sinfected(checkseg)) { + // This subsegment will be flipped. Queue it. + if (b->verbose > 1) { + printf(" Queue a flipped segment (%d, %d).\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + } + sinfect(checkseg); // Only save it once. + subsegstack->newindex((void **) &pssub); + *pssub = checkseg; } - sinfect(checkseg); // Only save it once. - subsegstack->newindex((void **) &pssub); - *pssub = checkseg; + // Clean the seg-to-tet pointer. + stdissolve(checkseg); } } @@ -1566,6 +1583,7 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag) tsspivot(topcastets[i], checkseg); if (checkseg.sh != NULL) { tssbond1(fliptets[0], checkseg); + sstbond(checkseg, fliptets[0]); } enextself(fliptets[0]); } @@ -1577,6 +1595,7 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag) tsspivot(casface, checkseg); if (checkseg.sh != NULL) { tssbond1(newface, checkseg); + sstbond(checkseg, newface); } enextself(fliptets[0]); } @@ -1585,6 +1604,7 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag) tsspivot(botcastets[i], checkseg); if (checkseg.sh != NULL) { tssbond1(fliptets[1], checkseg); + sstbond(checkseg, fliptets[1]); } enext2self(fliptets[1]); } @@ -1596,6 +1616,7 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag) tsspivot(casface, checkseg); if (checkseg.sh != NULL) { tssbond1(newface, checkseg); + sstbond(checkseg, newface); } enext2self(fliptets[1]); } @@ -1873,7 +1894,7 @@ void tetgenmesh::lawsonflip3d(int flipflag) point *pt, pd, pe; REAL sign, ori; long flipcount; - // bool bflag; // for flipflag = 3. + bool bflag; // for flipflag = 3. int n, i; int *iptr; @@ -1963,29 +1984,29 @@ void tetgenmesh::lawsonflip3d(int flipflag) } if (i == 3) { // A 2-to-3 flip is found. - // if (flipflag == 3) { - // // Do not flip a subface. - // tspivot(fliptet, checksh); - // bflag = (checksh.sh == NULL); - // } else { - // bflag = true; - // } - // if (bflag) { + if (flipflag == 3) { + // Do not flip a subface. + tspivot(fliptet, checksh); + bflag = (checksh.sh == NULL); + } else { + bflag = true; + } + if (bflag) { fliptets[0] = fliptet; // tet abcd, d is the new vertex. symedge(fliptets[0], fliptets[1]); // tet bace. flip23(fliptets, 0, flipflag); recenttet = fliptets[0]; // for point location. - // } + } } else { // A 3-to-2 or 4-to-4 may possible. - // if (flipflag == 3) { - // // Do not flip a subsegment. - // tsspivot(fliptet, checkseg); - // bflag = (checkseg.sh == NULL); - // } else { - // bflag = true; - // } - // if (bflag) { + if (flipflag == 3) { + // Do not flip a subsegment. + tsspivot(fliptet, checkseg); + bflag = (checkseg.sh == NULL); + } else { + bflag = true; + } + if (bflag) { enext0fnext(fliptet, fliptets[0]); esymself(fliptets[0]); // tet badc, d is the new vertex. n = 0; @@ -2015,19 +2036,29 @@ void tetgenmesh::lawsonflip3d(int flipflag) recenttet = fliptets[0]; // for point location. } else { // An unflipable face. Will be flipped later. - if (flipflag > 1) { + if (flipflag == 2) { // if (flipflag > 1) { // Queue all other faces at the edge for flipping. + if (b->verbose > 1) { + printf(" Queue an N32 edge (%d, %d)", + pointmark(org(fliptets[0])), pointmark(dest(fliptets[0]))); + } pe = apex(fliptets[0]); fliptets[1] = fliptets[0]; while (1) { + if (b->verbose > 1) { + printf(" %d", pointmark(apex(fliptets[1]))); + } pd = oppo(fliptets[1]); futureflip = flippush(futureflip, &fliptets[1], pd); fnextself(fliptets[1]); if (apex(fliptets[1]) == pe) break; } + if (b->verbose > 1) { + printf("\n"); + } } } - // } // bflag + } // bflag } // if (i == 3) } // if (sign < 0) } diff --git a/contrib/Tetgen/main.cxx b/contrib/Tetgen/main.cxx index dafc3a88779712980127b355ae6e461a29329e9b..82edf9afd5dbe80b560e2f0d3c051697d15835ab 100644 --- a/contrib/Tetgen/main.cxx +++ b/contrib/Tetgen/main.cxx @@ -187,20 +187,32 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, printf("seconds: %g\n", (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); } } + + if (b->quality) { + m.enforcequality(); + } + + tv[5] = clock(); + + if (!b->quiet) { + if (b->quality) { + printf("Quality seconds: %g\n", (tv[5] - tv[4])/(REAL) CLOCKS_PER_SEC); + } + } - if (b->plc) { + if (b->plc) { if (b->convexity == 0) { // if has no -c option. m.carveholes(); } } - tv[5] = clock(); + tv[6] = clock(); if (!b->quiet) { if (b->plc) { if (b->convexity == 0) { // if has no -c option. printf("Holes and region seconds: %g\n", - (tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC); + (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC); } } } @@ -284,7 +296,8 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, if (b->docheck) { if (b->plc) { - m.checkshells(1); + if (m.checkshells(1) > 0) assert(0); + if (m.checksegments() > 0) assert(0); } if (m.checkdelaunay(b->plc) > 0) assert(0); } @@ -348,7 +361,7 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, } // FOR DEBUG -S1 option. - if (b.steiner > 0) { + if (b.steinerleft > 0) { test_tri_tri(&b, &in); terminatetetgen(0); } diff --git a/contrib/Tetgen/meshstat.cxx b/contrib/Tetgen/meshstat.cxx index 8c4e7ec30492c9bd11a3358b269b6ba20013d6da..c5c0c992c1ee91c73a2b9e6bac8a8c0288ddb249 100644 --- a/contrib/Tetgen/meshstat.cxx +++ b/contrib/Tetgen/meshstat.cxx @@ -104,13 +104,13 @@ int tetgenmesh::mi1mo3[3] = {2, 0, 1}; // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::checkmesh() +int tetgenmesh::checkmesh() { triface tetloop; triface neightet, symtet; point pa, pb, pc, pd; REAL ori; - int horrors; + int horrors, i; if (!b->quiet) { printf(" Checking consistency of mesh...\n"); @@ -138,11 +138,13 @@ void tetgenmesh::checkmesh() horrors++; } } - if (infected(tetloop)) { + if (infected(tetloop)) { + // This may be a bug. Report it. printf(" !! (%d, %d, %d, %d) is infected.\n", pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd)); } if (marktested(tetloop)) { + // This may be a bug. Report it. printf(" !! (%d, %d, %d, %d) is marked.\n", pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd)); } @@ -178,6 +180,16 @@ void tetgenmesh::checkmesh() pointmark(oppo(neightet))); horrors++; } + // Check if they have the same apex. + if (apex(neightet) != pc) { + printf(" !! !! Wrong face-face bond:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } // Check if they have the same opposite. if (oppo(neightet) == pd) { printf(" !! !! Two identical tetra:\n"); @@ -189,6 +201,22 @@ void tetgenmesh::checkmesh() horrors++; } } + if (facemarked(tetloop)) { + // This may be a bug. Report it. + printf(" !! tetface (%d, %d, %d) %d is marked.\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + } + // Check the six edges of this tet. + for (i = 0; i < 6; i++) { + tetloop.loc = edge2locver[i][0]; + tetloop.ver = edge2locver[i][1]; + if (edgemarked(tetloop)) { + // This may be a bug. Report it. + printf(" !! tetedge (%d, %d) %d, %d is marked.\n", + pointmark(org(tetloop)), pointmark(dest(tetloop)), + pointmark(apex(tetloop)), pointmark(oppo(tetloop))); + } } tetloop.tet = tetrahedrontraverse(); } @@ -200,6 +228,8 @@ void tetgenmesh::checkmesh() printf(" !! !! !! !! %d %s witnessed.\n", horrors, horrors > 1 ? "abnormity" : "abnormities"); } + + return horrors; } /////////////////////////////////////////////////////////////////////////////// @@ -215,7 +245,8 @@ int tetgenmesh::checkshells(int sub2tet) triface neightet, symtet; face shloop, spinsh, nextsh; face checkseg; - point pa, pb; + point pa, pb, *ppt; + int count; int horrors, i; tetrahedron ptr; @@ -273,9 +304,33 @@ int tetgenmesh::checkshells(int sub2tet) // Check the tet-subface connections. stpivot(shloop, neightet); if (neightet.tet != NULL) { + // Check if the tet and subface have the same vertices. + ppt = (point *) shloop.sh; + for (i = 0; i < 3; i++) { + pinfect(ppt[3 + i]); // Infect the subface vertices. + } + ppt = (point *) neightet.tet; + count = 0; // Count the infected vertices of this tet. + for (i = 0; i < 4; i++) { + if (pinfected(ppt[4 + i])) count++; + } + ppt = (point *) shloop.sh; + for (i = 0; i < 3; i++) { + puninfect(ppt[3 + i]); // Uninfect the subface vertices. + } + if (count != 3) { + printf(" !! !! Wrong tet-sub connection (face does not match).\n"); + printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh, + pmark(sorg(shloop)), pmark(sdest(shloop)), pmark(sapex(shloop))); + printf(" Tet: x%lx (%d, %d, %d, %d).\n", + (unsigned long) neightet.tet, pmark(org(neightet)), + pmark(dest(neightet)), pmark(apex(neightet)), + pmark(oppo(neightet))); + horrors++; + } tspivot(neightet, spinsh); if (spinsh.sh != shloop.sh) { - printf(" !! !! Wrong connection betwee tet and subface.\n"); + printf(" !! !! Wrong tet-sub connection (wrong subfaces).\n"); printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh, pmark(sorg(shloop)), pmark(sdest(shloop)), pmark(sapex(shloop))); printf(" Tet: x%lx (%d, %d, %d, %d).\n", @@ -287,7 +342,7 @@ int tetgenmesh::checkshells(int sub2tet) symself(neightet); tspivot(neightet, spinsh); if (spinsh.sh != shloop.sh) { - printf(" !! !! Wrong connection betwee tet and subface.\n"); + printf(" !! !! Wrong tet-sub connection (wrong subfaces).\n"); printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long) shloop.sh, pmark(sorg(shloop)), pmark(sdest(shloop)), pmark(sapex(shloop))); printf(" Tet: x%lx (%d, %d, %d, %d).\n", @@ -304,6 +359,16 @@ int tetgenmesh::checkshells(int sub2tet) // horrors++; } } + if (sinfected(shloop)) { + // This may be a bug. report it. + printf(" !! A infected subface: (%d, %d, %d).\n", pmark(sorg(shloop)), + pmark(sdest(shloop)), pmark(sapex(shloop))); + } + if (smarktested(shloop)) { + // This may be a bug. report it. + printf(" !! A marked subface: (%d, %d, %d).\n", pmark(sorg(shloop)), + pmark(sdest(shloop)), pmark(sapex(shloop))); + } shloop.sh = shellfacetraverse(subfacepool); } @@ -440,11 +505,11 @@ int tetgenmesh::checksegments() do { tsspivot(neightet, checkseg); if (checkseg.sh != sseg.sh) { - printf(" !! Wrong tet-seg connection.\n"); + printf(" !! Wrong tet->seg connection.\n"); printf(" Tet: x%lx (%d, %d, %d, %d) - ", - (unsigned long) tetloop.tet, pointmark(org(tetloop)), - pointmark(dest(tetloop)), pointmark(apex(tetloop)), - pointmark(oppo(tetloop))); + (unsigned long) neightet.tet, pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); if (checkseg.sh != NULL) { printf("Seg x%lx (%d, %d).\n", (unsigned long) checkseg.sh, pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); @@ -456,6 +521,23 @@ int tetgenmesh::checksegments() fnextself(neightet); } while (neightet.tet != tetloop.tet); } + // Check the seg->tet pointer. + stpivot(sseg, neightet); + if (neightet.tet == NULL) { + printf(" !! Wrong seg->tet connection (A NULL tet).\n"); + horrors++; + } else { + if (!(((org(neightet) == pa) && (dest(neightet) == pb)) || + ((org(neightet) == pb) && (dest(neightet) == pa)))) { + printf(" !! Wrong seg->tet connection (Wrong edge).\n"); + printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", + (unsigned long) neightet.tet, pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet)), (unsigned long) sseg.sh, + pointmark(pa), pointmark(pb)); + horrors++; + } + } } } } @@ -471,6 +553,42 @@ int tetgenmesh::checksegments() return horrors; } +/////////////////////////////////////////////////////////////////////////////// +// // +// checkconforming() Check if the mesh is boundary conforming Delaunay. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkconforming() +{ + face shloop; + int horrors; + + horrors = 0; + + // Find all encroached segments. + subsegpool->traversalinit(); + shloop.shver = 0; + shloop.sh = shellfacetraverse(subsegpool); + while (shloop.sh != NULL) { + if (checkedge4encroach(shloop, NULL, 0)) { + printf(" !! Segment x%lx (%d, %d) is encroached.\n", + (unsigned long) shloop.sh, pointmark(sorg(shloop)), + pointmark(sdest(shloop))); + horrors++; + } + shloop.sh = shellfacetraverse(subsegpool); + } + + if (horrors == 0) { + printf(" Segments are boundary conforming Delaunay.\n"); + } else { + printf(" !! !! !! !! Found %d encroached segments.\n", horrors); + } + + return horrors; +} + /////////////////////////////////////////////////////////////////////////////// // // // algorithmstatistics() Report algorithmic performances. // @@ -592,8 +710,8 @@ void tetgenmesh::statistics() if (b->verbose > 0) { printf(" Euler characteristic of mesh domain: %ld\n", pointpool->items - meshedges + facenumber - tetnumber); - printf(" Euler characteristic of boundary: %ld\n", pointpool->items - - meshsubedges + subfacepool->items); + //printf(" Euler characteristic of boundary: %ld\n", pointpool->items + // - meshsubedges + subfacepool->items); printf("\n"); } @@ -871,13 +989,16 @@ void tetgenmesh::psh(face *s) } else { printf(" [8] = x%lx %d\n", (unsigned long) prtsh.sh, prtsh.shver); } + } - decode(s->sh[9], prttet); - if (prttet.tet == NULL) { + // Print the adjacent tet of this subface or segment. + decode(s->sh[9], prttet); + if (prttet.tet == NULL) { printf(" [9] = Outer space\n"); - } else { - printf(" [9] = x%lx %d\n",(unsigned long) prttet.tet, prttet.loc); - } + } else { + printf(" [9] = x%lx (%d, %d, %d, %d)\n",(unsigned long) prttet.tet, + pointmark(org(prttet)), pointmark(dest(prttet)), + pointmark(apex(prttet)), pointmark(oppo(prttet))); } } @@ -1129,9 +1250,11 @@ void tetgenmesh::psubface(int i, int j, int k) /////////////////////////////////////////////////////////////////////////////// // Print the information of the subsegment (i, j). +// Return 1 if seg-to-tet pointer is broken. -void tetgenmesh::psubseg(int i, int j) +int tetgenmesh::psubseg(int i, int j) { + triface t; face s; //, s1; point forg, fdest; bool bflag; @@ -1157,6 +1280,26 @@ void tetgenmesh::psubseg(int i, int j) fdest = farsdest(s); printf(" seg x%lx (%d, %d) < (%d, %d)\n", (unsigned long) s.sh, i, j, pointmark(forg), pointmark(fdest)); + // Chekc seg-to-tet pointer + stpivot(s, t); + if (t.tet != NULL) { + if (t.tet[4] != NULL) { + forg = org(t); + fdest = dest(t); + if (((pointmark(forg) == i) && (pointmark(fdest) == j)) || + ((pointmark(forg) == j) && (pointmark(fdest) == i))) { + printf(" adj tet x%lx (%d, %d, %d, %d)\n", (unsigned long) t.tet, + pointmark(forg), pointmark(fdest), pointmark(apex(t)), + pointmark(oppo(t))); + } else { + printf(" !! Wrong seg-to-tet pointer.\n"); + return 1; + } + } else { + printf(" !! A DEAD seg-to-tet pointer.\n"); + return 1; + } + } /*// Print the adjacent subsegments at i and j. senext2(s, s1); spivotself(s1); @@ -1184,6 +1327,7 @@ void tetgenmesh::psubseg(int i, int j) if (!bflag) { printf(" !! Not exist.\n"); } + return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -1571,4 +1715,50 @@ void tetgenmesh::dump_facetof(face *pssub, char *filename) delete tmpfaces; } +/////////////////////////////////////////////////////////////////////////////// +// Dump the new tets of a cavity (in draw_tet() lua commands) + +void tetgenmesh::dump_cavitynewtets() +{ + arraypool *newtets; + triface searchtet, neightet, *parytet; + point *ppt; + int i; + + newtets = new arraypool(sizeof(triface), 8); + + // Collect all tets of the DT. + marktest(recenttet); + newtets->newindex((void **) &parytet); + *parytet = recenttet; + for (i = 0; i < newtets->objects; i++) { + searchtet = * (triface *) fastlookup(newtets, i); + for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) { + sym(searchtet, neightet); + if (!marktested(neightet)) { + marktest(neightet); + newtets->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + // Comment: All new tets are marktested. + + // Output the new tets. + for (i = 0; i < newtets->objects; i++) { + parytet = (triface *) fastlookup(newtets, i); + ppt = (point *) parytet->tet; + if (ppt[7] != dummypoint) { + printf("p:draw_tet(%d, %d, %d, %d) -- %i\n", pointmark(ppt[4]), + pointmark(ppt[5]), pointmark(ppt[6]), pointmark(ppt[7]), i); + } else { + printf("-- p:draw_tet(%d, %d, %d, -1) -- %i\n", pointmark(ppt[4]), + pointmark(ppt[5]), pointmark(ppt[6]), i); + } + unmarktest(*parytet); // Unmarktest it. + } + + delete newtets; +} + #endif // #ifndef meshstatCXX \ No newline at end of file diff --git a/contrib/Tetgen/surface.cxx b/contrib/Tetgen/surface.cxx index b0305591abb2247454d9e24ef258673532bb8f1e..e9a72b232d3f59cb1a2872968275a0cf63d7125d 100644 --- a/contrib/Tetgen/surface.cxx +++ b/contrib/Tetgen/surface.cxx @@ -260,7 +260,7 @@ enum tetgenmesh::location tetgenmesh::sinsertvertex(point insertpt, face neighsh, newsh, casout, casin; face aseg, bseg, aoutseg, boutseg; face checkseg; - triface neightet; + triface neightet, spintet; point pa, pb, pc; enum location loc; REAL sign, ori, area; @@ -598,6 +598,22 @@ enum tetgenmesh::location tetgenmesh::sinsertvertex(point insertpt, printf(" Split seg (%d, %d) by %d.\n", pointmark(pa), pointmark(pb), pointmark(insertpt)); } + // Detach the adjacent tets from this segment. + stpivot(aseg, neightet); + if (neightet.tet != NULL) { + // It should not be a dead tet. + assert(neightet.tet[4] != NULL); // SELF_CHECK + assert(((org(neightet) == pa) && (dest(neightet) == pb)) || + ((org(neightet) == pb) && (dest(neightet) == pa))); // SELF_CHECK + spintet = neightet; + while (1) { + tssdissolve(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + // Clean the seg-to-tet pointer. + stdissolve(aseg); + } // Insert the new point p. makeshellface(subsegpool, &bseg); setshvertices(bseg, insertpt, pb, NULL); diff --git a/contrib/Tetgen/tetgen.h b/contrib/Tetgen/tetgen.h index b44a68876f4033b6c4503dc76c28ee9bd8247e00..b7e971f039fc8b72e3d06494d3e3b60989664b53 100644 --- a/contrib/Tetgen/tetgen.h +++ b/contrib/Tetgen/tetgen.h @@ -442,11 +442,11 @@ class tetgenbehavior { // Begin of class tetgenbehavior int nomerge; // -M int nobisect; // -Y int nojettison; // -J - int steiner; // -S with a number int docheck; // -C int quiet; // -Q int verbose; // -V int useshelles; // -p, -r, -q, -d, or -R + long steinerleft; // -S with a number REAL minratio; // number after -q REAL goodratio; // number calculated from 'minratio' REAL minangle; // minimum angle bound @@ -1373,9 +1373,13 @@ void tssbond1(triface& t, face& s) { } // Bond the segment. ((shellface *) (t).tet[8])[locver2edge[(t).loc][(t).ver]] = sencode((s)); -} +} + +// sstbond() -- bond a tet edge to a segment (only at the segment side). -// tssdissolve() -- dissolve a tet-seg bond at the tet edge. +#define sstbond(s, t) (s).sh[9] = (shellface) encode(t) + +// tssdissolve() -- dissolve a tet-seg bond at the tet edge side. void tssdissolve(triface& t) { if ((t).tet[8] != NULL) { @@ -1383,6 +1387,9 @@ void tssdissolve(triface& t) { } } +// sstdissolve() -- dissolve a tet-seg bond at the segment side. +// Use stdissolve(). + /////////////////////////////////////////////////////////////////////////////// // // // Primitives for points // @@ -1473,6 +1480,7 @@ memorypool *subsegpool, *subfacepool; memorypool *tet2segpool, *tet2subpool; memorypool *pointpool; memorypool *flippool; +memorypool *badsegpool, *badsubpool, *badtetpool; // A dummy point at infinity (see [Guibas & Stolfi 85]). All the hull // tetrahedra having this point as a vertex. @@ -1488,6 +1496,12 @@ arraypool *caveshlist, *caveshbdlist; // Stacks used by the boundary recovery algorithm. arraypool *subsegstack, *subfacstack; +// Arrays used for facet recovery. +arraypool *tg_crosstets, *tg_topnewtets, *tg_botnewtets; +arraypool *tg_topfaces, *tg_botfaces, *tg_midfaces; +arraypool *tg_topshells, *tg_botshells, *tg_facfaces; +arraypool *tg_toppoints, *tg_botpoints, *tg_facpoints; + // Variables for accessing data fields (initialized in initializepools()). int point2tetindex, pointmarkindex; int elemmarkerindex; @@ -1529,7 +1543,7 @@ long triedgcount, triedgcopcount, trivercopcount; long across_face_count, across_edge_count, across_max_count; long r1count, r2count, r3count; long maxcavsize, maxregionsize; -long ndelaunayedgecount, cavityexpcount; +long cavitycount, ndelaunayedgecount, cavityexpcount; /////////////////////////////////////////////////////////////////////////////// // // @@ -1681,6 +1695,8 @@ void meshsurface(); enum intersection finddirection(triface* searchtet, point endpt); enum intersection scoutsegment(face* sseg, triface* searchtet, point* refpt); void getsegmentsplitpoint(face* sseg, point refpt, REAL* vt); +void getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt); +void getsegmentsplitpoint3(face* sseg, point refpt, REAL* vt); void delaunizesegments(); enum intersection scoutsubface(face* ssub, triface* searchtet); @@ -1696,12 +1712,27 @@ void restorecavity(arraypool*, arraypool*, arraypool*); void splitsubedge(point, face*, arraypool*, arraypool*); void constrainedfacets(); -enum intersection scoutedge(point, point, triface*, arraypool*, arraypool*, - arraypool*); +enum intersection scoutsegment2(face*, triface*, arraypool*); +bool tetrasegcavity(face*, arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*, arraypool*, arraypool*); +bool recoversegments(arraypool*, arraypool*, arraypool*, arraypool*, + arraypool*, arraypool*); +void constrainedsegments(); void formskeleton(); void carveholes(); +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh refinement functions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool checkedge4encroach(face& seg, point testpt, int enqflag); +void repairencsegs(); + +void enforcequality(); + /////////////////////////////////////////////////////////////////////////////// // // // Mesh input & output functions. // @@ -1730,11 +1761,11 @@ void outvoronoi(tetgenio* out); // // /////////////////////////////////////////////////////////////////////////////// -void checkmesh(); +int checkmesh(); int checkshells(int); int checkdelaunay(int); int checksegments(); -void checkconforming(); +int checkconforming(); void algorithmstatistics(); void qualitystatistics(); void statistics(); @@ -1754,11 +1785,16 @@ void initialize() tet2segpool = tet2subpool = (memorypool *) NULL; pointpool = (memorypool *) NULL; flippool = (memorypool *) NULL; + badsegpool = badsubpool = badtetpool = (memorypool *) NULL; dummypoint = (point) NULL; futureflip = (badface *) NULL; cavetetlist = cavebdrylist = caveoldtetlist = (arraypool *) NULL; caveshlist = caveshbdlist = (arraypool *) NULL; subsegstack = subfacstack = (arraypool *) NULL; + arraypool *tg_crosstets, *tg_topnewtets, *tg_botnewtets; + tg_topfaces = tg_botfaces = tg_midfaces = NULL; + tg_topshells = tg_botshells = tg_facfaces = NULL; + tg_toppoints = tg_botpoints = tg_facpoints = NULL; point2tetindex = pointmarkindex = 0; elemmarkerindex = 0; elemattribindex = volumeboundindex = highorderindex = 0; @@ -1781,7 +1817,7 @@ void initialize() across_face_count = across_edge_count = across_max_count = 0l; r1count = r2count = r3count = 0l; maxcavsize = maxregionsize = 0l; - ndelaunayedgecount = cavityexpcount = 0l; + cavitycount = ndelaunayedgecount = cavityexpcount = 0l; } void deinitialize() @@ -1834,7 +1870,7 @@ void pteti(int i, int j, int k, int l); void pface(int i, int j, int k); bool pedge(int i, int j); void psubface(int i, int j, int k); -void psubseg(int i, int j); +int psubseg(int i, int j); int pmark(point p); void pvert(point p); int pverti(int i); @@ -1848,6 +1884,7 @@ void print_facearray(arraypool* facearray); void print_subfacearray(arraypool* subfacearray); void dump_cavity(arraypool *topfaces, arraypool *botfaces); void dump_facetof(face* pssub, char* filename); +void dump_cavitynewtets(); /////////////////////////////////////////////////////////////////////////////// }; // End of class tetgenmesh;