Beginning with the equation of a circle:

public void circleSimple(int xCenter, int yCenter, int radius, Color c) { int pix = c.getRGB(); int x, y, r2; r2 = radius * radius; for (x = -radius; x <= radius; x++) { y = (int) (Math.sqrt(r2 - x*x) + 0.5); raster.setPixel(pix, xCenter + x, yCenter + y); raster.setPixel(pix, xCenter + x, yCenter - y); } }And it would result in circles that look like:

The above applet demonstrates thecircleSimple()algorithm. Click and drag the left button to specify the circle's center and a point on its radius. Selecting the right button will clear the drawing. The circle approximation generated by the algorithm is overlaid with an ideal circle for comparison.

As you can see the circles look fine in areas where only one pixel is required for each column, but in areas of the circle where the local slope is greater the one the circle appears discontinuous (where have we seen this before?).

We could take the approach of computing the derivative (i.e. the local slope) of the function at each point and then make a decision whether to step in the x direction or the y direction. But, we will explore a different tact here.

A circle exhibits a great deal of symmetry. We've already exploited this somewhat
by plotting two pixels for each function evaluation; one for each possible sign of the
square-root function. This symmetry was about the x-axis. The reason that a square-root
function brings out this symmetry results from our predilection that the x-axis should be
used as an independent variable in function evaluations while the y-axis is dependent.
Thus, since a function can yield only one value for member of the domain, we are forced
to make a choice between positive and negative square-roots. The net result is that our
simple circle-drawing algorithm exploits *2-way symmetry* about the x-axis.

Obviously, a circle has a great deal more symmetry. Just as every point above an x-axis drawn through a circle's center has a symmetric point an equal distance from, but on the other side of the x-axis, each point also has a symmetric point on the opposite side of a y-axis drawn through the circle's center.

public void circleSym4(int xCenter, int yCenter, int radius, Color c) { int pix = c.getRGB(); int x, y, r2; r2 = radius * radius; raster.setPixel(pix, xCenter, yCenter + radius); raster.setPixel(pix, xCenter, yCenter - radius); for (x = 1; x <= radius; x++) { y = (int) (Math.sqrt(r2 - x*x) + 0.5); raster.setPixel(pix, xCenter + x, yCenter + y); raster.setPixel(pix, xCenter + x, yCenter - y); raster.setPixel(pix, xCenter - x, yCenter + y); raster.setPixel(pix, xCenter - x, yCenter - y); } }This circle-drawing algorithm uses

The above applet demonstrates thecircleSym4()algorithm. Click and drag the left button to specify the circle's center and a point on its radius. Selecting the right button will clear the drawing. The circle approximation generated by the algorithm is overlaid with an ideal circle for comparison.

This algorithm **has all the problems** of our previous algorithm, but, it gives
the same result with **half as many function evaluations**. So much for "making
it work first" before optimizing. But, we're on a roll so let's push this symmetry thing as
far as it will take us.

Notice also that a circle exhibits symmetry about the pair of lines with slopes of one and minus one, as shown below.

The following routine takes advantage of this **8-way symmetry**.

public void circleSym8(int xCenter, int yCenter, int radius, Color c) { int pix = c.getRGB(); int x, y, r2; r2 = radius * radius; raster.setPixel(pix, xCenter, yCenter + radius); raster.setPixel(pix, xCenter, yCenter - radius); raster.setPixel(pix, xCenter + radius, yCenter); raster.setPixel(pix, xCenter - radius, yCenter); y = radius; x = 1; y = (int) (Math.sqrt(r2 - 1) + 0.5); while (x < y) { raster.setPixel(pix, xCenter + x, yCenter + y); raster.setPixel(pix, xCenter + x, yCenter - y); raster.setPixel(pix, xCenter - x, yCenter + y); raster.setPixel(pix, xCenter - x, yCenter - y); raster.setPixel(pix, xCenter + y, yCenter + x); raster.setPixel(pix, xCenter + y, yCenter - x); raster.setPixel(pix, xCenter - y, yCenter + x); raster.setPixel(pix, xCenter - y, yCenter - x); x += 1; y = (int) (Math.sqrt(r2 - x*x) + 0.5); } if (x == y) { raster.setPixel(pix, xCenter + x, yCenter + y); raster.setPixel(pix, xCenter + x, yCenter - y); raster.setPixel(pix, xCenter - x, yCenter + y); raster.setPixel(pix, xCenter - x, yCenter - y); } }So now we get 8 points for every function evaluation, and this routine should be approximately 4-times faster than our initial circle-drawing algorithm. What's going on with the four pixels that are set outside the loop (both at the top and bottom)? Didn't I say that every point determines 7 others?

The above applet demonstrates thecircleSym4()algorithm. Click and drag the left button to specify the circle's center and a point on its radius. Selecting the right button will clear the drawing. The circle approximation generated by the algorithm is overlaid with an ideal circle for comparison.

What has happened here?

It seems suddenly that our circle's appear continuous, and we added no special code to test for the slope. Symmetry has come to our rescue (actually, symmetry is also what saved us on lines... think about it).

So our next objective is to simplify the function evaluation that takes place on each iteration of our circle-drawing algorithm. All those multiplies and square-root evaluations are expensive. We can do better.

One approach is to manipulate of circle equation slightly. First, we translate our coordinate system so that the circle's center is at the origin (the book leaves out this step), giving:

What we'd like to do is to use this discriminating function to maintain our trajectory of drawn
pixels as close as possible to the desired circle. Luckily, we can start with a point on the circle,
*(x _{0}, y_{0}+r)* (or (

Consider what the next value of the discriminating function is in the case that there is no change in y.

Thus when we are inside the circle with a negative discriminant we can incrementally
update the discriminant's value by incrementing by *2x + 1*.

Suppose instead that we find ourselves outside of the circle, in this case the next value of the descriminant should be:

All that remains is to compute the initial value for our discriminating function. Our initial
point, *(0,r)*, was on the circle. The very next point, *(1, r)*, will be outside if we
continue without changing *y* (why?). However, we'd like to adjust our equation so that
we don't make a change in y unless we are more than half way to it's next value. This can be
accomplished by initialing the discriminating function to the value at *y - 1/2*, a point
slightly inside of the circle. This is similar to the initial offset that we added to the DDA line to
avoid rounding at every step.

private final void circlePoints(int cx, int cy, int x, int y, int pix) { int act = Color.red.getRGB(); if (x == 0) { raster.setPixel(act, cx, cy + y); raster.setPixel(pix, cx, cy - y); raster.setPixel(pix, cx + y, cy); raster.setPixel(pix, cx - y, cy); } else if (x == y) { raster.setPixel(act, cx + x, cy + y); raster.setPixel(pix, cx - x, cy + y); raster.setPixel(pix, cx + x, cy - y); raster.setPixel(pix, cx - x, cy - y); } else if (x < y) { raster.setPixel(act, cx + x, cy + y); raster.setPixel(pix, cx - x, cy + y); raster.setPixel(pix, cx + x, cy - y); raster.setPixel(pix, cx - x, cy - y); raster.setPixel(pix, cx + y, cy + x); raster.setPixel(pix, cx - y, cy + x); raster.setPixel(pix, cx + y, cy - x); raster.setPixel(pix, cx - y, cy - x); } } public void circleMidpoint(int xCenter, int yCenter, int radius, Color c) { int pix = c.getRGB(); int x = 0; int y = radius; int p = (5 - radius*4)/4; circlePoints(xCenter, yCenter, x, y, pix); while (x < y) { x++; if (p < 0) { p += 2*x+1; } else { y--; p += 2*(x-y)+1; } circlePoints(xCenter, yCenter, x, y, pix); } }