Beispiel #1
0
        /// <summary>
        /// Checks if the input <param>cyclePerim</param> contains any vertices in <param>nodeGroup</param> within itself
        /// that are NOT part of its perimeter.
        /// </summary>
        /// <param name="cyclePerim">A planar face found within <param>connectedGroup</param>, which has a planar embedding.</param>
        /// <param name="groupNodesPerim">A group of nodes that form the perimeter of the ConnectedNodeGroup <param>cycle</param>
        /// was extracted from.</param>
        /// <returns></returns>
        public static bool IsCycleOuterPerim(Vector2[] cyclePerim, List <PolygonSplittingGraphNode> groupNodesPerim)
        {
            if (groupNodesPerim is null)
            {
                throw new ArgumentNullException(nameof(groupNodesPerim));
            }

            var groupPerim = new Vector2[groupNodesPerim.Count];

            for (int i = 0; i < groupNodesPerim.Count; i++)
            {
                groupPerim[i] = new Vector2(groupNodesPerim[i].x, groupNodesPerim[i].y);
            }
            return(GeometryFuncs.ArePolysIdentical(cyclePerim, groupPerim));
        }
Beispiel #2
0
        /// <summary>
        /// Partitions a connected node group by:
        ///     1. Repeatedly running BFS on its nodes to find cycles and removing edges for the next BFS until no more
        ///        cycles can be found.
        ///         1.1 Note that the BFS only runs on nodes with two or less VALID EDGES.  Edges are only removed IFF either
        ///             of its vertices has two or less valid edges.  This ensures we do not remove an edge that could be used
        ///             twice, for example two adjacent cycles that share an edge.
        ///     2. The cycle is added to a list of perimeters (AKA a list of nodes) IFF it is NOT a hole.
        /// </summary>
        /// <param name="connectedGroup">ConnectedNodeGroup that is being partitioned.</param>
        /// <param name="idsContainedInGroup">The IDs of the ConnectedNodeGroups contained within <param>connectedGroup</param></param>
        /// <param name="connectedGroups">List of all ConnectedNodeGroups.</param>
        /// <param name="nodes">List of all nodes.</param>
        /// <param name="holes">Holes of the polygon that was made into a PolygonSplittingGraph.</param>
        /// <returns></returns>
        public static List <ChordlessPolygon> PartitionConnectedNodeGroup(ConnectedNodeGroup connectedGroup,
                                                                          SortedDictionary <int, HashSet <int> > idsContainedInGroup,
                                                                          SortedList <int, ConnectedNodeGroup> connectedGroups,
                                                                          SortedList <int, PolygonSplittingGraphNode> nodes,
                                                                          List <Vector2>[] holes)
        {
            if (connectedGroup is null)
            {
                throw new ArgumentNullException(nameof(connectedGroup));
            }
            if (idsContainedInGroup is null)
            {
                throw new ArgumentNullException(nameof(idsContainedInGroup));
            }
            if (connectedGroups is null)
            {
                throw new ArgumentNullException(nameof(connectedGroups));
            }
            if (nodes is null)
            {
                throw new ArgumentNullException(nameof(nodes));
            }
            if (holes is null)
            {
                throw new ArgumentNullException(nameof(holes));
            }

            var outerPerimCycle = new List <PolygonSplittingGraphNode>();
            var polyCycles      = new List <List <PolygonSplittingGraphNode> >();
            var holeCycles      = new List <List <PolygonSplittingGraphNode> >();

            List <List <PolygonSplittingGraphNode> > allFaces = _GetAllFaces(connectedGroup);
            var uniqueFaces = new List <List <Vector2> >();

            foreach (List <PolygonSplittingGraphNode> newFace in allFaces)
            {
                //construct Vector2[] or List<Vector2> describing face perim in Vector2s
                var newFacePerim = new Vector2[newFace.Count];
                for (int i = 0; i < newFace.Count; i++)
                {
                    PolygonSplittingGraphNode node = newFace[i];
                    newFacePerim[i] = new Vector2(node.x, node.y);
                }

                bool newFaceUnique = true;
                foreach (List <Vector2> uniqueFace in uniqueFaces)
                {
                    if (GeometryFuncs.ArePolysIdentical(uniqueFace.ToArray(), newFacePerim))
                    {
                        newFaceUnique = false;
                        break;
                    }
                }

                if (newFaceUnique)
                {
                    uniqueFaces.Add(newFacePerim.ToList());
                    if (IsCycleHole(newFacePerim, holes))
                    {
                        holeCycles.Add(newFace);
                    }
                    else if (IsCycleOuterPerim(newFacePerim, connectedGroup.outerPerimNodes))
                    {
                        outerPerimCycle.AddRange(newFace);
                    }
                    else if (IsCycleComplex(newFacePerim))
                    {
                        continue;
                    }
                    else
                    {
                        polyCycles.Add(newFace);
                    }
                }
            }

            var innerHoles = holeCycles.Select(_FinaliseInnerHole).ToList();
            var partitions = new List <ChordlessPolygon>();

            foreach (List <PolygonSplittingGraphNode> polyCycle in polyCycles)
            {
                partitions.Add(_FinalisePartition(polyCycle, connectedGroup, connectedGroups,
                                                  idsContainedInGroup, nodes, innerHoles));
            }

            if (partitions.Count == 0 && outerPerimCycle.Count > 0) //planar face that represents the outer perim is only relevant IFF there are no other (non-hole) faces
            {
                partitions.Add(_FinalisePartition(outerPerimCycle, connectedGroup, connectedGroups,
                                                  idsContainedInGroup, nodes, innerHoles));
            }
            return(partitions);
        }
Beispiel #3
0
 /// <summary>
 /// Checks if the cycle is a hole.  NOTE that no checks are done to ensure that cyclePerim is simplified, but
 /// this is not required as holes are always passed into PolygonSplittingGraph in their most simple form, and
 /// when extracted they keep the same vertices.
 /// Q: "But what if a bridge or chord or something connects the outside perimeter to a point on a hole where a vertex
 /// does not already exist?"
 /// A: This will never happen as the only way a hole will be connected to anything else is via a chord, which MUST
 /// be attached to a concave vertex.
 /// </summary>
 /// <param name="cyclePerim">Cycle discovered from planar graph.></param>
 /// <param name="holes">List of holes (which are a list of Vector2s).</param>
 /// <returns>True if the cycle is a hole, false otherwise.</returns>
 public static bool IsCycleHole(Vector2[] cyclePerim, List <Vector2>[] holes)
 {
     return(holes.Any(hole => GeometryFuncs.ArePolysIdentical(cyclePerim, hole.ToArray())));
 }