diff --git a/Triangle.NET/TestApp/FormMain.cs b/Triangle.NET/TestApp/FormMain.cs index d27ac27..557f6bd 100644 --- a/Triangle.NET/TestApp/FormMain.cs +++ b/Triangle.NET/TestApp/FormMain.cs @@ -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 { diff --git a/Triangle.NET/Triangle.Rendering/IRenderContext.cs b/Triangle.NET/Triangle.Rendering/IRenderContext.cs index f65f2f1..1103763 100644 --- a/Triangle.NET/Triangle.Rendering/IRenderContext.cs +++ b/Triangle.NET/Triangle.Rendering/IRenderContext.cs @@ -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 { diff --git a/Triangle.NET/Triangle.Rendering/IRenderLayer.cs b/Triangle.NET/Triangle.Rendering/IRenderLayer.cs index 6437a63..4371f8b 100644 --- a/Triangle.NET/Triangle.Rendering/IRenderLayer.cs +++ b/Triangle.NET/Triangle.Rendering/IRenderLayer.cs @@ -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 { diff --git a/Triangle.NET/Triangle.Rendering/RenderContext.cs b/Triangle.NET/Triangle.Rendering/RenderContext.cs index 73fb686..ffd4dc9 100644 --- a/Triangle.NET/Triangle.Rendering/RenderContext.cs +++ b/Triangle.NET/Triangle.Rendering/RenderContext.cs @@ -5,7 +5,7 @@ namespace TriangleNet.Rendering using System.Linq; using TriangleNet.Geometry; using TriangleNet.Meshing; - using TriangleNet.Tools; + using TriangleNet.Voronoi.Legacy; /// /// The RenderContext class brings all the rendering parts together. diff --git a/Triangle.NET/Triangle.Rendering/RenderLayer.cs b/Triangle.NET/Triangle.Rendering/RenderLayer.cs index f85999f..9ebe91f 100644 --- a/Triangle.NET/Triangle.Rendering/RenderLayer.cs +++ b/Triangle.NET/Triangle.Rendering/RenderLayer.cs @@ -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 { diff --git a/Triangle.NET/Triangle.Rendering/RenderManager.cs b/Triangle.NET/Triangle.Rendering/RenderManager.cs index 6356632..c0c9044 100644 --- a/Triangle.NET/Triangle.Rendering/RenderManager.cs +++ b/Triangle.NET/Triangle.Rendering/RenderManager.cs @@ -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 { diff --git a/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs b/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs index ca13b74..09600b0 100644 --- a/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs +++ b/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs @@ -8,7 +8,7 @@ namespace TriangleNet.Smoothing { using TriangleNet.Geometry; using TriangleNet.Meshing; - using TriangleNet.Tools; + using TriangleNet.Voronoi.Legacy; /// /// Simple mesh smoother implementation. @@ -54,7 +54,7 @@ namespace TriangleNet.Smoothing /// private void Step(Mesh mesh) { - BoundedVoronoi voronoi = new BoundedVoronoi(mesh, false); + BoundedVoronoiLegacy voronoi = new BoundedVoronoiLegacy(mesh, false); var cells = voronoi.Regions; diff --git a/Triangle.NET/Triangle/Triangle.csproj b/Triangle.NET/Triangle/Triangle.csproj index ed444a4..7be2058 100644 --- a/Triangle.NET/Triangle/Triangle.csproj +++ b/Triangle.NET/Triangle/Triangle.csproj @@ -96,17 +96,23 @@ - + - + - - + + + + + + + + diff --git a/Triangle.NET/Triangle/Voronoi/BoundedVoronoi.cs b/Triangle.NET/Triangle/Voronoi/BoundedVoronoi.cs new file mode 100644 index 0000000..5af7591 --- /dev/null +++ b/Triangle.NET/Triangle/Voronoi/BoundedVoronoi.cs @@ -0,0 +1,24 @@ +// ----------------------------------------------------------------------- +// +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +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. + } + } +} diff --git a/Triangle.NET/Triangle/Voronoi/DCEL/Face.cs b/Triangle.NET/Triangle/Voronoi/DCEL/Face.cs new file mode 100644 index 0000000..1bb3e39 --- /dev/null +++ b/Triangle.NET/Triangle/Voronoi/DCEL/Face.cs @@ -0,0 +1,65 @@ +// ----------------------------------------------------------------------- +// +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.Voronoi.DCEL +{ + using TriangleNet.Geometry; + + /// + /// A face of DCEL mesh. + /// + public class Face + { + internal Point generator; + + internal HalfEdge edge; + internal bool bounded; + + /// + /// Gets or sets a half-edge connected to the face. + /// + public HalfEdge Edge + { + get { return edge; } + set { edge = value; } + } + + /// + /// Gets or sets a value, indicating if the face is bounded (for Voronoi diagram). + /// + public bool Bounded + { + get { return bounded; } + set { bounded = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The generator of this face (for Voronoi diagram) + public Face(Point generator) + : this(generator, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The generator of this face (for Voronoi diagram) + /// The half-edge connected to this face. + 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); + } + } +} diff --git a/Triangle.NET/Triangle/Voronoi/DCEL/HalfEdge.cs b/Triangle.NET/Triangle/Voronoi/DCEL/HalfEdge.cs new file mode 100644 index 0000000..8d1e8b0 --- /dev/null +++ b/Triangle.NET/Triangle/Voronoi/DCEL/HalfEdge.cs @@ -0,0 +1,85 @@ +// ----------------------------------------------------------------------- +// +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.Voronoi.DCEL +{ + public class HalfEdge + { + internal int id; + + internal Vertex origin; + internal Face face; + internal HalfEdge twin; + internal HalfEdge next; + + /// + /// Gets or sets the origin of the half-edge. + /// + public Vertex Origin + { + get { return origin; } + set { origin = value; } + } + + /// + /// Gets or sets the face connected to the half-edge. + /// + public Face Face + { + get { return face; } + set { face = value; } + } + + /// + /// Gets or sets the twin of the half-edge. + /// + public HalfEdge Twin + { + get { return twin; } + set { twin = value; } + } + + /// + /// Gets or sets the next pointer of the half-edge. + /// + public HalfEdge Next + { + get { return next; } + set { next = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The origin of this half-edge. + public HalfEdge(Vertex origin) + { + this.origin = origin; + } + + /// + /// Initializes a new instance of the class. + /// + /// The origin of this half-edge. + /// The face connected to this half-edge. + 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); + } + } +} diff --git a/Triangle.NET/Triangle/Voronoi/DCEL/Vertex.cs b/Triangle.NET/Triangle/Voronoi/DCEL/Vertex.cs new file mode 100644 index 0000000..cb1cdf5 --- /dev/null +++ b/Triangle.NET/Triangle/Voronoi/DCEL/Vertex.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.Voronoi.DCEL +{ + using TriangleNet.Geometry; + + public class Vertex : Point + { + internal HalfEdge leaving; + + /// + /// Gets or sets a half-edge leaving the vertex. + /// + public HalfEdge Leaving + { + get { return leaving; } + set { leaving = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The x coordinate. + /// The y coordinate. + public Vertex(double x, double y) + : base(x, y) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The x coordinate. + /// The y coordinate. + /// A half-edge leaving this vertex. + public Vertex(double x, double y, HalfEdge leaving) + : base(x, y) + { + this.leaving = leaving; + } + } +} diff --git a/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs b/Triangle.NET/Triangle/Voronoi/Legacy/BoundedVoronoiLegacy.cs similarity index 96% rename from Triangle.NET/Triangle/Tools/BoundedVoronoi.cs rename to Triangle.NET/Triangle/Voronoi/Legacy/BoundedVoronoiLegacy.cs index 3d5eb76..2cdf1f9 100644 --- a/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs +++ b/Triangle.NET/Triangle/Voronoi/Legacy/BoundedVoronoiLegacy.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -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 /// - public class BoundedVoronoi : IVoronoi + public class BoundedVoronoiLegacy : IVoronoi { Mesh mesh; @@ -34,19 +34,19 @@ namespace TriangleNet.Tools bool includeBoundary = true; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Mesh instance. - public BoundedVoronoi(Mesh mesh) + public BoundedVoronoiLegacy(Mesh mesh) : this(mesh, true) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Mesh instance. - public BoundedVoronoi(Mesh mesh, bool includeBoundary) + public BoundedVoronoiLegacy(Mesh mesh, bool includeBoundary) { this.mesh = mesh; this.includeBoundary = includeBoundary; diff --git a/Triangle.NET/Triangle/Tools/IVoronoi.cs b/Triangle.NET/Triangle/Voronoi/Legacy/IVoronoi.cs similarity index 92% rename from Triangle.NET/Triangle/Tools/IVoronoi.cs rename to Triangle.NET/Triangle/Voronoi/Legacy/IVoronoi.cs index e50ca29..3dd9dc3 100644 --- a/Triangle.NET/Triangle/Tools/IVoronoi.cs +++ b/Triangle.NET/Triangle/Voronoi/Legacy/IVoronoi.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Tools +namespace TriangleNet.Voronoi.Legacy { using System.Collections.Generic; using TriangleNet.Geometry; diff --git a/Triangle.NET/Triangle/Tools/Voronoi.cs b/Triangle.NET/Triangle/Voronoi/Legacy/Voronoi.cs similarity index 96% rename from Triangle.NET/Triangle/Tools/Voronoi.cs rename to Triangle.NET/Triangle/Voronoi/Legacy/Voronoi.cs index 8ae83e2..155406a 100644 --- a/Triangle.NET/Triangle/Tools/Voronoi.cs +++ b/Triangle.NET/Triangle/Voronoi/Legacy/Voronoi.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Tools +namespace TriangleNet.Voronoi.Legacy { using System; using System.Collections.Generic; diff --git a/Triangle.NET/Triangle/Tools/VoronoiRegion.cs b/Triangle.NET/Triangle/Voronoi/Legacy/VoronoiRegion.cs similarity index 95% rename from Triangle.NET/Triangle/Tools/VoronoiRegion.cs rename to Triangle.NET/Triangle/Voronoi/Legacy/VoronoiRegion.cs index 433169a..d172d82 100644 --- a/Triangle.NET/Triangle/Tools/VoronoiRegion.cs +++ b/Triangle.NET/Triangle/Voronoi/Legacy/VoronoiRegion.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Tools +namespace TriangleNet.Voronoi.Legacy { using System; using System.Collections.Generic; diff --git a/Triangle.NET/Triangle/Voronoi/StandardVoronoi.cs b/Triangle.NET/Triangle/Voronoi/StandardVoronoi.cs new file mode 100644 index 0000000..b35832a --- /dev/null +++ b/Triangle.NET/Triangle/Voronoi/StandardVoronoi.cs @@ -0,0 +1,31 @@ +// ----------------------------------------------------------------------- +// +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +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. + } + } +} diff --git a/Triangle.NET/Triangle/Voronoi/VoronoiBase.cs b/Triangle.NET/Triangle/Voronoi/VoronoiBase.cs new file mode 100644 index 0000000..2d314b4 --- /dev/null +++ b/Triangle.NET/Triangle/Voronoi/VoronoiBase.cs @@ -0,0 +1,291 @@ +// ----------------------------------------------------------------------- +// +// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.Voronoi +{ + using System.Collections.Generic; + + using TriangleNet.Data; + using TriangleNet.Geometry; + using TriangleNet.Voronoi.DCEL; + + using Vertex = TriangleNet.Voronoi.DCEL.Vertex; + + /// + /// The Voronoi diagram is the dual of a pointset triangulation. + /// + public abstract class VoronoiBase + { + protected Vertex[] vertices; + protected List edges; + protected Face[] faces; + + /// + /// Gets the vertices of the Voronoi diagram. + /// + public Vertex[] Vertices + { + get { return vertices; } + } + + /// + /// Gets the list of half-edges specify the Voronoi diagram topology. + /// + public List HalfEdges + { + get { return edges; } + } + + /// + /// Gets the faces of the Voronoi diagram. + /// + public Face[] Faces + { + get { return faces; } + } + + /// + /// Gets the collection of edges of the Voronoi diagram. + /// + public IEnumerable Edges + { + get { return EnumerateEdges(); } + } + + /// + /// Initializes a new instance of the class. + /// + /// Triangle mesh. + /// If set to true, the constuctor will call the Generate + /// method, which builds the Voronoi diagram. + protected VoronoiBase(Mesh mesh, bool generate) + { + if (generate) + { + Generate(mesh); + } + } + + /// + /// Generate the Voronoi diagram from given triangle mesh.. + /// + /// + 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(); + + // 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); + } + + /// + /// Compute the Voronoi vertices (the circumcenters of the triangles). + /// + /// An empty map, which will map all vertices to a list of leaving edges. + protected List[] 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[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(); + } + + return map; + } + + /// + /// Compute the edges of the Voronoi diagram. + /// + /// Empty vertex map. + protected void ComputeEdges(Mesh mesh, List[] 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); + } + } + } + } + + /// + /// Connect all edges of the Voronoi diagram. + /// + /// Maps all vertices to a list of leaving edges. + protected void ConnectEdges(List[] 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 EnumerateEdges() + { + var edges = new List(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; + } + } +}