Ejemplo n.º 1
0
        //The original algorithm calculates the intersection between two polygons, this will instead get the outside
        //Assumes the polygons are oriented counter clockwise
        //poly is the polygon we want to cut
        //Assumes the polygon we want to remove from the other polygon is convex, so clipPolygon has to be convex
        //We will end up with the !intersection of the polygons
        public static List <List <MyVector2> > ClipPolygonInverted(List <MyVector2> poly, List <Plane2> clippingPlanes)
        {
            //The result may be more than one polygons
            List <List <MyVector2> > finalPolygons = new List <List <MyVector2> >();

            List <MyVector2> vertices = new List <MyVector2>(poly);

            //The remaining polygon after each cut
            List <MyVector2> vertices_tmp = new List <MyVector2>();

            //Clip the polygon
            for (int i = 0; i < clippingPlanes.Count; i++)
            {
                Plane2 plane = clippingPlanes[i];

                //A new polygon which is the part of the polygon which is outside of this plane
                List <MyVector2> outsidePolygon = new List <MyVector2>();

                for (int j = 0; j < vertices.Count; j++)
                {
                    int jPlusOne = MathUtility.ClampListIndex(j + 1, vertices.Count);

                    MyVector2 v1 = vertices[j];
                    MyVector2 v2 = vertices[jPlusOne];

                    //Calculate the distance to the plane from each vertex
                    //This is how we will know if they are inside or outside
                    //If they are inside, the distance is positive, which is why the planes normals have to be oriented to the inside
                    float dist_to_v1 = Geometry.DistanceFromPointToPlane(plane.normal, plane.pos, v1);
                    float dist_to_v2 = Geometry.DistanceFromPointToPlane(plane.normal, plane.pos, v2);

                    //TODO: What will happen if they are exactly 0?

                    //Case 1. Both are inside (= to the left), save v2 to the other polygon
                    if (dist_to_v1 >= 0f && dist_to_v2 >= 0f)
                    {
                        vertices_tmp.Add(v2);
                    }
                    //Case 2. Both are outside (= to the right), save v1
                    else if (dist_to_v1 < 0f && dist_to_v2 < 0f)
                    {
                        outsidePolygon.Add(v2);
                    }
                    //Case 3. Outside -> Inside, save intersection point
                    else if (dist_to_v1 < 0f && dist_to_v2 >= 0f)
                    {
                        MyVector2 rayDir = MyVector2.Normalize(v2 - v1);

                        MyVector2 intersectionPoint = Intersections.GetRayPlaneIntersectionPoint(plane.pos, plane.normal, v1, rayDir);

                        outsidePolygon.Add(intersectionPoint);

                        vertices_tmp.Add(intersectionPoint);

                        vertices_tmp.Add(v2);
                    }
                    //Case 4. Inside -> Outside, save intersection point and v2
                    else if (dist_to_v1 >= 0f && dist_to_v2 < 0f)
                    {
                        MyVector2 rayDir = MyVector2.Normalize(v2 - v1);

                        MyVector2 intersectionPoint = Intersections.GetRayPlaneIntersectionPoint(plane.pos, plane.normal, v1, rayDir);

                        outsidePolygon.Add(intersectionPoint);

                        outsidePolygon.Add(v2);

                        vertices_tmp.Add(intersectionPoint);
                    }
                }

                //Add the polygon outside of this plane to the list of all polygons that are outside of all planes
                if (outsidePolygon.Count > 0)
                {
                    finalPolygons.Add(outsidePolygon);
                }

                //Add the polygon which was inside of this and previous planes to the polygon we want to test
                vertices.Clear();

                vertices.AddRange(vertices_tmp);

                vertices_tmp.Clear();
            }

            return(finalPolygons);
        }
Ejemplo n.º 2
0
        //
        // Add the constraints to the delaunay triangulation
        //

        //timer is for debugging
        private static HalfEdgeData2 AddConstraints(HalfEdgeData2 triangleData, List <MyVector2> constraints, bool shouldRemoveTriangles, System.Diagnostics.Stopwatch timer = null)
        {
            //Validate the data
            if (constraints == null)
            {
                return(triangleData);
            }


            //Get a list with all edges
            //This is faster than first searching for unique edges
            //The report suggest we should do a triangle walk, but it will not work if the mesh has holes
            //The mesh has holes because we remove triangles while adding constraints one-by-one
            //so maybe better to remove triangles after we added all constraints...
            HashSet <HalfEdge2> edges = triangleData.edges;


            //The steps numbering is from the report
            //Step 1. Loop over each constrained edge. For each of these edges, do steps 2-4
            for (int i = 0; i < constraints.Count; i++)
            {
                //Let each constrained edge be defined by the vertices:
                MyVector2 c_p1 = constraints[i];
                MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)];

                //Check if this constraint already exists in the triangulation,
                //if so we are happy and dont need to worry about this edge
                //timer.Start();
                if (IsEdgeInListOfEdges(edges, c_p1, c_p2))
                {
                    continue;
                }
                //timer.Stop();

                //Step 2. Find all edges in the current triangulation that intersects with this constraint
                //Is returning unique edges only, so not one edge going in the opposite direction
                //timer.Start();
                Queue <HalfEdge2> intersectingEdges = FindIntersectingEdges_BruteForce(edges, c_p1, c_p2);
                //timer.Stop();

                //Debug.Log("Intersecting edges: " + intersectingEdges.Count);

                //Step 3. Remove intersecting edges by flipping triangles
                //This takes 0 seconds so is not bottleneck
                //timer.Start();
                List <HalfEdge2> newEdges = RemoveIntersectingEdges(c_p1, c_p2, intersectingEdges);
                //timer.Stop();

                //Step 4. Try to restore delaunay triangulation
                //Because we have constraints we will never get a delaunay triangulation
                //This takes 0 seconds so is not bottleneck
                //timer.Start();
                RestoreDelaunayTriangulation(c_p1, c_p2, newEdges);
                //timer.Stop();
            }

            //Step 5. Remove superfluous triangles, such as the triangles "inside" the constraints
            if (shouldRemoveTriangles)
            {
                //timer.Start();
                RemoveSuperfluousTriangles(triangleData, constraints);
                //timer.Stop();
            }

            return(triangleData);
        }
        public static List <List <MyVector2> > ClipPolygons(List <MyVector2> polyVector2, List <MyVector2> clipPolyVector2, BooleanOperation booleanOperation)
        {
            List <List <MyVector2> > finalPoly = new List <List <MyVector2> >();



            //Step 0. Create the data structure needed
            List <ClipVertex> poly = InitDataStructure(polyVector2);

            List <ClipVertex> clipPoly = InitDataStructure(clipPolyVector2);



            //Step 1. Find intersection points
            //Need to test if we have found an intersection point,
            //if none is found, the polygons dont intersect, or one polygon is inside the other
            bool hasFoundIntersection = false;

            for (int i = 0; i < poly.Count; i++)
            {
                ClipVertex currentVertex = poly[i];

                //Important to use iPlusOne because poly.next may change
                int iPlusOne = MathUtility.ClampListIndex(i + 1, poly.Count);

                MyVector2 a = poly[i].coordinate;

                MyVector2 b = poly[iPlusOne].coordinate;

                //Gizmos.DrawWireSphere(poly[i].coordinate, 0.02f);
                //Gizmos.DrawWireSphere(poly[i].next.coordinate, 0.02f);

                for (int j = 0; j < clipPoly.Count; j++)
                {
                    int jPlusOne = MathUtility.ClampListIndex(j + 1, clipPoly.Count);

                    MyVector2 c = clipPoly[j].coordinate;

                    MyVector2 d = clipPoly[jPlusOne].coordinate;

                    //Are these lines intersecting?
                    if (Intersections.LineLine(a, b, c, d, true))
                    {
                        hasFoundIntersection = true;

                        MyVector2 intersectionPoint2D = Intersections.GetLineLineIntersectionPoint(a, b, c, d);

                        //Vector3 intersectionPoint = new Vector3(intersectionPoint2D.x, 0f, intersectionPoint2D.y);

                        //Gizmos.color = Color.red;

                        //Gizmos.DrawWireSphere(intersectionPoint, 0.04f);

                        //We need to insert this intersection vertex into both polygons
                        //Insert into the polygon
                        ClipVertex vertexOnPolygon = InsertIntersectionVertex(a, b, intersectionPoint2D, currentVertex);

                        //Insert into the clip polygon
                        ClipVertex vertexOnClipPolygon = InsertIntersectionVertex(c, d, intersectionPoint2D, clipPoly[j]);

                        //Also connect the intersection vertices with each other
                        vertexOnPolygon.neighbor = vertexOnClipPolygon;

                        vertexOnClipPolygon.neighbor = vertexOnPolygon;
                    }
                }
            }


            //Debug in which order the vertices are in the linked list
            //InWhichOrderAreVerticesAdded(poly);

            //InWhichOrderAreVerticesAdded(clipPoly);



            //If the polygons are intersecting
            if (hasFoundIntersection)
            {
                //Step 2. Trace each polygon and mark entry and exit points to the other polygon's interior
                MarkEntryExit(poly, clipPolyVector2);

                MarkEntryExit(clipPoly, polyVector2);

                //Debug entry exit points
                DebugEntryExit(poly);
                //DebugEntryExit(clipPoly);



                //Step 3. Create the desired clipped polygon
                if (booleanOperation == BooleanOperation.Intersection)
                {
                    //Where the two polygons intersect
                    List <ClipVertex> intersectionVertices = GetClippedPolygon(poly, true);

                    //Debug.Log(intersectionVertices.Count);

                    AddPolygonToList(intersectionVertices, finalPoly, false);

                    //Debug.Log();
                }
                else if (booleanOperation == BooleanOperation.Difference)
                {
                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false);

                    AddPolygonToList(outsidePolyVertices, finalPoly, true);
                }
                else if (booleanOperation == BooleanOperation.ExclusiveOr)
                {
                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false);

                    AddPolygonToList(outsidePolyVertices, finalPoly, true);

                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false);

                    AddPolygonToList(outsideClipPolyVertices, finalPoly, true);
                }
                else if (booleanOperation == BooleanOperation.Union)
                {
                    //Where the two polygons intersect
                    List <ClipVertex> intersectionVertices = GetClippedPolygon(poly, true);

                    AddPolygonToList(intersectionVertices, finalPoly, false);

                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false);

                    AddPolygonToList(outsidePolyVertices, finalPoly, true);

                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false);

                    AddPolygonToList(outsideClipPolyVertices, finalPoly, true);
                }

                //Where the two polygons intersect
                //List<ClipVertex> intersectionVertices = GetClippedPolygon(poly, true);
                //Whats outside of the polygon that doesnt intersect
                //These will be in clockwise order so remember to change to counter clockwise
                //List<ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false);
                //List<ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false);
            }
            //Check if one polygon is inside the other
            else
            {
                //Is the polygon inside the clip polygon?
                //Depending on the type of boolean operation, we might get a hole
                if (IsPolygonInsidePolygon(polyVector2, clipPolyVector2))
                {
                    Debug.Log("Poly is inside clip poly");
                }
                else if (IsPolygonInsidePolygon(clipPolyVector2, polyVector2))
                {
                    Debug.Log("Clip poly is inside poly");
                }
                else
                {
                    Debug.Log("Polygons are not intersecting");
                }
            }

            return(finalPoly);
        }
        //Assumes the polygons are oriented counter clockwise
        //poly is the polygon we want to cut
        //Assumes the polygon we want to remove from the other polygon is convex, so clipPolygon has to be convex
        //We will end up with the intersection of the polygons
        public static List <MyVector2> ClipPolygon(List <MyVector2> poly, List <Plane2> clippingPlanes)
        {
            //Clone the vertices because we will remove vertices from this list
            List <MyVector2> vertices = new List <MyVector2>(poly);

            //Save the new vertices temporarily in this list before transfering them to vertices
            List <MyVector2> vertices_tmp = new List <MyVector2>();

            //Clip the polygon
            for (int i = 0; i < clippingPlanes.Count; i++)
            {
                Plane2 plane = clippingPlanes[i];

                for (int j = 0; j < vertices.Count; j++)
                {
                    int jPlusOne = MathUtility.ClampListIndex(j + 1, vertices.Count);

                    MyVector2 v1 = vertices[j];
                    MyVector2 v2 = vertices[jPlusOne];

                    //Calculate the distance to the plane from each vertex
                    //This is how we will know if they are inside or outside
                    //If they are inside, the distance is positive, which is why the planes normals have to be oriented to the inside
                    float dist_to_v1 = _Geometry.GetSignedDistanceFromPointToPlane(plane, v1);
                    float dist_to_v2 = _Geometry.GetSignedDistanceFromPointToPlane(plane, v2);

                    //TODO: What will happen if they are exactly 0? Should maybe use a tolerance of 0.001

                    //Case 1. Both are outside (= to the right), do nothing

                    //Case 2. Both are inside (= to the left), save v2
                    if (dist_to_v1 >= 0f && dist_to_v2 >= 0f)
                    {
                        vertices_tmp.Add(v2);
                    }
                    //Case 3. Outside -> Inside, save intersection point and v2
                    else if (dist_to_v1 < 0f && dist_to_v2 >= 0f)
                    {
                        MyVector2 rayDir = MyVector2.Normalize(v2 - v1);

                        Ray2 ray = new Ray2(v1, rayDir);

                        MyVector2 intersectionPoint = _Intersections.GetRayPlaneIntersectionPoint(plane, ray);

                        vertices_tmp.Add(intersectionPoint);

                        vertices_tmp.Add(v2);
                    }
                    //Case 4. Inside -> Outside, save intersection point
                    else if (dist_to_v1 >= 0f && dist_to_v2 < 0f)
                    {
                        MyVector2 rayDir = MyVector2.Normalize(v2 - v1);

                        Ray2 ray = new Ray2(v1, rayDir);

                        MyVector2 intersectionPoint = _Intersections.GetRayPlaneIntersectionPoint(plane, ray);

                        vertices_tmp.Add(intersectionPoint);
                    }
                }

                //Add the new vertices to the list of vertices
                vertices.Clear();

                vertices.AddRange(vertices_tmp);

                vertices_tmp.Clear();
            }

            return(vertices);
        }
Ejemplo n.º 5
0
        //The points on the hull (vertices) should be ordered counter-clockwise (and no doubles)
        //The holes should be ordered clockwise (and no doubles)
        //Optimize triangles means that we will get a better-looking triangulation, which resembles a constrained Delaunay triangulation
        public static HashSet <Triangle2> Triangulate(List <MyVector2> vertices, List <List <MyVector2> > allHoleVertices = null, bool optimizeTriangles = true)
        {
            //Validate the data
            if (vertices == null || vertices.Count <= 2)
            {
                Debug.LogWarning("Can't triangulate with Ear Clipping because too few vertices on the hull");

                return(null);
            }



            //Step -1. Merge the holes with the points on the hull into one big polygon with invisible edges between the holes and the hull
            if (allHoleVertices != null && allHoleVertices.Count > 0)
            {
                vertices = EarClippingHoleMethods.MergeHolesWithHull(vertices, allHoleVertices);
            }


            //TestAlgorithmsHelpMethods.DebugDrawCircle(vertices[29].ToVector3(1f), 0.3f, Color.red);


            //Step 0. Create a linked list connecting all vertices with each other which will make the calculations easier and faster
            List <LinkedVertex> verticesLinked = new List <LinkedVertex>();

            for (int i = 0; i < vertices.Count; i++)
            {
                LinkedVertex v = new LinkedVertex(vertices[i]);

                verticesLinked.Add(v);
            }

            //Link them to each other
            for (int i = 0; i < verticesLinked.Count; i++)
            {
                LinkedVertex v = verticesLinked[i];

                v.prevLinkedVertex = verticesLinked[MathUtility.ClampListIndex(i - 1, verticesLinked.Count)];
                v.nextLinkedVertex = verticesLinked[MathUtility.ClampListIndex(i + 1, verticesLinked.Count)];
            }

            //Debug.Log("Number of vertices: " + CountLinkedVertices(verticesLinked[0]));



            //Step 1. Find:
            //- Convex vertices (interior angle smaller than 180 degrees)
            //- Reflect vertices (interior angle greater than 180 degrees) so should maybe be called concave vertices?
            //Interior angle is the angle between two vectors inside the polygon if we move around the polygon counter-clockwise
            //If they are neither we assume they are reflect (or we will end up with odd triangulations)
            HashSet <LinkedVertex> convexVerts  = new HashSet <LinkedVertex>();
            HashSet <LinkedVertex> reflectVerts = new HashSet <LinkedVertex>();

            foreach (LinkedVertex v in verticesLinked)
            {
                bool isConvex = IsVertexConvex(v);

                if (isConvex)
                {
                    convexVerts.Add(v);
                }
                else
                {
                    reflectVerts.Add(v);
                }
            }



            //Step 2. Find the initial ears
            HashSet <LinkedVertex> earVerts = new HashSet <LinkedVertex>();

            //An ear is always a convex vertex
            foreach (LinkedVertex v in convexVerts)
            {
                //And we only need to test the reflect vertices
                if (IsVertexEar(v, reflectVerts))
                {
                    earVerts.Add(v);
                }
            }


            //Debug
            //DisplayVertices(earVertices);



            //Step 3. Build the triangles
            HashSet <Triangle2> triangulation = new HashSet <Triangle2>();

            //We know how many triangles we will get (number of vertices - 2) which is true for all simple polygons
            //This can be used to stop the algorithm
            int maxTriangles = verticesLinked.Count - 2;

            //Because we use a while loop, having an extra safety is always good so we dont get stuck in infinite loop
            int safety = 0;

            while (true)
            {
                //Pick an ear vertex and form a triangle
                LinkedVertex ear = GetEarVertex(earVerts, optimizeTriangles);

                if (ear == null)
                {
                    Debug.Log("Cant find ear");

                    break;
                }

                LinkedVertex v_prev = ear.prevLinkedVertex;
                LinkedVertex v_next = ear.nextLinkedVertex;

                Triangle2 t = new Triangle2(ear.pos, v_prev.pos, v_next.pos);

                //Try to flip this triangle according to Delaunay triangulation
                if (optimizeTriangles)
                {
                    OptimizeTriangle(t, triangulation);
                }
                else
                {
                    triangulation.Add(t);
                }



                //Check if we have found all triangles
                //This should also prevent us from getting stuck in an infinite loop
                if (triangulation.Count >= maxTriangles)
                {
                    break;
                }


                //If we havent found all triangles we have to reconfigure the data structure

                //Remove the ear we used to build a triangle
                convexVerts.Remove(ear);
                earVerts.Remove(ear);

                //Reconnect the vertices because one vertex has now been removed
                v_prev.nextLinkedVertex = v_next;
                v_next.prevLinkedVertex = v_prev;

                //Reconfigure the adjacent vertices
                ReconfigureAdjacentVertex(v_prev, convexVerts, reflectVerts, earVerts);
                ReconfigureAdjacentVertex(v_next, convexVerts, reflectVerts, earVerts);


                //if (safety > 4)
                //{
                //    Debug.Log(earVerts.Count);

                //    Debug.DrawLine(v_next.pos.ToVector3(), Vector3.zero, Color.blue, 3f);

                //    //Debug.Log(IsVertexEar(v_next, reflectVerts));

                //    Debug.Log(earVerts.Contains(v_next));

                //    break;
                //}



                safety += 1;

                if (safety > 50000)
                {
                    Debug.Log("Ear Clipping is stuck in an infinite loop!");

                    break;
                }
            }



            //Step 4. Improve triangulation
            //Some triangles may be too sharp, and if you want a nice looking triangle, you should try to swap edges
            //according to Delaunay triangulation
            //A report suggests that should be done while createing the triangulation
            //But maybe it's easier to do it afterwards with some standardized constrained Delaunay triangulation?
            //But that would also be stupid because then we could have used the constrained Delaunay from the beginning!


            return(triangulation);
        }
        //We assume an edge is visible from a point if the triangle (formed by travering edges in the convex hull
        //of the existing triangulation) form a clockwise triangle with the point
        //https://stackoverflow.com/questions/8898255/check-whether-a-point-is-visible-from-a-face-of-a-2d-convex-hull
        private static HashSet <Triangle2> TriangulatePointsConvexHull(HashSet <MyVector2> points)
        {
            if (points.Count < 3)
            {
                Debug.Log("You need at least 3 points to form a triangle!");

                return(null);
            }


            //Step 0. Init the triangles we will return
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();



            //Step 1. Sort the points
            List <MyVector2> sortedPoints = new List <MyVector2>(points);

            //OrderBy is always soring in ascending order - use OrderByDescending to get in the other order
            //sortedPoints = sortedPoints.OrderBy(n => n.x).ToList();

            //If we have colinear points we have to sort in both x and y
            sortedPoints = sortedPoints.OrderBy(n => n.x).ThenBy(n => n.y).ToList();



            //Step 2. Create the first triangle so we can start the algorithm because we need edges to look at
            //and see if they are visible

            //Pick the first two points in the sorted list - These are always a part of the first triangle
            MyVector2 p1 = sortedPoints[0];
            MyVector2 p2 = sortedPoints[1];

            //Remove them
            sortedPoints.RemoveAt(0);
            sortedPoints.RemoveAt(0);

            //The problem is the third point
            //If we have colinear points, then the third point in the sorted list is not always a valid point
            //to form a triangle because then it will be flat
            //So we have to look for a better point
            for (int i = 0; i < sortedPoints.Count; i++)
            {
                //We have found a non-colinear point
                LeftOnRight pointRelation = Geometry.IsPoint_Left_On_Right_OfVector(p1, p2, sortedPoints[i]);

                if (pointRelation == LeftOnRight.Left || pointRelation == LeftOnRight.Right)
                {
                    MyVector2 p3 = sortedPoints[i];

                    //Remove this point
                    sortedPoints.RemoveAt(i);

                    //Build the first triangle
                    Triangle2 newTriangle = new Triangle2(p1, p2, p3);

                    triangles.Add(newTriangle);

                    break;
                }
            }

            //If we have finished search and not found a triangle, that means that all points
            //are colinear and we cant form any triangles
            if (triangles.Count == 0)
            {
                Debug.Log("All points you want to triangulate a co-linear");

                return(null);
            }



            //Step 3. Add the other points one-by-one

            //For each point we add we have to calculate a convex hull of the previous points
            //An optimization is to not use all points in the triangulation
            //to calculate the hull because many of them might be inside of the hull
            //So we will use the previous points on the hull and add the point we added last iteration
            //to generate the new convex hull

            //First we need to init the convex hull
            HashSet <MyVector2> triangulatePoints = new HashSet <MyVector2>();

            foreach (Triangle2 t in triangles)
            {
                triangulatePoints.Add(t.p1);
                triangulatePoints.Add(t.p2);
                triangulatePoints.Add(t.p3);
            }

            //Calculate the first convex hull
            List <MyVector2> pointsOnHull = _ConvexHull.JarvisMarch(triangulatePoints);

            //Add the other points one-by-one
            foreach (MyVector2 pointToAdd in sortedPoints)
            {
                bool couldFormTriangle = false;

                //Loop through all edges in the convex hull
                for (int j = 0; j < pointsOnHull.Count; j++)
                {
                    MyVector2 hull_p1 = pointsOnHull[j];
                    MyVector2 hull_p2 = pointsOnHull[MathUtility.ClampListIndex(j + 1, pointsOnHull.Count)];

                    //First we have to check if the points are colinear, then we cant form a triangle
                    LeftOnRight pointRelation = Geometry.IsPoint_Left_On_Right_OfVector(hull_p1, hull_p2, pointToAdd);

                    if (pointRelation == LeftOnRight.On)
                    {
                        continue;
                    }

                    //If this triangle is clockwise, then we can see the edge
                    //so we should create a new triangle with this edge and the point
                    if (Geometry.IsTriangleOrientedClockwise(hull_p1, hull_p2, pointToAdd))
                    {
                        triangles.Add(new Triangle2(hull_p1, hull_p2, pointToAdd));

                        couldFormTriangle = true;
                    }
                }

                //Add the point to the list of points on the hull
                //Will re-generate the hull by using these points so dont worry that the
                //list is not valid anymore
                if (couldFormTriangle)
                {
                    pointsOnHull.Add(pointToAdd);

                    //Find the convex hull of the current triangulation
                    //It generates a counter-clockwise convex hull
                    pointsOnHull = _ConvexHull.JarvisMarch(new HashSet <MyVector2>(pointsOnHull));
                }
                else
                {
                    Debug.Log("This point could not form any triangles " + pointToAdd.x + " " + pointToAdd.y);
                }
            }



            return(triangles);
        }
Ejemplo n.º 7
0
        //
        // Connected line segments
        //
        //isConnected means if the end points are connected to form a loop
        public static HashSet <Triangle2> ConnectedLineSegments(List <MyVector2> points, float width, bool isConnected)
        {
            if (points != null && points.Count < 2)
            {
                Debug.Log("Cant form a line with fewer than two points");

                return(null);
            }



            //Generate the triangles
            HashSet <Triangle2> lineTriangles = new HashSet <Triangle2>();

            //If the lines are connected we need to do plane-plane intersection to find the
            //coordinate where the lines meet at each point, or the line segments will
            //not get the same size
            //(There might be a better way to do it than with plane-plane intersection)
            List <MyVector2> topCoordinate    = new List <MyVector2>();
            List <MyVector2> bottomCoordinate = new List <MyVector2>();

            float halfWidth = width * 0.5f;

            for (int i = 0; i < points.Count; i++)
            {
                MyVector2 p = points[i];

                //First point = special case if the lines are not connected
                if (i == 0 && !isConnected)
                {
                    MyVector2 lineDir = points[1] - points[0];

                    MyVector2 lineNormal = MyVector2.Normalize(new MyVector2(lineDir.y, -lineDir.x));

                    topCoordinate.Add(p + lineNormal * halfWidth);

                    bottomCoordinate.Add(p - lineNormal * halfWidth);
                }
                //Last point = special case if the lines are not connected
                else if (i == points.Count - 1 && !isConnected)
                {
                    MyVector2 lineDir = p - points[points.Count - 2];

                    MyVector2 lineNormal = MyVector2.Normalize(new MyVector2(lineDir.y, -lineDir.x));

                    topCoordinate.Add(p + lineNormal * halfWidth);

                    bottomCoordinate.Add(p - lineNormal * halfWidth);
                }
                else
                {
                    //Now we need to find the intersection points between the top line and the bottom line
                    MyVector2 p_before = points[MathUtility.ClampListIndex(i - 1, points.Count)];

                    MyVector2 p_after = points[MathUtility.ClampListIndex(i + 1, points.Count)];

                    MyVector2 pTop = GetIntersectionPoint(p_before, p, p_after, halfWidth, isTopPoint: true);

                    MyVector2 pBottom = GetIntersectionPoint(p_before, p, p_after, halfWidth, isTopPoint: false);



                    topCoordinate.Add(pTop);

                    bottomCoordinate.Add(pBottom);
                }
            }

            //Debug.Log();

            for (int i = 0; i < points.Count; i++)
            {
                //Skip the first point if it is not connected to the last point
                if (i == 0 && !isConnected)
                {
                    continue;
                }

                int i_minus_one = MathUtility.ClampListIndex(i - 1, points.Count);

                MyVector2 p1_T = topCoordinate[i_minus_one];
                MyVector2 p1_B = bottomCoordinate[i_minus_one];

                MyVector2 p2_T = topCoordinate[i];
                MyVector2 p2_B = bottomCoordinate[i];

                HashSet <Triangle2> triangles = LineSegment(p1_T, p1_B, p2_T, p2_B);

                foreach (Triangle2 t in triangles)
                {
                    lineTriangles.Add(t);
                }
            }

            //Debug.Log(lineTriangles.Count);

            return(lineTriangles);
        }
Ejemplo n.º 8
0
        //
        // Algorithm 1
        //

        //If you have points on a convex hull, sorted one after each other
        //If you have colinear points, it will ignore some of them but still triangulate the entire area
        //Colinear points are not changing the shape
        public static HashSet <Triangle2> GetTriangles(List <MyVector2> pointsOnHull, bool addColinearPoints)
        {
            //If we hadnt have to deal with colinear points, this algorithm would be really simple:
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            //This vertex will be a vertex in all triangles
            MyVector2 a = pointsOnHull[0];

            //And then we just loop through the other edges to make all triangles
            for (int i = 1; i < pointsOnHull.Count; i++)
            {
                MyVector2 b = pointsOnHull[i];
                MyVector2 c = pointsOnHull[MathUtility.ClampListIndex(i + 1, pointsOnHull.Count)];

                //Is this a valid triangle?
                //If a, b, c are on the same line, the triangle has no area and we can't add it
                LeftOnRight pointRelation = _Geometry.IsPoint_Left_On_Right_OfVector(a, b, c);

                if (pointRelation == LeftOnRight.On)
                {
                    continue;
                }

                triangles.Add(new Triangle2(a, b, c));
            }


            //Add the missing colinear points by splitting triangles
            //Step 2.1. We have now triangulated the entire convex polygon, but if we have colinear points
            //some of those points were not added
            //We can add them by splitting triangles

            //Find colinear points
            if (addColinearPoints)
            {
                HashSet <MyVector2> colinearPoints = new HashSet <MyVector2>(pointsOnHull);

                //Remove points that are in the triangulation from the points on the convex hull
                //and we can see which where not added = the colinear points
                foreach (Triangle2 t in triangles)
                {
                    colinearPoints.Remove(t.p1);
                    colinearPoints.Remove(t.p2);
                    colinearPoints.Remove(t.p3);
                }

                //Debug.Log("Colinear points: " + colinearPoints.Count);

                //Go through all colinear points and find which edge they should split
                //On the border we only need to split one edge because this edge has no neighbors
                foreach (MyVector2 p in colinearPoints)
                {
                    foreach (Triangle2 t in triangles)
                    {
                        //Is this point in the triangle
                        if (_Intersections.PointTriangle(t, p, includeBorder: true))
                        {
                            SplitTriangleEdge(t, p, triangles);

                            break;
                        }
                    }
                }
            }


            return(triangles);
        }
Ejemplo n.º 9
0
        //We assume an edge is visible from a point if the triangle (formed by travering edges in the convex hull
        //of the existing triangulation) form a clockwise triangle with the point
        //https://stackoverflow.com/questions/8898255/check-whether-a-point-is-visible-from-a-face-of-a-2d-convex-hull
        private static HashSet <Triangle2> TriangulatePointsConvexHull(HashSet <MyVector2> pointsHashset)
        {
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            //The points we will add
            List <MyVector2> originalPoints = new List <MyVector2>(pointsHashset);


            //Step 1. Sort the points along x-axis
            //OrderBy is always soring in ascending order - use OrderByDescending to get in the other order
            //originalPoints = originalPoints.OrderBy(n => n.x).ThenBy(n => n.y).ToList();
            originalPoints = originalPoints.OrderBy(n => n.x).ToList();


            //Step 2. Create the first triangle so we can start the algorithm

            //Assumes the convex hull algorithm sorts in the same way in x and y directions as we do above
            MyVector2 p1Start = originalPoints[0];
            MyVector2 p2Start = originalPoints[1];
            MyVector2 p3Start = originalPoints[2];

            //Theese points for the first triangle
            Triangle2 newTriangle = new Triangle2(p1Start, p2Start, p3Start);

            triangles.Add(newTriangle);


            //All points that form the triangles
            HashSet <MyVector2> triangulatedPoints = new HashSet <MyVector2>();

            triangulatedPoints.Add(p1Start);
            triangulatedPoints.Add(p2Start);
            triangulatedPoints.Add(p3Start);


            //Step 3. Add the other points one-by-one
            //Add the other points one by one
            //Starts at 3 because we have already added 0,1,2
            for (int i = 3; i < originalPoints.Count; i++)
            {
                MyVector2 pointToAdd = originalPoints[i];

                //Find the convex hull of the current triangulation
                //It generates a counter-clockwise convex hull
                List <MyVector2> pointsOnHull = _ConvexHull.JarvisMarch(triangulatedPoints);

                if (pointsOnHull == null)
                {
                    Debug.Log("No points on hull when triangulating");

                    continue;
                }

                bool couldFormTriangle = false;

                //Loop through all edges in the convex hull
                for (int j = 0; j < pointsOnHull.Count; j++)
                {
                    MyVector2 p1 = pointsOnHull[j];
                    MyVector2 p2 = pointsOnHull[MathUtility.ClampListIndex(j + 1, pointsOnHull.Count)];

                    //If this triangle is clockwise, then we can see the edge
                    //so we should create a new triangle with this edge and the point
                    if (Geometry.IsTriangleOrientedClockwise(p1, p2, pointToAdd))
                    {
                        triangles.Add(new Triangle2(p1, p2, pointToAdd));

                        couldFormTriangle = true;
                    }
                }

                //Add the point to the list of all points in the current triangulation
                //if the point could form a triangle
                if (couldFormTriangle)
                {
                    triangulatedPoints.Add(pointToAdd);
                }
            }



            return(triangles);
        }