ExtrudeGeometry.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  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. //console.log(shape);
  32. this.addShape( shape, options );
  33. }
  34. };
  35. THREE.ExtrudeGeometry.prototype = new THREE.Geometry();
  36. THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;
  37. THREE.ExtrudeGeometry.prototype.addShape = function( shape, options ) {
  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. // We should set bevel segments to 0 if bevel is not enabled.
  44. // (also bevelThickness and bevelSize make mess when bevel is not enabled,
  45. // whole shape gets thicker)
  46. if ( !bevelEnabled ) {
  47. bevelSegments = 0;
  48. bevelThickness = 0;
  49. bevelSize = 0;
  50. }
  51. var steps = options.steps !== undefined ? options.steps : 1;
  52. var extrudePath = options.path !== undefined ? options.path : null;
  53. var extrudePts, extrudeByPath = false;
  54. if ( extrudePath ) {
  55. extrudePts = extrudePath.getPoints();
  56. steps = extrudePts.length;
  57. extrudeByPath = true;
  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. //extractAllPoints
  66. var shapePoints = shape.extractAllPoints(); // use shape.extractAllSpacedPoints() for points with equal divisions
  67. var vertices = shapePoints.shape;
  68. var holes = shapePoints.holes;
  69. var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ;
  70. if ( reverse ) {
  71. vertices = vertices.reverse();
  72. // Maybe we should also check if holes are in the opposite direction, just to be safe ...
  73. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  74. ahole = holes[ h ];
  75. if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
  76. holes[ h ] = ahole.reverse();
  77. }
  78. }
  79. reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
  80. }
  81. var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
  82. //var faces = THREE.Shape.Utils.triangulate2( vertices, holes );
  83. //console.log(faces);
  84. ////
  85. /// Handle Vertices
  86. ////
  87. var contour = vertices; // vertices has all points but contour has only points of circumference
  88. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  89. ahole = holes[ h ];
  90. vertices = vertices.concat( ahole );
  91. }
  92. // Find all centroids of shapes and holes
  93. var i, il;
  94. var sum = new THREE.Vector2();
  95. for ( i = 0, il = contour.length; i < il; i ++ ) {
  96. sum.addSelf( contour[ i ] );
  97. }
  98. var contourCentroid = sum.divideScalar( contour.length );
  99. var holesCentroids = [];
  100. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  101. sum = new THREE.Vector2();
  102. ahole = holes[ h ];
  103. for ( i=0, il = ahole.length; i < il; i ++ ) {
  104. sum.addSelf( ahole[ i ] );
  105. }
  106. holesCentroids[ h ] = sum.divideScalar( ahole.length );
  107. }
  108. function scalePt ( pt, centroid, size, expandOutwards /* Boolean */ ) {
  109. var vectorFromCentroid = pt.clone().subSelf( centroid );
  110. var adj = size / vectorFromCentroid.length();
  111. if ( expandOutwards ) {
  112. adj = 1 + adj;
  113. } else {
  114. adj = 1 - adj;
  115. }
  116. return vectorFromCentroid.multiplyScalar( adj ).addSelf( centroid );
  117. }
  118. function scalePt2 ( pt, vec, size ) {
  119. //return vec.clone().multiplyScalar( size ).addSelf( pt );
  120. return pt.clone().addSelf( vec.clone().multiplyScalar( size ) );
  121. }
  122. var b, bs, t, z,
  123. vert, vlen = vertices.length,
  124. face, flen = faces.length,
  125. cont, clen = contour.length;
  126. //------
  127. // Find directions for point movement
  128. //
  129. var RAD_TO_DEGREES = 180 / Math.PI;
  130. function getBevelVec( pt_i, pt_j, pt_k ) {
  131. var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x );
  132. var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x );
  133. if ( anglea > angleb ) {
  134. angleb += Math.PI * 2;
  135. }
  136. // var anglea = Math.atan2(pt_i.y - pt_j.y, pt_i.x - pt_j.x);
  137. // var angleb = Math.atan2(pt_i.y - pt_k.y, pt_i.x - pt_k.x);
  138. // console.log('>?', anglea > angleb);
  139. //
  140. // if ( anglea < angleb) {
  141. // angleb += Math.PI*2;
  142. // }
  143. //x = Math.cos(anglea) + Math.cos(angleb);
  144. //y = Math.sin(anglea) + Math.sin(angleb);
  145. //anglec = Math.atan2(y,x);
  146. anglec = ( anglea + angleb ) / 2;
  147. //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
  148. var x = - Math.cos( anglec );
  149. var y = - Math.sin( anglec );
  150. var vec = new THREE.Vector2( x, y ).normalize();
  151. return vec;
  152. }
  153. var contourMovements = [];
  154. for ( i = 0, il = contour.length, j = il-1, k = i + 1; i < il; i++, j++, k++ ) {
  155. if ( j == il ) j = 0;
  156. if ( k == il ) k = 0;
  157. // (j)---(i)---(k)
  158. // console.log('i,j,k', i, j , k)
  159. var pt_i = contour[ i ];
  160. var pt_j = contour[ j ];
  161. var pt_k = contour[ k ];
  162. contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
  163. }
  164. var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
  165. for ( h = 0, hl = holes.length; h < hl; h++ ) {
  166. ahole = holes[ h ];
  167. oneHoleMovements = [];
  168. for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++ ) {
  169. if ( j == il ) j = 0;
  170. if ( k == il ) k = 0;
  171. // (j)---(i)---(k)
  172. oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
  173. }
  174. holesMovements.push( oneHoleMovements );
  175. verticesMovements = verticesMovements.concat( oneHoleMovements );
  176. }
  177. // Loop bevelSegments, 1 for the front, 1 for the back
  178. for ( b = 0; b < bevelSegments; b ++ ) {
  179. //for ( b = bevelSegments; b > 0; b -- ) {
  180. t = b / bevelSegments;
  181. z = bevelThickness * ( 1 - t );
  182. //z = bevelThickness * t;
  183. bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
  184. //bs = bevelSize * t ; // linear
  185. // contract shape
  186. for ( i = 0, il = contour.length; i < il; i ++ ) {
  187. vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  188. //vert = scalePt( contour[ i ], contourCentroid, bs, false );
  189. v( vert.x, vert.y, - z );
  190. }
  191. // expand holes
  192. for ( h = 0, hl = holes.length; h < hl; h++ ) {
  193. ahole = holes[ h ];
  194. oneHoleMovements = holesMovements[ h ];
  195. for ( i = 0, il = ahole.length; i < il; i++ ) {
  196. vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  197. //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
  198. v( vert.x, vert.y, -z );
  199. }
  200. }
  201. }
  202. bs = bevelSize;
  203. // Back facing vertices
  204. for ( i = 0; i < vlen; i ++ ) {
  205. //vert = vertices[ i ];
  206. vert = scalePt2( vertices[ i ], verticesMovements[ i ], bs );
  207. if ( !extrudeByPath ) {
  208. v( vert.x, vert.y, 0 );
  209. } else {
  210. v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
  211. }
  212. }
  213. // Add stepped vertices...
  214. // Including front facing vertices
  215. var s;
  216. for ( s = 1; s <= steps; s ++ ) {
  217. for ( i = 0; i < vlen; i ++ ) {
  218. //vert = vertices[ i ];
  219. vert = scalePt2(vertices[ i ], verticesMovements[i], bs);
  220. if ( !extrudeByPath ) {
  221. v( vert.x, vert.y, amount / steps * s );
  222. } else {
  223. v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
  224. }
  225. }
  226. }
  227. // Add bevel segments planes
  228. //for ( b = 1; b <= bevelSegments; b ++ ) {
  229. for ( b = bevelSegments - 1; b >= 0; b -- ) {
  230. t = b / bevelSegments;
  231. z = bevelThickness * ( 1 - t );
  232. //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
  233. bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
  234. // contract shape
  235. for ( i = 0, il = contour.length; i < il; i ++ ) {
  236. vert = scalePt2(contour[i], contourMovements[i], bs);
  237. //vert = scalePt( contour[ i ], contourCentroid, bs, false );
  238. v( vert.x, vert.y, amount + z);
  239. }
  240. // expand holes
  241. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  242. ahole = holes[ h ];
  243. oneHoleMovements = holesMovements[ h ];
  244. for ( i = 0, il = ahole.length; i < il; i++ ) {
  245. //vert = scalePt( ahole[ i ], holesCentroids[h], bs, true );
  246. vert = scalePt2(ahole[i], oneHoleMovements[i], bs);
  247. if ( !extrudeByPath ) {
  248. v( vert.x, vert.y, amount + z );
  249. } else {
  250. v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x +z );
  251. }
  252. }
  253. }
  254. }
  255. ////
  256. /// Handle Faces
  257. ////
  258. // not used anywhere
  259. // var layers = ( steps + bevelSegments * 2 ) * vlen;
  260. // Bottom faces
  261. if ( bevelEnabled ) {
  262. var layer = 0 ; // steps + 1
  263. var offset = vlen * layer;
  264. for ( i = 0; i < flen; i ++ ) {
  265. face = faces[ i ];
  266. f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset );
  267. }
  268. layer = steps + bevelSegments * 2;
  269. offset = vlen * layer;
  270. // Top faces
  271. for ( i = 0; i < flen; i ++ ) {
  272. face = faces[ i ];
  273. f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
  274. }
  275. } else {
  276. for ( i = 0; i < flen; i++ ) {
  277. face = faces[ i ];
  278. f3( face[ 2 ], face[ 1 ], face[ 0 ] );
  279. }
  280. // Top faces
  281. for ( i = 0; i < flen; i ++ ) {
  282. face = faces[ i ];
  283. f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
  284. }
  285. }
  286. var tmpPt;
  287. var j, k, l, m;
  288. var layeroffset = 0;
  289. // Sides faces
  290. sidewalls( contour );
  291. layeroffset += contour.length;
  292. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  293. ahole = holes[ h ];
  294. sidewalls( ahole );
  295. //, true
  296. layeroffset += ahole.length;
  297. }
  298. // Create faces for the z-sides of the shape
  299. function sidewalls( contour ) {
  300. i = contour.length;
  301. while ( --i >= 0 ) {
  302. tmpPt = contour[ i ];
  303. j = i;
  304. k = i - 1;
  305. if ( k < 0 ) k = contour.length - 1;
  306. //console.log('b', i,j, i-1, k,vertices.length);
  307. var s = 0;
  308. for ( s = 0; s < ( steps + bevelSegments * 2 ); s ++ ) {
  309. var slen1 = vlen * s;
  310. var slen2 = vlen * ( s + 1 );
  311. f4( layeroffset + j + slen1, layeroffset + k + slen1, layeroffset + k + slen2, layeroffset + j + slen2 );
  312. }
  313. }
  314. }
  315. // UVs to be added
  316. this.computeCentroids();
  317. this.computeFaceNormals();
  318. //this.computeVertexNormals();
  319. function v( x, y, z ) {
  320. scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) );
  321. }
  322. function f3( a, b, c ) {
  323. a += shapesOffset;
  324. b += shapesOffset;
  325. c += shapesOffset;
  326. scope.faces.push( new THREE.Face3( a, b, c ) );
  327. }
  328. function f4( a, b, c, d ) {
  329. a += shapesOffset;
  330. b += shapesOffset;
  331. c += shapesOffset;
  332. d += shapesOffset;
  333. scope.faces.push( new THREE.Face4( a, b, c, d ) );
  334. }
  335. };