X logo billboarding
[faros-demo] / src / track.cc
1 #include <assert.h>
2 #include <algorithm>
3 #include "track.h"
4
5 static long remap_time(ExtrapMode mode, long t, long t0, long t1);
6 static float smoothstep(float a, float b, float x);
7
8 Track::Track()
9 {
10         interp = INTERP_SIGMOID;
11         extrap = EXTRAP_CLAMP;
12         keys_sorted = true;
13         defval = 0.0f;
14 }
15
16 void Track::clear()
17 {
18         keys.clear();
19 }
20
21 bool Track::empty() const
22 {
23         return keys.empty();
24 }
25
26 int Track::get_num_keys() const
27 {
28         return (int)keys.size();
29 }
30
31 const TrackKey &Track::operator [](int idx) const
32 {
33         return keys[idx];
34 }
35
36 TrackKey &Track::operator [](int idx)
37 {
38         return keys[idx];
39 }
40
41 void Track::set_value(long tm, float val)
42 {
43         int idx = find_key(tm);
44         if(idx >= 0) {
45                 keys[idx].value = val;
46                 return;
47         }
48
49         TrackKey key = {tm, val};
50         keys.push_back(key);
51         keys_sorted = false;
52 }
53
54 float Track::get_value(long tm) const
55 {
56         int idx = find_key(tm);
57         if(idx == -1) return 0.0f;
58         return keys[idx].value;
59 }
60
61 int Track::find_key(long tm) const
62 {
63         int sz = (int)keys.size();
64         for(int i=0; i<sz; i++) {
65                 if(keys[i].time == tm) return i;
66         }
67         return -1;
68 }
69
70 static bool keycmp(const TrackKey &a, const TrackKey &b)
71 {
72         return a.time < b.time;
73 }
74
75 float Track::operator ()(long tm) const
76 {
77         if(keys.empty()) {
78                 return defval;
79         }
80
81         int nkeys = keys.size();
82         if(nkeys == 1) {
83                 return keys[0].value;
84         }
85         if(!keys_sorted) {
86                 Track *track = (Track*)this;
87                 std::sort(track->keys.begin(), track->keys.end(), keycmp);
88                 keys_sorted = true;
89         }
90
91         long tstart = keys[0].time;
92         long tend = keys[nkeys - 1].time;
93
94         tm = remap_time(extrap, tm, tstart, tend);
95
96         int idx0 = get_interval(tm);
97         assert(idx0 >= 0 && idx0 < nkeys);
98         int idx1 = idx0 + 1;
99
100         if(idx0 == nkeys - 1) {
101                 return keys[idx0].value;
102         }
103
104         float dt = (float)(keys[idx1].time - keys[idx0].time);
105         float t = (float)(tm - keys[idx0].time) / dt;
106
107         switch(interp) {
108         case INTERP_STEP:
109                 return keys[idx0].value;
110
111         case INTERP_SIGMOID:
112                 t = smoothstep(0, 1, t);
113         case INTERP_LINEAR:
114                 return keys[idx0].value + (keys[idx1].value - keys[idx0].value) * t;
115
116         default:
117                 break;
118         }
119         return 0.0f;
120 }
121
122 int Track::get_interval(long tm) const
123 {
124         int nkeys = (int)keys.size();
125
126         for(int i=0; i<nkeys-1; i++) {
127                 if(tm < keys[i + 1].time) {
128                         return i;
129                 }
130         }
131         return nkeys - 1;
132 }
133
134 static long remap_time(ExtrapMode mode, long t, long t0, long t1)
135 {
136         long interval = t1 - t0;
137
138         switch(mode) {
139         case EXTRAP_CLAMP:
140                 if(interval <= 0) return t0;
141                 return t < t0 ? t0 : (t >= t1 ? t1 : t);
142
143         case EXTRAP_REPEAT:
144                 if(interval > 0) {
145                         long x = (t - t0) % interval;
146                         if(x < 0) x += interval;
147                         return x + t0;
148                 }
149                 return t0;
150
151         default:
152                 break;
153         }
154
155         assert(!"unreachable");
156         return t;
157 }
158
159 static float smoothstep(float a, float b, float x)
160 {
161         if(x < a) return 0.0f;
162         if(x >= b) return 1.0f;
163
164         x = (x - a) / (b - a);
165         return x * x * (3.0f - 2.0f * x);
166 }