// ----------------------------------------------------------------------- // // 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 .Voronoi { using System.Collections.Generic; using Animation.TriangleNet.Topology; using Animation.TriangleNet.Geometry; using Animation.TriangleNet.Topology.DCEL; using Vertex = Animation.TriangleNet.Topology.DCEL.Vertex; /// /// The Voronoi diagram is the dual of a pointset triangulation. /// internal abstract class VoronoiBase : DcelMesh { protected IPredicates predicates; protected IVoronoiFactory factory; // List of infinite half-edges, i.e. half-edges that start at circumcenters of triangles // which lie on the domain boundary. protected List rays; /// /// Initializes a new instance of the class. /// /// Triangle mesh. /// Voronoi object factory. /// Geometric predicates implementation. /// If set to true, the constuctor will call the Generate /// method, which builds the Voronoi diagram. protected VoronoiBase(Mesh mesh, IVoronoiFactory factory, IPredicates predicates, bool generate) : base(false) { this.factory = factory; this.predicates = predicates; if (generate) { Generate(mesh); } } /// /// Generate the Voronoi diagram from given triangle mesh.. /// /// /// protected void Generate(Mesh mesh) { mesh.Renumber(); base.edges = new List(); this.rays = new List(); // Allocate space for Voronoi diagram. var vertices = new Vertex[mesh.triangles.Count + mesh.hullsize]; var faces = new Face[mesh.vertices.Count]; if (factory == null) { factory = new DefaultVoronoiFactory(); } factory.Initialize(vertices.Length, 2 * mesh.NumberOfEdges, faces.Length); // Compute triangles circumcenters. var map = ComputeVertices(mesh, vertices); // Create all Voronoi faces. foreach (var vertex in mesh.vertices.Values) { faces[vertex.id] = factory.CreateFace(vertex); } ComputeEdges(mesh, vertices, faces, map); // At this point all edges are computed, but the (edge.next) pointers aren't set. ConnectEdges(map); base.vertices = new List(vertices); base.faces = new List(faces); } /// /// Compute the Voronoi vertices (the circumcenters of the triangles). /// /// An empty map, which will map all vertices to a list of leaving edges. protected List[] ComputeVertices(Mesh mesh, Vertex[] vertices) { Otri tri = default(Otri); double xi = 0, eta = 0; Vertex vertex; Point pt; int id; // Maps all vertices to a list of leaving edges. var map = new List[mesh.triangles.Count]; // Compue triangle circumcenters foreach (var t in mesh.triangles) { id = t.id; tri.tri = t; pt = predicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta); vertex = factory.CreateVertex(pt.x, pt.y); vertex.id = id; vertices[id] = vertex; map[id] = new List(); } return map; } /// /// Compute the edges of the Voronoi diagram. /// /// /// /// /// Empty vertex map. protected void ComputeEdges(Mesh mesh, Vertex[] vertices, Face[] faces, List[] map) { Otri tri, neighbor = default(Otri); Animation.TriangleNet.Geometry.Vertex org, dest; double px, py; int id, nid, count = mesh.triangles.Count; Face face, neighborFace; HalfEdge edge, twin; Vertex vertex, end; // Count infinte edges (vertex id for their endpoints). int j = 0; // Count half-edges (edge ids). int k = 0; // To loop over the set of edges, loop over all triangles, and look at the // three edges of each triangle. If there isn't another triangle adjacent // to the edge, operate on the edge. If there is another adjacent triangle, // operate on the edge only if the current triangle has a smaller id than // its neighbor. This way, each edge is considered only once. 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) { // Get the endpoints of the current triangle edge. org = tri.Org(); dest = tri.Dest(); face = faces[org.id]; neighborFace = faces[dest.id]; vertex = vertices[id]; // For each edge in the triangle mesh, there's a corresponding edge // in the Voronoi diagram, i.e. two half-edges will be created. if (nid < 0) { // Unbounded edge, direction perpendicular to the boundary edge, // pointing outwards. px = dest.y - org.y; py = org.x - dest.x; end = factory.CreateVertex(vertex.x + px, vertex.y + py); end.id = count + j++; vertices[end.id] = end; edge = factory.CreateHalfEdge(end, face); twin = factory.CreateHalfEdge(vertex, neighborFace); // Make (face.edge) always point to an edge that starts at an infinite // vertex. This will allow traversing of unbounded faces. face.edge = edge; face.bounded = false; map[id].Add(twin); rays.Add(twin); } else { end = vertices[nid]; // Create half-edges. edge = factory.CreateHalfEdge(end, face); twin = factory.CreateHalfEdge(vertex, neighborFace); // Add to vertex map. map[nid].Add(edge); map[id].Add(twin); } vertex.leaving = twin; end.leaving = edge; edge.twin = twin; twin.twin = edge; edge.id = k++; twin.id = k++; this.edges.Add(edge); this.edges.Add(twin); } } } } /// /// Connect all edges of the Voronoi diagram. /// /// Maps all vertices to a list of leaving edges. protected virtual void ConnectEdges(List[] map) { int length = map.Length; // For each half-edge, find its successor in the connected face. foreach (var edge in this.edges) { var face = edge.face.generator.id; // The id of the dest vertex of current edge. int id = edge.twin.origin.id; // The edge origin can also be an infinite vertex. Sort them out // by checking the id. if (id < length) { // Look for the edge that is connected to the current face. Each // Voronoi vertex has degree 3, so this loop is actually O(1). foreach (var next in map[id]) { if (next.face.generator.id == face) { edge.next = next; break; } } } } } protected override IEnumerable EnumerateEdges() { var edges = new List(this.edges.Count / 2); foreach (var edge in this.edges) { var twin = edge.twin; // Report edge only once. if (twin == null) { edges.Add(new Edge(edge.origin.id, edge.next.origin.id)); } else if (edge.id < twin.id) { edges.Add(new Edge(edge.origin.id, twin.origin.id)); } } return edges; } } }