diff --git a/Triangle.NET/TestApp/Controls/AngleHistogram.cs b/Triangle.NET/TestApp/Controls/AngleHistogram.cs index 465be1e..1dac085 100644 --- a/Triangle.NET/TestApp/Controls/AngleHistogram.cs +++ b/Triangle.NET/TestApp/Controls/AngleHistogram.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -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; /// - /// TODO: Update summary. + /// Displays an angle histogram. /// + /// + /// 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) + /// 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); } } } diff --git a/Triangle.NET/TestApp/Controls/ColorScheme.cs b/Triangle.NET/TestApp/Controls/ColorScheme.cs new file mode 100644 index 0000000..f00428d --- /dev/null +++ b/Triangle.NET/TestApp/Controls/ColorScheme.cs @@ -0,0 +1,40 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace MeshExplorer.Controls +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Drawing; +using System.Drawing.Drawing2D; + + /// + /// TODO: Update 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); + } +} diff --git a/Triangle.NET/TestApp/Controls/DarkButton.cs b/Triangle.NET/TestApp/Controls/DarkButton.cs index 153fb79..ac8c5e6 100644 --- a/Triangle.NET/TestApp/Controls/DarkButton.cs +++ b/Triangle.NET/TestApp/Controls/DarkButton.cs @@ -1,6 +1,6 @@  -namespace TestApp.Controls +namespace MeshExplorer.Controls { using System; using System.Collections.Generic; diff --git a/Triangle.NET/TestApp/Controls/DarkCheckBox.cs b/Triangle.NET/TestApp/Controls/DarkCheckBox.cs index f177297..00942b2 100644 --- a/Triangle.NET/TestApp/Controls/DarkCheckBox.cs +++ b/Triangle.NET/TestApp/Controls/DarkCheckBox.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -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); diff --git a/Triangle.NET/TestApp/Controls/DarkListBox.cs b/Triangle.NET/TestApp/Controls/DarkListBox.cs new file mode 100644 index 0000000..5bc1918 --- /dev/null +++ b/Triangle.NET/TestApp/Controls/DarkListBox.cs @@ -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); + } + } + } +} diff --git a/Triangle.NET/TestApp/Controls/DarkSlider.cs b/Triangle.NET/TestApp/Controls/DarkSlider.cs new file mode 100644 index 0000000..46af015 --- /dev/null +++ b/Triangle.NET/TestApp/Controls/DarkSlider.cs @@ -0,0 +1,430 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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 + + /// + /// Encapsulates control that visualy displays certain integer value and allows user to change + /// it within desired range. It imitates as far as + /// mouse usage is concerned. + /// + public class DarkSlider : Control + { + #region Designer + + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + + #endregion + + #region Events + + /// + /// Fires when Slider position has changed + /// + public event EventHandler ValueChanging; + + private void OnValueChanging() + { + var evt = ValueChanging; + + if (evt != null) + { + evt(this, EventArgs.Empty); + } + } + + /// + /// Fires when Slider position has changed + /// + 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; + /// + /// Gets or sets the value of Slider. + /// + /// The 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; + /// + /// Gets or sets the minimum value. + /// + /// The minimum 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; + /// + /// Gets or sets the maximum value. + /// + /// The maximum 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; + /// + /// Gets or sets trackbar's small change. It affects how to behave when directional keys are pressed + /// + /// The small change 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 + + /// + /// Initializes a new instance of the class. + /// + 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 + + /// + /// Raises the event. + /// + /// A that contains the event data. + 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); + } + } + } + + /// + /// Draws the colorslider control using passed colors. + /// + /// The instance containing the event data. + /// The thumb outer color paint. + /// The thumb inner color paint. + /// The thumb pen color paint. + /// The bar outer color paint. + /// The bar inner color paint. + /// The bar pen color paint. + /// The elapsed outer color paint. + /// The elapsed inner color paint. + 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 + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnEnabledChanged(EventArgs e) + { + base.OnEnabledChanged(e); + Invalidate(); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + mouseInThumbRegion = false; + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + if (e.Button == MouseButtons.Left) + { + this.Capture = true; + OnValueChanging(); + OnMouseMove(e); + } + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + 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(); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + this.Capture = false; + mouseInThumbRegion = thumbRect.Contains(e.Location); + OnValueChanged(); + Invalidate(); + } + + #endregion + + #region Help routines + + /// + /// Sets the trackbar value so that it wont exceed allowed range. + /// + /// The value. + private void SetProperValue(int val) + { + if (val < barMinimum) Value = barMinimum; + else if (val > barMaximum) Value = barMaximum; + else Value = val; + } + + #endregion + } +} diff --git a/Triangle.NET/TestApp/Controls/DarkTabControl.cs b/Triangle.NET/TestApp/Controls/DarkTabControl.cs new file mode 100644 index 0000000..b191563 --- /dev/null +++ b/Triangle.NET/TestApp/Controls/DarkTabControl.cs @@ -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 description for FlatTabControl. + /// + public class DarkTabControl : System.Windows.Forms.TabControl + { + #region Designer + + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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); + } + } +} diff --git a/Triangle.NET/TestApp/Controls/DarkTextBox.cs b/Triangle.NET/TestApp/Controls/DarkTextBox.cs index b0f7ff5..f92cc78 100644 --- a/Triangle.NET/TestApp/Controls/DarkTextBox.cs +++ b/Triangle.NET/TestApp/Controls/DarkTextBox.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -namespace TestApp.Controls +namespace MeshExplorer.Controls { using System; using System.Collections.Generic; diff --git a/Triangle.NET/TestApp/Controls/DarkToolStripRenderer.cs b/Triangle.NET/TestApp/Controls/DarkToolStripRenderer.cs new file mode 100644 index 0000000..d55cae6 --- /dev/null +++ b/Triangle.NET/TestApp/Controls/DarkToolStripRenderer.cs @@ -0,0 +1,66 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace MeshExplorer.Controls +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Windows.Forms; + using System.Drawing; + + /// + /// TODO: Update 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); + } + } +} diff --git a/Triangle.NET/TestApp/Controls/MeshRenderer.cs b/Triangle.NET/TestApp/Controls/MeshRenderer.cs index 632f318..98b72b8 100644 --- a/Triangle.NET/TestApp/Controls/MeshRenderer.cs +++ b/Triangle.NET/TestApp/Controls/MeshRenderer.cs @@ -4,24 +4,24 @@ // // ----------------------------------------------------------------------- -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; /// /// Renders a mesh using GDI. /// - 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 diff --git a/Triangle.NET/TestApp/DarkMessageBox.cs b/Triangle.NET/TestApp/DarkMessageBox.cs new file mode 100644 index 0000000..4fc2a22 --- /dev/null +++ b/Triangle.NET/TestApp/DarkMessageBox.cs @@ -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 + + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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); + } + } +} diff --git a/Triangle.NET/TestApp/Examples.cs b/Triangle.NET/TestApp/Examples.cs index e175a52..9ce16a4 100644 --- a/Triangle.NET/TestApp/Examples.cs +++ b/Triangle.NET/TestApp/Examples.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -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; /// /// Code of the online examples. - /// /// 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); } + + /// + /// Drawing the Voronoi diagram. + /// + 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); + } + + /// + /// Smoothing a mesh. + /// + 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); + } + + /// + /// Smoothing a mesh. + /// + 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); + } } } diff --git a/Triangle.NET/TestApp/FormLog.Designer.cs b/Triangle.NET/TestApp/FormLog.Designer.cs new file mode 100644 index 0000000..0e3347e --- /dev/null +++ b/Triangle.NET/TestApp/FormLog.Designer.cs @@ -0,0 +1,89 @@ +namespace MeshExplorer +{ + partial class FormLog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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; + } +} \ No newline at end of file diff --git a/Triangle.NET/TestApp/FormLog.cs b/Triangle.NET/TestApp/FormLog.cs new file mode 100644 index 0000000..5ecf4af --- /dev/null +++ b/Triangle.NET/TestApp/FormLog.cs @@ -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 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); + } + } + } + } + } +} diff --git a/Triangle.NET/TestApp/FormMain.Designer.cs b/Triangle.NET/TestApp/FormMain.Designer.cs index 189484a..a52ccd9 100644 --- a/Triangle.NET/TestApp/FormMain.Designer.cs +++ b/Triangle.NET/TestApp/FormMain.Designer.cs @@ -1,4 +1,4 @@ -namespace TestApp +namespace MeshExplorer { partial class FormMain { @@ -28,148 +28,846 @@ /// 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; } } diff --git a/Triangle.NET/TestApp/FormMain.cs b/Triangle.NET/TestApp/FormMain.cs index 0911c7f..a56edf8 100644 --- a/Triangle.NET/TestApp/FormMain.cs +++ b/Triangle.NET/TestApp/FormMain.cs @@ -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 points = new List(); + 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 } } diff --git a/Triangle.NET/TestApp/FormMain.resx b/Triangle.NET/TestApp/FormMain.resx index 29dcb1b..0f6d8eb 100644 --- a/Triangle.NET/TestApp/FormMain.resx +++ b/Triangle.NET/TestApp/FormMain.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/Triangle.NET/TestApp/FormQuality.Designer.cs b/Triangle.NET/TestApp/FormQuality.Designer.cs index 38c7a6b..ec5ce91 100644 --- a/Triangle.NET/TestApp/FormQuality.Designer.cs +++ b/Triangle.NET/TestApp/FormQuality.Designer.cs @@ -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; } } \ No newline at end of file diff --git a/Triangle.NET/TestApp/FormQuality.cs b/Triangle.NET/TestApp/FormQuality.cs index c5eaa1a..6606983 100644 --- a/Triangle.NET/TestApp/FormQuality.cs +++ b/Triangle.NET/TestApp/FormQuality.cs @@ -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(); + } } } } diff --git a/Triangle.NET/TestApp/IO/FileProcessor.cs b/Triangle.NET/TestApp/IO/FileProcessor.cs index 1671bbe..348378c 100644 --- a/Triangle.NET/TestApp/IO/FileProcessor.cs +++ b/Triangle.NET/TestApp/IO/FileProcessor.cs @@ -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") { diff --git a/Triangle.NET/TestApp/IO/Formats/JsonFile.cs b/Triangle.NET/TestApp/IO/Formats/JsonFile.cs new file mode 100644 index 0000000..d3ad046 --- /dev/null +++ b/Triangle.NET/TestApp/IO/Formats/JsonFile.cs @@ -0,0 +1,134 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + + /// + /// TODO: Update summary. + /// + public class JsonFile : MeshExplorer.IO.IMeshFormat + { + /// + /// Gets the supported file extensions. + /// + public string[] Extensions + { + get { return new string[] { ".json" }; } + } + + /// + /// + /// + /// + /// + public InputGeometry Read(string filename) + { + string json = File.ReadAllText(filename); + + JsonParser parser = new JsonParser("json"); + object rawData = parser.Decode(); + + InputGeometry data = new InputGeometry(); + + List triangles = new List(); + + 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("}"); + } + } + } +} \ No newline at end of file diff --git a/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs b/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs new file mode 100644 index 0000000..c5642ad --- /dev/null +++ b/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs @@ -0,0 +1,133 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + + /// + /// TODO: Update summary. + /// + public class TriangleFile : MeshExplorer.IO.IMeshFormat + { + /// + /// Gets the supported file extensions. + /// + 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); + } + } + } + } +} diff --git a/Triangle.NET/TestApp/ImageWriter.cs b/Triangle.NET/TestApp/ImageWriter.cs index a0711f1..b3037cb 100644 --- a/Triangle.NET/TestApp/ImageWriter.cs +++ b/Triangle.NET/TestApp/ImageWriter.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -namespace TestApp +namespace MeshExplorer { using System; using System.Drawing; @@ -20,8 +20,7 @@ namespace TestApp /// 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); - + /// /// Sets the color scheme. /// @@ -39,7 +38,7 @@ namespace TestApp /// Steiner points color. /// Line color. /// Segment color. - 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 /// The target width of the image (pixel). 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); + } + + /// + /// Draws the voronoi diagram and writes the image file. + /// + /// The mesh to visualize. + /// The filename (only PNG supported). + /// The target width of the image (pixel). + 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 /// /// Draw mesh to the graphics object. /// - 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(); } + + /// + /// Draw mesh to the graphics object. + /// + 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); + } + + /// + /// Bounding box. + /// + 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; + } + } } } diff --git a/Triangle.NET/TestApp/Mesh Explorer.csproj b/Triangle.NET/TestApp/Mesh Explorer.csproj index 4b52f34..49a561a 100644 --- a/Triangle.NET/TestApp/Mesh Explorer.csproj +++ b/Triangle.NET/TestApp/Mesh Explorer.csproj @@ -46,6 +46,7 @@ + Component @@ -55,10 +56,29 @@ Component + + Component + + + Component + + + Component + Component + + + Form + + + Form + + + FormLog.cs + Form @@ -77,12 +97,16 @@ + + + + FormMain.cs diff --git a/Triangle.NET/TestApp/PolygonGenerator.cs b/Triangle.NET/TestApp/PolygonGenerator.cs new file mode 100644 index 0000000..cc6a254 --- /dev/null +++ b/Triangle.NET/TestApp/PolygonGenerator.cs @@ -0,0 +1,115 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +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; + + /// + /// TODO: Update 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; + } + } +} diff --git a/Triangle.NET/TestApp/Program.cs b/Triangle.NET/TestApp/Program.cs index ef7d4ae..928c59e 100644 --- a/Triangle.NET/TestApp/Program.cs +++ b/Triangle.NET/TestApp/Program.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -namespace TestApp +namespace MeshExplorer { static class Program { diff --git a/Triangle.NET/TestApp/Rendering/RenderData.cs b/Triangle.NET/TestApp/Rendering/RenderData.cs index c9ed18c..b46d030 100644 --- a/Triangle.NET/TestApp/Rendering/RenderData.cs +++ b/Triangle.NET/TestApp/Rendering/RenderData.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -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 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(); + } + + /// + /// + /// + /// + /// This methods assumes that the mesh.Renumber() has been called. 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 edgeList = new List(mesh.NumberOfEdges); + + while (e.MoveNext()) + { + edgeList.Add(e.Current); + } + + this.Edges = edgeList.ToArray(); + } + + private void SetPoints(IEnumerable 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]; - } - } } } } diff --git a/Triangle.NET/TestApp/Rendering/Zoom.cs b/Triangle.NET/TestApp/Rendering/Zoom.cs index b3b8595..5512a5e 100644 --- a/Triangle.NET/TestApp/Rendering/Zoom.cs +++ b/Triangle.NET/TestApp/Rendering/Zoom.cs @@ -4,7 +4,7 @@ // // ----------------------------------------------------------------------- -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() diff --git a/Triangle.NET/TestApp/Settings.cs b/Triangle.NET/TestApp/Settings.cs new file mode 100644 index 0000000..f3c48c1 --- /dev/null +++ b/Triangle.NET/TestApp/Settings.cs @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace MeshExplorer +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.IO; + using System.Windows.Forms; + + /// + /// TODO: Update 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; + } + } +} diff --git a/Triangle.NET/Triangle/Algorithm/Dwyer.cs b/Triangle.NET/Triangle/Algorithm/Dwyer.cs index 0b56db4..fb2415e 100644 --- a/Triangle.NET/Triangle/Algorithm/Dwyer.cs +++ b/Triangle.NET/Triangle/Algorithm/Dwyer.cs @@ -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; + /// /// Form a Delaunay triangulation by the divide-and-conquer method. /// @@ -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; diff --git a/Triangle.NET/Triangle/Algorithm/Incremental.cs b/Triangle.NET/Triangle/Algorithm/Incremental.cs index 15622d2..d1c4850 100644 --- a/Triangle.NET/Triangle/Algorithm/Incremental.cs +++ b/Triangle.NET/Triangle/Algorithm/Incremental.cs @@ -9,6 +9,7 @@ namespace TriangleNet.Algorithm { using TriangleNet.Data; using TriangleNet.Log; + using TriangleNet.Geometry; /// /// 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. /// - 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; diff --git a/Triangle.NET/Triangle/Algorithm/SweepLine.cs b/Triangle.NET/Triangle/Algorithm/SweepLine.cs index 6f9064c..5252085 100644 --- a/Triangle.NET/Triangle/Algorithm/SweepLine.cs +++ b/Triangle.NET/Triangle/Algorithm/SweepLine.cs @@ -13,27 +13,14 @@ namespace TriangleNet.Algorithm using System.Text; using TriangleNet.Data; using TriangleNet.Log; + using TriangleNet.Geometry; + using TriangleNet.Tools; /// /// Builds a delaunay triangulation using the sweepline algorithm. /// class SweepLine { - /// - /// 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.). - /// - 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 + + /// + /// A node in a heap used to store events for the sweepline Delaunay algorithm. + /// + /// + /// 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'. + /// + 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. + } + + /// + /// 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.). + /// + class SweepEventVertex : Vertex + { + public SweepEvent evt; + + public SweepEventVertex(SweepEvent e) + { + evt = e; + } + } + + /// + /// A node in the splay tree. + /// + /// + /// 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. + /// + 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 } } diff --git a/Triangle.NET/Triangle/BadTriQueue.cs b/Triangle.NET/Triangle/BadTriQueue.cs index f7fd187..ac98753 100644 --- a/Triangle.NET/Triangle/BadTriQueue.cs +++ b/Triangle.NET/Triangle/BadTriQueue.cs @@ -11,8 +11,13 @@ namespace TriangleNet using TriangleNet.Data; /// - /// TODO: Update summary. + /// A (priority) queue for bad triangles. /// + /// + // 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. + /// 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 - /// /// Add a bad triangle data structure to the end of a queue. /// - /// - /// - // 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. - /// + /// The bad triangle to enqueue. public void Enqueue(BadTriangle badtri) { double length, multiplier; @@ -154,9 +151,6 @@ namespace TriangleNet /// /// /// - /// - /// Allocates a badtriang data structure for the triangle, then passes it to enqueuebadtriang(). - /// 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 /// 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 } } diff --git a/Triangle.NET/Triangle/Behavior.cs b/Triangle.NET/Triangle/Behavior.cs index 7f0a13b..df358e5 100644 --- a/Triangle.NET/Triangle/Behavior.cs +++ b/Triangle.NET/Triangle/Behavior.cs @@ -15,32 +15,104 @@ namespace TriangleNet /// static class Behavior { + /// + /// Input is a Planar Straight Line Graph. + /// public static bool Poly { get; set; } + /// + /// Quality mesh generation. + /// public static bool Quality { get; set; } + /// + /// Apply a maximum triangle area constraint. + /// public static bool VarArea { get; set; } + /// + /// Apply a maximum triangle area constraint. + /// public static bool FixedArea { get; set; } + /// + /// Apply a user-defined triangle constraint. + /// public static bool Usertest { get; set; } + /// + /// Apply attributes to identify triangles in certain regions. + /// public static bool RegionAttrib { get; set; } + /// + /// Enclose the convex hull with segments. + /// public static bool Convex { get; set; } + /// + /// Jettison unused vertices from output. + /// public static bool Jettison { get; set; } + /// + /// Compute boundary information. + /// public static bool UseBoundaryMarkers { get; set; } + /// + /// Ignores holes in polygons. + /// public static bool NoHoles { get; set; } + /// + /// No exact arithmetic. + /// public static bool NoExact { get; set; } + /// + /// Conforming Delaunay (all triangles are truly Delaunay). + /// public static bool ConformDel { get; set; } + /// + /// Algorithm to use for triangulation. + /// public static TriangulationAlgorithm Algorithm { get; set; } + /// + /// Log detailed information. + /// public static bool Verbose { get; set; } - public static bool UseSegments { get; set; } + /// + /// Use segments (should not be set manually) + /// + public static bool UseSegments { get; set; } // TODO: internal set - public static int NoBisect { get; set; } // <- int + /// + /// Suppresses boundary segment splitting. + /// + public static int NoBisect { get; set; } // <- int ! + /// + /// Use maximum number of added Steiner points. + /// public static int Steiner { get; set; } + /// + /// Minimum angle constraint. + /// public static double MinAngle { get; set; } + /// + /// (should not be set manually) + /// public static double GoodAngle { get; set; } + /// + /// (should not be set manually) + /// public static double Offconstant { get; set; } + /// + /// Maximum area constraint. + /// public static double MaxArea { get; set; } + /// + /// Maximum angle constraint. + /// public static double MaxAngle { get; set; } + /// + /// (should not be set manually) + /// public static double MaxGoodAngle { get; set; } + /// + /// Load behavior defaults. + /// public static void Init() { Poly = false; diff --git a/Triangle.NET/Triangle/Carver.cs b/Triangle.NET/Triangle/Carver.cs index e4505ed..5883194 100644 --- a/Triangle.NET/Triangle/Carver.cs +++ b/Triangle.NET/Triangle/Carver.cs @@ -9,6 +9,7 @@ namespace TriangleNet { using TriangleNet.Data; using System; + using TriangleNet.Geometry; using System.Collections.Generic; /// @@ -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. /// - 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(); } /// - /// 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. /// - /// - /// - /// - /// 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(); } } } diff --git a/Triangle.NET/Triangle/Data/BadSubseg.cs b/Triangle.NET/Triangle/Data/BadSubseg.cs index 03a9347..d95d8b7 100644 --- a/Triangle.NET/Triangle/Data/BadSubseg.cs +++ b/Triangle.NET/Triangle/Data/BadSubseg.cs @@ -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); } }; } diff --git a/Triangle.NET/Triangle/Data/BadTriangle.cs b/Triangle.NET/Triangle/Data/BadTriangle.cs index d98bf34..bc68f1f 100644 --- a/Triangle.NET/Triangle/Data/BadTriangle.cs +++ b/Triangle.NET/Triangle/Data/BadTriangle.cs @@ -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); } } } diff --git a/Triangle.NET/Triangle/Data/FlipStacker.cs b/Triangle.NET/Triangle/Data/FlipStacker.cs deleted file mode 100644 index 517ab5c..0000000 --- a/Triangle.NET/Triangle/Data/FlipStacker.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ----------------------------------------------------------------------- -// -// 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/ -// -// ----------------------------------------------------------------------- - -namespace TriangleNet.Data -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - - /// - /// A stack of triangles flipped during the most recent vertex insertion. - /// - /// - /// The stack is used to undo the vertex insertion if the vertex encroaches - /// upon a subsegment. - /// - class FlipStacker - { - public Otri flippedtri; // A recently flipped triangle. - public FlipStacker prevflip; // Previous flip in the stack. - } -} diff --git a/Triangle.NET/Triangle/Data/Osub.cs b/Triangle.NET/Triangle/Data/Osub.cs index ca2be29..4a7a334 100644 --- a/Triangle.NET/Triangle/Data/Osub.cs +++ b/Triangle.NET/Triangle/Data/Osub.cs @@ -23,16 +23,16 @@ namespace TriangleNet.Data /// 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 /// public void Sym(ref Osub o2) { - o2.ss = ss; - o2.ssorient = 1 - ssorient; + o2.seg = seg; + o2.orient = 1 - orient; } /// @@ -53,7 +53,7 @@ namespace TriangleNet.Data /// public void SymSelf() { - ssorient = 1 - ssorient; + orient = 1 - orient; } /// @@ -64,7 +64,7 @@ namespace TriangleNet.Data /// public void Pivot(ref Osub o2) { - o2 = ss.subsegs[ssorient]; + o2 = seg.subsegs[orient]; //sdecode(sptr, o2); } @@ -73,7 +73,7 @@ namespace TriangleNet.Data /// public void PivotSelf() { - this = ss.subsegs[ssorient]; + this = seg.subsegs[orient]; //sdecode(sptr, osub); } @@ -85,7 +85,7 @@ namespace TriangleNet.Data /// 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 /// public void NextSelf() { - this = ss.subsegs[1 - ssorient]; + this = seg.subsegs[1 - orient]; //sdecode(sptr, osub); } @@ -103,7 +103,7 @@ namespace TriangleNet.Data /// public Vertex Org() { - return ss.vertices[ssorient]; + return seg.vertices[orient]; } /// @@ -111,7 +111,7 @@ namespace TriangleNet.Data /// public Vertex Dest() { - return ss.vertices[1 - ssorient]; + return seg.vertices[1 - orient]; } /// @@ -119,7 +119,7 @@ namespace TriangleNet.Data /// public void SetOrg(Vertex ptr) { - ss.vertices[ssorient] = ptr; + seg.vertices[orient] = ptr; } /// @@ -127,7 +127,7 @@ namespace TriangleNet.Data /// public void SetDest(Vertex ptr) { - ss.vertices[1 - ssorient] = ptr; + seg.vertices[1 - orient] = ptr; } /// @@ -135,7 +135,7 @@ namespace TriangleNet.Data /// public Vertex SegOrg() { - return ss.vertices[2 + ssorient]; + return seg.vertices[2 + orient]; } /// @@ -143,7 +143,7 @@ namespace TriangleNet.Data /// public Vertex SegDest() { - return ss.vertices[3 - ssorient]; + return seg.vertices[3 - orient]; } /// @@ -151,7 +151,7 @@ namespace TriangleNet.Data /// public void SetSegOrg(Vertex ptr) { - ss.vertices[2 + ssorient] = ptr; + seg.vertices[2 + orient] = ptr; } /// @@ -159,7 +159,7 @@ namespace TriangleNet.Data /// public void SetSegDest(Vertex ptr) { - ss.vertices[3 - ssorient] = ptr; + seg.vertices[3 - orient] = ptr; } /// @@ -169,7 +169,7 @@ namespace TriangleNet.Data /// setting boundary conditions in finite element solvers. public int Mark() { - return ss.boundary; + return seg.boundary; } /// @@ -177,7 +177,7 @@ namespace TriangleNet.Data /// public void SetMark(int value) { - ss.boundary = value; + seg.boundary = value; } /// @@ -185,8 +185,8 @@ namespace TriangleNet.Data /// 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; } /// @@ -196,7 +196,7 @@ namespace TriangleNet.Data /// connected to this subsegment. public void Dissolve() { - ss.subsegs[ssorient].ss = Mesh.dummysub; + seg.subsegs[orient].seg = Mesh.dummysub; } /// @@ -204,8 +204,8 @@ namespace TriangleNet.Data /// public void Copy(ref Osub o2) { - o2.ss = ss; - o2.ssorient = ssorient; + o2.seg = seg; + o2.orient = orient; } /// @@ -213,24 +213,24 @@ namespace TriangleNet.Data /// public bool Equal(Osub o2) { - return ((ss == o2.ss) && (ssorient == o2.ssorient)); + return ((seg == o2.seg) && (orient == o2.orient)); } /// /// Check a subsegment's deallocation. /// - public static bool IsDead(Subseg sub) + public static bool IsDead(Segment sub) { - return sub.subsegs[0].ss == null; + return sub.subsegs[0].seg == null; } /// /// Set a subsegment's deallocation. /// - 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; } /// @@ -238,7 +238,7 @@ namespace TriangleNet.Data /// public void TriPivot(ref Otri ot) { - ot = ss.triangles[ssorient]; + ot = seg.triangles[orient]; //decode(ptr, otri) } @@ -247,7 +247,7 @@ namespace TriangleNet.Data /// public void TriDissolve() { - ss.triangles[ssorient].triangle = Mesh.dummytri; + seg.triangles[orient].triangle = Mesh.dummytri; } #endregion diff --git a/Triangle.NET/Triangle/Data/Otri.cs b/Triangle.NET/Triangle/Data/Otri.cs index bf661bb..339fc59 100644 --- a/Triangle.NET/Triangle/Data/Otri.cs +++ b/Triangle.NET/Triangle/Data/Otri.cs @@ -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 /// 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; } /// @@ -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; } /// @@ -470,7 +476,7 @@ namespace TriangleNet.Data /// public void SegDissolve() { - triangle.subsegs[orient].ss = Mesh.dummysub; + triangle.subsegs[orient].seg = Mesh.dummysub; } #endregion diff --git a/Triangle.NET/Triangle/Data/Point2.cs b/Triangle.NET/Triangle/Data/Point2.cs deleted file mode 100644 index afc6735..0000000 --- a/Triangle.NET/Triangle/Data/Point2.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ----------------------------------------------------------------------- -// -// 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/ -// -// ----------------------------------------------------------------------- - -namespace TriangleNet.Data -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - - /// - /// Represents a 2D point. - /// - 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); - } - } -} diff --git a/Triangle.NET/Triangle/Data/Region.cs b/Triangle.NET/Triangle/Data/Region.cs deleted file mode 100644 index 053833b..0000000 --- a/Triangle.NET/Triangle/Data/Region.cs +++ /dev/null @@ -1,31 +0,0 @@ -// ----------------------------------------------------------------------- -// -// 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/ -// -// ----------------------------------------------------------------------- - -namespace TriangleNet.Data -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - - /// - /// TODO: Update 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]; - } - } -} diff --git a/Triangle.NET/Triangle/Data/Subseg.cs b/Triangle.NET/Triangle/Data/Segment.cs similarity index 55% rename from Triangle.NET/Triangle/Data/Subseg.cs rename to Triangle.NET/Triangle/Data/Segment.cs index 7551d54..108fb1d 100644 --- a/Triangle.NET/Triangle/Data/Subseg.cs +++ b/Triangle.NET/Triangle/Data/Segment.cs @@ -18,32 +18,25 @@ namespace TriangleNet.Data /// /// 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. /// - 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 + /// - /// Reset the hash seed. + /// Gets the first endpoints vertex id. /// - /// The new has seed value. - /// Reset value will usally 0, if a new triangulation starts, - /// or the number of subsegments, if refinement is done. - 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; } } + /// + /// Gets the seconds endpoints vertex id. + /// + public int P1 + { + get { return this.vertices[1].id; } + } + + /// + /// Gets the segment boundary mark. + /// + 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); } } } diff --git a/Triangle.NET/Triangle/Data/SplayNode.cs b/Triangle.NET/Triangle/Data/SplayNode.cs deleted file mode 100644 index b04080f..0000000 --- a/Triangle.NET/Triangle/Data/SplayNode.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ----------------------------------------------------------------------- -// -// 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/ -// -// ----------------------------------------------------------------------- - -namespace TriangleNet.Data -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - - /// - /// A node in the splay tree. - /// - /// - /// 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. - /// - 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. - } -} diff --git a/Triangle.NET/Triangle/Data/SweepEvent.cs b/Triangle.NET/Triangle/Data/SweepEvent.cs deleted file mode 100644 index 17f37ef..0000000 --- a/Triangle.NET/Triangle/Data/SweepEvent.cs +++ /dev/null @@ -1,33 +0,0 @@ -// ----------------------------------------------------------------------- -// -// 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/ -// -// ----------------------------------------------------------------------- - -namespace TriangleNet.Data -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - - /// - /// A node in a heap used to store events for the sweepline Delaunay algorithm. - /// - /// - /// 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'. - /// - 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. - } -} diff --git a/Triangle.NET/Triangle/Data/Triangle.cs b/Triangle.NET/Triangle/Data/Triangle.cs index 6d18a05..4143590 100644 --- a/Triangle.NET/Triangle/Data/Triangle.cs +++ b/Triangle.NET/Triangle/Data/Triangle.cs @@ -11,28 +11,24 @@ namespace TriangleNet.Data using System.Collections.Generic; using System.Linq; using System.Text; + using TriangleNet.Geometry; /// /// The triangle data structure. /// /// - /// 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". /// - 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 + /// - /// Reset the hash seed. + /// Gets the triangle id. /// - /// The new has seed value. - /// Reset value will usally 0, if a new triangulation starts, - /// or the number of triangles, if refinement is done. - 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; } } + /// + /// Gets the first corners vertex id. + /// + public int P0 + { + get { return this.vertices[0] == null ? -1 : this.vertices[0].id; } + } + + /// + /// Gets the seconds corners vertex id. + /// + public int P1 + { + get { return this.vertices[1] == null ? -1 : this.vertices[1].id; } + } + + /// + /// Gets the specified corners vertex id. + /// + public int this[int index] + { + get { return this.vertices[index] == null ? -1 : this.vertices[index].id; } + } + + /// + /// Gets the third corners vertex id. + /// + public int P2 + { + get { return this.vertices[2] == null ? -1 : this.vertices[2].id; } + } + + public bool SupportsNeighbors + { + get { return true; } + } + + /// + /// Gets the first neighbors id. + /// + public int N0 + { + get { return this.neighbors[0].triangle.id; } + } + + /// + /// Gets the second neighbors id. + /// + public int N1 + { + get { return this.neighbors[1].triangle.id; } + } + + /// + /// Gets the third neighbors id. + /// + public int N2 + { + get { return this.neighbors[2].triangle.id; } + } + + /// + /// Gets the triangle area constraint. + /// + public double Area + { + get { return this.area; } + } + + /// + /// Gets the triangle attributes. + /// + 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); } } } diff --git a/Triangle.NET/Triangle/Data/Vertex.cs b/Triangle.NET/Triangle/Data/Vertex.cs index 83223ba..c933fd8 100644 --- a/Triangle.NET/Triangle/Data/Vertex.cs +++ b/Triangle.NET/Triangle/Data/Vertex.cs @@ -11,43 +11,52 @@ namespace TriangleNet.Data using System.Collections.Generic; using System.Linq; using System.Text; - + using TriangleNet.Geometry; /// /// The vertex data structure. /// - /// - /// Each vertex is actually an array of doubles. An integer boundary marker, - /// and sometimes a to a triangle, is appended after the doubles. - /// - class Vertex : IComparable, IEquatable + 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 + + /// + /// Gets the vertex id. + /// + public int ID + { + get { return this.id; } + } + + /// + /// Gets the vertex type. + /// + public VertexType Type + { + get { return this.type; } } /// @@ -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."); } } - /// - /// Reset the hash seed. - /// - /// The new has seed value. - /// Reset value will usally 0, if a new triangulation starts, - /// or the number of points, if refinement is done. - 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; } } } diff --git a/Triangle.NET/Triangle/Enums.cs b/Triangle.NET/Triangle/Enums.cs index cef6836..da37e44 100644 --- a/Triangle.NET/Triangle/Enums.cs +++ b/Triangle.NET/Triangle/Enums.cs @@ -90,5 +90,5 @@ namespace TriangleNet /// /// The type of the mesh vertex. /// - enum VertexType { InputVertex, SegmentVertex, FreeVertex, DeadVertex, UndeadVertex }; + public enum VertexType { InputVertex, SegmentVertex, FreeVertex, DeadVertex, UndeadVertex }; } diff --git a/Triangle.NET/Triangle/Geometry/EdgeEnumerator.cs b/Triangle.NET/Triangle/Geometry/EdgeEnumerator.cs new file mode 100644 index 0000000..581abaf --- /dev/null +++ b/Triangle.NET/Triangle/Geometry/EdgeEnumerator.cs @@ -0,0 +1,99 @@ +// ----------------------------------------------------------------------- +// +// TODO: Update copyright text. +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.Geometry +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using TriangleNet.Data; + + /// + /// Enumerates the edges of a triangulation. + /// + public class EdgeEnumerator : IEnumerator + { + IEnumerator triangles; + Otri tri = default(Otri); + Otri neighbor = default(Otri); + Edge current; + Vertex p1, p2; + + /// + /// Initializes a new instance of the class. + /// + 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(); + } + } +} diff --git a/Triangle.NET/Triangle/Geometry/InputGeometry.cs b/Triangle.NET/Triangle/Geometry/InputGeometry.cs index a503376..e306329 100644 --- a/Triangle.NET/Triangle/Geometry/InputGeometry.cs +++ b/Triangle.NET/Triangle/Geometry/InputGeometry.cs @@ -75,7 +75,7 @@ namespace TriangleNet.Geometry /// public IEnumerable Points { - get { return null; } + get { return points; } } /// @@ -131,7 +131,7 @@ namespace TriangleNet.Geometry /// Boundary marker. 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 /// Segment marker. public void AddSegment(int p0, int p1, int boundary) { - if (p0 == p1) + if (p0 == p1 || p0 < 0 || p1 < 0) { throw new NotSupportedException("Invalid endpoints."); } diff --git a/Triangle.NET/Triangle/IO/DataReader.cs b/Triangle.NET/Triangle/IO/DataReader.cs index c153375..d58db40 100644 --- a/Triangle.NET/Triangle/IO/DataReader.cs +++ b/Triangle.NET/Triangle/IO/DataReader.cs @@ -14,6 +14,7 @@ namespace TriangleNet.IO using System.Globalization; using TriangleNet.Data; using TriangleNet.Log; + using TriangleNet.Geometry; /// /// 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. /// - 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 diff --git a/Triangle.NET/Triangle/IO/DataWriter.cs b/Triangle.NET/Triangle/IO/DataWriter.cs index 6b28a7b..1d5dcca 100644 --- a/Triangle.NET/Triangle/IO/DataWriter.cs +++ b/Triangle.NET/Triangle/IO/DataWriter.cs @@ -12,286 +12,15 @@ namespace TriangleNet.IO using System.Collections.Generic; using System.Globalization; using TriangleNet.Data; + using TriangleNet.Geometry; /// /// Generates a mesh representaion using arrays. /// public static class DataWriter { - static NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat; - - static int verticesCount; - static int elementsCount; - #region Library - /// - /// Number the vertices and write them to raw output data. - /// - /// - /// - 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++; - } - } - } - - /// - /// Write the triangles to raw output data. - /// - /// - /// - 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++; - } - } - - /// - /// Write the segments and holes to raw output data. - /// - /// - /// - 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++; - } - } - } - - /// - /// Write the edges to raw output data. - /// - /// - /// - 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++; - } - } - } - } - - /// - /// Write the triangle neighbors to raw output data. - /// - /// - /// - /// WARNING: Be sure WriteElements has been called before, - /// so the elements are numbered right! - 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++; - } - } - /// /// Gets the Voronoi diagram as raw output data. /// @@ -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 }; } diff --git a/Triangle.NET/Triangle/IO/DebugWriter.cs b/Triangle.NET/Triangle/IO/DebugWriter.cs new file mode 100644 index 0000000..9b3bde5 --- /dev/null +++ b/Triangle.NET/Triangle/IO/DebugWriter.cs @@ -0,0 +1,164 @@ +// ----------------------------------------------------------------------- +// +// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/ +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.IO +{ + using System; + using System.IO; + using System.Globalization; + using TriangleNet.Data; + + /// + /// Writes a the current mesh into a text file. + /// + /// + /// 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 + /// + 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"; + } + + /// + /// Start a new session with given name. + /// + /// Name of the session (and output files). + public void NewSession(string name) + { + this.iteration = 0; + this.name = name + "-{0}.mesh"; + } + + /// + /// Write complete mesh to a .mesh file. + /// + 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); + } + } + } + } +} diff --git a/Triangle.NET/Triangle/IO/FileReader.cs b/Triangle.NET/Triangle/IO/FileReader.cs index 4b8104c..18b7bc4 100644 --- a/Triangle.NET/Triangle/IO/FileReader.cs +++ b/Triangle.NET/Triangle/IO/FileReader.cs @@ -12,6 +12,8 @@ namespace TriangleNet.IO using System.Globalization; using TriangleNet.Data; using TriangleNet.Log; + using TriangleNet.Geometry; + using System.Collections.Generic; /// /// Helper for reading Triangle files. @@ -26,7 +28,7 @@ namespace TriangleNet.IO /// /// The file to read. /// Will NOT read associated files by default. - public static MeshData ReadFile(string filename) + public static InputGeometry ReadFile(string filename) { return ReadFile(filename, false); } @@ -36,7 +38,7 @@ namespace TriangleNet.IO /// /// The file to read. /// Read associated files (ele, area, neigh). - 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) + /// + /// + /// + /// + /// + /// + /// Number of point attributes + 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); } /// @@ -112,7 +117,7 @@ namespace TriangleNet.IO /// /// /// Will NOT read associated .ele by default. - public static MeshData ReadNodeFile(string nodefilename) + public static InputGeometry ReadNodeFile(string nodefilename) { return ReadNodeFile(nodefilename, false); } @@ -122,9 +127,9 @@ namespace TriangleNet.IO /// /// /// - 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 /// /// /// Will NOT read associated .ele by default. - public static MeshData ReadPolyFile(string polyfilename) + public static InputGeometry ReadPolyFile(string polyfilename) { return ReadPolyFile(polyfilename, false, false); } @@ -231,7 +226,7 @@ namespace TriangleNet.IO /// /// If true, look for an associated .ele file. /// Will NOT read associated .area by default. - 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 /// /// If true, look for an associated .ele file. /// If true, look for an associated .area file. - 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 ReadEleFile(string elefilename) { - MeshData data = new MeshData(); - - ReadEleFile(elefilename, data, false); - - return data; + return ReadEleFile(elefilename, false); } /// @@ -504,10 +462,12 @@ namespace TriangleNet.IO /// /// /// - private static void ReadEleFile(string elefilename, MeshData data, bool readArea) + private static List ReadEleFile(string elefilename, bool readArea) { int intriangles = 0, attributes = 0; + List 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(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; } /// @@ -586,8 +547,10 @@ namespace TriangleNet.IO /// /// /// - 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 ReadEdgeFile(string edgeFile, int invertices) { // Read poly file - MeshData data = new MeshData(); + List data = null; startIndex = 0; @@ -653,15 +618,10 @@ namespace TriangleNet.IO if (inedges > 0) { - data.Edges = new int[inedges][]; + data = new List(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)); + } } } diff --git a/Triangle.NET/Triangle/IO/FileWriter.cs b/Triangle.NET/Triangle/IO/FileWriter.cs index 2d0a527..e2420ee 100644 --- a/Triangle.NET/Triangle/IO/FileWriter.cs +++ b/Triangle.NET/Triangle/IO/FileWriter.cs @@ -11,6 +11,7 @@ namespace TriangleNet.IO using System.IO; using System.Globalization; using TriangleNet.Data; + using TriangleNet.Geometry; /// /// 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); } } } diff --git a/Triangle.NET/Triangle/IO/InputTriangle.cs b/Triangle.NET/Triangle/IO/InputTriangle.cs new file mode 100644 index 0000000..dc23e8f --- /dev/null +++ b/Triangle.NET/Triangle/IO/InputTriangle.cs @@ -0,0 +1,107 @@ +// ----------------------------------------------------------------------- +// +// 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/ +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.IO +{ + using System; + using TriangleNet.Geometry; + + /// + /// Simple triangle class for input. + /// + 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 + + /// + /// Gets the triangle id. + /// + public int ID + { + get { return 0; } + } + + /// + /// Gets the first corners vertex id. + /// + public int P0 + { + get { return this.vertices[0]; } + } + + /// + /// Gets the seconds corners vertex id. + /// + public int P1 + { + get { return this.vertices[1]; } + } + + /// + /// Gets the third corners vertex id. + /// + public int P2 + { + get { return this.vertices[2]; } + } + + /// + /// Gets the specified corners vertex id. + /// + 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; } + } + + /// + /// Gets the triangle area constraint. + /// + public double Area + { + get { return -1; } + } + + /// + /// Gets the triangle attributes. + /// + public double[] Attributes + { + get { return null; } + } + + #endregion + } +} diff --git a/Triangle.NET/Triangle/IO/MeshData.cs b/Triangle.NET/Triangle/IO/MeshData.cs deleted file mode 100644 index 7508a79..0000000 --- a/Triangle.NET/Triangle/IO/MeshData.cs +++ /dev/null @@ -1,33 +0,0 @@ -// ----------------------------------------------------------------------- -// -// 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 -// -// ----------------------------------------------------------------------- - -namespace TriangleNet.IO -{ - /// - /// Stores the mesh data in- and output. - /// - 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 - } -} diff --git a/Triangle.NET/Triangle/IO/VoronoiData.cs b/Triangle.NET/Triangle/IO/VoronoiData.cs index 905d6ba..41dc354 100644 --- a/Triangle.NET/Triangle/IO/VoronoiData.cs +++ b/Triangle.NET/Triangle/IO/VoronoiData.cs @@ -7,16 +7,18 @@ namespace TriangleNet.IO { + using TriangleNet.Geometry; + /// /// Stores the voronoi data (output only). /// 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; } -} +} \ No newline at end of file diff --git a/Triangle.NET/Triangle/Log/SimpleLog.cs b/Triangle.NET/Triangle/Log/SimpleLog.cs index 2ee2269..1f13403 100644 --- a/Triangle.NET/Triangle/Log/SimpleLog.cs +++ b/Triangle.NET/Triangle/Log/SimpleLog.cs @@ -12,7 +12,7 @@ namespace TriangleNet.Log using System.Text; /// - /// A simple logger, which logs messages to a List. + /// A simple logger, which logs messages to a List. /// /// Using singleton pattern as proposed by Jon Skeet. /// http://csharpindepth.com/Articles/General/Singleton.aspx diff --git a/Triangle.NET/Triangle/Mesh.cs b/Triangle.NET/Triangle/Mesh.cs index 8bdbf4c..74563fd 100644 --- a/Triangle.NET/Triangle/Mesh.cs +++ b/Triangle.NET/Triangle/Mesh.cs @@ -15,6 +15,8 @@ namespace TriangleNet using TriangleNet.IO; using TriangleNet.Algorithm; using TriangleNet.Smoothing; + using TriangleNet.Geometry; + using TriangleNet.Tools; /// /// Mesh data structure. @@ -28,36 +30,41 @@ namespace TriangleNet Quality quality; Sampler sampler; - // Variable that maintains the stack of recently flipped triangles. - List flipstackers; - FlipStacker lastflip; + // Stack that maintains a list of recently flipped triangles. + Stack flipstack; + //FlipStacker lastflip; + + // TODO: Check if custom hashmap implementation could be faster. // Using hashsets for memory management should quite fast. internal Dictionary triangles; - internal Dictionary subsegs; + internal Dictionary subsegs; internal Dictionary vertices; - // TODO: Check if custom hashmap implementation could be faster. + // Hash seeds (should belong to mesh instance) + internal int hash_vtx = 0; + internal int hash_seg = 0; + internal int hash_tri = 0; - internal List holes; - internal List regions; + internal List holes; + internal List regions; internal List viri; // Other variables. - internal double xmin, xmax, ymin, ymax; // x and y bounds. - internal int invertices; // Number of input vertices. - internal int inelements; // Number of input triangles. - internal int insegments; // Number of input segments. - internal int undeads; // Number of input vertices that don't appear in the mesh. - internal int edges; // Number of output edges. - internal int mesh_dim; // Dimension (ought to be 2). - internal int nextras; // Number of attributes per vertex. - internal int eextras; // Number of attributes per triangle. - internal int hullsize; // Number of edges in convex hull. - internal int steinerleft; // Number of Steiner points not yet used. - internal bool checksegments; // Are there segments in the triangulation yet? - internal bool checkquality; // Has quality triangulation begun yet? + internal BoundingBox bounds; // x and y bounds. + internal int invertices; // Number of input vertices. + internal int inelements; // Number of input triangles. + internal int insegments; // Number of input segments. + internal int undeads; // Number of input vertices that don't appear in the mesh. + internal int edges; // Number of output edges. + internal int mesh_dim; // Dimension (ought to be 2). + internal int nextras; // Number of attributes per vertex. + internal int eextras; // Number of attributes per triangle. + internal int hullsize; // Number of edges in convex hull. + internal int steinerleft; // Number of Steiner points not yet used. + internal bool checksegments; // Are there segments in the triangulation yet? + internal bool checkquality; // Has quality triangulation begun yet? // Triangular bounding box vertices. internal Vertex infvertex1, infvertex2, infvertex3; @@ -68,21 +75,88 @@ namespace TriangleNet // The omnipresent subsegment. Referenced by any triangle or // subsegment that isn't really connected to a subsegment at // that location. - internal static Subseg dummysub; + internal static Segment dummysub; // Pointer to a recently visited triangle. Improves point location if // proximate vertices are inserted sequentially. internal Otri recenttri; - static FlipStacker dummyflip; + //static FlipStacker dummyflip; #endregion + #region Public properties + + /// + /// Gets the mesh bounding box. + /// + public BoundingBox Bounds + { + get { return this.bounds; } + } + + /// + /// Gets the mesh vertices. + /// + public IEnumerable Vertices + { + get { return this.vertices.Values; } + } + + /// + /// Gets the mesh holes. + /// + public IList Holes + { + get { return this.holes; } + } + + /// + /// Gets the mesh triangles. + /// + public IEnumerable Triangles + { + get { return this.triangles.Values; } + } + + /// + /// Gets the mesh segments. + /// + public IEnumerable Segments + { + get { return this.subsegs.Values; } + } + /// /// Gets the number of input vertices. /// public int NumberOfInputPoints { get { return invertices; } } + /// + /// Gets the number of mesh vertices. + /// + public int NumberOfVertices { get { return this.vertices.Count; } } + + /// + /// Gets the number of mesh triangles. + /// + public int NumberOfTriangles { get { return this.triangles.Count; } } + + /// + /// Gets the number of mesh segments. + /// + public int NumberOfSegments { get { return this.subsegs.Count; } } + + /// + /// Gets the number of mesh edges. + /// + public int NumberOfEdges { get { return this.edges; } } + + #endregion + + /// + /// Initializes a new instance of the class. + /// public Mesh() { logger = SimpleLog.Instance; @@ -91,13 +165,13 @@ namespace TriangleNet vertices = new Dictionary(); triangles = new Dictionary(); - subsegs = new Dictionary(); + subsegs = new Dictionary(); viri = new List(); - flipstackers = new List(); + flipstack = new Stack(); - holes = new List(); - regions = new List(); + holes = new List(); + regions = new List(); quality = new Quality(this); @@ -107,6 +181,7 @@ namespace TriangleNet if (dummytri == null) { + // Initialize static dummy triangle and subseg. DummyInit(); } } @@ -116,7 +191,7 @@ namespace TriangleNet /// public void Load(string inputfile) { - MeshData input = FileReader.ReadFile(inputfile, true); + InputGeometry input = FileReader.ReadFile(inputfile, true); this.Load(input); } @@ -124,9 +199,11 @@ namespace TriangleNet /// /// Reconstructs a mesh from raw input data. /// - public void Load(MeshData input) + public void Load(InputGeometry input) { - if (input.Triangles == null) + throw new NotImplementedException("Load"); + + if (input == null)// TODO: if (input.Triangles == null) { throw new ArgumentException("The input data contains no triangles."); } @@ -139,15 +216,15 @@ namespace TriangleNet Behavior.Poly = true; } - if (input.EdgeMarkers != null) - { - Behavior.UseBoundaryMarkers = true; - } + //if (input.EdgeMarkers != null) + //{ + // Behavior.UseBoundaryMarkers = true; + //} - if (input.TriangleAreas != null) - { - Behavior.VarArea = true; - } + //if (input.TriangleAreas != null) + //{ + // Behavior.VarArea = true; + //} if (!Behavior.Poly) { @@ -163,7 +240,7 @@ namespace TriangleNet TransferNodes(input); // Read and reconstruct a mesh. - hullsize = DataReader.Reconstruct(this, input); + hullsize = DataReader.Reconstruct(this, input, null); } /// @@ -172,7 +249,7 @@ namespace TriangleNet /// public void Triangulate(string inputFile) { - MeshData input = FileReader.ReadFile(inputFile); + InputGeometry input = FileReader.ReadFile(inputFile); this.Triangulate(input); } @@ -181,19 +258,19 @@ namespace TriangleNet /// Triangulate given input data. /// /// - public void Triangulate(MeshData input) + public void Triangulate(InputGeometry input) { ResetData(); - if (input.Segments != null) + if (input.HasSegments) { Behavior.Poly = true; } - if (input.EdgeMarkers != null) - { - Behavior.UseBoundaryMarkers = true; - } + //if (input.EdgeMarkers != null) + //{ + // Behavior.UseBoundaryMarkers = true; + //} if (!Behavior.Poly) { @@ -213,7 +290,7 @@ namespace TriangleNet hullsize = Delaunay(); // Triangulate the vertices. // Ensure that no vertex can be mistaken for a triangular bounding - // box vertex in insertvertex(). + // box vertex in insertvertex(). infvertex1 = null; infvertex2 = null; infvertex3 = null; @@ -229,24 +306,20 @@ namespace TriangleNet if (Behavior.Poly && (triangles.Count > 0)) { - if (input.Holes != null) + // Copy holes + foreach (var item in input.holes) { - // Copy holes - for (int i = 0; i < input.Holes.Length; i++) - { - holes.Add(new Point2(input.Holes[i][0], input.Holes[i][1])); - } + holes.Add(item); } - if (input.Regions != null) + // Copy regions + foreach (var item in input.regions) { - // Copy regions - for (int i = 0; i < input.Regions.Length; i++) - { - regions.Add(new Region(input.Regions[i])); - } + regions.Add(item); } + //dummytri.neighbors[2].triangle = dummytri; + // Carve out holes and concavities. Carver c = new Carver(this); c.CarveHoles(); @@ -283,8 +356,8 @@ namespace TriangleNet foreach (var t in this.triangles.Values) { - tmp = (t.vertices[2].pt.X - t.vertices[0].pt.X) * (t.vertices[1].pt.Y - t.vertices[0].pt.Y) - - (t.vertices[1].pt.X - t.vertices[0].pt.X) * (t.vertices[2].pt.Y - t.vertices[0].pt.Y); + tmp = (t.vertices[2].x - t.vertices[0].x) * (t.vertices[1].y - t.vertices[0].y) - + (t.vertices[1].x - t.vertices[0].x) * (t.vertices[2].y - t.vertices[0].y); tmp = Math.Abs(tmp) / 2.0; @@ -378,6 +451,24 @@ namespace TriangleNet //smoother.Smooth(); } + /// + /// Renumber vertex and triangle id's. + /// + public void Renumber() + { + int id = 0; + foreach (var item in this.vertices.Values) + { + item.id = id++; + } + + id = 0; + foreach (var item in this.triangles.Values) + { + item.id = id++; + } + } + /// /// Check mesh consistency and (constrained) Delaunay property. /// @@ -387,63 +478,6 @@ namespace TriangleNet quality.CheckDelaunay(); } - /// - /// Returns the raw mesh data. - /// - /// Mesh data. - public MeshData GetMeshData() - { - return GetMeshData(true, true, true); - } - - /// - /// Returns the raw mesh data. - /// - /// Write elements to output. - /// Write edges to output. - /// Write neighbour information to output. - /// Mesh data. - public MeshData GetMeshData(bool writeElements, bool writeEdges, bool writeNeighbors) - { - MeshData output = new MeshData(); - - if (Behavior.UseSegments) - { - //output.NumberOfSegments = subsegs.Count; - } - else - { - //output.NumberOfSegments = (int)hullsize; - } - - // Numbers the vertices too. - DataWriter.WriteNodes(this, output); - - if (writeElements) - { - DataWriter.WriteElements(this, output); - } - - // The -c switch (convex switch) causes a PSLG to be written - // even if none was read. - if (Behavior.Poly || Behavior.Convex) - { - DataWriter.WritePoly(this, output); - } - - if (writeEdges) - { - DataWriter.WriteEdges(this, output); - } - - if (writeElements && writeNeighbors) - { - DataWriter.WriteNeighbors(this, output); - } - - return output; - } - #region Options /// @@ -590,7 +624,7 @@ namespace TriangleNet /// Form a Delaunay triangulation. /// /// The number of points on the hull. - int Delaunay() + private int Delaunay() { int hulledges = 0; @@ -612,22 +646,15 @@ namespace TriangleNet hulledges = alg.Triangulate(this); } - if (triangles.Count == 0) - { - // The input vertices were all collinear, - // so there are no triangles. - return 0; - } - - return hulledges; + // The input vertices may all be collinear, so there are + // no triangles. + return (triangles.Count == 0) ? 0 : hulledges; } /// /// Reset all the mesh data. This method will also wipe /// out all mesh data. /// - /// The number of input points. Used to initialize - /// memory for the mesh data. private void ResetData() { vertices.Clear(); @@ -637,23 +664,19 @@ namespace TriangleNet holes.Clear(); regions.Clear(); - Triangle.ResetHashSeed(0); - Vertex.ResetHashSeed(0); - Subseg.ResetHashSeed(0); + this.hash_vtx = 0; + this.hash_seg = 0; + this.hash_tri = 0; viri.Clear(); - flipstackers.Clear(); + flipstack.Clear(); hullsize = 0; - xmin = 0; - xmax = 0; - ymin = 0; - ymax = 0; edges = 0; - sampler.Reset(); - Reset(); + + sampler.Reset(); } /// @@ -680,7 +703,7 @@ namespace TriangleNet /// /// The triangle that fills "outer space," called 'dummytri', is pointed to /// by every triangle and subsegment on a boundary (be it outer or inner) of - /// the triangulation. Also, 'dummytri' points to one of the triangles on + /// the triangulation. Also, 'dummytri' points to one of the triangles on /// the convex hull (until the holes and concavities are carved), making it /// possible to find a starting triangle for point location. // @@ -689,9 +712,9 @@ namespace TriangleNet /// to point to. // /// 'dummytri' and 'dummysub' are generally required to fulfill only a few - /// invariants: their vertices must remain NULL and 'dummytri' must always + /// invariants: their vertices must remain NULL and 'dummytri' must always /// be bonded (at offset zero) to some triangle on the convex hull of the - /// mesh, via a boundary edge. Otherwise, the connections of 'dummytri' and + /// mesh, via a boundary edge. Otherwise, the connections of 'dummytri' and /// 'dummysub' may change willy-nilly. This makes it possible to avoid /// writing a good deal of special-case code (in the edge flip, for example) /// for dealing with the boundary of the mesh, places where no subsegment is @@ -703,8 +726,10 @@ namespace TriangleNet { // Set up 'dummytri', the 'triangle' that occupies "outer space." dummytri = new Triangle(0); + dummytri.hash = -1; + dummytri.id = -1; - // Initialize the three adjoining triangles to be "outer space." These + // Initialize the three adjoining triangles to be "outer space." These // will eventually be changed by various bonding operations, but their // values don't really matter, as long as they can legally be // dereferenced. @@ -717,41 +742,34 @@ namespace TriangleNet // Set up 'dummysub', the omnipresent subsegment pointed to by any // triangle side or subsegment end that isn't attached to a real // subsegment. - dummysub = new Subseg(); + dummysub = new Segment(); + dummysub.hash = -1; // Initialize the two adjoining subsegments to be the omnipresent - // subsegment. These will eventually be changed by various bonding + // subsegment. These will eventually be changed by various bonding // operations, but their values don't really matter, as long as they // can legally be dereferenced. - dummysub.subsegs[0].ss = dummysub; - dummysub.subsegs[1].ss = dummysub; + dummysub.subsegs[0].seg = dummysub; + dummysub.subsegs[1].seg = dummysub; // Initialize the three adjoining subsegments of 'dummytri' to be // the omnipresent subsegment. - dummytri.subsegs[0].ss = dummysub; - dummytri.subsegs[1].ss = dummysub; - dummytri.subsegs[2].ss = dummysub; + dummytri.subsegs[0].seg = dummysub; + dummytri.subsegs[1].seg = dummysub; + dummytri.subsegs[2].seg = dummysub; } - - dummyflip = new FlipStacker(); } /// /// Read the vertices from memory. /// /// The input data. - private void TransferNodes(MeshData data) + private void TransferNodes(InputGeometry data) { - Vertex vertex; - double x, y; - int attribs; + List points = data.points; - double[][] points = data.Points; - attribs = data.PointAttributes == null ? 0 : data.PointAttributes.Length; - - this.invertices = data.Points.Length; + this.invertices = points.Count; this.mesh_dim = 2; - this.nextras = attribs; if (this.invertices < 3) { @@ -759,45 +777,20 @@ namespace TriangleNet throw new Exception("Input must have at least three input vertices."); } - // Read the vertices. - for (int i = 0; i < this.invertices; i++) + this.nextras = 0; // TODO: points[0].Attributes == null ? 0 : points[0].Attributes.Length; + + foreach (Vertex vertex in points) { - vertex = new Vertex(nextras); - vertex.type = VertexType.InputVertex; - vertex.mark = 0; - vertex.ID = vertex.Hash; + // TODO: Set vertex attributes. + //vertex.attribs = points[i].Attributes; - // Read the vertex coordinates. - x = vertex.pt.X = points[i][0]; - y = vertex.pt.Y = points[i][1]; + vertex.hash = this.hash_vtx++; + vertex.id = vertex.hash; - // Read the vertex attributes. - for (int j = 0; j < attribs; j++) - { - //vertexloop.pt.attribs[j] = pointattriblist[i][j]; - } - if (data.PointMarkers != null) - { - // Read a vertex marker. - vertex.mark = data.PointMarkers[i]; - } - - this.vertices.Add(vertex.Hash, vertex); - - // Determine the smallest and largest x and y coordinates. - if (i == 0) - { - this.xmin = this.xmax = x; - this.ymin = this.ymax = y; - } - else - { - this.xmin = (x < this.xmin) ? x : this.xmin; - this.xmax = (x > this.xmax) ? x : this.xmax; - this.ymin = (y < this.ymin) ? y : this.ymin; - this.ymax = (y > this.ymax) ? y : this.ymax; - } + this.vertices.Add(vertex.hash, vertex); } + + this.bounds = data.Bounds; } #endregion @@ -810,12 +803,14 @@ namespace TriangleNet /// Reference to the new triangle. internal void MakeTriangle(ref Otri newotri) { - Triangle t = new Triangle(eextras); + Triangle tri = new Triangle(eextras); + tri.hash = this.hash_tri++; + tri.id = tri.hash; - newotri.triangle = t; + newotri.triangle = tri; newotri.orient = 0; - triangles.Add(t.Hash, t); + triangles.Add(tri.hash, tri); } /// @@ -824,12 +819,13 @@ namespace TriangleNet /// Reference to the new subseg. internal void MakeSubseg(ref Osub newsubseg) { - Subseg s = new Subseg(); + Segment seg = new Segment(); + seg.hash = this.hash_seg++; - newsubseg.ss = s; - newsubseg.ssorient = 0; + newsubseg.seg = seg; + newsubseg.orient = 0; - subsegs.Add(s.Hash, s); + subsegs.Add(seg.hash, seg); } #endregion @@ -839,47 +835,53 @@ namespace TriangleNet /// Insert a vertex into a Delaunay triangulation, performing flips as necessary /// to maintain the Delaunay property. /// + /// The point to be inserted. + /// The triangle to start the search. + /// Segment to split. + /// Check for creation of encroached subsegments. + /// Check for creation of bad quality triangles. + /// If a duplicate vertex or violated segment does not prevent the + /// vertex from being inserted, the return value will be ENCROACHINGVERTEX if + /// the vertex encroaches upon a subsegment (and checking is enabled), or + /// SUCCESSFULVERTEX otherwise. In either case, 'searchtri' is set to a handle + /// whose origin is the newly inserted vertex. /// - /// The point 'newvertex' is located. If 'searchtri.tri' is not NULL, + /// The point 'newvertex' is located. If 'searchtri.triangle' is not NULL, /// the search for the containing triangle begins from 'searchtri'. If - /// 'searchtri.tri' is NULL, a full point location procedure is called. + /// 'searchtri.triangle' is NULL, a full point location procedure is called. /// If 'insertvertex' is found inside a triangle, the triangle is split into /// three; if 'insertvertex' lies on an edge, the edge is split in two, - /// thereby splitting the two adjacent triangles into four. Edge flips are - /// used to restore the Delaunay property. If 'insertvertex' lies on an + /// thereby splitting the two adjacent triangles into four. Edge flips are + /// used to restore the Delaunay property. If 'insertvertex' lies on an /// existing vertex, no action is taken, and the value DUPLICATEVERTEX is - /// returned. On return, 'searchtri' is set to a handle whose origin is the + /// returned. On return, 'searchtri' is set to a handle whose origin is the /// existing vertex. /// /// InsertVertex() does not use flip() for reasons of speed; some /// information can be reused from edge flip to edge flip, like the /// locations of subsegments. - /// - /// The point to be inserted. - /// The triangle to start the search. - /// Normally, the parameter 'splitseg' is set to NULL, - /// implying that no subsegment should be split. In this case, if 'insertvertex' + /// + /// Param 'splitseg': Normally, the parameter 'splitseg' is set to NULL, + /// implying that no subsegment should be split. In this case, if 'insertvertex' /// is found to lie on a segment, no action is taken, and the value VIOLATINGVERTEX /// is returned. On return, 'searchtri' is set to a handle whose primary edge is the /// violated subsegment. + /// If the calling routine wishes to split a subsegment by inserting a vertex in it, + /// the parameter 'splitseg' should be that subsegment. In this case, 'searchtri' + /// MUST be the triangle handle reached by pivoting from that subsegment; no point + /// location is done. /// - /// If the calling routine wishes to split a subsegment - /// by inserting a vertex in it, the parameter 'splitseg' should be that subsegment. - /// In this case, 'searchtri' MUST be the triangle handle reached by pivoting - /// from that subsegment; no point location is done. - /// Flags that indicate whether or not there should + /// Param 'segmentflaws': Flags that indicate whether or not there should /// be checks for the creation of encroached subsegments. If a newly inserted /// vertex encroaches upon subsegments, these subsegments are added to the list - /// of subsegments to be split if 'segmentflaws' is set. - /// Flags that indicate whether or not there should be + /// of subsegments to be split if 'segmentflaws' is set. + /// + /// Param 'triflaws': Flags that indicate whether or not there should be /// checks for the creation of bad quality triangles. If bad triangles are - /// created, these are added to the queue if 'triflaws' is set. - /// If a duplicate vertex or violated segment does not prevent the - /// vertex from being inserted, the return value will be ENCROACHINGVERTEX if - /// the vertex encroaches upon a subsegment (and checking is enabled), or - /// SUCCESSFULVERTEX otherwise. In either case, 'searchtri' is set to a handle - /// whose origin is the newly inserted vertex. - internal InsertVertexResult InsertVertex(Vertex newvertex, ref Otri searchtri, ref Osub splitseg, bool segmentflaws, bool triflaws) + /// created, these are added to the queue if 'triflaws' is set. + /// + internal InsertVertexResult InsertVertex(Vertex newvertex, ref Otri searchtri, + ref Osub splitseg, bool segmentflaws, bool triflaws) { Otri horiz = default(Otri); Otri top = default(Otri); @@ -897,7 +899,7 @@ namespace TriangleNet Osub rightsubseg = default(Osub); Osub newsubseg = default(Osub); BadSubseg encroached; - FlipStacker newflip; + //FlipStacker newflip; Vertex first; Vertex leftvertex, rightvertex, botvertex, topvertex, farvertex; Vertex segmentorg, segmentdest; @@ -910,7 +912,7 @@ namespace TriangleNet bool enq; int i; - if (splitseg.ss == null) + if (splitseg.seg == null) { // Find the location of the vertex to be inserted. Check if a good // starting triangle has already been provided by the caller. @@ -921,13 +923,13 @@ namespace TriangleNet horiz.orient = 0; horiz.SymSelf(); // Search for a triangle containing 'newvertex'. - intersect = Locate(newvertex.pt, ref horiz); + intersect = Locate(newvertex, ref horiz); } else { // Start searching from the triangle provided by the caller. searchtri.Copy(ref horiz); - intersect = PreciseLocate(newvertex.pt, ref horiz, true); + intersect = PreciseLocate(newvertex, ref horiz, true); } } else @@ -949,11 +951,11 @@ namespace TriangleNet if ((intersect == LocateResult.OnEdge) || (intersect == LocateResult.Outside)) { // The vertex falls on an edge or boundary. - if (checksegments && (splitseg.ss == null)) + if (checksegments && (splitseg.seg == null)) { // Check whether the vertex falls on a subsegment. horiz.SegPivot(ref brokensubseg); - if (brokensubseg.ss != dummysub) + if (brokensubseg.seg != dummysub) { // The vertex falls on a subsegment, and hence will not be inserted. if (segmentflaws) @@ -1053,7 +1055,7 @@ namespace TriangleNet { botright.SegPivot(ref botrsubseg); - if (botrsubseg.ss != dummysub) + if (botrsubseg.seg != dummysub) { botright.SegDissolve(); newbotright.SegBond(ref botrsubseg); @@ -1062,7 +1064,7 @@ namespace TriangleNet if (mirrorflag) { topright.SegPivot(ref toprsubseg); - if (toprsubseg.ss != dummysub) + if (toprsubseg.seg != dummysub) { topright.SegDissolve(); newtopright.SegBond(ref toprsubseg); @@ -1085,7 +1087,7 @@ namespace TriangleNet newtopright.Bond(ref newbotright); } - if (splitseg.ss != null) + if (splitseg.seg != null) { // Split the subsegment into two. splitseg.SetDest(newvertex); @@ -1093,7 +1095,7 @@ namespace TriangleNet segmentdest = splitseg.SegDest(); splitseg.SymSelf(); splitseg.Pivot(ref rightsubseg); - InsertSubseg(ref newbotright, splitseg.ss.boundary); + InsertSubseg(ref newbotright, splitseg.seg.boundary); newbotright.SegPivot(ref newsubseg); newsubseg.SetSegOrg(segmentorg); newsubseg.SetSegDest(segmentdest); @@ -1105,22 +1107,16 @@ namespace TriangleNet // Transfer the subsegment's boundary marker to the vertex if required. if (newvertex.mark == 0) { - newvertex.mark = splitseg.ss.boundary; + newvertex.mark = splitseg.seg.boundary; } } if (checkquality) { - //poolrestart(&m.flipstackers); TODO - flipstackers.Clear(); + flipstack.Clear(); - lastflip = new FlipStacker(); - lastflip.flippedtri = horiz; - lastflip.prevflip = dummyflip; - - flipstackers.Add(lastflip); // TODO: Could be ok??? - - //throw new Exception("insertvertex: what to do here??"); + flipstack.Push(default(Otri)); // Dummy flip (see UndoVertex) + flipstack.Push(horiz); } // Position 'horiz' on the first edge to check for @@ -1166,17 +1162,17 @@ namespace TriangleNet } // There may be subsegments that need to be bonded - // to the new triangles. + // to the new triangles. if (checksegments) { botleft.SegPivot(ref botlsubseg); - if (botlsubseg.ss != dummysub) + if (botlsubseg.seg != dummysub) { botleft.SegDissolve(); newbotleft.SegBond(ref botlsubseg); } botright.SegPivot(ref botrsubseg); - if (botrsubseg.ss != dummysub) + if (botrsubseg.seg != dummysub) { botright.SegDissolve(); newbotright.SegBond(ref botrsubseg); @@ -1196,24 +1192,17 @@ namespace TriangleNet if (checkquality) { - // poolrestart(&m->flipstackers); TODO - flipstackers.Clear(); - - lastflip = new FlipStacker(); - lastflip.flippedtri = horiz; - lastflip.prevflip = null; - - flipstackers.Add(lastflip); + flipstack.Clear(); + flipstack.Push(horiz); } } // The insertion is successful by default, unless an encroached // subsegment is found. success = InsertVertexResult.Successful; - // Circle around the newly inserted vertex, checking each edge opposite - // it for the Delaunay property. Non-Delaunay edges are flipped. - // 'horiz' is always the edge being checked. 'first' marks where to - // stop circling. + // Circle around the newly inserted vertex, checking each edge opposite it + // for the Delaunay property. Non-Delaunay edges are flipped. 'horiz' is + // always the edge being checked. 'first' marks where to stop circling. first = horiz.Org(); rightvertex = first; leftvertex = horiz.Dest(); @@ -1227,7 +1216,7 @@ namespace TriangleNet { // Check for a subsegment, which cannot be flipped. horiz.SegPivot(ref checksubseg); - if (checksubseg.ss != dummysub) + if (checksubseg.seg != dummysub) { // The edge is a subsegment and cannot be flipped. doflip = false; @@ -1258,17 +1247,17 @@ namespace TriangleNet farvertex = top.Apex(); // In the incremental Delaunay triangulation algorithm, any of // 'leftvertex', 'rightvertex', and 'farvertex' could be vertices - // of the triangular bounding box. These vertices must be + // of the triangular bounding box. These vertices must be // treated as if they are infinitely distant, even though their // "coordinates" are not. if ((leftvertex == infvertex1) || (leftvertex == infvertex2) || (leftvertex == infvertex3)) { - // 'leftvertex' is infinitely distant. Check the convexity of - // the boundary of the triangulation. 'farvertex' might be + // 'leftvertex' is infinitely distant. Check the convexity of + // the boundary of the triangulation. 'farvertex' might be // infinite as well, but trust me, this same condition should // be applied. - doflip = Primitives.CounterClockwise(newvertex.pt, rightvertex.pt, farvertex.pt) > 0.0; + doflip = Primitives.CounterClockwise(newvertex, rightvertex, farvertex) > 0.0; } else if ((rightvertex == infvertex1) || (rightvertex == infvertex2) || @@ -1278,7 +1267,7 @@ namespace TriangleNet // the boundary of the triangulation. 'farvertex' might be // infinite as well, but trust me, this same condition should // be applied. - doflip = Primitives.CounterClockwise(farvertex.pt, leftvertex.pt, newvertex.pt) > 0.0; + doflip = Primitives.CounterClockwise(farvertex, leftvertex, newvertex) > 0.0; } else if ((farvertex == infvertex1) || (farvertex == infvertex2) || @@ -1291,7 +1280,7 @@ namespace TriangleNet else { // Test whether the edge is locally Delaunay. - doflip = Primitives.InCircle(leftvertex.pt, newvertex.pt, rightvertex.pt, farvertex.pt) > 0.0; + doflip = Primitives.InCircle(leftvertex, newvertex, rightvertex, farvertex) > 0.0; } if (doflip) { @@ -1318,7 +1307,7 @@ namespace TriangleNet botleft.SegPivot(ref botlsubseg); botright.SegPivot(ref botrsubseg); topright.SegPivot(ref toprsubseg); - if (toplsubseg.ss == dummysub) + if (toplsubseg.seg == dummysub) { topright.SegDissolve(); } @@ -1326,7 +1315,7 @@ namespace TriangleNet { topright.SegBond(ref toplsubseg); } - if (botlsubseg.ss == dummysub) + if (botlsubseg.seg == dummysub) { topleft.SegDissolve(); } @@ -1334,7 +1323,7 @@ namespace TriangleNet { topleft.SegBond(ref botlsubseg); } - if (botrsubseg.ss == dummysub) + if (botrsubseg.seg == dummysub) { botleft.SegDissolve(); } @@ -1342,7 +1331,7 @@ namespace TriangleNet { botleft.SegBond(ref botrsubseg); } - if (toprsubseg.ss == dummysub) + if (toprsubseg.seg == dummysub) { botright.SegDissolve(); } @@ -1387,17 +1376,11 @@ namespace TriangleNet if (checkquality) { - newflip = new FlipStacker(); - newflip.flippedtri = horiz; - newflip.prevflip = lastflip; - lastflip = newflip; - - flipstackers.Add(newflip); + flipstack.Push(horiz); } - // On the next iterations, consider the two edges that were - // exposed (this is, are now visible to the newly inserted - // vertex) by the edge flip. + // On the next iterations, consider the two edges that were exposed (this + // is, are now visible to the newly inserted vertex) by the edge flip. horiz.LprevSelf(); leftvertex = farvertex; } @@ -1416,11 +1399,11 @@ namespace TriangleNet horiz.LnextSelf(); horiz.Sym(ref testtri); // Check for finishing a complete revolution about the new vertex, or - // falling outside of the triangulation. The latter will happen - // when a vertex is inserted at a boundary. + // falling outside of the triangulation. The latter will happen when + // a vertex is inserted at a boundary. if ((leftvertex == first) || (testtri.triangle == dummytri)) { - // We're done. Return a triangle whose origin is the new vertex. + // We're done. Return a triangle whose origin is the new vertex. horiz.Lnext(ref searchtri); horiz.Lnext(ref recenttri); return success; @@ -1460,7 +1443,7 @@ namespace TriangleNet } // Check if there's already a subsegment here. tri.SegPivot(ref newsubseg); - if (newsubseg.ss == dummysub) + if (newsubseg.seg == dummysub) { // Make new subsegment and initialize its vertices. MakeSubseg(ref newsubseg); @@ -1469,20 +1452,19 @@ namespace TriangleNet newsubseg.SetSegOrg(tridest); newsubseg.SetSegDest(triorg); // Bond new subsegment to the two triangles it is sandwiched between. - // Note that the facing triangle 'oppotri' might be equal to - // 'dummytri' (outer space), but the new subsegment is bonded to it - // all the same. + // Note that the facing triangle 'oppotri' might be equal to 'dummytri' + // (outer space), but the new subsegment is bonded to it all the same. tri.SegBond(ref newsubseg); tri.Sym(ref oppotri); newsubseg.SymSelf(); oppotri.SegBond(ref newsubseg); - newsubseg.ss.boundary = subsegmark; + newsubseg.seg.boundary = subsegmark; } else { - if (newsubseg.ss.boundary == 0) + if (newsubseg.seg.boundary == 0) { - newsubseg.ss.boundary = subsegmark; + newsubseg.seg.boundary = subsegmark; } } } @@ -1491,11 +1473,11 @@ namespace TriangleNet /// Transform two triangles to two different triangles by flipping an edge /// counterclockwise within a quadrilateral. /// - /// + /// Handle to the edge that will be flipped. /// Imagine the original triangles, abc and bad, oriented so that the /// shared edge ab lies in a horizontal plane, with the vertex b on the left - /// and the vertex a on the right. The vertex c lies below the edge, and - /// the vertex d lies above the edge. The 'flipedge' handle holds the edge + /// and the vertex a on the right. The vertex c lies below the edge, and + /// the vertex d lies above the edge. The 'flipedge' handle holds the edge /// ab of triangle abc, and is directed left, from vertex a to vertex b. /// /// The triangles abc and bad are deleted and replaced by the triangles cdb @@ -1547,6 +1529,26 @@ namespace TriangleNet botvertex = flipedge.Apex(); flipedge.Sym(ref top); + // SELF CHECK + + //if (top.triangle == dummytri) + //{ + // logger.Error("Attempt to flip on boundary.", "Mesh.Flip()"); + // flipedge.LnextSelf(); + // return; + //} + + //if (checksegments) + //{ + // flipedge.SegPivot(ref toplsubseg); + // if (toplsubseg.ss != dummysub) + // { + // logger.Error("Attempt to flip a segment.", "Mesh.Flip()"); + // flipedge.LnextSelf(); + // return; + // } + //} + farvertex = top.Apex(); // Identify the casing of the quadrilateral. @@ -1571,7 +1573,8 @@ namespace TriangleNet botleft.SegPivot(ref botlsubseg); botright.SegPivot(ref botrsubseg); topright.SegPivot(ref toprsubseg); - if (toplsubseg.ss == Mesh.dummysub) + + if (toplsubseg.seg == Mesh.dummysub) { topright.SegDissolve(); } @@ -1579,7 +1582,8 @@ namespace TriangleNet { topright.SegBond(ref toplsubseg); } - if (botlsubseg.ss == Mesh.dummysub) + + if (botlsubseg.seg == Mesh.dummysub) { topleft.SegDissolve(); } @@ -1587,7 +1591,8 @@ namespace TriangleNet { topleft.SegBond(ref botlsubseg); } - if (botrsubseg.ss == Mesh.dummysub) + + if (botrsubseg.seg == Mesh.dummysub) { botleft.SegDissolve(); } @@ -1595,7 +1600,8 @@ namespace TriangleNet { botleft.SegBond(ref botrsubseg); } - if (toprsubseg.ss == Mesh.dummysub) + + if (toprsubseg.seg == Mesh.dummysub) { botright.SegDissolve(); } @@ -1616,35 +1622,17 @@ namespace TriangleNet /// /// Transform two triangles to two different triangles by flipping an edge - /// clockwise within a quadrilateral. Reverses the flip() operation so that + /// clockwise within a quadrilateral. Reverses the flip() operation so that /// the data structures representing the triangles are back where they were /// before the flip(). /// /// /// - /// Imagine the original triangles, abc and bad, oriented so that the - /// shared edge ab lies in a horizontal plane, with the vertex b on the left - /// and the vertex a on the right. The vertex c lies below the edge, and - /// the vertex d lies above the edge. The 'flipedge' handle holds the edge - /// ab of triangle abc, and is directed left, from vertex a to vertex b. - /// - /// The triangles abc and bad are deleted and replaced by the triangles cdb - /// and dca. The triangles that represent abc and bad are NOT deallocated; - /// they are reused for cdb and dca, respectively. Hence, any handles that - /// may have held the original triangles are still valid, although not - /// directed as they were before. + /// See above Flip() remarks for more information. /// /// Upon completion of this routine, the 'flipedge' handle holds the edge /// cd of triangle cdb, and is directed up, from vertex c to vertex d. /// (Hence, the two triangles have rotated clockwise.) - /// - /// WARNING: This transformation is geometrically valid only if the - /// quadrilateral adbc is convex. Furthermore, this transformation is - /// valid only if there is not a subsegment between the triangles abc and - /// bad. This routine does not check either of these preconditions, and - /// it is the responsibility of the calling routine to ensure that they are - /// met. If they are not, the streets shall be filled with wailing and - /// gnashing of teeth. /// internal void Unflip(ref Otri flipedge) { @@ -1688,7 +1676,7 @@ namespace TriangleNet botleft.SegPivot(ref botlsubseg); botright.SegPivot(ref botrsubseg); topright.SegPivot(ref toprsubseg); - if (toplsubseg.ss == Mesh.dummysub) + if (toplsubseg.seg == Mesh.dummysub) { botleft.SegDissolve(); } @@ -1696,7 +1684,7 @@ namespace TriangleNet { botleft.SegBond(ref toplsubseg); } - if (botlsubseg.ss == Mesh.dummysub) + if (botlsubseg.seg == Mesh.dummysub) { botright.SegDissolve(); } @@ -1704,7 +1692,7 @@ namespace TriangleNet { botright.SegBond(ref botlsubseg); } - if (botrsubseg.ss == Mesh.dummysub) + if (botrsubseg.seg == Mesh.dummysub) { topright.SegDissolve(); } @@ -1712,7 +1700,7 @@ namespace TriangleNet { topright.SegBond(ref botrsubseg); } - if (toprsubseg.ss == Mesh.dummysub) + if (toprsubseg.seg == Mesh.dummysub) { topleft.SegDissolve(); } @@ -1744,14 +1732,14 @@ namespace TriangleNet /// A flag that determines whether the new triangles should /// be tested for quality, and enqueued if they are bad. /// - // This is a conceptually difficult routine. The starting assumption is - // that we have a polygon with n sides. n - 1 of these sides are currently - // represented as edges in the mesh. One side, called the "base", need not + // This is a conceptually difficult routine. The starting assumption is + // that we have a polygon with n sides. n - 1 of these sides are currently + // represented as edges in the mesh. One side, called the "base", need not // be. // // Inside the polygon is a structure I call a "fan", consisting of n - 1 // triangles that share a common origin. For each of these triangles, the - // edge opposite the origin is one of the sides of the polygon. The + // edge opposite the origin is one of the sides of the polygon. The // primary edge of each triangle is the edge directed from the origin to // the destination; note that this is not the same edge that is a side of // the polygon. 'firstedge' is the primary edge of the first triangle. @@ -1803,8 +1791,8 @@ namespace TriangleNet Vertex leftbasevertex, rightbasevertex; Vertex testvertex; Vertex bestvertex; - int bestnumber; - int i; + + int bestnumber = 1; // Identify the base vertices. leftbasevertex = lastedge.Apex(); @@ -1814,13 +1802,13 @@ namespace TriangleNet firstedge.Onext(ref besttri); bestvertex = besttri.Dest(); besttri.Copy(ref testtri); - bestnumber = 1; - for (i = 2; i <= edgecount - 2; i++) + + for (int i = 2; i <= edgecount - 2; i++) { testtri.OnextSelf(); testvertex = testtri.Dest(); // Is this a better vertex? - if (Primitives.InCircle(leftbasevertex.pt, rightbasevertex.pt, bestvertex.pt, testvertex.pt) > 0.0) + if (Primitives.InCircle(leftbasevertex, rightbasevertex, bestvertex, testvertex) > 0.0) { testtri.Copy(ref besttri); bestvertex = testvertex; @@ -1864,12 +1852,12 @@ namespace TriangleNet /// triangulation remains Delaunay. /// /// - /// The origin of 'deltri' is deleted. The union of the triangles adjacent - /// to this vertex is a polygon, for which the Delaunay triangulation is - /// found. Two triangles are removed from the mesh. + /// The origin of 'deltri' is deleted. The union of the triangles + /// adjacent to this vertex is a polygon, for which the Delaunay triangulation + /// is found. Two triangles are removed from the mesh. /// - /// Only interior vertices that do not lie on segments or boundaries may be - /// deleted. + /// Only interior vertices that do not lie on segments or boundaries + /// may be deleted. /// internal void DeleteVertex(ref Otri deltri) { @@ -1914,12 +1902,12 @@ namespace TriangleNet deltri.Bond(ref leftcasing); deltriright.Bond(ref rightcasing); lefttri.SegPivot(ref leftsubseg); - if (leftsubseg.ss != Mesh.dummysub) + if (leftsubseg.seg != Mesh.dummysub) { deltri.SegBond(ref leftsubseg); } righttri.SegPivot(ref rightsubseg); - if (rightsubseg.ss != Mesh.dummysub) + if (rightsubseg.seg != Mesh.dummysub) { deltriright.SegBond(ref rightsubseg); } @@ -1948,7 +1936,8 @@ namespace TriangleNet /// internal void UndoVertex() { - Otri fliptri = default(Otri); + Otri fliptri; + Otri botleft = default(Otri), botright = default(Otri), topright = default(Otri); Otri botlcasing = default(Otri), botrcasing = default(Otri), toprcasing = default(Otri); Otri gluetri = default(Otri); @@ -1957,16 +1946,16 @@ namespace TriangleNet // Walk through the list of transformations (flips and a vertex insertion) // in the reverse of the order in which they were done, and undo them. - while (lastflip != null) + while (flipstack.Count > 0) { // Find a triangle involved in the last unreversed transformation. - fliptri = lastflip.flippedtri; + fliptri = flipstack.Pop(); // We are reversing one of three transformations: a trisection of one // triangle into three (by inserting a vertex in the triangle), a // bisection of two triangles into four (by inserting a vertex in an // edge), or an edge flip. - if (lastflip.prevflip == null) + if (flipstack.Count == 0) { // Restore a triangle that was split into three triangles, // so it is again one triangle. @@ -1992,7 +1981,7 @@ namespace TriangleNet TriangleDealloc(botleft.triangle); TriangleDealloc(botright.triangle); } - else if (lastflip.prevflip == dummyflip) + else if (flipstack.Peek().triangle == null) // Dummy flip { // Restore two triangles that were split into four triangles, // so they are again two triangles. @@ -2026,17 +2015,13 @@ namespace TriangleNet TriangleDealloc(topright.triangle); } - // This is the end of the list, sneakily encoded. - lastflip.prevflip = null; + flipstack.Clear(); } else { // Undo an edge flip. Unflip(ref fliptri); } - - // Go on and process the next transformation. - lastflip = lastflip.prevflip; } } @@ -2050,12 +2035,12 @@ namespace TriangleNet /// /// /// Traverses all the triangles, and provides each corner of each triangle - /// with a pointer to that triangle. Of course, pointers will be - /// overwritten by other pointers because (almost) each vertex is a corner - /// of several triangles, but in the end every vertex will point to some - /// triangle that contains it. + /// with a pointer to that triangle. Of course, pointers will be overwritten + /// by other pointers because (almost) each vertex is a corner of several + /// triangles, but in the end every vertex will point to some triangle + /// that contains it. /// - void MakeVertexMap() + private void MakeVertexMap() { Otri tri = default(Otri); Vertex triorg; @@ -2081,24 +2066,24 @@ namespace TriangleNet /// will stop if it tries to walk through a subsegment, and will return OUTSIDE. /// Location information. /// - /// Begins its search from 'searchtri'. It is important that 'searchtri' + /// Begins its search from 'searchtri'. It is important that 'searchtri' /// be a handle with the property that 'searchpoint' is strictly to the left /// of the edge denoted by 'searchtri', or is collinear with that edge and - /// does not intersect that edge. (In particular, 'searchpoint' should not + /// does not intersect that edge. (In particular, 'searchpoint' should not /// be the origin or destination of that edge.) /// /// These conditions are imposed because preciselocate() is normally used in /// one of two situations: /// /// (1) To try to find the location to insert a new point. Normally, we - /// know an edge that the point is strictly to the left of. In the + /// know an edge that the point is strictly to the left of. In the /// incremental Delaunay algorithm, that edge is a bounding box edge. /// In Ruppert's Delaunay refinement algorithm for quality meshing, /// that edge is the shortest edge of the triangle whose circumcenter /// is being inserted. /// /// (2) To try to find an existing point. In this case, any edge on the - /// convex hull is a good starting edge. You must screen out the + /// convex hull is a good starting edge. You must screen out the /// possibility that the vertex sought is an endpoint of the starting /// edge before you call preciselocate(). /// @@ -2106,31 +2091,31 @@ namespace TriangleNet /// /// This implementation differs from that given by Guibas and Stolfi. It /// walks from triangle to triangle, crossing an edge only if 'searchpoint' - /// is on the other side of the line containing that edge. After entering + /// is on the other side of the line containing that edge. After entering /// a triangle, there are two edges by which one can leave that triangle. /// If both edges are valid ('searchpoint' is on the other side of both /// edges), one of the two is chosen by drawing a line perpendicular to /// the entry edge (whose endpoints are 'forg' and 'fdest') passing through - /// 'fapex'. Depending on which side of this perpendicular 'searchpoint' + /// 'fapex'. Depending on which side of this perpendicular 'searchpoint' /// falls on, an exit edge is chosen. /// /// This implementation is empirically faster than the Guibas and Stolfi /// point location routine (which I originally used), which tends to spiral /// in toward its target. /// - /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri' + /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri' /// is a handle whose origin is the existing vertex. /// - /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a + /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a /// handle whose primary edge is the edge on which the point lies. /// /// Returns INTRIANGLE if the point lies strictly within a triangle. /// 'searchtri' is a handle on the triangle that contains the point. /// - /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a + /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a /// handle whose primary edge the point is to the right of. This might /// occur when the circumcenter of a triangle falls just slightly outside - /// the mesh due to floating-point roundoff error. It also occurs when + /// the mesh due to floating-point roundoff error. It also occurs when /// seeking a hole or region point that a foolish user has placed outside /// the mesh. /// @@ -2138,7 +2123,7 @@ namespace TriangleNet /// not generally work after the holes and concavities have been carved. /// However, it can still be used to find the circumcenter of a triangle, as /// long as the search is begun from the triangle in question. - internal LocateResult PreciseLocate(Point2 searchpoint, ref Otri searchtri, + internal LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri, bool stopatsubsegment) { Otri backtracktri = default(Otri); @@ -2154,17 +2139,17 @@ namespace TriangleNet while (true) { // Check whether the apex is the point we seek. - if ((fapex.pt.X == searchpoint.X) && (fapex.pt.Y == searchpoint.Y)) + if ((fapex.x == searchpoint.X) && (fapex.y == searchpoint.Y)) { searchtri.LprevSelf(); return LocateResult.OnVertex; } // Does the point lie on the other side of the line defined by the // triangle edge opposite the triangle's destination? - destorient = Primitives.CounterClockwise(forg.pt, fapex.pt, searchpoint); + destorient = Primitives.CounterClockwise(forg, fapex, searchpoint); // Does the point lie on the other side of the line defined by the // triangle edge opposite the triangle's origin? - orgorient = Primitives.CounterClockwise(fapex.pt, fdest.pt, searchpoint); + orgorient = Primitives.CounterClockwise(fapex, fdest, searchpoint); if (destorient > 0.0) { if (orgorient > 0.0) @@ -2174,8 +2159,8 @@ namespace TriangleNet // a line perpendicular to the line (forg, fdest) and passing // through 'fapex', and determining which side of this line // 'searchpoint' falls on. - moveleft = (fapex.pt.X - searchpoint.X) * (fdest.pt.X - forg.pt.X) + - (fapex.pt.Y - searchpoint.Y) * (fdest.pt.Y - forg.pt.Y) > 0.0; + moveleft = (fapex.x - searchpoint.X) * (fdest.x - forg.x) + + (fapex.y - searchpoint.Y) * (fdest.y - forg.y) > 0.0; } else { @@ -2206,7 +2191,7 @@ namespace TriangleNet } } - // Move to another triangle. Leave a trace 'backtracktri' in case + // Move to another triangle. Leave a trace 'backtracktri' in case // floating-point roundoff or some such bogey causes us to walk // off a boundary of the triangulation. if (moveleft) @@ -2225,7 +2210,7 @@ namespace TriangleNet { // Check for walking through a subsegment. backtracktri.SegPivot(ref checkedge); - if (checkedge.ss != dummysub) + if (checkedge.seg != dummysub) { // Go back to the last triangle. backtracktri.Copy(ref searchtri); @@ -2253,8 +2238,8 @@ namespace TriangleNet /// /// Searching begins from one of: the input 'searchtri', a recently /// encountered triangle 'recenttri', or from a triangle chosen from a - /// random sample. The choice is made by determining which triangle's - /// origin is closest to the point we are searching for. Normally, + /// random sample. The choice is made by determining which triangle's + /// origin is closest to the point we are searching for. Normally, /// 'searchtri' should be a handle on the convex hull of the triangulation. /// /// Details on the random sampling method can be found in the Mucke, Saias, @@ -2262,39 +2247,37 @@ namespace TriangleNet /// /// On completion, 'searchtri' is a triangle that contains 'searchpoint'. /// - /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri' + /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri' /// is a handle whose origin is the existing vertex. /// - /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a + /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a /// handle whose primary edge is the edge on which the point lies. /// /// Returns INTRIANGLE if the point lies strictly within a triangle. /// 'searchtri' is a handle on the triangle that contains the point. /// - /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a + /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a /// handle whose primary edge the point is to the right of. This might /// occur when the circumcenter of a triangle falls just slightly outside - /// the mesh due to floating-point roundoff error. It also occurs when + /// the mesh due to floating-point roundoff error. It also occurs when /// seeking a hole or region point that a foolish user has placed outside /// the mesh. /// /// WARNING: This routine is designed for convex triangulations, and will /// not generally work after the holes and concavities have been carved. /// - internal LocateResult Locate(Point2 searchpoint, ref Otri searchtri) + internal LocateResult Locate(Point searchpoint, ref Otri searchtri) { Otri sampletri = default(Otri); Vertex torg, tdest; double searchdist, dist; double ahead; - //long samplesperblock, totalsamplesleft, samplesleft; - //long population, totalpopulation; // Record the distance from the suggested starting triangle to the // point we seek. torg = searchtri.Org(); - searchdist = (searchpoint.X - torg.pt.X) * (searchpoint.X - torg.pt.X) + - (searchpoint.Y - torg.pt.Y) * (searchpoint.Y - torg.pt.Y); + searchdist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) + + (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y); // If a recently encountered triangle has been recorded and has not been // deallocated, test it as a good starting point. @@ -2303,13 +2286,13 @@ namespace TriangleNet if (!Otri.IsDead(recenttri.triangle)) { torg = recenttri.Org(); - if ((torg.pt.X == searchpoint.X) && (torg.pt.Y == searchpoint.Y)) + if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y)) { recenttri.Copy(ref searchtri); return LocateResult.OnVertex; } - dist = (searchpoint.X - torg.pt.X) * (searchpoint.X - torg.pt.X) + - (searchpoint.Y - torg.pt.Y) * (searchpoint.Y - torg.pt.Y); + dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) + + (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y); if (dist < searchdist) { recenttri.Copy(ref searchtri); @@ -2328,8 +2311,8 @@ namespace TriangleNet if (!Otri.IsDead(sampletri.triangle)) { torg = sampletri.Org(); - dist = (searchpoint.X - torg.pt.X) * (searchpoint.X - torg.pt.X) + - (searchpoint.Y - torg.pt.Y) * (searchpoint.Y - torg.pt.Y); + dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) + + (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y); if (dist < searchdist) { sampletri.Copy(ref searchtri); @@ -2342,17 +2325,17 @@ namespace TriangleNet torg = searchtri.Org(); tdest = searchtri.Dest(); // Check the starting triangle's vertices. - if ((torg.pt.X == searchpoint.X) && (torg.pt.Y == searchpoint.Y)) + if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y)) { return LocateResult.OnVertex; } - if ((tdest.pt.X == searchpoint.X) && (tdest.pt.Y == searchpoint.Y)) + if ((tdest.x == searchpoint.X) && (tdest.y == searchpoint.Y)) { searchtri.LnextSelf(); return LocateResult.OnVertex; } // Orient 'searchtri' to fit the preconditions of calling preciselocate(). - ahead = Primitives.CounterClockwise(torg.pt, tdest.pt, searchpoint); + ahead = Primitives.CounterClockwise(torg, tdest, searchpoint); if (ahead < 0.0) { // Turn around so that 'searchpoint' is to the left of the @@ -2362,8 +2345,8 @@ namespace TriangleNet else if (ahead == 0.0) { // Check if 'searchpoint' is between 'torg' and 'tdest'. - if (((torg.pt.X < searchpoint.X) == (searchpoint.X < tdest.pt.X)) && - ((torg.pt.Y < searchpoint.Y) == (searchpoint.Y < tdest.pt.Y))) + if (((torg.x < searchpoint.X) == (searchpoint.X < tdest.x)) && + ((torg.y < searchpoint.Y) == (searchpoint.Y < tdest.y))) { return LocateResult.OnEdge; } @@ -2391,7 +2374,7 @@ namespace TriangleNet /// is used to find the direction to move in to get from one point to /// another. /// - FindDirectionResult FindDirection(ref Otri searchtri, Vertex searchpoint) + private FindDirectionResult FindDirection(ref Otri searchtri, Vertex searchpoint) { Otri checktri = default(Otri); Vertex startvertex; @@ -2403,10 +2386,10 @@ namespace TriangleNet rightvertex = searchtri.Dest(); leftvertex = searchtri.Apex(); // Is 'searchpoint' to the left? - leftccw = Primitives.CounterClockwise(searchpoint.pt, startvertex.pt, leftvertex.pt); + leftccw = Primitives.CounterClockwise(searchpoint, startvertex, leftvertex); leftflag = leftccw > 0.0; // Is 'searchpoint' to the right? - rightccw = Primitives.CounterClockwise(startvertex.pt, searchpoint.pt, rightvertex.pt); + rightccw = Primitives.CounterClockwise(startvertex, searchpoint, rightvertex); rightflag = rightccw > 0.0; if (leftflag && rightflag) { @@ -2433,7 +2416,7 @@ namespace TriangleNet } leftvertex = searchtri.Apex(); rightccw = leftccw; - leftccw = Primitives.CounterClockwise(searchpoint.pt, startvertex.pt, leftvertex.pt); + leftccw = Primitives.CounterClockwise(searchpoint, startvertex, leftvertex); leftflag = leftccw > 0.0; } while (rightflag) @@ -2447,7 +2430,7 @@ namespace TriangleNet } rightvertex = searchtri.Dest(); leftccw = rightccw; - rightccw = Primitives.CounterClockwise(startvertex.pt, searchpoint.pt, rightvertex.pt); + rightccw = Primitives.CounterClockwise(startvertex, searchpoint, rightvertex); rightflag = rightccw > 0.0; } if (leftccw == 0.0) @@ -2480,7 +2463,7 @@ namespace TriangleNet /// On completion, splittri is a handle having the newly inserted /// intersection point as its origin, and endpoint1 as its destination. /// - void SegmentIntersection(ref Otri splittri, ref Osub splitsubseg, Vertex endpoint2) + private void SegmentIntersection(ref Otri splittri, ref Osub splitsubseg, Vertex endpoint2) { Osub opposubseg = default(Osub); Vertex endpoint1; @@ -2499,30 +2482,28 @@ namespace TriangleNet torg = splittri.Org(); tdest = splittri.Dest(); // Segment intersection formulae; see the Antonio reference. - tx = tdest.pt.X - torg.pt.X; - ty = tdest.pt.Y - torg.pt.Y; - ex = endpoint2.pt.X - endpoint1.pt.X; - ey = endpoint2.pt.Y - endpoint1.pt.Y; - etx = torg.pt.X - endpoint2.pt.X; - ety = torg.pt.Y - endpoint2.pt.Y; + tx = tdest.x - torg.x; + ty = tdest.y - torg.y; + ex = endpoint2.x - endpoint1.x; + ey = endpoint2.y - endpoint1.y; + etx = torg.x - endpoint2.x; + ety = torg.y - endpoint2.y; denom = ty * ex - tx * ey; if (denom == 0.0) { - logger.Error("Attempt to find intersection of parallel segments.", + logger.Error("Attempt to find intersection of parallel segments.", "Mesh.SegmentIntersection()"); throw new Exception("Attempt to find intersection of parallel segments."); } split = (ey * etx - ex * ety) / denom; // Create the new vertex. - newvertex = new Vertex(nextras); //(vertex) poolalloc(&m.vertices); - vertices.Add(newvertex.Hash, newvertex); - // Interpolate its coordinate and attributes. - //for (i = 0; i < 2 + nextras; i++) { - newvertex.pt.X = torg.pt.X + split * (tdest.pt.X - torg.pt.X); - newvertex.pt.Y = torg.pt.Y + split * (tdest.pt.Y - torg.pt.Y); - - newvertex.mark = splitsubseg.ss.boundary; - newvertex.type = VertexType.InputVertex; + newvertex = new Vertex(torg.x + split * (tdest.x - torg.x), + torg.y + split * (tdest.y - torg.y), splitsubseg.seg.boundary); + newvertex.hash = this.hash_vtx++; + newvertex.id = newvertex.hash; + // TODO: nextras //(vertex) poolalloc(&m.vertices); + // Interpolate its attributes. + vertices.Add(newvertex.hash, newvertex); // Insert the intersection vertex. This should always succeed. success = InsertVertex(newvertex, ref splittri, ref splitsubseg, false, false); @@ -2547,23 +2528,23 @@ namespace TriangleNet { splitsubseg.SetSegOrg(newvertex); splitsubseg.NextSelf(); - } while (splitsubseg.ss != Mesh.dummysub); + } while (splitsubseg.seg != Mesh.dummysub); do { opposubseg.SetSegOrg(newvertex); opposubseg.NextSelf(); - } while (opposubseg.ss != Mesh.dummysub); + } while (opposubseg.seg != Mesh.dummysub); // Inserting the vertex may have caused edge flips. We wish to rediscover // the edge connecting endpoint1 to the new intersection vertex. collinear = FindDirection(ref splittri, endpoint1); rightvertex = splittri.Dest(); leftvertex = splittri.Apex(); - if ((leftvertex.pt.X == endpoint1.pt.X) && (leftvertex.pt.Y == endpoint1.pt.Y)) + if ((leftvertex.x == endpoint1.x) && (leftvertex.y == endpoint1.y)) { splittri.OnextSelf(); } - else if ((rightvertex.pt.X != endpoint1.pt.X) || (rightvertex.pt.Y != endpoint1.pt.Y)) + else if ((rightvertex.x != endpoint1.x) || (rightvertex.y != endpoint1.y)) { logger.Error("Topological inconsistency after splitting a segment.", "Mesh.SegmentIntersection()"); throw new Exception("Topological inconsistency after splitting a segment."); @@ -2579,8 +2560,8 @@ namespace TriangleNet /// /// /// - /// Returns one if the entire segment is successfully inserted, and zero - /// if the job must be finished by conformingedge() or constrainededge(). + /// Returns true if the entire segment is successfully inserted, and false + /// if the job must be finished by ConstrainedEdge(). /// /// If the first triangle on the path has the second endpoint as its /// destination or apex, a subsegment is inserted and the job is done. @@ -2594,7 +2575,7 @@ namespace TriangleNet /// then there is a segment that intersects the segment being inserted. /// Their intersection vertex is inserted, splitting the subsegment. /// - bool ScoutSegment(ref Otri searchtri, Vertex endpoint2, int newmark) + private bool ScoutSegment(ref Otri searchtri, Vertex endpoint2, int newmark) { Otri crosstri = default(Otri); Osub crosssubseg = default(Osub); @@ -2604,11 +2585,11 @@ namespace TriangleNet collinear = FindDirection(ref searchtri, endpoint2); rightvertex = searchtri.Dest(); leftvertex = searchtri.Apex(); - if (((leftvertex.pt.X == endpoint2.pt.X) && (leftvertex.pt.Y == endpoint2.pt.Y)) || - ((rightvertex.pt.X == endpoint2.pt.X) && (rightvertex.pt.Y == endpoint2.pt.Y))) + if (((leftvertex.x == endpoint2.x) && (leftvertex.y == endpoint2.y)) || + ((rightvertex.x == endpoint2.x) && (rightvertex.y == endpoint2.y))) { // The segment is already an edge in the mesh. - if ((leftvertex.pt.X == endpoint2.pt.X) && (leftvertex.pt.Y == endpoint2.pt.Y)) + if ((leftvertex.x == endpoint2.x) && (leftvertex.y == endpoint2.y)) { searchtri.LprevSelf(); } @@ -2639,7 +2620,7 @@ namespace TriangleNet searchtri.Lnext(ref crosstri); crosstri.SegPivot(ref crosssubseg); // Check for a crossing segment. - if (crosssubseg.ss == Mesh.dummysub) + if (crosssubseg.seg == Mesh.dummysub) { return false; } @@ -2655,121 +2636,26 @@ namespace TriangleNet } } - /* - /// - /// Force a segment into a conforming Delaunay triangulation by inserting a - /// vertex at its midpoint, and recursively forcing in the two half-segments - /// if necessary. - /// - /// - /// - /// - /// - /// Generates a sequence of subsegments connecting 'endpoint1' to - /// 'endpoint2'. 'newmark' is the boundary marker of the segment, assigned - /// to each new splitting vertex and subsegment. - /// - /// Note that conformingedge() does not always maintain the conforming - /// Delaunay property. Once inserted, segments are locked into place; - /// vertices inserted later (to force other segments in) may render these - /// fixed segments non-Delaunay. The conforming Delaunay property will be - /// restored by enforcequality() by splitting encroached subsegments. - /// - void ConformingEdge(Vertex endpoint1, Vertex endpoint2, int newmark) - { - Otri searchtri1 = default(Otri), searchtri2 = default(Otri); - Osub brokensubseg = default(Osub); - Vertex newvertex; - Vertex midvertex1, midvertex2; - InsertVertexResult success; - int i; - - // Create a new vertex to insert in the middle of the segment. - //newvertex = (vertex) poolalloc(&m.vertices); - newvertex = new Vertex(nextras); - vertices.Add(newvertex.Hash, newvertex); - - // Interpolate coordinates and attributes. - for (i = 0; i < 2 + nextras; i++) - { - //newvertex.pt[i] = 0.5 * (endpoint1.pt[i] + endpoint2.pt[i]); - } - newvertex.mark = newmark; - newvertex.type = VertexType.SegmentVertex; - // No known triangle to search from. - searchtri1.triangle = Mesh.dummytri; - // Attempt to insert the new vertex. - Osub tmp = default(Osub); - success = InsertVertex(newvertex, ref searchtri1, ref tmp, false, false); - if (success == InsertVertexResult.Duplicate) - { - // Segment intersects existing vertex. Use the vertex that's already there. - VertexDealloc(newvertex); - newvertex = searchtri1.Org(); - } - else - { - if (success == InsertVertexResult.Violating) - { - // Two segments intersect. By fluke, we've landed right on another segment. Split it. - searchtri1.SegPivot(ref brokensubseg); - success = InsertVertex(newvertex, ref searchtri1, ref brokensubseg, false, false); - if (success != InsertVertexResult.Successful) - { - logger.Error("Failure to split a segment.", "Mesh.ConformingEdge()"); - throw new Exception("Failure to split a segment."); - } - } - // The vertex has been inserted successfully. - if (steinerleft > 0) - { - steinerleft--; - } - } - searchtri1.Copy(ref searchtri2); - // 'searchtri1' and 'searchtri2' are fastened at their origins to - // 'newvertex', and will be directed toward 'endpoint1' and 'endpoint2' - // respectively. First, we must get 'searchtri2' out of the way so it - // won't be invalidated during the insertion of the first half of the - // segment. - FindDirection(ref searchtri2, endpoint2); - if (!ScoutSegment(ref searchtri1, endpoint1, newmark)) - { - // The origin of searchtri1 may have changed if a collision with an - // intervening vertex on the segment occurred. - midvertex1 = searchtri1.Org(); - ConformingEdge(midvertex1, endpoint1, newmark); - } - if (!ScoutSegment(ref searchtri2, endpoint2, newmark)) - { - // The origin of searchtri2 may have changed if a collision with an - // intervening vertex on the segment occurred. - midvertex2 = searchtri2.Org(); - ConformingEdge(midvertex2, endpoint2, newmark); - } - } - * */ - /// /// Enforce the Delaunay condition at an edge, fanning out recursively from /// an existing vertex. Pay special attention to stacking inverted triangles. /// /// - /// indicates whether or not fixuptri is to the left of - /// the segment being inserted. (Imagine that the segment is pointing up from + /// Indicates whether or not fixuptri is to the left of + /// the segment being inserted. (Imagine that the segment is pointing up from /// endpoint1 to endpoint2.) /// /// This is a support routine for inserting segments into a constrained /// Delaunay triangulation. /// /// The origin of fixuptri is treated as if it has just been inserted, and - /// the local Delaunay condition needs to be enforced. It is only enforced + /// the local Delaunay condition needs to be enforced. It is only enforced /// in one sector, however, that being the angular range defined by /// fixuptri. /// /// This routine also needs to make decisions regarding the "stacking" of - /// triangles. (Read the description of constrainededge() below before - /// reading on here, so you understand the algorithm.) If the position of + /// triangles. (Read the description of ConstrainedEdge() below before + /// reading on here, so you understand the algorithm.) If the position of /// the new vertex (the origin of fixuptri) indicates that the vertex before /// it on the polygon is a reflex vertex, then "stack" the triangle by /// doing nothing. (fixuptri is an inverted triangle, which is how stacked @@ -2777,7 +2663,7 @@ namespace TriangleNet /// /// Otherwise, check whether the vertex before that was a reflex vertex. /// If so, perform an edge flip, thereby eliminating an inverted triangle - /// (popping it off the stack). The edge flip may result in the creation + /// (popping it off the stack). The edge flip may result in the creation /// of a new inverted triangle, depending on whether or not the new vertex /// is visible to the vertex three edges behind on the polygon. /// @@ -2785,7 +2671,7 @@ namespace TriangleNet /// vertices, fixuptri and fartri, the triangle opposite it, are not /// inverted; hence, ensure that the edge between them is locally Delaunay. /// - void DelaunayFixup(ref Otri fixuptri, bool leftside) + private void DelaunayFixup(ref Otri fixuptri, bool leftside) { Otri neartri = default(Otri); Otri fartri = default(Otri); @@ -2800,7 +2686,7 @@ namespace TriangleNet return; } neartri.SegPivot(ref faredge); - if (faredge.ss != Mesh.dummysub) + if (faredge.seg != Mesh.dummysub) { return; } @@ -2812,7 +2698,7 @@ namespace TriangleNet // Check whether the previous polygon vertex is a reflex vertex. if (leftside) { - if (Primitives.CounterClockwise(nearvertex.pt, leftvertex.pt, farvertex.pt) <= 0.0) + if (Primitives.CounterClockwise(nearvertex, leftvertex, farvertex) <= 0.0) { // leftvertex is a reflex vertex too. Nothing can // be done until a convex section is found. @@ -2821,20 +2707,20 @@ namespace TriangleNet } else { - if (Primitives.CounterClockwise(farvertex.pt, rightvertex.pt, nearvertex.pt) <= 0.0) + if (Primitives.CounterClockwise(farvertex, rightvertex, nearvertex) <= 0.0) { // rightvertex is a reflex vertex too. Nothing can // be done until a convex section is found. return; } } - if (Primitives.CounterClockwise(rightvertex.pt, leftvertex.pt, farvertex.pt) > 0.0) + if (Primitives.CounterClockwise(rightvertex, leftvertex, farvertex) > 0.0) { // fartri is not an inverted triangle, and farvertex is not a reflex // vertex. As there are no reflex vertices, fixuptri isn't an // inverted triangle, either. Hence, test the edge between the // triangles to ensure it is locally Delaunay. - if (Primitives.InCircle(leftvertex.pt, farvertex.pt, rightvertex.pt, nearvertex.pt) <= 0.0) + if (Primitives.InCircle(leftvertex, farvertex, rightvertex, nearvertex) <= 0.0) { return; } @@ -2862,23 +2748,23 @@ namespace TriangleNet /// boundary marker of the segment. /// /// To insert a segment, every triangle whose interior intersects the - /// segment is deleted. The union of these deleted triangles is a polygon + /// segment is deleted. The union of these deleted triangles is a polygon /// (which is not necessarily monotone, but is close enough), which is - /// divided into two polygons by the new segment. This routine's task is + /// divided into two polygons by the new segment. This routine's task is /// to generate the Delaunay triangulation of these two polygons. /// /// You might think of this routine's behavior as a two-step process. The /// first step is to walk from endpoint1 to endpoint2, flipping each edge /// encountered. This step creates a fan of edges connected to endpoint1, - /// including the desired edge to endpoint2. The second step enforces the + /// including the desired edge to endpoint2. The second step enforces the /// Delaunay condition on each side of the segment in an incremental manner: /// proceeding along the polygon from endpoint1 to endpoint2 (this is done /// independently on each side of the segment), each vertex is "enforced" /// as if it had just been inserted, but affecting only the previous - /// vertices. The result is the same as if the vertices had been inserted + /// vertices. The result is the same as if the vertices had been inserted /// in the order they appear on the polygon, so the result is Delaunay. /// - /// In truth, constrainededge() interleaves these two steps. The procedure + /// In truth, ConstrainedEdge() interleaves these two steps. The procedure /// walks from endpoint1 to endpoint2, and each time an edge is encountered /// and flipped, the newly exposed vertex (at the far end of the flipped /// edge) is "enforced" upon the previously flipped edges, usually affecting @@ -2888,21 +2774,21 @@ namespace TriangleNet /// The algorithm is complicated by the need to handle polygons that are not /// convex. Although the polygon is not necessarily monotone, it can be /// triangulated in a manner similar to the stack-based algorithms for - /// monotone polygons. For each reflex vertex (local concavity) of the + /// monotone polygons. For each reflex vertex (local concavity) of the /// polygon, there will be an inverted triangle formed by one of the edge - /// flips. (An inverted triangle is one with negative area - that is, its + /// flips. (An inverted triangle is one with negative area - that is, its /// vertices are arranged in clockwise order - and is best thought of as a /// wrinkle in the fabric of the mesh.) Each inverted triangle can be /// thought of as a reflex vertex pushed on the stack, waiting to be fixed /// later. /// /// A reflex vertex is popped from the stack when a vertex is inserted that - /// is visible to the reflex vertex. (However, if the vertex behind the + /// is visible to the reflex vertex. (However, if the vertex behind the /// reflex vertex is not visible to the reflex vertex, a new inverted - /// triangle will take its place on the stack.) These details are handled - /// by the delaunayfixup() routine above. + /// triangle will take its place on the stack.) These details are handled + /// by the DelaunayFixup() routine above. /// - void ConstrainedEdge(ref Otri starttri, Vertex endpoint2, int newmark) + private void ConstrainedEdge(ref Otri starttri, Vertex endpoint2, int newmark) { Otri fixuptri = default(Otri), fixuptri2 = default(Otri); Osub crosssubseg = default(Osub); @@ -2923,8 +2809,8 @@ namespace TriangleNet { farvertex = fixuptri.Org(); // 'farvertex' is the extreme point of the polygon we are "digging" - // to get from endpoint1 to endpoint2. - if ((farvertex.pt.X == endpoint2.pt.X) && (farvertex.pt.Y == endpoint2.pt.Y)) + // to get from endpoint1 to endpoint2. + if ((farvertex.x == endpoint2.x) && (farvertex.y == endpoint2.y)) { fixuptri.Oprev(ref fixuptri2); // Enforce the Delaunay condition around endpoint2. @@ -2934,10 +2820,9 @@ namespace TriangleNet } else { - // Check whether farvertex is to the left or right of the segment - // being inserted, to decide which edge of fixuptri to dig - // through next. - area = Primitives.CounterClockwise(endpoint1.pt, endpoint2.pt, farvertex.pt); + // Check whether farvertex is to the left or right of the segment being + // inserted, to decide which edge of fixuptri to dig through next. + area = Primitives.CounterClockwise(endpoint1, endpoint2, farvertex); if (area == 0.0) { // We've collided with a vertex between endpoint1 and endpoint2. @@ -2951,7 +2836,8 @@ namespace TriangleNet else { if (area > 0.0) - { // farvertex is to the left of the segment. + { + // farvertex is to the left of the segment. fixuptri.Oprev(ref fixuptri2); // Enforce the Delaunay condition around farvertex, on the // left side of the segment only. @@ -2962,7 +2848,8 @@ namespace TriangleNet fixuptri.LprevSelf(); } else - { // farvertex is to the right of the segment. + { + // farvertex is to the right of the segment. DelaunayFixup(ref fixuptri, false); // Flip the edge that crosses the segment. After the edge is // flipped, one of its endpoints is the fan vertex, and the @@ -2971,7 +2858,7 @@ namespace TriangleNet } // Check for two intersecting segments. fixuptri.SegPivot(ref crosssubseg); - if (crosssubseg.ss == Mesh.dummysub) + if (crosssubseg.seg == Mesh.dummysub) { Flip(ref fixuptri); // May create inverted triangle at left. } @@ -3006,7 +2893,7 @@ namespace TriangleNet /// /// /// - void InsertSegment(Vertex endpoint1, Vertex endpoint2, int newmark) + private void InsertSegment(Vertex endpoint1, Vertex endpoint2, int newmark) { Otri searchtri1 = default(Otri), searchtri2 = default(Otri); Vertex checkvertex = null; @@ -3025,7 +2912,7 @@ namespace TriangleNet searchtri1.orient = 0; searchtri1.SymSelf(); // Search for the segment's first endpoint by point location. - if (Locate(endpoint1.pt, ref searchtri1) != LocateResult.OnVertex) + if (Locate(endpoint1, ref searchtri1) != LocateResult.OnVertex) { logger.Error("Unable to locate PSLG vertex in triangulation.", "Mesh.InsertSegment().1"); throw new Exception("Unable to locate PSLG vertex in triangulation."); @@ -3059,7 +2946,7 @@ namespace TriangleNet searchtri2.orient = 0; searchtri2.SymSelf(); // Search for the segment's second endpoint by point location. - if (Locate(endpoint2.pt, ref searchtri2) != LocateResult.OnVertex) + if (Locate(endpoint2, ref searchtri2) != LocateResult.OnVertex) { logger.Error("Unable to locate PSLG vertex in triangulation.", "Mesh.InsertSegment().2"); throw new Exception("Unable to locate PSLG vertex in triangulation."); @@ -3078,13 +2965,6 @@ namespace TriangleNet // vertex on the segment occurred. endpoint2 = searchtri2.Org(); - //if (Behavior.SplitSeg) - //{ - // // Insert vertices to force the segment into the triangulation. - // ConformingEdge(endpoint1, endpoint2, newmark); - //} - //else - // Insert the segment directly into the triangulation. ConstrainedEdge(ref searchtri1, endpoint2, newmark); } @@ -3092,7 +2972,7 @@ namespace TriangleNet /// /// Cover the convex hull of a triangulation with subsegments. /// - void MarkHull() + private void MarkHull() { Otri hulltri = default(Otri); Otri nexttri = default(Otri); @@ -3127,19 +3007,16 @@ namespace TriangleNet /// /// /// - void FormSkeleton(MeshData data) + private void FormSkeleton(InputGeometry input) { Vertex endpoint1, endpoint2; - bool segmentmarkers; int end1, end2; int boundmarker; - int numberofsegments = data.Segments == null ? 0 : data.Segments.Length; + + this.insegments = 0; if (Behavior.Poly) { - insegments = numberofsegments; - segmentmarkers = data.SegmentMarkers != null; - // If the input vertices are collinear, there is no triangulation, // so don't try to insert segments. if (triangles.Count == 0) @@ -3149,21 +3026,21 @@ namespace TriangleNet // If segments are to be inserted, compute a mapping // from vertices to triangles. - if (insegments > 0) + if (input.HasSegments) { MakeVertexMap(); } boundmarker = 0; + // Read and insert the segments. - for (int i = 0; i < insegments; i++) + foreach (var seg in input.segments) { - end1 = data.Segments[i][0]; - end2 = data.Segments[i][1]; - if (segmentmarkers) - { - boundmarker = data.SegmentMarkers[i]; - } + this.insegments++; + + end1 = seg.P0; + end2 = seg.P1; + boundmarker = seg.Boundary; if ((end1 < 0) || (end1 >= invertices)) { @@ -3185,9 +3062,9 @@ namespace TriangleNet // It should be. The ID gets appropriately set in TransferNodes(). // Find the vertices numbered 'end1' and 'end2'. - endpoint1 = vertices[end1]; // getvertex(end1); - endpoint2 = vertices[end2]; // getvertex(end2); - if ((endpoint1.pt.X == endpoint2.pt.X) && (endpoint1.pt.Y == endpoint2.pt.Y)) + endpoint1 = vertices[end1]; + endpoint2 = vertices[end2]; + if ((endpoint1.x == endpoint2.x) && (endpoint1.y == endpoint2.y)) { if (Behavior.Verbose) { @@ -3201,10 +3078,6 @@ namespace TriangleNet } } } - else - { - insegments = 0; - } if (Behavior.Convex || !Behavior.Poly) { @@ -3223,10 +3096,10 @@ namespace TriangleNet /// internal void TriangleDealloc(Triangle dyingtriangle) { - // Mark the triangle as dead. This makes it possible to detect dead + // Mark the triangle as dead. This makes it possible to detect dead // triangles when traversing the list of all triangles. Otri.Kill(dyingtriangle); - triangles.Remove(dyingtriangle.Hash); + triangles.Remove(dyingtriangle.hash); } /// @@ -3235,22 +3108,22 @@ namespace TriangleNet /// internal void VertexDealloc(Vertex dyingvertex) { - // Mark the vertex as dead. This makes it possible to detect dead + // Mark the vertex as dead. This makes it possible to detect dead // vertices when traversing the list of all vertices. dyingvertex.type = VertexType.DeadVertex; - vertices.Remove(dyingvertex.Hash); + vertices.Remove(dyingvertex.hash); } /// /// Deallocate space for a subsegment, marking it dead. /// /// - internal void SubsegDealloc(Subseg dyingsubseg) + internal void SubsegDealloc(Segment dyingsubseg) { - // Mark the subsegment as dead. This makes it possible to detect dead + // Mark the subsegment as dead. This makes it possible to detect dead // subsegments when traversing the list of all subsegments. Osub.Kill(dyingsubseg); - subsegs.Remove(dyingsubseg.Hash); + subsegs.Remove(dyingsubseg.hash); } #endregion diff --git a/Triangle.NET/Triangle/NewLocation.cs b/Triangle.NET/Triangle/NewLocation.cs index 0967532..c4cf09f 100644 --- a/Triangle.NET/Triangle/NewLocation.cs +++ b/Triangle.NET/Triangle/NewLocation.cs @@ -9,6 +9,8 @@ namespace TriangleNet { using System; using TriangleNet.Data; + using TriangleNet.Geometry; + using TriangleNet.Tools; /// /// Find new Steiner Point locations. @@ -78,12 +80,12 @@ namespace TriangleNet double shortestEdgeDist = 0, middleEdgeDist = 0, longestEdgeDist = 0; // keeps the vertices according to the angle incident to that vertex in a triangle - Point2 smallestAngleCorner, middleAngleCorner, largestAngleCorner; + Point smallestAngleCorner, middleAngleCorner, largestAngleCorner; // keeps the type of orientation if the triangle int orientation = 0; // keeps the coordinates of circumcenter of itself and neighbor triangle circumcenter - Point2 myCircumcenter = default(Point2), neighborCircumcenter = default(Point2); + Point myCircumcenter = default(Point), neighborCircumcenter = default(Point); // keeps if bad triangle is almost good or not int almostGood = 0; @@ -132,17 +134,17 @@ namespace TriangleNet Statistic.CircumcenterCount++; // Compute the circumcenter of the triangle. - xdo = tdest.pt.X - torg.pt.X; - ydo = tdest.pt.Y - torg.pt.Y; - xao = tapex.pt.X - torg.pt.X; - yao = tapex.pt.Y - torg.pt.Y; - xda = tapex.pt.X - tdest.pt.X; - yda = tapex.pt.Y - tdest.pt.Y; + xdo = tdest.x - torg.x; + ydo = tdest.y - torg.y; + xao = tapex.x - torg.x; + yao = tapex.y - torg.y; + xda = tapex.x - tdest.x; + yda = tapex.y - tdest.y; // keeps the square of the distances dodist = xdo * xdo + ydo * ydo; aodist = xao * xao + yao * yao; - dadist = (tdest.pt.X - tapex.pt.X) * (tdest.pt.X - tapex.pt.X) + - (tdest.pt.Y - tapex.pt.Y) * (tdest.pt.Y - tapex.pt.Y); + dadist = (tdest.x - tapex.x) * (tdest.x - tapex.x) + + (tdest.y - tapex.y) * (tdest.y - tapex.y); // checking if the user wanted exact arithmetic or not if (Behavior.NoExact) { @@ -153,7 +155,7 @@ namespace TriangleNet // Use the counterclockwise() routine to ensure a positive (and // reasonably accurate) result, avoiding any possibility of // division by zero. - denominator = 0.5 / Primitives.CounterClockwise(tdest.pt, tapex.pt, torg.pt); + denominator = 0.5 / Primitives.CounterClockwise(tdest, tapex, torg); // Don't count the above as an orientation test. Statistic.CounterClockwiseCount--; } @@ -162,8 +164,8 @@ namespace TriangleNet dy = (xdo * aodist - xao * dodist) * denominator; // for debugging and for keeping circumcenter to use later // coordinate value of the circumcenter - myCircumcenter.X = torg.pt.X + dx; - myCircumcenter.Y = torg.pt.Y + dy; + myCircumcenter.x = torg.x + dx; + myCircumcenter.y = torg.y + dy; delotri = badotri; // save for later ///////////////// FINDING THE ORIENTATION OF TRIANGLE ////////////////// @@ -197,9 +199,9 @@ namespace TriangleNet middleEdgeDist = dadist; longestEdgeDist = dodist; - smallestAngleCorner = tdest.pt; - middleAngleCorner = torg.pt; - largestAngleCorner = tapex.pt; + smallestAngleCorner = tdest; + middleAngleCorner = torg; + largestAngleCorner = tapex; break; case 132: // assign necessary information @@ -213,9 +215,9 @@ namespace TriangleNet middleEdgeDist = dodist; longestEdgeDist = dadist; - smallestAngleCorner = tdest.pt; - middleAngleCorner = tapex.pt; - largestAngleCorner = torg.pt; + smallestAngleCorner = tdest; + middleAngleCorner = tapex; + largestAngleCorner = torg; break; case 213: // assign necessary information @@ -229,9 +231,9 @@ namespace TriangleNet middleEdgeDist = aodist; longestEdgeDist = dodist; - smallestAngleCorner = torg.pt; - middleAngleCorner = tdest.pt; - largestAngleCorner = tapex.pt; + smallestAngleCorner = torg; + middleAngleCorner = tdest; + largestAngleCorner = tapex; break; case 231: // assign necessary information /// smallest angle corner: org @@ -244,9 +246,9 @@ namespace TriangleNet middleEdgeDist = dodist; longestEdgeDist = aodist; - smallestAngleCorner = torg.pt; - middleAngleCorner = tapex.pt; - largestAngleCorner = tdest.pt; + smallestAngleCorner = torg; + middleAngleCorner = tapex; + largestAngleCorner = tdest; break; case 312: // assign necessary information /// smallest angle corner: apex @@ -259,9 +261,9 @@ namespace TriangleNet middleEdgeDist = aodist; longestEdgeDist = dadist; - smallestAngleCorner = tapex.pt; - middleAngleCorner = tdest.pt; - largestAngleCorner = torg.pt; + smallestAngleCorner = tapex; + middleAngleCorner = tdest; + largestAngleCorner = torg; break; case 321: // assign necessary information default: // TODO: is this safe? @@ -275,9 +277,9 @@ namespace TriangleNet middleEdgeDist = dadist; longestEdgeDist = aodist; - smallestAngleCorner = tapex.pt; - middleAngleCorner = torg.pt; - largestAngleCorner = tdest.pt; + smallestAngleCorner = tapex; + middleAngleCorner = torg; + largestAngleCorner = tdest; break; }// end of switch @@ -375,10 +377,10 @@ namespace TriangleNet { Statistic.RelocationCount++; - dx = newloc[0] - torg.pt.X; - dy = newloc[1] - torg.pt.Y; - origin_x = torg.pt.X; // keep for later use - origin_y = torg.pt.Y; + dx = newloc[0] - torg.x; + dy = newloc[1] - torg.y; + origin_x = torg.x; // keep for later use + origin_y = torg.y; switch (relocated) { case 1: @@ -407,24 +409,24 @@ namespace TriangleNet /// compute two possible centers of the petal /// // finding the center // first find the middle point of smallest edge - xMidOfShortestEdge = (middleAngleCorner.X + largestAngleCorner.X) / 2.0; - yMidOfShortestEdge = (middleAngleCorner.Y + largestAngleCorner.Y) / 2.0; + xMidOfShortestEdge = (middleAngleCorner.x + largestAngleCorner.x) / 2.0; + yMidOfShortestEdge = (middleAngleCorner.y + largestAngleCorner.y) / 2.0; // two possible centers - xPetalCtr_1 = xMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.Y - - largestAngleCorner.Y) / Math.Sqrt(shortestEdgeDist); - yPetalCtr_1 = yMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.X - - middleAngleCorner.X) / Math.Sqrt(shortestEdgeDist); + xPetalCtr_1 = xMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y - + largestAngleCorner.y) / Math.Sqrt(shortestEdgeDist); + yPetalCtr_1 = yMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x - + middleAngleCorner.x) / Math.Sqrt(shortestEdgeDist); - xPetalCtr_2 = xMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.Y - - largestAngleCorner.Y) / Math.Sqrt(shortestEdgeDist); - yPetalCtr_2 = yMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.X - - middleAngleCorner.X) / Math.Sqrt(shortestEdgeDist); + xPetalCtr_2 = xMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y - + largestAngleCorner.y) / Math.Sqrt(shortestEdgeDist); + yPetalCtr_2 = yMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x - + middleAngleCorner.x) / Math.Sqrt(shortestEdgeDist); // find the correct circle since there will be two possible circles // calculate the distance to smallest angle corner - dxcenter1 = (xPetalCtr_1 - smallestAngleCorner.X) * (xPetalCtr_1 - smallestAngleCorner.X); - dycenter1 = (yPetalCtr_1 - smallestAngleCorner.Y) * (yPetalCtr_1 - smallestAngleCorner.Y); - dxcenter2 = (xPetalCtr_2 - smallestAngleCorner.X) * (xPetalCtr_2 - smallestAngleCorner.X); - dycenter2 = (yPetalCtr_2 - smallestAngleCorner.Y) * (yPetalCtr_2 - smallestAngleCorner.Y); + dxcenter1 = (xPetalCtr_1 - smallestAngleCorner.x) * (xPetalCtr_1 - smallestAngleCorner.x); + dycenter1 = (yPetalCtr_1 - smallestAngleCorner.y) * (yPetalCtr_1 - smallestAngleCorner.y); + dxcenter2 = (xPetalCtr_2 - smallestAngleCorner.x) * (xPetalCtr_2 - smallestAngleCorner.x); + dycenter2 = (yPetalCtr_2 - smallestAngleCorner.y) * (yPetalCtr_2 - smallestAngleCorner.y); // whichever is closer to smallest angle corner, it must be the center if (dxcenter1 + dycenter1 <= dxcenter2 + dycenter2) @@ -437,8 +439,8 @@ namespace TriangleNet } /// find the third point of the neighbor triangle /// - neighborNotFound = GetNeighborsVertex(m, badotri, middleAngleCorner.X, middleAngleCorner.Y, - smallestAngleCorner.X, smallestAngleCorner.Y, ref thirdPoint, ref neighborotri); + neighborNotFound = GetNeighborsVertex(m, badotri, middleAngleCorner.x, middleAngleCorner.y, + smallestAngleCorner.x, smallestAngleCorner.y, ref thirdPoint, ref neighborotri); /// find the circumcenter of the neighbor triangle /// dxFirstSuggestion = dx; // if we cannot find any appropriate suggestion, we use circumcenter dyFirstSuggestion = dy; @@ -449,26 +451,26 @@ namespace TriangleNet neighborvertex_2 = neighborotri.Dest(); neighborvertex_3 = neighborotri.Apex(); // now calculate neighbor's circumcenter which is the voronoi site - neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1.pt, neighborvertex_2.pt, neighborvertex_3.pt, ref xi_tmp, ref eta_tmp, false); + neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1, neighborvertex_2, neighborvertex_3, ref xi_tmp, ref eta_tmp, false); /// compute petal and Voronoi edge intersection /// // in order to avoid degenerate cases, we need to do a vector based calculation for line - vector_x = (middleAngleCorner.Y - smallestAngleCorner.Y);//(-y, x) - vector_y = smallestAngleCorner.X - middleAngleCorner.X; - vector_x = myCircumcenter.X + vector_x; - vector_y = myCircumcenter.Y + vector_y; + vector_x = (middleAngleCorner.y - smallestAngleCorner.y);//(-y, x) + vector_y = smallestAngleCorner.x - middleAngleCorner.x; + vector_x = myCircumcenter.x + vector_x; + vector_y = myCircumcenter.y + vector_y; // by intersecting bisectors you will end up with the one you want to walk on // then this line and circle should be intersected - CircleLineIntersection(myCircumcenter.X, myCircumcenter.Y, vector_x, vector_y, + CircleLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, xPetalCtr, yPetalCtr, petalRadius, ref p); /// choose the correct intersection point /// // calculate middle point of the longest edge(bisector) - xMidOfLongestEdge = (middleAngleCorner.X + smallestAngleCorner.X) / 2.0; - yMidOfLongestEdge = (middleAngleCorner.Y + smallestAngleCorner.Y) / 2.0; + xMidOfLongestEdge = (middleAngleCorner.x + smallestAngleCorner.x) / 2.0; + yMidOfLongestEdge = (middleAngleCorner.y + smallestAngleCorner.y) / 2.0; // we need to find correct intersection point, since line intersects circle twice isCorrect = ChooseCorrectPoint(xMidOfLongestEdge, yMidOfLongestEdge, p[3], p[4], - myCircumcenter.X, myCircumcenter.Y, isObtuse); + myCircumcenter.x, myCircumcenter.y, isObtuse); // make sure which point is the correct one to be considered if (isCorrect) { @@ -482,8 +484,8 @@ namespace TriangleNet } /// check if there is a Voronoi vertex between before intersection /// // check if the voronoi vertex is between the intersection and circumcenter - PointBetweenPoints(inter_x, inter_y, myCircumcenter.X, myCircumcenter.Y, - neighborCircumcenter.X, neighborCircumcenter.Y, ref voronoiOrInter); + PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, + neighborCircumcenter.x, neighborCircumcenter.y, ref voronoiOrInter); /// determine the point to be suggested /// if (p[0] > 0.0) @@ -492,7 +494,7 @@ namespace TriangleNet // if it returns 1.0 this means we have a voronoi vertex within feasible region if (Math.Abs(voronoiOrInter[0] - 1.0) <= EPS) { - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, neighborCircumcenter.X, neighborCircumcenter.Y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, neighborCircumcenter.x, neighborCircumcenter.y)) { // go back to circumcenter dxFirstSuggestion = dx; @@ -502,30 +504,30 @@ namespace TriangleNet else { // we are not creating a bad triangle // neighbor's circumcenter is suggested - dxFirstSuggestion = voronoiOrInter[2] - torg.pt.X; - dyFirstSuggestion = voronoiOrInter[3] - torg.pt.Y; + dxFirstSuggestion = voronoiOrInter[2] - torg.x; + dyFirstSuggestion = voronoiOrInter[3] - torg.y; } } else { // there is no voronoi vertex between intersection point and circumcenter - if (IsBadTriangleAngle(largestAngleCorner.X, largestAngleCorner.Y, middleAngleCorner.X, middleAngleCorner.Y, inter_x, inter_y)) + if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.y, inter_x, inter_y)) { // if it is inside feasible region, then insert v2 // apply perturbation // find the distance between circumcenter and intersection point - d = Math.Sqrt((inter_x - myCircumcenter.X) * (inter_x - myCircumcenter.X) + - (inter_y - myCircumcenter.Y) * (inter_y - myCircumcenter.Y)); + d = Math.Sqrt((inter_x - myCircumcenter.x) * (inter_x - myCircumcenter.x) + + (inter_y - myCircumcenter.y) * (inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter - ax = myCircumcenter.X - inter_x; - ay = myCircumcenter.Y - inter_y; + ax = myCircumcenter.x - inter_x; + ay = myCircumcenter.y - inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter inter_x = inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); inter_y = inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, inter_x, inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // go back to circumcenter dxFirstSuggestion = dx; @@ -535,26 +537,26 @@ namespace TriangleNet else { // intersection point is suggested - dxFirstSuggestion = inter_x - torg.pt.X; - dyFirstSuggestion = inter_y - torg.pt.Y; + dxFirstSuggestion = inter_x - torg.x; + dyFirstSuggestion = inter_y - torg.y; } } else { // intersection point is suggested - dxFirstSuggestion = inter_x - torg.pt.X; - dyFirstSuggestion = inter_y - torg.pt.Y; + dxFirstSuggestion = inter_x - torg.x; + dyFirstSuggestion = inter_y - torg.y; } } /// if it is an acute triangle, check if it is a good enough location /// // for acute triangle case, we need to check if it is ok to use either of them - if ((smallestAngleCorner.X - myCircumcenter.X) * (smallestAngleCorner.X - myCircumcenter.X) + - (smallestAngleCorner.Y - myCircumcenter.Y) * (smallestAngleCorner.Y - myCircumcenter.Y) > - lengthConst * ((smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)))) + if ((smallestAngleCorner.x - myCircumcenter.x) * (smallestAngleCorner.x - myCircumcenter.x) + + (smallestAngleCorner.y - myCircumcenter.y) * (smallestAngleCorner.y - myCircumcenter.y) > + lengthConst * ((smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)))) { // use circumcenter dxFirstSuggestion = dx; @@ -566,8 +568,8 @@ namespace TriangleNet /// DO THE SAME THING FOR THE OTHER DIRECTION /// /// find the third point of the neighbor triangle /// - neighborNotFound = GetNeighborsVertex(m, badotri, largestAngleCorner.X, largestAngleCorner.Y, - smallestAngleCorner.X, smallestAngleCorner.Y, ref thirdPoint, ref neighborotri); + neighborNotFound = GetNeighborsVertex(m, badotri, largestAngleCorner.x, largestAngleCorner.y, + smallestAngleCorner.x, smallestAngleCorner.y, ref thirdPoint, ref neighborotri); /// find the circumcenter of the neighbor triangle /// dxSecondSuggestion = dx; // if we cannot find any appropriate suggestion, we use circumcenter dySecondSuggestion = dy; @@ -578,29 +580,29 @@ namespace TriangleNet neighborvertex_2 = neighborotri.Dest(); neighborvertex_3 = neighborotri.Apex(); // now calculate neighbor's circumcenter which is the voronoi site - neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1.pt, neighborvertex_2.pt, neighborvertex_3.pt, ref xi_tmp, ref eta_tmp, false); + neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1, neighborvertex_2, neighborvertex_3, ref xi_tmp, ref eta_tmp, false); /// compute petal and Voronoi edge intersection /// // in order to avoid degenerate cases, we need to do a vector based calculation for line - vector_x = (largestAngleCorner.Y - smallestAngleCorner.Y);//(-y, x) - vector_y = smallestAngleCorner.X - largestAngleCorner.X; - vector_x = myCircumcenter.X + vector_x; - vector_y = myCircumcenter.Y + vector_y; + vector_x = (largestAngleCorner.y - smallestAngleCorner.y);//(-y, x) + vector_y = smallestAngleCorner.x - largestAngleCorner.x; + vector_x = myCircumcenter.x + vector_x; + vector_y = myCircumcenter.y + vector_y; // by intersecting bisectors you will end up with the one you want to walk on // then this line and circle should be intersected - CircleLineIntersection(myCircumcenter.X, myCircumcenter.Y, vector_x, vector_y, + CircleLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, xPetalCtr, yPetalCtr, petalRadius, ref p); /// choose the correct intersection point /// // calcuwedgeslate middle point of the longest edge(bisector) - xMidOfMiddleEdge = (largestAngleCorner.X + smallestAngleCorner.X) / 2.0; - yMidOfMiddleEdge = (largestAngleCorner.Y + smallestAngleCorner.Y) / 2.0; + xMidOfMiddleEdge = (largestAngleCorner.x + smallestAngleCorner.x) / 2.0; + yMidOfMiddleEdge = (largestAngleCorner.y + smallestAngleCorner.y) / 2.0; // we need to find correct intersection point, since line intersects circle twice // this direction is always ACUTE isCorrect = ChooseCorrectPoint(xMidOfMiddleEdge, yMidOfMiddleEdge, p[3], p[4], - myCircumcenter.X, myCircumcenter.Y, false/*(isObtuse+1)%2*/); + myCircumcenter.x, myCircumcenter.y, false/*(isObtuse+1)%2*/); // make sure which point is the correct one to be considered if (isCorrect) { @@ -615,8 +617,8 @@ namespace TriangleNet /// check if there is a Voronoi vertex between before intersection /// // check if the voronoi vertex is between the intersection and circumcenter - PointBetweenPoints(inter_x, inter_y, myCircumcenter.X, myCircumcenter.Y, - neighborCircumcenter.X, neighborCircumcenter.Y, ref voronoiOrInter); + PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, + neighborCircumcenter.x, neighborCircumcenter.y, ref voronoiOrInter); /// determine the point to be suggested /// if (p[0] > 0.0) @@ -625,7 +627,7 @@ namespace TriangleNet // if it returns 1.0 this means we have a voronoi vertex within feasible region if (Math.Abs(voronoiOrInter[0] - 1.0) <= EPS) { - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, neighborCircumcenter.X, neighborCircumcenter.Y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, neighborCircumcenter.x, neighborCircumcenter.y)) { // go back to circumcenter dxSecondSuggestion = dx; @@ -635,31 +637,31 @@ namespace TriangleNet else { // we are not creating a bad triangle // neighbor's circumcenter is suggested - dxSecondSuggestion = voronoiOrInter[2] - torg.pt.X; - dySecondSuggestion = voronoiOrInter[3] - torg.pt.Y; + dxSecondSuggestion = voronoiOrInter[2] - torg.x; + dySecondSuggestion = voronoiOrInter[3] - torg.y; } } else { // there is no voronoi vertex between intersection point and circumcenter - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, inter_x, inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // if it is inside feasible region, then insert v2 // apply perturbation // find the distance between circumcenter and intersection point - d = Math.Sqrt((inter_x - myCircumcenter.X) * (inter_x - myCircumcenter.X) + - (inter_y - myCircumcenter.Y) * (inter_y - myCircumcenter.Y)); + d = Math.Sqrt((inter_x - myCircumcenter.x) * (inter_x - myCircumcenter.x) + + (inter_y - myCircumcenter.y) * (inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter - ax = myCircumcenter.X - inter_x; - ay = myCircumcenter.Y - inter_y; + ax = myCircumcenter.x - inter_x; + ay = myCircumcenter.y - inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter inter_x = inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); inter_y = inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, inter_x, inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // go back to circumcenter dxSecondSuggestion = dx; @@ -669,26 +671,26 @@ namespace TriangleNet else { // intersection point is suggested - dxSecondSuggestion = inter_x - torg.pt.X; - dySecondSuggestion = inter_y - torg.pt.Y; + dxSecondSuggestion = inter_x - torg.x; + dySecondSuggestion = inter_y - torg.y; } } else { // intersection point is suggested - dxSecondSuggestion = inter_x - torg.pt.X; - dySecondSuggestion = inter_y - torg.pt.Y; + dxSecondSuggestion = inter_x - torg.x; + dySecondSuggestion = inter_y - torg.y; } } /// if it is an acute triangle, check if it is a good enough location /// // for acute triangle case, we need to check if it is ok to use either of them - if ((smallestAngleCorner.X - myCircumcenter.X) * (smallestAngleCorner.X - myCircumcenter.X) + - (smallestAngleCorner.Y - myCircumcenter.Y) * (smallestAngleCorner.Y - myCircumcenter.Y) > - lengthConst * ((smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)))) + if ((smallestAngleCorner.x - myCircumcenter.x) * (smallestAngleCorner.x - myCircumcenter.x) + + (smallestAngleCorner.y - myCircumcenter.y) * (smallestAngleCorner.y - myCircumcenter.y) > + lengthConst * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)))) { // use circumcenter dxSecondSuggestion = dx; @@ -704,14 +706,14 @@ namespace TriangleNet } else { // acute : consider other direction - if (justAcute * ((smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y))) > - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y))) + if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * + (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -728,13 +730,13 @@ namespace TriangleNet if (relocated <= 0) { - circumcenter.pt.X = torg.pt.X + dx; - circumcenter.pt.Y = torg.pt.Y + dy; + circumcenter.x = torg.x + dx; + circumcenter.y = torg.y + dy; } else { - circumcenter.pt.X = origin_x + dx; - circumcenter.pt.Y = origin_y + dy; + circumcenter.x = origin_x + dx; + circumcenter.y = origin_y + dy; } xi = (yao * dx - xao * dy) * (2.0 * denominator); @@ -774,12 +776,12 @@ namespace TriangleNet double shortestEdgeDist = 0, middleEdgeDist = 0, longestEdgeDist = 0; // keeps the vertices according to the angle incident to that vertex in a triangle - Point2 smallestAngleCorner, middleAngleCorner, largestAngleCorner; + Point smallestAngleCorner, middleAngleCorner, largestAngleCorner; // keeps the type of orientation if the triangle int orientation = 0; // keeps the coordinates of circumcenter of itself and neighbor triangle circumcenter - Point2 myCircumcenter, neighborCircumcenter; + Point myCircumcenter, neighborCircumcenter; // keeps if bad triangle is almost good or not int almostGood = 0; @@ -841,17 +843,17 @@ namespace TriangleNet Statistic.CircumcenterCount++; // Compute the circumcenter of the triangle. - xdo = tdest.pt.X - torg.pt.X; - ydo = tdest.pt.Y - torg.pt.Y; - xao = tapex.pt.X - torg.pt.X; - yao = tapex.pt.Y - torg.pt.Y; - xda = tapex.pt.X - tdest.pt.X; - yda = tapex.pt.Y - tdest.pt.Y; + xdo = tdest.x - torg.x; + ydo = tdest.y - torg.y; + xao = tapex.x - torg.x; + yao = tapex.y - torg.y; + xda = tapex.x - tdest.x; + yda = tapex.y - tdest.y; // keeps the square of the distances dodist = xdo * xdo + ydo * ydo; aodist = xao * xao + yao * yao; - dadist = (tdest.pt.X - tapex.pt.X) * (tdest.pt.X - tapex.pt.X) + - (tdest.pt.Y - tapex.pt.Y) * (tdest.pt.Y - tapex.pt.Y); + dadist = (tdest.x - tapex.x) * (tdest.x - tapex.x) + + (tdest.y - tapex.y) * (tdest.y - tapex.y); // checking if the user wanted exact arithmetic or not if (Behavior.NoExact) { @@ -862,7 +864,7 @@ namespace TriangleNet // Use the counterclockwise() routine to ensure a positive (and // reasonably accurate) result, avoiding any possibility of // division by zero. - denominator = 0.5 / Primitives.CounterClockwise(tdest.pt, tapex.pt, torg.pt); + denominator = 0.5 / Primitives.CounterClockwise(tdest, tapex, torg); // Don't count the above as an orientation test. Statistic.CounterClockwiseCount--; } @@ -871,8 +873,7 @@ namespace TriangleNet dy = (xdo * aodist - xao * dodist) * denominator; // for debugging and for keeping circumcenter to use later // coordinate value of the circumcenter - myCircumcenter.X = torg.pt.X + dx; - myCircumcenter.Y = torg.pt.Y + dy; + myCircumcenter = new Point(torg.x + dx, torg.y + dy); delotri = badotri; // save for later ///////////////// FINDING THE ORIENTATION OF TRIANGLE ////////////////// @@ -906,9 +907,9 @@ namespace TriangleNet middleEdgeDist = dadist; longestEdgeDist = dodist; - smallestAngleCorner = tdest.pt; - middleAngleCorner = torg.pt; - largestAngleCorner = tapex.pt; + smallestAngleCorner = tdest; + middleAngleCorner = torg; + largestAngleCorner = tapex; break; case 132: // assign necessary information @@ -922,9 +923,9 @@ namespace TriangleNet middleEdgeDist = dodist; longestEdgeDist = dadist; - smallestAngleCorner = tdest.pt; - middleAngleCorner = tapex.pt; - largestAngleCorner = torg.pt; + smallestAngleCorner = tdest; + middleAngleCorner = tapex; + largestAngleCorner = torg; break; case 213: // assign necessary information @@ -938,9 +939,9 @@ namespace TriangleNet middleEdgeDist = aodist; longestEdgeDist = dodist; - smallestAngleCorner = torg.pt; - middleAngleCorner = tdest.pt; - largestAngleCorner = tapex.pt; + smallestAngleCorner = torg; + middleAngleCorner = tdest; + largestAngleCorner = tapex; break; case 231: // assign necessary information /// smallest angle corner: org @@ -953,9 +954,9 @@ namespace TriangleNet middleEdgeDist = dodist; longestEdgeDist = aodist; - smallestAngleCorner = torg.pt; - middleAngleCorner = tapex.pt; - largestAngleCorner = tdest.pt; + smallestAngleCorner = torg; + middleAngleCorner = tapex; + largestAngleCorner = tdest; break; case 312: // assign necessary information /// smallest angle corner: apex @@ -968,9 +969,9 @@ namespace TriangleNet middleEdgeDist = aodist; longestEdgeDist = dadist; - smallestAngleCorner = tapex.pt; - middleAngleCorner = tdest.pt; - largestAngleCorner = torg.pt; + smallestAngleCorner = tapex; + middleAngleCorner = tdest; + largestAngleCorner = torg; break; case 321: // assign necessary information default: // TODO: is this safe? @@ -984,9 +985,9 @@ namespace TriangleNet middleEdgeDist = dadist; longestEdgeDist = aodist; - smallestAngleCorner = tapex.pt; - middleAngleCorner = torg.pt; - largestAngleCorner = tdest.pt; + smallestAngleCorner = tapex; + middleAngleCorner = torg; + largestAngleCorner = tdest; break; }// end of switch @@ -1084,10 +1085,10 @@ namespace TriangleNet { Statistic.RelocationCount++; - dx = newloc[0] - torg.pt.X; - dy = newloc[1] - torg.pt.Y; - origin_x = torg.pt.X; // keep for later use - origin_y = torg.pt.Y; + dx = newloc[0] - torg.x; + dy = newloc[1] - torg.y; + origin_x = torg.x; // keep for later use + origin_y = torg.y; switch (relocated) { case 1: @@ -1125,24 +1126,24 @@ namespace TriangleNet /// compute two possible centers of the petal /// // finding the center // first find the middle point of smallest edge - xMidOfShortestEdge = (middleAngleCorner.X + largestAngleCorner.X) / 2.0; - yMidOfShortestEdge = (middleAngleCorner.Y + largestAngleCorner.Y) / 2.0; + xMidOfShortestEdge = (middleAngleCorner.x + largestAngleCorner.x) / 2.0; + yMidOfShortestEdge = (middleAngleCorner.y + largestAngleCorner.y) / 2.0; // two possible centers - xPetalCtr_1 = xMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.Y - - largestAngleCorner.Y) / Math.Sqrt(shortestEdgeDist); - yPetalCtr_1 = yMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.X - - middleAngleCorner.X) / Math.Sqrt(shortestEdgeDist); + xPetalCtr_1 = xMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y - + largestAngleCorner.y) / Math.Sqrt(shortestEdgeDist); + yPetalCtr_1 = yMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x - + middleAngleCorner.x) / Math.Sqrt(shortestEdgeDist); - xPetalCtr_2 = xMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.Y - - largestAngleCorner.Y) / Math.Sqrt(shortestEdgeDist); - yPetalCtr_2 = yMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.X - - middleAngleCorner.X) / Math.Sqrt(shortestEdgeDist); + xPetalCtr_2 = xMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y - + largestAngleCorner.y) / Math.Sqrt(shortestEdgeDist); + yPetalCtr_2 = yMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x - + middleAngleCorner.x) / Math.Sqrt(shortestEdgeDist); // find the correct circle since there will be two possible circles // calculate the distance to smallest angle corner - dxcenter1 = (xPetalCtr_1 - smallestAngleCorner.X) * (xPetalCtr_1 - smallestAngleCorner.X); - dycenter1 = (yPetalCtr_1 - smallestAngleCorner.Y) * (yPetalCtr_1 - smallestAngleCorner.Y); - dxcenter2 = (xPetalCtr_2 - smallestAngleCorner.X) * (xPetalCtr_2 - smallestAngleCorner.X); - dycenter2 = (yPetalCtr_2 - smallestAngleCorner.Y) * (yPetalCtr_2 - smallestAngleCorner.Y); + dxcenter1 = (xPetalCtr_1 - smallestAngleCorner.x) * (xPetalCtr_1 - smallestAngleCorner.x); + dycenter1 = (yPetalCtr_1 - smallestAngleCorner.y) * (yPetalCtr_1 - smallestAngleCorner.y); + dxcenter2 = (xPetalCtr_2 - smallestAngleCorner.x) * (xPetalCtr_2 - smallestAngleCorner.x); + dycenter2 = (yPetalCtr_2 - smallestAngleCorner.y) * (yPetalCtr_2 - smallestAngleCorner.y); // whichever is closer to smallest angle corner, it must be the center if (dxcenter1 + dycenter1 <= dxcenter2 + dycenter2) @@ -1154,8 +1155,8 @@ namespace TriangleNet xPetalCtr = xPetalCtr_2; yPetalCtr = yPetalCtr_2; } /// find the third point of the neighbor triangle /// - neighborNotFound_first = GetNeighborsVertex(m, badotri, middleAngleCorner.X, middleAngleCorner.Y, - smallestAngleCorner.X, smallestAngleCorner.Y, ref thirdPoint, ref neighborotri); + neighborNotFound_first = GetNeighborsVertex(m, badotri, middleAngleCorner.x, middleAngleCorner.y, + smallestAngleCorner.x, smallestAngleCorner.y, ref thirdPoint, ref neighborotri); /// find the circumcenter of the neighbor triangle /// dxFirstSuggestion = dx; // if we cannot find any appropriate suggestion, we use circumcenter dyFirstSuggestion = dy; @@ -1179,7 +1180,7 @@ namespace TriangleNet y_2 = petal_bisector_x * Math.Sin(alpha) + petal_bisector_y * Math.Cos(alpha) + yPetalCtr - xPetalCtr * Math.Sin(alpha) - yPetalCtr * Math.Cos(alpha); // we need to find correct intersection point, since there are two possibilities // weather it is obtuse/acute the one closer to the minimum angle corner is the first direction - isCorrect = ChooseCorrectPoint(x_2, y_2, middleAngleCorner.X, middleAngleCorner.Y, x_1, y_1, true); + isCorrect = ChooseCorrectPoint(x_2, y_2, middleAngleCorner.x, middleAngleCorner.y, x_1, y_1, true); // make sure which point is the correct one to be considered if (isCorrect) { @@ -1197,29 +1198,29 @@ namespace TriangleNet } /// choose the correct intersection point /// // calculate middle point of the longest edge(bisector) - xMidOfLongestEdge = (middleAngleCorner.X + smallestAngleCorner.X) / 2.0; - yMidOfLongestEdge = (middleAngleCorner.Y + smallestAngleCorner.Y) / 2.0; + xMidOfLongestEdge = (middleAngleCorner.x + smallestAngleCorner.x) / 2.0; + yMidOfLongestEdge = (middleAngleCorner.y + smallestAngleCorner.y) / 2.0; // if there is a neighbor triangle if (!neighborNotFound_first) { - neighborvertex_1=neighborotri.Org(); - neighborvertex_2=neighborotri.Dest(); - neighborvertex_3=neighborotri.Apex(); + neighborvertex_1 = neighborotri.Org(); + neighborvertex_2 = neighborotri.Dest(); + neighborvertex_3 = neighborotri.Apex(); // now calculate neighbor's circumcenter which is the voronoi site - neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1.pt, neighborvertex_2.pt, neighborvertex_3.pt, ref xi_tmp, ref eta_tmp, false); + neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1, neighborvertex_2, neighborvertex_3, ref xi_tmp, ref eta_tmp, false); /// compute petal and Voronoi edge intersection /// // in order to avoid degenerate cases, we need to do a vector based calculation for line - vector_x = (middleAngleCorner.Y - smallestAngleCorner.Y);//(-y, x) - vector_y = smallestAngleCorner.X - middleAngleCorner.X; - vector_x = myCircumcenter.X + vector_x; - vector_y = myCircumcenter.Y + vector_y; + vector_x = (middleAngleCorner.y - smallestAngleCorner.y);//(-y, x) + vector_y = smallestAngleCorner.x - middleAngleCorner.x; + vector_x = myCircumcenter.x + vector_x; + vector_y = myCircumcenter.y + vector_y; // by intersecting bisectors you will end up with the one you want to walk on // then this line and circle should be intersected - CircleLineIntersection(myCircumcenter.X, myCircumcenter.Y, vector_x, vector_y, + CircleLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, xPetalCtr, yPetalCtr, petalRadius, ref p); // we need to find correct intersection point, since line intersects circle twice isCorrect = ChooseCorrectPoint(xMidOfLongestEdge, yMidOfLongestEdge, p[3], p[4], - myCircumcenter.X, myCircumcenter.Y, isObtuse); + myCircumcenter.x, myCircumcenter.y, isObtuse); // make sure which point is the correct one to be considered if (isCorrect) { @@ -1233,16 +1234,16 @@ namespace TriangleNet } //----------------------hale new first direction: for slab calculation---------------// // calculate the intersection of angle lines and Voronoi - linepnt1_x = middleAngleCorner.X; - linepnt1_y = middleAngleCorner.Y; + linepnt1_x = middleAngleCorner.x; + linepnt1_y = middleAngleCorner.y; // vector from middleAngleCorner to largestAngleCorner - line_vector_x = largestAngleCorner.X - middleAngleCorner.X; - line_vector_y = largestAngleCorner.Y - middleAngleCorner.Y; + line_vector_x = largestAngleCorner.x - middleAngleCorner.x; + line_vector_y = largestAngleCorner.y - middleAngleCorner.y; // rotate the vector around middleAngleCorner in cw by maxangle degrees linepnt2_x = petal_slab_inter_x_first; linepnt2_y = petal_slab_inter_y_first; // now calculate the intersection of two lines - LineLineIntersection(myCircumcenter.X, myCircumcenter.Y, vector_x, vector_y, linepnt1_x, linepnt1_y, linepnt2_x, linepnt2_y, ref line_p); + LineLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, linepnt1_x, linepnt1_y, linepnt2_x, linepnt2_y, ref line_p); // check if there is a suitable intersection if (line_p[0] > 0.0) { @@ -1253,14 +1254,14 @@ namespace TriangleNet { // for debugging (to make sure) //printf("1) No intersection between two lines!!!\n"); - //printf("(%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f)\n",myCircumcenter.X,myCircumcenter.Y,vector_x,vector_y,linepnt1_x,linepnt1_y,linepnt2_x,linepnt2_y); + //printf("(%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f)\n",myCircumcenter.x,myCircumcenter.y,vector_x,vector_y,linepnt1_x,linepnt1_y,linepnt2_x,linepnt2_y); } //---------------------------------------------------------------------// /// check if there is a Voronoi vertex between before intersection /// // check if the voronoi vertex is between the intersection and circumcenter - PointBetweenPoints(inter_x, inter_y, myCircumcenter.X, myCircumcenter.Y, - neighborCircumcenter.X, neighborCircumcenter.Y, ref voronoiOrInter); + PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, + neighborCircumcenter.x, neighborCircumcenter.y, ref voronoiOrInter); /// determine the point to be suggested /// if (p[0] > 0.0) @@ -1271,46 +1272,46 @@ namespace TriangleNet { //-----------------hale new continues 1------------------// // now check if the line intersection is between cc and voronoi - PointBetweenPoints(voronoiOrInter[2], voronoiOrInter[3], myCircumcenter.X, myCircumcenter.Y, line_inter_x, line_inter_y, ref line_result); + PointBetweenPoints(voronoiOrInter[2], voronoiOrInter[3], myCircumcenter.x, myCircumcenter.y, line_inter_x, line_inter_y, ref line_result); if (Math.Abs(line_result[0] - 1.0) <= EPS && line_p[0] > 0.0) { // check if we can go further by picking the slab line and petal intersection // calculate the distance to the smallest angle corner // check if we create a bad triangle or not - if (((smallestAngleCorner.X - petal_slab_inter_x_first) * (smallestAngleCorner.X - petal_slab_inter_x_first) + - (smallestAngleCorner.Y - petal_slab_inter_y_first) * (smallestAngleCorner.Y - petal_slab_inter_y_first) > - lengthConst * ((smallestAngleCorner.X - line_inter_x) * - (smallestAngleCorner.X - line_inter_x) + - (smallestAngleCorner.Y - line_inter_y) * - (smallestAngleCorner.Y - line_inter_y))) - && (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, petal_slab_inter_x_first, petal_slab_inter_y_first)) + if (((smallestAngleCorner.x - petal_slab_inter_x_first) * (smallestAngleCorner.x - petal_slab_inter_x_first) + + (smallestAngleCorner.y - petal_slab_inter_y_first) * (smallestAngleCorner.y - petal_slab_inter_y_first) > + lengthConst * ((smallestAngleCorner.x - line_inter_x) * + (smallestAngleCorner.x - line_inter_x) + + (smallestAngleCorner.y - line_inter_y) * + (smallestAngleCorner.y - line_inter_y))) + && (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, petal_slab_inter_x_first, petal_slab_inter_y_first)) && MinDistanceToNeighbor(m, petal_slab_inter_x_first, petal_slab_inter_y_first, ref neighborotri) > MinDistanceToNeighbor(m, line_inter_x, line_inter_y, ref neighborotri)) { // /// check the neighbor's vertices also, which one if better //slab and petal intersection is advised - dxFirstSuggestion = petal_slab_inter_x_first - torg.pt.X; - dyFirstSuggestion = petal_slab_inter_y_first - torg.pt.Y; + dxFirstSuggestion = petal_slab_inter_x_first - torg.x; + dyFirstSuggestion = petal_slab_inter_y_first - torg.y; } else { // slab intersection point is further away - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, line_inter_x, line_inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // apply perturbation // find the distance between circumcenter and intersection point - d = Math.Sqrt((line_inter_x - myCircumcenter.X) * (line_inter_x - myCircumcenter.X) + - (line_inter_y - myCircumcenter.Y) * (line_inter_y - myCircumcenter.Y)); + d = Math.Sqrt((line_inter_x - myCircumcenter.x) * (line_inter_x - myCircumcenter.x) + + (line_inter_y - myCircumcenter.y) * (line_inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter - ax = myCircumcenter.X - line_inter_x; - ay = myCircumcenter.Y - line_inter_y; + ax = myCircumcenter.x - line_inter_x; + ay = myCircumcenter.y - line_inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter line_inter_x = line_inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); line_inter_y = line_inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, line_inter_x, line_inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // go back to circumcenter dxFirstSuggestion = dx; @@ -1321,8 +1322,8 @@ namespace TriangleNet else { // intersection point is suggested - dxFirstSuggestion = line_inter_x - torg.pt.X; - dyFirstSuggestion = line_inter_y - torg.pt.Y; + dxFirstSuggestion = line_inter_x - torg.x; + dyFirstSuggestion = line_inter_y - torg.y; } @@ -1332,8 +1333,8 @@ namespace TriangleNet else {// we are not creating a bad triangle // slab intersection is advised - dxFirstSuggestion = line_result[2] - torg.pt.X; - dyFirstSuggestion = line_result[3] - torg.pt.Y; + dxFirstSuggestion = line_result[2] - torg.x; + dyFirstSuggestion = line_result[3] - torg.y; } } @@ -1342,7 +1343,7 @@ namespace TriangleNet else { /// NOW APPLY A BREADTH-FIRST SEARCH ON THE VORONOI - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, neighborCircumcenter.X, neighborCircumcenter.Y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, neighborCircumcenter.x, neighborCircumcenter.y)) { // go back to circumcenter dxFirstSuggestion = dx; @@ -1354,8 +1355,8 @@ namespace TriangleNet { // we are not creating a bad triangle // neighbor's circumcenter is suggested - dxFirstSuggestion = voronoiOrInter[2] - torg.pt.X; - dyFirstSuggestion = voronoiOrInter[3] - torg.pt.Y; + dxFirstSuggestion = voronoiOrInter[2] - torg.x; + dyFirstSuggestion = voronoiOrInter[3] - torg.y; } @@ -1366,43 +1367,43 @@ namespace TriangleNet { // there is no voronoi vertex between intersection point and circumcenter //-----------------hale new continues 2-----------------// // now check if the line intersection is between cc and intersection point - PointBetweenPoints(inter_x, inter_y, myCircumcenter.X, myCircumcenter.Y, line_inter_x, line_inter_y, ref line_result); + PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, line_inter_x, line_inter_y, ref line_result); if (Math.Abs(line_result[0] - 1.0) <= EPS && line_p[0] > 0.0) { // check if we can go further by picking the slab line and petal intersection // calculate the distance to the smallest angle corner - if (((smallestAngleCorner.X - petal_slab_inter_x_first) * (smallestAngleCorner.X - petal_slab_inter_x_first) + - (smallestAngleCorner.Y - petal_slab_inter_y_first) * (smallestAngleCorner.Y - petal_slab_inter_y_first) > - lengthConst * ((smallestAngleCorner.X - line_inter_x) * - (smallestAngleCorner.X - line_inter_x) + - (smallestAngleCorner.Y - line_inter_y) * - (smallestAngleCorner.Y - line_inter_y))) - && (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, petal_slab_inter_x_first, petal_slab_inter_y_first)) + if (((smallestAngleCorner.x - petal_slab_inter_x_first) * (smallestAngleCorner.x - petal_slab_inter_x_first) + + (smallestAngleCorner.y - petal_slab_inter_y_first) * (smallestAngleCorner.y - petal_slab_inter_y_first) > + lengthConst * ((smallestAngleCorner.x - line_inter_x) * + (smallestAngleCorner.x - line_inter_x) + + (smallestAngleCorner.y - line_inter_y) * + (smallestAngleCorner.y - line_inter_y))) + && (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, petal_slab_inter_x_first, petal_slab_inter_y_first)) && MinDistanceToNeighbor(m, petal_slab_inter_x_first, petal_slab_inter_y_first, ref neighborotri) > MinDistanceToNeighbor(m, line_inter_x, line_inter_y, ref neighborotri)) { //slab and petal intersection is advised - dxFirstSuggestion = petal_slab_inter_x_first - torg.pt.X; - dyFirstSuggestion = petal_slab_inter_y_first - torg.pt.Y; + dxFirstSuggestion = petal_slab_inter_x_first - torg.x; + dyFirstSuggestion = petal_slab_inter_y_first - torg.y; } else { // slab intersection point is further away - if (IsBadTriangleAngle(largestAngleCorner.X, largestAngleCorner.Y, middleAngleCorner.X, middleAngleCorner.Y, line_inter_x, line_inter_y)) + if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.y, line_inter_x, line_inter_y)) { // apply perturbation // find the distance between circumcenter and intersection point - d = Math.Sqrt((line_inter_x - myCircumcenter.X) * (line_inter_x - myCircumcenter.X) + - (line_inter_y - myCircumcenter.Y) * (line_inter_y - myCircumcenter.Y)); + d = Math.Sqrt((line_inter_x - myCircumcenter.x) * (line_inter_x - myCircumcenter.x) + + (line_inter_y - myCircumcenter.y) * (line_inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter - ax = myCircumcenter.X - line_inter_x; - ay = myCircumcenter.Y - line_inter_y; + ax = myCircumcenter.x - line_inter_x; + ay = myCircumcenter.y - line_inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter line_inter_x = line_inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); line_inter_y = line_inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, line_inter_x, line_inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // go back to circumcenter dxFirstSuggestion = dx; @@ -1412,8 +1413,8 @@ namespace TriangleNet else { // intersection point is suggested - dxFirstSuggestion = line_inter_x - torg.pt.X; - dyFirstSuggestion = line_inter_y - torg.pt.Y; + dxFirstSuggestion = line_inter_x - torg.x; + dyFirstSuggestion = line_inter_y - torg.y; } @@ -1422,8 +1423,8 @@ namespace TriangleNet else {// we are not creating a bad triangle // slab intersection is advised - dxFirstSuggestion = line_result[2] - torg.pt.X; - dyFirstSuggestion = line_result[3] - torg.pt.Y; + dxFirstSuggestion = line_result[2] - torg.x; + dyFirstSuggestion = line_result[3] - torg.y; } } @@ -1433,24 +1434,24 @@ namespace TriangleNet else { - if (IsBadTriangleAngle(largestAngleCorner.X, largestAngleCorner.Y, middleAngleCorner.X, middleAngleCorner.Y, inter_x, inter_y)) + if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.y, inter_x, inter_y)) { //printf("testtriangle returned false! bad triangle\n"); // if it is inside feasible region, then insert v2 // apply perturbation // find the distance between circumcenter and intersection point - d = Math.Sqrt((inter_x - myCircumcenter.X) * (inter_x - myCircumcenter.X) + - (inter_y - myCircumcenter.Y) * (inter_y - myCircumcenter.Y)); + d = Math.Sqrt((inter_x - myCircumcenter.x) * (inter_x - myCircumcenter.x) + + (inter_y - myCircumcenter.y) * (inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter - ax = myCircumcenter.X - inter_x; - ay = myCircumcenter.Y - inter_y; + ax = myCircumcenter.x - inter_x; + ay = myCircumcenter.y - inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter inter_x = inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); inter_y = inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, inter_x, inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // go back to circumcenter dxFirstSuggestion = dx; @@ -1462,28 +1463,28 @@ namespace TriangleNet else { // intersection point is suggested - dxFirstSuggestion = inter_x - torg.pt.X; - dyFirstSuggestion = inter_y - torg.pt.Y; + dxFirstSuggestion = inter_x - torg.x; + dyFirstSuggestion = inter_y - torg.y; } } else { // intersection point is suggested - dxFirstSuggestion = inter_x - torg.pt.X; - dyFirstSuggestion = inter_y - torg.pt.Y; + dxFirstSuggestion = inter_x - torg.x; + dyFirstSuggestion = inter_y - torg.y; } } } /// if it is an acute triangle, check if it is a good enough location /// // for acute triangle case, we need to check if it is ok to use either of them - if ((smallestAngleCorner.X - myCircumcenter.X) * (smallestAngleCorner.X - myCircumcenter.X) + - (smallestAngleCorner.Y - myCircumcenter.Y) * (smallestAngleCorner.Y - myCircumcenter.Y) > - lengthConst * ((smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)))) + if ((smallestAngleCorner.x - myCircumcenter.x) * (smallestAngleCorner.x - myCircumcenter.x) + + (smallestAngleCorner.y - myCircumcenter.y) * (smallestAngleCorner.y - myCircumcenter.y) > + lengthConst * ((smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)))) { // use circumcenter dxFirstSuggestion = dx; @@ -1496,42 +1497,42 @@ namespace TriangleNet /// DO THE SAME THING FOR THE OTHER DIRECTION /// /// find the third point of the neighbor triangle /// - neighborNotFound_second = GetNeighborsVertex(m, badotri, largestAngleCorner.X, largestAngleCorner.Y, - smallestAngleCorner.X, smallestAngleCorner.Y, ref thirdPoint, ref neighborotri); + neighborNotFound_second = GetNeighborsVertex(m, badotri, largestAngleCorner.x, largestAngleCorner.y, + smallestAngleCorner.x, smallestAngleCorner.y, ref thirdPoint, ref neighborotri); /// find the circumcenter of the neighbor triangle /// dxSecondSuggestion = dx; // if we cannot find any appropriate suggestion, we use circumcenter dySecondSuggestion = dy; /// choose the correct intersection point /// // calculate middle point of the longest edge(bisector) - xMidOfMiddleEdge = (largestAngleCorner.X + smallestAngleCorner.X) / 2.0; - yMidOfMiddleEdge = (largestAngleCorner.Y + smallestAngleCorner.Y) / 2.0; + xMidOfMiddleEdge = (largestAngleCorner.x + smallestAngleCorner.x) / 2.0; + yMidOfMiddleEdge = (largestAngleCorner.y + smallestAngleCorner.y) / 2.0; // if there is a neighbor triangle if (!neighborNotFound_second) { - neighborvertex_1=neighborotri.Org(); - neighborvertex_2=neighborotri.Dest(); - neighborvertex_3=neighborotri.Apex(); + neighborvertex_1 = neighborotri.Org(); + neighborvertex_2 = neighborotri.Dest(); + neighborvertex_3 = neighborotri.Apex(); // now calculate neighbor's circumcenter which is the voronoi site - neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1.pt, neighborvertex_2.pt, neighborvertex_3.pt, ref xi_tmp, ref eta_tmp, false); + neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1, neighborvertex_2, neighborvertex_3, ref xi_tmp, ref eta_tmp, false); /// compute petal and Voronoi edge intersection /// // in order to avoid degenerate cases, we need to do a vector based calculation for line - vector_x = (largestAngleCorner.Y - smallestAngleCorner.Y);//(-y, x) - vector_y = smallestAngleCorner.X - largestAngleCorner.X; - vector_x = myCircumcenter.X + vector_x; - vector_y = myCircumcenter.Y + vector_y; + vector_x = (largestAngleCorner.y - smallestAngleCorner.y);//(-y, x) + vector_y = smallestAngleCorner.x - largestAngleCorner.x; + vector_x = myCircumcenter.x + vector_x; + vector_y = myCircumcenter.y + vector_y; // by intersecting bisectors you will end up with the one you want to walk on // then this line and circle should be intersected - CircleLineIntersection(myCircumcenter.X, myCircumcenter.Y, vector_x, vector_y, + CircleLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, xPetalCtr, yPetalCtr, petalRadius, ref p); // we need to find correct intersection point, since line intersects circle twice // this direction is always ACUTE isCorrect = ChooseCorrectPoint(xMidOfMiddleEdge, yMidOfMiddleEdge, p[3], p[4], - myCircumcenter.X, myCircumcenter.Y, false/*(isObtuse+1)%2*/); + myCircumcenter.x, myCircumcenter.y, false/*(isObtuse+1)%2*/); // make sure which point is the correct one to be considered if (isCorrect) { @@ -1545,16 +1546,16 @@ namespace TriangleNet } //----------------------hale new second direction:for slab calculation---------------// // calculate the intersection of angle lines and Voronoi - linepnt1_x = largestAngleCorner.X; - linepnt1_y = largestAngleCorner.Y; + linepnt1_x = largestAngleCorner.x; + linepnt1_y = largestAngleCorner.y; // vector from largestAngleCorner to middleAngleCorner - line_vector_x = middleAngleCorner.X - largestAngleCorner.X; - line_vector_y = middleAngleCorner.Y - largestAngleCorner.Y; + line_vector_x = middleAngleCorner.x - largestAngleCorner.x; + line_vector_y = middleAngleCorner.y - largestAngleCorner.y; // rotate the vector around largestAngleCorner in ccw by maxangle degrees linepnt2_x = petal_slab_inter_x_second; linepnt2_y = petal_slab_inter_y_second; // now calculate the intersection of two lines - LineLineIntersection(myCircumcenter.X, myCircumcenter.Y, vector_x, vector_y, linepnt1_x, linepnt1_y, linepnt2_x, linepnt2_y, ref line_p); + LineLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, linepnt1_x, linepnt1_y, linepnt2_x, linepnt2_y, ref line_p); // check if there is a suitable intersection if (line_p[0] > 0.0) { @@ -1565,13 +1566,13 @@ namespace TriangleNet { // for debugging (to make sure) //printf("1) No intersection between two lines!!!\n"); - //printf("(%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f)\n",myCircumcenter.X,myCircumcenter.Y,vector_x,vector_y,linepnt1_x,linepnt1_y,linepnt2_x,linepnt2_y); + //printf("(%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f)\n",myCircumcenter.x,myCircumcenter.y,vector_x,vector_y,linepnt1_x,linepnt1_y,linepnt2_x,linepnt2_y); } //---------------------------------------------------------------------// /// check if there is a Voronoi vertex between before intersection /// // check if the voronoi vertex is between the intersection and circumcenter - PointBetweenPoints(inter_x, inter_y, myCircumcenter.X, myCircumcenter.Y, - neighborCircumcenter.X, neighborCircumcenter.Y, ref voronoiOrInter); + PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, + neighborCircumcenter.x, neighborCircumcenter.y, ref voronoiOrInter); /// determine the point to be suggested /// if (p[0] > 0.0) { // there is at least one intersection point @@ -1581,45 +1582,45 @@ namespace TriangleNet { //-----------------hale new continues 1------------------// // now check if the line intersection is between cc and voronoi - PointBetweenPoints(voronoiOrInter[2], voronoiOrInter[3], myCircumcenter.X, myCircumcenter.Y, line_inter_x, line_inter_y, ref line_result); + PointBetweenPoints(voronoiOrInter[2], voronoiOrInter[3], myCircumcenter.x, myCircumcenter.y, line_inter_x, line_inter_y, ref line_result); if (Math.Abs(line_result[0] - 1.0) <= EPS && line_p[0] > 0.0) { // check if we can go further by picking the slab line and petal intersection // calculate the distance to the smallest angle corner // - if (((smallestAngleCorner.X - petal_slab_inter_x_second) * (smallestAngleCorner.X - petal_slab_inter_x_second) + - (smallestAngleCorner.Y - petal_slab_inter_y_second) * (smallestAngleCorner.Y - petal_slab_inter_y_second) > - lengthConst * ((smallestAngleCorner.X - line_inter_x) * - (smallestAngleCorner.X - line_inter_x) + - (smallestAngleCorner.Y - line_inter_y) * - (smallestAngleCorner.Y - line_inter_y))) - && (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, petal_slab_inter_x_second, petal_slab_inter_y_second)) + if (((smallestAngleCorner.x - petal_slab_inter_x_second) * (smallestAngleCorner.x - petal_slab_inter_x_second) + + (smallestAngleCorner.y - petal_slab_inter_y_second) * (smallestAngleCorner.y - petal_slab_inter_y_second) > + lengthConst * ((smallestAngleCorner.x - line_inter_x) * + (smallestAngleCorner.x - line_inter_x) + + (smallestAngleCorner.y - line_inter_y) * + (smallestAngleCorner.y - line_inter_y))) + && (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, petal_slab_inter_x_second, petal_slab_inter_y_second)) && MinDistanceToNeighbor(m, petal_slab_inter_x_second, petal_slab_inter_y_second, ref neighborotri) > MinDistanceToNeighbor(m, line_inter_x, line_inter_y, ref neighborotri)) { // slab and petal intersection is advised - dxSecondSuggestion = petal_slab_inter_x_second - torg.pt.X; - dySecondSuggestion = petal_slab_inter_y_second - torg.pt.Y; + dxSecondSuggestion = petal_slab_inter_x_second - torg.x; + dySecondSuggestion = petal_slab_inter_y_second - torg.y; } else { // slab intersection point is further away - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, line_inter_x, line_inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // apply perturbation // find the distance between circumcenter and intersection point - d = Math.Sqrt((line_inter_x - myCircumcenter.X) * (line_inter_x - myCircumcenter.X) + - (line_inter_y - myCircumcenter.Y) * (line_inter_y - myCircumcenter.Y)); + d = Math.Sqrt((line_inter_x - myCircumcenter.x) * (line_inter_x - myCircumcenter.x) + + (line_inter_y - myCircumcenter.y) * (line_inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter - ax = myCircumcenter.X - line_inter_x; - ay = myCircumcenter.Y - line_inter_y; + ax = myCircumcenter.x - line_inter_x; + ay = myCircumcenter.y - line_inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter line_inter_x = line_inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); line_inter_y = line_inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, line_inter_x, line_inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // go back to circumcenter dxSecondSuggestion = dx; @@ -1630,8 +1631,8 @@ namespace TriangleNet else { // intersection point is suggested - dxSecondSuggestion = line_inter_x - torg.pt.X; - dySecondSuggestion = line_inter_y - torg.pt.Y; + dxSecondSuggestion = line_inter_x - torg.x; + dySecondSuggestion = line_inter_y - torg.y; } @@ -1640,8 +1641,8 @@ namespace TriangleNet else {// we are not creating a bad triangle // slab intersection is advised - dxSecondSuggestion = line_result[2] - torg.pt.X; - dySecondSuggestion = line_result[3] - torg.pt.Y; + dxSecondSuggestion = line_result[2] - torg.x; + dySecondSuggestion = line_result[3] - torg.y; } } @@ -1649,7 +1650,7 @@ namespace TriangleNet } else { - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, neighborCircumcenter.X, neighborCircumcenter.Y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, neighborCircumcenter.x, neighborCircumcenter.y)) { // go back to circumcenter dxSecondSuggestion = dx; @@ -1660,8 +1661,8 @@ namespace TriangleNet else { // we are not creating a bad triangle // neighbor's circumcenter is suggested - dxSecondSuggestion = voronoiOrInter[2] - torg.pt.X; - dySecondSuggestion = voronoiOrInter[3] - torg.pt.Y; + dxSecondSuggestion = voronoiOrInter[2] - torg.x; + dySecondSuggestion = voronoiOrInter[3] - torg.y; } @@ -1671,43 +1672,43 @@ namespace TriangleNet { // there is no voronoi vertex between intersection point and circumcenter //-----------------hale new continues 2-----------------// // now check if the line intersection is between cc and intersection point - PointBetweenPoints(inter_x, inter_y, myCircumcenter.X, myCircumcenter.Y, line_inter_x, line_inter_y, ref line_result); + PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, line_inter_x, line_inter_y, ref line_result); if (Math.Abs(line_result[0] - 1.0) <= EPS && line_p[0] > 0.0) { // check if we can go further by picking the slab line and petal intersection // calculate the distance to the smallest angle corner - if (((smallestAngleCorner.X - petal_slab_inter_x_second) * (smallestAngleCorner.X - petal_slab_inter_x_second) + - (smallestAngleCorner.Y - petal_slab_inter_y_second) * (smallestAngleCorner.Y - petal_slab_inter_y_second) > - lengthConst * ((smallestAngleCorner.X - line_inter_x) * - (smallestAngleCorner.X - line_inter_x) + - (smallestAngleCorner.Y - line_inter_y) * - (smallestAngleCorner.Y - line_inter_y))) - && (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, petal_slab_inter_x_second, petal_slab_inter_y_second)) + if (((smallestAngleCorner.x - petal_slab_inter_x_second) * (smallestAngleCorner.x - petal_slab_inter_x_second) + + (smallestAngleCorner.y - petal_slab_inter_y_second) * (smallestAngleCorner.y - petal_slab_inter_y_second) > + lengthConst * ((smallestAngleCorner.x - line_inter_x) * + (smallestAngleCorner.x - line_inter_x) + + (smallestAngleCorner.y - line_inter_y) * + (smallestAngleCorner.y - line_inter_y))) + && (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, petal_slab_inter_x_second, petal_slab_inter_y_second)) && MinDistanceToNeighbor(m, petal_slab_inter_x_second, petal_slab_inter_y_second, ref neighborotri) > MinDistanceToNeighbor(m, line_inter_x, line_inter_y, ref neighborotri)) { // slab and petal intersection is advised - dxSecondSuggestion = petal_slab_inter_x_second - torg.pt.X; - dySecondSuggestion = petal_slab_inter_y_second - torg.pt.Y; + dxSecondSuggestion = petal_slab_inter_x_second - torg.x; + dySecondSuggestion = petal_slab_inter_y_second - torg.y; } else { // slab intersection point is further away ; - if (IsBadTriangleAngle(largestAngleCorner.X, largestAngleCorner.Y, middleAngleCorner.X, middleAngleCorner.Y, line_inter_x, line_inter_y)) + if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.y, line_inter_x, line_inter_y)) { // apply perturbation // find the distance between circumcenter and intersection point - d = Math.Sqrt((line_inter_x - myCircumcenter.X) * (line_inter_x - myCircumcenter.X) + - (line_inter_y - myCircumcenter.Y) * (line_inter_y - myCircumcenter.Y)); + d = Math.Sqrt((line_inter_x - myCircumcenter.x) * (line_inter_x - myCircumcenter.x) + + (line_inter_y - myCircumcenter.y) * (line_inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter - ax = myCircumcenter.X - line_inter_x; - ay = myCircumcenter.Y - line_inter_y; + ax = myCircumcenter.x - line_inter_x; + ay = myCircumcenter.y - line_inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter line_inter_x = line_inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); line_inter_y = line_inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, line_inter_x, line_inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // go back to circumcenter dxSecondSuggestion = dx; @@ -1717,8 +1718,8 @@ namespace TriangleNet else { // intersection point is suggested - dxSecondSuggestion = line_inter_x - torg.pt.X; - dySecondSuggestion = line_inter_y - torg.pt.Y; + dxSecondSuggestion = line_inter_x - torg.x; + dySecondSuggestion = line_inter_y - torg.y; } @@ -1727,8 +1728,8 @@ namespace TriangleNet else {// we are not creating a bad triangle // slab intersection is advised - dxSecondSuggestion = line_result[2] - torg.pt.X; - dySecondSuggestion = line_result[3] - torg.pt.Y; + dxSecondSuggestion = line_result[2] - torg.x; + dySecondSuggestion = line_result[3] - torg.y; } } @@ -1737,23 +1738,23 @@ namespace TriangleNet } else { - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, inter_x, inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // if it is inside feasible region, then insert v2 // apply perturbation // find the distance between circumcenter and intersection point - d = Math.Sqrt((inter_x - myCircumcenter.X) * (inter_x - myCircumcenter.X) + - (inter_y - myCircumcenter.Y) * (inter_y - myCircumcenter.Y)); + d = Math.Sqrt((inter_x - myCircumcenter.x) * (inter_x - myCircumcenter.x) + + (inter_y - myCircumcenter.y) * (inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter - ax = myCircumcenter.X - inter_x; - ay = myCircumcenter.Y - inter_y; + ax = myCircumcenter.x - inter_x; + ay = myCircumcenter.y - inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter inter_x = inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); inter_y = inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); - if (IsBadTriangleAngle(middleAngleCorner.X, middleAngleCorner.Y, largestAngleCorner.X, largestAngleCorner.Y, inter_x, inter_y)) + if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // go back to circumcenter dxSecondSuggestion = dx; @@ -1764,8 +1765,8 @@ namespace TriangleNet else { // intersection point is suggested - dxSecondSuggestion = inter_x - torg.pt.X; - dySecondSuggestion = inter_y - torg.pt.Y; + dxSecondSuggestion = inter_x - torg.x; + dySecondSuggestion = inter_y - torg.y; } } @@ -1773,8 +1774,8 @@ namespace TriangleNet { // intersection point is suggested - dxSecondSuggestion = inter_x - torg.pt.X; - dySecondSuggestion = inter_y - torg.pt.Y; + dxSecondSuggestion = inter_x - torg.x; + dySecondSuggestion = inter_y - torg.y; } } @@ -1782,12 +1783,12 @@ namespace TriangleNet /// if it is an acute triangle, check if it is a good enough location /// // for acute triangle case, we need to check if it is ok to use either of them - if ((smallestAngleCorner.X - myCircumcenter.X) * (smallestAngleCorner.X - myCircumcenter.X) + - (smallestAngleCorner.Y - myCircumcenter.Y) * (smallestAngleCorner.Y - myCircumcenter.Y) > - lengthConst * ((smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)))) + if ((smallestAngleCorner.x - myCircumcenter.x) * (smallestAngleCorner.x - myCircumcenter.x) + + (smallestAngleCorner.y - myCircumcenter.y) * (smallestAngleCorner.y - myCircumcenter.y) > + lengthConst * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)))) { // use circumcenter dxSecondSuggestion = dx; @@ -1801,14 +1802,14 @@ namespace TriangleNet if (neighborNotFound_first && neighborNotFound_second) { //obtuse: check if the other direction works - if (justAcute * ((smallestAngleCorner.X - (xMidOfMiddleEdge)) * - (smallestAngleCorner.X - (xMidOfMiddleEdge)) + - (smallestAngleCorner.Y - (yMidOfMiddleEdge)) * - (smallestAngleCorner.Y - (yMidOfMiddleEdge))) > - (smallestAngleCorner.X - (xMidOfLongestEdge)) * - (smallestAngleCorner.X - (xMidOfLongestEdge)) + - (smallestAngleCorner.Y - (yMidOfLongestEdge)) * - (smallestAngleCorner.Y - (yMidOfLongestEdge))) + if (justAcute * ((smallestAngleCorner.x - (xMidOfMiddleEdge)) * + (smallestAngleCorner.x - (xMidOfMiddleEdge)) + + (smallestAngleCorner.y - (yMidOfMiddleEdge)) * + (smallestAngleCorner.y - (yMidOfMiddleEdge))) > + (smallestAngleCorner.x - (xMidOfLongestEdge)) * + (smallestAngleCorner.x - (xMidOfLongestEdge)) + + (smallestAngleCorner.y - (yMidOfLongestEdge)) * + (smallestAngleCorner.y - (yMidOfLongestEdge))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -1824,14 +1825,14 @@ namespace TriangleNet else if (neighborNotFound_first) { //obtuse: check if the other direction works - if (justAcute * ((smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y))) > - (smallestAngleCorner.X - (xMidOfLongestEdge)) * - (smallestAngleCorner.X - (xMidOfLongestEdge)) + - (smallestAngleCorner.Y - (yMidOfLongestEdge)) * - (smallestAngleCorner.Y - (yMidOfLongestEdge))) + if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * + (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > + (smallestAngleCorner.x - (xMidOfLongestEdge)) * + (smallestAngleCorner.x - (xMidOfLongestEdge)) + + (smallestAngleCorner.y - (yMidOfLongestEdge)) * + (smallestAngleCorner.y - (yMidOfLongestEdge))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -1847,14 +1848,14 @@ namespace TriangleNet else if (neighborNotFound_second) { //obtuse: check if the other direction works - if (justAcute * ((smallestAngleCorner.X - (xMidOfMiddleEdge)) * - (smallestAngleCorner.X - (xMidOfMiddleEdge)) + - (smallestAngleCorner.Y - (yMidOfMiddleEdge)) * - (smallestAngleCorner.Y - (yMidOfMiddleEdge))) > - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y))) + if (justAcute * ((smallestAngleCorner.x - (xMidOfMiddleEdge)) * + (smallestAngleCorner.x - (xMidOfMiddleEdge)) + + (smallestAngleCorner.y - (yMidOfMiddleEdge)) * + (smallestAngleCorner.y - (yMidOfMiddleEdge))) > + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -1870,14 +1871,14 @@ namespace TriangleNet else { //obtuse: check if the other direction works - if (justAcute * ((smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y))) > - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y))) + if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * + (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -1897,14 +1898,14 @@ namespace TriangleNet if (neighborNotFound_first && neighborNotFound_second) { //obtuse: check if the other direction works - if (justAcute * ((smallestAngleCorner.X - (xMidOfMiddleEdge)) * - (smallestAngleCorner.X - (xMidOfMiddleEdge)) + - (smallestAngleCorner.Y - (yMidOfMiddleEdge)) * - (smallestAngleCorner.Y - (yMidOfMiddleEdge))) > - (smallestAngleCorner.X - (xMidOfLongestEdge)) * - (smallestAngleCorner.X - (xMidOfLongestEdge)) + - (smallestAngleCorner.Y - (yMidOfLongestEdge)) * - (smallestAngleCorner.Y - (yMidOfLongestEdge))) + if (justAcute * ((smallestAngleCorner.x - (xMidOfMiddleEdge)) * + (smallestAngleCorner.x - (xMidOfMiddleEdge)) + + (smallestAngleCorner.y - (yMidOfMiddleEdge)) * + (smallestAngleCorner.y - (yMidOfMiddleEdge))) > + (smallestAngleCorner.x - (xMidOfLongestEdge)) * + (smallestAngleCorner.x - (xMidOfLongestEdge)) + + (smallestAngleCorner.y - (yMidOfLongestEdge)) * + (smallestAngleCorner.y - (yMidOfLongestEdge))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -1920,14 +1921,14 @@ namespace TriangleNet else if (neighborNotFound_first) { //obtuse: check if the other direction works - if (justAcute * ((smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y))) > - (smallestAngleCorner.X - (xMidOfLongestEdge)) * - (smallestAngleCorner.X - (xMidOfLongestEdge)) + - (smallestAngleCorner.Y - (yMidOfLongestEdge)) * - (smallestAngleCorner.Y - (yMidOfLongestEdge))) + if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * + (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > + (smallestAngleCorner.x - (xMidOfLongestEdge)) * + (smallestAngleCorner.x - (xMidOfLongestEdge)) + + (smallestAngleCorner.y - (yMidOfLongestEdge)) * + (smallestAngleCorner.y - (yMidOfLongestEdge))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -1943,14 +1944,14 @@ namespace TriangleNet else if (neighborNotFound_second) { //obtuse: check if the other direction works - if (justAcute * ((smallestAngleCorner.X - (xMidOfMiddleEdge)) * - (smallestAngleCorner.X - (xMidOfMiddleEdge)) + - (smallestAngleCorner.Y - (yMidOfMiddleEdge)) * - (smallestAngleCorner.Y - (yMidOfMiddleEdge))) > - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y))) + if (justAcute * ((smallestAngleCorner.x - (xMidOfMiddleEdge)) * + (smallestAngleCorner.x - (xMidOfMiddleEdge)) + + (smallestAngleCorner.y - (yMidOfMiddleEdge)) * + (smallestAngleCorner.y - (yMidOfMiddleEdge))) > + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -1966,14 +1967,14 @@ namespace TriangleNet else { //obtuse: check if the other direction works - if (justAcute * ((smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxSecondSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dySecondSuggestion + torg.pt.Y))) > - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) * - (smallestAngleCorner.X - (dxFirstSuggestion + torg.pt.X)) + - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y)) * - (smallestAngleCorner.Y - (dyFirstSuggestion + torg.pt.Y))) + if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * + (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * + (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; @@ -1993,13 +1994,13 @@ namespace TriangleNet if (relocated <= 0) { - circumcenter.pt.X = torg.pt.X + dx; - circumcenter.pt.Y = torg.pt.Y + dy; + circumcenter.x = torg.x + dx; + circumcenter.y = torg.y + dy; } else { - circumcenter.pt.X = origin_x + dx; - circumcenter.pt.Y = origin_y + dy; + circumcenter.x = origin_x + dx; + circumcenter.y = origin_y + dy; } xi = (yao * dx - xao * dy) * (2.0 * denominator); eta = (xdo * dy - ydo * dx) * (2.0 * denominator); @@ -2308,28 +2309,28 @@ namespace TriangleNet switch (whichPoint) { case 1: - first_x = p.pt.X; // point at the center - first_y = p.pt.Y; - second_x = r.pt.X; // second vertex of first edge to consider - second_y = r.pt.Y; - third_x = q.pt.X; // for terminating the search - third_y = q.pt.Y; + first_x = p.x; // point at the center + first_y = p.y; + second_x = r.x; // second vertex of first edge to consider + second_y = r.y; + third_x = q.x; // for terminating the search + third_y = q.y; break; case 2: - first_x = q.pt.X; // point at the center - first_y = q.pt.Y; - second_x = p.pt.X; // second vertex of first edge to consider - second_y = p.pt.Y; - third_x = r.pt.X; // for terminating the search - third_y = r.pt.Y; + first_x = q.x; // point at the center + first_y = q.y; + second_x = p.x; // second vertex of first edge to consider + second_y = p.y; + third_x = r.x; // for terminating the search + third_y = r.y; break; case 3: - first_x = r.pt.X; // point at the center - first_y = r.pt.Y; - second_x = q.pt.X; // second vertex of first edge to consider - second_y = q.pt.Y; - third_x = p.pt.X; // for terminating the search - third_y = p.pt.Y; + first_x = r.x; // point at the center + first_y = r.y; + second_x = q.x; // second vertex of first edge to consider + second_y = q.y; + third_x = p.x; // for terminating the search + third_y = p.y; break; } tempotri = badotri; @@ -2416,9 +2417,9 @@ namespace TriangleNet neighborvertex_3 = neighbor.Apex(); // check if it is really a triangle - if ((neighborvertex_1.pt.X == neighborvertex_2.pt.X && neighborvertex_1.pt.Y == neighborvertex_2.pt.Y) - || (neighborvertex_2.pt.X == neighborvertex_3.pt.X && neighborvertex_2.pt.Y == neighborvertex_3.pt.Y) - || (neighborvertex_1.pt.X == neighborvertex_3.pt.X && neighborvertex_1.pt.Y == neighborvertex_3.pt.Y)) + if ((neighborvertex_1.x == neighborvertex_2.x && neighborvertex_1.y == neighborvertex_2.y) + || (neighborvertex_2.x == neighborvertex_3.x && neighborvertex_2.y == neighborvertex_3.y) + || (neighborvertex_1.x == neighborvertex_3.x && neighborvertex_1.y == neighborvertex_3.y)) { //printf("Two vertices are the same!!!!!!!\n"); } @@ -2426,20 +2427,20 @@ namespace TriangleNet { // begin searching for the correct neighbor triangle firstVertexMatched = 0; - if ((Math.Abs(first_x - neighborvertex_1.pt.X) < EPS) && - (Math.Abs(first_y - neighborvertex_1.pt.Y) < EPS)) + if ((Math.Abs(first_x - neighborvertex_1.x) < EPS) && + (Math.Abs(first_y - neighborvertex_1.y) < EPS)) { firstVertexMatched = 11; // neighbor's 1st vertex is matched to first vertex } - else if ((Math.Abs(first_x - neighborvertex_2.pt.X) < EPS) && - (Math.Abs(first_y - neighborvertex_2.pt.Y) < EPS)) + else if ((Math.Abs(first_x - neighborvertex_2.x) < EPS) && + (Math.Abs(first_y - neighborvertex_2.y) < EPS)) { firstVertexMatched = 12; // neighbor's 2nd vertex is matched to first vertex } - else if ((Math.Abs(first_x - neighborvertex_3.pt.X) < EPS) && - (Math.Abs(first_y - neighborvertex_3.pt.Y) < EPS)) + else if ((Math.Abs(first_x - neighborvertex_3.x) < EPS) && + (Math.Abs(first_y - neighborvertex_3.y) < EPS)) { firstVertexMatched = 13; // neighbor's 3rd vertex is matched to first vertex @@ -2448,18 +2449,18 @@ namespace TriangleNet } // end of first vertex matching */ secondVertexMatched = 0; - if ((Math.Abs(second_x - neighborvertex_1.pt.X) < EPS) && - (Math.Abs(second_y - neighborvertex_1.pt.Y) < EPS)) + if ((Math.Abs(second_x - neighborvertex_1.x) < EPS) && + (Math.Abs(second_y - neighborvertex_1.y) < EPS)) { secondVertexMatched = 21; // neighbor's 1st vertex is matched to second vertex } - else if ((Math.Abs(second_x - neighborvertex_2.pt.X) < EPS) && - (Math.Abs(second_y - neighborvertex_2.pt.Y) < EPS)) + else if ((Math.Abs(second_x - neighborvertex_2.x) < EPS) && + (Math.Abs(second_y - neighborvertex_2.y) < EPS)) { secondVertexMatched = 22; // neighbor's 2nd vertex is matched to second vertex } - else if ((Math.Abs(second_x - neighborvertex_3.pt.X) < EPS) && - (Math.Abs(second_y - neighborvertex_3.pt.Y) < EPS)) + else if ((Math.Abs(second_x - neighborvertex_3.x) < EPS) && + (Math.Abs(second_y - neighborvertex_3.y) < EPS)) { secondVertexMatched = 23; // neighbor's 3rd vertex is matched to second vertex }/*else{ @@ -2484,39 +2485,39 @@ namespace TriangleNet case 11: if (secondVertexMatched == 22) { - thirdpoint[0] = neighborvertex_3.pt.X; - thirdpoint[1] = neighborvertex_3.pt.Y; + thirdpoint[0] = neighborvertex_3.x; + thirdpoint[1] = neighborvertex_3.y; } else if (secondVertexMatched == 23) { - thirdpoint[0] = neighborvertex_2.pt.X; - thirdpoint[1] = neighborvertex_2.pt.Y; + thirdpoint[0] = neighborvertex_2.x; + thirdpoint[1] = neighborvertex_2.y; } else { notFound = true; } break; case 12: if (secondVertexMatched == 21) { - thirdpoint[0] = neighborvertex_3.pt.X; - thirdpoint[1] = neighborvertex_3.pt.Y; + thirdpoint[0] = neighborvertex_3.x; + thirdpoint[1] = neighborvertex_3.y; } else if (secondVertexMatched == 23) { - thirdpoint[0] = neighborvertex_1.pt.X; - thirdpoint[1] = neighborvertex_1.pt.Y; + thirdpoint[0] = neighborvertex_1.x; + thirdpoint[1] = neighborvertex_1.y; } else { notFound = true; } break; case 13: if (secondVertexMatched == 21) { - thirdpoint[0] = neighborvertex_2.pt.X; - thirdpoint[1] = neighborvertex_2.pt.Y; + thirdpoint[0] = neighborvertex_2.x; + thirdpoint[1] = neighborvertex_2.y; } else if (secondVertexMatched == 22) { - thirdpoint[0] = neighborvertex_1.pt.X; - thirdpoint[1] = neighborvertex_1.pt.Y; + thirdpoint[0] = neighborvertex_1.x; + thirdpoint[1] = neighborvertex_1.y; } else { notFound = true; } break; @@ -4101,7 +4102,7 @@ namespace TriangleNet double d1, d2, d3, ahead; //triangle ptr; // Temporary variable used by sym(). - Point2 newvertex = new Point2(newlocX, newlocY); + Point newvertex = new Point(newlocX, newlocY); // printf("newvertex %f,%f\n", newvertex[0], newvertex[1]); // Find the location of the vertex to be inserted. Check if a good @@ -4116,13 +4117,13 @@ namespace TriangleNet torg = searchtri.Org(); tdest = searchtri.Dest(); // Check the starting triangle's vertices. - if ((torg.pt.X == newvertex.X) && (torg.pt.Y == newvertex.Y)) + if ((torg.x == newvertex.x) && (torg.y == newvertex.y)) { intersect = LocateResult.OnVertex; searchtri.Copy(ref horiz); } - else if ((tdest.pt.X == newvertex.X) && (tdest.pt.Y == newvertex.Y)) + else if ((tdest.x == newvertex.x) && (tdest.y == newvertex.y)) { searchtri.LnextSelf(); intersect = LocateResult.OnVertex; @@ -4131,7 +4132,7 @@ namespace TriangleNet else { // Orient 'searchtri' to fit the preconditions of calling preciselocate(). - ahead = Primitives.CounterClockwise(torg.pt, tdest.pt, newvertex); + ahead = Primitives.CounterClockwise(torg, tdest, newvertex); if (ahead < 0.0) { // Turn around so that 'searchpoint' is to the left of the @@ -4143,8 +4144,8 @@ namespace TriangleNet else if (ahead == 0.0) { // Check if 'searchpoint' is between 'torg' and 'tdest'. - if (((torg.pt.X < newvertex.X) == (newvertex.X < tdest.pt.X)) && - ((torg.pt.Y < newvertex.Y) == (newvertex.Y < tdest.pt.Y))) + if (((torg.x < newvertex.x) == (newvertex.x < tdest.x)) && + ((torg.y < newvertex.y) == (newvertex.y < tdest.y))) { intersect = LocateResult.OnEdge; searchtri.Copy(ref horiz); @@ -4169,9 +4170,9 @@ namespace TriangleNet v1 = horiz.Org(); v2 = horiz.Dest(); v3 = horiz.Apex(); - d1 = (v1.pt.X - newvertex.X) * (v1.pt.X - newvertex.X) + (v1.pt.Y - newvertex.Y) * (v1.pt.Y - newvertex.Y); - d2 = (v2.pt.X - newvertex.X) * (v2.pt.X - newvertex.X) + (v2.pt.Y - newvertex.Y) * (v2.pt.Y - newvertex.Y); - d3 = (v3.pt.X - newvertex.X) * (v3.pt.X - newvertex.X) + (v3.pt.Y - newvertex.Y) * (v3.pt.Y - newvertex.Y); + d1 = (v1.x - newvertex.x) * (v1.x - newvertex.x) + (v1.y - newvertex.y) * (v1.y - newvertex.y); + d2 = (v2.x - newvertex.x) * (v2.x - newvertex.x) + (v2.y - newvertex.y) * (v2.y - newvertex.y); + d3 = (v3.x - newvertex.x) * (v3.x - newvertex.x) + (v3.y - newvertex.y) * (v3.y - newvertex.y); //m.VertexDealloc(newvertex); // find minimum of the distance if (d1 <= d2 && d1 <= d3) @@ -4188,142 +4189,5 @@ namespace TriangleNet } } } - - /* - /// - /// Finds min angle of a triangle for .part file. - /// - /// - /// - /// - /// - /// - /// - /// - static double ReturnMinAngle(double p1x, double p1y, double p2x, double p2y, double p3x, double p3y) - { - double angle_p1, angle_p2, angle_p3, a2, b2, c2; - a2 = (p1x - p2x) * (p1x - p2x) + (p1y - p2y) * (p1y - p2y); - b2 = (p1x - p3x) * (p1x - p3x) + (p1y - p3y) * (p1y - p3y); - c2 = (p2x - p3x) * (p2x - p3x) + (p2y - p3y) * (p2y - p3y); - angle_p1 = Math.Acos((a2 + b2 - c2) / (2.0 * Math.Sqrt(a2) * Math.Sqrt(b2))) * 180.0 / Math.PI; - angle_p2 = Math.Acos((a2 + c2 - b2) / (2.0 * Math.Sqrt(a2) * Math.Sqrt(c2))) * 180.0 / Math.PI; - angle_p3 = Math.Acos((c2 + b2 - a2) / (2.0 * Math.Sqrt(c2) * Math.Sqrt(b2))) * 180.0 / Math.PI; - if (angle_p1 <= angle_p2 && angle_p1 <= angle_p3) - { - return angle_p1; - } - else if (angle_p2 <= angle_p3) - { - return angle_p2; - } - else - { - return angle_p3; - } - } - - /// - /// Finds max angle of a triangle for .part file. - /// - /// - /// - /// - /// - /// - /// - /// - static double returnMaxAngle(double p1x, double p1y, double p2x, double p2y, double p3x, double p3y) - { - double angle_p1, angle_p2, angle_p3, a2, b2, c2; - a2 = (p1x - p2x) * (p1x - p2x) + (p1y - p2y) * (p1y - p2y); - b2 = (p1x - p3x) * (p1x - p3x) + (p1y - p3y) * (p1y - p3y); - c2 = (p2x - p3x) * (p2x - p3x) + (p2y - p3y) * (p2y - p3y); - angle_p1 = Math.Acos((a2 + b2 - c2) / (2.0 * Math.Sqrt(a2) * Math.Sqrt(b2))) * 180.0 / Math.PI; - angle_p2 = Math.Acos((a2 + c2 - b2) / (2.0 * Math.Sqrt(a2) * Math.Sqrt(c2))) * 180.0 / Math.PI; - angle_p3 = Math.Acos((c2 + b2 - a2) / (2.0 * Math.Sqrt(c2) * Math.Sqrt(b2))) * 180.0 / Math.PI; - if (angle_p1 >= angle_p2 && angle_p1 >= angle_p3) - { - return angle_p1; - } - else if (angle_p2 >= angle_p3) - { - return angle_p2; - } - else - { - return angle_p3; - } - } - - //***************************************************************** - // - // writeparts() Write the triangles to a .part file with the information - // that it is bad or good - // - //***************************************************************** - #ifndef TRILIBRARY - void writeparts(mesh m, int argc, char **argv) - { - FILE *outfile; - otri triangleloop; - vertex p1, p2, p3; - long elementnumber; - int i; - int bad; - double minAngleTri, maxAngleTri; - char partfilename[FILENAMESIZE]; - strcpy(partfilename, behavior.innodefilename); - partfilename[strlen(partfilename) - 5] = '\0'; - strcat(partfilename, ".1.part"); - if (!behavior.quiet) { - printf("Writing %s.\n", partfilename); - } - outfile = fopen(partfilename, "w"); - if (outfile == (FILE *) NULL) { - printf(" Error: Cannot create file %s.\n", partfilename); - triexit(1); - } - // Number of triangles, vertices per triangle, attributes per triangle. - fprintf(outfile, "%ld 103\n", m.triangles.items); - - traversalinit(&m.triangles); - triangleloop.tri = triangletraverse(m); - triangleloop.orient = 0; - elementnumber = behavior.firstnumber; - while (triangleloop.tri != (triangle *) NULL) { - org(triangleloop, p1); - dest(triangleloop, p2); - apex(triangleloop, p3); - if (behavior.order == 1) { - // we need to check if this triangle is good or not - //bad = testTriangleAngleArea(m, b, &p1[0], &p1[1],&p2[0], &p2[1],&p3[0], &p3[1]); - bad = testTriangleAngle(m, b, &p1[0], &p1[1],&p2[0], &p2[1],&p3[0], &p3[1]); - minAngleTri = returnMinAngle(p1[0], p1[1],p2[0], p2[1],p3[0], p3[1]); - maxAngleTri = returnMaxAngle(p1[0], p1[1],p2[0], p2[1],p3[0], p3[1]); - if(bad){ - //printf("!!!!!!!!bad >> p1[0]=%f,p1[1]=%f,p2[0]=%f,p2[1]=%f,p3[0]=%f,p3[1]=%f\n", p1[0], p1[1],p2[0], p2[1],p3[0], p3[1]); - // Triangle number, indices for three vertices. - if(behavior.maxangle != 0.00000){ - fprintf(outfile, "%4ld %d ", elementnumber,(int)((180.0-maxAngleTri)*0.35+2.0)); - }else{ - fprintf(outfile, "%4ld %d ", elementnumber, 1); - } - }else{ - if(behavior.maxangle != 0.00000){ - fprintf(outfile, "%4ld %d ", elementnumber,(int)((180.0-maxAngleTri)*0.35+54.0+2.0)); - }else{ - fprintf(outfile, "%4ld %d ", elementnumber, 103); - } - } - } - - fprintf(outfile, "\n"); - triangleloop.tri = triangletraverse(m); - elementnumber++; - } - finishfile(outfile, argc, argv); - } - */ } } \ No newline at end of file diff --git a/Triangle.NET/Triangle/Primitives.cs b/Triangle.NET/Triangle/Primitives.cs index 3e61aed..3f22673 100644 --- a/Triangle.NET/Triangle/Primitives.cs +++ b/Triangle.NET/Triangle/Primitives.cs @@ -9,6 +9,8 @@ namespace TriangleNet { using System; using TriangleNet.Data; + using TriangleNet.Geometry; + using TriangleNet.Tools; /// /// 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. /// - /// - /// - /// + /// Point a. + /// Point b. + /// Point c. /// 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. /// - /// 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. /// - 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. /// - /// - /// - /// - /// + /// Point a. + /// Point b. + /// Point c. + /// Point d. /// 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. @@ -178,7 +180,7 @@ namespace TriangleNet /// /// See Robust Predicates paper for details. /// - 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. /// - /// - /// - /// - /// - /// - /// - /// 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. - /// - public static double NonRegular(Point2 pa, Point2 pb, Point2 pc, Point2 pd) + /// Point a. + /// Point b. + /// Point c. + /// Point d. + /// 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. + public static double NonRegular(Point pa, Point pb, Point pc, Point pd) { return InCircle(pa, pb, pc, pd); } @@ -259,13 +256,13 @@ namespace TriangleNet /// /// Find the circumcenter of a triangle. /// - /// - /// - /// - /// - /// - /// - /// + /// Triangle point. + /// Triangle point. + /// Triangle point. + /// Relative coordinate of new location. + /// Relative coordinate of new location. + /// Use off-center for new location. + /// Coordinates of the circumcenter (or off-center) /// /// 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. /// - 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; } - - - /* - /// - /// 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// 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. - /// - /// 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. - /// - 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); - } - */ } } diff --git a/Triangle.NET/Triangle/Quality.cs b/Triangle.NET/Triangle/Quality.cs index a094ae0..f1db6a6 100644 --- a/Triangle.NET/Triangle/Quality.cs +++ b/Triangle.NET/Triangle/Quality.cs @@ -11,6 +11,7 @@ namespace TriangleNet using System.Collections.Generic; using TriangleNet.Data; using TriangleNet.Log; + using TriangleNet.Geometry; /// /// Provides methods for mesh quality enforcement and testing. @@ -20,7 +21,9 @@ namespace TriangleNet Queue badsubsegs; BadTriQueue queue; Mesh mesh; - Func userTest; + + // Not used at the moment + Func userTest; ILog logger; @@ -34,9 +37,9 @@ namespace TriangleNet } /// - /// Deallocate space for a bad subsegment, marking it dead. + /// Add a bad subsegment to the queue. /// - /// + /// Bad subsegment. 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 /// /// Check a subsegment to see if it is encroached; add it to the list if it is. /// - /// + /// The subsegment to check. /// Returns a nonzero value if the subsegment is encroached. /// /// 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. /// - 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. /// - 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 /// /// Test every triangle in the mesh for quality measures. /// - 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. /// /// - 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 } } diff --git a/Triangle.NET/Triangle/Statistic.cs b/Triangle.NET/Triangle/Tools/Statistic.cs similarity index 93% rename from Triangle.NET/Triangle/Statistic.cs rename to Triangle.NET/Triangle/Tools/Statistic.cs index 150c87a..620005e 100644 --- a/Triangle.NET/Triangle/Statistic.cs +++ b/Triangle.NET/Triangle/Tools/Statistic.cs @@ -5,11 +5,12 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet +namespace TriangleNet.Tools { using System; using System.Text; using TriangleNet.Data; + using TriangleNet.Geometry; /// /// Gather mesh statistics. @@ -74,7 +75,7 @@ namespace TriangleNet /// /// Gets the shortest altitude. /// - public double ShortestAltitude { get { return minAspect; } } + public double ShortestAltitude { get { return minAspect; } } double maxAspect = 0; /// @@ -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; } diff --git a/Triangle.NET/Triangle/Triangle.csproj b/Triangle.NET/Triangle/Triangle.csproj index 07e5998..fe17888 100644 --- a/Triangle.NET/Triangle/Triangle.csproj +++ b/Triangle.NET/Triangle/Triangle.csproj @@ -47,25 +47,23 @@ - - - - - - + + + + @@ -82,9 +80,8 @@ - + -