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