//------------------------------------------------------------------------------ private void InitEdge(TEdge e, TEdge eNext, TEdge ePrev, IntPoint pt) { e.Next = eNext; e.Prev = ePrev; e.Curr = pt; e.OutIdx = Unassigned; }
//------------------------------------------------------------------------------ #endif internal bool Pt2IsBetweenPt1AndPt3(IntPoint pt1, IntPoint pt2, IntPoint pt3) { if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) return false; else if (pt1.X != pt3.X) return (pt2.X > pt1.X) == (pt2.X < pt3.X); else return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); }
//------------------------------------------------------------------------------ internal bool PointOnPolygon(IntPoint pt, OutPt pp, bool UseFullRange) { OutPt pp2 = pp; while (true) { if (PointOnLineSegment(pt, pp2.Pt, pp2.Next.Pt, UseFullRange)) return true; pp2 = pp2.Next; if (pp2 == pp) break; } return false; }
//------------------------------------------------------------------------------ protected static 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 (cInt)(pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (cInt)(pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0; }
//------------------------------------------------------------------------------ private static bool SlopesNearCollinear(IntPoint pt1, IntPoint pt2, IntPoint pt3, double distSqrd) { if (DistanceSqrd(pt1, pt2) > DistanceSqrd(pt1, pt3)) return false; DoublePoint cpol = ClosestPointOnLine(pt2, pt1, pt3); double dx = pt2.X - cpol.X; double dy = pt2.Y - cpol.Y; return (dx*dx + dy*dy) < distSqrd; }
//------------------------------------------------------------------------------ internal bool PointIsVertex(IntPoint pt, OutPt pp) { OutPt pp2 = pp; do { if (pp2.Pt == pt) return true; pp2 = pp2.Next; } while (pp2 != pp); return false; }
//------------------------------------------------------------------------------ private void SwapIntersectNodes(IntersectNode int1, IntersectNode int2) { TEdge e1 = int1.Edge1; TEdge e2 = int1.Edge2; IntPoint p = new IntPoint(int1.Pt); int1.Edge1 = int2.Edge1; int1.Edge2 = int2.Edge2; int1.Pt = int2.Pt; int2.Edge1 = e1; int2.Edge2 = e2; int2.Pt = p; }
//------------------------------------------------------------------------------ private static double DistanceSqrd(IntPoint pt1, IntPoint pt2) { double dx = ((double)pt1.X - pt2.X); double dy = ((double)pt1.Y - pt2.Y); return (dx*dx + dy*dy); }
//------------------------------------------------------------------------------ private void ProcessHorizontal(TEdge horzEdge, bool isTopOfScanbeam) { Direction dir; cInt horzLeft, horzRight; GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight); TEdge eLastHorz = horzEdge, eMaxPair = null; while (eLastHorz.NextInLML != null && IsHorizontal(eLastHorz.NextInLML)) eLastHorz = eLastHorz.NextInLML; if (eLastHorz.NextInLML == null) eMaxPair = GetMaximaPair(eLastHorz); for (;;) { bool IsLastHorz = (horzEdge == eLastHorz); TEdge e = GetNextInAEL(horzEdge, dir); while(e != null) { //Break if we've got to the end of an intermediate horizontal edge ... //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. if (e.Curr.X == horzEdge.Top.X && horzEdge.NextInLML != null && e.Dx < horzEdge.NextInLML.Dx) break; TEdge eNext = GetNextInAEL(e, dir); //saves eNext for later if ((dir == Direction.dLeftToRight && e.Curr.X <= horzRight) || (dir == Direction.dRightToLeft && e.Curr.X >= horzLeft)) { //so far we're still in range of the horizontal Edge but make sure //we're at the last of consec. horizontals when matching with eMaxPair if(e == eMaxPair && IsLastHorz) { if (horzEdge.OutIdx >= 0 && horzEdge.WindDelta != 0) PrepareHorzJoins(horzEdge, isTopOfScanbeam); if (dir == Direction.dLeftToRight) IntersectEdges(horzEdge, e, e.Top); else IntersectEdges(e, horzEdge, e.Top); if (eMaxPair.OutIdx >= 0) throw new ClipperException("ProcessHorizontal error"); return; } else if(dir == Direction.dLeftToRight) { IntPoint Pt = new IntPoint(e.Curr.X, horzEdge.Curr.Y); IntersectEdges(horzEdge, e, Pt, true); } else { IntPoint Pt = new IntPoint(e.Curr.X, horzEdge.Curr.Y); IntersectEdges(e, horzEdge, Pt, true); } SwapPositionsInAEL(horzEdge, e); } else if ((dir == Direction.dLeftToRight && e.Curr.X >= horzRight) || (dir == Direction.dRightToLeft && e.Curr.X <= horzLeft)) break; e = eNext; } //end while if (horzEdge.OutIdx >= 0 && horzEdge.WindDelta != 0) PrepareHorzJoins(horzEdge, isTopOfScanbeam); if (horzEdge.NextInLML != null && IsHorizontal(horzEdge.NextInLML)) { UpdateEdgeIntoAEL(ref horzEdge); if (horzEdge.OutIdx >= 0) AddOutPt(horzEdge, horzEdge.Bot); GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight); } else break; } //end for (;;) if(horzEdge.NextInLML != null) { if(horzEdge.OutIdx >= 0) { OutPt op1 = AddOutPt( horzEdge, horzEdge.Top); UpdateEdgeIntoAEL(ref horzEdge); if (horzEdge.WindDelta == 0) return; //nb: HorzEdge is no longer horizontal here TEdge ePrev = horzEdge.PrevInAEL; TEdge eNext = horzEdge.NextInAEL; if (ePrev != null && ePrev.Curr.X == horzEdge.Bot.X && ePrev.Curr.Y == horzEdge.Bot.Y && ePrev.WindDelta != 0 && (ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && SlopesEqual(horzEdge, ePrev, m_UseFullRange))) { OutPt op2 = AddOutPt(ePrev, horzEdge.Bot); AddJoin(op1, op2, horzEdge.Top); } else if (eNext != null && eNext.Curr.X == horzEdge.Bot.X && eNext.Curr.Y == horzEdge.Bot.Y && eNext.WindDelta != 0 && eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && SlopesEqual(horzEdge, eNext, m_UseFullRange)) { OutPt op2 = AddOutPt(eNext, horzEdge.Bot); AddJoin(op1, op2, horzEdge.Top); } } else UpdateEdgeIntoAEL(ref horzEdge); } else if (eMaxPair != null) { if (eMaxPair.OutIdx >= 0) { if (dir == Direction.dLeftToRight) IntersectEdges(horzEdge, eMaxPair, horzEdge.Top); else IntersectEdges(eMaxPair, horzEdge, horzEdge.Top); if (eMaxPair.OutIdx >= 0) throw new ClipperException("ProcessHorizontal error"); } else { DeleteFromAEL(horzEdge); DeleteFromAEL(eMaxPair); } } else { if (horzEdge.OutIdx >= 0) AddOutPt(horzEdge, horzEdge.Top); DeleteFromAEL(horzEdge); } }
//------------------------------------------------------------------------------ private void InsertIntersectNode(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 (newNode.Pt.Y > m_IntersectNodes.Pt.Y) { newNode.Next = m_IntersectNodes; m_IntersectNodes = newNode; } else { IntersectNode iNode = m_IntersectNodes; while (iNode.Next != null && newNode.Pt.Y < iNode.Next.Pt.Y) iNode = iNode.Next; newNode.Next = iNode.Next; iNode.Next = newNode; } }
//------------------------------------------------------------------------------ private void IntersectEdges(TEdge e1, TEdge e2, IntPoint pt, bool protect = false) { //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 = !protect && e1.NextInLML == null && e1.Top.X == pt.X && e1.Top.Y == pt.Y; bool e2stops = !protect && e2.NextInLML == null && e2.Top.X == pt.X && e2.Top.Y == pt.Y; bool e1Contributing = (e1.OutIdx >= 0); bool e2Contributing = (e2.OutIdx >= 0); #if use_lines //if either edge is on an OPEN path ... if (e1.WindDelta == 0 || e2.WindDelta == 0) { //ignore subject-subject open path intersections UNLESS they //are both open paths, AND they are both 'contributing maximas' ... if (e1.WindDelta == 0 && e2.WindDelta == 0) { if ((e1stops || e2stops) && e1Contributing && e2Contributing) AddLocalMaxPoly(e1, e2, pt); } //if intersecting a subj line with a subj poly ... else if (e1.PolyTyp == e2.PolyTyp && e1.WindDelta != e2.WindDelta && m_ClipType == ClipType.ctUnion) { if (e1.WindDelta == 0) { if (e2Contributing) { AddOutPt(e1, pt); if (e1Contributing) e1.OutIdx = Unassigned; } } else { if (e1Contributing) { AddOutPt(e2, pt); if (e2Contributing) e2.OutIdx = Unassigned; } } } else if (e1.PolyTyp != e2.PolyTyp) { if ((e1.WindDelta == 0) && Math.Abs(e2.WindCnt) == 1 && (m_ClipType != ClipType.ctUnion || e2.WindCnt2 == 0)) { AddOutPt(e1, pt); if (e1Contributing) e1.OutIdx = Unassigned; } else if ((e2.WindDelta == 0) && (Math.Abs(e1.WindCnt) == 1) && (m_ClipType != ClipType.ctUnion || e1.WindCnt2 == 0)) { AddOutPt(e2, pt); if (e2Contributing) e2.OutIdx = Unassigned; } } if (e1stops) if (e1.OutIdx < 0) DeleteFromAEL(e1); else throw new ClipperException("Error intersecting polylines"); if (e2stops) if (e2.OutIdx < 0) DeleteFromAEL(e2); else throw new ClipperException("Error intersecting polylines"); return; } #endif //update winding counts... //assumes that e1 will be to the Right of e2 ABOVE the intersection if (e1.PolyTyp == e2.PolyTyp) { 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.PolyTyp == PolyType.ptSubject) { e1FillType = m_SubjFillType; e1FillType2 = m_ClipFillType; } else { e1FillType = m_ClipFillType; e1FillType2 = m_SubjFillType; } if (e2.PolyTyp == 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.PolyTyp != e2.PolyTyp && m_ClipType != ClipType.ctXor)) AddLocalMaxPoly(e1, e2, pt); else { AddOutPt(e1, pt); AddOutPt(e2, pt); SwapSides(e1, e2); SwapPolyIndexes(e1, e2); } } else if (e1Contributing) { if (e2Wc == 0 || e2Wc == 1) { AddOutPt(e1, pt); SwapSides(e1, e2); SwapPolyIndexes(e1, e2); } } else if (e2Contributing) { if (e1Wc == 0 || e1Wc == 1) { AddOutPt(e2, pt); SwapSides(e1, e2); SwapPolyIndexes(e1, e2); } } else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) { //neither edge is currently contributing ... cInt 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.PolyTyp != e2.PolyTyp) 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.PolyTyp == PolyType.ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || ((e1.PolyTyp == 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 double GetDx(IntPoint pt1, IntPoint pt2) { if (pt1.Y == pt2.Y) return horizontal; else return (double)(pt2.X - pt1.X) / (pt2.Y - pt1.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 HorzSegmentsOverlap( IntPoint Pt1a, IntPoint Pt1b, IntPoint Pt2a, IntPoint Pt2b) { //precondition: both segments are horizontal if ((Pt1a.X > Pt2a.X) == (Pt1a.X < Pt2b.X)) return true; else if ((Pt1b.X > Pt2a.X) == (Pt1b.X < Pt2b.X)) return true; else if ((Pt2a.X > Pt1a.X) == (Pt2a.X < Pt1b.X)) return true; else if ((Pt2b.X > Pt1a.X) == (Pt2b.X < Pt1b.X)) return true; else if ((Pt1a.X == Pt2a.X) && (Pt1b.X == Pt2b.X)) return true; else if ((Pt1a.X == Pt2b.X) && (Pt1b.X == Pt2a.X)) return true; else return false; }
//------------------------------------------------------------------------------ private bool IntersectPoint(TEdge edge1, TEdge edge2, out IntPoint ip) { ip = new IntPoint(); double b1, b2; //nb: with very large coordinate values, it's possible for SlopesEqual() to //return false but for the edge.Dx value be equal due to double precision rounding. if (SlopesEqual(edge1, edge2, m_UseFullRange) || edge1.Dx == edge2.Dx) { if (edge2.Bot.Y > edge1.Bot.Y) ip.Y = edge2.Bot.Y; else ip.Y = edge1.Bot.Y; return false; } else if (edge1.Delta.X == 0) { ip.X = edge1.Bot.X; if (IsHorizontal(edge2)) { ip.Y = edge2.Bot.Y; } else { b2 = edge2.Bot.Y - (edge2.Bot.X / edge2.Dx); ip.Y = Round(ip.X / edge2.Dx + b2); } } else if (edge2.Delta.X == 0) { ip.X = edge2.Bot.X; if (IsHorizontal(edge1)) { ip.Y = edge1.Bot.Y; } else { b1 = edge1.Bot.Y - (edge1.Bot.X / edge1.Dx); ip.Y = Round(ip.X / edge1.Dx + b1); } } else { b1 = edge1.Bot.X - edge1.Bot.Y * edge1.Dx; b2 = edge2.Bot.X - edge2.Bot.Y * edge2.Dx; double q = (b2 - b1) / (edge1.Dx - edge2.Dx); ip.Y = Round(q); if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx)) ip.X = Round(edge1.Dx * q + b1); else ip.X = Round(edge2.Dx * q + b2); } if (ip.Y < edge1.Top.Y || ip.Y < edge2.Top.Y) { if (edge1.Top.Y > edge2.Top.Y) { ip.Y = edge1.Top.Y; ip.X = TopX(edge2, edge1.Top.Y); return ip.X < edge1.Top.X; } else { ip.Y = edge2.Top.Y; ip.X = TopX(edge1, edge2.Top.Y); return ip.X > edge2.Top.X; } } else return true; }
//------------------------------------------------------------------------------ public static Paths OffsetPaths(Paths polys, double delta, JoinType jointype, EndType endtype, double MiterLimit) { Paths out_polys = new Paths(polys.Count); IntPoint botPt = new IntPoint(); IntPoint pt; int botIdx = -1; for (int i = 0; i < polys.Count; ++i) { out_polys.Add(new Path()); if (StripDupsAndGetBotPt(polys[i], out_polys[i], endtype == EndType.etClosed, out pt)) if (botIdx < 0 || pt.Y > botPt.Y || (pt.Y == botPt.Y && pt.X < botPt.X)) { botPt = pt; botIdx = i; } } if (endtype == EndType.etClosed && botIdx >= 0 && !Orientation(out_polys[botIdx])) ReversePaths(out_polys); Paths result; new PolyOffsetBuilder(out_polys, out result, delta, jointype, endtype, MiterLimit); return result; }
//------------------------------------------------------------------------------ bool JoinHorz(OutPt op1, OutPt op1b, OutPt op2, OutPt op2b, IntPoint Pt, bool DiscardLeft) { Direction Dir1 = (op1.Pt.X > op1b.Pt.X ? Direction.dRightToLeft : Direction.dLeftToRight); Direction Dir2 = (op2.Pt.X > op2b.Pt.X ? Direction.dRightToLeft : Direction.dLeftToRight); if (Dir1 == Dir2) return false; //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) //So, to facilitate this while inserting Op1b and Op2b ... //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) if (Dir1 == Direction.dLeftToRight) { while (op1.Next.Pt.X <= Pt.X && op1.Next.Pt.X >= op1.Pt.X && op1.Next.Pt.Y == Pt.Y) op1 = op1.Next; if (DiscardLeft && (op1.Pt.X != Pt.X)) op1 = op1.Next; op1b = DupOutPt(op1, !DiscardLeft); if (op1b.Pt != Pt) { op1 = op1b; op1.Pt = Pt; op1b = DupOutPt(op1, !DiscardLeft); } } else { while (op1.Next.Pt.X >= Pt.X && op1.Next.Pt.X <= op1.Pt.X && op1.Next.Pt.Y == Pt.Y) op1 = op1.Next; if (!DiscardLeft && (op1.Pt.X != Pt.X)) op1 = op1.Next; op1b = DupOutPt(op1, DiscardLeft); if (op1b.Pt != Pt) { op1 = op1b; op1.Pt = Pt; op1b = DupOutPt(op1, DiscardLeft); } } if (Dir2 == Direction.dLeftToRight) { while (op2.Next.Pt.X <= Pt.X && op2.Next.Pt.X >= op2.Pt.X && op2.Next.Pt.Y == Pt.Y) op2 = op2.Next; if (DiscardLeft && (op2.Pt.X != Pt.X)) op2 = op2.Next; op2b = DupOutPt(op2, !DiscardLeft); if (op2b.Pt != Pt) { op2 = op2b; op2.Pt = Pt; op2b = DupOutPt(op2, !DiscardLeft); }; } else { while (op2.Next.Pt.X >= Pt.X && op2.Next.Pt.X <= op2.Pt.X && op2.Next.Pt.Y == Pt.Y) op2 = op2.Next; if (!DiscardLeft && (op2.Pt.X != Pt.X)) op2 = op2.Next; op2b = DupOutPt(op2, DiscardLeft); if (op2b.Pt != Pt) { op2 = op2b; op2.Pt = Pt; op2b = DupOutPt(op2, DiscardLeft); }; }; if ((Dir1 == Direction.dLeftToRight) == DiscardLeft) { op1.Prev = op2; op2.Next = op1; op1b.Next = op2b; op2b.Prev = op1b; } else { op1.Next = op2; op2.Prev = op1; op1b.Prev = op2b; op2b.Next = op1b; } return true; }
//------------------------------------------------------------------------------ private static DoublePoint ClosestPointOnLine(IntPoint pt, IntPoint linePt1, IntPoint linePt2) { double dx = ((double)linePt2.X - linePt1.X); double dy = ((double)linePt2.Y - linePt1.Y); if (dx == 0 && dy == 0) return new DoublePoint(linePt1.X, linePt1.Y); double q = ((pt.X-linePt1.X)*dx + (pt.Y-linePt1.Y)*dy) / (dx*dx + dy*dy); return new DoublePoint( (1-q)*linePt1.X + q*linePt2.X, (1-q)*linePt1.Y + q*linePt2.Y); }
//------------------------------------------------------------------------------ // OffsetPolygon functions ... //------------------------------------------------------------------------------ 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 static bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) { double dx = (double)pt1.X - pt2.X; double dy = (double)pt1.Y - pt2.Y; return ((dx * dx) + (dy * dy) <= distSqrd); }
//------------------------------------------------------------------------------ public PolyOffsetBuilder(Paths pts, out Paths solution, double delta, JoinType jointype, EndType endtype, double limit = 0) { //precondition: solution != pts solution = new Paths(); if (ClipperBase.near_zero(delta)) {solution = pts; return; } m_p = pts; if (endtype != EndType.etClosed && delta < 0) delta = -delta; m_delta = delta; if (jointype == JoinType.jtMiter) { //m_miterVal: see offset_triginometry.svg in the documentation folder ... if (limit > 2) m_miterLim = 2 / (limit * limit); else m_miterLim = 0.5; if (endtype == EndType.etRound) limit = 0.25; } if (jointype == JoinType.jtRound || endtype == EndType.etRound) { if (limit <= 0) limit = 0.25; else if (limit > Math.Abs(delta)*0.25) limit = Math.Abs(delta)*0.25; //m_roundVal: see offset_triginometry2.svg in the documentation folder ... m_Steps360 = Math.PI / Math.Acos(1 - limit / Math.Abs(delta)); m_sin = Math.Sin(2 * Math.PI / m_Steps360); m_cos = Math.Cos(2 * Math.PI / m_Steps360); m_Steps360 /= Math.PI * 2; if (delta < 0) m_sin = -m_sin; } solution.Capacity = pts.Count; for (m_i = 0; m_i < pts.Count; m_i++) { int len = pts[m_i].Count; if (len == 0 || (len < 3 && delta <= 0)) continue; if (len == 1) { if (jointype == JoinType.jtRound) { double X = 1.0, Y = 0.0; for (cInt j = 1; j <= Round(m_Steps360 * 2 * Math.PI); j++) { AddPoint(new IntPoint( Round(m_p[m_i][0].X + X * delta), Round(m_p[m_i][0].Y + Y * delta))); double X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; } } else { double X = -1.0, Y = -1.0; for (int j = 0; j < 4; ++j) { AddPoint(new IntPoint(Round(m_p[m_i][0].X + X * delta), Round(m_p[m_i][0].Y + Y * delta))); if (X < 0) X = 1; else if (Y < 0) Y = 1; else X = -1; } } continue; } //build normals ... normals.Clear(); normals.Capacity = len; for (int j = 0; j < len -1; ++j) normals.Add(GetUnitNormal(pts[m_i][j], pts[m_i][j+1])); if (endtype == EndType.etClosed) normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0])); else normals.Add(new DoublePoint(normals[len - 2])); currentPoly = new Path(); if (endtype == EndType.etClosed) { m_k = len - 1; for (m_j = 0; m_j < len; ++m_j) OffsetPoint(jointype); solution.Add(currentPoly); } else { m_k = 0; for (m_j = 1; m_j < len - 1; ++m_j) OffsetPoint(jointype); IntPoint pt1; if (endtype == EndType.etButt) { m_j = len - 1; pt1 = new IntPoint((cInt)Round(pts[m_i][m_j].X + normals[m_j].X * delta), (cInt)Round(pts[m_i][m_j].Y + normals[m_j].Y * delta)); AddPoint(pt1); pt1 = new IntPoint((cInt)Round(pts[m_i][m_j].X - normals[m_j].X * delta), (cInt)Round(pts[m_i][m_j].Y - normals[m_j].Y * delta)); AddPoint(pt1); } else { m_j = len - 1; m_k = len - 2; m_sinA = 0; normals[m_j] = new DoublePoint(-normals[m_j].X, -normals[m_j].Y); if (endtype == EndType.etSquare) DoSquare(); else DoRound(); } //re-build Normals ... for (int j = len - 1; j > 0; j--) normals[j] = new DoublePoint(-normals[j - 1].X, -normals[j - 1].Y); normals[0] = new DoublePoint(-normals[1].X, -normals[1].Y); m_k = len - 1; for (m_j = m_k - 1; m_j > 0; --m_j) OffsetPoint(jointype); if (endtype == EndType.etButt) { pt1 = new IntPoint((cInt)Round(pts[m_i][0].X - normals[0].X * delta), (cInt)Round(pts[m_i][0].Y - normals[0].Y * delta)); AddPoint(pt1); pt1 = new IntPoint((cInt)Round(pts[m_i][0].X + normals[0].X * delta), (cInt)Round(pts[m_i][0].Y + normals[0].Y * delta)); AddPoint(pt1); } else { m_k = 1; m_sinA = 0; if (endtype == EndType.etSquare) DoSquare(); else DoRound(); } solution.Add(currentPoly); } } //finally, clean up untidy corners ... Clipper clpr = new Clipper(); clpr.AddPaths(solution, PolyType.ptSubject, true); if (delta > 0) { clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive); } else { IntRect r = clpr.GetBounds(); Path outer = new Path(4); outer.Add(new IntPoint(r.left - 10, r.bottom + 10)); outer.Add(new IntPoint(r.right + 10, r.bottom + 10)); outer.Add(new IntPoint(r.right + 10, r.top - 10)); outer.Add(new IntPoint(r.left - 10, r.top - 10)); clpr.AddPath(outer, PolyType.ptSubject, true); clpr.ReverseSolution = true; clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative); if (solution.Count > 0) solution.RemoveAt(0); } }
//------------------------------------------------------------------------------ internal bool PointOnLineSegment(IntPoint pt, IntPoint linePt1, IntPoint linePt2, bool UseFullRange) { if (UseFullRange) return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) || ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) || (((pt.X > linePt1.X) == (pt.X < linePt2.X)) && ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) && ((Int128.Int128Mul((pt.X - linePt1.X), (linePt2.Y - linePt1.Y)) == Int128.Int128Mul((linePt2.X - linePt1.X), (pt.Y - linePt1.Y))))); else return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) || ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) || (((pt.X > linePt1.X) == (pt.X < linePt2.X)) && ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) && ((pt.X - linePt1.X) * (linePt2.Y - linePt1.Y) == (linePt2.X - linePt1.X) * (pt.Y - linePt1.Y))); }
//------------------------------------------------------------------------------ internal void AddPoint(IntPoint pt) { if (currentPoly.Count == currentPoly.Capacity) currentPoly.Capacity += m_buffLength; currentPoly.Add(pt); }
//------------------------------------------------------------------------------ internal bool PointInPolygon(IntPoint pt, OutPt pp, bool UseFullRange) { OutPt pp2 = pp; bool result = false; if (UseFullRange) { do { if (((pp2.Pt.Y > pt.Y) != (pp2.Prev.Pt.Y > 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 { //http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html if (((pp2.Pt.Y > pt.Y) != (pp2.Prev.Pt.Y > 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; }
} //end PolyOffsetBuilder //------------------------------------------------------------------------------ internal static bool UpdateBotPt(IntPoint pt, ref IntPoint botPt) { if (pt.Y > botPt.Y || (pt.Y == botPt.Y && pt.X < botPt.X)) { botPt = pt; return true; } else return false; }
//------------------------------------------------------------------------------ void RangeTest(IntPoint Pt, ref bool useFullRange) { if (useFullRange) { if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) throw new ClipperException("Coordinate outside allowed range"); } else if (Pt.X > loRange || Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) { useFullRange = true; RangeTest(Pt, ref useFullRange); } }
//------------------------------------------------------------------------------ internal static bool StripDupsAndGetBotPt(Path in_path, Path out_path, bool closed, out IntPoint botPt) { botPt = new IntPoint(0, 0); int len = in_path.Count; if (closed) while (len > 0 && (in_path[0] == in_path[len -1])) len--; if (len == 0) return false; out_path.Capacity = len; int j = 0; out_path.Add(in_path[0]); botPt = in_path[0]; for (int i = 1; i < len; ++i) if (in_path[i] != out_path[j]) { out_path.Add(in_path[i]); j++; if (out_path[j].Y > botPt.Y || ((out_path[j].Y == botPt.Y) && out_path[j].X < botPt.X)) botPt = out_path[j]; } j++; if (j < 2 || (closed && (j == 2))) j = 0; while (out_path.Count > j) out_path.RemoveAt(j); return j > 0; }
public DoublePoint(IntPoint ip) { this.X = ip.X; this.Y = ip.Y; }
public IntPoint(IntPoint pt) { this.X = pt.X; this.Y = pt.Y; }
public static Vector3 IntPointToV3(Pathfinding.ClipperLib.IntPoint p) { VInt3 num = new VInt3((int)p.X, 0, (int)p.Y); return((Vector3)num); }
//------------------------------------------------------------------------------ internal void SwapPoints(ref IntPoint pt1, ref IntPoint pt2) { IntPoint tmp = new IntPoint(pt1); pt1 = pt2; pt2 = tmp; }