FontUtils.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. /**
  2. * @author zz85 / http://www.lab4games.net/zz85/blog
  3. * @author alteredq / http://alteredqualia.com/
  4. *
  5. * For Text operations in three.js (See TextGeometry)
  6. *
  7. * It uses techniques used in:
  8. *
  9. * Triangulation ported from AS3
  10. * Simple Polygon Triangulation
  11. * http://actionsnippet.com/?p=1462
  12. *
  13. * A Method to triangulate shapes with holes
  14. * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
  15. *
  16. */
  17. THREE.FontUtils = {
  18. faces: {},
  19. // Just for now. face[weight][style]
  20. face: 'helvetiker',
  21. weight: 'normal',
  22. style: 'normal',
  23. size: 150,
  24. divisions: 10,
  25. getFace: function () {
  26. try {
  27. return this.faces[ this.face.toLowerCase() ][ this.weight ][ this.style ];
  28. } catch ( e ) {
  29. throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing."
  30. }
  31. },
  32. loadFace: function ( data ) {
  33. var family = data.familyName.toLowerCase();
  34. var ThreeFont = this;
  35. ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
  36. ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
  37. ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
  38. ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
  39. return data;
  40. },
  41. drawText: function ( text ) {
  42. // RenderText
  43. var i,
  44. face = this.getFace(),
  45. scale = this.size / face.resolution,
  46. offset = 0,
  47. chars = String( text ).split( '' ),
  48. length = chars.length;
  49. var fontPaths = [];
  50. for ( i = 0; i < length; i ++ ) {
  51. var path = new THREE.Path();
  52. var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
  53. offset += ret.offset;
  54. fontPaths.push( ret.path );
  55. }
  56. // get the width
  57. var width = offset / 2;
  58. //
  59. // for ( p = 0; p < allPts.length; p++ ) {
  60. //
  61. // allPts[ p ].x -= width;
  62. //
  63. // }
  64. //var extract = this.extractPoints( allPts, characterPts );
  65. //extract.contour = allPts;
  66. //extract.paths = fontPaths;
  67. //extract.offset = width;
  68. return { paths: fontPaths, offset: width };
  69. },
  70. extractGlyphPoints: function ( c, face, scale, offset, path ) {
  71. var pts = [];
  72. var i, i2, divisions,
  73. outline, action, length,
  74. scaleX, scaleY,
  75. x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
  76. laste,
  77. glyph = face.glyphs[ c ] || face.glyphs[ '?' ];
  78. if ( ! glyph ) return;
  79. if ( glyph.o ) {
  80. outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
  81. length = outline.length;
  82. scaleX = scale;
  83. scaleY = scale;
  84. for ( i = 0; i < length; ) {
  85. action = outline[ i ++ ];
  86. //console.log( action );
  87. switch ( action ) {
  88. case 'm':
  89. // Move To
  90. x = outline[ i ++ ] * scaleX + offset;
  91. y = outline[ i ++ ] * scaleY;
  92. path.moveTo( x, y );
  93. break;
  94. case 'l':
  95. // Line To
  96. x = outline[ i ++ ] * scaleX + offset;
  97. y = outline[ i ++ ] * scaleY;
  98. path.lineTo( x, y );
  99. break;
  100. case 'q':
  101. // QuadraticCurveTo
  102. cpx = outline[ i ++ ] * scaleX + offset;
  103. cpy = outline[ i ++ ] * scaleY;
  104. cpx1 = outline[ i ++ ] * scaleX + offset;
  105. cpy1 = outline[ i ++ ] * scaleY;
  106. path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
  107. laste = pts[ pts.length - 1 ];
  108. if ( laste ) {
  109. cpx0 = laste.x;
  110. cpy0 = laste.y;
  111. for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
  112. var t = i2 / divisions;
  113. THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
  114. THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
  115. }
  116. }
  117. break;
  118. case 'b':
  119. // Cubic Bezier Curve
  120. cpx = outline[ i ++ ] * scaleX + offset;
  121. cpy = outline[ i ++ ] * scaleY;
  122. cpx1 = outline[ i ++ ] * scaleX + offset;
  123. cpy1 = outline[ i ++ ] * scaleY;
  124. cpx2 = outline[ i ++ ] * scaleX + offset;
  125. cpy2 = outline[ i ++ ] * scaleY;
  126. path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
  127. laste = pts[ pts.length - 1 ];
  128. if ( laste ) {
  129. cpx0 = laste.x;
  130. cpy0 = laste.y;
  131. for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
  132. var t = i2 / divisions;
  133. THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
  134. THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
  135. }
  136. }
  137. break;
  138. }
  139. }
  140. }
  141. return { offset: glyph.ha * scale, path: path };
  142. }
  143. };
  144. THREE.FontUtils.generateShapes = function ( text, parameters ) {
  145. // Parameters
  146. parameters = parameters || {};
  147. var size = parameters.size !== undefined ? parameters.size : 100;
  148. var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4;
  149. var font = parameters.font !== undefined ? parameters.font : 'helvetiker';
  150. var weight = parameters.weight !== undefined ? parameters.weight : 'normal';
  151. var style = parameters.style !== undefined ? parameters.style : 'normal';
  152. THREE.FontUtils.size = size;
  153. THREE.FontUtils.divisions = curveSegments;
  154. THREE.FontUtils.face = font;
  155. THREE.FontUtils.weight = weight;
  156. THREE.FontUtils.style = style;
  157. // Get a Font data json object
  158. var data = THREE.FontUtils.drawText( text );
  159. var paths = data.paths;
  160. var shapes = [];
  161. for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
  162. Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
  163. }
  164. return shapes;
  165. };
  166. /**
  167. * This code is a quick port of code written in C++ which was submitted to
  168. * flipcode.com by John W. Ratcliff // July 22, 2000
  169. * See original code and more information here:
  170. * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
  171. *
  172. * ported to actionscript by Zevan Rosser
  173. * www.actionsnippet.com
  174. *
  175. * ported to javascript by Joshua Koo
  176. * http://www.lab4games.net/zz85/blog
  177. *
  178. */
  179. ( function ( namespace ) {
  180. var EPSILON = 0.0000000001;
  181. // takes in an contour array and returns
  182. var process = function ( contour, indices ) {
  183. var n = contour.length;
  184. if ( n < 3 ) return null;
  185. var result = [],
  186. verts = [],
  187. vertIndices = [];
  188. /* we want a counter-clockwise polygon in verts */
  189. var u, v, w;
  190. if ( area( contour ) > 0.0 ) {
  191. for ( v = 0; v < n; v ++ ) verts[ v ] = v;
  192. } else {
  193. for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;
  194. }
  195. var nv = n;
  196. /* remove nv - 2 vertices, creating 1 triangle every time */
  197. var count = 2 * nv; /* error detection */
  198. for ( v = nv - 1; nv > 2; ) {
  199. /* if we loop, it is probably a non-simple polygon */
  200. if ( ( count -- ) <= 0 ) {
  201. //** Triangulate: ERROR - probable bad polygon!
  202. //throw ( "Warning, unable to triangulate polygon!" );
  203. //return null;
  204. // Sometimes warning is fine, especially polygons are triangulated in reverse.
  205. console.warn( 'THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()' );
  206. if ( indices ) return vertIndices;
  207. return result;
  208. }
  209. /* three consecutive vertices in current polygon, <u,v,w> */
  210. u = v; if ( nv <= u ) u = 0; /* previous */
  211. v = u + 1; if ( nv <= v ) v = 0; /* new v */
  212. w = v + 1; if ( nv <= w ) w = 0; /* next */
  213. if ( snip( contour, u, v, w, nv, verts ) ) {
  214. var a, b, c, s, t;
  215. /* true names of the vertices */
  216. a = verts[ u ];
  217. b = verts[ v ];
  218. c = verts[ w ];
  219. /* output Triangle */
  220. result.push( [ contour[ a ],
  221. contour[ b ],
  222. contour[ c ] ] );
  223. vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
  224. /* remove v from the remaining polygon */
  225. for ( s = v, t = v + 1; t < nv; s ++, t ++ ) {
  226. verts[ s ] = verts[ t ];
  227. }
  228. nv --;
  229. /* reset error detection counter */
  230. count = 2 * nv;
  231. }
  232. }
  233. if ( indices ) return vertIndices;
  234. return result;
  235. };
  236. // calculate area of the contour polygon
  237. var area = function ( contour ) {
  238. var n = contour.length;
  239. var a = 0.0;
  240. for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
  241. a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
  242. }
  243. return a * 0.5;
  244. };
  245. var snip = function ( contour, u, v, w, n, verts ) {
  246. var p;
  247. var ax, ay, bx, by;
  248. var cx, cy, px, py;
  249. ax = contour[ verts[ u ] ].x;
  250. ay = contour[ verts[ u ] ].y;
  251. bx = contour[ verts[ v ] ].x;
  252. by = contour[ verts[ v ] ].y;
  253. cx = contour[ verts[ w ] ].x;
  254. cy = contour[ verts[ w ] ].y;
  255. if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;
  256. var aX, aY, bX, bY, cX, cY;
  257. var apx, apy, bpx, bpy, cpx, cpy;
  258. var cCROSSap, bCROSScp, aCROSSbp;
  259. aX = cx - bx; aY = cy - by;
  260. bX = ax - cx; bY = ay - cy;
  261. cX = bx - ax; cY = by - ay;
  262. for ( p = 0; p < n; p ++ ) {
  263. px = contour[ verts[ p ] ].x;
  264. py = contour[ verts[ p ] ].y;
  265. if ( ( ( px === ax ) && ( py === ay ) ) ||
  266. ( ( px === bx ) && ( py === by ) ) ||
  267. ( ( px === cx ) && ( py === cy ) ) ) continue;
  268. apx = px - ax; apy = py - ay;
  269. bpx = px - bx; bpy = py - by;
  270. cpx = px - cx; cpy = py - cy;
  271. // see if p is inside triangle abc
  272. aCROSSbp = aX * bpy - aY * bpx;
  273. cCROSSap = cX * apy - cY * apx;
  274. bCROSScp = bX * cpy - bY * cpx;
  275. if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false;
  276. }
  277. return true;
  278. };
  279. namespace.Triangulate = process;
  280. namespace.Triangulate.area = area;
  281. return namespace;
  282. } )( THREE.FontUtils );
  283. // To use the typeface.js face files, hook up the API
  284. THREE.typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
  285. if ( typeof self !== 'undefined' ) self._typeface_js = THREE.typeface_js;