From 2bc0f2a67ccb536be6844ebd45dd5a8750442e2e Mon Sep 17 00:00:00 2001 From: Ano-sys Date: Thu, 5 Feb 2026 22:38:23 +0100 Subject: [PATCH] Added demonstration bowyer watson walkthrough algorithm with a simple example triangle --- .../ViewModels/MainWindowViewModel.cs | 150 +++++++++++++++++- DelaunayTriangulation/Views/MainWindow.axaml | 2 +- 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/DelaunayTriangulation/ViewModels/MainWindowViewModel.cs b/DelaunayTriangulation/ViewModels/MainWindowViewModel.cs index 63cac3c..3a0c596 100644 --- a/DelaunayTriangulation/ViewModels/MainWindowViewModel.cs +++ b/DelaunayTriangulation/ViewModels/MainWindowViewModel.cs @@ -38,6 +38,7 @@ public partial class MainWindowViewModel : ViewModelBase [ObservableProperty] private bool _doNearestTriangle = false; [ObservableProperty] private bool _doDelaunayLib = false; [ObservableProperty] private bool _doBowyerWatson = true; + [ObservableProperty] private bool _doDemonstration = false; [ObservableProperty] private int? _generationDelay = 20; private CancellationTokenSource? _genCts; @@ -160,8 +161,6 @@ public partial class MainWindowViewModel : ViewModelBase i--; retry++; } - - ; } l.Sort(Geometries.PointComparer.Instance); @@ -566,6 +565,149 @@ public partial class MainWindowViewModel : ViewModelBase } } + async Task DemonstrationDelaunay(CancellationToken ct) + { + async Task RunCancellationSafe(Action toRun) + { + toRun(); + await DelayAsync(ct); + ct.ThrowIfCancellationRequested(); + } + + // first point + var c = new Geometries.Point(Width / 2, Height / 2); + await RunCancellationSafe(() => Points.Add(c)); + + // build super triangle + var t1 = new Geometries.Point(c.X, c.Y - 100); + var t2 = new Geometries.Point(c.X - 100, c.Y + 75); + var t3 = new Geometries.Point(c.X + 100, c.Y + 75); + + await RunCancellationSafe(() => Points.Add(t1)); + await RunCancellationSafe(() => Points.Add(t2)); + await RunCancellationSafe(() => Points.Add(t3)); + + // combine Points + var e1 = new Geometries.Edge(t1.X, t1.Y, t2.X, t2.Y); + var e2 = new Geometries.Edge(t1.X, t1.Y, t3.X, t3.Y); + var e3 = new Geometries.Edge(t2.X, t2.Y, t3.X, t3.Y); + var e4 = new Geometries.Edge(t1.X, t1.Y, c.X, c.Y); + var e5 = new Geometries.Edge(t2.X, t2.Y, c.X, c.Y); + var e6 = new Geometries.Edge(t3.X, t3.Y, c.X, c.Y); + + await RunCancellationSafe(() => Edges.Add(e1)); + await RunCancellationSafe(() => Edges.Add(e2)); + await RunCancellationSafe(() => Edges.Add(e3)); + await RunCancellationSafe(() => Edges.Add(e4)); + await RunCancellationSafe(() => Edges.Add(e5)); + await RunCancellationSafe(() => Edges.Add(e6)); + + // show circumcircles to ensure delaunay criteria + var c1 = new Geometries.Circle(t1, t2, c); + var c2 = new Geometries.Circle(t2, t3, c); + var c3 = new Geometries.Circle(t1, t3, c); + + await RunCancellationSafe(() => Circles.Add(c1)); + await RunCancellationSafe(() => Circles.Add(c2)); + await RunCancellationSafe(() => Circles.Add(c3)); + + // add extra point which breaks delaunay criteria + var t4 = new Geometries.Point(c.X - 50, c.Y + 35); + await RunCancellationSafe(() => Points.Add(t4)); + + // Fix triangulation + await RunCancellationSafe(() => Circles.Remove(c1)); + await RunCancellationSafe(() => Circles.Remove(c2)); + + await RunCancellationSafe(() => Edges.Remove(e1)); + await RunCancellationSafe(() => Edges.Remove(e3)); + await RunCancellationSafe(() => Edges.Remove(e5)); + + // add new edges + e5 = new Geometries.Edge(t2.X, t2.Y, t4.X, t4.Y); + var e7 = new Geometries.Edge(t1.X, t1.Y, t4.X, t4.Y); + var e8 = new Geometries.Edge(t3.X, t3.Y, t4.X, t4.Y); + var e9 = new Geometries.Edge(c.X, c.Y, t4.X, t4.Y); + + await RunCancellationSafe(() => Edges.Add(e1)); + await RunCancellationSafe(() => Edges.Add(e3)); + await RunCancellationSafe(() => Edges.Add(e5)); + await RunCancellationSafe(() => Edges.Add(e7)); + await RunCancellationSafe(() => Edges.Add(e8)); + await RunCancellationSafe(() => Edges.Add(e9)); + + c1 = new Geometries.Circle(t1, t2, t4); + c2 = new Geometries.Circle(t2, t4, t3); + var c4 = new Geometries.Circle(t1, t4, c); + var c5 = new Geometries.Circle(t4, t3, c); + + await RunCancellationSafe(() => Circles.Add(c1)); + await RunCancellationSafe(() => Circles.Add(c2)); + await RunCancellationSafe(() => Circles.Add(c4)); + await RunCancellationSafe(() => Circles.Add(c5)); + + // add another point + var t5 = new Geometries.Point(c.X + 50, c.Y + 45); + await RunCancellationSafe(() => Points.Add(t5)); + + await RunCancellationSafe(() => Circles.Remove(c2)); + await RunCancellationSafe(() => Circles.Remove(c3)); + await RunCancellationSafe(() => Circles.Remove(c5)); + + await RunCancellationSafe(() => Edges.Remove(e3)); + await RunCancellationSafe(() => Edges.Remove(e8)); + await RunCancellationSafe(() => Edges.Remove(e6)); + await RunCancellationSafe(() => Edges.Remove(e2)); + + e6 = new Geometries.Edge(c.X, c.Y, t5.X, t5.Y); + e8 = new Geometries.Edge(t2.X, t2.Y, t5.X, t5.Y); + var e10 = new Geometries.Edge(t5.X, t5.Y, t3.X, t3.Y); + var e11 = new Geometries.Edge(t5.X, t5.Y, t4.X, t4.Y); + var e12 = new Geometries.Edge(t5.X, t5.Y, t1.X, t1.Y); + + await RunCancellationSafe(() => Edges.Add(e3)); + await RunCancellationSafe(() => Edges.Add(e2)); + await RunCancellationSafe(() => Edges.Add(e6)); + await RunCancellationSafe(() => Edges.Add(e8)); + await RunCancellationSafe(() => Edges.Add(e10)); + await RunCancellationSafe(() => Edges.Add(e11)); + await RunCancellationSafe(() => Edges.Add(e12)); + + c2 = new Geometries.Circle(t2, t5, t3); + c3 = new Geometries.Circle(t1, t5, t3); + c5 = new Geometries.Circle(c, t4, t5); + var c6 = new Geometries.Circle(t2, t4, t5); + var c7 = new Geometries.Circle(t1, c, t5); + + await RunCancellationSafe(() => Circles.Add(c2)); + await RunCancellationSafe(() => Circles.Add(c3)); + await RunCancellationSafe(() => Circles.Add(c5)); + await RunCancellationSafe(() => Circles.Add(c6)); + await RunCancellationSafe(() => Circles.Add(c7)); + + // remove super triangle + await RunCancellationSafe(() => Circles.Remove(c1)); + await RunCancellationSafe(() => Circles.Remove(c2)); + await RunCancellationSafe(() => Circles.Remove(c3)); + await RunCancellationSafe(() => Circles.Remove(c4)); + await RunCancellationSafe(() => Circles.Remove(c6)); + await RunCancellationSafe(() => Circles.Remove(c7)); + + await RunCancellationSafe(() => Edges.Remove(e1)); + await RunCancellationSafe(() => Edges.Remove(e2)); + await RunCancellationSafe(() => Edges.Remove(e3)); + await RunCancellationSafe(() => Edges.Remove(e4)); + await RunCancellationSafe(() => Edges.Remove(e5)); + await RunCancellationSafe(() => Edges.Remove(e7)); + await RunCancellationSafe(() => Edges.Remove(e8)); + await RunCancellationSafe(() => Edges.Remove(e10)); + await RunCancellationSafe(() => Edges.Remove(e12)); + + await RunCancellationSafe(() => Points.Remove(t1)); + await RunCancellationSafe(() => Points.Remove(t2)); + await RunCancellationSafe(() => Points.Remove(t3)); + } + public void CancelGeneration() => _genCts?.CancelAsync(); private Task DelayAsync(CancellationToken ct) => @@ -595,12 +737,14 @@ public partial class MainWindowViewModel : ViewModelBase IsGenerationRunning = true; - await GenerateRandomPoints(PointCount ?? 20, ct); + if(!DoDemonstration) await GenerateRandomPoints(PointCount ?? 20, ct); + if(DoConnectPoints) await ConnectPoints(ct); else if(DoConnectPointsV2) await ConnectPointsV2(ct); else if(DoNearestTriangle) await Triangulate(ct); else if(DoDelaunayLib) await DelaunayTriangulateAsync(ct); else if(DoBowyerWatson) await BowyerWatson(ct); + else if (DoDemonstration) await DemonstrationDelaunay(ct); IsGenerationRunning = false; } diff --git a/DelaunayTriangulation/Views/MainWindow.axaml b/DelaunayTriangulation/Views/MainWindow.axaml index 0574b82..debacb4 100644 --- a/DelaunayTriangulation/Views/MainWindow.axaml +++ b/DelaunayTriangulation/Views/MainWindow.axaml @@ -118,7 +118,7 @@ - +