Пример #1
0
        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);
        }