Motive Animation System
An open source project by FPL.
 All Classes Functions Variables Typedefs Friends Pages
compact_spline.h
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef MOTIVE_MATH_COMPACT_SPLINE_H_
16 #define MOTIVE_MATH_COMPACT_SPLINE_H_
17 
18 #include "motive/common.h"
19 #include "motive/math/compact_spline_node.h"
20 #include "motive/math/curve.h"
21 
22 namespace motive {
23 
24 class BulkSplineEvaluator;
25 
26 /// @typedef CompactSplineIndex
27 /// Index into the spline. Some high values have special meaning (see below).
28 typedef uint16_t CompactSplineIndex;
29 static const CompactSplineIndex kInvalidSplineIndex =
30  static_cast<CompactSplineIndex>(-1);
31 static const CompactSplineIndex kBeforeSplineIndex =
32  static_cast<CompactSplineIndex>(-2);
33 static const CompactSplineIndex kAfterSplineIndex =
34  static_cast<CompactSplineIndex>(-3);
35 static const CompactSplineIndex kMaxSplineIndex =
36  static_cast<CompactSplineIndex>(-4);
37 
38 /// Return true if `index` is not an index into the spline.
39 inline bool OutsideSpline(CompactSplineIndex index) {
40  return index >= kAfterSplineIndex;
41 }
42 
43 enum CompactSplineAddMethod {
44  kAddWithoutModification, /// Add node straight-up. No changes.
45  kEnsureCubicWellBehaved, /// Insert an intermediate node, if required,
46  /// to ensure cubic splines have uniform curvature.
47 };
48 
49 /// @struct UncompressedNode
50 /// @brief Float representation of a point on the spline.
51 ///
52 /// This node represents the x, y, and derivative values of a data point.
53 /// Users can pass in an array of such nodes to CompactSpline::InitFromNodes().
54 /// Useful when you want to specify a reasonably short spline in code.
56  float x;
57  float y;
58  float derivative;
59 };
60 
61 /// @class CompactSpline
62 /// @brief Represent a smooth curve in a small amount of memory.
63 ///
64 /// This spline interpolates a series of (x, y, derivative) nodes to create a
65 /// smooth curve.
66 ///
67 /// This class holds a series of such nodes, and aids with the construction of
68 /// that series by inserting extra nodes when extra smoothness is required.
69 ///
70 /// The data in this class is compacted as quantized values. It's not intended
71 /// to be read directly. You should use the BulkSplineEvaluator to update
72 /// and read values from the splines in a performant manner.
74  public:
75  /// When a `CompactSpline` is created on the stack, it will have this many
76  /// nodes. This amount is sufficient for the vast majority of cases where
77  /// you are procedurally generating a spline. We used a fixed number instead
78  /// of an `std::vector` to avoid dynamic memory allocation.
79  static const CompactSplineIndex kDefaultMaxNodes = 7;
80 
82  : x_granularity_(0.0f), num_nodes_(0), max_nodes_(kDefaultMaxNodes) {}
83  CompactSpline(const Range& y_range, const float x_granularity)
84  : max_nodes_(kDefaultMaxNodes) {
85  Init(y_range, x_granularity);
86  }
87  CompactSpline(const CompactSpline& rhs) : max_nodes_(kDefaultMaxNodes) {
88  *this = rhs;
89  }
90  CompactSpline& operator=(const CompactSpline& rhs) {
91  assert(rhs.num_nodes_ <= max_nodes_);
92  y_range_ = rhs.y_range_;
93  x_granularity_ = rhs.x_granularity_;
94  num_nodes_ = rhs.num_nodes_;
95  memcpy(nodes_, rhs.nodes_, rhs.num_nodes_ * sizeof(nodes_[0]));
96  return *this;
97  }
98 
99  /// The range of values for x and y must be specified at spline creation time
100  /// and cannot be changed afterwards. Empties all nodes, if we have any.
101  ///
102  /// @param y_range The upper and lower bounds for y-values in the nodes.
103  /// The more narrow this is, the better the precision of the
104  /// fixed point numbers. Note that you should add 10% padding
105  /// here, since AddNode may insert a smoothing node that is
106  /// slightly beyond the source y range.
107  /// @param x_granularity The minimum increment of x-values. If you're working
108  /// with a spline changes at most 30 times per second,
109  /// and your x is in units of 1/1000th of a second, then
110  /// x_granularity = 33 is a good baseline. You'll
111  /// probably want granularity around 1/50th of that
112  /// baseline value, though, since AddNode may insert
113  /// smoothing nodes at intermediate x's.
114  /// In our example here, you could set
115  /// x_granularity near 33 / 50. For ease of debugging,
116  /// an x_granularity of 0.5 or 1 is probably best.
117  void Init(const Range& y_range, const float x_granularity) {
118  num_nodes_ = 0;
119  y_range_ = y_range;
120  x_granularity_ = x_granularity;
121  }
122 
123  /// Initialize the CompactSpline and add curve in the `nodes` array.
124  ///
125  /// @param nodes An array of uncompressed nodes.
126  /// @param num_nodes Length of the `nodes` array.
127  void InitFromNodes(const UncompressedNode* nodes, size_t num_nodes);
128 
129  /// Evaluate `spline` at uniform x intervals, where the distance between
130  /// consecutive x's is spline.LengthX() / (max_nodes() - 1). Initialize
131  /// this spline with the results.
132  ///
133  /// @param spline The source spline to evaluate at uniform x intervals.
134  void InitFromSpline(const CompactSpline& spline);
135 
136  /// Add a node to the end of the spline. Depending on the method, an
137  /// intermediate node may also be inserted.
138  ///
139  /// @param x Must be greater than the x-value of the last spline node. If not,
140  /// this call is a nop.
141  /// @param y Must be within the `y_range` specified in Init().
142  /// @param derivative No restrictions, but excessively large values may still
143  /// result in overshoot, even with an intermediate node.
144  /// @param method If kAddWithoutModification, adds the node and does nothing
145  /// else. If kEnsureCubicWellBehaved, adds the node and
146  /// (if required) inserts another node in the middle so that
147  /// the individual cubics have uniform curvature.
148  /// Uniform curvature means always curving upward or always
149  /// curving downward. See docs/dual_cubics.pdf for details.
150  void AddNode(const float x, const float y, const float derivative,
151  const CompactSplineAddMethod method = kEnsureCubicWellBehaved);
152 
153  /// Add values without converting them. Useful when initializing from
154  /// precalculated data.
155  void AddNodeVerbatim(const CompactSplineXGrain x, const CompactSplineYRung y,
156  const CompactSplineAngle angle) {
158  }
159 
160  /// Compress `nodes` and append them to the spline.
161  ///
162  /// @param nodes An array of uncompressed nodes.
163  /// @param num_nodes Length of the `nodes` array.
165 
166  /// Indicate that we have stopped adding nodes and want to release the
167  /// remaining memory. Useful for when we have one giant buffer from which
168  /// we want to add many splines of (potentially unknown) various sizes.
169  /// We can do something like,
170  /// \code{.cpp}
171  /// size_t CreateSplines(char* memory_buffer, size_t memory_buffer_size) {
172  /// char* buf = memory_buffer;
173  /// const char* end = memory_buffer + memory_buffer_size;
174  ///
175  /// while (MoreSplinesToCreate()) {
176  /// // Allocate a spline that can hold as many nodes as buf can hold.
177  /// CompactSpline* spline =
178  /// CompactSpline::CreateInPlaceMaxNodes(buf, end - buf);
179  ///
180  /// while (MoreNodesToAdd()) {
181  /// // Ensure we haven't reached the end of the buffer.
182  /// if (spline->num_splines() == spline->max_splines()) break;
183  ///
184  /// // ... spline creation logic ...
185  /// spline->AddNode(...);
186  /// }
187  ///
188  /// // Shrink `spline` to be the size that it actually is.
189  /// spline->Finalize();
190  ///
191  /// // Advance pointer so next spline starts where this one ends.
192  /// buf += spline->Size();
193  /// }
194  ///
195  /// // Return the total bytes consumed from `memory_buffer`.
196  /// return end - buf;
197  /// }
198  /// \endcode
199  void Finalize() {
200  max_nodes_ = num_nodes_;
201  }
202 
203  /// Remove all nodes from the spline.
204  void Clear() { num_nodes_ = 0; }
205 
206  /// Returns the memory occupied by this spline.
207  size_t Size() const { return Size(max_nodes_); }
208 
209  /// Use on an array of splines created by CreateArrayInPlace().
210  /// Returns the next spline in the array.
211  CompactSpline* Next() { return NextAtIdx(1); }
212  const CompactSpline* Next() const { return NextAtIdx(1); }
213 
214  /// Use on an array of splines created by CreateArrayInPlace().
215  /// Returns the idx'th spline in the array.
217  // Use union to avoid potential aliasing bugs.
218  union {
219  CompactSpline* spline;
220  uint8_t* ptr;
221  } p;
222  p.spline = this;
223  p.ptr += idx * Size();
224  return p.spline;
225  }
226  const CompactSpline* NextAtIdx(int idx) const {
227  return const_cast<CompactSpline*>(this)->NextAtIdx(idx);
228  }
229 
230  /// Return index of the first node before `x`.
231  /// If `x` is before the first node, return kBeforeSplineIndex.
232  /// If `x` is past the last node, return kAfterSplineIndex.
233  ///
234  /// @param x x-value in the spline. Most often, the x-axis represents time.
235  /// @param guess_index Best guess at what the index for `x` will be.
236  /// Often the caller will be traversing from low to high x,
237  /// so a good guess is the index after the current index.
238  /// If you have no idea, set to 0.
239  CompactSplineIndex IndexForX(const float x,
240  const CompactSplineIndex guess_index) const;
241 
242  /// If `repeat` is true, loop to x = 0 when `x` >= EndX().
243  /// If `repeat` is false, same as IndexForX().
244  CompactSplineIndex IndexForXAllowingRepeat(
245  const float x, const CompactSplineIndex guess_index,
246  const bool repeat, float* final_x) const;
247 
248  /// Returns closest index between 0 and NumNodes() - 1.
249  /// Clamps `x` to a value in the range of index.
250  /// `index` must be a valid value: i.e. kBeforeSplineIndex, kAfterSplineIndex,
251  /// or between 0..NumNodes()-1.
252  CompactSplineIndex ClampIndex(const CompactSplineIndex index, float* x) const;
253 
254  // First and last x, y, and derivatives in the spline.
255  float StartX() const { return Front().X(x_granularity_); }
256  float StartY() const { return Front().Y(y_range_); }
257  float StartDerivative() const { return nodes_[0].Derivative(); }
258 
259  float EndX() const { return Back().X(x_granularity_); }
260  float EndY() const { return Back().Y(y_range_); }
261  float EndDerivative() const { return Back().Derivative(); }
262  float NodeX(const CompactSplineIndex index) const;
263  float NodeY(const CompactSplineIndex index) const;
264  float NodeDerivative(const CompactSplineIndex index) const {
265  assert(index < num_nodes_);
266  return nodes_[index].Derivative();
267  }
268  float LengthX() const { return EndX() - StartX(); }
269  Range RangeX() const { return Range(StartX(), EndX()); }
270  const Range& RangeY() const { return y_range_; }
271 
272  /// Calls CalculatedSlowly at `x`, with `kCurveValue` to evaluate the y value.
273  /// If calling from inside a loop, replace the loop with one call to Ys(),
274  /// which is significantly faster.
275  float YCalculatedSlowly(const float x) const {
276  return CalculatedSlowly(x, kCurveValue);
277  }
278 
279  /// Evaluate spline at `x`. This function is somewhat slow because it
280  /// must find the node for `x` and create the cubic before the returned
281  /// value can be evaluated.
282  float CalculatedSlowly(const float x, const CurveValueType value_type) const;
283 
284  /// Fast evaluation of a subset of the x-domain of the spline.
285  /// Spline is evaluated from `start_x` and subsequent intervals of `delta_x`.
286  /// Evaluated values are returned in `ys` and, if not nullptr, `derivatives`.
287  void Ys(const float start_x, const float delta_x, const size_t num_points,
288  float* ys, float* derivatives = nullptr) const;
289 
290  /// The start and end x-values covered by the segment after `index`.
291  Range RangeX(const CompactSplineIndex index) const;
292 
293  /// Initialization parameters for a cubic curve that starts at `index` and
294  /// ends at `index` + 1. Or a constant curve if `index` is kBeforeSplineIndex
295  /// or kAfterSplineIndex.
296  CubicInit CreateCubicInit(const CompactSplineIndex index) const;
297 
298  /// Returns the index of the last node in the spline.
299  CompactSplineIndex LastNodeIndex() const {
300  assert(num_nodes_ >= 1);
301  return num_nodes_ - 1;
302  }
303 
304  /// Returns the start index of the last segment in the spline.
305  CompactSplineIndex LastSegmentIndex() const {
306  assert(num_nodes_ >= 2);
307  return num_nodes_ - 2;
308  }
309 
310  /// Returns the number of nodes in this spline.
311  CompactSplineIndex num_nodes() const { return num_nodes_; }
312  CompactSplineIndex max_nodes() const { return max_nodes_; }
313 
314  /// Return const versions of internal values. For serialization.
315  const detail::CompactSplineNode* nodes() const { return nodes_; }
316  const Range& y_range() const { return y_range_; }
317  float x_granularity() const { return x_granularity_; }
318 
319  /// Allocate memory for a spline using global `new`.
320  /// @param max_nodes The maximum number of nodes that this spline class
321  /// can hold. Memory is allocated so that these nodes are
322  /// held contiguously in memory with the rest of the
323  /// class.
324  static CompactSpline* Create(CompactSplineIndex max_nodes) {
325  uint8_t* buffer = new uint8_t[Size(max_nodes)];
326  return CreateInPlace(max_nodes, buffer);
327  }
328 
329  /// Create a CompactSpline in the memory provided by `buffer`.
330  /// @param buffer chunk of memory of size CompactSpline::Size(max_nodes)
331  ///
332  /// Useful for creating small splines on the stack.
333  static CompactSpline* CreateInPlace(CompactSplineIndex max_nodes,
334  void* buffer) {
335  CompactSpline* spline = new (buffer) CompactSpline();
336  spline->max_nodes_ = max_nodes;
337  return spline;
338  }
339 
340  /// Allocate memory using global `new`, and initialize it with `nodes`.
341  /// @param nodes An array holding the curve, in uncompressed floats.
342  /// @param num_nodes The length of the `nodes` array, and max nodes in the
343  /// returned spline.
345  size_t num_nodes) {
346  assert(num_nodes <= kMaxSplineIndex);
347  CompactSpline* spline = Create(static_cast<CompactSplineIndex>(num_nodes));
348  spline->InitFromNodes(nodes, num_nodes);
349  return spline;
350  }
351 
352  /// Create a CompactSpline from `nodes` in the memory provided by `buffer`.
353  /// @param nodes array of node data, uncompressed as floats.
354  /// @param num_nodes length of the `nodes` array.
355  /// @param buffer chunk of memory of size CompactSpline::Size(num_nodes).
356  ///
357  /// The returned CompactSpline does not need to be destroyed, but once the
358  /// backing memory `buffer` disappears (e.g. if `buffer` is an array on the
359  /// stack), you must stop referencing the returned CompactSpline.
361  size_t num_nodes, void* buffer) {
362  assert(num_nodes <= kMaxSplineIndex);
363  CompactSpline* spline =
364  CreateInPlace(static_cast<CompactSplineIndex>(num_nodes), buffer);
365  spline->InitFromNodes(nodes, num_nodes);
366  return spline;
367  }
368 
369  /// Allocate memory using global `new`, and initialize it by evaluating
370  /// `source_spline` at a uniform x-interval.
371  /// @param source_spline Spline to evaluate. The curve in the returned spline
372  /// matches `source_spline` with its x points spaced
373  /// uniformly.
374  /// @param num_nodes The number of uniform x-intervals in the returned spline.
375  /// Also the max_nodes of the returned spline.
376  static CompactSpline* CreateFromSpline(const CompactSpline& source_spline,
377  size_t num_nodes) {
378  assert(num_nodes <= kMaxSplineIndex);
379  CompactSpline* spline = Create(static_cast<CompactSplineIndex>(num_nodes));
380  spline->InitFromSpline(source_spline);
381  return spline;
382  }
383 
384  /// Create a CompactSpline from `source_spline` in the memory provided by
385  /// `buffer`.
386  /// @param source_spline Spline to evaluate. The curve in the returned spline
387  /// matches `source_spline` with its x points spaced
388  /// uniformly.
389  /// @param num_nodes The number of uniform x-intervals in the returned spline.
390  /// Also the max_nodes of the returned spline.
391  /// @param buffer chunk of memory of size CompactSpline::Size(num_nodes).
393  const CompactSpline& source_spline, size_t num_nodes, void* buffer) {
394  assert(num_nodes <= kMaxSplineIndex);
395  CompactSpline* spline =
396  CreateInPlace(static_cast<CompactSplineIndex>(num_nodes), buffer);
397  spline->InitFromSpline(source_spline);
398  return spline;
399  }
400 
401  /// Deallocate the splines memory using global `delete`.
402  /// Be sure to call this for every spline returned from @ref Create(),
403  /// @ref CreateFromNodes(), @ref CreateFromSpline().
404  static void Destroy(CompactSpline* spline) {
405  if (spline == nullptr) return;
406  // By design, spline does not have a destructor.
407  delete[] reinterpret_cast<uint8_t*>(spline);
408  }
409 
410  /// Allocate an array of splines, contiguous in memory, each of which can
411  /// hold up to `max_nodes`. Use the global `new` operator to allocate the
412  /// memory buffer.
413  ///
414  /// This function is useful when passing several-dimensions-worth of splines
415  /// to MotivatorNf::SetSplines(), for example Motivator3f::SetSplines() takes
416  /// an array of three splines, like this function returns.
417  static CompactSpline* CreateArray(CompactSplineIndex max_nodes,
418  int num_splines) {
419  uint8_t* buffer = new uint8_t[Size(max_nodes) * num_splines];
420  return CreateArrayInPlace(max_nodes, num_splines, buffer);
421  }
422 
423  /// Allocates `num_splines` CompactSplines contiguously in memory, and returns
424  /// a pointer to the first spline.
425  /// Each spline is the same size. Access the next Spline with Next().
426  /// @param buffer chuck of memory of size
427  /// CompactSpline::Size(max_nodes) * num_splines
428  ///
429  /// The returned CompactSpline array does not need to be destroyed, but once
430  /// the backing memory `buffer` disappears (e.g. if `buffer` is an array on
431  /// the stack), you must stop referencing the returned CompactSpline array.
432  static CompactSpline* CreateArrayInPlace(CompactSplineIndex max_nodes,
433  int num_splines, void* buffer) {
434  const size_t size = Size(max_nodes);
435  uint8_t* b = reinterpret_cast<uint8_t*>(buffer);
436  for (int i = 0; i < num_splines; ++i) {
437  CreateInPlace(max_nodes, b);
438  b += size;
439  }
440  return reinterpret_cast<CompactSpline*>(buffer);
441  }
442 
443  /// Frees the memory allocated with CreateArray() using global `delete`.
444  static void DestroyArray(CompactSpline* splines, int /*num_splines*/) {
445  if (splines == nullptr) return;
446  // By design, spline does not have a destructor.
447  delete[] reinterpret_cast<uint8_t*>(splines);
448  }
449 
450  /// Returns the size, in bytes, of a CompactSpline class with `max_nodes`
451  /// nodes.
452  ///
453  /// This function is useful when you want to provide your own memory buffer
454  /// for splines, and then pass that buffer into CreateInPlace(). Your memory
455  /// buffer must be at least Size().
456  static size_t Size(CompactSplineIndex max_nodes) {
457  // Total size of the class must be rounded up to the nearest alignment
458  // so that arrays of the class are properly aligned.
459  // Largest type in the class is a float.
460  const size_t kAlignMask = sizeof(float) - 1;
461  const size_t size =
462  kBaseSize + max_nodes * sizeof(detail::CompactSplineNode);
463  const size_t aligned = (size + kAlignMask) & ~kAlignMask;
464  return aligned;
465  }
466 
467  /// Returns the size, in bytes, of an array of CompactSplines (as allocated
468  /// with CreateArray(), say).
469  ///
470  /// This function is useful when allocating a buffer for splines on your own,
471  /// from which you can then call CreateArrayInPlace().
472  static size_t ArraySize(size_t num_splines, size_t num_nodes) {
473  return num_splines * kBaseSize +
474  num_nodes * sizeof(detail::CompactSplineNode);
475  }
476 
477  /// Recommend a granularity given a maximal-x value. We want to have the
478  /// most precise granularity when quantizing x's.
479  static float RecommendXGranularity(const float max_x);
480 
481  /// Callback interface for BulkEvaluate(). AddPoint() will be called
482  /// `num_points` times, once for every x = start_x + n * delta_x,
483  /// where n = 0..num_points-1.
484  class BulkOutput {
485  public:
486  virtual ~BulkOutput() {}
487  virtual void AddPoint(int point_index,
488  const BulkSplineEvaluator& evaluator) = 0;
489  };
490 
491  /// Called by BulkYs with the an additional BulkOutputInterface
492  /// parameter. BulkOutputInterface specifies the type of evaluations
493  /// on the splines.
494  static void BulkEvaluate(const CompactSpline* const splines,
495  const size_t num_splines, const float start_x,
496  const float delta_x, const size_t num_points,
497  BulkOutput* out);
498 
499  /// Fast evaluation of several splines.
500  /// @param splines input splines of length `num_splines`.
501  /// @param num_splines number of splines to evaluate.
502  /// @param start_x starting point for every spline.
503  /// @param delta_x increment for each output y.
504  /// @param num_points the upper dimension of the `ys` and `derivatives`
505  /// arrays.
506  /// @param ys two dimensional output array, ys[num_points][num_splines].
507  /// ys[0] are `splines` evaluated at start_x.
508  /// ys[num_points - 1] are `splines` evaluated at
509  /// start_x + delta_x * num_points.
510  /// @param derivatives two dimensional output array, with the same indexing
511  /// as `ys`.
512  static void BulkYs(const CompactSpline* const splines,
513  const size_t num_splines, const float start_x,
514  const float delta_x, const size_t num_points, float* ys,
515  float* derivatives = nullptr);
516 
517  /// Fast evaluation of several splines, with mathfu::VectorPacked interface.
518  /// Useful for evaluate three splines which together form a mathfu::vec3,
519  /// for instance.
520  template <int kDimensions>
521  static void BulkYs(const CompactSpline* const splines, const float start_x,
522  const float delta_x, const size_t num_ys,
523  mathfu::VectorPacked<float, kDimensions>* ys) {
524  BulkYs(splines, kDimensions, start_x, delta_x, num_ys,
525  reinterpret_cast<float*>(ys));
526  }
527 
528  private:
529  static const size_t kBaseSize;
530 
531  /// All other AddNode() functions end up calling this one.
532  void AddNodeVerbatim(const detail::CompactSplineNode& node) {
533  assert(num_nodes_ < max_nodes_);
534  nodes_[num_nodes_++] = node;
535  }
536 
537  /// Return true iff `x` is between the the nodes at `index` and `index` + 1.
538  bool IndexContainsX(const CompactSplineXGrain compact_x,
539  const CompactSplineIndex index) const;
540 
541  /// Search the nodes to find the index of the first node before `x`.
542  CompactSplineIndex BinarySearchIndexForX(
543  const CompactSplineXGrain compact_x) const;
544 
545  /// Return e.x - s.x, converted from quantized to external units.
546  float WidthX(const detail::CompactSplineNode& s,
547  const detail::CompactSplineNode& e) const {
548  return (e.x() - s.x()) * x_granularity_;
549  }
550 
551  /// Create the initialization parameters for a cubic running from `s` to `e`.
552  CubicInit CreateCubicInit(const detail::CompactSplineNode& s,
553  const detail::CompactSplineNode& e) const;
554 
555  const detail::CompactSplineNode& Front() const {
556  assert(num_nodes_ > 0);
557  return nodes_[0];
558  }
559 
560  const detail::CompactSplineNode& Back() const {
561  assert(num_nodes_ > 0);
562  return nodes_[num_nodes_ - 1];
563  }
564 
565  /// Extreme values for y. See comments on Init() for details.
566  Range y_range_;
567 
568  /// Minimum increment for x. See comments on Init() for details.
569  float x_granularity_;
570 
571  /// Length of the `nodes_` array.
572  CompactSplineIndex num_nodes_;
573 
574  /// Maximum length of the `nodes_` array. This may be different from
575  /// `kDefaultMaxNodes` if CreateInPlace() was called.
576  CompactSplineIndex max_nodes_;
577 
578  /// Array of key points (x, y, derivative) that describe the curve.
579  /// The curve is interpolated smoothly between these key points.
580  /// Key points are stored in quantized form, and converted back to world
581  /// co-ordinates by using `y_range_` and `x_granularity_`.
582  /// Note: This array can be longer or shorter than kDefaultMaxNodes if
583  /// the class was created with CreateInPlace(). The actual length of
584  /// this array is stored in max_nodes_.
585  detail::CompactSplineNode nodes_[kDefaultMaxNodes];
586 };
587 
588 /// @class SplinePlayback
589 /// @brief Parameters to specify how a spline should be traversed.
591  /// Initialize all channels with same spline.
592  /// Especially useful when kDimensions = 1, since there is only one channel.
593  explicit SplinePlayback(float start_x = 0.0f, bool repeat = false,
594  float playback_rate = 1.0f, float blend_x = 0.0f,
595  float y_offset = 0.0f, float y_scale = 1.0f)
596  : start_x(start_x),
597  blend_x(blend_x),
600  y_scale(y_scale),
601  repeat(repeat) {}
602 
603  /// The starting point from which to play.
604  float start_x;
605 
606  /// The point at which to be 100% in this spline. We create a smooth spline
607  /// from the current state to the spline state that lasts for `blend_x`.
608  float blend_x;
609 
610  /// The playback rate of the spline. Scales `delta_time` of the update to
611  /// to x-axis of `splines`.
612  /// 0 ==> paused
613  /// 0.5 ==> half speed (slow motion)
614  /// 1 ==> authored speed
615  /// 2 ==> double speed (fast forward)
617 
618  /// Offset that we add to spline to shift it along the y-axis.
619  /// The spline is scaled first by `y_scale`, and then shifted by `y_offset`.
620  float y_offset;
621 
622  /// Factor by which we scale the spline along the y-axis.
623  /// The spline is scaled first by `y_scale`, and then shifted by `y_offset`.
624  float y_scale;
625 
626  /// If true, start back at the beginning after we reach the end.
627  bool repeat;
628 };
629 
630 struct SplineState {
631  float y;
632  float derivative;
633 
634  SplineState() : y(0.0f), derivative(0.0f) {}
635  SplineState(float y, float derivative) : y(y), derivative(derivative) {}
636 };
637 
638 struct SplineBlend {
639  SplineState current;
640  const CompactSpline* spline;
641 
642  SplineBlend() : spline(nullptr) {}
643  SplineBlend(const SplineState& current, const CompactSpline& spline)
644  : current(current), spline(&spline) {}
645 };
646 
647 } // namespace motive
648 
649 #endif // MOTIVE_MATH_COMPACT_SPLINE_H_
float y_scale
Definition: compact_spline.h:624
CompactSplineIndex IndexForX(const float x, const CompactSplineIndex guess_index) const
CompactSplineIndex IndexForXAllowingRepeat(const float x, const CompactSplineIndex guess_index, const bool repeat, float *final_x) const
void Finalize()
Definition: compact_spline.h:199
void AddUncompressedNodes(const UncompressedNode *nodes, size_t num_nodes)
static size_t ArraySize(size_t num_splines, size_t num_nodes)
Definition: compact_spline.h:472
Float representation of a point on the spline.
Definition: compact_spline.h:55
Initialization parameters to create a cubic curve with start and end y-values and derivatives...
Definition: curve.h:335
bool repeat
If true, start back at the beginning after we reach the end.
Definition: compact_spline.h:627
Traverse through a set of splines in a performant way.
Definition: bulk_spline_evaluator.h:37
void Init(const Range &y_range, const float x_granularity)
Definition: compact_spline.h:117
void InitFromNodes(const UncompressedNode *nodes, size_t num_nodes)
Definition: compact_spline_node.h:55
Definition: compact_spline.h:638
SplinePlayback(float start_x=0.0f, bool repeat=false, float playback_rate=1.0f, float blend_x=0.0f, float y_offset=0.0f, float y_scale=1.0f)
Definition: compact_spline.h:593
float CalculatedSlowly(const float x, const CurveValueType value_type) const
static void BulkEvaluate(const CompactSpline *const splines, const size_t num_splines, const float start_x, const float delta_x, const size_t num_points, BulkOutput *out)
Definition: compact_spline.h:484
CompactSpline * Next()
Definition: compact_spline.h:211
static CompactSpline * CreateArrayInPlace(CompactSplineIndex max_nodes, int num_splines, void *buffer)
Definition: compact_spline.h:432
void AddNodeVerbatim(const CompactSplineXGrain x, const CompactSplineYRung y, const CompactSplineAngle angle)
Definition: compact_spline.h:155
static void BulkYs(const CompactSpline *const splines, const float start_x, const float delta_x, const size_t num_ys, mathfu::VectorPacked< float, kDimensions > *ys)
Definition: compact_spline.h:521
CompactSplineIndex LastNodeIndex() const
Returns the index of the last node in the spline.
Definition: compact_spline.h:299
float blend_x
Definition: compact_spline.h:608
static CompactSpline * CreateInPlace(CompactSplineIndex max_nodes, void *buffer)
Definition: compact_spline.h:333
void Ys(const float start_x, const float delta_x, const size_t num_points, float *ys, float *derivatives=nullptr) const
CompactSpline * NextAtIdx(int idx)
Definition: compact_spline.h:216
CompactSplineIndex num_nodes() const
Returns the number of nodes in this spline.
Definition: compact_spline.h:311
float start_x
The starting point from which to play.
Definition: compact_spline.h:604
static float RecommendXGranularity(const float max_x)
static CompactSpline * CreateFromNodes(const UncompressedNode *nodes, size_t num_nodes)
Definition: compact_spline.h:344
static void Destroy(CompactSpline *spline)
Definition: compact_spline.h:404
static CompactSpline * CreateArray(CompactSplineIndex max_nodes, int num_splines)
Definition: compact_spline.h:417
CompactSplineIndex LastSegmentIndex() const
Returns the start index of the last segment in the spline.
Definition: compact_spline.h:305
Parameters to specify how a spline should be traversed.
Definition: compact_spline.h:590
static size_t Size(CompactSplineIndex max_nodes)
Definition: compact_spline.h:456
size_t Size() const
Returns the memory occupied by this spline.
Definition: compact_spline.h:207
static CompactSpline * CreateFromSpline(const CompactSpline &source_spline, size_t num_nodes)
Definition: compact_spline.h:376
static CompactSpline * CreateFromNodesInPlace(const UncompressedNode *nodes, size_t num_nodes, void *buffer)
Definition: compact_spline.h:360
Definition: compact_spline.h:630
float YCalculatedSlowly(const float x) const
Definition: compact_spline.h:275
void Clear()
Remove all nodes from the spline.
Definition: compact_spline.h:204
static CompactSpline * Create(CompactSplineIndex max_nodes)
Definition: compact_spline.h:324
static void DestroyArray(CompactSpline *splines, int)
Frees the memory allocated with CreateArray() using global delete.
Definition: compact_spline.h:444
static void BulkYs(const CompactSpline *const splines, const size_t num_splines, const float start_x, const float delta_x, const size_t num_points, float *ys, float *derivatives=nullptr)
static const CompactSplineIndex kDefaultMaxNodes
Definition: compact_spline.h:79
Represent a smooth curve in a small amount of memory.
Definition: compact_spline.h:73
float playback_rate
Definition: compact_spline.h:616
void InitFromSpline(const CompactSpline &spline)
void AddNode(const float x, const float y, const float derivative, const CompactSplineAddMethod method=kEnsureCubicWellBehaved)
CubicInit CreateCubicInit(const CompactSplineIndex index) const
const detail::CompactSplineNode * nodes() const
Return const versions of internal values. For serialization.
Definition: compact_spline.h:315
static CompactSpline * CreateFromSplineInPlace(const CompactSpline &source_spline, size_t num_nodes, void *buffer)
Definition: compact_spline.h:392
CompactSplineIndex ClampIndex(const CompactSplineIndex index, float *x) const
Represent an interval on a number line.
Definition: range.h:37
float y_offset
Definition: compact_spline.h:620