//------------------------------------------------------------------------------ private void AppendPolygon(TEdge e1, TEdge e2) { //get the start and ends of both output polygons ... OutRec outRec1 = m_PolyOuts[e1.outIdx]; OutRec outRec2 = m_PolyOuts[e2.outIdx]; OutRec holeStateRec; if (outRec1.FirstLeft == outRec2) holeStateRec = outRec2; else if (outRec2.FirstLeft == outRec1) holeStateRec = outRec1; else holeStateRec = GetLowermostRec(outRec1, outRec2); OutPt p1_lft = outRec1.pts; OutPt p1_rt = p1_lft.prev; OutPt p2_lft = outRec2.pts; OutPt p2_rt = p2_lft.prev; 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; outRec1.pts = 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; outRec1.pts = 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; } if (holeStateRec == outRec2) { outRec1.bottomPt = outRec2.bottomPt; outRec1.bottomPt.idx = outRec1.idx; if (outRec2.FirstLeft != outRec1) outRec1.FirstLeft = outRec2.FirstLeft; outRec1.isHole = outRec2.isHole; } outRec2.pts = null; outRec2.bottomPt = null; outRec2.AppendLink = outRec1; int OKIdx = e1.outIdx; int ObsoleteIdx = e2.outIdx; e1.outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly e2.outIdx = -1; TEdge e = m_ActiveEdges; while( e != null ) { if( e.outIdx == ObsoleteIdx ) { e.outIdx = OKIdx; e.side = side; break; } e = e.nextInAEL; } for (int i = 0; i < m_Joins.Count; ++i) { if (m_Joins[i].poly1Idx == ObsoleteIdx) m_Joins[i].poly1Idx = OKIdx; if (m_Joins[i].poly2Idx == ObsoleteIdx) m_Joins[i].poly2Idx = OKIdx; } for (int i = 0; i < m_HorizJoins.Count; ++i) { if (m_HorizJoins[i].savedIdx == ObsoleteIdx) m_HorizJoins[i].savedIdx = OKIdx; } }
//--------------------------------------------------------------------------- TEdge AddBoundsToLML(TEdge 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; }
public Clipper() { m_Scanbeam = null; m_ActiveEdges = null; m_SortedEdges = null; m_IntersectNodes = null; m_ExecuteLocked = false; m_PolyOuts = new List<OutRec>(); m_Joins = new List<JoinRec>(); m_HorizJoins = new List<HorzJoinRec>(); m_ReverseOutput = false; }
//------------------------------------------------------------------------------ private void DoMaxima(TEdge e, Int64 topY) { TEdge eMaxPair = GetMaximaPair(e); Int64 X = e.xtop; TEdge 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 InitEdge(TEdge e, TEdge eNext, TEdge 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; }
//------------------------------------------------------------------------------ private bool FixupIntersections() { if ( m_IntersectNodes.next == null ) return true; CopyAELToSEL(); IntersectNode int1 = m_IntersectNodes; IntersectNode int2 = m_IntersectNodes.next; while (int2 != null) { TEdge e1 = int1.edge1; TEdge 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 void AddIntersectNode(TEdge e1, TEdge 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 void DeleteFromSEL(TEdge e) { TEdge SelPrev = e.prevInSEL; TEdge 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 UpdateEdgeIntoAEL(ref TEdge e) { if (e.nextInLML == null) throw new ClipperException("UpdateEdgeIntoAEL: invalid call"); TEdge AelPrev = e.prevInAEL; TEdge 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); }
//------------------------------------------------------------------------------ private void IntersectEdges(TEdge e1, TEdge e2, IntPoint pt, Protects protects) { //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before //e2 in AEL except when e1 is being inserted at the intersection point ... bool e1stops = (Protects.ipLeft & protects) == 0 && e1.nextInLML == null && e1.xtop == pt.X && e1.ytop == pt.Y; bool e2stops = (Protects.ipRight & protects) == 0 && e2.nextInLML == null && e2.xtop == pt.X && e2.ytop == pt.Y; bool e1Contributing = (e1.outIdx >= 0); bool e2contributing = (e2.outIdx >= 0); //update winding counts... //assumes that e1 will be to the right of e2 ABOVE the intersection if (e1.polyType == e2.polyType) { if (IsEvenOddFillType(e1)) { int oldE1WindCnt = e1.windCnt; e1.windCnt = e2.windCnt; e2.windCnt = oldE1WindCnt; } else { if (e1.windCnt + e2.windDelta == 0) e1.windCnt = -e1.windCnt; else e1.windCnt += e2.windDelta; if (e2.windCnt - e1.windDelta == 0) e2.windCnt = -e2.windCnt; else e2.windCnt -= e1.windDelta; } } else { if (!IsEvenOddFillType(e2)) e1.windCnt2 += e2.windDelta; else e1.windCnt2 = (e1.windCnt2 == 0) ? 1 : 0; if (!IsEvenOddFillType(e1)) e2.windCnt2 -= e1.windDelta; else e2.windCnt2 = (e2.windCnt2 == 0) ? 1 : 0; } PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; if (e1.polyType == PolyType.ptSubject) { e1FillType = m_SubjFillType; e1FillType2 = m_ClipFillType; } else { e1FillType = m_ClipFillType; e1FillType2 = m_SubjFillType; } if (e2.polyType == PolyType.ptSubject) { e2FillType = m_SubjFillType; e2FillType2 = m_ClipFillType; } else { e2FillType = m_ClipFillType; e2FillType2 = m_SubjFillType; } int e1Wc, e2Wc; switch (e1FillType) { case PolyFillType.pftPositive: e1Wc = e1.windCnt; break; case PolyFillType.pftNegative: e1Wc = -e1.windCnt; break; default: e1Wc = Math.Abs(e1.windCnt); break; } switch (e2FillType) { case PolyFillType.pftPositive: e2Wc = e2.windCnt; break; case PolyFillType.pftNegative: e2Wc = -e2.windCnt; break; default: e2Wc = Math.Abs(e2.windCnt); break; } if (e1Contributing && e2contributing) { if ( e1stops || e2stops || (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || (e1.polyType != e2.polyType && m_ClipType != ClipType.ctXor)) AddLocalMaxPoly(e1, e2, pt); else DoBothEdges(e1, e2, pt); } else if (e1Contributing) { if ((e2Wc == 0 || e2Wc == 1) && (m_ClipType != ClipType.ctIntersection || e2.polyType == PolyType.ptSubject || (e2.windCnt2 != 0))) DoEdge1(e1, e2, pt); } else if (e2contributing) { if ((e1Wc == 0 || e1Wc == 1) && (m_ClipType != ClipType.ctIntersection || e1.polyType == PolyType.ptSubject || (e1.windCnt2 != 0))) DoEdge2(e1, e2, pt); } else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) { //neither edge is currently contributing ... Int64 e1Wc2, e2Wc2; switch (e1FillType2) { case PolyFillType.pftPositive: e1Wc2 = e1.windCnt2; break; case PolyFillType.pftNegative: e1Wc2 = -e1.windCnt2; break; default: e1Wc2 = Math.Abs(e1.windCnt2); break; } switch (e2FillType2) { case PolyFillType.pftPositive: e2Wc2 = e2.windCnt2; break; case PolyFillType.pftNegative: e2Wc2 = -e2.windCnt2; break; default: e2Wc2 = Math.Abs(e2.windCnt2); break; } if (e1.polyType != e2.polyType) AddLocalMinPoly(e1, e2, pt); else if (e1Wc == 1 && e2Wc == 1) switch (m_ClipType) { case ClipType.ctIntersection: { if (e1Wc2 > 0 && e2Wc2 > 0) AddLocalMinPoly(e1, e2, pt); break; } case ClipType.ctUnion: { if (e1Wc2 <= 0 && e2Wc2 <= 0) AddLocalMinPoly(e1, e2, pt); break; } case ClipType.ctDifference: { if (((e1.polyType == PolyType.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || ((e1.polyType == PolyType.ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) AddLocalMinPoly(e1, e2, pt); break; } case ClipType.ctXor: { AddLocalMinPoly(e1, e2, pt); break; } } else SwapSides(e1, e2); } if ((e1stops != e2stops) && ((e1stops && (e1.outIdx >= 0)) || (e2stops && (e2.outIdx >= 0)))) { SwapSides(e1, e2); SwapPolyIndexes(e1, e2); } //finally, delete any non-contributing maxima edges ... if (e1stops) DeleteFromAEL(e1); if (e2stops) DeleteFromAEL(e2); }
//------------------------------------------------------------------------------ private void DeleteFromAEL(TEdge e) { TEdge AelPrev = e.prevInAEL; TEdge 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 DoBothEdges(TEdge edge1, TEdge edge2, IntPoint pt) { AddOutPt(edge1, pt); AddOutPt(edge2, pt); SwapSides(edge1, edge2); SwapPolyIndexes(edge1, edge2); }
//------------------------------------------------------------------------------ private static void SwapPolyIndexes(TEdge edge1, TEdge edge2) { int outIdx = edge1.outIdx; edge1.outIdx = edge2.outIdx; edge2.outIdx = outIdx; }
//------------------------------------------------------------------------------ private static void SwapSides(TEdge edge1, TEdge edge2) { EdgeSide side = edge1.side; edge1.side = edge2.side; edge2.side = side; }
//------------------------------------------------------------------------------ private bool ProcessIntersections(Int64 botY, Int64 topY) { if( m_ActiveEdges == null ) return true; try { BuildIntersectList(botY, topY); if ( m_IntersectNodes == null) return true; if ( FixupIntersections() ) ProcessIntersectList(); else return false; } catch { m_SortedEdges = null; DisposeIntersectNodes(); throw new ClipperException("ProcessIntersections error"); } return true; }
//------------------------------------------------------------------------------ private void ProcessHorizontal(TEdge horzEdge) { Direction Direction; Int64 horzLeft, horzRight; if (horzEdge.xcurr < horzEdge.xtop) { horzLeft = horzEdge.xcurr; horzRight = horzEdge.xtop; Direction = Direction.dLeftToRight; } else { horzLeft = horzEdge.xtop; horzRight = horzEdge.xcurr; Direction = Direction.dRightToLeft; } TEdge eMaxPair; if (horzEdge.nextInLML != null) eMaxPair = null; else eMaxPair = GetMaximaPair(horzEdge); TEdge e = GetNextInAEL(horzEdge, Direction); while (e != null) { TEdge eNext = GetNextInAEL(e, Direction); if (eMaxPair != null || ((Direction == Direction.dLeftToRight) && (e.xcurr <= horzRight)) || ((Direction == Direction.dRightToLeft) && (e.xcurr >= horzLeft))) { //ok, so far it looks like we're still in range of the horizontal edge if (e.xcurr == horzEdge.xtop && eMaxPair == null) { if (SlopesEqual(e, horzEdge.nextInLML, m_UseFullRange)) { //if output polygons share an edge, they'll need joining later ... if (horzEdge.outIdx >= 0 && e.outIdx >= 0) AddJoin(horzEdge.nextInLML, e, horzEdge.outIdx, -1); break; //we've reached the end of the horizontal line } else if (e.dx < horzEdge.nextInLML.dx) //we really have got to the end of the intermediate horz edge so quit. //nb: More -ve slopes follow more +ve slopes ABOVE the horizontal. break; } if (e == eMaxPair) { //horzEdge is evidently a maxima horizontal and we've arrived at its end. if (Direction == Direction.dLeftToRight) IntersectEdges(horzEdge, e, new IntPoint(e.xcurr, horzEdge.ycurr), 0); else IntersectEdges(e, horzEdge, new IntPoint(e.xcurr, horzEdge.ycurr), 0); if (eMaxPair.outIdx >= 0) throw new ClipperException("ProcessHorizontal error"); return; } else if (e.dx == horizontal && !IsMinima(e) && !(e.xcurr > e.xtop)) { if (Direction == Direction.dLeftToRight) IntersectEdges(horzEdge, e, new IntPoint(e.xcurr, horzEdge.ycurr), (IsTopHorz(horzEdge, e.xcurr)) ? Protects.ipLeft : Protects.ipBoth); else IntersectEdges(e, horzEdge, new IntPoint(e.xcurr, horzEdge.ycurr), (IsTopHorz(horzEdge, e.xcurr)) ? Protects.ipRight : Protects.ipBoth); } else if (Direction == Direction.dLeftToRight) { IntersectEdges(horzEdge, e, new IntPoint(e.xcurr, horzEdge.ycurr), (IsTopHorz(horzEdge, e.xcurr)) ? Protects.ipLeft : Protects.ipBoth); } else { IntersectEdges(e, horzEdge, new IntPoint(e.xcurr, horzEdge.ycurr), (IsTopHorz(horzEdge, e.xcurr)) ? Protects.ipRight : Protects.ipBoth); } SwapPositionsInAEL(horzEdge, e); } else if ( (Direction == Direction.dLeftToRight && e.xcurr > horzRight && horzEdge.nextInSEL == null) || (Direction == Direction.dRightToLeft && e.xcurr < horzLeft && horzEdge.nextInSEL == null) ) break; e = eNext; } //end while ( e ) if (horzEdge.nextInLML != null) { if (horzEdge.outIdx >= 0) AddOutPt(horzEdge, new IntPoint(horzEdge.xtop, horzEdge.ytop)); UpdateEdgeIntoAEL(ref horzEdge); } else { if (horzEdge.outIdx >= 0) IntersectEdges(horzEdge, eMaxPair, new IntPoint(horzEdge.xtop, horzEdge.ycurr), Protects.ipBoth); DeleteFromAEL(eMaxPair); DeleteFromAEL(horzEdge); } }
//------------------------------------------------------------------------------ private void BuildIntersectList(Int64 botY, Int64 topY) { if ( m_ActiveEdges == null ) return; //prepare for sorting ... TEdge 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 ) { TEdge eNext = e.nextInSEL; IntPoint pt = new IntPoint(); if(e.tmpX > eNext.tmpX && IntersectPoint(e, eNext, ref pt)) { if (pt.Y > botY) { pt.Y = botY; pt.X = TopX(e, pt.Y); } 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 bool IsTopHorz(TEdge horzEdge, double XPos) { TEdge e = m_SortedEdges; while (e != null) { if ((XPos >= Math.Min(e.xcurr, e.xtop)) && (XPos <= Math.Max(e.xcurr, e.xtop))) return false; e = e.nextInSEL; } return true; }
//------------------------------------------------------------------------------ private static Int64 TopX(TEdge edge, Int64 currentY) { if (currentY == edge.ytop) return edge.xtop; return edge.xbot + Round(edge.dx *(currentY - edge.ybot)); }
//------------------------------------------------------------------------------ private TEdge GetNextInAEL(TEdge e, Direction Direction) { return Direction == Direction.dLeftToRight ? e.nextInAEL: e.prevInAEL; }
//------------------------------------------------------------------------------ private bool IntersectPoint(TEdge edge1, TEdge edge2, ref IntPoint ip) { double b1, b2; if (SlopesEqual(edge1, edge2, m_UseFullRange)) return false; else if (edge1.dx == 0) { ip.X = edge1.xbot; if (edge2.dx == horizontal) { ip.Y = edge2.ybot; } else { b2 = edge2.ybot - (edge2.xbot/edge2.dx); ip.Y = Round(ip.X/edge2.dx + b2); } } else if (edge2.dx == 0) { ip.X = edge2.xbot; if (edge1.dx == horizontal) { ip.Y = edge1.ybot; } else { b1 = edge1.ybot - (edge1.xbot/edge1.dx); ip.Y = Round(ip.X/edge1.dx + b1); } } else { b1 = edge1.xbot - edge1.ybot * edge1.dx; b2 = edge2.xbot - edge2.ybot * edge2.dx; b2 = (b2-b1)/(edge1.dx - edge2.dx); ip.Y = Round(b2); ip.X = Round(edge1.dx * b2 + b1); } return //can be *so close* to the top of one edge that the rounded Y equals one ytop ... (ip.Y == edge1.ytop && ip.Y >= edge2.ytop && edge1.tmpX > edge2.tmpX) || (ip.Y == edge2.ytop && ip.Y >= edge1.ytop && edge1.tmpX > edge2.tmpX) || (ip.Y > edge1.ytop && ip.Y > edge2.ytop); }
//------------------------------------------------------------------------------ private bool IsMinima(TEdge e) { return e != null && (e.prev.nextInLML != e) && (e.next.nextInLML != e); }
//------------------------------------------------------------------------------ internal bool SlopesEqual(TEdge e1, TEdge e2, bool UseFullRange) { if (UseFullRange) return Int128.Int128Mul(e1.ytop - e1.ybot, e2.xtop - e2.xbot) == Int128.Int128Mul(e1.xtop - e1.xbot, e2.ytop - e2.ybot); else return (Int64)(e1.ytop - e1.ybot) * (e2.xtop - e2.xbot) - (Int64)(e1.xtop - e1.xbot)*(e2.ytop - e2.ybot) == 0; }
//------------------------------------------------------------------------------ private bool IsMaxima(TEdge e, double Y) { return (e != null && e.ytop == Y && e.nextInLML == null); }
//------------------------------------------------------------------------------ private void SetDx(TEdge e) { if (e.ybot == e.ytop) e.dx = horizontal; else e.dx = (double)(e.xtop - e.xbot)/(e.ytop - e.ybot); }
//------------------------------------------------------------------------------ private bool IsIntermediate(TEdge e, double Y) { return (e.ytop == Y && e.nextInLML != null); }
//------------------------------------------------------------------------------ private void SwapX(TEdge 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 TEdge GetMaximaPair(TEdge e) { if (!IsMaxima(e.next, e.ytop) || (e.next.xtop != e.xtop)) return e.prev; else return e.next; }
//------------------------------------------------------------------------------ protected override void Reset() { base.Reset(); m_Scanbeam = null; m_ActiveEdges = null; m_SortedEdges = null; DisposeAllPolyPts(); LocalMinima lm = m_MinimaList; while (lm != null) { InsertScanbeam(lm.Y); InsertScanbeam(lm.leftBound.ytop); lm = lm.next; } }
//------------------------------------------------------------------------------ private void SetHoleState(TEdge e, OutRec outRec) { bool isHole = false; TEdge e2 = e.prevInAEL; while (e2 != null) { if (e2.outIdx >= 0) { isHole = !isHole; if (outRec.FirstLeft == null) outRec.FirstLeft = m_PolyOuts[e2.outIdx]; } e2 = e2.prevInAEL; } if (isHole) outRec.isHole = true; }