Ejemplo n.º 1
0
        /// <summary>
        /// Triangulates a list of vertices, interpreted as the vertices of a planar polygon in clockwise order.
        /// </summary>
        /// <param name="vertices">The vertices.</param>
        /// <param name="normal">The normal of the polygon, if not know, use the overload without normal.</param>
        /// <param name="triangles">The output in which the resulting triangles will be written.</param>
        public static void Triangulate(this IList <Vector3> vertices, Vector3 normal, List <int> triangles)
        {
            if (vertices.Count == 3)
            {
                triangles.AddRange(new[] { 0, 1, 2 });
                return;
            }

            if (vertices.Count == 4)
            {
                var d1 = Vector3.SqrMagnitude(vertices[0] - vertices[2]);
                var d2 = Vector3.SqrMagnitude(vertices[1] - vertices[3]);

                if (d1 > d2)
                {
                    triangles.AddRange(new[] { 0, 1, 3, 1, 2, 3 });
                    return;
                }

                triangles.AddRange(new[] { 0, 1, 2, 0, 2, 3 });
                return;
            }

            var vertexReferences = new LinkedList <(Vector3 v, float a, int i)>();

            for (var i = 0; i < vertices.Count; i++)
            {
                var prev    = vertices[Nums.Mod(i - 1, vertices.Count)];
                var current = vertices[i];
                var next    = vertices[(i + 1) % vertices.Count];

                vertexReferences.AddLast((current, Math3D.InnerAngle(prev, current, next, normal), i));
            }

            while (vertexReferences.Count > 3)
            {
                var prevCount   = vertexReferences.Count;
                var currentNode = vertexReferences.First;

                // The inner angle when all inner angles are equal, used to exclude inner angles larger than this, as a smaller angle must exist then. (incl. epsilon)
                var minAngle = 180f - (360f / vertexReferences.Count) + 1e-5f;

                while (currentNode != null)
                {
                    var prevNode = currentNode.Previous ?? vertexReferences.Last;
                    var nextNode = currentNode.Next ?? vertexReferences.First;
                    var current  = currentNode.Value;

                    var angle = current.a;

                    // Test whether the three points are nearly collinear.
                    if (Mathf.Abs((angle - 0.001f) % 180) <= 0.001f)
                    {
                        // Don't create invalid triangles.
                        continue;
                    }

                    // If the inner angle of this vertex is pointy enough
                    if (angle <= minAngle)
                    {
                        var v0        = prevNode.Value.v;
                        var v1        = current.v;
                        var v2        = nextNode.Value.v;
                        var collision = false;

                        // Test whether any other vertex intersects with the triangle that we want as ear
                        foreach (var vr in vertexReferences)
                        {
                            if (vr.i == prevNode.Value.i || vr.i == current.i || vr.i == nextNode.Value.i)
                            {
                                continue;
                            }

                            if (Math3D.IsPointInTriangle(v0, v1, v2, vr.v))
                            {
                                collision = true;
                                break;
                            }
                        }

                        // Extract the triangle, update the data structure.
                        if (!collision)
                        {
                            vertexReferences.Remove(currentNode);
                            triangles.Add(prevNode.Value.i);
                            triangles.Add(current.i);
                            triangles.Add(nextNode.Value.i);

                            // Update angles for prev and next
                            var pPrevVertex = (prevNode.Previous ?? vertexReferences.Last).Value.v;
                            var nNextVertex = (nextNode.Next ?? vertexReferences.First).Value.v;

                            var prevValue = prevNode.Value;
                            var nextValue = nextNode.Value;

                            prevNode.Value = (prevValue.v, Math3D.InnerAngle(pPrevVertex, prevValue.v, nextValue.v, normal), prevValue.i);
                            nextNode.Value = (nextValue.v, Math3D.InnerAngle(prevValue.v, nextValue.v, nNextVertex, normal), nextValue.i);

                            break;
                        }
                    }

                    currentNode = currentNode.Next;
                }

                // Prevents endless loops in case the provided polygon somehow breaks the algorithm.
                if (vertexReferences.Count >= prevCount)
                {
                    throw new ArgumentException("This polygon cannot be triangulated.");
                }
            }

            if (vertexReferences.Count == 3)
            {
                foreach (var value in vertexReferences)
                {
                    triangles.Add(value.i);
                }
            }
        }
Ejemplo n.º 2
0
 /// <summary>
 /// Triangulates a list of vertices, interpreted as the vertices of a planar polygon in clockwise order.
 /// </summary>
 /// <param name="vertices">The vertices.</param>
 /// <param name="triangles">The output in which the resulting triangles will be written.</param>
 public static void Triangulate(this IList <Vector3> vertices, List <int> triangles)
 {
     Triangulate(vertices, Math3D.PlanarPolygonNormal(vertices), triangles);
 }