//------------------------------------------------------------------------------ private void UpdateEdgeIntoAEL(ref TEdge4 e) { if (e.nextInLML == null) throw new ClipperException("UpdateEdgeIntoAEL: invalid call"); TEdge4 AelPrev = e.prevInAEL; TEdge4 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.prevInAEL = AelPrev; e.nextInAEL = AelNext; if (e.dx != horizontal) { InsertScanbeam(e.ytop); //if output polygons share an edge, they'll need joining later ... if (e.outIdx >= 0 && AelPrev != null && AelPrev.outIdx >= 0 && AelPrev.xbot == e.xcurr && SlopesEqual(e, AelPrev)) AddJoin(e, AelPrev); } }
//------------------------------------------------------------------------------ private bool FixupIntersections() { if ( m_IntersectNodes.next == null ) return true; CopyAELToSEL(); IntersectNode int1 = m_IntersectNodes; IntersectNode int2 = m_IntersectNodes.next; while (int2 != null) { TEdge4 e1 = int1.edge1; TEdge4 e2; if (e1.prevInSEL == int1.edge2) e2 = e1.prevInSEL; else if (e1.nextInSEL == int1.edge2) e2 = e1.nextInSEL; else { //The current intersection is out of order, so try and swap it with //a subsequent intersection ... while (int2 != null) { if (int2.edge1.nextInSEL == int2.edge2 || int2.edge1.prevInSEL == int2.edge2) break; else int2 = int2.next; } if (int2 == null) return false; //oops!!! //found an intersect node that can be swapped ... SwapIntersectNodes(int1, int2); e1 = int1.edge1; e2 = int1.edge2; } SwapPositionsInSEL(e1, e2); int1 = int1.next; int2 = int1.next; } m_SortedEdges = null; //finally, check the last intersection too ... return (int1.edge1.prevInSEL == int1.edge2 || int1.edge1.nextInSEL == int1.edge2); }
//------------------------------------------------------------------------------ private TEdge4 GetNextInAEL(TEdge4 e, Direction Direction) { if (Direction == Direction.dLeftToRight) return e.nextInAEL; else return e.prevInAEL; }
//------------------------------------------------------------------------------ private void DeleteFromSEL(TEdge4 e) { TEdge4 SelPrev = e.prevInSEL; TEdge4 SelNext = e.nextInSEL; if (SelPrev == null && SelNext == null && (e != m_SortedEdges)) return; //already deleted if (SelPrev != null) SelPrev.nextInSEL = SelNext; else m_SortedEdges = SelNext; if (SelNext != null) SelNext.prevInSEL = SelPrev; e.nextInSEL = null; e.prevInSEL = null; }
//------------------------------------------------------------------------------ private void DoMaxima(TEdge4 e, int topY) { TEdge4 eMaxPair = GetMaximaPair(e); int X = e.xtop; TEdge4 eNext = e.nextInAEL; while( eNext != eMaxPair ) { if (eNext == null) throw new ClipperException("DoMaxima error"); IntersectEdges( e, eNext, new IntPoint(X, topY), Protects.ipBoth ); eNext = eNext.nextInAEL; } if( e.outIdx < 0 && eMaxPair.outIdx < 0 ) { DeleteFromAEL( e ); DeleteFromAEL( eMaxPair ); } else if( e.outIdx >= 0 && eMaxPair.outIdx >= 0 ) { IntersectEdges(e, eMaxPair, new IntPoint(X, topY), Protects.ipNone); } else throw new ClipperException("DoMaxima error"); }
//------------------------------------------------------------------------------ private void AppendPolygon(TEdge4 e1, TEdge4 e2) { //get the start and ends of both output polygons ... PolyPt p1_lft = m_PolyPts[e1.outIdx]; PolyPt p1_rt = p1_lft.prev; PolyPt p2_lft = m_PolyPts[e2.outIdx]; PolyPt p2_rt = p2_lft.prev; //fixup orientation (hole) flag if necessary ... if (p1_lft.isHole != p2_lft.isHole) { PolyPt p, pp; PolyPt bottom1 = PolygonBottom(p1_lft); PolyPt bottom2 = PolygonBottom(p2_lft); if (bottom1.pt.Y > bottom2.pt.Y) p = p2_lft; else if (bottom1.pt.Y < bottom2.pt.Y) p = p1_lft; else if (bottom1.pt.X < bottom2.pt.X) p = p2_lft; else if (bottom1.pt.X > bottom2.pt.X) p = p1_lft; //todo - the following line still isn't 100% ... else if (bottom1.isHole) p = p1_lft; else p = p2_lft; bool hole = !p.isHole; pp = p; do { pp.isHole = hole; pp = pp.next; } while (pp != p); } EdgeSide side; //join e2 poly onto e1 poly and delete pointers to e2 ... if( e1.side == EdgeSide.esLeft ) { if (e2.side == EdgeSide.esLeft) { //z y x a b c ReversePolyPtLinks(p2_lft); p2_lft.next = p1_lft; p1_lft.prev = p2_lft; p1_rt.next = p2_rt; p2_rt.prev = p1_rt; m_PolyPts[e1.outIdx] = p2_rt; } else { //x y z a b c p2_rt.next = p1_lft; p1_lft.prev = p2_rt; p2_lft.prev = p1_rt; p1_rt.next = p2_lft; m_PolyPts[e1.outIdx] = p2_lft; } side = EdgeSide.esLeft; } else { if (e2.side == EdgeSide.esRight) { //a b c z y x ReversePolyPtLinks( p2_lft ); p1_rt.next = p2_rt; p2_rt.prev = p1_rt; p2_lft.next = p1_lft; p1_lft.prev = p2_lft; } else { //a b c x y z p1_rt.next = p2_lft; p2_lft.prev = p1_rt; p1_lft.prev = p2_rt; p2_rt.next = p1_lft; } side = EdgeSide.esRight; } int OKIdx = e1.outIdx; int ObsoleteIdx = e2.outIdx; m_PolyPts[ObsoleteIdx] = null; e1.outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly e2.outIdx = -1; TEdge4 e = m_ActiveEdges; while( e != null ) { if( e.outIdx == ObsoleteIdx ) { e.outIdx = OKIdx; e.side = side; break; } e = e.nextInAEL; } }
//------------------------------------------------------------------------------ private void CopyAELToSEL() { TEdge4 e = m_ActiveEdges; m_SortedEdges = e; if (m_ActiveEdges == null) return; m_SortedEdges.prevInSEL = null; e = e.nextInAEL; while (e != null) { e.prevInSEL = e.prevInAEL; e.prevInSEL.nextInSEL = e; e.nextInSEL = null; e = e.nextInAEL; } }
//------------------------------------------------------------------------------ protected override bool Reset() { if ( !base.Reset() ) return false; m_Scanbeam = null; m_ActiveEdges = null; m_SortedEdges = null; LocalMinima lm = m_MinimaList; while (lm != null) { InsertScanbeam(lm.Y); InsertScanbeam(lm.leftBound.ytop); lm = lm.next; } return true; }
//------------------------------------------------------------------------------ private static void SwapPolyIndexes(TEdge4 edge1, TEdge4 edge2) { int outIdx = edge1.outIdx; edge1.outIdx = edge2.outIdx; edge2.outIdx = outIdx; }
//------------------------------------------------------------------------------ private void SetDx(TEdge4 e) { if (e.ybot == e.ytop) e.dx = horizontal; else e.dx = (double)(e.xtop - e.xbot)/(e.ytop - e.ybot); }
//------------------------------------------------------------------------------ private void SwapX(TEdge4 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.] e.xcurr = e.xtop; e.xtop = e.xbot; e.xbot = e.xcurr; }
//------------------------------------------------------------------------------ private void InitEdge(TEdge4 e, TEdge4 eNext, TEdge4 ePrev, IntPoint pt, PolyType polyType) { e.next = eNext; e.prev = ePrev; e.xcurr = pt.X; e.ycurr = pt.Y; if (e.ycurr >= e.next.ycurr) { e.xbot = e.xcurr; e.ybot = e.ycurr; e.xtop = e.next.xcurr; e.ytop = e.next.ycurr; e.windDelta = 1; } else { e.xtop = e.xcurr; e.ytop = e.ycurr; e.xbot = e.next.xcurr; e.ybot = e.next.ycurr; e.windDelta = -1; } SetDx(e); e.polyType = polyType; e.outIdx = -1; }
//--------------------------------------------------------------------------- TEdge4 AddBoundsToLML(TEdge4 e) { //Starting at the top of one bound we progress to the bottom where there's //a local minima. We then go to the top of the next bound. These two bounds //form the left and right (or right and left) bounds of the local minima. e.nextInLML = null; e = e.next; for (;;) { if ( e.dx == horizontal ) { //nb: proceed through horizontals when approaching from their right, // but break on horizontal minima if approaching from their left. // This ensures 'local minima' are always on the left of horizontals. if (e.next.ytop < e.ytop && e.next.xbot > e.prev.xbot) break; if (e.xtop != e.prev.xbot) SwapX(e); e.nextInLML = e.prev; } else if (e.ycurr == e.prev.ycurr) break; else e.nextInLML = e.prev; e = e.next; } //e and e.prev are now at a local minima ... LocalMinima newLm = new LocalMinima(); newLm.next = null; newLm.Y = e.prev.ybot; if ( e.dx == horizontal ) //horizontal edges never start a left bound { if (e.xbot != e.prev.xbot) SwapX(e); newLm.leftBound = e.prev; newLm.rightBound = e; } else if (e.dx < e.prev.dx) { newLm.leftBound = e.prev; newLm.rightBound = e; } else { newLm.leftBound = e; newLm.rightBound = e.prev; } newLm.leftBound.side = EdgeSide.esLeft; newLm.rightBound.side = EdgeSide.esRight; InsertLocalMinima( newLm ); for (;;) { if ( e.next.ytop == e.ytop && e.next.dx != horizontal ) break; e.nextInLML = e.next; e = e.next; if ( e.dx == horizontal && e.xbot != e.prev.xtop) SwapX(e); } return e.next; }
//------------------------------------------------------------------------------ internal bool SlopesEqual(TEdge4 e1, TEdge4 e2) { if (e1.ybot == e1.ytop) return (e2.ybot == e2.ytop); else if (e2.ybot == e2.ytop) return false; else return (Int64)(e1.ytop - e1.ybot)*(e2.xtop - e2.xbot) - (Int64)(e1.xtop - e1.xbot)*(e2.ytop - e2.ybot) == 0; }
//------------------------------------------------------------------------------ private void AddLocalMinPoly(TEdge4 e1, TEdge4 e2, IntPoint pt) { if (e2.dx == horizontal || (e1.dx > e2.dx)) { AddPolyPt(e1, pt); e2.outIdx = e1.outIdx; e1.side = EdgeSide.esLeft; e2.side = EdgeSide.esRight; } else { AddPolyPt(e2, pt); e1.outIdx = e2.outIdx; e1.side = EdgeSide.esRight; e2.side = EdgeSide.esLeft; } }
//------------------------------------------------------------------------------ private static void SwapSides(TEdge4 edge1, TEdge4 edge2) { EdgeSide side = edge1.side; edge1.side = edge2.side; edge2.side = side; }
//------------------------------------------------------------------------------ private PolyPt AddPolyPt(TEdge4 e, IntPoint pt) { bool ToFront = (e.side == EdgeSide.esLeft); if( e.outIdx < 0 ) { PolyPt newPolyPt = new PolyPt(); newPolyPt.pt = pt; newPolyPt.isHole = IsHole(e); m_PolyPts.Add(newPolyPt); newPolyPt.next = newPolyPt; newPolyPt.prev = newPolyPt; e.outIdx = m_PolyPts.Count-1; return newPolyPt; } else { PolyPt pp = m_PolyPts[e.outIdx]; if (ToFront && PointsEqual(pt, pp.pt)) return pp; if (!ToFront && PointsEqual(pt, pp.prev.pt)) return pp.prev; PolyPt newPolyPt = new PolyPt(); newPolyPt.pt = pt; newPolyPt.isHole = pp.isHole; newPolyPt.next = pp; newPolyPt.prev = pp.prev; newPolyPt.prev.next = newPolyPt; pp.prev = newPolyPt; if (ToFront) m_PolyPts[e.outIdx] = newPolyPt; return newPolyPt; } }
//------------------------------------------------------------------------------ private static int TopX(TEdge4 edge, int currentY) { if (currentY == edge.ytop) return edge.xtop; return edge.xbot + Round(edge.dx *(currentY - edge.ybot)); }
//------------------------------------------------------------------------------ private void BuildIntersectList(int topY) { if ( m_ActiveEdges == null ) return; //prepare for sorting ... TEdge4 e = m_ActiveEdges; e.tmpX = TopX( e, topY ); m_SortedEdges = e; m_SortedEdges.prevInSEL = null; e = e.nextInAEL; while( e != null ) { e.prevInSEL = e.prevInAEL; e.prevInSEL.nextInSEL = e; e.nextInSEL = null; e.tmpX = TopX( e, topY ); e = e.nextInAEL; } //bubblesort ... bool isModified = true; while( isModified && m_SortedEdges != null ) { isModified = false; e = m_SortedEdges; while( e.nextInSEL != null ) { TEdge4 eNext = e.nextInSEL; IntPoint pt = new IntPoint(); if(e.tmpX > eNext.tmpX && IntersectPoint(e, eNext, ref pt)) { AddIntersectNode( e, eNext, pt ); SwapPositionsInSEL(e, eNext); isModified = true; } else e = eNext; } if( e.prevInSEL != null ) e.prevInSEL.nextInSEL = null; else break; } m_SortedEdges = null; }
//------------------------------------------------------------------------------ private void AddEdgeToSEL(TEdge4 edge) { //SEL pointers in PEdge are reused to build a list of horizontal edges. //However, we don't need to worry about order with horizontal edge processing. if (m_SortedEdges == null) { m_SortedEdges = edge; edge.prevInSEL = null; edge.nextInSEL = null; } else { edge.nextInSEL = m_SortedEdges; edge.prevInSEL = null; m_SortedEdges.prevInSEL = edge; m_SortedEdges = edge; } }
//------------------------------------------------------------------------------ private void DeleteFromAEL(TEdge4 e) { TEdge4 AelPrev = e.prevInAEL; TEdge4 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; }
//------------------------------------------------------------------------------ private void AddHorzJoin(TEdge4 e, int idx) { HorzJoinRec hj = new HorzJoinRec(); hj.edge = e; hj.savedIdx = idx; m_HorizJoins.Add(hj); }
//------------------------------------------------------------------------------ private void DoEdge2(TEdge4 edge1, TEdge4 edge2, IntPoint pt) { AddPolyPt(edge2, pt); SwapSides(edge1, edge2); SwapPolyIndexes(edge1, edge2); }
//------------------------------------------------------------------------------ private void AddIntersectNode(TEdge4 e1, TEdge4 e2, IntPoint pt) { IntersectNode newNode = new IntersectNode(); newNode.edge1 = e1; newNode.edge2 = e2; newNode.pt = pt; newNode.next = null; if (m_IntersectNodes == null) m_IntersectNodes = newNode; else if( Process1Before2(newNode, m_IntersectNodes) ) { newNode.next = m_IntersectNodes; m_IntersectNodes = newNode; } else { IntersectNode iNode = m_IntersectNodes; while( iNode.next != null && Process1Before2(iNode.next, newNode) ) iNode = iNode.next; newNode.next = iNode.next; iNode.next = newNode; } }
//---------------------------------------------------------------------- private bool E2InsertsBeforeE1(TEdge4 e1, TEdge4 e2) { if (e2.xcurr == e1.xcurr) return e2.dx > e1.dx; else return e2.xcurr < e1.xcurr; }
//------------------------------------------------------------------------------ private void AddJoin(TEdge4 e1, TEdge4 e2, int e1OutIdx = -1) { JoinRec jr = new JoinRec(); if (e1OutIdx >= 0) jr.poly1Idx = e1OutIdx; else jr.poly1Idx = e1.outIdx; jr.pt1a = new IntPoint(e1.xbot, e1.ybot); jr.pt1b = new IntPoint(e1.xtop, e1.ytop); jr.poly2Idx = e2.outIdx; jr.pt2a = new IntPoint(e2.xbot, e2.ybot); jr.pt2b = new IntPoint(e2.xtop, e2.ytop); m_Joins.Add(jr); }
//------------------------------------------------------------------------------ private TEdge4 GetMaximaPair(TEdge4 e) { if (!IsMaxima(e.next, e.ytop) || (e.next.xtop != e.xtop)) return e.prev; else return e.next; }
//------------------------------------------------------------------------------ private void AddLocalMaxPoly(TEdge4 e1, TEdge4 e2, IntPoint pt) { AddPolyPt(e1, pt); if (e1.outIdx == e2.outIdx) { e1.outIdx = -1; e2.outIdx = -1; } else AppendPolygon(e1, e2); }
//------------------------------------------------------------------------------ private void InsertEdgeIntoAEL(TEdge4 edge) { edge.prevInAEL = null; edge.nextInAEL = null; if (m_ActiveEdges == null) { m_ActiveEdges = edge; } else if( E2InsertsBeforeE1(m_ActiveEdges, edge) ) { edge.nextInAEL = m_ActiveEdges; m_ActiveEdges.prevInAEL = edge; m_ActiveEdges = edge; } else { TEdge4 e = m_ActiveEdges; while (e.nextInAEL != null && !E2InsertsBeforeE1(e.nextInAEL, edge)) e = e.nextInAEL; edge.nextInAEL = e.nextInAEL; if (e.nextInAEL != null) e.nextInAEL.prevInAEL = edge; edge.prevInAEL = e; e.nextInAEL = edge; } }
//------------------------------------------------------------------------------ private void SwapPositionsInSEL(TEdge4 edge1, TEdge4 edge2) { if (edge1.nextInSEL == null && edge1.prevInSEL == null) return; if (edge2.nextInSEL == null && edge2.prevInSEL == null) return; if (edge1.nextInSEL == edge2) { TEdge4 next = edge2.nextInSEL; if (next != null) next.prevInSEL = edge1; TEdge4 prev = edge1.prevInSEL; if (prev != null) prev.nextInSEL = edge2; edge2.prevInSEL = prev; edge2.nextInSEL = edge1; edge1.prevInSEL = edge2; edge1.nextInSEL = next; } else if (edge2.nextInSEL == edge1) { TEdge4 next = edge1.nextInSEL; if (next != null) next.prevInSEL = edge2; TEdge4 prev = edge2.prevInSEL; if (prev != null) prev.nextInSEL = edge1; edge1.prevInSEL = prev; edge1.nextInSEL = edge2; edge2.prevInSEL = edge1; edge2.nextInSEL = next; } else { TEdge4 next = edge1.nextInSEL; TEdge4 prev = edge1.prevInSEL; edge1.nextInSEL = edge2.nextInSEL; if (edge1.nextInSEL != null) edge1.nextInSEL.prevInSEL = edge1; edge1.prevInSEL = edge2.prevInSEL; if (edge1.prevInSEL != null) edge1.prevInSEL.nextInSEL = edge1; edge2.nextInSEL = next; if (edge2.nextInSEL != null) edge2.nextInSEL.prevInSEL = edge2; edge2.prevInSEL = prev; if (edge2.prevInSEL != null) edge2.prevInSEL.nextInSEL = edge2; } if (edge1.prevInSEL == null) m_SortedEdges = edge1; else if (edge2.prevInSEL == null) m_SortedEdges = edge2; }