From 686b2b551df4bc2bf0d90539ad4e7042485a6248 Mon Sep 17 00:00:00 2001 From: badlogic Date: Wed, 19 Apr 2017 14:41:16 +0200 Subject: [PATCH] [csharp] Ported ConvexDecomposer --- spine-csharp/spine-csharp.csproj | 2 + spine-csharp/src/ConvexDecomposer.cs | 405 +++++++++++++-------------- 2 files changed, 203 insertions(+), 204 deletions(-) diff --git a/spine-csharp/spine-csharp.csproj b/spine-csharp/spine-csharp.csproj index f1326425f..1767eb8cc 100644 --- a/spine-csharp/spine-csharp.csproj +++ b/spine-csharp/spine-csharp.csproj @@ -71,6 +71,7 @@ + @@ -85,6 +86,7 @@ + diff --git a/spine-csharp/src/ConvexDecomposer.cs b/spine-csharp/src/ConvexDecomposer.cs index 23614d3a6..7151b6a73 100644 --- a/spine-csharp/src/ConvexDecomposer.cs +++ b/spine-csharp/src/ConvexDecomposer.cs @@ -28,241 +28,238 @@ * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +using System; + namespace Spine { internal class ConvexDecomposer { - // private final Array convexPolygons = new Array(); - // private final Array convexPolygonsIndices = new Array(); + private readonly ExposedList> convexPolygons = new ExposedList>(); + private readonly ExposedList> convexPolygonsIndices = new ExposedList>(); - // private final ShortArray indicesArray = new ShortArray(); - // private final BooleanArray isConcaveArray = new BooleanArray(); - // private final ShortArray triangles = new ShortArray(); + private readonly ExposedList indicesArray = new ExposedList(); + private readonly ExposedList isConcaveArray = new ExposedList(); + private readonly ExposedList triangles = new ExposedList(); - // private final Pool polygonPool = new Pool() { - // protected FloatArray newObject() { - // return new FloatArray(16); - // } - //}; + private readonly Pool> polygonPool = new Pool>(); + private readonly Pool> polygonIndicesPool = new Pool>(); - //private final Pool polygonIndicesPool = new Pool() { - // protected ShortArray newObject() { - // return new ShortArray(16); - //} - // }; + public ExposedList> Decompose(float[] input) { + var vertices = input; + int vertexCount = input.Length >> 1; - // public Array decompose(FloatArray input) { - // float[] vertices = input.items; - // int vertexCount = input.size >> 1; + var indicesArray = this.indicesArray; + indicesArray.Clear(); + short[] indices = indicesArray.Resize(vertexCount).Items; + for (short i = 0; i < vertexCount; i++) + indices[i] = i; - // ShortArray indicesArray = this.indicesArray; - // indicesArray.clear(); - // short[] indices = indicesArray.setSize(vertexCount); - // for (short i = 0; i < vertexCount; i++) - // indices[i] = i; + var isConcaveArray = this.isConcaveArray; + bool[] isConcave = isConcaveArray.Resize(vertexCount).Items; + for (int i = 0, n = vertexCount; i < n; ++i) + isConcave[i] = IsConcave(i, vertexCount, vertices, indices); - // BooleanArray isConcaveArray = this.isConcaveArray; - // boolean[] isConcave = isConcaveArray.setSize(vertexCount); - // for (int i = 0, n = vertexCount; i < n; ++i) - // isConcave[i] = isConcave(i, vertexCount, vertices, indices); + var triangles = this.triangles; + triangles.Clear(); + triangles.EnsureCapacity(Math.Max(0, vertexCount - 2) << 2); - // ShortArray triangles = this.triangles; - // triangles.clear(); - // triangles.ensureCapacity(Math.max(0, vertexCount - 2) << 2); + while (vertexCount > 3) { + // Find ear tip. + int previous = vertexCount - 1, i = 0, next = 1; + while (true) { + outer: + if (!isConcave[i]) { + int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; + float p1x = vertices[p1], p1y = vertices[p1 + 1]; + float p2x = vertices[p2], p2y = vertices[p2 + 1]; + float p3x = vertices[p3], p3y = vertices[p3 + 1]; + for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { + if (!isConcave[ii]) continue; + int v = indices[ii] << 1; + float vx = vertices[v], vy = vertices[v + 1]; + if (PositiveArea(p3x, p3y, p1x, p1y, vx, vy)) { + if (PositiveArea(p1x, p1y, p2x, p2y, vx, vy)) { + if (PositiveArea(p2x, p2y, p3x, p3y, vx, vy)) goto outer; + } + } + } + break; + } - // while (vertexCount > 3) { - // // Find ear tip. - // int previous = vertexCount - 1, i = 0, next = 1; - // while (true) { - // outer: - // if (!isConcave[i]) { - // int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; - // float p1x = vertices[p1], p1y = vertices[p1 + 1]; - // float p2x = vertices[p2], p2y = vertices[p2 + 1]; - // float p3x = vertices[p3], p3y = vertices[p3 + 1]; - // for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { - // if (!isConcave[ii]) continue; - // int v = indices[ii] << 1; - // float vx = vertices[v], vy = vertices[v + 1]; - // if (positiveArea(p3x, p3y, p1x, p1y, vx, vy)) { - // if (positiveArea(p1x, p1y, p2x, p2y, vx, vy)) { - // if (positiveArea(p2x, p2y, p3x, p3y, vx, vy)) break outer; - // } - // } - // } - // break; - // } + if (next == 0) { + do { + if (!isConcave[i]) break; + i--; + } while (i > 0); + break; + } - // if (next == 0) { - // do { - // if (!isConcave[i]) break; - // i--; - // } while (i > 0); - // break; - // } + previous = i; + i = next; + next = (next + 1) % vertexCount; + } - // previous = i; - // i = next; - // next = (next + 1) % vertexCount; - // } + // Cut ear tip. + triangles.Add(indices[(vertexCount + i - 1) % vertexCount]); + triangles.Add(indices[i]); + triangles.Add(indices[(i + 1) % vertexCount]); + indicesArray.RemoveAt(i); + isConcaveArray.RemoveAt(i); + vertexCount--; - // // Cut ear tip. - // triangles.add(indices[(vertexCount + i - 1) % vertexCount]); - // triangles.add(indices[i]); - // triangles.add(indices[(i + 1) % vertexCount]); - // indicesArray.removeIndex(i); - // isConcaveArray.removeIndex(i); - // vertexCount--; + int previousIndex = (vertexCount + i - 1) % vertexCount; + int nextIndex = i == vertexCount ? 0 : i; + isConcave[previousIndex] = IsConcave(previousIndex, vertexCount, vertices, indices); + isConcave[nextIndex] = IsConcave(nextIndex, vertexCount, vertices, indices); + } - // int previousIndex = (vertexCount + i - 1) % vertexCount; - // int nextIndex = i == vertexCount ? 0 : i; - // isConcave[previousIndex] = isConcave(previousIndex, vertexCount, vertices, indices); - // isConcave[nextIndex] = isConcave(nextIndex, vertexCount, vertices, indices); - // } + if (vertexCount == 3) { + triangles.Add(indices[2]); + triangles.Add(indices[0]); + triangles.Add(indices[1]); + } - // if (vertexCount == 3) { - // triangles.add(indices[2]); - // triangles.add(indices[0]); - // triangles.add(indices[1]); - // } + var convexPolygons = this.convexPolygons; + for (int i = 0, n = convexPolygons.Count; i < n; i++) { + polygonPool.Free(convexPolygons.Items[i]); + } + convexPolygons.Clear(); - // Array convexPolygons = this.convexPolygons; - // polygonPool.freeAll(convexPolygons); - // convexPolygons.clear(); + var convexPolygonsIndices = this.convexPolygonsIndices; + for (int i = 0, n = convexPolygonsIndices.Count; i < n; i++) { + polygonIndicesPool.Free(convexPolygonsIndices.Items[i]); + } + convexPolygonsIndices.Clear(); - // Array convexPolygonsIndices = this.convexPolygonsIndices; - // polygonIndicesPool.freeAll(convexPolygonsIndices); - // convexPolygonsIndices.clear(); + var polygonIndices = polygonIndicesPool.Obtain(); + polygonIndices.Clear(); - // ShortArray polygonIndices = polygonIndicesPool.obtain(); - // polygonIndices.clear(); + var polygon = polygonPool.Obtain(); + polygon.Clear(); - // FloatArray polygon = polygonPool.obtain(); - // polygon.clear(); + // Merge subsequent triangles if they form a triangle fan. + int fanBaseIndex = -1, lastWinding = 0; + short[] trianglesItems = triangles.Items; + for (int i = 0, n = triangles.Count; i < n; i += 3) { + int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1; + float x1 = vertices[t1], y1 = vertices[t1 + 1]; + float x2 = vertices[t2], y2 = vertices[t2 + 1]; + float x3 = vertices[t3], y3 = vertices[t3 + 1]; - // // Merge subsequent triangles if they form a triangle fan. - // int fanBaseIndex = -1, lastWinding = 0; - // short[] trianglesItems = triangles.items; - // for (int i = 0, n = triangles.size; i < n; i += 3) { - // int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1; - // float x1 = vertices[t1], y1 = vertices[t1 + 1]; - // float x2 = vertices[t2], y2 = vertices[t2 + 1]; - // float x3 = vertices[t3], y3 = vertices[t3 + 1]; + // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). + var merged = false; + if (fanBaseIndex == t1) { + int o = polygon.Count - 4; + float[] p = polygon.Items; + int winding1 = Winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3); + int winding2 = Winding(x3, y3, p[0], p[1], p[2], p[3]); + if (winding1 == lastWinding && winding2 == lastWinding) { + polygon.Add(x3); + polygon.Add(y3); + polygonIndices.Add((short)t3); + merged = true; + } + } - // // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). - // boolean merged = false; - // if (fanBaseIndex == t1) { - // int o = polygon.size - 4; - // float[] p = polygon.items; - // int winding1 = winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3); - // int winding2 = winding(x3, y3, p[0], p[1], p[2], p[3]); - // if (winding1 == lastWinding && winding2 == lastWinding) { - // polygon.add(x3); - // polygon.add(y3); - // polygonIndices.add(t3); - // merged = true; - // } - // } + // Otherwise make this triangle the new base. + if (!merged) { + if (polygon.Count > 0) { + convexPolygons.Add(polygon); + convexPolygonsIndices.Add(polygonIndices); + } + polygon = polygonPool.Obtain(); + polygon.Clear(); + polygon.Add(x1); + polygon.Add(y1); + polygon.Add(x2); + polygon.Add(y2); + polygon.Add(x3); + polygon.Add(y3); + polygonIndices = polygonIndicesPool.Obtain(); + polygonIndices.Clear(); + polygonIndices.Add((short)t1); + polygonIndices.Add((short)t2); + polygonIndices.Add((short)t3); + lastWinding = Winding(x1, y1, x2, y2, x3, y3); + fanBaseIndex = t1; + } + } - // // Otherwise make this triangle the new base. - // if (!merged) { - // if (polygon.size > 0) { - // convexPolygons.add(polygon); - // convexPolygonsIndices.add(polygonIndices); - // } - // polygon = polygonPool.obtain(); - // polygon.clear(); - // polygon.add(x1); - // polygon.add(y1); - // polygon.add(x2); - // polygon.add(y2); - // polygon.add(x3); - // polygon.add(y3); - // polygonIndices = polygonIndicesPool.obtain(); - // polygonIndices.clear(); - // polygonIndices.add(t1); - // polygonIndices.add(t2); - // polygonIndices.add(t3); - // lastWinding = winding(x1, y1, x2, y2, x3, y3); - // fanBaseIndex = t1; - // } - // } + if (polygon.Count > 0) { + convexPolygons.Add(polygon); + convexPolygonsIndices.Add(polygonIndices); + } - // if (polygon.size > 0) { - // convexPolygons.add(polygon); - // convexPolygonsIndices.add(polygonIndices); - // } + // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans. + for (int i = 0, n = convexPolygons.Count; i < n; i++) { + polygonIndices = convexPolygonsIndices.Items[i]; + if (polygonIndices.Count == 0) continue; + int firstIndex = polygonIndices.Items[0]; + int lastIndex = polygonIndices.Items[polygonIndices.Count - 1]; - // // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans. - // for (int i = 0, n = convexPolygons.size; i < n; i++) { - // polygonIndices = convexPolygonsIndices.get(i); - // if (polygonIndices.size == 0) continue; - // int firstIndex = polygonIndices.get(0); - // int lastIndex = polygonIndices.get(polygonIndices.size - 1); + polygon = convexPolygons.Items[i]; + int o = polygon.Count - 4; + float[] p = polygon.Items; + float prevPrevX = p[o], prevPrevY = p[o + 1]; + float prevX = p[o + 2], prevY = p[o + 3]; + float firstX = p[0], firstY = p[1]; + float secondX = p[2], secondY = p[3]; + int winding = Winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); - // polygon = convexPolygons.get(i); - // int o = polygon.size - 4; - // float[] p = polygon.items; - // float prevPrevX = p[o], prevPrevY = p[o + 1]; - // float prevX = p[o + 2], prevY = p[o + 3]; - // float firstX = p[0], firstY = p[1]; - // float secondX = p[2], secondY = p[3]; - // int winding = winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); + for (int ii = 0; ii < n; ii++) { + if (ii == i) continue; + var otherIndices = convexPolygonsIndices.Items[ii]; + if (otherIndices.Count != 3) continue; + int otherFirstIndex = otherIndices.Items[0]; + int otherSecondIndex = otherIndices.Items[1]; + int otherLastIndex = otherIndices.Items[2]; - // for (int ii = 0; ii < n; ii++) { - // if (ii == i) continue; - // ShortArray otherIndices = convexPolygonsIndices.get(ii); - // if (otherIndices.size != 3) continue; - // int otherFirstIndex = otherIndices.get(0); - // int otherSecondIndex = otherIndices.get(1); - // int otherLastIndex = otherIndices.get(2); + var otherPoly = convexPolygons.Items[ii]; + float x3 = otherPoly.Items[otherPoly.Count - 2], y3 = otherPoly.Items[otherPoly.Count - 1]; - // FloatArray otherPoly = convexPolygons.get(ii); - // float x3 = otherPoly.get(otherPoly.size - 2), y3 = otherPoly.get(otherPoly.size - 1); + if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue; + int winding1 = Winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); + int winding2 = Winding(x3, y3, firstX, firstY, secondX, secondY); + if (winding1 == winding && winding2 == winding) { + otherPoly.Clear(); + otherIndices.Clear(); + polygon.Add(x3); + polygon.Add(y3); + polygonIndices.Add((short)otherLastIndex); + prevPrevX = prevX; + prevPrevY = prevY; + prevX = x3; + prevY = y3; + ii = 0; + } + } + } - // if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue; - // int winding1 = winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); - // int winding2 = winding(x3, y3, firstX, firstY, secondX, secondY); - // if (winding1 == winding && winding2 == winding) { - // otherPoly.clear(); - // otherIndices.clear(); - // polygon.add(x3); - // polygon.add(y3); - // polygonIndices.add(otherLastIndex); - // prevPrevX = prevX; - // prevPrevY = prevY; - // prevX = x3; - // prevY = y3; - // ii = 0; - // } - // } - // } + // Remove empty polygons that resulted from the merge step above. + for (int i = convexPolygons.Count - 1; i >= 0; i--) { + polygon = convexPolygons.Items[i]; + if (polygon.Count == 0) { + convexPolygons.RemoveAt(i); + polygonPool.Free(polygon); + } + } - // // Remove empty polygons that resulted from the merge step above. - // for (int i = convexPolygons.size - 1; i >= 0; i--) { - // polygon = convexPolygons.get(i); - // if (polygon.size == 0) { - // convexPolygons.removeIndex(i); - // polygonPool.free(polygon); - // } - // } + return convexPolygons; + } - // return convexPolygons; - //} + static private bool IsConcave(int index, int vertexCount, float[] vertices, short[] indices) { + int previous = indices[(vertexCount + index - 1) % vertexCount] << 1; + int current = indices[index] << 1; + int next = indices[(index + 1) % vertexCount] << 1; + return !PositiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], + vertices[next + 1]); + } - //static private boolean isConcave(int index, int vertexCount, float[] vertices, short[] indices) { - // int previous = indices[(vertexCount + index - 1) % vertexCount] << 1; - // int current = indices[index] << 1; - // int next = indices[(index + 1) % vertexCount] << 1; - // return !positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], - // vertices[next + 1]); - //} + static private bool PositiveArea(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; + } - //static private boolean positiveArea(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { - // return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; - //} - - //static private int winding(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { - // float px = p2x - p1x, py = p2y - p1y; - // return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; - //} + static private int Winding(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + float px = p2x - p1x, py = p2y - p1y; + return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; + } } }