Skip to content

Commit

Permalink
feat: dynamic split halfedge
Browse files Browse the repository at this point in the history
  • Loading branch information
andywiecko committed Nov 9, 2024
1 parent a11adde commit e09e7ab
Show file tree
Hide file tree
Showing 3 changed files with 337 additions and 7 deletions.
173 changes: 173 additions & 0 deletions Runtime/Triangulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,25 @@ public static void DynamicInsertPoint(this UnsafeTriangulator @this, OutputData<
var p = bar.x * p0 + bar.y * p1 + bar.z * p2;
new UnsafeTriangulator<double, double2, double, TransformDouble, DoubleUtils>().DynamicInsertPoint(output, tId, p, allocator);
}
/// <summary>
/// Splits the halfedge specified by <paramref name="he"/> by inserting a point at a position determined by linear interpolation.
/// The position is interpolated between the start and end points of the halfedge in the triangulation <paramref name="output"/>
/// using <paramref name="alpha"/> as the interpolation parameter.
/// This method preserves the "constrained" state of the halfedge, meaning that if the specified halfedge is constrained,
/// the two resulting sub-segments will also be marked as constrained.
/// </summary>
/// <remarks>
/// <b>Note:</b>
/// This method requires that <paramref name="output"/> contains valid triangulation data.
/// The <paramref name="output"/> native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
/// </remarks>
/// <param name="he">The index of the halfedge to split.</param>
/// <param name="alpha">
/// The interpolation parameter for positioning the new point between the start and end points of the halfedge,
/// where <c>p = (1 - alpha) * start + alpha * end</c>.
/// </param>
/// <param name="allocator">The allocator to use. If called from a job, consider using <see cref="Allocator.Temp"/>.</param>
public static void DynamicSplitHalfedge(this UnsafeTriangulator @this, OutputData<double2> output, int he, double alpha, Allocator allocator) => new UnsafeTriangulator<double, double2, double, TransformDouble, DoubleUtils>().DynamicSplitHalfedge(output, he, alpha, allocator);

/// <summary>
/// Performs triangulation on the given <paramref name="input"/>, producing the result in <paramref name="output"/> based on the settings specified in <paramref name="args"/>.
Expand Down Expand Up @@ -914,6 +933,25 @@ public static void DynamicInsertPoint(this UnsafeTriangulator<float2> @this, Out
var p = bar.x * p0 + bar.y * p1 + bar.z * p2;
new UnsafeTriangulator<float, float2, float, TransformFloat, FloatUtils>().DynamicInsertPoint(output, tId, p, allocator);
}
/// <summary>
/// Splits the halfedge specified by <paramref name="he"/> by inserting a point at a position determined by linear interpolation.
/// The position is interpolated between the start and end points of the halfedge in the triangulation <paramref name="output"/>
/// using <paramref name="alpha"/> as the interpolation parameter.
/// This method preserves the "constrained" state of the halfedge, meaning that if the specified halfedge is constrained,
/// the two resulting sub-segments will also be marked as constrained.
/// </summary>
/// <remarks>
/// <b>Note:</b>
/// This method requires that <paramref name="output"/> contains valid triangulation data.
/// The <paramref name="output"/> native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
/// </remarks>
/// <param name="he">The index of the halfedge to split.</param>
/// <param name="alpha">
/// The interpolation parameter for positioning the new point between the start and end points of the halfedge,
/// where <c>p = (1 - alpha) * start + alpha * end</c>.
/// </param>
/// <param name="allocator">The allocator to use. If called from a job, consider using <see cref="Allocator.Temp"/>.</param>
public static void DynamicSplitHalfedge(this UnsafeTriangulator<float2> @this, OutputData<float2> output, int he, float alpha, Allocator allocator) => new UnsafeTriangulator<float, float2, float, TransformFloat, FloatUtils>().DynamicSplitHalfedge(output, he, alpha, allocator);

/// <summary>
/// Performs triangulation on the given <paramref name="input"/>, producing the result in <paramref name="output"/> based on the settings specified in <paramref name="args"/>.
Expand Down Expand Up @@ -968,6 +1006,25 @@ public static void DynamicInsertPoint(this UnsafeTriangulator<float2> @this, Out
/// <param name="allocator">The allocator to use. If called from a job, consider using <see cref="Allocator.Temp"/>.</param>
public static void DynamicInsertPoint(this UnsafeTriangulator<Vector2> @this, OutputData<Vector2> output, int tId, Vector3 bar, Allocator allocator) =>
new UnsafeTriangulator<float2>().DynamicInsertPoint(UnsafeUtility.As<OutputData<Vector2>, OutputData<float2>>(ref output), tId, bar, allocator);
/// <summary>
/// Splits the halfedge specified by <paramref name="he"/> by inserting a point at a position determined by linear interpolation.
/// The position is interpolated between the start and end points of the halfedge in the triangulation <paramref name="output"/>
/// using <paramref name="alpha"/> as the interpolation parameter.
/// This method preserves the "constrained" state of the halfedge, meaning that if the specified halfedge is constrained,
/// the two resulting sub-segments will also be marked as constrained.
/// </summary>
/// <remarks>
/// <b>Note:</b>
/// This method requires that <paramref name="output"/> contains valid triangulation data.
/// The <paramref name="output"/> native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
/// </remarks>
/// <param name="he">The index of the halfedge to split.</param>
/// <param name="alpha">
/// The interpolation parameter for positioning the new point between the start and end points of the halfedge,
/// where <c>p = (1 - alpha) * start + alpha * end</c>.
/// </param>
/// <param name="allocator">The allocator to use. If called from a job, consider using <see cref="Allocator.Temp"/>.</param>
public static void DynamicSplitHalfedge(this UnsafeTriangulator<Vector2> @this, OutputData<Vector2> output, int he, float alpha, Allocator allocator) => new UnsafeTriangulator<float2>().DynamicSplitHalfedge(UnsafeUtility.As<OutputData<Vector2>, OutputData<float2>>(ref output), he, alpha, allocator);

/// <summary>
/// Performs triangulation on the given <paramref name="input"/>, producing the result in <paramref name="output"/> based on the settings specified in <paramref name="args"/>.
Expand Down Expand Up @@ -1027,6 +1084,25 @@ public static void DynamicInsertPoint(this UnsafeTriangulator<double2> @this, Ou
var p = bar.x * p0 + bar.y * p1 + bar.z * p2;
new UnsafeTriangulator<double, double2, double, TransformDouble, DoubleUtils>().DynamicInsertPoint(output, tId, p, allocator);
}
/// <summary>
/// Splits the halfedge specified by <paramref name="he"/> by inserting a point at a position determined by linear interpolation.
/// The position is interpolated between the start and end points of the halfedge in the triangulation <paramref name="output"/>
/// using <paramref name="alpha"/> as the interpolation parameter.
/// This method preserves the "constrained" state of the halfedge, meaning that if the specified halfedge is constrained,
/// the two resulting sub-segments will also be marked as constrained.
/// </summary>
/// <remarks>
/// <b>Note:</b>
/// This method requires that <paramref name="output"/> contains valid triangulation data.
/// The <paramref name="output"/> native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
/// </remarks>
/// <param name="he">The index of the halfedge to split.</param>
/// <param name="alpha">
/// The interpolation parameter for positioning the new point between the start and end points of the halfedge,
/// where <c>p = (1 - alpha) * start + alpha * end</c>.
/// </param>
/// <param name="allocator">The allocator to use. If called from a job, consider using <see cref="Allocator.Temp"/>.</param>
public static void DynamicSplitHalfedge(this UnsafeTriangulator<double2> @this, OutputData<double2> output, int he, double alpha, Allocator allocator) => new UnsafeTriangulator<double, double2, double, TransformDouble, DoubleUtils>().DynamicSplitHalfedge(output, he, alpha, allocator);

/// <summary>
/// Performs triangulation on the given <paramref name="input"/>, producing the result in <paramref name="output"/> based on the settings specified in <paramref name="args"/>.
Expand Down Expand Up @@ -1109,6 +1185,25 @@ public static void DynamicInsertPoint(this UnsafeTriangulator<fp2> @this, Output
var p = bar.x * p0 + bar.y * p1 + bar.z * p2;
new UnsafeTriangulator<fp, fp2, fp, TransformFp, FpUtils>().DynamicInsertPoint(output, tId, p, allocator);
}
/// <summary>
/// Splits the halfedge specified by <paramref name="he"/> by inserting a point at a position determined by linear interpolation.
/// The position is interpolated between the start and end points of the halfedge in the triangulation <paramref name="output"/>
/// using <paramref name="alpha"/> as the interpolation parameter.
/// This method preserves the "constrained" state of the halfedge, meaning that if the specified halfedge is constrained,
/// the two resulting sub-segments will also be marked as constrained.
/// </summary>
/// <remarks>
/// <b>Note:</b>
/// This method requires that <paramref name="output"/> contains valid triangulation data.
/// The <paramref name="output"/> native containers must be allocated by the user. Some buffers are optional; refer to the documentation for more details.
/// </remarks>
/// <param name="he">The index of the halfedge to split.</param>
/// <param name="alpha">
/// The interpolation parameter for positioning the new point between the start and end points of the halfedge,
/// where <c>p = (1 - alpha) * start + alpha * end</c>.
/// </param>
/// <param name="allocator">The allocator to use. If called from a job, consider using <see cref="Allocator.Temp"/>.</param>
public static void DynamicSplitHalfedge(this UnsafeTriangulator<fp2> @this, OutputData<fp2> output, int he, fp alpha, Allocator allocator) => new UnsafeTriangulator<fp, fp2, fp, TransformFp, FpUtils>().DynamicSplitHalfedge(output, he, alpha, allocator);
#endif
}

Expand Down Expand Up @@ -1280,6 +1375,84 @@ public void DynamicInsertPoint(OutputData<T2> output, int tId, T2 p, Allocator a
}.UnsafeInsertPointBulk(p, tId);
}

public void DynamicSplitHalfedge(OutputData<T2> output, int he, T alpha, Allocator allocator)
{
using var badTriangles = new NativeList<int>(allocator);
using var pathHalfedges = new NativeList<int>(allocator);
using var pathPoints = new NativeList<int>(allocator);
using var trianglesQueue = new NativeQueue<int>(allocator);
using var visitedTriangles = new NativeList<bool>(allocator);

var triangles = output.Triangles;
var halfedges = output.Halfedges;
var constrainedHalfedges = output.ConstrainedHalfedges;

var (i, j) = (triangles[he], triangles[NextHalfedge(he)]);
var (p0, p1) = (output.Positions[i], output.Positions[j]);
var p = utils.lerp(p0, p1, alpha);

var bw = new RefineMeshStep.UnsafeBowerWatson
{
Circles = default,
Output = output,
BadTriangles = badTriangles,
PathHalfedges = pathHalfedges,
PathPoints = pathPoints,
TrianglesQueue = trianglesQueue,
VisitedTriangles = visitedTriangles,
};

constrainedHalfedges[he] = false;
var ohe = halfedges[he];
if (ohe != -1)
{
constrainedHalfedges[ohe] = false;
}

if (ohe == -1)
{
bw.UnsafeInsertPointBoundary(p, he);

//var h0 = triangles.Length - 3;
var id = 3 * (pathPoints.Length - 1);
var hi = halfedges.Length - 1;
var hj = halfedges.Length - id;
constrainedHalfedges[hi] = true;
constrainedHalfedges[hj] = true;
}
else
{
bw.UnsafeInsertPointBulk(p, he / 3);

var h0 = triangles.Length - 3;
var hi = -1;
var hj = -1;
while (hi == -1 || hj == -1)
{
var h1 = NextHalfedge(h0);
if (triangles[h1] == i)
{
hi = h0;
}
if (triangles[h1] == j)
{
hj = h0;
}

var h2 = NextHalfedge(h1);
h0 = halfedges[h2];
}

var ohi = halfedges[hi];
var ohj = halfedges[hj];

constrainedHalfedges[hi] = true;
constrainedHalfedges[ohi] = true;
constrainedHalfedges[hj] = true;
constrainedHalfedges[ohj] = true;
}
}

private void PreProcessInputStep(InputData<T2> input, OutputData<T2> output, Args args, out NativeArray<T2> localHoles, out TTransform lt, Allocator allocator)
{
using var _ = Markers.PreProcessInputStep.Auto();
Expand Down
22 changes: 15 additions & 7 deletions Tests/TestUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ public static class TestExtensions
// TODO: add cast in the fork repo.
public static fp2 ToFp2(this float2 v) => new((fp)v.x, (fp)v.y);
#endif
public static float2 ToFloat2<T2>(this T2 v) where T2 : unmanaged => default(T2) switch
{
#if UNITY_MATHEMATICS_FIXEDPOINT
fp2 => math.float2((float)((dynamic)v).x, (float)((dynamic)v).y),
#endif
_ => (float2)(dynamic)v,
};
public static void Triangulate<T>(this LowLevel.Unsafe.UnsafeTriangulator<T> triangulator, LowLevel.Unsafe.InputData<T> input, LowLevel.Unsafe.OutputData<T> output, LowLevel.Unsafe.Args args, Allocator allocator) where T : unmanaged =>
LowLevel.Unsafe.Extensions.Triangulate((dynamic)triangulator, (dynamic)input, (dynamic)output, args, allocator);
public static void PlantHoleSeeds<T>(this LowLevel.Unsafe.UnsafeTriangulator<T> triangulator, LowLevel.Unsafe.InputData<T> input, LowLevel.Unsafe.OutputData<T> output, LowLevel.Unsafe.Args args, Allocator allocator) where T : unmanaged =>
Expand All @@ -180,6 +187,13 @@ public static void PlantHoleSeeds<T>(this LowLevel.Unsafe.UnsafeTriangulator<T>
_ => (dynamic)bar
},
allocator);
public static void DynamicSplitHalfedge<T2>(this LowLevel.Unsafe.UnsafeTriangulator<T2> triangulator, LowLevel.Unsafe.OutputData<T2> output, int he, float alpha, Allocator allocator) where T2 : unmanaged => LowLevel.Unsafe.Extensions.DynamicSplitHalfedge((dynamic)triangulator, (dynamic)output, he, default(T2) switch
{
#if UNITY_MATHEMATICS_FIXEDPOINT
fp2 => (fp)alpha,
#endif
_ => (dynamic)alpha,
}, allocator);
public static void Run<T>(this Triangulator<T> triangulator) where T : unmanaged =>
Extensions.Run((dynamic)triangulator);
public static JobHandle Schedule<T>(this Triangulator<T> triangulator, JobHandle dependencies = default) where T : unmanaged =>
Expand All @@ -196,13 +210,7 @@ public static float3 NextBarcoords3(ref this Unity.Mathematics.Random random)
var bz = 1 - bx - by;
return new(bx, by, bz);
}
public static float2[] CastToFloat2<T>(this IEnumerable<T> data) where T : unmanaged => data.Select(i => default(T) switch
{
#if UNITY_MATHEMATICS_FIXEDPOINT
fp2 => math.float2((float)((dynamic)i).x, (float)((dynamic)i).y),
#endif
_ => (float2)(dynamic)i,
}).ToArray();
public static float2[] CastToFloat2<T>(this IEnumerable<T> data) where T : unmanaged => data.Select(i => i.ToFloat2()).ToArray();
public static T[] DynamicCast<T>(this IEnumerable<float2> data) where T : unmanaged => default(T) switch
{
#if UNITY_MATHEMATICS_FIXEDPOINT
Expand Down
Loading

0 comments on commit e09e7ab

Please sign in to comment.