diff --git a/DelauneyTriangulation/ViewModels/MainWindowViewModel.cs b/DelauneyTriangulation/ViewModels/MainWindowViewModel.cs index a18bb8e..fecdd21 100644 --- a/DelauneyTriangulation/ViewModels/MainWindowViewModel.cs +++ b/DelauneyTriangulation/ViewModels/MainWindowViewModel.cs @@ -227,12 +227,14 @@ public partial class MainWindowViewModel : ViewModelBase return $"{Q(ax)},{Q(ay)}|{Q(bx)},{Q(by)}"; } - void DrawCircles(IEnumerable triangles, Geometries.Point superA, Geometries.Point superB, + async Task DrawCircles(CancellationToken ct, IEnumerable triangles, Geometries.Point superA, Geometries.Point superB, Geometries.Point superC) { Circles.Clear(); foreach (var t in triangles) { + ct.ThrowIfCancellationRequested(); + if (UsesVertex(t, superA) || UsesVertex(t, superB) || UsesVertex(t, superC)) continue; try { @@ -242,7 +244,10 @@ public partial class MainWindowViewModel : ViewModelBase var cc = new Geometries.Circle(a, b, c); if (double.IsFinite(cc.X) && double.IsFinite(cc.Y) && double.IsFinite(cc.R) && cc.R > 0) + { Circles.Add(cc); + await DelayAsync(ct); + } } catch (ArgumentException) { @@ -250,13 +255,15 @@ public partial class MainWindowViewModel : ViewModelBase } } - void BuildVoronoi(Geometries.Point sa, Geometries.Point sb, Geometries.Point sc) + async Task BuildVoronoi(CancellationToken ct, Geometries.Point sa, Geometries.Point sb, Geometries.Point sc) { VoronoiDiagram.Clear(); var center = new Dictionary(); foreach (var t in Triangles) { + ct.ThrowIfCancellationRequested(); + if (UsesVertex(t, sa) || UsesVertex(t, sb) || UsesVertex(t, sc)) continue; try { @@ -274,6 +281,8 @@ public partial class MainWindowViewModel : ViewModelBase var map = new Dictionary tris)>(); foreach (var t in Triangles) { + ct.ThrowIfCancellationRequested(); + if (UsesVertex(t, sa) || UsesVertex(t, sb) || UsesVertex(t, sc)) continue; foreach (var e in ExtractTriangleEdges(t)) { @@ -287,11 +296,16 @@ public partial class MainWindowViewModel : ViewModelBase var far = 10 * Math.Max(Width, Height); foreach (var kv in map.Values) { + ct.ThrowIfCancellationRequested(); + var e = kv.edge; var tris = kv.tris; if (tris.Count == 2 && center.TryGetValue(tris[0], out var c0) && center.TryGetValue(tris[1], out var c1)) + { VoronoiDiagram.Add(new Geometries.Edge(c0.x, c0.y, c1.x, c1.y)); + await DelayAsync(ct); + } else if (tris.Count == 1 && center.TryGetValue(tris[0], out var c)) { @@ -320,10 +334,57 @@ public partial class MainWindowViewModel : ViewModelBase } VoronoiDiagram.Add(new Geometries.Edge(c.x, c.y, c.x + nx * far, c.y + ny * far)); + await DelayAsync(ct); } } } + static double Cross(Geometries.Point o, Geometries.Point a, Geometries.Point b) => + (a.X - o.X) * (b.Y - o.Y) - (a.Y - o.Y) * (b.X - o.X); + + List ComputeConvexHull(IList pts) + { + if (pts.Count <= 1) return pts.ToList(); + + var p = pts.OrderBy(x => x.X).ThenBy(x => x.Y).ToList(); + + var lower = new List(); + foreach (var pt in p) + { + while (lower.Count >= 2 && Cross(lower[^2], lower[^1], pt) <= 0) lower.RemoveAt(lower.Count - 1); + lower.Add(pt); + } + + var upper = new List(); + for (int i = p.Count - 1; i >= 0; i--) + { + var pt = p[i]; + while (upper.Count >= 2 && Cross(upper[^2], upper[^1], pt) <= 0) upper.RemoveAt(upper.Count - 1); + upper.Add(pt); + } + + lower.RemoveAt(lower.Count - 1); + upper.RemoveAt(upper.Count - 1); + return lower.Concat(upper).ToList(); + } + + async Task DrawConvexHull(CancellationToken ct) + { + Edges.Clear(); + var hull = ComputeConvexHull(Points); + if (hull.Count < 2) return; + + for (int i = 0; i < hull.Count; i++) + { + ct.ThrowIfCancellationRequested(); + + var a = hull[i]; + var b = hull[(i + 1) % hull.Count]; + Edges.Add(new Geometries.Edge(a.X, a.Y, b.X, b.Y)); + await DelayAsync(ct); + } + } + async Task BowyerWatson(CancellationToken ct) { Triangles.Clear(); @@ -351,8 +412,7 @@ public partial class MainWindowViewModel : ViewModelBase } } - DrawCircles(bad, sa, sb, sc); - await DelayAsync(ct); + await DrawCircles(ct, bad, sa, sb, sc); var edgeCount = new Dictionary(); var counter = new Dictionary(); @@ -391,11 +451,9 @@ public partial class MainWindowViewModel : ViewModelBase var toRemove = Triangles.Where(t => UsesVertex(t, sa) || UsesVertex(t, sb) || UsesVertex(t, sc)).ToList(); foreach (var t in toRemove) Triangles.Remove(t); - DrawCircles(Triangles, sa, sb, sc); - await DelayAsync(ct); - - BuildVoronoi(sa, sb, sc); - await DelayAsync(ct); + await DrawCircles(ct, Triangles, sa, sb, sc); + await BuildVoronoi(ct, sa, sb, sc); + await DrawConvexHull(ct); } async Task DelaunayTriangulateAsync(CancellationToken ct)