/*	Willows Software, Inc. - Feb 1997	*/
/*	"@(#)curves.c	1.1 :/users/sccs/src/win/mac/s.curves.c 2/6/97 17:40:49" */

#include <ToolUtils.h>
#include <Memory.h>
#include <QuickDraw.h>
#include <FixMath.h>

#include "curves.h"

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

void FrameCurve(curve* cur, int level)
{
	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);
		right.end = cur->end;

		FrameCurve( &left, level-1 );
		FrameCurve( &right, level-1 );
	}
	else
		flineto( cur->end.x, cur->end.y );
}

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

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

int NextPathSegment( pathWalker* w)
{
	long prevIndex, nextIndex;

	/* 0 means this is the first segment */
	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	/* start where we previously left off */
		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* FramePath(path* cont, int closed)
{
	pathWalker	walker;

	InitPathWalker(&walker, cont);
	/*
	 *	The first segment is special, since it calls fmoveto.
	 */
	if (NextPathSegment(&walker))
	{	fmoveto( walker.c.start.x, walker.c.start.y );
		if (walker.isLine)
			flineto(walker.c.end.x, walker.c.end.y );
		else
			FrameCurve(&walker.c, kCurveLimit);
	}
	/*
	 *	Keep looping until we run out of segments
	 */
	while (NextPathSegment(&walker))
		if (walker.isLine)
			flineto(walker.c.end.x, walker.c.end.y );
		else
			FrameCurve(&walker.c, kCurveLimit);

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

void FramePaths(paths* aPath, int closed)
{
	long ctr = aPath->contours;
	path* cont = aPath->contour;

	while (ctr--) {
		cont = FramePath( cont, closed );
	}
}

void PaintDiamond(Rect* r)
{
	short size = sizeof(short) + sizeof(Rect) + sizeof(Point) * 4;
	PolyHandle p = (PolyHandle)NewHandle( size );
	short* pt;

	p = OpenPoly();
	MoveTo( r->right + r->left >> 1, r->top - 1 );
	LineTo( r->right , r->top + r->bottom >> 1 );
	LineTo( r->right + r->left >> 1, r->bottom );
	LineTo( r->left - 1, r->top + r->bottom >> 1 );
	ClosePoly();
	PaintPoly(p);
	KillPoly(p);
}

void ExamplePaths()
{
	long myPaths[] = {
		5,				/* 5 contours */
		3, 0xE0000000, 0, ff(16), 0, ff(8), ff(14), ff(12),
		3, 0xE0000000, ff(8), 0, ff(16), 0, ff(12), ff(14),
		3, 0xE0000000, ff(24), ff(8), ff(24), ff(16), ff(10), ff(12),
		3, 0xE0000000, ff(16), ff(24), ff(8), ff(24), ff(12), ff(10),
		16, 0x11110000,
				ff(8), 0, ff(12), ff(4), ff(16), 0, ff(16), ff(8),
				ff(24), ff(8), ff(20), ff(12), ff(24), ff(16), ff(16), ff(16),
				ff(16), ff(24), ff(12), ff(20), ff(8), ff(24), ff(8), ff(16),
				0, ff(16), ff(4), ff(12), 0, ff(8), ff(8), ff(8)
	};

	ScalePaths((paths*)myPaths, ff(15), ff(15));
	OffsetPaths((paths*)myPaths, ff(25), ff(25));
	FramePaths((paths*)myPaths, 0);
}

void MarkPaths(paths* aPath)
{
	long ctr = aPath->contours;
	path* cont = aPath->contour;
	Point loc;
	Rect r;

	while (ctr--)
	{	long* bits = cont->controlBits;
		long* coord = (long*)(bits + (cont->vectors + 31 >> 5));
		long ptIndex;

		for (ptIndex = 0; ptIndex < cont->vectors; ptIndex++)
		{	r.left = FR(*coord++) - 2;
			r.top = FR(*coord++) - 2;
			r.right = r.left + 5;
			r.bottom = r.top + 5;
			if (OnCurve(bits, ptIndex))
				PaintOval(&r);
#if 0
			else
				FillOval(&r, &qd.gray);
#endif
		}
		cont = (path*)coord;
	}
}

/*
 *	Create a new path = dst + src, and then dispose dst.
 */
paths* AppendPaths(paths* dst, paths* src)
{
	long dstSize, srcSize = GetPtrSize((Ptr)src);
	paths* newDst;

	if (dst)
	{	dstSize = GetPtrSize((Ptr)dst);
		newDst = (paths*)NewPtr(dstSize + srcSize - 4);
	}
	else
		newDst = (paths*)NewPtr(srcSize);

	if (!newDst || MemError()) {
#ifdef	DEBUG
		DebugStr("\pAppendPaths(): Not enough memory for new path.");
#endif	//	DEBUG
		return( dst );
	}

	if (dst)
	{	BlockMove((Ptr)dst, (Ptr)newDst, dstSize);
		BlockMove((Ptr)src + 4, (Ptr)newDst + dstSize, srcSize - 4);
		*(long*)newDst += *(long*)src;
		DisposePaths(dst);
	}
	else
		BlockMove((Ptr)src, (Ptr)newDst, srcSize);

	return newDst;
}

void DisposePaths(paths* p)
{
	DisposePtr((Ptr)p);
}

void GetPathsBounds(paths* p, Rect* r)
{
	long ctr = p->contours;
	path* cont = p->contour;

	r->left = r->top = 32767;
	r->right = r->bottom = -32678;

	while (ctr--)
	{	long* bits = cont->controlBits;
		long* coord = (long*)(bits + (cont->vectors + 31 >> 5));
		long ptIndex;

		for (ptIndex = 0; ptIndex < cont->vectors; ptIndex++)
		{	short x = FR(*coord++);
			short y = FR(*coord++);

			if (x < r->left) r->left = x; else
			if (x > r->right) r->right = x;
			if (y < r->top) r->top = y; else
			if (y > r->bottom) r->bottom = y;
		}
		cont = (path*)coord;
	}
}

void OffsetPaths( paths* p, Fixed dx, Fixed dy )
{
	long ctrs = p->contours;
	path* aPath = p->contour;

	while (ctrs--)
	{	long pts = aPath->vectors;
		Fixed* coord = (Fixed*)aPath + 1 + (pts + 31 >> 5);
		
		while (pts--)
		{	*coord++ += dx;
			*coord++ += dy;
		}
		aPath = (path*)coord;
	}
}

void ScalePaths(paths* p, Fixed sx, Fixed sy)
{
	long ctrs = p->contours;
	path* aPath = p->contour;

	while (ctrs--)
	{	long pts = aPath->vectors;
		Fixed* coord = (Fixed*)aPath + 1 + (pts + 31 >> 5);
		
		while (pts--)
		{	*coord = FixMul(*coord, sx);
			coord++;
			*coord = FixMul(*coord, sy);
			coord++;
		}
		aPath = (path*)coord;
	}
}

void MapPaths(paths* p, Rect* src, Rect* dst)
{
	Fixed sx = FixDiv( dst->right - dst->left, src->right - src->left );
	Fixed sy = FixDiv( dst->bottom - dst->top, src->bottom - src->top );

	OffsetPaths(p, -ff(src->left), -ff(src->top));
	ScalePaths(p, sx, sy);
	OffsetPaths(p, ff(dst->left), ff(dst->top));
}

long SizeOfPaths(paths* p)
{
	long size = sizeof(long);
	long ctrs = p->contours;
	path* cont = p->contour;

	while (ctrs--)
	{	long pathSize = (1 + (cont->vectors + 31 >> 5) + (cont->vectors << 1)) << 2;

		size += pathSize;
		cont = (path*)((Ptr)cont + pathSize);
	}
	return size;
}

paths* CopyPaths(paths* p)
{
	long size = SizeOfPaths(p);
	Ptr p2 = NewPtr( size );

	BlockMove( (Ptr)p, p2, size );
	return (paths*)p2;
}

