diff --git a/src/CDT.Core/Triangulation.cs b/src/CDT.Core/Triangulation.cs
index a6f3039..43afca1 100644
--- a/src/CDT.Core/Triangulation.cs
+++ b/src/CDT.Core/Triangulation.cs
@@ -35,6 +35,18 @@ public DuplicateVertexException(int v1, int v2)
}
}
+///
+/// Thrown when a mutating method is called after the triangulation has been finalized
+/// (i.e., after any Erase* method has been called).
+///
+public sealed class TriangulationFinalizedException : TriangulationException
+{
+ ///
+ public TriangulationFinalizedException()
+ : base("The triangulation has already been finalized. No further modifications are allowed after calling an Erase method.")
+ { }
+}
+
/// Thrown when intersecting constraint edges are detected and is set.
public sealed class IntersectingConstraintsException : TriangulationException
{
@@ -154,6 +166,7 @@ public Triangulation(
/// Inserts a list of vertices into the triangulation.
public void InsertVertices(IReadOnlyList> newVertices)
{
+ if (IsFinalized) throw new TriangulationFinalizedException();
if (newVertices.Count == 0) return;
bool isFirstInsertion = _kdTree == null && _verticesCount == 0;
@@ -217,6 +230,7 @@ public void InsertVertices(IReadOnlyList> newVertices)
/// Inserts constraint edges (constrained Delaunay triangulation).
public void InsertEdges(IReadOnlyList edges)
{
+ if (IsFinalized) throw new TriangulationFinalizedException();
var remaining = new List(4);
var tppTasks = new List(8);
var polyL = new List(8);
@@ -246,6 +260,7 @@ public void InsertEdges(IReadOnlyList edges)
///
public void ConformToEdges(IReadOnlyList edges)
{
+ if (IsFinalized) throw new TriangulationFinalizedException();
var remaining = new List(8);
var flipStack = new Stack(4);
var flippedFixed = new List(8);
@@ -270,6 +285,7 @@ public void ConformToEdges(IReadOnlyList edges)
/// Removes the super-triangle to produce a convex hull triangulation.
public void EraseSuperTriangle()
{
+ if (IsFinalized) throw new TriangulationFinalizedException();
if (_superGeomType != SuperGeometryType.SuperTriangle) return;
var toErase = new HashSet();
for (int i = 0; i < _trianglesCount; i++)
@@ -283,6 +299,7 @@ public void EraseSuperTriangle()
/// Removes all outer triangles (flood-fill from super-triangle vertex until a constraint edge).
public void EraseOuterTriangles()
{
+ if (IsFinalized) throw new TriangulationFinalizedException();
if (_vertTris[0] == Indices.NoNeighbor)
throw new TriangulationException("No vertex triangle data – already finalized?");
var seeds = new Stack();
@@ -297,6 +314,7 @@ public void EraseOuterTriangles()
///
public void EraseOuterTrianglesAndHoles()
{
+ if (IsFinalized) throw new TriangulationFinalizedException();
var depths = CalculateTriangleDepths();
var toErase = new HashSet();
for (int i = 0; i < _trianglesCount; i++)
diff --git a/test/CDT.Tests/TriangulationTests.cs b/test/CDT.Tests/TriangulationTests.cs
index 10b5056..d3a6a41 100644
--- a/test/CDT.Tests/TriangulationTests.cs
+++ b/test/CDT.Tests/TriangulationTests.cs
@@ -222,6 +222,81 @@ public void FivePoints_ValidTopology()
Assert.True(TopologyVerifier.VerifyTopology(cdt));
Assert.Equal(5, cdt.Vertices.Length);
}
+
+ // -------------------------------------------------------------------------
+ // IsFinalized guard checks
+ // -------------------------------------------------------------------------
+
+ [Fact]
+ public void InsertVertices_AfterFinalized_ThrowsTriangulationFinalizedException()
+ {
+ var cdt = CreateCdt();
+ cdt.InsertVertices([Pt(0, 0), Pt(1, 0), Pt(0.5, 1)]);
+ cdt.EraseSuperTriangle();
+
+ Assert.True(cdt.IsFinalized);
+ Assert.Throws(() =>
+ cdt.InsertVertices([Pt(2, 0)]));
+ }
+
+ [Fact]
+ public void InsertEdges_AfterFinalized_ThrowsTriangulationFinalizedException()
+ {
+ var cdt = CreateCdt();
+ cdt.InsertVertices([Pt(0, 0), Pt(1, 0), Pt(1, 1), Pt(0, 1)]);
+ cdt.EraseSuperTriangle();
+
+ Assert.True(cdt.IsFinalized);
+ Assert.Throws(() =>
+ cdt.InsertEdges([new Edge(0, 2)]));
+ }
+
+ [Fact]
+ public void ConformToEdges_AfterFinalized_ThrowsTriangulationFinalizedException()
+ {
+ var cdt = CreateCdt();
+ cdt.InsertVertices([Pt(0, 0), Pt(1, 0), Pt(1, 1), Pt(0, 1)]);
+ cdt.EraseSuperTriangle();
+
+ Assert.True(cdt.IsFinalized);
+ Assert.Throws(() =>
+ cdt.ConformToEdges([new Edge(0, 2)]));
+ }
+
+ [Fact]
+ public void EraseSuperTriangle_AfterFinalized_ThrowsTriangulationFinalizedException()
+ {
+ var cdt = CreateCdt();
+ cdt.InsertVertices([Pt(0, 0), Pt(1, 0), Pt(0.5, 1)]);
+ cdt.EraseSuperTriangle();
+
+ Assert.True(cdt.IsFinalized);
+ Assert.Throws(() => cdt.EraseSuperTriangle());
+ }
+
+ [Fact]
+ public void EraseOuterTriangles_AfterFinalized_ThrowsTriangulationFinalizedException()
+ {
+ var cdt = CreateCdt();
+ cdt.InsertVertices([Pt(0, 0), Pt(1, 0), Pt(1, 1), Pt(0, 1)]);
+ cdt.InsertEdges([new Edge(0, 1), new Edge(1, 2), new Edge(2, 3), new Edge(3, 0)]);
+ cdt.EraseOuterTriangles();
+
+ Assert.True(cdt.IsFinalized);
+ Assert.Throws(() => cdt.EraseOuterTriangles());
+ }
+
+ [Fact]
+ public void EraseOuterTrianglesAndHoles_AfterFinalized_ThrowsTriangulationFinalizedException()
+ {
+ var cdt = CreateCdt();
+ cdt.InsertVertices([Pt(0, 0), Pt(1, 0), Pt(1, 1), Pt(0, 1)]);
+ cdt.InsertEdges([new Edge(0, 1), new Edge(1, 2), new Edge(2, 3), new Edge(3, 0)]);
+ cdt.EraseOuterTrianglesAndHoles();
+
+ Assert.True(cdt.IsFinalized);
+ Assert.Throws(() => cdt.EraseOuterTrianglesAndHoles());
+ }
}
/// Triangulation tests for .