Michener’s Algorithm An efficient scheme for drawing circles (and filling circular disks)

advertisement
Michener’s Algorithm
An efficient scheme for drawing
circles (and filling circular disks)
on a raster graphics display
Scan conversion
•
•
•
•
•
•
Geometric objects possess implicit parameters
Example: A circle has a ‘center’ and a ‘radius’
Its equation is: (x – xc)2 + (y - yc)2 = R2
x and y are from the continuum of real numbers
CRT display is a discrete 2D array of ‘pixels’
So drawing a circle requires a conversion, from
something continuous into something discrete
• In computer graphics it’s called scan conversion
• Imperfections: unavoidable, but to be minimized
Graphics Animations
• Fast drawing is essential for animations
• Some guidelines for speed:
– eliminate all redundant computations
– prefer integer arithmetic to floating-point
– prefer add and subtract to multiply or divide
• Famous example: ‘Michener Algorithm’
• We can use it later with our ‘pong’ game
Eight-fold symmetry
(-x, y)
(x, y)
(-y, x)
(y, x)
(-y, -x)
(y, -x)
(-x, -y)
(x, -y)
Compute only one octant
Subroutine: draw_octant_points
Arguments: int x, y, xcent, ycent, color;
draw_pixel( xcent + x, ycent + y, color );
draw_pixel( xcent + y, ycent + x, color );
draw_pixel( xcent - x, ycent + y, color );
draw_pixel( xcent - y, ycent + x, color );
draw_pixel( xcent + x, ycent - y, color );
draw_pixel( xcent + y, ycent - x, color );
draw_pixel( xcent - x, ycent - y, color );
draw_pixel( xcent - y, ycent - x, color );
The “best” pixel to draw?
Blue pixel: too far from center
(E>0)
Red pixel: too near the center
(E<0)
Error-term: E = (x2 + y2) – R2
Decision at n-th stage
Pn-1
An ?
yn-1
Bn ?
xn-1
xn
Algorithm: compute sum = error( An ) + error( Bn );
If ( sum < 0 ) choose A n; otherwise choose B n.
Formula: sum of error-terms
•
•
•
•
Assume circle has radius R, center (0,0)
error( An) = (xn-1+1)2 + (yn-1)2 – R2
error( Bn) = (xn-1+1)2 + (yn-1 – 1)2 – R2
sumn = 2(xn-1+1)2 + 2(yn-1)2 –2(yn-1) + 12R2
• Now, how is sumn different from sumn+1:
– If An is chosen at the n-th step?
– If Bn is chosen at the n-th step?
Difference Equation
• Observe that: sumn+1 – sumn =
4xn-1 + 6 + 2(yn2 – yn-12) – 2(yn – yn-1)
• When An is selected at n-th stage:
– we will have yn = yn-1
– thus: sumn+1 = sumn + 4xn-1 + 6
• When Bn is selected at n-th stage:
– we will have yn = yn-1 - 1
– thus: sumn+1 = sumn + 4(xn-1 – yn-1) + 10
Algorithm initialization
• We start with the point P0 = (x0,y0), where
x0 = 0 and y0 = R
• In this case: A1 = (1, R) and B1 = (1, R-1)
• So the initial ‘sum-of-errors’ term will be:
sum1 = error(A1) + error(B1)
= [(12 + R2) – R2] + [(12 + R2–2R+1) – R2]
= 3 – 2R
Michener’s Algorithm
int x = 0, y = R, sum = 3 – 2*R;
while ( x <= y )
{
draw_octant_points( x, y, xc, yc, color );
if ( sum < 0 ) sum += 4*x + 6;
else { sum += 4*(x - y) + 10; --y; }
++x;
}
Reference
• Francis S. Hill, jr., “Computer Graphics,”
Macmillan (1990), pp. 433-435.
• NOTE: Michener’s circle-drawing method
owes its inspiration to a famous algorithm
for efficient line-drawing, devised in 1965
by J. E. Bresenham (see the IBM Systems
Journal, vol 4, pp. 305-311).
Circle ‘fill’ also exploits symmetry
(-x, y)
(x, y)
(-y, x)
(y, x)
(-y, -x)
(y, -x)
(-x, -y)
(x, -y)
Subroutine: draw_segments
Arguments: int x, y, xc, yc, color;
draw_horiz( xc – x, xc + x, yc + y, color );
draw_horiz( xc – x, xc + x, yc - y, color );
draw_horiz( xc – y, xc + y, yc + x, color );
draw_horiz( xc – y, xc + y, yc - x, color );
draw_horiz( int xlo, xhi, y, color );
Clipping to screen boundaries:
If (( y < ymin )||( y > ymax )) return;
if ( xlo < xmin ) xlo = xmin;
if ( xhi > xmax ) xhi = xmax;
Drawing the horizontal segment:
for (x = xlo; x <= xhi; x++)
draw_pixel( x, y, color );
Demo-program
•
•
•
•
•
Try the ‘michener.cpp’ demo
It uses VESA graphics-mode 0x4101
Screen resolution is 640x480
Color depth is 8 bits-per-pixel (8bpp)
SVGA’s Linear Frame Buffer is enabled
In-Class Exercise
• Modify the ‘michener.cpp’ demo:
– Use the standard ‘rand()’ function
– Draw lots of color-filled circles
– Stop if user hits <ESCAPE> key
• NOTE: For the latter feature, we need to
discuss setting up the terminal keyboard
so it uses a ‘non-canonical’ input-mode
The ‘tty’ interface
•
•
•
•
‘tty’ is an acronyn for ‘TeleTYpe’ terminal
Such devices have a keyboard and screen
Behavior emulates technology from 1950s
Usually a tty operates in ‘canonical’ mode:
– Each user-keystroke is ‘echoed’ to screen
– Some editing is allowed (e.g., backspace)
– The keyboard-input is internally buffered
– The <ENTER>-key signals an ‘end-of-line’
– Programs receive input one-line-at-a-time
‘tty’ customization
• Sometimes canonical mode isn’t suitable
(an example: animated computer games)
• The terminal’s behavior can be modified!
• UNIX provides a convenient interface:
– #include <termios.h>
– struct termios tty;
– int tcgetattr( int fd, struct termios *tty );
– int tcsetattr( int fd, int flag, struct termios *tty );
How does the ‘tty’ work?
SOFTWARE
application
User space
tty_driver
c_lflag
Kernel space
input handling
c_iflag
c_cc
output handling
c_oflag
terminal_driver
c_cflag
HARDWARE
TeleTYpe display device
struct tty {
c_iflag;
c_oflag;
c_cflag;
c_lflag;
c_line;
c_cc[ ];
};
The ‘c_lflag’ field
•
•
•
•
•
•
This field is just an array of flag bits
Individual bits have symbolic names
Names conform to a POSIX standard
Linux names match other UNIX’s names
Though actual symbol values may differ
Your C/C++ program should use:
#include <termios.h>
for portability to other UNIX environments
ICANON and ECHO
• Normally the ‘c_lflag’ field has these set
• They can be cleared using bitwise logic:
tty.c_lflag &= ~ECHO;
// inhibit echo
tty.c_lflag &= ~ICANON; // no buffering
The ‘c_cc[ ]’ array
•
•
•
•
•
‘struct termios’ objects include an array
The array-indices have symbolic names
Symbol-names are standardized in UNIX
Array entries are ‘tty’ operating parameters
Two useful ones for our purposes are:
tty.c_cc[ VMIN ] and tty.c_cc[ VTIME ]
How to setup ‘raw’ terminal-mode
• Step 1: Use ‘tcgetattr()’ to get a copy of the
current tty’s ‘struct termios’ settings
• Step 2: Make a working copy of that object
• Step 3: Modify its flags and control-codes
• Step 4: Use ‘tcsetattr()’ to install changes
• Step 5: Perform desired ‘raw’ mode input
• Step 6: Use ‘tcsetattr()’ to restore the
terminal to its original default settings
‘raw’ mode needs four changes
• tty.c_cc[ VMIN ] = 1;
– so the ‘read()’ function will return as soon as
at least one new input-character is available
• tty.c_cc[ VTIME ] = 0;
– so there will be no time-delay after each new
key pressed until the ‘read()’ function returns
• tty.c_lflag &= ~ECHO;
• tty.c_lflag &= ~ICANON;
// no echoing
// no buffering
Demo program: ‘rawtty.cpp’
•
•
•
•
•
This program may soon prove useful
It shows the keyboard scancode values
It demonstrates ‘noncanonical’ tty mode
It clears the ISIG bit (in ‘c_lflags’ field)
This prevents <CONTROL>-C from being
used to abort the program: the user must
‘quit’ by hitting the <ESCAPE>-key; so
default terminal-settings will get reinstalled
In-class Exercise
• Use the Linux ‘tty’ interface-functions to
reprogram your terminal for ‘raw’ input
• Draw ramdom color-filled circles -- until your
user hits the <ESCAPE> key
• Algorithm:
int
done = 0;
do {
draw_next_circle( x, y, r, color );
int
inch = 0;
read( 0, &inch, 4 );
if ( inch == 0x1B ) done = 1;
} while ( !done );
Download