BSplinebasis
Loading...
Searching...
No Matches
Spline.h
1/*
2 * template<typename T, size_t order>
3 * class Spline
4 *
5 * Represents a spline of dataytype T and order order. The datatype has to
6 * fulfill the following requirements:
7 * - comparisons <, <=, >, >=, == and != have to be implemented.
8 * - arithmetic operators + - * / += -= *= /= have to be implemented
9 * - A pathway must exist, such that integer values of type T can be
10 * constructed via static_cast<T>(int) (e. g. via a constructor taking an int).
11 * BSplines can be generated via the method generateBspline(...).
12 *
13 * All methods accessing two splines assume that these splines are defined on
14 * the same grid (i.e. that both splines have the same interval boundaries
15 * within the intersection of their respective supports). This may also cause
16 * problems when splines are constructed by adding up multiple splines. To be
17 * safe, make sure that the supports of two splines being added overlap at least
18 * in one grid point.
19 *
20 *
21 * ########################################################################
22 * The contents of this file is free and unencumbered software released into the
23 * public domain. For more information, please refer to <http://unlicense.org/>
24 * ########################################################################
25 */
26
27#ifndef BSPLINE_SPLINE_H
28#define BSPLINE_SPLINE_H
29
30#include <bspline/exceptions/BSplineException.h>
31#include <bspline/internal/misc.h>
32#include <bspline/support/Support.h>
33
34#include <algorithm>
35#include <array>
36#include <optional>
37#include <type_traits>
38#include <vector>
39
43namespace bspline {
44
45using namespace support;
46using namespace bspline::exceptions;
47
58template <typename T, size_t order>
60 private:
62 static constexpr size_t ARRAY_SIZE = order + 1;
64 Support<T> _support;
66 std::vector<std::array<T, ARRAY_SIZE>> _coefficients;
67
77 std::optional<size_t> findInterval(const T &x) const {
78 if (_support.size() < 2 || x > _support.back() || x < _support.front())
79 return std::nullopt; // x is not part of the spline's support
80
81 const auto begin = _support.begin();
82 const auto it = std::lower_bound(begin, _support.end(), x);
83
84 // Type of intervalEndIndex is signed.
85 const auto intervalEndIndex = std::distance(begin, it);
86 return std::max<decltype(intervalEndIndex)>(0, intervalEndIndex - 1);
87 };
88
97 void checkValidity(
98 const Support<T> &support,
99 const std::vector<std::array<T, ARRAY_SIZE>> &coefficients) const {
100 const bool isValid =
101 (!support.containsIntervals() && coefficients.size() == 0) ||
102 (support.size() >= 2 &&
103 coefficients.size() == support.numberOfIntervals());
104 if (!isValid) throw BSplineException(ErrorCode::INCONSISTENT_DATA);
105 };
106
112 void checkValidity() const { checkValidity(_support, _coefficients); };
113
121 void setData(Support<T> support,
122 std::vector<std::array<T, ARRAY_SIZE>> coefficients) {
123 checkValidity(support, coefficients);
124 _support = std::move(support);
125 _coefficients = std::move(coefficients);
126 };
127
128 public:
132 using data_type = T;
133
137 static constexpr size_t spline_order = order;
138
146 std::vector<std::array<T, ARRAY_SIZE>> coefficients)
147 : _support(std::move(support)), _coefficients(std::move(coefficients)) {
148 checkValidity(_support, _coefficients);
149 };
150
157 : Spline(Support<T>::createEmpty(std::move(grid)), {}){};
158
165 return _support;
166 };
167
175 const std::vector<std::array<T, ARRAY_SIZE>> &getCoefficients()
178 return _coefficients;
179 };
180
189 T operator()(const T &x) const {
191 const auto intervalIndex = findInterval(x);
192
193 if (!intervalIndex) return static_cast<T>(0);
194
195 const T xm = (_support[*intervalIndex + 1] + _support[*intervalIndex]) /
196 static_cast<T>(2);
197
198 return internal::evaluateInterval(x, _coefficients[*intervalIndex], xm);
199 };
200
209 const T &front() const {
211 return _support.front();
212 };
213
222 const T &back() const {
224 return _support.back();
225 };
226
235 template <size_t order2>
236 bool checkOverlap(const Spline<T, order2> &m2) const {
238 if (!_support.containsIntervals() || !m2.getSupport().containsIntervals())
239 return false;
240 const bool isNotOverlapping = m2.getSupport().back() <= _support.front() ||
241 m2.getSupport().front() >= _support.back();
242 return !isNotOverlapping;
243 }
244
254 bool isZero() const {
256 static const T ZERO = static_cast<T>(0);
257 if (!_support.containsIntervals()) return true;
258 for (const auto &cs : _coefficients) {
259 for (const auto &c : cs) {
260 if (c != ZERO) return false;
261 }
262 }
263 return true;
264 };
265
266 // ######################## Operator definitions ########################
267 // ######################################################################
268
278 return (*this) * (static_cast<T>(1) / d);
279 };
280
290 Spline<T, order> ret(*this);
291 for (auto &cs : ret._coefficients) {
292 for (auto &c : cs) {
293 c *= d;
294 }
295 }
296 return ret;
297 };
298
309 for (auto &cs : _coefficients) {
310 for (auto &c : cs) {
311 c *= d;
312 }
313 }
314 return *this;
315 };
316
327 (*this) *= (static_cast<T>(1) / d);
328 return *this;
329 };
330
337 return (*this) * static_cast<T>(-1);
338 };
339
350 template <size_t ordera>
353 // The case ordera == order should be handled by the default assignment
354 // operator which is automatically generated.
355 static_assert(
356 ordera < order,
357 "The assignment operator is only defined if the order of the rhs "
358 "spline is lower than or equal to that of the lhs spline.");
359
360 std::vector<std::array<T, ARRAY_SIZE>> ncoefficients(
361 a.getCoefficients().size(),
362 internal::make_array<T, ARRAY_SIZE>(static_cast<T>(0)));
363 for (size_t i = 0; i < a.getCoefficients().size(); i++) {
364 const auto &coeffsi = a.getCoefficients()[i];
365 auto &ncoeffsi = ncoefficients[i];
366 for (size_t j = 0; j < coeffsi.size(); j++) ncoeffsi[j] = coeffsi[j];
367 }
368 setData(a.getSupport(), std::move(ncoefficients));
369 return *this;
370 }
371
382 template <size_t ordera>
385 static constexpr size_t NEW_ORDER = order + ordera;
386 static constexpr size_t NEW_ARRAY_SIZE = NEW_ORDER + 1;
387
388 // Will also check whether the two grids are equivalent.
389 Support newSupport = _support.calcIntersection(a.getSupport());
390 const size_t nintervals = newSupport.numberOfIntervals();
391
392 if (nintervals == 0)
393 return Spline<T, NEW_ORDER>(std::move(newSupport), {}); // No overlap
394
395 std::vector<std::array<T, NEW_ARRAY_SIZE>> newCoefficients(
396 nintervals, internal::make_array<T, NEW_ARRAY_SIZE>(static_cast<T>(0)));
397
398 for (size_t i = 0; i < nintervals; i++) {
399 const auto ai = newSupport.absoluteFromRelative(i);
400
401 auto thisRelIndex = _support.intervalIndexFromAbsolute(ai).value();
402 auto aRelIndex = a.getSupport().intervalIndexFromAbsolute(ai).value();
403
404 const auto &thiscoeffs = _coefficients[thisRelIndex];
405 const auto &acoeffs = a.getCoefficients()[aRelIndex];
406 auto &coeffsi = newCoefficients[i];
407
408 for (size_t j = 0; j < order + 1; j++) {
409 for (size_t k = 0; k < ordera + 1; k++) {
410 coeffsi[j + k] += thiscoeffs[j] * acoeffs[k];
411 }
412 }
413 }
414 return Spline<T, NEW_ORDER>(std::move(newSupport),
415 std::move(newCoefficients));
416 }
417
427 template <size_t ordera>
429 const Spline<T, ordera> &a) const {
431 static constexpr size_t NEW_ORDER = std::max(order, ordera);
432 static constexpr size_t NEW_ARRAY_SIZE = NEW_ORDER + 1;
433
434 // Will also check whether the two grids are equivalent.
435 Support newSupport = _support.calcUnion(a.getSupport());
436 const size_t nintervals = newSupport.numberOfIntervals();
437
438 std::vector<std::array<T, NEW_ARRAY_SIZE>> ncoefficients(nintervals);
439
440 for (size_t i = 0; i < nintervals; i++) {
441 const auto absIndex = newSupport.absoluteFromRelative(i);
442 const auto thisRelIndex = _support.intervalIndexFromAbsolute(absIndex);
443 const auto aRelIndex = a.getSupport().intervalIndexFromAbsolute(absIndex);
444
445 if (thisRelIndex && !aRelIndex) {
447 internal::changearraysize<T, order + 1, NEW_ARRAY_SIZE>(
448 _coefficients[*thisRelIndex]);
449 } else if (aRelIndex && !thisRelIndex) {
451 internal::changearraysize<T, ordera + 1, NEW_ARRAY_SIZE>(
452 a.getCoefficients()[*aRelIndex]);
453 } else if (thisRelIndex && aRelIndex) {
454 ncoefficients[i] = internal::add<T, ordera + 1, order + 1>(
455 a.getCoefficients()[*aRelIndex], _coefficients[*thisRelIndex]);
456 } else {
458 internal::make_array<T, NEW_ARRAY_SIZE>(static_cast<T>(0));
459 }
460 }
461 return Spline<T, NEW_ORDER>(std::move(newSupport),
462 std::move(ncoefficients));
463 }
464
476 template <size_t ordera>
479 static_assert(
480 ordera <= order,
481 "The operators += and -= are only defined if the order of the rhs "
482 "spline is lower than or equal to that of the lhs spline.");
483 (*this) = (*this) + a;
484 return *this;
485 }
486
498 template <size_t ordera>
500 (*this) += (static_cast<T>(-1) * a);
501 return *this;
502 }
503
514 template <size_t ordera>
516 const Spline<T, ordera> &a) const {
517 return (*this) + (static_cast<T>(-1) * a);
518 }
519
526 bool operator==(const Spline &other) const {
527 return _support == other._support && _coefficients == other._coefficients;
528 }
529
536 bool operator!=(const Spline &other) const { return !(*this == other); }
537}; // class Spline
538
542template <typename T, size_t ARRAY_SIZE>
543Spline(Support<T> support, std::vector<std::array<T, ARRAY_SIZE>> coefficients)
544 -> Spline<T, ARRAY_SIZE - 1>;
545
546// ################### End of defintion of Spline class ###################
547// ########################################################################
548
558template <typename T, size_t order>
560 return b * d;
561}
562
582template <typename CoeffIter, typename SplineIter>
585 // The spline data type.
586 using Spline = typename std::remove_cv_t<
587 typename std::iterator_traits<SplineIter>::value_type>;
588
589 // The coefficient data type.
590 using T = typename std::remove_cv_t<
591 typename std::iterator_traits<CoeffIter>::value_type>;
592
593 // The order of the spline.
594 constexpr size_t order = Spline::spline_order;
595
596 // Check, the data type of the spline and the coefficients are consistent.
597 static_assert(
598 std::is_same<typename Spline::data_type, T>::value,
599 "Coefficients must be of the same type as the data type of the spline.");
600
601 {
602 // The number of coefficients and splines.
603 const auto coeffsSize = std::distance(coeffsBegin, coeffsEnd);
604 const auto splinesSize = std::distance(splinesBegin, splinesEnd);
605
606 // Check, the data is consistent.
607 if (coeffsSize != splinesSize) {
608 throw BSplineException(
609 ErrorCode::INCONSISTENT_DATA,
610 "The number of coefficients and splines must coincide.");
611 }
612
613 // std::distance() may return negative values.
614 if (coeffsSize <= 0) {
615 throw BSplineException(
616 ErrorCode::MISSING_DATA,
617 "The number of coefficients and splines may not be zero.");
618 }
619 }
620
621 const auto &support0 = splinesBegin->getSupport();
622 std::optional<size_t> startIndex;
623 std::optional<size_t> endIndex;
624
625 // Determine the union of all the splines' supports.
626 for (auto it = splinesBegin; it < splinesEnd; it++) {
627 if (!it->getSupport().hasSameGrid(support0)) {
628 throw BSplineException(ErrorCode::DIFFERING_GRIDS);
629 }
630
631 if (!it->getSupport().empty()) {
632 const size_t si = it->getSupport().getStartIndex();
633 if (!startIndex || si < *startIndex) {
634 startIndex = si;
635 }
636
637 const size_t ei = it->getSupport().getEndIndex();
638 if (!endIndex || ei > *endIndex) {
639 endIndex = ei;
640 }
641 }
642 }
643
644 // Set up support and coefficients vector for the returned spline.
645 Support newSupport(support0.getGrid(), startIndex.value_or(0),
646 endIndex.value_or(0));
647 std::vector<std::array<T, order + 1>> newCoefficients(
648 newSupport.numberOfIntervals(),
649 internal::make_array<T, order + 1>(static_cast<T>(0)));
650
651 auto coeffIt = coeffsBegin;
652 auto splineIt = splinesBegin;
653 while (splineIt < splinesEnd) {
654 // Get spline and coefficient.
655 const auto &spline = *splineIt;
656 const T coeff = *coeffIt;
657
658 for (size_t j = 0; j < spline.getSupport().numberOfIntervals(); j++) {
659 // Index of the interval relative to the global grid.
660 const size_t absoluteIndex = spline.getSupport().absoluteFromRelative(j);
661
662 // Index of the interval relative to the newSupport.
663 const size_t newSupportIndex =
664 newSupport.intervalIndexFromAbsolute(absoluteIndex).value();
665
666 const std::array<T, order + 1> &splineCoeffs =
667 spline.getCoefficients().at(j);
668 std::array<T, order + 1> &newCoeffs = newCoefficients.at(newSupportIndex);
669
670 for (size_t k = 0; k < order + 1; k++) {
672 }
673 }
674
675 splineIt++;
676 coeffIt++;
677 }
678
679 return Spline(std::move(newSupport), std::move(newCoefficients));
680}
681
698template <typename CoeffCollection, typename SplineCollection>
704
705#ifndef BSPLINE_DOXYGEN_IGNORE
712template <typename S>
713struct is_spline : std::false_type {};
714
722template <typename T, size_t order>
723struct is_spline<Spline<T, order>> : std::true_type {};
724#endif // BSPLINE_DOXYGEN_IGNORE
725
730template <typename S>
731inline constexpr bool is_spline_v = is_spline<S>::value;
732
733} // namespace bspline
734#endif // BSPLINE_SPLINE_H
The central Spline class of the library.
Definition Spline.h:59
Spline< T, order > & operator*=(const T &d)
In-place scalar-multiplication operator.
Definition Spline.h:307
bool checkOverlap(const Spline< T, order2 > &m2) const
Checks whether the supports of the two splines overlap.
Definition Spline.h:236
T operator()(const T &x) const
Evaluates the spline at point x.
Definition Spline.h:189
bool isZero() const
Checks whether this spline is a zero over the whole number line.
Definition Spline.h:254
const std::vector< std::array< T, ARRAY_SIZE > > & getCoefficients() const noexcept
The spline's polynomial coefficients.
Definition Spline.h:175
bool operator==(const Spline &other) const
Compares two splines for equality.
Definition Spline.h:526
static constexpr size_t spline_order
The order of the spline.
Definition Spline.h:137
Spline< T, order > & operator=(const Spline< T, ordera > &a)
Copy assign operator.
Definition Spline.h:351
Spline< T, std::max(order, ordera)> operator+(const Spline< T, ordera > &a) const
Spline addition operator.
Definition Spline.h:428
const T & back() const
Returns the end of the spline's support.
Definition Spline.h:222
Spline< T, order > & operator/=(const T &d)
In-place scalar-division operator.
Definition Spline.h:325
const Support< T > & getSupport() const noexcept
Returns the spline's support.
Definition Spline.h:163
Spline< T, order > & operator+=(const Spline< T, ordera > &a)
In-place addition operator.
Definition Spline.h:477
const T & front() const
Returns the begin of the spline's support.
Definition Spline.h:209
bool operator!=(const Spline &other) const
Compares two splines for inequality.
Definition Spline.h:536
Spline< T, order+ordera > operator*(const Spline< T, ordera > &a) const
Spline-spline multiplication operator.
Definition Spline.h:383
Spline(Grid< T > grid)
Constructs an empty spline on the global grid.
Definition Spline.h:156
Spline< T, order > operator-() const
Unary minus operator.
Definition Spline.h:335
Spline< T, order > & operator-=(const Spline< T, ordera > &a)
Binary in-place subtraction operator.
Definition Spline.h:499
Spline< T, order > operator/(const T &d) const
Scalar-division operator.
Definition Spline.h:276
Spline< T, order > operator*(const T &d) const
Scalar-multiplication operator.
Definition Spline.h:288
Spline< T, std::max(order, ordera)> operator-(const Spline< T, ordera > &a) const
Binary subtraction operator.
Definition Spline.h:515
Spline(Support< T > support, std::vector< std::array< T, ARRAY_SIZE > > coefficients)
Constructor setting the data. Performs sanity checks.
Definition Spline.h:145
The main exception class.
Definition BSplineException.h:84
Represents a global Grid.
Definition Grid.h:27
const T & at(size_t i) const
Gives access to the i-th element of the Grid.
Definition Grid.h:218
const_iterator end() const
Returns the end iterator of the Grid..
Definition Grid.h:265
size_t size() const
Returns the number of elements of the Grid.
Definition Grid.h:169
bool empty() const
Checks whether the spline is empty.
Definition Grid.h:192
const_iterator begin() const
Returns the begin iterator of the Grid.
Definition Grid.h:256
const T & back() const
Returns a reference to the last element of the Grid.
Definition Grid.h:244
const T & front() const
Returns a reference to the first element of the Grid.
Definition Grid.h:231
Represents the Spline's Support.
Definition Support.h:33
Exceptions and error codes.
Definition BSplineException.h:19
Main namespace for this library.
Definition BSplineGenerator.h:21
Spline< T, order > operator*(const T &d, const Spline< T, order > &b)
Commutation of spline scalar multiplication operator.
Definition Spline.h:559
constexpr bool is_spline_v
Definition Spline.h:731
auto linearCombination(CoeffIter coeffsBegin, CoeffIter coeffsEnd, SplineIter splinesBegin, SplineIter splinesEnd)
Calculates the linear combination.
Definition Spline.h:583