/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.tests; import java.util.ArrayList; import java.util.List; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Mesh; import com.badlogic.gdx.graphics.VertexAttribute; import com.badlogic.gdx.graphics.VertexAttributes; import com.badlogic.gdx.graphics.VertexAttributes.Usage; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.math.EarClippingTriangulator; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.tests.utils.GdxTest; import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.ShortArray; public class EarClippingTriangulatorTest { // public class EarClippingTriangulatorTest extends GdxTest { // // private List<TestCase> testCases = new ArrayList<TestCase>(); // private int casesX; // private int casesY; // // @Override // public void create () { // // An empty "polygon" // testCases.add(new TestCase(new float[] {}, true)); // // // A point // testCases.add(new TestCase(new float[] {0, 0}, true)); // // // A line segment // testCases.add(new TestCase(new float[] {0, 0, 1, 1}, true)); // // // A counterclockwise triangle // testCases.add(new TestCase(new float[] {0, 0, 0, 1, 1, 0,})); // // // A counterclockwise square // testCases.add(new TestCase(new float[] {0, 0, 0, 1, 1, 1, 1, 0,})); // // // A clockwise square // testCases.add(new TestCase(new float[] {0, 0, 1, 0, 1, 1, 0, 1,})); // // // Starfleet insigna // testCases.add(new TestCase(new float[] {0, 0, 0.6f, 0.4f, 1, 0, 0.5f, 1,})); // // // Starfleet insigna with repeated point // testCases.add(new TestCase(new float[] {0, 0, 0.6f, 0.4f, 0.6f, 0.4f, 1, 0, 0.5f, 1,})); // // // Three collinear points // testCases.add(new TestCase(new float[] {0, 0, 1, 0, 2, 0,})); // // // Four collinear points // testCases.add(new TestCase(new float[] {0, 0, 1, 0, 2, 0, 3, 0,})); // // // Non-consecutive collinear points // testCases.add(new TestCase(new float[] {0, 0, 1, 1, 2, 0, 3, 1, 4, 0,}, true)); // // // Plus shape // testCases.add(new TestCase(new float[] {1, 0, 2, 0, 2, 1, 3, 1, 3, 2, 2, 2, 2, 3, 1, 3, 1, 2, 0, 2, 0, 1, 1, 1,})); // // // Star shape // testCases.add(new TestCase(new float[] {4, 0, 5, 3, 8, 4, 5, 5, 4, 8, 3, 5, 0, 4, 3, 3,})); // // // U shape // testCases.add(new TestCase(new float[] {1, 0, 2, 0, 3, 1, 3, 3, 2, 3, 2, 1, 1, 1, 1, 3, 0, 3, 0, 1,})); // // // Spiral // testCases.add(new TestCase(new float[] {1, 0, 4, 0, 5, 1, 5, 4, 4, 5, 1, 5, 0, 4, 0, 3, 1, 2, 2, 2, 3, 3, 1, 3, 1, 4, 4, 4, // 4, 1, 0, 1,})); // // // Test case from http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml // testCases.add(new TestCase(new float[] {0, 6, 0, 0, 3, 0, 4, 1, 6, 1, 8, 0, 12, 0, 13, 2, 8, 2, 8, 4, 11, 4, 11, 6, 6, 6, // 4, 3, 2, 6,})); // // // Self-intersection // testCases.add(new TestCase(new float[] {0, 0, 1, 1, 2, -1, 3, 1, 4, 0,}, true)); // // // Self-touching // testCases.add(new TestCase(new float[] {0, 0, 4, 0, 4, 4, 2, 4, 2, 3, 3, 3, 3, 1, 1, 1, 1, 3, 2, 3, 2, 4, 0, 4,}, true)); // // // Self-overlapping // testCases.add(new TestCase(new float[] {0, 0, 4, 0, 4, 4, 1, 4, 1, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 3, 3, 4, 0, 4,}, true)); // // // Test case from http://www.davdata.nl/math/polygons.html // testCases.add(new TestCase(new float[] {190, 480, 140, 180, 310, 100, 330, 390, 290, 390, 280, 260, 220, 260, 220, 430, // 370, 430, 350, 30, 50, 30, 160, 560, 730, 510, 710, 20, 410, 30, 470, 440, 640, 410, 630, 140, 590, 140, 580, 360, 510, // 370, 510, 60, 650, 70, 660, 450, 190, 480,})); // // // Issue 815, http://code.google.com/p/libgdx/issues/detail?id=815 // testCases.add(new TestCase(new float[] {-2.0f, 0.0f, -2.0f, 0.5f, 0.0f, 1.0f, 0.5f, 2.875f, 1.0f, 0.5f, 1.5f, 1.0f, 2.0f, // 1.0f, 2.0f, 0.0f,})); // // // Issue 207, comment #1, http://code.google.com/p/libgdx/issues/detail?id=207#c1 // testCases.add(new TestCase(new float[] {72.42465f, 197.07095f, 78.485535f, 189.92776f, 86.12059f, 180.92929f, 99.68253f, // 164.94557f, 105.24325f, 165.79604f, 107.21862f, 166.09814f, 112.41958f, 162.78253f, 113.73238f, 161.94562f, 123.29477f, // 167.93805f, 126.70667f, 170.07617f, 73.22717f, 199.51062f,})); // // // Issue 207, comment #11, http://code.google.com/p/libgdx/issues/detail?id=207#c11 // // Also on issue 1081, http://code.google.com/p/libgdx/issues/detail?id=1081 // testCases.add(new TestCase(new float[] {2400.0f, 480.0f, 2400.0f, 176.0f, 1920.0f, 480.0f, 1920.0459f, 484.22314f, // 1920.1797f, 487.91016f, 1920.3955f, 491.0874f, 1920.6875f, 493.78125f, 1921.0498f, 496.01807f, 1921.4766f, 497.82422f, // 1921.9619f, 499.22607f, 1922.5f, 500.25f, 1923.085f, 500.92236f, 1923.7109f, 501.26953f, 1924.3721f, 501.31787f, // 1925.0625f, 501.09375f, 1925.7764f, 500.62354f, 1926.5078f, 499.9336f, 1927.251f, 499.0503f, 1928.0f, 498.0f, 1928.749f, // 496.80908f, 1929.4922f, 495.5039f, 1930.2236f, 494.11084f, 1930.9375f, 492.65625f, 1931.6279f, 491.1665f, 1932.2891f, // 489.66797f, 1932.915f, 488.187f, 1933.5f, 486.75f, 1934.0381f, 485.3833f, 1934.5234f, 484.11328f, 1934.9502f, 482.9663f, // 1935.3125f, 481.96875f, 1935.6045f, 481.14697f, 1935.8203f, 480.52734f, 1935.9541f, 480.13623f, 1936.0f, 480.0f,})); // // // Issue 1407, http://code.google.com/p/libgdx/issues/detail?id=1407 // testCases.add(new TestCase(new float[] {3.914329f, 1.9008259f, 4.414321f, 1.903619f, 4.8973203f, 1.9063174f, 5.4979978f, // 1.9096732f,}, true)); // // // Issue 1407, http://code.google.com/p/libgdx/issues/detail?id=1407, // // with an additional point to show what is happening. // testCases.add(new TestCase(new float[] {3.914329f, 1.9008259f, 4.414321f, 1.903619f, 4.8973203f, 1.9063174f, 5.4979978f, // 1.9096732f, 4, 4,})); // // casesX = (int)Math.ceil(Math.sqrt(testCases.size())); // casesY = (int)Math.ceil((float)testCases.size() / casesX); // // Gdx.input.setInputProcessor(new InputAdapter() { // @Override // public boolean keyDown (int keycode) { // switch (keycode) { // case Keys.RIGHT: // cycle(1); // break; // case Keys.LEFT: // cycle(-1); // break; // case Keys.SPACE: // reverse(); // break; // default: // return super.keyDown(keycode); // } // return true; // } // }); // } // // @Override // public void render () { // Gdx.gl.glClearColor(1, 1, 1, 1); // Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // // int w = Gdx.graphics.getWidth(); // int h = Gdx.graphics.getHeight(); // Gdx.gl20.glViewport(0, 0, w, h); // // final float M = 0.1f; // Gdx.gl10.glMatrixMode(GL10.GL_PROJECTION); // Gdx.gl10.glLoadIdentity(); // Gdx.gl10.glOrthof(-M, casesX * (1 + M), -M, casesY * (1 + M), -1, 1); // Gdx.gl10.glMatrixMode(GL10.GL_MODELVIEW); // Gdx.gl10.glLoadIdentity(); // // int x = 0; // int y = 0; // for (TestCase testCase : testCases) { // Gdx.gl10.glPushMatrix(); // Gdx.gl10.glTranslatef(x * (1 + M), y * (1 + M), 0); // testCase.render(); // Gdx.gl10.glPopMatrix(); // // x++; // if (x >= casesX) { // x = 0; // y++; // } // } // } // // @Override // public void dispose () { // for (TestCase testCase : testCases) { // testCase.dispose(); // } // } // // void cycle (int step) { // for (TestCase testCase : testCases) { // testCase.cycle(step); // } // } // // void reverse () { // for (TestCase testCase : testCases) { // testCase.reverse(); // } // } // // static final Color VALID_COLOR = new Color(0.8f, 1.0f, 0.8f, 1.0f); // static final Color INVALID_COLOR = new Color(1.0f, 0.8f, 0.8f, 1.0f); // // private class TestCase implements Disposable { // final FloatArray polygon; // final boolean invalid; // // final Mesh polygonMesh; // final Mesh interiorMesh; // final Mesh triangleOutlineMesh; // final Rectangle boundingRect; // // public TestCase (float[] p) { // this(p, false); // } // // public TestCase (float[] p, boolean invalid) { // this.invalid = invalid; // polygon = new FloatArray(p); // // int numPolygonVertices = polygon.size; // Vector2 min = new Vector2(Float.MAX_VALUE, Float.MAX_VALUE); // Vector2 max = new Vector2(-Float.MAX_VALUE, -Float.MAX_VALUE); // for (int i = 0; i < numPolygonVertices; i++) { // float x = polygon.get(i++); // float y = polygon.get(i); // min.x = Math.min(min.x, x); // min.y = Math.min(min.y, y); // max.x = Math.max(max.x, x); // max.y = Math.max(max.y, y); // } // boundingRect = new Rectangle(min.x, min.y, Math.max(0.001f, max.x - min.x), Math.max(0.001f, max.y - min.y)); // // int numTriangles = Math.max(0, polygon.size / 2 - 2); // VertexAttributes position = new VertexAttributes( // new VertexAttribute(Usage.Position, 2, ShaderProgram.POSITION_ATTRIBUTE)); // VertexAttributes positionAndColor = new VertexAttributes(new VertexAttribute(Usage.Position, 2, // ShaderProgram.POSITION_ATTRIBUTE), new VertexAttribute(Usage.Color, 4, ShaderProgram.COLOR_ATTRIBUTE)); // polygonMesh = new Mesh(true, polygon.size / 2, 0, position); // interiorMesh = new Mesh(true, 3 * numTriangles, 0, positionAndColor); // triangleOutlineMesh = new Mesh(true, 6 * numTriangles, 0, position); // // triangulate(); // } // // private void triangulate () { // ShortArray triangles = new EarClippingTriangulator().computeTriangles(polygon); // // FloatArray triangleOutlines = new FloatArray(triangles.size * 2); // for (int i = 0; i < triangles.size; i += 3) { // float ax = polygon.get(triangles.get(i) * 2); // float ay = polygon.get(triangles.get(i) * 2 + 1); // float bx = polygon.get(triangles.get(i + 1) * 2); // float by = polygon.get(triangles.get(i + 1) * 2 + 1); // float cx = polygon.get(triangles.get(i + 2) * 2); // float cy = polygon.get(triangles.get(i + 2) * 2 + 1); // triangleOutlines.add(ax); // triangleOutlines.add(ay); // triangleOutlines.add(bx); // triangleOutlines.add(by); // triangleOutlines.add(bx); // triangleOutlines.add(by); // triangleOutlines.add(cx); // triangleOutlines.add(cy); // triangleOutlines.add(cx); // triangleOutlines.add(cy); // triangleOutlines.add(ax); // triangleOutlines.add(ay); // } // // polygonMesh.setVertices(polygon.items, 0, polygon.size); // interiorMesh.setVertices(listToColoredVertexArray(polygon, triangles, getColor())); // triangleOutlineMesh.setVertices(triangleOutlines.items, 0, triangleOutlines.size); // } // // public void cycle (int step) { // if (polygon.size == 0) { // return; // } // while (step > 0) { // polygon.insert(0, polygon.pop()); // polygon.insert(0, polygon.pop()); // --step; // } // while (step < 0) { // polygon.add(polygon.removeIndex(0)); // ++step; // } // triangulate(); // } // // public void reverse () { // float[] vertices = polygon.items; // for (int i = 0, lastIndex = polygon.size - 2, n = polygon.size / 2; i < n; i++) { // int ii = lastIndex - i; // float temp = vertices[i]; // vertices[i] = vertices[ii]; // vertices[ii] = temp; // i++; // ii++; // temp = vertices[i]; // vertices[i] = vertices[ii]; // vertices[ii] = temp; // } // } // // private Color getColor () { // if (invalid) { // return INVALID_COLOR; // } else { // return VALID_COLOR; // } // } // // public void render () { // Gdx.gl10.glScalef(1 / boundingRect.width, 1 / boundingRect.height, 1); // Gdx.gl10.glTranslatef(-boundingRect.x, -boundingRect.y, 0); // // interiorMesh.render(GL10.GL_TRIANGLES); // // Gdx.gl10.glColor4f(0.4f, 0.4f, 0.4f, 1.0f); // Gdx.gl10.glLineWidth(1.0f); // triangleOutlineMesh.render(GL10.GL_LINES); // // Gdx.gl10.glColor4f(0.3f, 0.0f, 0.0f, 1.0f); // Gdx.gl10.glLineWidth(2.0f); // polygonMesh.render(GL10.GL_LINE_LOOP); // } // // @Override // public void dispose () { // polygonMesh.dispose(); // interiorMesh.dispose(); // triangleOutlineMesh.dispose(); // } // } // // static float[] listToColoredVertexArray (FloatArray vertices, ShortArray triangles, Color color) { // int n = triangles.size; // float[] array = new float[n * 6]; // int i = 0; // int j = 0; // for (int k = 0; k < n; k++, j++) { // float percent = n <= 3 ? 1 : (j / 3) / (float)(n / 3 - 1); // float brightness = 0.3f + 0.4f * percent; // array[i++] = vertices.get(triangles.get(k) * 2); // array[i++] = vertices.get(triangles.get(k) * 2 + 1); // array[i++] = color.r * brightness; // array[i++] = color.g * brightness; // array[i++] = color.b * brightness; // array[i++] = 1; // } // return array; // } }