Example #1
0
        //
        // Find edges that intersect with a constraint
        //

        //Method 1. Brute force by testing all unique edges
        //Find all edges of the current triangulation that intersects with the constraint edge between p1 and p2
        private static Queue <HalfEdge2> FindIntersectingEdges_BruteForce(List <HalfEdge2> uniqueEdges, MyVector2 c_p1, MyVector2 c_p2)
        {
            //Should be in a queue because we will later plop the first in the queue and add edges in the back of the queue
            Queue <HalfEdge2> intersectingEdges = new Queue <HalfEdge2>();

            //Loop through all edges and see if they are intersecting with the constrained edge
            for (int i = 0; i < uniqueEdges.Count; i++)
            {
                //The edges the triangle consists of
                HalfEdge2 e = uniqueEdges[i];

                //The position the edge is going to
                MyVector2 e_p1 = e.v.position;
                //The position the edge is coming from
                MyVector2 e_p2 = e.prevEdge.v.position;

                //Is this edge intersecting with the constraint?
                if (IsEdgeCrossingEdge(e_p1, e_p2, c_p1, c_p2))
                {
                    //If so add it to the queue of edges
                    intersectingEdges.Enqueue(e);
                }
            }

            return(intersectingEdges);
        }
Example #2
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
        }
Example #3
0
        //Get a list with unique edges
        //Currently we have two half-edges for each edge, making it time consuming
        //So this method is not always needed, but can be useful
        public List <HalfEdge2> GetUniqueEdges()
        {
            List <HalfEdge2> uniqueEdges = new List <HalfEdge2>();

            foreach (HalfEdge2 e in edges)
            {
                MyVector2 p1 = e.v.position;
                MyVector2 p2 = e.prevEdge.v.position;

                bool isInList = false;

                for (int j = 0; j < uniqueEdges.Count; j++)
                {
                    HalfEdge2 testEdge = uniqueEdges[j];

                    MyVector2 p1_test = testEdge.v.position;
                    MyVector2 p2_test = testEdge.prevEdge.v.position;

                    if ((p1.Equals(p1_test) && p2.Equals(p2_test)) || (p2.Equals(p1_test) && p1.Equals(p2_test)))
                    {
                        isInList = true;

                        break;
                    }
                }

                if (!isInList)
                {
                    uniqueEdges.Add(e);
                }
            }

            return(uniqueEdges);
        }
        //Try to add a voronoi edge. Not all edges have a neighboring triangle, and if it hasnt we cant add a voronoi edge
        private static void TryAddVoronoiEdgeFromTriangleEdge(HalfEdge2 e, MyVector2 voronoiVertex, List <VoronoiEdge2> allEdges)
        {
            //Ignore if this edge has no neighboring triangle
            //If no opposite exists, we could maybe add a fake opposite to get an edge far away
            if (e.oppositeEdge == null)
            {
                return;
            }

            //Calculate the circumcenter of the neighbor
            HalfEdge2 eNeighbor = e.oppositeEdge;

            MyVector2 v1 = eNeighbor.v.position;
            MyVector2 v2 = eNeighbor.nextEdge.v.position;
            MyVector2 v3 = eNeighbor.nextEdge.nextEdge.v.position;

            MyVector2 voronoiVertexNeighbor = _Geometry.CalculateCircleCenter(v1, v2, v3);

            //Create a new voronoi edge between the voronoi vertices
            //Each edge in the half-edge data structure points TO a vertex, so this edge will be associated
            //with the vertex the edge is going from
            VoronoiEdge2 edge = new VoronoiEdge2(voronoiVertex, voronoiVertexNeighbor, sitePos: e.prevEdge.v.position);

            allEdges.Add(edge);
        }
        //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;
                }
            }
        }
        //
        // Split triangle edge
        //
        //Split an edge at a point on the edge to form four new triangles, while removing two old
        //public static void SplitTriangleEdge(HalfEdge e, Vector3 splitPosition)
        //{

        //}



        //
        // Split triangle face
        //
        //Split a face (which we know is a triangle) at a point to create three new triangles while removing the old triangle
        //Could maybe make it more general so we can split a face, which consists of n edges
        public static void SplitTriangleFaceAtPoint(HalfEdgeFace2 f, MyVector2 splitPosition, HalfEdgeData2 data)
        {
            //The edges that belongs to this face
            HalfEdge2 e_1 = f.edge;
            HalfEdge2 e_2 = e_1.nextEdge;
            HalfEdge2 e_3 = e_2.nextEdge;

            //A list with new edges so we can connect the new edges with an edge on the opposite side
            HashSet <HalfEdge2> newEdges = new HashSet <HalfEdge2>();

            CreateNewFace(e_1, splitPosition, data, newEdges);
            CreateNewFace(e_2, splitPosition, data, newEdges);
            CreateNewFace(e_3, splitPosition, data, newEdges);

            //Debug.Log("New edges " + newEdges.Count);

            //Find the opposite connections
            foreach (HalfEdge2 e in newEdges)
            {
                //If we have already found a opposite
                if (e.oppositeEdge != null)
                {
                    continue;
                }

                MyVector2 eGoingTo   = e.v.position;
                MyVector2 eGoingFrom = e.prevEdge.v.position;

                foreach (HalfEdge2 eOpposite in newEdges)
                {
                    if (e == eOpposite || eOpposite.oppositeEdge != null)
                    {
                        continue;
                    }

                    MyVector2 eGoingTo_Other   = eOpposite.v.position;
                    MyVector2 eGoingFrom_Other = eOpposite.prevEdge.v.position;

                    if (eGoingTo.Equals(eGoingFrom_Other) && eGoingFrom.Equals(eGoingTo_Other))
                    {
                        e.oppositeEdge = eOpposite;
                        //Might as well connect it from the other way as well
                        eOpposite.oppositeEdge = e;

                        //Debug.Log("Found opposite");
                    }
                }
            }

            //Delete the old triangle
            DeleteTriangleFace(f, data, false);
        }
        //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;
            }
        }
        //
        // 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);
        }
Example #9
0
        //Try to add a voronoi edge. Not all edges have a neighboring triangle, and if it hasnt we cant add a voronoi edge
        private static void TryAddVoronoiEdgeFromTriangleEdge(HalfEdge2 e, MyVector2 voronoiVertex, List <VoronoiEdge2> allEdges)
        {
            //Ignore if this edge has no neighboring triangle
            if (e.oppositeEdge == null)
            {
                return;
            }

            //Calculate the circumcenter of the neighbor
            HalfEdge2 eNeighbor = e.oppositeEdge;

            MyVector2 v1 = eNeighbor.v.position;
            MyVector2 v2 = eNeighbor.nextEdge.v.position;
            MyVector2 v3 = eNeighbor.nextEdge.nextEdge.v.position;

            MyVector2 voronoiVertexNeighbor = _Geometry.CalculateCircleCenter(v1, v2, v3);

            //Create a new vornoi edge between the voronoi vertices
            VoronoiEdge2 edge = new VoronoiEdge2(voronoiVertex, voronoiVertexNeighbor, sitePos: e.prevEdge.v.position);

            allEdges.Add(edge);
        }
Example #10
0
        //
        // Try to restore the delaunay triangulation by flipping newly created edges
        //

        //This process is similar to when we created the original delaunay triangulation
        //This step can maybe be skipped if you just want a triangulation and Ive noticed its often not flipping any triangles
        private static void RestoreDelaunayTriangulation(MyVector2 c_p1, MyVector2 c_p2, List <HalfEdge2> newEdges)
        {
            int safety = 0;

            int flippedEdges = 0;

            //Repeat 4.1 - 4.3 until no further swaps take place
            while (true)
            {
                safety += 1;

                if (safety > 100000)
                {
                    Debug.Log("Stuck in endless loop when delaunay after fixing constrained edges");

                    break;
                }

                bool hasFlippedEdge = false;

                //Step 4.1. Loop over each edge in the list of newly created edges
                for (int j = 0; j < newEdges.Count; j++)
                {
                    HalfEdge2 e = newEdges[j];

                    //Step 4.2. Let the newly created edge be defined by the vertices
                    MyVector2 v_k = e.v.position;
                    MyVector2 v_l = e.prevEdge.v.position;

                    //If this edge is equal to the constrained edge, then skip to step 4.1
                    //because we are not allowed to flip the constrained edge
                    if ((v_k.Equals(c_p1) && v_l.Equals(c_p2)) || (v_l.Equals(c_p1) && v_k.Equals(c_p2)))
                    {
                        continue;
                    }

                    //Step 4.3. If the two triangles that share edge v_k and v_l don't satisfy the delaunay criterion,
                    //so that a vertex of one of the triangles is inside the circumcircle of the other triangle, flip the edge
                    //The third vertex of the triangle belonging to this edge
                    MyVector2 v_third_pos = e.nextEdge.v.position;
                    //The vertice belonging to the triangle on the opposite side of the edge and this vertex is not a part of the edge
                    MyVector2 v_opposite_pos = e.oppositeEdge.nextEdge.v.position;

                    //Test if we should flip this edge
                    if (DelaunayMethods.ShouldFlipEdge(v_l, v_k, v_third_pos, v_opposite_pos))
                    {
                        //Flip the edge
                        hasFlippedEdge = true;

                        HalfEdgeHelpMethods.FlipTriangleEdge(e);

                        flippedEdges += 1;
                    }
                }

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

                    break;
                }
            }
        }
        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);
        }
Example #12
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);
        }
        //
        // Alternative 2. Triangulation walk
        //

        //Fast but a little more complicated to understand
        //We can also give it a list, which should be empty so we can display the triangulation walk
        public static HalfEdgeFace2 TriangulationWalk(MyVector2 p, HalfEdgeFace2 startTriangle, HalfEdgeData2 triangulationData, List <HalfEdgeFace2> visitedTriangles = null)
        {
            HalfEdgeFace2 intersectingTriangle = null;


            //If we have a triangle to start in which may speed up the algorithm
            HalfEdgeFace2 currentTriangle = null;

            //We can feed it a start triangle to sometimes make the algorithm faster
            if (startTriangle != null)
            {
                currentTriangle = startTriangle;
            }
            //Find a random start triangle which is faster than starting at the first triangle?
            else
            {
                int randomPos = Random.Range(0, triangulationData.faces.Count);

                int i = 0;

                //faces are stored in a hashset so we have to loop through them while counting
                //to find the start triangle
                foreach (HalfEdgeFace2 f in triangulationData.faces)
                {
                    if (i == randomPos)
                    {
                        currentTriangle = f;

                        break;
                    }

                    i += 1;
                }
            }

            if (currentTriangle == null)
            {
                Debug.Log("Couldnt find start triangle when walking in triangulation");

                return(null);
            }

            if (visitedTriangles != null)
            {
                visitedTriangles.Add(currentTriangle);
            }



            //Start the triangulation walk to find the intersecting triangle
            int safety = 0;

            while (true)
            {
                safety += 1;

                if (safety > 1000000)
                {
                    Debug.Log("Stuck in endless loop when walking in triangulation");

                    break;
                }

                //Is the point intersecting with the current triangle?
                //We need to do 3 tests where each test is using the triangles edges
                //If the point is to the right of all edges, then it's inside the triangle
                //If the point is to the left we jump to that triangle instead
                HalfEdge2 e1 = currentTriangle.edge;
                HalfEdge2 e2 = e1.nextEdge;
                HalfEdge2 e3 = e2.nextEdge;


                //Test 1
                if (IsPointToTheRightOrOnLine(e1.prevEdge.v.position, e1.v.position, p))
                {
                    //Test 2
                    if (IsPointToTheRightOrOnLine(e2.prevEdge.v.position, e2.v.position, p))
                    {
                        //Test 3
                        if (IsPointToTheRightOrOnLine(e3.prevEdge.v.position, e3.v.position, p))
                        {
                            //We have found the triangle the point is in
                            intersectingTriangle = currentTriangle;

                            break;
                        }
                        //If to the left, move to this triangle
                        else
                        {
                            currentTriangle = e3.oppositeEdge.face;
                        }
                    }
                    //If to the left, move to this triangle
                    else
                    {
                        currentTriangle = e2.oppositeEdge.face;
                    }
                }
                //If to the left, move to this triangle
                else
                {
                    currentTriangle = e1.oppositeEdge.face;
                }


                if (visitedTriangles != null)
                {
                    visitedTriangles.Add(currentTriangle);
                }
            }


            //Add the last triangle if we found it
            if (visitedTriangles != null && intersectingTriangle != null)
            {
                visitedTriangles.Add(intersectingTriangle);
            }

            return(intersectingTriangle);
        }
        //Create a new triangle face when splitting triangle face
        private static void CreateNewFace(HalfEdge2 e_old, MyVector2 splitPosition, HalfEdgeData2 data, HashSet <HalfEdge2> newEdges)
        {
            //This triangle has the following positons
            MyVector2 p_split = splitPosition;
            MyVector2 p_next  = e_old.prevEdge.v.position;
            MyVector2 p_prev  = e_old.v.position;

            //Create the new stuff
            HalfEdgeVertex2 v_split = new HalfEdgeVertex2(p_split);
            HalfEdgeVertex2 v_next  = new HalfEdgeVertex2(p_next);
            HalfEdgeVertex2 v_prev  = new HalfEdgeVertex2(p_prev);

            //This is the edge that has the same position as the old edge
            HalfEdge2 e_1 = new HalfEdge2(v_prev);
            HalfEdge2 e_2 = new HalfEdge2(v_split);
            HalfEdge2 e_3 = new HalfEdge2(v_next);

            //The new face
            HalfEdgeFace2 f = new HalfEdgeFace2(e_1);


            //Create the connections
            //The new edge e has the same opposite as the old edge
            e_1.oppositeEdge = e_old.oppositeEdge;
            //But the opposite edge needs a new reference to this edge if its not a border
            if (e_1.oppositeEdge != null)
            {
                e_old.oppositeEdge.oppositeEdge = e_1;
            }

            //The other new edges will find the opposite in a loop when we have created all new edges
            newEdges.Add(e_2);
            newEdges.Add(e_3);

            //Create the connections between the edges
            e_1.nextEdge = e_2;
            e_1.prevEdge = e_3;

            e_2.nextEdge = e_3;
            e_2.prevEdge = e_1;

            e_3.nextEdge = e_1;
            e_3.prevEdge = e_2;

            //Each edge needs to connect to a face
            e_1.face = f;
            e_2.face = f;
            e_3.face = f;

            //The vertices need an edge that starts at that point
            v_split.edge = e_3;
            v_next.edge  = e_1;
            v_prev.edge  = e_2;

            //Add them to the lists
            data.faces.Add(f);

            data.edges.Add(e_1);
            data.edges.Add(e_2);
            data.edges.Add(e_3);

            data.vertices.Add(v_split);
            data.vertices.Add(v_next);
            data.vertices.Add(v_prev);
        }
        //
        // 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);
        }
 public HalfEdgeFace2(HalfEdge2 edge)
 {
     this.edge = edge;
 }
        //
        // 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);
        }
Example #18
0
        //
        // Remove the edges that intersects with a constraint by flipping triangles
        //

        //The idea here is that all possible triangulations for a set of points can be found
        //by systematically swapping the diagonal in each convex quadrilateral formed by a pair of triangles
        //So we will test all possible arrangements and will always find a triangulation which includes the constrained edge
        private static List <HalfEdge2> RemoveIntersectingEdges(MyVector2 v_i, MyVector2 v_j, Queue <HalfEdge2> intersectingEdges)
        {
            List <HalfEdge2> newEdges = new List <HalfEdge2>();

            int safety = 0;

            //While some edges still cross the constrained edge, do steps 3.1 and 3.2
            while (intersectingEdges.Count > 0)
            {
                safety += 1;

                if (safety > 100000)
                {
                    Debug.Log("Stuck in infinite loop when fixing constrained edges");

                    break;
                }

                //Step 3.1. Remove an edge from the list of edges that intersects the constrained edge
                HalfEdge2 e = intersectingEdges.Dequeue();

                //The vertices belonging to the two triangles
                MyVector2 v_k   = e.v.position;
                MyVector2 v_l   = e.prevEdge.v.position;
                MyVector2 v_3rd = e.nextEdge.v.position;
                //The vertex belonging to the opposite triangle and isn't shared by the current edge
                MyVector2 v_opposite_pos = e.oppositeEdge.nextEdge.v.position;

                //Step 3.2. If the two triangles don't form a convex quadtrilateral
                //place the edge back on the list of intersecting edges (because this edge cant be flipped)
                //and go to step 3.1
                if (!_Geometry.IsQuadrilateralConvex(v_k, v_l, v_3rd, v_opposite_pos))
                {
                    intersectingEdges.Enqueue(e);

                    continue;
                }
                else
                {
                    //Flip the edge like we did when we created the delaunay triangulation
                    HalfEdgeHelpMethods.FlipTriangleEdge(e);

                    //The new diagonal is defined by the vertices
                    MyVector2 v_m = e.v.position;
                    MyVector2 v_n = e.prevEdge.v.position;

                    //If this new diagonal intersects with the constrained edge, add it to the list of intersecting edges
                    if (IsEdgeCrossingEdge(v_i, v_j, v_m, v_n))
                    {
                        intersectingEdges.Enqueue(e);
                    }
                    //Place it in the list of newly created edges
                    else
                    {
                        newEdges.Add(e);
                    }
                }
            }

            return(newEdges);
        }
        //
        // Flip triangle edge
        //
        //So the edge shared by two triangles is going between the two other vertices originally not part of the edge
        public static void FlipTriangleEdge(HalfEdge2 e)
        {
            //The data we need
            //This edge's triangle edges
            HalfEdge2 e_1 = e;
            HalfEdge2 e_2 = e_1.nextEdge;
            HalfEdge2 e_3 = e_1.prevEdge;
            //The opposite edge's triangle edges
            HalfEdge2 e_4 = e_1.oppositeEdge;
            HalfEdge2 e_5 = e_4.nextEdge;
            HalfEdge2 e_6 = e_4.prevEdge;
            //The 4 vertex positions
            MyVector2 aPos = e_1.v.position;
            MyVector2 bPos = e_2.v.position;
            MyVector2 cPos = e_3.v.position;
            MyVector2 dPos = e_5.v.position;

            //The 6 old vertices, we can use
            HalfEdgeVertex2 a_old          = e_1.v;
            HalfEdgeVertex2 b_old          = e_1.nextEdge.v;
            HalfEdgeVertex2 c_old          = e_1.prevEdge.v;
            HalfEdgeVertex2 a_opposite_old = e_4.prevEdge.v;
            HalfEdgeVertex2 c_opposite_old = e_4.v;
            HalfEdgeVertex2 d_old          = e_4.nextEdge.v;

            //Flip

            //Vertices
            //Triangle 1: b-c-d
            HalfEdgeVertex2 b = b_old;
            HalfEdgeVertex2 c = c_old;
            HalfEdgeVertex2 d = d_old;
            //Triangle 1: b-d-a
            HalfEdgeVertex2 b_opposite = a_opposite_old;

            b_opposite.position = bPos;
            HalfEdgeVertex2 d_opposite = c_opposite_old;

            d_opposite.position = dPos;
            HalfEdgeVertex2 a = a_old;


            //Change half-edge - half-edge connections
            e_1.nextEdge = e_3;
            e_1.prevEdge = e_5;

            e_2.nextEdge = e_4;
            e_2.prevEdge = e_6;

            e_3.nextEdge = e_5;
            e_3.prevEdge = e_1;

            e_4.nextEdge = e_6;
            e_4.prevEdge = e_2;

            e_5.nextEdge = e_1;
            e_5.prevEdge = e_3;

            e_6.nextEdge = e_2;
            e_6.prevEdge = e_4;

            //Half-edge - vertex connection
            e_1.v = b;
            e_2.v = b_opposite;
            e_3.v = c;
            e_4.v = d_opposite;
            e_5.v = d;
            e_6.v = a;

            //Half-edge - face connection
            HalfEdgeFace2 f1 = e_1.face;
            HalfEdgeFace2 f2 = e_4.face;

            e_1.face = f1;
            e_3.face = f1;
            e_5.face = f1;

            e_2.face = f2;
            e_4.face = f2;
            e_6.face = f2;

            //Face - half-edge connection
            f1.edge = e_3;
            f2.edge = e_4;

            //Vertices connection, which should have a reference to a half-edge going away from the vertex
            //Triangle 1: b-c-d
            b.edge = e_3;
            c.edge = e_5;
            d.edge = e_1;
            //Triangle 1: b-d-a
            b_opposite.edge = e_4;
            d_opposite.edge = e_6;
            a.edge          = e_2;

            //Opposite-edges are not changing!
            //And neither are we adding, removing data so we dont need to update the lists with all data
        }