@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
|
@@ -9,9 +9,9 @@ export class SweepEvent {
|
|
|
9
9
|
* @param {boolean} left
|
|
10
10
|
* @param {SweepEvent=} otherEvent
|
|
11
11
|
* @param {boolean} isSubject
|
|
12
|
-
* @param {number}
|
|
12
|
+
* @param {number} type OPTIONAL
|
|
13
13
|
*/
|
|
14
|
-
constructor (point, left, otherEvent, isSubject,
|
|
14
|
+
constructor (point, left, otherEvent, isSubject, type = NORMAL) {
|
|
15
15
|
/**
|
|
16
16
|
* Is left endpoint?
|
|
17
17
|
* @type {Boolean}
|
|
@@ -39,7 +39,7 @@ export class SweepEvent {
|
|
|
39
39
|
* Edge contribution type
|
|
40
40
|
* @type {Number}
|
|
41
41
|
*/
|
|
42
|
-
this.type =
|
|
42
|
+
this.type = type
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* In-out transition for the sweepline crossing polygon
|
|
@@ -115,18 +115,4 @@ export class SweepEvent {
|
|
|
115
115
|
get inResult () {
|
|
116
116
|
return this.resultTransition !== 0
|
|
117
117
|
}
|
|
118
|
-
|
|
119
|
-
clone () {
|
|
120
|
-
const copy = new SweepEvent(
|
|
121
|
-
this.point, this.left, this.otherEvent, this.isSubject, this.type)
|
|
122
|
-
|
|
123
|
-
copy.contourId = this.contourId
|
|
124
|
-
copy.resultTransition = this.resultTransition
|
|
125
|
-
copy.prevInResult = this.prevInResult
|
|
126
|
-
copy.isExteriorRing = this.isExteriorRing
|
|
127
|
-
copy.inOut = this.inOut
|
|
128
|
-
copy.otherInOut = this.otherInOut
|
|
129
|
-
|
|
130
|
-
return copy
|
|
131
|
-
}
|
|
132
118
|
}
|
|
@@ -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
|
|
@@ -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 { subtractGeom3 } from './subtractGeom3.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.subtract
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
|
-
* let myshape = subtract(cuboid({size:
|
|
20
|
+
* let myshape = subtract(cuboid({size: 5}), cuboid({size: 5, center: [3,3,3]}))
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* +-------+ +-------+
|
|
@@ -30,9 +30,9 @@ import { subtractGeom3 } from './subtractGeom3.js'
|
|
|
30
30
|
* +-------+
|
|
31
31
|
*/
|
|
32
32
|
export const subtract = (...geometries) => {
|
|
33
|
-
geometries =
|
|
34
|
-
if (geometries.length === 0) throw new Error('subtract 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('subtract 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 { subtract } from './index.js'
|
|
6
6
|
|
|
7
|
-
test('subtract
|
|
8
|
-
|
|
9
|
-
t.
|
|
10
|
-
t.
|
|
11
|
-
t.
|
|
7
|
+
test('subtract empty arguments', (t) => {
|
|
8
|
+
t.is(subtract(), undefined)
|
|
9
|
+
t.is(subtract([]), undefined)
|
|
10
|
+
t.is(subtract([[], []]), undefined)
|
|
11
|
+
t.is(subtract(null, null), undefined)
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
test('subtract error different geometry types', (t) => {
|
|
@@ -20,6 +20,5 @@ test('subtract error non-geometries', (t) => {
|
|
|
20
20
|
const message = 'subtract unsupported geometry type'
|
|
21
21
|
t.throws(() => subtract([1, 2, 3], [4, 5, 6]), { message })
|
|
22
22
|
t.throws(() => subtract([], [123]), { message })
|
|
23
|
-
t.throws(() => subtract(
|
|
24
|
-
t.throws(() => subtract(null, null), { message })
|
|
23
|
+
t.throws(() => subtract('one', 'two'), { message })
|
|
25
24
|
})
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import { flatten } from '../../utils/flatten.js'
|
|
2
|
-
|
|
3
1
|
import { DIFFERENCE } 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 the first geometry but
|
|
8
6
|
* not 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 subtractGeom2 = (
|
|
13
|
-
geometries = flatten(geometries)
|
|
14
|
-
|
|
10
|
+
export const subtractGeom2 = (geometries) => {
|
|
15
11
|
let newGeometry = geometries.shift()
|
|
16
12
|
geometries.forEach((geometry) => {
|
|
17
13
|
newGeometry = boolean(newGeometry, geometry, DIFFERENCE)
|
|
@@ -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 { center } from '../transforms/index.js'
|
|
12
12
|
|
|
@@ -75,3 +75,27 @@ test('subtract: subtract of one or more geom2 objects produces expected geometry
|
|
|
75
75
|
t.is(obs.length, 0)
|
|
76
76
|
t.deepEqual(obs, exp)
|
|
77
77
|
})
|
|
78
|
+
|
|
79
|
+
test('subtract with undefined/null values', (t) => {
|
|
80
|
+
const square1 = square({ size: 8 })
|
|
81
|
+
const square2 = square({ size: 6 })
|
|
82
|
+
const square3 = square({ size: 4 })
|
|
83
|
+
const geometries = [square1, undefined, square2, null, square3]
|
|
84
|
+
|
|
85
|
+
const obs = subtract(...geometries)
|
|
86
|
+
const pts = geom2.toPoints(obs)
|
|
87
|
+
t.notThrows(() => geom2.validate(obs))
|
|
88
|
+
t.is(pts.length, 8)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('subtract of nested arrays', (t) => {
|
|
92
|
+
const square1 = square({ size: 8 })
|
|
93
|
+
const square2 = square({ size: 6 })
|
|
94
|
+
const square3 = square({ size: 4 })
|
|
95
|
+
const geometries = [square1, [square2, [square3]]]
|
|
96
|
+
|
|
97
|
+
const obs = subtract(...geometries)
|
|
98
|
+
const pts = geom2.toPoints(obs)
|
|
99
|
+
t.notThrows(() => geom2.validate(obs))
|
|
100
|
+
t.is(pts.length, 8)
|
|
101
|
+
})
|
|
@@ -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 { subtractGeom3Sub } from './subtractGeom3Sub.js'
|
|
@@ -7,12 +5,10 @@ import { subtractGeom3Sub } from './subtractGeom3Sub.js'
|
|
|
7
5
|
/*
|
|
8
6
|
* Return a new 3D geometry representing space in this geometry but not in the given geometries.
|
|
9
7
|
* Neither this geometry nor 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 subtractGeom3 = (
|
|
14
|
-
geometries = flatten(geometries)
|
|
15
|
-
|
|
11
|
+
export const subtractGeom3 = (geometries) => {
|
|
16
12
|
let newGeometry = geometries.shift()
|
|
17
13
|
geometries.forEach((geometry) => {
|
|
18
14
|
newGeometry = subtractGeom3Sub(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('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
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('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
79
80
|
const result2 = subtract(geometry1, geometry2)
|
|
80
81
|
obs = geom3.toPoints(result2)
|
|
81
82
|
t.notThrows.skip(() => geom3.validate(result2))
|
|
83
|
+
t.is(measureArea(result2), 44.053756306589825)
|
|
82
84
|
t.is(measureVolume(result2), 25.751611331979678)
|
|
83
85
|
t.is(obs.length, 32)
|
|
84
86
|
|
|
@@ -102,6 +104,7 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
102
104
|
[[12, 9, 8], [12, 8, 8], [9, 8, 8], [9, 9, 8]]
|
|
103
105
|
]
|
|
104
106
|
t.notThrows.skip(() => geom3.validate(result3))
|
|
107
|
+
t.is(measureArea(result3), 96)
|
|
105
108
|
t.is(measureVolume(result3), 63)
|
|
106
109
|
t.is(obs.length, 12)
|
|
107
110
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
@@ -110,6 +113,7 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
110
113
|
const result4 = subtract(geometry1, geometry3)
|
|
111
114
|
obs = geom3.toPoints(result4)
|
|
112
115
|
t.notThrows(() => geom3.validate(result4))
|
|
116
|
+
t.is(measureArea(result4), 0)
|
|
113
117
|
t.is(measureVolume(result4), 0)
|
|
114
118
|
t.is(obs.length, 0)
|
|
115
119
|
})
|
|
@@ -5,7 +5,7 @@ import * as poly3 from '../../../geometries/poly3/index.js'
|
|
|
5
5
|
// # class Node
|
|
6
6
|
// Holds a node in a BSP tree.
|
|
7
7
|
// A BSP tree is built from a collection of polygons by picking a polygon to split along.
|
|
8
|
-
// Polygons are not stored directly in the tree, but in PolygonTreeNodes, stored in this.polygontreenodes.
|
|
8
|
+
// Polygons are not stored directly in the BSP tree, but in PolygonTreeNodes, stored in this.polygontreenodes.
|
|
9
9
|
// Those PolygonTreeNodes are children of the owning Tree.polygonTree.
|
|
10
10
|
// This is not a leafy BSP tree since there is no distinction between internal and leaf nodes.
|
|
11
11
|
export class Node {
|
|
@@ -23,17 +23,16 @@ export class Node {
|
|
|
23
23
|
let node
|
|
24
24
|
for (let i = 0; i < queue.length; i++) {
|
|
25
25
|
node = queue[i]
|
|
26
|
-
if (node.plane) node.plane = plane.flip(plane.create(), node.plane)
|
|
27
|
-
if (node.front) queue.push(node.front)
|
|
28
|
-
if (node.back) queue.push(node.back)
|
|
26
|
+
if (node.plane !== null) node.plane = plane.flip(plane.create(), node.plane)
|
|
27
|
+
if (node.front !== null) queue.push(node.front)
|
|
28
|
+
if (node.back !== null) queue.push(node.back)
|
|
29
29
|
const temp = node.front
|
|
30
30
|
node.front = node.back
|
|
31
31
|
node.back = temp
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
//
|
|
36
|
-
// calls remove() for all clipped PolygonTreeNodes
|
|
35
|
+
// Clip the given tree nodes to our plane
|
|
37
36
|
clipPolygons (polygonTreeNodes, alsoRemoveCoplanarFront) {
|
|
38
37
|
let current = { node: this, polygonTreeNodes }
|
|
39
38
|
let node
|
|
@@ -43,26 +42,27 @@ export class Node {
|
|
|
43
42
|
node = current.node
|
|
44
43
|
polygonTreeNodes = current.polygonTreeNodes
|
|
45
44
|
|
|
46
|
-
if (node.plane) {
|
|
45
|
+
if (node.plane !== null) {
|
|
47
46
|
const plane = node.plane
|
|
48
47
|
|
|
49
48
|
const backNodes = []
|
|
50
49
|
const frontNodes = []
|
|
51
50
|
const coplanarFrontNodes = alsoRemoveCoplanarFront ? backNodes : frontNodes
|
|
52
|
-
polygonTreeNodes.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
//
|
|
51
|
+
for (let i = 0; i < polygonTreeNodes.length; i++) {
|
|
52
|
+
const treeNode = polygonTreeNodes[i]
|
|
53
|
+
if (treeNode.canSplit()) {
|
|
54
|
+
// split this tree node using the plane
|
|
55
|
+
// NOTE: children are added to the tree node if there are spanning polygons
|
|
56
56
|
treeNode.splitByPlane(plane, coplanarFrontNodes, backNodes, frontNodes, backNodes)
|
|
57
57
|
}
|
|
58
|
-
}
|
|
58
|
+
}
|
|
59
59
|
|
|
60
|
-
if (node.front &&
|
|
60
|
+
if (node.front !== null && frontNodes.length > 0) {
|
|
61
61
|
// add front node for further splitting
|
|
62
62
|
stack.push({ node: node.front, polygonTreeNodes: frontNodes })
|
|
63
63
|
}
|
|
64
64
|
const numBackNodes = backNodes.length
|
|
65
|
-
if (node.back &&
|
|
65
|
+
if (node.back !== null && numBackNodes > 0) {
|
|
66
66
|
// add back node for further splitting
|
|
67
67
|
stack.push({ node: node.back, polygonTreeNodes: backNodes })
|
|
68
68
|
} else {
|
|
@@ -76,17 +76,16 @@ export class Node {
|
|
|
76
76
|
} while (current !== undefined)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// Remove all polygons in this BSP tree that are inside the
|
|
80
|
-
|
|
81
|
-
clipTo (tree, alsoRemoveCoplanarFront) {
|
|
79
|
+
// Remove all polygons in this BSP tree that are inside the given BSP tree
|
|
80
|
+
clipTo (bsptree, alsoRemoveCoplanarFront) {
|
|
82
81
|
let node = this
|
|
83
82
|
const stack = []
|
|
84
83
|
do {
|
|
85
84
|
if (node.polygontreenodes.length > 0) {
|
|
86
|
-
|
|
85
|
+
bsptree.clipPolygons(node.polygontreenodes, alsoRemoveCoplanarFront)
|
|
87
86
|
}
|
|
88
|
-
if (node.front) stack.push(node.front)
|
|
89
|
-
if (node.back) stack.push(node.back)
|
|
87
|
+
if (node.front !== null) stack.push(node.front)
|
|
88
|
+
if (node.back !== null) stack.push(node.back)
|
|
90
89
|
node = stack.pop()
|
|
91
90
|
} while (node !== undefined)
|
|
92
91
|
}
|
|
@@ -103,7 +102,7 @@ export class Node {
|
|
|
103
102
|
current = stack.pop()
|
|
104
103
|
continue
|
|
105
104
|
}
|
|
106
|
-
if (
|
|
105
|
+
if (node.plane === null) {
|
|
107
106
|
let index = 0 // default
|
|
108
107
|
index = Math.floor(len / 2)
|
|
109
108
|
// index = len >> 1
|
|
@@ -118,21 +117,20 @@ export class Node {
|
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
if (frontNodes.length > 0) {
|
|
121
|
-
if (
|
|
120
|
+
if (node.front === null) node.front = new Node(node)
|
|
122
121
|
|
|
123
122
|
// unable to split by any of the current nodes
|
|
124
123
|
const stopCondition = len === frontNodes.length && backNodes.length === 0
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
|
|
125
|
+
stopCondition ? node.front.polygontreenodes = frontNodes : stack.push({ node: node.front, polygonTreeNodes: frontNodes })
|
|
127
126
|
}
|
|
128
127
|
if (backNodes.length > 0) {
|
|
129
|
-
if (
|
|
128
|
+
if (node.back === null) node.back = new Node(node)
|
|
130
129
|
|
|
131
130
|
// unable to split by any of the current nodes
|
|
132
131
|
const stopCondition = len === backNodes.length && frontNodes.length === 0
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
else stack.push({ node: node.back, polygonTreeNodes: backNodes })
|
|
133
|
+
stopCondition ? node.back.polygontreenodes = backNodes : stack.push({ node: node.back, polygonTreeNodes: backNodes })
|
|
136
134
|
}
|
|
137
135
|
|
|
138
136
|
current = stack.pop()
|