1% =========================================================================== %
2%> @brief animates a quaternion vector with position data
4%>
animateQuat(quat, pos) animates the orientation data in the quaternion vector
5%> with the position data in the position vector.
7%>
animateQuat(quat, pos, options) allows to specify additional options
for the
10%>
animateQuat(quat1, pos1, quat2, pos2, ...) animates multiple quaternion and
11%> position vectors in the same figure.
13%> @param quat - Nx4 numeric vector or Nx1 quaternion vector
14%> @param pos - Nx3 numeric vector with position data
15%> @param options (optional) - struct with optional input arguments
16%> - names - cell array with names for the orientation data (default: 1:N)
17%> - freq - Sampling frequency of the data [Hz] (default: 200)
18%> - fps - Desired Frames per second [Hz] (default: 30)
19%> - speedup - Speedup factor of the animation (default: 1)
20%> - title - Title of the figure (default: "Orientation and Position Animation")
21%> - position - Position of the figure window (default: [0,0,0.5,0.5])
27%> @copyright see the file @ref LICENSE in the root directory of the repository
28% =========================================================================== %
34 quat (:,:) {mustBeNonmissing}
35 pos (:,3) {mustBeNonmissing}
39 options.names (:,1) string = string.empty(1,0)
40 options.freq (1,1) double = 200;
41 options.fps (1,1)
double = 30;
42 options.speedup (1,1)
double = 1;
43 options.title (1,1)
string =
"Orientation and Position Animation"
44 options.position = [0,0,0.5,0.5]
49% check
if quaternion vector is provided
50% define max sample size
52 if ~isa(quat{i},
'quaternion')
53 if size(quat{i}, 2) ~= 4
54 error(
'Quaternion vector must be Nx4');
56 quat{i} = quaternion(quat{i});
61 N_samples = max(N_samples, numel(quat{i}));
64% check
if position vector is the same size as the quaternion vector
66 if size(pos{i}, 1) ~= size(quat{i}, 1)
67 error(
'Position vector must be the same size as the quaternion vector');
71% check
if names are provided
72if isempty(options.names) || numel(options.names) ~= N_quat
73 options.names = string(1:N_quat);
78smoothed_fps = options.fps;
81t = timer(
'ExecutionMode',
'fixedRate',
'Period', round(1/options.fps,3),
'TimerFcn', @
timer_callback);
85fig = figure(
'Name', options.title,
'NumberTitle',
'off',
'MenuBar',
'none',
'ToolBar',
'figure',
'Units',
'normalized',
"Position",options.position);
89 title(options.names(i));
91 plot3(pos{i}(:,1), pos{i}(:,2), pos{i}(:,3),
'k',
'LineWidth', 2);
95% add a close request function to the figure
98% add a time passed indicator to the bottom left of the figure
99text_passed = uicontrol(
'Style',
'text',
'String', sprintf(
'Time passed: %.2f s', 0),
'Units',
'normalized',
'Position', [0.01 0.01 0.3 0.05],
'FontSize', 12,
'FontWeight',
'bold');
101% add a fps indicator to the bottom right of the figure
102text_fps = uicontrol(
'Style',
'text',
'String', sprintf(
'FPS: %d', options.fps),
'Units',
'normalized',
'Position', [0.69 0.01 0.3 0.05],
'FontSize', 12,
'FontWeight',
'bold');
104% add a line to the top of the figure that indicates the current time
105line_curr_time = annotation(
'line', [0 0], [1,1],
'Units',
'normalized',
'LineWidth', 5,
'Color',
'r');
111% timer callback function
113 if t.UserData > N_samples
118 if t.UserData < numel(quat{i})
119 pp(i).Orientation = quat{i}(t.UserData);
120 pp(i).Position = pos{i}(t.UserData, :);
122 pp(i).PatchFaceColor =
'r';
126 % update time passed indicator
127 text_passed.String = sprintf(
'Time passed: %.2f s', t.UserData/options.freq);
129 % update frequency indicator
130 curr_fps = 1/(t.InstantPeriod);
132 curr_fps = options.fps;
135 % Smooth the FPS value
using an IIR filter
136 alpha = 0.05; % Smoothing factor
137 smoothed_fps = alpha * round(curr_fps) + (1 - alpha) * smoothed_fps;
138 text_fps.String = sprintf(
'FPS: %d', round(smoothed_fps));
140 % calculate steps to skip to keep up with the desired frequency
141 skip = round(options.speedup * options.freq/curr_fps);
143 % update current time indicator
144 line_curr_time.X = [0, t.UserData/N_samples];
147 t.UserData = t.UserData + skip;
151% close request function
function animateQuatPos(in quat, in pos, in options)
function timer_callback(in ignoredArg, in ignoredArg)
function my_closereq(in ignoredArg, in ignoredArg)
function animateQuat(in quat, in options)