@jscad/modeling 2.5.2 → 2.7.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/jscad-modeling.min.js +217 -202
  3. package/package.json +2 -2
  4. package/src/colors/colorize.js +4 -5
  5. package/src/colors/colorize.test.js +19 -12
  6. package/src/geometries/geom2/applyTransforms.js +1 -1
  7. package/src/geometries/geom2/clone.js +2 -12
  8. package/src/geometries/geom2/transform.js +2 -6
  9. package/src/geometries/geom3/applyTransforms.js +1 -1
  10. package/src/geometries/geom3/clone.js +2 -14
  11. package/src/geometries/geom3/clone.test.js +0 -2
  12. package/src/geometries/geom3/create.js +0 -2
  13. package/src/geometries/geom3/create.test.js +0 -2
  14. package/src/geometries/geom3/fromCompactBinary.js +4 -6
  15. package/src/geometries/geom3/fromToCompactBinary.test.js +0 -6
  16. package/src/geometries/geom3/invert.test.js +0 -2
  17. package/src/geometries/geom3/toCompactBinary.js +8 -10
  18. package/src/geometries/geom3/transform.js +2 -7
  19. package/src/geometries/geom3/transform.test.js +0 -1
  20. package/src/geometries/geom3/type.d.ts +0 -1
  21. package/src/geometries/path2/applyTransforms.js +1 -1
  22. package/src/geometries/path2/clone.js +2 -13
  23. package/src/geometries/path2/transform.js +2 -7
  24. package/src/geometries/poly3/isConvex.js +1 -1
  25. package/src/geometries/poly3/measureArea.js +12 -13
  26. package/src/geometries/poly3/measureArea.test.js +15 -0
  27. package/src/geometries/poly3/plane.js +1 -2
  28. package/src/maths/mat4/index.js +1 -0
  29. package/src/maths/mat4/isOnlyTransformScale.js +21 -0
  30. package/src/maths/mat4/isOnlyTransformScale.test.js +27 -0
  31. package/src/maths/plane/fromPoints.js +32 -10
  32. package/src/maths/plane/fromPoints.test.js +4 -0
  33. package/src/maths/utils/index.d.ts +0 -1
  34. package/src/maths/utils/index.js +0 -1
  35. package/src/maths/vec2/rotate.js +1 -1
  36. package/src/maths/vec2/rotate.test.js +3 -3
  37. package/src/measurements/index.js +4 -1
  38. package/src/measurements/measureBoundingBox.js +128 -38
  39. package/src/measurements/measureBoundingBox.test.js +8 -0
  40. package/src/measurements/measureBoundingSphere.js +146 -0
  41. package/src/measurements/measureBoundingSphere.test.js +59 -0
  42. package/src/measurements/measureCenter.js +28 -0
  43. package/src/measurements/measureCenter.test.js +58 -0
  44. package/src/measurements/measureCenterOfMass.js +106 -0
  45. package/src/measurements/measureCenterOfMass.test.js +58 -0
  46. package/src/measurements/measureDimensions.js +28 -0
  47. package/src/measurements/measureDimensions.test.js +58 -0
  48. package/src/measurements/measureEpsilon.js +3 -9
  49. package/src/operations/booleans/reTesselateCoplanarPolygons.js +1 -1
  50. package/src/operations/booleans/trees/PolygonTreeNode.js +0 -1
  51. package/src/operations/expansions/expand.test.js +1 -1
  52. package/src/operations/extrusions/extrudeRotate.test.js +18 -10
  53. package/src/operations/modifiers/generalize.js +0 -1
  54. package/src/operations/modifiers/snapPolygons.js +2 -2
  55. package/src/operations/modifiers/snapPolygons.test.js +13 -5
  56. package/src/primitives/index.d.ts +1 -0
  57. package/src/primitives/index.js +2 -1
  58. package/src/primitives/triangle.d.ts +10 -0
  59. package/src/primitives/triangle.js +164 -0
  60. package/src/primitives/triangle.test.js +95 -0
  61. package/src/maths/utils/clamp.d.ts +0 -3
  62. package/src/maths/utils/clamp.js +0 -4
  63. package/src/maths/utils/quantizeForSpace.d.ts +0 -3
  64. package/src/maths/utils/quantizeForSpace.js +0 -6
@@ -13,4 +13,8 @@ test('plane: fromPoints() should return a new plane with correct values', (t) =>
13
13
  // planes created from the same points results in an invalid plane
14
14
  const obs3 = fromPoints(obs1, [0, 6, 0], [0, 6, 0], [0, 6, 0])
15
15
  t.true(compareVectors(obs3, [0 / 0, 0 / 0, 0 / 0, 0 / 0]))
16
+
17
+ // polygon with co-linear vertices
18
+ const obs4 = fromPoints(obs1, [0, 0, 0], [1, 0, 0], [2, 0, 0], [0, 1, 0])
19
+ t.true(compareVectors(obs4, [0, 0, 1, 0]))
16
20
  })
@@ -1,6 +1,5 @@
1
1
  export { default as aboutEqualNormals } from './aboutEqualNormals'
2
2
  export { default as area } from './area'
3
- export { default as clamp } from './clamp'
4
3
  export { default as interpolateBetween2DPointsForY } from './interpolateBetween2DPointsForY'
5
4
  export { default as intersect } from './intersect'
6
5
  export { default as solve2Linear } from './solve2Linear'
@@ -7,7 +7,6 @@
7
7
  module.exports = {
8
8
  aboutEqualNormals: require('./aboutEqualNormals'),
9
9
  area: require('./area'),
10
- clamp: require('./clamp'),
11
10
  interpolateBetween2DPointsForY: require('./interpolateBetween2DPointsForY'),
12
11
  intersect: require('./intersect'),
13
12
  solve2Linear: require('./solve2Linear')
@@ -10,7 +10,7 @@
10
10
  */
11
11
  const rotate = (out, vector, origin, radians) => {
12
12
  const x = vector[0] - origin[0]
13
- const y = vector[1] - origin[0]
13
+ const y = vector[1] - origin[1]
14
14
  const c = Math.cos(radians)
15
15
  const s = Math.sin(radians)
16
16
 
@@ -22,7 +22,7 @@ test('vec2: rotate() called with three paramerters should update a vec2 with cor
22
22
  t.true(compareVectors(ret3, [2, -1], 1e-15))
23
23
 
24
24
  const obs4 = fromValues(0, 0)
25
- const ret4 = rotate(obs4, [-1, 2], [0, 0], -radians)
26
- t.true(compareVectors(obs4, [2, 1], 1e-15))
27
- t.true(compareVectors(ret4, [2, 1], 1e-15))
25
+ const ret4 = rotate(obs4, [-1, 2], [-3, -3], -radians)
26
+ t.true(compareVectors(obs4, [2, -5], 1e-15))
27
+ t.true(compareVectors(ret4, [2, -5], 1e-15))
28
28
  })
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * All shapes (primitives or the results of operations) can be measured, e.g. calculate volume, etc.
3
- * In all cases, the function returns the results, and never changes the original shapes.
4
3
  * @module modeling/measurements
5
4
  * @example
6
5
  * const { measureArea, measureBoundingBox, measureVolume } = require('@jscad/modeling').measurements
@@ -12,6 +11,10 @@ module.exports = {
12
11
  measureAggregateVolume: require('./measureAggregateVolume'),
13
12
  measureArea: require('./measureArea'),
14
13
  measureBoundingBox: require('./measureBoundingBox'),
14
+ measureBoundingSphere: require('./measureBoundingSphere'),
15
+ measureCenter: require('./measureCenter'),
16
+ measureCenterOfMass: require('./measureCenterOfMass'),
17
+ measureDimensions: require('./measureDimensions'),
15
18
  measureEpsilon: require('./measureEpsilon'),
16
19
  measureVolume: require('./measureVolume')
17
20
  }
@@ -2,6 +2,7 @@ const flatten = require('../utils/flatten')
2
2
 
3
3
  const vec2 = require('../maths/vec2')
4
4
  const vec3 = require('../maths/vec3')
5
+ const mat4 = require('../maths/mat4')
5
6
 
6
7
  const geom2 = require('../geometries/geom2')
7
8
  const geom3 = require('../geometries/geom3')
@@ -11,86 +12,129 @@ const poly3 = require('../geometries/poly3')
11
12
  const cache = new WeakMap()
12
13
 
13
14
  /*
14
- * Measure the min and max bounds of the given (path2) geometry.
15
- * @return {Array[]} the min and max bounds for the geometry
15
+ * Measure the min and max bounds of the given (path2) geometry.points.
16
+ * @return {Array[]} the min and max bounds for the geometry.points
16
17
  */
17
- const measureBoundingBoxOfPath2 = (geometry) => {
18
- let boundingBox = cache.get(geometry)
18
+ const measureBoundingBoxOfPath2Points = (points) => {
19
+ let boundingBox = cache.get(points)
19
20
  if (boundingBox) return boundingBox
20
21
 
21
- const points = path2.toPoints(geometry)
22
-
23
22
  let minpoint
24
23
  if (points.length === 0) {
25
24
  minpoint = vec2.create()
26
25
  } else {
27
26
  minpoint = vec2.clone(points[0])
28
27
  }
29
- let maxpoint = vec2.clone(minpoint)
28
+ const maxpoint = vec2.clone(minpoint)
30
29
 
31
30
  points.forEach((point) => {
32
31
  vec2.min(minpoint, minpoint, point)
33
32
  vec2.max(maxpoint, maxpoint, point)
34
33
  })
35
- minpoint = [minpoint[0], minpoint[1], 0]
36
- maxpoint = [maxpoint[0], maxpoint[1], 0]
37
-
38
- boundingBox = [minpoint, maxpoint]
39
-
40
- cache.set(geometry, boundingBox)
34
+ boundingBox = [[minpoint[0], minpoint[1], 0], [maxpoint[0], maxpoint[1], 0]]
41
35
 
36
+ cache.set(points, boundingBox)
42
37
  return boundingBox
43
38
  }
44
39
 
45
40
  /*
46
- * Measure the min and max bounds of the given (geom2) geometry.
41
+ * Measure the min and max bounds of the given (path2) geometry.
47
42
  * @return {Array[]} the min and max bounds for the geometry
48
43
  */
49
- const measureBoundingBoxOfGeom2 = (geometry) => {
44
+ const measureBoundingBoxOfPath2 = (geometry) => {
50
45
  let boundingBox = cache.get(geometry)
51
46
  if (boundingBox) return boundingBox
52
47
 
53
- const points = geom2.toPoints(geometry)
54
-
55
- let minpoint
56
- if (points.length === 0) {
57
- minpoint = vec2.create()
48
+ if (mat4.isOnlyTransformScale(geometry.transforms)) {
49
+ // get boundingBox of original points and transform it
50
+ boundingBox = transformBoundingBox(measureBoundingBoxOfPath2Points(geometry.points), geometry.transforms)
58
51
  } else {
59
- minpoint = vec2.clone(points[0])
52
+ // transform the points and then caclulate the boundingBox
53
+ boundingBox = measureBoundingBoxOfPath2Points(path2.toPoints(geometry))
60
54
  }
61
- let maxpoint = vec2.clone(minpoint)
62
55
 
63
- points.forEach((point) => {
64
- vec2.min(minpoint, minpoint, point)
65
- vec2.max(maxpoint, maxpoint, point)
66
- })
56
+ cache.set(geometry, boundingBox)
57
+ return boundingBox
58
+ }
67
59
 
68
- minpoint = [minpoint[0], minpoint[1], 0]
69
- maxpoint = [maxpoint[0], maxpoint[1], 0]
60
+ /*
61
+ * Measure the min and max bounds of the given (geom2) geometry.points/sides.
62
+ * @return {Array[]} the min and max bounds for the geometr.points/sidesy
63
+ */
64
+ const measureBoundingBoxOfGeom2Points = ({ points, sides }) => {
65
+ const cacheKey = points || sides
70
66
 
71
- boundingBox = [minpoint, maxpoint]
67
+ let boundingBox = cache.get(cacheKey)
68
+ if (boundingBox) return boundingBox
72
69
 
73
- cache.set(geometry, boundingBox)
70
+ let minpoint, maxpoint
71
+
72
+ if (points) {
73
+ if (points.length === 0) {
74
+ minpoint = vec2.create()
75
+ } else {
76
+ minpoint = vec2.clone(points[0])
77
+ }
78
+ maxpoint = vec2.clone(minpoint)
79
+
80
+ points.forEach((point) => {
81
+ vec2.min(minpoint, minpoint, point)
82
+ vec2.max(maxpoint, maxpoint, point)
83
+ })
84
+ } else { // sides
85
+ // to avoid calling costly toPoints, we take advantage of the knowlege how the toPoints works
86
+ if (sides.length === 0) {
87
+ minpoint = vec2.create()
88
+ } else {
89
+ minpoint = vec2.clone(sides[0][0])
90
+ }
91
+ maxpoint = vec2.clone(minpoint)
92
+
93
+ sides.forEach((side) => {
94
+ vec2.min(minpoint, minpoint, side[0])
95
+ vec2.max(maxpoint, maxpoint, side[0])
96
+ })
97
+ }
98
+ boundingBox = [[minpoint[0], minpoint[1], 0], [maxpoint[0], maxpoint[1], 0]]
74
99
 
100
+ cache.set(cacheKey, boundingBox)
75
101
  return boundingBox
76
102
  }
77
103
 
78
104
  /*
79
- * Measure the min and max bounds of the given (geom3) geometry.
105
+ * Measure the min and max bounds of the given (geom2) geometry.
80
106
  * @return {Array[]} the min and max bounds for the geometry
81
107
  */
82
- const measureBoundingBoxOfGeom3 = (geometry) => {
108
+ const measureBoundingBoxOfGeom2 = (geometry) => {
83
109
  let boundingBox = cache.get(geometry)
84
110
  if (boundingBox) return boundingBox
85
111
 
86
- const polygons = geom3.toPolygons(geometry)
112
+ if (mat4.isOnlyTransformScale(geometry.transforms)) {
113
+ // get boundingBox of original points and transform it
114
+ boundingBox = transformBoundingBox(measureBoundingBoxOfGeom2Points(geometry), geometry.transforms)
115
+ } else {
116
+ // transform the points and then caclulate the boundingBox
117
+ boundingBox = measureBoundingBoxOfGeom2Points({ points: geom2.toPoints(geometry) })
118
+ }
119
+
120
+ cache.set(geometry, boundingBox)
121
+ return boundingBox
122
+ }
123
+
124
+ /*
125
+ * Measure the min and max bounds of the given (geom3) geometry.polygons.
126
+ * @return {Array[]} the min and max bounds for the geometry.polygons
127
+ */
128
+ const measureBoundingBoxOfGeom3Polygons = (polygons) => {
129
+ let boundingBox = cache.get(polygons)
130
+ if (boundingBox) return boundingBox
87
131
 
88
- let minpoint = vec3.create()
132
+ const minpoint = vec3.create()
89
133
  if (polygons.length > 0) {
90
134
  const points = poly3.toPoints(polygons[0])
91
135
  vec3.copy(minpoint, points[0])
92
136
  }
93
- let maxpoint = vec3.clone(minpoint)
137
+ const maxpoint = vec3.clone(minpoint)
94
138
 
95
139
  polygons.forEach((polygon) => {
96
140
  poly3.toPoints(polygon).forEach((point) => {
@@ -99,13 +143,59 @@ const measureBoundingBoxOfGeom3 = (geometry) => {
99
143
  })
100
144
  })
101
145
 
102
- minpoint = [minpoint[0], minpoint[1], minpoint[2]]
103
- maxpoint = [maxpoint[0], maxpoint[1], maxpoint[2]]
146
+ boundingBox = [[minpoint[0], minpoint[1], minpoint[2]], [maxpoint[0], maxpoint[1], maxpoint[2]]]
104
147
 
105
- boundingBox = [minpoint, maxpoint]
148
+ cache.set(polygons, boundingBox)
149
+ return boundingBox
150
+ }
151
+
152
+ /*
153
+ * Measure the min and max bounds of the given (geom3) geometry.
154
+ * @return {Array[]} the min and max bounds for the geometry
155
+ */
156
+ const measureBoundingBoxOfGeom3 = (geometry) => {
157
+ let boundingBox = cache.get(geometry)
158
+ if (boundingBox) return boundingBox
159
+
160
+ if (mat4.isOnlyTransformScale(geometry.transforms)) {
161
+ // get boundingBox of original points and transform it
162
+ boundingBox = transformBoundingBox(measureBoundingBoxOfGeom3Polygons(geometry.polygons), geometry.transforms)
163
+ } else {
164
+ // transform the points and then caclulate the boundingBox
165
+ boundingBox = measureBoundingBoxOfGeom3Polygons(geom3.toPolygons(geometry))
166
+ }
106
167
 
107
168
  cache.set(geometry, boundingBox)
169
+ return boundingBox
170
+ }
108
171
 
172
+ /*
173
+ * swap values if specific axis value in the second vector has lower value than the axis value in first vector
174
+ */
175
+ const fixBound = (i, v1, v2) => {
176
+ if (v1[i] > v2[i]) {
177
+ const tmp = v1[i]
178
+ v1[i] = v2[i]
179
+ v2[i] = tmp
180
+ }
181
+ }
182
+
183
+ /*
184
+ * Transform the given bounding box
185
+ */
186
+ const transformBoundingBox = (boundingBox, transforms) => {
187
+ if (!mat4.isIdentity(transforms)) {
188
+ vec3.transform(boundingBox[0], boundingBox[0], transforms)
189
+ vec3.transform(boundingBox[1], boundingBox[1], transforms)
190
+
191
+ // we now have a new 2 vectors: [v1,v2] => [ [x1,y1,z1], [x2,y2,z2] ]
192
+ // transform can move bounding box corner in such way that it is no longer true that
193
+ // - v1 = [min(x1,x2),min(y1,y2),min(z1,z2)]
194
+ // - v2 = [max(x1,x2),max(y1,y2),max(z1,z2)]
195
+ fixBound(0, ...boundingBox) // swap x, if higher value is in first vector
196
+ fixBound(1, ...boundingBox) // swap y, if higher value is in first vector
197
+ fixBound(2, ...boundingBox) // swap z, if higher value is in first vector
198
+ }
109
199
  return boundingBox
110
200
  }
111
201
 
@@ -4,6 +4,8 @@ const { geom2, geom3, path2 } = require('../geometries')
4
4
 
5
5
  const { line, rectangle, cuboid } = require('../primitives')
6
6
 
7
+ const { mirror } = require('../operations/transforms')
8
+
7
9
  const { measureBoundingBox } = require('./index')
8
10
 
9
11
  test('measureBoundingBox (single objects)', (t) => {
@@ -56,3 +58,9 @@ test('measureBoundingBox (multiple objects)', (t) => {
56
58
  allbounds = measureBoundingBox(aline, arect, acube, o)
57
59
  t.deepEqual(allbounds, [[[10, 10, 0], [15, 15, 0]], [[-5, -10, 0], [5, 10, 0]], [[-1, -1, -1], [1, 1, 1]], [[0, 0, 0], [0, 0, 0]]])
58
60
  })
61
+
62
+ test('measureBoundingBox invert', (t) => {
63
+ const acube = mirror({}, cuboid())
64
+ const cbounds = measureBoundingBox(acube)
65
+ t.deepEqual(cbounds, [[-1, -1, -1], [1, 1, 1]])
66
+ })
@@ -0,0 +1,146 @@
1
+ const flatten = require('../utils/flatten')
2
+
3
+ const vec2 = require('../maths/vec2')
4
+ const vec3 = require('../maths/vec3')
5
+
6
+ const geom2 = require('../geometries/geom2')
7
+ const geom3 = require('../geometries/geom3')
8
+ const path2 = require('../geometries/path2')
9
+ const poly3 = require('../geometries/poly3')
10
+
11
+ const cacheOfBoundingSpheres = new WeakMap()
12
+
13
+ /*
14
+ * Measure the bounding sphere of the given (path2) geometry.
15
+ * @return {[[x, y, z], radius]} the bounding sphere for the geometry
16
+ */
17
+ const measureBoundingSphereOfPath2 = (geometry) => {
18
+ let boundingSphere = cacheOfBoundingSpheres.get(geometry)
19
+ if (boundingSphere !== undefined) return boundingSphere
20
+
21
+ const centroid = vec3.create()
22
+ let radius = 0
23
+
24
+ const points = path2.toPoints(geometry)
25
+
26
+ if (points.length > 0) {
27
+ // calculate the centriod of the geometry
28
+ let numPoints = 0
29
+ const temp = vec3.create()
30
+ points.forEach((point) => {
31
+ vec3.add(centroid, centroid, vec3.fromVec2(temp, point, 0))
32
+ numPoints++
33
+ })
34
+ vec3.scale(centroid, centroid, 1 / numPoints)
35
+
36
+ // find the farthest point from the centroid
37
+ points.forEach((point) => {
38
+ radius = Math.max(radius, vec2.squaredDistance(centroid, point))
39
+ })
40
+ radius = Math.sqrt(radius)
41
+ }
42
+
43
+ boundingSphere = [centroid, radius]
44
+ cacheOfBoundingSpheres.set(geometry, boundingSphere)
45
+
46
+ return boundingSphere
47
+ }
48
+
49
+ /*
50
+ * Measure the bounding sphere of the given (geom2) geometry.
51
+ * @return {[[x, y, z], radius]} the bounding sphere for the geometry
52
+ */
53
+ const measureBoundingSphereOfGeom2 = (geometry) => {
54
+ let boundingSphere = cacheOfBoundingSpheres.get(geometry)
55
+ if (boundingSphere !== undefined) return boundingSphere
56
+
57
+ const centroid = vec3.create()
58
+ let radius = 0
59
+
60
+ const sides = geom2.toSides(geometry)
61
+
62
+ if (sides.length > 0) {
63
+ // calculate the centriod of the geometry
64
+ let numPoints = 0
65
+ const temp = vec3.create()
66
+ sides.forEach((side) => {
67
+ vec3.add(centroid, centroid, vec3.fromVec2(temp, side[0], 0))
68
+ numPoints++
69
+ })
70
+ vec3.scale(centroid, centroid, 1 / numPoints)
71
+
72
+ // find the farthest point from the centroid
73
+ sides.forEach((side) => {
74
+ radius = Math.max(radius, vec2.squaredDistance(centroid, side[0]))
75
+ })
76
+ radius = Math.sqrt(radius)
77
+ }
78
+
79
+ boundingSphere = [centroid, radius]
80
+ cacheOfBoundingSpheres.set(geometry, boundingSphere)
81
+
82
+ return boundingSphere
83
+ }
84
+
85
+ /*
86
+ * Measure the bounding sphere of the given (geom3) geometry.
87
+ * @return {[[x, y, z], radius]} the bounding sphere for the geometry
88
+ */
89
+ const measureBoundingSphereOfGeom3 = (geometry) => {
90
+ let boundingSphere = cacheOfBoundingSpheres.get(geometry)
91
+ if (boundingSphere !== undefined) return boundingSphere
92
+
93
+ const centroid = vec3.create()
94
+ let radius = 0
95
+
96
+ const polygons = geom3.toPolygons(geometry)
97
+
98
+ if (polygons.length > 0) {
99
+ // calculate the centriod of the geometry
100
+ let numPoints = 0
101
+ polygons.forEach((polygon) => {
102
+ poly3.toPoints(polygon).forEach((point) => {
103
+ vec3.add(centroid, centroid, point)
104
+ numPoints++
105
+ })
106
+ })
107
+ vec3.scale(centroid, centroid, 1 / numPoints)
108
+
109
+ // find the farthest point from the centroid
110
+ polygons.forEach((polygon) => {
111
+ poly3.toPoints(polygon).forEach((point) => {
112
+ radius = Math.max(radius, vec3.squaredDistance(centroid, point))
113
+ })
114
+ })
115
+ radius = Math.sqrt(radius)
116
+ }
117
+
118
+ boundingSphere = [centroid, radius]
119
+ cacheOfBoundingSpheres.set(geometry, boundingSphere)
120
+
121
+ return boundingSphere
122
+ }
123
+
124
+ /**
125
+ * Measure the (aproximate) bounding sphere of the given geometries.
126
+ * @see https://en.wikipedia.org/wiki/Bounding_sphere
127
+ * @param {...Object} geometries - the geometries to measure
128
+ * @return {Array} the bounding sphere for each geometry, i.e. [centroid, radius]
129
+ * @alias module:modeling/measurements.measureBoundingSphere
130
+ *
131
+ * @example
132
+ * let bounds = measureBoundingSphere(cube())
133
+ */
134
+ const measureBoundingSphere = (...geometries) => {
135
+ geometries = flatten(geometries)
136
+
137
+ const results = geometries.map((geometry) => {
138
+ if (path2.isA(geometry)) return measureBoundingSphereOfPath2(geometry)
139
+ if (geom2.isA(geometry)) return measureBoundingSphereOfGeom2(geometry)
140
+ if (geom3.isA(geometry)) return measureBoundingSphereOfGeom3(geometry)
141
+ return [[0, 0, 0], 0]
142
+ })
143
+ return results.length === 1 ? results[0] : results
144
+ }
145
+
146
+ module.exports = measureBoundingSphere
@@ -0,0 +1,59 @@
1
+ const test = require('ava')
2
+
3
+ const { geom2, geom3, path2 } = require('../geometries')
4
+
5
+ const { line, rectangle, ellipsoid } = require('../primitives')
6
+
7
+ const { measureBoundingSphere } = require('./index')
8
+
9
+ test('measureBoundingSphere (single objects)', (t) => {
10
+ const aline = line([[10, 10], [15, 15]])
11
+ const arect = rectangle()
12
+ const aellipsoid = ellipsoid({ radius: [5, 10, 15], center: [5, 5, 5] })
13
+
14
+ const apath2 = path2.create()
15
+ const ageom2 = geom2.create()
16
+ const ageom3 = geom3.create()
17
+
18
+ const n = null
19
+ const o = {}
20
+ const x = 'hi'
21
+
22
+ const lbounds = measureBoundingSphere(aline)
23
+ const rbounds = measureBoundingSphere(arect)
24
+ const cbounds = measureBoundingSphere(aellipsoid)
25
+
26
+ const p2bounds = measureBoundingSphere(apath2)
27
+ const g2bounds = measureBoundingSphere(ageom2)
28
+ const g3bounds = measureBoundingSphere(ageom3)
29
+
30
+ const nbounds = measureBoundingSphere(n)
31
+ const obounds = measureBoundingSphere(o)
32
+ const xbounds = measureBoundingSphere(x)
33
+
34
+ t.deepEqual(lbounds, [[12.5, 12.5, 0], 3.5355339059327378])
35
+ t.deepEqual(rbounds, [[0, 0, 0], 1.4142135623730951])
36
+ t.deepEqual(cbounds, [[5.000000000000018, 4.999999999999983, 5.000000000000001], 15])
37
+
38
+ t.deepEqual(p2bounds, [[0, 0, 0], 0])
39
+ t.deepEqual(g2bounds, [[0, 0, 0], 0])
40
+ t.deepEqual(g3bounds, [[0, 0, 0], 0])
41
+
42
+ t.deepEqual(nbounds, [[0, 0, 0], 0])
43
+ t.deepEqual(obounds, [[0, 0, 0], 0])
44
+ t.deepEqual(xbounds, [[0, 0, 0], 0])
45
+ })
46
+
47
+ test('measureBoundingSphere (multiple objects)', (t) => {
48
+ const aline = line([[10, 10], [15, 15]])
49
+ const arect = rectangle()
50
+ const aellipsoid = ellipsoid({ radius: [5, 10, 15], center: [5, 5, 5] })
51
+ const o = {}
52
+
53
+ let allbounds = measureBoundingSphere(aline, arect, aellipsoid, o)
54
+ t.deepEqual(allbounds, [[[12.5, 12.5, 0], 3.5355339059327378], [[0, 0, 0], 1.4142135623730951], [[5.000000000000018, 4.999999999999983, 5.000000000000001], 15], [[0, 0, 0], 0]])
55
+
56
+ // test caching
57
+ allbounds = measureBoundingSphere(aline, arect, aellipsoid, o)
58
+ t.deepEqual(allbounds, [[[12.5, 12.5, 0], 3.5355339059327378], [[0, 0, 0], 1.4142135623730951], [[5.000000000000018, 4.999999999999983, 5.000000000000001], 15], [[0, 0, 0], 0]])
59
+ })
@@ -0,0 +1,28 @@
1
+ const flatten = require('../utils/flatten')
2
+
3
+ const measureBoundingBox = require('./measureBoundingBox')
4
+
5
+ /**
6
+ * Measure the center of the given geometries.
7
+ * @param {...Object} geometries - the geometries to measure
8
+ * @return {Array} the center point for each geometry, i.e. [X, Y, Z]
9
+ * @alias module:modeling/measurements.measureCenter
10
+ *
11
+ * @example
12
+ * let center = measureCenter(sphere())
13
+ */
14
+ const measureCenter = (...geometries) => {
15
+ geometries = flatten(geometries)
16
+
17
+ const results = geometries.map((geometry) => {
18
+ const bounds = measureBoundingBox(geometry)
19
+ return [
20
+ (bounds[0][0] + ((bounds[1][0] - bounds[0][0]) / 2)),
21
+ (bounds[0][1] + ((bounds[1][1] - bounds[0][1]) / 2)),
22
+ (bounds[0][2] + ((bounds[1][2] - bounds[0][2]) / 2))
23
+ ]
24
+ })
25
+ return results.length === 1 ? results[0] : results
26
+ }
27
+
28
+ module.exports = measureCenter
@@ -0,0 +1,58 @@
1
+ const test = require('ava')
2
+
3
+ const { geom2, geom3, path2 } = require('../geometries')
4
+
5
+ const { line, rectangle, cuboid } = require('../primitives')
6
+
7
+ const { measureCenter } = require('./index')
8
+
9
+ test('measureCenter (single objects)', (t) => {
10
+ const aline = line([[10, 10], [15, 15]])
11
+ const arect = rectangle({ center: [5, 5] })
12
+ const acube = cuboid({ center: [-5, -5, -5] })
13
+
14
+ const apath2 = path2.create()
15
+ const ageom2 = geom2.create()
16
+ const ageom3 = geom3.create()
17
+
18
+ const n = null
19
+ const o = {}
20
+ const x = 'hi'
21
+
22
+ const lcenter = measureCenter(aline)
23
+ const rcenter = measureCenter(arect)
24
+ const ccenter = measureCenter(acube)
25
+
26
+ const p2center = measureCenter(apath2)
27
+ const g2center = measureCenter(ageom2)
28
+ const g3center = measureCenter(ageom3)
29
+
30
+ const ncenter = measureCenter(n)
31
+ const ocenter = measureCenter(o)
32
+ const xcenter = measureCenter(x)
33
+
34
+ t.deepEqual(lcenter, [12.5, 12.5, 0])
35
+ t.deepEqual(rcenter, [5, 5, 0])
36
+ t.deepEqual(ccenter, [-5, -5, -5])
37
+
38
+ t.deepEqual(p2center, [0, 0, 0])
39
+ t.deepEqual(g2center, [0, 0, 0])
40
+ t.deepEqual(g3center, [0, 0, 0])
41
+
42
+ t.deepEqual(ncenter, [0, 0, 0])
43
+ t.deepEqual(ocenter, [0, 0, 0])
44
+ t.deepEqual(xcenter, [0, 0, 0])
45
+ })
46
+
47
+ test('measureCenter (multiple objects)', (t) => {
48
+ const aline = line([[10, 10], [15, 15]])
49
+ const arect = rectangle({ size: [10, 20] })
50
+ const acube = cuboid({ center: [-5, -5, -5] })
51
+ const o = {}
52
+
53
+ let allcenters = measureCenter(aline, arect, acube, o)
54
+ t.deepEqual(allcenters, [[12.5, 12.5, 0], [0, 0, 0], [-5, -5, -5], [0, 0, 0]])
55
+
56
+ allcenters = measureCenter(aline, arect, acube, o)
57
+ t.deepEqual(allcenters, [[12.5, 12.5, 0], [0, 0, 0], [-5, -5, -5], [0, 0, 0]])
58
+ })