@jscad/modeling 3.0.2-alpha.0 → 3.0.4-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 +25 -0
- package/dist/jscad-modeling.es.js +2 -7
- package/dist/jscad-modeling.min.js +2 -7
- package/package.json +6 -7
- package/rollup.config.js +8 -4
- package/src/colors/colorize.test.js +1 -1
- package/src/curves/bezier/arcLengthToT.js +1 -1
- package/src/curves/bezier/create.js +1 -1
- package/src/curves/bezier/index.js +7 -7
- package/src/curves/bezier/length.js +1 -1
- package/src/curves/bezier/lengths.js +2 -1
- package/src/curves/bezier/tangentAt.js +1 -1
- package/src/curves/bezier/valueAt.js +1 -1
- package/src/curves/index.js +3 -3
- package/src/geometries/geom2/applyTransforms.js +3 -1
- package/src/geometries/geom2/clone.js +5 -1
- package/src/geometries/geom2/create.js +4 -14
- package/src/geometries/geom2/fromSides.js +4 -2
- package/src/geometries/geom2/index.d.ts +0 -2
- package/src/geometries/geom2/index.js +21 -7
- package/src/geometries/geom2/isA.js +5 -1
- package/src/geometries/geom2/reverse.js +4 -2
- package/src/geometries/geom2/toOutlines.js +2 -1
- package/src/geometries/geom2/toPoints.js +5 -2
- package/src/geometries/geom2/toSides.js +4 -3
- package/src/geometries/geom2/toString.js +3 -2
- package/src/geometries/geom2/transform.js +4 -2
- package/src/geometries/geom2/validate.js +6 -2
- package/src/geometries/geom3/applyTransforms.test.js +2 -2
- package/src/geometries/geom3/clone.js +5 -1
- package/src/geometries/geom3/clone.test.js +2 -2
- package/src/geometries/geom3/create.js +6 -28
- package/src/geometries/geom3/{fromPoints.d.ts → fromVertices.d.ts} +1 -1
- package/src/geometries/geom3/{fromPoints.js → fromVertices.js} +15 -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 +4 -5
- package/src/geometries/geom3/index.js +29 -9
- package/src/geometries/geom3/invert.js +5 -1
- package/src/geometries/geom3/invert.test.js +2 -2
- package/src/geometries/geom3/isA.js +5 -1
- package/src/geometries/geom3/isA.test.js +2 -2
- package/src/geometries/geom3/isConvex.d.ts +3 -0
- package/src/geometries/geom3/isConvex.js +65 -0
- package/src/geometries/geom3/isConvex.test.js +44 -0
- package/src/geometries/geom3/toPolygons.js +4 -2
- package/src/geometries/geom3/toString.js +3 -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/toVertices.js +20 -0
- package/src/geometries/geom3/{toPoints.test.js → toVertices.test.js} +4 -4
- package/src/geometries/geom3/transform.js +5 -2
- package/src/geometries/geom3/transform.test.js +2 -2
- package/src/geometries/geom3/validate.js +6 -2
- package/src/geometries/geom3/validate.test.js +4 -4
- package/src/geometries/index.d.ts +1 -0
- package/src/geometries/index.js +10 -7
- package/src/geometries/path2/appendArc.js +7 -5
- package/src/geometries/path2/appendArc.test.js +11 -15
- package/src/geometries/path2/appendBezier.js +6 -4
- package/src/geometries/path2/appendPoints.js +4 -2
- package/src/geometries/path2/applyTransforms.js +3 -0
- package/src/geometries/path2/clone.js +5 -1
- package/src/geometries/path2/close.js +5 -1
- package/src/geometries/path2/concat.js +3 -2
- package/src/geometries/path2/create.js +5 -25
- package/src/geometries/path2/equals.js +12 -7
- package/src/geometries/path2/fromPoints.js +5 -3
- package/src/geometries/path2/index.d.ts +0 -2
- package/src/geometries/path2/index.js +21 -6
- package/src/geometries/path2/isA.js +5 -1
- package/src/geometries/path2/reverse.js +4 -2
- package/src/geometries/path2/toPoints.js +5 -3
- package/src/geometries/path2/toString.js +3 -2
- package/src/geometries/path2/transform.js +4 -2
- package/src/geometries/path2/validate.js +5 -1
- 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 +33 -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 +35 -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 +14 -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 +50 -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 +44 -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 +37 -0
- package/src/geometries/path3/isA.d.ts +3 -0
- package/src/geometries/path3/isA.js +22 -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 +18 -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 +23 -0
- package/src/geometries/path3/toVertices.d.ts +4 -0
- package/src/geometries/path3/toVertices.js +15 -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 +20 -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 +44 -0
- package/src/geometries/poly2/arePointsInside.js +4 -1
- package/src/geometries/poly2/clone.js +4 -1
- package/src/geometries/poly2/create.js +3 -15
- package/src/geometries/poly2/index.js +16 -4
- package/src/geometries/poly2/isA.js +5 -1
- package/src/geometries/poly2/isConvex.js +5 -1
- package/src/geometries/poly2/isSimple.js +5 -1
- package/src/geometries/poly2/measureArea.js +4 -1
- package/src/geometries/poly2/measureBoundingBox.js +6 -1
- package/src/geometries/poly2/reverse.js +4 -1
- package/src/geometries/poly2/toPoints.js +6 -1
- package/src/geometries/poly2/toString.js +5 -1
- package/src/geometries/poly2/transform.js +5 -1
- package/src/geometries/poly2/validate.js +6 -2
- package/src/geometries/poly3/clone.js +4 -1
- package/src/geometries/poly3/create.js +4 -17
- package/src/geometries/poly3/fromVerticesAndPlane.js +3 -1
- package/src/geometries/poly3/index.js +19 -4
- package/src/geometries/poly3/invert.js +4 -1
- package/src/geometries/poly3/isA.js +5 -1
- package/src/geometries/poly3/isConvex.js +5 -1
- package/src/geometries/poly3/measureArea.js +5 -1
- package/src/geometries/poly3/measureBoundingBox.js +4 -1
- package/src/geometries/poly3/measureBoundingSphere.js +4 -3
- package/src/geometries/poly3/measureSignedVolume.js +6 -1
- package/src/geometries/poly3/plane.js +6 -0
- package/src/geometries/poly3/toString.js +5 -1
- package/src/geometries/poly3/toVertices.js +6 -1
- package/src/geometries/poly3/transform.js +5 -1
- package/src/geometries/poly3/validate.js +6 -2
- package/src/geometries/slice/calculatePlane.js +3 -3
- package/src/geometries/slice/clone.js +4 -1
- package/src/geometries/slice/create.js +5 -10
- package/src/geometries/slice/equals.js +5 -1
- package/src/geometries/slice/fromGeom2.js +1 -1
- package/src/geometries/slice/fromVertices.js +3 -3
- package/src/geometries/slice/index.js +19 -4
- package/src/geometries/slice/isA.js +5 -1
- package/src/geometries/slice/reverse.js +5 -2
- package/src/geometries/slice/toEdges.js +5 -3
- package/src/geometries/slice/toPolygons.js +5 -1
- package/src/geometries/slice/toString.js +5 -1
- package/src/geometries/slice/toVertices.js +5 -3
- package/src/geometries/slice/transform.js +4 -3
- package/src/geometries/slice/validate.js +3 -2
- package/src/index.d.ts +1 -0
- package/src/index.js +4 -0
- package/src/maths/constants.js +11 -7
- package/src/maths/index.js +2 -1
- package/src/maths/mat4/isOnlyTransformScale.js +1 -1
- package/src/operations/booleans/index.js +2 -0
- package/src/operations/booleans/intersect.js +0 -1
- package/src/operations/booleans/intersectGeom3.test.js +4 -4
- package/src/operations/booleans/scission.js +0 -1
- package/src/operations/booleans/subtractGeom3.test.js +4 -4
- package/src/operations/booleans/trees/splitLineSegmentByPlane.js +1 -4
- package/src/operations/booleans/trees/splitPolygonByPlane.test.js +138 -0
- package/src/operations/booleans/unionGeom3.test.js +40 -5
- package/src/operations/extrusions/extrudeFromSlices.js +15 -5
- package/src/operations/extrusions/extrudeFromSlices.test.js +6 -6
- package/src/operations/extrusions/extrudeLinear.test.js +8 -8
- package/src/operations/extrusions/extrudeRotate.js +2 -1
- package/src/operations/extrusions/extrudeRotate.test.js +46 -12
- package/src/operations/extrusions/extrudeWalls.test.js +60 -0
- package/src/operations/hulls/hull.test.js +5 -5
- package/src/operations/hulls/hullChain.test.js +5 -5
- package/src/operations/hulls/toUniquePoints.js +2 -2
- package/src/operations/minkowski/index.d.ts +1 -0
- package/src/operations/minkowski/index.js +15 -0
- package/src/operations/minkowski/minkowskiSum.d.ts +4 -0
- package/src/operations/minkowski/minkowskiSum.js +223 -0
- package/src/operations/minkowski/minkowskiSum.test.js +199 -0
- package/src/operations/modifiers/generalize.test.js +6 -6
- package/src/operations/modifiers/insertTjunctions.test.js +2 -2
- package/src/operations/modifiers/reTesselateCoplanarPolygons.js +10 -3
- package/src/operations/modifiers/reTesselateCoplanarPolygons.test.js +36 -1
- package/src/operations/modifiers/retessellate.js +4 -2
- package/src/operations/modifiers/retessellate.test.js +10 -10
- package/src/operations/modifiers/snap.test.js +28 -19
- package/src/operations/offsets/offsetGeom3.test.js +9 -11
- 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/arc.js +2 -2
- package/src/primitives/arc.test.js +104 -113
- 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/utils/flatten.js +1 -1
- package/src/utils/flatten.test.js +94 -0
- 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/geom3/toPoints.js +0 -15
- 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
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import test from 'ava'
|
|
2
|
+
|
|
3
|
+
import { geom3 } from '../../geometries/index.js'
|
|
4
|
+
import { cuboid, sphere, torus } from '../../primitives/index.js'
|
|
5
|
+
import { subtract } from '../booleans/index.js'
|
|
6
|
+
import { measureBoundingBox } from '../../measurements/index.js'
|
|
7
|
+
|
|
8
|
+
import { minkowskiSum } from './index.js'
|
|
9
|
+
|
|
10
|
+
test('minkowskiSum: throws for non-geom3 inputs', (t) => {
|
|
11
|
+
t.throws(() => minkowskiSum('invalid', cuboid()), { message: /requires geom3/ })
|
|
12
|
+
t.throws(() => minkowskiSum(cuboid(), 'invalid'), { message: /requires geom3/ })
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('minkowskiSum: throws for wrong number of geometries', (t) => {
|
|
16
|
+
t.throws(() => minkowskiSum(), { message: /exactly two/ })
|
|
17
|
+
t.throws(() => minkowskiSum(cuboid()), { message: /exactly two/ })
|
|
18
|
+
t.throws(() => minkowskiSum(cuboid(), cuboid(), cuboid()), { message: /exactly two/ })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('minkowskiSum: cube + cube produces correct bounds', (t) => {
|
|
22
|
+
// Cube1: size 10 (±5 from origin)
|
|
23
|
+
// Cube2: size 4 (±2 from origin)
|
|
24
|
+
// Minkowski sum should be size 14 (±7 from origin)
|
|
25
|
+
const cube1 = cuboid({ size: [10, 10, 10] })
|
|
26
|
+
const cube2 = cuboid({ size: [4, 4, 4] })
|
|
27
|
+
|
|
28
|
+
const result = minkowskiSum(cube1, cube2)
|
|
29
|
+
|
|
30
|
+
t.notThrows(() => geom3.validate(result))
|
|
31
|
+
|
|
32
|
+
const bounds = measureBoundingBox(result)
|
|
33
|
+
// Allow small tolerance for floating point
|
|
34
|
+
t.true(Math.abs(bounds[0][0] - (-7)) < 0.001)
|
|
35
|
+
t.true(Math.abs(bounds[0][1] - (-7)) < 0.001)
|
|
36
|
+
t.true(Math.abs(bounds[0][2] - (-7)) < 0.001)
|
|
37
|
+
t.true(Math.abs(bounds[1][0] - 7) < 0.001)
|
|
38
|
+
t.true(Math.abs(bounds[1][1] - 7) < 0.001)
|
|
39
|
+
t.true(Math.abs(bounds[1][2] - 7) < 0.001)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('minkowskiSum: cube + sphere produces correct bounds', (t) => {
|
|
43
|
+
// Cube: size 10 (±5 from origin)
|
|
44
|
+
// Sphere: radius 2
|
|
45
|
+
// Minkowski sum should be ±7 from origin
|
|
46
|
+
const cube = cuboid({ size: [10, 10, 10] })
|
|
47
|
+
const sph = sphere({ radius: 2, segments: 16 })
|
|
48
|
+
|
|
49
|
+
const result = minkowskiSum(cube, sph)
|
|
50
|
+
|
|
51
|
+
t.notThrows(() => geom3.validate(result))
|
|
52
|
+
|
|
53
|
+
const bounds = measureBoundingBox(result)
|
|
54
|
+
// Allow small tolerance
|
|
55
|
+
t.true(Math.abs(bounds[0][0] - (-7)) < 0.1)
|
|
56
|
+
t.true(Math.abs(bounds[1][0] - 7) < 0.1)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('minkowskiSum: sphere + sphere produces correct bounds', (t) => {
|
|
60
|
+
// Sphere1: radius 3
|
|
61
|
+
// Sphere2: radius 2
|
|
62
|
+
// Minkowski sum should be a sphere-like shape with radius ~5
|
|
63
|
+
const sph1 = sphere({ radius: 3, segments: 16 })
|
|
64
|
+
const sph2 = sphere({ radius: 2, segments: 16 })
|
|
65
|
+
|
|
66
|
+
const result = minkowskiSum(sph1, sph2)
|
|
67
|
+
|
|
68
|
+
t.notThrows(() => geom3.validate(result))
|
|
69
|
+
|
|
70
|
+
const bounds = measureBoundingBox(result)
|
|
71
|
+
// Should be approximately ±5
|
|
72
|
+
t.true(Math.abs(bounds[0][0] - (-5)) < 0.2)
|
|
73
|
+
t.true(Math.abs(bounds[1][0] - 5) < 0.2)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('minkowskiSum: empty geometry returns empty', (t) => {
|
|
77
|
+
const empty = geom3.create()
|
|
78
|
+
const cube = cuboid({ size: [10, 10, 10] })
|
|
79
|
+
|
|
80
|
+
const result = minkowskiSum(empty, cube)
|
|
81
|
+
|
|
82
|
+
t.notThrows(() => geom3.validate(result))
|
|
83
|
+
t.is(geom3.toPolygons(result).length, 0)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('minkowskiSum: result is convex', (t) => {
|
|
87
|
+
const cube = cuboid({ size: [10, 10, 10] })
|
|
88
|
+
const sph = sphere({ radius: 2, segments: 12 })
|
|
89
|
+
|
|
90
|
+
const result = minkowskiSum(cube, sph)
|
|
91
|
+
|
|
92
|
+
t.notThrows(() => geom3.validate(result))
|
|
93
|
+
t.true(geom3.isConvex(result))
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Non-convex tests
|
|
97
|
+
|
|
98
|
+
test('minkowskiSum: non-convex + convex produces valid geometry', (t) => {
|
|
99
|
+
// Create L-shaped non-convex geometry
|
|
100
|
+
const big = cuboid({ size: [10, 10, 10] })
|
|
101
|
+
const corner = cuboid({ size: [6, 6, 12], center: [3, 3, 0] })
|
|
102
|
+
const lShape = subtract(big, corner)
|
|
103
|
+
|
|
104
|
+
t.false(geom3.isConvex(lShape))
|
|
105
|
+
|
|
106
|
+
const sph = sphere({ radius: 1, segments: 8 })
|
|
107
|
+
|
|
108
|
+
const result = minkowskiSum(lShape, sph)
|
|
109
|
+
|
|
110
|
+
t.true(geom3.toPolygons(result).length > 0)
|
|
111
|
+
t.true(geom3.isA(result))
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('minkowskiSum: non-convex + convex produces correct bounds', (t) => {
|
|
115
|
+
// Cube with hole through it
|
|
116
|
+
const cube = cuboid({ size: [10, 10, 10] })
|
|
117
|
+
const hole = cuboid({ size: [4, 4, 20] })
|
|
118
|
+
const cubeWithHole = subtract(cube, hole)
|
|
119
|
+
|
|
120
|
+
t.false(geom3.isConvex(cubeWithHole))
|
|
121
|
+
|
|
122
|
+
// Offset by sphere of radius 1
|
|
123
|
+
const sph = sphere({ radius: 1, segments: 8 })
|
|
124
|
+
const result = minkowskiSum(cubeWithHole, sph)
|
|
125
|
+
|
|
126
|
+
t.true(geom3.isA(result))
|
|
127
|
+
|
|
128
|
+
const bounds = measureBoundingBox(result)
|
|
129
|
+
|
|
130
|
+
// Original cube is ±5, plus sphere radius 1 = ±6
|
|
131
|
+
t.true(Math.abs(bounds[0][0] - (-6)) < 0.2)
|
|
132
|
+
t.true(Math.abs(bounds[1][0] - 6) < 0.2)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('minkowskiSum: convex + non-convex swaps operands', (t) => {
|
|
136
|
+
// Minkowski sum is commutative, so A⊕B = B⊕A
|
|
137
|
+
const cube = cuboid({ size: [10, 10, 10] })
|
|
138
|
+
const hole = cuboid({ size: [4, 4, 20] })
|
|
139
|
+
const cubeWithHole = subtract(cube, hole)
|
|
140
|
+
|
|
141
|
+
const sph = sphere({ radius: 1, segments: 8 })
|
|
142
|
+
|
|
143
|
+
// convex + non-convex should work (swaps internally)
|
|
144
|
+
const result = minkowskiSum(sph, cubeWithHole)
|
|
145
|
+
|
|
146
|
+
t.true(geom3.isA(result))
|
|
147
|
+
t.true(geom3.toPolygons(result).length > 0)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('minkowskiSum: throws for two non-convex geometries', (t) => {
|
|
151
|
+
const cube1 = cuboid({ size: [10, 10, 10] })
|
|
152
|
+
const hole1 = cuboid({ size: [4, 4, 20] })
|
|
153
|
+
const nonConvex1 = subtract(cube1, hole1)
|
|
154
|
+
|
|
155
|
+
const cube2 = cuboid({ size: [8, 8, 8] })
|
|
156
|
+
const hole2 = cuboid({ size: [3, 3, 16] })
|
|
157
|
+
const nonConvex2 = subtract(cube2, hole2)
|
|
158
|
+
|
|
159
|
+
t.throws(() => minkowskiSum(nonConvex1, nonConvex2), { message: /two non-convex/ })
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('minkowskiSum: torus + sphere preserves hole (face-local apex)', (t) => {
|
|
163
|
+
// Torus with innerRadius=3 (tube radius) and outerRadius=8 (distance to tube center)
|
|
164
|
+
// At z=0, the torus extends from radius 5 to 11 (8-3 to 8+3)
|
|
165
|
+
// Adding sphere of radius 1 should give 4 to 12
|
|
166
|
+
const torusShape = torus({
|
|
167
|
+
innerRadius: 3,
|
|
168
|
+
outerRadius: 8,
|
|
169
|
+
innerSegments: 16,
|
|
170
|
+
outerSegments: 24
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const sph = sphere({ radius: 1, segments: 8 })
|
|
174
|
+
|
|
175
|
+
t.false(geom3.isConvex(torusShape))
|
|
176
|
+
|
|
177
|
+
const result = minkowskiSum(torusShape, sph)
|
|
178
|
+
|
|
179
|
+
t.true(geom3.isA(result))
|
|
180
|
+
t.true(geom3.toPolygons(result).length > 0)
|
|
181
|
+
|
|
182
|
+
// Check that the hole is preserved by examining vertices at z≈0
|
|
183
|
+
const polygons = geom3.toPolygons(result)
|
|
184
|
+
let minRadius = Infinity
|
|
185
|
+
|
|
186
|
+
for (const poly of polygons) {
|
|
187
|
+
for (const v of poly.vertices) {
|
|
188
|
+
if (Math.abs(v[2]) < 0.5) {
|
|
189
|
+
const r = Math.sqrt(v[0] * v[0] + v[1] * v[1])
|
|
190
|
+
if (r < minRadius) minRadius = r
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// With face-local apex, hole should be preserved
|
|
196
|
+
// Inner radius should be around 4 (8-3-1 = 4)
|
|
197
|
+
// If centroid-based (buggy), hole would be filled and minRadius would be ~0
|
|
198
|
+
t.true(minRadius > 3, `hole should be preserved, got minRadius=${minRadius}`)
|
|
199
|
+
})
|
|
@@ -15,7 +15,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
15
15
|
|
|
16
16
|
// apply no modifications
|
|
17
17
|
let result = generalize({}, geometry1)
|
|
18
|
-
let pts = geom3.
|
|
18
|
+
let pts = geom3.toVertices(result)
|
|
19
19
|
let exp = [
|
|
20
20
|
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
21
21
|
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793], [-1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
|
|
@@ -35,7 +35,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
35
35
|
|
|
36
36
|
// apply snap only
|
|
37
37
|
result = generalize({ snap: true }, geometry1)
|
|
38
|
-
pts = geom3.
|
|
38
|
+
pts = geom3.toVertices(result)
|
|
39
39
|
exp = [
|
|
40
40
|
[[-1.5707910908071407, -0.7854138713607164, -3.1415821816142815], [-1.5707910908071407, -0.7854138713607164, 3.1415821816142815],
|
|
41
41
|
[-1.5707910908071407, 0.7854138713607164, 3.1415821816142815], [-1.5707910908071407, 0.7854138713607164, -3.1415821816142815]],
|
|
@@ -55,7 +55,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
55
55
|
|
|
56
56
|
// apply triangulate only
|
|
57
57
|
result = generalize({ triangulate: true }, geometry1)
|
|
58
|
-
pts = geom3.
|
|
58
|
+
pts = geom3.toVertices(result)
|
|
59
59
|
exp = [
|
|
60
60
|
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
61
61
|
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
@@ -89,7 +89,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
89
89
|
|
|
90
90
|
// apply simplify only (triangles => quads)
|
|
91
91
|
result = generalize({ simplify: true }, geometry2)
|
|
92
|
-
pts = geom3.
|
|
92
|
+
pts = geom3.toVertices(result)
|
|
93
93
|
exp = [
|
|
94
94
|
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
95
95
|
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793], [-1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
|
|
@@ -109,7 +109,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
109
109
|
})
|
|
110
110
|
|
|
111
111
|
test('generalize: generalize of a geom3 with T junctions produces an expected geom3', (t) => {
|
|
112
|
-
const geometry1 = geom3.
|
|
112
|
+
const geometry1 = geom3.fromVertices(
|
|
113
113
|
[
|
|
114
114
|
[[-1, -1, -1], [-1, -1, 1], [-1, 1, 1], [-1, 1, -1]],
|
|
115
115
|
[[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, -1, 1]],
|
|
@@ -132,7 +132,7 @@ test('generalize: generalize of a geom3 with T junctions produces an expected ge
|
|
|
132
132
|
)
|
|
133
133
|
|
|
134
134
|
const result = generalize({ snap: true, triangulate: true }, geometry1)
|
|
135
|
-
const pts = geom3.
|
|
135
|
+
const pts = geom3.toVertices(result)
|
|
136
136
|
const exp = [
|
|
137
137
|
[[-1, 0, 0.2], [-1, -1, -1], [-1, -1, 1]],
|
|
138
138
|
[[-1, 0, 0.2], [-1, -1, 1], [-1, 0, 1]],
|
|
@@ -11,7 +11,7 @@ import { insertTjunctions } from './insertTjunctions.js'
|
|
|
11
11
|
test('insertTjunctions: insertTjunctions produces expected polygons', (t) => {
|
|
12
12
|
const geometry1 = geom3.create()
|
|
13
13
|
const geometry2 = cuboid({ size: [2, 2, 2] })
|
|
14
|
-
const geometry3 = geom3.
|
|
14
|
+
const geometry3 = geom3.fromVertices(
|
|
15
15
|
[
|
|
16
16
|
[[-1, -1, -1], [-1, -1, 1], [-1, 1, 1], [-1, 1, -1]],
|
|
17
17
|
[[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, -1, 1]],
|
|
@@ -24,7 +24,7 @@ test('insertTjunctions: insertTjunctions produces expected polygons', (t) => {
|
|
|
24
24
|
[[-1, 1, 1], [-1, -1, 1], [0, 0, 1]]
|
|
25
25
|
]
|
|
26
26
|
)
|
|
27
|
-
const geometry4 = geom3.
|
|
27
|
+
const geometry4 = geom3.fromVertices(
|
|
28
28
|
[
|
|
29
29
|
[[-1, -1, -1], [-1, -1, 1], [-1, 1, 1], [-1, 1, -1]],
|
|
30
30
|
[[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, -1, 1]],
|
|
@@ -121,6 +121,7 @@ export const reTesselateCoplanarPolygons = (sourcePolygons) => {
|
|
|
121
121
|
// at the left and right side of the polygon
|
|
122
122
|
// Iterate over all polygons that have a corner at this y coordinate:
|
|
123
123
|
const polygonIndexesWithCorner = yCoordinateToPolygonIndexes.get(yCoordinate)
|
|
124
|
+
let removeCount = 0 // track removals to filter at end (avoids O(n²) splice)
|
|
124
125
|
for (let activePolygonIndex = 0; activePolygonIndex < activePolygons.length; ++activePolygonIndex) {
|
|
125
126
|
const activePolygon = activePolygons[activePolygonIndex]
|
|
126
127
|
const polygonIndex = activePolygon.polygonIndex
|
|
@@ -144,9 +145,9 @@ export const reTesselateCoplanarPolygons = (sourcePolygons) => {
|
|
|
144
145
|
}
|
|
145
146
|
if ((newLeftVertexIndex !== activePolygon.leftVertexIndex) && (newLeftVertexIndex === newRightVertexIndex)) {
|
|
146
147
|
// We have increased leftVertexIndex or decreased rightVertexIndex, and now they point to the same vertex
|
|
147
|
-
// This means that this is the bottom point of the polygon
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
// This means that this is the bottom point of the polygon, so Mark it for removal
|
|
149
|
+
activePolygon.remove = true
|
|
150
|
+
removeCount++
|
|
150
151
|
} else {
|
|
151
152
|
activePolygon.leftVertexIndex = newLeftVertexIndex
|
|
152
153
|
activePolygon.rightVertexIndex = newRightVertexIndex
|
|
@@ -161,6 +162,12 @@ export const reTesselateCoplanarPolygons = (sourcePolygons) => {
|
|
|
161
162
|
}
|
|
162
163
|
} // if polygon has corner here
|
|
163
164
|
} // for activePolygonIndex
|
|
165
|
+
|
|
166
|
+
// Filter out marked polygons in single pass (O(n) instead of O(n²) splice)
|
|
167
|
+
if (removeCount > 0) {
|
|
168
|
+
activePolygons = activePolygons.filter((p) => !p.remove)
|
|
169
|
+
}
|
|
170
|
+
|
|
164
171
|
let nextYcoordinate
|
|
165
172
|
if (yIndex >= yCoordinates.length - 1) {
|
|
166
173
|
// last row, all polygons must be finished here:
|
|
@@ -16,7 +16,7 @@ const rotatePoly3 = (angles, polygon) => {
|
|
|
16
16
|
return poly3.transform(matrix, polygon)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
test
|
|
19
|
+
test('retessellateCoplanarPolygons: should merge coplanar polygons', (t) => {
|
|
20
20
|
const polyA = poly3.create([[-5, -5, 0], [5, -5, 0], [5, 5, 0], [-5, 5, 0]])
|
|
21
21
|
const polyB = poly3.create([[5, -5, 0], [8, 0, 0], [5, 5, 0]])
|
|
22
22
|
const polyC = poly3.create([[-5, 5, 0], [-8, 0, 0], [-5, -5, 0]])
|
|
@@ -68,3 +68,38 @@ test.only('retessellateCoplanarPolygons: should merge coplanar polygons', (t) =>
|
|
|
68
68
|
obs = reTesselateCoplanarPolygons([polyH, polyI, polyJ, polyK, polyL])
|
|
69
69
|
t.is(obs.length, 1)
|
|
70
70
|
})
|
|
71
|
+
|
|
72
|
+
// Test for mark-and-filter optimization: multiple polygons that reach their
|
|
73
|
+
// bottom point at the same y-coordinate (triggering the removal code path)
|
|
74
|
+
test('retessellateCoplanarPolygons: should correctly handle multiple polygon removals', (t) => {
|
|
75
|
+
// Create multiple triangular polygons that all end at the same y-coordinate
|
|
76
|
+
// This exercises the mark-and-filter removal optimization
|
|
77
|
+
const poly1 = poly3.create([[0, 0, 0], [2, 0, 0], [1, 3, 0]]) // triangle pointing up
|
|
78
|
+
const poly2 = poly3.create([[3, 0, 0], [5, 0, 0], [4, 3, 0]]) // triangle pointing up
|
|
79
|
+
const poly3a = poly3.create([[6, 0, 0], [8, 0, 0], [7, 3, 0]]) // triangle pointing up
|
|
80
|
+
|
|
81
|
+
// These polygons share the same plane and have vertices at y=0 and y=3
|
|
82
|
+
// During retessellation, all three will be active and then removed at y=3
|
|
83
|
+
const obs = reTesselateCoplanarPolygons([poly1, poly2, poly3a])
|
|
84
|
+
|
|
85
|
+
// Each triangle should be preserved (they don't overlap)
|
|
86
|
+
t.is(obs.length, 3)
|
|
87
|
+
|
|
88
|
+
// Verify each polygon has 3 vertices (triangles)
|
|
89
|
+
obs.forEach((polygon) => {
|
|
90
|
+
t.is(polygon.vertices.length, 3)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// Test for mark-and-filter with overlapping polygons that get merged
|
|
95
|
+
test('retessellateCoplanarPolygons: should merge adjacent polygons with shared edges', (t) => {
|
|
96
|
+
// Two adjacent squares sharing an edge at x=5
|
|
97
|
+
const poly1 = poly3.create([[0, 0, 0], [5, 0, 0], [5, 5, 0], [0, 5, 0]])
|
|
98
|
+
const poly2 = poly3.create([[5, 0, 0], [10, 0, 0], [10, 5, 0], [5, 5, 0]])
|
|
99
|
+
|
|
100
|
+
const obs = reTesselateCoplanarPolygons([poly1, poly2])
|
|
101
|
+
|
|
102
|
+
// Should merge into a single rectangle
|
|
103
|
+
t.is(obs.length, 1)
|
|
104
|
+
t.is(obs[0].vertices.length, 4) // rectangle has 4 vertices
|
|
105
|
+
})
|
|
@@ -24,8 +24,10 @@ export const retessellate = (geometry) => {
|
|
|
24
24
|
const destPolygons = []
|
|
25
25
|
classified.forEach((group) => {
|
|
26
26
|
if (Array.isArray(group)) {
|
|
27
|
-
const
|
|
28
|
-
|
|
27
|
+
const coplanarPolygons = reTesselateCoplanarPolygons(group)
|
|
28
|
+
for (let i = 0; i < coplanarPolygons.length; i++) {
|
|
29
|
+
destPolygons.push(coplanarPolygons[i])
|
|
30
|
+
}
|
|
29
31
|
} else {
|
|
30
32
|
destPolygons.push(group)
|
|
31
33
|
}
|
|
@@ -82,15 +82,15 @@ test('geom3: retessellate() should create proper geometry from solid geometries'
|
|
|
82
82
|
[[10.0, -5.0, -5.0], [10.0, 5.0, -5.0], [10.0, 5.0, 5.0], [10.0, -5.0, 5.0]] // end
|
|
83
83
|
]
|
|
84
84
|
|
|
85
|
-
const obj1 = geom3.
|
|
86
|
-
const obj2 = geom3.
|
|
87
|
-
const obj3 = geom3.
|
|
88
|
-
const obj4 = geom3.
|
|
89
|
-
const obj5 = geom3.
|
|
85
|
+
const obj1 = geom3.fromVertices(box1)
|
|
86
|
+
const obj2 = geom3.fromVertices(box1.concat(box2)) // combined geometry
|
|
87
|
+
const obj3 = geom3.fromVertices(box3)
|
|
88
|
+
const obj4 = geom3.fromVertices(box4)
|
|
89
|
+
const obj5 = geom3.fromVertices(box5)
|
|
90
90
|
|
|
91
91
|
// one solid geometry
|
|
92
92
|
const ret1 = retessellate(obj1)
|
|
93
|
-
const pts1 = geom3.
|
|
93
|
+
const pts1 = geom3.toVertices(ret1)
|
|
94
94
|
const exp1 = [
|
|
95
95
|
[[-5, -5, -5], [-5, -5, 5], [-5, 5, 5], [-5, 5, -5]],
|
|
96
96
|
[[5, -5, -5], [5, 5, -5], [5, 5, 5], [5, -5, 5]],
|
|
@@ -103,7 +103,7 @@ test('geom3: retessellate() should create proper geometry from solid geometries'
|
|
|
103
103
|
|
|
104
104
|
// two non-overlapping geometries
|
|
105
105
|
const ret2 = retessellate(obj2)
|
|
106
|
-
const pts2 = geom3.
|
|
106
|
+
const pts2 = geom3.toVertices(ret2)
|
|
107
107
|
const exp2 = [
|
|
108
108
|
[[-5, -5, -5], [-5, -5, 5], [-5, 5, 5], [-5, 5, -5]],
|
|
109
109
|
[[5, -5, -5], [5, 5, -5], [5, 5, 5], [5, -5, 5]],
|
|
@@ -122,7 +122,7 @@ test('geom3: retessellate() should create proper geometry from solid geometries'
|
|
|
122
122
|
|
|
123
123
|
// two touching geometries (faces)
|
|
124
124
|
const ret3 = retessellate(obj3)
|
|
125
|
-
const pts3 = geom3.
|
|
125
|
+
const pts3 = geom3.toVertices(ret3)
|
|
126
126
|
const exp3 = [
|
|
127
127
|
[[-5, 5, 15], [-5, 5, -5], [-5, -5, -5], [-5, -5, 15]],
|
|
128
128
|
[[5, -5, 15], [5, -5, -5], [5, 5, -5], [5, 5, 15]],
|
|
@@ -135,7 +135,7 @@ test('geom3: retessellate() should create proper geometry from solid geometries'
|
|
|
135
135
|
|
|
136
136
|
// two overlapping geometries
|
|
137
137
|
const ret4 = retessellate(obj4)
|
|
138
|
-
const pts4 = geom3.
|
|
138
|
+
const pts4 = geom3.toVertices(ret4)
|
|
139
139
|
const exp4 = [
|
|
140
140
|
[[-5, -5, -5], [-5, -5, 5], [-5, 5, 5], [-5, 5, -5]],
|
|
141
141
|
[[-5, -5, -5], [5, -5, -5], [5, -5, 5], [-5, -5, 5]],
|
|
@@ -160,7 +160,7 @@ test('geom3: retessellate() should create proper geometry from solid geometries'
|
|
|
160
160
|
|
|
161
161
|
// coplanar polygons
|
|
162
162
|
const ret5 = retessellate(obj5)
|
|
163
|
-
const pts5 = geom3.
|
|
163
|
+
const pts5 = geom3.toVertices(ret5)
|
|
164
164
|
const exp5 = [
|
|
165
165
|
[[-5, -5, -5], [-5, -5, 5], [-5, 5, 5], [-5, 5, -5]],
|
|
166
166
|
[[10, -5, -5], [10, -5, 5], [-5, -5, 5], [-5, -5, -5]],
|
|
@@ -25,31 +25,40 @@ test('snap: snap of a path2 produces an expected path2', (t) => {
|
|
|
25
25
|
|
|
26
26
|
pts = path2.toPoints(results[1])
|
|
27
27
|
exp = [
|
|
28
|
-
[0.5, 0],
|
|
29
|
-
[0.
|
|
30
|
-
[
|
|
31
|
-
[-0.
|
|
32
|
-
[0.
|
|
28
|
+
[0.5, 0],
|
|
29
|
+
[0.35355000000000003, 0.35355000000000003],
|
|
30
|
+
[0, 0.5],
|
|
31
|
+
[-0.35355000000000003, 0.35355000000000003],
|
|
32
|
+
[-0.5, 0],
|
|
33
|
+
[-0.35355000000000003, -0.35355000000000003],
|
|
34
|
+
[0, -0.5],
|
|
35
|
+
[0.35355000000000003, -0.35355000000000003]
|
|
33
36
|
]
|
|
34
37
|
t.true(comparePoints(pts, exp))
|
|
35
38
|
|
|
36
39
|
pts = path2.toPoints(results[2])
|
|
37
40
|
exp = [
|
|
38
|
-
[0.6666666666666666, 0],
|
|
39
|
-
[0.
|
|
40
|
-
[
|
|
41
|
-
[-0.
|
|
42
|
-
[0.
|
|
41
|
+
[0.6666666666666666, 0],
|
|
42
|
+
[0.4714, 0.4714],
|
|
43
|
+
[0, 0.6666666666666666],
|
|
44
|
+
[-0.4714, 0.4714],
|
|
45
|
+
[-0.6666666666666666, 0],
|
|
46
|
+
[-0.4714, -0.4714],
|
|
47
|
+
[0, -0.6666666666666666],
|
|
48
|
+
[0.4714, -0.4714]
|
|
43
49
|
]
|
|
44
50
|
t.true(comparePoints(pts, exp))
|
|
45
51
|
|
|
46
52
|
pts = path2.toPoints(results[3])
|
|
47
53
|
exp = [
|
|
48
|
-
[1570.
|
|
49
|
-
[
|
|
50
|
-
[
|
|
51
|
-
[-
|
|
52
|
-
[
|
|
54
|
+
[1570.7963267948967, 0],
|
|
55
|
+
[1110.7100826766714, 1110.7100826766714],
|
|
56
|
+
[0, 1570.7963267948967],
|
|
57
|
+
[-1110.7100826766714, 1110.7100826766714],
|
|
58
|
+
[-1570.7963267948967, 0],
|
|
59
|
+
[-1110.7100826766714, -1110.7100826766714],
|
|
60
|
+
[0, -1570.7963267948967],
|
|
61
|
+
[1110.7100826766714, -1110.7100826766714]
|
|
53
62
|
]
|
|
54
63
|
t.true(comparePoints(pts, exp))
|
|
55
64
|
})
|
|
@@ -117,11 +126,11 @@ test('snap: snap of a geom3 produces an expected geom3', (t) => {
|
|
|
117
126
|
const results = snap(geometry1, geometry2, geometry3, geometry4)
|
|
118
127
|
t.is(results.length, 4)
|
|
119
128
|
|
|
120
|
-
let pts = geom3.
|
|
129
|
+
let pts = geom3.toVertices(results[0])
|
|
121
130
|
let exp = []
|
|
122
131
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
123
132
|
|
|
124
|
-
pts = geom3.
|
|
133
|
+
pts = geom3.toVertices(results[1])
|
|
125
134
|
exp = [
|
|
126
135
|
[[-0.5, -0.5, -0.5], [-0.5, -0.5, 0.5], [-0.5, 0.5, 0.5], [-0.5, 0.5, -0.5]],
|
|
127
136
|
[[0.5, -0.5, -0.5], [0.5, 0.5, -0.5], [0.5, 0.5, 0.5], [0.5, -0.5, 0.5]],
|
|
@@ -132,7 +141,7 @@ test('snap: snap of a geom3 produces an expected geom3', (t) => {
|
|
|
132
141
|
]
|
|
133
142
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
134
143
|
|
|
135
|
-
pts = geom3.
|
|
144
|
+
pts = geom3.toVertices(results[2])
|
|
136
145
|
exp = [
|
|
137
146
|
[[-0.6666666666666667, -0.6666666666666667, -0.6666666666666667], [-0.6666666666666667, -0.6666666666666667, 0.6666666666666667],
|
|
138
147
|
[-0.6666666666666667, 0.6666666666666667, 0.6666666666666667], [-0.6666666666666667, 0.6666666666666667, -0.6666666666666667]],
|
|
@@ -149,7 +158,7 @@ test('snap: snap of a geom3 produces an expected geom3', (t) => {
|
|
|
149
158
|
]
|
|
150
159
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
151
160
|
|
|
152
|
-
pts = geom3.
|
|
161
|
+
pts = geom3.toVertices(results[3])
|
|
153
162
|
exp = [
|
|
154
163
|
[[-1570.7963267948967, -1570.7963267948967, -1570.7963267948967], [-1570.7963267948967, -1570.7963267948967, 1570.7963267948967],
|
|
155
164
|
[-1570.7963267948967, 1570.7963267948967, 1570.7963267948967], [-1570.7963267948967, 1570.7963267948967, -1570.7963267948967]],
|
|
@@ -19,7 +19,7 @@ test('offset: offset empty geom3', (t) => {
|
|
|
19
19
|
t.is(measureArea(result), 0)
|
|
20
20
|
t.is(measureVolume(result), 0)
|
|
21
21
|
t.is(geom3.toPolygons(result).length, 0)
|
|
22
|
-
t.is(geom3.
|
|
22
|
+
t.is(geom3.toVertices(result).length, 0)
|
|
23
23
|
})
|
|
24
24
|
|
|
25
25
|
test('offset: offset geom3 preserves color', (t) => {
|
|
@@ -37,10 +37,10 @@ test('offset: offset of a geom3 produces expected changes to polygons', (t) => {
|
|
|
37
37
|
[[-5, -5, -5], [-5, 15, -5], [15, 15, -5], [15, -5, -5]],
|
|
38
38
|
[[-5, -5, 15], [15, -5, 15], [15, 15, 15], [-5, 15, 15]]
|
|
39
39
|
]
|
|
40
|
-
const geometry = geom3.
|
|
40
|
+
const geometry = geom3.fromVertices(polygonsAsPoints)
|
|
41
41
|
|
|
42
42
|
const obs = offset({ delta: 2, corners: 'round', segments: 8 }, geometry)
|
|
43
|
-
const pts = geom3.
|
|
43
|
+
const pts = geom3.toVertices(obs)
|
|
44
44
|
const exp0 = [
|
|
45
45
|
[-7, -5, -5],
|
|
46
46
|
[-7, -5, 15],
|
|
@@ -62,15 +62,14 @@ test('offset: offset of a geom3 produces expected changes to polygons', (t) => {
|
|
|
62
62
|
|
|
63
63
|
const geometry2 = sphere({ radius: 5, segments: 8 })
|
|
64
64
|
const obs2 = offset({ delta: 5 }, geometry2)
|
|
65
|
-
const pts2 = geom3.
|
|
65
|
+
const pts2 = geom3.toVertices(obs2)
|
|
66
66
|
t.notThrows.skip(() => geom3.validate(obs2))
|
|
67
67
|
t.is(measureArea(obs), 3178.8059464475555)
|
|
68
68
|
t.is(measureVolume(obs), 13504.574121271067)
|
|
69
69
|
t.is(pts2.length, 864)
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
test('offsetGeom3: offset completes properly, issue 876',
|
|
73
|
-
setTimeout(() => t.fail(), 1000)
|
|
72
|
+
test('offsetGeom3: offset completes properly, issue 876', (t) => {
|
|
74
73
|
const polies = [
|
|
75
74
|
poly3.create([[-19.61, -0.7999999999999986, 11.855], [-19.61, -0.8000000000000015, -11.855], [-19.61, -2.7500000000000018, -11.855], [-19.61, -2.7499999999999982, 11.855]]),
|
|
76
75
|
poly3.create([[-17.32, -2.75, 10], [-17.32, -2.7500000000000013, -10], [-17.32, -0.8000000000000014, -10], [-17.32, -0.7999999999999987, 10]]),
|
|
@@ -90,9 +89,8 @@ test('offsetGeom3: offset completes properly, issue 876', async (t) => {
|
|
|
90
89
|
|
|
91
90
|
const sub = geom3.create(polies)
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
})
|
|
92
|
+
const obs = offset({ delta: 1.3, corners: 'round', segments: 12 }, sub)
|
|
93
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
94
|
+
t.is(measureArea(obs), 524.9674760547548)
|
|
95
|
+
t.is(measureVolume(obs), 604.0599465573156)
|
|
98
96
|
})
|
|
@@ -53,11 +53,11 @@ test('center: centering of a geom3 produces expected changes to polygons', (t) =
|
|
|
53
53
|
[[-2, -7, -12], [-2, 13, -12], [8, 13, -12], [8, -7, -12]],
|
|
54
54
|
[[-2, -7, 18], [8, -7, 18], [8, 13, 18], [-2, 13, 18]]
|
|
55
55
|
]
|
|
56
|
-
const geometry = geom3.
|
|
56
|
+
const geometry = geom3.fromVertices(points)
|
|
57
57
|
|
|
58
58
|
// center about X
|
|
59
59
|
let centered = center({ axes: [true, false, false] }, geometry)
|
|
60
|
-
let pts = geom3.
|
|
60
|
+
let pts = geom3.toVertices(centered)
|
|
61
61
|
let exp = [
|
|
62
62
|
[[-5, -7, -12], [-5, -7, 18], [-5, 13, 18], [-5, 13, -12]],
|
|
63
63
|
[[5, -7, -12], [5, 13, -12], [5, 13, 18], [5, -7, 18]],
|
|
@@ -71,14 +71,14 @@ test('center: centering of a geom3 produces expected changes to polygons', (t) =
|
|
|
71
71
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
72
72
|
|
|
73
73
|
centered = centerX(geometry)
|
|
74
|
-
pts = geom3.
|
|
74
|
+
pts = geom3.toVertices(centered)
|
|
75
75
|
t.notThrows(() => geom3.validate(centered))
|
|
76
76
|
t.is(measureVolume(centered), measureVolume(geometry))
|
|
77
77
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
78
78
|
|
|
79
79
|
// center about Y
|
|
80
80
|
centered = center({ axes: [false, true, false] }, geometry)
|
|
81
|
-
pts = geom3.
|
|
81
|
+
pts = geom3.toVertices(centered)
|
|
82
82
|
exp = [
|
|
83
83
|
[[-2, -10, -12], [-2, -10, 18], [-2, 10, 18], [-2, 10, -12]],
|
|
84
84
|
[[8, -10, -12], [8, 10, -12], [8, 10, 18], [8, -10, 18]],
|
|
@@ -92,14 +92,14 @@ test('center: centering of a geom3 produces expected changes to polygons', (t) =
|
|
|
92
92
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
93
93
|
|
|
94
94
|
centered = centerY(geometry)
|
|
95
|
-
pts = geom3.
|
|
95
|
+
pts = geom3.toVertices(centered)
|
|
96
96
|
t.notThrows(() => geom3.validate(centered))
|
|
97
97
|
t.is(measureVolume(centered), measureVolume(geometry))
|
|
98
98
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
99
99
|
|
|
100
100
|
// center about Z
|
|
101
101
|
centered = center({ axes: [false, false, true] }, geometry)
|
|
102
|
-
pts = geom3.
|
|
102
|
+
pts = geom3.toVertices(centered)
|
|
103
103
|
exp = [
|
|
104
104
|
[[-2, -7, -15], [-2, -7, 15], [-2, 13, 15], [-2, 13, -15]],
|
|
105
105
|
[[8, -7, -15], [8, 13, -15], [8, 13, 15], [8, -7, 15]],
|
|
@@ -113,7 +113,7 @@ test('center: centering of a geom3 produces expected changes to polygons', (t) =
|
|
|
113
113
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
114
114
|
|
|
115
115
|
centered = centerZ(geometry)
|
|
116
|
-
pts = geom3.
|
|
116
|
+
pts = geom3.toVertices(centered)
|
|
117
117
|
t.notThrows(() => geom3.validate(centered))
|
|
118
118
|
t.is(measureVolume(centered), measureVolume(geometry))
|
|
119
119
|
t.true(comparePolygonsAsPoints(pts, exp))
|