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