@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
|
@@ -16,14 +16,14 @@ test('project (defaults)', (t) => {
|
|
|
16
16
|
|
|
17
17
|
const results = project({ }, geometry0, geometry1, geometry2, geometry3, geometry4)
|
|
18
18
|
t.is(results.length, 5)
|
|
19
|
-
t.
|
|
20
|
-
t.
|
|
19
|
+
t.notThrows(() => geom2.validate(results[0]))
|
|
20
|
+
t.notThrows(() => geom2.validate(results[1]))
|
|
21
21
|
t.is(results[2], geometry2)
|
|
22
22
|
t.is(results[3], geometry3)
|
|
23
23
|
t.is(results[4], geometry4)
|
|
24
24
|
|
|
25
25
|
const result = project({ }, torus({ outerSegments: 4 }))
|
|
26
|
-
t.
|
|
26
|
+
t.notThrows(() => geom2.validate(result))
|
|
27
27
|
const pts = geom2.toPoints(result)
|
|
28
28
|
const exp = [
|
|
29
29
|
[0, -2.9999933333333333],
|
|
@@ -40,7 +40,7 @@ test('project (defaults)', (t) => {
|
|
|
40
40
|
|
|
41
41
|
test('project (X and Y axis)', (t) => {
|
|
42
42
|
let result = project({ axis: [1, 0, 0], origin: [1, 0, 0] }, torus({ outerSegments: 4 }))
|
|
43
|
-
t.
|
|
43
|
+
t.notThrows(() => geom2.validate(result))
|
|
44
44
|
let pts = geom2.toPoints(result)
|
|
45
45
|
let exp = [
|
|
46
46
|
[-1.0000200000000001, -3.999986666666667],
|
|
@@ -81,7 +81,7 @@ test('project (X and Y axis)', (t) => {
|
|
|
81
81
|
t.true(comparePoints(pts, exp))
|
|
82
82
|
|
|
83
83
|
result = project({ axis: [0, 1, 0], origin: [0, -1, 0] }, torus({ outerSegments: 4 }))
|
|
84
|
-
t.
|
|
84
|
+
t.notThrows(() => geom2.validate(result))
|
|
85
85
|
pts = geom2.toPoints(result)
|
|
86
86
|
exp = [
|
|
87
87
|
[3.999986666666667, -1.0000200000000001],
|
|
@@ -23,10 +23,13 @@ const calculatePlane = (slice) => {
|
|
|
23
23
|
let farthestEdge
|
|
24
24
|
let distance = 0
|
|
25
25
|
edges.forEach((edge) => {
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
distance
|
|
26
|
+
// Make sure that the farthest edge is not a self-edge
|
|
27
|
+
if (!vec3.equals(edge[0], edge[1])) {
|
|
28
|
+
const d = vec3.squaredDistance(midpoint, edge[0])
|
|
29
|
+
if (d > distance) {
|
|
30
|
+
farthestEdge = edge
|
|
31
|
+
distance = d
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
34
|
})
|
|
32
35
|
// find the before edge
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {slice} object - the object to
|
|
2
|
+
* Determine if the given object is a slice.
|
|
3
|
+
* @param {slice} object - the object to interrogate
|
|
4
4
|
* @returns {Boolean} true if the object matches a slice
|
|
5
5
|
* @alias module:modeling/extrusions/slice.isA
|
|
6
6
|
*/
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const vec3 = require('../../../maths/vec3')
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Mend gaps in a 2D slice to make it a closed polygon
|
|
5
|
+
*/
|
|
6
|
+
const repairSlice = (slice) => {
|
|
7
|
+
if (!slice.edges) return slice
|
|
8
|
+
const vertexMap = {} // string key to vertex map
|
|
9
|
+
const edgeCount = {} // count of (in - out) edges
|
|
10
|
+
slice.edges.forEach((edge) => {
|
|
11
|
+
const inKey = edge[0].toString()
|
|
12
|
+
const outKey = edge[1].toString()
|
|
13
|
+
vertexMap[inKey] = edge[0]
|
|
14
|
+
vertexMap[outKey] = edge[1]
|
|
15
|
+
edgeCount[inKey] = (edgeCount[inKey] || 0) + 1 // in
|
|
16
|
+
edgeCount[outKey] = (edgeCount[outKey] || 0) - 1 // out
|
|
17
|
+
})
|
|
18
|
+
// find vertices which are missing in or out edges
|
|
19
|
+
const missingIn = Object.keys(edgeCount).filter((e) => edgeCount[e] < 0)
|
|
20
|
+
const missingOut = Object.keys(edgeCount).filter((e) => edgeCount[e] > 0)
|
|
21
|
+
// pairwise distance of bad vertices
|
|
22
|
+
missingIn.forEach((key1) => {
|
|
23
|
+
const v1 = vertexMap[key1]
|
|
24
|
+
// find the closest vertex that is missing an out edge
|
|
25
|
+
let bestDistance = Infinity
|
|
26
|
+
let bestReplacement
|
|
27
|
+
missingOut.forEach((key2) => {
|
|
28
|
+
const v2 = vertexMap[key2]
|
|
29
|
+
const distance = Math.hypot(v1[0] - v2[0], v1[1] - v2[1])
|
|
30
|
+
if (distance < bestDistance) {
|
|
31
|
+
bestDistance = distance
|
|
32
|
+
bestReplacement = v2
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
console.warn(`repairSlice: repairing vertex gap ${v1} to ${bestReplacement} distance ${bestDistance}`)
|
|
36
|
+
// merge broken vertices
|
|
37
|
+
slice.edges.forEach((edge) => {
|
|
38
|
+
if (edge[0].toString() === key1) edge[0] = bestReplacement
|
|
39
|
+
if (edge[1].toString() === key1) edge[1] = bestReplacement
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
// Remove self-edges
|
|
43
|
+
slice.edges = slice.edges.filter((e) => !vec3.equals(e[0], e[1]))
|
|
44
|
+
return slice
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = repairSlice
|
|
@@ -1,21 +1,6 @@
|
|
|
1
|
-
const vec3 = require('../../../maths/vec3')
|
|
2
|
-
|
|
3
|
-
const geom3 = require('../../../geometries/geom3')
|
|
4
1
|
const poly3 = require('../../../geometries/poly3')
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const calculatePlane = require('./calculatePlane')
|
|
9
|
-
|
|
10
|
-
const toPolygon3D = (vector, edge) => {
|
|
11
|
-
const points = [
|
|
12
|
-
vec3.subtract(vec3.create(), edge[0], vector),
|
|
13
|
-
vec3.subtract(vec3.create(), edge[1], vector),
|
|
14
|
-
vec3.add(vec3.create(), edge[1], vector),
|
|
15
|
-
vec3.add(vec3.create(), edge[0], vector)
|
|
16
|
-
]
|
|
17
|
-
return poly3.fromPoints(points)
|
|
18
|
-
}
|
|
2
|
+
const earcut = require('../earcut')
|
|
3
|
+
const PolygonHierarchy = require('../earcut/polygonHierarchy')
|
|
19
4
|
|
|
20
5
|
/**
|
|
21
6
|
* Return a list of polygons which are enclosed by the slice.
|
|
@@ -24,52 +9,31 @@ const toPolygon3D = (vector, edge) => {
|
|
|
24
9
|
* @alias module:modeling/extrusions/slice.toPolygons
|
|
25
10
|
*/
|
|
26
11
|
const toPolygons = (slice) => {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
12
|
+
const hierarchy = new PolygonHierarchy(slice)
|
|
13
|
+
|
|
14
|
+
const polygons = []
|
|
15
|
+
hierarchy.roots.forEach(({ solid, holes }) => {
|
|
16
|
+
// hole indices
|
|
17
|
+
let index = solid.length
|
|
18
|
+
const holesIndex = []
|
|
19
|
+
holes.forEach((hole, i) => {
|
|
20
|
+
holesIndex.push(index)
|
|
21
|
+
index += hole.length
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// compute earcut triangulation for each solid
|
|
25
|
+
const vertices = [solid, ...holes].flat()
|
|
26
|
+
const data = vertices.flat()
|
|
27
|
+
// Get original 3D vertex by index
|
|
28
|
+
const getVertex = (i) => hierarchy.to3D(vertices[i])
|
|
29
|
+
const indices = earcut(data, holesIndex)
|
|
30
|
+
for (let i = 0; i < indices.length; i += 3) {
|
|
31
|
+
// Map back to original vertices
|
|
32
|
+
const tri = indices.slice(i, i + 3).map(getVertex)
|
|
33
|
+
polygons.push(poly3.fromPointsAndPlane(tri, hierarchy.plane))
|
|
42
34
|
}
|
|
43
35
|
})
|
|
44
36
|
|
|
45
|
-
// create one LARGE polygon to encompass the side, i.e. base
|
|
46
|
-
const direction = vec3.subtract(vec3.create(), farthestEdge[0], midpoint)
|
|
47
|
-
const perpendicular = vec3.cross(vec3.create(), splane, direction)
|
|
48
|
-
|
|
49
|
-
const p1 = vec3.add(vec3.create(), midpoint, direction)
|
|
50
|
-
vec3.add(p1, p1, direction)
|
|
51
|
-
const p2 = vec3.add(vec3.create(), midpoint, perpendicular)
|
|
52
|
-
vec3.add(p2, p2, perpendicular)
|
|
53
|
-
const p3 = vec3.subtract(vec3.create(), midpoint, direction)
|
|
54
|
-
vec3.subtract(p3, p3, direction)
|
|
55
|
-
const p4 = vec3.subtract(vec3.create(), midpoint, perpendicular)
|
|
56
|
-
vec3.subtract(p4, p4, perpendicular)
|
|
57
|
-
const poly1 = poly3.fromPoints([p1, p2, p3, p4])
|
|
58
|
-
const base = geom3.create([poly1])
|
|
59
|
-
|
|
60
|
-
const wallPolygons = edges.map((edge) => toPolygon3D(splane, edge))
|
|
61
|
-
const walls = geom3.create(wallPolygons)
|
|
62
|
-
|
|
63
|
-
// make an insection of the base and the walls, creating... a set of polygons!
|
|
64
|
-
const geometry3 = intersectGeom3Sub(base, walls)
|
|
65
|
-
|
|
66
|
-
// return only those polygons from the base
|
|
67
|
-
let polygons = geom3.toPolygons(geometry3)
|
|
68
|
-
polygons = polygons.filter((polygon) => {
|
|
69
|
-
const a = vec3.angle(splane, poly3.plane(polygon))
|
|
70
|
-
// walls should be PI / 2 radians rotated from the base
|
|
71
|
-
return Math.abs(a) < (Math.PI / 90)
|
|
72
|
-
})
|
|
73
37
|
return polygons
|
|
74
38
|
}
|
|
75
39
|
|
|
@@ -16,12 +16,14 @@ test('hull (single, geom2)', (t) => {
|
|
|
16
16
|
let obs = hull(geometry)
|
|
17
17
|
let pts = geom2.toPoints(obs)
|
|
18
18
|
|
|
19
|
+
t.notThrows(() => geom2.validate(geometry))
|
|
19
20
|
t.is(pts.length, 0)
|
|
20
21
|
|
|
21
22
|
geometry = geom2.fromPoints([[5, 5], [-5, 5], [-5, -5], [5, -5]])
|
|
22
23
|
obs = hull(geometry)
|
|
23
24
|
pts = geom2.toPoints(obs)
|
|
24
25
|
|
|
26
|
+
t.notThrows(() => geom2.validate(geometry))
|
|
25
27
|
t.is(pts.length, 4)
|
|
26
28
|
|
|
27
29
|
// convex C shape
|
|
@@ -40,10 +42,11 @@ test('hull (single, geom2)', (t) => {
|
|
|
40
42
|
obs = hull(geometry)
|
|
41
43
|
pts = geom2.toPoints(obs)
|
|
42
44
|
|
|
45
|
+
t.notThrows(() => geom2.validate(geometry))
|
|
43
46
|
t.is(pts.length, 7)
|
|
44
47
|
})
|
|
45
48
|
|
|
46
|
-
test('hull (multiple,
|
|
49
|
+
test('hull (multiple, overlapping, geom2)', (t) => {
|
|
47
50
|
const geometry1 = geom2.fromPoints([[5, 5], [-5, 5], [-5, -5], [5, -5]])
|
|
48
51
|
const geometry2 = geom2.fromPoints([[3, 3], [-3, 3], [-3, -3], [3, -3]])
|
|
49
52
|
const geometry3 = geom2.fromPoints([[6, 3], [-6, 3], [-6, -3], [6, -3]])
|
|
@@ -66,23 +69,27 @@ test('hull (multiple, overlaping, geom2)', (t) => {
|
|
|
66
69
|
let obs = hull(geometry1, geometry1)
|
|
67
70
|
let pts = geom2.toPoints(obs)
|
|
68
71
|
|
|
72
|
+
t.notThrows(() => geom2.validate(obs))
|
|
69
73
|
t.is(pts.length, 4)
|
|
70
74
|
|
|
71
75
|
// one inside another
|
|
72
76
|
obs = hull(geometry1, geometry2)
|
|
73
77
|
pts = geom2.toPoints(obs)
|
|
74
78
|
|
|
79
|
+
t.notThrows(() => geom2.validate(obs))
|
|
75
80
|
t.is(pts.length, 4)
|
|
76
81
|
|
|
77
82
|
// one overlapping another
|
|
78
83
|
obs = hull(geometry1, geometry3)
|
|
79
84
|
pts = geom2.toPoints(obs)
|
|
80
85
|
|
|
86
|
+
t.notThrows(() => geom2.validate(obs))
|
|
81
87
|
t.is(pts.length, 8)
|
|
82
88
|
|
|
83
89
|
obs = hull(geometry2, geometry4)
|
|
84
90
|
pts = geom2.toPoints(obs)
|
|
85
91
|
|
|
92
|
+
t.notThrows(() => geom2.validate(obs))
|
|
86
93
|
t.is(pts.length, 7)
|
|
87
94
|
})
|
|
88
95
|
|
|
@@ -108,22 +115,27 @@ test('hull (multiple, various, geom2)', (t) => {
|
|
|
108
115
|
|
|
109
116
|
let obs = hull(geometry1, geometry2)
|
|
110
117
|
let pts = geom2.toPoints(obs)
|
|
118
|
+
t.notThrows(() => geom2.validate(obs))
|
|
111
119
|
t.is(pts.length, 5)
|
|
112
120
|
|
|
113
121
|
obs = hull(geometry1, geometry3)
|
|
114
122
|
pts = geom2.toPoints(obs)
|
|
123
|
+
t.notThrows(() => geom2.validate(obs))
|
|
115
124
|
t.is(pts.length, 5)
|
|
116
125
|
|
|
117
126
|
obs = hull(geometry2, geometry3)
|
|
118
127
|
pts = geom2.toPoints(obs)
|
|
128
|
+
t.notThrows(() => geom2.validate(obs))
|
|
119
129
|
t.is(pts.length, 5)
|
|
120
130
|
|
|
121
131
|
obs = hull(geometry1, geometry2, geometry3)
|
|
122
132
|
pts = geom2.toPoints(obs)
|
|
133
|
+
t.notThrows(() => geom2.validate(obs))
|
|
123
134
|
t.is(pts.length, 6)
|
|
124
135
|
|
|
125
136
|
obs = hull(geometry5, geometry4)
|
|
126
137
|
pts = geom2.toPoints(obs)
|
|
138
|
+
t.notThrows(() => geom2.validate(obs))
|
|
127
139
|
t.is(pts.length, 8)
|
|
128
140
|
})
|
|
129
141
|
|
|
@@ -133,6 +145,7 @@ test('hull (single, path2)', (t) => {
|
|
|
133
145
|
let obs = hull(geometry)
|
|
134
146
|
let pts = path2.toPoints(obs)
|
|
135
147
|
|
|
148
|
+
t.notThrows(() => path2.validate(obs))
|
|
136
149
|
t.is(pts.length, 0)
|
|
137
150
|
|
|
138
151
|
geometry = path2.fromPoints({}, [[0, 0], [5, 0], [5, 10], [4, 1]])
|
|
@@ -140,6 +153,7 @@ test('hull (single, path2)', (t) => {
|
|
|
140
153
|
obs = hull(geometry)
|
|
141
154
|
pts = path2.toPoints(obs)
|
|
142
155
|
|
|
156
|
+
t.notThrows(() => path2.validate(obs))
|
|
143
157
|
t.is(pts.length, 3)
|
|
144
158
|
})
|
|
145
159
|
|
|
@@ -165,22 +179,27 @@ test('hull (multiple, various, path2)', (t) => {
|
|
|
165
179
|
|
|
166
180
|
let obs = hull(geometry1, geometry2)
|
|
167
181
|
let pts = path2.toPoints(obs)
|
|
182
|
+
t.notThrows(() => path2.validate(obs))
|
|
168
183
|
t.is(pts.length, 5)
|
|
169
184
|
|
|
170
185
|
obs = hull(geometry1, geometry3)
|
|
171
186
|
pts = path2.toPoints(obs)
|
|
187
|
+
t.notThrows(() => path2.validate(obs))
|
|
172
188
|
t.is(pts.length, 5)
|
|
173
189
|
|
|
174
190
|
obs = hull(geometry2, geometry3)
|
|
175
191
|
pts = path2.toPoints(obs)
|
|
192
|
+
t.notThrows(() => path2.validate(obs))
|
|
176
193
|
t.is(pts.length, 5)
|
|
177
194
|
|
|
178
195
|
obs = hull(geometry1, geometry2, geometry3)
|
|
179
196
|
pts = path2.toPoints(obs)
|
|
197
|
+
t.notThrows(() => path2.validate(obs))
|
|
180
198
|
t.is(pts.length, 6)
|
|
181
199
|
|
|
182
200
|
obs = hull(geometry5, geometry4)
|
|
183
201
|
pts = path2.toPoints(obs)
|
|
202
|
+
t.notThrows(() => path2.validate(obs))
|
|
184
203
|
t.is(pts.length, 8)
|
|
185
204
|
})
|
|
186
205
|
|
|
@@ -190,6 +209,7 @@ test('hull (single, geom3)', (t) => {
|
|
|
190
209
|
let obs = hull(geometry)
|
|
191
210
|
let pts = geom3.toPoints(obs)
|
|
192
211
|
|
|
212
|
+
t.notThrows(() => geom3.validate(obs))
|
|
193
213
|
t.is(pts.length, 0)
|
|
194
214
|
|
|
195
215
|
geometry = sphere({ radius: 2, segments: 8 })
|
|
@@ -197,6 +217,7 @@ test('hull (single, geom3)', (t) => {
|
|
|
197
217
|
obs = hull(geometry)
|
|
198
218
|
pts = geom3.toPoints(obs)
|
|
199
219
|
|
|
220
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
200
221
|
t.is(pts.length, 32)
|
|
201
222
|
})
|
|
202
223
|
|
|
@@ -214,6 +235,7 @@ test('hull (multiple, geom3)', (t) => {
|
|
|
214
235
|
[[1, -1, 1], [-1, -1, 1], [-1, -1, -1], [1, -1, -1]]
|
|
215
236
|
]
|
|
216
237
|
|
|
238
|
+
t.notThrows(() => geom3.validate(obs))
|
|
217
239
|
t.is(pts.length, 6)
|
|
218
240
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
219
241
|
|
|
@@ -236,6 +258,7 @@ test('hull (multiple, geom3)', (t) => {
|
|
|
236
258
|
[[1, 1, -1], [6.5, 6.5, 3.5], [6.5, 3.5, 3.5], [1, -1, -1]]
|
|
237
259
|
]
|
|
238
260
|
|
|
261
|
+
t.notThrows(() => geom3.validate(obs))
|
|
239
262
|
t.is(pts.length, 12)
|
|
240
263
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
241
264
|
})
|
|
@@ -248,6 +271,6 @@ test('hull (multiple, overlapping, geom3)', (t) => {
|
|
|
248
271
|
const obs = hull(geometry1, geometry2, geometry3)
|
|
249
272
|
const pts = geom3.toPoints(obs)
|
|
250
273
|
|
|
251
|
-
|
|
274
|
+
t.notThrows(() => geom3.validate(obs))
|
|
252
275
|
t.is(pts.length, 92)
|
|
253
276
|
})
|
|
@@ -5,7 +5,7 @@ const union = require('../booleans/union')
|
|
|
5
5
|
const hull = require('./hull')
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Create a chain of hulled geometries from the given
|
|
8
|
+
* Create a chain of hulled geometries from the given geometries.
|
|
9
9
|
* Essentially hull A+B, B+C, C+D, etc., then union the results.
|
|
10
10
|
* The given geometries should be of the same type, either geom2 or geom3 or path2.
|
|
11
11
|
*
|
|
@@ -12,12 +12,14 @@ test('hullChain (two, geom2)', (t) => {
|
|
|
12
12
|
let obs = hullChain(geometry1, geometry1)
|
|
13
13
|
let pts = geom2.toPoints(obs)
|
|
14
14
|
|
|
15
|
+
t.notThrows(() => geom2.validate(obs))
|
|
15
16
|
t.is(pts.length, 4)
|
|
16
17
|
|
|
17
18
|
// different
|
|
18
19
|
obs = hullChain(geometry1, geometry2)
|
|
19
20
|
pts = geom2.toPoints(obs)
|
|
20
21
|
|
|
22
|
+
t.notThrows(() => geom2.validate(obs))
|
|
21
23
|
t.is(pts.length, 6)
|
|
22
24
|
})
|
|
23
25
|
|
|
@@ -31,16 +33,16 @@ test('hullChain (three, geom2)', (t) => {
|
|
|
31
33
|
let pts = geom2.toPoints(obs)
|
|
32
34
|
|
|
33
35
|
// the sides change based on the bestplane chosen in trees/Node.js
|
|
36
|
+
t.notThrows(() => geom2.validate(obs))
|
|
34
37
|
t.is(pts.length, 10)
|
|
35
|
-
// t.is(pts.length, 11)
|
|
36
38
|
|
|
37
39
|
// closed
|
|
38
40
|
obs = hullChain(geometry1, geometry2, geometry3, geometry1)
|
|
39
41
|
pts = geom2.toPoints(obs)
|
|
40
42
|
|
|
41
43
|
// the sides change based on the bestplane chosen in trees/Node.js
|
|
44
|
+
t.notThrows(() => geom2.validate(obs))
|
|
42
45
|
t.is(pts.length, 10)
|
|
43
|
-
// t.is(pts.length, 13)
|
|
44
46
|
})
|
|
45
47
|
|
|
46
48
|
test('hullChain (three, geom3)', (t) => {
|
|
@@ -73,13 +75,13 @@ test('hullChain (three, geom3)', (t) => {
|
|
|
73
75
|
let obs = hullChain(geometry1, geometry2, geometry3)
|
|
74
76
|
let pts = geom3.toPoints(obs)
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
77
79
|
t.is(pts.length, 23)
|
|
78
80
|
|
|
79
81
|
// closed
|
|
80
82
|
obs = hullChain(geometry1, geometry2, geometry3, geometry1)
|
|
81
83
|
pts = geom3.toPoints(obs)
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
84
86
|
t.is(pts.length, 28)
|
|
85
87
|
})
|
|
@@ -30,7 +30,7 @@ const hullGeom2 = (...geometries) => {
|
|
|
30
30
|
|
|
31
31
|
const hullpoints = hullPoints2(uniquepoints)
|
|
32
32
|
|
|
33
|
-
// NOTE: more
|
|
33
|
+
// NOTE: more than three points are required to create a new geometry
|
|
34
34
|
if (hullpoints.length < 3) return geom2.create()
|
|
35
35
|
|
|
36
36
|
// assemble a new geometry from the list of points
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const flatten = require('../../utils/flatten')
|
|
2
2
|
|
|
3
|
-
const vec2 = require('../../maths/vec2')
|
|
4
|
-
|
|
5
3
|
const path2 = require('../../geometries/path2')
|
|
6
4
|
|
|
7
5
|
const hullPoints2 = require('./hullPoints2')
|
|
@@ -16,11 +14,15 @@ const hullPath2 = (...geometries) => {
|
|
|
16
14
|
|
|
17
15
|
// extract the unique points from the geometries
|
|
18
16
|
const uniquepoints = []
|
|
17
|
+
const found = new Set()
|
|
19
18
|
geometries.forEach((geometry) => {
|
|
20
19
|
const points = path2.toPoints(geometry)
|
|
21
20
|
points.forEach((point) => {
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
21
|
+
const key = point.toString()
|
|
22
|
+
if (!found.has(key)) {
|
|
23
|
+
uniquepoints.push(point)
|
|
24
|
+
found.add(key)
|
|
25
|
+
}
|
|
24
26
|
})
|
|
25
27
|
})
|
|
26
28
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const test = require('ava')
|
|
2
|
+
|
|
3
|
+
const { path2 } = require('../../geometries')
|
|
4
|
+
|
|
5
|
+
const hullPath2 = require('./hullPath2')
|
|
6
|
+
|
|
7
|
+
test('hullPath2', (t) => {
|
|
8
|
+
const closed = true
|
|
9
|
+
const geometry1 = path2.fromPoints({ closed }, [[0, 0], [-4, 4], [-4, -4]])
|
|
10
|
+
const geometry2 = path2.fromPoints({ closed }, [[0, 0], [4, -4], [4, 4]])
|
|
11
|
+
|
|
12
|
+
const obs = hullPath2(geometry1, geometry2)
|
|
13
|
+
t.notThrows(() => path2.validate(obs))
|
|
14
|
+
const pts = path2.toPoints(obs)
|
|
15
|
+
t.is(pts.length, 4)
|
|
16
|
+
})
|
|
@@ -42,7 +42,7 @@ test('hullPoints2 bug #114 2 circles with 18 segments', (t) => {
|
|
|
42
42
|
[7.298133329356933, -1.9283628290596186]
|
|
43
43
|
]
|
|
44
44
|
|
|
45
|
-
// we just want to
|
|
45
|
+
// we just want to be sure no err happens for this case
|
|
46
46
|
const out = hullPoints2(points)
|
|
47
47
|
t.is(out.length, 19)
|
|
48
48
|
})
|
|
@@ -149,7 +149,7 @@ const splitEdge = (openedges, edge, eps) => {
|
|
|
149
149
|
*/
|
|
150
150
|
const cullOpenEdges = (edges) => {
|
|
151
151
|
const openedges = []
|
|
152
|
-
edges.forEach((edge
|
|
152
|
+
edges.forEach((edge) => {
|
|
153
153
|
const polygons = edge.polygons
|
|
154
154
|
if (polygons.length === 1) {
|
|
155
155
|
// console.log('open edge: ',edge[0],'<-->',edge[1])
|
|
@@ -81,7 +81,7 @@ const generalize = (options, ...geometries) => {
|
|
|
81
81
|
geometries = flatten(geometries)
|
|
82
82
|
if (geometries.length === 0) throw new Error('wrong number of arguments')
|
|
83
83
|
|
|
84
|
-
const results = geometries.map((geometry
|
|
84
|
+
const results = geometries.map((geometry) => {
|
|
85
85
|
if (path2.isA(geometry)) return generalizePath2(options, geometry)
|
|
86
86
|
if (geom2.isA(geometry)) return generalizeGeom2(options, geometry)
|
|
87
87
|
if (geom3.isA(geometry)) return generalizeGeom3(options, geometry)
|
|
@@ -28,6 +28,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
28
28
|
[[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
29
29
|
[1.5707963267948966, 0.7853981633974483, 3.141592653589793], [-1.5707963267948966, 0.7853981633974483, 3.141592653589793]]
|
|
30
30
|
]
|
|
31
|
+
t.notThrows(() => geom3.validate(result))
|
|
31
32
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
32
33
|
|
|
33
34
|
// apply snap only
|
|
@@ -47,6 +48,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
47
48
|
[[-1.5707910908071407, -0.7854138713607164, 3.1415821816142815], [1.5707910908071407, -0.7854138713607164, 3.1415821816142815],
|
|
48
49
|
[1.5707910908071407, 0.7854138713607164, 3.1415821816142815], [-1.5707910908071407, 0.7854138713607164, 3.1415821816142815]]
|
|
49
50
|
]
|
|
51
|
+
t.notThrows(() => geom3.validate(result))
|
|
50
52
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
51
53
|
|
|
52
54
|
// apply triangulate only
|
|
@@ -78,6 +80,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
78
80
|
[[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
79
81
|
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793]]
|
|
80
82
|
]
|
|
83
|
+
t.notThrows(() => geom3.validate(result))
|
|
81
84
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
82
85
|
|
|
83
86
|
const geometry2 = result // save the triangles for another test
|
|
@@ -99,6 +102,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
99
102
|
[[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
100
103
|
[1.5707963267948966, 0.7853981633974483, 3.141592653589793], [-1.5707963267948966, 0.7853981633974483, 3.141592653589793]]
|
|
101
104
|
]
|
|
105
|
+
t.notThrows(() => geom3.validate(result))
|
|
102
106
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
103
107
|
|
|
104
108
|
// apply repairs only (triangles)
|
|
@@ -130,6 +134,7 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
130
134
|
[[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
131
135
|
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793]]
|
|
132
136
|
]
|
|
137
|
+
t.notThrows(() => geom3.validate(result))
|
|
133
138
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
134
139
|
})
|
|
135
140
|
|
|
@@ -190,5 +195,6 @@ test('generalize: generalize of a geom3 with T junctions produces an expected ge
|
|
|
190
195
|
[[0, 1, 1], [-1, 1, 1], [0, 0, 1]],
|
|
191
196
|
[[-1, 1, 1], [-1, 0, 1], [0, 0, 1]]
|
|
192
197
|
]
|
|
198
|
+
t.notThrows(() => geom3.validate(result))
|
|
193
199
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
194
200
|
})
|
|
@@ -11,7 +11,7 @@ const { snap } = require('./index')
|
|
|
11
11
|
test('snap: snap of a path2 produces an expected path2', (t) => {
|
|
12
12
|
const geometry1 = path2.create()
|
|
13
13
|
const geometry2 = arc({ radius: 1 / 2, segments: 8 })
|
|
14
|
-
const geometry3 = arc({ radius: 1.
|
|
14
|
+
const geometry3 = arc({ radius: 1.3333333333333333 / 2, segments: 8 })
|
|
15
15
|
const geometry4 = arc({ radius: Math.PI * 1000 / 2, segments: 8 })
|
|
16
16
|
|
|
17
17
|
const results = snap(geometry1, geometry2, geometry3, geometry4)
|
|
@@ -55,7 +55,7 @@ test('snap: snap of a path2 produces an expected path2', (t) => {
|
|
|
55
55
|
test('snap: snap of a geom2 produces an expected geom2', (t) => {
|
|
56
56
|
const geometry1 = geom2.create()
|
|
57
57
|
const geometry2 = rectangle({ size: [1, 1, 1] })
|
|
58
|
-
const geometry3 = rectangle({ size: [1.
|
|
58
|
+
const geometry3 = rectangle({ size: [1.3333333333333333, 1.3333333333333333, 1.3333333333333333] })
|
|
59
59
|
const geometry4 = rectangle({ size: [Math.PI * 1000, Math.PI * 1000, Math.PI * 1000] })
|
|
60
60
|
|
|
61
61
|
const results = snap(geometry1, geometry2, geometry3, geometry4)
|
|
@@ -87,7 +87,7 @@ test('snap: snap of a geom2 produces an expected geom2', (t) => {
|
|
|
87
87
|
test('snap: snap of a geom3 produces an expected geom3', (t) => {
|
|
88
88
|
const geometry1 = geom3.create()
|
|
89
89
|
const geometry2 = cuboid({ size: [1, 1, 1] })
|
|
90
|
-
const geometry3 = cuboid({ size: [1.
|
|
90
|
+
const geometry3 = cuboid({ size: [1.3333333333333333, 1.3333333333333333, 1.3333333333333333] })
|
|
91
91
|
const geometry4 = cuboid({ size: [Math.PI * 1000, Math.PI * 1000, Math.PI * 1000] })
|
|
92
92
|
|
|
93
93
|
const results = snap(geometry1, geometry2, geometry3, geometry4)
|
|
@@ -7,7 +7,7 @@ type NullableNumber = null | number
|
|
|
7
7
|
|
|
8
8
|
export interface AlignOptions {
|
|
9
9
|
modes?: Array<'center' | 'max' | 'min' | 'none'>
|
|
10
|
-
relativeTo?: [NullableNumber] | [NullableNumber
|
|
10
|
+
relativeTo?: [NullableNumber] | [NullableNumber, NullableNumber] | [NullableNumber, NullableNumber, NullableNumber]
|
|
11
11
|
grouped?: boolean
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
2
|
|
|
3
3
|
const { comparePoints } = require('../../../test/helpers')
|
|
4
|
+
const { geom3 } = require('../../geometries')
|
|
4
5
|
const { measureBoundingBox, measureAggregateBoundingBox } = require('../../measurements')
|
|
5
6
|
const { cube } = require('../../primitives')
|
|
6
7
|
|
|
@@ -11,6 +12,7 @@ test('align: single object returns geometry unchanged if all axes are none', (t)
|
|
|
11
12
|
const aligned = align({ modes: ['none', 'none', 'none'] }, original)
|
|
12
13
|
const bounds = measureBoundingBox(aligned)
|
|
13
14
|
const expectedBounds = [[8, 8, 8], [12, 12, 12]]
|
|
15
|
+
t.notThrows(() => geom3.validate(aligned))
|
|
14
16
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected. Result: ' + JSON.stringify(bounds))
|
|
15
17
|
})
|
|
16
18
|
|
|
@@ -19,6 +21,7 @@ test('align: single objects returns geometry aligned, different modes on each ax
|
|
|
19
21
|
const aligned = align({ modes: ['center', 'min', 'max'] }, original)
|
|
20
22
|
const bounds = measureBoundingBox(aligned)
|
|
21
23
|
const expectedBounds = [[-2, 0, -4], [2, 4, 0]]
|
|
24
|
+
t.notThrows(() => geom3.validate(aligned))
|
|
22
25
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected. Result: ' + JSON.stringify(bounds))
|
|
23
26
|
})
|
|
24
27
|
|
|
@@ -27,6 +30,7 @@ test('align: unfilled modes and relativeTo arrays return results with expected v
|
|
|
27
30
|
const aligned = align({ modes: ['center'], relativeTo: [0] }, original)
|
|
28
31
|
const bounds = measureBoundingBox(aligned)
|
|
29
32
|
const expectedBounds = [[-2, 8, 8], [2, 12, 12]]
|
|
33
|
+
t.notThrows(() => geom3.validate(aligned))
|
|
30
34
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected. Result: ' + JSON.stringify(bounds))
|
|
31
35
|
})
|
|
32
36
|
|
|
@@ -38,6 +42,8 @@ test('align: multiple objects grouped returns geometry aligned, different modes
|
|
|
38
42
|
const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [6, -10, 0], grouped: true }, original)
|
|
39
43
|
const bounds = measureAggregateBoundingBox(aligned)
|
|
40
44
|
const expectedBounds = [[1.5, -10, -9], [10.5, -1, 0]]
|
|
45
|
+
t.notThrows(() => geom3.validate(aligned[0]))
|
|
46
|
+
t.notThrows(() => geom3.validate(aligned[1]))
|
|
41
47
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected. Result: ' + JSON.stringify(bounds))
|
|
42
48
|
})
|
|
43
49
|
|
|
@@ -49,6 +55,8 @@ test('align: multiple objects ungrouped returns geometry aligned, different mode
|
|
|
49
55
|
const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [30, 30, 30] }, original)
|
|
50
56
|
const bounds = measureAggregateBoundingBox(aligned)
|
|
51
57
|
const expectedBounds = [[28, 30, 26], [32, 34, 30]]
|
|
58
|
+
t.notThrows(() => geom3.validate(aligned[0]))
|
|
59
|
+
t.notThrows(() => geom3.validate(aligned[1]))
|
|
52
60
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected. Result: ' + JSON.stringify(bounds))
|
|
53
61
|
})
|
|
54
62
|
|
|
@@ -60,6 +68,8 @@ test('align: multiple objects grouped, relativeTo is nulls, returns geometry unc
|
|
|
60
68
|
const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [null, null, null], grouped: true }, original)
|
|
61
69
|
const bounds = measureAggregateBoundingBox(aligned)
|
|
62
70
|
const expectedBounds = [[3, 3, 3], [12, 12, 12]]
|
|
71
|
+
t.notThrows(() => geom3.validate(aligned[0]))
|
|
72
|
+
t.notThrows(() => geom3.validate(aligned[1]))
|
|
63
73
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected. Result: ' + JSON.stringify(bounds))
|
|
64
74
|
})
|
|
65
75
|
|
|
@@ -71,6 +81,8 @@ test('align: multiple objects ungrouped, relativeTo is nulls, returns geometry a
|
|
|
71
81
|
const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [null, null, null], grouped: false }, original)
|
|
72
82
|
const bounds = measureAggregateBoundingBox(aligned)
|
|
73
83
|
const expectedBounds = [[5.5, 3, 8], [9.5, 7, 12]]
|
|
84
|
+
t.notThrows(() => geom3.validate(aligned[0]))
|
|
85
|
+
t.notThrows(() => geom3.validate(aligned[1]))
|
|
74
86
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected. Result: ' + JSON.stringify(bounds))
|
|
75
87
|
})
|
|
76
88
|
|