Tuesday, January 04, 2011

Drawing a circle in MATLAB (curved rectangle instead of approximating with filled polygon)

Today, someone I know googled for some help drawing a filled circle in MATLAB and asked me what I thought of the code that was found. The code found, which seems to be the conventional way to draw circles in MATLAB according to Internet posters, generates a polygonal approximation of a circle and then fills it. It goes something like this (where you can use linspace(0,2*pi,N+1) and daspect([1,1,1]) (or axis square) instead of two of the commands used here):
xc = 0;
yc = 0;
r = 2;
N = 256;
theta = (0:N)*2*pi/N;
x = r*cos(theta) + xc;
y = r*sin(theta) + yc;
fill(x, y, 'k');
axis equal;
where (xc,yc) is the coordinate of the center of the circle, r is the radius of the circle, and (N-1) is the number of sides of the polygon that is inscribed inside the desired circle (i.e., the polygon's corners intersect the circle). The circle is filled with the color black, which corresponds to the 'k' parameter of the fill command. To quickly get rid of the circle without clearing the figure, change the fill command so its figure handle gets assigned to a variable (e.g., h = fill(x,y,'k');) and you can delete that figure handle later (e.g., delete(h);). I notice that some people precede the fill command with a plot(x,y); command, but that is not necessary; the fill command will construct the closed finite polygon and put a (black) border around it by default, and so there is no need to draw the border ahead of time with plot.

[ Of course, there are methods very similar to the above that use a rotating complex phasor (i.e., a complex exponential from r*exp(i*theta)) and then let plot generate the projected Cartesian plane coordinates from the complex plane. These methods are equivalent but depend on MATLAB to do the trigonometry behind the scenes, which may or may not be faster... ]

What surprises me is that there is a perfectly nice built-in MATLAB command that will draw rectangles, rounded rectangles, circles, and ellipses very quickly essentially without having to approximate them with finite polygons. It is the rectangle command. Some of the people who posted code like the above example apparently know about the rectangle command (because they use it to verify that their circular approximation is sufficiently circular); however, I guess it's not ideal for them because it references the figure to a corner as opposed to its center. Nevertheless, a lot of people who might not be as picky may not know about the rectangle command, and so here's an example similar to the above.
w = 4;
xc = 0;
yc = 0;
rectangle('Curvature',[1 1], ...
          'Position',[xc-w/2 yc-w/2 w w], ...
          'FaceColor','k');
axis equal;
The 'Curvature' property tells MATLAB that the lines connecting the corners of the rectangle should have no straight portions. By decreasing each of the 1's to 0 in the curvature property, the circle looks more like a rounded rectangle with flat sides and rounded corners. The 'Position' property places the bottom-left corner of the rectangle in its first two elements and then sets the width and height of the "rectangle". So here, a circle centered at (xc, yc) is drawn with a radius of w/2. The circle is filled with 'FaceColor'. Again, axis equal; could be replaced with daspect([1,1,1]); or axis square;. Additionally, rectangle can be assigned to a variable (e.g., h = rectangle(...);) so that the rectangle can be easily removed later (e.g., delete(h);) without having to clear the figure. So this example draws a circle, but by playing with the rectangle parameters, it is easy to draw rectangles, rounded rectangles, and ellipses as well.

It is possible that the former method might be faster, but I can't imagine it would be much faster. The latter method seems more elegant and intuitive to me, and I have to imagine that's what the people at Mathworks would advise you to use if you called them up. Having said that, I have not called them up, and so you decide for yourself.

5 comments:

Johnny said...

To make more than one circle, or oval

xc = [2 0];
yc = [2 0];
% If circle only, use line vector
% r= [4 5];
% If oval, use row 1 for x radius, and row 2 for y radius
r = [2 1;1 1];
Color='k';

N = 256; % Number of side
theta = linspace(0,2*pi,N)';
x = cos(theta)*r(1,:) + ones(N,1)*xc;
y = sin(theta)*r(end,:) + ones(N,1)*yc;

hCircle=fill(x, y,Color);

% Removing the legend entry
arrayfun(@(h) set(get(get(h,'Annotation'),'LegendInformation'),'IconDisplayStyle','off'), hCircle);


axis equal;

Ted said...

Likewise, it seems more elegant to just use arrayfun with rectangle rather than a bunch of polygonal approximations. Something like (not tested)...

arrayfun(@(c) rectangle('Curvature',[1 1],'Position',[c.c-c.r 2*c.r 2*c.r]), s );
axis equal;

where you've already defined s as an array of circles with center c and radius r:

s(1).c = [2 2];
s(1).r = 4;
s(2).c = [0 0];
s(2).r = 5;

(which can also be modified for ellipses) That way you guarantee your circles are circular (as opposed to polygonal).

Jack said...

hi Ted,
Can u please help me with code for identifying for the convex components from an image....

Ted said...

Jack -- I'm afraid image analysis is a bit out of my wheelhouse. I would be surprised if MATLAB did not already have a variety of tools available that would assist in that task though; it seems like it would come up very often.

407 said...

Hello! Can you tell me please how can i draw this circle on a picture? Thanks