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