From a67b518b2d66e32e4f38dc56d49e63bb721500c5 Mon Sep 17 00:00:00 2001 From: wo80 Date: Mon, 14 Feb 2022 14:01:35 +0100 Subject: [PATCH] Minor updates to Triangle.Rendering code. --- src/Triangle.Rendering/GDI/ImageRenderer.cs | 139 +++++++++++++++++++- src/Triangle.Rendering/IRenderContext.cs | 2 + src/Triangle.Rendering/IRenderLayer.cs | 1 - src/Triangle.Rendering/RenderLayer.cs | 1 - src/Triangle.Rendering/RenderManager.cs | 6 + 5 files changed, 144 insertions(+), 5 deletions(-) diff --git a/src/Triangle.Rendering/GDI/ImageRenderer.cs b/src/Triangle.Rendering/GDI/ImageRenderer.cs index a30d65e..9045fbd 100644 --- a/src/Triangle.Rendering/GDI/ImageRenderer.cs +++ b/src/Triangle.Rendering/GDI/ImageRenderer.cs @@ -25,11 +25,44 @@ namespace TriangleNet.Rendering.GDI public bool EnableRegions { get; set; } public bool EnablePoints { get; set; } - + /// - /// Export the mesh to PNG format. + /// Exports a polygon to PNG format. /// - /// The current mesh. + /// The polygon. + /// The desired width (pixel) of the image. + /// The PNG filename. + /// Enable rendering of regions. + /// Enable rendering of points. + public static void Save(Geometry.IPolygon poly, string file = null, int width = 800, + bool points = true) + { + // Check file name + if (string.IsNullOrWhiteSpace(file)) + { + file = string.Format("poly-{0}.png", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss")); + } + + // Ensure .png extension. + if (file.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) + { + Path.ChangeExtension(file, ".png"); + } + + var renderer = new ImageRenderer(); + + renderer.EnableRegions = false; + renderer.EnablePoints = points; + + var bitmap = renderer.Render(poly, width); + + bitmap.Save(file, ImageFormat.Png); + } + + /// + /// Exports a mesh to PNG format. + /// + /// The mesh. /// The desired width (pixel) of the image. /// The PNG filename. /// Enable rendering of regions. @@ -59,6 +92,58 @@ namespace TriangleNet.Rendering.GDI bitmap.Save(file, ImageFormat.Png); } + /// + /// Renders the polygon to a bitmap. + /// + /// The polygon. + /// The desired width (pixel) of the image. + /// The bitmap. + /// + /// The width has to be at least 2 * sqrt(n), n the number of vertices. + /// Otherwise, an empty bitmap + /// + public Bitmap Render(Geometry.IPolygon poly, int width = 800) + { + Bitmap bitmap; + + // Check if the specified width is reasonable + if (width < 2 * Math.Sqrt(poly.Points.Count)) + { + return new Bitmap(1, 1); + } + + var bounds = poly.Bounds(); + + // World margin on each side + float margin = (float)bounds.Height * 0.05f; + float scale = width / ((float)bounds.Width + 2 * margin); + + var target = new Rectangle(0, 0, width, (int)((bounds.Height + 2 * margin) * scale)); + + bitmap = new Bitmap(width, target.Height, PixelFormat.Format32bppPArgb); + + using (var g = Graphics.FromImage(bitmap)) + { + g.Clear(colors.Background); + g.SmoothingMode = SmoothingMode.HighQuality; + + var context = new RenderContext(new Projection(target), colors); + context.Add(poly); + + if (!EnablePoints) + { + context.Enable(3, false); + } + + var renderer = new LayerRenderer(); + renderer.Context = context; + renderer.RenderTarget = g; + renderer.Render(); + } + + return bitmap; + } + /// /// Renders the mesh to a bitmap. /// @@ -116,6 +201,54 @@ namespace TriangleNet.Rendering.GDI return bitmap; } + public Bitmap Render(IMesh mesh, Topology.DCEL.DcelMesh dcel, int width = 800) + { + Bitmap bitmap; + + // Check if the specified width is reasonable + if (width < 2 * Math.Sqrt(mesh.Vertices.Count)) + { + return new Bitmap(1, 1); + } + + var bounds = mesh.Bounds; + + // World margin on each side + float margin = (float)bounds.Height * 0.05f; + float scale = width / ((float)bounds.Width + 2 * margin); + + var target = new Rectangle(0, 0, width, (int)((bounds.Height + 2 * margin) * scale)); + + bitmap = new Bitmap(width, target.Height, PixelFormat.Format32bppPArgb); + + using (var g = Graphics.FromImage(bitmap)) + { + g.Clear(colors.Background); + g.SmoothingMode = SmoothingMode.HighQuality; + + var context = new RenderContext(new Projection(target), colors); + context.Add(mesh, true); + context.Add(dcel.Vertices.ToArray(), dcel.Edges, false); + + if (EnableRegions) + { + context.Add(GetRegions(mesh)); + } + + if (!EnablePoints) + { + context.Enable(3, false); + } + + var renderer = new LayerRenderer(); + renderer.Context = context; + renderer.RenderTarget = g; + renderer.Render(); + } + + return bitmap; + } + private int[] GetRegions(IMesh mesh) { mesh.Renumber(); diff --git a/src/Triangle.Rendering/IRenderContext.cs b/src/Triangle.Rendering/IRenderContext.cs index 7d0e8ee..46f1036 100644 --- a/src/Triangle.Rendering/IRenderContext.cs +++ b/src/Triangle.Rendering/IRenderContext.cs @@ -28,5 +28,7 @@ namespace TriangleNet.Rendering void Add(int[] partition); void Enable(int layer, bool enabled); + + void Clear(); } } diff --git a/src/Triangle.Rendering/IRenderLayer.cs b/src/Triangle.Rendering/IRenderLayer.cs index 8a11104..a1785ad 100644 --- a/src/Triangle.Rendering/IRenderLayer.cs +++ b/src/Triangle.Rendering/IRenderLayer.cs @@ -13,7 +13,6 @@ namespace TriangleNet.Rendering { int Count { get; } - // Points can be set, because layers may share vertices. IBuffer Points { get; } IBuffer Indices { get; } diff --git a/src/Triangle.Rendering/RenderLayer.cs b/src/Triangle.Rendering/RenderLayer.cs index b6a66b7..1042c9a 100644 --- a/src/Triangle.Rendering/RenderLayer.cs +++ b/src/Triangle.Rendering/RenderLayer.cs @@ -32,7 +32,6 @@ namespace TriangleNet.Rendering public IBuffer Points { get { return points; } - set { points = value; } } public IBuffer Indices diff --git a/src/Triangle.Rendering/RenderManager.cs b/src/Triangle.Rendering/RenderManager.cs index a5d1044..382db90 100644 --- a/src/Triangle.Rendering/RenderManager.cs +++ b/src/Triangle.Rendering/RenderManager.cs @@ -77,6 +77,12 @@ namespace TriangleNet.Rendering control.HandleResize(); } + public void Clear() + { + context.Clear(); + control.Refresh(); + } + public void Enable(int layer, bool enabled) { context.Enable(layer, enabled);