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("{2}", - xa.ToString("0.0", Util.Nfi), ya.ToString("0.0", Util.Nfi), tri.ID); + labels.AppendFormat("{2}", + xa, ya, tri.ID); labels.AppendLine(); } @@ -139,9 +137,8 @@ namespace MeshExplorer.IO x2 = scale * seg.GetVertex(1).X; y2 = scale * seg.GetVertex(1).Y; - svg.Write("M {0},{1} L {2},{3} ", - x1.ToString("0.0", Util.Nfi), y1.ToString("0.0", Util.Nfi), - x2.ToString("0.0", Util.Nfi), y2.ToString("0.0", Util.Nfi)); + svg.Write("M {0:0.#},{1:0.#} L {2:0.#},{3:0.#} ", + x1, y1, x2, y2); } svg.WriteLine("\" style=\"stroke:#4682B4; fill:none; stroke-linejoin:bevel; stroke-width:2px;\"/>"); @@ -177,13 +174,13 @@ namespace MeshExplorer.IO x = scale * node.X; y = scale * node.Y; - svg.WriteLine(" ", - x.ToString("0.0", Util.Nfi), y.ToString("0.0", Util.Nfi), circle_size); + svg.WriteLine(" ", + x, y, circle_size); if (label) { - labels.AppendFormat("{2}", - x.ToString("0.0", Util.Nfi), y.ToString("0.0", Util.Nfi), node.ID); + labels.AppendFormat("{2}", + x, y, node.ID); labels.AppendLine(); } } diff --git a/Triangle.NET/Triangle.Rendering/Triangle.Rendering.csproj b/Triangle.NET/Triangle.Rendering/Triangle.Rendering.csproj index f9ad27f..93ee2f2 100644 --- a/Triangle.NET/Triangle.Rendering/Triangle.Rendering.csproj +++ b/Triangle.NET/Triangle.Rendering/Triangle.Rendering.csproj @@ -70,6 +70,11 @@ + + + + +