Motive Animation System
An open source project by FPL.
 All Classes Functions Variables Typedefs Friends Pages
range.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_MATH_RANGE_H_
16 #define MOTIVE_MATH_RANGE_H_
17 
18 #include <vector>
19 #include "mathfu/utilities.h"
20 
21 namespace motive {
22 
23 // If using modular arithmetic, there are two paths to the target: one that
24 // goes directly and one that wraps around. This enum represents differet ways
25 // to choose the path.
26 enum ModularDirection {
27  kDirectionClosest,
28  kDirectionFarthest,
29  kDirectionPositive,
30  kDirectionNegative,
31  kDirectionDirect,
32 };
33 
34 /// @class RangeT
35 /// @brief Represent an interval on a number line.
36 template <class T>
37 class RangeT {
38  public:
39  template <size_t kMaxLen>
40  struct TArray {
41  size_t len;
42  T arr[kMaxLen];
43  };
44 
45  template <size_t kMaxLen>
46  struct RangeArray {
47  size_t len;
48  RangeT<T> arr[kMaxLen];
49  };
50 
51  // By default, initialize to an invalid range.
52  RangeT() : start_(static_cast<T>(1)), end_(static_cast<T>(0)) {}
53  explicit RangeT(const T point) : start_(point), end_(point) {}
54  RangeT(const T start, const T end) : start_(start), end_(end) {}
55 
56  /// A range is valid if it contains at least one number.
57  bool Valid() const { return start_ <= end_; }
58 
59  /// Returns the mid-point of the range, rounded down for integers.
60  /// Behavior is undefined for invalid regions.
61  T Middle() const { return (start_ + end_) / static_cast<T>(2); }
62 
63  /// Returns the span of the range. Returns 0 when only one number in range.
64  /// Behavior is undefined for invalid regions.
65  T Length() const { return end_ - start_; }
66 
67  /// Returns `x` if it is within the range. Otherwise, returns start_ or end_,
68  /// whichever is closer to `x`.
69  /// Behavior is undefined for invalid regions.
70  T Clamp(const T x) const { return mathfu::Clamp(x, start_, end_); }
71 
72  /// Clamp `x` so it is inside the start bound. Can save cycles if you
73  /// already know that `x` is inside the end bound.
74  T ClampAfterStart(const T x) const { return std::max(x, start_); }
75 
76  /// Clamp `x` so it is inside the end bound. Can save cycles if you
77  /// already know that `x` is inside the start bound.
78  T ClampBeforeEnd(const T x) const { return std::min(x, end_); }
79 
80  /// Returns distance outside of the range. If inside the range, returns 0.
81  /// Behavior is undefined for invalid regions.
82  T DistanceFrom(const T x) const { return fabs(x - Clamp(x)); }
83 
84  /// Lerps between the start and the end.
85  /// 'percent' of 0 returns start. 'percent' of 1 returns end.
86  /// Behavior is undefined for invalid regions.
87  T Lerp(const float percent) const {
88  return mathfu::Lerp(start_, end_, percent);
89  }
90 
91  /// Returns percent 0~1, from start to end. *Not* clamped to 0~1.
92  /// 0 ==> start; 1 ==> end; 0.5 ==> Middle(); -1 ==> start - Length()
93  float Percent(const T x) const { return (x - start_) / Length(); }
94 
95  /// Returns percent 0~1, from start to end. Clamped to 0~1.
96  /// 0 ==> start or earlier; 1 ==> end or later; 0.5 ==> Middle()
97  T PercentClamped(const T x) const {
98  return mathfu::Clamp(Percent(x), 0.0f, 1.0f);
99  }
100 
101  /// Ensure `x` is within the valid constraint range, by subtracting or
102  /// adding Length() to it.
103  /// `x` must be within +-Length() of the range bounds. This is a reasonable
104  /// restriction in most cases (such as after an arithmetic operation).
105  /// For cases where `x` may be wildly outside the range, use
106  /// NormalizeCloseValue() or NormalizeWildValue() instead.
107  T Normalize(T x) const {
108  const float normalized = x + ModularAdjustment(x);
109  assert(ContainsExcludingStart(normalized));
110  return normalized;
111  }
112 
113  /// Ensure `x` is within the valid constraint range, by subtracting or
114  /// adding Length() to it repeatedly.
115  /// `x` must be within +-kMaxAdjustments * Length() of the range bounds
116  /// for this function to be effective. If its greater, we end up calling
117  /// NormalizeWildValue(), so you'd be better off calling NormalizeWildValue()
118  /// from the beginning.
119  /// This function is intended to be called in situations where `x` is almost
120  /// always within one or two lengths of being normalized, so we don't want to
121  /// incur the division cost of NormalizeWildValue(). It's still guaranteed
122  /// to return a normalized value, however.
123  T NormalizeCloseValue(T x) const {
124  static const int kMaxAdjustments = 4;
125 
126  // Return without change if `x` is already normalized.
127  const bool below = x <= start_;
128  const bool above = x > end_;
129  if (!below && !above) return x;
130 
131  // Each time through the loop, we'll adjust by one length closer to the
132  // valid interval.
133  const T length = Length();
134  int num_adjustments = 0;
135 
136  if (below) {
137  // Keep adding until we're in the range.
138  do {
139  x += length;
140  if (num_adjustments++ > kMaxAdjustments) return NormalizeWildValue(x);
141  } while (x <= start_);
142  } else {
143  // Keep subtracting until we're in the range.
144  do {
145  x -= length;
146  if (num_adjustments++ > kMaxAdjustments) return NormalizeWildValue(x);
147  } while (x > end_);
148  }
149  return x;
150  }
151 
152  /// Ensure `x` is within the valid constraint range, by subtracting multiples
153  /// of Length() from it until it is.
154  /// `x` can be any value.
155  T NormalizeWildValue(T x) const {
156  // Use (expensive) division to determine how many lengths we are away from
157  // the normalized range.
158  const T length = Length();
159  const T units = (x - start_) / length;
160  const T whole_units = floor(units);
161 
162  // Subtract off those units to get something that (mathematically) should
163  // be normalized. Due to Ting point error, it sometimes is slightly
164  // outside the bounds, so we need to do a standard normalization afterwards.
165  const T close = x - whole_units * length;
166  const T normalized = close + ModularAdjustment(close);
167  assert(ContainsExcludingStart(normalized));
168  return normalized;
169  }
170 
171  /// Returns:
172  /// Length() if `x` is below the valid range
173  /// -Length() if `x` is above the valid range
174  /// 0 if `x` is within the valid range.
175  T ModularAdjustment(T x) const {
176  const T length = Length();
177  const T adjustment = x <= start_ ? length : x > end_ ? -length : 0.0f;
178  return adjustment;
179  }
180 
181  /// In modular arithmetic, you can get from 'a' to 'b' by going directly, or
182  /// by wrapping around.
183  /// Return the closest difference from 'a' to 'b' under modular arithmetic.
184  float ModDiffClose(T a, T b) const { return Normalize(b - a); }
185 
186  /// Return the farthest difference from 'a' to 'b' under modular arithmetic.
187  float ModDiffFar(T a, T b) const {
188  const float length = Length();
189  const float close = ModDiffClose(a, b);
190  return close >= 0.0f ? close - length : close + length;
191  }
192 
193  /// Return the difference from 'a' to 'b' under modular arithmetic that is
194  /// positive.
195  float ModDiffPositive(T a, T b) const {
196  const float length = Length();
197  const float close = ModDiffClose(a, b);
198  return close >= 0.0f ? close : close + length;
199  }
200 
201  /// Return the difference from 'a' to 'b' under modular arithmetic that is
202  /// negative.
203  float ModDiffNegative(T a, T b) const {
204  const float length = Length();
205  const float close = ModDiffClose(a, b);
206  return close >= 0.0f ? close - length : close;
207  }
208 
209  /// Return the difference from 'a' to 'b' that satisfies the 'direction'
210  /// criteria.
211  float ModDiff(T a, T b, ModularDirection direction) const {
212  switch (direction) {
213  case kDirectionClosest:
214  return ModDiffClose(a, b);
215  case kDirectionFarthest:
216  return ModDiffFar(a, b);
217  case kDirectionPositive:
218  return ModDiffPositive(a, b);
219  case kDirectionNegative:
220  return ModDiffNegative(a, b);
221  case kDirectionDirect:
222  return b - a;
223  }
224  assert(false);
225  return 0.0f;
226  }
227 
228  /// Return true if `x` is in [start_, end_], i.e. the **inclusive** range.
229  bool Contains(const T x) const { return start_ <= x && x <= end_; }
230 
231  /// Return true if `x` is in (start_, end_], i.e. the range that includes the
232  /// end bound but not the start bound.
233  bool ContainsExcludingStart(const T x) const {
234  return start_ < x && x <= end_;
235  }
236 
237  /// Return true if `x` is in [start_, end_), i.e. the range that includes the
238  /// start bound but not the end bound.
239  bool ContainsExcludingEnd(const T x) const { return start_ <= x && x < end_; }
240 
241  /// Return true if `x` is in (start_, end_), i.e. the **exclusive** range.
242  bool StrictlyContains(const T x) const { return start_ < x && x < end_; }
243 
244  /// Return true if `x` is in [start_ - tolerance, end_ + tolerance],
245  /// where tolerance = Length() * percent.
246  bool ContainsWithTolerance(const T x, const T percent) const {
247  const float tolerance = Length() * percent;
248  return start_ - tolerance <= x && x <= end_ + tolerance;
249  }
250 
251  /// Swap start and end. When 'a' and 'b' don't overlap, if you invert the
252  /// return value of Range::Intersect(a, b), you'll get the gap between
253  /// 'a' and 'b'.
254  RangeT Invert() const { return RangeT(end_, start_); }
255 
256  /// Returns a range that is 'percent' longer. If 'percent' is < 1.0, then
257  /// returned range will actually be shorter.
258  RangeT Lengthen(const float percent) const {
259  const T extra = static_cast<T>(Length() * percent * 0.5f);
260  return RangeT(start_ - extra, end_ + extra);
261  }
262 
263  /// Returns the smallest range that contains both `x` and the range in
264  /// `this`.
265  RangeT Include(const T x) const {
266  return RangeT(std::min<T>(start_, x), std::max<T>(end_, x));
267  }
268 
269  /// Equality is strict. No epsilon checking here.
270  bool operator==(const RangeT& rhs) const {
271  return start_ == rhs.start_ && end_ == rhs.end_;
272  }
273  bool operator!=(const RangeT& rhs) const { return !operator==(rhs); }
274 
275  /// Scale by multiplying by a scalar.
276  RangeT operator*(const float s) const { return RangeT(s * start_, s * end_); }
277 
278  /// Accessors.
279  T start() const { return start_; }
280  T end() const { return end_; }
281  void set_start(const T start) { start_ = start; }
282  void set_end(const T end) { end_ = end; }
283 
284  /// Return the overlap of 'a' and 'b', or an invalid range if they do not
285  /// overlap at all.
286  /// When 'a' and 'b' don't overlap at all, calling Invert on the returned
287  /// range will give the gap between 'a' and 'b'.
288  static RangeT Intersect(const RangeT& a, const RangeT& b) {
289  // Possible cases:
290  // 1. |-a---| |-b---| ==> return invalid
291  // 2. |-b---| |-a---| ==> return invalid
292  // 3. |-a---------| ==> return b
293  // |-b---|
294  // 4. |-b---------| ==> return a
295  // |-a---|
296  // 5. |-a---| ==> return (b.start, a.end)
297  // |-b---|
298  // 6. |-b---| ==> return (a.start, b.end)
299  // |-a---|
300  //
301  // All satisfied by,
302  // intersection.start = max(a.start, b.start)
303  // intersection.end = min(a.end, b.end)
304  // Note that ranges where start > end are considered invalid.
305  return RangeT(std::max(a.start_, b.start_), std::min(a.end_, b.end_));
306  }
307 
308  /// Return the smallest range that covers all of 'a' and 'b'.
309  static RangeT Union(const RangeT& a, const RangeT& b) {
310  // Possible cases:
311  // 1. |-a---| |-b---| ==> return (a.start, b.end)
312  // 2. |-b---| |-a---| ==> return (b.start, a.end)
313  // 3. |-a---------| ==> return a
314  // |-b---|
315  // 4. |-b---------| ==> return b
316  // |-a---|
317  // 5. |-a---| ==> return (a.start, b.end)
318  // |-b---|
319  // 6. |-b---| ==> return (b.start, a.end)
320  // |-a---|
321  //
322  // All satisfied by,
323  // intersection.start = min(a.start, b.start)
324  // intersection.end = max(a.end, b.end)
325  return RangeT(std::min(a.start_, b.start_), std::max(a.end_, b.end_));
326  }
327 
328  /// Only keep entries in 'values' if they are in
329  /// (range.start - epsition, range.end + epsilon).
330  /// Any values that are kept are clamped to 'range'.
331  ///
332  /// This function is useful when floating point precision error might put a
333  /// value slightly outside 'range' even though mathematically it should be
334  /// inside 'range'. This often happens with values right on the border of the
335  /// valid range.
336  static size_t ValuesInRange(const RangeT& range, T epsilon, size_t num_values,
337  T* values) {
338  size_t num_returned = 0;
339  for (size_t i = 0; i < num_values; ++i) {
340  const T value = values[i];
341  const T clamped = range.Clamp(value);
342  const T dist = fabs(value - clamped);
343 
344  // If the distance from the range is small, keep the clamped value.
345  if (dist <= epsilon) {
346  values[num_returned++] = clamped;
347  }
348  }
349  return num_returned;
350  }
351 
352  template <size_t kMaxLen>
353  static void ValuesInRange(const RangeT& range, T epsilon,
354  TArray<kMaxLen>* values) {
355  values->len = ValuesInRange(range, epsilon, values->len, values->arr);
356  }
357 
358  /// Intersect every element of 'a' with every element of 'b'. Append
359  /// intersections to 'intersections'. Note that 'intersections' is not reset
360  /// at the start of the call.
361  static size_t IntersectRanges(const RangeT* a, size_t len_a, const RangeT* b,
362  size_t len_b, RangeT* intersections,
363  RangeT* gaps = nullptr,
364  size_t* len_gaps = nullptr) {
365  size_t num_intersections = 0;
366  size_t num_gaps = 0;
367 
368  for (size_t i = 0; i < len_a; ++i) {
369  for (size_t j = 0; j < len_b; ++j) {
370  const RangeT intersection = RangeT::Intersect(a[i], b[j]);
371  if (intersection.Valid()) {
372  intersections[num_intersections++] = intersection;
373 
374  } else if (gaps != nullptr) {
375  // Return the gaps, too, if requested. Invert() invalid intersections
376  // to get the gap between the ranges.
377  gaps[num_gaps++] = intersection.Invert();
378  }
379  }
380  }
381 
382  // Set return values.
383  if (len_gaps != nullptr) {
384  *len_gaps = num_gaps;
385  }
386  return num_intersections;
387  }
388 
389  template <size_t kMaxLen>
390  static void IntersectRanges(const RangeArray<kMaxLen>& a,
391  const RangeArray<kMaxLen>& b,
392  RangeArray<kMaxLen * kMaxLen>* intersections,
393  RangeArray<kMaxLen * kMaxLen>* gaps = nullptr) {
394  const bool use_gaps = gaps != nullptr;
395  intersections->len = IntersectRanges(
396  a.arr, a.len, b.arr, b.len, intersections->arr,
397  use_gaps ? gaps->arr : nullptr, use_gaps ? &gaps->len : nullptr);
398  }
399 
400  /// Return the index of the longest range in `ranges`.
401  static size_t IndexOfLongest(const RangeT* ranges, size_t len) {
402  T longest_length = -1.0f;
403  size_t longest_index = 0;
404  for (size_t i = 0; i < len; ++i) {
405  const T length = ranges[i].Length();
406  if (length > longest_length) {
407  longest_length = length;
408  longest_index = i;
409  }
410  }
411  return longest_index;
412  }
413 
414  template <size_t kMaxLen>
415  static size_t IndexOfLongest(const RangeArray<kMaxLen>& ranges) {
416  return IndexOfLongest(ranges.arr, ranges.len);
417  }
418 
419  /// Return the index of the shortest range in `ranges`.
420  static size_t IndexOfShortest(const RangeT* ranges, size_t len) {
421  T shortest_length = std::numeric_limits<T>::infinity();
422  size_t shortest_index = 0;
423  for (size_t i = 0; i < len; ++i) {
424  const T length = ranges[i].Length();
425  if (length < shortest_length) {
426  shortest_length = length;
427  shortest_index = i;
428  }
429  }
430  return shortest_index;
431  }
432 
433  template <size_t kMaxLen>
434  static size_t IndexOfShortest(const RangeArray<kMaxLen>& ranges) {
435  return IndexOfShortest(ranges.arr, ranges.len);
436  }
437 
438  /// Return the index of the shortest range in `ranges`.
439  static T ClampToClosest(T x, const RangeT* ranges, size_t len) {
440  T closest_dist = std::numeric_limits<T>::infinity();
441  T closest_clamp = x;
442  for (size_t i = 0; i < len; ++i) {
443  const T clamp = ranges[i].Clamp(x);
444  const T dist = fabs(x - clamp);
445  if (dist < closest_dist) {
446  closest_dist = dist;
447  closest_clamp = clamp;
448  }
449  }
450  return closest_clamp;
451  }
452 
453  template <size_t kMaxLen>
454  static T ClampToClosest(T x, const RangeArray<kMaxLen>& ranges) {
455  return ClampToClosest(x, ranges.arr, ranges.len);
456  }
457 
458  /// Returns the range that covers all values in f(array).
459  /// f is a lambda to calculate T from S. See Covers() for a simple example.
460  /// In general, if your S has a function `T GetValue()`, then your lambda
461  /// can look something like,
462  /// const RangeT<T> range = RangeT<T>::CoversLambda(
463  /// s_array, len, [](const S& s) { return s.GetValue(); });
464  template <typename S, typename F>
465  static RangeT<T> CoversLambda(const S* array, size_t len, const F& f) {
466  RangeT<T> r = Empty();
467  for (size_t i = 0; i < len; ++i) {
468  r = r.Include(f(array[i]));
469  }
470  return r;
471  }
472 
473  /// Return the range that covers all values in `array`.
474  static RangeT<T> Covers(const T* array, size_t len) {
475  return CoversLambda(array, len, [](const T& t) { return t; });
476  }
477 
478  /// Returns the complete range. Every T is contained in this range.
479  static RangeT<T> Full() {
480  return RangeT<T>(-std::numeric_limits<T>::infinity(),
481  std::numeric_limits<T>::infinity());
482  }
483 
484  /// Returns the most empty range possible. The lower bound is
485  /// greater than everything, and the upper bound is less than
486  /// everything. Useful when finding the min/max values of an
487  /// array of numbers.
488  static RangeT<T> Empty() {
489  return RangeT<T>(std::numeric_limits<T>::infinity(),
490  -std::numeric_limits<T>::infinity());
491  }
492 
493  /// Returns the range of positive numbers: [0, +infinity].
494  static RangeT<T> Positive() {
495  return RangeT<T>(0.0f, std::numeric_limits<T>::infinity());
496  }
497 
498  /// Returns the range of negative numbers.: [-infinity, 0].
499  static RangeT<T> Negative() {
500  return RangeT<T>(-std::numeric_limits<T>::infinity(), 0.0f);
501  }
502 
503  private:
504  T start_; // Start of the range. Range is valid if start_ <= end_.
505  T end_; // End of the range. Range is inclusive of start_ and end_.
506 };
507 
508 /// Given two numbers, create a range that has the lower one as min,
509 /// and the higher one as max.
510 template <class T>
511 RangeT<T> CreateValidRange(const T a, const T b) {
512  return RangeT<T>(std::min<T>(a, b), std::max<T>(a, b));
513 }
514 
515 // Instantiate for various scalars.
516 typedef RangeT<float> RangeFloat;
517 typedef RangeT<double> RangeDouble;
518 typedef RangeT<int> RangeInt;
519 typedef RangeT<unsigned int> RangeUInt;
520 
521 // Since the float specialization will be most common, we give it a simple name.
522 typedef RangeFloat Range;
523 
524 // Useful constants.
525 static const Range kAngleRange(-static_cast<float>(M_PI),
526  static_cast<float>(M_PI));
527 static const Range kInvalidRange;
528 
529 } // namespace motive
530 
531 #endif // MOTIVE_MATH_RANGE_H_
float Percent(const T x) const
Definition: range.h:93
static RangeT Union(const RangeT &a, const RangeT &b)
Return the smallest range that covers all of 'a' and 'b'.
Definition: range.h:309
float ModDiffNegative(T a, T b) const
Definition: range.h:203
bool ContainsExcludingEnd(const T x) const
Definition: range.h:239
float ModDiffClose(T a, T b) const
Definition: range.h:184
static size_t IndexOfLongest(const RangeT *ranges, size_t len)
Return the index of the longest range in ranges.
Definition: range.h:401
T ClampBeforeEnd(const T x) const
Definition: range.h:78
static T ClampToClosest(T x, const RangeT *ranges, size_t len)
Return the index of the shortest range in ranges.
Definition: range.h:439
static RangeT< T > Positive()
Returns the range of positive numbers: [0, +infinity].
Definition: range.h:494
bool ContainsExcludingStart(const T x) const
Definition: range.h:233
RangeT Invert() const
Definition: range.h:254
RangeT Include(const T x) const
Definition: range.h:265
static RangeT Intersect(const RangeT &a, const RangeT &b)
Definition: range.h:288
Definition: range.h:46
static size_t IntersectRanges(const RangeT *a, size_t len_a, const RangeT *b, size_t len_b, RangeT *intersections, RangeT *gaps=nullptr, size_t *len_gaps=nullptr)
Definition: range.h:361
T Lerp(const float percent) const
Definition: range.h:87
T NormalizeWildValue(T x) const
Definition: range.h:155
bool Contains(const T x) const
Return true if x is in [start_, end_], i.e. the inclusive range.
Definition: range.h:229
RangeT operator*(const float s) const
Scale by multiplying by a scalar.
Definition: range.h:276
float ModDiffPositive(T a, T b) const
Definition: range.h:195
Definition: range.h:40
float ModDiff(T a, T b, ModularDirection direction) const
Definition: range.h:211
T ClampAfterStart(const T x) const
Definition: range.h:74
T NormalizeCloseValue(T x) const
Definition: range.h:123
static RangeT< T > CoversLambda(const S *array, size_t len, const F &f)
Definition: range.h:465
float ModDiffFar(T a, T b) const
Return the farthest difference from 'a' to 'b' under modular arithmetic.
Definition: range.h:187
T DistanceFrom(const T x) const
Definition: range.h:82
bool operator==(const RangeT &rhs) const
Equality is strict. No epsilon checking here.
Definition: range.h:270
static RangeT< T > Full()
Returns the complete range. Every T is contained in this range.
Definition: range.h:479
T PercentClamped(const T x) const
Definition: range.h:97
T Middle() const
Definition: range.h:61
static size_t ValuesInRange(const RangeT &range, T epsilon, size_t num_values, T *values)
Definition: range.h:336
T Normalize(T x) const
Definition: range.h:107
T start() const
Accessors.
Definition: range.h:279
static RangeT< T > Negative()
Returns the range of negative numbers.: [-infinity, 0].
Definition: range.h:499
static size_t IndexOfShortest(const RangeT *ranges, size_t len)
Return the index of the shortest range in ranges.
Definition: range.h:420
T ModularAdjustment(T x) const
Definition: range.h:175
static RangeT< T > Empty()
Definition: range.h:488
RangeT Lengthen(const float percent) const
Definition: range.h:258
bool StrictlyContains(const T x) const
Return true if x is in (start_, end_), i.e. the exclusive range.
Definition: range.h:242
bool ContainsWithTolerance(const T x, const T percent) const
Definition: range.h:246
static RangeT< T > Covers(const T *array, size_t len)
Return the range that covers all values in array.
Definition: range.h:474
T Clamp(const T x) const
Definition: range.h:70
T Length() const
Definition: range.h:65
Represent an interval on a number line.
Definition: range.h:37
bool Valid() const
A range is valid if it contains at least one number.
Definition: range.h:57