VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
curveEvaluator.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3#include "curveEvaluator.h"
4
5#include <cmath>
6
7#include "curve.h"
8
9namespace visutwin::canvas
10{
11 CurveEvaluator::CurveEvaluator(Curve* curve, const float time) : _curve(curve)
12 {
13 reset(time);
14 }
15
16 float CurveEvaluator::evaluate(const float time, const bool forceReset)
17 {
18 if (!_curve) {
19 return 0.0f;
20 }
21
22 if (forceReset || time < _left || time >= _right) {
23 reset(time);
24 }
25
26 if (_curve->type == CURVE_STEP) {
27 return _p0;
28 }
29
30 const float t = (_recip == 0.0f) ? 0.0f : (time - _left) * _recip;
31 if (_curve->type == CURVE_LINEAR) {
32 return _p0 + (_p1 - _p0) * t;
33 }
34
35 if (_curve->type == CURVE_SMOOTHSTEP) {
36 const float s = t * t * (3.0f - 2.0f * t);
37 return _p0 + (_p1 - _p0) * s;
38 }
39
40 return evaluateHermite(_p0, _p1, _m0, _m1, t);
41 }
42
43 void CurveEvaluator::reset(const float time)
44 {
45 if (!_curve) {
46 _left = -std::numeric_limits<float>::infinity();
47 _right = std::numeric_limits<float>::infinity();
48 _recip = 0.0f;
49 _p0 = _p1 = _m0 = _m1 = 0.0f;
50 return;
51 }
52
53 const auto& keys = _curve->keys;
54 const size_t len = keys.size();
55
56 if (len == 0) {
57 _left = -std::numeric_limits<float>::infinity();
58 _right = std::numeric_limits<float>::infinity();
59 _recip = 0.0f;
60 _p0 = _p1 = _m0 = _m1 = 0.0f;
61 return;
62 }
63
64 if (time < keys[0].first) {
65 _left = -std::numeric_limits<float>::infinity();
66 _right = keys[0].first;
67 _recip = 0.0f;
68 _p0 = _p1 = keys[0].second;
69 _m0 = _m1 = 0.0f;
70 return;
71 }
72
73 if (time >= keys[len - 1].first) {
74 _left = keys[len - 1].first;
75 _right = std::numeric_limits<float>::infinity();
76 _recip = 0.0f;
77 _p0 = _p1 = keys[len - 1].second;
78 _m0 = _m1 = 0.0f;
79 return;
80 }
81
82 size_t index = 0;
83 while (time >= keys[index + 1].first) {
84 index++;
85 }
86
87 _left = keys[index].first;
88 _right = keys[index + 1].first;
89 const float diff = 1.0f / (_right - _left);
90 _recip = std::isfinite(diff) ? diff : 0.0f;
91 _p0 = keys[index].second;
92 _p1 = keys[index + 1].second;
93
94 if (_curve->type == CURVE_SPLINE) {
95 calcTangents(keys, index);
96 } else {
97 _m0 = _m1 = 0.0f;
98 }
99 }
100
101 void CurveEvaluator::calcTangents(const std::vector<std::pair<float, float>>& keys, const size_t index)
102 {
103 std::pair<float, float> a;
104 const std::pair<float, float>& b = keys[index];
105 const std::pair<float, float>& c = keys[index + 1];
106 std::pair<float, float> d;
107
108 if (index == 0) {
109 a = {
110 keys[0].first + (keys[0].first - keys[1].first),
111 keys[0].second + (keys[0].second - keys[1].second)
112 };
113 } else {
114 a = keys[index - 1];
115 }
116
117 if (index == keys.size() - 2) {
118 d = {
119 keys[index + 1].first + (keys[index + 1].first - keys[index].first),
120 keys[index + 1].second + (keys[index + 1].second - keys[index].second)
121 };
122 } else {
123 d = keys[index + 2];
124 }
125
126 const float s1_ = 2.0f * (c.first - b.first) / (c.first - a.first);
127 const float s2_ = 2.0f * (c.first - b.first) / (d.first - b.first);
128
129 _m0 = _curve->tension * (std::isfinite(s1_) ? s1_ : 0.0f) * (c.second - a.second);
130 _m1 = _curve->tension * (std::isfinite(s2_) ? s2_ : 0.0f) * (d.second - b.second);
131 }
132
133 float CurveEvaluator::evaluateHermite(const float p0, const float p1, const float m0, const float m1, const float t)
134 {
135 const float t2 = t * t;
136 const float twot = t + t;
137 const float omt = 1.0f - t;
138 const float omt2 = omt * omt;
139
140 return p0 * ((1.0f + twot) * omt2) +
141 m0 * (t * omt2) +
142 p1 * (t2 * (3.0f - twot)) +
143 m1 * (t2 * (t - 1.0f));
144 }
145}
float evaluate(float time, bool forceReset=false)
CurveEvaluator(Curve *curve, float time=0.0f)
std::vector< std::pair< float, float > > keys
Definition curve.h:27