how can I increase the patch animation speed?

51 views (last 30 days)
I am developing a simulation that animates a 3D model. For the visualisation I work with patches. But the speed drops as the number of vertices increase. I have written the below example. In the profiler I see that most time is spent on the command: drawnow. Is there a way to speed this up?
%% Draw a cylindrical patch object
n = 20000;
alpha = linspace(0,360,n);
vertices = zeros(3,2*n);
vertices([1 2],1:n) = [cosd(alpha); sind(alpha)] * 1;
vertices([1 2],n+1:2*n) = vertices([1 2],1:n);
vertices(3,n+1:2*n) = 1;
faces = [1:n; [2:n 1]; [n+2:2*n n+1]; n+1:2*n]';
hp = patch( ...
'EdgeColor', 'none', ...
'FaceColor', [0 0 1], ...
'Faces', faces, ...
'Vertices', vertices');
%% Make up axis
axis equal
axis([-2 2 -2 2 -2 2])
view(20,30)
%% Animate and calculate fps
m = 90;
range = linspace(0,90,m);
Rxx = eye(3);
profile on
tic
for phi = range
Rxx = [1 0 0; 0 cosd(phi) -sind(phi); 0 sind(phi) cosd(phi)];
hp.Vertices = (Rxx * vertices)';
drawnow
end % for phi = range
fps = m/toc
profile viewer
  1 Comment
Star Strider
Star Strider on 28 Jul 2020
Is there a way to speed this up?
Probably not. Consider saving the frames and playing them as a movie. See getframe and its friends (linked to in that page) for details.

Sign in to comment.

Answers (1)

Adam Danz
Adam Danz on 28 Jul 2020
Edited: Adam Danz on 28 Jul 2020
In addition to Star Strider's advice to use getframe, check out drawnow limitrate which will limit the number of times drawnow is executed.
Alternatively, you could update the graphics every n iterations of the loop. The example below updates every 3 iterations and then once again at the end.
m = 90;
range = linspace(0,90,m);
updateFrequency = 3; % every 3 iterations
for i = 1:numel(range)
Rxx = [1 0 0; 0 cosd(range(i)) -sind(range(i)); 0 sind(range(i)) cosd(range(i))];
hp.Vertices = (Rxx * vertices)';
if ismember(i, 1:updateFrequency:numel(range))
drawnow
end
end % for phi = range
drawnow
Lastly, the verticies could all be calculated outside of the loop and then indexed within the loop but I doubt that will save much time and be worth the effort. The drawnow command is clearly the time-killer. Close all graphics other than the animation plot before running the code. The drawnow command updates all graphics from all existing plots, GUIs, and Apps so the more graphics that exist, the more time it will take to update every time drawnow is executed.
  4 Comments
Bruno Luong
Bruno Luong on 3 Aug 2020
Edited: Bruno Luong on 3 Aug 2020
Personally I never manage to underststand how it could help for faster/smother animation, everytime I try it get worse.
I even have an even bigger problem of understand the finality of such command. It looks like it's use to avoid some large queue graphical update requestes internally. But I have no clue how it really works.
Adam Danz
Adam Danz on 3 Aug 2020
Hi Bruno,
I've found that it depends on how fine the movement is in the animation and what else is going on within the loop.
Take this overly-simplified example where a marker moves 10,000 steps along a sinusoid (fine steps, fast loop). The first loop uses the limitrate and second does not. You'll see that there's a tradeoff between speed and smoothness where the version with the limitrate is much faster and much less smoother.
fig = figure();
x = linspace(-pi,pi,10000);
y = sin(x);
h = plot(nan,nan,'o');
xlim([-pi,pi])
ylim([-1,1])
hold on
tic
for i = 1:numel(x)
h.XData = x(i);
h.YData = y(i);
%pause(0.1)
drawnow limitrate
end
fprintf('%.0f seconds with limitrate\n', toc)
tic
for i = 1:numel(x)
h.XData = x(i);
h.YData = y(i);
%pause(0.1)
drawnow
end
fprintf('%.0f seconds without limitrate\n', toc)
Here's another example of the sinusoidal trajectory but with only 25 steps instead of 10k. I added a pause in each loop to simulate other processes that might be happening within the animation loop (coarse steps, more workload).
fig = figure();
x = linspace(-pi,pi,25);
y = sin(x);
h = plot(nan,nan,'o');
xlim([-pi,pi])
ylim([-1,1])
hold on
tic
for i = 1:numel(x)
h.XData = x(i);
h.YData = y(i);
pause(0.05)
drawnow limitrate
end
fprintf('%.0f seconds with limitrate\n', toc)
for i = 1:numel(x)
h.XData = x(i);
h.YData = y(i);
pause(0.05)
drawnow
end
fprintf('%.0f seconds without limitrate\n', toc)
The boxplots below show the timing of both loops with 5 repetitions. The difference is small and significant (ranksum test p=0.0079), but not perceptible.
Obviously these are overly simple examples but when the animation is fine an the loop has a high workload, the limitrate may come in handy. But often times there's a tradeoff between speed and smoothness. In those cases there are better animation techniques.

Sign in to comment.

Categories

Find more on Graphics Performance in Help Center and File Exchange

Products


Release

R2020a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!