@jscad/modeling 2.7.2 → 2.9.1
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 +48 -0
- package/dist/jscad-modeling.min.js +443 -398
- package/package.json +2 -2
- package/src/curves/bezier/tangentAt.test.js +1 -1
- package/src/curves/bezier/valueAt.test.js +1 -1
- package/src/geometries/geom2/index.d.ts +1 -0
- package/src/geometries/geom2/index.js +12 -1
- package/src/geometries/geom2/isA.js +2 -2
- package/src/geometries/geom2/toCompactBinary.js +4 -4
- package/src/geometries/geom2/toString.js +1 -1
- package/src/geometries/geom2/transform.test.js +1 -1
- package/src/geometries/geom2/validate.d.ts +3 -0
- package/src/geometries/geom2/validate.js +36 -0
- package/src/geometries/geom3/fromCompactBinary.js +1 -1
- package/src/geometries/geom3/index.d.ts +1 -0
- package/src/geometries/geom3/index.js +19 -1
- package/src/geometries/geom3/isA.js +2 -2
- package/src/geometries/geom3/toCompactBinary.js +4 -4
- package/src/geometries/geom3/toString.js +1 -1
- package/src/geometries/geom3/transform.test.js +1 -1
- package/src/geometries/geom3/validate.d.ts +3 -0
- package/src/geometries/geom3/validate.js +62 -0
- package/src/geometries/index.js +8 -1
- package/src/geometries/path2/eachPoint.js +3 -3
- package/src/geometries/path2/index.d.ts +1 -0
- package/src/geometries/path2/index.js +13 -1
- package/src/geometries/path2/isA.js +2 -2
- package/src/geometries/path2/reverse.js +4 -4
- package/src/geometries/path2/toCompactBinary.js +6 -6
- package/src/geometries/path2/toString.js +1 -1
- package/src/geometries/path2/transform.test.js +1 -1
- package/src/geometries/path2/validate.d.ts +3 -0
- package/src/geometries/path2/validate.js +41 -0
- package/src/geometries/poly2/arePointsInside.js +0 -35
- package/src/geometries/poly2/arePointsInside.test.js +1 -1
- package/src/geometries/poly2/index.js +6 -0
- package/src/geometries/poly3/index.d.ts +1 -0
- package/src/geometries/poly3/index.js +9 -2
- package/src/geometries/poly3/invert.js +7 -1
- package/src/geometries/poly3/isA.js +2 -2
- package/src/geometries/poly3/isConvex.js +2 -2
- package/src/geometries/poly3/measureArea.js +4 -4
- package/src/geometries/poly3/measureArea.test.js +16 -16
- package/src/geometries/poly3/measureBoundingBox.js +2 -2
- package/src/geometries/poly3/measureBoundingSphere.js +2 -2
- package/src/geometries/poly3/measureBoundingSphere.test.js +8 -8
- package/src/geometries/poly3/measureSignedVolume.js +4 -4
- package/src/geometries/poly3/toPoints.js +2 -2
- package/src/geometries/poly3/toString.js +2 -2
- package/src/geometries/poly3/transform.js +2 -2
- package/src/geometries/poly3/validate.d.ts +4 -0
- package/src/geometries/poly3/validate.js +50 -0
- package/src/maths/index.js +1 -1
- package/src/maths/line2/equals.js +2 -2
- package/src/maths/line2/fromValues.js +2 -2
- package/src/maths/line2/intersectPointOfLines.js +1 -1
- package/src/maths/line2/intersectPointOfLines.test.js +1 -1
- package/src/maths/line2/reverse.test.js +1 -1
- package/src/maths/line2/transform.test.js +1 -1
- package/src/maths/line3/equals.js +2 -2
- package/src/maths/line3/reverse.test.js +1 -1
- package/src/maths/line3/transform.test.js +1 -1
- package/src/maths/mat4/fromVectorRotation.js +1 -1
- package/src/maths/mat4/fromVectorRotation.test.js +1 -1
- package/src/maths/mat4/identity.test.js +1 -1
- package/src/maths/mat4/invert.js +18 -18
- package/src/maths/mat4/isIdentity.js +1 -1
- package/src/maths/mat4/isMirroring.js +4 -4
- package/src/maths/mat4/isMirroring.test.js +1 -1
- package/src/maths/mat4/leftMultiplyVec3.js +2 -2
- package/src/maths/mat4/toString.js +2 -2
- package/src/maths/mat4/translate.test.js +1 -1
- package/src/maths/plane/flip.test.js +1 -1
- package/src/maths/plane/fromPoints.d.ts +1 -1
- package/src/maths/plane/fromPoints.js +1 -3
- package/src/maths/plane/signedDistanceToPoint.js +1 -1
- package/src/maths/plane/transform.test.js +1 -1
- package/src/maths/utils/aboutEqualNormals.js +2 -2
- package/src/maths/vec2/abs.d.ts +1 -1
- package/src/maths/vec2/add.test.js +1 -1
- package/src/maths/vec2/angleDegrees.d.ts +1 -1
- package/src/maths/vec2/angleRadians.d.ts +1 -1
- package/src/maths/vec2/create.js +1 -1
- package/src/maths/vec2/cross.test.js +1 -1
- package/src/maths/vec2/divide.test.js +1 -1
- package/src/maths/vec2/fromAngleDegrees.js +1 -1
- package/src/maths/vec2/fromScalar.js +1 -1
- package/src/maths/vec2/length.d.ts +1 -1
- package/src/maths/vec2/length.js +1 -1
- package/src/maths/vec2/lerp.test.js +1 -1
- package/src/maths/vec2/multiply.test.js +1 -1
- package/src/maths/vec2/negate.test.js +1 -1
- package/src/maths/vec2/normal.js +1 -1
- package/src/maths/vec2/normalize.d.ts +1 -1
- package/src/maths/vec2/normalize.test.js +1 -1
- package/src/maths/vec2/rotate.test.js +1 -1
- package/src/maths/vec2/squaredLength.d.ts +1 -1
- package/src/maths/vec2/squaredLength.js +3 -3
- package/src/maths/vec2/subtract.test.js +1 -1
- package/src/maths/vec2/toString.js +1 -1
- package/src/maths/vec2/transform.test.js +1 -1
- package/src/maths/vec3/abs.d.ts +1 -1
- package/src/maths/vec3/add.test.js +1 -1
- package/src/maths/vec3/cross.test.js +1 -1
- package/src/maths/vec3/divide.test.js +1 -1
- package/src/maths/vec3/fromScalar.js +1 -1
- package/src/maths/vec3/fromVec2.d.ts +1 -1
- package/src/maths/vec3/fromVec2.js +3 -3
- package/src/maths/vec3/length.d.ts +1 -1
- package/src/maths/vec3/length.js +4 -4
- package/src/maths/vec3/lerp.test.js +1 -1
- package/src/maths/vec3/multiply.test.js +1 -1
- package/src/maths/vec3/negate.d.ts +1 -1
- package/src/maths/vec3/negate.test.js +1 -1
- package/src/maths/vec3/normalize.d.ts +1 -1
- package/src/maths/vec3/normalize.test.js +1 -1
- package/src/maths/vec3/rotateX.test.js +1 -1
- package/src/maths/vec3/rotateY.test.js +1 -1
- package/src/maths/vec3/rotateZ.test.js +1 -1
- package/src/maths/vec3/scale.test.js +1 -1
- package/src/maths/vec3/squaredLength.d.ts +1 -1
- package/src/maths/vec3/squaredLength.js +4 -4
- package/src/maths/vec3/subtract.test.js +1 -1
- package/src/maths/vec3/toString.js +1 -1
- package/src/maths/vec3/transform.test.js +1 -1
- package/src/maths/vec4/toString.js +1 -1
- package/src/maths/vec4/transform.test.js +1 -1
- package/src/measurements/measureBoundingSphere.js +4 -4
- package/src/measurements/measureCenterOfMass.js +1 -1
- package/src/measurements/measureCenterOfMass.test.js +2 -2
- package/src/operations/booleans/intersect.test.js +8 -0
- package/src/operations/booleans/mayOverlap.js +3 -3
- package/src/operations/booleans/retessellate.js +2 -2
- package/src/operations/booleans/scission.js +1 -1
- package/src/operations/booleans/scission.test.js +4 -4
- package/src/operations/booleans/subtract.js +1 -1
- package/src/operations/booleans/subtract.test.js +8 -0
- package/src/operations/booleans/trees/Node.js +10 -16
- package/src/operations/booleans/trees/PolygonTreeNode.js +13 -14
- package/src/operations/booleans/trees/Tree.js +1 -2
- package/src/operations/booleans/trees/splitPolygonByPlane.js +2 -3
- package/src/operations/booleans/union.test.js +28 -1
- package/src/operations/booleans/unionGeom3Sub.js +1 -1
- package/src/operations/expansions/expand.js +2 -2
- package/src/operations/expansions/expand.test.js +32 -55
- package/src/operations/expansions/expandShell.js +24 -18
- package/src/operations/expansions/offset.js +1 -1
- package/src/operations/expansions/offset.test.js +50 -89
- package/src/operations/expansions/offsetFromPoints.js +11 -6
- package/src/operations/extrusions/earcut/assignHoles.js +91 -0
- package/src/operations/extrusions/earcut/assignHoles.test.js +74 -0
- package/src/operations/extrusions/earcut/eliminateHoles.js +131 -0
- package/src/operations/extrusions/earcut/index.js +252 -0
- package/src/operations/extrusions/earcut/linkedList.js +58 -0
- package/src/operations/extrusions/earcut/linkedListSort.js +54 -0
- package/src/operations/extrusions/earcut/linkedPolygon.js +197 -0
- package/src/operations/extrusions/earcut/polygonHierarchy.js +64 -0
- package/src/operations/extrusions/earcut/triangle.js +16 -0
- package/src/operations/extrusions/extrudeFromSlices.js +10 -3
- package/src/operations/extrusions/extrudeFromSlices.test.js +47 -31
- package/src/operations/extrusions/extrudeLinear.js +10 -5
- package/src/operations/extrusions/extrudeLinear.test.js +91 -35
- package/src/operations/extrusions/extrudeLinearGeom2.js +5 -2
- package/src/operations/extrusions/extrudeLinearPath2.js +24 -0
- package/src/operations/extrusions/extrudeRectangular.js +1 -1
- package/src/operations/extrusions/extrudeRectangular.test.js +22 -15
- package/src/operations/extrusions/extrudeRotate.test.js +31 -27
- package/src/operations/extrusions/project.js +1 -1
- package/src/operations/extrusions/project.test.js +5 -5
- package/src/operations/extrusions/slice/calculatePlane.js +7 -4
- package/src/operations/extrusions/slice/isA.js +2 -2
- package/src/operations/extrusions/slice/repairSlice.js +47 -0
- package/src/operations/extrusions/slice/toPolygons.js +24 -60
- package/src/operations/hulls/hull.test.js +25 -2
- package/src/operations/hulls/hullChain.js +1 -1
- package/src/operations/hulls/hullChain.test.js +6 -4
- package/src/operations/hulls/hullGeom2.js +1 -1
- package/src/operations/hulls/hullPath2.js +6 -4
- package/src/operations/hulls/hullPath2.test.js +16 -0
- package/src/operations/hulls/hullPoints2.test.js +1 -1
- package/src/operations/modifiers/edges.js +1 -1
- package/src/operations/modifiers/generalize.js +1 -1
- package/src/operations/modifiers/generalize.test.js +6 -0
- package/src/operations/modifiers/snap.test.js +3 -3
- package/src/operations/transforms/align.d.ts +1 -1
- package/src/operations/transforms/align.test.js +12 -0
- package/src/operations/transforms/center.js +17 -17
- package/src/operations/transforms/center.test.js +12 -0
- package/src/operations/transforms/mirror.js +12 -12
- package/src/operations/transforms/mirror.test.js +16 -0
- package/src/operations/transforms/rotate.js +12 -12
- package/src/operations/transforms/rotate.test.js +10 -0
- package/src/operations/transforms/scale.js +19 -19
- package/src/operations/transforms/scale.test.js +15 -0
- package/src/operations/transforms/transform.js +3 -3
- package/src/operations/transforms/transform.test.js +5 -0
- package/src/operations/transforms/translate.js +14 -14
- package/src/operations/transforms/translate.test.js +16 -0
- package/src/primitives/arc.js +1 -1
- package/src/primitives/arc.test.js +11 -0
- package/src/primitives/circle.test.js +15 -9
- package/src/primitives/cube.test.js +3 -0
- package/src/primitives/cuboid.test.js +9 -24
- package/src/primitives/cylinder.test.js +7 -4
- package/src/primitives/cylinderElliptic.js +13 -6
- package/src/primitives/cylinderElliptic.test.js +72 -52
- package/src/primitives/ellipse.js +3 -1
- package/src/primitives/ellipse.test.js +14 -8
- package/src/primitives/ellipsoid.js +7 -5
- package/src/primitives/ellipsoid.test.js +84 -82
- package/src/primitives/geodesicSphere.d.ts +0 -1
- package/src/primitives/geodesicSphere.test.js +3 -0
- package/src/primitives/line.test.js +1 -0
- package/src/primitives/polygon.test.js +15 -10
- package/src/primitives/polyhedron.js +1 -1
- package/src/primitives/polyhedron.test.js +14 -42
- package/src/primitives/rectangle.test.js +3 -0
- package/src/primitives/roundedCuboid.test.js +5 -0
- package/src/primitives/roundedCylinder.js +6 -4
- package/src/primitives/roundedCylinder.test.js +40 -36
- package/src/primitives/roundedRectangle.test.js +5 -0
- package/src/primitives/sphere.test.js +52 -73
- package/src/primitives/square.test.js +3 -0
- package/src/primitives/star.test.js +6 -0
- package/src/primitives/torus.d.ts +0 -1
- package/src/primitives/torus.test.js +8 -1
- package/src/primitives/triangle.js +1 -1
- package/src/primitives/triangle.test.js +7 -0
- package/src/text/vectorText.js +2 -2
- package/src/utils/areAllShapesTheSameType.js +2 -2
- package/src/utils/areAllShapesTheSameType.test.js +17 -0
- package/src/utils/index.d.ts +1 -0
- package/src/utils/index.js +3 -1
- package/src/utils/padArrayToLength.js +1 -1
- package/src/utils/trigonometry.d.ts +2 -0
- package/src/utils/trigonometry.js +35 -0
- package/src/utils/trigonometry.test.js +25 -0
- package/test/helpers/nearlyEqual.js +4 -1
|
@@ -20,9 +20,9 @@ test('scission: scission of one or more geom3 objects produces expected geometry
|
|
|
20
20
|
t.is(result2.length, 3)
|
|
21
21
|
t.is(result2[0].length, 0)
|
|
22
22
|
t.is(result2[1].length, 1)
|
|
23
|
-
t.
|
|
23
|
+
t.notThrows(() => geom3.validate(result2[1][0]))
|
|
24
24
|
t.is(result2[2].length, 1)
|
|
25
|
-
t.
|
|
25
|
+
t.notThrows(() => geom3.validate(result2[2][0]))
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
test('scission: scission of complex geom3 produces expected geometry', (t) => {
|
|
@@ -40,8 +40,8 @@ test('scission: scission of complex geom3 produces expected geometry', (t) => {
|
|
|
40
40
|
|
|
41
41
|
const result1 = scission(geometry3)
|
|
42
42
|
t.is(result1.length, 2)
|
|
43
|
-
t.
|
|
44
|
-
t.
|
|
43
|
+
t.notThrows.skip(() => geom3.validate(result1[0]))
|
|
44
|
+
t.notThrows.skip(() => geom3.validate(result1[1]))
|
|
45
45
|
|
|
46
46
|
const rc1 = geom3.toPolygons(result1[0]).length
|
|
47
47
|
const rc2 = geom3.toPolygons(result1[1]).length
|
|
@@ -17,7 +17,7 @@ const subtractGeom3 = require('./subtractGeom3')
|
|
|
17
17
|
* @alias module:modeling/booleans.subtract
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
|
-
* let myshape = subtract(
|
|
20
|
+
* let myshape = subtract(cuboid({size: [5,5,5]}), cuboid({size: [5,5,5], center: [5,5,5]}))
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* +-------+ +-------+
|
|
@@ -36,6 +36,7 @@ test('subtract: subtract of one or more geom2 objects produces expected geometry
|
|
|
36
36
|
[0, -2],
|
|
37
37
|
[1.4142000000000001, -1.4142000000000001]
|
|
38
38
|
]
|
|
39
|
+
t.notThrows(() => geom2.validate(result1))
|
|
39
40
|
t.is(obs.length, 8)
|
|
40
41
|
t.true(comparePoints(obs, exp))
|
|
41
42
|
|
|
@@ -54,6 +55,7 @@ test('subtract: subtract of one or more geom2 objects produces expected geometry
|
|
|
54
55
|
[0, -2],
|
|
55
56
|
[1.4142000000000001, -1.4142000000000001]
|
|
56
57
|
]
|
|
58
|
+
t.notThrows(() => geom2.validate(result2))
|
|
57
59
|
t.is(obs.length, 8)
|
|
58
60
|
t.true(comparePoints(obs, exp))
|
|
59
61
|
|
|
@@ -65,6 +67,7 @@ test('subtract: subtract of one or more geom2 objects produces expected geometry
|
|
|
65
67
|
exp = [
|
|
66
68
|
[12, 12], [9, 9], [8, 9], [8, 12], [9, 8], [12, 8]
|
|
67
69
|
]
|
|
70
|
+
t.notThrows(() => geom2.validate(result3))
|
|
68
71
|
t.is(obs.length, 6)
|
|
69
72
|
t.true(comparePoints(obs, exp))
|
|
70
73
|
|
|
@@ -73,6 +76,7 @@ test('subtract: subtract of one or more geom2 objects produces expected geometry
|
|
|
73
76
|
obs = geom2.toPoints(result4)
|
|
74
77
|
exp = [
|
|
75
78
|
]
|
|
79
|
+
t.notThrows(() => geom2.validate(result4))
|
|
76
80
|
t.is(obs.length, 0)
|
|
77
81
|
t.deepEqual(obs, exp)
|
|
78
82
|
})
|
|
@@ -133,6 +137,7 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
133
137
|
[[0.9999999999999998, 1.0000000000000002, -1.414213562373095], [1.4142135623730951, 3.4638242249419736e-16, -1.414213562373095], [8.65956056235493e-17, 8.659560562354935e-17, -2]],
|
|
134
138
|
[[8.65956056235493e-17, 8.659560562354935e-17, 2], [1.4142135623730951, 3.4638242249419736e-16, 1.414213562373095], [0.9999999999999998, 1.0000000000000002, 1.414213562373095]]
|
|
135
139
|
]
|
|
140
|
+
t.notThrows.skip(() => geom3.validate(result1))
|
|
136
141
|
t.is(obs.length, 32)
|
|
137
142
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
138
143
|
|
|
@@ -141,6 +146,7 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
141
146
|
|
|
142
147
|
const result2 = subtract(geometry1, geometry2)
|
|
143
148
|
obs = geom3.toPoints(result2)
|
|
149
|
+
t.notThrows.skip(() => geom3.validate(result2))
|
|
144
150
|
t.is(obs.length, 32)
|
|
145
151
|
|
|
146
152
|
// subtract of two partially overlapping objects
|
|
@@ -162,11 +168,13 @@ test('subtract: subtract of one or more geom3 objects produces expected geometry
|
|
|
162
168
|
[[12, 12, 8], [12, 9, 8], [8, 9, 8], [8, 12, 8]],
|
|
163
169
|
[[12, 9, 8], [12, 8, 8], [9, 8, 8], [9, 9, 8]]
|
|
164
170
|
]
|
|
171
|
+
t.notThrows.skip(() => geom3.validate(result3))
|
|
165
172
|
t.is(obs.length, 12)
|
|
166
173
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
167
174
|
|
|
168
175
|
// subtract of two completely overlapping objects
|
|
169
176
|
const result4 = subtract(geometry1, geometry3)
|
|
170
177
|
obs = geom3.toPoints(result4)
|
|
178
|
+
t.notThrows(() => geom3.validate(result4))
|
|
171
179
|
t.is(obs.length, 0)
|
|
172
180
|
})
|
|
@@ -42,28 +42,32 @@ class Node {
|
|
|
42
42
|
node = current.node
|
|
43
43
|
polygontreenodes = current.polygontreenodes
|
|
44
44
|
|
|
45
|
-
// begin "function"
|
|
46
45
|
if (node.plane) {
|
|
46
|
+
const plane = node.plane
|
|
47
|
+
|
|
47
48
|
const backnodes = []
|
|
48
49
|
const frontnodes = []
|
|
49
50
|
const coplanarfrontnodes = alsoRemovecoplanarFront ? backnodes : frontnodes
|
|
50
|
-
const plane = node.plane
|
|
51
51
|
const numpolygontreenodes = polygontreenodes.length
|
|
52
52
|
for (let i = 0; i < numpolygontreenodes; i++) {
|
|
53
|
-
const
|
|
54
|
-
if (!
|
|
55
|
-
|
|
53
|
+
const treenode = polygontreenodes[i]
|
|
54
|
+
if (!treenode.isRemoved()) {
|
|
55
|
+
// split this polygon tree node using the plane
|
|
56
|
+
// NOTE: children are added to the tree if there are spanning polygons
|
|
57
|
+
treenode.splitByPlane(plane, coplanarfrontnodes, backnodes, frontnodes, backnodes)
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
if (node.front && (frontnodes.length > 0)) {
|
|
62
|
+
// add front node for further splitting
|
|
60
63
|
stack.push({ node: node.front, polygontreenodes: frontnodes })
|
|
61
64
|
}
|
|
62
65
|
const numbacknodes = backnodes.length
|
|
63
66
|
if (node.back && (numbacknodes > 0)) {
|
|
67
|
+
// add back node for further splitting
|
|
64
68
|
stack.push({ node: node.back, polygontreenodes: backnodes })
|
|
65
69
|
} else {
|
|
66
|
-
//
|
|
70
|
+
// remove all back nodes from processing
|
|
67
71
|
for (let i = 0; i < numbacknodes; i++) {
|
|
68
72
|
backnodes[i].remove()
|
|
69
73
|
}
|
|
@@ -135,16 +139,6 @@ class Node {
|
|
|
135
139
|
current = stack.pop()
|
|
136
140
|
} while (current !== undefined)
|
|
137
141
|
}
|
|
138
|
-
|
|
139
|
-
// TODO is this still used?
|
|
140
|
-
getParentPlaneNormals (normals, maxdepth) {
|
|
141
|
-
if (maxdepth > 0) {
|
|
142
|
-
if (this.parent) {
|
|
143
|
-
normals.push(this.parent.plane.normal)
|
|
144
|
-
this.parent.getParentPlaneNormals(normals, maxdepth - 1)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
142
|
}
|
|
149
143
|
|
|
150
144
|
module.exports = Node
|
|
@@ -21,11 +21,11 @@ const splitPolygonByPlane = require('./splitPolygonByPlane')
|
|
|
21
21
|
// since they are no longer intact.
|
|
22
22
|
class PolygonTreeNode {
|
|
23
23
|
// constructor creates the root node
|
|
24
|
-
constructor () {
|
|
25
|
-
this.parent =
|
|
24
|
+
constructor (parent, polygon) {
|
|
25
|
+
this.parent = parent
|
|
26
26
|
this.children = []
|
|
27
|
-
this.polygon =
|
|
28
|
-
this.removed = false
|
|
27
|
+
this.polygon = polygon
|
|
28
|
+
this.removed = false // state of branch or leaf
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// fill the tree with polygons. Should be called on the root node only; child nodes must
|
|
@@ -47,6 +47,7 @@ class PolygonTreeNode {
|
|
|
47
47
|
remove () {
|
|
48
48
|
if (!this.removed) {
|
|
49
49
|
this.removed = true
|
|
50
|
+
this.polygon = null
|
|
50
51
|
|
|
51
52
|
// remove ourselves from the parent's children list:
|
|
52
53
|
const parentschildren = this.parent.children
|
|
@@ -183,9 +184,7 @@ class PolygonTreeNode {
|
|
|
183
184
|
// a child should be created for every fragment of the split polygon
|
|
184
185
|
// returns the newly created child
|
|
185
186
|
addChild (polygon) {
|
|
186
|
-
const newchild = new PolygonTreeNode()
|
|
187
|
-
newchild.parent = this
|
|
188
|
-
newchild.polygon = polygon
|
|
187
|
+
const newchild = new PolygonTreeNode(this, polygon)
|
|
189
188
|
this.children.push(newchild)
|
|
190
189
|
return newchild
|
|
191
190
|
}
|
|
@@ -206,13 +205,13 @@ class PolygonTreeNode {
|
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
|
|
208
|
+
// private method
|
|
209
|
+
// remove the polygon from the node, and all parent nodes above it
|
|
210
|
+
// called to invalidate parents of removed nodes
|
|
209
211
|
recursivelyInvalidatePolygon () {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (node.parent) {
|
|
214
|
-
node = node.parent
|
|
215
|
-
}
|
|
212
|
+
this.polygon = null
|
|
213
|
+
if (this.parent) {
|
|
214
|
+
this.parent.recursivelyInvalidatePolygon()
|
|
216
215
|
}
|
|
217
216
|
}
|
|
218
217
|
|
|
@@ -248,7 +247,7 @@ class PolygonTreeNode {
|
|
|
248
247
|
node = children[j]
|
|
249
248
|
result += `${prefix}PolygonTreeNode (${node.isRootNode()}): ${node.children.length}`
|
|
250
249
|
if (node.polygon) {
|
|
251
|
-
result += `\n ${prefix}
|
|
250
|
+
result += `\n ${prefix}polygon: ${node.polygon.vertices}\n`
|
|
252
251
|
} else {
|
|
253
252
|
result += '\n'
|
|
254
253
|
}
|
|
@@ -19,8 +19,7 @@ class Tree {
|
|
|
19
19
|
|
|
20
20
|
// Remove all polygons in this BSP tree that are inside the other BSP tree
|
|
21
21
|
// `tree`.
|
|
22
|
-
clipTo (tree, alsoRemovecoplanarFront) {
|
|
23
|
-
alsoRemovecoplanarFront = !!alsoRemovecoplanarFront
|
|
22
|
+
clipTo (tree, alsoRemovecoplanarFront = false) {
|
|
24
23
|
this.rootnode.clipTo(tree, alsoRemovecoplanarFront)
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -36,7 +36,7 @@ const splitPolygonByPlane = (splane, polygon) => {
|
|
|
36
36
|
const MINEPS = -EPS
|
|
37
37
|
for (let i = 0; i < numvertices; i++) {
|
|
38
38
|
const t = vec3.dot(splane, vertices[i]) - splane[3]
|
|
39
|
-
const isback = (t <
|
|
39
|
+
const isback = (t < MINEPS)
|
|
40
40
|
vertexIsBack.push(isback)
|
|
41
41
|
if (t > EPS) hasfront = true
|
|
42
42
|
if (t < MINEPS) hasback = true
|
|
@@ -69,9 +69,8 @@ const splitPolygonByPlane = (splane, polygon) => {
|
|
|
69
69
|
}
|
|
70
70
|
} else {
|
|
71
71
|
// line segment intersects plane:
|
|
72
|
-
const point = vertex
|
|
73
72
|
const nextpoint = vertices[nextvertexindex]
|
|
74
|
-
const intersectionpoint = splitLineSegmentByPlane(splane,
|
|
73
|
+
const intersectionpoint = splitLineSegmentByPlane(splane, vertex, nextpoint)
|
|
75
74
|
if (isback) {
|
|
76
75
|
backvertices.push(vertex)
|
|
77
76
|
backvertices.push(intersectionpoint)
|
|
@@ -9,6 +9,7 @@ const { circle, rectangle, sphere, cuboid } = require('../../primitives')
|
|
|
9
9
|
const { union } = require('./index')
|
|
10
10
|
|
|
11
11
|
const { center } = require('../transforms/center')
|
|
12
|
+
const { translate } = require('../transforms/translate')
|
|
12
13
|
|
|
13
14
|
// test('union: union of a path produces expected changes to points', (t) => {
|
|
14
15
|
// let geometry = path.fromPoints({}, [[0, 1, 0], [1, 0, 0]])
|
|
@@ -36,6 +37,7 @@ test('union of one or more geom2 objects produces expected geometry', (t) => {
|
|
|
36
37
|
[0, -2],
|
|
37
38
|
[1.4142000000000001, -1.4142000000000001]
|
|
38
39
|
]
|
|
40
|
+
t.notThrows(() => geom2.validate(result1))
|
|
39
41
|
t.true(comparePoints(obs, exp))
|
|
40
42
|
|
|
41
43
|
// union of two non-overlapping objects
|
|
@@ -57,6 +59,7 @@ test('union of one or more geom2 objects produces expected geometry', (t) => {
|
|
|
57
59
|
[12, 12],
|
|
58
60
|
[1.4142000000000001, -1.4142000000000001]
|
|
59
61
|
]
|
|
62
|
+
t.notThrows(() => geom2.validate(result2))
|
|
60
63
|
t.true(comparePoints(obs, exp))
|
|
61
64
|
|
|
62
65
|
// union of two partially overlapping objects
|
|
@@ -74,6 +77,7 @@ test('union of one or more geom2 objects produces expected geometry', (t) => {
|
|
|
74
77
|
[7.999933333333333, 9.000053333333334],
|
|
75
78
|
[11.999973333333333, 7.999933333333333]
|
|
76
79
|
]
|
|
80
|
+
t.notThrows(() => geom2.validate(result3))
|
|
77
81
|
t.true(comparePoints(obs, exp))
|
|
78
82
|
|
|
79
83
|
// union of two completely overlapping objects
|
|
@@ -85,7 +89,24 @@ test('union of one or more geom2 objects produces expected geometry', (t) => {
|
|
|
85
89
|
[9.000046666666666, 9.000046666666666],
|
|
86
90
|
[-9.000046666666666, 9.000046666666666]
|
|
87
91
|
]
|
|
92
|
+
t.notThrows(() => geom2.validate(result4))
|
|
88
93
|
t.true(comparePoints(obs, exp))
|
|
94
|
+
|
|
95
|
+
// union of unions of non-overlapping objects (BSP gap from #907)
|
|
96
|
+
const circ = circle({ radius: 1, segments: 32 })
|
|
97
|
+
const result5 = union(
|
|
98
|
+
union(
|
|
99
|
+
translate([17, 21], circ),
|
|
100
|
+
translate([7, 0], circ),
|
|
101
|
+
),
|
|
102
|
+
union(
|
|
103
|
+
translate([3, 21], circ),
|
|
104
|
+
translate([17, 21], circ),
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
obs = geom2.toPoints(result5)
|
|
108
|
+
t.notThrows.skip(() => geom2.validate(result5))
|
|
109
|
+
t.is(obs.length, 112)
|
|
89
110
|
})
|
|
90
111
|
|
|
91
112
|
test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
@@ -144,6 +165,7 @@ test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
144
165
|
[[0.9999999999999998, 1.0000000000000002, -1.414213562373095], [1.4142135623730951, 3.4638242249419736e-16, -1.414213562373095], [8.65956056235493e-17, 8.659560562354935e-17, -2]],
|
|
145
166
|
[[8.65956056235493e-17, 8.659560562354935e-17, 2], [1.4142135623730951, 3.4638242249419736e-16, 1.414213562373095], [0.9999999999999998, 1.0000000000000002, 1.414213562373095]]
|
|
146
167
|
]
|
|
168
|
+
t.notThrows.skip(() => geom3.validate(result1))
|
|
147
169
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
148
170
|
|
|
149
171
|
// union of two non-overlapping objects
|
|
@@ -151,6 +173,7 @@ test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
151
173
|
|
|
152
174
|
const result2 = union(geometry1, geometry2)
|
|
153
175
|
obs = geom3.toPoints(result2)
|
|
176
|
+
t.notThrows.skip(() => geom3.validate(result2))
|
|
154
177
|
t.is(obs.length, 38)
|
|
155
178
|
|
|
156
179
|
// union of two partially overlapping objects
|
|
@@ -178,6 +201,7 @@ test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
178
201
|
[[-9, 9, 9], [-9, 8, 9], [8, 8, 9], [8, 9, 9]],
|
|
179
202
|
[[-9, 8, 9], [-9, -9, 9], [9, -9, 9], [9, 8, 9]]
|
|
180
203
|
]
|
|
204
|
+
t.notThrows.skip(() => geom3.validate(result3))
|
|
181
205
|
t.is(obs.length, 18)
|
|
182
206
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
183
207
|
|
|
@@ -192,16 +216,18 @@ test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
192
216
|
[[-9, -9, -9], [-9, 9, -9], [9, 9, -9], [9, -9, -9]],
|
|
193
217
|
[[-9, -9, 9], [9, -9, 9], [9, 9, 9], [-9, 9, 9]]
|
|
194
218
|
]
|
|
219
|
+
t.notThrows(() => geom3.validate(result4))
|
|
195
220
|
t.is(obs.length, 6)
|
|
196
221
|
t.true(comparePolygonsAsPoints(obs, exp))
|
|
197
222
|
})
|
|
198
223
|
|
|
199
224
|
test('union of geom3 with rounding issues #137', (t) => {
|
|
200
225
|
const geometry1 = center({ relativeTo: [0, 0, -1] }, cuboid({ size: [44, 26, 5] }))
|
|
201
|
-
const geometry2 = center({ relativeTo: [0, 0, -4.400001] }, cuboid({ size: [44, 26, 1.8] })) // introduce
|
|
226
|
+
const geometry2 = center({ relativeTo: [0, 0, -4.400001] }, cuboid({ size: [44, 26, 1.8] })) // introduce precision error
|
|
202
227
|
|
|
203
228
|
const obs = union(geometry1, geometry2)
|
|
204
229
|
const pts = geom3.toPoints(obs)
|
|
230
|
+
t.notThrows(() => geom3.validate(obs))
|
|
205
231
|
t.is(pts.length, 6) // number of polygons in union
|
|
206
232
|
})
|
|
207
233
|
|
|
@@ -266,6 +292,7 @@ test('union of geom2 with closing issues #15', (t) => {
|
|
|
266
292
|
[-49.34040695243976, -15.797284338334542],
|
|
267
293
|
[-45.82121705016925, -16.857333163105647]
|
|
268
294
|
]
|
|
295
|
+
t.notThrows(() => geom2.validate(obs))
|
|
269
296
|
t.is(pts.length, 20) // number of sides in union
|
|
270
297
|
t.true(comparePoints(pts, exp))
|
|
271
298
|
})
|
|
@@ -7,7 +7,7 @@ const { Tree } = require('./trees')
|
|
|
7
7
|
* Return a new 3D geometry representing the space in the given geometries.
|
|
8
8
|
* @param {geom3} geometry1 - geometry to union
|
|
9
9
|
* @param {geom3} geometry2 - geometry to union
|
|
10
|
-
* @returns {
|
|
10
|
+
* @returns {geom3} new 3D geometry
|
|
11
11
|
*/
|
|
12
12
|
const unionSub = (geometry1, geometry2) => {
|
|
13
13
|
if (!mayOverlap(geometry1, geometry2)) {
|
|
@@ -10,14 +10,14 @@ const expandPath2 = require('./expandPath2')
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Expand the given geometry using the given options.
|
|
13
|
-
* Both
|
|
13
|
+
* Both internal and external space is expanded for 2D and 3D shapes.
|
|
14
14
|
*
|
|
15
15
|
* Note: Contract is expand using a negative delta.
|
|
16
16
|
* @param {Object} options - options for expand
|
|
17
17
|
* @param {Number} [options.delta=1] - delta (+/-) of expansion
|
|
18
18
|
* @param {String} [options.corners='edge'] - type of corner to create after expanding; edge, chamfer, round
|
|
19
19
|
* @param {Integer} [options.segments=16] - number of segments when creating round corners
|
|
20
|
-
* @param {...Objects}
|
|
20
|
+
* @param {...Objects} objects - the geometries to expand
|
|
21
21
|
* @return {Object|Array} new geometry, or list of new geometries
|
|
22
22
|
* @alias module:modeling/expansions.expand
|
|
23
23
|
*
|
|
@@ -14,6 +14,7 @@ test('expand: edge-expanding a straight line produces rectangle', (t) => {
|
|
|
14
14
|
const expandedPathGeom2 = expand({ delta: 2, corners: 'edge', segments: 8 }, linePath2)
|
|
15
15
|
const expandedPoints = geom2.toPoints(expandedPathGeom2)
|
|
16
16
|
|
|
17
|
+
t.notThrows(() => geom2.validate(expandedPathGeom2))
|
|
17
18
|
t.is(area(expandedPoints), 40)
|
|
18
19
|
t.true(comparePoints(measureBoundingBox(expandedPathGeom2), [[-2, 0, 0], [2, 10, 0]]))
|
|
19
20
|
})
|
|
@@ -24,6 +25,7 @@ test('expand: edge-expanding a bent line produces expected geometry', (t) => {
|
|
|
24
25
|
const expandedPathGeom2 = expand({ delta: 2, corners: 'edge', segments: 8 }, linePath2)
|
|
25
26
|
const expandedPoints = geom2.toPoints(expandedPathGeom2)
|
|
26
27
|
|
|
28
|
+
t.notThrows(() => geom2.validate(expandedPathGeom2))
|
|
27
29
|
t.is(area(expandedPoints), 60)
|
|
28
30
|
const boundingBox = measureBoundingBox(expandedPathGeom2)
|
|
29
31
|
t.true(comparePoints(boundingBox, [[-5, 0, 0], [2, 12, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -35,6 +37,7 @@ test('expand: edge-expanding a bent line, reversed points, produces expected geo
|
|
|
35
37
|
const expandedPathGeom2 = expand({ delta: 2, corners: 'edge', segments: 8 }, linePath2)
|
|
36
38
|
const expandedPoints = geom2.toPoints(expandedPathGeom2)
|
|
37
39
|
|
|
40
|
+
t.notThrows(() => geom2.validate(expandedPathGeom2))
|
|
38
41
|
t.is(area(expandedPoints), 60)
|
|
39
42
|
const boundingBox = measureBoundingBox(expandedPathGeom2)
|
|
40
43
|
t.true(comparePoints(boundingBox, [[-5, 0, 0], [2, 12, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -47,6 +50,7 @@ test('expand: round-expanding a bent line produces expected geometry', (t) => {
|
|
|
47
50
|
const expandedPathGeom2 = expand({ delta, corners: 'round', segments: 128 }, linePath2)
|
|
48
51
|
const expandedPoints = geom2.toPoints(expandedPathGeom2)
|
|
49
52
|
|
|
53
|
+
t.notThrows(() => geom2.validate(expandedPathGeom2))
|
|
50
54
|
const expectedArea = 56 + 2 * Math.PI * delta * 1.25 // shape will have 1 and 1/4 circles
|
|
51
55
|
nearlyEqual(t, area(expandedPoints), expectedArea, 0.01, 'Measured area should be pretty close')
|
|
52
56
|
const boundingBox = measureBoundingBox(expandedPathGeom2)
|
|
@@ -60,6 +64,7 @@ test('expand: chamfer-expanding a bent line produces expected geometry', (t) =>
|
|
|
60
64
|
const expandedPathGeom2 = expand({ delta, corners: 'chamfer', segments: 8 }, linePath2)
|
|
61
65
|
const expandedPoints = geom2.toPoints(expandedPathGeom2)
|
|
62
66
|
|
|
67
|
+
t.notThrows(() => geom2.validate(expandedPathGeom2))
|
|
63
68
|
t.is(area(expandedPoints), 58)
|
|
64
69
|
const boundingBox = measureBoundingBox(expandedPathGeom2)
|
|
65
70
|
t.true(comparePoints(boundingBox, [[-5, 0, 0], [2, 12, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -84,6 +89,7 @@ test('expand: expanding of a geom2 produces expected changes to points', (t) =>
|
|
|
84
89
|
[-10, 8],
|
|
85
90
|
[-10, -8]
|
|
86
91
|
]
|
|
92
|
+
t.notThrows(() => geom2.validate(obs))
|
|
87
93
|
t.is(pts.length, 12)
|
|
88
94
|
t.true(comparePoints(pts, exp))
|
|
89
95
|
})
|
|
@@ -113,6 +119,7 @@ test('expand: expanding of a geom3 produces expected changes to polygons', (t) =
|
|
|
113
119
|
[16, -6.414213562373095, 16]
|
|
114
120
|
]
|
|
115
121
|
|
|
122
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
116
123
|
t.is(pts.length, 62)
|
|
117
124
|
t.true(comparePoints(pts[0], exp0))
|
|
118
125
|
t.true(comparePoints(pts[61], exp61))
|
|
@@ -120,90 +127,60 @@ test('expand: expanding of a geom3 produces expected changes to polygons', (t) =
|
|
|
120
127
|
const geometry2 = sphere({ radius: 5, segments: 8 })
|
|
121
128
|
const obs2 = expand({ delta: 5 }, geometry2)
|
|
122
129
|
const pts2 = geom3.toPoints(obs2)
|
|
123
|
-
t.
|
|
130
|
+
t.notThrows.skip(() => geom3.validate(obs2))
|
|
131
|
+
t.is(pts2.length, 864)
|
|
124
132
|
})
|
|
125
133
|
|
|
126
134
|
test('expand (options): offsetting of a complex geom2 produces expected offset geom2', (t) => {
|
|
127
135
|
const geometry = geom2.create([
|
|
128
|
-
[[-75
|
|
129
|
-
[[-75
|
|
130
|
-
[[75
|
|
131
|
-
[[-40
|
|
132
|
-
[[75
|
|
133
|
-
[[40
|
|
134
|
-
[[40
|
|
135
|
-
[[-40
|
|
136
|
-
[[15
|
|
137
|
-
[[-15
|
|
138
|
-
[[-15
|
|
139
|
-
[[-8
|
|
140
|
-
[[15
|
|
141
|
-
[[-8
|
|
142
|
-
[[8
|
|
143
|
-
[[8
|
|
144
|
-
[[-2
|
|
145
|
-
[[-2
|
|
146
|
-
[[2
|
|
147
|
-
[[2
|
|
136
|
+
[[-75, 75], [-75, -75]],
|
|
137
|
+
[[-75, -75], [75, -75]],
|
|
138
|
+
[[75, -75], [75, 75]],
|
|
139
|
+
[[-40, 75], [-75, 75]],
|
|
140
|
+
[[75, 75], [40, 75]],
|
|
141
|
+
[[40, 75], [40, 0]],
|
|
142
|
+
[[40, 0], [-40, 0]],
|
|
143
|
+
[[-40, 0], [-40, 75]],
|
|
144
|
+
[[15, -10], [15, -40]],
|
|
145
|
+
[[-15, -10], [15, -10]],
|
|
146
|
+
[[-15, -40], [-15, -10]],
|
|
147
|
+
[[-8, -40], [-15, -40]],
|
|
148
|
+
[[15, -40], [8, -40]],
|
|
149
|
+
[[-8, -25], [-8, -40]],
|
|
150
|
+
[[8, -25], [-8, -25]],
|
|
151
|
+
[[8, -40], [8, -25]],
|
|
152
|
+
[[-2, -15], [-2, -19]],
|
|
153
|
+
[[-2, -19], [2, -19]],
|
|
154
|
+
[[2, -19], [2, -15]],
|
|
155
|
+
[[2, -15], [-2, -15]]
|
|
148
156
|
])
|
|
149
157
|
|
|
150
158
|
// expand +
|
|
151
159
|
const obs = expand({ delta: 2, corners: 'edge' }, geometry)
|
|
152
160
|
const pts = geom2.toPoints(obs)
|
|
153
161
|
const exp = [
|
|
154
|
-
[-77, -77],
|
|
155
|
-
[-75, -77],
|
|
156
|
-
[75, -77],
|
|
157
162
|
[77, -77],
|
|
158
|
-
[77, -75],
|
|
159
|
-
[77, 75],
|
|
160
163
|
[77, 77],
|
|
161
|
-
[75, 77],
|
|
162
|
-
[40, 77],
|
|
163
164
|
[38, 77],
|
|
164
|
-
[38, 75],
|
|
165
165
|
[38, 2],
|
|
166
166
|
[-38, 2],
|
|
167
|
-
[-38, 75],
|
|
168
167
|
[-37.99999999999999, 77],
|
|
169
|
-
[-40, 77],
|
|
170
|
-
[-75, 77],
|
|
171
168
|
[-77, 77],
|
|
172
|
-
[-77, 75],
|
|
173
|
-
[17, -40],
|
|
174
169
|
[16.999999999999996, -42],
|
|
175
|
-
[15, -42],
|
|
176
|
-
[8, -42],
|
|
177
170
|
[6, -42],
|
|
178
|
-
[6, -40],
|
|
179
171
|
[6, -27],
|
|
180
172
|
[-6, -27],
|
|
181
|
-
[-6, -40],
|
|
182
173
|
[-6.000000000000001, -42],
|
|
183
|
-
[-8, -42],
|
|
184
|
-
[-15, -42],
|
|
185
174
|
[-17, -42],
|
|
186
|
-
[-17, -40],
|
|
187
|
-
[-17, -10],
|
|
188
175
|
[-16.999999999999996, -8],
|
|
189
|
-
[-15, -8],
|
|
190
|
-
[15, -8],
|
|
191
176
|
[17, -8.000000000000004],
|
|
192
|
-
[17, -10],
|
|
193
|
-
[-4, -19],
|
|
194
177
|
[-4, -21],
|
|
195
|
-
[-2, -21],
|
|
196
|
-
[1.9999999999999998, -21],
|
|
197
178
|
[3.9999999999999996, -21],
|
|
198
|
-
[4, -19],
|
|
199
|
-
[4, -15],
|
|
200
179
|
[4, -13],
|
|
201
|
-
[2, -13],
|
|
202
|
-
[-1.9999999999999998, -13],
|
|
203
180
|
[-4, -13],
|
|
204
|
-
[-
|
|
205
|
-
[-77, -75]
|
|
181
|
+
[-77, -77]
|
|
206
182
|
]
|
|
207
|
-
t.
|
|
183
|
+
t.notThrows(() => geom2.validate(obs))
|
|
184
|
+
t.is(pts.length, 20)
|
|
208
185
|
t.true(comparePoints(pts, exp))
|
|
209
186
|
})
|
|
@@ -15,37 +15,43 @@ const unionGeom3Sub = require('../booleans/unionGeom3Sub')
|
|
|
15
15
|
|
|
16
16
|
const extrudePolygon = require('./extrudePolygon')
|
|
17
17
|
|
|
18
|
+
/*
|
|
19
|
+
* Collect all planes adjacent to each vertex
|
|
20
|
+
*/
|
|
18
21
|
const mapPlaneToVertex = (map, vertex, plane) => {
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
22
|
+
const key = vertex.toString()
|
|
23
|
+
if (!map.has(key)) {
|
|
21
24
|
const entry = [vertex, [plane]]
|
|
22
|
-
map.
|
|
23
|
-
|
|
25
|
+
map.set(key, entry)
|
|
26
|
+
} else {
|
|
27
|
+
const planes = map.get(key)[1]
|
|
28
|
+
planes.push(plane)
|
|
24
29
|
}
|
|
25
|
-
const planes = map[i][1]
|
|
26
|
-
planes.push(plane)
|
|
27
|
-
return i
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
/*
|
|
33
|
+
* Collect all planes adjacent to each edge.
|
|
34
|
+
* Combine undirected edges, no need for duplicate cylinders.
|
|
35
|
+
*/
|
|
30
36
|
const mapPlaneToEdge = (map, edge, plane) => {
|
|
31
|
-
const
|
|
32
|
-
|
|
37
|
+
const key0 = edge[0].toString()
|
|
38
|
+
const key1 = edge[1].toString()
|
|
39
|
+
// Sort keys to make edges undirected
|
|
40
|
+
const key = key0 < key1 ? `${key0},${key1}` : `${key1},${key0}`
|
|
41
|
+
if (!map.has(key)) {
|
|
33
42
|
const entry = [edge, [plane]]
|
|
34
|
-
map.
|
|
35
|
-
|
|
43
|
+
map.set(key, entry)
|
|
44
|
+
} else {
|
|
45
|
+
const planes = map.get(key)[1]
|
|
46
|
+
planes.push(plane)
|
|
36
47
|
}
|
|
37
|
-
const planes = map[i][1]
|
|
38
|
-
planes.push(plane)
|
|
39
|
-
return i
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
const addUniqueAngle = (map, angle) => {
|
|
43
51
|
const i = map.findIndex((item) => item === angle)
|
|
44
52
|
if (i < 0) {
|
|
45
53
|
map.push(angle)
|
|
46
|
-
return map.length
|
|
47
54
|
}
|
|
48
|
-
return i
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
/*
|
|
@@ -65,8 +71,8 @@ const expandShell = (options, geometry) => {
|
|
|
65
71
|
const { delta, segments } = Object.assign({ }, defaults, options)
|
|
66
72
|
|
|
67
73
|
let result = geom3.create()
|
|
68
|
-
const vertices2planes =
|
|
69
|
-
const edges2planes =
|
|
74
|
+
const vertices2planes = new Map() // {vertex: [vertex, [plane, ...]]}
|
|
75
|
+
const edges2planes = new Map() // {edge: [[vertex, vertex], [plane, ...]]}
|
|
70
76
|
|
|
71
77
|
const v1 = vec3.create()
|
|
72
78
|
const v2 = vec3.create()
|
|
@@ -13,7 +13,7 @@ const offsetPath2 = require('./offsetPath2')
|
|
|
13
13
|
* @param {Float} [options.delta=1] - delta of offset (+ to exterior, - from interior)
|
|
14
14
|
* @param {String} [options.corners='edge'] - type of corner to create after offseting; edge, chamfer, round
|
|
15
15
|
* @param {Integer} [options.segments=16] - number of segments when creating round corners
|
|
16
|
-
* @param {...Object}
|
|
16
|
+
* @param {...Object} objects - the geometries to offset
|
|
17
17
|
* @return {Object|Array} new geometry, or list of new geometries
|
|
18
18
|
* @alias module:modeling/expansions.offset
|
|
19
19
|
*
|