@jscad/modeling 3.0.0-alpha.0 → 3.0.1-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 (134) hide show
  1. package/CHANGELOG.md +35 -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/poly3/type.d.ts +1 -1
  18. package/src/geometries/slice/validate.js +1 -2
  19. package/src/maths/index.js +1 -0
  20. package/src/maths/mat4/isOnlyTransformScale.js +1 -1
  21. package/src/measurements/measureAggregateArea.js +0 -1
  22. package/src/measurements/measureAggregateBoundingBox.js +0 -1
  23. package/src/measurements/measureAggregateEpsilon.js +0 -1
  24. package/src/measurements/measureAggregateVolume.js +0 -1
  25. package/src/measurements/measureArea.js +0 -1
  26. package/src/measurements/measureBoundingBox.js +0 -1
  27. package/src/measurements/measureEpsilon.js +0 -1
  28. package/src/measurements/measureVolume.js +0 -1
  29. package/src/operations/booleans/index.d.ts +1 -0
  30. package/src/operations/booleans/intersect.js +5 -5
  31. package/src/operations/booleans/intersect.test.js +6 -7
  32. package/src/operations/booleans/intersectGeom2.js +2 -6
  33. package/src/operations/booleans/intersectGeom2.test.js +25 -1
  34. package/src/operations/booleans/intersectGeom3.js +2 -6
  35. package/src/operations/booleans/intersectGeom3.test.js +5 -1
  36. package/src/operations/booleans/mayOverlap.js +0 -1
  37. package/src/operations/booleans/scission.d.ts +5 -0
  38. package/src/operations/booleans/scission.js +3 -5
  39. package/src/operations/booleans/scission.test.js +6 -0
  40. package/src/operations/booleans/subtract.js +5 -5
  41. package/src/operations/booleans/subtract.test.js +6 -7
  42. package/src/operations/booleans/subtractGeom2.js +2 -6
  43. package/src/operations/booleans/subtractGeom2.test.js +25 -1
  44. package/src/operations/booleans/subtractGeom3.js +2 -6
  45. package/src/operations/booleans/subtractGeom3.test.js +5 -1
  46. package/src/operations/booleans/trees/splitPolygonByPlane.d.ts +33 -0
  47. package/src/operations/booleans/union.js +5 -5
  48. package/src/operations/booleans/union.test.js +6 -7
  49. package/src/operations/booleans/unionGeom2.js +2 -6
  50. package/src/operations/booleans/unionGeom2.test.js +25 -1
  51. package/src/operations/booleans/unionGeom3.js +2 -6
  52. package/src/operations/booleans/unionGeom3.test.js +6 -1
  53. package/src/operations/extrusions/extrudeFromSlices.test.js +8 -1
  54. package/src/operations/extrusions/extrudeHelical.js +2 -8
  55. package/src/operations/extrusions/extrudeLinear.js +1 -5
  56. package/src/operations/extrusions/extrudeLinear.test.js +7 -1
  57. package/src/operations/extrusions/extrudeRotate.js +3 -2
  58. package/src/operations/extrusions/extrudeRotate.test.js +13 -1
  59. package/src/operations/extrusions/project.js +1 -5
  60. package/src/operations/hulls/hull.js +6 -5
  61. package/src/operations/hulls/hull.test.js +56 -3
  62. package/src/operations/hulls/hullChain.js +11 -6
  63. package/src/operations/hulls/hullChain.test.js +12 -2
  64. package/src/operations/hulls/hullGeom2.js +5 -6
  65. package/src/operations/hulls/hullGeom3.js +9 -18
  66. package/src/operations/hulls/hullPath2.js +6 -7
  67. package/src/operations/hulls/hullPath2.test.js +1 -1
  68. package/src/operations/hulls/hullPoints2.d.ts +3 -0
  69. package/src/operations/hulls/hullPoints2.js +4 -2
  70. package/src/operations/hulls/hullPoints3.d.ts +4 -0
  71. package/src/operations/hulls/hullPoints3.js +21 -0
  72. package/src/operations/hulls/index.d.ts +2 -0
  73. package/src/operations/hulls/index.js +3 -1
  74. package/src/operations/modifiers/generalize.js +2 -6
  75. package/src/operations/modifiers/index.js +1 -1
  76. package/src/operations/modifiers/snap.js +2 -6
  77. package/src/operations/offsets/offset.js +1 -5
  78. package/src/operations/offsets/offsetFromPoints.test.js +0 -1
  79. package/src/operations/offsets/offsetGeom2.test.js +1 -0
  80. package/src/operations/offsets/offsetGeom3.js +0 -2
  81. package/src/operations/offsets/offsetGeom3.test.js +9 -1
  82. package/src/operations/offsets/offsetPath2.js +3 -3
  83. package/src/operations/transforms/align.js +8 -7
  84. package/src/operations/transforms/align.test.js +2 -2
  85. package/src/operations/transforms/center.js +6 -9
  86. package/src/operations/transforms/center.test.js +19 -1
  87. package/src/operations/transforms/mirror.js +5 -8
  88. package/src/operations/transforms/mirror.test.js +7 -7
  89. package/src/operations/transforms/rotate.js +5 -8
  90. package/src/operations/transforms/scale.js +5 -8
  91. package/src/operations/transforms/transform.js +2 -5
  92. package/src/operations/transforms/translate.js +5 -8
  93. package/src/primitives/arc.js +2 -0
  94. package/src/primitives/arc.test.js +11 -11
  95. package/src/primitives/circle.test.js +18 -8
  96. package/src/primitives/cube.test.js +10 -0
  97. package/src/primitives/cuboid.test.js +10 -0
  98. package/src/primitives/cylinder.test.js +12 -0
  99. package/src/primitives/cylinderElliptic.test.js +21 -1
  100. package/src/primitives/ellipse.test.js +18 -8
  101. package/src/primitives/ellipsoid.test.js +12 -0
  102. package/src/primitives/geodesicSphere.test.js +8 -0
  103. package/src/primitives/line.test.js +1 -1
  104. package/src/primitives/polygon.d.ts +1 -0
  105. package/src/primitives/polygon.js +13 -4
  106. package/src/primitives/polygon.test.js +15 -0
  107. package/src/primitives/polyhedron.js +1 -0
  108. package/src/primitives/polyhedron.test.js +8 -2
  109. package/src/primitives/rectangle.test.js +9 -3
  110. package/src/primitives/roundedCuboid.js +1 -1
  111. package/src/primitives/roundedCuboid.test.js +20 -4
  112. package/src/primitives/roundedCylinder.js +1 -1
  113. package/src/primitives/roundedCylinder.test.js +20 -0
  114. package/src/primitives/roundedRectangle.js +1 -1
  115. package/src/primitives/roundedRectangle.test.js +15 -6
  116. package/src/primitives/sphere.test.js +12 -0
  117. package/src/primitives/square.test.js +10 -4
  118. package/src/primitives/star.test.js +14 -6
  119. package/src/primitives/torus.js +1 -1
  120. package/src/primitives/torus.test.js +11 -1
  121. package/src/primitives/triangle.test.js +17 -9
  122. package/src/utils/coalesce.d.ts +3 -0
  123. package/src/utils/coalesce.js +20 -0
  124. package/src/utils/index.js +2 -2
  125. package/src/maths/mat4/leftMultiplyVec2.d.ts +0 -4
  126. package/src/maths/mat4/leftMultiplyVec2.js +0 -26
  127. package/src/maths/mat4/leftMultiplyVec3.d.ts +0 -4
  128. package/src/maths/mat4/leftMultiplyVec3.js +0 -27
  129. package/src/maths/mat4/mirror.d.ts +0 -4
  130. package/src/maths/mat4/mirror.js +0 -32
  131. package/src/maths/mat4/rightMultiplyVec2.d.ts +0 -4
  132. package/src/maths/mat4/rightMultiplyVec2.js +0 -27
  133. package/src/maths/mat4/rightMultiplyVec3.d.ts +0 -4
  134. package/src/maths/mat4/rightMultiplyVec3.js +0 -28
@@ -151,7 +151,7 @@ export const roundedCuboid = (options) => {
151
151
 
152
152
  if (roundRadius > (size[0] - EPS) ||
153
153
  roundRadius > (size[1] - EPS) ||
154
- roundRadius > (size[2] - EPS)) throw new Error('roundRadius must be smaller then the radius of all dimensions')
154
+ roundRadius > (size[2] - EPS)) throw new Error('roundRadius must be smaller than the radius of all dimensions')
155
155
 
156
156
  segments = Math.floor(segments / 4)
157
157
 
@@ -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 { roundedCuboid } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -11,13 +13,17 @@ test('roundedCuboid (defaults)', (t) => {
11
13
  const pts = geom3.toPoints(obs)
12
14
 
13
15
  t.notThrows(() => geom3.validate(obs))
14
- t.deepEqual(pts.length, 614)
16
+ t.is(measureArea(obs), 21.87859958298585)
17
+ t.is(measureVolume(obs), 7.800061070935406)
18
+ t.is(pts.length, 614)
15
19
  })
16
20
 
17
21
  test('roundedCuboid (zero size)', (t) => {
18
22
  const obs = roundedCuboid({ size: [1, 1, 0] })
19
23
  const pts = geom3.toPoints(obs)
20
24
  t.notThrows(() => geom3.validate(obs))
25
+ t.is(measureArea(obs), 0)
26
+ t.is(measureVolume(obs), 0)
21
27
  t.is(pts.length, 0)
22
28
  })
23
29
 
@@ -25,7 +31,9 @@ test('roundedCuboid (zero radius)', (t) => {
25
31
  const obs = roundedCuboid({ roundRadius: 0 })
26
32
  const pts = geom3.toPoints(obs)
27
33
  t.notThrows(() => geom3.validate(obs))
28
- t.deepEqual(pts.length, 6)
34
+ t.is(measureArea(obs), 24)
35
+ t.is(measureVolume(obs), 7.999999999999999)
36
+ t.is(pts.length, 6)
29
37
  })
30
38
 
31
39
  test('roundedCuboid (options)', (t) => {
@@ -35,6 +43,8 @@ test('roundedCuboid (options)', (t) => {
35
43
  let exp = []
36
44
 
37
45
  t.notThrows(() => geom3.validate(obs))
46
+ t.is(measureArea(obs), 21.65472758198208)
47
+ t.is(measureVolume(obs), 7.734600480283937)
38
48
  t.is(pts.length, 62)
39
49
 
40
50
  // test center
@@ -44,6 +54,8 @@ test('roundedCuboid (options)', (t) => {
44
54
  ]
45
55
 
46
56
  t.notThrows(() => geom3.validate(obs))
57
+ t.is(measureArea(obs), 21.65472758198207)
58
+ t.is(measureVolume(obs), 7.73460048028392)
47
59
  t.is(pts.length, 62)
48
60
 
49
61
  // test size
@@ -144,7 +156,9 @@ test('roundedCuboid (options)', (t) => {
144
156
  [-3.8, 4.8, -6], [3.8, 4.8, -6]]
145
157
  ]
146
158
  t.notThrows(() => geom3.validate(obs))
147
- t.deepEqual(pts.length, 62)
159
+ t.is(measureArea(obs), 580.6448151876211)
160
+ t.is(measureVolume(obs), 958.6098905200406)
161
+ t.is(pts.length, 62)
148
162
  t.true(comparePolygonsAsPoints(pts, exp))
149
163
 
150
164
  // test roundRadius
@@ -243,6 +257,8 @@ test('roundedCuboid (options)', (t) => {
243
257
  [[2, -3, -6], [-2, -3, -6], [-2, 3, -6], [2, 3, -6]]
244
258
  ]
245
259
  t.notThrows(() => geom3.validate(obs))
246
- t.deepEqual(pts.length, 62)
260
+ t.is(measureArea(obs), 470.09666312772333)
261
+ t.is(measureVolume(obs), 835.1892253143822)
262
+ t.is(pts.length, 62)
247
263
  t.true(comparePolygonsAsPoints(pts, exp))
248
264
  })
@@ -38,7 +38,7 @@ export const roundedCylinder = (options) => {
38
38
  if (!isGTE(height, 0)) throw new Error('height must be positive')
39
39
  if (!isGTE(radius, 0)) throw new Error('radius must be positive')
40
40
  if (!isGTE(roundRadius, 0)) throw new Error('roundRadius must be positive')
41
- if (roundRadius > radius) throw new Error('roundRadius must be smaller then the radius')
41
+ if (roundRadius > radius) throw new Error('roundRadius must be smaller than the radius')
42
42
  if (!isGTE(segments, 4)) throw new Error('segments must be four or more')
43
43
 
44
44
  // if size is zero return empty geometry
@@ -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 { roundedCylinder } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -11,6 +13,8 @@ test('roundedCylinder (defaults)', (t) => {
11
13
  const pts = geom3.toPoints(obs)
12
14
 
13
15
  t.notThrows(() => geom3.validate(obs))
16
+ t.is(measureArea(obs), 16.844951865908268)
17
+ t.is(measureVolume(obs), 5.81870059177007)
14
18
  t.is(pts.length, 544)
15
19
  })
16
20
 
@@ -18,6 +22,8 @@ test('roundedCylinder (zero height)', (t) => {
18
22
  const obs = roundedCylinder({ height: 0 })
19
23
  const pts = geom3.toPoints(obs)
20
24
  t.notThrows(() => geom3.validate(obs))
25
+ t.is(measureArea(obs), 0)
26
+ t.is(measureVolume(obs), 0)
21
27
  t.is(pts.length, 0)
22
28
  })
23
29
 
@@ -25,6 +31,8 @@ test('roundedCylinder (zero radius)', (t) => {
25
31
  const obs = roundedCylinder({ radius: 0, roundRadius: 0 })
26
32
  const pts = geom3.toPoints(obs)
27
33
  t.notThrows(() => geom3.validate(obs))
34
+ t.is(measureArea(obs), 0)
35
+ t.is(measureVolume(obs), 0)
28
36
  t.is(pts.length, 0)
29
37
  })
30
38
 
@@ -32,6 +40,8 @@ test('roundedCylinder (zero roundRadius)', (t) => {
32
40
  const obs = roundedCylinder({ roundRadius: 0 })
33
41
  const pts = geom3.toPoints(obs)
34
42
  t.notThrows(() => geom3.validate(obs))
43
+ t.is(measureArea(obs), 18.789084266699856)
44
+ t.is(measureVolume(obs), 6.2428903045161)
35
45
  t.is(pts.length, 96)
36
46
  })
37
47
 
@@ -43,6 +53,8 @@ test('roundedCylinder (options)', (t) => {
43
53
  ]
44
54
 
45
55
  t.notThrows(() => geom3.validate(obs))
56
+ t.is(measureArea(obs), 14.303000362787825)
57
+ t.is(measureVolume(obs), 4.121244903945666)
46
58
  t.is(pts.length, 15)
47
59
 
48
60
  // test center
@@ -72,6 +84,8 @@ test('roundedCylinder (options)', (t) => {
72
84
  ]
73
85
 
74
86
  t.notThrows(() => geom3.validate(obs))
87
+ t.is(measureArea(obs), 14.303000362787827)
88
+ t.is(measureVolume(obs), 4.121244903945658)
75
89
  t.is(pts.length, 15)
76
90
  t.true(comparePolygonsAsPoints(pts, exp))
77
91
 
@@ -102,6 +116,8 @@ test('roundedCylinder (options)', (t) => {
102
116
  ]
103
117
 
104
118
  t.notThrows(() => geom3.validate(obs))
119
+ t.is(measureArea(obs), 120.104345775433)
120
+ t.is(measureVolume(obs), 46.91878813722758)
105
121
  t.is(pts.length, 15)
106
122
  t.true(comparePolygonsAsPoints(pts, exp))
107
123
 
@@ -131,6 +147,8 @@ test('roundedCylinder (options)', (t) => {
131
147
  ]
132
148
 
133
149
  t.notThrows(() => geom3.validate(obs))
150
+ t.is(measureArea(obs), 569.7191848255909)
151
+ t.is(measureVolume(obs), 412.1244903945666)
134
152
  t.is(pts.length, 15)
135
153
  t.true(comparePolygonsAsPoints(pts, exp))
136
154
 
@@ -160,6 +178,8 @@ test('roundedCylinder (options)', (t) => {
160
178
  ]
161
179
 
162
180
  t.notThrows(() => geom3.validate(obs))
181
+ t.is(measureArea(obs), 602.8474323274462)
182
+ t.is(measureVolume(obs), 1030.3112259864165)
163
183
  t.is(pts.length, 15)
164
184
  t.true(comparePolygonsAsPoints(pts, exp))
165
185
  })
@@ -45,7 +45,7 @@ export const roundedRectangle = (options) => {
45
45
  size = size.map((v) => v / 2) // convert to radius
46
46
 
47
47
  if (roundRadius > (size[0] - EPS) ||
48
- roundRadius > (size[1] - EPS)) throw new Error('roundRadius must be smaller then the radius of all dimensions')
48
+ roundRadius > (size[1] - EPS)) throw new Error('roundRadius must be smaller than the radius of all dimensions')
49
49
 
50
50
  const cornerSegments = Math.floor(segments / 4)
51
51
 
@@ -2,6 +2,8 @@ import test from 'ava'
2
2
 
3
3
  import { geom2 } from '../geometries/index.js'
4
4
 
5
+ import { measureArea } from '../measurements/index.js'
6
+
5
7
  import { roundedRectangle } from './index.js'
6
8
 
7
9
  import { comparePoints } from '../../test/helpers/index.js'
@@ -11,13 +13,15 @@ test('roundedRectangle (defaults)', (t) => {
11
13
  const obs = geom2.toPoints(geometry)
12
14
 
13
15
  t.notThrows(() => geom2.validate(geometry))
14
- t.deepEqual(obs.length, 36)
16
+ t.is(measureArea(geometry), 3.964857806090323)
17
+ t.is(obs.length, 36)
15
18
  })
16
19
 
17
20
  test('roundedRectangle (zero size)', (t) => {
18
21
  const obs = roundedRectangle({ size: [1, 0] })
19
22
  const pts = geom2.toPoints(obs)
20
23
  t.notThrows(() => geom2.validate(obs))
24
+ t.is(measureArea(obs), 0)
21
25
  t.is(pts.length, 0)
22
26
  })
23
27
 
@@ -25,7 +29,8 @@ test('roundedRectangle (zero radius)', (t) => {
25
29
  const obs = roundedRectangle({ roundRadius: 0 })
26
30
  const pts = geom2.toPoints(obs)
27
31
  t.notThrows(() => geom2.validate(obs))
28
- t.deepEqual(pts.length, 4)
32
+ t.is(measureArea(obs), 4)
33
+ t.is(pts.length, 4)
29
34
  })
30
35
 
31
36
  test('roundedRectangle (options)', (t) => {
@@ -55,7 +60,8 @@ test('roundedRectangle (options)', (t) => {
55
60
  [5, 4.2]
56
61
  ]
57
62
  t.notThrows(() => geom2.validate(geometry))
58
- t.deepEqual(obs.length, 20)
63
+ t.is(measureArea(geometry), 3.962458698356829)
64
+ t.is(obs.length, 20)
59
65
  t.true(comparePoints(obs, exp))
60
66
 
61
67
  // test size
@@ -84,7 +90,8 @@ test('roundedRectangle (options)', (t) => {
84
90
  [5, -2.8]
85
91
  ]
86
92
  t.notThrows(() => geom2.validate(geometry))
87
- t.deepEqual(obs.length, 20)
93
+ t.is(measureArea(geometry), 59.96245869835682)
94
+ t.is(obs.length, 20)
88
95
  t.true(comparePoints(obs, exp))
89
96
 
90
97
  // test roundRadius
@@ -113,12 +120,14 @@ test('roundedRectangle (options)', (t) => {
113
120
  [5, -1.0000000000000004]
114
121
  ]
115
122
  t.notThrows(() => geom2.validate(geometry))
116
- t.deepEqual(obs.length, 20)
123
+ t.is(measureArea(geometry), 56.24586983568288)
124
+ t.is(obs.length, 20)
117
125
  t.true(comparePoints(obs, exp))
118
126
 
119
127
  // test segments
120
128
  geometry = roundedRectangle({ size: [10, 6], roundRadius: 2, segments: 64 })
121
129
  obs = geom2.toPoints(geometry)
122
130
  t.notThrows(() => geom2.validate(geometry))
123
- t.deepEqual(obs.length, 68)
131
+ t.is(measureArea(geometry), 56.546193962183764)
132
+ t.is(obs.length, 68)
124
133
  })
@@ -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 { sphere } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -11,6 +13,8 @@ test('sphere (defaults)', (t) => {
11
13
  const pts = geom3.toPoints(obs)
12
14
 
13
15
  t.notThrows(() => geom3.validate(obs))
16
+ t.is(measureArea(obs), 12.465694088650583)
17
+ t.is(measureVolume(obs), 4.121941740785839)
14
18
  t.is(pts.length, 512)
15
19
  })
16
20
 
@@ -20,6 +24,8 @@ test('sphere (options)', (t) => {
20
24
  let pts = geom3.toPoints(obs)
21
25
  let exp = []
22
26
  t.notThrows(() => geom3.validate(obs))
27
+ t.is(measureArea(obs), 296.5322084069296)
28
+ t.is(measureVolume(obs), 466.5063509461097)
23
29
  t.is(pts.length, 72)
24
30
  // t.true(comparePolygonsAsPoints(pts, exp))
25
31
 
@@ -103,6 +109,8 @@ test('sphere (options)', (t) => {
103
109
  [0.4999999999999999, 0.5000000000000001, 0.7071067811865475]]
104
110
  ]
105
111
  t.notThrows(() => geom3.validate(obs))
112
+ t.is(measureArea(obs), 11.013439076647456)
113
+ t.is(measureVolume(obs), 3.2189514164974597)
106
114
  t.is(pts.length, 32)
107
115
  t.true(comparePolygonsAsPoints(pts, exp))
108
116
 
@@ -160,6 +168,8 @@ test('sphere (options)', (t) => {
160
168
  [[-3, 5, 8], [-2.2928932188134525, 5, 7.707106781186548], [-2.5, 5.5, 7.707106781186548]]
161
169
  ]
162
170
  t.notThrows(() => geom3.validate(obs))
171
+ t.is(measureArea(obs), 11.013439076647467)
172
+ t.is(measureVolume(obs), 3.218951416497485)
163
173
  t.is(pts.length, 32)
164
174
  t.true(comparePolygonsAsPoints(pts, exp))
165
175
  })
@@ -168,5 +178,7 @@ test('sphere (zero radius)', (t) => {
168
178
  const obs = sphere({ radius: 0 })
169
179
  const pts = geom3.toPoints(obs)
170
180
  t.notThrows(() => geom3.validate(obs))
181
+ t.is(measureArea(obs), 0)
182
+ t.is(measureVolume(obs), 0)
171
183
  t.is(pts.length, 0)
172
184
  })
@@ -2,15 +2,18 @@ import test from 'ava'
2
2
 
3
3
  import { geom2 } from '../geometries/index.js'
4
4
 
5
+ import { measureArea } from '../measurements/index.js'
6
+
5
7
  import { square } from './index.js'
6
8
 
7
9
  import { comparePoints } from '../../test/helpers/index.js'
8
10
 
9
11
  test('square (defaults)', (t) => {
10
12
  const geometry = square()
11
- const obs = geom2.toPoints(geometry)
13
+ const pts = geom2.toPoints(geometry)
12
14
  t.notThrows(() => geom2.validate(geometry))
13
- t.deepEqual(obs.length, 4)
15
+ t.is(measureArea(geometry), 4)
16
+ t.is(pts.length, 4)
14
17
  })
15
18
 
16
19
  test('square (options)', (t) => {
@@ -25,6 +28,7 @@ test('square (options)', (t) => {
25
28
  ]
26
29
 
27
30
  t.notThrows(() => geom2.validate(obs))
31
+ t.is(measureArea(obs), 49)
28
32
  t.is(pts.length, 4)
29
33
  t.true(comparePoints(pts, exp))
30
34
 
@@ -39,13 +43,15 @@ test('square (options)', (t) => {
39
43
  ]
40
44
 
41
45
  t.notThrows(() => geom2.validate(obs))
46
+ t.is(measureArea(obs), 49)
42
47
  t.is(pts.length, 4)
43
48
  t.true(comparePoints(pts, exp))
44
49
  })
45
50
 
46
51
  test('square (zero size)', (t) => {
47
52
  const geometry = square({ size: 0 })
48
- const obs = geom2.toPoints(geometry)
53
+ const pts = geom2.toPoints(geometry)
49
54
  t.notThrows(() => geom2.validate(geometry))
50
- t.is(obs.length, 0)
55
+ t.is(measureArea(geometry), 0)
56
+ t.is(pts.length, 0)
51
57
  })
@@ -2,6 +2,8 @@ import test from 'ava'
2
2
 
3
3
  import { geom2 } from '../geometries/index.js'
4
4
 
5
+ import { measureArea } from '../measurements/index.js'
6
+
5
7
  import { star } from './index.js'
6
8
 
7
9
  import { comparePoints } from '../../test/helpers/index.js'
@@ -23,7 +25,8 @@ test('star (defaults)', (t) => {
23
25
  ]
24
26
 
25
27
  t.notThrows(() => geom2.validate(geometry))
26
- t.deepEqual(pts.length, 10)
28
+ t.is(measureArea(geometry), 1.1225699414489634)
29
+ t.is(pts.length, 10)
27
30
  t.true(comparePoints(pts, exp))
28
31
  })
29
32
 
@@ -45,7 +48,8 @@ test('star (options)', (t) => {
45
48
  ]
46
49
 
47
50
  t.notThrows(() => geom2.validate(geometry))
48
- t.deepEqual(pts.length, 10)
51
+ t.is(measureArea(geometry), 28.06424853622408)
52
+ t.is(pts.length, 10)
49
53
  t.true(comparePoints(pts, exp))
50
54
 
51
55
  // test vertices
@@ -71,7 +75,8 @@ test('star (options)', (t) => {
71
75
  ]
72
76
 
73
77
  t.notThrows(() => geom2.validate(geometry))
74
- t.deepEqual(pts.length, 16)
78
+ t.is(measureArea(geometry), 58.5786437626905)
79
+ t.is(pts.length, 16)
75
80
  t.true(comparePoints(pts, exp))
76
81
 
77
82
  // test density
@@ -97,7 +102,8 @@ test('star (options)', (t) => {
97
102
  ]
98
103
 
99
104
  t.notThrows(() => geom2.validate(geometry))
100
- t.deepEqual(pts.length, 16)
105
+ t.is(measureArea(geometry), 41.42135623730952)
106
+ t.is(pts.length, 16)
101
107
  t.true(comparePoints(pts, exp))
102
108
 
103
109
  // test innerRadius
@@ -123,7 +129,8 @@ test('star (options)', (t) => {
123
129
  ]
124
130
 
125
131
  t.notThrows(() => geom2.validate(geometry))
126
- t.deepEqual(pts.length, 16)
132
+ t.is(measureArea(geometry), 15.30733729460359)
133
+ t.is(pts.length, 16)
127
134
  t.true(comparePoints(pts, exp))
128
135
 
129
136
  // test start angle
@@ -143,6 +150,7 @@ test('star (options)', (t) => {
143
150
  ]
144
151
 
145
152
  t.notThrows(() => geom2.validate(geometry))
146
- t.deepEqual(pts.length, 10)
153
+ t.is(measureArea(geometry), 28.06424853622409)
154
+ t.is(pts.length, 10)
147
155
  t.true(comparePoints(pts, exp))
148
156
  })
@@ -43,7 +43,7 @@ export const torus = (options) => {
43
43
  if (!isGTE(startAngle, 0)) throw new Error('startAngle must be positive')
44
44
  if (!isGT(outerRotation, 0)) throw new Error('outerRotation must be greater than zero')
45
45
 
46
- if (innerRadius >= outerRadius) throw new Error('inner circle is two large to rotate about the outer circle')
46
+ if (innerRadius >= outerRadius) throw new Error('inner circle is too large to rotate about the outer circle')
47
47
 
48
48
  let innerCircle = circle({ radius: innerRadius, segments: innerSegments })
49
49
 
@@ -4,7 +4,7 @@ import { TAU } from '../maths/constants.js'
4
4
 
5
5
  import { geom3 } from '../geometries/index.js'
6
6
 
7
- import { measureBoundingBox } from '../measurements/index.js'
7
+ import { measureArea, measureBoundingBox, measureVolume } from '../measurements/index.js'
8
8
 
9
9
  import { torus } from './index.js'
10
10
 
@@ -15,6 +15,8 @@ test('torus (defaults)', (t) => {
15
15
  const pts = geom3.toPoints(obs)
16
16
 
17
17
  t.notThrows(() => geom3.validate(obs))
18
+ t.is(measureArea(obs), 157.0282327749074)
19
+ t.is(measureVolume(obs), 77.94735870844194)
18
20
  t.is(pts.length, 2048) // 32 * 32 * 2 (polys/segment) = 2048
19
21
 
20
22
  const bounds = measureBoundingBox(obs)
@@ -26,6 +28,8 @@ test('torus (simple options)', (t) => {
26
28
  const obs = torus({ innerRadius: 0.5, innerSegments: 4, outerRadius: 5, outerSegments: 8 })
27
29
  const pts = geom3.toPoints(obs)
28
30
  t.notThrows(() => geom3.validate(obs))
31
+ t.is(measureArea(obs), 83.36086132479792)
32
+ t.is(measureVolume(obs), 14.14213562373095)
29
33
  t.is(pts.length, 64) // 4 * 8 * 2 (polys/segment) = 64
30
34
 
31
35
  const bounds = measureBoundingBox(obs)
@@ -37,6 +41,8 @@ test('torus (complex options)', (t) => {
37
41
  const obs = torus({ innerRadius: 1, outerRadius: 5, innerSegments: 32, outerSegments: 72, startAngle: TAU / 4, outerRotation: TAU / 4 })
38
42
  const pts = geom3.toPoints(obs)
39
43
  t.notThrows(() => geom3.validate(obs))
44
+ t.is(measureArea(obs), 55.472610544494)
45
+ t.is(measureVolume(obs), 24.484668362201525)
40
46
  t.is(pts.length, 1212)
41
47
 
42
48
  const bounds = measureBoundingBox(obs)
@@ -48,6 +54,8 @@ test('torus (startAngle)', (t) => {
48
54
  const obs = torus({ startAngle: 1, endAngle: 1 + TAU })
49
55
  const pts = geom3.toPoints(obs)
50
56
  t.notThrows(() => geom3.validate(obs))
57
+ t.is(measureArea(obs), 157.0282327749074)
58
+ t.is(measureVolume(obs), 77.94735870844195)
51
59
  t.is(pts.length, 2048)
52
60
  })
53
61
 
@@ -57,5 +65,7 @@ test('torus (square by square)', (t) => {
57
65
  const bounds = measureBoundingBox(obs)
58
66
  const expectedBounds = [[-5, -5, -1], [5, 5, 1]]
59
67
  t.notThrows(() => geom3.validate(obs))
68
+ t.is(measureArea(obs), 110.85125168440814)
69
+ t.is(measureVolume(obs), 32)
60
70
  t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected: ' + JSON.stringify(bounds))
61
71
  })
@@ -1,11 +1,13 @@
1
1
  import test from 'ava'
2
2
 
3
- import { TAU } from '../maths/constants.js'
4
-
5
3
  import { degToRad } from '../utils/index.js'
6
4
 
7
5
  import { geom2 } from '../geometries/index.js'
8
6
 
7
+ import { TAU } from '../maths/constants.js'
8
+
9
+ import { measureArea } from '../measurements/index.js'
10
+
9
11
  import { triangle } from './index.js'
10
12
 
11
13
  import { comparePoints } from '../../test/helpers/index.js'
@@ -20,7 +22,8 @@ test('triangle (defaults)', (t) => {
20
22
  ]
21
23
 
22
24
  t.notThrows(() => geom2.validate(geometry))
23
- t.deepEqual(obs.length, 3)
25
+ t.is(measureArea(geometry), 0.43301270189221935)
26
+ t.is(obs.length, 3)
24
27
  t.true(comparePoints(obs, exp))
25
28
  })
26
29
 
@@ -35,7 +38,8 @@ test('triangle (options)', (t) => {
35
38
  ]
36
39
 
37
40
  t.notThrows(() => geom2.validate(geometry))
38
- t.deepEqual(obs.length, 3)
41
+ t.is(measureArea(geometry), 20.33316256758894)
42
+ t.is(obs.length, 3)
39
43
  t.true(comparePoints(obs, exp))
40
44
 
41
45
  // test AAA
@@ -48,7 +52,7 @@ test('triangle (options)', (t) => {
48
52
  ]
49
53
 
50
54
  t.notThrows(() => geom2.validate(geometry))
51
- t.deepEqual(obs.length, 3)
55
+ t.is(obs.length, 3)
52
56
  t.true(comparePoints(obs, exp))
53
57
 
54
58
  // test AAS
@@ -61,7 +65,8 @@ test('triangle (options)', (t) => {
61
65
  ]
62
66
 
63
67
  t.notThrows(() => geom2.validate(geometry))
64
- t.deepEqual(obs.length, 3)
68
+ t.is(measureArea(geometry), 15.796947276180953)
69
+ t.is(obs.length, 3)
65
70
  t.true(comparePoints(obs, exp))
66
71
 
67
72
  // test ASA
@@ -74,7 +79,8 @@ test('triangle (options)', (t) => {
74
79
  ]
75
80
 
76
81
  t.notThrows(() => geom2.validate(geometry))
77
- t.deepEqual(obs.length, 3)
82
+ t.is(measureArea(geometry), 23.384870895211314)
83
+ t.is(obs.length, 3)
78
84
  t.true(comparePoints(obs, exp))
79
85
 
80
86
  // test SAS
@@ -87,7 +93,8 @@ test('triangle (options)', (t) => {
87
93
  ]
88
94
 
89
95
  t.notThrows(() => geom2.validate(geometry))
90
- t.deepEqual(obs.length, 3)
96
+ t.is(measureArea(geometry), 13.207417653898512)
97
+ t.is(obs.length, 3)
91
98
  t.true(comparePoints(obs, exp))
92
99
 
93
100
  // test SSA
@@ -100,6 +107,7 @@ test('triangle (options)', (t) => {
100
107
  ]
101
108
 
102
109
  t.notThrows(() => geom2.validate(geometry))
103
- t.deepEqual(obs.length, 3)
110
+ t.is(measureArea(geometry), 51.962298292283386)
111
+ t.is(obs.length, 3)
104
112
  t.true(comparePoints(obs, exp))
105
113
  })
@@ -0,0 +1,3 @@
1
+ import type { RecursiveArray } from './recursiveArray.d.ts'
2
+
3
+ export function coalesce<T>(arr: RecursiveArray<T>): Array<T>
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Flattens and filters out nullish values from the given list of arguments.
3
+ * The arguments can be composed of multiple depths of objects and arrays.
4
+ * The output is a single flat array with no missing values.
5
+ * @param {Array} arr - list of arguments
6
+ * @returns {Array} a flat list of arguments
7
+ * @alias module:modeling/utils.coalesce
8
+ */
9
+ export const coalesce = (arr) => flattenHelper(arr, [])
10
+
11
+ // Helper to recursively append to a given list.
12
+ // This is MUCH faster than other flatten methods.
13
+ const flattenHelper = (arr, out) => {
14
+ if (Array.isArray(arr)) {
15
+ arr.forEach((child) => flattenHelper(child, out))
16
+ } else if (arr != null && arr !== undefined) {
17
+ out.push(arr)
18
+ }
19
+ return out
20
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Utility functions of various sorts.
2
+ * Utility functions of various sorts, including conversions from different angular measures.
3
3
  * @module modeling/utils
4
4
  * @example
5
- * import { degToRad } from '@jscad/modeling'
5
+ * import { degToRad, flatten, radiusToSegments, radToDeg } from '@jscad/modeling'
6
6
  */
7
7
  export { degToRad } from './degToRad.js'
8
8
  export { flatten } from './flatten.js'
@@ -1,4 +0,0 @@
1
- import type { Mat4 } from './type.d.ts'
2
- import type { Vec2 } from '../vec2/type.d.ts'
3
-
4
- export function leftMultiplyVec2(vector: Vec2, matrix: Mat4): Vec2
@@ -1,26 +0,0 @@
1
- import { fromValues } from '../vec2/index.js'
2
-
3
- /*
4
- * Multiply the input matrix by a Vector2 (interpreted as 2 column, 1 row)
5
- * (result = v*M)
6
- * Fourth element is set to 1
7
- * @param {Vec2} vector the input vector
8
- * @param {Mat4} matrix the input matrix
9
- * @returns {Vec2} output
10
- */
11
- export const leftMultiplyVec2 = (vector, matrix) => {
12
- const [v0, v1] = vector
13
- const v2 = 0
14
- const v3 = 1
15
- let x = v0 * matrix[0] + v1 * matrix[4] + v2 * matrix[8] + v3 * matrix[12]
16
- let y = v0 * matrix[1] + v1 * matrix[5] + v2 * matrix[9] + v3 * matrix[13]
17
- const w = v0 * matrix[3] + v1 * matrix[7] + v2 * matrix[11] + v3 * matrix[15]
18
-
19
- // scale such that fourth element becomes 1:
20
- if (w !== 1) {
21
- const invw = 1.0 / w
22
- x *= invw
23
- y *= invw
24
- }
25
- return fromValues(x, y)
26
- }
@@ -1,4 +0,0 @@
1
- import type { Mat4 } from './type.d.ts'
2
- import type { Vec3 } from '../vec3/type.d.ts'
3
-
4
- export function leftMultiplyVec3(vector: Vec3, matrix: Mat4): Vec3
@@ -1,27 +0,0 @@
1
- import { fromValues } from '../vec3/index.js'
2
-
3
- /*
4
- * Multiply the input matrix by a Vector3 (interpreted as 3 column, 1 row)
5
- * (result = v*M)
6
- * Fourth element is set to 1
7
- * @param {Vec3} vector the input vector
8
- * @param {Mat4} matrix the input matrix
9
- * @returns {Vec3} output
10
- */
11
- export const leftMultiplyVec3 = (vector, matrix) => {
12
- const [v0, v1, v2] = vector
13
- const v3 = 1
14
- let x = v0 * matrix[0] + v1 * matrix[4] + v2 * matrix[8] + v3 * matrix[12]
15
- let y = v0 * matrix[1] + v1 * matrix[5] + v2 * matrix[9] + v3 * matrix[13]
16
- let z = v0 * matrix[2] + v1 * matrix[6] + v2 * matrix[10] + v3 * matrix[14]
17
- const w = v0 * matrix[3] + v1 * matrix[7] + v2 * matrix[11] + v3 * matrix[15]
18
-
19
- // scale such that fourth element becomes 1:
20
- if (w !== 1) {
21
- const invw = 1.0 / w
22
- x *= invw
23
- y *= invw
24
- z *= invw
25
- }
26
- return fromValues(x, y, z)
27
- }