Alan Murphy's thick line algorithm and implementation
Based on the Alan Murphy’s modified Bresenham line drawing algorithm[/url] and gnu hp2xx source codes I have prepared simple routine for drawing pixel line any weight.
Main routine is named murphy_wideline and draws a line from (from_x,from_y) point to (to_x,to_y) point. Color and line thickness is described by global variables currStrokeWeight and currFillColor. Pixel setting function has signature: pixel(int x, int y, color_t c).
/* global */
int currStrokeWeight;
color_t currFillColor;
void pixel(int x, int y, color_t c);
/* end */
#define my_lrint(a) ((int)((a)+0.5))
#if defined __STRICT_ANSI__
# define HYPOT(x,y) sqrtf((x)*(x)+(y)*(y))
#else
# define HYPOT(x,y) hypotf(x,y)
#endif
struct murphy_t {
int u,v; /* delta x , delta y */
int ku,kt,kv,kd,ks; /* loop constants */
int tk;
bool oct2;
bool quad4;
};
void murphy_wideline(int from_x, int from_y, int to_x, int to_y) {
murphy_t murphy;
int pt_x, pt_y;
float offset = currStrokeWeight / 2.;
murphy.u=to_x-from_x; /*delta x*/
murphy.v=to_y-from_y; /*delta y*/
if (murphy.u < 0) { /* swap to make sure we are in quadrants 1 or 4 */
stl::swap(from_x, to_x);
stl::swap(from_y, to_y);
murphy.u *= -1;
murphy.v *= -1;
}
if (murphy.v < 0) { /* swap to 1st quadrant and flag */
murphy.v *= -1;
murphy.quad4 = true;
} else
murphy.quad4 = false;
if (murphy.v > murphy.u) { /* swap things if in 2 octant */
stl::swap(murphy.u, murphy.v);
murphy.oct2 = true;
} else
murphy.oct2 = false;
murphy.ku=murphy.u+murphy.u; /*change in l for square shift*/
murphy.kv=murphy.v+murphy.v; /*change in d for square shift*/
murphy.kd=murphy.kv-murphy.ku; /*change in d for diagonal shift*/
murphy.ks=murphy.kv+murphy.ku; /*change in l for diagonal shift*/
murphy.kt=murphy.u-murphy.kv; /*diag/square decision threshold*/
int d0=0,d1=0;
float ang = atanf((float)murphy.v/(float)murphy.u); /* calc new initial point - offset both sides of ideal */
if (!murphy.oct2) {
pt_x = from_x + my_lrint(offset * sinf(ang));
if (!murphy.quad4)
pt_y = from_y - my_lrint(offset * cosf(ang));
else
pt_y = from_y + my_lrint(offset * cosf(ang));
} else {
pt_x = from_x - my_lrint(offset * cosf(ang));
if (!murphy.quad4)
pt_y = from_y + my_lrint(offset * sinf(ang));
else
pt_y = from_y - my_lrint(offset * sinf(ang));
}
murphy.tk=4.*HYPOT(pt_x-from_x, pt_y-from_y)*normconstant(murphy.u,murphy.v); /*used here for constant thickness line*/
int p=0; /*outer loop counter*/
while(p<=murphy.u) { /*outer loop, stepping along line*/
murphy_perpendicular(murphy,pt_x,pt_y,d0,d1);
if (d0<murphy.kt) { /*square move*/
if (murphy.oct2 == 0) {
if (murphy.quad4 == 0)
++pt_y;
else
--pt_y;
} else
++pt_x;
/*++pt_x;*/ /*move M0*/
} else { /*diagonal move*/
d0-=murphy.ku;
if (d1<murphy.kt) { /*normal start*/
++pt_x;
++pt_y; /*move M1*/
d1+=murphy.kv;
} else { /*double square move, need extra perpendicular line*/
++pt_y; /*move M2*/
d1+=murphy.kd;
murphy_perpendicular(murphy,pt_x,pt_y,d0,d1); /*extra perpendicular*/
++pt_x; /*move m0*/
}
}
d0+=murphy.kv;
++p;
}
}
/* implements Figure 5B */
void murphy_paraline(murphy_t &murphy, int pt_x, int pt_y, int d1) {
d1 = -d1;
int p; /* pel counter, p=along line */
for (p = 0; p <= murphy.u; p++) { /* test for end of parallel line */
pixels(pt_x, pt_y, currFillColor);
if (d1 <= murphy.kt) { /* square move */
if (!murphy.oct2) {
pt_x++;
} else {
if (!murphy.quad4)
pt_y++;
else
pt_y--;
}
d1 += murphy.kv;
} else { /* diagonal move */
pt_x++;
if (!murphy.quad4)
pt_y++;
else
pt_y--;
d1 += murphy.kd;
}
}
}
inline float normconstant(float fu, float fv) {
const int choice=2; /*change this to suit taste/compute power etc.*/
const int u=fabs(fu), v=fabs(fv);
switch (choice) {
case 1: return (u+v>>2); /*12% thickness error - uses add and shift only*/
case 2: if ((v+v+v)>u) /*2.7% thickness error, uses compare, add and shift only*/
return (u-(u>>3)+(v>>1));
else
return (u+(v>>3));
case 3: return HYPOT(u,v); /*ideal*/
}
return 0;
}
void murphy_perpendicular(murphy_t &murphy, int pt_x, int pt_y, int d0, int d1) { /*Implements Figure 4B*/
d0=-d0;
for( int q=0; d0<=murphy.tk; ++q ) { /*inner loop counter*/
pixels(pt_x, pt_y, currStrokeColor);
if (d1<murphy.kt) { /*square move MS*/
++pt_y;
d1+=murphy.kv;
d0+=murphy.ku;
} else { /*diagonal move MD*/
--pt_x;
++pt_y;
d1+=murphy.kd;
d0+=murphy.ks;
}
}
}
Comments