Created
August 3, 2025 01:08
-
-
Save Tom-Ski/65a0d966a3f1696ccf93487c030cad14 to your computer and use it in GitHub Desktop.
Earcut comparison test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.badlogic.gdx.tests.lwjgl3; | |
| import com.badlogic.gdx.ApplicationAdapter; | |
| import com.badlogic.gdx.Gdx; | |
| import com.badlogic.gdx.Input; | |
| import com.badlogic.gdx.InputAdapter; | |
| import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; | |
| import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; | |
| import com.badlogic.gdx.graphics.Color; | |
| import com.badlogic.gdx.graphics.OrthographicCamera; | |
| import com.badlogic.gdx.graphics.glutils.ShapeRenderer; | |
| import com.badlogic.gdx.math.EarClippingTriangulator; | |
| import com.badlogic.gdx.math.MathUtils; | |
| import com.badlogic.gdx.math.Vector3; | |
| import com.badlogic.gdx.utils.IntArray; | |
| import com.badlogic.gdx.utils.Json; | |
| import com.badlogic.gdx.utils.ScreenUtils; | |
| import com.badlogic.gdx.utils.ShortArray; | |
| import com.badlogic.gdx.utils.viewport.ExtendViewport; | |
| import earcut4j.Earcut; | |
| import java.util.List; | |
| public class Test extends ApplicationAdapter { | |
| private float[] vertices; | |
| private double[] doubleVertices; | |
| private ShapeRenderer shapeRenderer; | |
| private float minX, minY, maxX, maxY; | |
| private ExtendViewport viewport; | |
| private CameraController cameraContoller; | |
| static class Point { | |
| float x; | |
| float y; | |
| Point () { | |
| } | |
| } | |
| static class Coordinates { | |
| Point[] coordinates; | |
| public Coordinates () { | |
| } | |
| } | |
| @Override | |
| public void create () { | |
| super.create(); | |
| shapeRenderer = new ShapeRenderer(); | |
| Json json = new Json(); | |
| Coordinates coordinates = json.fromJson(Coordinates.class, Gdx.files.internal("data/polys.json")); | |
| minX = Float.MAX_VALUE; | |
| minY = Float.MAX_VALUE; | |
| maxX = -Float.MAX_VALUE; | |
| maxY = -Float.MAX_VALUE; | |
| int length = coordinates.coordinates.length; | |
| length *= 2; | |
| vertices = new float[length]; | |
| doubleVertices = new double[length]; | |
| for (int i = 0; i < coordinates.coordinates.length; i++) { | |
| Point point = coordinates.coordinates[i]; | |
| vertices[i * 2] = point.x; | |
| vertices[i * 2 + 1] = point.y; | |
| doubleVertices[i * 2] = point.x; | |
| doubleVertices[i * 2 + 1] = point.y; | |
| if (point.x < minX) { | |
| minX = point.x; | |
| } | |
| if (point.y < minY) { | |
| minY = point.y; | |
| } | |
| if (point.x > maxX) { | |
| maxX = point.x; | |
| } | |
| if (point.y > maxY) { | |
| maxY = point.y; | |
| } | |
| } | |
| float worldWidth = maxX - minX; | |
| float worldHeight = maxY - minY; | |
| float bufferWidth = worldWidth * 0.1f; | |
| float bufferHeight = worldHeight * 0.1f; | |
| worldWidth += bufferWidth; | |
| worldHeight += bufferHeight; | |
| viewport = new ExtendViewport(worldWidth, worldHeight); | |
| viewport.getCamera().position.set(worldWidth / 2f - bufferWidth/2f, worldHeight / 2f - bufferHeight/2f, 0); | |
| viewport.getCamera().update(); | |
| cameraContoller = new CameraController((OrthographicCamera) viewport.getCamera()); | |
| Gdx.input.setInputProcessor(cameraContoller); | |
| } | |
| EarClippingTriangulator triangulator = new EarClippingTriangulator(); | |
| @Override | |
| public void render () { | |
| super.render(); | |
| long start = System.nanoTime(); | |
| ShortArray gdxTriangles = triangulator.computeTriangles(vertices); | |
| long end = System.nanoTime(); | |
| System.out.println("EarClippingTriangulator took " + (end - start) / 1000000f + " ms"); | |
| start = System.nanoTime(); | |
| List<Integer> earcutTriangles = Earcut.earcut(doubleVertices); | |
| end = System.nanoTime(); | |
| System.out.println("Earcut4j took " + (end - start) / 1000000f + " ms"); | |
| start = System.nanoTime(); | |
| IntArray gdxEarcutTriangles = GdxEarcut.earcut(vertices); | |
| end = System.nanoTime(); | |
| System.out.println("GdxEarcut4j took " + (end - start) / 1000000f + " ms"); | |
| ScreenUtils.clear(0, 0, 0, 1f); | |
| viewport.apply(); | |
| shapeRenderer.setProjectionMatrix(viewport.getCamera().combined); | |
| shapeRenderer.begin(ShapeRenderer.ShapeType.Line); | |
| boolean renderGdx = Gdx.input.isKeyPressed(Input.Keys.NUM_1); | |
| boolean renderEarcut = Gdx.input.isKeyPressed(Input.Keys.NUM_2); | |
| boolean renderGdxEarcut = Gdx.input.isKeyPressed(Input.Keys.NUM_3); | |
| if (renderGdx) { | |
| shapeRenderer.setColor(Color.RED); | |
| for (int i = 0; i < gdxTriangles.size; i += 3) { | |
| int tri1 = gdxTriangles.get(i); | |
| int tri2 = gdxTriangles.get(i + 1); | |
| int tri3 = gdxTriangles.get(i + 2); | |
| float triangleX1 = vertices[tri1 * 2]; | |
| float triangleY1 = vertices[tri1 * 2 + 1]; | |
| float triangleX2 = vertices[tri2 * 2]; | |
| float triangleY2 = vertices[tri2 * 2 + 1]; | |
| float triangleX3 = vertices[tri3 * 2]; | |
| float triangleY3 = vertices[tri3 * 2 + 1]; | |
| shapeRenderer.triangle(triangleX1, triangleY1, triangleX2, triangleY2, triangleX3, triangleY3); | |
| } | |
| } | |
| if (renderEarcut) { | |
| shapeRenderer.setColor(Color.GREEN); | |
| for (int i = 0; i < earcutTriangles.size(); i += 3) { | |
| int tri1 = gdxTriangles.get(i); | |
| int tri2 = gdxTriangles.get(i + 1); | |
| int tri3 = gdxTriangles.get(i + 2); | |
| float triangleX1 = vertices[tri1 * 2]; | |
| float triangleY1 = vertices[tri1 * 2 + 1]; | |
| float triangleX2 = vertices[tri2 * 2]; | |
| float triangleY2 = vertices[tri2 * 2 + 1]; | |
| float triangleX3 = vertices[tri3 * 2]; | |
| float triangleY3 = vertices[tri3 * 2 + 1]; | |
| shapeRenderer.triangle(triangleX1, triangleY1, triangleX2, triangleY2, triangleX3, triangleY3); | |
| } | |
| } | |
| if (renderGdxEarcut) { | |
| shapeRenderer.setColor(Color.BLUE); | |
| for (int i = 0; i < gdxEarcutTriangles.size; i += 3) { | |
| int tri1 = gdxTriangles.get(i); | |
| int tri2 = gdxTriangles.get(i + 1); | |
| int tri3 = gdxTriangles.get(i + 2); | |
| float triangleX1 = vertices[tri1 * 2]; | |
| float triangleY1 = vertices[tri1 * 2 + 1]; | |
| float triangleX2 = vertices[tri2 * 2]; | |
| float triangleY2 = vertices[tri2 * 2 + 1]; | |
| float triangleX3 = vertices[tri3 * 2]; | |
| float triangleY3 = vertices[tri3 * 2 + 1]; | |
| shapeRenderer.triangle(triangleX1, triangleY1, triangleX2, triangleY2, triangleX3, triangleY3); | |
| } | |
| } | |
| shapeRenderer.end(); | |
| } | |
| @Override | |
| public void resize (int width, int height) { | |
| viewport.update(width, height); | |
| } | |
| public static class CameraController extends InputAdapter { | |
| private final OrthographicCamera camera; | |
| private final Vector3 tmp = new Vector3(); | |
| private final Vector3 lastTouch = new Vector3(); | |
| public float minZoom = 0.0001f; | |
| public float maxZoom = 10000f; | |
| public CameraController (OrthographicCamera camera) { | |
| this.camera = camera; | |
| } | |
| @Override | |
| public boolean touchDown (int screenX, int screenY, int pointer, int button) { | |
| if (button == Input.Buttons.LEFT) { | |
| lastTouch.set(screenX, screenY, 0); | |
| } | |
| return true; | |
| } | |
| @Override | |
| public boolean touchDragged (int screenX, int screenY, int pointer) { | |
| tmp.set(screenX, screenY, 0); | |
| camera.unproject(tmp); | |
| Vector3 lastWorld = new Vector3(lastTouch); | |
| camera.unproject(lastWorld); | |
| camera.position.add(lastWorld.x - tmp.x, lastWorld.y - tmp.y, 0); | |
| lastTouch.set(screenX, screenY, 0); | |
| return true; | |
| } | |
| @Override | |
| public boolean scrolled (float amountX, float amountY) { | |
| float zoomFactor = 1.1f; | |
| if (amountY > 0) | |
| camera.zoom *= zoomFactor; | |
| else if (amountY < 0) | |
| camera.zoom /= zoomFactor; | |
| camera.zoom = MathUtils.clamp(camera.zoom, minZoom, maxZoom); | |
| return true; | |
| } | |
| } | |
| public static void main (String[] args) { | |
| new Lwjgl3Application(new Test(), new Lwjgl3ApplicationConfiguration()); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment