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 deleted file mode 100644 index d08e17e5d..000000000 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/ClippingTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -package com.esotericsoftware.spine; - -import com.badlogic.gdx.ApplicationAdapter; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.backends.lwjgl.LwjglApplication; -import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; -import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.graphics.OrthographicCamera; -import com.badlogic.gdx.graphics.g2d.BitmapFont; -import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; -import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.math.WindowedMean; -import com.esotericsoftware.spine.attachments.ClippingAttachment; - -public class ClippingTest extends ApplicationAdapter { - OrthographicCamera camera; - PolygonSpriteBatch batch; - SkeletonRenderer renderer; - SkeletonRendererDebug debugRenderer; - BitmapFont font; - - TextureAtlas atlas; - Skeleton skeleton; - AnimationState state; - - WindowedMean mean = new WindowedMean(30); - - public void create () { - camera = new OrthographicCamera(); - batch = new PolygonSpriteBatch(2048); - renderer = new SkeletonRenderer(); - renderer.setPremultipliedAlpha(true); - debugRenderer = new SkeletonRendererDebug(); - debugRenderer.setBoundingBoxes(false); - debugRenderer.setRegionAttachments(false); - font = new BitmapFont(); - - atlas = new TextureAtlas(Gdx.files.internal("raptor/raptor-pma.atlas")); - SkeletonJson json = new SkeletonJson(atlas); - json.setScale(0.6f); - SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("raptor/raptor.json")); - - skeleton = new Skeleton(skeletonData); - skeleton.setPosition(250, 20); - - AnimationStateData stateData = new AnimationStateData(skeletonData); -// stateData.setMix("run", "jump", 0.2f); -// stateData.setMix("jump", "run", 0.2f); - - state = new AnimationState(stateData); - state.setTimeScale(0.5f); - - state.setAnimation(0, "walk", true); - state.addAnimation(0, "Jump", false, 2); - state.addAnimation(0, "walk", true, 0); - - // Create a clipping attachment, slot data, and slot. - ClippingAttachment clip = new ClippingAttachment("clip"); - - // Spiral. - clip.setVertices(new float[] {430.90802f, 278.212f, 72.164f, 361.816f, 31.143997f, 128.804f, 191.896f, 61.0f, 291.312f, - 175.73201f, 143.956f, 207.408f, 161.4f, 145.628f, 227.456f, 160.61601f, 224.392f, 126.535995f, 188.264f, 113.144f, - 147.13199f, 108.87601f, 77.035995f, 158.212f, 86.15199f, 220.676f, 102.77199f, 240.716f, 174.74399f, 243.20801f, - 250.572f, 216.74802f, 324.772f, 200.33202f, 309.388f, 124.968f, 258.168f, 60.503998f, 199.696f, 42.872f, 116.951996f, - 6.7400017f, 11.332001f, 72.48f, -6.708008f, 143.136f, 1.0679932f, 239.92801f, 26.5f, 355.6f, -47.380005f, 377.52798f, - -40.608f, 303.1f, -53.584015f, 77.316f, 5.4600067f, 8.728001f, 113.343994f, -56.04f, 192.42801f, -45.112f, 274.564f, - -38.784f, 322.592f, -10.604f, 371.98f, 21.920002f, 405.16f, 60.896004f, 428.68f, 104.852005f, 406.996f, 188.976f, - 364.58398f, 220.14401f, 309.3f, 238.788f, 263.232f, 244.75201f, 219.468f, 271.58002f, 210.824f, 294.176f, 250.664f, - 295.2f, 295.972f, 276.02f, 357.46f, 269.172f, 420.008f, 242.37201f, 466.63602f, 207.648f, 437.516f, -10.579998f, - 378.05603f, -64.624f, 465.24f, -104.992f, 554.11206f, 95.43199f, 514.89197f, 259.02f}); - - // Polygon: -// clip.setVertices( -// new float[] { 94.0f, 84.0f, 45.0f, 165.0f, 218.0f, 292.0f, 476.0f, 227.0f, 480.0f, 125.0f, 325.0f, 191.0f, 333.0f, 77.0f, -// 302.0f, 30.0f, 175.0f, 140.0f }); - - // Rectangle: -// new float[] { // -// -140, 50, // -// 250, 50, // -// 250, 350, // -// -140, 350, // -// }); - - // Self intersection: -// clip.setVertices(new float[] { // -// -140, -50, // -// 120, 50, // -// 120, -50, // -// -140, 50, // -// }); - - for (int j = 0; j < clip.getVertices().length; j += 2) { - clip.getVertices()[j] = (clip.getVertices()[j] - 150f); - clip.getVertices()[j + 1] = (clip.getVertices()[j + 1] + 100); - } - clip.setWorldVerticesLength(clip.getVertices().length); - clip.setEndSlot(skeleton.findSlot("front_hand").data.index); - - SlotData clipSlotData = new SlotData(skeletonData.getSlots().size, "clip slot", skeletonData.getBones().first()); - skeletonData.getSlots().add(clipSlotData); - - Slot clipSlot = new Slot(clipSlotData, skeleton.getRootBone()); - clipSlot.setAttachment(clip); - skeleton.getSlots().add(clipSlot); - skeleton.getDrawOrder().insert(skeletonData.findSlot("back_hand").getIndex(), clipSlot); - } - - public void render () { - state.update(Gdx.graphics.getDeltaTime() * 0.3f); - state.update(0); - - Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1); - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - - state.apply(skeleton); - skeleton.updateWorldTransform(); - - camera.update(); - batch.getProjectionMatrix().set(camera.combined); - debugRenderer.getShapeRenderer().setProjectionMatrix(camera.combined); - - batch.begin(); - long start = System.nanoTime(); - renderer.draw(batch, skeleton); - mean.addValue((System.nanoTime() - start) / 1000000.0f); - renderer.setPremultipliedAlpha(false); - font.draw(batch, "Time: " + mean.getMean() + "ms", 10, Gdx.graphics.getHeight() - font.getLineHeight()); - batch.end(); - - debugRenderer.draw(skeleton); - } - - public void resize (int width, int height) { - camera.setToOrtho(false); - } - - public void dispose () { - atlas.dispose(); - } - - public static void main (String[] args) throws Exception { - LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); - config.width = 800; - config.height = 600; - new LwjglApplication(new ClippingTest(), config); - } -} diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/ConvexDecomposerTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/ConvexDecomposerTest.java deleted file mode 100644 index 92954f55e..000000000 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/ConvexDecomposerTest.java +++ /dev/null @@ -1,274 +0,0 @@ - -package com.esotericsoftware.spine; - -import com.badlogic.gdx.ApplicationAdapter; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Input.Buttons; -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.Color; -import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.graphics.OrthographicCamera; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.BitmapFont; -import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; -import com.badlogic.gdx.math.Intersector; -import com.badlogic.gdx.math.MathUtils; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.FloatArray; -import com.esotericsoftware.spine.utils.Clipper; -import com.esotericsoftware.spine.utils.ConvexDecomposer; - -public class ConvexDecomposerTest extends ApplicationAdapter { - OrthographicCamera sceneCamera; - ShapeRenderer shapes; - PolygonSpriteBatch polyBatcher; - Texture image; - ConvexDecomposer decomposer = new ConvexDecomposer(); - FloatArray polygon = new FloatArray(); - Array convexPolygons = new Array(); - boolean isCreatingPolygon = false; - Vector3 tmp = new Vector3(); - Array colors = new Array(); - BitmapFont font; - - @Override - public void create () { - sceneCamera = new OrthographicCamera(); - shapes = new ShapeRenderer(); - polyBatcher = new PolygonSpriteBatch(); - image = new Texture("skin/skin.png"); - font = new BitmapFont(); - - float[] v = new float[] {430.90802f, 278.212f, 72.164f, 361.816f, 31.143997f, 128.804f, 191.896f, 61.0f, 291.312f, - 175.73201f, 143.956f, 207.408f, 161.4f, 145.628f, 227.456f, 160.61601f, 224.392f, 126.535995f, 188.264f, 113.144f, - 147.13199f, 108.87601f, 77.035995f, 158.212f, 86.15199f, 220.676f, 102.77199f, 240.716f, 174.74399f, 243.20801f, - 250.572f, 216.74802f, 324.772f, 200.33202f, 309.388f, 124.968f, 258.168f, 60.503998f, 199.696f, 42.872f, 116.951996f, - 6.7400017f, 11.332001f, 72.48f, -6.708008f, 143.136f, 1.0679932f, 239.92801f, 26.5f, 355.6f, -47.380005f, 377.52798f, - -40.608f, 303.1f, -53.584015f, 77.316f, 5.4600067f, 8.728001f, 113.343994f, -56.04f, 192.42801f, -45.112f, 274.564f, - -38.784f, 322.592f, -10.604f, 371.98f, 21.920002f, 405.16f, 60.896004f, 428.68f, 104.852005f, 406.996f, 188.976f, - 364.58398f, 220.14401f, 309.3f, 238.788f, 263.232f, 244.75201f, 219.468f, 271.58002f, 210.824f, 294.176f, 250.664f, - 295.2f, 295.972f, 276.02f, 357.46f, 269.172f, 420.008f, 242.37201f, 466.63602f, 207.648f, 437.516f, -10.579998f, - 378.05603f, -64.624f, 465.24f, -104.992f, 554.11206f, 95.43199f, 514.89197f, 259.02f}; - for (int i = 0, n = v.length; i < n; i++) - v[i] += 200; - - // float[] v = new float[] { 94.0f, 84.0f, 45.0f, 165.0f, 218.0f, 292.0f, 476.0f, 227.0f, 480.0f, 125.0f, 325.0f, 191.0f, - // 333.0f, 77.0f, 302.0f, 30.0f, 175.0f, 140.0f }; -// float[] v = {87, 288, 217, 371, 456, 361, 539, 175, 304, 194, 392, 290, 193, 214, 123, 15, 14, 137}; -// float[] v = { 336, 153, 207, 184, 364, 333, 529, 326, 584, 130, 438, 224 }; - polygon.addAll(v); - triangulate(); - } - - @Override - public void resize (int width, int height) { - sceneCamera.setToOrtho(false); - } - - @Override - public void render () { - Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1); - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - - processInput(); - renderScene(); - } - - private void processInput () { - tmp.set(Gdx.input.getX(), Gdx.input.getY(), 0); - sceneCamera.unproject(tmp); - - if (Gdx.input.justTouched()) { - if (!isCreatingPolygon) { - polygon.clear(); - convexPolygons = null; - isCreatingPolygon = true; - } - - polygon.add((int)tmp.x); - polygon.add((int)tmp.y); - - if (Gdx.input.isButtonPressed(Buttons.RIGHT)) { - isCreatingPolygon = false; - System.out.print("float[] v = { "); - for (int i = 0; i < polygon.size; i++) { - System.out.print(polygon.get(i)); - if (i != polygon.size - 1) System.out.print(", "); - } - System.out.println("};"); - triangulate(); - } - } - - if (Gdx.input.isKeyJustPressed(Keys.R)) { - long start = System.nanoTime(); - generateRandomPolygon(); - System.out.println("Took: " + (System.nanoTime() - start) / 1000000000.0f + " secs"); - System.out.print("float[] v = { "); - for (int i = 0; i < polygon.size; i++) { - System.out.print(polygon.get(i)); - if (i != polygon.size - 1) System.out.print(", "); - } - System.out.println("};"); - triangulate(); - } - - if (Gdx.input.isKeyJustPressed(Keys.T)) { - triangulate(); - } - } - - private void generateRandomPolygon () { - polygon.clear(); - convexPolygons.clear(); - - int numVertices = MathUtils.random(3, 30); - for (int i = 0; i < numVertices; i++) { - float x = (float)(50 + Math.random() * (Gdx.graphics.getWidth() - 50)); - float y = (float)(50 + Math.random() * (Gdx.graphics.getHeight() - 50)); - - polygon.add(x); - polygon.add(y); - System.out.println(polygon.toString(",")); - if (selfIntersects(polygon)) { - polygon.size -= 2; - i--; - } - } - } - - private boolean selfIntersects (FloatArray polygon) { - Vector2 tmp = new Vector2(); - if (polygon.size == 6) return false; - for (int i = 0, n = polygon.size; i <= n; i += 2) { - float x1 = polygon.get(i % n); - float y1 = polygon.get((i + 1) % n); - float x2 = polygon.get((i + 2) % n); - float y2 = polygon.get((i + 3) % n); - - for (int j = 0; j <= n; j += 2) { - float x3 = polygon.get(j % n); - float y3 = polygon.get((j + 1) % n); - float x4 = polygon.get((j + 2) % n); - float y4 = polygon.get((j + 3) % n); - if (x1 == x3 && y1 == y3) continue; - if (x1 == x4 && y1 == y4) continue; - if (x2 == x3 && y2 == y3) continue; - if (x2 == x4 && y2 == y4) continue; - if (Intersector.intersectSegments(x1, y1, x2, y2, x3, y3, x4, y4, tmp)) return true; - } - } - return false; - } - - private void renderScene () { - sceneCamera.update(); - shapes.setProjectionMatrix(sceneCamera.combined); - polyBatcher.setProjectionMatrix(sceneCamera.combined); - - polyBatcher.begin(); - polyBatcher.disableBlending(); - - polyBatcher.end(); - - // polygon - shapes.setColor(Color.RED); - shapes.begin(ShapeType.Line); - if (isCreatingPolygon) { - tmp.set(Gdx.input.getX(), Gdx.input.getY(), 0); - sceneCamera.unproject(tmp); - polygon.add(tmp.x); - polygon.add(tmp.y); - } - - // polygon while drawing -// switch (polygon.size) { -// case 0: -// break; -// case 2: -// shapes.end(); -// shapes.begin(ShapeType.Point); -// GL11.glPointSize(4); -// shapes.point(polygon.get(0), polygon.get(1), 0); -// shapes.end(); -// shapes.begin(ShapeType.Line); -// break; -// case 4: -// shapes.line(polygon.get(0), polygon.get(1), polygon.get(2), polygon.get(3)); -// break; -// default: -// shapes.polygon(polygon.items, 0, polygon.size); -// } - - // edge normals -// shapes.setColor(Color.YELLOW); -// if (polygon.size > 2) { -// boolean clockwise = Clipper.isClockwise(polygon); -// for (int i = 0; i < polygon.size; i += 2) { -// float x = polygon.get(i); -// float y = polygon.get(i + 1); -// float x2 = polygon.get((i + 2) % polygon.size); -// float y2 = polygon.get((i + 3) % polygon.size); -// -// float mx = x + (x2 - x) / 2; -// float my = y + (y2 - y) / 2; -// float nx = (y2 - y); -// float ny = -(x2 - x); -// if (!clockwise) { -// nx = -nx; -// ny = -ny; -// } -// float l = 1 / (float)Math.sqrt(nx * nx + ny * ny); -// nx *= l * 20; -// ny *= l * 20; -// -// shapes.line(mx, my, mx + nx, my + ny); -// } -// } - - // decomposition - if (convexPolygons != null) { - for (int i = 0, n = convexPolygons.size; i < n; i++) { - if (colors.size <= i) { - colors.add(new Color(MathUtils.random(), MathUtils.random(), MathUtils.random(), 1)); - } - shapes.setColor(colors.get(i)); - shapes.polygon(convexPolygons.get(i).items, 0, convexPolygons.get(i).size); - if (i == 29) break; - } - } - - if (isCreatingPolygon) { - polygon.setSize(polygon.size - 2); - } - shapes.end(); - - polyBatcher.begin(); - polyBatcher.enableBlending(); - for (int i = 0; i < polygon.size; i += 2) { - float x = polygon.get(i); - float y = polygon.get(i + 1); - font.draw(polyBatcher, "" + (i >> 1), x, y); // + ", " + x + ", " + y, x, y); - } - font.draw(polyBatcher, Gdx.input.getX() + ", " + (Gdx.graphics.getHeight() - Gdx.input.getY()), 0, 20); - polyBatcher.end(); - } - - private void triangulate () { - Clipper.makeClockwise(polygon); - convexPolygons = decomposer.decompose(polygon); - } - - public static void main (String[] args) { - LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); - config.width = 800; - config.height = 600; - new LwjglApplication(new ConvexDecomposerTest(), config); - } -} diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SoftwareClippingTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SoftwareClippingTest.java deleted file mode 100644 index 8eb5fa5be..000000000 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SoftwareClippingTest.java +++ /dev/null @@ -1,265 +0,0 @@ - -package com.esotericsoftware.spine; - -import org.lwjgl.opengl.GL11; - -import com.badlogic.gdx.ApplicationAdapter; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Input.Buttons; -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.Color; -import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.graphics.OrthographicCamera; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; -import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.FloatArray; -import com.badlogic.gdx.utils.ShortArray; -import com.esotericsoftware.spine.utils.Clipper; -import com.esotericsoftware.spine.utils.ConvexDecomposer; - -public class SoftwareClippingTest extends ApplicationAdapter { - OrthographicCamera sceneCamera; - ShapeRenderer shapes; - PolygonSpriteBatch polyBatcher; - Texture image; - - float[] triangleOutline = {100, 100, 300, 100, 200, 300}; - float[] triangle = {100, 100, Color.WHITE.toFloatBits(), 0, 1, 300, 100, Color.WHITE.toFloatBits(), 1, 1, 200, 300, - Color.WHITE.toFloatBits(), 0.5f, 0}; - short[] triangleIndices = {0, 1, 2}; - FloatArray clippingPolygon = new FloatArray(); - FloatArray clippedPolygon = new FloatArray(); - - FloatArray clippedPolygonVertices = new FloatArray(); - ShortArray clippedPolygonIndices = new ShortArray(); - - boolean isCreatingClippingArea = false; - Vector3 tmp = new Vector3(); - Clipper clipper; - ConvexDecomposer decomposer; - - @Override - public void create () { - sceneCamera = new OrthographicCamera(); - shapes = new ShapeRenderer(); - polyBatcher = new PolygonSpriteBatch(); - clipper = new Clipper(); - decomposer = new ConvexDecomposer(); - image = new Texture("skin/skin.png"); - - float[] v = new float[] {430.90802f, 278.212f, 72.164f, 361.816f, 31.143997f, 128.804f, 191.896f, 61.0f, 291.312f, - 175.73201f, 143.956f, 207.408f, 161.4f, 145.628f, 227.456f, 160.61601f, 224.392f, 126.535995f, 188.264f, 113.144f, - 147.13199f, 108.87601f, 77.035995f, 158.212f, 86.15199f, 220.676f, 102.77199f, 240.716f, 174.74399f, 243.20801f, - 250.572f, 216.74802f, 324.772f, 200.33202f, 309.388f, 124.968f, 258.168f, 60.503998f, 199.696f, 42.872f, 116.951996f, - 6.7400017f, 11.332001f, 72.48f, -6.708008f, 143.136f, 1.0679932f, 239.92801f, 26.5f, 355.6f, -47.380005f, 377.52798f, - -40.608f, 303.1f, -53.584015f, 77.316f, 5.4600067f, 8.728001f, 113.343994f, -56.04f, 192.42801f, -45.112f, 274.564f, - -38.784f, 322.592f, -10.604f, 371.98f, 21.920002f, 405.16f, 60.896004f, 428.68f, 104.852005f, 406.996f, 188.976f, - 364.58398f, 220.14401f, 309.3f, 238.788f, 263.232f, 244.75201f, 219.468f, 271.58002f, 210.824f, 294.176f, 250.664f, - 295.2f, 295.972f, 276.02f, 357.46f, 269.172f, 420.008f, 242.37201f, 466.63602f, 207.648f, 437.516f, -10.579998f, - 378.05603f, -64.624f, 465.24f, -104.992f, 554.11206f, 95.43199f, 514.89197f, 259.02f}; - for (int i = 0, n = v.length; i < n; i++) - v[i] = v[i] * 0.5f + 70; - clippingPolygon.addAll(v); - clip(); - } - - @Override - public void resize (int width, int height) { - sceneCamera.setToOrtho(false); - } - - @Override - public void render () { - Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1); - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - - processInput(); - renderScene(); - } - - private void processInput () { - tmp.set(Gdx.input.getX(), Gdx.input.getY(), 0); - sceneCamera.unproject(tmp); - - if (Gdx.input.justTouched()) { - if (!isCreatingClippingArea) { - clippingPolygon.clear(); - isCreatingClippingArea = true; - } - - clippingPolygon.add((int)tmp.x); - clippingPolygon.add((int)tmp.y); - - if (Gdx.input.isButtonPressed(Buttons.RIGHT)) { - isCreatingClippingArea = false; - clip(); - } - } - - if (Gdx.input.isKeyJustPressed(Keys.T)) { - clip(); - } - } - - private void renderScene () { - sceneCamera.update(); - shapes.setProjectionMatrix(sceneCamera.combined); - polyBatcher.setProjectionMatrix(sceneCamera.combined); - - polyBatcher.begin(); - polyBatcher.disableBlending(); - - // clipped polygon - if (clippedPolygonVertices.size == 0) { - polyBatcher.draw(image, triangle, 0, 15, triangleIndices, 0, 3); - } else { - polyBatcher.draw(image, clippedPolygonVertices.items, 0, clippedPolygonVertices.size, clippedPolygonIndices.items, 0, - clippedPolygonIndices.size); - } - polyBatcher.end(); - - shapes.begin(ShapeType.Line); - - // triangle - shapes.setColor(Color.GREEN); - shapes.polygon(triangleOutline); - - // clipping area - shapes.setColor(Color.RED); - if (isCreatingClippingArea) { - tmp.set(Gdx.input.getX(), Gdx.input.getY(), 0); - sceneCamera.unproject(tmp); - clippingPolygon.add(tmp.x); - clippingPolygon.add(tmp.y); - } - - switch (clippingPolygon.size) { - case 0: - break; - case 2: - shapes.end(); - shapes.begin(ShapeType.Point); - GL11.glPointSize(4); - shapes.point(clippingPolygon.get(0), clippingPolygon.get(1), 0); - shapes.end(); - shapes.begin(ShapeType.Line); - break; - case 4: - shapes.line(clippingPolygon.get(0), clippingPolygon.get(1), clippingPolygon.get(2), clippingPolygon.get(3)); - break; - default: - shapes.polygon(clippingPolygon.items, 0, clippingPolygon.size); - } - -// // edge normals -// shapes.setColor(Color.YELLOW); -// if (clippingPolygon.size > 2) { -// boolean clockwise = Clipper.isClockwise(clippingPolygon); -// for (int i = 0; i < clippingPolygon.size; i += 2) { -// float x = clippingPolygon.get(i); -// float y = clippingPolygon.get(i + 1); -// float x2 = clippingPolygon.get((i + 2) % clippingPolygon.size); -// float y2 = clippingPolygon.get((i + 3) % clippingPolygon.size); -// -// float mx = x + (x2 - x) / 2; -// float my = y + (y2 - y) / 2; -// float nx = (y2 - y); -// float ny = -(x2 - x); -// if (!clockwise) { -// nx = -nx; -// ny = -ny; -// } -// float l = 1 / (float)Math.sqrt(nx * nx + ny * ny); -// nx *= l * 20; -// ny *= l * 20; -// -// shapes.line(mx, my, mx + nx, my + ny); -// } -// } - - if (isCreatingClippingArea) { - clippingPolygon.setSize(clippingPolygon.size - 2); - } - -// // clipped polygon -// shapes.setColor(Color.PINK); -// if (clippedPolygon.size > 0) { -// shapes.polygon(clippedPolygon.items, 0, clippedPolygon.size); -// } - - shapes.end(); - } - - private void clip () { - float x1 = triangle[0]; - float y1 = triangle[1]; - float x2 = triangle[5]; - float y2 = triangle[6]; - float x3 = triangle[10]; - float y3 = triangle[11]; - - Clipper.makeClockwise(clippingPolygon); - Array clippingPolygons = decomposer.decompose(clippingPolygon); - clippedPolygonVertices.clear(); - clippedPolygonIndices.clear(); - - for (FloatArray poly : clippingPolygons) { - Clipper.makeClockwise(poly); - poly.add(poly.get(0)); - poly.add(poly.get(1)); - - boolean clipped = clipper.clip(x1, y1, x2, y2, x3, y3, poly, clippedPolygon); - System.out.println("Clipped: " + clipped); - if (clipped) { - 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); - - // triangulate by creating a triangle fan, duplicate vertices - int o = clippedPolygonVertices.size / 5; - float color = Color.WHITE.toFloatBits(); - for (int i = 0; i < clippedPolygon.size; i += 2) { - float x = clippedPolygon.get(i); - float y = clippedPolygon.get(i + 1); - - float a = (d0 * (x - x3) + d1 * (y - y3)) * denom; - float b = (d4 * (x - x3) + d2 * (y - y3)) * denom; - float c = 1.0f - a - b; - - float u = triangle[3] * a + triangle[8] * b + triangle[13] * c; - float v = triangle[4] * a + triangle[9] * b + triangle[14] * c; - clippedPolygonVertices.add(x); - clippedPolygonVertices.add(y); - clippedPolygonVertices.add(color); - clippedPolygonVertices.add(u); - clippedPolygonVertices.add(v); - } - - for (int i = 1; i < (clippedPolygon.size >> 1) - 1; i++) { - clippedPolygonIndices.add(o); - clippedPolygonIndices.add(o + i); - clippedPolygonIndices.add(o + i + 1); - } - } else { - clippedPolygon.clear(); - } - - poly.setSize(poly.size - 2); - } - } - - public static void main (String[] args) { - LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); - new LwjglApplication(new SoftwareClippingTest(), config); - } -} 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 deleted file mode 100644 index 29b2a53a3..000000000 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/Clipper.java +++ /dev/null @@ -1,146 +0,0 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -package com.esotericsoftware.spine.utils; - -import com.badlogic.gdx.utils.FloatArray; - -public class Clipper { - 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. */ - public boolean clip (float x1, float y1, float x2, float y2, float x3, float y3, FloatArray clippingArea, FloatArray output) { - FloatArray originalOutput = output; - boolean clipped = false; - - // Avoid copy at the end. - FloatArray input = null; - if (clippingArea.size % 4 >= 2) { - input = output; - output = scratch; - } else - input = scratch; - - input.clear(); - input.add(x1); - input.add(y1); - input.add(x2); - input.add(y2); - input.add(x3); - input.add(y3); - input.add(x1); - input.add(y1); - output.clear(); - - float[] clippingVertices = clippingArea.items; - int clippingVerticesLast = clippingArea.size - 4; - for (int i = 0;; i += 2) { - float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; - float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; - float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; - - float[] inputVertices = input.items; - int inputVerticesLength = input.size - 2, outputStart = output.size; - 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]; - boolean side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; - if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { - if (side2) { // v1 inside, v2 inside - output.add(inputX2); - output.add(inputY2); - continue; - } - // v1 inside, v2 outside - float c0 = inputY2 - inputY, c2 = inputX2 - inputX; - float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); - output.add(edgeX + (edgeX2 - edgeX) * ua); - output.add(edgeY + (edgeY2 - edgeY) * ua); - } else if (side2) { // v1 outside, v2 inside - float c0 = inputY2 - inputY, c2 = inputX2 - inputX; - float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); - output.add(edgeX + (edgeX2 - edgeX) * ua); - output.add(edgeY + (edgeY2 - edgeY) * ua); - output.add(inputX2); - output.add(inputY2); - } - clipped = true; - } - - if (outputStart == output.size) { // All edges outside. - originalOutput.clear(); - return true; - } - - output.add(output.items[0]); - output.add(output.items[1]); - - if (i == clippingVerticesLast) break; - FloatArray temp = output; - output = input; - output.clear(); - input = temp; - } - - if (originalOutput != output) { - originalOutput.clear(); - originalOutput.addAll(output.items, 0, output.size - 2); - } else - originalOutput.setSize(originalOutput.size - 2); - - return clipped; - } - - static public void makeClockwise (FloatArray polygon) { - float[] vertices = polygon.items; - int verticeslength = polygon.size; - - float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], 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 < 0) return; - - for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { - float x = vertices[i], y = vertices[i + 1]; - int other = lastX - i; - vertices[i] = vertices[other]; - vertices[i + 1] = vertices[other + 1]; - vertices[other] = x; - vertices[other + 1] = y; - } - } -} 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 be742c139..949b1968a 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 @@ -36,7 +36,7 @@ import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.Pool; import com.badlogic.gdx.utils.ShortArray; -public class ConvexDecomposer { +class ConvexDecomposer { private final Array convexPolygons = new Array(); private final Array convexPolygonsIndices = new Array(); @@ -75,40 +75,60 @@ public class ConvexDecomposer { triangles.clear(); triangles.ensureCapacity(Math.max(0, vertexCount - 2) << 2); - // Triangulate. while (vertexCount > 3) { // Find ear tip. - int i = 0; + int previous = vertexCount - 1, i = 0, next = 1; while (true) { - if (!isConcave[i] && isEarTip(i, vertexCount, vertices, indices)) break; - i++; - if (i == vertexCount) { + 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 { - i--; if (!isConcave[i]) break; + i--; } while (i > 0); break; } + + previous = i; + i = next; + next = (next + 1) % vertexCount; } // Cut ear tip. - triangles.add(indices[previousIndex(i, vertexCount)]); + triangles.add(indices[(vertexCount + i - 1) % vertexCount]); triangles.add(indices[i]); - triangles.add(indices[nextIndex(i, vertexCount)]); + triangles.add(indices[(i + 1) % vertexCount]); indicesArray.removeIndex(i); isConcaveArray.removeIndex(i); vertexCount--; - int previousIndex = previousIndex(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); } if (vertexCount == 3) { - triangles.add(indicesArray.get(2)); - triangles.add(indicesArray.get(0)); - triangles.add(indicesArray.get(1)); + triangles.add(indices[2]); + triangles.add(indices[0]); + triangles.add(indices[1]); } Array convexPolygons = this.convexPolygons; @@ -127,18 +147,20 @@ public class ConvexDecomposer { // 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 = 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); + 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). boolean merged = false; 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)); + 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); @@ -185,10 +207,11 @@ public class ConvexDecomposer { polygon = convexPolygons.get(i); int o = polygon.size - 4; - float prevPrevX = polygon.get(o), prevPrevY = polygon.get(o + 1); - float prevX = polygon.get(o + 2), prevY = polygon.get(o + 3); - float firstX = polygon.get(0), firstY = polygon.get(1); - float secondX = polygon.get(2), secondY = polygon.get(3); + 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++) { @@ -200,8 +223,7 @@ public class ConvexDecomposer { int otherLastIndex = otherIndices.get(2); FloatArray otherPoly = convexPolygons.get(ii); - float x3 = otherPoly.get(otherPoly.size - 2); - float y3 = otherPoly.get(otherPoly.size - 1); + 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); @@ -233,47 +255,14 @@ public class ConvexDecomposer { return convexPolygons; } - private boolean isEarTip (int earTipIndex, int vertexCount, float[] vertices, short[] indices) { - int previousIndex = previousIndex(earTipIndex, vertexCount); - int nextIndex = nextIndex(earTipIndex, vertexCount); - int p1 = indices[previousIndex] << 1; - int p2 = indices[earTipIndex] << 1; - int p3 = indices[nextIndex] << 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]; - boolean[] isConcave = this.isConcaveArray.items; - - for (int i = nextIndex(nextIndex, vertexCount); i != previousIndex; i = nextIndex(i, vertexCount)) { - if (isConcave[i]) { - int v = indices[i] << 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)) return false; - } - } - } - } - return true; - } - static private boolean isConcave (int index, int vertexCount, float[] vertices, short[] indices) { - int previous = indices[previousIndex(index, vertexCount)] << 1; + int previous = indices[(vertexCount + index - 1) % vertexCount] << 1; int current = indices[index] << 1; - int next = indices[nextIndex(index, vertexCount)] << 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 int previousIndex (int index, int vertexCount) { - return (index == 0 ? vertexCount : index) - 1; - } - - static private int nextIndex (int index, int vertexCount) { - return (index + 1) % vertexCount; - } - 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; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java index d7a326843..0318e1067 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonClipping.java @@ -37,12 +37,12 @@ import com.esotericsoftware.spine.Slot; import com.esotericsoftware.spine.attachments.ClippingAttachment; 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 final FloatArray clipOutput = new FloatArray(128); + private final FloatArray clippedVertices = new FloatArray(128); + private final ShortArray clippedTriangles = new ShortArray(128); + private final FloatArray scratch = new FloatArray(); private ClippingAttachment clipAttachment; private Array clippingPolygons; @@ -54,10 +54,10 @@ public class SkeletonClipping { int n = clip.getWorldVerticesLength(); float[] vertices = clippingPolygon.setSize(n); clip.computeWorldVertices(slot, 0, n, vertices, 0, 2); - Clipper.makeClockwise(clippingPolygon); + makeClockwise(clippingPolygon); clippingPolygons = decomposer.decompose(clippingPolygon); for (FloatArray polygon : clippingPolygons) { - Clipper.makeClockwise(polygon); + makeClockwise(polygon); polygon.add(polygon.items[0]); polygon.add(polygon.items[1]); } @@ -79,7 +79,6 @@ public class SkeletonClipping { public void clipTriangles (float[] vertices, int verticesLength, short[] triangles, int trianglesLength, float[] uvs, float light, float dark, boolean twoColor) { - Clipper clipper = this.clipper; FloatArray clipOutput = this.clipOutput, clippedVertices = this.clippedVertices; ShortArray clippedTriangles = this.clippedTriangles; Object[] polygons = clippingPolygons.items; @@ -105,7 +104,7 @@ public class SkeletonClipping { for (int p = 0; p < polygonsCount; p++) { int s = clippedVertices.size; - if (clipper.clip(x1, y1, x2, y2, x3, y3, (FloatArray)polygons[p], clipOutput)) { + if (clip(x1, y1, x2, y2, x3, y3, (FloatArray)polygons[p], clipOutput)) { int clipOutputLength = clipOutput.size; if (clipOutputLength == 0) continue; float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; @@ -196,6 +195,90 @@ public class SkeletonClipping { } } + /** Clips the input triangle against the convex, clockwise clipping area. 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. */ + boolean clip (float x1, float y1, float x2, float y2, float x3, float y3, FloatArray clippingArea, FloatArray output) { + FloatArray originalOutput = output; + boolean clipped = false; + + // Avoid copy at the end. + FloatArray input = null; + if (clippingArea.size % 4 >= 2) { + input = output; + output = scratch; + } else + input = scratch; + + input.clear(); + input.add(x1); + input.add(y1); + input.add(x2); + input.add(y2); + input.add(x3); + input.add(y3); + input.add(x1); + input.add(y1); + output.clear(); + + float[] clippingVertices = clippingArea.items; + int clippingVerticesLast = clippingArea.size - 4; + for (int i = 0;; i += 2) { + float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; + float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; + float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; + + float[] inputVertices = input.items; + int inputVerticesLength = input.size - 2, outputStart = output.size; + 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]; + boolean side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; + if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { + if (side2) { // v1 inside, v2 inside + output.add(inputX2); + output.add(inputY2); + continue; + } + // v1 inside, v2 outside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + output.add(edgeX + (edgeX2 - edgeX) * ua); + output.add(edgeY + (edgeY2 - edgeY) * ua); + } else if (side2) { // v1 outside, v2 inside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + output.add(edgeX + (edgeX2 - edgeX) * ua); + output.add(edgeY + (edgeY2 - edgeY) * ua); + output.add(inputX2); + output.add(inputY2); + } + clipped = true; + } + + if (outputStart == output.size) { // All edges outside. + originalOutput.clear(); + return true; + } + + output.add(output.items[0]); + output.add(output.items[1]); + + if (i == clippingVerticesLast) break; + FloatArray temp = output; + output = input; + output.clear(); + input = temp; + } + + if (originalOutput != output) { + originalOutput.clear(); + originalOutput.addAll(output.items, 0, output.size - 2); + } else + originalOutput.setSize(originalOutput.size - 2); + + return clipped; + } + public FloatArray getClippedVertices () { return clippedVertices; } @@ -203,4 +286,28 @@ public class SkeletonClipping { public ShortArray getClippedTriangles () { return clippedTriangles; } + + static void makeClockwise (FloatArray polygon) { + float[] vertices = polygon.items; + int verticeslength = polygon.size; + + float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], 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 < 0) return; + + for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { + float x = vertices[i], y = vertices[i + 1]; + int other = lastX - i; + vertices[i] = vertices[other]; + vertices[i + 1] = vertices[other + 1]; + vertices[other] = x; + vertices[other + 1] = y; + } + } }