// ----------------------------------------------------------------------- // // Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ // // ----------------------------------------------------------------------- namespace UnityEngine.U2D.Animation.TriangleNet .Meshing { using System; using System.Collections.Generic; using System.Linq; using Animation.TriangleNet.Geometry; using Animation.TriangleNet.Topology; using Animation.TriangleNet.Topology.DCEL; using HVertex = Animation.TriangleNet.Topology.DCEL.Vertex; using TVertex = Animation.TriangleNet.Geometry.Vertex; /// /// The Converter class provides methods for mesh reconstruction and conversion. /// internal static class Converter { #region Triangle mesh conversion /// /// Reconstruct a triangulation from its raw data representation. /// internal static Mesh ToMesh(Polygon polygon, IList triangles) { return ToMesh(polygon, triangles.ToArray()); } /// /// Reconstruct a triangulation from its raw data representation. /// internal static Mesh ToMesh(Polygon polygon, ITriangle[] triangles) { Otri tri = default(Otri); Osub subseg = default(Osub); int i = 0; int elements = triangles == null ? 0 : triangles.Length; int segments = polygon.Segments.Count; // TODO: Configuration should be a function argument. var mesh = new Mesh(new Configuration()); mesh.TransferNodes(polygon.Points); mesh.regions.AddRange(polygon.Regions); mesh.behavior.useRegions = polygon.Regions.Count > 0; if (polygon.Segments.Count > 0) { mesh.behavior.Poly = true; mesh.holes.AddRange(polygon.Holes); } // Create the triangles. for (i = 0; i < elements; i++) { mesh.MakeTriangle(ref tri); } if (mesh.behavior.Poly) { mesh.insegments = segments; // Create the subsegments. for (i = 0; i < segments; i++) { mesh.MakeSegment(ref subseg); } } var vertexarray = SetNeighbors(mesh, triangles); SetSegments(mesh, polygon, vertexarray); return mesh; } /// /// Finds the adjacencies between triangles by forming a stack of triangles for /// each vertex. Each triangle is on three different stacks simultaneously. /// private static List[] SetNeighbors(Mesh mesh, ITriangle[] triangles) { Otri tri = default(Otri); Otri triangleleft = default(Otri); Otri checktri = default(Otri); Otri checkleft = default(Otri); Otri nexttri; TVertex tdest, tapex; TVertex checkdest, checkapex; int[] corner = new int[3]; int aroundvertex; int i; // Allocate a temporary array that maps each vertex to some adjacent triangle. var vertexarray = new List[mesh.vertices.Count]; // Each vertex is initially unrepresented. for (i = 0; i < mesh.vertices.Count; i++) { Otri tmp = default(Otri); tmp.tri = mesh.dummytri; vertexarray[i] = new List(3); vertexarray[i].Add(tmp); } i = 0; // Read the triangles from the .ele file, and link // together those that share an edge. foreach (var item in mesh.triangles) { tri.tri = item; // Copy the triangle's three corners. for (int j = 0; j < 3; j++) { corner[j] = triangles[i].GetVertexID(j); if ((corner[j] < 0) || (corner[j] >= mesh.invertices)) { Log.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()"); throw new Exception("Triangle has an invalid vertex index."); } } // Read the triangle's attributes. tri.tri.label = triangles[i].Label; // TODO: VarArea if (mesh.behavior.VarArea) { tri.tri.area = triangles[i].Area; } // Set the triangle's vertices. tri.orient = 0; tri.SetOrg(mesh.vertices[corner[0]]); tri.SetDest(mesh.vertices[corner[1]]); tri.SetApex(mesh.vertices[corner[2]]); // Try linking the triangle to others that share these vertices. for (tri.orient = 0; tri.orient < 3; tri.orient++) { // Take the number for the origin of triangleloop. aroundvertex = corner[tri.orient]; int index = vertexarray[aroundvertex].Count - 1; // Look for other triangles having this vertex. nexttri = vertexarray[aroundvertex][index]; // Push the current triangle onto the stack. vertexarray[aroundvertex].Add(tri); checktri = nexttri; if (checktri.tri.id != Mesh.DUMMY) { tdest = tri.Dest(); tapex = tri.Apex(); // Look for other triangles that share an edge. do { checkdest = checktri.Dest(); checkapex = checktri.Apex(); if (tapex == checkdest) { // The two triangles share an edge; bond them together. tri.Lprev(ref triangleleft); triangleleft.Bond(ref checktri); } if (tdest == checkapex) { // The two triangles share an edge; bond them together. checktri.Lprev(ref checkleft); tri.Bond(ref checkleft); } // Find the next triangle in the stack. index--; nexttri = vertexarray[aroundvertex][index]; checktri = nexttri; } while (checktri.tri.id != Mesh.DUMMY); } } i++; } return vertexarray; } /// /// Finds the adjacencies between triangles and subsegments. /// private static void SetSegments(Mesh mesh, Polygon polygon, List[] vertexarray) { Otri checktri = default(Otri); Otri nexttri; // Triangle TVertex checkdest; Otri checkneighbor = default(Otri); Osub subseg = default(Osub); Otri prevlink; // Triangle TVertex tmp; TVertex sorg, sdest; bool notfound; //bool segmentmarkers = false; int boundmarker; int aroundvertex; int i; int hullsize = 0; // Prepare to count the boundary edges. if (mesh.behavior.Poly) { // Link the segments to their neighboring triangles. boundmarker = 0; i = 0; foreach (var item in mesh.subsegs.Values) { subseg.seg = item; sorg = polygon.Segments[i].GetVertex(0); sdest = polygon.Segments[i].GetVertex(1); boundmarker = polygon.Segments[i].Label; if ((sorg.id < 0 || sorg.id >= mesh.invertices) || (sdest.id < 0 || sdest.id >= mesh.invertices)) { Log.Instance.Error("Segment has an invalid vertex index.", "MeshReader.Reconstruct()"); throw new Exception("Segment has an invalid vertex index."); } // set the subsegment's vertices. subseg.orient = 0; subseg.SetOrg(sorg); subseg.SetDest(sdest); subseg.SetSegOrg(sorg); subseg.SetSegDest(sdest); subseg.seg.boundary = boundmarker; // Try linking the subsegment to triangles that share these vertices. for (subseg.orient = 0; subseg.orient < 2; subseg.orient++) { // Take the number for the destination of subsegloop. aroundvertex = subseg.orient == 1 ? sorg.id : sdest.id; int index = vertexarray[aroundvertex].Count - 1; // Look for triangles having this vertex. prevlink = vertexarray[aroundvertex][index]; nexttri = vertexarray[aroundvertex][index]; checktri = nexttri; tmp = subseg.Org(); notfound = true; // Look for triangles having this edge. Note that I'm only // comparing each triangle's destination with the subsegment; // each triangle's apex is handled through a different vertex. // Because each triangle appears on three vertices' lists, each // occurrence of a triangle on a list can (and does) represent // an edge. In this way, most edges are represented twice, and // every triangle-subsegment bond is represented once. while (notfound && (checktri.tri.id != Mesh.DUMMY)) { checkdest = checktri.Dest(); if (tmp == checkdest) { // We have a match. Remove this triangle from the list. //prevlink = vertexarray[aroundvertex][index]; vertexarray[aroundvertex].Remove(prevlink); // Bond the subsegment to the triangle. checktri.SegBond(ref subseg); // Check if this is a boundary edge. checktri.Sym(ref checkneighbor); if (checkneighbor.tri.id == Mesh.DUMMY) { // The next line doesn't insert a subsegment (because there's // already one there), but it sets the boundary markers of // the existing subsegment and its vertices. mesh.InsertSubseg(ref checktri, 1); hullsize++; } notfound = false; } index--; // Find the next triangle in the stack. prevlink = vertexarray[aroundvertex][index]; nexttri = vertexarray[aroundvertex][index]; checktri = nexttri; } } i++; } } // Mark the remaining edges as not being attached to any subsegment. // Also, count the (yet uncounted) boundary edges. for (i = 0; i < mesh.vertices.Count; i++) { // Search the stack of triangles adjacent to a vertex. int index = vertexarray[i].Count - 1; nexttri = vertexarray[i][index]; checktri = nexttri; while (checktri.tri.id != Mesh.DUMMY) { // Find the next triangle in the stack before this // information gets overwritten. index--; nexttri = vertexarray[i][index]; // No adjacent subsegment. (This overwrites the stack info.) checktri.SegDissolve(mesh.dummysub); checktri.Sym(ref checkneighbor); if (checkneighbor.tri.id == Mesh.DUMMY) { mesh.InsertSubseg(ref checktri, 1); hullsize++; } checktri = nexttri; } } mesh.hullsize = hullsize; } #endregion #region DCEL conversion internal static DcelMesh ToDCEL(Mesh mesh) { var dcel = new DcelMesh(); var vertices = new HVertex[mesh.vertices.Count]; var faces = new Face[mesh.triangles.Count]; dcel.HalfEdges.Capacity = 2 * mesh.NumberOfEdges; mesh.Renumber(); HVertex vertex; foreach (var v in mesh.vertices.Values) { vertex = new HVertex(v.x, v.y); vertex.id = v.id; vertex.label = v.label; vertices[v.id] = vertex; } // Maps a triangle to its 3 edges (used to set next pointers). var map = new List[mesh.triangles.Count]; Face face; foreach (var t in mesh.triangles) { face = new Face(null); face.id = t.id; faces[t.id] = face; map[t.id] = new List(3); } Otri tri = default(Otri), neighbor = default(Otri); Animation.TriangleNet.Geometry.Vertex org, dest; int id, nid, count = mesh.triangles.Count; HalfEdge edge, twin, next; var edges = dcel.HalfEdges; // Count half-edges (edge ids). int k = 0; // Maps a vertex to its leaving boundary edge. var boundary = new Dictionary(); foreach (var t in mesh.triangles) { id = t.id; tri.tri = t; for (int i = 0; i < 3; i++) { tri.orient = i; tri.Sym(ref neighbor); nid = neighbor.tri.id; if (id < nid || nid < 0) { face = faces[id]; // Get the endpoints of the current triangle edge. org = tri.Org(); dest = tri.Dest(); // Create half-edges. edge = new HalfEdge(vertices[org.id], face); twin = new HalfEdge(vertices[dest.id], nid < 0 ? Face.Empty : faces[nid]); map[id].Add(edge); if (nid >= 0) { map[nid].Add(twin); } else { boundary.Add(dest.id, twin); } // Set leaving edges. edge.origin.leaving = edge; twin.origin.leaving = twin; // Set twin edges. edge.twin = twin; twin.twin = edge; edge.id = k++; twin.id = k++; edges.Add(edge); edges.Add(twin); } } } // Set next pointers for each triangle face. foreach (var t in map) { edge = t[0]; next = t[1]; if (edge.twin.origin.id == next.origin.id) { edge.next = next; next.next = t[2]; t[2].next = edge; } else { edge.next = t[2]; next.next = edge; t[2].next = next; } } // Resolve boundary edges. foreach (var e in boundary.Values) { e.next = boundary[e.twin.origin.id]; } dcel.Vertices.AddRange(vertices); dcel.Faces.AddRange(faces); return dcel; } #endregion } }