@jscad/modeling 2.9.0 → 2.9.3
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 +42 -0
- package/README.md +4 -4
- package/dist/jscad-modeling.min.js +437 -428
- package/package.json +3 -2
- package/src/colors/colorize.test.js +1 -1
- package/src/geometries/geom2/index.d.ts +1 -0
- package/src/geometries/geom2/index.js +2 -1
- package/src/geometries/geom2/toOutlines.js +66 -52
- package/src/geometries/geom2/validate.d.ts +3 -0
- package/src/geometries/geom2/validate.js +36 -0
- package/src/geometries/geom3/create.js +1 -1
- package/src/geometries/geom3/create.test.js +1 -1
- package/src/geometries/geom3/fromPoints.js +1 -1
- package/src/geometries/geom3/index.d.ts +1 -0
- package/src/geometries/geom3/index.js +2 -1
- package/src/geometries/geom3/isA.js +1 -1
- package/src/geometries/geom3/validate.d.ts +3 -0
- package/src/geometries/geom3/validate.js +62 -0
- package/src/geometries/path2/index.d.ts +1 -1
- package/src/geometries/path2/index.js +2 -2
- 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/poly3/create.js +1 -1
- package/src/geometries/poly3/index.d.ts +1 -0
- package/src/geometries/poly3/index.js +2 -1
- package/src/geometries/poly3/measureArea.test.js +16 -16
- package/src/geometries/poly3/measureBoundingSphere.test.js +8 -8
- package/src/geometries/poly3/validate.d.ts +4 -0
- package/src/geometries/poly3/validate.js +64 -0
- package/src/maths/constants.d.ts +1 -0
- package/src/maths/constants.js +11 -0
- package/src/maths/utils/aboutEqualNormals.js +1 -5
- package/src/measurements/measureCenterOfMass.test.js +2 -2
- package/src/operations/booleans/intersect.test.js +8 -0
- package/src/operations/booleans/intersectGeom3.js +2 -1
- package/src/operations/booleans/scission.test.js +4 -4
- package/src/operations/booleans/subtract.test.js +8 -0
- package/src/operations/booleans/subtractGeom3.js +2 -1
- package/src/operations/booleans/to3DWalls.js +1 -1
- 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 +27 -0
- package/src/operations/booleans/unionGeom3.js +2 -1
- package/src/operations/expansions/expand.test.js +30 -21
- package/src/operations/expansions/expandGeom3.test.js +14 -14
- package/src/operations/expansions/expandShell.js +5 -4
- package/src/operations/expansions/extrudePolygon.js +7 -7
- package/src/operations/expansions/offset.test.js +25 -0
- package/src/operations/extrusions/earcut/assignHoles.js +7 -3
- package/src/operations/extrusions/earcut/assignHoles.test.js +50 -4
- package/src/operations/extrusions/earcut/linkedList.js +1 -1
- package/src/operations/extrusions/extrudeFromSlices.test.js +16 -10
- package/src/operations/extrusions/extrudeLinear.test.js +15 -9
- package/src/operations/extrusions/extrudeRectangular.test.js +15 -8
- package/src/operations/extrusions/extrudeRotate.js +5 -1
- package/src/operations/extrusions/extrudeRotate.test.js +12 -0
- package/src/operations/extrusions/extrudeWalls.js +2 -2
- package/src/operations/extrusions/project.js +11 -14
- package/src/operations/extrusions/project.test.js +55 -55
- package/src/operations/hulls/hull.test.js +24 -1
- package/src/operations/hulls/hullChain.test.js +6 -4
- package/src/operations/hulls/hullGeom2.js +6 -18
- package/src/operations/hulls/hullGeom3.js +5 -18
- package/src/operations/hulls/hullPath2.js +4 -14
- package/src/operations/hulls/hullPath2.test.js +1 -1
- package/src/operations/hulls/hullPoints2.js +43 -92
- package/src/operations/hulls/toUniquePoints.js +34 -0
- package/src/operations/modifiers/generalize.js +2 -13
- package/src/operations/modifiers/generalize.test.js +5 -31
- package/src/operations/modifiers/insertTjunctions.js +1 -1
- package/src/operations/modifiers/insertTjunctions.test.js +21 -21
- package/src/operations/modifiers/mergePolygons.js +11 -14
- package/src/operations/{booleans → modifiers}/reTesselateCoplanarPolygons.js +1 -1
- package/src/operations/{booleans → modifiers}/reTesselateCoplanarPolygons.test.js +5 -5
- package/src/operations/{booleans → modifiers}/retessellate.js +2 -9
- package/src/operations/{booleans → modifiers}/retessellate.test.js +0 -0
- package/src/operations/modifiers/snapPolygons.test.js +12 -12
- package/src/operations/modifiers/triangulatePolygons.js +3 -3
- package/src/operations/transforms/align.test.js +12 -0
- package/src/operations/transforms/center.js +1 -1
- package/src/operations/transforms/center.test.js +12 -0
- package/src/operations/transforms/mirror.test.js +16 -0
- package/src/operations/transforms/rotate.test.js +10 -0
- package/src/operations/transforms/scale.test.js +15 -0
- package/src/operations/transforms/transform.test.js +5 -0
- package/src/operations/transforms/translate.test.js +16 -0
- 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.js +1 -1
- package/src/primitives/cuboid.test.js +9 -24
- package/src/primitives/cylinder.test.js +7 -4
- package/src/primitives/cylinderElliptic.js +14 -7
- package/src/primitives/cylinderElliptic.test.js +72 -50
- package/src/primitives/ellipse.js +3 -1
- package/src/primitives/ellipse.test.js +14 -8
- package/src/primitives/ellipsoid.js +8 -6
- package/src/primitives/ellipsoid.test.js +84 -80
- 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.js +6 -6
- package/src/primitives/roundedCuboid.test.js +5 -0
- package/src/primitives/roundedCylinder.js +7 -5
- 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.test.js +8 -1
- package/src/primitives/triangle.js +1 -2
- package/src/primitives/triangle.test.js +7 -0
- 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/trigonometry.d.ts +2 -0
- package/src/utils/trigonometry.js +34 -0
- package/src/utils/trigonometry.test.js +25 -0
- package/test/helpers/nearlyEqual.js +4 -1
- package/src/geometries/path2/eachPoint.d.ts +0 -9
- package/src/geometries/path2/eachPoint.js +0 -17
- package/src/geometries/path2/eachPoint.test.js +0 -11
- package/src/operations/modifiers/edges.js +0 -195
- package/src/operations/modifiers/repairTjunctions.js +0 -44
|
@@ -1,108 +1,59 @@
|
|
|
1
1
|
const vec2 = require('../../maths/vec2')
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return 0
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Ported from https://github.com/bkiers/GrahamScan
|
|
21
|
-
const compute = (points) => {
|
|
22
|
-
if (points.length < 3) {
|
|
23
|
-
return points
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Find the lowest point
|
|
27
|
-
let min = 0
|
|
28
|
-
points.forEach((point, i) => {
|
|
29
|
-
const minpoint = points[min]
|
|
30
|
-
if (point[1] === minpoint[1]) {
|
|
31
|
-
if (point[0] < minpoint[0]) {
|
|
32
|
-
min = i
|
|
33
|
-
}
|
|
34
|
-
} else if (point[1] < minpoint[1]) {
|
|
35
|
-
min = i
|
|
3
|
+
/*
|
|
4
|
+
* Create a convex hull of the given set of points, where each point is an array of [x,y].
|
|
5
|
+
* Uses https://en.wikipedia.org/wiki/Graham_scan
|
|
6
|
+
* @param {Array} uniquePoints - list of UNIQUE points from which to create a hull
|
|
7
|
+
* @returns {Array} a list of points that form the hull
|
|
8
|
+
*/
|
|
9
|
+
const hullPoints2 = (uniquePoints) => {
|
|
10
|
+
// find min point
|
|
11
|
+
let min = vec2.fromValues(Infinity, Infinity)
|
|
12
|
+
uniquePoints.forEach((point) => {
|
|
13
|
+
if (point[1] < min[1] || (point[1] === min[1] && point[0] < min[0])) {
|
|
14
|
+
min = point
|
|
36
15
|
}
|
|
37
16
|
})
|
|
38
17
|
|
|
39
|
-
//
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
angle = angleBetweenPoints(points[min], points[i])
|
|
48
|
-
if (angle < 0) {
|
|
49
|
-
angle += Math.PI
|
|
50
|
-
}
|
|
51
|
-
dist = vec2.squaredDistance(points[min], points[i])
|
|
52
|
-
al.push({ index: i, angle: angle, distance: dist })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
al.sort((a, b) => compareIndex(a, b))
|
|
56
|
-
|
|
57
|
-
// Wind around the points CCW, removing interior points
|
|
58
|
-
const stack = new Array(points.length + 1)
|
|
59
|
-
let j = 2
|
|
60
|
-
for (let i = 0; i < points.length; i++) {
|
|
61
|
-
if (i === min) {
|
|
62
|
-
continue
|
|
63
|
-
}
|
|
64
|
-
stack[j] = al[j - 2].index
|
|
65
|
-
j++
|
|
66
|
-
}
|
|
67
|
-
stack[0] = stack[points.length]
|
|
68
|
-
stack[1] = min
|
|
18
|
+
// gather information for sorting by polar coordinates (point, angle, distSq)
|
|
19
|
+
const points = []
|
|
20
|
+
uniquePoints.forEach((point) => {
|
|
21
|
+
// use faster fakeAtan2 instead of Math.atan2
|
|
22
|
+
const angle = fakeAtan2(point[1] - min[1], point[0] - min[0])
|
|
23
|
+
const distSq = vec2.squaredDistance(point, min)
|
|
24
|
+
points.push({ point, angle, distSq })
|
|
25
|
+
})
|
|
69
26
|
|
|
70
|
-
//
|
|
71
|
-
|
|
27
|
+
// sort by polar coordinates
|
|
28
|
+
points.sort((pt1, pt2) => pt1.angle < pt2.angle ? -1 : pt1.angle > pt2.angle ? 1 :
|
|
29
|
+
pt1.distSq < pt2.distSq ? -1 : pt1.distSq > pt2.distSq ? 1 : 0)
|
|
72
30
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
while (ccw(stack[
|
|
77
|
-
|
|
31
|
+
const stack = [] // start with empty stack
|
|
32
|
+
points.forEach((point) => {
|
|
33
|
+
let cnt = stack.length
|
|
34
|
+
while (cnt > 1 && ccw(stack[cnt - 2], stack[cnt - 1], point.point) <= Number.EPSILON) {
|
|
35
|
+
stack.pop() // get rid of colinear and interior (clockwise) points
|
|
36
|
+
cnt = stack.length
|
|
78
37
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
stack[i] = stack[M]
|
|
82
|
-
stack[M] = tmp
|
|
83
|
-
}
|
|
38
|
+
stack.push(point.point)
|
|
39
|
+
})
|
|
84
40
|
|
|
85
|
-
|
|
86
|
-
const indices = new Array(M)
|
|
87
|
-
for (let i = 0; i < M; i++) {
|
|
88
|
-
indices[i] = stack[i + 1]
|
|
89
|
-
}
|
|
90
|
-
return indices
|
|
41
|
+
return stack
|
|
91
42
|
}
|
|
92
43
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* @param {Array} uniquepoints - list of UNIQUE points from which to create a hull
|
|
96
|
-
* @returns {Array} a list of points that form the hull
|
|
97
|
-
*/
|
|
98
|
-
const hullPoints2 = (uniquepoints) => {
|
|
99
|
-
const indices = compute(uniquepoints)
|
|
44
|
+
// returns: < 0 clockwise, 0 colinear, > 0 counter-clockwise
|
|
45
|
+
const ccw = (v1, v2, v3) => (v2[0] - v1[0]) * (v3[1] - v1[1]) - (v2[1] - v1[1]) * (v3[0] - v1[0])
|
|
100
46
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
47
|
+
// Returned "angle" is really 1/tan (inverse of slope) made negative to increase with angle.
|
|
48
|
+
// This function is strictly for sorting in this algorithm.
|
|
49
|
+
const fakeAtan2 = (y, x) => {
|
|
50
|
+
// The "if" is a special case for when the minimum vector found in loop above is present.
|
|
51
|
+
// We need to ensure that it sorts as the minimum point. Otherwise, this becomes NaN.
|
|
52
|
+
if (y === 0 && x === 0) {
|
|
53
|
+
return -Infinity
|
|
54
|
+
} else {
|
|
55
|
+
return -x / y
|
|
104
56
|
}
|
|
105
|
-
return hullpoints
|
|
106
57
|
}
|
|
107
58
|
|
|
108
59
|
module.exports = hullPoints2
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const geom2 = require('../../geometries/geom2')
|
|
2
|
+
const geom3 = require('../../geometries/geom3')
|
|
3
|
+
const path2 = require('../../geometries/path2')
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Return the unique vertices of a geometry
|
|
7
|
+
*/
|
|
8
|
+
const toUniquePoints = (geometries) => {
|
|
9
|
+
const found = new Set()
|
|
10
|
+
const uniquePoints = []
|
|
11
|
+
|
|
12
|
+
const addPoint = (point) => {
|
|
13
|
+
const key = point.toString()
|
|
14
|
+
if (!found.has(key)) {
|
|
15
|
+
uniquePoints.push(point)
|
|
16
|
+
found.add(key)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
geometries.forEach((geometry) => {
|
|
21
|
+
if (geom2.isA(geometry)) {
|
|
22
|
+
geom2.toPoints(geometry).forEach(addPoint)
|
|
23
|
+
} else if (geom3.isA(geometry)) {
|
|
24
|
+
// points are grouped by polygon
|
|
25
|
+
geom3.toPoints(geometry).forEach((points) => points.forEach(addPoint))
|
|
26
|
+
} else if (path2.isA(geometry)) {
|
|
27
|
+
path2.toPoints(geometry).forEach(addPoint)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return uniquePoints
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = toUniquePoints
|
|
@@ -11,8 +11,6 @@ const mergePolygons = require('./mergePolygons')
|
|
|
11
11
|
const insertTjunctions = require('./insertTjunctions')
|
|
12
12
|
const triangulatePolygons = require('./triangulatePolygons')
|
|
13
13
|
|
|
14
|
-
const repairTjunctions = require('./repairTjunctions')
|
|
15
|
-
|
|
16
14
|
/*
|
|
17
15
|
*/
|
|
18
16
|
const generalizePath2 = (options, geometry) => geometry
|
|
@@ -27,10 +25,9 @@ const generalizeGeom3 = (options, geometry) => {
|
|
|
27
25
|
const defaults = {
|
|
28
26
|
snap: false,
|
|
29
27
|
simplify: false,
|
|
30
|
-
triangulate: false
|
|
31
|
-
repair: false
|
|
28
|
+
triangulate: false
|
|
32
29
|
}
|
|
33
|
-
const { snap, simplify, triangulate
|
|
30
|
+
const { snap, simplify, triangulate } = Object.assign({}, defaults, options)
|
|
34
31
|
|
|
35
32
|
const epsilon = measureEpsilon(geometry)
|
|
36
33
|
let polygons = geom3.toPolygons(geometry)
|
|
@@ -52,13 +49,6 @@ const generalizeGeom3 = (options, geometry) => {
|
|
|
52
49
|
polygons = triangulatePolygons(epsilon, polygons)
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
// repair the polygons (possibly triangles) if requested
|
|
56
|
-
if (repair) {
|
|
57
|
-
// fix T junctions
|
|
58
|
-
polygons = repairTjunctions(epsilon, polygons)
|
|
59
|
-
// TODO fill holes
|
|
60
|
-
}
|
|
61
|
-
|
|
62
52
|
// FIXME replace with geom3.cloneShallow() when available
|
|
63
53
|
const clone = Object.assign({}, geometry)
|
|
64
54
|
clone.polygons = polygons
|
|
@@ -72,7 +62,6 @@ const generalizeGeom3 = (options, geometry) => {
|
|
|
72
62
|
* @param {Boolean} [options.snap=false] the geometries should be snapped to epsilons
|
|
73
63
|
* @param {Boolean} [options.simplify=false] the geometries should be simplified
|
|
74
64
|
* @param {Boolean} [options.triangulate=false] the geometries should be triangulated
|
|
75
|
-
* @param {Boolean} [options.repair=false] the geometries should be repaired
|
|
76
65
|
* @param {...Object} geometries - the geometries to generalize
|
|
77
66
|
* @return {Object|Array} the modified geometry, or a list of modified geometries
|
|
78
67
|
* @alias module:modeling/modifiers.generalize
|
|
@@ -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,37 +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
|
]
|
|
102
|
-
t.
|
|
103
|
-
|
|
104
|
-
// apply repairs only (triangles)
|
|
105
|
-
result = generalize({ repair: true }, geometry2)
|
|
106
|
-
pts = geom3.toPoints(result)
|
|
107
|
-
exp = [
|
|
108
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
109
|
-
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
110
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
111
|
-
[-1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
|
|
112
|
-
[[1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, -3.141592653589793],
|
|
113
|
-
[1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
114
|
-
[[1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
115
|
-
[1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
|
|
116
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, -0.7853981633974483, -3.141592653589793],
|
|
117
|
-
[1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
|
|
118
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
119
|
-
[-1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
|
|
120
|
-
[[-1.5707963267948966, 0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
121
|
-
[1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
122
|
-
[[-1.5707963267948966, 0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
123
|
-
[1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
|
|
124
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, -3.141592653589793],
|
|
125
|
-
[1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
|
|
126
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, -3.141592653589793],
|
|
127
|
-
[1.5707963267948966, -0.7853981633974483, -3.141592653589793]],
|
|
128
|
-
[[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
129
|
-
[1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
130
|
-
[[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
131
|
-
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793]]
|
|
132
|
-
]
|
|
105
|
+
t.notThrows(() => geom3.validate(result))
|
|
133
106
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
134
107
|
})
|
|
135
108
|
|
|
@@ -190,5 +163,6 @@ test('generalize: generalize of a geom3 with T junctions produces an expected ge
|
|
|
190
163
|
[[0, 1, 1], [-1, 1, 1], [0, 0, 1]],
|
|
191
164
|
[[-1, 1, 1], [-1, 0, 1], [0, 0, 1]]
|
|
192
165
|
]
|
|
166
|
+
t.notThrows(() => geom3.validate(result))
|
|
193
167
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
194
168
|
})
|
|
@@ -259,7 +259,7 @@ const insertTjunctions = (polygons) => {
|
|
|
259
259
|
// split the side by inserting the vertex:
|
|
260
260
|
const newvertices = polygon.vertices.slice(0)
|
|
261
261
|
newvertices.splice(insertionvertextagindex, 0, endvertex)
|
|
262
|
-
const newpolygon = poly3.
|
|
262
|
+
const newpolygon = poly3.create(newvertices)
|
|
263
263
|
|
|
264
264
|
newpolygons[polygonindex] = newpolygon
|
|
265
265
|
|
|
@@ -56,33 +56,33 @@ test('insertTjunctions: insertTjunctions produces expected polygons', (t) => {
|
|
|
56
56
|
|
|
57
57
|
const result3 = insertTjunctions(geom3.toPolygons(geometry3))
|
|
58
58
|
let exp = [
|
|
59
|
-
poly3.
|
|
60
|
-
poly3.
|
|
61
|
-
poly3.
|
|
62
|
-
poly3.
|
|
63
|
-
poly3.
|
|
64
|
-
poly3.
|
|
65
|
-
poly3.
|
|
66
|
-
poly3.
|
|
59
|
+
poly3.create([[-1, -1, -1], [-1, -1, 1], [-1, 1, 1], [-1, 1, -1]]),
|
|
60
|
+
poly3.create([[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, -1, 1]]),
|
|
61
|
+
poly3.create([[-1, -1, -1], [1, -1, -1], [1, -1, 1], [-1, -1, 1]]),
|
|
62
|
+
poly3.create([[-1, 1, -1], [-1, 1, 1], [1, 1, 1], [1, 1, -1]]),
|
|
63
|
+
poly3.create([[-1, -1, -1], [-1, 1, -1], [1, 1, -1], [1, -1, -1]]),
|
|
64
|
+
poly3.create([[0, 0, 1], [-1, -1, 1], [1, -1, 1], [1, 1, 1]]),
|
|
65
|
+
poly3.create([[1, 1, 1], [-1, 1, 1], [0, 0, 1]]),
|
|
66
|
+
poly3.create([[-1, 1, 1], [-1, -1, 1], [0, 0, 1]])
|
|
67
67
|
]
|
|
68
68
|
t.not(result3, geom3.toPolygons(geometry3))
|
|
69
69
|
t.true(comparePolygonLists(result3, exp))
|
|
70
70
|
|
|
71
71
|
const result4 = insertTjunctions(geom3.toPolygons(geometry4))
|
|
72
72
|
exp = [
|
|
73
|
-
poly3.
|
|
74
|
-
poly3.
|
|
75
|
-
poly3.
|
|
76
|
-
poly3.
|
|
77
|
-
poly3.
|
|
78
|
-
poly3.
|
|
79
|
-
poly3.
|
|
80
|
-
poly3.
|
|
81
|
-
poly3.
|
|
82
|
-
poly3.
|
|
83
|
-
poly3.
|
|
84
|
-
poly3.
|
|
85
|
-
poly3.
|
|
73
|
+
poly3.create([[-1, -1, -1], [-1, -1, 1], [-1, 0, 1], [-1, 1, 1], [-1, 1, -1]]),
|
|
74
|
+
poly3.create([[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, 0, 1], [1, -1, 1]]),
|
|
75
|
+
poly3.create([[-1, -1, -1], [1, -1, -1], [1, -1, 1], [0, -1, 1], [-1, -1, 1]]),
|
|
76
|
+
poly3.create([[-1, 1, -1], [-1, 1, 1], [0, 1, 1], [1, 1, 1], [1, 1, -1]]),
|
|
77
|
+
poly3.create([[-1, -1, -1], [-1, 1, -1], [1, 1, -1], [1, -1, -1]]),
|
|
78
|
+
poly3.create([[-1, -1, 1], [0, -1, 1], [0, 0, 1]]),
|
|
79
|
+
poly3.create([[-1, 0, 1], [-1, -1, 1], [0, 0, 1]]),
|
|
80
|
+
poly3.create([[0, -1, 1], [1, -1, 1], [0, 0, 1]]),
|
|
81
|
+
poly3.create([[1, -1, 1], [1, 0, 1], [0, 0, 1]]),
|
|
82
|
+
poly3.create([[1, 0, 1], [1, 1, 1], [0, 0, 1]]),
|
|
83
|
+
poly3.create([[1, 1, 1], [0, 1, 1], [0, 0, 1]]),
|
|
84
|
+
poly3.create([[0, 1, 1], [-1, 1, 1], [0, 0, 1]]),
|
|
85
|
+
poly3.create([[-1, 1, 1], [-1, 0, 1], [0, 0, 1]])
|
|
86
86
|
]
|
|
87
87
|
t.not(result4, geometry4)
|
|
88
88
|
t.true(comparePolygonLists(result4, exp))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const aboutEqualNormals = require('../../maths/utils/aboutEqualNormals')
|
|
1
2
|
const vec3 = require('../../maths/vec3')
|
|
2
3
|
|
|
3
4
|
const poly3 = require('../../geometries/poly3')
|
|
@@ -53,9 +54,12 @@ const calculateAnglesBetween = (current, opposite, normal) => {
|
|
|
53
54
|
return [angle1, angle2]
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
const v1 = vec3.create()
|
|
58
|
+
const v2 = vec3.create()
|
|
59
|
+
|
|
56
60
|
const calculateAngle = (prevpoint, point, nextpoint, normal) => {
|
|
57
|
-
const d0 = vec3.subtract(
|
|
58
|
-
const d1 = vec3.subtract(
|
|
61
|
+
const d0 = vec3.subtract(v1, point, prevpoint)
|
|
62
|
+
const d1 = vec3.subtract(v2, nextpoint, point)
|
|
59
63
|
vec3.cross(d0, d0, d1)
|
|
60
64
|
return vec3.dot(d0, normal)
|
|
61
65
|
}
|
|
@@ -76,7 +80,7 @@ const createPolygonAnd = (edge) => {
|
|
|
76
80
|
|
|
77
81
|
edge = next
|
|
78
82
|
}
|
|
79
|
-
if (points.length > 0) polygon = poly3.
|
|
83
|
+
if (points.length > 0) polygon = poly3.create(points)
|
|
80
84
|
return polygon
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -85,7 +89,7 @@ const createPolygonAnd = (edge) => {
|
|
|
85
89
|
* @param {poly3[]} sourcepolygons - list of polygons
|
|
86
90
|
* @returns {poly3[]} new set of polygons
|
|
87
91
|
*/
|
|
88
|
-
const mergeCoplanarPolygons = (
|
|
92
|
+
const mergeCoplanarPolygons = (sourcepolygons) => {
|
|
89
93
|
if (sourcepolygons.length < 2) return sourcepolygons
|
|
90
94
|
|
|
91
95
|
const normal = sourcepolygons[0].plane
|
|
@@ -167,18 +171,11 @@ const mergeCoplanarPolygons = (epsilon, sourcepolygons) => {
|
|
|
167
171
|
if (polygon) destpolygons.push(polygon)
|
|
168
172
|
})
|
|
169
173
|
|
|
174
|
+
edgeList.clear()
|
|
175
|
+
|
|
170
176
|
return destpolygons
|
|
171
177
|
}
|
|
172
178
|
|
|
173
|
-
// Normals are directional vectors with component values from 0 to 1.0, requiring specialized comparision
|
|
174
|
-
// This EPS is derived from a serieas of tests to determine the optimal precision for comparing coplanar polygons,
|
|
175
|
-
// as provided by the sphere primitive at high segmentation
|
|
176
|
-
// This EPS is for 64 bit Number values
|
|
177
|
-
const NEPS = 1e-13
|
|
178
|
-
|
|
179
|
-
// Compare two normals (unit vectors) for equality.
|
|
180
|
-
const aboutEqualNormals = (a, b) => (Math.abs(a[0] - b[0]) <= NEPS && Math.abs(a[1] - b[1]) <= NEPS && Math.abs(a[2] - b[2]) <= NEPS)
|
|
181
|
-
|
|
182
179
|
const coplanar = (plane1, plane2) => {
|
|
183
180
|
// expect the same distance from the origin, within tolerance
|
|
184
181
|
if (Math.abs(plane1[3] - plane2[3]) < 0.00000015) {
|
|
@@ -202,7 +199,7 @@ const mergePolygons = (epsilon, polygons) => {
|
|
|
202
199
|
let destpolygons = []
|
|
203
200
|
polygonsPerPlane.forEach((mapping) => {
|
|
204
201
|
const sourcepolygons = mapping[1]
|
|
205
|
-
const retesselayedpolygons = mergeCoplanarPolygons(
|
|
202
|
+
const retesselayedpolygons = mergeCoplanarPolygons(sourcepolygons)
|
|
206
203
|
destpolygons = destpolygons.concat(retesselayedpolygons)
|
|
207
204
|
})
|
|
208
205
|
return destpolygons
|
|
@@ -31,7 +31,7 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
|
|
|
31
31
|
// convert all polygon vertices to 2D
|
|
32
32
|
// Make a list of all encountered y coordinates
|
|
33
33
|
// And build a map of all polygons that have a vertex at a certain y coordinate:
|
|
34
|
-
const ycoordinateBinningFactor =
|
|
34
|
+
const ycoordinateBinningFactor = 10 / EPS
|
|
35
35
|
for (let polygonindex = 0; polygonindex < numpolygons; polygonindex++) {
|
|
36
36
|
const poly3d = sourcepolygons[polygonindex]
|
|
37
37
|
let vertices2d = []
|
|
@@ -17,11 +17,11 @@ const rotatePoly3 = (angles, polygon) => {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
test.only('retessellateCoplanarPolygons: should merge coplanar polygons', (t) => {
|
|
20
|
-
const polyA = poly3.
|
|
21
|
-
const polyB = poly3.
|
|
22
|
-
const polyC = poly3.
|
|
23
|
-
const polyD = poly3.
|
|
24
|
-
const polyE = poly3.
|
|
20
|
+
const polyA = poly3.create([[-5, -5, 0], [5, -5, 0], [5, 5, 0], [-5, 5, 0]])
|
|
21
|
+
const polyB = poly3.create([[5, -5, 0], [8, 0, 0], [5, 5, 0]])
|
|
22
|
+
const polyC = poly3.create([[-5, 5, 0], [-8, 0, 0], [-5, -5, 0]])
|
|
23
|
+
const polyD = poly3.create([[-5, 5, 0], [5, 5, 0], [0, 8, 0]])
|
|
24
|
+
const polyE = poly3.create([[5, -5, 0], [-5, -5, 0], [0, -8, 0]])
|
|
25
25
|
|
|
26
26
|
// combine polygons in each direction
|
|
27
27
|
let obs = reTesselateCoplanarPolygons([polyA, polyB])
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
const geom3 = require('../../geometries/geom3')
|
|
2
2
|
const poly3 = require('../../geometries/poly3')
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
// Normals are directional vectors with component values from 0 to 1.0, requiring specialized comparison
|
|
7
|
-
// This EPS is derived from a series of tests to determine the optimal precision for comparing coplanar polygons,
|
|
8
|
-
// as provided by the sphere primitive at high segmentation
|
|
9
|
-
// This EPS is for 64 bit Number values
|
|
10
|
-
const NEPS = 1e-13
|
|
4
|
+
const aboutEqualNormals = require('../../maths/utils/aboutEqualNormals')
|
|
11
5
|
|
|
12
|
-
|
|
13
|
-
const aboutEqualNormals = (a, b) => (Math.abs(a[0] - b[0]) <= NEPS && Math.abs(a[1] - b[1]) <= NEPS && Math.abs(a[2] - b[2]) <= NEPS)
|
|
6
|
+
const reTesselateCoplanarPolygons = require('./reTesselateCoplanarPolygons')
|
|
14
7
|
|
|
15
8
|
const coplanar = (plane1, plane2) => {
|
|
16
9
|
// expect the same distance from the origin, within tolerance
|
|
File without changes
|
|
@@ -7,49 +7,49 @@ const snapPolygons = require('./snapPolygons')
|
|
|
7
7
|
test('snapPolygons: snap of polygons produces expected results', (t) => {
|
|
8
8
|
const polygons = [
|
|
9
9
|
// valid polygons
|
|
10
|
-
poly3.
|
|
11
|
-
poly3.
|
|
10
|
+
poly3.create([[0, 0, 0], [0, 10, 0], [0, 10, 10]]), // OK
|
|
11
|
+
poly3.create([[0, 0, 0], [0, 10, 0], [0, 10, 10], [0, 0, 10]]), // OK
|
|
12
12
|
// invalid polygons
|
|
13
13
|
poly3.create(),
|
|
14
|
-
poly3.
|
|
15
|
-
poly3.
|
|
14
|
+
poly3.create([[0, 0, 0]]),
|
|
15
|
+
poly3.create([[0, 0, 0], [0, 10, 0]]),
|
|
16
16
|
// duplicated vertices
|
|
17
|
-
poly3.
|
|
17
|
+
poly3.create([
|
|
18
18
|
[-24.445112000000115, 19.346837333333426, 46.47572533333356],
|
|
19
19
|
[-24.44446933333345, 19.346837333333426, 46.47508266666689],
|
|
20
20
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
21
21
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019]
|
|
22
22
|
]), // OK
|
|
23
|
-
poly3.
|
|
23
|
+
poly3.create([
|
|
24
24
|
[-24.445112000000115, 19.346837333333426, 46.47572533333356],
|
|
25
25
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
26
26
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019]
|
|
27
27
|
]),
|
|
28
|
-
poly3.
|
|
28
|
+
poly3.create([
|
|
29
29
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
30
30
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019]
|
|
31
31
|
]),
|
|
32
32
|
// duplicate vertices after snap
|
|
33
|
-
poly3.
|
|
33
|
+
poly3.create([
|
|
34
34
|
[-24.445112000000115, 19.346837333333426, 46.47572533333356],
|
|
35
35
|
[-24.44446933333345, 19.346837333333426, 46.47508266666689],
|
|
36
36
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
37
37
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
|
|
38
38
|
]), // OK
|
|
39
|
-
poly3.
|
|
39
|
+
poly3.create([
|
|
40
40
|
[-24.445112000000115, 19.346837333333426, 46.47572533333356],
|
|
41
41
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
|
|
42
42
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
43
43
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
|
|
44
44
|
]),
|
|
45
|
-
poly3.
|
|
45
|
+
poly3.create([
|
|
46
46
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
47
47
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
|
|
48
48
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
49
49
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
|
|
50
50
|
]),
|
|
51
51
|
// inverted polygon
|
|
52
|
-
poly3.
|
|
52
|
+
poly3.create([
|
|
53
53
|
[20.109133333333336, -4.894033333333335, -1.0001266666666668],
|
|
54
54
|
[20.021120000000003, -5.1802133333333344, -1.0001266666666668],
|
|
55
55
|
[20.020300000000002, -5.182946666666668, -1.0001266666666668],
|
|
@@ -61,7 +61,7 @@ test('snapPolygons: snap of polygons produces expected results', (t) => {
|
|
|
61
61
|
const results = snapPolygons(0.0001, polygons)
|
|
62
62
|
t.is(results.length, 5)
|
|
63
63
|
|
|
64
|
-
const exp3 = poly3.
|
|
64
|
+
const exp3 = poly3.create([
|
|
65
65
|
[-24.4451, 19.3468, 46.4757],
|
|
66
66
|
[-24.4445, 19.3468, 46.475100000000005],
|
|
67
67
|
[-23.7054, 18.7986, 39.5645]
|
|
@@ -10,15 +10,15 @@ const triangulatePolygon = (epsilon, polygon, triangles) => {
|
|
|
10
10
|
polygon.vertices.forEach((vertice) => vec3.add(midpoint, midpoint, vertice))
|
|
11
11
|
vec3.snap(midpoint, vec3.divide(midpoint, midpoint, [nv, nv, nv]), epsilon)
|
|
12
12
|
for (let i = 0; i < nv; i++) {
|
|
13
|
-
const poly = poly3.
|
|
13
|
+
const poly = poly3.create([midpoint, polygon.vertices[i], polygon.vertices[(i + 1) % nv]])
|
|
14
14
|
if (polygon.color) poly.color = polygon.color
|
|
15
15
|
triangles.push(poly)
|
|
16
16
|
}
|
|
17
17
|
return
|
|
18
18
|
}
|
|
19
19
|
// exactly 4 vertices, use simple triangulation
|
|
20
|
-
const poly0 = poly3.
|
|
21
|
-
const poly1 = poly3.
|
|
20
|
+
const poly0 = poly3.create([polygon.vertices[0], polygon.vertices[1], polygon.vertices[2]])
|
|
21
|
+
const poly1 = poly3.create([polygon.vertices[0], polygon.vertices[2], polygon.vertices[3]])
|
|
22
22
|
if (polygon.color) {
|
|
23
23
|
poly0.color = polygon.color
|
|
24
24
|
poly1.color = polygon.color
|
|
@@ -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
|
|
|
@@ -39,7 +39,7 @@ const center = (options, ...objects) => {
|
|
|
39
39
|
const defaults = {
|
|
40
40
|
axes: [true, true, true],
|
|
41
41
|
relativeTo: [0, 0, 0]
|
|
42
|
-
// TODO
|
|
42
|
+
// TODO: Add additional 'methods' of centering: midpoint, centroid
|
|
43
43
|
}
|
|
44
44
|
const { axes, relativeTo } = Object.assign({}, defaults, options)
|
|
45
45
|
|