August 17, 2005
At the risk of nitty gritty detailiosis, can we
discuss the line drawing algorithms, such as the
Bresenham Line-Drawing Algorithm? Geometrically, think of
a line segment as defined by its endpoints. To plot the line
on a grid, or "raster monitor," we need approximate the line by a
sort "staircase." Recall that slope is
Sample the line at discrete positions and
determine the nearest pixel to the line for each sample.
The digital differential analyzer (DDA) scheme
is to sample at unit intervals in one coordinate (say, x)
and then compute the nearest integer to
Here, to be sure the pixels are
connected, we assume that |m| < 1.0, but if not, we simply
reverse the variables and compute
This also assumes we're plotting points from
left to right so that δx = 1. Plotting the other
direction is simply a matter of setting
δx = –1 so that δy = –m. Or, if |m|
> 1.0, take δy = –1 and δx = –1/m.
Here's how you might code this procedure
(adapted from Computer Graphics with OpenGL by Hearn/Baker):
void draw_lineDDA(point firstPt, point lastPt) {
int dx = lastPt.x -
firstPt.x;
int dy = lastPt.y - firstPt.y;
int steps, k;
float xIncrement, yIncrement, x = firstPt.x,
y = firstPt.y;
if(abs (dx) > abs (dy)) steps = abs (dx);
else steps = abs (dy);
xIncrement = float (dx) /
float (steps);
yIncrement = float (dy) /
float (steps);
canvas[LOC(int(x),int(y))]
= black;
for(k = 0; k < steps; ++k) {
x += xIncrement;
y += yIncrement;
canvas[LOC(int(x),int(y))]
= black;
}
}
Alternatively, Bresenham's algorithm determines
which pixel should next be colored by testing the sign of an integer
parameter whose value is proportional to the difference between the
vertical separations of the two pixel positions from the actual line
path. Starting from the left endpoint (x0, y0)
of a segment, step into the next pixel column (or row,
depending on whether the line is steeper than 45 degrees or not) and plot the
pixel whose scan-line y value (or x value, if going the other way) is closest to the line path.
Here's the derivation. It's pretty nifty!
Assuming that (xk, yk)
is the last point in the display, how do we choose (xk+1,
yk+1)? Since the slope is
less than 1, we can limit choices to (1+xk, yk)
and (1+xk, 1+yk) . The
exact y-coordinate is yk+1=
m(xk+1) + b Compare the
absolute value differences
dlower = y
– yk = m(xk+1) +
b – yk.
dupper = (yk+1)
– y = yk+1 – m(xk+1)
– b
An efficient test to determine which of these
is smaller is to compute the difference of differences:
dlower –
dupper = y – yk =
2m(xk+1) + 2b – 2yk –
1
The key to the Bresenham algorithm
is to realize that if we substitute m = δy/δx and
multiply through by δx, then we have the relatively easily
computed quantity pk as an integer decision
parameter:
pk = δx(dlower
– dupper) = 2.δy.xk
– 2.δx.yk + c
in which c = 2.δy.+ δx(2b–1)
is independent of the current pixel position, (xk,
yk) , and, as such, will be eliminated in the
recursive calls for pk. Now if pk
is negative then dlower < dupper
and the pixel yk is closer than 1+yk.
To get the recursive formula for pk+1
is fairly straightforward:
pk+1
= 2.δy.xk+1 –
2.δx.yk+1 + c
so that
pk+1
– pk = 2.δy.xk+1
– 2.δx.yk+1 + c –
( 2.δy.xk – 2.δx.yk
+ c) = 2.δy – 2.δx.(yk+1
– yk)
whence
pk+1
= pk + 2.δy – 2.δx.(yk+1
– yk)
Well, Carlsbad's calling, so I'll just slap up
the code so far and be gone. The next thing is to get this
algorithm implemented and maybe compare:
#include <GL/glut.h>
#include <stdio.h>
#include <cmath>
#include <stdlib.h>
void initGL();
void idle();
void redraw();
void display();
void keyEvent(unsigned char key,int x,int y);
void mouse(int button,int state,int x,int y);
void mouseMotion(int x,int y);
typedef struct {
int x, y;
} point;
typedef struct {
unsigned char r, g, b; } rgb_pixel;
void draw_lineDDA(point p0, point p1);
void fill_triangle(point a, point b, point c);
#define SCREENWIDTH 600
#define SCREENHEIGHT 500
const int screenWidth = SCREENWIDTH;
const int screenHeight = SCREENHEIGHT;
const rgb_pixel bgcolor = {210, 210, 255}; rgb_pixel black = {0, 0, 0};
rgb_pixel red = {255, 0, 0};
rgb_pixel green = {0, 255, 0};
rgb_pixel canvas[SCREENWIDTH * SCREENHEIGHT];
#define LOC(X,Y) ((Y)*screenWidth + (X))
int main(int argc, char **argv)
{
int i;
glutInit(&argc,argv);
initGL();
for(i = 0; i < screenWidth*screenHeight; i++) {
canvas[i] = bgcolor;
}
glutMainLoop();
return 0;
}
void initGL()
{
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize(screenWidth, screenHeight);
glutInitWindowPosition(200,100);
glutCreateWindow("CS 365 Lab 1");
glutDisplayFunc(display);
glutMotionFunc(mouseMotion);
glutIdleFunc(idle);
glutMouseFunc(mouse);
glutKeyboardFunc(keyEvent);
}
void idle()
{
glutPostRedisplay();
}
void redraw()
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glDrawPixels((GLsizei)screenWidth, (GLsizei)screenHeight, GL_RGB,
GL_UNSIGNED_BYTE, canvas);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
redraw();
glutSwapBuffers();
glFlush();
}
void keyEvent(unsigned char key,int x,int y)
{
switch (key) {
case 'Q':
case 'q':
exit(0);
break;
default:
break;
}
glutPostRedisplay();
}
int i = 1;
point triangle[3];
void mouse(int button,int state,int x,int y)
{
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
if(i%3 == 1) {
triangle[0].x = x;
triangle[0].y = screenHeight - y;
canvas[LOC(x,screenHeight - y)] = red;
}
if(i%3 == 2) {
triangle[1].x = x;
triangle[1].y = screenHeight - y;
draw_lineDDA(triangle[0],triangle[1]);
}
if(i%3 == 0) {
triangle[2].x = x;
triangle[2].y = screenHeight - y;
draw_lineDDA(triangle[1],triangle[2]);
draw_lineDDA(triangle[2],triangle[0]);
}
++i;
printf("i = %d\n", i);
}
if(button == GLUT_LEFT_BUTTON && state == GLUT_UP)
{
}
if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
{
}
if(button == GLUT_RIGHT_BUTTON && state == GLUT_UP)
{
}
}
void mouseMotion(int x,int y)
{
if ((x < 0) | (x >= screenWidth) | (y < 0) | (y >= screenHeight)) return;
}
void draw_lineDDA(point firstPt, point lastPt) {
int dx = lastPt.x - firstPt.x;
int dy = lastPt.y - firstPt.y;
int steps, k;
float xIncrement, yIncrement, x = firstPt.x, y = firstPt.y;
if(abs (dx) > abs (dy)) steps = abs (dx);
else steps = abs (dy);
xIncrement = float (dx) / float (steps);
yIncrement = float (dy) / float (steps);
canvas[LOC(int(x),int(y))] = black;
for(k = 0; k < steps; ++k) {
x += xIncrement;
y += yIncrement;
canvas[LOC(int(x),int(y))] = black;
}
}
void draw_lineBres(point firstPt, point lastPt) { ...to be finished...
Aedh Wishes for the
Clothes of Heaven - WBYeats
Had I the heavens' embroidered
cloths,
Enwrought with golden and silver light,
The blue and the dim and the dark cloths
Of night and light and the half light,
I would spread the cloths under your feet:
But I, being poor, have only my dreams;
I have spread my dreams under your feet;
Tread softly because you tread on my dreams.
|