|
'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 |
|
|
|
}; |
|
|
|
} )(); |