diff --git a/Triangle.NET/TestApp/FormMain.cs b/Triangle.NET/TestApp/FormMain.cs
index f3d3fce..63dafec 100644
--- a/Triangle.NET/TestApp/FormMain.cs
+++ b/Triangle.NET/TestApp/FormMain.cs
@@ -11,6 +11,7 @@ using TriangleNet.Meshing.Algorithm;
using TriangleNet.Rendering;
using TriangleNet.Smoothing;
using TriangleNet.Voronoi;
+using TriangleNet.Rendering.Text;
namespace MeshExplorer
{
@@ -704,17 +705,17 @@ namespace MeshExplorer
if (format == 1)
{
- EpsImage eps = new EpsImage();
+ var eps = new EpsImage();
eps.Export(this.mesh, export.ImageName, size);
}
else if (format == 2)
{
- SvgImage svg = new SvgImage();
+ var svg = new SvgImage();
svg.Export(this.mesh, export.ImageName, size);
}
else
{
- RasterImage img = new RasterImage();
+ var img = new RasterImage();
img.Export(this.mesh, export.ImageName, size);
}
}
diff --git a/Triangle.NET/TestApp/IO/EpsImage.cs b/Triangle.NET/TestApp/IO/EpsImage.cs
deleted file mode 100644
index 0b2a5e9..0000000
--- a/Triangle.NET/TestApp/IO/EpsImage.cs
+++ /dev/null
@@ -1,423 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/
-// Original Matlab code by John Burkardt, Florida State University
-//
-// -----------------------------------------------------------------------
-
-namespace MeshExplorer.IO
-{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using TriangleNet;
- using TriangleNet.Geometry;
-
- using IntPoint = System.Drawing.Point;
-
- ///
- /// Writes a mesh to an EPS file.
- ///
- public class EpsImage
- {
- // EPS page metrics
- int x_ps_max = 576;
- int x_ps_max_clip = 594;
- int x_ps_min = 36;
- int x_ps_min_clip = 18;
- int y_ps_max = 666;
- int y_ps_max_clip = 684;
- int y_ps_min = 126;
- int y_ps_min_clip = 108;
-
- // Mesh metrics
- double x_max, x_min;
- double y_max, y_min;
- double x_scale, y_scale;
-
- ///
- /// Export the mesh to EPS format.
- ///
- /// The current mesh.
- /// The EPS filename.
- /// The desired width of the image (currently ignored).
- public void Export(Mesh mesh, string filename, int width)
- {
- // Check file name
- if (String.IsNullOrWhiteSpace(filename))
- {
- filename = String.Format("mesh-{0}.eps", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss"));
- }
-
- if (!filename.EndsWith(".eps"))
- {
- filename = Path.ChangeExtension(filename, ".eps");
- }
-
- UpdateMetrics(mesh.Bounds);
-
- using (var eps = new FormattingStreamWriter(filename))
- {
- WriteHeader(filename, eps);
-
- DrawClip(eps);
-
- DrawEdges(eps, mesh);
-
- DrawSegments(eps, mesh);
-
- DrawPoints(eps, mesh);
-
- WriteTrailer(eps);
- }
- }
-
- private void WriteHeader(string filename, StreamWriter eps)
- {
- eps.WriteLine("%!PS-Adobe-3.0 EPSF-3.0");
- eps.WriteLine("%%Creator: Triangle.NET Mesh Explorer");
- eps.WriteLine("%%Title: {0}", filename);
- eps.WriteLine("%%Pages: 1");
- eps.WriteLine("%%BoundingBox: {0} {1} {2} {3}", x_ps_min, y_ps_min, x_ps_max, y_ps_max);
- eps.WriteLine("%%Document-Fonts: Times-Roman");
- eps.WriteLine("%%LanguageLevel: 2");
- eps.WriteLine("%%EndComments");
- eps.WriteLine("%%BeginProlog");
- eps.WriteLine("/inch {72 mul} def");
- eps.WriteLine("%%EndProlog");
- eps.WriteLine("%%Page: 1 1");
- }
-
- private static void WriteTrailer(StreamWriter eps)
- {
- eps.WriteLine("%");
- eps.WriteLine("restore showpage");
- eps.WriteLine("%");
- eps.WriteLine("% End of page.");
- eps.WriteLine("%");
- eps.WriteLine("%%Trailer");
- eps.WriteLine("%%EOF");
- }
-
- private void DrawClip(StreamWriter eps)
- {
- eps.WriteLine("save");
- eps.WriteLine("%");
- eps.WriteLine("% Set the RGB color to very light gray.");
- eps.WriteLine("%");
- eps.WriteLine("0.900 0.900 0.900 setrgbcolor");
- eps.WriteLine("%");
- eps.WriteLine("% Draw a gray border around the page.");
- eps.WriteLine("%");
- eps.WriteLine("newpath");
- eps.WriteLine(" {0} {1} moveto", x_ps_min, y_ps_min);
- eps.WriteLine(" {0} {1} lineto", x_ps_max, y_ps_min);
- eps.WriteLine(" {0} {1} lineto", x_ps_max, y_ps_max);
- eps.WriteLine(" {0} {1} lineto", x_ps_min, y_ps_max);
- eps.WriteLine(" {0} {1} lineto", x_ps_min, y_ps_min);
- eps.WriteLine("stroke");
- eps.WriteLine("%");
- eps.WriteLine("% Set the RGB color to black.");
- eps.WriteLine("%");
- eps.WriteLine("0.000 0.000 0.000 setrgbcolor");
- eps.WriteLine("%");
- eps.WriteLine("% Set the font and its size.");
- eps.WriteLine("%");
- eps.WriteLine("/Times-Roman findfont");
- eps.WriteLine("0.50 inch scalefont");
- eps.WriteLine("setfont");
- eps.WriteLine("%");
- eps.WriteLine("% Print a title.");
- eps.WriteLine("%");
- eps.WriteLine("%210 702 moveto");
- eps.WriteLine("%(Triangulation) show");
- eps.WriteLine("%");
- eps.WriteLine("% Define a clipping polygon.");
- eps.WriteLine("%");
- eps.WriteLine("newpath");
- eps.WriteLine(" {0} {1} moveto", x_ps_min_clip, y_ps_min_clip);
- eps.WriteLine(" {0} {1} lineto", x_ps_max_clip, y_ps_min_clip);
- eps.WriteLine(" {0} {1} lineto", x_ps_max_clip, y_ps_max_clip);
- eps.WriteLine(" {0} {1} lineto", x_ps_min_clip, y_ps_max_clip);
- eps.WriteLine(" {0} {1} lineto", x_ps_min_clip, y_ps_min_clip);
- eps.WriteLine("clip newpath");
- }
-
- private void DrawEdges(StreamWriter eps, Mesh mesh)
- {
- IntPoint a, b;
-
- eps.WriteLine("%");
- eps.WriteLine("% Draw the triangles (mesh edges).");
- eps.WriteLine("%");
-
- SetStroke(eps, 0.6f, 0.6f, 0.6f, 0.4f);
-
- eps.WriteLine(@"/L {
-2 dict begin
-/y2 exch def
-/x2 exch def
-/y1 exch def
-/x1 exch def
-gsave
-newpath x1 y1 moveto x2 y2 lineto stroke
-grestore
-end
-} def");
-
- foreach (var e in EnumerateEdges(mesh))
- {
- a = Transform(e.GetVertex(0));
- b = Transform(e.GetVertex(1));
-
- eps.WriteLine("{0} {1} {2} {3} L", a.X, a.Y, b.X, b.Y);
- }
- }
-
- private void DrawSegments(StreamWriter eps, Mesh mesh)
- {
- IntPoint a, b;
-
- eps.WriteLine("%");
- eps.WriteLine("% Draw the segments.");
- eps.WriteLine("%");
-
- SetStroke(eps, 0.27f, 0.5f, 0.7f, 0.8f);
-
- foreach (var s in mesh.Segments)
- {
- a = Transform(s.GetVertex(0));
- b = Transform(s.GetVertex(1));
-
- eps.WriteLine("{0} {1} {2} {3} L", a.X, a.Y, b.X, b.Y);
- }
- }
-
- private void DrawPoints(StreamWriter eps, Mesh mesh)
- {
- IntPoint p;
-
- int n = mesh.Vertices.Count;
-
- // Size of the points.
- int size = (n < 100) ? 3 : ((n < 500) ? 2 : 1);
-
- eps.WriteLine("%");
- eps.WriteLine("% Draw the vertices.");
- eps.WriteLine("%");
-
- SetColor(eps, 0.0f, 0.4f, 0.0f);
-
- eps.WriteLine(@"/P {
-2 dict begin
-/y exch def
-/x exch def
-gsave
-newpath x y 1 0 360 arc fill
-grestore
-end
-} def");
-
- // TODO: EPS point size.
- // newpath x y {size} 0 360 arc fill
-
- foreach (var node in mesh.Vertices)
- {
- p = Transform(node);
-
- eps.WriteLine("{0} {1} P", p.X, p.Y);
- }
- }
-
- /*
- private void DrawPointLabels(StreamWriter eps, Mesh mesh)
- {
- int n = mesh.Vertices.Count;
-
- IntPoint p;
-
- StringBuilder labels = new StringBuilder();
-
- foreach (var node in mesh.Vertices)
- {
- p = Transform(node);
-
- labels.AppendFormat(" {0} {1} moveto ({2}) show", p.X, p.Y + 5, node.ID);
- labels.AppendLine();
- }
-
- eps.WriteLine("%");
- eps.WriteLine("% Label the nodes.");
- eps.WriteLine("%");
- eps.WriteLine("% Set the RGB color to darker blue.");
- eps.WriteLine("%");
- eps.WriteLine("0.000 0.250 0.850 setrgbcolor");
- eps.WriteLine("/Times-Roman findfont");
- eps.WriteLine("0.20 inch scalefont");
- eps.WriteLine("setfont");
- eps.WriteLine("%");
-
- eps.WriteLine(labels.ToString());
- }
-
- private void DrawTriangles(StreamWriter eps, Mesh mesh, bool label)
- {
- eps.WriteLine("%");
- eps.WriteLine("% Set the triangle line color and width.");
- eps.WriteLine("%");
- eps.WriteLine("0.6 0.6 0.6 setrgbcolor");
- eps.WriteLine("0.5 setlinewidth");
- eps.WriteLine("%");
- eps.WriteLine("% Draw the triangles.");
- eps.WriteLine("%");
-
- IntPoint a, b, c;
-
- foreach (var t in mesh.Triangles)
- {
- a = Transform(t.GetVertex(0));
- b = Transform(t.GetVertex(1));
- c = Transform(t.GetVertex(2));
-
- eps.WriteLine("newpath");
-
- eps.WriteLine(" {0} {1} moveto", a.X, a.Y);
- eps.WriteLine(" {0} {1} lineto", b.X, b.Y);
- eps.WriteLine(" {0} {1} lineto", c.X, c.Y);
- eps.WriteLine(" {0} {1} lineto", a.X, a.Y);
-
- eps.WriteLine("stroke");
- }
- }
-
- private void DrawTriangleLabels(StreamWriter eps, Mesh mesh)
- {
- var labels = new StringBuilder();
-
- IntPoint a, b, c;
-
- foreach (var t in mesh.Triangles)
- {
- a = Transform(t.GetVertex(0));
- b = Transform(t.GetVertex(1));
- c = Transform(t.GetVertex(2));
-
- eps.WriteLine("newpath");
-
- a = Transform((a.X + b.X + c.X) / 3.0, (a.Y + b.Y + c.Y) / 3.0);
-
- labels.AppendFormat(" {0} {1} moveto ({2}) show", a.X, a.Y, t.ID);
- labels.AppendLine();
-
- eps.WriteLine("stroke");
- }
-
- eps.WriteLine("%");
- eps.WriteLine("% Label the triangles.");
- eps.WriteLine("%");
- eps.WriteLine("% Set the RGB color to darker red.");
- eps.WriteLine("%");
- eps.WriteLine("0.950 0.250 0.150 setrgbcolor");
- eps.WriteLine("/Times-Roman findfont");
- eps.WriteLine("0.20 inch scalefont");
- eps.WriteLine("setfont");
- eps.WriteLine("%");
-
- eps.WriteLine(labels.ToString());
- }
- //*/
-
- private void SetColor(StreamWriter eps, float r, float g, float b)
- {
- eps.WriteLine("{0} {1} {2} setrgbcolor", r, g, b);
- eps.WriteLine("%");
- }
-
- private void SetStroke(StreamWriter eps, float r, float g, float b, float width)
- {
- eps.WriteLine("{0} {1} {2} setrgbcolor", r, g, b);
- eps.WriteLine("{0} setlinewidth", width);
- eps.WriteLine("%");
- }
-
- private IntPoint Transform(Point p)
- {
- return Transform(p.X, p.Y);
- }
-
- private IntPoint Transform(double x, double y)
- {
- return new IntPoint(
- (int)Math.Floor(((x_max - x) * x_ps_min + (x - x_min) * x_ps_max) / (x_max - x_min)),
- (int)Math.Floor(((y_max - y) * y_ps_min + (y - y_min) * y_ps_max) / (y_max - y_min))
- );
- }
-
- private void UpdateMetrics(Rectangle bounds)
- {
- 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;
- x_max = x_max + 0.05 * x_scale;
- x_min = x_min - 0.05 * x_scale;
- x_scale = x_max - x_min;
-
- // Enlarge height 5% on each side
- y_scale = y_max - y_min;
- y_max = y_max + 0.05 * y_scale;
- y_min = y_min - 0.05 * y_scale;
- y_scale = y_max - y_min;
-
- if (x_scale < y_scale)
- {
- int delta = (int)Math.Round((x_ps_max - x_ps_min) * (y_scale - x_scale) / (2.0 * y_scale));
-
- x_ps_max = x_ps_max - delta;
- x_ps_min = x_ps_min + delta;
-
- x_ps_max_clip = x_ps_max_clip - delta;
- x_ps_min_clip = x_ps_min_clip + delta;
-
- x_scale = y_scale;
- }
- else
- {
- int delta = (int)Math.Round((y_ps_max - y_ps_min) * (x_scale - y_scale) / (2.0 * x_scale));
-
- y_ps_max = y_ps_max - delta;
- y_ps_min = y_ps_min + delta;
-
- y_ps_max_clip = y_ps_max_clip - delta;
- y_ps_min_clip = y_ps_min_clip + delta;
-
- y_scale = x_scale;
- }
- }
-
- public IEnumerable EnumerateEdges(Mesh mesh, bool segments = false)
- {
- foreach (var t in mesh.Triangles)
- {
- for (int i = 0; i < 3; i++)
- {
- int nid = t.GetNeighborID(i);
-
- if ((t.ID < nid) || (nid < 0))
- {
- if (segments || t.GetSegment(i) == null)
- {
- yield return new Segment(
- t.GetVertex((i + 1) % 3),
- t.GetVertex((i + 2) % 3));
- }
- }
- }
- }
- }
- }
-}
diff --git a/Triangle.NET/TestApp/IO/FormattingStreamWriter.cs b/Triangle.NET/TestApp/IO/FormattingStreamWriter.cs
deleted file mode 100644
index 08af983..0000000
--- a/Triangle.NET/TestApp/IO/FormattingStreamWriter.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO;
-
-namespace MeshExplorer.IO
-{
- // From http://stackoverflow.com/questions/12011789/streamwriter-and-iformatprovider
-
- public class FormattingStreamWriter : StreamWriter
- {
- private readonly IFormatProvider formatProvider;
-
- public FormattingStreamWriter(string path)
- : this(path, CultureInfo.InvariantCulture)
- {
- }
-
- public FormattingStreamWriter(string path, IFormatProvider formatProvider)
- : base(path)
- {
- this.formatProvider = formatProvider;
- }
-
- public override IFormatProvider FormatProvider
- {
- get
- {
- return this.formatProvider;
- }
- }
- }
-}
diff --git a/Triangle.NET/TestApp/IO/RasterImage.cs b/Triangle.NET/TestApp/IO/RasterImage.cs
index 9715f43..9425ad3 100644
--- a/Triangle.NET/TestApp/IO/RasterImage.cs
+++ b/Triangle.NET/TestApp/IO/RasterImage.cs
@@ -7,12 +7,7 @@
namespace MeshExplorer.IO
{
using System;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Drawing.Imaging;
- using System.IO;
using TriangleNet;
- using TriangleNet.Rendering;
using TriangleNet.Rendering.GDI;
///
@@ -20,14 +15,6 @@ namespace MeshExplorer.IO
///
public class RasterImage
{
- ColorManager colors = RasterImage.LightScheme();
-
- public ColorManager ColorScheme
- {
- get { return colors; }
- set { colors = value; }
- }
-
///
/// Export the mesh to PNG format.
///
@@ -42,72 +29,7 @@ namespace MeshExplorer.IO
filename = String.Format("mesh-{0}.png", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss"));
}
- Bitmap bitmap;
-
- // Check if the specified width is reasonable
- if (width < 2 * Math.Sqrt(mesh.Vertices.Count))
- {
- bitmap = new Bitmap(400, 200);
- Graphics g = Graphics.FromImage(bitmap);
- g.Clear(Color.White);
-
- string message = String.Format("Sorry, I won't render {0} points on such a small image!", mesh.Vertices.Count);
-
- SizeF sz = g.MeasureString(message, SystemFonts.DefaultFont);
-
- g.SmoothingMode = SmoothingMode.AntiAlias;
- g.DrawString(message, SystemFonts.DefaultFont, Brushes.Black,
- 200 - sz.Width / 2, 100 - sz.Height / 2);
-
- g.Dispose();
- }
- else
- {
- 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);
-
- Graphics g = Graphics.FromImage(bitmap);
- g.Clear(colors.Background);
-
- g.SmoothingMode = SmoothingMode.HighQuality;
-
- var context = new RenderContext(new Projection(target), colors);
- context.Add(mesh, true);
-
- var renderer = new LayerRenderer();
- renderer.Context = context;
- renderer.RenderTarget = g;
- renderer.Render();
-
- g.Dispose();
- }
-
- if (Path.GetExtension(filename) != ".png")
- {
- filename += ".png";
- }
-
- bitmap.Save(filename, ImageFormat.Png);
- }
-
- 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(150, 150, 150));
- colors.Segment = new Pen(Color.SteelBlue);
- colors.VoronoiLine = new Pen(Color.FromArgb(160, 170, 180));
-
- return colors;
+ ImageRenderer.Save(mesh, filename, width);
}
}
}
diff --git a/Triangle.NET/TestApp/Mesh Explorer.csproj b/Triangle.NET/TestApp/Mesh Explorer.csproj
index d9b5218..842d083 100644
--- a/Triangle.NET/TestApp/Mesh Explorer.csproj
+++ b/Triangle.NET/TestApp/Mesh Explorer.csproj
@@ -109,15 +109,12 @@
-
-
-
diff --git a/Triangle.NET/Triangle.Rendering/Text/EpsDocument.cs b/Triangle.NET/Triangle.Rendering/Text/EpsDocument.cs
new file mode 100644
index 0000000..6c799ac
--- /dev/null
+++ b/Triangle.NET/Triangle.Rendering/Text/EpsDocument.cs
@@ -0,0 +1,197 @@
+
+namespace TriangleNet.Rendering.Text
+{
+ using System;
+ using System.Drawing;
+ using System.IO;
+
+ public class EpsDocument : IDisposable
+ {
+ // Constant to convert from millimeters to PostScript units (1/72th inch).
+ private const double UNITS_PER_MM = 72.0 / 25.4;
+
+ private FormattingStreamWriter _w;
+ private PageSize _size;
+
+ ///
+ /// Gets or sets the document name.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Gets or sets the default point size (default = 1).
+ ///
+ public int DefaultPointSize { get; set; }
+
+ public EpsDocument(string filename, PageSize pageSize)
+ : this(File.Create(filename), pageSize)
+ {
+ Name = Path.GetFileName(filename);
+ }
+
+ public EpsDocument(Stream stream, PageSize pageSize)
+ {
+ _w = new FormattingStreamWriter(stream);
+ _w.NewLine = "\n";
+
+ _size = pageSize;
+
+ DefaultPointSize = 1;
+ }
+
+ //public void Append(string ps)
+ //{
+ // _w.WriteLine(ps);
+ //}
+
+ public void DrawPoint(Point p)
+ {
+ _w.WriteLine("{0} {1} P", p.X, p.Y);
+ }
+
+ public void DrawLine(Point p1, Point p2)
+ {
+ _w.WriteLine("{0} {1} {2} {3} L", p1.X, p1.Y, p2.X, p2.Y);
+ }
+
+ public void DrawRectangle(Rectangle rect)
+ {
+ _w.WriteLine("newpath");
+ _w.WriteLine(" {0} {1} moveto", rect.X, rect.Y);
+ _w.WriteLine(" {0} {1} lineto", rect.Right, rect.Y);
+ _w.WriteLine(" {0} {1} lineto", rect.Right, rect.Bottom);
+ _w.WriteLine(" {0} {1} lineto", rect.X, rect.Bottom);
+ _w.WriteLine(" {0} {1} lineto", rect.X, rect.Y);
+ _w.WriteLine("stroke");
+
+ }
+
+ public void SetClip(Rectangle rect)
+ {
+ _w.WriteLine("newpath");
+ _w.WriteLine(" {0} {1} moveto", rect.X, rect.Y);
+ _w.WriteLine(" {0} {1} lineto", rect.Right, rect.Y);
+ _w.WriteLine(" {0} {1} lineto", rect.Right, rect.Bottom);
+ _w.WriteLine(" {0} {1} lineto", rect.X, rect.Bottom);
+ _w.WriteLine(" {0} {1} lineto", rect.X, rect.Y);
+ _w.WriteLine("clip newpath");
+ }
+
+ public void SetColor(Color color)
+ {
+ _w.WriteLine("{0:0.###} {1:0.###} {2:0.###} setrgbcolor",
+ ((float)color.R) / 255f,
+ ((float)color.G) / 255f,
+ ((float)color.B) / 255f);
+ }
+
+ public void SetStroke(float width)
+ {
+ _w.WriteLine("{0:0.###} setlinewidth", width);
+ }
+
+ public void SetStroke(float width, Color color)
+ {
+ SetColor(color);
+ SetStroke(width);
+ }
+
+ public void WriteHeader()
+ {
+ var x = _size.X; // * UNITS_PER_MM
+ var y = _size.Y;
+ var right = _size.Right;
+ var bottom = _size.Bottom;
+
+ // Write document header.
+
+ _w.WriteLine("%!PS-Adobe-3.0 EPSF-3.0");
+ _w.WriteLine("%%Creator: Triangle.NET");
+ _w.WriteLine("%%Title: {0}", Name);
+ _w.WriteLine("%%Pages: 1");
+ _w.WriteLine("%%BoundingBox: {0} {1} {2} {3}", (int)x, (int)y, (int)right, (int)bottom);
+ _w.WriteLine("%%HiResBoundingBox: {0:0.#####} {1:0.#####} {2:0.#####} {3:0.#####}", x, y, right, bottom);
+ _w.WriteLine("%%Document-Fonts: Times-Roman");
+ _w.WriteLine("%%LanguageLevel: 3");
+ _w.WriteLine("%%EndComments");
+ _w.WriteLine("%%BeginProlog");
+ _w.WriteLine("/inch {72 mul} def");
+ _w.WriteLine("%%EndProlog");
+ _w.WriteLine("%%Page: 1 1");
+ _w.WriteLine("save");
+
+ // Define points.
+ _w.WriteLine("/P {");
+ _w.WriteLine("2 dict begin");
+ _w.WriteLine("/y exch def");
+ _w.WriteLine("/x exch def");
+ _w.WriteLine("gsave");
+ _w.WriteLine("newpath x y {0} 0 360 arc fill", DefaultPointSize);
+ _w.WriteLine("grestore");
+ _w.WriteLine("end");
+ _w.WriteLine("} def");
+
+ // Define lines.
+ _w.WriteLine("/L {");
+ _w.WriteLine("2 dict begin");
+ _w.WriteLine("/y2 exch def");
+ _w.WriteLine("/x2 exch def");
+ _w.WriteLine("/y1 exch def");
+ _w.WriteLine("/x1 exch def");
+ _w.WriteLine("gsave");
+ _w.WriteLine("newpath x1 y1 moveto x2 y2 lineto stroke");
+ _w.WriteLine("grestore");
+ _w.WriteLine("end");
+ _w.WriteLine("} def");
+ }
+
+ private void Close()
+ {
+ _w.WriteLine("%");
+ _w.WriteLine("restore showpage");
+ _w.WriteLine("%");
+ _w.WriteLine("% End of page.");
+ _w.WriteLine("%");
+ _w.WriteLine("%%Trailer");
+ _w.WriteLine("%%EOF");
+ }
+
+ #region IDisposable implementation
+
+ // Has Dispose already been called?
+ bool disposed = false;
+
+ // Public implementation of Dispose pattern callable by consumers.
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ // Protected implementation of Dispose pattern.
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ Close();
+
+ if (disposing)
+ {
+ _w.Dispose();
+ _w = null;
+ }
+
+ // Free any unmanaged objects here.
+ //
+ disposed = true;
+ }
+
+ ~EpsDocument()
+ {
+ Dispose(false);
+ }
+
+ #endregion
+ }
+}
diff --git a/Triangle.NET/Triangle.Rendering/Text/EpsImage.cs b/Triangle.NET/Triangle.Rendering/Text/EpsImage.cs
new file mode 100644
index 0000000..28b133f
--- /dev/null
+++ b/Triangle.NET/Triangle.Rendering/Text/EpsImage.cs
@@ -0,0 +1,297 @@
+// -----------------------------------------------------------------------
+//
+// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/
+// Original Matlab code by John Burkardt, Florida State University
+//
+// -----------------------------------------------------------------------
+
+namespace TriangleNet.Rendering.Text
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using TriangleNet;
+ using TriangleNet.Geometry;
+
+ using Color = System.Drawing.Color;
+ using IntPoint = System.Drawing.Point;
+ using IntRectangle = System.Drawing.Rectangle;
+
+ ///
+ /// Writes a mesh to an EPS file.
+ ///
+ public class EpsImage
+ {
+ // EPS page metrics
+
+ PageSize ps = new PageSize(36, 126, 576, 666);
+ PageSize clip = new PageSize(18, 108, 594, 684);
+
+ // Mesh metrics
+ double x_max, x_min;
+ double y_max, y_min;
+ //double x_scale, y_scale;
+
+ // TODO: use color manager
+ private static Color ColorPoints = Color.FromArgb(0, 100, 0);
+ private static Color ColorLines = Color.FromArgb(150, 150, 150);
+ private static Color ColorSegments = Color.FromArgb(70, 130, 180);
+ private static Color ColorBorder = Color.FromArgb(230, 230, 230);
+
+ ///
+ /// Export the mesh to EPS format.
+ ///
+ /// The current mesh.
+ /// The EPS filename.
+ /// The desired width of the image (currently ignored).
+ public void Export(Mesh mesh, string filename, int width)
+ {
+ // Check file name
+ if (String.IsNullOrWhiteSpace(filename))
+ {
+ filename = String.Format("mesh-{0}.eps", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss"));
+ }
+
+ if (!filename.EndsWith(".eps"))
+ {
+ filename = Path.ChangeExtension(filename, ".eps");
+ }
+
+ UpdateMetrics(mesh.Bounds);
+
+ using (var eps = new EpsDocument(filename, ps))
+ {
+ int n = mesh.Vertices.Count;
+
+ // Size of the points.
+ eps.DefaultPointSize = (n < 100) ? 3 : ((n < 500) ? 2 : 1);
+
+ eps.WriteHeader();
+
+ // Draw a gray border around the page.
+ eps.SetColor(ColorBorder);
+ eps.DrawRectangle(GetRectangle(ps));
+
+ // Define a clipping polygon.
+ eps.SetClip(GetRectangle(clip));
+
+ // Draw edges.
+ eps.SetStroke(0.4f, ColorLines);
+
+ foreach (var e in EnumerateEdges(mesh))
+ {
+ eps.DrawLine(Transform(e.GetVertex(0)), Transform(e.GetVertex(1)));
+ }
+
+ // Draw Segments.
+ eps.SetStroke(0.8f, ColorSegments);
+
+ foreach (var s in mesh.Segments)
+ {
+ eps.DrawLine(Transform(s.GetVertex(0)), Transform(s.GetVertex(1)));
+ }
+
+ // Draw points.
+ eps.SetColor(ColorPoints);
+
+ foreach (var node in mesh.Vertices)
+ {
+ eps.DrawPoint(Transform(node));
+ }
+ }
+ }
+
+ /*
+ private void DrawTitle(EpsDocument eps)
+ {
+ var buffer = new StringBuilder();
+
+ buffer.AppendLine("%");
+ buffer.AppendLine("% Set the RGB color to black.");
+ buffer.AppendLine("%");
+ buffer.AppendLine("0.000 0.000 0.000 setrgbcolor");
+ buffer.AppendLine("%");
+ buffer.AppendLine("% Set the font and its size.");
+ buffer.AppendLine("%");
+ buffer.AppendLine("/Times-Roman findfont");
+ buffer.AppendLine("0.50 inch scalefont");
+ buffer.AppendLine("setfont");
+ buffer.AppendLine("%");
+ buffer.AppendLine("% Print a title.");
+ buffer.AppendLine("%");
+ buffer.AppendLine("%210 702 moveto");
+ buffer.AppendLine("%(Triangulation) show");
+ }
+
+ private void DrawPointLabels(StreamWriter eps, Mesh mesh)
+ {
+ int n = mesh.Vertices.Count;
+
+ IntPoint p;
+
+ StringBuilder labels = new StringBuilder();
+
+ foreach (var node in mesh.Vertices)
+ {
+ p = Transform(node);
+
+ labels.AppendFormat(" {0} {1} moveto ({2}) show", p.X, p.Y + 5, node.ID);
+ labels.AppendLine();
+ }
+
+ eps.WriteLine("%");
+ eps.WriteLine("% Label the nodes.");
+ eps.WriteLine("%");
+ eps.WriteLine("% Set the RGB color to darker blue.");
+ eps.WriteLine("%");
+ eps.WriteLine("0.000 0.250 0.850 setrgbcolor");
+ eps.WriteLine("/Times-Roman findfont");
+ eps.WriteLine("0.20 inch scalefont");
+ eps.WriteLine("setfont");
+ eps.WriteLine("%");
+
+ eps.WriteLine(labels.ToString());
+ }
+
+ private void DrawTriangles(StreamWriter eps, Mesh mesh, bool label)
+ {
+ eps.WriteLine("%");
+ eps.WriteLine("% Set the triangle line color and width.");
+ eps.WriteLine("%");
+ eps.WriteLine("0.6 0.6 0.6 setrgbcolor");
+ eps.WriteLine("0.5 setlinewidth");
+ eps.WriteLine("%");
+ eps.WriteLine("% Draw the triangles.");
+ eps.WriteLine("%");
+
+ IntPoint a, b, c;
+
+ foreach (var t in mesh.Triangles)
+ {
+ a = Transform(t.GetVertex(0));
+ b = Transform(t.GetVertex(1));
+ c = Transform(t.GetVertex(2));
+
+ eps.WriteLine("newpath");
+
+ eps.WriteLine(" {0} {1} moveto", a.X, a.Y);
+ eps.WriteLine(" {0} {1} lineto", b.X, b.Y);
+ eps.WriteLine(" {0} {1} lineto", c.X, c.Y);
+ eps.WriteLine(" {0} {1} lineto", a.X, a.Y);
+
+ eps.WriteLine("stroke");
+ }
+ }
+
+ private void DrawTriangleLabels(StreamWriter eps, Mesh mesh)
+ {
+ var labels = new StringBuilder();
+
+ IntPoint a, b, c;
+
+ foreach (var t in mesh.Triangles)
+ {
+ a = Transform(t.GetVertex(0));
+ b = Transform(t.GetVertex(1));
+ c = Transform(t.GetVertex(2));
+
+ eps.WriteLine("newpath");
+
+ a = Transform((a.X + b.X + c.X) / 3.0, (a.Y + b.Y + c.Y) / 3.0);
+
+ labels.AppendFormat(" {0} {1} moveto ({2}) show", a.X, a.Y, t.ID);
+ labels.AppendLine();
+
+ eps.WriteLine("stroke");
+ }
+
+ eps.WriteLine("%");
+ eps.WriteLine("% Label the triangles.");
+ eps.WriteLine("%");
+ eps.WriteLine("% Set the RGB color to darker red.");
+ eps.WriteLine("%");
+ eps.WriteLine("0.950 0.250 0.150 setrgbcolor");
+ eps.WriteLine("/Times-Roman findfont");
+ eps.WriteLine("0.20 inch scalefont");
+ eps.WriteLine("setfont");
+ eps.WriteLine("%");
+
+ eps.WriteLine(labels.ToString());
+ }
+ //*/
+
+ private IntRectangle GetRectangle(PageSize size)
+ {
+ return new IntRectangle((int)size.X, (int)size.Y, (int)size.Width, (int)size.Height);
+ }
+
+ private IntPoint Transform(Point p)
+ {
+ return Transform(p.X, p.Y);
+ }
+
+ private IntPoint Transform(double x, double y)
+ {
+ return new IntPoint(
+ (int)Math.Floor(((x_max - x) * ps.X + (x - x_min) * ps.Right) / (x_max - x_min)),
+ (int)Math.Floor(((y_max - y) * ps.Y + (y - y_min) * ps.Bottom) / (y_max - y_min))
+ );
+ }
+
+ private void UpdateMetrics(Rectangle bounds)
+ {
+ x_max = bounds.Right;
+ x_min = bounds.Left;
+ y_max = bounds.Top;
+ y_min = bounds.Bottom;
+
+ // Enlarge width 5% on each side
+ double x_scale = x_max - x_min;
+ x_max = x_max + 0.05 * x_scale;
+ x_min = x_min - 0.05 * x_scale;
+ x_scale = x_max - x_min;
+
+ // Enlarge height 5% on each side
+ double y_scale = y_max - y_min;
+ y_max = y_max + 0.05 * y_scale;
+ y_min = y_min - 0.05 * y_scale;
+ y_scale = y_max - y_min;
+
+ if (x_scale < y_scale)
+ {
+ int delta = (int)Math.Round((ps.Right - ps.X) * (y_scale - x_scale) / (2.0 * y_scale));
+
+ ps.Expand(-delta, 0);
+ clip.Expand(-delta, 0);
+ }
+ else
+ {
+ int delta = (int)Math.Round((ps.Bottom - ps.Y) * (x_scale - y_scale) / (2.0 * x_scale));
+
+ ps.Expand(0, -delta);
+ clip.Expand(0, -delta);
+ }
+ }
+
+ public IEnumerable EnumerateEdges(Mesh mesh, bool segments = false)
+ {
+ foreach (var t in mesh.Triangles)
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ int nid = t.GetNeighborID(i);
+
+ if ((t.ID < nid) || (nid < 0))
+ {
+ if (segments || t.GetSegment(i) == null)
+ {
+ yield return new Segment(
+ t.GetVertex((i + 1) % 3),
+ t.GetVertex((i + 2) % 3));
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Triangle.NET/Triangle.Rendering/Text/FormattingStreamWriter.cs b/Triangle.NET/Triangle.Rendering/Text/FormattingStreamWriter.cs
new file mode 100644
index 0000000..b3cdd1d
--- /dev/null
+++ b/Triangle.NET/Triangle.Rendering/Text/FormattingStreamWriter.cs
@@ -0,0 +1,73 @@
+
+namespace TriangleNet.Rendering.Text
+{
+ using System;
+ using System.Globalization;
+ using System.IO;
+
+ ///
+ ///
+ ///
+ ///
+ /// From http://stackoverflow.com/questions/12011789/streamwriter-and-iformatprovider
+ ///
+ public class FormattingStreamWriter : StreamWriter
+ {
+ private readonly IFormatProvider formatProvider;
+
+ ///
+ /// Initializes a new instance of the StreamWriter class for the specified file
+ /// by using the default encoding and buffer size.
+ ///
+ /// The complete file path to write to.
+ public FormattingStreamWriter(string path)
+ : this(path, CultureInfo.InvariantCulture)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the StreamWriter class for the specified stream
+ /// by using UTF-8 encoding and the default buffer size.
+ ///
+ /// The stream to write to.
+ public FormattingStreamWriter(Stream stream)
+ : this(stream, CultureInfo.InvariantCulture)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the StreamWriter class for the specified file
+ /// by using the default encoding and buffer size.
+ ///
+ /// The complete file path to write to.
+ /// The format provider.
+ public FormattingStreamWriter(string path, IFormatProvider formatProvider)
+ : base(path)
+ {
+ this.formatProvider = formatProvider;
+ }
+
+ ///
+ /// Initializes a new instance of the StreamWriter class for the specified stream
+ /// by using UTF-8 encoding and the default buffer size.
+ ///
+ /// The stream to write to.
+ /// The format provider.
+ public FormattingStreamWriter(Stream stream, IFormatProvider formatProvider)
+ : base(stream)
+ {
+ this.formatProvider = formatProvider;
+ }
+
+ ///
+ /// Gets an object that controls formatting.
+ ///
+ public override IFormatProvider FormatProvider
+ {
+ get
+ {
+ return this.formatProvider;
+ }
+ }
+ }
+}
diff --git a/Triangle.NET/Triangle.Rendering/Text/PageSize.cs b/Triangle.NET/Triangle.Rendering/Text/PageSize.cs
new file mode 100644
index 0000000..9d2f5c9
--- /dev/null
+++ b/Triangle.NET/Triangle.Rendering/Text/PageSize.cs
@@ -0,0 +1,81 @@
+
+namespace TriangleNet.Rendering.Text
+{
+ using System.Drawing;
+
+ ///
+ /// Page size in millimeters.
+ ///
+ public struct PageSize
+ {
+ private const float MM_PER_INCH = 2.54f;
+
+ public static readonly PageSize A3 = new PageSize(297.0f, 420.0f);
+ public static readonly PageSize A4 = new PageSize(210.0f, 297.0f);
+ public static readonly PageSize A5 = new PageSize(148.0f, 210.0f);
+ public static readonly PageSize LETTER = new PageSize(8.5f * MM_PER_INCH, 11.0f * MM_PER_INCH);
+ public static readonly PageSize LEGAL = new PageSize(8.5f * MM_PER_INCH, 14.0f * MM_PER_INCH);
+
+ private float left;
+ private float top;
+ private float right;
+ private float bottom;
+
+ public float X
+ {
+ get { return left; }
+ }
+
+ public float Y
+ {
+ get { return top; }
+ }
+
+ public float Width
+ {
+ get { return right - left; }
+ }
+
+ public float Height
+ {
+ get { return bottom - top; }
+ }
+
+ public float Right
+ {
+ get { return right; }
+ }
+
+ public float Bottom
+ {
+ get { return bottom; }
+ }
+
+ public PageSize(float left, float top, float right, float bottom)
+ {
+ this.left = left;
+ this.top = top;
+ this.right = right;
+ this.bottom = bottom;
+ }
+
+ public PageSize(float width, float height)
+ : this(0.0f, 0.0f, width, height)
+ {
+ }
+
+ public PageSize(Rectangle size)
+ : this(size.Left, size.Right, size.Top, size.Bottom)
+ {
+ }
+
+ public void Expand(float dx, float dy)
+ {
+ left -= dx;
+ top -= dy;
+
+ right += dx;
+ bottom += dy;
+ }
+ }
+}
diff --git a/Triangle.NET/TestApp/IO/SvgImage.cs b/Triangle.NET/Triangle.Rendering/Text/SvgImage.cs
similarity index 80%
rename from Triangle.NET/TestApp/IO/SvgImage.cs
rename to Triangle.NET/Triangle.Rendering/Text/SvgImage.cs
index 86353f2..b7bb500 100644
--- a/Triangle.NET/TestApp/IO/SvgImage.cs
+++ b/Triangle.NET/Triangle.Rendering/Text/SvgImage.cs
@@ -4,7 +4,7 @@
//
// -----------------------------------------------------------------------
-namespace MeshExplorer.IO
+namespace TriangleNet.Rendering.Text
{
using System;
using System.IO;
@@ -54,7 +54,7 @@ namespace MeshExplorer.IO
int height = (int)((bounds.Height + 2 * margin) * scale);
- using (StreamWriter svg = new StreamWriter(filename))
+ using (var svg = new FormattingStreamWriter(filename))
{
svg.WriteLine("