@jscad/modeling 2.9.4 → 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 (106) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +12 -2
  3. package/dist/jscad-modeling.min.js +148 -151
  4. package/package.json +2 -2
  5. package/src/colors/colorize.d.ts +6 -5
  6. package/src/geometries/geom2/type.d.ts +3 -2
  7. package/src/geometries/geom3/type.d.ts +3 -2
  8. package/src/geometries/path2/appendArc.js +6 -5
  9. package/src/geometries/path2/appendArc.test.js +3 -1
  10. package/src/geometries/path2/appendBezier.js +2 -1
  11. package/src/geometries/path2/appendPoints.js +3 -12
  12. package/src/geometries/path2/appendPoints.test.js +16 -0
  13. package/src/geometries/path2/concat.js +9 -8
  14. package/src/geometries/path2/concat.test.js +13 -7
  15. package/src/geometries/path2/transform.js +1 -1
  16. package/src/geometries/path2/type.d.ts +3 -2
  17. package/src/geometries/poly3/measureBoundingSphere.d.ts +2 -2
  18. package/src/geometries/poly3/measureBoundingSphere.js +46 -8
  19. package/src/geometries/poly3/measureBoundingSphere.test.js +16 -26
  20. package/src/geometries/poly3/type.d.ts +3 -2
  21. package/src/geometries/types.d.ts +4 -2
  22. package/src/index.d.ts +1 -0
  23. package/src/maths/constants.js +10 -0
  24. package/src/maths/mat4/fromRotation.js +10 -8
  25. package/src/maths/mat4/fromTaitBryanRotation.js +9 -7
  26. package/src/maths/mat4/fromXRotation.js +5 -3
  27. package/src/maths/mat4/fromYRotation.js +5 -3
  28. package/src/maths/mat4/fromZRotation.js +5 -3
  29. package/src/maths/mat4/invert.test.js +5 -2
  30. package/src/maths/mat4/isOnlyTransformScale.js +1 -1
  31. package/src/maths/mat4/isOnlyTransformScale.test.js +3 -1
  32. package/src/maths/mat4/rotate.js +9 -5
  33. package/src/maths/mat4/rotateX.js +4 -2
  34. package/src/maths/mat4/rotateY.js +4 -2
  35. package/src/maths/mat4/rotateZ.js +4 -2
  36. package/src/maths/mat4/translate.test.js +2 -3
  37. package/src/maths/rotation.test.js +5 -2
  38. package/src/maths/utils/index.d.ts +1 -0
  39. package/src/maths/utils/index.js +2 -0
  40. package/src/{utils → maths/utils}/trigonometry.d.ts +0 -0
  41. package/src/{utils → maths/utils}/trigonometry.js +7 -7
  42. package/src/{utils → maths/utils}/trigonometry.test.js +10 -8
  43. package/src/maths/vec2/distance.js +1 -1
  44. package/src/maths/vec2/fromAngleDegrees.js +1 -1
  45. package/src/maths/vec2/fromAngleRadians.js +4 -2
  46. package/src/maths/vec2/fromAngleRadians.test.js +4 -1
  47. package/src/maths/vec2/length.js +1 -1
  48. package/src/maths/vec2/length.test.js +0 -10
  49. package/src/maths/vec2/normal.js +3 -1
  50. package/src/maths/vec2/rotate.test.js +4 -1
  51. package/src/maths/vec3/angle.js +2 -2
  52. package/src/maths/vec3/angle.test.js +0 -12
  53. package/src/maths/vec3/distance.js +1 -1
  54. package/src/maths/vec3/length.js +1 -1
  55. package/src/maths/vec3/length.test.js +0 -10
  56. package/src/maths/vec3/rotateX.test.js +4 -1
  57. package/src/maths/vec3/rotateY.test.js +4 -1
  58. package/src/maths/vec3/rotateZ.test.js +4 -1
  59. package/src/operations/booleans/trees/PolygonTreeNode.js +2 -2
  60. package/src/operations/expansions/expand.test.js +3 -1
  61. package/src/operations/expansions/expandShell.js +4 -4
  62. package/src/operations/expansions/offsetFromPoints.js +2 -2
  63. package/src/operations/extrusions/extrudeFromSlices.test.js +3 -2
  64. package/src/operations/extrusions/extrudeLinear.test.js +5 -3
  65. package/src/operations/extrusions/extrudeRectangular.js +1 -1
  66. package/src/operations/extrusions/extrudeRectangular.test.js +5 -3
  67. package/src/operations/extrusions/extrudeRotate.js +14 -13
  68. package/src/operations/extrusions/extrudeRotate.test.js +49 -47
  69. package/src/operations/extrusions/project.test.js +2 -2
  70. package/src/operations/extrusions/slice/calculatePlane.test.js +3 -2
  71. package/src/operations/extrusions/slice/index.js +2 -0
  72. package/src/operations/extrusions/slice/repair.js +1 -1
  73. package/src/operations/modifiers/generalize.d.ts +12 -0
  74. package/src/operations/modifiers/generalize.test.js +3 -1
  75. package/src/operations/modifiers/index.d.ts +2 -0
  76. package/src/operations/modifiers/insertTjunctions.js +34 -35
  77. package/src/operations/modifiers/snap.d.ts +6 -0
  78. package/src/operations/modifiers/snap.test.js +5 -3
  79. package/src/operations/transforms/rotate.js +1 -1
  80. package/src/operations/transforms/rotate.test.js +13 -11
  81. package/src/operations/transforms/transform.js +1 -1
  82. package/src/primitives/arc.js +8 -8
  83. package/src/primitives/arc.test.js +9 -8
  84. package/src/primitives/circle.js +4 -2
  85. package/src/primitives/circle.test.js +12 -4
  86. package/src/primitives/cylinderElliptic.js +13 -11
  87. package/src/primitives/cylinderElliptic.test.js +9 -2
  88. package/src/primitives/ellipse.js +11 -11
  89. package/src/primitives/ellipse.test.js +12 -4
  90. package/src/primitives/ellipsoid.js +4 -3
  91. package/src/primitives/geodesicSphere.js +3 -2
  92. package/src/primitives/roundedCuboid.js +10 -8
  93. package/src/primitives/roundedCylinder.js +4 -4
  94. package/src/primitives/roundedRectangle.js +5 -5
  95. package/src/primitives/star.js +3 -2
  96. package/src/primitives/torus.js +4 -2
  97. package/src/primitives/torus.test.js +11 -5
  98. package/src/primitives/triangle.test.js +2 -1
  99. package/src/utils/degToRad.test.js +5 -5
  100. package/src/utils/index.d.ts +0 -1
  101. package/src/utils/index.js +1 -3
  102. package/src/utils/radToDeg.test.js +6 -6
  103. package/src/utils/radiusToSegments.js +6 -4
  104. package/src/utils/radiusToSegments.test.js +5 -3
  105. package/src/maths/mat4/constants.d.ts +0 -1
  106. package/src/maths/mat4/constants.js +0 -5
@@ -1,4 +1,4 @@
1
- const { EPS } = require('../maths/constants')
1
+ const { EPS, TAU } = require('../maths/constants')
2
2
 
3
3
  const vec2 = require('../maths/vec2')
4
4
 
@@ -12,7 +12,7 @@ const { isGT, isGTE, isNumberArray } = require('./commonChecks')
12
12
  * @param {Array} [options.center=[0,0]] - center of arc
13
13
  * @param {Number} [options.radius=1] - radius of arc
14
14
  * @param {Number} [options.startAngle=0] - starting angle of the arc, in radians
15
- * @param {Number} [options.endAngle=Math.PI*2] - ending angle of the arc, in radians
15
+ * @param {Number} [options.endAngle=TAU] - ending angle of the arc, in radians
16
16
  * @param {Number} [options.segments=32] - number of segments to create per full rotation
17
17
  * @param {Boolean} [options.makeTangent=false] - adds line segments at both ends of the arc to ensure that the gradients at the edges are tangent
18
18
  * @returns {path2} new 2D path
@@ -23,7 +23,7 @@ const arc = (options) => {
23
23
  center: [0, 0],
24
24
  radius: 1,
25
25
  startAngle: 0,
26
- endAngle: (Math.PI * 2),
26
+ endAngle: TAU,
27
27
  makeTangent: false,
28
28
  segments: 32
29
29
  }
@@ -35,15 +35,15 @@ const arc = (options) => {
35
35
  if (!isGTE(endAngle, 0)) throw new Error('endAngle must be positive')
36
36
  if (!isGTE(segments, 4)) throw new Error('segments must be four or more')
37
37
 
38
- startAngle = startAngle % (Math.PI * 2)
39
- endAngle = endAngle % (Math.PI * 2)
38
+ startAngle = startAngle % TAU
39
+ endAngle = endAngle % TAU
40
40
 
41
- let rotation = (Math.PI * 2)
41
+ let rotation = TAU
42
42
  if (startAngle < endAngle) {
43
43
  rotation = endAngle - startAngle
44
44
  }
45
45
  if (startAngle > endAngle) {
46
- rotation = endAngle + ((Math.PI * 2) - startAngle)
46
+ rotation = endAngle + (TAU - startAngle)
47
47
  }
48
48
 
49
49
  const minangle = Math.acos(((radius * radius) + (radius * radius) - (EPS * EPS)) / (2 * radius * radius))
@@ -59,7 +59,7 @@ const arc = (options) => {
59
59
  pointArray.push(point)
60
60
  } else {
61
61
  // note: add one additional step to acheive full rotation
62
- const numsteps = Math.max(1, Math.floor(segments * (rotation / (Math.PI * 2)))) + 1
62
+ const numsteps = Math.max(1, Math.floor(segments * (rotation / TAU))) + 1
63
63
  let edgestepsize = numsteps * 0.5 / rotation // step size for half a degree
64
64
  if (edgestepsize > 0.25) edgestepsize = 0.25
65
65
 
@@ -1,11 +1,12 @@
1
1
  const test = require('ava')
2
2
 
3
- const { arc } = require('./index')
4
-
3
+ const { TAU } = require('../maths/constants')
5
4
  const path2 = require('../geometries/path2')
6
5
 
7
6
  const comparePoints = require('../../test/helpers/comparePoints')
8
7
 
8
+ const { arc } = require('./index')
9
+
9
10
  test('arc (defaults)', (t) => {
10
11
  const geometry = arc()
11
12
  const obs = path2.toPoints(geometry)
@@ -86,7 +87,7 @@ test('arc (options)', (t) => {
86
87
  [0.9350162426854147, -0.35460488704253595],
87
88
  [1, -2.4492935982947064e-16]
88
89
  ]
89
- geometry = arc({ startAngle: Math.PI / 2, segments: 16 })
90
+ geometry = arc({ startAngle: TAU / 4, segments: 16 })
90
91
  obs = path2.toPoints(geometry)
91
92
 
92
93
  t.notThrows(() => path2.validate(geometry))
@@ -102,7 +103,7 @@ test('arc (options)', (t) => {
102
103
  [0.30901699437494745, 0.9510565162951535],
103
104
  [6.123233995736766e-17, 1]
104
105
  ]
105
- geometry = arc({ endAngle: Math.PI / 2, segments: 16 })
106
+ geometry = arc({ endAngle: TAU / 4, segments: 16 })
106
107
  obs = path2.toPoints(geometry)
107
108
 
108
109
  t.notThrows(() => path2.validate(geometry))
@@ -167,7 +168,7 @@ test('arc (rotations)', (t) => {
167
168
  [-0.9510565162951535, 0.3090169943749475],
168
169
  [-1, 1.2246467991473532e-16]
169
170
  ]
170
- let geometry = arc({ startAngle: Math.PI / 2, endAngle: Math.PI, segments: 16 })
171
+ let geometry = arc({ startAngle: TAU / 4, endAngle: TAU / 2, segments: 16 })
171
172
  let obs = path2.toPoints(geometry)
172
173
 
173
174
  t.notThrows(() => path2.validate(geometry))
@@ -186,7 +187,7 @@ test('arc (rotations)', (t) => {
186
187
  [0.9396926207859084, -0.3420201433256686],
187
188
  [1, -2.4492935982947064e-16]
188
189
  ]
189
- geometry = arc({ startAngle: Math.PI, endAngle: Math.PI * 2, segments: 16 })
190
+ geometry = arc({ startAngle: TAU / 2, endAngle: TAU, segments: 16 })
190
191
  obs = path2.toPoints(geometry)
191
192
 
192
193
  t.notThrows(() => path2.validate(geometry))
@@ -205,7 +206,7 @@ test('arc (rotations)', (t) => {
205
206
  [0.34202014332566866, 0.9396926207859084],
206
207
  [3.061616997868383e-16, 1]
207
208
  ]
208
- geometry = arc({ startAngle: Math.PI * 2 * 0.75, endAngle: Math.PI / 2, segments: 16 })
209
+ geometry = arc({ startAngle: TAU * 0.75, endAngle: TAU / 4, segments: 16 })
209
210
  obs = path2.toPoints(geometry)
210
211
 
211
212
  t.notThrows(() => path2.validate(geometry))
@@ -213,7 +214,7 @@ test('arc (rotations)', (t) => {
213
214
  t.true(comparePoints(obs, exp))
214
215
 
215
216
  exp = [[-1.8369701987210297e-16, -1]]
216
- geometry = arc({ startAngle: Math.PI * 2 * 0.75, endAngle: 270.000000005 * 0.017453292519943295, segments: 16 })
217
+ geometry = arc({ startAngle: TAU * 0.75, endAngle: 270.000000005 * 0.017453292519943295, segments: 16 })
217
218
  obs = path2.toPoints(geometry)
218
219
 
219
220
  t.notThrows(() => path2.validate(geometry))
@@ -1,3 +1,5 @@
1
+ const { TAU } = require('../maths/constants')
2
+
1
3
  const ellipse = require('./ellipse')
2
4
 
3
5
  const { isGT } = require('./commonChecks')
@@ -9,7 +11,7 @@ const { isGT } = require('./commonChecks')
9
11
  * @param {Array} [options.center=[0,0]] - center of circle
10
12
  * @param {Number} [options.radius=1] - radius of circle
11
13
  * @param {Number} [options.startAngle=0] - start angle of circle, in radians
12
- * @param {Number} [options.endAngle=(Math.PI * 2)] - end angle of circle, in radians
14
+ * @param {Number} [options.endAngle=TAU] - end angle of circle, in radians
13
15
  * @param {Number} [options.segments=32] - number of segments to create per full rotation
14
16
  * @returns {geom2} new 2D geometry
15
17
  * @alias module:modeling/primitives.circle
@@ -21,7 +23,7 @@ const circle = (options) => {
21
23
  center: [0, 0],
22
24
  radius: 1,
23
25
  startAngle: 0,
24
- endAngle: (Math.PI * 2),
26
+ endAngle: TAU,
25
27
  segments: 32
26
28
  }
27
29
  let { center, radius, startAngle, endAngle, segments } = Object.assign({}, defaults, options)
@@ -1,11 +1,12 @@
1
1
  const test = require('ava')
2
2
 
3
- const { circle } = require('./index')
4
-
3
+ const { TAU } = require('../maths/constants')
5
4
  const geom2 = require('../geometries/geom2')
6
5
 
7
6
  const comparePoints = require('../../test/helpers/comparePoints')
8
7
 
8
+ const { circle } = require('./index')
9
+
9
10
  test('circle (defaults)', (t) => {
10
11
  const geometry = circle()
11
12
  const pts = geom2.toPoints(geometry)
@@ -84,7 +85,7 @@ test('circle (options)', (t) => {
84
85
  t.true(comparePoints(pts, exp))
85
86
 
86
87
  // test startAngle
87
- geometry = circle({ radius: 3.5, startAngle: Math.PI / 2, segments: 16 })
88
+ geometry = circle({ radius: 3.5, startAngle: TAU / 4, segments: 16 })
88
89
  pts = geom2.toPoints(geometry)
89
90
  exp = [
90
91
  [0, 3.5],
@@ -108,7 +109,7 @@ test('circle (options)', (t) => {
108
109
  t.true(comparePoints(pts, exp))
109
110
 
110
111
  // test endAngle
111
- geometry = circle({ radius: 3.5, endAngle: Math.PI / 2, segments: 16 })
112
+ geometry = circle({ radius: 3.5, endAngle: TAU / 4, segments: 16 })
112
113
  pts = geom2.toPoints(geometry)
113
114
  exp = [
114
115
  [3.5, 0],
@@ -123,6 +124,13 @@ test('circle (options)', (t) => {
123
124
  t.deepEqual(pts.length, 6)
124
125
  t.true(comparePoints(pts, exp))
125
126
 
127
+ // test full rotation with non-zero startAngle
128
+ geometry = circle({ startAngle: 1, endAngle: 1 + TAU })
129
+ pts = geom2.toPoints(geometry)
130
+
131
+ t.notThrows(() => geom2.validate(geometry))
132
+ t.deepEqual(pts.length, 32)
133
+
126
134
  // test segments
127
135
  geometry = circle({ radius: 3.5, segments: 5 })
128
136
  pts = geom2.toPoints(geometry)
@@ -1,11 +1,11 @@
1
- const { EPS } = require('../maths/constants')
1
+ const { EPS, TAU } = require('../maths/constants')
2
2
 
3
3
  const vec3 = require('../maths/vec3')
4
4
 
5
5
  const geom3 = require('../geometries/geom3')
6
6
  const poly3 = require('../geometries/poly3')
7
7
 
8
- const { sin, cos } = require('../utils/trigonometry')
8
+ const { sin, cos } = require('../maths/utils/trigonometry')
9
9
 
10
10
  const { isGT, isGTE, isNumberArray } = require('./commonChecks')
11
11
 
@@ -17,7 +17,7 @@ const { isGT, isGTE, isNumberArray } = require('./commonChecks')
17
17
  * @param {Array} [options.startRadius=[1,1]] - radius of rounded start, must be two dimensional array
18
18
  * @param {Number} [options.startAngle=0] - start angle of cylinder, in radians
19
19
  * @param {Array} [options.endRadius=[1,1]] - radius of rounded end, must be two dimensional array
20
- * @param {Number} [options.endAngle=(Math.PI * 2)] - end angle of cylinder, in radians
20
+ * @param {Number} [options.endAngle=TAU] - end angle of cylinder, in radians
21
21
  * @param {Number} [options.segments=32] - number of segments to create per full rotation
22
22
  * @returns {geom3} new geometry
23
23
  * @alias module:modeling/primitives.cylinderElliptic
@@ -32,7 +32,7 @@ const cylinderElliptic = (options) => {
32
32
  startRadius: [1, 1],
33
33
  startAngle: 0,
34
34
  endRadius: [1, 1],
35
- endAngle: (Math.PI * 2),
35
+ endAngle: TAU,
36
36
  segments: 32
37
37
  }
38
38
  let { center, height, startRadius, startAngle, endRadius, endAngle, segments } = Object.assign({}, defaults, options)
@@ -48,15 +48,15 @@ const cylinderElliptic = (options) => {
48
48
  if (!isGTE(endAngle, 0)) throw new Error('endAngle must be positive')
49
49
  if (!isGTE(segments, 4)) throw new Error('segments must be four or more')
50
50
 
51
- startAngle = startAngle % (Math.PI * 2)
52
- endAngle = endAngle % (Math.PI * 2)
51
+ startAngle = startAngle % TAU
52
+ endAngle = endAngle % TAU
53
53
 
54
- let rotation = (Math.PI * 2)
54
+ let rotation = TAU
55
55
  if (startAngle < endAngle) {
56
56
  rotation = endAngle - startAngle
57
57
  }
58
58
  if (startAngle > endAngle) {
59
- rotation = endAngle + ((Math.PI * 2) - startAngle)
59
+ rotation = endAngle + (TAU - startAngle)
60
60
  }
61
61
 
62
62
  const minradius = Math.min(startRadius[0], startRadius[1], endRadius[0], endRadius[1])
@@ -64,7 +64,7 @@ const cylinderElliptic = (options) => {
64
64
  (2 * minradius * minradius))
65
65
  if (rotation < minangle) throw new Error('startAngle and endAngle do not define a significant rotation')
66
66
 
67
- const slices = Math.floor(segments * (rotation / (Math.PI * 2)))
67
+ const slices = Math.floor(segments * (rotation / TAU))
68
68
 
69
69
  const start = vec3.fromValues(0, 0, -(height / 2))
70
70
  const end = vec3.fromValues(0, 0, height / 2)
@@ -96,7 +96,9 @@ const cylinderElliptic = (options) => {
96
96
  const polygons = []
97
97
  for (let i = 0; i < slices; i++) {
98
98
  const t0 = i / slices
99
- const t1 = (i + 1) / slices
99
+ let t1 = (i + 1) / slices
100
+ // fix rounding error when rotating TAU radians
101
+ if (rotation === TAU && i === slices - 1) t1 = 0
100
102
 
101
103
  if (endRadius[0] === startRadius[0] && endRadius[1] === startRadius[1]) {
102
104
  polygons.push(fromPoints(start, point(0, t1, endRadius), point(0, t0, endRadius)))
@@ -117,7 +119,7 @@ const cylinderElliptic = (options) => {
117
119
  }
118
120
  }
119
121
  }
120
- if (rotation < (Math.PI * 2)) {
122
+ if (rotation < TAU) {
121
123
  polygons.push(fromPoints(start, point(0, 0, startRadius), end))
122
124
  polygons.push(fromPoints(point(0, 0, startRadius), point(1, 0, endRadius), end))
123
125
  polygons.push(fromPoints(start, end, point(0, 1, startRadius)))
@@ -1,5 +1,6 @@
1
1
  const test = require('ava')
2
2
 
3
+ const { TAU } = require('../maths/constants')
3
4
  const geom3 = require('../geometries/geom3')
4
5
 
5
6
  const { cylinderElliptic } = require('./index')
@@ -132,12 +133,18 @@ test('cylinderElliptic (options)', (t) => {
132
133
  t.true(comparePolygonsAsPoints(pts, exp))
133
134
 
134
135
  // test startAngle and endAngle
135
- obs = cylinderElliptic({ startRadius: [1, 2], endRadius: [2, 1], startAngle: Math.PI / 2, endAngle: Math.PI * 2 * 0.75, segments: 12 })
136
+ obs = cylinderElliptic({ startRadius: [1, 2], endRadius: [2, 1], startAngle: TAU / 4, endAngle: TAU * 0.75, segments: 12 })
136
137
  pts = geom3.toPoints(obs)
137
138
 
138
139
  t.notThrows(() => geom3.validate(obs))
139
140
  t.is(pts.length, 28)
140
- // t.true(comparePolygonsAsPoints(pts, exp))
141
+
142
+ // test startAngle and endAngle
143
+ obs = cylinderElliptic({ startAngle: 1, endAngle: 1 + TAU })
144
+ pts = geom3.toPoints(obs)
145
+
146
+ t.notThrows(() => geom3.validate(obs))
147
+ t.is(pts.length, 96)
141
148
 
142
149
  // test segments
143
150
  obs = cylinderElliptic({ segments: 8 })
@@ -1,10 +1,10 @@
1
- const { EPS } = require('../maths/constants')
1
+ const { EPS, TAU } = require('../maths/constants')
2
2
 
3
3
  const vec2 = require('../maths/vec2')
4
4
 
5
5
  const geom2 = require('../geometries/geom2')
6
6
 
7
- const { sin, cos } = require('../utils/trigonometry')
7
+ const { sin, cos } = require('../maths/utils/trigonometry')
8
8
 
9
9
  const { isGTE, isNumberArray } = require('./commonChecks')
10
10
 
@@ -15,7 +15,7 @@ const { isGTE, isNumberArray } = require('./commonChecks')
15
15
  * @param {Array} [options.center=[0,0]] - center of ellipse
16
16
  * @param {Array} [options.radius=[1,1]] - radius of ellipse, along X and Y
17
17
  * @param {Number} [options.startAngle=0] - start angle of ellipse, in radians
18
- * @param {Number} [options.endAngle=(Math.PI * 2)] - end angle of ellipse, in radians
18
+ * @param {Number} [options.endAngle=TAU] - end angle of ellipse, in radians
19
19
  * @param {Number} [options.segments=32] - number of segments to create per full rotation
20
20
  * @returns {geom2} new 2D geometry
21
21
  * @alias module:modeling/primitives.ellipse
@@ -27,7 +27,7 @@ const ellipse = (options) => {
27
27
  center: [0, 0],
28
28
  radius: [1, 1],
29
29
  startAngle: 0,
30
- endAngle: (Math.PI * 2),
30
+ endAngle: TAU,
31
31
  segments: 32
32
32
  }
33
33
  let { center, radius, startAngle, endAngle, segments } = Object.assign({}, defaults, options)
@@ -39,15 +39,15 @@ const ellipse = (options) => {
39
39
  if (!isGTE(endAngle, 0)) throw new Error('endAngle must be positive')
40
40
  if (!isGTE(segments, 3)) throw new Error('segments must be three or more')
41
41
 
42
- startAngle = startAngle % (Math.PI * 2)
43
- endAngle = endAngle % (Math.PI * 2)
42
+ startAngle = startAngle % TAU
43
+ endAngle = endAngle % TAU
44
44
 
45
- let rotation = (Math.PI * 2)
45
+ let rotation = TAU
46
46
  if (startAngle < endAngle) {
47
47
  rotation = endAngle - startAngle
48
48
  }
49
49
  if (startAngle > endAngle) {
50
- rotation = endAngle + ((Math.PI * 2) - startAngle)
50
+ rotation = endAngle + (TAU - startAngle)
51
51
  }
52
52
 
53
53
  const minradius = Math.min(radius[0], radius[1])
@@ -55,20 +55,20 @@ const ellipse = (options) => {
55
55
  (2 * minradius * minradius))
56
56
  if (rotation < minangle) throw new Error('startAngle and endAngle do not define a significant rotation')
57
57
 
58
- segments = Math.floor(segments * (rotation / (Math.PI * 2)))
58
+ segments = Math.floor(segments * (rotation / TAU))
59
59
 
60
60
  const centerv = vec2.clone(center)
61
61
  const step = rotation / segments // radians per segment
62
62
 
63
63
  const points = []
64
- segments = (rotation < Math.PI * 2) ? segments + 1 : segments
64
+ segments = (rotation < TAU) ? segments + 1 : segments
65
65
  for (let i = 0; i < segments; i++) {
66
66
  const angle = (step * i) + startAngle
67
67
  const point = vec2.fromValues(radius[0] * cos(angle), radius[1] * sin(angle))
68
68
  vec2.add(point, centerv, point)
69
69
  points.push(point)
70
70
  }
71
- if (rotation < Math.PI * 2) points.push(centerv)
71
+ if (rotation < TAU) points.push(centerv)
72
72
  return geom2.fromPoints(points)
73
73
  }
74
74
 
@@ -1,11 +1,12 @@
1
1
  const test = require('ava')
2
2
 
3
- const { ellipse } = require('./index')
4
-
3
+ const { TAU } = require('../maths/constants')
5
4
  const geom2 = require('../geometries/geom2')
6
5
 
7
6
  const comparePoints = require('../../test/helpers/comparePoints')
8
7
 
8
+ const { ellipse } = require('./index')
9
+
9
10
  test('ellipse (defaults)', (t) => {
10
11
  const geometry = ellipse()
11
12
  const obs = geom2.toPoints(geometry)
@@ -84,7 +85,7 @@ test('ellipse (options)', (t) => {
84
85
  t.true(comparePoints(obs, exp))
85
86
 
86
87
  // test startAngle
87
- geometry = ellipse({ radius: [3, 5], startAngle: Math.PI / 2, segments: 16 })
88
+ geometry = ellipse({ radius: [3, 5], startAngle: TAU / 4, segments: 16 })
88
89
  obs = geom2.toPoints(geometry)
89
90
  exp = [
90
91
  [0, 5],
@@ -108,7 +109,7 @@ test('ellipse (options)', (t) => {
108
109
  t.true(comparePoints(obs, exp))
109
110
 
110
111
  // test endAngle
111
- geometry = ellipse({ radius: [3, 5], endAngle: Math.PI / 2, segments: 16 })
112
+ geometry = ellipse({ radius: [3, 5], endAngle: TAU / 4, segments: 16 })
112
113
  obs = geom2.toPoints(geometry)
113
114
  exp = [
114
115
  [3, 0],
@@ -123,6 +124,13 @@ test('ellipse (options)', (t) => {
123
124
  t.deepEqual(obs.length, 6)
124
125
  t.true(comparePoints(obs, exp))
125
126
 
127
+ // test full rotation with non-zero startAngle
128
+ geometry = ellipse({ startAngle: 1, endAngle: 1 + TAU })
129
+ obs = geom2.toPoints(geometry)
130
+
131
+ t.notThrows(() => geom2.validate(geometry))
132
+ t.deepEqual(obs.length, 32)
133
+
126
134
  // test segments
127
135
  geometry = ellipse({ segments: 72 })
128
136
  obs = geom2.toPoints(geometry)
@@ -1,9 +1,10 @@
1
+ const { TAU } = require('../maths/constants')
1
2
  const vec3 = require('../maths/vec3')
2
3
 
3
4
  const geom3 = require('../geometries/geom3')
4
5
  const poly3 = require('../geometries/poly3')
5
6
 
6
- const { sin, cos } = require('../utils/trigonometry')
7
+ const { sin, cos } = require('../maths/utils/trigonometry')
7
8
 
8
9
  const { isGTE, isNumberArray } = require('./commonChecks')
9
10
 
@@ -44,12 +45,12 @@ const ellipsoid = (options) => {
44
45
  const p1 = vec3.create()
45
46
  const p2 = vec3.create()
46
47
  for (let slice1 = 0; slice1 <= segments; slice1++) {
47
- const angle = 2 * Math.PI * slice1 / segments
48
+ const angle = TAU * slice1 / segments
48
49
  const cylinderpoint = vec3.add(vec3.create(), vec3.scale(p1, xvector, cos(angle)), vec3.scale(p2, yvector, sin(angle)))
49
50
  if (slice1 > 0) {
50
51
  let prevcospitch, prevsinpitch
51
52
  for (let slice2 = 0; slice2 <= qsegments; slice2++) {
52
- const pitch = 0.5 * Math.PI * slice2 / qsegments
53
+ const pitch = TAU / 4 * slice2 / qsegments
53
54
  const cospitch = cos(pitch)
54
55
  const sinpitch = sin(pitch)
55
56
  if (slice2 > 0) {
@@ -1,4 +1,5 @@
1
1
  const mat4 = require('../maths/mat4')
2
+ const vec3 = require('../maths/vec3')
2
3
 
3
4
  const geom3 = require('../geometries/geom3')
4
5
 
@@ -79,7 +80,7 @@ const geodesicSphere = (options) => {
79
80
 
80
81
  // -- normalize
81
82
  for (let k = 0; k < 3; k++) {
82
- const r = Math.hypot(q[k][0], q[k][1], q[k][2])
83
+ const r = vec3.length(q[k])
83
84
  for (let l = 0; l < 3; l++) {
84
85
  q[k][l] /= r
85
86
  }
@@ -95,7 +96,7 @@ const geodesicSphere = (options) => {
95
96
 
96
97
  // -- normalize
97
98
  for (let k = 0; k < 3; k++) {
98
- const r = Math.hypot(q[k][0], q[k][1], q[k][2])
99
+ const r = vec3.length(q[k])
99
100
  for (let l = 0; l < 3; l++) {
100
101
  q[k][l] /= r
101
102
  }
@@ -1,4 +1,4 @@
1
- const { EPS } = require('../maths/constants')
1
+ const { EPS, TAU } = require('../maths/constants')
2
2
 
3
3
  const vec2 = require('../maths/vec2')
4
4
  const vec3 = require('../maths/vec3')
@@ -6,12 +6,14 @@ const vec3 = require('../maths/vec3')
6
6
  const geom3 = require('../geometries/geom3')
7
7
  const poly3 = require('../geometries/poly3')
8
8
 
9
+ const { sin, cos } = require('../maths/utils/trigonometry')
10
+
9
11
  const { isGT, isGTE, isNumberArray } = require('./commonChecks')
10
12
 
11
13
  const createCorners = (center, size, radius, segments, slice, positive) => {
12
- const pitch = (Math.PI / 2) * slice / segments
13
- const cospitch = Math.cos(pitch)
14
- const sinpitch = Math.sin(pitch)
14
+ const pitch = (TAU / 4) * slice / segments
15
+ const cospitch = cos(pitch)
16
+ const sinpitch = sin(pitch)
15
17
 
16
18
  const layersegments = segments - slice
17
19
  let layerradius = radius * cospitch
@@ -29,16 +31,16 @@ const createCorners = (center, size, radius, segments, slice, positive) => {
29
31
  const corner2Points = []
30
32
  const corner3Points = []
31
33
  for (let i = 0; i <= layersegments; i++) {
32
- const radians = layersegments > 0 ? Math.PI / 2 * i / layersegments : 0
34
+ const radians = layersegments > 0 ? TAU / 4 * i / layersegments : 0
33
35
  const point2d = vec2.fromAngleRadians(vec2.create(), radians)
34
36
  vec2.scale(point2d, point2d, layerradius)
35
37
  const point3d = vec3.fromVec2(vec3.create(), point2d)
36
38
  corner0Points.push(vec3.add(vec3.create(), corner0, point3d))
37
- vec3.rotateZ(point3d, point3d, [0, 0, 0], Math.PI / 2)
39
+ vec3.rotateZ(point3d, point3d, [0, 0, 0], TAU / 4)
38
40
  corner1Points.push(vec3.add(vec3.create(), corner1, point3d))
39
- vec3.rotateZ(point3d, point3d, [0, 0, 0], Math.PI / 2)
41
+ vec3.rotateZ(point3d, point3d, [0, 0, 0], TAU / 4)
40
42
  corner2Points.push(vec3.add(vec3.create(), corner2, point3d))
41
- vec3.rotateZ(point3d, point3d, [0, 0, 0], Math.PI / 2)
43
+ vec3.rotateZ(point3d, point3d, [0, 0, 0], TAU / 4)
42
44
  corner3Points.push(vec3.add(vec3.create(), corner3, point3d))
43
45
  }
44
46
  if (!positive) {
@@ -1,11 +1,11 @@
1
- const { EPS } = require('../maths/constants')
1
+ const { EPS, TAU } = require('../maths/constants')
2
2
 
3
3
  const vec3 = require('../maths/vec3')
4
4
 
5
5
  const geom3 = require('../geometries/geom3')
6
6
  const poly3 = require('../geometries/poly3')
7
7
 
8
- const { sin, cos } = require('../utils/trigonometry')
8
+ const { sin, cos } = require('../maths/utils/trigonometry')
9
9
 
10
10
  const { isGT, isGTE, isNumberArray } = require('./commonChecks')
11
11
 
@@ -74,7 +74,7 @@ const roundedCylinder = (options) => {
74
74
  const v2 = vec3.create()
75
75
  let prevcylinderpoint
76
76
  for (let slice1 = 0; slice1 <= segments; slice1++) {
77
- const angle = 2 * Math.PI * slice1 / segments
77
+ const angle = TAU * slice1 / segments
78
78
  const cylinderpoint = vec3.add(vec3.create(), vec3.scale(v1, xvector, cos(angle)), vec3.scale(v2, yvector, sin(angle)))
79
79
  if (slice1 > 0) {
80
80
  // cylinder wall
@@ -87,7 +87,7 @@ const roundedCylinder = (options) => {
87
87
 
88
88
  let prevcospitch, prevsinpitch
89
89
  for (let slice2 = 0; slice2 <= qsegments; slice2++) {
90
- const pitch = 0.5 * Math.PI * slice2 / qsegments
90
+ const pitch = TAU / 4 * slice2 / qsegments
91
91
  const cospitch = cos(pitch)
92
92
  const sinpitch = sin(pitch)
93
93
  if (slice2 > 0) {
@@ -1,4 +1,4 @@
1
- const { EPS } = require('../maths/constants')
1
+ const { EPS, TAU } = require('../maths/constants')
2
2
 
3
3
  const vec2 = require('../maths/vec2')
4
4
 
@@ -51,15 +51,15 @@ const roundedRectangle = (options) => {
51
51
  const corner2Points = []
52
52
  const corner3Points = []
53
53
  for (let i = 0; i <= cornersegments; i++) {
54
- const radians = Math.PI / 2 * i / cornersegments
54
+ const radians = TAU / 4 * i / cornersegments
55
55
  const point = vec2.fromAngleRadians(vec2.create(), radians)
56
56
  vec2.scale(point, point, roundRadius)
57
57
  corner0Points.push(vec2.add(vec2.create(), corner0, point))
58
- vec2.rotate(point, point, vec2.create(), Math.PI / 2)
58
+ vec2.rotate(point, point, vec2.create(), TAU / 4)
59
59
  corner1Points.push(vec2.add(vec2.create(), corner1, point))
60
- vec2.rotate(point, point, vec2.create(), Math.PI / 2)
60
+ vec2.rotate(point, point, vec2.create(), TAU / 4)
61
61
  corner2Points.push(vec2.add(vec2.create(), corner2, point))
62
- vec2.rotate(point, point, vec2.create(), Math.PI / 2)
62
+ vec2.rotate(point, point, vec2.create(), TAU / 4)
63
63
  corner3Points.push(vec2.add(vec2.create(), corner3, point))
64
64
  }
65
65
 
@@ -1,3 +1,4 @@
1
+ const { TAU } = require('../maths/constants')
1
2
  const vec2 = require('../maths/vec2')
2
3
 
3
4
  const geom2 = require('../geometries/geom2')
@@ -13,7 +14,7 @@ const getRadiusRatio = (vertices, density) => {
13
14
  }
14
15
 
15
16
  const getPoints = (vertices, radius, startAngle, center) => {
16
- const a = (Math.PI * 2) / vertices
17
+ const a = TAU / vertices
17
18
 
18
19
  const points = []
19
20
  for (let i = 0; i < vertices; i++) {
@@ -63,7 +64,7 @@ const star = (options) => {
63
64
  vertices = Math.floor(vertices)
64
65
  density = Math.floor(density)
65
66
 
66
- startAngle = startAngle % (Math.PI * 2)
67
+ startAngle = startAngle % TAU
67
68
 
68
69
  if (innerRadius === 0) {
69
70
  if (!isGTE(density, 2)) throw new Error('density must be two or more')
@@ -1,3 +1,5 @@
1
+ const { TAU } = require('../maths/constants')
2
+
1
3
  const extrudeRotate = require('../operations/extrusions/extrudeRotate')
2
4
  const { rotate } = require('../operations/transforms/rotate')
3
5
  const { translate } = require('../operations/transforms/translate')
@@ -14,7 +16,7 @@ const { isGT, isGTE } = require('./commonChecks')
14
16
  * @param {Integer} [options.innerSegments=32] - number of segments to create per rotation
15
17
  * @param {Integer} [options.outerSegments=32] - number of segments to create per rotation
16
18
  * @param {Integer} [options.innerRotation=0] - rotation of small (inner) circle in radians
17
- * @param {Number} [options.outerRotation=(PI * 2)] - rotation (outer) of the torus (RADIANS)
19
+ * @param {Number} [options.outerRotation=TAU] - rotation (outer) of the torus (RADIANS)
18
20
  * @param {Number} [options.startAngle=0] - start angle of the torus (RADIANS)
19
21
  * @returns {geom3} new 3D geometry
20
22
  * @alias module:modeling/primitives.torus
@@ -30,7 +32,7 @@ const torus = (options) => {
30
32
  outerSegments: 32,
31
33
  innerRotation: 0,
32
34
  startAngle: 0,
33
- outerRotation: Math.PI * 2
35
+ outerRotation: TAU
34
36
  }
35
37
  const { innerRadius, innerSegments, outerRadius, outerSegments, innerRotation, startAngle, outerRotation } = Object.assign({}, defaults, options)
36
38