{"id":258,"date":"2015-07-01T09:02:18","date_gmt":"2015-07-01T13:02:18","guid":{"rendered":"https:\/\/blogs.mathworks.com\/graphics\/?p=258"},"modified":"2015-07-01T09:02:18","modified_gmt":"2015-07-01T13:02:18","slug":"stacked-bar3","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/graphics\/2015\/07\/01\/stacked-bar3\/","title":{"rendered":"Stacked Bar3"},"content":{"rendered":"<div class=\"content\"><h3>Stacked Bar3<\/h3><p>MATLAB's <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/bar3.html\">bar3 command<\/a> is kind of cool, but what if we have a 3D matrix like this?<\/p><pre class=\"codeinput\">rng(0)\r\narray = randi(10,[4 5 3]);\r\n<\/pre><p>It might be nice to create something that was sort of a stacked version of bar3. That seems hard at first, but it's not really that bad. Let's go through one way to do this step by step.<\/p><p>First we get the dimensions of the matrix. We need to know the number of rows (nr) and columns (nc) in each slice, as well as the number slices (ns).<\/p><pre class=\"codeinput\">dims = size(array);\r\n<span class=\"keyword\">switch<\/span> length(dims)\r\n    <span class=\"keyword\">case<\/span> 2\r\n        ns = 1;\r\n    <span class=\"keyword\">case<\/span> 3\r\n        ns = dims(3);\r\n    <span class=\"keyword\">otherwise<\/span>\r\n        error(<span class=\"string\">'Must be a 3D array'<\/span>)\r\n<span class=\"keyword\">end<\/span>\r\nnr = dims(1);\r\nnc = dims(2);\r\n<\/pre><p>Then we initialize the axes. We want a 3D view with a little extra room on the X and Y limits. We'll also grab the ColorOrder from the axes to use for coloring the slices.<\/p><pre class=\"codeinput\">ax = newplot;\r\nview(ax,3)\r\nxlim(ax,[.5 nc+.5])\r\nylim(ax,[.5 nr+.5])\r\nco = ax.ColorOrder;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/stacked_bar3_example_01.png\" alt=\"\"> <p>We use gobjects to create a matrix to hold the handles to our graphics objects, one for each slice.<\/p><pre class=\"codeinput\">h = gobjects(1,ns);\r\n<\/pre><p>We need a matrix that expands the center of each bar into the four corners. If the width of our bars is .8, then we need to offset + and - .4 in the X &amp; Y directions.<\/p><pre class=\"codeinput\">bw = .4;\r\noffmat = [-bw, +bw, 0; <span class=\"keyword\">...<\/span>\r\n          -bw, -bw, 0; <span class=\"keyword\">...<\/span>\r\n          +bw, -bw, 0; <span class=\"keyword\">...<\/span>\r\n          +bw, +bw, 0];\r\n<\/pre><p>Then we need a matrix that gives us the faces indices for each of the four faces that make up the sides of a bar. This gets a little tricky, so I drew a picture:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/bar_diagram.png\" alt=\"\"> <\/p><p>This is the first bar. Its bottom is centered around the point X,Y and it extends a distance bw in each of the positive and negative X &amp; Y directions. We want to put a rectangle on each of the four sides of this bar. Each rectangle visits four of the vertices in clockwise order. So the first one goes [1 2 82 81], and the next one goes [2 3 83 82]. We can make a matrix that represents this for any values of nr and nc like so:<\/p><pre class=\"codeinput\">sidemat = [1, 2, 2, 1; <span class=\"keyword\">...<\/span>\r\n           2, 3, 3, 2; <span class=\"keyword\">...<\/span>\r\n           3, 4, 4, 3; <span class=\"keyword\">...<\/span>\r\n           4, 1, 1, 4] <span class=\"keyword\">...<\/span>\r\n        + repmat([0, 0, 4*nr*nc, 4*nr*nc],[4, 1]);\r\n<\/pre><p>And we'll need one more rectangle to cap off the top of the bar. That looks like this [81 82 83 84]. For any values of nr and nc, that would look like this:<\/p><pre class=\"codeinput\">topmat = (1:4) + 4*nr*nc;\r\n<\/pre><p>We'll also need place to store the Z coordinate of the top of the bars.<\/p><pre class=\"codeinput\">top = zeros(dims(1:2));\r\n<\/pre><p>Start with the first slice. The matrix bottom holds zeros, while the matrix top holds the first slice of our input matrix.<\/p><pre class=\"codeinput\">s = 1;\r\nbottom = top;\r\ntop = bottom + array(:,:,s);\r\n<\/pre><p>Since we have 4*5 bars in a slice, we preallocate room for 160 vertices and 100 faces.<\/p><pre class=\"codeinput\">verts = zeros(4*nr*nc*2, 3);\r\nfaces = ones(5*nr*nc, 4);\r\n<\/pre><p>Then we fill the verts and faces matrices. To do this, we loop over the rows and columns. For each pair, we use repmat to create 4 copies of a vertex at the center bottom and 4 copies of a vertex at the center top. Then we add offmat to those to get the vertices at the corners.<\/p><p>We also need the face indices. Those are just our sidemat and topmat matrices, plus the number of vertices we've already created.<\/p><pre class=\"codeinput\"><span class=\"keyword\">for<\/span> r = 1:nr\r\n    <span class=\"keyword\">for<\/span> c = 1:nc\r\n        vindex = 4*(r-1 + nr*(c-1));\r\n        verts(vindex +           (1:4)', :) = repmat([c,r,bottom(r,c)],[4,1]) + offmat;\r\n        verts(vindex + 4*nr*nc + (1:4)', :) = repmat([c,r,   top(r,c)],[4,1]) + offmat;\r\n        lindex = 5*(r-1 + nr*(c-1));\r\n        rindex = 4*(r-1 + nr*(c-1));\r\n        faces(lindex + (1:5)',:) = rindex + [sidemat; topmat];\r\n    <span class=\"keyword\">end<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n<\/pre><p>Now we're ready to create a patch from those values. We'll pull the FaceColor from the ColorOrder we grabbed earlier, but we need to look out for the case where we have more slices than there are entries in the ColorOrder.<\/p><pre class=\"codeinput\">cix = 1+mod(s-1, size(co,1));\r\nh(s) = patch(<span class=\"string\">'Vertices'<\/span>, verts, <span class=\"keyword\">...<\/span>\r\n             <span class=\"string\">'Faces'<\/span>, faces, <span class=\"keyword\">...<\/span>\r\n             <span class=\"string\">'FaceColor'<\/span>, co(cix,:), <span class=\"keyword\">...<\/span>\r\n             <span class=\"string\">'Parent'<\/span>, ax);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/stacked_bar3_example_02.png\" alt=\"\"> <p>Repeat this for each slice, copying the old top to the new bottom each time, incrementing s, and doing the above steps again.<\/p><pre class=\"codeinput\">bottom = top;\r\ns = s+1;\r\n<\/pre><p>The result, with a for loop and some error checking, looks like this:<\/p><pre class=\"codeinput\">type <span class=\"string\">stacked_bar3<\/span>\r\n<\/pre><pre class=\"codeoutput\">\r\nfunction h = stacked_bar3(array)\r\n    if any(array(:) &lt; 0)\r\n        error('Only positive values supported')\r\n    end\r\n    \r\n    dims = size(array);\r\n    if any(dims==0)\r\n        error('Empty dimensions are not supported')\r\n    end    \r\n\r\n    switch length(dims)\r\n        case 2\r\n            ns = 1;\r\n        case 3\r\n            ns = dims(3);\r\n        otherwise\r\n            error('Must be a 3D array')\r\n    end\r\n    nr = dims(1);\r\n    nc = dims(2);\r\n    \r\n    ax = newplot;\r\n    co = ax.ColorOrder;    \r\n    h = gobjects(1,ns);\r\n    view(ax,3)\r\n    xlim(ax,[.5 nc+.5])\r\n    ylim(ax,[.5 nr+.5])\r\n    \r\n    bw = .4;\r\n    offmat = [-bw, +bw, 0; ...\r\n              -bw, -bw, 0; ...\r\n              +bw, -bw, 0; ...\r\n              +bw, +bw, 0];\r\n    sidemat = [1, 2, 2, 1; ...\r\n               2, 3, 3, 2; ...\r\n               3, 4, 4, 3; ...\r\n               4, 1, 1, 4] ...\r\n            + repmat([0, 0, 4*nr*nc, 4*nr*nc],[4, 1]);\r\n    topmat = (1:4) + 4*nr*nc;\r\n\r\n    top = zeros(dims(1:2));\r\n    for s = 1:ns\r\n        bottom = top;\r\n        top = bottom + array(:,:,s);\r\n\r\n        verts = zeros(4*nr*nc*2, 3);\r\n        faces = ones(5*nr*nc, 4);\r\n        for r = 1:nr\r\n            for c = 1:nc\r\n                vindex = 4*(r-1 + nr*(c-1));\r\n                lindex = 5*(r-1 + nr*(c-1));\r\n                rindex = 4*(r-1 + nr*(c-1));\r\n                verts(vindex +           (1:4)', :) = repmat([c,r,bottom(r,c)],[4,1]) + offmat;\r\n                verts(vindex + 4*nr*nc + (1:4)', :) = repmat([c,r,   top(r,c)],[4,1]) + offmat;\r\n                faces(lindex + (1:5)',:) = rindex + [sidemat; topmat];\r\n            end\r\n        end\r\n        \r\n        cix = 1+mod(s-1, size(co,1));\r\n        h(s) = patch('Vertices', verts, ...\r\n                     'Faces', faces, ...\r\n                     'FaceColor', co(cix,:), ...\r\n                     'Parent', ax);\r\n                 \r\n        bottom = top;\r\n    end\r\nend\r\n<\/pre><p>We can call it like this:<\/p><pre class=\"codeinput\">stacked_bar3(array);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/stacked_bar3_example_03.png\" alt=\"\"> <p>We can also add in features such as perspective ...<\/p><pre class=\"codeinput\">ax.Projection = <span class=\"string\">'perspective'<\/span>;\r\nax.ZGrid = <span class=\"string\">'on'<\/span>;\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/stacked_bar3_example_04.png\" alt=\"\"> <p>... or lighting ...<\/p><pre class=\"codeinput\">h = stacked_bar3(randi(10,[4 3 3]));\r\nlight(<span class=\"string\">'Position'<\/span>,[-.3 -.2 .8])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/stacked_bar3_example_05.png\" alt=\"\"> <p>... or transparency, although that currently disables the antialiasing.<\/p><pre class=\"codeinput\">h = stacked_bar3(randi(10,[4 5 3]));\r\nalpha(h,.75)\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/stacked_bar3_example_06.png\" alt=\"\"> <p>One other thing to consider is the DataAspectRatio. If the numbers of rows and columns are very different, then the bars don't come out square. That looks a little odd:<\/p><pre class=\"codeinput\">h = stacked_bar3(randi(10,[4 14 3]));\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/stacked_bar3_example_07.png\" alt=\"\"> <p>We can adjust that using the <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/daspect.html\">daspect function<\/a>. A value of [1 1 10] means that the X &amp; Y directions should have the same scale, but the scale of the Z direction sould be one tenth of that.<\/p><pre class=\"codeinput\">daspect([1 1 10])\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/images\/graphics\/2015\/stacked_bar3_example_08.png\" alt=\"\"> <script language=\"JavaScript\"> <!-- \r\n    function grabCode_fc15a9afce094ae2bf4bb288c978bf84() {\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='fc15a9afce094ae2bf4bb288c978bf84 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' fc15a9afce094ae2bf4bb288c978bf84';\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 2015 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_fc15a9afce094ae2bf4bb288c978bf84()\"><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; R2015a<br><\/p><\/div><!--\r\nfc15a9afce094ae2bf4bb288c978bf84 ##### SOURCE BEGIN #####\r\n%% Stacked Bar3\r\n% MATLAB's <https:\/\/www.mathworks.com\/help\/matlab\/ref\/bar3.html bar3 command>\r\n% is kind of cool, but what if we have a 3D matrix like this?\r\nrng(0)\r\narray = randi(10,[4 5 3]);\r\n\r\n%%\r\n% It might be nice to create something that was sort of a stacked\r\n% version of bar3. That seems hard at first, but it's not really that bad.\r\n% Let's go through one way to do this step by step.\r\n%\r\n% First we get the dimensions of the matrix. We need to know the number of \r\n% rows (nr) and columns (nc) in each slice, as well as the number slices\r\n% (ns).\r\ndims = size(array);\r\nswitch length(dims)\r\n    case 2\r\n        ns = 1;\r\n    case 3\r\n        ns = dims(3);\r\n    otherwise\r\n        error('Must be a 3D array')\r\nend\r\nnr = dims(1);\r\nnc = dims(2);\r\n\r\n%%\r\n% Then we initialize the axes. We want a 3D view with a little extra room\r\n% on the X and Y limits. We'll also grab the ColorOrder from the axes to\r\n% use for coloring the slices.\r\nax = newplot;\r\nview(ax,3)\r\nxlim(ax,[.5 nc+.5])\r\nylim(ax,[.5 nr+.5])\r\nco = ax.ColorOrder;\r\n\r\n%%\r\n% We use gobjects to create a matrix to hold the handles to our graphics\r\n% objects, one for each slice.\r\nh = gobjects(1,ns);\r\n\r\n%%\r\n% We need a matrix that expands the center of each bar into the four\r\n% corners. If the width of our bars is .8, then we need to offset + and -\r\n% .4 in the X & Y directions.\r\nbw = .4;\r\noffmat = [-bw, +bw, 0; ...\r\n          -bw, -bw, 0; ...\r\n          +bw, -bw, 0; ...\r\n          +bw, +bw, 0];\r\n      \r\n%%\r\n% Then we need a matrix that gives us the faces indices for each of the \r\n% four faces that make up the sides of a bar. This gets a little tricky, so\r\n% I drew a picture:\r\n%\r\n% <<..\/bar_diagram.png>>\r\n%\r\n% This is the first bar. Its bottom is centered around the point X,Y and it\r\n% extends a distance bw in each of the positive and negative X & Y\r\n% directions. We want to put a rectangle on each of the four sides of this\r\n% bar. Each rectangle visits four of the vertices in clockwise order. So\r\n% the first one goes [1 2 82 81], and the next one goes [2 3 83 82]. We can\r\n% make a matrix that represents this for any values of nr and nc like so:\r\n%\r\nsidemat = [1, 2, 2, 1; ...\r\n           2, 3, 3, 2; ...\r\n           3, 4, 4, 3; ...\r\n           4, 1, 1, 4] ...\r\n        + repmat([0, 0, 4*nr*nc, 4*nr*nc],[4, 1]);\r\n    \r\n%%\r\n% And we'll need one more rectangle to cap off the top of the bar. That\r\n% looks like this [81 82 83 84]. For any values of nr and nc, that would\r\n% look like this:\r\n%\r\ntopmat = (1:4) + 4*nr*nc;\r\n\r\n%%\r\n% We'll also need place to store the Z coordinate of the top of the bars. \r\ntop = zeros(dims(1:2));\r\n\r\n%%\r\n% Start with the first slice. The matrix bottom holds zeros, while the\r\n% matrix top holds the first slice of our input matrix.\r\n%\r\ns = 1;\r\nbottom = top;\r\ntop = bottom + array(:,:,s);\r\n\r\n%%\r\n% Since we have 4*5 bars in a slice, we preallocate room for 160 vertices \r\n% and 100 faces.\r\nverts = zeros(4*nr*nc*2, 3);\r\nfaces = ones(5*nr*nc, 4);\r\n\r\n%%\r\n% Then we fill the verts and faces matrices. To do this, we loop over the\r\n% rows and columns. For each pair, we use repmat to create 4 copies of a \r\n% vertex at the center bottom and 4 copies of a vertex at the center top.\r\n% Then we add offmat to those to get the vertices at the corners.\r\n%\r\n% We also need the face indices. Those are just our sidemat and topmat\r\n% matrices, plus the number of vertices we've already created.\r\nfor r = 1:nr\r\n    for c = 1:nc\r\n        vindex = 4*(r-1 + nr*(c-1));\r\n        verts(vindex +           (1:4)', :) = repmat([c,r,bottom(r,c)],[4,1]) + offmat;\r\n        verts(vindex + 4*nr*nc + (1:4)', :) = repmat([c,r,   top(r,c)],[4,1]) + offmat;\r\n        lindex = 5*(r-1 + nr*(c-1));\r\n        rindex = 4*(r-1 + nr*(c-1));\r\n        faces(lindex + (1:5)',:) = rindex + [sidemat; topmat];\r\n    end\r\nend\r\n\r\n%%\r\n% Now we're ready to create a patch from those values. We'll pull the\r\n% FaceColor from the ColorOrder we grabbed earlier, but we need to look out\r\n% for the case where we have more slices than there are entries in the\r\n% ColorOrder.\r\ncix = 1+mod(s-1, size(co,1));\r\nh(s) = patch('Vertices', verts, ...\r\n             'Faces', faces, ...\r\n             'FaceColor', co(cix,:), ...\r\n             'Parent', ax);\r\n\r\n%%\r\n% Repeat this for each slice, copying the old top to the new bottom each time,\r\n% incrementing s, and doing the above steps again.\r\nbottom = top;\r\ns = s+1;\r\n\r\n%%\r\n% The result, with a for loop and some error checking, looks like this:\r\ntype stacked_bar3\r\n\r\n%%\r\n% We can call it like this:\r\nstacked_bar3(array);\r\n\r\n%%\r\n% We can also add in features such as perspective ...\r\nax.Projection = 'perspective';\r\nax.ZGrid = 'on';\r\n\r\n%%\r\n% ... or lighting ...\r\nh = stacked_bar3(randi(10,[4 3 3]));\r\nlight('Position',[-.3 -.2 .8])\r\n\r\n%%\r\n% ... or transparency, although that currently disables the antialiasing.\r\nh = stacked_bar3(randi(10,[4 5 3]));\r\nalpha(h,.75)\r\n\r\n%%\r\n% One other thing to consider is the DataAspectRatio. If the numbers of\r\n% rows and columns are very different, then the bars don't come out square.\r\n% That looks a little odd:\r\nh = stacked_bar3(randi(10,[4 14 3]));\r\n\r\n%%\r\n% We can adjust that using the \r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/daspect.html daspect function>.\r\n% A value of [1 1 10] means that the X & Y directions should have the same\r\n% scale, but the scale of the Z direction sould be one tenth of that.\r\ndaspect([1 1 10])\r\n\r\n##### SOURCE END ##### fc15a9afce094ae2bf4bb288c978bf84\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/graphics\/files\/feature_image\/stacked_bar3_thumbnail.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>Stacked Bar3MATLAB's bar3 command is kind of cool, but what if we have a 3D matrix like this?rng(0)\r\narray = randi(10,[4 5 3]);\r\nIt might be nice to create something that was sort of a stacked... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/graphics\/2015\/07\/01\/stacked-bar3\/\">read more >><\/a><\/p>","protected":false},"author":89,"featured_media":260,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[7],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/258"}],"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=258"}],"version-history":[{"count":2,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/258\/revisions"}],"predecessor-version":[{"id":261,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/posts\/258\/revisions\/261"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media\/260"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/media?parent=258"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/categories?post=258"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/graphics\/wp-json\/wp\/v2\/tags?post=258"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}