@jscad/modeling 3.0.0-alpha.0 → 3.0.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE +1 -1
  3. package/dist/jscad-modeling.es.js +2 -2
  4. package/dist/jscad-modeling.min.js +2 -2
  5. package/package.json +2 -2
  6. package/rollup.config.js +1 -1
  7. package/src/colors/colorize.js +1 -5
  8. package/src/colors/colorize.test.js +8 -8
  9. package/src/geometries/geom2/transform.js +9 -1
  10. package/src/geometries/geom2/transform.test.js +57 -0
  11. package/src/geometries/geom3/fromPointsConvex.d.ts +4 -0
  12. package/src/geometries/geom3/fromPointsConvex.js +25 -0
  13. package/src/geometries/geom3/fromPointsConvex.test.js +32 -0
  14. package/src/geometries/geom3/index.d.ts +1 -0
  15. package/src/geometries/geom3/index.js +1 -0
  16. package/src/geometries/index.js +3 -4
  17. package/src/geometries/path2/appendBezier.js +1 -1
  18. package/src/geometries/poly3/index.js +1 -1
  19. package/src/geometries/poly3/measureBoundingBox.js +2 -0
  20. package/src/geometries/poly3/measureBoundingSphere.d.ts +2 -1
  21. package/src/geometries/poly3/measureBoundingSphere.js +25 -8
  22. package/src/geometries/poly3/measureBoundingSphere.test.js +12 -8
  23. package/src/geometries/poly3/type.d.ts +1 -1
  24. package/src/geometries/slice/validate.js +1 -2
  25. package/src/index.js +41 -0
  26. package/src/maths/index.js +1 -0
  27. package/src/maths/mat4/isOnlyTransformScale.js +1 -1
  28. package/src/measurements/measureAggregateArea.js +0 -1
  29. package/src/measurements/measureAggregateBoundingBox.js +0 -1
  30. package/src/measurements/measureAggregateEpsilon.js +0 -1
  31. package/src/measurements/measureAggregateVolume.js +0 -1
  32. package/src/measurements/measureArea.js +0 -1
  33. package/src/measurements/measureBoundingBox.js +0 -1
  34. package/src/measurements/measureBoundingSphere.js +2 -6
  35. package/src/measurements/measureEpsilon.js +0 -1
  36. package/src/measurements/measureVolume.js +0 -1
  37. package/src/operations/booleans/index.d.ts +1 -0
  38. package/src/operations/booleans/intersect.js +5 -5
  39. package/src/operations/booleans/intersect.test.js +6 -7
  40. package/src/operations/booleans/intersectGeom2.js +2 -6
  41. package/src/operations/booleans/intersectGeom2.test.js +25 -1
  42. package/src/operations/booleans/intersectGeom3.js +2 -6
  43. package/src/operations/booleans/intersectGeom3.test.js +5 -1
  44. package/src/operations/booleans/martinez/compareEvents.js +2 -7
  45. package/src/operations/booleans/martinez/connectEdges.js +30 -41
  46. package/src/operations/booleans/martinez/contour.js +1 -1
  47. package/src/operations/booleans/martinez/divideSegment.js +12 -11
  48. package/src/operations/booleans/martinez/fillQueue.js +24 -28
  49. package/src/operations/booleans/martinez/index.js +2 -1
  50. package/src/operations/booleans/martinez/possibleIntersection.js +41 -30
  51. package/src/operations/booleans/martinez/segmentIntersection.js +7 -9
  52. package/src/operations/booleans/martinez/splaytree.js +59 -457
  53. package/src/operations/booleans/martinez/subdivideSegments.js +4 -4
  54. package/src/operations/booleans/martinez/sweepEvent.js +3 -17
  55. package/src/operations/booleans/mayOverlap.js +0 -1
  56. package/src/operations/booleans/scission.d.ts +5 -0
  57. package/src/operations/booleans/scission.js +3 -5
  58. package/src/operations/booleans/scission.test.js +6 -0
  59. package/src/operations/booleans/subtract.js +5 -5
  60. package/src/operations/booleans/subtract.test.js +6 -7
  61. package/src/operations/booleans/subtractGeom2.js +2 -6
  62. package/src/operations/booleans/subtractGeom2.test.js +25 -1
  63. package/src/operations/booleans/subtractGeom3.js +2 -6
  64. package/src/operations/booleans/subtractGeom3.test.js +5 -1
  65. package/src/operations/booleans/trees/Node.js +25 -27
  66. package/src/operations/booleans/trees/PolygonTreeNode.js +153 -106
  67. package/src/operations/booleans/trees/Tree.js +9 -4
  68. package/src/operations/booleans/trees/splitLineSegmentByPlane.js +5 -3
  69. package/src/operations/booleans/trees/splitPolygonByPlane.d.ts +33 -0
  70. package/src/operations/booleans/trees/splitPolygonByPlane.js +39 -34
  71. package/src/operations/booleans/union.js +5 -5
  72. package/src/operations/booleans/union.test.js +6 -7
  73. package/src/operations/booleans/unionGeom2.js +2 -6
  74. package/src/operations/booleans/unionGeom2.test.js +25 -1
  75. package/src/operations/booleans/unionGeom3.js +2 -6
  76. package/src/operations/booleans/unionGeom3.test.js +6 -1
  77. package/src/operations/extrusions/extrudeFromSlices.test.js +8 -1
  78. package/src/operations/extrusions/extrudeHelical.js +2 -8
  79. package/src/operations/extrusions/extrudeLinear.js +1 -5
  80. package/src/operations/extrusions/extrudeLinear.test.js +7 -1
  81. package/src/operations/extrusions/extrudeRotate.js +3 -2
  82. package/src/operations/extrusions/extrudeRotate.test.js +13 -1
  83. package/src/operations/extrusions/extrudeWalls.js +3 -1
  84. package/src/operations/extrusions/project.js +1 -5
  85. package/src/operations/hulls/hull.js +6 -5
  86. package/src/operations/hulls/hull.test.js +56 -3
  87. package/src/operations/hulls/hullChain.js +11 -6
  88. package/src/operations/hulls/hullChain.test.js +12 -2
  89. package/src/operations/hulls/hullGeom2.js +5 -6
  90. package/src/operations/hulls/hullGeom3.js +9 -18
  91. package/src/operations/hulls/hullPath2.js +6 -7
  92. package/src/operations/hulls/hullPath2.test.js +1 -1
  93. package/src/operations/hulls/hullPoints2.d.ts +3 -0
  94. package/src/operations/hulls/hullPoints2.js +24 -30
  95. package/src/operations/hulls/hullPoints3.d.ts +4 -0
  96. package/src/operations/hulls/hullPoints3.js +21 -0
  97. package/src/operations/hulls/index.d.ts +2 -0
  98. package/src/operations/hulls/index.js +3 -1
  99. package/src/operations/modifiers/generalize.js +2 -6
  100. package/src/operations/modifiers/index.js +1 -1
  101. package/src/operations/modifiers/mergePolygons.js +2 -3
  102. package/src/operations/modifiers/reTesselateCoplanarPolygons.js +7 -7
  103. package/src/operations/modifiers/snap.js +2 -6
  104. package/src/operations/offsets/offset.js +1 -5
  105. package/src/operations/offsets/offsetFromPoints.test.js +0 -1
  106. package/src/operations/offsets/offsetGeom2.test.js +1 -0
  107. package/src/operations/offsets/offsetGeom3.js +0 -2
  108. package/src/operations/offsets/offsetGeom3.test.js +9 -1
  109. package/src/operations/offsets/offsetPath2.js +3 -3
  110. package/src/operations/transforms/align.js +8 -7
  111. package/src/operations/transforms/align.test.js +2 -2
  112. package/src/operations/transforms/center.js +6 -9
  113. package/src/operations/transforms/center.test.js +19 -1
  114. package/src/operations/transforms/mirror.js +5 -8
  115. package/src/operations/transforms/mirror.test.js +7 -7
  116. package/src/operations/transforms/rotate.js +5 -8
  117. package/src/operations/transforms/scale.js +5 -8
  118. package/src/operations/transforms/transform.js +2 -5
  119. package/src/operations/transforms/translate.js +5 -8
  120. package/src/primitives/arc.js +2 -0
  121. package/src/primitives/arc.test.js +11 -11
  122. package/src/primitives/circle.test.js +18 -8
  123. package/src/primitives/cube.test.js +10 -0
  124. package/src/primitives/cuboid.test.js +10 -0
  125. package/src/primitives/cylinder.test.js +12 -0
  126. package/src/primitives/cylinderElliptic.test.js +21 -1
  127. package/src/primitives/ellipse.test.js +18 -8
  128. package/src/primitives/ellipsoid.test.js +12 -0
  129. package/src/primitives/geodesicSphere.test.js +8 -0
  130. package/src/primitives/line.test.js +1 -1
  131. package/src/primitives/polygon.d.ts +1 -0
  132. package/src/primitives/polygon.js +13 -4
  133. package/src/primitives/polygon.test.js +15 -0
  134. package/src/primitives/polyhedron.js +1 -0
  135. package/src/primitives/polyhedron.test.js +8 -2
  136. package/src/primitives/rectangle.test.js +9 -3
  137. package/src/primitives/roundedCuboid.js +1 -1
  138. package/src/primitives/roundedCuboid.test.js +20 -4
  139. package/src/primitives/roundedCylinder.js +1 -1
  140. package/src/primitives/roundedCylinder.test.js +20 -0
  141. package/src/primitives/roundedRectangle.js +1 -1
  142. package/src/primitives/roundedRectangle.test.js +15 -6
  143. package/src/primitives/sphere.test.js +12 -0
  144. package/src/primitives/square.test.js +10 -4
  145. package/src/primitives/star.test.js +14 -6
  146. package/src/primitives/torus.js +1 -1
  147. package/src/primitives/torus.test.js +11 -1
  148. package/src/primitives/triangle.test.js +17 -9
  149. package/src/utils/coalesce.d.ts +3 -0
  150. package/src/utils/coalesce.js +20 -0
  151. package/src/utils/index.js +2 -2
  152. package/src/maths/mat4/leftMultiplyVec2.d.ts +0 -4
  153. package/src/maths/mat4/leftMultiplyVec2.js +0 -26
  154. package/src/maths/mat4/leftMultiplyVec3.d.ts +0 -4
  155. package/src/maths/mat4/leftMultiplyVec3.js +0 -27
  156. package/src/maths/mat4/mirror.d.ts +0 -4
  157. package/src/maths/mat4/mirror.js +0 -32
  158. package/src/maths/mat4/rightMultiplyVec2.d.ts +0 -4
  159. package/src/maths/mat4/rightMultiplyVec2.js +0 -27
  160. package/src/maths/mat4/rightMultiplyVec3.d.ts +0 -4
  161. package/src/maths/mat4/rightMultiplyVec3.js +0 -28
@@ -1,10 +1,8 @@
1
- import { flatten } from '../../utils/flatten.js'
2
-
3
1
  import * as geom2 from '../../geometries/geom2/index.js'
4
2
  import * as geom3 from '../../geometries/geom3/index.js'
5
3
  import * as path2 from '../../geometries/path2/index.js'
6
4
 
7
- import { measureBoundingBox } from '../../measurements/measureBoundingBox.js'
5
+ import { measureAggregateBoundingBox } from '../../measurements/measureAggregateBoundingBox.js'
8
6
 
9
7
  import { translate } from './translate.js'
10
8
 
@@ -15,7 +13,7 @@ const centerGeometry = (options, object) => {
15
13
  }
16
14
  const { axes, relativeTo } = Object.assign({}, defaults, options)
17
15
 
18
- const bounds = measureBoundingBox(object)
16
+ const bounds = measureAggregateBoundingBox(object)
19
17
  const offset = [0, 0, 0]
20
18
  if (axes[0]) offset[0] = relativeTo[0] - (bounds[0][0] + ((bounds[1][0] - bounds[0][0]) / 2))
21
19
  if (axes[1]) offset[1] = relativeTo[1] - (bounds[0][1] + ((bounds[1][1] - bounds[0][1]) / 2))
@@ -43,8 +41,6 @@ export const center = (options, ...objects) => {
43
41
  }
44
42
  const { axes, relativeTo } = Object.assign({}, defaults, options)
45
43
 
46
- objects = flatten(objects)
47
- if (objects.length === 0) throw new Error('wrong number of arguments')
48
44
  if (relativeTo.length !== 3) throw new Error('relativeTo must be an array of length 3')
49
45
 
50
46
  options = { axes, relativeTo }
@@ -53,6 +49,7 @@ export const center = (options, ...objects) => {
53
49
  if (path2.isA(object)) return centerGeometry(options, object)
54
50
  if (geom2.isA(object)) return centerGeometry(options, object)
55
51
  if (geom3.isA(object)) return centerGeometry(options, object)
52
+ if (Array.isArray(object)) return centerGeometry(options, object)
56
53
  return object
57
54
  })
58
55
  return results.length === 1 ? results[0] : results
@@ -64,7 +61,7 @@ export const center = (options, ...objects) => {
64
61
  * @return {Object|Array} the centered object, or a list of centered objects
65
62
  * @alias module:modeling/transforms.centerX
66
63
  */
67
- export const centerX = (...objects) => center({ axes: [true, false, false] }, objects)
64
+ export const centerX = (...objects) => center({ axes: [true, false, false] }, ...objects)
68
65
 
69
66
  /**
70
67
  * Center the given objects about the Y axis.
@@ -72,7 +69,7 @@ export const centerX = (...objects) => center({ axes: [true, false, false] }, ob
72
69
  * @return {Object|Array} the centered object, or a list of centered objects
73
70
  * @alias module:modeling/transforms.centerY
74
71
  */
75
- export const centerY = (...objects) => center({ axes: [false, true, false] }, objects)
72
+ export const centerY = (...objects) => center({ axes: [false, true, false] }, ...objects)
76
73
 
77
74
  /**
78
75
  * Center the given objects about the Z axis.
@@ -80,4 +77,4 @@ export const centerY = (...objects) => center({ axes: [false, true, false] }, ob
80
77
  * @return {Object|Array} the centered object, or a list of centered objects
81
78
  * @alias module:modeling/transforms.centerZ
82
79
  */
83
- export const centerZ = (...objects) => center({ axes: [false, false, true] }, objects)
80
+ export const centerZ = (...objects) => center({ axes: [false, false, true] }, ...objects)
@@ -4,7 +4,9 @@ import { comparePoints, comparePolygonsAsPoints } from '../../../test/helpers/in
4
4
 
5
5
  import { geom2, geom3, path2 } from '../../geometries/index.js'
6
6
 
7
- import { measureArea, measureVolume } from '../../measurements/index.js'
7
+ import { measureArea, measureAggregateBoundingBox, measureVolume } from '../../measurements/index.js'
8
+
9
+ import { square } from '../../primitives/index.js'
8
10
 
9
11
  import { center, centerX, centerY, centerZ } from './index.js'
10
12
 
@@ -136,3 +138,19 @@ test('center: centering of multiple objects produces expected changes', (t) => {
136
138
  t.notThrows(() => geom2.validate(centered[2]))
137
139
  t.true(comparePoints(pts2, exp2))
138
140
  })
141
+
142
+ test('center multiple separate', (t) => {
143
+ const square1 = square({ size: 4, center: [10, 10] })
144
+ const square2 = square({ size: 6, center: [-10, -10] })
145
+ const obs = center({}, square1, square2)
146
+ t.notThrows(() => obs.map(geom2.validate))
147
+ t.deepEqual([[-3, -3, 0], [3, 3, 0]], measureAggregateBoundingBox(obs))
148
+ })
149
+
150
+ test('center multiple grouped', (t) => {
151
+ const square1 = square({ size: 4, center: [10, 10] })
152
+ const square2 = square({ size: 6, center: [-10, -10] })
153
+ const obs = center({}, [square1, square2])
154
+ t.notThrows(() => obs.map(geom2.validate))
155
+ t.deepEqual([[-12.5, -12.5, 0], [12.5, 12.5, 0]], measureAggregateBoundingBox(obs))
156
+ })
@@ -1,5 +1,3 @@
1
- import { flatten } from '../../utils/flatten.js'
2
-
3
1
  import * as mat4 from '../../maths/mat4/index.js'
4
2
  import * as plane from '../../maths/plane/index.js'
5
3
 
@@ -26,9 +24,6 @@ export const mirror = (options, ...objects) => {
26
24
  }
27
25
  const { origin, normal } = Object.assign({}, defaults, options)
28
26
 
29
- objects = flatten(objects)
30
- if (objects.length === 0) throw new Error('wrong number of arguments')
31
-
32
27
  const planeOfMirror = plane.fromNormalAndPoint(plane.create(), normal, origin)
33
28
  // verify the plane, i.e. check that the given normal was valid
34
29
  if (Number.isNaN(planeOfMirror[0])) {
@@ -41,6 +36,8 @@ export const mirror = (options, ...objects) => {
41
36
  if (path2.isA(object)) return path2.transform(matrix, object)
42
37
  if (geom2.isA(object)) return geom2.transform(matrix, object)
43
38
  if (geom3.isA(object)) return geom3.transform(matrix, object)
39
+ // handle recursive arrays
40
+ if (Array.isArray(object)) return mirror(options, ...object)
44
41
  return object
45
42
  })
46
43
  return results.length === 1 ? results[0] : results
@@ -52,7 +49,7 @@ export const mirror = (options, ...objects) => {
52
49
  * @return {Object|Array} the mirrored object, or a list of mirrored objects
53
50
  * @alias module:modeling/transforms.mirrorX
54
51
  */
55
- export const mirrorX = (...objects) => mirror({ normal: [1, 0, 0] }, objects)
52
+ export const mirrorX = (...objects) => mirror({ normal: [1, 0, 0] }, ...objects)
56
53
 
57
54
  /**
58
55
  * Mirror the given objects about the Y axis.
@@ -60,7 +57,7 @@ export const mirrorX = (...objects) => mirror({ normal: [1, 0, 0] }, objects)
60
57
  * @return {Object|Array} the mirrored object, or a list of mirrored objects
61
58
  * @alias module:modeling/transforms.mirrorY
62
59
  */
63
- export const mirrorY = (...objects) => mirror({ normal: [0, 1, 0] }, objects)
60
+ export const mirrorY = (...objects) => mirror({ normal: [0, 1, 0] }, ...objects)
64
61
 
65
62
  /**
66
63
  * Mirror the given objects about the Z axis.
@@ -68,4 +65,4 @@ export const mirrorY = (...objects) => mirror({ normal: [0, 1, 0] }, objects)
68
65
  * @return {Object|Array} the mirrored object, or a list of mirrored objects
69
66
  * @alias module:modeling/transforms.mirrorZ
70
67
  */
71
- export const mirrorZ = (...objects) => mirror({ normal: [0, 0, 1] }, objects)
68
+ export const mirrorZ = (...objects) => mirror({ normal: [0, 0, 1] }, ...objects)
@@ -42,29 +42,29 @@ test('mirror: mirroring of geom2 about X/Y produces expected changes to points',
42
42
  // mirror about X
43
43
  let mirrored = mirror({ normal: [1, 0, 0] }, geometry)
44
44
  let obs = geom2.toPoints(mirrored)
45
- let exp = [[5, -5], [0, 5], [-10, -5]]
45
+ let exp = [[-10, -5], [0, 5], [5, -5]]
46
46
  t.notThrows(() => geom2.validate(mirrored))
47
- t.is(measureArea(mirrored), -measureArea(geometry))
47
+ t.is(measureArea(mirrored), measureArea(geometry))
48
48
  t.true(comparePoints(obs, exp))
49
49
 
50
50
  mirrored = mirrorX(geometry)
51
51
  obs = geom2.toPoints(mirrored)
52
52
  t.notThrows(() => geom2.validate(mirrored))
53
- t.is(measureArea(mirrored), -measureArea(geometry))
53
+ t.is(measureArea(mirrored), measureArea(geometry))
54
54
  t.true(comparePoints(obs, exp))
55
55
 
56
56
  // mirror about Y
57
57
  mirrored = mirror({ normal: [0, 1, 0] }, geometry)
58
58
  obs = geom2.toPoints(mirrored)
59
- exp = [[-5, 5], [0, -5], [10, 5]]
59
+ exp = [[10, 5], [0, -5], [-5, 5]]
60
60
  t.notThrows(() => geom2.validate(mirrored))
61
- t.is(measureArea(mirrored), -measureArea(geometry))
61
+ t.is(measureArea(mirrored), measureArea(geometry))
62
62
  t.true(comparePoints(obs, exp))
63
63
 
64
64
  mirrored = mirrorY(geometry)
65
65
  obs = geom2.toPoints(mirrored)
66
66
  t.notThrows(() => geom2.validate(mirrored))
67
- t.is(measureArea(mirrored), -measureArea(geometry))
67
+ t.is(measureArea(mirrored), measureArea(geometry))
68
68
  t.true(comparePoints(obs, exp))
69
69
  })
70
70
 
@@ -158,7 +158,7 @@ test('mirror: mirroring of multiple objects produces an array of mirrored object
158
158
  t.true(comparePoints(obs, exp))
159
159
 
160
160
  obs = geom2.toPoints(mirrored[2])
161
- exp = [[-5, 5], [0, -5], [10, 5]]
161
+ exp = [[10, 5], [0, -5], [-5, 5]]
162
162
  t.notThrows(() => geom2.validate(mirrored[2]))
163
163
  t.true(comparePoints(obs, exp))
164
164
  })
@@ -1,5 +1,3 @@
1
- import { flatten } from '../../utils/flatten.js'
2
-
3
1
  import * as mat4 from '../../maths/mat4/index.js'
4
2
 
5
3
  import * as geom2 from '../../geometries/geom2/index.js'
@@ -19,9 +17,6 @@ import * as path2 from '../../geometries/path2/index.js'
19
17
  export const rotate = (angles, ...objects) => {
20
18
  if (!Array.isArray(angles)) throw new Error('angles must be an array')
21
19
 
22
- objects = flatten(objects)
23
- if (objects.length === 0) throw new Error('wrong number of arguments')
24
-
25
20
  // adjust the angles if necessary
26
21
  angles = angles.slice() // don't modify the original
27
22
  while (angles.length < 3) angles.push(0)
@@ -36,6 +31,8 @@ export const rotate = (angles, ...objects) => {
36
31
  if (path2.isA(object)) return path2.transform(matrix, object)
37
32
  if (geom2.isA(object)) return geom2.transform(matrix, object)
38
33
  if (geom3.isA(object)) return geom3.transform(matrix, object)
34
+ // handle recursive arrays
35
+ if (Array.isArray(object)) return rotate(angles, ...object)
39
36
  return object
40
37
  })
41
38
  return results.length === 1 ? results[0] : results
@@ -48,7 +45,7 @@ export const rotate = (angles, ...objects) => {
48
45
  * @return {Object|Array} the rotated object, or a list of rotated objects
49
46
  * @alias module:modeling/transforms.rotateX
50
47
  */
51
- export const rotateX = (angle, ...objects) => rotate([angle, 0, 0], objects)
48
+ export const rotateX = (angle, ...objects) => rotate([angle, 0, 0], ...objects)
52
49
 
53
50
  /**
54
51
  * Rotate the given objects about the Y axis, using the given options.
@@ -57,7 +54,7 @@ export const rotateX = (angle, ...objects) => rotate([angle, 0, 0], objects)
57
54
  * @return {Object|Array} the rotated object, or a list of rotated objects
58
55
  * @alias module:modeling/transforms.rotateY
59
56
  */
60
- export const rotateY = (angle, ...objects) => rotate([0, angle, 0], objects)
57
+ export const rotateY = (angle, ...objects) => rotate([0, angle, 0], ...objects)
61
58
 
62
59
  /**
63
60
  * Rotate the given objects about the Z axis, using the given options.
@@ -66,4 +63,4 @@ export const rotateY = (angle, ...objects) => rotate([0, angle, 0], objects)
66
63
  * @return {Object|Array} the rotated object, or a list of rotated objects
67
64
  * @alias module:modeling/transforms.rotateZ
68
65
  */
69
- export const rotateZ = (angle, ...objects) => rotate([0, 0, angle], objects)
66
+ export const rotateZ = (angle, ...objects) => rotate([0, 0, angle], ...objects)
@@ -1,5 +1,3 @@
1
- import { flatten } from '../../utils/flatten.js'
2
-
3
1
  import * as mat4 from '../../maths/mat4/index.js'
4
2
 
5
3
  import * as geom2 from '../../geometries/geom2/index.js'
@@ -19,9 +17,6 @@ import * as path2 from '../../geometries/path2/index.js'
19
17
  export const scale = (factors, ...objects) => {
20
18
  if (!Array.isArray(factors)) throw new Error('factors must be an array')
21
19
 
22
- objects = flatten(objects)
23
- if (objects.length === 0) throw new Error('wrong number of arguments')
24
-
25
20
  // adjust the factors if necessary
26
21
  factors = factors.slice() // don't modify the original
27
22
  while (factors.length < 3) factors.push(1)
@@ -34,6 +29,8 @@ export const scale = (factors, ...objects) => {
34
29
  if (path2.isA(object)) return path2.transform(matrix, object)
35
30
  if (geom2.isA(object)) return geom2.transform(matrix, object)
36
31
  if (geom3.isA(object)) return geom3.transform(matrix, object)
32
+ // handle recursive arrays
33
+ if (Array.isArray(object)) return scale(factors, ...object)
37
34
  return object
38
35
  })
39
36
  return results.length === 1 ? results[0] : results
@@ -46,7 +43,7 @@ export const scale = (factors, ...objects) => {
46
43
  * @return {Object|Array} the scaled object, or a list of scaled objects
47
44
  * @alias module:modeling/transforms.scaleX
48
45
  */
49
- export const scaleX = (factor, ...objects) => scale([factor, 1, 1], objects)
46
+ export const scaleX = (factor, ...objects) => scale([factor, 1, 1], ...objects)
50
47
 
51
48
  /**
52
49
  * Scale the given objects about the Y axis using the given options.
@@ -55,7 +52,7 @@ export const scaleX = (factor, ...objects) => scale([factor, 1, 1], objects)
55
52
  * @return {Object|Array} the scaled object, or a list of scaled objects
56
53
  * @alias module:modeling/transforms.scaleY
57
54
  */
58
- export const scaleY = (factor, ...objects) => scale([1, factor, 1], objects)
55
+ export const scaleY = (factor, ...objects) => scale([1, factor, 1], ...objects)
59
56
 
60
57
  /**
61
58
  * Scale the given objects about the Z axis using the given options.
@@ -64,4 +61,4 @@ export const scaleY = (factor, ...objects) => scale([1, factor, 1], objects)
64
61
  * @return {Object|Array} the scaled object, or a list of scaled objects
65
62
  * @alias module:modeling/transforms.scaleZ
66
63
  */
67
- export const scaleZ = (factor, ...objects) => scale([1, 1, factor], objects)
64
+ export const scaleZ = (factor, ...objects) => scale([1, 1, factor], ...objects)
@@ -1,5 +1,3 @@
1
- import { flatten } from '../../utils/flatten.js'
2
-
3
1
  import * as geom2 from '../../geometries/geom2/index.js'
4
2
  import * as geom3 from '../../geometries/geom3/index.js'
5
3
  import * as path2 from '../../geometries/path2/index.js'
@@ -17,13 +15,12 @@ import * as path2 from '../../geometries/path2/index.js'
17
15
  export const transform = (matrix, ...objects) => {
18
16
  // TODO how to check that the matrix is REAL?
19
17
 
20
- objects = flatten(objects)
21
- if (objects.length === 0) throw new Error('wrong number of arguments')
22
-
23
18
  const results = objects.map((object) => {
24
19
  if (path2.isA(object)) return path2.transform(matrix, object)
25
20
  if (geom2.isA(object)) return geom2.transform(matrix, object)
26
21
  if (geom3.isA(object)) return geom3.transform(matrix, object)
22
+ // handle recursive arrays
23
+ if (Array.isArray(object)) return transform(matrix, ...object)
27
24
  return object
28
25
  })
29
26
  return results.length === 1 ? results[0] : results
@@ -1,5 +1,3 @@
1
- import { flatten } from '../../utils/flatten.js'
2
-
3
1
  import * as mat4 from '../../maths/mat4/index.js'
4
2
 
5
3
  import * as geom2 from '../../geometries/geom2/index.js'
@@ -19,9 +17,6 @@ import * as path2 from '../../geometries/path2/index.js'
19
17
  export const translate = (offset, ...objects) => {
20
18
  if (!Array.isArray(offset)) throw new Error('offset must be an array')
21
19
 
22
- objects = flatten(objects)
23
- if (objects.length === 0) throw new Error('wrong number of arguments')
24
-
25
20
  // adjust the offset if necessary
26
21
  offset = offset.slice() // don't modify the original
27
22
  while (offset.length < 3) offset.push(0)
@@ -32,6 +27,8 @@ export const translate = (offset, ...objects) => {
32
27
  if (path2.isA(object)) return path2.transform(matrix, object)
33
28
  if (geom2.isA(object)) return geom2.transform(matrix, object)
34
29
  if (geom3.isA(object)) return geom3.transform(matrix, object)
30
+ // handle recursive arrays
31
+ if (Array.isArray(object)) return translate(offset, ...object)
35
32
  return object
36
33
  })
37
34
  return results.length === 1 ? results[0] : results
@@ -44,7 +41,7 @@ export const translate = (offset, ...objects) => {
44
41
  * @return {Object|Array} the translated object, or a list of translated objects
45
42
  * @alias module:modeling/transforms.translateX
46
43
  */
47
- export const translateX = (offset, ...objects) => translate([offset, 0, 0], objects)
44
+ export const translateX = (offset, ...objects) => translate([offset, 0, 0], ...objects)
48
45
 
49
46
  /**
50
47
  * Translate the given objects along the Y axis using the given options.
@@ -53,7 +50,7 @@ export const translateX = (offset, ...objects) => translate([offset, 0, 0], obje
53
50
  * @return {Object|Array} the translated object, or a list of translated objects
54
51
  * @alias module:modeling/transforms.translateY
55
52
  */
56
- export const translateY = (offset, ...objects) => translate([0, offset, 0], objects)
53
+ export const translateY = (offset, ...objects) => translate([0, offset, 0], ...objects)
57
54
 
58
55
  /**
59
56
  * Translate the given objects along the Z axis using the given options.
@@ -62,4 +59,4 @@ export const translateY = (offset, ...objects) => translate([0, offset, 0], obje
62
59
  * @return {Object|Array} the translated object, or a list of translated objects
63
60
  * @alias module:modeling/transforms.translateZ
64
61
  */
65
- export const translateZ = (offset, ...objects) => translate([0, 0, offset], objects)
62
+ export const translateZ = (offset, ...objects) => translate([0, 0, offset], ...objects)
@@ -17,6 +17,8 @@ import { isGT, isGTE, isNumberArray } from './commonChecks.js'
17
17
  * @param {boolean} [options.makeTangent=false] - adds line segments at both ends of the arc to ensure that the gradients at the edges are tangent
18
18
  * @returns {Path2} new 2D path
19
19
  * @alias module:modeling/primitives.arc
20
+ * @example
21
+ * let myshape = arc({ center: [-1, -1], radius: 2, endAngle: (TAU / 4)})
20
22
  */
21
23
  export const arc = (options) => {
22
24
  const defaults = {
@@ -13,7 +13,7 @@ test('arc (defaults)', (t) => {
13
13
  const obs = path2.toPoints(geometry)
14
14
 
15
15
  t.notThrows(() => path2.validate(geometry))
16
- t.deepEqual(obs.length, 33)
16
+ t.is(obs.length, 33)
17
17
  })
18
18
 
19
19
  test('arc (options)', (t) => {
@@ -41,7 +41,7 @@ test('arc (options)', (t) => {
41
41
  let obs = path2.toPoints(geometry)
42
42
 
43
43
  t.notThrows(() => path2.validate(geometry))
44
- t.deepEqual(obs.length, 17)
44
+ t.is(obs.length, 17)
45
45
  t.true(comparePoints(obs, exp))
46
46
 
47
47
  // test radius
@@ -68,7 +68,7 @@ test('arc (options)', (t) => {
68
68
  obs = path2.toPoints(geometry)
69
69
 
70
70
  t.notThrows(() => path2.validate(geometry))
71
- t.deepEqual(obs.length, 17)
71
+ t.is(obs.length, 17)
72
72
  t.true(comparePoints(obs, exp))
73
73
 
74
74
  // test startAngle
@@ -92,7 +92,7 @@ test('arc (options)', (t) => {
92
92
  obs = path2.toPoints(geometry)
93
93
 
94
94
  t.notThrows(() => path2.validate(geometry))
95
- t.deepEqual(obs.length, 14)
95
+ t.is(obs.length, 14)
96
96
  t.true(comparePoints(obs, exp))
97
97
 
98
98
  // test endAngle
@@ -108,7 +108,7 @@ test('arc (options)', (t) => {
108
108
  obs = path2.toPoints(geometry)
109
109
 
110
110
  t.notThrows(() => path2.validate(geometry))
111
- t.deepEqual(obs.length, 6)
111
+ t.is(obs.length, 6)
112
112
  t.true(comparePoints(obs, exp))
113
113
 
114
114
  // test makeTangent
@@ -137,7 +137,7 @@ test('arc (options)', (t) => {
137
137
  obs = path2.toPoints(geometry)
138
138
 
139
139
  t.notThrows(() => path2.validate(geometry))
140
- t.deepEqual(obs.length, 19)
140
+ t.is(obs.length, 19)
141
141
  t.true(comparePoints(obs, exp))
142
142
 
143
143
  // test segments
@@ -156,7 +156,7 @@ test('arc (options)', (t) => {
156
156
  obs = path2.toPoints(geometry)
157
157
 
158
158
  t.notThrows(() => path2.validate(geometry))
159
- t.deepEqual(obs.length, 9)
159
+ t.is(obs.length, 9)
160
160
  t.true(comparePoints(obs, exp))
161
161
  })
162
162
 
@@ -173,7 +173,7 @@ test('arc (rotations)', (t) => {
173
173
  let obs = path2.toPoints(geometry)
174
174
 
175
175
  t.notThrows(() => path2.validate(geometry))
176
- t.deepEqual(obs.length, 6)
176
+ t.is(obs.length, 6)
177
177
  t.true(comparePoints(obs, exp))
178
178
 
179
179
  exp = [
@@ -192,7 +192,7 @@ test('arc (rotations)', (t) => {
192
192
  obs = path2.toPoints(geometry)
193
193
 
194
194
  t.notThrows(() => path2.validate(geometry))
195
- t.deepEqual(obs.length, 10)
195
+ t.is(obs.length, 10)
196
196
  t.true(comparePoints(obs, exp))
197
197
 
198
198
  exp = [
@@ -211,7 +211,7 @@ test('arc (rotations)', (t) => {
211
211
  obs = path2.toPoints(geometry)
212
212
 
213
213
  t.notThrows(() => path2.validate(geometry))
214
- t.deepEqual(obs.length, 10)
214
+ t.is(obs.length, 10)
215
215
  t.true(comparePoints(obs, exp))
216
216
 
217
217
  exp = [[-1.8369701987210297e-16, -1]]
@@ -219,6 +219,6 @@ test('arc (rotations)', (t) => {
219
219
  obs = path2.toPoints(geometry)
220
220
 
221
221
  t.notThrows(() => path2.validate(geometry))
222
- t.deepEqual(obs.length, 1)
222
+ t.is(obs.length, 1)
223
223
  t.true(comparePoints(obs, exp))
224
224
  })
@@ -1,8 +1,10 @@
1
1
  import test from 'ava'
2
2
 
3
+ import { geom2 } from '../geometries/index.js'
4
+
3
5
  import { TAU } from '../maths/constants.js'
4
6
 
5
- import { geom2 } from '../geometries/index.js'
7
+ import { measureArea } from '../measurements/measureArea.js'
6
8
 
7
9
  import { comparePoints } from '../../test/helpers/index.js'
8
10
 
@@ -13,7 +15,8 @@ test('circle (defaults)', (t) => {
13
15
  const pts = geom2.toPoints(geometry)
14
16
 
15
17
  t.notThrows(() => geom2.validate(geometry))
16
- t.deepEqual(pts.length, 32)
18
+ t.is(measureArea(geometry), 3.1214451522580537)
19
+ t.is(pts.length, 32)
17
20
  })
18
21
 
19
22
  test('circle (options)', (t) => {
@@ -56,7 +59,8 @@ test('circle (options)', (t) => {
56
59
  ]
57
60
 
58
61
  t.notThrows(() => geom2.validate(geometry))
59
- t.deepEqual(pts.length, 32)
62
+ t.is(measureArea(geometry), 38.23770311516116)
63
+ t.is(pts.length, 32)
60
64
  t.true(comparePoints(pts, exp))
61
65
 
62
66
  // test radius
@@ -82,7 +86,8 @@ test('circle (options)', (t) => {
82
86
  ]
83
87
 
84
88
  t.notThrows(() => geom2.validate(geometry))
85
- t.deepEqual(pts.length, 16)
89
+ t.is(measureArea(geometry), 37.5029763717788)
90
+ t.is(pts.length, 16)
86
91
  t.true(comparePoints(pts, exp))
87
92
 
88
93
  // test startAngle
@@ -106,7 +111,8 @@ test('circle (options)', (t) => {
106
111
  ]
107
112
 
108
113
  t.notThrows(() => geom2.validate(geometry))
109
- t.deepEqual(pts.length, 14)
114
+ t.is(measureArea(geometry), 28.127232278834093)
115
+ t.is(pts.length, 14)
110
116
  t.true(comparePoints(pts, exp))
111
117
 
112
118
  // test endAngle
@@ -122,7 +128,8 @@ test('circle (options)', (t) => {
122
128
  ]
123
129
 
124
130
  t.notThrows(() => geom2.validate(geometry))
125
- t.deepEqual(pts.length, 6)
131
+ t.is(measureArea(geometry), 9.3757440929447)
132
+ t.is(pts.length, 6)
126
133
  t.true(comparePoints(pts, exp))
127
134
 
128
135
  // test full rotation with non-zero startAngle
@@ -130,7 +137,8 @@ test('circle (options)', (t) => {
130
137
  pts = geom2.toPoints(geometry)
131
138
 
132
139
  t.notThrows(() => geom2.validate(geometry))
133
- t.deepEqual(pts.length, 32)
140
+ t.is(measureArea(geometry), 3.1214451522580537)
141
+ t.is(pts.length, 32)
134
142
 
135
143
  // test segments
136
144
  geometry = circle({ radius: 3.5, segments: 5 })
@@ -144,7 +152,8 @@ test('circle (options)', (t) => {
144
152
  ]
145
153
 
146
154
  t.notThrows(() => geom2.validate(geometry))
147
- t.deepEqual(pts.length, 5)
155
+ t.is(measureArea(geometry), 29.126105811539073)
156
+ t.is(pts.length, 5)
148
157
  t.true(comparePoints(pts, exp))
149
158
  })
150
159
 
@@ -152,5 +161,6 @@ test('circle (radius zero)', (t) => {
152
161
  const geometry = circle({ radius: 0 })
153
162
  const pts = geom2.toPoints(geometry)
154
163
  t.notThrows(() => geom2.validate(geometry))
164
+ t.is(measureArea(geometry), 0)
155
165
  t.is(pts.length, 0)
156
166
  })
@@ -2,6 +2,8 @@ import test from 'ava'
2
2
 
3
3
  import { geom3 } from '../geometries/index.js'
4
4
 
5
+ import { measureArea, measureVolume } from '../measurements/index.js'
6
+
5
7
  import { cube } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -10,6 +12,8 @@ test('cube (defaults)', (t) => {
10
12
  const obs = cube()
11
13
  const pts = geom3.toPoints(obs)
12
14
  t.notThrows(() => geom3.validate(obs))
15
+ t.is(measureArea(obs), 24)
16
+ t.is(measureVolume(obs), 7.999999999999999)
13
17
  t.is(pts.length, 6)
14
18
  })
15
19
 
@@ -27,6 +31,8 @@ test('cube (options)', (t) => {
27
31
  ]
28
32
 
29
33
  t.notThrows(() => geom3.validate(obs))
34
+ t.is(measureArea(obs), 294)
35
+ t.is(measureVolume(obs), 343)
30
36
  t.is(pts.length, 6)
31
37
  t.true(comparePolygonsAsPoints(pts, exp))
32
38
 
@@ -43,6 +49,8 @@ test('cube (options)', (t) => {
43
49
  ]
44
50
 
45
51
  t.notThrows(() => geom3.validate(obs))
52
+ t.is(measureArea(obs), 294)
53
+ t.is(measureVolume(obs), 343)
46
54
  t.is(pts.length, 6)
47
55
  t.true(comparePolygonsAsPoints(pts, exp))
48
56
  })
@@ -51,5 +59,7 @@ test('cube (zero size)', (t) => {
51
59
  const obs = cube({ size: 0 })
52
60
  const pts = geom3.toPoints(obs)
53
61
  t.notThrows(() => geom3.validate(obs))
62
+ t.is(measureArea(obs), 0)
63
+ t.is(measureVolume(obs), 0)
54
64
  t.is(pts.length, 0)
55
65
  })
@@ -2,6 +2,8 @@ import test from 'ava'
2
2
 
3
3
  import { geom3 } from '../geometries/index.js'
4
4
 
5
+ import { measureArea, measureVolume } from '../measurements/index.js'
6
+
5
7
  import { cuboid } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -18,6 +20,8 @@ test('cuboid (defaults)', (t) => {
18
20
  [[-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]]
19
21
  ]
20
22
  t.notThrows(() => geom3.validate(obs))
23
+ t.is(measureArea(obs), 24)
24
+ t.is(measureVolume(obs), 7.999999999999999)
21
25
  t.is(pts.length, 6)
22
26
  t.true(comparePolygonsAsPoints(pts, exp))
23
27
  })
@@ -36,6 +40,8 @@ test('cuboid (options)', (t) => {
36
40
  ]
37
41
 
38
42
  t.notThrows(() => geom3.validate(obs))
43
+ t.is(measureArea(obs), 216)
44
+ t.is(measureVolume(obs), 216)
39
45
  t.is(pts.length, 6)
40
46
  t.true(comparePolygonsAsPoints(pts, exp))
41
47
 
@@ -52,6 +58,8 @@ test('cuboid (options)', (t) => {
52
58
  ]
53
59
 
54
60
  t.notThrows(() => geom3.validate(obs))
61
+ t.is(measureArea(obs), 97.5)
62
+ t.is(measureVolume(obs), 47.25)
55
63
  t.is(pts.length, 6)
56
64
  t.true(comparePolygonsAsPoints(pts, exp))
57
65
  })
@@ -60,5 +68,7 @@ test('cuboid (zero size)', (t) => {
60
68
  const obs = cuboid({ size: [1, 1, 0] })
61
69
  const pts = geom3.toPoints(obs)
62
70
  t.notThrows(() => geom3.validate(obs))
71
+ t.is(measureArea(obs), 0)
72
+ t.is(measureVolume(obs), 0)
63
73
  t.is(pts.length, 0)
64
74
  })