Track.h
Go to the documentation of this file.
1#pragma once
2
3#include "VariantValue.h"
5
6namespace armarx::trajectory
7{
8
9 /**
10 * @brief A keyframe, representing a value at a given time.
11 */
12 template <typename ValueT>
13 struct Keyframe
14 {
15 /// Constructor.
16 Keyframe(float time, const ValueT& value) : time(time), value(value)
17 {
18 }
19
20 float time; ///< The time on the timeline.
21 ValueT value; ///< The value.
22 };
23
24 /// A keyframe with of type TValue.
26
27 /**
28 * @brief A track represents the timeline of a single value, identified by a track ID.
29 * A track is comprised of a sequence of keyframes and used to call a
30 * single update function.
31 */
32 template <typename ValueT>
33 class Track
34 {
35 public:
36 /// The update function type.
37 using UpdateFunc = std::function<void(ValueT)>;
38 // Forward declaration.
39 struct KeyframeProxy;
40
41 public:
42 /// Construct a track with given ID (and no update function).
43 Track(const TrackID& id) : id(id)
44 {
45 }
46
47 /// Construct a track with given ID and update function.
48 Track(const TrackID& id, UpdateFunc updateFunc) : id(id), updateFunc(updateFunc)
49 {
50 }
51
52 /// Indicate whether this track does not contain any keyframes.
53 bool empty() const;
54 /// Clear the track of all keyframes.
55 void clear();
56
57 /// Add a keyframe to this track.
58 void addKeyframe(const Keyframe<ValueT>& keyframe);
59 /// Add a keyframe to this track.
60 void addKeyframe(float time, const ValueT& value);
61
62 /// Add a keyframe by assignment: `track[time] = value;`
63 KeyframeProxy operator[](float time);
64
65
66 /**
67 * @brief Get the interpolated value at the given time.
68 * @throws `error::EmptyTrack` If the track is empty.
69 */
70 ValueT at(float time) const;
71
72 /**
73 * @brief Call the update function with the interpolated value at the given time.
74 *
75 * @param ignoreIfEmpty If true and the track is empty, the method does nothing.
76 * @throws `error::EmptyTrack` If the track is empty and `ignoreIfEmpty` is false.
77 */
78 void update(float time, bool ignoreIfEmpty = false);
79
80 template <typename V>
81 friend std::ostream& operator<<(std::ostream& os, const Track<V>& track);
82
83
84 private:
85 /// Sort the keyframes.
86 void sortKeyframes();
87
88 /**
89 * @throw `error::WrongValueTypeInKeyframe` If the given value's type
90 * conflicts with contained keyframes.
91 */
92 void checkValueType(const ValueT& value);
93
94
95 /// The track ID.
96 TrackID id;
97 /// The update function.
98 UpdateFunc updateFunc;
99 /// The sorted array of keyframes.
100 std::vector<Keyframe<ValueT>> keyframes;
101
102
103 public:
104 /// A proxy allowing for adding keyframes by: `track[time] = value;`
105 struct KeyframeProxy
106 {
107 public:
108 /// Get the value at time.
109 operator ValueT() const;
110 /// Add a keyframe on assignment.
111 void operator=(const ValueT& value);
112 /// Add a keyframe on assignment.
113 void operator=(const KeyframeProxy& value);
114
115 /// Get the value at time.
116 ValueT value() const;
117
118 private:
119 friend Track; // Allow TrackBase to use constructor.
120
121 KeyframeProxy(Track& track, float time);
122
123 Track& track;
124 float time;
125 };
126 };
127
128 /// A track with value type TValue.
130
131 template <typename V>
132 bool
134 {
135 return keyframes.empty();
136 }
137
138 template <typename V>
139 void
141 {
142 keyframes.clear();
143 }
144
145 template <typename V>
146 void
147 Track<V>::addKeyframe(const Keyframe<V>& keyframe)
148 {
149 checkValueType(keyframe.value);
150 keyframes.push_back(keyframe);
151 sortKeyframes();
152 }
153
154 template <typename V>
155 void
156 Track<V>::addKeyframe(float time, const V& value)
157 {
158 addKeyframe(Keyframe<V>(time, value));
159 }
160
161 template <typename V>
162 auto
164 {
165 return KeyframeProxy{*this, time};
166 }
167
168 template <typename V>
169 void
170 Track<V>::sortKeyframes()
171 {
172 std::sort(keyframes.begin(),
173 keyframes.end(),
174 [](const Keyframe<V>& lhs, const Keyframe<V>& rhs)
175 { return lhs.time < rhs.time; });
176 }
177
178 /// In general, do nothing.
179 template <typename V>
180 void
181 Track<V>::checkValueType(const V&)
182 {
183 }
184
185 /**
186 * If the track is not empty, check whether the given type of the given
187 * value is the same as the type of the values already in the track.
188 *
189 * @throws `error::WrongValueTypeInKeyframe` if the types do not match.
190 */
191 template <>
192 void Track<VariantValue>::checkValueType(const VariantValue& value);
193
194 template <typename V>
195 V
196 Track<V>::at(float time) const
197 {
198 if (empty())
199 {
200 throw error::EmptyTrack(id);
201 }
202
203 if (keyframes.size() == 1)
204 {
205 return keyframes.front().value;
206 }
207
208 if (time <= keyframes.front().time)
209 {
210 return keyframes.front().value;
211 }
212 if (time >= keyframes.back().time)
213 {
214 return keyframes.back().value;
215 }
216
217
218 std::size_t i = 0;
219 while (i + 1 <= keyframes.size() && keyframes[i + 1].time < time)
220 {
221 i++;
222 }
223
224 // interpolate between i and i+1
225 const Keyframe<V>& kf1 = keyframes.at(i);
226 const Keyframe<V>& kf2 = keyframes.at(i + 1);
227
228 float t = (time - kf1.time) / (kf2.time - kf1.time);
229 // t = 0 => full kf1, t = 1 => full kf2
230
231 return interpolate::linear<V>(t, kf1.value, kf2.value);
232 }
233
234 template <typename V>
235 void
236 Track<V>::update(float time, bool ignoreIfEmpty)
237 {
238 if (updateFunc && !(ignoreIfEmpty && empty()))
239 {
240 updateFunc(at(time));
241 }
242 }
243
244 template <typename V>
245 Track<V>::KeyframeProxy::KeyframeProxy(Track& track, float time) : track(track), time(time)
246 {
247 }
248
249 template <typename V>
250 Track<V>::KeyframeProxy::operator V() const
251 {
252 return track.at(time);
253 }
254
255 template <typename V>
256 void
258 {
259 track.addKeyframe(time, value);
260 }
261
262 template <typename V>
263 void
264 Track<V>::KeyframeProxy::operator=(const KeyframeProxy& other)
265 {
266 track.addKeyframe(time, other.value());
267 }
268
269 template <typename V>
270 auto
272 {
273 return track.at(time);
274 }
275
276 template <typename ValueT>
277 std::ostream&
278 operator<<(std::ostream& os, const Track<ValueT>& track)
279 {
280 os << "Track '" << track.id << "' with " << track.keyframes.size() << " keyframes: [";
281 for (const Keyframe<ValueT>& kf : track.keyframes)
282 {
283 os << kf.time << ", ";
284 }
285 return os << "]";
286 }
287
288} // namespace armarx::trajectory
A track represents the timeline of a single value, identified by a track ID.
Definition Track.h:34
ValueT at(float time) const
Get the interpolated value at the given time.
Definition Track.h:196
void addKeyframe(float time, const ValueT &value)
Add a keyframe to this track.
Track(const TrackID &id)
Construct a track with given ID (and no update function).
Definition Track.h:43
void update(float time, bool ignoreIfEmpty=false)
Call the update function with the interpolated value at the given time.
Definition Track.h:236
friend std::ostream & operator<<(std::ostream &os, const Track< V > &track)
bool empty() const
Indicate whether this track does not contain any keyframes.
Definition Track.h:133
std::function< void(ValueT)> UpdateFunc
The update function type.
Definition Track.h:37
void addKeyframe(const Keyframe< ValueT > &keyframe)
Add a keyframe to this track.
void clear()
Clear the track of all keyframes.
Definition Track.h:140
KeyframeProxy operator[](float time)
Add a keyframe by assignment: track[time] = value;
Definition Track.h:163
Track(const TrackID &id, UpdateFunc updateFunc)
Construct a track with given ID and update function.
Definition Track.h:48
ReturnT linear(float t, const VariantValue &lhs, const VariantValue &rhs)
Definition linear.h:58
std::variant< float, Eigen::MatrixXf, Eigen::Quaternionf > VariantValue
Variant for trajectory values.
Keyframe< VariantValue > VariantKeyframe
A keyframe with of type TValue.
Definition Track.h:25
Track< VariantValue > VariantTrack
A track with value type TValue.
Definition Track.h:129
std::string TrackID
ID of tracks.
A keyframe, representing a value at a given time.
Definition Track.h:14
Keyframe(float time, const ValueT &value)
Constructor.
Definition Track.h:16
A proxy allowing for adding keyframes by: track[time] = value;
Definition Track.h:106
void operator=(const ValueT &value)
Add a keyframe on assignment.
ValueT value() const
Get the value at time.
Definition Track.h:271