/// <summary> /// Returns the routes (multiple lines or part polygons) either inside or /// outside the polygons provided. These are based on the intersections /// of the 2 polygons e.g. the routes / part polygons of one inside or /// outside the other. /// </summary> /// <param name="Poly1">The first polygon.</param> /// <param name="bP1RoutesInside">True if routes inside the second polygon are /// required for the first polygon.</param> /// <param name="Poly2">The second polygon.</param> /// <param name="bP2RoutesInside">True if routes inside the first polygon are /// required for the second polygon.</param> /// <param name="Routes1">Output. Set of lines for the first polygon.</param> /// <param name="Routes2">Output. Set of lines for the second polygon.</param> public static void GetRoutes(C2DPolyBase Poly1, bool bP1RoutesInside, C2DPolyBase Poly2, bool bP2RoutesInside, C2DLineBaseSetSet Routes1, C2DLineBaseSetSet Routes2) { // Set up a collection of intersected points, and corresponding indexes. var IntPoints = new C2DPointSet(); var Indexes1 = new List <int>(); var Indexes2 = new List <int>(); // Use the line collections in each shape to find the intersections between them. Poly1.Lines.GetIntersections(Poly2.Lines, IntPoints, Indexes1, Indexes2, Poly1.BoundingRect, Poly2.BoundingRect); // Make a copy of the point set because this will be sorted by line index in the // Get routes function later. We need an unsorted set for each polygon. var IntPointsCopy = new C2DPointSet(); IntPointsCopy.MakeCopy(IntPoints); // Find out whether the first poly starts inside the second. bool bP1StartInside = Poly2.Contains(Poly1.Lines[0].GetPointFrom()); // Find out if poly 2 starts inside poly 1. bool bP2StartInside = Poly1.Contains(Poly2.Lines[0].GetPointFrom()); if (IntPoints.Count == 0 && !bP1StartInside && !bP2StartInside) { return; // No interaction between the 2. } // Get the routes of poly 1 inside / outside the other, passing the unsorted // intersection points and polygon1 intersection indexes. Poly1.GetRoutes(IntPoints, Indexes1, Routes1, bP1StartInside, bP1RoutesInside); // Do the same for poly 2 but pass it the unsorted copy of the intersection points // So that they correspond to the indexes. Poly2.GetRoutes(IntPointsCopy, Indexes2, Routes2, bP2StartInside, bP2RoutesInside); }
/// <summary> /// Adds a set of holes to the current set of polygons as holes within them. /// </summary> /// <param name="pOther">The polygon set to add as holes.</param> public void AddKnownHoles(List <C2DPolyBase> pOther) { if (Count != 0) { while (pOther.Count > 0) { C2DPolyBase pLast = pOther[pOther.Count - 1]; pOther.RemoveAt(pOther.Count - 1); if (pLast.Lines.Count > 0) { int i = Count - 1; bool bFound = false; while (i > 0 && !bFound) { if (this[i].Contains(pLast.Lines[0].GetPointFrom())) { this[i].AddHole(pLast); bFound = true; } i--; } if (!bFound) { this[0].AddHole(pLast); } } } } }
/// <summary> /// Constructor. /// </summary> /// <param name="Other">Other polygon to set this to.</param> public C2DHoledPolyBase(C2DHoledPolyBase Other) { _Rim = new C2DPolyBase(Other.Rim); for (var i = 0; i < Other.HoleCount; i++) { _Holes.Add(new C2DPolyBase(Other.GetHole(i))); } }
/// <summary> /// Constructor. /// </summary> /// <param name="Other">Other polygon to set this to.</param> public C2DHoledPolyBase(C2DHoledPolyBase Other) { _Rim = new C2DPolyBase( Other.Rim); for (int i = 0 ; i < Other.HoleCount; i++) { _Holes.Add(new C2DPolyBase(Other.GetHole(i))); } }
/// <summary> /// Distance from the polygon provided. /// </summary> /// <param name="Poly">Polygon to find the distance to.</param> /// <param name="ptOnThis">Closest point on this to recieve the result.</param> /// <param name="ptOnOther">Closest point on the other to recieve the result.</param> public double Distance(C2DPolyBase Poly, C2DPoint ptOnThis, C2DPoint ptOnOther) { var ptOnThisResult = new C2DPoint(); var ptOnOtherResult = new C2DPoint(); var dResult = _Rim.Distance(Poly, ptOnThis, ptOnOther); if (dResult == 0) { return(0); } ptOnThisResult.Set(ptOnThis); ptOnOtherResult.Set(ptOnOther); var bInside = dResult < 0; dResult = Math.Abs(dResult); for (var i = 0; i < _Holes.Count; i++) { var dDist = _Holes[i].Distance(Poly, ptOnThis, ptOnOther); if (dDist == 0) { return(0); } if (dDist < 0) { bInside = false; } if (Math.Abs(dDist) < dResult) { ptOnThisResult.Set(ptOnThis); ptOnOtherResult.Set(ptOnOther); dResult = Math.Abs(dDist); } } ptOnThis.Set(ptOnThisResult); ptOnOther.Set(ptOnOtherResult); if (bInside) { return(dResult); } else { return(-dResult); } }
/// <summary> /// Hole assignment. /// </summary> public new void SetHole(int i, C2DPolyBase Poly) { if (Poly is C2DPolyArc) { _Holes[i] = Poly; } else { Debug.Assert(false, "Invalid Hole type"); } }
/// <summary> /// Hole addition. /// </summary> public new void AddHole(C2DPolyBase Poly) { if (Poly is C2DPolyArc) { _Holes.Add(Poly); } else { Debug.Assert(false, "Invalid Hole type"); } }
/// <summary> /// Creates a path based on a polygon. /// </summary> private System.Drawing.Drawing2D.GraphicsPath CreatePath(C2DPolyBase Poly) { System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath(); if (Poly.Lines.Count == 0) { return(gp); } for (int i = 0; i < Poly.Lines.Count; i++) { if (Poly.Lines[i] is C2DLine) { C2DPoint ptFrom = Poly.Lines[i].GetPointFrom(); C2DPoint ptTo = Poly.Lines[i].GetPointTo(); ScaleAndOffSet(ptFrom); ScaleAndOffSet(ptTo); gp.AddLine((int)ptFrom.x, (int)ptFrom.y, (int)ptTo.x, (int)ptTo.y); } else if (Poly.Lines[i] is C2DArc) { C2DRect Rect = new C2DRect(); int nStartAngle = 0; int nSweepAngle = 0; GetArcParameters(Poly.Lines[i] as C2DArc, Rect, ref nStartAngle, ref nSweepAngle); if (nSweepAngle == 0) { nSweepAngle = 1; } int Width = (int)Rect.Width(); if (Width == 0) { Width = 1; } int Height = (int)Rect.Height(); if (Height == 0) { Height = 1; } gp.AddArc((int)Rect.TopLeft.x, (int)Rect.BottomRight.y, Width, Height, nStartAngle, nSweepAngle); } } gp.CloseFigure(); return(gp); }
/// <summary> /// Assigment sets from another /// </summary> /// <param name="Other">The other polygon.</param> public void Set(C2DPolyBase Other) { Clear(); Lines.MakeValueCopy(Other.Lines); BoundingRect.Set(Other.BoundingRect); for (var i = 0; i < Other.LineRects.Count; i++) { LineRects.Add(new C2DRect(Other.LineRects[i])); } }
/// <summary> /// Assigment sets from another /// </summary> /// <param name="Other">The other polygon.</param> public void Set(C2DPolyBase Other) { Clear(); Lines.MakeValueCopy(Other.Lines); BoundingRect.Set(Other.BoundingRect); for (int i = 0 ; i < Other.LineRects.Count ; i ++) { LineRects.Add(new C2DRect( Other.LineRects[i]) ); } }
/// <summary> /// Draws a polygon /// </summary> public void Draw(C2DPolyBase Poly, Graphics graphics, Pen pen) { for (int i = 0; i < Poly.Lines.Count; i++) { if (Poly.Lines[i] is C2DLine) { Draw(Poly.Lines[i] as C2DLine, graphics, pen); } else if (Poly.Lines[i] is C2DArc) { Draw(Poly.Lines[i] as C2DArc, graphics, pen); } } }
/// <summary> /// True if this overlaps the other. /// </summary> /// <param name="Other">Other polygon to test for.</param> public bool Overlaps(C2DPolyBase Other) { if (!_Rim.Overlaps(Other)) { return(false); } for (var i = 0; i < _Holes.Count; i++) { if (_Holes[i].Contains(Other)) { return(false); } } return(true); }
/// <summary> /// True if this crosses the other polygon. /// </summary> /// <param name="Poly">Polygon to test for.</param> public bool Crosses(C2DPolyBase Poly) { if (_Rim.Crosses(Poly)) { return(true); } for (var i = 0; i < _Holes.Count; i++) { if (_Holes[i].Crosses(Poly)) { return(true); } } return(false); }
/// <summary> /// Polygon entirely inside test. /// </summary> /// <param name="Polygon">Polygon to test for.</param> public bool Contains(C2DPolyBase Polygon) { if (!_Rim.Contains(Polygon)) { return(false); } for (var i = 0; i < _Holes.Count; i++) { if (_Holes[i].Overlaps(Polygon)) { return(false); } } return(true); }
/// <summary> /// True if it crosses the other. /// </summary> /// <param name="Other">The other polygon.</param> public bool Crosses(C2DPolyBase Other) { if (!BoundingRect.Overlaps(Other.BoundingRect)) { return(false); } var Temp = new List <C2DPoint>(); for (var i = 0; i < Lines.Count; i++) { if (Other.Crosses(Lines[i], Temp)) { return(true); } } return(false); }
/// <summary> /// True if this overlaps the other. /// </summary> /// <param name="Other">The other polygon.</param> public bool Overlaps(C2DPolyBase Other) { if (Lines.Count == 0 || Other.Lines.Count == 0) { return(false); } if (Other.Contains(Lines[0].GetPointTo())) { return(true); } if (Crosses(Other)) { return(true); } return(this.Contains(Other.Lines[0].GetPointTo())); }
/// <summary> /// True if it entirely contains the other. /// </summary> /// <param name="Other">The other polygon.</param> public bool Contains(C2DPolyBase Other) { if (Other.Lines.Count == 0) { return(false); } if (!BoundingRect.Contains(Other.BoundingRect)) { return(false); } if (!Contains(Other.Lines[0].GetPointFrom())) { return(false); } return(!this.Crosses(Other)); }
/// <summary> /// Intersection with another. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="IntersectionPts">Output. The intersection points.</param> public bool Crosses(C2DPolyBase Other, List <C2DPoint> IntersectionPts) { if (!BoundingRect.Overlaps(Other.BoundingRect)) { return(false); } var IntPtsTemp = new List <C2DPoint>(); var Index1 = new List <int>(); var Index2 = new List <int>(); Lines.GetIntersections(Other.Lines, IntPtsTemp, Index1, Index2, BoundingRect, Other.BoundingRect); var bResult = IntPtsTemp.Count > 0; IntersectionPts.InsertRange(0, IntPtsTemp); return(bResult); }
/// <summary> /// Creates a path based on a polygon. /// </summary> private System.Drawing.Drawing2D.GraphicsPath CreatePath( C2DPolyBase Poly) { System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath(); if (Poly.Lines.Count == 0) return gp; for (int i = 0; i < Poly.Lines.Count; i++) { if (Poly.Lines[i] is C2DLine) { C2DPoint ptFrom = Poly.Lines[i].GetPointFrom(); C2DPoint ptTo = Poly.Lines[i].GetPointTo(); ScaleAndOffSet(ptFrom); ScaleAndOffSet(ptTo); gp.AddLine((int)ptFrom.x, (int)ptFrom.y, (int)ptTo.x, (int)ptTo.y); } else if (Poly.Lines[i] is C2DArc) { C2DRect Rect = new C2DRect(); int nStartAngle = 0; int nSweepAngle = 0; GetArcParameters(Poly.Lines[i] as C2DArc, Rect, ref nStartAngle, ref nSweepAngle); if (nSweepAngle == 0) nSweepAngle = 1; int Width = (int)Rect.Width(); if (Width == 0) Width = 1; int Height = (int)Rect.Height(); if (Height == 0) Height = 1; gp.AddArc((int)Rect.TopLeft.x, (int)Rect.BottomRight.y, Width, Height, nStartAngle, nSweepAngle); } } gp.CloseFigure(); return gp; }
/// <summary> /// True if this crosses the other polygon. /// </summary> /// <param name="Poly">Polygon to test for.</param> public bool Crosses(C2DPolyBase Poly) { if (_Rim.Crosses(Poly)) return true; for (int i = 0 ; i < _Holes.Count; i++) { if(_Holes[i].Crosses(Poly)) return true; } return false; }
/// <summary> /// Distance from the polygon provided. /// </summary> /// <param name="Poly">Polygon to find the distance to.</param> /// <param name="ptOnThis">Closest point on this to recieve the result.</param> /// <param name="ptOnOther">Closest point on the other to recieve the result.</param> public double Distance(C2DPolyBase Poly, C2DPoint ptOnThis, C2DPoint ptOnOther) { C2DPoint ptOnThisResult = new C2DPoint(); C2DPoint ptOnOtherResult = new C2DPoint(); double dResult = _Rim.Distance(Poly, ptOnThis, ptOnOther); if (dResult == 0) return 0; ptOnThisResult.Set(ptOnThis); ptOnOtherResult.Set(ptOnOther); bool bInside = dResult < 0; dResult = Math.Abs(dResult); for (int i = 0; i < _Holes.Count; i++) { double dDist = _Holes[i].Distance(Poly, ptOnThis, ptOnOther); if (dDist == 0) return 0; if (dDist < 0) bInside = false; if (Math.Abs(dDist) < dResult) { ptOnThisResult.Set(ptOnThis); ptOnOtherResult.Set(ptOnOther); dResult = Math.Abs(dDist); } } ptOnThis.Set(ptOnThisResult); ptOnOther.Set(ptOnOtherResult); if (bInside) return dResult; else return - dResult; }
/// <summary> /// Hole addition. /// </summary> public void AddHole(C2DPolyBase Poly) { _Holes.Add(Poly); }
/// <summary> /// Returns the routes (multiple lines or part polygons) either inside or /// outside the polygons provided. These are based on the intersections /// of the 2 polygons e.g. the routes / part polygons of one inside or /// outside the other. /// </summary> /// <param name="Poly1">The first polygon.</param> /// <param name="bP1RoutesInside">True if routes inside the second polygon are /// required for the first polygon.</param> /// <param name="Poly2">The second polygon.</param> /// <param name="bP2RoutesInside">True if routes inside the first polygon are /// required for the second polygon.</param> /// <param name="Routes1">Output. Set of lines for the first polygon.</param> /// <param name="Routes2">Output. Set of lines for the second polygon.</param> public static void GetRoutes(C2DPolyBase Poly1, bool bP1RoutesInside, C2DPolyBase Poly2, bool bP2RoutesInside, C2DLineBaseSetSet Routes1, C2DLineBaseSetSet Routes2) { // Set up a collection of intersected points, and corresponding indexes. C2DPointSet IntPoints = new C2DPointSet(); List<int> Indexes1 = new List<int>(); List<int> Indexes2 = new List<int>(); // Use the line collections in each shape to find the intersections between them. Poly1.Lines.GetIntersections(Poly2.Lines, IntPoints, Indexes1, Indexes2, Poly1.BoundingRect, Poly2.BoundingRect); // Make a copy of the point set because this will be sorted by line index in the // Get routes function later. We need an unsorted set for each polygon. C2DPointSet IntPointsCopy = new C2DPointSet(); IntPointsCopy.MakeCopy(IntPoints); // Find out whether the first poly starts inside the second. bool bP1StartInside = Poly2.Contains(Poly1.Lines[0].GetPointFrom()); // Find out if poly 2 starts inside poly 1. bool bP2StartInside = Poly1.Contains(Poly2.Lines[0].GetPointFrom()); if (IntPoints.Count == 0 && !bP1StartInside && !bP2StartInside) return; // No interaction between the 2. // Get the routes of poly 1 inside / outside the other, passing the unsorted // intersection points and polygon1 intersection indexes. Poly1.GetRoutes(IntPoints, Indexes1, Routes1, bP1StartInside, bP1RoutesInside); // Do the same for poly 2 but pass it the unsorted copy of the intersection points // So that they correspond to the indexes. Poly2.GetRoutes(IntPointsCopy, Indexes2, Routes2, bP2StartInside, bP2RoutesInside); }
/// <summary> /// Returns the overlaps of this with another. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="Polygons">The output polygons.</param> /// <param name="grid">The degenerate settings.</param> public void GetOverlaps(C2DPolyBase Other, List<C2DHoledPolyBase> Polygons, CGrid grid) { GetBoolean(Other, Polygons, true, true, grid); }
/// <summary> /// True if this overlaps the other. /// </summary> /// <param name="Other">The other polygon.</param> public bool Overlaps(C2DPolyBase Other) { if (Lines.Count == 0 || Other.Lines.Count == 0) return false; if (Other.Contains(Lines[0].GetPointTo())) return true; if (Crosses(Other)) return true; return (this.Contains(Other.Lines[0].GetPointTo())); }
/// <summary> /// Hole addition. /// </summary> public new void AddHole(C2DPolyBase Poly) { if (Poly is C2DPolygon) { _Holes.Add(Poly); } else { Debug.Assert(false, "Invalid Hole type"); } }
/// <summary> /// Hole assignment. /// </summary> public void SetHole(int i, C2DPolyBase Poly) { _Holes[i] = Poly; }
/// <summary> /// Distance of the poly from the shape. /// </summary> /// <param name="Other">The other polygon test.</param> /// <param name="ptOnThis">Output. The closest point on this.</param> /// <param name="ptOnOther">The closest point on the other.</param> public double Distance(C2DPolyBase Other, C2DPoint ptOnThis, C2DPoint ptOnOther) { if (Lines.Count == 0) { return(0); } if (Other.Lines.Count == 0) { return(0); } if (Other.LineRects.Count != Other.Lines.Count) { return(0); } if (Lines.Count != LineRects.Count) { return(0); } // First we find the closest line rect to the other's bounding rectangle. var usThisClosestLineGuess = 0; var OtherBoundingRect = Other.BoundingRect; double dClosestDist = LineRects[0].Distance(OtherBoundingRect); for (var i = 1; i < LineRects.Count; i++) { double dDist = LineRects[i].Distance(OtherBoundingRect); if (dDist < dClosestDist) { dClosestDist = dDist; usThisClosestLineGuess = i; } } // Now cycle through all the other poly's line rects to find the closest to the // guessed at closest line on this. var usOtherClosestLineGuess = 0; dClosestDist = Other.LineRects[0].Distance(LineRects[usThisClosestLineGuess]); for (var j = 1; j < Other.LineRects.Count; j++) { double dDist = Other.LineRects[j].Distance(LineRects[usThisClosestLineGuess]); if (dDist < dClosestDist) { dClosestDist = dDist; usOtherClosestLineGuess = j; } } // Now we have a guess at the 2 closest lines. double dMinDistGuess = Lines[usThisClosestLineGuess].Distance( Other.Lines[usOtherClosestLineGuess], ptOnThis, ptOnOther); // If its 0 then return 0. if (dMinDistGuess == 0) { return(0); } var ptOnThisTemp = new C2DPoint(); var ptOnOtherTemp = new C2DPoint(); // Now go through all of our line rects and only check further if they are closer // to the other's bounding rect than the min guess. for (var i = 0; i < Lines.Count; i++) { if (LineRects[i].Distance(OtherBoundingRect) < dMinDistGuess) { for (var j = 0; j < Other.Lines.Count; j++) { double dDist = Lines[i].Distance(Other.Lines[j], ptOnThisTemp, ptOnOtherTemp); if (dDist < dMinDistGuess) { ptOnThis.Set(ptOnThisTemp); ptOnOther.Set(ptOnOtherTemp); if (dDist == 0) { return(0); } dMinDistGuess = dDist; } } } } // if we are here, there is no intersection but the other could be inside this or vice-versa if (BoundingRect.Contains(Other.BoundingRect) && Contains(ptOnOtherTemp)) { dMinDistGuess *= -1.0; } else if (Other.BoundingRect.Contains(BoundingRect) && Other.Contains(ptOnThisTemp)) { dMinDistGuess *= -1.0; } return(dMinDistGuess); }
/// <summary> /// Constructor sets from another /// </summary> /// <param name="Other">The other polygon.</param> public C2DPolyBase(C2DPolyBase Other) { Set(Other); }
/// <summary> /// Constructor. /// </summary> /// <param name="Other">Other polygon to set this to.</param> public C2DHoledPolyArc(C2DPolyBase Other) { _Rim = new C2DPolyArc(Other); }
/// <summary> /// Constructor. /// </summary> /// <param name="Other">Other polygon to set this to.</param> public C2DHoledPolygon(C2DPolyBase Other) { _Rim = new C2DPolygon(Other); }
/// <summary> /// Draws a polygon filled. /// </summary> public void DrawFilled(C2DPolyBase Poly, Graphics graphics, Brush brush) { System.Drawing.Drawing2D.GraphicsPath gp = CreatePath(Poly); graphics.FillPath(brush, gp); }
/// <summary> /// Returns the union of this with another. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="Polygons">The output polygons.</param> /// <param name="grid">The degenerate settings.</param> public void GetUnion(C2DPolyBase Other, List<C2DHoledPolyBase> Polygons, CGrid grid) { GetBoolean(Other, Polygons, false, false, grid); }
/// <summary> /// Gets the boolean operation with the other. e.g. union / intersection. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="HoledPolys">The set to recieve the result.</param> /// <param name="bThisInside">The flag to indicate routes inside.</param> /// <param name="bOtherInside">The flag to indicate routes inside for the other.</param> /// <param name="grid">The degenerate settings.</param> public void GetBoolean(C2DPolyBase Other, List <C2DHoledPolyBase> HoledPolys, bool bThisInside, bool bOtherInside, CGrid grid) { if (BoundingRect.Overlaps(Other.BoundingRect)) { switch (grid.DegenerateHandling) { case CGrid.eDegenerateHandling.None: { var Routes1 = new C2DLineBaseSetSet(); var Routes2 = new C2DLineBaseSetSet(); C2DPolyBase.GetRoutes(this, bThisInside, Other, bOtherInside, Routes1, Routes2); Routes1.ExtractAllOf(Routes2); if (Routes1.Count > 0) { // Add all the joining routes together to form closed routes Routes1.MergeJoining(); // Set up some temporary polygons. var Polygons = new List <C2DPolyBase>(); // Turn the routes into polygons. for (var i = Routes1.Count - 1; i >= 0; i--) { if (Routes1[i].IsClosed(true) && Routes1[i].Count > 2) { Polygons.Add(new C2DPolyBase()); Polygons[Polygons.Count - 1].CreateDirect(Routes1[i]); } else { // Debug.Assert(false); grid.LogDegenerateError(); } } // Set up some temporary holed polygons var NewComPolys = new C2DHoledPolyBaseSet(); // Turn the set of polygons into holed polygons. Not needed for intersection. if (!(bThisInside && bOtherInside)) { C2DHoledPolyBase.PolygonsToHoledPolygons(NewComPolys, Polygons); if (NewComPolys.Count != 1) { // Debug.Assert(false); grid.LogDegenerateError(); } } else { for (var i = 0; i < Polygons.Count; i++) { HoledPolys.Add(new C2DHoledPolyBase(Polygons[i])); } } // Now add them all to the provided set. for (var i = 0; i < NewComPolys.Count; i++) { HoledPolys.Add(NewComPolys[i]); } } } break; case CGrid.eDegenerateHandling.RandomPerturbation: { var OtherCopy = new C2DPolyBase(Other); OtherCopy.RandomPerturb(); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; GetBoolean(OtherCopy, HoledPolys, bThisInside, bOtherInside, grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.RandomPerturbation; } break; case CGrid.eDegenerateHandling.DynamicGrid: { var Rect = new C2DRect(); if (this.BoundingRect.Overlaps(Other.BoundingRect, Rect)) { var dOldGrid = grid.GridSize; grid.SetToMinGridSize(Rect, false); grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGrid; GetBoolean(Other, HoledPolys, bThisInside, bOtherInside, grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.DynamicGrid; } } break; case CGrid.eDegenerateHandling.PreDefinedGrid: { var P1 = new C2DPolyBase(this); var P2 = new C2DPolyBase(Other); P1.SnapToGrid(grid); P2.SnapToGrid(grid); var V1 = new C2DVector(P1.BoundingRect.TopLeft, P2.BoundingRect.TopLeft); var dPerturbation = grid.GridSize; // ensure it snaps back to original grid positions. if (V1.i > 0) { V1.i = dPerturbation; } else { V1.i = -dPerturbation; // move away slightly if possible } if (V1.j > 0) { V1.j = dPerturbation; } else { V1.j = -dPerturbation; // move away slightly if possible } V1.i *= 0.411923; // ensure it snaps back to original grid positions. V1.j *= 0.313131; // ensure it snaps back to original grid positions. P2.Move(V1); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; P1.GetBoolean(P2, HoledPolys, bThisInside, bOtherInside, grid); for (var i = 0; i < HoledPolys.Count; i++) { HoledPolys[i].SnapToGrid(grid); } grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGrid; } break; case CGrid.eDegenerateHandling.PreDefinedGridPreSnapped: { var P2 = new C2DPolyBase(Other); var V1 = new C2DVector(this.BoundingRect.TopLeft, P2.BoundingRect.TopLeft); var dPerturbation = grid.GridSize; // ensure it snaps back to original grid positions. if (V1.i > 0) { V1.i = dPerturbation; } else { V1.i = -dPerturbation; // move away slightly if possible } if (V1.j > 0) { V1.j = dPerturbation; } else { V1.j = -dPerturbation; // move away slightly if possible } V1.i *= 0.411923; // ensure it snaps back to original grid positions. V1.j *= 0.313131; // ensure it snaps back to original grid positions. P2.Move(V1); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; GetBoolean(P2, HoledPolys, bThisInside, bOtherInside, grid); for (var i = 0; i < HoledPolys.Count; i++) { HoledPolys[i].SnapToGrid(grid); } grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGridPreSnapped; } break; } } }
/// <summary> /// Gets the boolean operation with the other. e.g. union / intersection. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="HoledPolys">The set to recieve the result.</param> /// <param name="bThisInside">The flag to indicate routes inside.</param> /// <param name="bOtherInside">The flag to indicate routes inside for the other.</param> /// <param name="grid">The degenerate settings.</param> public void GetBoolean(C2DPolyBase Other, List<C2DHoledPolyBase> HoledPolys, bool bThisInside, bool bOtherInside, CGrid grid) { if (BoundingRect.Overlaps(Other.BoundingRect )) { switch (grid.DegenerateHandling) { case CGrid.eDegenerateHandling.None: { C2DLineBaseSetSet Routes1 = new C2DLineBaseSetSet(); C2DLineBaseSetSet Routes2 = new C2DLineBaseSetSet(); C2DPolyBase.GetRoutes( this, bThisInside, Other, bOtherInside, Routes1, Routes2); Routes1.ExtractAllOf(Routes2); if (Routes1.Count > 0) { // Add all the joining routes together to form closed routes Routes1.MergeJoining(); // Set up some temporary polygons. List<C2DPolyBase> Polygons = new List<C2DPolyBase>(); // Turn the routes into polygons. for (int i = Routes1.Count - 1; i >= 0; i--) { if (Routes1[i].IsClosed(true) && Routes1[i].Count > 2) { Polygons.Add(new C2DPolyBase()); Polygons[Polygons.Count - 1].CreateDirect( Routes1[i]); } else { // Debug.Assert(false); grid.LogDegenerateError(); } } // Set up some temporary holed polygons C2DHoledPolyBaseSet NewComPolys = new C2DHoledPolyBaseSet(); // Turn the set of polygons into holed polygons. Not needed for intersection. if (!(bThisInside && bOtherInside)) { C2DHoledPolyBase.PolygonsToHoledPolygons(NewComPolys, Polygons); if (NewComPolys.Count != 1) { // Debug.Assert(false); grid.LogDegenerateError(); } } else { for (int i = 0; i < Polygons.Count; i++) HoledPolys.Add(new C2DHoledPolyBase(Polygons[i])); } // Now add them all to the provided set. for (int i = 0 ; i < NewComPolys.Count; i++) HoledPolys.Add(NewComPolys[i]); } } break; case CGrid.eDegenerateHandling.RandomPerturbation: { C2DPolyBase OtherCopy = new C2DPolyBase(Other); OtherCopy.RandomPerturb(); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; GetBoolean( OtherCopy, HoledPolys, bThisInside, bOtherInside, grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.RandomPerturbation; } break; case CGrid.eDegenerateHandling.DynamicGrid: { C2DRect Rect = new C2DRect(); if (this.BoundingRect.Overlaps(Other.BoundingRect, Rect)) { double dOldGrid = grid.GridSize; grid.SetToMinGridSize(Rect, false); grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGrid; GetBoolean( Other, HoledPolys, bThisInside, bOtherInside, grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.DynamicGrid; } } break; case CGrid.eDegenerateHandling.PreDefinedGrid: { C2DPolyBase P1 = new C2DPolyBase(this); C2DPolyBase P2 = new C2DPolyBase(Other); P1.SnapToGrid(grid); P2.SnapToGrid(grid); C2DVector V1 = new C2DVector( P1.BoundingRect.TopLeft, P2.BoundingRect.TopLeft); double dPerturbation = grid.GridSize; // ensure it snaps back to original grid positions. if(V1.i > 0) V1.i = dPerturbation; else V1.i = -dPerturbation; // move away slightly if possible if(V1.j > 0) V1.j = dPerturbation; else V1.j = -dPerturbation; // move away slightly if possible V1.i *= 0.411923;// ensure it snaps back to original grid positions. V1.j *= 0.313131;// ensure it snaps back to original grid positions. P2.Move( V1 ); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; P1.GetBoolean( P2, HoledPolys, bThisInside, bOtherInside, grid); for (int i = 0; i < HoledPolys.Count; i++) HoledPolys[i].SnapToGrid(grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGrid; } break; case CGrid.eDegenerateHandling.PreDefinedGridPreSnapped: { C2DPolyBase P2 = new C2DPolyBase(Other); C2DVector V1 = new C2DVector( this.BoundingRect.TopLeft, P2.BoundingRect.TopLeft); double dPerturbation = grid.GridSize; // ensure it snaps back to original grid positions. if (V1.i > 0) V1.i = dPerturbation; else V1.i = -dPerturbation; // move away slightly if possible if (V1.j > 0) V1.j = dPerturbation; else V1.j = -dPerturbation; // move away slightly if possible V1.i *= 0.411923;// ensure it snaps back to original grid positions. V1.j *= 0.313131;// ensure it snaps back to original grid positions. P2.Move( V1 ); grid.DegenerateHandling = CGrid.eDegenerateHandling.None; GetBoolean( P2, HoledPolys, bThisInside, bOtherInside, grid); for (int i = 0; i < HoledPolys.Count; i++) HoledPolys[i].SnapToGrid(grid); grid.DegenerateHandling = CGrid.eDegenerateHandling.PreDefinedGridPreSnapped; } break; } } }
/// <summary> /// True if it entirely contains the other. /// </summary> /// <param name="Other">The other polygon.</param> public bool Contains(C2DPolyBase Other) { if (Other.Lines.Count == 0) return false; if (!BoundingRect.Contains(Other.BoundingRect)) return false; if (!Contains(Other.Lines[0].GetPointFrom())) return false; return !this.Crosses(Other); }
/// <summary> /// Distance of the poly from the shape. /// </summary> /// <param name="Other">The other polygon test.</param> /// <param name="ptOnThis">Output. The closest point on this.</param> /// <param name="ptOnOther">The closest point on the other.</param> public double Distance(C2DPolyBase Other, C2DPoint ptOnThis, C2DPoint ptOnOther) { if (Lines.Count == 0) return 0; if (Other.Lines.Count == 0) return 0; if (Other.LineRects.Count != Other.Lines.Count) return 0; if (Lines.Count != LineRects.Count) return 0; // First we find the closest line rect to the other's bounding rectangle. int usThisClosestLineGuess = 0; C2DRect OtherBoundingRect = Other.BoundingRect; double dClosestDist = LineRects[0].Distance(OtherBoundingRect); for (int i = 1; i < LineRects.Count; i++) { double dDist = LineRects[i].Distance(OtherBoundingRect); if (dDist < dClosestDist) { dClosestDist = dDist; usThisClosestLineGuess = i; } } // Now cycle through all the other poly's line rects to find the closest to the // guessed at closest line on this. int usOtherClosestLineGuess = 0; dClosestDist = Other.LineRects[0].Distance(LineRects[usThisClosestLineGuess]); for (int j = 1; j < Other.LineRects.Count; j++) { double dDist = Other.LineRects[j].Distance(LineRects[usThisClosestLineGuess]); if (dDist < dClosestDist) { dClosestDist = dDist; usOtherClosestLineGuess = j; } } // Now we have a guess at the 2 closest lines. double dMinDistGuess = Lines[usThisClosestLineGuess].Distance( Other.Lines[usOtherClosestLineGuess], ptOnThis, ptOnOther); // If its 0 then return 0. if (dMinDistGuess == 0) return 0; C2DPoint ptOnThisTemp = new C2DPoint(); C2DPoint ptOnOtherTemp = new C2DPoint(); // Now go through all of our line rects and only check further if they are closer // to the other's bounding rect than the min guess. for (int i = 0; i < Lines.Count; i++) { if (LineRects[i].Distance( OtherBoundingRect ) < dMinDistGuess) { for ( int j = 0 ; j < Other.Lines.Count ; j++) { double dDist = Lines[i].Distance(Other.Lines[j], ptOnThisTemp, ptOnOtherTemp); if (dDist < dMinDistGuess) { ptOnThis.Set(ptOnThisTemp); ptOnOther.Set(ptOnOtherTemp); if (dDist == 0) return 0; dMinDistGuess = dDist; } } } } // if we are here, there is no intersection but the other could be inside this or vice-versa if ( BoundingRect.Contains(Other.BoundingRect) && Contains(ptOnOtherTemp) ) { dMinDistGuess *= -1.0; } else if ( Other.BoundingRect.Contains(BoundingRect) && Other.Contains( ptOnThisTemp )) { dMinDistGuess *= -1.0; } return dMinDistGuess; }
/// <summary> /// Function to convert polygons to complex polygons. Assigning holes to those that are contained. /// The set of holed polygons will be filled from the set of simple polygons. /// </summary> /// <param name="HoledPolys">Holed polygon set.</param> /// <param name="Polygons">Simple polygon set.</param> public static void PolygonsToHoledPolygons(List <C2DHoledPolyBase> HoledPolys, List <C2DPolyBase> Polygons) { List <C2DPolyBase> Unmatched = new List <C2DPolyBase>(); List <C2DHoledPolyBase> NewHoledPolys = new List <C2DHoledPolyBase>(); for (int i = Polygons.Count - 1; i >= 0; i--) { bool bMatched = false; C2DPolyBase pPoly = Polygons[i]; Polygons.RemoveAt(i); // Cycle through the newly created polygons to see if it's a hole. for (int p = 0; p < NewHoledPolys.Count; p++) { if (NewHoledPolys[p].Rim.Contains(pPoly.Lines[0].GetPointFrom())) { NewHoledPolys[p].AddHole(pPoly); bMatched = true; break; } } // If its not then compare it to all the other unknowns. if (!bMatched) { int u = 0; bool bKnownRim = false; while (u < Unmatched.Count) { if (!bKnownRim && Unmatched[u].Contains(pPoly.Lines[0].GetPointFrom())) { // This is a hole. NewHoledPolys.Add(new C2DHoledPolyBase()); NewHoledPolys[NewHoledPolys.Count - 1].Rim = Unmatched[u]; Unmatched.RemoveAt(u); NewHoledPolys[NewHoledPolys.Count - 1].AddHole(pPoly); bMatched = true; break; } else if (pPoly.Contains(Unmatched[u].Lines[0].GetPointFrom())) { // int nCount = OverlapPolygons->GetCount(); // This is a rim. if (!bKnownRim) { // If we haven't alreay worked this out then record that its a rim // and set up the new polygon. bKnownRim = true; NewHoledPolys.Add(new C2DHoledPolyBase()); NewHoledPolys[NewHoledPolys.Count - 1].Rim = pPoly; NewHoledPolys[NewHoledPolys.Count - 1].AddHole(Unmatched[u]); Unmatched.RemoveAt(u); } else { // We already worked out this was a rim so it must be the last polygon. NewHoledPolys[NewHoledPolys.Count - 1].AddHole(Unmatched[u]); Unmatched.RemoveAt(u); } // Record that its been matched. bMatched = true; } else { // Only if there was no match do we increment the counter. u++; } } } if (!bMatched) { Unmatched.Add(pPoly); } } for (int i = 0; i < Unmatched.Count; i++) { C2DHoledPolyBase NewHoled = new C2DHoledPolyBase(); NewHoled.Rim = Unmatched[i]; HoledPolys.Add(NewHoled); } HoledPolys.AddRange(NewHoledPolys); }
/// <summary> /// Polygon entirely inside test. /// </summary> /// <param name="Polygon">Polygon to test for.</param> public bool Contains(C2DPolyBase Polygon) { if (!_Rim.Contains(Polygon)) return false; for (int i = 0 ; i < _Holes.Count; i++) { if (_Holes[i].Overlaps(Polygon)) return false; } return true; }
/// <summary> /// Returns the routes (multiple lines or part polygons) either inside or /// outside the polygons provided. These are based on the intersections /// of the 2 polygons e.g. the routes / part polygons of one inside or /// outside the other. /// </summary> /// <param name="Poly1">The first polygon.</param> /// <param name="bP1RoutesInside">True if routes inside the second polygon are /// required for the first polygon.</param> /// <param name="Poly2">The second polygon.</param> /// <param name="bP2RoutesInside">True if routes inside the first polygon are /// required for the second polygon.</param> /// <param name="Routes1">Output. Set of lines for the first polygon.</param> /// <param name="Routes2">Output. Set of lines for the second polygon.</param> /// <param name="CompleteHoles1">Output. Complete holes for the first polygon.</param> /// <param name="CompleteHoles2">Output. Complete holes for the second polygon.</param> /// <param name="grid">Contains the degenerate handling settings.</param> public static void GetRoutes(C2DHoledPolyBase Poly1, bool bP1RoutesInside, C2DHoledPolyBase Poly2, bool bP2RoutesInside, C2DLineBaseSetSet Routes1, C2DLineBaseSetSet Routes2, List <C2DPolyBase> CompleteHoles1, List <C2DPolyBase> CompleteHoles2, CGrid grid) { if (Poly1.Rim.Lines.Count == 0 || Poly2.Rim.Lines.Count == 0) { Debug.Assert(false, "Polygon with no lines"); return; } C2DPointSet IntPointsTemp = new C2DPointSet(); C2DPointSet IntPointsRim1 = new C2DPointSet(); C2DPointSet IntPointsRim2 = new C2DPointSet(); List <int> IndexesRim1 = new List <int>(); List <int> IndexesRim2 = new List <int>(); List <C2DPointSet> IntPoints1AllHoles = new List <C2DPointSet>(); List <C2DPointSet> IntPoints2AllHoles = new List <C2DPointSet>(); List <List <int> > Indexes1AllHoles = new List <List <int> >(); List <List <int> > Indexes2AllHoles = new List <List <int> >(); // std::vector<C2DPointSet* > IntPoints1AllHoles, IntPoints2AllHoles; // std::vector<CIndexSet*> Indexes1AllHoles, Indexes2AllHoles; int usP1Holes = Poly1.HoleCount; int usP2Holes = Poly2.HoleCount; // *** Rim Rim Intersections Poly1.Rim.Lines.GetIntersections(Poly2.Rim.Lines, IntPointsTemp, IndexesRim1, IndexesRim2, Poly1.Rim.BoundingRect, Poly2.Rim.BoundingRect); IntPointsRim1.AddCopy(IntPointsTemp); IntPointsRim2.ExtractAllOf(IntPointsTemp); // *** Rim Hole Intersections for (int i = 0; i < usP2Holes; i++) { Debug.Assert(IntPointsTemp.Count == 0); IntPoints2AllHoles.Add(new C2DPointSet()); Indexes2AllHoles.Add(new List <int>()); if (Poly1.Rim.BoundingRect.Overlaps(Poly2.GetHole(i).BoundingRect)) { Poly1.Rim.Lines.GetIntersections(Poly2.GetHole(i).Lines, IntPointsTemp, IndexesRim1, Indexes2AllHoles[i], Poly1.Rim.BoundingRect, Poly2.GetHole(i).BoundingRect); IntPointsRim1.AddCopy(IntPointsTemp); IntPoints2AllHoles[i].ExtractAllOf(IntPointsTemp); } } // *** Rim Hole Intersections for (int j = 0; j < usP1Holes; j++) { Debug.Assert(IntPointsTemp.Count == 0); IntPoints1AllHoles.Add(new C2DPointSet()); Indexes1AllHoles.Add(new List <int>()); if (Poly2.Rim.BoundingRect.Overlaps(Poly1.GetHole(j).BoundingRect)) { Poly2.Rim.Lines.GetIntersections(Poly1.GetHole(j).Lines, IntPointsTemp, IndexesRim2, Indexes1AllHoles[j], Poly2.Rim.BoundingRect, Poly1.GetHole(j).BoundingRect); IntPointsRim2.AddCopy(IntPointsTemp); IntPoints1AllHoles[j].ExtractAllOf(IntPointsTemp); } } // *** Quick Escape bool bRim1StartInPoly2 = Poly2.Contains(Poly1.Rim.Lines[0].GetPointFrom()); bool bRim2StartInPoly1 = Poly1.Contains(Poly2.Rim.Lines[0].GetPointFrom()); if (IntPointsRim1.Count != 0 || IntPointsRim2.Count != 0 || bRim1StartInPoly2 || bRim2StartInPoly1) // pos no interaction { // *** Rim Routes Poly1.Rim.GetRoutes(IntPointsRim1, IndexesRim1, Routes1, bRim1StartInPoly2, bP1RoutesInside); Poly2.Rim.GetRoutes(IntPointsRim2, IndexesRim2, Routes2, bRim2StartInPoly1, bP2RoutesInside); if (IntPointsRim1.Count % 2 != 0) // Must be even { grid.LogDegenerateError(); // Debug.Assert(false); } if (IntPointsRim2.Count % 2 != 0) // Must be even { grid.LogDegenerateError(); // Debug.Assert(false); } // *** Hole Hole Intersections for (int h = 0; h < usP1Holes; h++) { for (int k = 0; k < usP2Holes; k++) { Debug.Assert(IntPointsTemp.Count == 0); C2DPolyBase pHole1 = Poly1.GetHole(h); C2DPolyBase pHole2 = Poly2.GetHole(k); if (pHole1.BoundingRect.Overlaps(pHole2.BoundingRect)) { pHole1.Lines.GetIntersections(pHole2.Lines, IntPointsTemp, Indexes1AllHoles[h], Indexes2AllHoles[k], pHole1.BoundingRect, pHole2.BoundingRect); IntPoints1AllHoles[h].AddCopy(IntPointsTemp); IntPoints2AllHoles[k].ExtractAllOf(IntPointsTemp); } } } // *** Hole Routes for (int a = 0; a < usP1Holes; a++) { C2DPolyBase pHole = Poly1.GetHole(a); if (IntPoints1AllHoles[a].Count % 2 != 0) // Must be even { grid.LogDegenerateError(); // Debug.Assert(false); } if (pHole.Lines.Count != 0) { bool bHole1StartInside = Poly2.Contains(pHole.Lines[0].GetPointFrom()); if (IntPoints1AllHoles[a].Count == 0) { if (bHole1StartInside == bP1RoutesInside) { CompleteHoles1.Add(new C2DPolyBase(pHole)); } } else { pHole.GetRoutes(IntPoints1AllHoles[a], Indexes1AllHoles[a], Routes1, bHole1StartInside, bP1RoutesInside); } } } // *** Hole Routes for (int b = 0; b < usP2Holes; b++) { C2DPolyBase pHole = Poly2.GetHole(b); if (IntPoints2AllHoles[b].Count % 2 != 0) // Must be even { grid.LogDegenerateError(); // Debug.Assert(false); } if (pHole.Lines.Count != 0) { bool bHole2StartInside = Poly1.Contains(pHole.Lines[0].GetPointFrom()); if (IntPoints2AllHoles[b].Count == 0) { if (bHole2StartInside == bP2RoutesInside) { CompleteHoles2.Add(new C2DPolyBase(pHole)); } } else { pHole.GetRoutes(IntPoints2AllHoles[b], Indexes2AllHoles[b], Routes2, bHole2StartInside, bP2RoutesInside); } } } } //for (unsigned int i = 0 ; i < IntPoints1AllHoles.size(); i++) // delete IntPoints1AllHoles[i]; //for (unsigned int i = 0 ; i < IntPoints2AllHoles.size(); i++) // delete IntPoints2AllHoles[i]; //for (unsigned int i = 0 ; i < Indexes1AllHoles.size(); i++) // delete Indexes1AllHoles[i]; //for (unsigned int i = 0 ; i < Indexes2AllHoles.size(); i++) // delete Indexes2AllHoles[i]; }
/// <summary> /// True if this overlaps the other. /// </summary> /// <param name="Other">Other polygon to test for.</param> public bool Overlaps(C2DPolyBase Other) { if (!_Rim.Overlaps(Other)) return false; for (int i = 0 ; i < _Holes.Count; i++) { if (_Holes[i].Contains(Other)) return false; } return true; }
/// <summary> /// True if it crosses the other. /// </summary> /// <param name="Other">The other polygon.</param> public bool Crosses(C2DPolyBase Other) { if (!BoundingRect.Overlaps(Other.BoundingRect)) return false; List<C2DPoint> Temp = new List<C2DPoint>(); for (int i = 0; i < Lines.Count; i++) { if (Other.Crosses(Lines[i], Temp)) return true; } return false; }
/// <summary> /// Intersection with another. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="IntersectionPts">Output. The intersection points.</param> public bool Crosses(C2DPolyBase Other, List<C2DPoint> IntersectionPts) { if (!BoundingRect.Overlaps(Other.BoundingRect)) return false; List<C2DPoint> IntPtsTemp = new List<C2DPoint>(); List<int> Index1 = new List<int>(); List<int> Index2 = new List<int>(); Lines.GetIntersections(Other.Lines, IntPtsTemp, Index1, Index2, BoundingRect, Other.BoundingRect); bool bResult = IntPtsTemp.Count > 0; IntersectionPts.InsertRange(0, IntPtsTemp); return bResult; }
/// <summary> /// Constructor. /// </summary> /// <param name="Other">The other polygon.</param> public C2DPolyArc(C2DPolyBase Other) : base(Other) { }
/// <summary> /// Hole access. /// </summary> public new void SetHole(int i, C2DPolyBase Poly) { if (Poly is C2DPolygon) { _Holes[i] = Poly; } else { Debug.Assert(false, "Invalid Hole type"); } }
/// <summary> /// Returns the union of this with another. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="Polygons">The output polygons.</param> /// <param name="grid">The degenerate settings.</param> public void GetUnion(C2DPolyBase Other, List <C2DHoledPolyBase> Polygons, CGrid grid) { GetBoolean(Other, Polygons, false, false, grid); }
/// <summary> /// Returns the overlaps of this with another. /// </summary> /// <param name="Other">The other polygon.</param> /// <param name="Polygons">The output polygons.</param> /// <param name="grid">The degenerate settings.</param> public void GetOverlaps(C2DPolyBase Other, List <C2DHoledPolyBase> Polygons, CGrid grid) { GetBoolean(Other, Polygons, true, true, grid); }