TextGeometry.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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. * bevelEnabled: <bool>, // turn on bevel
  19. * bevelThickness: <float>, // how deep into text bevel goes
  20. * bevelSize: <float>, // how far from text outline is bevel
  21. *
  22. * bend: <bool> // bend according to hardcoded curve (generates bendPath)
  23. * bendPath: <curve> // wraps text according to bend Path
  24. * }
  25. *
  26. * It uses techniques used in:
  27. *
  28. * typeface.js and canvastext
  29. * For converting fonts and rendering with javascript
  30. * http://typeface.neocracy.org
  31. *
  32. * Triangulation ported from AS3
  33. * Simple Polygon Triangulation
  34. * http://actionsnippet.com/?p=1462
  35. *
  36. * A Method to triangulate shapes with holes
  37. * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
  38. *
  39. */
  40. THREE.TextGeometry = function ( text, parameters ) {
  41. var textPath = new THREE.TextPath( text, parameters );
  42. var textShapes = textPath.toShapes();
  43. // translate parameters to ExtrudeGeometry API
  44. parameters.amount = parameters.height !== undefined ? parameters.height : 50;
  45. // defaults
  46. if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
  47. if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
  48. if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
  49. if ( parameters.bend ) {
  50. var b = textShapes[ textShapes.length - 1 ].getBoundingBox();
  51. var max = b.maxX;
  52. parameters.bendPath = new THREE.QuadraticBezierCurve( new THREE.Vector2( 0, 0 ),
  53. new THREE.Vector2( max / 2, 120 ),
  54. new THREE.Vector2( max, 0 ) );
  55. }
  56. // // Bend Testings.
  57. //
  58. // var path = new THREE.CurvePath();
  59. //
  60. // path.add(new THREE.CubicBezierCurve(
  61. // new THREE.Vector2(0, 0),
  62. // new THREE.Vector2(10, 100),
  63. // new THREE.Vector2(200, -10),
  64. // new THREE.Vector2(300, 0)
  65. // ));
  66. //
  67. // path.add(new THREE.QuadraticBezierCurve(
  68. // new THREE.Vector2(300, 0),
  69. // new THREE.Vector2(450, -10),
  70. // new THREE.Vector2(500, 100)
  71. // ));
  72. //
  73. // parameters.bendPath = path;
  74. // var path = new THREE.CurvePath();
  75. // path.add(new THREE.LineCurve( 0, 50, 250, 150));
  76. // path.add(new THREE.LineCurve( 250, 150, 400, 50));
  77. // path.add(new THREE.LineCurve( 400, 50, 0, 50));
  78. // parameters.bendPath = path;
  79. // var path = new THREE.ArcCurve(0, 0, 200, Math.PI * 0, Math.PI * 2, true);
  80. // parameters.bendPath = path;
  81. // var path = new THREE.SplineCurve([
  82. // new THREE.Vector2(0, 0),
  83. // new THREE.Vector2(100, 40),
  84. // new THREE.Vector2(200, 0),
  85. // new THREE.Vector2(400, 20)
  86. // ]);
  87. //
  88. // console.log(path);
  89. //
  90. // path = new THREE.LineCurve( new THREE.Vector2(0, 0), new THREE.Vector2(400, 100));
  91. // var bend = new THREE.Path();
  92. // bend.moveTo(0,0);
  93. // bend.quadraticCurveTo( 500, 100, 1000, 0 );
  94. //
  95. // parameters.bendPath = bend;
  96. THREE.ExtrudeGeometry.call( this, textShapes, parameters );
  97. };
  98. THREE.TextGeometry.prototype = new THREE.ExtrudeGeometry();
  99. THREE.TextGeometry.prototype.constructor = THREE.TextGeometry;
  100. /*
  101. // TextGeometry wrapper
  102. var text3d = new TextGeometry( text, options );
  103. // Complete manner
  104. var textPath = new TextPath( text, options );
  105. var textShapes = textPath.toShapes();
  106. var text3d = new ExtrudeGeometry( textShapes, options );
  107. // Factory Method
  108. var textShapes = FontUtils.getTextShapes( text, options );
  109. text3d = new ExtrudeGeometry( textShapes, options );
  110. */
  111. THREE.FontUtils = {
  112. faces : {},
  113. // Just for now. face[weight][style]
  114. face : "helvetiker",
  115. weight: "normal",
  116. style : "normal",
  117. size : 150,
  118. divisions : 10,
  119. getFace : function() {
  120. return this.faces[ this.face ][ this.weight ][ this.style ];
  121. },
  122. getTextShapes: function( text, options ) {
  123. var textPath = new TextPath( text, options );
  124. var textShapes = textPath.toShapes();
  125. return textShapes;
  126. },
  127. loadFace : function( data ) {
  128. var family = data.familyName.toLowerCase();
  129. var ThreeFont = this;
  130. ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
  131. ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
  132. ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
  133. var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
  134. return data;
  135. },
  136. drawText : function( text ) {
  137. var characterPts = [], allPts = [];
  138. // RenderText
  139. var i, p,
  140. face = this.getFace(),
  141. scale = this.size / face.resolution,
  142. offset = 0,
  143. chars = String( text ).split( '' ),
  144. length = chars.length;
  145. var fontPaths = [];
  146. for ( i = 0; i < length; i ++ ) {
  147. var path = new THREE.Path();
  148. var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
  149. offset += ret.offset;
  150. //characterPts.push( ret.points );
  151. //allPts = allPts.concat( ret.points );
  152. fontPaths.push( ret.path );
  153. }
  154. // get the width
  155. var width = offset / 2;
  156. //
  157. // for ( p = 0; p < allPts.length; p++ ) {
  158. //
  159. // allPts[ p ].x -= width;
  160. //
  161. // }
  162. //var extract = this.extractPoints( allPts, characterPts );
  163. //extract.contour = allPts;
  164. //extract.paths = fontPaths;
  165. //extract.offset = width;
  166. return { paths : fontPaths, offset : width };
  167. },
  168. extractGlyphPoints : function( c, face, scale, offset, path ) {
  169. var pts = [];
  170. var i, i2,
  171. outline, action, length,
  172. scaleX, scaleY,
  173. x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
  174. laste,
  175. glyph = face.glyphs[ c ] || face.glyphs[ ctxt.options.fallbackCharacter ];
  176. if ( !glyph ) return;
  177. if ( glyph.o ) {
  178. outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
  179. length = outline.length;
  180. scaleX = scale;
  181. scaleY = scale;
  182. for ( i = 0; i < length; ) {
  183. action = outline[ i ++ ];
  184. //console.log( action );
  185. switch( action ) {
  186. case 'm':
  187. // Move To
  188. x = outline[ i++ ] * scaleX + offset;
  189. y = outline[ i++ ] * scaleY;
  190. pts.push( new THREE.Vector2( x, y ) );
  191. path.moveTo( x, y );
  192. break;
  193. case 'l':
  194. // Line To
  195. x = outline[ i++ ] * scaleX + offset;
  196. y = outline[ i++ ] * scaleY;
  197. pts.push( new THREE.Vector2( x, y ) );
  198. path.lineTo(x,y);
  199. break;
  200. case 'q':
  201. // QuadraticCurveTo
  202. cpx = outline[ i++ ] * scaleX + offset;
  203. cpy = outline[ i++ ] * scaleY;
  204. cpx1 = outline[ i++ ] * scaleX + offset;
  205. cpy1 = outline[ i++ ] * scaleY;
  206. path.quadraticCurveTo(cpx1, cpy1, cpx, cpy);
  207. laste = pts[ pts.length - 1 ];
  208. if ( laste ) {
  209. cpx0 = laste.x;
  210. cpy0 = laste.y;
  211. for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2++ ) {
  212. var t = i2 / divisions;
  213. var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
  214. var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
  215. pts.push( new THREE.Vector2( tx, ty ) );
  216. }
  217. }
  218. break;
  219. case 'b':
  220. // Cubic Bezier Curve
  221. cpx = outline[ i++ ] * scaleX + offset;
  222. cpy = outline[ i++ ] * scaleY;
  223. cpx1 = outline[ i++ ] * scaleX + offset;
  224. cpy1 = outline[ i++ ] * -scaleY;
  225. cpx2 = outline[ i++ ] * scaleX + offset;
  226. cpy2 = outline[ i++ ] * -scaleY;
  227. path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 );
  228. laste = pts[ pts.length - 1 ];
  229. if ( laste ) {
  230. cpx0 = laste.x;
  231. cpy0 = laste.y;
  232. for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2++ ) {
  233. var t = i2 / divisions;
  234. var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
  235. var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
  236. pts.push( new THREE.Vector2( tx, ty ) );
  237. }
  238. }
  239. break;
  240. }
  241. }
  242. }
  243. return { offset: glyph.ha*scale, points:pts, path:path};
  244. }
  245. };
  246. /**
  247. * This code is a quick port of code written in C++ which was submitted to
  248. * flipcode.com by John W. Ratcliff // July 22, 2000
  249. * See original code and more information here:
  250. * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
  251. *
  252. * ported to actionscript by Zevan Rosser
  253. * www.actionsnippet.com
  254. *
  255. * ported to javascript by Joshua Koo
  256. * http://www.lab4games.net/zz85/blog
  257. *
  258. */
  259. ( function( namespace ) {
  260. var EPSILON = 0.0000000001;
  261. // takes in an contour array and returns
  262. var process = function( contour, indices ) {
  263. var n = contour.length;
  264. if ( n < 3 ) return null;
  265. var result = [],
  266. verts = [],
  267. vertIndices = [];
  268. /* we want a counter-clockwise polygon in verts */
  269. var u, v, w;
  270. if ( area( contour ) > 0.0 ) {
  271. for ( v = 0; v < n; v++ ) verts[ v ] = v;
  272. } else {
  273. for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v;
  274. }
  275. var nv = n;
  276. /* remove nv - 2 vertices, creating 1 triangle every time */
  277. var count = 2 * nv; /* error detection */
  278. for( v = nv - 1; nv > 2; ) {
  279. /* if we loop, it is probably a non-simple polygon */
  280. if ( ( count-- ) <= 0 ) {
  281. //** Triangulate: ERROR - probable bad polygon!
  282. //throw ( "Warning, unable to triangulate polygon!" );
  283. //return null;
  284. // Sometimes warning is fine, especially polygons are triangulated in reverse.
  285. console.log( "Warning, unable to triangulate polygon!" );
  286. if ( indices ) return vertIndices;
  287. return result;
  288. }
  289. /* three consecutive vertices in current polygon, <u,v,w> */
  290. u = v; if ( nv <= u ) u = 0; /* previous */
  291. v = u + 1; if ( nv <= v ) v = 0; /* new v */
  292. w = v + 1; if ( nv <= w ) w = 0; /* next */
  293. if ( snip( contour, u, v, w, nv, verts ) ) {
  294. var a, b, c, s, t;
  295. /* true names of the vertices */
  296. a = verts[ u ];
  297. b = verts[ v ];
  298. c = verts[ w ];
  299. /* output Triangle */
  300. /*
  301. result.push( contour[ a ] );
  302. result.push( contour[ b ] );
  303. result.push( contour[ c ] );
  304. */
  305. result.push( [ contour[ a ],
  306. contour[ b ],
  307. contour[ c ] ] );
  308. vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
  309. /* remove v from the remaining polygon */
  310. for( s = v, t = v + 1; t < nv; s++, t++ ) {
  311. verts[ s ] = verts[ t ];
  312. }
  313. nv--;
  314. /* reset error detection counter */
  315. count = 2 * nv;
  316. }
  317. }
  318. if ( indices ) return vertIndices;
  319. return result;
  320. };
  321. // calculate area of the contour polygon
  322. var area = function ( contour ) {
  323. var n = contour.length;
  324. var a = 0.0;
  325. for( var p = n - 1, q = 0; q < n; p = q++ ) {
  326. a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
  327. }
  328. return a * 0.5;
  329. };
  330. // see if p is inside triangle abc
  331. var insideTriangle = function( ax, ay,
  332. bx, by,
  333. cx, cy,
  334. px, py ) {
  335. var aX, aY, bX, bY;
  336. var cX, cY, apx, apy;
  337. var bpx, bpy, cpx, cpy;
  338. var cCROSSap, bCROSScp, aCROSSbp;
  339. aX = cx - bx; aY = cy - by;
  340. bX = ax - cx; bY = ay - cy;
  341. cX = bx - ax; cY = by - ay;
  342. apx= px -ax; apy= py - ay;
  343. bpx= px - bx; bpy= py - by;
  344. cpx= px - cx; cpy= py - cy;
  345. aCROSSbp = aX*bpy - aY*bpx;
  346. cCROSSap = cX*apy - cY*apx;
  347. bCROSScp = bX*cpy - bY*cpx;
  348. return ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) );
  349. };
  350. var snip = function ( contour, u, v, w, n, verts ) {
  351. var p;
  352. var ax, ay, bx, by;
  353. var cx, cy, px, py;
  354. ax = contour[ verts[ u ] ].x;
  355. ay = contour[ verts[ u ] ].y;
  356. bx = contour[ verts[ v ] ].x;
  357. by = contour[ verts[ v ] ].y;
  358. cx = contour[ verts[ w ] ].x;
  359. cy = contour[ verts[ w ] ].y;
  360. if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false;
  361. for ( p = 0; p < n; p++ ) {
  362. if( (p == u) || (p == v) || (p == w) ) continue;
  363. px = contour[ verts[ p ] ].x
  364. py = contour[ verts[ p ] ].y
  365. if ( insideTriangle( ax, ay, bx, by, cx, cy, px, py ) ) return false;
  366. }
  367. return true;
  368. };
  369. namespace.Triangulate = process;
  370. namespace.Triangulate.area = area;
  371. return namespace;
  372. })(THREE.FontUtils);
  373. // To use the typeface.js face files, hook up the API
  374. window._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
粤ICP备19079148号