private void FixUpperEdge(ActiveRegion reg, MeshUtils.Edge newEdge) { _mesh.Delete(reg._eUp); reg._fixUpperEdge = false; reg._eUp = newEdge; newEdge._activeRegion = reg; }
private void DeleteRegion(ActiveRegion reg) { bool fixUpperEdge = reg._fixUpperEdge; reg._eUp._activeRegion = null; _dict.Remove(reg._nodeUp); }
private void SweepEvent(MeshUtils.Vertex vEvent) { _event = vEvent; MeshUtils.Edge edge = vEvent._anEdge; while (edge._activeRegion == null) { edge = edge._Onext; if (edge == vEvent._anEdge) { ConnectLeftVertex(vEvent); return; } } ActiveRegion activeRegion = TopLeftRegion(edge._activeRegion); ActiveRegion activeRegion2 = RegionBelow(activeRegion); MeshUtils.Edge eUp = activeRegion2._eUp; MeshUtils.Edge edge2 = FinishLeftRegions(activeRegion2, null); if (edge2._Onext == eUp) { ConnectRightVertex(activeRegion, edge2); } else { ActiveRegion regUp = activeRegion; MeshUtils.Edge onext = edge2._Onext; MeshUtils.Edge edge3 = eUp; AddRightEdges(regUp, onext, edge3, edge3, true); } }
/// <summary> /// 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. /// </summary> private void WalkDirtyRegions(ActiveRegion regUp) { ActiveRegion regLo = RegionBelow(regUp); while (true) { // Find the lowest dirty region (we walk from the bottom up). while (regLo._dirty) { regUp = regLo; regLo = RegionBelow(regLo); } switch (regUp._dirty) { case false: { regLo = regUp; regUp = RegionAbove(regUp); if (regUp is not { _dirty : true }) { // We've walked all the dirty regions return; } break; } }
private ActiveRegion TopLeftRegion(ActiveRegion reg) { MeshUtils.Vertex org = reg._eUp._Org; // Find the region above the uppermost edge with the same origin do { reg = RegionAbove(reg); } while (reg._eUp._Org == org); switch (reg._fixUpperEdge) { // If the edge above was a temporary edge introduced by ConnectRightVertex, // now is the time to fix it. case true: { MeshUtils.Edge e = Mesh.Connect(_pool, RegionBelow(reg)._eUp._Sym, reg._eUp._Lnext); FixUpperEdge(reg, e); reg = RegionAbove(reg); break; } } return(reg); }
/// <summary> /// Both edges must be directed from right to left (this is the canonical /// direction for the upper edge of each region). /// /// The strategy is to evaluate a "t" value for each edge at the /// current sweep line position, given by tess->event. The calculations /// are designed to be very stable, but of course they are not perfect. /// /// Special case: if both edge destinations are at the sweep event, /// we sort the edges by slope (they would otherwise compare equally). /// </summary> private bool EdgeLeq(ActiveRegion reg1, ActiveRegion reg2) { MeshUtils.Edge e1 = reg1._eUp; MeshUtils.Edge e2 = reg2._eUp; if (e1._Dst == _event) { if (e2._Dst != _event) { return(Geom.EdgeSign(e2._Dst, _event, e2._Org) <= 0.0f); } // Two edges right of the sweep line which meet at the sweep event. // Sort them by slope. if (Geom.VertLeq(e1._Org, e2._Org)) { return(Geom.EdgeSign(e2._Dst, e1._Org, e2._Org) <= 0.0f); } return(Geom.EdgeSign(e1._Dst, e2._Org, e1._Org) >= 0.0f); } if (e2._Dst == _event) { return(Geom.EdgeSign(e1._Dst, _event, e1._Org) >= 0.0f); } // General case - compute signed distance *from* e1, e2 to event double t1 = Geom.EdgeEval(e1._Dst, _event, e1._Org); double t2 = Geom.EdgeEval(e2._Dst, _event, e2._Org); return(t1 >= t2); }
/// <summary> /// Both edges must be directed from right to left (this is the canonical /// direction for the upper edge of each region). /// /// The strategy is to evaluate a "t" value for each edge at the /// current sweep line position, given by tess->event. The calculations /// are designed to be very stable, but of course they are not perfect. /// /// Special case: if both edge destinations are at the sweep event, /// we sort the edges by slope (they would otherwise compare equally). /// </summary> private bool EdgeLeq(ActiveRegion reg1, ActiveRegion reg2) { var e1 = reg1._eUp; var e2 = reg2._eUp; if (e1._Dst == _event) { if (e2._Dst == _event) { // Two edges right of the sweep line which meet at the sweep event. // Sort them by slope. if (Geom.VertLeq(e1._Org, e2._Org)) { return Geom.EdgeSign(e2._Dst, e1._Org, e2._Org) <= 0.0f; } return Geom.EdgeSign(e1._Dst, e2._Org, e1._Org) >= 0.0f; } return Geom.EdgeSign(e2._Dst, _event, e2._Org) <= 0.0f; } if (e2._Dst == _event) { return Geom.EdgeSign(e1._Dst, _event, e1._Org) >= 0.0f; } // General case - compute signed distance *from* e1, e2 to event var t1 = Geom.EdgeEval(e1._Dst, _event, e1._Org); var t2 = Geom.EdgeEval(e2._Dst, _event, e2._Org); return (t1 >= t2); }
private bool CheckForLeftSplice(ActiveRegion regUp) { ActiveRegion activeRegion = RegionBelow(regUp); MeshUtils.Edge eUp = regUp._eUp; MeshUtils.Edge eUp2 = activeRegion._eUp; if (Geom.VertLeq(eUp._Dst, eUp2._Dst)) { if (Geom.EdgeSign(eUp._Dst, eUp2._Dst, eUp._Org) < 0f) { return(false); } RegionAbove(regUp)._dirty = (regUp._dirty = true); MeshUtils.Edge edge = _mesh.SplitEdge(eUp); _mesh.Splice(eUp2._Sym, edge); edge._Lface._inside = regUp._inside; } else { if (Geom.EdgeSign(eUp2._Dst, eUp._Dst, eUp2._Org) > 0f) { return(false); } regUp._dirty = (activeRegion._dirty = true); MeshUtils.Edge edge2 = _mesh.SplitEdge(eUp2); _mesh.Splice(eUp._Lnext, eUp2._Sym); edge2._Rface._inside = regUp._inside; } return(true); }
private MeshUtils.Edge FinishLeftRegions(ActiveRegion regFirst, ActiveRegion regLast) { ActiveRegion activeRegion = regFirst; MeshUtils.Edge eUp = regFirst._eUp; while (activeRegion != regLast) { activeRegion._fixUpperEdge = false; ActiveRegion activeRegion2 = RegionBelow(activeRegion); MeshUtils.Edge edge = activeRegion2._eUp; if (edge._Org != eUp._Org) { if (!activeRegion2._fixUpperEdge) { FinishRegion(activeRegion); break; } edge = _mesh.Connect(eUp._Lprev, edge._Sym); FixUpperEdge(activeRegion2, edge); } if (eUp._Onext != edge) { _mesh.Splice(edge._Oprev, edge); _mesh.Splice(eUp, edge); } FinishRegion(activeRegion); eUp = activeRegion2._eUp; activeRegion = activeRegion2; } return(eUp); }
private bool EdgeLeq(ActiveRegion reg1, ActiveRegion reg2) { MeshUtils.Edge eUp = reg1._eUp; MeshUtils.Edge eUp2 = reg2._eUp; if (eUp._Dst == _event) { if (eUp2._Dst == _event) { if (Geom.VertLeq(eUp._Org, eUp2._Org)) { return(Geom.EdgeSign(eUp2._Dst, eUp._Org, eUp2._Org) <= 0f); } return(Geom.EdgeSign(eUp._Dst, eUp2._Org, eUp._Org) >= 0f); } return(Geom.EdgeSign(eUp2._Dst, _event, eUp2._Org) <= 0f); } if (eUp2._Dst == _event) { return(Geom.EdgeSign(eUp._Dst, _event, eUp._Org) >= 0f); } float num = Geom.EdgeEval(eUp._Dst, _event, eUp._Org); float num2 = Geom.EdgeEval(eUp2._Dst, _event, eUp2._Org); return(num >= num2); }
public Form1() { InitializeComponent(); for (int x = 0; x < world.TotalWorldWidthCells; x++) { Rock cell = new Rock(); cell.Color = RandomColorOffset(cell.Color, rng, colorVariance, colorVariance, colorVariance); world.GetCellAtPosition(x, 0).BaseSand = cell; cell = new Rock(); cell.Color = RandomColorOffset(cell.Color, rng, colorVariance, colorVariance, colorVariance); world.GetCellAtPosition(x, world.TotalWorldHeightCells - 1).BaseSand = cell; for (int xx = 0; xx < world.TotalWorldWidthCells - 2; xx++) { for (int y = 1; y < 5; y++) { BaseSand sCell = world.RNG.Next(0, 2) == 0 ? (BaseSand) new Sand() : new BlackSand(); sCell.Color = RandomColorOffset(sCell.Color, rng, colorVariance, colorVariance, colorVariance); world.GetCellAtPosition(xx, y).BaseSand = sCell; } } } for (int y = 0; y < world.TotalWorldHeightCells; y++) { Rock cell = new Rock(); cell.Color = RandomColorOffset(cell.Color, rng, colorVariance, colorVariance, colorVariance); world.GetCellAtPosition(0, y).BaseSand = cell; cell = new Rock(); cell.Color = RandomColorOffset(cell.Color, rng, colorVariance, colorVariance, colorVariance); world.GetCellAtPosition(world.TotalWorldWidthCells - 1, y).BaseSand = cell; } ImageBuffer = new Bitmap((int)(world.TotalWorldWidthCells * xResolution), (int)(world.TotalWorldHeightCells * yResolution)); backBufferGraphics = Graphics.FromImage(ImageBuffer); graphics = drawArea.CreateGraphics(); drawArea.MouseClick += DrawArea_MouseClick; for (int i = 0; i < SandTypes.Count; i++) { BaseSand type = SandTypes[i]; Button b = new Button(); b.Name = "bs:" + i; b.Text = type.Name; buttonLayout.Controls.Add(b); b.Click += B_Click; } ActiveRegion ar = new ActiveRegion(world, 0, 0, 8, 3); world.ActiveRegions.Add(ar); drawThread = new Thread(Draw); drawThread.Start(); }
/// <summary> /// Replace an upper edge which needs fixing (see ConnectRightVertex). /// </summary> private void FixUpperEdge(ActiveRegion reg, MeshUtils.Edge newEdge) { Debug.Assert(reg._fixUpperEdge); Mesh.Delete(_pool, reg._eUp); reg._fixUpperEdge = false; reg._eUp = newEdge; newEdge._activeRegion = reg; }
private void FinishRegion(ActiveRegion reg) { MeshUtils.Edge eUp = reg._eUp; MeshUtils.Face lface = eUp._Lface; lface._inside = reg._inside; lface._anEdge = eUp; DeleteRegion(reg); }
/// <summary> /// Delete a region from the sweep line. This happens when the upper /// and lower chains of a region meet (at a vertex on the sweep line). /// The "inside" flag is copied to the appropriate mesh face (we could /// not do this before -- since the structure of the mesh is always /// changing, this face may not have even existed until now). /// </summary> private void FinishRegion(ActiveRegion reg) { MeshUtils.Edge e = reg._eUp; MeshUtils.Face f = e._Lface; f._inside = reg._inside; f._anEdge = e; DeleteRegion(reg); }
private ActiveRegion TopRightRegion(ActiveRegion reg) { MeshUtils.Vertex dst = reg._eUp._Dst; do { reg = RegionAbove(reg); }while (reg._eUp._Dst == dst); return(reg); }
public static Node dictSearch(Dictionary dict, DictKey key) { Node node = dict._head; do { node = node.next; } while (node.Key != null && !ActiveRegion.EdgeLeq(dict._tess.currentSweepVertex, key, node.Key)); return(node); }
public static Node dictSearch(Dictionary dict, DictKey key) { Node node = dict.head; do { node = node.next; } while (node.Key != null && !ActiveRegion.EdgeLeq(dict.tesseator, key, node.Key)); return(node); }
private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp) { ActiveRegion activeRegion = new ActiveRegion(); activeRegion._eUp = eNewUp; activeRegion._nodeUp = _dict.InsertBefore(regAbove._nodeUp, activeRegion); activeRegion._fixUpperEdge = false; activeRegion._sentinel = false; activeRegion._dirty = false; eNewUp._activeRegion = activeRegion; return(activeRegion); }
private ActiveRegion TopRightRegion(ActiveRegion reg) { MeshUtils.Vertex dst = reg._eUp._Dst; // Find the region above the uppermost edge with the same destination do { reg = RegionAbove(reg); } while (reg._eUp._Dst == dst); return(reg); }
/// <summary> /// Add a new active region to the sweep line, *somewhere* below "regAbove" /// (according to where the new edge belongs in the sweep-line dictionary). /// The upper edge of the new region will be "eNewUp". /// Winding number and "inside" flag are not updated. /// </summary> private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp) { ActiveRegion regNew = _pool.Get <ActiveRegion>(); regNew._eUp = eNewUp; regNew._nodeUp = _dict.InsertBefore(regAbove._nodeUp, regNew); regNew._fixUpperEdge = false; regNew._sentinel = false; regNew._dirty = false; eNewUp._activeRegion = regNew; return(regNew); }
private void AddRightEdges(ActiveRegion regUp, MeshUtils.Edge eFirst, MeshUtils.Edge eLast, MeshUtils.Edge eTopLeft, bool cleanUp) { bool flag = true; MeshUtils.Edge edge = eFirst; do { AddRegionBelow(regUp, edge._Sym); edge = edge._Onext; }while (edge != eLast); if (eTopLeft == null) { eTopLeft = RegionBelow(regUp)._eUp._Rprev; } ActiveRegion activeRegion = regUp; MeshUtils.Edge edge2 = eTopLeft; while (true) { ActiveRegion activeRegion2 = RegionBelow(activeRegion); edge = activeRegion2._eUp._Sym; if (edge._Org != edge2._Org) { break; } if (edge._Onext != edge2) { _mesh.Splice(edge._Oprev, edge); _mesh.Splice(edge2._Oprev, edge); } activeRegion2._windingNumber = activeRegion._windingNumber - edge._winding; activeRegion2._inside = Geom.IsWindingInside(_windingRule, activeRegion2._windingNumber); activeRegion._dirty = true; if (!flag && CheckForRightSplice(activeRegion)) { Geom.AddWinding(edge, edge2); DeleteRegion(activeRegion); _mesh.Delete(edge2); } flag = false; activeRegion = activeRegion2; edge2 = edge; } activeRegion._dirty = true; if (cleanUp) { WalkDirtyRegions(activeRegion); } }
private ActiveRegion TopLeftRegion(ActiveRegion reg) { MeshUtils.Vertex org = reg._eUp._Org; do { reg = RegionAbove(reg); }while (reg._eUp._Org == org); if (reg._fixUpperEdge) { MeshUtils.Edge newEdge = _mesh.Connect(RegionBelow(reg)._eUp._Sym, reg._eUp._Lnext); FixUpperEdge(reg, newEdge); reg = RegionAbove(reg); } return(reg); }
private void DeleteRegion(ActiveRegion reg) { switch (reg._fixUpperEdge) { case true: // It was created with zero winding number, so it better be // deleted with zero winding number (ie. it better not get merged // with a real edge). Debug.Assert(reg._eUp._winding == 0); break; } reg._eUp._activeRegion = null; _dict.Remove(reg._nodeUp); _pool.Return(reg); }
public Node InsertBefore(Node node, DictKey key) { do { node = node.prev; } while (node.Key != null && !ActiveRegion.EdgeLeq(_tess.currentSweepVertex, node.Key, key)); var newNode = new Node(); newNode.Key = key; newNode.next = node.next; node.next.prev = newNode; newNode.prev = node; node.next = newNode; return(newNode); }
public Node InsertBefore(Node node, DictKey key) { Node newNode; do { node = node.prev; } while (node.Key != null && !ActiveRegion.EdgeLeq(this.tesseator, node.Key, key)); newNode = new Node(); newNode.Key = key; newNode.next = node.next; node.next.prev = newNode; newNode.prev = node; node.next = newNode; return(newNode); }
private void ConnectRightVertex(ActiveRegion regUp, MeshUtils.Edge eBottomLeft) { MeshUtils.Edge edge = eBottomLeft._Onext; ActiveRegion activeRegion = RegionBelow(regUp); MeshUtils.Edge eUp = regUp._eUp; MeshUtils.Edge eUp2 = activeRegion._eUp; bool flag = false; if (eUp._Dst != eUp2._Dst) { CheckForIntersect(regUp); } if (Geom.VertEq(eUp._Org, _event)) { _mesh.Splice(edge._Oprev, eUp); regUp = TopLeftRegion(regUp); edge = RegionBelow(regUp)._eUp; FinishLeftRegions(RegionBelow(regUp), activeRegion); flag = true; } if (Geom.VertEq(eUp2._Org, _event)) { _mesh.Splice(eBottomLeft, eUp2._Oprev); eBottomLeft = FinishLeftRegions(activeRegion, null); flag = true; } if (flag) { ActiveRegion regUp2 = regUp; MeshUtils.Edge onext = eBottomLeft._Onext; MeshUtils.Edge edge2 = edge; AddRightEdges(regUp2, onext, edge2, edge2, true); } else { MeshUtils.Edge eDst = (!Geom.VertLeq(eUp2._Org, eUp._Org)) ? eUp : eUp2._Oprev; eDst = _mesh.Connect(eBottomLeft._Lprev, eDst); ActiveRegion regUp3 = regUp; MeshUtils.Edge edge3 = eDst; AddRightEdges(regUp3, edge3, edge3._Onext, eDst._Onext, false); eDst._Sym._activeRegion._fixUpperEdge = true; WalkDirtyRegions(regUp); } }
private void AddSentinel(float smin, float smax, float t) { MeshUtils.Edge edge = _mesh.MakeEdge(); edge._Org._s = smax; edge._Org._t = t; edge._Dst._s = smin; edge._Dst._t = t; _event = edge._Dst; ActiveRegion activeRegion = new ActiveRegion(); activeRegion._eUp = edge; activeRegion._windingNumber = 0; activeRegion._inside = false; activeRegion._fixUpperEdge = false; activeRegion._sentinel = true; activeRegion._dirty = false; activeRegion._nodeUp = _dict.Insert(activeRegion); }
private void Awake() { Instance = this; grid = GetComponent <Grid>(); for (int i = radius; i > -radius; i--) { for (int j = radius; j > -radius; j--) { if (Math.Abs(i) < startRadius && Math.Abs(j) < startRadius) { activeCells.Add(new Vector3Int(i, j, 0)); continue; } Vector3Int cell = new Vector3Int(i, j, 0); tilemap.SetTile(cell, shadeTile); } } }
/// <summary> /// 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->event 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. /// </summary> private bool CheckForRightSplice(ActiveRegion regUp) { ActiveRegion regLo = RegionBelow(regUp); MeshUtils.Edge eUp = regUp._eUp; MeshUtils.Edge eLo = regLo._eUp; if (Geom.VertLeq(eUp._Org, eLo._Org)) { if (Geom.EdgeSign(eLo._Dst, eUp._Org, eLo._Org) > 0.0f) { return(false); } // eUp.Org appears to be below eLo if (!Geom.VertEq(eUp._Org, eLo._Org)) { // Splice eUp._Org into eLo _mesh.SplitEdge(_pool, eLo._Sym); Mesh.Splice(_pool, eUp, eLo._Oprev); regUp._dirty = regLo._dirty = true; } else if (eUp._Org != eLo._Org) { // merge the two vertices, discarding eUp.Org _pq.Remove(eUp._Org._pqHandle); SpliceMergeVertices(eLo._Oprev, eUp); } } else { if (Geom.EdgeSign(eUp._Dst, eLo._Org, eUp._Org) < 0.0f) { return(false); } // eLo.Org appears to be above eUp, so splice eLo.Org into eUp RegionAbove(regUp)._dirty = regUp._dirty = true; _mesh.SplitEdge(_pool, eUp._Sym); Mesh.Splice(_pool, eLo._Oprev, eUp); } return(true); }
/// <summary> /// We are given a vertex with one or more left-going edges. All affected /// edges should be in the edge dictionary. Starting at regFirst->eUp, /// we walk down deleting all regions where both edges have the same /// origin vOrg. At the same time we copy the "inside" flag from the /// active region to the face, since at this point each face will belong /// to at most one region (this was not necessarily true until this point /// in the sweep). The walk stops at the region above regLast; if regLast /// is null we walk as far as possible. At the same time we relink the /// mesh if necessary, so that the ordering of edges around vOrg is the /// same as in the dictionary. /// </summary> private MeshUtils.Edge FinishLeftRegions(ActiveRegion regFirst, ActiveRegion regLast) { ActiveRegion regPrev = regFirst; MeshUtils.Edge ePrev = regFirst._eUp; while (regPrev != regLast) { regPrev._fixUpperEdge = false; // placement was OK ActiveRegion reg = RegionBelow(regPrev); MeshUtils.Edge e = reg._eUp; if (e._Org != ePrev._Org) { if (!reg._fixUpperEdge) { // Remove the last left-going edge. Even though there are no further // edges in the dictionary with this origin, there may be further // such edges in the mesh (if we are adding left edges to a vertex // that has already been processed). Thus it is important to call // FinishRegion rather than just DeleteRegion. FinishRegion(regPrev); break; } // If the edge below was a temporary edge introduced by // ConnectRightVertex, now is the time to fix it. e = Mesh.Connect(_pool, ePrev._Lprev, e._Sym); FixUpperEdge(reg, e); } // Relink edges so that ePrev.Onext == e if (ePrev._Onext != e) { Mesh.Splice(_pool, e._Oprev, e); Mesh.Splice(_pool, ePrev, e); } FinishRegion(regPrev); // may change reg.eUp ePrev = reg._eUp; regPrev = reg; } return(ePrev); }
private void ConnectLeftDegenerate(ActiveRegion regUp, MeshUtils.Vertex vEvent) { MeshUtils.Edge eUp = regUp._eUp; if (Geom.VertEq(eUp._Org, vEvent)) { throw new InvalidOperationException("Vertices should have been merged before"); } if (!Geom.VertEq(eUp._Dst, vEvent)) { _mesh.SplitEdge(eUp._Sym); if (regUp._fixUpperEdge) { _mesh.Delete(eUp._Onext); regUp._fixUpperEdge = false; } _mesh.Splice(vEvent._anEdge, eUp); SweepEvent(vEvent); return; } throw new InvalidOperationException("Vertices should have been merged before"); }
/// <summary> /// The event vertex lies exacty on an already-processed edge or vertex. /// Adding the new vertex involves splicing it into the already-processed /// part of the mesh. /// </summary> private void ConnectLeftDegenerate(ActiveRegion regUp, MeshUtils.Vertex vEvent) { var e = regUp._eUp; if (Geom.VertEq(e._Org, vEvent)) { // e.Org is an unprocessed vertex - just combine them, and wait // for e.Org to be pulled from the queue // C# : in the C version, there is a flag but it was never implemented // the vertices are before beginning the tesselation throw new InvalidOperationException("Vertices should have been merged before"); } if (!Geom.VertEq(e._Dst, vEvent)) { // General case -- splice vEvent into edge e which passes through it _mesh.SplitEdge(e._Sym); if (regUp._fixUpperEdge) { // This edge was fixable -- delete unused portion of original edge _mesh.Delete(e._Onext); regUp._fixUpperEdge = false; } _mesh.Splice(vEvent._anEdge, e); SweepEvent(vEvent); // recurse return; } // See above throw new InvalidOperationException("Vertices should have been merged before"); }
private void ComputeWinding(ActiveRegion reg) { reg._windingNumber = RegionAbove(reg)._windingNumber + reg._eUp._winding; reg._inside = Geom.IsWindingInside(_windingRule, reg._windingNumber); }
/// <summary> /// 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->event 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. /// </summary> private bool CheckForRightSplice(ActiveRegion regUp) { var regLo = RegionBelow(regUp); var eUp = regUp._eUp; var eLo = regLo._eUp; if (Geom.VertLeq(eUp._Org, eLo._Org)) { if (Geom.EdgeSign(eLo._Dst, eUp._Org, eLo._Org) > 0.0f) { return false; } // eUp.Org appears to be below eLo if (!Geom.VertEq(eUp._Org, eLo._Org)) { // Splice eUp._Org into eLo _mesh.SplitEdge(eLo._Sym); _mesh.Splice(eUp, eLo._Oprev); regUp._dirty = regLo._dirty = true; } else if (eUp._Org != eLo._Org) { // merge the two vertices, discarding eUp.Org _pq.Remove(eUp._Org._pqHandle); SpliceMergeVertices(eLo._Oprev, eUp); } } else { if (Geom.EdgeSign(eUp._Dst, eLo._Org, eUp._Org) < 0.0f) { return false; } // eLo.Org appears to be above eUp, so splice eLo.Org into eUp RegionAbove(regUp)._dirty = regUp._dirty = true; _mesh.SplitEdge(eUp._Sym); _mesh.Splice(eLo._Oprev, eUp); } return true; }
private ActiveRegion RegionBelow(ActiveRegion reg) { return reg._nodeUp.Prev.Key; }
private ActiveRegion RegionAbove(ActiveRegion reg) { return reg._nodeUp._next._key; }
/// <summary> /// Delete a region from the sweep line. This happens when the upper /// and lower chains of a region meet (at a vertex on the sweep line). /// The "inside" flag is copied to the appropriate mesh face (we could /// not do this before -- since the structure of the mesh is always /// changing, this face may not have even existed until now). /// </summary> private void FinishRegion(ActiveRegion reg) { var e = reg._eUp; var f = e._Lface; f._inside = reg._inside; f._anEdge = e; DeleteRegion(reg); }
private void DeleteRegion(ActiveRegion reg) { if (reg._fixUpperEdge) { // It was created with zero winding number, so it better be // deleted with zero winding number (ie. it better not get merged // with a real edge). Debug.Assert(reg._eUp._winding == 0); } reg._eUp._activeRegion = null; _dict.Remove(reg._nodeUp); }
/// <summary> /// 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. /// </summary> private void WalkDirtyRegions(ActiveRegion regUp) { var regLo = RegionBelow(regUp); MeshUtils.Edge eUp, eLo; while (true) { // 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 = RegionAbove( regUp ); if(regUp == null || !regUp._dirty) { // We've walked all the dirty regions return; } } regUp._dirty = false; eUp = regUp._eUp; eLo = regLo._eUp; if (eUp._Dst != eLo._Dst) { // Check that the edge ordering is obeyed at the Dst vertices. if (CheckForLeftSplice(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.Delete(eLo); regLo = RegionBelow(regUp); eLo = regLo._eUp; } else if( regUp._fixUpperEdge ) { DeleteRegion(regUp); _mesh.Delete(eUp); regUp = RegionAbove(regLo); eUp = regUp._eUp; } } } if (eUp._Org != eLo._Org) { if( eUp._Dst != eLo._Dst && ! regUp._fixUpperEdge && ! regLo._fixUpperEdge && (eUp._Dst == _event || eLo._Dst == _event) ) { // When all else fails in CheckForIntersect(), it uses tess._event // as the intersection location. To make this possible, it requires // that tess._event 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.event, and // violate the invariant that fixable edges are the only right-going // edge from their associated vertex). if (CheckForIntersect(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(regUp); } } if (eUp._Org == eLo._Org && eUp._Dst == eLo._Dst) { // A degenerate loop consisting of only two edges -- delete it. Geom.AddWinding(eLo, eUp); DeleteRegion(regUp); _mesh.Delete(eUp); regUp = RegionAbove(regLo); } } }
private ActiveRegion TopRightRegion(ActiveRegion reg) { var dst = reg._eUp._Dst; // Find the region above the uppermost edge with the same destination do { reg = RegionAbove(reg); } while (reg._eUp._Dst == dst); return reg; }
private ActiveRegion TopLeftRegion(ActiveRegion reg) { var org = reg._eUp._Org; // Find the region above the uppermost edge with the same origin do { reg = RegionAbove(reg); } while (reg._eUp._Org == org); // If the edge above was a temporary edge introduced by ConnectRightVertex, // now is the time to fix it. if (reg._fixUpperEdge) { var e = _mesh.Connect(RegionBelow(reg)._eUp._Sym, reg._eUp._Lnext); FixUpperEdge(reg, e); reg = RegionAbove(reg); } return reg; }
/// <summary> /// Add a new active region to the sweep line, *somewhere* below "regAbove" /// (according to where the new edge belongs in the sweep-line dictionary). /// The upper edge of the new region will be "eNewUp". /// Winding number and "inside" flag are not updated. /// </summary> private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp) { var regNew = new ActiveRegion(); regNew._eUp = eNewUp; regNew._nodeUp = _dict.InsertBefore(regAbove._nodeUp, regNew); regNew._fixUpperEdge = false; regNew._sentinel = false; regNew._dirty = false; eNewUp._activeRegion = regNew; return regNew; }
/// <summary> /// Add a new active region to the sweep line, *somewhere* below "regAbove" /// (according to where the new edge belongs in the sweep-line dictionary). /// The upper edge of the new region will be "eNewUp". /// Winding number and "inside" flag are not updated. /// </summary> private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp) { var regNew = new ActiveRegion(); regNew._eUp = eNewUp; regNew._nodeUp = _dict.InsertBefore(regAbove._nodeUp, regNew); eNewUp._activeRegion = regNew; return regNew; }
/// <summary> /// Make the sentinel coordinates big enough that they will never be /// merged with real input features. /// /// We add two sentinel edges above and below all other edges, /// to avoid special cases at the top and bottom. /// </summary> private void AddSentinel(float smin, float smax, float t) { var e = _mesh.MakeEdge(); e._Org._s = smax; e._Org._t = t; e._Dst._s = smin; e._Dst._t = t; _event = e._Dst; // initialize it var reg = new ActiveRegion(); reg._eUp = e; reg._sentinel = true; reg._nodeUp = _dict.Insert(reg); }
/// <summary> /// Purpose: connect a "left" vertex (one where both edges go right) /// to the processed portion of the mesh. Let R be the active region /// containing vEvent, and let U and L be the upper and lower edge /// chains of R. There are two possibilities: /// /// - the normal case: split R into two regions, by connecting vEvent to /// the rightmost vertex of U or L lying to the left of the sweep line /// /// - the degenerate case: if vEvent is close enough to U or L, we /// merge vEvent into that edge chain. The subcases are: /// - merging with the rightmost vertex of U or L /// - merging with the active edge of U or L /// - merging with an already-processed portion of U or L /// </summary> private void ConnectLeftVertex(MeshUtils.Vertex vEvent) { var tmp = new ActiveRegion(); // Get a pointer to the active region containing vEvent tmp._eUp = vEvent._anEdge._Sym; var regUp = _dict.Find(tmp).Key; var regLo = RegionBelow(regUp); if (regLo == null) { // This may happen if the input polygon is coplanar. return; } var eUp = regUp._eUp; var eLo = regLo._eUp; // Try merging with U or L first if (Geom.EdgeSign(eUp._Dst, vEvent, eUp._Org) == 0.0f) { ConnectLeftDegenerate(regUp, vEvent); return; } // Connect vEvent to rightmost processed vertex of either chain. // e._Dst is the vertex that we will connect to vEvent. var reg = Geom.VertLeq(eLo._Dst, eUp._Dst) ? regUp : regLo; if (regUp._inside || reg._fixUpperEdge) { MeshUtils.Edge eNew; if (reg == regUp) { eNew = _mesh.Connect(vEvent._anEdge._Sym, eUp._Lnext); } else { eNew = _mesh.Connect(eLo._Dnext, vEvent._anEdge)._Sym; } if (reg._fixUpperEdge) { FixUpperEdge(reg, eNew); } else { ComputeWinding(AddRegionBelow(regUp, eNew)); } SweepEvent(vEvent); } else { // The new vertex is in a region which does not belong to the polygon. // We don't need to connect this vertex to the rest of the mesh. AddRightEdges(regUp, vEvent._anEdge, vEvent._anEdge, null, true); } }
/// <summary> /// Purpose: connect a "right" vertex vEvent (one where all edges go left) /// to the unprocessed portion of the mesh. Since there are no right-going /// edges, two regions (one above vEvent and one below) are being merged /// into one. "regUp" is the upper of these two regions. /// /// There are two reasons for doing this (adding a right-going edge): /// - if the two regions being merged are "inside", we must add an edge /// to keep them separated (the combined region would not be monotone). /// - in any case, we must leave some record of vEvent in the dictionary, /// so that we can merge vEvent with features that we have not seen yet. /// For example, maybe there is a vertical edge which passes just to /// the right of vEvent; we would like to splice vEvent into this edge. /// /// However, we don't want to connect vEvent to just any vertex. We don''t /// want the new edge to cross any other edges; otherwise we will create /// intersection vertices even when the input data had no self-intersections. /// (This is a bad thing; if the user's input data has no intersections, /// we don't want to generate any false intersections ourselves.) /// /// Our eventual goal is to connect vEvent to the leftmost unprocessed /// vertex of the combined region (the union of regUp and regLo). /// But because of unseen vertices with all right-going edges, and also /// new vertices which may be created by edge intersections, we don''t /// know where that leftmost unprocessed vertex is. In the meantime, we /// connect vEvent to the closest vertex of either chain, and mark the region /// as "fixUpperEdge". This flag says to delete and reconnect this edge /// to the next processed vertex on the boundary of the combined region. /// Quite possibly the vertex we connected to will turn out to be the /// closest one, in which case we won''t need to make any changes. /// </summary> private void ConnectRightVertex(ActiveRegion regUp, MeshUtils.Edge eBottomLeft) { var eTopLeft = eBottomLeft._Onext; var regLo = RegionBelow(regUp); var eUp = regUp._eUp; var eLo = regLo._eUp; bool degenerate = false; if (eUp._Dst != eLo._Dst) { CheckForIntersect(regUp); } // Possible new degeneracies: upper or lower edge of regUp may pass // through vEvent, or may coincide with new intersection vertex if (Geom.VertEq(eUp._Org, _event)) { _mesh.Splice(eTopLeft._Oprev, eUp); regUp = TopLeftRegion(regUp); eTopLeft = RegionBelow(regUp)._eUp; FinishLeftRegions(RegionBelow(regUp), regLo); degenerate = true; } if (Geom.VertEq(eLo._Org, _event)) { _mesh.Splice(eBottomLeft, eLo._Oprev); eBottomLeft = FinishLeftRegions(regLo, null); degenerate = true; } if (degenerate) { AddRightEdges(regUp, eBottomLeft._Onext, eTopLeft, eTopLeft, true); return; } // Non-degenerate situation -- need to add a temporary, fixable edge. // Connect to the closer of eLo.Org, eUp.Org. MeshUtils.Edge eNew; if (Geom.VertLeq(eLo._Org, eUp._Org)) { eNew = eLo._Oprev; } else { eNew = eUp; } eNew = _mesh.Connect(eBottomLeft._Lprev, eNew); // Prevent cleanup, otherwise eNew might disappear before we've even // had a chance to mark it as a temporary edge. AddRightEdges(regUp, eNew, eNew._Onext, eNew._Onext, false); eNew._Sym._activeRegion._fixUpperEdge = true; WalkDirtyRegions(regUp); }
/// <summary> /// Purpose: insert right-going edges into the edge dictionary, and update /// winding numbers and mesh connectivity appropriately. All right-going /// edges share a common origin vOrg. Edges are inserted CCW starting at /// eFirst; the last edge inserted is eLast.Oprev. If vOrg has any /// left-going edges already processed, then eTopLeft must be the edge /// such that an imaginary upward vertical segment from vOrg would be /// contained between eTopLeft.Oprev and eTopLeft; otherwise eTopLeft /// should be null. /// </summary> private void AddRightEdges(ActiveRegion regUp, MeshUtils.Edge eFirst, MeshUtils.Edge eLast, MeshUtils.Edge eTopLeft, bool cleanUp) { bool firstTime = true; var e = eFirst; do { Debug.Assert(Geom.VertLeq(e._Org, e._Dst)); AddRegionBelow(regUp, e._Sym); e = e._Onext; } while (e != eLast); // Walk *all* right-going edges from e.Org, in the dictionary order, // updating the winding numbers of each region, and re-linking the mesh // edges to match the dictionary ordering (if necessary). if (eTopLeft == null) { eTopLeft = RegionBelow(regUp)._eUp._Rprev; } ActiveRegion regPrev = regUp, reg; var ePrev = eTopLeft; while (true) { reg = RegionBelow(regPrev); e = reg._eUp._Sym; if (e._Org != ePrev._Org) break; if (e._Onext != ePrev) { // Unlink e from its current position, and relink below ePrev _mesh.Splice(e._Oprev, e); _mesh.Splice(ePrev._Oprev, e); } // Compute the winding number and "inside" flag for the new regions reg._windingNumber = regPrev._windingNumber - e._winding; reg._inside = Geom.IsWindingInside(_windingRule, reg._windingNumber); // Check for two outgoing edges with same slope -- process these // before any intersection tests (see example in tessComputeInterior). regPrev._dirty = true; if (!firstTime && CheckForRightSplice(regPrev)) { Geom.AddWinding(e, ePrev); DeleteRegion(regPrev); _mesh.Delete(ePrev); } firstTime = false; regPrev = reg; ePrev = e; } regPrev._dirty = true; Debug.Assert(regPrev._windingNumber - e._winding == reg._windingNumber); if (cleanUp) { // Check for intersections between newly adjacent edges. WalkDirtyRegions(regPrev); } }
/// <summary> /// We are given a vertex with one or more left-going edges. All affected /// edges should be in the edge dictionary. Starting at regFirst->eUp, /// we walk down deleting all regions where both edges have the same /// origin vOrg. At the same time we copy the "inside" flag from the /// active region to the face, since at this point each face will belong /// to at most one region (this was not necessarily true until this point /// in the sweep). The walk stops at the region above regLast; if regLast /// is null we walk as far as possible. At the same time we relink the /// mesh if necessary, so that the ordering of edges around vOrg is the /// same as in the dictionary. /// </summary> private MeshUtils.Edge FinishLeftRegions(ActiveRegion regFirst, ActiveRegion regLast) { var regPrev = regFirst; var ePrev = regFirst._eUp; while (regPrev != regLast) { regPrev._fixUpperEdge = false; // placement was OK var reg = RegionBelow(regPrev); var e = reg._eUp; if (e._Org != ePrev._Org) { if (!reg._fixUpperEdge) { // Remove the last left-going edge. Even though there are no further // edges in the dictionary with this origin, there may be further // such edges in the mesh (if we are adding left edges to a vertex // that has already been processed). Thus it is important to call // FinishRegion rather than just DeleteRegion. FinishRegion(regPrev); break; } // If the edge below was a temporary edge introduced by // ConnectRightVertex, now is the time to fix it. e = _mesh.Connect(ePrev._Lprev, e._Sym); FixUpperEdge(reg, e); } // Relink edges so that ePrev.Onext == e if (ePrev._Onext != e) { _mesh.Splice(e._Oprev, e); _mesh.Splice(ePrev, e); } FinishRegion(regPrev); // may change reg.eUp ePrev = reg._eUp; regPrev = reg; } return ePrev; }
/// <summary> /// Make the sentinel coordinates big enough that they will never be /// merged with real input features. /// /// We add two sentinel edges above and below all other edges, /// to avoid special cases at the top and bottom. /// </summary> private void AddSentinel(float smin, float smax, float t) { var e = _mesh.MakeEdge(); e._Org._s = smax; e._Org._t = t; e._Dst._s = smin; e._Dst._t = t; _event = e._Dst; // initialize it var reg = new ActiveRegion(); reg._eUp = e; reg._windingNumber = 0; reg._inside = false; reg._fixUpperEdge = false; reg._sentinel = true; reg._dirty = false; reg._nodeUp = _dict.Insert(reg); }
/// <summary> /// Replace an upper edge which needs fixing (see ConnectRightVertex). /// </summary> private void FixUpperEdge(ActiveRegion reg, MeshUtils.Edge newEdge) { Debug.Assert(reg._fixUpperEdge); _mesh.Delete(reg._eUp); reg._fixUpperEdge = false; reg._eUp = newEdge; newEdge._activeRegion = reg; }
/// <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) { var regLo = RegionBelow(regUp); var eUp = regUp._eUp; var eLo = regLo._eUp; var orgUp = eUp._Org; var orgLo = eLo._Org; var dstUp = eUp._Dst; var 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; } var tMinUp = Math.Min(orgUp._t, dstUp._t); var 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 var isect = new 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). var 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); 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(eUp._Sym); _mesh.Splice(eLo._Sym, eUp); regUp = TopLeftRegion(regUp); eUp = RegionBelow(regUp)._eUp; FinishLeftRegions(RegionBelow(regUp), regLo); AddRightEdges(regUp, eUp._Oprev, eUp, eUp, true); return true; } if( dstUp == _event ) { /* Splice dstUp into eLo, and process the new region(s) */ _mesh.SplitEdge(eLo._Sym); _mesh.Splice(eUp._Lnext, eLo._Oprev); regLo = regUp; regUp = TopRightRegion(regUp); var e = RegionBelow(regUp)._eUp._Rprev; regLo._eUp = eLo._Oprev; eLo = FinishLeftRegions(regLo, null); AddRightEdges(regUp, eLo._Onext, eUp._Rprev, e, true); 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(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(eLo._Sym); eLo._Org._s = _event._s; eLo._Org._t = _event._t; } // 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.SplitEdge(eUp._Sym); _mesh.SplitEdge(eLo._Sym); _mesh.Splice(eLo._Oprev, eUp); eUp._Org._s = isect._s; eUp._Org._t = isect._t; 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; }
private ActiveRegion RegionBelow(ActiveRegion reg) { return reg._nodeUp._prev._key; }
/// <summary> /// 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. /// </summary> private bool CheckForLeftSplice(ActiveRegion regUp) { var regLo = RegionBelow(regUp); var eUp = regUp._eUp; var eLo = regLo._eUp; Debug.Assert(!Geom.VertEq(eUp._Dst, eLo._Dst)); if (Geom.VertLeq(eUp._Dst, eLo._Dst)) { if (Geom.EdgeSign(eUp._Dst, eLo._Dst, eUp._Org) < 0.0f) { return false; } // eLo.Dst is above eUp, so splice eLo.Dst into eUp RegionAbove(regUp)._dirty = regUp._dirty = true; var e = _mesh.SplitEdge(eUp); _mesh.Splice(eLo._Sym, e); e._Lface._inside = regUp._inside; } else { if (Geom.EdgeSign(eLo._Dst, eUp._Dst, eLo._Org) > 0.0f) { return false; } // eUp.Dst is below eLo, so splice eUp.Dst into eLo regUp._dirty = regLo._dirty = true; var e = _mesh.SplitEdge(eLo); _mesh.Splice(eUp._Lnext, eLo._Sym); e._Rface._inside = regUp._inside; } return true; }