Replaced old test app with mesh explorer

Removed MeshData class (use InputGeometry for mesh input)
Direct access to mesh geometry using public properties
Lots of smaller changes

git-svn-id: https://triangle.svn.codeplex.com/svn@67719 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5
This commit is contained in:
SND\wo80_cp
2012-05-31 10:58:38 +00:00
parent 60cfbcda4e
commit c2fdcea816
65 changed files with 5739 additions and 3644 deletions
+113 -68
View File
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TestApp.Controls
namespace MeshExplorer.Controls
{
using System;
using System.Collections.Generic;
@@ -12,10 +12,16 @@ namespace TestApp.Controls
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Drawing.Text;
/// <summary>
/// TODO: Update summary.
/// Displays an angle histogram.
/// </summary>
/// <remarks>
/// The angle histogram is divided into two parts:
/// the minimum angles on the left side (0 to 60 degrees) and
/// the maximum angles on the right (60 to 180 degrees)
/// </remarks>
public class AngleHistogram : Control
{
#region Designer
@@ -53,102 +59,141 @@ namespace TestApp.Controls
#endregion
int[] data;
int max = 0;
int[] maxAngles;
int[] minAngles;
Brush fillBlue1 = new SolidBrush(Color.FromArgb(60, 100, 140));
Brush fillBlue2 = new SolidBrush(Color.FromArgb(110, 150, 200));
Brush textBack = new SolidBrush(Color.FromArgb(72, 0, 0, 0));
// The maximum number of angles
int maxAngleCount = 0;
public AngleHistogram()
{
this.BackColor = Color.FromArgb(76, 76, 76);
this.BackColor = ColorScheme.ColorGray78;
InitializeComponent();
}
public void SetData(int[] data)
public void SetData(int[] dataMin, int[] dataMax)
{
maxAngleCount = 0;
this.minAngles = dataMin;
this.maxAngles = dataMax;
ParseData(dataMin);
ParseData(dataMax);
if (maxAngleCount == 0)
{
this.maxAngles = null;
return;
}
this.Invalidate();
}
private void ParseData(int[] data)
{
if (data != null)
{
this.data = data;
this.max = 0;
for (int i = 0; i < data.Length; i++)
{
if (data[i] > max)
if (data[i] > maxAngleCount)
{
max = data[i];
maxAngleCount = data[i];
}
}
if (max == 0)
{
this.data = null;
return;
}
double lg10 = Math.Ceiling(Math.Log10(max)) - 1;
int norm = (int)Math.Pow(10, lg10);
int mod = -max % norm;
max = max + mod + norm;
this.Invalidate();
}
}
int padding = 1;
int paddingBottom = 0;
int paddingTop = 15;
private void DrawHistogram(Graphics g, int offset, int left, int size, int[] data, Brush brush, Brush brushTop)
{
int count = maxAngleCount;
int totalHeight = this.Height - paddingBottom - paddingTop;
int n = offset == 0 ? data.Length / 3 : data.Length;
float value = 0;
for (int i = offset; i < n; i++)
{
if (data[i] > 0)
{
// Scale to control height
value = totalHeight * data[i] / count;
// Fill bar
g.FillRectangle(brush,
left + i * size, this.Height - paddingBottom - value,
size - 1, value);
// Draw top of bar (just a little effect ...)
if (value > 2)
{
g.FillRectangle(brushTop,
left + i * size, this.Height - paddingBottom - value,
size - 1, 2);
}
}
}
}
private void DrawStrings(Graphics g, SizeF fSize, int size, int middle)
{
int fHeight = (int)(fSize.Height + 2);
g.FillRectangle(textBack, 0, this.Height - fHeight, this.Width, fHeight);
g.DrawString("0", this.Font, Brushes.White, padding, this.Height - fSize.Height - 1);
g.DrawString("60", this.Font, Brushes.White,
this.minAngles.Length * size / 3.0f - 2 * fSize.Width,
this.Height - fSize.Height - 1);
g.DrawString("60", this.Font, Brushes.White, middle, this.Height - fSize.Height - 1);
g.DrawString("180", this.Font, Brushes.White,
this.Width - 3 * fSize.Width,
this.Height - fSize.Height - 1);
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
SizeF s1 = g.MeasureString("180", base.Font, this.Width);
SizeF s2 = g.MeasureString(max.ToString(), base.Font, this.Width);
// Draw bottom rect
g.FillRectangle(Brushes.DimGray, s2.Width, this.Height - s1.Height - 4, this.Width, s1.Height + 4);
// Draw Histogram
if (data != null)
if (this.minAngles == null || this.maxAngles == null)
{
int n = data.Length;
float width = (this.Width - s2.Width) / n;
float value = 0;
for (int i = 0; i < data.Length; i++)
{
if (data[i] > 0)
{
// Scale to control height
value = (this.Height - s1.Height - 4) * data[i] / max;
g.FillRectangle(Brushes.DarkGreen,
s2.Width + i * width + width / 8,
this.Height - s1.Height - 4 - value,
3 * width / 4,
value);
if (value > 2)
{
g.FillRectangle(Brushes.Green,
s2.Width + i * width + width / 8,
this.Height - s1.Height - 4 - value,
3 * width / 4,
2);
}
}
}
return;
}
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
SizeF fSize = g.MeasureString("0", this.Font, this.Width);
// Draw data keys
g.DrawString("0", this.Font, Brushes.White, s2.Width + 2, this.Height - s1.Height - 2);
g.DrawString("90", this.Font, Brushes.White, this.Width / 2 - s1.Width / 3, this.Height - s1.Height - 2);
g.DrawString("180", this.Font, Brushes.White, this.Width - s1.Width - 2, this.Height - s1.Height - 2);
int n = this.minAngles.Length;
// Draw data values
if (max > 0)
// Hack --- TODO: Change stats class
if (n != this.maxAngles.Length)
{
g.DrawString(max.ToString(), this.Font, Brushes.White, 2, 10);
g.DrawString((max / 2).ToString(), this.Font, Brushes.White, 2, (this.Height - s1.Height) / 2);
n = this.minAngles.Length + this.maxAngles.Length;
}
// Each bar takes up this space
int size = (this.Width - 2 * padding) / (n + 1);
// Make pixel align
int middle = this.Width - padding - n * size;
DrawHistogram(g, 0, padding, size, this.minAngles, Brushes.DarkGreen, Brushes.Green);
DrawHistogram(g, n / 3, middle, size, this.maxAngles, fillBlue1, fillBlue2);
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
DrawStrings(g, fSize, size, middle + n / 3 * size);
}
}
}
@@ -0,0 +1,40 @@
// -----------------------------------------------------------------------
// <copyright file="ColorScheme.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace MeshExplorer.Controls
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
/// <summary>
/// TODO: Update summary.
/// </summary>
public static class ColorScheme
{
public static Color ColorGray13 = Color.FromArgb(13, 13, 13);
public static Color ColorGray46 = Color.FromArgb(46, 46, 46);
public static Color ColorGray64 = Color.FromArgb(64, 64, 64);
public static Color ColorGray68 = Color.FromArgb(68, 68, 68);
public static Color ColorGray78 = Color.FromArgb(78, 78, 78);
public static Color ColorGray89 = Color.FromArgb(89, 89, 89);
public static Color ColorGray98 = Color.FromArgb(98, 98, 98);
public static Color ColorGray107 = Color.FromArgb(107, 107, 107);
public static Color ColorGray110 = Color.FromArgb(110, 110, 110);
public static Color ColorGray122 = Color.FromArgb(122, 122, 122);
public static Brush BrushGray68 = new SolidBrush(ColorGray68);
public static Brush BrushGray78 = new SolidBrush(ColorGray78);
// Linear gradient horizontal
public static Brush SliderBorderBrush = new SolidBrush(ColorGray46);
public static Brush SliderFillBrush = new SolidBrush(ColorGray89);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
namespace TestApp.Controls
namespace MeshExplorer.Controls
{
using System;
using System.Collections.Generic;
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TestApp.Controls
namespace MeshExplorer.Controls
{
using System;
using System.Collections.Generic;
@@ -107,7 +107,7 @@ namespace TestApp.Controls
else
brushBorder = new Pen(Color.FromArgb(38, 38, 38), 1f);
brushOuter = new LinearGradientBrush(newRect, Color.FromArgb(82, 82, 82), Color.FromArgb(96, 96, 96), mode);
brushOuter = new LinearGradientBrush(newRect, ColorScheme.ColorGray107, ColorScheme.ColorGray110, mode);
e.Graphics.FillRectangle(brushOuter, newRect);
newRect = new Rectangle(2, y + 1, boxSize - 3, boxSize - 3);
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace MeshExplorer.Controls
{
public class DarkListBox : ListBox
{
Font _boldFont;
public DarkListBox()
{
_boldFont = new Font(base.Font.FontFamily, base.Font.Size, FontStyle.Bold);
this.DrawMode = DrawMode.OwnerDrawVariable;
this.ItemHeight = 22;
this.FontChanged += new EventHandler(ListBoxFontChanged);
this.BackColor = Color.FromArgb(96, 96, 96);
}
void ListBoxFontChanged(object sender, EventArgs e)
{
_boldFont = new Font(base.Font.FontFamily, base.Font.Size, FontStyle.Bold);
}
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
e.ItemHeight = 22;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (this.Items.Count == 0)
{
return;
}
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
int index = e.Index;
string content = "[Error]";
if (index < this.Items.Count && index >= 0)
{
content = this.Items[index].ToString();
}
Color color = (e.Index % 2) == 0 ? Color.FromArgb(85, 85, 85) : Color.FromArgb(90, 90, 90);
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
color = Color.FromArgb(100, 105, 110);
}
using (SolidBrush background = new SolidBrush(color))
{
e.Graphics.FillRectangle(background, e.Bounds);
}
using (SolidBrush pen = new SolidBrush(Color.White))
{
e.Graphics.DrawString(content, this.Font, pen,
new PointF(10, e.Bounds.Y + 3), StringFormat.GenericDefault);
}
}
}
}
+430
View File
@@ -0,0 +1,430 @@
// -----------------------------------------------------------------------
// <copyright file="SliderDark.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace MeshExplorer.Controls
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
// CodeProject: Owner-drawn trackbar (slider), Michal Brylka
/// <summary>
/// Encapsulates control that visualy displays certain integer value and allows user to change
/// it within desired range. It imitates <see cref="System.Windows.Forms.TrackBar"/> as far as
/// mouse usage is concerned.
/// </summary>
public class DarkSlider : Control
{
#region Designer
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
#endregion
#region Events
/// <summary>
/// Fires when Slider position has changed
/// </summary>
public event EventHandler ValueChanging;
private void OnValueChanging()
{
var evt = ValueChanging;
if (evt != null)
{
evt(this, EventArgs.Empty);
}
}
/// <summary>
/// Fires when Slider position has changed
/// </summary>
public event EventHandler ValueChanged;
private void OnValueChanged()
{
var evt = ValueChanged;
if (evt != null)
{
evt(this, EventArgs.Empty);
}
}
#endregion
#region Properties
int thumbSize = 7;
private Rectangle thumbRect; //bounding rectangle of thumb area
private Rectangle barRect; //bounding rectangle of bar area
private bool mouseInThumbRegion = false;
private int trackerValue = 50;
/// <summary>
/// Gets or sets the value of Slider.
/// </summary>
/// <value>The value.</value>
public int Value
{
get { return trackerValue; }
set
{
if (value >= barMinimum & value <= barMaximum)
{
trackerValue = value;
if (ValueChanged != null) ValueChanged(this, new EventArgs());
Invalidate();
}
// ArgumentOutOfRangeException("Value is outside appropriate range (min, max)");
}
}
private int barMinimum = 0;
/// <summary>
/// Gets or sets the minimum value.
/// </summary>
/// <value>The minimum value.</value>
public int Minimum
{
get { return barMinimum; }
set
{
if (value < barMaximum)
{
barMinimum = value;
if (trackerValue < barMinimum)
{
trackerValue = barMinimum;
if (ValueChanged != null) ValueChanged(this, new EventArgs());
}
Invalidate();
}
// ArgumentOutOfRangeException("Minimal value is greather than maximal one");
}
}
private int barMaximum = 100;
/// <summary>
/// Gets or sets the maximum value.
/// </summary>
/// <value>The maximum value.</value>
public int Maximum
{
get { return barMaximum; }
set
{
if (value > barMinimum)
{
barMaximum = value;
if (trackerValue > barMaximum)
{
trackerValue = barMaximum;
if (ValueChanged != null) ValueChanged(this, new EventArgs());
}
Invalidate();
}
// ArgumentOutOfRangeException("Maximal value is lower than minimal one");
}
}
private uint criticalPercent = 0;
/// <summary>
/// Gets or sets trackbar's small change. It affects how to behave when directional keys are pressed
/// </summary>
/// <value>The small change value.</value>
public uint CriticalPercent
{
get { return criticalPercent; }
set { criticalPercent = value; }
}
private Color thumbOuterColor = Color.White;
private Color thumbInnerColor = Color.Gainsboro;
private Color thumbPenColor = Color.Silver;
private Color barOuterColor = Color.SkyBlue;
private Color barInnerColor = Color.DarkSlateBlue;
private Color barPenColor = Color.Gainsboro;
private Color elapsedOuterColor = Color.DarkGreen;
private Color elapsedInnerColor = Color.Chartreuse;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ColorSlider"/> class.
/// </summary>
public DarkSlider()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.OptimizedDoubleBuffer
| ControlStyles.ResizeRedraw
| ControlStyles.Selectable
| ControlStyles.SupportsTransparentBackColor
| ControlStyles.UserMouse
| ControlStyles.UserPaint, true);
BackColor = Color.Transparent;
Minimum = 0;
Maximum = 100;
Value = 50;
}
#endregion
#region Paint
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.Paint"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"></see> that contains the event data.</param>
protected override void OnPaint(PaintEventArgs e)
{
if (!Enabled)
{
DrawColorSlider(e.Graphics);
}
else
{
//if (mouseEffects && mouseInRegion)
//{
// Color[] lightenedColors = LightenColors(thumbOuterColor, thumbInnerColor, thumbPenColor,
// barOuterColor, barInnerColor, barPenColor,
// elapsedOuterColor, elapsedInnerColor);
// DrawColorSlider(e, lightenedColors[0], lightenedColors[1], lightenedColors[2], lightenedColors[3],
// lightenedColors[4], lightenedColors[5], lightenedColors[6], lightenedColors[7]);
//}
//else
{
DrawColorSlider(e.Graphics);
}
}
}
/// <summary>
/// Draws the colorslider control using passed colors.
/// </summary>
/// <param name="e">The <see cref="T:System.Windows.Forms.PaintEventArgs"/> instance containing the event data.</param>
/// <param name="thumbOuterColorPaint">The thumb outer color paint.</param>
/// <param name="thumbInnerColorPaint">The thumb inner color paint.</param>
/// <param name="thumbPenColorPaint">The thumb pen color paint.</param>
/// <param name="barOuterColorPaint">The bar outer color paint.</param>
/// <param name="barInnerColorPaint">The bar inner color paint.</param>
/// <param name="barPenColorPaint">The bar pen color paint.</param>
/// <param name="elapsedOuterColorPaint">The elapsed outer color paint.</param>
/// <param name="elapsedInnerColorPaint">The elapsed inner color paint.</param>
private void DrawColorSlider(Graphics g)
{
try
{
//set up thumbRect aproprietly
int track = (((trackerValue - barMinimum) * (ClientRectangle.Width - thumbSize)) / (barMaximum - barMinimum));
thumbRect = new Rectangle(track, this.Height / 2 - 3, thumbSize - 1, 10);
//adjust drawing rects
barRect = new Rectangle(1, this.Height / 2, this.Width - 2, 5);
//get thumb shape path
GraphicsPath thumbPath = new GraphicsPath();
thumbPath.AddPolygon(new Point[] {
new Point(thumbRect.Left, thumbRect.Top),
new Point(thumbRect.Right, thumbRect.Top),
new Point(thumbRect.Right, thumbRect.Bottom - 4),
new Point(thumbRect.Left + thumbRect.Width / 2, thumbRect.Bottom),
new Point(thumbRect.Left, thumbRect.Bottom - 4)
});
Brush sliderLGBrushH = new LinearGradientBrush(barRect, ColorScheme.ColorGray122,
ColorScheme.ColorGray107, LinearGradientMode.Horizontal);
Brush barFill = (criticalPercent > 0 && trackerValue > criticalPercent) ? Brushes.Peru : Brushes.Green;
//draw bar
{
// Background gradient
g.FillRectangle(sliderLGBrushH, barRect);
// Background fill
g.FillRectangle(ColorScheme.SliderBorderBrush,
barRect.Left + 1, barRect.Top, barRect.Width - 2, barRect.Height - 1);
// Bar fill
g.FillRectangle(ColorScheme.SliderFillBrush,
barRect.Left + 2, barRect.Top + 1, barRect.Width - 4, barRect.Height - 3);
// Elapsed bar fill
g.FillRectangle(barFill,
barRect.Left + 2, barRect.Top + 1, thumbRect.Left + thumbSize / 2 - 2, barRect.Height - 3);
//draw bar band
//g.DrawRectangle(barPen, barRect);
}
sliderLGBrushH.Dispose();
//draw thumb
Brush brushInner = new LinearGradientBrush(thumbRect,
Color.FromArgb(111, 111, 111), Color.FromArgb(80, 80, 80),
LinearGradientMode.Vertical);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPath(brushInner, thumbPath);
g.DrawPath(Pens.Black, thumbPath);
brushInner.Dispose();
//draw thumb band
//Color newThumbPenColor = thumbPenColorPaint;
//if (mouseEffects && (Capture || mouseInThumbRegion))
// newThumbPenColor = ControlPaint.Dark(newThumbPenColor);
//g.DrawPath(thumbPen, thumbPath);
}
catch (Exception)
{ }
finally
{ }
}
#endregion
#region Overided events
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.EnabledChanged"></see> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
Invalidate();
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseLeave"></see> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
mouseInThumbRegion = false;
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseDown"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left)
{
this.Capture = true;
OnValueChanging();
OnMouseMove(e);
}
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
mouseInThumbRegion = thumbRect.Contains(e.Location);
if (Capture & e.Button == MouseButtons.Left)
{
Point pt = e.Location;
int p = pt.X;
int margin = thumbSize >> 1;
p -= margin;
float coef = (float)(barMaximum - barMinimum) /
(float)(ClientSize.Width - 2 * margin);
trackerValue = (int)(p * coef + barMinimum);
if (trackerValue <= barMinimum)
{
trackerValue = barMinimum;
}
else if (trackerValue >= barMaximum)
{
trackerValue = barMaximum;
}
OnValueChanging();
}
Invalidate();
}
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"></see> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
this.Capture = false;
mouseInThumbRegion = thumbRect.Contains(e.Location);
OnValueChanged();
Invalidate();
}
#endregion
#region Help routines
/// <summary>
/// Sets the trackbar value so that it wont exceed allowed range.
/// </summary>
/// <param name="val">The value.</param>
private void SetProperValue(int val)
{
if (val < barMinimum) Value = barMinimum;
else if (val > barMaximum) Value = barMaximum;
else Value = val;
}
#endregion
}
}
@@ -0,0 +1,182 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Text;
// CodeProject: A .NET Flat TabControl (CustomDraw), Oscar Londono
namespace MeshExplorer.Controls
{
/// <summary>
/// Summary description for FlatTabControl.
/// </summary>
public class DarkTabControl : System.Windows.Forms.TabControl
{
#region Designer
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
#endregion
private const int margin = 5;
private Color backColor = ColorScheme.ColorGray68;
public DarkTabControl()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
base.Multiline = false;
// double buffering
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SelectedIndexChanged += (obj, evt) => { Invalidate(); };
}
#region Properties
new public TabAlignment Alignment
{
get { return base.Alignment; }
set
{
TabAlignment ta = value;
if ((ta != TabAlignment.Top) && (ta != TabAlignment.Bottom))
{
ta = TabAlignment.Top;
}
base.Alignment = ta;
}
}
public override Color BackColor
{
get
{
return backColor;
}
set
{
base.BackColor = backColor;
}
}
#endregion
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawControl(e.Graphics);
}
private void DrawControl(Graphics g)
{
if (!Visible)
{
return;
}
Rectangle controlBounds = this.ClientRectangle;
Rectangle tabBounds = this.DisplayRectangle;
// Fill client area
Brush br = new SolidBrush(this.BackColor);
g.FillRectangle(br, controlBounds);
br.Dispose();
int width = tabBounds.Width + margin;
// Clip region for drawing tabs
Region clip = g.Clip;
Rectangle region = new Rectangle(tabBounds.Left, controlBounds.Top, width - margin, controlBounds.Height);
g.SetClip(region);
// Draw tabs
for (int i = 0; i < this.TabCount; i++)
{
DrawTab(g, this.TabPages[i], i);
}
g.Clip = clip;
}
private void DrawTab(Graphics g, TabPage tabPage, int index)
{
Rectangle tabBounds = this.GetTabRect(index);
bool selected = (this.SelectedIndex == index);
// Fill this tab with background color
g.FillRectangle(selected ? Brushes.DimGray : ColorScheme.BrushGray68, tabBounds);
if (selected)
{
// Clear bottom lines
Pen pen = new Pen(tabPage.BackColor);
switch (this.Alignment)
{
case TabAlignment.Top:
g.DrawLine(pen, tabBounds.Left, tabBounds.Bottom, tabBounds.Right - 1, tabBounds.Bottom);
g.DrawLine(pen, tabBounds.Left, tabBounds.Bottom + 1, tabBounds.Right - 1, tabBounds.Bottom + 1);
break;
case TabAlignment.Bottom:
g.DrawLine(pen, tabBounds.Left, tabBounds.Top, tabBounds.Right - 1, tabBounds.Top);
g.DrawLine(pen, tabBounds.Left, tabBounds.Top - 1, tabBounds.Right - 1, tabBounds.Top - 1);
g.DrawLine(pen, tabBounds.Left, tabBounds.Top - 2, tabBounds.Right - 1, tabBounds.Top - 2);
break;
}
pen.Dispose();
}
// Draw string
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
g.DrawString(tabPage.Text, Font, Brushes.White, tabBounds, stringFormat);
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TestApp.Controls
namespace MeshExplorer.Controls
{
using System;
using System.Collections.Generic;
@@ -0,0 +1,66 @@
// -----------------------------------------------------------------------
// <copyright file="DarkToolStripRenderer.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace MeshExplorer.Controls
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class DarkToolStripRenderer : ToolStripRenderer
{
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
{
e.TextColor = e.Item.Enabled ? Color.White : Color.Gray;
base.OnRenderItemText(e);
}
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{
if (!e.Item.Selected && !e.Item.Pressed)
{
e.ArrowColor = ColorScheme.ColorGray89;
}
base.OnRenderArrow(e);
}
protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e)
{
e.Graphics.FillRectangle(ColorScheme.BrushGray78, 0, 2, e.Item.Width, 1);
}
protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e)
{
if (e.Item.Enabled)
{
if (e.Item.Selected || e.Item.Pressed)
{
e.Graphics.FillRectangle(Brushes.DimGray, 0, 0, e.Item.Width, e.Item.Height);
}
else
{
e.Graphics.FillRectangle(ColorScheme.BrushGray68, 0, 0, e.Item.Width, e.Item.Height);
}
}
//base.OnRenderMenuItemBackground(e);
}
protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e)
{
e.Graphics.FillRectangle(ColorScheme.BrushGray68, e.AffectedBounds);
//base.OnRenderToolStripBackground(e);
}
}
}
+107 -89
View File
@@ -4,24 +4,24 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TestApp
namespace MeshExplorer.Controls
{
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using TriangleNet;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using MeshExplorer.Rendering;
using TriangleNet;
using TriangleNet.IO;
using TestApp.Rendering;
using TriangleNet.Data;
using TriangleNet.Geometry;
/// <summary>
/// Renders a mesh using GDI.
/// </summary>
public class MeshRenderer : System.Windows.Forms.Control
public class MeshRenderer : Control
{
// Rendering stuff
private BufferedGraphics buffer;
@@ -33,7 +33,12 @@ namespace TestApp
RenderData data;
bool initialized = false;
string coordinate = String.Empty;
Timer timer;
public long RenderTime { get; private set; }
public RenderData Data { get { return data; } }
public MeshRenderer()
{
@@ -44,39 +49,48 @@ namespace TestApp
zoom = new Zoom();
context = new BufferedGraphicsContext();
data = new RenderData();
timer = new Timer();
timer.Interval = 3000;
timer.Tick += (sender, e) => {
timer.Stop();
coordinate = String.Empty;
this.Invalidate();
};
}
public void SetData(MeshData meshdata, bool input)
public void Initialize()
{
data.SetData(meshdata);
if (input)
{
// Reset the zoom on new data
zoom.Initialize(this.ClientRectangle, data.Bounds);
}
zoom.Initialize(this.ClientRectangle, this.ClientRectangle);
InitializeBuffer();
initialized = true;
this.Render();
this.Invalidate();
}
public void SetData(Mesh mesh, bool input)
public void SetData(InputGeometry mesh)
{
data.SetData(mesh);
if (input)
{
// Reset the zoom on new data
zoom.Initialize(this.ClientRectangle, data.Bounds);
}
// Reset the zoom on new data
zoom.Initialize(this.ClientRectangle, data.Bounds);
initialized = true;
this.Render();
}
public void Zoom(Point location, int delta)
public void SetData(Mesh mesh)
{
data.SetData(mesh);
initialized = true;
this.Render();
}
public void Zoom(PointF location, int delta)
{
if (!initialized) return;
@@ -87,7 +101,13 @@ namespace TestApp
}
}
private void IntializeBuffer()
public void HandleResize()
{
zoom.Resize(this.ClientRectangle, data.Bounds);
InitializeBuffer();
}
private void InitializeBuffer()
{
if (this.Width > 0 && this.Height > 0)
{
@@ -95,6 +115,8 @@ namespace TestApp
{
if (this.ClientRectangle == buffer.Graphics.VisibleClipBounds)
{
this.Invalidate();
// Bounds didn't change. Probably we just restored the window
// from minimized state.
return;
@@ -150,21 +172,18 @@ namespace TestApp
PointF p0, p1, p2;
PointF[] pts = data.Points;
int[] tri;
var triangles = data.Triangles;
// Draw triangles
int n = data.Triangles.Length;
for (int i = 0; i < n; i++)
foreach (var tri in triangles)
{
tri = data.Triangles[i];
if (zoom.ViewportContains(pts[tri[0]]) ||
zoom.ViewportContains(pts[tri[1]]) ||
zoom.ViewportContains(pts[tri[2]]))
if (zoom.ViewportContains(pts[tri.P0]) ||
zoom.ViewportContains(pts[tri.P1]) ||
zoom.ViewportContains(pts[tri.P2]))
{
p0 = zoom.WorldToScreen(pts[tri[0]]);
p1 = zoom.WorldToScreen(pts[tri[1]]);
p2 = zoom.WorldToScreen(pts[tri[2]]);
p0 = zoom.WorldToScreen(pts[tri.P0]);
p1 = zoom.WorldToScreen(pts[tri.P1]);
p2 = zoom.WorldToScreen(pts[tri.P2]);
g.DrawLine(lines, p0, p1);
g.DrawLine(lines, p1, p2);
@@ -178,19 +197,16 @@ namespace TestApp
PointF p0, p1;
PointF[] pts = data.Points;
int[] tri;
var edges = data.Edges;
// Draw triangles
int n = data.Edges.Length;
for (int i = 0; i < n; i++)
// Draw edges
foreach (var edge in edges)
{
tri = data.Edges[i];
if (zoom.ViewportContains(pts[tri[0]]) ||
zoom.ViewportContains(pts[tri[1]]))
if (zoom.ViewportContains(pts[edge.P0]) ||
zoom.ViewportContains(pts[edge.P1]))
{
p0 = zoom.WorldToScreen(pts[tri[0]]);
p1 = zoom.WorldToScreen(pts[tri[1]]);
p0 = zoom.WorldToScreen(pts[edge.P0]);
p1 = zoom.WorldToScreen(pts[edge.P1]);
g.DrawLine(lines, p0, p1);
}
@@ -202,19 +218,15 @@ namespace TestApp
PointF p0, p1;
PointF[] pts = data.Points;
int[] tri;
var segments = data.Segments;
// Draw triangles
int n = data.Segments.Length;
for (int i = 0; i < n; i++)
foreach (var seg in segments)
{
tri = data.Segments[i];
if (zoom.ViewportContains(pts[tri[0]]) ||
zoom.ViewportContains(pts[tri[1]]))
if (zoom.ViewportContains(pts[seg.P0]) ||
zoom.ViewportContains(pts[seg.P1]))
{
p0 = zoom.WorldToScreen(pts[tri[0]]);
p1 = zoom.WorldToScreen(pts[tri[1]]);
p0 = zoom.WorldToScreen(pts[seg.P0]);
p1 = zoom.WorldToScreen(pts[seg.P1]);
g.DrawLine(Pens.DarkBlue, p0, p1);
}
@@ -223,10 +235,12 @@ namespace TestApp
private void Render()
{
coordinate = String.Empty;
Graphics g = buffer.Graphics;
g.Clear(this.BackColor);
if (!initialized)
if (!initialized || data == null)
{
return;
}
@@ -250,7 +264,10 @@ namespace TestApp
this.RenderSegments(g);
}
this.RenderPoints(g);
if (data.Points != null)
{
this.RenderPoints(g);
}
stopwatch.Stop();
@@ -263,28 +280,20 @@ namespace TestApp
protected override void OnPaint(PaintEventArgs pe)
{
Graphics g = buffer.Graphics;
g.SmoothingMode = SmoothingMode.Default;
Pen pen1 = new Pen(Color.FromArgb(82, 82, 82));
Pen pen2 = new Pen(Color.FromArgb(40, 40, 40));
g.DrawLine(pen1, 0, 0, this.Width, 0);
g.DrawLine(pen2, 0, 1, this.Width, 1);
pen1.Dispose();
pen2.Dispose();
if (!initialized)
{
base.OnPaint(pe);
return;
}
buffer.Render();
}
protected override void OnClientSizeChanged(EventArgs e)
{
if (buffer == null) return;
// Redraw
base.OnClientSizeChanged(e);
if (!String.IsNullOrEmpty(coordinate) && data.Points != null)
{
Graphics g = pe.Graphics;
g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
g.DrawString(coordinate, this.Font, Brushes.White, 10, 10);
}
}
protected override void OnMouseClick(MouseEventArgs e)
@@ -296,6 +305,20 @@ namespace TestApp
zoom.Reset();
this.Render();
}
else if (e.Button == MouseButtons.Left)
{
// Just in case ...
timer.Stop();
PointF c = zoom.ScreenToWorld((float)e.X / this.Width, (float)e.Y / this.Height);
coordinate = String.Format("X:{0} Y:{1}",
c.X.ToString(Util.Nfi),
c.Y.ToString(Util.Nfi));
this.Invalidate();
timer.Start();
}
base.OnMouseClick(e);
}
@@ -303,15 +326,10 @@ namespace TestApp
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// Do nothing
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
IntializeBuffer();
//zoom.Initialize(this.ClientRectangle, data.Bounds);
if (!initialized)
{
base.OnPaintBackground(pevent);
}
}
#endregion
+164
View File
@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MeshExplorer
{
class DarkMessageBox : Form
{
#region Designer
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnClose = new MeshExplorer.Controls.DarkButton();
this.btnShowLog = new MeshExplorer.Controls.DarkButton();
this.lbMessage = new System.Windows.Forms.Label();
this.lbInfo = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// btnClose
//
this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnClose.Location = new System.Drawing.Point(336, 87);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(92, 23);
this.btnClose.TabIndex = 0;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
//
// btnShowLog
//
this.btnShowLog.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnShowLog.Location = new System.Drawing.Point(234, 87);
this.btnShowLog.Name = "btnShowLog";
this.btnShowLog.Size = new System.Drawing.Size(92, 23);
this.btnShowLog.TabIndex = 1;
this.btnShowLog.Text = "Show Log";
this.btnShowLog.UseVisualStyleBackColor = true;
//
// lbMessage
//
this.lbMessage.BackColor = System.Drawing.Color.Transparent;
this.lbMessage.ForeColor = System.Drawing.Color.White;
this.lbMessage.Location = new System.Drawing.Point(12, 9);
this.lbMessage.Name = "lbMessage";
this.lbMessage.Size = new System.Drawing.Size(412, 30);
this.lbMessage.TabIndex = 2;
this.lbMessage.Text = "Message";
//
// lbInfo
//
this.lbInfo.AutoSize = true;
this.lbInfo.BackColor = System.Drawing.Color.Transparent;
this.lbInfo.ForeColor = System.Drawing.Color.White;
this.lbInfo.Location = new System.Drawing.Point(12, 47);
this.lbInfo.Name = "lbInfo";
this.lbInfo.Size = new System.Drawing.Size(28, 13);
this.lbInfo.TabIndex = 3;
this.lbInfo.Text = "Info";
//
// DarkMessageBox
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76)))));
this.ClientSize = new System.Drawing.Size(436, 118);
this.Controls.Add(this.lbInfo);
this.Controls.Add(this.lbMessage);
this.Controls.Add(this.btnShowLog);
this.Controls.Add(this.btnClose);
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "DarkMessageBox";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Message Box";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Controls.DarkButton btnClose;
private Label lbMessage;
private Label lbInfo;
private Controls.DarkButton btnShowLog;
#endregion
public DarkMessageBox()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var rect = this.ClientRectangle;
rect.Height -= 40;
e.Graphics.FillRectangle(Brushes.DimGray, rect);
}
public static DialogResult Show(string title, string message, string info, bool log)
{
DarkMessageBox m = new DarkMessageBox();
if (!log)
{
m.btnShowLog.Enabled = false;
m.btnShowLog.Visible = false;
}
m.Text = title;
m.lbInfo.Text = info;
m.lbMessage.Text = message;
return m.ShowDialog();
}
public static DialogResult Show(string title, string message, string info)
{
return Show(title, message, info, false);
}
public static DialogResult Show(string title, string message)
{
return Show(title, message, "", false);
}
}
}
+73 -4
View File
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TestApp
namespace MeshExplorer
{
using System;
using System.Collections.Generic;
@@ -12,10 +12,10 @@ namespace TestApp
using System.Text;
using TriangleNet;
using TriangleNet.IO;
using TriangleNet.Geometry;
/// <summary>
/// Code of the online examples.
///
/// </summary>
public static class Examples
{
@@ -39,7 +39,7 @@ namespace TestApp
// Read face polygon file and gernerate the delaunay triangulation
// of the PSLG. We reuse the mesh instance here.
MeshData data = FileReader.ReadFile(pathToData + "face.poly");
InputGeometry data = FileReader.ReadFile(pathToData + "face.poly");
mesh.Triangulate(data);
ImageWriter.WritePng(mesh, "face.png", 200);
@@ -62,7 +62,7 @@ namespace TestApp
// Read spiral node file and gernerate the delaunay triangulation.
// Set the mesh quality option to true, which will set a default
// minimum angle of 20 degrees.
MeshData data = FileReader.ReadNodeFile(pathToData + "spiral.node");
InputGeometry data = FileReader.ReadNodeFile(pathToData + "spiral.node");
mesh.SetOption(Options.Quality, true);
mesh.Triangulate(data);
ImageWriter.WritePng(mesh, "spiral-Angle-20.png", 200);
@@ -116,5 +116,74 @@ namespace TestApp
mesh.Refine();
ImageWriter.WritePng(mesh, "box-Refine-3.png", 200);
}
/// <summary>
/// Drawing the Voronoi diagram.
/// </summary>
public static void Example4()
{
ImageWriter.SetColorSchemeLight();
// Create mesh data (random point set)
//data.Points = Util.CreateCirclePoints(0, 0, 5, 50); // Ooops, TODO !!!
InputGeometry data = PolygonGenerator.CreateStarPoints(0, 0, 5, 10);
// Create a mesh instance.
Mesh mesh = new Mesh();
// Gernerate a delaunay triangulation
mesh.Triangulate(data);
ImageWriter.WritePng(mesh, "circle-mesh.png", 400);
ImageWriter.WriteVoronoiPng(mesh, "circle-voronoi.png", 400);
}
/// <summary>
/// Smoothing a mesh.
/// </summary>
public static void Example5()
{
ImageWriter.SetColorSchemeLight();
// Create a mesh instance.
Mesh mesh = new Mesh();
mesh.SetOption(Options.Quality, true);
mesh.SetOption(Options.MinAngle, 25);
mesh.SetOption(Options.MaxArea, 0.0075);
mesh.Triangulate(pathToData + "Smooth-Slit.poly");
mesh.Smooth();
ImageWriter.WritePng(mesh, "slit-smooth.png", 300);
}
/// <summary>
/// Smoothing a mesh.
/// </summary>
public static void ExampleXYZ()
{
ImageWriter.SetColorSchemeLight();
Mesh mesh = new Mesh();
mesh.SetOption(Options.Quality, true);
mesh.SetOption(Options.MinAngle, 25);
mesh.SetOption(Options.MaxArea, 0.05);
mesh.Triangulate(pathToData + "Smooth-Square.poly");
ImageWriter.WritePng(mesh, "test1.png", 300);
mesh.SetOption(Options.MaxArea, 0.01);
// Refine with new max area
mesh.Refine();
ImageWriter.WritePng(mesh, "test2.png", 300);
mesh.SetOption(Options.SteinerPoints, 50);
mesh.Triangulate(pathToData + "Smooth-Square.poly");
ImageWriter.WritePng(mesh, "test3.png", 300);
}
}
}
+89
View File
@@ -0,0 +1,89 @@
namespace MeshExplorer
{
partial class FormLog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.listLog = new System.Windows.Forms.ListView();
this.colMessage = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.colInfo = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.SuspendLayout();
//
// listLog
//
this.listLog.BackColor = System.Drawing.Color.White;
this.listLog.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.listLog.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.colMessage,
this.colInfo});
this.listLog.Dock = System.Windows.Forms.DockStyle.Fill;
this.listLog.ForeColor = System.Drawing.Color.White;
this.listLog.FullRowSelect = true;
this.listLog.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.listLog.Location = new System.Drawing.Point(0, 0);
this.listLog.Name = "listLog";
this.listLog.Size = new System.Drawing.Size(584, 262);
this.listLog.TabIndex = 2;
this.listLog.UseCompatibleStateImageBehavior = false;
this.listLog.View = System.Windows.Forms.View.Details;
this.listLog.DoubleClick += new System.EventHandler(this.listLog_DoubleClick);
//
// colMessage
//
this.colMessage.Text = "Message";
this.colMessage.Width = 350;
//
// colInfo
//
this.colInfo.Text = "Info";
this.colInfo.Width = 200;
//
// FormLog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(584, 262);
this.Controls.Add(this.listLog);
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FormLog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.Text = "Log";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormLog_FormClosing);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListView listLog;
private System.Windows.Forms.ColumnHeader colMessage;
private System.Windows.Forms.ColumnHeader colInfo;
}
}
+96
View File
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using TriangleNet.Log;
namespace MeshExplorer
{
public partial class FormLog : Form
{
public FormLog()
{
InitializeComponent();
}
public void UpdateItems()
{
listLog.Items.Clear();
ILog<SimpleLogItem> log = SimpleLog.Instance;
foreach (var item in log.Data)
{
listLog.Items.Add(CreateListViewItem(item));
}
}
private ListViewItem CreateListViewItem(SimpleLogItem item)
{
ListViewItem lvi = new ListViewItem(new string[] { item.Message, item.Info });
if (item.Level == LogLevel.Error)
{
lvi.ForeColor = Color.DarkRed;
}
else if (item.Level == LogLevel.Warning)
{
lvi.ForeColor = Color.Peru;
}
else
{
lvi.ForeColor = Color.Black;
}
lvi.UseItemStyleForSubItems = true;
return lvi;
}
private void FormLog_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
this.Hide();
}
}
private void listLog_DoubleClick(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (var item in listLog.SelectedItems)
{
GetRowText(sb, item);
}
if (sb.Length > 0)
{
Clipboard.SetText(sb.ToString());
}
}
private void GetRowText(StringBuilder sb, object item)
{
var row = item as ListViewItem;
if (row != null)
{
foreach (var col in row.SubItems)
{
var lvi = col as ListViewItem.ListViewSubItem;
if (lvi != null)
{
sb.AppendLine(lvi.Text);
}
}
}
}
}
}
+797 -99
View File
@@ -1,4 +1,4 @@
namespace TestApp
namespace MeshExplorer
{
partial class FormMain
{
@@ -28,148 +28,846 @@
/// </summary>
private void InitializeComponent()
{
this.btnStatistic = new TestApp.Controls.DarkButton();
this.tbNumPoints = new TestApp.Controls.DarkTextBox();
this.cbConvex = new TestApp.Controls.DarkCheckBox();
this.cbQuality = new TestApp.Controls.DarkCheckBox();
this.btnRun = new TestApp.Controls.DarkButton();
this.btnOpen = new TestApp.Controls.DarkButton();
this.btnRandPts = new TestApp.Controls.DarkButton();
this.meshRenderer1 = new TestApp.MeshRenderer();
this.lbTime = new System.Windows.Forms.Label();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.btnSmooth = new MeshExplorer.Controls.DarkButton();
this.btnMesh = new MeshExplorer.Controls.DarkButton();
this.label4 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.lbNumSeg = new System.Windows.Forms.Label();
this.lbNumSeg2 = new System.Windows.Forms.Label();
this.lbNumTri = new System.Windows.Forms.Label();
this.lbNumTri2 = new System.Windows.Forms.Label();
this.lbNumVert = new System.Windows.Forms.Label();
this.lbNumVert2 = new System.Windows.Forms.Label();
this.flatTabControl1 = new MeshExplorer.Controls.DarkTabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.lbMaxArea = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.lbMinAngle = new System.Windows.Forms.Label();
this.label9 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.slMaxArea = new MeshExplorer.Controls.DarkSlider();
this.slMinAngle = new MeshExplorer.Controls.DarkSlider();
this.cbConvex = new MeshExplorer.Controls.DarkCheckBox();
this.cbQuality = new MeshExplorer.Controls.DarkCheckBox();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.label13 = new System.Windows.Forms.Label();
this.label12 = new System.Windows.Forms.Label();
this.label16 = new System.Windows.Forms.Label();
this.label15 = new System.Windows.Forms.Label();
this.label14 = new System.Windows.Forms.Label();
this.lbAngleMax = new System.Windows.Forms.Label();
this.lbRatioMax = new System.Windows.Forms.Label();
this.lbEdgeMax = new System.Windows.Forms.Label();
this.lbAreaMax = new System.Windows.Forms.Label();
this.lbAngleMin = new System.Windows.Forms.Label();
this.lbRatioMin = new System.Windows.Forms.Label();
this.lbEdgeMin = new System.Windows.Forms.Label();
this.lbAreaMin = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
this.label17 = new System.Windows.Forms.Label();
this.label11 = new System.Windows.Forms.Label();
this.angleHistogram1 = new MeshExplorer.Controls.AngleHistogram();
this.tabPage3 = new System.Windows.Forms.TabPage();
this.label1 = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.lbShortcuts = new System.Windows.Forms.Label();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileMenu = new System.Windows.Forms.ToolStripMenuItem();
this.fileMenuOpen = new System.Windows.Forms.ToolStripMenuItem();
this.fileMenuSave = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.fileMenuQuit = new System.Windows.Forms.ToolStripMenuItem();
this.viewMenu = new System.Windows.Forms.ToolStripMenuItem();
this.viewMenuMQuality = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.viewMenuLog = new System.Windows.Forms.ToolStripMenuItem();
this.toolsMenu = new System.Windows.Forms.ToolStripMenuItem();
this.toolsMenuGen = new System.Windows.Forms.ToolStripMenuItem();
this.toolsMenuPoly1 = new System.Windows.Forms.ToolStripMenuItem();
this.toolsMenuRandPts = new System.Windows.Forms.ToolStripMenuItem();
this.toolsMenuCheck = new System.Windows.Forms.ToolStripMenuItem();
this.meshRenderer1 = new MeshExplorer.Controls.MeshRenderer();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.flatTabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.tabPage2.SuspendLayout();
this.tabPage3.SuspendLayout();
this.menuStrip1.SuspendLayout();
this.SuspendLayout();
//
// btnStatistic
// splitContainer1
//
this.btnStatistic.Location = new System.Drawing.Point(847, 12);
this.btnStatistic.Name = "btnStatistic";
this.btnStatistic.Size = new System.Drawing.Size(75, 23);
this.btnStatistic.TabIndex = 11;
this.btnStatistic.Text = "Statistic";
this.btnStatistic.UseVisualStyleBackColor = true;
this.btnStatistic.Click += new System.EventHandler(this.btnStatistic_Click);
this.splitContainer1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(68)))), ((int)(((byte)(68)))), ((int)(((byte)(68)))));
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
this.splitContainer1.IsSplitterFixed = true;
this.splitContainer1.Location = new System.Drawing.Point(0, 0);
this.splitContainer1.Name = "splitContainer1";
//
// tbNumPoints
// splitContainer1.Panel1
//
this.tbNumPoints.BackColor = System.Drawing.Color.DimGray;
this.tbNumPoints.Cursor = System.Windows.Forms.Cursors.IBeam;
this.tbNumPoints.ForeColor = System.Drawing.Color.LightGray;
this.tbNumPoints.Location = new System.Drawing.Point(244, 13);
this.tbNumPoints.Name = "tbNumPoints";
this.tbNumPoints.Size = new System.Drawing.Size(60, 21);
this.tbNumPoints.TabIndex = 10;
this.tbNumPoints.Text = "1000";
this.tbNumPoints.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
this.splitContainer1.Panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76)))));
this.splitContainer1.Panel1.Controls.Add(this.btnSmooth);
this.splitContainer1.Panel1.Controls.Add(this.btnMesh);
this.splitContainer1.Panel1.Controls.Add(this.label4);
this.splitContainer1.Panel1.Controls.Add(this.label3);
this.splitContainer1.Panel1.Controls.Add(this.label2);
this.splitContainer1.Panel1.Controls.Add(this.lbNumSeg);
this.splitContainer1.Panel1.Controls.Add(this.lbNumSeg2);
this.splitContainer1.Panel1.Controls.Add(this.lbNumTri);
this.splitContainer1.Panel1.Controls.Add(this.lbNumTri2);
this.splitContainer1.Panel1.Controls.Add(this.lbNumVert);
this.splitContainer1.Panel1.Controls.Add(this.lbNumVert2);
this.splitContainer1.Panel1.Controls.Add(this.flatTabControl1);
this.splitContainer1.Panel1.Controls.Add(this.menuStrip1);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.BackColor = System.Drawing.Color.Black;
this.splitContainer1.Panel2.Controls.Add(this.meshRenderer1);
this.splitContainer1.Size = new System.Drawing.Size(984, 612);
this.splitContainer1.SplitterDistance = 280;
this.splitContainer1.SplitterWidth = 1;
this.splitContainer1.TabIndex = 0;
//
// btnSmooth
//
this.btnSmooth.Enabled = false;
this.btnSmooth.Location = new System.Drawing.Point(150, 196);
this.btnSmooth.Name = "btnSmooth";
this.btnSmooth.Size = new System.Drawing.Size(126, 23);
this.btnSmooth.TabIndex = 12;
this.btnSmooth.Text = "Smooth";
this.btnSmooth.UseVisualStyleBackColor = true;
this.btnSmooth.Click += new System.EventHandler(this.btnSmooth_Click);
//
// btnMesh
//
this.btnMesh.Enabled = false;
this.btnMesh.Location = new System.Drawing.Point(4, 196);
this.btnMesh.Name = "btnMesh";
this.btnMesh.Size = new System.Drawing.Size(126, 23);
this.btnMesh.TabIndex = 12;
this.btnMesh.Text = "Triangulate";
this.btnMesh.UseVisualStyleBackColor = true;
this.btnMesh.Click += new System.EventHandler(this.btnMesh_Click);
//
// label4
//
this.label4.AutoSize = true;
this.label4.ForeColor = System.Drawing.Color.Gray;
this.label4.Location = new System.Drawing.Point(12, 121);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(60, 13);
this.label4.TabIndex = 7;
this.label4.Text = "Segments:";
//
// label3
//
this.label3.AutoSize = true;
this.label3.ForeColor = System.Drawing.Color.Gray;
this.label3.Location = new System.Drawing.Point(12, 142);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(56, 13);
this.label3.TabIndex = 9;
this.label3.Text = "Triangles:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.ForeColor = System.Drawing.Color.Gray;
this.label2.Location = new System.Drawing.Point(12, 100);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(50, 13);
this.label2.TabIndex = 11;
this.label2.Text = "Vertices:";
//
// lbNumSeg
//
this.lbNumSeg.ForeColor = System.Drawing.Color.White;
this.lbNumSeg.Location = new System.Drawing.Point(95, 121);
this.lbNumSeg.Name = "lbNumSeg";
this.lbNumSeg.Size = new System.Drawing.Size(70, 13);
this.lbNumSeg.TabIndex = 3;
this.lbNumSeg.Text = "-";
this.lbNumSeg.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbNumSeg2
//
this.lbNumSeg2.ForeColor = System.Drawing.Color.Gray;
this.lbNumSeg2.Location = new System.Drawing.Point(182, 121);
this.lbNumSeg2.Name = "lbNumSeg2";
this.lbNumSeg2.Size = new System.Drawing.Size(70, 13);
this.lbNumSeg2.TabIndex = 3;
this.lbNumSeg2.Text = "-";
this.lbNumSeg2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbNumTri
//
this.lbNumTri.ForeColor = System.Drawing.Color.White;
this.lbNumTri.Location = new System.Drawing.Point(95, 142);
this.lbNumTri.Name = "lbNumTri";
this.lbNumTri.Size = new System.Drawing.Size(70, 13);
this.lbNumTri.TabIndex = 2;
this.lbNumTri.Text = "-";
this.lbNumTri.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbNumTri2
//
this.lbNumTri2.ForeColor = System.Drawing.Color.Gray;
this.lbNumTri2.Location = new System.Drawing.Point(182, 142);
this.lbNumTri2.Name = "lbNumTri2";
this.lbNumTri2.Size = new System.Drawing.Size(70, 13);
this.lbNumTri2.TabIndex = 2;
this.lbNumTri2.Text = "-";
this.lbNumTri2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbNumVert
//
this.lbNumVert.ForeColor = System.Drawing.Color.White;
this.lbNumVert.Location = new System.Drawing.Point(95, 100);
this.lbNumVert.Name = "lbNumVert";
this.lbNumVert.Size = new System.Drawing.Size(70, 13);
this.lbNumVert.TabIndex = 4;
this.lbNumVert.Text = "-";
this.lbNumVert.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbNumVert2
//
this.lbNumVert2.ForeColor = System.Drawing.Color.Gray;
this.lbNumVert2.Location = new System.Drawing.Point(182, 100);
this.lbNumVert2.Name = "lbNumVert2";
this.lbNumVert2.Size = new System.Drawing.Size(70, 13);
this.lbNumVert2.TabIndex = 4;
this.lbNumVert2.Text = "-";
this.lbNumVert2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// flatTabControl1
//
this.flatTabControl1.Alignment = System.Windows.Forms.TabAlignment.Bottom;
this.flatTabControl1.Controls.Add(this.tabPage1);
this.flatTabControl1.Controls.Add(this.tabPage2);
this.flatTabControl1.Controls.Add(this.tabPage3);
this.flatTabControl1.Location = new System.Drawing.Point(0, 224);
this.flatTabControl1.Name = "flatTabControl1";
this.flatTabControl1.SelectedIndex = 0;
this.flatTabControl1.Size = new System.Drawing.Size(280, 387);
this.flatTabControl1.TabIndex = 1;
//
// tabPage1
//
this.tabPage1.BackColor = System.Drawing.Color.DimGray;
this.tabPage1.Controls.Add(this.lbMaxArea);
this.tabPage1.Controls.Add(this.label6);
this.tabPage1.Controls.Add(this.lbMinAngle);
this.tabPage1.Controls.Add(this.label9);
this.tabPage1.Controls.Add(this.label8);
this.tabPage1.Controls.Add(this.label5);
this.tabPage1.Controls.Add(this.slMaxArea);
this.tabPage1.Controls.Add(this.slMinAngle);
this.tabPage1.Controls.Add(this.cbConvex);
this.tabPage1.Controls.Add(this.cbQuality);
this.tabPage1.ForeColor = System.Drawing.Color.White;
this.tabPage1.Location = new System.Drawing.Point(4, 4);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Size = new System.Drawing.Size(272, 358);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Mesh Control";
//
// lbMaxArea
//
this.lbMaxArea.AutoSize = true;
this.lbMaxArea.Location = new System.Drawing.Point(227, 61);
this.lbMaxArea.Name = "lbMaxArea";
this.lbMaxArea.Size = new System.Drawing.Size(13, 13);
this.lbMaxArea.TabIndex = 14;
this.lbMaxArea.Text = "0";
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(8, 61);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(81, 13);
this.label6.TabIndex = 14;
this.label6.Text = "Maximum area";
//
// lbMinAngle
//
this.lbMinAngle.AutoSize = true;
this.lbMinAngle.Location = new System.Drawing.Point(227, 39);
this.lbMinAngle.Name = "lbMinAngle";
this.lbMinAngle.Size = new System.Drawing.Size(19, 13);
this.lbMinAngle.TabIndex = 14;
this.lbMinAngle.Text = "20";
//
// label9
//
this.label9.BackColor = System.Drawing.Color.DimGray;
this.label9.ForeColor = System.Drawing.Color.DarkGray;
this.label9.Location = new System.Drawing.Point(8, 166);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(258, 33);
this.label9.TabIndex = 14;
this.label9.Text = "Use the convex mesh option, if the convex hull should be included in the output.";
//
// label8
//
this.label8.BackColor = System.Drawing.Color.DimGray;
this.label8.ForeColor = System.Drawing.Color.DarkGray;
this.label8.Location = new System.Drawing.Point(8, 85);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(258, 33);
this.label8.TabIndex = 14;
this.label8.Text = "Hint: maximum area values of 0 or 1 will be irgnored (no area constraints are set" +
").";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(8, 39);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(87, 13);
this.label5.TabIndex = 14;
this.label5.Text = "Minimum angle";
//
// slMaxArea
//
this.slMaxArea.BackColor = System.Drawing.Color.Transparent;
this.slMaxArea.CriticalPercent = ((uint)(0u));
this.slMaxArea.Location = new System.Drawing.Point(102, 58);
this.slMaxArea.Maximum = 100;
this.slMaxArea.Minimum = 0;
this.slMaxArea.Name = "slMaxArea";
this.slMaxArea.Size = new System.Drawing.Size(119, 18);
this.slMaxArea.TabIndex = 13;
this.slMaxArea.Text = "darkSlider1";
this.slMaxArea.Value = 0;
this.slMaxArea.ValueChanging += new System.EventHandler(this.slMaxArea_ValueChanging);
//
// slMinAngle
//
this.slMinAngle.BackColor = System.Drawing.Color.Transparent;
this.slMinAngle.CriticalPercent = ((uint)(89u));
this.slMinAngle.Location = new System.Drawing.Point(102, 36);
this.slMinAngle.Maximum = 100;
this.slMinAngle.Minimum = 0;
this.slMinAngle.Name = "slMinAngle";
this.slMinAngle.Size = new System.Drawing.Size(119, 18);
this.slMinAngle.TabIndex = 13;
this.slMinAngle.Text = "darkSlider1";
this.slMinAngle.Value = 50;
this.slMinAngle.ValueChanging += new System.EventHandler(this.slMinAngle_ValueChanging);
//
// cbConvex
//
this.cbConvex.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76)))));
this.cbConvex.BackColor = System.Drawing.Color.DimGray;
this.cbConvex.Checked = false;
this.cbConvex.Location = new System.Drawing.Point(506, 12);
this.cbConvex.Location = new System.Drawing.Point(11, 146);
this.cbConvex.Name = "cbConvex";
this.cbConvex.Size = new System.Drawing.Size(110, 23);
this.cbConvex.TabIndex = 9;
this.cbConvex.Text = "Convex polygon";
this.cbConvex.Size = new System.Drawing.Size(115, 17);
this.cbConvex.TabIndex = 0;
this.cbConvex.Text = "Convex mesh";
this.cbConvex.UseVisualStyleBackColor = false;
//
// cbQuality
//
this.cbQuality.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76)))));
this.cbQuality.BackColor = System.Drawing.Color.DimGray;
this.cbQuality.Checked = false;
this.cbQuality.Location = new System.Drawing.Point(622, 12);
this.cbQuality.Location = new System.Drawing.Point(11, 13);
this.cbQuality.Name = "cbQuality";
this.cbQuality.Size = new System.Drawing.Size(138, 23);
this.cbQuality.TabIndex = 9;
this.cbQuality.Text = "Produce quality mesh";
this.cbQuality.Size = new System.Drawing.Size(115, 17);
this.cbQuality.TabIndex = 0;
this.cbQuality.Text = "Quality mesh";
this.cbQuality.UseVisualStyleBackColor = false;
//
// btnRun
// tabPage2
//
this.btnRun.Location = new System.Drawing.Point(766, 12);
this.btnRun.Name = "btnRun";
this.btnRun.Size = new System.Drawing.Size(75, 23);
this.btnRun.TabIndex = 8;
this.btnRun.Text = "Triangulate";
this.btnRun.UseVisualStyleBackColor = true;
this.btnRun.Click += new System.EventHandler(this.btnRun_Click);
this.tabPage2.BackColor = System.Drawing.Color.DimGray;
this.tabPage2.Controls.Add(this.label13);
this.tabPage2.Controls.Add(this.label12);
this.tabPage2.Controls.Add(this.label16);
this.tabPage2.Controls.Add(this.label15);
this.tabPage2.Controls.Add(this.label14);
this.tabPage2.Controls.Add(this.lbAngleMax);
this.tabPage2.Controls.Add(this.lbRatioMax);
this.tabPage2.Controls.Add(this.lbEdgeMax);
this.tabPage2.Controls.Add(this.lbAreaMax);
this.tabPage2.Controls.Add(this.lbAngleMin);
this.tabPage2.Controls.Add(this.lbRatioMin);
this.tabPage2.Controls.Add(this.lbEdgeMin);
this.tabPage2.Controls.Add(this.lbAreaMin);
this.tabPage2.Controls.Add(this.label10);
this.tabPage2.Controls.Add(this.label17);
this.tabPage2.Controls.Add(this.label11);
this.tabPage2.Controls.Add(this.angleHistogram1);
this.tabPage2.ForeColor = System.Drawing.Color.White;
this.tabPage2.Location = new System.Drawing.Point(4, 4);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(272, 358);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Statistic";
//
// btnOpen
// label13
//
this.btnOpen.Location = new System.Drawing.Point(12, 12);
this.btnOpen.Name = "btnOpen";
this.btnOpen.Size = new System.Drawing.Size(110, 23);
this.btnOpen.TabIndex = 7;
this.btnOpen.Text = "Open File";
this.btnOpen.UseVisualStyleBackColor = true;
this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click);
this.label13.AutoSize = true;
this.label13.ForeColor = System.Drawing.Color.DarkGray;
this.label13.Location = new System.Drawing.Point(196, 12);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(56, 13);
this.label13.TabIndex = 11;
this.label13.Text = "Maximum";
//
// btnRandPts
// label12
//
this.btnRandPts.Location = new System.Drawing.Point(128, 12);
this.btnRandPts.Name = "btnRandPts";
this.btnRandPts.Size = new System.Drawing.Size(110, 23);
this.btnRandPts.TabIndex = 6;
this.btnRandPts.Text = "Random Points";
this.btnRandPts.UseVisualStyleBackColor = true;
this.btnRandPts.Click += new System.EventHandler(this.btnRandPts_Click);
this.label12.AutoSize = true;
this.label12.ForeColor = System.Drawing.Color.DarkGray;
this.label12.Location = new System.Drawing.Point(106, 12);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(55, 13);
this.label12.TabIndex = 12;
this.label12.Text = "Minimum";
//
// label16
//
this.label16.AutoSize = true;
this.label16.ForeColor = System.Drawing.Color.DarkGray;
this.label16.Location = new System.Drawing.Point(8, 107);
this.label16.Name = "label16";
this.label16.Size = new System.Drawing.Size(40, 13);
this.label16.TabIndex = 9;
this.label16.Text = "Angle:";
//
// label15
//
this.label15.AutoSize = true;
this.label15.ForeColor = System.Drawing.Color.DarkGray;
this.label15.Location = new System.Drawing.Point(8, 84);
this.label15.Name = "label15";
this.label15.Size = new System.Drawing.Size(71, 13);
this.label15.TabIndex = 10;
this.label15.Text = "Aspect ratio:";
//
// label14
//
this.label14.AutoSize = true;
this.label14.ForeColor = System.Drawing.Color.DarkGray;
this.label14.Location = new System.Drawing.Point(9, 61);
this.label14.Name = "label14";
this.label14.Size = new System.Drawing.Size(73, 13);
this.label14.TabIndex = 15;
this.label14.Text = "Edge length:";
//
// lbAngleMax
//
this.lbAngleMax.ForeColor = System.Drawing.Color.White;
this.lbAngleMax.Location = new System.Drawing.Point(176, 107);
this.lbAngleMax.Name = "lbAngleMax";
this.lbAngleMax.Size = new System.Drawing.Size(76, 13);
this.lbAngleMax.TabIndex = 16;
this.lbAngleMax.Text = "-";
this.lbAngleMax.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbRatioMax
//
this.lbRatioMax.ForeColor = System.Drawing.Color.White;
this.lbRatioMax.Location = new System.Drawing.Point(176, 84);
this.lbRatioMax.Name = "lbRatioMax";
this.lbRatioMax.Size = new System.Drawing.Size(76, 13);
this.lbRatioMax.TabIndex = 13;
this.lbRatioMax.Text = "-";
this.lbRatioMax.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbEdgeMax
//
this.lbEdgeMax.ForeColor = System.Drawing.Color.White;
this.lbEdgeMax.Location = new System.Drawing.Point(176, 61);
this.lbEdgeMax.Name = "lbEdgeMax";
this.lbEdgeMax.Size = new System.Drawing.Size(76, 13);
this.lbEdgeMax.TabIndex = 14;
this.lbEdgeMax.Text = "-";
this.lbEdgeMax.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbAreaMax
//
this.lbAreaMax.ForeColor = System.Drawing.Color.White;
this.lbAreaMax.Location = new System.Drawing.Point(176, 39);
this.lbAreaMax.Name = "lbAreaMax";
this.lbAreaMax.Size = new System.Drawing.Size(76, 13);
this.lbAreaMax.TabIndex = 3;
this.lbAreaMax.Text = "-";
this.lbAreaMax.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbAngleMin
//
this.lbAngleMin.ForeColor = System.Drawing.Color.White;
this.lbAngleMin.Location = new System.Drawing.Point(94, 107);
this.lbAngleMin.Name = "lbAngleMin";
this.lbAngleMin.Size = new System.Drawing.Size(68, 13);
this.lbAngleMin.TabIndex = 4;
this.lbAngleMin.Text = "-";
this.lbAngleMin.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbRatioMin
//
this.lbRatioMin.ForeColor = System.Drawing.Color.White;
this.lbRatioMin.Location = new System.Drawing.Point(94, 84);
this.lbRatioMin.Name = "lbRatioMin";
this.lbRatioMin.Size = new System.Drawing.Size(68, 13);
this.lbRatioMin.TabIndex = 1;
this.lbRatioMin.Text = "-";
this.lbRatioMin.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbEdgeMin
//
this.lbEdgeMin.ForeColor = System.Drawing.Color.White;
this.lbEdgeMin.Location = new System.Drawing.Point(94, 61);
this.lbEdgeMin.Name = "lbEdgeMin";
this.lbEdgeMin.Size = new System.Drawing.Size(68, 13);
this.lbEdgeMin.TabIndex = 2;
this.lbEdgeMin.Text = "-";
this.lbEdgeMin.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbAreaMin
//
this.lbAreaMin.ForeColor = System.Drawing.Color.White;
this.lbAreaMin.Location = new System.Drawing.Point(94, 39);
this.lbAreaMin.Name = "lbAreaMin";
this.lbAreaMin.Size = new System.Drawing.Size(68, 13);
this.lbAreaMin.TabIndex = 7;
this.lbAreaMin.Text = "-";
this.lbAreaMin.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// label10
//
this.label10.AutoSize = true;
this.label10.ForeColor = System.Drawing.Color.DarkGray;
this.label10.Location = new System.Drawing.Point(9, 39);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(76, 13);
this.label10.TabIndex = 8;
this.label10.Text = "Triangle area:";
//
// label17
//
this.label17.AutoSize = true;
this.label17.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label17.ForeColor = System.Drawing.Color.White;
this.label17.Location = new System.Drawing.Point(8, 139);
this.label17.Name = "label17";
this.label17.Size = new System.Drawing.Size(97, 13);
this.label17.TabIndex = 5;
this.label17.Text = "Angle histogram:";
//
// label11
//
this.label11.AutoSize = true;
this.label11.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label11.ForeColor = System.Drawing.Color.White;
this.label11.Location = new System.Drawing.Point(8, 12);
this.label11.Name = "label11";
this.label11.Size = new System.Drawing.Size(50, 13);
this.label11.TabIndex = 6;
this.label11.Text = "Statistic:";
//
// angleHistogram1
//
this.angleHistogram1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76)))));
this.angleHistogram1.Font = new System.Drawing.Font("Segoe UI", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.angleHistogram1.Location = new System.Drawing.Point(6, 157);
this.angleHistogram1.Name = "angleHistogram1";
this.angleHistogram1.Size = new System.Drawing.Size(260, 195);
this.angleHistogram1.TabIndex = 0;
this.angleHistogram1.Text = "angleHistogram1";
//
// tabPage3
//
this.tabPage3.BackColor = System.Drawing.Color.DimGray;
this.tabPage3.Controls.Add(this.label1);
this.tabPage3.Controls.Add(this.label7);
this.tabPage3.Controls.Add(this.lbShortcuts);
this.tabPage3.Location = new System.Drawing.Point(4, 4);
this.tabPage3.Name = "tabPage3";
this.tabPage3.Padding = new System.Windows.Forms.Padding(3);
this.tabPage3.Size = new System.Drawing.Size(272, 358);
this.tabPage3.TabIndex = 2;
this.tabPage3.Text = "Shortcuts";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(8, 14);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(108, 13);
this.label1.TabIndex = 1;
this.label1.Text = "Keyboard shortcuts";
//
// label7
//
this.label7.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label7.ForeColor = System.Drawing.Color.White;
this.label7.Location = new System.Drawing.Point(55, 37);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(134, 108);
this.label7.TabIndex = 0;
this.label7.Text = "File Open\r\nFile Save\r\nReload Input\r\n\r\nTriangulate / Refine\r\nSmooth\r\n\r\nShow Log";
//
// lbShortcuts
//
this.lbShortcuts.ForeColor = System.Drawing.Color.White;
this.lbShortcuts.Location = new System.Drawing.Point(13, 37);
this.lbShortcuts.Name = "lbShortcuts";
this.lbShortcuts.Size = new System.Drawing.Size(36, 108);
this.lbShortcuts.TabIndex = 0;
this.lbShortcuts.Text = "F3\r\nF4\r\nF5\r\n\r\nF8\r\nF9\r\n\r\nF12";
this.lbShortcuts.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// menuStrip1
//
this.menuStrip1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.menuStrip1.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.menuStrip1.GripMargin = new System.Windows.Forms.Padding(0);
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileMenu,
this.viewMenu,
this.toolsMenu});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Padding = new System.Windows.Forms.Padding(0);
this.menuStrip1.Size = new System.Drawing.Size(280, 24);
this.menuStrip1.TabIndex = 0;
this.menuStrip1.Text = "menuStrip1";
//
// fileMenu
//
this.fileMenu.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.fileMenu.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.fileMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileMenuOpen,
this.fileMenuSave,
this.toolStripSeparator2,
this.fileMenuQuit});
this.fileMenu.Name = "fileMenu";
this.fileMenu.Size = new System.Drawing.Size(37, 24);
this.fileMenu.Text = "File";
//
// fileMenuOpen
//
this.fileMenuOpen.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.fileMenuOpen.Name = "fileMenuOpen";
this.fileMenuOpen.Size = new System.Drawing.Size(103, 22);
this.fileMenuOpen.Text = "Open";
this.fileMenuOpen.Click += new System.EventHandler(this.fileMenuOpen_Click);
//
// fileMenuSave
//
this.fileMenuSave.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.fileMenuSave.Name = "fileMenuSave";
this.fileMenuSave.Size = new System.Drawing.Size(103, 22);
this.fileMenuSave.Text = "Save";
this.fileMenuSave.Click += new System.EventHandler(this.fileMenuSave_Click);
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(100, 6);
//
// fileMenuQuit
//
this.fileMenuQuit.Name = "fileMenuQuit";
this.fileMenuQuit.Size = new System.Drawing.Size(103, 22);
this.fileMenuQuit.Text = "Quit";
this.fileMenuQuit.Click += new System.EventHandler(this.fileMenuQuit_Click);
//
// viewMenu
//
this.viewMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.viewMenuMQuality,
this.toolStripSeparator1,
this.viewMenuLog});
this.viewMenu.Name = "viewMenu";
this.viewMenu.Size = new System.Drawing.Size(44, 24);
this.viewMenu.Text = "View";
//
// viewMenuMQuality
//
this.viewMenuMQuality.Enabled = false;
this.viewMenuMQuality.Name = "viewMenuMQuality";
this.viewMenuMQuality.Size = new System.Drawing.Size(154, 22);
this.viewMenuMQuality.Text = "Mesh Quality ...";
this.viewMenuMQuality.Click += new System.EventHandler(this.viewMenuMQuality_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(151, 6);
//
// viewMenuLog
//
this.viewMenuLog.Name = "viewMenuLog";
this.viewMenuLog.Size = new System.Drawing.Size(154, 22);
this.viewMenuLog.Text = "Show Log";
this.viewMenuLog.Click += new System.EventHandler(this.viewMenuLog_Click);
//
// toolsMenu
//
this.toolsMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolsMenuGen,
this.toolsMenuCheck});
this.toolsMenu.Name = "toolsMenu";
this.toolsMenu.Size = new System.Drawing.Size(46, 24);
this.toolsMenu.Text = "Tools";
//
// toolsMenuGen
//
this.toolsMenuGen.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolsMenuPoly1,
this.toolsMenuRandPts});
this.toolsMenuGen.Name = "toolsMenuGen";
this.toolsMenuGen.Size = new System.Drawing.Size(152, 22);
this.toolsMenuGen.Text = "Generator";
//
// toolsMenuPoly1
//
this.toolsMenuPoly1.Name = "toolsMenuPoly1";
this.toolsMenuPoly1.Size = new System.Drawing.Size(153, 22);
this.toolsMenuPoly1.Text = "Star 1";
this.toolsMenuPoly1.Click += new System.EventHandler(this.toolsMenuPoly1_Click);
//
// toolsMenuRandPts
//
this.toolsMenuRandPts.Name = "toolsMenuRandPts";
this.toolsMenuRandPts.Size = new System.Drawing.Size(153, 22);
this.toolsMenuRandPts.Text = "Random points";
this.toolsMenuRandPts.Click += new System.EventHandler(this.toolsMenuRandPts_Click);
//
// toolsMenuCheck
//
this.toolsMenuCheck.Name = "toolsMenuCheck";
this.toolsMenuCheck.Size = new System.Drawing.Size(152, 22);
this.toolsMenuCheck.Text = "Check Mesh";
this.toolsMenuCheck.Click += new System.EventHandler(this.toolsMenuCheck_Click);
//
// meshRenderer1
//
this.meshRenderer1.BackColor = System.Drawing.Color.Black;
this.meshRenderer1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.meshRenderer1.Location = new System.Drawing.Point(0, 52);
this.meshRenderer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.meshRenderer1.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.meshRenderer1.Location = new System.Drawing.Point(0, 0);
this.meshRenderer1.Name = "meshRenderer1";
this.meshRenderer1.Size = new System.Drawing.Size(934, 600);
this.meshRenderer1.TabIndex = 5;
this.meshRenderer1.Size = new System.Drawing.Size(703, 612);
this.meshRenderer1.TabIndex = 0;
this.meshRenderer1.Text = "meshRenderer1";
//
// lbTime
//
this.lbTime.AutoSize = true;
this.lbTime.ForeColor = System.Drawing.SystemColors.HotTrack;
this.lbTime.Location = new System.Drawing.Point(323, 17);
this.lbTime.Name = "lbTime";
this.lbTime.Size = new System.Drawing.Size(0, 13);
this.lbTime.TabIndex = 2;
//
// Form1
// FormMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76)))));
this.ClientSize = new System.Drawing.Size(934, 652);
this.Controls.Add(this.btnStatistic);
this.Controls.Add(this.tbNumPoints);
this.Controls.Add(this.cbConvex);
this.Controls.Add(this.cbQuality);
this.Controls.Add(this.btnRun);
this.Controls.Add(this.btnOpen);
this.Controls.Add(this.btnRandPts);
this.Controls.Add(this.meshRenderer1);
this.Controls.Add(this.lbTime);
this.ClientSize = new System.Drawing.Size(984, 612);
this.Controls.Add(this.splitContainer1);
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.ForeColor = System.Drawing.Color.White;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.Name = "Form1";
this.Text = "Delaunay Triangulation";
this.KeyPreview = true;
this.MainMenuStrip = this.menuStrip1;
this.MinimumSize = new System.Drawing.Size(1000, 650);
this.Name = "FormMain";
this.Text = "Triangle.NET - Mesh Explorer";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResizeBegin += new System.EventHandler(this.ResizeBeginHandler);
this.ResizeEnd += new System.EventHandler(this.ResizeEndHandler);
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
this.Resize += new System.EventHandler(this.ResizeHandler);
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel1.PerformLayout();
this.splitContainer1.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
this.flatTabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage1.PerformLayout();
this.tabPage2.ResumeLayout(false);
this.tabPage2.PerformLayout();
this.tabPage3.ResumeLayout(false);
this.tabPage3.PerformLayout();
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private MeshRenderer meshRenderer1;
private Controls.DarkButton btnRandPts;
private Controls.DarkButton btnOpen;
private Controls.DarkButton btnRun;
private System.Windows.Forms.SplitContainer splitContainer1;
private Controls.MeshRenderer meshRenderer1;
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileMenu;
private System.Windows.Forms.ToolStripMenuItem fileMenuOpen;
private System.Windows.Forms.ToolStripMenuItem fileMenuSave;
private Controls.DarkTabControl flatTabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.TabPage tabPage2;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label lbNumSeg2;
private System.Windows.Forms.Label lbNumTri2;
private System.Windows.Forms.Label lbNumVert2;
private System.Windows.Forms.Label lbNumSeg;
private System.Windows.Forms.Label lbNumTri;
private System.Windows.Forms.Label lbNumVert;
private Controls.DarkButton btnSmooth;
private Controls.DarkButton btnMesh;
private Controls.DarkCheckBox cbQuality;
private Controls.DarkTextBox tbNumPoints;
private Controls.DarkButton btnStatistic;
private Controls.AngleHistogram angleHistogram1;
private Controls.DarkSlider slMinAngle;
private System.Windows.Forms.TabPage tabPage3;
private System.Windows.Forms.Label lbShortcuts;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label7;
private Controls.DarkCheckBox cbConvex;
private System.Windows.Forms.Label lbTime;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.Label label5;
private Controls.DarkSlider slMaxArea;
private System.Windows.Forms.Label lbMaxArea;
private System.Windows.Forms.Label lbMinAngle;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.Label label13;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.Label label16;
private System.Windows.Forms.Label label15;
private System.Windows.Forms.Label label14;
private System.Windows.Forms.Label lbAngleMax;
private System.Windows.Forms.Label lbRatioMax;
private System.Windows.Forms.Label lbEdgeMax;
private System.Windows.Forms.Label lbAreaMax;
private System.Windows.Forms.Label lbAngleMin;
private System.Windows.Forms.Label lbRatioMin;
private System.Windows.Forms.Label lbEdgeMin;
private System.Windows.Forms.Label lbAreaMin;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.Label label17;
private System.Windows.Forms.Label label11;
private System.Windows.Forms.ToolStripMenuItem viewMenu;
private System.Windows.Forms.ToolStripMenuItem viewMenuMQuality;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.ToolStripMenuItem viewMenuLog;
private System.Windows.Forms.ToolStripMenuItem toolsMenu;
private System.Windows.Forms.ToolStripMenuItem toolsMenuGen;
private System.Windows.Forms.ToolStripMenuItem toolsMenuPoly1;
private System.Windows.Forms.ToolStripMenuItem toolsMenuRandPts;
private System.Windows.Forms.ToolStripMenuItem toolsMenuCheck;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripMenuItem fileMenuQuit;
}
}
+422 -188
View File
@@ -1,115 +1,126 @@
using System;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
using MeshExplorer.Controls;
using MeshExplorer.IO;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.IO;
using TestApp.Rendering;
using TriangleNet.Tools;
namespace TestApp
namespace MeshExplorer
{
public partial class FormMain : Form
{
Random rand = new Random(DateTime.Now.Millisecond);
// Triangulation IO
MeshData input;
Settings settings;
InputGeometry input;
Mesh mesh;
Statistic stats;
// Filter index of the "Open file" dialog
int dlgFilterIndex = 1;
// Startup directory of the "Open file" dialog
string dlgDirectory = Application.StartupPath;
Statistic statistic = new Statistic();
FormQuality formStats;
FormLog frmLog;
FormQuality frmQuality;
public FormMain()
{
InitializeComponent();
ToolStripManager.Renderer = new DarkToolStripRenderer();
}
private double[][] CreateRandomPoints(int numPoints)
private void Form1_Load(object sender, EventArgs e)
{
bool save = false;
oldClientSize = this.ClientSize;
double[][] points = new double[numPoints][];
settings = new Settings();
int width = meshRenderer1.Width;
int height = meshRenderer1.Height;
if (save)
meshRenderer1.Initialize();
stats = new Statistic();
//BatchTest();
}
void BatchTest()
{
try
{
using (TextWriter fs = new StreamWriter(numPoints + ".txt"))
Mesh m = new Mesh();
m.SetOption(Options.MinAngle, 20);
for (int j = 0; j < 10; j++)
{
fs.WriteLine("{0} 2 0 0", numPoints);
for (int i = 0; i < numPoints; i++)
for (int i = 20; i > 0; i--)
{
points[i] = new double[] {
rand.NextDouble() * width,
rand.NextDouble() * height };
var geom = PolygonGenerator.CreateRandomPoints(10 * i, 100, 100);
fs.WriteLine(String.Format(CultureInfo.InvariantCulture.NumberFormat,
"{0} {1:0.0} {2:0.0}", (i + 1), points[i][0], points[i][1]));
m.Triangulate(geom);
}
}
}
else
catch (Exception e)
{
for (int i = 0; i < numPoints; i++)
{
points[i] = new double[] {
rand.NextDouble() * width,
rand.NextDouble() * height };
}
MessageBox.Show("BLUB\n" + e.Message);
}
return points;
}
private void Run()
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (input == null)
switch (e.KeyCode)
{
return;
case Keys.F3:
Open();
break;
case Keys.F4:
Save();
break;
case Keys.F5:
Reload();
break;
case Keys.F8:
TriangulateOrRefine();
break;
case Keys.F9:
Smooth();
break;
case Keys.F12:
ShowLog();
break;
}
}
Stopwatch sw = new Stopwatch();
private void btnMesh_Click(object sender, EventArgs e)
{
TriangulateOrRefine();
}
mesh = new Mesh();
mesh.SetOption(Options.Quality, cbQuality.Checked);
mesh.SetOption(Options.Convex, cbConvex.Checked);
private void btnSmooth_Click(object sender, EventArgs e)
{
//Smooth();
}
try
{
sw.Start();
mesh.Triangulate(input);
sw.Stop();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
private void slMinAngle_ValueChanging(object sender, EventArgs e)
{
// Between 0 and 40 (step 1)
int angle = (slMinAngle.Value * 40) / 100;
lbMinAngle.Text = angle.ToString();
}
meshRenderer1.SetData(mesh, false);
statistic.Update(mesh, 10);
if (formStats != null && !formStats.IsDisposed)
{
formStats.UpdateSatistic(statistic, sw.ElapsedMilliseconds, meshRenderer1.RenderTime);
}
private void slMaxArea_ValueChanging(object sender, EventArgs e)
{
// Between 0 and 1 (step 0.01)
double area = slMaxArea.Value * 0.01;
lbMaxArea.Text = area.ToString(Util.Nfi);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
Point pt = e.Location;
pt.Offset(0, -meshRenderer1.Top);
System.Drawing.Point pt = e.Location;
pt.Offset(-splitContainer1.SplitterDistance, 0);
if (meshRenderer1.ClientRectangle.Contains(pt))
{
@@ -118,174 +129,397 @@ namespace TestApp
base.OnMouseWheel(e);
}
private void Form1_Load(object sender, EventArgs e)
{
if (Directory.Exists(@"..\..\..\Data\"))
{
dlgDirectory = Path.GetFullPath(@"..\..\..\Data\");
#region Resize event handler
//Examples.Example1();
//Examples.Example2();
//Examples.Example3();
}
else if (Directory.Exists(@"Data\"))
bool isResizing = false;
Size oldClientSize;
private void ResizeHandler(object sender, EventArgs e)
{
// Handle window minimize and maximize
if (!isResizing)
{
dlgDirectory = Path.GetFullPath(@"Data\");
meshRenderer1.HandleResize();
}
}
private void btnRandPts_Click(object sender, EventArgs e)
private void ResizeEndHandler(object sender, EventArgs e)
{
btnRun.Text = "Triangulate";
isResizing = false;
int n = 10;
int.TryParse(tbNumPoints.Text, out n);
input = new MeshData();
input.Points = CreateRandomPoints(n);
meshRenderer1.SetData(input, true);
if (this.ClientSize != this.oldClientSize)
{
this.oldClientSize = this.ClientSize;
meshRenderer1.HandleResize();
}
}
private void btnOpen_Click(object sender, EventArgs e)
private void ResizeBeginHandler(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.InitialDirectory = dlgDirectory;
dlg.Filter = "Triangle polygon (*.node;*.poly)|*.node;*.poly|Polygon data (*.dat)|*.dat";
dlg.FilterIndex = dlgFilterIndex;
isResizing = true;
}
if (dlg.ShowDialog() == DialogResult.OK)
#endregion
#region State changes
private void HandleNewInput()
{
// Reset mesh
mesh = null;
// Reset state
settings.RefineMode = false;
settings.ExceptionThrown = false;
// Reset labels
lbNumVert2.Text = "-";
lbNumTri2.Text = "-";
lbNumSeg2.Text = "-";
lbNumVert.Text = input.Count.ToString();
lbNumSeg.Text = input.Segments.Count().ToString();
lbNumTri.Text = "0"; //input.Triangles == null ? "0" : input.Triangles.Length.ToString();
// Reset buttons
btnMesh.Enabled = true;
btnMesh.Text = "Triangulate";
btnSmooth.Enabled = false;
// Render input
meshRenderer1.SetData(input);
// Update window caption
this.Text = "Triangle.NET - Mesh Explorer - " + settings.CurrentFile;
// Disable menu items
viewMenuMQuality.Enabled = false;
}
private void HandleMeshChange()
{
// Render mesh
meshRenderer1.SetData(mesh);
// Previous mesh stats
lbNumVert2.Text = lbNumVert.Text;
lbNumTri2.Text = lbNumTri.Text;
lbNumSeg2.Text = lbNumSeg.Text;
// New mesh stats
lbNumVert.Text = stats.Vertices.ToString();
lbNumSeg.Text = stats.ConstrainedEdges.ToString();
lbNumTri.Text = stats.Triangles.ToString();
// Update statistics tab
angleHistogram1.SetData(stats.MinAngleHistogram, stats.MaxAngleHistogram);
lbAreaMin.Text = Util.DoubleToString(stats.SmallestArea);
lbAreaMax.Text = Util.DoubleToString(stats.LargestArea);
lbEdgeMin.Text = Util.DoubleToString(stats.ShortestEdge);
lbEdgeMax.Text = Util.DoubleToString(stats.LongestEdge);
lbRatioMin.Text = Util.DoubleToString(stats.ShortestAltitude);
lbRatioMax.Text = Util.DoubleToString(stats.LargestAspectRatio);
lbAngleMin.Text = Util.AngleToString(stats.SmallestAngle);
lbAngleMax.Text = Util.AngleToString(stats.LargestAngle);
// Enable menu items
viewMenuMQuality.Enabled = true;
}
#endregion
#region Commands
private void Open()
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = settings.OfdFilter;
ofd.FilterIndex = settings.OfdFilterIndex;
ofd.InitialDirectory = settings.OfdDirectory;
ofd.FileName = "";
if (ofd.ShowDialog() == DialogResult.OK)
{
dlgDirectory = Path.GetDirectoryName(dlg.FileName);
input = FileProcessor.Open(ofd.FileName);
string file = dlg.FileName;
string ext = Path.GetExtension(file);
if (ext == ".dat")
if (input != null)
{
dlgFilterIndex = 2;
input = new MeshData();
input.Points = ParseDatFile(file);
// Update settings
settings.CurrentFile = Path.GetFileName(ofd.FileName);
int n = input.Points.Length;
int[][] segments = new int[n][];
for (int i = 0; i < n; i++)
{
segments[i] = new int[] { i, (i + 1) % n };
}
input.Segments = segments;
}
else
{
dlgFilterIndex = 1;
try
{
input = FileReader.ReadFile(file);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
input = null;
return;
}
HandleNewInput();
}
// else Message
meshRenderer1.SetData(input, true);
btnRun.Text = "Triangulate";
// Update folder settings
settings.OfdFilterIndex = ofd.FilterIndex;
settings.OfdDirectory = Path.GetFullPath(ofd.FileName);
}
}
private void btnRun_Click(object sender, EventArgs e)
private void Save()
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = settings.SfdFilter;
sfd.FilterIndex = settings.SfdFilterIndex;
sfd.InitialDirectory = settings.SfdDirectory;
sfd.FileName = "";
if (sfd.ShowDialog() == DialogResult.OK)
{
FileProcessor.Save(sfd.FileName, mesh);
}
}
private void Reload()
{
if (input != null)
{
mesh = null;
settings.RefineMode = false;
settings.ExceptionThrown = false;
HandleNewInput();
}
}
private void TriangulateOrRefine()
{
if (input == null || settings.ExceptionThrown) return;
if (settings.RefineMode == false)
{
Triangulate();
if (cbQuality.Checked)
{
btnMesh.Text = "Refine";
//btnSmooth.Enabled = true;
}
}
else
{
Refine();
}
}
private void Triangulate()
{
if (input == null) return;
//Stopwatch sw = new Stopwatch();
mesh = new Mesh();
if (cbQuality.Checked)
{
if (btnRun.Text == "Triangulate")
mesh.SetOption(Options.Quality, true);
int angle = (slMinAngle.Value * 40) / 100;
mesh.SetOption(Options.MinAngle, angle);
double area = slMaxArea.Value * 0.01;
if (area > 0 && area < 1)
{
btnRun.Text = "Refine";
var size = input.Bounds;
Run();
}
else
{
Stopwatch sw = new Stopwatch();
double min = Math.Min(size.Width, size.Height);
try
{
sw.Start();
mesh.Refine(statistic.LargestArea / 2);
sw.Stop();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
meshRenderer1.SetData(mesh, false);
statistic.Update(mesh, 10);
if (formStats != null && !formStats.IsDisposed)
{
formStats.UpdateSatistic(statistic, sw.ElapsedMilliseconds, meshRenderer1.RenderTime);
}
mesh.SetOption(Options.MaxArea, area * min);
}
}
else
if (cbConvex.Checked)
{
btnRun.Text = "Triangulate";
Run();
mesh.SetOption(Options.Convex, true);
}
try
{
//sw.Start();
mesh.Triangulate(input);
//sw.Stop();
stats.Update(mesh, 10);
HandleMeshChange();
if (cbQuality.Checked)
{
settings.RefineMode = true;
}
}
catch (Exception ex)
{
settings.ExceptionThrown = true;
DarkMessageBox.Show("Exception - Triangulate", ex.Message);
}
UpdateLog();
}
private void btnStatistic_Click(object sender, EventArgs e)
private void Refine()
{
if (formStats == null)
if (mesh == null) return;
Stopwatch sw = new Stopwatch();
double area = slMaxArea.Value * 0.01;
if (area > 0 && area < 1)
{
formStats = new FormQuality();
mesh.SetOption(Options.MaxArea, area * stats.LargestArea);
}
if (!formStats.Visible)
int angle = (slMinAngle.Value * 40) / 100;
mesh.SetOption(Options.MinAngle, angle);
try
{
formStats.UpdateSatistic(this.statistic, -1, -1);
formStats.Show(this);
sw.Start();
mesh.Refine();
sw.Stop();
stats.Update(mesh, 10);
HandleMeshChange();
}
else
catch (Exception ex)
{
formStats.Hide();
settings.ExceptionThrown = true;
DarkMessageBox.Show("Exception - Refine", ex.Message);
}
UpdateLog();
}
public static double[][] ParseDatFile(string filename)
private void Smooth()
{
NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat;
if (mesh == null || settings.ExceptionThrown) return;
List<double[]> points = new List<double[]>();
Stopwatch sw = new Stopwatch();
string line;
string[] split;
using (TextReader reader = new StreamReader(filename))
try
{
while ((line = reader.ReadLine()) != null)
{
split = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
sw.Start();
mesh.Smooth();
sw.Stop();
if (split.Length == 2)
{
points.Add(new double[] {
double.Parse(split[0], nfi),
double.Parse(split[1], nfi)
});
}
}
stats.Update(mesh, 10);
HandleMeshChange();
}
catch (Exception ex)
{
settings.ExceptionThrown = true;
DarkMessageBox.Show("Exception - Smooth", ex.Message);
}
return points.ToArray();
UpdateLog();
}
private void ShowLog()
{
if (frmLog == null)
{
frmLog = new FormLog();
}
UpdateLog();
if (!frmLog.Visible)
{
frmLog.Show(this);
}
}
private void UpdateLog()
{
if (frmLog != null)
{
frmLog.UpdateItems();
}
}
private void ShowQuality()
{
if (frmQuality == null)
{
frmQuality = new FormQuality();
}
//UpdateLog();
if (!frmQuality.Visible)
{
frmQuality.Show(this);
}
}
#endregion
#region Menu Handler
private void fileMenuOpen_Click(object sender, EventArgs e)
{
Open();
}
private void fileMenuSave_Click(object sender, EventArgs ev)
{
if (mesh != null)
{
Save();
}
}
private void viewMenuLog_Click(object sender, EventArgs e)
{
ShowLog();
}
private void toolsMenuPoly1_Click(object sender, EventArgs e)
{
input = PolygonGenerator.StarInBox(20);
settings.CurrentFile = "star *";
HandleNewInput();
}
private void toolsMenuRandPts_Click(object sender, EventArgs e)
{
input = PolygonGenerator.CreateRandomPoints(10, 120, 100);
settings.CurrentFile = "points *";
HandleNewInput();
}
private void toolsMenuCheck_Click(object sender, EventArgs e)
{
if (mesh != null)
{
mesh.Check();
ShowLog();
}
}
private void viewMenuMQuality_Click(object sender, EventArgs e)
{
if (mesh != null)
{
ShowQuality();
}
frmQuality.UpdateQuality(meshRenderer1.Data);
}
private void fileMenuQuit_Click(object sender, EventArgs e)
{
this.Close();
}
#endregion
}
}
+3
View File
@@ -117,4 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>
+11 -432
View File
@@ -1,4 +1,4 @@
namespace TestApp
namespace MeshExplorer
{
partial class FormQuality
{
@@ -29,422 +29,33 @@
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.label9 = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
this.label11 = new System.Windows.Forms.Label();
this.label12 = new System.Windows.Forms.Label();
this.label13 = new System.Windows.Forms.Label();
this.label14 = new System.Windows.Forms.Label();
this.label15 = new System.Windows.Forms.Label();
this.label16 = new System.Windows.Forms.Label();
this.label17 = new System.Windows.Forms.Label();
this.lbNumInput = new System.Windows.Forms.Label();
this.lbNumOutput = new System.Windows.Forms.Label();
this.lbNumTri = new System.Windows.Forms.Label();
this.lbNumEdge = new System.Windows.Forms.Label();
this.lbNumBoundary = new System.Windows.Forms.Label();
this.lbCalcTime = new System.Windows.Forms.Label();
this.lbRenderTime = new System.Windows.Forms.Label();
this.lbAreaMin = new System.Windows.Forms.Label();
this.lbAreaMax = new System.Windows.Forms.Label();
this.lbEdgeMin = new System.Windows.Forms.Label();
this.lbEdgeMax = new System.Windows.Forms.Label();
this.lbRatioMin = new System.Windows.Forms.Label();
this.lbRatioMax = new System.Windows.Forms.Label();
this.lbAngleMin = new System.Windows.Forms.Label();
this.lbAngleMax = new System.Windows.Forms.Label();
this.histogram1 = new TestApp.Controls.AngleHistogram();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.ForeColor = System.Drawing.Color.Gray;
this.label1.Location = new System.Drawing.Point(11, 36);
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(79, 13);
this.label1.Size = new System.Drawing.Size(38, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Input vertices:";
this.label1.Text = "-";
//
// label2
//
this.label2.AutoSize = true;
this.label2.ForeColor = System.Drawing.Color.Gray;
this.label2.Location = new System.Drawing.Point(11, 59);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(79, 13);
this.label2.TabIndex = 0;
this.label2.Text = "Mesh vertices:";
//
// label3
//
this.label3.AutoSize = true;
this.label3.ForeColor = System.Drawing.Color.Gray;
this.label3.Location = new System.Drawing.Point(11, 81);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(86, 13);
this.label3.TabIndex = 0;
this.label3.Text = "Mesh triangles:";
//
// label4
//
this.label4.AutoSize = true;
this.label4.ForeColor = System.Drawing.Color.Gray;
this.label4.Location = new System.Drawing.Point(11, 103);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(72, 13);
this.label4.TabIndex = 0;
this.label4.Text = "Mesh edges:";
//
// label5
//
this.label5.AutoSize = true;
this.label5.ForeColor = System.Drawing.Color.Gray;
this.label5.Location = new System.Drawing.Point(11, 125);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(136, 13);
this.label5.TabIndex = 0;
this.label5.Text = "Exterior boundary edges:";
//
// label6
//
this.label6.AutoSize = true;
this.label6.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label6.ForeColor = System.Drawing.Color.White;
this.label6.Location = new System.Drawing.Point(12, 9);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(39, 13);
this.label6.TabIndex = 0;
this.label6.Text = "Mesh:";
//
// label7
//
this.label7.AutoSize = true;
this.label7.ForeColor = System.Drawing.Color.Gray;
this.label7.Location = new System.Drawing.Point(12, 191);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(79, 13);
this.label7.TabIndex = 0;
this.label7.Text = "Triangulation:";
//
// label8
//
this.label8.AutoSize = true;
this.label8.ForeColor = System.Drawing.Color.Gray;
this.label8.Location = new System.Drawing.Point(12, 212);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(64, 13);
this.label8.TabIndex = 0;
this.label8.Text = "Rendering:";
//
// label9
//
this.label9.AutoSize = true;
this.label9.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label9.ForeColor = System.Drawing.Color.White;
this.label9.Location = new System.Drawing.Point(11, 266);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(47, 13);
this.label9.TabIndex = 0;
this.label9.Text = "Quality:";
//
// label10
//
this.label10.AutoSize = true;
this.label10.ForeColor = System.Drawing.Color.Gray;
this.label10.Location = new System.Drawing.Point(12, 296);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(76, 13);
this.label10.TabIndex = 0;
this.label10.Text = "Triangle area:";
//
// label11
//
this.label11.AutoSize = true;
this.label11.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label11.ForeColor = System.Drawing.Color.White;
this.label11.Location = new System.Drawing.Point(12, 163);
this.label11.Name = "label11";
this.label11.Size = new System.Drawing.Size(65, 13);
this.label11.TabIndex = 0;
this.label11.Text = "Stopwatch:";
//
// label12
//
this.label12.AutoSize = true;
this.label12.ForeColor = System.Drawing.Color.Gray;
this.label12.Location = new System.Drawing.Point(109, 266);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(55, 13);
this.label12.TabIndex = 0;
this.label12.Text = "Minimum";
//
// label13
//
this.label13.AutoSize = true;
this.label13.ForeColor = System.Drawing.Color.Gray;
this.label13.Location = new System.Drawing.Point(199, 266);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(56, 13);
this.label13.TabIndex = 0;
this.label13.Text = "Maximum";
//
// label14
//
this.label14.AutoSize = true;
this.label14.ForeColor = System.Drawing.Color.Gray;
this.label14.Location = new System.Drawing.Point(12, 320);
this.label14.Name = "label14";
this.label14.Size = new System.Drawing.Size(73, 13);
this.label14.TabIndex = 0;
this.label14.Text = "Edge length:";
//
// label15
//
this.label15.AutoSize = true;
this.label15.ForeColor = System.Drawing.Color.Gray;
this.label15.Location = new System.Drawing.Point(11, 343);
this.label15.Name = "label15";
this.label15.Size = new System.Drawing.Size(71, 13);
this.label15.TabIndex = 0;
this.label15.Text = "Aspect ratio:";
//
// label16
//
this.label16.AutoSize = true;
this.label16.ForeColor = System.Drawing.Color.Gray;
this.label16.Location = new System.Drawing.Point(11, 366);
this.label16.Name = "label16";
this.label16.Size = new System.Drawing.Size(40, 13);
this.label16.TabIndex = 0;
this.label16.Text = "Angle:";
//
// label17
//
this.label17.AutoSize = true;
this.label17.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label17.ForeColor = System.Drawing.Color.White;
this.label17.Location = new System.Drawing.Point(11, 412);
this.label17.Name = "label17";
this.label17.Size = new System.Drawing.Size(97, 13);
this.label17.TabIndex = 0;
this.label17.Text = "Angle histogram:";
//
// lbNumInput
//
this.lbNumInput.AutoSize = true;
this.lbNumInput.ForeColor = System.Drawing.Color.White;
this.lbNumInput.Location = new System.Drawing.Point(171, 36);
this.lbNumInput.Name = "lbNumInput";
this.lbNumInput.Size = new System.Drawing.Size(11, 13);
this.lbNumInput.TabIndex = 0;
this.lbNumInput.Text = "-";
//
// lbNumOutput
//
this.lbNumOutput.AutoSize = true;
this.lbNumOutput.ForeColor = System.Drawing.Color.White;
this.lbNumOutput.Location = new System.Drawing.Point(171, 59);
this.lbNumOutput.Name = "lbNumOutput";
this.lbNumOutput.Size = new System.Drawing.Size(11, 13);
this.lbNumOutput.TabIndex = 0;
this.lbNumOutput.Text = "-";
//
// lbNumTri
//
this.lbNumTri.AutoSize = true;
this.lbNumTri.ForeColor = System.Drawing.Color.White;
this.lbNumTri.Location = new System.Drawing.Point(171, 81);
this.lbNumTri.Name = "lbNumTri";
this.lbNumTri.Size = new System.Drawing.Size(11, 13);
this.lbNumTri.TabIndex = 0;
this.lbNumTri.Text = "-";
//
// lbNumEdge
//
this.lbNumEdge.AutoSize = true;
this.lbNumEdge.ForeColor = System.Drawing.Color.White;
this.lbNumEdge.Location = new System.Drawing.Point(171, 103);
this.lbNumEdge.Name = "lbNumEdge";
this.lbNumEdge.Size = new System.Drawing.Size(11, 13);
this.lbNumEdge.TabIndex = 0;
this.lbNumEdge.Text = "-";
//
// lbNumBoundary
//
this.lbNumBoundary.AutoSize = true;
this.lbNumBoundary.ForeColor = System.Drawing.Color.White;
this.lbNumBoundary.Location = new System.Drawing.Point(171, 125);
this.lbNumBoundary.Name = "lbNumBoundary";
this.lbNumBoundary.Size = new System.Drawing.Size(11, 13);
this.lbNumBoundary.TabIndex = 0;
this.lbNumBoundary.Text = "-";
//
// lbCalcTime
//
this.lbCalcTime.AutoSize = true;
this.lbCalcTime.ForeColor = System.Drawing.Color.White;
this.lbCalcTime.Location = new System.Drawing.Point(171, 191);
this.lbCalcTime.Name = "lbCalcTime";
this.lbCalcTime.Size = new System.Drawing.Size(11, 13);
this.lbCalcTime.TabIndex = 0;
this.lbCalcTime.Text = "-";
//
// lbRenderTime
//
this.lbRenderTime.AutoSize = true;
this.lbRenderTime.ForeColor = System.Drawing.Color.White;
this.lbRenderTime.Location = new System.Drawing.Point(171, 212);
this.lbRenderTime.Name = "lbRenderTime";
this.lbRenderTime.Size = new System.Drawing.Size(11, 13);
this.lbRenderTime.TabIndex = 0;
this.lbRenderTime.Text = "-";
//
// lbAreaMin
//
this.lbAreaMin.ForeColor = System.Drawing.Color.White;
this.lbAreaMin.Location = new System.Drawing.Point(97, 296);
this.lbAreaMin.Name = "lbAreaMin";
this.lbAreaMin.Size = new System.Drawing.Size(68, 13);
this.lbAreaMin.TabIndex = 0;
this.lbAreaMin.Text = "-";
this.lbAreaMin.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbAreaMax
//
this.lbAreaMax.ForeColor = System.Drawing.Color.White;
this.lbAreaMax.Location = new System.Drawing.Point(179, 296);
this.lbAreaMax.Name = "lbAreaMax";
this.lbAreaMax.Size = new System.Drawing.Size(76, 13);
this.lbAreaMax.TabIndex = 0;
this.lbAreaMax.Text = "-";
this.lbAreaMax.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbEdgeMin
//
this.lbEdgeMin.ForeColor = System.Drawing.Color.White;
this.lbEdgeMin.Location = new System.Drawing.Point(97, 320);
this.lbEdgeMin.Name = "lbEdgeMin";
this.lbEdgeMin.Size = new System.Drawing.Size(68, 13);
this.lbEdgeMin.TabIndex = 0;
this.lbEdgeMin.Text = "-";
this.lbEdgeMin.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbEdgeMax
//
this.lbEdgeMax.ForeColor = System.Drawing.Color.White;
this.lbEdgeMax.Location = new System.Drawing.Point(179, 320);
this.lbEdgeMax.Name = "lbEdgeMax";
this.lbEdgeMax.Size = new System.Drawing.Size(76, 13);
this.lbEdgeMax.TabIndex = 0;
this.lbEdgeMax.Text = "-";
this.lbEdgeMax.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbRatioMin
//
this.lbRatioMin.ForeColor = System.Drawing.Color.White;
this.lbRatioMin.Location = new System.Drawing.Point(97, 343);
this.lbRatioMin.Name = "lbRatioMin";
this.lbRatioMin.Size = new System.Drawing.Size(68, 13);
this.lbRatioMin.TabIndex = 0;
this.lbRatioMin.Text = "-";
this.lbRatioMin.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbRatioMax
//
this.lbRatioMax.ForeColor = System.Drawing.Color.White;
this.lbRatioMax.Location = new System.Drawing.Point(179, 343);
this.lbRatioMax.Name = "lbRatioMax";
this.lbRatioMax.Size = new System.Drawing.Size(76, 13);
this.lbRatioMax.TabIndex = 0;
this.lbRatioMax.Text = "-";
this.lbRatioMax.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbAngleMin
//
this.lbAngleMin.ForeColor = System.Drawing.Color.White;
this.lbAngleMin.Location = new System.Drawing.Point(97, 366);
this.lbAngleMin.Name = "lbAngleMin";
this.lbAngleMin.Size = new System.Drawing.Size(68, 13);
this.lbAngleMin.TabIndex = 0;
this.lbAngleMin.Text = "-";
this.lbAngleMin.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// lbAngleMax
//
this.lbAngleMax.ForeColor = System.Drawing.Color.White;
this.lbAngleMax.Location = new System.Drawing.Point(179, 366);
this.lbAngleMax.Name = "lbAngleMax";
this.lbAngleMax.Size = new System.Drawing.Size(76, 13);
this.lbAngleMax.TabIndex = 0;
this.lbAngleMax.Text = "-";
this.lbAngleMax.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// histogram1
//
this.histogram1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76)))));
this.histogram1.Location = new System.Drawing.Point(15, 428);
this.histogram1.Name = "histogram1";
this.histogram1.Size = new System.Drawing.Size(257, 212);
this.histogram1.TabIndex = 1;
this.histogram1.Text = "histogram1";
//
// Form2
// FormQuality
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76)))));
this.ClientSize = new System.Drawing.Size(284, 652);
this.Controls.Add(this.histogram1);
this.Controls.Add(this.label8);
this.Controls.Add(this.label13);
this.Controls.Add(this.label12);
this.Controls.Add(this.label16);
this.Controls.Add(this.label15);
this.Controls.Add(this.label14);
this.Controls.Add(this.lbAngleMax);
this.Controls.Add(this.lbRatioMax);
this.Controls.Add(this.lbEdgeMax);
this.Controls.Add(this.lbAreaMax);
this.Controls.Add(this.lbAngleMin);
this.Controls.Add(this.lbRatioMin);
this.Controls.Add(this.lbEdgeMin);
this.Controls.Add(this.lbAreaMin);
this.Controls.Add(this.label10);
this.Controls.Add(this.label17);
this.Controls.Add(this.label9);
this.Controls.Add(this.label7);
this.Controls.Add(this.label11);
this.Controls.Add(this.label6);
this.Controls.Add(this.label5);
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.lbRenderTime);
this.Controls.Add(this.lbCalcTime);
this.Controls.Add(this.lbNumBoundary);
this.Controls.Add(this.lbNumEdge);
this.Controls.Add(this.lbNumTri);
this.Controls.Add(this.lbNumOutput);
this.Controls.Add(this.lbNumInput);
this.ClientSize = new System.Drawing.Size(200, 300);
this.Controls.Add(this.label1);
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.ForeColor = System.Drawing.Color.Gray;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Form2";
this.ShowIcon = false;
this.Name = "FormQuality";
this.ShowInTaskbar = false;
this.Text = "Mesh Statistic";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form2_FormClosing);
this.Text = "Mesh Quality";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormQuality_FormClosing);
this.ResumeLayout(false);
this.PerformLayout();
@@ -453,37 +64,5 @@
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.Label label11;
private System.Windows.Forms.Label label12;
private System.Windows.Forms.Label label13;
private System.Windows.Forms.Label label14;
private System.Windows.Forms.Label label15;
private System.Windows.Forms.Label label16;
private System.Windows.Forms.Label label17;
private System.Windows.Forms.Label lbNumInput;
private System.Windows.Forms.Label lbNumOutput;
private System.Windows.Forms.Label lbNumTri;
private System.Windows.Forms.Label lbNumEdge;
private System.Windows.Forms.Label lbNumBoundary;
private System.Windows.Forms.Label lbCalcTime;
private System.Windows.Forms.Label lbRenderTime;
private System.Windows.Forms.Label lbAreaMin;
private System.Windows.Forms.Label lbAreaMax;
private System.Windows.Forms.Label lbEdgeMin;
private System.Windows.Forms.Label lbEdgeMax;
private System.Windows.Forms.Label lbRatioMin;
private System.Windows.Forms.Label lbRatioMax;
private System.Windows.Forms.Label lbAngleMin;
private System.Windows.Forms.Label lbAngleMax;
private Controls.AngleHistogram histogram1;
}
}
+14 -55
View File
@@ -6,77 +6,36 @@ using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using TriangleNet;
using System.Globalization;
using TriangleNet.IO;
using MeshExplorer.Rendering;
namespace TestApp
namespace MeshExplorer
{
public partial class FormQuality : Form
{
static NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat;
public FormQuality()
{
InitializeComponent();
}
public void UpdateSatistic(Statistic stat, long calcTime, long renderTime)
public void UpdateQuality(RenderData data)
{
UpdateMesh(stat);
UpdateQuality(stat);
UpdateTime(calcTime, renderTime);
histogram1.SetData(stat.AngleHistogram);
}
private void UpdateMesh(Statistic stat)
{
lbNumInput.Text = stat.InputVertices.ToString();
lbNumOutput.Text = stat.Vertices.ToString();
lbNumTri.Text = stat.Triangles.ToString();
lbNumEdge.Text = stat.Edges.ToString();
lbNumBoundary.Text = stat.BoundaryEdges.ToString();
}
private void UpdateQuality(Statistic stat)
{
lbAreaMin.Text = stat.SmallestArea.ToString("0.00000", nfi);
lbAreaMax.Text = stat.LargestArea.ToString("0.00000", nfi);
lbEdgeMin.Text = stat.ShortestEdge.ToString("0.00000", nfi);
lbEdgeMax.Text = stat.LongestEdge.ToString("0.00000", nfi);
lbRatioMin.Text = stat.ShortestAltitude.ToString("0.00000", nfi);
lbRatioMax.Text = stat.LargestAspectRatio.ToString("0.00000", nfi);
lbAngleMin.Text = stat.SmallestAngle.ToString("0.00000", nfi);
lbAngleMax.Text = stat.LargestAngle.ToString("0.00000", nfi);
}
private void UpdateTime(long calcTime, long renderTime)
{
if (calcTime > 0)
if (data != null)
{
lbCalcTime.Text = calcTime + " ms";
}
else
{
lbCalcTime.Text = "-";
}
//MeshQuality q = new MeshQuality();
//string s = q.Update(data);
if (renderTime > 0)
{
lbRenderTime.Text = renderTime + " ms";
}
else
{
lbRenderTime.Text = "-";
//label1.Text = s;
}
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
private void FormQuality_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason != CloseReason.UserClosing) return;
e.Cancel = true;
this.Hide();
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
this.Hide();
}
}
}
}
+2 -2
View File
@@ -65,11 +65,11 @@ namespace MeshExplorer.IO
if (ext == ".node" || ext == ".poly")
{
//provider = new TriangleFile();
provider = new TriangleFile();
}
else if (ext == ".json")
{
//provider = new JsonFile();
provider = new JsonFile();
}
else if (ext == ".dat")
{
+134
View File
@@ -0,0 +1,134 @@
// -----------------------------------------------------------------------
// <copyright file="UcdFile.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace MeshExplorer.IO.Formats
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.IO;
using System.IO;
using MeshExplorer.Rendering;
using TriangleNet.Geometry;
using TriangleNet;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class JsonFile : MeshExplorer.IO.IMeshFormat
{
/// <summary>
/// Gets the supported file extensions.
/// </summary>
public string[] Extensions
{
get { return new string[] { ".json" }; }
}
/// <summary>
///
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
public InputGeometry Read(string filename)
{
string json = File.ReadAllText(filename);
JsonParser parser = new JsonParser("json");
object rawData = parser.Decode();
InputGeometry data = new InputGeometry();
List<ITriangle> triangles = new List<ITriangle>();
return data;
}
public void Write(string filename, Mesh data)
{
using (StreamWriter writer = new StreamWriter(filename))
{
int nv = data.NumberOfVertices;
int ns = data.NumberOfSegments;
int ne = data.NumberOfTriangles;
int nn = data.NumberOfTriangles;
int i = 0;
string sep = String.Empty;
// Header
writer.Write("{{\"format\":\"{0}\",\"dim\":2", ne > 0 ? "mesh" : (ns > 0 ? "poly" : "nodes"));
// Write the coordinates
writer.Write(",\"points\":[");
foreach (var item in data.Vertices)
{
sep = (i == nv - 1) ? String.Empty : ", ";
writer.Write("{0},{1}{2}",
item.X.ToString(Util.Nfi),
item.Y.ToString(Util.Nfi), sep);
i++;
}
writer.Write("]");
// Write the segments
if (ns > 0)
{
writer.Write(",\"segments\":[");
foreach (var item in data.Segments)
{
sep = (i == ns - 1) ? String.Empty : ", ";
writer.Write("{0},{1}{2}",
item.P0, item.P1, sep);
i++;
}
writer.Write("]");
}
// Write the elements
if (ne > 0)
{
writer.Write(",\"triangles\":[");
foreach (var item in data.Triangles)
{
sep = (i == ne - 1) ? String.Empty : ", ";
writer.Write("{0},{1},{2}{3}",
item.P0, item.P1, item.P2, sep);
i++;
}
writer.Write("]");
}
// Write neighbors
if (nn > 0)
{
writer.Write(",\"neighbors\":[");
//for (int i = 0; i < nn; i++)
//{
// sep = (i == nn - 1) ? String.Empty : ", ";
// writer.Write("{0},{1},{2}{3}",
// data.Neighbors[i][0],
// data.Neighbors[i][1],
// data.Neighbors[i][2], sep);
//}
writer.Write("]");
}
writer.Write("}");
}
}
}
}
@@ -0,0 +1,133 @@
// -----------------------------------------------------------------------
// <copyright file="TriangleFile.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace MeshExplorer.IO.Formats
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.IO;
using System.IO;
using TriangleNet.Geometry;
using TriangleNet;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class TriangleFile : MeshExplorer.IO.IMeshFormat
{
/// <summary>
/// Gets the supported file extensions.
/// </summary>
public string[] Extensions
{
get { return new string[] { ".node", ".poly" }; }
}
public InputGeometry Read(string file)
{
string supp = Path.ChangeExtension(file, ".ele");
return FileReader.ReadFile(file, File.Exists(supp));
}
public void Write(string file, Mesh data)
{
if (data.NumberOfVertices > 0)
{
WritePoly(file, data);
if (data.Triangles != null)
{
file = Path.ChangeExtension(file, "ele");
WriteElements(file, data);
}
}
}
private void WritePoly(string file, Mesh data)
{
int i = 0;
int n = data.NumberOfVertices;
bool markers = true;
using (StreamWriter writer = new StreamWriter(file))
{
// Write nodes
writer.WriteLine("{0} 2 0 {1}", n, markers ? "1" : "0");
// TODO: point attributes
foreach (var item in data.Vertices)
{
writer.Write("{0} {1} {2}", i++,
item.X.ToString(Util.Nfi),
item.Y.ToString(Util.Nfi));
if (markers)
{
writer.Write(" {0}", item.Boundary);
}
writer.WriteLine();
}
// Write segments
n = data.NumberOfSegments;
i = 0;
// Number of segments, number of boundary markers (zero or one).
writer.WriteLine("{0} {1}", n, markers ? "1" : "0");
foreach (var item in data.Segments)
{
writer.Write("{0} {1} {2}", i,
item.P0, item.P1);
if (markers)
{
writer.Write(" {0}", item.Boundary);
}
writer.WriteLine();
}
// Write holes
n = data.Holes.Count;
writer.WriteLine("{0}", n);
foreach (var item in data.Holes)
{
writer.WriteLine("{0} {1}",
data.Holes[i].X.ToString(Util.Nfi),
data.Holes[i].Y.ToString(Util.Nfi));
}
// TODO: Regions
}
}
public void WriteElements(string file, Mesh data)
{
using (StreamWriter writer = new StreamWriter(file))
{
int i = 0;
// TODO: attributes
writer.WriteLine("{0} 3 0", data.NumberOfTriangles);
foreach (var item in data.Triangles)
{
writer.WriteLine("{0} {1} {2} {3}", i++,
item.P0, item.P1, item.P2);
}
}
}
}
}
+310 -66
View File
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TestApp
namespace MeshExplorer
{
using System;
using System.Drawing;
@@ -20,8 +20,7 @@ namespace TestApp
/// </summary>
public static class ImageWriter
{
// Number of input points
static int NumberOfInputPoints = 0;
static PointF[] points;
// Default color scheme (dark)
static Color bgColor = Color.Black;
@@ -30,7 +29,7 @@ namespace TestApp
static Color lnColor = Color.FromArgb(30, 30, 30);
static Color sgColor = Color.Blue;
static Color trColor = Color.FromArgb(30, 40, 50);
/// <summary>
/// Sets the color scheme.
/// </summary>
@@ -39,7 +38,7 @@ namespace TestApp
/// <param name="steiner">Steiner points color.</param>
/// <param name="lines">Line color.</param>
/// <param name="segments">Segment color.</param>
public static void SetColorScheme(Color background, Color points, Color steiner,
public static void SetColorScheme(Color background, Color points, Color steiner,
Color lines, Color segments, Color triangles)
{
bgColor = background;
@@ -113,38 +112,20 @@ namespace TestApp
/// <param name="width">The target width of the image (pixel).</param>
public static void WritePng(Mesh mesh, string filename, int width)
{
NumberOfInputPoints = mesh.NumberOfInputPoints;
int i = 0, n = mesh.NumberOfVertices;
points = new PointF[n];
foreach (var pt in mesh.Vertices)
{
points[i++] = new PointF((float)pt.X, (float)pt.Y);
}
if (String.IsNullOrWhiteSpace(filename))
{
filename = String.Format("mesh-{0}.png", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss"));
}
MeshData data = mesh.GetMeshData(true, true, false);
// Mesh bounds
float minx = float.MaxValue;
float maxx = float.MinValue;
float miny = float.MaxValue;
float maxy = float.MinValue;
float x, y;
int n = data.Points.Length;
// Calculate bounds
for (int i = 0; i < n; i++)
{
x = (float)data.Points[i][0];
y = (float)data.Points[i][1];
// Update bounding box
if (minx > x) minx = x;
if (maxx < x) maxx = x;
if (miny > y) miny = y;
if (maxy < y) maxy = y;
}
Bitmap bitmap;
// Check if the specified width is reasonable
@@ -166,20 +147,87 @@ namespace TestApp
}
else
{
var bounds = mesh.Bounds;
// World margin on each side
float margin = (maxy - miny) * 0.05f;
float scale = width / (maxx - minx + 2 * margin);
float margin = (float)bounds.Height * 0.05f;
float scale = width / ((float)bounds.Width + 2 * margin);
bitmap = new Bitmap(width, (int)((maxy - miny + 2 * margin) * scale), PixelFormat.Format32bppArgb);
bitmap = new Bitmap(width, (int)((bounds.Height + 2 * margin) * scale), PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bitmap);
g.Clear(bgColor);
// Transform world to screen
g.ScaleTransform(scale, -scale);
g.TranslateTransform(-minx + margin, -maxy - margin);
g.TranslateTransform(-(float)bounds.Xmin + margin, -(float)bounds.Ymax - margin);
DrawMesh(g, data, scale);
DrawMesh(g, mesh, scale);
g.Dispose();
}
if (Path.GetExtension(filename) != ".png")
{
filename += ".png";
}
bitmap.Save(filename, ImageFormat.Png);
}
/// <summary>
/// Draws the voronoi diagram and writes the image file.
/// </summary>
/// <param name="mesh">The mesh to visualize.</param>
/// <param name="filename">The filename (only PNG supported).</param>
/// <param name="width">The target width of the image (pixel).</param>
public static void WriteVoronoiPng(Mesh mesh, string filename, int width)
{
if (String.IsNullOrWhiteSpace(filename))
{
filename = String.Format("mesh-{0}.png", DateTime.Now.ToString("yyyy-M-d-hh-mm-ss"));
}
VoronoiData data = DataWriter.WriteVoronoi(mesh);
int n = data.Points.Length;
var bounds = mesh.Bounds;
Bitmap bitmap;
// Check if the specified width is reasonable
if (width < 2 * Math.Sqrt(n))
{
bitmap = new Bitmap(400, 200);
Graphics g = Graphics.FromImage(bitmap);
g.Clear(Color.Black);
string message = String.Format("Sorry, I won't render {0} points on such a small image!", n);
SizeF sz = g.MeasureString(message, SystemFonts.DefaultFont);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawString(message, SystemFonts.DefaultFont, Brushes.White,
200 - sz.Width / 2, 100 - sz.Height / 2);
g.Dispose();
}
else
{
// World margin on each side
float margin = (float)bounds.Height * 0.05f;
float scale = width / ((float)bounds.Width + 2 * margin);
bitmap = new Bitmap(width, (int)((bounds.Height + 2 * margin) * scale), PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bitmap);
g.Clear(bgColor);
// Transform world to screen
g.ScaleTransform(scale, -scale);
g.TranslateTransform(-(float)bounds.Xmin + margin, -(float)bounds.Ymax - margin);
DrawVoronoi(g, mesh, data, scale);
g.Dispose();
}
@@ -195,11 +243,11 @@ namespace TestApp
/// <summary>
/// Draw mesh to the graphics object.
/// </summary>
private static void DrawMesh(Graphics g, MeshData mesh, float scale)
private static void DrawMesh(Graphics g, Mesh mesh, float scale)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
// Colors
Brush bgBrush = new SolidBrush(bgColor);
Brush ptBrush = new SolidBrush(ptColor);
Brush spBrush = new SolidBrush(spColor);
@@ -213,46 +261,38 @@ namespace TestApp
PointF p1, p2, p3;
int[] tmp;
// Draw triangle edges
int n = mesh.Triangles == null ? 0 : mesh.Triangles.Length;
for (int i = 0; i < n; i++)
foreach (var tri in mesh.Triangles)
{
tmp = mesh.Triangles[i];
p1 = new PointF((float)mesh.Points[tmp[0]][0], (float)mesh.Points[tmp[0]][1]);
p2 = new PointF((float)mesh.Points[tmp[1]][0], (float)mesh.Points[tmp[1]][1]);
p3 = new PointF((float)mesh.Points[tmp[2]][0], (float)mesh.Points[tmp[2]][1]);
p1 = points[tri.P0];
p2 = points[tri.P1];
p3 = points[tri.P2];
// Fill triangle
g.FillPolygon(trBrush, new PointF[] { p1, p2, p3 });
}
// Draw edges
/*
n = mesh.Edges == null ? 0 : mesh.Edges.Length;
for (int i = 0; i < n; i++)
{
tmp = mesh.Edges[i];
p1 = new PointF((float)mesh.Points[tmp[0]][0], (float)mesh.Points[tmp[0]][1]);
p2 = new PointF((float)mesh.Points[tmp[1]][0], (float)mesh.Points[tmp[1]][1]);
p1 = new PointF((float)mesh.Points[tmp[0]].X, (float)mesh.Points[tmp[0]].Y);
p2 = new PointF((float)mesh.Points[tmp[1]].X, (float)mesh.Points[tmp[1]].Y);
// Draw line
g.DrawLine(lnBrush, p1, p2);
}
* */
// Draw segments
n = mesh.Segments == null ? 0 : mesh.Segments.Length;
for (int i = 0; i < n; i++)
foreach (var seg in mesh.Segments)
{
tmp = mesh.Segments[i];
p1 = new PointF((float)mesh.Points[tmp[0]][0], (float)mesh.Points[tmp[0]][1]);
p2 = new PointF((float)mesh.Points[tmp[1]][0], (float)mesh.Points[tmp[1]][1]);
p1 = points[seg.P0];
p2 = points[seg.P1];
// Draw line
g.DrawLine(sgBrush, p1, p2);
@@ -262,19 +302,14 @@ namespace TestApp
float radius = 1.5f / scale, x, y;
// Draw points
n = mesh.Points.Length;
if (NumberOfInputPoints <= 0)
{
NumberOfInputPoints = n;
}
int n = mesh.NumberOfInputPoints;
for (int i = 0; i < n; i++)
{
x = (float)mesh.Points[i][0];
y = (float)mesh.Points[i][1];
x = points[i].X;
y = points[i].Y;
if (i < NumberOfInputPoints)
if (i < n)
{
g.FillEllipse(ptBrush, x - radius, y - radius, 2 * radius, 2 * radius);
//g.DrawEllipse(ptBrush, x - radius, y - radius, 2 * radius, 2 * radius);
@@ -293,5 +328,214 @@ namespace TestApp
sgBrush.Dispose();
trBrush.Dispose();
}
/// <summary>
/// Draw mesh to the graphics object.
/// </summary>
private static void DrawVoronoi(Graphics g, Mesh mesh, VoronoiData voronoi, float scale)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
// Colors
Brush bgBrush = new SolidBrush(bgColor);
Brush ptBrush = new SolidBrush(ptColor);
Brush spBrush = new SolidBrush(spColor);
Brush trBrush = new SolidBrush(trColor);
// Scale the pens to 1 pixel width
//Pen ptBrush = new Pen(ptColor, 1 / scale);
//Pen spBrush = new Pen(spColor, 1 / scale);
Pen lnBrush = new Pen(lnColor, 1 / scale);
Pen sgBrush = new Pen(sgColor, 1 / scale);
PointF p1, p2;
int[] tmp;
BBox bounds = new BBox(mesh.Bounds);
// Enlarge 50%
bounds.Extend(0.5f);
// Draw edges
int n = voronoi.Edges == null ? 0 : voronoi.Edges.Length;
for (int i = 0; i < n; i++)
{
var seg = voronoi.Edges[i];
if (seg.P1 == -1)
{
// Infinite voronoi edge
p1 = new PointF((float)voronoi.Points[seg.P0].X, (float)voronoi.Points[seg.P0].Y);
p2 = VoronoiBoxIntersection(bounds, voronoi.Points[seg.P0], voronoi.Directions[i]);
}
else
{
p1 = new PointF((float)voronoi.Points[seg.P0].X, (float)voronoi.Points[seg.P0].Y);
p2 = new PointF((float)voronoi.Points[seg.P1].X, (float)voronoi.Points[seg.P1].Y);
}
// Draw line
g.DrawLine(lnBrush, p1, p2);
}
// Shrink 50%
bounds.Extend(-0.5f);
// Scale the points radius to 2 pixel.
float radius = 1.5f / scale, x, y;
// Draw points
n = voronoi.Points.Length;
for (int i = 0; i < n; i++)
{
x = (float)voronoi.Points[i].X;
y = (float)voronoi.Points[i].Y;
g.FillEllipse(ptBrush, x - radius, y - radius, 2 * radius, 2 * radius);
//g.DrawEllipse(ptBrush, x - radius, y - radius, 2 * radius, 2 * radius);
}
// Draw input points
n = voronoi.InputPoints.Length;
for (int i = 0; i < n; i++)
{
x = (float)voronoi.InputPoints[i].X;
y = (float)voronoi.InputPoints[i].Y;
g.FillEllipse(spBrush, x - radius, y - radius, 2 * radius, 2 * radius);
//g.DrawEllipse(spBrush, x - radius, y - radius, 2 * radius, 2 * radius);
}
bgBrush.Dispose();
ptBrush.Dispose();
spBrush.Dispose();
lnBrush.Dispose();
sgBrush.Dispose();
trBrush.Dispose();
}
private static PointF VoronoiBoxIntersection(BBox bounds, TriangleNet.Geometry.Point pt, double[] direction)
{
double x = pt.X;
double y = pt.Y;
double dx = direction[0];
double dy = direction[1];
double t1, x1, y1, t2, x2, y2;
// Check if point is inside the bounds
if (x < bounds.MinX || x > bounds.MaxX || y < bounds.MinY || y > bounds.MaxY)
{
throw new ArgumentException("Point must be located inside the bounding box.");
}
// Calculate the cut through the vertical boundaries
if (dx < 0)
{
// Line going to the left: intersect with x = bounds.MinX
t1 = (bounds.MinX - x) / dx;
x1 = bounds.MinX;
y1 = y + t1 * dy;
}
else if (dx > 0)
{
// Line going to the right: intersect with x = bounds.MaxX
t1 = (bounds.MaxX - x) / dx;
x1 = bounds.MaxX;
y1 = y + t1 * dy;
}
else
{
// Line going straight up or down: no intersection possible
t1 = double.MaxValue;
x1 = y1 = 0;
}
// Calculate the cut through upper and lower boundaries
if (dy < 0)
{
// Line going downwards: intersect with y = bounds.MinY
t2 = (bounds.MinY - y) / dy;
x2 = x + t2 * dx;
y2 = bounds.MinY;
}
else if (dx > 0)
{
// Line going upwards: intersect with y = bounds.MaxY
t2 = (bounds.MaxY - y) / dy;
x2 = x + t2 * dx;
y2 = bounds.MaxY;
}
else
{
// Horizontal line: no intersection possible
t2 = double.MaxValue;
x2 = y2 = 0;
}
if (t1 < t2)
{
return new PointF((float)x1, (float)y1);
}
return new PointF((float)x2, (float)y2);
}
/// <summary>
/// Bounding box.
/// </summary>
struct BBox
{
public float MinX;
public float MaxX;
public float MinY;
public float MaxY;
public float Width { get { return MaxX - MinX; } }
public float Height { get { return MaxY - MinY; } }
public BBox(TriangleNet.Geometry.BoundingBox box)
{
MinX = (float)box.Xmin;
MaxX = (float)box.Xmax;
MinY = (float)box.Ymin;
MaxY = (float)box.Ymax;
}
public void Reset()
{
MinX = float.MaxValue;
MaxX = float.MinValue;
MinY = float.MaxValue;
MaxY = float.MinValue;
}
public void Update(TriangleNet.Geometry.Point pt)
{
float x = (float)pt.X;
float y = (float)pt.Y;
// Update bounding box
if (MinX > x) MinX = x;
if (MaxX < x) MaxX = x;
if (MinY > y) MinY = y;
if (MaxY < y) MaxY = y;
}
public void Extend(float amount)
{
float dx = amount * this.Width;
float dy = amount * this.Height;
MinX -= dx;
MaxX += dx;
MinY -= dy;
MaxY += dy;
}
}
}
}
+24
View File
@@ -46,6 +46,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Controls\ColorScheme.cs" />
<Compile Include="Controls\DarkButton.cs">
<SubType>Component</SubType>
</Compile>
@@ -55,10 +56,29 @@
<Compile Include="Controls\AngleHistogram.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\DarkListBox.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\DarkSlider.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\DarkTabControl.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\DarkTextBox.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Controls\DarkToolStripRenderer.cs" />
<Compile Include="DarkMessageBox.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Examples.cs" />
<Compile Include="FormLog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="FormLog.Designer.cs">
<DependentUpon>FormLog.cs</DependentUpon>
</Compile>
<Compile Include="FormMain.cs">
<SubType>Form</SubType>
</Compile>
@@ -77,12 +97,16 @@
<Compile Include="ImageWriter.cs" />
<Compile Include="IO\FileProcessor.cs" />
<Compile Include="IO\Formats\DatFile.cs" />
<Compile Include="IO\Formats\JsonFile.cs" />
<Compile Include="IO\Formats\TriangleFile.cs" />
<Compile Include="IO\IMeshFormat.cs" />
<Compile Include="IO\JsonParser.cs" />
<Compile Include="PolygonGenerator.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Rendering\RenderData.cs" />
<Compile Include="Rendering\Zoom.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Util.cs" />
<EmbeddedResource Include="FormMain.resx">
<DependentUpon>FormMain.cs</DependentUpon>
+115
View File
@@ -0,0 +1,115 @@
// -----------------------------------------------------------------------
// <copyright file="PolygonGenerator.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace MeshExplorer
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MeshExplorer.IO;
using TriangleNet.IO;
using MeshExplorer.Rendering;
using TriangleNet.Geometry;
/// <summary>
/// TODO: Update summary.
/// </summary>
public static class PolygonGenerator
{
public static InputGeometry StarInBox(int n)
{
InputGeometry input = new InputGeometry(n + 4);
input.AddPoint(0, 0); // Center
double x, y, r, e, step = 2 * Math.PI / n;
for (int i = 0; i < n; i++)
{
e = Util.Random.NextDouble() * step * 0.7;
r = (Util.Random.NextDouble() + 0.7) * 0.5;
x = r * Math.Cos(i * step + e);
y = r * Math.Sin(i * step + e);
input.AddPoint(x, y);
input.AddSegment(0, i + 1);
}
input.AddPoint(-1, -1); // Box
input.AddPoint(1, -1);
input.AddPoint(1, 1);
input.AddPoint(-1, 1);
n = input.Count;
input.AddSegment(n - 1, n - 2);
input.AddSegment(n - 2, n - 3);
input.AddSegment(n - 3, n - 4);
input.AddSegment(n - 4, n - 1);
return input;
}
public static InputGeometry CreateRandomPoints(int numPoints, int width, int height)
{
InputGeometry input = new InputGeometry(numPoints);
for (int i = 0; i < numPoints; i++)
{
input.AddPoint(Util.Random.NextDouble() * width,
Util.Random.NextDouble() * height);
}
return input;
}
public static InputGeometry CreateCirclePoints(double x, double y, double r, int n)
{
InputGeometry input = new InputGeometry(n + 1);
// Add center
input.AddPoint(x, y);
double angle = 0, step = 2 * Math.PI / n;
while (angle < 2 * Math.PI)
{
input.AddPoint(r * Math.Cos(angle), r * Math.Sin(angle));
angle += step;
}
return input;
}
public static InputGeometry CreateStarPoints(double x, double y, double r, int n)
{
InputGeometry input = new InputGeometry(n + 1);
// Add center
input.AddPoint(x, y);
double angle = 0, step = 2 * Math.PI / n;
while (angle < 2 * Math.PI)
{
input.AddPoint(r * Math.Cos(angle), r * Math.Sin(angle));
angle += step;
}
angle = step / 2;
r /= 1.5;
while (angle < 2 * Math.PI)
{
input.AddPoint(r * Math.Cos(angle), r * Math.Sin(angle));
angle += step;
}
return input;
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace TestApp
namespace MeshExplorer
{
static class Program
{
+68 -71
View File
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TestApp.Rendering
namespace MeshExplorer.Rendering
{
using System;
using System.Collections.Generic;
@@ -13,42 +13,82 @@ namespace TestApp.Rendering
using TriangleNet.IO;
using System.Drawing;
using TriangleNet;
using TriangleNet.Data;
using TriangleNet.Geometry;
public class RenderData
{
public PointF[] Points;
public int[][] Triangles;
public int[][] Edges;
public int[][] Segments;
public Edge[] Edges;
public Edge[] Segments;
public IEnumerable<ITriangle> Triangles;
public int NumberOfInputPoints;
public RectangleF Bounds;
public void SetData(InputGeometry data)
{
int n = data.Count;
int i = 0;
this.NumberOfInputPoints = n;
this.Triangles = null;
this.Edges = null;
// Convert points to float
this.Points = new PointF[n];
foreach (var pt in data.Points)
{
this.Points[i++] = new PointF((float)pt.X, (float)pt.Y);
}
this.Bounds = new RectangleF(
(float)data.Bounds.Xmin,
(float)data.Bounds.Ymin,
(float)data.Bounds.Width,
(float)data.Bounds.Height);
// Copy segments
this.Segments = data.Segments.ToArray();
}
/// <summary>
///
/// </summary>
/// <param name="mesh"></param>
/// <remarks>This methods assumes that the mesh.Renumber() has been called.</remarks>
public void SetData(Mesh mesh)
{
NumberOfInputPoints = mesh.NumberOfInputPoints;
mesh.Renumber();
SetData(mesh.GetMeshData(true, true, false), mesh.NumberOfInputPoints);
}
this.NumberOfInputPoints = mesh.NumberOfInputPoints;
public void SetData(MeshData data)
{
SetData(data, data.Points.Length);
}
this.Triangles = mesh.Triangles;
public void SetData(MeshData data, int inputCount)
{
NumberOfInputPoints = inputCount;
int n = mesh.NumberOfVertices;
int n = data.Points.Length;
// Reset
Triangles = null;
Edges = null;
Segments = null;
// Copy points
// Convert points to float
this.Points = new PointF[n];
SetPoints(mesh.Vertices);
// Get edges (more efficient than rendering triangles)
EdgeEnumerator e = new EdgeEnumerator(mesh);
List<Edge> edgeList = new List<Edge>(mesh.NumberOfEdges);
while (e.MoveNext())
{
edgeList.Add(e.Current);
}
this.Edges = edgeList.ToArray();
}
private void SetPoints(IEnumerable<Vertex> points)
{
// Bounds
float minx = float.MaxValue;
float maxx = float.MinValue;
@@ -56,11 +96,12 @@ namespace TestApp.Rendering
float maxy = float.MinValue;
float x, y;
int i = 0;
for (int i = 0; i < n; i += 1)
foreach (var pt in points)
{
x = (float)data.Points[i][0];
y = (float)data.Points[i][1];
x = (float)pt.X;
y = (float)pt.Y;
// Update bounding box
if (minx > x) minx = x;
if (maxx < x) maxx = x;
@@ -68,55 +109,11 @@ namespace TestApp.Rendering
if (maxy < y) maxy = y;
this.Points[i] = new PointF(x, y);
i++;
}
this.Bounds = new RectangleF(minx, miny, maxx - minx, maxy - miny);
n = data.Edges == null ? 0 : data.Edges.Length;
// Copy edges
if (data.Edges != null && n > 0)
{
Edges = new int[n][];
for (int i = 0; i < n; i++)
{
Edges[i] = new int[2];
Edges[i][0] = data.Edges[i][0];
Edges[i][1] = data.Edges[i][1];
}
}
n = data.Segments == null ? 0 : data.Segments.Length;
// Copy segments
if (data.Segments != null && n > 0)
{
Segments = new int[n][];
for (int i = 0; i < n; i++)
{
Segments[i] = new int[2];
Segments[i][0] = data.Segments[i][0];
Segments[i][1] = data.Segments[i][1];
}
}
n = data.Triangles == null ? 0 : data.Triangles.Length;
// Copy triangles
if (data.Triangles != null && n > 0)
{
Triangles = new int[n][];
for (int i = 0; i < n; i++)
{
Triangles[i] = new int[3];
Triangles[i][0] = data.Triangles[i][0];
Triangles[i][1] = data.Triangles[i][1];
Triangles[i][2] = data.Triangles[i][2];
}
}
}
}
}
+51 -12
View File
@@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TestApp.Rendering
namespace MeshExplorer.Rendering
{
using System;
using System.Collections.Generic;
@@ -18,8 +18,7 @@ namespace TestApp.Rendering
class Zoom
{
// The complete mesh
int screenWidth;
int screenHeight;
Rectangle Screen { get; set; }
// The complete mesh
RectangleF World { get; set; }
@@ -40,8 +39,7 @@ namespace TestApp.Rendering
public void Initialize(Rectangle screen, RectangleF world)
{
this.screenWidth = screen.Width;
this.screenHeight = screen.Height;
this.Screen = screen;
this.World = world;
this.Level = 1;
@@ -50,22 +48,57 @@ namespace TestApp.Rendering
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 = this.screenWidth / (float)this.screenHeight;
float screenRatio = screen.Width / (float)screen.Height;
float worldRatio = world.Width / world.Height;
float scale = (world.Width + worldMargin) / this.screenWidth;
float scale = (world.Width + worldMargin) / screen.Width;
if (screenRatio > worldRatio)
{
scale = (world.Height + worldMargin) / this.screenHeight;
scale = (world.Height + worldMargin) / screen.Height;
}
float centerX = world.X + world.Width / 2;
float centerY = world.Y + world.Height / 2;
// TODO: Add initial margin
this.Viewport = new RectangleF(centerX - this.screenWidth * scale / 2,
centerY - this.screenHeight * scale / 2,
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;
}
public void Resize(Rectangle screen, RectangleF world)
{
this.Screen = screen;
this.World = 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.X + World.Width / 2;
float centerY = World.Y + 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);
@@ -155,8 +188,14 @@ namespace TestApp.Rendering
public PointF WorldToScreen(PointF pt)
{
return new PointF((pt.X - Viewport.X) / Viewport.Width * screenWidth,
(1 - (pt.Y - Viewport.Y) / Viewport.Height) * screenHeight);
return new PointF((pt.X - Viewport.X) / Viewport.Width * Screen.Width,
(1 - (pt.Y - Viewport.Y) / Viewport.Height) * Screen.Height);
}
public PointF ScreenToWorld(float ptX, float ptY)
{
return new PointF(Viewport.X + Viewport.Width * ptX,
Viewport.Y + Viewport.Height * (1 - ptY));
}
public void Reset()
+71
View File
@@ -0,0 +1,71 @@
// -----------------------------------------------------------------------
// <copyright file="Settings.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace MeshExplorer
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class Settings
{
// Open file dialog
public string OfdDirectory { get; set; }
public string OfdFilter { get; set; }
public int OfdFilterIndex{ get; set; }
// Save file dialog
public string SfdDirectory { get; set; }
public string SfdFilter { get; set; }
public int SfdFilterIndex { get; set; }
public string CurrentFile { get; set; }
public bool RefineMode { get; set; }
public bool ExceptionThrown { get; set; }
public Settings()
{
if (Directory.Exists(@"..\..\..\Data\"))
{
OfdDirectory = Path.GetFullPath(@"..\..\..\Data\");
}
else if (Directory.Exists(@"Data\"))
{
OfdDirectory = Path.GetFullPath(@"Data\");
}
else
{
//System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase;
OfdDirectory = Application.StartupPath;
}
OfdFilter = "Triangle file (*.node;*.poly)|*.node;*.poly";
OfdFilter += "|Triangle.NET JSON (*.json)|*.json";
OfdFilter += "|Polygon data (*.dat)|*.dat";
//OfdFilter += "|COMSOL mesh (*.mphtxt)|*.mphtxt";
//OfdFilter += "|AVS UCD data (*.ucd)|*.ucd";
//OfdFilter += "|VTK data (*.vtk)|*.vtk";
OfdFilterIndex = 0;
SfdDirectory = OfdDirectory;
SfdFilter = OfdFilter;
SfdFilterIndex = 1;
CurrentFile = "";
RefineMode = false;
ExceptionThrown = false;
}
}
}
+44 -33
View File
@@ -76,7 +76,7 @@ namespace TriangleNet.Algorithm
{
var a = sortarray[i];
int j = i - 1;
while (j >= left && (sortarray[j].pt.X > a.pt.X || (sortarray[j].pt.X == a.pt.X && sortarray[j].pt.Y > a.pt.Y)))
while (j >= left && (sortarray[j].x > a.x || (sortarray[j].x == a.x && sortarray[j].y > a.y)))
{
sortarray[j + 1] = sortarray[j];
j--;
@@ -89,8 +89,8 @@ namespace TriangleNet.Algorithm
// Choose a random pivot to split the array.
pivot = rand.Next(left, right);
pivotx = sortarray[pivot].pt.X;
pivoty = sortarray[pivot].pt.Y;
pivotx = sortarray[pivot].x;
pivoty = sortarray[pivot].y;
// Split the array.
left--;
right++;
@@ -101,17 +101,17 @@ namespace TriangleNet.Algorithm
{
left++;
}
while ((left <= right) && ((sortarray[left].pt.X < pivotx) ||
((sortarray[left].pt.X == pivotx) &&
(sortarray[left].pt.Y < pivoty))));
while ((left <= right) && ((sortarray[left].x < pivotx) ||
((sortarray[left].x == pivotx) &&
(sortarray[left].y < pivoty))));
// Search for a vertex whose x-coordinate is too small for the right.
do
{
right--;
}
while ((left <= right) && ((sortarray[right].pt.X > pivotx) ||
((sortarray[right].pt.X == pivotx) &&
(sortarray[right].pt.Y > pivoty))));
while ((left <= right) && ((sortarray[right].x > pivotx) ||
((sortarray[right].x == pivotx) &&
(sortarray[right].y > pivoty))));
if (left < right)
{
@@ -295,10 +295,10 @@ namespace TriangleNet.Algorithm
ref Otri farright, int axis)
{
Otri leftcand = default(Otri), rightcand = default(Otri);
Otri baseedge = default(Otri);
Otri nextedge = default(Otri);
Otri sidecasing = default(Otri), topcasing = default(Otri), outercasing = default(Otri);
Otri checkedge = default(Otri);
Otri baseedge = default(Otri);
Vertex innerleftdest;
Vertex innerrightorg;
Vertex innerleftapex, innerrightapex;
@@ -326,7 +326,7 @@ namespace TriangleNet.Algorithm
// The pointers to the extremal vertices are shifted to point to the
// topmost and bottommost vertex of each hull, rather than the
// leftmost and rightmost vertices.
while (farleftapex.pt.Y < farleftpt.pt.Y)
while (farleftapex.y < farleftpt.y)
{
farleft.LnextSelf();
farleft.SymSelf();
@@ -335,7 +335,7 @@ namespace TriangleNet.Algorithm
}
innerleft.Sym(ref checkedge);
checkvertex = checkedge.Apex();
while (checkvertex.pt.Y > innerleftdest.pt.Y)
while (checkvertex.y > innerleftdest.y)
{
checkedge.Lnext(ref innerleft);
innerleftapex = innerleftdest;
@@ -343,7 +343,7 @@ namespace TriangleNet.Algorithm
innerleft.Sym(ref checkedge);
checkvertex = checkedge.Apex();
}
while (innerrightapex.pt.Y < innerrightorg.pt.Y)
while (innerrightapex.y < innerrightorg.y)
{
innerright.LnextSelf();
innerright.SymSelf();
@@ -352,7 +352,7 @@ namespace TriangleNet.Algorithm
}
farright.Sym(ref checkedge);
checkvertex = checkedge.Apex();
while (checkvertex.pt.Y > farrightpt.pt.Y)
while (checkvertex.y > farrightpt.y)
{
checkedge.Lnext(ref farright);
farrightapex = farrightpt;
@@ -366,7 +366,7 @@ namespace TriangleNet.Algorithm
{
changemade = false;
// Make innerleftdest the "bottommost" vertex of the left hull.
if (Primitives.CounterClockwise(innerleftdest.pt, innerleftapex.pt, innerrightorg.pt) > 0.0)
if (Primitives.CounterClockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0)
{
innerleft.LprevSelf();
innerleft.SymSelf();
@@ -375,7 +375,7 @@ namespace TriangleNet.Algorithm
changemade = true;
}
// Make innerrightorg the "bottommost" vertex of the right hull.
if (Primitives.CounterClockwise(innerrightapex.pt, innerrightorg.pt, innerleftdest.pt) > 0.0)
if (Primitives.CounterClockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0)
{
innerright.LnextSelf();
innerright.SymSelf();
@@ -384,6 +384,7 @@ namespace TriangleNet.Algorithm
changemade = true;
}
} while (changemade);
// Find the two candidates to be the next "gear tooth."
innerleft.Sym(ref leftcand);
innerright.Sym(ref rightcand);
@@ -418,12 +419,12 @@ namespace TriangleNet.Algorithm
// Walk up the gap between the two triangulations, knitting them together.
while (true)
{
// Have we reached the top? (This isn't quite the right question,
// because even though the left triangulation might seem finished now,
// moving up on the right triangulation might reveal a new vertex of
// the left triangulation. And vice-versa.)
leftfinished = Primitives.CounterClockwise(upperleft.pt, lowerleft.pt, lowerright.pt) <= 0.0;
rightfinished = Primitives.CounterClockwise(upperright.pt, lowerleft.pt, lowerright.pt) <= 0.0;
// Have we reached the top? (This isn't quite the right question,
// because even though the left triangulation might seem finished now,
// moving up on the right triangulation might reveal a new vertex of
// the left triangulation. And vice-versa.)
leftfinished = Primitives.CounterClockwise(upperleft, lowerleft, lowerright) <= 0.0;
rightfinished = Primitives.CounterClockwise(upperright, lowerleft, lowerright) <= 0.0;
if (leftfinished && rightfinished)
{
// Create the top new bounding triangle.
@@ -450,7 +451,7 @@ namespace TriangleNet.Algorithm
// The pointers to the extremal vertices are restored to the
// leftmost and rightmost vertices (rather than topmost and
// bottommost).
while (checkvertex.pt.X < farleftpt.pt.X)
while (checkvertex.x < farleftpt.x)
{
checkedge.Lprev(ref farleft);
farleftapex = farleftpt;
@@ -458,7 +459,7 @@ namespace TriangleNet.Algorithm
farleft.Sym(ref checkedge);
checkvertex = checkedge.Apex();
}
while (farrightapex.pt.X > farrightpt.pt.X)
while (farrightapex.x > farrightpt.x)
{
farright.LprevSelf();
farright.SymSelf();
@@ -480,7 +481,7 @@ namespace TriangleNet.Algorithm
if (nextapex != null)
{
// Check whether the edge is Delaunay.
badedge = Primitives.InCircle(lowerleft.pt, lowerright.pt, upperleft.pt, nextapex.pt) > 0.0;
badedge = Primitives.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0;
while (badedge)
{
// Eliminate the edge with an edge flip. As a result, the
@@ -510,7 +511,7 @@ namespace TriangleNet.Algorithm
if (nextapex != null)
{
// Check whether the edge is Delaunay.
badedge = Primitives.InCircle(lowerleft.pt, lowerright.pt, upperleft.pt, nextapex.pt) > 0.0;
badedge = Primitives.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0;
}
else
{
@@ -532,7 +533,7 @@ namespace TriangleNet.Algorithm
if (nextapex != null)
{
// Check whether the edge is Delaunay.
badedge = Primitives.InCircle(lowerleft.pt, lowerright.pt, upperright.pt, nextapex.pt) > 0.0;
badedge = Primitives.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
while (badedge)
{
// Eliminate the edge with an edge flip. As a result, the
@@ -562,7 +563,7 @@ namespace TriangleNet.Algorithm
if (nextapex != null)
{
// Check whether the edge is Delaunay.
badedge = Primitives.InCircle(lowerleft.pt, lowerright.pt, upperright.pt, nextapex.pt) > 0.0;
badedge = Primitives.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
}
else
{
@@ -573,7 +574,7 @@ namespace TriangleNet.Algorithm
}
}
if (leftfinished || (!rightfinished &&
(Primitives.InCircle(upperleft.pt, lowerleft.pt, lowerright.pt, upperright.pt) > 0.0)))
(Primitives.InCircle(upperleft, lowerleft, lowerright, upperright) > 0.0)))
{
// Knit the triangulations, adding an edge from 'lowerleft'
// to 'upperright'.
@@ -663,7 +664,7 @@ namespace TriangleNet.Algorithm
mesh.MakeTriangle(ref tri1);
mesh.MakeTriangle(ref tri2);
mesh.MakeTriangle(ref tri3);
area = Primitives.CounterClockwise(sortarray[left].pt, sortarray[left + 1].pt, sortarray[left + 2].pt);
area = Primitives.CounterClockwise(sortarray[left], sortarray[left + 1], sortarray[left + 2]);
if (area == 0.0)
{
// Three collinear vertices; the triangulation is two edges.
@@ -759,10 +760,13 @@ namespace TriangleNet.Algorithm
divider = vertices >> 1;
// Recursively triangulate each half.
DivconqRecurse(left, left + divider - 1, 1 - axis, ref farleft, ref innerleft);
///dbgWriter.Write();
DivconqRecurse(left + divider, right, 1 - axis, ref innerright, ref farright);
///dbgWriter.Write();
// Merge the two triangulations into one.
MergeHulls(ref farleft, ref innerleft, ref innerright, ref farright, axis);
///dbgWriter.Write();
}
}
@@ -792,6 +796,7 @@ namespace TriangleNet.Algorithm
dissolveedge.Lnext(ref deadtriangle);
dissolveedge.LprevSelf();
dissolveedge.SymSelf();
// If no PSLG is involved, set the boundary markers of all the vertices
// on the convex hull. If a PSLG is used, this step is done later.
if (!Behavior.Poly)
@@ -810,6 +815,7 @@ namespace TriangleNet.Algorithm
dissolveedge.Dissolve();
// Find the next bounding triangle.
deadtriangle.Sym(ref dissolveedge);
// Delete the bounding triangle.
mesh.TriangleDealloc(deadtriangle.triangle);
} while (!dissolveedge.Equal(startghost));
@@ -817,6 +823,8 @@ namespace TriangleNet.Algorithm
return hullsize;
}
IO.DebugWriter dbgWriter;
/// <summary>
/// Form a Delaunay triangulation by the divide-and-conquer method.
/// </summary>
@@ -833,7 +841,10 @@ namespace TriangleNet.Algorithm
this.mesh = m;
dbgWriter = new IO.DebugWriter(m);
// Allocate an array of pointers to vertices for sorting.
// TODO: use ToArray
this.sortarray = new Vertex[m.invertices];
i = 0;
foreach (var v in m.vertices.Values)
@@ -847,13 +858,13 @@ namespace TriangleNet.Algorithm
i = 0;
for (j = 1; j < m.invertices; j++)
{
if ((sortarray[i].pt.X == sortarray[j].pt.X)
&& (sortarray[i].pt.Y == sortarray[j].pt.Y))
if ((sortarray[i].x == sortarray[j].x)
&& (sortarray[i].y == sortarray[j].y))
{
if (Behavior.Verbose)
{
SimpleLog.Instance.Warning(
String.Format("A duplicate vertex appeared and was ignored (ID {0}).", sortarray[j].Hash),
String.Format("A duplicate vertex appeared and was ignored (ID {0}).", sortarray[j].hash),
"DivConquer.DivconqDelaunay()");
}
sortarray[j].type = VertexType.UndeadVertex;
+11 -16
View File
@@ -9,6 +9,7 @@ namespace TriangleNet.Algorithm
{
using TriangleNet.Data;
using TriangleNet.Log;
using TriangleNet.Geometry;
/// <summary>
/// Builds a delaunay triangulation using the incremental algorithm.
@@ -25,31 +26,25 @@ namespace TriangleNet.Algorithm
/// used by the point location routines, but (mostly) ignored by the
/// Delaunay edge flip routines.
/// </remarks>
void BoundingBox()
void GetBoundingBox()
{
Otri inftri = default(Otri); // Handle for the triangular bounding box.
double width;
BoundingBox box = mesh.bounds;
// Find the width (or height, whichever is larger) of the triangulation.
width = mesh.xmax - mesh.xmin;
if (mesh.ymax - mesh.ymin > width)
double width = box.Width;
if (box.Height > width)
{
width = mesh.ymax - mesh.ymin;
width = box.Height;
}
if (width == 0.0)
{
width = 1.0;
}
// Create the vertices of the bounding box.
mesh.infvertex1 = new Vertex();
mesh.infvertex2 = new Vertex();
mesh.infvertex3 = new Vertex();
mesh.infvertex1.pt.X = mesh.xmin - 50.0 * width;
mesh.infvertex1.pt.Y = mesh.ymin - 40.0 * width;
mesh.infvertex2.pt.X = mesh.xmax + 50.0 * width;
mesh.infvertex2.pt.Y = mesh.ymin - 40.0 * width;
mesh.infvertex3.pt.X = 0.5 * (mesh.xmin + mesh.xmax);
mesh.infvertex3.pt.Y = mesh.ymax + 60.0 * width;
mesh.infvertex1 = new Vertex(box.Xmin - 50.0 * width, box.Ymin - 40.0 * width);
mesh.infvertex2 = new Vertex(box.Xmax + 50.0 * width, box.Ymin - 40.0 * width);
mesh.infvertex3 = new Vertex(0.5 * (box.Xmin + box.Xmax), box.Ymax + 60.0 * width);
// Create the bounding box.
mesh.MakeTriangle(ref inftri);
@@ -160,7 +155,7 @@ namespace TriangleNet.Algorithm
Otri starttri = new Otri();
// Create a triangular bounding box.
BoundingBox();
GetBoundingBox();
foreach (var v in mesh.vertices.Values)
{
@@ -170,7 +165,7 @@ namespace TriangleNet.Algorithm
{
if (Behavior.Verbose)
{
SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.",
SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.",
"Incremental.IncrementalDelaunay()");
}
v.type = VertexType.UndeadVertex;
+104 -54
View File
@@ -13,27 +13,14 @@ namespace TriangleNet.Algorithm
using System.Text;
using TriangleNet.Data;
using TriangleNet.Log;
using TriangleNet.Geometry;
using TriangleNet.Tools;
/// <summary>
/// Builds a delaunay triangulation using the sweepline algorithm.
/// </summary>
class SweepLine
{
/// <summary>
/// Introducing a new class which aggregates a sweep event is the easiest way
/// to handle the pointer magic of the original code (casting a sweep event
/// to vertex etc.).
/// </summary>
class SweepEventVertex : Vertex
{
public SweepEvent evt;
public SweepEventVertex(SweepEvent e)
{
evt = e;
}
}
static int randomseed = 1;
static int SAMPLERATE = 10;
@@ -187,8 +174,8 @@ namespace TriangleNet.Algorithm
thisvertex = v;
evt = new SweepEvent();
evt.vertexEvent = thisvertex;
evt.xkey = thisvertex.pt.X;
evt.ykey = thisvertex.pt.Y;
evt.xkey = thisvertex.x;
evt.ykey = thisvertex.y;
HeapInsert(eventheap, i++, evt);
}
@@ -198,7 +185,7 @@ namespace TriangleNet.Algorithm
#region Splaytree
SplayNode Splay(SplayNode splaytree, Vertex searchpoint, ref Otri searchtri)
SplayNode Splay(SplayNode splaytree, Point searchpoint, ref Otri searchtri)
{
SplayNode child, grandchild;
SplayNode lefttree, righttree;
@@ -342,7 +329,7 @@ namespace TriangleNet.Algorithm
}
}
SplayNode SplayInsert(SplayNode splayroot, Otri newkey, Vertex searchpoint)
SplayNode SplayInsert(SplayNode splayroot, Otri newkey, Point searchpoint)
{
SplayNode newsplaynode;
@@ -378,22 +365,22 @@ namespace TriangleNet.Algorithm
double ccwabc;
double xac, yac, xbc, ybc;
double aclen2, bclen2;
Vertex searchpoint = new Vertex(mesh.nextras);
Point searchpoint = new Point(); // TODO: mesh.nextras
Otri dummytri = default(Otri);
ccwabc = Primitives.CounterClockwise(pa.pt, pb.pt, pc.pt);
xac = pa.pt.X - pc.pt.X;
yac = pa.pt.Y - pc.pt.Y;
xbc = pb.pt.X - pc.pt.X;
ybc = pb.pt.Y - pc.pt.Y;
ccwabc = Primitives.CounterClockwise(pa, pb, pc);
xac = pa.x - pc.x;
yac = pa.y - pc.y;
xbc = pb.x - pc.x;
ybc = pb.y - pc.y;
aclen2 = xac * xac + yac * yac;
bclen2 = xbc * xbc + ybc * ybc;
searchpoint.pt.X = pc.pt.X - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
searchpoint.pt.Y = topy;
searchpoint.x = pc.x - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
searchpoint.y = topy;
return SplayInsert(Splay(splayroot, searchpoint, ref dummytri), newkey, searchpoint);
}
bool RightOfHyperbola(ref Otri fronttri, Vertex newsite)
bool RightOfHyperbola(ref Otri fronttri, Point newsite)
{
Vertex leftvertex, rightvertex;
double dxa, dya, dxb, dyb;
@@ -402,26 +389,26 @@ namespace TriangleNet.Algorithm
leftvertex = fronttri.Dest();
rightvertex = fronttri.Apex();
if ((leftvertex.pt.Y < rightvertex.pt.Y) ||
((leftvertex.pt.Y == rightvertex.pt.Y) &&
(leftvertex.pt.X < rightvertex.pt.X)))
if ((leftvertex.y < rightvertex.y) ||
((leftvertex.y == rightvertex.y) &&
(leftvertex.x < rightvertex.x)))
{
if (newsite.pt.X >= rightvertex.pt.X)
if (newsite.x >= rightvertex.x)
{
return true;
}
}
else
{
if (newsite.pt.X <= leftvertex.pt.X)
if (newsite.x <= leftvertex.x)
{
return false;
}
}
dxa = leftvertex.pt.X - newsite.pt.X;
dya = leftvertex.pt.Y - newsite.pt.Y;
dxb = rightvertex.pt.X - newsite.pt.X;
dyb = rightvertex.pt.Y - newsite.pt.Y;
dxa = leftvertex.x - newsite.x;
dya = leftvertex.y - newsite.y;
dxb = rightvertex.x - newsite.x;
dyb = rightvertex.y - newsite.y;
return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya);
}
@@ -432,16 +419,16 @@ namespace TriangleNet.Algorithm
Statistic.CircleTopCount++;
xac = pa.pt.X - pc.pt.X;
yac = pa.pt.Y - pc.pt.Y;
xbc = pb.pt.X - pc.pt.X;
ybc = pb.pt.Y - pc.pt.Y;
xab = pa.pt.X - pb.pt.X;
yab = pa.pt.Y - pb.pt.Y;
xac = pa.x - pc.x;
yac = pa.y - pc.y;
xbc = pb.x - pc.x;
ybc = pb.y - pc.y;
xab = pa.x - pb.x;
yab = pa.y - pb.y;
aclen2 = xac * xac + yac * yac;
bclen2 = xbc * xbc + ybc * ybc;
ablen2 = xab * xab + yab * yab;
return pc.pt.Y + (xac * bclen2 - xbc * aclen2 + Math.Sqrt(aclen2 * bclen2 * ablen2)) / (2.0 * ccwabc);
return pc.y + (xac * bclen2 - xbc * aclen2 + Math.Sqrt(aclen2 * bclen2 * ablen2)) / (2.0 * ccwabc);
}
void Check4DeadEvent(ref Otri checktri, SweepEvent[] eventheap, ref int heapsize)
@@ -506,6 +493,7 @@ namespace TriangleNet.Algorithm
dissolveedge.Lnext(ref deadtriangle);
dissolveedge.LprevSelf();
dissolveedge.SymSelf();
// If no PSLG is involved, set the boundary markers of all the vertices
// on the convex hull. If a PSLG is used, this step is done later.
if (!Behavior.Poly)
@@ -524,6 +512,7 @@ namespace TriangleNet.Algorithm
dissolveedge.Dissolve();
// Find the next bounding triangle.
deadtriangle.Sym(ref dissolveedge);
// Delete the bounding triangle.
mesh.TriangleDealloc(deadtriangle.triangle);
} while (!dissolveedge.Equal(startghost));
@@ -537,7 +526,7 @@ namespace TriangleNet.Algorithm
// Nonexistent x value used as a flag to mark circle events in sweepline
// Delaunay algorithm.
xminextreme = 10 * mesh.xmin - 9 * mesh.xmax;
xminextreme = 10 * mesh.bounds.Xmin - 9 * mesh.bounds.Xmax;
SweepEvent[] eventheap;
@@ -589,8 +578,8 @@ namespace TriangleNet.Algorithm
secondvertex = eventheap[0].vertexEvent;
HeapDelete(eventheap, heapsize, 0);
heapsize--;
if ((firstvertex.pt.X == secondvertex.pt.X) &&
(firstvertex.pt.Y == secondvertex.pt.Y))
if ((firstvertex.x == secondvertex.x) &&
(firstvertex.y == secondvertex.y))
{
if (Behavior.Verbose)
{
@@ -600,8 +589,8 @@ namespace TriangleNet.Algorithm
secondvertex.type = VertexType.UndeadVertex;
mesh.undeads++;
}
} while ((firstvertex.pt.X == secondvertex.pt.X) &&
(firstvertex.pt.Y == secondvertex.pt.Y));
} while ((firstvertex.x == secondvertex.x) &&
(firstvertex.y == secondvertex.y));
lefttri.SetOrg(firstvertex);
lefttri.SetDest(secondvertex);
righttri.SetOrg(secondvertex);
@@ -615,7 +604,7 @@ namespace TriangleNet.Algorithm
HeapDelete(eventheap, heapsize, 0);
heapsize--;
check4events = true;
if (nextevent.xkey < mesh.xmin)
if (nextevent.xkey < mesh.bounds.Xmin)
{
fliptri = nextevent.otriEvent;
fliptri.Oprev(ref farlefttri);
@@ -645,8 +634,8 @@ namespace TriangleNet.Algorithm
else
{
nextvertex = nextevent.vertexEvent;
if ((nextvertex.pt.X == lastvertex.pt.X) &&
(nextvertex.pt.Y == lastvertex.pt.Y))
if ((nextvertex.x == lastvertex.x) &&
(nextvertex.y == lastvertex.y))
{
if (Behavior.Verbose)
{
@@ -714,7 +703,7 @@ namespace TriangleNet.Algorithm
leftvertex = farlefttri.Apex();
midvertex = lefttri.Dest();
rightvertex = lefttri.Apex();
lefttest = Primitives.CounterClockwise(leftvertex.pt, midvertex.pt, rightvertex.pt);
lefttest = Primitives.CounterClockwise(leftvertex, midvertex, rightvertex);
if (lefttest > 0.0)
{
newevent = new SweepEvent();
@@ -729,7 +718,7 @@ namespace TriangleNet.Algorithm
leftvertex = righttri.Apex();
midvertex = righttri.Org();
rightvertex = farrighttri.Apex();
righttest = Primitives.CounterClockwise(leftvertex.pt, midvertex.pt, rightvertex.pt);
righttest = Primitives.CounterClockwise(leftvertex, midvertex, rightvertex);
if (righttest > 0.0)
{
newevent = new SweepEvent();
@@ -748,5 +737,66 @@ namespace TriangleNet.Algorithm
bottommost.LprevSelf();
return RemoveGhosts(ref bottommost);
}
#region Internal classes
/// <summary>
/// A node in a heap used to store events for the sweepline Delaunay algorithm.
/// </summary>
/// <remarks>
/// Only used in the sweepline algorithm.
///
/// Nodes do not point directly to their parents or children in the heap. Instead, each
/// node knows its position in the heap, and can look up its parent and children in a
/// separate array. To distinguish site events from circle events, all circle events are
/// given an invalid (smaller than 'xmin') x-coordinate 'xkey'.
/// </remarks>
class SweepEvent
{
public double xkey, ykey; // Coordinates of the event.
public Vertex vertexEvent; // Vertex event.
public Otri otriEvent; // Circle event.
public int heapposition; // Marks this event's position in the heap.
}
/// <summary>
/// Introducing a new class which aggregates a sweep event is the easiest way
/// to handle the pointer magic of the original code (casting a sweep event
/// to vertex etc.).
/// </summary>
class SweepEventVertex : Vertex
{
public SweepEvent evt;
public SweepEventVertex(SweepEvent e)
{
evt = e;
}
}
/// <summary>
/// A node in the splay tree.
/// </summary>
/// <remarks>
/// Only used in the sweepline algorithm.
///
/// Each node holds an oriented ghost triangle that represents a boundary edge
/// of the growing triangulation. When a circle event covers two boundary edges
/// with a triangle, so that they are no longer boundary edges, those edges are
/// not immediately deleted from the tree; rather, they are lazily deleted when
/// they are next encountered. (Since only a random sample of boundary edges are
/// kept in the tree, lazy deletion is faster.) 'keydest' is used to verify that
/// a triangle is still the same as when it entered the splay tree; if it has
/// been rotated (due to a circle event), it no longer represents a boundary
/// edge and should be deleted.
/// </remarks>
class SplayNode
{
public Otri keyedge; // Lprev of an edge on the front.
public Vertex keydest; // Used to verify that splay node is still live.
public SplayNode lchild, rchild; // Children in splay tree.
}
#endregion
}
}
+14 -25
View File
@@ -11,8 +11,13 @@ namespace TriangleNet
using TriangleNet.Data;
/// <summary>
/// TODO: Update summary.
/// A (priority) queue for bad triangles.
/// </summary>
/// <remarks>
// The queue is actually a set of 4096 queues. I use multiple queues to
// give priority to smaller angles. I originally implemented a heap, but
// the queues are faster by a larger margin than I'd suspected.
/// </remarks>
class BadTriQueue
{
static readonly double SQRT2 = 1.4142135623730950488016887242096980785696718753769480732;
@@ -20,11 +25,10 @@ namespace TriangleNet
public int Count { get { return this.count; } }
// Variables that maintain the bad triangle queues. The queues are
// ordered from 4095 (highest priority) to 0 (lowest priority).
BadTriangle[] queuefront;//[4096];
BadTriangle[] queuetail;//[4096];
int[] nextnonemptyq;//[4096];
// ordered from 4095 (highest priority) to 0 (lowest priority).
BadTriangle[] queuefront;
BadTriangle[] queuetail;
int[] nextnonemptyq;
int firstnonemptyq;
int count;
@@ -42,17 +46,10 @@ namespace TriangleNet
count = 0;
}
#region Queue
/// <summary>
/// Add a bad triangle data structure to the end of a queue.
/// </summary>
/// <param name="badtri"></param>
/// <remarks>
// The queue is actually a set of 4096 queues. I use multiple queues to
// give priority to smaller angles. I originally implemented a heap, but
// the queues are faster by a larger margin than I'd suspected.
/// </remarks>
/// <param name="badtri">The bad triangle to enqueue.</param>
public void Enqueue(BadTriangle badtri)
{
double length, multiplier;
@@ -154,9 +151,6 @@ namespace TriangleNet
/// <param name="enqapex"></param>
/// <param name="enqorg"></param>
/// <param name="enqdest"></param>
/// <remarks>
/// Allocates a badtriang data structure for the triangle, then passes it to enqueuebadtriang().
/// </remarks>
public void Enqueue(ref Otri enqtri, double minedge, Vertex enqapex, Vertex enqorg, Vertex enqdest)
{
// Allocate space for the bad triangle.
@@ -172,8 +166,6 @@ namespace TriangleNet
Vertex dest = enqtri.Dest();
Vertex apex = enqtri.Apex();
//badtriangles.Add(newbad);
Enqueue(newbad);
}
@@ -183,8 +175,6 @@ namespace TriangleNet
/// <returns></returns>
public BadTriangle Dequeue()
{
BadTriangle result;
// If no queues are nonempty, return NULL.
if (firstnonemptyq < 0)
{
@@ -194,18 +184,17 @@ namespace TriangleNet
this.count--;
// Find the first triangle of the highest-priority queue.
result = queuefront[firstnonemptyq];
BadTriangle result = queuefront[firstnonemptyq];
// Remove the triangle from the queue.
queuefront[firstnonemptyq] = result.nexttriang;
// If this queue is now empty, note the new highest-priority
// nonempty queue.
// nonempty queue.
if (result == queuetail[firstnonemptyq])
{
firstnonemptyq = nextnonemptyq[firstnonemptyq];
}
return result;
}
#endregion
}
}
+74 -2
View File
@@ -15,32 +15,104 @@ namespace TriangleNet
/// </summary>
static class Behavior
{
/// <summary>
/// Input is a Planar Straight Line Graph.
/// </summary>
public static bool Poly { get; set; }
/// <summary>
/// Quality mesh generation.
/// </summary>
public static bool Quality { get; set; }
/// <summary>
/// Apply a maximum triangle area constraint.
/// </summary>
public static bool VarArea { get; set; }
/// <summary>
/// Apply a maximum triangle area constraint.
/// </summary>
public static bool FixedArea { get; set; }
/// <summary>
/// Apply a user-defined triangle constraint.
/// </summary>
public static bool Usertest { get; set; }
/// <summary>
/// Apply attributes to identify triangles in certain regions.
/// </summary>
public static bool RegionAttrib { get; set; }
/// <summary>
/// Enclose the convex hull with segments.
/// </summary>
public static bool Convex { get; set; }
/// <summary>
/// Jettison unused vertices from output.
/// </summary>
public static bool Jettison { get; set; }
/// <summary>
/// Compute boundary information.
/// </summary>
public static bool UseBoundaryMarkers { get; set; }
/// <summary>
/// Ignores holes in polygons.
/// </summary>
public static bool NoHoles { get; set; }
/// <summary>
/// No exact arithmetic.
/// </summary>
public static bool NoExact { get; set; }
/// <summary>
/// Conforming Delaunay (all triangles are truly Delaunay).
/// </summary>
public static bool ConformDel { get; set; }
/// <summary>
/// Algorithm to use for triangulation.
/// </summary>
public static TriangulationAlgorithm Algorithm { get; set; }
/// <summary>
/// Log detailed information.
/// </summary>
public static bool Verbose { get; set; }
public static bool UseSegments { get; set; }
/// <summary>
/// Use segments (should not be set manually)
/// </summary>
public static bool UseSegments { get; set; } // TODO: internal set
public static int NoBisect { get; set; } // <- int
/// <summary>
/// Suppresses boundary segment splitting.
/// </summary>
public static int NoBisect { get; set; } // <- int !
/// <summary>
/// Use maximum number of added Steiner points.
/// </summary>
public static int Steiner { get; set; }
/// <summary>
/// Minimum angle constraint.
/// </summary>
public static double MinAngle { get; set; }
/// <summary>
/// (should not be set manually)
/// </summary>
public static double GoodAngle { get; set; }
/// <summary>
/// (should not be set manually)
/// </summary>
public static double Offconstant { get; set; }
/// <summary>
/// Maximum area constraint.
/// </summary>
public static double MaxArea { get; set; }
/// <summary>
/// Maximum angle constraint.
/// </summary>
public static double MaxAngle { get; set; }
/// <summary>
/// (should not be set manually)
/// </summary>
public static double MaxGoodAngle { get; set; }
/// <summary>
/// Load behavior defaults.
/// </summary>
public static void Init()
{
Poly = false;
+52 -59
View File
@@ -9,6 +9,7 @@ namespace TriangleNet
{
using TriangleNet.Data;
using System;
using TriangleNet.Geometry;
using System.Collections.Generic;
/// <summary>
@@ -18,6 +19,7 @@ namespace TriangleNet
{
Mesh mesh;
public Carver(Mesh mesh)
{
this.mesh = mesh;
@@ -28,7 +30,7 @@ namespace TriangleNet
/// protected by subsegments. Where there are subsegments, set boundary
/// markers as appropriate.
/// </summary>
void InfectHull()
private void InfectHull()
{
Otri hulltri = default(Otri);
Otri nexttri = default(Otri);
@@ -50,7 +52,7 @@ namespace TriangleNet
{
// Is the triangle protected by a subsegment?
hulltri.SegPivot(ref hullsubseg);
if (hullsubseg.ss == Mesh.dummysub)
if (hullsubseg.seg == Mesh.dummysub)
{
// The triangle is not protected; infect it.
if (!hulltri.IsInfected())
@@ -62,9 +64,9 @@ namespace TriangleNet
else
{
// The triangle is protected; set boundary markers if appropriate.
if (hullsubseg.ss.boundary == 0)
if (hullsubseg.seg.boundary == 0)
{
hullsubseg.ss.boundary = 1;
hullsubseg.seg.boundary = 1;
horg = hulltri.Org();
hdest = hulltri.Dest();
if (horg.mark == 0)
@@ -86,6 +88,7 @@ namespace TriangleNet
nexttri.Copy(ref hulltri);
hulltri.Oprev(ref nexttri);
}
} while (!hulltri.Equal(starttri));
}
@@ -108,19 +111,19 @@ namespace TriangleNet
{
Otri testtri = default(Otri);
Otri neighbor = default(Otri);
Triangle virusloop;
Osub neighborsubseg = default(Osub);
Vertex testvertex;
Vertex norg, ndest;
//Vertex deadorg, deaddest, deadapex;
bool killorg;
// Loop through all the infected triangles, spreading the virus to
// their neighbors, then to their neighbors' neighbors.
for (int i = 0; i < mesh.viri.Count; i++)
{
virusloop = mesh.viri[i];
testtri.triangle = virusloop;
{
// WARNING: Don't use foreach, mesh.viri list may get modified.
testtri.triangle = mesh.viri[i];
// A triangle is marked as infected by messing with one of its pointers
// to subsegments, setting it to an illegal value. Hence, we have to
// temporarily uninfect this triangle so that we can examine its
@@ -138,16 +141,16 @@ namespace TriangleNet
// Check if the neighbor is nonexistent or already infected.
if ((neighbor.triangle == Mesh.dummytri) || neighbor.IsInfected())
{
if (neighborsubseg.ss != Mesh.dummysub)
if (neighborsubseg.seg != Mesh.dummysub)
{
// There is a subsegment separating the triangle from its
// neighbor, but both triangles are dying, so the subsegment
// dies too.
mesh.SubsegDealloc(neighborsubseg.ss);
// neighbor, but both triangles are dying, so the subsegment
// dies too.
mesh.SubsegDealloc(neighborsubseg.seg);
if (neighbor.triangle != Mesh.dummytri)
{
// Make sure the subsegment doesn't get deallocated again
// later when the infected neighbor is visited.
// later when the infected neighbor is visited.
neighbor.Uninfect();
neighbor.SegDissolve();
neighbor.Infect();
@@ -156,7 +159,7 @@ namespace TriangleNet
}
else
{ // The neighbor exists and is not infected.
if (neighborsubseg.ss == Mesh.dummysub)
if (neighborsubseg.seg == Mesh.dummysub)
{
// There is no subsegment protecting the neighbor, so
// the neighbor becomes infected.
@@ -165,13 +168,14 @@ namespace TriangleNet
mesh.viri.Add(neighbor.triangle);
}
else
{ // The neighbor is protected by a subsegment.
{
// The neighbor is protected by a subsegment.
// Remove this triangle from the subsegment.
neighborsubseg.TriDissolve();
// The subsegment becomes a boundary. Set markers accordingly.
if (neighborsubseg.ss.boundary == 0)
if (neighborsubseg.seg.boundary == 0)
{
neighborsubseg.ss.boundary = 1;
neighborsubseg.seg.boundary = 1;
}
norg = neighbor.Org();
ndest = neighbor.Dest();
@@ -191,17 +195,16 @@ namespace TriangleNet
testtri.Infect();
}
for (int i = 0; i < mesh.viri.Count; i++)
foreach (var virus in mesh.viri)
{
virusloop = mesh.viri[i];
testtri.triangle = virusloop;
testtri.triangle = virus;
// Check each of the three corners of the triangle for elimination.
// This is done by walking around each vertex, checking if it is
// still connected to at least one live triangle.
for (testtri.orient = 0; testtri.orient < 3; testtri.orient++)
{
testvertex=testtri.Org();
testvertex = testtri.Org();
// Check if the vertex has already been tested.
if (testvertex != null)
{
@@ -266,7 +269,7 @@ namespace TriangleNet
if (neighbor.triangle == Mesh.dummytri)
{
// There is no neighboring triangle on this edge, so this edge
// is a boundary edge. This triangle is being deleted, so this
// is a boundary edge. This triangle is being deleted, so this
// boundary edge is deleted.
mesh.hullsize--;
}
@@ -282,6 +285,7 @@ namespace TriangleNet
// Return the dead triangle to the pool of triangles.
mesh.TriangleDealloc(testtri.triangle);
}
// Empty the virus pool.
mesh.viri.Clear();
}
@@ -312,6 +316,8 @@ namespace TriangleNet
// neighbors.
for (int i = 0; i < mesh.viri.Count; i++)
{
// WARNING: Don't use foreach, mesh.viri list may get modified.
testtri.triangle = mesh.viri[i];
// A triangle is marked as infected by messing with one of its pointers
// to subsegments, setting it to an illegal value. Hence, we have to
@@ -340,7 +346,7 @@ namespace TriangleNet
// Make sure the neighbor exists, is not already infected, and
// isn't protected by a subsegment.
if ((neighbor.triangle != Mesh.dummytri) && !neighbor.IsInfected()
&& (neighborsubseg.ss == Mesh.dummysub))
&& (neighborsubseg.seg == Mesh.dummysub))
{
// Infect the neighbor.
neighbor.Infect();
@@ -364,14 +370,10 @@ namespace TriangleNet
mesh.viri.Clear();
}
/// <summary>
/// Find the holes and infect them. Find the area constraints and infect
/// Find the holes and infect them. Find the area constraints and infect
/// them. Infect the convex hull. Spread the infection and kill triangles.
/// Spread the area constraints.
/// </summary>
/// <param name="holelist"></param>
/// <param name="holes"></param>
/// <param name="regionlist"></param>
/// <param name="regions"></param>
public void CarveHoles()
{
Otri searchtri = default(Otri);
@@ -379,8 +381,7 @@ namespace TriangleNet
Vertex searchorg, searchdest;
LocateResult intersect;
int numRegions = mesh.regions.Count;
Otri[] regiontris = (numRegions > 0) ? new Otri[numRegions] : null;
Otri[] regionTris = null;
if (!Behavior.Convex)
{
@@ -393,10 +394,9 @@ namespace TriangleNet
{
// Infect each triangle in which a hole lies.
foreach (var hole in mesh.holes)
{
{
// Ignore holes that aren't within the bounds of the mesh.
if ((hole.X >= mesh.xmin) && (hole.X <= mesh.xmax)
&& (hole.Y >= mesh.ymin) && (hole.Y <= mesh.ymax))
if (mesh.bounds.Contains(hole))
{
// Start searching from some triangle on the outer boundary.
searchtri.triangle = Mesh.dummytri;
@@ -407,7 +407,7 @@ namespace TriangleNet
// falls within the starting triangle.
searchorg = searchtri.Org();
searchdest = searchtri.Dest();
if (Primitives.CounterClockwise(searchorg.pt, searchdest.pt, hole) > 0.0)
if (Primitives.CounterClockwise(searchorg, searchdest, hole) > 0.0)
{
// Find a triangle that contains the hole.
intersect = mesh.Locate(hole, ref searchtri);
@@ -427,17 +427,18 @@ namespace TriangleNet
// work when the triangulation is no longer convex. (Incidentally, this is the reason why
// regional attributes and area constraints can't be used when refining a preexisting mesh,
// which might not be convex; they can only be used with a freshly triangulated PSLG.)
if (numRegions > 0)
if (mesh.regions.Count > 0)
{
regionTris = new Otri[mesh.regions.Count];
int i = 0;
// Find the starting triangle for each region.
foreach (var region in mesh.regions)
{
regiontris[i].triangle = Mesh.dummytri;
regionTris[i].triangle = Mesh.dummytri;
// Ignore region points that aren't within the bounds of the mesh.
if ((region.pt.X >= mesh.xmin) && (region.pt.X <= mesh.xmax) &&
(region.pt.Y >= mesh.ymin) && (region.pt.Y <= mesh.ymax))
if (mesh.bounds.Contains(region.point))
{
// Start searching from some triangle on the outer boundary.
searchtri.triangle = Mesh.dummytri;
@@ -448,15 +449,15 @@ namespace TriangleNet
// region point falls within the starting triangle.
searchorg = searchtri.Org();
searchdest = searchtri.Dest();
if (Primitives.CounterClockwise(searchorg.pt, searchdest.pt, region.pt) > 0.0)
if (Primitives.CounterClockwise(searchorg, searchdest, region.point) > 0.0)
{
// Find a triangle that contains the region point.
intersect = mesh.Locate(region.pt, ref searchtri);
intersect = mesh.Locate(region.point, ref searchtri);
if ((intersect != LocateResult.Outside) && (!searchtri.IsInfected()))
{
// Record the triangle for processing after the
// holes have been carved.
searchtri.Copy(ref regiontris[i]);
searchtri.Copy(ref regionTris[i]);
}
}
}
@@ -472,7 +473,7 @@ namespace TriangleNet
}
// The virus pool should be empty now.
if (numRegions > 0)
if (regionTris != null)
{
if (Behavior.RegionAttrib)
{
@@ -489,19 +490,19 @@ namespace TriangleNet
}
}
for (int i = 0; i < numRegions; i++)
for (int i = 0; i < regionTris.Length; i++)
{
if (regiontris[i].triangle != Mesh.dummytri)
if (regionTris[i].triangle != Mesh.dummytri)
{
// Make sure the triangle under consideration still exists.
// It may have been eaten by the virus.
if (!Otri.IsDead(regiontris[i].triangle))
if (!Otri.IsDead(regionTris[i].triangle))
{
// Put one triangle in the virus pool.
regiontris[i].Infect();
mesh.viri.Add(regiontris[i].triangle);
regionTris[i].Infect();
mesh.viri.Add(regionTris[i].triangle);
// Apply one region's attribute and/or area constraint.
RegionPlague(mesh.regions[i].attribute, mesh.regions[i].area);
RegionPlague(mesh.regions[i].Attribute, mesh.regions[i].Area);
// The virus pool should be empty now.
}
}
@@ -514,16 +515,8 @@ namespace TriangleNet
}
}
// Free up memory.
if (((mesh.holes.Count > 0) && !Behavior.NoHoles) || !Behavior.Convex || (numRegions > 0))
{
mesh.viri.Clear();
}
if (numRegions > 0)
{
regiontris = null;
}
// Free up memory (virus pool should be empty anyway).
mesh.viri.Clear();
}
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ namespace TriangleNet.Data
public override string ToString()
{
return String.Format("B-SID {0}", encsubseg.ss.Hash);
return String.Format("B-SID {0}", encsubseg.seg.hash);
}
};
}
+1 -1
View File
@@ -36,7 +36,7 @@ namespace TriangleNet.Data
}
public override string ToString()
{
return String.Format("B-TID {0}", poortri.triangle.Hash);
return String.Format("B-TID {0}", poortri.triangle.hash);
}
}
}
-27
View File
@@ -1,27 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="FlipStacker.cs" company="">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Data
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// A stack of triangles flipped during the most recent vertex insertion.
/// </summary>
/// <remarks>
/// The stack is used to undo the vertex insertion if the vertex encroaches
/// upon a subsegment.
/// </remarks>
class FlipStacker
{
public Otri flippedtri; // A recently flipped triangle.
public FlipStacker prevflip; // Previous flip in the stack.
}
}
+34 -34
View File
@@ -23,16 +23,16 @@ namespace TriangleNet.Data
/// </remarks>
struct Osub
{
public Subseg ss;
public int ssorient; // Ranges from 0 to 1.
public Segment seg;
public int orient; // Ranges from 0 to 1.
public override string ToString()
{
if (ss == null)
if (seg == null)
{
return "O-TID [null]";
}
return String.Format("O-SID {0}", ss.Hash);
return String.Format("O-SID {0}", seg.hash);
}
#region Osub primitives
@@ -44,8 +44,8 @@ namespace TriangleNet.Data
/// </remarks>
public void Sym(ref Osub o2)
{
o2.ss = ss;
o2.ssorient = 1 - ssorient;
o2.seg = seg;
o2.orient = 1 - orient;
}
/// <summary>
@@ -53,7 +53,7 @@ namespace TriangleNet.Data
/// </summary>
public void SymSelf()
{
ssorient = 1 - ssorient;
orient = 1 - orient;
}
/// <summary>
@@ -64,7 +64,7 @@ namespace TriangleNet.Data
/// </remarks>
public void Pivot(ref Osub o2)
{
o2 = ss.subsegs[ssorient];
o2 = seg.subsegs[orient];
//sdecode(sptr, o2);
}
@@ -73,7 +73,7 @@ namespace TriangleNet.Data
/// </summary>
public void PivotSelf()
{
this = ss.subsegs[ssorient];
this = seg.subsegs[orient];
//sdecode(sptr, osub);
}
@@ -85,7 +85,7 @@ namespace TriangleNet.Data
/// </remarks>
public void Next(ref Osub o2)
{
o2 = ss.subsegs[1 - ssorient];
o2 = seg.subsegs[1 - orient];
//sdecode(sptr, o2);
}
@@ -94,7 +94,7 @@ namespace TriangleNet.Data
/// </summary>
public void NextSelf()
{
this = ss.subsegs[1 - ssorient];
this = seg.subsegs[1 - orient];
//sdecode(sptr, osub);
}
@@ -103,7 +103,7 @@ namespace TriangleNet.Data
/// </summary>
public Vertex Org()
{
return ss.vertices[ssorient];
return seg.vertices[orient];
}
/// <summary>
@@ -111,7 +111,7 @@ namespace TriangleNet.Data
/// </summary>
public Vertex Dest()
{
return ss.vertices[1 - ssorient];
return seg.vertices[1 - orient];
}
/// <summary>
@@ -119,7 +119,7 @@ namespace TriangleNet.Data
/// </summary>
public void SetOrg(Vertex ptr)
{
ss.vertices[ssorient] = ptr;
seg.vertices[orient] = ptr;
}
/// <summary>
@@ -127,7 +127,7 @@ namespace TriangleNet.Data
/// </summary>
public void SetDest(Vertex ptr)
{
ss.vertices[1 - ssorient] = ptr;
seg.vertices[1 - orient] = ptr;
}
/// <summary>
@@ -135,7 +135,7 @@ namespace TriangleNet.Data
/// </summary>
public Vertex SegOrg()
{
return ss.vertices[2 + ssorient];
return seg.vertices[2 + orient];
}
/// <summary>
@@ -143,7 +143,7 @@ namespace TriangleNet.Data
/// </summary>
public Vertex SegDest()
{
return ss.vertices[3 - ssorient];
return seg.vertices[3 - orient];
}
/// <summary>
@@ -151,7 +151,7 @@ namespace TriangleNet.Data
/// </summary>
public void SetSegOrg(Vertex ptr)
{
ss.vertices[2 + ssorient] = ptr;
seg.vertices[2 + orient] = ptr;
}
/// <summary>
@@ -159,7 +159,7 @@ namespace TriangleNet.Data
/// </summary>
public void SetSegDest(Vertex ptr)
{
ss.vertices[3 - ssorient] = ptr;
seg.vertices[3 - orient] = ptr;
}
/// <summary>
@@ -169,7 +169,7 @@ namespace TriangleNet.Data
/// setting boundary conditions in finite element solvers.</remarks>
public int Mark()
{
return ss.boundary;
return seg.boundary;
}
/// <summary>
@@ -177,7 +177,7 @@ namespace TriangleNet.Data
/// </summary>
public void SetMark(int value)
{
ss.boundary = value;
seg.boundary = value;
}
/// <summary>
@@ -185,8 +185,8 @@ namespace TriangleNet.Data
/// </summary>
public void Bond(ref Osub o2)
{
ss.subsegs[ssorient] = o2;
o2.ss.subsegs[o2.ssorient] = this;
seg.subsegs[orient] = o2;
o2.seg.subsegs[o2.orient] = this;
}
/// <summary>
@@ -196,7 +196,7 @@ namespace TriangleNet.Data
/// connected to this subsegment.</remarks>
public void Dissolve()
{
ss.subsegs[ssorient].ss = Mesh.dummysub;
seg.subsegs[orient].seg = Mesh.dummysub;
}
/// <summary>
@@ -204,8 +204,8 @@ namespace TriangleNet.Data
/// </summary>
public void Copy(ref Osub o2)
{
o2.ss = ss;
o2.ssorient = ssorient;
o2.seg = seg;
o2.orient = orient;
}
/// <summary>
@@ -213,24 +213,24 @@ namespace TriangleNet.Data
/// </summary>
public bool Equal(Osub o2)
{
return ((ss == o2.ss) && (ssorient == o2.ssorient));
return ((seg == o2.seg) && (orient == o2.orient));
}
/// <summary>
/// Check a subsegment's deallocation.
/// </summary>
public static bool IsDead(Subseg sub)
public static bool IsDead(Segment sub)
{
return sub.subsegs[0].ss == null;
return sub.subsegs[0].seg == null;
}
/// <summary>
/// Set a subsegment's deallocation.
/// </summary>
public static void Kill(Subseg sub)
public static void Kill(Segment sub)
{
sub.subsegs[0].ss = null;
sub.subsegs[1].ss = null;
sub.subsegs[0].seg = null;
sub.subsegs[1].seg = null;
}
/// <summary>
@@ -238,7 +238,7 @@ namespace TriangleNet.Data
/// </summary>
public void TriPivot(ref Otri ot)
{
ot = ss.triangles[ssorient];
ot = seg.triangles[orient];
//decode(ptr, otri)
}
@@ -247,7 +247,7 @@ namespace TriangleNet.Data
/// </summary>
public void TriDissolve()
{
ss.triangles[ssorient].triangle = Mesh.dummytri;
seg.triangles[orient].triangle = Mesh.dummytri;
}
#endregion
+11 -5
View File
@@ -32,7 +32,7 @@ namespace TriangleNet.Data
{
return "O-TID [null]";
}
return String.Format("O-TID {0}", triangle.Hash);
return String.Format("O-TID {0}", triangle.hash);
}
#region Otri primitives
@@ -372,8 +372,14 @@ namespace TriangleNet.Data
/// </summary>
public void Bond(ref Otri o2)
{
triangle.neighbors[orient] = o2;
o2.triangle.neighbors[o2.orient] = this;
//triangle.neighbors[orient]= o2;
//o2.triangle.neighbors[o2.orient] = this;
triangle.neighbors[orient].triangle = o2.triangle;
triangle.neighbors[orient].orient = o2.orient;
o2.triangle.neighbors[o2.orient].triangle = this.triangle;
o2.triangle.neighbors[o2.orient].orient = this.orient;
}
/// <summary>
@@ -462,7 +468,7 @@ namespace TriangleNet.Data
public void SegBond(ref Osub os)
{
triangle.subsegs[orient] = os;
os.ss.triangles[os.ssorient] = this;
os.seg.triangles[os.orient] = this;
}
/// <summary>
@@ -470,7 +476,7 @@ namespace TriangleNet.Data
/// </summary>
public void SegDissolve()
{
triangle.subsegs[orient].ss = Mesh.dummysub;
triangle.subsegs[orient].seg = Mesh.dummysub;
}
#endregion
-35
View File
@@ -1,35 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="Point2.cs" company="">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Data
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// Represents a 2D point.
/// </summary>
public struct Point2
{
public double X;
public double Y;
public Point2(double x, double y)
{
this.X = x;
this.Y = y;
}
public bool Equals(Point2 p)
{
// Return true if the fields match:
return (X == p.X) && (Y == p.Y);
}
}
}
-31
View File
@@ -1,31 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="Region.cs" company="">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Data
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// TODO: Update summary.
/// </summary>
struct Region
{
internal Point2 pt;
internal double attribute;
internal double area;
public Region(double[] region)
{
pt = new Point2(region[0], region[1]);
attribute = region[2];
area = region[3];
}
}
}
@@ -18,32 +18,25 @@ namespace TriangleNet.Data
/// <remarks>
/// Each subsegment contains two pointers to adjoining subsegments, plus
/// four pointers to vertices, plus two pointers to adjoining triangles,
/// plus one boundary marker, plus one segment number.
/// plus one boundary marker.
/// </remarks>
class Subseg
public class Segment
{
// Start at -1, so dummysub has that ID
private static int hashSeed = -1;
internal int Hash;
// Hash for dictionary. Will be set by mesh instance.
internal int hash;
// The ID is only used for mesh output.
//public int ID;
internal Osub[] subsegs;
internal Vertex[] vertices;
internal Otri[] triangles;
internal int boundary;
public Osub[] subsegs;
public Vertex[] vertices;
public Otri[] triangles;
public int boundary;
//public int segment;
public Subseg()
public Segment()
{
Hash = hashSeed++;
// Initialize the two adjoining subsegments to be the omnipresent
// subsegment.
// subsegment.
subsegs = new Osub[2];
subsegs[0].ss = Mesh.dummysub;
subsegs[1].ss = Mesh.dummysub;
subsegs[0].seg = Mesh.dummysub;
subsegs[1].seg = Mesh.dummysub;
// Four NULL vertices.
vertices = new Vertex[4];
@@ -57,29 +50,42 @@ namespace TriangleNet.Data
boundary = 0;
}
#region Public properties
/// <summary>
/// Reset the hash seed.
/// Gets the first endpoints vertex id.
/// </summary>
/// <param name="value">The new has seed value.</param>
/// <remarks>Reset value will usally 0, if a new triangulation starts,
/// or the number of subsegments, if refinement is done.</remarks>
internal static void ResetHashSeed(int value)
public int P0
{
if (value < 0)
{
throw new ArgumentException("A hash seed must be non negative.");
}
hashSeed = value;
get { return this.vertices[0].id; }
}
/// <summary>
/// Gets the seconds endpoints vertex id.
/// </summary>
public int P1
{
get { return this.vertices[1].id; }
}
/// <summary>
/// Gets the segment boundary mark.
/// </summary>
public int Boundary
{
get { return this.boundary; }
}
#endregion
public override int GetHashCode()
{
return this.Hash;
return this.hash;
}
public override string ToString()
{
return String.Format("SID {0}", Hash);
return String.Format("SID {0}", hash);
}
}
}
-37
View File
@@ -1,37 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="SplayNode.cs" company="">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Data
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// A node in the splay tree.
/// </summary>
/// <remarks>
/// Only used in the sweepline algorithm.
///
/// Each node holds an oriented ghost triangle that represents a boundary edge
/// of the growing triangulation. When a circle event covers two boundary edges
/// with a triangle, so that they are no longer boundary edges, those edges are
/// not immediately deleted from the tree; rather, they are lazily deleted when
/// they are next encountered. (Since only a random sample of boundary edges are
/// kept in the tree, lazy deletion is faster.) 'keydest' is used to verify that
/// a triangle is still the same as when it entered the splay tree; if it has
/// been rotated (due to a circle event), it no longer represents a boundary
/// edge and should be deleted.
/// </remarks>
class SplayNode
{
public Otri keyedge; // Lprev of an edge on the front.
public Vertex keydest; // Used to verify that splay node is still live.
public SplayNode lchild, rchild; // Children in splay tree.
}
}
-33
View File
@@ -1,33 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="SweepEvent.cs" company="">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Data
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/// <summary>
/// A node in a heap used to store events for the sweepline Delaunay algorithm.
/// </summary>
/// <remarks>
/// Only used in the sweepline algorithm.
///
/// Nodes do not point directly to their parents or children in the heap. Instead, each
/// node knows its position in the heap, and can look up its parent and children in a
/// separate array. To distinguish site events from circle events, all circle events are
/// given an invalid (smaller than 'xmin') x-coordinate 'xkey'.
/// </remarks>
class SweepEvent
{
public double xkey, ykey; // Coordinates of the event.
public Vertex vertexEvent; // Vertex event.
public Otri otriEvent; // Circle event.
public int heapposition; // Marks this event's position in the heap.
}
}
+100 -32
View File
@@ -11,28 +11,24 @@ namespace TriangleNet.Data
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
/// The triangle data structure.
/// </summary>
/// <remarks>
/// Each triangle contains three pointers to
/// adjoining triangles, plus three pointers to vertices, plus three
/// pointers to subsegments (declared below; these pointers are usually
/// 'dummysub'). It may or may not also contain user-defined attributes
/// and/or a floating-point "area constraint." It may also contain extra
/// pointers for nodes, when the user asks for high-order elements.
/// Because the size and structure of a 'triangle' is not decided until
/// runtime, I haven't simply declared the type 'triangle' as a struct.
/// Each triangle contains three pointers to adjoining triangles, plus three
/// pointers to vertices, plus three pointers to subsegments (declared below;
/// these pointers are usually 'dummysub'). It may or may not also contain
/// user-defined attributes and/or a floating-point "area constraint".
/// </remarks>
class Triangle
public class Triangle : ITriangle
{
// Start at -1, so dummytri has that ID
private static int hashSeed = -1;
internal int Hash;
// Hash for dictionary. Will be set by mesh instance.
internal int hash;
// The ID is only used for mesh output.
internal int ID;
internal int id;
internal Otri[] neighbors;
internal Vertex[] vertices;
@@ -43,9 +39,6 @@ namespace TriangleNet.Data
public Triangle(int numAttributes)
{
this.Hash = hashSeed++;
this.ID = this.Hash;
// Initialize the three adjoining triangles to be "outer space".
neighbors = new Otri[3];
neighbors[0].triangle = Mesh.dummytri;
@@ -54,15 +47,15 @@ namespace TriangleNet.Data
// Three NULL vertices.
vertices = new Vertex[3];
if (Behavior.UseSegments)
{
// Initialize the three adjoining subsegments to be the
// omnipresent subsegment.
subsegs = new Osub[3];
subsegs[0].ss = Mesh.dummysub;
subsegs[1].ss = Mesh.dummysub;
subsegs[2].ss = Mesh.dummysub;
subsegs[0].seg = Mesh.dummysub;
subsegs[1].seg = Mesh.dummysub;
subsegs[2].seg = Mesh.dummysub;
}
if (numAttributes > 0)
@@ -76,29 +69,104 @@ namespace TriangleNet.Data
}
}
#region Public properties
/// <summary>
/// Reset the hash seed.
/// Gets the triangle id.
/// </summary>
/// <param name="value">The new has seed value.</param>
/// <remarks>Reset value will usally 0, if a new triangulation starts,
/// or the number of triangles, if refinement is done.</remarks>
internal static void ResetHashSeed(int value)
public int ID
{
if (value < 0)
{
throw new ArgumentException("A hash seed must be non negative.");
}
hashSeed = value;
get { return this.id; }
}
/// <summary>
/// Gets the first corners vertex id.
/// </summary>
public int P0
{
get { return this.vertices[0] == null ? -1 : this.vertices[0].id; }
}
/// <summary>
/// Gets the seconds corners vertex id.
/// </summary>
public int P1
{
get { return this.vertices[1] == null ? -1 : this.vertices[1].id; }
}
/// <summary>
/// Gets the specified corners vertex id.
/// </summary>
public int this[int index]
{
get { return this.vertices[index] == null ? -1 : this.vertices[index].id; }
}
/// <summary>
/// Gets the third corners vertex id.
/// </summary>
public int P2
{
get { return this.vertices[2] == null ? -1 : this.vertices[2].id; }
}
public bool SupportsNeighbors
{
get { return true; }
}
/// <summary>
/// Gets the first neighbors id.
/// </summary>
public int N0
{
get { return this.neighbors[0].triangle.id; }
}
/// <summary>
/// Gets the second neighbors id.
/// </summary>
public int N1
{
get { return this.neighbors[1].triangle.id; }
}
/// <summary>
/// Gets the third neighbors id.
/// </summary>
public int N2
{
get { return this.neighbors[2].triangle.id; }
}
/// <summary>
/// Gets the triangle area constraint.
/// </summary>
public double Area
{
get { return this.area; }
}
/// <summary>
/// Gets the triangle attributes.
/// </summary>
public double[] Attributes
{
get { return this.attributes; }
}
#endregion
public override int GetHashCode()
{
return this.Hash;
return this.hash;
}
public override string ToString()
{
return String.Format("TID {0}", Hash);
return String.Format("TID {0}", hash);
}
}
}
+35 -113
View File
@@ -11,43 +11,52 @@ namespace TriangleNet.Data
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
/// The vertex data structure.
/// </summary>
/// <remarks>
/// Each vertex is actually an array of doubles. An integer boundary marker,
/// and sometimes a to a triangle, is appended after the doubles.
/// </remarks>
class Vertex : IComparable<Vertex>, IEquatable<Vertex>
public class Vertex : Point
{
private static int hashSeed = 0;
internal int Hash;
// Hash for dictionary. Will be set by mesh instance.
internal int hash;
// The ID is only used for mesh output.
internal int ID;
internal int id;
internal Point2 pt;
internal int mark;
internal VertexType type;
internal Otri tri;
internal double[] attributes;
public Vertex()
: this(0)
: this(0, 0, 0)
{ }
public Vertex(int numAttributes)
{
this.Hash = hashSeed++;
pt = default(Point2);
public Vertex(double x, double y)
: this(x, y, 0)
{ }
if (numAttributes > 0)
{
attributes = new double[numAttributes];
}
public Vertex(double x, double y, int mark)
: base(x, y, mark)
{
this.type = VertexType.InputVertex;
}
#region Public properties
/// <summary>
/// Gets the vertex id.
/// </summary>
public int ID
{
get { return this.id; }
}
/// <summary>
/// Gets the vertex type.
/// </summary>
public VertexType Type
{
get { return this.type; }
}
/// <summary>
@@ -61,110 +70,23 @@ namespace TriangleNet.Data
{
if (i == 0)
{
return pt.X;
return x;
}
if (i == 1)
{
return pt.Y;
return y;
}
throw new ArgumentOutOfRangeException("Index must be 0 or 1.");
}
}
/// <summary>
/// Reset the hash seed.
/// </summary>
/// <param name="value">The new has seed value.</param>
/// <remarks>Reset value will usally 0, if a new triangulation starts,
/// or the number of points, if refinement is done.</remarks>
internal static void ResetHashSeed(int value)
{
if (value < 0)
{
throw new ArgumentException("A hash seed must be non negative.");
}
hashSeed = value;
}
#region Operator overloading / overriding Equals
// Compare "Guidelines for Overriding Equals() and Operator =="
// http://msdn.microsoft.com/en-us/library/ms173147.aspx
public static bool operator ==(Vertex a, Vertex b)
{
// If both are null, or both are same instance, return true.
if (Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
return a.Equals(b);
}
public static bool operator !=(Vertex a, Vertex b)
{
return !(a == b);
}
public override bool Equals(object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
Vertex v = obj as Vertex;
if ((object)v == null)
{
return false;
}
return this.pt.Equals(v.pt);
}
public bool Equals(Vertex v)
{
// If vertex is null return false.
if ((object)v == null)
{
return false;
}
// Return true if the fields match:
return this.pt.Equals(v.pt);
}
#endregion
public override int GetHashCode()
{
return this.Hash;
}
#endregion
public override string ToString()
{
return String.Format("[{0},{1}]", pt.X, pt.Y);
}
public int CompareTo(Vertex other)
{
if (pt.X == other.pt.X && pt.Y == other.pt.Y)
{
return 0;
}
return (pt.X < other.pt.X || (pt.X == other.pt.X && pt.Y < other.pt.Y)) ? -1 : 1;
return this.hash;
}
}
}
+1 -1
View File
@@ -90,5 +90,5 @@ namespace TriangleNet
/// <summary>
/// The type of the mesh vertex.
/// </summary>
enum VertexType { InputVertex, SegmentVertex, FreeVertex, DeadVertex, UndeadVertex };
public enum VertexType { InputVertex, SegmentVertex, FreeVertex, DeadVertex, UndeadVertex };
}
@@ -0,0 +1,99 @@
// -----------------------------------------------------------------------
// <copyright file="EdgeEnumerator.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Geometry
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Data;
/// <summary>
/// Enumerates the edges of a triangulation.
/// </summary>
public class EdgeEnumerator : IEnumerator<Edge>
{
IEnumerator<Triangle> triangles;
Otri tri = default(Otri);
Otri neighbor = default(Otri);
Edge current;
Vertex p1, p2;
/// <summary>
/// Initializes a new instance of the <see cref="EdgeEnumerator" /> class.
/// </summary>
public EdgeEnumerator(Mesh mesh)
{
triangles = mesh.triangles.Values.GetEnumerator();
triangles.MoveNext();
tri.triangle = triangles.Current;
tri.orient = 0;
}
public Edge Current
{
get { return current; }
}
public void Dispose()
{
this.triangles.Dispose();
}
object System.Collections.IEnumerator.Current
{
get { return current; }
}
public bool MoveNext()
{
if (tri.triangle == null)
{
return false;
}
current = null;
while (current == null)
{
if (tri.orient == 3)
{
if (triangles.MoveNext())
{
tri.triangle = triangles.Current;
tri.orient = 0;
}
else
{
// Finally no more triangles
return false;
}
}
tri.Sym(ref neighbor);
if ((tri.triangle.id < neighbor.triangle.id) || (neighbor.triangle == Mesh.dummytri))
{
p1 = tri.Org();
p2 = tri.Dest();
current = new Edge(p1.id, p2.id);
}
tri.orient++;
}
return true;
}
public void Reset()
{
this.triangles.Reset();
}
}
}
@@ -75,7 +75,7 @@ namespace TriangleNet.Geometry
/// </summary>
public IEnumerable<Point> Points
{
get { return null; }
get { return points; }
}
/// <summary>
@@ -131,7 +131,7 @@ namespace TriangleNet.Geometry
/// <param name="boundary">Boundary marker.</param>
public void AddPoint(double x, double y, int boundary)
{
//points.Add(new Vertex(x, y, boundary));
points.Add(new Vertex(x, y, boundary));
bounds.Update(x, y);
}
@@ -176,7 +176,7 @@ namespace TriangleNet.Geometry
/// <param name="boundary">Segment marker.</param>
public void AddSegment(int p0, int p1, int boundary)
{
if (p0 == p1)
if (p0 == p1 || p0 < 0 || p1 < 0)
{
throw new NotSupportedException("Invalid endpoints.");
}
+21 -51
View File
@@ -14,6 +14,7 @@ namespace TriangleNet.IO
using System.Globalization;
using TriangleNet.Data;
using TriangleNet.Log;
using TriangleNet.Geometry;
/// <summary>
/// TODO: Update summary.
@@ -47,9 +48,9 @@ namespace TriangleNet.IO
/// the corresponding pointer is adjusted to refer to a subsegment rather
/// than the next triangle of the stack.
/// </remarks>
public static int Reconstruct(Mesh mesh, MeshData input)
public static int Reconstruct(Mesh mesh, InputGeometry input, ITriangle[] triangles)
{
long hullsize = 0;
int hullsize = 0;
Otri tri = default(Otri);
Otri triangleleft = default(Otri);
@@ -66,18 +67,17 @@ namespace TriangleNet.IO
Vertex segmentorg, segmentdest;
int[] corner = new int[3];
int[] end = new int[2];
bool segmentmarkers = false;
//bool segmentmarkers = false;
int boundmarker;
int aroundvertex;
bool notfound;
int i = 0;
int elements = input.Triangles == null ? 0 : input.Triangles.Length;
int attribs = input.TriangleAttributes == null ? 0 : input.TriangleAttributes.Length;
int numberofsegments = input.Segments == null ? 0 : input.Segments.Length;
int elements = triangles == null ? 0 : triangles.Length;
int numberofsegments = input.segments.Count;
mesh.inelements = elements;
mesh.eextras = attribs;
mesh.eextras = mesh.inelements > 0 ? triangles[0].Attributes.Length : 0;
// Create the triangles.
for (i = 0; i < mesh.inelements; i++)
@@ -90,7 +90,6 @@ namespace TriangleNet.IO
if (Behavior.Poly)
{
mesh.insegments = numberofsegments;
segmentmarkers = input.SegmentMarkers != null;
// Create the subsegments.
for (i = 0; i < mesh.insegments; i++)
@@ -115,7 +114,7 @@ namespace TriangleNet.IO
}
i = 0;
string debug = "";
// Read the triangles from the .ele file, and link
// together those that share an edge.
foreach (var item in mesh.triangles.Values)
@@ -125,7 +124,7 @@ namespace TriangleNet.IO
// Copy the triangle's three corners.
for (int j = 0; j < 3; j++)
{
corner[j] = input.Triangles[i][j];
corner[j] = triangles[i][j];
if ((corner[j] < 0) || (corner[j] >= mesh.invertices))
{
SimpleLog.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()");
@@ -134,15 +133,12 @@ namespace TriangleNet.IO
}
// Read the triangle's attributes.
for (int j = 0; j < mesh.eextras; j++)
{
tri.triangle.attributes[j] = input.TriangleAttributes[i][j];
}
tri.triangle.attributes = triangles[i].Attributes;
// TODO
if (Behavior.VarArea)
{
tri.triangle.area = input.TriangleAreas[i];
tri.triangle.area = triangles[i].Area;
}
// Set the triangle's vertices.
@@ -151,8 +147,6 @@ namespace TriangleNet.IO
tri.SetDest(mesh.vertices[corner[1]]);
tri.SetApex(mesh.vertices[corner[2]]);
debug += String.Format("Checking element {0} [{1}, {2}, {3}]\n", i, corner[0], corner[1], corner[2]);
// Try linking the triangle to others that share these vertices.
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
@@ -168,35 +162,25 @@ namespace TriangleNet.IO
checktri = nexttri;
debug += String.Format(" {0}: aroundvertex = {1}\n", tri.orient, aroundvertex);
if (checktri.triangle != Mesh.dummytri)
{
tdest = tri.Dest();
tapex = tri.Apex();
debug += String.Format(" No dummy: tdest ({0}, {1}), tapex ({2}, {3})\n",
tdest[0], tdest[1], tapex[0], tapex[1]);
// Look for other triangles that share an edge.
do
{
checkdest = checktri.Dest();
checkapex = checktri.Apex();
debug += String.Format(" checktri.orient {0}\n", checktri.orient);
debug += String.Format(" checkdest ({0}, {1}), checkapex ({2}, {3})\n",
checkdest[0], checkdest[1], checkapex[0], checkapex[1]);
if (tapex == checkdest)
{
debug += String.Format(" > tapex == checkdest\n");
// The two triangles share an edge; bond them together.
tri.Lprev(ref triangleleft);
triangleleft.Bond(ref checktri);
}
if (tdest == checkapex)
{
debug += String.Format(" > tdest == checkapex\n");
// The two triangles share an edge; bond them together.
checktri.Lprev(ref checkleft);
tri.Bond(ref checkleft);
@@ -223,14 +207,11 @@ namespace TriangleNet.IO
i = 0;
foreach (var item in mesh.subsegs.Values)
{
subseg.ss = item;
subseg.seg = item;
end[0] = input.Segments[i][0];
end[1] = input.Segments[i][1];
if (segmentmarkers)
{
boundmarker = input.SegmentMarkers[i];
}
end[0] = input.segments[i].P0;
end[1] = input.segments[i].P1;
boundmarker = input.segments[i].Boundary;
for (int j = 0; j < 2; j++)
{
@@ -241,22 +222,20 @@ namespace TriangleNet.IO
}
}
debug += String.Format("Checking segment {0} [{1}, {2}]\n", i, end[0], end[1]);
// set the subsegment's vertices.
subseg.ssorient = 0;
subseg.orient = 0;
segmentorg = mesh.vertices[end[0]];
segmentdest = mesh.vertices[end[1]];
subseg.SetOrg(segmentorg);
subseg.SetDest(segmentdest);
subseg.SetSegOrg(segmentorg);
subseg.SetSegDest(segmentdest);
subseg.ss.boundary = boundmarker;
subseg.seg.boundary = boundmarker;
// Try linking the subsegment to triangles that share these vertices.
for (subseg.ssorient = 0; subseg.ssorient < 2; subseg.ssorient++)
for (subseg.orient = 0; subseg.orient < 2; subseg.orient++)
{
// Take the number for the destination of subsegloop.
aroundvertex = end[1 - subseg.ssorient];
debug += String.Format(" {0}: aroundvertex = {1}\n", subseg.ssorient, aroundvertex);
aroundvertex = end[1 - subseg.orient];
int index = vertexarray[aroundvertex].Count - 1;
// Look for triangles having this vertex.
prevlink = vertexarray[aroundvertex][index];
@@ -275,12 +254,9 @@ namespace TriangleNet.IO
while (notfound && (checktri.triangle != Mesh.dummytri))
{
checkdest = checktri.Dest();
debug += String.Format(" No dummy: shorg ({0}, {1}), checkdest ({2}, {3})\n",
shorg[0], shorg[1], checkdest[0], checkdest[1]);
if (shorg == checkdest)
{
debug +=" shorg == checkdest\n";
// We have a match. Remove this triangle from the list.
//prevlink = vertexarray[aroundvertex][index];
vertexarray[aroundvertex].Remove(prevlink);
@@ -290,7 +266,6 @@ namespace TriangleNet.IO
checktri.Sym(ref checkneighbor);
if (checkneighbor.triangle == Mesh.dummytri)
{
debug +=" checkneighbor.tri == m->dummytri\n";
// The next line doesn't insert a subsegment (because there's
// already one there), but it sets the boundary markers of
// the existing subsegment and its vertices.
@@ -312,7 +287,6 @@ namespace TriangleNet.IO
}
}
debug += "\nMark the remaining edges\n\n";
// Mark the remaining edges as not being attached to any subsegment.
// Also, count the (yet uncounted) boundary edges.
for (i = 0; i < mesh.vertices.Count; i++)
@@ -324,7 +298,6 @@ namespace TriangleNet.IO
while (checktri.triangle != Mesh.dummytri)
{
debug += " checktri.triangle != Mesh.dummytri\n";
// Find the next triangle in the stack before this
// information gets overwritten.
index--;
@@ -336,16 +309,13 @@ namespace TriangleNet.IO
{
mesh.InsertSubseg(ref checktri, 1);
hullsize++;
debug += "checkneighbor.triangle == Mesh.dummytri (hullsize = " + hullsize + ")\n";
}
checktri = nexttri;
}
}
debug += "\nmesh.subsegs.Count = " + mesh.subsegs.Count;
return (int)hullsize;
return hullsize;
}
#endregion
+15 -286
View File
@@ -12,286 +12,15 @@ namespace TriangleNet.IO
using System.Collections.Generic;
using System.Globalization;
using TriangleNet.Data;
using TriangleNet.Geometry;
/// <summary>
/// Generates a mesh representaion using arrays.
/// </summary>
public static class DataWriter
{
static NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat;
static int verticesCount;
static int elementsCount;
#region Library
/// <summary>
/// Number the vertices and write them to raw output data.
/// </summary>
/// <param name="mesh"></param>
/// <param name="data"></param>
public static void WriteNodes(Mesh mesh, MeshData data)
{
Vertex vertex;
int outvertices = mesh.vertices.Count;
if (Behavior.Jettison)
{
outvertices = mesh.vertices.Count - mesh.undeads;
}
verticesCount = outvertices;
// Allocate memory for output vertices if necessary.
data.Points = new double[outvertices][];
// Allocate memory for output vertex attributes if necessary.
if (mesh.nextras > 0)
{
data.PointAttributes = new double[outvertices][];
}
// Allocate memory for output vertex markers if necessary.
if (Behavior.UseBoundaryMarkers)
{
data.PointMarkers = new int[outvertices];
}
int i = 0;
foreach (var item in mesh.vertices.Values)
{
vertex = item;
if (!Behavior.Jettison || vertex.type != VertexType.UndeadVertex)
{
// X and y coordinates.
data.Points[i] = new double[] { vertex.pt.X, vertex.pt.Y };
// Vertex attributes.
if (data.PointAttributes != null)
{
data.PointAttributes[i] = vertex.attributes;
}
if (Behavior.UseBoundaryMarkers)
{
// Save the boundary marker.
data.PointMarkers[i] = vertex.mark;
}
// Assign array index to vertex ID for later use.
vertex.ID = i++;
}
}
}
/// <summary>
/// Write the triangles to raw output data.
/// </summary>
/// <param name="mesh"></param>
/// <param name="data"></param>
public static void WriteElements(Mesh mesh, MeshData data)
{
Otri tri = default(Otri);
Vertex p1, p2, p3;
elementsCount = mesh.triangles.Count;
// Allocate memory for output triangles if necessary.
data.Triangles = new int[elementsCount][];
// Allocate memory for output triangle attributes if necessary.
if (mesh.eextras > 0)
{
data.TriangleAttributes = new double[mesh.triangles.Count][];
}
tri.orient = 0;
int i = 0;
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
p1 = tri.Org();
p2 = tri.Dest();
p3 = tri.Apex();
// Triangle order is always 1 (no higher order elements supported)
data.Triangles[i] = new int[] { p1.ID, p2.ID, p3.ID };
if (data.TriangleAttributes != null)
{
data.TriangleAttributes[i] = tri.triangle.attributes;
}
// Update ID for later use
item.ID = i++;
}
}
/// <summary>
/// Write the segments and holes to raw output data.
/// </summary>
/// <param name="mesh"></param>
/// <param name="data"></param>
public static void WritePoly(Mesh mesh, MeshData data)
{
Osub subseg = default(Osub);
Vertex pt1, pt2;
int n = mesh.subsegs.Count;
// Allocate memory for output segments if necessary.
data.Segments = new int[n][];
// Allocate memory for output segment markers if necessary.
if (Behavior.UseBoundaryMarkers)
{
data.SegmentMarkers = new int[n];
}
subseg.ssorient = 0;
int i = 0;
foreach (var item in mesh.subsegs.Values)
{
subseg.ss = item;
pt1 = subseg.Org();
pt2 = subseg.Dest();
// Copy indices of the segment's two endpoints.
data.Segments[i] = new int[] { pt1.ID, pt2.ID };
// Copy the boundary marker.
if (Behavior.UseBoundaryMarkers)
{
data.SegmentMarkers[i] = subseg.ss.boundary;
}
i++;
}
n = mesh.holes.Count;
if (n > 0)
{
data.Holes = new double[n][];
i = 0;
foreach (var hole in mesh.holes)
{
data.Holes[i] = new double[] { hole.X, hole.Y };
i++;
}
}
}
/// <summary>
/// Write the edges to raw output data.
/// </summary>
/// <param name="mesh"></param>
/// <param name="data"></param>
public static void WriteEdges(Mesh mesh, MeshData data)
{
Otri tri = default(Otri), trisym = default(Otri);
Osub checkmark = default(Osub);
Vertex p1, p2;
// Allocate memory for edges if necessary.
data.Edges = new int[mesh.edges][];
// Allocate memory for edge markers if necessary.
if (Behavior.UseBoundaryMarkers)
{
data.EdgeMarkers = new int[mesh.edges];
}
int index = 0;
// To loop over the set of edges, loop over all triangles, and look at
// the three edges of each triangle. If there isn't another triangle
// adjacent to the edge, operate on the edge. If there is another
// adjacent triangle, operate on the edge only if the current triangle
// has a smaller pointer than its neighbor. This way, each edge is
// considered only once.
foreach (var item in mesh.triangles.Values)
{
tri.triangle = item;
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
tri.Sym(ref trisym);
if ((tri.triangle.ID < trisym.triangle.ID) || (trisym.triangle == Mesh.dummytri))
{
p1 = tri.Org();
p2 = tri.Dest();
data.Edges[index] = new int[] { p1.ID, p2.ID };
if (Behavior.UseBoundaryMarkers)
{
// Edge number, indices of two endpoints, and a boundary marker.
// If there's no subsegment, the boundary marker is zero.
if (Behavior.UseSegments)
{
tri.SegPivot(ref checkmark);
if (checkmark.ss == Mesh.dummysub)
{
data.EdgeMarkers[index] = 0;
}
else
{
data.EdgeMarkers[index] = checkmark.ss.boundary;
}
}
else
{
data.EdgeMarkers[index] = (trisym.triangle == Mesh.dummytri ? 1 : 0);
}
}
index++;
}
}
}
}
/// <summary>
/// Write the triangle neighbors to raw output data.
/// </summary>
/// <param name="mesh"></param>
/// <param name="data"></param>
/// <remarks>WARNING: Be sure WriteElements has been called before,
/// so the elements are numbered right!</remarks>
public static void WriteNeighbors(Mesh mesh, MeshData data)
{
Otri tri = default(Otri), trisym = default(Otri);
// Allocate memory for neighbors if necessary.
data.Neighbors = new int[mesh.triangles.Count][];
Mesh.dummytri.ID = -1;
int i = 0;
foreach (var item in mesh.triangles.Values)
{
data.Neighbors[i] = new int[3];
tri.triangle = item;
tri.orient = 1;
tri.Sym(ref trisym);
data.Neighbors[i][0] = trisym.triangle.ID;
tri.orient = 2;
tri.Sym(ref trisym);
data.Neighbors[i][1] = trisym.triangle.ID;
tri.orient = 0;
tri.Sym(ref trisym);
data.Neighbors[i][2] = trisym.triangle.ID;
i++;
}
}
/// <summary>
/// Gets the Voronoi diagram as raw output data.
/// </summary>
@@ -312,31 +41,31 @@ namespace TriangleNet.IO
Otri tri = default(Otri), trisym = default(Otri);
Vertex torg, tdest, tapex;
Point2 circumcenter;
Point circumcenter;
double xi = 0, eta = 0;
int p1, p2;
int i = 0;
// Copy input points (actually not part of the voronoi diagram)
data.InputPoints = new double[mesh.vertices.Count][];
data.InputPoints = new Vertex[mesh.vertices.Count];
foreach (var item in mesh.vertices.Values)
{
if (item.type != VertexType.UndeadVertex)
{
data.InputPoints[i] = new double[] { item.pt.X, item.pt.Y };
data.InputPoints[i] = item;
i++;
}
}
// Allocate memory for Voronoi vertices.
data.Points = new double[mesh.triangles.Count][];
data.Points = new Vertex[mesh.triangles.Count];
int index = 0;
tri.orient = 0;
i = 0;
foreach (var item in mesh.triangles.Values)
{
@@ -344,17 +73,17 @@ namespace TriangleNet.IO
torg = tri.Org();
tdest = tri.Dest();
tapex = tri.Apex();
circumcenter = Primitives.FindCircumcenter(torg.pt, tdest.pt, tapex.pt, ref xi, ref eta, false);
circumcenter = Primitives.FindCircumcenter(torg, tdest, tapex, ref xi, ref eta, false);
// X and y coordinates.
data.Points[i] = new double[] { circumcenter.X, circumcenter.Y };
data.Points[i] = new Point(circumcenter.x, circumcenter.y);
// Update element id
tri.triangle.ID = i++;
tri.triangle.id = i++;
}
// Allocate memory for output Voronoi edges.
data.Edges = new int[mesh.edges][];
data.Edges = new Edge[mesh.edges];
// Allocate memory for output Voronoi norms.
data.Directions = new double[mesh.edges][];
@@ -373,10 +102,10 @@ namespace TriangleNet.IO
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
tri.Sym(ref trisym);
if ((tri.triangle.ID < trisym.triangle.ID) || (trisym.triangle == Mesh.dummytri))
if ((tri.triangle.id < trisym.triangle.id) || (trisym.triangle == Mesh.dummytri))
{
// Find the number of this triangle (and Voronoi vertex).
p1 = tri.triangle.ID;
p1 = tri.triangle.id;
if (trisym.triangle == Mesh.dummytri)
{
@@ -384,16 +113,16 @@ namespace TriangleNet.IO
tdest = tri.Dest();
// Copy an infinite ray. Index of one endpoint, and -1.
data.Edges[index] = new int[] { p1, -1};
data.Edges[index] = new Edge(p1, -1);
data.Directions[index] = new double[] { tdest[1] - torg[1], torg[0] - tdest[0] };
}
else
{
// Find the number of the adjacent triangle (and Voronoi vertex).
p2 = trisym.triangle.ID;
p2 = trisym.triangle.id;
// Finite edge. Write indices of two endpoints.
data.Edges[index] = new int[] { p1, p2 };
data.Edges[index] = new Edge(p1, p2);
data.Directions[index] = new double[] { 0, 0 };
}
+164
View File
@@ -0,0 +1,164 @@
// -----------------------------------------------------------------------
// <copyright file="DebugWriter.cs" company="">
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.IO
{
using System;
using System.IO;
using System.Globalization;
using TriangleNet.Data;
/// <summary>
/// Writes a the current mesh into a text file.
/// </summary>
/// <remarks>
/// File format:
///
/// num_nodes
/// nid_1 nx ny mark
/// ...
/// nid_n nx ny mark
///
/// num_segs
/// sid_1 p1 p2 mark
/// ...
/// sid_n p1 p2 mark
///
/// num_tris
/// tid_1 p1 p2 p3 n1 n2 n3
/// ...
/// tid_n p1 p2 p3 n1 n2 n3
/// </remarks>
class DebugWriter
{
static NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat;
Mesh mesh;
int iteration;
string name;
public DebugWriter(Mesh mesh)
{
this.mesh = mesh;
this.iteration = 0;
this.name = "debug-{0}.mesh";
}
/// <summary>
/// Start a new session with given name.
/// </summary>
/// <param name="name">Name of the session (and output files).</param>
public void NewSession(string name)
{
this.iteration = 0;
this.name = name + "-{0}.mesh";
}
/// <summary>
/// Write complete mesh to a .mesh file.
/// </summary>
public void Write()
{
Vertex p1, p2, p3;
string file = String.Format(name, iteration++);
using (StreamWriter writer = new StreamWriter(file))
{
// Number of vertices.
writer.WriteLine("{0}", mesh.vertices.Count);
foreach (var v in mesh.vertices.Values)
{
// Vertex number, x and y coordinates and marker.
writer.WriteLine("{0} {1} {2} {3}", v.hash, v.x.ToString(nfi), v.y.ToString(nfi), v.mark);
}
// Number of segments.
writer.WriteLine("{0}", mesh.subsegs.Count);
Osub subseg = default(Osub);
subseg.orient = 0;
foreach (var item in mesh.subsegs.Values)
{
if (item.hash <= 0)
{
continue;
}
subseg.seg = item;
p1 = subseg.Org();
p2 = subseg.Dest();
// Segment number, indices of its two endpoints, and marker.
writer.WriteLine("{0} {1} {2} {3}", subseg.seg.hash, p1.hash, p2.hash, subseg.seg.boundary);
}
Otri tri = default(Otri), trisym = default(Otri);
tri.orient = 0;
int n1, n2, n3, hash3;
// Number of triangles.
writer.WriteLine("{0}", mesh.triangles.Count);
foreach (var item in mesh.triangles.Values)
{
if (item.hash <= 0)
{
continue;
}
tri.triangle = item;
p1 = tri.Org();
p2 = tri.Dest();
p3 = tri.Apex();
if (p3 == null)
{
if (p1 == null || p2 == null)
{
continue;
}
hash3 = -1;
}
else
{
hash3 = p3.hash;
}
if (p1 == null || p2 == null)
{
continue;
}
// Triangle number, indices for three vertices.
writer.Write("{0} {1} {2} {3}", tri.triangle.hash, p1.hash, p2.hash, hash3);
tri.orient = 1;
tri.Sym(ref trisym);
n1 = trisym.triangle.hash;
tri.orient = 2;
tri.Sym(ref trisym);
n2 = trisym.triangle.hash;
tri.orient = 0;
tri.Sym(ref trisym);
n3 = trisym.triangle.hash;
// Neighboring triangle numbers.
writer.WriteLine(" {0} {1} {2}", n1, n2, n3);
}
}
}
}
}
+100 -148
View File
@@ -12,6 +12,8 @@ namespace TriangleNet.IO
using System.Globalization;
using TriangleNet.Data;
using TriangleNet.Log;
using TriangleNet.Geometry;
using System.Collections.Generic;
/// <summary>
/// Helper for reading Triangle files.
@@ -26,7 +28,7 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="filename">The file to read.</param>
/// <remarks>Will NOT read associated files by default.</remarks>
public static MeshData ReadFile(string filename)
public static InputGeometry ReadFile(string filename)
{
return ReadFile(filename, false);
}
@@ -36,7 +38,7 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="filename">The file to read.</param>
/// <param name="readsupp">Read associated files (ele, area, neigh).</param>
public static MeshData ReadFile(string filename, bool readsupp)
public static InputGeometry ReadFile(string filename, bool readsupp)
{
string ext = Path.GetExtension(filename);
@@ -78,33 +80,36 @@ namespace TriangleNet.IO
return true;
}
static void ReadVertex(MeshData data, int index, string[] line)
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="index"></param>
/// <param name="line"></param>
/// <param name="n">Number of point attributes</param>
static void ReadVertex(InputGeometry data, int index, string[] line, int n)
{
int n = data.PointAttributes == null ? 0 : data.PointAttributes.Length;
data.Points[index] = new double[] {
double.Parse(line[1], nfi),
double.Parse(line[2], nfi) };
double x = double.Parse(line[1], nfi);
double y = double.Parse(line[2], nfi);
int mark = 0;
// Read the vertex attributes.
for (int j = 0; j < n; j++)
{
data.PointAttributes[index] = new double[n];
if (line.Length > 3 + j)
{
data.PointAttributes[index][j] = double.Parse(line[3 + j]);
// TODO:
//vertex.attributes[j] = double.Parse(line[3 + j]);
}
}
if (data.PointMarkers != null)
// Read a vertex marker.
if (line.Length > 3 + n)
{
// Read a vertex marker.
if (line.Length > 3 + n)
{
data.PointMarkers[index] = int.Parse(line[3 + n]);
}
mark = int.Parse(line[3 + n]);
}
data.AddPoint(x, y, mark);
}
/// <summary>
@@ -112,7 +117,7 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="nodefilename"></param>
/// <remarks>Will NOT read associated .ele by default.</remarks>
public static MeshData ReadNodeFile(string nodefilename)
public static InputGeometry ReadNodeFile(string nodefilename)
{
return ReadNodeFile(nodefilename, false);
}
@@ -122,9 +127,9 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="nodefilename"></param>
/// <param name="readElements"></param>
public static MeshData ReadNodeFile(string nodefilename, bool readElements)
public static InputGeometry ReadNodeFile(string nodefilename, bool readElements)
{
MeshData data = new MeshData();
InputGeometry data;
startIndex = 0;
@@ -165,21 +170,11 @@ namespace TriangleNet.IO
nodemarkers = int.Parse(line[3]);
}
data = new InputGeometry(invertices);
// Read the vertices.
if (invertices > 0)
{
data.Points = new double[invertices][];
if (attributes > 0)
{
data.PointAttributes = new double[invertices][];
}
if (nodemarkers > 0)
{
data.PointMarkers = new int[invertices];
}
for (int i = 0; i < invertices; i++)
{
if (!TryReadLine(reader, out line))
@@ -197,7 +192,7 @@ namespace TriangleNet.IO
startIndex = int.Parse(line[0], nfi);
}
ReadVertex(data, i, line);
ReadVertex(data, i, line, attributes);
}
}
}
@@ -208,7 +203,7 @@ namespace TriangleNet.IO
string elefile = Path.ChangeExtension(nodefilename, ".ele");
if (File.Exists(elefile))
{
ReadEleFile(elefile, data, true);
ReadEleFile(elefile, true);
}
}
@@ -220,7 +215,7 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="polyfilename"></param>
/// <remarks>Will NOT read associated .ele by default.</remarks>
public static MeshData ReadPolyFile(string polyfilename)
public static InputGeometry ReadPolyFile(string polyfilename)
{
return ReadPolyFile(polyfilename, false, false);
}
@@ -231,7 +226,7 @@ namespace TriangleNet.IO
/// <param name="polyfilename"></param>
/// <param name="readElements">If true, look for an associated .ele file.</param>
/// <remarks>Will NOT read associated .area by default.</remarks>
public static MeshData ReadPolyFile(string polyfilename, bool readElements)
public static InputGeometry ReadPolyFile(string polyfilename, bool readElements)
{
return ReadPolyFile(polyfilename, readElements, false);
}
@@ -242,10 +237,10 @@ namespace TriangleNet.IO
/// <param name="polyfilename"></param>
/// <param name="readElements">If true, look for an associated .ele file.</param>
/// <param name="readElements">If true, look for an associated .area file.</param>
public static MeshData ReadPolyFile(string polyfilename, bool readElements, bool readArea)
public static InputGeometry ReadPolyFile(string polyfilename, bool readElements, bool readArea)
{
// Read poly file
MeshData data;
InputGeometry data;
startIndex = 0;
@@ -284,19 +279,7 @@ namespace TriangleNet.IO
// Read the vertices.
if (invertices > 0)
{
data = new MeshData();
data.Points = new double[invertices][];
if (attributes > 0)
{
data.PointAttributes = new double[invertices][];
}
if (nodemarkers > 0)
{
data.PointMarkers = new int[invertices];
}
data = new InputGeometry(invertices);
for (int i = 0; i < invertices; i++)
{
@@ -316,7 +299,7 @@ namespace TriangleNet.IO
startIndex = int.Parse(line[0], nfi);
}
ReadVertex(data, i, line);
ReadVertex(data, i, line, attributes);
}
}
else
@@ -325,7 +308,7 @@ namespace TriangleNet.IO
// the vertices should be read from a separate .node file.
string nodefile = Path.ChangeExtension(polyfilename, ".node");
data = ReadNodeFile(nodefile);
invertices = data.Points.Length;
invertices = data.Count;
}
if (data.Points == null)
@@ -349,17 +332,7 @@ namespace TriangleNet.IO
segmentmarkers = int.Parse(line[1]);
}
if (insegments > 0)
{
data.Segments = new int[insegments][];
}
if (segmentmarkers > 0)
{
data.SegmentMarkers = new int[insegments];
}
int end1, end2;
int end1, end2, mark;
// Read and insert the segments.
for (int i = 0; i < insegments; i++)
{
@@ -376,24 +349,18 @@ namespace TriangleNet.IO
// TODO: startIndex ok?
end1 = int.Parse(line[1]) - startIndex;
end2 = int.Parse(line[2]) - startIndex;
mark = 0;
if (segmentmarkers > 0)
if (segmentmarkers > 0 && line.Length > 3)
{
if (line.Length > 3)
{
data.SegmentMarkers[i] = int.Parse(line[3]);
}
else
{
data.SegmentMarkers[i] = 0;
}
mark = int.Parse(line[3]);
}
if ((end1 < 0) || (end1 >= invertices))
{
if (Behavior.Verbose)
{
SimpleLog.Instance.Warning("Invalid first endpoint of segment.",
SimpleLog.Instance.Warning("Invalid first endpoint of segment.",
"MeshReader.ReadPolyfile()");
}
}
@@ -401,13 +368,13 @@ namespace TriangleNet.IO
{
if (Behavior.Verbose)
{
SimpleLog.Instance.Warning("Invalid second endpoint of segment.",
SimpleLog.Instance.Warning("Invalid second endpoint of segment.",
"MeshReader.ReadPolyfile()");
}
}
else
{
data.Segments[i] = new int[] { end1, end2 };
data.AddSegment(end1, end2, mark);
}
}
@@ -422,8 +389,6 @@ namespace TriangleNet.IO
int holes = int.Parse(line[0]);
if (holes > 0)
{
data.Holes = new double[holes][];
for (int i = 0; i < holes; i++)
{
if (!TryReadLine(reader, out line))
@@ -436,9 +401,8 @@ namespace TriangleNet.IO
throw new Exception("Invalid hole.");
}
data.Holes[i] = new double[] {
double.Parse(line[1], nfi),
double.Parse(line[2], nfi) };
data.AddHole(double.Parse(line[1], nfi),
double.Parse(line[2], nfi));
}
}
@@ -449,8 +413,6 @@ namespace TriangleNet.IO
if (regions > 0)
{
data.Regions = new double[regions][];
for (int i = 0; i < regions; i++)
{
if (!TryReadLine(reader, out line))
@@ -463,14 +425,14 @@ namespace TriangleNet.IO
throw new Exception("Invalid region.");
}
data.Regions[i] = new double[] {
data.AddRegion(
// Region x and y
double.Parse(line[1]),
double.Parse(line[2]),
// Region attribute
double.Parse(line[3]),
// Region area constraint
double.Parse(line[4]) };
double.Parse(line[4]));
}
}
}
@@ -482,20 +444,16 @@ namespace TriangleNet.IO
string elefile = Path.ChangeExtension(polyfilename, ".ele");
if (File.Exists(elefile))
{
ReadEleFile(elefile, data, readArea);
ReadEleFile(elefile, readArea);
}
}
return data;
}
public static MeshData ReadEleFile(string elefilename)
public static List<ITriangle> ReadEleFile(string elefilename)
{
MeshData data = new MeshData();
ReadEleFile(elefilename, data, false);
return data;
return ReadEleFile(elefilename, false);
}
/// <summary>
@@ -504,10 +462,12 @@ namespace TriangleNet.IO
/// <param name="elefilename"></param>
/// <param name="data"></param>
/// <param name="readArea"></param>
private static void ReadEleFile(string elefilename, MeshData data, bool readArea)
private static List<ITriangle> ReadEleFile(string elefilename, bool readArea)
{
int intriangles = 0, attributes = 0;
List<ITriangle> triangles;
using (StreamReader reader = new StreamReader(elefilename))
{
// Read number of elements and number of attributes.
@@ -527,12 +487,9 @@ namespace TriangleNet.IO
attributes = int.Parse(line[2]);
}
data.Triangles = new int[intriangles][];
triangles = new List<ITriangle>(intriangles);
if (attributes > 0)
{
data.TriangleAttributes = new double[intriangles][];
}
InputTriangle tri;
// Read triangles.
for (int i = 0; i < intriangles; i++)
@@ -548,24 +505,26 @@ namespace TriangleNet.IO
}
// TODO: startIndex ok?
data.Triangles[i] = new int[] {
tri = new InputTriangle(
int.Parse(line[1]) - startIndex,
int.Parse(line[2]) - startIndex,
int.Parse(line[3]) - startIndex };
int.Parse(line[3]) - startIndex);
// Read triangle attributes
if (attributes > 0)
{
for (int j = 0; j < attributes; j++)
{
data.TriangleAttributes[i] = new double[attributes];
tri.attributes = new double[attributes];
if (line.Length > 4 + j)
{
data.TriangleAttributes[i][j] = double.Parse(line[4 + j]);
tri.attributes[j] = double.Parse(line[4 + j]);
}
}
}
triangles.Add(tri);
}
}
@@ -575,9 +534,11 @@ namespace TriangleNet.IO
string areafile = Path.ChangeExtension(elefilename, ".area");
if (File.Exists(areafile))
{
ReadAreaFile(areafile, intriangles, data);
ReadAreaFile(areafile, intriangles);
}
}
return triangles;
}
/// <summary>
@@ -586,8 +547,10 @@ namespace TriangleNet.IO
/// <param name="areafilename"></param>
/// <param name="intriangles"></param>
/// <param name="data"></param>
private static void ReadAreaFile(string areafilename, int intriangles, MeshData data)
private static double[] ReadAreaFile(string areafilename, int intriangles)
{
double[] data = null;
using (StreamReader reader = new StreamReader(areafilename))
{
string[] line;
@@ -599,12 +562,12 @@ namespace TriangleNet.IO
if (int.Parse(line[0]) != intriangles)
{
SimpleLog.Instance.Warning("Number of area constraints doesn't match number of triangles.",
SimpleLog.Instance.Warning("Number of area constraints doesn't match number of triangles.",
"ReadAreaFile()");
return;
return null;
}
data.TriangleAreas = new double[intriangles];
data = new double[intriangles];
// Read area constraints.
for (int i = 0; i < intriangles; i++)
@@ -619,15 +582,17 @@ namespace TriangleNet.IO
throw new Exception("Triangle has no nodes.");
}
data.TriangleAreas[i] = double.Parse(line[1], nfi);
data[i] = double.Parse(line[1], nfi);
}
}
return data;
}
public static MeshData ReadEdgeFile(string edgeFile)
public static List<Edge> ReadEdgeFile(string edgeFile, int invertices)
{
// Read poly file
MeshData data = new MeshData();
List<Edge> data = null;
startIndex = 0;
@@ -653,15 +618,10 @@ namespace TriangleNet.IO
if (inedges > 0)
{
data.Edges = new int[inedges][];
data = new List<Edge>(inedges);
}
if (edgemarkers > 0)
{
data.EdgeMarkers = new int[inedges];
}
int end1, end2;
int end1, end2, mark;
// Read and insert the segments.
for (int i = 0; i < inedges; i++)
{
@@ -678,41 +638,33 @@ namespace TriangleNet.IO
// TODO: startIndex ok?
end1 = int.Parse(line[1]) - startIndex;
end2 = int.Parse(line[2]) - startIndex;
mark = 0;
if (edgemarkers > 0)
if (edgemarkers > 0 && line.Length > 3)
{
if (line.Length > 3)
{
data.SegmentMarkers[i] = int.Parse(line[3]);
}
else
{
data.SegmentMarkers[i] = 0;
}
mark = int.Parse(line[3]);
}
data.Segments[i] = new int[] { end1, end2 };
//if ((end1 < 0) || (end1 >= invertices))
//{
// if (Behavior.Verbose)
// {
// SimpleLogger.Instance.Warning("Invalid first endpoint of segment.",
// "MeshReader.ReadPolyfile()");
// }
//}
//else if ((end2 < 0) || (end2 >= invertices))
//{
// if (Behavior.Verbose)
// {
// SimpleLogger.Instance.Warning("Invalid second endpoint of segment.",
// "MeshReader.ReadPolyfile()");
// }
//}
//else
//{
// data.Segments[i] = new int[] { end1, end2 };
//}
if ((end1 < 0) || (end1 >= invertices))
{
if (Behavior.Verbose)
{
SimpleLog.Instance.Warning("Invalid first endpoint of segment.",
"MeshReader.ReadPolyfile()");
}
}
else if ((end2 < 0) || (end2 >= invertices))
{
if (Behavior.Verbose)
{
SimpleLog.Instance.Warning("Invalid second endpoint of segment.",
"MeshReader.ReadPolyfile()");
}
}
else
{
data.Add(new Edge(end1, end2, mark));
}
}
}
+31 -30
View File
@@ -11,6 +11,7 @@ namespace TriangleNet.IO
using System.IO;
using System.Globalization;
using TriangleNet.Data;
using TriangleNet.Geometry;
/// <summary>
/// TODO: Update summary.
@@ -67,7 +68,7 @@ namespace TriangleNet.IO
if (!Behavior.Jettison || vertex.type != VertexType.UndeadVertex)
{
// Vertex number, x and y coordinates.
writer.Write("{0} {1} {2}", index, vertex.pt.X.ToString(nfi), vertex.pt.Y.ToString(nfi));
writer.Write("{0} {1} {2}", index, vertex.x.ToString(nfi), vertex.y.ToString(nfi));
// Write attributes.
for (int j = 0; j < mesh.nextras; j++)
@@ -84,7 +85,7 @@ namespace TriangleNet.IO
writer.WriteLine();
// Assign array index to vertex ID for later use.
vertex.ID = index++;
vertex.id = index++;
}
}
}
@@ -118,7 +119,7 @@ namespace TriangleNet.IO
p3 = tri.Apex();
// Triangle number, indices for three vertices.
writer.Write("{0} {1} {2} {3}", j, p1.ID, p2.ID, p3.ID);
writer.Write("{0} {1} {2} {3}", j, p1.id, p2.id, p3.id);
for (int i = 0; i < mesh.eextras; i++)
{
@@ -128,7 +129,7 @@ namespace TriangleNet.IO
writer.WriteLine();
// Number elements
item.ID = j++;
item.id = j++;
}
}
}
@@ -177,12 +178,12 @@ namespace TriangleNet.IO
writer.WriteLine("{0} {1}", mesh.subsegs.Count,
Behavior.UseBoundaryMarkers ? "1" : "0");
subseg.ssorient = 0;
subseg.orient = 0;
int j = 0;
foreach (var item in mesh.subsegs.Values)
{
subseg.ss = item;
subseg.seg = item;
pt1 = subseg.Org();
pt2 = subseg.Dest();
@@ -190,11 +191,11 @@ namespace TriangleNet.IO
// Segment number, indices of its two endpoints, and possibly a marker.
if (Behavior.UseBoundaryMarkers)
{
writer.WriteLine("{0} {1} {2} {3}", j, pt1.ID, pt2.ID, subseg.ss.boundary);
writer.WriteLine("{0} {1} {2} {3}", j, pt1.id, pt2.id, subseg.seg.boundary);
}
else
{
writer.WriteLine("{0} {1} {2}", j, pt1.ID, pt2.ID);
writer.WriteLine("{0} {1} {2}", j, pt1.id, pt2.id);
}
j++;
@@ -214,8 +215,8 @@ namespace TriangleNet.IO
writer.WriteLine("{0}", mesh.regions.Count);
foreach (var region in mesh.regions)
{
writer.WriteLine("{0} {1} {2} {3} {4}", j, region.pt.X.ToString(nfi),
region.pt.Y.ToString(nfi), region.attribute.ToString(nfi),
writer.WriteLine("{0} {1} {2} {3} {4}", j, region.point.X.ToString(nfi),
region.point.Y.ToString(nfi), region.attribute.ToString(nfi),
region.area.ToString(nfi));
j++;
@@ -254,7 +255,7 @@ namespace TriangleNet.IO
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
tri.Sym(ref trisym);
if ((tri.triangle.ID < trisym.triangle.ID) || (trisym.triangle == Mesh.dummytri))
if ((tri.triangle.id < trisym.triangle.id) || (trisym.triangle == Mesh.dummytri))
{
p1 = tri.Org();
p2 = tri.Dest();
@@ -267,26 +268,26 @@ namespace TriangleNet.IO
{
tri.SegPivot(ref checkmark);
if (checkmark.ss == Mesh.dummysub)
if (checkmark.seg == Mesh.dummysub)
{
writer.WriteLine("{0} {1} {2} {3}", index, p1.ID, p2.ID, 0);
writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id, 0);
}
else
{
writer.WriteLine("{0} {1} {2} {3}", index, p1.ID, p2.ID,
checkmark.ss.boundary);
writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id,
checkmark.seg.boundary);
}
}
else
{
writer.WriteLine("{0} {1} {2} {3}", index, p1.ID, p2.ID,
writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id,
trisym.triangle == Mesh.dummytri ? "1" : "0");
}
}
else
{
// Edge number, indices of two endpoints.
writer.WriteLine("{0} {1} {2}", index, p1.ID, p2.ID);
writer.WriteLine("{0} {1} {2}", index, p1.id, p2.id);
}
index++;
@@ -314,7 +315,7 @@ namespace TriangleNet.IO
// Number of triangles, three neighbors per triangle.
writer.WriteLine("{0} 3", mesh.triangles.Count);
Mesh.dummytri.ID = -1;
Mesh.dummytri.id = -1;
foreach (var item in mesh.triangles.Values)
{
@@ -322,15 +323,15 @@ namespace TriangleNet.IO
tri.orient = 1;
tri.Sym(ref trisym);
n1 = trisym.triangle.ID;
n1 = trisym.triangle.id;
tri.orient = 2;
tri.Sym(ref trisym);
n2 = trisym.triangle.ID;
n2 = trisym.triangle.id;
tri.orient = 0;
tri.Sym(ref trisym);
n3 = trisym.triangle.ID;
n3 = trisym.triangle.id;
// Triangle number, neighboring triangle numbers.
writer.WriteLine("{0} {1} {2} {3}", i++, n1, n2, n3);
@@ -357,7 +358,7 @@ namespace TriangleNet.IO
{
Otri tri = default(Otri), trisym = default(Otri);
Vertex torg, tdest, tapex;
Point2 circumcenter;
Point circumcenter;
double xi = 0, eta = 0;
int p1, p2, index = 0;
@@ -374,10 +375,10 @@ namespace TriangleNet.IO
torg = tri.Org();
tdest = tri.Dest();
tapex = tri.Apex();
circumcenter = Primitives.FindCircumcenter(torg.pt, tdest.pt, tapex.pt, ref xi, ref eta, false);
circumcenter = Primitives.FindCircumcenter(torg, tdest, tapex, ref xi, ref eta, false);
// X and y coordinates.
writer.Write("{0} {1} {2}", index, circumcenter.X.ToString(nfi),
writer.Write("{0} {1} {2}", index, circumcenter.X.ToString(nfi),
circumcenter.Y.ToString(nfi));
for (int i = 0; i < mesh.nextras; i++)
@@ -390,7 +391,7 @@ namespace TriangleNet.IO
}
writer.WriteLine();
tri.triangle.ID = index++;
tri.triangle.id = index++;
}
@@ -411,10 +412,10 @@ namespace TriangleNet.IO
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
tri.Sym(ref trisym);
if ((tri.triangle.ID < trisym.triangle.ID) || (trisym.triangle == Mesh.dummytri))
if ((tri.triangle.id < trisym.triangle.id) || (trisym.triangle == Mesh.dummytri))
{
// Find the number of this triangle (and Voronoi vertex).
p1 = tri.triangle.ID;
p1 = tri.triangle.id;
if (trisym.triangle == Mesh.dummytri)
{
@@ -431,7 +432,7 @@ namespace TriangleNet.IO
else
{
// Find the number of the adjacent triangle (and Voronoi vertex).
p2 = trisym.triangle.ID;
p2 = trisym.triangle.id;
// Finite edge. Write indices of two endpoints.
writer.WriteLine("{0} {1} {2}", index, p1, p2);
}
@@ -480,7 +481,7 @@ namespace TriangleNet.IO
// The "0.0" is here because the OFF format uses 3D coordinates.
writer.WriteLine(" {0} {1} 0.0", p1[0].ToString(nfi), p1[1].ToString(nfi));
p1.ID = index++;
p1.id = index++;
}
}
@@ -495,7 +496,7 @@ namespace TriangleNet.IO
p3 = tri.Apex();
// The "3" means a three-vertex polygon.
writer.WriteLine(" 3 {0} {1} {2}", p1.ID, p2.ID, p3.ID);
writer.WriteLine(" 3 {0} {1} {2}", p1.id, p2.id, p3.id);
}
}
}
+107
View File
@@ -0,0 +1,107 @@
// -----------------------------------------------------------------------
// <copyright file="Triangle.cs" company="">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.IO
{
using System;
using TriangleNet.Geometry;
/// <summary>
/// Simple triangle class for input.
/// </summary>
public class InputTriangle : ITriangle
{
internal int[] vertices;
internal double[] attributes;
internal double area;
public InputTriangle(int p0, int p1, int p2)
{
this.vertices = new int[] { p0, p1, p2 };
}
#region Public properties
/// <summary>
/// Gets the triangle id.
/// </summary>
public int ID
{
get { return 0; }
}
/// <summary>
/// Gets the first corners vertex id.
/// </summary>
public int P0
{
get { return this.vertices[0]; }
}
/// <summary>
/// Gets the seconds corners vertex id.
/// </summary>
public int P1
{
get { return this.vertices[1]; }
}
/// <summary>
/// Gets the third corners vertex id.
/// </summary>
public int P2
{
get { return this.vertices[2]; }
}
/// <summary>
/// Gets the specified corners vertex id.
/// </summary>
public int this[int index]
{
get { return this.vertices[index]; }
}
public bool SupportsNeighbors
{
get { return false; }
}
public int N0
{
get { return -1; }
}
public int N1
{
get { return -1; }
}
public int N2
{
get { return -1; }
}
/// <summary>
/// Gets the triangle area constraint.
/// </summary>
public double Area
{
get { return -1; }
}
/// <summary>
/// Gets the triangle attributes.
/// </summary>
public double[] Attributes
{
get { return null; }
}
#endregion
}
}
-33
View File
@@ -1,33 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="TriangulateIO.cs">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/index.html
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.IO
{
/// <summary>
/// Stores the mesh data in- and output.
/// </summary>
public class MeshData
{
public double[][] Points; // In / out
public double[][] PointAttributes; // In / out
public int[] PointMarkers; // In / out
public int[][] Triangles; // In / out
public double[][] TriangleAttributes; // In / out
public double[] TriangleAreas; // In only
public int[][] Neighbors; // Out only
public int[][] Segments; // In / out
public int[] SegmentMarkers; // In / out
public double[][] Holes; // In / pointer to array copied out
public double[][] Regions; // In / pointer to array copied out
public int[][] Edges; // Out only
public int[] EdgeMarkers; // Out only
}
}
+6 -4
View File
@@ -7,16 +7,18 @@
namespace TriangleNet.IO
{
using TriangleNet.Geometry;
/// <summary>
/// Stores the voronoi data (output only).
/// </summary>
public class VoronoiData
{
public double[][] InputPoints;
public double[][] Points;
public int[][] Edges;
public Point[] InputPoints;
public Point[] Points;
public Edge[] Edges;
// Stores the direction for infinite voronoi edges
public double[][] Directions;
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ namespace TriangleNet.Log
using System.Text;
/// <summary>
/// A simple logger, which logs messages to a List<string>.
/// A simple logger, which logs messages to a List.
/// </summary>
/// <remarks>Using singleton pattern as proposed by Jon Skeet.
/// http://csharpindepth.com/Articles/General/Singleton.aspx
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+47 -132
View File
@@ -9,6 +9,8 @@ namespace TriangleNet
{
using System;
using TriangleNet.Data;
using TriangleNet.Geometry;
using TriangleNet.Tools;
/// <summary>
/// Provides some primitives regularly used in computational geometry.
@@ -84,14 +86,14 @@ namespace TriangleNet
/// also a rough approximation of twice the signed area of the triangle defined
/// by the three points.
/// </summary>
/// <param name="pa"></param>
/// <param name="pb"></param>
/// <param name="pc"></param>
/// <param name="pa">Point a.</param>
/// <param name="pb">Point b.</param>
/// <param name="pc">Point c.</param>
/// <returns>Return a positive value if the points pa, pb, and pc occur in
/// counterclockwise order; a negative value if they occur in clockwise order;
/// and zero if they are collinear.</returns>
/// <remarks>
/// Uses exact arithmetic if necessary to ensure a correct answer. The
/// Uses exact arithmetic if necessary to ensure a correct answer. The
/// result returned is the determinant of a matrix. This determinant is
/// computed adaptively, in the sense that exact arithmetic is used only to
/// the degree it is needed to ensure that the returned value has the
@@ -100,15 +102,15 @@ namespace TriangleNet
///
/// See Robust Predicates paper for details.
/// </remarks>
public static double CounterClockwise(Point2 pa, Point2 pb, Point2 pc)
public static double CounterClockwise(Point pa, Point pb, Point pc)
{
double detleft, detright, det;
double detsum, errbound;
Statistic.CounterClockwiseCount++;
detleft = (pa.X - pc.X) * (pb.Y - pc.Y);
detright = (pa.Y - pc.Y) * (pb.X - pc.X);
detleft = (pa.x - pc.x) * (pb.y - pc.y);
detright = (pa.y - pc.y) * (pb.x - pc.x);
det = detleft - detright;
if (Behavior.NoExact)
@@ -161,10 +163,10 @@ namespace TriangleNet
/// points pa, pb, and pc must be in counterclockwise order, or the sign of the result
/// will be reversed.
/// </summary>
/// <param name="pa"></param>
/// <param name="pb"></param>
/// <param name="pc"></param>
/// <param name="pd"></param>
/// <param name="pa">Point a.</param>
/// <param name="pb">Point b.</param>
/// <param name="pc">Point c.</param>
/// <param name="pd">Point d.</param>
/// <returns>Return a positive value if the point pd lies inside the circle passing through
/// pa, pb, and pc; a negative value if it lies outside; and zero if the four points
/// are cocircular.</returns>
@@ -178,7 +180,7 @@ namespace TriangleNet
///
/// See Robust Predicates paper for details.
/// </remarks>
public static double InCircle(Point2 pa, Point2 pb, Point2 pc, Point2 pd)
public static double InCircle(Point pa, Point pb, Point pc, Point pd)
{
double adx, bdx, cdx, ady, bdy, cdy;
double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
@@ -188,12 +190,12 @@ namespace TriangleNet
Statistic.InCircleCount++;
adx = pa.X - pd.X;
bdx = pb.X - pd.X;
cdx = pc.X - pd.X;
ady = pa.Y - pd.Y;
bdy = pb.Y - pd.Y;
cdy = pc.Y - pd.Y;
adx = pa.x - pd.x;
bdx = pb.x - pd.x;
cdx = pc.x - pd.x;
ady = pa.y - pd.y;
bdy = pb.y - pd.y;
cdy = pc.y - pd.y;
bdxcdy = bdx * cdy;
cdxbdy = cdx * bdy;
@@ -239,19 +241,14 @@ namespace TriangleNet
/// pc must be in counterclockwise order, or the sign of the result will be
/// reversed.
/// </summary>
/// <param name="pa"></param>
/// <param name="pb"></param>
/// <param name="pc"></param>
/// <param name="pd"></param>
/// <returns></returns>
/// <remarks>
/// If the -w switch is used, the points are lifted onto the parabolic
/// lifting map, then they are dropped according to their weights, then the
/// 3D orientation test is applied. If the -W switch is used, the points'
/// heights are already provided, so the 3D orientation test is applied
/// directly. If neither switch is used, the incircle test is applied.
/// </remarks>
public static double NonRegular(Point2 pa, Point2 pb, Point2 pc, Point2 pd)
/// <param name="pa">Point a.</param>
/// <param name="pb">Point b.</param>
/// <param name="pc">Point c.</param>
/// <param name="pd">Point d.</param>
/// <returns>Return a positive value if the point pd lies inside the circle passing through
/// pa, pb, and pc; a negative value if it lies outside; and zero if the four points
/// are cocircular.</returns>
public static double NonRegular(Point pa, Point pb, Point pc, Point pd)
{
return InCircle(pa, pb, pc, pd);
}
@@ -259,13 +256,13 @@ namespace TriangleNet
/// <summary>
/// Find the circumcenter of a triangle.
/// </summary>
/// <param name="torg"></param>
/// <param name="tdest"></param>
/// <param name="tapex"></param>
/// <param name="circumcenter"></param>
/// <param name="xi"></param>
/// <param name="eta"></param>
/// <param name="offcenter"></param>
/// <param name="torg">Triangle point.</param>
/// <param name="tdest">Triangle point.</param>
/// <param name="tapex">Triangle point.</param>
/// <param name="xi">Relative coordinate of new location.</param>
/// <param name="eta">Relative coordinate of new location.</param>
/// <param name="offcenter">Use off-center for new location.</param>
/// <returns>Coordinates of the circumcenter (or off-center)</returns>
/// <remarks>
/// The result is returned both in terms of x-y coordinates and xi-eta
/// (barycentric) coordinates. The xi-eta coordinate system is defined in
@@ -275,7 +272,7 @@ namespace TriangleNet
/// This procedure also returns the square of the length of the triangle's
/// shortest edge.
/// </remarks>
public static Point2 FindCircumcenter(Point2 torg, Point2 tdest, Point2 tapex,
public static Point FindCircumcenter(Point torg, Point tdest, Point tapex,
ref double xi, ref double eta, bool offcenter)
{
double xdo, ydo, xao, yao;
@@ -286,14 +283,14 @@ namespace TriangleNet
Statistic.CircumcenterCount++;
// Compute the circumcenter of the triangle.
xdo = tdest.X - torg.X;
ydo = tdest.Y - torg.Y;
xao = tapex.X - torg.X;
yao = tapex.Y - torg.Y;
xdo = tdest.x - torg.x;
ydo = tdest.y - torg.y;
xao = tapex.x - torg.x;
yao = tapex.y - torg.y;
dodist = xdo * xdo + ydo * ydo;
aodist = xao * xao + yao * yao;
dadist = (tdest.X - tapex.X) * (tdest.X - tapex.X) +
(tdest.Y - tapex.Y) * (tdest.Y - tapex.Y);
dadist = (tdest.x - tapex.x) * (tdest.x - tapex.x) +
(tdest.y - tapex.y) * (tdest.y - tapex.y);
if (Behavior.NoExact)
{
denominator = 0.5 / (xdo * yao - xao * ydo);
@@ -350,10 +347,10 @@ namespace TriangleNet
{
if (offcenter && (Behavior.Offconstant > 0.0))
{
dxoff = 0.5 * (tapex.X - tdest.X) -
Behavior.Offconstant * (tapex.Y - tdest.Y);
dyoff = 0.5 * (tapex.Y - tdest.Y) +
Behavior.Offconstant * (tapex.X - tdest.X);
dxoff = 0.5 * (tapex.x - tdest.x) -
Behavior.Offconstant * (tapex.y - tdest.y);
dyoff = 0.5 * (tapex.y - tdest.y) +
Behavior.Offconstant * (tapex.x - tdest.x);
// If the off-center is closer to the destination than the
// circumcenter, use the off-center instead.
if (dxoff * dxoff + dyoff * dyoff <
@@ -365,7 +362,7 @@ namespace TriangleNet
}
}
Point2 circumcenter = new Point2(torg.X + dx, torg.Y + dy);
Point circumcenter = new Point(torg.x + dx, torg.y + dy);
// To interpolate vertex attributes for the new vertex inserted at
// the circumcenter, define a coordinate system with a xi-axis,
@@ -377,87 +374,5 @@ namespace TriangleNet
return circumcenter;
}
/*
/// <summary>
/// Return a positive value if the point pd lies below the plane passing
/// through pa, pb, and pc; "below" is defined so that pa, pb, and pc appear
/// in counterclockwise order when viewed from above the plane. The result is
/// also a rough approximation of six times the signed volume of the
/// tetrahedron defined by the four points.
/// </summary>
/// <param name="pa"></param>
/// <param name="pb"></param>
/// <param name="pc"></param>
/// <param name="pd"></param>
/// <param name="aheight"></param>
/// <param name="bheight"></param>
/// <param name="cheight"></param>
/// <param name="dheight"></param>
/// <returns>Return a positive value if the point pd lies below the plane
/// passing through pa, pb, and pc. Returns a negative value if pd lies above
/// the plane. Returns zero if the points are coplanar.</returns>
/// <remarks>
/// Uses exact arithmetic if necessary to ensure a correct answer. The
/// result returned is the determinant of a matrix. This determinant is
/// computed adaptively, in the sense that exact arithmetic is used only to
/// the degree it is needed to ensure that the returned value has the
/// correct sign. Hence, this function is usually quite fast, but will run
/// more slowly when the input points are coplanar or nearly so.
///
/// See my Robust Predicates paper for details.
/// </remarks>
public static double Orient3d2(Point2 pa, Point2 pb, Point2 pc, Point2 pd,
double aheight, double bheight, double cheight, double dheight)
{
double adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
double det;
double permanent, errbound;
Statistic.Orient3dCount++;
adx = pa.X - pd.X;
bdx = pb.X - pd.X;
cdx = pc.X - pd.X;
ady = pa.Y - pd.Y;
bdy = pb.Y - pd.Y;
cdy = pc.Y - pd.Y;
adheight = aheight - dheight;
bdheight = bheight - dheight;
cdheight = cheight - dheight;
bdxcdy = bdx * cdy;
cdxbdy = cdx * bdy;
cdxady = cdx * ady;
adxcdy = adx * cdy;
adxbdy = adx * bdy;
bdxady = bdx * ady;
det = adheight * (bdxcdy - cdxbdy)
+ bdheight * (cdxady - adxcdy)
+ cdheight * (adxbdy - bdxady);
if (Behavior.NoExact)
{
return det;
}
permanent = (Math.Abs(bdxcdy) + Math.Abs(cdxbdy)) * Math.Abs(adheight)
+ (Math.Abs(cdxady) + Math.Abs(adxcdy)) * Math.Abs(bdheight)
+ (Math.Abs(adxbdy) + Math.Abs(bdxady)) * Math.Abs(cdheight);
errbound = o3derrboundA * permanent;
if ((det > errbound) || (-det > errbound))
{
return det;
}
throw new Exception();
//return orient3dadapt(pa, pb, pc, pd, aheight, bheight, cheight, dheight, permanent);
}
*/
}
}
+94 -88
View File
@@ -11,6 +11,7 @@ namespace TriangleNet
using System.Collections.Generic;
using TriangleNet.Data;
using TriangleNet.Log;
using TriangleNet.Geometry;
/// <summary>
/// Provides methods for mesh quality enforcement and testing.
@@ -20,7 +21,9 @@ namespace TriangleNet
Queue<BadSubseg> badsubsegs;
BadTriQueue queue;
Mesh mesh;
Func<Vertex, Vertex, Vertex, double, bool> userTest;
// Not used at the moment
Func<Point, Point, Point, double, bool> userTest;
ILog<SimpleLogItem> logger;
@@ -34,9 +37,9 @@ namespace TriangleNet
}
/// <summary>
/// Deallocate space for a bad subsegment, marking it dead.
/// Add a bad subsegment to the queue.
/// </summary>
/// <param name="dyingseg"></param>
/// <param name="badseg">Bad subsegment.</param>
public void AddBadSubseg(BadSubseg badseg)
{
badsubsegs.Enqueue(badseg);
@@ -75,8 +78,10 @@ namespace TriangleNet
{ // Only test for inversion once.
// Test if the triangle is flat or inverted.
triapex = tri.Apex();
if (Primitives.CounterClockwise(triorg.pt, tridest.pt, triapex.pt) <= 0.0)
if (Primitives.CounterClockwise(triorg, tridest, triapex) <= 0.0)
{
logger.Warning("Triangle is flat or inverted.",
"Quality.CheckMesh()");
horrors++;
}
}
@@ -183,16 +188,17 @@ namespace TriangleNet
// If a subsegment separates the triangles, then the edge is
// constrained, so no local Delaunay test should be done.
triangleloop.SegPivot(ref opposubseg);
if (opposubseg.ss != Mesh.dummysub)
if (opposubseg.seg != Mesh.dummysub)
{
shouldbedelaunay = false;
}
}
if (shouldbedelaunay)
{
if (Primitives.NonRegular(triorg.pt, tridest.pt, triapex.pt, oppoapex.pt) > 0.0)
if (Primitives.NonRegular(triorg, tridest, triapex, oppoapex) > 0.0)
{
logger.Warning("Non-regular pair of triangles found.", "Quality.CheckDelaunay()");
string coords = triorg.ToString() + " " + tridest.ToString();
logger.Warning("Non-regular pair of triangles found. " + coords, "Quality.CheckDelaunay()");
horrors++;
}
}
@@ -223,7 +229,7 @@ namespace TriangleNet
/// <summary>
/// Check a subsegment to see if it is encroached; add it to the list if it is.
/// </summary>
/// <param name="testsubseg"></param>
/// <param name="testsubseg">The subsegment to check.</param>
/// <returns>Returns a nonzero value if the subsegment is encroached.</returns>
/// <remarks>
/// A subsegment is encroached if there is a vertex in its diametral lens.
@@ -268,17 +274,17 @@ namespace TriangleNet
// of two sides of the triangle is used to check whether the angle
// at the apex is greater than (180 - 2 'minangle') degrees (for
// lenses; 90 degrees for diametral circles).
dotproduct = (eorg.pt.X - eapex.pt.X) * (edest.pt.X - eapex.pt.X) +
(eorg.pt.Y - eapex.pt.Y) * (edest.pt.Y - eapex.pt.Y);
dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) +
(eorg.y - eapex.y) * (edest.y - eapex.y);
if (dotproduct < 0.0)
{
if (Behavior.ConformDel ||
(dotproduct * dotproduct >=
(2.0 * Behavior.GoodAngle - 1.0) * (2.0 * Behavior.GoodAngle - 1.0) *
((eorg.pt.X - eapex.pt.X) * (eorg.pt.X - eapex.pt.X) +
(eorg.pt.Y - eapex.pt.Y) * (eorg.pt.Y - eapex.pt.Y)) *
((edest.pt.X - eapex.pt.X) * (edest.pt.X - eapex.pt.X) +
(edest.pt.Y - eapex.pt.Y) * (edest.pt.Y - eapex.pt.Y))))
((eorg.x - eapex.x) * (eorg.x - eapex.x) +
(eorg.y - eapex.y) * (eorg.y - eapex.y)) *
((edest.x - eapex.x) * (edest.x - eapex.x) +
(edest.y - eapex.y) * (edest.y - eapex.y))))
{
encroached = 1;
}
@@ -295,17 +301,17 @@ namespace TriangleNet
eapex = neighbortri.Apex();
// Check whether the apex is in the diametral lens of the subsegment
// (or the diametral circle, if 'conformdel' is set).
dotproduct = (eorg.pt.X - eapex.pt.X) * (edest.pt.X - eapex.pt.X) +
(eorg.pt.Y - eapex.pt.Y) * (edest.pt.Y - eapex.pt.Y);
dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) +
(eorg.y - eapex.y) * (edest.y - eapex.y);
if (dotproduct < 0.0)
{
if (Behavior.ConformDel ||
(dotproduct * dotproduct >=
(2.0 * Behavior.GoodAngle - 1.0) * (2.0 * Behavior.GoodAngle - 1.0) *
((eorg.pt.X - eapex.pt.X) * (eorg.pt.X - eapex.pt.X) +
(eorg.pt.Y - eapex.pt.Y) * (eorg.pt.Y - eapex.pt.Y)) *
((edest.pt.X - eapex.pt.X) * (edest.pt.X - eapex.pt.X) +
(edest.pt.Y - eapex.pt.Y) * (edest.pt.Y - eapex.pt.Y))))
((eorg.x - eapex.x) * (eorg.x - eapex.x) +
(eorg.y - eapex.y) * (eorg.y - eapex.y)) *
((edest.x - eapex.x) * (edest.x - eapex.x) +
(edest.y - eapex.y) * (edest.y - eapex.y))))
{
encroached += 2;
}
@@ -329,7 +335,7 @@ namespace TriangleNet
encroachedseg.subsegorg = edest;
encroachedseg.subsegdest = eorg;
}
badsubsegs.Enqueue(encroachedseg);
}
@@ -365,12 +371,12 @@ namespace TriangleNet
torg = testtri.Org();
tdest = testtri.Dest();
tapex = testtri.Apex();
dxod = torg.pt.X - tdest.pt.X;
dyod = torg.pt.Y - tdest.pt.Y;
dxda = tdest.pt.X - tapex.pt.X;
dyda = tdest.pt.Y - tapex.pt.Y;
dxao = tapex.pt.X - torg.pt.X;
dyao = tapex.pt.Y - torg.pt.Y;
dxod = torg.x - tdest.x;
dyod = torg.y - tdest.y;
dxda = tdest.x - tapex.x;
dyda = tdest.y - tapex.y;
dxao = tapex.x - torg.x;
dyao = tapex.y - torg.y;
dxod2 = dxod * dxod;
dyod2 = dyod * dyod;
dxda2 = dxda * dxda;
@@ -487,7 +493,7 @@ namespace TriangleNet
// Check if both points lie in a common segment. If they do, the
// skinny triangle is enqueued to be split as usual.
tri1.SegPivot(ref testsub);
if (testsub.ss == Mesh.dummysub)
if (testsub.seg == Mesh.dummysub)
{
// No common segment. Find a subsegment that contains 'torg'.
tri1.Copy(ref tri2);
@@ -495,7 +501,7 @@ namespace TriangleNet
{
tri1.OprevSelf();
tri1.SegPivot(ref testsub);
} while (testsub.ss == Mesh.dummysub);
} while (testsub.seg == Mesh.dummysub);
// Find the endpoints of the containing segment.
org1 = testsub.SegOrg();
dest1 = testsub.SegDest();
@@ -504,17 +510,17 @@ namespace TriangleNet
{
tri2.DnextSelf();
tri2.SegPivot(ref testsub);
} while (testsub.ss == Mesh.dummysub);
} while (testsub.seg == Mesh.dummysub);
// Find the endpoints of the containing segment.
org2 = testsub.SegOrg();
dest2 = testsub.SegDest();
// Check if the two containing segments have an endpoint in common.
joinvertex = null;
if ((dest1.pt.X == org2.pt.X) && (dest1.pt.Y == org2.pt.Y))
if ((dest1.x == org2.x) && (dest1.y == org2.y))
{
joinvertex = dest1;
}
else if ((org1.pt.X == dest2.pt.X) && (org1.pt.Y == dest2.pt.Y))
else if ((org1.x == dest2.x) && (org1.y == dest2.y))
{
joinvertex = org1;
}
@@ -522,10 +528,10 @@ namespace TriangleNet
{
// Compute the distance from the common endpoint (of the two
// segments) to each of the endpoints of the shortest edge.
dist1 = ((base1.pt.X - joinvertex.pt.X) * (base1.pt.X - joinvertex.pt.X) +
(base1.pt.Y - joinvertex.pt.Y) * (base1.pt.Y - joinvertex.pt.Y));
dist2 = ((base2.pt.X - joinvertex.pt.X) * (base2.pt.X - joinvertex.pt.X) +
(base2.pt.Y - joinvertex.pt.Y) * (base2.pt.Y - joinvertex.pt.Y));
dist1 = ((base1.x - joinvertex.x) * (base1.x - joinvertex.x) +
(base1.y - joinvertex.y) * (base1.y - joinvertex.y));
dist2 = ((base2.x - joinvertex.x) * (base2.x - joinvertex.x) +
(base2.y - joinvertex.y) * (base2.y - joinvertex.y));
// If the two distances are equal, don't split the triangle.
if ((dist1 < 1.001 * dist2) && (dist1 > 0.999 * dist2))
{
@@ -549,19 +555,16 @@ namespace TriangleNet
/// Traverse the entire list of subsegments, and check each to see if it
/// is encroached. If so, add it to the list.
/// </summary>
void TallyEncs()
private void TallyEncs()
{
Osub subsegloop = default(Osub);
int dummy;
subsegloop.orient = 0;
subsegloop.ssorient = 0;
foreach (var s in mesh.subsegs.Values)
foreach (var seg in mesh.subsegs.Values)
{
subsegloop.ss = s;
subsegloop.seg = seg;
// If the segment is encroached, add it to the list.
dummy = CheckSeg4Encroach(ref subsegloop);
//subsegloop.ss = subsegtraverse(m);
CheckSeg4Encroach(ref subsegloop);
}
}
@@ -576,7 +579,7 @@ namespace TriangleNet
/// vertex at or near its midpoint. Newly inserted vertices may encroach
/// upon other subsegments; these are also repaired.
/// </remarks>
void SplitEncSegs(bool triflaws)
private void SplitEncSegs(bool triflaws)
{
Otri enctri = default(Otri);
Otri testtri = default(Otri);
@@ -610,7 +613,7 @@ namespace TriangleNet
// when it was determined to be encroached. If the segment was
// enqueued multiple times (because several newly inserted
// vertices encroached it), it may have already been split.
if (!Osub.IsDead(currentenc.ss) && (eorg == seg.subsegorg) && (edest == seg.subsegdest))
if (!Osub.IsDead(currentenc.seg) && (eorg == seg.subsegorg) && (edest == seg.subsegdest))
{
// To decide where to split a segment, we need to know if the
// segment shares an endpoint with an adjacent segment.
@@ -631,11 +634,11 @@ namespace TriangleNet
currentenc.TriPivot(ref enctri);
enctri.Lnext(ref testtri);
testtri.SegPivot(ref testsh);
acuteorg = testsh.ss != Mesh.dummysub;
acuteorg = testsh.seg != Mesh.dummysub;
// Is the destination shared with another segment?
testtri.LnextSelf();
testtri.SegPivot(ref testsh);
acutedest = testsh.ss != Mesh.dummysub;
acutedest = testsh.seg != Mesh.dummysub;
// If we're using Chew's algorithm (rather than Ruppert's)
// to define encroachment, delete free vertices from the
@@ -644,8 +647,8 @@ namespace TriangleNet
{
eapex = enctri.Apex();
while ((eapex.type == VertexType.FreeVertex) &&
((eorg.pt.X - eapex.pt.X) * (edest.pt.X - eapex.pt.X) +
(eorg.pt.Y - eapex.pt.Y) * (edest.pt.Y - eapex.pt.Y) < 0.0))
((eorg.x - eapex.x) * (edest.x - eapex.x) +
(eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0))
{
mesh.DeleteVertex(ref testtri);
currentenc.TriPivot(ref enctri);
@@ -661,12 +664,12 @@ namespace TriangleNet
// Is the destination shared with another segment?
testtri.LnextSelf();
testtri.SegPivot(ref testsh);
acutedest2 = testsh.ss != Mesh.dummysub;
acutedest2 = testsh.seg != Mesh.dummysub;
acutedest = acutedest || acutedest2;
// Is the origin shared with another segment?
testtri.LnextSelf();
testtri.SegPivot(ref testsh);
acuteorg2 = testsh.ss != Mesh.dummysub;
acuteorg2 = testsh.seg != Mesh.dummysub;
acuteorg = acuteorg || acuteorg2;
// Delete free vertices from the subsegment's diametral circle.
@@ -674,8 +677,8 @@ namespace TriangleNet
{
eapex = testtri.Org();
while ((eapex.type == VertexType.FreeVertex) &&
((eorg.pt.X - eapex.pt.X) * (edest.pt.X - eapex.pt.X) +
(eorg.pt.Y - eapex.pt.Y) * (edest.pt.Y - eapex.pt.Y) < 0.0))
((eorg.x - eapex.x) * (edest.x - eapex.x) +
(eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0))
{
mesh.DeleteVertex(ref testtri);
enctri.Sym(ref testtri);
@@ -689,8 +692,8 @@ namespace TriangleNet
// with another adjacent segment.
if (acuteorg || acutedest)
{
segmentlength = Math.Sqrt((edest.pt.X - eorg.pt.X) * (edest.pt.X - eorg.pt.X) +
(edest.pt.Y - eorg.pt.Y) * (edest.pt.Y - eorg.pt.Y));
segmentlength = Math.Sqrt((edest.x - eorg.x) * (edest.x - eorg.x) +
(edest.y - eorg.y) * (edest.y - eorg.y));
// Find the power of two that most evenly splits the segment.
// The worst case is a 2:1 ratio between subsegment lengths.
nearestpoweroftwo = 1.0;
@@ -717,8 +720,12 @@ namespace TriangleNet
}
// Create the new vertex.
newvertex = new Vertex(mesh.nextras);
mesh.vertices.Add(newvertex.Hash, newvertex);
newvertex = new Vertex(); // TODO: mesh.nextras
newvertex.hash = mesh.hash_vtx++;
newvertex.id = newvertex.hash;
mesh.vertices.Add(newvertex.hash, newvertex);
// Interpolate its coordinate and attributes.
for (int i = 0; i < mesh.nextras; i++)
@@ -727,25 +734,25 @@ namespace TriangleNet
+ split * (edest.attributes[i] - eorg.attributes[i]);
}
newvertex.pt.X = eorg.pt.X + split * (edest.pt.X - eorg.pt.X);
newvertex.pt.Y = eorg.pt.Y + split * (edest.pt.Y - eorg.pt.Y);
newvertex.x = eorg.x + split * (edest.x - eorg.x);
newvertex.y = eorg.y + split * (edest.y - eorg.y);
if (!Behavior.NoExact)
{
// Roundoff in the above calculation may yield a 'newvertex'
// that is not precisely collinear with 'eorg' and 'edest'.
// Improve collinearity by one step of iterative refinement.
multiplier = Primitives.CounterClockwise(eorg.pt, edest.pt, newvertex.pt);
divisor = ((eorg.pt.X - edest.pt.X) * (eorg.pt.X - edest.pt.X) +
(eorg.pt.Y - edest.pt.Y) * (eorg.pt.Y - edest.pt.Y));
multiplier = Primitives.CounterClockwise(eorg, edest, newvertex);
divisor = ((eorg.x - edest.x) * (eorg.x - edest.x) +
(eorg.y - edest.y) * (eorg.y - edest.y));
if ((multiplier != 0.0) && (divisor != 0.0))
{
multiplier = multiplier / divisor;
// Watch out for NANs.
if (!double.IsNaN(multiplier))
{
newvertex.pt.X += multiplier * (edest.pt.Y - eorg.pt.Y);
newvertex.pt.Y += multiplier * (eorg.pt.X - edest.pt.X);
newvertex.x += multiplier * (edest.y - eorg.y);
newvertex.y += multiplier * (eorg.x - edest.x);
}
}
}
@@ -754,8 +761,8 @@ namespace TriangleNet
newvertex.type = VertexType.SegmentVertex;
// Check whether the new vertex lies on an endpoint.
if (((newvertex.pt.X == eorg.pt.X) && (newvertex.pt.Y == eorg.pt.Y)) ||
((newvertex.pt.X == edest.pt.X) && (newvertex.pt.Y == edest.pt.Y)))
if (((newvertex.x == eorg.x) && (newvertex.y == eorg.y)) ||
((newvertex.x == edest.x) && (newvertex.y == edest.y)))
{
logger.Error("Ran out of precision: I attempted to split a"
@@ -791,15 +798,14 @@ namespace TriangleNet
/// <summary>
/// Test every triangle in the mesh for quality measures.
/// </summary>
void TallyFaces()
private void TallyFaces()
{
Otri triangleloop = default(Otri);
triangleloop.orient = 0;
foreach (var t in mesh.triangles.Values)
foreach (var tri in mesh.triangles.Values)
{
triangleloop.triangle = t;
triangleloop.triangle = tri;
// If the triangle is bad, enqueue it.
TestTriangle(ref triangleloop);
@@ -811,7 +817,7 @@ namespace TriangleNet
/// the newly inserted vertex if it encroaches upon a segment.
/// </summary>
/// <param name="badtri"></param>
void SplitTriangle(BadTriangle badtri)
private void SplitTriangle(BadTriangle badtri)
{
Otri badotri = default(Otri);
Vertex borg, bdest, bapex;
@@ -819,7 +825,6 @@ namespace TriangleNet
double xi = 0, eta = 0;
InsertVertexResult success;
bool errorflag;
int i;
badotri = badtri.poortri;
borg = badotri.Org();
@@ -834,7 +839,7 @@ namespace TriangleNet
{
errorflag = false;
// Create a new vertex at the triangle's circumcenter.
newvertex = new Vertex(mesh.nextras);
newvertex = new Vertex(); // TODO: mesh.nextras
// Using the original (simpler) Steiner point location method
// for mesh refinement.
@@ -842,7 +847,9 @@ namespace TriangleNet
// reset VertexType?
if (Behavior.FixedArea || Behavior.VarArea)
{
newvertex.pt = Primitives.FindCircumcenter(borg.pt, bdest.pt, bapex.pt, ref xi, ref eta, true);
Point tmp = Primitives.FindCircumcenter(borg, bdest, bapex, ref xi, ref eta, true);
newvertex.x = tmp.x;
newvertex.y = tmp.y;
}
else
{
@@ -850,9 +857,9 @@ namespace TriangleNet
}
// Check whether the new vertex lies on a triangle vertex.
if (((newvertex.pt.X == borg.pt.X) && (newvertex.pt.Y == borg.pt.Y)) ||
((newvertex.pt.X == bdest.pt.X) && (newvertex.pt.Y == bdest.pt.Y)) ||
((newvertex.pt.X == bapex.pt.X) && (newvertex.pt.Y == bapex.pt.Y)))
if (((newvertex.x == borg.x) && (newvertex.y == borg.y)) ||
((newvertex.x == bdest.x) && (newvertex.y == bdest.y)) ||
((newvertex.x == bapex.x) && (newvertex.y == bapex.y)))
{
if (Behavior.Verbose)
{
@@ -862,7 +869,7 @@ namespace TriangleNet
}
else
{
for (i = 0; i < mesh.nextras; i++)
for (int i = 0; i < mesh.nextras; i++)
{
// Interpolate the vertex attributes at the circumcenter.
newvertex.attributes[i] = borg.attributes[i]
@@ -871,8 +878,8 @@ namespace TriangleNet
}
// The new vertex must be in the interior, and therefore is a
// free vertex with a marker of zero.
newvertex.mark = 0;
newvertex.type = VertexType.FreeVertex;
//newvertex.mark = 0;
// Ensure that the handle 'badotri' does not represent the longest
// edge of the triangle. This ensures that the circumcenter must
@@ -893,7 +900,10 @@ namespace TriangleNet
if (success == InsertVertexResult.Successful)
{
mesh.vertices.Add(newvertex.Hash, newvertex);
newvertex.hash = mesh.hash_vtx++;
newvertex.id = newvertex.hash;
mesh.vertices.Add(newvertex.hash, newvertex);
if (mesh.steinerleft > 0)
{
@@ -971,11 +981,6 @@ namespace TriangleNet
// Record any new bad triangles that result.
SplitEncSegs(true);
}
else
{
// Return the bad triangle to the pool.
//queue.badtriangles.Remove(badtri);
}
}
}
@@ -986,13 +991,14 @@ namespace TriangleNet
// Might we have run out of Steiner points too soon?
if (Behavior.Verbose && Behavior.ConformDel && (badsubsegs.Count > 0) && (mesh.steinerleft == 0))
{
logger.Warning("I ran out of Steiner points, but the mesh has encroached subsegments, "
+ "and therefore might not be truly Delaunay. If the Delaunay property is important "
+ "to you, try increasing the number of Steiner points",
+ "to you, try increasing the number of Steiner points",
"Quality.EnforceQuality()");
}
}
#endregion
}
}
@@ -5,11 +5,12 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet
namespace TriangleNet.Tools
{
using System;
using System.Text;
using TriangleNet.Data;
using TriangleNet.Geometry;
/// <summary>
/// Gather mesh statistics.
@@ -74,7 +75,7 @@ namespace TriangleNet
/// <summary>
/// Gets the shortest altitude.
/// </summary>
public double ShortestAltitude { get { return minAspect; } }
public double ShortestAltitude { get { return minAspect; } }
double maxAspect = 0;
/// <summary>
@@ -224,8 +225,8 @@ namespace TriangleNet
{
j = plus1Mod3[i];
k = minus1Mod3[i];
dx[i] = p[j].pt.X - p[k].pt.X;
dy[i] = p[j].pt.Y - p[k].pt.Y;
dx[i] = p[j].x - p[k].x;
dy[i] = p[j].y - p[k].y;
edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
if (edgelength[i] > trilongest2)
{
@@ -234,8 +235,8 @@ namespace TriangleNet
}
//triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
triarea = Math.Abs((p[2].pt.X - p[0].pt.X) * (p[1].pt.Y - p[0].pt.Y) -
(p[1].pt.X - p[0].pt.X) * (p[2].pt.Y - p[0].pt.Y)) / 2.0;
triarea = Math.Abs((p[2].x - p[0].x) * (p[1].y - p[0].y) -
(p[1].x - p[0].x) * (p[2].y - p[0].y)) / 2.0;
triminaltitude2 = triarea * triarea / trilongest2;
@@ -272,7 +273,7 @@ namespace TriangleNet
intBoundaryEdges = mesh.subsegs.Count - (int)mesh.hullsize;
constrainedEdges = mesh.subsegs.Count;
Point2[] p = new Point2[3];
Point[] p = new Point[3];
int k1, k2;
int degreeStep;
@@ -310,7 +311,7 @@ namespace TriangleNet
angleTable[i] = 0;
}
minAspect = mesh.xmax - mesh.xmin + mesh.ymax - mesh.ymin;
minAspect = mesh.bounds.Width + mesh.bounds.Height;
minAspect = minAspect * minAspect;
maxAspect = 0.0;
minEdge = minAspect;
@@ -330,9 +331,9 @@ namespace TriangleNet
triMinAngle = 0; // Min angle: 0 < a < 60 degress
triMaxAngle = 1; // Max angle: 60 < a < 180 degress
p[0] = tri.vertices[0].pt;
p[1] = tri.vertices[1].pt;
p[2] = tri.vertices[2].pt;
p[0] = tri.vertices[0];
p[1] = tri.vertices[1];
p[2] = tri.vertices[2];
triLongest2 = 0.0;
@@ -476,7 +477,7 @@ namespace TriangleNet
{
maxAngles[sampleDegrees - degreeStep - 1]++;
}
acuteBiggestTri = true;
}
+5 -8
View File
@@ -47,25 +47,23 @@
<Compile Include="Carver.cs" />
<Compile Include="Data\BadSubseg.cs" />
<Compile Include="Data\BadTriangle.cs" />
<Compile Include="Data\FlipStacker.cs" />
<Compile Include="Data\Osub.cs" />
<Compile Include="Data\Otri.cs" />
<Compile Include="Data\Point2.cs" />
<Compile Include="Data\Region.cs" />
<Compile Include="Data\SplayNode.cs" />
<Compile Include="Data\Subseg.cs" />
<Compile Include="Data\SweepEvent.cs" />
<Compile Include="Data\Segment.cs" />
<Compile Include="Data\Triangle.cs" />
<Compile Include="Data\Vertex.cs" />
<Compile Include="Algorithm\Dwyer.cs" />
<Compile Include="Geometry\BoundingBox.cs" />
<Compile Include="Geometry\Edge.cs" />
<Compile Include="Geometry\EdgeEnumerator.cs" />
<Compile Include="Geometry\InputGeometry.cs" />
<Compile Include="Geometry\ITriangle.cs" />
<Compile Include="Geometry\Point.cs" />
<Compile Include="Geometry\RegionPointer.cs" />
<Compile Include="IO\DataReader.cs" />
<Compile Include="IO\DebugWriter.cs" />
<Compile Include="IO\FileWriter.cs" />
<Compile Include="IO\InputTriangle.cs" />
<Compile Include="IO\VoronoiData.cs" />
<Compile Include="IO\DataWriter.cs" />
<Compile Include="IO\FileReader.cs" />
@@ -82,9 +80,8 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Sampler.cs" />
<Compile Include="Smoothing\ISmoother.cs" />
<Compile Include="Statistic.cs" />
<Compile Include="Tools\Statistic.cs" />
<Compile Include="Algorithm\SweepLine.cs" />
<Compile Include="IO\MeshData.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />