Guest Writer: Abby Skofield In today's article, Abby Skofield slices through the debate about pie charts and introduces two new delectable desserts recently added to the MATLAB menu. Abby is a... read more >>

]]>I've been working with the MATLAB Charting team for 12 years, but you don't need to be surrounded by data viz nerds for that long to have heard the rumors about pie charts. At best, pie charts should be used carefully and sparingly; at worst, they "*subtract from the world's knowledge*." Pie charts have some haters, including my boss, who wears this shirt around the office sometimes.

And that's why I'm surprised to find myself here today telling you about our brand spanking new **piechart **released in R2023b. I have to admit, I find myself drawn back to pie charts again and again. And I know you are too, given the number of bug reports and enhancement requests we get from you about our existing pie function.

There are lots of different reasons why pie charts are tricky. They turn into a big mess as soon as you have more than a handful of wedges. They can be misleading if they are used to represent data that aren't parts of a whole (e.g., a pie chart showing the ages of people in your household). And at a basic, perceptual level humans are better at differentiating length (heights of different bars) as opposed to differentiating angle (pie wedges). For example, compare the pie and bar charts below.

data = [10 11 9.5 16 18 17];

figure

tiledlayout horizontal

nexttile

pie(data)

nexttile

bar(data, FaceColor = "flat", CData = 1:6);

clim([1 6])

% Axes niceness

axis padded square

Unfortunately, MATLAB's current pie chart, created with the pie command, is an easy target for the haters not only because it's an empirically bad way to visualize data, but the MATLAB implementation doesn't support many of the programming conventions that MATLAB graphics objects have adopted over the last decade or so. We've heard from customers over and over again, pointing out strange behaviors and asking for simpler workflows and new features. A lot of the pain comes from the fact that pie simply creates a bunch of patch and text objects in a Cartesian axes. Internally, we call this a "bucket of parts" chart because, well, that's what you get - a bucket of the parts used to cobble it together! (It's MATLAB, so of course your bucket is an array.)

figure

bucketOfPieParts = pie([10 11 9.5 16 18 17]);

axis on

bucketOfPieParts

There is no easy way to customize this pie chart as a whole after it's created - instead you need to use indexing or findobj to get the right text or patch handles and set properties on those. For example, here's how you would have to work with the old pie command to customize the chart's appearance and to use the standard ColorOrder colors you see elsewhere in MATLAB. (Requests for a simple way to change pie's colors is something we hear frequently from customers in the Community.)

figure

p = pie([10 11 9.5 16 18 17]);

% Option 1: index to get every other handle which is a text object

txt = p(2:2:end);

set(txt,'Color',[0.5 0.5 0.5])

% Option 2: alternatively, use findobj to get the set of objects with the desired "Type"

pch = findobj(p,'Type','patch');

set(pch,'EdgeColor','w','LineWidth',2)

colors = get(gca,'ColorOrder');

colorsCellArray = mat2cell(colors,ones(7,1),3);

set(pch,{'FaceColor'},colorsCellArray(1:numel(pch)))

Many of you have also reported running into surprising behaviors, most of which are due to the fact that the pie wedges are drawn in the data coordinates of a Cartesian axes. For example, the xlabel and ylabel functions appear to do nothing, while surprisingly the view(3) command, which rotates the axes, actually works.

figure

pie([10 11 9.5 16 18 17])

xlabel('Some important data') % doesn't show up, because pie sets the axes visibility off

view(3) % wow, that works!

Because we hear you, and because we don't judge you for needing an extra serving of pie, we are excited to introduce a new and improved piechart and her fashion-forward BFF donutchart in R2023b.

data = [10 11 9.5 16 18 17];

figure

tiledlayout('horizontal')

nexttile

pc = piechart(data);

nexttile

donutchart(data);

pc % let's check out some of the properties ...

Both charts are real graphics objects with properties, so you'll be able to work with them the way you do the other graphics objects you know and love. This also means we have a way to start addressing all your feature requests. Of course, we weren't able to deliver everything in this first version, but we are excited to have some fresh new ground to work on.

We've added properties to the new PieChart and DonutChart objects to give you some of the controls you've been looking for. For instance, in this example I'm setting the EdgeColor, LineWidth and InnerRadius properties to achieve a more minimal look with white edges and a thinner donut. And the colororder command can be used to change the color palette with one line of code! Note also that I'm shifting the StartAngle to -45 degrees to change the orientation of the chart, and I could have swapped the Direction of the wedges to clockwise if I had wanted to. The ability to change the orientation of a pie chart was another common request for the old pie chart that we were never able to accommodate.

figure

dc = donutchart([60 17 8 15], ["pass","in progress","filtered","fail"]);

dc.EdgeColor = 'w';

dc.LineWidth = 2;

dc.InnerRadius = 0.7;

colororder meadow

dc.StartAngle = -45;

The new piechart and donutchart functions support tables and table variable names as inputs. We've been adding support for plotting data directly from tables to many of our existing plotting functions, like plot and scatter, and the new piechart and donutchart functions extend the pattern. You can pass in a table as the first argument and then indicate which table variables to use for the data, and optionally the names.

% Create an example table

Status = ["pass","in progress","filtered","fail"]';

January = [132 4 24 228]';

February = [240 68 32 60]';

March = [344 12 4 48]';

t = table(Status, January, February, March)

figure

dc = donutchart(t,'January','Status');

colororder meadow

You can even update your chart to display the data from a different variable! For instance, here we start by visualizing the data from the "January" variable, and then switch the DataVariable to "February" to see how things have changed from month to month. The ability to update the data on an existing object will also make the new objects great for app building and animation workflows.

% Swap the data variable to look at another month

dc.DataVariable = 'February';

Finally, the new PieChart and DonutChart objects support interactive workflows. You can explore your data with data tips by hovering over the chart. In this example, piechart has binned a bunch of categorical values, allowing us to visualize the relative proportions of species in the array. The data tip shows us the count of how many "virginica" items were binned into the yellow wedge. And you can interactively modify the properties I described earlier, and discover what other properties are available, in the Property Inspector.

load fisheriris.mat

piechart(categorical(species([1:75 135:end])));

We can't wait to hear what you think of these new charts! Are you #teamdonut with me, or maybe you like things the old fashioned way? And what features are you excited for us to add next? Based on past requests, I expect I'll hear from many of you about what you want to do with those labels - do tell!

Guest Writer: Eric Ludlam Today's graphics adventure is written by Eric Ludlam, development manager of MATLAB's Charting team. Eric has a whopping 27 years of experience working in MATLAB... read more >>

]]>Guest Writer: Eric Ludlam

Today's graphics adventure is written by Eric Ludlam, development manager of MATLAB's Charting team. Eric has a whopping 27 years of experience working in MATLAB Graphics, starting in MATLAB 5.0! Eric is renowned within MathWorks for his love of trebuchets, catapults and *Punkin Chunkin*. His exceptional graphics demos have appeared in MathWorks' Community blog, MATLAB blog, and in MATLAB social media feeds. Today he answers the collective question we all have. How'd you do that?

The MATLAB social media team has been sharing images and code for different pumpkins around Halloween time for the past couple years, and these gourds have been pretty popular. One question asked in the comments was how the pumpkin was designed. Today I'd like to explain the types of decisions made while developing the code and share some fun tricks with vectorization and implicit expansion for anyone just learning how to write efficient code in MATLAB.

The pumpkin I'd like to use today is a speckled yellow and orange pumpkin I picked up locally here in Massachusetts. Here's a picture of our target pumpkin:

There are a few interesting features of this pumpkin, including a stubby little stem, 2 sets of creases/bumps, a 2-tone color scheme where the 2nd color is in the deeper crease, and speckles. A bunch of fun challenges!

Before we start let's review the various components of our algorithm. We can break the overall shape of our pumpkin into 2 major parts, the semi-spherical pumpkin part, and the stem.

The algorithm for the pumpkin part is made of 3 key components which will be combined together into one set of matrices drawn with the surf command.

- Unit Sphere of the underlying shape
- Creases and bumps as seen around the circumference
- Dimple on the top and bottom

I'll start describing how to make the patterns for creases, bumps, and the dimple for the pumpkin, then move to how the stem is made before combining it all at the end.

In this article, I'll use "crease" to describe an indentation that travels from the stem to the bottom of the pumpkin. I'll use the term "bump" to generally refer to one pattern that runs from crease-to-crease around the circumference.

There is one set of creases that are deeper (and more orange) than the other. I'll call a deep crease the 'primary' crease, and the other is the 'secondary' crease. Since these creases are mapped onto a sphere, there are the same number of bumps as there are creases. To develop a crease/bump pattern we need a way to create a repeating pattern numerically. To do this we need to set up a few parameters, including how many bumps there are, and how many vertices we'd like in our graphic.

I count around 10 creases, with secondary crease between each one. We also want to select enough vertices per bump to provide a smooth look across the pumpkin. I tried a few different values for vertices per bump and found that 10 looks good while still being a small number when multiplied out against the total number of bumps. We don't want too many vertices because this number also impacts the size of the speckles which we'll see at the end, and our target pumpkin has big spots.

We can compute our total number of vertices from the total number of bumps, and use that size for the combined set. The +1 for numVerts is because the first and last vertex around the sphere are shared, which will align the creases with a vertex so they look sharp.

numPrimaryBumps = 10;

totalBumps = numPrimaryBumps*2; % Add in-between creases.

vertsPerBump = 10;

numVerts = totalBumps*vertsPerBump+1;

Next, we need to compute the depth of the creases for both the primary and secondary creases. We'll specify the depth in terms of a unit sphere. To create our pattern vector we'll start with a linear list of values and use mod(x, 2) to create the repeating pattern which means we start with values from 0 to the number of bumps*2.

rPrimary = linspace(0,numPrimaryBumps*2,numVerts);

rSecondary = linspace(0,totalBumps*2,numVerts);

Next, we'll define some depths for the creases. The depth of the primary crease will be the sum of crease_depth and crease_depth2, so we need to keep that in mind while selecting depth values.

The bump vectors Rxy_primary and Rxy_secondary represent the change in radius of our unit sphere in the xy plane. mod is used to create a repeating pattern between [ 0 2 ]. That pattern needs to be shifted to [ -1 1 ] so that when the result is squared, it creates a curve.

crease_depth = .04;

crease_depth2 = .01;

Rxy_primary = 0 - (1-mod(rPrimary,2)).^2*crease_depth;

Rxy_secondary = 0 - (1-mod(rSecondary,2)).^2*crease_depth2;

Rxy = Rxy_primary + Rxy_secondary;

Let's plot the result in polar coordinates so we can get a feel for what it will look like. When plotting, add 1 to the radius to simulate how we'll be adding the pumpkin's core diameter to this crease pattern.

polarplot(Rxy+1, 'LineWidth',2);

rlim([0 1.1])

The secondary bump looks shallow in this diagram, but that is roughly what I found on the real pumpkin.

Speaking of which, let's take a quick look at what we have so far by multiplying Rxy to the X & Y coordinates of a sphere.

[ Xsphere, Ysphere, Zsphere ] = sphere(numVerts-1); % Sphere creates +1 verts

Xpunkin = (1+Rxy).*Xsphere;

Ypunkin = (1+Rxy).*Ysphere;

surf(Xpunkin,Ypunkin,Zsphere)

shading interp

daspect([1 1 1])

camlight

Well, this looks more like a ridgy sports ball than a pumpkin. That's because our target pumpkin has a dimple in the top and bottom.

The dimple on a pumpkin is caused by how the fruit expands off the end of the stem as it grows. We'll use an exponential function to create the dimple as we did for the creases, but across only a single span between -1 and 1. Note also that I'm creating a column vector. That will let us apply the shape to the columns of our sphere matrix to define its vertical profile instead of applying it to the rows which would affect the bump pattern.

To see how it looks we'll plot this in polar coordinates as well, but this time I'm only showing half the polar chart since I'm computing a single profile.

dimple = .2; % Fraction to dimple into top/bottom

rho = linspace(-1,1,numVerts)';

Rz_dimple = (0-rho.^4)*dimple;

polarplot(linspace(0,pi,numVerts),Rz_dimple+1, 'LineWidth',2);

rlim([0 1.1])

thetalim([0 180])

set(gca,'ThetaZeroLocation','bottom')

This looks a bit exaggerated in polar coordinates since the polar chart is perfectly round. When we apply it to the sphere we'll also squish it to simulate that our pumpkin is shorter than it is wide. That will give it a more natural look. I took a bit of a guess that the height of the pumpkin is just 80% of the diameter at the equator.

Computing the Zpunkin matrix requires adding bumps across the rows the same way as Xpunkin and Ypunkin do. It also needs the height to be adjusted, and the dimple applied on the columns. This is done using element-wise multiply .* using the row vector Rxy, and the column vector Rz_dimple.

heightratio = .8;

Zpunkin = (1+Rxy).*Zsphere.*(heightratio+Rz_dimple);

%Â Â Â Â Â ^bumpsÂ ^sphere mesh ^squish 80%Â ^column vector to apply dimple

surf(Xpunkin,Ypunkin,Zpunkin)

shading interp

daspect([1 1 1])

camlight

We're getting closer, but the dimple still looks a bit weird. Fortunately, we can stick a stem on there to hide that artifact.

Before starting on the stem, let's take a close look at our target pumpkin's stem:

You'll notice that every bump on the stem lines up with one of the primary creases in our pumpkin! Turns out this is because the bumps that naturally occur on the stem impact the pumpkin as it grows, constraining it into a crease. For this particular pumpkin it isn't as well defined as with other breeds, and one stem bump seems to be missing top-left, but that's ok. This is MATLAB, so we'll create a perfect pumpkin stem instead.

To start, we'll create another pattern, similar to the pumpkin creases by re-using rPrimary, but offset from the pumpkin bumps by 1/2 a pattern. That way the outward bumps of the stem line up with the creases in the pumpkin.

Next, we'll create some cylinder height coordinates as Zcyl and square it to compute a curved profile so the stem is wider on the bottom than on the top.

Rstem = (1-(1-mod(rPrimary+1,2)).^2)*.05;

thetac = linspace(0,2,numVerts);

Xcyl = cospi(thetac);

Ycyl = sinpi(thetac);

Zcyl = linspace(0,1,11)'; % column vector

Rstemz = .7+(1-Zcyl).^2*.6;

Once we have these base coordinates, compute the cylindrical mesh the same way we did for the pumpkin sphere, but instead apply it to the cylinder coordinates. The row vectors Xcyl and Ycyl, when multiplied by the column vector Rstemz using element-wise multiplication operator ".*", will implicitly expand into a 2D matrix, perfect for drawing a mesh.

Xstem = (.1+Rstem).*Xcyl.*Rstemz;

Ystem = (.1+Rstem).*Ycyl.*Rstemz;

Zstem = repmat(Zcyl*.15,1,numVerts);

Let's take a look at our stem using a surf plot and fill in the top with a patch.

surf(Xstem,Ystem,Zstem);

shading interp

patch('Vertices', [Xstem(end,:)' Ystem(end,:)' Zstem(end,:)'],...

Â Â Â Â Â Â 'Faces', 1:numVerts, ...

Â Â Â Â Â Â 'FaceColor','y','EdgeColor','none')

lighting gouraud

daspect([1 1 1]);

camlight

This stem has a lot more definition than our target pumpkin. We'll say it's a more perfect stem.

Ok, let's put it all together! In this next code snippet, we'll re-create the pumpkin and give it a new colormap with colors selected off the photo of our target pumpkin. Then we'll add the stem but offset it by the height of our pumpkin. We'll also color the stem and stem cap based on colors also selected from the photo.

One trick in this code snippet is the creation of a colormap with only 2 colors. The validatecolor function is a new feature from R2020b that will convert valid color strings like web hex codes into RGB colors. This makes it easy to use a common color specifier in MATLAB.

Spunkin = surf(Xpunkin,Ypunkin,Zpunkin,'FaceColor','interp','EdgeColor','none');

colormap(validatecolor({'#da8e26' '#dfc727'},'multiple'));

Sstem = surface(Xstem,Ystem,Zstem+heightratio^2,'FaceColor','#3d6766','EdgeColor','none');

Pstem = patch('Vertices', [Xstem(end,:)' Ystem(end,:)' Zstem(end,:)'+heightratio^2],...

Â Â Â Â Â Â Â Â Â Â Â Â 'Faces', 1:numVerts, ...

Â Â Â Â Â Â Â Â Â Â Â Â 'FaceColor','#b1cab5','EdgeColor','none');

daspect([1 1 1])

camlight

Hmm. We didn't spend any time computing where the colors would go, so by default, color is the same as Z.

Our goal is for the depths of the creases to be orange, and the bumps to be yellow, so we'll use hypot to compute distance from center as if we had a pumpkin with no dimples on the top and bottom. Let's see how that looks:

Cpunkin = hypot(hypot(Xpunkin,Ypunkin),(1+Rxy).*Zsphere); % As if pumpkin were round with no dimples

set(Spunkin,'CData',Cpunkin); % Pumpkin CData

set(Sstem,'CData',[]); % Make sure the stem doesn't contribute to auto Color Limits

This looks too crisp due to the sharp boundary between the orange and yellow colors caused by having only 2 colors in the colormap. Fortunately, we still need to add all the speckles which we can do by adding some normally distributed random noise. Selecting the right scale for the noise took a bit of fiddling so it didn't overwhelm the bumps.

set(Spunkin,'CData',Cpunkin+randn(numVerts)*0.015);

The speckles mitigated the hard color boundary, so with that out of the way, we are almost done. Let's decorate by improving the lighting and material properties to give it more of a waxy light reflectance and add in more ambient light. We'll also turn off the axes and zoom in a bit.

daspect([1 1 1])

axis off

camzoom(1.8)

lighting gouraud

material([Spunkin Sstem Pstem],[ .6 .9 .3 2 .6 ])

Share your MATLAB Pumpkin images with us in the comments below!

One of our missions for this blog is to boost our readers' familiarity with the latest features and to keep you in the forefront of MATLAB graphics and app building advancements. Last week I... read more >>

]]>One of our missions for this blog is to boost our readers' familiarity with the latest features and to keep you in the forefront of MATLAB graphics and app building advancements. Last week I mentioned that MATLAB R2023b has more than two dozen new features in the graphics and app building domains. Help us achieve our goal by letting us know which new features you would like to see covered in the blog!

One of my favorite features from R2023b is a trio of convenience functions that control secondary labels on Cartesian axes.

You might be wondering, what are secondary labels?

Secondary labels are designed to display additional information about an axis such as tick label units. By default, MATLAB adds a secondary label next to an axis when plotting datetime values, duration values, or numeric values in exponential form. The use of secondary labels often simplifies tick labels by eliminating repetitive information such as including the same year in all datetime tick labels. Here are some automatically generated secondary labels for numeric, duration, and datetime rulers.

To generate a datetime example similar to the purple axes above,

date1 = datetime(2023,09,13,0,3,0) + seconds(-45:5:45);

date2 = datetime(2023,10,28,0,0,0) + days(-45:5:45);

plot(date1, date2, "o");

axis tight

Here are some common situations reported by MATLAB users where these functions will come in handy.

Remove a secondary label. Time-of-day is the focus of your data and you don't want to show the date in the purple axes above. Remove the secondary label using,

xsecondarylabel(Visible = "off")

Change a secondary label. In your manuscript, "hr" is used to indicate "heart rate" and you want to change "hr" in the orange axes above to "hours". Change the secondary label using,

ysecondarylabel('hours')

Add your own secondary label. You're working with scaled data such that the values 0.5 and 1.0 should be interpreted as five million and ten million. Add the exponent notation shown in the blue axes above using,

xsecondarylabel('x10^7')

Label axis units. This is my favorite reason to use these new functions. Label the axes' units without relying on the xlabel or ylabel. Let's look at a full demo below.

In 1662 Robert Boyle discovered that the volume of an ideal gas is inversely proportional to the pressure exerted upon the gas assuming constant temperature (Boyle's Law). Graphical visualizations were not yet common in 1662 so Boyle published his results as a table of numbers. Let's plot the results to show the relationship between pressure and volume for ideal gases.

Read in Boyle's data from 1662 using Boyle's original generic variable names. We'll use columns A2 (gas volume) and D (pressure). For a complete explanation of the table variables, see J. B. West (1999, J Appl Physiol).

T = readtable("https://blogs.mathworks.com/graphics-and-apps/files/boylesData.csv")

Boyle trapped gas between the end of a j-shaped tube and a column of mercury. He measured pressure upon the gas in units of inHG or inches of mercury by gradually adding mercury to the tube which increases the pressure on the gas. Volume of the gas was measured by the height of the gas pocket at the end of the tube, in inches. I dug deep searching for the diameter of his tube so we could use actual units of volume but couldn't find it. Inches will suffice assuming the tube had a uniform diameter. A shout-out is waiting for anyone who can find Boyle's tube diameter from a credible source!

Boyle treated volume as the independent variable (table variable A2) so we'll plot volume along the x-axis and pressure (table variable D) on the y-axis.

figure();

plot(T.A2, T.D, "-s")

grid on

title("Boyle's Data")

xlabel("Volume")

ylabel("Pressure")

Add secondary labels to indicate axis units (starting in MATLAB R2023b).

xsecondarylabel("in")

ysecondarylabel("inHg")

Boyle's data shows that the volume of the gas is inversely proportional to the pressure exerted upon it. As pressure falls, volume increases like a balloon expanding in a vacuum. This relationship is beautifully shown by plotting the reciprocal of pressure against volume.

figure()

plot(T.A2, 1./T.D, "-o");

grid on

xlim([0, 15])

ylim([0, 0.04])

title("Boyle's Law")

xlabel("Volume")

ylabel("Inverse of pressure")

xsecondarylabel("in");

ysecondarylabel("inHg^-1")

We've learned how to change, create, or remove a secondary label. Let's continue the discussion in the comments section below!

- What other customizations would be helpful with secondary labels?
- What other recent new features in graphics and app building are you interested in learning more about?
- Do you think Robert Boyle's interest in pressure and volume was affected by being the 14th child in his family?

]]>

Welcome to the launch of the MATLAB Graphics and App Building blog! I am your host, Adam Danz, whom some readers may recognize from the MATLAB Central Community. My experience with MATLAB started as... read more >>

]]>Welcome to the launch of the MATLAB Graphics and App Building blog! I am your host, Adam Danz, whom some readers may recognize from the MATLAB Central Community. My experience with MATLAB started as an overwhelmed graduate student staring at an empty MATLAB desktop unsure of where to begin. Working as a Ph.D. student in a neurophysiology laboratory, specializing in vision science, I developed a passion for data analytics and building interactive apps to streamline my analyses. One evening in the lab I stumbled upon the MATLAB Central community and decided to answer some questions. Engaging with 1000s of fellow users since then and delving into their projects, goals, and challenges has been an immensely rewarding experience.

In 2020, I was honored to join the MathWorks Community Advisory Board, and in 2022, I became a developer on the MATLAB Graphics and Charting team at MathWorks. While my expertise is in areas of science, math, and data analytics, I also occasionally express some artistic creativity through MATLAB (secretly, I'm really in it for the math). Here are some graphics I generated as a grad student and MathWorker in the MathWorks Mini Hack contests (2021, 2022 galleries; but wait till you see what my manager can create!).

Today Iâ€™m excited to bridge my experiences as a MATLAB user, community contributor, and MathWorker as the host of the new MATLAB Graphics and App Building blog! In addition to my own contributions, expect to meet other esteemed guest writers from the graphics and app building area at MathWorks, who will each bring diverse perspectives and backgrounds.

Some readers may fondly recall MathWorksâ€™ Mike on MATLAB Graphics blog from 2014-2017 which served as a valuable resource for navigating the changes to the graphics system in R2014b. Since then, our system has grown in many exciting new ways and directions:

- App Designer was introduced in R2016a and has seen new UI components, app building conveniences, and performance improvements with every single MATLAB release.
- App Designer opened the door to MATLAB web apps, an entirely new workflow made possible by the MATLAB Web App Server.
- The breadth of charts in MATLAB has grown significantly, with many supporting important datatypes including tables, timetables, datetime, duration, and categoricals.
- The graphics interactions system has also evolved to allow for more seamless exploration and analysis of your data.
- New layouts and layout conveniences for figures, axes, and apps have become increasingly flexible and user-friendly.
- Exporting your carefully crafted visualizations and apps for presentations and publications is easier today with new functions and options.
- And much more, from performance improvements, to Simulink integrations, and even a sneak peek at dark mode for your plots and apps via the New Desktop for MATLAB (beta).

With such substantial progress made since our previous blog, there is an abundance of exciting updates and advancements to catch up on. In fact, the recent MATLAB R2023b release alone boasts more than two dozen new features in the graphics and app building domains!

We want to include you in the action, highlight whatâ€™s new, demonstrate workflows, inspire readers with neat demos, and share stories, tips, and tricks with readers from various backgrounds and skill levels.

Moreover, we value your thoughts and questions! What specific topics would you like to see covered in this blog? What burning questions or curiosities can we address? I encourage you to respond in the comments below or reach out to me by using the contact button in my profile.

I affectionately refer to the vibrant MATLAB community as The MATropolis where you and I are residents! The background image for this blog is intended to capture the essence of The MATropolis, created entirely using MATLAB. Let's step through the reproduction code and build it together.

The graphic is basically a 3D bar plot using random bar heights. To generate the same random numbers used in the image, specify the random number generator algorithm and seed (rng).

rng(20210214,'twister')

The figure size is 560x560 pixels. Instead of specifying the full 1x4 figure position, you can specify the width and height of the figure and then use movegui to ensure that the figure is still on the screen.

fig = figure('units','pixels');

fig.Position(3:4) = [560,560];

movegui(fig)

The custom colormap is a rearrangement of the r-g-b columns of the pink colormap. This is a pretty neat and easy way to generate up to 27 different colormaps just by indexing the columns of a single colormap.

cmap = pink();

customColormap = cmap(:,[2,3,1]);

Create the axes and assign the custom colormap. The view angle specifies an oblique line of sight on the axes. The axes color is set to a darker shade chosen from the colormap.

axes('Colormap', customColormap,...

Â Â Â Â 'Color', customColormap(25,:), ...

Â Â Â Â 'View',[164,19],...

Â Â Â Â 'CameraViewAngle', 3.5)

Hold the axes. I often see MATLAB users forget this step and wonder why their axes properties changed. Many of our graphics functions call newplot to set up the figure and axes. Newplot decides whether the axes should be reset or not. To avoid resetting the axes properties when a graphics object is added, specify hold on. Note, another approach would be to set the axes properties after adding the bar plot which does not require holding the axes.

hold on

Generate a 15x15 3D bar plot with random bar heights from a gamma distribution. The gamma distribution was chosen so that tall buildings are less frequent and stand out more than other buildings. randg requires the Statistics and Machine Learning Toolbox. An easy alternative to randg is to pull positive random values from a normal distribution: h=abs(randn(15))*2.

h=randg(1,15);

b=bar3(h);

axis equal

By default, the 3D bar colors vary along the x-axis. We want color to vary along the z-axis so that darker shades are at the bottom and lighter shades are at the top of the bars. The bars are constructed of surface objects with one surface object for each row of bars along the x-axis. For each surface object, set its color data according to the buildings' height and interpolate the face color to create a smooth transition from bottom to top of each building.

for i=1:numel(b)

Â Â set(b(i),'CData',b(i).ZData,'FaceColor','interp')

end

hold off

Tweak it, append it, or share your own representation of the MATropolis with us! Change the colormap, camera viewing angle, add a setting sun, or use a different distribution of building heights. Maybe you want to add windows, create a rooftop view, or add a bat signal! Thanks for making it this far and for being a part of the community!

]]>