/* patch.c */

#include "patch.h"
#include "polygon.h"
#include "Memory.h"
#include "error.h"
#include "debug.h"
#include "coefficients.h"
#include "sole.h"

/* #define DEBUG */
#include "monitor.h"

#ifdef BETTER_MEMMAN

static STORAGE *patchStor = (STORAGE *)NULL;
#define NEWPATCH()  	(PATCH *)New(sizeof(PATCH), &patchStor)
#define DISPOSEPATCH(ptr) Dispose((unsigned char *)(ptr), &patchStor)

static STORAGE *ovlStor[BASISMAXSIZE];
#define NEWOVERLAPMATRIX(patch)	{	\
        patch->inverse_overlap = (Float *)New(patch->basis->size * patch->basis->size * sizeof(Float), &ovlStor[patch->basis->size]);	\
				}
#define DISPOSEOVERLAPMATRIX(patch)  {	\
	Dispose((unsigned char *)(patch->inverse_overlap), &ovlStor[patch->basis->size]);				\
				}

static STORAGE *coeffStor[BASISMAXSIZE];
#define NEWCOEFFICIENTS(howmany)  	(COLOR *)New(howmany*sizeof(COLOR), &coeffStor[howmany-1])
#define DISPOSECOEFFICIENTS(ptr, howmany) Dispose((unsigned char *)(ptr), &coeffStor[howmany-1])

#else /*BETTER_MEMMAN*/

#define NEWPATCH()	(PATCH *)Alloc(sizeof(PATCH))
#define DISPOSEPATCH(ptr) Free((char *)ptr, sizeof(PATCH))

#define NEWOVERLAPMATRIX(patch)	{	\
        patch->inverse_overlap = (Float *)Alloc(patch->basis->size * patch->basis->size * sizeof(Float));	\
				}
#define DISPOSEOVERLAPMATRIX(patch)  {	\
	Free((char *)(patch->inverse_overlap), patch->basis->size * patch->basis->size * sizeof(Float));	\
				}

#define NEWCOEFFICIENTS(howmany)	(COLOR *)Alloc(currentBasis->size*sizeof(COLOR))
#define DISPOSECOEFFICIENTS(ptr, howmany) Free((char *)ptr, currentBasis->size*sizeof(COLOR))
#endif /*BETTER_MEMMAN*/

/* copies the right number of inverse overlap factors from 
 * overlap[BASISMAXSIZE][BASISMAXSIZE] to Float *patch->inverse_overlap */
#define INITOVERLAPMATRIX(patch, overlap)	{		\
	Float *o; int alpha, beta;				\
	for (o=patch->inverse_overlap, alpha=0; alpha<patch->basis->size; alpha++)	\
		for (beta=0; beta<patch->basis->size; beta++)	\
			*o++ = overlap[alpha][beta];		\
}



static int patchid = 1;  /* laat ids beginnen vanaf 1 -> geen problemen bij ID rendering (achtergrond moet zwart = ID 0) */
int TotalElements = 0;

int GetNumberOfElements(void)
{
	return TotalElements;
}

int PatchGetNextID(void)
{
	return patchid;
}

void PatchSetNextID(int id)
{
	patchid = id;
}

/* geeft een wijzer naar de normaalvector terug indien alles OK, NULL-wijzer indien 
 * ontaarde veelhoek */
VECTOR *PatchNormal(PATCH *patch, VECTOR *normal)
{
	float norm;
	POINT *prev, *cur;
	int i;

	normal->x = normal->y = normal->z = 0.;
	prev = patch->vertex[patch->nrvertices -1]->point;
	for(i=0; i<patch->nrvertices; i++, prev=cur) {
	  	cur = patch->vertex[i]->point;
		normal->x += (prev->y - cur->y) * (prev->z + cur->z);
		normal->y += (prev->z - cur->z) * (prev->x + cur->x);
		normal->z += (prev->x - cur->x) * (prev->y + cur->y);
	}

	if ((norm = VECTORNORM(*normal)) < EPSILON) {
		/*
		 * Degenerate normal --> degenerate polygon
		 */
		Warning("PatchNormal", "degenerate patch (id %d)", patch->id);
		return (VECTOR *)NULL;
	}
	VECTORSCALEINVERSE(norm, *normal, *normal);

	return normal;
}

/*
 * we also compute the jacobian J(u,v) for the coordinate mapping from [0,1]^2
 * (or half of it for triangles) to the patch.
 *
 * - for triangles: J(u,v) = the area of the triangle 
 * - for quadrilaterals: J(u,v) = A + B.u + C.v
 *	the area of the patch = A + (B+C)/2
 *	for paralellograms holds that B=C=0
 * 	the coefficients A,B,C are only explicitely stored if B and C are nonzero.
 *
 * The normal of the patch should have been computed before calling this routine.
 */

float PatchArea(PATCH *patch)
{
	POINT *p1, *p2, *p3, *p4;
	VECTOR d1, d2, d3, d4, cp1, cp2, cp3;
	float a, b, c;

	/* we need to compute the area and jacobian explicitely */
	switch (patch->nrvertices) {
	case 3:
		/* jacobian J(u,v) for the mapping of the triangle 
		 * (0,0),(0,1),(1,0) to a triangular patch is constant and equal 
		 * to the area of the triangle ... so there is no need to store 
		 * any coefficients explicitely */
		patch->jacobian = (JACOBIAN *)NULL;
		
		p1 = patch->vertex[0]->point;
		p2 = patch->vertex[1]->point;
		p3 = patch->vertex[2]->point;
		VECTORSUBSTRACT(*p2, *p1, d1);
		VECTORSUBSTRACT(*p3, *p2, d2);
		VECTORCROSSPRODUCT(d1, d2, cp1);
		patch->area = 0.5 * VECTORNORM(cp1);
		break;
	case 4:
		p1 = patch->vertex[0]->point;
		p2 = patch->vertex[1]->point;
		p3 = patch->vertex[2]->point;
		p4 = patch->vertex[3]->point;
		VECTORSUBSTRACT(*p2, *p1, d1);
		VECTORSUBSTRACT(*p3, *p2, d2);
		VECTORSUBSTRACT(*p3, *p4, d3);
		VECTORSUBSTRACT(*p4, *p1, d4);
		VECTORCROSSPRODUCT(d1, d4, cp1);
		VECTORCROSSPRODUCT(d1, d3, cp2);
		VECTORCROSSPRODUCT(d2, d4, cp3);
		a = VECTORDOTPRODUCT(cp1, patch->norm);
		b = VECTORDOTPRODUCT(cp2, patch->norm);
		c = VECTORDOTPRODUCT(cp3, patch->norm);

		if (b < EPSILON && c < EPSILON) {
			patch->jacobian = (JACOBIAN *)NULL;
			patch->area = a;
		} else {
			patch->jacobian = JacobianCreate(a, b, c);
			patch->area = a + 0.5 * (b + c);
		}
		break;
	default:
		Fatal(2, "PatchArea", "Can only handle triangular and quadrilateral patches.\n");
		patch->jacobian = (JACOBIAN *)NULL;
		patch->area = 0.0;
	}

	return patch->area;
}

POINT *PatchMidpoint(PATCH *patch, POINT *p)
{
	int i;

	VECTORSET(*p, 0, 0, 0);
	for (i=0; i<patch->nrvertices; i++)
		VECTORSUM(*p, *(patch->vertex[i]->point), *p);
	VECTORSCALEINVERSE((float)patch->nrvertices, *p, *p);

	return p;
}

void PatchConnectVertex(PATCH *patch, VERTEX *vertex)
{
	vertex->patches = PatchListAdd(vertex->patches, patch);
	if (!vertex->normal) 
		vertex->normal = &patch->norm;
}

void PatchConnectVertices(PATCH *patch)
{
	int i;

	for (i=0; i<patch->nrvertices; i++)
		PatchConnectVertex(patch, patch->vertex[i]);
}

/* returns the index of the vertex in the patches vertex list */
int PatchWhichVertex(PATCH *patch, VERTEX *vertex)
{
	int i;

	for (i=0; i<patch->nrvertices; i++) {
		if (patch->vertex[i] == vertex)
			return i;
	}

	Error("PatchWhichVertex", "called with a vertex not belonging to the patch (id %d)", patch->id);
	return -1;
}

void PatchDestroy(PATCH *patch)
{
	PatchListIterate(patch->subpatches, PatchDestroy);
	PatchListDestroy(patch->subpatches);

	InteractionListIterate(patch->interactions, InteractionDestroy);
	InteractionListDestroy(patch->interactions);

	if (patch->bounds) 
		BoundsDestroy(patch->bounds);

	if (patch->Tpatches) {
		TPatchListIterate(patch->Tpatches, TPatchDestroy);
		TPatchListDestroy(patch->Tpatches);
	}

	if (patch->jacobian)
		JacobianDestroy(patch->jacobian);

	DISPOSECOEFFICIENTS(patch->radiance, patch->basis->size);
	DISPOSECOEFFICIENTS(patch->unshot_radiance, patch->basis->size);
	DISPOSECOEFFICIENTS(patch->received_radiance, patch->basis->size);
	DISPOSECOEFFICIENTS(patch->importance, patch->basis->size);
	DISPOSECOEFFICIENTS(patch->unshot_importance, patch->basis->size);
	DISPOSECOEFFICIENTS(patch->received_importance, patch->basis->size);

	if (patch->inverse_overlap)
		DISPOSEOVERLAPMATRIX(patch);

	DISPOSEPATCH(patch);
	TotalElements--;
}

static void PatchInitCoefficients(PATCH *patch)
{
	patch->radiance = NEWCOEFFICIENTS(patch->basis->size);
	patch->unshot_radiance = NEWCOEFFICIENTS(patch->basis->size);
	patch->received_radiance = NEWCOEFFICIENTS(patch->basis->size);
	patch->importance = NEWCOEFFICIENTS(patch->basis->size);
	patch->unshot_importance = NEWCOEFFICIENTS(patch->basis->size);
	patch->received_importance = NEWCOEFFICIENTS(patch->basis->size);

	CLEARCOEFFICIENTS(patch->radiance, patch->basis->size);
	CLEARCOEFFICIENTS(patch->unshot_radiance, patch->basis->size);
	CLEARCOEFFICIENTS(patch->received_radiance, patch->basis->size);
	CLEARCOEFFICIENTS(patch->importance, patch->basis->size);
	CLEARCOEFFICIENTS(patch->unshot_importance, patch->basis->size);
	CLEARCOEFFICIENTS(patch->received_importance, patch->basis->size);

	patch->direct_importance = 0.;
}

/* computes the inverse of the basis overlap matrix on the patch, to
 * be computed for all irregular patches, remember that even orthogonal
 * basisfunctions become non orthogonal (iow, get overlap, the integral of
 * the product of two different basisfunctions does not need to be zero 
 * anymore) when mapped to irregular patches (that is: if the
 * jacobian of the patch is not equal to the area, but contains
 * coefficients depening on u and/or v). The inverse of the overlap
 * matrix is needed to renormalize received radiance etc..., since
 * the basisfunctions are not orthogonal on the patch, dividing
 * the radince coefficient to basisfunction alpha by the norm on
 * the patch of that basisfunction alpha does not suffice. A
 * set of linear equations must be solved. The coefficients of this set
 * are the overlap factors of the basisfunctions. Since we need to solve
 * such a system many times for the patch (each time a formfactor is
 * computed on it + each time to renormalize received radiance) it 
 * probably pays to compute the inverse coefficients once and store them
 * for later, probably frequent, use. */
static void PatchComputeInverseBasisOverlapMatrix(PATCH *patch)
{
	double overlap[BASISMAXSIZE][BASISMAXSIZE];
	int alpha, beta;

/* construct the overlap matrix */
	for (alpha=0; alpha<patch->basis->size; alpha++) {
		for (beta=0; beta<patch->basis->size; beta++)
			overlap[alpha][beta] = patch->basis->overlap[alpha][beta](patch);
	}

/* compute its inverse */
	if (InvertMatrix(overlap, overlap, patch->basis->size) == 0.) {
		Error("PatchComputeInverseBasisOverlapMatrix", "Singular basis overlap matrix ??");
		patch->inverse_overlap = (Float *)NULL;
		return;
	}

/* allocate some memory to hold the result and copy */
	NEWOVERLAPMATRIX(patch);
	INITOVERLAPMATRIX(patch, overlap);	
}

PATCH *PatchCreate(int nrvertices, VERTEX *v1, VERTEX *v2, VERTEX *v3, VERTEX *v4, int twosided)
{
	PATCH *patch;

/* this may occur after an error parsing the input file, and some other routine
 * will already complain */
	if (!v1 || !v2 || !v3 || (nrvertices==4 && !v4))
		return (PATCH *)NULL;	

/* it's sad but it's true */
	if (nrvertices != 3 && nrvertices != 4) {
		Error("PatchCreate", "Can only handle quadrilateral or triagular patches");
		return (PATCH *)NULL;
	}

	patch = NEWPATCH();
	patch->id = patchid++;

	patch->surface = (SURFACE *)NULL;

	patch->interactionscreated = FALSE;
	patch->interactions = InteractionListCreate();

	patch->subpatches = PatchListCreate();
	patch->parent = (PATCH *)NULL;

	patch->nrvertices = nrvertices;
	patch->vertex[0] = v1;
	patch->vertex[1] = v2;
	patch->vertex[2] = v3;
	patch->vertex[3] = v4;

	patch->twosided = twosided;

	patch->bounds = (float *)NULL;

	patch->Tpatches = TPatchListCreate();

	if (PatchNormal(patch, &patch->norm) == (VECTOR *)NULL) {    /* degenerate patch */
		DISPOSEPATCH(patch);
		return (PATCH *)NULL;
	}
	patch->d = VECTORDOTPRODUCT(patch->norm, *patch->vertex[0]->point); /* plane constant */
	patch->index = VectorDominantCoord(&patch->norm);     /* dominant part of normal */
	patch->area = PatchArea(patch);	/* also computes the jacobian */
	PatchMidpoint(patch, &patch->midpoint);

	PatchConnectVertices(patch);

	patch->basis = (nrvertices == 3 ? triBasis : quadBasis);
	PatchInitCoefficients(patch);

	if (patch->jacobian)	/* irregular quadrilateral patch */
		PatchComputeInverseBasisOverlapMatrix(patch);
	else
		patch->inverse_overlap = (Float *)NULL;

	patch->updated = FALSE;

	TotalElements++;

	return patch;
}

void PatchPrintID(FILE *out, PATCH *patch)
{
	fprintf(out, "%d ", patch->id);
}

void PatchPrint(FILE *out, PATCH *patch)
{
	int i;

	fprintf(out, "Patch id %d:\n", patch->id);

	fprintf(out, "%d vertices:\n", patch->nrvertices); 
	for (i=0; i<patch->nrvertices; i++)
		VertexPrint(out, patch->vertex[i]);
	fprintf(out, "\n");
	
	fprintf(out, "norm = ("); VectorPrint(out, patch->norm);
	fprintf(out, "), d = %g, area = %g, index = %s. midpoint = (",
		patch->d, patch->area, VECTORINDEXNAME(patch->index));
	VectorPrint(out, patch->midpoint);
	fprintf(out, ")\n");
	if (patch->jacobian) 
		fprintf(out, "Jacobian: %g %+g*u %+g*v \n",
			patch->jacobian->A, patch->jacobian->B, patch->jacobian->C);
	else
		fprintf(out, "No explicitely stored jacobian\n");

	fprintf(out, "radiance: "); PRINTCOEFFICIENTS(out, patch->radiance, patch->basis->size); fprintf(out, "\n");
	fprintf(out, "unshot radiance: "); PRINTCOEFFICIENTS(out, patch->unshot_radiance, patch->basis->size); fprintf(out, "\n");
	fprintf(out, "received radiance: "); PRINTCOEFFICIENTS(out, patch->received_radiance, patch->basis->size); fprintf(out, "\n");

	fprintf(out, "importance: "); PRINTCOEFFICIENTS(out, patch->importance, patch->basis->size); fprintf(out, "\n");
	fprintf(out, "unshot importance: "); PRINTCOEFFICIENTS(out, patch->unshot_importance, patch->basis->size); fprintf(out, "\n");
	fprintf(out, "received importance: "); PRINTCOEFFICIENTS(out, patch->received_importance, patch->basis->size); fprintf(out, "\n");

	fprintf(out, "subpatches IDs: ");
	PatchListIterate1B(patch->subpatches, PatchPrintID, out);
	fprintf(out, "\n");

	fprintf(out, "material: ");
	MaterialPrint(out, patch->surface->material);
	fprintf(out, "\n\n");
}

float *PatchBounds(PATCH *patch, float *bounds)
{
	int i;
	
	if (!patch->bounds) {
		patch->bounds = BoundsCreate();
		BoundsInit(patch->bounds);
		for (i=0; i<patch->nrvertices; i++) 
			BoundsEnlargePoint(patch->bounds, patch->vertex[i]->point);
	}

	BoundsCopy(patch->bounds, bounds);

	return bounds;	
}

/* returns NULL if no hit between mindist and *maxdist, 'patch' otherwise */
PATCH *PatchIntersect(PATCH *patch, RAY *ray, float mindist, float *maxdist)
{
/* one-sided patch, ray coming from behind ===> the patch is invisible */
	if (!patch->twosided && VECTORDOTPRODUCT(patch->norm, ray->dir)>0.)
		return (PATCH *)NULL;
	else
		return PolygonIntersect(patch, ray, mindist, maxdist);
}

/* given the parameter (u,v) of a point on the patch, this routine 
 * computes the 3D space coordinates of the same point */
POINT *PatchPoint(PATCH *patch, float u, float v, POINT *point)
{
	POINT *v1, *v2, *v3, *v4;

	v1 = patch->vertex[0]->point;
	v2 = patch->vertex[1]->point;
	v3 = patch->vertex[2]->point;

	if (patch->nrvertices == 3) {
		if (u+v > 1.) {
			u=1.-u; v=1.-v;
		/*	Warning("PatchPoint", "(u,v) outside unit triangle"); */
		}
		PINT(*v1, *v2, *v3, u, v, *point);
      	} else if (patch->nrvertices == 4) {
		v4 = patch->vertex[3]->point;
		PINQ(*v1, *v2, *v3, *v4, u, v, *point);
	} else
		Fatal(4, "PatchPoint", "Can only handle triangular or quadrilateral patches");

	return point;
}

/* returns (u,v) parameters of the point on the patch */
void PatchUV(PATCH *poly, POINT *point, float *u, float *v)
{
	static PATCH *cached, *thepoly;
	VEC2D uv;

	thepoly = poly ? poly : cached;

	switch (thepoly->nrvertices) {
	case 3:
		TriangleUV(thepoly, point, &uv);
		break;
	case 4:
		QuadUV(poly, point, &uv);
		break;
	default:
		Fatal(4, "PolygonUV", "Can only handle triangular or quadrilateral patches");
	}

	cached = thepoly;

	*u = uv.u;
	*v = uv.v;
}

static PATCHLIST *TriangleRefine(PATCH *patch)
{
	POINT t1, t2, t3, v1, v2, v3;
	VECTOR n1, n2, n3, k1, k2, k3;
       	VERTEX *m1, *m2, *m3;
	RGB c1, c2, c3;

	v1 = *(patch->vertex[0]->point);
	v2 = *(patch->vertex[1]->point);
	v3 = *(patch->vertex[2]->point);
       	MIDPOINT(v1, v2, t3); 
       	MIDPOINT(v2, v3, t1); 
       	MIDPOINT(v3, v1, t2); 

	k1 = *(patch->vertex[0]->normal);
	k2 = *(patch->vertex[1]->normal);
	k3 = *(patch->vertex[2]->normal);
       	VECTORSUM(k1, k2, n3); VECTORNORMALIZE(n3);
       	VECTORSUM(k2, k3, n1); VECTORNORMALIZE(n1);
       	VECTORSUM(k3, k1, n2); VECTORNORMALIZE(n2);

	RadianceToRGB(patch->basis->GetValueAtPoint(patch->radiance, 0.5, 0.0), &c1);
	RadianceToRGB(patch->basis->GetValueAtPoint(patch->radiance, 0.5, 0.5), &c2);
	RadianceToRGB(patch->basis->GetValueAtPoint(patch->radiance, 0.0, 0.5), &c3);

       	m1 = SurfaceInstallVertex(patch->surface, &t1, &n1, &c1);
       	m2 = SurfaceInstallVertex(patch->surface, &t2, &n2, &c2);
       	m3 = SurfaceInstallVertex(patch->surface, &t3, &n3, &c3);

	patch->subpatches = PatchListCreate();
/* don't change the order in which vertices are used without knowing very well
 * what you are doing ... we assume this order of subpatches for 1) T-vertex elimination
 * 2) push-pull operations with certain sets of basisfunctions. */
       	patch->subpatches = PatchListAdd(patch->subpatches, PatchCreate(3, patch->vertex[0], m3, m2, (VERTEX *)NULL, patch->twosided));
       	patch->subpatches = PatchListAdd(patch->subpatches, PatchCreate(3, patch->vertex[1], m1, m3, (VERTEX *)NULL, patch->twosided));
       	patch->subpatches = PatchListAdd(patch->subpatches, PatchCreate(3, patch->vertex[2], m2, m1, (VERTEX *)NULL, patch->twosided));
       	patch->subpatches = PatchListAdd(patch->subpatches, PatchCreate(3, m1, m2, m3, (VERTEX *)NULL, patch->twosided));
	
	return patch->subpatches;
}

static PATCHLIST *QuadrRefine(PATCH *patch)
{
	POINT t1, t2, t3, t4, t, v1, v2, v3, v4;
	VECTOR n1, n2, n3, n4, nt, k1, k2, k3, k4;
       	VERTEX *m1, *m2, *m3, *m4, *m;
	RGB c1, c2, c3, c4, cm;

	v1 = *(patch->vertex[0]->point);
	v2 = *(patch->vertex[1]->point);
	v3 = *(patch->vertex[2]->point);
	v4 = *(patch->vertex[3]->point);
       	MIDPOINT(v1, v2, t1); 
       	MIDPOINT(v2, v3, t2); 
       	MIDPOINT(v3, v4, t3); 
       	MIDPOINT(v4, v1, t4); 
	MIDPOINT(t1, t3, t);

	k1 = *(patch->vertex[0]->normal);
	k2 = *(patch->vertex[1]->normal);
	k3 = *(patch->vertex[2]->normal);
	k4 = *(patch->vertex[3]->normal);
       	VECTORSUM(k1, k2, n1); VECTORNORMALIZE(n1);
       	VECTORSUM(k2, k3, n2); VECTORNORMALIZE(n2);
       	VECTORSUM(k3, k4, n3); VECTORNORMALIZE(n3);
       	VECTORSUM(k4, k1, n4); VECTORNORMALIZE(n4);
       	VECTORSUM(n1, n3, nt); VECTORNORMALIZE(nt);

	RadianceToRGB(patch->basis->GetValueAtPoint(patch->radiance, 0.5, 0.0), &c1);
	RadianceToRGB(patch->basis->GetValueAtPoint(patch->radiance, 1.0, 0.5), &c2);
	RadianceToRGB(patch->basis->GetValueAtPoint(patch->radiance, 0.5, 1.0), &c3);
	RadianceToRGB(patch->basis->GetValueAtPoint(patch->radiance, 0.0, 0.5), &c4);
	RadianceToRGB(patch->basis->GetValueAtPoint(patch->radiance, 0.25, 0.25), &cm);

       	m1 = SurfaceInstallVertex(patch->surface, &t1, &n1, &c1);
       	m2 = SurfaceInstallVertex(patch->surface, &t2, &n2, &c2);
       	m3 = SurfaceInstallVertex(patch->surface, &t3, &n3, &c3);
       	m4 = SurfaceInstallVertex(patch->surface, &t4, &n4, &c4);
       	m  = SurfaceInstallVertex(patch->surface, &t , &nt, &cm);

	patch->subpatches = PatchListCreate();
/* don't change the order in which vertices are used without knowing very well
 * what you are doing ... */
       	patch->subpatches = PatchListAdd(patch->subpatches, PatchCreate(4, patch->vertex[0], m1, m, m4, patch->twosided));
       	patch->subpatches = PatchListAdd(patch->subpatches, PatchCreate(4, patch->vertex[1], m2, m, m1, patch->twosided));
       	patch->subpatches = PatchListAdd(patch->subpatches, PatchCreate(4, patch->vertex[2], m3, m, m2, patch->twosided));
       	patch->subpatches = PatchListAdd(patch->subpatches, PatchCreate(4, patch->vertex[3], m4, m, m3, patch->twosided));
	
	return patch->subpatches;
}

static void PatchConnectChild(PATCH *parent, PATCH *child)
{
	child->parent = parent;
	child->surface = parent->surface;
}

PATCHLIST *PatchRefine(PATCH *patch)
{
	if (patch->subpatches == (PATCHLIST *)NULL) {
		switch (patch->nrvertices) {
			case 3: TriangleRefine(patch); break;
			case 4: QuadrRefine(patch); break;
			default:
				Fatal(4, "PatchRefine", 
					 "Can only handle triangular or quadrilateral patches");
		}

		PatchListIterate1B(patch->subpatches, PatchConnectChild, patch);
		patch->basis->PushRadianceImportance(patch);

		ToplevelPatch(patch)->updated = TRUE;	
	}

	return patch->subpatches;
}

/* returns toplevel patch to which element belongs */
PATCH *ToplevelPatch(PATCH *patch)
{
	while (patch->parent)
		patch = patch->parent;

	return patch;
}

/* transforms the parameters (u,v) on the parent patch to the
 * parameter of the same point on the subpatch with given index --
 * indices must be consistent with the order by which subpatches are
 * added to the subpatches list of a patch in QuadRefine() and
 * TrangleRefine() */
void TransformToChild(PATCH *patch, float *u, float *v, int subpatchidx)
{
	float u2=0., v2=0.;

	switch (patch->nrvertices) {
	case 3:
		switch (subpatchidx) {
		case 0:
			u2 = 1. - 2. * (*u); v2 = 1. - 2. * (*v);
			break;
		case 1:
			u2 = 2. - 2. * (*u + *v); v2 = 2. * (*u);
			break;
		case 2:
			u2 = 2. * (*v); v2 = 2. - 2. * (*u+*v);
			break;
		case 3:
			u2 = 2. * (*u); v2 = 2. * (*v);
			break;
		default:
			Fatal(3, "TransformToChild", "Invalid subpatch index %d", subpatchidx);
		}
		break;
	case 4:
		switch (subpatchidx) {
		case 0:
			u2 = 2. * (1. - (*v)); v2 = 2. * (*u);
			break;
		case 1:
			u2 = 2. * (1. - (*u)); v2 = 2. * (1. - (*v));
			break;
		case 2:
			u2 = 2. * (*v); v2 = 2. * (1. - (*u));
			break;
		case 3:
			u2 = 2. * (*u); v2 = 2. * (*v);
			break;
		default:
			Fatal(3, "TransformToChild", "Invalid subpatch index %d", subpatchidx);
		}
		break;
	default:
		Fatal(3, "TransformToChild", "Can only handle triangular or quadrilateral patches");
	}

	*u = u2; *v = v2;
}

/* returns the lowest level patch at point (u,v) on the given patch.
 * Replaces (u,v) with the parameters of the corresponding point on the 
 * lowest level patch */
PATCH *LowestLevelPatch(PATCH *patch, float *u, float *v)
{
	int subpatchidx;
	PATCH *subpatch;
	PATCHLIST *subpatches;

	if (!patch->subpatches)
		return patch;

	switch (patch->nrvertices) {
	case 3:
		if (*u + *v <= 0.5)
			subpatchidx = 3;
		else if (*u > 0.5)
			subpatchidx = 2;
		else if (*v > 0.5) 
			subpatchidx = 1;
		else 
			subpatchidx = 0;
		break;
	case 4:
		if (*v >= 0.5) {
			if (*u < 0.5)
				subpatchidx = 0;
			else
				subpatchidx = 1;
		} else {
			if (*u >= 0.5)
				subpatchidx = 2;
			else
				subpatchidx = 3;
		}
		break;
	default:
		Fatal(3, "LowestlevelPatch", "Can only handle triangular or quadrilateral patches");
		return (PATCH *)NULL;
	}

	TransformToChild(patch, u, v, subpatchidx);

	subpatches = patch->subpatches; 
	do {
		subpatch = PatchListNext(&subpatches);
		subpatchidx --;
	} while (subpatchidx >= 0);

	return LowestLevelPatch(subpatch, u, v);
}
