@jscad/modeling 3.0.0-alpha.0 → 3.0.2-alpha.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 +49 -0
- package/LICENSE +1 -1
- package/dist/jscad-modeling.es.js +2 -2
- package/dist/jscad-modeling.min.js +2 -2
- package/package.json +2 -2
- package/rollup.config.js +1 -1
- package/src/colors/colorize.js +1 -5
- package/src/colors/colorize.test.js +8 -8
- package/src/geometries/geom2/transform.js +9 -1
- package/src/geometries/geom2/transform.test.js +57 -0
- package/src/geometries/geom3/fromPointsConvex.d.ts +4 -0
- package/src/geometries/geom3/fromPointsConvex.js +25 -0
- package/src/geometries/geom3/fromPointsConvex.test.js +32 -0
- package/src/geometries/geom3/index.d.ts +1 -0
- package/src/geometries/geom3/index.js +1 -0
- package/src/geometries/index.js +3 -4
- package/src/geometries/path2/appendBezier.js +1 -1
- package/src/geometries/poly3/index.js +1 -1
- package/src/geometries/poly3/measureBoundingBox.js +2 -0
- package/src/geometries/poly3/measureBoundingSphere.d.ts +2 -1
- package/src/geometries/poly3/measureBoundingSphere.js +25 -8
- package/src/geometries/poly3/measureBoundingSphere.test.js +12 -8
- package/src/geometries/poly3/type.d.ts +1 -1
- package/src/geometries/slice/validate.js +1 -2
- package/src/index.js +41 -0
- package/src/maths/index.js +1 -0
- package/src/maths/mat4/isOnlyTransformScale.js +1 -1
- package/src/measurements/measureAggregateArea.js +0 -1
- package/src/measurements/measureAggregateBoundingBox.js +0 -1
- package/src/measurements/measureAggregateEpsilon.js +0 -1
- package/src/measurements/measureAggregateVolume.js +0 -1
- package/src/measurements/measureArea.js +0 -1
- package/src/measurements/measureBoundingBox.js +0 -1
- package/src/measurements/measureBoundingSphere.js +2 -6
- package/src/measurements/measureEpsilon.js +0 -1
- package/src/measurements/measureVolume.js +0 -1
- package/src/operations/booleans/index.d.ts +1 -0
- package/src/operations/booleans/intersect.js +5 -5
- package/src/operations/booleans/intersect.test.js +6 -7
- package/src/operations/booleans/intersectGeom2.js +2 -6
- package/src/operations/booleans/intersectGeom2.test.js +25 -1
- package/src/operations/booleans/intersectGeom3.js +2 -6
- package/src/operations/booleans/intersectGeom3.test.js +5 -1
- package/src/operations/booleans/martinez/compareEvents.js +2 -7
- package/src/operations/booleans/martinez/connectEdges.js +30 -41
- package/src/operations/booleans/martinez/contour.js +1 -1
- package/src/operations/booleans/martinez/divideSegment.js +12 -11
- package/src/operations/booleans/martinez/fillQueue.js +24 -28
- package/src/operations/booleans/martinez/index.js +2 -1
- package/src/operations/booleans/martinez/possibleIntersection.js +41 -30
- package/src/operations/booleans/martinez/segmentIntersection.js +7 -9
- package/src/operations/booleans/martinez/splaytree.js +59 -457
- package/src/operations/booleans/martinez/subdivideSegments.js +4 -4
- package/src/operations/booleans/martinez/sweepEvent.js +3 -17
- package/src/operations/booleans/mayOverlap.js +0 -1
- package/src/operations/booleans/scission.d.ts +5 -0
- package/src/operations/booleans/scission.js +3 -5
- package/src/operations/booleans/scission.test.js +6 -0
- package/src/operations/booleans/subtract.js +5 -5
- package/src/operations/booleans/subtract.test.js +6 -7
- package/src/operations/booleans/subtractGeom2.js +2 -6
- package/src/operations/booleans/subtractGeom2.test.js +25 -1
- package/src/operations/booleans/subtractGeom3.js +2 -6
- package/src/operations/booleans/subtractGeom3.test.js +5 -1
- package/src/operations/booleans/trees/Node.js +25 -27
- package/src/operations/booleans/trees/PolygonTreeNode.js +153 -106
- package/src/operations/booleans/trees/Tree.js +9 -4
- package/src/operations/booleans/trees/splitLineSegmentByPlane.js +5 -3
- package/src/operations/booleans/trees/splitPolygonByPlane.d.ts +33 -0
- package/src/operations/booleans/trees/splitPolygonByPlane.js +39 -34
- package/src/operations/booleans/union.js +5 -5
- package/src/operations/booleans/union.test.js +6 -7
- package/src/operations/booleans/unionGeom2.js +2 -6
- package/src/operations/booleans/unionGeom2.test.js +25 -1
- package/src/operations/booleans/unionGeom3.js +2 -6
- package/src/operations/booleans/unionGeom3.test.js +6 -1
- package/src/operations/extrusions/extrudeFromSlices.test.js +8 -1
- package/src/operations/extrusions/extrudeHelical.js +2 -8
- package/src/operations/extrusions/extrudeLinear.js +1 -5
- package/src/operations/extrusions/extrudeLinear.test.js +7 -1
- package/src/operations/extrusions/extrudeRotate.js +3 -2
- package/src/operations/extrusions/extrudeRotate.test.js +13 -1
- package/src/operations/extrusions/extrudeWalls.js +3 -1
- package/src/operations/extrusions/project.js +1 -5
- package/src/operations/hulls/hull.js +6 -5
- package/src/operations/hulls/hull.test.js +56 -3
- package/src/operations/hulls/hullChain.js +11 -6
- package/src/operations/hulls/hullChain.test.js +12 -2
- package/src/operations/hulls/hullGeom2.js +5 -6
- package/src/operations/hulls/hullGeom3.js +9 -18
- package/src/operations/hulls/hullPath2.js +6 -7
- package/src/operations/hulls/hullPath2.test.js +1 -1
- package/src/operations/hulls/hullPoints2.d.ts +3 -0
- package/src/operations/hulls/hullPoints2.js +24 -30
- package/src/operations/hulls/hullPoints3.d.ts +4 -0
- package/src/operations/hulls/hullPoints3.js +21 -0
- package/src/operations/hulls/index.d.ts +2 -0
- package/src/operations/hulls/index.js +3 -1
- package/src/operations/modifiers/generalize.js +2 -6
- package/src/operations/modifiers/index.js +1 -1
- package/src/operations/modifiers/mergePolygons.js +2 -3
- package/src/operations/modifiers/reTesselateCoplanarPolygons.js +7 -7
- package/src/operations/modifiers/snap.js +2 -6
- package/src/operations/offsets/offset.js +1 -5
- package/src/operations/offsets/offsetFromPoints.test.js +0 -1
- package/src/operations/offsets/offsetGeom2.test.js +1 -0
- package/src/operations/offsets/offsetGeom3.js +0 -2
- package/src/operations/offsets/offsetGeom3.test.js +9 -1
- package/src/operations/offsets/offsetPath2.js +3 -3
- package/src/operations/transforms/align.js +8 -7
- package/src/operations/transforms/align.test.js +2 -2
- package/src/operations/transforms/center.js +6 -9
- package/src/operations/transforms/center.test.js +19 -1
- package/src/operations/transforms/mirror.js +5 -8
- package/src/operations/transforms/mirror.test.js +7 -7
- package/src/operations/transforms/rotate.js +5 -8
- package/src/operations/transforms/scale.js +5 -8
- package/src/operations/transforms/transform.js +2 -5
- package/src/operations/transforms/translate.js +5 -8
- package/src/primitives/arc.js +2 -0
- package/src/primitives/arc.test.js +11 -11
- package/src/primitives/circle.test.js +18 -8
- package/src/primitives/cube.test.js +10 -0
- package/src/primitives/cuboid.test.js +10 -0
- package/src/primitives/cylinder.test.js +12 -0
- package/src/primitives/cylinderElliptic.test.js +21 -1
- package/src/primitives/ellipse.test.js +18 -8
- package/src/primitives/ellipsoid.test.js +12 -0
- package/src/primitives/geodesicSphere.test.js +8 -0
- package/src/primitives/line.test.js +1 -1
- package/src/primitives/polygon.d.ts +1 -0
- package/src/primitives/polygon.js +13 -4
- package/src/primitives/polygon.test.js +15 -0
- package/src/primitives/polyhedron.js +1 -0
- package/src/primitives/polyhedron.test.js +8 -2
- package/src/primitives/rectangle.test.js +9 -3
- package/src/primitives/roundedCuboid.js +1 -1
- package/src/primitives/roundedCuboid.test.js +20 -4
- package/src/primitives/roundedCylinder.js +1 -1
- package/src/primitives/roundedCylinder.test.js +20 -0
- package/src/primitives/roundedRectangle.js +1 -1
- package/src/primitives/roundedRectangle.test.js +15 -6
- package/src/primitives/sphere.test.js +12 -0
- package/src/primitives/square.test.js +10 -4
- package/src/primitives/star.test.js +14 -6
- package/src/primitives/torus.js +1 -1
- package/src/primitives/torus.test.js +11 -1
- package/src/primitives/triangle.test.js +17 -9
- package/src/utils/coalesce.d.ts +3 -0
- package/src/utils/coalesce.js +20 -0
- package/src/utils/index.js +2 -2
- package/src/maths/mat4/leftMultiplyVec2.d.ts +0 -4
- package/src/maths/mat4/leftMultiplyVec2.js +0 -26
- package/src/maths/mat4/leftMultiplyVec3.d.ts +0 -4
- package/src/maths/mat4/leftMultiplyVec3.js +0 -27
- package/src/maths/mat4/mirror.d.ts +0 -4
- package/src/maths/mat4/mirror.js +0 -32
- package/src/maths/mat4/rightMultiplyVec2.d.ts +0 -4
- package/src/maths/mat4/rightMultiplyVec2.js +0 -27
- package/src/maths/mat4/rightMultiplyVec3.d.ts +0 -4
- package/src/maths/mat4/rightMultiplyVec3.js +0 -28
|
@@ -2,6 +2,8 @@ import test from 'ava'
|
|
|
2
2
|
|
|
3
3
|
import { geom3 } from '../geometries/index.js'
|
|
4
4
|
|
|
5
|
+
import { measureArea, measureVolume } from '../measurements/index.js'
|
|
6
|
+
|
|
5
7
|
import { roundedCylinder } from './index.js'
|
|
6
8
|
|
|
7
9
|
import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
|
|
@@ -11,6 +13,8 @@ test('roundedCylinder (defaults)', (t) => {
|
|
|
11
13
|
const pts = geom3.toPoints(obs)
|
|
12
14
|
|
|
13
15
|
t.notThrows(() => geom3.validate(obs))
|
|
16
|
+
t.is(measureArea(obs), 16.844951865908268)
|
|
17
|
+
t.is(measureVolume(obs), 5.81870059177007)
|
|
14
18
|
t.is(pts.length, 544)
|
|
15
19
|
})
|
|
16
20
|
|
|
@@ -18,6 +22,8 @@ test('roundedCylinder (zero height)', (t) => {
|
|
|
18
22
|
const obs = roundedCylinder({ height: 0 })
|
|
19
23
|
const pts = geom3.toPoints(obs)
|
|
20
24
|
t.notThrows(() => geom3.validate(obs))
|
|
25
|
+
t.is(measureArea(obs), 0)
|
|
26
|
+
t.is(measureVolume(obs), 0)
|
|
21
27
|
t.is(pts.length, 0)
|
|
22
28
|
})
|
|
23
29
|
|
|
@@ -25,6 +31,8 @@ test('roundedCylinder (zero radius)', (t) => {
|
|
|
25
31
|
const obs = roundedCylinder({ radius: 0, roundRadius: 0 })
|
|
26
32
|
const pts = geom3.toPoints(obs)
|
|
27
33
|
t.notThrows(() => geom3.validate(obs))
|
|
34
|
+
t.is(measureArea(obs), 0)
|
|
35
|
+
t.is(measureVolume(obs), 0)
|
|
28
36
|
t.is(pts.length, 0)
|
|
29
37
|
})
|
|
30
38
|
|
|
@@ -32,6 +40,8 @@ test('roundedCylinder (zero roundRadius)', (t) => {
|
|
|
32
40
|
const obs = roundedCylinder({ roundRadius: 0 })
|
|
33
41
|
const pts = geom3.toPoints(obs)
|
|
34
42
|
t.notThrows(() => geom3.validate(obs))
|
|
43
|
+
t.is(measureArea(obs), 18.789084266699856)
|
|
44
|
+
t.is(measureVolume(obs), 6.2428903045161)
|
|
35
45
|
t.is(pts.length, 96)
|
|
36
46
|
})
|
|
37
47
|
|
|
@@ -43,6 +53,8 @@ test('roundedCylinder (options)', (t) => {
|
|
|
43
53
|
]
|
|
44
54
|
|
|
45
55
|
t.notThrows(() => geom3.validate(obs))
|
|
56
|
+
t.is(measureArea(obs), 14.303000362787825)
|
|
57
|
+
t.is(measureVolume(obs), 4.121244903945666)
|
|
46
58
|
t.is(pts.length, 15)
|
|
47
59
|
|
|
48
60
|
// test center
|
|
@@ -72,6 +84,8 @@ test('roundedCylinder (options)', (t) => {
|
|
|
72
84
|
]
|
|
73
85
|
|
|
74
86
|
t.notThrows(() => geom3.validate(obs))
|
|
87
|
+
t.is(measureArea(obs), 14.303000362787827)
|
|
88
|
+
t.is(measureVolume(obs), 4.121244903945658)
|
|
75
89
|
t.is(pts.length, 15)
|
|
76
90
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
77
91
|
|
|
@@ -102,6 +116,8 @@ test('roundedCylinder (options)', (t) => {
|
|
|
102
116
|
]
|
|
103
117
|
|
|
104
118
|
t.notThrows(() => geom3.validate(obs))
|
|
119
|
+
t.is(measureArea(obs), 120.104345775433)
|
|
120
|
+
t.is(measureVolume(obs), 46.91878813722758)
|
|
105
121
|
t.is(pts.length, 15)
|
|
106
122
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
107
123
|
|
|
@@ -131,6 +147,8 @@ test('roundedCylinder (options)', (t) => {
|
|
|
131
147
|
]
|
|
132
148
|
|
|
133
149
|
t.notThrows(() => geom3.validate(obs))
|
|
150
|
+
t.is(measureArea(obs), 569.7191848255909)
|
|
151
|
+
t.is(measureVolume(obs), 412.1244903945666)
|
|
134
152
|
t.is(pts.length, 15)
|
|
135
153
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
136
154
|
|
|
@@ -160,6 +178,8 @@ test('roundedCylinder (options)', (t) => {
|
|
|
160
178
|
]
|
|
161
179
|
|
|
162
180
|
t.notThrows(() => geom3.validate(obs))
|
|
181
|
+
t.is(measureArea(obs), 602.8474323274462)
|
|
182
|
+
t.is(measureVolume(obs), 1030.3112259864165)
|
|
163
183
|
t.is(pts.length, 15)
|
|
164
184
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
165
185
|
})
|
|
@@ -45,7 +45,7 @@ export const roundedRectangle = (options) => {
|
|
|
45
45
|
size = size.map((v) => v / 2) // convert to radius
|
|
46
46
|
|
|
47
47
|
if (roundRadius > (size[0] - EPS) ||
|
|
48
|
-
roundRadius > (size[1] - EPS)) throw new Error('roundRadius must be smaller
|
|
48
|
+
roundRadius > (size[1] - EPS)) throw new Error('roundRadius must be smaller than the radius of all dimensions')
|
|
49
49
|
|
|
50
50
|
const cornerSegments = Math.floor(segments / 4)
|
|
51
51
|
|
|
@@ -2,6 +2,8 @@ import test from 'ava'
|
|
|
2
2
|
|
|
3
3
|
import { geom2 } from '../geometries/index.js'
|
|
4
4
|
|
|
5
|
+
import { measureArea } from '../measurements/index.js'
|
|
6
|
+
|
|
5
7
|
import { roundedRectangle } from './index.js'
|
|
6
8
|
|
|
7
9
|
import { comparePoints } from '../../test/helpers/index.js'
|
|
@@ -11,13 +13,15 @@ test('roundedRectangle (defaults)', (t) => {
|
|
|
11
13
|
const obs = geom2.toPoints(geometry)
|
|
12
14
|
|
|
13
15
|
t.notThrows(() => geom2.validate(geometry))
|
|
14
|
-
t.
|
|
16
|
+
t.is(measureArea(geometry), 3.964857806090323)
|
|
17
|
+
t.is(obs.length, 36)
|
|
15
18
|
})
|
|
16
19
|
|
|
17
20
|
test('roundedRectangle (zero size)', (t) => {
|
|
18
21
|
const obs = roundedRectangle({ size: [1, 0] })
|
|
19
22
|
const pts = geom2.toPoints(obs)
|
|
20
23
|
t.notThrows(() => geom2.validate(obs))
|
|
24
|
+
t.is(measureArea(obs), 0)
|
|
21
25
|
t.is(pts.length, 0)
|
|
22
26
|
})
|
|
23
27
|
|
|
@@ -25,7 +29,8 @@ test('roundedRectangle (zero radius)', (t) => {
|
|
|
25
29
|
const obs = roundedRectangle({ roundRadius: 0 })
|
|
26
30
|
const pts = geom2.toPoints(obs)
|
|
27
31
|
t.notThrows(() => geom2.validate(obs))
|
|
28
|
-
t.
|
|
32
|
+
t.is(measureArea(obs), 4)
|
|
33
|
+
t.is(pts.length, 4)
|
|
29
34
|
})
|
|
30
35
|
|
|
31
36
|
test('roundedRectangle (options)', (t) => {
|
|
@@ -55,7 +60,8 @@ test('roundedRectangle (options)', (t) => {
|
|
|
55
60
|
[5, 4.2]
|
|
56
61
|
]
|
|
57
62
|
t.notThrows(() => geom2.validate(geometry))
|
|
58
|
-
t.
|
|
63
|
+
t.is(measureArea(geometry), 3.962458698356829)
|
|
64
|
+
t.is(obs.length, 20)
|
|
59
65
|
t.true(comparePoints(obs, exp))
|
|
60
66
|
|
|
61
67
|
// test size
|
|
@@ -84,7 +90,8 @@ test('roundedRectangle (options)', (t) => {
|
|
|
84
90
|
[5, -2.8]
|
|
85
91
|
]
|
|
86
92
|
t.notThrows(() => geom2.validate(geometry))
|
|
87
|
-
t.
|
|
93
|
+
t.is(measureArea(geometry), 59.96245869835682)
|
|
94
|
+
t.is(obs.length, 20)
|
|
88
95
|
t.true(comparePoints(obs, exp))
|
|
89
96
|
|
|
90
97
|
// test roundRadius
|
|
@@ -113,12 +120,14 @@ test('roundedRectangle (options)', (t) => {
|
|
|
113
120
|
[5, -1.0000000000000004]
|
|
114
121
|
]
|
|
115
122
|
t.notThrows(() => geom2.validate(geometry))
|
|
116
|
-
t.
|
|
123
|
+
t.is(measureArea(geometry), 56.24586983568288)
|
|
124
|
+
t.is(obs.length, 20)
|
|
117
125
|
t.true(comparePoints(obs, exp))
|
|
118
126
|
|
|
119
127
|
// test segments
|
|
120
128
|
geometry = roundedRectangle({ size: [10, 6], roundRadius: 2, segments: 64 })
|
|
121
129
|
obs = geom2.toPoints(geometry)
|
|
122
130
|
t.notThrows(() => geom2.validate(geometry))
|
|
123
|
-
t.
|
|
131
|
+
t.is(measureArea(geometry), 56.546193962183764)
|
|
132
|
+
t.is(obs.length, 68)
|
|
124
133
|
})
|
|
@@ -2,6 +2,8 @@ import test from 'ava'
|
|
|
2
2
|
|
|
3
3
|
import { geom3 } from '../geometries/index.js'
|
|
4
4
|
|
|
5
|
+
import { measureArea, measureVolume } from '../measurements/index.js'
|
|
6
|
+
|
|
5
7
|
import { sphere } from './index.js'
|
|
6
8
|
|
|
7
9
|
import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
|
|
@@ -11,6 +13,8 @@ test('sphere (defaults)', (t) => {
|
|
|
11
13
|
const pts = geom3.toPoints(obs)
|
|
12
14
|
|
|
13
15
|
t.notThrows(() => geom3.validate(obs))
|
|
16
|
+
t.is(measureArea(obs), 12.465694088650583)
|
|
17
|
+
t.is(measureVolume(obs), 4.121941740785839)
|
|
14
18
|
t.is(pts.length, 512)
|
|
15
19
|
})
|
|
16
20
|
|
|
@@ -20,6 +24,8 @@ test('sphere (options)', (t) => {
|
|
|
20
24
|
let pts = geom3.toPoints(obs)
|
|
21
25
|
let exp = []
|
|
22
26
|
t.notThrows(() => geom3.validate(obs))
|
|
27
|
+
t.is(measureArea(obs), 296.5322084069296)
|
|
28
|
+
t.is(measureVolume(obs), 466.5063509461097)
|
|
23
29
|
t.is(pts.length, 72)
|
|
24
30
|
// t.true(comparePolygonsAsPoints(pts, exp))
|
|
25
31
|
|
|
@@ -103,6 +109,8 @@ test('sphere (options)', (t) => {
|
|
|
103
109
|
[0.4999999999999999, 0.5000000000000001, 0.7071067811865475]]
|
|
104
110
|
]
|
|
105
111
|
t.notThrows(() => geom3.validate(obs))
|
|
112
|
+
t.is(measureArea(obs), 11.013439076647456)
|
|
113
|
+
t.is(measureVolume(obs), 3.2189514164974597)
|
|
106
114
|
t.is(pts.length, 32)
|
|
107
115
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
108
116
|
|
|
@@ -160,6 +168,8 @@ test('sphere (options)', (t) => {
|
|
|
160
168
|
[[-3, 5, 8], [-2.2928932188134525, 5, 7.707106781186548], [-2.5, 5.5, 7.707106781186548]]
|
|
161
169
|
]
|
|
162
170
|
t.notThrows(() => geom3.validate(obs))
|
|
171
|
+
t.is(measureArea(obs), 11.013439076647467)
|
|
172
|
+
t.is(measureVolume(obs), 3.218951416497485)
|
|
163
173
|
t.is(pts.length, 32)
|
|
164
174
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
165
175
|
})
|
|
@@ -168,5 +178,7 @@ test('sphere (zero radius)', (t) => {
|
|
|
168
178
|
const obs = sphere({ radius: 0 })
|
|
169
179
|
const pts = geom3.toPoints(obs)
|
|
170
180
|
t.notThrows(() => geom3.validate(obs))
|
|
181
|
+
t.is(measureArea(obs), 0)
|
|
182
|
+
t.is(measureVolume(obs), 0)
|
|
171
183
|
t.is(pts.length, 0)
|
|
172
184
|
})
|
|
@@ -2,15 +2,18 @@ import test from 'ava'
|
|
|
2
2
|
|
|
3
3
|
import { geom2 } from '../geometries/index.js'
|
|
4
4
|
|
|
5
|
+
import { measureArea } from '../measurements/index.js'
|
|
6
|
+
|
|
5
7
|
import { square } from './index.js'
|
|
6
8
|
|
|
7
9
|
import { comparePoints } from '../../test/helpers/index.js'
|
|
8
10
|
|
|
9
11
|
test('square (defaults)', (t) => {
|
|
10
12
|
const geometry = square()
|
|
11
|
-
const
|
|
13
|
+
const pts = geom2.toPoints(geometry)
|
|
12
14
|
t.notThrows(() => geom2.validate(geometry))
|
|
13
|
-
t.
|
|
15
|
+
t.is(measureArea(geometry), 4)
|
|
16
|
+
t.is(pts.length, 4)
|
|
14
17
|
})
|
|
15
18
|
|
|
16
19
|
test('square (options)', (t) => {
|
|
@@ -25,6 +28,7 @@ test('square (options)', (t) => {
|
|
|
25
28
|
]
|
|
26
29
|
|
|
27
30
|
t.notThrows(() => geom2.validate(obs))
|
|
31
|
+
t.is(measureArea(obs), 49)
|
|
28
32
|
t.is(pts.length, 4)
|
|
29
33
|
t.true(comparePoints(pts, exp))
|
|
30
34
|
|
|
@@ -39,13 +43,15 @@ test('square (options)', (t) => {
|
|
|
39
43
|
]
|
|
40
44
|
|
|
41
45
|
t.notThrows(() => geom2.validate(obs))
|
|
46
|
+
t.is(measureArea(obs), 49)
|
|
42
47
|
t.is(pts.length, 4)
|
|
43
48
|
t.true(comparePoints(pts, exp))
|
|
44
49
|
})
|
|
45
50
|
|
|
46
51
|
test('square (zero size)', (t) => {
|
|
47
52
|
const geometry = square({ size: 0 })
|
|
48
|
-
const
|
|
53
|
+
const pts = geom2.toPoints(geometry)
|
|
49
54
|
t.notThrows(() => geom2.validate(geometry))
|
|
50
|
-
t.is(
|
|
55
|
+
t.is(measureArea(geometry), 0)
|
|
56
|
+
t.is(pts.length, 0)
|
|
51
57
|
})
|
|
@@ -2,6 +2,8 @@ import test from 'ava'
|
|
|
2
2
|
|
|
3
3
|
import { geom2 } from '../geometries/index.js'
|
|
4
4
|
|
|
5
|
+
import { measureArea } from '../measurements/index.js'
|
|
6
|
+
|
|
5
7
|
import { star } from './index.js'
|
|
6
8
|
|
|
7
9
|
import { comparePoints } from '../../test/helpers/index.js'
|
|
@@ -23,7 +25,8 @@ test('star (defaults)', (t) => {
|
|
|
23
25
|
]
|
|
24
26
|
|
|
25
27
|
t.notThrows(() => geom2.validate(geometry))
|
|
26
|
-
t.
|
|
28
|
+
t.is(measureArea(geometry), 1.1225699414489634)
|
|
29
|
+
t.is(pts.length, 10)
|
|
27
30
|
t.true(comparePoints(pts, exp))
|
|
28
31
|
})
|
|
29
32
|
|
|
@@ -45,7 +48,8 @@ test('star (options)', (t) => {
|
|
|
45
48
|
]
|
|
46
49
|
|
|
47
50
|
t.notThrows(() => geom2.validate(geometry))
|
|
48
|
-
t.
|
|
51
|
+
t.is(measureArea(geometry), 28.06424853622408)
|
|
52
|
+
t.is(pts.length, 10)
|
|
49
53
|
t.true(comparePoints(pts, exp))
|
|
50
54
|
|
|
51
55
|
// test vertices
|
|
@@ -71,7 +75,8 @@ test('star (options)', (t) => {
|
|
|
71
75
|
]
|
|
72
76
|
|
|
73
77
|
t.notThrows(() => geom2.validate(geometry))
|
|
74
|
-
t.
|
|
78
|
+
t.is(measureArea(geometry), 58.5786437626905)
|
|
79
|
+
t.is(pts.length, 16)
|
|
75
80
|
t.true(comparePoints(pts, exp))
|
|
76
81
|
|
|
77
82
|
// test density
|
|
@@ -97,7 +102,8 @@ test('star (options)', (t) => {
|
|
|
97
102
|
]
|
|
98
103
|
|
|
99
104
|
t.notThrows(() => geom2.validate(geometry))
|
|
100
|
-
t.
|
|
105
|
+
t.is(measureArea(geometry), 41.42135623730952)
|
|
106
|
+
t.is(pts.length, 16)
|
|
101
107
|
t.true(comparePoints(pts, exp))
|
|
102
108
|
|
|
103
109
|
// test innerRadius
|
|
@@ -123,7 +129,8 @@ test('star (options)', (t) => {
|
|
|
123
129
|
]
|
|
124
130
|
|
|
125
131
|
t.notThrows(() => geom2.validate(geometry))
|
|
126
|
-
t.
|
|
132
|
+
t.is(measureArea(geometry), 15.30733729460359)
|
|
133
|
+
t.is(pts.length, 16)
|
|
127
134
|
t.true(comparePoints(pts, exp))
|
|
128
135
|
|
|
129
136
|
// test start angle
|
|
@@ -143,6 +150,7 @@ test('star (options)', (t) => {
|
|
|
143
150
|
]
|
|
144
151
|
|
|
145
152
|
t.notThrows(() => geom2.validate(geometry))
|
|
146
|
-
t.
|
|
153
|
+
t.is(measureArea(geometry), 28.06424853622409)
|
|
154
|
+
t.is(pts.length, 10)
|
|
147
155
|
t.true(comparePoints(pts, exp))
|
|
148
156
|
})
|
package/src/primitives/torus.js
CHANGED
|
@@ -43,7 +43,7 @@ export const torus = (options) => {
|
|
|
43
43
|
if (!isGTE(startAngle, 0)) throw new Error('startAngle must be positive')
|
|
44
44
|
if (!isGT(outerRotation, 0)) throw new Error('outerRotation must be greater than zero')
|
|
45
45
|
|
|
46
|
-
if (innerRadius >= outerRadius) throw new Error('inner circle is
|
|
46
|
+
if (innerRadius >= outerRadius) throw new Error('inner circle is too large to rotate about the outer circle')
|
|
47
47
|
|
|
48
48
|
let innerCircle = circle({ radius: innerRadius, segments: innerSegments })
|
|
49
49
|
|
|
@@ -4,7 +4,7 @@ import { TAU } from '../maths/constants.js'
|
|
|
4
4
|
|
|
5
5
|
import { geom3 } from '../geometries/index.js'
|
|
6
6
|
|
|
7
|
-
import { measureBoundingBox } from '../measurements/index.js'
|
|
7
|
+
import { measureArea, measureBoundingBox, measureVolume } from '../measurements/index.js'
|
|
8
8
|
|
|
9
9
|
import { torus } from './index.js'
|
|
10
10
|
|
|
@@ -15,6 +15,8 @@ test('torus (defaults)', (t) => {
|
|
|
15
15
|
const pts = geom3.toPoints(obs)
|
|
16
16
|
|
|
17
17
|
t.notThrows(() => geom3.validate(obs))
|
|
18
|
+
t.is(measureArea(obs), 157.0282327749074)
|
|
19
|
+
t.is(measureVolume(obs), 77.94735870844194)
|
|
18
20
|
t.is(pts.length, 2048) // 32 * 32 * 2 (polys/segment) = 2048
|
|
19
21
|
|
|
20
22
|
const bounds = measureBoundingBox(obs)
|
|
@@ -26,6 +28,8 @@ test('torus (simple options)', (t) => {
|
|
|
26
28
|
const obs = torus({ innerRadius: 0.5, innerSegments: 4, outerRadius: 5, outerSegments: 8 })
|
|
27
29
|
const pts = geom3.toPoints(obs)
|
|
28
30
|
t.notThrows(() => geom3.validate(obs))
|
|
31
|
+
t.is(measureArea(obs), 83.36086132479792)
|
|
32
|
+
t.is(measureVolume(obs), 14.14213562373095)
|
|
29
33
|
t.is(pts.length, 64) // 4 * 8 * 2 (polys/segment) = 64
|
|
30
34
|
|
|
31
35
|
const bounds = measureBoundingBox(obs)
|
|
@@ -37,6 +41,8 @@ test('torus (complex options)', (t) => {
|
|
|
37
41
|
const obs = torus({ innerRadius: 1, outerRadius: 5, innerSegments: 32, outerSegments: 72, startAngle: TAU / 4, outerRotation: TAU / 4 })
|
|
38
42
|
const pts = geom3.toPoints(obs)
|
|
39
43
|
t.notThrows(() => geom3.validate(obs))
|
|
44
|
+
t.is(measureArea(obs), 55.472610544494)
|
|
45
|
+
t.is(measureVolume(obs), 24.484668362201525)
|
|
40
46
|
t.is(pts.length, 1212)
|
|
41
47
|
|
|
42
48
|
const bounds = measureBoundingBox(obs)
|
|
@@ -48,6 +54,8 @@ test('torus (startAngle)', (t) => {
|
|
|
48
54
|
const obs = torus({ startAngle: 1, endAngle: 1 + TAU })
|
|
49
55
|
const pts = geom3.toPoints(obs)
|
|
50
56
|
t.notThrows(() => geom3.validate(obs))
|
|
57
|
+
t.is(measureArea(obs), 157.0282327749074)
|
|
58
|
+
t.is(measureVolume(obs), 77.94735870844195)
|
|
51
59
|
t.is(pts.length, 2048)
|
|
52
60
|
})
|
|
53
61
|
|
|
@@ -57,5 +65,7 @@ test('torus (square by square)', (t) => {
|
|
|
57
65
|
const bounds = measureBoundingBox(obs)
|
|
58
66
|
const expectedBounds = [[-5, -5, -1], [5, 5, 1]]
|
|
59
67
|
t.notThrows(() => geom3.validate(obs))
|
|
68
|
+
t.is(measureArea(obs), 110.85125168440814)
|
|
69
|
+
t.is(measureVolume(obs), 32)
|
|
60
70
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected: ' + JSON.stringify(bounds))
|
|
61
71
|
})
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import test from 'ava'
|
|
2
2
|
|
|
3
|
-
import { TAU } from '../maths/constants.js'
|
|
4
|
-
|
|
5
3
|
import { degToRad } from '../utils/index.js'
|
|
6
4
|
|
|
7
5
|
import { geom2 } from '../geometries/index.js'
|
|
8
6
|
|
|
7
|
+
import { TAU } from '../maths/constants.js'
|
|
8
|
+
|
|
9
|
+
import { measureArea } from '../measurements/index.js'
|
|
10
|
+
|
|
9
11
|
import { triangle } from './index.js'
|
|
10
12
|
|
|
11
13
|
import { comparePoints } from '../../test/helpers/index.js'
|
|
@@ -20,7 +22,8 @@ test('triangle (defaults)', (t) => {
|
|
|
20
22
|
]
|
|
21
23
|
|
|
22
24
|
t.notThrows(() => geom2.validate(geometry))
|
|
23
|
-
t.
|
|
25
|
+
t.is(measureArea(geometry), 0.43301270189221935)
|
|
26
|
+
t.is(obs.length, 3)
|
|
24
27
|
t.true(comparePoints(obs, exp))
|
|
25
28
|
})
|
|
26
29
|
|
|
@@ -35,7 +38,8 @@ test('triangle (options)', (t) => {
|
|
|
35
38
|
]
|
|
36
39
|
|
|
37
40
|
t.notThrows(() => geom2.validate(geometry))
|
|
38
|
-
t.
|
|
41
|
+
t.is(measureArea(geometry), 20.33316256758894)
|
|
42
|
+
t.is(obs.length, 3)
|
|
39
43
|
t.true(comparePoints(obs, exp))
|
|
40
44
|
|
|
41
45
|
// test AAA
|
|
@@ -48,7 +52,7 @@ test('triangle (options)', (t) => {
|
|
|
48
52
|
]
|
|
49
53
|
|
|
50
54
|
t.notThrows(() => geom2.validate(geometry))
|
|
51
|
-
t.
|
|
55
|
+
t.is(obs.length, 3)
|
|
52
56
|
t.true(comparePoints(obs, exp))
|
|
53
57
|
|
|
54
58
|
// test AAS
|
|
@@ -61,7 +65,8 @@ test('triangle (options)', (t) => {
|
|
|
61
65
|
]
|
|
62
66
|
|
|
63
67
|
t.notThrows(() => geom2.validate(geometry))
|
|
64
|
-
t.
|
|
68
|
+
t.is(measureArea(geometry), 15.796947276180953)
|
|
69
|
+
t.is(obs.length, 3)
|
|
65
70
|
t.true(comparePoints(obs, exp))
|
|
66
71
|
|
|
67
72
|
// test ASA
|
|
@@ -74,7 +79,8 @@ test('triangle (options)', (t) => {
|
|
|
74
79
|
]
|
|
75
80
|
|
|
76
81
|
t.notThrows(() => geom2.validate(geometry))
|
|
77
|
-
t.
|
|
82
|
+
t.is(measureArea(geometry), 23.384870895211314)
|
|
83
|
+
t.is(obs.length, 3)
|
|
78
84
|
t.true(comparePoints(obs, exp))
|
|
79
85
|
|
|
80
86
|
// test SAS
|
|
@@ -87,7 +93,8 @@ test('triangle (options)', (t) => {
|
|
|
87
93
|
]
|
|
88
94
|
|
|
89
95
|
t.notThrows(() => geom2.validate(geometry))
|
|
90
|
-
t.
|
|
96
|
+
t.is(measureArea(geometry), 13.207417653898512)
|
|
97
|
+
t.is(obs.length, 3)
|
|
91
98
|
t.true(comparePoints(obs, exp))
|
|
92
99
|
|
|
93
100
|
// test SSA
|
|
@@ -100,6 +107,7 @@ test('triangle (options)', (t) => {
|
|
|
100
107
|
]
|
|
101
108
|
|
|
102
109
|
t.notThrows(() => geom2.validate(geometry))
|
|
103
|
-
t.
|
|
110
|
+
t.is(measureArea(geometry), 51.962298292283386)
|
|
111
|
+
t.is(obs.length, 3)
|
|
104
112
|
t.true(comparePoints(obs, exp))
|
|
105
113
|
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flattens and filters out nullish values from the given list of arguments.
|
|
3
|
+
* The arguments can be composed of multiple depths of objects and arrays.
|
|
4
|
+
* The output is a single flat array with no missing values.
|
|
5
|
+
* @param {Array} arr - list of arguments
|
|
6
|
+
* @returns {Array} a flat list of arguments
|
|
7
|
+
* @alias module:modeling/utils.coalesce
|
|
8
|
+
*/
|
|
9
|
+
export const coalesce = (arr) => flattenHelper(arr, [])
|
|
10
|
+
|
|
11
|
+
// Helper to recursively append to a given list.
|
|
12
|
+
// This is MUCH faster than other flatten methods.
|
|
13
|
+
const flattenHelper = (arr, out) => {
|
|
14
|
+
if (Array.isArray(arr)) {
|
|
15
|
+
arr.forEach((child) => flattenHelper(child, out))
|
|
16
|
+
} else if (arr != null && arr !== undefined) {
|
|
17
|
+
out.push(arr)
|
|
18
|
+
}
|
|
19
|
+
return out
|
|
20
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Utility functions of various sorts.
|
|
2
|
+
* Utility functions of various sorts, including conversions from different angular measures.
|
|
3
3
|
* @module modeling/utils
|
|
4
4
|
* @example
|
|
5
|
-
* import { degToRad } from '@jscad/modeling'
|
|
5
|
+
* import { degToRad, flatten, radiusToSegments, radToDeg } from '@jscad/modeling'
|
|
6
6
|
*/
|
|
7
7
|
export { degToRad } from './degToRad.js'
|
|
8
8
|
export { flatten } from './flatten.js'
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { fromValues } from '../vec2/index.js'
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
* Multiply the input matrix by a Vector2 (interpreted as 2 column, 1 row)
|
|
5
|
-
* (result = v*M)
|
|
6
|
-
* Fourth element is set to 1
|
|
7
|
-
* @param {Vec2} vector the input vector
|
|
8
|
-
* @param {Mat4} matrix the input matrix
|
|
9
|
-
* @returns {Vec2} output
|
|
10
|
-
*/
|
|
11
|
-
export const leftMultiplyVec2 = (vector, matrix) => {
|
|
12
|
-
const [v0, v1] = vector
|
|
13
|
-
const v2 = 0
|
|
14
|
-
const v3 = 1
|
|
15
|
-
let x = v0 * matrix[0] + v1 * matrix[4] + v2 * matrix[8] + v3 * matrix[12]
|
|
16
|
-
let y = v0 * matrix[1] + v1 * matrix[5] + v2 * matrix[9] + v3 * matrix[13]
|
|
17
|
-
const w = v0 * matrix[3] + v1 * matrix[7] + v2 * matrix[11] + v3 * matrix[15]
|
|
18
|
-
|
|
19
|
-
// scale such that fourth element becomes 1:
|
|
20
|
-
if (w !== 1) {
|
|
21
|
-
const invw = 1.0 / w
|
|
22
|
-
x *= invw
|
|
23
|
-
y *= invw
|
|
24
|
-
}
|
|
25
|
-
return fromValues(x, y)
|
|
26
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { fromValues } from '../vec3/index.js'
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
* Multiply the input matrix by a Vector3 (interpreted as 3 column, 1 row)
|
|
5
|
-
* (result = v*M)
|
|
6
|
-
* Fourth element is set to 1
|
|
7
|
-
* @param {Vec3} vector the input vector
|
|
8
|
-
* @param {Mat4} matrix the input matrix
|
|
9
|
-
* @returns {Vec3} output
|
|
10
|
-
*/
|
|
11
|
-
export const leftMultiplyVec3 = (vector, matrix) => {
|
|
12
|
-
const [v0, v1, v2] = vector
|
|
13
|
-
const v3 = 1
|
|
14
|
-
let x = v0 * matrix[0] + v1 * matrix[4] + v2 * matrix[8] + v3 * matrix[12]
|
|
15
|
-
let y = v0 * matrix[1] + v1 * matrix[5] + v2 * matrix[9] + v3 * matrix[13]
|
|
16
|
-
let z = v0 * matrix[2] + v1 * matrix[6] + v2 * matrix[10] + v3 * matrix[14]
|
|
17
|
-
const w = v0 * matrix[3] + v1 * matrix[7] + v2 * matrix[11] + v3 * matrix[15]
|
|
18
|
-
|
|
19
|
-
// scale such that fourth element becomes 1:
|
|
20
|
-
if (w !== 1) {
|
|
21
|
-
const invw = 1.0 / w
|
|
22
|
-
x *= invw
|
|
23
|
-
y *= invw
|
|
24
|
-
z *= invw
|
|
25
|
-
}
|
|
26
|
-
return fromValues(x, y, z)
|
|
27
|
-
}
|
package/src/maths/mat4/mirror.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* m the mat4 by the dimensions in the given vec3
|
|
3
|
-
* create an affine matrix for mirroring into an arbitrary plane:
|
|
4
|
-
*
|
|
5
|
-
* @param {Mat4} out - the receiving matrix
|
|
6
|
-
* @param {Vec3} vector - the vec3 to mirror the matrix by
|
|
7
|
-
* @param {Mat4} matrix - the matrix to mirror
|
|
8
|
-
* @returns {Mat4} out
|
|
9
|
-
*/
|
|
10
|
-
export const mirror = (out, vector, matrix) => {
|
|
11
|
-
const x = vector[0]
|
|
12
|
-
const y = vector[1]
|
|
13
|
-
const z = vector[2]
|
|
14
|
-
|
|
15
|
-
out[0] = matrix[0] * x
|
|
16
|
-
out[1] = matrix[1] * x
|
|
17
|
-
out[2] = matrix[2] * x
|
|
18
|
-
out[3] = matrix[3] * x
|
|
19
|
-
out[4] = matrix[4] * y
|
|
20
|
-
out[5] = matrix[5] * y
|
|
21
|
-
out[6] = matrix[6] * y
|
|
22
|
-
out[7] = matrix[7] * y
|
|
23
|
-
out[8] = matrix[8] * z
|
|
24
|
-
out[9] = matrix[9] * z
|
|
25
|
-
out[10] = matrix[10] * z
|
|
26
|
-
out[11] = matrix[11] * z
|
|
27
|
-
out[12] = matrix[12]
|
|
28
|
-
out[13] = matrix[13]
|
|
29
|
-
out[14] = matrix[14]
|
|
30
|
-
out[15] = matrix[15]
|
|
31
|
-
return out
|
|
32
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { fromValues } from '../vec2/index.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Multiply a 2D vector by a matrix (interpreted as 2 row, 1 column).
|
|
5
|
-
*
|
|
6
|
-
* Calculation: result = v*M, where the fourth element is set to 1.
|
|
7
|
-
* @param {Vec2} vector - input vector
|
|
8
|
-
* @param {Mat4} matrix - input matrix
|
|
9
|
-
* @returns {Vec2} a new vector
|
|
10
|
-
* @alias module:modeling/maths/mat4.rightMultiplyVec2
|
|
11
|
-
*/
|
|
12
|
-
export const rightMultiplyVec2 = (vector, matrix) => {
|
|
13
|
-
const [v0, v1] = vector
|
|
14
|
-
const v2 = 0
|
|
15
|
-
const v3 = 1
|
|
16
|
-
let x = v0 * matrix[0] + v1 * matrix[1] + v2 * matrix[2] + v3 * matrix[3]
|
|
17
|
-
let y = v0 * matrix[4] + v1 * matrix[5] + v2 * matrix[6] + v3 * matrix[7]
|
|
18
|
-
const w = v0 * matrix[12] + v1 * matrix[13] + v2 * matrix[14] + v3 * matrix[15]
|
|
19
|
-
|
|
20
|
-
// scale such that fourth element becomes 1:
|
|
21
|
-
if (w !== 1) {
|
|
22
|
-
const invw = 1.0 / w
|
|
23
|
-
x *= invw
|
|
24
|
-
y *= invw
|
|
25
|
-
}
|
|
26
|
-
return fromValues(x, y)
|
|
27
|
-
}
|