// ----------------------------------------------------------------------- // // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ // // ----------------------------------------------------------------------- namespace UnityEngine.U2D.Animation.TriangleNet .Topology.DCEL { using System.Collections.Generic; using Animation.TriangleNet.Geometry; internal class DcelMesh { protected List vertices; protected List edges; protected List faces; /// /// Initializes a new instance of the class. /// public DcelMesh() : this(true) { } /// /// Initializes a new instance of the class. /// /// If false, lists will not be initialized. protected DcelMesh(bool initialize) { if (initialize) { vertices = new List(); edges = new List(); faces = new List(); } } /// /// Gets the vertices of the Voronoi diagram. /// public List Vertices { get { return vertices; } } /// /// Gets the list of half-edges specify the Voronoi diagram topology. /// public List HalfEdges { get { return edges; } } /// /// Gets the faces of the Voronoi diagram. /// public List Faces { get { return faces; } } /// /// Gets the collection of edges of the Voronoi diagram. /// public IEnumerable Edges { get { return EnumerateEdges(); } } /// /// Check if the DCEL is consistend. /// /// If true, faces are assumed to be closed (i.e. all edges must have /// a valid next pointer). /// Maximum edge count of faces (default = 0 means skip check). /// public virtual bool IsConsistent(bool closed = true, int depth = 0) { // Check vertices for null pointers. foreach (var vertex in vertices) { if (vertex.id < 0) { continue; } if (vertex.leaving == null) { return false; } if (vertex.Leaving.Origin.id != vertex.id) { return false; } } // Check faces for null pointers. foreach (var face in faces) { if (face.ID < 0) { continue; } if (face.edge == null) { return false; } if (face.id != face.edge.face.id) { return false; } } // Check half-edges for null pointers. foreach (var edge in edges) { if (edge.id < 0) { continue; } if (edge.twin == null) { return false; } if (edge.origin == null) { return false; } if (edge.face == null) { return false; } if (closed && edge.next == null) { return false; } } // Check half-edges (topology). foreach (var edge in edges) { if (edge.id < 0) { continue; } var twin = edge.twin; var next = edge.next; if (edge.id != twin.twin.id) { return false; } if (closed) { if (next.origin.id != twin.origin.id) { return false; } if (next.twin.next.origin.id != edge.twin.origin.id) { return false; } } } if (closed && depth > 0) { // Check if faces are closed. foreach (var face in faces) { if (face.id < 0) { continue; } var edge = face.edge; var next = edge.next; int id = edge.id; int k = 0; while (next.id != id && k < depth) { next = next.next; k++; } if (next.id != id) { return false; } } } return true; } /// /// Search for half-edge without twin and add a twin. Connect twins to form connected /// boundary contours. /// /// /// This method assumes that all faces are closed (i.e. no edge.next pointers are null). /// public void ResolveBoundaryEdges() { // Maps vertices to leaving boundary edge. var map = new Dictionary(); // TODO: parallel? foreach (var edge in this.edges) { if (edge.twin == null) { var twin = edge.twin = new HalfEdge(edge.next.origin, Face.Empty); twin.twin = edge; map.Add(twin.origin.id, twin); } } int j = edges.Count; foreach (var edge in map.Values) { edge.id = j++; edge.next = map[edge.twin.origin.id]; this.edges.Add(edge); } } /// /// Enumerates all edges of the DCEL. /// /// /// This method assumes that each half-edge has a twin (i.e. NOT null). /// protected virtual 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 (edge.id < twin.id) { edges.Add(new Edge(edge.origin.id, twin.origin.id)); } } return edges; } } }