/// <summary> /// 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. /// </summary> private bool CheckForIntersect(ActiveRegion regUp) { ActiveRegion regLo = RegionBelow(regUp); MeshUtils.Edge eUp = regUp._eUp; MeshUtils.Edge eLo = regLo._eUp; MeshUtils.Vertex orgUp = eUp._Org; MeshUtils.Vertex orgLo = eLo._Org; MeshUtils.Vertex dstUp = eUp._Dst; MeshUtils.Vertex dstLo = eLo._Dst; Debug.Assert(!Geom.VertEq(dstLo, dstUp)); Debug.Assert(Geom.EdgeSign(dstUp, _event, orgUp) <= 0.0f); Debug.Assert(Geom.EdgeSign(dstLo, _event, orgLo) >= 0.0f); Debug.Assert(orgUp != _event && orgLo != _event); Debug.Assert(!regUp._fixUpperEdge && !regLo._fixUpperEdge); if (orgUp == orgLo) { // right endpoints are the same return(false); } double tMinUp = Math.Min(orgUp._t, dstUp._t); double tMaxLo = Math.Max(orgLo._t, dstLo._t); if (tMinUp > tMaxLo) { // t ranges do not overlap return(false); } if (Geom.VertLeq(orgUp, orgLo)) { if (Geom.EdgeSign(dstLo, orgUp, orgLo) > 0.0f) { return(false); } } else { if (Geom.EdgeSign(dstUp, orgLo, orgUp) < 0.0f) { return(false); } } // At this point the edges intersect, at least marginally MeshUtils.Vertex isect = _pool.Get <MeshUtils.Vertex>(); Geom.EdgeIntersect(dstUp, orgUp, dstLo, orgLo, isect); // The following properties are guaranteed: Debug.Assert(Math.Min(orgUp._t, dstUp._t) <= isect._t); Debug.Assert(isect._t <= Math.Max(orgLo._t, dstLo._t)); Debug.Assert(Math.Min(dstLo._s, dstUp._s) <= isect._s); Debug.Assert(isect._s <= Math.Max(orgLo._s, orgUp._s)); if (Geom.VertLeq(isect, _event)) { // 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._event. isect._s = _event._s; isect._t = _event._t; } // 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). MeshUtils.Vertex orgMin = Geom.VertLeq(orgUp, orgLo) ? orgUp : orgLo; if (Geom.VertLeq(orgMin, isect)) { isect._s = orgMin._s; isect._t = orgMin._t; } if (Geom.VertEq(isect, orgUp) || Geom.VertEq(isect, orgLo)) { // Easy case -- intersection at one of the right endpoints CheckForRightSplice(regUp); _pool.Return(isect); return(false); } if (!Geom.VertEq(dstUp, _event) && Geom.EdgeSign(dstUp, _event, isect) >= 0.0f || !Geom.VertEq(dstLo, _event) && Geom.EdgeSign(dstLo, _event, isect) <= 0.0f) { // Very unusual -- the new upper or lower edge would pass on the // wrong side of the sweep event, or through it. This can happen // due to very small numerical errors in the intersection calculation. if (dstLo == _event) { // Splice dstLo into eUp, and process the new region(s) _mesh.SplitEdge(_pool, eUp._Sym); Mesh.Splice(_pool, eLo._Sym, eUp); regUp = TopLeftRegion(regUp); eUp = RegionBelow(regUp)._eUp; FinishLeftRegions(RegionBelow(regUp), regLo); AddRightEdges(regUp, eUp._Oprev, eUp, eUp, true); _pool.Return(isect); return(true); } if (dstUp == _event) { /* Splice dstUp into eLo, and process the new region(s) */ _mesh.SplitEdge(_pool, eLo._Sym); Mesh.Splice(_pool, eUp._Lnext, eLo._Oprev); regLo = regUp; regUp = TopRightRegion(regUp); MeshUtils.Edge e = RegionBelow(regUp)._eUp._Rprev; regLo._eUp = eLo._Oprev; eLo = FinishLeftRegions(regLo, null); AddRightEdges(regUp, eLo._Onext, eUp._Rprev, e, true); _pool.Return(isect); return(true); } // Special case: called from ConnectRightVertex. If either // edge passes on the wrong side of tess._event, split it // (and wait for ConnectRightVertex to splice it appropriately). if (Geom.EdgeSign(dstUp, _event, isect) >= 0.0f) { RegionAbove(regUp)._dirty = regUp._dirty = true; _mesh.SplitEdge(_pool, eUp._Sym); eUp._Org._s = _event._s; eUp._Org._t = _event._t; } if (Geom.EdgeSign(dstLo, _event, isect) <= 0.0f) { regUp._dirty = regLo._dirty = true; _mesh.SplitEdge(_pool, eLo._Sym); eLo._Org._s = _event._s; eLo._Org._t = _event._t; } // leave the rest for ConnectRightVertex _pool.Return(isect); 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.SplitEdge(_pool, eUp._Sym); _mesh.SplitEdge(_pool, eLo._Sym); Mesh.Splice(_pool, eLo._Oprev, eUp); eUp._Org._s = isect._s; eUp._Org._t = isect._t; _pool.Return(isect); isect = null; eUp._Org._pqHandle = _pq.Insert(eUp._Org); if (eUp._Org._pqHandle._handle == PQHandle.Invalid) { throw new InvalidOperationException("PQHandle should not be invalid"); } GetIntersectData(eUp._Org, orgUp, dstUp, orgLo, dstLo); RegionAbove(regUp)._dirty = regUp._dirty = regLo._dirty = true; return(false); }