mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.8k stars 35.38k forks source link

Fast polygon triangulation (Seidel's Trapezoidation) - reasonably small #4920

Closed jahting closed 8 years ago

jahting commented 10 years ago

Three.js at present includes a Shape/Text triangulation which was easy to implement but also needed several add-ons, eg. to decide between contours and holes and to remove those holes, and it had quite some issues concerning:

It seems to work correctly by now but is quite slow if there are many vertices or holes.

Instead of trying to improve the present code further, I created a new project PnlTri.js: Polygon Near-Linear Triangulation in JavaScript

I also created a branch for Three.js with PnlTri, replacing the present mechanism. So interested users of Three.js can test their data against it. More test data would be welcome.

The main drawback might be that this algorithm often produces rather long, slim triangles. I'm thinking about a post-processing step to improve on this in the future. There is still some room to refactor PnlTri and reduce the code surrounding triangulation in Three.js. The next major step for PnlTri will be to allow polygons with intersections which would also increase robustness further.

@mrdoob: Is it small enough for integration into Three.js, or should it be left for people to combine them on their own ?

mrdoob commented 10 years ago

Hmm, how's it performance wise compared to the current code? If this is a full replacement, I'm tempted to have it as a library outside the lib (used in examples folder) and reduce the code in core even more...

jahting commented 10 years ago

From the past issues you can see that the problems with current implementation start with holes, which are needed for fonts also (e.g. "$", "%", "&").

For polygons without holes the current implementation is quite fast. While it has a worse worst case running time, for all practical cases (fonts or polygons up to at least 500 vertices) it seems to be faster than PnlTri.js . So I guess I should integrate the old core triangulation (which is quite small) for polygons without holes into PnlTri.js too.

For polygons with holes a big advantage of PnlTri.js is that it makes no assumption on winding order, a big problem with the current implementation. Even with using PnlTri.js the extrusion algorithm still relies on winding order (ref. "opQR" in: http://jsfiddle.net/MnV2R/2/) but that might be resolved.

Using my old Laptop (Core 2 Duo T7200, 2 GHz) with Firefox 30.0:

mrdoob commented 10 years ago

I like the numbers! :)

Do you mind doing a PR with the pnltri.min.js in examples/js and with the relevant examples updated?

zz85 commented 10 years ago

:+1:

jahting commented 10 years ago

Triangulation is only used internally by ExtrudeGeometry, ShapeGeometry and TextGeometry and not called explicitly in any example. So changing any example would just require the following glue code:

THREE.Shape.Utils.triangulateShape = function ( contour, holes ) {
    var pnlTriangulator = new PNLTRI.Triangulator();
    return  pnlTriangulator.triangulate_polygon( [ contour ].concat(holes) );
}

... a rather nasty method :-( .

So instead of changing any existing example I would suggest, that I make a copy of webgl_geometry_text.html -> webgl_geometry_text2.html and add this glue code as an additional example.

mrdoob commented 10 years ago

Well, that seems like a good first step. Does the triangulator needs to be instanced all the time, or could it be like this?

THREE.Shape.Utils.triangulateShape = ( function () {
    var pnlTriangulator = new PNLTRI.Triangulator();
    return function ( contour, holes ) {
        return pnlTriangulator.triangulate_polygon( [ contour ].concat(holes) );
    };
} )();
zz85 commented 10 years ago

@jahting do you think changing the way we do triangulation as suggested in this issue https://github.com/mrdoob/three.js/issues/4745 would be more better since you're working in this area too?

jahting commented 10 years ago

@zz85 yes I agree with your idea in #4745. As you can see from my example #4950 the API of the present triangulation and my new one is quite small - just an Array of vertex lists (polygons and holes), which are just Arrays of objects with at least the attributes x and y. The resulting triangles are presented as Array of triangles, which are triples of indexes into the original vertex list (faces). This API is not sufficient for triangulations which allow the creation of additional Steiner points, which at present seems not necessary. So it is easy to separate the triangulation and write adapters for other libraries.

arodic commented 10 years ago

Are there any intentions to replace THREE.triangulateShape with pnltri in the future?

mrdoob commented 10 years ago

I think it'll happen eventually. It's a matter of being able to fit it in the ~100kb gzip budget :)

jahting commented 10 years ago

I'm still working on improving performance and functionality and reducing size. It would help if someone knows how to change the output of the closure compiler so it can be used as input to the closure compiler again (for including the compiled version of pnltri in three.js).

tonnot commented 10 years ago

@jahting
Sorry if I ask something already answered...

Anyway, thanks for this superb job

jahting commented 10 years ago

@tonnot

I hope I could answer your questions and that pnltri.js is still useful for you.

mourner commented 9 years ago

@mrdoob FYI in case you're still considering a better triangulation algorithm. I wrote https://github.com/mapbox/earcut, ultimately the fastest and smallest (2.3k gzipped) JS triangulation library. It's very robust and scales well with shape complexity. Check it out!

WestLangley commented 9 years ago

@mourner #5959 : - )