//------------------------------------------------------------------------------ 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); }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ void RangeTest(IntPoint pt, ref Int64 maxrange) { if (pt.X > maxrange) { if (pt.X > hiRange) throw new ClipperException("Coordinate exceeds range bounds"); else maxrange = hiRange; } if (pt.Y > maxrange) { if (pt.Y > hiRange) throw new ClipperException("Coordinate exceeds range bounds"); else maxrange = hiRange; } }
//------------------------------------------------------------------------------ protected static bool PointsEqual(IntPoint pt1, IntPoint pt2) { return (pt1.X == pt2.X && pt1.Y == pt2.Y); }
//------------------------------------------------------------------------------ internal bool PointOnLineSegment(IntPoint pt, IntPoint linePt1, IntPoint linePt2, bool UseFullInt64Range) { if (UseFullInt64Range) 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))); }
//------------------------------------------------------------------------------ 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); }
//------------------------------------------------------------------------------ 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); }
//------------------------------------------------------------------------------ private bool IntersectPoint(TEdge edge1, TEdge edge2, ref IntPoint ip) { double b1, b2; if (SlopesEqual(edge1, edge2, m_UseFullRange)) { if (edge2.ybot > edge1.ybot) ip.Y = edge2.ybot; else ip.Y = edge1.ybot; 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; 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.ytop || ip.Y < edge2.ytop) { if (edge1.ytop > edge2.ytop) { ip.X = edge1.xtop; ip.Y = edge1.ytop; return TopX(edge2, edge1.ytop) < edge1.xtop; } else { ip.X = edge2.xtop; ip.Y = edge2.ytop; return TopX(edge1, edge2.ytop) > edge2.xtop; } } else return true; }
//------------------------------------------------------------------------------ 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 ePrev = e.prevInAEL; DoMaxima(e, topY); if (ePrev == null) e = m_ActiveEdges; else e = ePrev.nextInAEL; } else { bool intermediateVert = IsIntermediate(e, topY); //2. promote horizontal edges, otherwise update xcurr and ycurr ... if (intermediateVert && 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 { e.xcurr = TopX(e, topY); e.ycurr = topY; if (m_ForceSimple && e.prevInAEL != null && e.prevInAEL.xcurr == e.xcurr && e.outIdx >= 0 && e.prevInAEL.outIdx >= 0) { if (intermediateVert) AddOutPt(e.prevInAEL, new IntPoint(e.xcurr, topY)); else AddOutPt(e, new IntPoint(e.xcurr, 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 ... TEdge ePrev = e.prevInAEL; TEdge eNext = e.nextInAEL; if (ePrev != null && ePrev.xcurr == e.xbot && ePrev.ycurr == e.ybot && e.outIdx >= 0 && ePrev.outIdx >= 0 && ePrev.ycurr > ePrev.ytop && SlopesEqual(e, ePrev, m_UseFullRange)) { AddOutPt(ePrev, new IntPoint(e.xbot, e.ybot)); AddJoin(e, ePrev, -1, -1); } else if (eNext != null && eNext.xcurr == e.xbot && eNext.ycurr == e.ybot && e.outIdx >= 0 && eNext.outIdx >= 0 && eNext.ycurr > eNext.ytop && SlopesEqual(e, eNext, m_UseFullRange)) { AddOutPt(eNext, new IntPoint(e.xbot, e.ybot)); AddJoin(e, eNext, -1, -1); } } e = e.nextInAEL; } }
//------------------------------------------------------------------------------ private void BuildIntersectList(Int64 botY, Int64 topY) { if (m_ActiveEdges == null) return; //prepare for sorting ... TEdge e = m_ActiveEdges; m_SortedEdges = e; while (e != null) { e.prevInSEL = e.prevInAEL; e.nextInSEL = e.nextInAEL; e.xcurr = 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.xcurr > eNext.xcurr) { if (!IntersectPoint(e, eNext, ref pt) && e.xcurr > eNext.xcurr + 1) throw new ClipperException("Intersection error"); if (pt.Y > botY) { pt.Y = botY; pt.X = TopX(e, pt.Y); } InsertIntersectNode(e, eNext, pt); SwapPositionsInSEL(e, eNext); isModified = true; } else e = eNext; } if (e.prevInSEL != null) e.prevInSEL.nextInSEL = null; else break; } m_SortedEdges = null; }
//------------------------------------------------------------------------------ private void 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, 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 { 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 ... 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 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; }
} //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; }
//------------------------------------------------------------------------------ // OffsetPolygon functions ... //------------------------------------------------------------------------------ internal static PolygonClp BuildArc(IntPoint pt, double a1, double a2, double r, double limit) { //see notes in clipper.pas regarding steps double arcFrac = Math.Abs(a2 - a1) / (2 * Math.PI); int steps = (int)(arcFrac * Math.PI / Math.Acos(1 - limit / Math.Abs(r))); if (steps < 2) steps = 2; else if (steps > (int)(222.0 * arcFrac)) steps = (int)(222.0 * arcFrac); double x = Math.Cos(a1); double y = Math.Sin(a1); double c = Math.Cos((a2 - a1) / steps); double s = Math.Sin((a2 - a1) / steps); PolygonClp result = new PolygonClp(steps + 1); for (int i = 0; i <= steps; ++i) { result.Add(new IntPoint(pt.X + Round(x * r), pt.Y + Round(y * r))); double x2 = x; x = x * c - s * y; //cross product y = x2 * s + y * c; //dot product } return result; }
//------------------------------------------------------------------------------ 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); }
//------------------------------------------------------------------------------ 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 SlopesNearColinear(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; }
//------------------------------------------------------------------------------ public PolyOffsetBuilder(PolygonsClp pts, PolygonsClp solution, bool isPolygon, double delta, JoinType jointype, EndType endtype, double limit = 0) { //precondition: solution != pts if (delta == 0) { solution = pts; return; } m_p = pts; m_delta = delta; m_rmin = 0.5; if (jointype == JoinType.jtMiter) { if (limit > 2) m_rmin = 2.0 / (limit * limit); limit = 0.25; //just in case endtype == etRound } else { if (limit <= 0) limit = 0.25; else if (limit > Math.Abs(delta)) limit = Math.Abs(delta); } double deltaSq = delta * delta; solution.Clear(); 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; else if (len == 1) { currentPoly = new PolygonClp(); currentPoly = BuildArc(pts[m_i][0], 0, 2 * Math.PI, delta, limit); solution.Add(currentPoly); continue; } bool forceClose = PointsEqual(pts[m_i][0], pts[m_i][len - 1]); if (forceClose) len--; //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 (isPolygon || forceClose) normals.Add(GetUnitNormal(pts[m_i][len - 1], pts[m_i][0])); else normals.Add(new DoublePoint(normals[len - 2])); currentPoly = new PolygonClp(); if (isPolygon || forceClose) { m_k = len - 1; for (m_j = 0; m_j < len; ++m_j) OffsetPoint(jointype, limit); solution.Add(currentPoly); if (!isPolygon) { currentPoly = new PolygonClp(); m_delta = -m_delta; m_k = len - 1; for (m_j = 0; m_j < len; ++m_j) OffsetPoint(jointype, limit); m_delta = -m_delta; currentPoly.Reverse(); solution.Add(currentPoly); } } else { m_k = 0; for (m_j = 1; m_j < len - 1; ++m_j) OffsetPoint(jointype, limit); IntPoint pt1; if (endtype == EndType.etButt) { m_j = len - 1; pt1 = 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); pt1 = 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); } else { m_j = len - 1; m_k = len - 2; normals[m_j].X = -normals[m_j].X; normals[m_j].Y = -normals[m_j].Y; if (endtype == EndType.etSquare) DoSquare(); else DoRound(limit); } //re-build Normals ... for (int j = len - 1; j > 0; j--) { normals[j].X = -normals[j - 1].X; normals[j].Y = -normals[j - 1].Y; } normals[0].X = -normals[1].X; normals[0].Y = -normals[1].Y; m_k = len - 1; for (m_j = m_k - 1; m_j > 0; --m_j) OffsetPoint(jointype, limit); if (endtype == EndType.etButt) { pt1 = new IntPoint((Int64)Round(pts[m_i][0].X - normals[0].X * delta), (Int64)Round(pts[m_i][0].Y - normals[0].Y * delta)); AddPoint(pt1); pt1 = new IntPoint((Int64)Round(pts[m_i][0].X + normals[0].X * delta), (Int64)Round(pts[m_i][0].Y + normals[0].Y * delta)); AddPoint(pt1); } else { m_k = 1; if (endtype == EndType.etSquare) DoSquare(); else DoRound(limit); } solution.Add(currentPoly); } } //finally, clean up untidy corners ... Clipper clpr = new Clipper(); clpr.AddPolygons(solution, PolyType.ptSubject); if (delta > 0) { clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftPositive, PolyFillType.pftPositive); } else { IntRect r = clpr.GetBounds(); PolygonClp outer = new PolygonClp(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.AddPolygon(outer, PolyType.ptSubject); clpr.ReverseSolution = true; clpr.Execute(ClipType.ctUnion, solution, PolyFillType.pftNegative, PolyFillType.pftNegative); if (solution.Count > 0) solution.RemoveAt(0); } }
public IntPoint(IntPoint pt) { this.X = pt.X; this.Y = pt.Y; }
//------------------------------------------------------------------------------ internal void AddPoint(IntPoint pt) { if (currentPoly.Count == currentPoly.Capacity) currentPoly.Capacity += m_buffLength; currentPoly.Add(pt); }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ internal void DoSquare() { IntPoint pt1 = new IntPoint((Int64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), (Int64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); IntPoint pt2 = new IntPoint((Int64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), (Int64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_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(m_delta); 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(m_p[m_i][m_j]); AddPoint(pt2); } }
//------------------------------------------------------------------------------ internal bool PointOnPolygon(IntPoint pt, OutPt pp, bool UseFullInt64Range) { OutPt pp2 = pp; while (true) { if (PointOnLineSegment(pt, pp2.pt, pp2.next.pt, UseFullInt64Range)) return true; pp2 = pp2.next; if (pp2 == pp) break; } return false; }
//------------------------------------------------------------------------------ internal void DoMiter() { if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) { double q = m_delta / m_r; AddPoint(new IntPoint((Int64)Round(m_p[m_i][m_j].X + (normals[m_k].X + normals[m_j].X) * q), (Int64)Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); } else { IntPoint pt1 = new IntPoint((Int64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), (Int64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); IntPoint pt2 = new IntPoint((Int64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), (Int64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); AddPoint(pt1); AddPoint(m_p[m_i][m_j]); AddPoint(pt2); } }
//------------------------------------------------------------------------------ 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 (Int64)(pt1.Y - pt2.Y) * (pt3.X - pt4.X) - (Int64)(pt1.X - pt2.X) * (pt3.Y - pt4.Y) == 0; }
//------------------------------------------------------------------------------ internal void DoRound(double Limit) { IntPoint pt1 = new IntPoint(Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); IntPoint pt2 = new IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_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) * m_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 (m_delta > 0 && a2 < a1) a2 += Math.PI * 2; else if (m_delta < 0 && a2 > a1) a2 -= Math.PI * 2; PolygonClp arc = BuildArc(m_p[m_i][m_j], a1, a2, m_delta, Limit); for (int m = 0; m < arc.Count; m++) AddPoint(arc[m]); } } else AddPoint(m_p[m_i][m_j]); AddPoint(pt2); }
//------------------------------------------------------------------------------ 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 FindSegment(ref OutPt pp, bool UseFullInt64Range, 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, UseFullInt64Range) && SlopesEqual(pt1a, pt2a, pp.pt, UseFullInt64Range) && GetOverlapSegment(pt1a, pt2a, pp.pt, pp.prev.pt, ref pt1, ref pt2)) return true; pp = pp.next; } while (pp != pp2); return false; }