Example #1
0
        /// <summary>
        /// Determines if a non complex polygon is oriented clockwise (CW) or counter-clockwise (CCW).
        /// Works for convex as well as concave polygons.
        /// </summary>
        /// <param name="vertices">Ordered vertices defining th polygon</param>
        /// <returns>Polygon oriented CW (true) or CCW (false)</returns>
        /// /// <exception cref="ArgumentException">If fewer than 3 vertices are provided.</exception>
        public static bool PolygonOrientedClockwise(List <Vertex> vertices)
        {
            if (vertices.Count < 3)
            {
                throw new ArgumentException($"A polygon needs at least 3 vertices. Vertices provided: {vertices.Count}");
            }

            //https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order

            var sum         = 0f;
            var vertexCount = vertices.Count; //only get count once

            for (var i = 0; i < vertexCount; i++)
            {
                var indexOfNextVertex = CollectionsHelper.WrapIndex(i + 1, vertexCount);

                var x1 = vertices[i].Position.x;
                var y1 = vertices[i].Position.y;
                var x2 = vertices[indexOfNextVertex].Position.x;
                var y2 = vertices[indexOfNextVertex].Position.y;

                sum += (x2 - x1) * (y2 + y1);
            }

            // sum > 0 -> clockwise
            // sum = 0 -> Positive and negative areas cancel out, as in a figure-eight, probably not intended if that ever happens. No idea if this would still work; log warning
            // sum < 0 -> counter-clockwise
            if (Math.Abs(sum) < 0.000010f)
            {
                Debug.LogWarning("Sum of all positive and negative areas of polygon cancel out, something's probably wrong with your polygon.");
            }

            return(sum > 0);
        }
Example #2
0
        /// <summary>
        /// Triangulates a convex or concave polygon.
        /// The points on the polygon should be ordered counter-clockwise.
        /// This algorithm is called ear clipping and it's O(n*n) Another common algorithm is dividing it into trapezoids and it's O(n log n).
        /// </summary>
        /// <param name="vertices">Ordered list of vertices making up the polygon. Can be oriented CW as well as CCW.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException">If fewer than 3 vertices are provided.</exception>
        public static List <Triangle> TriangulateConcaveOrConvexPolygon(List <Vertex> vertices)
        {
            if (vertices.Count < 3)
            {
                throw new ArgumentException($"A polygon needs at least 3 vertices. Vertices provided: {vertices.Count}");
            }

            //Rest of functions needs orientation to be ccw. Change orientation if it's cw
            if (GeometryHelper.PolygonOrientedClockwise(vertices))
            {
                vertices.Reverse();
            }

            var triangles = new List <Triangle>();

            //If we just have three points, we can just return a single triangle
            if (vertices.Count == 3)
            {
                triangles.Add(new Triangle(vertices[0], vertices[2], vertices[1])); //vertices are expected to be in ccw order but Unity draw order for triangles in meshes is cw, hence 0,2,1 and not 0,1,2

                return(triangles);
            }

            //Step 1. Set the next and prev vertex for every vertex

            //Find the next and previous vertex
            for (var i = 0; i < vertices.Count; i++)
            {
                var nextPos = CollectionsHelper.WrapIndex(i + 1, vertices.Count);
                var prevPos = CollectionsHelper.WrapIndex(i - 1, vertices.Count);

                vertices[i].PreviousVertex = vertices[prevPos];
                vertices[i].NextVertex     = vertices[nextPos];
            }


            //Step 2. Find the reflex (concave) and convex vertices, and ear vertices
            foreach (var v in vertices)
            {
                SetIfVertexIsConcaveOrConvex(v);
            }

            //Have to find the ears after we have found if the vertex is concave or convex
            var earVertices = new List <Vertex>();

            foreach (var v in vertices)
            {
                SetIfVertexIsEar(v, vertices);
                if (v.IsEar)
                {
                    earVertices.Add(v);
                }
            }


            //Step 3. Triangulate!
            while (true)
            {
                //This means we have just one triangle left
                if (vertices.Count == 3)
                {
                    //The final triangle
                    triangles.Add(new Triangle(vertices[0], vertices[0].PreviousVertex, vertices[0].NextVertex));

                    break;
                }

                //Make a triangle of the first ear
                var earVertex = earVertices[0];

                var earVertexPrev = earVertex.PreviousVertex;
                var earVertexNext = earVertex.NextVertex;

                var newTriangle = new Triangle(earVertex, earVertexPrev, earVertexNext);

                triangles.Add(newTriangle);

                //Remove the vertex from the lists
                earVertices.Remove(earVertex);

                vertices.Remove(earVertex);

                //Update the previous vertex and next vertex so that they are now directly linked together (take current ear vertex out of doubly linked list)
                earVertexPrev.NextVertex     = earVertexNext;
                earVertexNext.PreviousVertex = earVertexPrev;

                //...see if we have found a new ear by investigating the two vertices that was part of the ear and add them to the list of ears, if that's the case.
                SetIfVertexIsConcaveOrConvex(earVertexPrev);
                SetIfVertexIsConcaveOrConvex(earVertexNext);

                earVertices.Remove(earVertexPrev);
                earVertices.Remove(earVertexNext);

                SetIfVertexIsEar(earVertexPrev, vertices);
                if (earVertexPrev.IsEar)
                {
                    earVertices.Add(earVertexPrev);
                }
                SetIfVertexIsEar(earVertexNext, vertices);
                if (earVertexNext.IsEar)
                {
                    earVertices.Add(earVertexNext);
                }
            }

            return(triangles);
        }