Example #1
0
        /// <summary>
        /// Triangulates the shape.
        /// </summary>
        /// <returns>The output list.</returns>
        public List<float[]> Triangulate(float[] offset, float scale = 1.0f)
        {
            var output = new List<float[]>();

            Points.Reverse();

            var context = new SweepContext();
            context.AddPoints(Points);

            // Hole edges
            foreach (Shape h in Holes)
                context.AddHole(h.Points);

            context.InitTriangulation();
            var sweep = new Sweep();
            sweep.Triangulate(context);

            var triangles = context.GetTriangles();

            foreach (Triangle tri in triangles)
            {
                //tri.ReversePointFlow();
                output.Add(((float[])tri.Points[0]).VectorScale(scale).Add(offset));
                output.Add(((float[])tri.Points[2]).VectorScale(scale).Add(offset));
                output.Add(((float[])tri.Points[1]).VectorScale(scale).Add(offset));
            }

            return output;
        }
Example #2
0
 private void FillEdgeEvent(SweepContext tcx, Edge edge, Node node)
 {
     if (tcx.EdgeEvent.Right)
     {
         FillRightAboveEdgeEvent(tcx, edge, node);
     }
     else
     {
         FillLeftAboveEdgeEvent(tcx, edge, node);
     }
 }
Example #3
0
        public void Triangulate(SweepContext tcx)
        {
            _nodes = new List <Node>();

            tcx.InitTriangulation();
            tcx.CreateAdvancingFront(_nodes);

            // Sweep points; build mesh
            SweepPoints(tcx);

            // Clean up
            FinalizationPolygon(tcx);
        }
Example #4
0
        private void FinalizationPolygon(SweepContext tcx)
        {
            // Get an Internal triangle to start with
            Triangle t = tcx.Front._head.Next.Triangle;
            TriPoint p = tcx.Front._head.Next.Point;

            while (!t.GetConstrainedEdgeCW(p))
            {
                t = t.NeighborCCW(p);
            }

            tcx.MeshClean(t);
        }
Example #5
0
        private void FillBasinReq(SweepContext tcx, Node node)
        {
            // if shallow stop filling
            if (IsShallow(tcx, node))
            {
                return;
            }

            Fill(tcx, node);

            if (node.Prev == tcx.Basin.LeftNode && node.Next == tcx.Basin.RightNode)
            {
                return;
            }
            else if (node.Prev == tcx.Basin.LeftNode)
            {
                Winding o = TriUtil.Orient2d(node.Point, node.Next.Point, node.Next.Next.Point);
                if (o == Winding.CW)
                {
                    return;
                }

                node = node.Next;
            }
            else if (node.Next == tcx.Basin.RightNode)
            {
                Winding o = TriUtil.Orient2d(node.Point, node.Prev.Point, node.Prev.Prev.Point);
                if (o == Winding.CCW)
                {
                    return;
                }

                node = node.Prev;
            }
            else
            {
                // Continue with the neighbor node with lowest Y value
                if (node.Prev.Point.Y < node.Next.Point.Y)
                {
                    node = node.Prev;
                }
                else
                {
                    node = node.Next;
                }
            }

            FillBasinReq(tcx, node);
        }
Example #6
0
 private void FillLeftAboveEdgeEvent(SweepContext tcx, Edge edge, Node node)
 {
     while (node.Prev.Point.X > edge.P.X)
     {
         // Check if next node is below the edge
         if (TriUtil.Orient2d(edge.Q, node.Prev.Point, edge.P) == Winding.CW)
         {
             FillLeftBelowEdgeEvent(tcx, edge, node);
         }
         else
         {
             node = node.Prev;
         }
     }
 }
Example #7
0
        private void SweepPoints(SweepContext tcx)
        {
            for (int i = 1; i < tcx.PointCount(); i++)
            {
                TriPoint point = tcx.GetPoint(i);
                Node     node  = PointEvent(tcx, point);

                if (point.EdgeList != null)
                {
                    for (int j = 0; j < point.EdgeList.Count; j++)
                    {
                        EdgeEvent(tcx, point.EdgeList[j], node);
                    }
                }
            }
        }
Example #8
0
        private void EdgeEvent(SweepContext tcx, Edge edge, Node node)
        {
            tcx.EdgeEvent.ConstrainedEdge = edge;
            tcx.EdgeEvent.Right           = (edge.P.X > edge.Q.X);

            if (IsEdgeSideOfTriangle(node.Triangle, edge.P, edge.Q))
            {
                return;
            }

            // For now we will do all needed filling
            // TODO: integrate with flip process might give some better performance
            //       but for now this avoid the issue with cases that needs both flips and fills
            FillEdgeEvent(tcx, edge, node);
            EdgeEvent(tcx, edge.P, edge.Q, node.Triangle, edge.Q);
        }
Example #9
0
        private Node PointEvent(SweepContext tcx, TriPoint point)
        {
            Node node     = tcx.LocateNode(point);
            Node new_node = NewFrontTriangle(tcx, point, node);

            // Only need to check +epsilon since point never have smaller
            // x value than node due to how we fetch nodes from the front
            if (point.X <= node.Point.X + TriUtil.EPSILON)
            {
                Fill(tcx, node);
            }

            //tcx.AddNode(new_node);

            FillAdvancingFront(tcx, new_node);
            return(new_node);
        }
Example #10
0
 private void FillLeftBelowEdgeEvent(SweepContext tcx, Edge edge, Node node)
 {
     if (node.Point.X > edge.P.X)
     {
         if (TriUtil.Orient2d(node.Point, node.Prev.Point, node.Prev.Prev.Point) == Winding.CW)
         {
             // Concave
             FillLeftConcaveEdgeEvent(tcx, edge, node);
         }
         else
         {
             // Convex
             FillLeftConvexEdgeEvent(tcx, edge, node);
             // Retry this one
             FillLeftBelowEdgeEvent(tcx, edge, node);
         }
     }
 }
Example #11
0
        /// <summary>
        /// Triangulates the shape.
        /// </summary>
        /// <returns>The output list.</returns>
        public List<Triangle> Triangulate()
        {
            var context = new SweepContext();

            var distinctPoints = Points.Distinct().ToList();
            context.AddPoints(distinctPoints);

            foreach (var hole in Holes)
                context.AddHole(hole.Points);

            context.InitTriangulation();

            var sweep = new Sweep();

            sweep.Triangulate(context);

            return context.GetTriangles();
        }
Example #12
0
        private void Fill(SweepContext tcx, Node node)
        {
            Triangle triangle = new Triangle(node.Prev.Point, node.Point, node.Next.Point);

            triangle.MarkNeighbor(node.Prev.Triangle);
            triangle.MarkNeighbor(node.Triangle);

            tcx.AddToMap(triangle);

            // Update the advancing front
            node.Prev.Next = node.Next;
            node.Next.Prev = node.Prev;

            // If it was legalized the triangle has already been mapped
            if (!Legalize(tcx, triangle))
            {
                tcx.MapTriangleToNodes(triangle);
            }
        }
Example #13
0
        private void FillBasin(SweepContext tcx, Node node)
        {
            if (TriUtil.Orient2d(node.Point, node.Next.Point, node.Next.Next.Point) == Winding.CCW)
            {
                tcx.Basin.LeftNode = node.Next.Next;
            }
            else
            {
                tcx.Basin.LeftNode = node.Next;
            }

            // Find the bottom and right node
            tcx.Basin.BottomNode = tcx.Basin.LeftNode;
            while (tcx.Basin.BottomNode.Next != null &&
                   tcx.Basin.BottomNode.Point.Y >= tcx.Basin.BottomNode.Next.Point.Y)
            {
                tcx.Basin.BottomNode = tcx.Basin.BottomNode.Next;
            }
            if (tcx.Basin.BottomNode == tcx.Basin.LeftNode)
            {
                // No valid basin
                return;
            }

            tcx.Basin.RightNode = tcx.Basin.BottomNode;
            while (tcx.Basin.RightNode.Next != null &&
                   tcx.Basin.RightNode.Point.Y < tcx.Basin.RightNode.Next.Point.Y)
            {
                tcx.Basin.RightNode = tcx.Basin.RightNode.Next;
            }
            if (tcx.Basin.RightNode == tcx.Basin.BottomNode)
            {
                // No valid basins
                return;
            }

            tcx.Basin.Width       = tcx.Basin.RightNode.Point.X - tcx.Basin.LeftNode.Point.X;
            tcx.Basin.LeftHighest = tcx.Basin.LeftNode.Point.Y > tcx.Basin.RightNode.Point.Y;

            FillBasinReq(tcx, tcx.Basin.BottomNode);
        }
Example #14
0
 private void FillLeftConcaveEdgeEvent(SweepContext tcx, Edge edge, Node node)
 {
     Fill(tcx, node.Prev);
     if (node.Prev.Point != edge.P)
     {
         // Next above or below edge?
         if (TriUtil.Orient2d(edge.Q, node.Prev.Point, edge.P) == Winding.CW)
         {
             // Below
             if (TriUtil.Orient2d(node.Point, node.Prev.Point, node.Prev.Prev.Point) == Winding.CW)
             {
                 // Next is concave
                 FillLeftConcaveEdgeEvent(tcx, edge, node);
             }
             else
             {
                 // Next is convex
             }
         }
     }
 }
Example #15
0
        private bool IsShallow(SweepContext tcx, Node node)
        {
            double height;

            if (tcx.Basin.LeftHighest)
            {
                height = tcx.Basin.LeftNode.Point.Y - node.Point.Y;
            }
            else
            {
                height = tcx.Basin.RightNode.Point.Y - node.Point.Y;
            }

            // if shallow stop filling
            if (tcx.Basin.Width > height)
            {
                return(true);
            }

            return(false);
        }
Example #16
0
        private void FlipEdgeEvent(SweepContext tcx, TriPoint ep, TriPoint eq, Triangle t, TriPoint p)
        {
            Triangle ot = t.NeighborAcross(p);
            TriPoint op = ot.OppositePoint(t, p);

            if (TriUtil.InScanArea(p, t.PointCCW(p), t.PointCW(p), op))
            {
                // Lets rotate shared edge one vertex CW
                RotateTrianglePair(t, p, ot, op);
                tcx.MapTriangleToNodes(t);
                tcx.MapTriangleToNodes(ot);

                if (p == eq && op == ep)
                {
                    if (eq == tcx.EdgeEvent.ConstrainedEdge.Q && ep == tcx.EdgeEvent.ConstrainedEdge.P)
                    {
                        t.MarkConstrainedEdge(ep, eq);
                        ot.MarkConstrainedEdge(ep, eq);
                        Legalize(tcx, t);
                        Legalize(tcx, ot);
                    }
                    else
                    {
                        // XXX: I think one of the triangles should be legalized here?
                    }
                }
                else
                {
                    Winding o = TriUtil.Orient2d(eq, op, ep);
                    t = NextFlipTriangle(tcx, o, t, ot, p, op);
                    FlipEdgeEvent(tcx, ep, eq, t, p);
                }
            }
            else
            {
                TriPoint newP = NextFlipPoint(ep, eq, ot, op);
                FlipScanEdgeEvent(tcx, ep, eq, t, ot, newP);
                EdgeEvent(tcx, ep, eq, t, p);
            }
        }
Example #17
0
        private void FillAdvancingFront(SweepContext tcx, Node n)
        {
            // Fill right holes
            Node node = n.Next;

            while (node.Next != null)
            {
                // if HoleAngle exceeds 90 degrees then break.
                if (LargeHole_DontFill(node))
                {
                    break;
                }
                Fill(tcx, node);
                node = node.Next;
            }

            // Fill left holes
            node = n.Prev;

            while (node.Prev != null)
            {
                // if HoleAngle exceeds 90 degrees then break.
                if (LargeHole_DontFill(node))
                {
                    break;
                }
                Fill(tcx, node);
                node = node.Prev;
            }

            // Fill right basins
            if (n.Next != null && n.Next.Next != null)
            {
                double angle = BasinAngle(n);
                if (angle < TriUtil.PI_3div4)
                {
                    FillBasin(tcx, n);
                }
            }
        }
Example #18
0
        private Triangle NextFlipTriangle(SweepContext tcx, Winding o, Triangle t, Triangle ot, TriPoint p, TriPoint op)
        {
            int edge_index;

            if (o == Winding.CCW)
            {
                // ot is not crossing edge after flip
                edge_index = ot.EdgeIndex(p, op);
                ot.DelaunayEdge[edge_index] = true;
                Legalize(tcx, ot);
                ot.ClearDelaunayEdges();
                return(t);
            }

            // t is not crossing edge after flip
            edge_index = t.EdgeIndex(p, op);

            t.DelaunayEdge[edge_index] = true;
            Legalize(tcx, t);
            t.ClearDelaunayEdges();
            return(ot);
        }
Example #19
0
        private Node NewFrontTriangle(SweepContext tcx, TriPoint point, Node node)
        {
            Triangle triangle = new Triangle(point, node.Point, node.Next.Point);

            triangle.MarkNeighbor(node.Triangle);
            tcx.AddToMap(triangle);

            Node new_node = new Node(point);

            _nodes.Add(new_node);

            new_node.Next  = node.Next;
            new_node.Prev  = node;
            node.Next.Prev = new_node;
            node.Next      = new_node;

            if (!Legalize(tcx, triangle))
            {
                tcx.MapTriangleToNodes(triangle);
            }

            return(new_node);
        }
Example #20
0
 private void FillRightConvexEdgeEvent(SweepContext tcx, Edge edge, Node node)
 {
     // Next concave or convex?
     if (TriUtil.Orient2d(node.Next.Point, node.Next.Next.Point, node.Next.Next.Next.Point) == Winding.CCW)
     {
         // Concave
         FillRightConcaveEdgeEvent(tcx, edge, node.Next);
     }
     else
     {
         // Convex
         // Next above or below edge?
         if (TriUtil.Orient2d(edge.Q, node.Next.Next.Point, edge.P) == Winding.CCW)
         {
             // Below
             FillRightConvexEdgeEvent(tcx, edge, node.Next);
         }
         else
         {
             // Above
         }
     }
 }
Example #21
0
        private void FlipScanEdgeEvent(SweepContext tcx, TriPoint ep, TriPoint eq, Triangle flip_triangle, Triangle t, TriPoint p)
        {
            Triangle ot = t.NeighborAcross(p);
            TriPoint op = ot.OppositePoint(t, p);

            if (TriUtil.InScanArea(eq, flip_triangle.PointCCW(eq), flip_triangle.PointCW(eq), op))
            {
                // flip with new edge op->eq
                FlipEdgeEvent(tcx, eq, op, ot, op);
                // TODO: Actually I just figured out that it should be possible to
                //       improve this by getting the next ot and op before the the above
                //       flip and continue the flipScanEdgeEvent here
                // set new ot and op here and loop back to inScanArea test
                // also need to set a new flip_triangle first
                // Turns out at first glance that this is somewhat complicated
                // so it will have to wait.
            }
            else
            {
                TriPoint newP = NextFlipPoint(ep, eq, ot, op);
                FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP);
            }
        }
Example #22
0
        private void EdgeEvent(SweepContext tcx, TriPoint ep, TriPoint eq, Triangle triangle, TriPoint point)
        {
            if (IsEdgeSideOfTriangle(triangle, ep, eq))
            {
                return;
            }

            TriPoint p1 = triangle.PointCCW(point);
            Winding  o1 = TriUtil.Orient2d(eq, p1, ep);

            if (o1 == Winding.Collinear)
            {
                if (triangle.Contains(eq, p1))
                {
                    triangle.MarkConstrainedEdge(eq, p1);
                    // We are modifying the constraint maybe it would be better to
                    // not change the given constraint and just keep a variable for the new constraint
                    tcx.EdgeEvent.ConstrainedEdge.Q = p1;
                    triangle = triangle.NeighborAcross(point);
                    EdgeEvent(tcx, ep, p1, triangle, p1);
                }
                else
                {
                    throw new NotSupportedException("EdgeEvent - collinear points not supported");
                }

                return;
            }

            TriPoint p2 = triangle.PointCW(point);
            Winding  o2 = TriUtil.Orient2d(eq, p2, ep);

            if (o2 == Winding.Collinear)
            {
                if (triangle.Contains(eq, p2))
                {
                    triangle.MarkConstrainedEdge(eq, p2);
                    // We are modifying the constraint maybe it would be better to
                    // not change the given constraint and just keep a variable for the new constraint
                    tcx.EdgeEvent.ConstrainedEdge.Q = p2;
                    triangle = triangle.NeighborAcross(point);
                    EdgeEvent(tcx, ep, p2, triangle, p2);
                }
                else
                {
                    throw new NotSupportedException("EdgeEvent - collinear points not supported");
                }

                return;
            }

            if (o1 == o2)
            {
                // Need to decide if we are rotating CW or CCW to get to a triangle
                // that will cross edge
                if (o1 == Winding.CW)
                {
                    triangle = triangle.NeighborCCW(point);
                }
                else
                {
                    triangle = triangle.NeighborCW(point);
                }
                EdgeEvent(tcx, ep, eq, triangle, point);
            }
            else
            {
                // This triangle crosses constraint so lets flippin start!
                FlipEdgeEvent(tcx, ep, eq, triangle, point);
            }
        }
Example #23
0
        private bool Legalize(SweepContext tcx, Triangle t)
        {
            // To legalize a triangle we start by finding if any of the three edges
            // violate the Delaunay condition
            for (int i = 0; i < 3; i++)
            {
                if (t.DelaunayEdge[i])
                {
                    continue;
                }

                Triangle ot = t.GetNeighbor(i);

                if (ot != null)
                {
                    TriPoint p  = t.Points[i];
                    TriPoint op = ot.OppositePoint(t, p);
                    int      oi = ot.Index(op);

                    // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
                    // then we should not try to legalize
                    if (ot.ConstrainedEdge[oi] || ot.DelaunayEdge[oi])
                    {
                        t.ConstrainedEdge[i] = ot.ConstrainedEdge[oi];
                        continue;
                    }

                    bool inside = Incircle(p, t.PointCCW(p), t.PointCW(p), op);

                    if (inside)
                    {
                        // Lets mark this shared edge as Delaunay
                        t.DelaunayEdge[i]   = true;
                        ot.DelaunayEdge[oi] = true;

                        // Lets rotate shared edge one vertex CW to legalize it
                        RotateTrianglePair(t, p, ot, op);

                        // We now got one valid Delaunay Edge shared by two triangles
                        // This gives us 4 new edges to check for Delaunay

                        // Make sure that triangle to node mapping is done only one time for a specific triangle
                        bool not_legalized = !Legalize(tcx, t);
                        if (not_legalized)
                        {
                            tcx.MapTriangleToNodes(t);
                        }

                        not_legalized = !Legalize(tcx, ot);
                        if (not_legalized)
                        {
                            tcx.MapTriangleToNodes(ot);
                        }

                        // Reset the Delaunay edges, since they only are valid Delaunay edges
                        // until we add a new triangle or point.
                        // XXX: need to think about this. Can these edges be tried after we
                        //      return to previous recursive level?
                        t.DelaunayEdge[i]   = false;
                        ot.DelaunayEdge[oi] = false;

                        // If triangle have been legalized no need to check the other edges since
                        // the recursive legalization will handles those so we can end here.
                        return(true);
                    }
                }
            }

            return(false);
        }