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