00001
00015 #ifndef DLR_NUMERIC_BSPLINE_H
00016 #define DLR_NUMERIC_BSPLINE_H
00017
00018 #include <vector>
00019 #include <dlrNumeric/array1D.h>
00020 #include <dlrNumeric/array2D.h>
00021 #include <dlrNumeric/polynomial.h>
00022
00023 namespace dlr {
00024
00025 namespace numeric {
00026
00047 template <class Type>
00048 class BSpline {
00049 public:
00050
00063 BSpline(size_t order=2, bool isPeriodic=true);
00064
00065
00071 BSpline(const BSpline& other);
00072
00073
00087 double
00088 getMaximumSValue();
00089
00090
00103 double
00104 getMinimumSValue();
00105
00106
00117 void
00118 setControlPoints(const std::vector<Type>& controlPoints);
00119
00120
00139 void
00140 setKnotMultiplicities(const std::vector<size_t>& knotMultiplicities);
00141
00142
00169 void
00170 setNumberOfNodes(size_t numberOfNodes,
00171 bool setKnotMultiplicitiesFlag=true);
00172
00173
00206 void
00207 setNumberOfNodes(size_t numberOfNodes,
00208 const std::vector<double>& nodePositions,
00209 bool setKnotMultiplicitiesFlag=true);
00210
00211
00212
00218 BSpline<Type>&
00219 operator=(const BSpline<Type>& other);
00220
00221
00228 Type
00229 operator()(double sValue);
00230
00231
00232 protected:
00233
00259 Polynomial<double>
00260 computeBasisFunction(size_t order,
00261 size_t spanNumber,
00262 size_t componentNumber,
00263 const Array1D<size_t>& cumulativeKnotCounts,
00264 const Array1D<double>& knotPositions);
00265
00266
00289 Array1D< Array1D<double> >
00290 getControlPointVectors(size_t order,
00291 size_t numberOfNodes,
00292 const Array1D<size_t>& cumulativeKnotCounts,
00293 const std::vector<Type>& controlPoints);
00294
00295
00325 Array1D< Array2D<double> >
00326 getCoefficientMatrices(size_t order,
00327 size_t numberOfNodes,
00328 const Array1D<double>& nodePositions,
00329 const Array1D<double>& knotPositions,
00330 const Array1D<size_t>& cumulativeKnotCounts);
00331
00332
00346 double
00347 getKnotPosition(int knotNumber,
00348 const Array1D<double>& knotPositions);
00349
00350
00360 size_t
00361 getSpanNumber(double sValue);
00362
00363
00371 void
00372 setNodePositions(const std::vector<double>& nodePositions);
00373
00374
00375 Array1D< Array2D<double> > m_coefficientMatrixArray;
00376 std::vector<Type> m_controlPoints;
00377 Array1D< Array1D<Type> > m_controlPointVectorArray;
00378 Array1D<size_t> m_cumulativeKnotCounts;
00379 Array1D<double> m_inputVector;
00380 bool m_isPeriodic;
00381 bool m_isUniform;
00382 Array1D<double> m_knotPositionArray;
00383 Array1D<double> m_nodePositionArray;
00384 size_t m_numberOfNodes;
00385 size_t m_order;
00386 size_t m_orderPlusOne;
00387 };
00388
00389
00390 }
00391
00392 }
00393
00394
00395
00396
00397
00398
00399
00400 #include <cmath>
00401 #include <dlrNumeric/functional.h>
00402 #include <dlrNumeric/utilities.h>
00403
00404 namespace dlr {
00405
00406 namespace numeric {
00407
00408
00409
00410
00411 template <class Type>
00412 BSpline<Type>::
00413 BSpline(size_t order, bool isPeriodic)
00414 : m_coefficientMatrixArray(),
00415 m_controlPoints(),
00416 m_controlPointVectorArray(),
00417 m_cumulativeKnotCounts(),
00418 m_inputVector(order + 1),
00419 m_isPeriodic(isPeriodic),
00420 m_isUniform(true),
00421 m_knotPositionArray(),
00422 m_nodePositionArray(),
00423 m_numberOfNodes(0),
00424 m_order(order),
00425 m_orderPlusOne(order + 1)
00426 {
00427
00428 }
00429
00430
00431
00432 template <class Type>
00433 BSpline<Type>::
00434 BSpline(const BSpline& other)
00435 : m_coefficientMatrixArray(other.m_coefficientMatrixArray.copy()),
00436 m_controlPoints(other.m_controlPoints),
00437 m_controlPointVectorArray(other.m_controlPointVectorArray.copy()),
00438 m_cumulativeKnotCounts(other.m_cumulativeKnotCounts.copy()),
00439 m_inputVector(other.m_inputVector.copy()),
00440 m_isPeriodic(other.isPeriodic),
00441 m_isUniform(other.isUniform),
00442 m_knotPositionArray(other.m_knotPositionArray.copy()),
00443 m_nodePositionArray(other.m_nodePositionArray.copy()),
00444 m_numberOfNodes(other.m_numberOfNodes),
00445 m_order(other.m_order),
00446 m_orderPlusOne(other.m_orderPlusOne)
00447 {
00448
00449 }
00450
00451
00452
00453
00454
00455
00456 template <class Type>
00457 double
00458 BSpline<Type>::
00459 getMaximumSValue()
00460 {
00461 if(m_nodePositionArray.size() == 0) {
00462 DLR_THROW(StateException, "BSpline::getMaximumSValue()",
00463 "Node positions have not been set. You must first call"
00464 " setNumberOfNodes() or setNodePositions().");
00465 }
00466 return m_nodePositionArray[m_nodePositionArray.size() - 1];
00467 }
00468
00469
00470
00471
00472 template <class Type>
00473 double
00474 BSpline<Type>::
00475 getMinimumSValue()
00476 {
00477 if(m_nodePositionArray.size() == 0) {
00478 DLR_THROW(StateException, "BSpline::getMinimumSValue()",
00479 "Node positions have not been set. You must first call"
00480 " setNumberOfNodes() or setNodePositions().");
00481 }
00482 return m_nodePositionArray[0];
00483 }
00484
00485
00486
00487
00488 template <class Type>
00489 void
00490 BSpline<Type>::
00491 setControlPoints(const std::vector<Type>& controlPoints)
00492 {
00493 if(m_isPeriodic) {
00494 if(controlPoints.size() != m_numberOfNodes - 1) {
00495 std::ostringstream message;
00496 message << "Argument controlPoints has " << controlPoints.size()
00497 << " elements, but should have " << m_numberOfNodes - 1
00498 << " elements for a periodic " << m_numberOfNodes
00499 << " node spline because the first and last nodes overlap.";
00500 DLR_THROW(ValueException, "BSpline::setControlPoints()",
00501 message.str().c_str());
00502 }
00503 } else {
00504 if(controlPoints.size() != m_numberOfNodes) {
00505 std::ostringstream message;
00506 message << "Argument controlPoints has " << controlPoints.size()
00507 << " elements, but should have " << m_numberOfNodes
00508 << " elements for a non-periodic " << m_numberOfNodes
00509 << " node spline.";
00510 DLR_THROW(ValueException, "BSpline::setControlPoints()",
00511 message.str().c_str());
00512 }
00513 }
00514 if(m_knotPositionArray.size() < m_nodePositionArray.size()) {
00515 DLR_THROW(StateException, "BSpline::setControlPoints()",
00516 "Knot positions have not been initialized. You must first"
00517 " call setKnotMultiplicies(), or else call"
00518 " setNumberOfNodes() with argument setKnotMultipliciesFlag"
00519 " set to true.");
00520 }
00521 m_controlPoints = controlPoints;
00522 m_controlPointVectorArray = this->getControlPointVectors(
00523 m_order, m_numberOfNodes, m_cumulativeKnotCounts, controlPoints);
00524 }
00525
00526
00527
00528
00529 template <class Type>
00530 void
00531 BSpline<Type>::
00532 setKnotMultiplicities(const std::vector<size_t>& knotMultiplicities)
00533 {
00534 if(knotMultiplicities.size() != m_numberOfNodes) {
00535 std::ostringstream message;
00536 message << "Argument knotMultiplicities has "
00537 << knotMultiplicities.size() << " elements, when a(n) "
00538 << m_numberOfNodes << " element vector was expected.";
00539 DLR_THROW(ValueException, "BSpline::setKnotMultiplicities()",
00540 message.str().c_str());
00541 }
00542 if(m_isPeriodic) {
00543 if(knotMultiplicities[0] != knotMultiplicities[m_numberOfNodes - 1]) {
00544 std::ostringstream message;
00545 message << "For a periodic spline, first and last node are actually "
00546 << "the same and so must have the same knot multiplicity, "
00547 << "but knotMultiplicities[0] = " << knotMultiplicities[0]
00548 << " and knotMultiplicities[" << m_numberOfNodes - 1
00549 << "] = " << knotMultiplicities[m_numberOfNodes - 1] << ".";
00550 DLR_THROW(ValueException, "BSpline::setKnotMultiplicities()",
00551 message.str().c_str());
00552 }
00553 } else {
00554 if(knotMultiplicities[0] != m_order
00555 || knotMultiplicities[m_numberOfNodes - 1] != m_order) {
00556 std::ostringstream message;
00557 message << "For a non-periodic spline of order " << m_order << ", "
00558 << "first and last knot must have multiplicity " << m_order
00559 << ", but knotMultiplicities[0] = " << knotMultiplicities[0]
00560 << " and knotMultiplicities[" << m_numberOfNodes - 1
00561 << "] = " << knotMultiplicities[m_numberOfNodes - 1] << ".";
00562 DLR_THROW(ValueException, "BSpline::setKnotMultiplicities()",
00563 message.str().c_str());
00564 }
00565 }
00566 if(m_cumulativeKnotCounts.size() != knotMultiplicities.size()) {
00567 m_cumulativeKnotCounts.reinit(knotMultiplicities.size());
00568 }
00569 std::partial_sum(knotMultiplicities.begin(), knotMultiplicities.end(),
00570 m_cumulativeKnotCounts.begin());
00571 size_t numberOfKnots = m_cumulativeKnotCounts[m_numberOfNodes - 1];
00572 if(m_knotPositionArray.size() != numberOfKnots) {
00573 m_knotPositionArray.reinit(numberOfKnots);
00574 }
00575 size_t knotIndex = 0;
00576 for(size_t nodeIndex = 0; nodeIndex < m_numberOfNodes; ++nodeIndex) {
00577 while(knotIndex < m_cumulativeKnotCounts[nodeIndex]) {
00578 m_knotPositionArray[knotIndex] = m_nodePositionArray[nodeIndex];
00579 ++knotIndex;
00580 }
00581 }
00582 m_coefficientMatrixArray = this->getCoefficientMatrices(
00583 m_order, m_numberOfNodes, m_nodePositionArray, m_knotPositionArray,
00584 m_cumulativeKnotCounts);
00585 }
00586
00587
00588
00589
00590
00591
00592 template <class Type>
00593 void
00594 BSpline<Type>::
00595 setNumberOfNodes(size_t numberOfNodes, bool setKnotMultiplicitiesFlag)
00596 {
00597 std::vector<double> nodePositions(numberOfNodes);
00598 for(size_t index0 = 0; index0 < numberOfNodes; ++index0) {
00599 nodePositions[index0] = static_cast<double>(index0);
00600 }
00601 this->setNumberOfNodes(
00602 numberOfNodes, nodePositions, setKnotMultiplicitiesFlag);
00603 m_isUniform = true;
00604 }
00605
00606
00607
00608
00609 template <class Type>
00610 void
00611 BSpline<Type>::
00612 setNumberOfNodes(size_t numberOfNodes,
00613 const std::vector<double>& nodePositions,
00614 bool setKnotMultiplicitiesFlag)
00615 {
00616 m_numberOfNodes = numberOfNodes;
00617 m_isUniform = false;
00618 this->setNodePositions(nodePositions);
00619 if(setKnotMultiplicitiesFlag) {
00620 std::vector<size_t> knotMultiplicities(numberOfNodes, size_t(1));
00621 if(!m_isPeriodic) {
00622 knotMultiplicities[0] = m_order + 1;
00623 knotMultiplicities[numberOfNodes - 1] = m_order + 1;
00624 }
00625 this->setKnotMultiplicities(knotMultiplicities);
00626 }
00627 }
00628
00629
00630
00631 template <class Type>
00632 BSpline<Type>&
00633 BSpline<Type>::
00634 operator=(const BSpline<Type>& other)
00635 {
00636 if(&other != this) {
00637 m_coefficientMatrixArray = other.m_coefficientMatrixArray.copy();
00638 m_controlPoints = other.m_controlPoints;
00639 m_controlPointVectorArray = other.m_controlPointVectorArray.copy();
00640 m_cumulativeKnotCounts = other.m_cumulativeKnotCounts.copy();
00641 m_inputVector = other.m_inputVector.copy();
00642 m_isPeriodic = other.isPeriodic;
00643 m_isUniform = other.isUniform;
00644 m_knotPositionArray = other.m_knotPositionArray.copy();
00645 m_nodePositionArray = other.m_nodePositionArray.copy();
00646 m_numberOfNodes = other.m_numberOfNodes;
00647 m_order = other.m_order;
00648 m_orderPlusOne = other.m_orderPlusOn;
00649 }
00650 }
00651
00652
00653
00654
00655 template <class Type>
00656 Type
00657 BSpline<Type>::
00658 operator()(double sValue)
00659 {
00660
00661 double accumulator = 1.0;
00662 m_inputVector[0] = accumulator;
00663 for(size_t index0 = 1; index0 < m_inputVector.size(); ++index0) {
00664 accumulator *= sValue;
00665 m_inputVector[index0] = accumulator;
00666 }
00667
00668
00669
00670 size_t spanNumber = this->getSpanNumber(sValue);
00671 const Array1D<Type>& controlPointVector =
00672 m_controlPointVectorArray[spanNumber];
00673 const Array2D<double>& coefficientMatrix =
00674 m_coefficientMatrixArray[spanNumber];
00675
00676
00677
00678
00679 Type result = (dot(coefficientMatrix.row(0), m_inputVector)
00680 * controlPointVector[0]);
00681 for(size_t index1 = 1; index1 < m_orderPlusOne; ++index1) {
00682 result += (dot(coefficientMatrix.row(index1), m_inputVector)
00683 * controlPointVector[index1]);
00684 }
00685 return result;
00686 }
00687
00688
00689
00690
00691 template <class Type>
00692 Polynomial<double>
00693 BSpline<Type>::
00694 computeBasisFunction(size_t order,
00695 size_t spanNumber,
00696 size_t componentNumber,
00697 const Array1D<size_t>& cumulativeKnotCounts,
00698 const Array1D<double>& knotPositions)
00699 {
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715 Array1D< Polynomial<double> > componentArray(order + 1);
00716 componentArray = Polynomial<double>(0.0);
00717 componentArray[order - componentNumber] = Polynomial<double>(1.0);
00718
00719
00720
00721
00722
00723
00724
00725 for(int orderIndex = 1; orderIndex <= static_cast<int>(order); ++orderIndex) {
00726
00727
00728
00729 Array1D< Polynomial<double> > newComponentArray(
00730 componentArray.size() - 1);
00731
00732
00733 for(size_t subComponentIndex = 0;
00734 subComponentIndex < newComponentArray.size();
00735 ++subComponentIndex) {
00736
00737
00738
00739 int knotNumber =
00740 (static_cast<int>(cumulativeKnotCounts[spanNumber]) - 1
00741 - static_cast<int>(m_order)
00742 + static_cast<int>(componentNumber)
00743 + static_cast<int>(subComponentIndex));
00744 double k_n =
00745 this->getKnotPosition(knotNumber, knotPositions);
00746 double k_nPlus1 =
00747 this->getKnotPosition(knotNumber + 1, knotPositions);
00748 double k_nPlusDMinus1 =
00749 this->getKnotPosition(knotNumber + orderIndex, knotPositions);
00750 double k_nPlusD =
00751 this->getKnotPosition(knotNumber + orderIndex + 1, knotPositions);
00752 double span0 = k_nPlusDMinus1 - k_n;
00753 double span1 = k_nPlusD - k_nPlus1;
00754 Polynomial<double> scaleFunction0(1.0 / span0, -k_n / span0);
00755 Polynomial<double> scaleFunction1(-1.0 / span1, k_nPlusD / span1);
00756 newComponentArray[subComponentIndex] = (
00757 scaleFunction0 * componentArray[subComponentIndex]
00758 + scaleFunction1 * componentArray[subComponentIndex + 1]);
00759 }
00760
00761
00762
00763 componentArray = newComponentArray;
00764 }
00765 if(componentArray.size() != 1) {
00766 DLR_THROW(LogicException, "BSpline::computeBasisFunction()",
00767 "Recursion terminated incorrectly.");
00768 }
00769 return componentArray[0];
00770 }
00771
00772
00773
00774
00775
00776
00777 template <class Type>
00778 Array1D< Array2D<double> >
00779 BSpline<Type>::
00780 getCoefficientMatrices(size_t order,
00781 size_t numberOfNodes,
00782 const Array1D<double>& nodePositions,
00783 const Array1D<double>& knotPositions,
00784 const Array1D<size_t>& cumulativeKnotCounts)
00785 {
00786 size_t numberOfSpans = numberOfNodes - 1;
00787 Array1D< Array2D<double> > coefficientMatrixArray(numberOfSpans);
00788 for(size_t spanNumber = 0; spanNumber < numberOfSpans; ++spanNumber) {
00789 coefficientMatrixArray[spanNumber] =
00790 Array2D<double>(order + 1, order + 1);
00791
00792 for(size_t componentIndex = 0; componentIndex < order + 1;
00793 ++componentIndex) {
00794 Polynomial<double> basisFunction = this->computeBasisFunction(
00795 order, spanNumber, componentIndex, cumulativeKnotCounts,
00796 knotPositions);
00797 if(basisFunction.getOrder() != order) {
00798 DLR_THROW(LogicException, "BSpline::getCoefficientMatrices()",
00799 "Basis function has incorrect order.");
00800 }
00801 coefficientMatrixArray[spanNumber].row(componentIndex).copy(
00802 basisFunction.getCoefficientArray());
00803 }
00804 }
00805 return coefficientMatrixArray;
00806 }
00807
00808
00809
00810
00811
00812
00813 template <class Type>
00814 Array1D< Array1D<double> >
00815 BSpline<Type>::
00816 getControlPointVectors(size_t order, size_t numberOfNodes,
00817 const Array1D<size_t>& cumulativeKnotCounts,
00818 const std::vector<Type>& controlPoints)
00819 {
00820 size_t numberOfSpans = numberOfNodes - 1;
00821 size_t numberOfBasisFunctions = order + 1;
00822 size_t numberOfKnots;
00823 if(m_isPeriodic) {
00824 numberOfKnots = cumulativeKnotCounts[numberOfNodes - 2];
00825 } else {
00826 numberOfKnots = cumulativeKnotCounts[numberOfNodes - 1];
00827 }
00828 Array1D< Array1D<Type> > controlPointVectorArray(numberOfSpans);
00829 for(size_t spanNumber = 0; spanNumber < numberOfSpans; ++spanNumber) {
00830 Array1D<Type> newVector(numberOfBasisFunctions);
00831 int knotIndex =
00832 static_cast<int>(cumulativeKnotCounts[spanNumber])
00833 - static_cast<int>(numberOfBasisFunctions);
00834 for(size_t controlPointIndex = 0;
00835 controlPointIndex < newVector.size();
00836 ++controlPointIndex) {
00837 if(knotIndex < 0) {
00838 newVector[controlPointIndex] =
00839 controlPoints[numberOfKnots + knotIndex];
00840 } else if(knotIndex >= static_cast<int>(numberOfKnots)) {
00841 newVector[controlPointIndex] =
00842 controlPoints[knotIndex - numberOfKnots];
00843 } else {
00844 newVector[controlPointIndex] = controlPoints[knotIndex];
00845 }
00846 ++knotIndex;
00847 }
00848 controlPointVectorArray[spanNumber] = newVector;
00849 }
00850 return controlPointVectorArray;
00851 }
00852
00853
00854
00855
00856
00857 template <class Type>
00858 double
00859 BSpline<Type>::
00860 getKnotPosition(int knotNumber,
00861 const Array1D<double>& knotPositions)
00862 {
00863 if(knotNumber < 0) {
00864 int wrappedIndex =
00865 static_cast<int>(knotPositions.size()) - 1 + knotNumber;
00866 double offsetDistance = (knotPositions[knotPositions.size() - 1]
00867 - knotPositions[wrappedIndex]);
00868 return knotPositions[0] - offsetDistance;
00869 } else if(knotNumber >= static_cast<int>(knotPositions.size())) {
00870 int wrappedIndex =
00871 knotNumber - static_cast<int>(knotPositions.size()) + 1;
00872 double offsetDistance = (knotPositions[wrappedIndex]
00873 - knotPositions[0]);
00874 return knotPositions[knotPositions.size() - 1] + offsetDistance;
00875 }
00876 return knotPositions[knotNumber];
00877 }
00878
00879
00880
00881
00882 template <class Type>
00883 size_t
00884 BSpline<Type>::
00885 getSpanNumber(double sValue)
00886 {
00887 size_t returnValue;
00888 if(m_isUniform) {
00889 int maximumSpanNumber = static_cast<int>(m_numberOfNodes) - 1;
00890 int spanNumber = int(floor(sValue));
00891 if(m_isPeriodic) {
00892 while(spanNumber < 0) {
00893 spanNumber += maximumSpanNumber;
00894 }
00895 while(spanNumber >= maximumSpanNumber) {
00896 spanNumber -= maximumSpanNumber;
00897 }
00898 } else {
00899 if((spanNumber < 0) || (spanNumber >= maximumSpanNumber)) {
00900 DLR_THROW(ValueException, "BSpline::getSpanNumber()",
00901 "Argument sValue is out of range.");
00902 }
00903 }
00904 returnValue = static_cast<size_t>(spanNumber);
00905 } else {
00906 double maximumSValue = m_nodePositionArray[m_numberOfNodes - 1];
00907 double minimumSValue = m_nodePositionArray[0];
00908 if(m_isPeriodic) {
00909 while(sValue < minimumSValue) {
00910 sValue += (maximumSValue - minimumSValue);
00911 }
00912 while(sValue >= maximumSValue) {
00913 sValue -= (maximumSValue - minimumSValue);
00914 }
00915 } else {
00916 if((sValue < minimumSValue) || (sValue >= maximumSValue)) {
00917 DLR_THROW(ValueException, "BSpline::getSpanNumber()",
00918 "Argument sValue is out of range.");
00919 }
00920 }
00921 Array1D<double>::const_iterator nodeIterator = std::upper_bound(
00922 m_nodePositionArray.begin(), m_nodePositionArray.end(), sValue);
00923 returnValue =
00924 static_cast<size_t>(nodeIterator - m_nodePositionArray.begin());
00925 }
00926 return returnValue;
00927 }
00928
00929
00930
00931
00932 template <class Type>
00933 void
00934 BSpline<Type>::
00935 setNodePositions(const std::vector<double>& nodePositions)
00936 {
00937 if(nodePositions.size() != m_numberOfNodes) {
00938 std::ostringstream message;
00939 message << "Argument nodePositions has "
00940 << nodePositions.size() << " elements, when a(n) "
00941 << m_numberOfNodes << " element vector was expected.";
00942 DLR_THROW(ValueException, "BSpline::setNodePositions()",
00943 message.str().c_str());
00944 }
00945 if(m_nodePositionArray.size() != nodePositions.size()) {
00946 m_nodePositionArray.reinit(nodePositions.size());
00947 }
00948 std::copy(nodePositions.begin(), nodePositions.end(),
00949 m_nodePositionArray.begin());
00950 }
00951
00952
00953 }
00954
00955 }
00956
00957 #endif