Radium Engine  1.5.0
Spline.hpp
1 #pragma once
2 
3 #include <Core/Containers/VectorArray.hpp>
4 #include <Core/Math/Math.hpp>
5 #include <Core/RaCore.hpp>
6 
7 #include <algorithm>
8 #include <vector>
9 
10 namespace Ra {
11 namespace Core {
12 namespace Geometry {
22 template <uint D, uint K = 2>
23 class Spline
24 {
25  public:
26  enum Type {
27  UNIFORM,
29  };
30 
31  using Vector = typename Eigen::Matrix<Scalar, D, 1>;
32 
33  public:
38  explicit inline Spline( Type type = OPEN_UNIFORM );
39 
41  inline void setCtrlPoints( const Core::VectorArray<Vector>& points );
42 
44  inline const Core::VectorArray<Vector>& getCtrlPoints() const;
45 
47  inline void setType( Type type );
48 
51  inline Vector f( Scalar u ) const;
52 
54  inline Vector df( Scalar u ) const;
55 
56  private:
57  // -------------------------------------------------------------------------
59  // -------------------------------------------------------------------------
60 
61  inline void assertSplines() const;
62 
65  inline void setNodalVector();
66 
68  inline void setNodeToUniform();
69 
71  inline void setNodeToOpenUniform();
72 
83  static inline Vector eval( Scalar u,
84  const Core::VectorArray<Vector>& points,
85  const std::vector<Scalar>& node,
86  uint k,
87  int off = 0 );
88 
89  static inline Vector evalRec( Scalar u,
90  const Core::VectorArray<Vector>& points,
91  const std::vector<Scalar>& node,
92  uint k );
93 
94  // -------------------------------------------------------------------------
96  // -------------------------------------------------------------------------
97 
98  Core::VectorArray<Vector> m_points;
100  std::vector<Scalar> m_node;
101  Type m_type;
102 };
103 
104 template <uint D, uint K>
105 inline Spline<D, K>::Spline( typename Spline<D, K>::Type type ) : m_type( type ) {
106  static_assert( K >= 2, "Order must be at least two" );
107  m_points.resize( K, Vector::Zero() );
108  m_vecs.resize( K - 1, Vector::Zero() );
109  m_node.resize( K + K, 0.f );
110  assertSplines();
111 }
112 
113 // -----------------------------------------------------------------------------
114 
115 template <uint D, uint K>
116 inline void
117 Spline<D, K>::setCtrlPoints( const Core::VectorArray<typename Spline<D, K>::Vector>& points ) {
118  m_points = points;
119  m_vecs.resize( points.size() - 1, Vector::Zero() );
120  for ( uint i = 0; i < m_vecs.size(); ++i ) {
121  m_vecs[i] = m_points[i + 1] - m_points[i];
122  }
123  setNodalVector();
124  assertSplines();
125 
126  for ( uint i = 0; i < m_vecs.size(); ++i ) {
127  m_vecs[i] /= m_node[K + i] - m_node[i + 1];
128  }
129 }
130 
131 // -----------------------------------------------------------------------------
132 
133 template <uint D, uint K>
135  return m_points;
136 }
137 
138 // -----------------------------------------------------------------------------
139 
141 template <uint D, uint K>
142 inline void Spline<D, K>::setType( Type type ) {
143  m_type = type;
144  setNodalVector();
145  assertSplines();
146 }
147 
148 // -----------------------------------------------------------------------------
149 
150 template <uint D, uint K>
151 inline typename Spline<D, K>::Vector Spline<D, K>::f( Scalar u ) const {
152  u = std::clamp( u, Scalar( 0 ), Scalar( 1 ) );
153  return eval( u, m_points, m_node, K );
154 }
155 
156 // -----------------------------------------------------------------------------
157 
158 template <uint D, uint K>
159 inline typename Spline<D, K>::Vector Spline<D, K>::df( Scalar u ) const {
160  u = std::clamp( u, Scalar( 0 ), Scalar( 1 ) );
161  return eval( u, m_vecs, m_node, K - 1, 1 ) * Scalar( K - 1 );
162 }
163 
164 // -----------------------------------------------------------------------------
165 
166 template <uint D, uint K>
167 inline void Spline<D, K>::assertSplines() const {
168  CORE_ASSERT( m_points.size() >= K, "Not enough points" );
169  CORE_ASSERT( m_node.size() == ( K + m_points.size() ), "Wrong nodal vector size" );
170  CORE_ASSERT( m_points.size() == ( m_vecs.size() + 1 ), "Wrong point / diffs size" );
171 }
172 
173 // -----------------------------------------------------------------------------
174 
175 template <uint D, uint K>
176 inline void Spline<D, K>::setNodalVector() {
177  switch ( m_type ) {
178  case OPEN_UNIFORM:
179  setNodeToOpenUniform();
180  break;
181  case UNIFORM:
182  setNodeToUniform();
183  break;
184  }
185 }
186 
187 // -----------------------------------------------------------------------------
188 
189 template <uint D, uint K>
190 inline void Spline<D, K>::setNodeToUniform() {
191  const uint n = m_points.size() - 1;
192  m_node.resize( K + n + 1 );
193 
194  Scalar step = 1.f / Scalar( n - K + 2 );
195  for ( uint i = 0; i < m_node.size(); ++i ) {
196  m_node[i] = Scalar( i ) * step - step * (Scalar)( K - 1 );
197  }
198 }
199 
200 // -----------------------------------------------------------------------------
201 
202 template <uint D, uint K>
203 inline void Spline<D, K>::setNodeToOpenUniform() {
204  m_node.resize( K + m_points.size() );
205 
206  uint acc = 1;
207  for ( uint i = 0; i < m_node.size(); ++i ) {
208  if ( i < K ) { m_node[i] = 0.f; }
209  else if ( i >= ( m_points.size() + 1 ) ) { m_node[i] = 1.f; }
210  else {
211  m_node[i] = Scalar( acc ) / Scalar( m_points.size() + 1 - K );
212  acc++;
213  }
214  }
215 }
216 
217 // -----------------------------------------------------------------------------
218 
219 template <uint D, uint K>
220 inline typename Spline<D, K>::Vector Spline<D, K>::eval( Scalar u,
221  const Core::VectorArray<Vector>& points,
222  const std::vector<Scalar>& node,
223  uint k,
224  int off ) {
225  CORE_ASSERT( k >= 2, "K must be at least 2" );
226  CORE_ASSERT( points.size() >= k, "Not enough points" );
227  uint dec = 0;
228  // TODO: better search with dichotomy ?
229  // TODO: check for overflow
230  while ( u > node[dec + k + off] ) {
231  dec++;
232  }
233 
234  // TODO: use buffers in attributes for better performances ?
235  Core::VectorArray<Vector> pOut( k, Vector::Zero() );
236  for ( uint i = dec, j = 0; i < ( dec + k ); ++i, ++j ) {
237  pOut[j] = points[i];
238  }
239 
240  std::vector<Scalar> nodeOut( k + k - 2, Scalar( 0 ) );
241  for ( uint i = ( dec + 1 ), j = 0; i < ( dec + k + k - 1 ); ++i, ++j ) {
242  nodeOut[j] = node[i + off];
243  }
244  return evalRec( u, pOut, nodeOut, k );
245 }
246 
247 // -----------------------------------------------------------------------------
248 
249 template <uint D, uint K>
250 inline typename Spline<D, K>::Vector Spline<D, K>::evalRec( Scalar u,
251  const Core::VectorArray<Vector>& points,
252  const std::vector<Scalar>& node,
253  uint k ) {
254  if ( points.size() == 1 ) { return points[0]; }
255 
256  // TODO: use buffers in attributes for better performances ?
257  Core::VectorArray<Vector> pOut( k - 1, Vector::Zero() );
258 
259  for ( uint i = 0; i < ( k - 1 ); ++i ) {
260  const Scalar n0 = node[i + k - 1];
261  const Scalar n1 = node[i];
262  const Scalar f0 = ( n0 - u ) / ( n0 - n1 );
263  const Scalar f1 = ( u - n1 ) / ( n0 - n1 );
264 
265  pOut[i] = points[i] * f0 + points[i + 1] * f1;
266  }
267 
268  std::vector<Scalar> nodeOut( node.size() - 2 );
269 
270  for ( uint i = 1; i < node.size() - 1; ++i ) {
271  nodeOut[i - 1] = node[i];
272  }
273  return evalRec( u, pOut, nodeOut, k - 1 );
274 }
275 } // namespace Geometry
276 } // namespace Core
277 } // namespace Ra
Handling spline curves of arbitrary dimensions.
Definition: Spline.hpp:24
@ OPEN_UNIFORM
Connected to the first and last control points.
Definition: Spline.hpp:28
const Core::VectorArray< Vector > & getCtrlPoints() const
Get the control points of the spline.
Definition: Spline.hpp:134
Vector f(Scalar u) const
Definition: Spline.hpp:151
void setCtrlPoints(const Core::VectorArray< Vector > &points)
Set the position of the spline control points.
Definition: Spline.hpp:117
Vector df(Scalar u) const
Evaluate speed of the spline.
Definition: Spline.hpp:159
void setType(Type type)
The the nodal vector type.
Definition: Spline.hpp:142
Spline(Type type=OPEN_UNIFORM)
Definition: Spline.hpp:105
Definition: Cage.cpp:3