diff --git a/Triangle.NET/TestApp/Controls/DarkTextBox.cs b/Triangle.NET/TestApp/Controls/DarkTextBox.cs index f92cc78..4d68107 100644 --- a/Triangle.NET/TestApp/Controls/DarkTextBox.cs +++ b/Triangle.NET/TestApp/Controls/DarkTextBox.cs @@ -53,11 +53,11 @@ namespace MeshExplorer.Controls // this.textBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); this.textBox.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.textBox.Location = new System.Drawing.Point(3, 2); + this.textBox.Location = new System.Drawing.Point(4, 2); this.textBox.Name = "textBox"; this.textBox.TabIndex = 0; // - // TextBoxDark + // DarkTextBox // this.BackColor = System.Drawing.Color.White; this.Controls.Add(this.textBox); @@ -83,40 +83,49 @@ namespace MeshExplorer.Controls }; textBox.Font = this.Font; - textBox.Location = new Point(3, (this.Height - textBox.Height) / 2 + 1); + textBox.Location = new Point(4, (this.Height - textBox.Height) / 2); textBox.Width = this.Width - 8; - textBox.TextAlign = HorizontalAlignment.Right; + textBox.TextAlign = HorizontalAlignment.Left; textBox.ForeColor = this.ForeColor; - textBox.MaxLength = 6; + //textBox.MaxLength = 6; textBox.GotFocus += delegate(object sender, EventArgs e) { - textBox.ForeColor = Color.White; + textBox.ForeColor = this.ForeColor; }; + textBox.LostFocus += delegate(object sender, EventArgs e) { - textBox.ForeColor = this.ForeColor; + textBox.ForeColor = ColorScheme.ColorGray68; }; } protected override void OnPaint(PaintEventArgs e) { + Graphics g = e.Graphics; + Rectangle rect = this.ClientRectangle; - Brush brushOuter = new LinearGradientBrush(rect, Color.FromArgb(82, 82, 82), Color.FromArgb(96, 96, 96), - LinearGradientMode.Vertical); + //Brush brushOuter = new LinearGradientBrush(rect, Color.FromArgb(82, 82, 82), + // Color.FromArgb(96, 96, 96), LinearGradientMode.Vertical); - Pen brushBorder = new Pen(Color.FromArgb(38, 38, 38), 1f); + Pen borderTop = new Pen(Color.FromArgb(76, 76, 76), 1f); + Pen borderBottom = new Pen(Color.FromArgb(128, 128, 128), 1f); - e.Graphics.FillRectangle(brushOuter, rect); + //e.Graphics.FillRectangle(brushOuter, rect); rect = new Rectangle(1, 1, this.Width - 3, this.Height - 3); - e.Graphics.FillRectangle(new SolidBrush(this.BackColor), rect); + g.FillRectangle(new SolidBrush(this.BackColor), rect); - e.Graphics.DrawRectangle(brushBorder, rect); + g.DrawLine(borderTop, 0, 0, this.Width - 1, 0); + g.DrawLine(borderTop, 0, 0, 0, this.Height - 1); + g.DrawLine(borderBottom, 1, this.Height - 1, this.Width - 1, this.Height - 1); + g.DrawLine(borderBottom, this.Width - 1, this.Height - 1, this.Width - 1, this.Height - 1); - brushOuter.Dispose(); - brushBorder.Dispose(); + + //brushOuter.Dispose(); + borderTop.Dispose(); + borderBottom.Dispose(); base.OnPaint(e); } diff --git a/Triangle.NET/TestApp/Controls/RendererControl.cs b/Triangle.NET/TestApp/Controls/RendererControl.cs index c15a8b6..6f75362 100644 --- a/Triangle.NET/TestApp/Controls/RendererControl.cs +++ b/Triangle.NET/TestApp/Controls/RendererControl.cs @@ -63,7 +63,7 @@ namespace MeshExplorer.Controls { SetStyle(ControlStyles.ResizeRedraw, true); - renderColors = RenderColors.Default; + renderColors = RenderColors.Default(); this.BackColor = renderColors.Background; diff --git a/Triangle.NET/TestApp/Examples.cs b/Triangle.NET/TestApp/Examples.cs index e2802b4..182c881 100644 --- a/Triangle.NET/TestApp/Examples.cs +++ b/Triangle.NET/TestApp/Examples.cs @@ -14,6 +14,7 @@ namespace MeshExplorer using TriangleNet.IO; using TriangleNet.Geometry; using MeshExplorer.IO; + using MeshExplorer.Rendering; /// /// Code of the online examples. @@ -23,14 +24,14 @@ namespace MeshExplorer // Make sure this path points to the polygon sample data. static readonly string pathToData = @"..\..\..\Data\"; - static ImageWriter imageWriter = new ImageWriter(); + static RasterImage imageWriter = new RasterImage(); /// /// Generating Delaunay triangulations /// public static void Example1() { - imageWriter.SetColorSchemeLight(); + imageWriter.ColorScheme = RenderColors.LightScheme(); // Create a mesh instance. Mesh mesh = new Mesh(); @@ -38,18 +39,18 @@ namespace MeshExplorer // Read spiral node file and gernerate the delaunay triangulation // of the point set. mesh.Triangulate(pathToData + "spiral.node"); - imageWriter.WritePng(mesh, "spiral.png", 180); + imageWriter.Export(mesh, "spiral.png", 180); // Read face polygon file and gernerate the delaunay triangulation // of the PSLG. We reuse the mesh instance here. InputGeometry data = FileReader.Read(pathToData + "face.poly"); mesh.Triangulate(data); - imageWriter.WritePng(mesh, "face.png", 200); + imageWriter.Export(mesh, "face.png", 200); // Generate a conforming delaunay triangulation of the face polygon. mesh.SetOption(Options.ConformingDelaunay, true); mesh.Triangulate(data); - imageWriter.WritePng(mesh, "face-CDT.png", 200); + imageWriter.Export(mesh, "face-CDT.png", 200); } /// @@ -57,7 +58,7 @@ namespace MeshExplorer /// public static void Example2() { - imageWriter.SetColorSchemeLight(); + imageWriter.ColorScheme = RenderColors.LightScheme(); // Create a mesh instance. Mesh mesh = new Mesh(); @@ -68,18 +69,18 @@ namespace MeshExplorer InputGeometry data = FileReader.ReadNodeFile(pathToData + "spiral.node"); mesh.SetOption(Options.Quality, true); mesh.Triangulate(data); - imageWriter.WritePng(mesh, "spiral-Angle-20.png", 200); + imageWriter.Export(mesh, "spiral-Angle-20.png", 200); // Set a minimum angle of 30 degrees. mesh.SetOption(Options.MinAngle, 35); mesh.Triangulate(data); - imageWriter.WritePng(mesh, "spiral-Angle-35.png", 200); + imageWriter.Export(mesh, "spiral-Angle-35.png", 200); // Reset the minimum angle and add a global area constraint. mesh.SetOption(Options.MinAngle, 20); mesh.SetOption(Options.MaxArea, 0.2); mesh.Triangulate(data); - imageWriter.WritePng(mesh, "spiral-Area.png", 200); + imageWriter.Export(mesh, "spiral-Area.png", 200); } /// @@ -87,7 +88,7 @@ namespace MeshExplorer /// public static void Example3() { - imageWriter.SetColorSchemeLight(); + imageWriter.ColorScheme = RenderColors.LightScheme(); // Create a mesh instance. Mesh mesh = new Mesh(); @@ -97,7 +98,7 @@ namespace MeshExplorer mesh.SetOption(Options.Quality, true); mesh.SetOption(Options.Convex, true); mesh.Triangulate(pathToData + "box.poly"); - imageWriter.WritePng(mesh, "box.png", 200); + imageWriter.Export(mesh, "box.png", 200); // Save the current mesh to .node and .ele files FileWriter.WriteNodes(mesh, "box.1.node"); @@ -105,11 +106,11 @@ namespace MeshExplorer // Refine the mesh by setting a global area constraint. mesh.Refine(0.2); - imageWriter.WritePng(mesh, "box-Refine-1.png", 200); + imageWriter.Export(mesh, "box-Refine-1.png", 200); // Refine again by setting a smaller area constraint. mesh.Refine(0.05); - imageWriter.WritePng(mesh, "box-Refine-2.png", 200); + imageWriter.Export(mesh, "box-Refine-2.png", 200); // Load the previously saved box.1 mesh. Since a box.1.area // file exist, the variable area constraint option is set @@ -117,7 +118,7 @@ namespace MeshExplorer mesh.Load(pathToData + "box.1.node"); mesh.SetOption(Options.MinAngle, 0); mesh.Refine(); - imageWriter.WritePng(mesh, "box-Refine-3.png", 200); + imageWriter.Export(mesh, "box-Refine-3.png", 200); } /// diff --git a/Triangle.NET/TestApp/FormExport.Designer.cs b/Triangle.NET/TestApp/FormExport.Designer.cs index 3f2efcf..929edf5 100644 --- a/Triangle.NET/TestApp/FormExport.Designer.cs +++ b/Triangle.NET/TestApp/FormExport.Designer.cs @@ -28,26 +28,105 @@ /// private void InitializeComponent() { + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); + this.lbSize = new System.Windows.Forms.Label(); + this.darkSlider1 = new MeshExplorer.Controls.DarkSlider(); + this.darkTextBox1 = new MeshExplorer.Controls.DarkTextBox(); + this.darkListBox1 = new MeshExplorer.Controls.DarkListBox(); this.darkButton1 = new MeshExplorer.Controls.DarkButton(); this.btnExport = new MeshExplorer.Controls.DarkButton(); this.SuspendLayout(); // + // label2 + // + this.label2.AutoSize = true; + this.label2.BackColor = System.Drawing.Color.Transparent; + this.label2.Location = new System.Drawing.Point(12, 9); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(65, 13); + this.label2.TabIndex = 2; + this.label2.Text = "File fromat:\r\n"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.BackColor = System.Drawing.Color.Transparent; + this.label3.Location = new System.Drawing.Point(12, 198); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(59, 13); + this.label3.TabIndex = 2; + this.label3.Text = "File name:"; + // // label1 // this.label1.AutoSize = true; this.label1.BackColor = System.Drawing.Color.Transparent; - this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Location = new System.Drawing.Point(12, 156); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(302, 39); + this.label1.Size = new System.Drawing.Size(74, 13); this.label1.TabIndex = 2; - this.label1.Text = "No options available at the moment.\r\n\r\nYou will find the saved image in the appli" + - "cation directory."; + this.label1.Text = "Image width:"; + // + // lbSize + // + this.lbSize.AutoSize = true; + this.lbSize.BackColor = System.Drawing.Color.Transparent; + this.lbSize.ForeColor = System.Drawing.Color.Gray; + this.lbSize.Location = new System.Drawing.Point(272, 156); + this.lbSize.Name = "lbSize"; + this.lbSize.Size = new System.Drawing.Size(40, 13); + this.lbSize.TabIndex = 2; + this.lbSize.Text = "800 px"; + // + // darkSlider1 + // + this.darkSlider1.BackColor = System.Drawing.Color.Transparent; + this.darkSlider1.CriticalPercent = ((uint)(0u)); + this.darkSlider1.Location = new System.Drawing.Point(15, 169); + this.darkSlider1.Maximum = 100; + this.darkSlider1.Minimum = 0; + this.darkSlider1.Name = "darkSlider1"; + this.darkSlider1.Size = new System.Drawing.Size(297, 17); + this.darkSlider1.TabIndex = 5; + this.darkSlider1.Text = "darkSlider1"; + this.darkSlider1.Value = 35; + this.darkSlider1.ValueChanging += new System.EventHandler(this.darkSlider1_ValueChanging); + // + // darkTextBox1 + // + this.darkTextBox1.BackColor = System.Drawing.Color.White; + this.darkTextBox1.Cursor = System.Windows.Forms.Cursors.IBeam; + this.darkTextBox1.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.darkTextBox1.ForeColor = System.Drawing.Color.Black; + this.darkTextBox1.Location = new System.Drawing.Point(12, 214); + this.darkTextBox1.Name = "darkTextBox1"; + this.darkTextBox1.Size = new System.Drawing.Size(300, 21); + this.darkTextBox1.TabIndex = 4; + this.darkTextBox1.TextAlign = System.Windows.Forms.HorizontalAlignment.Left; + // + // darkListBox1 + // + this.darkListBox1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(96)))), ((int)(((byte)(96)))), ((int)(((byte)(96))))); + this.darkListBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.darkListBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable; + this.darkListBox1.FormattingEnabled = true; + this.darkListBox1.ItemHeight = 22; + this.darkListBox1.Items.AddRange(new object[] { + "Portable Network Graphics (*.png)", + "Encapsulated PostScript (*.eps)", + "Scalable Vector Graphics (*.svg)"}); + this.darkListBox1.Location = new System.Drawing.Point(12, 25); + this.darkListBox1.Name = "darkListBox1"; + this.darkListBox1.Size = new System.Drawing.Size(302, 118); + this.darkListBox1.TabIndex = 3; + this.darkListBox1.SelectedIndexChanged += new System.EventHandler(this.darkListBox1_SelectedIndexChanged); // // darkButton1 // this.darkButton1.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.darkButton1.Location = new System.Drawing.Point(162, 141); + this.darkButton1.Location = new System.Drawing.Point(144, 270); this.darkButton1.Name = "darkButton1"; this.darkButton1.Size = new System.Drawing.Size(82, 23); this.darkButton1.TabIndex = 1; @@ -57,7 +136,7 @@ // btnExport // this.btnExport.DialogResult = System.Windows.Forms.DialogResult.OK; - this.btnExport.Location = new System.Drawing.Point(250, 141); + this.btnExport.Location = new System.Drawing.Point(232, 270); this.btnExport.Name = "btnExport"; this.btnExport.Size = new System.Drawing.Size(82, 23); this.btnExport.TabIndex = 0; @@ -69,8 +148,14 @@ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76))))); - this.ClientSize = new System.Drawing.Size(344, 172); + this.ClientSize = new System.Drawing.Size(324, 299); + this.Controls.Add(this.darkSlider1); + this.Controls.Add(this.darkTextBox1); + this.Controls.Add(this.darkListBox1); + this.Controls.Add(this.label2); + this.Controls.Add(this.lbSize); this.Controls.Add(this.label1); + this.Controls.Add(this.label3); this.Controls.Add(this.darkButton1); this.Controls.Add(this.btnExport); this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); @@ -91,6 +176,12 @@ private Controls.DarkButton btnExport; private Controls.DarkButton darkButton1; + private Controls.DarkListBox darkListBox1; + private System.Windows.Forms.Label label2; + private Controls.DarkTextBox darkTextBox1; + private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label1; + private Controls.DarkSlider darkSlider1; + private System.Windows.Forms.Label lbSize; } } \ No newline at end of file diff --git a/Triangle.NET/TestApp/FormExport.cs b/Triangle.NET/TestApp/FormExport.cs index ae52c26..37fb1cb 100644 --- a/Triangle.NET/TestApp/FormExport.cs +++ b/Triangle.NET/TestApp/FormExport.cs @@ -6,6 +6,7 @@ using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; +using System.IO; namespace MeshExplorer { @@ -16,6 +17,28 @@ namespace MeshExplorer InitializeComponent(); } + public int ImageFormat + { + get { return darkListBox1.SelectedIndex; } + } + + public int ImageSize + { + get + { + string s = lbSize.Text; + s = s.Substring(0, s.Length - 3); + int size = 0; + int.TryParse(s, out size); + return size; + } + } + + public string ImageName + { + get { return darkTextBox1.Text; } + } + protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); @@ -25,5 +48,35 @@ namespace MeshExplorer e.Graphics.FillRectangle(Brushes.DimGray, rect); } + + private void darkListBox1_SelectedIndexChanged(object sender, EventArgs e) + { + string filename = darkTextBox1.Text; + + if (!String.IsNullOrWhiteSpace(filename)) + { + string ext = ".png"; + + if (darkListBox1.SelectedIndex == 1) + { + ext = ".eps"; + } + else if (darkListBox1.SelectedIndex == 2) + { + ext = ".svg"; + } + + darkTextBox1.Text = Path.ChangeExtension(filename, ext); + } + } + + private void darkSlider1_ValueChanging(object sender, EventArgs e) + { + int size = (int)((2000.0 - 200.0) / 100.0 * darkSlider1.Value + 200.0); + + size = size - (size % 50); + + lbSize.Text = size + " px"; + } } } diff --git a/Triangle.NET/TestApp/FormMain.cs b/Triangle.NET/TestApp/FormMain.cs index 74d796a..89557e9 100644 --- a/Triangle.NET/TestApp/FormMain.cs +++ b/Triangle.NET/TestApp/FormMain.cs @@ -9,6 +9,7 @@ using MeshExplorer.IO; using TriangleNet; using TriangleNet.Geometry; using TriangleNet.Tools; +using MeshExplorer.Rendering; namespace MeshExplorer { @@ -319,21 +320,25 @@ namespace MeshExplorer HandleMeshImport(); } // else Message + + // Update folder settings + settings.OfdFilterIndex = ofd.FilterIndex; + settings.OfdDirectory = Path.GetFullPath(ofd.FileName); + + return; } } - else + + input = FileProcessor.Read(ofd.FileName); + + if (input != null) { - input = FileProcessor.Read(ofd.FileName); + // Update settings + settings.CurrentFile = Path.GetFileName(ofd.FileName); - if (input != null) - { - // Update settings - settings.CurrentFile = Path.GetFileName(ofd.FileName); - - HandleNewInput(); - } - // else Message + HandleNewInput(); } + // else Message // Update folder settings settings.OfdFilterIndex = ofd.FilterIndex; @@ -588,8 +593,25 @@ namespace MeshExplorer if (export.ShowDialog() == DialogResult.OK) { - ImageWriter imgWriter = new ImageWriter(); - imgWriter.WritePng(this.mesh); + int format = export.ImageFormat; + int size = export.ImageSize; + + if (format == 1) + { + EpsImage eps = new EpsImage(); + eps.Export(this.mesh, export.ImageName, size); + } + else if (format == 2) + { + SvgImage svg = new SvgImage(); + svg.Export(this.mesh, export.ImageName, size); + } + else + { + RasterImage img = new RasterImage(); + img.ColorScheme = RenderColors.LightScheme(); + img.Export(this.mesh, export.ImageName, size); + } } } } diff --git a/Triangle.NET/TestApp/IO/EpsImage.cs b/Triangle.NET/TestApp/IO/EpsImage.cs new file mode 100644 index 0000000..5544742 --- /dev/null +++ b/Triangle.NET/TestApp/IO/EpsImage.cs @@ -0,0 +1,358 @@ +// ----------------------------------------------------------------------- +// +// John Burkardt, Florida State University +// http://people.sc.fsu.edu/~jburkardt/m_src/fem2d_poisson/ +// +// ----------------------------------------------------------------------- + +namespace MeshExplorer.IO +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using MeshExplorer.Rendering; + using System.IO; + using TriangleNet; + using TriangleNet.Geometry; + + /// + /// 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; + + 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 (StreamWriter eps = new StreamWriter(filename)) + { + WriteHeader(filename, eps); + + DrawClip(eps); + + DrawTriangles(eps, mesh, false); + + DrawSegments(eps, mesh); + + DrawPoints(eps, mesh, false); + + 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: 1"); + 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 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("%"); + + StringBuilder labels = new StringBuilder(); + + double x1, y1, x2, y2, x3, y3, xa, ya; + int x_ps, y_ps; + + foreach (var tri in mesh.Triangles) + { + eps.WriteLine("newpath"); + + x1 = tri[0].X; y1 = tri[0].Y; + x2 = tri[1].X; y2 = tri[1].Y; + x3 = tri[2].X; y3 = tri[2].Y; + + x_ps = (int)Math.Floor(((x_max - x1) * x_ps_min + (x1 - x_min) * x_ps_max) / (x_max - x_min)); + y_ps = (int)Math.Floor(((y_max - y1) * y_ps_min + (y1 - y_min) * y_ps_max) / (y_max - y_min)); + eps.WriteLine(" {0} {1} moveto", x_ps, y_ps); + + x_ps = (int)Math.Floor(((x_max - x2) * x_ps_min + (x2 - x_min) * x_ps_max) / (x_max - x_min)); + y_ps = (int)Math.Floor(((y_max - y2) * y_ps_min + (y2 - y_min) * y_ps_max) / (y_max - y_min)); + eps.WriteLine(" {0} {1} lineto", x_ps, y_ps); + + x_ps = (int)Math.Floor(((x_max - x3) * x_ps_min + (x3 - x_min) * x_ps_max) / (x_max - x_min)); + y_ps = (int)Math.Floor(((y_max - y3) * y_ps_min + (y3 - y_min) * y_ps_max) / (y_max - y_min)); + eps.WriteLine(" {0} {1} lineto", x_ps, y_ps); + + x_ps = (int)Math.Floor(((x_max - x1) * x_ps_min + (x1 - x_min) * x_ps_max) / (x_max - x_min)); + y_ps = (int)Math.Floor(((y_max - y1) * y_ps_min + (y1 - y_min) * y_ps_max) / (y_max - y_min)); + eps.WriteLine(" {0} {1} lineto", x_ps, y_ps); + + if (label) + { + xa = (x1 + x2 + x3) / 3.0; + ya = (y1 + y2 + y3) / 3.0; + + x_ps = (int)Math.Floor(((x_max - xa) * x_ps_min + (xa - x_min) * x_ps_max) / (x_max - x_min)); + y_ps = (int)Math.Floor(((y_max - ya) * y_ps_min + (ya - y_min) * y_ps_max) / (y_max - y_min)); + + labels.AppendFormat(" {0} {1} moveto ({2}) show", x_ps, y_ps, tri.ID); + labels.AppendLine(); + } + + eps.WriteLine("stroke"); + } + + // Label the triangles. + if (label) + { + 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.Write(labels.ToString()); + } + } + + private void DrawSegments(StreamWriter eps, Mesh mesh) + { + eps.WriteLine("%"); + eps.WriteLine("% Set the triangle line color and width."); + eps.WriteLine("%"); + eps.WriteLine("0.27 0.5 0.7 setrgbcolor"); + eps.WriteLine("0.75 setlinewidth"); + eps.WriteLine("%"); + eps.WriteLine("% Draw the triangles."); + eps.WriteLine("%"); + + StringBuilder labels = new StringBuilder(); + + double x1, y1, x2, y2; + int x_ps, y_ps; + + foreach (var seg in mesh.Segments) + { + eps.WriteLine("newpath"); + + x1 = seg[0].X; y1 = seg[0].Y; + x2 = seg[1].X; y2 = seg[1].Y; + + x_ps = (int)Math.Floor(((x_max - x1) * x_ps_min + (x1 - x_min) * x_ps_max) / (x_max - x_min)); + y_ps = (int)Math.Floor(((y_max - y1) * y_ps_min + (y1 - y_min) * y_ps_max) / (y_max - y_min)); + eps.WriteLine(" {0} {1} moveto", x_ps, y_ps); + + x_ps = (int)Math.Floor(((x_max - x2) * x_ps_min + (x2 - x_min) * x_ps_max) / (x_max - x_min)); + y_ps = (int)Math.Floor(((y_max - y2) * y_ps_min + (y2 - y_min) * y_ps_max) / (y_max - y_min)); + eps.WriteLine(" {0} {1} lineto", x_ps, y_ps); + + + eps.WriteLine("stroke"); + } + } + + private void DrawPoints(StreamWriter eps, Mesh mesh, bool label) + { + int n = mesh.NumberOfVertices; + + int circle_size = 1; + + if (n < 100) + { + circle_size = 3; + } + else if (n < 500) + { + circle_size = 2; + } + + eps.WriteLine("%"); + eps.WriteLine("% Draw filled dots at the nodes."); + eps.WriteLine("%"); + eps.WriteLine("% Set the RGB color to blue."); + eps.WriteLine("%"); + eps.WriteLine("0.0 0.4 0.0 setrgbcolor"); + eps.WriteLine("%"); + + double x, y; + int x_ps, y_ps; + + StringBuilder labels = new StringBuilder(); + + foreach (var node in mesh.Vertices) + { + x = node.X; + y = node.Y; + + x_ps = (int)Math.Floor(((x_max - x) * x_ps_min + (x - x_min) * x_ps_max) / (x_max - x_min)); + y_ps = (int)Math.Floor(((y_max - y) * y_ps_min + (y - y_min) * y_ps_max) / (y_max - y_min)); + + eps.WriteLine(" newpath {0} {1} {2} 0 360 arc closepath fill", x_ps, y_ps, circle_size); + + if (label) + { + labels.AppendFormat(" {0} {1} moveto ({2}) show", x_ps, y_ps + 5, node); + labels.AppendLine(); + } + } + + // Label the nodes. + if (label) + { + 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.Write(labels.ToString()); + } + } + + private void UpdateMetrics(BoundingBox bounds) + { + x_max = bounds.Xmax; + x_min = bounds.Xmin; + y_max = bounds.Ymax; + y_min = bounds.Ymin; + + // 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; + } + } + } +} diff --git a/Triangle.NET/TestApp/IO/ImageWriter.cs b/Triangle.NET/TestApp/IO/RasterImage.cs similarity index 50% rename from Triangle.NET/TestApp/IO/ImageWriter.cs rename to Triangle.NET/TestApp/IO/RasterImage.cs index e453584..0a64500 100644 --- a/Triangle.NET/TestApp/IO/ImageWriter.cs +++ b/Triangle.NET/TestApp/IO/RasterImage.cs @@ -20,82 +20,14 @@ namespace MeshExplorer.IO /// /// Writes an image of the mesh to disk. /// - public class ImageWriter + public class RasterImage { - RenderColors colors = RenderColors.Default; + RenderColors colors = RenderColors.Default(); - /// - /// Sets the color scheme. - /// - /// Background color. - /// Points color. - /// Steiner points color. - /// Line color. - /// Segment color. - public void SetColorScheme(Color background, Color points, Color steiner, - Color lines, Color segments, Color triangles) + public RenderColors ColorScheme { - colors.Background = background; - colors.Point = new SolidBrush(points); - colors.SteinerPoint = new SolidBrush(steiner); - colors.Triangle = new SolidBrush(triangles); - colors.Line = new Pen(lines); - colors.Segment = new Pen(segments); - } - - /// - /// Set a color scheme with white background. - /// - public void SetColorSchemeLight() - { - colors.Background = Color.White; - colors.Point = new SolidBrush(Color.MidnightBlue); - colors.SteinerPoint = new SolidBrush(Color.DarkGreen); - colors.Triangle = new SolidBrush(Color.FromArgb(230, 240, 250)); - colors.Line = new Pen(Color.FromArgb(150, 150, 150)); - colors.Segment = new Pen(Color.SteelBlue); - } - - /// - /// Set a color scheme with black background. - /// - public void SetColorSchemeDark() - { - colors.Background = Color.Black; - colors.Point = new SolidBrush(Color.Green); - colors.SteinerPoint = new SolidBrush(Color.Peru); - colors.Triangle = new SolidBrush(Color.FromArgb(30, 40, 50)); - colors.Line = new Pen(Color.FromArgb(30, 30, 30)); - colors.Segment = new Pen(Color.Blue); - } - - /// - /// Draws the mesh and writes the image file. - /// - /// The mesh to visualize. - public void WritePng(Mesh mesh) - { - WritePng(mesh, "", 1000); - } - - /// - /// Draws the mesh and writes the image file. - /// - /// The mesh to visualize. - /// The filename (only PNG supported). - public void WritePng(Mesh mesh, string filename) - { - WritePng(mesh, filename, 1000); - } - - /// - /// Draws the mesh and writes the image file. - /// - /// The mesh to visualize. - /// The target width of the image (pixel). - public void WritePng(Mesh mesh, int width) - { - WritePng(mesh, "", width); + get { return colors; } + set { colors = value; } } /// @@ -104,7 +36,7 @@ namespace MeshExplorer.IO /// The mesh to visualize. /// The filename (only PNG supported). /// The target width of the image (pixel). - public void WritePng(Mesh mesh, string filename, int width) + public void Export(Mesh mesh, string filename, int width) { // Get mesh data -- TODO: Use RenderControl's RenderData RenderData data = new RenderData(); diff --git a/Triangle.NET/TestApp/IO/SvgImage.cs b/Triangle.NET/TestApp/IO/SvgImage.cs new file mode 100644 index 0000000..c1f8098 --- /dev/null +++ b/Triangle.NET/TestApp/IO/SvgImage.cs @@ -0,0 +1,196 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace MeshExplorer.IO +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using MeshExplorer.Rendering; + using System.IO; + using TriangleNet; + + /// + /// Writes a mesh to an SVG file. + /// + public class SvgImage + { + float scale = 1f; + + int x_offset = 0; + int y_offset = 0; + + public void Export(Mesh mesh, string filename, int width) + { + // Check file name + if (String.IsNullOrWhiteSpace(filename)) + { + filename = String.Format("mesh-{0}.svg", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss")); + } + + if (!filename.EndsWith(".svg")) + { + filename = Path.ChangeExtension(filename, ".svg"); + } + + if (width < 200) + { + width = 200; + } + + var bounds = mesh.Bounds; + + float margin = 0.05f * (float)bounds.Width; + + scale = width / ((float)bounds.Width + 2 * margin); + + x_offset = -(int)((bounds.Xmin - margin) * scale); + y_offset = (int)((bounds.Ymax + margin) * scale); + + int height = (int)((bounds.Height + 2 * margin) * scale); + + using (StreamWriter svg = new StreamWriter(filename)) + { + svg.WriteLine("", width, height); + + svg.WriteLine("", x_offset, y_offset); + + DrawTriangles(svg, mesh, false); + + DrawSegments(svg, mesh); + + DrawPoints(svg, mesh, false); + + svg.WriteLine(""); + + svg.WriteLine(""); + } + } + + private void DrawTriangles(StreamWriter svg, Mesh mesh, bool label) + { + svg.Write(" {2}", + xa.ToString("0.0", Util.Nfi), ya.ToString("0.0", Util.Nfi), tri.ID); + labels.AppendLine(); + } + + } + + svg.WriteLine("\" style=\"stroke:#969696; fill:none; stroke-linejoin:bevel;\"/>"); + + // Label the triangles. + if (label) + { + svg.WriteLine(" "); + svg.Write(labels.ToString()); + svg.WriteLine(" "); + } + } + + private void DrawSegments(StreamWriter svg, Mesh mesh) + { + svg.Write(" "); + } + + private void DrawPoints(StreamWriter svg, Mesh mesh, bool label) + { + int n = mesh.NumberOfVertices; + + int circle_size = 1; + + if (n < 100) + { + circle_size = 4; + } + else if (n < 500) + { + circle_size = 3; + } + else if (n < 1000) + { + circle_size = 2; + } + + svg.WriteLine(" "); + + double x, y; + + StringBuilder labels = new StringBuilder(); + + foreach (var node in mesh.Vertices) + { + x = scale * node.X; + y = scale * node.Y; + + svg.WriteLine(" ", + x.ToString("0.0", Util.Nfi), y.ToString("0.0", Util.Nfi), circle_size); + + if (label) + { + labels.AppendFormat("{2}", + x.ToString("0.0", Util.Nfi), y.ToString("0.0", Util.Nfi), node.ID); + labels.AppendLine(); + } + } + + svg.WriteLine(" "); + + // Label the nodes. + if (label) + { + svg.WriteLine(" "); + svg.Write(labels.ToString()); + svg.WriteLine(" "); + } + } + } +} diff --git a/Triangle.NET/TestApp/Mesh Explorer.csproj b/Triangle.NET/TestApp/Mesh Explorer.csproj index 5e50d85..155b194 100644 --- a/Triangle.NET/TestApp/Mesh Explorer.csproj +++ b/Triangle.NET/TestApp/Mesh Explorer.csproj @@ -105,13 +105,15 @@ - + + + diff --git a/Triangle.NET/TestApp/Rendering/RenderColors.cs b/Triangle.NET/TestApp/Rendering/RenderColors.cs index 16ed5e4..ee23b90 100644 --- a/Triangle.NET/TestApp/Rendering/RenderColors.cs +++ b/Triangle.NET/TestApp/Rendering/RenderColors.cs @@ -17,16 +17,40 @@ namespace MeshExplorer.Rendering /// public class RenderColors { - public static RenderColors Default = new RenderColors() + /// + /// Gets a color scheme with black background. + /// + public static RenderColors Default() { - Background = Color.FromArgb(0, 0, 0), - Point = new SolidBrush(Color.Green), - SteinerPoint = new SolidBrush(Color.Peru), - Triangle = new SolidBrush(Color.Black), - Line = new Pen(Color.FromArgb(30, 30, 30)), - Segment = new Pen(Color.DarkBlue), - VoronoiLine = new Pen(Color.FromArgb(40, 50, 60)) - }; + var colors = new RenderColors(); + + colors.Background = Color.FromArgb(0, 0, 0); + colors.Point = new SolidBrush(Color.Green); + colors.SteinerPoint = new SolidBrush(Color.Peru); + colors.Triangle = new SolidBrush(Color.Black); + colors.Line = new Pen(Color.FromArgb(30, 30, 30)); + colors.Segment = new Pen(Color.DarkBlue); + colors.VoronoiLine = new Pen(Color.FromArgb(40, 50, 60)); + + return colors; + } + /// + /// Gets a color scheme with white background. + /// + public static RenderColors LightScheme() + { + var colors = new RenderColors(); + + colors.Background = Color.White; + colors.Point = new SolidBrush(Color.FromArgb(60, 80, 120)); + colors.SteinerPoint = new SolidBrush(Color.DarkGreen); + colors.Triangle = new SolidBrush(Color.FromArgb(230, 240, 250)); + 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; + } public Color Background; public Brush Point; diff --git a/Triangle.NET/Triangle/Algorithm/Dwyer.cs b/Triangle.NET/Triangle/Algorithm/Dwyer.cs index ecd57c4..f3d2a87 100644 --- a/Triangle.NET/Triangle/Algorithm/Dwyer.cs +++ b/Triangle.NET/Triangle/Algorithm/Dwyer.cs @@ -826,8 +826,6 @@ namespace TriangleNet.Algorithm return hullsize; } - IO.DebugWriter dbgWriter; - /// /// Form a Delaunay triangulation by the divide-and-conquer method. /// @@ -844,8 +842,6 @@ namespace TriangleNet.Algorithm this.mesh = m; - dbgWriter = new IO.DebugWriter(m); - // Allocate an array of pointers to vertices for sorting. // TODO: use ToArray this.sortarray = new Vertex[m.invertices]; diff --git a/Triangle.NET/Triangle/Data/Segment.cs b/Triangle.NET/Triangle/Data/Segment.cs index 108fb1d..b440fc1 100644 --- a/Triangle.NET/Triangle/Data/Segment.cs +++ b/Triangle.NET/Triangle/Data/Segment.cs @@ -68,6 +68,14 @@ namespace TriangleNet.Data get { return this.vertices[1].id; } } + /// + /// Gets the segments endpoint. + /// + public Vertex this[int index] + { + get { return this.vertices[index]; } // TODO: Check range? + } + /// /// Gets the segment boundary mark. /// diff --git a/Triangle.NET/Triangle/Data/Triangle.cs b/Triangle.NET/Triangle/Data/Triangle.cs index ca74619..cf163c9 100644 --- a/Triangle.NET/Triangle/Data/Triangle.cs +++ b/Triangle.NET/Triangle/Data/Triangle.cs @@ -100,9 +100,9 @@ namespace TriangleNet.Data /// /// Gets the specified corners vertex id. /// - public int this[int index] + public Vertex this[int index] { - get { return this.vertices[index] == null ? -1 : this.vertices[index].id; } + get { return this.vertices[index]; } // TODO: Check range? } /// diff --git a/Triangle.NET/Triangle/Geometry/ITriangle.cs b/Triangle.NET/Triangle/Geometry/ITriangle.cs index 2b70fdb..dbf41df 100644 --- a/Triangle.NET/Triangle/Geometry/ITriangle.cs +++ b/Triangle.NET/Triangle/Geometry/ITriangle.cs @@ -10,6 +10,7 @@ namespace TriangleNet.Geometry using System.Collections.Generic; using System.Linq; using System.Text; + using TriangleNet.Data; /// /// Triangle interface. @@ -34,9 +35,9 @@ namespace TriangleNet.Geometry /// int P2 { get; } /// - /// Gets the specified vertex id. + /// Gets the vertex at specified index. /// - int this[int index] { get; } + Vertex this[int index] { get; } /// /// True if the triangle implementation contains neighbor information. diff --git a/Triangle.NET/Triangle/IO/DataReader.cs b/Triangle.NET/Triangle/IO/DataReader.cs index 596cabf..1e44ac5 100644 --- a/Triangle.NET/Triangle/IO/DataReader.cs +++ b/Triangle.NET/Triangle/IO/DataReader.cs @@ -119,10 +119,13 @@ namespace TriangleNet.IO { tri.triangle = item; + corner[0] = triangles[i].P0; + corner[1] = triangles[i].P1; + corner[2] = triangles[i].P2; + // Copy the triangle's three corners. for (int j = 0; j < 3; j++) { - corner[j] = triangles[i][j]; if ((corner[j] < 0) || (corner[j] >= mesh.invertices)) { SimpleLog.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()"); diff --git a/Triangle.NET/Triangle/IO/InputTriangle.cs b/Triangle.NET/Triangle/IO/InputTriangle.cs index dc23e8f..36f8e47 100644 --- a/Triangle.NET/Triangle/IO/InputTriangle.cs +++ b/Triangle.NET/Triangle/IO/InputTriangle.cs @@ -9,6 +9,7 @@ namespace TriangleNet.IO { using System; using TriangleNet.Geometry; + using TriangleNet.Data; /// /// Simple triangle class for input. @@ -61,9 +62,9 @@ namespace TriangleNet.IO /// /// Gets the specified corners vertex id. /// - public int this[int index] + public Vertex this[int index] { - get { return this.vertices[index]; } + get { return null; } // TODO: throw NotSupportedException? } public bool SupportsNeighbors diff --git a/Triangle.NET/Triangle/Tools/QualityMeasure.cs b/Triangle.NET/Triangle/Tools/QualityMeasure.cs index 40b664d..363dd5e 100644 --- a/Triangle.NET/Triangle/Tools/QualityMeasure.cs +++ b/Triangle.NET/Triangle/Tools/QualityMeasure.cs @@ -248,11 +248,11 @@ namespace TriangleNet.Tools { for (int j = 0; j < 3; j++) { - gi = tri[j]; + gi = tri[j].id; for (int k = 0; k < 3; k++) { - gj = tri[k]; + gj = tri[k].id; mu = Math.Max(mu, gj - gi); ml = Math.Max(ml, gi - gj);