More code reorganization (6)

git-svn-id: https://triangle.svn.codeplex.com/svn@75300 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5
This commit is contained in:
SND\wo80_cp
2014-08-27 16:43:48 +00:00
parent f022884735
commit f674161dff
38 changed files with 1112 additions and 1072 deletions
+6 -7
View File
@@ -5,6 +5,7 @@ using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Tools;
using TriangleNet.Topology;
namespace MeshExplorer
{
@@ -12,7 +13,7 @@ namespace MeshExplorer
{
Mesh mesh;
QuadTree tree;
OrientedTriangle current;
Otri current;
public FormTopology()
{
@@ -28,7 +29,7 @@ namespace MeshExplorer
topoControlView.PrimitiveCommandInvoked += PrimitiveCommandHandler;
current = new OrientedTriangle();
current = default(Otri);
}
void PrimitiveCommandHandler(object sender, GenericEventArgs<string> e)
@@ -46,12 +47,10 @@ namespace MeshExplorer
var tri = FindTriangleAt(((float)p.X) / size.Width, ((float)p.Y) / size.Height);
current.Triangle = tri;
current.Orientation = 0;
current.Triangle = (Triangle)tri;
renderControl.Update(current);
topoControlView.SetTriangle(current);
topoControlView.SetTriangle(current.Triangle);
}
private ITriangle FindTriangleAt(float x, float y)
@@ -110,7 +109,7 @@ namespace MeshExplorer
}
renderControl.Update(current);
topoControlView.SetTriangle(current);
topoControlView.SetTriangle(current.Triangle);
}
}
}
@@ -120,8 +120,6 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Topology\OrientedTriangle.cs" />
<Compile Include="Topology\RectanglePolygon.cs" />
<Compile Include="Topology\TopologyControlView.cs">
<SubType>UserControl</SubType>
</Compile>
@@ -1,158 +0,0 @@
namespace MeshExplorer.Topology
{
using TriangleNet.Topology;
using TriangleNet.Geometry;
public class OrientedTriangle
{
/// <summary>
///
/// </summary>
public ITriangle Triangle { get; set; }
/// <summary>
/// Ranges from 0 to 2.
/// </summary>
public int Orientation { get; set; }
#region Oriented triangle primitives
// For fast access
static readonly int[] plus1Mod3 = { 1, 2, 0 };
static readonly int[] minus1Mod3 = { 2, 0, 1 };
/// <summary>
/// Find the abutting triangle; same edge. [sym(abc) -> ba*]
/// </summary>
public void Sym()
{
if (this.Triangle == null)
{
return;
}
var org = this.Org();
Triangle = Triangle.GetNeighbor(Orientation);
Orientation = GetOrientation(Triangle, org.ID);
}
/// <summary>
/// Find the next edge (counterclockwise) of a triangle. [lnext(abc) -> bca]
/// </summary>
public void Lnext()
{
Orientation = plus1Mod3[Orientation];
}
/// <summary>
/// Find the previous edge (clockwise) of a triangle. [lprev(abc) -> cab]
/// </summary>
public void Lprev()
{
Orientation = minus1Mod3[Orientation];
}
/// <summary>
/// Find the next edge counterclockwise with the same origin. [onext(abc) -> ac*]
/// </summary>
public void Onext()
{
Lprev();
Sym();
}
/// <summary>
/// Find the next edge clockwise with the same origin. [oprev(abc) -> a*b]
/// </summary>
public void Oprev()
{
Sym();
Lnext();
}
/// <summary>
/// Find the next edge counterclockwise with the same destination. [dnext(abc) -> *ba]
/// </summary>
public void Dnext()
{
Sym();
Lprev();
}
/// <summary>
/// Find the next edge clockwise with the same destination. [dprev(abc) -> cb*]
/// </summary>
public void Dprev()
{
Lnext();
Sym();
}
/// <summary>
/// Find the next edge (counterclockwise) of the adjacent triangle. [rnext(abc) -> *a*]
/// </summary>
public void Rnext()
{
Sym();
Lnext();
Sym();
}
/// <summary>
/// Find the previous edge (clockwise) of the adjacent triangle. [rprev(abc) -> b**]
/// </summary>
public void Rprev()
{
Sym();
Lprev();
Sym();
}
/// <summary>
/// Origin [org(abc) -> a]
/// </summary>
public Vertex Org()
{
return Triangle.GetVertex(plus1Mod3[Orientation]);
}
/// <summary>
/// Destination [dest(abc) -> b]
/// </summary>
public Vertex Dest()
{
return Triangle.GetVertex(minus1Mod3[Orientation]);
}
/// <summary>
/// Apex [apex(abc) -> c]
/// </summary>
public Vertex Apex()
{
return Triangle.GetVertex(Orientation);
}
#endregion
private int GetOrientation(ITriangle tri, int org)
{
if (tri == null)
{
return 0;
}
if (tri.P0 == org)
{
return 1;
}
if (tri.P1 == org)
{
return 2;
}
return 0;
}
}
}
@@ -1,47 +0,0 @@
namespace MeshExplorer.Topology
{
using TriangleNet.Geometry;
internal static class RectanglePolygon
{
public static IPolygon Generate(int n, double bounds = 10.0)
{
var geometry = new Polygon((n + 1) * (n + 1));
double x, y, d = 2 * bounds / n;
int mark = 0;
for (int i = 0; i <= n; i++)
{
y = -bounds + i * d;
for (int j = 0; j <= n; j++)
{
x = -bounds + j * d;
geometry.Add(new Vertex(x, y, mark));
}
}
// Add boundary segments
for (int i = 0; i < n; i++)
{
// Bottom
geometry.Add(new Edge(i, i + 1));
// Right
geometry.Add(new Edge(i * (n + 1) + n, (i + 1) * (n + 1) + n));
// Top
geometry.Add(new Edge(n * (n + 1) + i, n * (n + 1) + (i + 1)));
// Left
geometry.Add(new Edge(i * (n + 1), (i + 1) * (n + 1)));
}
return geometry;
}
}
}
@@ -22,25 +22,23 @@ namespace MeshExplorer.Topology
lbPosition.Text = String.Format(nfi, "X: {0:0.0}, Y: {1:0.0}", p.X, p.Y);
}
public void SetTriangle(OrientedTriangle tri)
public void SetTriangle(ITriangle tri)
{
var t = tri.Triangle;
if (t != null)
if (tri != null)
{
lbTriangle.Text = t.ID.ToString();
lbTriangle.Text = tri.ID.ToString();
lbV0.Text = t.P0.ToString();
lbV1.Text = t.P1.ToString();
lbV2.Text = t.P2.ToString();
lbV0.Text = tri.P0.ToString();
lbV1.Text = tri.P1.ToString();
lbV2.Text = tri.P2.ToString();
lbN0.Text = t.N0.ToString();
lbN1.Text = t.N1.ToString();
lbN2.Text = t.N2.ToString();
lbN0.Text = tri.N0.ToString();
lbN1.Text = tri.N1.ToString();
lbN2.Text = tri.N2.ToString();
lbS0.Text = GetSegmentString(t.GetSegment(0));
lbS1.Text = GetSegmentString(t.GetSegment(1));
lbS2.Text = GetSegmentString(t.GetSegment(2));
lbS0.Text = GetSegmentString(tri.GetSegment(0));
lbS1.Text = GetSegmentString(tri.GetSegment(1));
lbS2.Text = GetSegmentString(tri.GetSegment(2));
}
else
{
@@ -7,6 +7,7 @@ namespace MeshExplorer.Topology
using System.Windows.Forms;
using TriangleNet;
using TriangleNet.Rendering;
using TriangleNet.Topology;
public class TopologyRenderControl : Control
{
@@ -57,9 +58,17 @@ namespace MeshExplorer.Topology
this.Render();
}
public void Update(OrientedTriangle otri)
public void Update(Otri otri)
{
renderer.SelectTriangle(otri.Triangle == null ? null : otri);
if (otri.Triangle == null || otri.Triangle.ID < 0)
{
renderer.SelectTriangle(null, null, null);
}
else
{
renderer.SelectTriangle(otri.Triangle, otri.Org(), otri.Dest());
}
this.Render();
}
@@ -6,6 +6,7 @@ namespace MeshExplorer.Topology
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Rendering;
using TriangleNet.Topology;
public class TopologyRenderer
{
@@ -25,7 +26,8 @@ namespace MeshExplorer.Topology
Font font, fontTri;
OrientedTriangle selection;
ITriangle currentTri;
Vertex currentOrg, currentDest;
/// <summary>
/// Initializes a new instance of the <see cref="MeshRenderer" /> class.
@@ -78,9 +80,11 @@ namespace MeshExplorer.Topology
}
}
public void SelectTriangle(OrientedTriangle tri)
public void SelectTriangle(ITriangle tri, Vertex org, Vertex dest)
{
selection = tri;
currentTri = tri;
currentOrg = org;
currentDest = dest;
}
#region Helpers
@@ -136,7 +140,7 @@ namespace MeshExplorer.Topology
int n = points.Length;
PointF pt;
int id = selection != null ? selection.Org().ID : -1;
int id = currentOrg != null ? currentOrg.ID : -1;
for (int i = 0; i < n; i++)
{
@@ -245,12 +249,12 @@ namespace MeshExplorer.Topology
private void RenderSelectedEdge(Graphics g)
{
if (selection != null)
if (currentTri != null)
{
PointF p0, p1;
p0 = points[selection.Org().ID];
p1 = points[selection.Dest().ID];
p0 = points[currentOrg.ID];
p1 = points[currentDest.ID];
zoom.WorldToScreen(ref p0);
zoom.WorldToScreen(ref p1);
@@ -261,15 +265,13 @@ namespace MeshExplorer.Topology
private void RenderSelectedTriangle(Graphics g)
{
if (selection != null)
if (currentTri != null)
{
var tri = selection.Triangle;
var p = new PointF[3];
p[0] = points[tri.P0];
p[1] = points[tri.P1];
p[2] = points[tri.P2];
p[0] = points[currentTri.P0];
p[1] = points[currentTri.P1];
p[2] = points[currentTri.P2];
zoom.WorldToScreen(ref p[0]);
zoom.WorldToScreen(ref p[1]);
+5 -5
View File
@@ -197,7 +197,7 @@ namespace TriangleNet.IO
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
p1 = tri.Org();
p2 = tri.Dest();
@@ -208,19 +208,19 @@ namespace TriangleNet.IO
h3 = (p3 == null) ? -1 : p3.hash;
// Triangle number, indices for three vertices.
stream.Write("{0} {1} {2} {3}", tri.triangle.hash, h1, h2, h3);
stream.Write("{0} {1} {2} {3}", tri.tri.hash, h1, h2, h3);
tri.orient = 1;
tri.Sym(ref trisym);
n1 = trisym.triangle.hash;
n1 = trisym.tri.hash;
tri.orient = 2;
tri.Sym(ref trisym);
n2 = trisym.triangle.hash;
n2 = trisym.tri.hash;
tri.orient = 0;
tri.Sym(ref trisym);
n3 = trisym.triangle.hash;
n3 = trisym.tri.hash;
// Neighboring triangle numbers.
stream.WriteLine(" {0} {1} {2}", n1, n2, n3);
+18 -18
View File
@@ -155,7 +155,7 @@ namespace TriangleNet.IO
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
p1 = tri.Org();
p2 = tri.Dest();
@@ -166,7 +166,7 @@ namespace TriangleNet.IO
if (regions)
{
writer.Write(" {0}", tri.triangle.region);
writer.Write(" {0}", tri.tri.region);
}
writer.WriteLine();
@@ -362,12 +362,12 @@ namespace TriangleNet.IO
// considered only once.
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
tri.Sym(ref trisym);
if ((tri.triangle.id < trisym.triangle.id) || (trisym.triangle.id == Triangle.EmptyID))
if ((tri.tri.id < trisym.tri.id) || (trisym.tri.id == Triangle.EmptyID))
{
p1 = tri.Org();
p2 = tri.Dest();
@@ -378,7 +378,7 @@ namespace TriangleNet.IO
// If there's no subsegment, the boundary marker is zero.
if (behavior.useSegments)
{
tri.SegPivot(ref checkmark);
tri.Pivot(ref checkmark);
if (checkmark.seg == Segment.Empty)
{
@@ -393,7 +393,7 @@ namespace TriangleNet.IO
else
{
writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id,
trisym.triangle.id == Triangle.EmptyID ? "1" : "0");
trisym.tri.id == Triangle.EmptyID ? "1" : "0");
}
}
else
@@ -429,19 +429,19 @@ namespace TriangleNet.IO
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
tri.orient = 1;
tri.Sym(ref trisym);
n1 = trisym.triangle.id;
n1 = trisym.tri.id;
tri.orient = 2;
tri.Sym(ref trisym);
n2 = trisym.triangle.id;
n2 = trisym.tri.id;
tri.orient = 0;
tri.Sym(ref trisym);
n3 = trisym.triangle.id;
n3 = trisym.tri.id;
// Triangle number, neighboring triangle numbers.
writer.WriteLine("{0} {1} {2} {3}", i++, n1, n2, n3);
@@ -481,7 +481,7 @@ namespace TriangleNet.IO
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
torg = tri.Org();
tdest = tri.Dest();
tapex = tri.Apex();
@@ -501,7 +501,7 @@ namespace TriangleNet.IO
}
writer.WriteLine();
tri.triangle.id = index++;
tri.tri.id = index++;
}
@@ -517,17 +517,17 @@ namespace TriangleNet.IO
// considered only once.
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
tri.Sym(ref trisym);
if ((tri.triangle.id < trisym.triangle.id) || (trisym.triangle.id == Triangle.EmptyID))
if ((tri.tri.id < trisym.tri.id) || (trisym.tri.id == Triangle.EmptyID))
{
// Find the number of this triangle (and Voronoi vertex).
p1 = tri.triangle.id;
p1 = tri.tri.id;
if (trisym.triangle.id == Triangle.EmptyID)
if (trisym.tri.id == Triangle.EmptyID)
{
torg = tri.Org();
tdest = tri.Dest();
@@ -542,7 +542,7 @@ namespace TriangleNet.IO
else
{
// Find the number of the adjacent triangle (and Voronoi vertex).
p2 = trisym.triangle.id;
p2 = trisym.tri.id;
// Finite edge. Write indices of two endpoints.
writer.WriteLine("{0} {1} {2}", index, p1, p2);
}
@@ -599,7 +599,7 @@ namespace TriangleNet.IO
tri.orient = 0;
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
p1 = tri.Org();
p2 = tri.Dest();
+83 -83
View File
@@ -569,7 +569,7 @@ namespace TriangleNet
foreach (var t in this.triangles.Values)
{
tri.triangle = t;
tri.tri = t;
// Check all three vertices of the triangle.
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
@@ -593,7 +593,7 @@ namespace TriangleNet
tri.hash = this.hash_tri++;
tri.id = tri.hash;
newotri.triangle = tri;
newotri.tri = tri;
newotri.orient = 0;
triangles.Add(tri.hash, tri);
@@ -702,12 +702,12 @@ namespace TriangleNet
{
// Find the location of the vertex to be inserted. Check if a good
// starting triangle has already been provided by the caller.
if (searchtri.triangle.id == Triangle.EmptyID)
if (searchtri.tri.id == Triangle.EmptyID)
{
// Find a boundary triangle.
horiz.triangle = Triangle.Empty;
horiz.tri = Triangle.Empty;
horiz.orient = 0;
horiz.SymSelf();
horiz.Sym();
// Search for a triangle containing 'newvertex'.
intersect = locator.Locate(newvertex, ref horiz);
}
@@ -740,7 +740,7 @@ namespace TriangleNet
if (checksegments && (splitseg.seg == null))
{
// Check whether the vertex falls on a subsegment.
horiz.SegPivot(ref brokensubseg);
horiz.Pivot(ref brokensubseg);
if (brokensubseg.seg != Segment.Empty)
{
// The vertex falls on a subsegment, and hence will not be inserted.
@@ -752,7 +752,7 @@ namespace TriangleNet
// This subsegment may be split only if it is an
// internal boundary.
horiz.Sym(ref testtri);
enq = testtri.triangle.id != Triangle.EmptyID;
enq = testtri.tri.id != Triangle.EmptyID;
}
if (enq)
{
@@ -779,10 +779,10 @@ namespace TriangleNet
botright.Sym(ref botrcasing);
horiz.Sym(ref topright);
// Is there a second triangle? (Or does this edge lie on a boundary?)
mirrorflag = topright.triangle.id != Triangle.EmptyID;
mirrorflag = topright.tri.id != Triangle.EmptyID;
if (mirrorflag)
{
topright.LnextSelf();
topright.Lnext();
topright.Sym(ref toprcasing);
MakeTriangle(ref newtopright);
}
@@ -803,12 +803,12 @@ namespace TriangleNet
horiz.SetOrg(newvertex);
// Set the region of a new triangle.
newbotright.triangle.region = botright.triangle.region;
newbotright.tri.region = botright.tri.region;
if (behavior.VarArea)
{
// Set the area constraint of a new triangle.
newbotright.triangle.area = botright.triangle.area;
newbotright.tri.area = botright.tri.area;
}
if (mirrorflag)
@@ -820,12 +820,12 @@ namespace TriangleNet
topright.SetOrg(newvertex);
// Set the region of another new triangle.
newtopright.triangle.region = topright.triangle.region;
newtopright.tri.region = topright.tri.region;
if (behavior.VarArea)
{
// Set the area constraint of another new triangle.
newtopright.triangle.area = topright.triangle.area;
newtopright.tri.area = topright.tri.area;
}
}
@@ -833,7 +833,7 @@ namespace TriangleNet
// to the new triangle(s).
if (checksegments)
{
botright.SegPivot(ref botrsubseg);
botright.Pivot(ref botrsubseg);
if (botrsubseg.seg != Segment.Empty)
{
@@ -843,7 +843,7 @@ namespace TriangleNet
if (mirrorflag)
{
topright.SegPivot(ref toprsubseg);
topright.Pivot(ref toprsubseg);
if (toprsubseg.seg != Segment.Empty)
{
topright.SegDissolve();
@@ -854,16 +854,16 @@ namespace TriangleNet
// Bond the new triangle(s) to the surrounding triangles.
newbotright.Bond(ref botrcasing);
newbotright.LprevSelf();
newbotright.Lprev();
newbotright.Bond(ref botright);
newbotright.LprevSelf();
newbotright.Lprev();
if (mirrorflag)
{
newtopright.Bond(ref toprcasing);
newtopright.LnextSelf();
newtopright.Lnext();
newtopright.Bond(ref topright);
newtopright.LnextSelf();
newtopright.Lnext();
newtopright.Bond(ref newbotright);
}
@@ -873,16 +873,16 @@ namespace TriangleNet
splitseg.SetDest(newvertex);
segmentorg = splitseg.SegOrg();
segmentdest = splitseg.SegDest();
splitseg.SymSelf();
splitseg.Sym();
splitseg.Pivot(ref rightsubseg);
InsertSubseg(ref newbotright, splitseg.seg.boundary);
newbotright.SegPivot(ref newsubseg);
newbotright.Pivot(ref newsubseg);
newsubseg.SetSegOrg(segmentorg);
newsubseg.SetSegDest(segmentdest);
splitseg.Bond(ref newsubseg);
newsubseg.SymSelf();
newsubseg.Sym();
newsubseg.Bond(ref rightsubseg);
splitseg.SymSelf();
splitseg.Sym();
// Transfer the subsegment's boundary marker to the vertex if required.
if (newvertex.mark == 0)
@@ -901,7 +901,7 @@ namespace TriangleNet
// Position 'horiz' on the first edge to check for
// the Delaunay property.
horiz.LnextSelf();
horiz.Lnext();
}
else
{
@@ -926,28 +926,28 @@ namespace TriangleNet
horiz.SetApex(newvertex);
// Set the region of the new triangles.
newbotleft.triangle.region = horiz.triangle.region;
newbotright.triangle.region = horiz.triangle.region;
newbotleft.tri.region = horiz.tri.region;
newbotright.tri.region = horiz.tri.region;
if (behavior.VarArea)
{
// Set the area constraint of the new triangles.
area = horiz.triangle.area;
newbotleft.triangle.area = area;
newbotright.triangle.area = area;
area = horiz.tri.area;
newbotleft.tri.area = area;
newbotright.tri.area = area;
}
// There may be subsegments that need to be bonded
// to the new triangles.
if (checksegments)
{
botleft.SegPivot(ref botlsubseg);
botleft.Pivot(ref botlsubseg);
if (botlsubseg.seg != Segment.Empty)
{
botleft.SegDissolve();
newbotleft.SegBond(ref botlsubseg);
}
botright.SegPivot(ref botrsubseg);
botright.Pivot(ref botrsubseg);
if (botrsubseg.seg != Segment.Empty)
{
botright.SegDissolve();
@@ -958,12 +958,12 @@ namespace TriangleNet
// Bond the new triangles to the surrounding triangles.
newbotleft.Bond(ref botlcasing);
newbotright.Bond(ref botrcasing);
newbotleft.LnextSelf();
newbotright.LprevSelf();
newbotleft.Lnext();
newbotright.Lprev();
newbotleft.Bond(ref newbotright);
newbotleft.LnextSelf();
newbotleft.Lnext();
botleft.Bond(ref newbotleft);
newbotright.LprevSelf();
newbotright.Lprev();
botright.Bond(ref newbotright);
if (checkquality)
@@ -991,7 +991,7 @@ namespace TriangleNet
if (checksegments)
{
// Check for a subsegment, which cannot be flipped.
horiz.SegPivot(ref checksubseg);
horiz.Pivot(ref checksubseg);
if (checksubseg.seg != Segment.Empty)
{
// The edge is a subsegment and cannot be flipped.
@@ -1012,7 +1012,7 @@ namespace TriangleNet
{
// Check if the edge is a boundary edge.
horiz.Sym(ref top);
if (top.triangle.id == Triangle.EmptyID)
if (top.tri.id == Triangle.EmptyID)
{
// The edge is a boundary edge and cannot be flipped.
doflip = false;
@@ -1079,10 +1079,10 @@ namespace TriangleNet
if (checksegments)
{
// Check for subsegments and rebond them to the quadrilateral.
topleft.SegPivot(ref toplsubseg);
botleft.SegPivot(ref botlsubseg);
botright.SegPivot(ref botrsubseg);
topright.SegPivot(ref toprsubseg);
topleft.Pivot(ref toplsubseg);
botleft.Pivot(ref botlsubseg);
botright.Pivot(ref botrsubseg);
topright.Pivot(ref toprsubseg);
if (toplsubseg.seg == Segment.Empty)
{
topright.SegDissolve();
@@ -1126,13 +1126,13 @@ namespace TriangleNet
// Assign region.
// TODO: check region ok (no Math.Min necessary)
region = Math.Min(top.triangle.region, horiz.triangle.region);
top.triangle.region = region;
horiz.triangle.region = region;
region = Math.Min(top.tri.region, horiz.tri.region);
top.tri.region = region;
horiz.tri.region = region;
if (behavior.VarArea)
{
if ((top.triangle.area <= 0.0) || (horiz.triangle.area <= 0.0))
if ((top.tri.area <= 0.0) || (horiz.tri.area <= 0.0))
{
area = -1.0;
}
@@ -1141,11 +1141,11 @@ namespace TriangleNet
// Take the average of the two triangles' area constraints.
// This prevents small area constraints from migrating a
// long, long way from their original location due to flips.
area = 0.5 * (top.triangle.area + horiz.triangle.area);
area = 0.5 * (top.tri.area + horiz.tri.area);
}
top.triangle.area = area;
horiz.triangle.area = area;
top.tri.area = area;
horiz.tri.area = area;
}
if (checkquality)
@@ -1155,7 +1155,7 @@ namespace TriangleNet
// On the next iterations, consider the two edges that were exposed (this
// is, are now visible to the newly inserted vertex) by the edge flip.
horiz.LprevSelf();
horiz.Lprev();
leftvertex = farvertex;
}
}
@@ -1170,12 +1170,12 @@ namespace TriangleNet
}
// Look for the next edge around the newly inserted vertex.
horiz.LnextSelf();
horiz.Lnext();
horiz.Sym(ref testtri);
// Check for finishing a complete revolution about the new vertex, or
// falling outside of the triangulation. The latter will happen when
// a vertex is inserted at a boundary.
if ((leftvertex == first) || (testtri.triangle.id == Triangle.EmptyID))
if ((leftvertex == first) || (testtri.tri.id == Triangle.EmptyID))
{
// We're done. Return a triangle whose origin is the new vertex.
horiz.Lnext(ref searchtri);
@@ -1220,7 +1220,7 @@ namespace TriangleNet
tridest.mark = subsegmark;
}
// Check if there's already a subsegment here.
tri.SegPivot(ref newsubseg);
tri.Pivot(ref newsubseg);
if (newsubseg.seg == Segment.Empty)
{
// Make new subsegment and initialize its vertices.
@@ -1234,7 +1234,7 @@ namespace TriangleNet
// (outer space), but the new subsegment is bonded to it all the same.
tri.SegBond(ref newsubseg);
tri.Sym(ref oppotri);
newsubseg.SymSelf();
newsubseg.Sym();
oppotri.SegBond(ref newsubseg);
newsubseg.seg.boundary = subsegmark;
}
@@ -1344,10 +1344,10 @@ namespace TriangleNet
if (checksegments)
{
// Check for subsegments and rebond them to the quadrilateral.
topleft.SegPivot(ref toplsubseg);
botleft.SegPivot(ref botlsubseg);
botright.SegPivot(ref botrsubseg);
topright.SegPivot(ref toprsubseg);
topleft.Pivot(ref toplsubseg);
botleft.Pivot(ref botlsubseg);
botright.Pivot(ref botrsubseg);
topright.Pivot(ref toprsubseg);
if (toplsubseg.seg == Segment.Empty)
{
@@ -1447,10 +1447,10 @@ namespace TriangleNet
if (checksegments)
{
// Check for subsegments and rebond them to the quadrilateral.
topleft.SegPivot(ref toplsubseg);
botleft.SegPivot(ref botlsubseg);
botright.SegPivot(ref botrsubseg);
topright.SegPivot(ref toprsubseg);
topleft.Pivot(ref toplsubseg);
botleft.Pivot(ref botlsubseg);
botright.Pivot(ref botrsubseg);
topright.Pivot(ref toprsubseg);
if (toplsubseg.seg == Segment.Empty)
{
botleft.SegDissolve();
@@ -1580,7 +1580,7 @@ namespace TriangleNet
for (int i = 2; i <= edgecount - 2; i++)
{
testtri.OnextSelf();
testtri.Onext();
testvertex = testtri.Dest();
// Is this a better vertex?
if (RobustPredicates.InCircle(leftbasevertex, rightbasevertex, bestvertex, testvertex) > 0.0)
@@ -1656,7 +1656,7 @@ namespace TriangleNet
while (!deltri.Equal(countingtri))
{
edgecount++;
countingtri.OnextSelf();
countingtri.Onext();
}
if (edgecount > 3)
@@ -1676,12 +1676,12 @@ namespace TriangleNet
righttri.Sym(ref rightcasing);
deltri.Bond(ref leftcasing);
deltriright.Bond(ref rightcasing);
lefttri.SegPivot(ref leftsubseg);
lefttri.Pivot(ref leftsubseg);
if (leftsubseg.seg != Segment.Empty)
{
deltri.SegBond(ref leftsubseg);
}
righttri.SegPivot(ref rightsubseg);
righttri.Pivot(ref rightsubseg);
if (rightsubseg.seg != Segment.Empty)
{
deltriright.SegBond(ref rightsubseg);
@@ -1696,8 +1696,8 @@ namespace TriangleNet
}
// Delete the two spliced-out triangles.
TriangleDealloc(lefttri.triangle);
TriangleDealloc(righttri.triangle);
TriangleDealloc(lefttri.tri);
TriangleDealloc(righttri.tri);
}
/// <summary>
@@ -1735,59 +1735,59 @@ namespace TriangleNet
// Restore a triangle that was split into three triangles,
// so it is again one triangle.
fliptri.Dprev(ref botleft);
botleft.LnextSelf();
botleft.Lnext();
fliptri.Onext(ref botright);
botright.LprevSelf();
botright.Lprev();
botleft.Sym(ref botlcasing);
botright.Sym(ref botrcasing);
botvertex = botleft.Dest();
fliptri.SetApex(botvertex);
fliptri.LnextSelf();
fliptri.Lnext();
fliptri.Bond(ref botlcasing);
botleft.SegPivot(ref botlsubseg);
botleft.Pivot(ref botlsubseg);
fliptri.SegBond(ref botlsubseg);
fliptri.LnextSelf();
fliptri.Lnext();
fliptri.Bond(ref botrcasing);
botright.SegPivot(ref botrsubseg);
botright.Pivot(ref botrsubseg);
fliptri.SegBond(ref botrsubseg);
// Delete the two spliced-out triangles.
TriangleDealloc(botleft.triangle);
TriangleDealloc(botright.triangle);
TriangleDealloc(botleft.tri);
TriangleDealloc(botright.tri);
}
else if (flipstack.Peek().triangle == null) // Dummy flip
else if (flipstack.Peek().tri == null) // Dummy flip
{
// Restore two triangles that were split into four triangles,
// so they are again two triangles.
fliptri.Lprev(ref gluetri);
gluetri.Sym(ref botright);
botright.LnextSelf();
botright.Lnext();
botright.Sym(ref botrcasing);
rightvertex = botright.Dest();
fliptri.SetOrg(rightvertex);
gluetri.Bond(ref botrcasing);
botright.SegPivot(ref botrsubseg);
botright.Pivot(ref botrsubseg);
gluetri.SegBond(ref botrsubseg);
// Delete the spliced-out triangle.
TriangleDealloc(botright.triangle);
TriangleDealloc(botright.tri);
fliptri.Sym(ref gluetri);
if (gluetri.triangle.id != Triangle.EmptyID)
if (gluetri.tri.id != Triangle.EmptyID)
{
gluetri.LnextSelf();
gluetri.Lnext();
gluetri.Dnext(ref topright);
topright.Sym(ref toprcasing);
gluetri.SetOrg(rightvertex);
gluetri.Bond(ref toprcasing);
topright.SegPivot(ref toprsubseg);
topright.Pivot(ref toprsubseg);
gluetri.SegBond(ref toprsubseg);
// Delete the spliced-out triangle.
TriangleDealloc(topright.triangle);
TriangleDealloc(topright.tri);
}
flipstack.Clear();
+10 -10
View File
@@ -34,7 +34,7 @@ namespace TriangleNet
// Run through the list of triangles, checking each one.
foreach (var t in mesh.triangles.Values)
{
tri.triangle = t;
tri.tri = t;
// Check all three edges of the triangle.
for (tri.orient = 0; tri.orient < 3; tri.orient++)
@@ -59,13 +59,13 @@ namespace TriangleNet
// Find the neighboring triangle on this edge.
tri.Sym(ref oppotri);
if (oppotri.triangle.id != Triangle.EmptyID)
if (oppotri.tri.id != Triangle.EmptyID)
{
// Check that the triangle's neighbor knows it's a neighbor.
oppotri.Sym(ref oppooppotri);
if ((tri.triangle != oppooppotri.triangle) || (tri.orient != oppooppotri.orient))
if ((tri.tri != oppooppotri.tri) || (tri.orient != oppooppotri.orient))
{
if (tri.triangle == oppooppotri.triangle && Log.Verbose)
if (tri.tri == oppooppotri.tri && Log.Verbose)
{
logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)",
"MeshValidator.IsConsistent()");
@@ -95,7 +95,7 @@ namespace TriangleNet
mesh.MakeVertexMap();
foreach (var v in mesh.vertices.Values)
{
if (v.tri.triangle == null && Log.Verbose)
if (v.tri.tri == null && Log.Verbose)
{
logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)",
"MeshValidator.IsConsistent()");
@@ -152,7 +152,7 @@ namespace TriangleNet
// Run through the list of triangles, checking each one.
foreach (var tri in mesh.triangles.Values)
{
loop.triangle = tri;
loop.tri = tri;
// Check all three edges of the triangle.
for (loop.orient = 0; loop.orient < 3; loop.orient++)
@@ -167,8 +167,8 @@ namespace TriangleNet
// 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.triangle.id < oppotri.triangle.id) &&
!Otri.IsDead(oppotri.triangle) && (oppotri.triangle.id != Triangle.EmptyID) &&
shouldbedelaunay = (loop.tri.id < oppotri.tri.id) &&
!Otri.IsDead(oppotri.tri) && (oppotri.tri.id != Triangle.EmptyID) &&
(org != inf1) && (org != inf2) && (org != inf3) &&
(dest != inf1) && (dest != inf2) && (dest != inf3) &&
(apex != inf1) && (apex != inf2) && (apex != inf3) &&
@@ -178,7 +178,7 @@ namespace TriangleNet
{
// If a subsegment separates the triangles, then the edge is
// constrained, so no local Delaunay test should be done.
loop.SegPivot(ref opposubseg);
loop.Pivot(ref opposubseg);
if (opposubseg.seg != Segment.Empty)
{
@@ -193,7 +193,7 @@ namespace TriangleNet
if (Log.Verbose)
{
logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).",
loop.triangle.id, oppotri.triangle.id), "MeshValidator.IsDelaunay()");
loop.tri.id, oppotri.tri.id), "MeshValidator.IsDelaunay()");
}
horrors++;
@@ -400,8 +400,8 @@ namespace TriangleNet.Meshing.Algorithm
// leftmost and rightmost vertices.
while (farleftapex.y < farleftpt.y)
{
farleft.LnextSelf();
farleft.SymSelf();
farleft.Lnext();
farleft.Sym();
farleftpt = farleftapex;
farleftapex = farleft.Apex();
}
@@ -417,8 +417,8 @@ namespace TriangleNet.Meshing.Algorithm
}
while (innerrightapex.y < innerrightorg.y)
{
innerright.LnextSelf();
innerright.SymSelf();
innerright.Lnext();
innerright.Sym();
innerrightorg = innerrightapex;
innerrightapex = innerright.Apex();
}
@@ -440,8 +440,8 @@ namespace TriangleNet.Meshing.Algorithm
// Make innerleftdest the "bottommost" vertex of the left hull.
if (RobustPredicates.CounterClockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0)
{
innerleft.LprevSelf();
innerleft.SymSelf();
innerleft.Lprev();
innerleft.Sym();
innerleftdest = innerleftapex;
innerleftapex = innerleft.Apex();
changemade = true;
@@ -449,8 +449,8 @@ namespace TriangleNet.Meshing.Algorithm
// Make innerrightorg the "bottommost" vertex of the right hull.
if (RobustPredicates.CounterClockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0)
{
innerright.LnextSelf();
innerright.SymSelf();
innerright.Lnext();
innerright.Sym();
innerrightorg = innerrightapex;
innerrightapex = innerright.Apex();
changemade = true;
@@ -464,9 +464,9 @@ namespace TriangleNet.Meshing.Algorithm
mesh.MakeTriangle(ref baseedge);
// Connect it to the bounding boxes of the left and right triangulations.
baseedge.Bond(ref innerleft);
baseedge.LnextSelf();
baseedge.Lnext();
baseedge.Bond(ref innerright);
baseedge.LnextSelf();
baseedge.Lnext();
baseedge.SetOrg(innerrightorg);
baseedge.SetDest(innerleftdest);
// Apex is intentionally left NULL.
@@ -506,9 +506,9 @@ namespace TriangleNet.Meshing.Algorithm
// Apex is intentionally left NULL.
// Connect it to the bounding boxes of the two triangulations.
nextedge.Bond(ref baseedge);
nextedge.LnextSelf();
nextedge.Lnext();
nextedge.Bond(ref rightcand);
nextedge.LnextSelf();
nextedge.Lnext();
nextedge.Bond(ref leftcand);
// Special treatment for horizontal cuts.
@@ -533,8 +533,8 @@ namespace TriangleNet.Meshing.Algorithm
}
while (farrightapex.x > farrightpt.x)
{
farright.LprevSelf();
farright.SymSelf();
farright.Lprev();
farright.Sym();
farrightpt = farrightapex;
farrightapex = farright.Apex();
}
@@ -546,7 +546,7 @@ namespace TriangleNet.Meshing.Algorithm
{
// What vertex would be exposed if an edge were deleted?
leftcand.Lprev(ref nextedge);
nextedge.SymSelf();
nextedge.Sym();
nextapex = nextedge.Apex();
// If nextapex is NULL, then no vertex would be exposed; the
// triangulation would have been eaten right through.
@@ -558,15 +558,15 @@ namespace TriangleNet.Meshing.Algorithm
{
// Eliminate the edge with an edge flip. As a result, the
// left triangulation will have one more boundary triangle.
nextedge.LnextSelf();
nextedge.Lnext();
nextedge.Sym(ref topcasing);
nextedge.LnextSelf();
nextedge.Lnext();
nextedge.Sym(ref sidecasing);
nextedge.Bond(ref topcasing);
leftcand.Bond(ref sidecasing);
leftcand.LnextSelf();
leftcand.Lnext();
leftcand.Sym(ref outercasing);
nextedge.LprevSelf();
nextedge.Lprev();
nextedge.Bond(ref outercasing);
// Correct the vertices to reflect the edge flip.
leftcand.SetOrg(lowerleft);
@@ -598,7 +598,7 @@ namespace TriangleNet.Meshing.Algorithm
{
// What vertex would be exposed if an edge were deleted?
rightcand.Lnext(ref nextedge);
nextedge.SymSelf();
nextedge.Sym();
nextapex = nextedge.Apex();
// If nextapex is NULL, then no vertex would be exposed; the
// triangulation would have been eaten right through.
@@ -610,15 +610,15 @@ namespace TriangleNet.Meshing.Algorithm
{
// Eliminate the edge with an edge flip. As a result, the
// right triangulation will have one more boundary triangle.
nextedge.LprevSelf();
nextedge.Lprev();
nextedge.Sym(ref topcasing);
nextedge.LprevSelf();
nextedge.Lprev();
nextedge.Sym(ref sidecasing);
nextedge.Bond(ref topcasing);
rightcand.Bond(ref sidecasing);
rightcand.LprevSelf();
rightcand.Lprev();
rightcand.Sym(ref outercasing);
nextedge.LnextSelf();
nextedge.Lnext();
nextedge.Bond(ref outercasing);
// Correct the vertices to reflect the edge flip.
rightcand.SetOrg(null);
@@ -715,11 +715,11 @@ namespace TriangleNet.Meshing.Algorithm
farright.SetDest(sortarray[left]);
// The apex is intentionally left NULL.
farleft.Bond(ref farright);
farleft.LprevSelf();
farright.LnextSelf();
farleft.Lprev();
farright.Lnext();
farleft.Bond(ref farright);
farleft.LprevSelf();
farright.LnextSelf();
farleft.Lprev();
farright.Lnext();
farleft.Bond(ref farright);
// Ensure that the origin of 'farleft' is sortarray[0].
@@ -750,16 +750,16 @@ namespace TriangleNet.Meshing.Algorithm
// All apices are intentionally left NULL.
midtri.Bond(ref tri1);
tri2.Bond(ref tri3);
midtri.LnextSelf();
tri1.LprevSelf();
tri2.LnextSelf();
tri3.LprevSelf();
midtri.Lnext();
tri1.Lprev();
tri2.Lnext();
tri3.Lprev();
midtri.Bond(ref tri3);
tri1.Bond(ref tri2);
midtri.LnextSelf();
tri1.LprevSelf();
tri2.LnextSelf();
tri3.LprevSelf();
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].
@@ -797,18 +797,18 @@ namespace TriangleNet.Meshing.Algorithm
}
// The topology does not depend on how the vertices are ordered.
midtri.Bond(ref tri1);
midtri.LnextSelf();
midtri.Lnext();
midtri.Bond(ref tri2);
midtri.LnextSelf();
midtri.Lnext();
midtri.Bond(ref tri3);
tri1.LprevSelf();
tri2.LnextSelf();
tri1.Lprev();
tri2.Lnext();
tri1.Bond(ref tri2);
tri1.LprevSelf();
tri3.LprevSelf();
tri1.Lprev();
tri3.Lprev();
tri1.Bond(ref tri3);
tri2.LnextSelf();
tri3.LprevSelf();
tri2.Lnext();
tri3.Lprev();
tri2.Bond(ref tri3);
// Ensure that the origin of 'farleft' is sortarray[0].
tri1.Copy(ref farleft);
@@ -859,7 +859,7 @@ namespace TriangleNet.Meshing.Algorithm
// Find an edge on the convex hull to start point location from.
startghost.Lprev(ref searchedge);
searchedge.SymSelf();
searchedge.Sym();
Triangle.Empty.neighbors[0] = searchedge;
// Remove the bounding box and count the convex hull edges.
startghost.Copy(ref dissolveedge);
@@ -868,15 +868,15 @@ namespace TriangleNet.Meshing.Algorithm
{
hullsize++;
dissolveedge.Lnext(ref deadtriangle);
dissolveedge.LprevSelf();
dissolveedge.SymSelf();
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.triangle.id != Triangle.EmptyID)
if (dissolveedge.tri.id != Triangle.EmptyID)
{
markorg = dissolveedge.Org();
if (markorg.mark == 0)
@@ -891,7 +891,7 @@ namespace TriangleNet.Meshing.Algorithm
deadtriangle.Sym(ref dissolveedge);
// Delete the bounding triangle.
mesh.TriangleDealloc(deadtriangle.triangle);
mesh.TriangleDealloc(deadtriangle.tri);
} while (!dissolveedge.Equal(startghost));
return hullsize;
@@ -35,7 +35,7 @@ namespace TriangleNet.Meshing.Algorithm
foreach (var v in mesh.vertices.Values)
{
starttri.triangle = Triangle.Empty;
starttri.tri = Triangle.Empty;
Osub tmp = default(Osub);
if (mesh.InsertVertex(v, ref starttri, ref tmp, false, false) == InsertVertexResult.Duplicate)
{
@@ -115,28 +115,28 @@ namespace TriangleNet.Meshing.Algorithm
bool noPoly = !mesh.behavior.Poly;
// Find a boundary triangle.
nextedge.triangle = Triangle.Empty;
nextedge.tri = Triangle.Empty;
nextedge.orient = 0;
nextedge.SymSelf();
nextedge.Sym();
// Mark a place to stop.
nextedge.Lprev(ref finaledge);
nextedge.LnextSelf();
nextedge.SymSelf();
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.SymSelf();
searchedge.Sym();
// Check whether nextedge is another boundary triangle
// adjacent to the first one.
nextedge.Lnext(ref checkedge);
checkedge.SymSelf();
if (checkedge.triangle.id == Triangle.EmptyID)
checkedge.Sym();
if (checkedge.tri.id == Triangle.EmptyID)
{
// 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.LprevSelf();
searchedge.SymSelf();
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.
@@ -146,7 +146,7 @@ namespace TriangleNet.Meshing.Algorithm
{
hullsize++;
nextedge.Lprev(ref dissolveedge);
dissolveedge.SymSelf();
dissolveedge.Sym();
// If not using a PSLG, the vertices should be marked now.
// (If using a PSLG, markhull() will do the job.)
if (noPoly)
@@ -155,7 +155,7 @@ namespace TriangleNet.Meshing.Algorithm
// 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.triangle.id != Triangle.EmptyID)
if (dissolveedge.tri.id != Triangle.EmptyID)
{
markorg = dissolveedge.Org();
if (markorg.mark == 0)
@@ -169,15 +169,15 @@ namespace TriangleNet.Meshing.Algorithm
nextedge.Lnext(ref deadtriangle);
deadtriangle.Sym(ref nextedge);
// Get rid of the bounding box triangle.
mesh.TriangleDealloc(deadtriangle.triangle);
mesh.TriangleDealloc(deadtriangle.tri);
// Do we need to turn the corner?
if (nextedge.triangle.id == Triangle.EmptyID)
if (nextedge.tri.id == Triangle.EmptyID)
{
// Turn the corner.
dissolveedge.Copy(ref nextedge);
}
}
mesh.TriangleDealloc(finaledge.triangle);
mesh.TriangleDealloc(finaledge.tri);
return hullsize;
}
@@ -70,11 +70,11 @@ namespace TriangleNet.Meshing.Algorithm
mesh.MakeTriangle(ref lefttri);
mesh.MakeTriangle(ref righttri);
lefttri.Bond(ref righttri);
lefttri.LnextSelf();
righttri.LprevSelf();
lefttri.Lnext();
righttri.Lprev();
lefttri.Bond(ref righttri);
lefttri.LnextSelf();
righttri.LprevSelf();
lefttri.Lnext();
righttri.Lprev();
lefttri.Bond(ref righttri);
firstvertex = eventheap[0].vertexEvent;
@@ -136,7 +136,7 @@ namespace TriangleNet.Meshing.Algorithm
if (randomnation(SAMPLERATE) == 0)
{
fliptri.SymSelf();
fliptri.Sym();
leftvertex = fliptri.Dest();
midvertex = fliptri.Apex();
rightvertex = fliptri.Org();
@@ -184,11 +184,11 @@ namespace TriangleNet.Meshing.Algorithm
righttri.SetOrg(nextvertex);
righttri.SetDest(connectvertex);
lefttri.Bond(ref righttri);
lefttri.LnextSelf();
righttri.LprevSelf();
lefttri.Lnext();
righttri.Lprev();
lefttri.Bond(ref righttri);
lefttri.LnextSelf();
righttri.LprevSelf();
lefttri.Lnext();
righttri.Lprev();
lefttri.Bond(ref farlefttri);
righttri.Bond(ref farrighttri);
if (!farrightflag && farrighttri.Equal(bottommost))
@@ -244,7 +244,7 @@ namespace TriangleNet.Meshing.Algorithm
}
splaynodes.Clear();
bottommost.LprevSelf();
bottommost.Lprev();
this.mesh.hullsize = RemoveGhosts(ref bottommost);
@@ -584,7 +584,7 @@ namespace TriangleNet.Meshing.Algorithm
farrightflag = false;
while (!farrightflag && RightOfHyperbola(ref searchtri, searchvertex))
{
searchtri.OnextSelf();
searchtri.Onext();
farrightflag = searchtri.Equal(bottommost);
}
farright = farrightflag;
@@ -700,7 +700,7 @@ namespace TriangleNet.Meshing.Algorithm
// Find an edge on the convex hull to start point location from.
startghost.Lprev(ref searchedge);
searchedge.SymSelf();
searchedge.Sym();
Triangle.Empty.neighbors[0] = searchedge;
// Remove the bounding box and count the convex hull edges.
startghost.Copy(ref dissolveedge);
@@ -709,15 +709,15 @@ namespace TriangleNet.Meshing.Algorithm
{
hullsize++;
dissolveedge.Lnext(ref deadtriangle);
dissolveedge.LprevSelf();
dissolveedge.SymSelf();
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.triangle.id != Triangle.EmptyID)
if (dissolveedge.tri.id != Triangle.EmptyID)
{
markorg = dissolveedge.Org();
if (markorg.mark == 0)
@@ -732,7 +732,7 @@ namespace TriangleNet.Meshing.Algorithm
deadtriangle.Sym(ref dissolveedge);
// Delete the bounding triangle.
mesh.TriangleDealloc(deadtriangle.triangle);
mesh.TriangleDealloc(deadtriangle.tri);
} while (!dissolveedge.Equal(startghost));
return hullsize;
@@ -64,9 +64,9 @@ namespace TriangleNet.Meshing
if (mesh.bounds.Contains(hole))
{
// Start searching from some triangle on the outer boundary.
searchtri.triangle = Triangle.Empty;
searchtri.tri = Triangle.Empty;
searchtri.orient = 0;
searchtri.SymSelf();
searchtri.Sym();
// Ensure that the hole is to the left of this boundary edge;
// otherwise, locate() will falsely report that the hole
// falls within the starting triangle.
@@ -81,7 +81,7 @@ namespace TriangleNet.Meshing
// Infect the triangle. This is done by marking the triangle
// as infected and including the triangle in the virus pool.
searchtri.Infect();
viri.Add(searchtri.triangle);
viri.Add(searchtri.tri);
}
}
}
@@ -106,9 +106,9 @@ namespace TriangleNet.Meshing
if (mesh.bounds.Contains(region.point))
{
// Start searching from some triangle on the outer boundary.
searchtri.triangle = Triangle.Empty;
searchtri.tri = Triangle.Empty;
searchtri.orient = 0;
searchtri.SymSelf();
searchtri.Sym();
// Ensure that the region point is to the left of this boundary
// edge; otherwise, locate() will falsely report that the
// region point falls within the starting triangle.
@@ -122,7 +122,7 @@ namespace TriangleNet.Meshing
{
// Record the triangle for processing after the
// holes have been carved.
regionTris[i] = searchtri.triangle;
regionTris[i] = searchtri.tri;
regionTris[i].region = region.id;
}
}
@@ -261,9 +261,9 @@ namespace TriangleNet.Meshing
Vertex horg, hdest;
// Find a triangle handle on the hull.
hulltri.triangle = Triangle.Empty;
hulltri.tri = Triangle.Empty;
hulltri.orient = 0;
hulltri.SymSelf();
hulltri.Sym();
// Remember where we started so we know when to stop.
hulltri.Copy(ref starttri);
// Go once counterclockwise around the convex hull.
@@ -273,14 +273,14 @@ namespace TriangleNet.Meshing
if (!hulltri.IsInfected())
{
// Is the triangle protected by a subsegment?
hulltri.SegPivot(ref hullsubseg);
hulltri.Pivot(ref hullsubseg);
if (hullsubseg.seg == Segment.Empty)
{
// The triangle is not protected; infect it.
if (!hulltri.IsInfected())
{
hulltri.Infect();
viri.Add(hulltri.triangle);
viri.Add(hulltri.tri);
}
}
else
@@ -303,9 +303,9 @@ namespace TriangleNet.Meshing
}
}
// To find the next hull edge, go clockwise around the next vertex.
hulltri.LnextSelf();
hulltri.Lnext();
hulltri.Oprev(ref nexttri);
while (nexttri.triangle.id != Triangle.EmptyID)
while (nexttri.tri.id != Triangle.EmptyID)
{
nexttri.Copy(ref hulltri);
hulltri.Oprev(ref nexttri);
@@ -345,7 +345,7 @@ namespace TriangleNet.Meshing
{
// WARNING: Don't use foreach, mesh.viri list may get modified.
testtri.triangle = viri[i];
testtri.tri = viri[i];
// A triangle is marked as infected by messing with one of its pointers
// to subsegments, setting it to an illegal value. Hence, we have to
// temporarily uninfect this triangle so that we can examine its
@@ -359,9 +359,9 @@ namespace TriangleNet.Meshing
// Find the neighbor.
testtri.Sym(ref neighbor);
// Check for a subsegment between the triangle and its neighbor.
testtri.SegPivot(ref neighborsubseg);
testtri.Pivot(ref neighborsubseg);
// Check if the neighbor is nonexistent or already infected.
if ((neighbor.triangle.id == Triangle.EmptyID) || neighbor.IsInfected())
if ((neighbor.tri.id == Triangle.EmptyID) || neighbor.IsInfected())
{
if (neighborsubseg.seg != Segment.Empty)
{
@@ -369,7 +369,7 @@ namespace TriangleNet.Meshing
// neighbor, but both triangles are dying, so the subsegment
// dies too.
mesh.SubsegDealloc(neighborsubseg.seg);
if (neighbor.triangle.id != Triangle.EmptyID)
if (neighbor.tri.id != Triangle.EmptyID)
{
// Make sure the subsegment doesn't get deallocated again
// later when the infected neighbor is visited.
@@ -387,7 +387,7 @@ namespace TriangleNet.Meshing
// the neighbor becomes infected.
neighbor.Infect();
// Ensure that the neighbor's neighbors will be infected.
viri.Add(neighbor.triangle);
viri.Add(neighbor.tri);
}
else
{
@@ -419,7 +419,7 @@ namespace TriangleNet.Meshing
foreach (var virus in viri)
{
testtri.triangle = virus;
testtri.tri = virus;
// Check each of the three corners of the triangle for elimination.
// This is done by walking around each vertex, checking if it is
@@ -436,7 +436,7 @@ namespace TriangleNet.Meshing
// Walk counterclockwise about the vertex.
testtri.Onext(ref neighbor);
// Stop upon reaching a boundary or the starting triangle.
while ((neighbor.triangle.id != Triangle.EmptyID) &&
while ((neighbor.tri.id != Triangle.EmptyID) &&
(!neighbor.Equal(testtri)))
{
if (neighbor.IsInfected())
@@ -450,15 +450,15 @@ namespace TriangleNet.Meshing
killorg = false;
}
// Walk counterclockwise about the vertex.
neighbor.OnextSelf();
neighbor.Onext();
}
// If we reached a boundary, we must walk clockwise as well.
if (neighbor.triangle.id == Triangle.EmptyID)
if (neighbor.tri.id == Triangle.EmptyID)
{
// Walk clockwise about the vertex.
testtri.Oprev(ref neighbor);
// Stop upon reaching a boundary.
while (neighbor.triangle.id != Triangle.EmptyID)
while (neighbor.tri.id != Triangle.EmptyID)
{
if (neighbor.IsInfected())
{
@@ -471,7 +471,7 @@ namespace TriangleNet.Meshing
killorg = false;
}
// Walk clockwise about the vertex.
neighbor.OprevSelf();
neighbor.Oprev();
}
}
if (killorg)
@@ -488,7 +488,7 @@ namespace TriangleNet.Meshing
for (testtri.orient = 0; testtri.orient < 3; testtri.orient++)
{
testtri.Sym(ref neighbor);
if (neighbor.triangle.id == Triangle.EmptyID)
if (neighbor.tri.id == Triangle.EmptyID)
{
// There is no neighboring triangle on this edge, so this edge
// is a boundary edge. This triangle is being deleted, so this
@@ -505,7 +505,7 @@ namespace TriangleNet.Meshing
}
}
// Return the dead triangle to the pool of triangles.
mesh.TriangleDealloc(testtri.triangle);
mesh.TriangleDealloc(testtri.tri);
}
// Empty the virus pool.
@@ -554,7 +554,7 @@ namespace TriangleNet.Meshing
// 'searchtri' faces directly away from 'searchpoint'. We could go left
// or right. Ask whether it's a triangle or a boundary on the left.
searchtri.Onext(ref checktri);
if (checktri.triangle.id == Triangle.EmptyID)
if (checktri.tri.id == Triangle.EmptyID)
{
leftflag = false;
}
@@ -566,8 +566,8 @@ namespace TriangleNet.Meshing
while (leftflag)
{
// Turn left until satisfied.
searchtri.OnextSelf();
if (searchtri.triangle.id == Triangle.EmptyID)
searchtri.Onext();
if (searchtri.tri.id == Triangle.EmptyID)
{
logger.Error("Unable to find a triangle on path.", "Mesh.FindDirection().1");
throw new Exception("Unable to find a triangle on path.");
@@ -580,8 +580,8 @@ namespace TriangleNet.Meshing
while (rightflag)
{
// Turn right until satisfied.
searchtri.OprevSelf();
if (searchtri.triangle.id == Triangle.EmptyID)
searchtri.Oprev();
if (searchtri.tri.id == Triangle.EmptyID)
{
logger.Error("Unable to find a triangle on path.", "Mesh.FindDirection().2");
throw new Exception("Unable to find a triangle on path.");
@@ -688,19 +688,19 @@ namespace TriangleNet.Meshing
}
// Divide the segment into two, and correct the segment endpoints.
splitsubseg.SymSelf();
splitsubseg.Sym();
splitsubseg.Pivot(ref opposubseg);
splitsubseg.Dissolve();
opposubseg.Dissolve();
do
{
splitsubseg.SetSegOrg(newvertex);
splitsubseg.NextSelf();
splitsubseg.Next();
} while (splitsubseg.seg != Segment.Empty);
do
{
opposubseg.SetSegOrg(newvertex);
opposubseg.NextSelf();
opposubseg.Next();
} while (opposubseg.seg != Segment.Empty);
// Inserting the vertex may have caused edge flips. We wish to rediscover
@@ -711,7 +711,7 @@ namespace TriangleNet.Meshing
leftvertex = splittri.Apex();
if ((leftvertex.x == endpoint1.x) && (leftvertex.y == endpoint1.y))
{
splittri.OnextSelf();
splittri.Onext();
}
else if ((rightvertex.x != endpoint1.x) || (rightvertex.y != endpoint1.y))
{
@@ -760,7 +760,7 @@ namespace TriangleNet.Meshing
// The segment is already an edge in the mesh.
if ((leftvertex.x == endpoint2.x) && (leftvertex.y == endpoint2.y))
{
searchtri.LprevSelf();
searchtri.Lprev();
}
// Insert a subsegment, if there isn't already one there.
mesh.InsertSubseg(ref searchtri, newmark);
@@ -770,7 +770,7 @@ namespace TriangleNet.Meshing
{
// We've collided with a vertex between the segment's endpoints.
// Make the collinear vertex be the triangle's origin.
searchtri.LprevSelf();
searchtri.Lprev();
mesh.InsertSubseg(ref searchtri, newmark);
// Insert the remainder of the segment.
return ScoutSegment(ref searchtri, endpoint2, newmark);
@@ -780,14 +780,14 @@ namespace TriangleNet.Meshing
// We've collided with a vertex between the segment's endpoints.
mesh.InsertSubseg(ref searchtri, newmark);
// Make the collinear vertex be the triangle's origin.
searchtri.LnextSelf();
searchtri.Lnext();
// Insert the remainder of the segment.
return ScoutSegment(ref searchtri, endpoint2, newmark);
}
else
{
searchtri.Lnext(ref crosstri);
crosstri.SegPivot(ref crosssubseg);
crosstri.Pivot(ref crosssubseg);
// Check for a crossing segment.
if (crosssubseg.seg == Segment.Empty)
{
@@ -850,11 +850,11 @@ namespace TriangleNet.Meshing
fixuptri.Lnext(ref neartri);
neartri.Sym(ref fartri);
// Check if the edge opposite the origin of fixuptri can be flipped.
if (fartri.triangle.id == Triangle.EmptyID)
if (fartri.tri.id == Triangle.EmptyID)
{
return;
}
neartri.SegPivot(ref faredge);
neartri.Pivot(ref faredge);
if (faredge.seg != Segment.Empty)
{
return;
@@ -897,7 +897,7 @@ namespace TriangleNet.Meshing
}
// else fartri is inverted; remove it from the stack by flipping.
mesh.Flip(ref neartri);
fixuptri.LprevSelf(); // Restore the origin of fixuptri after the flip.
fixuptri.Lprev(); // Restore the origin of fixuptri after the flip.
// Recursively process the two triangles that result from the flip.
DelaunayFixup(ref fixuptri, leftside);
DelaunayFixup(ref fartri, leftside);
@@ -1014,7 +1014,7 @@ namespace TriangleNet.Meshing
// Flip the edge that crosses the segment. After the edge is
// flipped, one of its endpoints is the fan vertex, and the
// destination of fixuptri is the fan vertex.
fixuptri.LprevSelf();
fixuptri.Lprev();
}
else
{
@@ -1023,10 +1023,10 @@ namespace TriangleNet.Meshing
// Flip the edge that crosses the segment. After the edge is
// flipped, one of its endpoints is the fan vertex, and the
// destination of fixuptri is the fan vertex.
fixuptri.OprevSelf();
fixuptri.Oprev();
}
// Check for two intersecting segments.
fixuptri.SegPivot(ref crosssubseg);
fixuptri.Pivot(ref crosssubseg);
if (crosssubseg.seg == Segment.Empty)
{
mesh.Flip(ref fixuptri); // May create inverted triangle at left.
@@ -1069,7 +1069,7 @@ namespace TriangleNet.Meshing
// Find a triangle whose origin is the segment's first endpoint.
searchtri1 = endpoint1.tri;
if (searchtri1.triangle != null)
if (searchtri1.tri != null)
{
checkvertex = searchtri1.Org();
}
@@ -1077,9 +1077,9 @@ namespace TriangleNet.Meshing
if (checkvertex != endpoint1)
{
// Find a boundary triangle to search from.
searchtri1.triangle = Triangle.Empty;
searchtri1.tri = Triangle.Empty;
searchtri1.orient = 0;
searchtri1.SymSelf();
searchtri1.Sym();
// Search for the segment's first endpoint by point location.
if (locator.Locate(endpoint1, ref searchtri1) != LocateResult.OnVertex)
{
@@ -1104,16 +1104,16 @@ namespace TriangleNet.Meshing
// Find a triangle whose origin is the segment's second endpoint.
checkvertex = null;
searchtri2 = endpoint2.tri;
if (searchtri2.triangle != null)
if (searchtri2.tri != null)
{
checkvertex = searchtri2.Org();
}
if (checkvertex != endpoint2)
{
// Find a boundary triangle to search from.
searchtri2.triangle = Triangle.Empty;
searchtri2.tri = Triangle.Empty;
searchtri2.orient = 0;
searchtri2.SymSelf();
searchtri2.Sym();
// Search for the segment's second endpoint by point location.
if (locator.Locate(endpoint2, ref searchtri2) != LocateResult.OnVertex)
{
@@ -1148,9 +1148,9 @@ namespace TriangleNet.Meshing
Otri starttri = default(Otri);
// Find a triangle handle on the hull.
hulltri.triangle = Triangle.Empty;
hulltri.tri = Triangle.Empty;
hulltri.orient = 0;
hulltri.SymSelf();
hulltri.Sym();
// Remember where we started so we know when to stop.
hulltri.Copy(ref starttri);
// Go once counterclockwise around the convex hull.
@@ -1159,9 +1159,9 @@ namespace TriangleNet.Meshing
// Create a subsegment if there isn't already one here.
mesh.InsertSubseg(ref hulltri, 1);
// To find the next hull edge, go clockwise around the next vertex.
hulltri.LnextSelf();
hulltri.Lnext();
hulltri.Oprev(ref nexttri);
while (nexttri.triangle.id != Triangle.EmptyID)
while (nexttri.tri.id != Triangle.EmptyID)
{
nexttri.Copy(ref hulltri);
hulltri.Oprev(ref nexttri);
+13 -13
View File
@@ -18,7 +18,7 @@ namespace TriangleNet.Meshing
using TVertex = TriangleNet.Geometry.Vertex;
/// <summary>
/// The Converter class provides methods for mesh reconstruction.
/// The Converter class provides methods for mesh reconstruction and conversion.
/// </summary>
public static class Converter
{
@@ -106,7 +106,7 @@ namespace TriangleNet.Meshing
for (i = 0; i < mesh.vertices.Count; i++)
{
Otri tmp = default(Otri);
tmp.triangle = Triangle.Empty;
tmp.tri = Triangle.Empty;
vertexarray[i] = new List<Otri>(3);
vertexarray[i].Add(tmp);
}
@@ -117,7 +117,7 @@ namespace TriangleNet.Meshing
// together those that share an edge.
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
corner[0] = triangles[i].P0;
corner[1] = triangles[i].P1;
@@ -134,12 +134,12 @@ namespace TriangleNet.Meshing
}
// Read the triangle's attributes.
tri.triangle.region = triangles[i].Region;
tri.tri.region = triangles[i].Region;
// TODO: VarArea
if (mesh.behavior.VarArea)
{
tri.triangle.area = triangles[i].Area;
tri.tri.area = triangles[i].Area;
}
// Set the triangle's vertices.
@@ -164,7 +164,7 @@ namespace TriangleNet.Meshing
checktri = nexttri;
if (checktri.triangle.id != Triangle.EmptyID)
if (checktri.tri.id != Triangle.EmptyID)
{
tdest = tri.Dest();
tapex = tri.Apex();
@@ -192,7 +192,7 @@ namespace TriangleNet.Meshing
nexttri = vertexarray[aroundvertex][index];
checktri = nexttri;
} while (checktri.triangle.id != Triangle.EmptyID);
} while (checktri.tri.id != Triangle.EmptyID);
}
}
@@ -276,7 +276,7 @@ namespace TriangleNet.Meshing
// occurrence of a triangle on a list can (and does) represent
// an edge. In this way, most edges are represented twice, and
// every triangle-subsegment bond is represented once.
while (notfound && (checktri.triangle.id != Triangle.EmptyID))
while (notfound && (checktri.tri.id != Triangle.EmptyID))
{
checkdest = checktri.Dest();
@@ -289,7 +289,7 @@ namespace TriangleNet.Meshing
checktri.SegBond(ref subseg);
// Check if this is a boundary edge.
checktri.Sym(ref checkneighbor);
if (checkneighbor.triangle.id == Triangle.EmptyID)
if (checkneighbor.tri.id == Triangle.EmptyID)
{
// The next line doesn't insert a subsegment (because there's
// already one there), but it sets the boundary markers of
@@ -321,7 +321,7 @@ namespace TriangleNet.Meshing
nexttri = vertexarray[i][index];
checktri = nexttri;
while (checktri.triangle.id != Triangle.EmptyID)
while (checktri.tri.id != Triangle.EmptyID)
{
// Find the next triangle in the stack before this
// information gets overwritten.
@@ -330,7 +330,7 @@ namespace TriangleNet.Meshing
// No adjacent subsegment. (This overwrites the stack info.)
checktri.SegDissolve();
checktri.Sym(ref checkneighbor);
if (checkneighbor.triangle.id == Triangle.EmptyID)
if (checkneighbor.tri.id == Triangle.EmptyID)
{
mesh.InsertSubseg(ref checktri, 1);
hullsize++;
@@ -404,14 +404,14 @@ namespace TriangleNet.Meshing
{
id = t.id;
tri.triangle = t;
tri.tri = t;
for (int i = 0; i < 3; i++)
{
tri.orient = i;
tri.Sym(ref neighbor);
nid = neighbor.triangle.id;
nid = neighbor.tri.id;
if (id < nid || nid < 0)
{
@@ -28,7 +28,7 @@ namespace TriangleNet.Meshing.Data
public override string ToString()
{
return String.Format("B-TID {0}", poortri.triangle.hash);
return String.Format("B-TID {0}", poortri.tri.hash);
}
}
}
@@ -30,7 +30,7 @@ namespace TriangleNet.Meshing.Iterators
triangles = mesh.triangles.Values.GetEnumerator();
triangles.MoveNext();
tri.triangle = triangles.Current;
tri.tri = triangles.Current;
tri.orient = 0;
}
@@ -51,7 +51,7 @@ namespace TriangleNet.Meshing.Iterators
public bool MoveNext()
{
if (tri.triangle == null)
if (tri.tri == null)
{
return false;
}
@@ -64,7 +64,7 @@ namespace TriangleNet.Meshing.Iterators
{
if (triangles.MoveNext())
{
tri.triangle = triangles.Current;
tri.tri = triangles.Current;
tri.orient = 0;
}
else
@@ -76,12 +76,12 @@ namespace TriangleNet.Meshing.Iterators
tri.Sym(ref neighbor);
if ((tri.triangle.id < neighbor.triangle.id) || (neighbor.triangle.id == Triangle.EmptyID))
if ((tri.tri.id < neighbor.tri.id) || (neighbor.tri.id == Triangle.EmptyID))
{
p1 = tri.Org();
p2 = tri.Dest();
tri.SegPivot(ref sub);
tri.Pivot(ref sub);
// Boundary mark of dummysub is 0, so we don't need to worry about that.
current = new Edge(p1.id, p2.id, sub.seg.boundary);
@@ -46,7 +46,7 @@ namespace TriangleNet.Meshing.Iterators
{
// WARNING: Don't use foreach, viri list gets modified.
testtri.triangle = viri[i];
testtri.tri = viri[i];
// A triangle is marked as infected by messing with one of its pointers
// to subsegments, setting it to an illegal value. Hence, we have to
// temporarily uninfect this triangle so that we can examine its
@@ -55,7 +55,7 @@ namespace TriangleNet.Meshing.Iterators
testtri.Uninfect();
// Apply function.
func(testtri.triangle);
func(testtri.tri);
// Check each of the triangle's three neighbors.
for (testtri.orient = 0; testtri.orient < 3; testtri.orient++)
@@ -63,16 +63,16 @@ namespace TriangleNet.Meshing.Iterators
// Find the neighbor.
testtri.Sym(ref neighbor);
// Check for a subsegment between the triangle and its neighbor.
testtri.SegPivot(ref neighborsubseg);
testtri.Pivot(ref neighborsubseg);
// Make sure the neighbor exists, is not already infected, and
// isn't protected by a subsegment.
if ((neighbor.triangle.id != Triangle.EmptyID) && !neighbor.IsInfected()
if ((neighbor.tri.id != Triangle.EmptyID) && !neighbor.IsInfected()
&& (neighborsubseg.seg == Segment.Empty))
{
// Infect the neighbor.
neighbor.Infect();
// Ensure that the neighbor's neighbors will be infected.
viri.Add(neighbor.triangle);
viri.Add(neighbor.tri);
}
}
// Remark the triangle as infected, so it doesn't get added to the
+27 -27
View File
@@ -88,9 +88,9 @@ namespace TriangleNet.Meshing
eorg = testsubseg.Org();
edest = testsubseg.Dest();
// Check one neighbor of the subsegment.
testsubseg.TriPivot(ref neighbortri);
testsubseg.Pivot(ref neighbortri);
// Does the neighbor exist, or is this a boundary edge?
if (neighbortri.triangle.id != Triangle.EmptyID)
if (neighbortri.tri.id != Triangle.EmptyID)
{
sides++;
// Find a vertex opposite this subsegment.
@@ -118,9 +118,9 @@ namespace TriangleNet.Meshing
}
// Check the other neighbor of the subsegment.
testsubseg.Sym(ref testsym);
testsym.TriPivot(ref neighbortri);
testsym.Pivot(ref neighbortri);
// Does the neighbor exist, or is this a boundary edge?
if (neighbortri.triangle.id != Triangle.EmptyID)
if (neighbortri.tri.id != Triangle.EmptyID)
{
sides++;
// Find the other vertex opposite this subsegment.
@@ -260,7 +260,7 @@ namespace TriangleNet.Meshing
}
// Nonpositive area constraints are treated as unconstrained.
if ((behavior.VarArea) && (area > testtri.triangle.area) && (testtri.triangle.area > 0.0))
if ((behavior.VarArea) && (area > testtri.tri.area) && (testtri.tri.area > 0.0))
{
// Add this triangle to the list of bad triangles.
queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
@@ -270,7 +270,7 @@ namespace TriangleNet.Meshing
// Check whether the user thinks this triangle is too large.
if (behavior.UserTest != null)
{
if (behavior.UserTest(testtri.triangle, area))
if (behavior.UserTest(testtri.tri, area))
{
queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
return;
@@ -318,15 +318,15 @@ namespace TriangleNet.Meshing
{
// Check if both points lie in a common segment. If they do, the
// skinny triangle is enqueued to be split as usual.
tri1.SegPivot(ref testsub);
tri1.Pivot(ref testsub);
if (testsub.seg == Segment.Empty)
{
// No common segment. Find a subsegment that contains 'torg'.
tri1.Copy(ref tri2);
do
{
tri1.OprevSelf();
tri1.SegPivot(ref testsub);
tri1.Oprev();
tri1.Pivot(ref testsub);
} while (testsub.seg == Segment.Empty);
// Find the endpoints of the containing segment.
org1 = testsub.SegOrg();
@@ -334,8 +334,8 @@ namespace TriangleNet.Meshing
// Find a subsegment that contains 'tdest'.
do
{
tri2.DnextSelf();
tri2.SegPivot(ref testsub);
tri2.Dnext();
tri2.Pivot(ref testsub);
} while (testsub.seg == Segment.Empty);
// Find the endpoints of the containing segment.
org2 = testsub.SegOrg();
@@ -456,13 +456,13 @@ namespace TriangleNet.Meshing
// concentric circles for later splittings.)
// Is the origin shared with another segment?
currentenc.TriPivot(ref enctri);
currentenc.Pivot(ref enctri);
enctri.Lnext(ref testtri);
testtri.SegPivot(ref testsh);
testtri.Pivot(ref testsh);
acuteorg = testsh.seg != Segment.Empty;
// Is the destination shared with another segment?
testtri.LnextSelf();
testtri.SegPivot(ref testsh);
testtri.Lnext();
testtri.Pivot(ref testsh);
acutedest = testsh.seg != Segment.Empty;
// If we're using Chew's algorithm (rather than Ruppert's)
@@ -476,7 +476,7 @@ namespace TriangleNet.Meshing
(eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0))
{
mesh.DeleteVertex(ref testtri);
currentenc.TriPivot(ref enctri);
currentenc.Pivot(ref enctri);
eapex = enctri.Apex();
enctri.Lprev(ref testtri);
}
@@ -484,16 +484,16 @@ namespace TriangleNet.Meshing
// Now, check the other side of the segment, if there's a triangle there.
enctri.Sym(ref testtri);
if (testtri.triangle.id != Triangle.EmptyID)
if (testtri.tri.id != Triangle.EmptyID)
{
// Is the destination shared with another segment?
testtri.LnextSelf();
testtri.SegPivot(ref testsh);
testtri.Lnext();
testtri.Pivot(ref testsh);
acutedest2 = testsh.seg != Segment.Empty;
acutedest = acutedest || acutedest2;
// Is the origin shared with another segment?
testtri.LnextSelf();
testtri.SegPivot(ref testsh);
testtri.Lnext();
testtri.Pivot(ref testsh);
acuteorg2 = testsh.seg != Segment.Empty;
acuteorg = acuteorg || acuteorg2;
@@ -508,7 +508,7 @@ namespace TriangleNet.Meshing
mesh.DeleteVertex(ref testtri);
enctri.Sym(ref testtri);
eapex = testtri.Apex();
testtri.LprevSelf();
testtri.Lprev();
}
}
}
@@ -548,7 +548,7 @@ namespace TriangleNet.Meshing
newvertex = new Vertex(
eorg.x + split * (edest.x - eorg.x),
eorg.y + split * (edest.y - eorg.y),
currentenc.Mark(),
currentenc.seg.boundary,
mesh.nextras);
newvertex.type = VertexType.SegmentVertex;
@@ -610,7 +610,7 @@ namespace TriangleNet.Meshing
}
// Check the two new subsegments to see if they're encroached.
CheckSeg4Encroach(ref currentenc);
currentenc.NextSelf();
currentenc.Next();
CheckSeg4Encroach(ref currentenc);
}
@@ -630,7 +630,7 @@ namespace TriangleNet.Meshing
foreach (var tri in mesh.triangles.Values)
{
triangleloop.triangle = tri;
triangleloop.tri = tri;
// If the triangle is bad, enqueue it.
TestTriangle(ref triangleloop);
@@ -659,7 +659,7 @@ namespace TriangleNet.Meshing
// Make sure that this triangle is still the same triangle it was
// when it was tested and determined to be of bad quality.
// Subsequent transformations may have made it a different triangle.
if (!Otri.IsDead(badotri.triangle) && (borg == badtri.org) &&
if (!Otri.IsDead(badotri.tri) && (borg == badtri.org) &&
(bdest == badtri.dest) && (bapex == badtri.apex))
{
errorflag = false;
@@ -713,7 +713,7 @@ namespace TriangleNet.Meshing
// negative when it should be, so I test eta against xi.)
if (eta < xi)
{
badotri.LprevSelf();
badotri.Lprev();
}
// Insert the circumcenter, searching from the edge of the triangle,
+7 -7
View File
@@ -411,12 +411,12 @@ namespace TriangleNet
break;
case 2:
//printf("Relocate: (%f,%f)\n", tdest[0],tdest[1]);
delotri.LnextSelf();
delotri.Lnext();
mesh.DeleteVertex(ref delotri);
break;
case 3:
//printf("Relocate: (%f,%f)\n", tapex[0],tapex[1]);
delotri.LprevSelf();
delotri.Lprev();
mesh.DeleteVertex(ref delotri);
break;
@@ -1124,12 +1124,12 @@ namespace TriangleNet
break;
case 2:
//printf("Relocate: (%f,%f)\n", tdest[0],tdest[1]);
delotri.LnextSelf();
delotri.Lnext();
mesh.DeleteVertex(ref delotri);
break;
case 3:
//printf("Relocate: (%f,%f)\n", tapex[0],tapex[1]);
delotri.LprevSelf();
delotri.Lprev();
mesh.DeleteVertex(ref delotri);
break;
}
@@ -2364,7 +2364,7 @@ namespace TriangleNet
badotri.Sym(ref neighbor);
// check if it is the one we are looking for by checking the corners
// first check if the neighbor is nonexistent, since it can be on the border
if (neighbor.triangle.id != Triangle.EmptyID)
if (neighbor.tri.id != Triangle.EmptyID)
{
// then check if two wanted corners are also in this triangle
// take the vertices of the candidate neighbor
@@ -4092,7 +4092,7 @@ namespace TriangleNet
}
else if ((tdest.x == newvertex.x) && (tdest.y == newvertex.y))
{
searchtri.LnextSelf();
searchtri.Lnext();
intersect = LocateResult.OnVertex;
searchtri.Copy(ref horiz);
}
@@ -4104,7 +4104,7 @@ namespace TriangleNet
{
// Turn around so that 'searchpoint' is to the left of the
// edge specified by 'searchtri'.
searchtri.SymSelf();
searchtri.Sym();
searchtri.Copy(ref horiz);
intersect = mesh.locator.PreciseLocate(newvertex, ref horiz, false);
}
@@ -6,10 +6,10 @@
namespace TriangleNet.Smoothing
{
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Voronoi.Legacy;
using TriangleNet.Topology.DCEL;
using TriangleNet.Voronoi;
/// <summary>
/// Simple mesh smoother implementation.
@@ -56,48 +56,49 @@ namespace TriangleNet.Smoothing
smoothedMesh.CopyTo((Mesh)mesh);
}
/// <summary>
/// Smooth all free nodes.
/// </summary>
private void Step(Mesh mesh)
{
var voronoi = new BoundedVoronoiLegacy(mesh, false);
var cells = voronoi.Regions;
var voronoi = new BoundedVoronoi(mesh);
double x, y;
foreach (var cell in cells)
foreach (var face in voronoi.Faces)
{
Centroid((List<Point>)cell.Vertices, out x, out y);
if (face.generator.mark == 0)
{
Centroid(face, out x, out y);
cell.Generator.x = x;
cell.Generator.y = y;
face.generator.x = x;
face.generator.y = y;
}
}
}
/// <summary>
/// Calculate the centroid of a polygon.
/// </summary>
/// <param name="points">Points of the polygon.</param>
/// <param name="x">Centroid x coordinate.</param>
/// <param name="y">Centroid y coordinate.</param>
/// <remarks>
/// Based on ANSI C code from the article "Centroid of a Polygon" by Gerard Bashein
/// and Paul R. Detmer in "Graphics Gems IV", Academic Press, 1994
/// </remarks>
private void Centroid(List<Point> points, out double x, out double y)
private void Centroid(Face face, out double x, out double y)
{
int i, j, n = points.Count;
double ai, atmp = 0, xtmp = 0, ytmp = 0;
for (i = n - 1, j = 0; j < n; i = j, j++)
var edge = face.Edge;
var first = edge.Next.ID;
Point p, q;
do
{
ai = points[i].X * points[j].Y - points[j].X * points[i].Y;
p = edge.Origin;
q = edge.Twin.Origin;
ai = p.X * q.Y - q.X * p.Y;
atmp += ai;
xtmp += (points[j].X + points[i].X) * ai;
ytmp += (points[j].Y + points[i].Y) * ai;
}
xtmp += (q.X + p.X) * ai;
ytmp += (q.Y + p.Y) * ai;
edge = edge.Next;
} while (edge.Next.ID != first);
x = xtmp / (3 * atmp);
y = ytmp / (3 * atmp);
@@ -137,7 +137,7 @@ namespace TriangleNet.Tools
// Add edge (1,2) if this is the first occurrence, that is, if
// the edge (1,2) is on a boundary (nid <= 0) or if this triangle
// is the first of the pair in which the edge occurs (tid < nid).
neigh_id = tri.neighbors[2].triangle.id;
neigh_id = tri.neighbors[2].tri.id;
if (neigh_id < 0 || tri_id < neigh_id)
{
@@ -146,7 +146,7 @@ namespace TriangleNet.Tools
}
// Add edge (2,3).
neigh_id = tri.neighbors[0].triangle.id;
neigh_id = tri.neighbors[0].tri.id;
if (neigh_id < 0 || tri_id < neigh_id)
{
@@ -155,7 +155,7 @@ namespace TriangleNet.Tools
}
// Add edge (3,1).
neigh_id = tri.neighbors[1].triangle.id;
neigh_id = tri.neighbors[1].tri.id;
if (neigh_id < 0 || tri_id < neigh_id)
{
@@ -227,7 +227,7 @@ namespace TriangleNet.Tools
// Add edge (1,2) if this is the first occurrence, that is, if
// the edge (1,2) is on a boundary (nid <= 0) or if this triangle
// is the first of the pair in which the edge occurs (tid < nid).
nid = tri.neighbors[2].triangle.id;
nid = tri.neighbors[2].tri.id;
if (nid < 0 || tid < nid)
{
@@ -238,7 +238,7 @@ namespace TriangleNet.Tools
}
// Add edge (2,3).
nid = tri.neighbors[0].triangle.id;
nid = tri.neighbors[0].tri.id;
if (nid < 0 || tid < nid)
{
@@ -249,7 +249,7 @@ namespace TriangleNet.Tools
}
// Add edge (3,1).
nid = tri.neighbors[1].triangle.id;
nid = tri.neighbors[1].tri.id;
if (nid < 0 || tid < nid)
{
@@ -0,0 +1,196 @@
// -----------------------------------------------------------------------
// <copyright file="IntersectionHelper.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Tools
{
using TriangleNet.Geometry;
using Vertex = TriangleNet.Topology.DCEL.Vertex;
public static class IntersectionHelper
{
/// <summary>
/// Compute intersection of two segments.
/// </summary>
/// <param name="p0">Segment 1 start point.</param>
/// <param name="p1">Segment 1 end point.</param>
/// <param name="q0">Segment 2 start point.</param>
/// <param name="q1">Segment 2 end point.</param>
/// <param name="c0">The intersection point.</param>
/// <remarks>
/// This is a special case of segment intersection. Since the calling algorithm assures
/// that a valid intersection exists, there's no need to check for any special cases.
/// </remarks>
public static void IntersectSegments(Point p0, Point p1, Point q0, Point q1, ref Point c0)
{
double ux = p1.x - p0.x;
double uy = p1.y - p0.y;
double vx = q1.x - q0.x;
double vy = q1.y - q0.y;
double wx = p0.x - q0.x;
double wy = p0.y - q0.y;
double d = (ux * vy - uy * vx);
double s = (vx * wy - vy * wx) / d;
// Intersection point
c0.x = p0.X + s * ux;
c0.y = p0.Y + s * uy;
}
/// <summary>
/// Intersect segment with a bounding box.
/// </summary>
/// <param name="rect">The clip rectangle.</param>
/// <param name="p0">Segment endpoint.</param>
/// <param name="p1">Segment endpoint.</param>
/// <param name="c0">The new location of p0 (DCEL vertex).</param>
/// <param name="c1">The new location of p1 (DCEL vertex).</param>
/// <returns>Returns true, if segment is clipped.</returns>
/// <remarks>
/// Based on Liang-Barsky function by Daniel White:
/// http://www.skytopia.com/project/articles/compsci/clipping.html
/// </remarks>
public static bool LiangBarsky(Rectangle rect, Point p0, Point p1, ref Point c0, ref Point c1)
{
// Define the x/y clipping values for the border.
double xmin = rect.Left;
double xmax = rect.Right;
double ymin = rect.Bottom;
double ymax = rect.Top;
// Define the start and end points of the line.
double x0 = p0.X;
double y0 = p0.Y;
double x1 = p1.X;
double y1 = p1.Y;
double t0 = 0.0;
double t1 = 1.0;
double dx = x1 - x0;
double dy = y1 - y0;
double p = 0.0, q = 0.0, r;
for (int edge = 0; edge < 4; edge++)
{
// Traverse through left, right, bottom, top edges.
if (edge == 0) { p = -dx; q = -(xmin - x0); }
if (edge == 1) { p = dx; q = (xmax - x0); }
if (edge == 2) { p = -dy; q = -(ymin - y0); }
if (edge == 3) { p = dy; q = (ymax - y0); }
r = q / p;
if (p == 0 && q < 0) return false; // Don't draw line at all. (parallel line outside)
if (p < 0)
{
if (r > t1) return false; // Don't draw line at all.
else if (r > t0) t0 = r; // Line is clipped!
}
else if (p > 0)
{
if (r < t0) return false; // Don't draw line at all.
else if (r < t1) t1 = r; // Line is clipped!
}
}
c0.X = x0 + t0 * dx;
c0.Y = y0 + t0 * dy;
c1.X = x0 + t1 * dx;
c1.Y = y0 + t1 * dy;
return true; // (clipped) line is drawn
}
/// <summary>
/// Intersect a ray with a bounding box.
/// </summary>
/// <param name="rect">The clip rectangle.</param>
/// <param name="p0">The ray startpoint (inside the box).</param>
/// <param name="p1">Any point in ray direction (NOT the direction vector).</param>
/// <param name="c1">The intersection point (DCEL vertex).</param>
/// <returns>Returns false, if startpoint is outside the box.</returns>
public static bool BoxRayIntersection(Rectangle rect, Point p0, Point p1, ref Point c1)
{
double x = p0.X;
double y = p0.Y;
double dx = p1.x - x;
double dy = p1.y - y;
double t1, x1, y1, t2, x2, y2;
// Bounding box
double xmin = rect.Left;
double xmax = rect.Right;
double ymin = rect.Bottom;
double ymax = rect.Top;
// Check if point is inside the bounds
if (x < xmin || x > xmax || y < ymin || y > ymax)
{
return false;
}
// Calculate the cut through the vertical boundaries
if (dx < 0)
{
// Line going to the left: intersect with x = minX
t1 = (xmin - x) / dx;
x1 = xmin;
y1 = y + t1 * dy;
}
else if (dx > 0)
{
// Line going to the right: intersect with x = maxX
t1 = (xmax - x) / dx;
x1 = xmax;
y1 = y + t1 * dy;
}
else
{
// Line going straight up or down: no intersection possible
t1 = double.MaxValue;
x1 = y1 = 0;
}
// Calculate the cut through upper and lower boundaries
if (dy < 0)
{
// Line going downwards: intersect with y = minY
t2 = (ymin - y) / dy;
x2 = x + t2 * dx;
y2 = ymin;
}
else if (dy > 0)
{
// Line going upwards: intersect with y = maxY
t2 = (ymax - y) / dy;
x2 = x + t2 * dx;
y2 = ymax;
}
else
{
// Horizontal line: no intersection possible
t2 = double.MaxValue;
x2 = y2 = 0;
}
if (t1 < t2)
{
c1.x = x1;
c1.y = y1;
}
else
{
c1.x = x2;
c1.y = y2;
}
return true;
}
}
}
+3 -3
View File
@@ -12,7 +12,7 @@ namespace TriangleNet.Tools
using TriangleNet.Geometry;
/// <summary>
/// A Quadtree implementation optimised for triangles.
/// A Quadtree implementation optimized for triangles.
/// </summary>
public class QuadTree
{
@@ -181,9 +181,9 @@ namespace TriangleNet.Tools
{
// The four sub regions of the quad tree
// +--------------+
// | nw | ne |
// | nw 2 | ne 3 |
// |------+pivot--|
// | sw | se |
// | sw 0 | se 1 |
// +--------------+
Rectangle box;
+1 -1
View File
@@ -156,7 +156,7 @@ namespace TriangleNet.Tools
tri.orient = 0;
foreach (var t in mesh.triangles.Values)
{
tri.triangle = t;
tri.tri = t;
p[0] = tri.Org();
p[1] = tri.Dest();
p[2] = tri.Apex();
+111 -41
View File
@@ -70,71 +70,141 @@ namespace TriangleNet.Topology.DCEL
}
/// <summary>
/// Check if the DCEL ist consistend.
/// Check if the DCEL is consistend.
/// </summary>
/// <param name="closed">If true, faces are assumed to be closed (i.e. all edges must have
/// a valid next pointer).</param>
/// <param name="depth">Maximum edge count of faces (default = 0 means skip check).</param>
/// <returns></returns>
public bool IsConsistent(bool closed = true)
public virtual bool IsConsistent(bool closed = true, int depth = 0)
{
int horrors = 0;
// Check faces
foreach (var face in faces)
// Check vertices for null pointers.
foreach (var vertex in vertices)
{
if (face.edge == null)
if (vertex.id < 0)
{
horrors++;
continue;
}
if (vertex.leaving == null)
{
return false;
}
if (vertex.Leaving.Origin.id != vertex.id)
{
return false;
}
}
// Check half-edges
// 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)
{
var twin = edge.twin;
if (edge.id < 0)
{
continue;
}
if (edge.twin == null)
{
return false;
}
if (edge.origin == null)
{
horrors++;
}
if (twin == null)
{
horrors++;
}
else if (twin.twin != null && edge.id != twin.twin.id)
{
horrors++;
}
if (closed)
{
if (edge.next == null)
{
horrors++;
}
else if (twin != null && edge.next.origin.id != twin.origin.id)
{
horrors++;
}
return false;
}
if (edge.face == null)
{
horrors++;
return false;
}
}
// Check vertices
foreach (var vertex in vertices)
{
if (vertex.leaving == null)
if (closed && edge.next == null)
{
horrors++;
return false;
}
}
return horrors == 0;
// 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;
}
/// <summary>
@@ -144,7 +214,7 @@ namespace TriangleNet.Topology.DCEL
/// <remarks>
/// This method assumes that all faces are closed (i.e. no edge.next pointers are null).
/// </remarks>
internal void ResolveBoundaryEdges()
public void ResolveBoundaryEdges()
{
// Maps vertices to leaving boundary edge.
var map = new Dictionary<int, HalfEdge>();
+78 -74
View File
@@ -18,10 +18,15 @@ namespace TriangleNet.Topology
/// side of the edge. Hence, there are two possible orientations. By convention, the
/// edge is always directed so that the "side" denoted is the right side of the edge.
/// </remarks>
struct Osub
public struct Osub
{
public Segment seg;
public int orient; // Ranges from 0 to 1.
internal Segment seg;
internal int orient; // Ranges from 0 to 1.
public Segment Segment
{
get { return seg; }
}
public override string ToString()
{
@@ -37,18 +42,16 @@ namespace TriangleNet.Topology
/// <summary>
/// Reverse the orientation of a subsegment. [sym(ab) -> ba]
/// </summary>
/// <remarks>ssym() toggles the orientation of a subsegment.
/// </remarks>
public void Sym(ref Osub o2)
public void Sym(ref Osub os)
{
o2.seg = seg;
o2.orient = 1 - orient;
os.seg = seg;
os.orient = 1 - orient;
}
/// <summary>
/// Reverse the orientation of a subsegment. [sym(ab) -> ba]
/// </summary>
public void SymSelf()
public void Sym()
{
orient = 1 - orient;
}
@@ -59,40 +62,33 @@ namespace TriangleNet.Topology
/// <remarks>spivot() finds the other subsegment (from the same segment)
/// that shares the same origin.
/// </remarks>
public void Pivot(ref Osub o2)
public void Pivot(ref Osub os)
{
o2 = seg.subsegs[orient];
//sdecode(sptr, o2);
os = seg.subsegs[orient];
}
/// <summary>
/// Find adjoining subsegment with the same origin. [pivot(ab) -> a*]
/// Finds a triangle abutting a subsegment.
/// </summary>
public void PivotSelf()
internal void Pivot(ref Otri ot)
{
this = seg.subsegs[orient];
//sdecode(sptr, osub);
ot = seg.triangles[orient];
}
/// <summary>
/// Find next subsegment in sequence. [next(ab) -> b*]
/// </summary>
/// <remarks>snext() finds the next subsegment (from the same segment) in
/// sequence; one whose origin is the input subsegment's destination.
/// </remarks>
public void Next(ref Osub o2)
public void Next(ref Osub ot)
{
o2 = seg.subsegs[1 - orient];
//sdecode(sptr, o2);
ot = seg.subsegs[1 - orient];
}
/// <summary>
/// Find next subsegment in sequence. [next(ab) -> b*]
/// </summary>
public void NextSelf()
public void Next()
{
this = seg.subsegs[1 - orient];
//sdecode(sptr, osub);
}
/// <summary>
@@ -111,26 +107,31 @@ namespace TriangleNet.Topology
return seg.vertices[1 - orient];
}
#endregion
#region Osub primitives (internal)
/// <summary>
/// Set the origin or destination of a subsegment.
/// </summary>
public void SetOrg(Vertex ptr)
internal void SetOrg(Vertex vertex)
{
seg.vertices[orient] = ptr;
seg.vertices[orient] = vertex;
}
/// <summary>
/// Set destination of a subsegment.
/// </summary>
public void SetDest(Vertex ptr)
internal void SetDest(Vertex vertex)
{
seg.vertices[1 - orient] = ptr;
seg.vertices[1 - orient] = vertex;
}
/// <summary>
/// Get the origin of the segment that includes the subsegment.
/// </summary>
public Vertex SegOrg()
internal Vertex SegOrg()
{
return seg.vertices[2 + orient];
}
@@ -138,7 +139,7 @@ namespace TriangleNet.Topology
/// <summary>
/// Get the destination of the segment that includes the subsegment.
/// </summary>
public Vertex SegDest()
internal Vertex SegDest()
{
return seg.vertices[3 - orient];
}
@@ -146,17 +147,27 @@ namespace TriangleNet.Topology
/// <summary>
/// Set the origin of the segment that includes the subsegment.
/// </summary>
public void SetSegOrg(Vertex ptr)
internal void SetSegOrg(Vertex vertex)
{
seg.vertices[2 + orient] = ptr;
seg.vertices[2 + orient] = vertex;
}
/// <summary>
/// Set the destination of the segment that includes the subsegment.
/// </summary>
public void SetSegDest(Vertex ptr)
internal void SetSegDest(Vertex vertex)
{
seg.vertices[3 - orient] = ptr;
seg.vertices[3 - orient] = vertex;
}
/* Unused primitives.
/// <summary>
/// Find adjoining subsegment with the same origin. [pivot(ab) -> a*]
/// </summary>
public void PivotSelf()
{
this = seg.subsegs[orient];
}
/// <summary>
@@ -177,25 +188,6 @@ namespace TriangleNet.Topology
seg.boundary = value;
}
/// <summary>
/// Bond two subsegments together. [bond(abc, ba)]
/// </summary>
public void Bond(ref Osub o2)
{
seg.subsegs[orient] = o2;
o2.seg.subsegs[o2.orient] = this;
}
/// <summary>
/// Dissolve a subsegment bond (from one side).
/// </summary>
/// <remarks>Note that the other subsegment will still think it's
/// connected to this subsegment.</remarks>
public void Dissolve()
{
seg.subsegs[orient].seg = Segment.Empty;
}
/// <summary>
/// Copy a subsegment.
/// </summary>
@@ -205,18 +197,47 @@ namespace TriangleNet.Topology
o2.orient = orient;
}
//*/
/// <summary>
/// Bond two subsegments together. [bond(abc, ba)]
/// </summary>
internal void Bond(ref Osub os)
{
seg.subsegs[orient] = os;
os.seg.subsegs[os.orient] = this;
}
/// <summary>
/// Dissolve a subsegment bond (from one side).
/// </summary>
/// <remarks>Note that the other subsegment will still think it's
/// connected to this subsegment.</remarks>
internal void Dissolve()
{
seg.subsegs[orient].seg = Segment.Empty;
}
/// <summary>
/// Test for equality of subsegments.
/// </summary>
public bool Equal(Osub o2)
internal bool Equal(Osub os)
{
return ((seg == o2.seg) && (orient == o2.orient));
return ((seg == os.seg) && (orient == os.orient));
}
/// <summary>
/// Dissolve a bond (from the subsegment side).
/// </summary>
internal void TriDissolve()
{
seg.triangles[orient].tri = Triangle.Empty;
}
/// <summary>
/// Check a subsegment's deallocation.
/// </summary>
public static bool IsDead(Segment sub)
internal static bool IsDead(Segment sub)
{
return sub.subsegs[0].seg == null;
}
@@ -224,29 +245,12 @@ namespace TriangleNet.Topology
/// <summary>
/// Set a subsegment's deallocation.
/// </summary>
public static void Kill(Segment sub)
internal static void Kill(Segment sub)
{
sub.subsegs[0].seg = null;
sub.subsegs[1].seg = null;
}
/// <summary>
/// Finds a triangle abutting a subsegment.
/// </summary>
public void TriPivot(ref Otri ot)
{
ot = seg.triangles[orient];
//decode(ptr, otri)
}
/// <summary>
/// Dissolve a bond (from the subsegment side).
/// </summary>
public void TriDissolve()
{
seg.triangles[orient].triangle = Triangle.Empty;
}
#endregion
}
}
+185 -185
View File
@@ -18,73 +18,97 @@ namespace TriangleNet.Topology
/// of the triangle. Hence, there are three possible orientations. By convention, each
/// edge always points counterclockwise about the corresponding triangle.
/// </remarks>
struct Otri
public struct Otri
{
public Triangle triangle;
public int orient; // Ranges from 0 to 2.
internal Triangle tri;
internal int orient; // Ranges from 0 to 2.
public Triangle Triangle
{
get { return tri; }
set { tri = value; }
}
public override string ToString()
{
if (triangle == null)
if (tri == null)
{
return "O-TID [null]";
}
return String.Format("O-TID {0}", triangle.hash);
return String.Format("O-TID {0}", tri.hash);
}
#region Otri primitives
#region Otri primitives (public)
// For fast access
static readonly int[] plus1Mod3 = { 1, 2, 0 };
static readonly int[] minus1Mod3 = { 2, 0, 1 };
// The following handle manipulation primitives are all described by Guibas
// and Stolfi. However, Guibas and Stolfi use an edge-based data structure,
// The following primitives are all described by Guibas and Stolfi.
// However, Guibas and Stolfi use an edge-based data structure,
// whereas I use a triangle-based data structure.
//
// lnext: finds the next edge (counterclockwise) of a triangle.
//
// onext: spins counterclockwise around a vertex; that is, it finds
// the next edge with the same origin in the counterclockwise direction. This
// edge is part of a different triangle.
//
// oprev: spins clockwise around a vertex; that is, it finds the
// next edge with the same origin in the clockwise direction. This edge is
// part of a different triangle.
//
// dnext: spins counterclockwise around a vertex; that is, it finds
// the next edge with the same destination in the counterclockwise direction.
// This edge is part of a different triangle.
//
// dprev: spins clockwise around a vertex; that is, it finds the
// next edge with the same destination in the clockwise direction. This edge
// is part of a different triangle.
//
// rnext: moves one edge counterclockwise about the adjacent
// triangle. (It's best understood by reading Guibas and Stolfi. It
// involves changing triangles twice.)
//
// rprev: moves one edge clockwise about the adjacent triangle.
// (It's best understood by reading Guibas and Stolfi. It involves
// changing triangles twice.)
/// <summary>
/// Find the abutting triangle; same edge. [sym(abc) -> ba*]
/// </summary>
/// <remarks>
/// Note that the edge direction is necessarily reversed, because the handle specified
/// by an oriented triangle is directed counterclockwise around the triangle.
/// </remarks>
public void Sym(ref Otri o2)
public void Sym(ref Otri ot)
{
//o2 = tri.triangles[orient];
// decode(ptr, otri2);
o2.triangle = triangle.neighbors[orient].triangle;
o2.orient = triangle.neighbors[orient].orient;
ot.tri = tri.neighbors[orient].tri;
ot.orient = tri.neighbors[orient].orient;
}
/// <summary>
/// Find the abutting triangle; same edge. [sym(abc) -> ba*]
/// </summary>
public void SymSelf()
public void Sym()
{
//this = tri.triangles[orient];
// decode(ptr, otri);
int tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
}
// lnext() finds the next edge (counterclockwise) of a triangle.
/// <summary>
/// Find the next edge (counterclockwise) of a triangle. [lnext(abc) -> bca]
/// </summary>
public void Lnext(ref Otri o2)
public void Lnext(ref Otri ot)
{
o2.triangle = triangle;
o2.orient = plus1Mod3[orient];
ot.tri = tri;
ot.orient = plus1Mod3[orient];
}
/// <summary>
/// Find the next edge (counterclockwise) of a triangle. [lnext(abc) -> bca]
/// </summary>
public void LnextSelf()
public void Lnext()
{
orient = plus1Mod3[orient];
}
@@ -92,16 +116,16 @@ namespace TriangleNet.Topology
/// <summary>
/// Find the previous edge (clockwise) of a triangle. [lprev(abc) -> cab]
/// </summary>
public void Lprev(ref Otri o2)
public void Lprev(ref Otri ot)
{
o2.triangle = triangle;
o2.orient = minus1Mod3[orient];
ot.tri = tri;
ot.orient = minus1Mod3[orient];
}
/// <summary>
/// Find the previous edge (clockwise) of a triangle. [lprev(abc) -> cab]
/// </summary>
public void LprevSelf()
public void Lprev()
{
orient = minus1Mod3[orient];
}
@@ -109,62 +133,54 @@ namespace TriangleNet.Topology
/// <summary>
/// Find the next edge counterclockwise with the same origin. [onext(abc) -> ac*]
/// </summary>
/// <remarks>onext() spins counterclockwise around a vertex; that is, it finds
/// the next edge with the same origin in the counterclockwise direction. This
/// edge is part of a different triangle.
/// </remarks>
public void Onext(ref Otri o2)
public void Onext(ref Otri ot)
{
//Lprev(ref o2);
o2.triangle = triangle;
o2.orient = minus1Mod3[orient];
//Lprev(ref ot);
ot.tri = tri;
ot.orient = minus1Mod3[orient];
//o2.SymSelf();
int tmp = o2.orient;
o2.orient = o2.triangle.neighbors[tmp].orient;
o2.triangle = o2.triangle.neighbors[tmp].triangle;
//ot.SymSelf();
int tmp = ot.orient;
ot.orient = ot.tri.neighbors[tmp].orient;
ot.tri = ot.tri.neighbors[tmp].tri;
}
/// <summary>
/// Find the next edge counterclockwise with the same origin. [onext(abc) -> ac*]
/// </summary>
public void OnextSelf()
public void Onext()
{
//LprevSelf();
orient = minus1Mod3[orient];
//SymSelf();
int tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
}
/// <summary>
/// Find the next edge clockwise with the same origin. [oprev(abc) -> a*b]
/// </summary>
/// <remarks>oprev() spins clockwise around a vertex; that is, it finds the
/// next edge with the same origin in the clockwise direction. This edge is
/// part of a different triangle.
/// </remarks>
public void Oprev(ref Otri o2)
public void Oprev(ref Otri ot)
{
//Sym(ref o2);
o2.triangle = triangle.neighbors[orient].triangle;
o2.orient = triangle.neighbors[orient].orient;
//Sym(ref ot);
ot.tri = tri.neighbors[orient].tri;
ot.orient = tri.neighbors[orient].orient;
//o2.LnextSelf();
o2.orient = plus1Mod3[o2.orient];
//ot.LnextSelf();
ot.orient = plus1Mod3[ot.orient];
}
/// <summary>
/// Find the next edge clockwise with the same origin. [oprev(abc) -> a*b]
/// </summary>
public void OprevSelf()
public void Oprev()
{
//SymSelf();
int tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
//LnextSelf();
orient = plus1Mod3[orient];
@@ -173,29 +189,25 @@ namespace TriangleNet.Topology
/// <summary>
/// Find the next edge counterclockwise with the same destination. [dnext(abc) -> *ba]
/// </summary>
/// <remarks>dnext() spins counterclockwise around a vertex; that is, it finds
/// the next edge with the same destination in the counterclockwise direction.
/// This edge is part of a different triangle.
/// </remarks>
public void Dnext(ref Otri o2)
public void Dnext(ref Otri ot)
{
//Sym(ref o2);
o2.triangle = triangle.neighbors[orient].triangle;
o2.orient = triangle.neighbors[orient].orient;
//Sym(ref ot);
ot.tri = tri.neighbors[orient].tri;
ot.orient = tri.neighbors[orient].orient;
//o2.LprevSelf();
o2.orient = minus1Mod3[o2.orient];
//ot.LprevSelf();
ot.orient = minus1Mod3[ot.orient];
}
/// <summary>
/// Find the next edge counterclockwise with the same destination. [dnext(abc) -> *ba]
/// </summary>
public void DnextSelf()
public void Dnext()
{
//SymSelf();
int tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
//LprevSelf();
orient = minus1Mod3[orient];
@@ -204,116 +216,104 @@ namespace TriangleNet.Topology
/// <summary>
/// Find the next edge clockwise with the same destination. [dprev(abc) -> cb*]
/// </summary>
/// <remarks>dprev() spins clockwise around a vertex; that is, it finds the
/// next edge with the same destination in the clockwise direction. This edge
/// is part of a different triangle.
/// </remarks>
public void Dprev(ref Otri o2)
public void Dprev(ref Otri ot)
{
//Lnext(ref o2);
o2.triangle = triangle;
o2.orient = plus1Mod3[orient];
//Lnext(ref ot);
ot.tri = tri;
ot.orient = plus1Mod3[orient];
//o2.SymSelf();
int tmp = o2.orient;
o2.orient = o2.triangle.neighbors[tmp].orient;
o2.triangle = o2.triangle.neighbors[tmp].triangle;
//ot.SymSelf();
int tmp = ot.orient;
ot.orient = ot.tri.neighbors[tmp].orient;
ot.tri = ot.tri.neighbors[tmp].tri;
}
/// <summary>
/// Find the next edge clockwise with the same destination. [dprev(abc) -> cb*]
/// </summary>
public void DprevSelf()
public void Dprev()
{
//LnextSelf();
orient = plus1Mod3[orient];
//SymSelf();
int tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
}
/// <summary>
/// Find the next edge (counterclockwise) of the adjacent triangle. [rnext(abc) -> *a*]
/// </summary>
/// <remarks>rnext() moves one edge counterclockwise about the adjacent
/// triangle. (It's best understood by reading Guibas and Stolfi. It
/// involves changing triangles twice.)
/// </remarks>
public void Rnext(ref Otri o2)
public void Rnext(ref Otri ot)
{
//Sym(ref o2);
o2.triangle = triangle.neighbors[orient].triangle;
o2.orient = triangle.neighbors[orient].orient;
//Sym(ref ot);
ot.tri = tri.neighbors[orient].tri;
ot.orient = tri.neighbors[orient].orient;
//o2.LnextSelf();
o2.orient = plus1Mod3[o2.orient];
//ot.LnextSelf();
ot.orient = plus1Mod3[ot.orient];
//o2.SymSelf();
int tmp = o2.orient;
o2.orient = o2.triangle.neighbors[tmp].orient;
o2.triangle = o2.triangle.neighbors[tmp].triangle;
//ot.SymSelf();
int tmp = ot.orient;
ot.orient = ot.tri.neighbors[tmp].orient;
ot.tri = ot.tri.neighbors[tmp].tri;
}
/// <summary>
/// Find the next edge (counterclockwise) of the adjacent triangle. [rnext(abc) -> *a*]
/// </summary>
public void RnextSelf()
public void Rnext()
{
//SymSelf();
int tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
//LnextSelf();
orient = plus1Mod3[orient];
//SymSelf();
tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
}
/// <summary>
/// Find the previous edge (clockwise) of the adjacent triangle. [rprev(abc) -> b**]
/// </summary>
/// <remarks>rprev() moves one edge clockwise about the adjacent triangle.
/// (It's best understood by reading Guibas and Stolfi. It involves
/// changing triangles twice.)
/// </remarks>
public void Rprev(ref Otri o2)
public void Rprev(ref Otri ot)
{
//Sym(ref o2);
o2.triangle = triangle.neighbors[orient].triangle;
o2.orient = triangle.neighbors[orient].orient;
//Sym(ref ot);
ot.tri = tri.neighbors[orient].tri;
ot.orient = tri.neighbors[orient].orient;
//o2.LprevSelf();
o2.orient = minus1Mod3[o2.orient];
//ot.LprevSelf();
ot.orient = minus1Mod3[ot.orient];
//o2.SymSelf();
int tmp = o2.orient;
o2.orient = o2.triangle.neighbors[tmp].orient;
o2.triangle = o2.triangle.neighbors[tmp].triangle;
//ot.SymSelf();
int tmp = ot.orient;
ot.orient = ot.tri.neighbors[tmp].orient;
ot.tri = ot.tri.neighbors[tmp].tri;
}
/// <summary>
/// Find the previous edge (clockwise) of the adjacent triangle. [rprev(abc) -> b**]
/// </summary>
public void RprevSelf()
public void Rprev()
{
//SymSelf();
int tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
//LprevSelf();
orient = minus1Mod3[orient];
//SymSelf();
tmp = orient;
orient = triangle.neighbors[tmp].orient;
triangle = triangle.neighbors[tmp].triangle;
orient = tri.neighbors[tmp].orient;
tri = tri.neighbors[tmp].tri;
}
/// <summary>
@@ -321,7 +321,7 @@ namespace TriangleNet.Topology
/// </summary>
public Vertex Org()
{
return triangle.vertices[plus1Mod3[orient]];
return tri.vertices[plus1Mod3[orient]];
}
/// <summary>
@@ -329,7 +329,7 @@ namespace TriangleNet.Topology
/// </summary>
public Vertex Dest()
{
return triangle.vertices[minus1Mod3[orient]];
return tri.vertices[minus1Mod3[orient]];
}
/// <summary>
@@ -337,46 +337,56 @@ namespace TriangleNet.Topology
/// </summary>
public Vertex Apex()
{
return triangle.vertices[orient];
return tri.vertices[orient];
}
/// <summary>
/// Copy an oriented triangle.
/// </summary>
public void Copy(ref Otri ot)
{
ot.tri = tri;
ot.orient = orient;
}
#endregion
#region Otri primitives (internal)
/// <summary>
/// Set Origin
/// </summary>
public void SetOrg(Vertex ptr)
internal void SetOrg(Vertex v)
{
triangle.vertices[plus1Mod3[orient]] = ptr;
tri.vertices[plus1Mod3[orient]] = v;
}
/// <summary>
/// Set Destination
/// </summary>
public void SetDest(Vertex ptr)
internal void SetDest(Vertex v)
{
triangle.vertices[minus1Mod3[orient]] = ptr;
tri.vertices[minus1Mod3[orient]] = v;
}
/// <summary>
/// Set Apex
/// </summary>
public void SetApex(Vertex ptr)
internal void SetApex(Vertex v)
{
triangle.vertices[orient] = ptr;
tri.vertices[orient] = v;
}
/// <summary>
/// Bond two triangles together at the resepective handles. [bond(abc, bad)]
/// </summary>
public void Bond(ref Otri o2)
internal void Bond(ref Otri ot)
{
//triangle.neighbors[orient]= o2;
//o2.triangle.neighbors[o2.orient] = this;
tri.neighbors[orient].tri = ot.tri;
tri.neighbors[orient].orient = ot.orient;
triangle.neighbors[orient].triangle = o2.triangle;
triangle.neighbors[orient].orient = o2.orient;
o2.triangle.neighbors[o2.orient].triangle = this.triangle;
o2.triangle.neighbors[o2.orient].orient = this.orient;
ot.tri.neighbors[ot.orient].tri = this.tri;
ot.tri.neighbors[ot.orient].orient = this.orient;
}
/// <summary>
@@ -386,94 +396,84 @@ namespace TriangleNet.Topology
/// this triangle. Usually, however, the other triangle is being deleted
/// entirely, or bonded to another triangle, so it doesn't matter.
/// </remarks>
public void Dissolve()
internal void Dissolve()
{
triangle.neighbors[orient].triangle = Triangle.Empty;
triangle.neighbors[orient].orient = 0;
}
/// <summary>
/// Copy an oriented triangle.
/// </summary>
public void Copy(ref Otri o2)
{
o2.triangle = triangle;
o2.orient = orient;
tri.neighbors[orient].tri = Triangle.Empty;
tri.neighbors[orient].orient = 0;
}
/// <summary>
/// Test for equality of oriented triangles.
/// </summary>
public bool Equal(Otri o2)
internal bool Equal(Otri ot)
{
return ((triangle == o2.triangle) && (orient == o2.orient));
return ((tri == ot.tri) && (orient == ot.orient));
}
/// <summary>
/// Infect a triangle with the virus.
/// </summary>
public void Infect()
internal void Infect()
{
triangle.infected = true;
tri.infected = true;
}
/// <summary>
/// Cure a triangle from the virus.
/// </summary>
public void Uninfect()
internal void Uninfect()
{
triangle.infected = false;
tri.infected = false;
}
/// <summary>
/// Test a triangle for viral infection.
/// </summary>
public bool IsInfected()
internal bool IsInfected()
{
return triangle.infected;
}
/// <summary>
/// Check a triangle's deallocation.
/// </summary>
public static bool IsDead(Triangle tria)
{
return tria.neighbors[0].triangle == null;
}
/// <summary>
/// Set a triangle's deallocation.
/// </summary>
public static void Kill(Triangle tria)
{
tria.neighbors[0].triangle = null;
tria.neighbors[2].triangle = null;
return tri.infected;
}
/// <summary>
/// Finds a subsegment abutting a triangle.
/// </summary>
public void SegPivot(ref Osub os)
internal void Pivot(ref Osub os)
{
os = triangle.subsegs[orient];
//sdecode(sptr, osub)
os = tri.subsegs[orient];
}
/// <summary>
/// Bond a triangle to a subsegment.
/// </summary>
public void SegBond(ref Osub os)
internal void SegBond(ref Osub os)
{
triangle.subsegs[orient] = os;
tri.subsegs[orient] = os;
os.seg.triangles[os.orient] = this;
}
/// <summary>
/// Dissolve a bond (from the triangle side).
/// </summary>
public void SegDissolve()
internal void SegDissolve()
{
triangle.subsegs[orient].seg = Segment.Empty;
tri.subsegs[orient].seg = Segment.Empty;
}
/// <summary>
/// Check a triangle's deallocation.
/// </summary>
internal static bool IsDead(Triangle tria)
{
return tria.neighbors[0].tri == null;
}
/// <summary>
/// Set a triangle's deallocation.
/// </summary>
internal static void Kill(Triangle tri)
{
tri.neighbors[0].tri = null;
tri.neighbors[2].tri = null;
}
#endregion
+3 -3
View File
@@ -59,8 +59,8 @@ namespace TriangleNet.Topology
// Initialize the two adjoining triangles to be "outer space."
triangles = new Otri[2];
triangles[0].triangle = Triangle.Empty;
triangles[1].triangle = Triangle.Empty;
triangles[0].tri = Triangle.Empty;
triangles[1].tri = Triangle.Empty;
// Set the boundary marker to zero.
boundary = 0;
@@ -107,7 +107,7 @@ namespace TriangleNet.Topology
/// </summary>
public ITriangle GetTriangle(int index)
{
return triangles[index].triangle.id == Triangle.EmptyID ? null : triangles[index].triangle;
return triangles[index].tri.id == Triangle.EmptyID ? null : triangles[index].tri;
}
public override int GetHashCode()
+21 -18
View File
@@ -51,9 +51,9 @@ namespace TriangleNet.Topology
// will eventually be changed by various bonding operations, but their
// values don't really matter, as long as they can legally be
// dereferenced.
Empty.neighbors[0].triangle = Empty;
Empty.neighbors[1].triangle = Empty;
Empty.neighbors[2].triangle = Empty;
Empty.neighbors[0].tri = Empty;
Empty.neighbors[1].tri = Empty;
Empty.neighbors[2].tri = Empty;
if (Segment.Empty == null)
{
@@ -83,6 +83,9 @@ namespace TriangleNet.Topology
internal double area;
internal bool infected;
/// <summary>
/// Initializes a new instance of the <see cref="Triangle" /> class.
/// </summary>
public Triangle()
{
// Three NULL vertices.
@@ -96,9 +99,9 @@ namespace TriangleNet.Topology
// Initialize the three adjoining triangles to be "outer space".
neighbors = new Otri[3];
neighbors[0].triangle = Empty;
neighbors[1].triangle = Empty;
neighbors[2].triangle = Empty;
neighbors[0].tri = Empty;
neighbors[1].tri = Empty;
neighbors[2].tri = Empty;
// area = -1.0;
}
@@ -130,14 +133,6 @@ namespace TriangleNet.Topology
get { return this.vertices[1] == null ? -1 : this.vertices[1].id; }
}
/// <summary>
/// Gets the specified corners vertex.
/// </summary>
public Vertex GetVertex(int index)
{
return this.vertices[index]; // TODO: Check range?
}
/// <summary>
/// Gets the third corners vertex id.
/// </summary>
@@ -146,6 +141,14 @@ namespace TriangleNet.Topology
get { return this.vertices[2] == null ? -1 : this.vertices[2].id; }
}
/// <summary>
/// Gets the specified corners vertex.
/// </summary>
public Vertex GetVertex(int index)
{
return this.vertices[index]; // TODO: Check range?
}
public bool SupportsNeighbors
{
get { return true; }
@@ -156,7 +159,7 @@ namespace TriangleNet.Topology
/// </summary>
public int N0
{
get { return this.neighbors[0].triangle.id; }
get { return this.neighbors[0].tri.id; }
}
/// <summary>
@@ -164,7 +167,7 @@ namespace TriangleNet.Topology
/// </summary>
public int N1
{
get { return this.neighbors[1].triangle.id; }
get { return this.neighbors[1].tri.id; }
}
/// <summary>
@@ -172,7 +175,7 @@ namespace TriangleNet.Topology
/// </summary>
public int N2
{
get { return this.neighbors[2].triangle.id; }
get { return this.neighbors[2].tri.id; }
}
/// <summary>
@@ -182,7 +185,7 @@ namespace TriangleNet.Topology
/// <returns>The neigbbor opposite of vertex with given index.</returns>
public ITriangle GetNeighbor(int index)
{
return neighbors[index].triangle.id == EmptyID ? null : neighbors[index].triangle;
return neighbors[index].tri.id == EmptyID ? null : neighbors[index].tri;
}
/// <summary>
+1
View File
@@ -59,6 +59,7 @@
<Compile Include="Meshing\ConstraintMesher.cs" />
<Compile Include="Meshing\Data\BadSubseg.cs" />
<Compile Include="Meshing\Data\BadTriangle.cs" />
<Compile Include="Tools\IntersectionHelper.cs" />
<Compile Include="Topology\DCEL\DcelMesh.cs" />
<Compile Include="Topology\Osub.cs" />
<Compile Include="Topology\Otri.cs" />
+12 -12
View File
@@ -40,7 +40,7 @@ namespace TriangleNet
public void Reset()
{
recenttri.triangle = null; // No triangle has been visited yet.
recenttri.tri = null; // No triangle has been visited yet.
}
/// <summary>
@@ -127,7 +127,7 @@ namespace TriangleNet
// Check whether the apex is the point we seek.
if ((fapex.x == searchpoint.X) && (fapex.y == searchpoint.Y))
{
searchtri.LprevSelf();
searchtri.Lprev();
return LocateResult.OnVertex;
}
// Does the point lie on the other side of the line defined by the
@@ -165,12 +165,12 @@ namespace TriangleNet
// triangle.
if (destorient == 0.0)
{
searchtri.LprevSelf();
searchtri.Lprev();
return LocateResult.OnEdge;
}
if (orgorient == 0.0)
{
searchtri.LnextSelf();
searchtri.Lnext();
return LocateResult.OnEdge;
}
return LocateResult.InTriangle;
@@ -195,7 +195,7 @@ namespace TriangleNet
if (mesh.checksegments && stopatsubsegment)
{
// Check for walking through a subsegment.
backtracktri.SegPivot(ref checkedge);
backtracktri.Pivot(ref checkedge);
if (checkedge.seg != Segment.Empty)
{
// Go back to the last triangle.
@@ -204,7 +204,7 @@ namespace TriangleNet
}
}
// Check for walking right out of the triangulation.
if (searchtri.triangle.id == Triangle.EmptyID)
if (searchtri.tri.id == Triangle.EmptyID)
{
// Go back to the last triangle.
backtracktri.Copy(ref searchtri);
@@ -267,9 +267,9 @@ namespace TriangleNet
// If a recently encountered triangle has been recorded and has not been
// deallocated, test it as a good starting point.
if (recenttri.triangle != null)
if (recenttri.tri != null)
{
if (!Otri.IsDead(recenttri.triangle))
if (!Otri.IsDead(recenttri.tri))
{
torg = recenttri.Org();
if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y))
@@ -293,8 +293,8 @@ namespace TriangleNet
foreach (var key in samples)
{
sampletri.triangle = mesh.triangles[key];
if (!Otri.IsDead(sampletri.triangle))
sampletri.tri = mesh.triangles[key];
if (!Otri.IsDead(sampletri.tri))
{
torg = sampletri.Org();
dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
@@ -317,7 +317,7 @@ namespace TriangleNet
}
if ((tdest.x == searchpoint.X) && (tdest.y == searchpoint.Y))
{
searchtri.LnextSelf();
searchtri.Lnext();
return LocateResult.OnVertex;
}
// Orient 'searchtri' to fit the preconditions of calling preciselocate().
@@ -326,7 +326,7 @@ namespace TriangleNet
{
// Turn around so that 'searchpoint' is to the left of the
// edge specified by 'searchtri'.
searchtri.SymSelf();
searchtri.Sym();
}
else if (ahead == 0.0)
{
@@ -8,6 +8,7 @@ namespace TriangleNet.Voronoi
{
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Tools;
using TriangleNet.Topology.DCEL;
using HVertex = TriangleNet.Topology.DCEL.Vertex;
@@ -38,22 +39,12 @@ namespace TriangleNet.Voronoi
/// </summary>
private void PostProcess()
{
var infEdges = new List<HalfEdge>();
// TODO: save the half-infinite boundary edge in base class
// so we don't have to process the complete list here.
foreach (var edge in base.edges)
foreach (var edge in rays)
{
if (edge.next == null)
{
infEdges.Add(edge);
}
}
var twin = edge.twin;
foreach (var edge in infEdges)
{
var v1 = (TVertex)edge.face.generator;
var v2 = (TVertex)edge.twin.face.generator;
var v2 = (TVertex)twin.face.generator;
double dir = RobustPredicates.CounterClockwise(v1, v2, edge.origin);
@@ -111,7 +102,7 @@ namespace TriangleNet.Voronoi
}
/// <summary>
/// Case 1: edge origin lies outside the domain.
/// Case 2: edge origin lies outside the domain.
/// </summary>
private void HandleCase2(HalfEdge edge, TVertex v1, TVertex v2)
{
@@ -124,8 +115,8 @@ namespace TriangleNet.Voronoi
var e2 = e1.twin.next;
// Find the two intersections with boundary edge.
IntersectSegments(v1, v2, e1.origin, e1.twin.origin, ref p2);
IntersectSegments(v1, v2, e2.origin, e2.twin.origin, ref p1);
IntersectionHelper.IntersectSegments(v1, v2, e1.origin, e1.twin.origin, ref p2);
IntersectionHelper.IntersectSegments(v1, v2, e2.origin, e2.twin.origin, ref p1);
// The infinite edge will now lie on the boundary. Update pointers:
e1.twin.next = edge.twin;
@@ -182,56 +173,5 @@ namespace TriangleNet.Voronoi
return seg.seg.boundary;
}
//*/
/// <summary>
/// Compute intersection of two segments.
/// </summary>
/// <param name="p0">Segment 1 start point.</param>
/// <param name="p1">Segment 1 end point.</param>
/// <param name="q0">Segment 2 start point.</param>
/// <param name="q1">Segment 2 end point.</param>
/// <param name="i0">The intersection point.</param>
/// <remarks>
/// This is a special case of segment intersection. Since the calling algorithm assures
/// that a valid intersection exists, there's no need to check for any special cases.
/// </remarks>
private static void IntersectSegments(Point p0, Point p1, Point q0, Point q1, ref Point i0)
{
double ux = p1.x - p0.x;
double uy = p1.y - p0.y;
double vx = q1.x - q0.x;
double vy = q1.y - q0.y;
double wx = p0.x - q0.x;
double wy = p0.y - q0.y;
double d = (ux * vy - uy * vx);
double s = (vx * wy - vy * wx) / d;
// Intersection point
i0.x = p0.X + s * ux;
i0.y = p0.Y + s * uy;
}
protected override IEnumerable<IEdge> EnumerateEdges()
{
var edges = new List<IEdge>(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;
}
}
}
@@ -130,7 +130,7 @@ namespace TriangleNet.Voronoi.Legacy
// Compue triangle circumcenters
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
pt = RobustPredicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);
pt.id = item.id;
@@ -175,51 +175,51 @@ namespace TriangleNet.Voronoi.Legacy
// Push fe into triangles
e.seg = ss;
e.orient = 0;
e.TriPivot(ref f);
e.Pivot(ref f);
if (f.triangle.id != Triangle.EmptyID && !f.triangle.infected)
if (f.tri.id != Triangle.EmptyID && !f.tri.infected)
{
triangles.Push(f.triangle);
triangles.Push(f.tri);
}
e.SymSelf();
e.TriPivot(ref f);
e.Sym();
e.Pivot(ref f);
if (f.triangle.id != Triangle.EmptyID && !f.triangle.infected)
if (f.tri.id != Triangle.EmptyID && !f.tri.infected)
{
triangles.Push(f.triangle);
triangles.Push(f.tri);
}
// while triangles is non-empty
while (triangles.Count > 0)
{
// Pop f from stack triangles
f.triangle = triangles.Pop();
f.tri = triangles.Pop();
f.orient = 0;
// if f is blinded by e (use P) then
if (TriangleIsBlinded(ref f, ref e))
{
// Tag f as blinded by e
f.triangle.infected = true;
f.tri.infected = true;
blinded++;
// Store association triangle -> subseg
subsegMap.Add(f.triangle.hash, e.seg);
subsegMap.Add(f.tri.hash, e.seg);
// for each adjacent triangle f0 to f do
for (f.orient = 0; f.orient < 3; f.orient++)
{
f.Sym(ref f0);
f0.SegPivot(ref sub1);
f0.Pivot(ref sub1);
// if f0 is finite and tagged non-blind & the common edge
// between f and f0 is unconstrained then
if (f0.triangle.id != Triangle.EmptyID && !f0.triangle.infected && sub1.seg == Segment.Empty)
if (f0.tri.id != Triangle.EmptyID && !f0.tri.infected && sub1.seg == Segment.Empty)
{
// Push f0 into triangles.
triangles.Push(f0.triangle);
triangles.Push(f0.tri);
}
}
}
@@ -246,7 +246,7 @@ namespace TriangleNet.Voronoi.Legacy
Vertex sorg = seg.Org();
Vertex sdest = seg.Dest();
c = this.points[tri.triangle.id];
c = this.points[tri.tri.id];
if (SegmentsIntersect(sorg, sdest, c, torg, out pt, true))
{
@@ -301,19 +301,19 @@ namespace TriangleNet.Voronoi.Legacy
do
{
// Call Lffnext the line going through the circumcenters of f and f_next
cc_f = this.points[f.triangle.id];
cc_f_next = this.points[f_next.triangle.id];
cc_f = this.points[f.tri.id];
cc_f_next = this.points[f_next.tri.id];
// if f is tagged non-blind then
if (!f.triangle.infected)
if (!f.tri.infected)
{
// Insert the circumcenter of f into P
vpoints.Add(cc_f);
if (f_next.triangle.infected)
if (f_next.tri.infected)
{
// Call S_fnext the constrained edge blinding f_next
sfn.seg = subsegMap[f_next.triangle.hash];
sfn.seg = subsegMap[f_next.tri.hash];
// Insert point Lf,f_next /\ Sf_next into P
if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true))
@@ -327,10 +327,10 @@ namespace TriangleNet.Voronoi.Legacy
else
{
// Call Sf the constrained edge blinding f
sf.seg = subsegMap[f.triangle.hash];
sf.seg = subsegMap[f.tri.hash];
// if f_next is tagged non-blind then
if (!f_next.triangle.infected)
if (!f_next.tri.infected)
{
// Insert point Lf,f_next /\ Sf into P
if (SegmentsIntersect(sf.Org(), sf.Dest(), cc_f, cc_f_next, out p, true))
@@ -343,7 +343,7 @@ namespace TriangleNet.Voronoi.Legacy
else
{
// Call Sf_next the constrained edge blinding f_next
sfn.seg = subsegMap[f_next.triangle.hash];
sfn.seg = subsegMap[f_next.tri.hash];
// if Sf != Sf_next then
if (!sf.Equal(sfn))
@@ -370,7 +370,7 @@ namespace TriangleNet.Voronoi.Legacy
f_next.Copy(ref f);
// Call f_next the next triangle counterclockwise around x
f_next.OnextSelf();
f_next.Onext();
}
while (!f.Equal(f_init));
@@ -413,20 +413,20 @@ namespace TriangleNet.Voronoi.Legacy
f_init.Oprev(ref f_prev);
// Is the border to the left?
if (f_prev.triangle.id != Triangle.EmptyID)
if (f_prev.tri.id != Triangle.EmptyID)
{
// Go clockwise until we reach the border (or the initial triangle)
while (f_prev.triangle.id != Triangle.EmptyID && !f_prev.Equal(f_init))
while (f_prev.tri.id != Triangle.EmptyID && !f_prev.Equal(f_init))
{
f_prev.Copy(ref f);
f_prev.OprevSelf();
f_prev.Oprev();
}
f.Copy(ref f_init);
f.Onext(ref f_next);
}
if (f_prev.triangle.id == Triangle.EmptyID)
if (f_prev.tri.id == Triangle.EmptyID)
{
// For vertices on the domain boundaray, add the vertex. For
// internal boundaries don't add it.
@@ -450,11 +450,11 @@ namespace TriangleNet.Voronoi.Legacy
do
{
// Call Lffnext the line going through the circumcenters of f and f_next
cc_f = this.points[f.triangle.id];
cc_f = this.points[f.tri.id];
if (f_next.triangle.id == Triangle.EmptyID)
if (f_next.tri.id == Triangle.EmptyID)
{
if (!f.triangle.infected)
if (!f.tri.infected)
{
// Add last circumcenter
vpoints.Add(cc_f);
@@ -473,18 +473,18 @@ namespace TriangleNet.Voronoi.Legacy
break;
}
cc_f_next = this.points[f_next.triangle.id];
cc_f_next = this.points[f_next.tri.id];
// if f is tagged non-blind then
if (!f.triangle.infected)
if (!f.tri.infected)
{
// Insert the circumcenter of f into P
vpoints.Add(cc_f);
if (f_next.triangle.infected)
if (f_next.tri.infected)
{
// Call S_fnext the constrained edge blinding f_next
sfn.seg = subsegMap[f_next.triangle.hash];
sfn.seg = subsegMap[f_next.tri.hash];
// Insert point Lf,f_next /\ Sf_next into P
if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true))
@@ -498,13 +498,13 @@ namespace TriangleNet.Voronoi.Legacy
else
{
// Call Sf the constrained edge blinding f
sf.seg = subsegMap[f.triangle.hash];
sf.seg = subsegMap[f.tri.hash];
sorg = sf.Org();
sdest = sf.Dest();
// if f_next is tagged non-blind then
if (!f_next.triangle.infected)
if (!f_next.tri.infected)
{
tdest = f.Dest();
tapex = f.Apex();
@@ -534,7 +534,7 @@ namespace TriangleNet.Voronoi.Legacy
else
{
// Call Sf_next the constrained edge blinding f_next
sfn.seg = subsegMap[f_next.triangle.hash];
sfn.seg = subsegMap[f_next.tri.hash];
// if Sf != Sf_next then
if (!sf.Equal(sfn))
@@ -577,7 +577,7 @@ namespace TriangleNet.Voronoi.Legacy
f_next.Copy(ref f);
// Call f_next the next triangle counterclockwise around x
f_next.OnextSelf();
f_next.Onext();
}
while (!f.Equal(f_init));
@@ -118,7 +118,7 @@ namespace TriangleNet.Voronoi.Legacy
// Compue triangle circumcenters
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
tri.tri = item;
pt = RobustPredicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);
pt.id = item.id;
@@ -156,26 +156,26 @@ namespace TriangleNet.Voronoi.Legacy
f_init.Onext(ref f_next);
// Check if f_init lies on the boundary of the triangulation.
if (f_next.triangle.id == Triangle.EmptyID)
if (f_next.tri.id == Triangle.EmptyID)
{
f_init.Oprev(ref f_prev);
if (f_prev.triangle.id != Triangle.EmptyID)
if (f_prev.tri.id != Triangle.EmptyID)
{
f_init.Copy(ref f_next);
// Move one triangle clockwise
f_init.OprevSelf();
f_init.Oprev();
f_init.Copy(ref f);
}
}
// Go counterclockwise until we reach the border or the initial triangle.
while (f_next.triangle.id != Triangle.EmptyID)
while (f_next.tri.id != Triangle.EmptyID)
{
// Add circumcenter of current triangle
vpoints.Add(points[f.triangle.id]);
vpoints.Add(points[f.tri.id]);
region.AddNeighbor(f.triangle.id, regions[f.Apex().id]);
region.AddNeighbor(f.tri.id, regions[f.Apex().id]);
if (f_next.Equal(f_init))
{
@@ -185,7 +185,7 @@ namespace TriangleNet.Voronoi.Legacy
}
f_next.Copy(ref f);
f_next.OnextSelf();
f_next.Onext();
}
// Voronoi cell is unbounded
@@ -197,19 +197,19 @@ namespace TriangleNet.Voronoi.Legacy
// Find the boundary segment id (we use this id to number the endpoints of infinit rays).
f.Lprev(ref f_next);
f_next.SegPivot(ref sub);
f_next.Pivot(ref sub);
sid = sub.seg.hash;
// Last valid f lies at the boundary. Add the circumcenter.
vpoints.Add(points[f.triangle.id]);
region.AddNeighbor(f.triangle.id, regions[f.Apex().id]);
vpoints.Add(points[f.tri.id]);
region.AddNeighbor(f.tri.id, regions[f.Apex().id]);
// Check if the intersection with the bounding box has already been computed.
if (!rayPoints.TryGetValue(sid, out intersection))
{
torg = f.Org();
tapex = f.Apex();
BoxRayIntersection(points[f.triangle.id], torg.y - tapex.y, tapex.x - torg.x, out intersection);
BoxRayIntersection(points[f.tri.id], torg.y - tapex.y, tapex.x - torg.x, out intersection);
// Set the correct id for the vertex
intersection.id = n + rayIndex;
@@ -228,17 +228,17 @@ namespace TriangleNet.Voronoi.Legacy
f_init.Copy(ref f);
f.Oprev(ref f_prev);
while (f_prev.triangle.id != Triangle.EmptyID)
while (f_prev.tri.id != Triangle.EmptyID)
{
vpoints.Add(points[f_prev.triangle.id]);
region.AddNeighbor(f_prev.triangle.id, regions[f_prev.Apex().id]);
vpoints.Add(points[f_prev.tri.id]);
region.AddNeighbor(f_prev.tri.id, regions[f_prev.Apex().id]);
f_prev.Copy(ref f);
f_prev.OprevSelf();
f_prev.Oprev();
}
// Find the boundary segment id.
f.SegPivot(ref sub);
f.Pivot(ref sub);
sid = sub.seg.hash;
if (!rayPoints.TryGetValue(sid, out intersection))
@@ -247,7 +247,7 @@ namespace TriangleNet.Voronoi.Legacy
torg = f.Org();
tdest = f.Dest();
BoxRayIntersection(points[f.triangle.id], tdest.y - torg.y, torg.x - tdest.x, out intersection);
BoxRayIntersection(points[f.tri.id], tdest.y - torg.y, torg.x - tdest.x, out intersection);
// Set the correct id for the vertex
intersection.id = n + rayIndex;
@@ -11,21 +11,13 @@ namespace TriangleNet.Voronoi
public class StandardVoronoi : VoronoiBase
{
public StandardVoronoi(Mesh mesh)
: this(mesh, mesh.bounds)
{
}
public StandardVoronoi(Mesh mesh, Rectangle box)
: base(mesh, true)
{
// We explicitly told the base constructor to call the Generate method, so
// at this point the basic Voronoi diagram is already created.
PostProcess(box);
}
private void PostProcess(Rectangle box)
private void Intersect(Rectangle box)
{
// Compute edge intersections with bounding box.
// TODO: compute edge intersections with bounding box.
}
}
}
+50 -18
View File
@@ -20,6 +20,10 @@ namespace TriangleNet.Voronoi
/// </summary>
public abstract class VoronoiBase : DcelMesh
{
// List of infinite half-edges, i.e. half-edges that start at circumcenters of triangles
// which lie on the domain boundary.
protected List<HalfEdge> rays;
/// <summary>
/// Initializes a new instance of the <see cref="VoronoiBase" /> class.
/// </summary>
@@ -39,18 +43,19 @@ namespace TriangleNet.Voronoi
/// Generate the Voronoi diagram from given triangle mesh..
/// </summary>
/// <param name="mesh"></param>
/// <param name="bounded"></param>
protected void Generate(Mesh mesh)
{
mesh.Renumber();
mesh.MakeVertexMap();
base.edges = new List<HalfEdge>();
this.rays = new List<HalfEdge>();
// Allocate space for voronoi diagram
// Allocate space for Voronoi diagram.
var vertices = new Vertex[mesh.triangles.Count + mesh.hullsize];
var faces = new Face[mesh.vertices.Count];
// Compute triangles circumcenters and setup bounding box
// Compute triangles circumcenters.
var map = ComputeVertices(mesh, vertices);
// Create all Voronoi faces.
@@ -87,7 +92,7 @@ namespace TriangleNet.Voronoi
foreach (var t in mesh.triangles.Values)
{
id = t.id;
tri.triangle = t;
tri.tri = t;
pt = RobustPredicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);
@@ -104,6 +109,9 @@ namespace TriangleNet.Voronoi
/// <summary>
/// Compute the edges of the Voronoi diagram.
/// </summary>
/// <param name="mesh"></param>
/// <param name="vertices"></param>
/// <param name="faces"></param>
/// <param name="map">Empty vertex map.</param>
protected void ComputeEdges(Mesh mesh, Vertex[] vertices, Face[] faces, List<HalfEdge>[] map)
{
@@ -132,14 +140,14 @@ namespace TriangleNet.Voronoi
{
id = t.id;
tri.triangle = t;
tri.tri = t;
for (int i = 0; i < 3; i++)
{
tri.orient = i;
tri.Sym(ref neighbor);
nid = neighbor.triangle.id;
nid = neighbor.tri.id;
if (id < nid || nid < 0)
{
@@ -156,7 +164,7 @@ namespace TriangleNet.Voronoi
// in the Voronoi diagram, i.e. two half-edges will be created.
if (nid < 0)
{
// Undounded edge, direction perpendicular to the boundary edge,
// Unbounded edge, direction perpendicular to the boundary edge,
// pointing outwards.
px = dest.y - org.y;
py = org.x - dest.x;
@@ -169,31 +177,31 @@ namespace TriangleNet.Voronoi
edge = new HalfEdge(end, face);
twin = new HalfEdge(vertex, neighborFace);
end.leaving = edge;
// 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
{
// Create half-edges.
edge = new HalfEdge(vertices[nid], face);
twin = new HalfEdge(vertex, neighborFace);
end = vertices[nid];
// Set leaving edges.
vertices[nid].leaving = edge;
vertex.leaving = twin;
// Create half-edges.
edge = new HalfEdge(end, face);
twin = new HalfEdge(vertex, neighborFace);
// Add to vertex map.
map[nid].Add(edge);
map[id].Add(twin);
}
// Setup twin edges.
vertex.leaving = twin;
end.leaving = edge;
edge.twin = twin;
twin.twin = edge;
@@ -211,11 +219,11 @@ namespace TriangleNet.Voronoi
/// Connect all edges of the Voronoi diagram.
/// </summary>
/// <param name="map">Maps all vertices to a list of leaving edges.</param>
protected void ConnectEdges(List<HalfEdge>[] map)
protected virtual void ConnectEdges(List<HalfEdge>[] map)
{
int length = map.Length;
// For each halfe-edge, find its successor in the connected face.
// For each half-edge, find its successor in the connected face.
foreach (var edge in this.edges)
{
var face = edge.face.generator.id;
@@ -223,6 +231,8 @@ namespace TriangleNet.Voronoi
// 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
@@ -238,5 +248,27 @@ namespace TriangleNet.Voronoi
}
}
}
protected override IEnumerable<IEdge> EnumerateEdges()
{
var edges = new List<IEdge>(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;
}
}
}