{"id":116,"date":"2014-11-26T09:04:44","date_gmt":"2014-11-26T14:04:44","guid":{"rendered":"https:\/\/blogs.mathworks.com\/graphics\/?p=116"},"modified":"2017-09-03T17:45:45","modified_gmt":"2017-09-03T21:45:45","slug":"pretriangulation","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/26\/pretriangulation\/","title":{"rendered":"Pretriangulation"},"content":{"rendered":"<div class=\"content\"><h3>Pretriangulation<\/h3><p>An interesting question went by <a href=\"https:\/\/www.mathworks.com\/matlabcentral\/answers\/163217-patch-performance-with-caxis\">on MATLAB Answers the other day<\/a>. I've simplified it a bit here, but it looked something like this:<\/p><pre class=\"codeinput\">rng(0)\r\ncla\r\nnfaces = 5000;\r\nnsides = 6;\r\nnframes = 35;\r\n\r\nang = 0:(nsides-1) * pi * 2 \/ nsides;\r\nx = repmat(cos(ang)',[1 nfaces]);\r\ny = repmat(sin(ang)',[1 nfaces]);\r\nz = repmat([1:nfaces],[nsides 1]);\r\nc = rand(1,nfaces);\r\nxoff = repmat(randn(1,nfaces),[nsides, 1]);\r\nyoff = repmat(randn(1,nfaces),[nsides, 1]);\r\nh = patch(x+xoff,y+yoff,z,c);\r\nh.FaceColor = <span class=\"string\">'flat'<\/span>;\r\nh.EdgeColor = <span class=\"string\">'none'<\/span>;\r\naxis <span class=\"string\">equal<\/span>\r\nxlim <span class=\"string\">manual<\/span>\r\nylim <span class=\"string\">manual<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/pretriangulation_01.png\" alt=\"\"> <p>That creates a patch object which draws a bunch of filled hexagons. Now we want to move them around. I'm going to use these random rotation angles to spin them around the origin at various speeds.<\/p><pre class=\"codeinput\">angstep = rand(1,nfaces);\r\nca = cos(angstep);\r\nsa = sin(angstep);\r\nca = repmat(ca,[nsides 1]);\r\nsa = repmat(sa,[nsides 1]);\r\nca = ca(:);\r\nsa = sa(:);\r\n<\/pre><p>We can do that by modifying the Vertices property.<\/p><pre class=\"codeinput\">tic\r\n<span class=\"keyword\">for<\/span> i=1:nframes\r\n    x = h.Vertices(:,1);\r\n    y = h.Vertices(:,2);\r\n    h.Vertices(:,1) =  ca.*x + sa.*y;\r\n    h.Vertices(:,2) = -sa.*x + ca.*y;\r\n    drawnow;\r\n<span class=\"keyword\">end<\/span>\r\ndisp([num2str(nframes\/toc) <span class=\"string\">' frames per second'<\/span>])\r\n<\/pre><pre class=\"codeoutput\">3.3139 frames per second\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/pretriangulation_before_animation.gif\" alt=\"\"> <p>That's really slow, isn't it? What can we do to improve this?<\/p><p>There are a number of approaches we could take to speed this up. One which comes to mind is to use hgtransform like we did <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/10\/28\/makingthingsmove\/\">a couple of weeks ago<\/a>. We could try that, but it actually might not be as useful here because each of those hexagons is so much simpler than the blobs we were drawing in that example. Sending the graphics card a 4x4 transform matrix instead of one of those blobs was a big win, but sending a 4x4 instead of a little hexagon wouldn't be as big a win.<\/p><p>So lets look at a different technique. This technique is one we call \"pretriangulation\". The basic idea here is that a big part of the problem is that the patch object has hexagons, but the graphics card only knows how to draw triangles. Every time we change the Vertices, the patch object is converting the hexagons into triangles. This step is known as polygon triangulation. Good name, huh? Triangulation is actually one of the most <a href=\"http:\/\/en.wikipedia.org\/wiki\/Polygon_triangulation\">interesting corners of computer graphics and compuational geometry<\/a>. We'll be visiting it many times in future blog posts.<\/p><p>First we need to get an idea about how hard triangulation is. It doesn't seem like splitting a hexagon into triangles is a hard problem.<\/p><pre class=\"codeinput\">fig2 = figure;\r\nang = (0:5) * pi * 2 \/ 6;\r\nx = cos(ang');\r\ny = sin(ang');\r\np = patch(<span class=\"string\">'Vertices'<\/span>,[x, y, zeros(size(ang'))],<span class=\"string\">'Faces'<\/span>,[1:6]);\r\np.FaceColor = <span class=\"string\">'yellow'<\/span>;\r\naxis <span class=\"string\">equal<\/span>\r\n\r\nr = 7\/8;\r\n<span class=\"keyword\">for<\/span> i=1:6\r\n    text(r*cos(ang(i)),r*sin(ang(i)),[<span class=\"string\">'Pt_'<\/span> num2str(i)],<span class=\"string\">'HorizontalAlignment'<\/span>,<span class=\"string\">'center'<\/span>);\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/pretriangulation_03.png\" alt=\"\"> <p>We can just slice it like so:<\/p><pre class=\"codeinput\">line(x([1 3 5 1]),y([1 3 5 1]),<span class=\"string\">'Color'<\/span>,<span class=\"string\">'black'<\/span>);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/pretriangulation_04.png\" alt=\"\"> <p>But patch needs to worry about lots of different types of polygons. It can tell that we're dealing with hexagons because of the size of the Faces property, but hexagons come in a lot of different shapes. Let's take a quick look at some.<\/p><pre class=\"codeinput\">cla\r\n<span class=\"keyword\">for<\/span> r=1:3\r\n    <span class=\"keyword\">for<\/span> c=1:5\r\n        p = patch(<span class=\"string\">'Vertices'<\/span>,[3*c+randn(6,1),3*r+randn(6,1),zeros(6,1)],<span class=\"string\">'Faces'<\/span>,1:6);\r\n        p.FaceColor = rand(1,3);\r\n    <span class=\"keyword\">end<\/span>\r\n<span class=\"keyword\">end<\/span>\r\naxis <span class=\"string\">tight<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/pretriangulation_05.png\" alt=\"\"> <p>Hmm, maybe it's not as simple as it seems. It turns out that writing a really robust triangulator is pretty challenging, and it's very hard to write a robust one which is also very fast. This means that if we care about performance, we probably want to avoid triangulation whenever we can.<\/p><p>It turns out that in our example above we actually only need to triangulate once. That's because because that rotation we're doing is what's called an <a href=\"http:\/\/en.wikipedia.org\/wiki\/Affine_transformation\">affine transformation<\/a>. That means that the shape of the objects being transformed doesn't change. And that means that the triangulation can be reused.<\/p><p>The problem is that the patch object can't really figure that out. It just knows that the Vertices property has changed. Figuring out whether the old triangulation would still work turns out to be almost as expensive as just redoing the triangulation.<\/p><p>But in this case the triangulation is actually simple enough that we can do it ourselves. We can just follow the diagram above to turn each hexagon into four triangles. That will conver the 5,000 by 6 Faces array which represents 5,000 hexagons  into a 20,000 by 3 array which represents 20,000 triangles. When we give patch a Faces array with 3 columns, it will know that it can send them straight over to the graphics card without doing any triangulation.<\/p><pre class=\"codeinput\">close(fig2);\r\n\r\nf = h.Faces;\r\nf2 = zeros(4*nfaces,3);\r\n\r\n<span class=\"comment\">% First the big triangle in the center<\/span>\r\nf2(1:4:end,1:3) = f(:,[1,3,5]);\r\n\r\n<span class=\"comment\">% Then the three little triangles around the perimeter<\/span>\r\nf2(2:4:end,1:3) = f(:,[1,2,3]);\r\nf2(3:4:end,1:3) = f(:,[3,4,5]);\r\nf2(4:4:end,1:3) = f(:,[5,6,1]);\r\n\r\nh.Faces = f2;\r\n\r\n<span class=\"comment\">% Expand the colors by 4 too<\/span>\r\nc = h.FaceVertexCData;\r\nc2 = repmat(c',[4 1]);\r\nc2 = c2(:);\r\nh.FaceVertexCData = c2;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/pretriangulation_06.png\" alt=\"\"> <p>So let's try moving these pretriangulated hexagons around the same way we did before.<\/p><pre class=\"codeinput\">tic\r\n<span class=\"keyword\">for<\/span> i=1:nframes\r\n    x = h.Vertices(:,1);\r\n    y = h.Vertices(:,2);\r\n    h.Vertices(:,1) =  ca.*x + sa.*y;\r\n    h.Vertices(:,2) = -sa.*x + ca.*y;\r\n    drawnow;\r\n<span class=\"keyword\">end<\/span>\r\ndisp([num2str(nframes\/toc) <span class=\"string\">' frames per second'<\/span>])\r\n<\/pre><pre class=\"codeoutput\">34.1455 frames per second\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/pretriangulation_after_animation.gif\" alt=\"\"> <p>That's about 10 times as fast, which is pretty good. And it gets us over that 24 frames per second barrier that we want to cross for smooth animations.<\/p><p>But this approach does have some limitations. If we had been doing something to the hexagons which changed their shapes, then we couldn't reuse the original triangulation. And we were lucky that the triangulation was so simple in this case. For more complex polygons it can get pretty tricky. We can use the triangulator which patch uses, but it's kind of tricky to use. It'd be really neat if we could just ask patch to do the pretriangulation for us, wouldn't it? Also, if we hadn't turned the edges off then we would be seeing the interior edges between the triangles.<\/p><p>But even with those limitations, you may find cases where this is a useful technique to have in your MATLAB graphics bag of tricks.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_166490bffec64f52928474cc2f5c0838() {\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='166490bffec64f52928474cc2f5c0838 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 166490bffec64f52928474cc2f5c0838';\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_166490bffec64f52928474cc2f5c0838()\"><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\n166490bffec64f52928474cc2f5c0838 ##### SOURCE BEGIN #####\r\n%% Pretriangulation\r\n% An interesting question went by <https:\/\/www.mathworks.com\/matlabcentral\/answers\/163217-patch-performance-with-caxis on MATLAB Answers the other day>. I've simplified it a bit here, but it looked something like this:\r\nrng(0)\r\ncla\r\nnfaces = 5000;\r\nnsides = 6;\r\nnframes = 35;\r\n\r\nang = 0:(nsides-1) * pi * 2 \/ nsides;\r\nx = repmat(cos(ang)',[1 nfaces]);\r\ny = repmat(sin(ang)',[1 nfaces]);\r\nz = repmat([1:nfaces],[nsides 1]);\r\nc = rand(1,nfaces);\r\nxoff = repmat(randn(1,nfaces),[nsides, 1]); \r\nyoff = repmat(randn(1,nfaces),[nsides, 1]); \r\nh = patch(x+xoff,y+yoff,z,c);\r\nh.FaceColor = 'flat'; \r\nh.EdgeColor = 'none';\r\naxis equal\r\nxlim manual\r\nylim manual\r\n\r\n%%\r\n% That creates a patch object which draws a bunch of filled hexagons. Now\r\n% we want to move them around. I'm going to use these random rotation \r\n% angles to spin them around the origin at various speeds.\r\nangstep = rand(1,nfaces);\r\nca = cos(angstep);\r\nsa = sin(angstep);\r\nca = repmat(ca,[nsides 1]);\r\nsa = repmat(sa,[nsides 1]);\r\nca = ca(:);\r\nsa = sa(:);\r\n%%\r\n% We can do that by modifying the Vertices property.\r\ntic\r\nfor i=1:nframes\r\n    x = h.Vertices(:,1);\r\n    y = h.Vertices(:,2);\r\n    h.Vertices(:,1) =  ca.*x + sa.*y;\r\n    h.Vertices(:,2) = -sa.*x + ca.*y;\r\n    drawnow;\r\nend\r\ndisp([num2str(nframes\/toc) ' frames per second'])\r\n\r\n%%\r\n% That's really slow, isn't it? What can we do to improve this?\r\n%\r\n% There are a number of approaches we could take to speed this up. One which \r\n% comes to mind is to use hgtransform like we did \r\n% <https:\/\/blogs.mathworks.com\/graphics\/2014\/10\/28\/makingthingsmove\/ a couple of weeks ago>.\r\n% We could try that, but it actually might not be as useful here because \r\n% each of those hexagons is so much simpler than the blobs we were drawing \r\n% in that example. Sending the graphics card a 4x4 transform matrix instead\r\n% of one of those blobs was a big win, but sending a 4x4 instead of a\r\n% little hexagon wouldn't be as big a win.\r\n%\r\n% So lets look at a different technique. This technique is one we call \r\n% \"pretriangulation\". The basic idea here is that a big part of the problem is that the patch\r\n% object has hexagons, but the graphics card only knows how to draw\r\n% triangles. Every time we change the Vertices, the patch object is\r\n% converting the hexagons into triangles. This step is known as  polygon \r\n% triangulation. Good name, huh? Triangulation is actually one of the most \r\n% <http:\/\/en.wikipedia.org\/wiki\/Polygon_triangulation interesting corners of computer graphics and compuational geometry>. We'll \r\n% be visiting it many times in future blog posts.\r\n%\r\n% First we need to get an idea about how hard triangulation is. It doesn't\r\n% seem like splitting a hexagon into triangles is a hard problem.\r\nfig2 = figure;\r\nang = (0:5) * pi * 2 \/ 6;\r\nx = cos(ang');\r\ny = sin(ang');\r\np = patch('Vertices',[x, y, zeros(size(ang'))],'Faces',[1:6]);\r\np.FaceColor = 'yellow';\r\naxis equal\r\n\r\nr = 7\/8;\r\nfor i=1:6\r\n    text(r*cos(ang(i)),r*sin(ang(i)),['Pt_' num2str(i)],'HorizontalAlignment','center');\r\nend\r\n\r\n%%\r\n% We can just slice it like so:\r\nline(x([1 3 5 1]),y([1 3 5 1]),'Color','black');\r\n\r\n%% \r\n% But patch needs to worry about lots of different types of polygons.\r\n% It can tell that we're dealing with hexagons because of the size of the\r\n% Faces property, but hexagons come in a lot of different shapes. Let's\r\n% take a quick look at some.\r\ncla\r\nfor r=1:3\r\n    for c=1:5\r\n        p = patch('Vertices',[3*c+randn(6,1),3*r+randn(6,1),zeros(6,1)],'Faces',1:6);\r\n        p.FaceColor = rand(1,3);        \r\n    end\r\nend\r\naxis tight\r\n\r\n%%\r\n% Hmm, maybe it's not as simple as it seems. It turns out that writing a\r\n% really robust triangulator is pretty challenging, and it's very hard to\r\n% write a robust one which is also very fast. This means that if we care about\r\n% performance, we probably want to avoid triangulation whenever we can. \r\n%\r\n% It turns out that in our example above we actually only need to triangulate \r\n% once. That's because because that rotation we're doing is what's called an\r\n% <http:\/\/en.wikipedia.org\/wiki\/Affine_transformation affine\r\n% transformation>. That means that the shape of the objects being\r\n% transformed doesn't change. And that means that the triangulation can be\r\n% reused.\r\n%\r\n% The problem is that the patch object can't really figure that out. It\r\n% just knows that the Vertices property has changed. Figuring out whether\r\n% the old triangulation would still work turns out to be almost as\r\n% expensive as just redoing the triangulation.\r\n%\r\n% But in this case the triangulation is actually simple enough that we can\r\n% do it ourselves. We can just follow the diagram above to turn each\r\n% hexagon into four triangles. That will conver the 5,000 by 6 Faces array \r\n% which represents 5,000 hexagons  into a 20,000 by 3 array which\r\n% represents 20,000 triangles. When we give patch a Faces array with 3 columns, \r\n% it will know that it can send them straight over to the graphics card\r\n% without doing any triangulation. \r\n%\r\nclose(fig2);\r\n\r\nf = h.Faces;\r\nf2 = zeros(4*nfaces,3);\r\n\r\n% First the big triangle in the center\r\nf2(1:4:end,1:3) = f(:,[1,3,5]);\r\n\r\n% Then the three little triangles around the perimeter\r\nf2(2:4:end,1:3) = f(:,[1,2,3]);\r\nf2(3:4:end,1:3) = f(:,[3,4,5]);\r\nf2(4:4:end,1:3) = f(:,[5,6,1]);\r\n\r\nh.Faces = f2;\r\n\r\n% Expand the colors by 4 too\r\nc = h.FaceVertexCData;\r\nc2 = repmat(c',[4 1]);\r\nc2 = c2(:);\r\nh.FaceVertexCData = c2;\r\n\r\n%%\r\n% So let's try moving these pretriangulated hexagons around the same way we\r\n% did before.\r\ntic\r\nfor i=1:nframes\r\n    x = h.Vertices(:,1);\r\n    y = h.Vertices(:,2);\r\n    h.Vertices(:,1) =  ca.*x + sa.*y;\r\n    h.Vertices(:,2) = -sa.*x + ca.*y;\r\n    drawnow;\r\nend\r\ndisp([num2str(nframes\/toc) ' frames per second'])\r\n\r\n%%\r\n% That's about 10 times as fast, which is pretty good. And it gets us \r\n% over that 24 frames per second barrier that we want to cross for smooth \r\n% animations.\r\n%\r\n% But this approach does have some limitations. If we had been doing\r\n% something to the hexagons which changed their shapes, then we couldn't\r\n% reuse the original triangulation. And we were lucky that the\r\n% triangulation was so simple in this case. For more complex polygons it\r\n% can get pretty tricky. We can use\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/delaunaytriangulation-class.html the triangulator which patch uses>, \r\n% but it's kind of tricky to use. It'd be really neat if we could just ask\r\n% patch to do the pretriangulation for us, wouldn't it? Also, if we hadn't\r\n% turned the edges off then we would be seeing the interior edges between\r\n% the triangles. \r\n%\r\n% But even with those limitations, you may find cases where this is a\r\n% useful technique to have in your MATLAB graphics bag of tricks.\r\n\r\n\r\n##### SOURCE END ##### 166490bffec64f52928474cc2f5c0838\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/graphics\/files\/feature_image\/pretriangulation_after_animation.gif\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>PretriangulationAn interesting question went by on MATLAB Answers the other day. I've simplified it a bit here, but it looked something like this:rng(0)\r\ncla\r\nnfaces = 5000;\r\nnsides = 6;\r\nnframes =... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/11\/26\/pretriangulation\/\">read more >><\/a><\/p>","protected":false},"author":89,"featured_media":117,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[5,8],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/116"}],"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=116"}],"version-history":[{"count":4,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/116\/revisions"}],"predecessor-version":[{"id":708,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/116\/revisions\/708"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media\/117"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media?parent=116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/categories?post=116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/tags?post=116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}