@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.
- package/CHANGELOG.md +17 -0
- package/README.md +12 -2
- package/dist/jscad-modeling.min.js +28 -28
- package/package.json +2 -2
- package/src/geometries/path2/appendArc.js +6 -5
- package/src/geometries/path2/appendArc.test.js +3 -1
- package/src/geometries/path2/appendBezier.js +2 -1
- package/src/geometries/path2/transform.js +1 -1
- package/src/index.d.ts +1 -0
- package/src/maths/constants.js +10 -0
- package/src/maths/mat4/fromRotation.js +1 -1
- package/src/maths/mat4/fromTaitBryanRotation.js +1 -1
- package/src/maths/mat4/fromXRotation.js +1 -1
- package/src/maths/mat4/fromYRotation.js +1 -1
- package/src/maths/mat4/fromZRotation.js +1 -1
- package/src/maths/mat4/invert.test.js +5 -2
- package/src/maths/mat4/isOnlyTransformScale.js +1 -1
- package/src/maths/mat4/isOnlyTransformScale.test.js +3 -1
- package/src/maths/rotation.test.js +5 -2
- package/src/maths/utils/trigonometry.js +6 -6
- package/src/maths/utils/trigonometry.test.js +10 -8
- package/src/maths/vec2/fromAngleDegrees.js +1 -1
- package/src/maths/vec2/fromAngleRadians.test.js +4 -1
- package/src/maths/vec2/normal.js +3 -1
- package/src/maths/vec2/rotate.test.js +4 -1
- package/src/maths/vec3/rotateX.test.js +4 -1
- package/src/maths/vec3/rotateY.test.js +4 -1
- package/src/maths/vec3/rotateZ.test.js +4 -1
- package/src/operations/expansions/expand.test.js +3 -1
- package/src/operations/expansions/expandShell.js +4 -4
- package/src/operations/expansions/offsetFromPoints.js +2 -2
- package/src/operations/extrusions/extrudeFromSlices.test.js +3 -2
- package/src/operations/extrusions/extrudeLinear.test.js +5 -3
- package/src/operations/extrusions/extrudeRectangular.js +1 -1
- package/src/operations/extrusions/extrudeRectangular.test.js +5 -3
- package/src/operations/extrusions/extrudeRotate.js +14 -13
- package/src/operations/extrusions/extrudeRotate.test.js +7 -5
- package/src/operations/extrusions/slice/calculatePlane.test.js +3 -2
- package/src/operations/extrusions/slice/index.js +2 -0
- package/src/operations/modifiers/generalize.d.ts +12 -0
- package/src/operations/modifiers/generalize.test.js +3 -1
- package/src/operations/modifiers/index.d.ts +2 -0
- package/src/operations/modifiers/snap.d.ts +6 -0
- package/src/operations/modifiers/snap.test.js +5 -3
- package/src/operations/transforms/rotate.js +1 -1
- package/src/operations/transforms/rotate.test.js +13 -11
- package/src/operations/transforms/transform.js +1 -1
- package/src/primitives/arc.js +8 -8
- package/src/primitives/arc.test.js +9 -8
- package/src/primitives/circle.js +4 -2
- package/src/primitives/circle.test.js +6 -5
- package/src/primitives/cylinderElliptic.js +11 -11
- package/src/primitives/cylinderElliptic.test.js +3 -2
- package/src/primitives/ellipse.js +10 -10
- package/src/primitives/ellipse.test.js +6 -5
- package/src/primitives/ellipsoid.js +3 -2
- package/src/primitives/roundedCuboid.js +6 -6
- package/src/primitives/roundedCylinder.js +3 -3
- package/src/primitives/roundedRectangle.js +5 -5
- package/src/primitives/star.js +3 -2
- package/src/primitives/torus.js +4 -2
- package/src/primitives/torus.test.js +5 -3
- package/src/primitives/triangle.test.js +2 -1
- package/src/utils/degToRad.test.js +5 -5
- package/src/utils/radToDeg.test.js +6 -6
- package/src/utils/radiusToSegments.js +6 -4
- package/src/utils/radiusToSegments.test.js +5 -3
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
2
|
|
|
3
|
+
const { TAU } = require('../../maths/constants')
|
|
4
|
+
|
|
3
5
|
const { geom2, geom3, path2 } = require('../../geometries')
|
|
4
6
|
|
|
5
7
|
const { rotate, rotateX, rotateY, rotateZ } = require('./index')
|
|
@@ -10,7 +12,7 @@ test('rotate: rotating of a path2 produces expected changes to points', (t) => {
|
|
|
10
12
|
const geometry = path2.fromPoints({}, [[1, 0], [0, 1], [-1, 0]])
|
|
11
13
|
|
|
12
14
|
// rotate about Z
|
|
13
|
-
let rotated = rotate([0, 0,
|
|
15
|
+
let rotated = rotate([0, 0, TAU / 4], geometry)
|
|
14
16
|
let obs = path2.toPoints(rotated)
|
|
15
17
|
const exp = [
|
|
16
18
|
new Float32Array([0, 1]),
|
|
@@ -19,7 +21,7 @@ test('rotate: rotating of a path2 produces expected changes to points', (t) => {
|
|
|
19
21
|
]
|
|
20
22
|
t.true(comparePoints(obs, exp))
|
|
21
23
|
|
|
22
|
-
rotated = rotateZ(
|
|
24
|
+
rotated = rotateZ(TAU / 4, geometry)
|
|
23
25
|
obs = path2.toPoints(rotated)
|
|
24
26
|
t.notThrows(() => path2.validate(rotated))
|
|
25
27
|
t.true(comparePoints(obs, exp))
|
|
@@ -29,7 +31,7 @@ test('rotate: rotating of a geom2 produces expected changes to points', (t) => {
|
|
|
29
31
|
const geometry = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
|
|
30
32
|
|
|
31
33
|
// rotate about Z
|
|
32
|
-
let rotated = rotate([0, 0, -
|
|
34
|
+
let rotated = rotate([0, 0, -TAU / 4], geometry)
|
|
33
35
|
let obs = geom2.toPoints(rotated)
|
|
34
36
|
const exp = [
|
|
35
37
|
new Float32Array([0, 0]),
|
|
@@ -39,7 +41,7 @@ test('rotate: rotating of a geom2 produces expected changes to points', (t) => {
|
|
|
39
41
|
t.notThrows(() => geom2.validate(rotated))
|
|
40
42
|
t.true(comparePoints(obs, exp))
|
|
41
43
|
|
|
42
|
-
rotated = rotateZ(-
|
|
44
|
+
rotated = rotateZ(-TAU / 4, geometry)
|
|
43
45
|
obs = geom2.toPoints(rotated)
|
|
44
46
|
t.notThrows(() => geom2.validate(rotated))
|
|
45
47
|
t.true(comparePoints(obs, exp))
|
|
@@ -57,7 +59,7 @@ test('rotate: rotating of a geom3 produces expected changes to polygons', (t) =>
|
|
|
57
59
|
const geometry = geom3.fromPoints(points)
|
|
58
60
|
|
|
59
61
|
// rotate about X
|
|
60
|
-
let rotated = rotate([
|
|
62
|
+
let rotated = rotate([TAU / 4], geometry)
|
|
61
63
|
let obs = geom3.toPoints(rotated)
|
|
62
64
|
let exp = [
|
|
63
65
|
[[-2, 12, -7.000000000000001], [-2, -18, -6.999999999999999],
|
|
@@ -76,13 +78,13 @@ test('rotate: rotating of a geom3 produces expected changes to polygons', (t) =>
|
|
|
76
78
|
t.notThrows(() => geom3.validate(rotated))
|
|
77
79
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
78
80
|
|
|
79
|
-
rotated = rotateX(
|
|
81
|
+
rotated = rotateX(TAU / 4, geometry)
|
|
80
82
|
obs = geom3.toPoints(rotated)
|
|
81
83
|
t.notThrows(() => geom3.validate(rotated))
|
|
82
84
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
83
85
|
|
|
84
86
|
// rotate about Y
|
|
85
|
-
rotated = rotate([0, -
|
|
87
|
+
rotated = rotate([0, -TAU / 4], geometry)
|
|
86
88
|
obs = geom3.toPoints(rotated)
|
|
87
89
|
exp = [
|
|
88
90
|
[[12, -7, -2.000000000000001], [-18, -7, -1.999999999999999],
|
|
@@ -101,12 +103,12 @@ test('rotate: rotating of a geom3 produces expected changes to polygons', (t) =>
|
|
|
101
103
|
t.notThrows(() => geom3.validate(rotated))
|
|
102
104
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
103
105
|
|
|
104
|
-
rotated = rotateY(-
|
|
106
|
+
rotated = rotateY(-TAU / 4, geometry)
|
|
105
107
|
obs = geom3.toPoints(rotated)
|
|
106
108
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
107
109
|
|
|
108
110
|
// rotate about Z
|
|
109
|
-
rotated = rotate([0, 0,
|
|
111
|
+
rotated = rotate([0, 0, TAU / 2], geometry)
|
|
110
112
|
obs = geom3.toPoints(rotated)
|
|
111
113
|
exp = [
|
|
112
114
|
[[2.000000000000001, 7, -12], [2.000000000000001, 7, 18],
|
|
@@ -125,7 +127,7 @@ test('rotate: rotating of a geom3 produces expected changes to polygons', (t) =>
|
|
|
125
127
|
t.notThrows(() => geom3.validate(rotated))
|
|
126
128
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
127
129
|
|
|
128
|
-
rotated = rotateZ(
|
|
130
|
+
rotated = rotateZ(TAU / 2, geometry)
|
|
129
131
|
obs = geom3.toPoints(rotated)
|
|
130
132
|
t.notThrows(() => geom3.validate(rotated))
|
|
131
133
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
@@ -136,7 +138,7 @@ test('rotate: rotating of multiple objects produces expected changes', (t) => {
|
|
|
136
138
|
const geometry1 = path2.fromPoints({}, [[-5, 5], [5, 5], [-5, -5], [10, -5]])
|
|
137
139
|
const geometry2 = geom2.fromPoints([[-5, -5], [0, 5], [10, -5]])
|
|
138
140
|
|
|
139
|
-
const rotated = rotate([0, 0,
|
|
141
|
+
const rotated = rotate([0, 0, TAU / 4], junk, geometry1, geometry2)
|
|
140
142
|
|
|
141
143
|
t.is(rotated[0], junk)
|
|
142
144
|
|
|
@@ -12,7 +12,7 @@ const path2 = require('../../geometries/path2')
|
|
|
12
12
|
* @alias module:modeling/transforms.transform
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
|
-
* const newsphere = transform(mat4.rotateX(
|
|
15
|
+
* const newsphere = transform(mat4.rotateX(TAU / 8), sphere())
|
|
16
16
|
*/
|
|
17
17
|
const transform = (matrix, ...objects) => {
|
|
18
18
|
// TODO how to check that the matrix is REAL?
|
package/src/primitives/arc.js
CHANGED
|
@@ -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=
|
|
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:
|
|
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 %
|
|
39
|
-
endAngle = endAngle %
|
|
38
|
+
startAngle = startAngle % TAU
|
|
39
|
+
endAngle = endAngle % TAU
|
|
40
40
|
|
|
41
|
-
let rotation =
|
|
41
|
+
let rotation = TAU
|
|
42
42
|
if (startAngle < endAngle) {
|
|
43
43
|
rotation = endAngle - startAngle
|
|
44
44
|
}
|
|
45
45
|
if (startAngle > endAngle) {
|
|
46
|
-
rotation = endAngle + (
|
|
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 /
|
|
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 {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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))
|
package/src/primitives/circle.js
CHANGED
|
@@ -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=
|
|
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:
|
|
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 {
|
|
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:
|
|
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:
|
|
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],
|
|
@@ -124,7 +125,7 @@ test('circle (options)', (t) => {
|
|
|
124
125
|
t.true(comparePoints(pts, exp))
|
|
125
126
|
|
|
126
127
|
// test full rotation with non-zero startAngle
|
|
127
|
-
geometry = circle({ startAngle: 1, endAngle: 1 +
|
|
128
|
+
geometry = circle({ startAngle: 1, endAngle: 1 + TAU })
|
|
128
129
|
pts = geom2.toPoints(geometry)
|
|
129
130
|
|
|
130
131
|
t.notThrows(() => geom2.validate(geometry))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { EPS } = require('../maths/constants')
|
|
1
|
+
const { EPS, TAU } = require('../maths/constants')
|
|
2
2
|
|
|
3
3
|
const vec3 = require('../maths/vec3')
|
|
4
4
|
|
|
@@ -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=
|
|
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:
|
|
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 %
|
|
52
|
-
endAngle = endAngle %
|
|
51
|
+
startAngle = startAngle % TAU
|
|
52
|
+
endAngle = endAngle % TAU
|
|
53
53
|
|
|
54
|
-
let rotation =
|
|
54
|
+
let rotation = TAU
|
|
55
55
|
if (startAngle < endAngle) {
|
|
56
56
|
rotation = endAngle - startAngle
|
|
57
57
|
}
|
|
58
58
|
if (startAngle > endAngle) {
|
|
59
|
-
rotation = endAngle + (
|
|
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 /
|
|
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)
|
|
@@ -97,8 +97,8 @@ const cylinderElliptic = (options) => {
|
|
|
97
97
|
for (let i = 0; i < slices; i++) {
|
|
98
98
|
const t0 = i / slices
|
|
99
99
|
let t1 = (i + 1) / slices
|
|
100
|
-
// fix rounding error when rotating
|
|
101
|
-
if (rotation ===
|
|
100
|
+
// fix rounding error when rotating TAU radians
|
|
101
|
+
if (rotation === TAU && i === slices - 1) t1 = 0
|
|
102
102
|
|
|
103
103
|
if (endRadius[0] === startRadius[0] && endRadius[1] === startRadius[1]) {
|
|
104
104
|
polygons.push(fromPoints(start, point(0, t1, endRadius), point(0, t0, endRadius)))
|
|
@@ -119,7 +119,7 @@ const cylinderElliptic = (options) => {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
-
if (rotation <
|
|
122
|
+
if (rotation < TAU) {
|
|
123
123
|
polygons.push(fromPoints(start, point(0, 0, startRadius), end))
|
|
124
124
|
polygons.push(fromPoints(point(0, 0, startRadius), point(1, 0, endRadius), end))
|
|
125
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,14 +133,14 @@ 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:
|
|
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
141
|
|
|
141
142
|
// test startAngle and endAngle
|
|
142
|
-
obs = cylinderElliptic({ startAngle: 1, endAngle: 1 +
|
|
143
|
+
obs = cylinderElliptic({ startAngle: 1, endAngle: 1 + TAU })
|
|
143
144
|
pts = geom3.toPoints(obs)
|
|
144
145
|
|
|
145
146
|
t.notThrows(() => geom3.validate(obs))
|
|
@@ -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
|
|
|
@@ -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=
|
|
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:
|
|
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 %
|
|
43
|
-
endAngle = endAngle %
|
|
42
|
+
startAngle = startAngle % TAU
|
|
43
|
+
endAngle = endAngle % TAU
|
|
44
44
|
|
|
45
|
-
let rotation =
|
|
45
|
+
let rotation = TAU
|
|
46
46
|
if (startAngle < endAngle) {
|
|
47
47
|
rotation = endAngle - startAngle
|
|
48
48
|
}
|
|
49
49
|
if (startAngle > endAngle) {
|
|
50
|
-
rotation = endAngle + (
|
|
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 /
|
|
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 <
|
|
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 <
|
|
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 {
|
|
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:
|
|
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:
|
|
112
|
+
geometry = ellipse({ radius: [3, 5], endAngle: TAU / 4, segments: 16 })
|
|
112
113
|
obs = geom2.toPoints(geometry)
|
|
113
114
|
exp = [
|
|
114
115
|
[3, 0],
|
|
@@ -124,7 +125,7 @@ test('ellipse (options)', (t) => {
|
|
|
124
125
|
t.true(comparePoints(obs, exp))
|
|
125
126
|
|
|
126
127
|
// test full rotation with non-zero startAngle
|
|
127
|
-
geometry = ellipse({ startAngle: 1, endAngle: 1 +
|
|
128
|
+
geometry = ellipse({ startAngle: 1, endAngle: 1 + TAU })
|
|
128
129
|
obs = geom2.toPoints(geometry)
|
|
129
130
|
|
|
130
131
|
t.notThrows(() => geom2.validate(geometry))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { TAU } = require('../maths/constants')
|
|
1
2
|
const vec3 = require('../maths/vec3')
|
|
2
3
|
|
|
3
4
|
const geom3 = require('../geometries/geom3')
|
|
@@ -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 =
|
|
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 =
|
|
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,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')
|
|
@@ -11,7 +11,7 @@ const { sin, cos } = require('../maths/utils/trigonometry')
|
|
|
11
11
|
const { isGT, isGTE, isNumberArray } = require('./commonChecks')
|
|
12
12
|
|
|
13
13
|
const createCorners = (center, size, radius, segments, slice, positive) => {
|
|
14
|
-
const pitch = (
|
|
14
|
+
const pitch = (TAU / 4) * slice / segments
|
|
15
15
|
const cospitch = cos(pitch)
|
|
16
16
|
const sinpitch = sin(pitch)
|
|
17
17
|
|
|
@@ -31,16 +31,16 @@ const createCorners = (center, size, radius, segments, slice, positive) => {
|
|
|
31
31
|
const corner2Points = []
|
|
32
32
|
const corner3Points = []
|
|
33
33
|
for (let i = 0; i <= layersegments; i++) {
|
|
34
|
-
const radians = layersegments > 0 ?
|
|
34
|
+
const radians = layersegments > 0 ? TAU / 4 * i / layersegments : 0
|
|
35
35
|
const point2d = vec2.fromAngleRadians(vec2.create(), radians)
|
|
36
36
|
vec2.scale(point2d, point2d, layerradius)
|
|
37
37
|
const point3d = vec3.fromVec2(vec3.create(), point2d)
|
|
38
38
|
corner0Points.push(vec3.add(vec3.create(), corner0, point3d))
|
|
39
|
-
vec3.rotateZ(point3d, point3d, [0, 0, 0],
|
|
39
|
+
vec3.rotateZ(point3d, point3d, [0, 0, 0], TAU / 4)
|
|
40
40
|
corner1Points.push(vec3.add(vec3.create(), corner1, point3d))
|
|
41
|
-
vec3.rotateZ(point3d, point3d, [0, 0, 0],
|
|
41
|
+
vec3.rotateZ(point3d, point3d, [0, 0, 0], TAU / 4)
|
|
42
42
|
corner2Points.push(vec3.add(vec3.create(), corner2, point3d))
|
|
43
|
-
vec3.rotateZ(point3d, point3d, [0, 0, 0],
|
|
43
|
+
vec3.rotateZ(point3d, point3d, [0, 0, 0], TAU / 4)
|
|
44
44
|
corner3Points.push(vec3.add(vec3.create(), corner3, point3d))
|
|
45
45
|
}
|
|
46
46
|
if (!positive) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { EPS } = require('../maths/constants')
|
|
1
|
+
const { EPS, TAU } = require('../maths/constants')
|
|
2
2
|
|
|
3
3
|
const vec3 = require('../maths/vec3')
|
|
4
4
|
|
|
@@ -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 =
|
|
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 =
|
|
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 =
|
|
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(),
|
|
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(),
|
|
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(),
|
|
62
|
+
vec2.rotate(point, point, vec2.create(), TAU / 4)
|
|
63
63
|
corner3Points.push(vec2.add(vec2.create(), corner3, point))
|
|
64
64
|
}
|
|
65
65
|
|
package/src/primitives/star.js
CHANGED
|
@@ -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 =
|
|
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 %
|
|
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')
|
package/src/primitives/torus.js
CHANGED
|
@@ -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=
|
|
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:
|
|
35
|
+
outerRotation: TAU
|
|
34
36
|
}
|
|
35
37
|
const { innerRadius, innerSegments, outerRadius, outerSegments, innerRotation, startAngle, outerRotation } = Object.assign({}, defaults, options)
|
|
36
38
|
|