//------------------------------------------------------------------------------ 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 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 IntersectEdges(TEdge4 e1, TEdge4 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 (IsNonZeroFillType(e1)) { 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 { int oldE1WindCnt = e1.windCnt; e1.windCnt = e2.windCnt; e2.windCnt = oldE1WindCnt; } } else { if (IsNonZeroFillType(e2)) e1.windCnt2 += e2.windDelta; else e1.windCnt2 = (e1.windCnt2 == 0) ? 1 : 0; if (IsNonZeroFillType(e1)) e2.windCnt2 -= e1.windDelta; else e2.windCnt2 = (e2.windCnt2 == 0) ? 1 : 0; } if (e1Contributing && e2contributing) { if (e1stops || e2stops || Math.Abs(e1.windCnt) > 1 || Math.Abs(e2.windCnt) > 1 || (e1.polyType != e2.polyType && m_ClipType != ClipType.ctXor)) AddLocalMaxPoly(e1, e2, pt); else DoBothEdges(e1, e2, pt); } else if (e1Contributing) { if (m_ClipType == ClipType.ctIntersection) { if ((e2.polyType == PolyType.ptSubject || e2.windCnt2 != 0) && Math.Abs(e2.windCnt) < 2) DoEdge1(e1, e2, pt); } else { if (Math.Abs(e2.windCnt) < 2) DoEdge1(e1, e2, pt); } } else if (e2contributing) { if (m_ClipType == ClipType.ctIntersection) { if ((e1.polyType == PolyType.ptSubject || e1.windCnt2 != 0) && Math.Abs(e1.windCnt) < 2) DoEdge2(e1, e2, pt); } else { if (Math.Abs(e1.windCnt) < 2) DoEdge2(e1, e2, pt); } } else if (Math.Abs(e1.windCnt) < 2 && Math.Abs(e2.windCnt) < 2) { //neither edge is currently contributing ... if (e1.polyType != e2.polyType && !e1stops && !e2stops && Math.Abs(e1.windCnt) < 2 && Math.Abs(e2.windCnt) < 2) AddLocalMinPoly(e1, e2, pt); else if (Math.Abs(e1.windCnt) == 1 && Math.Abs(e2.windCnt) == 1) switch (m_ClipType) { case ClipType.ctIntersection: { if (Math.Abs(e1.windCnt2) > 0 && Math.Abs(e2.windCnt2) > 0) AddLocalMinPoly(e1, e2, pt); break; } case ClipType.ctUnion: { if (e1.windCnt2 == 0 && e2.windCnt2 == 0) AddLocalMinPoly(e1, e2, pt); break; } case ClipType.ctDifference: { if ((e1.polyType == PolyType.ptClip && e2.polyType == PolyType.ptClip && e1.windCnt2 != 0 && e2.windCnt2 != 0) || (e1.polyType == PolyType.ptSubject && e2.polyType == PolyType.ptSubject && e1.windCnt2 == 0 && e2.windCnt2 == 0)) AddLocalMinPoly(e1, e2, pt); break; } case ClipType.ctXor: { AddLocalMinPoly(e1, e2, pt); break; } } else if (Math.Abs(e1.windCnt) < 2 && Math.Abs(e2.windCnt) < 2) 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); }
//------------------------------------------------------------------------------ internal void SwapPoints(ref IntPoint pt1, ref IntPoint pt2) { IntPoint tmp = pt1; pt1 = pt2; pt2 = tmp; }
//------------------------------------------------------------------------------ private bool FindSegment(ref PolyPt pp, IntPoint pt1, IntPoint pt2) { if (pp == null) return false; PolyPt pp2 = pp; do { if (PointsEqual(pp.pt, pt1) && (PointsEqual(pp.next.pt, pt2) || PointsEqual(pp.prev.pt, pt2))) return true; pp = pp.next; } while (pp != pp2); return false; }
//------------------------------------------------------------------------------ private void InsertLocalMinimaIntoAEL(int botY) { while( m_CurrentLM != null && ( m_CurrentLM.Y == botY ) ) { TEdge4 lb = m_CurrentLM.leftBound; TEdge4 rb = m_CurrentLM.rightBound; InsertEdgeIntoAEL( lb ); InsertScanbeam( lb.ytop ); InsertEdgeIntoAEL( rb ); if ( IsNonZeroFillType( lb) ) rb.windDelta = -lb.windDelta; else { lb.windDelta = 1; rb.windDelta = 1; } SetWindingCount( lb ); rb.windCnt = lb.windCnt; rb.windCnt2 = lb.windCnt2; if( rb.dx == horizontal ) { //nb: only rightbounds can have a horizontal bottom edge AddEdgeToSEL( rb ); InsertScanbeam( rb.nextInLML.ytop ); } else InsertScanbeam( rb.ytop ); if( IsContributing(lb) ) AddLocalMinPoly(lb, rb, new IntPoint(lb.xcurr, m_CurrentLM.Y)); //if output polygons share an edge, they'll need joining later ... if (lb.outIdx >= 0 && lb.prevInAEL != null && lb.prevInAEL.outIdx >= 0 && lb.prevInAEL.xcurr == lb.xbot && SlopesEqual(lb, lb.prevInAEL)) AddJoin(lb, lb.prevInAEL); //if any output polygons share an edge, they'll need joining later ... if (rb.outIdx >= 0) { if (rb.dx == horizontal) { for (int i = 0; i < m_HorizJoins.Count; i++) { IntPoint pt = new IntPoint(), pt2 = new IntPoint(); //used as dummy params. HorzJoinRec hj = m_HorizJoins[i]; //if horizontals rb and hj.edge overlap, flag for joining later ... if (GetOverlapSegment(new IntPoint(hj.edge.xbot, hj.edge.ybot), new IntPoint(hj.edge.xtop, hj.edge.ytop), new IntPoint(rb.xbot, rb.ybot), new IntPoint(rb.xtop, rb.ytop), ref pt, ref pt2)) AddJoin(hj.edge, rb, hj.savedIdx); } } } if( lb.nextInAEL != rb ) { TEdge4 e = lb.nextInAEL; IntPoint pt = new IntPoint(lb.xcurr, lb.ycurr); while( e != rb ) { if(e == null) throw new ClipperException("InsertLocalMinimaIntoAEL: missing rightbound!"); //nb: For calculating winding counts etc, IntersectEdges() assumes //that param1 will be to the right of param2 ABOVE the intersection ... IntersectEdges( rb , e , pt , Protects.ipNone); //order important here e = e.nextInAEL; } } PopLocalMinima(); } }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ protected bool PointsEqual(IntPoint pt1, IntPoint pt2) { return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); }
//------------------------------------------------------------------------------ protected bool SlopesEqual(IntPoint pt1, IntPoint pt2, IntPoint pt3) { if (pt1.Y == pt2.Y) return (pt2.Y == pt3.Y); else if (pt2.Y == pt3.Y) return false; else return (Int64)(pt1.Y-pt2.Y)*(pt2.X-pt3.X) - (Int64)(pt1.X-pt2.X)*(pt2.Y-pt3.Y) == 0; }
//------------------------------------------------------------------------------ private int TopX(IntPoint pt1, IntPoint pt2, int currentY) { //preconditions: pt1.Y <> pt2.Y and pt1.Y > pt2.Y if (currentY >= pt1.Y) return pt1.X; else if (currentY == pt2.Y) return pt2.X; else if (pt1.X == pt2.X) return pt1.X; else { double q = (pt1.X-pt2.X)/(pt1.Y-pt2.Y); return (int)(pt1.X + (currentY - pt1.Y) *q); } }
//---------------------------------------------------------------------- private void JoinCommonEdges() { for (int i = 0; i < m_Joins.Count; i++) { PolyPt pp1a, pp1b, pp2a, pp2b; IntPoint pt1 = new IntPoint(), pt2 = new IntPoint(); JoinRec j = m_Joins[i]; if (j.poly1Idx < 0 || j.poly2Idx < 0) throw new ClipperException("JoinCommonEdges error"); pp1a = m_PolyPts[j.poly1Idx]; pp2a = m_PolyPts[j.poly2Idx]; if (FindSegment(ref pp1a, j.pt1a, j.pt1b) && FindSegment(ref pp2a, j.pt2a, j.pt2b)) { if (PointsEqual(pp1a.next.pt, j.pt1b)) pp1b = pp1a.next; else pp1b = pp1a.prev; if (PointsEqual(pp2a.next.pt, j.pt2b)) pp2b = pp2a.next; else pp2b = pp2a.prev; if (j.poly1Idx != j.poly2Idx && GetOverlapSegment(pp1a.pt, pp1b.pt, pp2a.pt, pp2b.pt, ref pt1, ref pt2)) { PolyPt p1, p2, p3, p4; //get p1 & p2 polypts - the overlap start & endpoints on poly1 Position pos1 = GetPosition(pp1a.pt, pp1b.pt, pt1); if (pos1 == Position.pFirst) p1 = pp1a; else if (pos1 == Position.pSecond) p1 = pp1b; else p1 = InsertPolyPtBetween(pp1a, pp1b, pt1); Position pos2 = GetPosition(pp1a.pt, pp1b.pt, pt2); if (pos2 == Position.pMiddle) { if (pos1 == Position.pMiddle) { if (Pt3IsBetweenPt1AndPt2(pp1a.pt, p1.pt, pt2)) p2 = InsertPolyPtBetween(pp1a, p1, pt2); else p2 = InsertPolyPtBetween(p1, pp1b, pt2); } else if (pos2 == Position.pFirst) p2 = pp1a; else p2 = pp1b; } else if (pos2 == Position.pFirst) p2 = pp1a; else p2 = pp1b; //get p3 & p4 polypts - the overlap start & endpoints on poly2 pos1 = GetPosition(pp2a.pt, pp2b.pt, pt1); if (pos1 == Position.pFirst) p3 = pp2a; else if (pos1 == Position.pSecond) p3 = pp2b; else p3 = InsertPolyPtBetween(pp2a, pp2b, pt1); pos2 = GetPosition(pp2a.pt, pp2b.pt, pt2); if (pos2 == Position.pMiddle) { if (pos1 == Position.pMiddle) { if (Pt3IsBetweenPt1AndPt2(pp2a.pt, p3.pt, pt2)) p4 = InsertPolyPtBetween(pp2a, p3, pt2); else p4 = InsertPolyPtBetween(p3, pp2b, pt2); } else if (pos2 == Position.pFirst) p4 = pp2a; else p4 = pp2b; } else if (pos2 == Position.pFirst) p4 = pp2a; else p4 = pp2b; if (p1.next == p2) p1.next = p3; else p1.prev = p3; if (p3.next == p4) p3.next = p1; else p3.prev = p1; if (p2.next == p1) p2.next = p4; else p2.prev = p4; if (p4.next == p3) p4.next = p2; else p4.prev = p2; m_PolyPts[j.poly2Idx] = null; } } } }
//------------------------------------------------------------------------------ private bool IntersectPoint(TEdge4 edge1, TEdge4 edge2, ref IntPoint ip) { double b1, b2; if (SlopesEqual(edge1, edge2)) 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 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; } }
//------------------------------------------------------------------------------ internal static Polygon BuildArc(IntPoint pt, double a1, double a2, double r) { int steps = Math.Max(6, (int)(Math.Sqrt(Math.Abs(r)) * Math.Abs(a2 - a1))); Polygon result = new Polygon(steps); int n = steps - 1; double da = (a2 - a1) / n; double a = a1; for (int i = 0; i < steps; ++i) { result.Add(new IntPoint(pt.X + (int)(Math.Cos(a)*r), pt.Y + (int)(Math.Sin(a)*r))); a += da; } return result; }
//------------------------------------------------------------------------------ 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; } }
//------------------------------------------------------------------------------ internal static DoublePoint GetUnitNormal(IntPoint pt1, IntPoint pt2) { double dx = ( pt2.X - pt1.X ); double dy = ( pt2.Y - pt1.Y ); if( ( dx == 0 ) && ( dy == 0 ) ) return new DoublePoint(); double f = 1 *1.0/ Math.Sqrt( dx*dx + dy*dy ); dx *= f; dy *= f; return new DoublePoint(dy, -dx); }
//------------------------------------------------------------------------------ private void DoEdge2(TEdge4 edge1, TEdge4 edge2, IntPoint pt) { AddPolyPt(edge2, pt); SwapSides(edge1, edge2); SwapPolyIndexes(edge1, edge2); }
//------------------------------------------------------------------------------ internal Position GetPosition(IntPoint pt1, IntPoint pt2, IntPoint pt) { if (PointsEqual(pt1, pt)) return Position.pFirst; else if (PointsEqual(pt2, pt)) return Position.pSecond; else return Position.pMiddle; }
//------------------------------------------------------------------------------ private bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, IntPoint pt2b, ref IntPoint pt1, ref IntPoint pt2) { //precondition: segments are colinear. if ( pt1a.Y == pt1b.Y || Math.Abs((pt1a.X - pt1b.X)/(pt1a.Y - pt1b.Y)) > 1 ) { if (pt1a.X > pt1b.X) SwapPoints(ref pt1a, ref pt1b); if (pt2a.X > pt2b.X) SwapPoints(ref pt2a, ref pt2b); if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; return pt1.X < pt2.X; } else { if (pt1a.Y < pt1b.Y) SwapPoints(ref pt1a, ref pt1b); if (pt2a.Y < pt2b.Y) SwapPoints(ref pt2a, ref pt2b); if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; return pt1.Y > pt2.Y; } }
//------------------------------------------------------------------------------ internal bool Pt3IsBetweenPt1AndPt2(IntPoint pt1, IntPoint pt2, IntPoint pt3) { if (PointsEqual(pt1, pt3) || PointsEqual(pt2, pt3)) return true; else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X); else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y); }
//--------------------------------------------------------------------- private IntPoint GenerateRandomPoint(int l, int t, int r, int b, Random rand) { IntPoint newPt = new IntPoint(); newPt.X = (rand.Next(r / 10) * 10 + l + 10) * scale; newPt.Y = (rand.Next(b / 10) * 10 + t + 10) * scale; return newPt; }
//------------------------------------------------------------------------------ private PolyPt InsertPolyPtBetween(PolyPt p1, PolyPt p2, IntPoint pt) { PolyPt result = new PolyPt(); result.pt = pt; result.isHole = p1.isHole; if (p2 == p1.next) { p1.next = result; p2.prev = result; result.next = p2; result.prev = p1; } else { p2.next = result; p1.prev = result; result.next = p1; result.prev = p2; } return result; }