Rename source folder.

This commit is contained in:
wo80
2022-02-13 12:28:55 +01:00
parent 625be1ab8f
commit a088a2f8c5
185 changed files with 38090 additions and 38090 deletions
+68
View File
@@ -0,0 +1,68 @@
namespace TriangleNet.Rendering
{
using System.Drawing;
public class BoundingBox
{
public float Left;
public float Right;
public float Bottom;
public float Top;
public float Width
{
get { return this.Right - this.Left; }
}
public float Height
{
get { return this.Top - this.Bottom; }
}
public BoundingBox()
{
Reset();
}
public BoundingBox(float left, float right, float bottom, float top)
{
this.Left = left;
this.Right = right;
this.Bottom = bottom;
this.Top = top;
}
public void Update(Point pt)
{
this.Update(pt.X, pt.Y);
}
public void Update(PointF pt)
{
this.Update(pt.X, pt.Y);
}
public void Update(double x, double y)
{
Update((float)x, (float)y);
}
public void Update(float x, float y)
{
// Update bounding box
if (this.Left > x) this.Left = x;
if (this.Right < x) this.Right = x;
if (this.Bottom > y) this.Bottom = y;
if (this.Top < y) this.Top = y;
}
public void Reset()
{
this.Left = float.MaxValue;
this.Right = -float.MaxValue;
this.Bottom = float.MaxValue;
this.Top = -float.MaxValue;
}
}
}
@@ -0,0 +1,41 @@
namespace TriangleNet.Rendering.Buffer
{
public abstract class BufferBase<T> : IBuffer<T> where T : struct
{
protected T[] data;
protected int size;
public BufferBase(int capacity, int size)
{
this.data = new T[capacity];
this.size = size;
}
public BufferBase(T[] data, int size)
{
this.data = data;
this.size = size;
}
public T[] Data
{
get { return data; }
}
public int Count
{
get { return data == null ? 0 : data.Length; }
}
public abstract int Size
{
get;
}
public abstract BufferTarget Target
{
get;
}
}
}
@@ -0,0 +1,29 @@
namespace TriangleNet.Rendering.Buffer
{
using System;
using System.Drawing;
public class ColorBuffer : BufferBase<Color>
{
public ColorBuffer(int capacity, int size)
: base(capacity, size)
{
}
public ColorBuffer(Color[] data, int size)
: base(data, size)
{
}
public override int Size
{
get { return 1; }
}
public override BufferTarget Target
{
get { return BufferTarget.ColorBuffer; }
}
}
}
+34
View File
@@ -0,0 +1,34 @@
namespace TriangleNet.Rendering.Buffer
{
public enum BufferTarget : byte
{
ColorBuffer,
IndexBuffer,
VertexBuffer
}
public interface IBuffer<T> where T : struct
{
/// <summary>
/// Gets the contents of the buffer.
/// </summary>
T[] Data { get; }
/// <summary>
/// Gets the size of the buffer.
/// </summary>
int Count { get; }
/// <summary>
/// Gets the size of one element in the buffer (i.e. 2 for 2D points
/// or 3 for triangles).
/// </summary>
int Size { get; }
/// <summary>
/// Gets the buffer target (vertices or indices).
/// </summary>
BufferTarget Target { get; }
}
}
@@ -0,0 +1,30 @@
namespace TriangleNet.Rendering.Buffer
{
public class IndexBuffer : BufferBase<int>
{
public IndexBuffer(int capacity, int size)
: base(capacity, size)
{
}
public IndexBuffer(int[] data, int size)
: base(data, size)
{
}
/// <summary>
/// Gets the number of indices for one element (i.e. 2 for segments
/// or 3 for triangles).
/// </summary>
public override int Size
{
get { return size; }
}
public override BufferTarget Target
{
get { return BufferTarget.IndexBuffer; }
}
}
}
@@ -0,0 +1,30 @@
namespace TriangleNet.Rendering.Buffer
{
public class VertexBuffer : BufferBase<float>
{
public VertexBuffer(int capacity, int size = 2)
: base(capacity, size)
{
}
public VertexBuffer(float[] data, int size = 2)
: base(data, size)
{
}
/// <summary>
/// Gets the number of coordinates of one vertex in the buffer (i.e. 2 for
/// 2D points or 3D points).
/// </summary>
public override int Size
{
get { return size; }
}
public override BufferTarget Target
{
get { return BufferTarget.VertexBuffer; }
}
}
}
+181
View File
@@ -0,0 +1,181 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using System.Drawing;
using TriangleNet.Rendering.Util;
public class ColorManager
{
Color background;
SolidBrush point;
SolidBrush steinerPoint;
Pen line;
Pen segment;
Pen voronoiLine;
#region Public properties
/// <summary>
/// Gets or sets the background color.
/// </summary>
public Color Background
{
get { return background; }
set { background = value; }
}
/// <summary>
/// Gets or sets the brush used for points.
/// </summary>
public SolidBrush Point
{
get { return point; }
set
{
if (point != null) point.Dispose();
point = value;
}
}
/// <summary>
/// Gets or sets the brush used for steiner points.
/// </summary>
public SolidBrush SteinerPoint
{
get { return steinerPoint; }
set
{
if (steinerPoint != null) steinerPoint.Dispose();
steinerPoint = value;
}
}
/// <summary>
/// Gets or sets the pen used for mesh edges.
/// </summary>
public Pen Line
{
get { return line; }
set
{
if (line != null) line.Dispose();
line = value;
}
}
/// <summary>
/// Gets or sets the pen used for mesh segments.
/// </summary>
public Pen Segment
{
get { return segment; }
set
{
if (segment != null) segment.Dispose();
segment = value;
}
}
/// <summary>
/// Gets or sets the pen used for Voronoi edges.
/// </summary>
public Pen VoronoiLine
{
get { return voronoiLine; }
set
{
if (voronoiLine != null) voronoiLine.Dispose();
voronoiLine = value;
}
}
#endregion
/// <summary>
/// Gets or sets a dictionary which maps region ids (or partition indices) to a color.
/// </summary>
public Dictionary<int, Color> ColorDictionary { get; set; }
/// <summary>
/// Gets or sets a colormap which is used for function plotting.
/// </summary>
public ColorMap ColorMap { get; set; }
/// <summary>
/// Creates an instance of the <see cref="ColorManager"/> class with default (dark) color scheme.
/// </summary>
public static ColorManager Default()
{
var colors = new ColorManager();
colors.Background = Color.FromArgb(0, 0, 0);
colors.Point = new SolidBrush(Color.Green);
colors.SteinerPoint = new SolidBrush(Color.Peru);
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;
}
public void CreateColorDictionary(int length)
{
var keys = new int[length];
for (int i = 0; i < length; i++)
{
keys[i] = i;
}
CreateColorDictionary(keys, length);
}
public void CreateColorDictionary(IEnumerable<int> keys, int length)
{
this.ColorDictionary = new Dictionary<int, Color>();
int i = 0, n = regionColors.Length;
foreach (var key in keys)
{
this.ColorDictionary.Add(key, regionColors[i]);
i = (i + 1) % n;
}
}
internal void Dispose(Dictionary<int, SolidBrush> brushes)
{
foreach (var brush in brushes.Values)
{
brush.Dispose();
}
}
internal Dictionary<int, SolidBrush> GetBrushDictionary()
{
var brushes = new Dictionary<int, SolidBrush>();
foreach (var item in ColorDictionary)
{
brushes.Add(item.Key, new SolidBrush(item.Value));
}
return brushes;
}
// Change or add as many colors as you like...
private static Color[] regionColors = {
Color.Transparent,
Color.FromArgb(200, 0, 255, 0),
Color.FromArgb(200, 255, 0, 0),
Color.FromArgb(200, 0, 0, 255),
Color.FromArgb(200, 0, 255, 255),
Color.FromArgb(200, 255, 255, 0),
Color.FromArgb(200, 255, 0, 255),
Color.FromArgb(200, 127, 0, 255),
Color.FromArgb(200, 0, 127, 255)
};
}
}
@@ -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.WorldToScreen(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.WorldToScreen(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;
}
}
}
}
+154
View File
@@ -0,0 +1,154 @@
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>
/// Export the mesh to PNG format.
/// </summary>
/// <param name="mesh">The current 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 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;
}
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, regions.Count);
}
return labels;
}
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(200, 200, 200));
colors.Segment = new Pen(Color.SteelBlue);
colors.VoronoiLine = new Pen(Color.FromArgb(160, 170, 180));
return colors;
}
}
}
+118
View File
@@ -0,0 +1,118 @@
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
{
meshRenderer.RenderEdges(layer.Points.Data, layer.Indices.Data, Context.ColorManager.Line);
}
}
private void RenderPolygon(IRenderLayer layer)
{
meshRenderer.RenderSegments(layer.Points.Data, layer.Indices.Data, Context.ColorManager.Segment);
}
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
{
meshRenderer.RenderEdges(layer.Points.Data, layer.Indices.Data, Context.ColorManager.VoronoiLine);
}
}
}
}
+173
View File
@@ -0,0 +1,173 @@
// -----------------------------------------------------------------------
// <copyright file="MeshRenderer.cs" company="">
// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Rendering.GDI
{
using System.Drawing;
using TriangleNet.Rendering.GDI.Native;
/// <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;
// Draw unchanged points
RenderPoints(points, size, 0, m, Context.ColorManager.Point);
// Draw new (Steiner) points
if (limit > 0)
{
RenderPoints(points, size, m, n, Context.ColorManager.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.WorldToScreen(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 ? Context.ColorManager.GetBrushDictionary() : null;
// TODO: remove hardcoded 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.WorldToScreen(ref tri[0]);
zoom.WorldToScreen(ref tri[1]);
zoom.WorldToScreen(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)
{
Context.ColorManager.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.WorldToScreen(ref p0);
zoom.WorldToScreen(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 contex</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 contex</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 contex</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;
}
}
+252
View File
@@ -0,0 +1,252 @@
// -----------------------------------------------------------------------
// <copyright file="RendererControl.cs" company="">
// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Rendering.GDI
{
using System;
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;
g.DrawString(coordinate, this.Font, Brushes.White, 10, 10);
}
}
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(ref c);
coordinate = String.Format(NumberFormatInfo.InvariantInfo,
"X:{0} Y:{1}", c.X, c.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
}
}
+32
View File
@@ -0,0 +1,32 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Voronoi.Legacy;
public interface IRenderContext
{
ColorManager ColorManager { get; }
BoundingBox Bounds { get; }
IList<IRenderLayer> RenderLayers { get; }
Projection Zoom { get; }
IMesh Mesh { get; }
bool HasData { get; }
void Add(IPolygon data);
void Add(IMesh data, bool reset);
void Add(ICollection<Point> points, IEnumerable<IEdge> edges, bool reset);
void Add(float[] values);
void Add(int[] partition);
void Enable(int layer, bool enabled);
}
}
+26
View File
@@ -0,0 +1,26 @@
// -----------------------------------------------------------------------
// <copyright file="IMeshRenderer.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Rendering
{
using System;
using System.Windows.Forms;
using System.Drawing;
/// <summary>
/// TODO: Update summary.
/// </summary>
public interface IRenderControl
{
IRenderer Renderer { get; set; }
Rectangle ClientRectangle { get; }
void Initialize();
void Refresh();
void HandleResize();
}
}
+44
View File
@@ -0,0 +1,44 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Buffer;
using TriangleNet.Rendering.Util;
using Color = System.Drawing.Color;
public interface IRenderLayer
{
int Count { get; }
// Points can be set, because layers may share vertices.
IBuffer<float> Points { get; }
IBuffer<int> Indices { get; }
bool IsEnabled { get; set; }
bool IsEmpty();
void Reset(bool clear);
// TODO: add boolean: reset
BoundingBox SetPoints(IBuffer<float> buffer);
BoundingBox SetPoints(IPolygon poly);
BoundingBox SetPoints(IMesh mesh);
BoundingBox SetPoints(ICollection<Point> points);
void SetPolygon(IPolygon poly);
void SetPolygon(IMesh mesh);
void SetMesh(IMesh mesh, bool elements);
void SetMesh(IEnumerable<IEdge> edges);
// TODO: better put these into a subclass.
IBuffer<int> Partition { get; }
IBuffer<Color> Colors { get; }
void AttachLayerData(float[] values, ColorMap colormap);
void AttachLayerData(int[] partition);
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace TriangleNet.Rendering
{
public interface IRenderer
{
IRenderContext Context { get; set; }
void Render();
}
}
+271
View File
@@ -0,0 +1,271 @@
// -----------------------------------------------------------------------
// <copyright file="Projection.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Rendering
{
using System;
using System.Drawing;
/// <summary>
/// Manages a world to screen transformation (2D orthographic projection).
/// </summary>
public class Projection
{
// The screen.
Rectangle screen;
// The complete mesh.
RectangleF world;
/// <summary>
/// Gets or sets the current viewport (visible mesh).
/// </summary>
public RectangleF Viewport { get; set; }
/// <summary>
/// Gets the current scale.
/// </summary>
public float Scale
{
get { return screen.Width / Viewport.Width; }
}
/// <summary>
/// Gets the zoom level.
/// </summary>
public int Level { get; private set; }
/// <summary>
/// Gets or sets a clip margin (default is 5% of viewport width on each side).
/// </summary>
public float ClipMargin { get; set; }
// The y-direction of windows screen coordinates is upside down,
// so inverY must be set to true.
bool invertY = false;
int maxZoomLevel = 100;
public Projection(Rectangle screen, bool invertY = true)
{
this.screen = screen;
this.world = screen;
this.Viewport = screen;
this.Level = 1;
this.ClipMargin = this.Viewport.Width * 0.05f;
this.invertY = invertY;
}
/// <summary>
/// Inititialize the projection.
/// </summary>
/// <param name="world">The world that should be transformed to screen coordinates.</param>
public void Initialize(BoundingBox world)
{
this.Level = 1;
// Add a margin so there's some space around the border
float worldMargin = (world.Width < world.Height) ? world.Height * 0.05f : world.Width * 0.05f;
// Get the initial viewport (complete mesh centered on the screen)
float screenRatio = screen.Width / (float)screen.Height;
float worldRatio = world.Width / world.Height;
float scale = (world.Width + worldMargin) / screen.Width;
if (screenRatio > worldRatio)
{
scale = (world.Height + worldMargin) / screen.Height;
}
float centerX = world.Left + world.Width / 2;
float centerY = world.Bottom + world.Height / 2;
// TODO: Add initial margin
this.Viewport = new RectangleF(centerX - screen.Width * scale / 2,
centerY - screen.Height * scale / 2,
screen.Width * scale,
screen.Height * scale);
this.ClipMargin = this.Viewport.Width * 0.05f;
this.world = this.Viewport;
}
/// <summary>
/// Handle resize of the screen.
/// </summary>
/// <param name="newScreen">The new screen dimensions.</param>
public void Resize(Rectangle newScreen)
{
// The viewport has to be updated, but we want to keep
// the scaling and the center.
// Get the screen scaling.
float scaleX = newScreen.Width / (float)screen.Width;
float scaleY = newScreen.Height / (float)screen.Height;
this.screen = newScreen;
var view = this.Viewport;
// Center of the viewport
float centerX = (view.Left + view.Right) / 2;
float centerY = (view.Bottom + view.Top) / 2;
// The new viewport dimensions.
float width = view.Width * scaleX;
float height = view.Height * scaleY;
this.Viewport = new RectangleF(
centerX - width / 2,
centerY - height / 2,
width, height);
// Do the same for the world:
centerX = (world.Left + world.Right) / 2;
centerY = (world.Bottom + world.Top) / 2;
width = world.Width * scaleX;
height = world.Height * scaleY;
this.world = new RectangleF(
centerX - width / 2,
centerY - height / 2,
width, height);
}
public bool Translate(int dx, int dy)
{
if (Level == 1)
{
return false;
}
var view = this.Viewport;
float x = view.X + dx * view.Width / 4;
float y = view.Y + dy * view.Height / 4;
this.Viewport = new RectangleF(x, y, view.Width, view.Height);
return true;
}
/// <summary>
/// Zoom in or out of the viewport.
/// </summary>
/// <param name="amount">Zoom amount</param>
/// <param name="focusX">Relative x point position</param>
/// <param name="focusY">Relative y point position</param>
public bool Zoom(int amount, float focusX, float focusY)
{
float width, height;
if (invertY)
{
focusY = 1 - focusY;
}
if (amount > 0) // Zoom in
{
this.Level++;
if (this.Level > maxZoomLevel)
{
this.Level = maxZoomLevel;
return false;
}
width = Viewport.Width / 1.1f;
height = Viewport.Height / 1.1f;
}
else
{
this.Level--;
if (this.Level < 1)
{
this.Level = 1;
this.Viewport = this.world;
return false;
}
width = Viewport.Width * 1.1f;
height = Viewport.Height * 1.1f;
}
// Current focus on viewport
float x = Viewport.X + Viewport.Width * focusX;
float y = Viewport.Y + Viewport.Height * focusY;
// New left and top positions
x = x - width * focusX;
y = y - height * focusY;
// Check if outside of world
if (x < world.X)
{
x = world.X;
}
else if (x + width > world.Right)
{
x = world.Right - width;
}
if (y < world.Y)
{
y = world.Y;
}
else if (y + height > world.Bottom)
{
y = world.Bottom - height;
}
// Set new viewport
this.Viewport = new RectangleF(x, y, width, height);
this.ClipMargin = this.Viewport.Width * 0.05f;
return true;
}
public void Reset()
{
this.Viewport = this.world;
this.Level = 1;
}
public void WorldToScreen(ref PointF pt)
{
pt.X = (pt.X - Viewport.X) / Viewport.Width * screen.Width;
pt.Y = (1 - (pt.Y - Viewport.Y) / Viewport.Height) * screen.Height;
}
public void ScreenToWorld(ref PointF pt)
{
pt.X = Viewport.X + Viewport.Width * pt.X;
pt.Y = Viewport.Y + Viewport.Height * (1 - pt.Y);
}
[Obsolete]
public PointF WorldToScreen(float x, float y)
{
return new PointF((x - Viewport.X) / Viewport.Width * screen.Width,
(1 - (y - Viewport.Y) / Viewport.Height) * screen.Height);
}
[Obsolete]
public PointF ScreenToWorld(float x, float y)
{
return new PointF(Viewport.X + Viewport.Width * x,
Viewport.Y + Viewport.Height * (1 - y));
}
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Triangle.Rendering")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Triangle.Rendering")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("14f2491b-ee62-41e4-ab93-206540302ece")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+162
View File
@@ -0,0 +1,162 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using System.Linq;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Voronoi.Legacy;
/// <summary>
/// The RenderContext class brings all the rendering parts together.
/// </summary>
public class RenderContext : IRenderContext
{
private ColorManager colorManager;
private BoundingBox bounds;
private Projection zoom;
private IMesh mesh;
private List<IRenderLayer> renderLayers;
public RenderContext(Projection zoom, ColorManager colorManager)
{
bounds = new BoundingBox();
renderLayers = new List<IRenderLayer>(6);
renderLayers.Add(new RenderLayer()); // 0 = mesh (filled)
renderLayers.Add(new RenderLayer()); // 1 = mesh (wireframe)
renderLayers.Add(new RenderLayer()); // 2 = polygon
renderLayers.Add(new RenderLayer()); // 3 = points
renderLayers.Add(new RenderLayer()); // 4 = voronoi overlay
renderLayers.Add(new RenderLayer()); // 5 = vector field
renderLayers.Add(new RenderLayer()); // 6 = contour lines
RenderLayers[1].IsEnabled = true;
RenderLayers[2].IsEnabled = true;
RenderLayers[3].IsEnabled = true;
this.zoom = zoom;
this.colorManager = colorManager;
}
public ColorManager ColorManager
{
get { return colorManager; }
}
public BoundingBox Bounds
{
get { return bounds; }
}
public IList<IRenderLayer> RenderLayers
{
get { return renderLayers; }
}
public Projection Zoom
{
get { return zoom; }
}
public IMesh Mesh
{
get { return mesh; }
}
public bool HasData
{
get
{
return renderLayers.Any(layer => !layer.IsEmpty());
}
}
public void Add(IPolygon data)
{
foreach (var layer in RenderLayers)
{
layer.Reset(true);
}
// Always clear voronoi layer.
RenderLayers[4].Reset(true);
int i = 0;
// Ensure linear numbering of polygon vertices.
foreach (var p in data.Points)
{
p.ID = i++;
}
this.bounds = RenderLayers[2].SetPoints(data);
this.zoom.Initialize(bounds);
RenderLayers[2].SetPolygon(data);
RenderLayers[3].SetPoints(RenderLayers[2].Points);
}
public void Add(IMesh data, bool reset)
{
foreach (var layer in RenderLayers)
{
layer.Reset(reset);
}
// Always clear voronoi layer.
RenderLayers[4].Reset(true);
// Save reference to mesh.
this.mesh = data;
this.bounds = RenderLayers[1].SetPoints(data);
this.zoom.Initialize(bounds);
RenderLayers[1].SetMesh(data, false);
RenderLayers[2].SetPoints(RenderLayers[1].Points);
RenderLayers[2].SetPolygon(data);
RenderLayers[3].SetPoints(RenderLayers[1].Points);
}
public void Add(ICollection<Point> points, IEnumerable<IEdge> edges, bool reset)
{
RenderLayers[4].SetPoints(points);
RenderLayers[4].SetMesh(edges);
RenderLayers[4].IsEnabled = true;
}
public void Add(float[] data)
{
// Add function values for filled mesh.
RenderLayers[0].SetPoints(RenderLayers[1].Points);
RenderLayers[0].SetMesh(this.mesh, true);
RenderLayers[0].AttachLayerData(data, colorManager.ColorMap);
RenderLayers[0].IsEnabled = true;
}
public void Add(int[] data)
{
// Add partition data for filled mesh.
RenderLayers[0].SetPoints(RenderLayers[1].Points);
RenderLayers[0].SetMesh(this.mesh, true);
RenderLayers[0].AttachLayerData(data);
RenderLayers[0].IsEnabled = true;
}
public void Enable(int layer, bool enabled)
{
renderLayers[layer].IsEnabled = enabled;
}
public void Clear()
{
}
}
}
+188
View File
@@ -0,0 +1,188 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Buffer;
using TriangleNet.Rendering.Util;
using Color = System.Drawing.Color;
public class RenderLayer : IRenderLayer
{
int count;
protected IBuffer<float> points;
protected IBuffer<int> indices;
protected IBuffer<int> partition;
protected IBuffer<Color> colors;
public RenderLayer()
{
this.IsEnabled = false;
}
public int Count
{
get { return count; }
}
public IBuffer<float> Points
{
get { return points; }
set { points = value; }
}
public IBuffer<int> Indices
{
get { return indices; }
}
public IBuffer<int> Partition
{
get { return partition; }
}
public IBuffer<Color> Colors
{
get { return colors; }
}
public bool IsEnabled { get; set; }
public bool IsEmpty()
{
return (points == null || points.Count == 0);
}
public void Reset(bool clear)
{
if (clear)
{
count = 0;
points = null;
}
indices = null;
partition = null;
colors = null;
}
public BoundingBox SetPoints(IBuffer<float> buffer)
{
BoundingBox bounds = new BoundingBox();
if (points != null && points.Count < buffer.Count)
{
count = points.Count / points.Size;
}
else
{
count = buffer.Count / buffer.Size;
}
this.points = buffer;
return bounds;
}
public BoundingBox SetPoints(IPolygon poly)
{
BoundingBox bounds = new BoundingBox();
points = BufferHelper.CreateVertexBuffer(poly.Points, ref bounds);
count = points.Count / points.Size;
return bounds;
}
public BoundingBox SetPoints(IMesh mesh)
{
BoundingBox bounds = new BoundingBox();
points = BufferHelper.CreateVertexBuffer(mesh.Vertices, ref bounds);
count = points.Count / points.Size;
return bounds;
}
public BoundingBox SetPoints(ICollection<Point> vertices)
{
BoundingBox bounds = new BoundingBox();
points = BufferHelper.CreateVertexBuffer(vertices, ref bounds);
count = points.Count / points.Size;
return bounds;
}
public void SetPolygon(IPolygon poly)
{
indices = BufferHelper.CreateIndexBuffer(poly.Segments, 2);
}
public void SetPolygon(IMesh mesh)
{
indices = BufferHelper.CreateIndexBuffer(mesh.Segments, 2);
}
public void SetMesh(IEnumerable<IEdge> edges)
{
indices = BufferHelper.CreateIndexBuffer(edges, 2);
}
public void SetMesh(IMesh mesh, bool elements)
{
mesh.Renumber();
if (!elements)
{
indices = BufferHelper.CreateIndexBuffer(mesh.Edges, 2);
}
if (elements || indices.Count == 0)
{
indices = BufferHelper.CreateIndexBuffer(mesh.Triangles, 3);
}
}
// TODO: remove colormap argument
public void AttachLayerData(float[] values, ColorMap colormap)
{
int length = values.Length;
Color[] data = new Color[length];
double min = double.MaxValue;
double max = double.MinValue;
// Find min and max of given values.
for (int i = 0; i < length; i++)
{
if (values[i] < min)
{
min = values[i];
}
if (values[i] > max)
{
max = values[i];
}
}
for (int i = 0; i < length; i++)
{
data[i] = colormap.GetColor(values[i], min, max);
}
colors = new ColorBuffer(data, 1);
}
public void AttachLayerData(int[] partition)
{
this.partition = new IndexBuffer(partition, 1);
}
}
}
+132
View File
@@ -0,0 +1,132 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using System.Windows.Forms;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.GDI;
using TriangleNet.Rendering.Util;
public class RenderManager
{
// TODO: delete
public static bool VORONOI_DEBUG = false;
IRenderControl control;
IRenderContext context;
IRenderer renderer;
Projection zoom;
public IRenderControl Control
{
get { return control; }
}
public IRenderContext Context
{
get { return context; }
}
public RenderManager()
{
}
public RenderManager(IRenderControl control)
{
Initialize(control);
}
public RenderManager(IRenderControl control, IRenderer renderer)
{
Initialize(control, renderer);
}
public void Initialize(IRenderControl control)
{
Initialize(control, new LayerRenderer());
}
public void Initialize(IRenderControl control, IRenderer renderer)
{
this.zoom = new Projection(control.ClientRectangle);
this.context = new RenderContext(zoom, ColorManager.Default());
this.renderer = renderer;
this.renderer.Context = context;
this.control = control;
this.control.Initialize();
this.control.Renderer = renderer;
}
public bool TryCreateControl(string assemblyName, IEnumerable<string> dependencies,
out IRenderControl control)
{
if (!ReflectionHelper.TryCreateControl(assemblyName, dependencies, out control))
{
return false;
}
return control is Control;
}
public void Resize()
{
control.HandleResize();
}
public void Enable(int layer, bool enabled)
{
context.Enable(layer, enabled);
control.Refresh();
}
public void Set(IPolygon data, bool refresh = true)
{
context.Add(data);
if (refresh)
{
control.Refresh();
}
}
public void Set(IMesh data, bool reset, bool refresh = true)
{
context.Add(data, reset);
if (refresh)
{
control.Refresh();
}
}
/// <summary>
/// Set data for Voronoi layer.
/// </summary>
public void Set(ICollection<Point> points, IEnumerable<IEdge> edges, bool reset, bool refresh = true)
{
context.Add(points, edges, reset);
if (refresh)
{
control.Refresh();
}
}
public void Update(float[] values)
{
context.Add(values);
control.Refresh();
}
public void Update(int[] partition)
{
context.Add(partition);
control.Refresh();
}
}
}
@@ -0,0 +1,58 @@
namespace TriangleNet.Rendering.Text
{
using System.Collections.Generic;
using TriangleNet.Geometry;
static class EdgeIterator
{
/// <summary>
/// Enumerate the edges of the mesh.
/// </summary>
/// <param name="mesh"></param>
/// <param name="skipSegments"></param>
/// <returns></returns>
/// <remarks>
/// In contrast to the <see cref="TriangleNet.Meshing.Iterators.EdgeIterator"/> this
/// method will return objects that include the vertex information (and not only the
/// indices).
/// </remarks>
public static IEnumerable<ISegment> EnumerateEdges(Mesh mesh, bool skipSegments = true)
{
foreach (var t in mesh.Triangles)
{
for (int i = 0; i < 3; i++)
{
int nid = t.GetNeighborID(i);
if ((t.ID < nid) || (nid < 0))
{
var s = t.GetSegment(i);
if (skipSegments && s == null)
{
// Since segments will be processed separately, don't
// include them in the enumeration.
yield return new Segment(
t.GetVertex((i + 1) % 3),
t.GetVertex((i + 2) % 3));
}
else
{
if (s == null)
{
yield return new Segment(
t.GetVertex((i + 1) % 3),
t.GetVertex((i + 2) % 3));
}
else
{
yield return s;
}
}
}
}
}
}
}
}
+208
View File
@@ -0,0 +1,208 @@
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;
/// <summary>
/// Gets or sets the document name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the default point size (default = 1).
/// </summary>
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 AddComment(string comment, int line = 1)
{
for (int i = 0; i < line; i++)
{
_w.WriteLine("%");
}
var t = comment.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries );
for (int i = 0; i < t.Length; i++)
{
_w.WriteLine("% " + t[i]);
}
for (int i = 0; i < line; i++)
{
_w.WriteLine("%");
}
}
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("%%Page: 1 1");
_w.WriteLine("save");
// Define points.
_w.WriteLine("% 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("% 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("%%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
}
}
+157
View File
@@ -0,0 +1,157 @@
// -----------------------------------------------------------------------
// <copyright file="EpsImage.cs" company="">
// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/
// Original Matlab code by John Burkardt, Florida State University
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Rendering.Text
{
using System;
using System.IO;
using TriangleNet;
using TriangleNet.Geometry;
using Color = System.Drawing.Color;
using IntPoint = System.Drawing.Point;
using IntRectangle = System.Drawing.Rectangle;
/// <summary>
/// Writes a mesh to an EPS file.
/// </summary>
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;
// 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);
/// <summary>
/// Export the mesh to EPS format.
/// </summary>
/// <param name="mesh">The current mesh.</param>
/// <param name="filename">The EPS filename.</param>
/// <param name="width">The desired width of the image (currently ignored).</param>
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.AddComment("Draw edges.");
eps.SetStroke(0.4f, ColorLines);
foreach (var e in EdgeIterator.EnumerateEdges(mesh))
{
eps.DrawLine(Transform(e.GetVertex(0)), Transform(e.GetVertex(1)));
}
// Draw Segments.
eps.AddComment("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.AddComment("Draw points.");
eps.SetColor(ColorPoints);
foreach (var node in mesh.Vertices)
{
eps.DrawPoint(Transform(node));
}
}
}
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);
}
}
}
}
@@ -0,0 +1,73 @@

namespace TriangleNet.Rendering.Text
{
using System;
using System.Globalization;
using System.IO;
/// <summary>
///
/// </summary>
/// <remarks>
/// From http://stackoverflow.com/questions/12011789/streamwriter-and-iformatprovider
/// </remarks>
public class FormattingStreamWriter : StreamWriter
{
private readonly IFormatProvider formatProvider;
/// <summary>
/// Initializes a new instance of the StreamWriter class for the specified file
/// by using the default encoding and buffer size.
/// </summary>
/// <param name="path">The complete file path to write to.</param>
public FormattingStreamWriter(string path)
: this(path, CultureInfo.InvariantCulture)
{
}
/// <summary>
/// Initializes a new instance of the StreamWriter class for the specified stream
/// by using UTF-8 encoding and the default buffer size.
/// </summary>
/// <param name="stream">The stream to write to.</param>
public FormattingStreamWriter(Stream stream)
: this(stream, CultureInfo.InvariantCulture)
{
}
/// <summary>
/// Initializes a new instance of the StreamWriter class for the specified file
/// by using the default encoding and buffer size.
/// </summary>
/// <param name="path">The complete file path to write to.</param>
/// <param name="formatProvider">The format provider.</param>
public FormattingStreamWriter(string path, IFormatProvider formatProvider)
: base(path)
{
this.formatProvider = formatProvider;
}
/// <summary>
/// Initializes a new instance of the StreamWriter class for the specified stream
/// by using UTF-8 encoding and the default buffer size.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="formatProvider">The format provider.</param>
public FormattingStreamWriter(Stream stream, IFormatProvider formatProvider)
: base(stream)
{
this.formatProvider = formatProvider;
}
/// <summary>
/// Gets an object that controls formatting.
/// </summary>
public override IFormatProvider FormatProvider
{
get
{
return this.formatProvider;
}
}
}
}
+81
View File
@@ -0,0 +1,81 @@

namespace TriangleNet.Rendering.Text
{
using System.Drawing;
/// <summary>
/// Page size in millimeters.
/// </summary>
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;
}
}
}
+254
View File
@@ -0,0 +1,254 @@
// -----------------------------------------------------------------------
// <copyright file="SvgImage.cs" company="">
// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Rendering.Text
{
using System;
using System.IO;
using System.Text;
using TriangleNet;
using TriangleNet.Geometry;
/// <summary>
/// Writes a mesh to an SVG file.
/// </summary>
public class SvgImage
{
// Iterations to insert a linebreak in SVG path.
private const int LINEBREAK_COUNT = 10;
float scale = 1f;
/// <summary>
/// Export the mesh to SVG format.
/// </summary>
/// <param name="mesh">The current mesh.</param>
/// <param name="filename">The SVG filename.</param>
/// <param name="width">The desired width of the image.</param>
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);
int x_offset = -(int)((bounds.Left - margin) * scale);
int y_offset = (int)((bounds.Top + margin) * scale);
int height = (int)((bounds.Height + 2 * margin) * scale);
using (var svg = new FormattingStreamWriter(filename))
{
svg.WriteLine("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"");
svg.WriteLine("\twidth=\"{0}px\" height=\"{1}px\"", width, height);
svg.WriteLine("\tviewBox=\"0 0 {0} {1}\">", width, height);
svg.WriteLine("<g transform=\"translate({0}, {1}) scale(1,-1)\">", x_offset, y_offset);
DrawTriangles(svg, mesh, false);
//DrawEdges(svg, mesh);
DrawSegments(svg, mesh);
DrawPoints(svg, mesh, false);
svg.WriteLine("</g>");
svg.WriteLine("</svg>");
}
}
private void DrawTriangles(StreamWriter svg, Mesh mesh, bool label)
{
svg.Write("\t<path d=\"");
StringBuilder labels = new StringBuilder();
Vertex v1, v2, v3;
double x1, y1, x2, y2, x3, y3, xa, ya;
int i = 1;
foreach (var tri in mesh.Triangles)
{
v1 = tri.GetVertex(0);
v2 = tri.GetVertex(1);
v3 = tri.GetVertex(2);
x1 = scale * v1.X;
y1 = scale * v1.Y;
x2 = scale * v2.X;
y2 = scale * v2.Y;
x3 = scale * v3.X;
y3 = scale * v3.Y;
svg.Write("M {0:0.#},{1:0.#} L {2:0.#},{3:0.#} {4:0.#},{5:0.#} Z ",
x1, y1, x2, y2, x3, y3);
if (i % LINEBREAK_COUNT == 0)
{
svg.WriteLine();
svg.Write("\t");
}
i++;
if (label)
{
xa = (x1 + x2 + x3) / 3.0;
ya = (y1 + y2 + y3) / 3.0;
labels.AppendFormat("<text x=\"{0:0.#}\" y=\"{1:0.#}\">{2}</text>",
xa, ya, tri.ID);
labels.AppendLine();
}
}
svg.WriteLine("\" style=\"stroke:#c2c2c2; fill:none; stroke-linejoin:bevel;\"/>");
// Label the triangles.
if (label)
{
svg.WriteLine("\t<g font-family=\"Verdana\" font-size=\"11\" fill=\"black\">");
svg.Write(labels.ToString());
svg.WriteLine("\t<g/>");
}
}
private void DrawEdges(StreamWriter svg, Mesh mesh)
{
svg.Write("\t<path d=\"");
StringBuilder labels = new StringBuilder();
Vertex v1, v2;
double x1, y1, x2, y2;
int i = 1;
foreach (var e in EdgeIterator.EnumerateEdges(mesh))
{
v1 = e.GetVertex(0);
v2 = e.GetVertex(1);
x1 = scale * v1.X;
y1 = scale * v1.Y;
x2 = scale * v2.X;
y2 = scale * v2.Y;
svg.Write("M {0:0.#},{1:0.#} L {2:0.#},{3:0.#} ",
x1, y1, x2, y2);
if (i % LINEBREAK_COUNT == 0)
{
svg.WriteLine();
svg.Write("\t");
}
i++;
}
svg.WriteLine("\" style=\"stroke:#c2c2c2; fill:none; stroke-linejoin:bevel;\"/>");
}
private void DrawSegments(StreamWriter svg, Mesh mesh)
{
svg.Write("\t<path d=\"");
StringBuilder labels = new StringBuilder();
double x1, y1, x2, y2;
int i = 1;
foreach (var seg in mesh.Segments)
{
x1 = scale * seg.GetVertex(0).X;
y1 = scale * seg.GetVertex(0).Y;
x2 = scale * seg.GetVertex(1).X;
y2 = scale * seg.GetVertex(1).Y;
svg.Write("M {0:0.#},{1:0.#} L {2:0.#},{3:0.#} ",
x1, y1, x2, y2);
if (i % LINEBREAK_COUNT == 0)
{
svg.WriteLine();
svg.Write("\t");
}
i++;
}
svg.WriteLine("\" style=\"stroke:#4682B4; fill:none; stroke-linejoin:bevel; stroke-width:2px;\"/>");
}
private void DrawPoints(StreamWriter svg, Mesh mesh, bool label)
{
int n = mesh.Vertices.Count;
float circle_size = 1.5f;
if (n < 100)
{
circle_size = 3;
}
else if (n < 500)
{
circle_size = 2;
}
svg.WriteLine("\t<g style=\"fill: #006400\">");
double x, y;
StringBuilder labels = new StringBuilder();
foreach (var node in mesh.Vertices)
{
x = scale * node.X;
y = scale * node.Y;
svg.WriteLine("\t\t<circle cx=\"{0:0.#}\" cy=\"{1:0.#}\" r=\"{2:0.#}\" />",
x, y, circle_size);
if (label)
{
labels.AppendFormat("<text x=\"{0:0.#}\" y=\"{1:0.#}\">{2}</text>",
x, y, node.ID);
labels.AppendLine();
}
}
svg.WriteLine("\t</g>");
// Label the nodes.
if (label)
{
svg.WriteLine("\t<g font-family=\"Verdana\" font-size=\"11\" fill=\"black\">");
svg.Write(labels.ToString());
svg.WriteLine("\t<g/>");
}
}
}
}
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{41022E0E-BD0F-439E-BC3A-AABB1B43471B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TriangleNet.Rendering</RootNamespace>
<AssemblyName>Triangle.Rendering</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="BoundingBox.cs" />
<Compile Include="Buffer\BufferBase.cs" />
<Compile Include="Buffer\ColorBuffer.cs" />
<Compile Include="Buffer\IBuffer.cs" />
<Compile Include="Buffer\IndexBuffer.cs" />
<Compile Include="Buffer\VertexBuffer.cs" />
<Compile Include="ColorManager.cs" />
<Compile Include="ExtensionMethods.cs" />
<Compile Include="GDI\FunctionRenderer.cs" />
<Compile Include="GDI\ImageRenderer.cs" />
<Compile Include="GDI\LayerRenderer.cs" />
<Compile Include="GDI\MeshRenderer.cs" />
<Compile Include="GDI\Native\GradientFillMode.cs" />
<Compile Include="GDI\Native\GradientRect.cs" />
<Compile Include="GDI\Native\GradientTriangle.cs" />
<Compile Include="GDI\Native\NativeMethods.cs" />
<Compile Include="GDI\Native\TriVertex.cs" />
<Compile Include="GDI\RenderControl.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="IRenderContext.cs" />
<Compile Include="IRenderControl.cs" />
<Compile Include="IRenderer.cs" />
<Compile Include="IRenderLayer.cs" />
<Compile Include="Projection.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RenderContext.cs" />
<Compile Include="RenderLayer.cs" />
<Compile Include="RenderManager.cs" />
<Compile Include="Text\EdgeIterator.cs" />
<Compile Include="Text\EpsDocument.cs" />
<Compile Include="Text\EpsImage.cs" />
<Compile Include="Text\FormattingStreamWriter.cs" />
<Compile Include="Text\PageSize.cs" />
<Compile Include="Text\SvgImage.cs" />
<Compile Include="Util\BufferHelper.cs" />
<Compile Include="Util\ColorMap.cs" />
<Compile Include="Util\ReflectionHelper.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<ProjectReference Include="..\Triangle\Triangle.csproj">
<Project>{f7907a0a-b75f-400b-9e78-bfad00db4d6b}</Project>
<Name>Triangle</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
+142
View File
@@ -0,0 +1,142 @@

namespace TriangleNet.Rendering.Util
{
using System.Collections.Generic;
using TriangleNet.Topology;
using TriangleNet.Geometry;
using TriangleNet.Rendering.Buffer;
internal static class BufferHelper
{
public static IBuffer<float> CreateVertexBuffer(double[] points, ref BoundingBox bounds)
{
int length = points.Length;
var buffer = new VertexBuffer(length);
bounds.Reset();
var data = buffer.Data;
float x, y;
length = length >> 1;
for (int i = 0; i < length; i++)
{
x = (float)points[2 * i];
y = (float)points[2 * i + 1];
data[2 * i] = x;
data[2 * i + 1] = y;
bounds.Update(x, y);
}
return buffer as IBuffer<float>;
}
public static IBuffer<float> CreateVertexBuffer(ICollection<Point> points, ref BoundingBox bounds)
{
var buffer = new VertexBuffer(2 * points.Count);
bounds.Reset();
var data = buffer.Data;
float x, y;
int i = 0;
foreach (var p in points)
{
x = (float)p.X;
y = (float)p.Y;
data[2 * i] = x;
data[2 * i + 1] = y;
bounds.Update(x, y);
i++;
}
return buffer as IBuffer<float>;
}
public static IBuffer<float> CreateVertexBuffer(ICollection<Vertex> points, ref BoundingBox bounds)
{
var buffer = new VertexBuffer(2 * points.Count);
bounds.Reset();
var data = buffer.Data;
int i = 0;
foreach (var p in points)
{
data[2 * i] = (float)p.X;
data[2 * i + 1] = (float)p.Y;
bounds.Update(p.X, p.Y);
i++;
}
return buffer as IBuffer<float>;
}
public static IBuffer<int> CreateIndexBuffer(IList<IEdge> segments, int size)
{
var buffer = new IndexBuffer(size * segments.Count, size);
var data = buffer.Data;
int i = 0;
foreach (var e in segments)
{
data[size * i + 0] = e.P0;
data[size * i + 1] = e.P1;
i++;
}
return buffer as IBuffer<int>;
}
public static IBuffer<int> CreateIndexBuffer(IEnumerable<IEdge> edges, int size)
{
var data = new List<int>();
foreach (var e in edges)
{
data.Add(e.P0);
data.Add(e.P1);
}
return new IndexBuffer(data.ToArray(), size) as IBuffer<int>;
}
public static IBuffer<int> CreateIndexBuffer(ICollection<Triangle> elements, int size)
{
var buffer = new IndexBuffer(size * elements.Count, size);
var data = buffer.Data;
int i = 0;
foreach (var e in elements)
{
data[size * i + 0] = e.GetVertexID(0);
data[size * i + 1] = e.GetVertexID(1);
data[size * i + 2] = e.GetVertexID(2);
i++;
}
return buffer as IBuffer<int>;
}
}
}
+112
View File
@@ -0,0 +1,112 @@

namespace TriangleNet.Rendering.Util
{
using System;
using System.Drawing;
public class ColorMap
{
#region Colormap definitions
public static ColorMap Jet(int size)
{
ColorMap map = new ColorMap(size);
float v, step = 1.0f / (size - 1);
float[] rgb = new float[3];
for (int i = 0; i < size; i += 1)
{
v = 4 * i * step;
rgb[0] = Math.Min(v - 1.5f, 4.5f - v);
rgb[1] = Math.Min(v - 0.5f, 3.5f - v);
rgb[2] = Math.Min(v + 0.5f, 2.5f - v);
Clamp(rgb, 0.0f, 1.0f);
map.colors[size - i - 1] = ColorFromRgb(rgb[0], rgb[1], rgb[2]);
}
return map;
}
public static ColorMap Hot(int size)
{
ColorMap map = new ColorMap(size);
float v, step = 1.0f / (size - 1);
float[] rgb = new float[3];
for (int i = 0; i < size; i += 1)
{
v = 2.5f * i * step;
rgb[0] = v;
rgb[1] = v - 1;
rgb[2] = 2 * v - 4;
Clamp(rgb, 0.0f, 1.0f);
map.colors[i] = ColorFromRgb(rgb[0], rgb[1], rgb[2]);
}
return map;
}
#endregion
#region Helper
private static Color ColorFromRgb(float r, float g, float b)
{
byte max = byte.MaxValue;
return Color.FromArgb((byte)(r * max), (byte)(g * max), (byte)(b * max));
}
private static void Clamp(float[] values, float min, float max)
{
int n = values.Length;
for (int i = 0; i < n; i += 1)
{
values[i] = Math.Min(max, Math.Max(min, values[i]));
}
}
private static int Clamp(int index, int max)
{
if (index < 0)
{
index = 0;
}
else if (index > max)
{
index = max;
}
return index;
}
#endregion
private Color[] colors;
private ColorMap(int size)
{
this.colors = new Color[size];
}
public ColorMap(Color[] colors)
{
this.colors = colors;
}
public Color GetColor(double value, double min, double max)
{
int n = this.colors.Length;
int i = (int)Math.Floor(n * (max - value) / (max - min));
return this.colors[Clamp(i, n - 1)];
}
}
}
@@ -0,0 +1,81 @@

namespace TriangleNet.Rendering.Util
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
internal static class ReflectionHelper
{
public static bool TryCreateControl(string assemblyName, IEnumerable<string> dependencies,
out IRenderControl control)
{
return TryCreateControl(assemblyName, dependencies, null, out control);
}
public static bool TryCreateControl(string assemblyName, IEnumerable<string> dependencies,
string className, out IRenderControl control)
{
control = null;
if (!FilesExist(assemblyName, dependencies))
{
return false;
}
assemblyName = Path.GetFileNameWithoutExtension(assemblyName);
// Try create render control instance.
try
{
// Load the assembly into the current application domain.
var assembly = Assembly.Load(assemblyName);
// Get all types implementing the IRenderControl interface.
var type = typeof(IRenderControl);
var matches = assembly.GetTypes().Where(s => type.IsAssignableFrom(s));
var match = string.IsNullOrEmpty(className) ? matches.FirstOrDefault()
: matches.Where(s => s.Name == className).FirstOrDefault();
if (match != null)
{
// Create an instance.
control = (IRenderControl)Activator.CreateInstance(match);
}
}
catch (Exception)
{
return false;
}
// Return true if render control was successfully created.
return (control != null);
}
private static bool FilesExist(string assemblyName, IEnumerable<string> dependencies)
{
// Check if assembly exists
if (!File.Exists(assemblyName))
{
return false;
}
// Check if dependencies exists
if (dependencies != null)
{
foreach (var item in dependencies)
{
if (!File.Exists(item))
{
return false;
}
}
}
return true;
}
}
}