ExtrudeGeometry.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. import { Geometry } from '../core/Geometry.js';
  2. import { BufferGeometry } from '../core/BufferGeometry.js';
  3. import { Float32BufferAttribute } from '../core/BufferAttribute.js';
  4. import { Vector2 } from '../math/Vector2.js';
  5. import { Vector3 } from '../math/Vector3.js';
  6. import { ShapeUtils } from '../extras/ShapeUtils.js';
  7. /**
  8. * Creates extruded geometry from a path shape.
  9. *
  10. * parameters = {
  11. *
  12. * curveSegments: <int>, // number of points on the curves
  13. * steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too
  14. * depth: <float>, // Depth to extrude the shape
  15. *
  16. * bevelEnabled: <bool>, // turn on bevel
  17. * bevelThickness: <float>, // how deep into the original shape bevel goes
  18. * bevelSize: <float>, // how far from shape outline (including bevelOffset) is bevel
  19. * bevelOffset: <float>, // how far from shape outline does bevel start
  20. * bevelSegments: <int>, // number of bevel layers
  21. *
  22. * extrudePath: <THREE.Curve> // curve to extrude shape along
  23. *
  24. * UVGenerator: <Object> // object that provides UV generator functions
  25. *
  26. * }
  27. */
  28. // ExtrudeGeometry
  29. class ExtrudeGeometry extends Geometry {
  30. constructor( shapes, options ) {
  31. super();
  32. this.type = 'ExtrudeGeometry';
  33. this.parameters = {
  34. shapes: shapes,
  35. options: options
  36. };
  37. this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) );
  38. this.mergeVertices();
  39. }
  40. toJSON() {
  41. const data = super.toJSON();
  42. const shapes = this.parameters.shapes;
  43. const options = this.parameters.options;
  44. return toJSON( shapes, options, data );
  45. }
  46. }
  47. // ExtrudeBufferGeometry
  48. class ExtrudeBufferGeometry extends BufferGeometry {
  49. constructor( shapes, options ) {
  50. super();
  51. this.type = 'ExtrudeBufferGeometry';
  52. this.parameters = {
  53. shapes: shapes,
  54. options: options
  55. };
  56. shapes = Array.isArray( shapes ) ? shapes : [ shapes ];
  57. const scope = this;
  58. const verticesArray = [];
  59. const uvArray = [];
  60. for ( let i = 0, l = shapes.length; i < l; i ++ ) {
  61. const shape = shapes[ i ];
  62. addShape( shape );
  63. }
  64. // build geometry
  65. this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) );
  66. this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) );
  67. this.computeVertexNormals();
  68. // functions
  69. function addShape( shape ) {
  70. const placeholder = [];
  71. // options
  72. const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
  73. const steps = options.steps !== undefined ? options.steps : 1;
  74. let depth = options.depth !== undefined ? options.depth : 100;
  75. let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true;
  76. let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6;
  77. let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2;
  78. let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0;
  79. let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
  80. const extrudePath = options.extrudePath;
  81. const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator;
  82. // deprecated options
  83. if ( options.amount !== undefined ) {
  84. console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' );
  85. depth = options.amount;
  86. }
  87. //
  88. let extrudePts, extrudeByPath = false;
  89. let splineTube, binormal, normal, position2;
  90. if ( extrudePath ) {
  91. extrudePts = extrudePath.getSpacedPoints( steps );
  92. extrudeByPath = true;
  93. bevelEnabled = false; // bevels not supported for path extrusion
  94. // SETUP TNB variables
  95. // TODO1 - have a .isClosed in spline?
  96. splineTube = extrudePath.computeFrenetFrames( steps, false );
  97. // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
  98. binormal = new Vector3();
  99. normal = new Vector3();
  100. position2 = new Vector3();
  101. }
  102. // Safeguards if bevels are not enabled
  103. if ( ! bevelEnabled ) {
  104. bevelSegments = 0;
  105. bevelThickness = 0;
  106. bevelSize = 0;
  107. bevelOffset = 0;
  108. }
  109. // Variables initialization
  110. const shapePoints = shape.extractPoints( curveSegments );
  111. let vertices = shapePoints.shape;
  112. const holes = shapePoints.holes;
  113. const reverse = ! ShapeUtils.isClockWise( vertices );
  114. if ( reverse ) {
  115. vertices = vertices.reverse();
  116. // Maybe we should also check if holes are in the opposite direction, just to be safe ...
  117. for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
  118. const ahole = holes[ h ];
  119. if ( ShapeUtils.isClockWise( ahole ) ) {
  120. holes[ h ] = ahole.reverse();
  121. }
  122. }
  123. }
  124. const faces = ShapeUtils.triangulateShape( vertices, holes );
  125. /* Vertices */
  126. const contour = vertices; // vertices has all points but contour has only points of circumference
  127. for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
  128. const ahole = holes[ h ];
  129. vertices = vertices.concat( ahole );
  130. }
  131. function scalePt2( pt, vec, size ) {
  132. if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" );
  133. return vec.clone().multiplyScalar( size ).add( pt );
  134. }
  135. const vlen = vertices.length, flen = faces.length;
  136. // Find directions for point movement
  137. function getBevelVec( inPt, inPrev, inNext ) {
  138. // computes for inPt the corresponding point inPt' on a new contour
  139. // shifted by 1 unit (length of normalized vector) to the left
  140. // if we walk along contour clockwise, this new contour is outside the old one
  141. //
  142. // inPt' is the intersection of the two lines parallel to the two
  143. // adjacent edges of inPt at a distance of 1 unit on the left side.
  144. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt
  145. // good reading for geometry algorithms (here: line-line intersection)
  146. // http://geomalgorithms.com/a05-_intersect-1.html
  147. const v_prev_x = inPt.x - inPrev.x,
  148. v_prev_y = inPt.y - inPrev.y;
  149. const v_next_x = inNext.x - inPt.x,
  150. v_next_y = inNext.y - inPt.y;
  151. const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
  152. // check for collinear edges
  153. const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
  154. if ( Math.abs( collinear0 ) > Number.EPSILON ) {
  155. // not collinear
  156. // length of vectors for normalizing
  157. const v_prev_len = Math.sqrt( v_prev_lensq );
  158. const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
  159. // shift adjacent points by unit vectors to the left
  160. const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
  161. const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
  162. const ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
  163. const ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
  164. // scaling factor for v_prev to intersection point
  165. const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
  166. ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /
  167. ( v_prev_x * v_next_y - v_prev_y * v_next_x );
  168. // vector from inPt to intersection point
  169. v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
  170. v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
  171. // Don't normalize!, otherwise sharp corners become ugly
  172. // but prevent crazy spikes
  173. const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );
  174. if ( v_trans_lensq <= 2 ) {
  175. return new Vector2( v_trans_x, v_trans_y );
  176. } else {
  177. shrink_by = Math.sqrt( v_trans_lensq / 2 );
  178. }
  179. } else {
  180. // handle special case of collinear edges
  181. let direction_eq = false; // assumes: opposite
  182. if ( v_prev_x > Number.EPSILON ) {
  183. if ( v_next_x > Number.EPSILON ) {
  184. direction_eq = true;
  185. }
  186. } else {
  187. if ( v_prev_x < - Number.EPSILON ) {
  188. if ( v_next_x < - Number.EPSILON ) {
  189. direction_eq = true;
  190. }
  191. } else {
  192. if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {
  193. direction_eq = true;
  194. }
  195. }
  196. }
  197. if ( direction_eq ) {
  198. // console.log("Warning: lines are a straight sequence");
  199. v_trans_x = - v_prev_y;
  200. v_trans_y = v_prev_x;
  201. shrink_by = Math.sqrt( v_prev_lensq );
  202. } else {
  203. // console.log("Warning: lines are a straight spike");
  204. v_trans_x = v_prev_x;
  205. v_trans_y = v_prev_y;
  206. shrink_by = Math.sqrt( v_prev_lensq / 2 );
  207. }
  208. }
  209. return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
  210. }
  211. const contourMovements = [];
  212. for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
  213. if ( j === il ) j = 0;
  214. if ( k === il ) k = 0;
  215. // (j)---(i)---(k)
  216. // console.log('i,j,k', i, j , k)
  217. contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
  218. }
  219. const holesMovements = [];
  220. let oneHoleMovements, verticesMovements = contourMovements.concat();
  221. for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
  222. const ahole = holes[ h ];
  223. oneHoleMovements = [];
  224. for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
  225. if ( j === il ) j = 0;
  226. if ( k === il ) k = 0;
  227. // (j)---(i)---(k)
  228. oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
  229. }
  230. holesMovements.push( oneHoleMovements );
  231. verticesMovements = verticesMovements.concat( oneHoleMovements );
  232. }
  233. // Loop bevelSegments, 1 for the front, 1 for the back
  234. for ( let b = 0; b < bevelSegments; b ++ ) {
  235. //for ( b = bevelSegments; b > 0; b -- ) {
  236. const t = b / bevelSegments;
  237. const z = bevelThickness * Math.cos( t * Math.PI / 2 );
  238. const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset;
  239. // contract shape
  240. for ( let i = 0, il = contour.length; i < il; i ++ ) {
  241. const vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  242. v( vert.x, vert.y, - z );
  243. }
  244. // expand holes
  245. for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
  246. const ahole = holes[ h ];
  247. oneHoleMovements = holesMovements[ h ];
  248. for ( let i = 0, il = ahole.length; i < il; i ++ ) {
  249. const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  250. v( vert.x, vert.y, - z );
  251. }
  252. }
  253. }
  254. const bs = bevelSize + bevelOffset;
  255. // Back facing vertices
  256. for ( let i = 0; i < vlen; i ++ ) {
  257. const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  258. if ( ! extrudeByPath ) {
  259. v( vert.x, vert.y, 0 );
  260. } else {
  261. // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
  262. normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );
  263. binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );
  264. position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );
  265. v( position2.x, position2.y, position2.z );
  266. }
  267. }
  268. // Add stepped vertices...
  269. // Including front facing vertices
  270. for ( let s = 1; s <= steps; s ++ ) {
  271. for ( let i = 0; i < vlen; i ++ ) {
  272. const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  273. if ( ! extrudeByPath ) {
  274. v( vert.x, vert.y, depth / steps * s );
  275. } else {
  276. // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
  277. normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );
  278. binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );
  279. position2.copy( extrudePts[ s ] ).add( normal ).add( binormal );
  280. v( position2.x, position2.y, position2.z );
  281. }
  282. }
  283. }
  284. // Add bevel segments planes
  285. //for ( b = 1; b <= bevelSegments; b ++ ) {
  286. for ( let b = bevelSegments - 1; b >= 0; b -- ) {
  287. const t = b / bevelSegments;
  288. const z = bevelThickness * Math.cos( t * Math.PI / 2 );
  289. const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset;
  290. // contract shape
  291. for ( let i = 0, il = contour.length; i < il; i ++ ) {
  292. const vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  293. v( vert.x, vert.y, depth + z );
  294. }
  295. // expand holes
  296. for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
  297. const ahole = holes[ h ];
  298. oneHoleMovements = holesMovements[ h ];
  299. for ( let i = 0, il = ahole.length; i < il; i ++ ) {
  300. const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  301. if ( ! extrudeByPath ) {
  302. v( vert.x, vert.y, depth + z );
  303. } else {
  304. v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
  305. }
  306. }
  307. }
  308. }
  309. /* Faces */
  310. // Top and bottom faces
  311. buildLidFaces();
  312. // Sides faces
  313. buildSideFaces();
  314. ///// Internal functions
  315. function buildLidFaces() {
  316. const start = verticesArray.length / 3;
  317. if ( bevelEnabled ) {
  318. let layer = 0; // steps + 1
  319. let offset = vlen * layer;
  320. // Bottom faces
  321. for ( let i = 0; i < flen; i ++ ) {
  322. const face = faces[ i ];
  323. f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );
  324. }
  325. layer = steps + bevelSegments * 2;
  326. offset = vlen * layer;
  327. // Top faces
  328. for ( let i = 0; i < flen; i ++ ) {
  329. const face = faces[ i ];
  330. f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
  331. }
  332. } else {
  333. // Bottom faces
  334. for ( let i = 0; i < flen; i ++ ) {
  335. const face = faces[ i ];
  336. f3( face[ 2 ], face[ 1 ], face[ 0 ] );
  337. }
  338. // Top faces
  339. for ( let i = 0; i < flen; i ++ ) {
  340. const face = faces[ i ];
  341. f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
  342. }
  343. }
  344. scope.addGroup( start, verticesArray.length / 3 - start, 0 );
  345. }
  346. // Create faces for the z-sides of the shape
  347. function buildSideFaces() {
  348. const start = verticesArray.length / 3;
  349. let layeroffset = 0;
  350. sidewalls( contour, layeroffset );
  351. layeroffset += contour.length;
  352. for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
  353. const ahole = holes[ h ];
  354. sidewalls( ahole, layeroffset );
  355. //, true
  356. layeroffset += ahole.length;
  357. }
  358. scope.addGroup( start, verticesArray.length / 3 - start, 1 );
  359. }
  360. function sidewalls( contour, layeroffset ) {
  361. let i = contour.length;
  362. while ( -- i >= 0 ) {
  363. const j = i;
  364. let k = i - 1;
  365. if ( k < 0 ) k = contour.length - 1;
  366. //console.log('b', i,j, i-1, k,vertices.length);
  367. for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) {
  368. const slen1 = vlen * s;
  369. const slen2 = vlen * ( s + 1 );
  370. const a = layeroffset + j + slen1,
  371. b = layeroffset + k + slen1,
  372. c = layeroffset + k + slen2,
  373. d = layeroffset + j + slen2;
  374. f4( a, b, c, d );
  375. }
  376. }
  377. }
  378. function v( x, y, z ) {
  379. placeholder.push( x );
  380. placeholder.push( y );
  381. placeholder.push( z );
  382. }
  383. function f3( a, b, c ) {
  384. addVertex( a );
  385. addVertex( b );
  386. addVertex( c );
  387. const nextIndex = verticesArray.length / 3;
  388. const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
  389. addUV( uvs[ 0 ] );
  390. addUV( uvs[ 1 ] );
  391. addUV( uvs[ 2 ] );
  392. }
  393. function f4( a, b, c, d ) {
  394. addVertex( a );
  395. addVertex( b );
  396. addVertex( d );
  397. addVertex( b );
  398. addVertex( c );
  399. addVertex( d );
  400. const nextIndex = verticesArray.length / 3;
  401. const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
  402. addUV( uvs[ 0 ] );
  403. addUV( uvs[ 1 ] );
  404. addUV( uvs[ 3 ] );
  405. addUV( uvs[ 1 ] );
  406. addUV( uvs[ 2 ] );
  407. addUV( uvs[ 3 ] );
  408. }
  409. function addVertex( index ) {
  410. verticesArray.push( placeholder[ index * 3 + 0 ] );
  411. verticesArray.push( placeholder[ index * 3 + 1 ] );
  412. verticesArray.push( placeholder[ index * 3 + 2 ] );
  413. }
  414. function addUV( vector2 ) {
  415. uvArray.push( vector2.x );
  416. uvArray.push( vector2.y );
  417. }
  418. }
  419. }
  420. toJSON() {
  421. const data = BufferGeometry.prototype.toJSON.call( this );
  422. const shapes = this.parameters.shapes;
  423. const options = this.parameters.options;
  424. return toJSON( shapes, options, data );
  425. }
  426. }
  427. const WorldUVGenerator = {
  428. generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) {
  429. const a_x = vertices[ indexA * 3 ];
  430. const a_y = vertices[ indexA * 3 + 1 ];
  431. const b_x = vertices[ indexB * 3 ];
  432. const b_y = vertices[ indexB * 3 + 1 ];
  433. const c_x = vertices[ indexC * 3 ];
  434. const c_y = vertices[ indexC * 3 + 1 ];
  435. return [
  436. new Vector2( a_x, a_y ),
  437. new Vector2( b_x, b_y ),
  438. new Vector2( c_x, c_y )
  439. ];
  440. },
  441. generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) {
  442. const a_x = vertices[ indexA * 3 ];
  443. const a_y = vertices[ indexA * 3 + 1 ];
  444. const a_z = vertices[ indexA * 3 + 2 ];
  445. const b_x = vertices[ indexB * 3 ];
  446. const b_y = vertices[ indexB * 3 + 1 ];
  447. const b_z = vertices[ indexB * 3 + 2 ];
  448. const c_x = vertices[ indexC * 3 ];
  449. const c_y = vertices[ indexC * 3 + 1 ];
  450. const c_z = vertices[ indexC * 3 + 2 ];
  451. const d_x = vertices[ indexD * 3 ];
  452. const d_y = vertices[ indexD * 3 + 1 ];
  453. const d_z = vertices[ indexD * 3 + 2 ];
  454. if ( Math.abs( a_y - b_y ) < 0.01 ) {
  455. return [
  456. new Vector2( a_x, 1 - a_z ),
  457. new Vector2( b_x, 1 - b_z ),
  458. new Vector2( c_x, 1 - c_z ),
  459. new Vector2( d_x, 1 - d_z )
  460. ];
  461. } else {
  462. return [
  463. new Vector2( a_y, 1 - a_z ),
  464. new Vector2( b_y, 1 - b_z ),
  465. new Vector2( c_y, 1 - c_z ),
  466. new Vector2( d_y, 1 - d_z )
  467. ];
  468. }
  469. }
  470. };
  471. function toJSON( shapes, options, data ) {
  472. data.shapes = [];
  473. if ( Array.isArray( shapes ) ) {
  474. for ( let i = 0, l = shapes.length; i < l; i ++ ) {
  475. const shape = shapes[ i ];
  476. data.shapes.push( shape.uuid );
  477. }
  478. } else {
  479. data.shapes.push( shapes.uuid );
  480. }
  481. if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON();
  482. return data;
  483. }
  484. export { ExtrudeGeometry, ExtrudeBufferGeometry };