@@ -227,19 +227,28 @@ class KnotVector
227
227
* For knot vector {u_0, ..., u_n}, returns i such that u_i <= t < u_i+1
228
228
* if t == u_n, returns i such that u_i < t <= u_i+1 (i.e. i = n - degree - 1)
229
229
*
230
- * \pre Assumes that the input t is within the knot vector
231
- *
232
230
* Implementation adapted from Algorithm A2.1 on page 68 of "The NURBS Book"
233
231
*
232
+ * \pre Assumes that the input t is in the span [u_0, u_n] (up to some tolerance)
233
+ *
234
+ * \note If t is outside the knot span up to this tolerance, it is clamped to the span
235
+ *
234
236
* \return The index of the knot span containing t
235
237
*/
236
238
axom::IndexType findSpan (T t) const
237
239
{
238
- SLIC_ASSERT (t >= m_knots[ 0 ] && t <= m_knots[m_knots. size () - 1 ] );
240
+ SLIC_ASSERT (isValidParameter (t) );
239
241
240
242
const axom::IndexType nkts = m_knots.size ();
241
243
242
- if (t == m_knots[nkts - 1 ])
244
+ // Handle cases where t is outside the knot span within a tolerance
245
+ // by implicitly clamping it to the nearest span
246
+ if (t <= m_knots[0 ])
247
+ {
248
+ return m_deg;
249
+ }
250
+
251
+ if (t >= m_knots[nkts - 1 ])
243
252
{
244
253
return nkts - m_deg - 2 ;
245
254
}
@@ -266,19 +275,29 @@ class KnotVector
266
275
* For knot vector {u_0, ..., u_n}, returns i such that u_i <= t < u_i+1
267
276
* if t == u_n, returns i such that u_i < t <= u_i+1 (i.e. i = n - degree - 1)
268
277
*
269
- * \pre Assumes that the input t is within the knot vector
278
+ * \pre Assumes that the input t is within the knot vector (up to some tolerance)
279
+ *
280
+ * \note If t is outside the knot span up to this tolerance, the returned multiplicity
281
+ * will be equal to the degree + 1 (required for clamped curves)
270
282
*
271
283
* \return The index of the knot span containing t
272
284
*/
273
285
axom::IndexType findSpan (T t, int & multiplicity) const
274
286
{
275
- SLIC_ASSERT (t >= m_knots[ 0 ] && t <= m_knots[m_knots. size () - 1 ] );
287
+ SLIC_ASSERT (isValidParameter (t) );
276
288
277
289
const auto nkts = m_knots.size ();
278
290
const auto span = findSpan (t);
279
291
292
+ // Early exit for known multiplicities
293
+ if (t <= m_knots[0 ] || t >= m_knots[nkts - 1 ])
294
+ {
295
+ multiplicity = m_deg + 1 ;
296
+ return span;
297
+ }
298
+
280
299
multiplicity = 0 ;
281
- for (auto i = (t == m_knots[nkts - 1 ]) ? nkts - 1 : span; i >= 0 ; --i)
300
+ for (auto i = span; i >= 0 ; --i)
282
301
{
283
302
if (m_knots[i] == t)
284
303
{
@@ -356,20 +375,22 @@ class KnotVector
356
375
* \param [in] t The value of the knot to insert
357
376
* \param [in] target_mutliplicity The number of times the knot will be present
358
377
*
359
- * \pre Assumes that the input t is within the knot vector
378
+ * \pre Assumes that the input t is within the knot vector (up to some tolerance)
360
379
*
361
380
* \note If the knot is already present, it will be inserted
362
381
* up to the given multiplicity, or the maximum permitted by the degree
363
382
*/
364
383
void insertKnot (T t, int target_multiplicity)
365
384
{
366
- SLIC_ASSERT (t >= m_knots[ 0 ] && t <= m_knots[m_knots. size () - 1 ] );
385
+ SLIC_ASSERT (isValidParameter (t) );
367
386
368
387
int multiplicity;
369
388
auto span = findSpan (t, multiplicity);
370
389
371
- int r =
372
- axom::utilities::clampVal (target_multiplicity - multiplicity, 0 , m_deg);
390
+ // Compute how many knots should be inserted
391
+ int r = axom::utilities::clampVal (target_multiplicity - multiplicity,
392
+ 0 ,
393
+ m_deg - multiplicity);
373
394
374
395
insertKnotBySpan (span, t, r);
375
396
}
@@ -424,8 +445,8 @@ class KnotVector
424
445
k2.normalize ();
425
446
}
426
447
427
- // SLIC_ASSERT(k1.isValid());
428
- // SLIC_ASSERT(k2.isValid());
448
+ SLIC_ASSERT (k1.isValid ());
449
+ SLIC_ASSERT (k2.isValid ());
429
450
}
430
451
431
452
/* !
@@ -436,11 +457,11 @@ class KnotVector
436
457
* \param [out] k2 The second knot vector
437
458
* \param [in] normalize Whether to normalize the output knot vectors
438
459
*
439
- * \pre Assumes that the input t is within the knot vector
460
+ * \pre Assumes that the input t is *interior* to the knot vector
440
461
*/
441
462
void split (T t, KnotVector& k1, KnotVector& k2, bool normalize = false ) const
442
463
{
443
- SLIC_ASSERT (t > m_knots[ 0 ] && t < m_knots[m_knots. size () - 1 ] );
464
+ SLIC_ASSERT (isValidInteriorParameter (t) );
444
465
445
466
int multiplicity;
446
467
axom::IndexType span = findSpan (t, multiplicity);
@@ -457,7 +478,7 @@ class KnotVector
457
478
* \param [in] span The span in which to evaluate the basis functions
458
479
* \param [in] t The parameter value
459
480
*
460
- * \pre Assumes that the input t is within the knot vector and that the span is valid
481
+ * \pre Assumes that the input t is within the correct span
461
482
* Implementation adapted from Algorithm A2.2 on page 70 of "The NURBS Book".
462
483
*
463
484
* \return An array of the `m_deg + 1` non-zero basis functions evaluated at t
@@ -496,13 +517,13 @@ class KnotVector
496
517
*
497
518
* \param [in] t The parameter value
498
519
*
499
- * \pre Assumes that the input t is within the knot vector
520
+ * \pre Assumes that the input t is within the knot vector (up to a tolerance)
500
521
*
501
522
* \return An array of the `m_deg + 1` non-zero basis functions evaluated at t
502
523
*/
503
524
axom::Array<T> calculateBasisFunctions (T t) const
504
525
{
505
- SLIC_ASSERT (t >= m_knots[ 0 ] && t <= m_knots[m_knots. size () - 1 ] );
526
+ SLIC_ASSERT (isValidParameter (t) );
506
527
return calculateBasisFunctionsBySpan (findSpan (t), t);
507
528
}
508
529
@@ -512,19 +533,23 @@ class KnotVector
512
533
* \param [in] span The span in which to evaluate the basis functions
513
534
* \param [in] t The parameter value
514
535
* \param [in] n The number of derivatives to compute
515
- * \param [out] ders An array of the `n + 1` derivatives evaluated at t
516
536
*
517
537
* Implementation adapted from Algorithm A2.2 on page 70 of "The NURBS Book".
538
+ *
539
+ * \pre Assumes that the input t is within the provided knot span
540
+ *
541
+ * \return An array of the `n + 1` derivatives evaluated at t
518
542
*/
519
- void derivativeBasisFunctionsBySpan (axom::IndexType span,
520
- T t,
521
- int n,
522
- axom::Array<axom::Array<T>>& ders) const
543
+ axom::Array<axom::Array<T>> derivativeBasisFunctionsBySpan (axom::IndexType span,
544
+ T t,
545
+ int n) const
523
546
{
524
547
SLIC_ASSERT (isValidSpan (span, t));
525
548
526
549
const int m_deg = getDegree ();
527
550
551
+ axom::Array<axom::Array<T>> ders (n + 1 );
552
+
528
553
axom::Array<axom::Array<T>> ndu (m_deg + 1 ), a (2 );
529
554
axom::Array<T> left (m_deg + 1 ), right (m_deg + 1 );
530
555
for (int j = 0 ; j <= m_deg; j++)
@@ -596,6 +621,7 @@ class KnotVector
596
621
std::swap (s1, s2);
597
622
}
598
623
}
624
+
599
625
// Multiply through by the correct factors (Eq. [2.9])
600
626
T r = static_cast <T>(m_deg);
601
627
for (int k = 1 ; k <= n; k++)
@@ -606,21 +632,24 @@ class KnotVector
606
632
}
607
633
r *= static_cast <T>(m_deg - k);
608
634
}
635
+
636
+ return ders;
609
637
}
610
638
611
639
/* !
612
640
* \brief Evaluates the NURBS basis functions and derivatives for parameter value t
613
641
*
614
642
* \param [in] t The parameter value
615
643
* \param [in] n The number of derivatives to compute
616
- * \param [out] ders An array of the `n + 1` derivatives evaluated at t
617
644
*
618
- * \pre Assumes that the input t is within the knot vector
645
+ * \pre Assumes that the input t is within the knot vector (up to a tolerance)
646
+ *
647
+ * \return An array of the `n + 1` derivatives evaluated at t
619
648
*/
620
- void derivativeBasisFunctions (T t, int n, axom::Array<axom::Array<T>>& ders ) const
649
+ axom::Array<axom::Array<T>> derivativeBasisFunctions (T t, int n ) const
621
650
{
622
- SLIC_ASSERT (t >= m_knots[ 0 ] && t <= m_knots[m_knots. size () - 1 ] );
623
- derivativeBasisFunctionsBySpan (findSpan (t), t, n, ders );
651
+ SLIC_ASSERT (isValidParameter (t) );
652
+ return derivativeBasisFunctionsBySpan (findSpan (t), t, n);
624
653
}
625
654
626
655
// / \brief Reverse the knot vector
@@ -755,6 +784,18 @@ class KnotVector
755
784
return !(lhs == rhs);
756
785
}
757
786
787
+ // / \brief Checks if given parameter is in knot span (to a tolerance)
788
+ bool isValidParameter (T t, T EPS = 1e-5 ) const
789
+ {
790
+ return t >= m_knots[0 ] - EPS && t <= m_knots[m_knots.size () - 1 ] + EPS;
791
+ }
792
+
793
+ // / \brief Checks if given parameter is *interior* to knot span (to a tolerance)
794
+ bool isValidInteriorParameter (T t) const
795
+ {
796
+ return t > m_knots[0 ] && t < m_knots[m_knots.size () - 1 ];
797
+ }
798
+
758
799
/* !
759
800
* \brief Simple formatted print of a knot vector instance
760
801
*
0 commit comments