@itwin/core-geometry 4.0.0-dev.66 → 4.0.0-dev.68

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 (53) hide show
  1. package/lib/cjs/Geometry.d.ts +367 -243
  2. package/lib/cjs/Geometry.d.ts.map +1 -1
  3. package/lib/cjs/Geometry.js +481 -311
  4. package/lib/cjs/Geometry.js.map +1 -1
  5. package/lib/cjs/clipping/ConvexClipPlaneSet.d.ts +6 -4
  6. package/lib/cjs/clipping/ConvexClipPlaneSet.d.ts.map +1 -1
  7. package/lib/cjs/clipping/ConvexClipPlaneSet.js +9 -7
  8. package/lib/cjs/clipping/ConvexClipPlaneSet.js.map +1 -1
  9. package/lib/cjs/clipping/UnionOfConvexClipPlaneSets.d.ts +5 -3
  10. package/lib/cjs/clipping/UnionOfConvexClipPlaneSets.d.ts.map +1 -1
  11. package/lib/cjs/clipping/UnionOfConvexClipPlaneSets.js +4 -2
  12. package/lib/cjs/clipping/UnionOfConvexClipPlaneSets.js.map +1 -1
  13. package/lib/cjs/geometry3d/Plane3dByOriginAndUnitNormal.d.ts +4 -4
  14. package/lib/cjs/geometry3d/Plane3dByOriginAndUnitNormal.d.ts.map +1 -1
  15. package/lib/cjs/geometry3d/Plane3dByOriginAndUnitNormal.js +17 -9
  16. package/lib/cjs/geometry3d/Plane3dByOriginAndUnitNormal.js.map +1 -1
  17. package/lib/cjs/geometry3d/Point2dVector2d.js +2 -2
  18. package/lib/cjs/geometry3d/Point2dVector2d.js.map +1 -1
  19. package/lib/cjs/geometry3d/Point3dVector3d.d.ts +13 -7
  20. package/lib/cjs/geometry3d/Point3dVector3d.d.ts.map +1 -1
  21. package/lib/cjs/geometry3d/Point3dVector3d.js +19 -18
  22. package/lib/cjs/geometry3d/Point3dVector3d.js.map +1 -1
  23. package/lib/cjs/geometry4d/Point4d.d.ts +1 -1
  24. package/lib/cjs/geometry4d/Point4d.d.ts.map +1 -1
  25. package/lib/cjs/geometry4d/Point4d.js +12 -13
  26. package/lib/cjs/geometry4d/Point4d.js.map +1 -1
  27. package/lib/esm/Geometry.d.ts +367 -243
  28. package/lib/esm/Geometry.d.ts.map +1 -1
  29. package/lib/esm/Geometry.js +481 -311
  30. package/lib/esm/Geometry.js.map +1 -1
  31. package/lib/esm/clipping/ConvexClipPlaneSet.d.ts +6 -4
  32. package/lib/esm/clipping/ConvexClipPlaneSet.d.ts.map +1 -1
  33. package/lib/esm/clipping/ConvexClipPlaneSet.js +9 -7
  34. package/lib/esm/clipping/ConvexClipPlaneSet.js.map +1 -1
  35. package/lib/esm/clipping/UnionOfConvexClipPlaneSets.d.ts +5 -3
  36. package/lib/esm/clipping/UnionOfConvexClipPlaneSets.d.ts.map +1 -1
  37. package/lib/esm/clipping/UnionOfConvexClipPlaneSets.js +4 -2
  38. package/lib/esm/clipping/UnionOfConvexClipPlaneSets.js.map +1 -1
  39. package/lib/esm/geometry3d/Plane3dByOriginAndUnitNormal.d.ts +4 -4
  40. package/lib/esm/geometry3d/Plane3dByOriginAndUnitNormal.d.ts.map +1 -1
  41. package/lib/esm/geometry3d/Plane3dByOriginAndUnitNormal.js +17 -9
  42. package/lib/esm/geometry3d/Plane3dByOriginAndUnitNormal.js.map +1 -1
  43. package/lib/esm/geometry3d/Point2dVector2d.js +2 -2
  44. package/lib/esm/geometry3d/Point2dVector2d.js.map +1 -1
  45. package/lib/esm/geometry3d/Point3dVector3d.d.ts +13 -7
  46. package/lib/esm/geometry3d/Point3dVector3d.d.ts.map +1 -1
  47. package/lib/esm/geometry3d/Point3dVector3d.js +19 -18
  48. package/lib/esm/geometry3d/Point3dVector3d.js.map +1 -1
  49. package/lib/esm/geometry4d/Point4d.d.ts +1 -1
  50. package/lib/esm/geometry4d/Point4d.d.ts.map +1 -1
  51. package/lib/esm/geometry4d/Point4d.js +12 -13
  52. package/lib/esm/geometry4d/Point4d.js.map +1 -1
  53. package/package.json +3 -3
@@ -8,7 +8,7 @@ import { Vector3d } from "./geometry3d/Point3dVector3d";
8
8
  /**
9
9
  * Enumeration of the 6 possible orderings of XYZ axis order
10
10
  * * **Note:** There are 3 axis order with right hand system (XYZ = 0, YZX = 1, ZXY = 2) and 3 axis order with
11
- * left hand system (XZY = 4, YXZ = 5, ZYX = 6). Note that AxisOrder is encoding the handedness as well. Cross
11
+ * left hand system (XZY = 4, YXZ = 5, ZYX = 6). Note that `AxisOrder` is encoding the handedness as well. Cross
12
12
  * product of the i_th axis in an ordering (i=0,1,2), with the i+1_th in that ordering, will produce the i+2_th
13
13
  * axis in that ordering.
14
14
  * @public
@@ -41,7 +41,8 @@ export var AxisIndex;
41
41
  /** 2 axis is index 2 */
42
42
  AxisIndex[AxisIndex["Z"] = 2] = "Z";
43
43
  })(AxisIndex || (AxisIndex = {}));
44
- /** Standard views. Used in `Matrix3d.createStandardViewAxes(index: StandardViewIndex, invert: boolean)`
44
+ /**
45
+ * Standard views. Used in `Matrix3d.createStandardViewAxes(index: StandardViewIndex, invert: boolean)`
45
46
  * @public
46
47
  */
47
48
  export var StandardViewIndex;
@@ -50,20 +51,21 @@ export var StandardViewIndex;
50
51
  StandardViewIndex[StandardViewIndex["Top"] = 1] = "Top";
51
52
  /** X to right, negative Y up */
52
53
  StandardViewIndex[StandardViewIndex["Bottom"] = 2] = "Bottom";
53
- /** negative Y to right, Z up */
54
+ /** Negative Y to right, Z up */
54
55
  StandardViewIndex[StandardViewIndex["Left"] = 3] = "Left";
55
- /** Y to right, Z up */
56
+ /** Y to right, Z up */
56
57
  StandardViewIndex[StandardViewIndex["Right"] = 4] = "Right";
57
58
  /** X to right, Z up */
58
59
  StandardViewIndex[StandardViewIndex["Front"] = 5] = "Front";
59
- /** negative X to right, Z up */
60
+ /** Negative X to right, Z up */
60
61
  StandardViewIndex[StandardViewIndex["Back"] = 6] = "Back";
61
- /** isometric: view towards origin from (-1,-1,1) */
62
+ /** Isometric: view towards origin from (-1,-1,1) */
62
63
  StandardViewIndex[StandardViewIndex["Iso"] = 7] = "Iso";
63
- /** right isometric: view towards origin from (1,-1,1) */
64
+ /** Right isometric: view towards origin from (1,-1,1) */
64
65
  StandardViewIndex[StandardViewIndex["RightIso"] = 8] = "RightIso";
65
66
  })(StandardViewIndex || (StandardViewIndex = {}));
66
- /** Enumeration among choice for how a coordinate transformation should incorporate scaling.
67
+ /**
68
+ * Enumeration among choice for how a coordinate transformation should incorporate scaling.
67
69
  * @public
68
70
  */
69
71
  export var AxisScaleSelect;
@@ -75,7 +77,8 @@ export var AxisScaleSelect;
75
77
  /** On each axis, the vector length matches he length of the corresponding edge of the range. */
76
78
  AxisScaleSelect[AxisScaleSelect["NonUniformRangeContainment"] = 2] = "NonUniformRangeContainment";
77
79
  })(AxisScaleSelect || (AxisScaleSelect = {}));
78
- /** Enumeration of possible locations of a point in the plane of a polygon.
80
+ /**
81
+ * Enumeration of possible locations of a point in the plane of a polygon.
79
82
  * @public
80
83
  */
81
84
  export var PolygonLocation;
@@ -108,18 +111,26 @@ export var PolygonLocation;
108
111
  * @public
109
112
  */
110
113
  class Geometry {
111
- /** Test if absolute value of x is huge.
112
- * * See `Geometry.hugeCoordinate`
114
+ /** Test if absolute value of x is large (larger than `Geometry.largeCoordinateResult`) */
115
+ static isLargeCoordinateResult(x) {
116
+ return x > this.largeCoordinateResult || x < -this.largeCoordinateResult;
117
+ }
118
+ /**
119
+ * Test if absolute value of x is large (larger than `Geometry.largeCoordinateResult`).
120
+ * @deprecated in 4.x. Use `isLargeCoordinateResult`.
113
121
  */
114
122
  static isHugeCoordinate(x) {
115
- return x > this.hugeCoordinate || x < -this.hugeCoordinate;
123
+ return Geometry.isLargeCoordinateResult(x);
116
124
  }
117
- /** Test if a number is odd.
118
- */
125
+ /** Test if a number is odd */
119
126
  static isOdd(x) {
120
- return (x & (0x01)) === 1;
127
+ return (x & (0x01)) === 1; // bitwise operation
121
128
  }
122
- /** Correct `distance` to zero if undefined or smaller than metric tolerance. Otherwise return it unchanged. */
129
+ /**
130
+ * Correct distance to zero.
131
+ * * If `distance` magnitude is `undefined` or smaller than `smallMetricDistance`, then return `replacement`
132
+ * (or 0 if replacement is not passed). Otherwise return `distance`.
133
+ */
123
134
  static correctSmallMetricDistance(distance, replacement = 0.0) {
124
135
  if (distance === undefined || Math.abs(distance) < Geometry.smallMetricDistance) {
125
136
  return replacement;
@@ -127,64 +138,119 @@ class Geometry {
127
138
  return distance;
128
139
  }
129
140
  /**
130
- * If `a` is large enough for safe division, return `1/a`, using Geometry.smallMetricDistance as the tolerance for declaring it as divide by zero. Otherwise return `undefined`.
131
- * @param a denominator of division
132
- */
133
- static inverseMetricDistance(a) {
134
- return (Math.abs(a) <= Geometry.smallMetricDistance) ? undefined : 1.0 / a;
141
+ * Correct `fraction` to `replacement` if `fraction` is undefined or too small.
142
+ * @param fraction number to test
143
+ * @param replacement value to return if `fraction` is too small
144
+ * @returns `fraction` if its absolute value is at least `Geometry.smallFraction`; otherwise returns `replacement`
145
+ */
146
+ static correctSmallFraction(fraction, replacement = 0.0) {
147
+ if (fraction === undefined || Math.abs(fraction) < Geometry.smallFraction) {
148
+ return replacement;
149
+ }
150
+ return fraction;
151
+ }
152
+ /**
153
+ * Return the inverse of `distance`.
154
+ * * If `distance` magnitude is smaller than `smallMetricDistance` (i.e. distance is large enough for safe division),
155
+ * then return `1/distance`. Otherwise return `undefined`.
156
+ */
157
+ static inverseMetricDistance(distance) {
158
+ return (Math.abs(distance) <= Geometry.smallMetricDistance) ? undefined : 1.0 / distance;
135
159
  }
136
160
  /**
137
- * If `a` is large enough, return `1/a`, using the square of Geometry.smallMetricDistance as the tolerance for declaring it as divide by zero. Otherwise return `undefined`.
138
- * @param a denominator of division
161
+ * Return the inverse of `distanceSquared`.
162
+ * * If `distanceSquared ` magnitude is smaller than `smallMetricDistanceSquared` (i.e. distanceSquared is large
163
+ * enough for safe division), then return `1/distanceSquared `. Otherwise return `undefined`.
139
164
  */
140
- static inverseMetricDistanceSquared(a) {
141
- return (Math.abs(a) <= Geometry.smallMetricDistanceSquared) ? undefined : 1.0 / a;
165
+ static inverseMetricDistanceSquared(distanceSquared) {
166
+ return (Math.abs(distanceSquared) <= Geometry.smallMetricDistanceSquared) ? undefined : 1.0 / distanceSquared;
142
167
  }
143
168
  /**
144
- * Boolean test for metric coordinate near-equality (i.e., if x and y are almost equal). If tolerance is not passed,
145
- * `Geometry.smallMetricDistance` is used as tolerance.
169
+ * Boolean test for metric coordinate near-equality (i.e., if `x` and `y` are almost equal) using `tolerance`.
170
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
146
171
  */
147
- static isSameCoordinate(x, y, tol) {
148
- if (tol)
149
- return Math.abs(x - y) < Math.abs(tol);
150
- return Math.abs(x - y) < Geometry.smallMetricDistance;
172
+ static isSameCoordinate(x, y, tolerance = Geometry.smallMetricDistance) {
173
+ let d = x - y;
174
+ if (d < 0)
175
+ d = -d;
176
+ return d <= tolerance;
151
177
  }
152
- /** Boolean test for metric coordinate near-equality, with toleranceFactor applied to the usual smallMetricDistance */
178
+ /**
179
+ * Boolean test for metric coordinate near-equality (i.e., if `x` and `y` are almost equal) using
180
+ * `tolerance = toleranceFactor * smallMetricDistance`
181
+ * */
153
182
  static isSameCoordinateWithToleranceFactor(x, y, toleranceFactor) {
154
183
  return Geometry.isSameCoordinate(x, y, toleranceFactor * Geometry.smallMetricDistance);
155
184
  }
156
- /** Boolean test for metric coordinate near-equality of x, y pair */
157
- static isSameCoordinateXY(x0, y0, x1, y1, tol = Geometry.smallMetricDistance) {
185
+ /**
186
+ * Boolean test for metric coordinate pair near-equality (i.e., if `x0` and `x1` are almost equal
187
+ * and `y0` and `y1` are almost equal) using `tolerance`.
188
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
189
+ */
190
+ static isSameCoordinateXY(x0, y0, x1, y1, tolerance = Geometry.smallMetricDistance) {
158
191
  let d = x1 - x0;
159
192
  if (d < 0)
160
193
  d = -d;
161
- if (d > tol)
194
+ if (d > tolerance)
162
195
  return false;
163
196
  d = y1 - y0;
164
197
  if (d < 0)
165
198
  d = -d;
166
- return d < tol;
167
- }
168
- /** Boolean test for squared metric coordinate near-equality */
169
- static isSameCoordinateSquared(x, y) {
170
- return Math.abs(Math.sqrt(x) - Math.sqrt(y)) < Geometry.smallMetricDistance;
171
- }
172
- /** boolean test for small `dataA.distance (dataB)` within `smallMetricDistance` */
173
- static isSamePoint3d(dataA, dataB) { return dataA.distance(dataB) < Geometry.smallMetricDistance; }
174
- /** boolean test for distance between `XYZ` objects within `smallMetricDistance`
175
- * * Note that Point3d and Vector3d are both derived from XYZ, so this method tolerates mixed types.
176
- */
177
- static isSameXYZ(dataA, dataB) { return dataA.distance(dataB) < Geometry.smallMetricDistance; }
178
- /** boolean test for small `dataA.distanceXY (dataB)` within `smallMetricDistance` */
179
- static isSamePoint3dXY(dataA, dataB) { return dataA.distanceXY(dataB) < Geometry.smallMetricDistance; }
180
- /** boolean test for small `dataA.distanceXY (dataB)` within `smallMetricDistance` */
181
- static isSameVector3d(dataA, dataB) { return dataA.distance(dataB) < Geometry.smallMetricDistance; }
182
- /** boolean test for small `dataA.distanceXY (dataB)` within `smallMetricDistance` */
183
- static isSamePoint2d(dataA, dataB) { return dataA.distance(dataB) < Geometry.smallMetricDistance; }
184
- /** boolean test for small `dataA.distanceXY (dataB)` within `smallMetricDistance` */
185
- static isSameVector2d(dataA, dataB) { return dataA.distance(dataB) < Geometry.smallMetricDistance; }
186
- /**
187
- * Lexical comparison of (a.x,a.y) (b.x,b.y) with x as first test, y second.
199
+ return d <= tolerance;
200
+ }
201
+ /**
202
+ * Boolean test for squared metric coordinate near-equality (i.e., if `sqrt(x)` and `sqrt(y)` are
203
+ * almost equal) using `tolerance`.
204
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
205
+ */
206
+ static isSameCoordinateSquared(x, y, tolerance = Geometry.smallMetricDistance) {
207
+ return Math.abs(Math.sqrt(x) - Math.sqrt(y)) <= tolerance;
208
+ }
209
+ /**
210
+ * Boolean test for small `dataA.distance(dataB)` within `tolerance`.
211
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
212
+ */
213
+ static isSamePoint3d(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
214
+ return dataA.distance(dataB) <= tolerance;
215
+ }
216
+ /**
217
+ * Boolean test for small xyz-distance within `tolerance`.
218
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
219
+ * * Note that Point3d and Vector3d are both derived from XYZ, so this method tolerates mixed types.
220
+ */
221
+ static isSameXYZ(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
222
+ return dataA.distance(dataB) <= tolerance;
223
+ }
224
+ /**
225
+ * Boolean test for small xy-distance (ignoring z) within `tolerance`.
226
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
227
+ */
228
+ static isSamePoint3dXY(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
229
+ return dataA.distanceXY(dataB) <= tolerance;
230
+ }
231
+ /**
232
+ * Boolean test for small xyz-distance within `tolerance`.
233
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
234
+ */
235
+ static isSameVector3d(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
236
+ return dataA.distance(dataB) <= tolerance;
237
+ }
238
+ /**
239
+ * Boolean test for small xy-distance within `tolerance`.
240
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
241
+ */
242
+ static isSamePoint2d(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
243
+ return dataA.distance(dataB) <= tolerance;
244
+ }
245
+ /**
246
+ * Boolean test for small xy-distance within `tolerance`.
247
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
248
+ */
249
+ static isSameVector2d(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
250
+ return dataA.distance(dataB) <= tolerance;
251
+ }
252
+ /**
253
+ * Lexical comparison of (a.x, a.y) and (b.x, b.y) with x as first test and y as second (z is ignored).
188
254
  * * This is appropriate for a horizontal sweep in the plane.
189
255
  */
190
256
  static lexicalXYLessThan(a, b) {
@@ -199,7 +265,7 @@ class Geometry {
199
265
  return 0;
200
266
  }
201
267
  /**
202
- * Lexical comparison of (a.x,a.y) (b.x,b.y) with y as first test, x second.
268
+ * Lexical comparison of (a.x, a.y) and (b.x, b.y) with y as first test and x as second (z is ignored).
203
269
  * * This is appropriate for a vertical sweep in the plane.
204
270
  */
205
271
  static lexicalYXLessThan(a, b) {
@@ -213,9 +279,7 @@ class Geometry {
213
279
  return 1;
214
280
  return 0;
215
281
  }
216
- /**
217
- * Lexical test, based on x first, y second, z third.
218
- */
282
+ /** Lexical comparison of (a.x, a.y, a.z) and (b.x, b.y, b.z) with x as first test, y as second, and z as third. */
219
283
  static lexicalXYZLessThan(a, b) {
220
284
  if (a.x < b.x)
221
285
  return -1;
@@ -231,19 +295,21 @@ class Geometry {
231
295
  return 1;
232
296
  return 0;
233
297
  }
234
- /** Test if `value` is small compared to `smallAngleRadians`.
298
+ /**
299
+ * Test if `value` is small compared to `smallFraction`.
235
300
  * * This is appropriate if `value` is know to be a typical 0..1 fraction.
236
301
  */
237
302
  static isSmallRelative(value) {
238
- return Math.abs(value) < Geometry.smallAngleRadians;
303
+ return Math.abs(value) < Geometry.smallFraction;
239
304
  }
240
305
  /** Test if `value` is small compared to `smallAngleRadians` */
241
306
  static isSmallAngleRadians(value) {
242
307
  return Math.abs(value) < Geometry.smallAngleRadians;
243
308
  }
244
- /** Returns true if both values are undefined or if both are defined and almost equal within tolerance.
245
- * If one is undefined and the other is not then false is returned.
246
- */
309
+ /**
310
+ * Returns `true` if both values are `undefined` or if both are defined and almost equal within tolerance.
311
+ * If one is `undefined` and the other is not, then `false` is returned.
312
+ */
247
313
  static isAlmostEqualOptional(a, b, tolerance) {
248
314
  if (a !== undefined && b !== undefined) {
249
315
  if (Math.abs(a - b) > tolerance)
@@ -255,44 +321,43 @@ class Geometry {
255
321
  }
256
322
  return true;
257
323
  }
258
- /** Toleranced equality test, using tolerance `smallAngleRadians * ( 1 + abs(a) + (abs(b)))`
259
- * * Effectively an absolute tolerance of `smallAngleRadians`, with tolerance increasing for larger values of a and b.
260
- */
261
- static isAlmostEqualNumber(a, b) {
324
+ /**
325
+ * Toleranced equality test using tolerance `tolerance * ( 1 + abs(a) + abs(b) )`.
326
+ * * `Geometry.smallAngleRadians` is used if tolerance is `undefined`.
327
+ */
328
+ static isAlmostEqualNumber(a, b, tolerance = Geometry.smallAngleRadians) {
262
329
  const sumAbs = 1.0 + Math.abs(a) + Math.abs(b);
263
- return Math.abs(a - b) <= Geometry.smallAngleRadians * sumAbs;
330
+ return Math.abs(a - b) <= tolerance * sumAbs;
264
331
  }
265
- /** Toleranced equality test, using tolerance `smallAngleRadians * ( 1 + abs(a) + (abs(b)))`
266
- * * Effectively an absolute tolerance of `smallAngleRadians`, with tolerance increasing for larger values of a and b.
267
- */
268
- static isAlmostEqualXAndY(a, b) {
269
- const sumAbs = 1.0 + Math.abs(a.x) + Math.abs(b.x) + Math.abs(a.y) + Math.abs(b.y);
270
- const tolerance = Geometry.smallAngleRadians * sumAbs;
271
- return Math.abs(a.x - b.x) <= tolerance && Math.abs(a.y - b.y) <= tolerance;
332
+ /**
333
+ * Toleranced equality test using tolerance `tolerance * ( 1 + abs(a.x) + abs(a.y) + abs(b.x) + abs(b.y) )`.
334
+ * * `Geometry.smallAngleRadians` is used if tolerance is `undefined`.
335
+ */
336
+ static isAlmostEqualXAndY(a, b, tolerance = Geometry.smallAngleRadians) {
337
+ const tol = tolerance * (1.0 + Math.abs(a.x) + Math.abs(b.x) + Math.abs(a.y) + Math.abs(b.y));
338
+ return (Math.abs(a.x - b.x) <= tol) && (Math.abs(a.y - b.y) <= tol);
272
339
  }
273
340
  /**
274
- * Toleranced equality test, using caller-supplied tolerance.
275
- * If no tolerance is given, use smallMetricDistance.
341
+ * Toleranced equality test using caller-supplied `tolerance`.
342
+ * * `Geometry.smallMetricDistance` is used if tolerance is `undefined`.
276
343
  */
277
- static isDistanceWithinTol(distance, tol) {
278
- if (tol !== undefined)
279
- return Math.abs(distance) <= Math.abs(tol);
280
- return Math.abs(distance) <= Geometry.smallMetricDistance;
344
+ static isDistanceWithinTol(distance, tolerance = Geometry.smallMetricDistance) {
345
+ return Math.abs(distance) <= tolerance;
281
346
  }
282
- /** Toleranced equality test, using `smallMetricDistance` tolerance. */
347
+ /** Toleranced equality test using `smallMetricDistance` tolerance. */
283
348
  static isSmallMetricDistance(distance) {
284
349
  return Math.abs(distance) <= Geometry.smallMetricDistance;
285
350
  }
286
- /** Toleranced equality, using `smallMetricDistanceSquared` tolerance. */
351
+ /** Toleranced equality test using `smallMetricDistanceSquared` tolerance. */
287
352
  static isSmallMetricDistanceSquared(distanceSquared) {
288
353
  return Math.abs(distanceSquared) <= Geometry.smallMetricDistanceSquared;
289
354
  }
290
355
  /**
291
356
  * Return `axis modulo 3` with proper handling of negative indices
292
357
  * ..., -3:x, -2:y, -1:z, 0:x, 1:y, 2:z, 3:x, 4:y, 5:z, 6:x, 7:y, 8:z, ...
293
- * */
358
+ */
294
359
  static cyclic3dAxis(axis) {
295
- /* Direct test for the most common cases, avoid modulo */
360
+ /* Direct test for the most common cases to avoid more expensive modulo operation */
296
361
  if (axis >= 0) {
297
362
  if (axis < 3)
298
363
  return axis;
@@ -305,7 +370,8 @@ class Geometry {
305
370
  return j;
306
371
  return 2 - ((-axis - 1) % 3);
307
372
  }
308
- /** Return the AxisOrder for which axisIndex is the first named axis.
373
+ /**
374
+ * Return the `AxisOrder` for which `axisIndex` is the first named axis.
309
375
  * * `axisIndex === 0` returns `AxisOrder.XYZ`
310
376
  * * `axisIndex === 1` returns `AxisOrder.YZX`
311
377
  * * `axisIndex === 2` returns `AxisOrder.ZXY`
@@ -319,32 +385,55 @@ class Geometry {
319
385
  return AxisOrder.ZXY;
320
386
  return Geometry.axisIndexToRightHandedAxisOrder(Geometry.cyclic3dAxis(axisIndex));
321
387
  }
322
- /** Return the largest absolute distance from a to either of b0 or b1 */
323
- static maxAbsDiff(a, b0, b1) {
324
- return Math.max(Math.abs(a - b0), Math.abs(a - b1));
388
+ /** Return the largest signed value among `a`, `b`, and `c` */
389
+ static maxXYZ(a, b, c) {
390
+ let max = a;
391
+ if (b > max)
392
+ max = b;
393
+ if (c > max)
394
+ max = c;
395
+ return max;
396
+ }
397
+ /** Return the smallest signed value among `a`, `b`, and `c` */
398
+ static minXYZ(a, b, c) {
399
+ let min = a;
400
+ if (b < min)
401
+ min = b;
402
+ if (c < min)
403
+ min = c;
404
+ return min;
405
+ }
406
+ /** Return the largest signed value among `a` and `b` */
407
+ static maxXY(a, b) {
408
+ let max = a;
409
+ if (b > max)
410
+ max = b;
411
+ return max;
325
412
  }
326
- /** Return the largest absolute absolute value among x,y,z */
413
+ /** Return the smallest signed value among `a` and `b` */
414
+ static minXY(a, b) {
415
+ let min = a;
416
+ if (b < min)
417
+ min = b;
418
+ return min;
419
+ }
420
+ /** Return the largest absolute value among `x`, `y`, and `z` */
327
421
  static maxAbsXYZ(x, y, z) {
328
422
  return Geometry.maxXYZ(Math.abs(x), Math.abs(y), Math.abs(z));
329
423
  }
330
- /** Return the largest absolute absolute value among x,y */
424
+ /** Return the largest absolute value among `x` and `y` */
331
425
  static maxAbsXY(x, y) {
332
426
  return Geometry.maxXY(Math.abs(x), Math.abs(y));
333
427
  }
334
- /** Return the largest signed value among a, b, c */
335
- static maxXYZ(a, b, c) {
336
- let q = a;
337
- if (b > q)
338
- q = b;
339
- if (c > q)
340
- q = c;
341
- return q;
428
+ /** Return the largest absolute distance from `a` to either of `b0` or `b1` */
429
+ static maxAbsDiff(a, b0, b1) {
430
+ return Math.max(Math.abs(a - b0), Math.abs(a - b1));
342
431
  }
343
432
  /**
344
- * Examine the value (particularly sign) of x.
345
- * * If x is negative, return outNegative.
346
- * * If x is true zero, return outZero
347
- * * If x is positive, return outPositive
433
+ * Examine the sign of `x`.
434
+ * * If `x` is negative, return `outNegative`
435
+ * * If `x` is true zero, return `outZero`
436
+ * * If `x` is positive, return `outPositive`
348
437
  */
349
438
  static split3WaySign(x, outNegative, outZero, outPositive) {
350
439
  if (x < 0)
@@ -366,45 +455,40 @@ class Geometry {
366
455
  return -1;
367
456
  return 0;
368
457
  }
369
- /** Return the largest signed value among a, b */
370
- static maxXY(a, b) {
371
- let q = a;
372
- if (b > q)
373
- q = b;
374
- return q;
375
- }
376
- /** Return the smallest signed value among a, b */
377
- static minXY(a, b) {
378
- let q = a;
379
- if (b < q)
380
- q = b;
381
- return q;
458
+ /** Return the square of x */
459
+ static square(x) {
460
+ return x * x;
382
461
  }
383
- /** Return the hypotenuse `sqrt(x*x + y*y)`. This is much faster than `Math.hypot(x,y)`. */
462
+ /**
463
+ * Return the hypotenuse (i.e., `sqrt(x*x + y*y)`).
464
+ * * This is much faster than `Math.hypot(x,y)`.
465
+ */
384
466
  static hypotenuseXY(x, y) {
385
467
  return Math.sqrt(x * x + y * y);
386
468
  }
387
- /** Return the squared `hypotenuse (x*x + y*y)`. */
469
+ /** Return the squared hypotenuse (i.e., `x*x + y*y`). */
388
470
  static hypotenuseSquaredXY(x, y) {
389
471
  return x * x + y * y;
390
472
  }
391
- /** Return the square of x */
392
- static square(x) {
393
- return x * x;
394
- }
395
- /** Return the hypotenuse `sqrt(x*x + y*y + z*z)`. This is much faster than `Math.hypot(x,y,z)`. */
473
+ /**
474
+ * Return the hypotenuse (i.e., `sqrt(x*x + y*y + z*z)`).
475
+ * * This is much faster than `Math.hypot(x,y,z)`.
476
+ */
396
477
  static hypotenuseXYZ(x, y, z) {
397
478
  return Math.sqrt(x * x + y * y + z * z);
398
479
  }
399
- /** Return the squared hypotenuse `(x*x + y*y + z*z)`. This is much faster than `Math.hypot(x,y,z)`. */
480
+ /** Return the squared hypotenuse (i.e., `x*x + y*y + z*z`). */
400
481
  static hypotenuseSquaredXYZ(x, y, z) {
401
482
  return x * x + y * y + z * z;
402
483
  }
403
- /** Return the (full 4d) hypotenuse `sqrt(x*x + y*y + z*z + w*w)`. This is much faster than `Math.hypot(x,y,z,w)`. */
484
+ /**
485
+ * Return the full 4d hypotenuse (i.e., `sqrt(x*x + y*y + z*z + w*w)`).
486
+ * * This is much faster than `Math.hypot(x,y,z,w)`.
487
+ */
404
488
  static hypotenuseXYZW(x, y, z, w) {
405
489
  return Math.sqrt(x * x + y * y + z * z + w * w);
406
490
  }
407
- /** Return the squared hypotenuse `(x*x + y*y + z*z+w*w)`. This is much faster than `Math.hypot(x,y,z)`. */
491
+ /** Return the squared hypotenuse (i.e., `x*x + y*y + z*z + w*w`). */
408
492
  static hypotenuseSquaredXYZW(x, y, z, w) {
409
493
  return x * x + y * y + z * z + w * w;
410
494
  }
@@ -430,8 +514,8 @@ class Geometry {
430
514
  static distanceXYZXYZ(x0, y0, z0, x1, y1, z1) {
431
515
  return Geometry.hypotenuseXYZ(x1 - x0, y1 - y0, z1 - z0);
432
516
  }
433
- /** Returns Returns the triple product of 3 vectors provided as x,y,z number sequences.
434
- *
517
+ /**
518
+ * Returns the triple product of 3 vectors provided as x,y,z number sequences.
435
519
  * * The triple product is the determinant of the 3x3 matrix with the 9 numbers (3 vectors placed in 3 rows).
436
520
  * * The triple product is positive if the 3 vectors form a right handed coordinate system.
437
521
  * * The triple product is negative if the 3 vectors form a left handed coordinate system.
@@ -439,170 +523,196 @@ class Geometry {
439
523
  * * U dot (V cross W)
440
524
  * * V dot (W cross U)
441
525
  * * W dot (U cross V)
442
- * * (-U dot (W cross V)) -- (note the negative -- reversing cross product order changes the sign)
443
- * * (-V dot (U cross W)) -- (note the negative -- reversing cross product order changes the sign)
444
- * * (-W dot (V cross U)) -- (note the negative -- reversing cross product order changes the sign)
445
- * * the triple product is 6 times the (signed) volume of the tetrahedron with the three vectors as edges from a common vertex.
526
+ * * -U dot (W cross V)
527
+ * * -V dot (U cross W)
528
+ * * -W dot (V cross U)
529
+ * * Note the negative in the last 3 formulas. Reversing cross product order changes the sign.
530
+ * * The triple product is 6 times the (signed) volume of the tetrahedron with the three vectors as edges from a
531
+ * common vertex.
446
532
  */
447
533
  static tripleProduct(ux, uy, uz, vx, vy, vz, wx, wy, wz) {
448
534
  return ux * (vy * wz - vz * wy)
449
535
  + uy * (vz * wx - vx * wz)
450
536
  + uz * (vx * wy - vy * wx);
451
537
  }
452
- /** Returns the determinant of the 4x4 matrix unrolled as the 16 parameters.
453
- */
538
+ /** Returns the determinant of the 4x4 matrix unrolled as the 16 parameters */
454
539
  static determinant4x4(xx, xy, xz, xw, yx, yy, yz, yw, zx, zy, zz, zw, wx, wy, wz, ww) {
455
540
  return xx * this.tripleProduct(yy, yz, yw, zy, zz, zw, wy, wz, ww)
456
541
  - yx * this.tripleProduct(xy, xz, xw, zy, zz, zw, wy, wz, ww)
457
542
  + zx * this.tripleProduct(xy, xz, xw, yy, yz, yw, wy, wz, ww)
458
543
  - wx * this.tripleProduct(xy, xz, xw, yy, yz, yw, zy, zz, zw);
459
544
  }
460
- /** Return the mean curvature for two radii, with 0 radius implying 0 curvature */
461
- static meanCurvatureOfRadii(r0, r1) {
462
- return 0.5 * (this.safeDivideFraction(1, r0, 0) + this.safeDivideFraction(1, r1, 0));
463
- }
464
545
  /**
465
- * Returns curvature magnitude from a first and second derivative vector.
466
- * @param ux first derivative x component
467
- * @param uy first derivative y component
468
- * @param uz first derivative z component
469
- * @param vx second derivative x component
470
- * @param vy second derivative y component
471
- * @param vz second derivative z component
472
- */
473
- static curvatureMagnitude(ux, uy, uz, vx, vy, vz) {
474
- let q = uy * vz - uz * vy;
475
- let sum = q * q;
476
- q = uz * vx - ux * vz;
477
- sum += q * q;
478
- q = ux * vy - uy * vx;
479
- sum += q * q;
480
- const a = Math.sqrt(ux * ux + uy * uy + uz * uz);
481
- const b = Math.sqrt(sum);
482
- // (sum and a are both nonnegative)
483
- const aaa = a * a * a;
484
- // radius of curvature = aaa / b;
485
- // curvature = b/aaa
486
- const tol = Geometry.smallAngleRadians;
487
- if (aaa > tol * b)
488
- return b / aaa;
489
- return 0; // hm.. maybe should be infinite?
490
- }
491
- /** Returns the determinant of 3x3 matrix with x and y rows taken from 3 points, third row from corresponding numbers.
492
- *
546
+ * Returns the determinant of 3x3 matrix with first and second rows created from the 3 xy points and the third
547
+ * row created from the 3 numbers:
548
+ * [columnA.x columnB.x columnC.x]
549
+ * [columnA.y columnB.y columnC.y]
550
+ * [ weightA weightB weightC ]
493
551
  */
494
552
  static tripleProductXYW(columnA, weightA, columnB, weightB, columnC, weightC) {
495
553
  return Geometry.tripleProduct(columnA.x, columnB.x, columnC.x, columnA.y, columnB.y, columnC.y, weightA, weightB, weightC);
496
554
  }
497
- /** Returns the determinant of 3x3 matrix with x and y rows taken from 3 points, third row from corresponding numbers.
498
- *
555
+ /**
556
+ * Returns the determinant of 3x3 matrix columns created by the given `Point4d` ignoring the z part:
557
+ * [columnA.x columnB.x columnC.x]
558
+ * [columnA.y columnB.y columnC.y]
559
+ * [columnA.w columnB.w columnC.w]
499
560
  */
500
561
  static tripleProductPoint4dXYW(columnA, columnB, columnC) {
501
562
  return Geometry.tripleProduct(columnA.x, columnB.x, columnC.x, columnA.y, columnB.y, columnC.y, columnA.w, columnB.w, columnC.w);
502
563
  }
503
- /** 2D cross product of vectors layed out as scalars. */
564
+ /** 2D cross product of vectors with the vectors presented as numbers. */
504
565
  static crossProductXYXY(ux, uy, vx, vy) {
505
566
  return ux * vy - uy * vx;
506
567
  }
507
- /** 3D cross product of vectors layed out as scalars. */
568
+ /** 3D cross product of vectors with the vectors presented as numbers. */
508
569
  static crossProductXYZXYZ(ux, uy, uz, vx, vy, vz, result) {
509
570
  return Vector3d.create(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx, result);
510
571
  }
511
- /** magnitude of 3D cross product of vectors, with the vectors presented as */
572
+ /** Magnitude of 3D cross product of vectors with the vectors presented as numbers. */
512
573
  static crossProductMagnitude(ux, uy, uz, vx, vy, vz) {
513
574
  return Geometry.hypotenuseXYZ(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx);
514
575
  }
515
- /** 3D dot product of vectors layed out as scalars. */
576
+ /** 2D dot product of vectors with the vectors presented as numbers. */
577
+ static dotProductXYXY(ux, uy, vx, vy) {
578
+ return ux * vx + uy * vy;
579
+ }
580
+ /** 3D dot product of vectors with the vectors presented as numbers. */
516
581
  static dotProductXYZXYZ(ux, uy, uz, vx, vy, vz) {
517
582
  return ux * vx + uy * vy + uz * vz;
518
583
  }
519
- /** 2D dot product of vectors layed out as scalars. */
520
- static dotProductXYXY(ux, uy, vx, vy) {
521
- return ux * vx + uy * vy;
584
+ /**
585
+ * Return the mean curvature for two radii.
586
+ * * Curvature is the reciprocal of radius.
587
+ * * 0 radius implies 0 curvature.
588
+ * @param r0 first radius
589
+ * @param r1 second radius
590
+ */
591
+ static meanCurvatureOfRadii(r0, r1) {
592
+ return 0.5 * (this.safeDivideFraction(1, r0, 0) + this.safeDivideFraction(1, r1, 0));
522
593
  }
523
594
  /**
524
- * Clamp to (min(a,b), max(a,b))
525
- * * always returns a number between a and b
526
- * @param x
527
- * @param a
528
- * @param b
595
+ * Returns curvature from the first and second derivative vectors.
596
+ * * If U is the first derivative and V is the second derivative, the curvature is defined as:
597
+ * * `|| U x V || / || U ||^3`.
598
+ * * Math details can be found at https://en.wikipedia.org/wiki/Curvature#General_expressions
599
+ * @param ux first derivative x component
600
+ * @param uy first derivative y component
601
+ * @param uz first derivative z component
602
+ * @param vx second derivative x component
603
+ * @param vy second derivative y component
604
+ * @param vz second derivative z component
605
+ */
606
+ static curvatureMagnitude(ux, uy, uz, vx, vy, vz) {
607
+ let q = uy * vz - uz * vy;
608
+ let sum = q * q;
609
+ q = uz * vx - ux * vz;
610
+ sum += q * q;
611
+ q = ux * vy - uy * vx;
612
+ sum += q * q;
613
+ const magUxV = Math.sqrt(sum);
614
+ const magU = Math.sqrt(ux * ux + uy * uy + uz * uz);
615
+ const magUCubed = magU * magU * magU;
616
+ if (magUCubed > Geometry.smallAngleRadians * magUxV)
617
+ return magUxV / magUCubed;
618
+ return 0;
619
+ }
620
+ /**
621
+ * Clamp to (min(a,b), max(a,b)).
622
+ * * Always returns a number between `a` and `b`.
623
+ * @param value value to clamp
624
+ * @param a smallest allowed output if `a < b` or largest allowed output if `a > b`
625
+ * @param b largest allowed output if `a < b` or smallest allowed output if `a > b`
529
626
  */
530
- static clampToStartEnd(x, a, b) {
627
+ static clampToStartEnd(value, a, b) {
531
628
  if (a > b)
532
- return Geometry.clampToStartEnd(x, b, a);
533
- if (x < a)
629
+ return Geometry.clampToStartEnd(value, b, a);
630
+ if (value < a)
534
631
  return a;
535
- if (b < x)
632
+ if (b < value)
536
633
  return b;
537
- return x;
634
+ return value;
538
635
  }
539
636
  /**
540
- * Clamp value to (min,max) with no test for order of (min,max)
637
+ * Clamp value to (min, max) with no test for order of (min, max).
638
+ * * Always returns a number between `min` and `max`.
541
639
  * @param value value to clamp
542
640
  * @param min smallest allowed output
543
- * @param max largest allowed result.
641
+ * @param max largest allowed output
544
642
  */
545
- static clamp(value, min, max) { return Math.max(min, Math.min(max, value)); }
546
- /** If given a number, return it. If given undefined, return `defaultValue`. */
643
+ static clamp(value, min, max) {
644
+ return Math.max(min, Math.min(max, value));
645
+ }
646
+ /** If given a `value`, return it. If given `undefined`, return `defaultValue`. */
547
647
  static resolveNumber(value, defaultValue = 0) {
548
648
  return value !== undefined ? value : defaultValue;
549
649
  }
550
- /** If given a value, return it. If given undefined, return `defaultValue`. */
650
+ /** If given a `value`, return it. If given `undefined`, return `defaultValue`. */
551
651
  static resolveValue(value, defaultValue) {
552
652
  return value !== undefined ? value : defaultValue;
553
653
  }
554
- /** If given value matches a target, return undefined. Otherwise return the value. */
654
+ /** If given `value` matches the `targetValue`, return `undefined`. Otherwise return the `value`. */
555
655
  static resolveToUndefined(value, targetValue) {
556
656
  return value === targetValue ? undefined : value;
557
657
  }
558
658
  /**
559
- * Simple interpolation between values, but choosing (based on fraction) a or b as starting
560
- * point for maximum accuracy.
659
+ * Simple interpolation between values `a` and `b` with fraction `f`.
561
660
  * * If `f = 0`, then `a` is returned and if `f = 1`, then `b` is returned.
661
+ * * For maximum accuracy, we choose `a` or `b` as starting point based on fraction `f`.
562
662
  */
563
663
  static interpolate(a, f, b) {
564
664
  return f <= 0.5 ? a + f * (b - a) : b - (1.0 - f) * (b - a);
565
665
  }
566
666
  /**
567
- * Given an axisOrder (e.g. XYZ, YZX, etc) and an index, returns the axis index at the given index.
568
- * * For example, if axisOrder = XYZ, then for index 0 returns X (or axis index 0), for index 1 returns
569
- * Y (or axis index 1), and for index 2 returns Z (or axis index 2). For indexes greater than 2 or smaller
570
- * than 0, it returns cyclic axis index. See Geometry.cyclic3dAxis for more info.
571
- * * Another example: if axisOrder = ZYX, then for index 0 returns Z (or axis index 2), for index 1 returns
572
- * Y (or axis index 1), and for index 2 returns X (or axis index 0).
573
- * */
667
+ * Given an `axisOrder` (e.g. XYZ, YZX, etc) and an `index`, return the `axis` at the given index.
668
+ * * For example, if `axisOrder = XYZ`, then for index 0 return `X` (or axis 0), for index 1 return
669
+ * `Y` (or axis 1), and for index 2 return `Z` (or axis 2).
670
+ * * Another example: if `axisOrder = ZXY`, then for index 0 return `Z` (or axis 2), for index 1 return
671
+ * `X` (or axis 0), and for index 2 return `Y` (or axis 1).
672
+ * * For indexes greater than 2 or smaller than 0, it return cyclic axis. See [[Geometry.cyclic3dAxis]]
673
+ * for more info.
674
+ */
574
675
  static axisOrderToAxis(order, index) {
575
676
  const axis = order <= AxisOrder.ZXY ? order + index : (order - AxisOrder.XZY) - index;
576
677
  return Geometry.cyclic3dAxis(axis);
577
678
  }
578
- /** Return (a modulo period), e.g. for use as a cyclic index. Both a and period may be negative. */
679
+ /**
680
+ * Return `a` modulo `period`.
681
+ * * Both `a` and `period` can be negative.
682
+ * * This function can be faster than the `%` operator for the common case when `p > 0` and `-p < a < 2p`.
683
+ */
579
684
  static modulo(a, period) {
685
+ // period is negative
580
686
  if (period <= 0) {
581
687
  if (period === 0)
582
688
  return a;
583
689
  return -Geometry.modulo(-a, -period);
584
690
  }
691
+ // period is positive
585
692
  if (a >= 0) {
586
- if (a < period)
693
+ if (a < period) // "0 < a < period"
587
694
  return a;
588
- if (a < 2 * period)
695
+ if (a < 2 * period) // "0 < period < a < 2*period"
589
696
  return a - period;
590
697
  }
591
- else {
592
- a += period; // hopefully move into primary period without division and floor
698
+ else { // "-period < a < 0"
699
+ a += period;
593
700
  if (a > 0)
594
701
  return a;
595
702
  }
703
+ // "0 < 2*period < a" or "a < -period < 0"
596
704
  const m = Math.floor(a / period);
597
705
  return a - m * period;
598
706
  }
599
- /** return 0 if the value is undefined, 1 if defined. */
600
- static defined01(value) { return value === undefined ? 0 : 1; }
707
+ /** Return 0 if the value is `undefined` and 1 if the value is defined. */
708
+ static defined01(value) {
709
+ return value === undefined ? 0 : 1;
710
+ }
601
711
  /**
602
- * Return `numerator` divided by `denominator`, or `undefined`.
712
+ * Return `numerator` divided by `denominator`.
603
713
  * @param numerator the numerator
604
714
  * @param denominator the denominator
605
- * @returns return `numerator/denominator` but if the ratio would exceed `Geometry.largeFractionResult`,
715
+ * @returns return `numerator/denominator` but if the ratio exceeds `Geometry.largeFractionResult`,
606
716
  * return `undefined`.
607
717
  */
608
718
  static conditionalDivideFraction(numerator, denominator) {
@@ -614,76 +724,105 @@ class Geometry {
614
724
  * Return `numerator` divided by `denominator`.
615
725
  * @param numerator the numerator
616
726
  * @param denominator the denominator
617
- * @returns return `numerator/denominator` but if the ratio would exceed `Geometry.largeFractionResult`,
727
+ * @returns return `numerator/denominator` but if the ratio exceeds `Geometry.largeFractionResult`,
618
728
  * return `defaultResult`.
619
729
  */
620
730
  static safeDivideFraction(numerator, denominator, defaultResult) {
621
- const a = Geometry.conditionalDivideFraction(numerator, denominator);
622
- if (a !== undefined)
623
- return a;
731
+ const ratio = Geometry.conditionalDivideFraction(numerator, denominator);
732
+ if (ratio !== undefined)
733
+ return ratio;
624
734
  return defaultResult;
625
735
  }
626
736
  /**
627
- * Return `numerator` divided by `denominator` (with a given `largestResult`), or `undefined`.
737
+ * Return `numerator` divided by `denominator` (with a given `largestResult`).
628
738
  * @param numerator the numerator
629
739
  * @param denominator the denominator
630
- * @param largestResult the ratio threshold.
631
- * @returns return `numerator/denominator` but if the ratio would exceed `largestResult`, return `undefined`.
740
+ * @param largestResult the ratio threshold
741
+ * @returns return `numerator/denominator` but if the ratio exceeds `largestResult`, return `undefined`.
632
742
  */
633
743
  static conditionalDivideCoordinate(numerator, denominator, largestResult = Geometry.largeCoordinateResult) {
634
744
  if (Math.abs(denominator * largestResult) > Math.abs(numerator))
635
745
  return numerator / denominator;
636
746
  return undefined;
637
747
  }
638
- /** return the 0, 1, or 2 pairs of (c,s) values that solve
639
- * {constCoff + cosCoff * c + sinCoff * s = 0}
640
- * with the constraint {c*c+s*s = 1}
748
+ /**
749
+ * Return solution(s) of equation `constCoff + cosCoff*c + sinCoff*s = 0` for `c` and `s` with the
750
+ * constraint `c*c + s*s = 1`.
751
+ * * There could be 0, 1, or 2 solutions. Return `undefined` if there is no solution.
641
752
  */
642
753
  static solveTrigForm(constCoff, cosCoff, sinCoff) {
643
- {
644
- const delta2 = cosCoff * cosCoff + sinCoff * sinCoff;
645
- const constCoff2 = constCoff * constCoff;
646
- // nSolution = 0
647
- let result;
648
- if (delta2 > 0.0) {
649
- const lambda = -constCoff / delta2;
650
- const a2 = constCoff2 / delta2;
651
- const D2 = 1.0 - a2;
652
- if (-Geometry.smallMetricDistanceSquared < D2 && D2 <= 0.0) { // observed D2 = -2.22e-16 in rotated system
653
- // nSolution = 1
654
- const c0 = lambda * cosCoff;
655
- const s0 = lambda * sinCoff;
656
- result = [Vector2d.create(c0, s0)];
657
- }
658
- else if (D2 > 0.0) {
659
- const mu = Math.sqrt(D2 / delta2);
660
- /* c0,s0 = closest approach of line to origin */
661
- const c0 = lambda * cosCoff;
662
- const s0 = lambda * sinCoff;
663
- // nSolution = 2
664
- result = [Vector2d.create(c0 - mu * sinCoff, s0 + mu * cosCoff), Vector2d.create(c0 + mu * sinCoff, s0 - mu * cosCoff)];
665
- }
754
+ /**
755
+ * Solutions can be found by finding the intersection of line "ax + by + d = 0" and unit circle "x^2 + y^2 = 1".
756
+ * From the line equation we have "y = (-ax - d) / b". By replacing this into the circle equation we get
757
+ * "x^2 + (ax+d)^2/b^2 = 1". If we solve this by quadratic formula we get
758
+ * x = (-ad +- b*sqrt(a^2+b^2-d^2)) / (a^2+b^2)
759
+ * y = (-ad -+ a*sqrt(a^2+b^2-d^2)) / (a^2+b^2)
760
+ *
761
+ * If "a^2+b^2-d^2 > 0" then there are two solutions (above).
762
+ * If "a^2+b^2-d^2 = 0" then there is one solution which is (-ad/(a^2+b^2), -bd/(a^2+b^2)).
763
+ * If "a^2+b^2-d^2 < 0" then there is no solution.
764
+ *
765
+ * Below in the code we have "a = cosCoff", "b = sinCoff", and "d = constCoff". Also equivalent criterion
766
+ * is used in the code. For example, "a^2+b^2-d^2 > 0" is equivalent of "1 - d^2/(a^2+b^2) > 0".
767
+ */
768
+ const a2b2 = cosCoff * cosCoff + sinCoff * sinCoff; // a^2+b^2
769
+ const d2 = constCoff * constCoff; // d^2
770
+ let result;
771
+ if (a2b2 > 0.0) {
772
+ const a2b2r = 1.0 / a2b2; // 1/(a^2+b^2)
773
+ const d2a2b2 = d2 * a2b2r; // d^2/(a^2+b^2)
774
+ const criteria = 1.0 - d2a2b2; // 1 - d^2/(a^2+b^2); the criteria to specify how many solutions we got
775
+ if (criteria < -Geometry.smallMetricDistanceSquared) // nSolution = 0
776
+ return result;
777
+ const da2b2 = -constCoff * a2b2r; // -d/(a^2+b^2)
778
+ const c0 = da2b2 * cosCoff; // -ad/(a^2+b^2)
779
+ const s0 = da2b2 * sinCoff; // -bd/(a^2+b^2)
780
+ if (criteria <= 0.0) { // nSolution = 1 (observed criteria = -2.22e-16 in rotated system)
781
+ result = [Vector2d.create(c0, s0)];
782
+ }
783
+ else if (criteria > 0.0) { // nSolution = 2
784
+ const s = Math.sqrt(criteria * a2b2r); // sqrt(a^2+b^2-d^2)) / (a^2+b^2)
785
+ result = [
786
+ Vector2d.create(c0 - s * sinCoff, s0 + s * cosCoff),
787
+ Vector2d.create(c0 + s * sinCoff, s0 - s * cosCoff),
788
+ ];
666
789
  }
667
- return result;
668
790
  }
791
+ return result;
669
792
  }
670
- /** For a line f(x) whose function values at x0 and x1 are f0 and f1, return the x value at which f(x)=fTarget; */
671
- static inverseInterpolate(x0, f0, x1, f1, targetF = 0, defaultResult) {
672
- const g = Geometry.conditionalDivideFraction(targetF - f0, f1 - f0);
673
- if (g)
674
- return Geometry.interpolate(x0, g, x1);
793
+ /**
794
+ * For a line `f(x)` where `f(x0) = f0` and `f(x1) = f1`, return the `x` value at which `f(x) = fTarget`
795
+ * Return `defaultResult` if `(fTarget - f0) / (f1 - f0)` exceeds `Geometry.largeFractionResult`
796
+ */
797
+ static inverseInterpolate(x0, f0, x1, f1, fTarget = 0, defaultResult) {
798
+ /**
799
+ * Line equation is "fTarget-f0 = (f1-f0)/(x1-x0) * (x-x0)" or "(fTarget-f0)/(f1-f0) = (x-x0)/(x1-x0)".
800
+ * The left hand side is known so if we call it "fr" (short for "fraction") we get "fr = (x-x0)/(x1-x0)".
801
+ * Therefore, "x = x0*(1-fr) + x1*fr". This is same as interpolation between "x0" and "x1" with fraction "fr".
802
+ */
803
+ const fr = Geometry.conditionalDivideFraction(fTarget - f0, f1 - f0); // (fTarget-f0)/(f1-f0)
804
+ if (fr !== undefined)
805
+ return Geometry.interpolate(x0, fr, x1); // x = x0*(1-fr) + x1*fr
675
806
  return defaultResult;
676
807
  }
677
- /** For a line f(x) whose function values at x=0 and x=1 are f0 and f1, return the x value at which f(x)=fTarget; */
678
- static inverseInterpolate01(f0, f1, targetF = 0) {
679
- return Geometry.conditionalDivideFraction(targetF - f0, f1 - f0);
808
+ /**
809
+ * For a line `f(x)` where `f(0) = f0` and `f(1) = f1`, return the `x` value at which `f(x) = fTarget`
810
+ * Return `undefined` if `(fTarget - f0) / (f1 - f0)` exceeds `Geometry.largeFractionResult`
811
+ */
812
+ static inverseInterpolate01(f0, f1, fTarget = 0) {
813
+ /**
814
+ * Line equation is "fTarget-f0 = (f1-f0)*x" so "x = (fTarget-f0)/(f1-f0)"
815
+ */
816
+ return Geometry.conditionalDivideFraction(fTarget - f0, f1 - f0); // x = (fTarget-f0)/(f1-f0)
680
817
  }
681
- /** Return true if json is an array with at least minEntries, and all entries are numbers (including those beyond minEntries) */
818
+ /**
819
+ * Return `true` if `json` is an array with at least `minEntries` entries and all entries are numbers (including
820
+ * those beyond minEntries).
821
+ */
682
822
  static isNumberArray(json, minEntries = 0) {
683
823
  if (Array.isArray(json) && json.length >= minEntries) {
684
824
  let entry;
685
825
  for (entry of json) {
686
- // if (!(entry as number) && entry !== 0.0)
687
826
  if (!Number.isFinite(entry))
688
827
  return false;
689
828
  }
@@ -691,10 +830,12 @@ class Geometry {
691
830
  }
692
831
  return false;
693
832
  }
694
- /** Return true if json is an array of at least numNumberArrays, with at least minEntries in each number array.
833
+ /**
834
+ * Return `true` if `json` is an array of at least `minArrays` arrays with at least `minEntries` entries in
835
+ * each array and all entries are numbers (including those beyond minEntries).
695
836
  */
696
- static isArrayOfNumberArray(json, numNumberArray, minEntries = 0) {
697
- if (Array.isArray(json) && json.length >= numNumberArray) {
837
+ static isArrayOfNumberArray(json, minArrays, minEntries = 0) {
838
+ if (Array.isArray(json) && json.length >= minArrays) {
698
839
  let entry;
699
840
  for (entry of json)
700
841
  if (!Geometry.isNumberArray(entry, minEntries))
@@ -703,36 +844,53 @@ class Geometry {
703
844
  }
704
845
  return false;
705
846
  }
706
- /** return the number of steps to take so that numSteps * stepSize >= total.
707
- * minCount is returned for both (a) setSize 0 or less and (b) stepSize > total.
708
- * A small tolerance is applied for almost
709
- */
847
+ /**
848
+ * Return the number of steps to take so that `numSteps * stepSize >= total`.
849
+ * * `minCount` is returned in the following 3 cases:
850
+ * * (a) `stepSize <= 0`
851
+ * * (b) `stepSize >= total`
852
+ * * (b) `numSteps < minCount`
853
+ * * `maxCount` is returned if `numSteps > maxCount`.
854
+ */
710
855
  static stepCount(stepSize, total, minCount = 1, maxCount = 101) {
711
856
  if (stepSize <= 0)
712
857
  return minCount;
713
858
  total = Math.abs(total);
714
859
  if (stepSize >= total)
715
860
  return minCount;
716
- const stepCount = Math.floor((total + 0.999999 * stepSize) / stepSize);
717
- if (stepCount < minCount)
861
+ /**
862
+ * 0.999999 is multiplied so we return the same "numSteps" if
863
+ * stepSize*(numSteps-1) < total <= stepSize*numSteps.
864
+ * For example, if "stepSize = 2" then we return "numSteps = 5" if 8 < total <= 10.
865
+ */
866
+ const numSteps = Math.floor((total + 0.999999 * stepSize) / stepSize);
867
+ if (numSteps < minCount)
718
868
  return minCount;
719
- if (stepCount > maxCount)
869
+ if (numSteps > maxCount)
720
870
  return maxCount;
721
- return stepCount;
871
+ return numSteps;
722
872
  }
723
- /** Test if x is in simple 0..1 interval. But optionally skip the test. (this odd behavior is very convenient for code that sometimes does not do the filtering.)
873
+ /**
874
+ * Test if `x` is in the interval [0,1] (but skip the test if `apply01 = false`).
875
+ * * This odd behavior is very convenient for code that sometimes does not do the filtering.
724
876
  * @param x value to test.
725
- * @param apply01 if false, accept all x.
877
+ * @param apply01 if false, return `true` for all values of `x`.
726
878
  */
727
- static isIn01(x, apply01 = true) { return apply01 ? x >= 0.0 && x <= 1.0 : true; }
728
- /** Test if x is in simple 0..1 interval. But optionally skip the test. (this odd behavior is very convenient for code that sometimes does not do the filtering.)
879
+ static isIn01(x, apply01 = true) {
880
+ return apply01 ? x >= 0.0 && x <= 1.0 : true;
881
+ }
882
+ /**
883
+ * Test if `x` is in the interval [0,1] for a given positive `tolerance`.
884
+ * * Make sure to pass a positive `tolerance` because there is no check for that in the code.
729
885
  * @param x value to test.
730
- * @param apply01 if false, accept all x.
886
+ * @param tolerance the tolerance.
731
887
  */
732
- static isIn01WithTolerance(x, tolerance) { return x + tolerance >= 0.0 && x - tolerance <= 1.0; }
888
+ static isIn01WithTolerance(x, tolerance) {
889
+ return x + tolerance >= 0.0 && x - tolerance <= 1.0;
890
+ }
733
891
  /**
734
- * restrict x so it is in the interval `[a,b]`, allowing a,b to be in either order.
735
- * @param x
892
+ * Restrict x so it is in the interval `[a,b]` (allowing `a` and `b` to be in either order).
893
+ * @param x value to restrict
736
894
  * @param a (usually the lower) interval limit
737
895
  * @param b (usually the upper) interval limit
738
896
  */
@@ -744,7 +902,7 @@ class Geometry {
744
902
  return b;
745
903
  return x;
746
904
  }
747
- // reversed interval ....
905
+ // reversed interval
748
906
  if (x < b)
749
907
  return b;
750
908
  if (x > a)
@@ -753,12 +911,15 @@ class Geometry {
753
911
  }
754
912
  /**
755
913
  * Case-insensitive string comparison.
756
- * * Return true if the toUpperCase values match.
914
+ * * Return `true` if the `toUpperCase` values of `string1` and `string2` match.
757
915
  */
758
916
  static equalStringNoCase(string1, string2) {
759
917
  return string1.toUpperCase() === string2.toUpperCase();
760
918
  }
761
- /** test for EXACT match of number arrays. */
919
+ /**
920
+ * Test for exact match of two number arrays.
921
+ * Returns `true` if both arrays have the same length and entries, or if both arrays are empty or `undefined`.
922
+ */
762
923
  static exactEqualNumberArrays(a, b) {
763
924
  if (Array.isArray(a) && a.length === 0)
764
925
  a = undefined;
@@ -776,7 +937,10 @@ class Geometry {
776
937
  }
777
938
  return false;
778
939
  }
779
- /** test for match of XYZ arrays. */
940
+ /**
941
+ * Test for match of two arrays of type `T`.
942
+ * Returns `true` if both arrays have the same length and have the same entries (or both are empty arrays).
943
+ */
780
944
  static almostEqualArrays(a, b, testFunction) {
781
945
  if (Array.isArray(a) && a.length === 0)
782
946
  a = undefined;
@@ -795,7 +959,10 @@ class Geometry {
795
959
  }
796
960
  return false;
797
961
  }
798
- /** test for match of typed arrays (e.g. Float64Array). */
962
+ /**
963
+ * Test for match of two arrays of type number or Float64Array.
964
+ * Returns `true` if both arrays have the same length and have the same entries (or both are empty arrays).
965
+ */
799
966
  static almostEqualNumberArrays(a, b, testFunction) {
800
967
  if (Array.isArray(a) && a.length === 0)
801
968
  a = undefined;
@@ -815,15 +982,12 @@ class Geometry {
815
982
  return false;
816
983
  }
817
984
  /**
818
- * Return
819
- * * true if both values are defined and equal (with ===).
820
- * * false if both defined by not equal
821
- * * return (option arg) resultIfBothUndefined when both are undefined.
822
- * * return false if one is defined and the other undefined
985
+ * Test for match of two values of type `T`.
823
986
  * @param a first value
824
987
  * @param b second value
825
- * @param resultIfBothUndefined return value when both are undefined.
826
- * @returns
988
+ * @param resultIfBothUndefined returned value when both are `undefined`
989
+ * @returns `true` if both values are defined and equal (with ===) and `false` if both values are defined
990
+ * but not equal or if one is defined and the other undefined.
827
991
  */
828
992
  static areEqualAllowUndefined(a, b, resultIfBothUndefined = true) {
829
993
  if (a === undefined && b === undefined)
@@ -832,46 +996,52 @@ class Geometry {
832
996
  return a === b;
833
997
  return false;
834
998
  }
835
- /** clone an array whose members have a clone method.
836
- * * undefined return from clone is forced into the output array.
837
- */
838
- static cloneMembers(a) {
839
- if (a === undefined)
999
+ /**
1000
+ * Clone an array whose members have type `T`, which implements the clone method.
1001
+ * * If the clone method returns `undefined`, then `undefined` is forced into the cloned array.
1002
+ */
1003
+ static cloneMembers(array) {
1004
+ if (array === undefined)
840
1005
  return undefined;
841
- const b = [];
842
- for (const p of a) {
843
- b.push(p.clone());
1006
+ const clonedArray = [];
1007
+ for (const element of array) {
1008
+ clonedArray.push(element.clone());
844
1009
  }
845
- return b;
1010
+ return clonedArray;
846
1011
  }
847
1012
  }
848
- /** Tolerance for small distances in metric coordinates */
1013
+ /** Tolerance for small distances in metric coordinates. */
849
1014
  Geometry.smallMetricDistance = 1.0e-6;
850
- /** Square of `smallMetricTolerance` */
1015
+ /** Square of `smallMetricDistance`. */
851
1016
  Geometry.smallMetricDistanceSquared = 1.0e-12;
852
- /** tolerance for small angle measured in radians. */
1017
+ /** Tolerance for small angle measured in radians. */
853
1018
  Geometry.smallAngleRadians = 1.0e-12;
854
- /** square of `smallAngleRadians` */
1019
+ /** Square of `smallAngleRadians`. */
855
1020
  Geometry.smallAngleRadiansSquared = 1.0e-24;
856
- /** tolerance for small angle measured in degrees. */
1021
+ /** Tolerance for small angle measured in degrees. */
857
1022
  Geometry.smallAngleDegrees = 5.7e-11;
858
- /** tolerance for small angle measured in arc-seconds. */
1023
+ /** Tolerance for small angle measured in arc-seconds. */
859
1024
  Geometry.smallAngleSeconds = 2e-7;
860
- /** numeric value that may be considered huge for a ratio of numbers.
861
- * * Note that the "allowed" result value is vastly larger than 1.
1025
+ /** Numeric value that may be considered zero for fractions between 0 and 1. */
1026
+ Geometry.smallFraction = 1.0e-10;
1027
+ /** Radians value for full circle 2PI radians minus `smallAngleRadians`. */
1028
+ Geometry.fullCircleRadiansMinusSmallAngle = 2.0 * Math.PI - Geometry.smallAngleRadians;
1029
+ /**
1030
+ * Numeric value that may be considered large for a ratio of numbers.
1031
+ * * Note that the allowed result value is vastly larger than 1.
862
1032
  */
863
1033
  Geometry.largeFractionResult = 1.0e10;
864
- /** numeric value that may be considered zero for fractions between 0 and 1. */
865
- Geometry.smallFraction = 1.0e-10;
866
- /** numeric value that may considered huge for numbers expected to be coordinates.
1034
+ /**
1035
+ * Numeric value that may considered large for numbers expected to be coordinates.
867
1036
  * * This allows larger results than `largeFractionResult`.
868
1037
  */
869
1038
  Geometry.largeCoordinateResult = 1.0e13;
870
- /** numeric value that may considered infinite for metric coordinates.
871
- * * This coordinate should be used only as a placeholder indicating "at infinity" -- computing actual points at this coordinate invites numerical problems.
1039
+ /**
1040
+ * Numeric value that may considered infinite for metric coordinates.
1041
+ * @deprecated in 4.x. Use `largeCoordinateResult`.
1042
+ * * This coordinate should be used only as a placeholder indicating "at infinity" -- computing actual
1043
+ * points at this coordinate invites numerical problems.
872
1044
  */
873
1045
  Geometry.hugeCoordinate = 1.0e12;
874
- /** Radians value for full circle 2PI radians minus `smallAngleRadians` */
875
- Geometry.fullCircleRadiansMinusSmallAngle = 2.0 * Math.PI - 1.0e-12; // smallAngleRadians less than 360degrees
876
1046
  export { Geometry };
877
1047
  //# sourceMappingURL=Geometry.js.map