From 7384b5fd077144d14d8b4bee3aaab3dde3d852ce Mon Sep 17 00:00:00 2001 From: "SND\\wo80_cp" Date: Thu, 29 May 2014 18:14:13 +0000 Subject: [PATCH] More code reorganization (for beta 4) git-svn-id: https://triangle.svn.codeplex.com/svn@75021 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5 --- Triangle.NET/MeshRenderer.Core/RenderData.cs | 16 +- Triangle.NET/TestApp/IO/EpsImage.cs | 10 +- Triangle.NET/TestApp/IO/Formats/JsonFile.cs | 26 +- .../TestApp/IO/Formats/TriangleFile.cs | 27 +- Triangle.NET/TestApp/IO/GeometryWriter.cs | 98 ------- Triangle.NET/TestApp/IO/IMeshFile.cs | 2 +- Triangle.NET/TestApp/IO/SvgImage.cs | 4 +- Triangle.NET/TestApp/Mesh Explorer.csproj | 1 - .../TestApp/Topology/TopologyRenderControl.cs | 4 +- Triangle.NET/Triangle/Geometry/Edge.cs | 2 +- Triangle.NET/Triangle/Geometry/IEdge.cs | 21 ++ Triangle.NET/Triangle/Geometry/IPolygon.cs | 24 ++ Triangle.NET/Triangle/Geometry/ISegment.cs | 17 +- .../Triangle/Geometry/InputGeometry.cs | 18 +- Triangle.NET/Triangle/Geometry/Polygon.cs | 246 ++++++++++++++++++ .../Geometry/{BoundingBox.cs => Rectangle.cs} | 60 +++-- Triangle.NET/Triangle/IO/FileProcessor.cs | 111 ++++++++ Triangle.NET/Triangle/IO/IFileFormat.cs | 8 + Triangle.NET/Triangle/IO/IGeometryFormat.cs | 27 -- Triangle.NET/Triangle/IO/IMeshFormat.cs | 19 +- Triangle.NET/Triangle/IO/IPolygonFormat.cs | 38 +++ Triangle.NET/Triangle/IO/TriangleFormat.cs | 52 +++- .../IO/{FileReader.cs => TriangleReader.cs} | 12 +- .../IO/{FileWriter.cs => TriangleWriter.cs} | 75 +++++- Triangle.NET/Triangle/Mesh.cs | 80 +----- .../Triangle/{ => Meshing}/Algorithm/Dwyer.cs | 2 +- .../{ => Meshing}/Algorithm/Incremental.cs | 10 +- .../{ => Meshing}/Algorithm/SweepLine.cs | 6 +- .../{ => Meshing}/ConstraintMesher.cs | 2 +- .../Triangle/Meshing/ConstraintOptions.cs | 31 +++ .../DataReader.cs => Meshing/Converter.cs} | 123 +++++---- .../Triangle/Meshing/IConstraintMesher.cs | 11 + .../Triangle/Meshing/IQualityMesher.cs | 11 + .../{Algorithm => Meshing}/ITriangulator.cs | 2 +- .../Triangle/{ => Meshing}/QualityMesher.cs | 2 +- .../Triangle/Meshing/QualityOptions.cs | 38 +++ Triangle.NET/Triangle/Tools/QuadTree.cs | 52 ++-- Triangle.NET/Triangle/Tools/Voronoi.cs | 14 +- Triangle.NET/Triangle/Triangle.csproj | 35 ++- 39 files changed, 927 insertions(+), 410 deletions(-) delete mode 100644 Triangle.NET/TestApp/IO/GeometryWriter.cs create mode 100644 Triangle.NET/Triangle/Geometry/IEdge.cs create mode 100644 Triangle.NET/Triangle/Geometry/IPolygon.cs create mode 100644 Triangle.NET/Triangle/Geometry/Polygon.cs rename Triangle.NET/Triangle/Geometry/{BoundingBox.cs => Rectangle.cs} (68%) create mode 100644 Triangle.NET/Triangle/IO/FileProcessor.cs create mode 100644 Triangle.NET/Triangle/IO/IFileFormat.cs delete mode 100644 Triangle.NET/Triangle/IO/IGeometryFormat.cs create mode 100644 Triangle.NET/Triangle/IO/IPolygonFormat.cs rename Triangle.NET/Triangle/IO/{FileReader.cs => TriangleReader.cs} (95%) rename Triangle.NET/Triangle/IO/{FileWriter.cs => TriangleWriter.cs} (85%) rename Triangle.NET/Triangle/{ => Meshing}/Algorithm/Dwyer.cs (97%) rename Triangle.NET/Triangle/{ => Meshing}/Algorithm/Incremental.cs (92%) rename Triangle.NET/Triangle/{ => Meshing}/Algorithm/SweepLine.cs (96%) rename Triangle.NET/Triangle/{ => Meshing}/ConstraintMesher.cs (97%) create mode 100644 Triangle.NET/Triangle/Meshing/ConstraintOptions.cs rename Triangle.NET/Triangle/{IO/DataReader.cs => Meshing/Converter.cs} (84%) create mode 100644 Triangle.NET/Triangle/Meshing/IConstraintMesher.cs create mode 100644 Triangle.NET/Triangle/Meshing/IQualityMesher.cs rename Triangle.NET/Triangle/{Algorithm => Meshing}/ITriangulator.cs (91%) rename Triangle.NET/Triangle/{ => Meshing}/QualityMesher.cs (97%) create mode 100644 Triangle.NET/Triangle/Meshing/QualityOptions.cs diff --git a/Triangle.NET/MeshRenderer.Core/RenderData.cs b/Triangle.NET/MeshRenderer.Core/RenderData.cs index d2f803a..c194e07 100644 --- a/Triangle.NET/MeshRenderer.Core/RenderData.cs +++ b/Triangle.NET/MeshRenderer.Core/RenderData.cs @@ -69,10 +69,10 @@ namespace MeshRenderer.Core } this.Bounds = new BoundingBox( - (float)data.Bounds.MinX, - (float)data.Bounds.MaxX, - (float)data.Bounds.MinY, - (float)data.Bounds.MaxY); + (float)data.Bounds.Left, + (float)data.Bounds.Right, + (float)data.Bounds.Bottom, + (float)data.Bounds.Top); } /// @@ -150,10 +150,10 @@ namespace MeshRenderer.Core this.Triangles = triangles.ToArray(); this.Bounds = new BoundingBox( - (float)mesh.Bounds.MinX, - (float)mesh.Bounds.MaxX, - (float)mesh.Bounds.MinY, - (float)mesh.Bounds.MaxY); + (float)mesh.Bounds.Left, + (float)mesh.Bounds.Right, + (float)mesh.Bounds.Bottom, + (float)mesh.Bounds.Top); } /// diff --git a/Triangle.NET/TestApp/IO/EpsImage.cs b/Triangle.NET/TestApp/IO/EpsImage.cs index 4218848..81bc10d 100644 --- a/Triangle.NET/TestApp/IO/EpsImage.cs +++ b/Triangle.NET/TestApp/IO/EpsImage.cs @@ -319,12 +319,12 @@ namespace MeshExplorer.IO } } - private void UpdateMetrics(BoundingBox bounds) + private void UpdateMetrics(Rectangle bounds) { - x_max = bounds.MaxX; - x_min = bounds.MinX; - y_max = bounds.MaxY; - y_min = bounds.MinY; + x_max = bounds.Right; + x_min = bounds.Left; + y_max = bounds.Top; + y_min = bounds.Bottom; // Enlarge width 5% on each side x_scale = x_max - x_min; diff --git a/Triangle.NET/TestApp/IO/Formats/JsonFile.cs b/Triangle.NET/TestApp/IO/Formats/JsonFile.cs index d4c57e6..e0d39b7 100644 --- a/Triangle.NET/TestApp/IO/Formats/JsonFile.cs +++ b/Triangle.NET/TestApp/IO/Formats/JsonFile.cs @@ -15,6 +15,7 @@ namespace MeshExplorer.IO.Formats using TriangleNet.Data; using TriangleNet.Geometry; using TriangleNet.IO; + using TriangleNet.Meshing; /// /// Read and write JSON files. @@ -74,6 +75,11 @@ namespace MeshExplorer.IO.Formats return false; } + public bool IsSupported(string file) + { + throw new NotImplementedException(); + } + public Mesh Import(string filename) { InputGeometry geometry = this.Read(filename); @@ -90,10 +96,9 @@ namespace MeshExplorer.IO.Formats } } - Mesh mesh = new Mesh(); - mesh.Load(geometry, triangles); + var converter = new Converter(); - return mesh; + return converter.ToMesh(geometry, triangles); } public void Write(Mesh mesh, string filename) @@ -151,6 +156,11 @@ namespace MeshExplorer.IO.Formats } } + public void Write(Mesh mesh, StreamWriter stream) + { + throw new NotImplementedException(); + } + /// /// /// @@ -213,6 +223,16 @@ namespace MeshExplorer.IO.Formats return data; } + public void Write(InputGeometry polygon, string filename) + { + throw new NotImplementedException(); + } + + public void Write(InputGeometry polygon, StreamWriter stream) + { + throw new NotImplementedException(); + } + private void ParseJson(string filename) { if (this.json == null || this.file != filename) diff --git a/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs b/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs index 4ffbfda..5aa43fe 100644 --- a/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs +++ b/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs @@ -7,13 +7,10 @@ namespace MeshExplorer.IO.Formats { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using TriangleNet.IO; using System.IO; - using TriangleNet.Geometry; using TriangleNet; + using TriangleNet.Geometry; + using TriangleNet.IO; /// /// Read and write files defined in classic Triangle format. @@ -45,11 +42,26 @@ namespace MeshExplorer.IO.Formats return (ext == ".ele"); } + public bool IsSupported(string file) + { + throw new NotImplementedException(); + } + public InputGeometry Read(string filename) { return format.Read(filename); } + public void Write(InputGeometry polygon, string filename) + { + format.Write(polygon, filename); + } + + public void Write(InputGeometry polygon, StreamWriter stream) + { + format.Write(polygon, stream); + } + public Mesh Import(string filename) { return format.Import(filename); @@ -62,5 +74,10 @@ namespace MeshExplorer.IO.Formats format.Write(mesh, filename); } } + + public void Write(Mesh mesh, StreamWriter stream) + { + throw new NotImplementedException(); + } } } diff --git a/Triangle.NET/TestApp/IO/GeometryWriter.cs b/Triangle.NET/TestApp/IO/GeometryWriter.cs deleted file mode 100644 index 79746e1..0000000 --- a/Triangle.NET/TestApp/IO/GeometryWriter.cs +++ /dev/null @@ -1,98 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/ -// -// ----------------------------------------------------------------------- - -namespace MeshExplorer.IO -{ - using System.Collections.Generic; - using System.IO; - using System.Linq; - using TriangleNet.Geometry; - - /// - /// Writes an InputGeometry to standard Triangle format. - /// - public static class GeometryWriter - { - private static int OFFSET = 0; - - /// - /// Writes an InputGeometry to a Triangle format .poly file. - /// - /// The InputGeometry to write. - /// The filename. - /// If true, indices will start at 1 (compatible with original C code). - public static void Write(InputGeometry geometry, string filename, bool compatibleMode = false) - { - OFFSET = compatibleMode ? 1 : 0; - - using (StreamWriter writer = new StreamWriter(filename)) - { - WritePoints(writer, geometry.Points, geometry.Count); - WriteSegments(writer, geometry.Segments); - WriteHoles(writer, geometry.Holes); - } - } - - private static void WritePoints(StreamWriter writer, IEnumerable points, int count) - { - int attributes = 0, index = OFFSET; - - var first = points.FirstOrDefault(); - - if (first.Attributes != null) - { - attributes = first.Attributes.Length; - } - - writer.WriteLine("{0} {1} {2} {3}", count, 2, attributes, 1); - - foreach (var item in points) - { - // Vertex number, x and y coordinates. - writer.Write("{0} {1} {2}", index, item.X.ToString(Util.Nfi), item.Y.ToString(Util.Nfi)); - - // Write attributes. - for (int j = 0; j < attributes; j++) - { - writer.Write(" {0}", item.Attributes[j].ToString(Util.Nfi)); - } - - // Write the boundary marker. - writer.WriteLine(" {0}", item.Boundary); - - index++; - } - } - - private static void WriteSegments(StreamWriter writer, IEnumerable edges) - { - int index = OFFSET; - - writer.WriteLine("{0} {1}", edges.Count(), 1); - - foreach (var item in edges) - { - writer.WriteLine("{0} {1} {2} {3}", index, item.P0 + OFFSET, item.P1 + OFFSET, item.Boundary); - - index++; - } - } - - private static void WriteHoles(StreamWriter writer, IEnumerable holes) - { - int index = OFFSET; - - writer.WriteLine("{0}", holes.Count()); - - foreach (var item in holes) - { - writer.WriteLine("{0} {1} {2}", index, item.X.ToString(Util.Nfi), item.Y.ToString(Util.Nfi)); - - index++; - } - } - } -} diff --git a/Triangle.NET/TestApp/IO/IMeshFile.cs b/Triangle.NET/TestApp/IO/IMeshFile.cs index 4c077f1..35e55cb 100644 --- a/Triangle.NET/TestApp/IO/IMeshFile.cs +++ b/Triangle.NET/TestApp/IO/IMeshFile.cs @@ -16,7 +16,7 @@ namespace MeshExplorer.IO /// /// Defines an interface for mesh file formats. /// - public interface IMeshFile : IGeometryFormat, IMeshFormat + public interface IMeshFile : IPolygonFormat, IMeshFormat { /// /// The supported file extensions. diff --git a/Triangle.NET/TestApp/IO/SvgImage.cs b/Triangle.NET/TestApp/IO/SvgImage.cs index e0d4943..8fc1c02 100644 --- a/Triangle.NET/TestApp/IO/SvgImage.cs +++ b/Triangle.NET/TestApp/IO/SvgImage.cs @@ -49,8 +49,8 @@ namespace MeshExplorer.IO scale = width / ((float)bounds.Width + 2 * margin); - int x_offset = -(int)((bounds.MinX - margin) * scale); - int y_offset = (int)((bounds.MaxY + margin) * scale); + int x_offset = -(int)((bounds.Left - margin) * scale); + int y_offset = (int)((bounds.Top + margin) * scale); int height = (int)((bounds.Height + 2 * margin) * scale); diff --git a/Triangle.NET/TestApp/Mesh Explorer.csproj b/Triangle.NET/TestApp/Mesh Explorer.csproj index 785e533..f5c7a75 100644 --- a/Triangle.NET/TestApp/Mesh Explorer.csproj +++ b/Triangle.NET/TestApp/Mesh Explorer.csproj @@ -115,7 +115,6 @@ - diff --git a/Triangle.NET/TestApp/Topology/TopologyRenderControl.cs b/Triangle.NET/TestApp/Topology/TopologyRenderControl.cs index fa59b5a..726b630 100644 --- a/Triangle.NET/TestApp/Topology/TopologyRenderControl.cs +++ b/Triangle.NET/TestApp/Topology/TopologyRenderControl.cs @@ -48,8 +48,8 @@ namespace MeshExplorer.Topology //zoom.ClipMargin = 10.0f; var b = mesh.Bounds; - zoom.Update(new BoundingBox((float)b.MinX, (float)b.MaxX, - (float)b.MinY, (float)b.MaxY)); + zoom.Update(new BoundingBox((float)b.Left, (float)b.Right, + (float)b.Bottom, (float)b.Top)); InitializeBuffer(); diff --git a/Triangle.NET/Triangle/Geometry/Edge.cs b/Triangle.NET/Triangle/Geometry/Edge.cs index 07d7bc6..d7c28d7 100644 --- a/Triangle.NET/Triangle/Geometry/Edge.cs +++ b/Triangle.NET/Triangle/Geometry/Edge.cs @@ -15,7 +15,7 @@ namespace TriangleNet.Geometry /// /// Represents a straight line segment in 2D space. /// - public class Edge + public class Edge : IEdge { /// /// Gets the first endpoints index. diff --git a/Triangle.NET/Triangle/Geometry/IEdge.cs b/Triangle.NET/Triangle/Geometry/IEdge.cs new file mode 100644 index 0000000..ab34b9f --- /dev/null +++ b/Triangle.NET/Triangle/Geometry/IEdge.cs @@ -0,0 +1,21 @@ + +namespace TriangleNet.Geometry +{ + public interface IEdge + { + /// + /// Gets the first endpoints index. + /// + int P0 { get; } + + /// + /// Gets the second endpoints index. + /// + int P1 { get; } + + /// + /// Gets the segments boundary mark. + /// + int Boundary { get; } + } +} diff --git a/Triangle.NET/Triangle/Geometry/IPolygon.cs b/Triangle.NET/Triangle/Geometry/IPolygon.cs new file mode 100644 index 0000000..358798f --- /dev/null +++ b/Triangle.NET/Triangle/Geometry/IPolygon.cs @@ -0,0 +1,24 @@ + +namespace TriangleNet.Geometry +{ + using System.Collections.Generic; + using TriangleNet.Data; + + public interface IPolygon + { + List Points { get; } + List Segments { get; } + + List Holes { get; } + //List Regions { get; } + List Regions { get; } + + bool HasPointMarkers { get; set; } + bool HasSegmentMarkers { get; set; } + + void AddContour(IEnumerable points, int marker, bool hole, bool convex); + void AddContour(IEnumerable points, int marker, Point hole); + + Rectangle Bounds(); + } +} diff --git a/Triangle.NET/Triangle/Geometry/ISegment.cs b/Triangle.NET/Triangle/Geometry/ISegment.cs index a7cb5e4..d982e07 100644 --- a/Triangle.NET/Triangle/Geometry/ISegment.cs +++ b/Triangle.NET/Triangle/Geometry/ISegment.cs @@ -11,25 +11,10 @@ namespace TriangleNet.Geometry /// /// Interface for segment geometry. /// - public interface ISegment + public interface ISegment : IEdge { #region Public properties - /// - /// Gets the first endpoints vertex id. - /// - int P0 { get; } - - /// - /// Gets the seconds endpoints vertex id. - /// - int P1 { get; } - - /// - /// Gets the segment boundary mark. - /// - int Boundary { get; } - /// /// Gets the segments endpoint. /// diff --git a/Triangle.NET/Triangle/Geometry/InputGeometry.cs b/Triangle.NET/Triangle/Geometry/InputGeometry.cs index 55b717b..1c04358 100644 --- a/Triangle.NET/Triangle/Geometry/InputGeometry.cs +++ b/Triangle.NET/Triangle/Geometry/InputGeometry.cs @@ -21,7 +21,7 @@ namespace TriangleNet.Geometry internal List holes; internal List regions; - BoundingBox bounds; + Rectangle bounds; // Used to check consitent use of point attributes. private int pointAttributes = -1; @@ -46,7 +46,7 @@ namespace TriangleNet.Geometry holes = new List(); regions = new List(); - bounds = new BoundingBox(); + bounds = new Rectangle(); pointAttributes = -1; } @@ -54,7 +54,7 @@ namespace TriangleNet.Geometry /// /// Gets the bounding box of the input geometry. /// - public BoundingBox Bounds + public Rectangle Bounds { get { return bounds; } } @@ -138,8 +138,9 @@ namespace TriangleNet.Geometry /// Boundary marker. public void AddPoint(double x, double y, int boundary) { - points.Add(new Vertex(x, y, boundary)); - bounds.Expand(x, y); + var v = new Vertex(x, y, boundary); + points.Add(v); + bounds.Expand(v); } /// @@ -176,8 +177,9 @@ namespace TriangleNet.Geometry throw new ArgumentException("Inconsitent use of point attributes."); } - points.Add(new Vertex(x, y, boundary) { attributes = attribs }); - bounds.Expand(x, y); + var v = new Vertex(x, y, boundary) { attributes = attribs }; + points.Add(v); + bounds.Expand(v); } /// @@ -202,7 +204,7 @@ namespace TriangleNet.Geometry } points.Add(v); - bounds.Expand(v.x, v.y); + bounds.Expand(v); } /// diff --git a/Triangle.NET/Triangle/Geometry/Polygon.cs b/Triangle.NET/Triangle/Geometry/Polygon.cs new file mode 100644 index 0000000..b177801 --- /dev/null +++ b/Triangle.NET/Triangle/Geometry/Polygon.cs @@ -0,0 +1,246 @@ + +namespace TriangleNet.Geometry +{ + using System; + using System.Collections.Generic; + using TriangleNet.Data; + + public class Polygon : IPolygon + { + List points; + List holes; + List regions; + + List segments; + + public List Points + { + get { return points; } + } + + public List Holes + { + get { return holes; } + } + + public List Regions + { + get { return regions; } + } + + public List Segments + { + get { return segments; } + } + + public bool HasPointMarkers { get; set; } + + public bool HasSegmentMarkers { get; set; } + + public int Count + { + get { return points.Count; } + } + + public Polygon() + : this(3) + { + } + + public Polygon(int capacity) + { + points = new List(capacity); + holes = new List(); + regions = new List(); + + segments = new List(); + + HasPointMarkers = false; + HasSegmentMarkers = false; + } + + public void AddContour(IEnumerable points, int marker = 0, + bool hole = false, bool convex = false) + { + // Copy input to list. + var contour = new List(points); + + int offset = this.points.Count; + int count = contour.Count; + + // Check if first vertex equals last vertex. + if (contour[0] == contour[count - 1]) + { + count--; + contour.RemoveAt(count); + } + + // Add points to polygon. + this.points.AddRange(contour); + + var centroid = new Point(0.0, 0.0); + + for (int i = 0; i < count; i++) + { + centroid.x += contour[i].x; + centroid.y += contour[i].y; + + // Add segments to polygon. + this.segments.Add(new Edge(offset + i, offset + ((i + 1) % count), marker)); + } + + if (hole) + { + if (convex) + { + // If the hole is convex, use its centroid. + centroid.x /= count; + centroid.y /= count; + + this.holes.Add(centroid); + } + else + { + this.holes.Add(FindPointInPolygon(contour)); + } + } + } + + public void AddContour(IEnumerable points, int marker, Point hole) + { + // Copy input to list. + var contour = new List(points); + + int offset = this.points.Count; + int count = contour.Count; + + // Check if first vertex equals last vertex. + if (contour[0] == contour[count - 1]) + { + count--; + contour.RemoveAt(count); + } + + // Add points to polygon. + this.points.AddRange(contour); + + for (int i = 0; i < count; i++) + { + // Add segments to polygon. + this.segments.Add(new Edge(offset + i, offset + ((i + 1) % count), marker)); + } + + this.holes.Add(hole); + } + + public Rectangle Bounds() + { + var bounds = new Rectangle(); + bounds.Expand(this.points); + + return bounds; + } + + public void Add(Vertex vertex) + { + this.points.Add(vertex); + } + + public void Add(Vertex vertex, double[] attributes) + { + // TODO: check attibutes + + vertex.attributes = attributes; + + this.points.Add(vertex); + } + + public void Add(Edge edge) + { + this.segments.Add(edge); + } + + private Point FindPointInPolygon(List contour) + { + var bounds = new Rectangle(); + bounds.Expand(contour); + + int length = contour.Count; + int limit = 8; + + var test = new Point(); + + Point a, b; // Current edge. + double cx, cy; // Center of current edge. + double dx, dy; // Direction perpendicular to edge. + + for (int i = 0; i < length; i++) + { + a = contour[i]; + b = contour[(i + 1) % length]; + + cx = (a.x + b.x) / 2; + cy = (a.y + b.y) / 2; + + dx = (b.y - a.y) / 1.374; + dy = (a.x - b.x) / 1.374; + + for (int j = 1; j <= limit; j++) + { + // Search to the right of the segment. + test.x = cx + dx / j; + test.y = cy + dy / j; + + if (bounds.Contains(test) && IsPointInPolygon(test, contour)) + { + return test; + } + + // Search on the other side of the segment. + test.x = cx - dx / j; + test.y = cy - dy / j; + + if (bounds.Contains(test) && IsPointInPolygon(test, contour)) + { + return test; + } + } + } + + throw new Exception(); + } + + /// + /// Return true if the given point is inside the polygon, or false if it is not. + /// + /// + /// + /// + /// + /// WARNING: If the point is exactly on the edge of the polygon, then the function + /// may return true or false. + /// + private bool IsPointInPolygon(Point point, List poly) + { + bool inside = false; + + double x = point.x; + double y = point.y; + + int count = poly.Count; + + for (int i = 0, j = count - 1; i < count; i++) + { + if (((poly[i].y < y && poly[j].y >= y) || (poly[j].y < y && poly[i].y >= y)) + && (poly[i].x <= x || poly[j].x <= x)) + { + inside ^= (poly[i].x + (y - poly[i].y) / (poly[j].y - poly[i].y) * (poly[j].x - poly[i].x) < x); + } + + j = i; + } + + return inside; + } + } +} diff --git a/Triangle.NET/Triangle/Geometry/BoundingBox.cs b/Triangle.NET/Triangle/Geometry/Rectangle.cs similarity index 68% rename from Triangle.NET/Triangle/Geometry/BoundingBox.cs rename to Triangle.NET/Triangle/Geometry/Rectangle.cs index 946154e..8947b3b 100644 --- a/Triangle.NET/Triangle/Geometry/BoundingBox.cs +++ b/Triangle.NET/Triangle/Geometry/Rectangle.cs @@ -7,36 +7,37 @@ namespace TriangleNet.Geometry { using System; + using System.Collections.Generic; /// /// A simple bounding box class. /// - public class BoundingBox + public class Rectangle { double xmin, ymin, xmax, ymax; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public BoundingBox() + public Rectangle() : this(double.MaxValue, double.MaxValue, -double.MaxValue, -double.MaxValue) { } - public BoundingBox(BoundingBox other) - : this(other.MinX, other.MinY, other.MaxX, other.MaxY) + public Rectangle(Rectangle other) + : this(other.Left, other.Bottom, other.Right, other.Top) { } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// with predefined bounds. /// /// Minimum x value. /// Minimum y value. /// Maximum x value. /// Maximum y value. - public BoundingBox(double xmin, double ymin, double xmax, double ymax) + public Rectangle(double xmin, double ymin, double xmax, double ymax) { this.xmin = xmin; this.xmax = xmax; @@ -47,7 +48,7 @@ namespace TriangleNet.Geometry /// /// Gets the minimum x value (left boundary). /// - public double MinX + public double Left { get { return xmin; } } @@ -55,7 +56,7 @@ namespace TriangleNet.Geometry /// /// Gets the maximum x value (right boundary). /// - public double MaxX + public double Right { get { return xmax; } } @@ -63,7 +64,7 @@ namespace TriangleNet.Geometry /// /// Gets the minimum y value (bottom boundary). /// - public double MinY + public double Bottom { get { return ymin; } } @@ -71,7 +72,7 @@ namespace TriangleNet.Geometry /// /// Gets the maximum y value (top boundary). /// - public double MaxY + public double Top { get { return ymax; } } @@ -109,13 +110,24 @@ namespace TriangleNet.Geometry /// Expand rectangle to include given point. /// /// X coordinate. - /// Y coordinate. - public void Expand(double x, double y) + /// Y coordinate. + public void Expand(Point p) { - xmin = Math.Min(xmin, x); - ymin = Math.Min(ymin, y); - xmax = Math.Max(xmax, x); - ymax = Math.Max(ymax, y); + xmin = Math.Min(xmin, p.x); + ymin = Math.Min(ymin, p.y); + xmax = Math.Max(xmax, p.x); + ymax = Math.Max(ymax, p.y); + } + + /// + /// Expand rectangle to include a list of points. + /// + public void Expand(IEnumerable points) + { + foreach (var p in points) + { + Expand(p); + } } /// @@ -123,7 +135,7 @@ namespace TriangleNet.Geometry /// /// X coordinate. /// Y coordinate. - public void Expand(BoundingBox other) + public void Expand(Rectangle other) { xmin = Math.Min(xmin, other.xmin); ymin = Math.Min(ymin, other.ymin); @@ -141,16 +153,16 @@ namespace TriangleNet.Geometry return ((pt.x >= xmin) && (pt.x <= xmax) && (pt.y >= ymin) && (pt.y <= ymax)); } - public bool Contains(BoundingBox other) + public bool Contains(Rectangle other) { - return (xmin <= other.MinX && other.MaxX <= xmax - && ymin <= other.MinY && other.MaxY <= ymax); + return (xmin <= other.Left && other.Right <= xmax + && ymin <= other.Bottom && other.Top <= ymax); } - public bool Intersects(BoundingBox other) + public bool Intersects(Rectangle other) { - return (other.MinX < xmax && xmin < other.MaxX - && other.MinY < ymax && ymin < other.MaxY); + return (other.Left < xmax && xmin < other.Right + && other.Bottom < ymax && ymin < other.Top); } } } diff --git a/Triangle.NET/Triangle/IO/FileProcessor.cs b/Triangle.NET/Triangle/IO/FileProcessor.cs new file mode 100644 index 0000000..46d20d7 --- /dev/null +++ b/Triangle.NET/Triangle/IO/FileProcessor.cs @@ -0,0 +1,111 @@ + +namespace TriangleNet.IO +{ + using System; + using System.Collections.Generic; + using TriangleNet.Geometry; + using TriangleNet.Meshing; + + public static class FileProcessor + { + static List formats = new List(); + + public static void Add(IFileFormat format) + { + formats.Add(format); + } + + public static bool IsSupported(string file) + { + foreach (var format in formats) + { + if (format.IsSupported(file)) + { + return true; + } + } + + return false; + } + + #region Polygon read/write + + /// + /// Read a file containing polygon geometry. + /// + /// The path of the file to read. + /// An instance of the class. + public static InputGeometry Read(string filename) + { + foreach (IPolygonFormat format in formats) + { + if (format != null && format.IsSupported(filename)) + { + return format.Read(filename); + } + } + + throw new Exception("File format not supported."); + } + + /// + /// Save a polygon geometry to disk. + /// + /// An instance of the class. + /// The path of the file to save. + public static void Write(InputGeometry polygon, string filename) + { + foreach (IPolygonFormat format in formats) + { + if (format != null && format.IsSupported(filename)) + { + format.Write(polygon, filename); + } + } + + throw new Exception("File format not supported."); + } + + #endregion + + #region Mesh read/write + + /// + /// Read a file containing a mesh. + /// + /// The path of the file to read. + /// An instance of the interface. + public static Mesh Import(string filename) + { + foreach (IMeshFormat format in formats) + { + if (format != null && format.IsSupported(filename)) + { + return format.Import(filename); + } + } + + throw new Exception("File format not supported."); + } + + /// + /// Save a mesh to disk. + /// + /// An instance of the interface. + /// The path of the file to save. + public static void Write(Mesh mesh, string filename) + { + foreach (IMeshFormat format in formats) + { + if (format != null && format.IsSupported(filename)) + { + format.Write(mesh, filename); + } + } + + throw new Exception("File format not supported."); + } + + #endregion + } +} diff --git a/Triangle.NET/Triangle/IO/IFileFormat.cs b/Triangle.NET/Triangle/IO/IFileFormat.cs new file mode 100644 index 0000000..f688adb --- /dev/null +++ b/Triangle.NET/Triangle/IO/IFileFormat.cs @@ -0,0 +1,8 @@ + +namespace TriangleNet.IO +{ + public interface IFileFormat + { + bool IsSupported(string file); + } +} diff --git a/Triangle.NET/Triangle/IO/IGeometryFormat.cs b/Triangle.NET/Triangle/IO/IGeometryFormat.cs deleted file mode 100644 index 2ee7532..0000000 --- a/Triangle.NET/Triangle/IO/IGeometryFormat.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ -// -// ----------------------------------------------------------------------- - -namespace TriangleNet.IO -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using TriangleNet.Geometry; - - /// - /// Interface for geometry input. - /// - public interface IGeometryFormat - { - /// - /// Read a file containing geometry information. - /// - /// The path of the file to read. - /// An instance of the class. - InputGeometry Read(string filename); - } -} diff --git a/Triangle.NET/Triangle/IO/IMeshFormat.cs b/Triangle.NET/Triangle/IO/IMeshFormat.cs index 3971810..1524f3a 100644 --- a/Triangle.NET/Triangle/IO/IMeshFormat.cs +++ b/Triangle.NET/Triangle/IO/IMeshFormat.cs @@ -6,29 +6,32 @@ namespace TriangleNet.IO { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using TriangleNet.Geometry; + using System.IO; /// /// Interface for mesh I/O. /// - public interface IMeshFormat + public interface IMeshFormat : IFileFormat { /// /// Read a file containing a mesh. /// /// The path of the file to read. - /// An instance of the class. + /// An instance of the interface. Mesh Import(string filename); /// /// Save a mesh to disk. /// - /// An instance of the class. + /// An instance of the interface. /// The path of the file to save. void Write(Mesh mesh, string filename); + + /// + /// Save a mesh to a . + /// + /// An instance of the interface. + /// The stream to save to. + void Write(Mesh mesh, StreamWriter stream); } } diff --git a/Triangle.NET/Triangle/IO/IPolygonFormat.cs b/Triangle.NET/Triangle/IO/IPolygonFormat.cs new file mode 100644 index 0000000..db9f0e7 --- /dev/null +++ b/Triangle.NET/Triangle/IO/IPolygonFormat.cs @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.IO +{ + using System.IO; + using TriangleNet.Geometry; + + /// + /// Interface for geometry input. + /// + public interface IPolygonFormat : IFileFormat + { + /// + /// Read a file containing polygon geometry. + /// + /// The path of the file to read. + /// An instance of the class. + InputGeometry Read(string filename); + + /// + /// Save a polygon geometry to disk. + /// + /// An instance of the class. + /// The path of the file to save. + void Write(InputGeometry polygon, string filename); + + /// + /// Save a polygon geometry to a . + /// + /// An instance of the class. + /// The stream to save to. + void Write(InputGeometry polygon, StreamWriter stream); + } +} diff --git a/Triangle.NET/Triangle/IO/TriangleFormat.cs b/Triangle.NET/Triangle/IO/TriangleFormat.cs index fd49fbc..6dcadbe 100644 --- a/Triangle.NET/Triangle/IO/TriangleFormat.cs +++ b/Triangle.NET/Triangle/IO/TriangleFormat.cs @@ -8,16 +8,27 @@ namespace TriangleNet.IO { using System; using System.Collections.Generic; - using System.Linq; - using System.Text; - using TriangleNet.Geometry; using System.IO; + using TriangleNet.Geometry; + using TriangleNet.Meshing; /// /// Implements geometry and mesh file formats of the the original Triangle code. /// - public class TriangleFormat : IGeometryFormat, IMeshFormat + public class TriangleFormat : IPolygonFormat, IMeshFormat { + public bool IsSupported(string file) + { + string ext = Path.GetExtension(file); + + if (ext == ".node" || ext == ".poly" || ext == ".ele") + { + return true; + } + + return false; + } + public Mesh Import(string filename) { string ext = Path.GetExtension(filename); @@ -27,14 +38,13 @@ namespace TriangleNet.IO List triangles; InputGeometry geometry; - FileReader.Read(filename, out geometry, out triangles); + TriangleReader.Read(filename, out geometry, out triangles); if (geometry != null && triangles != null) { - Mesh mesh = new Mesh(); - mesh.Load(geometry, triangles); + var converter = new Converter(); - return mesh; + return converter.ToMesh(geometry, triangles.ToArray()); } } @@ -43,8 +53,13 @@ namespace TriangleNet.IO public void Write(Mesh mesh, string filename) { - FileWriter.WritePoly(mesh, Path.ChangeExtension(filename, ".poly")); - FileWriter.WriteElements(mesh, Path.ChangeExtension(filename, ".ele")); + TriangleWriter.WritePoly((Mesh)mesh, Path.ChangeExtension(filename, ".poly")); + TriangleWriter.WriteElements((Mesh)mesh, Path.ChangeExtension(filename, ".ele")); + } + + public void Write(Mesh mesh, StreamWriter stream) + { + throw new NotImplementedException(); } public InputGeometry Read(string filename) @@ -53,15 +68,26 @@ namespace TriangleNet.IO if (ext == ".node") { - return FileReader.ReadNodeFile(filename); + return TriangleReader.ReadNodeFile(filename); } - + if (ext == ".poly") { - return FileReader.ReadPolyFile(filename); + return TriangleReader.ReadPolyFile(filename); } throw new NotSupportedException("File format '" + ext + "' not supported."); } + + + public void Write(InputGeometry polygon, string filename) + { + TriangleWriter.WritePoly(polygon, filename); + } + + public void Write(InputGeometry polygon, StreamWriter stream) + { + throw new NotImplementedException(); + } } } diff --git a/Triangle.NET/Triangle/IO/FileReader.cs b/Triangle.NET/Triangle/IO/TriangleReader.cs similarity index 95% rename from Triangle.NET/Triangle/IO/FileReader.cs rename to Triangle.NET/Triangle/IO/TriangleReader.cs index 29f4ae3..142dfa3 100644 --- a/Triangle.NET/Triangle/IO/FileReader.cs +++ b/Triangle.NET/Triangle/IO/TriangleReader.cs @@ -18,7 +18,7 @@ namespace TriangleNet.IO /// /// Helper methods for reading Triangle file formats. /// - public static class FileReader + public static class TriangleReader { static NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat; static int startIndex = 0; @@ -99,12 +99,12 @@ namespace TriangleNet.IO if (File.Exists(path)) { - geometry = FileReader.ReadPolyFile(path); + geometry = TriangleReader.ReadPolyFile(path); } else { path = Path.ChangeExtension(filename, ".node"); - geometry = FileReader.ReadNodeFile(path); + geometry = TriangleReader.ReadNodeFile(path); } } @@ -115,13 +115,13 @@ namespace TriangleNet.IO { triangles = null; - FileReader.Read(filename, out geometry); + TriangleReader.Read(filename, out geometry); string path = Path.ChangeExtension(filename, ".ele"); if (File.Exists(path) && geometry != null) { - triangles = FileReader.ReadEleFile(path); + triangles = TriangleReader.ReadEleFile(path); } } @@ -132,7 +132,7 @@ namespace TriangleNet.IO { InputGeometry geometry = null; - FileReader.Read(filename, out geometry); + TriangleReader.Read(filename, out geometry); return geometry; } diff --git a/Triangle.NET/Triangle/IO/FileWriter.cs b/Triangle.NET/Triangle/IO/TriangleWriter.cs similarity index 85% rename from Triangle.NET/Triangle/IO/FileWriter.cs rename to Triangle.NET/Triangle/IO/TriangleWriter.cs index b13f6d5..29e0a20 100644 --- a/Triangle.NET/Triangle/IO/FileWriter.cs +++ b/Triangle.NET/Triangle/IO/TriangleWriter.cs @@ -17,7 +17,7 @@ namespace TriangleNet.IO /// /// Helper methods for writing Triangle file formats. /// - public static class FileWriter + public static class TriangleWriter { static NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat; @@ -28,8 +28,8 @@ namespace TriangleNet.IO /// public static void Write(Mesh mesh, string filename) { - FileWriter.WritePoly(mesh, Path.ChangeExtension(filename, ".poly")); - FileWriter.WriteElements(mesh, Path.ChangeExtension(filename, ".ele")); + TriangleWriter.WritePoly(mesh, Path.ChangeExtension(filename, ".poly")); + TriangleWriter.WriteElements(mesh, Path.ChangeExtension(filename, ".ele")); } /// @@ -41,7 +41,7 @@ namespace TriangleNet.IO { using (StreamWriter writer = new StreamWriter(filename)) { - FileWriter.WriteNodes(writer, mesh); + TriangleWriter.WriteNodes(writer, mesh); } } @@ -177,6 +177,69 @@ namespace TriangleNet.IO } } + /// + /// Write the segments and holes to a .poly file. + /// + /// Data source. + /// File name. + /// Write nodes into this file. + /// If the nodes should not be written into this file, + /// make sure a .node file was written before, so that the nodes + /// are numbered right. + public static void WritePoly(InputGeometry polygon, string filename) + { + bool hasMarkers = true; + + using (StreamWriter writer = new StreamWriter(filename)) + { + // TODO: write vertex attributes + + // Write nodes to this file. + TriangleWriter.WriteNodes(writer, polygon.points, true, 0, false); + + // Number of segments, number of boundary markers (zero or one). + writer.WriteLine("{0} {1}", polygon.Segments.Count, hasMarkers ? "1" : "0"); + + int j = 0; + foreach (var seg in polygon.Segments) + { + // Segment number, indices of its two endpoints, and possibly a marker. + if (hasMarkers) + { + writer.WriteLine("{0} {1} {2} {3}", j, seg.P0, seg.P1, seg.Boundary); + } + else + { + writer.WriteLine("{0} {1} {2}", j, seg.P0, seg.P1); + } + + j++; + } + + // Holes + j = 0; + writer.WriteLine("{0}", polygon.Holes.Count); + foreach (var hole in polygon.Holes) + { + writer.WriteLine("{0} {1} {2}", j++, hole.X.ToString(nfi), hole.Y.ToString(nfi)); + } + + // Regions + if (polygon.Regions.Count > 0) + { + j = 0; + writer.WriteLine("{0}", polygon.Regions.Count); + foreach (var region in polygon.Regions) + { + writer.WriteLine("{0} {1} {2} {3}", j, region.point.X.ToString(nfi), + region.point.Y.ToString(nfi), region.id); + + j++; + } + } + } + } + /// /// Write the segments and holes to a .poly file. /// @@ -184,7 +247,7 @@ namespace TriangleNet.IO /// public static void WritePoly(Mesh mesh, string filename) { - FileWriter.WritePoly(mesh, filename, true); + TriangleWriter.WritePoly(mesh, filename, true); } /// @@ -208,7 +271,7 @@ namespace TriangleNet.IO if (writeNodes) { // Write nodes to this file. - FileWriter.WriteNodes(writer, mesh); + TriangleWriter.WriteNodes(writer, mesh); } else { diff --git a/Triangle.NET/Triangle/Mesh.cs b/Triangle.NET/Triangle/Mesh.cs index 6cb4dbe..d784b84 100644 --- a/Triangle.NET/Triangle/Mesh.cs +++ b/Triangle.NET/Triangle/Mesh.cs @@ -13,7 +13,8 @@ namespace TriangleNet using TriangleNet.Data; using TriangleNet.Log; using TriangleNet.IO; - using TriangleNet.Algorithm; + using TriangleNet.Meshing; + using TriangleNet.Meshing.Algorithm; using TriangleNet.Smoothing; using TriangleNet.Geometry; using TriangleNet.Tools; @@ -48,7 +49,7 @@ namespace TriangleNet internal List regions; // Other variables. - internal BoundingBox bounds; // x and y bounds. + internal Rectangle bounds; // x and y bounds. internal int invertices; // Number of input vertices. internal int inelements; // Number of input triangles. internal int insegments; // Number of input segments. @@ -95,7 +96,7 @@ namespace TriangleNet /// /// Gets the mesh bounding box. /// - public BoundingBox Bounds + public Rectangle Bounds { get { return this.bounds; } } @@ -213,82 +214,13 @@ namespace TriangleNet } } - /// - /// Load a mesh from file (.node/poly and .ele). - /// - public void Load(string filename) - { - List triangles; - InputGeometry geometry; - - FileReader.Read(filename, out geometry, out triangles); - - if (geometry != null && triangles != null) - { - Load(geometry, triangles); - } - } - - /// - /// Reconstructs a mesh from raw input data. - /// - public void Load(InputGeometry input, List triangles) - { - if (input == null || triangles == null) - { - throw new ArgumentException("Invalid input (argument is null)."); - } - - // Clear all data structures / reset hash seeds - this.ResetData(); - - if (input.HasSegments) - { - behavior.Poly = true; - - this.holes.AddRange(input.Holes); - } - - //if (input.EdgeMarkers != null) - //{ - // behavior.UseBoundaryMarkers = true; - //} - - //if (input.TriangleAreas != null) - //{ - // behavior.VarArea = true; - //} - - // TODO: remove - if (!behavior.Poly) - { - // Be careful not to allocate space for element area constraints that - // will never be assigned any value (other than the default -1.0). - behavior.VarArea = false; - - // Be careful not to add an extra attribute to each element unless the - // input supports it (PSLG in, but not refining a preexisting mesh). - behavior.useRegions = false; - } - - behavior.useRegions = input.Regions.Count > 0; - - TransferNodes(input); - - // Read and reconstruct a mesh. - hullsize = DataReader.Reconstruct(this, input, triangles.ToArray()); - - // Calculate the number of edges. - edges = (3 * triangles.Count + hullsize) / 2; - } - /// /// Triangulate given input file (.node or .poly). /// /// public void Triangulate(string inputFile) { - InputGeometry input = FileReader.Read(inputFile); + InputGeometry input = TriangleReader.Read(inputFile); this.Triangulate(input); } @@ -693,7 +625,7 @@ namespace TriangleNet /// Read the vertices from memory. /// /// The input data. - private void TransferNodes(InputGeometry data) + internal void TransferNodes(InputGeometry data) { List points = data.points; diff --git a/Triangle.NET/Triangle/Algorithm/Dwyer.cs b/Triangle.NET/Triangle/Meshing/Algorithm/Dwyer.cs similarity index 97% rename from Triangle.NET/Triangle/Algorithm/Dwyer.cs rename to Triangle.NET/Triangle/Meshing/Algorithm/Dwyer.cs index 335729d..7525772 100644 --- a/Triangle.NET/Triangle/Algorithm/Dwyer.cs +++ b/Triangle.NET/Triangle/Meshing/Algorithm/Dwyer.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Algorithm +namespace TriangleNet.Meshing.Algorithm { using System; using TriangleNet.Data; diff --git a/Triangle.NET/Triangle/Algorithm/Incremental.cs b/Triangle.NET/Triangle/Meshing/Algorithm/Incremental.cs similarity index 92% rename from Triangle.NET/Triangle/Algorithm/Incremental.cs rename to Triangle.NET/Triangle/Meshing/Algorithm/Incremental.cs index f77f962..d5056aa 100644 --- a/Triangle.NET/Triangle/Algorithm/Incremental.cs +++ b/Triangle.NET/Triangle/Meshing/Algorithm/Incremental.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Algorithm +namespace TriangleNet.Meshing.Algorithm { using TriangleNet.Data; using TriangleNet.Log; @@ -29,7 +29,7 @@ namespace TriangleNet.Algorithm void GetBoundingBox() { Otri inftri = default(Otri); // Handle for the triangular bounding box. - BoundingBox box = mesh.bounds; + Rectangle box = mesh.bounds; // Find the width (or height, whichever is larger) of the triangulation. double width = box.Width; @@ -42,9 +42,9 @@ namespace TriangleNet.Algorithm width = 1.0; } // Create the vertices of the bounding box. - mesh.infvertex1 = new Vertex(box.MinX - 50.0 * width, box.MinY - 40.0 * width); - mesh.infvertex2 = new Vertex(box.MaxX + 50.0 * width, box.MinY - 40.0 * width); - mesh.infvertex3 = new Vertex(0.5 * (box.MinX + box.MaxX), box.MaxY + 60.0 * width); + mesh.infvertex1 = new Vertex(box.Left - 50.0 * width, box.Bottom - 40.0 * width); + mesh.infvertex2 = new Vertex(box.Right + 50.0 * width, box.Bottom - 40.0 * width); + mesh.infvertex3 = new Vertex(0.5 * (box.Left + box.Right), box.Top + 60.0 * width); // Create the bounding box. mesh.MakeTriangle(ref inftri); diff --git a/Triangle.NET/Triangle/Algorithm/SweepLine.cs b/Triangle.NET/Triangle/Meshing/Algorithm/SweepLine.cs similarity index 96% rename from Triangle.NET/Triangle/Algorithm/SweepLine.cs rename to Triangle.NET/Triangle/Meshing/Algorithm/SweepLine.cs index 49aa268..9bef669 100644 --- a/Triangle.NET/Triangle/Algorithm/SweepLine.cs +++ b/Triangle.NET/Triangle/Meshing/Algorithm/SweepLine.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Algorithm +namespace TriangleNet.Meshing.Algorithm { using System; using System.Collections.Generic; @@ -525,7 +525,7 @@ namespace TriangleNet.Algorithm // Nonexistent x value used as a flag to mark circle events in sweepline // Delaunay algorithm. - xminextreme = 10 * mesh.bounds.MinX - 9 * mesh.bounds.MaxX; + xminextreme = 10 * mesh.bounds.Left - 9 * mesh.bounds.Right; SweepEvent[] eventheap; @@ -603,7 +603,7 @@ namespace TriangleNet.Algorithm HeapDelete(eventheap, heapsize, 0); heapsize--; check4events = true; - if (nextevent.xkey < mesh.bounds.MinX) + if (nextevent.xkey < mesh.bounds.Left) { fliptri = nextevent.otriEvent; fliptri.Oprev(ref farlefttri); diff --git a/Triangle.NET/Triangle/ConstraintMesher.cs b/Triangle.NET/Triangle/Meshing/ConstraintMesher.cs similarity index 97% rename from Triangle.NET/Triangle/ConstraintMesher.cs rename to Triangle.NET/Triangle/Meshing/ConstraintMesher.cs index db3809d..82f08f7 100644 --- a/Triangle.NET/Triangle/ConstraintMesher.cs +++ b/Triangle.NET/Triangle/Meshing/ConstraintMesher.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet +namespace TriangleNet.Meshing { using System; using System.Collections.Generic; diff --git a/Triangle.NET/Triangle/Meshing/ConstraintOptions.cs b/Triangle.NET/Triangle/Meshing/ConstraintOptions.cs new file mode 100644 index 0000000..7753270 --- /dev/null +++ b/Triangle.NET/Triangle/Meshing/ConstraintOptions.cs @@ -0,0 +1,31 @@ + +namespace TriangleNet.Meshing +{ + public class ConstraintOptions + { + public static ConstraintOptions Empty + { + get { return new ConstraintOptions(); } + } + + #region Public properties + + /// + /// Gets or sets a value indicating wether to use regions. + /// + public bool UseRegions { get; set; } + + /// + /// Gets or sets a value indicating wether to create a Conforming + /// Delaunay triangulation. + /// + public bool ConformingDelaunay { get; set; } + + /// + /// Enclose the convex hull with segments. + /// + public bool Convex { get; set; } + + #endregion + } +} diff --git a/Triangle.NET/Triangle/IO/DataReader.cs b/Triangle.NET/Triangle/Meshing/Converter.cs similarity index 84% rename from Triangle.NET/Triangle/IO/DataReader.cs rename to Triangle.NET/Triangle/Meshing/Converter.cs index 6ef37be..e0a36d6 100644 --- a/Triangle.NET/Triangle/IO/DataReader.cs +++ b/Triangle.NET/Triangle/Meshing/Converter.cs @@ -1,32 +1,32 @@ // ----------------------------------------------------------------------- -// +// // 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.IO +namespace TriangleNet.Meshing { using System; using System.Collections.Generic; - using System.IO; using System.Linq; - using System.Globalization; using TriangleNet.Data; - using TriangleNet.Log; using TriangleNet.Geometry; + using TriangleNet.Log; /// /// The DataReader class provides methods for mesh reconstruction. /// - static class DataReader + public class Converter { + public Mesh ToMesh(InputGeometry polygon, IList triangles) + { + return ToMesh(polygon, triangles.ToArray()); + } + /// /// Reconstruct a triangulation from its raw data representation. /// - /// - /// - /// /// /// Reads an .ele file and reconstructs the original mesh. If the -p switch /// is used, this procedure will also read a .poly file and reconstruct the @@ -46,43 +46,33 @@ namespace TriangleNet.IO /// the corresponding pointer is adjusted to refer to a subsegment rather /// than the next triangle of the stack. /// - public static int Reconstruct(Mesh mesh, InputGeometry input, ITriangle[] triangles) + public Mesh ToMesh(InputGeometry polygon, ITriangle[] triangles) { - int hullsize = 0; - Otri tri = default(Otri); - Otri triangleleft = default(Otri); - Otri checktri = default(Otri); - Otri checkleft = default(Otri); - Otri checkneighbor = default(Otri); Osub subseg = default(Osub); - List[] vertexarray; // Triangle - Otri prevlink; // Triangle - Otri nexttri; // Triangle - Vertex tdest, tapex; - Vertex checkdest, checkapex; - Vertex shorg; - Vertex segmentorg, segmentdest; - int[] corner = new int[3]; - int[] end = new int[2]; - //bool segmentmarkers = false; - int boundmarker; - int aroundvertex; - bool notfound; int i = 0; int elements = triangles == null ? 0 : triangles.Length; - int numberofsegments = input.segments.Count; + int numberofsegments = polygon.Segments.Count; + + var mesh = new Mesh(); + + mesh.TransferNodes(polygon); mesh.inelements = elements; - mesh.regions.AddRange(input.regions); + mesh.regions.AddRange(polygon.Regions); + mesh.behavior.useRegions = polygon.Regions.Count > 0; + + if (polygon.Segments.Count > 0) + { + mesh.behavior.Poly = true; + mesh.holes.AddRange(polygon.Holes); + } // Create the triangles. for (i = 0; i < mesh.inelements; i++) { mesh.MakeTriangle(ref tri); - // Mark the triangle as living. - //tri.triangle.neighbors[0].triangle = tri.triangle; } if (mesh.behavior.Poly) @@ -93,15 +83,37 @@ namespace TriangleNet.IO for (i = 0; i < mesh.insegments; i++) { mesh.MakeSegment(ref subseg); - // Mark the subsegment as living. - //subseg.ss.subsegs[0].ss = subseg.ss; } } + var vertexarray = SetNeighbors(mesh, triangles); + + SetSegments(mesh, polygon, vertexarray); + + return mesh; + } + + /// + /// Finds the adjacencies between triangles by forming a stack of triangles + /// for each vertex. + /// + private static List[] SetNeighbors(Mesh mesh, ITriangle[] triangles) + { + Otri tri = default(Otri); + Otri triangleleft = default(Otri); + Otri checktri = default(Otri); + Otri checkleft = default(Otri); + Otri nexttri; // Triangle + Vertex tdest, tapex; + Vertex checkdest, checkapex; + int[] corner = new int[3]; + int aroundvertex; + int i; + // Allocate a temporary array that maps each vertex to some adjacent - // triangle. I took care to allocate all the permanent memory for - // triangles and subsegments first. - vertexarray = new List[mesh.vertices.Count]; + // triangle. + var vertexarray = new List[mesh.vertices.Count]; + // Each vertex is initially unrepresented. for (i = 0; i < mesh.vertices.Count; i++) { @@ -198,21 +210,41 @@ namespace TriangleNet.IO i++; } + return vertexarray; + } + + private static void SetSegments(Mesh mesh, InputGeometry polygon, List[] vertexarray) + { + Otri checktri = default(Otri); + Otri nexttri; // Triangle + Vertex checkdest; + Otri checkneighbor = default(Otri); + Osub subseg = default(Osub); + Otri prevlink; // Triangle + Vertex shorg; + Vertex segmentorg, segmentdest; + int[] end = new int[2]; + bool notfound; + //bool segmentmarkers = false; + int boundmarker; + int aroundvertex; + int i; + + int hullsize = 0; + // Prepare to count the boundary edges. - hullsize = 0; if (mesh.behavior.Poly) { - // Read the segments from the .poly file, and link them - // to their neighboring triangles. + // Link the segments to their neighboring triangles. boundmarker = 0; i = 0; foreach (var item in mesh.subsegs.Values) { subseg.seg = item; - end[0] = input.segments[i].P0; - end[1] = input.segments[i].P1; - boundmarker = input.segments[i].Boundary; + end[0] = polygon.segments[i].P0; + end[1] = polygon.segments[i].P1; + boundmarker = polygon.segments[i].Boundary; for (int j = 0; j < 2; j++) { @@ -316,7 +348,8 @@ namespace TriangleNet.IO } } - return hullsize; + mesh.hullsize = hullsize; + mesh.edges = (3 * mesh.triangles.Count + hullsize) / 2; } } } diff --git a/Triangle.NET/Triangle/Meshing/IConstraintMesher.cs b/Triangle.NET/Triangle/Meshing/IConstraintMesher.cs new file mode 100644 index 0000000..a7190da --- /dev/null +++ b/Triangle.NET/Triangle/Meshing/IConstraintMesher.cs @@ -0,0 +1,11 @@ + +namespace TriangleNet.Meshing +{ + using TriangleNet.Geometry; + + public interface IConstraintMesher + { + Mesh Triangulate(IPolygon polygon); + Mesh Triangulate(IPolygon polygon, ConstraintOptions options); + } +} diff --git a/Triangle.NET/Triangle/Meshing/IQualityMesher.cs b/Triangle.NET/Triangle/Meshing/IQualityMesher.cs new file mode 100644 index 0000000..4e8f5f9 --- /dev/null +++ b/Triangle.NET/Triangle/Meshing/IQualityMesher.cs @@ -0,0 +1,11 @@ + +namespace TriangleNet.Meshing +{ + using TriangleNet.Geometry; + + public interface IQualityMesher + { + Mesh Triangulate(IPolygon polygon, QualityOptions quality); + Mesh Triangulate(IPolygon polygon, ConstraintOptions options, QualityOptions quality); + } +} diff --git a/Triangle.NET/Triangle/Algorithm/ITriangulator.cs b/Triangle.NET/Triangle/Meshing/ITriangulator.cs similarity index 91% rename from Triangle.NET/Triangle/Algorithm/ITriangulator.cs rename to Triangle.NET/Triangle/Meshing/ITriangulator.cs index fe9147b..5f42e61 100644 --- a/Triangle.NET/Triangle/Algorithm/ITriangulator.cs +++ b/Triangle.NET/Triangle/Meshing/ITriangulator.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Algorithm +namespace TriangleNet.Meshing { using System; using System.Collections.Generic; diff --git a/Triangle.NET/Triangle/QualityMesher.cs b/Triangle.NET/Triangle/Meshing/QualityMesher.cs similarity index 97% rename from Triangle.NET/Triangle/QualityMesher.cs rename to Triangle.NET/Triangle/Meshing/QualityMesher.cs index b60bcf0..f4f6ae1 100644 --- a/Triangle.NET/Triangle/QualityMesher.cs +++ b/Triangle.NET/Triangle/Meshing/QualityMesher.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet +namespace TriangleNet.Meshing { using System; using System.Collections.Generic; diff --git a/Triangle.NET/Triangle/Meshing/QualityOptions.cs b/Triangle.NET/Triangle/Meshing/QualityOptions.cs new file mode 100644 index 0000000..516c002 --- /dev/null +++ b/Triangle.NET/Triangle/Meshing/QualityOptions.cs @@ -0,0 +1,38 @@ + +namespace TriangleNet.Meshing +{ + using System; + using TriangleNet.Geometry; + + public class QualityOptions + { + public static QualityOptions Empty + { + get { return new QualityOptions(); } + } + + #region Public properties + + /// + /// Gets or sets a maximum angle constraint. + /// + public double MaximumAngle { get; set; } + + /// + /// Gets or sets a minimum angle constraint. + /// + public double MinimumAngle { get; set; } + + /// + /// Gets or sets a maximum triangle area constraint. + /// + public double MaximumArea { get; set; } + + /// + /// Apply a user-defined triangle constraint. + /// + public Func UserTest { get; set; } + + #endregion + } +} diff --git a/Triangle.NET/Triangle/Tools/QuadTree.cs b/Triangle.NET/Triangle/Tools/QuadTree.cs index 58dd60e..3052233 100644 --- a/Triangle.NET/Triangle/Tools/QuadTree.cs +++ b/Triangle.NET/Triangle/Tools/QuadTree.cs @@ -133,7 +133,7 @@ namespace TriangleNet.Tools static readonly byte[] BITVECTOR = { 0x1, 0x2, 0x4, 0x8 }; - BoundingBox bounds; + Rectangle bounds; Point pivot; QuadTree tree; QuadNode[] regions; @@ -141,17 +141,17 @@ namespace TriangleNet.Tools byte bitRegions; - public QuadNode(BoundingBox box, QuadTree tree) + public QuadNode(Rectangle box, QuadTree tree) : this(box, tree, false) { } - public QuadNode(BoundingBox box, QuadTree tree, bool init) + public QuadNode(Rectangle box, QuadTree tree, bool init) { this.tree = tree; - this.bounds = new BoundingBox(box.MinX, box.MinY, box.MaxX, box.MaxY); - this.pivot = new Point((box.MinX + box.MaxX) / 2, (box.MinY + box.MaxY) / 2); + this.bounds = new Rectangle(box.Left, box.Bottom, box.Right, box.Top); + this.pivot = new Point((box.Left + box.Right) / 2, (box.Bottom + box.Top) / 2); this.bitRegions = 0; @@ -190,22 +190,22 @@ namespace TriangleNet.Tools // |------+pivot--| // | sw | se | // +--------------+ - BoundingBox box; + Rectangle box; // 1. region south west - box = new BoundingBox(bounds.MinX, bounds.MinY, pivot.X, pivot.Y); + box = new Rectangle(bounds.Left, bounds.Bottom, pivot.X, pivot.Y); regions[0] = new QuadNode(box, tree); // 2. region south east - box = new BoundingBox(pivot.X, bounds.MinY, bounds.MaxX, pivot.Y); + box = new Rectangle(pivot.X, bounds.Bottom, bounds.Right, pivot.Y); regions[1] = new QuadNode(box, tree); // 3. region north west - box = new BoundingBox(bounds.MinX, pivot.Y, pivot.X, bounds.MaxY); + box = new Rectangle(bounds.Left, pivot.Y, pivot.X, bounds.Top); regions[2] = new QuadNode(box, tree); // 4. region north east - box = new BoundingBox(pivot.X, pivot.Y, bounds.MaxX, bounds.MaxY); + box = new Rectangle(pivot.X, pivot.Y, bounds.Right, bounds.Top); regions[3] = new QuadNode(box, tree); Point[] triangle = new Point[3]; @@ -295,12 +295,12 @@ namespace TriangleNet.Tools // we have an intersection double yComponent = triangle[k].Y + t * dy; - if (yComponent < pivot.Y && yComponent >= bounds.MinY) + if (yComponent < pivot.Y && yComponent >= bounds.Bottom) { AddToRegion(index, SW); AddToRegion(index, SE); } - else if (yComponent <= bounds.MaxY) + else if (yComponent <= bounds.Top) { AddToRegion(index, NW); AddToRegion(index, NE); @@ -308,34 +308,34 @@ namespace TriangleNet.Tools } // find intersection with plane x = m_boundingBox[0].dX - t = (bounds.MinX - triangle[k].X) / dx; + t = (bounds.Left - triangle[k].X) / dx; if (t < (1 + EPS) && t > -EPS) { // we have an intersection double yComponent = triangle[k].Y + t * dy; - if (yComponent < pivot.Y && yComponent >= bounds.MinY) + if (yComponent < pivot.Y && yComponent >= bounds.Bottom) { AddToRegion(index, SW); } - else if (yComponent <= bounds.MaxY) // TODO: check && yComponent >= pivot.Y + else if (yComponent <= bounds.Top) // TODO: check && yComponent >= pivot.Y { AddToRegion(index, NW); } } // find intersection with plane x = m_boundingBox[1].dX - t = (bounds.MaxX - triangle[k].X) / dx; + t = (bounds.Right - triangle[k].X) / dx; if (t < (1 + EPS) && t > -EPS) { // we have an intersection double yComponent = triangle[k].Y + t * dy; - if (yComponent < pivot.Y && yComponent >= bounds.MinY) + if (yComponent < pivot.Y && yComponent >= bounds.Bottom) { AddToRegion(index, SE); } - else if (yComponent <= bounds.MaxY) + else if (yComponent <= bounds.Top) { AddToRegion(index, NE); } @@ -353,12 +353,12 @@ namespace TriangleNet.Tools // we have an intersection xComponent = triangle[k].X + t * dx; - if (xComponent > pivot.X && xComponent <= bounds.MaxX) + if (xComponent > pivot.X && xComponent <= bounds.Right) { AddToRegion(index, SE); AddToRegion(index, NE); } - else if (xComponent >= bounds.MinX) + else if (xComponent >= bounds.Left) { AddToRegion(index, SW); AddToRegion(index, NW); @@ -366,34 +366,34 @@ namespace TriangleNet.Tools } // find intersection with plane y = m_boundingBox[0].dY - t = (bounds.MinY - triangle[k].Y) / dy; + t = (bounds.Bottom - triangle[k].Y) / dy; if (t < (1 + EPS) && t > -EPS) { // we have an intersection xComponent = triangle[k].X + t * dx; - if (xComponent > pivot.X && xComponent <= bounds.MaxX) + if (xComponent > pivot.X && xComponent <= bounds.Right) { AddToRegion(index, SE); } - else if (xComponent >= bounds.MinX) + else if (xComponent >= bounds.Left) { AddToRegion(index, SW); } } // find intersection with plane y = m_boundingBox[1].dY - t = (bounds.MaxY - triangle[k].Y) / dy; + t = (bounds.Top - triangle[k].Y) / dy; if (t < (1 + EPS) && t > -EPS) { // we have an intersection xComponent = triangle[k].X + t * dx; - if (xComponent > pivot.X && xComponent <= bounds.MaxX) + if (xComponent > pivot.X && xComponent <= bounds.Right) { AddToRegion(index, NE); } - else if (xComponent >= bounds.MinX) + else if (xComponent >= bounds.Left) { AddToRegion(index, NW); } diff --git a/Triangle.NET/Triangle/Tools/Voronoi.cs b/Triangle.NET/Triangle/Tools/Voronoi.cs index 27a8235..ab51785 100644 --- a/Triangle.NET/Triangle/Tools/Voronoi.cs +++ b/Triangle.NET/Triangle/Tools/Voronoi.cs @@ -27,7 +27,7 @@ namespace TriangleNet.Tools int rayIndex; // Bounding box of the triangles circumcenters. - BoundingBox bounds; + Rectangle bounds; /// /// Initializes a new instance of the class. @@ -82,7 +82,7 @@ namespace TriangleNet.Tools rayPoints = new Dictionary(); rayIndex = 0; - bounds = new BoundingBox(); + bounds = new Rectangle(); // Compute triangles circumcenters and setup bounding box ComputeCircumCenters(); @@ -119,7 +119,7 @@ namespace TriangleNet.Tools points[item.id] = pt; - bounds.Expand(pt.x, pt.y); + bounds.Expand(pt); } double ds = Math.Max(bounds.Width, bounds.Height); @@ -269,10 +269,10 @@ namespace TriangleNet.Tools double t1, x1, y1, t2, x2, y2; // Bounding box - double minX = bounds.MinX; - double maxX = bounds.MaxX; - double minY = bounds.MinY; - double maxY = bounds.MaxY; + double minX = bounds.Left; + double maxX = bounds.Right; + double minY = bounds.Bottom; + double maxY = bounds.Top; // Check if point is inside the bounds if (x < minX || x > maxX || y < minY || y > maxY) diff --git a/Triangle.NET/Triangle/Triangle.csproj b/Triangle.NET/Triangle/Triangle.csproj index 9bc56e7..e7cca62 100644 --- a/Triangle.NET/Triangle/Triangle.csproj +++ b/Triangle.NET/Triangle/Triangle.csproj @@ -41,10 +41,19 @@ - + + + + + + + + + + - + @@ -52,8 +61,8 @@ - - + + @@ -61,23 +70,23 @@ - - - + + - + + - + - + @@ -92,12 +101,14 @@ - + - + + +