public static bool Contains(Polygon2 <double> outer, Polygon2 <double> inner) { if (outer == null || inner == null) { return(false); } if (outer.HasHoles) { if (RingUtils.Contains(outer.OuterRing, inner.OuterRing)) { for (int i = 0; i < outer.InnerRings.Rings.Length; i++) { if (RingUtils.Contains(outer.InnerRings.Rings[i], inner.OuterRing)) { return(false); //in a hole } } return(true); } else { return(false); } } else { return(RingUtils.Contains(outer.OuterRing, inner.OuterRing)); } }
//point is inside the ring - not on the edge public static bool Contains(Polygon2 <double> ring, Point2 <double> pt) { if (ring == null || pt == null) { return(false); } if (ring.HasHoles) { if (RingUtils.Contains(ring.OuterRing, pt)) { for (int i = 0; i < ring.InnerRings.Rings.Length; i++) { if (RingUtils.Contains(ring.InnerRings.Rings[i], pt)) { return(false); //in a hole } } return(true); } else { return(false); } } else { return(RingUtils.Contains(ring.OuterRing, pt)); } }
//is the "inner" (ring) inside the "outer" (ring) //matches check for polygon holes, so single shared points on edge are ok, they share no area nor segment public static bool ContainsByArea(Polygon2 <double> outer, Polygon2 <double> inner) { if (outer == null || inner == null) { return(false); } return(RingUtils.ContainsByArea(outer.OuterRing, inner.OuterRing)); }
//is the "inner" (point) inside or on the "outer" (ring) //point is in or on the ring public static bool ContainsByArea(Polygon2 <double> ring, Point2 <double> pt) { if (ring == null || pt == null) { return(false); } return(RingUtils.ContainsByArea(ring.OuterRing, pt)); }
public static List <Polygon2 <double> > CleanSimplePolygonPoints(Point2 <double>[] outerRingPoints, Ring2 <double>[] innerRings) { List <Ring2 <double> > rings = RingUtils.CleanSimpleRingPoints(outerRingPoints); if (rings == null || rings.Count < 1) { return(null); } List <Polygon2 <double> > res = new List <Polygon2 <double> >(); Polygon2 <double> tmpP; if (rings.Count == 1) { tmpP = rings[0].Factory.ConstructPolygon(rings[0], innerRings); if (tmpP == null) { return(null); } res.Add(tmpP); } else { List <Ring2 <double> > inRings = new List <Ring2 <double> >(); foreach (Ring2 <double> curRing in rings) { if (curRing == null) { continue; } inRings.Clear(); foreach (Ring2 <double> curHole in innerRings) { if (RingUtils.PointInteriorToRing(curRing, curHole.Points[0]) || RingUtils.PointInteriorToRing(curRing, curHole.Points[1])) { inRings.Add(curHole); } } if (inRings.Count < 1) { res.Add((Polygon2 <double>)curRing); } else { tmpP = curRing.Factory.ConstructPolygon(curRing, inRings.ToArray()); if (tmpP == null) { return(null); } res.Add(tmpP); } } } return(res); }
public static Polygon2 <double> OpenSimplePolygonPoints(Point2 <double>[] outerRingPoints, Ring2 <double>[] innerRings) { Ring2 <double> ring = RingUtils.OpenSimpleRingPoints(outerRingPoints); if (ring == null) { return(null); } Polygon2 <double> tmpP; tmpP = ring.Factory.ConstructPolygon(ring, innerRings); return(tmpP); }
void Start() { RingUtils.load(); pointsUtils.load(); ui = gameObject.AddComponent <ThingsUI> (); for (int i = 0; i < 5; i++) { GetThing(RingUtils.RandomOneRing()); } for (int i = 0; i < 5; i++) { GetThing(pointsUtils.RandomOneRing()); } }
public RingTransform FindNext(float distanceFromZero) { return(RingUtils.FindNext(transform.forward * distanceFromZero, transform.forward, rings)); }
public static bool ValidRingSet(Ring2 <double>[] rings) { PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); HashSet <Ring2 <double> > ringHash = new HashSet <Ring2 <double> >(); //check for the silly condition of multiple references to the same ring foreach (Ring2 <double> r in rings) { if (r == null) { return(false); //not a ring, for shame } if (ringHash.Contains(r)) { return(false); //oops, duplicate reference } ringHash.Add(r); graph.Add(r); } ringHash.Clear(); ringHash = null; SegmentGroup <double> activeEdges = new SegmentGroup <double>(); IEnumerator <Node <double> > points = graph.GetEnumerator(); //walk through the points in xy sorted order Node <double> nd; LeakyResizableArray <Edge <double> > localEdges; int localCt; int activeCt = 0; Edge <double> localEdge; LineSegment2IntersectionResult <double> intersects; Point2 <double> localStart; Point2 <double> localEnd; Point2 <double> activeStart; Point2 <double> activeEnd; Point2 <double> intPoint; //new point event in the moving front while (points.MoveNext()) { nd = points.Current; localEdges = nd.Edges; //edges connected to this point localCt = (int)localEdges.Count; //compute intersections with other edges in the scan area for (int i = 0; i < localCt; i++) { localEdge = localEdges.Data[i]; localStart = localEdge.Start.Point; localEnd = localEdge.End.Point; activeCt = activeEdges.Edges.Count; foreach (Edge <double> activeEdge in activeEdges.Edges) { if (object.ReferenceEquals(localEdge, activeEdge) || object.ReferenceEquals(localEdge.Start.ParentShape, activeEdge.Start.ParentShape)) { continue; //exiting segment || 2 edges on same ring } activeStart = activeEdge.Start.Point; activeEnd = activeEdge.End.Point; intersects = SegmentUtils.ComputeIntersection(localStart, localEnd, activeStart, activeEnd); if (intersects.IntersectionType != LineIntersectionType.NoIntersection) { if (intersects.IntersectionType == LineIntersectionType.CollinearIntersection) { return(false); // there's a full segment of intersection } intPoint = (Point2 <double>)intersects.BasePoint; //we have a point intersection, and this is not on the same ring //if the intersection point is not coincident with an endpoint, we have a bad case //otherwise -- this is ok, since there is no common "length" //but we need to check the odd condition that the sequential points don't cross over the ring out-on-in or in-on-out if (PointUtilsDouble.PointsEqual(intPoint, localStart) || PointUtilsDouble.PointsEqual(intPoint, localEnd)) { //localEdge.Previous.Start, localStart, localEnd, localEdge.Next.End Ring2 <double> srcRing = (Ring2 <double>)activeEdge.Start.ParentShape; //we're comparing against the ring if (PointUtilsDouble.PointsEqual(intPoint, localStart)) { if (RingUtils.PointInteriorToRing(srcRing, localEnd)) { return(false); //point is inside ring } if (RingUtils.PointInteriorToRing(srcRing, localEdge.Previous.Start.Point)) //since localStart, then we need the start of the previous edge { return(false); //point is inside ring } } else //matched localEnd { if (RingUtils.PointInteriorToRing(srcRing, localStart)) { return(false); //point is inside ring } if (RingUtils.PointInteriorToRing(srcRing, localEdge.Next.End.Point)) //since localEnd, then we need the end of the next edge { return(false); //point is inside ring } } } else if (PointUtilsDouble.PointsEqual(intPoint, activeStart) || PointUtilsDouble.PointsEqual(intPoint, activeEnd)) { //localEdge.Previous.Start, localStart, localEnd, localEdge.Next.End Ring2 <double> srcRing = (Ring2 <double>)localEdge.Start.ParentShape; //we're comparing against the ring if (PointUtilsDouble.PointsEqual(intPoint, activeStart)) { if (RingUtils.PointInteriorToRing(srcRing, activeEnd)) { return(false); //point is inside ring } if (RingUtils.PointInteriorToRing(srcRing, activeEdge.Previous.Start.Point)) //since localStart, then we need the start of the previous edge { return(false); //point is inside ring } } else //matched activeEnd { if (RingUtils.PointInteriorToRing(srcRing, activeStart)) { return(false); //point is inside ring } if (RingUtils.PointInteriorToRing(srcRing, activeEdge.Next.End.Point)) //since localEnd, then we need the end of the next edge { return(false); //point is inside ring } } } else { return(false); //we have a mid-segment intersection between the outer ring and an inner ring } } } } //remove all exiting segments and add all starting segments //Action gets called exactly twice per edge -- once to add it, once to remove it for (int i = 0; i < localCt; i++) { activeEdges.Action(localEdges.Data[i]); } } //now, we need to check that none of the rings are inside of another ring -- we only need to check a single point per ring (unless that point is on the ring) //note we need to neglect the inclusive/exclusive nature and just focus on point being interior to ring, since these will often be holes for (int i = 1; i < rings.Length; i++) { Ring2 <double> r = rings[i]; Ring2 <double> k; for (int j = 0; j < i; j++) { k = rings[j]; if (RingUtils.PointInteriorToRing(r, k.Points[0]) || RingUtils.PointInteriorToRing(r, k.Points[1])) //have to be sure point not ON ring { return(false); } } } return(true); }
public static bool ValidPolygon(Ring2 <double> outer, RingSet2 <double> inner) { if (outer == null || inner == null) { return(false); } HashSet <Ring2 <double> > ringHash = new HashSet <Ring2 <double> >(); //check for the silly condition of multiple references to the same ring ringHash.Add(outer); Envelope2 <double> outerEnv = outer.Envelope; PlanarChainGraph <double> gr = new PlanarChainGraph <double>(); foreach (Ring2 <double> r in inner.Rings) { if (!ringHash.Add(r)) { return(false); //oops, duplicate reference } if (!Envelope2 <double> .EnvelopeContain(outerEnv, r.Envelope)) //all inner rings must be contained by outer ring, so the envelopes must overlap { return(false); } gr.Add(r); } ringHash.Clear(); ringHash = null; gr.Add(outer); SegmentGroup <double> activeEdges = new SegmentGroup <double>(); IEnumerator <Node <double> > points = gr.GetEnumerator(); //walk through the points in xy sorted order Node <double> nd; LeakyResizableArray <Edge <double> > localEdges; int localCt; int activeCt = 0; Edge <double> localEdge; LineSegment2IntersectionResult <double> intersects; Point2 <double> localStart; Point2 <double> localEnd; Point2 <double> activeStart; Point2 <double> activeEnd; Point2 <double> intPoint; SimpleGraph <object> touchingRings = new SimpleGraph <object>(); HashSet <object> outerRingTouching = new HashSet <object>(); //new point event in the moving front while (points.MoveNext()) { nd = points.Current; localEdges = nd.Edges; //edges connected to this point localCt = (int)localEdges.Count; //compute intersections with other edges in the scan area for (int i = 0; i < localCt; i++) { localEdge = localEdges.Data[i]; localStart = localEdge.Start.Point; localEnd = localEdge.End.Point; activeCt = activeEdges.Edges.Count; foreach (Edge <double> activeEdge in activeEdges.Edges) { if (object.ReferenceEquals(localEdge, activeEdge) || object.ReferenceEquals(localEdge.Start.ParentShape, activeEdge.Start.ParentShape)) { continue; //exiting edge || 2 edges on same ring } activeStart = activeEdge.Start.Point; activeEnd = activeEdge.End.Point; intersects = SegmentUtils.ComputeIntersection(localStart, localEnd, activeStart, activeEnd); if (intersects.IntersectionType != LineIntersectionType.NoIntersection) { if (intersects.IntersectionType == LineIntersectionType.CollinearIntersection) { return(false); // there's a full segment of intersection - must be between outer ring and an inner ring } //ok, we have an intersection that is a point between 2 different rings intPoint = intersects.BasePoint; if (object.ReferenceEquals(localEdge.Start.ParentShape, outer)) //localEdge is on the shell { if (PointUtilsDouble.PointsEqual(intPoint, activeStart)) { //only add the shell touching for segment intersection at segment start point - prevents recounting point if (!PointUtilsDouble.PointsEqual(intPoint, localEnd)) { if (!outerRingTouching.Add(activeEdge.Start.ParentShape)) { return(false); //same ring touches outer ring at multiple non-adjacent points } } if (!RingUtils.PointInteriorToRing(outer, activeEnd)) { return(false); //ring is outside of shell or crosses shell at a vertex } } else if (PointUtilsDouble.PointsEqual(intPoint, activeEnd)) { //only add the shell touching for segment intersection at segment start point - prevents recounting point if (!PointUtilsDouble.PointsEqual(intPoint, localEnd)) { if (!outerRingTouching.Add(activeEdge.Start.ParentShape)) { return(false); //same ring touches outer ring at multiple non-adjacent points } } if (!RingUtils.PointInteriorToRing(outer, activeStart)) { return(false); //ring is outside of shell or crosses shell at a vertex } } else { return(false); //we have a mid-segment intersection between the outer ring and an inner ring } } else if (object.ReferenceEquals(activeEdge.Start.ParentShape, outer)) //activeEdge is on the shell { if (PointUtilsDouble.PointsEqual(intPoint, localStart)) { //only add the shell touching for segment intersection at segment start point - prevents recounting point if (!PointUtilsDouble.PointsEqual(intPoint, activeEnd)) { if (!outerRingTouching.Add(localEdge.Start.ParentShape)) { return(false); //same ring touches outer ring at multiple non-adjacent points } } if (!RingUtils.PointInteriorToRing(outer, localEnd)) { return(false); //ring is outside of shell or crosses shell at a vertex } } else if (PointUtilsDouble.PointsEqual(intPoint, localEnd)) { //only add the shell touching for segment intersection at segment start point - prevents recounting point if (!PointUtilsDouble.PointsEqual(intPoint, activeEnd)) { if (!outerRingTouching.Add(localEdge.Start.ParentShape)) { return(false); //same ring touches outer ring at multiple non-adjacent points } } if (!RingUtils.PointInteriorToRing(outer, localStart)) { return(false); //ring is outside of shell or crosses shell at a vertex } } else { return(false); //we have a mid-segment intersection between the outer ring and an inner ring } } else //both are on inner rings { //since the ringset is valid, we just need to map the connectivity of rings if (!touchingRings.Contains(localEdge.Start.ParentShape)) { touchingRings.AddNode(localEdge.Start.ParentShape); } if (!touchingRings.Contains(activeEdge.Start.ParentShape)) { touchingRings.AddNode(activeEdge.Start.ParentShape); } touchingRings.AddEdge(localEdge.Start.ParentShape, activeEdge.Start.ParentShape); } } //end !nointersection } //end foreach } //end for //remove all exiting segments and add all starting segments //Action gets called exactly twice per edge -- once to add it, once to remove it for (int i = 0; i < localCt; i++) { activeEdges.Action(localEdges.Data[i]); } } //we now know all the rings of the ringset are "legally" connected wrt the shell -- but -- //inner rings may be adjacent to both the shell and other inner rings to form a "path" cutting the shell //any ring COULD be outside the shell -- we did already do the quick envelope check, so just do singular point-in-ring checks //shell cutting test (quick) object[] shellTouchers = outerRingTouching.ToArray(); for (int i = shellTouchers.Length - 1; i > 0; i--) { object adjacentRingA = shellTouchers[i]; if (touchingRings.Contains(adjacentRingA)) { for (int j = 0; j < i; j++) { object adjacentRingB = shellTouchers[j]; if (touchingRings.Contains(adjacentRingB)) { if (HasPath(adjacentRingA, adjacentRingB, touchingRings)) { return(false); //path exists between shell touching inner rings } } } } } //ring inside shell testing foreach (Ring2 <double> innerR in inner.Rings) { if (!(RingUtils.PointInteriorToRing(outer, innerR.Points[0]) || RingUtils.PointInteriorToRing(outer, innerR.Points[1]))) //at least one of 2 adjacent points must be interior { return(false); } } return(true); }
public static Polygon2 <double> Simplify(double minSegmentLength, Polygon2 <double> poly) { if (poly == null) { return(null); } Ring2 <double> outer = RingUtils.Simplify(minSegmentLength, poly.OuterRing); if (outer == null) { return(null); } if (poly.HasHoles) { List <Ring2 <double> > inners = new List <Ring2 <double> >(); Ring2 <double> curInner; double distSum; double minDist = 3.0 * minSegmentLength; for (int i = 0; i < poly.InnerRings.Rings.Length; i++) { curInner = poly.InnerRings.Rings[i]; //quick reject if the ring is "small" distSum = 0; for (uint j = 1; j < curInner.VertexCount; j++) { distSum += SegmentUtils.Length(curInner[j - 1], curInner[j]); if (distSum >= minDist) //quick exit for big rings { break; } } distSum += SegmentUtils.Length(curInner[curInner.VertexCount - 1], curInner[0]); if (distSum <= minDist) { continue; //can't exist as a ring with that small a boundary } curInner = RingUtils.Simplify(minSegmentLength, curInner); if (curInner != null) { inners.Add(curInner); } } if (inners.Count < 1) { return(outer); //no holes came through } RingSet2 <double> rs = poly.Factory.ConstructRingSet(inners); if (rs == null) { return(null); } return(poly.Factory.ConstructPolygon(outer, rs)); } else { return(outer); } }