Exemplo n.º 1
0
    //
    // Remove all triangles that are inside the constraint
    //

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

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

        if (trianglesToBeDeleted == null)
        {
            Debug.Log("There are no triangles to delete");

            yield return(null);
        }

        //Delete the triangles
        foreach (HalfEdgeFace2 t in trianglesToBeDeleted)
        {
            HalfEdgeHelpMethods.DeleteTriangleFace(t, triangleData, true);


            //
            // PAUSE AND VISUALIZE
            //

            visualizeController.DisplayMeshMain(triangleData, normalizer);

            yield return(new WaitForSeconds(0.5f));
        }
    }
Exemplo n.º 2
0
    public void DisplayMeshMain(HalfEdgeData2 meshData, Normalizer2 normalizer)
    {
        //UnNormalize and to 3d
        HalfEdgeData3 meshDataUnNormalized_3d = new HalfEdgeData3();


        //We dont want to modify the original data
        //HalfEdgeData2 meshDataUnNormalized = normalizer.UnNormalize(meshData);

        HashSet <HalfEdgeFace2> faces_2d = meshData.faces;

        foreach (HalfEdgeFace2 f in faces_2d)
        {
            MyVector2 p1 = f.edge.v.position;
            MyVector2 p2 = f.edge.nextEdge.v.position;
            MyVector2 p3 = f.edge.nextEdge.nextEdge.v.position;

            p1 = normalizer.UnNormalize(p1);
            p2 = normalizer.UnNormalize(p2);
            p3 = normalizer.UnNormalize(p3);

            meshDataUnNormalized_3d.AddTriangle(p1.ToMyVector3_Yis3D(), p2.ToMyVector3_Yis3D(), p3.ToMyVector3_Yis3D());
        }

        this.meshData = meshDataUnNormalized_3d.faces;

        DisplayMesh(meshDataUnNormalized_3d.faces, displayMeshHere);

        //Normalize again
        //meshData = normalizer.Normalize(meshDataUnNormalized);
    }
Exemplo 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
    }
Exemplo n.º 4
0
    //Generate the mesh from the half-edge data structure, which is called when we have flipped an edge
    public void GenerateMesh(HalfEdgeData2 triangleData_normalized)
    {
        //From half-edge to triangle while unnormalizing
        HashSet <Triangle2> triangles_2d = new HashSet <Triangle2>();

        foreach (HalfEdgeFace2 f in triangleData_normalized.faces)
        {
            //Each face has in this case three edges
            MyVector2 p1 = f.edge.v.position;
            MyVector2 p2 = f.edge.nextEdge.v.position;
            MyVector2 p3 = f.edge.nextEdge.nextEdge.v.position;

            //Unnormalize the point
            p1 = HelpMethods.UnNormalize(p1, normalizingBox, dMax);
            p2 = HelpMethods.UnNormalize(p2, normalizingBox, dMax);
            p3 = HelpMethods.UnNormalize(p3, normalizingBox, dMax);

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

            triangles_2d.Add(t);
        }


        //Make sure the triangles have the correct orientation
        //triangles_2d = HelpMethods.OrientTrianglesClockwise(triangles_2d);


        //From 2d to mesh in one step
        //Mesh displayMesh = _TransformBetweenDataStructures.Triangles2ToMesh(triangles_2d, useCompressedMesh: false);


        //Generate the triangle meshes
        GenerateTriangleMeshes(triangles_2d);
    }
    //
    // Visualz
    //

    //Show triangles
    private void ShowTriangles(HalfEdgeData2 triangles)
    {
        controller.ResetMultiColoredMeshes();

        List <Mesh> meshes = controller.GenerateTriangulationMesh(triangles, shouldUnNormalize: true);

        List <Material> materials = controller.GenerateRandomMaterials(meshes.Count);

        controller.multiColoredMeshes          = meshes;
        controller.multiColoredMeshesMaterials = materials;
    }
Exemplo n.º 6
0
    private void GenerateDelaunay(HashSet <MyVector2> points_2d)
    {
        //Normalize
        AABB2 normalizingBox = new AABB2(new List <MyVector2>(points_2d));

        float dMax = HelpMethods.CalculateDMax(normalizingBox);

        HashSet <MyVector2> points_2d_normalized = HelpMethods.Normalize(points_2d, normalizingBox, dMax);


        //Generate delaunay
        //HalfEdgeData2 delaunayData = _Delaunay.FlippingEdges(points_2d_normalized, new HalfEdgeData2());
        HalfEdgeData2 delaunayData = _Delaunay.PointByPoint(points_2d_normalized, new HalfEdgeData2());


        //UnNormalize
        HalfEdgeData2 triangleData = HelpMethods.UnNormalize(delaunayData, normalizingBox, dMax);

        //From halfedge to triangle
        HashSet <Triangle2> triangles = _TransformBetweenDataStructures.HalfEdge2ToTriangle2(triangleData);

        //Make sure they have the correct orientation
        triangles = HelpMethods.OrientTrianglesClockwise(triangles);

        //2d to 3d
        HashSet <Triangle3> triangles_3d = new HashSet <Triangle3>();


        int counter = -1;

        foreach (Triangle2 t in triangles)
        {
            counter++;

            //if (counter != 2)
            //{
            //    continue;
            //}

            triangles_3d.Add(new Triangle3(t.p1.ToMyVector3_Yis3D(), t.p2.ToMyVector3_Yis3D(), t.p3.ToMyVector3_Yis3D()));

            //Debug.Log($"p1: {t.p1.x} {t.p1.y} p2: {t.p2.x} {t.p2.y} p3: {t.p3.x} {t.p3.y}");

            //MyVector2 circleCenter = _Geometry.CalculateCircleCenter(t.p1, t.p2, t.p3);

            //Debug.Log("Circle center: " + circleCenter.x + " " + circleCenter.y);
        }

        Mesh delaunayMesh = _TransformBetweenDataStructures.Triangle3ToCompressedMesh(triangles_3d);

        //Display the delaunay triangles
        TestAlgorithmsHelpMethods.DisplayMeshEdges(delaunayMesh, Color.black);
    }
Exemplo n.º 7
0
    public void Delaunay_FlipEdges_Visualizer(HashSet <MyVector2> points, HalfEdgeData2 triangleData)
    {
        controller = GetComponent <DelaunayVisualizerController>();

        //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
        StartCoroutine(FlipEdges(triangleData));
    }
    //Remove the supertriangle
    IEnumerator 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);


            //VISUALZ
            ShowTriangles(triangulationData);

            yield return(new WaitForSeconds(controller.pauseTime));
        }


        //VISUALZ - show the colored mesh when its finished
        controller.shouldDisplayColoredMesh = true;

        yield return(null);
    }
    public void StartVisualizer(HashSet <MyVector2> points, HalfEdgeData2 triangleData)
    {
        controller = GetComponent <VisualizerController>();

        //Step 1. Triangulate the points with some algorithm. The result is a convex triangulation
        HashSet <Triangle2> triangles = _TriangulatePoints.VisibleEdgesTriangulation(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);

        //Generate the visual triangles
        controller.GenerateTriangulationMesh(triangleData);

        //Step 3. Flip edges until we have a delaunay triangulation
        StartCoroutine(FlipEdges(triangleData));
    }
    //Generate the mesh from the half-edge data structure, which is called when we have flipped an edge
    public void GenerateTriangulationMesh(HalfEdgeData2 triangleData_normalized)
    {
        //From half-edge to triangle
        HashSet <Triangle2> triangles_2d = new HashSet <Triangle2>();

        foreach (HalfEdgeFace2 f in triangleData_normalized.faces)
        {
            //Each face has in this case three edges
            MyVector2 p1 = f.edge.v.position;
            MyVector2 p2 = f.edge.nextEdge.v.position;
            MyVector2 p3 = f.edge.nextEdge.nextEdge.v.position;

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

            triangles_2d.Add(t);
        }

        GenerateTriangulationMesh(triangles_2d);
    }
Exemplo n.º 11
0
    //
    // Generate meshes
    //

    //Generate list of meshes from the Half-edge data structure
    public List <Mesh> GenerateTriangulationMesh(HalfEdgeData2 triangleData, bool shouldUnNormalize)
    {
        //From half-edge to triangle
        HashSet <Triangle2> triangles_2d = new HashSet <Triangle2>();

        foreach (HalfEdgeFace2 f in triangleData.faces)
        {
            //Each face has in this case three edges
            MyVector2 p1 = f.edge.v.position;
            MyVector2 p2 = f.edge.nextEdge.v.position;
            MyVector2 p3 = f.edge.nextEdge.nextEdge.v.position;

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

            triangles_2d.Add(t);
        }

        List <Mesh> meshes = GenerateTriangulationMesh(triangles_2d, shouldUnNormalize);

        return(meshes);
    }
    public void StartVisualizer(HashSet <MyVector2> points, HalfEdgeData2 triangulationData)
    {
        controller = GetComponent <VisualizerController>();


        //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)
        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);



        //Start the visualization
        StartCoroutine(InsertPoints(points, triangulationData, superTriangle));
    }
    private void GenerateDelaunay(HashSet <MyVector2> points)
    {
        HalfEdgeData2 delaunayData = _Delaunay.FlippingEdges(points, new HalfEdgeData2());

        //From halfedge to triangle
        HashSet <Triangle2> triangles = _TransformBetweenDataStructures.HalfEdge2ToTriangle2(delaunayData);

        //Make sure they have the correct orientation
        triangles = HelpMethods.OrientTrianglesClockwise(triangles);

        //2d to 3d
        HashSet <Triangle3> triangles_3d = new HashSet <Triangle3>();

        foreach (Triangle2 t in triangles)
        {
            triangles_3d.Add(new Triangle3(t.p1.ToMyVector3(), t.p2.ToMyVector3(), t.p3.ToMyVector3()));
        }

        Mesh delaunayMesh = _TransformBetweenDataStructures.Triangle3ToCompressedMesh(triangles_3d);

        //Display the delaunay triangles
        TestAlgorithmsHelpMethods.DisplayMeshEdges(delaunayMesh, Color.black);
    }
Exemplo n.º 14
0
    private void GenerateDelaunay(HashSet <MyVector2> points_2d)
    {
        //Normalize
        AABB2 normalizingBox = new AABB2(new List <MyVector2>(points_2d));

        float dMax = HelpMethods.CalculateDMax(normalizingBox);

        HashSet <MyVector2> points_2d_normalized = HelpMethods.Normalize(points_2d, normalizingBox, dMax);


        //Generate delaunay
        //HalfEdgeData2 delaunayData = _Delaunay.FlippingEdges(points_2d_normalized, new HalfEdgeData2());
        HalfEdgeData2 delaunayData = _Delaunay.PointByPoint(points_2d_normalized, new HalfEdgeData2());


        //UnNormalize
        HalfEdgeData2 triangleData = HelpMethods.UnNormalize(delaunayData, normalizingBox, dMax);

        //From halfedge to triangle
        HashSet <Triangle2> triangles = _TransformBetweenDataStructures.HalfEdge2ToTriangle2(triangleData);

        //Make sure they have the correct orientation
        triangles = HelpMethods.OrientTrianglesClockwise(triangles);

        //2d to 3d
        HashSet <Triangle3> triangles_3d = new HashSet <Triangle3>();

        foreach (Triangle2 t in triangles)
        {
            triangles_3d.Add(new Triangle3(t.p1.ToMyVector3(), t.p2.ToMyVector3(), t.p3.ToMyVector3()));
        }

        Mesh delaunayMesh = _TransformBetweenDataStructures.Triangle3ToCompressedMesh(triangles_3d);

        //Display the delaunay triangles
        TestAlgorithmsHelpMethods.DisplayMeshEdges(delaunayMesh, Color.black);
    }
Exemplo n.º 15
0
    private IEnumerator GenerateConstrainedDelaunayLoop(HashSet <MyVector2> points, List <MyVector2> hull, HashSet <List <MyVector2> > holes, bool shouldRemoveTriangles, HalfEdgeData2 triangleData, Normalizer2 normalizer)
    {
        //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);


        //
        // PAUSE AND VISUALIZE
        //

        visualizeController.DisplayMeshMain(triangleData, normalizer);

        yield return(new WaitForSeconds(3f));



        //Modify the triangulation by adding the constraints to the delaunay triangulation
        yield return(StartCoroutine(AddConstraints(triangleData, hull, shouldRemoveTriangles, normalizer)));

        foreach (List <MyVector2> hole in holes)
        {
            yield return(StartCoroutine(AddConstraints(triangleData, hole, shouldRemoveTriangles, normalizer)));
        }
    }
Exemplo n.º 16
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);
    }
Exemplo 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);
    }
Exemplo n.º 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 IEnumerator RemoveIntersectingEdges(MyVector2 v_i, MyVector2 v_j, Queue <HalfEdge2> intersectingEdges, List <HalfEdge2> newEdges, HalfEdgeData2 triangleData, Normalizer2 normalizer)
    {
        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);


                //
                // PAUSE AND VISUALIZE
                //

                visualizeController.DisplayMeshMain(triangleData, normalizer);

                yield return(new WaitForSeconds(0.5f));


                //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);
                }
            }
        }
    }
Exemplo n.º 19
0
    public void GenerateTriangulation()
    {
        //Get the random points
        //HashSet<Vector3> randomPoints = TestAlgorithmsHelpMethods.GenerateRandomPoints(seed, halfMapSize, numberOfPoints);

        //From 3d to 2d
        //HashSet<MyVector2> randomPoints_2d = new HashSet<MyVector2>(randomPoints.Select(x => x.ToMyVector2()));

        /*
         * List<MyVector2> constraints_2d = constraints.Select(x => x.ToMyVector2()).ToList();
         *
         * //Normalize to range 0-1
         * //We should use all points, including the constraints because the hole may be outside of the random points
         * List<MyVector2> allPoints = new List<MyVector2>();
         *
         * allPoints.AddRange(new List<MyVector2>(points_2d));
         * allPoints.AddRange(constraints_2d);
         *
         * AABB2 normalizingBox = new AABB2(new List<MyVector2>(points_2d));
         *
         * float dMax = HelpMethods.CalculateDMax(normalizingBox);
         *
         * HashSet<MyVector2> points_2d_normalized = HelpMethods.Normalize(points_2d, normalizingBox, dMax);
         *
         * List<MyVector2> constraints_2d_normalized = HelpMethods.Normalize(constraints_2d, normalizingBox, dMax);
         */


        //Hull
        List <Vector3> hullPoints = TestAlgorithmsHelpMethods.GetPointsFromParent(hullConstraintParent);

        List <MyVector2> hullPoints_2d = hullPoints.Select(x => x.ToMyVector2()).ToList();;

        //Holes
        HashSet <List <MyVector2> > allHolePoints_2d = new HashSet <List <MyVector2> >();

        foreach (Transform holeParent in holeConstraintParents)
        {
            List <Vector3> holePoints = TestAlgorithmsHelpMethods.GetPointsFromParent(holeParent);

            if (holePoints != null)
            {
                List <MyVector2> holePoints_2d = holePoints.Select(x => x.ToMyVector2()).ToList();

                allHolePoints_2d.Add(holePoints_2d);
            }
        }


        //Normalize to range 0-1
        //We should use all points, including the constraints because the hole may be outside of the random points
        List <MyVector2> allPoints = new List <MyVector2>();

        //allPoints.AddRange(randomPoints_2d);

        allPoints.AddRange(hullPoints_2d);

        foreach (List <MyVector2> hole in allHolePoints_2d)
        {
            allPoints.AddRange(hole);
        }

        AABB2 normalizingBox = new AABB2(allPoints);

        float dMax = HelpMethods.CalculateDMax(normalizingBox);

        List <MyVector2> hullPoints_2d_normalized = HelpMethods.Normalize(hullPoints_2d, normalizingBox, dMax);

        HashSet <List <MyVector2> > allHolePoints_2d_normalized = new HashSet <List <MyVector2> >();

        foreach (List <MyVector2> hole in allHolePoints_2d)
        {
            List <MyVector2> hole_normalized = HelpMethods.Normalize(hole, normalizingBox, dMax);

            allHolePoints_2d_normalized.Add(hole_normalized);
        }



        //
        // Generate the triangulation
        //

        //Algorithm 1. Delaunay by triangulate all points with some bad algorithm and then flip edges until we get a delaunay triangulation
        //HalfEdgeData2 triangleData_normalized = _Delaunay.FlippingEdges(points_2d_normalized, new HalfEdgeData2());


        //Algorithm 2. Delaunay by inserting point-by-point while flipping edges after inserting a single point
        //HalfEdgeData2 triangleData_normalized = _Delaunay.PointByPoint(points_2d_normalized, new HalfEdgeData2());


        //Algorithm 3. Constrained delaunay
        HalfEdgeData2 triangleData_normalized = _Delaunay.ConstrainedBySloan(null, hullPoints_2d_normalized, allHolePoints_2d_normalized, shouldRemoveTriangles: true, new HalfEdgeData2());



        //UnNormalize
        HalfEdgeData2 triangleData = HelpMethods.UnNormalize(triangleData_normalized, normalizingBox, dMax);

        //From half-edge to triangle
        HashSet <Triangle2> triangles_2d = _TransformBetweenDataStructures.HalfEdge2ToTriangle2(triangleData);

        //From triangulation to mesh

        //Make sure the triangles have the correct orientation
        triangles_2d = HelpMethods.OrientTrianglesClockwise(triangles_2d);

        //From 2d to 3d
        HashSet <Triangle3> triangles_3d = new HashSet <Triangle3>();

        foreach (Triangle2 t in triangles_2d)
        {
            triangles_3d.Add(new Triangle3(t.p1.ToMyVector3_Yis3D(), t.p2.ToMyVector3_Yis3D(), t.p3.ToMyVector3_Yis3D()));
        }

        triangulatedMesh = _TransformBetweenDataStructures.Triangle3ToCompressedMesh(triangles_3d);
    }
    IEnumerator InsertPoints(HashSet <MyVector2> points, HalfEdgeData2 triangulationData, Triangle2 superTriangle)
    {
        //VISUALZ
        ShowTriangles(triangulationData);

        //VISUALZ - dont show the colored mesh until its finished because its flickering
        controller.shouldDisplayColoredMesh = false;

        yield return(new WaitForSeconds(controller.pauseTime));


        //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. 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);


            //VISUALZ
            //Display the point as a black circle
            ShowCircle(p);

            yield return(new WaitForSeconds(controller.pauseTime));

            ShowTriangles(triangulationData);

            yield return(new WaitForSeconds(controller.pauseTime));


            //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;

                    //VISUALZ
                    controller.flipText.text = "Flipped edges: " + flippedEdges;

                    ShowTriangles(triangulationData);

                    yield return(new WaitForSeconds(controller.pauseTime));
                }
            }
        }


        //Dont show the last point we added
        controller.ResetBlackMeshes();


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

        yield return(null);
    }
Exemplo n.º 21
0
    public void GenererateTriangulation()
    {
        //Get the random points
        HashSet <Vector3> points = TestAlgorithmsHelpMethods.GenerateRandomPoints(seed, halfMapSize, numberOfPoints);


        //From 3d to 2d
        HashSet <MyVector2> points_2d = new HashSet <MyVector2>();

        foreach (Vector3 v in points)
        {
            points_2d.Add(v.ToMyVector2());
        }

        List <MyVector2> constraints_2d = new List <MyVector2>();

        foreach (Vector3 v in constraints)
        {
            constraints_2d.Add(v.ToMyVector2());
        }

        //Normalize to range 0-1
        //We should use all points, including the constraints
        List <MyVector2> allPoints = new List <MyVector2>();

        allPoints.AddRange(new List <MyVector2>(points_2d));
        allPoints.AddRange(constraints_2d);

        AABB2 normalizingBox = new AABB2(new List <MyVector2>(points_2d));

        float dMax = HelpMethods.CalculateDMax(normalizingBox);

        HashSet <MyVector2> points_2d_normalized = HelpMethods.Normalize(points_2d, normalizingBox, dMax);

        List <MyVector2> constraints_2d_normalized = HelpMethods.Normalize(constraints_2d, normalizingBox, dMax);



        //
        // Generate the triangulation
        //

        //Algorithm 1. Delaunay by triangulate all points with some bad algorithm and then flip edges until we get a delaunay triangulation
        //HalfEdgeData2 triangleData_normalized = _Delaunay.FlippingEdges(points_2d_normalized, new HalfEdgeData2());


        //Algorithm 2. Delaunay by inserting point-by-point while flipping edges after inserting a single point
        //HalfEdgeData2 triangleData_normalized = _Delaunay.PointByPoint(points_2d_normalized, new HalfEdgeData2());


        //Algorithm 3. Constrained delaunay
        HalfEdgeData2 triangleData_normalized = _Delaunay.ConstrainedBySloan(points_2d_normalized, constraints_2d_normalized, false, new HalfEdgeData2());



        //UnNormalize
        HalfEdgeData2 triangleData = HelpMethods.UnNormalize(triangleData_normalized, normalizingBox, dMax);

        //From half-edge to triangle
        HashSet <Triangle2> triangles_2d = _TransformBetweenDataStructures.HalfEdge2ToTriangle2(triangleData);

        //From triangulation to mesh

        //Make sure the triangles have the correct orientation
        triangles_2d = HelpMethods.OrientTrianglesClockwise(triangles_2d);

        //From 2d to 3d
        HashSet <Triangle3> triangles_3d = new HashSet <Triangle3>();

        foreach (Triangle2 t in triangles_2d)
        {
            triangles_3d.Add(new Triangle3(t.p1.ToMyVector3(), t.p2.ToMyVector3(), t.p3.ToMyVector3()));
        }

        triangulatedMesh = _TransformBetweenDataStructures.Triangle3ToCompressedMesh(triangles_3d);
    }
Exemplo n.º 22
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 IEnumerator RestoreDelaunayTriangulation(MyVector2 c_p1, MyVector2 c_p2, List <HalfEdge2> newEdges, HalfEdgeData2 triangleData, Normalizer2 normalizer)
    {
        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;


                    //
                    // PAUSE AND VISUALIZE
                    //

                    visualizeController.DisplayMeshMain(triangleData, normalizer);

                    yield return(new WaitForSeconds(0.5f));
                }
            }

            //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;
            }
        }
    }
    //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;
        }
    }
    //Flip edges until we get a delaunay triangulation
    private IEnumerator 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;

                //If we want to display the test circle
                //controller.GenerateDelaunayCircleMeshes(a, b, c, d);

                //yield return new WaitForSeconds(controller.pauseTime);

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

                    hasFlippedEdge = true;

                    HalfEdgeHelpMethods.FlipTriangleEdge(thisEdge);

                    controller.flipText.text = "Flipped edges: " + flippedEdges;

                    controller.GenerateTriangulationMesh(triangleData);

                    yield return(new WaitForSeconds(controller.pauseTime));
                }
            }

            //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 circle meshes so we see that we are finished
        controller.ClearBlackMeshes();

        yield return(null);
    }
Exemplo n.º 25
0
    //
    // Add the constraints to the delaunay triangulation
    //

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



        //
        // PAUSE AND VISUALIZE
        //

        //Show the constraint with a line mesh
        HashSet <Triangle2> lineTriangles = _GenerateMesh.ConnectedLineSegments(constraints, width: 0.01f, isConnected: true);

        //UnNormalized and to half-edge 3 (also move each vertex up a little or will intersect with the underlying mesh)
        HalfEdgeData3 lineData = new HalfEdgeData3();

        foreach (Triangle2 t in lineTriangles)
        {
            MyVector2 p1 = t.p1;
            MyVector2 p2 = t.p2;
            MyVector2 p3 = t.p3;

            p1 = normalizer.UnNormalize(p1);
            p2 = normalizer.UnNormalize(p2);
            p3 = normalizer.UnNormalize(p3);

            lineData.AddTriangle(p1.ToMyVector3_Yis3D(0.1f), p2.ToMyVector3_Yis3D(0.1f), p3.ToMyVector3_Yis3D(0.1f));
        }

        visualizeController.DisplayMeshOtherUnNormalized(lineData.faces);

        yield return(new WaitForSeconds(2f));



        //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 = new List <HalfEdge2>();

            yield return(StartCoroutine(RemoveIntersectingEdges(c_p1, c_p2, intersectingEdges, newEdges, triangleData, normalizer)));

            //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();
            yield return(StartCoroutine(RestoreDelaunayTriangulation(c_p1, c_p2, newEdges, triangleData, normalizer)));
            //timer.Stop();
        }

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

        //return triangleData;



        //
        // PAUSE AND VISUALIZE
        //

        visualizeController.HideMeshOther();

        yield return(new WaitForSeconds(2f));
    }