Rename source folder.
This commit is contained in:
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
namespace TriangleNet.Rendering
|
||||
{
|
||||
public interface IRenderer
|
||||
{
|
||||
IRenderContext Context { get; set; }
|
||||
|
||||
void Render();
|
||||
}
|
||||
}
|
||||
@@ -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")]
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user