bc775baed0ba6a16b39f25e77fd89970b9d20c97
[lighthouse] / 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 void Track::set_key(long tm, float val)
22 {
23         int idx = find_key(tm);
24         if(idx >= 0) {
25                 keys[idx].value = val;
26                 return;
27         }
28
29         TrackKey key = {tm, val};
30         keys.push_back(key);
31         keys_sorted = false;
32 }
33
34 float Track::get_key(long tm) const
35 {
36         int idx = find_key(tm);
37         if(idx == -1) return 0.0f;
38         return keys[idx].value;
39 }
40
41 int Track::find_key(long tm) const
42 {
43         int sz = (int)keys.size();
44         for(int i=0; i<sz; i++) {
45                 if(keys[i].time == tm) return i;
46         }
47         return -1;
48 }
49
50 static bool keycmp(const TrackKey &a, const TrackKey &b)
51 {
52         return a.time < b.time;
53 }
54
55 float Track::operator ()(long tm) const
56 {
57         if(keys.empty()) {
58                 return defval;
59         }
60
61         int nkeys = keys.size();
62         if(nkeys == 1) {
63                 return keys[0].value;
64         }
65         if(!keys_sorted) {
66                 Track *track = (Track*)this;
67                 std::sort(track->keys.begin(), track->keys.end(), keycmp);
68                 keys_sorted = true;
69         }
70
71         long tstart = keys[0].time;
72         long tend = keys[nkeys - 1].time;
73
74         tm = remap_time(extrap, tm, tstart, tend);
75
76         int idx0 = get_interval(tm);
77         assert(idx0 >= 0 && idx0 < nkeys);
78         int idx1 = idx0 + 1;
79
80         if(idx0 == nkeys - 1) {
81                 return keys[idx0].value;
82         }
83
84         float dt = (float)(keys[idx1].time - keys[idx0].time);
85         float t = (float)(tm - keys[idx0].time) / dt;
86
87         switch(interp) {
88         case INTERP_STEP:
89                 return keys[idx0].value;
90
91         case INTERP_SIGMOID:
92                 t = smoothstep(0, 1, t);
93         case INTERP_LINEAR:
94                 return keys[idx0].value + (keys[idx1].value - keys[idx0].value) * t;
95
96         default:
97                 break;
98         }
99         return 0.0f;
100 }
101
102 int Track::get_interval(long tm) const
103 {
104         int nkeys = (int)keys.size();
105
106         for(int i=0; i<nkeys-1; i++) {
107                 if(tm < keys[i + 1].time) {
108                         return i;
109                 }
110         }
111         return nkeys - 1;
112 }
113
114 static long remap_time(ExtrapMode mode, long t, long t0, long t1)
115 {
116         long interval = t1 - t0;
117
118         switch(mode) {
119         case EXTRAP_CLAMP:
120                 if(interval <= 0) return t0;
121                 return t < t0 ? t0 : (t >= t1 ? t1 : t);
122
123         case EXTRAP_REPEAT:
124                 if(interval > 0) {
125                         long x = (t - t0) % interval;
126                         if(x < 0) x += interval;
127                         return x + t0;
128                 }
129                 return t0;
130
131         default:
132                 break;
133         }
134
135         assert(!"unreachable");
136         return t;
137 }
138
139 static float smoothstep(float a, float b, float x)
140 {
141         if(x < a) return 0.0f;
142         if(x >= b) return 1.0f;
143
144         x = (x - a) / (b - a);
145         return x * x * (3.0f - 2.0f * x);
146 }