@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscad/modeling",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1-alpha.0",
|
|
4
4
|
"description": "Constructive Solid Geometry (CSG) Library for JSCAD",
|
|
5
5
|
"homepage": "https://openjscad.xyz/",
|
|
6
6
|
"repository": "https://github.com/jscad/OpenJSCAD.org",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"rollup": "^2.79.1",
|
|
65
65
|
"rollup-plugin-banner": "^0.2.1"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "0660b5c1f1a5faf54d4cfae1cb85bb94182a8d32"
|
|
68
68
|
}
|
package/rollup.config.js
CHANGED
|
@@ -16,6 +16,6 @@ export default {
|
|
|
16
16
|
],
|
|
17
17
|
plugins: [
|
|
18
18
|
banner('<%= pkg.description %>\n@module <%= pkg.name %>\n@version <%= pkg.version %>\n@license <%= pkg.license %>'),
|
|
19
|
-
terser({ compress: { module: true }, mangle: false, format: { comments: 'some'} })
|
|
19
|
+
terser({ compress: { module: true }, mangle: false, format: { comments: 'some' } })
|
|
20
20
|
]
|
|
21
21
|
}
|
package/src/colors/colorize.js
CHANGED
|
@@ -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'
|
|
@@ -47,14 +45,12 @@ export const colorize = (color, ...objects) => {
|
|
|
47
45
|
if (color.length < 3) throw new Error('color must contain R, G and B values')
|
|
48
46
|
if (color.length === 3) color = [color[0], color[1], color[2], 1.0] // add alpha
|
|
49
47
|
|
|
50
|
-
objects = flatten(objects)
|
|
51
|
-
if (objects.length === 0) throw new Error('wrong number of arguments')
|
|
52
|
-
|
|
53
48
|
const results = objects.map((object) => {
|
|
54
49
|
if (geom2.isA(object)) return colorGeom2(color, object)
|
|
55
50
|
if (geom3.isA(object)) return colorGeom3(color, object)
|
|
56
51
|
if (path2.isA(object)) return colorPath2(color, object)
|
|
57
52
|
if (poly3.isA(object)) return colorPoly3(color, object)
|
|
53
|
+
if (Array.isArray(object)) return colorize(color, ...object)
|
|
58
54
|
|
|
59
55
|
object.color = color
|
|
60
56
|
return object
|
|
@@ -11,14 +11,12 @@ test('color (rgb on objects)', (t) => {
|
|
|
11
11
|
const obs = colorize([1, 0, 0], obj1, obj2)
|
|
12
12
|
const exp1 = { color: [1, 0, 0, 1] }
|
|
13
13
|
const exp2 = { id: 'a', color: [1, 0, 0, 1] }
|
|
14
|
+
const exp3 = { id: 'b', color: [1, 0, 0, 1] }
|
|
14
15
|
|
|
15
|
-
t.is(obs.length,
|
|
16
|
+
t.is(obs.length, 2)
|
|
16
17
|
t.deepEqual(obs[0], exp1)
|
|
17
|
-
t.deepEqual(obs[1], exp2)
|
|
18
|
-
|
|
19
|
-
const obs3 = colorize([1, 0, 0], obj1)
|
|
20
|
-
const exp3 = { color: [1, 0, 0, 1] }
|
|
21
|
-
t.deepEqual(obs3, exp3)
|
|
18
|
+
t.deepEqual(obs[1][0], exp2)
|
|
19
|
+
t.deepEqual(obs[1][1], exp3)
|
|
22
20
|
})
|
|
23
21
|
|
|
24
22
|
test('color (rgba on objects)', (t) => {
|
|
@@ -28,10 +26,12 @@ test('color (rgba on objects)', (t) => {
|
|
|
28
26
|
const obs = colorize([1, 1, 0.5, 0.8], obj1, obj2)
|
|
29
27
|
const exp1 = { color: [1, 1, 0.5, 0.8] }
|
|
30
28
|
const exp2 = { id: 'a', color: [1, 1, 0.5, 0.8] }
|
|
29
|
+
const exp3 = { id: 'b', color: [1, 1, 0.5, 0.8] }
|
|
31
30
|
|
|
32
|
-
t.is(obs.length,
|
|
31
|
+
t.is(obs.length, 2)
|
|
33
32
|
t.deepEqual(obs[0], exp1)
|
|
34
|
-
t.deepEqual(obs[1], exp2)
|
|
33
|
+
t.deepEqual(obs[1][0], exp2)
|
|
34
|
+
t.deepEqual(obs[1][1], exp3)
|
|
35
35
|
})
|
|
36
36
|
|
|
37
37
|
test('color (rgba on geometry)', (t) => {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as mat4 from '../../maths/mat4/index.js'
|
|
2
2
|
|
|
3
|
+
import { reverse } from './reverse.js'
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Transform the given geometry using the given matrix.
|
|
5
7
|
* This is a lazy transform of the outlines, as this function only adjusts the transforms.
|
|
@@ -14,5 +16,11 @@ import * as mat4 from '../../maths/mat4/index.js'
|
|
|
14
16
|
*/
|
|
15
17
|
export const transform = (matrix, geometry) => {
|
|
16
18
|
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
17
|
-
|
|
19
|
+
const transformed = Object.assign({}, geometry, { transforms })
|
|
20
|
+
// determine if the transform is mirroring in 2D
|
|
21
|
+
if (matrix[0] * matrix[5] - matrix[4] * matrix[1] < 0) {
|
|
22
|
+
// reverse the order to preserve the orientation
|
|
23
|
+
return reverse(transformed)
|
|
24
|
+
}
|
|
25
|
+
return transformed
|
|
18
26
|
}
|
|
@@ -2,6 +2,12 @@ import test from 'ava'
|
|
|
2
2
|
|
|
3
3
|
import { mat4 } from '../../maths/index.js'
|
|
4
4
|
|
|
5
|
+
import { measureArea } from '../../measurements/index.js'
|
|
6
|
+
|
|
7
|
+
import { mirrorX, mirrorY, mirrorZ } from '../../operations/transforms/index.js'
|
|
8
|
+
|
|
9
|
+
import { square } from '../../primitives/index.js'
|
|
10
|
+
|
|
5
11
|
import { create, transform, toOutlines, toSides } from './index.js'
|
|
6
12
|
|
|
7
13
|
import { comparePoints, compareVectors } from '../../../test/helpers/index.js'
|
|
@@ -50,3 +56,54 @@ test('transform: adjusts the transforms of geom2', (t) => {
|
|
|
50
56
|
t.true(comparePoints(another.outlines[0], expected.outlines[0]))
|
|
51
57
|
t.true(compareVectors(another.transforms, expected.transforms))
|
|
52
58
|
})
|
|
59
|
+
|
|
60
|
+
test('transform: geom2 mirrorX', (t) => {
|
|
61
|
+
const geometry = square()
|
|
62
|
+
const transformed = mirrorX(geometry)
|
|
63
|
+
t.is(measureArea(geometry), 4)
|
|
64
|
+
// area will be negative unless we reversed the points
|
|
65
|
+
t.is(measureArea(transformed), 4)
|
|
66
|
+
const pts = toOutlines(transformed)[0]
|
|
67
|
+
const exp = [[1, 1], [-1, 1], [-1, -1], [1, -1]]
|
|
68
|
+
t.true(comparePoints(pts, exp))
|
|
69
|
+
t.deepEqual(toSides(transformed), [
|
|
70
|
+
[[1, 1], [-1, 1]],
|
|
71
|
+
[[-1, 1], [-1, -1]],
|
|
72
|
+
[[-1, -1], [1, -1]],
|
|
73
|
+
[[1, -1], [1, 1]]
|
|
74
|
+
])
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('transform: geom2 mirrorY', (t) => {
|
|
78
|
+
const geometry = square()
|
|
79
|
+
const transformed = mirrorY(geometry)
|
|
80
|
+
t.is(measureArea(geometry), 4)
|
|
81
|
+
// area will be negative unless we reversed the points
|
|
82
|
+
t.is(measureArea(transformed), 4)
|
|
83
|
+
const pts = toOutlines(transformed)[0]
|
|
84
|
+
const exp = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
|
|
85
|
+
t.true(comparePoints(pts, exp))
|
|
86
|
+
t.deepEqual(toSides(transformed), [
|
|
87
|
+
[[-1, -1], [1, -1]],
|
|
88
|
+
[[1, -1], [1, 1]],
|
|
89
|
+
[[1, 1], [-1, 1]],
|
|
90
|
+
[[-1, 1], [-1, -1]]
|
|
91
|
+
])
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('transform: geom2 mirrorZ', (t) => {
|
|
95
|
+
const geometry = square()
|
|
96
|
+
const transformed = mirrorZ(geometry)
|
|
97
|
+
t.is(measureArea(geometry), 4)
|
|
98
|
+
// area will be negative unless we DIDN'T reverse the points
|
|
99
|
+
t.is(measureArea(transformed), 4)
|
|
100
|
+
const pts = toOutlines(transformed)[0]
|
|
101
|
+
const exp = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
|
|
102
|
+
t.true(comparePoints(pts, exp))
|
|
103
|
+
t.deepEqual(toSides(transformed), [
|
|
104
|
+
[[-1, -1], [1, -1]],
|
|
105
|
+
[[1, -1], [1, 1]],
|
|
106
|
+
[[1, 1], [-1, 1]],
|
|
107
|
+
[[-1, 1], [-1, -1]]
|
|
108
|
+
])
|
|
109
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { runner } from '../../operations/hulls/quickhull/index.js'
|
|
2
|
+
import { create } from './create.js'
|
|
3
|
+
import * as poly3 from '../poly3/index.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Construct a new convex 3D geometry from a list of unique points.
|
|
7
|
+
*
|
|
8
|
+
* @param {Array} uniquePoints - list of points to construct convex 3D geometry
|
|
9
|
+
* @returns {geom3} a new geometry
|
|
10
|
+
* @alias module:modeling/geometries/geom3.fromPointsConvex
|
|
11
|
+
*/
|
|
12
|
+
export const fromPointsConvex = (uniquePoints) => {
|
|
13
|
+
if (!Array.isArray(uniquePoints)) {
|
|
14
|
+
throw new Error('the given points must be an array')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const faces = runner(uniquePoints, { skipTriangulation: true })
|
|
18
|
+
|
|
19
|
+
const polygons = faces.map((face) => {
|
|
20
|
+
const vertices = face.map((index) => uniquePoints[index])
|
|
21
|
+
return poly3.create(vertices)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
return create(polygons)
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import test from 'ava'
|
|
2
|
+
|
|
3
|
+
import { fromPointsConvex, validate } from './index.js'
|
|
4
|
+
|
|
5
|
+
test('fromPointsConvex (uniquePoints)', (t) => {
|
|
6
|
+
const out = []
|
|
7
|
+
for (let x = -9; x <= 9; ++x) {
|
|
8
|
+
for (let y = -9; y <= 9; ++y) {
|
|
9
|
+
for (let z = -9; z <= 9; ++z) {
|
|
10
|
+
if (x * x + y * y + z * z <= 96) {
|
|
11
|
+
out.push([x, y, z])
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const obs = fromPointsConvex(out)
|
|
18
|
+
validate(obs)
|
|
19
|
+
t.is(obs.polygons.length, 170)
|
|
20
|
+
t.true(obs.polygons.every((f) => ([3, 4, 8, 9].indexOf(f.vertices.length) !== -1)))
|
|
21
|
+
|
|
22
|
+
const c = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
23
|
+
obs.polygons.forEach((f) => c[f.vertices.length]++)
|
|
24
|
+
t.is(c[3], 120)
|
|
25
|
+
t.is(c[4], 24)
|
|
26
|
+
t.is(c[8], 18)
|
|
27
|
+
t.is(c[9], 8)
|
|
28
|
+
|
|
29
|
+
let edges2 = 336 * 2
|
|
30
|
+
obs.polygons.forEach((f) => { edges2 -= f.vertices.length })
|
|
31
|
+
t.is(edges2, 0)
|
|
32
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { clone } from './clone.js'
|
|
2
2
|
export { create } from './create.js'
|
|
3
3
|
export { fromPoints } from './fromPoints.js'
|
|
4
|
+
export { fromPointsConvex } from './fromPointsConvex.js'
|
|
4
5
|
export { fromCompactBinary } from './fromCompactBinary.js'
|
|
5
6
|
export { invert } from './invert.js'
|
|
6
7
|
export { isA } from './isA.js'
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
export { clone } from './clone.js'
|
|
18
18
|
export { create } from './create.js'
|
|
19
19
|
export { fromPoints } from './fromPoints.js'
|
|
20
|
+
export { fromPointsConvex } from './fromPointsConvex.js'
|
|
20
21
|
export { fromCompactBinary } from './fromCompactBinary.js'
|
|
21
22
|
export { invert } from './invert.js'
|
|
22
23
|
export { isA } from './isA.js'
|
package/src/geometries/index.js
CHANGED
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
* @see {@link geom2} - 2D geometry consisting of 2D outlines
|
|
6
6
|
* @see {@link geom3} - 3D geometry consisting of polygons
|
|
7
7
|
* @see {@link path2} - 2D geometry consisting of ordered points
|
|
8
|
-
* @see {@link poly2} - 2D polygon consisting of ordered
|
|
8
|
+
* @see {@link poly2} - 2D polygon consisting of ordered points
|
|
9
9
|
* @see {@link poly3} - 3D polygon consisting of ordered vertices
|
|
10
|
-
* @see {@link slice} - 3D geometry consisting of 3D
|
|
10
|
+
* @see {@link slice} - 3D geometry consisting of 3D contours
|
|
11
11
|
*
|
|
12
12
|
* @module modeling/geometries
|
|
13
13
|
* @example
|
|
14
|
-
* import {
|
|
15
|
-
* const { geom2, geom3, path2, poly2, poly3 } = geometries
|
|
14
|
+
* import { geom2, geom3, path2, poly2, poly3, slice } from '@jscad/modeling'
|
|
16
15
|
*/
|
|
17
16
|
export * as geom2 from './geom2/index.js'
|
|
18
17
|
export * as geom3 from './geom3/index.js'
|
|
@@ -28,10 +28,9 @@ export const validate = (object) => {
|
|
|
28
28
|
// contours must be coplanar
|
|
29
29
|
const contourPlane = poly3.plane(poly3.create(contour))
|
|
30
30
|
if (!plane.equals(slicePlane, contourPlane)) {
|
|
31
|
-
throw new Error(
|
|
31
|
+
throw new Error('slice contours must be coplanar')
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
34
|
for (let i = 0; i < contour.length; i++) {
|
|
36
35
|
const vertex = contour[i]
|
|
37
36
|
// check for infinity, nan
|
package/src/maths/index.js
CHANGED
|
@@ -14,7 +14,6 @@ import { measureArea } from './measureArea.js'
|
|
|
14
14
|
*/
|
|
15
15
|
export const measureAggregateArea = (...geometries) => {
|
|
16
16
|
geometries = flatten(geometries)
|
|
17
|
-
if (geometries.length === 0) throw new Error('measureAggregateArea: no geometries supplied')
|
|
18
17
|
const areas = measureArea(geometries)
|
|
19
18
|
if (geometries.length === 1) {
|
|
20
19
|
return areas
|
|
@@ -15,7 +15,6 @@ import { measureBoundingBox } from './measureBoundingBox.js'
|
|
|
15
15
|
*/
|
|
16
16
|
export const measureAggregateBoundingBox = (...geometries) => {
|
|
17
17
|
geometries = flatten(geometries)
|
|
18
|
-
if (geometries.length === 0) throw new Error('measureAggregateBoundingBox: no geometries supplied')
|
|
19
18
|
const bounds = measureBoundingBox(geometries)
|
|
20
19
|
if (geometries.length === 1) {
|
|
21
20
|
return bounds
|
|
@@ -18,7 +18,6 @@ import { calculateEpsilonFromBounds } from './calculateEpsilonFromBounds.js'
|
|
|
18
18
|
*/
|
|
19
19
|
export const measureAggregateEpsilon = (...geometries) => {
|
|
20
20
|
geometries = flatten(geometries)
|
|
21
|
-
if (geometries.length === 0) throw new Error('measureAggregateEpsilon: no geometries supplied')
|
|
22
21
|
const bounds = measureAggregateBoundingBox(geometries)
|
|
23
22
|
|
|
24
23
|
let dimensions = 0
|
|
@@ -14,7 +14,6 @@ import { measureVolume } from './measureVolume.js'
|
|
|
14
14
|
*/
|
|
15
15
|
export const measureAggregateVolume = (...geometries) => {
|
|
16
16
|
geometries = flatten(geometries)
|
|
17
|
-
if (geometries.length === 0) throw new Error('measureAggregateVolume: no geometries supplied')
|
|
18
17
|
const volumes = measureVolume(geometries)
|
|
19
18
|
if (geometries.length === 1) {
|
|
20
19
|
return volumes
|
|
@@ -85,7 +85,6 @@ const measureAreaOfSlice = (geometry) => {
|
|
|
85
85
|
*/
|
|
86
86
|
export const measureArea = (...geometries) => {
|
|
87
87
|
geometries = flatten(geometries)
|
|
88
|
-
if (geometries.length === 0) throw new Error('wrong number of arguments')
|
|
89
88
|
|
|
90
89
|
const results = geometries.map((geometry) => {
|
|
91
90
|
if (path2.isA(geometry)) return measureAreaOfPath2(geometry)
|
|
@@ -121,7 +121,6 @@ const measureBoundingBoxOfSlice = (geometry) => {
|
|
|
121
121
|
*/
|
|
122
122
|
export const measureBoundingBox = (...geometries) => {
|
|
123
123
|
geometries = flatten(geometries)
|
|
124
|
-
if (geometries.length === 0) throw new Error('wrong number of arguments')
|
|
125
124
|
|
|
126
125
|
const results = geometries.map((geometry) => {
|
|
127
126
|
if (path2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfPath2)
|
|
@@ -20,7 +20,6 @@ import { measureBoundingBox } from './measureBoundingBox.js'
|
|
|
20
20
|
*/
|
|
21
21
|
export const measureEpsilon = (...geometries) => {
|
|
22
22
|
geometries = flatten(geometries)
|
|
23
|
-
if (geometries.length === 0) throw new Error('wrong number of arguments')
|
|
24
23
|
|
|
25
24
|
const results = geometries.map((geometry) => {
|
|
26
25
|
if (path2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
|
|
@@ -34,7 +34,6 @@ const measureVolumeOfGeom3 = (geometry) => {
|
|
|
34
34
|
*/
|
|
35
35
|
export const measureVolume = (...geometries) => {
|
|
36
36
|
geometries = flatten(geometries)
|
|
37
|
-
if (geometries.length === 0) throw new Error('wrong number of arguments')
|
|
38
37
|
|
|
39
38
|
const results = geometries.map((geometry) => {
|
|
40
39
|
if (geom3.isA(geometry)) return measureVolumeOfGeom3(geometry)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { areAllShapesTheSameType } from '../../utils/areAllShapesTheSameType.js'
|
|
2
|
-
import {
|
|
2
|
+
import { coalesce } from '../../utils/coalesce.js'
|
|
3
3
|
|
|
4
4
|
import * as geom2 from '../../geometries/geom2/index.js'
|
|
5
5
|
import * as geom3 from '../../geometries/geom3/index.js'
|
|
@@ -13,11 +13,11 @@ import { intersectGeom3 } from './intersectGeom3.js'
|
|
|
13
13
|
* The given geometries should be of the same type, either geom2 or geom3.
|
|
14
14
|
*
|
|
15
15
|
* @param {...Object} geometries - list of geometries
|
|
16
|
-
* @returns {Geom2|
|
|
16
|
+
* @returns {Geom2|Geom3} a new geometry
|
|
17
17
|
* @alias module:modeling/booleans.intersect
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
|
-
* let myshape = intersect(cube({size:
|
|
20
|
+
* let myshape = intersect(cube({size: 5}), cube({size: 5, center: [3,3,3]}))
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* +-------+
|
|
@@ -30,9 +30,9 @@ import { intersectGeom3 } from './intersectGeom3.js'
|
|
|
30
30
|
* +-------+
|
|
31
31
|
*/
|
|
32
32
|
export const intersect = (...geometries) => {
|
|
33
|
-
geometries =
|
|
34
|
-
if (geometries.length === 0) throw new Error('intersect wrong number of arguments')
|
|
33
|
+
geometries = coalesce(geometries)
|
|
35
34
|
|
|
35
|
+
if (geometries.length === 0) return undefined
|
|
36
36
|
if (!areAllShapesTheSameType(geometries)) {
|
|
37
37
|
throw new Error('intersect arguments must be the same geometry type')
|
|
38
38
|
}
|
|
@@ -4,11 +4,11 @@ import { geom2, geom3 } from '../../geometries/index.js'
|
|
|
4
4
|
|
|
5
5
|
import { intersect } from './index.js'
|
|
6
6
|
|
|
7
|
-
test('intersect
|
|
8
|
-
|
|
9
|
-
t.
|
|
10
|
-
t.
|
|
11
|
-
t.
|
|
7
|
+
test('intersect empty arguments', (t) => {
|
|
8
|
+
t.is(intersect(), undefined)
|
|
9
|
+
t.is(intersect([]), undefined)
|
|
10
|
+
t.is(intersect([[], []]), undefined)
|
|
11
|
+
t.is(intersect(null, null), undefined)
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
test('intersect error different geometry types', (t) => {
|
|
@@ -20,6 +20,5 @@ test('intersect error non-geometries', (t) => {
|
|
|
20
20
|
const message = 'intersect unsupported geometry type'
|
|
21
21
|
t.throws(() => intersect([1, 2, 3], [4, 5, 6]), { message })
|
|
22
22
|
t.throws(() => intersect([], [123]), { message })
|
|
23
|
-
t.throws(() => intersect(
|
|
24
|
-
t.throws(() => intersect(null, null), { message })
|
|
23
|
+
t.throws(() => intersect('one', 'two'), { message })
|
|
25
24
|
})
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import { INTERSECTION } from './martinez/operation.js'
|
|
4
2
|
import { boolean } from './martinez/index.js'
|
|
5
3
|
|
|
6
4
|
/*
|
|
7
5
|
* Return a new 2D geometry representing space in both the first geometry and
|
|
8
6
|
* in the subsequent geometries. None of the given geometries are modified.
|
|
9
|
-
* @param {
|
|
7
|
+
* @param {Geom2[]} geometries - a flat list of 2D geometries
|
|
10
8
|
* @returns {Geom2} new 2D geometry
|
|
11
9
|
*/
|
|
12
|
-
export const intersectGeom2 = (
|
|
13
|
-
geometries = flatten(geometries)
|
|
14
|
-
|
|
10
|
+
export const intersectGeom2 = (geometries) => {
|
|
15
11
|
let newGeometry = geometries.shift()
|
|
16
12
|
geometries.forEach((geometry) => {
|
|
17
13
|
newGeometry = boolean(newGeometry, geometry, INTERSECTION)
|
|
@@ -6,7 +6,7 @@ import { geom2 } from '../../geometries/index.js'
|
|
|
6
6
|
|
|
7
7
|
import { measureArea } from '../../measurements/index.js'
|
|
8
8
|
|
|
9
|
-
import { circle, rectangle } from '../../primitives/index.js'
|
|
9
|
+
import { circle, rectangle, square } from '../../primitives/index.js'
|
|
10
10
|
|
|
11
11
|
import { intersect } from './index.js'
|
|
12
12
|
|
|
@@ -73,3 +73,27 @@ test('intersect: intersect of one or more geom2 objects produces expected geomet
|
|
|
73
73
|
t.is(obs.length, 8)
|
|
74
74
|
t.true(comparePoints(obs, exp))
|
|
75
75
|
})
|
|
76
|
+
|
|
77
|
+
test('intersect with undefined/null values', (t) => {
|
|
78
|
+
const square1 = square({ size: 8 })
|
|
79
|
+
const square2 = square({ size: 6 })
|
|
80
|
+
const square3 = square({ size: 4 })
|
|
81
|
+
const geometries = [square1, undefined, square2, null, square3]
|
|
82
|
+
|
|
83
|
+
const obs = intersect(...geometries)
|
|
84
|
+
const pts = geom2.toPoints(obs)
|
|
85
|
+
t.notThrows(() => geom2.validate(obs))
|
|
86
|
+
t.is(pts.length, 4)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('intersect of nested arrays', (t) => {
|
|
90
|
+
const square1 = square({ size: 8 })
|
|
91
|
+
const square2 = square({ size: 6 })
|
|
92
|
+
const square3 = square({ size: 4 })
|
|
93
|
+
const geometries = [square1, [square2, [square3]]]
|
|
94
|
+
|
|
95
|
+
const obs = intersect(...geometries)
|
|
96
|
+
const pts = geom2.toPoints(obs)
|
|
97
|
+
t.notThrows(() => geom2.validate(obs))
|
|
98
|
+
t.is(pts.length, 4)
|
|
99
|
+
})
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import { retessellate } from '../modifiers/retessellate.js'
|
|
4
2
|
|
|
5
3
|
import { intersectGeom3Sub } from './intersectGeom3Sub.js'
|
|
@@ -7,12 +5,10 @@ import { intersectGeom3Sub } from './intersectGeom3Sub.js'
|
|
|
7
5
|
/*
|
|
8
6
|
* Return a new 3D geometry representing space in both the first geometry and
|
|
9
7
|
* in the subsequent geometries. None of the given geometries are modified.
|
|
10
|
-
* @param {
|
|
8
|
+
* @param {Geom3[]} geometries - a flat list of 3D geometries
|
|
11
9
|
* @returns {Geom3} new 3D geometry
|
|
12
10
|
*/
|
|
13
|
-
export const intersectGeom3 = (
|
|
14
|
-
geometries = flatten(geometries)
|
|
15
|
-
|
|
11
|
+
export const intersectGeom3 = (geometries) => {
|
|
16
12
|
let newGeometry = geometries.shift()
|
|
17
13
|
geometries.forEach((geometry) => {
|
|
18
14
|
newGeometry = intersectGeom3Sub(newGeometry, geometry)
|
|
@@ -4,7 +4,7 @@ import { comparePolygonsAsPoints } from '../../../test/helpers/index.js'
|
|
|
4
4
|
|
|
5
5
|
import { geom3 } from '../../geometries/index.js'
|
|
6
6
|
|
|
7
|
-
import { measureVolume } from '../../measurements/index.js'
|
|
7
|
+
import { measureArea, measureVolume } from '../../measurements/index.js'
|
|
8
8
|
|
|
9
9
|
import { sphere, cuboid } from '../../primitives/index.js'
|
|
10
10
|
|
|
@@ -69,6 +69,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
|
|
|
69
69
|
[[8.65956056235493e-17, 8.659560562354935e-17, 2], [1.4142135623730951, 3.4638242249419736e-16, 1.414213562373095], [0.9999999999999998, 1.0000000000000002, 1.414213562373095]]
|
|
70
70
|
]
|
|
71
71
|
t.notThrows.skip(() => geom3.validate(result1))
|
|
72
|
+
t.is(measureArea(result1), 44.053756306589825)
|
|
72
73
|
t.is(measureVolume(result1), 25.751611331979678)
|
|
73
74
|
t.is(obs.length, 32)
|
|
74
75
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
@@ -79,6 +80,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
|
|
|
79
80
|
const result2 = intersect(geometry1, geometry2)
|
|
80
81
|
obs = geom3.toPoints(result2)
|
|
81
82
|
t.notThrows(() => geom3.validate(result2))
|
|
83
|
+
t.is(measureArea(result2), 0)
|
|
82
84
|
t.is(measureVolume(result2), 0)
|
|
83
85
|
t.is(obs.length, 0)
|
|
84
86
|
|
|
@@ -99,6 +101,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
|
|
|
99
101
|
]
|
|
100
102
|
|
|
101
103
|
t.notThrows(() => geom3.validate(result3))
|
|
104
|
+
t.is(measureArea(result3), 6)
|
|
102
105
|
t.is(measureVolume(result3), 1.0000000000000009)
|
|
103
106
|
t.is(obs.length, 6)
|
|
104
107
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
@@ -107,6 +110,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
|
|
|
107
110
|
const result4 = intersect(geometry1, geometry3)
|
|
108
111
|
obs = geom3.toPoints(result4)
|
|
109
112
|
t.notThrows.skip(() => geom3.validate(result4))
|
|
113
|
+
t.is(measureArea(result4), 44.053756306589825)
|
|
110
114
|
t.is(measureVolume(result4), 25.751611331979678)
|
|
111
115
|
t.is(obs.length, 32)
|
|
112
116
|
})
|
|
@@ -2,7 +2,6 @@ import { EPS } from '../../maths/constants.js'
|
|
|
2
2
|
|
|
3
3
|
import { measureBoundingBox } from '../../measurements/measureBoundingBox.js'
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
/*
|
|
7
6
|
* Determine if the given geometries overlap by comparing min and max bounds.
|
|
8
7
|
* NOTE: This is used in union for performance gains.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Geom3 } from '../../geometries/geom3/type.d.ts'
|
|
2
|
+
import type { RecursiveArray } from '../../utils/recursiveArray.d.ts'
|
|
3
|
+
|
|
4
|
+
export function scission(...geometries: RecursiveArray<Geom3>): Geom3
|
|
5
|
+
export function scission(...geometries: RecursiveArray<Geom3>): Array<Geom3>
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import * as geom3 from '../../geometries/geom3/index.js'
|
|
4
2
|
|
|
5
3
|
import { scissionGeom3 } from './scissionGeom3.js'
|
|
@@ -7,6 +5,8 @@ import { scissionGeom3 } from './scissionGeom3.js'
|
|
|
7
5
|
/**
|
|
8
6
|
* Scission (divide) the given geometry into the component pieces.
|
|
9
7
|
*
|
|
8
|
+
* NOTE: Currently only 3D geometries are supported.
|
|
9
|
+
*
|
|
10
10
|
* @param {...Object} objects - list of geometries
|
|
11
11
|
* @returns {Array} list of pieces from each geometry
|
|
12
12
|
* @alias module:modeling/booleans.scission
|
|
@@ -26,13 +26,11 @@ import { scissionGeom3 } from './scissionGeom3.js'
|
|
|
26
26
|
* +-------+ +-------+
|
|
27
27
|
*/
|
|
28
28
|
export const scission = (...objects) => {
|
|
29
|
-
objects = flatten(objects)
|
|
30
|
-
if (objects.length === 0) throw new Error('wrong number of arguments')
|
|
31
|
-
|
|
32
29
|
const results = objects.map((object) => {
|
|
33
30
|
// if (path2.isA(object)) return path2.transform(matrix, object)
|
|
34
31
|
// if (geom2.isA(object)) return geom2.transform(matrix, object)
|
|
35
32
|
if (geom3.isA(object)) return scissionGeom3(object)
|
|
33
|
+
if (Array.isArray(object)) return scission(...object)
|
|
36
34
|
return object
|
|
37
35
|
})
|
|
38
36
|
return results.length === 1 ? results[0] : results
|
|
@@ -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 { cube, torus } from '../../primitives/index.js'
|
|
6
8
|
|
|
7
9
|
import { scission, union } from './index.js'
|
|
@@ -42,6 +44,10 @@ test('scission: scission of complex geom3 produces expected geometry', (t) => {
|
|
|
42
44
|
t.is(result1.length, 2)
|
|
43
45
|
t.notThrows.skip(() => geom3.validate(result1[0]))
|
|
44
46
|
t.notThrows.skip(() => geom3.validate(result1[1]))
|
|
47
|
+
t.is(measureArea(result1[0]), 7720.0306508548)
|
|
48
|
+
t.is(measureArea(result1[1]), 3860.0153254273987)
|
|
49
|
+
t.is(measureVolume(result1[0]), 18745.166004060953)
|
|
50
|
+
t.is(measureVolume(result1[1]), 9372.583002030477)
|
|
45
51
|
|
|
46
52
|
const rc1 = geom3.toPolygons(result1[0]).length
|
|
47
53
|
const rc2 = geom3.toPolygons(result1[1]).length
|