Virtual Testbed
Ship dynamics simulator for extreme conditions
basis_spline.hh
1 #ifndef VTESTBED_GEOMETRY_BASIS_SPLINE_HH
2 #define VTESTBED_GEOMETRY_BASIS_SPLINE_HH
3 
4 #include <vtestbed/geometry/types.hh>
5 #include <vtestbed/linalg/linear_algebra.hh>
6 
7 namespace vtb {
8 
9  namespace geometry {
10 
17  template <class T, int N>
18  class Basis_spline {
19 
20  public:
21  using point_type = Vertex<T,N>;
22  using element_type = Vertex<T,N+1>;
23  using knot_type = T;
24  using weight_type = T;
30  using scalar_type = T;
31  using value_type = element_type;
32  using reference = value_type&;
33  using const_reference = const value_type&;
34  using size_type = size_t;
35 
36  private:
37  element_array _elements;
38  knot_array _knots;
39  T _x_min{0}, _x_max{1};
40 
41  public:
42 
43  Basis_spline() = default;
44  ~Basis_spline() = default;
45  Basis_spline(const Basis_spline&) = default;
46  Basis_spline& operator=(const Basis_spline&) = default;
47  Basis_spline(Basis_spline&&) = default;
48  Basis_spline& operator=(Basis_spline&&) = default;
49 
50  inline explicit
51  Basis_spline(element_array elements, knot_array knots):
52  _elements(elements), _knots(knots) {}
53 
54  inline explicit
55  Basis_spline(element_array elements, size_type degree=3):
56  Basis_spline(elements, make_knots(elements.size(), degree)) {}
57 
58  inline explicit
59  Basis_spline(point_array points, weight_array weights, knot_array knots):
60  Basis_spline(make_elements(points, weights), knots) {}
61 
62  inline explicit
63  Basis_spline(point_array points, knot_array knots):
64  Basis_spline(make_elements(points), knots) {}
65 
66  inline explicit
67  Basis_spline(point_array points, weight_array weights, size_type degree=3):
68  Basis_spline(make_elements(points, weights),
69  make_knots(points.size(), degree)) {}
70 
71  inline explicit
72  Basis_spline(point_array points, size_type degree=3):
73  Basis_spline(make_elements(points), degree) {}
74 
75  inline size_type
76  degree() const {
77  const auto m = num_knots()-1;
78  const auto n = num_points()-1;
79  return m-n-1;
80  }
81 
82  inline size_type
83  num_internal_knots() const {
84  const auto p = degree();
85  const auto m = num_knots()-1;
86  return m-p-1-(p+1)+1;
87  }
88 
94  inline element_type
95  operator()(T x) const {
96  const auto& t = this->_knots;
97  const auto& c = this->_elements;
98  const auto p = this->degree();
99  size_type k = 0;
100  locate(x, k);
101  if (k == t.size()) { return c.back(); }
102  if (k >= c.size()) { k = c.size()-1; }
103  element_array d(p+1);
104  for (size_type i=0; i<p+1; ++i) {
105  d[i] = multiply_by_weight(c[i+k-p]);
106  }
107  for (size_type r=1; r<p+1; ++r) {
108  for (size_type j=p; j>r-1; --j) {
109  T t0 = t[j+k-p];
110  T t1 = t[j+1+k-r];
111  T alpha = (x - t0) / (t1 - t0);
112  d[j] = (T{1} - alpha)*d[j-1] + alpha*d[j];
113  }
114  }
115  return divide_by_weight(d[p]);
116  }
117 
118  inline coefficient_array
119  coefficients(T x) const {
120  const auto& t = this->_knots;
121  const auto& c = this->_elements;
122  const auto p = this->degree();
123  auto npoints = c.size();
124  coefficient_array coefs(npoints);
125  size_type k = 0;
126  locate(x, k);
127  if (k == 0) { coefs.front() = 1; return coefs; }
128  if (k == t.size()) { coefs.back() = 1; return coefs; } // TODO ???
129  coefs[k] = 1;
130  for (size_type d=1; d<p+1; ++d) {
131  coefs[k-d] = ((t[k+1]-x) / (t[k+1]-t[k+1-d])) * coefs[k+1-d];
132  for (size_type i=k-d+1; i<k; ++i) {
133  coefs[i] = ((x-t[i]) / (t[i+d]-t[i]))*coefs[i] +
134  ((t[i+d+1]-x) / (t[i+d+1] - t[i+1])) * coefs[i+1];
135  }
136  coefs[k] = ((x-t[k]) / (t[k+d]-t[k]))*coefs[k];
137  }
138  return coefs;
139  }
140 
141  inline void
142  interpolate() {
143  using namespace vtb::linalg;
144  auto& c = this->_elements;
145  int n = c.size();
146  auto delta = T{1}/(n-1);
147  for (int k=0; k<N; ++k) {
148  Square_matrix<T> lhs(blitz::shape(n,n));
149  for (int i=0; i<n; ++i) {
150  const auto t = i*delta;
151  auto coefs = coefficients(t);
152  for (int j=0; j<n; ++j) { lhs(i,j) = coefs[j]; }
153  }
154  Vector<T> rhs(n);
155  for (int i=0; i<n; ++i) { rhs(i) = c[i](k); }
156  rhs = solve(lhs, rhs);
157  for (int i=0; i<n; ++i) { c[i](k) = rhs(i); }
158  }
159  }
160 
161  inline size_type size() const { return this->num_points(); }
162  inline size_type num_points() const { return this->_elements.size(); }
163  inline size_type num_knots() const { return this->_knots.size(); }
164  inline const knot_array& knots() const { return this->_knots; }
165  inline const element_array& elements() const { return this->_elements; }
166 
167  inline void
168  range(T min, T max) {
169  this->_x_min = min;
170  this->_x_max = max;
171  }
172 
173  inline static knot_array
174  make_knots(size_type npoints, size_type degree) {
175  const auto n = npoints-1;
176  if (degree > n) { degree = n; }
177  const auto p = degree;
178  const auto m = n + p + 1;
179  if (m+1 < p+p) {
180  throw std::invalid_argument("too few points");
181  }
182  knot_array knots;
183  knots.reserve(m+1);
184  const auto mreal = m+1-p-p;
185  T delta = T{1}/(mreal-1);
186  for (size_type i=0; i<p; ++i) { knots.emplace_back(0); }
187  for (size_type i=0; i<mreal; ++i) { knots.emplace_back(i*delta); }
188  for (size_type i=0; i<p; ++i) { knots.emplace_back(1); }
189  return knots;
190  }
191 
192  inline static knot_array
193  make_cyclic_knots(size_type npoints, size_type degree) {
194  npoints += degree;
195  const auto p = degree;
196  const auto n = npoints-1;
197  const auto m = n + p + 1;
198  if (m+1 < p+p) {
199  throw std::invalid_argument("too few points");
200  }
201  knot_array knots;
202  knots.reserve(m+1);
203  const auto mreal = m+1-p-p;
204  T delta = T{1}/(mreal-1);
205  for (size_type i=0; i<p; ++i) { knots.emplace_back((p-i)*(-delta)); }
206  for (size_type i=0; i<mreal; ++i) { knots.emplace_back(i*delta); }
207  for (size_type i=0; i<p; ++i) { knots.emplace_back((mreal+i)*delta); }
208  return knots;
209  }
210 
211  inline static element_type
212  to_element(const point_type& p, T weight) {
213  element_type elem;
214  for (int i=0; i<N; ++i) { elem(i) = p(i); }
215  elem(N) = weight;
216  return elem;
217  }
218 
219  inline static point_type
220  to_point(const element_type& elem) {
221  point_type point;
222  for (int i=0; i<N; ++i) { point(i) = elem(i); }
223  return point;
224  }
225 
226  private:
227 
228  inline void
229  locate(T& x, size_type& i) const {
230  x = _x_min + (_x_max - _x_min)*x;
231  const auto& knots = this->_knots;
232  const auto nknots = knots.size();
233  for (i=0; i<nknots; ++i) {
234  if (x < knots[i]) {
235  if (i > 0) { --i; }
236  return;
237  }
238  }
239  }
240 
241  inline static element_array
242  make_elements(const point_array& points, const weight_array& weights) {
243  if (points.size() != weights.size()) {
244  throw std::invalid_argument("bad size");
245  }
246  const auto npoints = points.size();
247  element_array elems;
248  if (npoints == 0) { return elems; }
249  elems.reserve(npoints);
250  for (size_type i=0; i<npoints; ++i) {
251  elems.emplace_back(to_element(points[i], weights[i]));
252  }
253  return elems;
254  }
255 
256  inline static element_array
257  make_elements(const point_array& points) {
258  const auto npoints = points.size();
259  element_array elems;
260  if (npoints == 0) { return elems; }
261  elems.reserve(npoints);
262  for (size_type i=0; i<npoints; ++i) {
263  elems.emplace_back(to_element(points[i], T{1}));
264  }
265  return elems;
266  }
267 
268  inline static element_type
269  multiply_by_weight(element_type elem) {
270  if (elem(N) != T{}) {
271  for (int i=0; i<N; ++i) { elem(i) *= elem(N); }
272  }
273  return elem;
274  }
275 
276  inline static element_type
277  divide_by_weight(element_type elem) {
278  if (elem(N) != T{}) {
279  for (int i=0; i<N; ++i) { elem(i) /= elem(N); }
280  }
281  return elem;
282  }
283 
284  };
285 
291  template <class T, int N>
292  class Closed_basis_spline: public Basis_spline<T,N> {
293 
294  private:
296 
297  public:
298  using typename basis_spline::value_type;
299  using typename basis_spline::scalar_type;
300  using typename basis_spline::size_type;
301  using typename basis_spline::element_array;
302  using typename basis_spline::knot_array;
303 
304  public:
305  inline explicit
306  Closed_basis_spline(element_array elements, size_type degree=3):
307  basis_spline(wrap(elements, degree),
308  basis_spline::make_cyclic_knots(elements.size(), degree)) {}
309 
310  private:
311 
312  inline element_array
313  wrap(element_array c, size_type degree) {
314  for (size_type i=0; i<degree; ++i) { c.emplace_back(c[i]); }
315  return c;
316  }
317 
318  };
319 
320  }
321 
322 }
323 
324 #endif // vim:filetype=cpp
Closed non-uniform rational basis spline (NURBS).
Linear algebra (matrix/vector wrappers and solvers).
T min(T... args)
T max(T... args)
Main namespace.
Definition: convert.hh:9
element_type operator()(T x) const
Uses de Boor's agorithm .
Definition: basis_spline.hh:95
Non-uniform rational basis spline (NURBS).
Definition: basis_spline.hh:18