/* constant.c: constant radiosity approximation basis */

#include "basis.h"

static double f1(double u, double v)
{
	return 1.0;	
}

static double n1(PATCH *patch)
{
	return patch->area;
}

static void SetConstant(COLOR *coeff, COLOR *color)
{
	coeff[0] = *color;
}

static void AddConstant(COLOR *coeff, COLOR *color)
{
	COLORADD(coeff[0], *color, coeff[0]);
}

static COLOR GetValueAtVertex(COLOR *coeff, int whichvertex)
{
	return coeff[0];
}

static COLOR GetValueAtPoint(COLOR *coeff, double u, double v)
{
	return coeff[0];
}

static COLOR GetPower(PATCH *patch)
{
	COLOR rad;
	COLORSCALE(M_PI*patch->area, patch->radiance[0], rad);
	return rad;
}

static COLOR GetUnshotPower(PATCH *patch)
{
	COLOR rad;
	COLORSCALE(M_PI*patch->area, patch->unshot_radiance[0], rad);
	return rad;
}

static COLOR GetImportance(PATCH *patch)
{
	COLOR imp;
	COLORSCALE(patch->area, patch->importance[0], imp);
	return imp;
}

static COLOR GetUnshotImportance(PATCH *patch)
{
	COLOR imp;
	COLORSCALE(patch->area, patch->unshot_importance[0], imp);
	return imp;
}


static void PushRadianceImportance(PATCH *P)
{
	PATCH *child;
	PATCHLIST *subpatches;

	subpatches = P->subpatches;
	while ((child = PatchListNext(&subpatches))) {
		child->radiance[0] = P->radiance[0];
		child->unshot_radiance[0] = P->unshot_radiance[0];

/* our definition of importance is other than in literature: it
 * is not proprtional to the patches' area. The definition is such
 * that push-pull operations are the same for importance and radiance */
		child->importance[0] = P->importance[0];
		child->unshot_importance[0] = P->unshot_importance[0];
	}
}

static COLOR GatherPushPullRadianceRecursive(PATCH *P, COLOR Bdown)
{
	COLOR Bup, Btmp, R;
	PATCH *Psub;
	PATCHLIST *Psubpatches;

	COLORSCALEINVERSE(P->area, P->received_radiance[0], R); 

	if (!P->subpatches) {
/* Ed is energy per surface area (= radiosity, dimensions [Watts/m^2]) Divide
 * by PI to obtain radiance from radiosity. The division by the number of
 * color channels is because we normalize colors such that Ed = (100,100,100)
 * is a white lightsource of 100W/m^2 */
		COLORADDSCALED(Bdown, 1./(COLORNRCHANNELS*M_PI), P->surface->material->Ed, Bdown);
		COLORADD(Bdown, R, Bup);
	} else {
		COLORSET(Bup, 0., 0., 0.);
		COLORADD(Bdown, R, Bdown);

		Psubpatches = P->subpatches;
		while ((Psub = PatchListNext(&Psubpatches))) {
			Btmp = GatherPushPullRadianceRecursive(Psub, Bdown);
			COLORADDSCALED(Bup, Psub->area/P->area, Btmp, Bup);
		}
	}

	P->radiance[0] = Bup;
	COLORSET(P->received_radiance[0], 0., 0., 0.);
	return Bup;
}

static void GatherPushPullRadiance(PATCH *P)
{
	COLOR Bdown = {0., 0., 0.};
	GatherPushPullRadianceRecursive(P, Bdown);
}

static COLOR GatherPushPullImportanceRecursive(PATCH *P, COLOR Idown)
{
	COLOR Iup, Itmp, R;
	PATCH *Psub;
	PATCHLIST *Psubpatches;

	COLORSCALEINVERSE(P->area, P->received_importance[0], R); 
	COLORADDCONSTANT(R, P->direct_importance, R);

	if (!P->subpatches) {
		COLORADD(Idown, R, Iup);
	} else {
		COLORSET(Iup, 0., 0., 0.);
		COLORADD(Idown, R, Idown);

		Psubpatches = P->subpatches;
		while ((Psub = PatchListNext(&Psubpatches))) {
			Itmp = GatherPushPullImportanceRecursive(Psub, Idown);
			COLORADDSCALED(Iup, Psub->area/P->area, Itmp, Iup);
		}
	}

	P->importance[0] = Iup;
	COLORSET(P->received_importance[0], 0., 0., 0.);
	return Iup;
}

static void GatherPushPullImportance(PATCH *P)
{
	COLOR Idown = {0., 0., 0.};
	GatherPushPullImportanceRecursive(P, Idown);
}

/* see eg Cohen en Wallace, Radiosity and Realistic Image Synthesis p 182 */
static COLOR ShootPushPullRadianceRecursive(PATCH *P, COLOR Bdown)
{
	COLOR Bup, Btmp, R;
	PATCH *Psub;
	PATCHLIST *Psubpatches;

	COLORSCALEINVERSE(P->area, P->received_radiance[0], R);

	if (!P->subpatches) 
		COLORADD(R, Bdown, Bup);
	else {
		COLORSET(Bup, 0., 0., 0.);
		COLORADD(R, Bdown, Bdown);

		Psubpatches = P->subpatches;
		while ((Psub = PatchListNext(&Psubpatches))) {
			Btmp = ShootPushPullRadianceRecursive(Psub, Bdown);
			COLORADDSCALED(Bup, Psub->area/P->area, Btmp, Bup);
		}
	}

	COLORADD(P->radiance[0], Bup, P->radiance[0]);
	COLORADD(P->unshot_radiance[0], Bup, P->unshot_radiance[0]);
	COLORSET(P->received_radiance[0], 0., 0., 0.);
	return Bup;
}

static void ShootPushPullRadiance(PATCH *P)
{
	COLOR Bdown = {0., 0., 0.};
	ShootPushPullRadianceRecursive(P, Bdown);
}

static COLOR ShootPushPullImportanceRecursive(PATCH *P, COLOR Idown)
{
	COLOR Iup, Itmp, R;
	PATCH *Psub;
	PATCHLIST *Psubpatches;

	COLORSCALEINVERSE(P->area, P->received_importance[0], R);

	if (!P->subpatches) 
		COLORADD(R, Idown, Iup);
	else {
		COLORSET(Iup, 0., 0., 0.);
		COLORADD(R, Idown, Idown);

		Psubpatches = P->subpatches;
		while ((Psub = PatchListNext(&Psubpatches))) {
			Itmp = ShootPushPullImportanceRecursive(Psub, Idown);
			COLORADDSCALED(Iup, Psub->area/P->area, Itmp, Iup);
		}
	}

	COLORADD(P->importance[0], Iup, P->importance[0]);
	COLORADD(P->unshot_importance[0], Iup, P->unshot_importance[0]);
	COLORSET(P->received_importance[0], 0., 0., 0.);
	return Iup;
}

static void ShootPushPullImportance(PATCH *P)
{
	COLOR Idown = {0., 0., 0.};
	ShootPushPullImportanceRecursive(P, Idown);
}

/* adds 'diff' to the unshot and total radiance at all levels of the hierarchy */
static void PushPullEmissivityChange(PATCH *P, COLOR diff)
{
	PATCH *Psub;
	PATCHLIST *Psubpatches;

	Psubpatches = P->subpatches;
	while ((Psub = PatchListNext(&Psubpatches))) 
		PushPullEmissivityChange(Psub, diff);

	COLORADD(P->radiance[0], diff, P->radiance[0]);
	COLORADD(P->unshot_radiance[0], diff, P->unshot_radiance[0]);
}

static COLOR PushPullReflectivityChangeRecursive(PATCH *P, COLOR delrho)
{
	COLOR Bup, R, E;

	if (!P->subpatches) {
		COLORSCALEINVERSE(COLORNRCHANNELS*M_PI, P->surface->material->Ed, E);
		COLORSUBSTRACT(P->radiance[0], E, R);
		COLORPROD(delrho, R, Bup);
	} else {
		PATCH *Psub;
		PATCHLIST *Psubpatches;
		COLOR Btmp;

		COLORSET(Bup, 0., 0., 0.);
		Psubpatches = P->subpatches;
		while ((Psub = PatchListNext(&Psubpatches))) {
			Btmp = PushPullReflectivityChangeRecursive(Psub, delrho);
			COLORADDSCALED(Bup, Psub->area/P->area, Btmp, Bup);
		}
	}

	COLORADD(P->radiance[0], Bup, P->radiance[0]);
	COLORADD(P->unshot_radiance[0], Bup, P->unshot_radiance[0]);
	return Bup;
}

/* brings a relative change of reflectivity (delrho = (newrho-oldrho)/oldrho) into 
 * account on the lowest level elements and pulls the new radiance and unshot radiance
 * up. We don't care about importance here ... */
static void PushPullReflectivityChange(PATCH *P, COLOR delrho)
{
	PushPullReflectivityChangeRecursive(P, delrho);
}

BASIS constantBasis = {
	"constant basis",	/* description */
	1,	/* size */
	TRUE,	/* orthogonal */
	{f1},	/* the one and only constant basis function */
	{n1},	/* the one and only norm */
        {{n1}}, /* overlap of the basisfunction with itself */
        {1.},	/* inverse of the overlap matrix on the standard domain */
	SetConstant, AddConstant,
	GetValueAtVertex, GetValueAtPoint,
	GetPower, GetUnshotPower, GetImportance, GetUnshotImportance,
	PushRadianceImportance,
	GatherPushPullRadiance, GatherPushPullImportance,
	ShootPushPullRadiance, ShootPushPullImportance,
	PushPullEmissivityChange, PushPullReflectivityChange
};


