forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmeshcat_animation.h
240 lines (197 loc) · 9.44 KB
/
meshcat_animation.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#pragma once
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
#include "drake/common/string_map.h"
#include "drake/math/rigid_transform.h"
namespace drake {
namespace geometry {
// Forward declaration.
class Meshcat;
/** An interface for recording/playback animations in Meshcat. Use
Meshcat::SetAnimation to publish a MeshcatAnimation to the visualizer.
Currently, an animation consists of (only) transforms and properties that are
set at a particular integer frame number. Although we do not support calls to
SetObject/Delete in an animation, you can consider using `SetProperty(frame,
path, "visible", true/false)` in your animation to make the object appear or
disappear at a particular frame.
*/
class MeshcatAnimation {
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(MeshcatAnimation);
/** Constructs the animation object.
@param frames_per_second a positive integer specifying the timing at which the
frames are played back. */
explicit MeshcatAnimation(double frames_per_second = 64.0);
~MeshcatAnimation();
/** Returns the frame rate at which the animation will be played back. */
double frames_per_second() const { return frames_per_second_; }
/** Uses the frame rate to convert from time to the frame number, using
std::floor.
@pre `time` ≥ start_time().
*/
int frame(double time) const {
DRAKE_DEMAND(time >= start_time_);
return static_cast<int>(
std::floor((time - start_time_) * frames_per_second_));
}
// The documentation is adapted from
// https://threejs.org/docs/index.html?q=Animation#api/en/animation/AnimationAction
// The values are from
// https://github.com/mrdoob/three.js/blob/dev/src/constants.js
enum LoopMode {
/** Plays the clip once. */
kLoopOnce = 2200,
/** Plays the clip with the chosen number of repetitions, each time jumping
from the end of the clip directly to its beginning. */
kLoopRepeat = 2201,
/** Plays the clip with the chosen number of repetitions, alternately
playing forward and backward. */
kLoopPingPong = 2202
};
// Accessors.
double start_time() const { return start_time_; }
bool autoplay() const { return play_; }
LoopMode loop_mode() const { return loop_mode_; }
int repetitions() const { return repetitions_; }
bool clamp_when_finished() const { return clamp_when_finished_; }
/** Set the start time of the animation. This is only for convenience; it is
used in the frame() method to allow callers to look up the frame number based
on the current time, the start time, and the frame rate. It is not passed to
Meshcat. It does not change any frames that have previously been set. The
default is zero.*/
void set_start_time(double time) { start_time_ = time; }
/** Set the behavior when the animation is first sent to the visualizer. The
animation will play immediately iff `play` is true. The default is true.*/
void set_autoplay(bool play) { play_ = play; }
/** Sets the loop behavior on play. @see LoopMode for details. The default
is kLoopRepeat. */
void set_loop_mode(LoopMode mode) { loop_mode_ = mode; }
/** Sets the number of repetitions of the animation each time it is played.
This number has no effect when the loop mode is set to kLoopOnce.
`repetitions` must be a positive integer. The default value is 1. */
void set_repetitions(int repetitions) {
DRAKE_DEMAND(repetitions >= 1);
repetitions_ = repetitions;
}
/** Sets the behavior at the end of the animation. If true, then the
animation will automatically be paused on its last frame. If false, the
scene will be reset to before the animation. The default is true.
Note: This setting has no impact if the action is interrupted (it has
only an effect if its last loop has really finished). */
void set_clamp_when_finished(bool clamp) { clamp_when_finished_ = clamp; }
/** Set the RigidTransform at `frame` in the animation for a given `path` in
the the scene tree. @see Meshcat::SetTransform.
@param frame a non-negative integer indicating the frame at which this
transform is applied.
@param path a "/"-delimited string indicating the path in the scene tree.
See @ref meshcat_path "Meshcat paths" for the semantics.
@param X_ParentPath the relative transform from the path to its immediate
parent.
@throws std::exception if the position or quaternion properties of this
path have already been set to an incorrect type.
*/
void SetTransform(int frame, std::string_view path,
const math::RigidTransformd& X_ParentPath);
/** Sets a single named property of the object at the given `path` at the
specified `frame` in the animation. @see Meshcat::SetProperty.
@param frame a non-negative integer indicating the frame at which this
transform is applied.
@param path a "/"-delimited string indicating the path in the scene tree.
See @ref meshcat_path for the semantics.
@param property the string name of the property to set
@param value the new value.
@throws std::exception if this path/property has already been set with a
different type.
@pydrake_mkdoc_identifier{bool}
*/
void SetProperty(int frame, std::string_view path, std::string_view property,
bool value);
/** Sets a single named property of the object at the given `path` at the
specified `frame` in the animation. @see Meshcat::SetProperty.
@param frame a non-negative integer indicating the frame at which this
transform is applied.
@param path a "/"-delimited string indicating the path in the scene tree.
See @ref meshcat_path for the semantics.
@param property the string name of the property to set
@param value the new value.
@throws std::exception if this path/property has already been set with a
different type.
@pydrake_mkdoc_identifier{double}
*/
void SetProperty(int frame, std::string_view path, std::string_view property,
double value);
/** Sets a single named property of the object at the given `path` at the
specified `frame` in the animation. @see Meshcat::SetProperty.
@param frame a non-negative integer indicating the frame at which this
transform is applied.
@param path a "/"-delimited string indicating the path in the scene tree.
See @ref meshcat_path for the semantics.
@param property the string name of the property to set
@param value the new value.
@throws std::exception if this path/property has already been set with a
different type.
@pydrake_mkdoc_identifier{vector_double}
*/
void SetProperty(int frame, std::string_view path, std::string_view property,
const std::vector<double>& value);
// TODO(russt): Consider ColorKeyframeTrack.js and/or StringKeyframeTrack.js
// TODO(russt): Possibly support interpolation modes and ending modes from
// https://threejs.org/docs/#api/en/constants/Animation.
// TODO(russt): Could implement a SetObject/Delete here that would effectively
// implement behind the scenes the work-around I've documented in the class
// documentation. There would be some subtleties to make it robust (e.g.
// setting multiple objects at different frames on the same path, but still
// supporting properties, etc), but it could work.
/** Returns the value information for a particular path/property at a
particular frame if a value of type T has been set, otherwise returns
std::nullopt. This method is intended primarily for testing.
@tparam T One of `bool`, `double`, or `vector<double>` */
template <typename T>
std::optional<T> get_key_frame(int frame, std::string_view path,
std::string_view property) const;
/** Returns the javascript type for a particular path/property, or the empty
string if nothing has been set. This method is intended primarily for
testing. */
std::string get_javascript_type(std::string_view path,
std::string_view property) const;
private:
// A map of frame => value.
template <typename T>
using Track = std::map<int, T>;
// All property values in a track must be the same type.
struct TypedTrack {
std::variant<std::monostate, Track<bool>, Track<double>,
Track<std::vector<double>>>
track;
std::string js_type;
};
// A map of property name => tracks.
using PropertyTracks = string_map<TypedTrack>;
// A map of path => property tracks.
using PathTracks = string_map<PropertyTracks>;
// TODO(russt): Narrow this access to restore encapsulation.
friend class Meshcat;
const TypedTrack* GetTypedTrack(std::string_view path,
std::string_view property) const;
TypedTrack& GetOrCreateTypedTrack(std::string_view path,
std::string_view property);
// Implements the SetProperty methods.
// js_type must match three.js getTrackTypeForValueTypeName implementation.
template <typename T>
void SetProperty(int frame, std::string_view path, std::string_view property,
std::string_view js_type, const T& value);
// A map of path name => property tracks.
PathTracks path_tracks_{};
const double frames_per_second_;
double start_time_{0.0};
bool play_{true};
LoopMode loop_mode_{kLoopRepeat};
int repetitions_{1};
bool clamp_when_finished_{true};
};
} // namespace geometry
} // namespace drake