FontUtils.js 9.1 KB

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