Exemple #1
0
        public static UnityEngine.Mesh Mesh(this Polygon this_, string name = "")
        {
            // Create geometry.
            InputGeometry geometry = this_.InputGeometry();

            // Triangulate.
            TriangleNet.Mesh triangulatedMesh = new TriangleNet.Mesh();
            triangulatedMesh.Triangulate(geometry);

            // Counts.
            int vertexCount   = triangulatedMesh.vertices.Count;
            int triangleCount = triangulatedMesh.triangles.Count;

            // Debug.Log("Mesh.vertexCount ("+vertexCount+")"); // NumberOfInputPoints
            // Debug.Log("Mesh.triangleCount ("+triangleCount+")"); // NumberOfInputPoints

            // Mesh store.
            Vector3[] _vertices  = new Vector3[vertexCount];
            Vector2[] _uv        = new Vector2[vertexCount];
            Vector3[] _normals   = new Vector3[vertexCount];
            int[]     _triangles = new int[triangleCount * 3];

            foreach (KeyValuePair <int, TriangleNet.Data.Vertex> eachEntry in triangulatedMesh.vertices)
            {
                int index = eachEntry.Key;
                TriangleNet.Data.Vertex eachVertex = eachEntry.Value;

                _vertices[index] = new Vector3(
                    (float)eachVertex.x,
                    (float)eachVertex.y,
                    0.0f                     // As of 2D
                    );

                _uv[index]      = _vertices[index];
                _normals[index] = Vector3.forward;
            }

            int cursor = 0;

            foreach (KeyValuePair <int, TriangleNet.Data.Triangle> eachPair in triangulatedMesh.triangles)
            {
                TriangleNet.Data.Triangle eachTriangle = eachPair.Value;
                _triangles[cursor]     = eachTriangle.P2;
                _triangles[cursor + 1] = eachTriangle.P1;
                _triangles[cursor + 2] = eachTriangle.P0;
                cursor += 3;
            }

            // Create / setup mesh.
            Mesh mesh = new Mesh();

            mesh.vertices     = _vertices;
            mesh.uv           = _uv;
            mesh.normals      = _normals;
            mesh.subMeshCount = 1;
            mesh.SetTriangles(_triangles, 0);
            mesh.name = name;

            return(mesh);
        }
Exemple #2
0
        /// <summary>
        /// Find the abutting triangle; same edge. [sym(abc) -> ba*]
        /// </summary>
        public void SymSelf()
        {
            //this = tri.triangles[orient];
            // decode(ptr, otri);

            int tmp = orient;
            orient = triangle.neighbors[tmp].orient;
            triangle = triangle.neighbors[tmp].triangle;
        }
Exemple #3
0
    public static Polygon GetPolygon(ref TriangleNet.Data.Triangle triangle)
    {
        List<Coordinate> coords = new List<Coordinate>();
        Vertex v1 = triangle.GetVertex(0);
        coords.Add(new Coordinate(v1.X, v1.Y));
        v1 = triangle.GetVertex(1);
        coords.Add(new Coordinate(v1.X, v1.Y));
        v1 = triangle.GetVertex(2);
        coords.Add(new Coordinate(v1.X, v1.Y));

        return new Polygon(coords);
    }
Exemple #4
0
    public List<Polygon> GetPolygons()
    {
        List<Polygon> polygons = new List<Polygon>();

        foreach (var ts in mesh.Triangles)
        {
            TriangleNet.Data.Triangle tr = ts;

            polygons.Add(GetPolygon(ref tr));
        }
        
        return polygons;
    }
Exemple #5
0
    public static List<Point> GetTriangleAsPoints(ref TriangleNet.Data.Triangle triangle)
    {
        Vertex v1 = triangle.GetVertex(0);
        Vertex v2 = triangle.GetVertex(1);
        Vertex v3 = triangle.GetVertex(2);

        List<Point> points = new List<Point>();

        points.Add(new Point(v1.X, v1.Y, v1.Attributes[0]));
        points.Add(new Point(v2.X, v2.Y, v2.Attributes[0]));
        points.Add(new Point(v3.X, v3.Y, v3.Attributes[0]));

        return points;
    }
Exemple #6
0
    public static bool Contains(ref TriangleNet.Data.Triangle triangle, double x, double y)
    {
        Vertex v1 = triangle.GetVertex(0);
        Vertex v2 = triangle.GetVertex(1);
        Vertex v3 = triangle.GetVertex(2);
        Vertex pt = new Vertex(x, y);
        bool b1, b2, b3;

        b1 = Sign(ref pt, ref v1, ref v2) < 0.0f;
        b2 = Sign(ref pt, ref v2, ref v3) < 0.0f;
        b3 = Sign(ref pt, ref v3, ref v1) < 0.0f;

        return ((b1 == b2) && (b2 == b3));
    }
Exemple #7
0
    public int FindTriangleThatContains(double x, double y)
    {
        for (int i = 0; i < triangles.Count; i++)
        {
            TriangleNet.Data.Triangle ts = triangles[i];

            if (Contains(ref ts, x, y))
            {
                return i;
            }
        }

        return -1;
    }
Exemple #8
0
    public double GetZ(int index, double x, double y)
    {
        if (index < triangles.Count)
        {
            TriangleNet.Data.Triangle tr = triangles[index];

            if (Contains(ref tr, x, y))
            {
                Point normal = triangleNormals[index];
                double d = trianglePlaneEquationDs[index];
                double value = (normal.X * x + normal.Y * y + d) / -normal.Z;
                return value;
            }
        }

        return double.MinValue;
    }
Exemple #9
0
    public double GetZ(double x, double y)
    {
        double value = -99999999999999999.00;
        int count = 0;


        foreach (var ts in triangles)
        {
            TriangleNet.Data.Triangle tr = ts;

            if (Contains(ref tr, x, y))
            {
                Point normal = triangleNormals[count];
                double d = trianglePlaneEquationDs[count];
                return (normal.X * x + normal.Y * y + d) / -normal.Z;
            }
        }

        return value;
    }
        /// <summary>
        /// Process all trianlges connected to given triangle and apply given action.
        /// </summary>
        public void Process(Triangle triangle, Action<Triangle> func)
        {
            if (triangle != Mesh.dummytri)
            {
                // Make sure the triangle under consideration still exists.
                // It may have been eaten by the virus.
                if (!Otri.IsDead(triangle))
                {
                    // Put one triangle in the virus pool.
                    triangle.infected = true;
                    viri.Add(triangle);
                    // Apply one region's attribute and/or area constraint.
                    ProcessRegion(func);
                    // The virus pool should be empty now.
                }
            }

            // Free up memory (virus pool should be empty anyway).
            viri.Clear();
        }
Exemple #11
0
    void OnDrawGizmos()
    {
        if (holes.Length <= 0)
        {
            return;
        }

        Gizmos.color = Color.black;
        logoOutline.GizmoDraw();

        Gizmos.color = Color.white;
        foreach (UPolygon hole in holes)
        {
            hole.GizmoDraw();
        }

        if (meshRepresentation == null)
        {
            return;
        }

        Gizmos.color = Color.cyan;

        foreach (KeyValuePair <int, TriangleNet.Data.Triangle> pair in meshRepresentation.triangles)
        {
            TriangleNet.Data.Triangle triangle = pair.Value;

            TriangleNet.Data.Vertex vertex0 = triangle.GetVertex(0);
            TriangleNet.Data.Vertex vertex1 = triangle.GetVertex(1);
            TriangleNet.Data.Vertex vertex2 = triangle.GetVertex(2);

            Vector2 p0 = new Vector2((float)vertex0.x, (float)vertex0.y);
            Vector2 p1 = new Vector2((float)vertex1.x, (float)vertex1.y);
            Vector2 p2 = new Vector2((float)vertex2.x, (float)vertex2.y);

            Gizmos.DrawLine(p0, p1);
            Gizmos.DrawLine(p1, p2);
            Gizmos.DrawLine(p2, p0);
        }
    }
Exemple #12
0
        /// <summary>
        /// Initialize the triangle that fills "outer space" and the omnipresent subsegment.
        /// </summary>
        /// <remarks>
        /// The triangle that fills "outer space," called 'dummytri', is pointed to
        /// by every triangle and subsegment on a boundary (be it outer or inner) of
        /// the triangulation. Also, 'dummytri' points to one of the triangles on
        /// the convex hull (until the holes and concavities are carved), making it
        /// possible to find a starting triangle for point location.
        //
        /// The omnipresent subsegment, 'dummysub', is pointed to by every triangle
        /// or subsegment that doesn't have a full complement of real subsegments
        /// to point to.
        //
        /// 'dummytri' and 'dummysub' are generally required to fulfill only a few
        /// invariants: their vertices must remain NULL and 'dummytri' must always
        /// be bonded (at offset zero) to some triangle on the convex hull of the
        /// mesh, via a boundary edge. Otherwise, the connections of 'dummytri' and
        /// 'dummysub' may change willy-nilly.  This makes it possible to avoid
        /// writing a good deal of special-case code (in the edge flip, for example)
        /// for dealing with the boundary of the mesh, places where no subsegment is
        /// present, and so forth.  Other entities are frequently bonded to
        /// 'dummytri' and 'dummysub' as if they were real mesh entities, with no
        /// harm done.
        /// </remarks>
        private void DummyInit()
        {
            // Set up 'dummytri', the 'triangle' that occupies "outer space."
            dummytri = new Triangle();
            dummytri.hash = -1;
            dummytri.id = -1;

            // Initialize the three adjoining triangles to be "outer space." These
            // will eventually be changed by various bonding operations, but their
            // values don't really matter, as long as they can legally be
            // dereferenced.
            dummytri.neighbors[0].triangle = dummytri;
            dummytri.neighbors[1].triangle = dummytri;
            dummytri.neighbors[2].triangle = dummytri;

            if (behavior.useSegments)
            {
                // Set up 'dummysub', the omnipresent subsegment pointed to by any
                // triangle side or subsegment end that isn't attached to a real
                // subsegment.
                dummysub = new Segment();
                dummysub.hash = -1;

                // Initialize the two adjoining subsegments to be the omnipresent
                // subsegment. These will eventually be changed by various bonding
                // operations, but their values don't really matter, as long as they
                // can legally be dereferenced.
                dummysub.subsegs[0].seg = dummysub;
                dummysub.subsegs[1].seg = dummysub;

                // Initialize the three adjoining subsegments of 'dummytri' to be
                // the omnipresent subsegment.
                dummytri.subsegs[0].seg = dummysub;
                dummytri.subsegs[1].seg = dummysub;
                dummytri.subsegs[2].seg = dummysub;
            }
        }
Exemple #13
0
 /// <summary>
 /// Deallocate space for a triangle, marking it dead.
 /// </summary>
 /// <param name="dyingtriangle"></param>
 internal void TriangleDealloc(Triangle dyingtriangle)
 {
     // Mark the triangle as dead. This makes it possible to detect dead
     // triangles when traversing the list of all triangles.
     Otri.Kill(dyingtriangle);
     triangles.Remove(dyingtriangle.hash);
 }
Exemple #14
0
        /// <summary>
        /// Create a new triangle with orientation zero.
        /// </summary>
        /// <param name="newotri">Reference to the new triangle.</param>
        internal void MakeTriangle(ref Otri newotri)
        {
            Triangle tri = new Triangle();
            tri.hash = this.hash_tri++;
            tri.id = tri.hash;

            newotri.triangle = tri;
            newotri.orient = 0;

            triangles.Add(tri.hash, tri);
        }
    public static void CreateMesh(GameObject meshContainer, string path, string path2, string adfname)
    {
        var subgraphs = _LoadMarkerToSubgraph(path, path2);

        meshContainer.name = adfname + "Mesh";

        InputGeometry geometry = new InputGeometry();

        TriangleNet.Mesh meshRepresentation;
        MeshFilter       mf = meshContainer.GetComponent <MeshFilter> () as MeshFilter;
        Mesh             mesh;
        float            zOffset = 0.1f;

        float[] maxArea = new float[2] {
            0, 0
        };

        List <List <Point> > clusters = new List <List <Point> > ();

        foreach (Graph <Node> s in subgraphs)
        {
            List <Point> points = new List <Point> ();
            foreach (Node n in s.Nodes)
            {
                Point tmp = new Point(n.position.x, n.position.z);
                zOffset = n.position.y;
                points.Add(tmp);
            }
            if (points.Count > 2)
            {
                clusters.Add(points);

                // Calculate areas of the clusters. The largest is the external ring, while the others must be holes.
                float num = CalculateArea(points);
                if (num > maxArea [0])
                {
                    maxArea [0] = num;
                    maxArea [1] = clusters.Count - 1;
                }
            }
            Debug.Log(clusters [clusters.Count - 1].Count);
        }

        geometry.AddRing(clusters[(int)maxArea[1]]);
        clusters.RemoveAt((int)maxArea [1]);

        foreach (List <Point> c in clusters)
        {
            geometry.AddRingAsHole(c);
        }
        meshRepresentation = new TriangleNet.Mesh();
        meshRepresentation.Triangulate(geometry);

        //		Dictionary<int, float> zOffsets = new Dictionary<int, float>();
        //
        //		foreach(KeyValuePair<int, TriangleNet.Data.Vertex> pair in meshRepresentation.vertices)
        //		{
        //			zOffsets.Add(pair.Key, Random.RandomRange(-zOffset, zOffset));
        //		}

        int            triangleIndex   = 0;
        List <Vector3> vertices        = new List <Vector3>(meshRepresentation.triangles.Count * 3);
        List <int>     triangleIndices = new List <int>(meshRepresentation.triangles.Count * 3);

        foreach (KeyValuePair <int, TriangleNet.Data.Triangle> pair in meshRepresentation.triangles)
        {
            TriangleNet.Data.Triangle triangle = pair.Value;

            TriangleNet.Data.Vertex vertex0 = triangle.GetVertex(0);
            TriangleNet.Data.Vertex vertex1 = triangle.GetVertex(1);
            TriangleNet.Data.Vertex vertex2 = triangle.GetVertex(2);

            Vector3 p0 = new Vector3(vertex0.x, zOffset, vertex0.y);
            Vector3 p1 = new Vector3(vertex1.x, zOffset, vertex1.y);
            Vector3 p2 = new Vector3(vertex2.x, zOffset, vertex2.y);

            //			Vector3 p0 = new Vector3( vertex0.x, vertex0.y, zOffsets[vertex0.id]);
            //			Vector3 p1 = new Vector3( vertex1.x, vertex1.y, zOffsets[vertex1.id]);
            //			Vector3 p2 = new Vector3( vertex2.x, vertex2.y, zOffsets[vertex2.id]);

            vertices.Add(p0);
            vertices.Add(p1);
            vertices.Add(p2);

            triangleIndices.Add(triangleIndex + 2);
            triangleIndices.Add(triangleIndex + 1);
            triangleIndices.Add(triangleIndex);

            triangleIndex += 3;
        }
        mesh           = new Mesh();
        mesh.vertices  = vertices.ToArray();
        mesh.triangles = triangleIndices.ToArray();
        mesh.RecalculateNormals();
        //GetComponent<MeshFilter>().mesh = mesh;
        mf.mesh = mesh;

        Exporter e = meshContainer.GetComponent <Exporter> () as Exporter;

        e.DoExport(true);
//
//		File.WriteAllBytes(Application.persistentDataPath + "/" + adfname + "Mesh", MeshSerializer.WriteMesh (mesh, true));

        meshContainer.SetActive(true);
        meshContainer.GetComponent <NavMeshSurface> ().BuildNavMesh();

        Debug.Log("NavMesh created");

        //MeshSaverEditor.SaveMesh (mesh, "meshtest", true, true);

        //ObjExporterScript.MeshToString(GetComponent<MeshFilter>(),
    }
Exemple #16
0
 /// <summary>
 /// Set the region attribute of all trianlges connected to given triangle.
 /// </summary>
 public void Process(Triangle triangle)
 {
     // Default action is to just set the region id for all trianlges.
     this.Process(triangle, (tri) => { tri.region = triangle.region; });
 }
Exemple #17
0
    public Mesh TriangulateMesh(Vector3[] Vertices)
    {
        if (Vertices.Length < 3)
        {
            return(null);
        }

        geometry = new InputGeometry();

        foreach (Vector3 Vert in Vertices)
        {
            geometry.AddPoint(Vert.x, Vert.y);
        }

        List <Point> points = new List <Point>();

        for (float offsetX = -distance; offsetX < distance; offsetX += boxDistance)
        {
            for (float offsetY = -verticalDistance; offsetY < verticalDistance; offsetY += boxDistance)
            {
                Vector2 offset = new Vector2(offsetX, offsetY) + Vector2.one * boxDistance * 0.5f;

                float radians = Random.Range(0, 2 * Mathf.PI);
                float length  = Random.Range(0, circleDistance);

                Vector2 pos = new Vector2(Mathf.Cos(radians), Mathf.Sin(radians)) * length;
                pos += offset;
            }
        }

        meshRepresentation = new TriangleNet.Mesh();
        meshRepresentation.Triangulate(geometry);

        //generate mesh based on triangulation

        Dictionary <int, float> zOffsets = new Dictionary <int, float>();

        foreach (KeyValuePair <int, TriangleNet.Data.Vertex> pair in meshRepresentation.vertices)
        {
            zOffsets.Add(pair.Key, Random.Range(-zOffset, zOffset));
        }

        int            triangleIndex   = 0;
        List <Vector3> vertices        = new List <Vector3>(meshRepresentation.triangles.Count * 3);
        List <int>     triangleIndices = new List <int>(meshRepresentation.triangles.Count * 3);

        foreach (KeyValuePair <int, TriangleNet.Data.Triangle> pair in meshRepresentation.triangles)
        {
            TriangleNet.Data.Triangle triangle = pair.Value;

            TriangleNet.Data.Vertex vertex0 = triangle.GetVertex(0);
            TriangleNet.Data.Vertex vertex1 = triangle.GetVertex(1);
            TriangleNet.Data.Vertex vertex2 = triangle.GetVertex(2);

            Vector3 p0 = new Vector3(vertex0.x, vertex0.y, zOffsets[vertex0.id]);
            Vector3 p1 = new Vector3(vertex1.x, vertex1.y, zOffsets[vertex1.id]);
            Vector3 p2 = new Vector3(vertex2.x, vertex2.y, zOffsets[vertex2.id]);

            vertices.Add(p0);
            vertices.Add(p1);
            vertices.Add(p2);

            triangleIndices.Add(triangleIndex + 2);
            triangleIndices.Add(triangleIndex + 1);
            triangleIndices.Add(triangleIndex);

            triangleIndex += 3;
        }

        mesh           = new Mesh();
        mesh.name      = "Triangulated Terrain";
        mesh.vertices  = vertices.ToArray();
        mesh.triangles = triangleIndices.ToArray();

        return(mesh);
    }
Exemple #18
0
    // Use this for initialization
    void Start()
    {
        geometry = new InputGeometry();

        List <Point> shape = new List <Point> ();

        shape.Add(new Point(0f, 0f));
        shape.Add(new Point(2f, 2f));
        shape.Add(new Point(1f, 4f));
        shape.Add(new Point(3f, 5f));
        shape.Add(new Point(-3f, 5f));
        shape.Add(new Point(-1f, 4f));
        shape.Add(new Point(-2f, 2f));
        geometry.AddRing(shape);

        //it is necessary to put a border around all the points in order to get triangulation to work correctly when holes are used
//        List<Point> border = new List<Point>();
//        border.Add(new Point(distance, verticalDistance));
//        border.Add(new Point(distance, -verticalDistance));
//        border.Add(new Point(-distance, -verticalDistance));
//        border.Add(new Point(-distance, verticalDistance));
//        geometry.AddRing(border);

//        List<Point> outlinePoints = new List<Point>(logoOutline.points.Length);
//        foreach (Vector2 coordinates in logoOutline.points)
//        {
//            outlinePoints.Add(new Point(coordinates));
//        }
//
//        geometry.AddRingAsHole(outlinePoints, 0);
//
//
//        foreach(UPolygon hole in holes)
//        {
//            List<Point> holePoints = new List<Point>(hole.points.Length);
//
//            foreach (Vector2 coordinates in hole.points)
//                holePoints.Add(new Point(coordinates));
//
//            geometry.AddRing(holePoints, 0);
//        }


//        List<Point> points = new List<Point>();
//        for (float offsetX = -distance; offsetX < distance; offsetX += boxDistance)
//        {
//            for (float offsetY = -verticalDistance; offsetY < verticalDistance; offsetY += boxDistance)
//            {
//                Vector2 offset = new Vector2(offsetX, offsetY) + Vector2.one * boxDistance * 0.5f;
//
//                float radians = Random.RandomRange(0, 2 * Mathf.PI);
//                float length = Random.RandomRange(0, circleDistance);
//
//                Vector2 pos = new Vector2(Mathf.Cos(radians), Mathf.Sin(radians)) * length;
//                pos += offset;
//
//                bool insideOutline = logoOutline.PointInPolygon(pos);
//
//                bool stillAlloved = false;
//                for (int i = 0; i < holes.Length; i++ )
//                {
//                    if (holes[i].PointInPolygon(pos))
//                        stillAlloved = true;
//                }
//
//                if (!insideOutline || stillAlloved)
//                    geometry.AddPoint((float)pos.x, (float)pos.y, 0);
//            }
//        }

        meshRepresentation = new TriangleNet.Mesh();
        meshRepresentation.Triangulate(geometry);

        //generate mesh based on triangulation

        Dictionary <int, float> zOffsets = new Dictionary <int, float>();

//        foreach(KeyValuePair<int, TriangleNet.Data.Vertex> pair in meshRepresentation.vertices)
//        {
//            zOffsets.Add(pair.Key, Random.RandomRange(-zOffset, zOffset));
//        }

        int            triangleIndex   = 0;
        List <Vector3> vertices        = new List <Vector3>(meshRepresentation.triangles.Count * 3);
        List <int>     triangleIndices = new List <int>(meshRepresentation.triangles.Count * 3);

        foreach (KeyValuePair <int, TriangleNet.Data.Triangle> pair in meshRepresentation.triangles)
        {
            TriangleNet.Data.Triangle triangle = pair.Value;

            TriangleNet.Data.Vertex vertex0 = triangle.GetVertex(0);
            TriangleNet.Data.Vertex vertex1 = triangle.GetVertex(1);
            TriangleNet.Data.Vertex vertex2 = triangle.GetVertex(2);

            Vector3 p0 = new Vector3(vertex0.x, vertex0.y, 0);
            Vector3 p1 = new Vector3(vertex1.x, vertex1.y, 0);
            Vector3 p2 = new Vector3(vertex2.x, vertex2.y, 0);

            vertices.Add(p0);
            vertices.Add(p1);
            vertices.Add(p2);

            triangleIndices.Add(triangleIndex + 2);
            triangleIndices.Add(triangleIndex + 1);
            triangleIndices.Add(triangleIndex);

            triangleIndex += 3;
        }

        mesh           = new Mesh();
        mesh.vertices  = vertices.ToArray();
        mesh.triangles = triangleIndices.ToArray();
        mesh.RecalculateNormals();
        GetComponent <MeshFilter>().mesh = mesh;
    }
Exemple #19
0
 /// <summary>
 /// Set a triangle's deallocation.
 /// </summary>
 public static void Kill(Triangle tria)
 {
     tria.neighbors[0].triangle = null;
     tria.neighbors[2].triangle = null;
 }
Exemple #20
0
 /// <summary>
 /// Check a triangle's deallocation.
 /// </summary>
 public static bool IsDead(Triangle tria)
 {
     return tria.neighbors[0].triangle == null;
 }
Exemple #21
0
        /// <summary>
        /// Find the previous edge (clockwise) of the adjacent triangle. [rprev(abc) -> b**]
        /// </summary>
        public void RprevSelf()
        {
            //SymSelf();
            int tmp = orient;
            orient = triangle.neighbors[tmp].orient;
            triangle = triangle.neighbors[tmp].triangle;

            //LprevSelf();
            orient = minus1Mod3[orient];

            //SymSelf();
            tmp = orient;
            orient = triangle.neighbors[tmp].orient;
            triangle = triangle.neighbors[tmp].triangle;
        }
Exemple #22
0
        /// <summary>
        /// Find the next edge (counterclockwise) of the adjacent triangle. [rnext(abc) -> *a*]
        /// </summary>
        public void RnextSelf()
        {
            //SymSelf();
            int tmp = orient;
            orient = triangle.neighbors[tmp].orient;
            triangle = triangle.neighbors[tmp].triangle;

            //LnextSelf();
            orient = plus1Mod3[orient];

            //SymSelf();
            tmp = orient;
            orient = triangle.neighbors[tmp].orient;
            triangle = triangle.neighbors[tmp].triangle;
        }
Exemple #23
0
        /// <summary>
        /// Find the holes and infect them. Find the area constraints and infect 
        /// them. Infect the convex hull. Spread the infection and kill triangles. 
        /// Spread the area constraints.
        /// </summary>
        public void CarveHoles()
        {
            Otri searchtri = default(Otri);
            Vertex searchorg, searchdest;
            LocateResult intersect;

            Triangle[] regionTris = null;

            if (!mesh.behavior.Convex)
            {
                // Mark as infected any unprotected triangles on the boundary.
                // This is one way by which concavities are created.
                InfectHull();
            }

            if (!mesh.behavior.NoHoles)
            {
                // Infect each triangle in which a hole lies.
                foreach (var hole in mesh.holes)
                {
                    // Ignore holes that aren't within the bounds of the mesh.
                    if (mesh.bounds.Contains(hole))
                    {
                        // Start searching from some triangle on the outer boundary.
                        searchtri.triangle = Mesh.dummytri;
                        searchtri.orient = 0;
                        searchtri.SymSelf();
                        // Ensure that the hole is to the left of this boundary edge;
                        // otherwise, locate() will falsely report that the hole
                        // falls within the starting triangle.
                        searchorg = searchtri.Org();
                        searchdest = searchtri.Dest();
                        if (Primitives.CounterClockwise(searchorg, searchdest, hole) > 0.0)
                        {
                            // Find a triangle that contains the hole.
                            intersect = mesh.locator.Locate(hole, ref searchtri);
                            if ((intersect != LocateResult.Outside) && (!searchtri.IsInfected()))
                            {
                                // Infect the triangle. This is done by marking the triangle
                                // as infected and including the triangle in the virus pool.
                                searchtri.Infect();
                                viri.Add(searchtri.triangle);
                            }
                        }
                    }
                }
            }

            // Now, we have to find all the regions BEFORE we carve the holes, because locate() won't
            // work when the triangulation is no longer convex. (Incidentally, this is the reason why
            // regional attributes and area constraints can't be used when refining a preexisting mesh,
            // which might not be convex; they can only be used with a freshly triangulated PSLG.)
            if (mesh.regions.Count > 0)
            {
                int i = 0;

                regionTris = new Triangle[mesh.regions.Count];

                // Find the starting triangle for each region.
                foreach (var region in mesh.regions)
                {
                    regionTris[i] = Mesh.dummytri;
                    // Ignore region points that aren't within the bounds of the mesh.
                    if (mesh.bounds.Contains(region.point))
                    {
                        // Start searching from some triangle on the outer boundary.
                        searchtri.triangle = Mesh.dummytri;
                        searchtri.orient = 0;
                        searchtri.SymSelf();
                        // Ensure that the region point is to the left of this boundary
                        // edge; otherwise, locate() will falsely report that the
                        // region point falls within the starting triangle.
                        searchorg = searchtri.Org();
                        searchdest = searchtri.Dest();
                        if (Primitives.CounterClockwise(searchorg, searchdest, region.point) > 0.0)
                        {
                            // Find a triangle that contains the region point.
                            intersect = mesh.locator.Locate(region.point, ref searchtri);
                            if ((intersect != LocateResult.Outside) && (!searchtri.IsInfected()))
                            {
                                // Record the triangle for processing after the
                                // holes have been carved.
                                regionTris[i] = searchtri.triangle;
                                regionTris[i].region = region.id;
                            }
                        }
                    }

                    i++;
                }
            }

            if (viri.Count > 0)
            {
                // Carve the holes and concavities.
                Plague();
            }

            if (regionTris != null)
            {
                var iterator = new RegionIterator(mesh);

                for (int i = 0; i < regionTris.Length; i++)
                {
                    if (regionTris[i] != Mesh.dummytri)
                    {
                        // Make sure the triangle under consideration still exists.
                        // It may have been eaten by the virus.
                        if (!Otri.IsDead(regionTris[i]))
                        {
                            // Apply one region's attribute and/or area constraint.
                            iterator.Process(regionTris[i]);
                        }
                    }
                }
            }

            // Free up memory (virus pool should be empty anyway).
            viri.Clear();
        }
        static List<Vertex> IndexVertices(Triangle triangle, Dictionary<int, int> indexDict, ref int highestIndex, out int[] newIndices)
        {
            var newVertices = new List<Vertex>();
            newIndices = new int[3];

            if (indexDict.ContainsKey(triangle.P0))
            {
                newIndices[0] = indexDict[triangle.P0];
            }
            else
            {
                TriangleNet.Data.Vertex vertex0 = triangle.GetVertex(0);
                newVertices.Add(new Vertex((float)vertex0.X, (float)vertex0.Y));
                indexDict.Add(vertex0.ID, highestIndex);
                newIndices[0] = highestIndex;
                highestIndex++;
            }

            if (indexDict.ContainsKey(triangle.P1))
            {
                newIndices[1] = indexDict[triangle.P1];
            }
            else
            {
                TriangleNet.Data.Vertex vertex1 = triangle.GetVertex(1);
                newVertices.Add(new Vertex((float)vertex1.X, (float)vertex1.Y));
                indexDict.Add(vertex1.ID, highestIndex);
                newIndices[1] = highestIndex;
                highestIndex++;
            }

            if (indexDict.ContainsKey(triangle.P2))
            {
                newIndices[2] = indexDict[triangle.P2];
            }
            else
            {
                TriangleNet.Data.Vertex vertex2 = triangle.GetVertex(2);
                newVertices.Add(new Vertex((float)vertex2.X, (float)vertex2.Y));
                indexDict.Add(vertex2.ID, highestIndex);
                newIndices[2] = highestIndex;
                highestIndex++;
            }

            return newVertices;
        }