Initial commit

This commit is contained in:
AbstractConcept 2022-09-13 00:36:34 -05:00
commit 3c7cc0c973
8391 changed files with 704313 additions and 0 deletions

View file

@ -0,0 +1,697 @@
// -----------------------------------------------------------------------
// <copyright file="Dwyer.cs">
// 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/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.Meshing.Algorithm
{
using System;
using System.Collections.Generic;
using Animation.TriangleNet.Geometry;
using Animation.TriangleNet.Tools;
using Animation.TriangleNet.Topology;
/// <summary>
/// Builds a delaunay triangulation using the divide-and-conquer algorithm.
/// </summary>
/// <remarks>
/// The divide-and-conquer bounding box
///
/// I originally implemented the divide-and-conquer and incremental Delaunay
/// triangulations using the edge-based data structure presented by Guibas
/// and Stolfi. Switching to a triangle-based data structure doubled the
/// speed. However, I had to think of a few extra tricks to maintain the
/// elegance of the original algorithms.
///
/// The "bounding box" used by my variant of the divide-and-conquer
/// algorithm uses one triangle for each edge of the convex hull of the
/// triangulation. These bounding triangles all share a common apical
/// vertex, which is represented by NULL and which represents nothing.
/// The bounding triangles are linked in a circular fan about this NULL
/// vertex, and the edges on the convex hull of the triangulation appear
/// opposite the NULL vertex. You might find it easiest to imagine that
/// the NULL vertex is a point in 3D space behind the center of the
/// triangulation, and that the bounding triangles form a sort of cone.
///
/// This bounding box makes it easy to represent degenerate cases. For
/// instance, the triangulation of two vertices is a single edge. This edge
/// is represented by two bounding box triangles, one on each "side" of the
/// edge. These triangles are also linked together in a fan about the NULL
/// vertex.
///
/// The bounding box also makes it easy to traverse the convex hull, as the
/// divide-and-conquer algorithm needs to do.
/// </remarks>
internal class Dwyer : ITriangulator
{
// Random is not threadsafe, so don't make this static.
// Random rand = new Random(DateTime.Now.Millisecond);
IPredicates predicates;
public bool UseDwyer = true;
Vertex[] sortarray;
Mesh mesh;
/// <summary>
/// Form a Delaunay triangulation by the divide-and-conquer method.
/// </summary>
/// <returns></returns>
/// <remarks>
/// Sorts the vertices, calls a recursive procedure to triangulate them, and
/// removes the bounding box, setting boundary markers as appropriate.
/// </remarks>
public IMesh Triangulate(IList<Vertex> points, Configuration config)
{
this.predicates = config.Predicates();
this.mesh = new Mesh(config);
this.mesh.TransferNodes(points);
Otri hullleft = default(Otri), hullright = default(Otri);
int i, j, n = points.Count;
// Allocate an array of pointers to vertices for sorting.
this.sortarray = new Vertex[n];
i = 0;
foreach (var v in points)
{
sortarray[i++] = v;
}
// Sort the vertices.
VertexSorter.Sort(sortarray);
// Discard duplicate vertices, which can really mess up the algorithm.
i = 0;
for (j = 1; j < n; j++)
{
if ((sortarray[i].x == sortarray[j].x) && (sortarray[i].y == sortarray[j].y))
{
if (Log.Verbose)
{
Log.Instance.Warning(
String.Format("A duplicate vertex appeared and was ignored (ID {0}).", sortarray[j].id),
"Dwyer.Triangulate()");
}
sortarray[j].type = VertexType.UndeadVertex;
mesh.undeads++;
}
else
{
i++;
sortarray[i] = sortarray[j];
}
}
i++;
if (UseDwyer)
{
// Re-sort the array of vertices to accommodate alternating cuts.
VertexSorter.Alternate(sortarray, i);
}
// Form the Delaunay triangulation.
DivconqRecurse(0, i - 1, 0, ref hullleft, ref hullright);
this.mesh.hullsize = RemoveGhosts(ref hullleft);
return this.mesh;
}
/// <summary>
/// Merge two adjacent Delaunay triangulations into a single Delaunay triangulation.
/// </summary>
/// <param name="farleft">Bounding triangles of the left triangulation.</param>
/// <param name="innerleft">Bounding triangles of the left triangulation.</param>
/// <param name="innerright">Bounding triangles of the right triangulation.</param>
/// <param name="farright">Bounding triangles of the right triangulation.</param>
/// <param name="axis"></param>
/// <remarks>
/// This is similar to the algorithm given by Guibas and Stolfi, but uses
/// a triangle-based, rather than edge-based, data structure.
///
/// The algorithm walks up the gap between the two triangulations, knitting
/// them together. As they are merged, some of their bounding triangles
/// are converted into real triangles of the triangulation. The procedure
/// pulls each hull's bounding triangles apart, then knits them together
/// like the teeth of two gears. The Delaunay property determines, at each
/// step, whether the next "tooth" is a bounding triangle of the left hull
/// or the right. When a bounding triangle becomes real, its apex is
/// changed from NULL to a real vertex.
///
/// Only two new triangles need to be allocated. These become new bounding
/// triangles at the top and bottom of the seam. They are used to connect
/// the remaining bounding triangles (those that have not been converted
/// into real triangles) into a single fan.
///
/// On label, 'farleft' and 'innerleft' are bounding triangles of the left
/// triangulation. The origin of 'farleft' is the leftmost vertex, and
/// the destination of 'innerleft' is the rightmost vertex of the
/// triangulation. Similarly, 'innerright' and 'farright' are bounding
/// triangles of the right triangulation. The origin of 'innerright' and
/// destination of 'farright' are the leftmost and rightmost vertices.
///
/// On completion, the origin of 'farleft' is the leftmost vertex of the
/// merged triangulation, and the destination of 'farright' is the rightmost
/// vertex.
/// </remarks>
void MergeHulls(ref Otri farleft, ref Otri innerleft, ref Otri innerright,
ref Otri farright, int axis)
{
Otri leftcand = default(Otri), rightcand = default(Otri);
Otri nextedge = default(Otri);
Otri sidecasing = default(Otri), topcasing = default(Otri), outercasing = default(Otri);
Otri checkedge = default(Otri);
Otri baseedge = default(Otri);
Vertex innerleftdest;
Vertex innerrightorg;
Vertex innerleftapex, innerrightapex;
Vertex farleftpt, farrightpt;
Vertex farleftapex, farrightapex;
Vertex lowerleft, lowerright;
Vertex upperleft, upperright;
Vertex nextapex;
Vertex checkvertex;
bool changemade;
bool badedge;
bool leftfinished, rightfinished;
innerleftdest = innerleft.Dest();
innerleftapex = innerleft.Apex();
innerrightorg = innerright.Org();
innerrightapex = innerright.Apex();
// Special treatment for horizontal cuts.
if (UseDwyer && (axis == 1))
{
farleftpt = farleft.Org();
farleftapex = farleft.Apex();
farrightpt = farright.Dest();
farrightapex = farright.Apex();
// The pointers to the extremal vertices are shifted to point to the
// topmost and bottommost vertex of each hull, rather than the
// leftmost and rightmost vertices.
while (farleftapex.y < farleftpt.y)
{
farleft.Lnext();
farleft.Sym();
farleftpt = farleftapex;
farleftapex = farleft.Apex();
}
innerleft.Sym(ref checkedge);
checkvertex = checkedge.Apex();
while (checkvertex.y > innerleftdest.y)
{
checkedge.Lnext(ref innerleft);
innerleftapex = innerleftdest;
innerleftdest = checkvertex;
innerleft.Sym(ref checkedge);
checkvertex = checkedge.Apex();
}
while (innerrightapex.y < innerrightorg.y)
{
innerright.Lnext();
innerright.Sym();
innerrightorg = innerrightapex;
innerrightapex = innerright.Apex();
}
farright.Sym(ref checkedge);
checkvertex = checkedge.Apex();
while (checkvertex.y > farrightpt.y)
{
checkedge.Lnext(ref farright);
farrightapex = farrightpt;
farrightpt = checkvertex;
farright.Sym(ref checkedge);
checkvertex = checkedge.Apex();
}
}
// Find a line tangent to and below both hulls.
do
{
changemade = false;
// Make innerleftdest the "bottommost" vertex of the left hull.
if (predicates.CounterClockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0)
{
innerleft.Lprev();
innerleft.Sym();
innerleftdest = innerleftapex;
innerleftapex = innerleft.Apex();
changemade = true;
}
// Make innerrightorg the "bottommost" vertex of the right hull.
if (predicates.CounterClockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0)
{
innerright.Lnext();
innerright.Sym();
innerrightorg = innerrightapex;
innerrightapex = innerright.Apex();
changemade = true;
}
}
while (changemade);
// Find the two candidates to be the next "gear tooth."
innerleft.Sym(ref leftcand);
innerright.Sym(ref rightcand);
// Create the bottom new bounding triangle.
mesh.MakeTriangle(ref baseedge);
// Connect it to the bounding boxes of the left and right triangulations.
baseedge.Bond(ref innerleft);
baseedge.Lnext();
baseedge.Bond(ref innerright);
baseedge.Lnext();
baseedge.SetOrg(innerrightorg);
baseedge.SetDest(innerleftdest);
// Apex is intentionally left NULL.
// Fix the extreme triangles if necessary.
farleftpt = farleft.Org();
if (innerleftdest == farleftpt)
{
baseedge.Lnext(ref farleft);
}
farrightpt = farright.Dest();
if (innerrightorg == farrightpt)
{
baseedge.Lprev(ref farright);
}
// The vertices of the current knitting edge.
lowerleft = innerleftdest;
lowerright = innerrightorg;
// The candidate vertices for knitting.
upperleft = leftcand.Apex();
upperright = rightcand.Apex();
// Walk up the gap between the two triangulations, knitting them together.
while (true)
{
// Have we reached the top? (This isn't quite the right question,
// because even though the left triangulation might seem finished now,
// moving up on the right triangulation might reveal a new vertex of
// the left triangulation. And vice-versa.)
leftfinished = predicates.CounterClockwise(upperleft, lowerleft, lowerright) <= 0.0;
rightfinished = predicates.CounterClockwise(upperright, lowerleft, lowerright) <= 0.0;
if (leftfinished && rightfinished)
{
// Create the top new bounding triangle.
mesh.MakeTriangle(ref nextedge);
nextedge.SetOrg(lowerleft);
nextedge.SetDest(lowerright);
// Apex is intentionally left NULL.
// Connect it to the bounding boxes of the two triangulations.
nextedge.Bond(ref baseedge);
nextedge.Lnext();
nextedge.Bond(ref rightcand);
nextedge.Lnext();
nextedge.Bond(ref leftcand);
// Special treatment for horizontal cuts.
if (UseDwyer && (axis == 1))
{
farleftpt = farleft.Org();
farleftapex = farleft.Apex();
farrightpt = farright.Dest();
farrightapex = farright.Apex();
farleft.Sym(ref checkedge);
checkvertex = checkedge.Apex();
// The pointers to the extremal vertices are restored to the
// leftmost and rightmost vertices (rather than topmost and
// bottommost).
while (checkvertex.x < farleftpt.x)
{
checkedge.Lprev(ref farleft);
farleftapex = farleftpt;
farleftpt = checkvertex;
farleft.Sym(ref checkedge);
checkvertex = checkedge.Apex();
}
while (farrightapex.x > farrightpt.x)
{
farright.Lprev();
farright.Sym();
farrightpt = farrightapex;
farrightapex = farright.Apex();
}
}
return;
}
// Consider eliminating edges from the left triangulation.
if (!leftfinished)
{
// What vertex would be exposed if an edge were deleted?
leftcand.Lprev(ref nextedge);
nextedge.Sym();
nextapex = nextedge.Apex();
// If nextapex is NULL, then no vertex would be exposed; the
// triangulation would have been eaten right through.
if (nextapex != null)
{
// Check whether the edge is Delaunay.
badedge = predicates.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0;
while (badedge)
{
// Eliminate the edge with an edge flip. As a result, the
// left triangulation will have one more boundary triangle.
nextedge.Lnext();
nextedge.Sym(ref topcasing);
nextedge.Lnext();
nextedge.Sym(ref sidecasing);
nextedge.Bond(ref topcasing);
leftcand.Bond(ref sidecasing);
leftcand.Lnext();
leftcand.Sym(ref outercasing);
nextedge.Lprev();
nextedge.Bond(ref outercasing);
// Correct the vertices to reflect the edge flip.
leftcand.SetOrg(lowerleft);
leftcand.SetDest(null);
leftcand.SetApex(nextapex);
nextedge.SetOrg(null);
nextedge.SetDest(upperleft);
nextedge.SetApex(nextapex);
// Consider the newly exposed vertex.
upperleft = nextapex;
// What vertex would be exposed if another edge were deleted?
sidecasing.Copy(ref nextedge);
nextapex = nextedge.Apex();
if (nextapex != null)
{
// Check whether the edge is Delaunay.
badedge = predicates.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0;
}
else
{
// Avoid eating right through the triangulation.
badedge = false;
}
}
}
}
// Consider eliminating edges from the right triangulation.
if (!rightfinished)
{
// What vertex would be exposed if an edge were deleted?
rightcand.Lnext(ref nextedge);
nextedge.Sym();
nextapex = nextedge.Apex();
// If nextapex is NULL, then no vertex would be exposed; the
// triangulation would have been eaten right through.
if (nextapex != null)
{
// Check whether the edge is Delaunay.
badedge = predicates.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
while (badedge)
{
// Eliminate the edge with an edge flip. As a result, the
// right triangulation will have one more boundary triangle.
nextedge.Lprev();
nextedge.Sym(ref topcasing);
nextedge.Lprev();
nextedge.Sym(ref sidecasing);
nextedge.Bond(ref topcasing);
rightcand.Bond(ref sidecasing);
rightcand.Lprev();
rightcand.Sym(ref outercasing);
nextedge.Lnext();
nextedge.Bond(ref outercasing);
// Correct the vertices to reflect the edge flip.
rightcand.SetOrg(null);
rightcand.SetDest(lowerright);
rightcand.SetApex(nextapex);
nextedge.SetOrg(upperright);
nextedge.SetDest(null);
nextedge.SetApex(nextapex);
// Consider the newly exposed vertex.
upperright = nextapex;
// What vertex would be exposed if another edge were deleted?
sidecasing.Copy(ref nextedge);
nextapex = nextedge.Apex();
if (nextapex != null)
{
// Check whether the edge is Delaunay.
badedge = predicates.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
}
else
{
// Avoid eating right through the triangulation.
badedge = false;
}
}
}
}
if (leftfinished || (!rightfinished &&
(predicates.InCircle(upperleft, lowerleft, lowerright, upperright) > 0.0)))
{
// Knit the triangulations, adding an edge from 'lowerleft'
// to 'upperright'.
baseedge.Bond(ref rightcand);
rightcand.Lprev(ref baseedge);
baseedge.SetDest(lowerleft);
lowerright = upperright;
baseedge.Sym(ref rightcand);
upperright = rightcand.Apex();
}
else
{
// Knit the triangulations, adding an edge from 'upperleft'
// to 'lowerright'.
baseedge.Bond(ref leftcand);
leftcand.Lnext(ref baseedge);
baseedge.SetOrg(lowerright);
lowerleft = upperleft;
baseedge.Sym(ref leftcand);
upperleft = leftcand.Apex();
}
}
}
/// <summary>
/// Recursively form a Delaunay triangulation by the divide-and-conquer method.
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <param name="axis"></param>
/// <param name="farleft"></param>
/// <param name="farright"></param>
/// <remarks>
/// Recursively breaks down the problem into smaller pieces, which are
/// knitted together by mergehulls(). The base cases (problems of two or
/// three vertices) are handled specially here.
///
/// On completion, 'farleft' and 'farright' are bounding triangles such that
/// the origin of 'farleft' is the leftmost vertex (breaking ties by
/// choosing the highest leftmost vertex), and the destination of
/// 'farright' is the rightmost vertex (breaking ties by choosing the
/// lowest rightmost vertex).
/// </remarks>
void DivconqRecurse(int left, int right, int axis,
ref Otri farleft, ref Otri farright)
{
Otri midtri = default(Otri);
Otri tri1 = default(Otri);
Otri tri2 = default(Otri);
Otri tri3 = default(Otri);
Otri innerleft = default(Otri), innerright = default(Otri);
double area;
int vertices = right - left + 1;
int divider;
if (vertices == 2)
{
// The triangulation of two vertices is an edge. An edge is
// represented by two bounding triangles.
mesh.MakeTriangle(ref farleft);
farleft.SetOrg(sortarray[left]);
farleft.SetDest(sortarray[left + 1]);
// The apex is intentionally left NULL.
mesh.MakeTriangle(ref farright);
farright.SetOrg(sortarray[left + 1]);
farright.SetDest(sortarray[left]);
// The apex is intentionally left NULL.
farleft.Bond(ref farright);
farleft.Lprev();
farright.Lnext();
farleft.Bond(ref farright);
farleft.Lprev();
farright.Lnext();
farleft.Bond(ref farright);
// Ensure that the origin of 'farleft' is sortarray[0].
farright.Lprev(ref farleft);
return;
}
else if (vertices == 3)
{
// The triangulation of three vertices is either a triangle (with
// three bounding triangles) or two edges (with four bounding
// triangles). In either case, four triangles are created.
mesh.MakeTriangle(ref midtri);
mesh.MakeTriangle(ref tri1);
mesh.MakeTriangle(ref tri2);
mesh.MakeTriangle(ref tri3);
area = predicates.CounterClockwise(sortarray[left], sortarray[left + 1], sortarray[left + 2]);
if (area == 0.0)
{
// Three collinear vertices; the triangulation is two edges.
midtri.SetOrg(sortarray[left]);
midtri.SetDest(sortarray[left + 1]);
tri1.SetOrg(sortarray[left + 1]);
tri1.SetDest(sortarray[left]);
tri2.SetOrg(sortarray[left + 2]);
tri2.SetDest(sortarray[left + 1]);
tri3.SetOrg(sortarray[left + 1]);
tri3.SetDest(sortarray[left + 2]);
// All apices are intentionally left NULL.
midtri.Bond(ref tri1);
tri2.Bond(ref tri3);
midtri.Lnext();
tri1.Lprev();
tri2.Lnext();
tri3.Lprev();
midtri.Bond(ref tri3);
tri1.Bond(ref tri2);
midtri.Lnext();
tri1.Lprev();
tri2.Lnext();
tri3.Lprev();
midtri.Bond(ref tri1);
tri2.Bond(ref tri3);
// Ensure that the origin of 'farleft' is sortarray[0].
tri1.Copy(ref farleft);
// Ensure that the destination of 'farright' is sortarray[2].
tri2.Copy(ref farright);
}
else
{
// The three vertices are not collinear; the triangulation is one
// triangle, namely 'midtri'.
midtri.SetOrg(sortarray[left]);
tri1.SetDest(sortarray[left]);
tri3.SetOrg(sortarray[left]);
// Apices of tri1, tri2, and tri3 are left NULL.
if (area > 0.0)
{
// The vertices are in counterclockwise order.
midtri.SetDest(sortarray[left + 1]);
tri1.SetOrg(sortarray[left + 1]);
tri2.SetDest(sortarray[left + 1]);
midtri.SetApex(sortarray[left + 2]);
tri2.SetOrg(sortarray[left + 2]);
tri3.SetDest(sortarray[left + 2]);
}
else
{
// The vertices are in clockwise order.
midtri.SetDest(sortarray[left + 2]);
tri1.SetOrg(sortarray[left + 2]);
tri2.SetDest(sortarray[left + 2]);
midtri.SetApex(sortarray[left + 1]);
tri2.SetOrg(sortarray[left + 1]);
tri3.SetDest(sortarray[left + 1]);
}
// The topology does not depend on how the vertices are ordered.
midtri.Bond(ref tri1);
midtri.Lnext();
midtri.Bond(ref tri2);
midtri.Lnext();
midtri.Bond(ref tri3);
tri1.Lprev();
tri2.Lnext();
tri1.Bond(ref tri2);
tri1.Lprev();
tri3.Lprev();
tri1.Bond(ref tri3);
tri2.Lnext();
tri3.Lprev();
tri2.Bond(ref tri3);
// Ensure that the origin of 'farleft' is sortarray[0].
tri1.Copy(ref farleft);
// Ensure that the destination of 'farright' is sortarray[2].
if (area > 0.0)
{
tri2.Copy(ref farright);
}
else
{
farleft.Lnext(ref farright);
}
}
return;
}
else
{
// Split the vertices in half.
divider = vertices >> 1;
// Recursively triangulate each half.
DivconqRecurse(left, left + divider - 1, 1 - axis, ref farleft, ref innerleft);
DivconqRecurse(left + divider, right, 1 - axis, ref innerright, ref farright);
// Merge the two triangulations into one.
MergeHulls(ref farleft, ref innerleft, ref innerright, ref farright, axis);
}
}
/// <summary>
/// Removes ghost triangles.
/// </summary>
/// <param name="startghost"></param>
/// <returns>Number of vertices on the hull.</returns>
int RemoveGhosts(ref Otri startghost)
{
Otri searchedge = default(Otri);
Otri dissolveedge = default(Otri);
Otri deadtriangle = default(Otri);
Vertex markorg;
int hullsize;
bool noPoly = !mesh.behavior.Poly;
// Find an edge on the convex hull to start point location from.
startghost.Lprev(ref searchedge);
searchedge.Sym();
mesh.dummytri.neighbors[0] = searchedge;
// Remove the bounding box and count the convex hull edges.
startghost.Copy(ref dissolveedge);
hullsize = 0;
do
{
hullsize++;
dissolveedge.Lnext(ref deadtriangle);
dissolveedge.Lprev();
dissolveedge.Sym();
// If no PSLG is involved, set the boundary markers of all the vertices
// on the convex hull. If a PSLG is used, this step is done later.
if (noPoly)
{
// Watch out for the case where all the input vertices are collinear.
if (dissolveedge.tri.id != Mesh.DUMMY)
{
markorg = dissolveedge.Org();
if (markorg.label == 0)
{
markorg.label = 1;
}
}
}
// Remove a bounding triangle from a convex hull triangle.
dissolveedge.Dissolve(mesh.dummytri);
// Find the next bounding triangle.
deadtriangle.Sym(ref dissolveedge);
// Delete the bounding triangle.
mesh.TriangleDealloc(deadtriangle.tri);
}
while (!dissolveedge.Equals(startghost));
return hullsize;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c2c3aea8fdba4a288f591cbf028963d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,192 @@
// -----------------------------------------------------------------------
// <copyright file="Incremental.cs">
// 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/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.Meshing.Algorithm
{
using System.Collections.Generic;
using Animation.TriangleNet.Topology;
using Animation.TriangleNet.Geometry;
/// <summary>
/// Builds a delaunay triangulation using the incremental algorithm.
/// </summary>
internal class Incremental : ITriangulator
{
Mesh mesh;
/// <summary>
/// Form a Delaunay triangulation by incrementally inserting vertices.
/// </summary>
/// <returns>Returns the number of edges on the convex hull of the
/// triangulation.</returns>
public IMesh Triangulate(IList<Vertex> points, Configuration config)
{
this.mesh = new Mesh(config);
this.mesh.TransferNodes(points);
Otri starttri = new Otri();
// Create a triangular bounding box.
GetBoundingBox();
foreach (var v in mesh.vertices.Values)
{
starttri.tri = mesh.dummytri;
Osub tmp = default(Osub);
if (mesh.InsertVertex(v, ref starttri, ref tmp, false, false) == InsertVertexResult.Duplicate)
{
if (Log.Verbose)
{
Log.Instance.Warning("A duplicate vertex appeared and was ignored.",
"Incremental.Triangulate()");
}
v.type = VertexType.UndeadVertex;
mesh.undeads++;
}
}
// Remove the bounding box.
this.mesh.hullsize = RemoveBox();
return this.mesh;
}
/// <summary>
/// Form an "infinite" bounding triangle to insert vertices into.
/// </summary>
/// <remarks>
/// The vertices at "infinity" are assigned finite coordinates, which are
/// used by the point location routines, but (mostly) ignored by the
/// Delaunay edge flip routines.
/// </remarks>
void GetBoundingBox()
{
Otri inftri = default(Otri); // Handle for the triangular bounding box.
Rectangle box = mesh.bounds;
// Find the width (or height, whichever is larger) of the triangulation.
double width = box.Width;
if (box.Height > width)
{
width = box.Height;
}
if (width == 0.0)
{
width = 1.0;
}
// Create the vertices of the bounding box.
mesh.infvertex1 = new Vertex(box.Left - 50.0 * width, box.Bottom - 40.0 * width);
mesh.infvertex2 = new Vertex(box.Right + 50.0 * width, box.Bottom - 40.0 * width);
mesh.infvertex3 = new Vertex(0.5 * (box.Left + box.Right), box.Top + 60.0 * width);
// Create the bounding box.
mesh.MakeTriangle(ref inftri);
inftri.SetOrg(mesh.infvertex1);
inftri.SetDest(mesh.infvertex2);
inftri.SetApex(mesh.infvertex3);
// Link dummytri to the bounding box so we can always find an
// edge to begin searching (point location) from.
mesh.dummytri.neighbors[0] = inftri;
}
/// <summary>
/// Remove the "infinite" bounding triangle, setting boundary markers as appropriate.
/// </summary>
/// <returns>Returns the number of edges on the convex hull of the triangulation.</returns>
/// <remarks>
/// The triangular bounding box has three boundary triangles (one for each
/// side of the bounding box), and a bunch of triangles fanning out from
/// the three bounding box vertices (one triangle for each edge of the
/// convex hull of the inner mesh). This routine removes these triangles.
/// </remarks>
int RemoveBox()
{
Otri deadtriangle = default(Otri);
Otri searchedge = default(Otri);
Otri checkedge = default(Otri);
Otri nextedge = default(Otri), finaledge = default(Otri), dissolveedge = default(Otri);
Vertex markorg;
int hullsize;
bool noPoly = !mesh.behavior.Poly;
// Find a boundary triangle.
nextedge.tri = mesh.dummytri;
nextedge.orient = 0;
nextedge.Sym();
// Mark a place to stop.
nextedge.Lprev(ref finaledge);
nextedge.Lnext();
nextedge.Sym();
// Find a triangle (on the boundary of the vertex set) that isn't
// a bounding box triangle.
nextedge.Lprev(ref searchedge);
searchedge.Sym();
// Check whether nextedge is another boundary triangle
// adjacent to the first one.
nextedge.Lnext(ref checkedge);
checkedge.Sym();
if (checkedge.tri.id == Mesh.DUMMY)
{
// Go on to the next triangle. There are only three boundary
// triangles, and this next triangle cannot be the third one,
// so it's safe to stop here.
searchedge.Lprev();
searchedge.Sym();
}
// Find a new boundary edge to search from, as the current search
// edge lies on a bounding box triangle and will be deleted.
mesh.dummytri.neighbors[0] = searchedge;
hullsize = -2;
while (!nextedge.Equals(finaledge))
{
hullsize++;
nextedge.Lprev(ref dissolveedge);
dissolveedge.Sym();
// If not using a PSLG, the vertices should be marked now.
// (If using a PSLG, markhull() will do the job.)
if (noPoly)
{
// Be careful! One must check for the case where all the input
// vertices are collinear, and thus all the triangles are part of
// the bounding box. Otherwise, the setvertexmark() call below
// will cause a bad pointer reference.
if (dissolveedge.tri.id != Mesh.DUMMY)
{
markorg = dissolveedge.Org();
if (markorg.label == 0)
{
markorg.label = 1;
}
}
}
// Disconnect the bounding box triangle from the mesh triangle.
dissolveedge.Dissolve(mesh.dummytri);
nextedge.Lnext(ref deadtriangle);
deadtriangle.Sym(ref nextedge);
// Get rid of the bounding box triangle.
mesh.TriangleDealloc(deadtriangle.tri);
// Do we need to turn the corner?
if (nextedge.tri.id == Mesh.DUMMY)
{
// Turn the corner.
dissolveedge.Copy(ref nextedge);
}
}
mesh.TriangleDealloc(finaledge.tri);
return hullsize;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ffa994dccc684238af9f51c4b71a053
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,812 @@
// -----------------------------------------------------------------------
// <copyright file="SweepLine.cs">
// 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/
// </copyright>
// -----------------------------------------------------------------------
namespace UnityEngine.U2D.Animation.TriangleNet
.Meshing.Algorithm
{
using System;
using System.Collections.Generic;
using Animation.TriangleNet.Topology;
using Animation.TriangleNet.Geometry;
using Animation.TriangleNet.Tools;
/// <summary>
/// Builds a delaunay triangulation using the sweepline algorithm.
/// </summary>
internal class SweepLine : ITriangulator
{
static int randomseed = 1;
static int SAMPLERATE = 10;
static int randomnation(int choices)
{
randomseed = (randomseed * 1366 + 150889) % 714025;
return randomseed / (714025 / choices + 1);
}
IPredicates predicates;
Mesh mesh;
double xminextreme; // Nonexistent x value used as a flag in sweepline.
List<SplayNode> splaynodes;
public IMesh Triangulate(IList<Vertex> points, Configuration config)
{
this.predicates = config.Predicates();
this.mesh = new Mesh(config);
this.mesh.TransferNodes(points);
// Nonexistent x value used as a flag to mark circle events in sweepline
// Delaunay algorithm.
xminextreme = 10 * mesh.bounds.Left - 9 * mesh.bounds.Right;
SweepEvent[] eventheap;
SweepEvent nextevent;
SweepEvent newevent;
SplayNode splayroot;
Otri bottommost = default(Otri);
Otri searchtri = default(Otri);
Otri fliptri;
Otri lefttri = default(Otri);
Otri righttri = default(Otri);
Otri farlefttri = default(Otri);
Otri farrighttri = default(Otri);
Otri inserttri = default(Otri);
Vertex firstvertex, secondvertex;
Vertex nextvertex, lastvertex;
Vertex connectvertex;
Vertex leftvertex, midvertex, rightvertex;
double lefttest, righttest;
int heapsize;
bool check4events, farrightflag = false;
splaynodes = new List<SplayNode>();
splayroot = null;
heapsize = points.Count;
CreateHeap(out eventheap, heapsize);//, out events, out freeevents);
mesh.MakeTriangle(ref lefttri);
mesh.MakeTriangle(ref righttri);
lefttri.Bond(ref righttri);
lefttri.Lnext();
righttri.Lprev();
lefttri.Bond(ref righttri);
lefttri.Lnext();
righttri.Lprev();
lefttri.Bond(ref righttri);
firstvertex = eventheap[0].vertexEvent;
HeapDelete(eventheap, heapsize, 0);
heapsize--;
do
{
if (heapsize == 0)
{
Log.Instance.Error("Input vertices are all identical.", "SweepLine.Triangulate()");
throw new Exception("Input vertices are all identical.");
}
secondvertex = eventheap[0].vertexEvent;
HeapDelete(eventheap, heapsize, 0);
heapsize--;
if ((firstvertex.x == secondvertex.x) &&
(firstvertex.y == secondvertex.y))
{
if (Log.Verbose)
{
Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + secondvertex.id + ").",
"SweepLine.Triangulate().1");
}
secondvertex.type = VertexType.UndeadVertex;
mesh.undeads++;
}
}
while ((firstvertex.x == secondvertex.x) &&
(firstvertex.y == secondvertex.y));
lefttri.SetOrg(firstvertex);
lefttri.SetDest(secondvertex);
righttri.SetOrg(secondvertex);
righttri.SetDest(firstvertex);
lefttri.Lprev(ref bottommost);
lastvertex = secondvertex;
while (heapsize > 0)
{
nextevent = eventheap[0];
HeapDelete(eventheap, heapsize, 0);
heapsize--;
check4events = true;
if (nextevent.xkey < mesh.bounds.Left)
{
fliptri = nextevent.otriEvent;
fliptri.Oprev(ref farlefttri);
Check4DeadEvent(ref farlefttri, eventheap, ref heapsize);
fliptri.Onext(ref farrighttri);
Check4DeadEvent(ref farrighttri, eventheap, ref heapsize);
if (farlefttri.Equals(bottommost))
{
fliptri.Lprev(ref bottommost);
}
mesh.Flip(ref fliptri);
fliptri.SetApex(null);
fliptri.Lprev(ref lefttri);
fliptri.Lnext(ref righttri);
lefttri.Sym(ref farlefttri);
if (randomnation(SAMPLERATE) == 0)
{
fliptri.Sym();
leftvertex = fliptri.Dest();
midvertex = fliptri.Apex();
rightvertex = fliptri.Org();
splayroot = CircleTopInsert(splayroot, lefttri, leftvertex, midvertex, rightvertex, nextevent.ykey);
}
}
else
{
nextvertex = nextevent.vertexEvent;
if ((nextvertex.x == lastvertex.x) &&
(nextvertex.y == lastvertex.y))
{
if (Log.Verbose)
{
Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + nextvertex.id + ").",
"SweepLine.Triangulate().2");
}
nextvertex.type = VertexType.UndeadVertex;
mesh.undeads++;
check4events = false;
}
else
{
lastvertex = nextvertex;
splayroot = FrontLocate(splayroot, bottommost, nextvertex, ref searchtri, ref farrightflag);
//bottommost.Copy(ref searchtri);
//farrightflag = false;
//while (!farrightflag && RightOfHyperbola(ref searchtri, nextvertex))
//{
// searchtri.OnextSelf();
// farrightflag = searchtri.Equal(bottommost);
//}
Check4DeadEvent(ref searchtri, eventheap, ref heapsize);
searchtri.Copy(ref farrighttri);
searchtri.Sym(ref farlefttri);
mesh.MakeTriangle(ref lefttri);
mesh.MakeTriangle(ref righttri);
connectvertex = farrighttri.Dest();
lefttri.SetOrg(connectvertex);
lefttri.SetDest(nextvertex);
righttri.SetOrg(nextvertex);
righttri.SetDest(connectvertex);
lefttri.Bond(ref righttri);
lefttri.Lnext();
righttri.Lprev();
lefttri.Bond(ref righttri);
lefttri.Lnext();
righttri.Lprev();
lefttri.Bond(ref farlefttri);
righttri.Bond(ref farrighttri);
if (!farrightflag && farrighttri.Equals(bottommost))
{
lefttri.Copy(ref bottommost);
}
if (randomnation(SAMPLERATE) == 0)
{
splayroot = SplayInsert(splayroot, lefttri, nextvertex);
}
else if (randomnation(SAMPLERATE) == 0)
{
righttri.Lnext(ref inserttri);
splayroot = SplayInsert(splayroot, inserttri, nextvertex);
}
}
}
if (check4events)
{
leftvertex = farlefttri.Apex();
midvertex = lefttri.Dest();
rightvertex = lefttri.Apex();
lefttest = predicates.CounterClockwise(leftvertex, midvertex, rightvertex);
if (lefttest > 0.0)
{
newevent = new SweepEvent();
newevent.xkey = xminextreme;
newevent.ykey = CircleTop(leftvertex, midvertex, rightvertex, lefttest);
newevent.otriEvent = lefttri;
HeapInsert(eventheap, heapsize, newevent);
heapsize++;
lefttri.SetOrg(new SweepEventVertex(newevent));
}
leftvertex = righttri.Apex();
midvertex = righttri.Org();
rightvertex = farrighttri.Apex();
righttest = predicates.CounterClockwise(leftvertex, midvertex, rightvertex);
if (righttest > 0.0)
{
newevent = new SweepEvent();
newevent.xkey = xminextreme;
newevent.ykey = CircleTop(leftvertex, midvertex, rightvertex, righttest);
newevent.otriEvent = farrighttri;
HeapInsert(eventheap, heapsize, newevent);
heapsize++;
farrighttri.SetOrg(new SweepEventVertex(newevent));
}
}
}
splaynodes.Clear();
bottommost.Lprev();
this.mesh.hullsize = RemoveGhosts(ref bottommost);
return this.mesh;
}
#region Heap
void HeapInsert(SweepEvent[] heap, int heapsize, SweepEvent newevent)
{
double eventx, eventy;
int eventnum;
int parent;
bool notdone;
eventx = newevent.xkey;
eventy = newevent.ykey;
eventnum = heapsize;
notdone = eventnum > 0;
while (notdone)
{
parent = (eventnum - 1) >> 1;
if ((heap[parent].ykey < eventy) ||
((heap[parent].ykey == eventy)
&& (heap[parent].xkey <= eventx)))
{
notdone = false;
}
else
{
heap[eventnum] = heap[parent];
heap[eventnum].heapposition = eventnum;
eventnum = parent;
notdone = eventnum > 0;
}
}
heap[eventnum] = newevent;
newevent.heapposition = eventnum;
}
void Heapify(SweepEvent[] heap, int heapsize, int eventnum)
{
SweepEvent thisevent;
double eventx, eventy;
int leftchild, rightchild;
int smallest;
bool notdone;
thisevent = heap[eventnum];
eventx = thisevent.xkey;
eventy = thisevent.ykey;
leftchild = 2 * eventnum + 1;
notdone = leftchild < heapsize;
while (notdone)
{
if ((heap[leftchild].ykey < eventy) ||
((heap[leftchild].ykey == eventy)
&& (heap[leftchild].xkey < eventx)))
{
smallest = leftchild;
}
else
{
smallest = eventnum;
}
rightchild = leftchild + 1;
if (rightchild < heapsize)
{
if ((heap[rightchild].ykey < heap[smallest].ykey) ||
((heap[rightchild].ykey == heap[smallest].ykey)
&& (heap[rightchild].xkey < heap[smallest].xkey)))
{
smallest = rightchild;
}
}
if (smallest == eventnum)
{
notdone = false;
}
else
{
heap[eventnum] = heap[smallest];
heap[eventnum].heapposition = eventnum;
heap[smallest] = thisevent;
thisevent.heapposition = smallest;
eventnum = smallest;
leftchild = 2 * eventnum + 1;
notdone = leftchild < heapsize;
}
}
}
void HeapDelete(SweepEvent[] heap, int heapsize, int eventnum)
{
SweepEvent moveevent;
double eventx, eventy;
int parent;
bool notdone;
moveevent = heap[heapsize - 1];
if (eventnum > 0)
{
eventx = moveevent.xkey;
eventy = moveevent.ykey;
do
{
parent = (eventnum - 1) >> 1;
if ((heap[parent].ykey < eventy) ||
((heap[parent].ykey == eventy)
&& (heap[parent].xkey <= eventx)))
{
notdone = false;
}
else
{
heap[eventnum] = heap[parent];
heap[eventnum].heapposition = eventnum;
eventnum = parent;
notdone = eventnum > 0;
}
}
while (notdone);
}
heap[eventnum] = moveevent;
moveevent.heapposition = eventnum;
Heapify(heap, heapsize - 1, eventnum);
}
void CreateHeap(out SweepEvent[] eventheap, int size)
{
Vertex thisvertex;
int maxevents;
int i;
SweepEvent evt;
maxevents = (3 * size) / 2;
eventheap = new SweepEvent[maxevents];
i = 0;
foreach (var v in mesh.vertices.Values)
{
thisvertex = v;
evt = new SweepEvent();
evt.vertexEvent = thisvertex;
evt.xkey = thisvertex.x;
evt.ykey = thisvertex.y;
HeapInsert(eventheap, i++, evt);
}
}
#endregion
#region Splaytree
SplayNode Splay(SplayNode splaytree, Point searchpoint, ref Otri searchtri)
{
SplayNode child, grandchild;
SplayNode lefttree, righttree;
SplayNode leftright;
Vertex checkvertex;
bool rightofroot, rightofchild;
if (splaytree == null)
{
return null;
}
checkvertex = splaytree.keyedge.Dest();
if (checkvertex == splaytree.keydest)
{
rightofroot = RightOfHyperbola(ref splaytree.keyedge, searchpoint);
if (rightofroot)
{
splaytree.keyedge.Copy(ref searchtri);
child = splaytree.rchild;
}
else
{
child = splaytree.lchild;
}
if (child == null)
{
return splaytree;
}
checkvertex = child.keyedge.Dest();
if (checkvertex != child.keydest)
{
child = Splay(child, searchpoint, ref searchtri);
if (child == null)
{
if (rightofroot)
{
splaytree.rchild = null;
}
else
{
splaytree.lchild = null;
}
return splaytree;
}
}
rightofchild = RightOfHyperbola(ref child.keyedge, searchpoint);
if (rightofchild)
{
child.keyedge.Copy(ref searchtri);
grandchild = Splay(child.rchild, searchpoint, ref searchtri);
child.rchild = grandchild;
}
else
{
grandchild = Splay(child.lchild, searchpoint, ref searchtri);
child.lchild = grandchild;
}
if (grandchild == null)
{
if (rightofroot)
{
splaytree.rchild = child.lchild;
child.lchild = splaytree;
}
else
{
splaytree.lchild = child.rchild;
child.rchild = splaytree;
}
return child;
}
if (rightofchild)
{
if (rightofroot)
{
splaytree.rchild = child.lchild;
child.lchild = splaytree;
}
else
{
splaytree.lchild = grandchild.rchild;
grandchild.rchild = splaytree;
}
child.rchild = grandchild.lchild;
grandchild.lchild = child;
}
else
{
if (rightofroot)
{
splaytree.rchild = grandchild.lchild;
grandchild.lchild = splaytree;
}
else
{
splaytree.lchild = child.rchild;
child.rchild = splaytree;
}
child.lchild = grandchild.rchild;
grandchild.rchild = child;
}
return grandchild;
}
else
{
lefttree = Splay(splaytree.lchild, searchpoint, ref searchtri);
righttree = Splay(splaytree.rchild, searchpoint, ref searchtri);
splaynodes.Remove(splaytree);
if (lefttree == null)
{
return righttree;
}
else if (righttree == null)
{
return lefttree;
}
else if (lefttree.rchild == null)
{
lefttree.rchild = righttree.lchild;
righttree.lchild = lefttree;
return righttree;
}
else if (righttree.lchild == null)
{
righttree.lchild = lefttree.rchild;
lefttree.rchild = righttree;
return lefttree;
}
else
{
// printf("Holy Toledo!!!\n");
leftright = lefttree.rchild;
while (leftright.rchild != null)
{
leftright = leftright.rchild;
}
leftright.rchild = righttree;
return lefttree;
}
}
}
SplayNode SplayInsert(SplayNode splayroot, Otri newkey, Point searchpoint)
{
SplayNode newsplaynode;
newsplaynode = new SplayNode(); //poolalloc(m.splaynodes);
splaynodes.Add(newsplaynode);
newkey.Copy(ref newsplaynode.keyedge);
newsplaynode.keydest = newkey.Dest();
if (splayroot == null)
{
newsplaynode.lchild = null;
newsplaynode.rchild = null;
}
else if (RightOfHyperbola(ref splayroot.keyedge, searchpoint))
{
newsplaynode.lchild = splayroot;
newsplaynode.rchild = splayroot.rchild;
splayroot.rchild = null;
}
else
{
newsplaynode.lchild = splayroot.lchild;
newsplaynode.rchild = splayroot;
splayroot.lchild = null;
}
return newsplaynode;
}
SplayNode FrontLocate(SplayNode splayroot, Otri bottommost, Vertex searchvertex,
ref Otri searchtri, ref bool farright)
{
bool farrightflag;
bottommost.Copy(ref searchtri);
splayroot = Splay(splayroot, searchvertex, ref searchtri);
farrightflag = false;
while (!farrightflag && RightOfHyperbola(ref searchtri, searchvertex))
{
searchtri.Onext();
farrightflag = searchtri.Equals(bottommost);
}
farright = farrightflag;
return splayroot;
}
SplayNode CircleTopInsert(SplayNode splayroot, Otri newkey,
Vertex pa, Vertex pb, Vertex pc, double topy)
{
double ccwabc;
double xac, yac, xbc, ybc;
double aclen2, bclen2;
Point searchpoint = new Point(); // TODO: mesh.nextras
Otri dummytri = default(Otri);
ccwabc = predicates.CounterClockwise(pa, pb, pc);
xac = pa.x - pc.x;
yac = pa.y - pc.y;
xbc = pb.x - pc.x;
ybc = pb.y - pc.y;
aclen2 = xac * xac + yac * yac;
bclen2 = xbc * xbc + ybc * ybc;
searchpoint.x = pc.x - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
searchpoint.y = topy;
return SplayInsert(Splay(splayroot, searchpoint, ref dummytri), newkey, searchpoint);
}
#endregion
bool RightOfHyperbola(ref Otri fronttri, Point newsite)
{
Vertex leftvertex, rightvertex;
double dxa, dya, dxb, dyb;
Statistic.HyperbolaCount++;
leftvertex = fronttri.Dest();
rightvertex = fronttri.Apex();
if ((leftvertex.y < rightvertex.y) ||
((leftvertex.y == rightvertex.y) &&
(leftvertex.x < rightvertex.x)))
{
if (newsite.x >= rightvertex.x)
{
return true;
}
}
else
{
if (newsite.x <= leftvertex.x)
{
return false;
}
}
dxa = leftvertex.x - newsite.x;
dya = leftvertex.y - newsite.y;
dxb = rightvertex.x - newsite.x;
dyb = rightvertex.y - newsite.y;
return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya);
}
double CircleTop(Vertex pa, Vertex pb, Vertex pc, double ccwabc)
{
double xac, yac, xbc, ybc, xab, yab;
double aclen2, bclen2, ablen2;
Statistic.CircleTopCount++;
xac = pa.x - pc.x;
yac = pa.y - pc.y;
xbc = pb.x - pc.x;
ybc = pb.y - pc.y;
xab = pa.x - pb.x;
yab = pa.y - pb.y;
aclen2 = xac * xac + yac * yac;
bclen2 = xbc * xbc + ybc * ybc;
ablen2 = xab * xab + yab * yab;
return pc.y + (xac * bclen2 - xbc * aclen2 + Math.Sqrt(aclen2 * bclen2 * ablen2)) / (2.0 * ccwabc);
}
void Check4DeadEvent(ref Otri checktri, SweepEvent[] eventheap, ref int heapsize)
{
SweepEvent deadevent;
SweepEventVertex eventvertex;
int eventnum = -1;
eventvertex = checktri.Org() as SweepEventVertex;
if (eventvertex != null)
{
deadevent = eventvertex.evt;
eventnum = deadevent.heapposition;
HeapDelete(eventheap, heapsize, eventnum);
heapsize--;
checktri.SetOrg(null);
}
}
/// <summary>
/// Removes ghost triangles.
/// </summary>
/// <param name="startghost"></param>
/// <returns>Number of vertices on the hull.</returns>
int RemoveGhosts(ref Otri startghost)
{
Otri searchedge = default(Otri);
Otri dissolveedge = default(Otri);
Otri deadtriangle = default(Otri);
Vertex markorg;
int hullsize;
bool noPoly = !mesh.behavior.Poly;
var dummytri = mesh.dummytri;
// Find an edge on the convex hull to start point location from.
startghost.Lprev(ref searchedge);
searchedge.Sym();
dummytri.neighbors[0] = searchedge;
// Remove the bounding box and count the convex hull edges.
startghost.Copy(ref dissolveedge);
hullsize = 0;
do
{
hullsize++;
dissolveedge.Lnext(ref deadtriangle);
dissolveedge.Lprev();
dissolveedge.Sym();
// If no PSLG is involved, set the boundary markers of all the vertices
// on the convex hull. If a PSLG is used, this step is done later.
if (noPoly)
{
// Watch out for the case where all the input vertices are collinear.
if (dissolveedge.tri.id != Mesh.DUMMY)
{
markorg = dissolveedge.Org();
if (markorg.label == 0)
{
markorg.label = 1;
}
}
}
// Remove a bounding triangle from a convex hull triangle.
dissolveedge.Dissolve(dummytri);
// Find the next bounding triangle.
deadtriangle.Sym(ref dissolveedge);
// Delete the bounding triangle.
mesh.TriangleDealloc(deadtriangle.tri);
}
while (!dissolveedge.Equals(startghost));
return hullsize;
}
#region Internal classes
/// <summary>
/// A node in a heap used to store events for the sweepline Delaunay algorithm.
/// </summary>
/// <remarks>
/// Only used in the sweepline algorithm.
///
/// Nodes do not point directly to their parents or children in the heap. Instead, each
/// node knows its position in the heap, and can look up its parent and children in a
/// separate array. To distinguish site events from circle events, all circle events are
/// given an invalid (smaller than 'xmin') x-coordinate 'xkey'.
/// </remarks>
class SweepEvent
{
public double xkey, ykey; // Coordinates of the event.
public Vertex vertexEvent; // Vertex event.
public Otri otriEvent; // Circle event.
public int heapposition; // Marks this event's position in the heap.
}
/// <summary>
/// Introducing a new class which aggregates a sweep event is the easiest way
/// to handle the pointer magic of the original code (casting a sweep event
/// to vertex etc.).
/// </summary>
class SweepEventVertex : Vertex
{
public SweepEvent evt;
public SweepEventVertex(SweepEvent e)
{
evt = e;
}
}
/// <summary>
/// A node in the splay tree.
/// </summary>
/// <remarks>
/// Only used in the sweepline algorithm.
///
/// Each node holds an oriented ghost triangle that represents a boundary edge
/// of the growing triangulation. When a circle event covers two boundary edges
/// with a triangle, so that they are no longer boundary edges, those edges are
/// not immediately deleted from the tree; rather, they are lazily deleted when
/// they are next encountered. (Since only a random sample of boundary edges are
/// kept in the tree, lazy deletion is faster.) 'keydest' is used to verify that
/// a triangle is still the same as when it entered the splay tree; if it has
/// been rotated (due to a circle event), it no longer represents a boundary
/// edge and should be deleted.
/// </remarks>
class SplayNode
{
public Otri keyedge; // Lprev of an edge on the front.
public Vertex keydest; // Used to verify that splay node is still live.
public SplayNode lchild, rchild; // Children in splay tree.
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e71212805f8c4fc7a7db8dd552b73a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: