@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
@@ -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 { cylinder } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -11,6 +13,8 @@ test('cylinder (defaults)', (t) => {
11
13
  const pts = geom3.toPoints(obs)
12
14
 
13
15
  t.notThrows(() => geom3.validate(obs))
16
+ t.is(measureArea(obs), 18.789084266699856)
17
+ t.is(measureVolume(obs), 6.2428903045161)
14
18
  t.is(pts.length, 96)
15
19
  })
16
20
 
@@ -18,6 +22,8 @@ test('cylinder (zero height)', (t) => {
18
22
  const obs = cylinder({ 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('cylinder (zero radius)', (t) => {
25
31
  const obs = cylinder({ radius: 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
 
@@ -55,6 +63,8 @@ test('cylinder (options)', (t) => {
55
63
  ]
56
64
 
57
65
  t.notThrows(() => geom3.validate(obs))
66
+ t.is(measureArea(obs), 311.1986222206015)
67
+ t.is(measureVolume(obs), 380.4226065180614)
58
68
  t.is(pts.length, 15)
59
69
  t.true(comparePolygonsAsPoints(pts, exp))
60
70
 
@@ -85,6 +95,8 @@ test('cylinder (options)', (t) => {
85
95
  ]
86
96
 
87
97
  t.notThrows(() => geom3.validate(obs))
98
+ t.is(measureArea(obs), 16.51098762732523)
99
+ t.is(measureVolume(obs), 4.755282581475773)
88
100
  t.is(pts.length, 15)
89
101
  t.true(comparePolygonsAsPoints(pts, exp))
90
102
  })
@@ -1,8 +1,10 @@
1
1
  import test from 'ava'
2
2
 
3
+ import { geom3 } from '../geometries/index.js'
4
+
3
5
  import { TAU } from '../maths/constants.js'
4
6
 
5
- import { geom3 } from '../geometries/index.js'
7
+ import { measureArea, measureVolume } from '../measurements/index.js'
6
8
 
7
9
  import { cylinderElliptic } from './index.js'
8
10
 
@@ -13,6 +15,8 @@ test('cylinderElliptic (defaults)', (t) => {
13
15
  const pts = geom3.toPoints(obs)
14
16
 
15
17
  t.notThrows(() => geom3.validate(obs))
18
+ t.is(measureArea(obs), 18.789084266699856)
19
+ t.is(measureVolume(obs), 6.2428903045161)
16
20
  t.is(pts.length, 96)
17
21
  })
18
22
 
@@ -72,6 +76,8 @@ test('cylinderElliptic (options)', (t) => {
72
76
  ]
73
77
 
74
78
  t.notThrows(() => geom3.validate(obs))
79
+ t.is(measureArea(obs), 68.11657082460499)
80
+ t.is(measureVolume(obs), 30.00000000000001)
75
81
  t.is(pts.length, 36)
76
82
  t.true(comparePolygonsAsPoints(pts, exp))
77
83
 
@@ -130,6 +136,8 @@ test('cylinderElliptic (options)', (t) => {
130
136
  ]
131
137
 
132
138
  t.notThrows(() => geom3.validate(obs))
139
+ t.is(measureArea(obs), 32.34210030145122)
140
+ t.is(measureVolume(obs), 12.999999999999991)
133
141
  t.is(pts.length, 48)
134
142
  t.true(comparePolygonsAsPoints(pts, exp))
135
143
 
@@ -138,6 +146,8 @@ test('cylinderElliptic (options)', (t) => {
138
146
  pts = geom3.toPoints(obs)
139
147
 
140
148
  t.notThrows(() => geom3.validate(obs))
149
+ t.is(measureArea(obs), 22.17105015072561)
150
+ t.is(measureVolume(obs), 6.5)
141
151
  t.is(pts.length, 28)
142
152
 
143
153
  // test startAngle and endAngle
@@ -145,6 +155,8 @@ test('cylinderElliptic (options)', (t) => {
145
155
  pts = geom3.toPoints(obs)
146
156
 
147
157
  t.notThrows(() => geom3.validate(obs))
158
+ t.is(measureArea(obs), 18.78908426669986)
159
+ t.is(measureVolume(obs), 6.2428903045160995)
148
160
  t.is(pts.length, 96)
149
161
 
150
162
  // test segments
@@ -152,6 +164,8 @@ test('cylinderElliptic (options)', (t) => {
152
164
  pts = geom3.toPoints(obs)
153
165
 
154
166
  t.notThrows(() => geom3.validate(obs))
167
+ t.is(measureArea(obs), 17.902724085175244)
168
+ t.is(measureVolume(obs), 5.6568542494923815)
155
169
  t.is(pts.length, 24)
156
170
 
157
171
  // test center
@@ -193,6 +207,8 @@ test('cylinderElliptic (options)', (t) => {
193
207
  ]
194
208
 
195
209
  t.notThrows(() => geom3.validate(obs))
210
+ t.is(measureArea(obs), 24.025659003016692)
211
+ t.is(measureVolume(obs), 8.485281374238578)
196
212
  t.is(pts.length, 24)
197
213
  t.true(comparePolygonsAsPoints(pts, exp))
198
214
  })
@@ -202,6 +218,8 @@ test('cylinderElliptic (cone)', (t) => {
202
218
  const pts = geom3.toPoints(obs)
203
219
 
204
220
  t.notThrows(() => geom3.validate(obs))
221
+ t.is(measureArea(obs), 10.128239395900382)
222
+ t.is(measureVolume(obs), 2.080963434838702)
205
223
  t.is(pts.length, 64)
206
224
  })
207
225
 
@@ -210,5 +228,7 @@ test('cylinderElliptic (squished)', (t) => {
210
228
  const pts = geom3.toPoints(obs)
211
229
 
212
230
  t.notThrows(() => geom3.validate(obs))
231
+ t.is(measureArea(obs), 8.47213595499958)
232
+ t.is(measureVolume(obs), 0.6666666666666666)
213
233
  t.is(pts.length, 8)
214
234
  })
@@ -1,8 +1,10 @@
1
1
  import test from 'ava'
2
2
 
3
+ import { geom2 } from '../geometries/index.js'
4
+
3
5
  import { TAU } from '../maths/constants.js'
4
6
 
5
- import { geom2 } from '../geometries/index.js'
7
+ import { measureArea } from '../measurements/index.js'
6
8
 
7
9
  import { comparePoints } from '../../test/helpers/index.js'
8
10
 
@@ -13,7 +15,8 @@ test('ellipse (defaults)', (t) => {
13
15
  const obs = geom2.toPoints(geometry)
14
16
 
15
17
  t.notThrows(() => geom2.validate(geometry))
16
- t.deepEqual(obs.length, 32)
18
+ t.is(measureArea(geometry), 3.1214451522580537)
19
+ t.is(obs.length, 32)
17
20
  })
18
21
 
19
22
  test('ellipse (options)', (t) => {
@@ -56,7 +59,8 @@ test('ellipse (options)', (t) => {
56
59
  ]
57
60
 
58
61
  t.notThrows(() => geom2.validate(geometry))
59
- t.deepEqual(obs.length, 32)
62
+ t.is(measureArea(geometry), 3.121445152258051)
63
+ t.is(obs.length, 32)
60
64
  t.true(comparePoints(obs, exp))
61
65
 
62
66
  // test radius
@@ -82,7 +86,8 @@ test('ellipse (options)', (t) => {
82
86
  ]
83
87
 
84
88
  t.notThrows(() => geom2.validate(geometry))
85
- t.deepEqual(obs.length, 16)
89
+ t.is(measureArea(geometry), 45.92201188381077)
90
+ t.is(obs.length, 16)
86
91
  t.true(comparePoints(obs, exp))
87
92
 
88
93
  // test startAngle
@@ -106,7 +111,8 @@ test('ellipse (options)', (t) => {
106
111
  ]
107
112
 
108
113
  t.notThrows(() => geom2.validate(geometry))
109
- t.deepEqual(obs.length, 14)
114
+ t.is(measureArea(geometry), 34.44150891285808)
115
+ t.is(obs.length, 14)
110
116
  t.true(comparePoints(obs, exp))
111
117
 
112
118
  // test endAngle
@@ -122,7 +128,8 @@ test('ellipse (options)', (t) => {
122
128
  ]
123
129
 
124
130
  t.notThrows(() => geom2.validate(geometry))
125
- t.deepEqual(obs.length, 6)
131
+ t.is(measureArea(geometry), 11.480502970952696)
132
+ t.is(obs.length, 6)
126
133
  t.true(comparePoints(obs, exp))
127
134
 
128
135
  // test full rotation with non-zero startAngle
@@ -130,18 +137,21 @@ test('ellipse (options)', (t) => {
130
137
  obs = geom2.toPoints(geometry)
131
138
 
132
139
  t.notThrows(() => geom2.validate(geometry))
133
- t.deepEqual(obs.length, 32)
140
+ t.is(measureArea(geometry), 3.1214451522580537)
141
+ t.is(obs.length, 32)
134
142
 
135
143
  // test segments
136
144
  geometry = ellipse({ segments: 72 })
137
145
  obs = geom2.toPoints(geometry)
138
146
  t.notThrows(() => geom2.validate(geometry))
139
- t.deepEqual(obs.length, 72)
147
+ t.is(measureArea(geometry), 3.1376067389156956)
148
+ t.is(obs.length, 72)
140
149
  })
141
150
 
142
151
  test('ellipse (zero radius)', (t) => {
143
152
  const geometry = ellipse({ radius: [1, 0] })
144
153
  const obs = geom2.toPoints(geometry)
145
154
  t.notThrows(() => geom2.validate(geometry))
155
+ t.is(measureArea(geometry), 0)
146
156
  t.is(obs.length, 0)
147
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 { ellipsoid } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -11,6 +13,8 @@ test('ellipsoid (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
 
@@ -141,6 +145,8 @@ test('ellipsoid (options)', (t) => {
141
145
  [[0, 0, 7], [1.5000000000000004, 0, 6.06217782649107], [1.2990381056766578, 1.2500000000000013, 6.06217782649107]]
142
146
  ]
143
147
  t.notThrows(() => geom3.validate(obs))
148
+ t.is(measureArea(obs), 291.2703265603712)
149
+ t.is(measureVolume(obs), 391.86533479473223)
144
150
  t.is(pts.length, 72)
145
151
  t.true(comparePolygonsAsPoints(pts, exp))
146
152
 
@@ -148,6 +154,8 @@ test('ellipsoid (options)', (t) => {
148
154
  obs = ellipsoid({ segments: 8 })
149
155
  pts = geom3.toPoints(obs)
150
156
  t.notThrows(() => geom3.validate(obs))
157
+ t.is(measureArea(obs), 11.013439076647456)
158
+ t.is(measureVolume(obs), 3.2189514164974597)
151
159
  t.is(pts.length, 32)
152
160
 
153
161
  obs = ellipsoid({ center: [-3, 5, 7], segments: 8 })
@@ -204,6 +212,8 @@ test('ellipsoid (options)', (t) => {
204
212
  ]
205
213
 
206
214
  t.notThrows(() => geom3.validate(obs))
215
+ t.is(measureArea(obs), 11.013439076647467)
216
+ t.is(measureVolume(obs), 3.218951416497485)
207
217
  t.is(pts.length, 32)
208
218
  t.true(comparePolygonsAsPoints(pts, exp))
209
219
  })
@@ -212,5 +222,7 @@ test('ellipsoid (zero radius)', (t) => {
212
222
  const obs = ellipsoid({ radius: [1, 1, 0] })
213
223
  const pts = geom3.toPoints(obs)
214
224
  t.notThrows(() => geom3.validate(obs))
225
+ t.is(measureArea(obs), 0)
226
+ t.is(measureVolume(obs), 0)
215
227
  t.is(pts.length, 0)
216
228
  })
@@ -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 { geodesicSphere } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -41,6 +43,8 @@ test('geodesicSphere (options)', (t) => {
41
43
  ]
42
44
 
43
45
  t.notThrows(() => geom3.validate(obs))
46
+ t.is(measureArea(obs), 239.3635345818432)
47
+ t.is(measureVolume(obs), 317.0188387650327)
44
48
  t.is(pts.length, 20)
45
49
  t.true(comparePolygonsAsPoints(pts, exp))
46
50
 
@@ -49,6 +53,8 @@ test('geodesicSphere (options)', (t) => {
49
53
  pts = geom3.toPoints(obs)
50
54
 
51
55
  t.notThrows.skip(() => geom3.validate(obs))
56
+ t.is(measureArea(obs), 303.76605423529395)
57
+ t.is(measureVolume(obs), 492.6739732379337)
52
58
  t.is(pts.length, 180)
53
59
  })
54
60
 
@@ -56,5 +62,7 @@ test('geodesicSphere (zero radius)', (t) => {
56
62
  const obs = geodesicSphere({ radius: 0 })
57
63
  const pts = geom3.toPoints(obs)
58
64
  t.notThrows(() => geom3.validate(obs))
65
+ t.is(measureArea(obs), 0)
66
+ t.is(measureVolume(obs), 0)
59
67
  t.is(pts.length, 0)
60
68
  })
@@ -12,6 +12,6 @@ test('line (defaults)', (t) => {
12
12
  const obs = path2.toPoints(geometry)
13
13
 
14
14
  t.notThrows(() => path2.validate(geometry))
15
- t.deepEqual(obs.length, 3)
15
+ t.is(obs.length, 3)
16
16
  t.true(comparePoints(obs, exp))
17
17
  })
@@ -4,6 +4,7 @@ import type { Geom2 } from '../geometries/geom2/type.d.ts'
4
4
  export interface PolygonOptions {
5
5
  points: Array<Vec2> | Array<Array<Vec2>>
6
6
  paths?: Array<number> | Array<Array<number>>
7
+ orientation?: 'counterclockwise' | 'clockwise'
7
8
  }
8
9
 
9
10
  export function polygon(options: PolygonOptions): Geom2
@@ -2,10 +2,13 @@ import * as geom2 from '../geometries/geom2/index.js'
2
2
 
3
3
  /**
4
4
  * Construct a polygon in two dimensional space from a list of points, or a list of points and paths.
5
- * NOTE: The ordering of points is VERY IMPORTANT.
5
+ *
6
+ * NOTE: The ordering of points is important, and must define a counter clockwise rotation of points.
7
+ *
6
8
  * @param {object} options - options for construction
7
9
  * @param {Array} options.points - points of the polygon : either flat or nested array of 2D points
8
10
  * @param {Array} [options.paths] - paths of the polygon : either flat or nested array of point indexes
11
+ * @param {String} [options.orientation='counterclockwise'] - orientation of points
9
12
  * @returns {Geom2} new 2D geometry
10
13
  * @alias module:modeling/primitives.polygon
11
14
  *
@@ -24,9 +27,10 @@ import * as geom2 from '../geometries/geom2/index.js'
24
27
  export const polygon = (options) => {
25
28
  const defaults = {
26
29
  points: [],
27
- paths: []
30
+ paths: [],
31
+ orientation: 'counterclockwise'
28
32
  }
29
- const { points, paths } = Object.assign({}, defaults, options)
33
+ const { points, paths, orientation } = Object.assign({}, defaults, options)
30
34
 
31
35
  if (!(Array.isArray(points) && Array.isArray(paths))) throw new Error('points and paths must be arrays')
32
36
 
@@ -63,5 +67,10 @@ export const polygon = (options) => {
63
67
  const setOfPoints = path.map((index) => allPoints[index])
64
68
  outlines.push(setOfPoints)
65
69
  })
66
- return geom2.create(outlines)
70
+
71
+ let geometry = geom2.create(outlines)
72
+ if (orientation === 'clockwise') {
73
+ geometry = geom2.reverse(geometry)
74
+ }
75
+ return geometry
67
76
  }
@@ -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 { polygon } from './index.js'
6
8
 
7
9
  import { comparePoints } from '../../test/helpers/index.js'
@@ -13,6 +15,7 @@ test('polygon: providing only object.points creates expected geometry', (t) => {
13
15
  let exp = [[0, 0], [100, 0], [130, 50], [30, 50]]
14
16
 
15
17
  t.notThrows(() => geom2.validate(geometry))
18
+ t.is(measureArea(geometry), 5000)
16
19
  t.true(comparePoints(obs, exp))
17
20
 
18
21
  geometry = polygon({ points: [[[0, 0], [100, 0], [0, 100]], [[10, 10], [80, 10], [10, 80]]] })
@@ -21,6 +24,7 @@ test('polygon: providing only object.points creates expected geometry', (t) => {
21
24
  exp = [[0, 0], [100, 0], [0, 100], [10, 10], [80, 10], [10, 80]]
22
25
 
23
26
  t.notThrows(() => geom2.validate(geometry))
27
+ t.is(measureArea(geometry), 7450)
24
28
  t.true(comparePoints(obs, exp))
25
29
  })
26
30
 
@@ -31,6 +35,7 @@ test('polygon: providing object.points (array) and object.path (array) creates e
31
35
  let exp = [[30, 50], [130, 50], [100, 0], [0, 0]]
32
36
 
33
37
  t.notThrows(() => geom2.validate(geometry))
38
+ t.is(measureArea(geometry), -5000)
34
39
  t.true(comparePoints(obs, exp))
35
40
 
36
41
  // multiple paths
@@ -40,6 +45,7 @@ test('polygon: providing object.points (array) and object.path (array) creates e
40
45
  exp = [[0, 0], [100, 0], [0, 100], [10, 10], [80, 10], [10, 80]]
41
46
 
42
47
  t.notThrows(() => geom2.validate(geometry))
48
+ t.is(measureArea(geometry), 7450)
43
49
  t.true(comparePoints(obs, exp))
44
50
 
45
51
  // multiple points and paths
@@ -49,5 +55,14 @@ test('polygon: providing object.points (array) and object.path (array) creates e
49
55
  exp = [[0, 0], [100, 0], [0, 100], [10, 10], [80, 10], [10, 80]]
50
56
 
51
57
  t.notThrows(() => geom2.validate(geometry))
58
+ t.is(measureArea(geometry), 7450)
52
59
  t.true(comparePoints(obs, exp))
53
60
  })
61
+
62
+ test('polygon: clockwise points', (t) => {
63
+ const poly = polygon({
64
+ points: [[-10, -0], [-10, -10], [-15, -5]],
65
+ orientation: 'clockwise'
66
+ })
67
+ t.is(measureArea(poly), 25)
68
+ })
@@ -5,6 +5,7 @@ import { isNumberArray } from './commonChecks.js'
5
5
 
6
6
  /**
7
7
  * Construct a polyhedron in three dimensional space from the given set of 3D vertices and faces.
8
+ *
8
9
  * The faces can define outward or inward facing polygons (orientation).
9
10
  * However, each face must define a counterclockwise rotation of vertices which follows the right hand rule.
10
11
  * @param {object} options - options for construction
@@ -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 { polyhedron } from './index.js'
6
8
 
7
9
  import { comparePolygonsAsPoints } from '../../test/helpers/index.js'
@@ -22,7 +24,9 @@ test('polyhedron (points and faces)', (t) => {
22
24
  [[-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]]
23
25
  ]
24
26
  t.notThrows(() => geom3.validate(obs))
25
- t.deepEqual(pts.length, 6)
27
+ t.is(measureArea(obs), 24)
28
+ t.is(measureVolume(obs), 7.999999999999999)
29
+ t.is(pts.length, 6)
26
30
  t.true(comparePolygonsAsPoints(pts, exp))
27
31
 
28
32
  // test orientation
@@ -39,6 +43,8 @@ test('polyhedron (points and faces)', (t) => {
39
43
  [[-10, 10, 0], [10, -10, 0], [-10, -10, 0]]
40
44
  ]
41
45
  t.notThrows(() => geom3.validate(obs))
42
- t.deepEqual(pts.length, 6)
46
+ t.is(measureArea(obs), 965.6854249492379)
47
+ t.is(measureVolume(obs), 1333.3333333333333)
48
+ t.is(pts.length, 6)
43
49
  t.true(comparePolygonsAsPoints(pts, exp))
44
50
  })
@@ -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 { rectangle } from './index.js'
6
8
 
7
9
  import { comparePoints } from '../../test/helpers/index.js'
@@ -17,7 +19,8 @@ test('rectangle (defaults)', (t) => {
17
19
  ]
18
20
 
19
21
  t.notThrows(() => geom2.validate(geometry))
20
- t.deepEqual(obs.length, 4)
22
+ t.is(measureArea(geometry), 4)
23
+ t.is(obs.length, 4)
21
24
  t.true(comparePoints(obs, exp))
22
25
  })
23
26
 
@@ -33,7 +36,8 @@ test('rectangle (options)', (t) => {
33
36
  ]
34
37
 
35
38
  t.notThrows(() => geom2.validate(geometry))
36
- t.deepEqual(obs.length, 4)
39
+ t.is(measureArea(geometry), 4)
40
+ t.is(obs.length, 4)
37
41
  t.true(comparePoints(obs, exp))
38
42
 
39
43
  // test size
@@ -47,7 +51,8 @@ test('rectangle (options)', (t) => {
47
51
  ]
48
52
 
49
53
  t.notThrows(() => geom2.validate(geometry))
50
- t.deepEqual(obs.length, 4)
54
+ t.is(measureArea(geometry), 60)
55
+ t.is(obs.length, 4)
51
56
  t.true(comparePoints(obs, exp))
52
57
  })
53
58
 
@@ -55,5 +60,6 @@ test('rectangle (zero size)', (t) => {
55
60
  const geometry = rectangle({ size: [1, 0] })
56
61
  const obs = geom2.toPoints(geometry)
57
62
  t.notThrows(() => geom2.validate(geometry))
63
+ t.is(measureArea(geometry), 0)
58
64
  t.is(obs.length, 0)
59
65
  })
@@ -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