public override Ring2 <double> ConstructRing(Point2 <double>[] points) { if (points == null || points.Length < 3) { return(null); } Point2 <double>[] pts = PointUtils.RemoveAdjacentDups <double>(points, true); if (pts.Length < 3) { return(null); } PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); graph.Add(pts, true); if (PlanarGraphUtils.AnyIntersections(graph)) { return(null); } Orientation o = Orientation.Clockwise; if (PointUtilsDouble.IsCCW(pts)) { o = Orientation.Counterclockwise; } return(new Ring2 <double>(pts, false, o)); }
public override RingSet2 <double> ConstructRingSet(IList <Ring2 <double> > chains) { if (chains == null || chains.Count < 1) { return(null); } Ring2 <double>[] Chains = new Ring2 <double> [chains.Count]; for (int i = 0; i < Chains.Length; i++) { Chains[i] = chains[i]; } PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); foreach (Ring2 <double> ch in Chains) { if (ch == null) { return(null); //can't have null chains } graph.Add(ch); } if (PlanarGraphUtils.AnyIntersections(graph)) //there must be overlap { return(null); } return(new RingSet2 <double>(Chains)); }
public override PolylineSet2 <double> ConstructPolylineSet(IList <Polyline2 <double> > chains) { if (chains == null || chains.Count < 1) { return(null); } Polyline2 <double>[] Chains = chains.ToArray(); PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); foreach (Polyline2 <double> ch in Chains) { if (ch == null) { return(null); //can't have null chains } graph.Add(new LineChain2 <double>(ch.Points)); //TODO -- fix this, it's broken } if (PlanarGraphUtils.AnyIntersections(graph)) //there must be overlap { return(null); } return(new PolylineSet2 <double>(Chains)); }
public override PolylineSet2 <double> ConstructPolylineSet(Polyline2 <double>[] chains) { if (chains == null || chains.Length < 1) { return(null); } Polyline2 <double>[] Chains = new Polyline2 <double> [chains.Length]; Polyline2 <double> ch; PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); for (int i = 0; i < Chains.Length; i++) { ch = chains[i]; if (ch == null) { return(null); //can't have null chains } Chains[i] = ch; graph.Add(new LineChain2 <double>(ch.Points)); //TODO -- fix this, it's broken } if (PlanarGraphUtils.AnyIntersections(graph)) //there must be overlap { return(null); } return(new PolylineSet2 <double>(Chains)); }
public override LineChainSet2 <double> ConstructLineSet(LineChain2 <double>[] chains) { if (chains == null || chains.Length < 1) { return(null); } LineChain2 <double>[] Chains = new LineChain2 <double> [chains.Length]; LineChain2 <double> ch; PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); for (int i = 0; i < Chains.Length; i++) { ch = chains[i]; if (ch == null) { return(null); //can't have null chains } Chains[i] = ch; graph.Add(ch); } if (PlanarGraphUtils.AnyIntersections(graph)) //there must be overlap { return(null); } return(new LineChainSet2 <double>(Chains)); }
public override LineChainSet2 <double> ConstructLineSet(IEnumerable <LineChain2 <double> > chains) { if (chains == null) { return(null); } LineChain2 <double>[] Chains = chains.ToArray(); PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); foreach (LineChain2 <double> ch in Chains) { if (ch == null) { return(null); //can't have null chains } graph.Add(ch); } if (PlanarGraphUtils.AnyIntersections(graph)) //there must be overlap { return(null); } return(new LineChainSet2 <double>(Chains)); }
public override RingSet2 <double> ConstructRingSet(Ring2 <double>[] chains) { if (chains == null || chains.Length < 1) { return(null); } Ring2 <double>[] Chains = new Ring2 <double> [chains.Length]; Array.Copy(chains, Chains, Chains.Length); PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); foreach (Ring2 <double> ch in Chains) { if (ch == null) { return(null); //can't have null chains } graph.Add(ch); } if (PlanarGraphUtils.AnyIntersections(graph)) //there must be overlap { return(null); } return(new RingSet2 <double>(Chains)); }
public override Polyline2 <double> ConstructPolyline(Point2 <double>[] points) { if (points == null || points.Length < 2) { return(null); } PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); Point2 <double>[] Coordinates = PointUtils.RemoveAdjacentDups <double>(points, false); if (Coordinates.Length < 2) { return(null); } graph.Add(Coordinates, false); return(new Polyline2 <double>(Coordinates)); }
public override Ring2 <double> CloseChain(LineChain2 <double> chain) { if (chain == null) { return(null); } if (chain.Points.Length < 3) { return(null); } PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); graph.Add(chain.Points, true); if (PlanarGraphUtils.AnyIntersections(graph)) { return(null); } Orientation o = Orientation.Clockwise; if (chain.IsReversed) { if (PointUtilsDouble.IsCCW(chain.Points)) { o = Orientation.Clockwise; } else { o = Orientation.Counterclockwise; } } else { if (PointUtilsDouble.IsCCW(chain.Points)) { o = Orientation.Counterclockwise; } else { o = Orientation.Counterclockwise; } } return(new Ring2 <double>(chain.Points, chain.IsReversed, o)); }
public override LineChain2 <double> ConstructLineChain(IEnumerable <Point2 <double> > points) { if (points == null) { return(null); } PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); Point2 <double>[] Coordinates = PointUtils.RemoveAdjacentDups <double>(points, false); if (Coordinates.Length < 2) { return(null); } graph.Add(Coordinates, false); if (PlanarGraphUtils.AnyIntersections(graph)) { return(null); } return(new LineChain2 <double>(Coordinates)); }
public override RingSet2 <double> ConstructRingSet(IEnumerable <Ring2 <double> > chains) { if (chains == null) { return(null); } LeakyResizableArray <Ring2 <double> > chainSet = new LeakyResizableArray <Ring2 <double> >(); foreach (Ring2 <double> ch in chains) { if (ch == null) { return(null); } chainSet.Add(ch); } chainSet.TrimExcess(); Ring2 <double>[] Chains = chainSet.Data; PlanarChainGraph <double> graph = new PlanarChainGraph <double>(); foreach (Ring2 <double> ch in Chains) { if (ch == null) { return(null); //can't have null chains } graph.Add(ch); } if (PlanarGraphUtils.AnyIntersections(graph)) //there must be overlap { return(null); } return(new RingSet2 <double>(Chains)); }
internal static bool AnyIntersections(PlanarChainGraph <double> graph) { if (graph == null) { return(false); } 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; LineIntersectionResult <double> intersects; Point2 <double> localStart; Point2 <double> localEnd; Point2 <double> activeStart; Point2 <double> activeEnd; //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)) { continue; //can't have a "full" match -- this is an exiting segment } activeStart = activeEdge.Start.Point; activeEnd = activeEdge.End.Point; intersects = Coordinate2Utils.GetIntersection(localStart.X, localStart.Y, localEnd.X, localEnd.Y, activeStart.X, activeStart.Y, activeEnd.X, activeEnd.Y); if (intersects.IntersectionType != LineIntersectionType.NoIntersection) { if (object.ReferenceEquals(localEdge.Previous, activeEdge) || object.ReferenceEquals(localEdge.Next, activeEdge)) { continue; // this is adjacent segments in a chain/ring } return(true); } } } //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]); } } return(false); }
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); }