/*
*  Script
*
*		fresnel.js
*
*  Purpose
*
*		Calculation of the reflection at one or more parallel
*		interfaces between media of specified refractive 
*		indicies of light with a specified angle of incidence
*		(from the normal) on the first interface.
*
*		Calculation is done using Snell's Law and the Fresnel 
*		equations:
*
*			http://en.wikipedia.org/wiki/Fresnel_equations
*			http://en.wikipedia.org/wiki/Snell%27s_law
*
*		All angles in radians.
*
*		Refractive indicies all relative to a suitable medium, typically
*		a vacuum or air.
*
*		Beam powers relative to the total incident beam through the first
*		layer (i.e., incident on the first interface).
*
*  Author
*
*		Ed Davies, 2008-10/12.
*/

/*
*
*  Set up namespace.
*
*/

var uk;

if (!uk)
	uk = {};
if (!uk.me)
	uk.me = {};
if (!uk.me.edavies)
	uk.me.edavies = {};
if (!uk.me.edavies.j2008)
	uk.me.edavies.j2008 = {};
if (!uk.me.edavies.j2008.m10)
	uk.me.edavies.j2008.m10 = {};
if (!uk.me.edavies.j2008.m10.Fresnel)
	uk.me.edavies.j2008.m10.Fresnel = {};

(function() {

/*
*
*  Function
*
*		uk.me.edavies.j2008.m10.Fresnel.snell
*
*  Purpose
*
*		To calculate the refracted beam angle from the normal at an interface
*		given the incident angle and the refractive indicies.
*
*  Result
*
*		The refracted beam angle if it's well defined, undefined if the incoming
*		beam is undefined or if total internal reflection happens.
*
*/
this.snell = function(incident, n1, n2) {

	if (incident == (void 0))
		return void 0;
	
	var t = (n1 / n2) * Math.sin(incident);
	
	if (t > 1.0)
		return void 0;
	else
		return Math.asin(t);
}


/*
*
*  Function
*
*		uk.me.edavies.j2008.m10.Fresnel.fresnel
*
*  Purpose
*
*		Calculate the refraction and reflection at the interfaces between 
*		media.
*
*  Parameters
*
*		incidence		Angle of incidence of the light through the first
*						medium with the first interface in radians from the
*						normal to the interface.
*
*		refractiveIndicies
*						Array of refractive indicies of the materials in 
*						each of the layers.  Only makes sense to call this
*						routine if there are least two values.
*
*		polarization	Object with sBeam and pBeam properties which are 
*						strengths of the light with those polarizations 
*						in the incident beam.
*
*  Result
*
*		An object with two properties, both arrays:
*
*		layers			Objects with details of the light in each of the 
*						layers of material.
*
*		interfaces		Objects with details of the refraction and reflection
*						at each of the interfaces between successive layers.
*						interfaces has one less elements than layers.
*
*/
this.fresnel = function(incidence, refractiveIndicies, polarization) {

	var i, interf, layer, t, diff, sum, t;

	// Create the main result structure.
	var layers = [];
	var interfaces = [];
		
	// Fill in the input information.
	for (i = 0; i < refractiveIndicies.length; i++)
		layers.push({ number: i + 1,
					  refractiveIndex: refractiveIndicies[i] });
		
	layers[0].beamAngle = incidence;
	
	// Set up the beam powers in the first layer.
	// sBeam is polarized perpendicular to the plane of the normal and the 
	// incident ray.
	// pBeam is polarized parallel to that plane.
	// We assume unpolarized light so half of the power is in each polarization
	// and unit power is the incident beam.
	layers[0].sBeam = polarization.sBeam;
	layers[0].pBeam = polarization.pBeam;
	
	// Create the interfaces and stitch them to their associated layers.
	for (i = 0; i < layers.length - 1; i++)
		interfaces.push({
			number: i + 1,
			prevLayer: layers[i],
			nextLayer: layers[i+1]
		});

	// Calculate what happens at each interface.
	for (i = 0; i < interfaces.length; i++) {
	
		interf = interfaces[i];
		
		// Calculate outgoing beam angle by Snell's law.
		interf.nextLayer.beamAngle = this.snell(
											interf.prevLayer.beamAngle,
											interf.prevLayer.refractiveIndex,
											interf.nextLayer.refractiveIndex);
	
		// Further calculation only makes sense if there isn't total internal
		// reflection at this or a previous interface.
		if (interf.nextLayer.beamAngle == (void 0)) {

			// Total internal reflection.
			interf.Rs = 1.0;
			interf.Rp = 1.0;

		} else {
		
			// Work out the reflection coefficients for the two polarizations
			// using Fresnel's equations.
			var cosi = Math.cos(interf.prevLayer.beamAngle);
			var cost = Math.cos(interf.nextLayer.beamAngle);
			var n1 = interf.prevLayer.refractiveIndex;
			var n2 = interf.nextLayer.refractiveIndex;
			
			t = ((n1 * cosi) - (n2 * cost)) / ((n1 * cosi) + (n2 * cost))
			interf.Rs = t * t;

			t = ((n1 * cost) - (n2 * cosi)) / ((n1 * cost) + (n2 * cosi))
			interf.Rp = t * t;
		
		}
		
		// Calculate the reflected powers at the interface.
		interf.reflectedSBeam = interf.prevLayer.sBeam * interf.Rs;
		interf.reflectedPBeam = interf.prevLayer.pBeam * interf.Rp;
		interf.reflectedTotal = interf.reflectedSBeam + interf.reflectedPBeam;
		
		// Calculate the transmitted powers.
		interf.nextLayer.sBeam = interf.prevLayer.sBeam - interf.reflectedSBeam;
		interf.nextLayer.pBeam = interf.prevLayer.pBeam - interf.reflectedPBeam;
	}
	
	// Fill in further information for each of the layers.
	for (i = 0; i < layers.length; i++) {
		layer = layers[i];
		layer.totalBeam = layer.sBeam + layer.pBeam;
	}
		
	// Done.
	return { layers: layers, interfaces: interfaces }
}

}).apply(uk.me.edavies.j2008.m10.Fresnel);

