'use strict'; if ( ! window.THREE ) throw new Error( 'ERROR: three.js not loaded' ); THREE.Triangulation = ( function() { var timer = false; var library = 'original'; /** * Names of the supported libraries * * @type {{original: string, earcut: string, poly2tri: string, libtess: string}} */ var libraries = { original: 'original', earcut: 'earcut', poly2tri: 'poly2tri', libtess: 'libtess' }; /** * Container object for holding the different library adapters * * @type {{}} */ var adapters = {}; adapters[ libraries.original ] = { triangulate: THREE.ShapeUtils.triangulate, triangulateShape: THREE.ShapeUtils.triangulateShape }; adapters[ libraries.earcut ] = { triangulateShape: function( contour, holes ) { var i, il, dim = 2, array; var holeIndices = []; var points = []; addPoints( contour ); for ( i = 0, il = holes.length; i < il; i ++ ) { holeIndices.push( points.length / dim ); addPoints( holes[ i ] ); } array = earcut( points, holeIndices, dim ); var result = []; for ( i = 0, il = array.length; i < il; i += 3 ) { result.push( array.slice( i, i + 3 ) ); } return result; function addPoints( a ) { var i, il = a.length; for ( i = 0; i < il; i ++ ) { points.push( a[ i ].x, a[ i ].y ); } } } }; adapters[ libraries.poly2tri ] = { triangulateShape: function( contour, holes ) { var i, il, object, sweepContext, triangles; var pointMap = {}, count = 0; points = makePoints( contour ); sweepContext = new poly2tri.SweepContext( points ); for ( i = 0, il = holes.length; i < il; i ++ ) { points = makePoints( holes[ i ] ); sweepContext.addHole( points ); points = points.concat( points ); } object = sweepContext.triangulate(); triangles = object.triangles_; var a, b, c, points, result = []; for ( i = 0, il = triangles.length; i < il; i ++ ) { points = triangles[ i ].points_; a = pointMap[ points[ 0 ].x + ',' + points[ 0 ].y ]; b = pointMap[ points[ 1 ].x + ',' + points[ 1 ].y ]; c = pointMap[ points[ 2 ].x + ',' + points[ 2 ].y ]; result.push( [ a, b, c ] ); } return result; function makePoints( a ) { var i, il = a.length, points = []; for ( i = 0; i < il; i ++ ) { points.push( new poly2tri.Point( a[ i ].x, a[ i ].y ) ); pointMap[ a[ i ].x + ',' + a[ i ].y ] = count; count ++; } return points; } } }; adapters[ libraries.libtess ] = { triangulateShape: function( contour, holes ) { var i, il, triangles = []; var pointMap = {}, count = 0; // libtess will take 3d verts and flatten to a plane for tesselation // since only doing 2d tesselation here, provide z=1 normal to skip // iterating over verts only to get the same answer. // comment out to test normal-generation code tessy.gluTessNormal( 0, 0, 1 ); tessy.gluTessBeginPolygon( triangles ); points = makePoints( contour ); for ( i = 0, il = holes.length; i < il; i ++ ) { points = makePoints( holes[ i ] ); } tessy.gluTessEndPolygon(); var a, b, c, points, result = []; for ( i = 0, il = triangles.length; i < il; i += 6 ) { a = pointMap[ triangles[ i ] + ',' + triangles[ i + 1 ]]; b = pointMap[ triangles[ i + 2 ] + ',' + triangles[ i + 3 ]]; c = pointMap[ triangles[ i + 4 ] + ',' + triangles[ i + 5 ]]; result.push( [ a, b, c ] ); } return result; function makePoints( a ) { var i, il = a.length, coordinates; tessy.gluTessBeginContour(); for ( i = 0; i < il; i ++ ) { coordinates = [ a[ i ].x, a[ i ].y, 0 ]; tessy.gluTessVertex( coordinates, coordinates ); pointMap[ a[ i ].x + ',' + a[ i ].y ] = count; count ++; } tessy.gluTessEndContour(); return points; } } }; /** * Initialize the library by attaching the triangulation methods to the three.js API */ function init() { checkDependencies( library ); if ( timer ) { THREE.ShapeUtils.triangulate = function() { return adapters[ library ].triangulate.apply( this, arguments ); }; THREE.ShapeUtils.triangulateShape = function() { console.time( library ); var result = adapters[ library ].triangulateShape.apply( this, arguments ); console.timeEnd( library ); return result; }; } else { THREE.ShapeUtils.triangulate = adapters[ library ].triangulate; THREE.ShapeUtils.triangulateShape = adapters[ library ].triangulateShape; } } /** * Checks dependencies needed for the current library * * @param library */ function checkDependencies( library ) { switch ( library ) { case libraries.earcut: if ( ! window.earcut ) throw new Error( 'ERROR: earcut not loaded' ); break; case libraries.poly2tri: if ( ! window.poly2tri ) throw new Error( 'ERROR: poly2tri not loaded' ); break; case libraries.libtess: if ( ! window.tessy ) throw new Error( 'ERROR: libtess not loaded' ); break; } } /** * Set the current triangulation library * * @param name */ function setLibrary( name ) { if ( ! libraries.hasOwnProperty( name ) ) throw new Error( 'ERROR: unknown library ' + name ); library = name; init(); } /** * Set timer for triangulation on/off * * @param boolean */ function setTimer( boolean ) { timer = boolean; init(); } init(); return { libraries: libraries, setTimer: setTimer, setLibrary: setLibrary }; } )();