Motive Animation System
An open source project by FPL.
 All Classes Functions Variables Typedefs Friends Pages
compact_spline_node.h
1 // Copyright 2016 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_NODE_H_
16 #define MOTIVE_MATH_COMPACT_SPLINE_NODE_H_
17 
18 #include "motive/common.h"
19 #include "motive/math/curve.h"
20 
21 namespace motive {
22 
23 /// @typedef CompactSplineXGrain
24 /// X-axis is quantized into units of `x_granularity`. X values are represented
25 /// by multiples of `x_granularity`. One unit of CompactSplineXGrain represents
26 /// one multiple of `x_granularity`.
27 typedef uint16_t CompactSplineXGrain;
28 
29 /// @typedef CompactSplineYRung
30 /// Y values within `y_range` can be represented. We quantize the `y_range`
31 /// into equally-sized rungs, and round to the closest rung.
32 typedef uint16_t CompactSplineYRung;
33 
34 /// @typedef CompactSplineAngle
35 /// Angles strictly between -90 and +90 can be represented. We record the angle
36 /// instead of the slope for more uniform distribution.
37 typedef int16_t CompactSplineAngle;
38 
39 namespace detail {
40 
41 // A spline is composed of a series of spline nodes (x, y, derivative) that are
42 // interpolated to form a smooth curve.
43 //
44 // This class represents a single spline node in 6-bytes. It quantizes the
45 // valid ranges of x, y, and slope into three 16-bit integers = 6 bytes.
46 //
47 // The x and y values are quantized to the valid range. The valid range is
48 // stored externally and passed in to each call. Please see comments on
49 // CompactSplineXGrain and CompactSplineYRung for more detail.
50 //
51 // The derivative is stored as the angle from the x-axis. This is so that we
52 // can equally represent derivatives <= 1 (that is, <= 45 degrees) and
53 // derivatives >= 1 (that is, >= 45 degrees) with a quantized number.
54 //
56  public:
57  // Don't initialize the data to save cycles.
59 
60  // Construct with values that have already been converted to quantized values.
61  // This constructor is useful when deserializing pre-converted data.
62  CompactSplineNode(const CompactSplineXGrain x, const CompactSplineYRung y,
63  const CompactSplineAngle angle)
64  : x_(x), y_(y), angle_(angle) {}
65 
66  // Construct with real-world values. Must pass in the valid x and y ranges.
67  CompactSplineNode(const float x, const float y, const float derivative,
68  const float x_granularity, const Range& y_range) {
69  SetX(x, x_granularity);
70  SetY(y, y_range);
71  SetDerivative(derivative);
72  }
73 
74  // Set with real-world values. The valid range of x and y must be passed in.
75  // These values are passed in so that we don't have to store multiple copies
76  // of them. Memory compactness is the purpose of this class.
77  void SetX(const float x, const float x_granularity) {
78  x_ = CompactX(x, x_granularity);
79  }
80  void SetY(const float y, const Range& y_range) { y_ = CompactY(y, y_range); }
81  void SetDerivative(const float derivative) {
82  angle_ = CompactDerivative(derivative);
83  }
84 
85  // Get real world values. The valid range of x and y must be passed in.
86  // The valid range must be the same as when x and y values were set.
87  float X(const float x_granularity) const {
88  return static_cast<float>(x_) * x_granularity;
89  }
90  float Y(const Range& y_range) const { return y_range.Lerp(YPercent()); }
91  float Derivative() const { return tan(Angle()); }
92 
93  // Get the quantized values. Useful for serializing a series of nodes.
94  CompactSplineXGrain x() const { return x_; }
95  CompactSplineYRung y() const { return y_; }
96  CompactSplineAngle angle() const { return angle_; }
97 
98  // Equivalence can be tested reasonably because internal types are integral,
99  // not floating point.
100  bool operator==(const CompactSplineNode& rhs) const {
101  return x_ == rhs.x_ && y_ == rhs.y_ && angle_ == rhs.angle_;
102  }
103  bool operator!=(const CompactSplineNode& rhs) const {
104  return !operator==(rhs);
105  }
106 
107  // Convert from real-world to quantized values.
108  // Please see type definitions for documentation on the quantized format.
109  static int QuantizeX(const float x, const float x_granularity) {
110  return static_cast<int>(x / x_granularity + 0.5f);
111  }
112 
113  static CompactSplineXGrain CompactX(const float x,
114  const float x_granularity) {
115  const int x_quantized = QuantizeX(x, x_granularity);
116  assert(0 <= x_quantized && x_quantized <= kMaxX);
117  return static_cast<CompactSplineXGrain>(x_quantized);
118  }
119 
120  static CompactSplineYRung CompactY(const float y, const Range& y_range) {
121  assert(y_range.Contains(y));
122  const float y_percent = y_range.PercentClamped(y);
123  const CompactSplineYRung compact_y =
124  static_cast<CompactSplineYRung>(kMaxY * y_percent);
125  return compact_y;
126  }
127 
128  static CompactSplineAngle CompactDerivative(const float derivative) {
129  const float angle_radians = atan(derivative);
130  const CompactSplineAngle angle =
131  static_cast<CompactSplineAngle>(angle_radians / kAngleScale);
132  return angle;
133  }
134 
135  static CompactSplineXGrain MaxX() { return kMaxX; }
136 
137  private:
138  static const CompactSplineXGrain kMaxX;
139  static const CompactSplineYRung kMaxY;
140  static const CompactSplineAngle kMinAngle;
141  static const float kYScale;
142  static const float kAngleScale;
143 
144  float YPercent() const { return static_cast<float>(y_) * kYScale; }
145  float Angle() const { return static_cast<float>(angle_) * kAngleScale; }
146 
147  // Position along x-axis. Multiplied by x-granularity to get actual domain.
148  // 0 ==> start. kMaxX ==> end, we should never reach the end. If we do,
149  // the x_granularity should be increased.
150  CompactSplineXGrain x_;
151 
152  // Position within y_range. 0 ==> y_range.start. kMaxY ==> y_range.end.
153  CompactSplineYRung y_;
154 
155  // Angle from x-axis. tan(angle) = rise / run = derivative.
156  CompactSplineAngle angle_;
157 };
158 
159 } // namespace detail
160 } // namespace motive
161 
162 #endif // MOTIVE_MATH_COMPACT_SPLINE_NODE_H_
T Lerp(const float percent) const
Definition: range.h:87
bool Contains(const T x) const
Return true if x is in [start_, end_], i.e. the inclusive range.
Definition: range.h:229
Definition: compact_spline_node.h:55
Represent an angle in radians, uniquely in the range (-pi, pi].
Definition: angle.h:97
T PercentClamped(const T x) const
Definition: range.h:97
Represent an interval on a number line.
Definition: range.h:37