From 57535b68dddfaefc9908e3ebfc2d3e8288436758 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Fri, 31 Mar 2017 19:52:06 +0900 Subject: [PATCH] Applied Nate's beautifier and standard set of useless optimizations. --- .../esotericsoftware/spine/ClippingTest.java | 1 - .../spine/SkeletonClipping.java | 209 +++++++------- .../spine/SkeletonRenderer.java | 17 +- .../esotericsoftware/spine/utils/Clipper.java | 156 ++++------- .../spine/utils/ConvexDecomposer.java | 260 ++++++++---------- 5 files changed, 278 insertions(+), 365 deletions(-) diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/ClippingTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/ClippingTest.java index 36133d7e2..b55bc1a79 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/ClippingTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/ClippingTest.java @@ -32,7 +32,6 @@ package com.esotericsoftware.spine; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.backends.lwjgl.LwjglApplication; import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; import com.badlogic.gdx.graphics.GL20; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonClipping.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonClipping.java index a2ccfc8fa..20ecf76fb 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonClipping.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonClipping.java @@ -38,170 +38,155 @@ import com.esotericsoftware.spine.utils.Clipper; import com.esotericsoftware.spine.utils.ConvexDecomposer; public class SkeletonClipping { + private final Clipper clipper = new Clipper(); + private final ConvexDecomposer decomposer = new ConvexDecomposer(); + private final FloatArray clippingPolygon = new FloatArray(); + private final FloatArray clipOutput = new FloatArray(400); + private final FloatArray clippedVertices = new FloatArray(400); + private final ShortArray clippedTriangles = new ShortArray(400); + private ClippingAttachment clipAttachment; - private Clipper clipper = new Clipper(); - private ConvexDecomposer decomposer = new ConvexDecomposer(); - private FloatArray clippingPolygon = new FloatArray(400); private Array convexClippingPolygons; - private FloatArray clipOutput = new FloatArray(400); - private FloatArray clippedVertices = new FloatArray(400); - private ShortArray clippedTriangles = new ShortArray(400); public void clipStart (Slot slot, ClippingAttachment clip) { if (clipAttachment != null) return; clipAttachment = clip; int n = clip.getWorldVerticesLength(); - float[] vertices = this.clippingPolygon.setSize(n); + float[] vertices = clippingPolygon.setSize(n); clip.computeWorldVertices(slot, 0, n, vertices, 0, 2); convexClippingPolygons = decomposer.decompose(clippingPolygon); - for (FloatArray poly : convexClippingPolygons) { - Clipper.makeClockwise(poly); - poly.add(poly.items[0]); - poly.add(poly.items[1]); + for (FloatArray polygon : convexClippingPolygons) { + Clipper.makeClockwise(polygon); + polygon.add(polygon.items[0]); + polygon.add(polygon.items[1]); } } - public void clipEnd () { + public void clipEnd (int index) { + if (clipAttachment == null || clipAttachment.getEndSlot() != index) return; + clipAttachment = null; + convexClippingPolygons = null; clippedVertices.clear(); clippedTriangles.clear(); clippingPolygon.clear(); - convexClippingPolygons = null; - clipAttachment = null; } public boolean isClipping () { return clipAttachment != null; } - public ClippingAttachment getClippingAttachment () { - return clipAttachment; - } + public void clipTriangles (float[] vertices, int verticesLength, short[] triangles, int trianglesLength, float uvs[], + float dark, float light, boolean twoColor) { - public void clipTriangles (final float[] vertices, final int verticesLength, final short[] triangles, - final int trianglesLength, final float uvs[], final float dark, final float light, final boolean twoColor) { - short idx = 0; + Clipper clipper = this.clipper; + FloatArray clipOutput = this.clipOutput, clippedVertices = this.clippedVertices; + ShortArray clippedTriangles = this.clippedTriangles; + int vertexSize = twoColor ? 6 : 5; + + short index = 0; clippedVertices.clear(); clippedTriangles.clear(); for (FloatArray convexClippingPolygon : convexClippingPolygons) { for (int i = 0; i < trianglesLength; i += 3) { int vertexOffset = triangles[i] << 1; - float x1 = vertices[vertexOffset]; - float y1 = vertices[vertexOffset + 1]; - float u1 = uvs[vertexOffset]; - float v1 = uvs[vertexOffset + 1]; + float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; + float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; vertexOffset = triangles[i + 1] << 1; - float x2 = vertices[vertexOffset]; - float y2 = vertices[vertexOffset + 1]; - float u2 = uvs[vertexOffset]; - float v2 = uvs[vertexOffset + 1]; + float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; + float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; vertexOffset = triangles[i + 2] << 1; - float x3 = vertices[vertexOffset]; - float y3 = vertices[vertexOffset + 1]; - float u3 = uvs[vertexOffset]; - float v3 = uvs[vertexOffset + 1]; + float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; + float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; - boolean clipped = clipper.clip(x1, y1, x2, y2, x3, y3, convexClippingPolygon, clipOutput); - if (clipped) { + int s = clippedVertices.size; + if (clipper.clip(x1, y1, x2, y2, x3, y3, convexClippingPolygon, clipOutput)) { if (clipOutput.size == 0) continue; - float d0 = y2 - y3; - float d1 = x3 - x2; - float d2 = x1 - x3; - float d3 = y1 - y3; - float d4 = y3 - y1; - - float denom = 1 / (d0 * d2 + d1 * d3); - - float[] clipVertices = clipOutput.items; - int s = clippedVertices.size; - clippedVertices.setSize(s + (clipOutput.size >> 1) * (twoColor ? 6 : 5)); - final float[] clippedVerticesArray = clippedVertices.items; - - for (int j = 0, n = clipOutput.size; j < n; j += 2) { - float x = clipVertices[j]; - float y = clipVertices[j + 1]; - - float c0 = x - x3; - float c1 = y - y3; - float a = (d0 * c0 + d1 * c1) * denom; - float b = (d4 * c0 + d2 * c1) * denom; - float c = 1.0f - a - b; + float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; + float d = 1 / (d0 * d2 + d1 * (y1 - y3)); + float[] clipOutputItems = clipOutput.items; + float[] clippedVerticesItems = clippedVertices.setSize(s + (clipOutput.size >> 1) * vertexSize); + for (int ii = 0, nn = clipOutput.size; ii < nn; ii += 2) { + float x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; + float c0 = x - x3, c1 = y - y3; + float a = (d0 * c0 + d1 * c1) * d; + float b = (d4 * c0 + d2 * c1) * d; + float c = 1 - a - b; float u = u1 * a + u2 * b + u3 * c; float v = v1 * a + v2 * b + v3 * c; - clippedVerticesArray[s++] = x; - clippedVerticesArray[s++] = y; - clippedVerticesArray[s++] = light; - if (twoColor) clippedVerticesArray[s++] = dark; - clippedVerticesArray[s++] = u; - clippedVerticesArray[s++] = v; + clippedVerticesItems[s] = x; + clippedVerticesItems[s + 1] = y; + clippedVerticesItems[s + 2] = light; + if (twoColor) { + clippedVerticesItems[s + 3] = dark; + clippedVerticesItems[s + 4] = u; + clippedVerticesItems[s + 5] = v; + s += 6; + } else { + clippedVerticesItems[s + 3] = u; + clippedVerticesItems[s + 4] = v; + s += 5; + } } s = clippedTriangles.size; - clippedTriangles.setSize(s + 3 * ((clipOutput.size >> 1) - 2)); - final short[] clippedTrianglesArray = clippedTriangles.items; - - for (int j = 1, n = (clipOutput.size >> 1) - 1; j < n; j++) { - clippedTrianglesArray[s++] = idx; - clippedTrianglesArray[s++] = (short)(idx + j); - clippedTrianglesArray[s++] = (short)(idx + j + 1); + short[] clippedTrianglesItems = clippedTriangles.setSize(s + 3 * ((clipOutput.size >> 1) - 2)); + for (int ii = 1, nn = (clipOutput.size >> 1) - 1; ii < nn; ii++) { + clippedTrianglesItems[s] = index; + clippedTrianglesItems[s + 1] = (short)(index + ii); + clippedTrianglesItems[s + 2] = (short)(index + ii + 1); + s += 3; } + index += clipOutput.size >> 1; - idx += clipOutput.size >> 1; } else { - int s = clippedVertices.size; - clippedVertices.setSize(s + 3 * (twoColor ? 6 : 5)); - final float[] clippedVerticesArray = clippedVertices.items; - + float[] clippedVerticesItems = clippedVertices.setSize(s + 3 * vertexSize); + clippedVerticesItems[s] = x1; + clippedVerticesItems[s + 1] = y1; + clippedVerticesItems[s + 2] = light; if (!twoColor) { - clippedVerticesArray[s] = x1; - clippedVerticesArray[s + 1] = y1; - clippedVerticesArray[s + 2] = light; - clippedVerticesArray[s + 3] = u1; - clippedVerticesArray[s + 4] = v1; + clippedVerticesItems[s + 3] = u1; + clippedVerticesItems[s + 4] = v1; - clippedVerticesArray[s + 5] = x2; - clippedVerticesArray[s + 6] = y2; - clippedVerticesArray[s + 7] = light; - clippedVerticesArray[s + 8] = u2; - clippedVerticesArray[s + 9] = v2; + clippedVerticesItems[s + 5] = x2; + clippedVerticesItems[s + 6] = y2; + clippedVerticesItems[s + 7] = light; + clippedVerticesItems[s + 8] = u2; + clippedVerticesItems[s + 9] = v2; - clippedVerticesArray[s + 10] = x3; - clippedVerticesArray[s + 11] = y3; - clippedVerticesArray[s + 12] = light; - clippedVerticesArray[s + 13] = u3; - clippedVerticesArray[s + 14] = v3; + clippedVerticesItems[s + 10] = x3; + clippedVerticesItems[s + 11] = y3; + clippedVerticesItems[s + 12] = light; + clippedVerticesItems[s + 13] = u3; + clippedVerticesItems[s + 14] = v3; } else { - clippedVerticesArray[s] = x1; - clippedVerticesArray[s + 1] = y1; - clippedVerticesArray[s + 2] = light; - clippedVerticesArray[s + 3] = dark; - clippedVerticesArray[s + 4] = u1; - clippedVerticesArray[s + 5] = v1; + clippedVerticesItems[s + 3] = dark; + clippedVerticesItems[s + 4] = u1; + clippedVerticesItems[s + 5] = v1; - clippedVerticesArray[s + 6] = x2; - clippedVerticesArray[s + 7] = y2; - clippedVerticesArray[s + 8] = light; - clippedVerticesArray[s + 9] = dark; - clippedVerticesArray[s + 10] = u2; - clippedVerticesArray[s + 11] = v2; + clippedVerticesItems[s + 6] = x2; + clippedVerticesItems[s + 7] = y2; + clippedVerticesItems[s + 8] = light; + clippedVerticesItems[s + 9] = dark; + clippedVerticesItems[s + 10] = u2; + clippedVerticesItems[s + 11] = v2; - clippedVerticesArray[s + 12] = x3; - clippedVerticesArray[s + 13] = y3; - clippedVerticesArray[s + 14] = light; - clippedVerticesArray[s + 15] = dark; - clippedVerticesArray[s + 16] = u3; - clippedVerticesArray[s + 17] = v3; + clippedVerticesItems[s + 12] = x3; + clippedVerticesItems[s + 13] = y3; + clippedVerticesItems[s + 14] = light; + clippedVerticesItems[s + 15] = dark; + clippedVerticesItems[s + 16] = u3; + clippedVerticesItems[s + 17] = v3; } s = clippedTriangles.size; - clippedTriangles.setSize(s + 3); - final short[] clippedTrianglesArray = clippedTriangles.items; - clippedTrianglesArray[s] = idx++; - clippedTrianglesArray[s + 1] = idx++; - clippedTrianglesArray[s + 2] = idx++; + short[] clippedTrianglesItems = clippedTriangles.setSize(s + 3); + clippedTrianglesItems[s] = index++; + clippedTrianglesItems[s + 1] = index++; + clippedTrianglesItems[s + 2] = index++; } } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java index 26056dc95..c877434eb 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java @@ -85,8 +85,7 @@ public class SkeletonRenderer implements Disposable { batch.draw(region.getRegion().getTexture(), vertices, 0, 20); } else if (attachment instanceof ClippingAttachment) { - ClippingAttachment clip = (ClippingAttachment)attachment; - clipper.clipStart(slot, clip); + clipper.clipStart(slot, (ClippingAttachment)attachment); continue; } else if (attachment instanceof MeshAttachment) { @@ -119,9 +118,9 @@ public class SkeletonRenderer implements Disposable { } } - if (clipper.isClipping() && clipper.getClippingAttachment().getEndSlot() == i) clipper.clipEnd(); + clipper.clipEnd(i); } - if (clipper.isClipping()) clipper.clipEnd(); + clipper.clipEnd(-1); } @SuppressWarnings("null") @@ -221,9 +220,9 @@ public class SkeletonRenderer implements Disposable { } } - if (clipper.isClipping() && clipper.getClippingAttachment().getEndSlot() == i) clipper.clipEnd(); + clipper.clipEnd(i); } - if (clipper.isClipping()) clipper.clipEnd(); + clipper.clipEnd(-1); } @SuppressWarnings("null") @@ -330,9 +329,9 @@ public class SkeletonRenderer implements Disposable { } } - if (clipper.isClipping() && clipper.getClippingAttachment().getEndSlot() == i) clipper.clipEnd(); + clipper.clipEnd(i); } - if (clipper.isClipping()) clipper.clipEnd(); + clipper.clipEnd(-1); } public void setPremultipliedAlpha (boolean premultipliedAlpha) { @@ -342,4 +341,4 @@ public class SkeletonRenderer implements Disposable { public void dispose () { renderer.dispose(); } -} \ No newline at end of file +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Clipper.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Clipper.java index 4622cc853..7179985c2 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Clipper.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Clipper.java @@ -33,22 +33,22 @@ package com.esotericsoftware.spine.utils; import com.badlogic.gdx.utils.FloatArray; public class Clipper { - final FloatArray scratch = new FloatArray(); + private final FloatArray scratch = new FloatArray(); - /** Clips the input triangle against the convex clipping area, which needs to be clockwise. If the triangle lies entirely within the clipping area, false is - * returned. The clipping area must duplicate the first vertex at the end of the vertices list! */ + /** Clips the input triangle against the convex clipping area, which needs to be clockwise. If the triangle lies entirely + * within the clipping area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices + * list. */ public boolean clip (float x1, float y1, float x2, float y2, float x3, float y3, FloatArray clippingArea, FloatArray output) { - final FloatArray originalOutput = output; + FloatArray originalOutput = output; boolean clipped = false; + // Avoid copy at the end. FloatArray input = null; - // avoid copy at the end - if ((clippingArea.size / 2) % 2 != 0) { + if (clippingArea.size % 4 >= 2) { input = output; output = scratch; - } else { + } else input = scratch; - } input.clear(); input.add(x1); @@ -61,77 +61,51 @@ public class Clipper { input.add(y1); output.clear(); - final float[] clippingVertices = clippingArea.items; - final int clippingVerticesLength = clippingArea.size - 2; + float[] clippingVertices = clippingArea.items; + int clippingVerticesLength = clippingArea.size - 2; for (int i = 0; i < clippingVerticesLength; i += 2) { - float edgeX = clippingVertices[i]; - float edgeY = clippingVertices[i + 1]; - float edgeX2 = clippingVertices[i + 2]; - float edgeY2 = clippingVertices[i + 3]; + float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; + float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; + float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; - final float deltaX = edgeX - edgeX2; - final float deltaY = edgeY - edgeY2; - - final float[] inputVertices = input.items; - final int inputVerticesLength = input.size - 2; - - int numOutside = 0; - - for (int j = 0; j < inputVerticesLength; j += 2) { - final float inputX = inputVertices[j]; - final float inputY = inputVertices[j + 1]; - final float inputX2 = inputVertices[j + 2]; - final float inputY2 = inputVertices[j + 3]; - - final int side = deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0 ? 1 : -1; - final int side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0 ? 1 : -1; + float[] inputVertices = input.items; + int inputVerticesLength = input.size - 2; + int numOutside = 0; + for (int ii = 0; ii < inputVerticesLength; ii += 2) { + float inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; + float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; + int side = deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0 ? 1 : -1; + int side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0 ? 1 : -1; if (side >= 0) { - // v1 inside, v2 inside - if (side2 >= 0) { + if (side2 >= 0) { // v1 inside, v2 inside output.add(inputX2); output.add(inputY2); - } - // v1 inside, v2 outside - else { - float c0 = inputY2 - inputY; - float c1 = edgeX2 - edgeX; - float c2 = inputX2 - inputX; - float c3 = edgeY2 - edgeY; - float d = c0 * c1 - c2 * c3; - + } else { // v1 inside, v2 outside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float d = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY); float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / d; output.add(edgeX + (edgeX2 - edgeX) * ua); output.add(edgeY + (edgeY2 - edgeY) * ua); clipped = true; } } else { - // v1 outside, v2 outside - if (side2 < 0) { - // no output - clipped = true; + if (side2 < 0) // v1 outside, v2 outside: no output numOutside += 2; - } - // v1 outside, v2 inside - else { - float c0 = inputY2 - inputY; - float c1 = edgeX2 - edgeX; - float c2 = inputX2 - inputX; - float c3 = edgeY2 - edgeY; - float d = c0 * c1 - c2 * c3; - + else { // v1 outside, v2 inside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float d = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY); float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / d; output.add(edgeX + (edgeX2 - edgeX) * ua); output.add(edgeY + (edgeY2 - edgeY) * ua); - output.add(inputX2); output.add(inputY2); - clipped = true; } + clipped = true; } } - - // early out if all edges were outside + + // Early out if all edges were outside. if (numOutside == inputVerticesLength) { originalOutput.clear(); return true; @@ -141,57 +115,43 @@ public class Clipper { output.add(output.items[1]); if (i < clippingVerticesLength - 2) { - FloatArray tmp = output; + FloatArray temp = output; output = input; output.clear(); - input = tmp; + input = temp; } } if (originalOutput != output) { originalOutput.clear(); - originalOutput.addAll(output.items, 0, output.size); - } - - originalOutput.setSize(originalOutput.size - 2); + originalOutput.addAll(output.items, 0, output.size - 2); + } else + originalOutput.setSize(originalOutput.size - 2); return clipped; } - - public static void makeClockwise (FloatArray poly) { - if (isClockwise(poly)) return; - - int lastX = poly.size - 2; - final float[] polygon = poly.items; - for (int i = 0, n = poly.size / 2; i < n; i += 2) { + + static public void makeClockwise (FloatArray polygon) { + float[] vertices = polygon.items; + int verticeslength = polygon.size; + + float area = 0, p1x, p1y, p2x, p2y; + for (int i = 0, n = verticeslength - 3; i < n; i += 2) { + p1x = vertices[i]; + p1y = vertices[i + 1]; + p2x = vertices[i + 2]; + p2y = vertices[i + 3]; + area += p1x * p2y - p2x * p1y; + } + if (area + vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1] < 0) return; + + for (int i = 0, lastX = verticeslength - 2, n = verticeslength / 2; i < n; i += 2) { + float x = vertices[i], y = vertices[i + 1]; int other = lastX - i; - float x = polygon[i]; - float y = polygon[i + 1]; - polygon[i] = polygon[other]; - polygon[i + 1] = polygon[other + 1]; - polygon[other] = x; - polygon[other + 1] = y; + vertices[i] = vertices[other]; + vertices[i + 1] = vertices[other + 1]; + vertices[other] = x; + vertices[other + 1] = y; } } - - public static boolean isClockwise (FloatArray poly) { - return area(poly) < 0; - } - - public static float area (FloatArray poly) { - float area = 0; - - final float[] polyVertices = poly.items; - final int polySize = poly.size; - for (int i = 0; i < polySize; i += 2) { - float x = polyVertices[i]; - float y = polyVertices[i + 1]; - float x2 = polyVertices[(i + 2) % poly.size]; - float y2 = polyVertices[(i + 3) % poly.size]; - - area += x * y2 - y * x2; - } - - return area; - } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/ConvexDecomposer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/ConvexDecomposer.java index dc25c88dd..c254752b6 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/ConvexDecomposer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/ConvexDecomposer.java @@ -33,70 +33,61 @@ package com.esotericsoftware.spine.utils; import java.util.Iterator; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.BooleanArray; import com.badlogic.gdx.utils.FloatArray; -import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.Pool; import com.badlogic.gdx.utils.ShortArray; public class ConvexDecomposer { - static private final int CONCAVE = -1; - static private final int CONVEX = 1; - - private Pool polygonPool = new Pool() { - @Override - protected FloatArray newObject () { - return new FloatArray(16); - } - }; - - private Pool polygonIndicesPool = new Pool() { - @Override - protected ShortArray newObject () { - return new ShortArray(16); - } - }; - - private Array convexPolygons = new Array(); - private Array convexPolygonsIndices = new Array(); + private final Array convexPolygons = new Array(); + private final Array convexPolygonsIndices = new Array(); private final ShortArray indicesArray = new ShortArray(); private short[] indices; private float[] vertices; private int vertexCount; - private final IntArray vertexTypes = new IntArray(); + private final BooleanArray isConcaveArray = new BooleanArray(); private final ShortArray triangles = new ShortArray(); - public Array decompose (FloatArray polygon) { - this.vertices = polygon.items; - int vertexCount = this.vertexCount = polygon.size / 2; + private final Pool polygonPool = new Pool() { + protected FloatArray newObject () { + return new FloatArray(16); + } + }; + + private final Pool polygonIndicesPool = new Pool() { + protected ShortArray newObject () { + return new ShortArray(16); + } + }; + + public Array decompose (FloatArray input) { + vertices = input.items; + int vertexCount = this.vertexCount = input.size / 2; ShortArray indicesArray = this.indicesArray; indicesArray.clear(); - indicesArray.ensureCapacity(vertexCount); - indicesArray.size = vertexCount; - short[] indices = this.indices = indicesArray.items; + short[] indices = this.indices = indicesArray.setSize(vertexCount); for (short i = 0; i < vertexCount; i++) indices[i] = i; - IntArray vertexTypes = this.vertexTypes; - vertexTypes.clear(); - vertexTypes.ensureCapacity(vertexCount); + boolean[] isConcave = isConcaveArray.setSize(vertexCount); for (int i = 0, n = vertexCount; i < n; ++i) - vertexTypes.add(classifyVertex(i)); + isConcave[i] = isConcave(i); ShortArray triangles = this.triangles; triangles.clear(); triangles.ensureCapacity(Math.max(0, vertexCount - 2) * 4); - // Triangulate + // Triangulate. while (this.vertexCount > 3) { int earTipIndex = findEarTip(); cutEarTip(earTipIndex); int previousIndex = previousIndex(earTipIndex); int nextIndex = earTipIndex == vertexCount ? 0 : earTipIndex; - vertexTypes.set(previousIndex, classifyVertex(previousIndex)); - vertexTypes.set(nextIndex, classifyVertex(nextIndex)); + isConcave[previousIndex] = isConcave(previousIndex); + isConcave[nextIndex] = isConcave(nextIndex); } if (this.vertexCount == 3) { @@ -110,102 +101,91 @@ public class ConvexDecomposer { polygonIndicesPool.freeAll(convexPolygonsIndices); convexPolygonsIndices.clear(); - ShortArray polyIndices = polygonIndicesPool.obtain(); - polyIndices.clear(); - FloatArray poly = polygonPool.obtain(); - poly.clear(); - int fanBaseIndex = -1; - int lastWinding = 0; + ShortArray polygonIndices = polygonIndicesPool.obtain(); + polygonIndices.clear(); + FloatArray polygon = polygonPool.obtain(); + polygon.clear(); + int fanBaseIndex = -1, lastWinding = 0; - // Merge subsequent triangles if they form a triangle fan + // Merge subsequent triangles if they form a triangle fan. for (int i = 0, n = triangles.size; i < n; i += 3) { - int idx1 = triangles.get(i) << 1; - int idx2 = triangles.get(i + 1) << 1; - int idx3 = triangles.get(i + 2) << 1; + int t1 = triangles.get(i) << 1, t2 = triangles.get(i + 1) << 1, t3 = triangles.get(i + 2) << 1; + float x1 = input.get(t1), y1 = input.get(t1 + 1); + float x2 = input.get(t2), y2 = input.get(t2 + 1); + float x3 = input.get(t3), y3 = input.get(t3 + 1); - float x1 = polygon.get(idx1); - float y1 = polygon.get(idx1 + 1); - float x2 = polygon.get(idx2); - float y2 = polygon.get(idx2 + 1); - float x3 = polygon.get(idx3); - float y3 = polygon.get(idx3 + 1); - - // if the base of the last triangle - // is the same as this triangle's base - // check if they form a convex polygon (triangle fan) + // If the base of the last triangle is the same as this triangle's base, check if they form a convex polygon (triangle + // fan). boolean merged = false; - if (fanBaseIndex == idx1) { - int o = poly.size - 4; - int winding1 = winding(poly.get(o), poly.get(o + 1), poly.get(o + 2), poly.get(o + 3), x3, y3); - int winding2 = winding(x3, y3, poly.get(0), poly.get(1), poly.get(2), poly.get(3)); + if (fanBaseIndex == t1) { + int o = polygon.size - 4; + int winding1 = winding(polygon.get(o), polygon.get(o + 1), polygon.get(o + 2), polygon.get(o + 3), x3, y3); + int winding2 = winding(x3, y3, polygon.get(0), polygon.get(1), polygon.get(2), polygon.get(3)); if (winding1 == lastWinding && winding2 == lastWinding) { - poly.add(x3); - poly.add(y3); - polyIndices.add(idx3); + polygon.add(x3); + polygon.add(y3); + polygonIndices.add(t3); merged = true; } } - // otherwise make this triangle - // the new base + // Otherwise make this triangle the new base. if (!merged) { - if (poly.size > 0) { - convexPolygons.add(poly); - convexPolygonsIndices.add(polyIndices); + if (polygon.size > 0) { + convexPolygons.add(polygon); + convexPolygonsIndices.add(polygonIndices); } - poly = polygonPool.obtain(); - poly.clear(); - poly.add(x1); - poly.add(y1); - poly.add(x2); - poly.add(y2); - poly.add(x3); - poly.add(y3); - polyIndices = polygonIndicesPool.obtain(); - polyIndices.clear(); - polyIndices.add(idx1); - polyIndices.add(idx2); - polyIndices.add(idx3); + 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 = idx1; + fanBaseIndex = t1; } } - if (poly.size > 0) { - convexPolygons.add(poly); - convexPolygonsIndices.add(polyIndices); + 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 + // 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++) { - polyIndices = convexPolygonsIndices.get(i); - if (polyIndices.size == 0) continue; - int firstIndex = polyIndices.get(0); - int lastIndex = polyIndices.get(polyIndices.size - 1); + polygonIndices = convexPolygonsIndices.get(i); + if (polygonIndices.size == 0) continue; + int firstIndex = polygonIndices.get(0); + int lastIndex = polygonIndices.get(polygonIndices.size - 1); - poly = convexPolygons.get(i); - int o = poly.size - 4; - float prevPrevX = poly.get(o); - float prevPrevY = poly.get(o + 1); - float prevX = poly.get(o + 2); - float prevY = poly.get(o + 3); - float firstX = poly.get(0); - float firstY = poly.get(1); - float secondX = poly.get(2); - float secondY = poly.get(3); + polygon = convexPolygons.get(i); + int o = polygon.size - 4; + float prevPrevX = polygon.get(o); + float prevPrevY = polygon.get(o + 1); + float prevX = polygon.get(o + 2); + float prevY = polygon.get(o + 3); + float firstX = polygon.get(0); + float firstY = polygon.get(1); + float secondX = polygon.get(2); + float secondY = polygon.get(3); int winding = winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); - for (int j = 0; j < n; j++) { - if (j == i) continue; - ShortArray otherIndices = convexPolygonsIndices.get(j); + 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); - FloatArray otherPoly = convexPolygons.get(j); + FloatArray otherPoly = convexPolygons.get(ii); float x3 = otherPoly.get(otherPoly.size - 2); float y3 = otherPoly.get(otherPoly.size - 1); @@ -215,40 +195,38 @@ public class ConvexDecomposer { if (winding1 == winding && winding2 == winding) { otherPoly.clear(); otherIndices.clear(); - poly.add(x3); - poly.add(y3); - polyIndices.add(otherLastIndex); + polygon.add(x3); + polygon.add(y3); + polygonIndices.add(otherLastIndex); prevPrevX = prevX; prevPrevY = prevY; prevX = x3; prevY = y3; - j = 0; + ii = 0; } } } - // Remove empty polygons that resulted from the - // merge step above - Iterator polyIter = convexPolygons.iterator(); - while (polyIter.hasNext()) { - poly = polyIter.next(); - if (poly.size == 0) { - polyIter.remove(); - polygonPool.free(poly); + // Remove empty polygons that resulted from the merge step above. + for (Iterator iter = convexPolygons.iterator(); iter.hasNext();) { + polygon = iter.next(); + if (polygon.size == 0) { + iter.remove(); + polygonPool.free(polygon); } } return convexPolygons; } - private int classifyVertex (int index) { + private boolean isConcave (int index) { short[] indices = this.indices; int previous = indices[previousIndex(index)] * 2; int current = indices[index] * 2; int next = indices[nextIndex(index)] * 2; float[] vertices = this.vertices; - return computeSpannedAreaSign(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], - vertices[next], vertices[next + 1]); + return !positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], + vertices[next + 1]); } private int findEarTip () { @@ -256,15 +234,15 @@ public class ConvexDecomposer { for (int i = 0; i < vertexCount; i++) if (isEarTip(i)) return i; - int[] vertexTypes = this.vertexTypes.items; + boolean[] isConcave = this.isConcaveArray.items; for (int i = 0; i < vertexCount; i++) - if (vertexTypes[i] != CONCAVE) return i; + if (!isConcave[i]) return i; return 0; } private boolean isEarTip (int earTipIndex) { - int[] vertexTypes = this.vertexTypes.items; - if (vertexTypes[earTipIndex] == CONCAVE) return false; + boolean[] isConcave = this.isConcaveArray.items; + if (isConcave[earTipIndex]) return false; int previousIndex = previousIndex(earTipIndex); int nextIndex = nextIndex(earTipIndex); @@ -278,13 +256,12 @@ public class ConvexDecomposer { float p3x = vertices[p3], p3y = vertices[p3 + 1]; for (int i = nextIndex(nextIndex); i != previousIndex; i = nextIndex(i)) { - if (vertexTypes[i] != CONVEX) { + if (isConcave[i]) { int v = indices[i] * 2; - float vx = vertices[v]; - float vy = vertices[v + 1]; - if (computeSpannedAreaSign(p3x, p3y, p1x, p1y, vx, vy) >= 0) { - if (computeSpannedAreaSign(p1x, p1y, p2x, p2y, vx, vy) >= 0) { - if (computeSpannedAreaSign(p2x, p2y, p3x, p3y, vx, vy) >= 0) return false; + 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)) return false; } } } @@ -296,15 +273,12 @@ public class ConvexDecomposer { short[] indices = this.indices; ShortArray triangles = this.triangles; - short idx1 = indices[previousIndex(earTipIndex)]; - short idx2 = indices[earTipIndex]; - short idx3 = indices[nextIndex(earTipIndex)]; - triangles.add(idx1); - triangles.add(idx2); - triangles.add(idx3); + triangles.add(indices[previousIndex(earTipIndex)]); + triangles.add(indices[earTipIndex]); + triangles.add(indices[nextIndex(earTipIndex)]); indicesArray.removeIndex(earTipIndex); - vertexTypes.removeIndex(earTipIndex); + isConcaveArray.removeIndex(earTipIndex); vertexCount--; } @@ -316,16 +290,12 @@ public class ConvexDecomposer { return (index + 1) % vertexCount; } - static private int computeSpannedAreaSign (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { - float area = p1x * (p3y - p2y); - area += p2x * (p1y - p3y); - area += p3x * (p2y - p1y); - return (int)Math.signum(area); + 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; } - public static int winding (float v1x, float v1y, float v2x, float v2y, float v3x, float v3y) { - float vx = v2x - v1x; - float vy = v2y - v1y; - return v3x * vy - v3y * vx + vx * v1y - v1x * vy >= 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; } }