diff --git a/Triangle.NET/MeshRenderer.Core/GDI/MeshRenderer.cs b/Triangle.NET/MeshRenderer.Core/GDI/MeshRenderer.cs index 1b4a66f..68ec82a 100644 --- a/Triangle.NET/MeshRenderer.Core/GDI/MeshRenderer.cs +++ b/Triangle.NET/MeshRenderer.Core/GDI/MeshRenderer.cs @@ -22,12 +22,28 @@ namespace MeshRenderer.Core.GDI RenderData data; ColorManager renderColors; + // If true, even geometry parts outside of bounds will be rendered. + bool ignoreBounds; + /// /// Initializes a new instance of the class. /// public MeshRenderer(RenderData data) { this.data = data; + + int featureCount = data.Points.Length; + + if (data.MeshEdges != null) + { + featureCount += data.MeshEdges.Length; + } + else if (data.Triangles != null) + { + featureCount += 2 * data.Triangles.Length; + } + + this.ignoreBounds = featureCount < 1000; } /// @@ -106,7 +122,7 @@ namespace MeshRenderer.Core.GDI for (i = 0; i < n; i++) { k = 2 * i; - if (zoom.ViewportContains(pts[k], pts[k + 1])) + if (ignoreBounds || zoom.ViewportContains(pts[k], pts[k + 1])) { pt = zoom.WorldToScreen(pts[k], pts[k + 1]); g.FillEllipse(renderColors.Point, pt.X - 1.5f, pt.Y - 1.5f, 3, 3); @@ -118,7 +134,7 @@ namespace MeshRenderer.Core.GDI for (; i < n; i++) { k = 2 * i; - if (zoom.ViewportContains(pts[k], pts[k + 1])) + if (ignoreBounds || zoom.ViewportContains(pts[k], pts[k + 1])) { pt = zoom.WorldToScreen(pts[k], pts[k + 1]); g.FillEllipse(renderColors.SteinerPoint, pt.X - 1.5f, pt.Y - 1.5f, 3, 3); @@ -140,9 +156,10 @@ namespace MeshRenderer.Core.GDI k1 = 2 * data.Triangles[3 * i + 1]; k2 = 2 * data.Triangles[3 * i + 2]; - if (zoom.ViewportContains(pts[k0], pts[k0 + 1]) || + if (ignoreBounds || + (zoom.ViewportContains(pts[k0], pts[k0 + 1]) || zoom.ViewportContains(pts[k1], pts[k1 + 1]) || - zoom.ViewportContains(pts[k2], pts[k2 + 1])) + zoom.ViewportContains(pts[k2], pts[k2 + 1]))) { p0 = zoom.WorldToScreen(pts[k0], pts[k0 + 1]); p1 = zoom.WorldToScreen(pts[k1], pts[k1 + 1]); @@ -168,8 +185,9 @@ namespace MeshRenderer.Core.GDI k0 = 2 * data.MeshEdges[2 * i]; k1 = 2 * data.MeshEdges[2 * i + 1]; - if (zoom.ViewportContains(pts[k0], pts[k0 + 1]) || - zoom.ViewportContains(pts[k1], pts[k1 + 1])) + if (ignoreBounds || + (zoom.ViewportContains(pts[k0], pts[k0 + 1]) || + zoom.ViewportContains(pts[k1], pts[k1 + 1]))) { p0 = zoom.WorldToScreen(pts[k0], pts[k0 + 1]); p1 = zoom.WorldToScreen(pts[k1], pts[k1 + 1]); @@ -192,8 +210,9 @@ namespace MeshRenderer.Core.GDI k0 = 2 * data.Segments[2 * i]; k1 = 2 * data.Segments[2 * i + 1]; - if (zoom.ViewportContains(pts[k0], pts[k0 + 1]) || - zoom.ViewportContains(pts[k1], pts[k1 + 1])) + if (ignoreBounds || + (zoom.ViewportContains(pts[k0], pts[k0 + 1]) || + zoom.ViewportContains(pts[k1], pts[k1 + 1]))) { p0 = zoom.WorldToScreen(pts[k0], pts[k0 + 1]); p1 = zoom.WorldToScreen(pts[k1], pts[k1 + 1]); diff --git a/Triangle.NET/MeshRenderer.Core/GDI/RenderControl.cs b/Triangle.NET/MeshRenderer.Core/GDI/RenderControl.cs index 016011a..9c22657 100644 --- a/Triangle.NET/MeshRenderer.Core/GDI/RenderControl.cs +++ b/Triangle.NET/MeshRenderer.Core/GDI/RenderControl.cs @@ -224,19 +224,18 @@ namespace MeshRenderer.Core.GDI } else if (e.Button == MouseButtons.Left) { - /* - // Just in case ... timer.Stop(); + var nfi = System.Globalization.CultureInfo.InvariantCulture.NumberFormat; + 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)); + c.X.ToString(nfi), + c.Y.ToString(nfi)); this.Invalidate(); timer.Start(); - * */ } base.OnMouseClick(e); diff --git a/Triangle.NET/MeshRenderer.Core/RenderData.cs b/Triangle.NET/MeshRenderer.Core/RenderData.cs index c97dc96..953476a 100644 --- a/Triangle.NET/MeshRenderer.Core/RenderData.cs +++ b/Triangle.NET/MeshRenderer.Core/RenderData.cs @@ -154,7 +154,6 @@ namespace MeshRenderer.Core /// public void SetVoronoi(IVoronoi voro, int infCount) { - int i, n = voro.Points.Length; // Copy points diff --git a/Triangle.NET/TestApp/FormMain.cs b/Triangle.NET/TestApp/FormMain.cs index c51fb46..555a21d 100644 --- a/Triangle.NET/TestApp/FormMain.cs +++ b/Triangle.NET/TestApp/FormMain.cs @@ -441,6 +441,11 @@ namespace MeshExplorer mesh.SetOption(Options.ConformingDelaunay, true); } + if (meshControlView.ParamSweeplineChecked) + { + mesh.SetOption(Options.TriangulationAlgorithm, TriangulationAlgorithm.SweepLine); + } + if (meshControlView.ParamQualityChecked) { mesh.SetOption(Options.Quality, true); @@ -705,10 +710,7 @@ namespace MeshExplorer bool isConsistent, isDelaunay; mesh.Check(out isConsistent, out isDelaunay); - if (!isConsistent || !isDelaunay) - { - ShowLog(); - } + ShowLog(); } } diff --git a/Triangle.NET/TestApp/Views/MeshControlView.Designer.cs b/Triangle.NET/TestApp/Views/MeshControlView.Designer.cs index 7117dc4..7f4d56b 100644 --- a/Triangle.NET/TestApp/Views/MeshControlView.Designer.cs +++ b/Triangle.NET/TestApp/Views/MeshControlView.Designer.cs @@ -40,6 +40,8 @@ this.cbConformDel = new MeshExplorer.Controls.DarkCheckBox(); this.cbConvex = new MeshExplorer.Controls.DarkCheckBox(); this.cbQuality = new MeshExplorer.Controls.DarkCheckBox(); + this.cbSweepline = new MeshExplorer.Controls.DarkCheckBox(); + this.label1 = new System.Windows.Forms.Label(); this.SuspendLayout(); // // lbMaxArea @@ -175,6 +177,27 @@ this.cbQuality.Text = "Quality mesh"; this.cbQuality.UseVisualStyleBackColor = false; // + // cbSweepline + // + this.cbSweepline.BackColor = System.Drawing.Color.DimGray; + this.cbSweepline.Checked = false; + this.cbSweepline.Location = new System.Drawing.Point(11, 260); + this.cbSweepline.Name = "cbSweepline"; + this.cbSweepline.Size = new System.Drawing.Size(187, 17); + this.cbSweepline.TabIndex = 15; + this.cbSweepline.Text = "Use Sweepline algorithm"; + this.cbSweepline.UseVisualStyleBackColor = false; + // + // label1 + // + this.label1.BackColor = System.Drawing.Color.DimGray; + this.label1.ForeColor = System.Drawing.Color.DarkGray; + this.label1.Location = new System.Drawing.Point(8, 280); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(258, 33); + this.label1.TabIndex = 26; + this.label1.Text = "Use Sweepline algorithm for triangulation instead of default Divide && Conquer."; + // // MeshControlView // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -184,12 +207,14 @@ this.Controls.Add(this.label6); this.Controls.Add(this.lbMinAngle); this.Controls.Add(this.label23); + this.Controls.Add(this.label1); this.Controls.Add(this.label9); this.Controls.Add(this.label8); this.Controls.Add(this.label5); this.Controls.Add(this.slMaxArea); this.Controls.Add(this.slMinAngle); this.Controls.Add(this.cbConformDel); + this.Controls.Add(this.cbSweepline); this.Controls.Add(this.cbConvex); this.Controls.Add(this.cbQuality); this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); @@ -215,5 +240,7 @@ private Controls.DarkCheckBox cbConformDel; private Controls.DarkCheckBox cbConvex; private Controls.DarkCheckBox cbQuality; + private Controls.DarkCheckBox cbSweepline; + private System.Windows.Forms.Label label1; } } diff --git a/Triangle.NET/TestApp/Views/MeshControlView.cs b/Triangle.NET/TestApp/Views/MeshControlView.cs index 0b30bc4..dbb311c 100644 --- a/Triangle.NET/TestApp/Views/MeshControlView.cs +++ b/Triangle.NET/TestApp/Views/MeshControlView.cs @@ -33,6 +33,11 @@ namespace MeshExplorer.Views get { return cbConformDel.Checked; } } + public bool ParamSweeplineChecked + { + get { return cbSweepline.Checked; } + } + public int ParamMinAngleValue { get { return (slMinAngle.Value * 40) / 100; } diff --git a/Triangle.NET/Triangle/Algorithm/Dwyer.cs b/Triangle.NET/Triangle/Algorithm/Dwyer.cs index 12e03c9..edf0b79 100644 --- a/Triangle.NET/Triangle/Algorithm/Dwyer.cs +++ b/Triangle.NET/Triangle/Algorithm/Dwyer.cs @@ -53,7 +53,6 @@ namespace TriangleNet.Algorithm /// /// Sort an array of vertices by x-coordinate, using the y-coordinate as a secondary key. /// - /// /// /// /// @@ -137,7 +136,6 @@ namespace TriangleNet.Algorithm /// An order statistic algorithm, almost. Shuffles an array of vertices so that /// the first 'median' vertices occur lexicographically before the remaining vertices. /// - /// /// /// /// @@ -219,7 +217,6 @@ namespace TriangleNet.Algorithm /// Sorts the vertices as appropriate for the divide-and-conquer algorithm with /// alternating cuts. /// - /// /// /// /// diff --git a/Triangle.NET/Triangle/Mesh.cs b/Triangle.NET/Triangle/Mesh.cs index e36d14e..5c5a6f5 100644 --- a/Triangle.NET/Triangle/Mesh.cs +++ b/Triangle.NET/Triangle/Mesh.cs @@ -233,6 +233,8 @@ namespace TriangleNet if (input.HasSegments) { behavior.Poly = true; + + this.holes.AddRange(input.Holes); } //if (input.EdgeMarkers != null) @@ -261,7 +263,6 @@ namespace TriangleNet // Read and reconstruct a mesh. hullsize = DataReader.Reconstruct(this, input, triangles.ToArray()); - // Calculate the number of edges. edges = (3 * triangles.Count + hullsize) / 2; } diff --git a/Triangle.NET/Triangle/Primitives.cs b/Triangle.NET/Triangle/Primitives.cs index 1f40079..a08f2a9 100644 --- a/Triangle.NET/Triangle/Primitives.cs +++ b/Triangle.NET/Triangle/Primitives.cs @@ -147,12 +147,42 @@ namespace TriangleNet return det; } - return det; - - // TODO: throw new Exception(); - // return counterclockwiseadapt(pa, pb, pc, detsum); + return (double)CounterClockwiseDecimal(pa, pb, pc); } + private static decimal CounterClockwiseDecimal(Point pa, Point pb, Point pc) + { + decimal detleft, detright, det, detsum; + + detleft = ((decimal)pa.x - (decimal)pc.x) * ((decimal)pb.y - (decimal)pc.y); + detright = ((decimal)pa.y - (decimal)pc.y) * ((decimal)pb.x - (decimal)pc.x); + det = detleft - detright; + + if (detleft > 0.0m) + { + if (detright <= 0.0m) + { + return det; + } + else + { + detsum = detleft + detright; + } + } + else if (detleft < 0.0m) + { + if (detright >= 0.0m) + { + return det; + } + else + { + detsum = -detleft - detright; + } + } + + return det; + } /// /// Check if the point pd lies inside the circle passing through pa, pb, and pc. The @@ -223,10 +253,37 @@ namespace TriangleNet return det; } - return det; + return (double)InCircleDecimal(pa, pb, pc, pd); + } - // TODO: throw new Exception(); - //return incircleadapt(pa, pb, pc, pd, permanent); + private static decimal InCircleDecimal(Point pa, Point pb, Point pc, Point pd) + { + decimal adx, bdx, cdx, ady, bdy, cdy; + decimal bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + decimal alift, blift, clift; + + adx = (decimal)pa.x - (decimal)pd.x; + bdx = (decimal)pb.x - (decimal)pd.x; + cdx = (decimal)pc.x - (decimal)pd.x; + ady = (decimal)pa.y - (decimal)pd.y; + bdy = (decimal)pb.y - (decimal)pd.y; + cdy = (decimal)pc.y - (decimal)pd.y; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + return alift * (bdxcdy - cdxbdy) + + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); } ///