Skip to content

Commit

Permalink
pref: refinement constrained halfedges
Browse files Browse the repository at this point in the history
This commit significantly improves the performance of the refinement job. However, the introduced buffer for constrained halfedges should be implemented in other jobs as well.
  • Loading branch information
andywiecko committed Dec 17, 2023
1 parent d183d25 commit a5c2c72
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 70 deletions.
129 changes: 74 additions & 55 deletions Runtime/Triangulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1574,6 +1574,8 @@ private struct RefineMeshJob : IJob
private NativeList<bool> visitedTriangles;
[NativeDisableContainerSafetyRestriction]
private NativeList<Edge> constraints;
[NativeDisableContainerSafetyRestriction]
private NativeList<bool> constrainedHalfedges;

public RefineMeshJob(Triangulator triangulator, NativeList<Edge> constraints)
{
Expand All @@ -1595,6 +1597,7 @@ public RefineMeshJob(Triangulator triangulator, NativeList<Edge> constraints)
pathPoints = default;
pathHalfedges = default;
visitedTriangles = default;
constrainedHalfedges = default;

this.constraints = constraints;
}
Expand All @@ -1619,6 +1622,7 @@ public void Execute()
using var _pathPoints = pathPoints = new NativeList<int>(Allocator.Temp);
using var _pathHalfedges = pathHalfedges = new NativeList<int>(Allocator.Temp);
using var _visitedTriangles = visitedTriangles = new NativeList<bool>(triangles.Length / 3, Allocator.Temp);
using var _constrainedHalfedges = constrainedHalfedges = new NativeList<bool>(triangles.Length, Allocator.Temp) { Length = triangles.Length };

using var heQueue = new NativeList<int>(triangles.Length, Allocator.Temp);
using var tQueue = new NativeList<int>(triangles.Length, Allocator.Temp);
Expand Down Expand Up @@ -1647,30 +1651,27 @@ public void Execute()
}
}

// Collect encroached half-edges.
for (int id = 0; id < constraints.Length; id++)
for (int he = 0; he < constrainedHalfedges.Length; he++)
{
var (ci, cj) = constraints[id];
var h = -1;
for (int he = 0; he < triangles.Length; he++)
for (int id = 0; id < constraints.Length; id++)
{
var (ci, cj) = constraints[id];
var (i, j) = (triangles[he], triangles[NextHalfedge(he)]);
(i, j) = i < j ? (i, j) : (j, i);
if (ci == i && cj == j)
{
h = he;
constrainedHalfedges[he] = true;
break;
}
}
}

var oh = halfedges[h];
if (IsEncroached(h))
{
heQueue.Add(h);
}
else if (oh != -1 && IsEncroached(oh))
// Collect encroached half-edges.
for (int he = 0; he < constrainedHalfedges.Length; he++)
{
if (constrainedHalfedges[he] && IsEncroached(he))
{
heQueue.Add(oh);
heQueue.Add(he);
}
}

Expand Down Expand Up @@ -1751,13 +1752,12 @@ private void SplitEdge(int he, NativeList<int> heQueue, NativeList<int> tQueue)
p = (1 - alpha) * e0 + alpha * e1;
}

var edge = new Edge(i, j);
var pId = outputPositions.Length;
var eId = constraints.IndexOf(edge);

constraints.RemoveAt(eId);
constraints.Add((pId, i));
constraints.Add((pId, j));
constrainedHalfedges[he] = false;
var ohe = halfedges[he];
if (ohe != -1)
{
constrainedHalfedges[ohe] = false;
}

if (halfedges[he] != -1)
{
Expand Down Expand Up @@ -1800,6 +1800,11 @@ private void SplitEdge(int he, NativeList<int> heQueue, NativeList<int> tQueue)
{
heQueue.Add(ohj);
}

constrainedHalfedges[hi] = true;
constrainedHalfedges[ohi] = true;
constrainedHalfedges[hj] = true;
constrainedHalfedges[ohj] = true;
}
else
{
Expand All @@ -1819,6 +1824,9 @@ private void SplitEdge(int he, NativeList<int> heQueue, NativeList<int> tQueue)
{
heQueue.Add(hj);
}

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

Expand All @@ -1836,25 +1844,21 @@ private void SplitTriangle(int tId, NativeList<int> heQueue, NativeList<int> tQu
var c = circles[tId];
var edges = new NativeList<int>(Allocator.Temp);

for (int id = 0; id < constraints.Length; id++)
for (int he = 0; he < constrainedHalfedges.Length; he++)
{
var (ci, cj) = constraints[id];
var h = -1;
for (int he = 0; he < triangles.Length; he++)
if (!constrainedHalfedges[he])
{
var (i, j) = (triangles[he], triangles[NextHalfedge(he)]);
(i, j) = i < j ? (i, j) : (j, i);
if (ci == i && cj == j)
{
h = he;
break;
}
continue;
}

var (p0, p1) = (outputPositions[ci], outputPositions[cj]);
if (math.dot(p0 - c.Center, p1 - c.Center) <= 0)
var (i, j) = (triangles[he], triangles[NextHalfedge(he)]);
if (halfedges[he] == -1 || i < j)
{
edges.Add(h);
var (p0, p1) = (outputPositions[i], outputPositions[j]);
if (math.dot(p0 - c.Center, p1 - c.Center) <= 0)
{
edges.Add(he);
}
}
}

Expand Down Expand Up @@ -1957,22 +1961,16 @@ private void RecalculateBadTriangles(float2 p)
{
while (trianglesQueue.TryDequeue(out var tId))
{
VisitEdge(p, 3 * tId + 0, 3 * tId + 1);
VisitEdge(p, 3 * tId + 1, 3 * tId + 2);
VisitEdge(p, 3 * tId + 2, 3 * tId + 0);
VisitEdge(p, 3 * tId + 0);
VisitEdge(p, 3 * tId + 1);
VisitEdge(p, 3 * tId + 2);
}
}

private void VisitEdge(float2 p, int t0, int t1)
private void VisitEdge(float2 p, int t0)
{
var e = new Edge(triangles[t0], triangles[t1]);
if (constraints.Contains(e))
{
return;
}

var he = halfedges[t0];
if (he == -1)
if (he == -1 || constrainedHalfedges[he])
{
return;
}
Expand Down Expand Up @@ -2078,6 +2076,9 @@ private void ProcessBadTriangles(int pId, NativeList<int> heQueue, NativeList<in
RemoveHalfedge(3 * tId + 2, 0);
RemoveHalfedge(3 * tId + 1, 1);
RemoveHalfedge(3 * tId + 0, 2);
constrainedHalfedges.RemoveAt(3 * tId + 2);
constrainedHalfedges.RemoveAt(3 * tId + 1);
constrainedHalfedges.RemoveAt(3 * tId + 0);

for (int i = 3 * tId; i < halfedges.Length; i++)
{
Expand Down Expand Up @@ -2169,13 +2170,19 @@ private void BuildNewTriangles(int pId, NativeList<int> heQueue, NativeList<int>
// Build half-edges for inserted point pId.
var heOffset = halfedges.Length;
halfedges.Length += 3 * pathPoints.Length;
constrainedHalfedges.Length += 3 * pathPoints.Length;
for (int i = 0; i < pathPoints.Length - 1; i++)
{
var he = pathHalfedges[i];
halfedges[3 * i + 1 + heOffset] = pathHalfedges[i];
halfedges[3 * i + 1 + heOffset] = he;
if (he != -1)
{
halfedges[pathHalfedges[i]] = 3 * i + 1 + heOffset;
halfedges[he] = 3 * i + 1 + heOffset;
constrainedHalfedges[3 * i + 1 + heOffset] = constrainedHalfedges[he];
}
else
{
constrainedHalfedges[3 * i + 1 + heOffset] = true;
}
halfedges[3 * i + 2 + heOffset] = 3 * i + 3 + heOffset;
halfedges[3 * i + 3 + heOffset] = 3 * i + 2 + heOffset;
Expand All @@ -2185,6 +2192,11 @@ private void BuildNewTriangles(int pId, NativeList<int> heQueue, NativeList<int>
if (phe != -1)
{
halfedges[phe] = heOffset + 3 * (pathPoints.Length - 1) + 1;
constrainedHalfedges[heOffset + 3 * (pathPoints.Length - 1) + 1] = constrainedHalfedges[phe];
}
else
{
constrainedHalfedges[heOffset + 3 * (pathPoints.Length - 1) + 1] = true;
}
halfedges[heOffset] = heOffset + 3 * (pathPoints.Length - 1) + 2;
halfedges[heOffset + 3 * (pathPoints.Length - 1) + 2] = heOffset;
Expand All @@ -2194,9 +2206,7 @@ private void BuildNewTriangles(int pId, NativeList<int> heQueue, NativeList<int>
for (int i = 0; i < pathPoints.Length - 1; i++)
{
var he = heOffset + 3 * i + 1;
var edge = new Edge(triangles[he], triangles[NextHalfedge(he)]);

if (constraints.Contains(edge) && IsEncroached(he))
if (constrainedHalfedges[he] && IsEncroached(he))
{
heQueue.Add(he);
}
Expand Down Expand Up @@ -2225,13 +2235,19 @@ private void BuildNewTrianglesForAmphitheater(int pId, NativeList<int> heQueue,
// Build half-edges for inserted point pId.
var heOffset = halfedges.Length;
halfedges.Length += 3 * (pathPoints.Length - 1);
constrainedHalfedges.Length += 3 * (pathPoints.Length - 1);
for (int i = 0; i < pathPoints.Length - 2; i++)
{
var he = pathHalfedges[i];
halfedges[3 * i + 1 + heOffset] = pathHalfedges[i];
halfedges[3 * i + 1 + heOffset] = he;
if (he != -1)
{
halfedges[pathHalfedges[i]] = 3 * i + 1 + heOffset;
halfedges[he] = 3 * i + 1 + heOffset;
constrainedHalfedges[3 * i + 1 + heOffset] = constrainedHalfedges[he];
}
else
{
constrainedHalfedges[3 * i + 1 + heOffset] = true;
}
halfedges[3 * i + 2 + heOffset] = 3 * i + 3 + heOffset;
halfedges[3 * i + 3 + heOffset] = 3 * i + 2 + heOffset;
Expand All @@ -2242,6 +2258,11 @@ private void BuildNewTrianglesForAmphitheater(int pId, NativeList<int> heQueue,
if (phe != -1)
{
halfedges[phe] = heOffset + 3 * (pathPoints.Length - 2) + 1;
constrainedHalfedges[heOffset + 3 * (pathPoints.Length - 2) + 1] = constrainedHalfedges[phe];
}
else
{
constrainedHalfedges[heOffset + 3 * (pathPoints.Length - 2) + 1] = true;
}
halfedges[heOffset] = -1;
halfedges[heOffset + 3 * (pathPoints.Length - 2) + 2] = -1;
Expand All @@ -2251,9 +2272,7 @@ private void BuildNewTrianglesForAmphitheater(int pId, NativeList<int> heQueue,
for (int i = 0; i < pathPoints.Length - 1; i++)
{
var he = heOffset + 3 * i + 1;
var edge = new Edge(triangles[he], triangles[NextHalfedge(he)]);

if (constraints.Contains(edge) && IsEncroached(he))
if (constrainedHalfedges[he] && IsEncroached(he))
{
heQueue.Add(he);
}
Expand Down
72 changes: 57 additions & 15 deletions Tests/TriangulatorEditorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1082,16 +1082,16 @@ public void TriangulationWithHolesWithRefinementTest()
(9, 1, 5),
(9, 5, 6),
(9, 6, 2),
(10, 2, 6),
(10, 6, 7),
(10, 7, 3),
(11, 3, 7),
(11, 4, 0),
(11, 7, 4),
(10, 3, 7),
(10, 4, 0),
(10, 7, 4),
(11, 2, 6),
(11, 6, 7),
(11, 7, 3),
};

Assert.That(triangulator.Output.Positions.AsArray(), Is.EqualTo(expectedPositions));
Assert.That(triangulator.GetTrisTuple(), Is.EqualTo(expectedTriangles));
Assert.That(triangulator.Output.Positions.AsArray(), Is.EquivalentTo(expectedPositions));
Assert.That(triangulator.GetTrisTuple(), Is.EquivalentTo(expectedTriangles));
}

[BurstCompile]
Expand Down Expand Up @@ -1195,15 +1195,15 @@ public void DeferredArraySupportTest()
(9, 1, 5),
(9, 5, 6),
(9, 6, 2),
(10, 2, 6),
(10, 6, 7),
(10, 7, 3),
(11, 3, 7),
(11, 4, 0),
(11, 7, 4),
(10, 3, 7),
(10, 4, 0),
(10, 7, 4),
(11, 2, 6),
(11, 6, 7),
(11, 7, 3),
};

Assert.That(triangulator.Output.Positions.AsArray(), Is.EqualTo(expectedPositions));
Assert.That(triangulator.Output.Positions.AsArray(), Is.EquivalentTo(expectedPositions));
Assert.That(triangulator.GetTrisTuple(), Is.EquivalentTo(expectedTriangles));
}

Expand Down Expand Up @@ -1850,5 +1850,47 @@ public void AccuteInputAngleTest()

triangulator.Run();
}

[Test]
public void GenericCase1Test()
{
using var positions = new NativeArray<float2>(new[] {
math.float2(0, 0),
math.float2(3, 0),
math.float2(3, 1),
math.float2(0, 1),
}, Allocator.Persistent);
using var constraints = new NativeArray<int>(new[] {
0, 1,
1, 2,
2, 3,
3, 0,
0, 2,
}, Allocator.Persistent);
using var triangulator = new Triangulator(1024 * 1024, Allocator.Persistent)
{
Settings =
{
ValidateInput = true,
RefineMesh = true,
RestoreBoundary = true,
ConstrainEdges = true,
RefinementThresholds =
{
Area = .1f,
Angle = math.radians(33f),
}
},
Input =
{
Positions = positions,
ConstraintEdges = constraints,
}
};

triangulator.Run();

triangulator.Draw();
}
}
}

0 comments on commit a5c2c72

Please sign in to comment.