Added 'TriangleNet.Voronoi' namespace,

Added new Voronoi code (edge based construction), 
Moved old Voronoi code to 'Legacy' namespace

git-svn-id: https://triangle.svn.codeplex.com/svn@75112 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5
This commit is contained in:
SND\wo80_cp
2014-06-26 12:41:32 +00:00
parent e2fcbf3675
commit 0b4853faf8
18 changed files with 571 additions and 22 deletions
+2 -1
View File
@@ -12,6 +12,7 @@ using TriangleNet.Meshing.Algorithm;
using TriangleNet.Tools;
using TriangleNet.Smoothing;
using TriangleNet.Rendering;
using TriangleNet.Voronoi.Legacy;
namespace MeshExplorer
{
@@ -611,7 +612,7 @@ namespace MeshExplorer
if (mesh.IsPolygon)
{
this.voronoi = new BoundedVoronoi(mesh);
this.voronoi = new BoundedVoronoiLegacy(mesh);
}
else
{
@@ -4,7 +4,7 @@ namespace TriangleNet.Rendering
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Tools;
using TriangleNet.Voronoi.Legacy;
public interface IRenderContext
{
@@ -6,7 +6,7 @@ namespace TriangleNet.Rendering
using TriangleNet.Meshing;
using TriangleNet.Rendering.Buffer;
using TriangleNet.Rendering.Util;
using TriangleNet.Tools;
using TriangleNet.Voronoi.Legacy;
public interface IRenderLayer
{
@@ -5,7 +5,7 @@ namespace TriangleNet.Rendering
using System.Linq;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Tools;
using TriangleNet.Voronoi.Legacy;
/// <summary>
/// The RenderContext class brings all the rendering parts together.
@@ -6,7 +6,7 @@ namespace TriangleNet.Rendering
using TriangleNet.Meshing;
using TriangleNet.Rendering.Buffer;
using TriangleNet.Rendering.Util;
using TriangleNet.Tools;
using TriangleNet.Voronoi.Legacy;
public class RenderLayer : IRenderLayer
{
@@ -1,13 +1,13 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using System.Windows.Forms;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.GDI;
using TriangleNet.Tools;
using System.Collections.Generic;
using TriangleNet.Rendering.Util;
using TriangleNet.Voronoi.Legacy;
public class RenderManager
{
@@ -8,7 +8,7 @@ namespace TriangleNet.Smoothing
{
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Tools;
using TriangleNet.Voronoi.Legacy;
/// <summary>
/// Simple mesh smoother implementation.
@@ -54,7 +54,7 @@ namespace TriangleNet.Smoothing
/// </summary>
private void Step(Mesh mesh)
{
BoundedVoronoi voronoi = new BoundedVoronoi(mesh, false);
BoundedVoronoiLegacy voronoi = new BoundedVoronoiLegacy(mesh, false);
var cells = voronoi.Regions;
+10 -4
View File
@@ -96,17 +96,23 @@
<Compile Include="Smoothing\ISmoother.cs" />
<Compile Include="Smoothing\SimpleSmoother.cs" />
<Compile Include="Tools\AdjacencyMatrix.cs" />
<Compile Include="Tools\BoundedVoronoi.cs" />
<Compile Include="Voronoi\Legacy\BoundedVoronoiLegacy.cs" />
<Compile Include="Tools\CuthillMcKee.cs" />
<Compile Include="Tools\IVoronoi.cs" />
<Compile Include="Voronoi\Legacy\IVoronoi.cs" />
<Compile Include="Tools\QuadTree.cs" />
<Compile Include="Tools\QualityMeasure.cs" />
<Compile Include="Meshing\Iterators\RegionIterator.cs" />
<Compile Include="Tools\Statistic.cs" />
<Compile Include="Meshing\Algorithm\SweepLine.cs" />
<Compile Include="Tools\Voronoi.cs" />
<Compile Include="Tools\VoronoiRegion.cs" />
<Compile Include="Voronoi\Legacy\Voronoi.cs" />
<Compile Include="Voronoi\Legacy\VoronoiRegion.cs" />
<Compile Include="TriangleLocator.cs" />
<Compile Include="Voronoi\BoundedVoronoi.cs" />
<Compile Include="Voronoi\DCEL\Face.cs" />
<Compile Include="Voronoi\DCEL\HalfEdge.cs" />
<Compile Include="Voronoi\DCEL\Vertex.cs" />
<Compile Include="Voronoi\StandardVoronoi.cs" />
<Compile Include="Voronoi\VoronoiBase.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -0,0 +1,24 @@
// -----------------------------------------------------------------------
// <copyright file="BoundedVoronoi.cs">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Voronoi
{
public class BoundedVoronoi : VoronoiBase
{
public BoundedVoronoi(Mesh mesh)
: 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();
}
private void PostProcess()
{
// Compute edge intersections with mesh boundary edges.
}
}
}
@@ -0,0 +1,65 @@
// -----------------------------------------------------------------------
// <copyright file="Face.cs">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Voronoi.DCEL
{
using TriangleNet.Geometry;
/// <summary>
/// A face of DCEL mesh.
/// </summary>
public class Face
{
internal Point generator;
internal HalfEdge edge;
internal bool bounded;
/// <summary>
/// Gets or sets a half-edge connected to the face.
/// </summary>
public HalfEdge Edge
{
get { return edge; }
set { edge = value; }
}
/// <summary>
/// Gets or sets a value, indicating if the face is bounded (for Voronoi diagram).
/// </summary>
public bool Bounded
{
get { return bounded; }
set { bounded = value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="Face" /> class.
/// </summary>
/// <param name="generator">The generator of this face (for Voronoi diagram)</param>
public Face(Point generator)
: this(generator, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Face" /> class.
/// </summary>
/// <param name="generator">The generator of this face (for Voronoi diagram)</param>
/// <param name="edge">The half-edge connected to this face.</param>
public Face(Point generator, HalfEdge edge)
{
this.generator = generator;
this.edge = edge;
this.bounded = true;
}
public override string ToString()
{
return string.Format("F-ID {0}", generator.id);
}
}
}
@@ -0,0 +1,85 @@
// -----------------------------------------------------------------------
// <copyright file="HalfEdge.cs">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Voronoi.DCEL
{
public class HalfEdge
{
internal int id;
internal Vertex origin;
internal Face face;
internal HalfEdge twin;
internal HalfEdge next;
/// <summary>
/// Gets or sets the origin of the half-edge.
/// </summary>
public Vertex Origin
{
get { return origin; }
set { origin = value; }
}
/// <summary>
/// Gets or sets the face connected to the half-edge.
/// </summary>
public Face Face
{
get { return face; }
set { face = value; }
}
/// <summary>
/// Gets or sets the twin of the half-edge.
/// </summary>
public HalfEdge Twin
{
get { return twin; }
set { twin = value; }
}
/// <summary>
/// Gets or sets the next pointer of the half-edge.
/// </summary>
public HalfEdge Next
{
get { return next; }
set { next = value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="HalfEdge" /> class.
/// </summary>
/// <param name="origin">The origin of this half-edge.</param>
public HalfEdge(Vertex origin)
{
this.origin = origin;
}
/// <summary>
/// Initializes a new instance of the <see cref="HalfEdge" /> class.
/// </summary>
/// <param name="origin">The origin of this half-edge.</param>
/// <param name="face">The face connected to this half-edge.</param>
public HalfEdge(Vertex origin, Face face)
{
this.origin = origin;
this.face = face;
// IMPORTANT: do not remove the (this.face == null) check!
if (face != null && this.face.edge == null)
{
face.edge = this;
}
}
public override string ToString()
{
return string.Format("HE-ID {0} (Origin = VID-{1})", id, origin.id);
}
}
}
@@ -0,0 +1,46 @@
// -----------------------------------------------------------------------
// <copyright file="Vertex.cs">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Voronoi.DCEL
{
using TriangleNet.Geometry;
public class Vertex : Point
{
internal HalfEdge leaving;
/// <summary>
/// Gets or sets a half-edge leaving the vertex.
/// </summary>
public HalfEdge Leaving
{
get { return leaving; }
set { leaving = value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="Vertex" /> class.
/// </summary>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
public Vertex(double x, double y)
: base(x, y)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Vertex" /> class.
/// </summary>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
/// <param name="leaving">A half-edge leaving this vertex.</param>
public Vertex(double x, double y, HalfEdge leaving)
: base(x, y)
{
this.leaving = leaving;
}
}
}
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Tools
namespace TriangleNet.Voronoi.Legacy
{
using System;
using System.Collections.Generic;
@@ -18,7 +18,7 @@ namespace TriangleNet.Tools
/// 2D Centroidal Voronoi Tessellations with Constraints, 2010,
/// Jane Tournois, Pierre Alliez and Olivier Devillers
/// </remarks>
public class BoundedVoronoi : IVoronoi
public class BoundedVoronoiLegacy : IVoronoi
{
Mesh mesh;
@@ -34,19 +34,19 @@ namespace TriangleNet.Tools
bool includeBoundary = true;
/// <summary>
/// Initializes a new instance of the <see cref="BoundedVoronoi" /> class.
/// Initializes a new instance of the <see cref="BoundedVoronoiLegacy" /> class.
/// </summary>
/// <param name="mesh">Mesh instance.</param>
public BoundedVoronoi(Mesh mesh)
public BoundedVoronoiLegacy(Mesh mesh)
: this(mesh, true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BoundedVoronoi" /> class.
/// Initializes a new instance of the <see cref="BoundedVoronoiLegacy" /> class.
/// </summary>
/// <param name="mesh">Mesh instance.</param>
public BoundedVoronoi(Mesh mesh, bool includeBoundary)
public BoundedVoronoiLegacy(Mesh mesh, bool includeBoundary)
{
this.mesh = mesh;
this.includeBoundary = includeBoundary;
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Tools
namespace TriangleNet.Voronoi.Legacy
{
using System.Collections.Generic;
using TriangleNet.Geometry;
@@ -5,7 +5,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Tools
namespace TriangleNet.Voronoi.Legacy
{
using System;
using System.Collections.Generic;
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Tools
namespace TriangleNet.Voronoi.Legacy
{
using System;
using System.Collections.Generic;
@@ -0,0 +1,31 @@
// -----------------------------------------------------------------------
// <copyright file="StandardVoronoi.cs">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Voronoi
{
using TriangleNet.Geometry;
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)
{
// Compute edge intersections with bounding box.
}
}
}
@@ -0,0 +1,291 @@
// -----------------------------------------------------------------------
// <copyright file="VoronoiBase.cs">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Voronoi
{
using System.Collections.Generic;
using TriangleNet.Data;
using TriangleNet.Geometry;
using TriangleNet.Voronoi.DCEL;
using Vertex = TriangleNet.Voronoi.DCEL.Vertex;
/// <summary>
/// The Voronoi diagram is the dual of a pointset triangulation.
/// </summary>
public abstract class VoronoiBase
{
protected Vertex[] vertices;
protected List<HalfEdge> edges;
protected Face[] faces;
/// <summary>
/// Gets the vertices of the Voronoi diagram.
/// </summary>
public Vertex[] Vertices
{
get { return vertices; }
}
/// <summary>
/// Gets the list of half-edges specify the Voronoi diagram topology.
/// </summary>
public List<HalfEdge> HalfEdges
{
get { return edges; }
}
/// <summary>
/// Gets the faces of the Voronoi diagram.
/// </summary>
public Face[] Faces
{
get { return faces; }
}
/// <summary>
/// Gets the collection of edges of the Voronoi diagram.
/// </summary>
public IEnumerable<IEdge> Edges
{
get { return EnumerateEdges(); }
}
/// <summary>
/// Initializes a new instance of the <see cref="VoronoiBase" /> class.
/// </summary>
/// <param name="mesh">Triangle mesh.</param>
/// <param name="generate">If set to true, the constuctor will call the Generate
/// method, which builds the Voronoi diagram.</param>
protected VoronoiBase(Mesh mesh, bool generate)
{
if (generate)
{
Generate(mesh);
}
}
/// <summary>
/// Generate the Voronoi diagram from given triangle mesh..
/// </summary>
/// <param name="mesh"></param>
protected void Generate(Mesh mesh)
{
mesh.Renumber();
mesh.MakeVertexMap();
// Allocate space for voronoi diagram
this.vertices = new Vertex[mesh.triangles.Count + mesh.hullsize];
this.faces = new Face[mesh.vertices.Count];
this.edges = new List<HalfEdge>();
// Compute triangles circumcenters and setup bounding box
var map = ComputeVertices(mesh);
// Create all Voronoi faces.
foreach (var vertex in mesh.vertices.Values)
{
faces[vertex.id] = new Face(vertex);
}
ComputeEdges(mesh, map);
// At this point all edges are computed, but the (edge.next) pointers aren't set.
ConnectEdges(map);
}
/// <summary>
/// Compute the Voronoi vertices (the circumcenters of the triangles).
/// </summary>
/// <returns>An empty map, which will map all vertices to a list of leaving edges.</returns>
protected List<HalfEdge>[] ComputeVertices(Mesh mesh)
{
Otri tri = default(Otri);
double xi = 0, eta = 0;
Vertex vertex;
Point pt;
int id;
// Maps all vertices to a list of leaving edges.
var map = new List<HalfEdge>[mesh.triangles.Count];
// Compue triangle circumcenters
foreach (var t in mesh.triangles.Values)
{
id = t.id;
tri.triangle = t;
pt = RobustPredicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);
vertex = new Vertex(pt.x, pt.y);
vertex.id = id;
vertices[id] = vertex;
map[id] = new List<HalfEdge>();
}
return map;
}
/// <summary>
/// Compute the edges of the Voronoi diagram.
/// </summary>
/// <param name="map">Empty vertex map.</param>
protected void ComputeEdges(Mesh mesh, List<HalfEdge>[] map)
{
Otri tri, neighbor = default(Otri);
TriangleNet.Geometry.Vertex org, dest;
double px, py;
int id, nid, count = mesh.triangles.Count;
Face face, neighborFace;
HalfEdge edge, twin;
Vertex vertex, end;
// Count infinte edges (vertex id for their endpoints).
int j = 0;
// Count half-edges (edge ids).
int k = 0;
// To loop over the set of edges, loop over all triangles, and look at the
// three edges of each triangle. If there isn't another triangle adjacent
// to the edge, operate on the edge. If there is another adjacent triangle,
// operate on the edge only if the current triangle has a smaller id than
// its neighbor. This way, each edge is considered only once.
foreach (var t in mesh.triangles.Values)
{
id = t.id;
tri.triangle = t;
for (int i = 0; i < 3; i++)
{
tri.orient = i;
tri.Sym(ref neighbor);
nid = neighbor.triangle.id;
if (id < nid || nid < 0)
{
// Get the endpoints of the current triangle edge.
org = tri.Org();
dest = tri.Dest();
face = faces[org.id];
neighborFace = faces[dest.id];
vertex = vertices[id];
// For each edge in the triangle mesh, there's a corresponding edge
// in the Voronoi diagram, i.e. two half-edges will be created.
if (nid < 0)
{
// Undounded edge, direction perpendicular to the boundary edge,
// pointing outwards.
px = dest.y - org.y;
py = org.x - dest.x;
end = new Vertex(vertex.x + px, vertex.y + py);
end.id = count + j++;
vertices[end.id] = end;
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);
}
else
{
// Create half-edges.
edge = new HalfEdge(vertices[nid], face);
twin = new HalfEdge(vertex, neighborFace);
// Set leaving edges.
vertices[nid].leaving = edge;
vertex.leaving = twin;
// Add to vertex map.
map[nid].Add(edge);
map[id].Add(twin);
}
// Setup twin edges.
edge.twin = twin;
twin.twin = edge;
edge.id = k++;
twin.id = k++;
this.edges.Add(edge);
this.edges.Add(twin);
}
}
}
}
/// <summary>
/// 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)
{
int length = map.Length;
// For each halfe-edge, find its successor in the connected face.
foreach (var edge in this.edges)
{
var face = edge.face.generator.id;
// The id of the dest vertex of current edge.
int id = edge.twin.origin.id;
if (id < length)
{
// Look for the edge that is connected to the current face. Each
// Voronoi vertex has degree 3, so this loop is actually O(1).
foreach (var next in map[id])
{
if (next.face.generator.id == face)
{
edge.next = next;
break;
}
}
}
}
}
protected 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 (edge.id < twin.id)
{
edges.Add(new Edge(edge.origin.id, twin.origin.id));
}
}
return edges;
}
}
}