//------------------------------------------------------------------------------ private void AddOutPt(TEdge e, IntPoint pt) { bool ToFront = (e.side == EdgeSide.esLeft); if( e.outIdx < 0 ) { OutRec outRec = CreateOutRec(); m_PolyOuts.Add(outRec); outRec.idx = m_PolyOuts.Count -1; e.outIdx = outRec.idx; OutPt op = new OutPt(); outRec.pts = op; outRec.bottomPt = op; op.pt = pt; op.idx = outRec.idx; op.next = op; op.prev = op; SetHoleState(e, outRec); } else { OutRec outRec = m_PolyOuts[e.outIdx]; OutPt op = outRec.pts, op2, opBot; if (ToFront && PointsEqual(pt, op.pt) || (!ToFront && PointsEqual(pt, op.prev.pt))) return; if ((e.side | outRec.sides) != outRec.sides) { //check for 'rounding' artefacts ... if (outRec.sides == EdgeSide.esNeither && pt.Y == op.pt.Y) if (ToFront) { if (pt.X == op.pt.X + 1) return; //ie wrong side of bottomPt } else if (pt.X == op.pt.X - 1) return; //ie wrong side of bottomPt outRec.sides = (EdgeSide)(outRec.sides | e.side); if (outRec.sides == EdgeSide.esBoth) { //A vertex from each side has now been added. //Vertices of one side of an output polygon are quite commonly close to //or even 'touching' edges of the other side of the output polygon. //Very occasionally vertices from one side can 'cross' an edge on the //the other side. The distance 'crossed' is always less that a unit //and is purely an artefact of coordinate rounding. Nevertheless, this //results in very tiny self-intersections. Because of the way //orientation is calculated, even tiny self-intersections can cause //the Orientation function to return the wrong result. Therefore, it's //important to ensure that any self-intersections close to BottomPt are //detected and removed before orientation is assigned. if (ToFront) { opBot = outRec.pts; op2 = opBot.next; //op2 == right side if (opBot.pt.Y != op2.pt.Y && opBot.pt.Y != pt.Y && ((opBot.pt.X - pt.X) / (opBot.pt.Y - pt.Y) < (opBot.pt.X - op2.pt.X) / (opBot.pt.Y - op2.pt.Y))) outRec.bottomFlag = opBot; } else { opBot = outRec.pts.prev; op2 = opBot.next; //op2 == left side if (opBot.pt.Y != op2.pt.Y && opBot.pt.Y != pt.Y && ((opBot.pt.X - pt.X) / (opBot.pt.Y - pt.Y) > (opBot.pt.X - op2.pt.X) / (opBot.pt.Y - op2.pt.Y))) outRec.bottomFlag = opBot; } } } op2 = new OutPt(); op2.pt = pt; op2.idx = outRec.idx; if (op2.pt.Y == outRec.bottomPt.pt.Y && op2.pt.X < outRec.bottomPt.pt.X) outRec.bottomPt = op2; op2.next = op; op2.prev = op.prev; op2.prev.next = op2; op.prev = op2; if (ToFront) outRec.pts = op2; } }
//------------------------------------------------------------------------------ internal bool PointIsVertex(IntPoint pt, OutPt pp) { OutPt pp2 = pp; do { if (PointsEqual(pp2.pt, pt)) return true; pp2 = pp2.next; } while (pp2 != pp); return false; }
//------------------------------------------------------------------------------ protected bool SlopesEqual(IntPoint pt1, IntPoint pt2, IntPoint pt3, IntPoint pt4, bool UseFullRange) { if (UseFullRange) return Int128.Int128Mul(pt1.Y - pt2.Y, pt3.X - pt4.X) == Int128.Int128Mul(pt1.X - pt2.X, pt3.Y - pt4.Y); else return (Int64)(pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (Int64)(pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0; }
//------------------------------------------------------------------------------ internal void DoSquare(double mul) { IntPoint pt1 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_k].X * delta), (Int64)Round(pts[m_i][m_j].Y + normals[m_k].Y * delta)); IntPoint pt2 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_j].X * delta), (Int64)Round(pts[m_i][m_j].Y + normals[m_j].Y * delta)); if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * delta >= 0) { double a1 = Math.Atan2(normals[m_k].Y, normals[m_k].X); double a2 = Math.Atan2(-normals[m_j].Y, -normals[m_j].X); a1 = Math.Abs(a2 - a1); if (a1 > Math.PI) a1 = Math.PI * 2 - a1; double dx = Math.Tan((Math.PI - a1) / 4) * Math.Abs(delta * mul); pt1 = new IntPoint((Int64)(pt1.X - normals[m_k].Y * dx), (Int64)(pt1.Y + normals[m_k].X * dx)); AddPoint(pt1); pt2 = new IntPoint((Int64)(pt2.X + normals[m_j].Y * dx), (Int64)(pt2.Y - normals[m_j].X * dx)); AddPoint(pt2); } else { AddPoint(pt1); AddPoint(pts[m_i][m_j]); AddPoint(pt2); } }
//------------------------------------------------------------------------------ internal void DoRound() { IntPoint pt1 = new IntPoint(Round(pts[m_i][m_j].X + normals[m_k].X * delta), Round(pts[m_i][m_j].Y + normals[m_k].Y * delta)); IntPoint pt2 = new IntPoint(Round(pts[m_i][m_j].X + normals[m_j].X * delta), Round(pts[m_i][m_j].Y + normals[m_j].Y * delta)); AddPoint(pt1); //round off reflex angles (ie > 180 deg) unless almost flat (ie < 10deg). //cross product normals < 0 . angle > 180 deg. //dot product normals == 1 . no angle if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * delta >= 0) { if ((normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y) < 0.985) { double a1 = Math.Atan2(normals[m_k].Y, normals[m_k].X); double a2 = Math.Atan2(normals[m_j].Y, normals[m_j].X); if (delta > 0 && a2 < a1) a2 += Math.PI * 2; else if (delta < 0 && a2 > a1) a2 -= Math.PI * 2; Polygon arc = BuildArc(pts[m_i][m_j], a1, a2, delta); for (int m = 0; m < arc.Count; m++) AddPoint(arc[m]); } } else AddPoint(pts[m_i][m_j]); AddPoint(pt2); }
//---------------------------------------------------------------------- private void JoinCommonEdges(bool fixHoleLinkages) { for (int i = 0; i < m_Joins.Count; i++) { JoinRec j = m_Joins[i]; OutRec outRec1 = m_PolyOuts[j.poly1Idx]; OutPt pp1a = outRec1.pts; OutRec outRec2 = m_PolyOuts[j.poly2Idx]; OutPt pp2a = outRec2.pts; IntPoint pt1 = new IntPoint(j.pt2a); IntPoint pt2 = new IntPoint(j.pt2b); IntPoint pt3 = new IntPoint(j.pt1a); IntPoint pt4 = new IntPoint(j.pt1b); if (!FindSegment(ref pp1a, ref pt1, ref pt2)) continue; if (j.poly1Idx == j.poly2Idx) { //we're searching the same polygon for overlapping segments so //segment 2 mustn't be the same as segment 1 ... pp2a = pp1a.next; if (!FindSegment(ref pp2a, ref pt3, ref pt4) || (pp2a == pp1a)) continue; } else if (!FindSegment(ref pp2a, ref pt3, ref pt4)) continue; if (!GetOverlapSegment(pt1, pt2, pt3, pt4, ref pt1, ref pt2)) continue; OutPt p1, p2, p3, p4; OutPt prev = pp1a.prev; //get p1 & p2 polypts - the overlap start & endpoints on poly1 if (PointsEqual(pp1a.pt, pt1)) p1 = pp1a; else if (PointsEqual(prev.pt, pt1)) p1 = prev; else p1 = InsertPolyPtBetween(pp1a, prev, pt1); if (PointsEqual(pp1a.pt, pt2)) p2 = pp1a; else if (PointsEqual(prev.pt, pt2)) p2 = prev; else if ((p1 == pp1a) || (p1 == prev)) p2 = InsertPolyPtBetween(pp1a, prev, pt2); else if (Pt3IsBetweenPt1AndPt2(pp1a.pt, p1.pt, pt2)) p2 = InsertPolyPtBetween(pp1a, p1, pt2); else p2 = InsertPolyPtBetween(p1, prev, pt2); //get p3 & p4 polypts - the overlap start & endpoints on poly2 prev = pp2a.prev; if (PointsEqual(pp2a.pt, pt1)) p3 = pp2a; else if (PointsEqual(prev.pt, pt1)) p3 = prev; else p3 = InsertPolyPtBetween(pp2a, prev, pt1); if (PointsEqual(pp2a.pt, pt2)) p4 = pp2a; else if (PointsEqual(prev.pt, pt2)) p4 = prev; else if ((p3 == pp2a) || (p3 == prev)) p4 = InsertPolyPtBetween(pp2a, prev, pt2); else if (Pt3IsBetweenPt1AndPt2(pp2a.pt, p3.pt, pt2)) p4 = InsertPolyPtBetween(pp2a, p3, pt2); else p4 = InsertPolyPtBetween(p3, prev, pt2); //p1.pt should equal p3.pt and p2.pt should equal p4.pt here, so ... //join p1 to p3 and p2 to p4 ... if (p1.next == p2 && p3.prev == p4) { p1.next = p3; p3.prev = p1; p2.prev = p4; p4.next = p2; } else if (p1.prev == p2 && p3.next == p4) { p1.prev = p3; p3.next = p1; p2.next = p4; p4.prev = p2; } else continue; //an orientation is probably wrong if (j.poly2Idx == j.poly1Idx) { //instead of joining two polygons, we've just created a new one by //splitting one polygon into two. outRec1.pts = GetBottomPt(p1); outRec1.bottomPt = outRec1.pts; outRec1.bottomPt.idx = outRec1.idx; outRec2 = CreateOutRec(); m_PolyOuts.Add(outRec2); outRec2.idx = m_PolyOuts.Count - 1; j.poly2Idx = outRec2.idx; outRec2.pts = GetBottomPt(p2); outRec2.bottomPt = outRec2.pts; outRec2.bottomPt.idx = outRec2.idx; if (PointInPolygon(outRec2.pts.pt, outRec1.pts, m_UseFullRange)) { //outRec1 is contained by outRec2 ... outRec2.isHole = !outRec1.isHole; outRec2.FirstLeft = outRec1; if (outRec2.isHole == Orientation(outRec2, m_UseFullRange)) ReversePolyPtLinks(outRec2.pts); } else if (PointInPolygon(outRec1.pts.pt, outRec2.pts, m_UseFullRange)) { //outRec2 is contained by outRec1 ... outRec2.isHole = outRec1.isHole; outRec1.isHole = !outRec2.isHole; outRec2.FirstLeft = outRec1.FirstLeft; outRec1.FirstLeft = outRec2; if (outRec1.isHole == Orientation(outRec1, m_UseFullRange)) ReversePolyPtLinks(outRec1.pts); //make sure any contained holes now link to the correct polygon ... if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2); } else { outRec2.isHole = outRec1.isHole; outRec2.FirstLeft = outRec1.FirstLeft; //make sure any contained holes now link to the correct polygon ... if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2); } //now fixup any subsequent m_Joins that match this polygon for (int k = i + 1; k < m_Joins.Count; k++) { JoinRec j2 = m_Joins[k]; if (j2.poly1Idx == j.poly1Idx && PointIsVertex(j2.pt1a, p2)) j2.poly1Idx = j.poly2Idx; if (j2.poly2Idx == j.poly1Idx && PointIsVertex(j2.pt2a, p2)) j2.poly2Idx = j.poly2Idx; } //now cleanup redundant edges too ... FixupOutPolygon(outRec1); FixupOutPolygon(outRec2); } else { //joined 2 polygons together ... //make sure any holes contained by outRec2 now link to outRec1 ... if (fixHoleLinkages) CheckHoleLinkages2(outRec1, outRec2); //now cleanup redundant edges too ... FixupOutPolygon(outRec1); if (outRec1.pts != null) { outRec1.isHole = !Orientation(outRec1, m_UseFullRange); if (outRec1.isHole && outRec1.FirstLeft == null) outRec1.FirstLeft = outRec2.FirstLeft; } //delete the obsolete pointer ... int OKIdx = outRec1.idx; int ObsoleteIdx = outRec2.idx; outRec2.pts = null; outRec2.bottomPt = null; outRec2.AppendLink = outRec1; //now fixup any subsequent joins that match this polygon for (int k = i + 1; k < m_Joins.Count; k++) { JoinRec j2 = m_Joins[k]; if (j2.poly1Idx == ObsoleteIdx) j2.poly1Idx = OKIdx; if (j2.poly2Idx == ObsoleteIdx) j2.poly2Idx = OKIdx; } } } }
//------------------------------------------------------------------------------ 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 double GetDx(IntPoint pt1, IntPoint pt2) { if (pt1.Y == pt2.Y) return horizontal; else return (double)(pt2.X - pt1.X) / (double)(pt2.Y - pt1.Y); }
//------------------------------------------------------------------------------ private void DoBothEdges(TEdge edge1, TEdge edge2, IntPoint pt) { AddOutPt(edge1, pt); AddOutPt(edge2, pt); SwapSides(edge1, edge2); SwapPolyIndexes(edge1, edge2); }
//------------------------------------------------------------------------------ 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 OutPt InsertPolyPtBetween(OutPt p1, OutPt p2, IntPoint pt) { OutPt result = new OutPt(); result.pt = pt; 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; }
//------------------------------------------------------------------------------ private bool FindSegment(ref OutPt pp, ref IntPoint pt1, ref IntPoint pt2) { if (pp == null) return false; OutPt pp2 = pp; IntPoint pt1a = new IntPoint(pt1); IntPoint pt2a = new IntPoint(pt2); do { if (SlopesEqual(pt1a, pt2a, pp.pt, pp.prev.pt, true) && SlopesEqual(pt1a, pt2a, pp.pt, true) && GetOverlapSegment(pt1a, pt2a, pp.pt, pp.prev.pt, ref pt1, ref pt2)) return true; pp = pp.next; } while (pp != pp2); return false; }
//------------------------------------------------------------------------------ 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 void SwapPoints(ref IntPoint pt1, ref IntPoint pt2) { IntPoint tmp = pt1; pt1 = pt2; pt2 = tmp; }
//------------------------------------------------------------------------------ private bool Orientation(OutRec outRec, bool UseFull64BitRange) { //first make sure bottomPt is correctly assigned ... OutPt opBottom = outRec.pts, op = outRec.pts.next; while (op != outRec.pts) { if (op.pt.Y >= opBottom.pt.Y) { if (op.pt.Y > opBottom.pt.Y || op.pt.X < opBottom.pt.X) opBottom = op; } op = op.next; } outRec.bottomPt = opBottom; opBottom.idx = outRec.idx; op = opBottom; //find vertices either side of bottomPt (skipping duplicate points) .... OutPt opPrev = op.prev; OutPt opNext = op.next; while (op != opPrev && PointsEqual(op.pt, opPrev.pt)) opPrev = opPrev.prev; while (op != opNext && PointsEqual(op.pt, opNext.pt)) opNext = opNext.next; IntPoint vec1 = new IntPoint(op.pt.X - opPrev.pt.X, op.pt.Y - opPrev.pt.Y); IntPoint vec2 = new IntPoint(opNext.pt.X - op.pt.X, opNext.pt.Y - op.pt.Y); if (UseFull64BitRange) { Int128 cross = Int128.Int128Mul(vec1.X, vec2.Y) - Int128.Int128Mul(vec2.X, vec1.Y); return !cross.IsNegative(); } else return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0; }
//------------------------------------------------------------------------------ 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); }
public IntPoint(IntPoint pt) { this.X = pt.X; this.Y = pt.Y; }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ // OffsetPolygon functions ... //------------------------------------------------------------------------------ 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 + Round(Math.Cos(a) * r), pt.Y + Round(Math.Sin(a) * r))); a += da; } return result; }
//------------------------------------------------------------------------------ private Int64 TopX(IntPoint pt1, IntPoint pt2, Int64 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 (Int64)Round(pt1.X + (currentY - pt1.Y) * q); } }
//------------------------------------------------------------------------------ internal void AddPoint(IntPoint pt) { int len = currentPoly.Count; if (len == currentPoly.Capacity) currentPoly.Capacity = len + buffLength; currentPoly.Add(pt); }
//------------------------------------------------------------------------------ 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; } }
//------------------------------------------------------------------------------ internal void DoMiter() { if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * delta >= 0) { double q = delta / m_R; AddPoint(new IntPoint((Int64)Round(pts[m_i][m_j].X + (normals[m_k].X + normals[m_j].X) * q), (Int64)Round(pts[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); } else { IntPoint pt1 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_k].X * delta), (Int64)Round(pts[m_i][m_j].Y + normals[m_k].Y * delta)); IntPoint pt2 = new IntPoint((Int64)Round(pts[m_i][m_j].X + normals[m_j].X * delta), (Int64)Round(pts[m_i][m_j].Y + normals[m_j].Y * delta)); AddPoint(pt1); AddPoint(pts[m_i][m_j]); AddPoint(pt2); } }
//------------------------------------------------------------------------------ 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); }
//------------------------------------------------------------------------------ protected static bool PointsEqual(IntPoint pt1, IntPoint pt2) { return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); }
//------------------------------------------------------------------------------ private void ProcessEdgesAtTopOfScanbeam(Int64 topY) { TEdge e = m_ActiveEdges; while( e != null ) { //1. process maxima, treating them as if they're 'bent' horizontal edges, // but exclude maxima with horizontal edges. nb: e can't be a horizontal. if( IsMaxima(e, topY) && GetMaximaPair(e).dx != horizontal ) { //'e' might be removed from AEL, as may any following edges so ... TEdge ePrior = e.prevInAEL; DoMaxima(e, topY); if( ePrior == null ) e = m_ActiveEdges; else e = ePrior.nextInAEL; } else { //2. promote horizontal edges, otherwise update xcurr and ycurr ... if( IsIntermediate(e, topY) && e.nextInLML.dx == horizontal ) { if (e.outIdx >= 0) { AddOutPt(e, new IntPoint(e.xtop, e.ytop)); for (int i = 0; i < m_HorizJoins.Count; ++i) { IntPoint pt = new IntPoint(), pt2 = new IntPoint(); HorzJoinRec hj = m_HorizJoins[i]; if (GetOverlapSegment(new IntPoint(hj.edge.xbot, hj.edge.ybot), new IntPoint(hj.edge.xtop, hj.edge.ytop), new IntPoint(e.nextInLML.xbot, e.nextInLML.ybot), new IntPoint(e.nextInLML.xtop, e.nextInLML.ytop), ref pt, ref pt2)) AddJoin(hj.edge, e.nextInLML, hj.savedIdx, e.outIdx); } AddHorzJoin(e.nextInLML, e.outIdx); } UpdateEdgeIntoAEL(ref e); AddEdgeToSEL(e); } else { //this just simplifies horizontal processing ... e.xcurr = TopX( e, topY ); e.ycurr = topY; } e = e.nextInAEL; } } //3. Process horizontals at the top of the scanbeam ... ProcessHorizontals(); //4. Promote intermediate vertices ... e = m_ActiveEdges; while( e != null ) { if( IsIntermediate( e, topY ) ) { if (e.outIdx >= 0) AddOutPt(e, new IntPoint(e.xtop, e.ytop)); UpdateEdgeIntoAEL(ref e); //if output polygons share an edge, they'll need joining later ... if (e.outIdx >= 0 && e.prevInAEL != null && e.prevInAEL.outIdx >= 0 && e.prevInAEL.xcurr == e.xbot && e.prevInAEL.ycurr == e.ybot && SlopesEqual(new IntPoint(e.xbot, e.ybot), new IntPoint(e.xtop, e.ytop), new IntPoint(e.xbot, e.ybot), new IntPoint(e.prevInAEL.xtop, e.prevInAEL.ytop), m_UseFullRange)) { AddOutPt(e.prevInAEL, new IntPoint(e.xbot, e.ybot)); AddJoin(e, e.prevInAEL, -1, -1); } else if (e.outIdx >= 0 && e.nextInAEL != null && e.nextInAEL.outIdx >= 0 && e.nextInAEL.ycurr > e.nextInAEL.ytop && e.nextInAEL.ycurr <= e.nextInAEL.ybot && e.nextInAEL.xcurr == e.xbot && e.nextInAEL.ycurr == e.ybot && SlopesEqual(new IntPoint(e.xbot, e.ybot), new IntPoint(e.xtop, e.ytop), new IntPoint(e.xbot, e.ybot), new IntPoint(e.nextInAEL.xtop, e.nextInAEL.ytop), m_UseFullRange)) { AddOutPt(e.nextInAEL, new IntPoint(e.xbot, e.ybot)); AddJoin(e, e.nextInAEL, -1, -1); } } e = e.nextInAEL; } }
//------------------------------------------------------------------------------ internal bool PointInPolygon(IntPoint pt, OutPt pp, bool UseFulllongRange) { OutPt pp2 = pp; bool result = false; if (UseFulllongRange) { do { if ((((pp2.pt.Y <= pt.Y) && (pt.Y < pp2.prev.pt.Y)) || ((pp2.prev.pt.Y <= pt.Y) && (pt.Y < pp2.pt.Y))) && new Int128(pt.X - pp2.pt.X) < Int128.Int128Mul(pp2.prev.pt.X - pp2.pt.X, pt.Y - pp2.pt.Y) / new Int128(pp2.prev.pt.Y - pp2.pt.Y)) result = !result; pp2 = pp2.next; } while (pp2 != pp); } else { do { if ((((pp2.pt.Y <= pt.Y) && (pt.Y < pp2.prev.pt.Y)) || ((pp2.prev.pt.Y <= pt.Y) && (pt.Y < pp2.pt.Y))) && (pt.X - pp2.pt.X < (pp2.prev.pt.X - pp2.pt.X) * (pt.Y - pp2.pt.Y) / (pp2.prev.pt.Y - pp2.pt.Y))) result = !result; pp2 = pp2.next; } while (pp2 != pp); } return result; }
//------------------------------------------------------------------------------ public static bool Orientation(Polygon poly) { int highI = poly.Count -1; if (highI < 2) return false; int j = 0, jplus, jminus; for (int i = 0; i <= highI; ++i) { if (poly[i].Y < poly[j].Y) continue; if ((poly[i].Y > poly[j].Y || poly[i].X < poly[j].X)) j = i; }; if (j == highI) jplus = 0; else jplus = j +1; if (j == 0) jminus = highI; else jminus = j -1; //get cross product of vectors of the edges adjacent to highest point ... IntPoint vec1 = new IntPoint(poly[j].X - poly[jminus].X, poly[j].Y - poly[jminus].Y); IntPoint vec2 = new IntPoint(poly[jplus].X - poly[j].X, poly[jplus].Y - poly[j].Y); if (Math.Abs(vec1.X) > loRange || Math.Abs(vec1.Y) > loRange || Math.Abs(vec2.X) > loRange || Math.Abs(vec2.Y) > loRange) { if (Math.Abs(vec1.X) > hiRange || Math.Abs(vec1.Y) > hiRange || Math.Abs(vec2.X) > hiRange || Math.Abs(vec2.Y) > hiRange) throw new ClipperException("Coordinate exceeds range bounds."); Int128 cross = Int128.Int128Mul(vec1.X, vec2.Y) - Int128.Int128Mul(vec2.X, vec1.Y); return !cross.IsNegative(); } else return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0; }
//------------------------------------------------------------------------------ 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 void AddLocalMinPoly(TEdge e1, TEdge e2, IntPoint pt) { TEdge e, prevE; if (e2.dx == horizontal || (e1.dx > e2.dx)) { AddOutPt(e1, pt); e2.outIdx = e1.outIdx; e1.side = EdgeSide.esLeft; e2.side = EdgeSide.esRight; e = e1; if (e.prevInAEL == e2) prevE = e2.prevInAEL; else prevE = e.prevInAEL; } else { AddOutPt(e2, pt); e1.outIdx = e2.outIdx; e1.side = EdgeSide.esRight; e2.side = EdgeSide.esLeft; e = e2; if (e.prevInAEL == e1) prevE = e1.prevInAEL; else prevE = e.prevInAEL; } if (prevE != null && prevE.outIdx >= 0 && (TopX(prevE, pt.Y) == TopX(e, pt.Y)) && SlopesEqual(e, prevE, m_UseFullRange)) AddJoin(e, prevE, -1, -1); }