// -----------------------------------------------------------------------
//
// 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
{
using System;
using Animation.TriangleNet.Topology;
using Animation.TriangleNet.Geometry;
internal static class MeshValidator
{
private static RobustPredicates predicates = RobustPredicates.Default;
///
/// Test the mesh for topological consistency.
///
internal static bool IsConsistent(Mesh mesh)
{
Otri tri = default(Otri);
Otri oppotri = default(Otri), oppooppotri = default(Otri);
Vertex org, dest, apex;
Vertex oppoorg, oppodest;
var logger = Log.Instance;
// Temporarily turn on exact arithmetic if it's off.
bool saveexact = Behavior.NoExact;
Behavior.NoExact = false;
int horrors = 0;
// Run through the list of triangles, checking each one.
foreach (var t in mesh.triangles)
{
tri.tri = t;
// Check all three edges of the triangle.
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
org = tri.Org();
dest = tri.Dest();
if (tri.orient == 0)
{
// Only test for inversion once.
// Test if the triangle is flat or inverted.
apex = tri.Apex();
if (predicates.CounterClockwise(org, dest, apex) <= 0.0)
{
if (Log.Verbose)
{
logger.Warning(String.Format("Triangle is flat or inverted (ID {0}).", t.id),
"MeshValidator.IsConsistent()");
}
horrors++;
}
}
// Find the neighboring triangle on this edge.
tri.Sym(ref oppotri);
if (oppotri.tri.id != Mesh.DUMMY)
{
// Check that the triangle's neighbor knows it's a neighbor.
oppotri.Sym(ref oppooppotri);
if ((tri.tri != oppooppotri.tri) || (tri.orient != oppooppotri.orient))
{
if (tri.tri == oppooppotri.tri && Log.Verbose)
{
logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)",
"MeshValidator.IsConsistent()");
}
horrors++;
}
// Check that both triangles agree on the identities
// of their shared vertices.
oppoorg = oppotri.Org();
oppodest = oppotri.Dest();
if ((org != oppodest) || (dest != oppoorg))
{
if (Log.Verbose)
{
logger.Warning("Mismatched edge coordinates between two triangles.",
"MeshValidator.IsConsistent()");
}
horrors++;
}
}
}
}
// Check for unconnected vertices
mesh.MakeVertexMap();
foreach (var v in mesh.vertices.Values)
{
if (v.tri.tri == null && Log.Verbose)
{
logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)",
"MeshValidator.IsConsistent()");
}
}
// Restore the status of exact arithmetic.
Behavior.NoExact = saveexact;
return (horrors == 0);
}
///
/// Check if the mesh is (conforming) Delaunay.
///
internal static bool IsDelaunay(Mesh mesh)
{
return IsDelaunay(mesh, false);
}
///
/// Check if that the mesh is (constrained) Delaunay.
///
internal static bool IsConstrainedDelaunay(Mesh mesh)
{
return IsDelaunay(mesh, true);
}
///
/// Ensure that the mesh is (constrained) Delaunay.
///
private static bool IsDelaunay(Mesh mesh, bool constrained)
{
Otri loop = default(Otri);
Otri oppotri = default(Otri);
Osub opposubseg = default(Osub);
Vertex org, dest, apex;
Vertex oppoapex;
bool shouldbedelaunay;
var logger = Log.Instance;
// Temporarily turn on exact arithmetic if it's off.
bool saveexact = Behavior.NoExact;
Behavior.NoExact = false;
int horrors = 0;
var inf1 = mesh.infvertex1;
var inf2 = mesh.infvertex2;
var inf3 = mesh.infvertex3;
// Run through the list of triangles, checking each one.
foreach (var tri in mesh.triangles)
{
loop.tri = tri;
// Check all three edges of the triangle.
for (loop.orient = 0; loop.orient < 3; loop.orient++)
{
org = loop.Org();
dest = loop.Dest();
apex = loop.Apex();
loop.Sym(ref oppotri);
oppoapex = oppotri.Apex();
// Only test that the edge is locally Delaunay if there is an
// adjoining triangle whose pointer is larger (to ensure that
// each pair isn't tested twice).
shouldbedelaunay = (loop.tri.id < oppotri.tri.id) &&
!Otri.IsDead(oppotri.tri) && (oppotri.tri.id != Mesh.DUMMY) &&
(org != inf1) && (org != inf2) && (org != inf3) &&
(dest != inf1) && (dest != inf2) && (dest != inf3) &&
(apex != inf1) && (apex != inf2) && (apex != inf3) &&
(oppoapex != inf1) && (oppoapex != inf2) && (oppoapex != inf3);
if (constrained && mesh.checksegments && shouldbedelaunay)
{
// If a subsegment separates the triangles, then the edge is
// constrained, so no local Delaunay test should be done.
loop.Pivot(ref opposubseg);
if (opposubseg.seg.hash != Mesh.DUMMY)
{
shouldbedelaunay = false;
}
}
if (shouldbedelaunay)
{
if (predicates.NonRegular(org, dest, apex, oppoapex) > 0.0)
{
if (Log.Verbose)
{
logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).",
loop.tri.id, oppotri.tri.id), "MeshValidator.IsDelaunay()");
}
horrors++;
}
}
}
}
// Restore the status of exact arithmetic.
Behavior.NoExact = saveexact;
return (horrors == 0);
}
}
}