@jscad/modeling 3.0.1-alpha.0 → 3.0.3-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 +22 -0
- package/dist/jscad-modeling.es.js +2 -2
- package/dist/jscad-modeling.min.js +2 -2
- package/package.json +2 -2
- package/src/colors/colorize.test.js +1 -1
- package/src/geometries/geom2/index.d.ts +0 -2
- package/src/geometries/geom2/index.js +0 -2
- package/src/geometries/geom3/applyTransforms.test.js +2 -2
- package/src/geometries/geom3/clone.test.js +2 -2
- package/src/geometries/geom3/create.js +1 -9
- package/src/geometries/geom3/{fromPoints.d.ts → fromVertices.d.ts} +1 -1
- package/src/geometries/geom3/{fromPoints.js → fromVertices.js} +3 -2
- package/src/geometries/geom3/{fromPoints.test.js → fromVertices.test.js} +6 -6
- package/src/geometries/geom3/{fromPointsConvex.d.ts → fromVerticesConvex.d.ts} +1 -1
- package/src/geometries/geom3/fromVerticesConvex.js +25 -0
- package/src/geometries/geom3/{fromPointsConvex.test.js → fromVerticesConvex.test.js} +3 -3
- package/src/geometries/geom3/index.d.ts +3 -5
- package/src/geometries/geom3/index.js +4 -6
- package/src/geometries/geom3/invert.test.js +2 -2
- package/src/geometries/geom3/isA.test.js +2 -2
- package/src/geometries/geom3/toString.test.js +2 -2
- package/src/geometries/geom3/{toPoints.d.ts → toVertices.d.ts} +1 -1
- package/src/geometries/geom3/{toPoints.js → toVertices.js} +3 -2
- package/src/geometries/geom3/{toPoints.test.js → toVertices.test.js} +4 -4
- package/src/geometries/geom3/transform.test.js +2 -2
- package/src/geometries/geom3/validate.test.js +4 -4
- package/src/geometries/index.d.ts +1 -0
- package/src/geometries/index.js +1 -0
- package/src/geometries/path2/appendBezier.js +1 -1
- package/src/geometries/path2/create.js +1 -10
- package/src/geometries/path2/index.d.ts +0 -2
- package/src/geometries/path2/index.js +0 -2
- package/src/geometries/path3/applyTransforms.js +22 -0
- package/src/geometries/path3/applyTransforms.test.js +28 -0
- package/src/geometries/path3/close.d.ts +3 -0
- package/src/geometries/path3/close.js +31 -0
- package/src/geometries/path3/close.test.js +43 -0
- package/src/geometries/path3/concat.d.ts +3 -0
- package/src/geometries/path3/concat.js +36 -0
- package/src/geometries/path3/concat.test.js +35 -0
- package/src/geometries/path3/create.d.ts +4 -0
- package/src/geometries/path3/create.js +30 -0
- package/src/geometries/path3/create.test.js +8 -0
- package/src/geometries/path3/equals.d.ts +3 -0
- package/src/geometries/path3/equals.js +48 -0
- package/src/geometries/path3/equals.test.js +38 -0
- package/src/geometries/path3/fromVertices.d.ts +8 -0
- package/src/geometries/path3/fromVertices.js +45 -0
- package/src/geometries/path3/fromVertices.test.js +33 -0
- package/src/geometries/path3/index.d.ts +13 -0
- package/src/geometries/path3/index.js +21 -0
- package/src/geometries/path3/isA.d.ts +3 -0
- package/src/geometries/path3/isA.js +20 -0
- package/src/geometries/path3/isA.test.js +19 -0
- package/src/geometries/path3/reverse.d.ts +3 -0
- package/src/geometries/path3/reverse.js +19 -0
- package/src/geometries/path3/reverse.test.js +9 -0
- package/src/geometries/path3/toString.d.ts +3 -0
- package/src/geometries/path3/toString.js +24 -0
- package/src/geometries/path3/toVertices.d.ts +4 -0
- package/src/geometries/path3/toVertices.js +16 -0
- package/src/geometries/path3/toVertices.test.js +13 -0
- package/src/geometries/path3/transform.d.ts +4 -0
- package/src/geometries/path3/transform.js +21 -0
- package/src/geometries/path3/transform.test.js +50 -0
- package/src/geometries/path3/type.d.ts +10 -0
- package/src/geometries/path3/validate.d.ts +1 -0
- package/src/geometries/path3/validate.js +41 -0
- package/src/geometries/poly2/create.js +1 -6
- package/src/geometries/poly3/create.js +1 -6
- 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/index.js +41 -0
- package/src/measurements/measureBoundingSphere.js +2 -6
- package/src/operations/booleans/intersectGeom3.test.js +4 -4
- 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/subtractGeom3.test.js +4 -4
- 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.js +39 -34
- package/src/operations/booleans/unionGeom3.test.js +5 -5
- package/src/operations/extrusions/extrudeFromSlices.test.js +6 -6
- package/src/operations/extrusions/extrudeLinear.test.js +8 -8
- package/src/operations/extrusions/extrudeRotate.test.js +12 -12
- package/src/operations/extrusions/extrudeWalls.js +3 -1
- package/src/operations/hulls/hull.test.js +5 -5
- package/src/operations/hulls/hullChain.test.js +5 -5
- package/src/operations/hulls/hullPoints2.js +20 -28
- package/src/operations/hulls/toUniquePoints.js +2 -2
- package/src/operations/modifiers/generalize.test.js +6 -6
- package/src/operations/modifiers/insertTjunctions.test.js +2 -2
- package/src/operations/modifiers/mergePolygons.js +2 -3
- package/src/operations/modifiers/reTesselateCoplanarPolygons.js +7 -7
- package/src/operations/modifiers/retessellate.test.js +10 -10
- package/src/operations/modifiers/snap.test.js +4 -4
- package/src/operations/offsets/offsetGeom3.test.js +4 -4
- package/src/operations/transforms/center.test.js +7 -7
- package/src/operations/transforms/mirror.test.js +7 -7
- package/src/operations/transforms/rotate.test.js +7 -7
- package/src/operations/transforms/scale.test.js +7 -7
- package/src/operations/transforms/transform.test.js +2 -2
- package/src/operations/transforms/translate.test.js +7 -7
- package/src/primitives/cube.test.js +4 -4
- package/src/primitives/cuboid.test.js +4 -4
- package/src/primitives/cylinder.test.js +5 -5
- package/src/primitives/cylinderElliptic.test.js +9 -9
- package/src/primitives/ellipsoid.test.js +5 -5
- package/src/primitives/geodesicSphere.test.js +4 -4
- package/src/primitives/polyhedron.test.js +2 -2
- package/src/primitives/roundedCuboid.test.js +7 -7
- package/src/primitives/roundedCylinder.test.js +9 -9
- package/src/primitives/sphere.test.js +5 -5
- package/src/primitives/torus.test.js +4 -4
- package/src/geometries/geom2/fromCompactBinary.d.ts +0 -3
- package/src/geometries/geom2/fromCompactBinary.js +0 -40
- package/src/geometries/geom2/fromToCompactBinary.test.js +0 -100
- package/src/geometries/geom2/toCompactBinary.d.ts +0 -3
- package/src/geometries/geom2/toCompactBinary.js +0 -56
- package/src/geometries/geom3/fromCompactBinary.d.ts +0 -3
- package/src/geometries/geom3/fromCompactBinary.js +0 -42
- package/src/geometries/geom3/fromPointsConvex.js +0 -25
- package/src/geometries/geom3/fromToCompactBinary.test.js +0 -139
- package/src/geometries/geom3/toCompactBinary.d.ts +0 -3
- package/src/geometries/geom3/toCompactBinary.js +0 -66
- package/src/geometries/path2/fromCompactBinary.d.ts +0 -3
- package/src/geometries/path2/fromCompactBinary.js +0 -31
- package/src/geometries/path2/fromToCompactBinary.test.js +0 -114
- package/src/geometries/path2/toCompactBinary.d.ts +0 -3
- package/src/geometries/path2/toCompactBinary.js +0 -50
|
@@ -17,7 +17,7 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
17
17
|
|
|
18
18
|
// subtract of one object
|
|
19
19
|
const result1 = subtract(geometry1)
|
|
20
|
-
let obs = geom3.
|
|
20
|
+
let obs = geom3.toVertices(result1)
|
|
21
21
|
let exp = [
|
|
22
22
|
[[2, 0, 0], [1.4142135623730951, -1.414213562373095, 0],
|
|
23
23
|
[1.0000000000000002, -1, -1.414213562373095], [1.4142135623730951, 0, -1.414213562373095]],
|
|
@@ -78,7 +78,7 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
78
78
|
const geometry2 = center({ relativeTo: [10, 10, 10] }, cuboid({ size: [4, 4, 4] }))
|
|
79
79
|
|
|
80
80
|
const result2 = subtract(geometry1, geometry2)
|
|
81
|
-
obs = geom3.
|
|
81
|
+
obs = geom3.toVertices(result2)
|
|
82
82
|
t.notThrows.skip(() => geom3.validate(result2))
|
|
83
83
|
t.is(measureArea(result2), 44.053756306589825)
|
|
84
84
|
t.is(measureVolume(result2), 25.751611331979678)
|
|
@@ -88,7 +88,7 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
88
88
|
const geometry3 = cuboid({ size: [18, 18, 18] })
|
|
89
89
|
|
|
90
90
|
const result3 = subtract(geometry2, geometry3)
|
|
91
|
-
obs = geom3.
|
|
91
|
+
obs = geom3.toVertices(result3)
|
|
92
92
|
exp = [
|
|
93
93
|
[[12, 8, 8], [12, 12, 8], [12, 12, 12], [12, 8, 12]],
|
|
94
94
|
[[8, 12, 8], [8, 12, 12], [12, 12, 12], [12, 12, 8]],
|
|
@@ -111,7 +111,7 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
111
111
|
|
|
112
112
|
// subtract of two completely overlapping objects
|
|
113
113
|
const result4 = subtract(geometry1, geometry3)
|
|
114
|
-
obs = geom3.
|
|
114
|
+
obs = geom3.toVertices(result4)
|
|
115
115
|
t.notThrows(() => geom3.validate(result4))
|
|
116
116
|
t.is(measureArea(result4), 0)
|
|
117
117
|
t.is(measureVolume(result4), 0)
|
|
@@ -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()
|
|
@@ -6,6 +6,9 @@ import * as poly3 from '../../../geometries/poly3/index.js'
|
|
|
6
6
|
|
|
7
7
|
import { splitPolygonByPlane } from './splitPolygonByPlane.js'
|
|
8
8
|
|
|
9
|
+
// cached values to boost performance
|
|
10
|
+
const splitResult = { type: 0, front: null, back: null }
|
|
11
|
+
|
|
9
12
|
// # class PolygonTreeNode
|
|
10
13
|
// This class manages hierarchical splits of polygons.
|
|
11
14
|
// At the top is a root node which does not hold a polygon, only child PolygonTreeNodes.
|
|
@@ -13,72 +16,72 @@ import { splitPolygonByPlane } from './splitPolygonByPlane.js'
|
|
|
13
16
|
// The polygons can be in different planes.
|
|
14
17
|
// splitByPlane() splits a node by a plane. If the plane intersects the polygon,
|
|
15
18
|
// two new child nodes are created holding the split polygon.
|
|
16
|
-
// getPolygons() retrieves the polygons from the
|
|
19
|
+
// getPolygons() retrieves the polygons from the node. If for PolygonTreeNode the polygon is split but
|
|
17
20
|
// the two split parts (child nodes) are still intact, then the unsplit polygon is returned.
|
|
18
21
|
// This ensures that we can safely split a polygon into many fragments. If the fragments are untouched,
|
|
19
22
|
// getPolygons() will return the original unsplit polygon instead of the fragments.
|
|
20
|
-
// remove() removes a polygon from the
|
|
23
|
+
// remove() removes a polygon from the node. Once a polygon is removed, the parent polygons are invalidated
|
|
21
24
|
// since they are no longer intact.
|
|
22
25
|
export class PolygonTreeNode {
|
|
23
26
|
// constructor creates the root node
|
|
24
27
|
constructor (parent, polygon) {
|
|
25
28
|
this.parent = parent
|
|
26
|
-
this.children = []
|
|
27
29
|
this.polygon = polygon
|
|
28
|
-
this.
|
|
30
|
+
this.children = []
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
// fill the
|
|
33
|
+
// fill the node with polygons. Should be called on the root node only; child nodes must
|
|
32
34
|
// always be a derivate (split) of the parent node.
|
|
33
35
|
addPolygons (polygons) {
|
|
34
36
|
// new polygons can only be added to root node; children can only be split polygons
|
|
35
|
-
if (!this.isRootNode())
|
|
36
|
-
|
|
37
|
+
if (!this.isRootNode()) throw new Error('PolygonTreeNode01')
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < polygons.length; i++) {
|
|
40
|
+
this.addChild(polygons[i])
|
|
37
41
|
}
|
|
38
|
-
const _this = this
|
|
39
|
-
polygons.forEach((polygon) => {
|
|
40
|
-
_this.addChild(polygon)
|
|
41
|
-
})
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// remove a node
|
|
45
45
|
// - the siblings become toplevel nodes
|
|
46
46
|
// - the parent is removed recursively
|
|
47
47
|
remove () {
|
|
48
|
-
|
|
49
|
-
this.removed = true
|
|
50
|
-
this.polygon = null
|
|
48
|
+
this.polygon = null
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
// remove ourselves from the parent's children list:
|
|
51
|
+
const parentschildren = this.parent.children
|
|
52
|
+
const i = parentschildren.indexOf(this)
|
|
53
|
+
if (i < 0) throw new Error('PolyTreeNode02')
|
|
54
|
+
parentschildren.splice(i, 1)
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
56
|
+
// invalidate the parent's polygon, and of all parents above it:
|
|
57
|
+
this.parent._recursivelyInvalidatePolygon()
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
/*
|
|
61
|
+
* Can the node be split, either base polygon or children
|
|
62
|
+
*/
|
|
63
|
+
canSplit () {
|
|
64
|
+
return this.polygon != null || this.children.length > 0
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
isRootNode () {
|
|
68
68
|
return !this.parent
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
// invert all polygons in the
|
|
71
|
+
// invert all polygons in the node. Call on the root node
|
|
72
72
|
invert () {
|
|
73
|
-
if (!this.isRootNode()) throw new Error('
|
|
74
|
-
this.
|
|
73
|
+
if (!this.isRootNode()) throw new Error('PolyTreeNode03')
|
|
74
|
+
this._invertSub()
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
getPolygon () {
|
|
78
|
-
if (
|
|
78
|
+
if (this.polygon === null) throw new Error('PolyTreeNode04')
|
|
79
79
|
return this.polygon
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/*
|
|
83
|
+
* Get all polygons from the node, and add to the result
|
|
84
|
+
*/
|
|
82
85
|
getPolygons (result) {
|
|
83
86
|
let children = [this]
|
|
84
87
|
const queue = [children]
|
|
@@ -87,23 +90,37 @@ export class PolygonTreeNode {
|
|
|
87
90
|
children = queue[i]
|
|
88
91
|
for (j = 0, l = children.length; j < l; j++) { // ok to cache length
|
|
89
92
|
node = children[j]
|
|
90
|
-
if (node.polygon) {
|
|
91
|
-
// the polygon hasn't been broken yet. We can ignore the children and return our polygon
|
|
93
|
+
if (node.polygon !== null) {
|
|
94
|
+
// the polygon hasn't been broken yet. We can ignore the children and return our polygon
|
|
92
95
|
result.push(node.polygon)
|
|
93
96
|
} else {
|
|
94
|
-
// our polygon has been split up and broken, so gather all subpolygons
|
|
95
|
-
if (node.children.length > 0)
|
|
97
|
+
// our polygon has been split up and broken, so gather all subpolygons
|
|
98
|
+
if (node.children.length > 0) {
|
|
99
|
+
queue.push(node.children)
|
|
100
|
+
}
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
}
|
|
100
105
|
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
// NOTE: This version of getPolygons() is much SLOWER.
|
|
107
|
+
getPolygonsNew (result) {
|
|
108
|
+
if (this.polygon !== null) {
|
|
109
|
+
// the polygon hasn't been broken yet, so return the original polygon
|
|
110
|
+
result.push(this.polygon)
|
|
111
|
+
} else {
|
|
112
|
+
// the polygon has been split, so gather all polygons from the children
|
|
113
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
114
|
+
const node = this.children[i]
|
|
115
|
+
node.getPolygons(result)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// split the node by a plane, adding the resulting nodes to the frontNodes and backNodes array
|
|
121
|
+
// Also see canSplit()
|
|
122
|
+
splitByPlaneOld (plane, coplanarfrontnodes, coplanarbacknodes, frontnodes, backnodes) {
|
|
123
|
+
if (this.children.length > 0) {
|
|
107
124
|
const queue = [this.children]
|
|
108
125
|
let i
|
|
109
126
|
let j
|
|
@@ -117,68 +134,90 @@ export class PolygonTreeNode {
|
|
|
117
134
|
if (node.children.length > 0) {
|
|
118
135
|
queue.push(node.children)
|
|
119
136
|
} else {
|
|
120
|
-
|
|
121
|
-
|
|
137
|
+
if (this.polygon !== null) {
|
|
138
|
+
// no children. Split the polygon:
|
|
139
|
+
node._splitByPlane(plane, coplanarfrontnodes, coplanarbacknodes, frontnodes, backnodes)
|
|
140
|
+
}
|
|
122
141
|
}
|
|
123
142
|
}
|
|
124
143
|
}
|
|
125
144
|
} else {
|
|
126
|
-
this.
|
|
145
|
+
if (this.polygon !== null) {
|
|
146
|
+
this._splitByPlane(plane, coplanarfrontnodes, coplanarbacknodes, frontnodes, backnodes)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
splitByPlane (plane, coplanarFrontNodes, coplanarBackNodes, frontNodes, backNodes) {
|
|
152
|
+
if (this.children.length > 0) {
|
|
153
|
+
// the polygon has been split, so split the children by the given plane
|
|
154
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
155
|
+
const node = this.children[i]
|
|
156
|
+
node.splitByPlane(plane, coplanarFrontNodes, coplanarBackNodes, frontNodes, backNodes)
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
if (this.polygon !== null) {
|
|
160
|
+
// the polygon hasn't been split, so split this node by the given plane
|
|
161
|
+
this._splitByPlane(plane, coplanarFrontNodes, coplanarBackNodes, frontNodes, backNodes)
|
|
162
|
+
}
|
|
127
163
|
}
|
|
128
164
|
}
|
|
129
165
|
|
|
166
|
+
// PRIVATE
|
|
167
|
+
// If the plane doesn't intersect the polygon, the 'this' object is added to one of the arrays
|
|
168
|
+
// If the plane does intersect the polygon, two new child nodes are created for the front and back fragments,
|
|
169
|
+
// and added to both arrays.
|
|
130
170
|
// only to be called for nodes with no children
|
|
131
171
|
_splitByPlane (splane, coplanarFrontNodes, coplanarBackNodes, frontNodes, backNodes) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const splitResult = splitPolygonByPlane(splane, polygon)
|
|
144
|
-
switch (splitResult.type) {
|
|
145
|
-
case 0:
|
|
146
|
-
// coplanar front:
|
|
147
|
-
coplanarFrontNodes.push(this)
|
|
148
|
-
break
|
|
172
|
+
// perform a quick check to see the plane is outside the bounds of the polygon
|
|
173
|
+
const bounds = poly3.measureBoundingSphereAndCache(this.polygon)
|
|
174
|
+
const sphereRadius = bounds[3] + EPS // ensure radius is LARGER then polygon
|
|
175
|
+
const d = vec3.dot(splane, bounds) - splane[3]
|
|
176
|
+
if (d > sphereRadius) {
|
|
177
|
+
frontNodes.push(this)
|
|
178
|
+
return
|
|
179
|
+
} else if (d < -sphereRadius) {
|
|
180
|
+
backNodes.push(this)
|
|
181
|
+
return
|
|
182
|
+
}
|
|
149
183
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
184
|
+
// the plane may intersect the polyogn
|
|
185
|
+
splitPolygonByPlane(splitResult, splane, this.polygon)
|
|
186
|
+
switch (splitResult.type) {
|
|
187
|
+
case 0:
|
|
188
|
+
// coplanar front:
|
|
189
|
+
coplanarFrontNodes.push(this)
|
|
190
|
+
break
|
|
154
191
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
192
|
+
case 1:
|
|
193
|
+
// coplanar back:
|
|
194
|
+
coplanarBackNodes.push(this)
|
|
195
|
+
break
|
|
159
196
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
197
|
+
case 2:
|
|
198
|
+
// front:
|
|
199
|
+
frontNodes.push(this)
|
|
200
|
+
break
|
|
164
201
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
break
|
|
202
|
+
case 3:
|
|
203
|
+
// back:
|
|
204
|
+
backNodes.push(this)
|
|
205
|
+
break
|
|
206
|
+
|
|
207
|
+
case 4:
|
|
208
|
+
// spanning:
|
|
209
|
+
if (splitResult.front !== null) {
|
|
210
|
+
const frontNode = this.addChild(splitResult.front)
|
|
211
|
+
frontNodes.push(frontNode)
|
|
176
212
|
}
|
|
177
|
-
|
|
213
|
+
if (splitResult.back !== null) {
|
|
214
|
+
const backNode = this.addChild(splitResult.back)
|
|
215
|
+
backNodes.push(backNode)
|
|
216
|
+
}
|
|
217
|
+
break
|
|
178
218
|
}
|
|
179
219
|
}
|
|
180
220
|
|
|
181
|
-
// PRIVATE methods from here:
|
|
182
221
|
// add child to a node
|
|
183
222
|
// this should be called whenever the polygon is split
|
|
184
223
|
// a child should be created for every fragment of the split polygon
|
|
@@ -189,7 +228,9 @@ export class PolygonTreeNode {
|
|
|
189
228
|
return newChild
|
|
190
229
|
}
|
|
191
230
|
|
|
192
|
-
|
|
231
|
+
// PRIVATE
|
|
232
|
+
// See invert()
|
|
233
|
+
_invertSub () {
|
|
193
234
|
let children = [this]
|
|
194
235
|
const queue = [children]
|
|
195
236
|
let i, j, l, node
|
|
@@ -197,7 +238,7 @@ export class PolygonTreeNode {
|
|
|
197
238
|
children = queue[i]
|
|
198
239
|
for (j = 0, l = children.length; j < l; j++) {
|
|
199
240
|
node = children[j]
|
|
200
|
-
if (node.polygon) {
|
|
241
|
+
if (node.polygon !== null) {
|
|
201
242
|
node.polygon = poly3.invert(node.polygon)
|
|
202
243
|
}
|
|
203
244
|
if (node.children.length > 0) queue.push(node.children)
|
|
@@ -205,34 +246,40 @@ export class PolygonTreeNode {
|
|
|
205
246
|
}
|
|
206
247
|
}
|
|
207
248
|
|
|
208
|
-
//
|
|
249
|
+
// NOTE: This verison is SLOWER
|
|
250
|
+
_invertSubNew () {
|
|
251
|
+
if (this.polygon !== null) {
|
|
252
|
+
this.polygon = poly3.invert(this.polygon)
|
|
253
|
+
}
|
|
254
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
255
|
+
const node = this.children[i]
|
|
256
|
+
node._invertSub()
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// PRIVATE
|
|
209
261
|
// remove the polygon from the node, and all parent nodes above it
|
|
210
262
|
// called to invalidate parents of removed nodes
|
|
211
|
-
|
|
263
|
+
_recursivelyInvalidatePolygon () {
|
|
212
264
|
this.polygon = null
|
|
213
|
-
if (this.parent) {
|
|
214
|
-
this.parent.
|
|
265
|
+
if (this.parent !== null) {
|
|
266
|
+
this.parent._recursivelyInvalidatePolygon()
|
|
215
267
|
}
|
|
216
268
|
}
|
|
217
269
|
|
|
218
270
|
clear () {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
if (node.parent) {
|
|
230
|
-
node.parent = null
|
|
231
|
-
}
|
|
232
|
-
if (node.children.length > 0) queue.push(node.children)
|
|
233
|
-
node.children = []
|
|
234
|
-
}
|
|
271
|
+
// clear children
|
|
272
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
273
|
+
const node = this.children[i]
|
|
274
|
+
node.clear()
|
|
275
|
+
}
|
|
276
|
+
this.children.length = 0
|
|
277
|
+
// unlink polygon
|
|
278
|
+
if (this.polygon !== null) {
|
|
279
|
+
this.polygon = null
|
|
235
280
|
}
|
|
281
|
+
// unlink parent
|
|
282
|
+
this.parent = null
|
|
236
283
|
}
|
|
237
284
|
|
|
238
285
|
toString () {
|
|
@@ -246,7 +293,7 @@ export class PolygonTreeNode {
|
|
|
246
293
|
for (j = 0, l = children.length; j < l; j++) { // ok to cache length
|
|
247
294
|
node = children[j]
|
|
248
295
|
result += `${prefix}PolygonTreeNode (${node.isRootNode()}): ${node.children.length}`
|
|
249
|
-
if (node.polygon) {
|
|
296
|
+
if (node.polygon !== null) {
|
|
250
297
|
result += `\n ${prefix}polygon: ${node.polygon.vertices}\n`
|
|
251
298
|
} else {
|
|
252
299
|
result += '\n'
|
|
@@ -7,7 +7,7 @@ import { PolygonTreeNode } from './PolygonTreeNode.js'
|
|
|
7
7
|
// The actual tree is kept in this.rootnode
|
|
8
8
|
export class Tree {
|
|
9
9
|
constructor (polygons) {
|
|
10
|
-
this.polygonTree = new PolygonTreeNode()
|
|
10
|
+
this.polygonTree = new PolygonTreeNode(null, null)
|
|
11
11
|
this.rootnode = new Node(null)
|
|
12
12
|
if (polygons) this.addPolygons(polygons)
|
|
13
13
|
}
|
|
@@ -17,10 +17,9 @@ export class Tree {
|
|
|
17
17
|
this.rootnode.invert()
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
// Remove all polygons in this
|
|
21
|
-
// `tree`.
|
|
20
|
+
// Remove all polygons in this tree that are inside the given tree
|
|
22
21
|
clipTo (tree, alsoRemoveCoplanarFront = false) {
|
|
23
|
-
this.rootnode.clipTo(tree, alsoRemoveCoplanarFront)
|
|
22
|
+
this.rootnode.clipTo(tree.rootnode, alsoRemoveCoplanarFront)
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
allPolygons () {
|
|
@@ -37,6 +36,12 @@ export class Tree {
|
|
|
37
36
|
this.rootnode.addPolygonTreeNodes(polygonTreeNodes)
|
|
38
37
|
}
|
|
39
38
|
|
|
39
|
+
// NOTE: This version is SLOWER
|
|
40
|
+
addPolygonsNew (polygons) {
|
|
41
|
+
this.polygonTree.addPolygons(polygons)
|
|
42
|
+
this.rootnode.addPolygonTreeNodes(this.polygonTree.children)
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
clear () {
|
|
41
46
|
this.polygonTree.clear()
|
|
42
47
|
}
|
|
@@ -3,9 +3,11 @@ import * as vec3 from '../../../maths/vec3/index.js'
|
|
|
3
3
|
export const splitLineSegmentByPlane = (plane, p1, p2) => {
|
|
4
4
|
const direction = vec3.subtract(vec3.create(), p2, p1)
|
|
5
5
|
let lambda = (plane[3] - vec3.dot(plane, p1)) / vec3.dot(plane, direction)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
|
|
7
|
+
Number.isNaN(lambda) ? lambda = 0
|
|
8
|
+
: lambda > 1 ? lambda = 1
|
|
9
|
+
: lambda < 0 ? lambda = 0
|
|
10
|
+
: lambda
|
|
9
11
|
|
|
10
12
|
vec3.scale(direction, direction, lambda)
|
|
11
13
|
vec3.add(direction, p1, direction)
|