@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jscad/modeling",
3
- "version": "3.0.0-alpha.0",
3
+ "version": "3.0.2-alpha.0",
4
4
  "description": "Constructive Solid Geometry (CSG) Library for JSCAD",
5
5
  "homepage": "https://openjscad.xyz/",
6
6
  "repository": "https://github.com/jscad/OpenJSCAD.org",
@@ -64,5 +64,5 @@
64
64
  "rollup": "^2.79.1",
65
65
  "rollup-plugin-banner": "^0.2.1"
66
66
  },
67
- "gitHead": "3656d36ed9cd738ab884e86aae5a2ce08d52adf7"
67
+ "gitHead": "ac80d1a7cb0a8efb21aff9193d4a8bccb6e31f1c"
68
68
  }
package/rollup.config.js CHANGED
@@ -16,6 +16,6 @@ export default {
16
16
  ],
17
17
  plugins: [
18
18
  banner('<%= pkg.description %>\n@module <%= pkg.name %>\n@version <%= pkg.version %>\n@license <%= pkg.license %>'),
19
- terser({ compress: { module: true }, mangle: false, format: { comments: 'some'} })
19
+ terser({ compress: { module: true }, mangle: false, format: { comments: 'some' } })
20
20
  ]
21
21
  }
@@ -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'
@@ -47,14 +45,12 @@ export const colorize = (color, ...objects) => {
47
45
  if (color.length < 3) throw new Error('color must contain R, G and B values')
48
46
  if (color.length === 3) color = [color[0], color[1], color[2], 1.0] // add alpha
49
47
 
50
- objects = flatten(objects)
51
- if (objects.length === 0) throw new Error('wrong number of arguments')
52
-
53
48
  const results = objects.map((object) => {
54
49
  if (geom2.isA(object)) return colorGeom2(color, object)
55
50
  if (geom3.isA(object)) return colorGeom3(color, object)
56
51
  if (path2.isA(object)) return colorPath2(color, object)
57
52
  if (poly3.isA(object)) return colorPoly3(color, object)
53
+ if (Array.isArray(object)) return colorize(color, ...object)
58
54
 
59
55
  object.color = color
60
56
  return object
@@ -11,14 +11,12 @@ test('color (rgb on objects)', (t) => {
11
11
  const obs = colorize([1, 0, 0], obj1, obj2)
12
12
  const exp1 = { color: [1, 0, 0, 1] }
13
13
  const exp2 = { id: 'a', color: [1, 0, 0, 1] }
14
+ const exp3 = { id: 'b', color: [1, 0, 0, 1] }
14
15
 
15
- t.is(obs.length, 3)
16
+ t.is(obs.length, 2)
16
17
  t.deepEqual(obs[0], exp1)
17
- t.deepEqual(obs[1], exp2)
18
-
19
- const obs3 = colorize([1, 0, 0], obj1)
20
- const exp3 = { color: [1, 0, 0, 1] }
21
- t.deepEqual(obs3, exp3)
18
+ t.deepEqual(obs[1][0], exp2)
19
+ t.deepEqual(obs[1][1], exp3)
22
20
  })
23
21
 
24
22
  test('color (rgba on objects)', (t) => {
@@ -28,10 +26,12 @@ test('color (rgba on objects)', (t) => {
28
26
  const obs = colorize([1, 1, 0.5, 0.8], obj1, obj2)
29
27
  const exp1 = { color: [1, 1, 0.5, 0.8] }
30
28
  const exp2 = { id: 'a', color: [1, 1, 0.5, 0.8] }
29
+ const exp3 = { id: 'b', color: [1, 1, 0.5, 0.8] }
31
30
 
32
- t.is(obs.length, 3)
31
+ t.is(obs.length, 2)
33
32
  t.deepEqual(obs[0], exp1)
34
- t.deepEqual(obs[1], exp2)
33
+ t.deepEqual(obs[1][0], exp2)
34
+ t.deepEqual(obs[1][1], exp3)
35
35
  })
36
36
 
37
37
  test('color (rgba on geometry)', (t) => {
@@ -1,5 +1,7 @@
1
1
  import * as mat4 from '../../maths/mat4/index.js'
2
2
 
3
+ import { reverse } from './reverse.js'
4
+
3
5
  /**
4
6
  * Transform the given geometry using the given matrix.
5
7
  * This is a lazy transform of the outlines, as this function only adjusts the transforms.
@@ -14,5 +16,11 @@ import * as mat4 from '../../maths/mat4/index.js'
14
16
  */
15
17
  export const transform = (matrix, geometry) => {
16
18
  const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
17
- return Object.assign({}, geometry, { transforms })
19
+ const transformed = Object.assign({}, geometry, { transforms })
20
+ // determine if the transform is mirroring in 2D
21
+ if (matrix[0] * matrix[5] - matrix[4] * matrix[1] < 0) {
22
+ // reverse the order to preserve the orientation
23
+ return reverse(transformed)
24
+ }
25
+ return transformed
18
26
  }
@@ -2,6 +2,12 @@ import test from 'ava'
2
2
 
3
3
  import { mat4 } from '../../maths/index.js'
4
4
 
5
+ import { measureArea } from '../../measurements/index.js'
6
+
7
+ import { mirrorX, mirrorY, mirrorZ } from '../../operations/transforms/index.js'
8
+
9
+ import { square } from '../../primitives/index.js'
10
+
5
11
  import { create, transform, toOutlines, toSides } from './index.js'
6
12
 
7
13
  import { comparePoints, compareVectors } from '../../../test/helpers/index.js'
@@ -50,3 +56,54 @@ test('transform: adjusts the transforms of geom2', (t) => {
50
56
  t.true(comparePoints(another.outlines[0], expected.outlines[0]))
51
57
  t.true(compareVectors(another.transforms, expected.transforms))
52
58
  })
59
+
60
+ test('transform: geom2 mirrorX', (t) => {
61
+ const geometry = square()
62
+ const transformed = mirrorX(geometry)
63
+ t.is(measureArea(geometry), 4)
64
+ // area will be negative unless we reversed the points
65
+ t.is(measureArea(transformed), 4)
66
+ const pts = toOutlines(transformed)[0]
67
+ const exp = [[1, 1], [-1, 1], [-1, -1], [1, -1]]
68
+ t.true(comparePoints(pts, exp))
69
+ t.deepEqual(toSides(transformed), [
70
+ [[1, 1], [-1, 1]],
71
+ [[-1, 1], [-1, -1]],
72
+ [[-1, -1], [1, -1]],
73
+ [[1, -1], [1, 1]]
74
+ ])
75
+ })
76
+
77
+ test('transform: geom2 mirrorY', (t) => {
78
+ const geometry = square()
79
+ const transformed = mirrorY(geometry)
80
+ t.is(measureArea(geometry), 4)
81
+ // area will be negative unless we reversed the points
82
+ t.is(measureArea(transformed), 4)
83
+ const pts = toOutlines(transformed)[0]
84
+ const exp = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
85
+ t.true(comparePoints(pts, exp))
86
+ t.deepEqual(toSides(transformed), [
87
+ [[-1, -1], [1, -1]],
88
+ [[1, -1], [1, 1]],
89
+ [[1, 1], [-1, 1]],
90
+ [[-1, 1], [-1, -1]]
91
+ ])
92
+ })
93
+
94
+ test('transform: geom2 mirrorZ', (t) => {
95
+ const geometry = square()
96
+ const transformed = mirrorZ(geometry)
97
+ t.is(measureArea(geometry), 4)
98
+ // area will be negative unless we DIDN'T reverse the points
99
+ t.is(measureArea(transformed), 4)
100
+ const pts = toOutlines(transformed)[0]
101
+ const exp = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
102
+ t.true(comparePoints(pts, exp))
103
+ t.deepEqual(toSides(transformed), [
104
+ [[-1, -1], [1, -1]],
105
+ [[1, -1], [1, 1]],
106
+ [[1, 1], [-1, 1]],
107
+ [[-1, 1], [-1, -1]]
108
+ ])
109
+ })
@@ -0,0 +1,4 @@
1
+ import type { Geom3 } from './type.d.ts'
2
+ import type { Vec3 } from '../../maths/vec3/type.d.ts'
3
+
4
+ export function fromPointsConvex(points: Array<Array<Vec3>>): Geom3
@@ -0,0 +1,25 @@
1
+ import { runner } from '../../operations/hulls/quickhull/index.js'
2
+ import { create } from './create.js'
3
+ import * as poly3 from '../poly3/index.js'
4
+
5
+ /**
6
+ * Construct a new convex 3D geometry from a list of unique points.
7
+ *
8
+ * @param {Array} uniquePoints - list of points to construct convex 3D geometry
9
+ * @returns {geom3} a new geometry
10
+ * @alias module:modeling/geometries/geom3.fromPointsConvex
11
+ */
12
+ export const fromPointsConvex = (uniquePoints) => {
13
+ if (!Array.isArray(uniquePoints)) {
14
+ throw new Error('the given points must be an array')
15
+ }
16
+
17
+ const faces = runner(uniquePoints, { skipTriangulation: true })
18
+
19
+ const polygons = faces.map((face) => {
20
+ const vertices = face.map((index) => uniquePoints[index])
21
+ return poly3.create(vertices)
22
+ })
23
+
24
+ return create(polygons)
25
+ }
@@ -0,0 +1,32 @@
1
+ import test from 'ava'
2
+
3
+ import { fromPointsConvex, validate } from './index.js'
4
+
5
+ test('fromPointsConvex (uniquePoints)', (t) => {
6
+ const out = []
7
+ for (let x = -9; x <= 9; ++x) {
8
+ for (let y = -9; y <= 9; ++y) {
9
+ for (let z = -9; z <= 9; ++z) {
10
+ if (x * x + y * y + z * z <= 96) {
11
+ out.push([x, y, z])
12
+ }
13
+ }
14
+ }
15
+ }
16
+
17
+ const obs = fromPointsConvex(out)
18
+ validate(obs)
19
+ t.is(obs.polygons.length, 170)
20
+ t.true(obs.polygons.every((f) => ([3, 4, 8, 9].indexOf(f.vertices.length) !== -1)))
21
+
22
+ const c = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
23
+ obs.polygons.forEach((f) => c[f.vertices.length]++)
24
+ t.is(c[3], 120)
25
+ t.is(c[4], 24)
26
+ t.is(c[8], 18)
27
+ t.is(c[9], 8)
28
+
29
+ let edges2 = 336 * 2
30
+ obs.polygons.forEach((f) => { edges2 -= f.vertices.length })
31
+ t.is(edges2, 0)
32
+ })
@@ -1,6 +1,7 @@
1
1
  export { clone } from './clone.js'
2
2
  export { create } from './create.js'
3
3
  export { fromPoints } from './fromPoints.js'
4
+ export { fromPointsConvex } from './fromPointsConvex.js'
4
5
  export { fromCompactBinary } from './fromCompactBinary.js'
5
6
  export { invert } from './invert.js'
6
7
  export { isA } from './isA.js'
@@ -17,6 +17,7 @@
17
17
  export { clone } from './clone.js'
18
18
  export { create } from './create.js'
19
19
  export { fromPoints } from './fromPoints.js'
20
+ export { fromPointsConvex } from './fromPointsConvex.js'
20
21
  export { fromCompactBinary } from './fromCompactBinary.js'
21
22
  export { invert } from './invert.js'
22
23
  export { isA } from './isA.js'
@@ -5,14 +5,13 @@
5
5
  * @see {@link geom2} - 2D geometry consisting of 2D outlines
6
6
  * @see {@link geom3} - 3D geometry consisting of polygons
7
7
  * @see {@link path2} - 2D geometry consisting of ordered points
8
- * @see {@link poly2} - 2D polygon consisting of ordered vertices
8
+ * @see {@link poly2} - 2D polygon consisting of ordered points
9
9
  * @see {@link poly3} - 3D polygon consisting of ordered vertices
10
- * @see {@link slice} - 3D geometry consisting of 3D outlines
10
+ * @see {@link slice} - 3D geometry consisting of 3D contours
11
11
  *
12
12
  * @module modeling/geometries
13
13
  * @example
14
- * import { geometries } from '@jscad/modeling'
15
- * const { geom2, geom3, path2, poly2, poly3 } = geometries
14
+ * import { geom2, geom3, path2, poly2, poly3, slice } from '@jscad/modeling'
16
15
  */
17
16
  export * as geom2 from './geom2/index.js'
18
17
  export * as geom3 from './geom3/index.js'
@@ -14,7 +14,7 @@ import { toPoints } from './toPoints.js'
14
14
  * In other words, the trailing gradient of the geometry matches the new gradient of the curve.
15
15
  * @param {object} options - options for construction
16
16
  * @param {Array} options.controlPoints - list of control points (2D) for the Bézier curve
17
- * @param {number} [options.segment=16] - number of segments per 360 rotation
17
+ * @param {number} [options.segments=16] - number of segments per 360 rotation
18
18
  * @param {Path2} geometry - the path of which to append points
19
19
  * @returns {Path2} a new path with the appended points
20
20
  * @alias module:modeling/geometries/path2.appendBezier
@@ -15,7 +15,7 @@ export { isA } from './isA.js'
15
15
  export { isConvex } from './isConvex.js'
16
16
  export { measureArea } from './measureArea.js'
17
17
  export { measureBoundingBox } from './measureBoundingBox.js'
18
- export { measureBoundingSphere } from './measureBoundingSphere.js'
18
+ export { measureBoundingSphere, measureBoundingSphereAndCache } from './measureBoundingSphere.js'
19
19
  export { measureSignedVolume } from './measureSignedVolume.js'
20
20
  export { plane } from './plane.js'
21
21
  export { toVertices } from './toVertices.js'
@@ -1,6 +1,8 @@
1
1
  import * as vec3 from '../../maths/vec3/index.js'
2
2
 
3
3
  /**
4
+ * Measure the bounding box of the given polygon.
5
+ *
4
6
  * @param {Poly3} polygon - the polygon to measure
5
7
  * @returns {Array} an array of two vectors (3D); minimum and maximum coordinates
6
8
  * @alias module:modeling/geometries/poly3.measureBoundingBox
@@ -1,4 +1,5 @@
1
1
  import type { Poly3 } from './type.d.ts'
2
2
  import type { Vec4 } from '../../maths/vec4/type.d.ts'
3
3
 
4
- export function measureBoundingSphere(polygon: Poly3): Vec4
4
+ export function measureBoundingSphere(out: Vec4, polygon: Poly3): Vec4
5
+ export function measureBoundingSphereAndCache(polygon: Poly3): Vec4
@@ -4,16 +4,14 @@ const cache = new WeakMap()
4
4
 
5
5
  /**
6
6
  * Measure the bounding sphere of the given polygon.
7
+ *
8
+ * @param {Vec4} out - receiving vector
7
9
  * @param {Poly3} polygon - the polygon to measure
8
10
  * @returns {Vec4} the computed bounding sphere; center vertex (3D) and radius
9
11
  * @alias module:modeling/geometries/poly3.measureBoundingSphere
10
12
  */
11
- export const measureBoundingSphere = (polygon) => {
12
- const boundingSphere = cache.get(polygon)
13
- if (boundingSphere) return boundingSphere
14
-
13
+ export const measureBoundingSphere = (out, polygon) => {
15
14
  const vertices = polygon.vertices
16
- const out = vec4.create()
17
15
 
18
16
  if (vertices.length === 0) {
19
17
  out[0] = 0
@@ -31,14 +29,15 @@ export const measureBoundingSphere = (polygon) => {
31
29
  let maxy = minx
32
30
  let maxz = minx
33
31
 
34
- vertices.forEach((v) => {
32
+ for (let i = 0; i < vertices.length; i++) {
33
+ const v = vertices[i]
35
34
  if (minx[0] > v[0]) minx = v
36
35
  if (miny[1] > v[1]) miny = v
37
36
  if (minz[2] > v[2]) minz = v
38
37
  if (maxx[0] < v[0]) maxx = v
39
38
  if (maxy[1] < v[1]) maxy = v
40
39
  if (maxz[2] < v[2]) maxz = v
41
- })
40
+ }
42
41
 
43
42
  out[0] = (minx[0] + maxx[0]) * 0.5 // center of sphere
44
43
  out[1] = (miny[1] + maxy[1]) * 0.5
@@ -48,7 +47,25 @@ export const measureBoundingSphere = (polygon) => {
48
47
  const z = out[2] - maxz[2]
49
48
  out[3] = Math.sqrt(x * x + y * y + z * z) // radius of sphere
50
49
 
51
- cache.set(polygon, out)
50
+ return out
51
+ }
52
+
53
+ /**
54
+ * Measure the bounding sphere of the given polygon.
55
+ *
56
+ * This version maintains a cache, retrievning previously calculated bounds if found.
57
+ *
58
+ * @param {Poly3} polygon - the polygon to measure
59
+ * @returns {Vec4} the computed bounding sphere; center vertex (3D) and radius
60
+ * @alias module:modeling/geometries/poly3.measureBoundingSphere
61
+ */
62
+ export const measureBoundingSphereAndCache = (polygon) => {
63
+ const boundingSphere = cache.get(polygon)
64
+ if (boundingSphere) return boundingSphere
52
65
 
66
+ const out = [0, 0, 0, 0]
67
+ measureBoundingSphere(out, polygon)
68
+
69
+ cache.set(polygon, out)
53
70
  return out
54
71
  }
@@ -7,19 +7,22 @@ import { measureBoundingSphere, create, transform } from './index.js'
7
7
  test('poly3: measureBoundingSphere() should return correct values', (t) => {
8
8
  let ply1 = create()
9
9
  let exp1 = [0, 0, 0, 0]
10
- let ret1 = measureBoundingSphere(ply1)
10
+ let ret1 = [0, 0, 0, 0]
11
+ ret1 = measureBoundingSphere(ret1, ply1)
11
12
  t.deepEqual(ret1, exp1)
12
13
 
13
14
  // simple triangle
14
15
  let ply2 = create([[0, 0, 0], [0, 10, 0], [0, 10, 10]])
15
16
  let exp2 = [0, 5, 5, 7.0710678118654755]
16
- let ret2 = measureBoundingSphere(ply2)
17
+ let ret2 = [0, 0, 0, 0]
18
+ ret2 = measureBoundingSphere(ret2, ply2)
17
19
  t.deepEqual(ret2, exp2)
18
20
 
19
21
  // simple square
20
22
  let ply3 = create([[0, 0, 0], [0, 10, 0], [0, 10, 10], [0, 0, 10]])
21
23
  let exp3 = [0, 5, 5, 7.0710678118654755]
22
- let ret3 = measureBoundingSphere(ply3)
24
+ let ret3 = [0, 0, 0, 0]
25
+ ret3 = measureBoundingSphere(ret3, ply3)
23
26
  t.deepEqual(ret3, exp3)
24
27
 
25
28
  // V-shape
@@ -37,7 +40,8 @@ test('poly3: measureBoundingSphere() should return correct values', (t) => {
37
40
  ]
38
41
  let ply4 = create(vertices)
39
42
  let exp4 = [0, 4.5, 3, 4.6097722286464435]
40
- let ret4 = measureBoundingSphere(ply4)
43
+ let ret4 = [0, 0, 0, 0]
44
+ ret4 = measureBoundingSphere(ret4, ply4)
41
45
  t.deepEqual(ret4, exp4)
42
46
 
43
47
  // rotated to various angles
@@ -46,10 +50,10 @@ test('poly3: measureBoundingSphere() should return correct values', (t) => {
46
50
  ply2 = transform(rotation, ply2)
47
51
  ply3 = transform(rotation, ply3)
48
52
  ply4 = transform(rotation, ply4)
49
- ret1 = measureBoundingSphere(ply1)
50
- ret2 = measureBoundingSphere(ply2)
51
- ret3 = measureBoundingSphere(ply3)
52
- ret4 = measureBoundingSphere(ply4)
53
+ ret1 = measureBoundingSphere(ret1, ply1)
54
+ ret2 = measureBoundingSphere(ret2, ply2)
55
+ ret3 = measureBoundingSphere(ret3, ply3)
56
+ ret4 = measureBoundingSphere(ret4, ply4)
53
57
  exp1 = [0, 0, 0, 0]
54
58
  t.deepEqual(ret1, exp1)
55
59
  exp2 = [-3.5355339059327373, 3.5355339059327378, 5, 7.0710678118654755]
@@ -6,6 +6,6 @@ export interface Poly3 {
6
6
  vertices: Array<Vec3>
7
7
  color?: Color
8
8
 
9
- // used internally for caching:
9
+ // used internally for calculations
10
10
  plane?: Plane
11
11
  }
@@ -28,10 +28,9 @@ export const validate = (object) => {
28
28
  // contours must be coplanar
29
29
  const contourPlane = poly3.plane(poly3.create(contour))
30
30
  if (!plane.equals(slicePlane, contourPlane)) {
31
- throw new Error(`slice contours must be coplanar`)
31
+ throw new Error('slice contours must be coplanar')
32
32
  }
33
33
 
34
-
35
34
  for (let i = 0; i < contour.length; i++) {
36
35
  const vertex = contour[i]
37
36
  // check for infinity, nan
package/src/index.js CHANGED
@@ -13,3 +13,44 @@ export * from './operations/hulls/index.js'
13
13
  export * from './operations/modifiers/index.js'
14
14
  export * from './operations/offsets/index.js'
15
15
  export * from './operations/transforms/index.js'
16
+
17
+ // V2 API compatibility:
18
+ export * as colors from './colors/index.js'
19
+ export * as curves from './curves/index.js'
20
+ import { geom2, geom3, path2, poly2, poly3 } from './geometries/index.js'
21
+ export const geometries = {
22
+ geom2: {
23
+ ...geom2,
24
+ create: (sides) => geom2.fromSides(sides),
25
+ fromPoints: (points) => geometries.geom2.create([points])
26
+ },
27
+ geom3,
28
+ path2,
29
+ poly2,
30
+ poly3: {
31
+ ...poly3,
32
+ fromPoints: (points) => poly3.create([points]),
33
+ fromPointsAndPlane: poly3.fromVerticesAndPlane,
34
+ toPoints: poly3.toVertices
35
+ }
36
+ }
37
+ export * as maths from './maths/index.js'
38
+ export * as measurements from './measurements/index.js'
39
+ export * as primitives from './primitives/index.js'
40
+ export * as text from './text/index.js'
41
+ export * as booleans from './operations/booleans/index.js'
42
+ import * as extrusion from './operations/extrusions/index.js'
43
+ import * as offsets from './operations/offsets/index.js'
44
+ export const extrusions = {
45
+ ...extrusion,
46
+ extrudeRectangular: (opt, geom) => extrusions.extrudeLinear(opt, offsets.offset(opt, geom)),
47
+ slice: geometries.slice
48
+ }
49
+ export * as hulls from './operations/hulls/index.js'
50
+ export * as modifiers from './operations/modifiers/index.js'
51
+ export const expansions = {
52
+ ...offsets,
53
+ expand: offsets.offset
54
+ }
55
+ export * as transforms from './operations/transforms/index.js'
56
+ export * as utils from './utils/index.js'
@@ -15,3 +15,4 @@ export * as vec2 from './vec2/index.js'
15
15
  export * as vec3 from './vec3/index.js'
16
16
  export * as vec4 from './vec4/index.js'
17
17
  export { cos, sin } from './utils/trigonometry.js'
18
+ export { area } from './utils/area.js'
@@ -18,7 +18,7 @@ export const isOnlyTransformScale = (matrix) => (
18
18
  )
19
19
 
20
20
  /**
21
- * @param {number} num
21
+ * @param {number} num
22
22
  * @returns {boolean}
23
23
  */
24
24
  const isZero = (num) => Math.abs(num) < Number.EPSILON
@@ -14,7 +14,6 @@ import { measureArea } from './measureArea.js'
14
14
  */
15
15
  export const measureAggregateArea = (...geometries) => {
16
16
  geometries = flatten(geometries)
17
- if (geometries.length === 0) throw new Error('measureAggregateArea: no geometries supplied')
18
17
  const areas = measureArea(geometries)
19
18
  if (geometries.length === 1) {
20
19
  return areas
@@ -15,7 +15,6 @@ import { measureBoundingBox } from './measureBoundingBox.js'
15
15
  */
16
16
  export const measureAggregateBoundingBox = (...geometries) => {
17
17
  geometries = flatten(geometries)
18
- if (geometries.length === 0) throw new Error('measureAggregateBoundingBox: no geometries supplied')
19
18
  const bounds = measureBoundingBox(geometries)
20
19
  if (geometries.length === 1) {
21
20
  return bounds
@@ -18,7 +18,6 @@ import { calculateEpsilonFromBounds } from './calculateEpsilonFromBounds.js'
18
18
  */
19
19
  export const measureAggregateEpsilon = (...geometries) => {
20
20
  geometries = flatten(geometries)
21
- if (geometries.length === 0) throw new Error('measureAggregateEpsilon: no geometries supplied')
22
21
  const bounds = measureAggregateBoundingBox(geometries)
23
22
 
24
23
  let dimensions = 0
@@ -14,7 +14,6 @@ import { measureVolume } from './measureVolume.js'
14
14
  */
15
15
  export const measureAggregateVolume = (...geometries) => {
16
16
  geometries = flatten(geometries)
17
- if (geometries.length === 0) throw new Error('measureAggregateVolume: no geometries supplied')
18
17
  const volumes = measureVolume(geometries)
19
18
  if (geometries.length === 1) {
20
19
  return volumes
@@ -85,7 +85,6 @@ const measureAreaOfSlice = (geometry) => {
85
85
  */
86
86
  export const measureArea = (...geometries) => {
87
87
  geometries = flatten(geometries)
88
- if (geometries.length === 0) throw new Error('wrong number of arguments')
89
88
 
90
89
  const results = geometries.map((geometry) => {
91
90
  if (path2.isA(geometry)) return measureAreaOfPath2(geometry)
@@ -121,7 +121,6 @@ const measureBoundingBoxOfSlice = (geometry) => {
121
121
  */
122
122
  export const measureBoundingBox = (...geometries) => {
123
123
  geometries = flatten(geometries)
124
- if (geometries.length === 0) throw new Error('wrong number of arguments')
125
124
 
126
125
  const results = geometries.map((geometry) => {
127
126
  if (path2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfPath2)
@@ -64,17 +64,13 @@ const measureBoundingSphereOfPoints = (points) => {
64
64
  * Measure the bounding sphere of the given (path2) geometry.
65
65
  * @return {[[x, y, z], radius]} the bounding sphere for the geometry
66
66
  */
67
- const measureBoundingSphereOfPath2 = (points) => {
68
- return measureBoundingSphereOfPoints(path2.toPoints(points))
69
- }
67
+ const measureBoundingSphereOfPath2 = (geometry) => measureBoundingSphereOfPoints(path2.toPoints(geometry))
70
68
 
71
69
  /*
72
70
  * Measure the bounding sphere of the given (geom2) geometry.
73
71
  * @return {[[x, y, z], radius]} the bounding sphere for the geometry
74
72
  */
75
- const measureBoundingSphereOfGeom2 = (geometry) => {
76
- return measureBoundingSphereOfPoints(geom2.toPoints(geometry))
77
- }
73
+ const measureBoundingSphereOfGeom2 = (geometry) => measureBoundingSphereOfPoints(geom2.toPoints(geometry))
78
74
 
79
75
  /*
80
76
  * Measure the bounding sphere of the given (geom3) geometry.
@@ -20,7 +20,6 @@ import { measureBoundingBox } from './measureBoundingBox.js'
20
20
  */
21
21
  export const measureEpsilon = (...geometries) => {
22
22
  geometries = flatten(geometries)
23
- if (geometries.length === 0) throw new Error('wrong number of arguments')
24
23
 
25
24
  const results = geometries.map((geometry) => {
26
25
  if (path2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
@@ -34,7 +34,6 @@ const measureVolumeOfGeom3 = (geometry) => {
34
34
  */
35
35
  export const measureVolume = (...geometries) => {
36
36
  geometries = flatten(geometries)
37
- if (geometries.length === 0) throw new Error('wrong number of arguments')
38
37
 
39
38
  const results = geometries.map((geometry) => {
40
39
  if (geom3.isA(geometry)) return measureVolumeOfGeom3(geometry)
@@ -1,3 +1,4 @@
1
1
  export { intersect } from './intersect.js'
2
2
  export { subtract } from './subtract.js'
3
3
  export { union } from './union.js'
4
+ export { scission } from './scission.js'
@@ -1,5 +1,5 @@
1
1
  import { areAllShapesTheSameType } from '../../utils/areAllShapesTheSameType.js'
2
- import { flatten } from '../../utils/flatten.js'
2
+ import { coalesce } from '../../utils/coalesce.js'
3
3
 
4
4
  import * as geom2 from '../../geometries/geom2/index.js'
5
5
  import * as geom3 from '../../geometries/geom3/index.js'
@@ -13,11 +13,11 @@ import { intersectGeom3 } from './intersectGeom3.js'
13
13
  * The given geometries should be of the same type, either geom2 or geom3.
14
14
  *
15
15
  * @param {...Object} geometries - list of geometries
16
- * @returns {Geom2|geom3} a new geometry
16
+ * @returns {Geom2|Geom3} a new geometry
17
17
  * @alias module:modeling/booleans.intersect
18
18
  *
19
19
  * @example
20
- * let myshape = intersect(cube({size: [5,5,5]}), cube({size: [5,5,5], center: [5,5,5]}))
20
+ * let myshape = intersect(cube({size: 5}), cube({size: 5, center: [3,3,3]}))
21
21
  *
22
22
  * @example
23
23
  * +-------+
@@ -30,9 +30,9 @@ import { intersectGeom3 } from './intersectGeom3.js'
30
30
  * +-------+
31
31
  */
32
32
  export const intersect = (...geometries) => {
33
- geometries = flatten(geometries)
34
- if (geometries.length === 0) throw new Error('intersect wrong number of arguments')
33
+ geometries = coalesce(geometries)
35
34
 
35
+ if (geometries.length === 0) return undefined
36
36
  if (!areAllShapesTheSameType(geometries)) {
37
37
  throw new Error('intersect arguments must be the same geometry type')
38
38
  }