@jscad/modeling 3.0.0-alpha.0 → 3.0.1-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 +35 -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/poly3/type.d.ts +1 -1
- package/src/geometries/slice/validate.js +1 -2
- 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/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/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/splitPolygonByPlane.d.ts +33 -0
- 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/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 +4 -2
- 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/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
|
@@ -3,8 +3,11 @@ import test from 'ava'
|
|
|
3
3
|
import { comparePoints } from '../../../test/helpers/index.js'
|
|
4
4
|
|
|
5
5
|
import { colorize } from '../../colors/index.js'
|
|
6
|
+
|
|
6
7
|
import { geom3, poly3 } from '../../geometries/index.js'
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
import { measureArea, measureVolume } from '../../measurements/index.js'
|
|
10
|
+
|
|
8
11
|
import { cube, sphere } from '../../primitives/index.js'
|
|
9
12
|
|
|
10
13
|
import { offset } from './index.js'
|
|
@@ -13,6 +16,7 @@ test('offset: offset empty geom3', (t) => {
|
|
|
13
16
|
const geometry = geom3.create()
|
|
14
17
|
const result = offset({ }, geometry)
|
|
15
18
|
t.notThrows(() => geom3.validate(result))
|
|
19
|
+
t.is(measureArea(result), 0)
|
|
16
20
|
t.is(measureVolume(result), 0)
|
|
17
21
|
t.is(geom3.toPolygons(result).length, 0)
|
|
18
22
|
t.is(geom3.toPoints(result).length, 0)
|
|
@@ -50,6 +54,8 @@ test('offset: offset of a geom3 produces expected changes to polygons', (t) => {
|
|
|
50
54
|
]
|
|
51
55
|
|
|
52
56
|
t.notThrows.skip(() => geom3.validate(obs))
|
|
57
|
+
t.is(measureArea(obs), 3178.8059464475555)
|
|
58
|
+
t.is(measureVolume(obs), 13504.574121271067)
|
|
53
59
|
t.is(pts.length, 62)
|
|
54
60
|
t.true(comparePoints(pts[0], exp0))
|
|
55
61
|
t.true(comparePoints(pts[61], exp61))
|
|
@@ -58,6 +64,8 @@ test('offset: offset of a geom3 produces expected changes to polygons', (t) => {
|
|
|
58
64
|
const obs2 = offset({ delta: 5 }, geometry2)
|
|
59
65
|
const pts2 = geom3.toPoints(obs2)
|
|
60
66
|
t.notThrows.skip(() => geom3.validate(obs2))
|
|
67
|
+
t.is(measureArea(obs), 3178.8059464475555)
|
|
68
|
+
t.is(measureVolume(obs), 13504.574121271067)
|
|
61
69
|
t.is(pts2.length, 864)
|
|
62
70
|
})
|
|
63
71
|
|
|
@@ -85,9 +85,9 @@ export const offsetPath2 = (options, geometry) => {
|
|
|
85
85
|
internal: offsetFromPoints({ delta: -delta, corners, segments, closed }, points)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
const output = geometry.isClosed
|
|
89
|
-
createGeometryFromClosedPath(paths)
|
|
90
|
-
createGeometryFromOpenPath(paths, segments, corners, delta)
|
|
88
|
+
const output = geometry.isClosed
|
|
89
|
+
? createGeometryFromClosedPath(paths)
|
|
90
|
+
: createGeometryFromOpenPath(paths, segments, corners, delta)
|
|
91
91
|
if (geometry.color) output.color = geometry.color
|
|
92
92
|
return output
|
|
93
93
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as vec3 from '../../maths/vec3/index.js'
|
|
2
|
+
|
|
3
|
+
import { coalesce } from '../../utils/coalesce.js'
|
|
2
4
|
import { padArrayToLength } from '../../utils/padArrayToLength.js'
|
|
3
5
|
|
|
4
6
|
import { measureAggregateBoundingBox } from '../../measurements/measureAggregateBoundingBox.js'
|
|
@@ -34,9 +36,9 @@ const populateRelativeToFromBounds = (relativeTo, modes, bounds) => {
|
|
|
34
36
|
return relativeTo
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
const alignGeometries = (
|
|
38
|
-
const bounds = measureAggregateBoundingBox(
|
|
39
|
-
const translation =
|
|
39
|
+
const alignGeometries = (geometries, modes, relativeTo) => {
|
|
40
|
+
const bounds = measureAggregateBoundingBox(geometries)
|
|
41
|
+
const translation = vec3.create()
|
|
40
42
|
for (let i = 0; i < 3; i++) {
|
|
41
43
|
if (modes[i] === 'center') {
|
|
42
44
|
translation[i] = relativeTo[i] - (bounds[0][i] + bounds[1][i]) / 2
|
|
@@ -47,7 +49,7 @@ const alignGeometries = (geometry, modes, relativeTo) => {
|
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
return translate(translation,
|
|
52
|
+
return translate(translation, geometries)
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
/**
|
|
@@ -73,14 +75,13 @@ export const align = (options, ...geometries) => {
|
|
|
73
75
|
|
|
74
76
|
options = validateOptions(options)
|
|
75
77
|
let { modes, relativeTo, grouped } = options
|
|
76
|
-
geometries = flatten(geometries)
|
|
77
|
-
if (geometries.length === 0) throw new Error('align(): No geometries were provided to act upon')
|
|
78
78
|
|
|
79
79
|
if (relativeTo.filter((val) => val == null).length) {
|
|
80
80
|
const bounds = measureAggregateBoundingBox(geometries)
|
|
81
81
|
relativeTo = populateRelativeToFromBounds(relativeTo, modes, bounds)
|
|
82
82
|
}
|
|
83
83
|
if (grouped) {
|
|
84
|
+
geometries = coalesce(geometries)
|
|
84
85
|
geometries = alignGeometries(geometries, modes, relativeTo)
|
|
85
86
|
} else {
|
|
86
87
|
geometries = geometries.map((geometry) => alignGeometries(geometry, modes, relativeTo))
|
|
@@ -53,7 +53,7 @@ test('align: multiple objects ungrouped returns geometry aligned, different mode
|
|
|
53
53
|
cube({ size: 4, center: [10, 10, 10] }),
|
|
54
54
|
cube({ size: 2, center: [4, 4, 4] })
|
|
55
55
|
]
|
|
56
|
-
const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [30, 30, 30] }, original)
|
|
56
|
+
const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [30, 30, 30] }, ...original)
|
|
57
57
|
const bounds = measureAggregateBoundingBox(aligned)
|
|
58
58
|
const expectedBounds = [[28, 30, 26], [32, 34, 30]]
|
|
59
59
|
t.notThrows(() => geom3.validate(aligned[0]))
|
|
@@ -79,7 +79,7 @@ test('align: multiple objects ungrouped, relativeTo is nulls, returns geometry a
|
|
|
79
79
|
cube({ size: 2, center: [4, 4, 4] }),
|
|
80
80
|
cube({ size: 4, center: [10, 10, 10] })
|
|
81
81
|
]
|
|
82
|
-
const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [null, null, null], grouped: false }, original)
|
|
82
|
+
const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [null, null, null], grouped: false }, ...original)
|
|
83
83
|
const bounds = measureAggregateBoundingBox(aligned)
|
|
84
84
|
const expectedBounds = [[5.5, 3, 8], [9.5, 7, 12]]
|
|
85
85
|
t.notThrows(() => geom3.validate(aligned[0]))
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import * as geom2 from '../../geometries/geom2/index.js'
|
|
4
2
|
import * as geom3 from '../../geometries/geom3/index.js'
|
|
5
3
|
import * as path2 from '../../geometries/path2/index.js'
|
|
6
4
|
|
|
7
|
-
import {
|
|
5
|
+
import { measureAggregateBoundingBox } from '../../measurements/measureAggregateBoundingBox.js'
|
|
8
6
|
|
|
9
7
|
import { translate } from './translate.js'
|
|
10
8
|
|
|
@@ -15,7 +13,7 @@ const centerGeometry = (options, object) => {
|
|
|
15
13
|
}
|
|
16
14
|
const { axes, relativeTo } = Object.assign({}, defaults, options)
|
|
17
15
|
|
|
18
|
-
const bounds =
|
|
16
|
+
const bounds = measureAggregateBoundingBox(object)
|
|
19
17
|
const offset = [0, 0, 0]
|
|
20
18
|
if (axes[0]) offset[0] = relativeTo[0] - (bounds[0][0] + ((bounds[1][0] - bounds[0][0]) / 2))
|
|
21
19
|
if (axes[1]) offset[1] = relativeTo[1] - (bounds[0][1] + ((bounds[1][1] - bounds[0][1]) / 2))
|
|
@@ -43,8 +41,6 @@ export const center = (options, ...objects) => {
|
|
|
43
41
|
}
|
|
44
42
|
const { axes, relativeTo } = Object.assign({}, defaults, options)
|
|
45
43
|
|
|
46
|
-
objects = flatten(objects)
|
|
47
|
-
if (objects.length === 0) throw new Error('wrong number of arguments')
|
|
48
44
|
if (relativeTo.length !== 3) throw new Error('relativeTo must be an array of length 3')
|
|
49
45
|
|
|
50
46
|
options = { axes, relativeTo }
|
|
@@ -53,6 +49,7 @@ export const center = (options, ...objects) => {
|
|
|
53
49
|
if (path2.isA(object)) return centerGeometry(options, object)
|
|
54
50
|
if (geom2.isA(object)) return centerGeometry(options, object)
|
|
55
51
|
if (geom3.isA(object)) return centerGeometry(options, object)
|
|
52
|
+
if (Array.isArray(object)) return centerGeometry(options, object)
|
|
56
53
|
return object
|
|
57
54
|
})
|
|
58
55
|
return results.length === 1 ? results[0] : results
|
|
@@ -64,7 +61,7 @@ export const center = (options, ...objects) => {
|
|
|
64
61
|
* @return {Object|Array} the centered object, or a list of centered objects
|
|
65
62
|
* @alias module:modeling/transforms.centerX
|
|
66
63
|
*/
|
|
67
|
-
export const centerX = (...objects) => center({ axes: [true, false, false] }, objects)
|
|
64
|
+
export const centerX = (...objects) => center({ axes: [true, false, false] }, ...objects)
|
|
68
65
|
|
|
69
66
|
/**
|
|
70
67
|
* Center the given objects about the Y axis.
|
|
@@ -72,7 +69,7 @@ export const centerX = (...objects) => center({ axes: [true, false, false] }, ob
|
|
|
72
69
|
* @return {Object|Array} the centered object, or a list of centered objects
|
|
73
70
|
* @alias module:modeling/transforms.centerY
|
|
74
71
|
*/
|
|
75
|
-
export const centerY = (...objects) => center({ axes: [false, true, false] }, objects)
|
|
72
|
+
export const centerY = (...objects) => center({ axes: [false, true, false] }, ...objects)
|
|
76
73
|
|
|
77
74
|
/**
|
|
78
75
|
* Center the given objects about the Z axis.
|
|
@@ -80,4 +77,4 @@ export const centerY = (...objects) => center({ axes: [false, true, false] }, ob
|
|
|
80
77
|
* @return {Object|Array} the centered object, or a list of centered objects
|
|
81
78
|
* @alias module:modeling/transforms.centerZ
|
|
82
79
|
*/
|
|
83
|
-
export const centerZ = (...objects) => center({ axes: [false, false, true] }, objects)
|
|
80
|
+
export const centerZ = (...objects) => center({ axes: [false, false, true] }, ...objects)
|
|
@@ -4,7 +4,9 @@ import { comparePoints, comparePolygonsAsPoints } from '../../../test/helpers/in
|
|
|
4
4
|
|
|
5
5
|
import { geom2, geom3, path2 } from '../../geometries/index.js'
|
|
6
6
|
|
|
7
|
-
import { measureArea, measureVolume } from '../../measurements/index.js'
|
|
7
|
+
import { measureArea, measureAggregateBoundingBox, measureVolume } from '../../measurements/index.js'
|
|
8
|
+
|
|
9
|
+
import { square } from '../../primitives/index.js'
|
|
8
10
|
|
|
9
11
|
import { center, centerX, centerY, centerZ } from './index.js'
|
|
10
12
|
|
|
@@ -136,3 +138,19 @@ test('center: centering of multiple objects produces expected changes', (t) => {
|
|
|
136
138
|
t.notThrows(() => geom2.validate(centered[2]))
|
|
137
139
|
t.true(comparePoints(pts2, exp2))
|
|
138
140
|
})
|
|
141
|
+
|
|
142
|
+
test('center multiple separate', (t) => {
|
|
143
|
+
const square1 = square({ size: 4, center: [10, 10] })
|
|
144
|
+
const square2 = square({ size: 6, center: [-10, -10] })
|
|
145
|
+
const obs = center({}, square1, square2)
|
|
146
|
+
t.notThrows(() => obs.map(geom2.validate))
|
|
147
|
+
t.deepEqual([[-3, -3, 0], [3, 3, 0]], measureAggregateBoundingBox(obs))
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('center multiple grouped', (t) => {
|
|
151
|
+
const square1 = square({ size: 4, center: [10, 10] })
|
|
152
|
+
const square2 = square({ size: 6, center: [-10, -10] })
|
|
153
|
+
const obs = center({}, [square1, square2])
|
|
154
|
+
t.notThrows(() => obs.map(geom2.validate))
|
|
155
|
+
t.deepEqual([[-12.5, -12.5, 0], [12.5, 12.5, 0]], measureAggregateBoundingBox(obs))
|
|
156
|
+
})
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import * as mat4 from '../../maths/mat4/index.js'
|
|
4
2
|
import * as plane from '../../maths/plane/index.js'
|
|
5
3
|
|
|
@@ -26,9 +24,6 @@ export const mirror = (options, ...objects) => {
|
|
|
26
24
|
}
|
|
27
25
|
const { origin, normal } = Object.assign({}, defaults, options)
|
|
28
26
|
|
|
29
|
-
objects = flatten(objects)
|
|
30
|
-
if (objects.length === 0) throw new Error('wrong number of arguments')
|
|
31
|
-
|
|
32
27
|
const planeOfMirror = plane.fromNormalAndPoint(plane.create(), normal, origin)
|
|
33
28
|
// verify the plane, i.e. check that the given normal was valid
|
|
34
29
|
if (Number.isNaN(planeOfMirror[0])) {
|
|
@@ -41,6 +36,8 @@ export const mirror = (options, ...objects) => {
|
|
|
41
36
|
if (path2.isA(object)) return path2.transform(matrix, object)
|
|
42
37
|
if (geom2.isA(object)) return geom2.transform(matrix, object)
|
|
43
38
|
if (geom3.isA(object)) return geom3.transform(matrix, object)
|
|
39
|
+
// handle recursive arrays
|
|
40
|
+
if (Array.isArray(object)) return mirror(options, ...object)
|
|
44
41
|
return object
|
|
45
42
|
})
|
|
46
43
|
return results.length === 1 ? results[0] : results
|
|
@@ -52,7 +49,7 @@ export const mirror = (options, ...objects) => {
|
|
|
52
49
|
* @return {Object|Array} the mirrored object, or a list of mirrored objects
|
|
53
50
|
* @alias module:modeling/transforms.mirrorX
|
|
54
51
|
*/
|
|
55
|
-
export const mirrorX = (...objects) => mirror({ normal: [1, 0, 0] }, objects)
|
|
52
|
+
export const mirrorX = (...objects) => mirror({ normal: [1, 0, 0] }, ...objects)
|
|
56
53
|
|
|
57
54
|
/**
|
|
58
55
|
* Mirror the given objects about the Y axis.
|
|
@@ -60,7 +57,7 @@ export const mirrorX = (...objects) => mirror({ normal: [1, 0, 0] }, objects)
|
|
|
60
57
|
* @return {Object|Array} the mirrored object, or a list of mirrored objects
|
|
61
58
|
* @alias module:modeling/transforms.mirrorY
|
|
62
59
|
*/
|
|
63
|
-
export const mirrorY = (...objects) => mirror({ normal: [0, 1, 0] }, objects)
|
|
60
|
+
export const mirrorY = (...objects) => mirror({ normal: [0, 1, 0] }, ...objects)
|
|
64
61
|
|
|
65
62
|
/**
|
|
66
63
|
* Mirror the given objects about the Z axis.
|
|
@@ -68,4 +65,4 @@ export const mirrorY = (...objects) => mirror({ normal: [0, 1, 0] }, objects)
|
|
|
68
65
|
* @return {Object|Array} the mirrored object, or a list of mirrored objects
|
|
69
66
|
* @alias module:modeling/transforms.mirrorZ
|
|
70
67
|
*/
|
|
71
|
-
export const mirrorZ = (...objects) => mirror({ normal: [0, 0, 1] }, objects)
|
|
68
|
+
export const mirrorZ = (...objects) => mirror({ normal: [0, 0, 1] }, ...objects)
|
|
@@ -42,29 +42,29 @@ test('mirror: mirroring of geom2 about X/Y produces expected changes to points',
|
|
|
42
42
|
// mirror about X
|
|
43
43
|
let mirrored = mirror({ normal: [1, 0, 0] }, geometry)
|
|
44
44
|
let obs = geom2.toPoints(mirrored)
|
|
45
|
-
let exp = [[
|
|
45
|
+
let exp = [[-10, -5], [0, 5], [5, -5]]
|
|
46
46
|
t.notThrows(() => geom2.validate(mirrored))
|
|
47
|
-
t.is(measureArea(mirrored),
|
|
47
|
+
t.is(measureArea(mirrored), measureArea(geometry))
|
|
48
48
|
t.true(comparePoints(obs, exp))
|
|
49
49
|
|
|
50
50
|
mirrored = mirrorX(geometry)
|
|
51
51
|
obs = geom2.toPoints(mirrored)
|
|
52
52
|
t.notThrows(() => geom2.validate(mirrored))
|
|
53
|
-
t.is(measureArea(mirrored),
|
|
53
|
+
t.is(measureArea(mirrored), measureArea(geometry))
|
|
54
54
|
t.true(comparePoints(obs, exp))
|
|
55
55
|
|
|
56
56
|
// mirror about Y
|
|
57
57
|
mirrored = mirror({ normal: [0, 1, 0] }, geometry)
|
|
58
58
|
obs = geom2.toPoints(mirrored)
|
|
59
|
-
exp = [[
|
|
59
|
+
exp = [[10, 5], [0, -5], [-5, 5]]
|
|
60
60
|
t.notThrows(() => geom2.validate(mirrored))
|
|
61
|
-
t.is(measureArea(mirrored),
|
|
61
|
+
t.is(measureArea(mirrored), measureArea(geometry))
|
|
62
62
|
t.true(comparePoints(obs, exp))
|
|
63
63
|
|
|
64
64
|
mirrored = mirrorY(geometry)
|
|
65
65
|
obs = geom2.toPoints(mirrored)
|
|
66
66
|
t.notThrows(() => geom2.validate(mirrored))
|
|
67
|
-
t.is(measureArea(mirrored),
|
|
67
|
+
t.is(measureArea(mirrored), measureArea(geometry))
|
|
68
68
|
t.true(comparePoints(obs, exp))
|
|
69
69
|
})
|
|
70
70
|
|
|
@@ -158,7 +158,7 @@ test('mirror: mirroring of multiple objects produces an array of mirrored object
|
|
|
158
158
|
t.true(comparePoints(obs, exp))
|
|
159
159
|
|
|
160
160
|
obs = geom2.toPoints(mirrored[2])
|
|
161
|
-
exp = [[
|
|
161
|
+
exp = [[10, 5], [0, -5], [-5, 5]]
|
|
162
162
|
t.notThrows(() => geom2.validate(mirrored[2]))
|
|
163
163
|
t.true(comparePoints(obs, exp))
|
|
164
164
|
})
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import * as mat4 from '../../maths/mat4/index.js'
|
|
4
2
|
|
|
5
3
|
import * as geom2 from '../../geometries/geom2/index.js'
|
|
@@ -19,9 +17,6 @@ import * as path2 from '../../geometries/path2/index.js'
|
|
|
19
17
|
export const rotate = (angles, ...objects) => {
|
|
20
18
|
if (!Array.isArray(angles)) throw new Error('angles must be an array')
|
|
21
19
|
|
|
22
|
-
objects = flatten(objects)
|
|
23
|
-
if (objects.length === 0) throw new Error('wrong number of arguments')
|
|
24
|
-
|
|
25
20
|
// adjust the angles if necessary
|
|
26
21
|
angles = angles.slice() // don't modify the original
|
|
27
22
|
while (angles.length < 3) angles.push(0)
|
|
@@ -36,6 +31,8 @@ export const rotate = (angles, ...objects) => {
|
|
|
36
31
|
if (path2.isA(object)) return path2.transform(matrix, object)
|
|
37
32
|
if (geom2.isA(object)) return geom2.transform(matrix, object)
|
|
38
33
|
if (geom3.isA(object)) return geom3.transform(matrix, object)
|
|
34
|
+
// handle recursive arrays
|
|
35
|
+
if (Array.isArray(object)) return rotate(angles, ...object)
|
|
39
36
|
return object
|
|
40
37
|
})
|
|
41
38
|
return results.length === 1 ? results[0] : results
|
|
@@ -48,7 +45,7 @@ export const rotate = (angles, ...objects) => {
|
|
|
48
45
|
* @return {Object|Array} the rotated object, or a list of rotated objects
|
|
49
46
|
* @alias module:modeling/transforms.rotateX
|
|
50
47
|
*/
|
|
51
|
-
export const rotateX = (angle, ...objects) => rotate([angle, 0, 0], objects)
|
|
48
|
+
export const rotateX = (angle, ...objects) => rotate([angle, 0, 0], ...objects)
|
|
52
49
|
|
|
53
50
|
/**
|
|
54
51
|
* Rotate the given objects about the Y axis, using the given options.
|
|
@@ -57,7 +54,7 @@ export const rotateX = (angle, ...objects) => rotate([angle, 0, 0], objects)
|
|
|
57
54
|
* @return {Object|Array} the rotated object, or a list of rotated objects
|
|
58
55
|
* @alias module:modeling/transforms.rotateY
|
|
59
56
|
*/
|
|
60
|
-
export const rotateY = (angle, ...objects) => rotate([0, angle, 0], objects)
|
|
57
|
+
export const rotateY = (angle, ...objects) => rotate([0, angle, 0], ...objects)
|
|
61
58
|
|
|
62
59
|
/**
|
|
63
60
|
* Rotate the given objects about the Z axis, using the given options.
|
|
@@ -66,4 +63,4 @@ export const rotateY = (angle, ...objects) => rotate([0, angle, 0], objects)
|
|
|
66
63
|
* @return {Object|Array} the rotated object, or a list of rotated objects
|
|
67
64
|
* @alias module:modeling/transforms.rotateZ
|
|
68
65
|
*/
|
|
69
|
-
export const rotateZ = (angle, ...objects) => rotate([0, 0, angle], objects)
|
|
66
|
+
export const rotateZ = (angle, ...objects) => rotate([0, 0, angle], ...objects)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import * as mat4 from '../../maths/mat4/index.js'
|
|
4
2
|
|
|
5
3
|
import * as geom2 from '../../geometries/geom2/index.js'
|
|
@@ -19,9 +17,6 @@ import * as path2 from '../../geometries/path2/index.js'
|
|
|
19
17
|
export const scale = (factors, ...objects) => {
|
|
20
18
|
if (!Array.isArray(factors)) throw new Error('factors must be an array')
|
|
21
19
|
|
|
22
|
-
objects = flatten(objects)
|
|
23
|
-
if (objects.length === 0) throw new Error('wrong number of arguments')
|
|
24
|
-
|
|
25
20
|
// adjust the factors if necessary
|
|
26
21
|
factors = factors.slice() // don't modify the original
|
|
27
22
|
while (factors.length < 3) factors.push(1)
|
|
@@ -34,6 +29,8 @@ export const scale = (factors, ...objects) => {
|
|
|
34
29
|
if (path2.isA(object)) return path2.transform(matrix, object)
|
|
35
30
|
if (geom2.isA(object)) return geom2.transform(matrix, object)
|
|
36
31
|
if (geom3.isA(object)) return geom3.transform(matrix, object)
|
|
32
|
+
// handle recursive arrays
|
|
33
|
+
if (Array.isArray(object)) return scale(factors, ...object)
|
|
37
34
|
return object
|
|
38
35
|
})
|
|
39
36
|
return results.length === 1 ? results[0] : results
|
|
@@ -46,7 +43,7 @@ export const scale = (factors, ...objects) => {
|
|
|
46
43
|
* @return {Object|Array} the scaled object, or a list of scaled objects
|
|
47
44
|
* @alias module:modeling/transforms.scaleX
|
|
48
45
|
*/
|
|
49
|
-
export const scaleX = (factor, ...objects) => scale([factor, 1, 1], objects)
|
|
46
|
+
export const scaleX = (factor, ...objects) => scale([factor, 1, 1], ...objects)
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
49
|
* Scale the given objects about the Y axis using the given options.
|
|
@@ -55,7 +52,7 @@ export const scaleX = (factor, ...objects) => scale([factor, 1, 1], objects)
|
|
|
55
52
|
* @return {Object|Array} the scaled object, or a list of scaled objects
|
|
56
53
|
* @alias module:modeling/transforms.scaleY
|
|
57
54
|
*/
|
|
58
|
-
export const scaleY = (factor, ...objects) => scale([1, factor, 1], objects)
|
|
55
|
+
export const scaleY = (factor, ...objects) => scale([1, factor, 1], ...objects)
|
|
59
56
|
|
|
60
57
|
/**
|
|
61
58
|
* Scale the given objects about the Z axis using the given options.
|
|
@@ -64,4 +61,4 @@ export const scaleY = (factor, ...objects) => scale([1, factor, 1], objects)
|
|
|
64
61
|
* @return {Object|Array} the scaled object, or a list of scaled objects
|
|
65
62
|
* @alias module:modeling/transforms.scaleZ
|
|
66
63
|
*/
|
|
67
|
-
export const scaleZ = (factor, ...objects) => scale([1, 1, factor], objects)
|
|
64
|
+
export const scaleZ = (factor, ...objects) => scale([1, 1, factor], ...objects)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import * as geom2 from '../../geometries/geom2/index.js'
|
|
4
2
|
import * as geom3 from '../../geometries/geom3/index.js'
|
|
5
3
|
import * as path2 from '../../geometries/path2/index.js'
|
|
@@ -17,13 +15,12 @@ import * as path2 from '../../geometries/path2/index.js'
|
|
|
17
15
|
export const transform = (matrix, ...objects) => {
|
|
18
16
|
// TODO how to check that the matrix is REAL?
|
|
19
17
|
|
|
20
|
-
objects = flatten(objects)
|
|
21
|
-
if (objects.length === 0) throw new Error('wrong number of arguments')
|
|
22
|
-
|
|
23
18
|
const results = objects.map((object) => {
|
|
24
19
|
if (path2.isA(object)) return path2.transform(matrix, object)
|
|
25
20
|
if (geom2.isA(object)) return geom2.transform(matrix, object)
|
|
26
21
|
if (geom3.isA(object)) return geom3.transform(matrix, object)
|
|
22
|
+
// handle recursive arrays
|
|
23
|
+
if (Array.isArray(object)) return transform(matrix, ...object)
|
|
27
24
|
return object
|
|
28
25
|
})
|
|
29
26
|
return results.length === 1 ? results[0] : results
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import * as mat4 from '../../maths/mat4/index.js'
|
|
4
2
|
|
|
5
3
|
import * as geom2 from '../../geometries/geom2/index.js'
|
|
@@ -19,9 +17,6 @@ import * as path2 from '../../geometries/path2/index.js'
|
|
|
19
17
|
export const translate = (offset, ...objects) => {
|
|
20
18
|
if (!Array.isArray(offset)) throw new Error('offset must be an array')
|
|
21
19
|
|
|
22
|
-
objects = flatten(objects)
|
|
23
|
-
if (objects.length === 0) throw new Error('wrong number of arguments')
|
|
24
|
-
|
|
25
20
|
// adjust the offset if necessary
|
|
26
21
|
offset = offset.slice() // don't modify the original
|
|
27
22
|
while (offset.length < 3) offset.push(0)
|
|
@@ -32,6 +27,8 @@ export const translate = (offset, ...objects) => {
|
|
|
32
27
|
if (path2.isA(object)) return path2.transform(matrix, object)
|
|
33
28
|
if (geom2.isA(object)) return geom2.transform(matrix, object)
|
|
34
29
|
if (geom3.isA(object)) return geom3.transform(matrix, object)
|
|
30
|
+
// handle recursive arrays
|
|
31
|
+
if (Array.isArray(object)) return translate(offset, ...object)
|
|
35
32
|
return object
|
|
36
33
|
})
|
|
37
34
|
return results.length === 1 ? results[0] : results
|
|
@@ -44,7 +41,7 @@ export const translate = (offset, ...objects) => {
|
|
|
44
41
|
* @return {Object|Array} the translated object, or a list of translated objects
|
|
45
42
|
* @alias module:modeling/transforms.translateX
|
|
46
43
|
*/
|
|
47
|
-
export const translateX = (offset, ...objects) => translate([offset, 0, 0], objects)
|
|
44
|
+
export const translateX = (offset, ...objects) => translate([offset, 0, 0], ...objects)
|
|
48
45
|
|
|
49
46
|
/**
|
|
50
47
|
* Translate the given objects along the Y axis using the given options.
|
|
@@ -53,7 +50,7 @@ export const translateX = (offset, ...objects) => translate([offset, 0, 0], obje
|
|
|
53
50
|
* @return {Object|Array} the translated object, or a list of translated objects
|
|
54
51
|
* @alias module:modeling/transforms.translateY
|
|
55
52
|
*/
|
|
56
|
-
export const translateY = (offset, ...objects) => translate([0, offset, 0], objects)
|
|
53
|
+
export const translateY = (offset, ...objects) => translate([0, offset, 0], ...objects)
|
|
57
54
|
|
|
58
55
|
/**
|
|
59
56
|
* Translate the given objects along the Z axis using the given options.
|
|
@@ -62,4 +59,4 @@ export const translateY = (offset, ...objects) => translate([0, offset, 0], obje
|
|
|
62
59
|
* @return {Object|Array} the translated object, or a list of translated objects
|
|
63
60
|
* @alias module:modeling/transforms.translateZ
|
|
64
61
|
*/
|
|
65
|
-
export const translateZ = (offset, ...objects) => translate([0, 0, offset], objects)
|
|
62
|
+
export const translateZ = (offset, ...objects) => translate([0, 0, offset], ...objects)
|
package/src/primitives/arc.js
CHANGED
|
@@ -17,6 +17,8 @@ import { isGT, isGTE, isNumberArray } from './commonChecks.js'
|
|
|
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
|
|
19
19
|
* @alias module:modeling/primitives.arc
|
|
20
|
+
* @example
|
|
21
|
+
* let myshape = arc({ center: [-1, -1], radius: 2, endAngle: (TAU / 4)})
|
|
20
22
|
*/
|
|
21
23
|
export const arc = (options) => {
|
|
22
24
|
const defaults = {
|
|
@@ -13,7 +13,7 @@ test('arc (defaults)', (t) => {
|
|
|
13
13
|
const obs = path2.toPoints(geometry)
|
|
14
14
|
|
|
15
15
|
t.notThrows(() => path2.validate(geometry))
|
|
16
|
-
t.
|
|
16
|
+
t.is(obs.length, 33)
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
test('arc (options)', (t) => {
|
|
@@ -41,7 +41,7 @@ test('arc (options)', (t) => {
|
|
|
41
41
|
let obs = path2.toPoints(geometry)
|
|
42
42
|
|
|
43
43
|
t.notThrows(() => path2.validate(geometry))
|
|
44
|
-
t.
|
|
44
|
+
t.is(obs.length, 17)
|
|
45
45
|
t.true(comparePoints(obs, exp))
|
|
46
46
|
|
|
47
47
|
// test radius
|
|
@@ -68,7 +68,7 @@ test('arc (options)', (t) => {
|
|
|
68
68
|
obs = path2.toPoints(geometry)
|
|
69
69
|
|
|
70
70
|
t.notThrows(() => path2.validate(geometry))
|
|
71
|
-
t.
|
|
71
|
+
t.is(obs.length, 17)
|
|
72
72
|
t.true(comparePoints(obs, exp))
|
|
73
73
|
|
|
74
74
|
// test startAngle
|
|
@@ -92,7 +92,7 @@ test('arc (options)', (t) => {
|
|
|
92
92
|
obs = path2.toPoints(geometry)
|
|
93
93
|
|
|
94
94
|
t.notThrows(() => path2.validate(geometry))
|
|
95
|
-
t.
|
|
95
|
+
t.is(obs.length, 14)
|
|
96
96
|
t.true(comparePoints(obs, exp))
|
|
97
97
|
|
|
98
98
|
// test endAngle
|
|
@@ -108,7 +108,7 @@ test('arc (options)', (t) => {
|
|
|
108
108
|
obs = path2.toPoints(geometry)
|
|
109
109
|
|
|
110
110
|
t.notThrows(() => path2.validate(geometry))
|
|
111
|
-
t.
|
|
111
|
+
t.is(obs.length, 6)
|
|
112
112
|
t.true(comparePoints(obs, exp))
|
|
113
113
|
|
|
114
114
|
// test makeTangent
|
|
@@ -137,7 +137,7 @@ test('arc (options)', (t) => {
|
|
|
137
137
|
obs = path2.toPoints(geometry)
|
|
138
138
|
|
|
139
139
|
t.notThrows(() => path2.validate(geometry))
|
|
140
|
-
t.
|
|
140
|
+
t.is(obs.length, 19)
|
|
141
141
|
t.true(comparePoints(obs, exp))
|
|
142
142
|
|
|
143
143
|
// test segments
|
|
@@ -156,7 +156,7 @@ test('arc (options)', (t) => {
|
|
|
156
156
|
obs = path2.toPoints(geometry)
|
|
157
157
|
|
|
158
158
|
t.notThrows(() => path2.validate(geometry))
|
|
159
|
-
t.
|
|
159
|
+
t.is(obs.length, 9)
|
|
160
160
|
t.true(comparePoints(obs, exp))
|
|
161
161
|
})
|
|
162
162
|
|
|
@@ -173,7 +173,7 @@ test('arc (rotations)', (t) => {
|
|
|
173
173
|
let obs = path2.toPoints(geometry)
|
|
174
174
|
|
|
175
175
|
t.notThrows(() => path2.validate(geometry))
|
|
176
|
-
t.
|
|
176
|
+
t.is(obs.length, 6)
|
|
177
177
|
t.true(comparePoints(obs, exp))
|
|
178
178
|
|
|
179
179
|
exp = [
|
|
@@ -192,7 +192,7 @@ test('arc (rotations)', (t) => {
|
|
|
192
192
|
obs = path2.toPoints(geometry)
|
|
193
193
|
|
|
194
194
|
t.notThrows(() => path2.validate(geometry))
|
|
195
|
-
t.
|
|
195
|
+
t.is(obs.length, 10)
|
|
196
196
|
t.true(comparePoints(obs, exp))
|
|
197
197
|
|
|
198
198
|
exp = [
|
|
@@ -211,7 +211,7 @@ test('arc (rotations)', (t) => {
|
|
|
211
211
|
obs = path2.toPoints(geometry)
|
|
212
212
|
|
|
213
213
|
t.notThrows(() => path2.validate(geometry))
|
|
214
|
-
t.
|
|
214
|
+
t.is(obs.length, 10)
|
|
215
215
|
t.true(comparePoints(obs, exp))
|
|
216
216
|
|
|
217
217
|
exp = [[-1.8369701987210297e-16, -1]]
|
|
@@ -219,6 +219,6 @@ test('arc (rotations)', (t) => {
|
|
|
219
219
|
obs = path2.toPoints(geometry)
|
|
220
220
|
|
|
221
221
|
t.notThrows(() => path2.validate(geometry))
|
|
222
|
-
t.
|
|
222
|
+
t.is(obs.length, 1)
|
|
223
223
|
t.true(comparePoints(obs, exp))
|
|
224
224
|
})
|