Ejemplo n.º 1
0
        //Flip edges until we get a delaunay triangulation
        private static void FlipEdges(HalfEdgeData2 triangleData)
        {
            //The edges we want to flip
            HashSet <HalfEdge2> edges = triangleData.edges;

            //To avoid getting stuck in infinite loop
            int safety = 0;

            //Count how many edges we have flipped, which may be interesting to display
            int flippedEdges = 0;

            while (true)
            {
                safety += 1;

                if (safety > 100000)
                {
                    Debug.Log("Stuck in endless loop when flipping edges to get a Delaunay triangulation");

                    break;
                }

                bool hasFlippedEdge = false;

                //Search through all edges to see if we can flip an edge
                foreach (HalfEdge2 thisEdge in edges)
                {
                    //Is this edge sharing an edge with another triangle, otherwise its a border, and then we cant flip the edge
                    if (thisEdge.oppositeEdge == null)
                    {
                        continue;
                    }

                    //The positions of the vertices belonging to the two triangles that we might flip
                    //a-c should be the edge that we might flip
                    MyVector2 a = thisEdge.v.position;
                    MyVector2 b = thisEdge.nextEdge.v.position;
                    MyVector2 c = thisEdge.nextEdge.nextEdge.v.position;
                    MyVector2 d = thisEdge.oppositeEdge.nextEdge.v.position;

                    //Test if we should flip this edge
                    if (DelaunayMethods.ShouldFlipEdge(a, b, c, d))
                    {
                        flippedEdges += 1;

                        hasFlippedEdge = true;

                        HalfEdgeHelpMethods.FlipTriangleEdge(thisEdge);
                    }
                }

                //We have searched through all edges and havent found an edge to flip, so we have a Delaunay triangulation!
                if (!hasFlippedEdge)
                {
                    Debug.Log("Found a delaunay triangulation in " + flippedEdges + " flips");

                    break;
                }
            }
        }
        //Remove the supertriangle
        private static void RemoveSuperTriangle(Triangle2 superTriangle, HalfEdgeData2 triangulationData)
        {
            //The super triangle doesnt exists anymore because we have split it into many new triangles
            //But we can use its vertices to figure out which new triangles (or faces belonging to the triangle)
            //we should delete

            HashSet <HalfEdgeFace2> triangleFacesToDelete = new HashSet <HalfEdgeFace2>();

            //Loop through all vertices belongin to the triangulation
            foreach (HalfEdgeVertex2 v in triangulationData.vertices)
            {
                //If the face attached to this vertex already exists in the list of faces we want to delete
                //Then dont add it again
                if (triangleFacesToDelete.Contains(v.edge.face))
                {
                    continue;
                }

                MyVector2 v1 = v.position;

                //Is this vertex in the triangulation a vertex in the super triangle?
                if (v1.Equals(superTriangle.p1) || v1.Equals(superTriangle.p2) || v1.Equals(superTriangle.p3))
                {
                    triangleFacesToDelete.Add(v.edge.face);
                }
            }

            //Debug.Log("Triangles to delete: " + trianglesToDelete.Count);

            //Delete the new triangles with vertices attached to the super triangle
            foreach (HalfEdgeFace2 f in triangleFacesToDelete)
            {
                HalfEdgeHelpMethods.DeleteTriangleFace(f, triangulationData, shouldSetOppositeToNull: true);
            }
        }
Ejemplo n.º 3
0
        //Method 2. Triangulation walk
        //This assumes there are no holes in the mesh
        //And that we have a super-triangle around the triangulation
        private static void FindIntersectingEdges_TriangleWalk(HalfEdgeData2 triangleData, MyVector2 c_p1, MyVector2 c_p2, List <HalfEdge2> intersectingEdges)
        {
            //Step 1. Begin at a triangle connected to the constraint edges's vertex c_p1
            HalfEdgeFace2 f = null;

            foreach (HalfEdgeFace2 testFace in triangleData.faces)
            {
                //The edges the triangle consists of
                HalfEdge2 e1 = testFace.edge;
                HalfEdge2 e2 = e1.nextEdge;
                HalfEdge2 e3 = e2.nextEdge;

                //Does one of these edges include the first vertex in the constraint edge
                if (e1.v.position.Equals(c_p1) || e2.v.position.Equals(c_p1) || e3.v.position.Equals(c_p1))
                {
                    f = testFace;

                    break;
                }
            }



            //Step2. Walk around p1 until we find a triangle with an edge that intersects with the edge p1-p2


            //Step3. March from one triangle to the next in the general direction of p2
        }
        //Insert a new point in the triangulation we already have, so we need at least one triangle
        public static void InsertNewPointInTriangulation(MyVector2 p, HalfEdgeData2 triangulationData, ref int missedPoints, ref int flippedEdges)
        {
            //Step 5. Insert the new point in the triangulation
            //Find the existing triangle the point is in
            HalfEdgeFace2 f = PointTriangulationIntersection.TriangulationWalk(p, null, triangulationData);

            //We couldnt find a triangle maybe because the point is not in the triangulation?
            if (f == null)
            {
                missedPoints += 1;
            }

            //Delete this triangle and form 3 new triangles by connecting p to each of the vertices in the old triangle
            HalfEdgeHelpMethods.SplitTriangleFaceAtPoint(f, p, triangulationData);


            //Step 6. Initialize stack. Place all triangles which are adjacent to the edges opposite p on a LIFO stack
            //The report says we should place triangles, but it's easier to place edges with our data structure
            Stack <HalfEdge2> trianglesToInvestigate = new Stack <HalfEdge2>();

            AddTrianglesOppositePToStack(p, trianglesToInvestigate, triangulationData);


            //Step 7. Restore delaunay triangulation
            //While the stack is not empty
            int safety = 0;

            while (trianglesToInvestigate.Count > 0)
            {
                safety += 1;

                if (safety > 1000000)
                {
                    Debug.Log("Stuck in infinite loop when restoring delaunay in incremental sloan algorithm");

                    break;
                }

                //Step 7.1. Remove a triangle from the stack
                HalfEdge2 edgeToTest = trianglesToInvestigate.Pop();

                //Step 7.2. Do we need to flip this edge?
                //If p is outside or on the circumcircle for this triangle, we have a delaunay triangle and can return to next loop
                MyVector2 a = edgeToTest.v.position;
                MyVector2 b = edgeToTest.prevEdge.v.position;
                MyVector2 c = edgeToTest.nextEdge.v.position;

                //abc are here counter-clockwise
                if (DelaunayMethods.ShouldFlipEdgeStable(a, b, c, p))
                {
                    HalfEdgeHelpMethods.FlipTriangleEdge(edgeToTest);

                    //Step 7.3. Place any triangles which are now opposite p on the stack
                    AddTrianglesOppositePToStack(p, trianglesToInvestigate, triangulationData);

                    flippedEdges += 1;
                }
            }
        }
Ejemplo n.º 5
0
        //HalfEdgeData2
        public static HalfEdgeData2 UnNormalize(HalfEdgeData2 data, AABB2 aabb, float dMax)
        {
            foreach (HalfEdgeVertex2 v in data.vertices)
            {
                MyVector2 vUnNormalized = HelpMethods.UnNormalize(v.position, aabb, dMax);

                v.position = vUnNormalized;
            }

            return(data);
        }
Ejemplo n.º 6
0
        //HalfEdgeData2
        public HalfEdgeData2 UnNormalize(HalfEdgeData2 data)
        {
            foreach (HalfEdgeVertex2 v in data.vertices)
            {
                MyVector2 vUnNormalized = UnNormalize(v.position);

                v.position = vUnNormalized;
            }

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

        private static HalfEdgeData2 AddConstraints(HalfEdgeData2 triangleData, List <MyVector2> constraints, bool shouldRemoveTriangles)
        {
            //Validate the data
            if (constraints == null)
            {
                return(triangleData);
            }


            //First create a list with all unique edges
            //In the half-edge data structure, we have for each edge an half edge going in each direction,
            //making it unneccessary to loop through all edges for intersection tests
            //The report suggest we should do a triangle walk, but it will not work if the mesh has holes
            List <HalfEdge2> uniqueEdges = triangleData.GetUniqueEdges();


            //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
                if (IsEdgeInListOfEdges(uniqueEdges, c_p1, c_p2))
                {
                    continue;
                }

                //Step 2. Find all edges in the current triangulation that intersects with this constraint
                Queue <HalfEdge2> intersectingEdges = FindIntersectingEdges_BruteForce(uniqueEdges, c_p1, c_p2);

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

                //Step 3. Remove intersecting edges by flipping triangles
                List <HalfEdge2> newEdges = RemoveIntersectingEdges(c_p1, c_p2, intersectingEdges);

                //Step 4. Try to restore delaunay triangulation
                //Because we have constraints we will never get a delaunay triangulation
                RestoreDelaunayTriangulation(c_p1, c_p2, newEdges);
            }

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

            return(triangleData);
        }
        //
        // Remove all triangles that are inside the constraint
        //

        //This assumes the vertices in the constraint are ordered clockwise
        private static void RemoveSuperfluousTriangles(HalfEdgeData2 triangleData, List <MyVector2> constraints)
        {
            //This assumes we have at least 3 vertices in the constraint because we cant delete triangles inside a line
            if (constraints.Count < 3)
            {
                return;
            }

            HashSet <HalfEdgeFace2> trianglesToBeDeleted = FindTrianglesWithinConstraint(triangleData, constraints);

            //Delete the triangles
            foreach (HalfEdgeFace2 t in trianglesToBeDeleted)
            {
                HalfEdgeHelpMethods.DeleteTriangleFace(t, triangleData, true);
            }
        }
        //
        // Delete a triangle
        //
        public static void DeleteTriangleFace(HalfEdgeFace2 t, HalfEdgeData2 data, bool shouldSetOppositeToNull)
        {
            //Update the data structure
            //In the half-edge data structure there's an edge going in the opposite direction
            //on the other side of this triangle with a reference to this edge, so we have to set these to null
            HalfEdge2 t_e1 = t.edge;
            HalfEdge2 t_e2 = t_e1.nextEdge;
            HalfEdge2 t_e3 = t_e2.nextEdge;

            //If we want to remove the triangle and create a hole
            //But sometimes we have created a new triangle and then we cant set the opposite to null
            if (shouldSetOppositeToNull)
            {
                if (t_e1.oppositeEdge != null)
                {
                    t_e1.oppositeEdge.oppositeEdge = null;
                }
                if (t_e2.oppositeEdge != null)
                {
                    t_e2.oppositeEdge.oppositeEdge = null;
                }
                if (t_e3.oppositeEdge != null)
                {
                    t_e3.oppositeEdge.oppositeEdge = null;
                }
            }


            //Remove from the data structure

            //Remove from the list of all triangles
            data.faces.Remove(t);

            //Remove the edges from the list of all edges
            data.edges.Remove(t_e1);
            data.edges.Remove(t_e2);
            data.edges.Remove(t_e3);

            //Remove the vertices
            data.vertices.Remove(t_e1.v);
            data.vertices.Remove(t_e2.v);
            data.vertices.Remove(t_e3.v);
        }
        //
        // Half-edge to triangle if we know the half-edge consists of triangles
        //
        public static HashSet <Triangle2> HalfEdge2ToTriangle2(HalfEdgeData2 data)
        {
            if (data == null)
            {
                return(null);
            }

            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            foreach (HalfEdgeFace2 face in data.faces)
            {
                MyVector2 p1 = face.edge.v.position;
                MyVector2 p2 = face.edge.nextEdge.v.position;
                MyVector2 p3 = face.edge.nextEdge.nextEdge.v.position;

                Triangle2 t = new Triangle2(p1, p2, p3);

                triangles.Add(t);
            }

            return(triangles);
        }
        //
        // Alternative 1. Search through all triangles and use point-in-triangle
        //

        //Simple but slow
        public static HalfEdgeFace2 BruteForce(MyVector2 p, HalfEdgeData2 triangulationData)
        {
            HalfEdgeFace2 intersectingTriangle = null;

            foreach (HalfEdgeFace2 f in triangulationData.faces)
            {
                //The corners of this triangle
                MyVector2 v1 = f.edge.v.position;
                MyVector2 v2 = f.edge.nextEdge.v.position;
                MyVector2 v3 = f.edge.nextEdge.nextEdge.v.position;

                Triangle2 t = new Triangle2(v1, v2, v3);

                //Is the point in this triangle?
                if (_Intersections.PointTriangle(t, p, true))
                {
                    intersectingTriangle = f;

                    break;
                }
            }

            return(intersectingTriangle);
        }
Ejemplo n.º 12
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);
        }
        //Find all triangles opposite of vertex p
        //But we will find all edges opposite to p, and from these edges we can find the triangles
        private static void AddTrianglesOppositePToStack(MyVector2 p, Stack <HalfEdge2> trianglesOppositeP, HalfEdgeData2 triangulationData)
        {
            //Find a vertex at position p and then rotate around it, triangle-by-triangle, to find all opposite edges
            HalfEdgeVertex2 rotateAroundThis = null;

            foreach (HalfEdgeVertex2 v in triangulationData.vertices)
            {
                if (v.position.Equals(p))
                {
                    rotateAroundThis = v;
                }
            }

            //Which triangle is this vertex a part of, so we know when we have rotated all the way around
            HalfEdgeFace2 tStart = rotateAroundThis.edge.face;

            HalfEdgeFace2 tCurrent = null;

            int safety = 0;

            while (tCurrent != tStart)
            {
                safety += 1;

                if (safety > 10000)
                {
                    Debug.Log("Stuck in endless loop when finding opposite edges in Delaunay Sloan");

                    break;
                }

                //The edge opposite to p
                HalfEdge2 edgeOppositeRotateVertex = rotateAroundThis.edge.nextEdge.oppositeEdge;

                //Try to add the edge to the list iof triangles we are interested in
                //Null might happen if we are at the border
                //A stack might include duplicates so we have to check for that as well
                if (edgeOppositeRotateVertex != null && !trianglesOppositeP.Contains(edgeOppositeRotateVertex))
                {
                    trianglesOppositeP.Push(edgeOppositeRotateVertex);
                }

                //Rotate left - this assumes we can always rotate left so no holes are allowed
                //and neither can we investigate one of the vertices thats a part of the supertriangle
                //which we dont need to worry about because p is never a part of the supertriangle
                rotateAroundThis = rotateAroundThis.edge.oppositeEdge.v;

                //In which triangle are we now?
                tCurrent = rotateAroundThis.edge.face;
            }
        }
        public static HalfEdgeData2 GenerateTriangulation(HashSet <MyVector2> points, HalfEdgeData2 triangulationData)
        {
            //We need more than 1 point to
            if (points.Count < 2)
            {
                Debug.Log("Can make a delaunay with sloan with less than 2 points");

                return(null);
            }



            //Step 1.Normalize the points to the range(0 - 1), which assumes we have more than 1 point
            //Is not being done here, we assume the points are already normalized



            //Step 2. Sort the points into bins to make it faster to find which triangle a point is in
            //TODO



            //Step 3. Establish the supertriangle
            //The report says that the supertriangle should be at (-100, 100) which is way
            //outside of the points which are in the range(0, 1)
            //So make sure you have NORMALIZED the points
            Triangle2 superTriangle = new Triangle2(new MyVector2(-100f, -100f), new MyVector2(100f, -100f), new MyVector2(0f, 100f));

            //Create the triangulation data with the only triangle we have
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            triangles.Add(superTriangle);

            //Change to half-edge data structure
            _TransformBetweenDataStructures.Triangle2ToHalfEdge2(triangles, triangulationData);



            //Step 4. Loop over each point we want to insert and do Steps 5-7

            //These are for display purposes only
            int missedPoints = 0;
            int flippedEdges = 0;

            foreach (MyVector2 p in points)
            {
                //Step 5-7
                InsertNewPointInTriangulation(p, triangulationData, ref missedPoints, ref flippedEdges);
            }



            //Step 8. Delete the vertices belonging to the supertriangle
            RemoveSuperTriangle(superTriangle, triangulationData);



            //Step 9.Reset the coordinates to their original values because they are currently in the range (0,1)
            //Is being done outside of this method

            //TODO: replace this with StringBuilder
            string meshDataString = "Delaunay with sloan created a triangulation with: ";

            meshDataString += "Faces: " + triangulationData.faces.Count;
            meshDataString += " - Vertices: " + triangulationData.vertices.Count;
            meshDataString += " - Edges: " + triangulationData.edges.Count;
            meshDataString += " - Flipped egdes: " + flippedEdges;
            meshDataString += " - Missed points: " + missedPoints;

            Debug.Log(meshDataString);


            return(triangulationData);
        }
Ejemplo n.º 15
0
        public static HalfEdgeData2 GenerateTriangulation(HashSet <MyVector2> points, List <MyVector2> hull, HashSet <List <MyVector2> > holes, bool shouldRemoveTriangles, HalfEdgeData2 triangleData)
        {
            //Start by generating a delaunay triangulation with all points, including the constraints
            HashSet <MyVector2> allPoints = new HashSet <MyVector2>();

            if (points != null)
            {
                allPoints.UnionWith(points);
            }

            if (hull != null)
            {
                allPoints.UnionWith(hull);
            }

            if (holes != null)
            {
                foreach (List <MyVector2> hole in holes)
                {
                    allPoints.UnionWith(hole);
                }
            }


            //Generate the Delaunay triangulation with some algorithm
            //triangleData = _Delaunay.FlippingEdges(allPoints);
            triangleData = _Delaunay.PointByPoint(allPoints, triangleData);


            //Modify the triangulation by adding the constraints to the delaunay triangulation
            triangleData = AddConstraints(triangleData, hull, shouldRemoveTriangles);

            foreach (List <MyVector2> hole in holes)
            {
                triangleData = AddConstraints(triangleData, hole, shouldRemoveTriangles);
            }


            //Debug.Log(triangleData.faces.Count);

            return(triangleData);
        }
Ejemplo n.º 16
0
        //
        // Constrained Delaunay
        //

        //Algorithm 1. From the report "An algorithm for generating constrained delaunay triangulations" by Sloan
        //Start with a delaunay triangulation of all points, including the constraints
        //Then flip edges to make sure the constrains are in the triangulation
        //Then remove the unwanted triangles within the constraints (if we want to)
        // - sites: just some points
        // Constraints:
        // - hull: remove all triangles outside of the hull, should be ordered counter-clock-wise
        // - holes: remove all triangles within the holes, should be ordered clock-wise
        public static HalfEdgeData2 ConstrainedBySloan(HashSet <MyVector2> points, List <MyVector2> hull, HashSet <List <MyVector2> > holes, bool shouldRemoveTriangles, HalfEdgeData2 triangleData)
        {
            ConstrainedDelaunaySloan.GenerateTriangulation(points, hull, holes, shouldRemoveTriangles, triangleData);

            return(triangleData);
        }
Ejemplo n.º 17
0
        //
        // Find which triangles are within a constraint
        //

        public static HashSet <HalfEdgeFace2> FindTrianglesWithinConstraint(HalfEdgeData2 triangleData, List <MyVector2> constraints)
        {
            HashSet <HalfEdgeFace2> trianglesToDelete = new HashSet <HalfEdgeFace2>();

            //Store the triangles we flood fill in this queue
            Queue <HalfEdgeFace2> trianglesToCheck = new Queue <HalfEdgeFace2>();


            //Step 1. Find all half-edges in the current triangulation which are constraint
            //Maybe faster to find all constraintEdges for ALL constraints because we are doing this per hole and hull
            //We have to find ALL because some triangles are not connected and will thus be missed if we find just a single start-triangle
            //Is also needed when flood-filling so we dont jump over a constraint
            HashSet <HalfEdge2> constraintEdges = FindAllConstraintEdges(constraints, triangleData);

            //Each edge is associated with a face which should be deleted
            foreach (HalfEdge2 e in constraintEdges)
            {
                if (!trianglesToCheck.Contains(e.face))
                {
                    trianglesToCheck.Enqueue(e.face);
                }
            }


            //Step 2. Find the rest of the triangles within the constraint by using a flood-fill algorithm
            int safety = 0;

            List <HalfEdge2> edgesToCheck = new List <HalfEdge2>();

            while (true)
            {
                safety += 1;

                if (safety > 100000)
                {
                    Debug.Log("Stuck in infinite loop when looking for triangles within constraint");

                    break;
                }

                //Stop if we are out of neighbors
                if (trianglesToCheck.Count == 0)
                {
                    break;
                }

                //Pick the first triangle in the list and investigate its neighbors
                HalfEdgeFace2 t = trianglesToCheck.Dequeue();

                //Add it for deletion
                trianglesToDelete.Add(t);

                //Investigate the triangles on the opposite sides of these edges
                edgesToCheck.Clear();

                edgesToCheck.Add(t.edge);
                edgesToCheck.Add(t.edge.nextEdge);
                edgesToCheck.Add(t.edge.nextEdge.nextEdge);

                //A triangle is a neighbor within the constraint if:
                //- The neighbor is not an outer border meaning no neighbor exists
                //- If we have not already visited the neighbor
                //- If the edge between the neighbor and this triangle is not a constraint
                foreach (HalfEdge2 e in edgesToCheck)
                {
                    //No neighbor exists
                    if (e.oppositeEdge == null)
                    {
                        continue;
                    }

                    HalfEdgeFace2 neighbor = e.oppositeEdge.face;

                    //We have already visited this neighbor
                    if (trianglesToDelete.Contains(neighbor) || trianglesToCheck.Contains(neighbor))
                    {
                        continue;
                    }

                    //This edge is a constraint and we can't jump across constraints
                    if (constraintEdges.Contains(e))
                    {
                        continue;
                    }

                    trianglesToCheck.Enqueue(neighbor);
                }
            }

            return(trianglesToDelete);
        }
Ejemplo n.º 18
0
        //Find a triangle which has an edge going from p1 to p2
        private static HalfEdgeFace2 FindTriangleWithEdge(MyVector2 p1, MyVector2 p2, HalfEdgeData2 triangleData)
        {
            HashSet <HalfEdge2> edges = triangleData.edges;

            foreach (HalfEdge2 e in edges)
            {
                //An edge is going TO a vertex
                MyVector2 e_p1 = e.prevEdge.v.position;
                MyVector2 e_p2 = e.v.position;

                if (e_p1.Equals(p1) && e_p2.Equals(p2))
                {
                    return(e.face);
                }
            }

            return(null);
        }
        public static HalfEdgeData2 GenerateTriangulation(HashSet <MyVector2> points, List <MyVector2> constraints, bool shouldRemoveTriangles, HalfEdgeData2 triangleData)
        {
            //Start by generating a delaunay triangulation with all points, including the constraints
            if (constraints != null)
            {
                points.UnionWith(constraints);
            }

            //Generate the Delaunay triangulation with some algorithm
            //triangleData = _Delaunay.FlippingEdges(points);
            triangleData = _Delaunay.PointByPoint(points, triangleData);


            //Modify the triangulation by adding the constraints to the delaunay triangulation
            if (constraints != null)
            {
                triangleData = AddConstraints(triangleData, constraints, shouldRemoveTriangles);
            }

            //Debug.Log(triangleData.faces.Count);

            return(triangleData);
        }
        //
        // Find which triangles are within a constraint
        //

        public static HashSet <HalfEdgeFace2> FindTrianglesWithinConstraint(HalfEdgeData2 triangleData, List <MyVector2> constraints)
        {
            HashSet <HalfEdgeFace2> trianglesToDelete = new HashSet <HalfEdgeFace2>();


            //Step 1. Find a triangle with an edge that shares an edge with the first constraint edge in the list
            //Since both are clockwise we know we are "inside" of the constraint, so this is a triangle we should delete
            HalfEdgeFace2 borderTriangle = null;

            MyVector2 c_p1 = constraints[0];
            MyVector2 c_p2 = constraints[1];

            //Search through all triangles
            foreach (HalfEdgeFace2 t in triangleData.faces)
            {
                //The edges in this triangle
                HalfEdge2 e1 = t.edge;
                HalfEdge2 e2 = e1.nextEdge;
                HalfEdge2 e3 = e2.nextEdge;

                //Is any of these edges a constraint? If so we have find the first triangle
                if (e1.v.position.Equals(c_p2) && e1.prevEdge.v.position.Equals(c_p1))
                {
                    borderTriangle = t;

                    break;
                }
                if (e2.v.position.Equals(c_p2) && e2.prevEdge.v.position.Equals(c_p1))
                {
                    borderTriangle = t;

                    break;
                }
                if (e3.v.position.Equals(c_p2) && e3.prevEdge.v.position.Equals(c_p1))
                {
                    borderTriangle = t;

                    break;
                }
            }

            if (borderTriangle == null)
            {
                return(null);
            }



            //Step 2. Find the rest of the triangles within the constraint by using a flood fill algorithm

            //Maybe better to first find all the other border triangles?

            //We know this triangle should be deleted
            trianglesToDelete.Add(borderTriangle);

            //Store the triangles we flood filling in this queue
            Queue <HalfEdgeFace2> trianglesToCheck = new Queue <HalfEdgeFace2>();

            //Start at the triangle we know is within the constraints
            trianglesToCheck.Enqueue(borderTriangle);

            int safety = 0;

            while (true)
            {
                safety += 1;

                if (safety > 100000)
                {
                    Debug.Log("Stuck in infinite loop when looking for triangles within constraint");

                    break;
                }

                //Stop if we are out of neighbors
                if (trianglesToCheck.Count == 0)
                {
                    break;
                }

                //Pick the first triangle in the list and investigate its neighbors
                HalfEdgeFace2 t = trianglesToCheck.Dequeue();

                //Investigate the triangles on the opposite sides of these edges
                HalfEdge2 e1 = t.edge;
                HalfEdge2 e2 = e1.nextEdge;
                HalfEdge2 e3 = e2.nextEdge;

                //A triangle is a neighbor within the constraint if:
                //- The neighbor is not an outer border meaning no neighbor exists
                //- If we have not already visited the neighbor
                //- If the edge between the neighbor and this triangle is not a constraint
                if (e1.oppositeEdge != null &&
                    !trianglesToDelete.Contains(e1.oppositeEdge.face) &&
                    !trianglesToCheck.Contains(e1.oppositeEdge.face) &&
                    !IsEdgeAConstraint(e1.v.position, e1.prevEdge.v.position, constraints))
                {
                    trianglesToCheck.Enqueue(e1.oppositeEdge.face);

                    trianglesToDelete.Add(e1.oppositeEdge.face);
                }
                if (e2.oppositeEdge != null &&
                    !trianglesToDelete.Contains(e2.oppositeEdge.face) &&
                    !trianglesToCheck.Contains(e2.oppositeEdge.face) &&
                    !IsEdgeAConstraint(e2.v.position, e2.prevEdge.v.position, constraints))
                {
                    trianglesToCheck.Enqueue(e2.oppositeEdge.face);

                    trianglesToDelete.Add(e2.oppositeEdge.face);
                }
                if (e3.oppositeEdge != null &&
                    !trianglesToDelete.Contains(e3.oppositeEdge.face) &&
                    !trianglesToCheck.Contains(e3.oppositeEdge.face) &&
                    !IsEdgeAConstraint(e3.v.position, e3.prevEdge.v.position, constraints))
                {
                    trianglesToCheck.Enqueue(e3.oppositeEdge.face);

                    trianglesToDelete.Add(e3.oppositeEdge.face);
                }
            }

            return(trianglesToDelete);
        }
Ejemplo n.º 21
0
        //
        // Delaunay
        //

        //Algorithm 1. Triangulate the points with some algorithm - then flip edges until we have a delaunay triangulation
        public static HalfEdgeData2 FlippingEdges(HashSet <MyVector2> points, HalfEdgeData2 triangleData)
        {
            triangleData = DelaunayFlipEdges.GenerateTriangulation(points, triangleData);

            return(triangleData);
        }
Ejemplo n.º 22
0
        public static List <VoronoiCell2> GenerateVoronoiDiagram(HashSet <MyVector2> sites)
        {
            //First generate the delaunay triangulation
            //This one has caused a bug so should be avoided
            //HalfEdgeData2 data = _Delaunay.FlippingEdges(sites, new HalfEdgeData2());
            //This one is faster and more accurate, so use it. But if you are using it, make sure to normalize the sites!
            HalfEdgeData2 data = _Delaunay.PointByPoint(sites, new HalfEdgeData2());


            //Generate the voronoi diagram

            //Step 1. For every delaunay edge, compute a voronoi edge
            //The voronoi edge is the edge connecting the circumcenters of two neighboring delaunay triangles
            List <VoronoiEdge2> voronoiEdges = new List <VoronoiEdge2>();

            HashSet <HalfEdgeFace2> triangles = data.faces;

            foreach (HalfEdgeFace2 t in triangles)
            {
                //Each triangle consists of these edges
                HalfEdge2 e1 = t.edge;
                HalfEdge2 e2 = e1.nextEdge;
                HalfEdge2 e3 = e2.nextEdge;

                //Calculate the circumcenter for this triangle
                MyVector2 v1 = e1.v.position;
                MyVector2 v2 = e2.v.position;
                MyVector2 v3 = e3.v.position;

                //The circumcenter is the center of a circle where the triangles corners is on the circumference of that circle
                //The circumcenter is also known as a voronoi vertex, which is a position in the diagram where we are equally
                //close to the surrounding sites
                MyVector2 voronoiVertex = _Geometry.CalculateCircleCenter(v1, v2, v3);

                //This will generate double edges - one belonging to each site, and could maybe be improved in the future
                //by using the half-edge data structure
                TryAddVoronoiEdgeFromTriangleEdge(e1, voronoiVertex, voronoiEdges);
                TryAddVoronoiEdgeFromTriangleEdge(e2, voronoiVertex, voronoiEdges);
                TryAddVoronoiEdgeFromTriangleEdge(e3, voronoiVertex, voronoiEdges);
            }


            //Step 2. Find the voronoi cells where each cell is a list of all edges belonging to a site
            List <VoronoiCell2> voronoiCells = new List <VoronoiCell2>();

            for (int i = 0; i < voronoiEdges.Count; i++)
            {
                VoronoiEdge2 e = voronoiEdges[i];

                //Find the position in the list of all cells that includes this site
                int cellPos = TryFindCellPos(e, voronoiCells);

                //No cell was found so we need to create a new cell
                if (cellPos == -1)
                {
                    VoronoiCell2 newCell = new VoronoiCell2(e.sitePos);

                    voronoiCells.Add(newCell);

                    newCell.edges.Add(e);
                }
                else
                {
                    voronoiCells[cellPos].edges.Add(e);
                }
            }


            return(voronoiCells);
        }
Ejemplo n.º 23
0
        //Find a triangle which has an edge going from p1 to p2
        //private static HalfEdgeFace2 FindTriangleWithEdge(MyVector2 p1, MyVector2 p2, HalfEdgeData2 triangleData)
        //{
        //    HashSet<HalfEdge2> edges = triangleData.edges;

        //    foreach (HalfEdge2 e in edges)
        //    {
        //        //An edge is going TO a vertex
        //        MyVector2 e_p1 = e.prevEdge.v.position;
        //        MyVector2 e_p2 = e.v.position;

        //        if (e_p1.Equals(p1) && e_p2.Equals(p2))
        //        {
        //            return e.face;
        //        }
        //    }

        //    return null;
        //}



        //Find all half-edges that are constraint
        private static HashSet <HalfEdge2> FindAllConstraintEdges(List <MyVector2> constraints, HalfEdgeData2 triangleData)
        {
            HashSet <HalfEdge2> constrainEdges = new HashSet <HalfEdge2>();


            //Create a new set with all constrains, and as we discover new constraints, we delete constrains, which will make searching faster
            //A constraint can only exist once!
            HashSet <Edge2> constraintsEdges = new HashSet <Edge2>();

            for (int i = 0; i < constraints.Count; i++)
            {
                MyVector2 c_p1 = constraints[i];
                MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)];

                constraintsEdges.Add(new Edge2(c_p1, c_p2));
            }


            //All edges we have to search
            HashSet <HalfEdge2> edges = triangleData.edges;

            foreach (HalfEdge2 e in edges)
            {
                //An edge is going TO a vertex
                MyVector2 e_p1 = e.prevEdge.v.position;
                MyVector2 e_p2 = e.v.position;

                //Is this edge a constraint?
                foreach (Edge2 c_edge in constraintsEdges)
                {
                    if (e_p1.Equals(c_edge.p1) && e_p2.Equals(c_edge.p2))
                    {
                        constrainEdges.Add(e);

                        constraintsEdges.Remove(c_edge);

                        //Move on to the next edge
                        break;
                    }
                }

                //We have found all constraint, so don't need to search anymore
                if (constraintsEdges.Count == 0)
                {
                    break;
                }
            }

            return(constrainEdges);
        }
Ejemplo n.º 24
0
        //Find all triangles that share a specific constraint
        private static HashSet <HalfEdgeFace2> FindAllTrianglesBorderingTheConstraint(List <MyVector2> constraints, HalfEdgeData2 triangleData)
        {
            HashSet <HalfEdgeFace2> facesOnTheBorder = new HashSet <HalfEdgeFace2>();


            //Create a new set with all constrains, and as we discover new constraints, we delete constrains, which will make searching faster
            HashSet <Edge2> constraintsEdges = new HashSet <Edge2>();

            for (int i = 0; i < constraints.Count; i++)
            {
                MyVector2 c_p1 = constraints[i];
                MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)];

                constraintsEdges.Add(new Edge2(c_p1, c_p2));
            }


            //Faster to search faces becase a triangle might have multiple constraints bordering it and we just need to find one
            HashSet <HalfEdgeFace2> faces = triangleData.faces;

            List <HalfEdge2> edges = new List <HalfEdge2>();

            foreach (HalfEdgeFace2 f in faces)
            {
                edges.Clear();

                edges.Add(f.edge);
                edges.Add(f.edge.nextEdge);
                edges.Add(f.edge.nextEdge.nextEdge);

                foreach (HalfEdge2 e in edges)
                {
                    //An edge is going TO a vertex
                    MyVector2 e_p1 = e.prevEdge.v.position;
                    MyVector2 e_p2 = e.v.position;

                    //Is this edge a constraint?
                    bool foundConstraint = false;

                    foreach (Edge2 c_edge in constraintsEdges)
                    {
                        if (e_p1.Equals(c_edge.p1) && e_p2.Equals(c_edge.p2))
                        {
                            facesOnTheBorder.Add(f);

                            constraintsEdges.Remove(c_edge);

                            foundConstraint = true;

                            break;
                        }
                    }

                    if (foundConstraint)
                    {
                        break;
                    }
                }

                if (constraintsEdges.Count == 0)
                {
                    break;
                }
            }

            return(facesOnTheBorder);
        }
        //
        // Triangle to half-edge
        //
        public static HalfEdgeData2 Triangle2ToHalfEdge2(HashSet <Triangle2> triangles, HalfEdgeData2 data)
        {
            //Make sure the triangles have the same orientation, which is clockwise
            triangles = HelpMethods.OrientTrianglesClockwise(triangles);


            //Fill the data structure
            foreach (Triangle2 t in triangles)
            {
                HalfEdgeVertex2 v1 = new HalfEdgeVertex2(t.p1);
                HalfEdgeVertex2 v2 = new HalfEdgeVertex2(t.p2);
                HalfEdgeVertex2 v3 = new HalfEdgeVertex2(t.p3);

                //The vertices the edge points to
                HalfEdge2 he1 = new HalfEdge2(v1);
                HalfEdge2 he2 = new HalfEdge2(v2);
                HalfEdge2 he3 = new HalfEdge2(v3);

                he1.nextEdge = he2;
                he2.nextEdge = he3;
                he3.nextEdge = he1;

                he1.prevEdge = he3;
                he2.prevEdge = he1;
                he3.prevEdge = he2;

                //The vertex needs to know of an edge going from it
                v1.edge = he2;
                v2.edge = he3;
                v3.edge = he1;

                //The face the half-edge is connected to
                HalfEdgeFace2 face = new HalfEdgeFace2(he1);

                //Each edge needs to know of the face connected to this edge
                he1.face = face;
                he2.face = face;
                he3.face = face;


                //Add everything to the lists
                data.edges.Add(he1);
                data.edges.Add(he2);
                data.edges.Add(he3);

                data.faces.Add(face);

                data.vertices.Add(v1);
                data.vertices.Add(v2);
                data.vertices.Add(v3);
            }


            //Step 4. Find the half-edges going in the opposite direction of each edge we have
            //Is there a faster way to do this because this is the bottleneck?
            foreach (HalfEdge2 e in data.edges)
            {
                HalfEdgeVertex2 goingToVertex   = e.v;
                HalfEdgeVertex2 goingFromVertex = e.prevEdge.v;

                foreach (HalfEdge2 eOther in data.edges)
                {
                    //Dont compare with itself
                    if (e == eOther)
                    {
                        continue;
                    }

                    //Is this edge going between the vertices in the opposite direction
                    if (goingFromVertex.position.Equals(eOther.v.position) && goingToVertex.position.Equals(eOther.prevEdge.v.position))
                    {
                        e.oppositeEdge = eOther;

                        break;
                    }
                }
            }


            return(data);
        }
Ejemplo n.º 26
0
        public static HalfEdgeData2 GenerateTriangulation(HashSet <MyVector2> points, HalfEdgeData2 triangleData)
        {
            //Step 1. Triangulate the points with some algorithm. The result is a convex triangulation
            //List<Triangle> triangles = TriangulatePoints.IncrementalTriangulation(points);
            HashSet <Triangle2> triangles = _TriangulatePoints.TriangleSplitting(points);

            //Step 2. Change the data structure from triangle to half-edge to make it easier to flip edges
            triangleData = _TransformBetweenDataStructures.Triangle2ToHalfEdge2(triangles, triangleData);

            //Step 3. Flip edges until we have a delaunay triangulation
            FlipEdges(triangleData);

            return(triangleData);
        }
        public static HashSet <VoronoiCell2> GenerateVoronoiDiagram(HashSet <MyVector2> sites)
        {
            //First generate the delaunay triangulation
            //This one has caused a bug so should be avoided
            //HalfEdgeData2 data = _Delaunay.FlippingEdges(sites, new HalfEdgeData2());
            //This one is faster and more accurate, so use it. But if you are using it, make sure to normalize the sites!
            HalfEdgeData2 delaunayTriangulation = _Delaunay.PointByPoint(sites, new HalfEdgeData2());


            //Generate the voronoi diagram

            //Step 1. For every delaunay edge, compute a voronoi edge
            //The voronoi edge is the edge connecting the circumcenters of two neighboring delaunay triangles
            List <VoronoiEdge2> voronoiEdges = new List <VoronoiEdge2>();

            HashSet <HalfEdgeFace2> triangles = delaunayTriangulation.faces;

            //Loop through each triangle
            foreach (HalfEdgeFace2 t in triangles)
            {
                //Each triangle consists of these edges
                HalfEdge2 e1 = t.edge;
                HalfEdge2 e2 = e1.nextEdge;
                HalfEdge2 e3 = e2.nextEdge;

                //Calculate the circumcenter for this triangle
                MyVector2 v1 = e1.v.position;
                MyVector2 v2 = e2.v.position;
                MyVector2 v3 = e3.v.position;

                //The circumcenter is the center of a circle where the triangles corners is on the circumference of that circle
                //The circumcenter is also known as a voronoi vertex, which is a position in the diagram where we are equally
                //close to the surrounding sites (= the corners ina voronoi cell)
                MyVector2 voronoiVertex = _Geometry.CalculateCircleCenter(v1, v2, v3);

                //Debug.Log(voronoiVertex.x + " " + voronoiVertex.y);

                //We will generate a single edge belonging to this site
                //Try means that this edge might not have an opposite and then we can't generate an edge
                TryAddVoronoiEdgeFromTriangleEdge(e1, voronoiVertex, voronoiEdges);
                TryAddVoronoiEdgeFromTriangleEdge(e2, voronoiVertex, voronoiEdges);
                TryAddVoronoiEdgeFromTriangleEdge(e3, voronoiVertex, voronoiEdges);
            }


            //Step 2. Find the voronoi cells where each cell is a list of all edges belonging to a site
            //So we have a lot of edges and now each edge should get a cell
            //These edges are not sorted, so they are added as we find them
            HashSet <VoronoiCell2> voronoiCells = new HashSet <VoronoiCell2>();

            for (int i = 0; i < voronoiEdges.Count; i++)
            {
                VoronoiEdge2 e = voronoiEdges[i];

                //Find the cell in the list of all cells that includes this site
                VoronoiCell2 cell = TryFindCell(e, voronoiCells);

                //No cell was found so we need to create a new cell
                if (cell == null)
                {
                    VoronoiCell2 newCell = new VoronoiCell2(e.sitePos);

                    voronoiCells.Add(newCell);

                    newCell.edges.Add(e);
                }
                else
                {
                    cell.edges.Add(e);
                }
            }


            return(voronoiCells);
        }
Ejemplo n.º 28
0
        public static HalfEdgeData2 GenerateTriangulation(HashSet <MyVector2> points, List <MyVector2> hull, HashSet <List <MyVector2> > holes, bool shouldRemoveTriangles, HalfEdgeData2 triangleData)
        {
            //Start by generating a delaunay triangulation with all points, including the constraints
            HashSet <MyVector2> allPoints = new HashSet <MyVector2>();

            if (points != null)
            {
                allPoints.UnionWith(points);
            }

            if (hull != null)
            {
                allPoints.UnionWith(hull);
            }

            if (holes != null)
            {
                foreach (List <MyVector2> hole in holes)
                {
                    allPoints.UnionWith(hole);
                }
            }


            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();

            //Generate the Delaunay triangulation with some algorithm
            //timer.Start();

            //triangleData = _Delaunay.FlippingEdges(allPoints);
            triangleData = _Delaunay.PointByPoint(allPoints, triangleData);

            //timer.Stop();

            //Delaunay takes 0.003 seconds for the house so is not the bottle neck
            //Debug.Log($"Delaunay triangulation took {timer.ElapsedMilliseconds / 1000f} seconds");


            //Modify the triangulation by adding the constraints to the delaunay triangulation
            triangleData = AddConstraints(triangleData, hull, shouldRemoveTriangles, timer);

            foreach (List <MyVector2> hole in holes)
            {
                triangleData = AddConstraints(triangleData, hole, shouldRemoveTriangles, timer);
            }


            //Debug.Log(triangleData.faces.Count);

            //Debug.Log($"Whatever time we measured it took {timer.ElapsedMilliseconds / 1000f} seconds");

            return(triangleData);
        }
Ejemplo n.º 29
0
        //
        // Find which triangles are within a constraint
        //

        public static HashSet <HalfEdgeFace2> FindTrianglesWithinConstraint(HalfEdgeData2 triangleData, List <MyVector2> constraints)
        {
            HashSet <HalfEdgeFace2> trianglesToDelete = new HashSet <HalfEdgeFace2>();

            //Store the triangles we flood fill in this queue
            Queue <HalfEdgeFace2> trianglesToCheck = new Queue <HalfEdgeFace2>();

            //Step 1. Find all triangles with an edge that is a constraint
            //We have to find all because they are not always connected, so we cant just find one and flood fill from it
            //for (int i = 0; i < constraints.Count; i++)
            //{
            //    MyVector2 c_p1 = constraints[i];
            //    MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)];

            //    HalfEdgeFace2 borderTriangle = FindTriangleWithEdge(c_p1, c_p2, triangleData);

            //    //Maybe this edge has not triangle, which can happen if it's on the border
            //    if (borderTriangle != null)
            //    {
            //        trianglesToCheck.Enqueue(borderTriangle);
            //    }
            //}

            HashSet <HalfEdgeFace2> borderFaces = FindAllTrianglesBorderingTheConstraint(constraints, triangleData);

            foreach (HalfEdgeFace2 f in borderFaces)
            {
                trianglesToCheck.Enqueue(f);
            }



            //Step 2. Find the rest of the triangles within the constraint by using a flood fill algorithm
            int safety = 0;

            List <HalfEdge2> edgesToCheck = new List <HalfEdge2>();

            while (true)
            {
                safety += 1;

                if (safety > 100000)
                {
                    Debug.Log("Stuck in infinite loop when looking for triangles within constraint");

                    break;
                }

                //Stop if we are out of neighbors
                if (trianglesToCheck.Count == 0)
                {
                    break;
                }

                //Pick the first triangle in the list and investigate its neighbors
                HalfEdgeFace2 t = trianglesToCheck.Dequeue();

                //Add it for deletion
                trianglesToDelete.Add(t);

                //Investigate the triangles on the opposite sides of these edges
                edgesToCheck.Clear();

                edgesToCheck.Add(t.edge);
                edgesToCheck.Add(t.edge.nextEdge);
                edgesToCheck.Add(t.edge.nextEdge.nextEdge);

                //A triangle is a neighbor within the constraint if:
                //- The neighbor is not an outer border meaning no neighbor exists
                //- If we have not already visited the neighbor
                //- If the edge between the neighbor and this triangle is not a constraint
                foreach (HalfEdge2 e in edgesToCheck)
                {
                    //No neighbor exists
                    if (e.oppositeEdge == null)
                    {
                        continue;
                    }

                    HalfEdgeFace2 neighbor = e.oppositeEdge.face;

                    //We have already visited this neighbor
                    if (trianglesToDelete.Contains(neighbor) || trianglesToCheck.Contains(neighbor))
                    {
                        continue;
                    }

                    //We have to check if this edge is a constraint because it might be alone on the border without neighbors that are also constraints
                    MyVector2 p1 = e.prevEdge.v.position;
                    MyVector2 p2 = e.v.position;

                    if (IsEdgeAConstraint(p1, p2, constraints))
                    {
                        continue;
                    }

                    trianglesToCheck.Enqueue(neighbor);
                }
            }

            return(trianglesToDelete);
        }
Ejemplo n.º 30
0
        //Algorithm 2. Start with one triangle covering all points - then insert the points one-by-one while flipping edges
        //From the report "A fast algorithm for constructing Delaunay triangulations in the plane" by Sloan
        public static HalfEdgeData2 PointByPoint(HashSet <MyVector2> points, HalfEdgeData2 triangleData)
        {
            triangleData = DelaunayIncrementalSloan.GenerateTriangulation(points, triangleData);

            return(triangleData);
        }