Virtual Testbed
Ship dynamics simulator for extreme conditions
cubic_spline.hh
1 #ifndef VTESTBED_GEOMETRY_CUBIC_SPLINE_HH
2 #define VTESTBED_GEOMETRY_CUBIC_SPLINE_HH
3 
4 #include <vector>
5 
6 #include <vtestbed/base/blitz.hh>
7 #include <vtestbed/geometry/curve_segments.hh>
8 #include <vtestbed/geometry/math.hh>
9 
10 namespace vtb {
11 
12  namespace geometry {
13 
14  template <class V, class T>
15  inline V
16  difference(const V& a, const V& b, T delta) {
17  V result = a-b;
18  if (delta > 0) {
19  result /= delta;
20  }
21  return result;
22  }
23 
24  template <class V>
25  inline V
26  difference(const V& a, const V& b) {
27  auto delta = distance(a,b);
28  V result = a-b;
29  if (delta > 0) {
30  result /= delta;
31  }
32  return result;
33  }
34 
35  template <class T, int N=3>
36  class Cubic_spline {
37 
38  public:
39  using value_type = Vertex<T,N>;
40  using reference = value_type&;
41  using const_reference = const value_type&;
43  using size_type = size_t;
45 
46  private:
47  vertex_array _points;
48  segments_type _segments;
49  vertex_array _derivatives;
50 
51  public:
52 
53  inline explicit
54  Cubic_spline(const vertex_array& values):
55  _points(values), _segments(values) {
56  const auto n = values.size();
57  if (n <= 1) { return; }
58  const auto& p = this->_points;
59  auto& dt = this->_segments;
60  auto& m = this->_derivatives;
61  m.reserve(n);
62  m.emplace_back(difference(p[1],p[0],dt[0]));
63  for (size_t i=1; i<n-1; ++i) {
64  m.emplace_back(T{0.5}*(
65  difference(p[i+1],p[i],dt[i]) +
66  difference(p[i],p[i-1],dt[i-1])
67  ));
68  }
69  m.emplace_back(difference(p[n-1],p[n-2],dt[n-2]));
70  dt.normalise();
71  }
72 
73  inline value_type
74  operator()(T t) const {
75  const auto& segments = this->_segments;
76  const auto& points = this->_points;
77  const auto& derivatives = this->_derivatives;
78  if (points.size() == 1) {
79  return points.front();
80  }
81  size_type i0 = 0;
82  segments.locate(t, i0);
83  if (i0 == segments.size()) {
84  return points.back();
85  }
86  size_type i1 = i0+1;
87  const auto& p0 = points[i0];
88  const auto& p1 = points[i1];
89  const auto& m0 = derivatives[i0];
90  const auto& m1 = derivatives[i1];
91  T t1 = (T{1} - t);
92  t1 *= t1;
93  T t2 = t*t;
94  T h00 = (T{1} + T{2}*t)*t1;
95  T h10 = t*t1;
96  T h01 = t2*(T{3} - T{2}*t);
97  T h11 = t2*(t - T{1});
98  return h00*p0 + h10*m0 + h01*p1 + h11*m1;
99  }
100 
101  inline const_reference
102  front() const noexcept {
103  return this->_points.front();
104  }
105 
106  inline const_reference
107  back() const noexcept {
108  return this->_points.back();
109  }
110 
111  inline size_type
112  size() const {
113  return this->_points.size();
114  }
115 
116  inline const vertex_array&
117  derivatives() const {
118  return this->_derivatives;
119  }
120 
121  inline const segments_type&
122  segments() const {
123  return this->_segments;
124  }
125 
126  inline segments_type&
127  segments() {
128  return this->_segments;
129  }
130 
131  inline void
132  range(T min, T max) {
133  this->_segments.range(min, max);
134  }
135 
136  inline T
137  length() const {
138  return this->_segments.length();
139  }
140 
141  inline void
142  smooth_left(value_type p_left) {
143  auto& m = this->_derivatives;
144  const auto& p = this->_points;
145  m[0] = T{0.5}*(difference(p[1],p[0]) + difference(p[0], p_left));
146  }
147 
148  inline void
149  smooth_right(value_type p_right) {
150  auto& m = this->_derivatives;
151  const auto& p = this->_points;
152  const auto n = p.size();
153  m[n-1] = T{0.5}*(difference(p[n-1],p[n-2]) + difference(p[n-1], p_right));
154  }
155 
156  };
157 
158  }
159 
160 }
161 
162 #endif // vim:filetype=cpp
T distance(T... args)
Main namespace.
Definition: convert.hh:9