I need an algorithm which can be (a bit) slower than the Bresenham line drawing algorithm but has to be a lot more exact. With \'exact\' I mean: every touched pixel should b
If your line is thin and pixels are rectangular (square):
then consider using of voxel grid traversal algorithms, for example, see article "Fast Voxel Traversal Algorithm..." by Woo and Amanatides.
Practical implementation (in grid traversal section)
Answer to comment:
Proper initialization for X-coordinate variables (the same for Y)
DX = X2 - X1
tDeltaX = GridCellWidth / DX
tMaxX = tDeltaX * (1.0 - Frac(X1 / GridCellWidth))
//Frac if fractional part of float, for example, Frac(1.3) = 0.3
example in my answer here
If you need just constant color (not interpolated by used area of pixel) then use DDA:
void line_DDA_subpixel(int x0,int y0,int x1,int y1,int col) // DDA subpixel -> thick
{
int kx,ky,c,i,xx,yy,dx,dy;
x1-=x0; kx=0; if (x1>0) kx=+1; if (x1<0) { kx=-1; x1=-x1; } x1++;
y1-=y0; ky=0; if (y1>0) ky=+1; if (y1<0) { ky=-1; y1=-y1; } y1++;
if (x1>=y1)
for (c=x1,i=0;i<x1;i++,x0+=kx)
{
pnt(x0,y0,col); // this is normal pixel the two below are subpixels
c-=y1; if (c<=0) { if (i!=x1-1) pnt(x0+kx,y0,col); c+=x1; y0+=ky; if (i!=x1-1) pnt(x0,y0,col); }
}
else
for (c=y1,i=0;i<y1;i++,y0+=ky)
{
pnt(x0,y0,col); // this is normal pixel the two below are subpixels
c-=x1; if (c<=0) { if (i!=y1-1) pnt(x0,y0+ky,col); c+=y1; x0+=kx; if (i!=y1-1) pnt(x0,y0,col); }
}
}
where:
void pnt(int x,int y,int col);
is routine that rasterize pixel (x,y)
with color col The source is in C++
I think it is strait forward but anyway
DDA use parametric line equation y=k*x+q
or x=ky+q
dependent on the difference (if is bigger x
or y
difference so there are no holes). The k
is dy/dx
or dx/dy
and the whole division is reduced to substraction+addition inside loop (last line of each loop). This can be easily modified to any number of dimensions (I usually use 7D or more with this). On modern machines is the speed sometimes better then Bresenham (depends on the Platform and usage).
This is how it looks like compared to simple DDA
[edit2] double coordinates // originally [edit1]
OK here is new code:
void line_DDA_subpixel1(double x0,double y0,double x1,double y1,int col) // DDA subpixel -> thick
{
int i,n,x,y,xx,yy;
// prepare data n-pixels,x1,y1 is line dx,dy step per pixel
x1-=x0; i=ceil(fabs(x1));
y1-=y0; n=ceil(fabs(y1));
if (n<i) n=i; if (!n) n=1;
x1/=double(n);
y1/=double(n); n++;
// rasterize DDA line
for (xx=x0,yy=y0,i=0;i<=n;i++,x0+=x1,y0+=y1)
{
// direct pixel
pnt(x,y,col);
// subpixels on change in both axises
x=x0; y=y0;
if ((i<n)&&(x!=xx)&&(y!=yy)) { pnt(xx,y,col); pnt(x,yy,col); }
xx=x; yy=y;
}
}
And this is how it looks like:
Angle should be in double
precision now but pnt(x,y,col) is still on integers !!!
[edit3] pixel grid crossing
void DDAf_line_subpixel(float x0,float y0,float x1,float y1,int col) // DDA subpixel -> thick
{
int i,n; float a,a0,a1,aa,b,d;
// end-points
pnt(x0,y0,col);
pnt(x1,y1,col);
// x-axis pixel cross
a0=1; a1=0; n=0;
if (x0<x1) { a0=ceil(x0); a1=floor(x1); d=(y1-y0)/(x1-x0); a=a0; b=y0+(a0-x0)*d; n=fabs(a1-a0); } else
if (x0>x1) { a0=ceil(x1); a1=floor(x0); d=(y1-y0)/(x1-x0); a=a0; b=y1+(a0-x1)*d; n=fabs(a1-a0); }
if (a0<=a1) for (aa=a,i=0;i<=n;i++,aa=a,a++,b+=d) { pnt(aa,b,col); pnt( a,b,col); }
// y-axis pixel cross
a0=1; a1=0; n=0;
if (y0<y1) { a0=ceil(y0); a1=floor(y1); d=(x1-x0)/(y1-y0); a=a0; b=x0+(a0-y0)*d; n=fabs(a1-a0); } else
if (y0>y1) { a0=ceil(y1); a1=floor(y0); d=(x1-x0)/(y1-y0); a=a0; b=x1+(a0-y1)*d; n=fabs(a1-a0); }
if (a0<=a1) for (aa=a,i=0;i<=n;i++,aa=a,a++,b+=d) { pnt(b,aa,col); pnt(b, a,col); }
}
Finally had some time for this so I tweaked DDA a little but id lead to many if
s so I change rasterization quite a bit. Now all pixel grid crossing (intersections) are computed and then for each the right sub-pixel is added. This is how it looks like (no wrong sub-pixels):
For each x
or y
grid lines is the first cross point computed (a,b)
and step
is in one axis 1
pixel and in second the rest according to dy/dx
or dx/dy
. After this the for loop fill the sub-pixels ...