git-svn-id: https://triangle.svn.codeplex.com/svn@78017 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5

This commit is contained in:
SND\wo80_cp
2016-01-15 17:06:26 +00:00
parent b9fac8b83e
commit 0a7be0a1b6
13 changed files with 345 additions and 73 deletions
+9 -11
View File
@@ -592,11 +592,11 @@ namespace MeshExplorer
UpdateLog();
}
private void CreateVoronoi()
private bool CreateVoronoi()
{
if (mesh == null)
{
return;
return false;
}
if (mesh.IsPolygon)
@@ -616,7 +616,7 @@ namespace MeshExplorer
DarkMessageBox.Show("Exception - Bounded Voronoi", ex.Message, MessageBoxButtons.OK);
}
this.voronoi = null;
return false;
}
}
else
@@ -624,12 +624,11 @@ namespace MeshExplorer
this.voronoi = new StandardVoronoi(mesh);
}
if (this.voronoi != null)
{
// HACK: List<Vertex> -> ICollection<Point> ? Nope, no way.
// Vertex[] -> ICollection<Point> ? Well, ok.
renderManager.Set(voronoi.Vertices.ToArray(), voronoi.Edges, false);
}
// HACK: List<Vertex> -> ICollection<Point> ? Nope, no way.
// Vertex[] -> ICollection<Point> ? Well, ok.
renderManager.Set(voronoi.Vertices.ToArray(), voronoi.Edges, false);
return true;
}
private void ShowLog()
@@ -722,8 +721,7 @@ namespace MeshExplorer
{
if (this.voronoi == null)
{
CreateVoronoi();
menuViewVoronoi.Checked = true;
menuViewVoronoi.Checked = CreateVoronoi();
}
else
{
@@ -167,12 +167,13 @@ namespace TriangleNet.Rendering
// Change or add as many colors as you like...
private static Color[] regionColors = {
Color.FromArgb(200, 0, 0, 255),
Color.Transparent,
Color.FromArgb(200, 0, 255, 0),
Color.FromArgb(200, 255, 0, 0),
Color.FromArgb(200, 0, 0, 255),
Color.FromArgb(200, 0, 255, 255),
Color.FromArgb(200, 255, 255, 0),
Color.FromArgb(200, 255, 0, 255),
Color.FromArgb(200, 0, 255, 0),
Color.FromArgb(200, 127, 0, 255),
Color.FromArgb(200, 0, 127, 255)
};
@@ -0,0 +1,150 @@
namespace TriangleNet.Rendering.GDI
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
public class ImageRenderer
{
ColorManager colors = ImageRenderer.LightScheme();
public ColorManager ColorScheme
{
get { return colors; }
set { colors = value; }
}
public bool EnableRegions { get; set; }
public bool EnablePoints { get; set; }
/// <summary>
/// Export the mesh to PNG format.
/// </summary>
/// <param name="mesh">The current mesh.</param>
/// <param name="width">The desired width (pixel) of the image.</param>
/// <param name="file">The PNG filename.</param>
/// <param name="regions">Enable rendering of regions.</param>
/// <param name="points">Enable rendering of points.</param>
public static void Save(Mesh mesh, string file = null, int width = 800,
bool regions = false, bool points = true)
{
// Check file name
if (String.IsNullOrWhiteSpace(file))
{
file = String.Format("mesh-{0}.png", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss"));
}
// Ensure .png extension.
if (file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
{
Path.ChangeExtension(file, ".png");
}
var renderer = new ImageRenderer();
renderer.EnableRegions = regions;
renderer.EnablePoints = points;
var bitmap = renderer.Render(mesh, width);
bitmap.Save(file, ImageFormat.Png);
}
/// <summary>
/// Export the mesh to PNG format.
/// </summary>
/// <param name="mesh">The current mesh.</param>
/// <param name="width">The desired width (pixel) of the image.</param>
/// <returns>The bitmap.</returns>
/// <remarks>
/// The width has to be at least 2 * sqrt(n), n the number of vertices.
/// Otherwise, an empty bitmap
/// </remarks>
public Bitmap Render(Mesh mesh, int width = 800)
{
Bitmap bitmap;
// Check if the specified width is reasonable
if (width < 2 * Math.Sqrt(mesh.Vertices.Count))
{
return new Bitmap(1, 1);
}
var bounds = mesh.Bounds;
// World margin on each side
float margin = (float)bounds.Height * 0.05f;
float scale = width / ((float)bounds.Width + 2 * margin);
var target = new Rectangle(0, 0, width, (int)((bounds.Height + 2 * margin) * scale));
bitmap = new Bitmap(width, target.Height, PixelFormat.Format32bppPArgb);
using (var g = Graphics.FromImage(bitmap))
{
g.Clear(colors.Background);
g.SmoothingMode = SmoothingMode.HighQuality;
var context = new RenderContext(new Projection(target), colors);
context.Add(mesh, true);
if (EnableRegions)
{
context.Add(GetRegions(mesh));
}
if (!EnablePoints)
{
context.Enable(3, false);
}
var renderer = new LayerRenderer();
renderer.Context = context;
renderer.RenderTarget = g;
renderer.Render();
}
return bitmap;
}
private int[] GetRegions(Mesh mesh)
{
mesh.Renumber();
var labels = new int[mesh.Triangles.Count];
var regions = new HashSet<int>();
foreach (var t in mesh.Triangles)
{
labels[t.ID] = t.Label;
regions.Add(t.Label);
}
if (colors.ColorDictionary == null)
{
colors.CreateColorDictionary(regions.Count);
}
return labels;
}
public static ColorManager LightScheme()
{
var colors = new ColorManager();
colors.Background = Color.White;
colors.Point = new SolidBrush(Color.FromArgb(60, 80, 120));
colors.SteinerPoint = new SolidBrush(Color.DarkGreen);
colors.Line = new Pen(Color.FromArgb(200, 200, 200));
colors.Segment = new Pen(Color.SteelBlue);
colors.VoronoiLine = new Pen(Color.FromArgb(160, 170, 180));
return colors;
}
}
}
@@ -50,6 +50,7 @@
<Compile Include="ColorManager.cs" />
<Compile Include="ExtensionMethods.cs" />
<Compile Include="GDI\FunctionRenderer.cs" />
<Compile Include="GDI\ImageRenderer.cs" />
<Compile Include="GDI\LayerRenderer.cs" />
<Compile Include="GDI\MeshRenderer.cs" />
<Compile Include="GDI\Native\GradientFillMode.cs" />
+17 -17
View File
@@ -76,8 +76,8 @@ namespace TriangleNet.Geometry
/// <summary>
/// Try to find a point inside the contour.
/// </summary>
/// <param name="limit">The number of iterations on each segment (default = 8).</param>
/// <param name="eps">Threshold for co-linear points (default = 2e-6).</param>
/// <param name="limit">The number of iterations on each segment (default = 5).</param>
/// <param name="eps">Threshold for co-linear points (default = 2e-5).</param>
/// <returns>Point inside the contour</returns>
/// <exception cref="Exception">Throws if no point could be found.</exception>
/// <remarks>
@@ -86,16 +86,16 @@ namespace TriangleNet.Geometry
/// on the bisecting line, or, if <see cref="IPredicates.CounterClockwise"/> is less than
/// eps, on the perpendicular line.
/// A given number of points will be tested (limit), while the distance to the contour
/// boundary will be reduced in each iteration (with a factor of 1/2^i, i = 1 ... limit).
/// boundary will be reduced in each iteration (with a factor 1 / 2^i, i = 1 ... limit).
/// </remarks>
public Point FindInteriorPoint(int limit = 8, double eps = 2e-6)
public Point FindInteriorPoint(int limit = 5, double eps = 2e-5)
{
var point = new Point(0.0, 0.0);
if (convex)
{
int count = this.Points.Count;
var point = new Point(0.0, 0.0);
for (int i = 0; i < count; i++)
{
point.x += this.Points[i].x;
@@ -152,8 +152,8 @@ namespace TriangleNet.Geometry
c = contour[(i + 2) % length];
// Corner point.
bx = b.X;
by = b.Y;
bx = b.x;
by = b.y;
// NOTE: if we knew the contour points were in counterclockwise order, we
// could skip concave corners and search only in one direction.
@@ -163,14 +163,14 @@ namespace TriangleNet.Geometry
if (Math.Abs(h) < eps)
{
// Points are nearly co-linear. Use perpendicular direction.
dx = (c.Y - a.Y) / 2;
dy = (a.X - c.X) / 2;
dx = (c.y - a.y) / 2;
dy = (a.x - c.x) / 2;
}
else
{
// Direction [midpoint(a-c) -> corner point]
dx = (a.X + c.X) / 2 - bx;
dy = (a.Y + c.Y) / 2 - by;
dx = (a.x + c.x) / 2 - bx;
dy = (a.y + c.y) / 2 - by;
}
// Move around the contour.
@@ -182,8 +182,8 @@ namespace TriangleNet.Geometry
for (int j = 0; j < limit; j++)
{
// Search in direction.
test.X = bx + dx / h;
test.Y = by + dy / h;
test.x = bx + dx * h;
test.y = by + dy * h;
if (bounds.Contains(test) && IsPointInPolygon(test, contour))
{
@@ -191,15 +191,15 @@ namespace TriangleNet.Geometry
}
// Search in opposite direction (see NOTE above).
test.X = bx - dx / h;
test.Y = by - dy / h;
test.x = bx - dx * h;
test.y = by - dy * h;
if (bounds.Contains(test) && IsPointInPolygon(test, contour))
{
return test;
}
h = 2.0 * h;
h = h / 2;
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ namespace TriangleNet.Geometry
#endif
public Point()
: this(0, 0, 0)
: this(0.0, 0.0, 0)
{
}
+24 -25
View File
@@ -18,6 +18,30 @@ namespace TriangleNet.Geometry
int label;
/// <summary>
/// Gets or sets the segments boundary mark.
/// </summary>
public int Label
{
get { return label; }
set { label = value; }
}
/// <summary>
/// Gets the first endpoints index.
/// </summary>
public int P0
{
get { return v0.id; }
}
/// <summary>
/// Gets the second endpoints index.
/// </summary>
public int P1
{
get { return v1.id; }
}
/// <summary>
/// Initializes a new instance of the <see cref="Segment" /> class.
/// </summary>
@@ -64,30 +88,5 @@ namespace TriangleNet.Geometry
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the first endpoints index.
/// </summary>
public int P0
{
get { return v0.id; }
}
/// <summary>
/// Gets the second endpoints index.
/// </summary>
public int P1
{
get { return v1.id; }
}
/// <summary>
/// Gets or sets the segments boundary mark.
/// </summary>
public int Label
{
get { return label; }
set { label = value; }
}
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ namespace TriangleNet.IO
{
public bool IsSupported(string file)
{
string ext = Path.GetExtension(file);
string ext = Path.GetExtension(file).ToLower();
if (ext == ".node" || ext == ".poly" || ext == ".ele")
{
@@ -0,0 +1,104 @@
namespace TriangleNet.Meshing.Iterators
{
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Topology;
public class VertexCirculator
{
List<Otri> cache = new List<Otri>();
Mesh mesh;
public VertexCirculator(Mesh mesh)
{
this.mesh = mesh;
mesh.MakeVertexMap();
}
/// <summary>
/// Enumerate all vertices adjacent to given vertex.
/// </summary>
/// <param name="vertex">The center vertex.</param>
/// <returns></returns>
public IEnumerable<Vertex> EnumerateVertices(Vertex vertex)
{
BuildCache(vertex, true);
foreach (var item in cache)
{
yield return item.Dest();
}
}
/// <summary>
/// Enumerate all triangles adjacent to given vertex.
/// </summary>
/// <param name="vertex">The center vertex.</param>
/// <returns></returns>
public IEnumerable<ITriangle> EnumerateTriangles(Vertex vertex)
{
BuildCache(vertex, false);
foreach (var item in cache)
{
yield return item.tri;
}
}
private void BuildCache(Vertex vertex, bool vertices)
{
cache.Clear();
Otri init = vertex.tri;
Otri next = default(Otri);
Otri prev = default(Otri);
init.Copy(ref next);
// Move counter-clockwise around the vertex.
while (next.tri.id != Mesh.DUMMY)
{
cache.Add(next);
next.Copy(ref prev);
next.Onext();
if (next.Equals(init))
{
break;
}
}
if (next.tri.id == Mesh.DUMMY)
{
// We reached the boundary. To get all adjacent triangles, start
// again at init triangle and now move clockwise.
init.Copy(ref next);
if (vertices)
{
// Don't forget to add the vertex lying on the boundary.
prev.Lnext();
cache.Add(prev);
}
next.Oprev();
while (next.tri.id != Mesh.DUMMY)
{
cache.Insert(0, next);
next.Oprev();
if (next.Equals(init))
{
break;
}
}
}
}
}
}
@@ -20,7 +20,8 @@ namespace TriangleNet.Smoothing
/// </remarks>
public class SimpleSmoother : ISmoother
{
IPredicates predicates;
TrianglePool pool;
Configuration config;
IVoronoiFactory factory;
@@ -30,19 +31,34 @@ namespace TriangleNet.Smoothing
/// Initializes a new instance of the <see cref="SimpleSmoother" /> class.
/// </summary>
public SimpleSmoother()
: this(new VoronoiFactory(), RobustPredicates.Default)
: this(new VoronoiFactory())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SimpleSmoother" /> class.
/// </summary>
/// <param name="factory">Voronoi object factory.</param>
/// <param name="predicates">Geometric predicates implementation.</param>
public SimpleSmoother(IVoronoiFactory factory, IPredicates predicates)
public SimpleSmoother(IVoronoiFactory factory)
{
this.factory = factory;
this.predicates = predicates;
this.pool = new TrianglePool();
this.config = new Configuration(
() => RobustPredicates.Default,
() => pool.Restart());
this.options = new ConstraintOptions() { ConformingDelaunay = true };
}
/// <summary>
/// Initializes a new instance of the <see cref="SimpleSmoother" /> class.
/// </summary>
/// <param name="factory">Voronoi object factory.</param>
/// <param name="config">Configuration.</param>
public SimpleSmoother(IVoronoiFactory factory, Configuration config)
{
this.factory = factory;
this.config = config;
this.options = new ConstraintOptions() { ConformingDelaunay = true };
}
@@ -56,18 +72,21 @@ namespace TriangleNet.Smoothing
{
var smoothedMesh = (Mesh)mesh;
var mesher = new GenericMesher(config);
var predicates = config.Predicates();
// The smoother should respect the mesh segment splitting behavior.
this.options.SegmentSplitting = smoothedMesh.behavior.NoBisect;
// Take a few smoothing rounds (Lloyd's algorithm).
for (int i = 0; i < limit; i++)
{
Step(smoothedMesh, factory);
Step(smoothedMesh, factory, predicates);
// Actually, we only want to rebuild, if mesh is no longer
// Actually, we only want to rebuild, if the mesh is no longer
// Delaunay. Flipping edges could be the right choice instead
// of re-triangulating...
smoothedMesh = (Mesh)Rebuild(smoothedMesh).Triangulate(options);
smoothedMesh = (Mesh)mesher.Triangulate(Rebuild(smoothedMesh), options);
factory.Reset();
}
@@ -75,7 +94,7 @@ namespace TriangleNet.Smoothing
smoothedMesh.CopyTo((Mesh)mesh);
}
private void Step(Mesh mesh, IVoronoiFactory factory)
private void Step(Mesh mesh, IVoronoiFactory factory, IPredicates predicates)
{
var voronoi = new BoundedVoronoi(mesh, factory, predicates);
+4 -5
View File
@@ -40,11 +40,10 @@ namespace TriangleNet.Tools
dx = vertex.x - org.x;
dy = vertex.y - org.y;
// To interpolate vertex attributes for the new vertex inserted at
// the circumcenter, define a coordinate system with a xi-axis,
// directed from the triangle's origin to its destination, and
// an eta-axis, directed from its origin to its apex.
// Calculate the xi and eta coordinates of the circumcenter.
// To interpolate vertex attributes for the new vertex, define a
// coordinate system with a xi-axis directed from the triangle's
// origin to its destination, and an eta-axis, directed from its
// origin to its apex.
xi = (yao * dx - xao * dy) * (2.0 * denominator);
eta = (xdo * dy - ydo * dx) * (2.0 * denominator);
+1
View File
@@ -71,6 +71,7 @@
<Compile Include="Meshing\IConstraintMesher.cs" />
<Compile Include="Meshing\IMesh.cs" />
<Compile Include="Meshing\IQualityMesher.cs" />
<Compile Include="Meshing\Iterators\VertexCirculator.cs" />
<Compile Include="Meshing\ITriangulator.cs" />
<Compile Include="Meshing\QualityOptions.cs" />
<Compile Include="Meshing\QualityMesher.cs" />
+1 -1
View File
@@ -298,7 +298,7 @@ namespace TriangleNet
public void Reset()
{
index = 0;
index = offset = 0;
}
}
}