/* stuff/ditroff/backends/Bspline.c   444   2570      0        7364  3653702062  12214 */
/*
 * Draw a spline for troff back-ends.
 * Same algorithm as the one in draw.c (qv.), but cleaner and uses
 * t_rule() from the rest of the back-end rather than drawing dots.
 * NB. the calling convention is different from the draw.c version.
 */
#include <ctype.h>

#ifndef lint
static char *sccsid = "@(#)Bspline.c	1.1 (UKC) 25/4/86";
#endif  lint

extern int hpos,vpos;

static int eovals = 0;	/* have reached end of vals list when > 0 */

/*
 *	Absolute value of x.
 *	Should be a macro, but dist then blows up with "too complex"
 */
int
abs(x)
register int x;
{
	return((x<0)?-x:x);
}

/*
 *	Distance between (x1,y1) and (x2,y2).
 *	To a first approx., the max of the absolute x or y distances
 */
#define dist(x1,y1,x2,y2)	((abs(x2-x1)>abs(y2-y1))?abs(x2-x1):abs(y2-y1))

/*
 *	Do a rule from (x,y) width w and height h
 */
#define do_rule(x,y,w,h)	{ hpos = (x);vpos = (y); t_rule((w),(h)); }

/*
 *	Draw a Bspline from (hpos,vpos) via pairs of coords (-1 terminated)
 *	taken from input. Width is (nominally) width.
 */
spline(width)
int width;
{
	int x[3], y[3];
	int xp, yp;
	int getval();
	double t1, t2, t3, w;
	register int j, numdots;
	register int lastx,lasty,lastw,lasth;

	/* initialise caches for rasteroptimisation */
	lastx = lasty = -1 ; lastw = lasth = 0;

	/* start with 2 control points at current position */
	x[1] = x[2] = hpos;
	y[1] = y[2] = vpos;

	while(!eovals) {
		/* shuffle values */
		x[0] = x[1] ; y[0] = y[1]; x[1] = x[2]; y[1] = y[2];
		x[2] = getval()+x[1]; y[2] = getval()+y[1];
		if(eovals) {
			/* duplicate final control point */
			x[2] = x[1];
			y[2] = y[1];
		}
		numdots = (dist(x[0],y[0],x[1],y[1])+dist(x[1],y[1],x[2],y[2])) / width;
		for (j = 0; j < numdots; j++) {	/* points within */
			w = (double) j / numdots;
			t1 = 0.5 * w * w;
			w = w - 0.5;
			t2 = 0.75 - w * w;
			w = w - 0.5;
			t3 = 0.5 * w * w;
			xp = t1 * x[2] + t2 * x[1] + t3 * x[0] + 0.5;
			yp = t1 * y[2] + t2 * y[1] + t3 * y[0] + 0.5;
			/*
			 * use a write-behind system to minimise rasteroping
			 */
			if(xp==lastx && yp==lasty)
				/* same place again!! Can't be useful */
				continue;
			if((xp==lastx)&&(width==lastw)) {
				if((yp>=lasty)&&(yp<=(lasty+lasth))) {
					/* new one starts in old one */
					if((yp+width)>(lasty+lasth))
						/* iff it extends out */
						lasth = yp + width - lasty;
				} else if((yp<lasty)&&((yp+width)>=lasty)) {
					/* yp + width falls into held rule */
					lasth += lasty - yp;
					lasty = yp;
				} else goto flushit;	/* give up */
			} else if((yp==lasty)&&(width==lasth)) {
				if((xp>=lastx)&&(xp<=(lastx+lastw))) {
					/* new one starts in old one */
					if((xp+width)>(lastx+lastw))
						/* iff it extends */
						lastw = xp + width - lastx;
				} else if((xp<lastx)&&((xp+width)>=lastx)) {
					/* xp + width falls into held rule */
					lastw += lastx - xp;
					lastx = xp;
				} else goto flushit;	/* give up */
			} else {
flushit:			/* flush pending rule */
				if(lastw && lasth)
					do_rule(lastx,lasty,lastw,lasth);
				lastx = xp ; lasty = yp;
				lastw = width ; lasth = width;
			}
		}
	}
	/* flush any hanging vector */
	if(lastw && lasth)
		do_rule(lastx,lasty,lastw,lasth);

	/* set current position to last control point */
	hpos = x[2] ; vpos = y[2];
}

static char *p;

/*
 *	Return the next integer from the string handed in to drawwig()
 */
int
getval()
{	register int res;

	while(!isdigit(*p) && (*p != '-') && (*p != '+')) {
		if(*p == '\0') {
			eovals++;
			return(-1);
		}
		p++;
	}
	res = atoi(p);
	while(isdigit(*p) || (*p == '-') || (*p == '+')) p++;
	return(res);
}

/*
 *	Draw a wiggly line (B-spline) from (hpos,vpos)
 *	via the (relative) points given (in ASCII) in
 *	the string buffer buf.
 *	Nominal width of line is w.
 */
drawwig(w,buf)
int w;
register char *buf;
{
	p = buf;
	eovals = 0;
	spline(w);
}
/* CODE MAY BE MISSING HERE DUE TO TAPE READ ERRORS. -martin, 28 Oct 2004 */
