Many years ago (around 1984) I got my very first Motorola VME-10 development system. It ran a real-time OS named VersaDOS and could multi-task if you wanted to hook several CRT terminals up to it. It was based on the 68010 CPU and had a fairly nice graphics system. I used it for all my program development and had it feeding source code into our Computer Automation LSI-2 Naked Mini to compile programs because the CA only had floppy drives and an OS that didn't support folders. Supporting multiple jobs for many customers on a flat file system is not fun and trying to keep all the source code on a flat directory was impossible. The VME-10 had a multi-user file system to allow separate accounts and could feed the CA to compile code while I worked on other jobs. Of course, this was all due to some custom programs I came up with to automate the systems.
One day I decided to have some fun so I wrote some line and circle drawing routines that directly addressed the pixel locations as Motorola didn't provide any primitive graphic operations. This line drawing method I came up with is similar to Bresenham's line algorithm except it uses only integer math and works for lines in all directions. It is based on the equation for a line, converted to an integer differential analyzer. Such line drawing methods are useful if you need to quickly process pixels along an arbitrary line in an image.
Consider the equation of a line for pixels (x, y) from (0, 0) to (10, 1). We can calculate y from x by using y = y1 + x*dy/dx for x from x1 = 0 to x2 = 10 where dy = 1 and dx = 10. Because pixels are expressed as integer locations, we need to use trunc(y + 0.5) to get the line to increment at the center. This works only for dx>=dy and dx>=0 and dy>=0. In C we would write:
const int x1=0;
const int x2=10;
const int y1=0;
const int y2=1;
const double dx=x2-x1;
const double dy=y2-y1;
double y;
int iy;
for (int ix=x1;ix<=x2;ix++)
{
y=y1+ix*dy/dx;
iy=trunc(y+0.5);
plot(ix,iy);
}
This can be converted to simple addition in this way:
const double slope=dy/dx;
ix=x1;
y=y1+0.5;
while (ix<=x2)
{
plot(ix,trunc(y));
y=y+slope;
ix++;
}
Now the trick is to convert this so we don't need to work with y. We do this by introducing a ysum value. Instead of working directly with y, we only increment iy when we accumulate each slope total of 1:
const double s=dy/dx;
ix=x1;
iy=y1;
double ysum=0.5;
while (ix<=x2)
{
plot(ix,iy);
ysum=ysum+s;
if (ysum>=1.0)
{
ysum=ysum-1.0;
iy++;
}
ix++;
}
This still isn't all integer mode, so we see that we can use all integers if we multiply everything related to ysum by dx:
const int idx=x2-x1;
const int idy=y2-y1;
ix=x1;
iy=y1;
int ysum=idx/2;
while (ix<=x2)
{
plot(ix,iy);
ysum=ysum+idy;
if (ysum>=idx)
{
ysum=ysum-idx;
iy++;
}
ix++;
}
Work this out for all line directions and add steering logic and you have my method. For another way of doing this see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm.
Here is a modern C++ class definition for a parametric line with the drawing methods:
//---------------------------------------------------------------------------
#ifndef LineDrawH
#define LineDrawH
//---------------------------------------------------------------------------
// parametric line class
class TVT_ParaLine
{
private:
bool xInc,pdx,pdy;
int ix1,iy1,ix2,iy2,idx,idy,adx,ady,IncSum;
protected:
public:
// this constructs an integer based line from (X1, Y1) to (X2, Y2)
TVT_ParaLine(int X1, int Y1, int X2, int Y2);
/*
fast interpolation of pixels along line
example use:
SetFirst(X,Y);
do {
(process pixel X,Y here)
} while (NextXY(X,Y));
*/
void SetFirst(int& X, int& Y);
bool NextXY(int& X, int& Y);
};
//---------------------------------------------------------------------------
#endif
Here is the implementation of the class:
//---------------------------------------------------------------------------
#include "LineDraw.h"
#include <math.h>
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TVT_ParaLine::TVT_ParaLine(int X1, int Y1, int X2, int Y2)
{
ix1=X1;
iy1=Y1;
ix2=X2;
iy2=Y2;
idx=X2-X1;
idy=Y2-Y1;
adx=abs(idx);
ady=abs(idy);
xInc=adx>=ady;
pdx=idx>=0;
pdy=idy>=0;
}
//---------------------------------------------------------------------------
void TVT_ParaLine::SetFirst(int& X, int& Y)
{
if (xInc)
{
IncSum=adx/2;
}
else
{
IncSum=ady/2;
}
X=ix1;
Y=iy1;
}
//---------------------------------------------------------------------------
bool TVT_ParaLine::NextXY(int& X, int& Y)
{
bool done;
if (xInc)
{
if ((IncSum+=ady)>=adx)
{
if (pdy) Y++; else Y--;
IncSum-=adx;
}
if (pdx)
{
X++;
done=X>ix2;
}
else
{
X--;
done=X<ix2;
}
}
else
{
if ((IncSum+=adx)>=ady)
{
if (pdx) X++; else X--;
IncSum-=ady;
}
if (pdy)
{
Y++;
done=Y>iy2;
}
else
{
Y--;
done=Y<iy2;
}
}
return !done;
}
//---------------------------------------------------------------------------