TextGeometry.js 21 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. /**
  2. * @author zz85 / http://www.lab4games.net/zz85/blog
  3. * @author alteredq / http://alteredqualia.com/
  4. *
  5. * For creating 3D text geometry in three.js
  6. *
  7. * Text = 3D Text
  8. *
  9. * parameters = {
  10. * size: <float>, // size of the text
  11. * height: <float>, // thickness to extrude text
  12. * curveSegments: <int>, // number of points on the curves
  13. *
  14. * font: <string>, // font name
  15. * weight: <string>, // font weight (normal, bold)
  16. * style: <string>, // font style (normal, italics)
  17. *
  18. * bezelEnabled: <bool>, // turn on bezel
  19. * bezelThickness: <float>, // how deep into text bezel goes
  20. * bezelSize: <float>, // how far from text outline is bezel
  21. * }
  22. *
  23. * It uses techniques used in:
  24. *
  25. * typeface.js and canvastext
  26. * For converting fonts and rendering with javascript
  27. * http://typeface.neocracy.org
  28. *
  29. * Triangulation ported from AS3
  30. * Simple Polygon Triangulation
  31. * http://actionsnippet.com/?p=1462
  32. *
  33. * A Method to triangulate shapes with holes
  34. * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
  35. *
  36. */
  37. THREE.TextGeometry = function ( text, parameters ) {
  38. THREE.Geometry.call( this );
  39. this.parameters = parameters || {};
  40. this.set( text );
  41. };
  42. /*
  43. var text3d = new TextGeometry(text);
  44. FactoryStyle
  45. var text3d = FontUtils.createText(text);
  46. var textPath = new TextPath(text);
  47. var textShapes = textPath.toShapes();
  48. var text3d = new ExtrudeGeometry(textShapes, options);
  49. var textShapes = FontUtils.getTextShapes(text);
  50. text3d = new ExtrudeGeometry(textShapes, options);
  51. */
  52. THREE.TextGeometry.prototype = new THREE.Geometry();
  53. THREE.TextGeometry.prototype.constructor = THREE.TextGeometry;
  54. THREE.TextGeometry.prototype.set = function ( text, parameters ) {
  55. this.text = text;
  56. var parameters = parameters || this.parameters;
  57. var size = parameters.size !== undefined ? parameters.size : 100;
  58. var height = parameters.height !== undefined ? parameters.height : 50;
  59. var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4;
  60. var font = parameters.font !== undefined ? parameters.font : "helvetiker";
  61. var weight = parameters.weight !== undefined ? parameters.weight : "normal";
  62. var style = parameters.style !== undefined ? parameters.style : "normal";
  63. var bezelThickness = parameters.bezelThickness !== undefined ? parameters.bezelThickness : 10;
  64. var bezelSize = parameters.bezelSize !== undefined ? parameters.bezelSize : 8;
  65. var bezelEnabled = parameters.bezelEnabled !== undefined ? parameters.bezelEnabled : false;
  66. THREE.FontUtils.size = size;
  67. THREE.FontUtils.divisions = curveSegments;
  68. THREE.FontUtils.face = font;
  69. THREE.FontUtils.weight = weight;
  70. THREE.FontUtils.style = style;
  71. THREE.FontUtils.bezelSize = bezelSize;
  72. };
  73. THREE.TextGeometry.prototype.get = function () {
  74. // Get a Font data json object
  75. var data = THREE.FontUtils.drawText( this.text );
  76. var paths = data.paths;
  77. var shapes = [];
  78. for (var p=0, pl = paths.length; p<pl; p++) {
  79. shapes = shapes.concat(paths[p].toShapes());
  80. }
  81. //console.log(path);
  82. //console.log(fontShapes);
  83. // Either find actions or curves.
  84. var text3d = new THREE.ExtrudeGeometry( shapes , { amount: 20, bevelEnabled:true, bevelThickness:3 } );
  85. // TOFIX: Fillet Cap
  86. // FIX HOLES
  87. return text3d;
  88. };
  89. THREE.FontUtils = {
  90. faces : {},
  91. // Just for now. face[weight][style]
  92. face : "helvetiker",
  93. weight: "normal",
  94. style : "normal",
  95. size : 150,
  96. divisions : 10,
  97. getFace : function() {
  98. return this.faces[ this.face ][ this.weight ][ this.style ];
  99. },
  100. loadFace : function( data ) {
  101. var family = data.familyName.toLowerCase();
  102. var ThreeFont = this;
  103. ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
  104. ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
  105. ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
  106. var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
  107. return data;
  108. },
  109. extractPoints : function( allPoints, charactersPoints ) {
  110. // Quick exit
  111. if ( allPoints.length < 3 ) {
  112. //throw "not valid polygon";
  113. console.log( "not valid polygon" );
  114. return {
  115. points: allPoints,
  116. faces: []
  117. };
  118. }
  119. // Try to split shapes and holes.
  120. var p, point, shape,
  121. all,
  122. ch, singleCharPoints,
  123. isolatedShapes = [];
  124. // Use a quick hashmap for locating duplicates
  125. for ( var c = 0; c < charactersPoints.length; c ++ ) {
  126. singleCharPoints = charactersPoints[ c ];
  127. all = [];
  128. // Use a quick hashmap for locating duplicates
  129. for ( var p = 0; p < singleCharPoints.length; p ++ ) {
  130. point = singleCharPoints[ p ];
  131. all.push( point.x + "," + point.y );
  132. }
  133. var firstIndex, firstPt, endPt, holes;
  134. // We check the first loop whether its CW or CCW direction to determine
  135. // whether its shapes or holes first
  136. endPt = all.slice( 1 ).indexOf( all[ 0 ] );
  137. var shapesFirst = this.Triangulate.area( singleCharPoints.slice( 0, endPt + 1 ) ) < 0;
  138. //console.log( singleCharPoints.length, "shapesFirst", shapesFirst );
  139. holes = [];
  140. endPt = -1;
  141. while ( endPt < all.length ) {
  142. firstIndex = endPt + 1;
  143. firstPt = all[ firstIndex ];
  144. endPt = all.slice( firstIndex + 1 ).indexOf( firstPt ) + firstIndex;
  145. if ( endPt <= firstIndex ) break;
  146. var contours = singleCharPoints.slice( firstIndex, endPt + 1 );
  147. if ( shapesFirst ) {
  148. if ( this.Triangulate.area( contours ) < 0 ) {
  149. // we got new isolated shape
  150. if ( firstIndex > 0 ) {
  151. isolatedShapes.push( { shape: shape, holes: holes } );
  152. }
  153. // Save the old shapes, then work on new additional separated shape
  154. shape = contours;
  155. holes = [];
  156. } else {
  157. holes.push( contours );
  158. }
  159. } else {
  160. if ( this.Triangulate.area( contours ) < 0 ) {
  161. isolatedShapes.push( { shape: contours, holes: holes } );
  162. holes = [];
  163. } else {
  164. holes.push( contours );
  165. }
  166. }
  167. endPt++;
  168. }
  169. if ( shapesFirst ) {
  170. isolatedShapes.push( { shape: shape, holes: holes } );
  171. }
  172. }
  173. //console.log("isolatedShapes", isolatedShapes);
  174. /* For each isolated shape, find the closest points and break to the hole to allow triangulation*/
  175. // Find closest points between holes
  176. // we could optimize with
  177. // http://en.wikipedia.org/wiki/Proximity_problems
  178. // http://en.wikipedia.org/wiki/Closest_pair_of_points
  179. // http://stackoverflow.com/questions/1602164/shortest-distance-between-points-algorithm
  180. var prevShapeVert, nextShapeVert,
  181. prevHoleVert, nextHoleVert,
  182. holeIndex, shapeIndex,
  183. shapeId, shapeGroup,
  184. h, h2,
  185. hole, shortest, d,
  186. p, pts1, pts2,
  187. tmpShape1, tmpShape2,
  188. tmpHole1, tmpHole2,
  189. verts = [];
  190. for ( shapeId = 0; shapeId < isolatedShapes.length; shapeId ++ ) {
  191. shapeGroup = isolatedShapes[ shapeId ];
  192. shape = shapeGroup.shape;
  193. holes = shapeGroup.holes;
  194. for ( h = 0; h < holes.length; h++ ) {
  195. // we slice to each hole when neccessary
  196. hole = holes[ h ];
  197. shortest = Number.POSITIVE_INFINITY;
  198. for ( h2 = 0; h2 < hole.length; h2++ ) {
  199. pts1 = hole[ h2 ];
  200. for ( p = 0; p < shape.length; p++ ) {
  201. pts2 = shape[ p ];
  202. d = pts1.distanceTo( pts2 );
  203. if ( d < shortest ) {
  204. shortest = d;
  205. holeIndex = h2;
  206. shapeIndex = p;
  207. }
  208. }
  209. }
  210. prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
  211. nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0;
  212. prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
  213. nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ;
  214. var areaapts = [];
  215. areaapts.push( hole[ holeIndex ] );
  216. areaapts.push( shape[ shapeIndex ] );
  217. areaapts.push( shape[ prevShapeVert ] );
  218. var areaa = this.Triangulate.area( areaapts );
  219. var areabpts = [];
  220. areabpts.push( hole[ holeIndex ] );
  221. areabpts.push( hole[ prevHoleVert ] );
  222. areabpts.push( shape[ shapeIndex ] );
  223. var areab = this.Triangulate.area( areabpts );
  224. var shapeOffset =1;
  225. var holeOffset = -1;
  226. var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex;
  227. shapeIndex += shapeOffset;
  228. holeIndex += holeOffset;
  229. if ( shapeIndex < 0 ) { shapeIndex += shape.length; }
  230. shapeIndex %= shape.length;
  231. if ( holeIndex < 0 ) { holeIndex += hole.length; }
  232. holeIndex %= shape.length;
  233. prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
  234. nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0;
  235. prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
  236. nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ;
  237. areaapts = [];
  238. areaapts.push( hole[ holeIndex ] );
  239. areaapts.push( shape[ shapeIndex ] );
  240. areaapts.push( shape[ prevShapeVert ] );
  241. var areaa2 = this.Triangulate.area( areaapts );
  242. areabpts = [];
  243. areabpts.push( hole[ holeIndex ] );
  244. areabpts.push( hole[ prevHoleVert ] );
  245. areabpts.push( shape[ shapeIndex ] );
  246. var areab2 = this.Triangulate.area( areabpts );
  247. if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) {
  248. shapeIndex = oldShapeIndex;
  249. holeIndex = oldHoleIndex ;
  250. if ( shapeIndex < 0 ) { shapeIndex += shape.length; }
  251. shapeIndex %= shape.length;
  252. if ( holeIndex < 0 ) { holeIndex += hole.length; }
  253. holeIndex %= shape.length;
  254. prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
  255. nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0;
  256. prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
  257. nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ;
  258. }
  259. tmpShape1 = shape.slice( 0, shapeIndex );
  260. tmpShape2 = shape.slice( shapeIndex );
  261. tmpHole1 = hole.slice( holeIndex );
  262. tmpHole2 = hole.slice( 0, holeIndex );
  263. verts.push( hole[ holeIndex ] );
  264. verts.push( shape[ shapeIndex ] );
  265. verts.push( shape[ prevShapeVert ] );
  266. verts.push( hole[ holeIndex ] );
  267. verts.push( hole[ prevHoleVert ] );
  268. verts.push( shape[ shapeIndex ] );
  269. shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
  270. }
  271. shapeGroup.shape = shape;
  272. }
  273. var triangulatedPoints = [];
  274. var triangulatedFaces = [];
  275. var lastTriangles = 0;
  276. for ( shapeId = 0; shapeId < isolatedShapes.length; shapeId ++ ) {
  277. shapeGroup = isolatedShapes[ shapeId ];
  278. shape = shapeGroup.shape;
  279. triangulatedPoints = triangulatedPoints.concat( shape );
  280. var triangles = THREE.FontUtils.Triangulate( shape, true );
  281. // We need to offset vertex indices for faces
  282. for ( var v = 0; v < triangles.length; v++ ) {
  283. var face = triangles[ v ];
  284. face[ 0 ] += lastTriangles;
  285. face[ 1 ] += lastTriangles;
  286. face[ 2 ] += lastTriangles;
  287. }
  288. triangulatedFaces = triangulatedFaces.concat( triangles );
  289. lastTriangles += shape.length;
  290. }
  291. // Now we push the "cut" vertices back to the triangulated indices.
  292. //console.log("we have verts.length",verts.length,verts);
  293. var v, j, k, l, found, face;
  294. for ( v = 0; v < verts.length / 3; v++ ) {
  295. face = [];
  296. for ( k = 0; k < 3; k++ ) {
  297. found = false;
  298. for ( j = 0; j < triangulatedPoints.length && !found; j++ ) {
  299. l = v * 3 + k;
  300. if ( triangulatedPoints[ j ].equals( verts[ l ] ) ) {
  301. face.push( j );
  302. found = true;
  303. }
  304. }
  305. // you would not wish to reach this point of code, something went wrong
  306. if ( !found ) {
  307. triangulatedPoints.push( verts[ l ] );
  308. face.push( triangulatedPoints.length - 1 );
  309. console.log( "not found" )
  310. }
  311. }
  312. triangulatedFaces.push( face );
  313. }
  314. //console.log( "triangles", triangulatedFaces.length, "points", triangulatedPoints );
  315. return {
  316. points: triangulatedPoints,
  317. faces: triangulatedFaces
  318. };
  319. },
  320. drawText : function( text ) {
  321. var characterPts = [], allPts = [];
  322. // RenderText
  323. var i, p,
  324. face = this.getFace(),
  325. scale = this.size / face.resolution,
  326. offset = 0,
  327. chars = String( text ).split( '' ),
  328. length = chars.length;
  329. var fontPaths = [];
  330. for ( i = 0; i < length; i++ ) {
  331. var path = new THREE.Path();
  332. var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
  333. offset += ret.offset;
  334. characterPts.push( ret.points );
  335. allPts = allPts.concat( ret.points );
  336. fontPaths.push( ret.path );
  337. }
  338. // get the width
  339. var width = offset / 2;
  340. for ( p = 0; p < allPts.length; p++ ) {
  341. allPts[ p ].x -= width;
  342. }
  343. var extract = this.extractPoints( allPts, characterPts );
  344. extract.contour = allPts;
  345. extract.paths = fontPaths;
  346. var bezelPoints = [];
  347. var centroids = [], forCentroids = [], expandOutwards = [], sum = new THREE.Vector2(), lastV;
  348. i = allPts.length;
  349. while ( --i >= 0 ) {
  350. if ( !lastV ) {
  351. lastV = allPts[ i ];
  352. } else if ( lastV.equals( allPts[ i ] ) ) {
  353. // We reached the last point of a closed loop
  354. lastV = null;
  355. var bool = this.Triangulate.area( forCentroids ) > 0;
  356. expandOutwards.push( bool );
  357. centroids.push( sum.divideScalar( forCentroids.length ) );
  358. forCentroids = [];
  359. sum = new THREE.Vector2();
  360. continue;
  361. }
  362. sum.addSelf( allPts[ i ] );
  363. forCentroids.push( allPts[ i ] );
  364. }
  365. i = allPts.length;
  366. p = 0;
  367. var pt, centroid ;
  368. var dirV, adj;
  369. while ( --i >= 0 ) {
  370. pt = allPts[ i ];
  371. centroid = centroids[ p ];
  372. dirV = pt.clone().subSelf( centroid );
  373. adj = this.bezelSize / dirV.length();
  374. if ( expandOutwards[ p ] ) {
  375. adj += 1;
  376. } else {
  377. adj = 1 - adj;
  378. }
  379. adj = dirV.multiplyScalar( adj ).addSelf( centroid );
  380. bezelPoints.unshift( adj );
  381. if ( !lastV ) {
  382. lastV = allPts[ i ];
  383. } else if ( lastV.equals( allPts[ i ] ) ) {
  384. // We reached the last point of a closed loop
  385. lastV = null;
  386. p++;
  387. continue;
  388. }
  389. }
  390. /*
  391. for ( p = 0; p < allPts.length; p++ ) {
  392. pt = allPts[ p ];
  393. bezelPoints.push( new THREE.Vector2( pt.x + this.bezelSize, pt.y + this.bezelSize ) );
  394. }
  395. */
  396. extract.bezel = bezelPoints;
  397. return extract;
  398. },
  399. // Bezier Curves formulas obtained from
  400. // http://en.wikipedia.org/wiki/B%C3%A9zier_curve
  401. // Quad Bezier Functions
  402. b2p0: function ( t, p ) {
  403. var k = 1 - t;
  404. return k * k * p;
  405. },
  406. b2p1: function ( t, p ) {
  407. return 2 * ( 1 - t ) * t * p;
  408. },
  409. b2p2: function ( t, p ) {
  410. return t * t * p;
  411. },
  412. b2: function ( t, p0, p1, p2 ) {
  413. return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
  414. },
  415. // Cubic Bezier Functions
  416. b3p0: function ( t, p ) {
  417. var k = 1 - t;
  418. return k * k * k * p;
  419. },
  420. b3p1: function ( t, p ) {
  421. var k = 1 - t;
  422. return 3 * k * k * t * p;
  423. },
  424. b3p2: function ( t, p ) {
  425. var k = 1 - t;
  426. return 3 * k * t * t * p;
  427. },
  428. b3p3: function ( t, p ) {
  429. return t * t * t * p;
  430. },
  431. b3: function ( t, p0, p1, p2, p3 ) {
  432. return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 );
  433. },
  434. extractGlyphPoints : function( c, face, scale, offset, path ) {
  435. var pts = [];
  436. var i, i2,
  437. outline, action, length,
  438. scaleX, scaleY,
  439. x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
  440. laste,
  441. glyph = face.glyphs[ c ] || face.glyphs[ ctxt.options.fallbackCharacter ];
  442. if ( !glyph ) return;
  443. if ( glyph.o ) {
  444. outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
  445. length = outline.length;
  446. scaleX = scale;
  447. scaleY = scale;
  448. for ( i = 0; i < length; ) {
  449. action = outline[ i++ ];
  450. //console.log(action);
  451. switch( action ) {
  452. case 'm':
  453. // Move To
  454. x = outline[ i++ ] * scaleX + offset;
  455. y = outline[ i++ ] * scaleY;
  456. pts.push( new THREE.Vector2( x, y ) );
  457. path.moveTo(x,y);
  458. break;
  459. case 'l':
  460. // Line To
  461. x = outline[ i++ ] * scaleX + offset;
  462. y = outline[ i++ ] * scaleY;
  463. pts.push( new THREE.Vector2( x, y ) );
  464. path.lineTo(x,y);
  465. break;
  466. case 'q':
  467. // QuadraticCurveTo
  468. cpx = outline[ i++ ] * scaleX + offset;
  469. cpy = outline[ i++ ] * scaleY;
  470. cpx1 = outline[ i++ ] * scaleX + offset;
  471. cpy1 = outline[ i++ ] * scaleY;
  472. path.quadraticCurveTo(cpx1, cpy1, cpx, cpy);
  473. laste = pts[ pts.length - 1 ];
  474. if ( laste ) {
  475. cpx0 = laste.x;
  476. cpy0 = laste.y;
  477. for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2++ ) {
  478. var t = i2 / divisions;
  479. var tx = THREE.FontUtils.b2( t, cpx0, cpx1, cpx );
  480. var ty = THREE.FontUtils.b2( t, cpy0, cpy1, cpy );
  481. pts.push( new THREE.Vector2( tx, ty ) );
  482. }
  483. }
  484. break;
  485. case 'b':
  486. // Cubic Bezier Curve
  487. cpx = outline[ i++ ] * scaleX + offset;
  488. cpy = outline[ i++ ] * scaleY;
  489. cpx1 = outline[ i++ ] * scaleX + offset;
  490. cpy1 = outline[ i++ ] * -scaleY;
  491. cpx2 = outline[ i++ ] * scaleX + offset;
  492. cpy2 = outline[ i++ ] * -scaleY;
  493. path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 );
  494. laste = pts[ pts.length - 1 ];
  495. if ( laste ) {
  496. cpx0 = laste.x;
  497. cpy0 = laste.y;
  498. for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2++ ) {
  499. var t = i2 / divisions;
  500. var tx = THREE.FontUtils.b3( t, cpx0, cpx1, cpx2, cpx );
  501. var ty = THREE.FontUtils.b3( t, cpy0, cpy1, cpy2, cpy );
  502. pts.push( new THREE.Vector2( tx, ty ) );
  503. }
  504. }
  505. break;
  506. }
  507. }
  508. }
  509. return { offset: glyph.ha*scale, points:pts, path:path};
  510. }
  511. };
  512. /**
  513. * This code is a quick port of code written in C++ which was submitted to
  514. * flipcode.com by John W. Ratcliff // July 22, 2000
  515. * See original code and more information here:
  516. * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
  517. *
  518. * ported to actionscript by Zevan Rosser
  519. * www.actionsnippet.com
  520. *
  521. * ported to javascript by Joshua Koo
  522. * http://www.lab4games.net/zz85/blog
  523. *
  524. */
  525. ( function( namespace ) {
  526. var EPSILON = 0.0000000001;
  527. // takes in an contour array and returns
  528. var process = function( contour, indices ) {
  529. var n = contour.length;
  530. if ( n < 3 ) return null;
  531. var result = [],
  532. verts = [],
  533. vertIndices = [];
  534. /* we want a counter-clockwise polygon in verts */
  535. var u, v, w;
  536. if ( area( contour ) > 0.0 ) {
  537. for ( v = 0; v < n; v++ ) verts[ v ] = v;
  538. } else {
  539. for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v;
  540. }
  541. var nv = n;
  542. /* remove nv - 2 vertices, creating 1 triangle every time */
  543. var count = 2 * nv; /* error detection */
  544. for( v = nv - 1; nv > 2; ) {
  545. /* if we loop, it is probably a non-simple polygon */
  546. if ( ( count-- ) <= 0 ) {
  547. //** Triangulate: ERROR - probable bad polygon!
  548. //throw ( "Warning, unable to triangulate polygon!" );
  549. //return null;
  550. // Sometimes warning is fine, especially polygons are triangulated in reverse.
  551. console.log( "Warning, unable to triangulate polygon!" );
  552. if ( indices ) return vertIndices;
  553. return result;
  554. }
  555. /* three consecutive vertices in current polygon, <u,v,w> */
  556. u = v; if ( nv <= u ) u = 0; /* previous */
  557. v = u + 1; if ( nv <= v ) v = 0; /* new v */
  558. w = v + 1; if ( nv <= w ) w = 0; /* next */
  559. if ( snip( contour, u, v, w, nv, verts ) ) {
  560. var a, b, c, s, t;
  561. /* true names of the vertices */
  562. a = verts[ u ];
  563. b = verts[ v ];
  564. c = verts[ w ];
  565. /* output Triangle */
  566. /*
  567. result.push( contour[ a ] );
  568. result.push( contour[ b ] );
  569. result.push( contour[ c ] );
  570. */
  571. result.push( [ contour[ a ],
  572. contour[ b ],
  573. contour[ c ] ] );
  574. vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
  575. /* remove v from the remaining polygon */
  576. for( s = v, t = v + 1; t < nv; s++, t++ ) {
  577. verts[ s ] = verts[ t ];
  578. }
  579. nv--;
  580. /* reset error detection counter */
  581. count = 2 * nv;
  582. }
  583. }
  584. if ( indices ) return vertIndices;
  585. return result;
  586. };
  587. // calculate area of the contour polygon
  588. var area = function ( contour ) {
  589. var n = contour.length;
  590. var a = 0.0;
  591. for( var p = n - 1, q = 0; q < n; p = q++ ) {
  592. a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
  593. }
  594. return a * 0.5;
  595. };
  596. // see if p is inside triangle abc
  597. var insideTriangle = function( ax, ay,
  598. bx, by,
  599. cx, cy,
  600. px, py ) {
  601. var aX, aY, bX, bY;
  602. var cX, cY, apx, apy;
  603. var bpx, bpy, cpx, cpy;
  604. var cCROSSap, bCROSScp, aCROSSbp;
  605. aX = cx - bx; aY = cy - by;
  606. bX = ax - cx; bY = ay - cy;
  607. cX = bx - ax; cY = by - ay;
  608. apx= px -ax; apy= py - ay;
  609. bpx= px - bx; bpy= py - by;
  610. cpx= px - cx; cpy= py - cy;
  611. aCROSSbp = aX*bpy - aY*bpx;
  612. cCROSSap = cX*apy - cY*apx;
  613. bCROSScp = bX*cpy - bY*cpx;
  614. return ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) );
  615. };
  616. var snip = function ( contour, u, v, w, n, verts ) {
  617. var p;
  618. var ax, ay, bx, by;
  619. var cx, cy, px, py;
  620. ax = contour[ verts[ u ] ].x;
  621. ay = contour[ verts[ u ] ].y;
  622. bx = contour[ verts[ v ] ].x;
  623. by = contour[ verts[ v ] ].y;
  624. cx = contour[ verts[ w ] ].x;
  625. cy = contour[ verts[ w ] ].y;
  626. if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false;
  627. for ( p = 0; p < n; p++ ) {
  628. if( (p == u) || (p == v) || (p == w) ) continue;
  629. px = contour[ verts[ p ] ].x
  630. py = contour[ verts[ p ] ].y
  631. if ( insideTriangle( ax, ay, bx, by, cx, cy, px, py ) ) return false;
  632. }
  633. return true;
  634. };
  635. namespace.Triangulate = process;
  636. namespace.Triangulate.area = area;
  637. return namespace;
  638. })(THREE.FontUtils);
  639. // To use the typeface.js face files, hook up the API
  640. window._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };