{"id":64,"date":"2014-10-28T09:35:08","date_gmt":"2014-10-28T13:35:08","guid":{"rendered":"https:\/\/blogs.mathworks.com\/graphics\/?p=64"},"modified":"2015-01-20T16:08:51","modified_gmt":"2015-01-20T21:08:51","slug":"makingthingsmove","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/graphics\/2014\/10\/28\/makingthingsmove\/","title":{"rendered":"Making Things Move"},"content":{"rendered":"<div class=\"content\"><!--introduction--><p>After <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/10\/21\/double_pendulum\/\">my post about using MATLAB Graphics from Simulink<\/a>, Aditya had a great question about using this technique for 3D animations. This is a really interesting area, and I really wanted to use a 3D example for that post. Unfortunately I couldn't come up with one that was simple enough to fit into that blog post. But if you do decide to explore this area on your own, or even if you're doing 3D animations without Simulink, there are some tricks you should probably know about.<\/p><p>Today we're going to look at one technique for getting good performance when you're doing 3D animations with MATLAB Graphics, although this technique can also be useful in 2D in some cases.<\/p><!--\/introduction--><p>I don't have a good STL model handy for us to animate, so I'm going to start with some spheres. Well, not spheres really, more like lumpy potatoes.<\/p><p>We'll start with a set of random points on the surface of a sphere. The simplest way to do that is to use a random number generator to select random values for latitude and longitude. But that doesn't look very good because you get a lot of points at the poles and not many points at the equator.<\/p><p>A better approach is to stretch the distribution using the following transform:<\/p><p>$$longitude = 2 \\pi a - pi$$<\/p><p>$$latitude = \\cos^{-1}(2 b - 1)$$<\/p><pre class=\"codeinput\">rng(0)\r\nnpts = 1000;\r\nlon = 2*pi*rand(1,npts) - pi;\r\nlat = acos(2*rand(1,npts) - 1);\r\nx = cos(lon).*cos(lat);\r\ny = cos(lon).*sin(lat);\r\nz = sin(lon);\r\n<\/pre><p>Once we have these points, we can draw them using <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/scatter3.html\">scatter3<\/a>.<\/p><pre class=\"codeinput\">hs = scatter3(x,y,z,<span class=\"string\">'filled'<\/span>);\r\naxis <span class=\"string\">vis3d<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/sphere_points_01.png\" alt=\"\"> <p>But we want surfaces for this example, not points. We can use convhull for this. The <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/convhull.html\">convhull command<\/a> gives us the \"convex hull\" of the points. In other words, the smallest, convex polyhedron which encloses all of the points. It returns the polyhedron as a list of triangles which we can pass straight into the patch command like this:<\/p><pre class=\"codeinput\">ax = gca;\r\ntris = convhull(x,y,z);\r\nh = patch(<span class=\"string\">'Faces'<\/span>,tris,<span class=\"string\">'Vertices'<\/span>,[x', y', z']);\r\nh.FaceColor = ax.ColorOrder(2,:);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/sphere_points_02.png\" alt=\"\"> <p>Let's stop to make that look a little nicer. I wrote the following little helper function because we'll want to make several pictures look the same.<\/p><pre class=\"language-matlab\"><span class=\"keyword\">function<\/span> init3dview()\r\n  axis <span class=\"string\">vis3d<\/span>\r\n  xlim([-3 3])\r\n  ylim([-3 3])\r\n  zlim([-3 3])\r\n  view(3)\r\n  camlight\r\n<\/pre><pre class=\"codeinput\">init3dview()\r\nh.EdgeColor = <span class=\"string\">'none'<\/span>;\r\ndelete(hs);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/sphere_points_03.png\" alt=\"\"> <p>What is the Greek name for a 1,000 sided polyhedron? A millihedron? Or a kilohedron?<\/p><p>Now that we know how to draw these blobs, let's look at how to animate them. The obvious way to do this is to add an offset to the Vertices property in the inner loop. For two blobs moving randomly, that would look something like this:<\/p><pre class=\"codeinput\">cla\r\nrng(0)\r\nax = gca;\r\n<span class=\"comment\">% Create a couple of spheres<\/span>\r\nnspheres = 2;\r\ng = gobjects(1,nspheres);\r\ncolors = ax.ColorOrder;\r\n<span class=\"keyword\">for<\/span> i=1:nspheres\r\n    offset = randn(1,3);\r\n    g(i) = patch(<span class=\"string\">'Faces'<\/span>,tris,<span class=\"string\">'Vertices'<\/span>,[x',y',z']+repmat(offset,[npts 1]));\r\n    g(i).FaceColor = colors(i,:);\r\n    g(i).EdgeColor = <span class=\"string\">'none'<\/span>;\r\n<span class=\"keyword\">end<\/span>\r\ninit3dview()\r\n\r\n<span class=\"comment\">% Now move them around by setting the Vertices property<\/span>\r\ntic\r\n<span class=\"keyword\">for<\/span> i=1:100\r\n    <span class=\"keyword\">for<\/span> j=1:nspheres\r\n        offset = randn(1,3)\/100;\r\n        g(j).Vertices = g(j).Vertices + repmat(offset,[npts 1]);\r\n    <span class=\"keyword\">end<\/span>\r\n    drawnow;\r\n<span class=\"keyword\">end<\/span>\r\ntoc\r\n<\/pre><pre class=\"codeoutput\">Elapsed time is 2.099485 seconds.\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/sphere_points_04.png\" alt=\"\"> <p>As you can see, the performance isn't bad. I get about 45 frames per second on my machine. But when we're doing animations, we always want to look for more performance. This allows us to animate more complex models, and it also gives us more compute time for our simulation, which might be something a bit more complicated than a random number generator.<\/p><p>There's actually a faster way to do this. What's happening when we change the patch's Vertices property is that we're sending all of the new vertices over to the graphics card. When we do this, the memory bandwidth between the memory which the CPU uses and the memory on the graphics card can become a bottleneck.<\/p><p>It's much better to park the vertices on the graphics card and just send a small description of the motion. The <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/hgtransform.html\">hgtransform function<\/a> is perfect for this job. It just sends a 4x4 matrix which describes the transformation instead of resending all of the data.<\/p><pre class=\"codeinput\">cla\r\nrng(0)\r\nax = gca;\r\n<span class=\"comment\">% Create a couple of spheres again, put hang them off hgtransform objects.<\/span>\r\nnspheres = 2;\r\ng = gobjects(1,nspheres);\r\ncolors = ax.ColorOrder;\r\n<span class=\"keyword\">for<\/span> i=1:nspheres\r\n    offset = randn(1,3);\r\n    <span class=\"comment\">% Create an hgtransform<\/span>\r\n    g(i) = hgtransform;\r\n    g(i).Matrix = makehgtform(<span class=\"string\">'translate'<\/span>,offset);\r\n    <span class=\"comment\">% Use it as the Parent of the patch<\/span>\r\n    p = patch(<span class=\"string\">'Faces'<\/span>,tris,<span class=\"string\">'Vertices'<\/span>,[x',y',z'],<span class=\"string\">'Parent'<\/span>,g(i));\r\n    p.FaceColor = colors(i,:);\r\n    p.EdgeColor = <span class=\"string\">'none'<\/span>;\r\n<span class=\"keyword\">end<\/span>\r\ninit3dview()\r\n\r\n<span class=\"comment\">% Now move them around by setting the Matrix property on the hgtransforms.<\/span>\r\ntic\r\n<span class=\"keyword\">for<\/span> i=1:100\r\n    <span class=\"keyword\">for<\/span> j=1:nspheres\r\n        offset = randn(1,3)\/100;\r\n        g(j).Matrix = g(j).Matrix * makehgtform(<span class=\"string\">'translate'<\/span>,offset);\r\n    <span class=\"keyword\">end<\/span>\r\n    drawnow;\r\n<span class=\"keyword\">end<\/span>\r\ntoc\r\n<\/pre><pre class=\"codeoutput\">Elapsed time is 0.423583 seconds.\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/sphere_points_05.png\" alt=\"\"> <p>On my machine I get more like 200 frames per second with this approach. Of course the exact performance is a function of a lot of variables such as the number of objects you're animating, the number of triangles in each object, and the type of graphics card in your computer. For example, here's what I get when I vary the number of blobs.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/hg2blobperformance.png\" alt=\"\"> <\/p><p>I always like to add that grey line at 24 frames per second to my performance charts. That's because that is roughly the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Flicker_fusion_threshold\">\"flicker fusion rate\"<\/a> of the human visual system. If our animation is slower than that, it will look \"choppy\". So this chart is telling us that things aren't going to look good if we go past 4 blobs with the Vertices property approach, but with the hgtransform approach I can basically use as many as I would like.<\/p><p>It also depends which version of MATLAB you're using. For example, this is what I get when I run the same example with R2014a.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/hg1blobperformance.png\" alt=\"\"> <\/p><p>But in general this hgtransform approach will be at least as fast as the changing the data approach, and often quite a bit better.<\/p><p>So that's a useful thing to know if you want to animate 3D objects, like my caffeinated blobs here.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/blob_animation.gif\" alt=\"\"> <\/p><p>In future posts we'll discuss other tricks for getting the best performance out of MATLAB Graphics. Do you have some tricks you use?<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_ff98365f169c4f6fb5747b6ca9885fed() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='ff98365f169c4f6fb5747b6ca9885fed ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' ff98365f169c4f6fb5747b6ca9885fed';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2014 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_ff98365f169c4f6fb5747b6ca9885fed()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2014b<br><\/p><\/div><!--\r\nff98365f169c4f6fb5747b6ca9885fed ##### SOURCE BEGIN #####\r\n%% Making Things Move\r\n% After <https:\/\/blogs.mathworks.com\/graphics\/2014\/10\/21\/double_pendulum\/ my post about using MATLAB Graphics from Simulink>, Aditya had a\r\n% great question about using this technique for 3D animations. This is a\r\n% really interesting area, and I really wanted to use a 3D example for that\r\n% post. Unfortunately I couldn't come up with one that was simple enough to\r\n% fit into that blog post. But if you do decide to explore this area on your\r\n% own, or even if you're doing 3D animations without Simulink, there are\r\n% some tricks you should probably know about.\r\n%\r\n% Today we're going to look at one technique for getting good performance \r\n% when you're doing 3D animations with MATLAB Graphics, although this \r\n% technique can also be useful in 2D in some cases.\r\n\r\n%%\r\n% I don't have a good STL model handy for us to animate, so I'm going to\r\n% start with some spheres. Well, not spheres really, more like lumpy\r\n% potatoes. \r\n%\r\n% We'll start with a set of random points on the surface of a sphere. The\r\n% simplest way to do that is to use a random number generator to select\r\n% random values for latitude and longitude. But that doesn't look very good\r\n% because you get a lot of points at the poles and not many points at the\r\n% equator.\r\n%\r\n% A better approach is to stretch the distribution using the following\r\n% transform:\r\n%\r\n% $$longitude = 2 \\pi a - pi$$\r\n%\r\n% $$latitude = \\cos^{-1}(2 b - 1)$$\r\n%\r\nrng(0)\r\nnpts = 1000;\r\nlon = 2*pi*rand(1,npts) - pi;\r\nlat = acos(2*rand(1,npts) - 1);\r\nx = cos(lon).*cos(lat);\r\ny = cos(lon).*sin(lat);\r\nz = sin(lon);\r\n\r\n%%\r\n% Once we have these points, we can draw them using <https:\/\/www.mathworks.com\/help\/matlab\/ref\/scatter3.html scatter3>.\r\n%\r\nhs = scatter3(x,y,z,'filled');\r\naxis vis3d\r\n\r\n%%\r\n% But we want surfaces for this example, not points. We can use convhull for this. The \r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/convhull.html convhull command> gives us the \"convex hull\" of the points. In other\r\n% words, the smallest, convex polyhedron which encloses all of the points.\r\n% It returns the polyhedron as a list of triangles which we can pass\r\n% straight into the patch command like this:\r\n%\r\nax = gca;\r\ntris = convhull(x,y,z);\r\nh = patch('Faces',tris,'Vertices',[x', y', z']);\r\nh.FaceColor = ax.ColorOrder(2,:);\r\n\r\n%%\r\n% Let's stop to make that look a little nicer. I wrote the following little\r\n% helper function because we'll want to make several pictures look the\r\n% same.\r\n%\r\n%   function init3dview()\r\n%     axis vis3d\r\n%     xlim([-3 3])\r\n%     ylim([-3 3])\r\n%     zlim([-3 3])\r\n%     view(3)\r\n%     camlight\r\n%\r\ninit3dview()\r\nh.EdgeColor = 'none';\r\ndelete(hs);\r\n\r\n%%\r\n% What is the Greek name for a 1,000 sided polyhedron? A millihedron? Or a\r\n% kilohedron?\r\n%\r\n\r\n%%\r\n% Now that we know how to draw these blobs, let's look at how to animate\r\n% them. The obvious way to do this is to add an offset to the Vertices\r\n% property in the inner loop. For two blobs moving randomly, that would\r\n% look something like this:\r\n%\r\ncla\r\nrng(0)\r\nax = gca;\r\n% Create a couple of spheres\r\nnspheres = 2;\r\ng = gobjects(1,nspheres);\r\ncolors = ax.ColorOrder;\r\nfor i=1:nspheres\r\n    offset = randn(1,3);\r\n    g(i) = patch('Faces',tris,'Vertices',[x',y',z']+repmat(offset,[npts 1]));\r\n    g(i).FaceColor = colors(i,:);\r\n    g(i).EdgeColor = 'none';\r\nend\r\ninit3dview()\r\n\r\n% Now move them around by setting the Vertices property\r\ntic \r\nfor i=1:100\r\n    for j=1:nspheres\r\n        offset = randn(1,3)\/100;\r\n        g(j).Vertices = g(j).Vertices + repmat(offset,[npts 1]);\r\n    end\r\n    drawnow;\r\nend\r\ntoc\r\n\r\n%%\r\n% As you can see, the performance isn't bad. I get about 40 frames per\r\n% second on my machine. But when we're doing animations, we always want to\r\n% look for more performance. This allows us to animate more complex models,\r\n% and it also gives us more compute time for our simulation, which might be\r\n% something a bit more complicated than a random number generator.\r\n%\r\n% There's actually a faster way to do this. What's happening when we change the \r\n% patch's Vertices property is that we're sending all of the new vertices\r\n% over to the graphics card. When we do this, the memory bandwidth between\r\n% the memory which the CPU uses and the memory on the graphics card can\r\n% become a bottleneck. \r\n%\r\n% It's much better to park the vertices on the graphics card and just send\r\n% a small description of the motion. The <https:\/\/www.mathworks.com\/help\/matlab\/ref\/hgtransform.html hgtransform function> is perfect\r\n% for this job. It just sends a 4x4 matrix which describes the\r\n% transformation instead of resending all of the data.\r\n%\r\ncla\r\nrng(0)\r\nax = gca;\r\n% Create a couple of spheres again, put hang them off hgtransform objects.\r\nnspheres = 2;\r\ng = gobjects(1,nspheres);\r\ncolors = ax.ColorOrder;\r\nfor i=1:nspheres\r\n    offset = randn(1,3);\r\n    % Create an hgtransform\r\n    g(i) = hgtransform;\r\n    g(i).Matrix = makehgtform('translate',offset);\r\n    % Use it as the Parent of the patch\r\n    p = patch('Faces',tris,'Vertices',[x',y',z'],'Parent',g(i));\r\n    p.FaceColor = colors(i,:);\r\n    p.EdgeColor = 'none';\r\nend\r\ninit3dview()\r\n\r\n% Now move them around by setting the Matrix property on the hgtransforms.\r\ntic\r\nfor i=1:100\r\n    for j=1:nspheres\r\n        offset = randn(1,3)\/100;\r\n        g(j).Matrix = g(j).Matrix * makehgtform('translate',offset);\r\n    end\r\n    drawnow;\r\nend\r\ntoc\r\n\r\n%%\r\n% On my machine I get more like 150 frames per second with this approach. Of\r\n% course the exact performance is a function of a lot of variables such as\r\n% the number of objects you're animating, the number of triangles in each\r\n% object, and the type of graphics card in your computer. For example,\r\n% here's what I get when I vary the number of blobs.\r\n%\r\n% <<..\/hg2performance.png>>\r\n%\r\n% I always like to add that grey line at 24 frames per second to my\r\n% performance charts. That's because that is roughly the <http:\/\/en.wikipedia.org\/wiki\/Flicker_fusion_threshold \"flicker fusion rate\"> of the human\r\n% visual system. If our animation is slower than that, it will look\r\n% \"choppy\". So this chart is telling us that things aren't going to look\r\n% good if we go past 4 blobs with the Vertices property approach, but\r\n% with the hgtransform approach I can basically use as many as I would like.\r\n%\r\n% It also depends which version of MATLAB you're using. For example, this\r\n% is what I get when I run the same example with R2014a.\r\n%\r\n% <<..\/hg1performance.png>>\r\n%\r\n% But in general this hgtransform approach will be at least as fast as the\r\n% changing the data approach, and often quite a bit better.\r\n%\r\n% So that's a useful thing to know if you want to animate 3D objects, like\r\n% my caffeinated blobs here.\r\n%\r\n% <<..\/blob_animation.gif>>\r\n%\r\n% In future posts we'll discuss other tricks for getting the best\r\n% performance out of MATLAB Graphics. Do you have some tricks you use? \r\n%\r\n\r\n\r\n##### SOURCE END ##### ff98365f169c4f6fb5747b6ca9885fed\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/graphics\/files\/feature_image\/blob_animation.gif\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><!--introduction--><p>After <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/10\/21\/double_pendulum\/\">my post about using MATLAB Graphics from Simulink<\/a>, Aditya had a great question about using this technique for 3D animations. This is a really interesting area, and I really wanted to use a 3D example for that post. Unfortunately I couldn't come up with one that was simple enough to fit into that blog post. But if you do decide to explore this area on your own, or even if you're doing 3D animations without Simulink, there are some tricks you should probably know about.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/10\/28\/makingthingsmove\/\">read more >><\/a><\/p>","protected":false},"author":89,"featured_media":70,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[4,8],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/64"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/users\/89"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/comments?post=64"}],"version-history":[{"count":16,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/64\/revisions"}],"predecessor-version":[{"id":81,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/64\/revisions\/81"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media\/70"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media?parent=64"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/categories?post=64"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/tags?post=64"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}