Overte C++ Documentation
CubicHermiteSpline.h
1 //
2 // CubicHermiteSpline.h
3 //
4 // Copyright 2017 High Fidelity, Inc.
5 //
6 // Distributed under the Apache License, Version 2.0.
7 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8 //
9 
10 #ifndef hifi_CubicHermiteSpline_h
11 #define hifi_CubicHermiteSpline_h
12 
13 #include "GLMHelpers.h"
14 
15 class CubicHermiteSplineFunctor {
16 public:
17  CubicHermiteSplineFunctor() : _p0(), _m0(), _p1(), _m1() {}
18  CubicHermiteSplineFunctor(const glm::vec3& p0, const glm::vec3& m0, const glm::vec3& p1, const glm::vec3& m1) : _p0(p0), _m0(m0), _p1(p1), _m1(m1) {}
19 
20  CubicHermiteSplineFunctor(const CubicHermiteSplineFunctor& orig) : _p0(orig._p0), _m0(orig._m0), _p1(orig._p1), _m1(orig._m1) {}
21 
22  CubicHermiteSplineFunctor& operator=(const CubicHermiteSplineFunctor&) = default;
23 
24  virtual ~CubicHermiteSplineFunctor() {}
25 
26  // evalute the hermite curve at parameter t (0..1)
27  glm::vec3 operator()(float t) const {
28  float t2 = t * t;
29  float t3 = t2 * t;
30  float w0 = 2.0f * t3 - 3.0f * t2 + 1.0f;
31  float w1 = t3 - 2.0f * t2 + t;
32  float w2 = -2.0f * t3 + 3.0f * t2;
33  float w3 = t3 - t2;
34  return w0 * _p0 + w1 * _m0 + w2 * _p1 + w3 * _m1;
35  }
36 
37  // evaulate the first derivative of the hermite curve at parameter t (0..1)
38  glm::vec3 d(float t) const {
39  float t2 = t * t;
40  float w0 = -6.0f * t + 6.0f * t2;
41  float w1 = 1.0f - 4.0f * t + 3.0f * t2;
42  float w2 = 6.0f * t - 6.0f * t2;
43  float w3 = -2.0f * t + 3.0f * t2;
44  return w0 * _p0 + w1 * _m0 + w2 * _p1 + w3 * _m1;
45  }
46 
47  // evaulate the second derivative of the hermite curve at paramter t (0..1)
48  glm::vec3 d2(float t) const {
49  float w0 = -6.0f + 12.0f * t;
50  float w1 = -4.0f + 6.0f * t;
51  float w2 = 6.0f - 12.0f * t;
52  float w3 = -2.0f + 6.0f * t;
53  return w0 + _p0 + w1 * _m0 + w2 * _p1 + w3 * _m1;
54  }
55 
56 protected:
57  glm::vec3 _p0;
58  glm::vec3 _m0;
59  glm::vec3 _p1;
60  glm::vec3 _m1;
61 };
62 
63 class CubicHermiteSplineFunctorWithArcLength : public CubicHermiteSplineFunctor {
64 public:
65  enum Constants { NUM_SUBDIVISIONS = 15 };
66 
67  CubicHermiteSplineFunctorWithArcLength() : CubicHermiteSplineFunctor() {
68  memset(_values, 0, sizeof(float) * (NUM_SUBDIVISIONS + 1));
69  }
70  CubicHermiteSplineFunctorWithArcLength(const glm::vec3& p0, const glm::vec3& m0, const glm::vec3& p1, const glm::vec3& m1) : CubicHermiteSplineFunctor(p0, m0, p1, m1) {
71 
72  initValues();
73  }
74 
75  CubicHermiteSplineFunctorWithArcLength(const glm::quat& tipRot, const glm::vec3& tipTrans, const glm::quat& baseRot, const glm::vec3& baseTrans, float baseGain = 1.0f, float tipGain = 1.0f) : CubicHermiteSplineFunctor() {
76 
77  float linearDistance = glm::length(baseTrans - tipTrans);
78  _p0 = baseTrans;
79  _m0 = baseGain * linearDistance * (baseRot * Vectors::UNIT_Y);
80  _p1 = tipTrans;
81  _m1 = tipGain * linearDistance * (tipRot * Vectors::UNIT_Y);
82 
83  initValues();
84  }
85 
86  CubicHermiteSplineFunctorWithArcLength(const CubicHermiteSplineFunctorWithArcLength& orig) : CubicHermiteSplineFunctor(orig) {
87  memcpy(_values, orig._values, sizeof(float) * (NUM_SUBDIVISIONS + 1));
88  }
89 
90  CubicHermiteSplineFunctorWithArcLength& operator=(const CubicHermiteSplineFunctorWithArcLength&) = default;
91 
92  // given the spline parameter (0..1) output the arcLength of the spline up to that point.
93  float arcLength(float t) const {
94  float index = t * NUM_SUBDIVISIONS;
95  int prevIndex = std::min(std::max(0, (int)glm::floor(index)), (int)NUM_SUBDIVISIONS);
96  int nextIndex = std::min(std::max(0, (int)glm::ceil(index)), (int)NUM_SUBDIVISIONS);
97  float alpha = glm::fract(index);
98  return lerp(_values[prevIndex], _values[nextIndex], alpha);
99  }
100 
101  // given an arcLength compute the spline parameter (0..1) that cooresponds to that arcLength.
102  float arcLengthInverse(float s) const {
103  // find first item in _values that is > s.
104  int nextIndex;
105  for (nextIndex = 0; nextIndex < NUM_SUBDIVISIONS; nextIndex++) {
106  if (_values[nextIndex] > s) {
107  break;
108  }
109  }
110  int prevIndex = std::min(std::max(0, nextIndex - 1), (int)NUM_SUBDIVISIONS);
111  float alpha = glm::clamp((s - _values[prevIndex]) / (_values[nextIndex] - _values[prevIndex]), 0.0f, 1.0f);
112  const float DELTA = 1.0f / NUM_SUBDIVISIONS;
113  return lerp(prevIndex * DELTA, nextIndex * DELTA, alpha);
114  }
115 protected:
116  float _values[NUM_SUBDIVISIONS + 1];
117 
118  void initValues() {
119  // initialize _values with the accumulated arcLength along the spline.
120  const float DELTA = 1.0f / NUM_SUBDIVISIONS;
121  float alpha = 0.0f;
122  float accum = 0.0f;
123  _values[0] = 0.0f;
124  for (int i = 1; i < NUM_SUBDIVISIONS + 1; i++) {
125  accum += glm::distance(this->operator()(alpha),
126  this->operator()(alpha + DELTA));
127  alpha += DELTA;
128  _values[i] = accum;
129  }
130 
131  }
132 };
133 
134 #endif // hifi_CubicHermiteSpline_h