//------------------------------------------------------------------------------ private void Triangulate(OutRec outrec) { OutPt op = outrec.Pts; if (op.Next == op.Prev) { return; } OutPt end_op = op.Next; OutPtTri opt; for (;;) { OutPt op2 = op; Int64 cpval = 0; while (op.Prev != end_op) { if (CrossProductVal(op.Pt, op.Prev.Pt, op.Prev.Prev.Pt, out cpval) >= 0) { break; } if (op2 != op) { //Due to rounding, the clipping algorithm can occasionally produce //tiny self-intersections and these need removing ... if (CrossProductVal(op2.Pt, op.Pt, op.Prev.Prev.Pt, out cpval) > 0) { opt = (OutPtTri)op; if (opt.outrec != null) { UpdateHelper(opt.outrec, op2); } DisposeOutPt(op); op = op2; continue; } } op = op.Prev; } if (op.Prev == end_op) { break; } if (cpval != 0) { AddPolygon(op.Pt, op.Prev.Pt, op.Prev.Prev.Pt); } opt = (OutPtTri)op.Prev; if (opt.outrec != null) { UpdateHelper(opt.outrec, op); } DisposeOutPt(op.Prev); if (op != outrec.Pts) { op = op.Next; } } }
/// <summary> /// Reset all fields to default values in preparation for object recycling /// </summary> public void PrepareForRecycle() { Idx = 0; IsHole = IsOpen = false; FirstLeft = null; Pts = BottomPt = null; PolyNode = null; }
//------------------------------------------------------------------------------ private OutPtTri InsertPt(Point pt, OutPt afterOutPt) { OutPtTri result = (OutPtTri)CreateOutPt(); result.Pt = pt; result.Prev = afterOutPt; result.Next = afterOutPt.Next; result.outrec = (afterOutPt as OutPtTri).outrec; result.rightOutrec = null; afterOutPt.Next.Prev = result; afterOutPt.Next = result; return(result); }
//------------------------------------------------------------------------------ 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); }
internal bool PointIsVertex(IntPoint pt, OutPt pp) { OutPt outPt = pp; do { if (outPt.Pt == pt) { return(true); } outPt = outPt.Next; }while (outPt != pp); return(false); }
internal bool PointOnPolygon(IntPoint pt, OutPt pp, bool UseFullRange) { OutPt outPt = pp; do { if (PointOnLineSegment(pt, outPt.Pt, outPt.Next.Pt, UseFullRange)) { return(true); } outPt = outPt.Next; }while (outPt != pp); return(false); }
//------------------------------------------------------------------------------ void Update(OutPt op, OutRec outrec) { OutPt op2 = op; do { OutPtTri opt = (OutPtTri)op2; if (opt.rightOutrec != null) { UpdateHelper(opt.rightOutrec, null); } opt.outrec = outrec; op2 = op2.Next; } while (op2 != op); }
//------------------------------------------------------------------------------ private int PointCount(OutPt op) { if (op == null) { return(0); } OutPt p = op; int cnt = 0; do { cnt++; p = p.Next; } while (p != op); return(cnt); }
//------------------------------------------------------------------------------ private void DisposeOutPt(OutPt op) { if (op.Prev != null) { op.Prev.Next = op.Next; } if (op.Next != null) { op.Next.Prev = op.Prev; } OutPtTri opt = (OutPtTri)op; if (opt.rightOutrec != null) { opt.rightOutrec.leftOutpt = null; } }
//------------------------------------------------------------------------------ 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); }
//------------------------------------------------------------------------------ private void UpdateHelper(OutRec rightOutrec, OutPt leftOutpt) { OutPtTri leftOpt = (OutPtTri)leftOutpt; OutRecTri rightOrt = (OutRecTri)rightOutrec; if (leftOpt != null && leftOpt.rightOutrec != null) { leftOpt.rightOutrec.leftOutpt = null; } if (rightOrt.leftOutpt != null) { rightOrt.leftOutpt.rightOutrec = null; } rightOrt.leftOutpt = leftOpt; if (leftOpt != null) { leftOpt.rightOutrec = rightOrt; } }
//------------------------------------------------------------------------------ protected override OutPt AddOutPt(Active e, Point pt) { OutPt result = base.AddOutPt(e, pt); OutPtTri opt = (OutPtTri)result; opt.outrec = e.OutRec; LastOp = result; Triangulate(e.OutRec); //Triangulate() above may assign Result.OutRecRt so ... if (IsStartSide(e) && opt.rightOutrec == null) { Active e2 = GetRightAdjacentHotEdge(e); if (e2 != null) { UpdateHelper(e2.OutRec, result); } } return(result); }
//------------------------------------------------------------------------------ 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); }
//------------------------------------------------------------------------------ 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; }
//---------------------------------------------------------------------- private bool Poly2ContainsPoly1(OutPt outPt1, OutPt outPt2, bool UseFullInt64Range) { OutPt pt = outPt1; //Because the polygons may be touching, we need to find a vertex that //isn't touching the other polygon ... if (PointOnPolygon(pt.pt, outPt2, UseFullInt64Range)) { pt = pt.next; while (pt != outPt1 && PointOnPolygon(pt.pt, outPt2, UseFullInt64Range)) pt = pt.next; if (pt == outPt1) return true; } return PointInPolygon(pt.pt, outPt2, UseFullInt64Range); }
//------------------------------------------------------------------------------ private bool JoinPoints(Join j, out OutPt p1, out OutPt p2) { OutRec outRec1 = GetOutRec(j.OutPt1.Idx); OutRec outRec2 = GetOutRec(j.OutPt2.Idx); OutPt op1 = j.OutPt1, op1b; OutPt op2 = j.OutPt2, op2b; p1 = null; p2 = null; //There are 3 kinds of joins for output polygons ... //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same //location at the Bottom of the overlapping segment (& Join.OffPt is above). //3. StrictlySimple joins where edges touch but are not collinear and where //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. bool isHorizontal = (j.OutPt1.Pt.Y == j.OffPt.Y); if (isHorizontal && (j.OffPt == j.OutPt1.Pt) && (j.OffPt == j.OutPt2.Pt)) { //Strictly Simple join ... op1b = j.OutPt1.Next; while (op1b != op1 && (op1b.Pt == j.OffPt)) op1b = op1b.Next; bool reverse1 = (op1b.Pt.Y > j.OffPt.Y); op2b = j.OutPt2.Next; while (op2b != op2 && (op2b.Pt == j.OffPt)) op2b = op2b.Next; bool reverse2 = (op2b.Pt.Y > j.OffPt.Y); if (reverse1 == reverse2) return false; if (reverse1) { op1b = DupOutPt(op1, false); op2b = DupOutPt(op2, true); op1.Prev = op2; op2.Next = op1; op1b.Next = op2b; op2b.Prev = op1b; p1 = op1; p2 = op1b; return true; } else { op1b = DupOutPt(op1, true); op2b = DupOutPt(op2, false); op1.Next = op2; op2.Prev = op1; op1b.Prev = op2b; op2b.Next = op1b; p1 = op1; p2 = op1b; return true; } } else if (isHorizontal) { //treat horizontal joins differently to non-horizontal joins since with //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt //may be anywhere along the horizontal edge. op1b = op1; while (op1.Prev.Pt.Y == op1.Pt.Y && op1.Prev != op1b && op1.Prev != op2) op1 = op1.Prev; while (op1b.Next.Pt.Y == op1b.Pt.Y && op1b.Next != op1 && op1b.Next != op2) op1b = op1b.Next; if (op1b.Next == op1 || op1b.Next == op2) return false; //a flat 'polygon' op2b = op2; while (op2.Prev.Pt.Y == op2.Pt.Y && op2.Prev != op2b && op2.Prev != op1b) op2 = op2.Prev; while (op2b.Next.Pt.Y == op2b.Pt.Y && op2b.Next != op2 && op2b.Next != op1) op2b = op2b.Next; if (op2b.Next == op2 || op2b.Next == op1) return false; //a flat 'polygon' cInt Left, Right; //Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges if (!GetOverlap(op1.Pt.X, op1b.Pt.X, op2.Pt.X, op2b.Pt.X, out Left, out Right)) return false; //DiscardLeftSide: when overlapping edges are joined, a spike will created //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up //on the discard Side as either may still be needed for other joins ... IntPoint Pt; bool DiscardLeftSide; if (op1.Pt.X >= Left && op1.Pt.X <= Right) { Pt = op1.Pt; DiscardLeftSide = (op1.Pt.X > op1b.Pt.X); } else if (op2.Pt.X >= Left&& op2.Pt.X <= Right) { Pt = op2.Pt; DiscardLeftSide = (op2.Pt.X > op2b.Pt.X); } else if (op1b.Pt.X >= Left && op1b.Pt.X <= Right) { Pt = op1b.Pt; DiscardLeftSide = op1b.Pt.X > op1.Pt.X; } else { Pt = op2b.Pt; DiscardLeftSide = (op2b.Pt.X > op2.Pt.X); } p1 = op1; p2 = op2; return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); } else { //nb: For non-horizontal joins ... // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y // 2. Jr.OutPt1.Pt > Jr.OffPt.Y //make sure the polygons are correctly oriented ... op1b = op1.Next; while ((op1b.Pt == op1.Pt) && (op1b != op1)) op1b = op1b.Next; bool Reverse1 = ((op1b.Pt.Y > op1.Pt.Y) || !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, m_UseFullRange)); if (Reverse1) { op1b = op1.Prev; while ((op1b.Pt == op1.Pt) && (op1b != op1)) op1b = op1b.Prev; if ((op1b.Pt.Y > op1.Pt.Y) || !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt, m_UseFullRange)) return false; }; op2b = op2.Next; while ((op2b.Pt == op2.Pt) && (op2b != op2)) op2b = op2b.Next; bool Reverse2 = ((op2b.Pt.Y > op2.Pt.Y) || !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, m_UseFullRange)); if (Reverse2) { op2b = op2.Prev; while ((op2b.Pt == op2.Pt) && (op2b != op2)) op2b = op2b.Prev; if ((op2b.Pt.Y > op2.Pt.Y) || !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt, m_UseFullRange)) return false; } if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; if (Reverse1) { op1b = DupOutPt(op1, false); op2b = DupOutPt(op2, true); op1.Prev = op2; op2.Next = op1; op1b.Next = op2b; op2b.Prev = op1b; p1 = op1; p2 = op1b; return true; } else { op1b = DupOutPt(op1, true); op2b = DupOutPt(op2, false); op1.Next = op2; op2.Prev = op1; op1b.Prev = op2b; op2b.Next = op1b; p1 = op1; p2 = op1b; return true; } } }
//------------------------------------------------------------------------------ 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 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; } }
/// <summary> /// Reset all fields to default values in preparation for object recycling /// </summary> public void PrepareForRecycle() { OutPt1 = null; OutPt2 = null; OffPt = new IntPoint(); }
//------------------------------------------------------------------------------ private OutPt PolygonBottom(OutPt pp) { OutPt p = pp.next; OutPt result = pp; while (p != pp) { if (p.pt.Y > result.pt.Y) result = p; else if (p.pt.Y == result.pt.Y && p.pt.X < result.pt.X) result = p; p = p.next; } return result; }
/// <summary> /// Reset all fields to default values in preparation for object recycling /// </summary> public void PrepareForRecycle() { Idx = 0; Pt = new IntPoint(); Next = Prev = null; }
//---------------------------------------------------------------------- private bool Poly2ContainsPoly1(OutPt outPt1, OutPt outPt2, bool UseFullInt64Range) { //find the first pt in outPt1 that isn't also a vertex of outPt2 ... OutPt outPt = outPt1; do { if (!PointIsVertex(outPt.pt, outPt2)) break; outPt = outPt.next; } while (outPt != outPt1); bool result; //sometimes a point on one polygon can be touching the other polygon //so to be totally confident outPt1 is inside outPt2 repeat ... do { result = PointInPolygon(outPt.pt, outPt2, UseFullInt64Range); outPt = outPt.next; } while (result && outPt != outPt1); return result; }
//------------------------------------------------------------------------------ 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 JoinPoints(JoinRec j, out OutPt p1, out OutPt p2) { p1 = null; p2 = null; OutRec outRec1 = m_PolyOuts[j.poly1Idx]; OutRec outRec2 = m_PolyOuts[j.poly2Idx]; if (outRec1 == null || outRec2 == null) return false; OutPt pp1a = outRec1.pts; OutPt pp2a = outRec2.pts; IntPoint pt1 = j.pt2a, pt2 = j.pt2b; IntPoint pt3 = j.pt1a, pt4 = j.pt1b; if (!FindSegment(ref pp1a, ref pt1, ref pt2)) return false; if (outRec1 == outRec2) { //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)) return false; } else if (!FindSegment(ref pp2a, ref pt3, ref pt4)) return false; if (!GetOverlapSegment(pt1, pt2, pt3, pt4, ref pt1, ref pt2)) return false; OutPt p3, p4, 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 == p3.pt and p2.pt == p4.pt 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; return true; } else if (p1.prev == p2 && p3.next == p4) { p1.prev = p3; p3.next = p1; p2.next = p4; p4.prev = p2; return true; } else return false; //an orientation is probably wrong }
//--------------------------------------------------------------------------- private bool FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2) { OutPt p = btmPt1.prev; while (PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) p = p.prev; double dx1p = Math.Abs(GetDx(btmPt1.pt, p.pt)); p = btmPt1.next; while (PointsEqual(p.pt, btmPt1.pt) && (p != btmPt1)) p = p.next; double dx1n = Math.Abs(GetDx(btmPt1.pt, p.pt)); p = btmPt2.prev; while (PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) p = p.prev; double dx2p = Math.Abs(GetDx(btmPt2.pt, p.pt)); p = btmPt2.next; while (PointsEqual(p.pt, btmPt2.pt) && (p != btmPt2)) p = p.next; double dx2n = Math.Abs(GetDx(btmPt2.pt, p.pt)); return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); }
//------------------------------------------------------------------------------ //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf private static int PointInPolygon(IntPoint pt, OutPt op) { //returns 0 if false, +1 if true, -1 if pt ON polygon boundary int result = 0; OutPt startOp = op; int ptx = pt.X, pty = pt.Y; int poly0x = op.Pt.X, poly0y = op.Pt.Y; do { op = op.Next; int poly1x = op.Pt.X, poly1y = op.Pt.Y; if (poly1y == pty) { if ((poly1x == ptx) || (poly0y == pty && ((poly1x > ptx) == (poly0x < ptx)))) return -1; } if ((poly0y < pty) != (poly1y < pty)) { if (poly0x >= ptx) { if (poly1x > ptx) result = 1 - result; else { double d = (double)(poly0x - ptx) * (poly1y - pty) - (double)(poly1x - ptx) * (poly0y - pty); if (d == 0) return -1; if ((d > 0) == (poly1y > poly0y)) result = 1 - result; } } else { if (poly1x > ptx) { double d = (double)(poly0x - ptx) * (poly1y - pty) - (double)(poly1x - ptx) * (poly0y - pty); if (d == 0) return -1; if ((d > 0) == (poly1y > poly0y)) result = 1 - result; } } } poly0x = poly1x; poly0y = poly1y; } while (startOp != op); return result; }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ protected override void AddLocalMinPoly(Active e1, Active e2, Point pt) { base.AddLocalMinPoly(e1, e2, pt); OutRec locMinOr = e1.OutRec; (locMinOr.Pts as OutPtTri).outrec = locMinOr; UpdateHelper(locMinOr, locMinOr.Pts); if (locMinOr.Flag == OutrecFlag.Outer) { return; } //do 'keyholing' ... Active e = GetRightAdjacentHotEdge(e1); if (e == e2) { e = GetRightAdjacentHotEdge(e2); } if (e == null) { e = GetLeftAdjacentHotEdge(e1); } OutPt botLft = (e.OutRec as OutRecTri).leftOutpt; OutPt botRt = GetOutPt(e); if (botLft == null || botRt.Pt.Y < botLft.Pt.Y) { botLft = botRt; } botRt = InsertPt(botLft.Pt, botLft.Prev); OutRec botOr = (botLft as OutPtTri).outrec; if (botOr.Pts == null) { botOr = botOr.Owner; } OutPt startOp = botOr.Pts; OutPt endOp = startOp.Next; locMinOr.Flag = OutrecFlag.Outer; locMinOr.Owner = null; OutPt locMinLft = locMinOr.Pts; OutPt locMinRt = InsertPt(locMinLft.Pt, locMinLft); //locMinOr will contain the polygon to the right of the join (ascending), //and botOr will contain the polygon to the left of the join (descending). //tail . botRt . locMinRt : locMinRt is joined by botRt tail locMinRt.Next = endOp; endOp.Prev = locMinRt; botRt.Next = locMinRt; locMinRt.Prev = botRt; locMinOr.Pts = locMinRt; //locMinLft . botLft . head : locMinLft joins behind botLft (left) startOp.Next = locMinLft; locMinLft.Prev = startOp; botLft.Prev = locMinLft; locMinLft.Next = botLft; (locMinLft as OutPtTri).outrec = botOr; //ie abreviated update() Update(locMinRt, locMinOr); //updates the outrec for each op //exchange endE's ... e = botOr.EndE; botOr.EndE = locMinOr.EndE; locMinOr.EndE = e; botOr.EndE.OutRec = botOr; locMinOr.EndE.OutRec = locMinOr; //update helper info ... UpdateHelper(locMinOr, locMinRt); UpdateHelper(botOr, botOr.Pts); Triangulate(locMinOr); Triangulate(botOr); }
//------------------------------------------------------------------------------ private void AddOutPt(TEdge e, IntPoint pt) { bool ToFront = (e.side == EdgeSide.esLeft); if (e.outIdx < 0) { OutRec outRec = CreateOutRec(); e.outIdx = outRec.idx; OutPt op = new OutPt(); outRec.pts = 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; if (ToFront && PointsEqual(pt, op.pt) || (!ToFront && PointsEqual(pt, op.prev.pt))) return; op2 = new OutPt(); op2.pt = pt; op2.idx = outRec.idx; op2.next = op; op2.prev = op.prev; op2.prev.next = op2; op.prev = op2; if (ToFront) outRec.pts = op2; } }
//------------------------------------------------------------------------------ private void AddOutPt(TEdge e, TEdge altE, 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; outRec.bottomE1 = e; outRec.bottomE2 = altE; 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; if (ToFront && PointsEqual(pt, op.pt) || (!ToFront && PointsEqual(pt, op.prev.pt))) return; OutPt 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; outRec.bottomE1 = e; outRec.bottomE2 = altE; } op2.next = op; op2.prev = op.prev; op2.prev.next = op2; op.prev = op2; if (ToFront) outRec.pts = op2; } }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ private static bool Poly2ContainsPoly1(OutPt outPt1, OutPt outPt2) { OutPt op = outPt1; do { //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon int res = PointInPolygon(op.Pt, outPt2); if (res >= 0) return res > 0; op = op.Next; } while (op != outPt1); return true; }
//------------------------------------------------------------------------------ private OutPt GetBottomPt(OutPt pp) { OutPt dups = null; OutPt p = pp.next; while (p != pp) { if (p.pt.Y > pp.pt.Y) { pp = p; dups = null; } else if (p.pt.Y == pp.pt.Y && p.pt.X <= pp.pt.X) { if (p.pt.X < pp.pt.X) { dups = null; pp = p; } else { if (p.next != pp && p.prev != pp) dups = p; } } p = p.next; } if (dups != null) { //there appears to be at least 2 vertices at bottomPt so ... while (dups != p) { if (!FirstIsBottomPt(p, dups)) pp = dups; dups = dups.next; while (!PointsEqual(dups.pt, pp.pt)) dups = dups.next; } } return pp; }
//------------------------------------------------------------------------------ private void AddJoin(OutPt Op1, OutPt Op2, IntPoint OffPt) { Join j = new Join(); j.OutPt1 = Op1; j.OutPt2 = Op2; j.OffPt = OffPt; m_Joins.Add(j); }
//---------------------------------------------------------------------- private void FixupJoinRecs(JoinRec j, OutPt pt, int startIdx) { for (int k = startIdx; k < m_Joins.Count; k++) { JoinRec j2 = m_Joins[k]; if (j2.poly1Idx == j.poly1Idx && PointIsVertex(j2.pt1a, pt)) j2.poly1Idx = j.poly2Idx; if (j2.poly2Idx == j.poly1Idx && PointIsVertex(j2.pt2a, pt)) j2.poly2Idx = j.poly2Idx; } }
//------------------------------------------------------------------------------ private void AddGhostJoin(OutPt Op, IntPoint OffPt) { Join j = new Join(); j.OutPt1 = Op; j.OffPt = OffPt; m_GhostJoins.Add(j); }
//------------------------------------------------------------------------------ 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 OutPt AddOutPt(TEdge e, IntPoint pt) { bool ToFront = (e.Side == EdgeSide.esLeft); if( e.OutIdx < 0 ) { OutRec outRec = CreateOutRec(); outRec.IsOpen = (e.WindDelta == 0); OutPt newOp = new OutPt(); outRec.Pts = newOp; newOp.Idx = outRec.Idx; newOp.Pt = pt; newOp.Next = newOp; newOp.Prev = newOp; if (!outRec.IsOpen) SetHoleState(e, outRec); #if use_xyz if (pt == e.Bot) newOp.Pt = e.Bot; else if (pt == e.Top) newOp.Pt = e.Top; else SetZ(ref newOp.Pt, e); #endif e.OutIdx = outRec.Idx; //nb: do this after SetZ ! return newOp; } else { OutRec outRec = m_PolyOuts[e.OutIdx]; //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' OutPt op = outRec.Pts; if (ToFront && pt == op.Pt) return op; else if (!ToFront && pt == op.Prev.Pt) return op.Prev; OutPt newOp = new OutPt(); newOp.Idx = outRec.Idx; newOp.Pt = pt; newOp.Next = op; newOp.Prev = op.Prev; newOp.Prev.Next = newOp; op.Prev = newOp; if (ToFront) outRec.Pts = newOp; #if use_xyz if (pt == e.Bot) newOp.Pt = e.Bot; else if (pt == e.Top) newOp.Pt = e.Top; else SetZ(ref newOp.Pt, e); #endif return newOp; } }
//------------------------------------------------------------------------------ private int PointCount(OutPt pts) { if (pts == null) return 0; int result = 0; OutPt p = pts; do { result++; p = p.next; } while (p != pts); return result; }
//--------------------------------------------------------------------------- private bool FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2) { OutPt p = btmPt1.Prev; while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) p = p.Prev; double dx1p = Math.Abs(GetDx(btmPt1.Pt, p.Pt)); p = btmPt1.Next; while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) p = p.Next; double dx1n = Math.Abs(GetDx(btmPt1.Pt, p.Pt)); p = btmPt2.Prev; while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) p = p.Prev; double dx2p = Math.Abs(GetDx(btmPt2.Pt, p.Pt)); p = btmPt2.Next; while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) p = p.Next; double dx2n = Math.Abs(GetDx(btmPt2.Pt, p.Pt)); return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); }
//------------------------------------------------------------------------------ private void ReversePolyPtLinks(OutPt pp) { OutPt pp1; OutPt pp2; pp1 = pp; do { pp2 = pp1.next; pp1.next = pp1.prev; pp1.prev = pp2; pp1 = pp2; } while (pp1 != pp); }
//------------------------------------------------------------------------------ private void ReversePolyPtLinks(OutPt pp) { if (pp == null) return; OutPt pp1; OutPt pp2; pp1 = pp; do { pp2 = pp1.Next; pp1.Next = pp1.Prev; pp1.Prev = pp2; pp1 = pp2; } while (pp1 != pp); }
//------------------------------------------------------------------------------ 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; }
//------------------------------------------------------------------------------ OutPt DupOutPt(OutPt outPt, bool InsertAfter) { OutPt result = new OutPt(); result.Pt = outPt.Pt; result.Idx = outPt.Idx; if (InsertAfter) { result.Next = outPt.Next; result.Prev = outPt; outPt.Next.Prev = result; outPt.Next = result; } else { result.Prev = outPt.Prev; result.Next = outPt; outPt.Prev.Next = result; outPt.Prev = result; } return result; }
//------------------------------------------------------------------------------ private void DisposeOutPts(OutPt pp) { if (pp == null) return; OutPt tmpPp = null; pp.prev.next = null; while (pp != null) { tmpPp = pp; pp = pp.next; tmpPp = null; } }
//------------------------------------------------------------------------------ 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; }