@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
|
@@ -6,13 +6,14 @@ import { isA } from './isA.js'
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Determine if the given object is a valid slice.
|
|
9
|
+
*
|
|
9
10
|
* Checks for valid data points.
|
|
10
11
|
*
|
|
11
|
-
* **If the geometry is not valid, an exception will be thrown with details of the geometry error.**
|
|
12
|
+
* **NOTE: If the geometry is not valid, an exception will be thrown with details of the geometry error.**
|
|
12
13
|
*
|
|
13
14
|
* @param {object} object - the object to interrogate
|
|
14
15
|
* @throws {Error} error if the geometry is not valid
|
|
15
|
-
* @alias module:modeling/
|
|
16
|
+
* @alias module:modeling/slice.validate
|
|
16
17
|
*/
|
|
17
18
|
export const validate = (object) => {
|
|
18
19
|
if (!isA(object)) {
|
package/src/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { degToRad, flatten, radiusToSegments, radToDeg } from './utils'
|
|
|
10
10
|
export * as booleans from './operations/booleans'
|
|
11
11
|
export * as extrusions from './operations/extrusions'
|
|
12
12
|
export * as hulls from './operations/hulls'
|
|
13
|
+
export * as minkowski from './operations/minkowski'
|
|
13
14
|
export * as modifiers from './operations/modifiers'
|
|
14
15
|
export * as offsets from './operations/offsets'
|
|
15
16
|
export * as transforms from './operations/transforms'
|
package/src/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module modeling
|
|
3
|
+
*/
|
|
1
4
|
export * from './colors/index.js'
|
|
2
5
|
export * from './curves/index.js'
|
|
3
6
|
export * from './geometries/index.js'
|
|
@@ -10,6 +13,7 @@ export { degToRad, flatten, radiusToSegments, radToDeg } from './utils/index.js'
|
|
|
10
13
|
export * from './operations/booleans/index.js'
|
|
11
14
|
export * from './operations/extrusions/index.js'
|
|
12
15
|
export * from './operations/hulls/index.js'
|
|
16
|
+
export * from './operations/minkowski/index.js'
|
|
13
17
|
export * from './operations/modifiers/index.js'
|
|
14
18
|
export * from './operations/offsets/index.js'
|
|
15
19
|
export * from './operations/transforms/index.js'
|
package/src/maths/constants.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Epsilon used during determination of near zero distances.
|
|
3
|
-
*
|
|
4
|
-
* @
|
|
3
|
+
*
|
|
4
|
+
* @constant
|
|
5
5
|
* @alias module:modeling/maths.EPS
|
|
6
6
|
* @example
|
|
7
|
-
*
|
|
7
|
+
* import { EPS } from '@jscad/modeling'
|
|
8
8
|
*/
|
|
9
9
|
export const EPS = 1e-5
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Smaller epsilon used for measuring near zero distances.
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
|
+
* @constant
|
|
15
|
+
* @static
|
|
14
16
|
* @alias module:modeling/maths.NEPS
|
|
15
17
|
* @example
|
|
16
|
-
*
|
|
18
|
+
* import { NEPS } from '@jscad/modeling'
|
|
17
19
|
*/
|
|
18
20
|
export const NEPS = 1e-13
|
|
19
21
|
// NEPS is derived from a series of tests to determine the optimal precision
|
|
@@ -22,10 +24,12 @@ export const NEPS = 1e-13
|
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* The TAU property represents the ratio of the circumference of a circle to its radius.
|
|
27
|
+
*
|
|
25
28
|
* Approximately 6.28318530717958647692
|
|
29
|
+
*
|
|
30
|
+
* @constant
|
|
26
31
|
* @alias module:modeling/maths.TAU
|
|
27
|
-
* @default
|
|
28
32
|
* @example
|
|
29
|
-
*
|
|
33
|
+
* import { TAU } from '@jscad/modeling'
|
|
30
34
|
*/
|
|
31
35
|
export const TAU = Math.PI * 2
|
package/src/maths/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Maths are computational units for fundamental Euclidean geometry. All maths operate upon array data structures.
|
|
3
|
+
*
|
|
3
4
|
* Note: Maths data structures are considered immutable, so never change the contents directly.
|
|
4
5
|
* @see Most computations are based upon the glMatrix library (glmatrix.net)
|
|
5
6
|
* @module modeling/maths
|
|
6
7
|
* @example
|
|
7
|
-
* import {
|
|
8
|
+
* import { TAU, line2, line3, mat4, plane, utils, vec2, vec3, vec4 } from '@jscad/modeling'
|
|
8
9
|
*/
|
|
9
10
|
export * from './constants.js'
|
|
10
11
|
export * as line2 from './line2/index.js'
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* All shapes (primitives or the results of operations) can be passed to boolean functions
|
|
3
3
|
* to perform logical operations, e.g. remove a hole from a board.
|
|
4
|
+
*
|
|
4
5
|
* In all cases, the function returns the results, and never changes the original shapes.
|
|
5
6
|
* @module modeling/booleans
|
|
7
|
+
*
|
|
6
8
|
* @example
|
|
7
9
|
* import { intersect, scission, subtract, union } from '@jscad/modeling'
|
|
8
10
|
*/
|
|
@@ -6,7 +6,6 @@ import * as geom3 from '../../geometries/geom3/index.js'
|
|
|
6
6
|
|
|
7
7
|
import { intersectGeom2 } from './intersectGeom2.js'
|
|
8
8
|
import { intersectGeom3 } from './intersectGeom3.js'
|
|
9
|
-
|
|
10
9
|
/**
|
|
11
10
|
* Return a new geometry representing space in both the first geometry and
|
|
12
11
|
* all subsequent geometries.
|
|
@@ -17,7 +17,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
|
|
|
17
17
|
|
|
18
18
|
// intersect of one object
|
|
19
19
|
const result1 = intersect(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('intersect: intersect of one or more geom3 objects produces expected geomet
|
|
|
78
78
|
const geometry2 = center({ relativeTo: [10, 10, 10] }, cuboid({ size: [4, 4, 4] }))
|
|
79
79
|
|
|
80
80
|
const result2 = intersect(geometry1, geometry2)
|
|
81
|
-
obs = geom3.
|
|
81
|
+
obs = geom3.toVertices(result2)
|
|
82
82
|
t.notThrows(() => geom3.validate(result2))
|
|
83
83
|
t.is(measureArea(result2), 0)
|
|
84
84
|
t.is(measureVolume(result2), 0)
|
|
@@ -88,7 +88,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
|
|
|
88
88
|
const geometry3 = cuboid({ size: [18, 18, 18] })
|
|
89
89
|
|
|
90
90
|
const result3 = intersect(geometry2, geometry3)
|
|
91
|
-
obs = geom3.
|
|
91
|
+
obs = geom3.toVertices(result3)
|
|
92
92
|
|
|
93
93
|
// the order changes based on the best plane chosen in Node.js
|
|
94
94
|
exp = [
|
|
@@ -108,7 +108,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
|
|
|
108
108
|
|
|
109
109
|
// intersect of two completely overlapping objects
|
|
110
110
|
const result4 = intersect(geometry1, geometry3)
|
|
111
|
-
obs = geom3.
|
|
111
|
+
obs = geom3.toVertices(result4)
|
|
112
112
|
t.notThrows.skip(() => geom3.validate(result4))
|
|
113
113
|
t.is(measureArea(result4), 44.053756306589825)
|
|
114
114
|
t.is(measureVolume(result4), 25.751611331979678)
|
|
@@ -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)
|
|
@@ -4,10 +4,7 @@ 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
6
|
|
|
7
|
-
Number.isNaN(lambda) ? lambda = 0
|
|
8
|
-
: lambda > 1 ? lambda = 1
|
|
9
|
-
: lambda < 0 ? lambda = 0
|
|
10
|
-
: lambda
|
|
7
|
+
Number.isNaN(lambda) ? lambda = 0 : lambda > 1 ? lambda = 1 : lambda < 0 ? lambda = 0 : lambda
|
|
11
8
|
|
|
12
9
|
vec3.scale(direction, direction, lambda)
|
|
13
10
|
vec3.add(direction, p1, direction)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import test from 'ava'
|
|
2
|
+
|
|
3
|
+
import { poly3 } from '../../../geometries/index.js'
|
|
4
|
+
import { plane } from '../../../maths/index.js'
|
|
5
|
+
|
|
6
|
+
import { splitPolygonByPlane } from './splitPolygonByPlane.js'
|
|
7
|
+
|
|
8
|
+
test('splitPolygonByPlane: test coplanar-front polygon returns type 0.', (t) => {
|
|
9
|
+
// Polygon in XY plane at z=0
|
|
10
|
+
const polygon = poly3.create([[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]])
|
|
11
|
+
// Plane is also XY plane at z=0, normal pointing up
|
|
12
|
+
const splane = plane.fromPoints(plane.create(), [0, 0, 0], [1, 0, 0], [1, 1, 0])
|
|
13
|
+
|
|
14
|
+
const result = {}
|
|
15
|
+
splitPolygonByPlane(result, splane, polygon)
|
|
16
|
+
t.is(result.type, 0) // coplanar-front
|
|
17
|
+
t.is(result.front, undefined)
|
|
18
|
+
t.is(result.back, undefined)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('splitPolygonByPlane: test polygon entirely in front returns type 2.', (t) => {
|
|
22
|
+
// Polygon at z=5
|
|
23
|
+
const polygon = poly3.create([[0, 0, 5], [1, 0, 5], [1, 1, 5], [0, 1, 5]])
|
|
24
|
+
// Plane at z=0
|
|
25
|
+
const splane = [0, 0, 1, 0] // normal (0,0,1), w=0
|
|
26
|
+
|
|
27
|
+
const result = {}
|
|
28
|
+
splitPolygonByPlane(result, splane, polygon)
|
|
29
|
+
t.is(result.type, 2) // front
|
|
30
|
+
t.is(result.front, undefined)
|
|
31
|
+
t.is(result.back, undefined)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('splitPolygonByPlane: test polygon entirely in back returns type 3.', (t) => {
|
|
35
|
+
// Polygon at z=-5
|
|
36
|
+
const polygon = poly3.create([[0, 0, -5], [1, 0, -5], [1, 1, -5], [0, 1, -5]])
|
|
37
|
+
// Plane at z=0
|
|
38
|
+
const splane = [0, 0, 1, 0] // normal (0,0,1), w=0
|
|
39
|
+
|
|
40
|
+
const result = {}
|
|
41
|
+
splitPolygonByPlane(result, splane, polygon)
|
|
42
|
+
t.is(result.type, 3) // back
|
|
43
|
+
t.is(result.front, undefined)
|
|
44
|
+
t.is(result.back, undefined)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('splitPolygonByPlane: test spanning polygon returns type 4 with front and back.', (t) => {
|
|
48
|
+
// Polygon spanning z=0 plane (from z=-1 to z=1)
|
|
49
|
+
const polygon = poly3.create([[0, 0, -1], [1, 0, -1], [1, 0, 1], [0, 0, 1]])
|
|
50
|
+
// Plane at z=0
|
|
51
|
+
const splane = [0, 0, 1, 0] // normal (0,0,1), w=0
|
|
52
|
+
|
|
53
|
+
const result = {}
|
|
54
|
+
splitPolygonByPlane(result, splane, polygon)
|
|
55
|
+
t.is(result.type, 4) // spanning
|
|
56
|
+
t.not(result.front, undefined)
|
|
57
|
+
t.not(result.back, undefined)
|
|
58
|
+
|
|
59
|
+
// Front polygon should have z >= 0
|
|
60
|
+
const frontPoints = poly3.toVertices(result.front)
|
|
61
|
+
t.true(frontPoints.length >= 3)
|
|
62
|
+
frontPoints.forEach((p) => {
|
|
63
|
+
t.true(p[2] >= -1e-5, `front point z=${p[2]} should be >= 0`)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Back polygon should have z <= 0
|
|
67
|
+
const backPoints = poly3.toVertices(result.back)
|
|
68
|
+
t.true(backPoints.length >= 3)
|
|
69
|
+
backPoints.forEach((p) => {
|
|
70
|
+
t.true(p[2] <= 1e-5, `back point z=${p[2]} should be <= 0`)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('splitPolygonByPlane: test duplicate vertices are removed from split result.', (t) => {
|
|
75
|
+
// Create a polygon that when split would produce duplicate vertices
|
|
76
|
+
// Triangle with one vertex on the plane
|
|
77
|
+
const polygon = poly3.create([[0, 0, 0], [1, 0, 1], [1, 0, -1]])
|
|
78
|
+
// Plane at z=0
|
|
79
|
+
const splane = [0, 0, 1, 0]
|
|
80
|
+
|
|
81
|
+
const result = {}
|
|
82
|
+
splitPolygonByPlane(result, splane, polygon)
|
|
83
|
+
t.is(result.type, 4) // spanning
|
|
84
|
+
|
|
85
|
+
// Verify no consecutive duplicate vertices in front
|
|
86
|
+
if (result.front) {
|
|
87
|
+
const frontPoints = poly3.toVertices(result.front)
|
|
88
|
+
for (let i = 0; i < frontPoints.length; i++) {
|
|
89
|
+
const curr = frontPoints[i]
|
|
90
|
+
const next = frontPoints[(i + 1) % frontPoints.length]
|
|
91
|
+
const dx = curr[0] - next[0]
|
|
92
|
+
const dy = curr[1] - next[1]
|
|
93
|
+
const dz = curr[2] - next[2]
|
|
94
|
+
const distSq = dx * dx + dy * dy + dz * dz
|
|
95
|
+
t.true(distSq > 1e-10, 'front polygon should not have duplicate consecutive vertices')
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Verify no consecutive duplicate vertices in back
|
|
100
|
+
if (result.back) {
|
|
101
|
+
const backPoints = poly3.toVertices(result.back)
|
|
102
|
+
for (let i = 0; i < backPoints.length; i++) {
|
|
103
|
+
const curr = backPoints[i]
|
|
104
|
+
const next = backPoints[(i + 1) % backPoints.length]
|
|
105
|
+
const dx = curr[0] - next[0]
|
|
106
|
+
const dy = curr[1] - next[1]
|
|
107
|
+
const dz = curr[2] - next[2]
|
|
108
|
+
const distSq = dx * dx + dy * dy + dz * dz
|
|
109
|
+
t.true(distSq > 1e-10, 'back polygon should not have duplicate consecutive vertices')
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('splitPolygonByPlane: test complex spanning polygon splits correctly.', (t) => {
|
|
115
|
+
// Hexagon spanning the XY plane
|
|
116
|
+
const polygon = poly3.create([
|
|
117
|
+
[1, 0, -1],
|
|
118
|
+
[0.5, 0.866, -1],
|
|
119
|
+
[-0.5, 0.866, 1],
|
|
120
|
+
[-1, 0, 1],
|
|
121
|
+
[-0.5, -0.866, 1],
|
|
122
|
+
[0.5, -0.866, -1]
|
|
123
|
+
])
|
|
124
|
+
// Plane at z=0
|
|
125
|
+
const splane = [0, 0, 1, 0]
|
|
126
|
+
|
|
127
|
+
const result = {}
|
|
128
|
+
splitPolygonByPlane(result, splane, polygon)
|
|
129
|
+
t.is(result.type, 4) // spanning
|
|
130
|
+
t.not(result.front, undefined)
|
|
131
|
+
t.not(result.back, undefined)
|
|
132
|
+
|
|
133
|
+
// Both resulting polygons should be valid (at least 3 vertices)
|
|
134
|
+
const frontPoints = poly3.toVertices(result.front)
|
|
135
|
+
const backPoints = poly3.toVertices(result.back)
|
|
136
|
+
t.true(frontPoints.length >= 3, 'front polygon should have at least 3 vertices')
|
|
137
|
+
t.true(backPoints.length >= 3, 'back polygon should have at least 3 vertices')
|
|
138
|
+
})
|
|
@@ -17,7 +17,7 @@ test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
17
17
|
|
|
18
18
|
// union of one object
|
|
19
19
|
const result1 = union(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('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
78
78
|
const geometry2 = center({ relativeTo: [10, 10, 10] }, cuboid({ size: [4, 4, 4] }))
|
|
79
79
|
|
|
80
80
|
const result2 = union(geometry1, geometry2)
|
|
81
|
-
obs = geom3.
|
|
81
|
+
obs = geom3.toVertices(result2)
|
|
82
82
|
t.notThrows.skip(() => geom3.validate(result2))
|
|
83
83
|
t.is(measureArea(result2), 140.05375630658983)
|
|
84
84
|
t.is(measureVolume(result2), 89.75161133197969)
|
|
@@ -88,7 +88,7 @@ test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
88
88
|
const geometry3 = cuboid({ size: [18, 18, 18] })
|
|
89
89
|
|
|
90
90
|
const result3 = union(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]],
|
|
@@ -117,7 +117,7 @@ test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
117
117
|
|
|
118
118
|
// union of two completely overlapping objects
|
|
119
119
|
const result4 = union(geometry1, geometry3)
|
|
120
|
-
obs = geom3.
|
|
120
|
+
obs = geom3.toVertices(result4)
|
|
121
121
|
exp = [
|
|
122
122
|
[[-9, -9, -9], [-9, -9, 9], [-9, 9, 9], [-9, 9, -9]],
|
|
123
123
|
[[9, -9, -9], [9, 9, -9], [9, 9, 9], [9, -9, 9]],
|
|
@@ -138,9 +138,44 @@ test('union of geom3 with rounding issues #137', (t) => {
|
|
|
138
138
|
const geometry2 = center({ relativeTo: [0, 0, -4.400001] }, cuboid({ size: [44, 26, 1.8] })) // introduce precision error
|
|
139
139
|
|
|
140
140
|
const result = union(geometry1, geometry2)
|
|
141
|
-
const pts = geom3.
|
|
141
|
+
const pts = geom3.toVertices(result)
|
|
142
142
|
t.notThrows(() => geom3.validate(result))
|
|
143
143
|
t.is(measureArea(result), 3240.00014)
|
|
144
144
|
t.is(measureVolume(result), 7779.201144000001)
|
|
145
145
|
t.is(pts.length, 6) // number of polygons in union
|
|
146
146
|
})
|
|
147
|
+
|
|
148
|
+
// Test for push loop optimization: verify union works correctly with multiple geometries
|
|
149
|
+
// This ensures the concat-to-push-loop optimization handles array merging properly
|
|
150
|
+
test('union of geom3 with multiple overlapping geometries', (t) => {
|
|
151
|
+
// Create several overlapping cuboids to generate a complex union
|
|
152
|
+
const geometry1 = cuboid({ size: [10, 10, 10] })
|
|
153
|
+
const geometry2 = center({ relativeTo: [5, 0, 0] }, cuboid({ size: [10, 10, 10] }))
|
|
154
|
+
const geometry3 = center({ relativeTo: [0, 5, 0] }, cuboid({ size: [10, 10, 10] }))
|
|
155
|
+
|
|
156
|
+
// Union should work correctly with multiple geometries
|
|
157
|
+
const obs = union(geometry1, geometry2, geometry3)
|
|
158
|
+
const pts = geom3.toVertices(obs)
|
|
159
|
+
|
|
160
|
+
// Skip manifold validation - focus on testing polygon merging works correctly
|
|
161
|
+
// (CSG on overlapping boxes can produce non-manifold edges at coplanar faces)
|
|
162
|
+
// Should produce a merged geometry with polygons from all inputs
|
|
163
|
+
t.true(pts.length > 6) // more than a single cube
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
// Test for push loop optimization: verify non-overlapping geometries combine correctly
|
|
167
|
+
test('union of multiple non-overlapping geom3 preserves all polygons', (t) => {
|
|
168
|
+
// Create multiple small cuboids that don't overlap
|
|
169
|
+
const cubes = []
|
|
170
|
+
for (let i = 0; i < 10; i++) {
|
|
171
|
+
cubes.push(center({ relativeTo: [i * 5, 0, 0] }, cuboid({ size: [2, 2, 2] })))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Union all of them
|
|
175
|
+
const obs = union(...cubes)
|
|
176
|
+
const pts = geom3.toVertices(obs)
|
|
177
|
+
|
|
178
|
+
t.notThrows(() => geom3.validate(obs))
|
|
179
|
+
// Each cuboid has 6 faces, so 10 cuboids = 60 polygons
|
|
180
|
+
t.is(pts.length, 60)
|
|
181
|
+
})
|
|
@@ -59,7 +59,7 @@ export const extrudeFromSlices = (options, base) => {
|
|
|
59
59
|
let startSlice = null
|
|
60
60
|
let endSlice = null
|
|
61
61
|
let prevSlice = null
|
|
62
|
-
|
|
62
|
+
const polygons = []
|
|
63
63
|
for (let s = 0; s < numberOfSlices; s++) {
|
|
64
64
|
// invoke the callback function to get the next slice
|
|
65
65
|
// NOTE: callback can return null to skip the slice
|
|
@@ -71,7 +71,10 @@ export const extrudeFromSlices = (options, base) => {
|
|
|
71
71
|
if (currentSlice.contours.length === 0) throw new Error('the callback function must return slices with one or more contours')
|
|
72
72
|
|
|
73
73
|
if (prevSlice) {
|
|
74
|
-
|
|
74
|
+
const walls = extrudeWalls(prevSlice, currentSlice)
|
|
75
|
+
for (let i = 0; i < walls.length; i++) {
|
|
76
|
+
polygons.push(walls[i])
|
|
77
|
+
}
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
// save start and end slices for caps if necessary
|
|
@@ -85,17 +88,24 @@ export const extrudeFromSlices = (options, base) => {
|
|
|
85
88
|
if (capEnd) {
|
|
86
89
|
// create a cap at the end
|
|
87
90
|
const endPolygons = slice.toPolygons(endSlice)
|
|
88
|
-
|
|
91
|
+
for (let i = 0; i < endPolygons.length; i++) {
|
|
92
|
+
polygons.push(endPolygons[i])
|
|
93
|
+
}
|
|
89
94
|
}
|
|
90
95
|
if (capStart) {
|
|
91
96
|
// create a cap at the start
|
|
92
97
|
const startPolygons = slice.toPolygons(startSlice).map(poly3.invert)
|
|
93
|
-
|
|
98
|
+
for (let i = 0; i < startPolygons.length; i++) {
|
|
99
|
+
polygons.push(startPolygons[i])
|
|
100
|
+
}
|
|
94
101
|
}
|
|
95
102
|
if (!capStart && !capEnd) {
|
|
96
103
|
// create walls between end and start slices
|
|
97
104
|
if (close && !slice.equals(endSlice, startSlice)) {
|
|
98
|
-
|
|
105
|
+
const walls = extrudeWalls(endSlice, startSlice)
|
|
106
|
+
for (let i = 0; i < walls.length; i++) {
|
|
107
|
+
polygons.push(walls[i])
|
|
108
|
+
}
|
|
99
109
|
}
|
|
100
110
|
}
|
|
101
111
|
return geom3.create(polygons)
|
|
@@ -17,7 +17,7 @@ test('extrudeFromSlices (defaults)', (t) => {
|
|
|
17
17
|
const geometry2 = square({ size: 20 })
|
|
18
18
|
|
|
19
19
|
let geometry3 = extrudeFromSlices({ }, geometry2)
|
|
20
|
-
let pts = geom3.
|
|
20
|
+
let pts = geom3.toVertices(geometry3)
|
|
21
21
|
const exp = [
|
|
22
22
|
[[-10, -10, 0], [10, -10, 0], [10, -10, 1]],
|
|
23
23
|
[[-10, -10, 0], [10, -10, 1], [-10, -10, 1]],
|
|
@@ -39,7 +39,7 @@ test('extrudeFromSlices (defaults)', (t) => {
|
|
|
39
39
|
|
|
40
40
|
const poly2 = poly3.create([[-10, -10, 0], [10, -10, 0], [10, 10, 0], [-10, 10, 0]])
|
|
41
41
|
geometry3 = extrudeFromSlices({ }, poly2)
|
|
42
|
-
pts = geom3.
|
|
42
|
+
pts = geom3.toVertices(geometry3)
|
|
43
43
|
|
|
44
44
|
t.notThrows(() => geom3.validate(geometry3))
|
|
45
45
|
t.is(measureArea(geometry3), 880)
|
|
@@ -75,7 +75,7 @@ test('extrudeFromSlices (torus)', (t) => {
|
|
|
75
75
|
}
|
|
76
76
|
}, hex
|
|
77
77
|
)
|
|
78
|
-
const pts = geom3.
|
|
78
|
+
const pts = geom3.toVertices(geometry3)
|
|
79
79
|
t.notThrows(() => geom3.validate(geometry3))
|
|
80
80
|
t.is(measureArea(geometry3), 7070.694617452831)
|
|
81
81
|
t.is(measureVolume(geometry3), 29393.876913398108)
|
|
@@ -96,7 +96,7 @@ test('extrudeFromSlices (same shape, changing dimensions)', (t) => {
|
|
|
96
96
|
}
|
|
97
97
|
}, base
|
|
98
98
|
)
|
|
99
|
-
const pts = geom3.
|
|
99
|
+
const pts = geom3.toVertices(geometry3)
|
|
100
100
|
// expected to throw because capEnd is false (non-closed geometry)
|
|
101
101
|
t.throws(() => geom3.validate(geometry3))
|
|
102
102
|
t.is(measureArea(geometry3), 53.70100297794013)
|
|
@@ -117,7 +117,7 @@ test('extrudeFromSlices (changing shape, changing dimensions)', (t) => {
|
|
|
117
117
|
}
|
|
118
118
|
}, base
|
|
119
119
|
)
|
|
120
|
-
const pts = geom3.
|
|
120
|
+
const pts = geom3.toVertices(geometry3)
|
|
121
121
|
t.notThrows.skip(() => geom3.validate(geometry3))
|
|
122
122
|
t.is(measureArea(geometry3), 1965.8643589631802)
|
|
123
123
|
t.is(measureVolume(geometry3), 5260.067107417433)
|
|
@@ -130,7 +130,7 @@ test('extrudeFromSlices (holes)', (t) => {
|
|
|
130
130
|
[[-5, -5], [-5, 5], [5, 5], [5, -5]]
|
|
131
131
|
])
|
|
132
132
|
const geometry3 = extrudeFromSlices({ }, geometry2)
|
|
133
|
-
const pts = geom3.
|
|
133
|
+
const pts = geom3.toVertices(geometry3)
|
|
134
134
|
const exp = [
|
|
135
135
|
[[-10, 10, 0], [-10, -10, 0], [-10, -10, 1]],
|
|
136
136
|
[[-10, 10, 0], [-10, -10, 1], [-10, 10, 1]],
|
|
@@ -18,7 +18,7 @@ test('extrudeLinear (defaults)', (t) => {
|
|
|
18
18
|
const geometry2 = square({ size: 10 })
|
|
19
19
|
|
|
20
20
|
const geometry3 = extrudeLinear({ }, geometry2)
|
|
21
|
-
const pts = geom3.
|
|
21
|
+
const pts = geom3.toVertices(geometry3)
|
|
22
22
|
const exp = [
|
|
23
23
|
[[-5, -5, 0], [5, -5, 0], [5, -5, 1]],
|
|
24
24
|
[[-5, -5, 0], [5, -5, 1], [-5, -5, 1]],
|
|
@@ -55,7 +55,7 @@ test('extrudeLinear (no twist)', (t) => {
|
|
|
55
55
|
const geometry2 = square({ size: 10 })
|
|
56
56
|
|
|
57
57
|
let geometry3 = extrudeLinear({ height: 15 }, geometry2)
|
|
58
|
-
let pts = geom3.
|
|
58
|
+
let pts = geom3.toVertices(geometry3)
|
|
59
59
|
let exp = [
|
|
60
60
|
[[-5, -5, 0], [5, -5, 0], [5, -5, 15]],
|
|
61
61
|
[[-5, -5, 0], [5, -5, 15], [-5, -5, 15]],
|
|
@@ -77,7 +77,7 @@ test('extrudeLinear (no twist)', (t) => {
|
|
|
77
77
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
78
78
|
|
|
79
79
|
geometry3 = extrudeLinear({ height: -15 }, geometry2)
|
|
80
|
-
pts = geom3.
|
|
80
|
+
pts = geom3.toVertices(geometry3)
|
|
81
81
|
exp = [
|
|
82
82
|
[[-5, 5, 0], [5, 5, 0], [5, 5, -15]],
|
|
83
83
|
[[-5, 5, 0], [5, 5, -15], [-5, 5, -15]],
|
|
@@ -103,7 +103,7 @@ test('extrudeLinear (twist)', (t) => {
|
|
|
103
103
|
const geometry2 = square({ size: 10 })
|
|
104
104
|
|
|
105
105
|
let geometry3 = extrudeLinear({ height: 15, twistAngle: -TAU / 8 }, geometry2)
|
|
106
|
-
let pts = geom3.
|
|
106
|
+
let pts = geom3.toVertices(geometry3)
|
|
107
107
|
let exp = [
|
|
108
108
|
[[-5, -5, 0], [5, -5, 0], [4.440892098500626e-16, -7.0710678118654755, 15]],
|
|
109
109
|
[[-5, -5, 0], [4.440892098500626e-16, -7.0710678118654755, 15], [-7.0710678118654755, -4.440892098500626e-16, 15]],
|
|
@@ -133,7 +133,7 @@ test('extrudeLinear (twist)', (t) => {
|
|
|
133
133
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
134
134
|
|
|
135
135
|
geometry3 = extrudeLinear({ height: 15, twistAngle: TAU / 4, twistSteps: 3 }, geometry2)
|
|
136
|
-
pts = geom3.
|
|
136
|
+
pts = geom3.toVertices(geometry3)
|
|
137
137
|
exp = [
|
|
138
138
|
[[-5, -5, 0], [5, -5, 0], [6.830127018922193, -1.830127018922194, 5]],
|
|
139
139
|
[[-5, -5, 0], [6.830127018922193, -1.830127018922194, 5], [-1.830127018922194, -6.830127018922193, 5]],
|
|
@@ -168,7 +168,7 @@ test('extrudeLinear (twist)', (t) => {
|
|
|
168
168
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
169
169
|
|
|
170
170
|
geometry3 = extrudeLinear({ height: 15, twistAngle: TAU / 2, twistSteps: 30 }, geometry2)
|
|
171
|
-
pts = geom3.
|
|
171
|
+
pts = geom3.toVertices(geometry3)
|
|
172
172
|
t.notThrows(() => geom3.validate(geometry3))
|
|
173
173
|
t.is(measureArea(geometry3), 1091.9932843446968)
|
|
174
174
|
t.is(measureVolume(geometry3), 1444.9967160503095)
|
|
@@ -181,7 +181,7 @@ test('extrudeLinear (holes)', (t) => {
|
|
|
181
181
|
[[-2, -2], [-2, 2], [2, 2], [2, -2]]
|
|
182
182
|
])
|
|
183
183
|
const geometry3 = extrudeLinear({ height: 15 }, geometry2)
|
|
184
|
-
const pts = geom3.
|
|
184
|
+
const pts = geom3.toVertices(geometry3)
|
|
185
185
|
const exp = [
|
|
186
186
|
[[-5, 5, 0], [-5, -5, 0], [-5, -5, 15]],
|
|
187
187
|
[[-5, 5, 0], [-5, -5, 15], [-5, 5, 15]],
|
|
@@ -227,7 +227,7 @@ test('extrudeLinear (path2)', (t) => {
|
|
|
227
227
|
const geometry2 = path2.fromPoints({ closed: true }, [[6, 10], [0, 0], [12, 0]])
|
|
228
228
|
const geometry3 = extrudeLinear({ height: 15 }, geometry2)
|
|
229
229
|
t.notThrows(() => geom3.validate(geometry3))
|
|
230
|
-
const pts = geom3.
|
|
230
|
+
const pts = geom3.toVertices(geometry3)
|
|
231
231
|
const exp = [
|
|
232
232
|
[[6, 10, 0], [0, 0, 0], [0, 0, 15]],
|
|
233
233
|
[[6, 10, 0], [0, 0, 15], [6, 10, 15]],
|
|
@@ -110,13 +110,14 @@ export const extrudeRotate = (options, geometry) => {
|
|
|
110
110
|
baseSlice = slice.reverse(baseSlice)
|
|
111
111
|
|
|
112
112
|
const matrix = mat4.create()
|
|
113
|
+
const xRotationMatrix = mat4.fromXRotation(mat4.create(), TAU / 4) // compute once, reuse
|
|
113
114
|
const createSlice = (progress, index, base) => {
|
|
114
115
|
let Zrotation = rotationPerSlice * index + startAngle
|
|
115
116
|
// fix rounding error when rotating TAU radians
|
|
116
117
|
if (totalRotation === TAU && index === segments) {
|
|
117
118
|
Zrotation = startAngle
|
|
118
119
|
}
|
|
119
|
-
mat4.multiply(matrix, mat4.fromZRotation(matrix, Zrotation),
|
|
120
|
+
mat4.multiply(matrix, mat4.fromZRotation(matrix, Zrotation), xRotationMatrix)
|
|
120
121
|
|
|
121
122
|
return slice.transform(matrix, base)
|
|
122
123
|
}
|