Motive Animation System
An open source project by FPL.
 All Classes Functions Variables Typedefs Friends Pages
target.h
1 // Copyright 2015 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_TARGET_H
16 #define MOTIVE_TARGET_H
17 
18 #include "motive/math/range.h"
19 #include "motive/math/vector_converter.h"
20 
21 namespace motive {
22 
23 /// @class MotiveCurveShape
24 /// @brief A target curve shape for the motivator.
25 ///
26 /// The curve shape is defined by the typical distance to travel, the time it
27 /// takes to travel it, and the bias. Using these variables, the actual time it
28 /// takes to travel the curve will be calculated.
31  : typical_delta_value(0.0f), typical_total_time(0.0f), bias(0.0f) {}
33  float bias)
34  : typical_delta_value(typical_delta_value),
35  typical_total_time(typical_total_time),
36  bias(bias) {}
37 
38  /// The typical difference between the start and end values.
40 
41  /// The typical time it takes to go the typical distance.
43 
44  /// Determines how much the curve should ease-in and how much it should
45  /// ease-out. Should be a value from 0.0 to 1.0.
46  /// Examples of potential bias values and what they would represent:
47  /// 0.0: ease-in but no ease out (a.k.a. "fly-out").
48  /// 0.3: ease-in more slowly and ease-out more quickly (i.e. less responsive).
49  /// 0.5: symmetrical curve: equal ease-in and ease-out.
50  /// 0.7: ease-out more slowly and ease-in more quickly (i.e. more reponsive).
51  /// 1.0: ease-out but no ease in (a.k.a. "fly-in").
52  float bias;
53 };
54 
55 /// @class MotiveNode1f
56 /// @brief A waypoint in MotiveTarget1f.
57 /// Describes one key point through which a value is animated.
58 struct MotiveNode1f {
59  /// Desired value to animate to at `time`.
60  float value;
61 
62  /// Speed when at `time`.
63  float velocity;
64 
65  /// Time to achieve this key point.
66  MotiveTime time;
67 
68  /// When using modular arithmetic, which of two directions to go.
69  ModularDirection direction;
70 
71  MotiveNode1f()
72  : value(0.0f),
73  velocity(0.0f),
74  time(0),
75  direction(motive::kDirectionClosest) {}
76  MotiveNode1f(float value, float velocity, MotiveTime time,
77  motive::ModularDirection direction = motive::kDirectionClosest)
78  : value(value), velocity(velocity), time(time), direction(direction) {}
79 };
80 
81 /// @class MotiveTarget1f
82 /// @brief Set the current and/or target state for a one-dimensional Motivator.
83 ///
84 /// A series of waypoints through which we animate. If the first waypoint has
85 /// time = 0, the current value and velocity jumps to that waypoint's value and
86 /// velocity.
87 ///
88 /// MotiveTarget1fs are most easily created with the utility functions below,
89 /// for example Current1f, Target1f, CurrentToTarget1f.
90 ///
91 /// If the current value and velocity are not specified (i.e. if the first
92 /// waypoint has time > 0), then the current value and velocity in
93 /// the Motivator are maintained.
94 ///
95 /// If the target is not specified (i.e. only one waypoint which has time = 0),
96 /// then the current value is set as specified, and the velocity is set to 0.
97 ///
99  public:
100  static const int kMaxNodes = 3;
101 
102  MotiveTarget1f() : num_nodes_(0) {}
103 
104  /// Create with only one waypoint.
105  /// If n0.time = 0, set the current value and velocity.
106  /// If n0.time > 0, maintain the current value and velocity and animate to
107  /// n0's value and velocity in n0.time.
108  explicit MotiveTarget1f(const MotiveNode1f& n0) : num_nodes_(1) {
109  nodes_[0] = n0;
110  }
111 
112  /// Create with two waypoints.
113  /// Can be current to target, if n0.time = 0.
114  /// Or can maintain the current and to through two targets: first n0, then n1.
115  /// Precondition: n0.time < n1.time.
117  : num_nodes_(2) {
118  assert(0 <= n0.time && n0.time < n1.time);
119  nodes_[0] = n0;
120  nodes_[1] = n1;
121  }
122 
123  /// Create with three waypoints.
124  /// 0 <= n0.time < n1.time < n2.time
126  const MotiveNode1f& n2)
127  : num_nodes_(3) {
128  assert(0 <= n0.time && n0.time < n1.time && n1.time < n2.time);
129  nodes_[0] = n0;
130  nodes_[1] = n1;
131  nodes_[2] = n2;
132  }
133 
134  /// Empty the target of all waypoints.
135  void Reset() { num_nodes_ = 0; }
136 
137  /// Return nth waypoint.
138  /// @param node_index nth waypoint. 0 <= node_index < num_nodes()
139  const MotiveNode1f& Node(int node_index) const {
140  assert(0 <= node_index && node_index < num_nodes_);
141  return nodes_[node_index];
142  }
143 
144  /// Return smallest range that covers the values of all waypoints.
145  /// @param start_value An extra value to include in the min/max calculation.
146  /// Most often is the current value of the Motivator1f.
147  motive::Range ValueRange(float start_value) const {
148  assert(num_nodes_ > 0);
149  float min = start_value;
150  float max = start_value;
151  for (int i = 0; i < num_nodes_; ++i) {
152  min = std::min(nodes_[i].value, min);
153  max = std::max(nodes_[i].value, max);
154  }
155  return motive::Range(min, max);
156  }
157 
158  /// Return time of the last waypoint.
159  MotiveTime EndTime() const {
160  assert(num_nodes_ > 0);
161  return nodes_[num_nodes_ - 1].time;
162  }
163 
164  int num_nodes() const { return num_nodes_; }
165  const MotiveTarget1f* targets() const { return this; }
166 
167  private:
168  /// Length of nodes_.
169  int num_nodes_;
170 
171  /// Constant-size array, to avoid dynamic memory allocation.
172  /// This class is often used as a parameter and allocated on the stack.
173  MotiveNode1f nodes_[kMaxNodes];
174 };
175 
176 /// @class MotiveTargetN
177 /// @brief N-dimensional MotiveTargets are simply arrays of one dimensional
178 /// MotiveTargets.
179 template <int kDimensionsT>
181  public:
182  static const int kDimensions = kDimensionsT;
183 
184  const MotiveTarget1f& operator[](int i) const {
185  assert(0 <= i && i < kDimensions);
186  return targets_[i];
187  }
188 
189  MotiveTarget1f& operator[](int i) {
190  assert(0 <= i && i < kDimensions);
191  return targets_[i];
192  }
193 
194  const MotiveTarget1f* targets() const { return targets_; }
195 
196  private:
197  /// Each dimension gets its own target, that can be accessed with [].
198  /// Note that the 'time' values do not have to correspond between dimensions.
199  /// When using static constructor functions like `Current()` or `Target()`
200  /// all the times will be the same, but in general, we want to allow each
201  /// dimension to specify time independently. This will lead to maximal
202  /// compression. There's no reason why the x, y, and z channels should act
203  /// similarly.
204  MotiveTarget1f targets_[kDimensions];
205 };
206 
210 
211 template <int>
213  typedef void type;
214 };
215 template <>
216 struct MotiveTargetT<1> {
217  typedef MotiveTarget1f type;
218 };
219 template <>
220 struct MotiveTargetT<2> {
221  typedef MotiveTarget2f type;
222 };
223 template <>
224 struct MotiveTargetT<3> {
225  typedef MotiveTarget3f type;
226 };
227 template <>
228 struct MotiveTargetT<4> {
229  typedef MotiveTarget4f type;
230 };
231 
232 /// Set the Motivator's current values. Target values are reset to be the same
233 /// as the new current values.
234 inline MotiveTarget1f Current1f(float current_value,
235  float current_velocity = 0.0f) {
236  return MotiveTarget1f(MotiveNode1f(current_value, current_velocity, 0));
237 }
238 
239 /// Keep the Motivator's current values, but set the Motivator's target values.
240 /// If Motivator uses modular arithmetic, traverse from the current to the
241 /// target according to 'direction'.
242 inline MotiveTarget1f Target1f(float target_value, float target_velocity,
243  MotiveTime target_time,
244  motive::ModularDirection direction =
245  motive::kDirectionClosest) {
246  assert(target_time >= 0);
247  return MotiveTarget1f(
248  MotiveNode1f(target_value, target_velocity, target_time, direction));
249 }
250 
251 /// Set both the current and target values for an Motivator.
252 inline MotiveTarget1f CurrentToTarget1f(
253  float current_value, float current_velocity, float target_value,
254  float target_velocity, MotiveTime target_time,
255  motive::ModularDirection direction = motive::kDirectionClosest) {
256  return MotiveTarget1f(
257  MotiveNode1f(current_value, current_velocity, 0),
258  MotiveNode1f(target_value, target_velocity, target_time, direction));
259 }
260 
261 /// Move from the current value to the target value at a constant speed.
262 inline MotiveTarget1f CurrentToTargetConstVelocity1f(float current_value,
263  float target_value,
264  MotiveTime target_time) {
265  assert(target_time > 0);
266  const float velocity = (target_value - current_value) / target_time;
267  return MotiveTarget1f(MotiveNode1f(current_value, velocity, 0),
268  MotiveNode1f(target_value, velocity, target_time,
269  motive::kDirectionDirect));
270 }
271 
272 /// Keep the Motivator's current values, but set two targets for the Motivator.
273 /// After the first target, go on to the next.
274 inline MotiveTarget1f TargetToTarget1f(float target_value,
275  float target_velocity,
276  MotiveTime target_time,
277  float third_value, float third_velocity,
278  MotiveTime third_time) {
279  return MotiveTarget1f(
280  MotiveNode1f(target_value, target_velocity, target_time),
281  MotiveNode1f(third_value, third_velocity, third_time));
282 }
283 
284 /// Set the Motivator's current values, and two targets afterwards.
285 inline MotiveTarget1f CurrentToTargetToTarget1f(
286  float current_value, float current_velocity, float target_value,
287  float target_velocity, MotiveTime target_time, float third_value,
288  float third_velocity, MotiveTime third_time) {
289  return MotiveTarget1f(
290  MotiveNode1f(current_value, current_velocity, 0),
291  MotiveNode1f(target_value, target_velocity, target_time),
292  MotiveNode1f(third_value, third_velocity, third_time));
293 }
294 
295 /// Utility functions to construct MotiveTargets of dimension >= 2.
296 template <class VectorConverter, int kDimensionsT>
298  typedef VectorConverter C;
299 
300  public:
301  static const int kDimensions = kDimensionsT;
302  typedef typename VectorT<C, kDimensionsT>::type Vec;
304  typedef const Vec& VecIn;
305 
306  /// Set the Motivator's current values. Target values are reset to be the same
307  /// as the new current values.
308  static MotiveTarget Current(VecIn current_value,
309  VecIn current_velocity = Vec(0.0f)) {
310  const float* current_value_in = C::ToPtr(current_value);
311  const float* current_velocity_in = C::ToPtr(current_velocity);
312 
313  MotiveTarget t;
314  for (int i = 0; i < kDimensions; ++i) {
315  t[i] = Current1f(current_value_in[i], current_velocity_in[i]);
316  }
317  return t;
318  }
319 
320  /// Keep the Motivator's current values, but set the Motivator's target
321  /// values. If Motivator uses modular arithmetic, traverse from the current
322  /// to the target according to 'direction'.
324  VecIn target_value, VecIn target_velocity, MotiveTime target_time,
325  motive::ModularDirection direction = motive::kDirectionClosest) {
326  const float* target_value_in = C::ToPtr(target_value);
327  const float* target_velocity_in = C::ToPtr(target_velocity);
328 
329  MotiveTarget t;
330  for (int i = 0; i < kDimensions; ++i) {
331  t[i] = Target1f(target_value_in[i], target_velocity_in[i], target_time,
332  direction);
333  }
334  return t;
335  }
336 
337  /// Set both the current and target values for an Motivator.
339  VecIn current_value, VecIn current_velocity, VecIn target_value,
340  VecIn target_velocity, MotiveTime target_time,
341  motive::ModularDirection direction = motive::kDirectionClosest) {
342  const float* current_value_in = C::ToPtr(current_value);
343  const float* current_velocity_in = C::ToPtr(current_velocity);
344  const float* target_value_in = C::ToPtr(target_value);
345  const float* target_velocity_in = C::ToPtr(target_velocity);
346 
347  MotiveTarget t;
348  for (int i = 0; i < kDimensions; ++i) {
349  t[i] = CurrentToTarget1f(current_value_in[i], current_velocity_in[i],
350  target_value_in[i], target_velocity_in[i],
351  target_time, direction);
352  }
353  return t;
354  }
355 
356  /// Move from the current value to the target value at a constant speed.
357  static MotiveTarget CurrentToTargetConstVelocity(VecIn current_value,
358  VecIn target_value,
359  MotiveTime target_time) {
360  const float* current_value_in = C::ToPtr(current_value);
361  const float* target_value_in = C::ToPtr(target_value);
362 
363  // TODO OPT: Do the math using the vector types.
364  MotiveTarget t;
365  for (int i = 0; i < kDimensions; ++i) {
366  t[i] = CurrentToTargetConstVelocity1f(current_value_in[i],
367  target_value_in[i], target_time);
368  }
369  return t;
370  }
371 };
372 
373 /// Utility functions to construct MotiveTarget1fs.
374 template <class VectorConverter>
375 class MotiveTargetBuilderTemplate<VectorConverter, 1> {
376  public:
377  static const int kDimensions = 1;
378  typedef float Vec;
380  typedef float VecIn;
381 
382  static MotiveTarget Current(VecIn current_value,
383  VecIn current_velocity = Vec(0.0f)) {
384  return Current1f(current_value, current_velocity);
385  }
386 
387  static MotiveTarget Target(
388  VecIn target_value, VecIn target_velocity, MotiveTime target_time,
389  motive::ModularDirection direction = motive::kDirectionClosest) {
390  return Target1f(target_value, target_velocity, target_time, direction);
391  }
392 
394  VecIn current_value, VecIn current_velocity, VecIn target_value,
395  VecIn target_velocity, MotiveTime target_time,
396  motive::ModularDirection direction = motive::kDirectionClosest) {
397  return CurrentToTarget1f(current_value, current_velocity, target_value,
398  target_velocity, target_time, direction);
399  }
400 
401  static MotiveTarget CurrentToTargetConstVelocity(VecIn current_value,
402  VecIn target_value,
403  MotiveTime target_time) {
404  return CurrentToTargetConstVelocity1f(current_value, target_value,
405  target_time);
406  }
407 };
408 
409 // Typedefs to make it easy to construct MotiveTargets with dimension >= 2.
410 //
411 // For example, to create a MotiveTarget2f that sets both the current and
412 // future values, you could use this syntax.
413 //
414 // Mo2f::CurrentToTarget(vec2(0,1), vec2(0,0), vec2(2,3), vec2(1,1), 100);
415 //
416 // These classes use mathfu types in their external API. If you have your own
417 // vector types, create your own VectorConverter and your own typedefs.
418 //
423 
424 } // namespace motive
425 
426 #endif // MOTIVE_TARGET_H
motive::Range ValueRange(float start_value) const
Definition: target.h:147
ModularDirection direction
When using modular arithmetic, which of two directions to go.
Definition: target.h:69
Utility functions to construct MotiveTargets of dimension >= 2.
Definition: target.h:297
MotiveTime time
Time to achieve this key point.
Definition: target.h:66
MotiveTarget1f(const MotiveNode1f &n0, const MotiveNode1f &n1, const MotiveNode1f &n2)
Definition: target.h:125
float typical_delta_value
The typical difference between the start and end values.
Definition: target.h:39
static MotiveTarget Current(VecIn current_value, VecIn current_velocity=Vec(0.0f))
Definition: target.h:308
const MotiveNode1f & Node(int node_index) const
Definition: target.h:139
static MotiveTarget Target(VecIn target_value, VecIn target_velocity, MotiveTime target_time, motive::ModularDirection direction=motive::kDirectionClosest)
Definition: target.h:323
float bias
Definition: target.h:52
float velocity
Speed when at time.
Definition: target.h:63
MotiveTime EndTime() const
Return time of the last waypoint.
Definition: target.h:159
A target curve shape for the motivator.
Definition: target.h:29
MotiveTarget1f(const MotiveNode1f &n0)
Definition: target.h:108
float value
Desired value to animate to at time.
Definition: target.h:60
float typical_total_time
The typical time it takes to go the typical distance.
Definition: target.h:42
N-dimensional MotiveTargets are simply arrays of one dimensional MotiveTargets.
Definition: target.h:180
MotiveTarget1f(const MotiveNode1f &n0, const MotiveNode1f &n1)
Definition: target.h:116
static MotiveTarget CurrentToTarget(VecIn current_value, VecIn current_velocity, VecIn target_value, VecIn target_velocity, MotiveTime target_time, motive::ModularDirection direction=motive::kDirectionClosest)
Set both the current and target values for an Motivator.
Definition: target.h:338
A waypoint in MotiveTarget1f. Describes one key point through which a value is animated.
Definition: target.h:58
static MotiveTarget CurrentToTargetConstVelocity(VecIn current_value, VecIn target_value, MotiveTime target_time)
Move from the current value to the target value at a constant speed.
Definition: target.h:357
void Reset()
Empty the target of all waypoints.
Definition: target.h:135
Set the current and/or target state for a one-dimensional Motivator.
Definition: target.h:98
Definition: target.h:212
Represent an interval on a number line.
Definition: range.h:37