NAP
fcurve.h
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 
7 #include <utility/dllexport.h>
8 #include <rtti/rtti.h>
9 #include <rtti/object.h>
10 #include <glm/glm.hpp>
11 #include <nap/resource.h>
12 
13 namespace nap
14 {
15  namespace math
16  {
20  enum class ECurveInterp : int
21  {
22  Bezier = 0,
23  Linear,
24  Stepped
25  };
26 
27 
34  template<typename T, typename V>
35  struct FComplex
36  {
40  FComplex() = default;
41 
47  FComplex(const T& t, const V& value) : mTime(t), mValue(value) {}
48 
49  T mTime;
50  V mValue;
51 
52  FComplex operator+(const FComplex& other) const
53  {
54  return {mTime + other.mTime, mValue + other.mValue};
55  }
56 
57  FComplex operator-(const FComplex& other) const
58  {
59  return {mTime - other.mTime, mValue - other.mValue};
60  }
61 
62  FComplex operator*(const T& other) const
63  {
64  return {mTime * other, mValue * other};
65  }
66  };
67 
68 
74  template<typename T, typename V>
75  struct FCurvePoint
76  {
80  FCurvePoint() = default;
81 
88  FCurvePoint(const FComplex<T, V>& pos, const FComplex<T, V>& inTan, const FComplex<T, V>& outTan)
89  : mPos(pos), mInTan(inTan), mOutTan(outTan) {}
90 
95 
96  bool mTangentsAligned = true;
97  };
98 
99 
101  // 1-D Curve Resource
103 
111  template<typename T, typename V>
112  class FCurve : public Resource
113  {
114  RTTI_ENABLE(Resource)
115  public:
119  FCurve();
120 
124  ~FCurve() override { mSortedPoints.clear(); }
125 
126 
132  V evaluate(const T& t);
133 
137  void invalidate() { mPointsSorted = false; }
138 
142  std::vector<FCurvePoint<T, V>> mPoints;
143 
144  private:
148  FComplex<T, V> bezier(const FComplex<T, V> (& pts)[4], T t);
149 
161  T tForX(const FComplex<T, V> (& pts)[4], T x, T threshold = 0.0001, int maxIterations = 100);
162 
170  V lerp(const V& a, const V& b, const T& t);
171 
181  V evalCurveSegmentBezier(const nap::math::FComplex<T, V> (& pts)[4], T x);
182 
192  V evalCurveSegmentLinear(const FComplex<T, V> (& pts)[4], T x);
193 
203  V evalCurveSegmentStepped(const FComplex<T, V>(& pts)[4], T x);
204 
208  void sortPoints();
209 
215  int pointIndexAtTime(const T& t) const;
216 
225  void limitOverhang(const T& x0, T& x1, T& x2, const T& x3);
226 
235  void limitOverhangPoints(const FComplex<T, V>& pa, FComplex<T, V>& pb, FComplex<T, V>& pc, const FComplex<T, V>& pd);
236 
237  mutable std::vector<FCurvePoint<T, V>*> mSortedPoints;
238  bool mPointsSorted = false;
239  };
240 
241 
243  // Type definitions for all supported curve types
245 
250 
255 
260 
261 
263  // Template Definitions
265 
266  template<typename T, typename V>
268  {
269  if (mPoints.empty())
270  return V();
271 
272  // Ensure points are sorted before evaluation
273  if (!mPointsSorted)
274  {
275  sortPoints();
276  mPointsSorted = true;
277  }
278 
279  const FCurvePoint<T, V>* firstPoint = mSortedPoints[0];
280  if (t < firstPoint->mPos.mTime)
281  return firstPoint->mPos.mValue;
282 
283  const FCurvePoint<T, V>* lastPoint = mSortedPoints[mSortedPoints.size() - 1];
284  if (t >= lastPoint->mPos.mTime)
285  return lastPoint->mPos.mValue;
286 
287  int idx = pointIndexAtTime(t);
288  const FCurvePoint<T, V>* curr = mSortedPoints[idx];
289  const FCurvePoint<T, V>* next = mSortedPoints[idx + 1];
290 
291  auto a = curr->mPos;
292  auto b = a + curr->mOutTan;
293  auto d = next->mPos;
294  auto c = d + next->mInTan;
295 
296  limitOverhangPoints(a, b, c, d);
297  switch (curr->mInterp)
298  {
300  return evalCurveSegmentBezier({ a, b, c, d }, t);
302  return evalCurveSegmentLinear({ a, b, c, d }, t);
304  return evalCurveSegmentStepped({ a, b, c, d }, t);
305  default:
306  assert(false);
307  }
308 
309  return V();
310  }
311 
312  template<typename T, typename V>
314  {
315  T u = 1 - t;
316  T a = u * u * u;
317  T b = 3 * u * u * t;
318  T c = 3 * u * t * t;
319  T d = t * t * t;
320  return pts[0] * a + pts[1] * b + pts[2] * c + pts[3] * d;
321  }
322 
323 
324  template<typename T, typename V>
325  T nap::math::FCurve<T, V>::tForX(const FComplex<T, V>(&pts)[4], T x, T threshold /*= 0.0001*/, int maxIterations /*= 100*/)
326  {
327  T depth = 0.5;
328  T t = 0.5;
329  for (int i = 0; i < maxIterations; i++)
330  {
331  auto dx = x - bezier(pts, t).mTime;
332  auto adx = std::abs(dx);
333  if (adx <= threshold)
334  break;
335 
336  depth *= 0.5;
337  t += (dx > 0) ? depth : -depth;
338  }
339  return t;
340  }
341 
342  template<typename T, typename V>
343  V nap::math::FCurve<T, V>::lerp(const V& a, const V& b, const T& t)
344  {
345  return a + t * (b - a);
346  }
347 
348  template<typename T, typename V>
350  {
351  T t = tForX(pts, x);
352  return bezier(pts, t).mValue;
353  }
354 
355  template<typename T, typename V>
356  V nap::math::FCurve<T, V>::evalCurveSegmentLinear(const FComplex<T, V>(&pts)[4], T x)
357  {
358  const auto& a = pts[0];
359  const auto& b = pts[3];
360  T t = (x - a.mTime) / (b.mTime - a.mTime);
361  return lerp(a.mValue, b.mValue, t);
362  }
363 
364 
365  template<typename T, typename V>
366  V nap::math::FCurve<T, V>::evalCurveSegmentStepped(const FComplex<T, V>(&pts)[4], T x)
367  {
368  return pts[0].mValue;
369  }
370 
371  template<typename T, typename V>
373  {
374  mSortedPoints.clear();
375  for (int i = 0; i < mPoints.size(); i++)
376  mSortedPoints.emplace_back(&mPoints[i]);
377 
378  std::sort(mSortedPoints.begin(), mSortedPoints.end(),
379  [](const FCurvePoint<T, V>* lhs, const FCurvePoint<T, V>* rhs)
380  {
381  return lhs->mPos.mTime < rhs->mPos.mTime;
382  });
383  }
384 
385  template<typename T, typename V>
386  int nap::math::FCurve<T, V>::pointIndexAtTime(const T& t) const
387  {
388  for (size_t i = 0, len = mSortedPoints.size(); i < len; i++)
389  {
390  if (t < mSortedPoints[i]->mPos.mTime)
391  {
392  if (i == 0)
393  return 0;
394  return static_cast<int>(i - 1);
395  }
396  }
397  assert(false);
398  return -1;
399  }
400 
401  template<typename T, typename V>
402  void nap::math::FCurve<T, V>::limitOverhang(const T& x0, T& x1, T& x2, const T& x3)
403  {
404  x1 = std::min(x1, x3);
405  x2 = std::max(x2, x0);
406  }
407 
408  template<typename T, typename V>
409  void nap::math::FCurve<T, V>::limitOverhangPoints(const FComplex<T, V>& pa, FComplex<T, V>& pb, FComplex<T, V>& pc, const FComplex<T, V>& pd)
410  {
411  auto a = pa.mTime;
412  auto b = pb.mTime;
413  auto c = pc.mTime;
414  auto d = pd.mTime;
415  auto bb = b;
416  auto cc = c;
417 
418  limitOverhang(a, b, c, d);
419 
420  // calculate ratios for both tangents
421  auto rb = (b - a) / (bb - a);
422  auto rc = (c - d) / (cc - d);
423 
424  // apply corrected times
425  pb.mTime = b;
426  pc.mTime = c;
427 
428  // we limited time, keep the value on the tangent line for c1 continuity
429  pb.mValue = ((pb.mValue - pa.mValue) * rb) + pa.mValue;
430  pc.mValue = ((pc.mValue - pd.mValue) * rc) + pd.mValue;
431  }
432 
433 
435  // Forward Declarations
437 
438  template<>
440 
441  template<>
443  }
444 }
nap::math::FCurve::mPoints
std::vector< FCurvePoint< T, V > > mPoints
Definition: fcurve.h:142
nap::math::FComplex
Definition: fcurve.h:35
nap::math::ECurveInterp
ECurveInterp
Definition: fcurve.h:20
nap::math::FComplex::operator+
FComplex operator+(const FComplex &other) const
Definition: fcurve.h:52
nap::math::FComplex::operator*
FComplex operator*(const T &other) const
Definition: fcurve.h:62
nap::math::ECurveInterp::Stepped
@ Stepped
Stepped interpolated (hold previous value until next curve point.
nap::math::FComplex::mValue
V mValue
Value mapped to the y axis.
Definition: fcurve.h:50
nap::math::FComplex::operator-
FComplex operator-(const FComplex &other) const
Definition: fcurve.h:57
nap::math::FCurve::~FCurve
~FCurve() override
Definition: fcurve.h:124
nap::math::FComplex::FComplex
FComplex()=default
nap::math::FCurvePoint::mInTan
FComplex< T, V > mInTan
Left tangent position, relative to mPos.
Definition: fcurve.h:92
nap::math::FCurvePoint::FCurvePoint
FCurvePoint()=default
nap::math::ECurveInterp::Bezier
@ Bezier
Bezier style interpolation.
nap::math::FCurve
Definition: fcurve.h:112
nap::math::FCurve::invalidate
void invalidate()
Definition: fcurve.h:137
nap::math::FCurve::evaluate
V evaluate(const T &t)
Definition: fcurve.h:267
nap::math::lerp
T lerp(const T &start, const T &end, float percent)
nap
Definition: templateapp.h:17
nap::math::FCurvePoint::mOutTan
FComplex< T, V > mOutTan
Right tangent position, relative to mPos.
Definition: fcurve.h:93
nap::math::FCurvePoint::FCurvePoint
FCurvePoint(const FComplex< T, V > &pos, const FComplex< T, V > &inTan, const FComplex< T, V > &outTan)
Definition: fcurve.h:88
nap::Resource
Definition: resource.h:19
nap::math::FCurve::FCurve
FCurve()
nap::math::FCurvePoint::mInterp
ECurveInterp mInterp
Definition: fcurve.h:94
nap::math::FCurvePoint
Definition: fcurve.h:75
nap::math::FComplex::FComplex
FComplex(const T &t, const V &value)
Definition: fcurve.h:47
nap::math::FCurvePoint::mTangentsAligned
bool mTangentsAligned
Non-essential to functionality, but necessary for editing.
Definition: fcurve.h:96
nap::math::FCurvePoint::mPos
FComplex< T, V > mPos
Position of the handle.
Definition: fcurve.h:91
nap::math::FComplex::mTime
T mTime
Time mapped to the x axis.
Definition: fcurve.h:49
nap::math::ECurveInterp::Linear
@ Linear
Linear style interpolation.