TextGeometry.js 23 KB

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