{"id":130,"date":"2014-12-16T09:06:56","date_gmt":"2014-12-16T14:06:56","guid":{"rendered":"https:\/\/blogs.mathworks.com\/graphics\/?p=130"},"modified":"2014-12-16T09:06:56","modified_gmt":"2014-12-16T14:06:56","slug":"down-the-tubes","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/graphics\/2014\/12\/16\/down-the-tubes\/","title":{"rendered":"Down the Tubes (Parametric Curves Part 2)"},"content":{"rendered":"<div class=\"content\"><!--introduction--><p>The ribbons we <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/12\/04\/tie-a-ribbon-round-it-parametric-curves-part-1\/\">created last time<\/a> are nice, but sometimes we want something a bit more solid. Something like a tube.<\/p><p>To do that, a single vector normal to the curve is not going to be sufficient. We need two vectors which are normal to the curve and also normal to each other. In theory the third partial (aka the binormal) fits the bill, but it tends to behave rather poorly in practice.<\/p><p>Another approach is to use the cross product. Remember that if we compute the cross product of two vectors, we get a third vector which is normal to each of those. This means that if we cross the tangent and the normal vectors that we computed last time, we'll get another vector with exactly the behavior we need. We can do that with this function.<\/p><pre class=\"language-matlab\"><span class=\"keyword\">function<\/span> [xo,yo,zo] = cross_vectors(x1,y1,z1, x2,y2,z2)\r\n  n = numel(x1);\r\n  xo = zeros(1,n);\r\n  yo = zeros(1,n);\r\n  zo = zeros(1,n);\r\n  <span class=\"keyword\">for<\/span> i=1:n\r\n    xo(i) = y1(i)*z2(i) - z1(i)*y2(i);\r\n    yo(i) = z1(i)*x2(i) - x1(i)*z2(i);\r\n    zo(i) = x1(i)*y2(i) - y1(i)*x2(i);\r\n  <span class=\"keyword\">end<\/span>\r\n<\/pre><!--\/introduction--><p>Which we can use like this:<\/p><pre class=\"codeinput\">t = 0:pi\/128:2*pi;\r\nx = 3*cos(t)+cos(10*t).*cos(t);\r\ny = 3*sin(t)+cos(10*t).*sin(t);\r\nz = sin(10*t);\r\n<\/pre><p>Get the tangent vector.<\/p><pre class=\"codeinput\">dxdt = -3*sin(t) - cos(10*t).*sin(t) - 10*sin(10*t).*cos(t);\r\ndydt = 3*cos(t) + cos(10*t).*cos(t) - 10*sin(10*t).*sin(t);\r\ndzdt = 10*cos(10*t);\r\n<\/pre><p>Get the normal vector.<\/p><pre class=\"codeinput\">d2xdt2 = -3*cos(t) + 20*sin(10*t).*sin(t) - 101*cos(10*t).*cos(t);\r\nd2ydt2 = -3*sin(t) - 101*cos(10*t).*sin(t) - 20*sin(10*t).*cos(t);\r\nd2zdt2 = -100*sin(10*t);\r\n[n1x,n1y,n1z] = normalize_vector(d2xdt2,d2ydt2,d2zdt2);\r\n<\/pre><p>And cross them to get a second normal vector.<\/p><pre class=\"codeinput\">[cx,cy,cz] = cross_vectors(dxdt,dydt,dzdt, d2xdt2,d2ydt2,d2zdt2);\r\n[n2x,n2y,n2z] = normalize_vector(cx,cy,cz);\r\n<\/pre><pre class=\"codeinput\">plot_vectors(x,y,z, n1x,n1y,n1z);\r\nhold <span class=\"string\">on<\/span>\r\nplot_vectors(x,y,z, n2x,n2y,n2z);\r\nset(gca,<span class=\"string\">'DataAspectRatio'<\/span>,[1 1 1])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/down_the_tubes_01.png\" alt=\"\"> <p>Now we have two sets of normals which we can use to create four ribbons which make a closed tube around the curve. Notice the pattern here. We create 4 ribbons. Each one uses one of our 2 vectors in either the + or - direction, and then creates 2 rows from the + and - versions of the \"other\" vector.<\/p><pre class=\"codeinput\">sides = gobjects(1,4);\r\n\r\nsx = [x+(-n1x-n2x)\/2; x+( n1x-n2x)\/2];\r\nsy = [y+(-n1y-n2y)\/2; y+( n1y-n2y)\/2];\r\nsz = [z+(-n1z-n2z)\/2; z+( n1z-n2z)\/2];\r\nsides(1) = surf(sx,sy,sz,[t;t]);\r\nhold <span class=\"string\">on<\/span>\r\n\r\nsx = [x+( n1x-n2x)\/2; x+( n1x+n2x)\/2];\r\nsy = [y+( n1y-n2y)\/2; y+( n1y+n2y)\/2];\r\nsz = [z+( n1z-n2z)\/2; z+( n1z+n2z)\/2];\r\nsides(2) = surf(sx,sy,sz,[t;t]);\r\n\r\nsx = [x+( n1x+n2x)\/2; x+(-n1x+n2x)\/2];\r\nsy = [y+( n1y+n2y)\/2; y+(-n1y+n2y)\/2];\r\nsz = [z+( n1z+n2z)\/2; z+(-n1z+n2z)\/2];\r\nsides(3) = surf(sx,sy,sz,[t;t]);\r\n\r\nsx = [x+(-n1x+n2x)\/2; x+(-n1x-n2x)\/2];\r\nsy = [y+(-n1y+n2y)\/2; y+(-n1y-n2y)\/2];\r\nsz = [z+(-n1z+n2z)\/2; z+(-n1z-n2z)\/2];\r\nsides(4) = surf(sx,sy,sz,[t;t]);\r\n\r\nhold <span class=\"string\">off<\/span>\r\nset(sides,<span class=\"string\">'EdgeColor'<\/span>,<span class=\"string\">'none'<\/span>);\r\nset(sides,<span class=\"string\">'FaceColor'<\/span>,<span class=\"string\">'interp'<\/span>);\r\nset(sides,<span class=\"string\">'FaceLighting'<\/span>,<span class=\"string\">'gouraud'<\/span>);\r\nset(gca,<span class=\"string\">'DataAspectRatio'<\/span>,[1 1 1])\r\ncolormap(hsv(256))\r\ncamlight <span class=\"string\">headlight<\/span>\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2014\/down_the_tubes_02.png\" alt=\"\"> <p>That looks pretty good, but let's take a closer look. What if we move the camera inside the scene and use a perspective projection? While we're at it, let's save it to an animated GIF.<\/p><pre class=\"codeinput\">a = gca;\r\na.Visible = <span class=\"string\">'off'<\/span>;\r\na.Projection = <span class=\"string\">'perspective'<\/span>;\r\na.CameraViewAngle = 45;\r\nfirst = true;\r\n<span class=\"keyword\">for<\/span> theta=t\r\n    a.CameraPosition = 3*[cos(theta), sin(theta), 0];\r\n    a.CameraTarget = a.CameraPosition + [-sin(theta), cos(theta), 0];\r\n    im = getframe(gcf);\r\n    [A,map] = rgb2ind(im.cdata,256);\r\n    <span class=\"keyword\">if<\/span> first\r\n        first = false;\r\n        imwrite(A,map,<span class=\"string\">'torus_animation.gif'<\/span>,<span class=\"string\">'LoopCount'<\/span>,Inf,<span class=\"string\">'DelayTime'<\/span>,1\/24);\r\n    <span class=\"keyword\">else<\/span>\r\n        imwrite(A,map,<span class=\"string\">'torus_animation.gif'<\/span>,<span class=\"string\">'WriteMode'<\/span>,<span class=\"string\">'append'<\/span>,<span class=\"string\">'DelayTime'<\/span>,1\/24);\r\n    <span class=\"keyword\">end<\/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\/torus_animation.gif\" alt=\"\"> <script language=\"JavaScript\"> <!-- \r\n    function grabCode_6a6b9e70ee024072b6e4580bdbc440fe() {\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='6a6b9e70ee024072b6e4580bdbc440fe ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 6a6b9e70ee024072b6e4580bdbc440fe';\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_6a6b9e70ee024072b6e4580bdbc440fe()\"><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\n6a6b9e70ee024072b6e4580bdbc440fe ##### SOURCE BEGIN #####\r\n%% Down the Tubes (Parametric Curves: Part 2)\r\n%\r\n% The ribbons we\r\n% <https:\/\/blogs.mathworks.com\/graphics\/2014\/12\/04\/tie-a-ribbon-round-it-parametric-curves-part-1\/ created last time>\r\n% are nice, but sometimes we want something a bit more solid. Something\r\n% like a tube.\r\n%\r\n% To do that, a single vector normal to the curve is not going to be\r\n% sufficient. We need two vectors which are normal to the curve and also\r\n% normal to each other. In theory the third partial (aka the binormal) fits\r\n% the bill, but it tends to behave rather poorly in practice.\r\n%\r\n% Another approach is to use the cross product. Remember that if we compute\r\n% the cross product of two vectors, we get a third vector which is normal\r\n% to each of those. This means that if we cross the tangent and the normal\r\n% vectors that we computed last time, we'll get another vector with exactly \r\n% the behavior we need. We can do that with this function.\r\n%\r\n%   function [xo,yo,zo] = cross_vectors(x1,y1,z1, x2,y2,z2)\r\n%     n = numel(x1);\r\n%     xo = zeros(1,n);\r\n%     yo = zeros(1,n);\r\n%     zo = zeros(1,n);\r\n%     for i=1:n\r\n%       xo(i) = y1(i)*z2(i) - z1(i)*y2(i);\r\n%       yo(i) = z1(i)*x2(i) - x1(i)*z2(i);\r\n%       zo(i) = x1(i)*y2(i) - y1(i)*x2(i);\r\n%     end\r\n    \r\n%%\r\n% Which we can use like this:\r\nt = 0:pi\/128:2*pi;\r\nx = 3*cos(t)+cos(10*t).*cos(t);\r\ny = 3*sin(t)+cos(10*t).*sin(t);\r\nz = sin(10*t);\r\n\r\n%%\r\n% Get the tangent vector.\r\ndxdt = -3*sin(t) - cos(10*t).*sin(t) - 10*sin(10*t).*cos(t);\r\ndydt = 3*cos(t) + cos(10*t).*cos(t) - 10*sin(10*t).*sin(t);\r\ndzdt = 10*cos(10*t);\r\n\r\n%%\r\n% Get the normal vector.\r\nd2xdt2 = -3*cos(t) + 20*sin(10*t).*sin(t) - 101*cos(10*t).*cos(t);\r\nd2ydt2 = -3*sin(t) - 101*cos(10*t).*sin(t) - 20*sin(10*t).*cos(t);\r\nd2zdt2 = -100*sin(10*t);\r\n[n1x,n1y,n1z] = normalize_vector(d2xdt2,d2ydt2,d2zdt2);\r\n\r\n%%\r\n% And cross them to get a second normal vector.\r\n[cx,cy,cz] = cross_vectors(dxdt,dydt,dzdt, d2xdt2,d2ydt2,d2zdt2);\r\n[n2x,n2y,n2z] = normalize_vector(cx,cy,cz);\r\n\r\n%%\r\n%\r\nplot_vectors(x,y,z, n1x,n1y,n1z);\r\nhold on\r\nplot_vectors(x,y,z, n2x,n2y,n2z);\r\nset(gca,'DataAspectRatio',[1 1 1])\r\n\r\n%%\r\n% Now we have two sets of normals which we can use to create four ribbons\r\n% which make a closed tube around the curve. Notice the pattern here. We\r\n% create 4 ribbons. Each one uses one of our 2 vectors in either the + or -\r\n% direction, and then creates 2 rows from the + and - versions of the\r\n% \"other\" vector.\r\nsides = gobjects(1,4);\r\n\r\nsx = [x+(-n1x-n2x)\/2; x+( n1x-n2x)\/2];\r\nsy = [y+(-n1y-n2y)\/2; y+( n1y-n2y)\/2];\r\nsz = [z+(-n1z-n2z)\/2; z+( n1z-n2z)\/2];\r\nsides(1) = surf(sx,sy,sz,[t;t]);\r\nhold on\r\n\r\nsx = [x+( n1x-n2x)\/2; x+( n1x+n2x)\/2];\r\nsy = [y+( n1y-n2y)\/2; y+( n1y+n2y)\/2];\r\nsz = [z+( n1z-n2z)\/2; z+( n1z+n2z)\/2];\r\nsides(2) = surf(sx,sy,sz,[t;t]);\r\n\r\nsx = [x+( n1x+n2x)\/2; x+(-n1x+n2x)\/2];\r\nsy = [y+( n1y+n2y)\/2; y+(-n1y+n2y)\/2];\r\nsz = [z+( n1z+n2z)\/2; z+(-n1z+n2z)\/2];\r\nsides(3) = surf(sx,sy,sz,[t;t]);\r\n\r\nsx = [x+(-n1x+n2x)\/2; x+(-n1x-n2x)\/2];\r\nsy = [y+(-n1y+n2y)\/2; y+(-n1y-n2y)\/2];\r\nsz = [z+(-n1z+n2z)\/2; z+(-n1z-n2z)\/2];\r\nsides(4) = surf(sx,sy,sz,[t;t]);\r\n\r\nhold off\r\nset(sides,'EdgeColor','none');\r\nset(sides,'FaceColor','interp');\r\nset(sides,'FaceLighting','gouraud');\r\nset(gca,'DataAspectRatio',[1 1 1])\r\ncolormap(hsv(256))\r\ncamlight headlight\r\n\r\n%%\r\n% That looks pretty good, but let's take a closer look. What if we move the\r\n% camera inside the scene and use a perspective projection? While we're at\r\n% it, let's save it to an animated GIF.\r\na = gca;\r\na.Visible = 'off';\r\na.Projection = 'perspective';\r\na.CameraViewAngle = 45;\r\nfirst = true;\r\nfor theta=t\r\n    a.CameraPosition = 3*[cos(theta), sin(theta), 0];\r\n    a.CameraTarget = a.CameraPosition + [-sin(theta), cos(theta), 0];\r\n    im = getframe(gcf);\r\n    [A,map] = rgb2ind(im.cdata,256);\r\n    if first\r\n        first = false;\r\n        imwrite(A,map,'torus_animation.gif','LoopCount',Inf,'DelayTime',1\/24);\r\n    else\r\n        imwrite(A,map,'torus_animation.gif','WriteMode','append','DelayTime',1\/24);\r\n    end\r\nend\r\n\r\n##### SOURCE END ##### 6a6b9e70ee024072b6e4580bdbc440fe\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/graphics\/files\/feature_image\/torus_animation.gif\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><!--introduction--><p>The ribbons we <a href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/12\/04\/tie-a-ribbon-round-it-parametric-curves-part-1\/\">created last time<\/a> are nice, but sometimes we want something a bit more solid. Something like a tube.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/graphics\/2014\/12\/16\/down-the-tubes\/\">read more >><\/a><\/p>","protected":false},"author":89,"featured_media":159,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[5],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/130"}],"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=130"}],"version-history":[{"count":7,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/130\/revisions"}],"predecessor-version":[{"id":160,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/130\/revisions\/160"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media\/159"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media?parent=130"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/categories?post=130"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/tags?post=130"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}