280 likes | 411 Views
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 = R 2
E N D
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)
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) + 1-2R2 • 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? application User space tty_driver c_lflag Kernel space input handling c_iflag c_cc output handling c_oflag SOFTWARE struct tty { c_iflag; c_oflag; c_cflag; c_lflag; c_line; c_cc[ ]; }; terminal_driver c_cflag HARDWARE TeleTYpe display device
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; // no echoing • tty.c_lflag &= ~ICANON; // 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 );