/* color.h */

#ifndef _COLOR_H_
#define _COLOR_H_

#include <math.h>
#include "Float.h"

/* should be a physically based color model. This color model is used throughout
 * the computations. It is only converted to RGB for rendering . */
typedef struct COLOR {
	Float r, g, b;
} COLOR;

/* 3 color channels in the RGB model which we use in this version of the program.
 * This constant is needed because the data are such that when a user enters a material
 * with emissivity (100,100,100) he wants a white light source emitting 100 W/m^2, not
 * 3*100 W/m^2. This is an assumption that may change however ... */
#define COLORNRCHANNELS 3

/* macro's to manipulate colors: if you want another color model all you should have
 * to do is change the macros below and of course the struct COLOR above */

#define COLORCLEAR(color)	((color).r = (color).g = (color).b = 0.)

#define COLORNULL(color)	((color).r < EPSILON && (color).r > -EPSILON &&	\
			 (color).g < EPSILON && (color).g > -EPSILON &&	\
			 (color).b < EPSILON && (color).b > -EPSILON)
#define COLORSET(color, R, G, B)  ((color).r = (R), (color).g = (G), (color).b = (B))
#define COLORSETMONOCHROME(color, x) ((color).r = (color).g = (color).b = x)

#define COLORPROD(color1, color2, d) ((d).r = (color1).r * (color2).r ,	\
				(d).g = (color1).g * (color2).g ,	\
				(d).b = (color1).b * (color2).b )
#define COLORPRODSCALED(color1, a, color2, d) ((d).r = (color1).r * (a) * (color2).r ,	\
				(d).g = (color1).g * (a) * (color2).g ,	\
				(d).b = (color1).b * (a) * (color2).b )
#define COLORSCALARPRODUCT(color1, color2) ((color1).r * (color2).r +	\
				(color1).g * (color2).g +	\
				(color1).b * (color2).b )
#define COLORADD(color1, color2, d)  ((d).r = (color1).r + (color2).r ,	\
				(d).g = (color1).g + (color2).g ,	\
				(d).b = (color1).b + (color2).b )
#define COLORADDSCALED(color1, a, color2, d)  ((d).r = (color1).r + (a)*(color2).r ,	\
				(d).g = (color1).g + (a)*(color2).g ,	\
				(d).b = (color1).b + (a)*(color2).b )
#define COLORADDCONSTANT(color1, a,  d)  ((d).r = (color1).r + (a),	\
				(d).g = (color1).g + (a),	\
				(d).b = (color1).b + (a) )
#define COLORSUBSTRACT(color1, color2, d)  ((d).r = (color1).r - (color2).r ,	\
				(d).g = (color1).g - (color2).g ,	\
				(d).b = (color1).b - (color2).b )
#define COLORSUBSTRACTSCALED(color1, a, color2, d)  ((d).r = (color1).r - (a)*(color2).r , \
				(d).g = (color1).g - (a)*(color2).g ,	\
				(d).b = (color1).b - (a)*(color2).b )
#define COLORSCALE(a, color, d)    ((d).r = (a)*(color).r , \
				(d).g = (a)*(color).g , \
				(d).b = (a)*(color).b)
#define COLORSCALEINVERSE(a, color, d)    ((d).r = (color).r/(a) , \
				(d).g = (color).g/(a) , \
				(d).b = (color).b/(a))
#define COLORMAXCOMPONENT(color)	(\
			(color).g > (color).r ? ((color).b > (color).g ? (color).b : (color).g) : (\
					((color).b > (color).r ? (color).b : (color).r)))
#define COLORSUMABSCOMPONENTS(color)	(fabs((color).r) + fabs((color).b) + fabs((color).g))
#define COLORABS(color, abs)	{(abs).r = fabs((color).r); \
				 (abs).g = fabs((color).g); \
				 (abs).b = fabs((color).b);}

/* computes the relative (to the old) difference of the two colors new and 
 * old */
#define COLORRELATIVEDIFFERENCE(new, old, del)	{			\
		del.r = old.r < EPSILON ? 0.0 : (new.r-old.r)/old.r;	\
		del.g = old.g < EPSILON ? 0.0 : (new.g-old.g)/old.g;	\
		del.b = old.b < EPSILON ? 0.0 : (new.b-old.b)/old.b;	\
					 }

#include <stdio.h>
#define COLORPrint(fp, color) 	fprintf(fp, "%g %g %g", (color).r, (color).g, (color).b);


/* colors in RGB, the colormodel used by the rendering subsystem (my display
 * wants RGB, 3 floats) */
typedef struct RGB {
	float r, g, b;
} RGB;

/* macro's to manipulate RGB colors for the display */
#define RGBCLIP(color)		(\
			(color).r = ((color).r < 0. ? 0. : ((color).r > 1. ? 1. : (color).r)), \
			(color).g = ((color).g < 0. ? 0. : ((color).g > 1. ? 1. : (color).g)), \
			(color).b = ((color).b < 0. ? 0. : ((color).b > 1. ? 1. : (color).b)))
#define RGBGAMMACORRECT(color, gamma)  (\
			(color).r = (gamma==1. ? (color).r : pow((color).r, 1./gamma)), \
			(color).g = (gamma==1. ? (color).g : pow((color).g, 1./gamma)), \
			(color).b = (gamma==1. ? (color).b : pow((color).b, 1./gamma)))

#define RGBPrint(fp, color) 	fprintf(fp, "%g %g %g", (color).r, (color).g, (color).b);

/* some predefined colors */
extern RGB Black, White, Green, Yellow, Red, Magenta, Blue, Turquoise;


/* set radiance rescale factor: should be average maximum color component of the colors
 * to be rescaled - depends on the model for the human eyes sensistivity that is being used,
 * we employ a very simple model. */
extern void SetRadianceRescaleFactor(Float factor);

/* does radiance rescaling - simulates the light sensitivity of the human visual system:
 * Radiances have a large dynamic range in which the sensitivity of the human visual
 * system is not linear. The dynamic range of a computer display is however quite
 * limited and the sensitivity of the eye is in good approximation linear over that
 * range. So we have to simulate the sensitivity of the human eye, mapping the large 
 * synamic range of computed radiances to the limited range that can be displayed
 * on the screen. */
extern COLOR *RescaleRadiance(COLOR in, COLOR *out);

/* converts physically based COLORs to colors in the RGB model */
extern RGB *ColorToRGB(COLOR color, RGB *rgb);

/* sets gamma correction factor to be used */
extern void RGBSetGamma(float gam);

/* does gamma correction for the display being used */
extern RGB *RGBGammaCorrect(RGB *rgb);

/* converts radiance to RGB. Three stages: first RescaleRadiance(), than ColorToRGB()
 * finally RGBGammaCorrect() iow, this routine does everything to map colors as given
 * in our physically based color model to RGB colors ready for display on the screen
 * being used. */
extern RGB *RadianceToRGB(COLOR radiance, RGB *rgb);

#endif /*_COLOR_H_*/
