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