예제 #1
0
        /// <summary>
        ///     Triangulates a Polygon into faces.
        /// </summary>
        /// <param name="points2D">The 2D points represented in loops.</param>
        /// <param name="isPositive">Indicates whether the corresponding loop is positive or not.</param>
        /// <returns>List&lt;Point[]&gt;, which represents vertices of new faces.</returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public static List<PolygonalFace> Run(List<Point[]> points2D, Boolean[] isPositive)
        {
            //Return variable triangles
            var triangles = new List<PolygonalFace>();

            #region Preprocessing
            //Preprocessing
            // 1) For each loop in points2D
            // 2)   Create nodes and lines from points, and retain whether a point
            //      was in a positive or negative loop.
            // 3)   Add nodes to an ordered loop (same as points2D except now Nodes)
            //      and a sorted loop (used for sweeping).
            var i = 0;
            var orderedLoops = new List<List<Node>>();
            var sortedLoops = new List<List<Node>>();
            var listPositive = isPositive.ToList<bool>();

            foreach (var loop in points2D)
            {
                var orderedLoop = new List<Node>();

                //Create first node
                var nodeType = GetNodeType(loop.Last(), loop[0], loop[1], isPositive[i]);
                var firstNode = new Node(loop[0], nodeType, i);
                var previousNode = firstNode;
                orderedLoop.Add(firstNode);

                //Create other nodes
                for (var j = 1; j < loop.Count() - 1; j++)
                {
                    //Create New Node
                    nodeType = GetNodeType(loop[j - 1], loop[j], loop[j + 1], isPositive[i]);
                    var node = new Node(loop[j], nodeType, i);

                    //Add node to the ordered loop
                    orderedLoop.Add(node);

                    //Create New Line
                    var line = new Line(previousNode, node);
                    previousNode.StartLine = line;
                    node.EndLine = line;

                    previousNode = node;
                }

                //Create last node
                nodeType = GetNodeType(loop[loop.Count() - 2], loop[loop.Count() - 1], loop[0], isPositive[i]);
                var lastNode = new Node(loop[loop.Count() - 1], nodeType, i);
                orderedLoop.Add(lastNode);

                //Create both missing lines
                var line1 = new Line(previousNode, lastNode);
                previousNode.StartLine = line1;
                lastNode.EndLine = line1;
                var line2 = new Line(lastNode, firstNode);
                lastNode.StartLine = line2;
                firstNode.EndLine = line2;

                //Sort nodes by descending Y, ascending X
                var sortedLoop = orderedLoop.OrderByDescending(node => node.Y).ThenBy(node => node.X).ToList<Node>();
                orderedLoops.Add(orderedLoop);
                sortedLoops.Add(sortedLoop);
                i++;
            }
            #endregion

            // 1) For each positive loop
            // 2)   Remove it from orderedLoops.
            // 3)   Create a new group
            // 4)   Insert the first node from all the negative loops remaining into the group list in the correct sorted order.
            // 5)   Use the red-black tree to determine if the first node from a negative loop is inside the polygon.
            //         Refer to http://alienryderflex.com/polygon/ for how to determine if a point is inside a polygon.
            // 6)   If not inside, remove that nodes from the group list.
            // 7)      else remove the negative loop from orderedLoops and merge the negative loop with the group list.
            // 8)   Continue with Trapezoidation
            List<List<Node>> completeListSortedLoops = new List<List<Node>>(sortedLoops);
            while (orderedLoops.Any())
            {
                //Get information about positive loop, remove from loops, and create new group
                i = listPositive.FindIndex(true);
                var posOrderedLoop = new List<Node>(orderedLoops[i]);
                var sortedGroup = new List<Node>(sortedLoops[i]);
                listPositive.RemoveAt(i);
                orderedLoops.RemoveAt(i);
                sortedLoops.RemoveAt(i);

                //Insert the first node from all the negative loops remaining into the group list in the correct sorted order.
                for (var j = 0; j < orderedLoops.Count(); j++)
                {
                    if (listPositive[j] == false)
                    {
                        InsertNodeInSortedList(sortedGroup, sortedLoops[j][0]);
                    }
                }

                //inititallize lineList and sortedNodes
                var lineList = new List<Line>();
                var nodes = new List<Node>();

                #region Trapezoidize Polygons

                var trapTree = new List<PartialTrapezoid>();
                var completedTrapezoids = new List<Trapezoid>();

                //Use the red-black tree to determine if the first node from a negative loop is inside the polygon.
                //for each node in sortedNodes, update the lineList. Note that at this point each node only has two edges.
                foreach (var node in sortedGroup)
                {
                    Line leftLine = null;
                    Line rightLine = null;

                    //Check if negative loop is inside polygon
                    //note that listPositive changes order /size , while isPositive is static like loopID.
                    //Similarly points2D is static.
                    if (node == completeListSortedLoops[node.LoopID][0] && isPositive[node.LoopID] == false) //if first point in the sorted loop and loop is negative
                    {
                        if (LinesToLeft(node, lineList, out leftLine) % 2 != 0) //If remainder is not equal to 0, then it is odd.
                        {
                            if (LinesToRight(node, lineList, out rightLine) % 2 == 0) //If remainder is not equal to 0, then it is odd.
                            {
                                //NOTE: This node must be a reflex upward point by observation
                                //leftLine and rightLine are set in the two previous call and are now not null.

                                //Add remaining points from loop into sortedGroup.
                                MergeSortedListsOfNodes(sortedGroup, completeListSortedLoops[node.LoopID], node);
                            }
                            else //Number of lines is even. Remove from group and go to next node
                            {
                                sortedGroup.Remove(node);
                                continue;
                            }
                        }
                        else //Number of lines is even. Remove from group and go to next node
                        {
                            sortedGroup.Remove(node);
                            continue;
                        }
                    }

                    //Add to or remove from Red-Black Tree
                    if (lineList.Contains(node.StartLine))
                    {
                        lineList.Remove(node.StartLine);
                    }
                    else
                    {
                        lineList.Add(node.StartLine);
                    }
                    if (lineList.Contains(node.EndLine))
                    {
                        lineList.Remove(node.EndLine);
                    }
                    else
                    {
                        lineList.Add(node.EndLine);
                    }

                    switch (node.Type)
                    {
                        case NodeType.DownwardReflex:
                            {
                                FindLeftLine(node, lineList, out leftLine);
                                FindRightLine(node, lineList, out rightLine);

                                //Close two trapezoids
                                //Left trapezoid:
                                InsertTrapezoid(node, leftLine, node.StartLine, ref trapTree, ref completedTrapezoids);
                                //Right trapezoid:
                                InsertTrapezoid(node, node.EndLine, rightLine, ref trapTree, ref completedTrapezoids);

                                //Create one new partial trapezoid
                                var newPartialTrapezoid = new PartialTrapezoid(node, leftLine, rightLine);
                                trapTree.Add(newPartialTrapezoid);
                            }
                            break;
                        case NodeType.UpwardReflex:
                            {
                                if (leftLine == null) //If from the first negative point, leftLine and rightLine will already be set.
                                {
                                    FindLeftLine(node, lineList, out leftLine);
                                    FindRightLine(node, lineList, out rightLine);
                                }

                                //Close one trapezoid
                                InsertTrapezoid(node, leftLine, rightLine, ref trapTree, ref completedTrapezoids);

                                //Create two new partial trapezoids
                                //Left Trapezoid
                                var newPartialTrapezoid1 = new PartialTrapezoid(node, leftLine, node.EndLine);
                                trapTree.Add(newPartialTrapezoid1);
                                //Right Trapezoid
                                var newPartialTrapezoid2 = new PartialTrapezoid(node, node.StartLine, rightLine);
                                trapTree.Add(newPartialTrapezoid2);
                            }
                            break;
                        case NodeType.Peak:
                            {
                                //Create one new partial trapezoid
                                var newPartialTrapezoid = new PartialTrapezoid(node, node.StartLine, node.EndLine);
                                trapTree.Add(newPartialTrapezoid);
                            }
                            break;
                        case NodeType.Root:
                            {
                                //Close one trapezoid
                                InsertTrapezoid(node, node.EndLine, node.StartLine, ref trapTree, ref completedTrapezoids);
                            }
                            break;
                        case NodeType.Left:
                            {
                                //Create one trapezoid
                                FindLeftLine(node, lineList, out leftLine);
                                rightLine = node.StartLine;
                                InsertTrapezoid(node, leftLine, rightLine, ref trapTree, ref completedTrapezoids);

                                //Create one new partial trapezoid
                                var newPartialTrapezoid = new PartialTrapezoid(node, leftLine, node.EndLine);
                                trapTree.Add(newPartialTrapezoid);
                            }
                            break;
                        case NodeType.Right:
                            {
                                //Create one trapezoid
                                FindRightLine(node, lineList, out rightLine);
                                leftLine = node.EndLine;
                                InsertTrapezoid(node, leftLine, rightLine, ref trapTree, ref completedTrapezoids);

                                //Create one new partial trapezoid
                                var newPartialTrapezoid = new PartialTrapezoid(node, node.StartLine, rightLine);
                                trapTree.Add(newPartialTrapezoid);
                            }
                            break;
                    }
                }
                #endregion

                #region Create Monotone Polygons

                //for each trapezoid with a reflex edge, split in two.
                //Insert new trapezoids in correct position in list.
                for (var j = 0; j < completedTrapezoids.Count; j++)
                {
                    var trapezoid = completedTrapezoids[j];
                    if (trapezoid.TopNode.Type == NodeType.DownwardReflex) //If upper node is reflex down (bottom node could be reflex up, reflex down, or other)
                    {
                        var newLine = new Line(trapezoid.TopNode, trapezoid.BottomNode);
                        completedTrapezoids.RemoveAt(j);
                        var leftTrapezoid = new Trapezoid(trapezoid.TopNode, trapezoid.BottomNode, trapezoid.LeftLine, newLine);
                        var rightTrapezoid = new Trapezoid(trapezoid.TopNode, trapezoid.BottomNode, newLine, trapezoid.RightLine);
                        completedTrapezoids.Insert(j, rightTrapezoid); //right trapezoid will end up right below left trapezoid
                        completedTrapezoids.Insert(j, leftTrapezoid); //left trapezoid will end up were the original trapezoid was located
                        j++; //Extra counter to skip extra trapezoid
                    }
                    else if (trapezoid.BottomNode.Type == NodeType.UpwardReflex) //If bottom node is reflex up (if TopNode.Type = 0, this if statement will be skipped).
                    {
                        var newLine = new Line(trapezoid.TopNode, trapezoid.BottomNode);
                        completedTrapezoids.RemoveAt(j);
                        var leftTrapezoid = new Trapezoid(trapezoid.TopNode, trapezoid.BottomNode, trapezoid.LeftLine, newLine);
                        var rightTrapezoid = new Trapezoid(trapezoid.TopNode, trapezoid.BottomNode, newLine, trapezoid.RightLine);
                        completedTrapezoids.Insert(j, rightTrapezoid); //right trapezoid will end up right below left trapezoid
                        completedTrapezoids.Insert(j, leftTrapezoid); //left trapezoid will end up were the original trapezoid was located
                        j++; //Extra counter to skip extra trapezoid
                    }
                }

                //Create Monotone Polygons from Trapezoids
                var currentTrap = completedTrapezoids[0];
                var monotoneTrapPolygon = new List<Trapezoid> { currentTrap };
                var monotoneTrapPolygons = new List<List<Trapezoid>> { monotoneTrapPolygon };
                //for each trapezoid except the first one, which was added in the intitialization above.
                for (var j = 1; j < completedTrapezoids.Count; j++)
                {
                    //Check if next trapezoid can attach to any existing monotone polygon
                    var boolstatus = false;
                    for (var k = 0; k < monotoneTrapPolygons.Count; k++)
                    {
                        currentTrap = monotoneTrapPolygons[k].Last();

                        if (currentTrap.BottomNode == completedTrapezoids[j].TopNode)
                        {
                            if (currentTrap.LeftLine == completedTrapezoids[j].LeftLine ||
                                currentTrap.RightLine == completedTrapezoids[j].RightLine)
                            {
                                monotoneTrapPolygons[k].Add(completedTrapezoids[j]);
                                boolstatus = true;
                                break;
                            }
                        }
                    }
                    // If they cannot be attached to any existing monotone polygon, create a new monotone polygon
                    if (boolstatus == false)
                    {
                        var trapezoidList = new List<Trapezoid> { completedTrapezoids[j] };
                        monotoneTrapPolygons.Add(trapezoidList);
                    }
                }

                //Convert the lists of trapezoids that form monotone polygons into the monotone polygon class\
                //This class includes a sorted list of all the points in the monotone polygon and two monotone chains.
                //Both of these lists are used during traingulation.
                var monotonePolygons = new List<MonotonePolygon>();
                foreach (var monotoneTrapPoly in monotoneTrapPolygons)
                {
                    //Biuld the right left chains and the sorted list of all nodes
                    var monotoneRightChain = new List<Node>();
                    var monotoneLeftChain = new List<Node>();
                    var sortedMonotonePolyNodes = new List<Node>();

                    //Add upper node to both chains and sorted list
                    monotoneRightChain.Add(monotoneTrapPoly[0].TopNode);
                    monotoneLeftChain.Add(monotoneTrapPoly[0].TopNode);
                    sortedMonotonePolyNodes.Add(monotoneTrapPoly[0].TopNode);

                    //Add all the middle nodes to one chain (right or left)
                    for (var j = 1; j < monotoneTrapPoly.Count; j++)
                    {
                        //Add the topNode of each trapezoid (minus the initial trapezoid) to the sorted list.
                        sortedMonotonePolyNodes.Add(monotoneTrapPoly[j].TopNode);

                        //If trapezoid upper node is on the right line, add it to the right chain
                        if (monotoneTrapPoly[j].RightLine.FromNode == monotoneTrapPoly[j].TopNode ||
                            monotoneTrapPoly[j].RightLine.ToNode == monotoneTrapPoly[j].TopNode)
                        {
                            monotoneRightChain.Add(monotoneTrapPoly[j].TopNode);
                        }
                        //Else add it to the left chain
                        else
                        {
                            monotoneLeftChain.Add(monotoneTrapPoly[j].TopNode);
                        }
                    }

                    //Add bottom node of last trapezoid to both chains and sorted list
                    monotoneRightChain.Add(monotoneTrapPoly.Last().BottomNode);
                    monotoneLeftChain.Add(monotoneTrapPoly.Last().BottomNode);
                    sortedMonotonePolyNodes.Add(monotoneTrapPoly.Last().BottomNode);

                    //Create new monotone polygon based on these two chains and sorted list.
                    var monotonePolygon = new MonotonePolygon(monotoneLeftChain, monotoneRightChain, sortedMonotonePolyNodes);
                    monotonePolygons.Add(monotonePolygon);
                }
                #endregion

                #region Triangulate Monotone Polygons
                //Triangulates the monotone polygons
                foreach (var monotonePolygon2 in monotonePolygons)
                    triangles.AddRange(Triangulate(monotonePolygon2));
                #endregion
            }
            return triangles;
        }
예제 #2
0
 internal static void InsertTrapezoid(Node node, Line leftLine, Line rightLine, ref List<PartialTrapezoid> trapTree, ref List<Trapezoid> completedTrapezoids)
 {
     var matchesTrap = false;
     var i = 0;
     while (matchesTrap == false && i < trapTree.Count)
     {
         var partialTrap = trapTree[i];
         if (partialTrap.Contains(leftLine, rightLine))
         {
             var newTrapezoid = new Trapezoid(partialTrap.TopNode, node, partialTrap.LeftLine, partialTrap.RightLine);
             completedTrapezoids.Add(newTrapezoid);
             trapTree.Remove(partialTrap);
             matchesTrap = true;
         }
         i++;
     }
 }