ExtrudeGeometry.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. /**
  2. * @author zz85 / http://www.lab4games.net/zz85/blog
  3. *
  4. * Creates extruded geometry from a path shape.
  5. *
  6. * parameters = {
  7. * size: <float>, // size of the text
  8. * height: <float>, // thickness to extrude text
  9. * curveSegments: <int>, // number of points on the curves
  10. *
  11. * font: <string>, // font name
  12. * weight: <string>, // font weight (normal, bold)
  13. * style: <string>, // font style (normal, italics)
  14. *
  15. * bevelEnabled: <bool>, // turn on bevel
  16. * bevelThickness: <float>, // how deep into text bevel goes
  17. * bevelSize: <float>, // how far from text outline is bevel
  18. * bevelSegments: <int>, // number of bevel layers
  19. * }
  20. **/
  21. THREE.ExtrudeGeometry = function( shapes, options ) {
  22. if( typeof( shapes ) == "undefined" ) {
  23. shapes = [];
  24. return;
  25. }
  26. THREE.Geometry.call( this );
  27. shapes = shapes instanceof Array ? shapes : [ shapes ];
  28. var s, sl = shapes.length, shape;
  29. for ( s = 0; s < sl; s ++ ) {
  30. shape = shapes[ s ];
  31. this.addShape( shape, options );
  32. }
  33. };
  34. THREE.ExtrudeGeometry.prototype = new THREE.Geometry();
  35. THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;
  36. THREE.ExtrudeGeometry.prototype.addShape = function( shape, options ) {
  37. //var startTime = Date.now();
  38. var amount = options.amount !== undefined ? options.amount : 100;
  39. var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
  40. var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
  41. var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
  42. var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
  43. var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
  44. var steps = options.steps !== undefined ? options.steps : 1;
  45. var extrudePath = options.path !== undefined ? options.path : null;
  46. var extrudePts, extrudeByPath = false;
  47. if ( extrudePath ) {
  48. extrudePts = extrudePath.getPoints( curveSegments );
  49. steps = extrudePts.length;
  50. extrudeByPath = true;
  51. bevelEnabled = false; // bevels not supported for path extrusion
  52. }
  53. // Safeguards if bevels are not enabled
  54. if ( !bevelEnabled ) {
  55. bevelSegments = 0;
  56. bevelThickness = 0;
  57. bevelSize = 0;
  58. }
  59. // TODO, extrude by path's tangents? also via 3d path?
  60. // Variables initalization
  61. var ahole, h, hl; // looping of holes
  62. var scope = this;
  63. var bevelPoints = [];
  64. var shapesOffset = this.vertices.length;
  65. var shapePoints = shape.extractAllPoints( curveSegments ); // use shape.extractAllSpacedPoints( curveSegments ) for points with equal divisions
  66. var vertices = shapePoints.shape;
  67. var holes = shapePoints.holes;
  68. var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ;
  69. if ( reverse ) {
  70. vertices = vertices.reverse();
  71. // Maybe we should also check if holes are in the opposite direction, just to be safe ...
  72. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  73. ahole = holes[ h ];
  74. if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
  75. holes[ h ] = ahole.reverse();
  76. }
  77. }
  78. reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
  79. }
  80. var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
  81. //var faces = THREE.Shape.Utils.triangulate2( vertices, holes );
  82. //console.log(faces);
  83. ////
  84. /// Handle Vertices
  85. ////
  86. var contour = vertices; // vertices has all points but contour has only points of circumference
  87. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  88. ahole = holes[ h ];
  89. vertices = vertices.concat( ahole );
  90. }
  91. var i, il;
  92. // We no longer need centroids
  93. // Find all centroids of shapes and holes
  94. //var sum = new THREE.Vector2();
  95. // for ( i = 0, il = contour.length; i < il; i ++ ) {
  96. //
  97. // sum.addSelf( contour[ i ] );
  98. //
  99. // }
  100. //
  101. // var contourCentroid = sum.divideScalar( contour.length );
  102. //
  103. // var holesCentroids = [];
  104. //
  105. // for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  106. //
  107. // sum = new THREE.Vector2();
  108. // ahole = holes[ h ];
  109. //
  110. // for ( i=0, il = ahole.length; i < il; i ++ ) {
  111. //
  112. // sum.addSelf( ahole[ i ] );
  113. //
  114. // }
  115. //
  116. // holesCentroids[ h ] = sum.divideScalar( ahole.length );
  117. //
  118. // }
  119. //
  120. // function scalePt ( pt, centroid, size, expandOutwards /* Boolean */ ) {
  121. //
  122. // var vectorFromCentroid = pt.clone().subSelf( centroid );
  123. // var adj = size / vectorFromCentroid.length();
  124. //
  125. // if ( expandOutwards ) {
  126. //
  127. // adj = 1 + adj;
  128. //
  129. // } else {
  130. //
  131. // adj = 1 - adj;
  132. //
  133. // }
  134. //
  135. // return vectorFromCentroid.multiplyScalar( adj ).addSelf( centroid );
  136. //
  137. // }
  138. function scalePt2 ( pt, vec, size ) {
  139. if ( !vec ) console.log( "die" );
  140. return vec.clone().multiplyScalar( size ).addSelf( pt );
  141. }
  142. var b, bs, t, z,
  143. vert, vlen = vertices.length,
  144. face, flen = faces.length,
  145. cont, clen = contour.length;
  146. //------
  147. // Find directions for point movement
  148. //
  149. var RAD_TO_DEGREES = 180 / Math.PI;
  150. function getBevelVec( pt_i, pt_j, pt_k ) {
  151. // Algorithm 2
  152. return getBevelVec2( pt_i, pt_j, pt_k );
  153. }
  154. function getBevelVec1( pt_i, pt_j, pt_k ) {
  155. var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x );
  156. var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x );
  157. if ( anglea > angleb ) {
  158. angleb += Math.PI * 2;
  159. }
  160. anglec = ( anglea + angleb ) / 2;
  161. //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
  162. var x = - Math.cos( anglec );
  163. var y = - Math.sin( anglec );
  164. var vec = new THREE.Vector2( x, y ); //.normalize();
  165. return vec;
  166. }
  167. function getBevelVec2( pt_i, pt_j, pt_k ) {
  168. var a = THREE.ExtrudeGeometry.__v1,
  169. b = THREE.ExtrudeGeometry.__v2,
  170. v_hat = THREE.ExtrudeGeometry.__v3,
  171. w_hat = THREE.ExtrudeGeometry.__v4,
  172. p = THREE.ExtrudeGeometry.__v5,
  173. q = THREE.ExtrudeGeometry.__v6,
  174. v, w,
  175. v_dot_w_hat, q_sub_p_dot_w_hat,
  176. s, intersection;
  177. // good reading for line-line intersection
  178. // http://sputsoft.com/blog/2010/03/line-line-intersection.html
  179. // define a as vector j->i
  180. // define b as vectot k->i
  181. a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y );
  182. b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y );
  183. // get unit vectors
  184. v = a.normalize();
  185. w = b.normalize();
  186. // normals from pt i
  187. v_hat.set( -v.y, v.x );
  188. w_hat.set( w.y, -w.x );
  189. // pts from i
  190. p.copy( pt_i ).addSelf( v_hat );
  191. q.copy( pt_i ).addSelf( w_hat );
  192. if ( p.equals( q ) ) {
  193. //console.log("Warning: lines are straight");
  194. return w_hat.clone();
  195. }
  196. // Points from j, k. helps prevents points cross overover most of the time
  197. p.copy( pt_j ).addSelf( v_hat );
  198. q.copy( pt_k ).addSelf( w_hat );
  199. v_dot_w_hat = v.dot( w_hat );
  200. q_sub_p_dot_w_hat = q.subSelf( p ).dot( w_hat );
  201. // We should not reach these conditions
  202. if ( v_dot_w_hat == 0 ) {
  203. console.log( "Either infinite or no solutions!" );
  204. if ( q_sub_p_dot_w_hat == 0 ) {
  205. console.log( "Its finite solutions." );
  206. } else {
  207. console.log( "Too bad, no solutions." );
  208. }
  209. }
  210. s = q_sub_p_dot_w_hat / v_dot_w_hat;
  211. if ( s < 0 ) {
  212. // in case of emergecy, revert to algorithm 1.
  213. // console.log("opps");
  214. return getBevelVec1( pt_i, pt_j, pt_k );
  215. }
  216. intersection = v.multiplyScalar( s ).addSelf( p );
  217. return intersection.subSelf( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly
  218. }
  219. var contourMovements = [];
  220. for ( i = 0, il = contour.length, j = il-1, k = i + 1; i < il; i++, j++, k++ ) {
  221. if ( j == il ) j = 0;
  222. if ( k == il ) k = 0;
  223. // (j)---(i)---(k)
  224. // console.log('i,j,k', i, j , k)
  225. var pt_i = contour[ i ];
  226. var pt_j = contour[ j ];
  227. var pt_k = contour[ k ];
  228. contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
  229. }
  230. var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
  231. for ( h = 0, hl = holes.length; h < hl; h++ ) {
  232. ahole = holes[ h ];
  233. oneHoleMovements = [];
  234. for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++ ) {
  235. if ( j == il ) j = 0;
  236. if ( k == il ) k = 0;
  237. // (j)---(i)---(k)
  238. oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
  239. }
  240. holesMovements.push( oneHoleMovements );
  241. verticesMovements = verticesMovements.concat( oneHoleMovements );
  242. }
  243. // Loop bevelSegments, 1 for the front, 1 for the back
  244. for ( b = 0; b < bevelSegments; b ++ ) {
  245. //for ( b = bevelSegments; b > 0; b -- ) {
  246. t = b / bevelSegments;
  247. z = bevelThickness * ( 1 - t );
  248. //z = bevelThickness * t;
  249. bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
  250. //bs = bevelSize * t ; // linear
  251. // contract shape
  252. for ( i = 0, il = contour.length; i < il; i ++ ) {
  253. vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  254. //vert = scalePt( contour[ i ], contourCentroid, bs, false );
  255. v( vert.x, vert.y, - z );
  256. }
  257. // expand holes
  258. for ( h = 0, hl = holes.length; h < hl; h++ ) {
  259. ahole = holes[ h ];
  260. oneHoleMovements = holesMovements[ h ];
  261. for ( i = 0, il = ahole.length; i < il; i++ ) {
  262. vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  263. //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
  264. v( vert.x, vert.y, -z );
  265. }
  266. }
  267. }
  268. bs = bevelSize;
  269. // Back facing vertices
  270. for ( i = 0; i < vlen; i ++ ) {
  271. vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  272. if ( !extrudeByPath ) {
  273. v( vert.x, vert.y, 0 );
  274. } else {
  275. v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
  276. }
  277. }
  278. // Add stepped vertices...
  279. // Including front facing vertices
  280. var s;
  281. for ( s = 1; s <= steps; s ++ ) {
  282. for ( i = 0; i < vlen; i ++ ) {
  283. vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  284. if ( !extrudeByPath ) {
  285. v( vert.x, vert.y, amount / steps * s );
  286. } else {
  287. v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
  288. }
  289. }
  290. }
  291. // Add bevel segments planes
  292. //for ( b = 1; b <= bevelSegments; b ++ ) {
  293. for ( b = bevelSegments - 1; b >= 0; b -- ) {
  294. t = b / bevelSegments;
  295. z = bevelThickness * ( 1 - t );
  296. //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
  297. bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
  298. // contract shape
  299. for ( i = 0, il = contour.length; i < il; i ++ ) {
  300. vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  301. //vert = scalePt( contour[ i ], contourCentroid, bs, false );
  302. v( vert.x, vert.y, amount + z );
  303. }
  304. // expand holes
  305. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  306. ahole = holes[ h ];
  307. oneHoleMovements = holesMovements[ h ];
  308. for ( i = 0, il = ahole.length; i < il; i++ ) {
  309. //vert = scalePt( ahole[ i ], holesCentroids[h], bs, true );
  310. vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  311. if ( !extrudeByPath ) {
  312. v( vert.x, vert.y, amount + z );
  313. } else {
  314. v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
  315. }
  316. }
  317. }
  318. }
  319. ////
  320. /// Handle Faces
  321. ////
  322. // not used anywhere
  323. // var layers = ( steps + bevelSegments * 2 ) * vlen;
  324. // Bottom faces
  325. if ( bevelEnabled ) {
  326. var layer = 0 ; // steps + 1
  327. var offset = vlen * layer;
  328. for ( i = 0; i < flen; i ++ ) {
  329. face = faces[ i ];
  330. f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset );
  331. }
  332. layer = steps + bevelSegments * 2;
  333. offset = vlen * layer;
  334. // Top faces
  335. for ( i = 0; i < flen; i ++ ) {
  336. face = faces[ i ];
  337. f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
  338. }
  339. } else {
  340. for ( i = 0; i < flen; i++ ) {
  341. face = faces[ i ];
  342. f3( face[ 2 ], face[ 1 ], face[ 0 ] );
  343. }
  344. // Top faces
  345. for ( i = 0; i < flen; i ++ ) {
  346. face = faces[ i ];
  347. f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
  348. }
  349. }
  350. var tmpPt;
  351. var j, k, l, m;
  352. var layeroffset = 0;
  353. // Sides faces
  354. sidewalls( contour );
  355. layeroffset += contour.length;
  356. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  357. ahole = holes[ h ];
  358. sidewalls( ahole );
  359. //, true
  360. layeroffset += ahole.length;
  361. }
  362. // Create faces for the z-sides of the shape
  363. function sidewalls( contour ) {
  364. i = contour.length;
  365. while ( --i >= 0 ) {
  366. tmpPt = contour[ i ];
  367. j = i;
  368. k = i - 1;
  369. if ( k < 0 ) k = contour.length - 1;
  370. //console.log('b', i,j, i-1, k,vertices.length);
  371. var s = 0;
  372. for ( s = 0; s < ( steps + bevelSegments * 2 ); s ++ ) {
  373. var slen1 = vlen * s;
  374. var slen2 = vlen * ( s + 1 );
  375. f4( layeroffset + j + slen1, layeroffset + k + slen1, layeroffset + k + slen2, layeroffset + j + slen2 );
  376. }
  377. }
  378. }
  379. // UVs to be added
  380. // How can we create UVs on this?
  381. this.computeCentroids();
  382. this.computeFaceNormals();
  383. //this.computeVertexNormals();
  384. //console.log( "took", ( Date.now() - startTime ) );
  385. function v( x, y, z ) {
  386. scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) );
  387. }
  388. function f3( a, b, c ) {
  389. a += shapesOffset;
  390. b += shapesOffset;
  391. c += shapesOffset;
  392. scope.faces.push( new THREE.Face3( a, b, c ) );
  393. }
  394. function f4( a, b, c, d ) {
  395. a += shapesOffset;
  396. b += shapesOffset;
  397. c += shapesOffset;
  398. d += shapesOffset;
  399. scope.faces.push( new THREE.Face4( a, b, c, d ) );
  400. }
  401. };
  402. THREE.ExtrudeGeometry.__v1 = new THREE.Vector2();
  403. THREE.ExtrudeGeometry.__v2 = new THREE.Vector2();
  404. THREE.ExtrudeGeometry.__v3 = new THREE.Vector2();
  405. THREE.ExtrudeGeometry.__v4 = new THREE.Vector2();
  406. THREE.ExtrudeGeometry.__v5 = new THREE.Vector2();
  407. THREE.ExtrudeGeometry.__v6 = new THREE.Vector2();