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;
        }
    }
}