Minor changes to (standard) Voronoi diagram

git-svn-id: https://triangle.svn.codeplex.com/svn@74966 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5
This commit is contained in:
SND\wo80_cp
2014-05-12 17:13:51 +00:00
parent 4963b68eef
commit 94fafe03c0
16 changed files with 285 additions and 184 deletions
+8 -8
View File
@@ -69,10 +69,10 @@ namespace MeshRenderer.Core
} }
this.Bounds = new BoundingBox( this.Bounds = new BoundingBox(
(float)data.Bounds.Xmin, (float)data.Bounds.MinX,
(float)data.Bounds.Xmax, (float)data.Bounds.MaxX,
(float)data.Bounds.Ymin, (float)data.Bounds.MinY,
(float)data.Bounds.Ymax); (float)data.Bounds.MaxY);
} }
/// <summary> /// <summary>
@@ -150,10 +150,10 @@ namespace MeshRenderer.Core
this.Triangles = triangles.ToArray(); this.Triangles = triangles.ToArray();
this.Bounds = new BoundingBox( this.Bounds = new BoundingBox(
(float)mesh.Bounds.Xmin, (float)mesh.Bounds.MinX,
(float)mesh.Bounds.Xmax, (float)mesh.Bounds.MaxX,
(float)mesh.Bounds.Ymin, (float)mesh.Bounds.MinY,
(float)mesh.Bounds.Ymax); (float)mesh.Bounds.MaxY);
} }
/// <summary> /// <summary>
+40 -22
View File
@@ -8,6 +8,7 @@ using MeshExplorer.IO;
using MeshRenderer.Core; using MeshRenderer.Core;
using TriangleNet; using TriangleNet;
using TriangleNet.Geometry; using TriangleNet.Geometry;
using TriangleNet.Log;
using TriangleNet.Tools; using TriangleNet.Tools;
namespace MeshExplorer namespace MeshExplorer
@@ -340,31 +341,38 @@ namespace MeshExplorer
private bool Open(string filename) private bool Open(string filename)
{ {
if (FileProcessor.ContainsMeshData(filename)) if (!FileProcessor.CanHandleFile(filename))
{ {
if (DarkMessageBox.Show("Import mesh", Settings.ImportString, // TODO: show message.
"Do you want to import the mesh?", MessageBoxButtons.YesNo) == DialogResult.OK)
{
input = null;
mesh = FileProcessor.Import(filename);
if (mesh != null)
{
statisticView.UpdateStatistic(mesh);
// Update settings
settings.CurrentFile = Path.GetFileName(filename);
HandleMeshImport();
btnSmooth.Enabled = true; // TODO: Remove
}
// else Message
return true;
}
} }
else
{
if (FileProcessor.ContainsMeshData(filename))
{
if (DarkMessageBox.Show("Import mesh", Settings.ImportString,
"Do you want to import the mesh?", MessageBoxButtons.YesNo) == DialogResult.OK)
{
input = null;
mesh = FileProcessor.Import(filename);
input = FileProcessor.Read(filename); if (mesh != null)
{
statisticView.UpdateStatistic(mesh);
// Update settings
settings.CurrentFile = Path.GetFileName(filename);
HandleMeshImport();
btnSmooth.Enabled = true; // TODO: Remove
}
// else Message
return true;
}
}
input = FileProcessor.Read(filename);
}
if (input != null) if (input != null)
{ {
@@ -737,6 +745,16 @@ namespace MeshExplorer
Behavior.Verbose = save; Behavior.Verbose = save;
if (isConsistent)
{
SimpleLog.Instance.Info("Mesh topology appears to be consistent.");
}
if (isDelaunay)
{
SimpleLog.Instance.Info("Mesh is (conforming) Delaunay.");
}
ShowLog(); ShowLog();
} }
} }
+4 -4
View File
@@ -321,10 +321,10 @@ namespace MeshExplorer.IO
private void UpdateMetrics(BoundingBox bounds) private void UpdateMetrics(BoundingBox bounds)
{ {
x_max = bounds.Xmax; x_max = bounds.MaxX;
x_min = bounds.Xmin; x_min = bounds.MinX;
y_max = bounds.Ymax; y_max = bounds.MaxY;
y_min = bounds.Ymin; y_min = bounds.MinY;
// Enlarge width 5% on each side // Enlarge width 5% on each side
x_scale = x_max - x_min; x_scale = x_max - x_min;
+2 -2
View File
@@ -49,8 +49,8 @@ namespace MeshExplorer.IO
scale = width / ((float)bounds.Width + 2 * margin); scale = width / ((float)bounds.Width + 2 * margin);
int x_offset = -(int)((bounds.Xmin - margin) * scale); int x_offset = -(int)((bounds.MinX - margin) * scale);
int y_offset = (int)((bounds.Ymax + margin) * scale); int y_offset = (int)((bounds.MaxY + margin) * scale);
int height = (int)((bounds.Height + 2 * margin) * scale); int height = (int)((bounds.Height + 2 * margin) * scale);
@@ -48,8 +48,8 @@ namespace MeshExplorer.Topology
//zoom.ClipMargin = 10.0f; //zoom.ClipMargin = 10.0f;
var b = mesh.Bounds; var b = mesh.Bounds;
zoom.Update(new BoundingBox((float)b.Xmin, (float)b.Xmax, zoom.Update(new BoundingBox((float)b.MinX, (float)b.MaxX,
(float)b.Ymin, (float)b.Ymax)); (float)b.MinY, (float)b.MaxY));
InitializeBuffer(); InitializeBuffer();
@@ -42,9 +42,9 @@ namespace TriangleNet.Algorithm
width = 1.0; width = 1.0;
} }
// Create the vertices of the bounding box. // Create the vertices of the bounding box.
mesh.infvertex1 = new Vertex(box.Xmin - 50.0 * width, box.Ymin - 40.0 * width); mesh.infvertex1 = new Vertex(box.MinX - 50.0 * width, box.MinY - 40.0 * width);
mesh.infvertex2 = new Vertex(box.Xmax + 50.0 * width, box.Ymin - 40.0 * width); mesh.infvertex2 = new Vertex(box.MaxX + 50.0 * width, box.MinY - 40.0 * width);
mesh.infvertex3 = new Vertex(0.5 * (box.Xmin + box.Xmax), box.Ymax + 60.0 * width); mesh.infvertex3 = new Vertex(0.5 * (box.MinX + box.MaxX), box.MaxY + 60.0 * width);
// Create the bounding box. // Create the bounding box.
mesh.MakeTriangle(ref inftri); mesh.MakeTriangle(ref inftri);
+2 -2
View File
@@ -525,7 +525,7 @@ namespace TriangleNet.Algorithm
// Nonexistent x value used as a flag to mark circle events in sweepline // Nonexistent x value used as a flag to mark circle events in sweepline
// Delaunay algorithm. // Delaunay algorithm.
xminextreme = 10 * mesh.bounds.Xmin - 9 * mesh.bounds.Xmax; xminextreme = 10 * mesh.bounds.MinX - 9 * mesh.bounds.MaxX;
SweepEvent[] eventheap; SweepEvent[] eventheap;
@@ -603,7 +603,7 @@ namespace TriangleNet.Algorithm
HeapDelete(eventheap, heapsize, 0); HeapDelete(eventheap, heapsize, 0);
heapsize--; heapsize--;
check4events = true; check4events = true;
if (nextevent.xkey < mesh.bounds.Xmin) if (nextevent.xkey < mesh.bounds.MinX)
{ {
fliptri = nextevent.otriEvent; fliptri = nextevent.otriEvent;
fliptri.Oprev(ref farlefttri); fliptri.Oprev(ref farlefttri);
+53 -26
View File
@@ -19,11 +19,13 @@ namespace TriangleNet.Geometry
/// Initializes a new instance of the <see cref="BoundingBox" /> class. /// Initializes a new instance of the <see cref="BoundingBox" /> class.
/// </summary> /// </summary>
public BoundingBox() public BoundingBox()
: this(double.MaxValue, double.MaxValue, -double.MaxValue, -double.MaxValue)
{
}
public BoundingBox(BoundingBox other)
: this(other.MinX, other.MinY, other.MaxX, other.MaxY)
{ {
xmin = double.MaxValue;
ymin = double.MaxValue;
xmax = -double.MaxValue;
ymax = -double.MaxValue;
} }
/// <summary> /// <summary>
@@ -37,39 +39,39 @@ namespace TriangleNet.Geometry
public BoundingBox(double xmin, double ymin, double xmax, double ymax) public BoundingBox(double xmin, double ymin, double xmax, double ymax)
{ {
this.xmin = xmin; this.xmin = xmin;
this.ymin = ymin;
this.xmax = xmax; this.xmax = xmax;
this.ymin = ymin;
this.ymax = ymax; this.ymax = ymax;
} }
/// <summary> /// <summary>
/// Gets the minimum x value (left boundary). /// Gets the minimum x value (left boundary).
/// </summary> /// </summary>
public double Xmin public double MinX
{ {
get { return xmin; } get { return xmin; }
} }
/// <summary>
/// Gets the minimum y value (bottom boundary).
/// </summary>
public double Ymin
{
get { return ymin; }
}
/// <summary> /// <summary>
/// Gets the maximum x value (right boundary). /// Gets the maximum x value (right boundary).
/// </summary> /// </summary>
public double Xmax public double MaxX
{ {
get { return xmax; } get { return xmax; }
} }
/// <summary>
/// Gets the minimum y value (bottom boundary).
/// </summary>
public double MinY
{
get { return ymin; }
}
/// <summary> /// <summary>
/// Gets the maximum y value (top boundary). /// Gets the maximum y value (top boundary).
/// </summary> /// </summary>
public double Ymax public double MaxY
{ {
get { return ymax; } get { return ymax; }
} }
@@ -91,11 +93,24 @@ namespace TriangleNet.Geometry
} }
/// <summary> /// <summary>
/// Update bounds. /// Scale bounds.
/// </summary>
/// <param name="dx">Add dx to left and right bounds.</param>
/// <param name="dy">Add dy to top and bottom bounds.</param>
public void Resize(double dx, double dy)
{
xmin -= dx;
xmax += dx;
ymin -= dy;
ymax += dy;
}
/// <summary>
/// Expand rectangle to include given point.
/// </summary> /// </summary>
/// <param name="x">X coordinate.</param> /// <param name="x">X coordinate.</param>
/// <param name="y">Y coordinate.</param> /// <param name="y">Y coordinate.</param>
public void Update(double x, double y) public void Expand(double x, double y)
{ {
xmin = Math.Min(xmin, x); xmin = Math.Min(xmin, x);
ymin = Math.Min(ymin, y); ymin = Math.Min(ymin, y);
@@ -104,16 +119,16 @@ namespace TriangleNet.Geometry
} }
/// <summary> /// <summary>
/// Scale bounds. /// Expand rectangle to include given rectangle.
/// </summary> /// </summary>
/// <param name="dx">Add dx to left and right bounds.</param> /// <param name="x">X coordinate.</param>
/// <param name="dy">Add dy to top and bottom bounds.</param> /// <param name="y">Y coordinate.</param>
public void Scale(double dx, double dy) public void Expand(BoundingBox other)
{ {
xmin -= dx; xmin = Math.Min(xmin, other.xmin);
xmax += dx; ymin = Math.Min(ymin, other.ymin);
ymin -= dy; xmax = Math.Max(xmax, other.xmax);
ymax += dy; ymax = Math.Max(ymax, other.ymax);
} }
/// <summary> /// <summary>
@@ -125,5 +140,17 @@ namespace TriangleNet.Geometry
{ {
return ((pt.x >= xmin) && (pt.x <= xmax) && (pt.y >= ymin) && (pt.y <= ymax)); return ((pt.x >= xmin) && (pt.x <= xmax) && (pt.y >= ymin) && (pt.y <= ymax));
} }
public bool Contains(BoundingBox other)
{
return (xmin <= other.MinX && other.MaxX <= xmax
&& ymin <= other.MinY && other.MaxY <= ymax);
}
public bool Intersects(BoundingBox other)
{
return (other.MinX < xmax && xmin < other.MaxX
&& other.MinY < ymax && ymin < other.MaxY);
}
} }
} }
@@ -139,7 +139,7 @@ namespace TriangleNet.Geometry
public void AddPoint(double x, double y, int boundary) public void AddPoint(double x, double y, int boundary)
{ {
points.Add(new Vertex(x, y, boundary)); points.Add(new Vertex(x, y, boundary));
bounds.Update(x, y); bounds.Expand(x, y);
} }
/// <summary> /// <summary>
@@ -177,7 +177,7 @@ namespace TriangleNet.Geometry
} }
points.Add(new Vertex(x, y, boundary) { attributes = attribs }); points.Add(new Vertex(x, y, boundary) { attributes = attribs });
bounds.Update(x, y); bounds.Expand(x, y);
} }
/// <summary> /// <summary>
@@ -202,7 +202,7 @@ namespace TriangleNet.Geometry
} }
points.Add(v); points.Add(v);
bounds.Update(v.x, v.y); bounds.Expand(v.x, v.y);
} }
/// <summary> /// <summary>
+61 -38
View File
@@ -43,16 +43,21 @@ namespace TriangleNet
triorg = tri.Org(); triorg = tri.Org();
tridest = tri.Dest(); tridest = tri.Dest();
if (tri.orient == 0) if (tri.orient == 0)
{ // Only test for inversion once. {
// Only test for inversion once.
// Test if the triangle is flat or inverted. // Test if the triangle is flat or inverted.
triapex = tri.Apex(); triapex = tri.Apex();
if (Primitives.CounterClockwise(triorg, tridest, triapex) <= 0.0) if (Primitives.CounterClockwise(triorg, tridest, triapex) <= 0.0)
{ {
logger.Warning("Triangle is flat or inverted.", if (Behavior.Verbose)
"Quality.CheckMesh()"); {
logger.Warning("Triangle is flat or inverted.", "Quality.CheckMesh()");
}
horrors++; horrors++;
} }
} }
// Find the neighboring triangle on this edge. // Find the neighboring triangle on this edge.
tri.Sym(ref oppotri); tri.Sym(ref oppotri);
if (oppotri.triangle != Mesh.dummytri) if (oppotri.triangle != Mesh.dummytri)
@@ -61,7 +66,7 @@ namespace TriangleNet
oppotri.Sym(ref oppooppotri); oppotri.Sym(ref oppooppotri);
if ((tri.triangle != oppooppotri.triangle) || (tri.orient != oppooppotri.orient)) if ((tri.triangle != oppooppotri.triangle) || (tri.orient != oppooppotri.orient))
{ {
if (tri.triangle == oppooppotri.triangle) if (tri.triangle == oppooppotri.triangle && Behavior.Verbose)
{ {
logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)", logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)",
"Quality.CheckMesh()"); "Quality.CheckMesh()");
@@ -75,8 +80,11 @@ namespace TriangleNet
oppodest = oppotri.Dest(); oppodest = oppotri.Dest();
if ((triorg != oppodest) || (tridest != oppoorg)) if ((triorg != oppodest) || (tridest != oppoorg))
{ {
logger.Warning("Mismatched edge coordinates between two triangles.", if (Behavior.Verbose)
"Quality.CheckMesh()"); {
logger.Warning("Mismatched edge coordinates between two triangles.",
"Quality.CheckMesh()");
}
horrors++; horrors++;
} }
@@ -88,18 +96,13 @@ namespace TriangleNet
mesh.MakeVertexMap(); mesh.MakeVertexMap();
foreach (var v in mesh.vertices.Values) foreach (var v in mesh.vertices.Values)
{ {
if (v.tri.triangle == null) if (v.tri.triangle == null && Behavior.Verbose)
{ {
logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)", logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)",
"Quality.CheckMesh()"); "Quality.CheckMesh()");
} }
} }
if (horrors == 0) // && Behavior.Verbose
{
logger.Info("Mesh topology appears to be consistent.");
}
// Restore the status of exact arithmetic. // Restore the status of exact arithmetic.
Behavior.NoExact = saveexact; Behavior.NoExact = saveexact;
@@ -107,15 +110,32 @@ namespace TriangleNet
} }
/// <summary> /// <summary>
/// Ensure that the mesh is (constrained) Delaunay. /// Check if the mesh is (conforming) Delaunay.
/// </summary> /// </summary>
public static bool IsDelaunay(Mesh mesh) public static bool IsDelaunay(Mesh mesh)
{
return IsDelaunay(mesh, false);
}
/// <summary>
/// Check if that the mesh is (constrained) Delaunay.
/// </summary>
public static bool IsConstrainedDelaunay(Mesh mesh)
{
return IsDelaunay(mesh, true);
}
/// <summary>
/// Ensure that the mesh is (constrained) Delaunay.
/// </summary>
private static bool IsDelaunay(Mesh mesh, bool constrained)
{ {
Otri loop = default(Otri); Otri loop = default(Otri);
Otri oppotri = default(Otri); Otri oppotri = default(Otri);
Osub opposubseg = default(Osub); Osub opposubseg = default(Osub);
Vertex triorg, tridest, triapex; Vertex org, dest, apex;
Vertex oppoapex; Vertex oppoapex;
Vertex inf1, inf2, inf3;
bool shouldbedelaunay; bool shouldbedelaunay;
int horrors; int horrors;
bool saveexact; bool saveexact;
@@ -127,49 +147,57 @@ namespace TriangleNet
Behavior.NoExact = false; Behavior.NoExact = false;
horrors = 0; horrors = 0;
inf1 = mesh.infvertex1;
inf2 = mesh.infvertex2;
inf3 = mesh.infvertex3;
// Run through the list of triangles, checking each one. // Run through the list of triangles, checking each one.
foreach (var tri in mesh.triangles.Values) foreach (var tri in mesh.triangles.Values)
{ {
loop.triangle = tri; loop.triangle = tri;
// Check all three edges of the triangle. // Check all three edges of the triangle.
for (loop.orient = 0; loop.orient < 3; for (loop.orient = 0; loop.orient < 3; loop.orient++)
loop.orient++)
{ {
triorg = loop.Org(); org = loop.Org();
tridest = loop.Dest(); dest = loop.Dest();
triapex = loop.Apex(); apex = loop.Apex();
loop.Sym(ref oppotri); loop.Sym(ref oppotri);
oppoapex = oppotri.Apex(); oppoapex = oppotri.Apex();
// Only test that the edge is locally Delaunay if there is an // Only test that the edge is locally Delaunay if there is an
// adjoining triangle whose pointer is larger (to ensure that // adjoining triangle whose pointer is larger (to ensure that
// each pair isn't tested twice). // each pair isn't tested twice).
shouldbedelaunay = (oppotri.triangle != Mesh.dummytri) && shouldbedelaunay = (loop.triangle.id < oppotri.triangle.id) &&
!Otri.IsDead(oppotri.triangle) && loop.triangle.id < oppotri.triangle.id && !Otri.IsDead(oppotri.triangle) && (oppotri.triangle != Mesh.dummytri) &&
(triorg != mesh.infvertex1) && (triorg != mesh.infvertex2) && (org != inf1) && (org != inf2) && (org != inf3) &&
(triorg != mesh.infvertex3) && (dest != inf1) && (dest != inf2) && (dest != inf3) &&
(tridest != mesh.infvertex1) && (tridest != mesh.infvertex2) && (apex != inf1) && (apex != inf2) && (apex != inf3) &&
(tridest != mesh.infvertex3) && (oppoapex != inf1) && (oppoapex != inf2) && (oppoapex != inf3);
(triapex != mesh.infvertex1) && (triapex != mesh.infvertex2) &&
(triapex != mesh.infvertex3) && if (constrained && mesh.checksegments && shouldbedelaunay)
(oppoapex != mesh.infvertex1) && (oppoapex != mesh.infvertex2) &&
(oppoapex != mesh.infvertex3);
if (mesh.checksegments && shouldbedelaunay)
{ {
// If a subsegment separates the triangles, then the edge is // If a subsegment separates the triangles, then the edge is
// constrained, so no local Delaunay test should be done. // constrained, so no local Delaunay test should be done.
loop.SegPivot(ref opposubseg); loop.SegPivot(ref opposubseg);
if (opposubseg.seg != Mesh.dummysub) if (opposubseg.seg != Mesh.dummysub)
{ {
shouldbedelaunay = false; shouldbedelaunay = false;
} }
} }
if (shouldbedelaunay) if (shouldbedelaunay)
{ {
if (Primitives.NonRegular(triorg, tridest, triapex, oppoapex) > 0.0) if (Primitives.NonRegular(org, dest, apex, oppoapex) > 0.0)
{ {
logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).", if (Behavior.Verbose)
loop.triangle.id, oppotri.triangle.id), "Quality.CheckDelaunay()"); {
logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).",
loop.triangle.id, oppotri.triangle.id), "Quality.CheckDelaunay()");
}
horrors++; horrors++;
} }
} }
@@ -177,11 +205,6 @@ namespace TriangleNet
} }
if (horrors == 0) // && Behavior.Verbose
{
logger.Info("Mesh is Delaunay.");
}
// Restore the status of exact arithmetic. // Restore the status of exact arithmetic.
Behavior.NoExact = saveexact; Behavior.NoExact = saveexact;
@@ -1,5 +1,6 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// <copyright file="AdjacencyMatrix.cs" company=""> // <copyright file="AdjacencyMatrix.cs" company="">
// Original Matlab code by John Burkardt, Florida State University
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright> // </copyright>
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -67,7 +67,7 @@ namespace TriangleNet.Tools
/// <summary> /// <summary>
/// Gets the list of Voronoi regions. /// Gets the list of Voronoi regions.
/// </summary> /// </summary>
public List<VoronoiRegion> Regions public ICollection<VoronoiRegion> Regions
{ {
get { return regions; } get { return regions; }
} }
@@ -95,11 +95,11 @@ namespace TriangleNet.Tools
// TODO: Need a reliable way to check if a vertex is on a segment // TODO: Need a reliable way to check if a vertex is on a segment
if (v.type == VertexType.FreeVertex || v.Boundary == 0) if (v.type == VertexType.FreeVertex || v.Boundary == 0)
{ {
ConstructBvdCell(v); ConstructCell(v);
} }
else if (includeBoundary) else if (includeBoundary)
{ {
ConstructBoundaryBvdCell(v); ConstructBoundaryCell(v);
} }
} }
@@ -262,7 +262,7 @@ namespace TriangleNet.Tools
return false; return false;
} }
private void ConstructBvdCell(Vertex vertex) private void ConstructCell(Vertex vertex)
{ {
VoronoiRegion region = new VoronoiRegion(vertex); VoronoiRegion region = new VoronoiRegion(vertex);
regions.Add(region); regions.Add(region);
@@ -374,7 +374,7 @@ namespace TriangleNet.Tools
region.Add(vpoints); region.Add(vpoints);
} }
private void ConstructBoundaryBvdCell(Vertex vertex) private void ConstructBoundaryCell(Vertex vertex)
{ {
VoronoiRegion region = new VoronoiRegion(vertex); VoronoiRegion region = new VoronoiRegion(vertex);
regions.Add(region); regions.Add(region);
+2 -2
View File
@@ -10,7 +10,7 @@ namespace TriangleNet.Tools
using TriangleNet.Geometry; using TriangleNet.Geometry;
/// <summary> /// <summary>
/// TODO: Update summary. /// Voronoi diagram interface.
/// </summary> /// </summary>
public interface IVoronoi public interface IVoronoi
{ {
@@ -22,6 +22,6 @@ namespace TriangleNet.Tools
/// <summary> /// <summary>
/// Gets the list of Voronoi regions. /// Gets the list of Voronoi regions.
/// </summary> /// </summary>
List<VoronoiRegion> Regions { get; } ICollection<VoronoiRegion> Regions { get; }
} }
} }
+22 -22
View File
@@ -150,8 +150,8 @@ namespace TriangleNet.Tools
{ {
this.tree = tree; this.tree = tree;
this.bounds = new BoundingBox(box.Xmin, box.Ymin, box.Xmax, box.Ymax); this.bounds = new BoundingBox(box.MinX, box.MinY, box.MaxX, box.MaxY);
this.pivot = new Point((box.Xmin + box.Xmax) / 2, (box.Ymin + box.Ymax) / 2); this.pivot = new Point((box.MinX + box.MaxX) / 2, (box.MinY + box.MaxY) / 2);
this.bitRegions = 0; this.bitRegions = 0;
@@ -193,19 +193,19 @@ namespace TriangleNet.Tools
BoundingBox box; BoundingBox box;
// 1. region south west // 1. region south west
box = new BoundingBox(bounds.Xmin, bounds.Ymin, pivot.X, pivot.Y); box = new BoundingBox(bounds.MinX, bounds.MinY, pivot.X, pivot.Y);
regions[0] = new QuadNode(box, tree); regions[0] = new QuadNode(box, tree);
// 2. region south east // 2. region south east
box = new BoundingBox(pivot.X, bounds.Ymin, bounds.Xmax, pivot.Y); box = new BoundingBox(pivot.X, bounds.MinY, bounds.MaxX, pivot.Y);
regions[1] = new QuadNode(box, tree); regions[1] = new QuadNode(box, tree);
// 3. region north west // 3. region north west
box = new BoundingBox(bounds.Xmin, pivot.Y, pivot.X, bounds.Ymax); box = new BoundingBox(bounds.MinX, pivot.Y, pivot.X, bounds.MaxY);
regions[2] = new QuadNode(box, tree); regions[2] = new QuadNode(box, tree);
// 4. region north east // 4. region north east
box = new BoundingBox(pivot.X, pivot.Y, bounds.Xmax, bounds.Ymax); box = new BoundingBox(pivot.X, pivot.Y, bounds.MaxX, bounds.MaxY);
regions[3] = new QuadNode(box, tree); regions[3] = new QuadNode(box, tree);
Point[] triangle = new Point[3]; Point[] triangle = new Point[3];
@@ -295,12 +295,12 @@ namespace TriangleNet.Tools
// we have an intersection // we have an intersection
double yComponent = triangle[k].Y + t * dy; double yComponent = triangle[k].Y + t * dy;
if (yComponent < pivot.Y && yComponent >= bounds.Ymin) if (yComponent < pivot.Y && yComponent >= bounds.MinY)
{ {
AddToRegion(index, SW); AddToRegion(index, SW);
AddToRegion(index, SE); AddToRegion(index, SE);
} }
else if (yComponent <= bounds.Ymax) else if (yComponent <= bounds.MaxY)
{ {
AddToRegion(index, NW); AddToRegion(index, NW);
AddToRegion(index, NE); AddToRegion(index, NE);
@@ -308,34 +308,34 @@ namespace TriangleNet.Tools
} }
// find intersection with plane x = m_boundingBox[0].dX // find intersection with plane x = m_boundingBox[0].dX
t = (bounds.Xmin - triangle[k].X) / dx; t = (bounds.MinX - triangle[k].X) / dx;
if (t < (1 + EPS) && t > -EPS) if (t < (1 + EPS) && t > -EPS)
{ {
// we have an intersection // we have an intersection
double yComponent = triangle[k].Y + t * dy; double yComponent = triangle[k].Y + t * dy;
if (yComponent < pivot.Y && yComponent >= bounds.Ymin) if (yComponent < pivot.Y && yComponent >= bounds.MinY)
{ {
AddToRegion(index, SW); AddToRegion(index, SW);
} }
else if (yComponent <= bounds.Ymax) // TODO: check && yComponent >= pivot.Y else if (yComponent <= bounds.MaxY) // TODO: check && yComponent >= pivot.Y
{ {
AddToRegion(index, NW); AddToRegion(index, NW);
} }
} }
// find intersection with plane x = m_boundingBox[1].dX // find intersection with plane x = m_boundingBox[1].dX
t = (bounds.Xmax - triangle[k].X) / dx; t = (bounds.MaxX - triangle[k].X) / dx;
if (t < (1 + EPS) && t > -EPS) if (t < (1 + EPS) && t > -EPS)
{ {
// we have an intersection // we have an intersection
double yComponent = triangle[k].Y + t * dy; double yComponent = triangle[k].Y + t * dy;
if (yComponent < pivot.Y && yComponent >= bounds.Ymin) if (yComponent < pivot.Y && yComponent >= bounds.MinY)
{ {
AddToRegion(index, SE); AddToRegion(index, SE);
} }
else if (yComponent <= bounds.Ymax) else if (yComponent <= bounds.MaxY)
{ {
AddToRegion(index, NE); AddToRegion(index, NE);
} }
@@ -353,12 +353,12 @@ namespace TriangleNet.Tools
// we have an intersection // we have an intersection
xComponent = triangle[k].X + t * dx; xComponent = triangle[k].X + t * dx;
if (xComponent > pivot.X && xComponent <= bounds.Xmax) if (xComponent > pivot.X && xComponent <= bounds.MaxX)
{ {
AddToRegion(index, SE); AddToRegion(index, SE);
AddToRegion(index, NE); AddToRegion(index, NE);
} }
else if (xComponent >= bounds.Xmin) else if (xComponent >= bounds.MinX)
{ {
AddToRegion(index, SW); AddToRegion(index, SW);
AddToRegion(index, NW); AddToRegion(index, NW);
@@ -366,34 +366,34 @@ namespace TriangleNet.Tools
} }
// find intersection with plane y = m_boundingBox[0].dY // find intersection with plane y = m_boundingBox[0].dY
t = (bounds.Ymin - triangle[k].Y) / dy; t = (bounds.MinY - triangle[k].Y) / dy;
if (t < (1 + EPS) && t > -EPS) if (t < (1 + EPS) && t > -EPS)
{ {
// we have an intersection // we have an intersection
xComponent = triangle[k].X + t * dx; xComponent = triangle[k].X + t * dx;
if (xComponent > pivot.X && xComponent <= bounds.Xmax) if (xComponent > pivot.X && xComponent <= bounds.MaxX)
{ {
AddToRegion(index, SE); AddToRegion(index, SE);
} }
else if (xComponent >= bounds.Xmin) else if (xComponent >= bounds.MinX)
{ {
AddToRegion(index, SW); AddToRegion(index, SW);
} }
} }
// find intersection with plane y = m_boundingBox[1].dY // find intersection with plane y = m_boundingBox[1].dY
t = (bounds.Ymax - triangle[k].Y) / dy; t = (bounds.MaxY - triangle[k].Y) / dy;
if (t < (1 + EPS) && t > -EPS) if (t < (1 + EPS) && t > -EPS)
{ {
// we have an intersection // we have an intersection
xComponent = triangle[k].X + t * dx; xComponent = triangle[k].X + t * dx;
if (xComponent > pivot.X && xComponent <= bounds.Xmax) if (xComponent > pivot.X && xComponent <= bounds.MaxX)
{ {
AddToRegion(index, NE); AddToRegion(index, NE);
} }
else if (xComponent >= bounds.Xmin) else if (xComponent >= bounds.MinX)
{ {
AddToRegion(index, NW); AddToRegion(index, NW);
} }
+43 -40
View File
@@ -20,9 +20,9 @@ namespace TriangleNet.Tools
Mesh mesh; Mesh mesh;
Point[] points; Point[] points;
List<VoronoiRegion> regions; Dictionary<int, VoronoiRegion> regions;
// Stores the endpoints of rays of infinite Voronoi cells // Stores the endpoints of rays of unbounded Voronoi cells
Dictionary<int, Point> rayPoints; Dictionary<int, Point> rayPoints;
int rayIndex; int rayIndex;
@@ -54,9 +54,9 @@ namespace TriangleNet.Tools
/// <summary> /// <summary>
/// Gets the list of Voronoi regions. /// Gets the list of Voronoi regions.
/// </summary> /// </summary>
public List<VoronoiRegion> Regions public ICollection<VoronoiRegion> Regions
{ {
get { return regions; } get { return regions.Values; }
} }
/// <summary> /// <summary>
@@ -77,7 +77,7 @@ namespace TriangleNet.Tools
// Allocate space for voronoi diagram // Allocate space for voronoi diagram
this.points = new Point[mesh.triangles.Count + mesh.hullsize]; this.points = new Point[mesh.triangles.Count + mesh.hullsize];
this.regions = new List<VoronoiRegion>(mesh.vertices.Count); this.regions = new Dictionary<int, VoronoiRegion>(mesh.vertices.Count);
rayPoints = new Dictionary<int, Point>(); rayPoints = new Dictionary<int, Point>();
rayIndex = 0; rayIndex = 0;
@@ -87,12 +87,18 @@ namespace TriangleNet.Tools
// Compute triangles circumcenters and setup bounding box // Compute triangles circumcenters and setup bounding box
ComputeCircumCenters(); ComputeCircumCenters();
// Add all Voronoi regions to the map.
foreach (var vertex in mesh.vertices.Values)
{
regions.Add(vertex.id, new VoronoiRegion(vertex));
}
// Loop over the mesh vertices (Voronoi generators). // Loop over the mesh vertices (Voronoi generators).
foreach (var item in mesh.vertices.Values) foreach (var region in regions.Values)
{ {
//if (item.Boundary == 0) //if (item.Boundary == 0)
{ {
ConstructVoronoiRegion(item); ConstructVoronoiRegion(region);
} }
} }
} }
@@ -113,11 +119,11 @@ namespace TriangleNet.Tools
points[item.id] = pt; points[item.id] = pt;
bounds.Update(pt.x, pt.y); bounds.Expand(pt.x, pt.y);
} }
double ds = Math.Max(bounds.Width, bounds.Height); double ds = Math.Max(bounds.Width, bounds.Height);
bounds.Scale(ds, ds); bounds.Resize(ds, ds);
} }
/// <summary> /// <summary>
@@ -125,12 +131,11 @@ namespace TriangleNet.Tools
/// </summary> /// </summary>
/// <param name="vertex"></param> /// <param name="vertex"></param>
/// <returns>The circumcenter indices which make up the cell.</returns> /// <returns>The circumcenter indices which make up the cell.</returns>
private void ConstructVoronoiRegion(Vertex vertex) private void ConstructVoronoiRegion(VoronoiRegion region)
{ {
VoronoiRegion region = new VoronoiRegion(vertex); var vertex = region.Generator as Vertex;
regions.Add(region);
List<Point> vpoints = new List<Point>(); var vpoints = new List<Point>();
Otri f = default(Otri); Otri f = default(Otri);
Otri f_init = default(Otri); Otri f_init = default(Otri);
@@ -165,6 +170,8 @@ namespace TriangleNet.Tools
// Add circumcenter of current triangle // Add circumcenter of current triangle
vpoints.Add(points[f.triangle.id]); vpoints.Add(points[f.triangle.id]);
region.AddNeighbor(f.triangle.id, regions[f.Apex().id]);
if (f_next.Equal(f_init)) if (f_next.Equal(f_init))
{ {
// Voronoi cell is complete (bounded case). // Voronoi cell is complete (bounded case).
@@ -179,23 +186,21 @@ namespace TriangleNet.Tools
// Voronoi cell is unbounded // Voronoi cell is unbounded
region.Bounded = false; region.Bounded = false;
Vertex torg, tdest, tapex, intersection; Vertex torg, tdest, tapex;
Point intersection;
int sid, n = mesh.triangles.Count; int sid, n = mesh.triangles.Count;
// Find the boundary segment id. // Find the boundary segment id (we use this id to number the endpoints of infinit rays).
f.Lprev(ref f_next); f.Lprev(ref f_next);
f_next.SegPivot(ref sub); f_next.SegPivot(ref sub);
sid = sub.seg.hash; sid = sub.seg.hash;
// Last valid f lies at the boundary. Add the circumcenter. // Last valid f lies at the boundary. Add the circumcenter.
vpoints.Add(points[f.triangle.id]); vpoints.Add(points[f.triangle.id]);
region.AddNeighbor(f.triangle.id, regions[f.Apex().id]);
// Check if the intersection with the bounding box has already been computed. // Check if the intersection with the bounding box has already been computed.
if (rayPoints.ContainsKey(sid)) if (!rayPoints.TryGetValue(sid, out intersection))
{
vpoints.Add(rayPoints[sid]);
}
else
{ {
torg = f.Org(); torg = f.Org();
tapex = f.Apex(); tapex = f.Apex();
@@ -205,13 +210,13 @@ namespace TriangleNet.Tools
intersection.id = n + rayIndex; intersection.id = n + rayIndex;
points[n + rayIndex] = intersection; points[n + rayIndex] = intersection;
rayIndex++; rayIndex++;
vpoints.Add(intersection);
rayPoints.Add(sid, intersection); rayPoints.Add(sid, intersection);
} }
vpoints.Add(intersection);
// Now walk from f_init clockwise till we reach the boundary. // Now walk from f_init clockwise till we reach the boundary.
vpoints.Reverse(); vpoints.Reverse();
@@ -221,6 +226,7 @@ namespace TriangleNet.Tools
while (f_prev.triangle != Mesh.dummytri) while (f_prev.triangle != Mesh.dummytri)
{ {
vpoints.Add(points[f_prev.triangle.id]); vpoints.Add(points[f_prev.triangle.id]);
region.AddNeighbor(f_prev.triangle.id, regions[f_prev.Apex().id]);
f_prev.Copy(ref f); f_prev.Copy(ref f);
f_prev.OprevSelf(); f_prev.OprevSelf();
@@ -230,11 +236,7 @@ namespace TriangleNet.Tools
f.SegPivot(ref sub); f.SegPivot(ref sub);
sid = sub.seg.hash; sid = sub.seg.hash;
if (rayPoints.ContainsKey(sid)) if (!rayPoints.TryGetValue(sid, out intersection))
{
vpoints.Add(rayPoints[sid]);
}
else
{ {
// Intersection has not been computed yet. // Intersection has not been computed yet.
torg = f.Org(); torg = f.Org();
@@ -245,20 +247,21 @@ namespace TriangleNet.Tools
// Set the correct id for the vertex // Set the correct id for the vertex
intersection.id = n + rayIndex; intersection.id = n + rayIndex;
points[n + rayIndex] = intersection;
rayIndex++;
vpoints.Add(intersection);
rayPoints.Add(sid, intersection); rayPoints.Add(sid, intersection);
points[n + rayIndex] = intersection;
rayIndex++;
} }
vpoints.Add(intersection);
region.AddNeighbor(intersection.id, regions[f.Dest().id]);
// Add the new points to the region (in counter-clockwise order) // Add the new points to the region (in counter-clockwise order)
vpoints.Reverse(); vpoints.Reverse();
region.Add(vpoints); region.Add(vpoints);
} }
private bool BoxRayIntersection(Point pt, double dx, double dy, out Vertex intersect) private bool BoxRayIntersection(Point pt, double dx, double dy, out Point intersect)
{ {
double x = pt.X; double x = pt.X;
double y = pt.Y; double y = pt.Y;
@@ -266,10 +269,10 @@ namespace TriangleNet.Tools
double t1, x1, y1, t2, x2, y2; double t1, x1, y1, t2, x2, y2;
// Bounding box // Bounding box
double minX = bounds.Xmin; double minX = bounds.MinX;
double maxX = bounds.Xmax; double maxX = bounds.MaxX;
double minY = bounds.Ymin; double minY = bounds.MinY;
double maxY = bounds.Ymax; double maxY = bounds.MaxY;
// Check if point is inside the bounds // Check if point is inside the bounds
if (x < minX || x > maxX || y < minY || y > maxY) if (x < minX || x > maxX || y < minY || y > maxY)
@@ -308,7 +311,7 @@ namespace TriangleNet.Tools
x2 = x + t2 * dx; x2 = x + t2 * dx;
y2 = minY; y2 = minY;
} }
else if (dx > 0) else if (dy > 0)
{ {
// Line going upwards: intersect with y = maxY // Line going upwards: intersect with y = maxY
t2 = (maxY - y) / dy; t2 = (maxY - y) / dy;
@@ -324,11 +327,11 @@ namespace TriangleNet.Tools
if (t1 < t2) if (t1 < t2)
{ {
intersect = new Vertex(x1, y1, -1); intersect = new Point(x1, y1);
} }
else else
{ {
intersect = new Vertex(x2, y2, -1); intersect = new Point(x2, y2);
} }
return true; return true;
+33 -4
View File
@@ -1,6 +1,6 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// <copyright file="VoronoiRegion.cs" company=""> // <copyright file="VoronoiRegion.cs" company="">
// TODO: Update copyright text. // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright> // </copyright>
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -8,10 +8,8 @@ namespace TriangleNet.Tools
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
using TriangleNet.Data; using TriangleNet.Data;
using TriangleNet.Geometry;
/// <summary> /// <summary>
/// Represents a region in the Voronoi diagram. /// Represents a region in the Voronoi diagram.
@@ -23,6 +21,9 @@ namespace TriangleNet.Tools
List<Point> vertices; List<Point> vertices;
bool bounded; bool bounded;
// A map (vertex id) -> (neighbor across adjacent edge)
Dictionary<int, VoronoiRegion> neighbors;
/// <summary> /// <summary>
/// Gets the Voronoi region id (which is the same as the generators vertex id). /// Gets the Voronoi region id (which is the same as the generators vertex id).
/// </summary> /// </summary>
@@ -62,6 +63,8 @@ namespace TriangleNet.Tools
this.generator = generator; this.generator = generator;
this.vertices = new List<Point>(); this.vertices = new List<Point>();
this.bounded = true; this.bounded = true;
this.neighbors = new Dictionary<int, VoronoiRegion>();
} }
public void Add(Point point) public void Add(Point point)
@@ -74,6 +77,32 @@ namespace TriangleNet.Tools
this.vertices.AddRange(points); this.vertices.AddRange(points);
} }
/// <summary>
/// Returns the neighbouring Voronoi region, that lies across the edge starting at
/// given vertex.
/// </summary>
/// <param name="p">Vertex defining an edge of the region.</param>
/// <returns>Neighbouring Voronoi region</returns>
/// <remarks>
/// The edge starting at p is well defined (vertices are ordered counterclockwise).
/// </remarks>
public VoronoiRegion GetNeighbor(Point p)
{
VoronoiRegion neighbor;
if (neighbors.TryGetValue(p.id, out neighbor))
{
return neighbor;
}
return null;
}
internal void AddNeighbor(int id, VoronoiRegion neighbor)
{
this.neighbors.Add(id, neighbor);
}
public override string ToString() public override string ToString()
{ {
return String.Format("R-ID {0}", id); return String.Format("R-ID {0}", id);