@itwin/core-geometry 5.0.0-dev.62 → 5.0.0-dev.64

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 (55) hide show
  1. package/lib/cjs/bspline/BSpline1dNd.d.ts +90 -54
  2. package/lib/cjs/bspline/BSpline1dNd.d.ts.map +1 -1
  3. package/lib/cjs/bspline/BSpline1dNd.js +134 -99
  4. package/lib/cjs/bspline/BSpline1dNd.js.map +1 -1
  5. package/lib/cjs/bspline/BSplineCurve.d.ts +193 -155
  6. package/lib/cjs/bspline/BSplineCurve.d.ts.map +1 -1
  7. package/lib/cjs/bspline/BSplineCurve.js +245 -181
  8. package/lib/cjs/bspline/BSplineCurve.js.map +1 -1
  9. package/lib/cjs/bspline/BezierCurve3d.d.ts +3 -1
  10. package/lib/cjs/bspline/BezierCurve3d.d.ts.map +1 -1
  11. package/lib/cjs/bspline/BezierCurve3d.js +3 -5
  12. package/lib/cjs/bspline/BezierCurve3d.js.map +1 -1
  13. package/lib/cjs/bspline/KnotVector.d.ts +74 -54
  14. package/lib/cjs/bspline/KnotVector.d.ts.map +1 -1
  15. package/lib/cjs/bspline/KnotVector.js +127 -80
  16. package/lib/cjs/bspline/KnotVector.js.map +1 -1
  17. package/lib/cjs/curve/Arc3d.d.ts +2 -0
  18. package/lib/cjs/curve/Arc3d.d.ts.map +1 -1
  19. package/lib/cjs/curve/Arc3d.js +2 -0
  20. package/lib/cjs/curve/Arc3d.js.map +1 -1
  21. package/lib/cjs/geometry3d/PointHelpers.d.ts +3 -3
  22. package/lib/cjs/geometry3d/PointHelpers.js +3 -3
  23. package/lib/cjs/geometry3d/PointHelpers.js.map +1 -1
  24. package/lib/cjs/geometry3d/Ray3d.d.ts +2 -2
  25. package/lib/cjs/geometry3d/Ray3d.d.ts.map +1 -1
  26. package/lib/cjs/geometry3d/Ray3d.js +8 -11
  27. package/lib/cjs/geometry3d/Ray3d.js.map +1 -1
  28. package/lib/esm/bspline/BSpline1dNd.d.ts +90 -54
  29. package/lib/esm/bspline/BSpline1dNd.d.ts.map +1 -1
  30. package/lib/esm/bspline/BSpline1dNd.js +134 -99
  31. package/lib/esm/bspline/BSpline1dNd.js.map +1 -1
  32. package/lib/esm/bspline/BSplineCurve.d.ts +193 -155
  33. package/lib/esm/bspline/BSplineCurve.d.ts.map +1 -1
  34. package/lib/esm/bspline/BSplineCurve.js +245 -181
  35. package/lib/esm/bspline/BSplineCurve.js.map +1 -1
  36. package/lib/esm/bspline/BezierCurve3d.d.ts +3 -1
  37. package/lib/esm/bspline/BezierCurve3d.d.ts.map +1 -1
  38. package/lib/esm/bspline/BezierCurve3d.js +3 -5
  39. package/lib/esm/bspline/BezierCurve3d.js.map +1 -1
  40. package/lib/esm/bspline/KnotVector.d.ts +74 -54
  41. package/lib/esm/bspline/KnotVector.d.ts.map +1 -1
  42. package/lib/esm/bspline/KnotVector.js +127 -80
  43. package/lib/esm/bspline/KnotVector.js.map +1 -1
  44. package/lib/esm/curve/Arc3d.d.ts +2 -0
  45. package/lib/esm/curve/Arc3d.d.ts.map +1 -1
  46. package/lib/esm/curve/Arc3d.js +2 -0
  47. package/lib/esm/curve/Arc3d.js.map +1 -1
  48. package/lib/esm/geometry3d/PointHelpers.d.ts +3 -3
  49. package/lib/esm/geometry3d/PointHelpers.js +3 -3
  50. package/lib/esm/geometry3d/PointHelpers.js.map +1 -1
  51. package/lib/esm/geometry3d/Ray3d.d.ts +2 -2
  52. package/lib/esm/geometry3d/Ray3d.d.ts.map +1 -1
  53. package/lib/esm/geometry3d/Ray3d.js +8 -11
  54. package/lib/esm/geometry3d/Ray3d.js.map +1 -1
  55. package/package.json +3 -3
@@ -28,83 +28,117 @@ import { BSplineCurveOps } from "./BSplineCurveOps";
28
28
  import { BSplineWrapMode, KnotVector } from "./KnotVector";
29
29
  /**
30
30
  * Base class for BSplineCurve3d and BSplineCurve3dH.
31
- * * A bspline curve consists of a set of knots and a set of poles.
32
- * * The bspline curve is a function of the independent "knot axis" variable
33
- * * The curve "follows" the poles loosely.
34
- * * The is a set of polynomial spans.
31
+ * * A B-spline curve consists of an array of `knots`, an array of `poles`, and a `degree`.
32
+ * * The knot array is a non-decreasing sequence of numbers. It is also called a "knot vector".
33
+ * * The curve is a parametric function whose domain is a sub-range of its knots.
34
+ * * The API sometimes refers to a domain parameter `u` as a "knot", even if `u` is not actually an entry in the
35
+ * knot array.
36
+ * * The curve loosely "follows" the line string formed by the poles, aka the "control polygon".
37
+ * * The curve is a chain of polynomial segments, aka "spans" or "fragments". B-spline theory identifies these as
38
+ * Bezier curves.
35
39
  * * The polynomial spans all have same `degree`.
36
- * * Within each span, the polynomial of that `degree` is controlled by `order = degree + 1` contiguous points called poles.
37
- * * The is a strict relationship between knot and poles counts: `numPoles + order = numKnots + 2'
38
- * * The number of spans is `numSpan = numPoles - degree`
39
- * * For a given `spanIndex`:
40
- * * The `order` poles begin at index `spanIndex`.
41
- * * The `2*order` knots begin as span index
42
- * * The knot interval for this span is from `knot[degree+span-1] to knot[degree+span]`
43
- * * The active part of the knot axis is `knot[degree-1] < knot < knot[degree-1 + numSpan]` i.e. `knot[degree-1] < knot < knot[numPoles]
40
+ * * Each span is controlled by `order = degree + 1` contiguous points in the pole array.
41
+ * * There is a strict relationship between knot and poles counts: `numPoles + order = numKnots + 2'.
42
+ * * The number of spans is `numSpan = numPoles - degree`.
43
+ * * For a span with index `spanIndex`:
44
+ * * The `order` relevant poles begin at pole index `spanIndex`.
45
+ * * The `2*degree` relevant knots begin at knot index `spanIndex`.
46
+ * * The span domain is the knot range `[knot[spanIndex+degree-1], knot[spanIndex+degree]]`.
47
+ * * The curve domain is the knot range `[knot[degree-1], knot[numSpan+degree-1]]`, or equivalently
48
+ * `[knot[degree-1], knot[numPoles-1]]`. The API refers to this domain as the "active knot interval" of the curve.
44
49
  *
45
- * Nearly all bsplines are "clamped ".
46
- * * Clamping make the curve pass through its first and last poles, with tangents directed along the first and last edges of the control polygon.
47
- * * The knots for a clamped bspline have `degree` copies of the lowest knot value and `degree` copies of the highest knot value.
48
- * * For instance, the knot vector `[0,0,0,1,2,3,3,3]
49
- * * can be evaluated from `0<=knot<=3`
50
- * * has 3 spans: 0 to 1, 1 to 2, 2 to 3
50
+ * Nearly all B-spline curves are "clamped".
51
+ * * This means that in the `knots` array, the first `degree` knots are equal, and the last `degree` knots are equal.
52
+ * We say the smallest knot and the largest knot have multiplicity `degree`.
53
+ * * Clamping make the curve pass through its first and last poles, with tangents directed along the first and
54
+ * last edges of the control polygon.
55
+ * * For instance, a cubic B-spline curve with knot vector `[0,0,0,1,2,3,3,3]`
56
+ * * can be evaluated at parameter values in the range `[0, 3]`
57
+ * * has 3 spans, with domains `[0, 1]`, `[1, 2]`, and `[2, 3]`
51
58
  * * has 6 poles
52
59
  * * passes through its first and last poles.
53
- * * `create` methods may allow classic convention that has an extra knot at the beginning and end of the knot vector.
54
- * * The extra knots (first and last) were never referenced by the bspline recurrence relations.
55
- * * When the `create` methods recognize the classic setup (`numPoles + order = numKnots`), the extra knot is not saved with the BSplineCurve3dBase knots.
60
+ * * The `create` methods may allow the classic convention that has an extra knot at the beginning and end of the
61
+ * knot vector.
62
+ * * These two extra knots are not actually needed to define the B-spline curve.
63
+ * * When the `create` methods recognize the classic setup (`numPoles + order = numKnots`), the extra knots are
64
+ * not saved with the BSplineCurve3dBase knots.
56
65
  *
57
- * * The weighted variant has the problem that CurvePrimitive 3d typing does not allow undefined result where Point4d has zero weight.
58
- * * The convention for these is to return 000 in such places.
66
+ * * The weighted variant [[BSplineCurve3dH]] has the problem that `CurvePrimitive` 3D typing does not allow the
67
+ * undefined result where a homogeneous pole has zero weight; the convention in this case is to return 000.
59
68
  *
60
69
  * * Note the class relationships:
61
- * * BSpline1dNd knows the bspline recurrence relations for control points (poles) with no physical meaning.
62
- * * BsplineCurve3dBase owns a protected BSpline1dNd
63
- * * BsplineCurve3dBase is derived from CurvePrimitive, which creates obligation to act as a 3D curve, such as
64
- * * evaluate fraction to point and derivatives wrt fraction
65
- * * compute intersection with plane
66
- * * BSplineCurve3d and BSplineCurve3dH have variant logic driven by whether or not there are "weights" on the poles.
67
- * * For `BSplineCurve3d`, the xyz value of pole calculations are "final" values for 3d evaluation
70
+ * * [[BSpline1dNd]] knows the definitional B-spline recurrence relation with no physical interpretation for the poles.
71
+ * * BsplineCurve3dBase owns a protected BSpline1dNd.
72
+ * * `BsplineCurve3dBase` is derived from [[CurvePrimitive]], which creates obligation to act as a 3D curve, e.g.,
73
+ * * evaluate fraction to point and derivatives wrt fraction.
74
+ * * compute intersection with plane.
75
+ * * [[BSplineCurve3d]] and [[BSplineCurve3dH]] have variant logic driven by whether or not there are "weights" on the poles.
76
+ * * For `BSplineCurve3d`, the xyz value of pole calculations are "final" values for 3d evaluation.
68
77
  * * For `BSplineCurve3dH`, various `BSpline1dNd` results with xyzw have to be normalized back to xyz.
69
78
  *
70
79
  * * These classes do not support "periodic" variants.
71
- * * Periodic curves need to have certain leading knots and poles replicated at the end
80
+ * * Periodic curves historically have carried a flag (e.g., "closed") indicating that certain un-stored
81
+ * leading/trailing knots and poles are understood to wrap around periodically.
82
+ * * Instead, these classes carry no such flag. They represent such curves with explicitly wrapped knots/poles.
83
+ *
84
+ * * Visualization can be found at https://www.itwinjs.org/sandbox/SaeedTorabi/BSpline/
72
85
  * @public
73
86
  */
74
87
  export class BSplineCurve3dBase extends CurvePrimitive {
75
- /** String name for schema properties */
88
+ /** String name for schema properties. */
76
89
  curvePrimitiveType = "bsplineCurve";
77
- /** The underlying blocked-pole spline, with simple x,y,z poles */
90
+ /** The underlying blocked-pole spline, with simple x,y,z poles. */
78
91
  _bcurve;
79
92
  _definitionData;
80
- set definitionData(data) { this._definitionData = data; }
81
- get definitionData() { return this._definitionData; }
93
+ set definitionData(data) {
94
+ this._definitionData = data;
95
+ }
96
+ get definitionData() {
97
+ return this._definitionData;
98
+ }
82
99
  constructor(poleDimension, numPoles, order, knots) {
83
100
  super();
84
101
  this._bcurve = BSpline1dNd.create(numPoles, poleDimension, order, knots);
85
102
  }
86
- /** Return the degree (one less than the order) of the curve */
87
- get degree() { return this._bcurve.degree; }
88
- /** Return the order (one more than degree) of the curve */
89
- get order() { return this._bcurve.order; }
90
- /** Return the number of bezier spans in the curve. Note that this number includes the number of null spans at repeated knows */
91
- get numSpan() { return this._bcurve.numSpan; }
92
- /** Return the number of poles */
93
- get numPoles() { return this._bcurve.numPoles; }
94
- /** Return live reference to the packed control point coordinates of the curve. */
95
- get polesRef() { return this._bcurve.packedData; }
96
- /** Return live reference to the knots of the curve. */
97
- get knotsRef() { return this._bcurve.knots.knots; }
98
- /** Number of components per pole.
99
- * * 3 for conventional (x,y,z) curve
100
- * * 4 for weighted (wx,wy,wz,w) curve
103
+ /** Return the degree (one less than the order) of the curve. */
104
+ get degree() {
105
+ return this._bcurve.degree;
106
+ }
107
+ /** Return the order (one more than degree) of the curve. */
108
+ get order() {
109
+ return this._bcurve.order;
110
+ }
111
+ /**
112
+ * Return the number of Bezier spans in the curve. Note that this number includes the number of null
113
+ * spans at repeated knows.
101
114
  */
102
- get poleDimension() { return this._bcurve.poleLength; }
115
+ get numSpan() {
116
+ return this._bcurve.numSpan;
117
+ }
118
+ /** Return the number of poles. */
119
+ get numPoles() {
120
+ return this._bcurve.numPoles;
121
+ }
122
+ /** Return live reference to the poles of the curve. */
123
+ get polesRef() {
124
+ return this._bcurve.packedData;
125
+ }
126
+ /** Return live reference to the knots of the curve. */
127
+ get knotsRef() {
128
+ return this._bcurve.knots.knots;
129
+ }
103
130
  /**
104
- * return a simple array form of the knots. optionally replicate the first and last
105
- * in classic over-clamped manner
131
+ * Number of components per pole, e.g.,
132
+ * * 3 for conventional (x,y,z) curve.
133
+ * * 4 for weighted (wx,wy,wz,w) curve.
106
134
  */
107
- copyKnots(includeExtraEndKnot) { return this._bcurve.knots.copyKnots(includeExtraEndKnot); }
135
+ get poleDimension() {
136
+ return this._bcurve.poleLength;
137
+ }
138
+ /** Return a simple array form of the knots. Optionally replicate the first and last in classic over-clamped manner. */
139
+ copyKnots(includeExtraEndKnot) {
140
+ return this._bcurve.knots.copyKnots(includeExtraEndKnot);
141
+ }
108
142
  /** Get the flag indicating the curve might be suitable for having wrapped "closed" interpretation. */
109
143
  getWrappable() {
110
144
  return this._bcurve.knots.wrappable;
@@ -114,7 +148,7 @@ export class BSplineCurve3dBase extends CurvePrimitive {
114
148
  this._bcurve.knots.wrappable = value;
115
149
  }
116
150
  /**
117
- * Test knots and control points to determine if it is possible to close (aka "wrap") the curve.
151
+ * Test knots and poles to determine if it is possible to close (aka "wrap") the curve.
118
152
  * @returns the manner in which it is possible to close the curve. See `BSplineWrapMode` for particulars of each mode.
119
153
  */
120
154
  get isClosableCurve() {
@@ -127,25 +161,18 @@ export class BSplineCurve3dBase extends CurvePrimitive {
127
161
  return BSplineWrapMode.None;
128
162
  return mode;
129
163
  }
130
- /** Evaluate the curve point at `fraction` */
164
+ /** Evaluate the curve point at the given fractional parameter. */
131
165
  fractionToPoint(fraction, result) {
132
166
  return this.knotToPoint(this._bcurve.knots.fractionToKnot(fraction), result);
133
167
  }
134
- /** Construct a ray with
135
- * * origin at the fractional position along the arc
136
- * * direction is the first derivative, i.e. tangent along the curve
137
- */
168
+ /** Evaluate the curve and derivative at the given fractional parameter. */
138
169
  fractionToPointAndDerivative(fraction, result) {
139
170
  const knot = this._bcurve.knots.fractionToKnot(fraction);
140
171
  result = this.knotToPointAndDerivative(knot, result);
141
172
  result.direction.scaleInPlace(this._bcurve.knots.knotLength01);
142
173
  return result;
143
174
  }
144
- /** Construct a plane with
145
- * * origin at the fractional position along the arc
146
- * * x axis is the first derivative, i.e. tangent along the curve
147
- * * y axis is the second derivative
148
- */
175
+ /** Evaluate the curve and two derivatives at the given fractional parameter. */
149
176
  fractionToPointAnd2Derivatives(fraction, result) {
150
177
  const knot = this._bcurve.knots.fractionToKnot(fraction);
151
178
  result = this.knotToPointAnd2Derivatives(knot, result);
@@ -154,22 +181,23 @@ export class BSplineCurve3dBase extends CurvePrimitive {
154
181
  result.vectorV.scaleInPlace(a * a);
155
182
  return result;
156
183
  }
184
+ /** Return the start point of the curve. */
185
+ startPoint() {
186
+ return this.evaluatePointInSpan(0, 0.0);
187
+ }
188
+ /** Return the end point of the curve. */
189
+ endPoint() {
190
+ return this.evaluatePointInSpan(this.numSpan - 1, 1.0);
191
+ }
157
192
  /**
158
- * Return the start point of the curve.
159
- */
160
- startPoint() { return this.evaluatePointInSpan(0, 0.0); }
161
- /**
162
- * Return the end point of the curve
163
- */
164
- endPoint() { return this.evaluatePointInSpan(this.numSpan - 1, 1.0); }
165
- /** Reverse the curve in place.
166
- * * Poles are reversed
167
- * * knot values are mirrored around the middle of the
168
- */
169
- reverseInPlace() { this._bcurve.reverseInPlace(); }
170
- /**
171
- * Return an array with this curve's bezier fragments.
193
+ * Reverse the curve in place.
194
+ * * Poles are reversed.
195
+ * * Knot values are mirrored around the middle of the knot array.
172
196
  */
197
+ reverseInPlace() {
198
+ this._bcurve.reverseInPlace();
199
+ }
200
+ /** Return an array with this curve's Bezier fragments. */
173
201
  collectBezierSpans(prefer3dH) {
174
202
  const result = [];
175
203
  const numSpans = this.numSpan;
@@ -188,17 +216,18 @@ export class BSplineCurve3dBase extends CurvePrimitive {
188
216
  return poleIndex * this._bcurve.poleLength;
189
217
  return undefined;
190
218
  }
191
- /** Search for the curve point that is closest to the spacePoint.
192
- *
219
+ /**
220
+ * Search for the curve point that is closest to the spacePoint.
193
221
  * * If the space point is exactly on the curve, this is the reverse of fractionToPoint.
194
- * * Since CurvePrimitive should always have start and end available as candidate points, this method should always succeed
195
- * @param spacePoint point in space
222
+ * * Since CurvePrimitive should always have start and end available as candidate points, this method should always
223
+ * succeed.
224
+ * @param spacePoint point in space.
196
225
  * @param _extend ignored (pass false). A BSplineCurve3dBase cannot be extended.
197
226
  * @param result optional pre-allocated detail to populate and return.
198
227
  * @returns details of the closest point.
199
228
  */
200
229
  closestPoint(spacePoint, _extend, result) {
201
- // seed at start point -- final point comes with final bezier perpendicular step.
230
+ // seed at start point; final point comes with final bezier perpendicular step
202
231
  const point = this.fractionToPoint(0);
203
232
  result = CurveLocationDetail.createCurveFractionPointDistance(this, 0.0, point, point.distance(spacePoint), result);
204
233
  let span;
@@ -207,9 +236,9 @@ export class BSplineCurve3dBase extends CurvePrimitive {
207
236
  if (this._bcurve.knots.isIndexOfRealSpan(i)) {
208
237
  span = this.getSaturatedBezierSpan3dOr3dH(i, true, span);
209
238
  if (span) {
210
- // umm ... if the bspline is discontinuous, both ends should be tested. Ignore that possibility ...
239
+ // if the B-spline is discontinuous, both ends should be tested; ignore that possibility
211
240
  if (span.updateClosestPointByTruePerpendicular(spacePoint, result, false, true)) {
212
- // the detail records the span bezier -- promote it to the parent curve . ..
241
+ // the detail records the span bezier; promote it to the parent curve
213
242
  result.curve = this;
214
243
  result.fraction = span.fractionToParentFraction(result.fraction);
215
244
  }
@@ -220,13 +249,14 @@ export class BSplineCurve3dBase extends CurvePrimitive {
220
249
  }
221
250
  /** Return a transformed deep clone. */
222
251
  cloneTransformed(transform) {
223
- const curve1 = this.clone();
224
- curve1.tryTransformInPlace(transform);
225
- return curve1;
252
+ const curve = this.clone();
253
+ curve.tryTransformInPlace(transform);
254
+ return curve;
226
255
  }
227
- /** Return a curve primitive which is a portion of this curve.
228
- * @param fractionA [in] start fraction
229
- * @param fractionB [in] end fraction
256
+ /**
257
+ * Return a curve primitive which is a portion of this curve.
258
+ * @param fractionA start fraction.
259
+ * @param fractionB end fraction.
230
260
  */
231
261
  clonePartialCurve(fractionA, fractionB) {
232
262
  const clone = this.clone();
@@ -237,11 +267,8 @@ export class BSplineCurve3dBase extends CurvePrimitive {
237
267
  clone._bcurve.addKnot(knotB, clone.degree);
238
268
  if (origNumKnots === clone._bcurve.knots.knots.length)
239
269
  return clone; // full curve
240
- if (knotA > knotB) {
241
- const tmp = knotA;
242
- knotA = knotB;
243
- knotB = tmp;
244
- }
270
+ if (knotA > knotB)
271
+ [knotA, knotB] = [knotB, knotA];
245
272
  // choose first/last knot and pole such that knotA/knotB has degree multiplicity in the new knot sequence
246
273
  const iStartKnot = clone._bcurve.knots.knotToLeftKnotIndex(knotA) - clone.degree + 1;
247
274
  const iStartPole = iStartKnot * clone._bcurve.poleLength;
@@ -251,15 +278,17 @@ export class BSplineCurve3dBase extends CurvePrimitive {
251
278
  iLastKnotLeftMultiple = iLastKnot + 1;
252
279
  const iEndPole = (iLastKnotLeftMultiple + 1) * clone._bcurve.poleLength; // one past last pole
253
280
  const iEndKnot = iLastKnotLeftMultiple + clone.degree; // one past last knot
254
- // trim the arrays (leave knots unnormalized!)
281
+ // trim the arrays (leave knots unnormalized)
255
282
  clone._bcurve.knots.setKnotsCapture(clone._bcurve.knots.knots.slice(iStartKnot, iEndKnot));
256
283
  clone._bcurve.packedData = clone._bcurve.packedData.slice(iStartPole, iEndPole);
257
284
  clone.setWrappable(BSplineWrapMode.None); // always open
258
285
  return clone;
259
286
  }
260
- /** Implement `CurvePrimitive.appendPlaneIntersections`
261
- * @param plane A plane (e.g. specific type Plane3dByOriginAndUnitNormal or Point4d)
262
- * @param result growing array of plane intersections
287
+ /**
288
+ * Implement `CurvePrimitive.appendPlaneIntersections` to compute intersections of the curve with a plane.
289
+ * @param plane the plane with which to intersect the curve. Concrete types include [[Plane3dByOriginAndUnitNormal]],
290
+ * [[Point4d]], etc.
291
+ * @param result growing array of plane intersections.
263
292
  * @return number of intersections appended to the array.
264
293
  */
265
294
  appendPlaneIntersectionPoints(plane, result) {
@@ -270,34 +299,32 @@ export class BSplineCurve3dBase extends CurvePrimitive {
270
299
  const point4d = Point4d.create();
271
300
  // compute all pole altitudes from the plane
272
301
  const minMax = Range1d.createNull();
273
- // Put the altitudes of all the bspline poles in one array.
302
+ // put the altitudes of all the B-spline poles in one array
274
303
  for (let i = 0; i < numPole; i++) {
275
304
  allCoffs[i] = plane.weightedAltitude(this.getPolePoint4d(i, point4d));
276
305
  minMax.extendX(allCoffs[i]);
277
306
  }
278
- // A univariate bspline through the altitude poles gives altitude as function of the bspline knot.
307
+ // A univariate B-spline through the altitude poles gives altitude as function of the B-spline knot.
279
308
  // The (bspline) altitude function for each span is `order` consecutive altitudes.
280
309
  // If those altitudes bracket zero, the span may potentially have a crossing.
281
- // When that occurs,
282
310
  let univariateBezier;
283
311
  let numFound = 0;
284
312
  let previousFraction = -1000.0;
285
313
  if (minMax.containsX(0.0)) {
286
314
  for (let spanIndex = 0; spanIndex < numSpan; spanIndex++) {
287
- if (this._bcurve.knots.isIndexOfRealSpan(spanIndex)) { // ignore trivial knot intervals.
315
+ if (this._bcurve.knots.isIndexOfRealSpan(spanIndex)) { // ignore trivial knot intervals
288
316
  // outer range test ...
289
317
  minMax.setNull();
290
318
  minMax.extendArraySubset(allCoffs, spanIndex, order);
291
319
  if (minMax.containsX(0.0)) {
292
- // pack the bspline support into a univariate bezier ...
320
+ // pack the B-spline support into a univariate bezier
293
321
  univariateBezier = UnivariateBezier.createArraySubset(allCoffs, spanIndex, order, univariateBezier);
294
322
  // saturate and solve the bezier
295
323
  Bezier1dNd.saturate1dInPlace(univariateBezier.coffs, this._bcurve.knots, spanIndex);
296
324
  const roots = univariateBezier.roots(0.0, true);
297
325
  if (roots) {
298
326
  for (const spanFraction of roots) {
299
- // promote each local bezier fraction to global fraction.
300
- // save the curve evaluation at that fraction.
327
+ // promote each local bezier fraction to global fraction and save the curve evaluation at that fraction
301
328
  numFound++;
302
329
  const fraction = this._bcurve.knots.spanFractionToFraction(spanIndex, spanFraction);
303
330
  if (!Geometry.isAlmostEqualNumber(fraction, previousFraction)) {
@@ -316,9 +343,7 @@ export class BSplineCurve3dBase extends CurvePrimitive {
316
343
  }
317
344
  /**
318
345
  * Construct an offset of the instance curve as viewed in the xy-plane (ignoring z).
319
- * * No attempt is made to join the offsets of smaller constituent primitives. To construct a fully joined offset
320
- * for an aggregate instance (e.g., LineString3d, CurveChainWithDistanceIndex), use RegionOps.constructCurveXYOffset() instead.
321
- * @param offsetDistanceOrOptions offset distance (positive to left of the instance curve), or options object
346
+ * @param offsetDistanceOrOptions offset distance (positive to left of the instance curve), or options object.
322
347
  */
323
348
  constructOffsetXY(offsetDistanceOrOptions) {
324
349
  const options = OffsetOptions.create(offsetDistanceOrOptions);
@@ -326,18 +351,21 @@ export class BSplineCurve3dBase extends CurvePrimitive {
326
351
  this.emitStrokableParts(handler, options.strokeOptions);
327
352
  return handler.claimResult();
328
353
  }
329
- /** Project instance geometry (via dispatch) onto the given ray, and return the extreme fractional parameters of projection.
354
+ /**
355
+ * Project instance geometry (via dispatch) onto the given ray, and return the extreme fractional parameters
356
+ * of projection.
330
357
  * @param ray ray onto which the instance is projected. A `Vector3d` is treated as a `Ray3d` with zero origin.
331
- * @param lowHigh optional receiver for output
332
- * @returns range of fractional projection parameters onto the ray, where 0.0 is start of the ray and 1.0 is the end of the ray.
358
+ * @param lowHigh optional receiver for output.
359
+ * @returns range of fractional projection parameters onto the ray, where 0.0 is start of the ray and 1.0 is the
360
+ * end of the ray.
333
361
  */
334
362
  projectedParameterRange(ray, lowHigh) {
335
363
  return PlaneAltitudeRangeContext.findExtremeFractionsAlongDirection(this, ray, lowHigh);
336
364
  }
337
365
  }
338
366
  /**
339
- * A BSplineCurve3d is a bspline curve whose poles are Point3d.
340
- * See BSplineCurve3dBase for description of knots, order, degree.
367
+ * A BSplineCurve3d is a B-spline curve whose poles are Point3d.
368
+ * See BSplineCurve3dBase for description of knots, order, degree, and poles.
341
369
  * @public
342
370
  */
343
371
  export class BSplineCurve3d extends BSplineCurve3dBase {
@@ -347,11 +375,19 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
347
375
  this._workBezier = BezierCurve3d.createOrder(this.order);
348
376
  return this._workBezier;
349
377
  }
350
- /** test of `other` is an instance of BSplineCurve3d */
351
- isSameGeometryClass(other) { return other instanceof BSplineCurve3d; }
378
+ constructor(numPoles, order, knots) {
379
+ super(3, numPoles, order, knots);
380
+ }
381
+ /** Test if `other` is an instance of BSplineCurve3d. */
382
+ isSameGeometryClass(other) {
383
+ return other instanceof BSplineCurve3d;
384
+ }
352
385
  /** Apply `transform` to the poles. */
353
- tryTransformInPlace(transform) { Point3dArray.multiplyInPlace(transform, this._bcurve.packedData); return true; }
354
- /** Get a pole as simple Point3d. */
386
+ tryTransformInPlace(transform) {
387
+ Point3dArray.multiplyInPlace(transform, this._bcurve.packedData);
388
+ return true;
389
+ }
390
+ /** Get a pole as a simple Point3d. */
355
391
  getPolePoint3d(poleIndex, result) {
356
392
  const k = this.poleIndexToDataIndex(poleIndex);
357
393
  if (k !== undefined) {
@@ -360,7 +396,7 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
360
396
  }
361
397
  return undefined;
362
398
  }
363
- /** Get a pole as Point4d with weight 1 */
399
+ /** Get a pole as Point4d with weight 1. */
364
400
  getPolePoint4d(poleIndex, result) {
365
401
  const k = this.poleIndexToDataIndex(poleIndex);
366
402
  if (k !== undefined) {
@@ -369,23 +405,26 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
369
405
  }
370
406
  return undefined;
371
407
  }
372
- /** Convert `spanIndex` and `localFraction` to a knot. */
373
- spanFractionToKnot(span, localFraction) {
374
- return this._bcurve.spanFractionToKnot(span, localFraction);
375
- }
376
- constructor(numPoles, order, knots) {
377
- super(3, numPoles, order, knots);
378
- }
379
- /** Return a simple array of arrays with the control points as `[[x,y,z],[x,y,z],..]` */
380
- copyPoints() { return Point3dArray.unpackNumbersToNestedArrays(this._bcurve.packedData, 3); }
381
- /** Return a simple array of the control points coordinates */
382
- copyPointsFloat64Array() { return this._bcurve.packedData.slice(); }
383
408
  /**
384
- * return a simple array form of the knots. optionally replicate the first and last
385
- * in classic over-clamped manner
409
+ * Convert the fractional position in the given span to a knot.
410
+ * * The returned value is not necessarily a knot, but it is a valid parameter in the domain of the B-spline curve.
386
411
  */
387
- copyKnots(includeExtraEndKnot) { return this._bcurve.knots.copyKnots(includeExtraEndKnot); }
388
- /** Create a bspline with uniform knots. */
412
+ spanFractionToKnot(spanIndex, spanFraction) {
413
+ return this._bcurve.spanFractionToKnot(spanIndex, spanFraction);
414
+ }
415
+ /** Return a simple array of arrays with the poles as `[[x,y,z],[x,y,z],..]`. */
416
+ copyPoints() {
417
+ return Point3dArray.unpackNumbersToNestedArrays(this._bcurve.packedData, 3);
418
+ }
419
+ /** Return a simple array of poles' coordinates. */
420
+ copyPointsFloat64Array() {
421
+ return this._bcurve.packedData.slice();
422
+ }
423
+ /** Return a simple array form of the knots. Optionally replicate the first and last in classic over-clamped manner. */
424
+ copyKnots(includeExtraEndKnot) {
425
+ return this._bcurve.knots.copyKnots(includeExtraEndKnot);
426
+ }
427
+ /** Create a B-spline with uniform knots. */
389
428
  static createUniformKnots(poles, order) {
390
429
  const numPoles = poles instanceof Float64Array ? poles.length / 3 : poles.length;
391
430
  if (order < 2 || numPoles < order)
@@ -409,9 +448,10 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
409
448
  }
410
449
  return curve;
411
450
  }
412
- /** Create a smoothly closed B-spline curve with uniform knots.
413
- * Note that the curve does not start at the first pole!
414
- */
451
+ /**
452
+ * Create a smoothly closed B-spline curve with uniform knots.
453
+ * * Note that the curve does not start at the first pole.
454
+ */
415
455
  static createPeriodicUniformKnots(poles, order) {
416
456
  if (order < 2)
417
457
  return undefined;
@@ -482,21 +522,23 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
482
522
  return BSplineCurveOps.createThroughPointsC2Cubic(options);
483
523
  }
484
524
  /**
485
- *
525
+ * Create a B-spline curve from an Akima curve.
486
526
  * @param options collection of points and end conditions.
487
527
  */
488
528
  static createFromAkimaCurve3dOptions(options) {
489
529
  return BSplineCurveOps.createThroughPoints(options.fitPoints, 4); // temporary
490
530
  }
491
531
  /**
492
- * Create a bspline with given knots.
532
+ * Create a B-spline curve with given knots.
493
533
  * * The poles have several variants:
494
- * * Float64Array(3 * numPoles) in blocks of [x,y,z]
495
- * * Point3d[]
496
- * * number[][], with inner dimension 3
534
+ * * Float64Array(3 * numPoles) in blocks of [x,y,z].
535
+ * * Point3d[].
536
+ * * number[][], with inner dimension 3.
497
537
  * * Two count conditions are recognized:
498
- * * If poleArray.length + order === knotArray.length, the first and last are assumed to be the extraneous knots of classic clamping.
538
+ * * If poleArray.length + order === knotArray.length, the first and last are assumed to be the extraneous knots
539
+ * of classic clamping.
499
540
  * * If poleArray.length + order === knotArray.length + 2, the knots are in modern form.
541
+ * * Visualization can be found at https://www.itwinjs.org/sandbox/SaeedTorabi/BSpline/
500
542
  */
501
543
  static create(poleArray, knotArray, order) {
502
544
  if (order < 2)
@@ -534,31 +576,42 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
534
576
  }
535
577
  return curve;
536
578
  }
537
- /** Return a deep clone */
579
+ /** Return a deep clone. */
538
580
  clone() {
539
- const knotVector1 = this._bcurve.knots.clone();
540
- const curve1 = new BSplineCurve3d(this.numPoles, this.order, knotVector1);
541
- curve1._bcurve.packedData = this._bcurve.packedData.slice();
542
- return curve1;
581
+ const knotVector = this._bcurve.knots.clone();
582
+ const curve = new BSplineCurve3d(this.numPoles, this.order, knotVector);
583
+ curve._bcurve.packedData = this._bcurve.packedData.slice();
584
+ return curve;
543
585
  }
544
- /** Evaluate at a position given by fractional position within a span. */
586
+ /** Evaluate the curve at a fractional position within a given span. */
545
587
  evaluatePointInSpan(spanIndex, spanFraction) {
546
588
  this._bcurve.evaluateBuffersInSpan(spanIndex, spanFraction);
547
589
  return Point3d.createFrom(this._bcurve.poleBuffer);
548
590
  }
549
- /** Evaluate point and derivative vector at a position given by fractional position within a span.
550
- * * The derivative is with respect to the span fraction (NOT scaled to either global fraction or knot)
591
+ /**
592
+ * Evaluate the curve and derivative at a fractional position within a given span.
593
+ * * The derivative is with respect to the span fractional parameter, _not_ to the curve's parameter or fractional parameter.
551
594
  */
552
595
  evaluatePointAndDerivativeInSpan(spanIndex, spanFraction) {
553
596
  this._bcurve.evaluateBuffersInSpan1(spanIndex, spanFraction);
554
597
  return Ray3d.createCapture(Point3d.createFrom(this._bcurve.poleBuffer), Vector3d.createFrom(this._bcurve.poleBuffer1));
555
598
  }
556
- /** Evaluate at a position given by a knot value. */
599
+ /**
600
+ * Evaluate the curve at the given parameter.
601
+ * @param u parameter in curve domain.
602
+ * @param result optional result.
603
+ * @returns the point on the curve.
604
+ */
557
605
  knotToPoint(u, result) {
558
606
  this._bcurve.evaluateBuffersAtKnot(u);
559
607
  return Point3d.createFrom(this._bcurve.poleBuffer, result);
560
608
  }
561
- /** Evaluate at a position given by a knot value. */
609
+ /**
610
+ * Evaluate the curve and derivative at the given parameter.
611
+ * @param u parameter in curve domain.
612
+ * @param result optional result.
613
+ * @returns the ray with origin at the curve point and direction as the derivative.
614
+ */
562
615
  knotToPointAndDerivative(u, result) {
563
616
  this._bcurve.evaluateBuffersAtKnot(u, 1);
564
617
  if (!result)
@@ -567,12 +620,17 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
567
620
  result.direction.setFrom(this._bcurve.poleBuffer1);
568
621
  return result;
569
622
  }
570
- /** Evaluate at a position given by a knot value. Return point with 2 derivatives. */
623
+ /**
624
+ * Evaluate the curve and two derivatives at the given parameter.
625
+ * @param u parameter in the curve domain.
626
+ * @param result optional result.
627
+ * @returns the plane with origin at the curve point, vectorU as the 1st derivative, and vectorV as the 2nd derivative.
628
+ */
571
629
  knotToPointAnd2Derivatives(u, result) {
572
630
  this._bcurve.evaluateBuffersAtKnot(u, 2);
573
631
  return Plane3dByOriginAndVectors.createOriginAndVectorsXYZ(this._bcurve.poleBuffer[0], this._bcurve.poleBuffer[1], this._bcurve.poleBuffer[2], this._bcurve.poleBuffer1[0], this._bcurve.poleBuffer1[1], this._bcurve.poleBuffer1[2], this._bcurve.poleBuffer2[0], this._bcurve.poleBuffer2[1], this._bcurve.poleBuffer2[2], result);
574
632
  }
575
- /** test if almost the same curve as `other` */
633
+ /** Test if `this` is almost the same curve as `other`. */
576
634
  isAlmostEqual(other) {
577
635
  if (other instanceof BSplineCurve3d) {
578
636
  return this._bcurve.knots.isAlmostEqual(other._bcurve.knots)
@@ -580,15 +638,19 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
580
638
  }
581
639
  return false;
582
640
  }
583
- /** test if this curve is entirely within plane. */
641
+ /** Test if this curve lies entirely in the given plane. */
584
642
  isInPlane(plane) {
585
643
  return Point3dArray.isCloseToPlane(this._bcurve.packedData, plane);
586
644
  }
587
- /** Return the control polygon length as approximation (always overestimate) of the curve length. */
588
- quickLength() { return Point3dArray.sumEdgeLengths(this._bcurve.packedData); }
589
- /** Emit beziers or strokes (selected by the stroke options) to the handler. */
645
+ /**
646
+ * Return the control polygon length as an approximation to the curve length.
647
+ * * The returned length is always an overestimate.
648
+ */
649
+ quickLength() {
650
+ return Point3dArray.sumEdgeLengths(this._bcurve.packedData);
651
+ }
652
+ /** Emit Beziers or strokes (selected by the stroke options) to the handler. */
590
653
  emitStrokableParts(handler, options) {
591
- const needBeziers = handler.announceBezierCurve !== undefined;
592
654
  const workBezier = this.initializeWorkBezier();
593
655
  const numSpan = this.numSpan;
594
656
  let numStrokes;
@@ -596,7 +658,7 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
596
658
  const bezier = this.getSaturatedBezierSpan3dOr3dH(spanIndex, false, workBezier);
597
659
  if (bezier) {
598
660
  numStrokes = bezier.computeStrokeCountForOptions(options);
599
- if (needBeziers) {
661
+ if (handler.announceBezierCurve) {
600
662
  handler.announceBezierCurve(bezier, numStrokes, this, spanIndex, this._bcurve.knots.spanFractionToFraction(spanIndex, 0.0), this._bcurve.knots.spanFractionToFraction(spanIndex, 1.0));
601
663
  }
602
664
  else {
@@ -621,7 +683,7 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
621
683
  return numStroke;
622
684
  }
623
685
  /**
624
- * Compute individual segment stroke counts. Attach in a StrokeCountMap.
686
+ * Compute individual segment stroke counts. Attach in a StrokeCountMap.
625
687
  * @param options StrokeOptions that determine count
626
688
  * @param parentStrokeMap evolving parent map.
627
689
  * @alpha
@@ -640,7 +702,7 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
640
702
  }
641
703
  CurvePrimitive.installStrokeCountMap(this, myData, parentStrokeMap);
642
704
  }
643
- /** Append strokes to a linestring. */
705
+ /** Append strokes to the given linestring. */
644
706
  emitStrokes(dest, options) {
645
707
  const workBezier = this.initializeWorkBezier();
646
708
  const numSpan = this.numSpan;
@@ -651,16 +713,17 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
651
713
  }
652
714
  }
653
715
  /**
654
- * Test knots and control points to determine if it is possible to close (aka "wrap") the curve.
716
+ * Test knots and poles to determine if it is possible to close (aka "wrap") the curve.
655
717
  * @returns the manner in which it is possible to close the curve. See `BSplineWrapMode` for particulars of each mode.
656
718
  */
657
719
  get isClosable() {
658
720
  return this.isClosableCurve;
659
721
  }
660
722
  /**
661
- * Return a BezierCurveBase for this curve. The concrete return type may be BezierCurve3d or BezierCurve3dH according to this type.
662
- * @param spanIndex
663
- * @param result optional reusable curve. This will only be reused if it is a BezierCurve3d with matching order.
723
+ * Return the Bezier fragment corresponding to the given span of this curve.
724
+ * * The concrete return type may be [[BezierCurve3d]] or [[BezierCurve3dH]] according to the instance type and `prefer3dH`.
725
+ * @param spanIndex index of span.
726
+ * @param result optional reusable curve. This will only be reused if its type and order matches.
664
727
  */
665
728
  getSaturatedBezierSpan3dOr3dH(spanIndex, prefer3dH, result) {
666
729
  if (prefer3dH)
@@ -668,9 +731,9 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
668
731
  return this.getSaturatedBezierSpan3d(spanIndex, result);
669
732
  }
670
733
  /**
671
- * Return a CurvePrimitive (which is a BezierCurve3d) for a specified span of this curve.
672
- * @param spanIndex
673
- * @param result optional reusable curve. This will only be reused if it is a BezierCurve3d with matching order.
734
+ * Return the Bezier fragment corresponding to the given span of this curve.
735
+ * @param spanIndex index of span.
736
+ * @param result optional reusable curve. This will only be reused if its type and order matches.
674
737
  */
675
738
  getSaturatedBezierSpan3d(spanIndex, result) {
676
739
  if (spanIndex < 0 || spanIndex >= this.numSpan)
@@ -681,13 +744,13 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
681
744
  const bezier = result;
682
745
  bezier.loadSpanPoles(this._bcurve.packedData, spanIndex);
683
746
  if (bezier.saturateInPlace(this._bcurve.knots, spanIndex))
684
- return result;
747
+ return bezier;
685
748
  return undefined;
686
749
  }
687
750
  /**
688
- * Return a CurvePrimitive (which is a BezierCurve3dH) for a specified span of this curve.
689
- * @param spanIndex
690
- * @param result optional reusable curve. This will only be reused if it is a BezierCurve3d with matching order.
751
+ * Return the Bezier fragment corresponding to the given span of this curve.
752
+ * @param spanIndex index of span.
753
+ * @param result optional reusable curve. This will only be reused if its type and order matches.
691
754
  */
692
755
  getSaturatedBezierSpan3dH(spanIndex, result) {
693
756
  if (spanIndex < 0 || spanIndex >= this.numSpan)
@@ -701,15 +764,16 @@ export class BSplineCurve3d extends BSplineCurve3dBase {
701
764
  return bezier;
702
765
  return undefined;
703
766
  }
704
- /** Second step of double dispatch: call `handler.handleBSplineCurve3d(this)` */
767
+ /** Second step of double dispatch: call `handler.handleBSplineCurve3d(this)`. */
705
768
  dispatchToGeometryHandler(handler) {
706
769
  return handler.handleBSplineCurve3d(this);
707
770
  }
708
771
  /**
709
- * Extend a range so in includes the range of this curve
710
- * * REMARK: this is based on the poles, not the exact curve. This is generally larger than the true curve range.
711
- * @param rangeToExtend
712
- * @param transform transform to apply to points as they are entered into the range.
772
+ * Extend a range so it contains the range of this curve.
773
+ * * This computation is based on the poles, not the curve itself, so the returned range is generally larger than the
774
+ * tightest possible range.
775
+ * @param rangeToExtend range to extend.
776
+ * @param transform transform to apply to the poles as they are entered into the range.
713
777
  */
714
778
  extendRange(rangeToExtend, transform) {
715
779
  const buffer = this._bcurve.packedData;