/* test program for bezier curves */

#include "std_headers.h"
#include "newlib/yfunctions.h"

struct Library			*IntuitionBase,
						*GfxBase;
struct Window			*Window;
struct Screen			*Screen;
struct RastPort			*rp;

USHORT colors[8] = { 0x000,0xFFF,0xE00,0xFE0,0x666,0xABB,0x0E0,0x0A0 };
struct NewScreen NewScreen = { 0,0,320,200, 3, 0,1, NULL, CUSTOMSCREEN, };

struct NewWindow back_ground =
{	0,0,320,200, 0,1, MENUPICK | MOUSEMOVE,
	NOCAREREFRESH | ACTIVATE | SMART_REFRESH | BORDERLESS | REPORTMOUSE,
	NULL,NULL,NULL,NULL,NULL,320,200,320,200,CUSTOMSCREEN,
};

struct IntuiText quit_tx = { 5,1,JAM2,4,1,NULL,(UBYTE *)"Exit" };
struct MenuItem quit_mi = { NULL,0,0,190,10,ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ,0,(APTR)&quit_tx,0,'X' };
struct Menu menu1 = { NULL, 1,0,56,10, MENUENABLED | MIDRAWN, (BYTE *)"File", &quit_mi };

void test(void);

main()
{	if ((IntuitionBase = OpenLibrary("intuition.library",0)) == NULL) LEAVE;
	if ((GfxBase = OpenLibrary("graphics.library",0)) == NULL) LEAVE;
	if ((Screen = OpenScreen(&NewScreen)) == NULL) LEAVE;
	LoadRGB4(&(Screen->ViewPort),colors,elementsof(colors));
	back_ground.Screen = Screen;

	unless (Window = OpenWindow(&back_ground)) LEAVE;
	rp = Window->RPort;
	SetMenuStrip(Window,&menu1);

	SetAPen(rp,1); SetRast(rp,0);

	test();

	for (;;)
	{	struct IntuiMessage *imsg;

		Wait(1 << Window->UserPort->mp_SigBit);

		while (imsg = (struct IntuiMessage *)GetMsg(Window->UserPort))
		{	ULONG		class;
			USHORT		code;

			class = imsg->Class;
			code = imsg->Code;
			ReplyMsg(&imsg->ExecMessage);

			if (class == MENUPICK && code != MENUNULL) LEAVE;
		}
	}
exitit:
	if (Window){ ClearMenuStrip(Window); CloseWindow(Window); }
	if (Screen) CloseScreen(Screen); 
	if (GfxBase) CloseLibrary(GfxBase);
	if (IntuitionBase) CloseLibrary(IntuitionBase);
}

typedef long			Fixed;

	/* conversion routines for fixed-point numbers */

#define FR(x)			((x) + 0x8000 >> 16)	/* convert fixed to integer		*/
#define ff(x)			((long)(x) << 16)		/* convert integer to fixed		*/
#define AVE(a,b) 		(((a) + (b)) / 2)

typedef struct {								/* A 2-D point					*/
	Fixed				x,
						y;
} point;

typedef struct {								/* 3 points make a curve		*/
	point				start,
						control,
						end;
} curve;

	/*	An edgelist is the output of the scan conversion routine. It points
		to a block of memory which is an array of x coordinates. If there are
		more coordinates than will fit in the array, a new edgelist and a
		new array are created, and the 'next' pointer of the current edgelist
		points to the new edgelist.
	*/

typedef struct _edgelist {
	struct _edgelist	*next;					/* pointer to next in chain		*/
	WORD				top,					/* topmost scanline of this list*/
						bottom;					/* bottom scanline of this list */
	UBYTE				left_edge;				/* a left edge or right edge?	*/
	UBYTE				rising;					/* a rising edge				*/
	WORD				*points;				/* next point to fill in		*/
} edgelist;

	/*	This structure defines the memory blocks which are allocated for
		storing the coordinates.
	*/

#define	BLOCKSIZE		1024

struct edge_block {
	struct edge_block	*next;
	UBYTE				point_data[BLOCKSIZE];
};

	/*	This structure contains all the variables used by the path drawer. */
	/*	NOTE: Added start_xpos and start_ypos to keep track of the first
		"pen-adjusted" point we drew at.
	*/

typedef struct {
	struct edge_block 	*first_block,			/* head of block chain			*/
						*current_block;			/* currently filling block		*/

	edgelist			*first_edge,			/* head of edgelist chain		*/
						*current_edge;			/* currently filling edgelist	*/

	WORD				*first_available_point,	/* marks available space in block*/
						*last_available_point;

	Fixed				cx,						/* current pen positions.		*/
						cy;

	WORD				current_xpos,			/* used for tracking edge moves	*/
						current_ypos,
/* new */				start_xpos,				/* original _adjusted_ points	*/
/* new */				start_ypos;

	WORD				pen_width,				/* makes fill shapes thicker	*/
						pen_height;
							
	WORD				pen_offset;				/* used in pen calculations		*/
	UBYTE				error_code;				/* Set if there was an error	*/
	UBYTE				is_hole;				/* TRUE if shape is a 'hole'	*/

	WORD				min_x,					/* limits for drawing			*/
						min_y,
						max_x,
						max_y;
} PathInfo;

	/*	a global PathInfo structure, which will eventually become a parameter... */

PathInfo				testpath;
PathInfo				*thePath = &testpath;

	/*	move and draw, using fixed-point numbers */

#define fmoveto(path,x,y)	{ path->current_xpos = FR(path->cx = x); path->current_ypos = FR(path->cy = y); }
#define flineto(path,x,y)	LineEdge(path->cx, path->cy, x, y)

	/*	test to determine if an array of coordinates is full */

#define EDGELIST_FULL(e)	(thePath->first_available_point >= thePath->last_available_point)

	/*	As we finish an edge, we insert it into the list of edges in sorted order.
		Later the edge drawer will traverse that list in drawing the edges.
	*/

void insert_last_edge(void)
{	
		/*	Look for insertion point, and insert there.
		*/

	if (thePath->current_edge)
	{	edgelist		**s = &thePath->first_edge;

		for (;;)
		{	if ( ((*s) == NULL) ||
				 (*s)->top > thePath->current_edge->top ||
				 ((*s)->top == thePath->current_edge->top &&
				  *(*s)->points > *thePath->current_edge->points) )
				  	break;
			s = &(*s)->next;
		}
		thePath->current_edge->next = *s;
		*s = thePath->current_edge;
	}
}

	/*	Make a new edgelist. Note that there are two basic types of edgelists,
		Those that are drawing upwards (decreasing scanline order) and those
		that are drawing downwards (increasing scanline order). The upwards
		edgelists fill coordinates from the top of the array down, while
		the downward edgelists fill coordinates from the bottom of the array
		up. (If an edgelist switches direction, it is terminated and a new
		edgelist is begun). Eventually things meet in the middle, at which
		point the array is full and a new array and edglist are created.
	*/

edgelist *alloc_edgelist(int upwards)
{
		/* put the now-completed previous edge into the sorted list */

	insert_last_edge();

		/*	The edgelists themselves are actually stored in the buffer
			along with the coordinates. This checks to see if there is
			enough space in the current buffer for an edgelist.
		*/

	if ( thePath->first_available_point + (sizeof *thePath->current_edge / 2) >=
			thePath->last_available_point )
	{	struct edge_block	*new;

			/*	This is the part that actually allocates a new memory block.
				The Path drawer has been designed to allow the option of
				re-using the same blocks for each path drawn, rather than
				allocating and freeing them each time. That's why this
				next line of code.
			*/

		if (thePath->current_block && thePath->current_block->next)
			new = thePath->current_block->next;
		else
		{	unless (new = AllocMem(sizeof (*new),0L)) return NULL;
			new->next = NULL;
		}

			/*	inform the path structure that there's a new block */

		thePath->first_available_point = (WORD *)(new->point_data);
		thePath->last_available_point = (WORD *)(new + 1);
		if (thePath->current_block) thePath->current_block->next = new;
		else thePath->first_block = new;
		thePath->current_block = new;
	}

		/* now, mark the space in the block as used by the edgelist header. */

	thePath->current_edge = (edgelist *)thePath->first_available_point;
	thePath->first_available_point += sizeof (edgelist) / 2;

		/* innitialize the new edgelist structure */

	thePath->current_edge->next = NULL;
	if (upwards)
	{	thePath->current_edge->points = thePath->last_available_point;
		thePath->current_edge->bottom = thePath->current_ypos;
	}
	else
	{	thePath->current_edge->points = thePath->first_available_point;
		thePath->current_edge->top = thePath->current_ypos;
	}
	thePath->current_edge->rising = upwards;
	thePath->current_edge->left_edge = upwards;

		/*	This commented-out statement could eventually be used to
			provide "holes" by reverse the sense of left / right for
			a particular path.
		*/

/*	thePath->current_edge->left_edge = is_hole ? !upwards : upwards; */
	return thePath->current_edge;
}

	/*	This routine clars out the PathInfo structure. If there are any memory
		blocks left over from the last path we drew, it resets them.
	*/

BOOL init_edgelists(void)
{
	if ( (thePath->current_block = thePath->first_block) )
	{	thePath->first_available_point = (WORD *)(thePath->current_block->point_data);
		thePath->last_available_point = (WORD *)(thePath->current_block + 1);
	}
	else
	{	thePath->first_available_point = thePath->last_available_point = NULL;
	}

	thePath->first_edge = NULL;
	thePath->current_edge = NULL;
}

	/*	This routine frees all but the first memory block, unless "all" is
		non-zero, in which case it frees the first one too. The reason for
		this is that it you are drawing a lot of small splines, you may want
		to keep one block around always.
	*/

void flush_edgelists(BOOL all)
{
	thePath->first_available_point = thePath->last_available_point = NULL;
	if (thePath->first_block)
	{	struct edge_block *new;
	
		while (new = thePath->first_block->next)
		{	thePath->first_block->next = new->next;
			FreeMem(new,sizeof *new);
		}
		if (all)
		{	FreeMem(thePath->first_block,sizeof *thePath->first_block);
			thePath->first_block = thePath->current_block = NULL;
		}
	}
}

	/*	This (rather complicated looking) routine adds points to an edgelist.

		If the new point is on the same scanline as the previous point, then
		not very much happens. For each scanline of difference between the
		new point and the old, however, a new coordinate is added. Right now,
		the X coordinates of any newly added points will be the same as the
		X coordinte of the new point. This is mainly for the convenience of
		the higher-level line-drawing routines which may not wish to deal
		with vertical or horizontal lines (which some of them indeed do,
		for speed).
		
		Note that this means that each newly added coordinate represents
		the point on the old, not the new, scan line.
	*/

void add_edge_points(WORD x,WORD y)
{	WORD				old_pen_offset = thePath->pen_offset;

		/* note that the ordering of the following operations is important */

		/* adjust y position for pen height */

	if (x < thePath->current_xpos) thePath->pen_offset = thePath->pen_height;
	else if (x > thePath->current_xpos) thePath->pen_offset = 0;

		/* clip y position to screen coords */

	y = clamp(thePath->min_y, y + thePath->pen_offset, thePath->max_y);

/* new code */

		/*	This code is a special case for the first generated point. Basically.
			it attempts to compensate for the fact that because of the pen width
			and height, the current_xpos and current_ypos may not be an actual
			point on the path. We can't adjust that beforehand since we don't
			yet know what direction we are going to be drawing in until we
			get to this point, and pen offsets are derived from drawing direction.

			What this does is adjust the current_xpos and current_ypos to
			the correct pen offset and save those points. Later, we will attempt
			to "hook up" to those same points as the last drawing stroke.
		*/

	if (thePath->current_edge == NULL)
	{
		WORD upwards;
		WORD adjusted_current_ypos;

		adjusted_current_ypos = 
			clamp(thePath->min_y, thePath->current_ypos + thePath->pen_offset, thePath->max_y);

		if (y == adjusted_current_ypos)		/* if no change in y, do nothing...		*/
			return;

			/*	At this point, we know we are definately going to draw _something_.
				We know the drawing direction. So set up all the variables
				we need to remember the start point.
			*/
				
		thePath->current_ypos = adjusted_current_ypos;
		upwards = (thePath->current_ypos > y);
		
		thePath->start_xpos = thePath->current_xpos;
		thePath->start_ypos = thePath->current_ypos;

			/*	All that remains is to make sure that the "first" point also gets
				added to the edge list.

				We do this by cheating -- basically, we lie about what scan line
				we're on.
			*/

		if (upwards)
		{	thePath->current_ypos++;
		}
		else
		{	/* thePath->current_ypos--; */ /* I don't know why this doesn't work */
			thePath->current_xpos += thePath->pen_width;
		}
	}
/* end new code */

	if (thePath->current_ypos < y)					/* if line drawing down */
	{
			/* if old edge was rising, or full, then alloc a new edge */

		if (!thePath->current_edge || thePath->current_edge->rising)
		{	unless (alloc_edgelist(FALSE)) return;
		}

			/* kludge for pen width */

		if (old_pen_offset != thePath->pen_offset)
		{	while (thePath->current_ypos < y-1)
			{
				if (EDGELIST_FULL(thePath->current_edge))
				{	unless (alloc_edgelist(FALSE)) return;
				}
				
				*thePath->first_available_point++ = thePath->current_xpos + thePath->pen_width;
				thePath->current_ypos++;
			}
		}

			/* draw edge down the page */

		while (thePath->current_ypos < y)
		{
			if (EDGELIST_FULL(thePath->current_edge))
			{	unless (alloc_edgelist(FALSE)) return;
			}

			*thePath->first_available_point++ = x + thePath->pen_width;
			thePath->current_ypos++;
		}
		thePath->current_edge->bottom = y;
	}
	else if (thePath->current_ypos > y)
	{	
			/* if old edge was falling, or full, then alloc a new edge */
	
		if (!thePath->current_edge || !thePath->current_edge->rising)
		{	unless (alloc_edgelist(TRUE)) return;
		}

			/* kludge for pen offset */

		if (old_pen_offset != thePath->pen_offset)
		{	while (thePath->current_ypos > y + 1)
			{
				if (EDGELIST_FULL(thePath->current_edge))
				{	unless (alloc_edgelist(TRUE)) return;
				}

				*--thePath->last_available_point = thePath->current_xpos;
				thePath->current_ypos--;
			}
		}

			/* draw edge up the page */

		while (thePath->current_ypos > y)
		{
			if (EDGELIST_FULL(thePath->current_edge))
			{	unless (alloc_edgelist(TRUE)) return;
			}

			*--thePath->last_available_point = x;
			thePath->current_ypos--;
		}
		thePath->current_edge->points = thePath->last_available_point;
		thePath->current_edge->top = y;
	}

	thePath->current_xpos = x;
}

	/*	This is just a glue function which converts fixed to integer and then
		adds the edge points.
	*/

void VLineEdgePoints(Fixed x, Fixed y)
{	add_edge_points(FR(x),FR(y));
}

	/* This routine draws short lines (up to 256 pixels) with accurate jaggies
		based on sub-pixel positioning of endpoints.

		For longer lines, we should eventually subdivide and recurse...
	*/

void LineEdge(Fixed x1, Fixed y1, Fixed x2, Fixed y2)
{	Fixed				dx = x2 - x1;
	Fixed				dy;
	WORD				height,
						y,
						yoffset;

	x1 += 0x8000;
	y1 += 0x8000;
	y2 += 0x8000;
	y = y1 >> 16;

	if (y1 < y2)
	{
		height = (y2 >> 16) - y;
		if (height == 0) return;

			/* We could check for very long lines and handle differently... */

		dy = (y2 - y1) >> 12;
	
		yoffset = (y1 >> 12) & 0x0f;

		if (dy < 1) dy = 1;
		dx = (dx << 4) / dy;
		x1 += (dx * (15 - yoffset)) >> 4;

		while (height--)
		{	add_edge_points(x1>>16,++y);
			x1 += dx;
		}
	}
	else
	{
		height = y - (y2 >> 16);
		if (height == 0) return;

			/* We could check for very long lines and handle differently... */

		dy = (y1 - y2) >> 12;

		yoffset = (y1 >> 12) & 0x0f;
	
		if (dy < 1) dy = 1;
		dx = (dx << 4) / dy;
		x1 += (dx * yoffset) >> 4;

		while (height--)
		{	add_edge_points(x1>>16,--y);
			x1 += dx;
		}
	}
}

	/* A recursive curve-drawer */

void CurveEdge(curve *cur, WORD level)
{
	WORD	dx = FR(cur->start.x) - FR(cur->end.x);
	WORD	dy = FR(cur->start.y) - FR(cur->end.y);

		/* if all points are on the same scanline */

	if ( dy == 0 && FR(cur->start.y) == FR(cur->control.y) )
	{	VLineEdgePoints(cur->control.x, cur->control.y);
		VLineEdgePoints(cur->end.x, cur->end.y);
		return;
	}

		/* if all points are in the same column */

	if ( dx == 0 && FR(cur->start.x) == FR(cur->control.x) )
	{	VLineEdgePoints(cur->control.x, cur->control.y);
		VLineEdgePoints(cur->end.x, cur->end.y);
		return;
	}

	if (level)
	{	curve			left,
						right;
						
		left.start = cur->start;
		left.control.x = AVE(cur->start.x, cur->control.x);
		left.control.y = AVE(cur->start.y, cur->control.y);
		right.control.x = AVE(cur->control.x, cur->end.x);
		right.control.y = AVE(cur->control.y, cur->end.y);
		left.end.x = right.start.x = AVE(left.control.x, right.control.x);
		left.end.y = right.start.y = AVE(left.control.y, right.control.y);

		if ( ABS(cur->control.x - left.end.x) < 0x800 &&
			 ABS(cur->control.y - left.end.y) < 0x800 )
		{
			LineEdge(cur->start.x, cur->start.y, cur->end.x, cur->end.y);
			return;
		}

		right.end = cur->end;

		CurveEdge(&left,level-1);
		CurveEdge(&right,level-1);
	}
	else
	{	LineEdge(cur->start.x, cur->start.y, cur->end.x, cur->end.y);
	}
}

	/* This part probably does't need to be downcoded... */

typedef struct {
	long				vectors;
	long				controlBits[1];
	/* point			vector[anyNumber]; */	/* variable-length data			*/
} path;

BOOL OnCurve(long *bits, long index)
{	bits += index >> 5;
	index &= 31;
	return (*bits & (0x80000000 >> index)) == 0;
}

typedef struct {
	int					isLine;
	curve				c;
	
	/* Private */
	long				index;
	long				ep;
	long				*bits;
	point				*p;
} pathWalker;

void InitPathWalker (pathWalker *w, path *aPath)
{	w->index = 0;
	w->ep = aPath->vectors - 1;
	w->bits = aPath->controlBits;
	/* skip path the controlbits to point to the first point */
	w->p = (point *)(w->bits + (aPath->vectors + 31 >> 5));
}

int NextPathSegment(pathWalker *w)
{	long				prevIndex,
						nextIndex;
						
	if (w->index == 0)
	{	if (OnCurve(w->bits, w->ep))
			w->c.start = w->p[w->ep];
		else
		{	if (OnCurve(w->bits,0))
			{	w->c.start = w->p[0];
				w->index = 1;
			}
			else	/* start at an implied on-curve point */
			{	w->c.start.x = AVE(w->p[0].x, w->p[w->ep].x);
				w->c.start.y = AVE(w->p[0].y, w->p[w->ep].y);
			}
		}
	}
	else
		w->c.start = w->c.end;

NEXT_SEGMENT:
	/* Compute the point index before and after the current one.
	 * This wraps around, since we assume the contour is closed. */
	 
	 prevIndex = w->index == 0 ? w->ep : w->index - 1;
	 nextIndex = w->index == w->ep ? 0 : w->index + 1;
	 
	 if (OnCurve(w->bits, w->index))
	 {	if (OnCurve(w->bits, prevIndex))
	 	{	w->isLine = TRUE;				/* this means we have a line */
			w->c.end = w->p[w->index];
		}
		else if (w->index++ <= w->ep)
			goto NEXT_SEGMENT;
	}
	else
	{	w->isLine = FALSE;					/* this means we have a curve */
		w->c.control = w->p[w->index];
		if (OnCurve(w->bits,nextIndex))
			w->c.end = w->p[nextIndex];
		else
		{	w->c.end.x = AVE(w->p[w->index].x, w->p[nextIndex].x);
			w->c.end.y = AVE(w->p[w->index].y, w->p[nextIndex].y);
		}
	}
	return w->index++ <= w->ep;	/* return TRUE if there are still more segments */
}

path *NextPath(path *aPath)
{	return (path *)((long *)aPath + 1 + (aPath->vectors + 31 >> 5) +
		aPath->vectors * 2);
}

	/*	This routine traverses all the data structures we built, and calls
		a routine to paint the data onto the screen.
	
		This part should be downcoded, and in addition the part that does the
		actual drawing should be a callback.
	*/

#define kCurveLimit			4

BOOL fill_edges(WORD *ystart, UWORD maxlines, UWORD windmask)
{	WORD				ypos = *ystart;
	WORD				start_x;
	WORD				wind;
	edgelist			*alist = NULL,
						*tlist,
						*edge,
						**edge_ptr;

	ypos = thePath->first_edge->top;

	while (maxlines--)
	{	if (alist == NULL && thePath->first_edge == NULL) return FALSE;

			/* transfer polygons from thePath->first_edge to active edge list... */

		while (thePath->first_edge &&
			   thePath->first_edge->top <= ypos)	/* if scan line intersects next poly */
		{	edge = thePath->first_edge;
			thePath->first_edge = edge->next;

			edge->next = alist;
			alist = edge;
			edge->bottom--;
		}

			/* if sorting needed, re-sort list of active edges */

		tlist = alist;
		alist = NULL;

			/* sort the list of active edges...
				Note that this sort takes advantage of existing coherency
				in the list and is therefore quite fast.
			*/

		while (tlist)
		{	edge = tlist;
			tlist = tlist->next;
			if (ypos >= edge->bottom) continue;

			for (edge_ptr = &alist; *edge_ptr; edge_ptr = &(*edge_ptr)->next )
			{	if ( *(*edge_ptr)->points > *edge->points ) break;
			}
			edge->next = *edge_ptr;
			*edge_ptr = edge;
		}

			/* "winding fill" algorithm */

		wind = 0;							/* initially outside of figure */

		for (edge = alist; edge; edge = edge->next)
		{	WORD	stop_x;

		/*	stop_x = clamp(thePath->min_x, *edge->points++, thePath->max_x); */
			stop_x = *edge->points++;

			if ((wind & windmask) == 0) start_x = stop_x;

			if (edge->left_edge) ++wind; else --wind;

				/* Here's where we actualy draw the line. We can replace
					this with whatever code is appropriate... */
				
			if ((wind & windmask) == 0)
				PaintRect(rp,start_x,ypos,stop_x-start_x,1);
		}

		ypos++;
	}
	*ystart = ypos;
	return (alist || thePath->first_edge);
}

	/* This is the overall routine which calls all the others */

path *FramePath(path *cont)
{	pathWalker				walker;
	WORD					org_x,org_y;
	WORD					ypos;

	unless (init_edgelists()) return NULL;

	InitPathWalker(&walker,cont);

		/* the first segment is special, since it calls fmoveto */

	if (NextPathSegment(&walker))
	{	fmoveto(thePath,walker.c.start.x, walker.c.start.y);
		org_x = thePath->current_xpos;
		org_y = thePath->current_ypos;
		if (walker.isLine)
			flineto(thePath,walker.c.end.x, walker.c.end.y);
		else
			CurveEdge(&walker.c, kCurveLimit);
	}

		/* keep looping until we run out of segments */

	while (NextPathSegment(&walker))
	{	
		if (walker.isLine)
			flineto(thePath,walker.c.end.x, walker.c.end.y);
		else
			CurveEdge(&walker.c, kCurveLimit);
	}

#if 0
	add_edge_points(org_x,org_y);
	thePath->pen_offset = 0;
	add_edge_points(org_x,org_y);
	insert_last_edge();
#endif
/* new code */
	{	WORD		save_width, save_height;

			/* draw back to origin point */

		add_edge_points(org_x,org_y);

			/* now, draw back to EXACT (no pen adjustment) first point. */

		save_width = thePath->pen_width; thePath->pen_width = 0;
		save_height = thePath->pen_height; thePath->pen_height = 0;
		thePath->pen_offset = 0;

		add_edge_points(thePath->start_xpos,thePath->start_ypos);

		thePath->pen_width = save_width;
		thePath->pen_height = save_height;

		insert_last_edge();
	}
/* end new code */

		/* now, traverse the lists... */
	
	ypos = thePath->first_edge->top;
	fill_edges(&ypos,-1,~0);

		/* return the next path, used if this path is one of several within
			a series of paths.
		*/
	
	return NextPath(cont);
}

void test(void)
{
	long myPath[] = {
		4,
		0x50000000,
		ff(47),ff(200-101),
		ff(146),ff(200-124),
		ff(225),ff(200-68),
		ff(146),ff(200-119),
	};

	long myPath2[] = {
		4,
		0x50000000,
		ff(17),ff(7),
		ff(30),ff(17),
		ff(42),ff(5),
		ff(30),ff(19),
	};

	int					i;
	long sec1,sec2,mic1,mic2;

	thePath->min_x = 0;
	thePath->max_x = 320;

	thePath->min_y = 0;
	thePath->max_y = 200;

	thePath->pen_width = 1;
	thePath->pen_height = 1;
		
	FramePath( (path *)myPath2 );

	CurrentTime((ULONG *)&sec1,(ULONG *)&mic1);
	for (i=0; i<100; i++) FramePath( (path *)myPath );
	CurrentTime((ULONG *)&sec2,(ULONG *)&mic2);

	sec1 = sec1 * 1000 + mic1 / 1000;
	sec2 = sec2 * 1000 + mic2 / 1000;

	Printf("Elapsed time was %ld milliseconds\n",sec2-sec1);

	flush_edgelists(TRUE);

};
