Move Windows-specific projects into separate solution.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
|
||||
namespace TriangleNet.Rendering
|
||||
{
|
||||
using System.Drawing;
|
||||
|
||||
internal static class ExtensionMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if segment (a, b) intersects rectangle.
|
||||
/// </summary>
|
||||
public static bool Intersects(this RectangleF rect, PointF a, PointF b)
|
||||
{
|
||||
// TODO: implement intersection.
|
||||
return rect.Contains(a) || rect.Contains(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if triangle (a, b, c) intersects rectangle.
|
||||
/// </summary>
|
||||
public static bool Intersects(this RectangleF rect, PointF a, PointF b, PointF c)
|
||||
{
|
||||
// TODO: implement intersection.
|
||||
return rect.Contains(a) || rect.Contains(b) || rect.Contains(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
|
||||
namespace TriangleNet.Rendering.GDI
|
||||
{
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using TriangleNet.Rendering.GDI.Native;
|
||||
|
||||
public class FunctionRenderer
|
||||
{
|
||||
TriVertex[] points;
|
||||
GradientTriangle[] elements;
|
||||
|
||||
public Graphics RenderTarget { get; set; }
|
||||
|
||||
public IRenderContext Context { get; set; }
|
||||
|
||||
public void Render(IRenderLayer layer)
|
||||
{
|
||||
Create(layer);
|
||||
|
||||
var hdc = RenderTarget.GetHdc();
|
||||
|
||||
NativeMethods.GradientFill(hdc,
|
||||
points, (uint)points.Length, elements, (uint)elements.Length,
|
||||
GradientFillMode.GRADIENT_FILL_TRIANGLE);
|
||||
|
||||
RenderTarget.ReleaseHdc(hdc);
|
||||
}
|
||||
|
||||
private void Create(IRenderLayer layer)
|
||||
{
|
||||
var zoom = Context.Zoom;
|
||||
var colors = layer.Colors.Data;
|
||||
|
||||
int length = colors.Length;
|
||||
|
||||
int size = layer.Points.Size;
|
||||
var data = layer.Points.Data;
|
||||
|
||||
if (length != data.Length / size)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
this.points = new TriVertex[length];
|
||||
|
||||
TriVertex vertex;
|
||||
Color color;
|
||||
PointF p = new PointF((float)data[0], (float)data[1]);
|
||||
|
||||
zoom.NdcToScreen(ref p);
|
||||
|
||||
// Get correction distance
|
||||
float dx = (p.X - (int)p.X) * 2.0f;
|
||||
float dy = (p.Y - (int)p.Y) * 2.0f;
|
||||
|
||||
// Create vertices.
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
p.X = (float)data[size * i];
|
||||
p.Y = (float)data[size * i + 1];
|
||||
|
||||
zoom.NdcToScreen(ref p);
|
||||
|
||||
color = colors[i];
|
||||
|
||||
vertex = new TriVertex();
|
||||
|
||||
vertex.x = (int)(p.X + dx);
|
||||
vertex.y = (int)(p.Y + dy);
|
||||
|
||||
vertex.Red = (ushort)(color.R << 8);
|
||||
vertex.Green = (ushort)(color.G << 8);
|
||||
vertex.Blue = (ushort)(color.B << 8);
|
||||
vertex.Alpha = (ushort)(color.A << 8);
|
||||
|
||||
this.points[i] = vertex;
|
||||
}
|
||||
|
||||
var triangles = layer.Indices.Data;
|
||||
|
||||
length = triangles.Length / 3;
|
||||
|
||||
this.elements = new GradientTriangle[length];
|
||||
|
||||
GradientTriangle e;
|
||||
|
||||
// Create triangles.
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
e = new GradientTriangle();
|
||||
|
||||
e.Vertex1 = (uint)triangles[3 * i];
|
||||
e.Vertex2 = (uint)triangles[3 * i + 1];
|
||||
e.Vertex3 = (uint)triangles[3 * i + 2];
|
||||
|
||||
this.elements[i] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace TriangleNet.Rendering.GDI
|
||||
{
|
||||
internal class Helper
|
||||
{
|
||||
public static void Dispose(Dictionary<int, SolidBrush> brushes)
|
||||
{
|
||||
foreach (var brush in brushes.Values)
|
||||
{
|
||||
brush.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<int, SolidBrush> GetBrushDictionary(Dictionary<int, Color> ColorDictionary)
|
||||
{
|
||||
var brushes = new Dictionary<int, SolidBrush>();
|
||||
|
||||
foreach (var item in ColorDictionary)
|
||||
{
|
||||
brushes.Add(item.Key, new SolidBrush(item.Value));
|
||||
}
|
||||
|
||||
return brushes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
|
||||
namespace TriangleNet.Rendering.GDI
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using TriangleNet.Meshing;
|
||||
|
||||
/// <summary>
|
||||
/// Enables rendering of polygons or meshes to a bitmap.
|
||||
/// </summary>
|
||||
public class ImageRenderer
|
||||
{
|
||||
ColorManager colors = LightScheme();
|
||||
|
||||
public ColorManager ColorScheme
|
||||
{
|
||||
get { return colors; }
|
||||
set { colors = value; }
|
||||
}
|
||||
|
||||
public bool EnableRegions { get; set; }
|
||||
|
||||
public bool EnablePoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Exports a polygon to PNG format.
|
||||
/// </summary>
|
||||
/// <param name="poly">The polygon.</param>
|
||||
/// <param name="width">The desired width (pixel) of the image.</param>
|
||||
/// <param name="file">The PNG filename.</param>
|
||||
/// <param name="regions">Enable rendering of regions.</param>
|
||||
/// <param name="points">Enable rendering of points.</param>
|
||||
public static void Save(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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports a mesh to PNG format.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh.</param>
|
||||
/// <param name="width">The desired width (pixel) of the image.</param>
|
||||
/// <param name="file">The PNG filename.</param>
|
||||
/// <param name="regions">Enable rendering of regions.</param>
|
||||
/// <param name="points">Enable rendering of points.</param>
|
||||
public static void Save(IMesh mesh, string file = null, int width = 800,
|
||||
bool regions = false, bool points = true)
|
||||
{
|
||||
// Check file name
|
||||
if (string.IsNullOrWhiteSpace(file))
|
||||
{
|
||||
file = string.Format("mesh-{0}.png", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss"));
|
||||
}
|
||||
|
||||
// Ensure .png extension.
|
||||
if (!file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Path.ChangeExtension(file, ".png");
|
||||
}
|
||||
|
||||
var renderer = new ImageRenderer();
|
||||
|
||||
renderer.EnableRegions = regions;
|
||||
renderer.EnablePoints = points;
|
||||
|
||||
var bitmap = renderer.Render(mesh, width);
|
||||
|
||||
bitmap.Save(file, ImageFormat.Png);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the polygon to a bitmap.
|
||||
/// </summary>
|
||||
/// <param name="poly">The polygon.</param>
|
||||
/// <param name="width">The desired width (pixel) of the image.</param>
|
||||
/// <returns>The bitmap.</returns>
|
||||
/// <remarks>
|
||||
/// The width has to be at least 2 * sqrt(n), n the number of vertices.
|
||||
/// Otherwise, an empty bitmap
|
||||
/// </remarks>
|
||||
public Bitmap Render(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the mesh to a bitmap.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The current mesh.</param>
|
||||
/// <param name="width">The desired width (pixel) of the image.</param>
|
||||
/// <returns>The bitmap.</returns>
|
||||
/// <remarks>
|
||||
/// The width has to be at least 2 * sqrt(n), n the number of vertices.
|
||||
/// Otherwise, an empty bitmap
|
||||
/// </remarks>
|
||||
public Bitmap Render(IMesh mesh, int width = 800)
|
||||
{
|
||||
Bitmap bitmap;
|
||||
|
||||
// Check if the specified width is reasonable
|
||||
if (width < 2 * Math.Sqrt(mesh.Vertices.Count))
|
||||
{
|
||||
return new Bitmap(1, 1);
|
||||
}
|
||||
|
||||
var bounds = mesh.Bounds;
|
||||
|
||||
// World margin on each side
|
||||
float margin = (float)bounds.Height * 0.05f;
|
||||
float scale = width / ((float)bounds.Width + 2 * margin);
|
||||
|
||||
var target = new Rectangle(0, 0, width, (int)((bounds.Height + 2 * margin) * scale));
|
||||
|
||||
bitmap = new Bitmap(width, target.Height, PixelFormat.Format32bppPArgb);
|
||||
|
||||
using (var g = Graphics.FromImage(bitmap))
|
||||
{
|
||||
g.Clear(colors.Background);
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
|
||||
var context = new RenderContext(new Projection(target), colors);
|
||||
context.Add(mesh, true);
|
||||
|
||||
if (EnableRegions)
|
||||
{
|
||||
context.Add(GetRegions(mesh));
|
||||
}
|
||||
|
||||
if (!EnablePoints)
|
||||
{
|
||||
context.Enable(3, false);
|
||||
}
|
||||
|
||||
var renderer = new LayerRenderer();
|
||||
renderer.Context = context;
|
||||
renderer.RenderTarget = g;
|
||||
renderer.Render();
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
var labels = new int[mesh.Triangles.Count];
|
||||
var regions = new SortedSet<int>();
|
||||
|
||||
foreach (var t in mesh.Triangles)
|
||||
{
|
||||
labels[t.ID] = t.Label;
|
||||
regions.Add(t.Label);
|
||||
}
|
||||
|
||||
if (colors.ColorDictionary == null)
|
||||
{
|
||||
colors.CreateColorDictionary(regions);
|
||||
}
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
public static ColorManager LightScheme()
|
||||
{
|
||||
var colors = new ColorManager();
|
||||
|
||||
colors.Background = Color.White;
|
||||
colors.Point = Color.FromArgb(60, 80, 120);
|
||||
colors.SteinerPoint = Color.DarkGreen;
|
||||
colors.Line = Color.FromArgb(200, 200, 200);
|
||||
colors.Segment = Color.SteelBlue;
|
||||
colors.VoronoiLine = Color.FromArgb(160, 170, 180);
|
||||
|
||||
return colors;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
namespace TriangleNet.Rendering.GDI
|
||||
{
|
||||
using System.Drawing;
|
||||
|
||||
public class LayerRenderer : IRenderer
|
||||
{
|
||||
MeshRenderer meshRenderer;
|
||||
FunctionRenderer functionRenderer;
|
||||
|
||||
public IRenderContext Context { get; set; }
|
||||
|
||||
public Graphics RenderTarget { get; set; }
|
||||
|
||||
public LayerRenderer()
|
||||
{
|
||||
meshRenderer = new MeshRenderer();
|
||||
functionRenderer = new FunctionRenderer();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
meshRenderer.Context = Context;
|
||||
meshRenderer.RenderTarget = RenderTarget;
|
||||
|
||||
functionRenderer.Context = Context;
|
||||
functionRenderer.RenderTarget = RenderTarget;
|
||||
|
||||
// 0 = mesh (filled)
|
||||
// 1 = mesh (wireframe)
|
||||
// 2 = polygon
|
||||
// 3 = points
|
||||
// 4 = voronoi overlay
|
||||
// 5 = vector field
|
||||
// 6 = contour lines
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (var layer in this.Context.RenderLayers)
|
||||
{
|
||||
if (!layer.IsEmpty() && layer.IsEnabled)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
RenderFilledMesh(layer);
|
||||
break;
|
||||
case 1:
|
||||
RenderMesh(layer);
|
||||
break;
|
||||
case 2:
|
||||
RenderPolygon(layer);
|
||||
break;
|
||||
case 3:
|
||||
RenderPoints(layer);
|
||||
break;
|
||||
case 4:
|
||||
RenderVoronoi(layer);
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderFilledMesh(IRenderLayer layer)
|
||||
{
|
||||
if (layer.Partition != null)
|
||||
{
|
||||
meshRenderer.RenderElements(layer.Points.Data, layer.Indices.Data, 3, layer.Partition.Data);
|
||||
}
|
||||
else if (layer.Colors != null)
|
||||
{
|
||||
functionRenderer.Render(layer);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderMesh(IRenderLayer layer)
|
||||
{
|
||||
if (layer.Indices.Size == 3)
|
||||
{
|
||||
meshRenderer.RenderElements(layer.Points.Data, layer.Indices.Data, 3, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var pen = new Pen(Context.ColorManager.Line);
|
||||
|
||||
meshRenderer.RenderEdges(layer.Points.Data, layer.Indices.Data, pen);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderPolygon(IRenderLayer layer)
|
||||
{
|
||||
using var pen = new Pen(Context.ColorManager.Segment);
|
||||
|
||||
meshRenderer.RenderSegments(layer.Points.Data, layer.Indices.Data, pen);
|
||||
}
|
||||
|
||||
private void RenderPoints(IRenderLayer layer)
|
||||
{
|
||||
meshRenderer.RenderPoints(layer.Points.Data, layer.Points.Size, layer.Count);
|
||||
}
|
||||
|
||||
private void RenderVoronoi(IRenderLayer layer)
|
||||
{
|
||||
if (RenderManager.VORONOI_DEBUG)
|
||||
{
|
||||
meshRenderer.RenderEdges(layer.Points.Data, layer.Indices.Data, Pens.Purple);
|
||||
meshRenderer.RenderPoints(layer.Points.Data, layer.Points.Size, 0, layer.Count, Brushes.Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var pen = new Pen(Context.ColorManager.VoronoiLine);
|
||||
|
||||
meshRenderer.RenderEdges(layer.Points.Data, layer.Indices.Data, pen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="MeshRenderer.cs" company="">
|
||||
// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace TriangleNet.Rendering.GDI
|
||||
{
|
||||
using System.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Renders a mesh.
|
||||
/// </summary>
|
||||
public class MeshRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MeshRenderer" /> class.
|
||||
/// </summary>
|
||||
public MeshRenderer()
|
||||
{
|
||||
}
|
||||
|
||||
public Graphics RenderTarget { get; set; }
|
||||
|
||||
public IRenderContext Context { get; set; }
|
||||
|
||||
public void RenderPoints(float[] points, int size, int limit = 0)
|
||||
{
|
||||
int n = points.Length / size;
|
||||
int m = limit > 0 ? limit : n;
|
||||
|
||||
using var Point = new SolidBrush(Context.ColorManager.Point);
|
||||
using var SteinerPoint = new SolidBrush(Context.ColorManager.SteinerPoint);
|
||||
|
||||
// Draw unchanged points
|
||||
RenderPoints(points, size, 0, m, Point);
|
||||
|
||||
// Draw new (Steiner) points
|
||||
if (limit > 0)
|
||||
{
|
||||
RenderPoints(points, size, m, n, SteinerPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderPoints(float[] points, int size, int start, int end, Brush brush)
|
||||
{
|
||||
var g = this.RenderTarget;
|
||||
var zoom = this.Context.Zoom;
|
||||
|
||||
int i, k, n = points.Length / size;
|
||||
PointF p = new PointF();
|
||||
|
||||
// Render points
|
||||
for (i = start; i < end; i++)
|
||||
{
|
||||
k = size * i;
|
||||
|
||||
p.X = points[k];
|
||||
p.Y = points[k + 1];
|
||||
|
||||
if (zoom.Viewport.Contains(p))
|
||||
{
|
||||
zoom.NdcToScreen(ref p);
|
||||
g.FillEllipse(brush, p.X - 1.5f, p.Y - 1.5f, 3, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderSegments(float[] points, int[] indices, Pen pen)
|
||||
{
|
||||
RenderLines(points, indices, pen);
|
||||
}
|
||||
|
||||
public void RenderEdges(float[] points, int[] indices, Pen pen)
|
||||
{
|
||||
RenderLines(points, indices, pen);
|
||||
}
|
||||
|
||||
public void RenderElements(float[] points, int[] indices, int size, int[] partition)
|
||||
{
|
||||
var g = this.RenderTarget;
|
||||
var zoom = this.Context.Zoom;
|
||||
|
||||
int n = indices.Length / size;
|
||||
int k0, k1, k2;
|
||||
|
||||
var tri = new PointF[size];
|
||||
|
||||
bool filled = partition != null;
|
||||
|
||||
var brushes = filled ? Helper.GetBrushDictionary(Context.ColorManager.ColorDictionary) : null;
|
||||
|
||||
// TODO: remove hard-coded color
|
||||
var pen = new Pen(Color.FromArgb(20, 20, 20));
|
||||
|
||||
// Draw triangles
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
k0 = 2 * indices[3 * i];
|
||||
k1 = 2 * indices[3 * i + 1];
|
||||
k2 = 2 * indices[3 * i + 2];
|
||||
|
||||
tri[0].X = points[k0];
|
||||
tri[0].Y = points[k0 + 1];
|
||||
|
||||
tri[1].X = points[k1];
|
||||
tri[1].Y = points[k1 + 1];
|
||||
|
||||
tri[2].X = points[k2];
|
||||
tri[2].Y = points[k2 + 1];
|
||||
|
||||
if (zoom.Viewport.Intersects(tri[0], tri[1], tri[2]))
|
||||
{
|
||||
zoom.NdcToScreen(ref tri[0]);
|
||||
zoom.NdcToScreen(ref tri[1]);
|
||||
zoom.NdcToScreen(ref tri[2]);
|
||||
|
||||
if (filled)
|
||||
{
|
||||
var b = brushes[partition[i]];
|
||||
|
||||
if (b.Color.A > 0)
|
||||
{
|
||||
g.FillPolygon(b, tri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g.DrawPolygon(pen, tri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pen.Dispose();
|
||||
|
||||
if (filled)
|
||||
{
|
||||
Helper.Dispose(brushes);
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderLines(float[] points, int[] indices, Pen pen)
|
||||
{
|
||||
var g = this.RenderTarget;
|
||||
var zoom = this.Context.Zoom;
|
||||
|
||||
int n = indices.Length / 2;
|
||||
int k0, k1;
|
||||
|
||||
PointF p0 = new PointF();
|
||||
PointF p1 = new PointF();
|
||||
|
||||
// Draw edges
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
k0 = 2 * indices[2 * i];
|
||||
k1 = 2 * indices[2 * i + 1];
|
||||
|
||||
p0.X = points[k0];
|
||||
p0.Y = points[k0 + 1];
|
||||
|
||||
p1.X = points[k1];
|
||||
p1.Y = points[k1 + 1];
|
||||
|
||||
if (zoom.Viewport.Intersects(p0, p1))
|
||||
{
|
||||
zoom.NdcToScreen(ref p0);
|
||||
zoom.NdcToScreen(ref p1);
|
||||
|
||||
g.DrawLine(pen, p0, p1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
namespace TriangleNet.Rendering.GDI.Native
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies gradient fill mode
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum GradientFillMode : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// In this mode, two endpoints describe a rectangle. The rectangle is defined
|
||||
/// to have a constant color (specified by the TRIVERTEX structure) for the
|
||||
/// left and right edges. GDI interpolates the color from the left to right
|
||||
/// edge and fills the interior
|
||||
/// </summary>
|
||||
GRADIENT_FILL_RECT_H = 0,
|
||||
/// <summary>
|
||||
/// In this mode, two endpoints describe a rectangle. The rectangle is
|
||||
/// defined to have a constant color (specified by the TRIVERTEX structure)
|
||||
/// for the top and bottom edges. GDI interpolates the color from the top
|
||||
/// to bottom edge and fills the interior
|
||||
/// </summary>
|
||||
GRADIENT_FILL_RECT_V = 1,
|
||||
/// <summary>
|
||||
/// In this mode, an array of TRIVERTEX structures is passed to GDI
|
||||
/// along with a list of array indexes that describe separate triangles.
|
||||
/// GDI performs linear interpolation between triangle vertices and fills
|
||||
/// the interior. Drawing is done directly in 24- and 32-bpp modes.
|
||||
/// Dithering is performed in 16-, 8-, 4-, and 1-bpp mode
|
||||
/// </summary>
|
||||
GRADIENT_FILL_TRIANGLE = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
namespace TriangleNet.Rendering.GDI.Native
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// The GRADIENT_RECT structure specifies the index of two vertices in the
|
||||
/// pVertex array in the GradientFill function. These two vertices form the
|
||||
/// upper-left and lower-right boundaries of a rectangle.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/windows/desktop/dd144958.aspx
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct GradientRect
|
||||
{
|
||||
/// <summary>
|
||||
/// The upper-left corner of a rectangle.
|
||||
/// </summary>
|
||||
public uint UpperLeft;
|
||||
|
||||
/// <summary>
|
||||
/// The lower-right corner of a rectangle.
|
||||
/// </summary>
|
||||
public uint LowerRight;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
namespace TriangleNet.Rendering.GDI.Native
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// The GRADIENT_TRIANGLE structure specifies the index of three
|
||||
/// vertices in the pVertex array in the GradientFill function.
|
||||
/// These three vertices form one triangle
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/windows/desktop/dd144959.aspx
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct GradientTriangle
|
||||
{
|
||||
/// <summary>
|
||||
/// The first point of the triangle where sides intersect.
|
||||
/// </summary>
|
||||
public uint Vertex1;
|
||||
|
||||
/// <summary>
|
||||
/// The second point of the triangle where sides intersect.
|
||||
/// </summary>
|
||||
public uint Vertex2;
|
||||
|
||||
/// <summary>
|
||||
/// The third point of the triangle where sides intersect.
|
||||
/// </summary>
|
||||
public uint Vertex3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
namespace TriangleNet.Rendering.GDI.Native
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// PInvoke signatures for GradientFill methods.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Minimum requirements: Windows 2000 Professional
|
||||
///
|
||||
/// http://msdn.microsoft.com/en-us/library/windows/desktop/dd144957.aspx
|
||||
/// </remarks>
|
||||
internal static class NativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// The GradientFill function fills rectangle and triangle structures
|
||||
/// </summary>
|
||||
/// <param name="hdc">Handle to the destination device context</param>
|
||||
/// <param name="pVertex">Array of TRIVERTEX structures that each define a triangle vertex</param>
|
||||
/// <param name="nVertex">The number of vertices in pVertex</param>
|
||||
/// <param name="pMesh">Array of elements</param>
|
||||
/// <param name="nMesh">The number of elements in pMesh</param>
|
||||
/// <param name="ulMode">Specifies gradient fill mode</param>
|
||||
/// <returns>If the function succeeds, the return value is true, false</returns>
|
||||
public static bool GradientFill([In] IntPtr hdc, TriVertex[] pVertex, uint nVertex, uint[] pMesh, uint nMesh,
|
||||
GradientFillMode ulMode)
|
||||
{
|
||||
return Native.GradientFill(hdc, pVertex, nVertex, pMesh, nMesh, ulMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The GradientFill function fills rectangle and triangle structures
|
||||
/// </summary>
|
||||
/// <param name="hdc">Handle to the destination device context</param>
|
||||
/// <param name="pVertex">Array of TRIVERTEX structures that each define a triangle vertex</param>
|
||||
/// <param name="nVertex">The number of vertices in pVertex</param>
|
||||
/// <param name="pMesh">Array of GRADIENT_TRIANGLE structures in triangle mode</param>
|
||||
/// <param name="nMesh">The number of elements in pMesh</param>
|
||||
/// <param name="ulMode">Specifies gradient fill mode</param>
|
||||
/// <returns>If the function succeeds, the return value is true, false</returns>
|
||||
public static bool GradientFill([In] IntPtr hdc, TriVertex[] pVertex, uint nVertex, GradientTriangle[] pMesh,
|
||||
uint nMesh, GradientFillMode ulMode)
|
||||
{
|
||||
return Native.GradientFill(hdc, pVertex, nVertex, pMesh, nMesh, ulMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The GradientFill function fills rectangle and triangle structures
|
||||
/// </summary>
|
||||
/// <param name="hdc">Handle to the destination device context</param>
|
||||
/// <param name="pVertex">Array of TRIVERTEX structures that each define a triangle vertex</param>
|
||||
/// <param name="nVertex">The number of vertices in pVertex</param>
|
||||
/// <param name="pMesh">an array of GRADIENT_RECT structures in rectangle mode</param>
|
||||
/// <param name="nMesh">The number of elements in pMesh</param>
|
||||
/// <param name="ulMode">Specifies gradient fill mode</param>
|
||||
/// <returns>If the function succeeds, the return value is true, false</returns>
|
||||
public static bool GradientFill([In] IntPtr hdc, TriVertex[] pVertex, uint nVertex, GradientRect[] pMesh,
|
||||
uint nMesh, GradientFillMode ulMode)
|
||||
{
|
||||
return Native.GradientFill(hdc, pVertex, nVertex, pMesh, nMesh, ulMode);
|
||||
}
|
||||
|
||||
#region Nested type: Native
|
||||
|
||||
internal class Native
|
||||
{
|
||||
[DllImport("msimg32.dll", EntryPoint = "GradientFill", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GradientFill([In] IntPtr hdc,
|
||||
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 2)] TriVertex[] pVertex,
|
||||
uint nVertex, uint[] pMesh, uint nMesh, GradientFillMode ulMode);
|
||||
|
||||
[DllImport("msimg32.dll", EntryPoint = "GradientFill", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GradientFill([In] IntPtr hdc,
|
||||
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 2)] TriVertex[] pVertex,
|
||||
uint nVertex, GradientRect[] pMesh, uint nMesh, GradientFillMode ulMode);
|
||||
|
||||
[DllImport("msimg32.dll", EntryPoint = "GradientFill", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GradientFill([In] IntPtr hdc,
|
||||
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 2)] TriVertex[] pVertex,
|
||||
uint nVertex, GradientTriangle[] pMesh, uint nMesh, GradientFillMode ulMode);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
namespace TriangleNet.Rendering.GDI.Native
|
||||
{
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// The TRIVERTEX structure contains color information and position information.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145142.aspx
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct TriVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// The x-coordinate, in logical units, of the upper-left corner of the rectangle
|
||||
/// </summary>
|
||||
public int x;
|
||||
|
||||
/// <summary>
|
||||
/// The y-coordinate, in logical units, of the upper-left corner of the rectangle
|
||||
/// </summary>
|
||||
public int y;
|
||||
|
||||
/// <summary>
|
||||
/// The color information at the point of x, y
|
||||
/// </summary>
|
||||
public ushort Red;
|
||||
|
||||
/// <summary>
|
||||
/// The color information at the point of x, y
|
||||
/// </summary>
|
||||
public ushort Green;
|
||||
|
||||
/// <summary>
|
||||
/// The color information at the point of x, y
|
||||
/// </summary>
|
||||
public ushort Blue;
|
||||
|
||||
/// <summary>
|
||||
/// The color information at the point of x, y
|
||||
/// </summary>
|
||||
public ushort Alpha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="RendererControl.cs" company="">
|
||||
// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace TriangleNet.Rendering.GDI
|
||||
{
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Text;
|
||||
using System.Globalization;
|
||||
using System.Windows.Forms;
|
||||
|
||||
/// <summary>
|
||||
/// Renders a mesh using GDI.
|
||||
/// </summary>
|
||||
public class RenderControl : Control, IRenderControl
|
||||
{
|
||||
// Rendering stuff
|
||||
private BufferedGraphics buffer;
|
||||
private BufferedGraphicsContext context;
|
||||
|
||||
//ColorManager renderColors;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
string coordinate = string.Empty;
|
||||
|
||||
Timer timer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderControl" /> class.
|
||||
/// </summary>
|
||||
public RenderControl()
|
||||
{
|
||||
//this.SetStyle(ControlStyles.UserPaint, true);
|
||||
//this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
|
||||
//this.SetStyle(ControlStyles.Selectable, true);
|
||||
this.SetStyle(ControlStyles.ResizeRedraw, true);
|
||||
|
||||
//renderColors = ColorManager.Default();
|
||||
|
||||
this.BackColor = Color.Black;
|
||||
|
||||
context = BufferedGraphicsManager.Current;// new BufferedGraphicsContext();
|
||||
|
||||
timer = new Timer();
|
||||
timer.Interval = 3000;
|
||||
timer.Tick += (sender, e) =>
|
||||
{
|
||||
timer.Stop();
|
||||
coordinate = string.Empty;
|
||||
this.Invalidate();
|
||||
};
|
||||
}
|
||||
|
||||
public IRenderer Renderer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the graphics buffer (should be called in the forms load event).
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
//zoom.Initialize(this.ClientRectangle);
|
||||
InitializeBuffer();
|
||||
|
||||
initialized = true;
|
||||
|
||||
this.Invalidate();
|
||||
}
|
||||
|
||||
public override void Refresh()
|
||||
{
|
||||
this.Render();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update graphics buffer and zoom after a resize.
|
||||
/// </summary>
|
||||
public void HandleResize()
|
||||
{
|
||||
var zoom = this.Renderer.Context.Zoom;
|
||||
|
||||
zoom.Resize(this.ClientRectangle);
|
||||
InitializeBuffer();
|
||||
}
|
||||
|
||||
private void InitializeBuffer()
|
||||
{
|
||||
if (this.Width > 0 && this.Height > 0)
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
if (this.ClientRectangle == buffer.Graphics.VisibleClipBounds)
|
||||
{
|
||||
this.Invalidate();
|
||||
|
||||
// Bounds didn't change. Probably we just restored the
|
||||
// window from minimized state.
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.Dispose();
|
||||
}
|
||||
|
||||
//buffer = context.Allocate(Graphics.FromHwnd(this.Handle), this.ClientRectangle);
|
||||
buffer = context.Allocate(this.CreateGraphics(), this.ClientRectangle);
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
this.Render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Render()
|
||||
{
|
||||
coordinate = string.Empty;
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var g = buffer.Graphics;
|
||||
var renderer = this.Renderer as LayerRenderer;
|
||||
|
||||
g.Clear(renderer.Context.ColorManager.Background);
|
||||
|
||||
if (!initialized || renderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
renderer.RenderTarget = g;
|
||||
renderer.Render();
|
||||
|
||||
this.Invalidate();
|
||||
}
|
||||
|
||||
#region Protected overrides
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.Render();
|
||||
|
||||
if (!string.IsNullOrEmpty(coordinate) && Renderer.Context.HasData)
|
||||
{
|
||||
Graphics g = e.Graphics;
|
||||
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
|
||||
TextRenderer.DrawText(g, coordinate, Font, new Point(10, 10), Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs pevent)
|
||||
{
|
||||
// Do nothing
|
||||
if (!initialized)
|
||||
{
|
||||
base.OnPaintBackground(pevent);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseWheel(MouseEventArgs e)
|
||||
{
|
||||
if (!initialized) return;
|
||||
|
||||
var zoom = this.Renderer.Context.Zoom;
|
||||
|
||||
if (zoom.Zoom(e.Delta, (float)e.X / Width, (float)e.Y / Height))
|
||||
{
|
||||
// Redraw
|
||||
this.Render();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMouseClick(MouseEventArgs e)
|
||||
{
|
||||
// We need to manually set the focus to get proper handling of
|
||||
// the KeyUp and MouseWheel events.
|
||||
this.Focus();
|
||||
|
||||
if (!initialized) return;
|
||||
|
||||
var zoom = this.Renderer.Context.Zoom;
|
||||
|
||||
if (e.Button == MouseButtons.Middle)
|
||||
{
|
||||
zoom.Reset();
|
||||
this.Render();
|
||||
}
|
||||
else if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
timer.Stop();
|
||||
|
||||
PointF c = new PointF((float)e.X / Width, (float)e.Y / Height);
|
||||
zoom.ScreenToWorld(c, out double x, out double y);
|
||||
coordinate = string.Format(NumberFormatInfo.InvariantInfo,
|
||||
"X:{0} Y:{1}", x, y);
|
||||
|
||||
this.Invalidate();
|
||||
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(KeyEventArgs e)
|
||||
{
|
||||
if (!initialized) return;
|
||||
|
||||
var zoom = this.Renderer.Context.Zoom;
|
||||
|
||||
bool redraw = false;
|
||||
|
||||
if (e.KeyCode == Keys.Up)
|
||||
{
|
||||
redraw = zoom.Translate(0, 1);
|
||||
}
|
||||
else if (e.KeyCode == Keys.Down)
|
||||
{
|
||||
redraw = zoom.Translate(0, -1);
|
||||
}
|
||||
else if (e.KeyCode == Keys.Left)
|
||||
{
|
||||
redraw = zoom.Translate(-1, 0);
|
||||
}
|
||||
else if (e.KeyCode == Keys.Right)
|
||||
{
|
||||
redraw = zoom.Translate(1, 0);
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
{
|
||||
this.Render();
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<RootNamespace>TriangleNet.Rendering.GDI</RootNamespace>
|
||||
<AssemblyName>Triangle.Rendering.GDI</AssemblyName>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Triangle\Triangle.csproj" />
|
||||
<ProjectReference Include="..\Triangle.Rendering\Triangle.Rendering.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user