Reduction (recursion theory): Difference between revisions
en>Bomazi Disambiguated: recursive → recursive set using Dab solver |
en>Addbot m Bot: Migrating 1 interwiki links, now provided by Wikidata on d:q7306360 |
||
Line 1: | Line 1: | ||
The | In [[computer graphics]], the '''midpoint circle algorithm''' is an algorithm used to determine the points needed for drawing a circle. The algorithm is a variant of [[Bresenham's line algorithm]], and is thus sometimes known as '''Bresenham's circle algorithm''', although not actually invented by [[Jack E. Bresenham]]. The algorithm can be generalized to [[conic section]]s.<ref name="HearnBaker1994">{{cite book|author1=Donald Hearn|author2=M. Pauline Baker|title=Computer graphics|url=http://books.google.com/books?id=WJiYQgAACAAJ|publisher=Prentice-Hall|isbn=978-0-13-161530-4}}</ref> | ||
[[File:Bresenham circle.svg|right|320px|thumb|Rasterisation of a circle by the Bresenham algorithm]] | |||
The algorithm is related to work by Pitteway<ref>Pitteway, M.L.V., "Algorithm for Drawing Ellipses or Hyperbolae with a Digital Plotter", Computer J., 10(3) November 1967, pp 282-289</ref> and Van Aken.<ref>Van Aken, J.R., "An Efficient Ellipse Drawing Algorithm", CG&A, 4(9), September 1984, pp 24-35</ref> | |||
==Algorithm== | |||
{{Confusing|date=February 2009}} | |||
This algorithm starts with the [[circle]] equation <math>x^2 + y^2 = r^2</math>. For simplicity, assume the center of the circle is at <math>(0,0)</math>. We consider first only the first octant and draw a curve which starts at point <math>(r,0)</math> and proceeds counterclockwise, reaching the angle of 45. | |||
The "fast" direction here (the [[Basis_(linear_algebra)|basis vector]] with the greater increase in value) is the <math>y</math> direction. The algorithm always takes a step in the positive <math>y</math> direction (upwards), and occasionally takes a step in the "slow" direction (the negative <math>x</math> direction). | |||
From the circle equation we obtain the transformed equation <math>x^2 + y^2 - r^2 = 0</math>, where <math>r^2</math> is computed only a single time during initialization. | |||
Let the points on the circle be a sequence of coordinates of the vector to the point (in the usual basis). Points are numbered according to the order in which they are drawn, with <math>n=1</math> assigned to the first point <math>(r,0)</math>. | |||
For each point, the following holds: | |||
:<math>\begin{align} x_n^2 + y_n^2 = r^2 \end{align}</math> | |||
This can be rearranged as follows: | |||
:<math>\begin{align} x_n^2 = r^2 - y_n^2 \end{align}</math> | |||
And likewise for the next point: | |||
:<math>\begin{align} x_{n+1}^2 = r^2 - y_{n+1}^2 \end{align}</math> | |||
In general, it is true that: | |||
:<math>\begin{align} y_{n+1}^2 &= (y_n + 1)^2 \\ &= y_n^2 + 2y_n + 1 \end{align}</math> | |||
:<math>\begin{align} x_{n+1}^2 = r^2 - y_n^2 - 2y_n - 1 \end{align}</math> | |||
So we refashion our next-point-equation into a recursive one by substituting <math>x_n^2 = r^2 - y_n^2</math>: | |||
:<math>\begin{align} x_{n+1}^2 = x_n^2 - 2y_n - 1 \end{align}</math> | |||
Because of the continuity of a circle and because the maxima along both axes is the same, we know we will not be skipping x points as we advance in the sequence. Usually we will stay on the same x coordinate, and sometimes advance by one. | |||
The resulting co-ordinate is then translated by adding midpoint coordinates. These frequent integer additions do not limit the [[performance tuning|performance]] much, as we can spare those square (root) computations in the inner loop in turn. Again, the zero in the transformed circle equation is replaced by the error term. | |||
The initialization of the error term is derived from an offset of ½ pixel at the start. Until the intersection with the perpendicular line, this leads to an accumulated value of <math>r</math> in the error term, so that this value is used for initialization. | |||
The frequent computations of [[square (algebra)|square]]s in the circle equation, [[Trigonometry|trigonometric]] expressions and [[square root]]s can again be avoided by dissolving everything into single steps and using recursive computation of the quadratic terms from the preceding iterations. | |||
===Variant with Integer-Based Arithmetic=== | |||
Just as with [[Bresenham's line algorithm]], this algorithm can be optimized for integer-based math. Because of symmetry, if an algorithm can be found that only computes the pixels for one octant, the pixels can be reflected to get the whole circle. | |||
We start by defining the radius error as the difference between the exact representation of the circle and the center point of each pixel (or any other arbitrary mathematical point on the pixel, so long as it's consistent across all pixels). For any pixel with a center at <math>(x_i, y_i)</math>, we define the radius error to be: | |||
:<math>RE(x_i,y_i) = \left\vert x_i^2 + y_i^2 - r^2 \right\vert</math> | |||
For clarity, we derive this formula for a circle at the origin, but the algorithm can be modified for any location. We will want to start with the point <math>(r,0)</math> on the positive X-axis. Because the radius will be a whole number of pixels, we can see that the radius error will be zero: | |||
:<math>RE(x_i,y_i) = \left\vert r^2 + 0^2 - r^2 \right\vert = 0</math> | |||
Because we are starting in the first CCW positive octant, we will step in the direction with the greatest "travel", the Y direction, so we can say that <math> y_{i+1} = y_i + 1</math>. Also, because we are only concerned with this octant only, we know that the X values only have 2 options: to stay the same as the previous iteration, or decrease by 1. We can create a decision variable that determines if the following is true: | |||
:<math>RE(x_i-1, y_i+1) < RE(x_i,y_i+1)</math> | |||
If this inequality holds, we plot <math>(x_i-1, y_i+1)</math>; if not, then we plot <math>(x_i,y_i+1)</math>. So how do we determine if this inequality holds? We can start with our definition of radius error: | |||
:<math> | |||
\begin{array}{lcl} | |||
RE(x_i-1, y_i+1) & < & RE(x_i,y_i+1) \\ | |||
\left\vert (x_i-1)^2 + (y_i+1)^2 - r^2 \right\vert & < & \left\vert x_i^2 + (y_i+1)^2 - r^2 \right\vert \\ | |||
\left\vert (x_i^2 - 2 x_i + 1) + (y_i^2 + 2 y_i + 1) - r^2 \right\vert & < & \left\vert x_i^2 + (y_i^2 + 2 y_i + 1) - r^2 \right\vert \\ | |||
\end{array} | |||
</math> | |||
The absolute value function doesn't really help us, so let's square both sides, since the square is always positive: | |||
:<math> | |||
\begin{array}{lcl} | |||
\left [ (x_i^2 - 2 x_i + 1) + (y_i^2 + 2 y_i + 1) - r^2 \right ]^2 & < & \left [ x_i^2 + (y_i^2 + 2 y_i + 1) - r^2 \right ]^2 \\ | |||
\left [ (x_i^2 + y_i^2 - r^2 + 2 y_i + 1) + (1 - 2 x_i) \right ]^2 & < & \left [ x_i^2 + y_i^2 - r^2 + 2 y_i + 1 \right ]^2 \\ | |||
\left ( x_i^2 + y_i^2 - r^2 + 2 y_i + 1 \right )^2 + 2 (1 - 2 x_i) (x_i^2 + y_i^2 - r^2 + 2 y_i + 1) + (1 - 2 x_i)^2 & < & \left [ x_i^2 + y_i^2 - r^2 + 2 y_i + 1 \right ]^2 \\ | |||
2 (1 - 2 x_i) (x_i^2 + y_i^2 - r^2 + 2 y_i + 1) + (1 - 2 x_i)^2 & < & 0 \\ | |||
\end{array} | |||
</math> | |||
Since x > 0, the term <math>(1 - 2 x_i) < 0</math>, so dividing we get: | |||
:<math> | |||
\begin{array}{lcl} | |||
2 \left [ (x_i^2 + y_i^2 - r^2) + (2 y_i + 1) \right ] + (1 - 2 x_i) & > & 0 \\ | |||
2 \left [ RE(x_i,y_i) + YChange \right ] + XChange & > & 0 \\ | |||
\end{array} | |||
</math> | |||
Thus, we change our decision criterion from using floating-point operations to simple integer addition, subtraction, and bit shifting (for the multiply by 2 operations). If ''2(RE+YChange)+XChange > 0'', then we decrement our X value. If ''2(RE+YChange)+XChange <= 0'', then we keep the same X value. Again, by reflecting these points in all the octants, we get the full circle. | |||
===Example=== | |||
The following C# integer implementation follows the logic very closely: | |||
<source lang="csharp"> | |||
public static void DrawCircle(int x0, int y0, int radius) | |||
{ | |||
int x = radius, y = 0; | |||
int radiusError = 1-x; | |||
while(x >= y) | |||
{ | |||
DrawPixel(x + x0, y + y0); | |||
DrawPixel(y + x0, x + y0); | |||
DrawPixel(-x + x0, y + y0); | |||
DrawPixel(-y + x0, x + y0); | |||
DrawPixel(-x + x0, -y + y0); | |||
DrawPixel(-y + x0, -x + y0); | |||
DrawPixel(x + x0, -y + y0); | |||
DrawPixel(y + x0, -x + y0); | |||
y++; | |||
if(radiusError<0) | |||
radiusError+=2*y+1; | |||
else | |||
{ | |||
x--; | |||
radiusError+=2*(y-x+1); | |||
} | |||
} | |||
} | |||
</source> | |||
== Drawing Incomplete Octants == | |||
The implementations above always only draw complete octants or circles. To draw only a certain [[arc (geometry)|arc]] from an angle <math>\alpha</math> to an angle <math>\beta</math>, the algorithm needs first to calculate the <math>x</math> and <math>y</math> coordinates of these end points, where it is necessary to resort to trigonometric or square root computations (see [[Methods of computing square roots]]). Then the Bresenham algorithm is run over the complete octant or circle and sets the pixels only if they fall into the wanted interval. After finishing this arc, the algorithm can be ended prematurely. | |||
Note that if the angles are given as [[slope]]s, then no trigonometry or square roots are required: one simply checks that <math>y/x</math> is between the desired slopes. | |||
== Generalizations == | |||
=== Ellipses === | |||
It is possible to generalize the algorithm to handle [[ellipse]]s (of which circles are a special case). These algorithms involve calculating a full quadrant of the ellipse, as opposed to an octant as explained above, since non-circular ellipses lack the x-y symmetry of a circle. | |||
One such algorithm is presented in the paper "A Fast Bresenham Type Algorithm For Drawing Ellipses" by John Kennedy. [ftp://pc.fk0.name/pub/books/programming/bezier-ellipse.pdf] | |||
=== Parabola === | |||
It is also possible to use the same concept to plot a parabola. | |||
==References== | |||
{{reflist}} | |||
==External links== | |||
*[http://members.chello.at/~easyfilter/bresenham.html The Beauty of Bresenham's Algorithm] – A simple implementation to plot lines, circles, ellipses and Bézier curves | |||
*[https://banu.com/blog/7/drawing-circles/ Drawing circles] - An article on drawing circles, that derives from a simple scheme to an efficient one. | |||
*[http://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm Midpoint Circle Algorithm in several programming languages] | |||
[[Category:Geometric algorithms]] | |||
[[Category:Digital geometry]] | |||
[[Category:Articles with example C code]] | |||
[[de:Rasterung von Kreisen#Midpoint-Algorithmus]] |
Latest revision as of 19:34, 16 March 2013
In computer graphics, the midpoint circle algorithm is an algorithm used to determine the points needed for drawing a circle. The algorithm is a variant of Bresenham's line algorithm, and is thus sometimes known as Bresenham's circle algorithm, although not actually invented by Jack E. Bresenham. The algorithm can be generalized to conic sections.[1]
The algorithm is related to work by Pitteway[2] and Van Aken.[3]
Algorithm
I'm Robin and was born on 14 August 1971. My hobbies are Disc golf and Hooping.
My web site - http://www.hostgator1centcoupon.info/
This algorithm starts with the circle equation . For simplicity, assume the center of the circle is at . We consider first only the first octant and draw a curve which starts at point and proceeds counterclockwise, reaching the angle of 45.
The "fast" direction here (the basis vector with the greater increase in value) is the direction. The algorithm always takes a step in the positive direction (upwards), and occasionally takes a step in the "slow" direction (the negative direction).
From the circle equation we obtain the transformed equation , where is computed only a single time during initialization.
Let the points on the circle be a sequence of coordinates of the vector to the point (in the usual basis). Points are numbered according to the order in which they are drawn, with assigned to the first point .
For each point, the following holds:
This can be rearranged as follows:
And likewise for the next point:
In general, it is true that:
So we refashion our next-point-equation into a recursive one by substituting :
Because of the continuity of a circle and because the maxima along both axes is the same, we know we will not be skipping x points as we advance in the sequence. Usually we will stay on the same x coordinate, and sometimes advance by one.
The resulting co-ordinate is then translated by adding midpoint coordinates. These frequent integer additions do not limit the performance much, as we can spare those square (root) computations in the inner loop in turn. Again, the zero in the transformed circle equation is replaced by the error term.
The initialization of the error term is derived from an offset of ½ pixel at the start. Until the intersection with the perpendicular line, this leads to an accumulated value of in the error term, so that this value is used for initialization.
The frequent computations of squares in the circle equation, trigonometric expressions and square roots can again be avoided by dissolving everything into single steps and using recursive computation of the quadratic terms from the preceding iterations.
Variant with Integer-Based Arithmetic
Just as with Bresenham's line algorithm, this algorithm can be optimized for integer-based math. Because of symmetry, if an algorithm can be found that only computes the pixels for one octant, the pixels can be reflected to get the whole circle.
We start by defining the radius error as the difference between the exact representation of the circle and the center point of each pixel (or any other arbitrary mathematical point on the pixel, so long as it's consistent across all pixels). For any pixel with a center at , we define the radius error to be:
For clarity, we derive this formula for a circle at the origin, but the algorithm can be modified for any location. We will want to start with the point on the positive X-axis. Because the radius will be a whole number of pixels, we can see that the radius error will be zero:
Because we are starting in the first CCW positive octant, we will step in the direction with the greatest "travel", the Y direction, so we can say that . Also, because we are only concerned with this octant only, we know that the X values only have 2 options: to stay the same as the previous iteration, or decrease by 1. We can create a decision variable that determines if the following is true:
If this inequality holds, we plot ; if not, then we plot . So how do we determine if this inequality holds? We can start with our definition of radius error:
The absolute value function doesn't really help us, so let's square both sides, since the square is always positive:
Since x > 0, the term , so dividing we get:
Thus, we change our decision criterion from using floating-point operations to simple integer addition, subtraction, and bit shifting (for the multiply by 2 operations). If 2(RE+YChange)+XChange > 0, then we decrement our X value. If 2(RE+YChange)+XChange <= 0, then we keep the same X value. Again, by reflecting these points in all the octants, we get the full circle.
Example
The following C# integer implementation follows the logic very closely:
public static void DrawCircle(int x0, int y0, int radius)
{
int x = radius, y = 0;
int radiusError = 1-x;
while(x >= y)
{
DrawPixel(x + x0, y + y0);
DrawPixel(y + x0, x + y0);
DrawPixel(-x + x0, y + y0);
DrawPixel(-y + x0, x + y0);
DrawPixel(-x + x0, -y + y0);
DrawPixel(-y + x0, -x + y0);
DrawPixel(x + x0, -y + y0);
DrawPixel(y + x0, -x + y0);
y++;
if(radiusError<0)
radiusError+=2*y+1;
else
{
x--;
radiusError+=2*(y-x+1);
}
}
}
Drawing Incomplete Octants
The implementations above always only draw complete octants or circles. To draw only a certain arc from an angle to an angle , the algorithm needs first to calculate the and coordinates of these end points, where it is necessary to resort to trigonometric or square root computations (see Methods of computing square roots). Then the Bresenham algorithm is run over the complete octant or circle and sets the pixels only if they fall into the wanted interval. After finishing this arc, the algorithm can be ended prematurely.
Note that if the angles are given as slopes, then no trigonometry or square roots are required: one simply checks that is between the desired slopes.
Generalizations
Ellipses
It is possible to generalize the algorithm to handle ellipses (of which circles are a special case). These algorithms involve calculating a full quadrant of the ellipse, as opposed to an octant as explained above, since non-circular ellipses lack the x-y symmetry of a circle.
One such algorithm is presented in the paper "A Fast Bresenham Type Algorithm For Drawing Ellipses" by John Kennedy. [1]
Parabola
It is also possible to use the same concept to plot a parabola.
References
43 year old Petroleum Engineer Harry from Deep River, usually spends time with hobbies and interests like renting movies, property developers in singapore new condominium and vehicle racing. Constantly enjoys going to destinations like Camino Real de Tierra Adentro.
External links
- The Beauty of Bresenham's Algorithm – A simple implementation to plot lines, circles, ellipses and Bézier curves
- Drawing circles - An article on drawing circles, that derives from a simple scheme to an efficient one.
- Midpoint Circle Algorithm in several programming languages
de:Rasterung von Kreisen#Midpoint-Algorithmus
- ↑ 20 year-old Real Estate Agent Rusty from Saint-Paul, has hobbies and interests which includes monopoly, property developers in singapore and poker. Will soon undertake a contiki trip that may include going to the Lower Valley of the Omo.
My blog: http://www.primaboinca.com/view_profile.php?userid=5889534 - ↑ Pitteway, M.L.V., "Algorithm for Drawing Ellipses or Hyperbolae with a Digital Plotter", Computer J., 10(3) November 1967, pp 282-289
- ↑ Van Aken, J.R., "An Efficient Ellipse Drawing Algorithm", CG&A, 4(9), September 1984, pp 24-35