@jscad/modeling 2.9.6 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +12 -2
  3. package/dist/jscad-modeling.min.js +28 -28
  4. package/package.json +2 -2
  5. package/src/geometries/path2/appendArc.js +6 -5
  6. package/src/geometries/path2/appendArc.test.js +3 -1
  7. package/src/geometries/path2/appendBezier.js +2 -1
  8. package/src/geometries/path2/transform.js +1 -1
  9. package/src/index.d.ts +1 -0
  10. package/src/maths/constants.js +10 -0
  11. package/src/maths/mat4/fromRotation.js +1 -1
  12. package/src/maths/mat4/fromTaitBryanRotation.js +1 -1
  13. package/src/maths/mat4/fromXRotation.js +1 -1
  14. package/src/maths/mat4/fromYRotation.js +1 -1
  15. package/src/maths/mat4/fromZRotation.js +1 -1
  16. package/src/maths/mat4/invert.test.js +5 -2
  17. package/src/maths/mat4/isOnlyTransformScale.js +1 -1
  18. package/src/maths/mat4/isOnlyTransformScale.test.js +3 -1
  19. package/src/maths/rotation.test.js +5 -2
  20. package/src/maths/utils/trigonometry.js +6 -6
  21. package/src/maths/utils/trigonometry.test.js +10 -8
  22. package/src/maths/vec2/fromAngleDegrees.js +1 -1
  23. package/src/maths/vec2/fromAngleRadians.test.js +4 -1
  24. package/src/maths/vec2/normal.js +3 -1
  25. package/src/maths/vec2/rotate.test.js +4 -1
  26. package/src/maths/vec3/rotateX.test.js +4 -1
  27. package/src/maths/vec3/rotateY.test.js +4 -1
  28. package/src/maths/vec3/rotateZ.test.js +4 -1
  29. package/src/operations/expansions/expand.test.js +3 -1
  30. package/src/operations/expansions/expandShell.js +4 -4
  31. package/src/operations/expansions/offsetFromPoints.js +2 -2
  32. package/src/operations/extrusions/extrudeFromSlices.test.js +3 -2
  33. package/src/operations/extrusions/extrudeLinear.test.js +5 -3
  34. package/src/operations/extrusions/extrudeRectangular.js +1 -1
  35. package/src/operations/extrusions/extrudeRectangular.test.js +5 -3
  36. package/src/operations/extrusions/extrudeRotate.js +14 -13
  37. package/src/operations/extrusions/extrudeRotate.test.js +7 -5
  38. package/src/operations/extrusions/slice/calculatePlane.test.js +3 -2
  39. package/src/operations/extrusions/slice/index.js +2 -0
  40. package/src/operations/modifiers/generalize.d.ts +12 -0
  41. package/src/operations/modifiers/generalize.test.js +3 -1
  42. package/src/operations/modifiers/index.d.ts +2 -0
  43. package/src/operations/modifiers/snap.d.ts +6 -0
  44. package/src/operations/modifiers/snap.test.js +5 -3
  45. package/src/operations/transforms/rotate.js +1 -1
  46. package/src/operations/transforms/rotate.test.js +13 -11
  47. package/src/operations/transforms/transform.js +1 -1
  48. package/src/primitives/arc.js +8 -8
  49. package/src/primitives/arc.test.js +9 -8
  50. package/src/primitives/circle.js +4 -2
  51. package/src/primitives/circle.test.js +6 -5
  52. package/src/primitives/cylinderElliptic.js +11 -11
  53. package/src/primitives/cylinderElliptic.test.js +3 -2
  54. package/src/primitives/ellipse.js +10 -10
  55. package/src/primitives/ellipse.test.js +6 -5
  56. package/src/primitives/ellipsoid.js +3 -2
  57. package/src/primitives/roundedCuboid.js +6 -6
  58. package/src/primitives/roundedCylinder.js +3 -3
  59. package/src/primitives/roundedRectangle.js +5 -5
  60. package/src/primitives/star.js +3 -2
  61. package/src/primitives/torus.js +4 -2
  62. package/src/primitives/torus.test.js +5 -3
  63. package/src/primitives/triangle.test.js +2 -1
  64. package/src/utils/degToRad.test.js +5 -5
  65. package/src/utils/radToDeg.test.js +6 -6
  66. package/src/utils/radiusToSegments.js +6 -4
  67. package/src/utils/radiusToSegments.test.js +5 -3
package/CHANGELOG.md CHANGED
@@ -3,6 +3,23 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [2.10.0](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/modeling@2.9.6...@jscad/modeling@2.10.0) (2022-08-21)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **modeling:** added TS definitions for modifiers ([33ae2c4](https://github.com/jscad/OpenJSCAD.org/commit/33ae2c4c07986218464d30d08147d248c50a8a0b))
12
+ * **modeling:** export modifiers for TypeScript ([#1129](https://github.com/jscad/OpenJSCAD.org/issues/1129)) ([a936675](https://github.com/jscad/OpenJSCAD.org/commit/a93667515922ab99bcd4488f59ee0488dcac6ef4))
13
+
14
+
15
+ ### Features
16
+
17
+ * **modeling:** added TAU constant to maths, and refactored code and documentation ([#1128](https://github.com/jscad/OpenJSCAD.org/issues/1128)) ([b8dd700](https://github.com/jscad/OpenJSCAD.org/commit/b8dd700d5641ec27b842dfdf4b6f9403a64084aa))
18
+
19
+
20
+
21
+
22
+
6
23
  ## [2.9.6](https://github.com/jscad/OpenJSCAD.org/compare/@jscad/modeling@2.9.5...@jscad/modeling@2.9.6) (2022-07-17)
7
24
 
8
25
 
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [![License](https://img.shields.io/github/license/jscad/OpenJSCAD.org)](https://github.com/jscad/OpenJSCAD.org/blob/master/LICENSE)
10
10
 
11
11
  [![User Group](https://img.shields.io/badge/maintained%20by-user%20group-blue)](https://openjscad.nodebb.com/)
12
- [![Lerna](https://img.shields.io/badge/maintained%20with-lerna-blue)](https://lernajs.io/)
12
+ [![Lerna](https://img.shields.io/badge/maintained%20with-lerna-blue)](https://lerna.js.org/)
13
13
  [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-blue)](https://standardjs.com)
14
14
 
15
15
  [![Backers](https://img.shields.io/opencollective/backers/openjscad)](https://opencollective.com/openjscad)
@@ -33,10 +33,17 @@ This library contains boolean operations based on Constructive Solid Geometry (C
33
33
 
34
34
  ## Installation
35
35
 
36
+ This package is published and available for download via [NPM](https://www.npmjs.com/org/jscad)
36
37
  ```
37
38
  npm install @jscad/modeling
38
39
  ```
39
40
 
41
+ In addition this package is hosted at [UNPKG](https://unpkg.com/), and can be imported directly into webpages.
42
+
43
+ ```
44
+ <script language="javascript" src="https://unpkg.com/@jscad/modeling" id="MODELING"></script>
45
+ ```
46
+
40
47
  ## Usage
41
48
 
42
49
  As a Node module:
@@ -70,9 +77,12 @@ Small Note: If editing this README, please conform to the [standard-readme](http
70
77
 
71
78
  ## Community
72
79
 
73
- See for more details
80
+ Discuss designs or issues
74
81
  * [JSCAD User Group](https://openjscad.xyz/forum.html)
75
82
 
83
+ Or chat with other users
84
+ * [Discord Community](https://openjscad.xyz/discord.html)
85
+
76
86
  ## Copyrights
77
87
 
78
88
  Some copyrights apply from integration of original libraries.
@@ -137,12 +137,12 @@ const poly3=require("../poly3"),isA=require("./isA"),validate=e=>{if(!isA(e))thr
137
137
  module.exports={geom2:require("./geom2"),geom3:require("./geom3"),path2:require("./path2"),poly2:require("./poly2"),poly3:require("./poly3")};
138
138
 
139
139
  },{"./geom2":22,"./geom3":37,"./path2":58,"./poly2":69,"./poly3":75}],47:[function(require,module,exports){
140
- const vec2=require("../../maths/vec2"),fromPoints=require("./fromPoints"),toPoints=require("./toPoints"),appendArc=(e,t)=>{let{endpoint:r,radius:a,xaxisrotation:o,clockwise:n,large:s,segments:c}=Object.assign({},{radius:[0,0],xaxisrotation:0,clockwise:!1,large:!1,segments:16},e);if(!Array.isArray(r))throw new Error("endpoint must be an array of X and Y values");if(r.length<2)throw new Error("endpoint must contain X and Y values");if(r=vec2.clone(r),!Array.isArray(a))throw new Error("radius must be an array of X and Y values");if(a.length<2)throw new Error("radius must contain X and Y values");if(c<4)throw new Error("segments must be four or more");if(t.isClosed)throw new Error("the given path cannot be closed");const i=toPoints(t);if(i.length<1)throw new Error("the given path must contain one or more points (as the starting point for the arc)");let h=a[0],u=a[1];const l=i[i.length-1];h=Math.round(1e5*h)/1e5,u=Math.round(1e5*u)/1e5,r=vec2.fromValues(Math.round(1e5*r[0])/1e5,Math.round(1e5*r[1])/1e5);const d=!n;let v=[];if(0===h||0===u)v.push(r);else{h=Math.abs(h),u=Math.abs(u);const t=o,a=Math.cos(t),n=Math.sin(t),i=vec2.subtract(vec2.create(),l,r);vec2.scale(i,i,.5);const f=Math.round(1e5*(a*i[0]+n*i[1]))/1e5,m=Math.round(1e5*(-n*i[0]+a*i[1]))/1e5,M=vec2.fromValues(f,m),p=M[0]*M[0]/(h*h)+M[1]*M[1]/(u*u);if(p>1){const e=Math.sqrt(p);h*=e,u*=e,h=Math.round(1e5*h)/1e5,u=Math.round(1e5*u)/1e5}let w=Math.sqrt((h*h*u*u-h*h*M[1]*M[1]-u*u*M[0]*M[0])/(h*h*M[1]*M[1]+u*u*M[0]*M[0]));d===s&&(w=-w);const g=vec2.fromValues(h*M[1]/u,-u*M[0]/h);vec2.scale(g,g,w);let P=vec2.fromValues(a*g[0]-n*g[1],n*g[0]+a*g[1]);P=vec2.add(P,P,vec2.scale(vec2.create(),vec2.add(vec2.create(),l,r),.5));const b=vec2.fromValues((M[0]-g[0])/h,(M[1]-g[1])/u),E=vec2.fromValues((-M[0]-g[0])/h,(-M[1]-g[1])/u),V=vec2.angleRadians(b);let y=vec2.angleRadians(E)-V;y%=2*Math.PI,!d&&y>0?y-=2*Math.PI:d&&y<0&&(y+=2*Math.PI);let A=Math.ceil(Math.abs(y)/(2*Math.PI)*c)+1;A<1&&(A=1);for(let e=1;e<A;e++){const t=V+e/A*y,r=Math.cos(t),o=Math.sin(t),s=vec2.fromValues(a*h*r-n*u*o,n*h*r+a*u*o);vec2.add(s,s,P),v.push(s)}A&&v.push(e.endpoint)}return v=i.concat(v),fromPoints({},v)};module.exports=appendArc;
140
+ const{TAU:TAU}=require("../../maths/constants"),vec2=require("../../maths/vec2"),fromPoints=require("./fromPoints"),toPoints=require("./toPoints"),appendArc=(e,t)=>{let{endpoint:r,radius:a,xaxisrotation:o,clockwise:n,large:s,segments:c}=Object.assign({},{radius:[0,0],xaxisrotation:0,clockwise:!1,large:!1,segments:16},e);if(!Array.isArray(r))throw new Error("endpoint must be an array of X and Y values");if(r.length<2)throw new Error("endpoint must contain X and Y values");if(r=vec2.clone(r),!Array.isArray(a))throw new Error("radius must be an array of X and Y values");if(a.length<2)throw new Error("radius must contain X and Y values");if(c<4)throw new Error("segments must be four or more");if(t.isClosed)throw new Error("the given path cannot be closed");const i=toPoints(t);if(i.length<1)throw new Error("the given path must contain one or more points (as the starting point for the arc)");let h=a[0],u=a[1];const l=i[i.length-1];h=Math.round(1e5*h)/1e5,u=Math.round(1e5*u)/1e5,r=vec2.fromValues(Math.round(1e5*r[0])/1e5,Math.round(1e5*r[1])/1e5);const d=!n;let v=[];if(0===h||0===u)v.push(r);else{h=Math.abs(h),u=Math.abs(u);const t=o,a=Math.cos(t),n=Math.sin(t),i=vec2.subtract(vec2.create(),l,r);vec2.scale(i,i,.5);const f=Math.round(1e5*(a*i[0]+n*i[1]))/1e5,m=Math.round(1e5*(-n*i[0]+a*i[1]))/1e5,M=vec2.fromValues(f,m),p=M[0]*M[0]/(h*h)+M[1]*M[1]/(u*u);if(p>1){const e=Math.sqrt(p);h*=e,u*=e,h=Math.round(1e5*h)/1e5,u=Math.round(1e5*u)/1e5}let w=Math.sqrt((h*h*u*u-h*h*M[1]*M[1]-u*u*M[0]*M[0])/(h*h*M[1]*M[1]+u*u*M[0]*M[0]));d===s&&(w=-w);const g=vec2.fromValues(h*M[1]/u,-u*M[0]/h);vec2.scale(g,g,w);let A=vec2.fromValues(a*g[0]-n*g[1],n*g[0]+a*g[1]);A=vec2.add(A,A,vec2.scale(vec2.create(),vec2.add(vec2.create(),l,r),.5));const b=vec2.fromValues((M[0]-g[0])/h,(M[1]-g[1])/u),E=vec2.fromValues((-M[0]-g[0])/h,(-M[1]-g[1])/u),V=vec2.angleRadians(b);let q=vec2.angleRadians(E)-V;q%=TAU,!d&&q>0?q-=TAU:d&&q<0&&(q+=TAU);let y=Math.ceil(Math.abs(q)/TAU*c)+1;y<1&&(y=1);for(let e=1;e<y;e++){const t=V+e/y*q,r=Math.cos(t),o=Math.sin(t),s=vec2.fromValues(a*h*r-n*u*o,n*h*r+a*u*o);vec2.add(s,s,A),v.push(s)}y&&v.push(e.endpoint)}return v=i.concat(v),fromPoints({},v)};module.exports=appendArc;
141
141
 
142
- },{"../../maths/vec2":186,"./fromPoints":57,"./toPoints":62}],48:[function(require,module,exports){
143
- const vec2=require("../../maths/vec2"),vec3=require("../../maths/vec2"),appendPoints=require("./appendPoints"),toPoints=require("./toPoints"),appendBezier=(e,t)=>{let{controlPoints:o,segments:r}=Object.assign({},{segments:16},e);if(!Array.isArray(o))throw new Error("controlPoints must be an array of one or more points");if(o.length<1)throw new Error("controlPoints must be an array of one or more points");if(r<4)throw new Error("segments must be four or more");if(t.isClosed)throw new Error("the given geometry cannot be closed");const n=toPoints(t);if(n.length<1)throw new Error("the given path must contain one or more points (as the starting point for the bezier curve)");if(null===(o=o.slice())[0]){if(o.length<2)throw new Error("a null control point must be passed with one more control points");let e=n[n.length-2];if("lastBezierControlPoint"in t&&(e=t.lastBezierControlPoint),!Array.isArray(e))throw new Error("the given path must contain TWO or more points if given a null control point");const r=vec2.scale(vec2.create(),n[n.length-1],2);vec2.subtract(r,r,e),o[0]=r}o.unshift(n[n.length-1]);const s=o.length-1,i=[];let c=1;for(let e=0;e<=s;++e)e>0&&(c*=e),i.push(c);const a=[];for(let e=0;e<=s;++e){const t=i[s]/(i[e]*i[s-e]);a.push(t)}const l=vec2.create(),h=vec2.create(),p=vec3.create(),u=e=>{let t=1,r=Math.pow(1-e,s);const n=1!==e?1/(1-e):1,i=vec2.create();for(let c=0;c<=s;++c){c===s&&(r=1);const h=a[c]*t*r,p=vec2.scale(l,o[c],h);vec2.add(i,i,p),t*=e,r*=n}return i},v=[],f=[],m=s+1;for(let e=0;e<m;++e){const t=e/(m-1),o=u(t);v.push(o),f.push(t)}let g=1;const w=2*Math.PI/r,P=Math.sin(w);for(;g<v.length-1;){const e=vec2.subtract(l,v[g],v[g-1]);vec2.normalize(e,e);const t=vec2.subtract(h,v[g+1],v[g]);vec2.normalize(t,t);const o=vec2.cross(p,e,t);if(Math.abs(o[2])>P){const e=f[g-1],t=f[g+1],o=e+1*(t-e)/3,r=e+2*(t-e)/3,n=u(o),s=u(r);v.splice(g,1,n,s),f.splice(g,1,o,r),--g<1&&(g=1)}else++g}v.shift();const b=appendPoints(v,t);return b.lastBezierControlPoint=o[o.length-2],b};module.exports=appendBezier;
142
+ },{"../../maths/constants":90,"../../maths/vec2":186,"./fromPoints":57,"./toPoints":62}],48:[function(require,module,exports){
143
+ const{TAU:TAU}=require("../../maths/constants"),vec2=require("../../maths/vec2"),vec3=require("../../maths/vec2"),appendPoints=require("./appendPoints"),toPoints=require("./toPoints"),appendBezier=(e,t)=>{let{controlPoints:o,segments:r}=Object.assign({},{segments:16},e);if(!Array.isArray(o))throw new Error("controlPoints must be an array of one or more points");if(o.length<1)throw new Error("controlPoints must be an array of one or more points");if(r<4)throw new Error("segments must be four or more");if(t.isClosed)throw new Error("the given geometry cannot be closed");const n=toPoints(t);if(n.length<1)throw new Error("the given path must contain one or more points (as the starting point for the bezier curve)");if(null===(o=o.slice())[0]){if(o.length<2)throw new Error("a null control point must be passed with one more control points");let e=n[n.length-2];if("lastBezierControlPoint"in t&&(e=t.lastBezierControlPoint),!Array.isArray(e))throw new Error("the given path must contain TWO or more points if given a null control point");const r=vec2.scale(vec2.create(),n[n.length-1],2);vec2.subtract(r,r,e),o[0]=r}o.unshift(n[n.length-1]);const s=o.length-1,i=[];let c=1;for(let e=0;e<=s;++e)e>0&&(c*=e),i.push(c);const a=[];for(let e=0;e<=s;++e){const t=i[s]/(i[e]*i[s-e]);a.push(t)}const l=vec2.create(),h=vec2.create(),p=vec3.create(),u=e=>{let t=1,r=Math.pow(1-e,s);const n=1!==e?1/(1-e):1,i=vec2.create();for(let c=0;c<=s;++c){c===s&&(r=1);const h=a[c]*t*r,p=vec2.scale(l,o[c],h);vec2.add(i,i,p),t*=e,r*=n}return i},v=[],m=[],f=s+1;for(let e=0;e<f;++e){const t=e/(f-1),o=u(t);v.push(o),m.push(t)}let g=1;const w=TAU/r,P=Math.sin(w);for(;g<v.length-1;){const e=vec2.subtract(l,v[g],v[g-1]);vec2.normalize(e,e);const t=vec2.subtract(h,v[g+1],v[g]);vec2.normalize(t,t);const o=vec2.cross(p,e,t);if(Math.abs(o[2])>P){const e=m[g-1],t=m[g+1],o=e+1*(t-e)/3,r=e+2*(t-e)/3,n=u(o),s=u(r);v.splice(g,1,n,s),m.splice(g,1,o,r),--g<1&&(g=1)}else++g}v.shift();const b=appendPoints(v,t);return b.lastBezierControlPoint=o[o.length-2],b};module.exports=appendBezier;
144
144
 
145
- },{"../../maths/vec2":186,"./appendPoints":49,"./toPoints":62}],49:[function(require,module,exports){
145
+ },{"../../maths/constants":90,"../../maths/vec2":186,"./appendPoints":49,"./toPoints":62}],49:[function(require,module,exports){
146
146
  const concat=require("./concat"),create=require("./create"),appendPoints=(e,c)=>concat(c,create(e));module.exports=appendPoints;
147
147
 
148
148
  },{"./concat":53,"./create":54}],50:[function(require,module,exports){
@@ -266,7 +266,7 @@ module.exports={colors:require("./colors"),curves:require("./curves"),geometries
266
266
  const mat4=require("./mat4"),vec2=require("./vec2"),vec3=require("./vec3"),OrthoNormalBasis=function(e,t){arguments.length<2&&(t=vec3.orthogonal(vec3.create(),e)),this.v=vec3.normalize(vec3.create(),vec3.cross(vec3.create(),e,t)),this.u=vec3.cross(vec3.create(),this.v,e),this.plane=e,this.planeorigin=vec3.scale(vec3.create(),e,e[3])};OrthoNormalBasis.GetCartesian=function(e,t){const i=e+"/"+t;let s,n;if("X/Y"===i)s=[0,0,1],n=[1,0,0];else if("Y/-X"===i)s=[0,0,1],n=[0,1,0];else if("-X/-Y"===i)s=[0,0,1],n=[-1,0,0];else if("-Y/X"===i)s=[0,0,1],n=[0,-1,0];else if("-X/Y"===i)s=[0,0,-1],n=[-1,0,0];else if("-Y/-X"===i)s=[0,0,-1],n=[0,-1,0];else if("X/-Y"===i)s=[0,0,-1],n=[1,0,0];else if("Y/X"===i)s=[0,0,-1],n=[0,1,0];else if("X/Z"===i)s=[0,-1,0],n=[1,0,0];else if("Z/-X"===i)s=[0,-1,0],n=[0,0,1];else if("-X/-Z"===i)s=[0,-1,0],n=[-1,0,0];else if("-Z/X"===i)s=[0,-1,0],n=[0,0,-1];else if("-X/Z"===i)s=[0,1,0],n=[-1,0,0];else if("-Z/-X"===i)s=[0,1,0],n=[0,0,-1];else if("X/-Z"===i)s=[0,1,0],n=[1,0,0];else if("Z/X"===i)s=[0,1,0],n=[0,0,1];else if("Y/Z"===i)s=[1,0,0],n=[0,1,0];else if("Z/-Y"===i)s=[1,0,0],n=[0,0,1];else if("-Y/-Z"===i)s=[1,0,0],n=[0,-1,0];else if("-Z/Y"===i)s=[1,0,0],n=[0,0,-1];else if("-Y/Z"===i)s=[-1,0,0],n=[0,-1,0];else if("-Z/-Y"===i)s=[-1,0,0],n=[0,0,-1];else if("Y/-Z"===i)s=[-1,0,0],n=[0,1,0];else{if("Z/Y"!==i)throw new Error("OrthoNormalBasis.GetCartesian: invalid combination of axis identifiers. Should pass two string arguments from [X,Y,Z,-X,-Y,-Z], being two different axes.");s=[-1,0,0],n=[0,0,1]}return new OrthoNormalBasis(new Plane(new Vector3D(s),0),new Vector3D(n))},OrthoNormalBasis.Z0Plane=function(){const e=new Plane(new Vector3D([0,0,1]),0);return new OrthoNormalBasis(e,new Vector3D([1,0,0]))},OrthoNormalBasis.prototype={getProjectionMatrix:function(){return mat4.fromValues(this.u[0],this.v[0],this.plane[0],0,this.u[1],this.v[1],this.plane[1],0,this.u[2],this.v[2],this.plane[2],0,0,0,-this.plane[3],1)},getInverseProjectionMatrix:function(){const e=vec3.scale(vec3.create(),this.plane,this.plane[3]);return mat4.fromValues(this.u[0],this.u[1],this.u[2],0,this.v[0],this.v[1],this.v[2],0,this.plane[0],this.plane[1],this.plane[2],0,e[0],e[1],e[2],1)},to2D:function(e){return vec2.fromValues(vec3.dot(e,this.u),vec3.dot(e,this.v))},to3D:function(e){const t=vec3.scale(vec3.create(),this.u,e[0]),i=vec3.scale(vec3.create(),this.v,e[1]),s=vec3.add(t,t,this.planeorigin);return vec3.add(i,i,s)},line3Dto2D:function(e){const t=e.point,i=e.direction.plus(t),s=this.to2D(t),n=this.to2D(i);return Line2D.fromPoints(s,n)},line2Dto3D:function(e){const t=e.origin(),i=e.direction().plus(t),s=this.to3D(t),n=this.to3D(i);return Line3D.fromPoints(s,n)},transform:function(e){const t=this.plane.transform(e),i=this.u.transform(e),s=new Vector3D(0,0,0).transform(e),n=i.minus(s);return new OrthoNormalBasis(t,n)}},module.exports=OrthoNormalBasis;
267
267
 
268
268
  },{"./mat4":139,"./vec2":186,"./vec3":217}],90:[function(require,module,exports){
269
- const spatialResolution=1e5,EPS=1e-5,NEPS=1e-13;module.exports={EPS:EPS,NEPS:NEPS,spatialResolution:1e5};
269
+ const spatialResolution=1e5,EPS=1e-5,NEPS=1e-13,TAU=2*Math.PI;module.exports={EPS:EPS,NEPS:NEPS,TAU:TAU,spatialResolution:1e5};
270
270
 
271
271
  },{}],91:[function(require,module,exports){
272
272
  module.exports={constants:require("./constants"),line2:require("./line2"),line3:require("./line3"),mat4:require("./mat4"),plane:require("./plane"),utils:require("./utils"),vec2:require("./vec2"),vec3:require("./vec3"),vec4:require("./vec4")};
@@ -542,7 +542,7 @@ const dot=(o,t)=>o[0]*t[0]+o[1]*t[1];module.exports=dot;
542
542
  const equals=(e,s)=>e[0]===s[0]&&e[1]===s[1];module.exports=equals;
543
543
 
544
544
  },{}],182:[function(require,module,exports){
545
- const fromAngleRadians=require("./fromAngleRadians"),fromAngleDegrees=(e,r)=>fromAngleRadians(e,Math.PI*r/180);module.exports=fromAngleDegrees;
545
+ const fromAngleRadians=require("./fromAngleRadians"),fromAngleDegrees=(e,r)=>fromAngleRadians(e,.017453292519943295*r);module.exports=fromAngleDegrees;
546
546
 
547
547
  },{"./fromAngleRadians":183}],183:[function(require,module,exports){
548
548
  const{sin:sin,cos:cos}=require("../utils/trigonometry"),fromAngleRadians=(s,o)=>(s[0]=cos(o),s[1]=sin(o),s);module.exports=fromAngleRadians;
@@ -575,9 +575,9 @@ const multiply=(l,t,m)=>(l[0]=t[0]*m[0],l[1]=t[1]*m[1],l);module.exports=multipl
575
575
  const negate=(e,t)=>(e[0]=-t[0],e[1]=-t[1],e);module.exports=negate;
576
576
 
577
577
  },{}],193:[function(require,module,exports){
578
- const create=require("./create"),rotate=require("./rotate"),normal=(e,r)=>rotate(e,r,create(),Math.PI/2);module.exports=normal;
578
+ const{TAU:TAU}=require("../constants"),create=require("./create"),rotate=require("./rotate"),normal=(e,r)=>rotate(e,r,create(),TAU/4);module.exports=normal;
579
579
 
580
- },{"./create":176,"./rotate":195}],194:[function(require,module,exports){
580
+ },{"../constants":90,"./create":176,"./rotate":195}],194:[function(require,module,exports){
581
581
  const normalize=(t,e)=>{const o=e[0],r=e[1];let n=o*o+r*r;return n>0&&(n=1/Math.sqrt(n)),t[0]=o*n,t[1]=r*n,t};module.exports=normalize;
582
582
 
583
583
  },{}],195:[function(require,module,exports){
@@ -860,7 +860,7 @@ const geom3=require("../../geometries/geom3"),union=require("../booleans/union")
860
860
  const area=require("../../maths/utils/area"),vec2=require("../../maths/vec2"),geom2=require("../../geometries/geom2"),path2=require("../../geometries/path2"),offsetFromPoints=require("./offsetFromPoints"),createGeometryFromClosedOffsets=e=>{let{external:t,internal:r}=e;area(t)<0?t=t.reverse():r=r.reverse();const o=path2.fromPoints({closed:!0},t),s=path2.fromPoints({closed:!0},r),n=geom2.toSides(geom2.fromPoints(path2.toPoints(o))),a=geom2.toSides(geom2.fromPoints(path2.toPoints(s)));return n.push(...a),geom2.create(n)},createGeometryFromExpandedOpenPath=(e,t,r,o)=>{const{points:s,external:n,internal:a}=e,c=Math.floor(t/2),m=[],i=[];if("round"===r&&c>0){const e=Math.PI/c,t=s[s.length-1],r=vec2.angle(vec2.subtract(vec2.create(),n[n.length-1],t)),d=s[0],l=vec2.angle(vec2.subtract(vec2.create(),a[0],d));for(let s=1;s<c;s++){let n=r+e*s,a=vec2.fromAngleRadians(vec2.create(),n);vec2.scale(a,a,o),vec2.add(a,a,t),m.push(a),n=l+e*s,a=vec2.fromAngleRadians(vec2.create(),n),vec2.scale(a,a,o),vec2.add(a,a,d),i.push(a)}}const d=[];return d.push(...n,...m,...a.reverse(),...i),geom2.fromPoints(d)},expandPath2=(e,t)=>{e=Object.assign({},{delta:1,corners:"edge",segments:16},e);const{delta:r,corners:o,segments:s}=e;if(r<=0)throw new Error("the given delta must be positive for paths");if("edge"!==o&&"chamfer"!==o&&"round"!==o)throw new Error('corners must be "edge", "chamfer", or "round"');const n=t.isClosed,a=path2.toPoints(t);if(0===a.length)throw new Error("the given geometry cannot be empty");const c={points:a,external:offsetFromPoints({delta:r,corners:o,segments:s,closed:n},a),internal:offsetFromPoints({delta:-r,corners:o,segments:s,closed:n},a)};return t.isClosed?createGeometryFromClosedOffsets(c):createGeometryFromExpandedOpenPath(c,s,o,r)};module.exports=expandPath2;
861
861
 
862
862
  },{"../../geometries/geom2":22,"../../geometries/path2":58,"../../maths/utils/area":163,"../../maths/vec2":186,"./offsetFromPoints":292}],288:[function(require,module,exports){
863
- const{EPS:EPS}=require("../../maths/constants"),mat4=require("../../maths/mat4"),vec3=require("../../maths/vec3"),fnNumberSort=require("../../utils/fnNumberSort"),geom3=require("../../geometries/geom3"),poly3=require("../../geometries/poly3"),sphere=require("../../primitives/sphere"),retessellate=require("../modifiers/retessellate"),unionGeom3Sub=require("../booleans/unionGeom3Sub"),extrudePolygon=require("./extrudePolygon"),mapPlaneToVertex=(e,t,o)=>{const n=t.toString();if(e.has(n)){e.get(n)[1].push(o)}else{const s=[t,[o]];e.set(n,s)}},mapPlaneToEdge=(e,t,o)=>{const n=t[0].toString(),s=t[1].toString(),r=n<s?`${n},${s}`:`${s},${n}`;if(e.has(r)){e.get(r)[1].push(o)}else{const n=[t,[o]];e.set(r,n)}},addUniqueAngle=(e,t)=>{e.findIndex(e=>e===t)<0&&e.push(t)},expandShell=(e,t)=>{const{delta:o,segments:n}=Object.assign({},{delta:1,segments:12},e);let s=geom3.create();const r=new Map,c=new Map,a=vec3.create(),l=vec3.create();return geom3.toPolygons(t).forEach((e,t)=>{const n=vec3.scale(vec3.create(),poly3.plane(e),2*o),a=poly3.transform(mat4.fromTranslation(mat4.create(),vec3.scale(vec3.create(),n,-.5)),e),l=extrudePolygon(n,a);s=unionGeom3Sub(s,l);const u=e.vertices;for(let t=0;t<u.length;t++){mapPlaneToVertex(r,u[t],poly3.plane(e));const o=(t+1)%u.length,n=[u[t],u[o]];mapPlaneToEdge(c,n,poly3.plane(e))}}),c.forEach(e=>{const t=e[0],r=e[1],c=t[0],u=t[1],i=vec3.subtract(vec3.create(),u,c);vec3.normalize(i,i);const m=r[0],h=vec3.cross(vec3.create(),m,i);let g=[];for(let e=0;e<n;e++)addUniqueAngle(g,e*Math.PI*2/n);for(let e=0,t=r.length;e<t;e++){const t=r[e],o=vec3.dot(h,t),n=vec3.dot(m,t);let s=Math.atan2(o,n);s<0&&(s+=2*Math.PI),addUniqueAngle(g,s),(s=Math.atan2(-o,-n))<0&&(s+=2*Math.PI),addUniqueAngle(g,s)}const p=(g=g.sort(fnNumberSort)).length;let v,d;const f=[],S=[],q=[];for(let e=-1;e<p;e++){const t=g[e<0?e+p:e],n=Math.sin(t),s=Math.cos(t);vec3.scale(a,m,s*o),vec3.scale(l,h,n*o),vec3.add(a,a,l);const r=vec3.add(vec3.create(),c,a),i=vec3.add(vec3.create(),u,a);let P=!1;if(e>=0&&vec3.distance(r,v)<EPS&&(P=!0),!P){if(e>=0){f.push(r),S.push(i);const e=[d,i,r,v],t=poly3.create(e);q.push(t)}v=r,d=i}}S.reverse(),q.push(poly3.create(f)),q.push(poly3.create(S));const P=geom3.create(q);s=unionGeom3Sub(s,P)}),r.forEach(e=>{const t=e[0],r=e[1],c=r[0];let u=null,i=0;for(let e=1;e<r.length;e++){const t=r[e],o=vec3.cross(a,c,t),n=vec3.length(o);n>.05&&n>i&&(i=n,u=t)}u||(u=vec3.orthogonal(a,c));const m=vec3.cross(a,c,u);vec3.normalize(m,m);const h=vec3.cross(l,m,c),g=sphere({center:[t[0],t[1],t[2]],radius:o,segments:n,axes:[c,m,h]});s=unionGeom3Sub(s,g)}),retessellate(s)};module.exports=expandShell;
863
+ const{EPS:EPS,TAU:TAU}=require("../../maths/constants"),mat4=require("../../maths/mat4"),vec3=require("../../maths/vec3"),fnNumberSort=require("../../utils/fnNumberSort"),geom3=require("../../geometries/geom3"),poly3=require("../../geometries/poly3"),sphere=require("../../primitives/sphere"),retessellate=require("../modifiers/retessellate"),unionGeom3Sub=require("../booleans/unionGeom3Sub"),extrudePolygon=require("./extrudePolygon"),mapPlaneToVertex=(e,t,o)=>{const n=t.toString();if(e.has(n)){e.get(n)[1].push(o)}else{const s=[t,[o]];e.set(n,s)}},mapPlaneToEdge=(e,t,o)=>{const n=t[0].toString(),s=t[1].toString(),r=n<s?`${n},${s}`:`${s},${n}`;if(e.has(r)){e.get(r)[1].push(o)}else{const n=[t,[o]];e.set(r,n)}},addUniqueAngle=(e,t)=>{e.findIndex(e=>e===t)<0&&e.push(t)},expandShell=(e,t)=>{const{delta:o,segments:n}=Object.assign({},{delta:1,segments:12},e);let s=geom3.create();const r=new Map,c=new Map,a=vec3.create(),l=vec3.create();return geom3.toPolygons(t).forEach((e,t)=>{const n=vec3.scale(vec3.create(),poly3.plane(e),2*o),a=poly3.transform(mat4.fromTranslation(mat4.create(),vec3.scale(vec3.create(),n,-.5)),e),l=extrudePolygon(n,a);s=unionGeom3Sub(s,l);const u=e.vertices;for(let t=0;t<u.length;t++){mapPlaneToVertex(r,u[t],poly3.plane(e));const o=(t+1)%u.length,n=[u[t],u[o]];mapPlaneToEdge(c,n,poly3.plane(e))}}),c.forEach(e=>{const t=e[0],r=e[1],c=t[0],u=t[1],i=vec3.subtract(vec3.create(),u,c);vec3.normalize(i,i);const m=r[0],g=vec3.cross(vec3.create(),m,i);let p=[];for(let e=0;e<n;e++)addUniqueAngle(p,e*TAU/n);for(let e=0,t=r.length;e<t;e++){const t=r[e],o=vec3.dot(g,t),n=vec3.dot(m,t);let s=Math.atan2(o,n);s<0&&(s+=TAU),addUniqueAngle(p,s),(s=Math.atan2(-o,-n))<0&&(s+=TAU),addUniqueAngle(p,s)}const h=(p=p.sort(fnNumberSort)).length;let v,d;const f=[],S=[],q=[];for(let e=-1;e<h;e++){const t=p[e<0?e+h:e],n=Math.sin(t),s=Math.cos(t);vec3.scale(a,m,s*o),vec3.scale(l,g,n*o),vec3.add(a,a,l);const r=vec3.add(vec3.create(),c,a),i=vec3.add(vec3.create(),u,a);let y=!1;if(e>=0&&vec3.distance(r,v)<EPS&&(y=!0),!y){if(e>=0){f.push(r),S.push(i);const e=[d,i,r,v],t=poly3.create(e);q.push(t)}v=r,d=i}}S.reverse(),q.push(poly3.create(f)),q.push(poly3.create(S));const y=geom3.create(q);s=unionGeom3Sub(s,y)}),r.forEach(e=>{const t=e[0],r=e[1],c=r[0];let u=null,i=0;for(let e=1;e<r.length;e++){const t=r[e],o=vec3.cross(a,c,t),n=vec3.length(o);n>.05&&n>i&&(i=n,u=t)}u||(u=vec3.orthogonal(a,c));const m=vec3.cross(a,c,u);vec3.normalize(m,m);const g=vec3.cross(l,m,c),p=sphere({center:[t[0],t[1],t[2]],radius:o,segments:n,axes:[c,m,g]});s=unionGeom3Sub(s,p)}),retessellate(s)};module.exports=expandShell;
864
864
 
865
865
  },{"../../geometries/geom3":37,"../../geometries/poly3":75,"../../maths/constants":90,"../../maths/mat4":139,"../../maths/vec3":217,"../../primitives/sphere":379,"../../utils/fnNumberSort":392,"../booleans/unionGeom3Sub":283,"../modifiers/retessellate":349,"./extrudePolygon":289}],289:[function(require,module,exports){
866
866
  const mat4=require("../../maths/mat4"),vec3=require("../../maths/vec3"),geom3=require("../../geometries/geom3"),poly3=require("../../geometries/poly3"),extrudePolygon=(e,r)=>{vec3.dot(poly3.plane(r),e)>0&&(r=poly3.invert(r));const t=[r],o=poly3.transform(mat4.fromTranslation(mat4.create(),e),r),s=r.vertices.length;for(let e=0;e<s;e++){const c=e<s-1?e+1:0,i=poly3.create([r.vertices[e],o.vertices[e],o.vertices[c],r.vertices[c]]);t.push(i)}return t.push(poly3.invert(o)),geom3.create(t)};module.exports=extrudePolygon;
@@ -872,7 +872,7 @@ module.exports={expand:require("./expand"),offset:require("./offset")};
872
872
  const flatten=require("../../utils/flatten"),geom2=require("../../geometries/geom2"),path2=require("../../geometries/path2"),offsetGeom2=require("./offsetGeom2"),offsetPath2=require("./offsetPath2"),offset=(e,...t)=>{if(0===(t=flatten(t)).length)throw new Error("wrong number of arguments");const o=t.map(t=>path2.isA(t)?offsetPath2(e,t):geom2.isA(t)?offsetGeom2(e,t):t);return 1===o.length?o[0]:o};module.exports=offset;
873
873
 
874
874
  },{"../../geometries/geom2":22,"../../geometries/path2":58,"../../utils/flatten":391,"./offsetGeom2":293,"./offsetPath2":294}],292:[function(require,module,exports){
875
- const{EPS:EPS}=require("../../maths/constants"),intersect=require("../../maths/utils/intersect"),line2=require("../../maths/line2"),vec2=require("../../maths/vec2"),area=require("../../maths/utils/area"),offsetFromPoints=(e,t)=>{let{delta:s,corners:c,closed:n,segments:a}=Object.assign({},{delta:1,corners:"edge",closed:!1,segments:16},e);if(Math.abs(s)<EPS)return t;let r=e.closed?area(t):1;0===r&&(r=1);const o=r>0&&s>=0||r<0&&s<0;s=Math.abs(s);let i=null,l=[];const u=[],v=vec2.create(),f=t.length;for(let e=0;e<f;e++){const c=(e+1)%f,a=t[e],r=t[c];o?vec2.subtract(v,a,r):vec2.subtract(v,r,a),vec2.normal(v,v),vec2.normalize(v,v),vec2.scale(v,v,s);const h=vec2.add(vec2.create(),a,v),d=vec2.add(vec2.create(),r,v),m=[h,d];if(null!=i&&(n||!n&&0!==c)){const e=intersect(i[0],i[1],m[0],m[1]);e?(l.pop(),m[0]=e):u.push({c:a,s0:i,s1:m})}i=[h,d],(0!==c||n)&&(l.push(m[0]),l.push(m[1]))}if(n&&null!=i){const e=l[0],s=l[1],c=intersect(i[0],i[1],e,s);if(c)l[0]=c,l.pop();else{const c=t[0],n=[e,s];u.push({c:c,s0:i,s1:n})}}if("edge"===c){const e=new Map;l.forEach((t,s)=>e.set(t,s));const t=line2.create(),s=line2.create();u.forEach(c=>{line2.fromPoints(t,c.s0[0],c.s0[1]),line2.fromPoints(s,c.s1[0],c.s1[1]);const n=line2.intersectPointOfLines(t,s);if(Number.isFinite(n[0])&&Number.isFinite(n[1])){const t=c.s0[1],s=e.get(t);l[s]=n,l[(s+1)%l.length]=void 0}else{const t=c.s1[0],s=e.get(t);l[s]=void 0}}),l=l.filter(e=>void 0!==e)}if("round"===c){let e=Math.floor(a/4);const t=vec2.create();u.forEach(c=>{let n=vec2.angle(vec2.subtract(t,c.s1[0],c.c));if(n-=vec2.angle(vec2.subtract(t,c.s0[1],c.c)),o&&n<0&&(n+=Math.PI)<0&&(n+=Math.PI),!o&&n>0&&(n-=Math.PI)>0&&(n-=Math.PI),0!==n){const r=n/(e=Math.floor(a*(Math.abs(n)/(2*Math.PI)))),o=vec2.angle(vec2.subtract(t,c.s0[1],c.c)),i=[];for(let t=1;t<e;t++){const e=o+r*t,n=vec2.fromAngleRadians(vec2.create(),e);vec2.scale(n,n,s),vec2.add(n,n,c.c),i.push(n)}if(i.length>0){const e=c.s0[1];let t=l.findIndex(t=>vec2.equals(e,t));t=(t+1)%l.length,l.splice(t,0,...i)}}else{const e=c.s1[0],t=l.findIndex(t=>vec2.equals(e,t));l.splice(t,1)}})}return l};module.exports=offsetFromPoints;
875
+ const{EPS:EPS,TAU:TAU}=require("../../maths/constants"),intersect=require("../../maths/utils/intersect"),line2=require("../../maths/line2"),vec2=require("../../maths/vec2"),area=require("../../maths/utils/area"),offsetFromPoints=(e,t)=>{let{delta:s,corners:c,closed:n,segments:a}=Object.assign({},{delta:1,corners:"edge",closed:!1,segments:16},e);if(Math.abs(s)<EPS)return t;let r=e.closed?area(t):1;0===r&&(r=1);const o=r>0&&s>=0||r<0&&s<0;s=Math.abs(s);let i=null,l=[];const u=[],v=vec2.create(),f=t.length;for(let e=0;e<f;e++){const c=(e+1)%f,a=t[e],r=t[c];o?vec2.subtract(v,a,r):vec2.subtract(v,r,a),vec2.normal(v,v),vec2.normalize(v,v),vec2.scale(v,v,s);const h=vec2.add(vec2.create(),a,v),d=vec2.add(vec2.create(),r,v),m=[h,d];if(null!=i&&(n||!n&&0!==c)){const e=intersect(i[0],i[1],m[0],m[1]);e?(l.pop(),m[0]=e):u.push({c:a,s0:i,s1:m})}i=[h,d],(0!==c||n)&&(l.push(m[0]),l.push(m[1]))}if(n&&null!=i){const e=l[0],s=l[1],c=intersect(i[0],i[1],e,s);if(c)l[0]=c,l.pop();else{const c=t[0],n=[e,s];u.push({c:c,s0:i,s1:n})}}if("edge"===c){const e=new Map;l.forEach((t,s)=>e.set(t,s));const t=line2.create(),s=line2.create();u.forEach(c=>{line2.fromPoints(t,c.s0[0],c.s0[1]),line2.fromPoints(s,c.s1[0],c.s1[1]);const n=line2.intersectPointOfLines(t,s);if(Number.isFinite(n[0])&&Number.isFinite(n[1])){const t=c.s0[1],s=e.get(t);l[s]=n,l[(s+1)%l.length]=void 0}else{const t=c.s1[0],s=e.get(t);l[s]=void 0}}),l=l.filter(e=>void 0!==e)}if("round"===c){let e=Math.floor(a/4);const t=vec2.create();u.forEach(c=>{let n=vec2.angle(vec2.subtract(t,c.s1[0],c.c));if(n-=vec2.angle(vec2.subtract(t,c.s0[1],c.c)),o&&n<0&&(n+=Math.PI)<0&&(n+=Math.PI),!o&&n>0&&(n-=Math.PI)>0&&(n-=Math.PI),0!==n){const r=n/(e=Math.floor(a*(Math.abs(n)/TAU))),o=vec2.angle(vec2.subtract(t,c.s0[1],c.c)),i=[];for(let t=1;t<e;t++){const e=o+r*t,n=vec2.fromAngleRadians(vec2.create(),e);vec2.scale(n,n,s),vec2.add(n,n,c.c),i.push(n)}if(i.length>0){const e=c.s0[1];let t=l.findIndex(t=>vec2.equals(e,t));t=(t+1)%l.length,l.splice(t,0,...i)}}else{const e=c.s1[0],t=l.findIndex(t=>vec2.equals(e,t));l.splice(t,1)}})}return l};module.exports=offsetFromPoints;
876
876
 
877
877
  },{"../../maths/constants":90,"../../maths/line2":101,"../../maths/utils/area":163,"../../maths/utils/intersect":166,"../../maths/vec2":186}],293:[function(require,module,exports){
878
878
  const geom2=require("../../geometries/geom2"),poly2=require("../../geometries/poly2"),offsetFromPoints=require("./offsetFromPoints"),offsetGeom2=(e,o)=>{const{delta:r,corners:s,segments:t}=Object.assign({},{delta:1,corners:"edge",segments:0},e);if("edge"!==s&&"chamfer"!==s&&"round"!==s)throw new Error('corners must be "edge", "chamfer", or "round"');const n=geom2.toOutlines(o),m=n.map(o=>{const m=n.reduce((e,r)=>e+poly2.arePointsInside(o,poly2.create(r)),0);return offsetFromPoints(e={delta:m%2==0?r:-r,corners:s,closed:!0,segments:t},o)}).reduce((e,o)=>e.concat(geom2.toSides(geom2.fromPoints(o))),[]);return geom2.create(m)};module.exports=offsetGeom2;
@@ -926,9 +926,9 @@ const{area:area}=require("../../maths/utils"),geom2=require("../../geometries/ge
926
926
  const path2=require("../../geometries/path2"),expand=require("../expansions/expand"),extrudeLinearGeom2=require("./extrudeLinearGeom2"),extrudeRectangularPath2=(e,t)=>{const{size:r,height:n}=Object.assign({},{size:1,height:1},e);if(e.delta=r,e.offset=[0,0,n],0===path2.toPoints(t).length)throw new Error("the given geometry cannot be empty");const a=expand(e,t);return extrudeLinearGeom2(e,a)};module.exports=extrudeRectangularPath2;
927
927
 
928
928
  },{"../../geometries/path2":58,"../expansions/expand":284,"./extrudeLinearGeom2":305}],310:[function(require,module,exports){
929
- const mat4=require("../../maths/mat4"),{mirrorX:mirrorX}=require("../transforms/mirror"),geom2=require("../../geometries/geom2"),slice=require("./slice"),extrudeFromSlices=require("./extrudeFromSlices"),extrudeRotate=(e,t)=>{const r={segments:12,startAngle:0,angle:2*Math.PI,overflow:"cap"};let{segments:a,startAngle:o,angle:s,overflow:m}=Object.assign({},r,e);if(a<3)throw new Error("segments must be greater then 3");o=Math.abs(o)>2*Math.PI?o%(2*Math.PI):o,s=Math.abs(s)>2*Math.PI?s%(2*Math.PI):s;let n=o+s;if((n=Math.abs(n)>2*Math.PI?n%(2*Math.PI):n)<o){const e=o;o=n,n=e}let l=n-o;if(l<=0&&(l=2*Math.PI),Math.abs(l)<2*Math.PI){const e=2*Math.PI/a;a=Math.floor(Math.abs(l)/e),Math.abs(l)>a*e&&a++}let h=geom2.toSides(t);if(0===h.length)throw new Error("the given geometry cannot be empty");const i=h.filter(e=>e[0][0]<0),c=h.filter(e=>e[0][0]>=0);i.length>0&&c.length>0&&"cap"===m&&(i.length>c.length?(h=h.map(e=>{let t=e[0],r=e[1];return[t=[Math.min(t[0],0),t[1]],r=[Math.min(r[0],0),r[1]]]}),t=geom2.reverse(geom2.create(h)),t=mirrorX(t)):c.length>=i.length&&(h=h.map(e=>{let t=e[0],r=e[1];return[t=[Math.max(t[0],0),t[1]],r=[Math.max(r[0],0),r[1]]]}),t=geom2.create(h)));const g=l/a,M=Math.abs(l)<2*Math.PI,u=slice.fromSides(geom2.toSides(t));slice.reverse(u,u);const f=mat4.create();return extrudeFromSlices(e={numberOfSlices:a+1,capStart:M,capEnd:M,close:!M,callback:(e,t,r)=>{let s=g*t+o;return l===2*Math.PI&&t===a&&(s=o),mat4.multiply(f,mat4.fromZRotation(f,s),mat4.fromXRotation(mat4.create(),Math.PI/2)),slice.transform(f,r)}},u)};module.exports=extrudeRotate;
929
+ const{TAU:TAU}=require("../../maths/constants"),mat4=require("../../maths/mat4"),{mirrorX:mirrorX}=require("../transforms/mirror"),geom2=require("../../geometries/geom2"),slice=require("./slice"),extrudeFromSlices=require("./extrudeFromSlices"),extrudeRotate=(e,t)=>{const r={segments:12,startAngle:0,angle:TAU,overflow:"cap"};let{segments:a,startAngle:o,angle:s,overflow:m}=Object.assign({},r,e);if(a<3)throw new Error("segments must be greater then 3");o=Math.abs(o)>TAU?o%TAU:o,s=Math.abs(s)>TAU?s%TAU:s;let n=o+s;if((n=Math.abs(n)>TAU?n%TAU:n)<o){const e=o;o=n,n=e}let l=n-o;if(l<=0&&(l=TAU),Math.abs(l)<TAU){const e=TAU/a;a=Math.floor(Math.abs(l)/e),Math.abs(l)>a*e&&a++}let i=geom2.toSides(t);if(0===i.length)throw new Error("the given geometry cannot be empty");const c=i.filter(e=>e[0][0]<0),g=i.filter(e=>e[0][0]>=0);c.length>0&&g.length>0&&"cap"===m&&(c.length>g.length?(i=i.map(e=>{let t=e[0],r=e[1];return[t=[Math.min(t[0],0),t[1]],r=[Math.min(r[0],0),r[1]]]}),t=geom2.reverse(geom2.create(i)),t=mirrorX(t)):g.length>=c.length&&(i=i.map(e=>{let t=e[0],r=e[1];return[t=[Math.max(t[0],0),t[1]],r=[Math.max(r[0],0),r[1]]]}),t=geom2.create(i)));const h=l/a,u=Math.abs(l)<TAU,A=slice.fromSides(geom2.toSides(t));slice.reverse(A,A);const f=mat4.create();return extrudeFromSlices(e={numberOfSlices:a+1,capStart:u,capEnd:u,close:!u,callback:(e,t,r)=>{let s=h*t+o;return l===TAU&&t===a&&(s=o),mat4.multiply(f,mat4.fromZRotation(f,s),mat4.fromXRotation(mat4.create(),TAU/4)),slice.transform(f,r)}},A)};module.exports=extrudeRotate;
930
930
 
931
- },{"../../geometries/geom2":22,"../../maths/mat4":139,"../transforms/mirror":356,"./extrudeFromSlices":303,"./slice":320}],311:[function(require,module,exports){
931
+ },{"../../geometries/geom2":22,"../../maths/constants":90,"../../maths/mat4":139,"../transforms/mirror":356,"./extrudeFromSlices":303,"./slice":320}],311:[function(require,module,exports){
932
932
  const{EPS:EPS}=require("../../maths/constants"),vec3=require("../../maths/vec3"),poly3=require("../../geometries/poly3"),slice=require("./slice"),gcd=(e,t)=>e===t?e:e<t?gcd(t,e):1===t?1:0===t?e:gcd(t,e%t),lcm=(e,t)=>e*t/gcd(e,t),repartitionEdges=(e,t)=>{const r=e/t.length;if(1===r)return t;const s=vec3.fromValues(r,r,r),c=[];return t.forEach(e=>{const t=vec3.subtract(vec3.create(),e[1],e[0]);vec3.divide(t,t,s);let l=e[0];for(let e=1;e<=r;++e){const e=vec3.add(vec3.create(),l,t);c.push([l,e]),l=e}}),c},EPSAREA=EPS*EPS/2*Math.sin(Math.PI/3),extrudeWalls=(e,t)=>{let r=slice.toEdges(e),s=slice.toEdges(t);if(r.length!==s.length){const e=lcm(r.length,s.length);e!==r.length&&(r=repartitionEdges(e,r)),e!==s.length&&(s=repartitionEdges(e,s))}const c=[];return r.forEach((e,t)=>{const r=s[t],l=poly3.create([e[0],e[1],r[1]]),o=poly3.measureArea(l);Number.isFinite(o)&&o>EPSAREA&&c.push(l);const n=poly3.create([e[0],r[1],r[0]]),i=poly3.measureArea(n);Number.isFinite(i)&&i>EPSAREA&&c.push(n)}),c};module.exports=extrudeWalls;
933
933
 
934
934
  },{"../../geometries/poly3":75,"../../maths/constants":90,"../../maths/vec3":217,"./slice":320}],312:[function(require,module,exports){
@@ -1079,12 +1079,12 @@ const flatten=require("../../utils/flatten"),geom2=require("../../geometries/geo
1079
1079
  const flatten=require("../../utils/flatten"),mat4=require("../../maths/mat4"),geom2=require("../../geometries/geom2"),geom3=require("../../geometries/geom3"),path2=require("../../geometries/path2"),translate=(t,...e)=>{if(!Array.isArray(t))throw new Error("offset must be an array");if(0===(e=flatten(e)).length)throw new Error("wrong number of arguments");for(t=t.slice();t.length<3;)t.push(0);const r=mat4.fromTranslation(mat4.create(),t),a=e.map(t=>path2.isA(t)?path2.transform(r,t):geom2.isA(t)?geom2.transform(r,t):geom3.isA(t)?geom3.transform(r,t):t);return 1===a.length?a[0]:a},translateX=(t,...e)=>translate([t,0,0],e),translateY=(t,...e)=>translate([0,t,0],e),translateZ=(t,...e)=>translate([0,0,t],e);module.exports={translate:translate,translateX:translateX,translateY:translateY,translateZ:translateZ};
1080
1080
 
1081
1081
  },{"../../geometries/geom2":22,"../../geometries/geom3":37,"../../geometries/path2":58,"../../maths/mat4":139,"../../utils/flatten":391}],361:[function(require,module,exports){
1082
- const{EPS:EPS}=require("../maths/constants"),vec2=require("../maths/vec2"),path2=require("../geometries/path2"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),arc=e=>{const r={center:[0,0],radius:1,startAngle:0,endAngle:2*Math.PI,makeTangent:!1,segments:32};let{center:t,radius:s,startAngle:a,endAngle:n,makeTangent:o,segments:c}=Object.assign({},r,e);if(!isNumberArray(t,2))throw new Error("center must be an array of X and Y values");if(!isGT(s,0))throw new Error("radius must be greater than zero");if(!isGTE(a,0))throw new Error("startAngle must be positive");if(!isGTE(n,0))throw new Error("endAngle must be positive");if(!isGTE(c,4))throw new Error("segments must be four or more");a%=2*Math.PI,n%=2*Math.PI;let i=2*Math.PI;a<n&&(i=n-a),a>n&&(i=n+(2*Math.PI-a));const m=Math.acos((s*s+s*s-EPS*EPS)/(2*s*s)),h=vec2.clone(t);let l;const u=[];if(i<m)l=vec2.fromAngleRadians(vec2.create(),a),vec2.scale(l,l,s),vec2.add(l,l,h),u.push(l);else{const e=Math.max(1,Math.floor(c*(i/(2*Math.PI))))+1;let r=.5*e/i;r>.25&&(r=.25);const t=o?e+2:e;for(let n=0;n<=t;n++){let t=n;o&&((t=(n-1)*(e-2*r)/e+r)<0&&(t=0),t>e&&(t=e));const c=a+t*(i/e);l=vec2.fromAngleRadians(vec2.create(),c),vec2.scale(l,l,s),vec2.add(l,l,h),u.push(l)}}return path2.fromPoints({closed:!1},u)};module.exports=arc;
1082
+ const{EPS:EPS,TAU:TAU}=require("../maths/constants"),vec2=require("../maths/vec2"),path2=require("../geometries/path2"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),arc=e=>{const r={center:[0,0],radius:1,startAngle:0,endAngle:TAU,makeTangent:!1,segments:32};let{center:t,radius:s,startAngle:n,endAngle:a,makeTangent:o,segments:c}=Object.assign({},r,e);if(!isNumberArray(t,2))throw new Error("center must be an array of X and Y values");if(!isGT(s,0))throw new Error("radius must be greater than zero");if(!isGTE(n,0))throw new Error("startAngle must be positive");if(!isGTE(a,0))throw new Error("endAngle must be positive");if(!isGTE(c,4))throw new Error("segments must be four or more");let i=TAU;(n%=TAU)<(a%=TAU)&&(i=a-n),n>a&&(i=a+(TAU-n));const m=Math.acos((s*s+s*s-EPS*EPS)/(2*s*s)),l=vec2.clone(t);let u;const A=[];if(i<m)u=vec2.fromAngleRadians(vec2.create(),n),vec2.scale(u,u,s),vec2.add(u,u,l),A.push(u);else{const e=Math.max(1,Math.floor(c*(i/TAU)))+1;let r=.5*e/i;r>.25&&(r=.25);const t=o?e+2:e;for(let a=0;a<=t;a++){let t=a;o&&((t=(a-1)*(e-2*r)/e+r)<0&&(t=0),t>e&&(t=e));const c=n+t*(i/e);u=vec2.fromAngleRadians(vec2.create(),c),vec2.scale(u,u,s),vec2.add(u,u,l),A.push(u)}}return path2.fromPoints({closed:!1},A)};module.exports=arc;
1083
1083
 
1084
1084
  },{"../geometries/path2":58,"../maths/constants":90,"../maths/vec2":186,"./commonChecks":363}],362:[function(require,module,exports){
1085
- const ellipse=require("./ellipse"),{isGT:isGT}=require("./commonChecks"),circle=e=>{const r={center:[0,0],radius:1,startAngle:0,endAngle:2*Math.PI,segments:32};let{center:s,radius:t,startAngle:n,endAngle:i,segments:l}=Object.assign({},r,e);if(!isGT(t,0))throw new Error("radius must be greater than zero");return ellipse({center:s,radius:t=[t,t],startAngle:n,endAngle:i,segments:l})};module.exports=circle;
1085
+ const{TAU:TAU}=require("../maths/constants"),ellipse=require("./ellipse"),{isGT:isGT}=require("./commonChecks"),circle=e=>{const s={center:[0,0],radius:1,startAngle:0,endAngle:TAU,segments:32};let{center:r,radius:t,startAngle:n,endAngle:i,segments:l}=Object.assign({},s,e);if(!isGT(t,0))throw new Error("radius must be greater than zero");return ellipse({center:r,radius:t=[t,t],startAngle:n,endAngle:i,segments:l})};module.exports=circle;
1086
1086
 
1087
- },{"./commonChecks":363,"./ellipse":368}],363:[function(require,module,exports){
1087
+ },{"../maths/constants":90,"./commonChecks":363,"./ellipse":368}],363:[function(require,module,exports){
1088
1088
  const isNumberArray=(i,r)=>!!(Array.isArray(i)&&i.length>=r)&&i.every(i=>Number.isFinite(i)),isGT=(i,r)=>Number.isFinite(i)&&i>r,isGTE=(i,r)=>Number.isFinite(i)&&i>=r;module.exports={isNumberArray:isNumberArray,isGT:isGT,isGTE:isGTE};
1089
1089
 
1090
1090
  },{}],364:[function(require,module,exports){
@@ -1097,15 +1097,15 @@ const geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),
1097
1097
  const cylinderElliptic=require("./cylinderElliptic"),{isGT:isGT}=require("./commonChecks"),cylinder=e=>{const{center:i,height:r,radius:t,segments:s}=Object.assign({},{center:[0,0,0],height:2,radius:1,segments:32},e);if(!isGT(t,0))throw new Error("radius must be greater than zero");return cylinderElliptic({center:i,height:r,startRadius:[t,t],endRadius:[t,t],segments:s})};module.exports=cylinder;
1098
1098
 
1099
1099
  },{"./commonChecks":363,"./cylinderElliptic":367}],367:[function(require,module,exports){
1100
- const{EPS:EPS}=require("../maths/constants"),vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),cylinderElliptic=e=>{const r={center:[0,0,0],height:2,startRadius:[1,1],startAngle:0,endRadius:[1,1],endAngle:2*Math.PI,segments:32};let{center:t,height:s,startRadius:a,startAngle:i,endRadius:o,endAngle:n,segments:u}=Object.assign({},r,e);if(!isNumberArray(t,3))throw new Error("center must be an array of X, Y and Z values");if(!isGT(s,0))throw new Error("height must be greater then zero");if(!isNumberArray(a,2))throw new Error("startRadius must be an array of X and Y values");if(!a.every(e=>e>=0))throw new Error("startRadius values must be positive");if(!isNumberArray(o,2))throw new Error("endRadius must be an array of X and Y values");if(!o.every(e=>e>=0))throw new Error("endRadius values must be positive");if(o.every(e=>0===e)&&a.every(e=>0===e))throw new Error("at least one radius must be positive");if(!isGTE(i,0))throw new Error("startAngle must be positive");if(!isGTE(n,0))throw new Error("endAngle must be positive");if(!isGTE(u,4))throw new Error("segments must be four or more");i%=2*Math.PI,n%=2*Math.PI;let c=2*Math.PI;i<n&&(c=n-i),i>n&&(c=n+(2*Math.PI-i));const h=Math.min(a[0],a[1],o[0],o[1]),l=Math.acos((h*h+h*h-EPS*EPS)/(2*h*h));if(c<l)throw new Error("startAngle and endAngle do not define a significant rotation");const m=Math.floor(u*(c/(2*Math.PI))),d=vec3.fromValues(0,0,-s/2),v=vec3.fromValues(0,0,s/2),f=vec3.subtract(vec3.create(),v,d),g=vec3.fromValues(1,0,0),p=vec3.fromValues(0,1,0),w=vec3.create(),E=vec3.create(),y=vec3.create(),b=(e,r,t)=>{const s=r*c+i;return vec3.scale(w,g,t[0]*cos(s)),vec3.scale(E,p,t[1]*sin(s)),vec3.add(w,w,E),vec3.scale(y,f,e),vec3.add(y,y,d),vec3.add(vec3.create(),w,y)},A=(...e)=>{const r=e.map(e=>vec3.add(vec3.create(),e,t));return poly3.create(r)},P=[];for(let e=0;e<m;e++){const r=e/m;let t=(e+1)/m;c===2*Math.PI&&e===m-1&&(t=0),o[0]===a[0]&&o[1]===a[1]?(P.push(A(d,b(0,t,o),b(0,r,o))),P.push(A(b(0,t,o),b(1,t,o),b(1,r,o),b(0,r,o))),P.push(A(v,b(1,r,o),b(1,t,o)))):(a[0]>0&&a[1]>0&&P.push(A(d,b(0,t,a),b(0,r,a))),(a[0]>0||a[1]>0)&&P.push(A(b(0,r,a),b(0,t,a),b(1,r,o))),o[0]>0&&o[1]>0&&P.push(A(v,b(1,r,o),b(1,t,o))),(o[0]>0||o[1]>0)&&P.push(A(b(1,r,o),b(0,t,a),b(1,t,o))))}return c<2*Math.PI&&(P.push(A(d,b(0,0,a),v)),P.push(A(b(0,0,a),b(1,0,o),v)),P.push(A(d,v,b(0,1,a))),P.push(A(b(0,1,a),v,b(1,1,o)))),geom3.create(P)};module.exports=cylinderElliptic;
1100
+ const{EPS:EPS,TAU:TAU}=require("../maths/constants"),vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),cylinderElliptic=e=>{const r={center:[0,0,0],height:2,startRadius:[1,1],startAngle:0,endRadius:[1,1],endAngle:TAU,segments:32};let{center:s,height:t,startRadius:a,startAngle:i,endRadius:o,endAngle:n,segments:u}=Object.assign({},r,e);if(!isNumberArray(s,3))throw new Error("center must be an array of X, Y and Z values");if(!isGT(t,0))throw new Error("height must be greater then zero");if(!isNumberArray(a,2))throw new Error("startRadius must be an array of X and Y values");if(!a.every(e=>e>=0))throw new Error("startRadius values must be positive");if(!isNumberArray(o,2))throw new Error("endRadius must be an array of X and Y values");if(!o.every(e=>e>=0))throw new Error("endRadius values must be positive");if(o.every(e=>0===e)&&a.every(e=>0===e))throw new Error("at least one radius must be positive");if(!isGTE(i,0))throw new Error("startAngle must be positive");if(!isGTE(n,0))throw new Error("endAngle must be positive");if(!isGTE(u,4))throw new Error("segments must be four or more");let c=TAU;(i%=TAU)<(n%=TAU)&&(c=n-i),i>n&&(c=n+(TAU-i));const l=Math.min(a[0],a[1],o[0],o[1]),m=Math.acos((l*l+l*l-EPS*EPS)/(2*l*l));if(c<m)throw new Error("startAngle and endAngle do not define a significant rotation");const h=Math.floor(u*(c/TAU)),d=vec3.fromValues(0,0,-t/2),v=vec3.fromValues(0,0,t/2),f=vec3.subtract(vec3.create(),v,d),g=vec3.fromValues(1,0,0),p=vec3.fromValues(0,1,0),A=vec3.create(),w=vec3.create(),E=vec3.create(),y=(e,r,s)=>{const t=r*c+i;return vec3.scale(A,g,s[0]*cos(t)),vec3.scale(w,p,s[1]*sin(t)),vec3.add(A,A,w),vec3.scale(E,f,e),vec3.add(E,E,d),vec3.add(vec3.create(),A,E)},T=(...e)=>{const r=e.map(e=>vec3.add(vec3.create(),e,s));return poly3.create(r)},b=[];for(let e=0;e<h;e++){const r=e/h;let s=(e+1)/h;c===TAU&&e===h-1&&(s=0),o[0]===a[0]&&o[1]===a[1]?(b.push(T(d,y(0,s,o),y(0,r,o))),b.push(T(y(0,s,o),y(1,s,o),y(1,r,o),y(0,r,o))),b.push(T(v,y(1,r,o),y(1,s,o)))):(a[0]>0&&a[1]>0&&b.push(T(d,y(0,s,a),y(0,r,a))),(a[0]>0||a[1]>0)&&b.push(T(y(0,r,a),y(0,s,a),y(1,r,o))),o[0]>0&&o[1]>0&&b.push(T(v,y(1,r,o),y(1,s,o))),(o[0]>0||o[1]>0)&&b.push(T(y(1,r,o),y(0,s,a),y(1,s,o))))}return c<TAU&&(b.push(T(d,y(0,0,a),v)),b.push(T(y(0,0,a),y(1,0,o),v)),b.push(T(d,v,y(0,1,a))),b.push(T(y(0,1,a),v,y(1,1,o)))),geom3.create(b)};module.exports=cylinderElliptic;
1101
1101
 
1102
1102
  },{"../geometries/geom3":37,"../geometries/poly3":75,"../maths/constants":90,"../maths/utils/trigonometry":168,"../maths/vec3":217,"./commonChecks":363}],368:[function(require,module,exports){
1103
- const{EPS:EPS}=require("../maths/constants"),vec2=require("../maths/vec2"),geom2=require("../geometries/geom2"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),ellipse=e=>{const r={center:[0,0],radius:[1,1],startAngle:0,endAngle:2*Math.PI,segments:32};let{center:t,radius:s,startAngle:n,endAngle:o,segments:a}=Object.assign({},r,e);if(!isNumberArray(t,2))throw new Error("center must be an array of X and Y values");if(!isNumberArray(s,2))throw new Error("radius must be an array of X and Y values");if(!s.every(e=>e>0))throw new Error("radius values must be greater than zero");if(!isGTE(n,0))throw new Error("startAngle must be positive");if(!isGTE(o,0))throw new Error("endAngle must be positive");if(!isGTE(a,3))throw new Error("segments must be three or more");n%=2*Math.PI,o%=2*Math.PI;let i=2*Math.PI;n<o&&(i=o-n),n>o&&(i=o+(2*Math.PI-n));const m=Math.min(s[0],s[1]);if(i<Math.acos((m*m+m*m-EPS*EPS)/(2*m*m)))throw new Error("startAngle and endAngle do not define a significant rotation");a=Math.floor(a*(i/(2*Math.PI)));const u=vec2.clone(t),h=i/a,c=[];a=i<2*Math.PI?a+1:a;for(let e=0;e<a;e++){const r=h*e+n,t=vec2.fromValues(s[0]*cos(r),s[1]*sin(r));vec2.add(t,u,t),c.push(t)}return i<2*Math.PI&&c.push(u),geom2.fromPoints(c)};module.exports=ellipse;
1103
+ const{EPS:EPS,TAU:TAU}=require("../maths/constants"),vec2=require("../maths/vec2"),geom2=require("../geometries/geom2"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),ellipse=e=>{const r={center:[0,0],radius:[1,1],startAngle:0,endAngle:TAU,segments:32};let{center:s,radius:t,startAngle:n,endAngle:o,segments:i}=Object.assign({},r,e);if(!isNumberArray(s,2))throw new Error("center must be an array of X and Y values");if(!isNumberArray(t,2))throw new Error("radius must be an array of X and Y values");if(!t.every(e=>e>0))throw new Error("radius values must be greater than zero");if(!isGTE(n,0))throw new Error("startAngle must be positive");if(!isGTE(o,0))throw new Error("endAngle must be positive");if(!isGTE(i,3))throw new Error("segments must be three or more");let a=TAU;(n%=TAU)<(o%=TAU)&&(a=o-n),n>o&&(a=o+(TAU-n));const m=Math.min(t[0],t[1]);if(a<Math.acos((m*m+m*m-EPS*EPS)/(2*m*m)))throw new Error("startAngle and endAngle do not define a significant rotation");i=Math.floor(i*(a/TAU));const u=vec2.clone(s),c=a/i,l=[];i=a<TAU?i+1:i;for(let e=0;e<i;e++){const r=c*e+n,s=vec2.fromValues(t[0]*cos(r),t[1]*sin(r));vec2.add(s,u,s),l.push(s)}return a<TAU&&l.push(u),geom2.fromPoints(l)};module.exports=ellipse;
1104
1104
 
1105
1105
  },{"../geometries/geom2":22,"../maths/constants":90,"../maths/utils/trigonometry":168,"../maths/vec2":186,"./commonChecks":363}],369:[function(require,module,exports){
1106
- const vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),ellipsoid=e=>{const{center:c,radius:r,segments:a,axes:s}=Object.assign({},{center:[0,0,0],radius:[1,1,1],segments:32,axes:[[1,0,0],[0,-1,0],[0,0,1]]},e);if(!isNumberArray(c,3))throw new Error("center must be an array of X, Y and Z values");if(!isNumberArray(r,3))throw new Error("radius must be an array of X, Y and Z values");if(!r.every(e=>e>0))throw new Error("radius values must be greater than zero");if(!isGTE(a,4))throw new Error("segments must be four or more");const t=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),s[0]),r[0]),v=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),s[1]),r[1]),o=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),s[2]),r[2]),l=Math.round(a/4);let u;const i=[],d=vec3.create(),n=vec3.create();for(let e=0;e<=a;e++){const r=2*Math.PI*e/a,s=vec3.add(vec3.create(),vec3.scale(d,t,cos(r)),vec3.scale(n,v,sin(r)));if(e>0){let e,r;for(let a=0;a<=l;a++){const t=.5*Math.PI*a/l,v=cos(t),m=sin(t);if(a>0){let t,h=[];t=vec3.subtract(vec3.create(),vec3.scale(d,u,e),vec3.scale(n,o,r)),h.push(vec3.add(t,t,c)),t=vec3.subtract(vec3.create(),vec3.scale(d,s,e),vec3.scale(n,o,r)),h.push(vec3.add(t,t,c)),a<l&&(t=vec3.subtract(vec3.create(),vec3.scale(d,s,v),vec3.scale(n,o,m)),h.push(vec3.add(t,t,c))),t=vec3.subtract(vec3.create(),vec3.scale(d,u,v),vec3.scale(n,o,m)),h.push(vec3.add(t,t,c)),i.push(poly3.create(h)),h=[],t=vec3.add(vec3.create(),vec3.scale(d,u,e),vec3.scale(n,o,r)),h.push(vec3.add(vec3.create(),c,t)),t=vec3.add(t,vec3.scale(d,s,e),vec3.scale(n,o,r)),h.push(vec3.add(vec3.create(),c,t)),a<l&&(t=vec3.add(t,vec3.scale(d,s,v),vec3.scale(n,o,m)),h.push(vec3.add(vec3.create(),c,t))),t=vec3.add(t,vec3.scale(d,u,v),vec3.scale(n,o,m)),h.push(vec3.add(vec3.create(),c,t)),h.reverse(),i.push(poly3.create(h))}e=v,r=m}}u=s}return geom3.create(i)};module.exports=ellipsoid;
1106
+ const{TAU:TAU}=require("../maths/constants"),vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),ellipsoid=e=>{const{center:c,radius:r,segments:a,axes:s}=Object.assign({},{center:[0,0,0],radius:[1,1,1],segments:32,axes:[[1,0,0],[0,-1,0],[0,0,1]]},e);if(!isNumberArray(c,3))throw new Error("center must be an array of X, Y and Z values");if(!isNumberArray(r,3))throw new Error("radius must be an array of X, Y and Z values");if(!r.every(e=>e>0))throw new Error("radius values must be greater than zero");if(!isGTE(a,4))throw new Error("segments must be four or more");const t=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),s[0]),r[0]),v=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),s[1]),r[1]),o=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),s[2]),r[2]),l=Math.round(a/4);let u;const i=[],d=vec3.create(),n=vec3.create();for(let e=0;e<=a;e++){const r=TAU*e/a,s=vec3.add(vec3.create(),vec3.scale(d,t,cos(r)),vec3.scale(n,v,sin(r)));if(e>0){let e,r;for(let a=0;a<=l;a++){const t=TAU/4*a/l,v=cos(t),m=sin(t);if(a>0){let t,h=[];t=vec3.subtract(vec3.create(),vec3.scale(d,u,e),vec3.scale(n,o,r)),h.push(vec3.add(t,t,c)),t=vec3.subtract(vec3.create(),vec3.scale(d,s,e),vec3.scale(n,o,r)),h.push(vec3.add(t,t,c)),a<l&&(t=vec3.subtract(vec3.create(),vec3.scale(d,s,v),vec3.scale(n,o,m)),h.push(vec3.add(t,t,c))),t=vec3.subtract(vec3.create(),vec3.scale(d,u,v),vec3.scale(n,o,m)),h.push(vec3.add(t,t,c)),i.push(poly3.create(h)),h=[],t=vec3.add(vec3.create(),vec3.scale(d,u,e),vec3.scale(n,o,r)),h.push(vec3.add(vec3.create(),c,t)),t=vec3.add(t,vec3.scale(d,s,e),vec3.scale(n,o,r)),h.push(vec3.add(vec3.create(),c,t)),a<l&&(t=vec3.add(t,vec3.scale(d,s,v),vec3.scale(n,o,m)),h.push(vec3.add(vec3.create(),c,t))),t=vec3.add(t,vec3.scale(d,u,v),vec3.scale(n,o,m)),h.push(vec3.add(vec3.create(),c,t)),h.reverse(),i.push(poly3.create(h))}e=v,r=m}}u=s}return geom3.create(i)};module.exports=ellipsoid;
1107
1107
 
1108
- },{"../geometries/geom3":37,"../geometries/poly3":75,"../maths/utils/trigonometry":168,"../maths/vec3":217,"./commonChecks":363}],370:[function(require,module,exports){
1108
+ },{"../geometries/geom3":37,"../geometries/poly3":75,"../maths/constants":90,"../maths/utils/trigonometry":168,"../maths/vec3":217,"./commonChecks":363}],370:[function(require,module,exports){
1109
1109
  const mat4=require("../maths/mat4"),vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),polyhedron=require("./polyhedron"),{isGT:isGT,isGTE:isGTE}=require("./commonChecks"),geodesicSphere=e=>{let{radius:r,frequency:t}=Object.assign({},{radius:1,frequency:6},e);if(!isGT(r,0))throw new Error("radius must be greater than zero");if(!isGTE(t,6))throw new Error("frequency must be six or more");t=Math.floor(t/6);const o=[[.850651,0,-.525731],[.850651,-0,.525731],[-.850651,-0,.525731],[-.850651,0,-.525731],[0,-.525731,.850651],[0,.525731,.850651],[0,.525731,-.850651],[0,-.525731,-.850651],[-.525731,-.850651,-0],[.525731,-.850651,-0],[.525731,.850651,0],[-.525731,.850651,0]],s=[[0,9,1],[1,10,0],[6,7,0],[10,6,0],[7,9,0],[5,1,4],[4,1,9],[5,10,1],[2,8,3],[3,11,2],[2,5,4],[4,8,2],[2,11,5],[3,7,6],[6,11,3],[8,7,3],[9,8,4],[11,10,5],[10,11,6],[8,9,7]],n=(e,r,t)=>{const o=e[0],s=e[1],n=e[2];let c=t;const a=[],l=[];for(let e=0;e<r;e++)for(let t=0;t<r-e;t++){const f=e/r,u=(e+1)/r,h=t/(r-e),m=(t+1)/(r-e),g=r-e-1?t/(r-e-1):1,p=[];p[0]=i(i(o,s,h),n,f),p[1]=i(i(o,s,m),n,f),p[2]=i(i(o,s,g),n,u);for(let e=0;e<3;e++){const r=vec3.length(p[e]);for(let t=0;t<3;t++)p[e][t]/=r}if(a.push(p[0],p[1],p[2]),l.push([c,c+1,c+2]),c+=3,t<r-e-1){const h=r-e-1?(t+1)/(r-e-1):1;p[0]=i(i(o,s,m),n,f),p[1]=i(i(o,s,h),n,u),p[2]=i(i(o,s,g),n,u);for(let e=0;e<3;e++){const r=vec3.length(p[e]);for(let t=0;t<3;t++)p[e][t]/=r}a.push(p[0],p[1],p[2]),l.push([c,c+1,c+2]),c+=3}}return{points:a,triangles:l,offset:c}},i=(e,r,t)=>{const o=1-t,s=[];for(let n=0;n<3;n++)s[n]=e[n]*o+r[n]*t;return s};let c=[],a=[],l=0;for(let e=0;e<s.length;e++){const r=n([o[s[e][0]],o[s[e][1]],o[s[e][2]]],t,l);c=c.concat(r.points),a=a.concat(r.triangles),l=r.offset}let f=polyhedron({points:c,faces:a,orientation:"inward"});return 1!==r&&(f=geom3.transform(mat4.fromScaling(mat4.create(),[r,r,r]),f)),f};module.exports=geodesicSphere;
1110
1110
 
1111
1111
  },{"../geometries/geom3":37,"../maths/mat4":139,"../maths/vec3":217,"./commonChecks":363,"./polyhedron":374}],371:[function(require,module,exports){
@@ -1124,13 +1124,13 @@ const geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),
1124
1124
  const vec2=require("../maths/vec2"),geom2=require("../geometries/geom2"),{isNumberArray:isNumberArray}=require("./commonChecks"),rectangle=e=>{const{center:r,size:t}=Object.assign({},{center:[0,0],size:[2,2]},e);if(!isNumberArray(r,2))throw new Error("center must be an array of X and Y values");if(!isNumberArray(t,2))throw new Error("size must be an array of X and Y values");if(!t.every(e=>e>0))throw new Error("size values must be greater than zero");const a=[t[0]/2,t[1]/2],c=[a[0],-a[1]],s=[vec2.subtract(vec2.create(),r,a),vec2.add(vec2.create(),r,c),vec2.add(vec2.create(),r,a),vec2.subtract(vec2.create(),r,c)];return geom2.fromPoints(s)};module.exports=rectangle;
1125
1125
 
1126
1126
  },{"../geometries/geom2":22,"../maths/vec2":186,"./commonChecks":363}],376:[function(require,module,exports){
1127
- const{EPS:EPS}=require("../maths/constants"),vec2=require("../maths/vec2"),vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),createCorners=(e,r,t,s,c,o)=>{const a=Math.PI/2*c/s,n=cos(a),i=sin(a),u=s-c;let h=t*n,l=r[2]-(t-t*i);o||(l=t-t*i-r[2]),h=h>EPS?h:0;const d=vec3.add(vec3.create(),e,[r[0]-t,r[1]-t,l]),v=vec3.add(vec3.create(),e,[t-r[0],r[1]-t,l]),m=vec3.add(vec3.create(),e,[t-r[0],t-r[1],l]),p=vec3.add(vec3.create(),e,[r[0]-t,t-r[1],l]),f=[],g=[],E=[],y=[];for(let e=0;e<=u;e++){const r=u>0?Math.PI/2*e/u:0,t=vec2.fromAngleRadians(vec2.create(),r);vec2.scale(t,t,h);const s=vec3.fromVec2(vec3.create(),t);f.push(vec3.add(vec3.create(),d,s)),vec3.rotateZ(s,s,[0,0,0],Math.PI/2),g.push(vec3.add(vec3.create(),v,s)),vec3.rotateZ(s,s,[0,0,0],Math.PI/2),E.push(vec3.add(vec3.create(),m,s)),vec3.rotateZ(s,s,[0,0,0],Math.PI/2),y.push(vec3.add(vec3.create(),p,s))}return o?[f,g,E,y]:(f.reverse(),g.reverse(),E.reverse(),y.reverse(),[y,E,g,f])},stitchCorners=(e,r)=>{const t=[];for(let s=0;s<e.length;s++){const c=e[s],o=r[s];for(let e=0;e<c.length-1;e++)t.push(poly3.create([c[e],c[e+1],o[e]])),e<o.length-1&&t.push(poly3.create([o[e],c[e+1],o[e+1]]))}return t},stitchWalls=(e,r)=>{const t=[];for(let s=0;s<e.length;s++){let c=e[s],o=r[s];const a=c[c.length-1],n=o[o.length-1],i=(s+1)%e.length;c=e[i],o=r[i];const u=c[0],h=o[0];t.push(poly3.create([a,u,h,n]))}return t},stitchSides=(e,r)=>{e=(e=[e[3],e[2],e[1],e[0]]).map(e=>e.slice().reverse());const t=[];e.forEach(e=>{e.forEach(e=>t.push(e))});const s=[];r.forEach(e=>{e.forEach(e=>s.push(e))});const c=[];for(let e=0;e<s.length;e++){const r=(e+1)%s.length;c.push(poly3.create([t[e],t[r],s[r],s[e]]))}return c},roundedCuboid=e=>{let{center:r,size:t,roundRadius:s,segments:c}=Object.assign({},{center:[0,0,0],size:[2,2,2],roundRadius:.2,segments:32},e);if(!isNumberArray(r,3))throw new Error("center must be an array of X, Y and Z values");if(!isNumberArray(t,3))throw new Error("size must be an array of X, Y and Z values");if(!t.every(e=>e>0))throw new Error("size values must be greater than zero");if(!isGT(s,0))throw new Error("roundRadius must be greater than zero");if(!isGTE(c,4))throw new Error("segments must be four or more");if(s>(t=t.map(e=>e/2))[0]-EPS||s>t[1]-EPS||s>t[2]-EPS)throw new Error("roundRadius must be smaller then the radius of all dimensions");c=Math.floor(c/4);let o=null,a=null,n=[];for(let e=0;e<=c;e++){const i=createCorners(r,t,s,c,e,!0),u=createCorners(r,t,s,c,e,!1);if(0===e&&(n=n.concat(stitchSides(u,i))),o&&(n=n.concat(stitchCorners(o,i),stitchWalls(o,i))),a&&(n=n.concat(stitchCorners(a,u),stitchWalls(a,u))),e===c){let e=i.map(e=>e[0]);n.push(poly3.create(e)),e=u.map(e=>e[0]),n.push(poly3.create(e))}o=i,a=u}return geom3.create(n)};module.exports=roundedCuboid;
1127
+ const{EPS:EPS,TAU:TAU}=require("../maths/constants"),vec2=require("../maths/vec2"),vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),createCorners=(e,r,t,s,c,o)=>{const a=TAU/4*c/s,n=cos(a),i=sin(a),u=s-c;let l=t*n,h=r[2]-(t-t*i);o||(h=t-t*i-r[2]),l=l>EPS?l:0;const d=vec3.add(vec3.create(),e,[r[0]-t,r[1]-t,h]),v=vec3.add(vec3.create(),e,[t-r[0],r[1]-t,h]),m=vec3.add(vec3.create(),e,[t-r[0],t-r[1],h]),p=vec3.add(vec3.create(),e,[r[0]-t,t-r[1],h]),f=[],g=[],E=[],y=[];for(let e=0;e<=u;e++){const r=u>0?TAU/4*e/u:0,t=vec2.fromAngleRadians(vec2.create(),r);vec2.scale(t,t,l);const s=vec3.fromVec2(vec3.create(),t);f.push(vec3.add(vec3.create(),d,s)),vec3.rotateZ(s,s,[0,0,0],TAU/4),g.push(vec3.add(vec3.create(),v,s)),vec3.rotateZ(s,s,[0,0,0],TAU/4),E.push(vec3.add(vec3.create(),m,s)),vec3.rotateZ(s,s,[0,0,0],TAU/4),y.push(vec3.add(vec3.create(),p,s))}return o?[f,g,E,y]:(f.reverse(),g.reverse(),E.reverse(),y.reverse(),[y,E,g,f])},stitchCorners=(e,r)=>{const t=[];for(let s=0;s<e.length;s++){const c=e[s],o=r[s];for(let e=0;e<c.length-1;e++)t.push(poly3.create([c[e],c[e+1],o[e]])),e<o.length-1&&t.push(poly3.create([o[e],c[e+1],o[e+1]]))}return t},stitchWalls=(e,r)=>{const t=[];for(let s=0;s<e.length;s++){let c=e[s],o=r[s];const a=c[c.length-1],n=o[o.length-1],i=(s+1)%e.length;c=e[i],o=r[i];const u=c[0],l=o[0];t.push(poly3.create([a,u,l,n]))}return t},stitchSides=(e,r)=>{e=(e=[e[3],e[2],e[1],e[0]]).map(e=>e.slice().reverse());const t=[];e.forEach(e=>{e.forEach(e=>t.push(e))});const s=[];r.forEach(e=>{e.forEach(e=>s.push(e))});const c=[];for(let e=0;e<s.length;e++){const r=(e+1)%s.length;c.push(poly3.create([t[e],t[r],s[r],s[e]]))}return c},roundedCuboid=e=>{let{center:r,size:t,roundRadius:s,segments:c}=Object.assign({},{center:[0,0,0],size:[2,2,2],roundRadius:.2,segments:32},e);if(!isNumberArray(r,3))throw new Error("center must be an array of X, Y and Z values");if(!isNumberArray(t,3))throw new Error("size must be an array of X, Y and Z values");if(!t.every(e=>e>0))throw new Error("size values must be greater than zero");if(!isGT(s,0))throw new Error("roundRadius must be greater than zero");if(!isGTE(c,4))throw new Error("segments must be four or more");if(s>(t=t.map(e=>e/2))[0]-EPS||s>t[1]-EPS||s>t[2]-EPS)throw new Error("roundRadius must be smaller then the radius of all dimensions");c=Math.floor(c/4);let o=null,a=null,n=[];for(let e=0;e<=c;e++){const i=createCorners(r,t,s,c,e,!0),u=createCorners(r,t,s,c,e,!1);if(0===e&&(n=n.concat(stitchSides(u,i))),o&&(n=n.concat(stitchCorners(o,i),stitchWalls(o,i))),a&&(n=n.concat(stitchCorners(a,u),stitchWalls(a,u))),e===c){let e=i.map(e=>e[0]);n.push(poly3.create(e)),e=u.map(e=>e[0]),n.push(poly3.create(e))}o=i,a=u}return geom3.create(n)};module.exports=roundedCuboid;
1128
1128
 
1129
1129
  },{"../geometries/geom3":37,"../geometries/poly3":75,"../maths/constants":90,"../maths/utils/trigonometry":168,"../maths/vec2":186,"../maths/vec3":217,"./commonChecks":363}],377:[function(require,module,exports){
1130
- const{EPS:EPS}=require("../maths/constants"),vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),roundedCylinder=e=>{const{center:c,height:r,radius:s,roundRadius:a,segments:t}=Object.assign({},{center:[0,0,0],height:2,radius:1,roundRadius:.2,segments:32},e);if(!isNumberArray(c,3))throw new Error("center must be an array of X, Y and Z values");if(!isGT(r,0))throw new Error("height must be greater then zero");if(!isGT(s,0))throw new Error("radius must be greater then zero");if(!isGT(a,0))throw new Error("roundRadius must be greater then zero");if(a>s-EPS)throw new Error("roundRadius must be smaller then the radius");if(!isGTE(t,4))throw new Error("segments must be four or more");const v=[0,0,-r/2],o=[0,0,r/2],u=vec3.subtract(vec3.create(),o,v);if(2*a>vec3.length(u)-EPS)throw new Error("height must be larger than twice roundRadius");let d;d=Math.abs(u[0])>Math.abs(u[1])?vec3.fromValues(0,1,0):vec3.fromValues(1,0,0);const i=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),u),a),n=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),vec3.cross(vec3.create(),i,d)),s),h=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),vec3.cross(vec3.create(),n,i)),s);vec3.add(v,v,i),vec3.subtract(o,o,i);const l=Math.floor(.25*t),m=e=>{const r=e.map(e=>vec3.add(e,e,c));return poly3.create(r)},p=[],b=vec3.create(),g=vec3.create();let f;for(let e=0;e<=t;e++){const c=2*Math.PI*e/t,r=vec3.add(vec3.create(),vec3.scale(b,n,cos(c)),vec3.scale(g,h,sin(c)));if(e>0){let e,c,s=[];s.push(vec3.add(vec3.create(),v,r)),s.push(vec3.add(vec3.create(),v,f)),s.push(vec3.add(vec3.create(),o,f)),s.push(vec3.add(vec3.create(),o,r)),p.push(m(s));for(let a=0;a<=l;a++){const t=.5*Math.PI*a/l,u=cos(t),d=sin(t);if(a>0){let t;s=[],t=vec3.add(vec3.create(),v,vec3.subtract(b,vec3.scale(b,f,e),vec3.scale(g,i,c))),s.push(t),t=vec3.add(vec3.create(),v,vec3.subtract(b,vec3.scale(b,r,e),vec3.scale(g,i,c))),s.push(t),a<l&&(t=vec3.add(vec3.create(),v,vec3.subtract(b,vec3.scale(b,r,u),vec3.scale(g,i,d))),s.push(t)),t=vec3.add(vec3.create(),v,vec3.subtract(b,vec3.scale(b,f,u),vec3.scale(g,i,d))),s.push(t),p.push(m(s)),s=[],t=vec3.add(vec3.create(),vec3.scale(b,f,e),vec3.scale(g,i,c)),vec3.add(t,t,o),s.push(t),t=vec3.add(vec3.create(),vec3.scale(b,r,e),vec3.scale(g,i,c)),vec3.add(t,t,o),s.push(t),a<l&&(t=vec3.add(vec3.create(),vec3.scale(b,r,u),vec3.scale(g,i,d)),vec3.add(t,t,o),s.push(t)),t=vec3.add(vec3.create(),vec3.scale(b,f,u),vec3.scale(g,i,d)),vec3.add(t,t,o),s.push(t),s.reverse(),p.push(m(s))}e=u,c=d}}f=r}return geom3.create(p)};module.exports=roundedCylinder;
1130
+ const{EPS:EPS,TAU:TAU}=require("../maths/constants"),vec3=require("../maths/vec3"),geom3=require("../geometries/geom3"),poly3=require("../geometries/poly3"),{sin:sin,cos:cos}=require("../maths/utils/trigonometry"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),roundedCylinder=e=>{const{center:c,height:r,radius:s,roundRadius:a,segments:t}=Object.assign({},{center:[0,0,0],height:2,radius:1,roundRadius:.2,segments:32},e);if(!isNumberArray(c,3))throw new Error("center must be an array of X, Y and Z values");if(!isGT(r,0))throw new Error("height must be greater then zero");if(!isGT(s,0))throw new Error("radius must be greater then zero");if(!isGT(a,0))throw new Error("roundRadius must be greater then zero");if(a>s-EPS)throw new Error("roundRadius must be smaller then the radius");if(!isGTE(t,4))throw new Error("segments must be four or more");const v=[0,0,-r/2],o=[0,0,r/2],u=vec3.subtract(vec3.create(),o,v);if(2*a>vec3.length(u)-EPS)throw new Error("height must be larger than twice roundRadius");let d;d=Math.abs(u[0])>Math.abs(u[1])?vec3.fromValues(0,1,0):vec3.fromValues(1,0,0);const i=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),u),a),n=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),vec3.cross(vec3.create(),i,d)),s),l=vec3.scale(vec3.create(),vec3.normalize(vec3.create(),vec3.cross(vec3.create(),n,i)),s);vec3.add(v,v,i),vec3.subtract(o,o,i);const h=Math.floor(.25*t),m=e=>{const r=e.map(e=>vec3.add(e,e,c));return poly3.create(r)},p=[],b=vec3.create(),g=vec3.create();let f;for(let e=0;e<=t;e++){const c=TAU*e/t,r=vec3.add(vec3.create(),vec3.scale(b,n,cos(c)),vec3.scale(g,l,sin(c)));if(e>0){let e,c,s=[];s.push(vec3.add(vec3.create(),v,r)),s.push(vec3.add(vec3.create(),v,f)),s.push(vec3.add(vec3.create(),o,f)),s.push(vec3.add(vec3.create(),o,r)),p.push(m(s));for(let a=0;a<=h;a++){const t=TAU/4*a/h,u=cos(t),d=sin(t);if(a>0){let t;s=[],t=vec3.add(vec3.create(),v,vec3.subtract(b,vec3.scale(b,f,e),vec3.scale(g,i,c))),s.push(t),t=vec3.add(vec3.create(),v,vec3.subtract(b,vec3.scale(b,r,e),vec3.scale(g,i,c))),s.push(t),a<h&&(t=vec3.add(vec3.create(),v,vec3.subtract(b,vec3.scale(b,r,u),vec3.scale(g,i,d))),s.push(t)),t=vec3.add(vec3.create(),v,vec3.subtract(b,vec3.scale(b,f,u),vec3.scale(g,i,d))),s.push(t),p.push(m(s)),s=[],t=vec3.add(vec3.create(),vec3.scale(b,f,e),vec3.scale(g,i,c)),vec3.add(t,t,o),s.push(t),t=vec3.add(vec3.create(),vec3.scale(b,r,e),vec3.scale(g,i,c)),vec3.add(t,t,o),s.push(t),a<h&&(t=vec3.add(vec3.create(),vec3.scale(b,r,u),vec3.scale(g,i,d)),vec3.add(t,t,o),s.push(t)),t=vec3.add(vec3.create(),vec3.scale(b,f,u),vec3.scale(g,i,d)),vec3.add(t,t,o),s.push(t),s.reverse(),p.push(m(s))}e=u,c=d}}f=r}return geom3.create(p)};module.exports=roundedCylinder;
1131
1131
 
1132
1132
  },{"../geometries/geom3":37,"../geometries/poly3":75,"../maths/constants":90,"../maths/utils/trigonometry":168,"../maths/vec3":217,"./commonChecks":363}],378:[function(require,module,exports){
1133
- const{EPS:EPS}=require("../maths/constants"),vec2=require("../maths/vec2"),geom2=require("../geometries/geom2"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),roundedRectangle=e=>{let{center:r,size:a,roundRadius:t,segments:s}=Object.assign({},{center:[0,0],size:[2,2],roundRadius:.2,segments:32},e);if(!isNumberArray(r,2))throw new Error("center must be an array of X and Y values");if(!isNumberArray(a,2))throw new Error("size must be an array of X and Y values");if(!a.every(e=>e>0))throw new Error("size values must be greater than zero");if(!isGT(t,0))throw new Error("roundRadius must be greater than zero");if(!isGTE(s,4))throw new Error("segments must be four or more");if(t>(a=a.map(e=>e/2))[0]-EPS||t>a[1]-EPS)throw new Error("roundRadius must be smaller then the radius of all dimensions");const c=Math.floor(s/4),o=vec2.add(vec2.create(),r,[a[0]-t,a[1]-t]),n=vec2.add(vec2.create(),r,[t-a[0],a[1]-t]),i=vec2.add(vec2.create(),r,[t-a[0],t-a[1]]),u=vec2.add(vec2.create(),r,[a[0]-t,t-a[1]]),d=[],v=[],m=[],h=[];for(let e=0;e<=c;e++){const r=Math.PI/2*e/c,a=vec2.fromAngleRadians(vec2.create(),r);vec2.scale(a,a,t),d.push(vec2.add(vec2.create(),o,a)),vec2.rotate(a,a,vec2.create(),Math.PI/2),v.push(vec2.add(vec2.create(),n,a)),vec2.rotate(a,a,vec2.create(),Math.PI/2),m.push(vec2.add(vec2.create(),i,a)),vec2.rotate(a,a,vec2.create(),Math.PI/2),h.push(vec2.add(vec2.create(),u,a))}return geom2.fromPoints(d.concat(v,m,h))};module.exports=roundedRectangle;
1133
+ const{EPS:EPS,TAU:TAU}=require("../maths/constants"),vec2=require("../maths/vec2"),geom2=require("../geometries/geom2"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),roundedRectangle=e=>{let{center:r,size:a,roundRadius:t,segments:s}=Object.assign({},{center:[0,0],size:[2,2],roundRadius:.2,segments:32},e);if(!isNumberArray(r,2))throw new Error("center must be an array of X and Y values");if(!isNumberArray(a,2))throw new Error("size must be an array of X and Y values");if(!a.every(e=>e>0))throw new Error("size values must be greater than zero");if(!isGT(t,0))throw new Error("roundRadius must be greater than zero");if(!isGTE(s,4))throw new Error("segments must be four or more");if(t>(a=a.map(e=>e/2))[0]-EPS||t>a[1]-EPS)throw new Error("roundRadius must be smaller then the radius of all dimensions");const c=Math.floor(s/4),o=vec2.add(vec2.create(),r,[a[0]-t,a[1]-t]),n=vec2.add(vec2.create(),r,[t-a[0],a[1]-t]),i=vec2.add(vec2.create(),r,[t-a[0],t-a[1]]),u=vec2.add(vec2.create(),r,[a[0]-t,t-a[1]]),d=[],v=[],m=[],h=[];for(let e=0;e<=c;e++){const r=TAU/4*e/c,a=vec2.fromAngleRadians(vec2.create(),r);vec2.scale(a,a,t),d.push(vec2.add(vec2.create(),o,a)),vec2.rotate(a,a,vec2.create(),TAU/4),v.push(vec2.add(vec2.create(),n,a)),vec2.rotate(a,a,vec2.create(),TAU/4),m.push(vec2.add(vec2.create(),i,a)),vec2.rotate(a,a,vec2.create(),TAU/4),h.push(vec2.add(vec2.create(),u,a))}return geom2.fromPoints(d.concat(v,m,h))};module.exports=roundedRectangle;
1134
1134
 
1135
1135
  },{"../geometries/geom2":22,"../maths/constants":90,"../maths/vec2":186,"./commonChecks":363}],379:[function(require,module,exports){
1136
1136
  const ellipsoid=require("./ellipsoid"),{isGT:isGT}=require("./commonChecks"),sphere=e=>{let{center:s,radius:r,segments:i,axes:t}=Object.assign({},{center:[0,0,0],radius:1,segments:32,axes:[[1,0,0],[0,-1,0],[0,0,1]]},e);if(!isGT(r,0))throw new Error("radius must be greater than zero");return ellipsoid({center:s,radius:r=[r,r,r],segments:i,axes:t})};module.exports=sphere;
@@ -1139,12 +1139,12 @@ const ellipsoid=require("./ellipsoid"),{isGT:isGT}=require("./commonChecks"),sph
1139
1139
  const rectangle=require("./rectangle"),{isGT:isGT}=require("./commonChecks"),square=e=>{let{center:r,size:s}=Object.assign({},{center:[0,0],size:2},e);if(!isGT(s,0))throw new Error("size must be greater than zero");return rectangle({center:r,size:s=[s,s]})};module.exports=square;
1140
1140
 
1141
1141
  },{"./commonChecks":363,"./rectangle":375}],381:[function(require,module,exports){
1142
- const vec2=require("../maths/vec2"),geom2=require("../geometries/geom2"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),getRadiusRatio=(e,r)=>e>0&&r>1&&r<e/2?Math.cos(Math.PI*r/e)/Math.cos(Math.PI*(r-1)/e):0,getPoints=(e,r,t,s)=>{const o=2*Math.PI/e,i=[];for(let a=0;a<e;a++){const e=vec2.fromAngleRadians(vec2.create(),o*a+t);vec2.scale(e,e,r),vec2.add(e,s,e),i.push(e)}return i},star=e=>{let{center:r,vertices:t,outerRadius:s,innerRadius:o,density:i,startAngle:a}=Object.assign({},{center:[0,0],vertices:5,outerRadius:1,innerRadius:0,density:2,startAngle:0},e);if(!isNumberArray(r,2))throw new Error("center must be an array of X and Y values");if(!isGTE(t,2))throw new Error("vertices must be two or more");if(!isGT(s,0))throw new Error("outerRadius must be greater than zero");if(!isGTE(o,0))throw new Error("innerRadius must be greater than zero");if(!isGTE(a,0))throw new Error("startAngle must be greater than zero");if(t=Math.floor(t),i=Math.floor(i),a%=2*Math.PI,0===o){if(!isGTE(i,2))throw new Error("density must be two or more");o=s*getRadiusRatio(t,i)}const n=vec2.clone(r),u=getPoints(t,s,a,n),c=getPoints(t,o,a+Math.PI/t,n),h=[];for(let e=0;e<t;e++)h.push(u[e]),h.push(c[e]);return geom2.fromPoints(h)};module.exports=star;
1142
+ const{TAU:TAU}=require("../maths/constants"),vec2=require("../maths/vec2"),geom2=require("../geometries/geom2"),{isGT:isGT,isGTE:isGTE,isNumberArray:isNumberArray}=require("./commonChecks"),getRadiusRatio=(e,r)=>e>0&&r>1&&r<e/2?Math.cos(Math.PI*r/e)/Math.cos(Math.PI*(r-1)/e):0,getPoints=(e,r,t,s)=>{const o=TAU/e,i=[];for(let a=0;a<e;a++){const e=vec2.fromAngleRadians(vec2.create(),o*a+t);vec2.scale(e,e,r),vec2.add(e,s,e),i.push(e)}return i},star=e=>{let{center:r,vertices:t,outerRadius:s,innerRadius:o,density:i,startAngle:a}=Object.assign({},{center:[0,0],vertices:5,outerRadius:1,innerRadius:0,density:2,startAngle:0},e);if(!isNumberArray(r,2))throw new Error("center must be an array of X and Y values");if(!isGTE(t,2))throw new Error("vertices must be two or more");if(!isGT(s,0))throw new Error("outerRadius must be greater than zero");if(!isGTE(o,0))throw new Error("innerRadius must be greater than zero");if(!isGTE(a,0))throw new Error("startAngle must be greater than zero");if(t=Math.floor(t),i=Math.floor(i),a%=TAU,0===o){if(!isGTE(i,2))throw new Error("density must be two or more");o=s*getRadiusRatio(t,i)}const n=vec2.clone(r),u=getPoints(t,s,a,n),c=getPoints(t,o,a+Math.PI/t,n),h=[];for(let e=0;e<t;e++)h.push(u[e]),h.push(c[e]);return geom2.fromPoints(h)};module.exports=star;
1143
1143
 
1144
- },{"../geometries/geom2":22,"../maths/vec2":186,"./commonChecks":363}],382:[function(require,module,exports){
1145
- const extrudeRotate=require("../operations/extrusions/extrudeRotate"),{rotate:rotate}=require("../operations/transforms/rotate"),{translate:translate}=require("../operations/transforms/translate"),circle=require("./circle"),{isGT:isGT,isGTE:isGTE}=require("./commonChecks"),torus=e=>{const r={innerRadius:1,innerSegments:32,outerRadius:4,outerSegments:32,innerRotation:0,startAngle:0,outerRotation:2*Math.PI},{innerRadius:t,innerSegments:o,outerRadius:n,outerSegments:s,innerRotation:i,startAngle:a,outerRotation:u}=Object.assign({},r,e);if(!isGT(t,0))throw new Error("innerRadius must be greater than zero");if(!isGTE(o,3))throw new Error("innerSegments must be three or more");if(!isGT(n,0))throw new Error("outerRadius must be greater than zero");if(!isGTE(s,3))throw new Error("outerSegments must be three or more");if(!isGTE(a,0))throw new Error("startAngle must be positive");if(!isGT(u,0))throw new Error("outerRotation must be greater than zero");if(t>=n)throw new Error("inner circle is two large to rotate about the outer circle");let m=circle({radius:t,segments:o});return 0!==i&&(m=rotate([0,0,i],m)),m=translate([n,0],m),extrudeRotate({startAngle:a,angle:u,segments:s},m)};module.exports=torus;
1144
+ },{"../geometries/geom2":22,"../maths/constants":90,"../maths/vec2":186,"./commonChecks":363}],382:[function(require,module,exports){
1145
+ const{TAU:TAU}=require("../maths/constants"),extrudeRotate=require("../operations/extrusions/extrudeRotate"),{rotate:rotate}=require("../operations/transforms/rotate"),{translate:translate}=require("../operations/transforms/translate"),circle=require("./circle"),{isGT:isGT,isGTE:isGTE}=require("./commonChecks"),torus=e=>{const r={innerRadius:1,innerSegments:32,outerRadius:4,outerSegments:32,innerRotation:0,startAngle:0,outerRotation:TAU},{innerRadius:t,innerSegments:o,outerRadius:n,outerSegments:s,innerRotation:i,startAngle:a,outerRotation:u}=Object.assign({},r,e);if(!isGT(t,0))throw new Error("innerRadius must be greater than zero");if(!isGTE(o,3))throw new Error("innerSegments must be three or more");if(!isGT(n,0))throw new Error("outerRadius must be greater than zero");if(!isGTE(s,3))throw new Error("outerSegments must be three or more");if(!isGTE(a,0))throw new Error("startAngle must be positive");if(!isGT(u,0))throw new Error("outerRotation must be greater than zero");if(t>=n)throw new Error("inner circle is two large to rotate about the outer circle");let m=circle({radius:t,segments:o});return 0!==i&&(m=rotate([0,0,i],m)),m=translate([n,0],m),extrudeRotate({startAngle:a,angle:u,segments:s},m)};module.exports=torus;
1146
1146
 
1147
- },{"../operations/extrusions/extrudeRotate":310,"../operations/transforms/rotate":357,"../operations/transforms/translate":360,"./circle":362,"./commonChecks":363}],383:[function(require,module,exports){
1147
+ },{"../maths/constants":90,"../operations/extrusions/extrudeRotate":310,"../operations/transforms/rotate":357,"../operations/transforms/translate":360,"./circle":362,"./commonChecks":363}],383:[function(require,module,exports){
1148
1148
  const{NEPS:NEPS}=require("../maths/constants"),vec2=require("../maths/vec2"),geom2=require("../geometries/geom2"),{isNumberArray:isNumberArray}=require("./commonChecks"),solveAngleFromSSS=(e,r,t)=>Math.acos((e*e+r*r-t*t)/(2*e*r)),solveSideFromSAS=(e,r,t)=>r>NEPS?Math.sqrt(e*e+t*t-2*e*t*Math.cos(r)):Math.sqrt((e-t)*(e-t)+e*t*r*r*(1-r*r/12)),solveAAA=e=>{if(Math.abs(e[0]+e[1]+e[2]-Math.PI)>NEPS)throw new Error("AAA triangles require angles that sum to PI");const r=e[0],t=e[1],s=Math.PI-r-t,a=1/Math.sin(s)*Math.sin(r),n=1/Math.sin(s)*Math.sin(t);return createTriangle(r,t,s,a,n,1)},solveAAS=e=>{const r=e[0],t=e[1],s=Math.PI+NEPS-r-t;if(s<NEPS)throw new Error("AAS triangles require angles that sum to PI");const a=e[2],n=a/Math.sin(r)*Math.sin(t),o=a/Math.sin(r)*Math.sin(s);return createTriangle(r,t,s,a,n,o)},solveASA=e=>{const r=e[0],t=e[2],s=Math.PI+NEPS-r-t;if(s<NEPS)throw new Error("ASA triangles require angles that sum to PI");const a=e[1],n=a/Math.sin(s)*Math.sin(r),o=a/Math.sin(s)*Math.sin(t);return createTriangle(r,t,s,n,o,a)},solveSAS=e=>{const r=e[0],t=e[1],s=e[2],a=solveSideFromSAS(r,t,s),n=solveAngleFromSSS(a,r,s),o=Math.PI-n-t;return createTriangle(n,t,o,s,a,r)},solveSSA=e=>{const r=e[0],t=e[1],s=e[2],a=Math.asin(t*Math.sin(s)/r),n=Math.PI-a-s,o=r/Math.sin(s)*Math.sin(n);return createTriangle(a,n,s,t,o,r)},solveSSS=e=>{const r=e[1],t=e[2],s=e[0];if(r+t<=s||t+s<=r||s+r<=t)throw new Error("SSS triangle is incorrect, as the longest side is longer than the sum of the other sides");const a=solveAngleFromSSS(t,s,r),n=solveAngleFromSSS(s,r,t),o=Math.PI-a-n;return createTriangle(a,n,o,r,t,s)},createTriangle=(e,r,t,s,a,n)=>{const o=vec2.fromValues(0,0),i=vec2.fromValues(n,0),S=vec2.fromValues(s,0);return vec2.add(S,vec2.rotate(S,S,[0,0],Math.PI-r),i),geom2.fromPoints([o,i,S])},triangle=e=>{let{type:r,values:t}=Object.assign({},{type:"SSS",values:[1,1,1]},e);if("string"!=typeof r)throw new Error("triangle type must be a string");if("A"!==(r=r.toUpperCase())[0]&&"S"!==r[0]||"A"!==r[1]&&"S"!==r[1]||"A"!==r[2]&&"S"!==r[2])throw new Error("triangle type must contain three letters; A or S");if(!isNumberArray(t,3))throw new Error("triangle values must contain three values");if(!t.every(e=>e>0))throw new Error("triangle values must be greater than zero");switch(r){case"AAA":return solveAAA(t);case"AAS":return solveAAS(t);case"ASA":return solveASA(t);case"SAS":return solveSAS(t);case"SSA":return solveSSA(t);case"SSS":return solveSSS(t);default:throw new Error("invalid triangle type, try again")}};module.exports=triangle;
1149
1149
 
1150
1150
  },{"../geometries/geom2":22,"../maths/constants":90,"../maths/vec2":186,"./commonChecks":363}],384:[function(require,module,exports){
@@ -1187,7 +1187,7 @@ const padArrayToLength=(r,e,o)=>{for(r=r.slice();r.length<o;)r.push(e);return r}
1187
1187
  const radToDeg=o=>57.29577951308232*o;module.exports=radToDeg;
1188
1188
 
1189
1189
  },{}],397:[function(require,module,exports){
1190
- const radiusToSegments=(t,e,a)=>{const s=e>0?2*t*Math.PI/e:0,o=a>0?2*Math.PI/a:0;return Math.ceil(Math.max(s,o,4))};module.exports=radiusToSegments;
1190
+ const{TAU:TAU}=require("../maths/constants"),radiusToSegments=(t,e,s)=>{const a=e>0?t*TAU/e:0,n=s>0?TAU/s:0;return Math.ceil(Math.max(a,n,4))};module.exports=radiusToSegments;
1191
1191
 
1192
- },{}]},{},[88])(88)
1192
+ },{"../maths/constants":90}]},{},[88])(88)
1193
1193
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jscad/modeling",
3
- "version": "2.9.6",
3
+ "version": "2.10.0",
4
4
  "description": "Constructive Solid Geometry (CSG) Library for JSCAD",
5
5
  "homepage": "https://openjscad.xyz/",
6
6
  "repository": "https://github.com/jscad/OpenJSCAD.org",
@@ -61,5 +61,5 @@
61
61
  "nyc": "15.1.0",
62
62
  "uglifyify": "5.0.2"
63
63
  },
64
- "gitHead": "9768af96e5da00cd113c00ddeb0f6046707819b1"
64
+ "gitHead": "13572067545460affd53b64b3ab834a39af8c7e5"
65
65
  }
@@ -1,3 +1,4 @@
1
+ const { TAU } = require('../../maths/constants')
1
2
  const vec2 = require('../../maths/vec2')
2
3
 
3
4
  const fromPoints = require('./fromPoints')
@@ -12,7 +13,7 @@ const toPoints = require('./toPoints')
12
13
  * @param {vec2} [options.radius=[0,0]] - radius of arc (X and Y)
13
14
  * @param {Number} [options.xaxisrotation=0] - rotation (RADIANS) of the X axis of the arc with respect to the X axis of the coordinate system
14
15
  * @param {Boolean} [options.clockwise=false] - draw an arc clockwise with respect to the center point
15
- * @param {Boolean} [options.large=false] - draw an arc longer than PI radians
16
+ * @param {Boolean} [options.large=false] - draw an arc longer than TAU / 2 radians
16
17
  * @param {Number} [options.segments=16] - number of segments per full rotation
17
18
  * @param {path2} geometry - the path of which to append the arc
18
19
  * @returns {path2} a new path with the appended points
@@ -111,15 +112,15 @@ const appendArc = (options, geometry) => {
111
112
  const theta1 = vec2.angleRadians(vector1)
112
113
  const theta2 = vec2.angleRadians(vector2)
113
114
  let deltatheta = theta2 - theta1
114
- deltatheta = deltatheta % (2 * Math.PI)
115
+ deltatheta = deltatheta % TAU
115
116
  if ((!sweepFlag) && (deltatheta > 0)) {
116
- deltatheta -= 2 * Math.PI
117
+ deltatheta -= TAU
117
118
  } else if ((sweepFlag) && (deltatheta < 0)) {
118
- deltatheta += 2 * Math.PI
119
+ deltatheta += TAU
119
120
  }
120
121
 
121
122
  // Ok, we have the center point and angle range (from theta1, deltatheta radians) so we can create the ellipse
122
- let numsteps = Math.ceil(Math.abs(deltatheta) / (2 * Math.PI) * segments) + 1
123
+ let numsteps = Math.ceil(Math.abs(deltatheta) / TAU * segments) + 1
123
124
  if (numsteps < 1) numsteps = 1
124
125
  for (let step = 1; step < numsteps; step++) {
125
126
  const theta = theta1 + step / numsteps * deltatheta
@@ -1,5 +1,7 @@
1
1
  const test = require('ava')
2
2
 
3
+ const { TAU } = require('../../maths/constants')
4
+
3
5
  const { appendArc, fromPoints, toPoints } = require('./index')
4
6
 
5
7
  const { comparePoints } = require('../../../test/helpers/')
@@ -48,7 +50,7 @@ test('appendArc: appending to a path produces a new path', (t) => {
48
50
  t.is(pts.length, 16)
49
51
 
50
52
  // test xaxisrotation
51
- obs = appendArc({ endpoint: [12, -22], radius: [15, -20], xaxisrotation: Math.PI / 2 }, p2)
53
+ obs = appendArc({ endpoint: [12, -22], radius: [15, -20], xaxisrotation: TAU / 4 }, p2)
52
54
  pts = toPoints(obs)
53
55
  exp = [
54
56
  [27, -22],
@@ -1,3 +1,4 @@
1
+ const { TAU } = require('../../maths/constants')
1
2
  const vec2 = require('../../maths/vec2')
2
3
  const vec3 = require('../../maths/vec2')
3
4
 
@@ -117,7 +118,7 @@ const appendBezier = (options, geometry) => {
117
118
 
118
119
  // subdivide each segment until the angle at each vertex becomes small enough:
119
120
  let subdivideBase = 1
120
- const maxangle = Math.PI * 2 / segments
121
+ const maxangle = TAU / segments
121
122
  const maxsinangle = Math.sin(maxangle)
122
123
  while (subdivideBase < newpoints.length - 1) {
123
124
  const dir1 = vec2.subtract(v0, newpoints[subdivideBase], newpoints[subdivideBase - 1])
@@ -10,7 +10,7 @@ const mat4 = require('../../maths/mat4')
10
10
  * @alias module:modeling/geometries/path2.transform
11
11
  *
12
12
  * @example
13
- * let newpath = transform(fromZRotation(Math.PI / 4), path)
13
+ * let newpath = transform(fromZRotation(TAU / 8), path)
14
14
  */
15
15
  const transform = (matrix, geometry) => {
16
16
  const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
package/src/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export * as booleans from './operations/booleans'
11
11
  export * as expansions from './operations/expansions'
12
12
  export * as extrusions from './operations/extrusions'
13
13
  export * as hulls from './operations/hulls'
14
+ export * as modifiers from './operations/modifiers'
14
15
  export * as transforms from './operations/transforms'
15
16
 
16
17
  export as namespace modeling
@@ -24,8 +24,18 @@ const NEPS = 1e-13
24
24
  // for comparing coplanar polygons, as provided by the sphere primitive at high
25
25
  // segmentation. NEPS is for 64 bit Number values.
26
26
 
27
+ /**
28
+ * The TAU property represents the ratio of the circumference of a circle to its radius.
29
+ * Approximately 6.28318530717958647692
30
+ * @default
31
+ * @example
32
+ * const { TAU } = require('@jscad/modeling').maths.constants
33
+ */
34
+ const TAU = Math.PI * 2
35
+
27
36
  module.exports = {
28
37
  EPS,
29
38
  NEPS,
39
+ TAU,
30
40
  spatialResolution
31
41
  }
@@ -17,7 +17,7 @@ const identity = require('./identity')
17
17
  * @returns {mat4} out
18
18
  * @alias module:modeling/maths/mat4.fromRotation
19
19
  * @example
20
- * let matrix = fromRotation(create(), Math.PI / 2, [0, 0, 3])
20
+ * let matrix = fromRotation(create(), TAU / 4, [0, 0, 3])
21
21
  */
22
22
  const fromRotation = (out, rad, axis) => {
23
23
  let [x, y, z] = axis
@@ -13,7 +13,7 @@ const { sin, cos } = require('../utils/trigonometry')
13
13
  * @returns {mat4} out
14
14
  * @alias module:modeling/maths/mat4.fromTaitBryanRotation
15
15
  * @example
16
- * let matrix = fromTaitBryanRotation(create(), Math.PI / 2, 0, Math.PI)
16
+ * let matrix = fromTaitBryanRotation(create(), TAU / 4, 0, TAU / 2)
17
17
  */
18
18
  const fromTaitBryanRotation = (out, yaw, pitch, roll) => {
19
19
  // precompute sines and cosines of Euler angles
@@ -12,7 +12,7 @@ const { sin, cos } = require('../utils/trigonometry')
12
12
  * @returns {mat4} out
13
13
  * @alias module:modeling/maths/mat4.fromXRotation
14
14
  * @example
15
- * let matrix = fromXRotation(create(), Math.PI / 2)
15
+ * let matrix = fromXRotation(create(), TAU / 4)
16
16
  */
17
17
  const fromXRotation = (out, radians) => {
18
18
  const s = sin(radians)
@@ -12,7 +12,7 @@ const { sin, cos } = require('../utils/trigonometry')
12
12
  * @returns {mat4} out
13
13
  * @alias module:modeling/maths/mat4.fromYRotation
14
14
  * @example
15
- * let matrix = fromYRotation(create(), Math.PI / 2)
15
+ * let matrix = fromYRotation(create(), TAU / 4)
16
16
  */
17
17
  const fromYRotation = (out, radians) => {
18
18
  const s = sin(radians)
@@ -12,7 +12,7 @@ const { sin, cos } = require('../utils/trigonometry')
12
12
  * @returns {mat4} out
13
13
  * @alias module:modeling/maths/mat4.fromZRotation
14
14
  * @example
15
- * let matrix = fromZRotation(create(), Math.PI / 2)
15
+ * let matrix = fromZRotation(create(), TAU / 4)
16
16
  */
17
17
  const fromZRotation = (out, radians) => {
18
18
  const s = sin(radians)
@@ -1,8 +1,11 @@
1
1
  const test = require('ava')
2
+
3
+ const { TAU } = require('../constants')
4
+ const vec3 = require('../vec3/index')
5
+
2
6
  const { create, invert, fromTranslation, fromXRotation } = require('./index')
3
7
 
4
8
  const { compareVectors } = require('../../../test/helpers/index')
5
- const vec3 = require('../vec3/index')
6
9
 
7
10
  test('mat4: invert() translate ', (t) => {
8
11
  const matrix = fromTranslation(create(), [10, 10, 0])
@@ -17,7 +20,7 @@ test('mat4: invert() translate ', (t) => {
17
20
  })
18
21
 
19
22
  test('mat4: invert() rotate ', (t) => {
20
- const matrix = fromXRotation(create(), Math.PI / 2)
23
+ const matrix = fromXRotation(create(), TAU / 4)
21
24
  const matrixInv = invert(create(), matrix)
22
25
 
23
26
  const vec1 = [10, 10, 10]