/* formfactor.c */
#include <math.h>
#include "formfactor.h"
#include "ray.h"
#include "mymath.h"
#include "drand.h"
#include "shadowcaching.h"
#include "error.h"
#include "cubature.h"

/* returns true if the two patches are (at least partially) facing 
 * each other */
int Facing(PATCH *p, PATCH *q)
{
	int i;
	VECTOR d;

	if (p->twosided || q->twosided)
		return TRUE;

	for (i=0; i<p->nrvertices; i++) {
		VECTORSUBSTRACT(*(p->vertex[i]->point), q->midpoint, d);
		if (VECTORDOTPRODUCT(d, q->norm) >= 0.) 
			return TRUE;
	}

	for (i=0; i<q->nrvertices; i++) {
		VECTORSUBSTRACT(*(q->vertex[i]->point), p->midpoint, d);
		if (VECTORDOTPRODUCT(d, p->norm) >= 0.) 
			return TRUE;
	}

	return FALSE;
}

/* set of cubature rules to be used for formfactor computation and approximation error
 * estimation: different rules for ReCeiVing and EMitting TRIangular or 
 * QUADrilateral patches.  */
static CUBARULE *CRRcvTri=&CRT5, *CRRcvQuad=&CRQ5, *CREmtTri=&CRT5, *CREmtQuad=&CRQ5;

/* passing NULL for a parameter will leave the corresponding rule unchanged */
void SetCubatureRules(CUBARULE *rcvtri, CUBARULE *rcvquad, 
		      CUBARULE *emtri, CUBARULE *emquad)
{
	if (rcvtri)  CRRcvTri = rcvtri;
	if (rcvquad) CRRcvQuad = rcvquad;
	if (emtri)   CREmtTri = emtri;
	if (emquad)  CREmtQuad = emquad;
}

/* evaluates the radiosity kernel (*not* taking into account the reflectivity of
 * the receiving patch) at points x on patch P (receiving patch) and y on
 * patch Q (shooting patch). P, Q and ShadowList (a list of potential occluders)
 * are global parameters - they remain the same for all kernel avaluations between
 * each pair of patches. Shadowcaching is used to speed up the visibility detection
 * between x and y. The shadow cache should be initialized before doing the first
 * evaluation of the kernel between each pair of patches. */
static PATCH *P, *Q;
static GEOMLIST *ShadowList;

static double KernelEval(POINT *x, POINT *y)
{
	double dist, cosp, cosq;
	RAY ray;

	ray.pos = *x;
	VECTORSUBSTRACT(*y, *x, ray.dir);
	dist = VectorNormalize(&ray.dir);
	cosp = VECTORDOTPRODUCT(ray.dir, P->norm);
	cosq = -VECTORDOTPRODUCT(ray.dir, Q->norm);

	if (P->twosided)
		cosp = fabs(cosp);
	if (Q->twosided)
		cosq = fabs(cosq);

	if ((cosp > 0. && cosq > 0.) &&
	    (!ShadowList || !ShadowTestGeomlist(&ray, ShadowList, dist-EPSILON))) {
		return cosp * cosq / (M_PI * dist * dist);
	} else
		return 0.;
}

/* computes the coupling coefficient(s) (formfactors) between patches i and j: 
 * - i should be the patch receiving light from j. 
 * - shadowlist is a list of potential occluders (maybe the whole
 * scene, but only a fraction of it when doing shaftculling). 
 * - fills in the coupling coefficient(s) in float *FF.
 * - Returns (part of) an
 * approximation error estimate over the would-be link. The other needed parts
 * are the radiosity on the emitting patch and reflectivity of the receiving
 * patch. They are not taken into account here, we only return some information
 * concerning the position, orientation, ... of the patches. It is the only data
 * that remains unchanged during the computations (just like the formfactors
 * themselves): reflectivities, radiosities ... can change. 
 */
Float AreaToAreaFormFactor(PATCH *i, PATCH *j, GEOMLIST *shadowlist, 
			   Float FF[BASISMAXSIZE][BASISMAXSIZE], int *vis)
{
	int k, l, m, n;
	CUBARULE *cri=&CRQ5, *crj=&CRQ5;
	static POINT x[CUBAMAXNODES], y[CUBAMAXNODES];
	static double J_i[CUBAMAXNODES], J_j[CUBAMAXNODES];
	double G, Gx, Gxy, Gmax, Gmin;

/* visibility information: zero if the patches don't interact */
	*vis = 0;

/* planar patches don't interact with themselves and may cause problems if not handled
 * explicitely here */
	if (i == j) 
		return 0.;

/* initialize the shadow cache - shadow caching seriously speeds up visibility
 * detection in many cases. */
	InitShadowCache();

/* some data we don't want to pass as parameters to KernelEval()  every time */
	P=i, Q=j; ShadowList=shadowlist;

/* which cubature rules should be used over the receiving patch i ?? */
	switch (i->nrvertices) {
	case 3: cri = CRRcvTri; break;
	case 4: cri = CRRcvQuad; break;
	default:
		Fatal(4, "AreaToAreaFormFactor", "Can only handle triangular and quadrilateral patches");
	}
	n = cri->nrnodes;

/* which cubature rules should be used over the light emitting patch j ?? */
	switch (j->nrvertices) {
	case 3: crj = CREmtTri; break;
	case 4: crj = CREmtQuad; break;
	default:
		Fatal(4, "AreaToAreaFormFactor", "Can only handle triangular and quadrilateral patches");
	}
	m = crj->nrnodes;

/* compute the points x[k] corresponding to the nodes of the cubature rule
 * in the unit square or triangle used to parametrise the receiving patch i.
 * compute also the value of the basisfunctions and the jacobian J_i(u,v) of
 * the parametrisation at each node */
	for (k=0; k<n; k++) {
		PatchPoint(i, cri->u[k], cri->v[k], &x[k]);

		J_i[k] = i->jacobian ? 
			 i->jacobian->A + i->jacobian->B*cri->u[k] + i->jacobian->C*cri->v[k] :
			 i->area;
	}

/* same for the source patch j */
	for (l=0; l<m; l++) {
		PatchPoint(j, crj->u[l], crj->v[l], &y[l]);

		J_j[l] = j->jacobian ? 
			 j->jacobian->A + j->jacobian->B*crj->u[l] + j->jacobian->C*crj->v[l] :
			 j->area;
	}

/* evaluate the radiosity kernel between each pair of cubature nodes on patch i and j,
 * compute the formfactor from it and determine an approximation error estimate from
 * the difference between the maximum and minimum obtained kernel value */
	G = 0.; Gmin = HUGE; Gmax = -HUGE;
	for (k=0; k<n; k++) {
		Gx = 0.;
		for (l=0; l<m; l++) {			
			Gxy = KernelEval(&x[k], &y[l]);
			if (Gxy > 0.)
				(*vis) ++;
			if (Gxy > Gmax) 
				Gmax = Gxy;
			if (Gxy < Gmin) 
				Gmin = Gxy;
			Gx += crj->w[l] * Gxy * J_j[l];
		}
		G += cri->w[k] * Gx * J_i[k];
	}

	FF[0][0] = G;
	return (Gmax - Gmin)/2.;
}



