//------------------------------------------------------------------------------ internal virtual void Reset() { m_CurrentLM = m_MinimaList; if (m_CurrentLM == null) { return; //ie nothing to process } //reset all edges ... m_Scanbeam = null; ClipperLocalMinima lm = m_MinimaList; while (lm != null) { InsertScanbeam(lm.Y); ClipperTEdge e = lm.LeftBound; if (e != null) { e.Curr = e.Bot; e.OutIdx = Unassigned; } e = lm.RightBound; if (e != null) { e.Curr = e.Bot; e.OutIdx = Unassigned; } lm = lm.Next; } m_ActiveEdges = null; }
//------------------------------------------------------------------------------ private ClipperTEdge FindNextLocMin(ClipperTEdge E) { ClipperTEdge E2; for (; ;) { while (E.Bot != E.Prev.Bot || E.Curr == E.Top) { E = E.Next; } if (E.Dx != Horizontal && E.Prev.Dx != Horizontal) { break; } while (E.Prev.Dx == Horizontal) { E = E.Prev; } E2 = E; while (E.Dx == Horizontal) { E = E.Next; } if (E.Top.Y == E.Prev.Bot.Y) { continue; //ie just an intermediate horz. } if (E2.Prev.Bot.X < E.Bot.X) { E = E2; } break; } return(E); }
//------------------------------------------------------------------------------ private void ReverseHorizontal(ClipperTEdge e) { //swap horizontal edges' top and bottom x's so they follow the natural //progression of the bounds - ie so their xbots will align with the //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] Swap(ref e.Top.X, ref e.Bot.X); }
//------------------------------------------------------------------------------ internal void UpdateEdgeIntoAEL(ref ClipperTEdge e) { if (e.NextInLML == null) { throw new ClipperException("UpdateEdgeIntoAEL: invalid call"); } ClipperTEdge AelPrev = e.PrevInAEL; ClipperTEdge AelNext = e.NextInAEL; e.NextInLML.OutIdx = e.OutIdx; if (AelPrev != null) { AelPrev.NextInAEL = e.NextInLML; } else { m_ActiveEdges = e.NextInLML; } if (AelNext != null) { AelNext.PrevInAEL = e.NextInLML; } e.NextInLML.Side = e.Side; e.NextInLML.WindDelta = e.WindDelta; e.NextInLML.WindCnt = e.WindCnt; e.NextInLML.WindCnt2 = e.WindCnt2; e = e.NextInLML; e.Curr = e.Bot; e.PrevInAEL = AelPrev; e.NextInAEL = AelNext; if (!IsHorizontal(e)) { InsertScanbeam(e.Top.Y); } }
//------------------------------------------------------------------------------ private void InitEdge(ClipperTEdge e, ClipperTEdge eNext, ClipperTEdge ePrev, ClipperIntPoint pt) { e.Next = eNext; e.Prev = ePrev; e.Curr = pt; e.OutIdx = Unassigned; }
//------------------------------------------------------------------------------ ClipperTEdge RemoveEdge(ClipperTEdge e) { //removes e from double_linked_list (but without removing from memory) e.Prev.Next = e.Next; e.Next.Prev = e.Prev; ClipperTEdge result = e.Next; e.Prev = null; //flag as removed (see ClipperBase.Clear) return(result); }
//------------------------------------------------------------------------------ private void SetDx(ClipperTEdge e) { e.Delta.X = (e.Top.X - e.Bot.X); e.Delta.Y = (e.Top.Y - e.Bot.Y); if (e.Delta.Y == 0) { e.Dx = Horizontal; } else { e.Dx = (double)(e.Delta.X) / (e.Delta.Y); } }
//------------------------------------------------------------------------------ internal static bool SlopesEqual(ClipperTEdge e1, ClipperTEdge e2, bool UseFullRange) { if (UseFullRange) { return(ClipperInt128.Int128Mul(e1.Delta.Y, e2.Delta.X) == ClipperInt128.Int128Mul(e1.Delta.X, e2.Delta.Y)); } else { return((long)(e1.Delta.Y) * (e2.Delta.X) == (long)(e1.Delta.X) * (e2.Delta.Y)); } }
//------------------------------------------------------------------------------ private void InitEdge2(ClipperTEdge e, ClipperPolyType polyType) { if (e.Curr.Y >= e.Next.Curr.Y) { e.Bot = e.Curr; e.Top = e.Next.Curr; } else { e.Top = e.Curr; e.Bot = e.Next.Curr; } SetDx(e); e.PolyTyp = polyType; }
//------------------------------------------------------------------------------ internal void DeleteFromAEL(ClipperTEdge e) { ClipperTEdge AelPrev = e.PrevInAEL; ClipperTEdge AelNext = e.NextInAEL; if (AelPrev == null && AelNext == null && (e != m_ActiveEdges)) { return; //already deleted } if (AelPrev != null) { AelPrev.NextInAEL = AelNext; } else { m_ActiveEdges = AelNext; } if (AelNext != null) { AelNext.PrevInAEL = AelPrev; } e.NextInAEL = null; e.PrevInAEL = null; }
//------------------------------------------------------------------------------ public bool AddPath(List <ClipperIntPoint> pg, ClipperPolyType polyType, bool Closed) { if (!Closed && polyType == ClipperPolyType.Clip) { throw new ClipperException("AddPath: Open paths must be subject."); } int highI = (int)pg.Count - 1; if (Closed) { while (highI > 0 && (pg[highI] == pg[0])) { --highI; } } while (highI > 0 && (pg[highI] == pg[highI - 1])) { --highI; } if ((Closed && highI < 2) || (!Closed && highI < 1)) { return(false); } //create a new edge array ... List <ClipperTEdge> edges = new List <ClipperTEdge>(highI + 1); for (int i = 0; i <= highI; i++) { edges.Add(new ClipperTEdge()); } bool IsFlat = true; //1. Basic (first) edge initialization ... edges[1].Curr = pg[1]; RangeTest(pg[0], ref m_UseFullRange); RangeTest(pg[highI], ref m_UseFullRange); InitEdge(edges[0], edges[1], edges[highI], pg[0]); InitEdge(edges[highI], edges[0], edges[highI - 1], pg[highI]); for (int i = highI - 1; i >= 1; --i) { RangeTest(pg[i], ref m_UseFullRange); InitEdge(edges[i], edges[i + 1], edges[i - 1], pg[i]); } ClipperTEdge eStart = edges[0]; //2. Remove duplicate vertices, and (when closed) collinear edges ... ClipperTEdge E = eStart, eLoopStop = eStart; for (; ;) { //nb: allows matching start and end points when not Closed ... if (E.Curr == E.Next.Curr && (Closed || E.Next != eStart)) { if (E == E.Next) { break; } if (E == eStart) { eStart = E.Next; } E = RemoveEdge(E); eLoopStop = E; continue; } if (E.Prev == E.Next) { break; //only two vertices } else if (Closed && SlopesEqual(E.Prev.Curr, E.Curr, E.Next.Curr, m_UseFullRange) && (!PreserveCollinear || !Pt2IsBetweenPt1AndPt3(E.Prev.Curr, E.Curr, E.Next.Curr))) { //Collinear edges are allowed for open paths but in closed paths //the default is to merge adjacent collinear edges into a single edge. //However, if the PreserveCollinear property is enabled, only overlapping //collinear edges (ie spikes) will be removed from closed paths. if (E == eStart) { eStart = E.Next; } E = RemoveEdge(E); E = E.Prev; eLoopStop = E; continue; } E = E.Next; if ((E == eLoopStop) || (!Closed && E.Next == eStart)) { break; } } if ((!Closed && (E == E.Next)) || (Closed && (E.Prev == E.Next))) { return(false); } if (!Closed) { m_HasOpenPaths = true; eStart.Prev.OutIdx = Skip; } //3. Do second stage of edge initialization ... E = eStart; do { InitEdge2(E, polyType); E = E.Next; if (IsFlat && E.Curr.Y != eStart.Curr.Y) { IsFlat = false; } }while (E != eStart); //4. Finally, add edge bounds to LocalMinima list ... //Totally flat paths must be handled differently when adding them //to LocalMinima list to avoid endless loops etc ... if (IsFlat) { if (Closed) { return(false); } E.Prev.OutIdx = Skip; ClipperLocalMinima locMin = new ClipperLocalMinima { Next = null, Y = E.Bot.Y, LeftBound = null, RightBound = E }; locMin.RightBound.Side = ClipperEdgeSide.Right; locMin.RightBound.WindDelta = 0; for (; ;) { if (E.Bot.X != E.Prev.Top.X) { ReverseHorizontal(E); } if (E.Next.OutIdx == Skip) { break; } E.NextInLML = E.Next; E = E.Next; } InsertLocalMinima(locMin); m_edges.Add(edges); return(true); } m_edges.Add(edges); bool leftBoundIsForward; ClipperTEdge EMin = null; //workaround to avoid an endless loop in the while loop below when //open paths have matching start and end points ... if (E.Prev.Bot == E.Prev.Top) { E = E.Next; } for (; ;) { E = FindNextLocMin(E); if (E == EMin) { break; } else if (EMin == null) { EMin = E; } //E and E.Prev now share a local minima (left aligned if horizontal). //Compare their slopes to find which starts which bound ... ClipperLocalMinima locMin = new ClipperLocalMinima { Next = null, Y = E.Bot.Y }; if (E.Dx < E.Prev.Dx) { locMin.LeftBound = E.Prev; locMin.RightBound = E; leftBoundIsForward = false; //Q.nextInLML = Q.prev } else { locMin.LeftBound = E; locMin.RightBound = E.Prev; leftBoundIsForward = true; //Q.nextInLML = Q.next } locMin.LeftBound.Side = ClipperEdgeSide.Left; locMin.RightBound.Side = ClipperEdgeSide.Right; if (!Closed) { locMin.LeftBound.WindDelta = 0; } else if (locMin.LeftBound.Next == locMin.RightBound) { locMin.LeftBound.WindDelta = -1; } else { locMin.LeftBound.WindDelta = 1; } locMin.RightBound.WindDelta = -locMin.LeftBound.WindDelta; E = ProcessBound(locMin.LeftBound, leftBoundIsForward); if (E.OutIdx == Skip) { E = ProcessBound(E, leftBoundIsForward); } ClipperTEdge E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); if (E2.OutIdx == Skip) { E2 = ProcessBound(E2, !leftBoundIsForward); } if (locMin.LeftBound.OutIdx == Skip) { locMin.LeftBound = null; } else if (locMin.RightBound.OutIdx == Skip) { locMin.RightBound = null; } InsertLocalMinima(locMin); if (!leftBoundIsForward) { E = E2; } } return(true); }
//------------------------------------------------------------------------------ private ClipperTEdge ProcessBound(ClipperTEdge E, bool LeftBoundIsForward) { ClipperTEdge EStart, Result = E; ClipperTEdge Horz; if (Result.OutIdx == Skip) { //check if there are edges beyond the skip edge in the bound and if so //create another LocMin and calling ProcessBound once more ... E = Result; if (LeftBoundIsForward) { while (E.Top.Y == E.Next.Bot.Y) { E = E.Next; } while (E != Result && E.Dx == Horizontal) { E = E.Prev; } } else { while (E.Top.Y == E.Prev.Bot.Y) { E = E.Prev; } while (E != Result && E.Dx == Horizontal) { E = E.Next; } } if (E == Result) { if (LeftBoundIsForward) { Result = E.Next; } else { Result = E.Prev; } } else { //there are more edges in the bound beyond result starting with E if (LeftBoundIsForward) { E = Result.Next; } else { E = Result.Prev; } ClipperLocalMinima locMin = new ClipperLocalMinima { Next = null, Y = E.Bot.Y, LeftBound = null, RightBound = E }; E.WindDelta = 0; Result = ProcessBound(E, LeftBoundIsForward); InsertLocalMinima(locMin); } return(Result); } if (E.Dx == Horizontal) { //We need to be careful with open paths because this may not be a //true local minima (ie E may be following a skip edge). //Also, consecutive horz. edges may start heading left before going right. if (LeftBoundIsForward) { EStart = E.Prev; } else { EStart = E.Next; } if (EStart.Dx == Horizontal) //ie an adjoining horizontal skip edge { if (EStart.Bot.X != E.Bot.X && EStart.Top.X != E.Bot.X) { ReverseHorizontal(E); } } else if (EStart.Bot.X != E.Bot.X) { ReverseHorizontal(E); } } EStart = E; if (LeftBoundIsForward) { while (Result.Top.Y == Result.Next.Bot.Y && Result.Next.OutIdx != Skip) { Result = Result.Next; } if (Result.Dx == Horizontal && Result.Next.OutIdx != Skip) { //nb: at the top of a bound, horizontals are added to the bound //only when the preceding edge attaches to the horizontal's left vertex //unless a Skip edge is encountered when that becomes the top divide Horz = Result; while (Horz.Prev.Dx == Horizontal) { Horz = Horz.Prev; } if (Horz.Prev.Top.X > Result.Next.Top.X) { Result = Horz.Prev; } } while (E != Result) { E.NextInLML = E.Next; if (E.Dx == Horizontal && E != EStart && E.Bot.X != E.Prev.Top.X) { ReverseHorizontal(E); } E = E.Next; } if (E.Dx == Horizontal && E != EStart && E.Bot.X != E.Prev.Top.X) { ReverseHorizontal(E); } Result = Result.Next; //move to the edge just beyond current bound } else { while (Result.Top.Y == Result.Prev.Bot.Y && Result.Prev.OutIdx != Skip) { Result = Result.Prev; } if (Result.Dx == Horizontal && Result.Prev.OutIdx != Skip) { Horz = Result; while (Horz.Next.Dx == Horizontal) { Horz = Horz.Next; } if (Horz.Next.Top.X == Result.Prev.Top.X || Horz.Next.Top.X > Result.Prev.Top.X) { Result = Horz.Next; } } while (E != Result) { E.NextInLML = E.Prev; if (E.Dx == Horizontal && E != EStart && E.Bot.X != E.Next.Top.X) { ReverseHorizontal(E); } E = E.Prev; } if (E.Dx == Horizontal && E != EStart && E.Bot.X != E.Next.Top.X) { ReverseHorizontal(E); } Result = Result.Prev; //move to the edge just beyond current bound } return(Result); }
//------------------------------------------------------------------------------ internal static bool IsHorizontal(ClipperTEdge e) { return(e.Delta.Y == 0); }
//------------------------------------------------------------------------------ internal void SwapPositionsInAEL(ClipperTEdge edge1, ClipperTEdge edge2) { //check that one or other edge hasn't already been removed from AEL ... if (edge1.NextInAEL == edge1.PrevInAEL || edge2.NextInAEL == edge2.PrevInAEL) { return; } if (edge1.NextInAEL == edge2) { ClipperTEdge next = edge2.NextInAEL; if (next != null) { next.PrevInAEL = edge1; } ClipperTEdge prev = edge1.PrevInAEL; if (prev != null) { prev.NextInAEL = edge2; } edge2.PrevInAEL = prev; edge2.NextInAEL = edge1; edge1.PrevInAEL = edge2; edge1.NextInAEL = next; } else if (edge2.NextInAEL == edge1) { ClipperTEdge next = edge1.NextInAEL; if (next != null) { next.PrevInAEL = edge2; } ClipperTEdge prev = edge2.PrevInAEL; if (prev != null) { prev.NextInAEL = edge1; } edge1.PrevInAEL = prev; edge1.NextInAEL = edge2; edge2.PrevInAEL = edge1; edge2.NextInAEL = next; } else { ClipperTEdge next = edge1.NextInAEL; ClipperTEdge prev = edge1.PrevInAEL; edge1.NextInAEL = edge2.NextInAEL; if (edge1.NextInAEL != null) { edge1.NextInAEL.PrevInAEL = edge1; } edge1.PrevInAEL = edge2.PrevInAEL; if (edge1.PrevInAEL != null) { edge1.PrevInAEL.NextInAEL = edge1; } edge2.NextInAEL = next; if (edge2.NextInAEL != null) { edge2.NextInAEL.PrevInAEL = edge2; } edge2.PrevInAEL = prev; if (edge2.PrevInAEL != null) { edge2.PrevInAEL.NextInAEL = edge2; } } if (edge1.PrevInAEL == null) { m_ActiveEdges = edge1; } else if (edge2.PrevInAEL == null) { m_ActiveEdges = edge2; } }