FontUtils.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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. /*
  222. result.push( contour[ a ] );
  223. result.push( contour[ b ] );
  224. result.push( contour[ c ] );
  225. */
  226. result.push( [ contour[ a ],
  227. contour[ b ],
  228. contour[ c ] ] );
  229. vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
  230. /* remove v from the remaining polygon */
  231. for( s = v, t = v + 1; t < nv; s++, t++ ) {
  232. verts[ s ] = verts[ t ];
  233. }
  234. nv--;
  235. /* reset error detection counter */
  236. count = 2 * nv;
  237. }
  238. }
  239. if ( indices ) return vertIndices;
  240. return result;
  241. };
  242. // calculate area of the contour polygon
  243. var area = function ( contour ) {
  244. var n = contour.length;
  245. var a = 0.0;
  246. for( var p = n - 1, q = 0; q < n; p = q++ ) {
  247. a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
  248. }
  249. return a * 0.5;
  250. };
  251. // see if p is inside triangle abc
  252. var insideTriangle = function( ax, ay,
  253. bx, by,
  254. cx, cy,
  255. px, py ) {
  256. var aX, aY, bX, bY;
  257. var cX, cY, apx, apy;
  258. var 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. apx= px -ax; apy= py - ay;
  264. bpx= px - bx; bpy= py - by;
  265. cpx= px - cx; cpy= py - cy;
  266. aCROSSbp = aX*bpy - aY*bpx;
  267. cCROSSap = cX*apy - cY*apx;
  268. bCROSScp = bX*cpy - bY*cpx;
  269. return ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) );
  270. };
  271. var snip = function ( contour, u, v, w, n, verts ) {
  272. var p;
  273. var ax, ay, bx, by;
  274. var cx, cy, px, py;
  275. ax = contour[ verts[ u ] ].x;
  276. ay = contour[ verts[ u ] ].y;
  277. bx = contour[ verts[ v ] ].x;
  278. by = contour[ verts[ v ] ].y;
  279. cx = contour[ verts[ w ] ].x;
  280. cy = contour[ verts[ w ] ].y;
  281. if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false;
  282. for ( p = 0; p < n; p++ ) {
  283. if( (p == u) || (p == v) || (p == w) ) continue;
  284. px = contour[ verts[ p ] ].x
  285. py = contour[ verts[ p ] ].y
  286. if ( insideTriangle( ax, ay, bx, by, cx, cy, px, py ) ) return false;
  287. }
  288. return true;
  289. };
  290. namespace.Triangulate = process;
  291. namespace.Triangulate.area = area;
  292. return namespace;
  293. })(THREE.FontUtils);
  294. // To use the typeface.js face files, hook up the API
  295. self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
粤ICP备19079148号