@jscad/modeling 3.0.4-alpha.0 → 3.0.5-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 (78) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/jscad-modeling.es.js +2 -2
  3. package/dist/jscad-modeling.min.js +2 -2
  4. package/package.json +4 -4
  5. package/rollup.config.js +1 -1
  6. package/src/colors/colorize.js +17 -1
  7. package/src/geometries/geom2/fromPoints.d.ts +4 -0
  8. package/src/geometries/geom2/fromPoints.js +28 -0
  9. package/src/geometries/geom2/fromPoints.test.js +22 -0
  10. package/src/geometries/geom2/index.d.ts +1 -0
  11. package/src/geometries/geom2/index.js +1 -0
  12. package/src/geometries/path3/clone.d.ts +3 -0
  13. package/src/geometries/path3/clone.js +11 -0
  14. package/src/geometries/path3/index.d.ts +1 -0
  15. package/src/geometries/path3/index.js +1 -0
  16. package/src/geometries/poly2/type.d.ts +1 -5
  17. package/src/geometries/slice/fromOutlines.d.ts +5 -0
  18. package/src/geometries/slice/fromOutlines.js +16 -0
  19. package/src/geometries/slice/fromOutlines.test.js +17 -0
  20. package/src/geometries/slice/index.d.ts +1 -1
  21. package/src/geometries/slice/index.js +1 -1
  22. package/src/maths/plane/fromNormalAndPoint.js +4 -6
  23. package/src/maths/plane/fromPoints.js +8 -7
  24. package/src/maths/plane/fromPointsRandom.js +13 -13
  25. package/src/measurements/measureAggregateEpsilon.js +3 -1
  26. package/src/measurements/measureAggregateEpsilon.test.js +1 -1
  27. package/src/measurements/measureArea.js +6 -4
  28. package/src/measurements/measureArea.test.js +4 -1
  29. package/src/measurements/measureBoundingBox.js +16 -2
  30. package/src/measurements/measureBoundingBox.test.js +4 -1
  31. package/src/measurements/measureBoundingSphere.js +38 -29
  32. package/src/measurements/measureBoundingSphere.test.js +4 -1
  33. package/src/measurements/measureCenterOfMass.js +3 -2
  34. package/src/measurements/measureEpsilon.js +4 -2
  35. package/src/operations/booleans/trees/splitPolygonByPlane.d.ts +1 -3
  36. package/src/operations/booleans/union.js +1 -1
  37. package/src/operations/extrusions/extrudeFromSlices.js +1 -1
  38. package/src/operations/extrusions/extrudeFromSlices.test.js +1 -1
  39. package/src/operations/extrusions/extrudeHelical.js +2 -1
  40. package/src/operations/extrusions/extrudeLinear.js +1 -1
  41. package/src/operations/extrusions/extrudeLinearGeom2.js +2 -1
  42. package/src/operations/extrusions/extrudeRotate.js +1 -1
  43. package/src/operations/hulls/hull.js +3 -2
  44. package/src/operations/hulls/toUniquePoints.js +3 -0
  45. package/src/operations/modifiers/generalize.js +9 -2
  46. package/src/operations/modifiers/snap.js +22 -3
  47. package/src/operations/transforms/align.js +2 -1
  48. package/src/operations/transforms/align.test.js +1 -1
  49. package/src/operations/transforms/mirror.js +6 -2
  50. package/src/operations/transforms/rotate.js +6 -2
  51. package/src/operations/transforms/scale.js +6 -2
  52. package/src/operations/transforms/transform.js +6 -2
  53. package/src/operations/transforms/transform.test.js +16 -5
  54. package/src/operations/transforms/translate.js +6 -2
  55. package/src/primitives/arc.js +11 -10
  56. package/src/primitives/circle.js +10 -9
  57. package/src/primitives/cube.js +5 -6
  58. package/src/primitives/cuboid.js +6 -6
  59. package/src/primitives/cylinder.js +8 -8
  60. package/src/primitives/cylinderElliptic.js +11 -11
  61. package/src/primitives/ellipse.js +10 -9
  62. package/src/primitives/ellipsoid.js +8 -8
  63. package/src/primitives/geodesicSphere.js +6 -6
  64. package/src/primitives/line.js +2 -0
  65. package/src/primitives/polygon.js +6 -7
  66. package/src/primitives/polyhedron.js +7 -8
  67. package/src/primitives/rectangle.js +6 -6
  68. package/src/primitives/roundedCuboid.js +8 -8
  69. package/src/primitives/roundedCylinder.js +9 -9
  70. package/src/primitives/roundedRectangle.js +8 -8
  71. package/src/primitives/sphere.js +7 -8
  72. package/src/primitives/square.js +6 -6
  73. package/src/primitives/star.js +10 -10
  74. package/src/primitives/torus.js +11 -11
  75. package/src/primitives/triangle.js +7 -6
  76. package/src/utils/areAllShapesTheSameType.js +4 -0
  77. package/src/geometries/slice/fromGeom2.d.ts +0 -5
  78. package/src/geometries/slice/fromGeom2.js +0 -17
@@ -95,9 +95,10 @@ export const measureCenterOfMass = (...geometries) => {
95
95
  geometries = flatten(geometries)
96
96
 
97
97
  const results = geometries.map((geometry) => {
98
- // NOTE: center of mass for geometry path2 is not possible
99
- if (geom2.isA(geometry)) return measureCenterOfMassGeom2(geometry)
100
98
  if (geom3.isA(geometry)) return measureCenterOfMassGeom3(geometry)
99
+ if (geom2.isA(geometry)) return measureCenterOfMassGeom2(geometry)
100
+ // TODO if (slice.isA(geometry)) return measureCenterOfMassSlice(geometry)
101
+ // NOTE: center of mass for geometry path2 and path3 is not possible
101
102
  return [0, 0, 0]
102
103
  })
103
104
  return results.length === 1 ? results[0] : results
@@ -3,6 +3,7 @@ import { flatten } from '../utils/flatten.js'
3
3
  import * as geom2 from '../geometries/geom2/index.js'
4
4
  import * as geom3 from '../geometries/geom3/index.js'
5
5
  import * as path2 from '../geometries/path2/index.js'
6
+ import * as path3 from '../geometries/path3/index.js'
6
7
  import * as slice from '../geometries/slice/index.js'
7
8
 
8
9
  import { calculateEpsilonFromBounds } from './calculateEpsilonFromBounds.js'
@@ -22,9 +23,10 @@ export const measureEpsilon = (...geometries) => {
22
23
  geometries = flatten(geometries)
23
24
 
24
25
  const results = geometries.map((geometry) => {
25
- if (path2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
26
- if (geom2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
27
26
  if (geom3.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
27
+ if (geom2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
28
+ if (path2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
29
+ if (path3.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
28
30
  if (slice.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
29
31
  return 0
30
32
  })
@@ -28,6 +28,4 @@ interface SplitRes
28
28
  // In case the polygon is spanning, returns:
29
29
  // .front: a Polygon3 of the front part
30
30
  // .back: a Polygon3 of the back part
31
- declare function splitPolygonByPlane(plane: Plane, polygon: Poly3): SplitRes;
32
-
33
- export default splitPolygonByPlane;
31
+ export declare function splitPolygonByPlane(plane: Plane, polygon: Poly3): SplitRes;
@@ -37,7 +37,7 @@ export const union = (...geometries) => {
37
37
  }
38
38
 
39
39
  const geometry = geometries[0]
40
- // if (path.isA(geometry)) return unionPath(matrix, geometries)
40
+ // TODO if (path2.isA(geometry)) return unionPath(geometries)
41
41
  if (geom2.isA(geometry)) return unionGeom2(geometries)
42
42
  if (geom3.isA(geometry)) return unionGeom3(geometries)
43
43
  throw new Error('union unsupported geometry type')
@@ -9,7 +9,7 @@ import { extrudeWalls } from './extrudeWalls.js'
9
9
 
10
10
  const defaultCallback = (progress, index, base) => {
11
11
  let baseSlice = null
12
- if (geom2.isA(base)) baseSlice = slice.fromGeom2(base)
12
+ if (geom2.isA(base)) baseSlice = slice.fromOutlines(geom2.toOutlines(base))
13
13
  if (poly3.isA(base)) baseSlice = slice.fromVertices(poly3.toVertices(base))
14
14
 
15
15
  return progress === 0 || progress === 1 ? slice.transform(mat4.fromTranslation(mat4.create(), [0, 0, progress]), baseSlice) : null
@@ -111,7 +111,7 @@ test('extrudeFromSlices (changing shape, changing dimensions)', (t) => {
111
111
  numberOfSlices: 5,
112
112
  callback: (progress, count, base) => {
113
113
  const newShape = circle({ radius: 5 + count, segments: 4 + count })
114
- let newSlice = slice.fromGeom2(newShape)
114
+ let newSlice = slice.fromOutlines(geom2.toOutlines(newShape))
115
115
  newSlice = slice.transform(mat4.fromTranslation(mat4.create(), [0, 0, count * 10]), newSlice)
116
116
  return newSlice
117
117
  }
@@ -1,4 +1,5 @@
1
1
  import { TAU } from '../../maths/constants.js'
2
+ import * as geom2 from '../../geometries/geom2/index.js'
2
3
  import * as slice from '../../geometries/slice/index.js'
3
4
  import * as mat4 from '../../maths/mat4/index.js'
4
5
  import { measureBoundingBox } from '../../measurements/measureBoundingBox.js'
@@ -45,7 +46,7 @@ export const extrudeHelical = (options, geometry) => {
45
46
 
46
47
  if (segmentsPerRotation < minNumberOfSegments) { throw new Error('The number of segments per rotation needs to be at least 3.') }
47
48
 
48
- let baseSlice = slice.fromGeom2(geometry)
49
+ let baseSlice = slice.fromOutlines(geom2.toOutlines(geometry))
49
50
 
50
51
  const bounds = measureBoundingBox(geometry)
51
52
  if (bounds[1][0] <= 0) {
@@ -28,7 +28,7 @@ export const extrudeLinear = (options, ...objects) => {
28
28
  }
29
29
  const { height, twistAngle, twistSteps, repair } = Object.assign({ }, defaults, options)
30
30
 
31
- options = { offset: [0, 0, height], twistAngle, twistSteps, repair }
31
+ options = { height, twistAngle, twistSteps, repair, offset: [0, 0, height] }
32
32
 
33
33
  const results = objects.map((object) => {
34
34
  if (path2.isA(object)) return extrudeLinearPath2(options, object)
@@ -1,6 +1,7 @@
1
1
  import * as mat4 from '../../maths/mat4/index.js'
2
2
  import * as vec3 from '../../maths/vec3/index.js'
3
3
 
4
+ import * as geom2 from '../../geometries/geom2/index.js'
4
5
  import * as slice from '../../geometries/slice/index.js'
5
6
 
6
7
  import { extrudeFromSlices } from './extrudeFromSlices.js'
@@ -34,7 +35,7 @@ export const extrudeLinearGeom2 = (options, geometry) => {
34
35
  // convert to vector in order to perform transforms
35
36
  const offsetV = vec3.clone(offset)
36
37
 
37
- let baseSlice = slice.fromGeom2(geometry)
38
+ let baseSlice = slice.fromOutlines(geom2.toOutlines(geometry))
38
39
  if (offsetV[2] < 0) baseSlice = slice.reverse(baseSlice)
39
40
 
40
41
  const matrix = mat4.create()
@@ -106,7 +106,7 @@ export const extrudeRotate = (options, geometry) => {
106
106
 
107
107
  const rotationPerSlice = totalRotation / segments
108
108
  const isCapped = Math.abs(totalRotation) < TAU
109
- let baseSlice = slice.fromGeom2(sliceGeometry)
109
+ let baseSlice = slice.fromOutlines(geom2.toOutlines(sliceGeometry))
110
110
  baseSlice = slice.reverse(baseSlice)
111
111
 
112
112
  const matrix = mat4.create()
@@ -42,9 +42,10 @@ export const hull = (...geometries) => {
42
42
  }
43
43
 
44
44
  const geometry = geometries[0]
45
- if (path2.isA(geometry)) return hullPath2(geometries)
46
- if (geom2.isA(geometry)) return hullGeom2(geometries)
47
45
  if (geom3.isA(geometry)) return hullGeom3(geometries)
46
+ if (geom2.isA(geometry)) return hullGeom2(geometries)
47
+ if (path2.isA(geometry)) return hullPath2(geometries)
48
+ // FIXME return geom3? if (path3.isA(geometry)) return hullPath3(geometries)
48
49
 
49
50
  // FIXME should this throw an error for unknown geometries?
50
51
  return geometry
@@ -1,6 +1,7 @@
1
1
  import * as geom2 from '../../geometries/geom2/index.js'
2
2
  import * as geom3 from '../../geometries/geom3/index.js'
3
3
  import * as path2 from '../../geometries/path2/index.js'
4
+ import * as path3 from '../../geometries/path3/index.js'
4
5
 
5
6
  /*
6
7
  * Return the unique vertices of a geometry
@@ -25,6 +26,8 @@ export const toUniquePoints = (geometries) => {
25
26
  geom3.toVertices(geometry).forEach((vertices) => vertices.forEach(addPoint))
26
27
  } else if (path2.isA(geometry)) {
27
28
  path2.toPoints(geometry).forEach(addPoint)
29
+ } else if (path3.isA(geometry)) {
30
+ path3.toVertices(geometry).forEach(addPoint)
28
31
  }
29
32
  })
30
33
 
@@ -3,6 +3,8 @@ import { measureEpsilon } from '../../measurements/measureEpsilon.js'
3
3
  import * as geom2 from '../../geometries/geom2/index.js'
4
4
  import * as geom3 from '../../geometries/geom3/index.js'
5
5
  import * as path2 from '../../geometries/path2/index.js'
6
+ import * as path3 from '../../geometries/path3/index.js'
7
+ import * as slice from '../../geometries/slice/index.js'
6
8
 
7
9
  import { snapPolygons } from './snapPolygons.js'
8
10
  import { mergePolygons } from './mergePolygons.js'
@@ -13,6 +15,10 @@ import { triangulatePolygons } from './triangulatePolygons.js'
13
15
  */
14
16
  const generalizePath2 = (options, geometry) => geometry
15
17
 
18
+ /*
19
+ */
20
+ const generalizePath3 = (options, geometry) => geometry
21
+
16
22
  /*
17
23
  */
18
24
  const generalizeGeom2 = (options, geometry) => geometry
@@ -66,9 +72,10 @@ const generalizeGeom3 = (options, geometry) => {
66
72
  */
67
73
  export const generalize = (options, ...geometries) => {
68
74
  const results = geometries.map((geometry) => {
69
- if (path2.isA(geometry)) return generalizePath2(options, geometry)
70
- if (geom2.isA(geometry)) return generalizeGeom2(options, geometry)
71
75
  if (geom3.isA(geometry)) return generalizeGeom3(options, geometry)
76
+ if (geom2.isA(geometry)) return generalizeGeom2(options, geometry)
77
+ if (path2.isA(geometry)) return generalizePath2(options, geometry)
78
+ if (path3.isA(geometry)) return generalizePath3(options, geometry)
72
79
  if (Array.isArray(geometry)) return generalize(options, ...geometry)
73
80
  return geometry
74
81
  })
@@ -1,22 +1,38 @@
1
1
  import * as vec2 from '../../maths/vec2/index.js'
2
+ import * as vec3 from '../../maths/vec2/index.js'
2
3
 
3
4
  import * as geom2 from '../../geometries/geom2/index.js'
4
5
  import * as geom3 from '../../geometries/geom3/index.js'
5
6
  import * as path2 from '../../geometries/path2/index.js'
7
+ import * as path3 from '../../geometries/path3/index.js'
6
8
  import * as poly2 from '../../geometries/poly2/index.js'
7
9
 
8
10
  import { measureEpsilon } from '../../measurements/measureEpsilon.js'
9
11
 
10
12
  import { snapPolygons } from './snapPolygons.js'
11
13
 
14
+ /*
15
+ */
12
16
  const snapPath2 = (geometry) => {
13
17
  const epsilon = measureEpsilon(geometry)
14
18
  const points = path2.toPoints(geometry)
15
19
  const newPoints = points.map((point) => vec2.snap(vec2.create(), point, epsilon))
16
20
  // snap can produce duplicate points, remove those
17
- return path2.create(newPoints)
21
+ return path2.fromPoints({}, newPoints)
22
+ }
23
+
24
+ /*
25
+ */
26
+ const snapPath3 = (geometry) => {
27
+ const epsilon = measureEpsilon(geometry)
28
+ const vertices = path3.toVertices(geometry)
29
+ const newVertices = vertices.map((vertice) => vec3.snap(vec3.create(), vertice, epsilon))
30
+ // snap can produce duplicate points, remove those
31
+ return path3.fromVertices({}, newVertices)
18
32
  }
19
33
 
34
+ /*
35
+ */
20
36
  const snapGeom2 = (geometry) => {
21
37
  const epsilon = measureEpsilon(geometry)
22
38
  const outlines = geom2.toOutlines(geometry)
@@ -38,6 +54,8 @@ const snapGeom2 = (geometry) => {
38
54
  return geom2.create(newOutlines)
39
55
  }
40
56
 
57
+ /*
58
+ */
41
59
  const snapGeom3 = (geometry) => {
42
60
  const epsilon = measureEpsilon(geometry)
43
61
  const polygons = geom3.toPolygons(geometry)
@@ -54,9 +72,10 @@ const snapGeom3 = (geometry) => {
54
72
  */
55
73
  export const snap = (...geometries) => {
56
74
  const results = geometries.map((geometry) => {
57
- if (path2.isA(geometry)) return snapPath2(geometry)
58
- if (geom2.isA(geometry)) return snapGeom2(geometry)
59
75
  if (geom3.isA(geometry)) return snapGeom3(geometry)
76
+ if (geom2.isA(geometry)) return snapGeom2(geometry)
77
+ if (path2.isA(geometry)) return snapPath2(geometry)
78
+ if (path3.isA(geometry)) return snapPath3(geometry)
60
79
  if (Array.isArray(geometry)) return snap(...geometry)
61
80
  return geometry
62
81
  })
@@ -76,12 +76,13 @@ export const align = (options, ...geometries) => {
76
76
  options = validateOptions(options)
77
77
  let { modes, relativeTo, grouped } = options
78
78
 
79
+ geometries = coalesce(geometries)
80
+
79
81
  if (relativeTo.filter((val) => val == null).length) {
80
82
  const bounds = measureAggregateBoundingBox(geometries)
81
83
  relativeTo = populateRelativeToFromBounds(relativeTo, modes, bounds)
82
84
  }
83
85
  if (grouped) {
84
- geometries = coalesce(geometries)
85
86
  geometries = alignGeometries(geometries, modes, relativeTo)
86
87
  } else {
87
88
  geometries = geometries.map((geometry) => alignGeometries(geometry, modes, relativeTo))
@@ -79,7 +79,7 @@ test('align: multiple objects ungrouped, relativeTo is nulls, returns geometry a
79
79
  cube({ size: 2, center: [4, 4, 4] }),
80
80
  cube({ size: 4, center: [10, 10, 10] })
81
81
  ]
82
- const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [null, null, null], grouped: false }, ...original)
82
+ const aligned = align({ modes: ['center', 'min', 'max'], relativeTo: [null, null, null], grouped: false }, original)
83
83
  const bounds = measureAggregateBoundingBox(aligned)
84
84
  const expectedBounds = [[5.5, 3, 8], [9.5, 7, 12]]
85
85
  t.notThrows(() => geom3.validate(aligned[0]))
@@ -4,6 +4,8 @@ import * as plane from '../../maths/plane/index.js'
4
4
  import * as geom2 from '../../geometries/geom2/index.js'
5
5
  import * as geom3 from '../../geometries/geom3/index.js'
6
6
  import * as path2 from '../../geometries/path2/index.js'
7
+ import * as path3 from '../../geometries/path3/index.js'
8
+ import * as slice from '../../geometries/slice/index.js'
7
9
 
8
10
  /**
9
11
  * Mirror the given objects using the given options.
@@ -33,9 +35,11 @@ export const mirror = (options, ...objects) => {
33
35
  const matrix = mat4.mirrorByPlane(mat4.create(), planeOfMirror)
34
36
 
35
37
  const results = objects.map((object) => {
36
- if (path2.isA(object)) return path2.transform(matrix, object)
37
- if (geom2.isA(object)) return geom2.transform(matrix, object)
38
38
  if (geom3.isA(object)) return geom3.transform(matrix, object)
39
+ if (geom2.isA(object)) return geom2.transform(matrix, object)
40
+ if (path2.isA(object)) return path2.transform(matrix, object)
41
+ if (path3.isA(object)) return path3.transform(matrix, object)
42
+ if (slice.isA(object)) return slice.transform(matrix, object)
39
43
  // handle recursive arrays
40
44
  if (Array.isArray(object)) return mirror(options, ...object)
41
45
  return object
@@ -3,6 +3,8 @@ import * as mat4 from '../../maths/mat4/index.js'
3
3
  import * as geom2 from '../../geometries/geom2/index.js'
4
4
  import * as geom3 from '../../geometries/geom3/index.js'
5
5
  import * as path2 from '../../geometries/path2/index.js'
6
+ import * as path3 from '../../geometries/path3/index.js'
7
+ import * as slice from '../../geometries/slice/index.js'
6
8
 
7
9
  /**
8
10
  * Rotate the given objects using the given options.
@@ -28,9 +30,11 @@ export const rotate = (angles, ...objects) => {
28
30
  const matrix = mat4.fromTaitBryanRotation(mat4.create(), yaw, pitch, roll)
29
31
 
30
32
  const results = objects.map((object) => {
31
- if (path2.isA(object)) return path2.transform(matrix, object)
32
- if (geom2.isA(object)) return geom2.transform(matrix, object)
33
33
  if (geom3.isA(object)) return geom3.transform(matrix, object)
34
+ if (geom2.isA(object)) return geom2.transform(matrix, object)
35
+ if (path2.isA(object)) return path2.transform(matrix, object)
36
+ if (path3.isA(object)) return path3.transform(matrix, object)
37
+ if (slice.isA(object)) return slice.transform(matrix, object)
34
38
  // handle recursive arrays
35
39
  if (Array.isArray(object)) return rotate(angles, ...object)
36
40
  return object
@@ -3,6 +3,8 @@ import * as mat4 from '../../maths/mat4/index.js'
3
3
  import * as geom2 from '../../geometries/geom2/index.js'
4
4
  import * as geom3 from '../../geometries/geom3/index.js'
5
5
  import * as path2 from '../../geometries/path2/index.js'
6
+ import * as path3 from '../../geometries/path3/index.js'
7
+ import * as slice from '../../geometries/slice/index.js'
6
8
 
7
9
  /**
8
10
  * Scale the given objects using the given options.
@@ -26,9 +28,11 @@ export const scale = (factors, ...objects) => {
26
28
  const matrix = mat4.fromScaling(mat4.create(), factors)
27
29
 
28
30
  const results = objects.map((object) => {
29
- if (path2.isA(object)) return path2.transform(matrix, object)
30
- if (geom2.isA(object)) return geom2.transform(matrix, object)
31
31
  if (geom3.isA(object)) return geom3.transform(matrix, object)
32
+ if (geom2.isA(object)) return geom2.transform(matrix, object)
33
+ if (path2.isA(object)) return path2.transform(matrix, object)
34
+ if (path3.isA(object)) return path3.transform(matrix, object)
35
+ if (slice.isA(object)) return slice.transform(matrix, object)
32
36
  // handle recursive arrays
33
37
  if (Array.isArray(object)) return scale(factors, ...object)
34
38
  return object
@@ -1,6 +1,8 @@
1
1
  import * as geom2 from '../../geometries/geom2/index.js'
2
2
  import * as geom3 from '../../geometries/geom3/index.js'
3
3
  import * as path2 from '../../geometries/path2/index.js'
4
+ import * as path3 from '../../geometries/path3/index.js'
5
+ import * as slice from '../../geometries/slice/index.js'
4
6
 
5
7
  /**
6
8
  * Transform the given objects using the given matrix.
@@ -16,9 +18,11 @@ export const transform = (matrix, ...objects) => {
16
18
  // TODO how to check that the matrix is REAL?
17
19
 
18
20
  const results = objects.map((object) => {
19
- if (path2.isA(object)) return path2.transform(matrix, object)
20
- if (geom2.isA(object)) return geom2.transform(matrix, object)
21
21
  if (geom3.isA(object)) return geom3.transform(matrix, object)
22
+ if (geom2.isA(object)) return geom2.transform(matrix, object)
23
+ if (path2.isA(object)) return path2.transform(matrix, object)
24
+ if (path3.isA(object)) return path3.transform(matrix, object)
25
+ if (slice.isA(object)) return slice.transform(matrix, object)
22
26
  // handle recursive arrays
23
27
  if (Array.isArray(object)) return transform(matrix, ...object)
24
28
  return object
@@ -4,11 +4,11 @@ import { comparePoints, comparePolygonsAsPoints } from '../../../test/helpers/in
4
4
 
5
5
  import { mat4 } from '../../maths/index.js'
6
6
 
7
- import { geom2, geom3, path2 } from '../../geometries/index.js'
7
+ import { geom2, geom3, path2, path3 } from '../../geometries/index.js'
8
8
 
9
9
  import { transform } from './index.js'
10
10
 
11
- test('transform: transforming of a path2 produces expected changes to points', (t) => {
11
+ test('transform: (path2)', (t) => {
12
12
  const matrix = mat4.fromTranslation(mat4.create(), [2, 2, 0])
13
13
  let geometry = path2.fromPoints({}, [[0, 0], [1, 0]])
14
14
 
@@ -19,7 +19,18 @@ test('transform: transforming of a path2 produces expected changes to points', (
19
19
  t.true(comparePoints(obs, exp))
20
20
  })
21
21
 
22
- test('transform: transforming of a geom2 produces expected changes to sides', (t) => {
22
+ test('transform: (path3)', (t) => {
23
+ const matrix = mat4.fromTranslation(mat4.create(), [2, 2, 2])
24
+ let geometry = path3.fromVertices({ closed: true }, [[0, 0, 0], [1, 0, 1], [3, 2, 1]])
25
+
26
+ geometry = transform(matrix, geometry)
27
+ const obs = path3.toVertices(geometry)
28
+ const exp = [[2, 2, 2], [3, 2, 3], [5, 4, 3]]
29
+ t.notThrows(() => path3.validate(geometry))
30
+ t.true(comparePoints(obs, exp))
31
+ })
32
+
33
+ test('transform: (geom2)', (t) => {
23
34
  const matrix = mat4.fromScaling(mat4.create(), [5, 5, 5])
24
35
  let geometry = geom2.create([[[0, 0], [1, 0], [0, 1]]])
25
36
 
@@ -30,7 +41,7 @@ test('transform: transforming of a geom2 produces expected changes to sides', (t
30
41
  t.true(comparePoints(obs, exp))
31
42
  })
32
43
 
33
- test('transform: transforming of a geom3 produces expected changes to polygons', (t) => {
44
+ test('transform: (geom3)', (t) => {
34
45
  const matrix = mat4.fromTranslation(mat4.create(), [-3, -3, -3])
35
46
  const points = [
36
47
  [[-2, -7, -12], [-2, -7, 18], [-2, 13, 18], [-2, 13, -12]],
@@ -55,7 +66,7 @@ test('transform: transforming of a geom3 produces expected changes to polygons',
55
66
  t.true(comparePolygonsAsPoints(obs, exp))
56
67
  })
57
68
 
58
- test('transform: transforming of multiple objects produces expected changes', (t) => {
69
+ test('transform: (multiple objects)', (t) => {
59
70
  const junk = 'hello'
60
71
  const geometry1 = path2.fromPoints({}, [[-5, 5], [5, 5], [-5, -5], [10, -5]])
61
72
  const geometry2 = geom2.create([[[-5, -5], [0, 5], [10, -5]]])
@@ -3,6 +3,8 @@ import * as mat4 from '../../maths/mat4/index.js'
3
3
  import * as geom2 from '../../geometries/geom2/index.js'
4
4
  import * as geom3 from '../../geometries/geom3/index.js'
5
5
  import * as path2 from '../../geometries/path2/index.js'
6
+ import * as path3 from '../../geometries/path3/index.js'
7
+ import * as slice from '../../geometries/slice/index.js'
6
8
 
7
9
  /**
8
10
  * Translate the given objects using the given options.
@@ -24,9 +26,11 @@ export const translate = (offset, ...objects) => {
24
26
  const matrix = mat4.fromTranslation(mat4.create(), offset)
25
27
 
26
28
  const results = objects.map((object) => {
27
- if (path2.isA(object)) return path2.transform(matrix, object)
28
- if (geom2.isA(object)) return geom2.transform(matrix, object)
29
29
  if (geom3.isA(object)) return geom3.transform(matrix, object)
30
+ if (geom2.isA(object)) return geom2.transform(matrix, object)
31
+ if (path2.isA(object)) return path2.transform(matrix, object)
32
+ if (path3.isA(object)) return path3.transform(matrix, object)
33
+ if (slice.isA(object)) return slice.transform(matrix, object)
30
34
  // handle recursive arrays
31
35
  if (Array.isArray(object)) return translate(offset, ...object)
32
36
  return object
@@ -8,6 +8,7 @@ import { isGT, isGTE, isNumberArray } from './commonChecks.js'
8
8
 
9
9
  /**
10
10
  * Construct an arc in two dimensional space where all points are at the same distance from the center.
11
+ *
11
12
  * @param {object} [options] - options for construction
12
13
  * @param {Array} [options.center=[0,0]] - center of arc
13
14
  * @param {number} [options.radius=1] - radius of arc
@@ -17,19 +18,19 @@ import { isGT, isGTE, isNumberArray } from './commonChecks.js'
17
18
  * @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
19
  * @returns {Path2} new 2D path
19
20
  * @alias module:modeling/primitives.arc
21
+ *
20
22
  * @example
21
23
  * let myshape = arc({ center: [-1, -1], radius: 2, endAngle: (TAU / 4)})
22
24
  */
23
- export const arc = (options) => {
24
- const defaults = {
25
- center: [0, 0],
26
- radius: 1,
27
- startAngle: 0,
28
- endAngle: TAU,
29
- makeTangent: false,
30
- segments: 32
31
- }
32
- let { center, radius, startAngle, endAngle, makeTangent, segments } = Object.assign({}, defaults, options)
25
+ export const arc = (options = {}) => {
26
+ let {
27
+ center = [0, 0],
28
+ radius = 1,
29
+ startAngle = 0,
30
+ endAngle = TAU,
31
+ makeTangent = false,
32
+ segments = 32
33
+ } = options
33
34
 
34
35
  if (!isNumberArray(center, 2)) throw new Error('center must be an array of X and Y values')
35
36
  if (!isGT(radius, 0)) throw new Error('radius must be greater than zero')
@@ -6,6 +6,7 @@ import { isGTE } from './commonChecks.js'
6
6
 
7
7
  /**
8
8
  * Construct a circle in two dimensional space where all points are at the same distance from the center.
9
+ *
9
10
  * @see [ellipse]{@link module:modeling/primitives.ellipse} for more options
10
11
  * @param {object} [options] - options for construction
11
12
  * @param {Array} [options.center=[0,0]] - center of circle
@@ -15,18 +16,18 @@ import { isGTE } from './commonChecks.js'
15
16
  * @param {number} [options.segments=32] - number of segments to create per full rotation
16
17
  * @returns {Geom2} new 2D geometry
17
18
  * @alias module:modeling/primitives.circle
19
+ *
18
20
  * @example
19
21
  * let myshape = circle({radius: 10})
20
22
  */
21
- export const circle = (options) => {
22
- const defaults = {
23
- center: [0, 0],
24
- radius: 1,
25
- startAngle: 0,
26
- endAngle: TAU,
27
- segments: 32
28
- }
29
- let { center, radius, startAngle, endAngle, segments } = Object.assign({}, defaults, options)
23
+ export const circle = (options = {}) => {
24
+ let {
25
+ center = [0, 0],
26
+ radius = 1,
27
+ startAngle = 0,
28
+ endAngle = TAU,
29
+ segments = 32
30
+ } = options
30
31
 
31
32
  if (!isGTE(radius, 0)) throw new Error('radius must be positive')
32
33
 
@@ -12,12 +12,11 @@ import { isGTE } from './commonChecks.js'
12
12
  * @example
13
13
  * let myshape = cube({size: 10})
14
14
  */
15
- export const cube = (options) => {
16
- const defaults = {
17
- center: [0, 0, 0],
18
- size: 2
19
- }
20
- let { center, size } = Object.assign({}, defaults, options)
15
+ export const cube = (options = {}) => {
16
+ let {
17
+ center = [0, 0, 0],
18
+ size = 2
19
+ } = options
21
20
 
22
21
  if (!isGTE(size, 0)) throw new Error('size must be positive')
23
22
 
@@ -5,6 +5,7 @@ import { isNumberArray } from './commonChecks.js'
5
5
 
6
6
  /**
7
7
  * Construct an axis-aligned solid cuboid in three dimensional space.
8
+ *
8
9
  * @param {object} [options] - options for construction
9
10
  * @param {Array} [options.center=[0,0,0]] - center of cuboid
10
11
  * @param {Array} [options.size=[2,2,2]] - dimensions of cuboid; width, depth, height
@@ -14,12 +15,11 @@ import { isNumberArray } from './commonChecks.js'
14
15
  * @example
15
16
  * let myshape = cuboid({size: [5, 10, 5]})
16
17
  */
17
- export const cuboid = (options) => {
18
- const defaults = {
19
- center: [0, 0, 0],
20
- size: [2, 2, 2]
21
- }
22
- const { center, size } = Object.assign({}, defaults, options)
18
+ export const cuboid = (options = {}) => {
19
+ const {
20
+ center = [0, 0, 0],
21
+ size = [2, 2, 2]
22
+ } = options
23
23
 
24
24
  if (!isNumberArray(center, 3)) throw new Error('center must be an array of X, Y and Z values')
25
25
  if (!isNumberArray(size, 3)) throw new Error('size must be an array of width, depth and height values')
@@ -4,6 +4,7 @@ import * as geom3 from '../geometries/geom3/index.js'
4
4
 
5
5
  /**
6
6
  * Construct a Z axis-aligned cylinder in three dimensional space.
7
+ *
7
8
  * @see [cylinderElliptic]{@link module:modeling/primitives.cylinderElliptic} for more options
8
9
  * @param {object} [options] - options for construction
9
10
  * @param {Array} [options.center=[0,0,0]] - center of cylinder
@@ -16,14 +17,13 @@ import * as geom3 from '../geometries/geom3/index.js'
16
17
  * @example
17
18
  * let myshape = cylinder({height: 2, radius: 10})
18
19
  */
19
- export const cylinder = (options) => {
20
- const defaults = {
21
- center: [0, 0, 0],
22
- height: 2,
23
- radius: 1,
24
- segments: 32
25
- }
26
- const { center, height, radius, segments } = Object.assign({}, defaults, options)
20
+ export const cylinder = (options = {}) => {
21
+ const {
22
+ center = [0, 0, 0],
23
+ height = 2,
24
+ radius = 1,
25
+ segments = 32
26
+ } = options
27
27
 
28
28
  if (!isGTE(radius, 0)) throw new Error('radius must be positive')
29
29
 
@@ -11,6 +11,7 @@ import { isGT, isGTE, isNumberArray } from './commonChecks.js'
11
11
 
12
12
  /**
13
13
  * Construct a Z axis-aligned elliptic cylinder in three dimensional space.
14
+ *
14
15
  * @param {object} [options] - options for construction
15
16
  * @param {Array} [options.center=[0,0,0]] - center of cylinder
16
17
  * @param {number} [options.height=2] - height of cylinder
@@ -25,17 +26,16 @@ import { isGT, isGTE, isNumberArray } from './commonChecks.js'
25
26
  * @example
26
27
  * let myshape = cylinderElliptic({height: 2, startRadius: [10,5], endRadius: [8,3]})
27
28
  */
28
- export const cylinderElliptic = (options) => {
29
- const defaults = {
30
- center: [0, 0, 0],
31
- height: 2,
32
- startRadius: [1, 1],
33
- startAngle: 0,
34
- endRadius: [1, 1],
35
- endAngle: TAU,
36
- segments: 32
37
- }
38
- let { center, height, startRadius, startAngle, endRadius, endAngle, segments } = Object.assign({}, defaults, options)
29
+ export const cylinderElliptic = (options = {}) => {
30
+ let {
31
+ center = [0, 0, 0],
32
+ height = 2,
33
+ startRadius = [1, 1],
34
+ startAngle = 0,
35
+ endRadius = [1, 1],
36
+ endAngle = TAU,
37
+ segments = 32
38
+ } = options
39
39
 
40
40
  if (!isNumberArray(center, 3)) throw new Error('center must be an array of X, Y and Z values')
41
41
  if (!isGT(height, 0)) throw new Error('height must be greater then zero')