// Texture, Finish, and Color objects. By John Haggerty (Slime).
// http://www.slimeland.com/
// Created January 21, 2003

// requires vector.js.

// TEXTURE OBJECT

function Texture(color,finish,pigment) // pigment is optional and should be a function that returns a color. The color argument is then ignored.
{
	if (color) this.color = color;
	else this.color = new Color(1,1,1);
	if (finish)	this.finish = finish;
	else this.finish = new Finish();
	if (pigment) this.pigment = pigment;
	this.transform = Transformation.IdentityTrans.copy();
}
Texture.prototype.copy = function() {
	var toreturn;
	if (this.pigment)
		toreturn = new Texture(this.color.copy(),this.finish.copy(),new Function(this.pigment));
	else
		toreturn = new Texture(this.color.copy(),this.finish.copy());
	toreturn.transform = this.transform.copy();
	return toreturn;
}
Texture.prototype.colorAt = function(pos)
{
	if (this.pigment) {
		var postransformed = pos.transformed(this.transform.inverse);
		try {
			var toreturn = this.pigment(postransformed);
		}
		catch(err) {
			if (!confirm('Error in pigment function when evaluated at ' + postransformed + ': \n' + err.message + '\n Substituting black as the return value. Continue render?'))
				errored = true;
			toreturn = new Color(0,0,0);
		}
		return toreturn;
	}
	else return this.color;
}

// FINISH OBJECT

function Finish(specular,glossiness,reflection,ambient,diffuse)
{
	if (typeof(specular) == 'undefined') this.specular = 0;
	else this.specular = specular;
	if (typeof(glossiness) == 'undefined') this.glossiness = 10;
	else this.glossiness = glossiness;
	if (typeof(reflection) == 'undefined') this.reflection = 0;
	else this.reflection = reflection;
	if (typeof(ambient) == 'undefined') this.ambient = Texture.defaultambientlight;
	else this.ambient = ambient;
	if (typeof(diffuse) == 'undefined') this.diffuse = 1-Texture.defaultambientlight;
	else this.diffuse = diffuse;
}
Finish.prototype.copy = function() {
	return new Finish(this.specular,this.reflection,this.ambient,this.diffuse);
}

// COLOR OBJECT

function Color(red,green,blue)
{
	this.red = red;
	this.green = green;
	this.blue = blue;
}
Color.prototype.copy = function() {
	return new Color(this.red,this.green,this.blue);
}
Color.prototype.clip = function() {
	/* // my attempt at good looking clipping.
	alllight = this.red + this.green + this.blue;
	excesslight = alllight - 3;
	if (excesslight > 0) {
		this.red = this.red + excesslight*(this.red/(alllight));
		this.green= this.green + excesslight*(this.green/(alllight));
		this.blue = this.blue + excesslight*(this.blue/(alllight));
	}*/
	if (this.red > 1) this.red = 1;
	if (this.green > 1) this.green = 1;
	if (this.blue > 1) this.blue = 1;
	if (this.red < 0) this.red = 0;
	if (this.green < 0) this.green = 0;
	if (this.blue < 0) this.blue = 0;
}
Color.twochars = function(num)
{
	num = '' + num;
	if (num.length == 1) return '0' + num;
	return num;
}
Color.prototype.gethtml = function() {
	r = Math.round(this.red*255);
	g = Math.round(this.green*255);
	b = Math.round(this.blue*255);
	return '#' + Color.twochars(r.toString(16)) + Color.twochars(g.toString(16)) + Color.twochars(b.toString(16));
};
Color.prototype.toString = function() {return this.gethtml();}
// Color.difference is not subtraction.
Color.difference = function(col1,col2) {return Math.abs(col1.red-col2.red) + Math.abs(col1.green-col2.green) + Math.abs(col1.blue-col2.blue);}

Color.prototype.neg = function() {return new Color(-this.red,-this.green,-this.blue);}

Color.mult = function(c1,c2) {return new Color(c1.red*c2.red,c1.green*c2.green,c1.blue*c2.blue);}
Color.scalar = function(c1,s) {return new Color(c1.red*s,c1.green*s,c1.blue*s);}
Color.add = function(c1,c2) {return new Color(c1.red+c2.red,c1.green+c2.green,c1.blue+c2.blue);}

Texture.defaultambientlight = .2;
Texture.defaulttex = new Texture(new Color(1,1,1), new Finish(0,0));
