diff --git a/Runtime/Triangulator.cs b/Runtime/Triangulator.cs
index ae2d171..812d9d7 100644
--- a/Runtime/Triangulator.cs
+++ b/Runtime/Triangulator.cs
@@ -868,6 +868,19 @@ public static class Extensions
///
/// The allocator to use. If called from a job, consider using .
public static void DynamicSplitHalfedge(this UnsafeTriangulator @this, OutputData output, int he, double alpha, Allocator allocator) => new UnsafeTriangulator().DynamicSplitHalfedge(output, he, alpha, allocator);
+ ///
+ /// Removes the specified point from the data
+ /// and re-triangulates the affected region to maintain a valid triangulation.
+ /// This method supports only the removal of bulk points, i.e., points that are not located on the triangulation boundary.
+ ///
+ ///
+ /// Note:
+ /// This method requires that contains valid triangulation data.
+ /// The native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
+ ///
+ /// The index of the bulk point to remove.
+ /// The allocator to use. If called from a job, consider using .
+ public static void DynamicRemoveBulkPoint(this UnsafeTriangulator @this, OutputData output, int pId, Allocator allocator) => new UnsafeTriangulator().DynamicRemoveBulkPoint(output, pId, allocator);
///
/// Performs triangulation on the given , producing the result in based on the settings specified in .
@@ -946,6 +959,19 @@ public static void DynamicInsertPoint(this UnsafeTriangulator @this, Out
///
/// The allocator to use. If called from a job, consider using .
public static void DynamicSplitHalfedge(this UnsafeTriangulator @this, OutputData output, int he, float alpha, Allocator allocator) => new UnsafeTriangulator().DynamicSplitHalfedge(output, he, alpha, allocator);
+ ///
+ /// Removes the specified point from the data
+ /// and re-triangulates the affected region to maintain a valid triangulation.
+ /// This method supports only the removal of bulk points, i.e., points that are not located on the triangulation boundary.
+ ///
+ ///
+ /// Note:
+ /// This method requires that contains valid triangulation data.
+ /// The native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
+ ///
+ /// The index of the bulk point to remove.
+ /// The allocator to use. If called from a job, consider using .
+ public static void DynamicRemoveBulkPoint(this UnsafeTriangulator @this, OutputData output, int pId, Allocator allocator) => new UnsafeTriangulator().DynamicRemoveBulkPoint(output, pId, allocator);
///
/// Performs triangulation on the given , producing the result in based on the settings specified in .
@@ -1019,6 +1045,19 @@ public static void DynamicInsertPoint(this UnsafeTriangulator @this, Ou
///
/// The allocator to use. If called from a job, consider using .
public static void DynamicSplitHalfedge(this UnsafeTriangulator @this, OutputData output, int he, float alpha, Allocator allocator) => new UnsafeTriangulator().DynamicSplitHalfedge(UnsafeUtility.As, OutputData>(ref output), he, alpha, allocator);
+ ///
+ /// Removes the specified point from the data
+ /// and re-triangulates the affected region to maintain a valid triangulation.
+ /// This method supports only the removal of bulk points, i.e., points that are not located on the triangulation boundary.
+ ///
+ ///
+ /// Note:
+ /// This method requires that contains valid triangulation data.
+ /// The native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
+ ///
+ /// The index of the bulk point to remove.
+ /// The allocator to use. If called from a job, consider using .
+ public static void DynamicRemoveBulkPoint(this UnsafeTriangulator @this, OutputData output, int pId, Allocator allocator) => new UnsafeTriangulator().DynamicRemoveBulkPoint(UnsafeUtility.As, OutputData>(ref output), pId, allocator);
///
/// Performs triangulation on the given , producing the result in based on the settings specified in .
@@ -1097,6 +1136,19 @@ public static void DynamicInsertPoint(this UnsafeTriangulator @this, Ou
///
/// The allocator to use. If called from a job, consider using .
public static void DynamicSplitHalfedge(this UnsafeTriangulator @this, OutputData output, int he, double alpha, Allocator allocator) => new UnsafeTriangulator().DynamicSplitHalfedge(output, he, alpha, allocator);
+ ///
+ /// Removes the specified point from the data
+ /// and re-triangulates the affected region to maintain a valid triangulation.
+ /// This method supports only the removal of bulk points, i.e., points that are not located on the triangulation boundary.
+ ///
+ ///
+ /// Note:
+ /// This method requires that contains valid triangulation data.
+ /// The native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
+ ///
+ /// The index of the bulk point to remove.
+ /// The allocator to use. If called from a job, consider using .
+ public static void DynamicRemoveBulkPoint(this UnsafeTriangulator @this, OutputData output, int pId, Allocator allocator) => new UnsafeTriangulator().DynamicRemoveBulkPoint(output, pId, allocator);
///
/// Performs triangulation on the given , producing the result in based on the settings specified in .
@@ -1198,6 +1250,19 @@ public static void DynamicInsertPoint(this UnsafeTriangulator @this, Output
///
/// The allocator to use. If called from a job, consider using .
public static void DynamicSplitHalfedge(this UnsafeTriangulator @this, OutputData output, int he, fp alpha, Allocator allocator) => new UnsafeTriangulator().DynamicSplitHalfedge(output, he, alpha, allocator);
+ ///
+ /// Removes the specified point from the data
+ /// and re-triangulates the affected region to maintain a valid triangulation.
+ /// This method supports only the removal of bulk points, i.e., points that are not located on the triangulation boundary.
+ ///
+ ///
+ /// Note:
+ /// This method requires that contains valid triangulation data.
+ /// The native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
+ ///
+ /// The index of the bulk point to remove.
+ /// The allocator to use. If called from a job, consider using .
+ public static void DynamicRemoveBulkPoint(this UnsafeTriangulator @this, OutputData output, int pId, Allocator allocator) => new UnsafeTriangulator().DynamicRemoveBulkPoint(output, pId, allocator);
#endif
}
@@ -1443,6 +1508,229 @@ public void DynamicSplitHalfedge(OutputData output, int he, T alpha, Allocat
}
}
+ public void DynamicRemoveBulkPoint(OutputData output, int pId, Allocator allocator)
+ {
+ /// This utility removes the specified point `pId`. It is designed specifically for handling bulk points only!
+ ///
+ /// The algorithm operates as follows:
+ ///
+ /// 1. Iterate over all triangles containing `pId` to create:
+ /// loops of halfedges `heLoop`, points `pIdLoop`, and visited triangles.
+ ///
+ /// p1 h1 p2 h2
+ /// o --------- o
+ /// .' '.
+ /// .' '.
+ /// .' * pId 'o p3 h3
+ /// o p0 h0 ..''
+ /// .... ....''
+ /// '''....o'''
+ /// p4 h3
+ ///
+ /// heLoop = [h0, h1, h2, h3, h4]
+ /// pIdLoop = [p0, p1, p2, p3, p4]
+ /// oheLoop = [h0', h1', h2', h3', h4'], hi' = halfedges[hi]
+ ///
+ /// 2. Remove all visited triangles and adapt `oheLoop` to reflect the changes.
+ /// 3. Triangulate (with restoring boundaries) the cavity created by `pIdLoop` points.
+ /// 4. Merge the resulting triangulation with the newly created triangulated cavity.
+ /// 5. Remove `pId` and adjust triangle indexes accordingly.
+ using var heLoop = new NativeList(allocator);
+ BuildHeLoop(output, heLoop, pId);
+ using var visitedTriangles = new NativeArray(output.Triangles.Length / 3, allocator);
+ using var pIdLoop = new NativeArray(heLoop.Length, allocator);
+ using var oheLoop = new NativeArray(heLoop.Length, allocator);
+ using var oheConstrained = new NativeArray(heLoop.Length, allocator);
+ BuildLoops(output, heLoop, visitedTriangles, pIdLoop, oheLoop, oheConstrained, out var tIdMinVisited);
+ RemoveTriangles(output, visitedTriangles, tIdMinVisited, oheLoop);
+ using var cavityTriangles = new NativeList(allocator);
+ using var cavityHalfedges = new NativeList(allocator);
+ TriangulateCavity(output, pIdLoop, cavityTriangles, cavityHalfedges, allocator);
+ MergeTriangulations(output, pIdLoop, cavityTriangles, cavityHalfedges, oheLoop, oheConstrained);
+ AdaptPoints(output, pId);
+ }
+
+ private static void BuildHeLoop(OutputData output, NativeList heLoop, int pId)
+ {
+ var h0 = -1;
+ /// NOTE: This can be optimized to an O(1) operation by introducing a `pointToHalfedge` buffer.
+ /// However, `pointToHalfedge` is currently only utilized during the Sloan algorithm (constraints),
+ /// and other parts of the code are not yet adapted to incorporate `pointToHalfedge`.
+ /// That said, having O(n) complexity here is not a significant issue.
+ for (int i = 0; i < output.Triangles.Length; i++)
+ {
+ if (output.Triangles[i] == pId)
+ {
+ h0 = i;
+ break;
+ }
+ }
+
+ h0 = NextHalfedge(h0);
+ var h1 = NextHalfedge(h0);
+ var p = output.Triangles[h0];
+ var q = output.Triangles[h1];
+ heLoop.Add(h0);
+ while (p != q)
+ {
+ h0 = NextHalfedge(output.Halfedges[h1]);
+ h1 = NextHalfedge(h0);
+ q = output.Triangles[h1];
+ heLoop.Add(h0);
+ }
+ }
+
+ private static void BuildLoops(OutputData output, NativeList heLoop, NativeArray visitedTriangles, NativeArray pIdLoop, NativeArray oheLoop, NativeArray oheConstrained, out int tIdMinVisited)
+ {
+ tIdMinVisited = int.MaxValue;
+ for (int i = 0; i < heLoop.Length; i++)
+ {
+ var he = heLoop[i];
+ visitedTriangles[he / 3] = true;
+ tIdMinVisited = math.min(he / 3, tIdMinVisited);
+ pIdLoop[i] = output.Triangles[he];
+ oheLoop[i] = output.Halfedges[he];
+ oheConstrained[i] = output.ConstrainedHalfedges[he];
+ }
+ }
+
+ private static void RemoveTriangles(OutputData output, NativeArray visitedTriangles, int tIdMinVisited, NativeArray halfedgeLoop)
+ {
+ static void DisableHe(NativeList halfedges, int he, int rId)
+ {
+ var ohe = halfedges[3 * rId + he];
+ if (ohe != -1)
+ {
+ halfedges[ohe] = -1;
+ }
+ }
+
+ static void AdaptHe(NativeList halfedges, int he, int rId, int wId)
+ {
+ var ohe = halfedges[3 * rId + he];
+ halfedges[3 * wId + he] = ohe;
+ if (ohe != -1)
+ {
+ halfedges[ohe] = 3 * wId + he;
+ }
+ }
+
+ var triangles = output.Triangles;
+ var halfedges = output.Halfedges;
+ var constrainedHalfedges = output.ConstrainedHalfedges;
+
+ // Reinterpret to a larger struct to make copies of whole triangles slightly more efficient
+ var constrainedHalfedges3 = constrainedHalfedges.AsArray().Reinterpret(1);
+ var triangles3 = triangles.AsArray().Reinterpret(4);
+
+ var wId = tIdMinVisited;
+ for (int rId = tIdMinVisited; rId < triangles3.Length; rId++)
+ {
+ if (!visitedTriangles[rId])
+ {
+ triangles3[wId] = triangles3[rId];
+ constrainedHalfedges3[wId] = constrainedHalfedges3[rId];
+ AdaptHe(halfedges, 0, rId, wId);
+ AdaptHe(halfedges, 1, rId, wId);
+ AdaptHe(halfedges, 2, rId, wId);
+ wId++;
+ }
+ else
+ {
+ DisableHe(halfedges, 0, rId);
+ DisableHe(halfedges, 1, rId);
+ DisableHe(halfedges, 2, rId);
+
+ for (int i = 0; i < halfedgeLoop.Length; i++)
+ {
+ var he = halfedgeLoop[i];
+ halfedgeLoop[i] = he != -1 && he / 3 > wId ? he - 3 : he;
+ }
+ }
+ }
+
+ // Trim the data to reflect removed triangles.
+ triangles.Length = 3 * wId;
+ constrainedHalfedges.Length = 3 * wId;
+ halfedges.Length = 3 * wId;
+ }
+
+ private static void TriangulateCavity(OutputData output, NativeArray pIdLoop, NativeList cavityTriangles, NativeList cavityHalfedges, Allocator allocator)
+ {
+ using var cavityPositions = new NativeArray(pIdLoop.Length, allocator);
+ using var cavityConstraints = new NativeArray(2 * pIdLoop.Length, allocator);
+
+ void BuildInput(NativeArray cavityPositions, NativeArray cavityConstraints)
+ {
+ for (int i = 0; i < pIdLoop.Length; i++)
+ {
+ cavityPositions[i] = output.Positions[pIdLoop[i]];
+ }
+ for (int i = 0; i < pIdLoop.Length - 1; i++)
+ {
+ cavityConstraints[2 * i + 0] = i;
+ cavityConstraints[2 * i + 1] = i + 1;
+ }
+ cavityConstraints[^2] = pIdLoop.Length - 1;
+ cavityConstraints[^1] = 0;
+ }
+
+ BuildInput(cavityPositions, cavityConstraints);
+
+ new UnsafeTriangulator().Triangulate(
+ input: new() { Positions = cavityPositions, ConstraintEdges = cavityConstraints },
+ output: new() { Triangles = cavityTriangles, Halfedges = cavityHalfedges },
+ args: Args.Default(restoreBoundary: true),
+ allocator
+ );
+ }
+
+ private static void MergeTriangulations(OutputData output, NativeArray pIdLoop, NativeList cavityTriangles, NativeList cavityHalfedges, NativeArray oheLoop, NativeArray oheConstrained)
+ {
+ output.ConstrainedHalfedges.Length += cavityHalfedges.Length;
+
+ var ell = output.Triangles.Length;
+ for (int i = 0; i < cavityHalfedges.Length; i++)
+ {
+ var he = cavityHalfedges[i];
+ if (he != -1)
+ {
+ cavityHalfedges[i] = he + ell;
+ }
+ else
+ {
+ var id = cavityTriangles[i];
+ var ohe = oheLoop[id];
+ output.ConstrainedHalfedges[i + ell] |= oheConstrained[id];
+ cavityHalfedges[i] = ohe;
+ if (ohe != -1)
+ {
+ output.Halfedges[ohe] = i + ell;
+ }
+ }
+ }
+
+ for (int i = 0; i < cavityTriangles.Length; i++)
+ {
+ cavityTriangles[i] = pIdLoop[cavityTriangles[i]];
+ }
+
+ output.Triangles.AddRange(cavityTriangles.AsArray());
+ output.Halfedges.AddRange(cavityHalfedges.AsArray());
+ }
+
+ private static void AdaptPoints(OutputData output, int pId)
+ {
+ output.Positions.RemoveAt(pId);
+
+ var triangles = output.Triangles;
+ for (int i = 0; i < triangles.Length; i++)
+ {
+ var t = triangles[i];
+ triangles[i] = t > pId ? t - 1 : t;
+ }
+ }
+
private void PreProcessInputStep(InputData input, OutputData output, Args args, out NativeArray localHoles, out TTransform lt, Allocator allocator)
{
using var _ = Markers.PreProcessInputStep.Auto();
diff --git a/Tests/TestUtils.cs b/Tests/TestUtils.cs
index 3756be1..f165f50 100644
--- a/Tests/TestUtils.cs
+++ b/Tests/TestUtils.cs
@@ -194,6 +194,7 @@ public static void PlantHoleSeeds(this LowLevel.Unsafe.UnsafeTriangulator
#endif
_ => (dynamic)alpha,
}, allocator);
+ public static void DynamicRemoveBulkPoint(this LowLevel.Unsafe.UnsafeTriangulator triangulator, LowLevel.Unsafe.OutputData output, int pId, Allocator allocator) where T : unmanaged => LowLevel.Unsafe.Extensions.DynamicRemoveBulkPoint((dynamic)triangulator, (dynamic)output, pId, allocator);
public static void Run(this Triangulator triangulator) where T : unmanaged =>
Extensions.Run((dynamic)triangulator);
public static JobHandle Schedule(this Triangulator triangulator, JobHandle dependencies = default) where T : unmanaged =>
diff --git a/Tests/UnsafeTriangulatorEditorTests.cs b/Tests/UnsafeTriangulatorEditorTests.cs
index 33a98b0..845783e 100644
--- a/Tests/UnsafeTriangulatorEditorTests.cs
+++ b/Tests/UnsafeTriangulatorEditorTests.cs
@@ -737,5 +737,189 @@ float len(int he)
static int NextHalfedge(int he) => he % 3 == 2 ? he - 2 : he + 1;
}
+
+ [Test]
+ public void RemovePointPartialTriangulationTest()
+ {
+ var t = new UnsafeTriangulator();
+
+ using var inputPositions = new NativeArray(new[]
+ {
+ math.double2(0, 0),
+ math.double2(3, 0),
+ math.double2(3, 3),
+ math.double2(0, 3),
+ math.double2(1, 0.5f),
+ math.double2(2, 0.5f),
+ math.double2(2.5f, 1.5f),
+ math.double2(1.5f, 2.5f),
+ math.double2(0.5f, 1.5f),
+ math.double2(1.5f, 1.5f),
+ }.DynamicCast(), Allocator.Persistent);
+
+ using var outputPositions = new NativeList(Allocator.Persistent);
+ using var triangles = new NativeList(Allocator.Persistent);
+ using var constrainedHalfedges = new NativeList(Allocator.Persistent);
+ using var halfedges = new NativeList(Allocator.Persistent);
+ t.Triangulate(
+ input: new() { Positions = inputPositions },
+ output: new() { Positions = outputPositions, Triangles = triangles, ConstrainedHalfedges = constrainedHalfedges, Halfedges = halfedges },
+ args: Args.Default(),
+ allocator: Allocator.Persistent
+ );
+
+ TestUtils.Draw(outputPositions.AsReadOnly().CastToFloat2(), triangles.AsReadOnly(), Color.blue, 5f);
+
+ var constrain = constrainedHalfedges.AsArray();
+ for (int i = 0; i < triangles.Length; i++)
+ {
+ var p = triangles[i];
+ var q = triangles[NextHalfedge(i)];
+ static int NextHalfedge(int he) => he % 3 == 2 ? he - 2 : he + 1;
+ if (p == 5 && q == 6)
+ {
+ constrain[i] = true;
+ constrain[halfedges[i]] = true;
+ break;
+ }
+ }
+
+ t.DynamicRemoveBulkPoint(
+ output: new() { Positions = outputPositions, Triangles = triangles, ConstrainedHalfedges = constrainedHalfedges, Halfedges = halfedges },
+ pId: 9,
+ allocator: Allocator.Persistent
+ );
+
+ TestUtils.Draw(outputPositions.AsReadOnly().CastToFloat2(), triangles.AsReadOnly(), Color.red, 5f);
+
+ Assert.That(outputPositions.AsReadOnly(), Is.EqualTo(inputPositions.AsReadOnly().ToArray()[..^1]));
+ Assert.That(triangles.AsReadOnly(), Is.EqualTo(
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
+ new[] { 6, 1, 5, 5, 1, 4, 7, 2, 6, 6, 2, 1, 1, 0, 4, 4, 0, 8, 8, 3, 7, 7, 3, 2, 0, 3, 8, 6, 5, 4, 4, 8, 6, 8, 7, 6, }
+ ));
+ Assert.That(halfedges.AsReadOnly(), Is.EqualTo(
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
+ new[] { 11, 3, 27, 1, 14, 28, 23, 9, 34, 7, -1, 0, -1, 15, 4, 13, 26, 30, 25, 21, 33, 19, -1, 6, -1, 18, 16, 2, 5, 32, 17, 35, 29, 20, 8, 31, }
+ ));
+ var expectedConstrained = new bool[36];
+ expectedConstrained[2] = true;
+ expectedConstrained[27] = true;
+ Assert.That(constrainedHalfedges.AsReadOnly(), Is.EqualTo(expectedConstrained));
+ }
+
+ [Test]
+ public void RemovePointFullTriangulationTest()
+ {
+ var t = new UnsafeTriangulator();
+
+ using var inputPositions = new NativeArray(new[]
+ {
+ math.double2(0, 0),
+ math.double2(1, 0),
+ math.double2(2, 1),
+ math.double2(2, 2),
+ math.double2(1, 3),
+ math.double2(0, 3),
+ math.double2(-1, 2),
+ math.double2(-1, 1),
+ math.double2(1, 1),
+ }.DynamicCast(), Allocator.Persistent);
+
+ using var outputPositions = new NativeList(Allocator.Persistent);
+ using var triangles = new NativeList(Allocator.Persistent);
+ using var constrainedHalfedges = new NativeList(Allocator.Persistent);
+ using var halfedges = new NativeList(Allocator.Persistent);
+ t.Triangulate(
+ input: new() { Positions = inputPositions },
+ output: new() { Positions = outputPositions, Triangles = triangles, ConstrainedHalfedges = constrainedHalfedges, Halfedges = halfedges },
+ args: Args.Default(),
+ allocator: Allocator.Persistent
+ );
+
+ TestUtils.Draw(outputPositions.AsReadOnly().CastToFloat2(), triangles.AsReadOnly(), Color.blue, 5f);
+
+ var constrain = constrainedHalfedges.AsArray();
+ for (int i = 0; i < triangles.Length; i++)
+ {
+ var p = triangles[i];
+ var q = triangles[NextHalfedge(i)];
+ static int NextHalfedge(int he) => he % 3 == 2 ? he - 2 : he + 1;
+ if (p == 1 && q == 0)
+ {
+ constrain[i] = true;
+ break;
+ }
+ }
+
+ t.DynamicRemoveBulkPoint(
+ output: new() { Positions = outputPositions, Triangles = triangles, ConstrainedHalfedges = constrainedHalfedges, Halfedges = halfedges },
+ pId: 8,
+ allocator: Allocator.Persistent
+ );
+
+ TestUtils.Draw(outputPositions.AsReadOnly().CastToFloat2(), triangles.AsReadOnly(), Color.red, 5f);
+
+ Assert.That(outputPositions.AsReadOnly(), Is.EqualTo(inputPositions.AsReadOnly().ToArray()[..^1]));
+ Assert.That(triangles.AsReadOnly(), Is.EqualTo(
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
+ new[] { 1, 0, 7, 7, 6, 1, 6, 5, 1, 5, 4, 1, 4, 3, 1, 3, 2, 1, }
+ ));
+ Assert.That(halfedges.AsReadOnly(), Is.EqualTo(
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
+ new[] { -1, -1, 5, -1, 8, 2, -1, 11, 4, -1, 14, 7, -1, 17, 10, -1, -1, 13, }
+ ));
+ Assert.That(constrainedHalfedges.AsReadOnly(), Is.EqualTo(new[] {
+ true, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ false, false, false, false, false,
+ }));
+ }
+
+ [Test]
+ public void RemovePointFromMiddleIndexTest()
+ {
+ var t = new UnsafeTriangulator();
+
+ using var inputPositions = new NativeArray(new[]
+ {
+ math.double2(0, 0),
+ math.double2(1, 0),
+ math.double2(0.5f, 1),
+ math.double2(1.5f, 1.5f),
+ math.double2(0.5f, 2f),
+ math.double2(-0.5f, 1.5f),
+ }.DynamicCast(), Allocator.Persistent);
+
+ using var outputPositions = new NativeList(Allocator.Persistent);
+ using var triangles = new NativeList(Allocator.Persistent);
+ using var constrainedHalfedges = new NativeList(Allocator.Persistent);
+ using var halfedges = new NativeList(Allocator.Persistent);
+ t.Triangulate(
+ input: new() { Positions = inputPositions },
+ output: new() { Positions = outputPositions, Triangles = triangles, ConstrainedHalfedges = constrainedHalfedges, Halfedges = halfedges },
+ args: Args.Default(),
+ allocator: Allocator.Persistent
+ );
+
+ TestUtils.Draw(outputPositions.AsReadOnly().CastToFloat2(), triangles.AsReadOnly(), Color.blue, 5f);
+
+ t.DynamicRemoveBulkPoint(
+ output: new() { Positions = outputPositions, Triangles = triangles, ConstrainedHalfedges = constrainedHalfedges, Halfedges = halfedges },
+ pId: 2,
+ allocator: Allocator.Persistent
+ );
+
+ TestUtils.Draw(outputPositions.AsReadOnly().CastToFloat2(), triangles.AsReadOnly(), Color.red, 5f);
+
+ Assert.That(triangles.AsReadOnly(), Is.EqualTo(
+ // 0 1 2 3 4 5 6 7 8
+ new[] { 3, 2, 1, 1, 0, 3, 0, 4, 3, }
+ ));
+ Assert.That(halfedges.AsReadOnly(), Is.EqualTo(
+ // 0 1 2 3 4 5 6 7 8
+ new[] { -1, -1, 5, -1, 8, 2, -1, -1, 4, }
+ ));
+ }
}
}