New rendering code check-in

git-svn-id: https://triangle.svn.codeplex.com/svn@75033 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5
This commit is contained in:
SND\wo80_cp
2014-05-31 12:12:08 +00:00
parent a9de99f651
commit 70b25002dc
42 changed files with 2648 additions and 11 deletions
@@ -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,180 @@
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, Brush> brushes)
{
foreach (var brush in brushes.Values)
{
brush.Dispose();
}
}
internal Dictionary<int, Brush> GetBrushDictionary()
{
var brushes = new Dictionary<int, Brush>();
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.FromArgb(200, 0, 0, 255),
Color.FromArgb(200, 255, 0, 0),
Color.FromArgb(200, 0, 255, 255),
Color.FromArgb(200, 255, 255, 0),
Color.FromArgb(200, 255, 0, 255),
Color.FromArgb(200, 0, 255, 0),
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,110 @@
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.IsActive)
{
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)
{
meshRenderer.RenderEdges(layer.Points.Data, layer.Indices.Data, Context.ColorManager.VoronoiLine);
}
}
}
@@ -0,0 +1,168 @@
// -----------------------------------------------------------------------
// <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)
{
g.FillPolygon(brushes[partition[i]], 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,220 @@
// -----------------------------------------------------------------------
// <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.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.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();
}
public void HandleMouseClick(float x, float y, MouseButtons button)
{
if (!initialized) return;
var zoom = this.Renderer.Context.Zoom;
if (button == MouseButtons.Middle)
{
zoom.Reset();
this.Render();
}
else if (button == MouseButtons.Left)
{
timer.Stop();
var nfi = System.Globalization.CultureInfo.InvariantCulture.NumberFormat;
PointF c = new PointF(x / this.Width, y / this.Height);
zoom.ScreenToWorld(ref c);
coordinate = String.Format(nfi, "X:{0} Y:{1}", c.X, c.Y);
this.Invalidate();
timer.Start();
}
}
/// <summary>
/// Zoom to the given location.
/// </summary>
/// <param name="location">The zoom focus.</param>
/// <param name="delta">Indicates whether to zoom in or out.</param>
public void HandleMouseWheel(float x, float y, int delta)
{
if (!initialized) return;
var zoom = this.Renderer.Context.Zoom;
if (zoom.Zoom(delta, x, y))
{
// Redraw
this.Render();
}
}
/// <summary>
/// Update graphics buffer and zoom after a resize.
/// </summary>
public void HandleResize()
{
var zoom = this.Renderer.Context.Zoom;
var bounds = this.Renderer.Context.Bounds;
zoom.Initialize(this.ClientRectangle, bounds);
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);
}
}
#endregion
}
}
@@ -0,0 +1,30 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Tools;
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(IVoronoi voronoi, bool reset);
void Add(float[] values);
void Add(int[] partition);
}
}
@@ -0,0 +1,28 @@
// -----------------------------------------------------------------------
// <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 HandleMouseClick(float x, float y, MouseButtons button);
void HandleMouseWheel(float x, float y, int delta);
void HandleResize();
}
}
@@ -0,0 +1,42 @@
namespace TriangleNet.Rendering
{
using System.Drawing;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Buffer;
using TriangleNet.Rendering.Util;
using TriangleNet.Tools;
public interface IRenderLayer
{
int Count { get; }
// Points can be set, because layers may share vertices.
IBuffer<float> Points { get; }
IBuffer<int> Indices { get; }
bool IsActive { get; set; }
bool IsEmpty { get; }
void Reset(bool clear);
// TODO: add boolean: reset
BoundingBox SetPoints(IBuffer<float> buffer);
BoundingBox SetPoints(IPolygon poly);
BoundingBox SetPoints(IMesh mesh);
BoundingBox SetPoints(IVoronoi voronoi);
void SetPolygon(IPolygon poly);
void SetPolygon(IMesh mesh);
void SetMesh(IMesh mesh, bool elements);
void SetMesh(IVoronoi voronoi);
// 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,214 @@
// -----------------------------------------------------------------------
// <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 = 50;
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;
}
public void Initialize(BoundingBox world)
{
Initialize(this.screen, world);
}
public void Initialize(Rectangle screen, BoundingBox world)
{
this.screen = screen;
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>
/// 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,142 @@
namespace TriangleNet.Rendering
{
using System.Collections.Generic;
using System.Linq;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Tools;
/// <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
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);
}
this.bounds = RenderLayers[2].SetPoints(data);
this.zoom.Initialize(bounds);
RenderLayers[2].SetPolygon(data);
RenderLayers[2].IsActive = true;
RenderLayers[3].SetPoints(RenderLayers[2].Points);
RenderLayers[3].IsActive = true;
}
public void Add(IMesh data, bool reset)
{
foreach (var layer in RenderLayers)
{
layer.Reset(reset);
}
// Save reference to mesh.
this.mesh = data;
this.bounds = RenderLayers[1].SetPoints(data);
this.zoom.Initialize(bounds);
RenderLayers[1].SetMesh(data, false);
RenderLayers[1].IsActive = true;
RenderLayers[2].SetPoints(RenderLayers[1].Points);
RenderLayers[2].SetPolygon(data);
RenderLayers[2].IsActive = true;
RenderLayers[3].SetPoints(RenderLayers[1].Points);
RenderLayers[3].IsActive = true;
}
// Voronoi
public void Add(IVoronoi voronoi, bool reset)
{
RenderLayers[4].SetPoints(voronoi);
RenderLayers[4].SetMesh(voronoi);
RenderLayers[4].IsActive = 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].IsActive = 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].IsActive = true;
}
}
}
@@ -0,0 +1,187 @@
namespace TriangleNet.Rendering
{
using System.Drawing;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Buffer;
using TriangleNet.Rendering.Util;
using TriangleNet.Tools;
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.IsActive = 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 IsActive { get; set; }
public bool IsEmpty
{
get { 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(IVoronoi voronoi)
{
BoundingBox bounds = new BoundingBox();
points = BufferHelper.CreateVertexBuffer(voronoi.Points, 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(IVoronoi voronoi)
{
indices = BufferHelper.CreateIndexBuffer(voronoi.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,131 @@
namespace TriangleNet.Rendering
{
using System.Windows.Forms;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.GDI;
using TriangleNet.Tools;
using System.Collections.Generic;
using TriangleNet.Rendering.Util;
public class RenderManager
{
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 Click(float x, float y, MouseButtons button)
{
control.HandleMouseClick(x, y, button);
}
public void Zoom(float x, float y, int delta)
{
control.HandleMouseWheel(x, y, delta);
}
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();
}
}
// Voronoi
public void Set(IVoronoi voronoi, bool reset, bool refresh = true)
{
context.Add(voronoi, 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,91 @@
<?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\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="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,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -0,0 +1,142 @@
namespace TriangleNet.Rendering.Util
{
using System.Collections.Generic;
using TriangleNet.Data;
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(Point[] points, ref BoundingBox bounds)
{
var buffer = new VertexBuffer(2 * points.Length);
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.P0;
data[size * i + 1] = e.P1;
data[size * i + 2] = e.P2;
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;
}
}
}
+18 -3
View File
@@ -1,15 +1,17 @@
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Triangle", "Triangle\Triangle.csproj", "{F7907A0A-B75F-400B-9E78-BFAD00DB4D6B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mesh Explorer", "TestApp\Mesh Explorer.csproj", "{336AAF8A-5316-4303-9E73-5E38BD0B28AF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeshRenderer.Core", "MeshRenderer.Core\MeshRenderer.Core.csproj", "{9C5040DA-C739-43A1-8540-E6BD3ED6DB55}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Triangle.Rendering", "Triangle.Rendering\Triangle.Rendering.csproj", "{41022E0E-BD0F-439E-BC3A-AABB1B43471B}"
EndProject
Global
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 4
SccNumberOfProjects = 5
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = https://tfs.codeplex.com/tfs/tfs06
SccLocalPath0 = .
@@ -22,6 +24,9 @@ Global
SccProjectUniqueName3 = MeshRenderer.Core\\MeshRenderer.Core.csproj
SccProjectName3 = MeshRenderer.Core
SccLocalPath3 = MeshRenderer.Core
SccProjectUniqueName4 = Triangle.Rendering\\Triangle.Rendering.csproj
SccProjectName4 = Triangle.Rendering
SccLocalPath4 = Triangle.Rendering
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -62,6 +67,16 @@ Global
{9C5040DA-C739-43A1-8540-E6BD3ED6DB55}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{9C5040DA-C739-43A1-8540-E6BD3ED6DB55}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{9C5040DA-C739-43A1-8540-E6BD3ED6DB55}.Release|x86.ActiveCfg = Release|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Debug|x86.ActiveCfg = Debug|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Release|Any CPU.Build.0 = Release|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{41022E0E-BD0F-439E-BC3A-AABB1B43471B}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+2 -2
View File
@@ -1,5 +1,5 @@
// -----------------------------------------------------------------------
// <copyright file="BoundingBox.cs" company="">
// <copyright file="Rectangle.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
@@ -150,7 +150,7 @@ namespace TriangleNet.Geometry
/// <returns>Return true, if bounding box contains given point.</returns>
public bool Contains(Point pt)
{
return ((pt.x >= xmin) && (pt.x <= xmax) && (pt.y >= ymin) && (pt.y <= ymax));
return ((pt.X >= xmin) && (pt.X <= xmax) && (pt.Y >= ymin) && (pt.Y <= ymax));
}
/// <summary>
+1 -1
View File
@@ -1,5 +1,5 @@
// -----------------------------------------------------------------------
// <copyright file="SimpleLog.cs" company="">
// <copyright file="Log.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
@@ -42,7 +42,7 @@ namespace TriangleNet.Meshing.Algorithm
if (Log.Verbose)
{
Log.Instance.Warning("A duplicate vertex appeared and was ignored.",
"Incremental.IncrementalDelaunay()");
"Incremental.Triangulate()");
}
v.type = VertexType.UndeadVertex;
mesh.undeads++;
@@ -78,6 +78,19 @@ namespace TriangleNet.Meshing
return mesh;
}
/// <summary>
/// Generates a structured mesh with bounds (0, 0, width, height).
/// </summary>
/// <param name="width">Width of the mesh (must be > 0).</param>
/// <param name="height">Height of the mesh (must be > 0).</param>
/// <param name="nx">Number of segments in x direction.</param>
/// <param name="ny">Number of segments in y direction.</param>
/// <returns>Mesh</returns>
public IMesh StructurdMesh(double width, double height, int nx, int ny)
{
return StructurdMesh(new Rectangle(0.0, 0.0, width, height), nx, ny);
}
/// <summary>
/// Generates a structured mesh.
/// </summary>
+1
View File
@@ -15,6 +15,7 @@ namespace TriangleNet.Meshing
Rectangle Bounds { get; }
void Renumber();
void Refine(QualityOptions quality);
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// -----------------------------------------------------------------------
// <copyright file="Primitives.cs">
// <copyright file="RobustPredicates.cs">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
@@ -70,6 +70,11 @@ namespace TriangleNet.Tools
get { return regions; }
}
public IEnumerable<IEdge> Edges
{
get { return EnumerateEdges(); }
}
/// <summary>
/// Computes the bounded voronoi diagram.
/// </summary>
@@ -644,5 +649,41 @@ namespace TriangleNet.Tools
// Success.
return true;
}
// TODO: Voronoi enumerate edges
private IEnumerable<IEdge> EnumerateEdges()
{
// Copy edges
Point first, last;
var edges = new List<IEdge>(this.Regions.Count * 2);
foreach (var region in this.Regions)
{
first = null;
last = null;
foreach (var pt in region.Vertices)
{
if (first == null)
{
first = pt;
last = pt;
}
else
{
edges.Add(new Edge(last.ID, pt.ID));
last = pt;
}
}
if (region.Bounded && first != null)
{
edges.Add(new Edge(last.ID, first.ID));
}
}
return edges;
}
}
}
+5
View File
@@ -23,5 +23,10 @@ namespace TriangleNet.Tools
/// Gets the list of Voronoi regions.
/// </summary>
ICollection<VoronoiRegion> Regions { get; }
/// <summary>
/// Gets the list of edges.
/// </summary>
IEnumerable<IEdge> Edges { get; }
}
}
+41
View File
@@ -59,6 +59,11 @@ namespace TriangleNet.Tools
get { return regions.Values; }
}
public IEnumerable<IEdge> Edges
{
get { return EnumerateEdges(); }
}
/// <summary>
/// Gets the Voronoi diagram as raw output data.
/// </summary>
@@ -336,5 +341,41 @@ namespace TriangleNet.Tools
return true;
}
// TODO: Voronoi enumerate edges
private IEnumerable<IEdge> EnumerateEdges()
{
// Copy edges
Point first, last;
var edges = new List<IEdge>(this.Regions.Count * 2);
foreach (var region in this.Regions)
{
first = null;
last = null;
foreach (var pt in region.Vertices)
{
if (first == null)
{
first = pt;
last = pt;
}
else
{
edges.Add(new Edge(last.ID, pt.ID));
last = pt;
}
}
if (region.Bounded && first != null)
{
edges.Add(new Edge(last.ID, first.ID));
}
}
return edges;
}
}
}
+1 -3
View File
@@ -108,9 +108,7 @@
<Compile Include="Tools\VoronoiRegion.cs" />
<Compile Include="TriangleLocator.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Algorithm\" />
</ItemGroup>
<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.