public void SnapToOriginInPlace(IList <C2DPoint> input) { var minXy = MinMax(input); for (var i = 0; i < input.Count; ++i) { input[i] = new C2DPoint(input[i].X - minXy.Item1, input[i].Y - minXy.Item2); } var poly = new C2DPolygon(input.ToList(), true); poly.RandomPerturb(); var pointsCopy = new C2DPointSet(); poly.GetPointsCopy(pointsCopy); for (var i = 0; i < pointsCopy.Count; ++i) { input[i] = pointsCopy[i]; } }
/// <summary> /// True if part of this line is above the other. Returns the point /// on this and on the other. /// </summary> /// <param name="Other"></param> /// <param name="dVerticalDistance"></param> /// <param name="ptOnThis"></param> /// <param name="ptOnOther"></param> /// <returns></returns> public bool OverlapsAbove(C2DLine Other, ref double dVerticalDistance, C2DPoint ptOnThis, C2DPoint ptOnOther) { // Get the 2 points for both lines C2DPoint OtherTo = new C2DPoint(Other.point.x + Other.vector.i, Other.point.y + Other.vector.j); C2DPoint ThisTo = new C2DPoint(point.x + vector.i, point.y + vector.j); // Make an interval for both in the x plane CInterval iThis = new CInterval( point.x, point.x); iThis.ExpandToInclude( ThisTo.x ); CInterval iOther = new CInterval( Other.point.x, Other.point.x); iOther.ExpandToInclude( OtherTo.x ); // This is an interval for the overlap between the 2 CInterval iOverlap = new CInterval(); // If there is an overlap... if (iThis.Overlaps(iOther, iOverlap)) { double dThisYMin; double dThisYMax; double dOtherYMin; double dOtherYMax; // If the line is vertical then y at the x min / max can be set to the ends of the line. if (vector.i == 0) { dThisYMin = point.y; dThisYMax = ThisTo.y; } else // otherwise, caluclate the y values at the interval ends { dThisYMin = GetY(iOverlap.dMin); dThisYMax = GetY(iOverlap.dMax); } // Now do the same for the other line if (Other.vector.i == 0) { dOtherYMin = Other.point.y; dOtherYMax = OtherTo.y; } else { dOtherYMin = Other.GetY(iOverlap.dMin); dOtherYMax = Other.GetY(iOverlap.dMax); } // Now find the distance between the 2 at the ends double dDistMin = dThisYMin - dOtherYMin; double dDistMax = dThisYMax - dOtherYMax; // If they are both > 0 then no intersection if ( (dDistMin > 0) && (dDistMax > 0)) { dDistMin = Math.Abs( dDistMin); dDistMax = Math.Abs(dDistMax); // find which one is smallest if ( dDistMin > dDistMax) { dVerticalDistance = dDistMax; // distance at the max is smallest ptOnThis.x = iOverlap.dMax; ptOnThis.y = dThisYMax; ptOnOther.x = iOverlap.dMax; ptOnOther.y = dOtherYMax; } else { dVerticalDistance = dDistMin; // distance at the min is smallest ptOnThis.x = iOverlap.dMin; ptOnThis.y = dThisYMin; ptOnOther.x = iOverlap.dMin; ptOnOther.y = dOtherYMin; } return true; } else if ( (dDistMin < 0) && (dDistMax < 0)) // This is below. { return false; } else { // find the intersection. dVerticalDistance = 0; C2DPointSet pts = new C2DPointSet(); if(this.Crosses(Other, pts)) { ptOnThis = pts[0]; ptOnOther = ptOnThis; } else { Debug.Assert(false); } } return true; } else { return false; } }
/// <summary> /// Returns the lines that make up this defined by the points which are assumed /// to be on this line. i.e. splits the line up. /// </summary> /// <param name="PtsOnLine">The point set defining how this is to be broken up.</param> /// <param name="LineSet">Output. The sub lines.</param> public override void GetSubLines(List<C2DPoint> PtsOnLine, List<C2DLineBase> LineSet) { // if there are no points on the line to split on then add a copy of this and return. int usPointsCount = PtsOnLine.Count; if (usPointsCount == 0 ) { LineSet.Add(new C2DLine(this)); } else { C2DPointSet TempPts = new C2DPointSet(); TempPts.MakeCopy(PtsOnLine); if (usPointsCount > 1) // They need sorting { // Now sort the points according to the order in which they will be encountered TempPts.SortByDistance(point); } // Add the line from the start of this to the first. LineSet.Add(new C2DLine(point, TempPts[0] )); // Add all the sub lines. for (int i = 1; i < usPointsCount; i++) LineSet.Add(new C2DLine(TempPts[i - 1], TempPts[i])); // Add the line from the last point on this to the end of this. LineSet.Add(new C2DLine(TempPts[TempPts.Count - 1], GetPointTo())); } Debug.Assert(LineSet.Count == (PtsOnLine.Count + 1)); }
/// <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 crosses the ray, returns the intersection points. /// </summary> /// <param name="Ray">Ray to test for.</param> /// <param name="IntersectionPts">Intersection points.</param> public bool CrossesRay(C2DLine Ray, C2DPointSet IntersectionPts) { C2DPointSet IntPts = new C2DPointSet(); _Rim.CrossesRay(Ray, IntPts); IntersectionPts.ExtractAllOf(IntPts); for (int i = 0 ; i < _Holes.Count; i++) { if (_Holes[i].CrossesRay(Ray, IntPts)) { double dDist = Ray.point.Distance(IntPts[0]); int nInsert = 0; while (nInsert < IntersectionPts.Count && Ray.point.Distance( IntersectionPts[nInsert]) < dDist ) { nInsert++; } IntersectionPts.InsertRange(nInsert, IntPts); } } return (IntersectionPts.Count > 0); }
/// <summary> /// True if this crosses the line. /// </summary> /// <param name="Line">Line to test for.</param> /// <param name="IntersectionPts">Point set to recieve the intersections.</param> public bool Crosses(C2DLineBase Line, C2DPointSet IntersectionPts) { C2DPointSet IntPts = new C2DPointSet(); _Rim.Crosses(Line, IntPts); for (int i = 0 ; i < _Holes.Count; i++) { _Holes[i].Crosses(Line, IntPts); } bool bResult = (IntPts.Count != 0); IntersectionPts.ExtractAllOf(IntPts); return (bResult); }
/// <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> /// True if the point is in the shape. /// </summary> /// <param name="pt">The point to test set.</param> public bool Contains(C2DPoint pt) { if (!BoundingRect.Contains(pt)) return false; C2DPointSet IntersectedPts = new C2DPointSet (); C2DLine Ray = new C2DLine(pt, new C2DVector(BoundingRect.Width(), 0.000001)); // Make sure to leave if (!this.Crosses(Ray, IntersectedPts)) return false; else { IntersectedPts.SortByDistance(Ray.point); if ( IntersectedPts[0].PointEqualTo(pt)) { // For integers, the pt can start On a line, meaning it's INSIDE, but the ray could cross again // so just return true. Because the equality test is really a test for proximity, this leads to the // possibility that a point could lie just outside the shape but be considered to be inside. This would // only be a problem with very small shapes that are a very long way from the origin. E.g. a 1m2 object // 1 million metres from the origin and a point 0.1mm away from the edge would give rise to a relative // difference of 0.0001 / 1000000 = 0.000000001 which would just be consider to be inside. return true; } else { // Return true if the ray return (IntersectedPts.Count & (int)1) > 0; } } }
/// <summary> /// Returns the routes (collection of lines and sublines) either inside or outside another /// Given the intersection points. /// </summary> /// <param name="IntPts">The intersection points of this with the other polygon.</param> /// <param name="IntIndexes">The corresponding line indexes.</param> /// <param name="Routes">Output. The routes to get the result.</param> /// <param name="bStartInside">True if this polygon starts inside the other.</param> /// <param name="bRoutesInside">True if we require routes of this polygon inside the other.</param> public void GetRoutes(C2DPointSet IntPts, List<int> IntIndexes, C2DLineBaseSetSet Routes, bool bStartInside, bool bRoutesInside) { // Make sure the intersection indexes and points are the same size. if (IntIndexes.Count != IntPts.Count ) { Debug.Assert(false); return; } // Set up a new collection of routes. C2DLineBaseSetSet NewRoutes = new C2DLineBaseSetSet(); // If the polygon has no points then return. if ( _Lines.Count < 1) return; // Sort the intersections by index so we can go through them in order. IntPts.SortByIndex( IntIndexes ); // Set the inside / outside flag to the same as the start inside / outside flag. bool bInside = bStartInside; // If we are inside and want route inside or outside and want routes outside then add a new route. if (bInside == bRoutesInside) { NewRoutes.Add(new C2DLineBaseSet()); } // The current index of the intersects. int usCurrentIntIndex = 0; // cycle through the lines on the polygon. for (int i = 0 ; i < Lines.Count ; i++) { // Set up a list of intersection points on this line only. C2DPointSet IntsOnLine = new C2DPointSet(); // Cycle through all intersections on this line (leaving the usCurrentIntIndex at the next intersected line). while ( usCurrentIntIndex < IntIndexes.Count && IntIndexes[usCurrentIntIndex] == i) { // Add a copy of the points on this line that are intersections IntsOnLine.AddCopy( IntPts[ usCurrentIntIndex ] ); usCurrentIntIndex++; } // If the line in question intersects the other poly then we have left / entered. if ( IntsOnLine.Count > 0 ) { C2DLineBaseSet SubLines = new C2DLineBaseSet(); Lines[i].GetSubLines( IntsOnLine, SubLines ); while (SubLines.Count > 1) { if (bInside == bRoutesInside) { // We have 1. Left and want route in. OR 2. Entered and want routes out. NewRoutes[NewRoutes.Count - 1].Add( SubLines.ExtractAt(0) ); bInside = true ^ bRoutesInside; } else { NewRoutes.Add(new C2DLineBaseSet()); bInside = false ^ bRoutesInside; SubLines.RemoveAt(0); } } if (bInside == bRoutesInside) NewRoutes[NewRoutes.Count - 1].Add( SubLines.ExtractAt(SubLines.Count - 1 ) ); else SubLines.RemoveAt(SubLines.Count - 1); } // Otherwise, if we are e.g. inside and want routes in the keep adding the end poitn of the line. else if (bInside == bRoutesInside) { NewRoutes[NewRoutes.Count - 1].AddCopy( Lines[i] ); } } // Put all the new routes into the provided collection. Routes.ExtractAllOf(NewRoutes); }
/// <summary> /// True if it crosses the ray. Provides the intersection points. /// </summary> /// <param name="Ray">The infinite line.</param> /// <param name="IntersectionPts">Output. The intersection points.</param> public bool CrossesRay(C2DLine Ray, C2DPointSet IntersectionPts) { double dDist = Ray.point.Distance(BoundingRect.GetCentre()); C2DLine LineTemp = new C2DLine(Ray); LineTemp.vector.SetLength(dDist + BoundingRect.Width() + BoundingRect.Height()); return Crosses(LineTemp, IntersectionPts); }
/// <summary> /// True if it crosses the line. Provides the intersection points. /// </summary> /// <param name="Line">The other line.</param> /// <param name="IntersectionPts">Output. The intersection points.</param> public bool Crosses(C2DLineBase Line, List<C2DPoint> IntersectionPts) { C2DRect LineRect = new C2DRect(); Line.GetBoundingRect( LineRect); if (!BoundingRect.Overlaps(LineRect)) return false; Debug.Assert(Lines.Count == LineRects.Count); if(Lines.Count != LineRects.Count) return false; C2DPointSet IntersectionTemp = new C2DPointSet(); bool bResult = false; for (int i = 0; i < this.Lines.Count; i++) { if (LineRects[i].Overlaps(LineRect) && Lines[i].Crosses(Line, IntersectionTemp as List<C2DPoint>)) { bResult = true; } } IntersectionPts.InsertRange(0, IntersectionTemp); return bResult; }
/// <summary> /// True if it entirely contains the line. /// </summary> /// <param name="Line">The line to test.</param> public bool Contains(C2DLineBase Line) { if (!Contains(Line.GetPointFrom())) return false; C2DPointSet Pts = new C2DPointSet(); return !Crosses(Line, Pts); }
/// <summary> /// Returns the lines that go to make this up based on the set of points /// provided which are assumed to be on the line. /// </summary> /// <param name="PtsOnLine">The points defining how the line is to be split.</param> /// <param name="LineSet">The line set to recieve the result.</param> public override void GetSubLines(List<C2DPoint> PtsOnLine, List<C2DLineBase> LineSet) { // if there are no points on the line to split on then add a copy of this and return. int usPointsCount = PtsOnLine.Count; if (usPointsCount == 0 ) { LineSet.Add(new C2DArc(this)); return; } else { // Make a copy of the points for sorting. C2DPointSet TempPts = new C2DPointSet(); TempPts.MakeCopy(PtsOnLine); if (usPointsCount > 1) // They need sorting { // Make a line from the mid point of my line to the start C2DLine CenToStart = new C2DLine( Line.GetMidPoint(), Line.point ); // Now sort the points according to the order in which they will be encountered if (ArcOnRight) TempPts.SortByAngleToLeft( CenToStart ); else TempPts.SortByAngleToRight( CenToStart ); } C2DPoint ptCentre = new C2DPoint(GetCircleCentre()); // Add the line from the start of this to the first. C2DLine NewLine = new C2DLine( Line.point, TempPts[0] ); LineSet.Add(new C2DArc(NewLine, Radius, NewLine.IsOnRight(ptCentre), ArcOnRight)); // Add all the sub lines. for (int i = 1; i < usPointsCount; i++) { NewLine.Set(TempPts[i - 1], TempPts[i]); LineSet.Add(new C2DArc(NewLine, Radius, NewLine.IsOnRight(ptCentre), ArcOnRight)); } // Add the line from the last point on this to the end of this. NewLine.Set(TempPts[TempPts.Count - 1], Line.GetPointTo()); LineSet.Add(new C2DArc(NewLine, Radius, NewLine.IsOnRight(ptCentre), ArcOnRight)); } }