static bool CheckForLeftSplice(Tesselator tess, ActiveRegion regUp) /* * Check the upper and lower edge of "regUp", to make sure that the * eUp.Dst is above eLo, or eLo.Dst is below eUp (depending on which * destination is rightmost). * * Theoretically, this should always be true. However, splitting an edge * into two pieces can change the results of previous tests. For example, * suppose at one point we checked eUp and eLo, and decided that eUp.Dst * is barely above eLo. Then later, we split eLo into two edges (eg. from * a splice operation like this one). This can change the result of * the test so that now eUp.Dst is incident to eLo, or barely below it. * We must correct this condition to maintain the dictionary invariants * (otherwise new edges might get inserted in the wrong place in the * dictionary, and bad stuff will happen). * * We fix the problem by just splicing the offending vertex into the * other edge. */ { ActiveRegion regLo = RegionBelow(regUp); HalfEdge eUp = regUp.upperHalfEdge; HalfEdge eLo = regLo.upperHalfEdge; HalfEdge e; if (eUp.directionVertex.VertEq(eLo.directionVertex)) { throw new Exception(); } if (eUp.directionVertex.VertLeq(eLo.directionVertex)) { if (ContourVertex.EdgeSign(eUp.directionVertex, eLo.directionVertex, eUp.originVertex) < 0) { return false; } /* eLo.Dst is above eUp, so splice eLo.Dst into eUp */ regUp.RegionAbove().dirty = regUp.dirty = true; e = Mesh.meshSplitEdge(eUp); Mesh.meshSplice(eLo.otherHalfOfThisEdge, e); e.leftFace.isInterior = regUp.inside; } else { if (ContourVertex.EdgeSign(eLo.directionVertex, eUp.directionVertex, eLo.originVertex) > 0) return false; /* eUp.Dst is below eLo, so splice eUp.Dst into eLo */ regUp.dirty = regLo.dirty = true; e = Mesh.meshSplitEdge(eLo); Mesh.meshSplice(eUp.nextEdgeCCWAroundLeftFace, eLo.otherHalfOfThisEdge); e.rightFace.isInterior = regUp.inside; } return true; }
static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp) /* * Check the upper and lower edges of the given region to see if * they intersect. If so, create the intersection and add it * to the data structures. * * Returns true if adding the new intersection resulted in a recursive * call to AddRightEdges(); in this case all "dirty" regions have been * checked for intersections, and possibly regUp has been deleted. */ { ActiveRegion regLo = RegionBelow(regUp); HalfEdge eUp = regUp.upperHalfEdge; HalfEdge eLo = regLo.upperHalfEdge; ContourVertex orgUp = eUp.originVertex; ContourVertex orgLo = eLo.originVertex; ContourVertex dstUp = eUp.directionVertex; ContourVertex dstLo = eLo.directionVertex; double tMinUp, tMaxLo; ContourVertex isect = new ContourVertex(); ContourVertex orgMin; HalfEdge e; if (dstLo.VertEq(dstUp)) { throw new Exception(); } if (ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, orgUp) > 0) { throw new Exception(); } if (ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, orgLo) < 0) { throw new Exception(); } if (orgUp == tess.currentSweepVertex || orgLo == tess.currentSweepVertex) { throw new Exception(); } if (regUp.fixUpperEdge || regLo.fixUpperEdge) { throw new Exception(); } if (orgUp == orgLo) { return false; /* right endpoints are the same */ } tMinUp = Math.Min(orgUp.y, dstUp.y); tMaxLo = Math.Max(orgLo.y, dstLo.y); if (tMinUp > tMaxLo) { return false; /* t ranges do not overlap */ } if (orgUp.VertLeq(orgLo)) { if (ContourVertex.EdgeSign(dstLo, orgUp, orgLo) > 0) { return false; } } else { if (ContourVertex.EdgeSign(dstUp, orgLo, orgUp) < 0) { return false; } } EdgeIntersect(dstUp, orgUp, dstLo, orgLo, ref isect); // The following properties are guaranteed: if (!(Math.Min(orgUp.y, dstUp.y) <= isect.y)) { throw new System.Exception(); } if (!(isect.y <= Math.Max(orgLo.y, dstLo.y))) { throw new System.Exception(); } if (!(Math.Min(dstLo.x, dstUp.x) <= isect.x)) { throw new System.Exception(); } if (!(isect.x <= Math.Max(orgLo.x, orgUp.x))) { throw new System.Exception(); } if (isect.VertLeq(tess.currentSweepVertex)) { /* The intersection point lies slightly to the left of the sweep line, * so move it until it''s slightly to the right of the sweep line. * (If we had perfect numerical precision, this would never happen * in the first place). The easiest and safest thing to do is * replace the intersection by tess.currentSweepVertex. */ isect.x = tess.currentSweepVertex.x; isect.y = tess.currentSweepVertex.y; } /* Similarly, if the computed intersection lies to the right of the * rightmost origin (which should rarely happen), it can cause * unbelievable inefficiency on sufficiently degenerate inputs. * (If you have the test program, try running test54.d with the * "X zoom" option turned on). */ orgMin = orgUp.VertLeq(orgLo) ? orgUp : orgLo; if (orgMin.VertLeq(isect)) { isect.x = orgMin.x; isect.y = orgMin.y; } if (isect.VertEq(orgUp) || isect.VertEq(orgLo)) { /* Easy case -- intersection at one of the right endpoints */ CheckForRightSplice(tess, regUp); return false; } if ((!dstUp.VertEq(tess.currentSweepVertex) && ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, isect) >= 0) || (!dstLo.VertEq(tess.currentSweepVertex) && ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, isect) <= 0)) { /* Very unusual -- the new upper or lower edge would pass on the * wrong side of the sweep currentSweepVertex, or through it. This can happen * due to very small numerical errors in the intersection calculation. */ if (dstLo == tess.currentSweepVertex) { /* Splice dstLo into eUp, and process the new region(s) */ Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); Mesh.meshSplice(eLo.otherHalfOfThisEdge, eUp); regUp = TopLeftRegion(regUp); eUp = RegionBelow(regUp).upperHalfEdge; FinishLeftRegions(tess, RegionBelow(regUp), regLo); AddRightEdges(tess, regUp, eUp.Oprev, eUp, eUp, true); return true; } if (dstUp == tess.currentSweepVertex) { /* Splice dstUp into eLo, and process the new region(s) */ Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); Mesh.meshSplice(eUp.nextEdgeCCWAroundLeftFace, eLo.Oprev); regLo = regUp; regUp = TopRightRegion(regUp); e = RegionBelow(regUp).upperHalfEdge.Rprev; regLo.upperHalfEdge = eLo.Oprev; eLo = FinishLeftRegions(tess, regLo, null); AddRightEdges(tess, regUp, eLo.nextEdgeCCWAroundOrigin, eUp.Rprev, e, true); return true; } /* Special case: called from ConnectRightVertex. If either * edge passes on the wrong side of tess.currentSweepVertex, split it * (and wait for ConnectRightVertex to splice it appropriately). */ if (ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, isect) >= 0) { regUp.RegionAbove().dirty = regUp.dirty = true; Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); eUp.originVertex.x = tess.currentSweepVertex.x; eUp.originVertex.y = tess.currentSweepVertex.y; } if (ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, isect) <= 0) { regUp.dirty = regLo.dirty = true; Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); eLo.originVertex.x = tess.currentSweepVertex.x; eLo.originVertex.y = tess.currentSweepVertex.y; } /* leave the rest for ConnectRightVertex */ return false; } /* General case -- split both edges, splice into new vertex. * When we do the splice operation, the order of the arguments is * arbitrary as far as correctness goes. However, when the operation * creates a new face, the work done is proportional to the size of * the new face. We expect the faces in the processed part of * the mesh (ie. eUp.Lface) to be smaller than the faces in the * unprocessed original contours (which will be eLo.Oprev.Lface). */ Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); Mesh.meshSplice(eLo.Oprev, eUp); eUp.originVertex.x = isect.x; eUp.originVertex.y = isect.y; tess.vertexPriorityQue.Add(out eUp.originVertex.priorityQueueHandle, eUp.originVertex); /* __gl_pqSortInsert */ GetIntersectData(tess, eUp.originVertex, orgUp, dstUp, orgLo, dstLo); regUp.RegionAbove().dirty = regUp.dirty = regLo.dirty = true; return false; }
static void ComputeWinding(Tesselator tess, ActiveRegion reg) { reg.windingNumber = reg.RegionAbove().windingNumber + reg.upperHalfEdge.winding; reg.inside = tess.IsWindingInside(reg.windingNumber); }
static bool CheckForRightSplice(Tesselator tess, ActiveRegion regUp) /* * Check the upper and lower edge of "regUp", to make sure that the * eUp.Org is above eLo, or eLo.Org is below eUp (depending on which * origin is leftmost). * * The main purpose is to splice right-going edges with the same * dest vertex and nearly identical slopes (ie. we can't distinguish * the slopes numerically). However the splicing can also help us * to recover from numerical errors. For example, suppose at one * point we checked eUp and eLo, and decided that eUp.Org is barely * above eLo. Then later, we split eLo into two edges (eg. from * a splice operation like this one). This can change the result of * our test so that now eUp.Org is incident to eLo, or barely below it. * We must correct this condition to maintain the dictionary invariants. * * One possibility is to check these edges for intersection again * (ie. CheckForIntersect). This is what we do if possible. However * CheckForIntersect requires that tess.currentSweepVertex lies between eUp and eLo, * so that it has something to fall back on when the intersection * calculation gives us an unusable answer. So, for those cases where * we can't check for intersection, this routine fixes the problem * by just splicing the offending vertex into the other edge. * This is a guaranteed solution, no matter how degenerate things get. * Basically this is a combinatorial solution to a numerical problem. */ { ActiveRegion regLo = RegionBelow(regUp); HalfEdge eUp = regUp.upperHalfEdge; HalfEdge eLo = regLo.upperHalfEdge; if (eUp.originVertex.VertLeq(eLo.originVertex)) { if (ContourVertex.EdgeSign(eLo.directionVertex, eUp.originVertex, eLo.originVertex) > 0) { return false; } /* eUp.Org appears to be below eLo */ if (!eUp.originVertex.VertEq(eLo.originVertex)) { /* Splice eUp.Org into eLo */ Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); Mesh.meshSplice(eUp, eLo.Oprev); regUp.dirty = regLo.dirty = true; } else if (eUp.originVertex != eLo.originVertex) { /* merge the two vertices, discarding eUp.Org */ tess.vertexPriorityQue.Delete(eUp.originVertex.priorityQueueHandle); //pqDelete(tess.pq, eUp.Org.pqHandle); /* __gl_pqSortDelete */ SpliceMergeVertices(tess, eLo.Oprev, eUp); } } else { if (ContourVertex.EdgeSign(eUp.directionVertex, eLo.originVertex, eUp.originVertex) < 0) { return false; } /* eLo.Org appears to be above eUp, so splice eLo.Org into eUp */ regUp.RegionAbove().dirty = regUp.dirty = true; Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); Mesh.meshSplice(eLo.Oprev, eUp); } return true; }
static ActiveRegion TopRightRegion(ActiveRegion reg) { ContourVertex dst = reg.upperHalfEdge.directionVertex; /* Find the region above the uppermost edge with the same destination */ do { reg = reg.RegionAbove(); } while (reg.upperHalfEdge.directionVertex == dst); return reg; }
static ActiveRegion TopLeftRegion(ActiveRegion reg) { ContourVertex org = reg.upperHalfEdge.originVertex; HalfEdge e; /* Find the region above the uppermost edge with the same origin */ do { reg = reg.RegionAbove(); } while (reg.upperHalfEdge.originVertex == org); /* If the edge above was a temporary edge introduced by ConnectRightVertex, * now is the time to fix it. */ if (reg.fixUpperEdge) { e = Mesh.meshConnect(RegionBelow(reg).upperHalfEdge.otherHalfOfThisEdge, reg.upperHalfEdge.nextEdgeCCWAroundLeftFace); if (e == null) { return null; } if (!FixUpperEdge(reg, e)) { return null; } reg = reg.RegionAbove(); } return reg; }
static void WalkDirtyRegions(Tesselator tess, ActiveRegion regUp) /* * When the upper or lower edge of any region changes, the region is * marked "dirty". This routine walks through all the dirty regions * and makes sure that the dictionary invariants are satisfied * (see the comments at the beginning of this file). Of course * new dirty regions can be created as we make changes to restore * the invariants. */ { ActiveRegion regLo = RegionBelow(regUp); HalfEdge eUp, eLo; for (;;) { /* Find the lowest dirty region (we walk from the bottom up). */ while (regLo.dirty) { regUp = regLo; regLo = RegionBelow(regLo); } if (!regUp.dirty) { regLo = regUp; regUp = regUp.RegionAbove(); if (regUp == null || !regUp.dirty) { /* We've walked all the dirty regions */ return; } } regUp.dirty = false; eUp = regUp.upperHalfEdge; eLo = regLo.upperHalfEdge; if (eUp.directionVertex != eLo.directionVertex) { /* Check that the edge ordering is obeyed at the Dst vertices. */ if (CheckForLeftSplice(tess, regUp)) { /* If the upper or lower edge was marked fixUpperEdge, then * we no longer need it (since these edges are needed only for * vertices which otherwise have no right-going edges). */ if (regLo.fixUpperEdge) { DeleteRegion(regLo); Mesh.DeleteHalfEdge(eLo); regLo = RegionBelow(regUp); eLo = regLo.upperHalfEdge; } else if (regUp.fixUpperEdge) { DeleteRegion(regUp); Mesh.DeleteHalfEdge(eUp); regUp = regLo.RegionAbove(); eUp = regUp.upperHalfEdge; } } } if (eUp.originVertex != eLo.originVertex) { if (eUp.directionVertex != eLo.directionVertex && !regUp.fixUpperEdge && !regLo.fixUpperEdge && (eUp.directionVertex == tess.currentSweepVertex || eLo.directionVertex == tess.currentSweepVertex)) { /* When all else fails in CheckForIntersect(), it uses tess.currentSweepVertex * as the intersection location. To make this possible, it requires * that tess.currentSweepVertex lie between the upper and lower edges, and also * that neither of these is marked fixUpperEdge (since in the worst * case it might splice one of these edges into tess.currentSweepVertex, and * violate the invariant that fixable edges are the only right-going * edge from their associated vertex). */ if (CheckForIntersect(tess, regUp)) { /* WalkDirtyRegions() was called recursively; we're done */ return; } } else { /* Even though we can't use CheckForIntersect(), the Org vertices * may violate the dictionary edge ordering. Check and correct this. */ CheckForRightSplice(tess, regUp); } } if (eUp.originVertex == eLo.originVertex && eUp.directionVertex == eLo.directionVertex) { /* A degenerate loop consisting of only two edges -- delete it. */ AddWinding(eLo, eUp); DeleteRegion(regUp); Mesh.DeleteHalfEdge(eUp); regUp = regLo.RegionAbove(); } } }
/* * Check the upper and lower edge of "regUp", to make sure that the * eUp.Org is above eLo, or eLo.Org is below eUp (depending on which * origin is leftmost). * * The main purpose is to splice right-going edges with the same * dest vertex and nearly identical slopes (ie. we can't distinguish * the slopes numerically). However the splicing can also help us * to recover from numerical errors. For example, suppose at one * point we checked eUp and eLo, and decided that eUp.Org is barely * above eLo. Then later, we split eLo into two edges (eg. from * a splice operation like this one). This can change the result of * our test so that now eUp.Org is incident to eLo, or barely below it. * We must correct this condition to maintain the dictionary invariants. * * One possibility is to check these edges for intersection again * (ie. CheckForIntersect). This is what we do if possible. However * CheckForIntersect requires that tess.currentSweepVertex lies between eUp and eLo, * so that it has something to fall back on when the intersection * calculation gives us an unusable answer. So, for those cases where * we can't check for intersection, this routine fixes the problem * by just splicing the offending vertex into the other edge. * This is a guaranteed solution, no matter how degenerate things get. * Basically this is a combinatorial solution to a numerical problem. */ static bool CheckForRightSplice(Tesselator tess, ActiveRegion regUp) { ActiveRegion regLo = RegionBelow(regUp); HalfEdge eUp = regUp.upperHalfEdge; HalfEdge eLo = regLo.upperHalfEdge; if (eUp.originVertex.VertLeq(eLo.originVertex)) { if (ContourVertex.EdgeSign(eLo.directionVertex, eUp.originVertex, eLo.originVertex) > 0) { return false; } /* eUp.Org appears to be below eLo */ if (!eUp.originVertex.VertEq(eLo.originVertex)) { /* Splice eUp.Org into eLo */ Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge); Mesh.meshSplice(eUp, eLo.Oprev); regUp.dirty = regLo.dirty = true; } else if (eUp.originVertex != eLo.originVertex) { /* merge the two vertices, discarding eUp.Org */ tess.vertexPriorityQue.Delete(eUp.originVertex.priorityQueueHandle); //pqDelete(tess.pq, eUp.Org.pqHandle); /* __gl_pqSortDelete */ SpliceMergeVertices(tess, eLo.Oprev, eUp); } } else { if (ContourVertex.EdgeSign(eUp.directionVertex, eLo.originVertex, eUp.originVertex) < 0) { return false; } /* eLo.Org appears to be above eUp, so splice eLo.Org into eUp */ regUp.RegionAbove().dirty = regUp.dirty = true; Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge); Mesh.meshSplice(eLo.Oprev, eUp); } return true; }
/* * Check the upper and lower edge of "regUp", to make sure that the * eUp.Dst is above eLo, or eLo.Dst is below eUp (depending on which * destination is rightmost). * * Theoretically, this should always be true. However, splitting an edge * into two pieces can change the results of previous tests. For example, * suppose at one point we checked eUp and eLo, and decided that eUp.Dst * is barely above eLo. Then later, we split eLo into two edges (eg. from * a splice operation like this one). This can change the result of * the test so that now eUp.Dst is incident to eLo, or barely below it. * We must correct this condition to maintain the dictionary invariants * (otherwise new edges might get inserted in the wrong place in the * dictionary, and bad stuff will happen). * * We fix the problem by just splicing the offending vertex into the * other edge. */ static bool CheckForLeftSplice(Tesselator tess, ActiveRegion regUp) { ActiveRegion regLo = RegionBelow(regUp); HalfEdge eUp = regUp.upperHalfEdge; HalfEdge eLo = regLo.upperHalfEdge; HalfEdge e; if(eUp.directionVertex.VertEq(eLo.directionVertex)) { throw new Exception(); } if (eUp.directionVertex.VertLeq(eLo.directionVertex)) { if (ContourVertex.EdgeSign(eUp.directionVertex, eLo.directionVertex, eUp.originVertex) < 0) { return false; } /* eLo.Dst is above eUp, so splice eLo.Dst into eUp */ regUp.RegionAbove().dirty = regUp.dirty = true; e = Mesh.meshSplitEdge(eUp); Mesh.meshSplice(eLo.otherHalfOfThisEdge, e); e.leftFace.isInterior = regUp.inside; } else { if (ContourVertex.EdgeSign(eLo.directionVertex, eUp.directionVertex, eLo.originVertex) > 0) return false; /* eUp.Dst is below eLo, so splice eUp.Dst into eLo */ regUp.dirty = regLo.dirty = true; e = Mesh.meshSplitEdge(eLo); Mesh.meshSplice(eUp.nextEdgeCCWAroundLeftFace, eLo.otherHalfOfThisEdge); e.rightFace.isInterior = regUp.inside; } return true; }