question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

EarClippingTriangulator produces "inside-out" triangulation result

See original GitHub issue

Issue details

Using the test case below, it appears the the EarClippingTriangulator is returning a result that is “inside-out” where it tries to connect points using the area outside the polygon. This would be also be seen in situations where the CW/CCW winding is incorrectly detected however I don’t believe that to be the case here and if the input points are reversed then the problem is still present. The test case is a bunch of squares merged together into a larger polygon and therefore there are a lot of colinear points which I would speculate is the trigger for this issue.

geoms

Reproduction steps/code

private void test()
{
	float[] points = new float[] {
			41.92309f, -78.590225f,
			41.92219f, -78.590225f,
			41.92219f, -78.591415f,
			41.9195f, -78.591415f,
			41.9195f, -78.592606f,
			41.9186f, -78.592606f,
			41.9186f, -78.589035f,
			41.920395f, -78.589035f,
			41.920395f, -78.58785f,
			41.9186f, -78.58785f,
			41.9186f, -78.58547f,
			41.9195f, -78.58547f,
			41.9195f, -78.58666f,
			41.92219f, -78.58666f,
			41.92219f, -78.589035f,
			41.92309f, -78.589035f,
			41.92309f, -78.590225f
	};
	EarClippingTriangulator triangulator = new EarClippingTriangulator();
	ShortArray shortArray = triangulator.computeTriangles(points);
	// Produces a result of [0, 16, 15, 15, 14, 13, 13, 12, 11, 9, 8, 7, 9, 7, 6, 9, 6, 5, 10, 9, 5, 4, 3, 2, 2, 1, 0, 0, 15, 13, 0, 13, 11, 0, 11, 10, 0, 10, 5, 0, 5, 4, 4, 2, 0]
	// POLYGON ((41.92309 -78.590225, 41.92219 -78.590225, 41.92219 -78.591415, 41.9195 -78.591415, 41.9195 -78.592606, 41.9186 -78.592606, 41.9186 -78.589035, 41.920395 -78.589035, 41.920395 -78.58785, 41.9186 -78.58785, 41.9186 -78.58547, 41.9195 -78.58547, 41.9195 -78.58666, 41.92219 -78.58666, 41.92219 -78.589035, 41.92309 -78.589035, 41.92309 -78.590225))
	// MULTIPOLYGON (((41.92309 -78.590225, 41.92309 -78.590225, 41.92309 -78.589035, 41.92309 -78.590225)), ((41.92309 -78.589035, 41.92219 -78.589035, 41.92219 -78.58666, 41.92309 -78.589035)), ((41.92219 -78.58666, 41.9195 -78.58666, 41.9195 -78.58547, 41.92219 -78.58666)), ((41.9186 -78.58785, 41.920395 -78.58785, 41.920395 -78.589035, 41.9186 -78.58785)), ((41.9186 -78.58785, 41.920395 -78.589035, 41.9186 -78.589035, 41.9186 -78.58785)), ((41.9186 -78.58785, 41.9186 -78.589035, 41.9186 -78.592606, 41.9186 -78.58785)), ((41.9186 -78.58547, 41.9186 -78.58785, 41.9186 -78.592606, 41.9186 -78.58547)), ((41.9195 -78.592606, 41.9195 -78.591415, 41.92219 -78.591415, 41.9195 -78.592606)), ((41.92219 -78.591415, 41.92219 -78.590225, 41.92309 -78.590225, 41.92219 -78.591415)), ((41.92309 -78.590225, 41.92309 -78.589035, 41.92219 -78.58666, 41.92309 -78.590225)), ((41.92309 -78.590225, 41.92219 -78.58666, 41.9195 -78.58547, 41.92309 -78.590225)), ((41.92309 -78.590225, 41.9195 -78.58547, 41.9186 -78.58547, 41.92309 -78.590225)), ((41.92309 -78.590225, 41.9186 -78.58547, 41.9186 -78.592606, 41.92309 -78.590225)), ((41.92309 -78.590225, 41.9186 -78.592606, 41.9195 -78.592606, 41.92309 -78.590225)), ((41.9195 -78.592606, 41.92219 -78.591415, 41.92309 -78.590225, 41.9195 -78.592606)))
}

Version of LibGDX and/or relevant dependencies

Tested on 1.9.3 Test run on Android

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
NathanSweetcommented, May 21, 2019

The data set above has no duplicate or nearly duplicate points I would say many of the points are nearly duplicate.

Like #5631, working with such scales is not ideal. This works correctly:

public class EarClip extends ApplicationAdapter {
	private ShapeRenderer renderer;
	EarClippingTriangulator triangulator = new EarClippingTriangulator();

	public void create () {
		renderer = new ShapeRenderer();
	}

	public void render () {
		float[] points = new float[] { //
			41.92309f, -78.590225f, 41.92219f, -78.590225f, 41.92219f, -78.591415f, 41.9195f, -78.591415f, 41.9195f, -78.592606f,
			41.9186f, -78.592606f, 41.9186f, -78.589035f, 41.920395f, -78.589035f, 41.920395f, -78.58785f, 41.9186f, -78.58785f,
			41.9186f, -78.58547f, 41.9195f, -78.58547f, 41.9195f, -78.58666f, 41.92219f, -78.58666f, 41.92219f, -78.589035f,
			41.92309f, -78.589035f, 41.92309f, -78.590225f};

		for (int i = 0, n = points.length; i < n; i += 2) {
			points[i] = (points[i] - 41.9f) * 20000;
			points[i + 1] = (points[i + 1] + 78.5f) * 20000 + 2000;
		}

		ShortArray triangles = triangulator.computeTriangles(points);

		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		renderer.begin(ShapeType.Line);

		renderer.setColor(Color.RED);
		for (int i = 0; i < triangles.size; i += 3) {
			int p1 = triangles.get(i) * 2;
			int p2 = triangles.get(i + 1) * 2;
			int p3 = triangles.get(i + 2) * 2;
			renderer.triangle( //
				points[p1], points[p1 + 1], //
				points[p2], points[p2 + 1], //
				points[p3], points[p3 + 1] //
			);
		}

		renderer.setColor(Color.GREEN);
		renderer.polygon(points);
		renderer.end();
	}

	public void resize (int width, int height) {
		renderer.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
	}

	public static void main (String[] args) throws Exception {
		new LwjglApplication(new EarClip());
	}
}

Probably you could do a general use massaging, like:

public class EarClip extends ApplicationAdapter {
	private ShapeRenderer renderer;
	EarClippingTriangulator triangulator = new EarClippingTriangulator();

	public void create () {
		renderer = new ShapeRenderer();
	}

	public void render () {
		float[] points = new float[] { //
			41.92309f, -78.590225f, 41.92219f, -78.590225f, 41.92219f, -78.591415f, 41.9195f, -78.591415f, 41.9195f, -78.592606f,
			41.9186f, -78.592606f, 41.9186f, -78.589035f, 41.920395f, -78.589035f, 41.920395f, -78.58785f, 41.9186f, -78.58785f,
			41.9186f, -78.58547f, 41.9195f, -78.58547f, 41.9195f, -78.58666f, 41.92219f, -78.58666f, 41.92219f, -78.589035f,
			41.92309f, -78.589035f, 41.92309f, -78.590225f};

		// Adjust for triangulation.
		float[] forTris = new float[points.length];
		System.arraycopy(points, 0, forTris, 0, points.length);
		float minX = Float.MAX_VALUE, minY = Float.MAX_VALUE;
		for (int i = 0, n = forTris.length; i < n; i += 2) {
			minX = Math.min(minX, forTris[i]);
			minY = Math.min(minY, forTris[i + 1]);
		}
		for (int i = 0, n = forTris.length; i < n; i += 2) {
			forTris[i] = forTris[i] - minX;
			forTris[i + 1] = forTris[i + 1] - minY;
		}
		ShortArray triangles = triangulator.computeTriangles(forTris);

		// Adjust for rendering.
		for (int i = 0, n = points.length; i < n; i += 2) {
			points[i] = (points[i] - 41.9f) * 20000;
			points[i + 1] = (points[i + 1] + 78.5f) * 20000 + 2000;
		}

		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		renderer.begin(ShapeType.Line);

		renderer.setColor(Color.RED);
		for (int i = 0; i < triangles.size; i += 3) {
			int p1 = triangles.get(i) * 2;
			int p2 = triangles.get(i + 1) * 2;
			int p3 = triangles.get(i + 2) * 2;
			renderer.triangle( //
				points[p1], points[p1 + 1], //
				points[p2], points[p2 + 1], //
				points[p3], points[p3 + 1] //
			);
		}

		renderer.setColor(Color.GREEN);
		renderer.polygon(points);
		renderer.end();
	}

	public void resize (int width, int height) {
		renderer.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
	}

	public static void main (String[] args) throws Exception {
		new LwjglApplication(new EarClip());
	}
}
0reactions
kurtzmarccommented, May 16, 2019

areVerticesClockwise is returning false. If we force it to return true, then the triangulation is correct.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Triangulation by Ear Clipping - Geometric Tools
An outer polygon with two inner polygons. The figure makes it clear that none of the vertices of inner polygon I1 are visible...
Read more >
Which triangulation algorithm creates these triangles
I think it is not delanuay or ear clipping but which polygon triangulation method create these triangles? Any help? picture of triangulation.
Read more >
Delaunay triangulation algorithm in shapely producing erratic ...
It is not producing an 'erratic' result. it takes the vertices as inputs as stated in the manual:.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found