README


------------------------------------------------------------------------------
	"Illumination and Color in Computer Generated Imagery"
	Source for code given in the Appendices
	(c)Roy Hall - 1988,1989
------------------------------------------------------------------------------

	This code was developed to test and demonstrate concepts
	discussed in "Illumination and Color for Computer Generated
	Imagery" (1989 from Springer-Verlag).  While the code was
	used in the generation of the diagrams and images presented
	in the text, it may still contain bugs.  The user is cautioned
	to independently verify that the code is suitable to his/her
	needs.  This code is distributed on an "as is" basis with
	all faults and without any implied or expressed warranties
	or support from either the author, Roy Hall, or from
	Springer-Verlag Publishers.

	I am making this code available to the user community.  You
	are free to use it as you wish.  In return I request two
	things.  First, that this README file always stays with any
	copy of the source, more specifically, that proper credit is
	given to the original author.  Second, that any corrections,
	improvements, etc. are passed back to the author for inclusion
	in subsequent distribution and editions of the book.

	Please note that the code was written for clarity of presentation,
	not for performance.  You will find that optimizations can be
	easily made.  Please do not report these as I will not incorporate
	them into future releases.  Note also that this code relects
	current work I am involved in and may differ slightly from
	that presented in the text.

	There have been some additions to the distribution (16 July 90):
		Corrections:    list of corrections for the book and
				the code
		Slectral/:      a directory containing a few spectral
				curves to help get you started

	Roy Hall
	Program of Computer Graphics
	120 Rand Hall
	Cornell University
	Ithaca, NY 14853

	roy@wisdom.graphics.cornell.edu





	Contents:
		README
		Corrections
		geo.h
		geo.c
		F.h
		F.c
		D.h
		D.c
		G.h
		G.c
		clr.h
		clr.c
		clr_clip.c
		clr_sample.c
		illum_mod.h
		illum_mod.c
		Spectral/


ome additions to the distribution (16 July 90):
		Corrections:    list of corrections for the book and
				the code
		Slectral/:      a directory containing a few spectral
				curves to help get you started

	Roy Hall
	Program of Computer Graphics
	120 Rand Hall
	Cornell University
	Ithaca, NY 14853

	roy@wisdom.graphics.cornell.edu





	Contents:
		README
		Corrections
		geo.h
		geo.c
		F.h
		F.c
		Corrections
-----------------------------------------------------------------
	Corrections since the first edition of the book
-----------------------------------------------------------------

    Confusion about emissivity -- this usually refers to energy,
    I mean intensity.

	p.40    - last line in 'Emissive Illumination',
		    'emissivity' should read 'emissive intensity'

	p.40    - 2nd line in 'A Generalized Illumination Expression',
		    'emissivity' should read 'emissive intensity'

	p.40    - footnote 18 should read:
		    Emissive intensity refers to the intensity
		    leaving the surface in a given direction at
		    any wavelength. However, in computer graphics
		    we are typically concerned only with the
		    visible spectrum.

	p.95    - Eq (4.22) - function of lambda is missing for the
		    ambient illumination in the ambient term

	p.99    - 2nd line in 'Radiosity for Diffuse Environments',
		    'emissivity' should read 'emissive intensity'

	p.154   - 'emissivity' should read 'emissive intensity',

		  revised some dependencies -
		    emissive intensity      - add lambda,theta, phi
		    intensity               - add lambda
		    coherent reflectance    - add sigma
		    coherent trans          - add sigma
		    attanuation             - add lambda

		  fix spelling on coherent reflection

	p.275   - Add to the Sancer (1969) reference the journal
		    name: IEEE Transactions on Antennas and
		    Propagation.

	p.280   - 'emissivity' should read 'emissive intensity'

    ----
	p.37    - equation 2.46 -- denomenator should be
			cos(theta sub i)cos(theta sub r)

	p.46    - end of second paragraph - 'radial position' should
		    read 'circumferential position'

	p.116   - 1st para, Rogers ref should be Rogers 1985

	p.120   - 2nd para, chapter reference 3.3, Color Spaces ...
		    should be Section 3.5, Color Spaces ...

	p.125   - sect 5.1.3, 2nd par, 1st line:
		    add 'and' after  'primaries of the monitor'

	p.125   - sect 5.1.3, 3rd par (after bullets), 1st line:
		    'results negative values' should be
		    'results in negative values'

	p.126   - 6th para, 5th line:
		    'maintains hue and saturation and modifies
		    the chromaticity' should read
		    'maintains hue and saturation and modifies
		    the intensity'

	p.127   - 3rd par, 3rd line:
		    'image bands of represent' should read
		    'image bands of color represent'

	p.133   - There is an error in the RGB to YIQ matrix.
		    it should be:
			 0.299     0.587      0.114
			 0.596    -0.275     -0.321
			 0.212    -0.523      0.311

		- footnote 15 -- missing '(' before the R
		    in Y(R-Y)(B-Y)

	p.249   - 5th para:
		    'plane that passes through the block point' to
		    'plane that passes through the black point'


    problems in the code:
	p.174   - change:
		    *      geo_getv        - generate a vector
		  to:
		    *      geo_line        - generate a line

	p.209   - 2nd line "ignored if a NULL" instead of
		    "ignored is a NULL"

	p.210   - remove 'surface has' in the first line of
		  Notes.

	p.227   - change 'CLR_xyz_lab' to 'CLR_xyz_to_lab'
		  change 'CLR_xyz_luv' to 'CLR_xyz_to_luv'

	p.228   - change rgb_to_yiq matrix (lines 86-89 of clr.c) to:
		     static double       rgb_yiq_mat[3][3] = {
			 {0.299,     0.587,      0.114},
			 {0.596,    -0.275,     -0.321},
			 {0.212,    -0.523,      0.311}};

	p.288   - change the data for 480nm entry in CIEXYZ (line 121
		    of clr.c) to:
	    {480, 0.0956, 0.1390, 0.8130}, {485, 0.0580, 0.1693, 0.6162},

	p.230   - note after XYZ_done label:
		    'Y os a sampled' should be
		    'Y of a sampled'

	p.232   - a routine 'CLR_blackbody' has been inserted
		    after 'CLR_read_mtl' and before 'CLR_add_spect'

	p.243   - The samp_to_ACC matrix is incorrect, should be:
		    static double   samp_to_ACC[3][4] =
			{{0.00000,  0.18892,    0.67493,    0.19253},
			 {0.00000,  0.31824,    0.00000,   -0.46008},
			 {0.54640,  0.00000,    0.00000,    0.00000}};

	p.244   - first line in the CLR_init_samples routine
		  should be:
		    CLR_exit_samples ();

		  immediately before the:
		    if(method == CLR_SAMPLE_MEYER) {

	p.248   - in notes (couple lines down):
		    'resulting a a step' should read
		    'resulting in a step'

	Added a CLR_xyz_to_spec routine that uses a method described
	    by Glassner (How to Derive a Spectrum from an RGB Triplet,
	    IEEE CG&A, July 1989).

    problems in figures:
	p.59    - annotate the start and end of the functions

	p.81    - figure 4.11 the image is reversed (left to right) -
		    when corrected, the left image should be labelled
		    'Gouraud shading' and the right image should be
		    labelled 'Phong shading'

	p.132   - figure 5.12 should be a black and white print

	p.164   - figure II.5 row1, column 3 Cka and Ckd should
		    be 0.35

	p.171   - figure III.2  - the reversed normal caption
		    is wrong -- should be:

		    N'' = -N sqrt(1 - |S'|^2)

		    get rid of the divide by N.V

 end of the functions

	p.81    - figure 4.11 the image is reversed (left to right) -
		    when corrected, the left image should be labellgeo.h
 *                            geo.h
 * ****************************************************************
 * include file for the geometric utilities
 */
#ifndef GEO_H
#define GEO_H

#ifndef TRUE
#define TRUE    1
#endif TRUE
#ifndef FALSE
#define FALSE   0
#endif FALSE

/* common geometric constructs
 */
typedef struct {double  i, j, k;}        DIR_VECT;
typedef struct {double  x, y, z;}        POINT3;
typedef struct {double  x, y, z, h;}     POINT4;
typedef struct {POINT3      start;
		DIR_VECT    dir; }       LINE;

/* geometric manipulation routines
 */
double          geo_dot();      /* vector dot product */
DIR_VECT        geo_cross();    /* vector cross product */
double          geo_norm();     /* vector normalize */
double          geo_line();     /* vector between two points */
DIR_VECT        *geo_rfl();     /* reflected vector */
DIR_VECT        *geo_rfr();     /* refracted vector */
DIR_VECT        *geo_H();       /* H vector */
DIR_VECT        *geo_Ht();      /* H' vector */

#endif GEO_H
/* ************************************************************* */
      geo_dot();      /* vector dot product */
DIR_VECT        geo_cross();    /* vector cross product */
double          geo_norm();     /* vector normalize */
double          geo_line();     /* vector between two points */
DIR_VECT        *geo_rfl();     /* reflected vector */
DIR_VECT        *geo_rfr();     /* refracted vector */
DIR_VECT        *geo_H();       /* H vector */
DIgeo.c
 *                            geo.c
 * ****************************************************************
 *  MODULE PURPOSE:
 *      This module contains routines that preform common
 *      geometric manipulations for image synthesis.
 *
 *  MODULE CONTENTS:
 *      geo_dot         - vector dot product
 *      geo_cross       - vector cross product
 *      geo_norm        - vector normalization
 *      geo_line        - generate a line
 *      geo_rfl         - compute the reflected vector
 *      geo_rfr         - compute the refracted vector
 *      geo_H           - compute H vector
 *      geo_Ht          - compute the H' vector
 */
#include <stdio.h>
#include <math.h>
#include "geo.h"

/* ****************************************************************
 *  double geo_dot (v0, v1)
 *  DIR_VECT    *v0, *v1    (in) normalized direction vectors
 *
 *  Returns the dot product of two direction vectors, ref
 *   Eq.(\*(rK).  Note that the dot product is the cosine
 *   between the direction vectors because the direction
 *   vectors are normalized.
 */
double geo_dot (v0, v1)
DIR_VECT        *v0, *v1;
{   return (v0->i * v1->i) + (v0->j * v1->j) + (v0->k * v1->k);
}

/* ****************************************************************
 *  DIR_VECT geo_cross (v0, v1)
 *   DIR_VECT   *v0, *v1    (in)  normalized direction vectors
 *
 *  Returns the cross product of two direction vectors, refer
 *   to Eq.(\*(rL).
 */
DIR_VECT geo_cross (v0, v1)
DIR_VECT        *v0, *v1;
{   DIR_VECT        v2;
    v2.i = (v0->j * v1->k) - (v0->k * v1->j);
    v2.j = (v0->k * v1->i) - (v0->i * v1->k);
    v2.k = (v0->i * v1->j) - (v0->j * v1->i);
    return v2;
}

/* ****************************************************************
 *  double geo_norm (*v0)
 *   DIR_VECT   *v0         (mod) vector to be normalized
 *
 *  Returns the length of the vector before normalization.
 *   Returns zero if the vector could not be normalized.
 *   Note that the input vector is modified.
 */
double geo_norm (v0)
DIR_VECT        *v0;
{   double          len;
    if ((len = geo_dot(v0, v0)) <= 0.0) return FALSE;
    len = sqrt((double)len);
    v0->i /= len;
    v0->j /= len;
    v0->k /= len;
    return TRUE;
}

/* ****************************************************************
 *  double geo_line (*p0, *p1, *v)
 *   POINT3     *p0, *p1    (in)  from, to points
 *   LINE       *v          (out) generated line
 *
 *  Returns the distance from p0 to p1.  Returns 0.0
 *   on error (p0=p1).  The line is expressed in parametric
 *   form with a start point (p0) and a normalized direction
 *   vector. The vector direction of the line is normalized.
 */
double geo_line (p0, p1, v)
POINT3          *p0, *p1;
LINE            *v;
{   v->start = *p0;
    v->dir.i = p1->x - p0->x;
    v->dir.j = p1->y - p0->y;
    v->dir.k = p1->z - p0->z;
    return geo_norm(&v->dir);
}

/* ****************************************************************
 *  DIR_VECT *geo_rfl (V, N)
 *   DIR_VECT   *V          (in)  incident vector
 *   DIR_VECT   *N          (in)  surface normal
 *
 *  Returns the reflected direction vector.  The reflected
 *   direction is computed using the method given by Whitted
 *   (1980), refer to Eq.(\*(rM).
 */
DIR_VECT *geo_rfl (V, N)
DIR_VECT        *V;
DIR_VECT        *N;
{   double              N_dot_V;
    static DIR_VECT     rfl;
    N_dot_V = geo_dot (N,V);
    rfl.i = (2.0 * N_dot_V * N->i) - V->i;
    rfl.j = (2.0 * N_dot_V * N->j) - V->j;
    rfl.k = (2.0 * N_dot_V * N->k) - V->k;
    return &rfl;
}

/* ****************************************************************
 *  DIR_VECT *geo_rfr (V, N, ni, nt)
 *   DIR_VECT   *V          (in)  incident vector
 *   DIR_VECT   *N          (in)  surface normal
 *   double     ni          (in)  index of refraction for the
 *                              material on the front of the
 *                              interface (same side as N)
 *   double     nt          (in)  index of refraction for the
 *                              material on the back of the
 *                              interface (opposite size as N)
 *
 *  Returns the refracted vector, if there complete internal
 *   refracted (no refracted vector) then a NULL vector is
 *   NULL is returned.  The vector is computed using the
 *   method given by Hall (1983) with enhancements as
 *   described in Appendix III.
 */
DIR_VECT *geo_rfr (V, N, ni, nt)
DIR_VECT        *V;
DIR_VECT        *N;
double          ni;
double          nt;
{   static DIR_VECT T;      /* the refracted vector */
    DIR_VECT    sin_T;      /* sin vect of the refracted vect */
    DIR_VECT    cos_V;      /* cos vect of the incident vect */
    double      len_sin_T;  /* length of sin T squared */
    double      n_mult;     /* ni over nt */
    double      N_dot_V;
    double      N_dot_T;
    if ((N_dot_V = geo_dot(N,V)) > 0.0) n_mult = ni / nt;
    else n_mult = nt / ni;
    cos_V.i = N_dot_V * N->i;
    cos_V.j = N_dot_V * N->j;
    cos_V.k = N_dot_V * N->k;
    sin_T.i = (n_mult) * (cos_V.i - V->i);
    sin_T.j = (n_mult) * (cos_V.j - V->j);
    sin_T.k = (n_mult) * (cos_V.k - V->k);
    if ((len_sin_T = geo_dot(&sin_T, &sin_T)) >= 1.0)
	return NULL;    /* internal reflection */
    N_dot_T = sqrt(1.0 - len_sin_T);
    if (N_dot_V < 0.0) N_dot_T = -N_dot_T;
    T.i = sin_T.i - (N->i * N_dot_T);
    T.j = sin_T.j - (N->j * N_dot_T);
    T.k = sin_T.k - (N->k * N_dot_T);
    return &T;
}

/* ****************************************************************
 *  DIR_VECT *geo_H (L, V)
 *   DIR_VECT   *L          (in) incident vector
 *   DIR_VECT   *V          (in) reflected vector
 *
 *  Returns H, NULL on error (if L+H = 0).  Refer
 *   to Eq.(\*(rO).
 */
DIR_VECT *geo_H (L, V)
DIR_VECT        *L;
DIR_VECT        *V;
{   static DIR_VECT         H;
    H.i = L->i + V->i;
    H.j = L->j + V->j;
    H.k = L->k + V->k;
    if (!geo_norm(&H)) return NULL;
    return &H;
}

/* ****************************************************************
 *  DIR_VECT *geo_Ht(L, T, ni, nt)
 *   DIR_VECT   *L          (in) incident vector
 *   DIR_VECT   *T          (in) transmitted hector
 *   double     ni          (in) incident index
 *   double     nt          (in) transmitted index
 *
 *  Returns H' oriented to the same side of the surface
 *   as L computed using the method suggested by
 *   Hall (1983).   Returns NULL on error (if the angle
 *   between V and L is less than the critical angle).
 */
DIR_VECT *geo_Ht (L, T, ni, nt)
DIR_VECT        *L;
DIR_VECT        *T;
double          ni;
double          nt;
{
    float                   L_dot_T;
    float                   divisor;
    static DIR_VECT         Ht;

    L_dot_T = -(geo_dot(L,T));
    /* check for special cases */
    if (ni == nt) {
	if (L_dot_T == 1.0) return L;
	else return NULL;
    }
    if (ni < nt) {
	if (L_dot_T < ni/nt) return NULL;
	divisor = (nt / ni) - 1.0;
	Ht.i = -(((L->i + T->i) / divisor) + T->i);
	Ht.j = -(((L->j + T->j) / divisor) + T->j);
	Ht.k = -(((L->k + T->k) / divisor) + T->k);
    }
    else
    {   if (L_dot_T < nt/ni) return NULL;
	divisor = (ni / nt) - 1.0;
	Ht.i = ((L->i + T->i) / divisor) + L->i;
	Ht.j = ((L->j + T->j) / divisor) + L->j;
	Ht.k = ((L->k + T->k) / divisor) + L->k;
    }
    (void)geo_norm(&Ht);
    return &Ht;
}
/* ************************************************************* */
 ni/nt) return NULL;
	divisor = (nt / ni) - 1.0;
	Ht.i = -(((L->i + T->i) / divisor) + T->i);
	Ht.j = -(((L->j + T->j) / divisor) + T->j);
	Ht.k = -(((L->k + T->k) / divisor) + T->k);
    }
    else
    {   if (L_dot_T < nt/ni) return NULL;
	divisor F.h
 *                            F.h
 * ****************************************************************
 * include file for the microfacet distribution functions
 */
#ifndef F_H
#define F_H

double  F_d_pl();
double  F_d_pp();
double  F_d_R();
double  F_c_pl();
double  F_c_pp();
double  F_c_R();
double  F_approx_n();
double  F_approx_k();
double  *F_approx_Fr();
double  *F_approx_Fr_Ft();

#endif F_H
/* ************************************************************* */
*****************************************
 *                            F.h
 * ****************************************************************
 * include file for the microfacet distribution functions
 */
#ifndef F_H
#define F_H

double  F_d_pl();
double  F_d_pp();
double  F_d_R();
double  F_c_pl();
double  F_c_pp();
double  F_c_R();
double  F_approx_n();
double  F_approx_k();
double  *F_approx_Fr();
double  *F_approx_Fr_Ft();

#endif F_H
/* ***************************************F.c
 *                            F.c
 * ****************************************************************
 *  MODULE PURPOSE:
 *      This module contains routines to for fresnel calculations.
 *
 *  MODULE CONTENTS:
 *      F_d_pl          - reflectance for a dielectric,
 *                          parallel polarized,
 *      F_d_pp          - reflectance for a dielectric,
 *                          perpendicular polarized
 *      F_d_R           - reflectance for a dielectric
 *      F_c_pl          - reflectance for a conductor,
 *                          parallel polarized,
 *      F_c_pp          - reflectance for a conductor,
 *                          perpendicular polarized
 *      F_c_R           - reflectance for a conductor
 *      F_approx_n      - approximate the average index of
 *                          refraction for a material
 *      F_approx_k      - approximate the average absoption
 *                          index for a material
 *      F_approx_Fr     - approximate conductor reflectance
 *      F_approx_Fr_Ft  - approximate dielectric reflectance
 *                          and transmittance
 *
 *
 *  ASSUMPTIONS:
 *      The following are defined in math.h
 *          HUGE    largest floating pont value
 *          M_PI_2  pi/2
 *          M_PI_4  pi/4
 */
#include <stdio.h>
#include <math.h>
#include "geo.h"
#include "F.h"

/* ****************************************************************
 *  double F_d_pl (N, L, T, ni, nt)
 *   DIR_VECT *N, *L, *T    (in) - N, L, T vectors
 *   double   ni        (in) - incident index of refraction
 *   double   nt        (in) - transmitted index of refraction
 *
 *  Returns the reflectance of a dielectric for light with
 *   polarization parallel to the plane of incidence (plane of the
 *   N and L vector), refer to Eq.(\*(eJ). N and L are assumed to
 *   be to the same side of the surface.
 */
double F_d_pl (N, L, T, ni, nt)
DIR_VECT    *N, *L, *T;
double      ni, nt;
{   double  amplitude, N_dot_L, N_dot_T;
    N_dot_L = geo_dot(N, L);
    N_dot_T = geo_dot(N, T);
    amplitude = ((nt * N_dot_L) + (ni * N_dot_T)) /
		((nt * N_dot_L) - (ni * N_dot_T));
    return amplitude * amplitude;
}

/* ****************************************************************
 *  double F_d_pp (N, L, T, ni, nt)
 *   DIR_VECT *N, *L, *T    (in) - N, L, T vectors
 *   double   ni        (in) - incident index of refraction
 *   double   nt        (in) - transmitted index of refraction
 *
 *  Returns the reflectance of a dielectric for light with
 *   polarization perpendicular to the plane of incidence
 *   (plane of the N and L vector), refer to Eq.(\*(eK). N and L
 *   are assumed to be to the same side of the surface.
 */
double F_d_pp (N, L, T, ni, nt)
DIR_VECT    *N, *L, *T;
double      ni, nt;
{   double  amplitude, N_dot_L, N_dot_T;
    N_dot_L = geo_dot(N, L);
    N_dot_T = geo_dot(N, T);
    amplitude = ((ni * N_dot_L) + (nt * N_dot_T)) /
		((ni * N_dot_L) - (nt * N_dot_T));
    return amplitude * amplitude;
}

/* ****************************************************************
 *  double F_d_R (N, L, T, ni, nt)
 *   DIR_VECT *N, *L, *T    (in) - N, L, T vectors
 *   double   ni        (in) incident index of refraction
 *   double   nt        (in) transmitted index of refraction
 *
 *  Returns the average reflectance for a dielectric.  Refer to
 *   Eq.(\*(eL).  N and L are assumed to be to the same side of the
 *   surface.
 */
double F_d_R (N, L, T, ni, nt)
DIR_VECT    *N, *L, *T;
double      ni, nt;
{   return (F_d_pl (N, L, T, ni, nt) +
	F_d_pp (N, L, T, ni, nt)) / 2.0;
}

/* ****************************************************************
 *  double F_c_pl (N, L, nt, k)
 *   DIR_VECT   *N, *L  (in) - N, L vectors
 *   double     nt      (in) - index of refraction
 *   double     k       (in) - absorption coefficient
 *
 *  Returns the reflectance of a conductor for light with
 *   polarization parallel to the plane of incidence (plane of the
 *   N and L vector), refer to Eq.(\*(eO).  N and L are assumed to
 *   be to the same side of the surface.
 */
double F_c_pl (N, L, nt, k)
DIR_VECT    *N, *L;
double      nt, k;
{   double  tmp_f, N_dot_L;
    N_dot_L = geo_dot(N, L);
    tmp_f = ((nt * nt) + (k * k)) * N_dot_L * N_dot_L;
    return (tmp_f - (2.0 * nt * N_dot_L) + 1) /
	   (tmp_f + (2.0 * nt * N_dot_L) + 1);
}

/* ****************************************************************
 *  double F_c_pp (N, L, nt, k)
 *   DIR_VECT   *N, *L  (in) - N, L vectors
 *   double     nt      (in) - index of refraction
 *   double     k       (in) - absorption coefficient
 *
 *  Returns the reflectance of a conductor for light with
 *   polarization perpendicular to the plane of incidence (plane of
 *   the N and L vector), refer to Eq.(\*(eP). N and L are assumed
 *   to be to the same side of the surface.
 */
double F_c_pp (N, L, nt, k)
DIR_VECT    *N, *L;
double      nt, k;
{   double  tmp_f, N_dot_L;
    N_dot_L = geo_dot(N, L);
    tmp_f = (nt * nt) + (k * k);
    return (tmp_f - (2.0 * nt * N_dot_L) + (N_dot_L * N_dot_L)) /
	   (tmp_f + (2.0 * nt * N_dot_L) + (N_dot_L * N_dot_L));
}

/* ****************************************************************
 *  double F_c_R (N, L, nt, k)
 *   DIR_VECT   *N, *L  (in) - N, L vectors
 *   double     nt      (in) - index of refraction
 *   double     k       (in) - absorption coefficient
 *
 *  Returns the average reflectance for a conductor.  Refer to
 *   Eq.(\*(eL).  N and L are assumed to be to the same side of
 *   the surface.
 */
double F_c_R (N, L, nt, k)
DIR_VECT    *N, *L;
double      nt, k;
{   return (F_c_pl (N, L, nt, k) + F_c_pp (N, L, nt, k))/2.0;
}

/* ****************************************************************
 *  double F_approx_n (Ro, n_samp, mtl)
 *   double     *Ro     (out) - average reflectance
 *   int        n_samp  (in)  - number of material samples
 *   double     *mtl    (in)  - material spectral curve
 *
 *  Returns the approximate n, and loads Ro.  This gives the
 *   correct n for a single mtl reflectance if the material is a
 *   dielectric, and the correct n assuming k=0 for a conductor,
 *   refer to Eq.(\*(eN).
 */
double F_approx_n (Ro, n_samp, mtl)
double      *Ro;
int         n_samp;
double      *mtl;
{   int     ct;
    *Ro = 0.0;
    for (ct=n_samp; --ct>=0; ) *Ro += *mtl++;
    *Ro /= (double)n_samp;
    return (1.0 + sqrt(*Ro)) / (1.0 - sqrt(*Ro));
}

/* ****************************************************************
 *  double F_approx_k (Ro, n_samp, mtl)
 *   double     *Ro     (out) - average reflectance
 *   int        n_samp  (in)  - number of material samples
 *   double     *mtl    (in)  - material spectral curve
 *
 *  Returns the approximate k, and loads Ro.  This assumes n=1.0
 *   and is applicable to conductors only. Refer to Eq.(\*(eQ).
 */
double F_approx_k (Ro, n_samp, mtl)
double      *Ro;
int         n_samp;
double      *mtl;
{   int     ct;
    *Ro = 0.0;
    for (ct=n_samp; --ct>=0; ) *Ro += *mtl++;
    *Ro /= (double)n_samp;
    return 2.0 * sqrt(*Ro / (1.0 - *Ro));
}

/* ****************************************************************
 *  double *F_approx_Fr (N, L, Ro, n, k, n_samp, mtl, Fr)
 *   DIR_VECT   *N, *L  (in) - N, L vectors
 *   double     Ro      (in) - average reflectance
 *   double     n       (in) - measured or approximate index
 *                              of refraction
 *   double     k       (in) - measured or approximate
 *                              absorption coefficient
 *   int        n_samp  (in)  number of material samples
 *   double     *mtl    (in)  material spectral curve
 *   double     *Fr     (out) reflectance
 *
 *  Loads the vector Fr with the approximate reflectance for each
 *   color sample point for a conductor, refer to Eq.(\*(eR).
 *   Returns the pointer to Fr.  The measured values for n and k
 *   should be used if they are available.  Otherwise, assume k=0
 *   and approximate n using F_approx_n(), or assume n=1 and
 *   approximate k using F_approx_k().
 */
double *F_approx_Fr (N, L, Ro, n, k, n_samp, mtl, Fr)
DIR_VECT    *N, *L;
double      Ro, n, k;
int         n_samp;
double      *mtl, *Fr;
{   double      R_theta;    /* ave R for N and L*/
    double      factor, *R_out;
    R_theta = F_c_R (N, L, n, k);
    factor = (R_theta - Ro) / (1.0 - Ro);
    for (R_out=Fr ;--n_samp>=0; Fr++, mtl++)
	if ((*Fr = *mtl + ((1.0 - *mtl) * factor)) < 0.0)
	    *Fr = 0.0;
    return R_out;
}

/* ****************************************************************
 *  double *F_approx_Fr_Ft (N, L, T, Ro, ni, nt,
 *                n_samp, *mtl_p, Fr, Ft)
 *  DIR_VECT    *N, *L, *T  (in) - N, L vectors
 *   double     Ro      (in)  average reflectance
 *   double     ni      (in)  ave n for incident material
 *   double     nt      (in)  ave n for transmitted material
 *   int        n_samp  (in)  number of material samples
 *   double     *mtl_p  (in)  pseudo material curve
 *   double     *Fr     (out) reflectance
 *   double     *Tr     (out) transmittance
 *
 *  Loads the vector Fr with the approximate reflectance and Ft
 *   with the approximate transmittance for each sample point for
 *   a conductor, refer to Eq.(\*(eR).  Returns the pointer to Fr.
 *   The measured value for n should be used if it is available,
 *   otherwise use F_approx_n() to approximate the index of
 *   refraction.
 */
double *F_approx_Fr_Ft (N, L, T, Ro, ni, nt, n_samp, mtl_p, Fr, Ft)
DIR_VECT    *N, *L, *T;
double      Ro, ni, nt;
int         n_samp;
double      *mtl_p, *Fr, *Ft;
{   double      R_theta;    /* ave R for N and L*/
    double      factor, *R_out;

    if (T == NULL)      /* internal reflection */
	for (R_out=Fr; --n_samp>=0; *Fr++ = 1.0, *Ft++ = 0.0) ;
    else {
	R_theta = F_d_R (N, L, T, ni, nt);
	factor = (R_theta - Ro) / (1.0 - Ro);
	for (R_out=Fr; --n_samp>=0; mtl_p++) {
	    if ((*Fr = *mtl_p + ((1.0 - *mtl_p) * factor)) < 0.0)
		*Fr = 0.0;
	    *Ft++ = 1.0 - *Fr++;
	}
    }
    return R_out;
}
/* ************************************************************* */
{   double      R_theta;    /* ave R for N and L*/
    double      factor, *R_out;

    if (T == NULL)      /* internal reflection *D.h
 *                            D.h
 * ****************************************************************
 * include file for the microfacet distribution functions
 */
#ifndef D_H
#define D_H

/* Microfacet distribution routines
 */
double D_phong_init();
double D_phong();
double D_blinn_init();
double D_blinn();
double D_gaussian_init();
double D_gaussian();
double D_reitz_init();
double D_reitz();
double D_cook_init();
double D_cook();
double D_g();
double D_tau();
double D_sigma();
double D_m();
double D_coherent();
double D_incoherent();
double D_Vxz();

#endif D_H
/* ************************************************************* */
crofacet distribution functions
 */
#ifndef D_H
#define D_H

/* Microfacet distribution routines
 */
double D_phong_init();
double D_phong();
double D_blinn_init();
double D_blinn();
double D_gaussian_init();
double D_gaussian();
double D_reitz_init();
double D_reitz();
double D_cook_init();
double D_cook();
doubleD.c
 *                            D.c
 * ****************************************************************
 *  MODULE PURPOSE:
 *    This module contains microfacet distribution routines and
 *    various support routines.  One of support functions includes
 *    computing the coefficients corresponding to beta for each
 *    function (as described in Section 4.3.1-Improved Specular
 *    Reflection).  Beta is the angle between H and N where the
 *    function is equal to half the value at N = H.  Beta is in
 *    radians.
 *
 *  MODULE CONTENTS:
 *    D_phong_init    - compute Ns given beta
 *    D_phong         - Phong cosine power function
 *    D_blinn_init    - compute Ns given beta
 *    D_blinn         - Blinn cosine power function
 *    D_gaussian_init - compute C1 given beta
 *    D_gaussian      - Gaussian distribution
 *    D_reitz_init    - compute c2 given beta
 *    D_reitz         - Trowbridge-Reitz
 *    D_cook_init     - compute m given beta
 *    D_cook          - Cook model
 *    D_g             - compute apparent roughness
 *    D_tau           - compute correlation distance
 *    D_sigma         - compute rms roughness
 *    D_m             - compute slope
 *    D_coherent      - coherent roughness attenuation
 *                        (per Beckmann)
 *    D_incoherent    - incoherent microfacet attenuation
 *                        (per Beckmann and Bahar)
 *    D_Vxy           - Vxy used in D_incoherent()
 *
 *  ASSUMPTIONS:
 *  > The following are defined in math.h
 *          HUGE        largest floating pont value
 *          M_PI        pi
 *          M_PI_2      pi/2
 *          M_PI_4      pi/4
 *          M_SQRT2     root of 2
 *
 *  > The direction vectors N, V, and L are assumed to be oriented
 *      to the same side of the surface.
 */
#include <stdio.h>
#include <math.h>
#include "geo.h"
#include "D.h"

/* ****************************************************************
 * double D_phong_init (beta)
 *  double      beta        (in) - angle between N and
 *                              H where function = .5
 *  Returns Ns given beta for the Phong (1975) specular function,
 *   per footnote to Eq.(\*(rH)
 */
double D_phong_init (beta)
double          beta;
{   if (beta <= 0.0) return HUGE;
    if (beta >= M_PI_4) return 0.0;
    return -(log(2.0) / log(cos(2.0 * beta)));
}

/* ****************************************************************
 * double D_phong (N, L, V, Ns)
 *  DIR_VECT    *N, *L, *V  (in) - N, L, V vectors
 *  double      Ns          (in) - specular exponent
 *
 *  Returns the value of the Phong (1975) specular function given
 *   surface normal, incident light direction, view direction, and
 *   the specular exponent.  Refer to Eqs.(\*(rU) and (\*(rI).
 */
double D_phong (N, L, V, Ns)
DIR_VECT        *N, *L, *V;
double          Ns;
{   double      Rv_dot_L;
    if ((Rv_dot_L = geo_dot(geo_rfl(V,N),L)) < 0.0) return 0.0;
    return pow (Rv_dot_L, Ns);
}

/* ****************************************************************
 * double D_blinn_init (beta)
 *  double      beta        (in) - angle between N and
 *                              H where function = .5
 *  Returns Ns given beta for the cosine power distribution
 *   function presented by Blinn (1977).  Refer to Eq.(\*(rHa)
 */
double D_blinn_init (beta)
double          beta;
{   if (beta <= 0.0) return HUGE;
    if (beta >= M_PI_2) return 0.0;
    return -(log(2.0) / log(cos(beta)));
}

/* ****************************************************************
 * double D_blinn (N, L, V, Ns)
 *  DIR_VECT    *N, *L, *V  (in) - N, L, V vectors
 *  double      Ns          (in) - specular exponent
 *
 *  Returns the cosine power distribution function presented by
 *   Blinn (1977) given surface normal, incident light direction,
 *   view direction, and the specular exponent. Refer to Eq.(\*(e5)
 */
double D_blinn (N, L, V, Ns)
DIR_VECT        *N, *L, *V;
double          Ns;
{   double      N_dot_H;
    if ((N_dot_H = geo_dot(N,geo_H(V,L))) < 0.0) return 0.0;
    return pow (N_dot_H, Ns);
}

/* ****************************************************************
 * double D_gaussian_init (beta)
 *  double      beta        (in) - angle between N and
 *                              H where function = .5
 *  Returns C1 given beta for the Gaussian distribution presented
 *   by Blinn (1977).  Refer to Eq.(\*(rHb)
 */
double D_gaussian_init (beta)
double          beta;
{   if (beta <= 0.0) return HUGE;
    return sqrt(log(2.0)) / beta;
}

/* ****************************************************************
 * double D_gaussian (N, L, V, C1)
 *  DIR_VECT    *N, *L, *V  (in) - N, L, V vectors
 *  double      C1          (in) - shape coefficient
 *
 *  Returns the Gaussian distribution function presented by
 *   Blinn (1977) given surface normal, incident light direction,
 *   view direction, and the specular exponent. Refer to Eq.(\*(eA)
 */
double D_gaussian (N, L, V, C1)
DIR_VECT        *N, *L, *V;
double          C1;
{   double      tmp;
    double      N_dot_H;
    if ((N_dot_H = geo_dot(N,geo_H(V,L))) < 0.0) return 0.0;
    tmp = acos(N_dot_H) * C1;
    return exp (-(tmp * tmp));
}

/* ****************************************************************
 * double D_reitz_init (beta)
 *  double      beta        (in) - angle between N and
 *                              H where function = .5
 *  Returns C2 squared given beta for the Trowbridge-Reitz
 *   distribution function presented by Blinn (1977). Refer to
 *   Eq.(\*(rHc). C2 is squared for computational efficiency later.
 */
double D_reitz_init (beta)
double          beta;
{   double      cos_beta;
    if (beta <= 0.0) return 0.0;
    cos_beta = cos(beta);
    return ((cos_beta * cos_beta) - 1.0) /
	((cos_beta * cos_beta) - sqrt(2.0));
}

/* ****************************************************************
 * double D_reitz (N, L, V, C2_2)
 *  DIR_VECT    *N, *L, *V  (in) - N, L, V vectors
 *  double      C2_2        (in) - C2 squared
 *
 *  Returns the Trowbridge-Reitz distribution function presented
 *   by Blinn (1977) given surface normal, incident light
 *   direction, view direction, and the specular exponent. Refer
 *   to Eq.(\*(eB)
 */
double D_reitz (N, L, V, C2_2)
DIR_VECT        *N, *L, *V;
double          C2_2;
{   double      tmp;
    double      N_dot_H;
    if ((N_dot_H = geo_dot(N,geo_H(V,L))) < 0.0) return 0.0;
    tmp = C2_2 / ((N_dot_H * N_dot_H * (C2_2 - 1.0)) + 1.0);
    return tmp * tmp;
}

/* ****************************************************************
 * double D_cook_init (beta)
 *  double      beta        (in) - angle between N and
 *                              H where function = .5
 *  Returns m squared corresponding to beta, refer to Eq.(\*(rT).
 *   m is squared for computational efficiency in later use.
 */
double D_cook_init (beta)
double          beta;
{   double      tan_beta;
    if (beta <= 0.0) return 0.0;
    if (beta >= M_PI_2) return HUGE;
    tan_beta = tan(beta);
    return -(tan_beta * tan_beta) /
	log(pow(cos(beta),4.0)/2.0);
}

/* ****************************************************************
 * double D_cook (N, L, V, m_2)
 *  DIR_VECT    *N, *L, *V  (in) - N, L, V vectors
 *  double      m_2         (in) - m squared
 *
 *  Returns microfacet distribution probability (Cook 1982) derived
 *   from (Beckmann 1963).  Refer to Eq.(\*(rX).
 */
double D_cook (N, L, V, m_2)
DIR_VECT        *N, *L, *V;
double          m_2;
{   double      tmp;
    double      N_dot_H;
    if ((N_dot_H = geo_dot(N,geo_H(V,L))) < 0.0) return 0.0;
    tmp = -(1.0 - (N_dot_H * N_dot_H)) / (N_dot_H * N_dot_H * m_2);
    return exp(tmp)/(4.0 * M_PI * m_2 * pow(N_dot_H,4.0));
}

/* ****************************************************************
 * double D_g (N, L, V, sigma, lambda)
 *  DIR_VECT    *N, *L, *V  (in) - N, L, V vectors
 *  double      sigma       (in) RMS roughness, nm
 *  double      lambda      (in) wavelength, nm
 *
 *  Returns the apparent roughness, g, as given by Beckmann (1963).
 *   Refer to Eq.(\*(rY).
 */
double D_g (N, L, V, sigma, lambda)
DIR_VECT        *N, *L, *V;
double          sigma, lambda;
{   double      tmp;
    tmp = geo_dot(N,L) + geo_dot(N,V);
    return (4.0 * M_PI * M_PI * sigma * sigma * tmp * tmp) /
	(lambda * lambda);
}

/* ****************************************************************
 * double D_tau (sigma, m)
 *  double      sigma       (in) rms roughness (nm)
 *  double      m           (in) rms slope
 *
 *  Returns tau (correlation distance) in nm.  The relationship of
 *   roughness, slope, and correlation distance given by
 *   Beckmann (1963) is assumed, refer to Eq.(\*(rD).
 */
double D_tau (sigma, m)
double          sigma, m;
{   return (2.0 * sigma) / m;
}

/* ****************************************************************
 * double D_sigma (m, tau)
 *  double      m           (in) rms slope
 *  double      tau         (in) correlation distance (nm)
 *
 *  Returns sigma (rms roughness). The relationship of roughness,
 *   slope, and correlation distance given by Beckmann (1963) is
 *   assumed, refer to Eq.(\*(rD).
 */
double D_sigma (m, tau)
double          m, tau;
{   return (m * tau)/ 2.0;
}

/* ****************************************************************
 * double D_m (sigma, tau)
 *  double      sigma       (in) rms roughness (nm)
 *  double      tau         (in) correlation distance (nm)
 *
 *  Returns m (rms slope). The relationship  of roughness, slope,
 *   and correlation distance given by Beckmann (1963) is assumed,
 *   refer to Eq.(\*(rD).
 */
double D_m (sigma, tau)
double          sigma, tau;
{   return (2.0 * sigma) / tau;
}

/* ****************************************************************
 * double D_coherent (g)
 *  double      g           (in) apparent roughness
 *
 *  Returns the coherent roughness attenuation given
 *  by Beckmann (1963), refer to Eq.(\*(rZ).
 */
double D_coherent (g)
double g;
{   return exp(-g);
}

/* ****************************************************************
 * double D_incoherent (N, L, V, m, g, lambda, tau)
 *  DIR_VECT    *N, *L, *V  (in) N, L, V vectors
 *  double      m           (in) m (slope)
 *  double      g           (in) apparent roughness
 *  double      lambda      (in) wavelength (nm)
 *  double      tau         (in) correlation distance (nm)
 *
 *  Returns the microfacet distribution function given by
 *   Beckmann (1963) as interpreted by Koestner (1986), refer to
 *   Eq.(\*(rC).
 */
double D_incoherent (N, L, V, m, g, lambda, tau)
DIR_VECT        *N, *L, *V;
double          m, g, lambda, tau;
{   DIR_VECT    *H;
    double      D1, D2;
    double      denom;
    double      N_dot_H, N_dot_H_2;

    if (g <= 0.0) return 0.0;
    if ((H = geo_H(V,L)) == NULL) return 0.0;
    if ((N_dot_H = geo_dot(N,H)) <= 0.0) return 0.0;
    N_dot_H_2 = N_dot_H * N_dot_H;
    denom = 4.0 * M_PI * m * m * N_dot_H_2 * N_dot_H_2;

    /* if g < 8.0, evaluate the series expansion, Eq.(\*(rCb)
     *  terminating when a term falls below 0.00001.  For small g
     *  the evaluation terminates quickly.  For large g the series
     *  converges slowly.  If g is near 8.0 the first terms of the
     *  series will be less that the termination criteria.  This
     *  requires an additional termination criteria that the values
     *  of the terms are decreasing at the time the termination
     *  criteria is reached.
     */
    if (g < 8.0) {
	double      inc, ct, ct_factorial, g_pow;
	double      last_inc, Vxz_t_over_4;
	Vxz_t_over_4 = (tau * tau * D_Vxz(N,L,V,lambda)) / 4.0;
	D1 = 0.0;
	ct = 1.0;
	ct_factorial = 1.0;
	g_pow = g * g;
	inc = 0.0;
	do {
	    last_inc = inc;
	    inc = (g_pow / (ct * ct_factorial)) *
		exp(-(g + Vxz_t_over_4/ct));
	    D1 += inc;
	    ct += 1.0;
	    ct_factorial *= ct;
	    g_pow *= g;
	} while (((inc/denom) > 0.00001) || (inc > last_inc));
	D1 /= denom;

	/* if g < 5.0, only the series expansion is used.  If
	 *  5.0 < g < 8.0, then we are in a transition range
	 *  for interpolation between the series expansion and
	 *  the convergence expression.
	 */
	if (g < 5.0) return D1;
    }

    /* if g > 5.0, evaluate the convergence expression, Eq.(\*(rCc)
     *  (this routine would have returned earlier if G < 5.0).
     */
    {   double  tmp;
	tmp = (N_dot_H_2 - 1.0) / (N_dot_H_2 * m * m);
	D2 = exp(tmp) / denom;

	/* if g > 8.0, only the convergence expression is used,
	 *  otherwise we are in a transition zone between the
	 *  convergent expression and series expansion.
	 */
	if (g > 8.0) return D2;
    }

    /* linear interpolation between D1 and D2 for
     *          5.0 < g < 8.0
     */
    return (((8.0 - g) * D1) + ((g - 5.0) * D2)) / 3.0;
}

/* ****************************************************************
 * double D_Vxz (N, L, V, lambda)
 *  DIR_VECT    *N, *L, *V  (in) N, L, V vectors
 *  double      lambda      (in) wavelength (nm)
 *
 *  Returns the value of Vxz per Eq.(\*(rCd).
 */
double D_Vxz (N, L, V, lambda)
DIR_VECT        *N, *L, *V;
double          lambda;
{   double      N_dot_L, N_dot_V, cos_phi;
    DIR_VECT    sin_i, sin_r;
    double      len_2_i, len_2_r;

    /* compute the sine squared of the incident angle
     */
    N_dot_L = geo_dot(N, L);
    len_2_i = 1.0 - (N_dot_L * N_dot_L);

    /* compute the sine squared of the reflected angle
     */
    N_dot_V = geo_dot(N, V);
    len_2_r = 1.0 - (N_dot_V * N_dot_V);

    /* if the sine of either the incident or the
     *  reflected angle is zero, then the middle
     *  term is zero.
     */
    if ((len_2_i > 0.0) && (len_2_r > 0.0)) {
	/* the two sine vectors are of length equal to the sine of
	 *  the angles.  The dot product is the cosine times the
	 *  lengths of the vectors (sines of the angles).
	 */
	sin_i.i = L->i - (N_dot_L * N->i);
	sin_i.j = L->j - (N_dot_L * N->j);
	sin_i.k = L->k - (N_dot_L * N->k);
	sin_r.i = V->i - (N_dot_V * N->i);
	sin_r.j = V->j - (N_dot_V * N->j);
	sin_r.k = V->k - (N_dot_V * N->k);
	cos_phi = geo_dot (&sin_i, &sin_r);
	return (4.0 * M_PI * M_PI * (len_2_i +
	    (2.0 * cos_phi) + len_2_r)) / (lambda * lambda);
    }
    else {
	return (4.0 * M_PI * M_PI * (len_2_i + len_2_r)) /
	    (lambda * lambda);
    }
}
/* ************************************************************* */
_i.i = L->i G.h
 *                            G.h
 * ****************************************************************
 * include file for the geometric attenuation functions
 */
#ifndef G_H
#define G_H
/* Microfacet distribution routines
 */
double G_torrance();
double G_sancer();
#endif G_H
/* ************************************************************* */
* lambda);
    }
}
/* ************************************************************* */
_i.i = L->i G.c
 *                            G.c
 * ****************************************************************
 *  MODULE PURPOSE:
 *      This module contains geometric attenuation
 *      functions
 *
 *  MODULE CONTENTS:
 *      D_torrance      - function by Torrance and Sparrow
 *      D_Sancer        - function by Sancer
 */
#include <math.h>
#include "geo.h"

#define ROOT_PI     1.7724538509055159

/* ****************************************************************
 * double G_torrance (N, L, V)
 *  DIR_VECT    *N, *L, *V  (in) - N, L, V vectors
 *  double      dummy       (in) - just to make the
 *                              calls identical
 *
 *  Returns geometric attenuation (Torrance and Sparrow
 *   1967), refer to Eq.(\*(eS).  The direction vectors
 *   N, V, and L are assumed to be oriented to the same
 *   side of the surface.
 */
double G_torrance (N, L, V, dummy)
DIR_VECT        *N, *L, *V;
double          dummy;
{
    double      N_dot_H, V_dot_H;
    double      N_dot_V, N_dot_L;
    double      g1, g2;
    DIR_VECT    *H;

    H = geo_H(V,L);
    N_dot_H = geo_dot (N,H);
    V_dot_H = geo_dot (V,H);
    N_dot_V = geo_dot (N,V);
    N_dot_L = geo_dot (N,L);

    if ((g1 = (2.0 * N_dot_H * N_dot_V) /
	V_dot_H) > 1.0) g1 = 1.0;
    if ((g2 = (2.0 * N_dot_H * N_dot_L) /
	V_dot_H) < g1) return g2;
    else return g1;
}



/* ****************************************************************
 * double G_sancer (N, L, V, m_2)
 *  DIR_VECT    *N, *L, *V  (in) - N, L, V vectors
 *  double      m_2         (in) - m squared
 *
 *  Returns geometric attenuation (Sancer 1969), refer to Eq.(\*(rT).
 *   The direction vectors N, V, and L are assumed to be oriented
 *   to the same side of the surface.
 *
 *  NOTE:
 *   > m can be related to beta using D_cook_init()
 *   > This implementation assumes slope relates roughness and
 *      correlation distance as described by Beckmann. This
 *      explains the missing factor of 2 with m_2 when compared
 *      to Eq.(\*(rT).
 */
double G_sancer (N, L, V, m_2)
DIR_VECT        *N, *L, *V;
double          m_2;
{
    double      N_dot_L, N_dot_V;
    double      c1, c2, root_c1, root_c2, ci, cr;

    if (m_2 <= 0.0) return 1.0;
    if ((N_dot_L = geo_dot(N,L)) <= 0.0) return 0.0;
    c1 = (N_dot_L * N_dot_L) / (m_2 * (1.0 - (N_dot_L * N_dot_L)));
    root_c1 = sqrt(c1);
    ci = (exp(-c1) /
	(2.0 * ROOT_PI * root_c1)) - (erfc(root_c1) / 2.0);

    if ((N_dot_V = geo_dot(N,V)) <= 0.0) return 0.0;
    c2 = (N_dot_V * N_dot_V) / (m_2 * (1.0 - (N_dot_V * N_dot_V)));
    root_c2 = sqrt(c2);
    cr = (exp(-c2) /
	(2.0 * ROOT_PI * root_c2)) - (erfc(root_c2) / 2.0);

    return 1.0/(1.0 + ci + cr);
}
/* ************************************************************* */
)) <= 0.0) return 0.0;
    c1 = (N_dot_L * N_dot_L) / (m_2 * (1.0 - (N_dot_L * N_dot_L)));
    root_c1 = sqrt(c1);
    ci = (exp(-c1) /
	(2.0 * ROOT_PI * root_c1)) - (erfc(root_c1) / 2.0);

    if ((N_dot_V = geo_dot(N,V)) <= 0.0) return 0.0;
    c2 = (N_dotclr.h
 *                            clr.h
 * ****************************************************************
 * include file for the geometric utilities
 */
#ifndef CLR_H
#define CLR_H

#ifndef TRUE
#define TRUE    1
#endif TRUE
#ifndef FALSE
#define FALSE   0
#endif FALSE

#define CLR_SAMPLE_MEYER        0
#define CLR_SAMPLE_HALL         1
#define CLR_SAMPLE_LIN_RAMP     2

/* common geometric constructs
 */
typedef struct {double  r, g, b;}       CLR_RGB;
typedef struct {double  x, y, z;}       CLR_XYZ;
typedef struct {double  l, a, b;}       CLR_LAB;
typedef struct {double  l, u, v;}       CLR_LUV;

/* color routine declarations
 */
int         CLR_init();
int         CLR_read_mtl();
int         CLR_add_spect();
int         CLR_mult_spect();
int         CLR_scale_spect();
double      CLR_area_spect();
CLR_XYZ     CLR_spect_to_xyz();
int         CLR_xyz_to_spect();
CLR_RGB     CLR_spect_to_rgb();
int         CLR_get_rgb();
int         CLR_get_min_wl();
int         CLR_get_max_wl();
int         CLR_get_xyz_rgb();
int         CLR_get_rgb_xyz();
int         CLR_get_yiq_rgb();
int         CLR_get_rgb_yiq();
int         CLR_rgb_to_aux_rgb();
CLR_LAB     CLR_xyz_to_lab();
CLR_LUV     CLR_xyz_to_luv();
int         CLR_t_concat();
int         CLR_t_inverse();

CLR_RGB     CLR_clamp_rgb();
CLR_RGB     CLR_scale_rgb();
CLR_RGB     CLR_clip_rgb();

int         CLR_init_samples();
int         CLR_num_samples();
int         CLR_spect_to_sample();
int         CLR_get_sample_rgb();
int         CLR_get_sample_xyz();
int         CLR_reconstruct();
int         CLR_exit_samples();

#endif CLR_H
/* ************************************************************* */
LAB     CLR_xyz_to_lab();
CLR_LUV     CLR_xyz_to_luv();
int         CLR_t_concat();
int         CLR_t_inverse();

CLR_RGB     CLR_clamp_rgb();
CLR_RGB     CLR_scale_rgb();
CLR_RGB     CLR_clip_rgb();

int         CLR_init_samples();
int         CLR_num_samples();
int         CLR_spect_to_sample();
int         Cclr.c
 *                           clr.c
 * ****************************************************************
 *  MODULE PURPOSE:
 *      This module contains routines for color
 *      transformations, color space manipulations,
 *      and spectral sampling.
 *
 *  MODULE CONTENTS:
 *      CLR_init            - initialized the color routines
 *      CLR_read_mtl        - read a material file
 *      CLR_blackbody       - returns the spectral curve for
 *                              a blackbody at the specified
 *                              temperature.
 *      CLR_add_spect       - add two spectral curves
 *      CLR_mult_spect      - multiply two spectral curves
 *      CLR_scale_spect     - scale a spectral curve
 *      CLR_area_spect      - get the area under a curve
 *      CLR_spect_to_xyz    - sample a curve to xyz
 *      CLR_xyz_to_spect    - reconstruct a spectral curve
 *      CLR_spect_to_rgb    - sample a curve to rgb
 *      CLR_get_CIEXYZ      - get the CIEXYZ matching functions
 *      CLR_get_rgb         - returns the color space primaries
 *      CLR_get_min_wl      - returns the max wavelength
 *      CLR_get_max_wl      - returns the min wavelength
 *      CLR_get_xyz_rgb     - returns the xyz to rgb matrix
 *      CLR_get_rgb_xyz     - returns the rgb to xyz matrix
 *      CLR_get_rgb_yiq     - returns the rgb to yiq matrix
 *      CLR_get_yiq_rgb     - returns the yiq to rgb matrix
 *      CLR_rgb_to_aux_rgb  - returns the matrix from the
 *                              current color space to an
 *                              auxiliary color space
 *      CLR_xyz_to_lab      - transforms from xyz to Lab
 *      CLR_xyz_to_luv      - transforms from xyz to Luv
 *      CLR_t_concat        - concatenate color transforms
 *      CLR_t_inverse       - return an inverse transform
 *      CLR_exit            - finish with the color routines
 *
 *  NOTES:
 *      > Spectral curve manipulations use the lower and upper
 *          bounds established at init.  Curves are at 1nm
 *          increments and are arrays of doubles, (max - min + 1)
 *          elements long.
 *      > Matrix scaling is so that an RGB of 1,1,1 transforms
 *          to an XYZ with a Y value of 1.0
 *      > The XYZ and RGB sampling from spectral curves are
 *          scaled so that that a mirror reflector (1.0 for all
 *          wavelengths) has a Y value of 1.0.
 *      > Wavelength bounds of 380nm to 780nm (the visible
 *          spectrum) are appropriate for most applications.
 */
#include <stdio.h>
#include <math.h>
#include "clr.h"

#define RED         0
#define GREEN       1
#define BLUE        2
#define WHITE       3
#define LOAD_MAT(a,b) \
{   int ii,jj; \
    for (ii=0; ii<=2; ii++) \
	for (jj=0; jj<=2; jj++) a[ii][jj] = b[ii][jj]; \
}

static int          init = FALSE;
static int          min_wl = 380;
static int          max_wl = 780;
static double       xyz_rgb_mat[3][3];
static double       rgb_xyz_mat[3][3];
static double       *X_tristim = NULL;
static double       *Y_tristim = NULL;
static double       *Z_tristim = NULL;
static double       *work_curve = NULL;
static double       *reconstruct[3] = NULL;
static double       XYZ_to_recon[3][3];
static double       xyz_scale = 1.0;
static CLR_XYZ      rgb_primary[4];
static int          clr__cspace_to_xyz ();
static CLR_XYZ      white;
static CLR_LUV      ref_luv;

/* This is the RGB to YIQ matrix and YIQ to RGB matrix as
 *  given in Sect 5.2 NTSC and RGB Video
 */
static double       rgb_yiq_mat[3][3] = {
    {0.299,     0.587,      0.114},
    {0.596,    -0.275,     -0.321},
    {0.212,    -0.523,      0.311}};

static double       yiq_rgb_mat[3][3] = {
    {1.0000,    0.9557,     0.6199},
    {1.0000,   -0.2716,    -0.6469},
    {1.0000,   -1.1082,     1.7051}};

/* This is the NTSC primaries with D6500 white point for use as
 *  the default initialization as given in sect 5.1.1 Color
 *  Correction for Display.
 */
static CLR_XYZ      rgb_NTSC[4] = {
    {0.670,     0.330,      0.000},     /* red */
    {0.210,     0.710,      0.080},     /* green */
    {0.140,     0.080,      0.780},     /* blue */
    {0.313,     0.329,      0.358}};    /* white */

/* This is the data for the CIEXYZ curves take from Judd and
 *  Wyszecki (1975), table 2.6, these are for the 1931 standard
 *  observer with a 2-degree visual field.
 */
static double       CIEXYZ[81][4] = {
    {380, 0.0014, 0.0000, 0.0065}, {385, 0.0022, 0.0001, 0.0105},
    {390, 0.0042, 0.0001, 0.0201}, {395, 0.0076, 0.0002, 0.0362},
    {400, 0.0143, 0.0004, 0.0679}, {405, 0.0232, 0.0006, 0.1102},
    {410, 0.0435, 0.0012, 0.2074}, {415, 0.0776, 0.0022, 0.3713},
    {420, 0.1344, 0.0040, 0.6456}, {425, 0.2148, 0.0073, 1.0391},
    {430, 0.2839, 0.0116, 1.3856}, {435, 0.3285, 0.0168, 1.6230},
    {440, 0.3483, 0.0230, 1.7471}, {445, 0.3481, 0.0298, 1.7826},
    {450, 0.3362, 0.0380, 1.7721}, {455, 0.3187, 0.0480, 1.7441},
    {460, 0.2908, 0.0600, 1.6692}, {465, 0.2511, 0.0739, 1.5281},
    {470, 0.1954, 0.0910, 1.2876}, {475, 0.1421, 0.1126, 1.0419},
    {480, 0.0956, 0.1390, 0.8130}, {485, 0.0580, 0.1693, 0.6162},
    {490, 0.0320, 0.2080, 0.4652}, {495, 0.0147, 0.2586, 0.3533},
    {500, 0.0049, 0.3230, 0.2720}, {505, 0.0024, 0.4073, 0.2123},
    {510, 0.0093, 0.5030, 0.1582}, {515, 0.0291, 0.6082, 0.1117},
    {520, 0.0633, 0.7100, 0.0782}, {525, 0.1096, 0.7932, 0.0573},
    {530, 0.1655, 0.8620, 0.0422}, {535, 0.2257, 0.9149, 0.0298},
    {540, 0.2904, 0.9540, 0.0203}, {545, 0.3597, 0.9803, 0.0134},
    {550, 0.4334, 0.9950, 0.0087}, {555, 0.5121, 1.0000, 0.0057},
    {560, 0.5945, 0.9950, 0.0039}, {565, 0.6784, 0.9786, 0.0027},
    {570, 0.7621, 0.9520, 0.0021}, {575, 0.8425, 0.9154, 0.0018},
    {580, 0.9163, 0.8700, 0.0017}, {585, 0.9786, 0.8163, 0.0014},
    {590, 1.0263, 0.7570, 0.0011}, {595, 1.0567, 0.6949, 0.0010},
    {600, 1.0622, 0.6310, 0.0008}, {605, 1.0456, 0.5668, 0.0006},
    {610, 1.0026, 0.5030, 0.0003}, {615, 0.9384, 0.4412, 0.0002},
    {620, 0.8544, 0.3810, 0.0002}, {625, 0.7514, 0.3210, 0.0001},
    {630, 0.6424, 0.2650, 0.0000}, {635, 0.5419, 0.2170, 0.0000},
    {640, 0.4479, 0.1750, 0.0000}, {645, 0.3608, 0.1382, 0.0000},
    {650, 0.2835, 0.1070, 0.0000}, {655, 0.2187, 0.0816, 0.0000},
    {660, 0.1649, 0.0610, 0.0000}, {665, 0.1212, 0.0446, 0.0000},
    {670, 0.0874, 0.0320, 0.0000}, {675, 0.0636, 0.0232, 0.0000},
    {680, 0.0468, 0.0170, 0.0000}, {685, 0.0329, 0.0119, 0.0000},
    {690, 0.0227, 0.0082, 0.0000}, {695, 0.0158, 0.0057, 0.0000},
    {700, 0.0114, 0.0041, 0.0000}, {705, 0.0081, 0.0029, 0.0000},
    {710, 0.0058, 0.0021, 0.0000}, {715, 0.0041, 0.0015, 0.0000},
    {720, 0.0029, 0.0010, 0.0000}, {725, 0.0020, 0.0007, 0.0000},
    {730, 0.0014, 0.0005, 0.0000}, {735, 0.0010, 0.0004, 0.0000},
    {740, 0.0007, 0.0002, 0.0000}, {745, 0.0005, 0.0002, 0.0000},
    {750, 0.0003, 0.0001, 0.0000}, {755, 0.0002, 0.0001, 0.0000},
    {760, 0.0002, 0.0001, 0.0000}, {765, 0.0001, 0.0000, 0.0000},
    {770, 0.0001, 0.0000, 0.0000}, {775, 0.0001, 0.0000, 0.0000},
    {780, 0.0000, 0.0000, 0.0000} };



/* ****************************************************************
 * CLR_init (min, max, rgb)
 *  int         min, max    (in) wavelength bounds (nm)
 *  CLR_XYZ     rgb[4]      (in) the primaries.  If NULL,
 *                              then the NTSC primaries with
 *                              a D6500 white point are used.
 *
 * Initializes the color routines for a spectral range from
 *  'min'nm to 'max'nm and an RGB color space given by 'rgb'
 *  Returns TRUE if successful and FALSE on error.
 */
int CLR_init (min, max, rgb)
int         min;
int         max;
CLR_XYZ     rgb[4];
{   int     color, ct;
    double  len;
    char    *malloc();

    if (init) return FALSE;
    init = TRUE;
    min_wl = min;
    max_wl = max;

    /* load primaries and build transformations, use the
     * defaults is rgb==NULL
     */
    for (color=RED; color<=WHITE; color++) {
	if (rgb == NULL) rgb_primary[color] = rgb_NTSC[color];
	else rgb_primary[color] = rgb[color];
	rgb_primary[color].z = 1.0 -
	    rgb_primary[color].x - rgb_primary[color].y;
    }
    if (!clr__cspace_to_xyz(rgb_primary, rgb_xyz_mat)) goto error;
    if (!CLR_t_inverse(rgb_xyz_mat, xyz_rgb_mat)) goto error;

    /* build reference info for L*a*b*, L*u*v* transforms */
    white.x =
	(100.0 * rgb_primary[WHITE].x) / rgb_primary[WHITE].y;
    white.y = 100.0;
    white.z =
	(100.0 * rgb_primary[WHITE].z) / rgb_primary[WHITE].y;
    ref_luv.u = (4.0 * white.x) /
	(white.x + (15.0 * white.y) + (3.0 * white.z));
    ref_luv.v = (9.0 * white.y) /
	(white.x + (15.0 * white.y) + (3.0 * white.z));

    /* build the XYZ sampling curves - allocate space for the
     *  curves and interpolate the CIEXYZ table into continuous
     *  curves at 1nm increments.
     */
    {   int     ii, nm;
	double  *x, *y, *z;
	double  x_cur, y_cur, z_cur;
	double  x_inc, y_inc, z_inc;
	if ((work_curve = (double *)malloc((unsigned)
	    (sizeof(double) * (max_wl-min_wl+1))))
	    == NULL) goto error;
	if ((x = X_tristim = (double *)malloc((unsigned)
	    (sizeof(double) * (max_wl-min_wl+1))))
	    == NULL) goto error;
	if ((y = Y_tristim = (double *)malloc((unsigned)
	    (sizeof(double) * (max_wl-min_wl+1))))
	    == NULL) goto error;
	if ((z = Z_tristim = (double *)malloc((unsigned)
	    (sizeof(double) * (max_wl-min_wl+1))))
	    == NULL) goto error;
	for (nm=min_wl; nm<380; nm++) *x++ = *y++ = *z++ = 0.0;
	for (ct=0; ct<80; ct++) {
	    x_cur = CIEXYZ[ct][1];
	    y_cur = CIEXYZ[ct][2];
	    z_cur = CIEXYZ[ct][3];
	    x_inc = (CIEXYZ[ct+1][1] - x_cur) / 5.0;
	    y_inc = (CIEXYZ[ct+1][2] - y_cur) / 5.0;
	    z_inc = (CIEXYZ[ct+1][3] - z_cur) / 5.0;
	    for (ii=0; ii<5; ii++, nm++) {
		if (nm > max_wl) goto XYZ_done;
		if (nm >= min_wl) {
		    *x++ = x_cur; *y++ = y_cur; *z++ = z_cur;
		}
		x_cur += x_inc; y_cur += y_inc; z_cur += z_inc;
	    }
	}
	if (nm <= max_wl) {
	    *x++ = x_cur; *y++ = y_cur; *z++ = z_cur; nm++;
	}
	for ( ;nm<=max_wl; nm++) *x++ = *y++ = *z++ = 0.0;
    }
XYZ_done:
    /* determine the scaling factor to be used in sampling spectral
     *  curves to bring Y of a sampled identity curve to 1.
     */
    xyz_scale = 1.0 / CLR_area_spect (Y_tristim);

    /* allocate and build the reconstruction curves and the
     * transformation from XYZ to the reconstruction weights
     */
    {   double      reconst_to_XYZ[3][3];
	CLR_XYZ     xyz;
	if ((reconstruct[0] = (double *)malloc((unsigned)
	    (sizeof(double) * (3 * (max_wl-min_wl+1)))))
	    == NULL) goto error;
	reconstruct[1] = reconstruct[0] + (max_wl-min_wl+1);
	reconstruct[2] = reconstruct[1] + (max_wl-min_wl+1);
	for (ct=min; ct<=max; ct++) {
	    *(reconstruct[0] + ct - min) = 1.0;
	    *(reconstruct[1] + ct - min) =
		sin((double)((2.0 * M_PI / 400.0) *
		((double)ct - 380.0)));
	    *(reconstruct[2] + ct - min) =
		cos((double)((2.0 * M_PI / 400.0) *
		((double)ct - 380.0)));
	}
	for (ct=0; ct<=2; ct++) {
	    xyz = CLR_spect_to_xyz (reconstruct[ct]);
	    reconst_to_XYZ[ct][0] = xyz.x;
	    reconst_to_XYZ[ct][1] = xyz.y;
	    reconst_to_XYZ[ct][2] = xyz.z;
	}
	CLR_t_inverse (reconst_to_XYZ, XYZ_to_recon);
    }
    return TRUE;

error:
    CLR_exit();
    return FALSE;
}

/* ****************************************************************
 * CLR_read_mtl (file, conduct, n, k, curve)
 *  char        *file       (in) material file name
 *  int         *conduct    (out) conductor - set to FALSE if not
 *                              specified in the material file
 *  double      *n, *k      (out) n and k, - set to 0.0 if not
 *                              specified in the material file
 *  double      *curve      (out) curve array
 *
 * Returns TRUE if the material was read, FALSE if the material
 *  could not be found or an error was detected in the material
 *  file.  NULL pointers can be given as arguments for 'conduct',
 *  'n', 'k', and/or 'curve' if the properties are not of interest.
 *
 * The material file must be in the format given in Sect III.7.1
 *  Material Data.
 */
int CLR_read_mtl (file, conduct, n, k, curve)
char        *file;
int         *conduct;
double      *n, *k;
double      *curve;
{   FILE        *fp, *fopen();
    char        in_str[200];
    int         flag = 1;
    int         cur_wl, last_wl, ind_wl;
    double      cur_val, last_val, inc_val;

    if (!init) return FALSE;
    if ((fp=fopen(file,"r")) == NULL) return FALSE;
    if (n != NULL) *n = 0.0;
    if (k != NULL) *k = 0.0;
    if (conduct != NULL) *conduct = FALSE;
    if (curve == NULL) flag = -1;
    ind_wl = min_wl;
    while (fgets(in_str,200,fp)) {
	switch (in_str[0]) {
	    case '#':       /* comment */
		break;
	    case 'k':       /* absorption coefficient */
		if (k != NULL) (void)sscanf (in_str, "k %lf", k);
		break;
	    case 'n':       /* index of refraction */
		if (n != NULL) (void)sscanf (in_str, "n %lf", n);
		break;
	    case 'c':       /* conductor */
		if (conduct != NULL) *conduct = TRUE;
		break;
	    case 'd':       /* dielectric */
		if (conduct != NULL) *conduct = FALSE;
		break;
	    default:        /* spectral data */
		if ((flag != -1) && (sscanf(in_str,
		    "%d %lf", &cur_wl, &cur_val) == 2)) {
		    if (flag == 1) {
			flag = 0;
			while (ind_wl <= cur_wl) {
			    *curve++ = cur_val;
			    if (++ind_wl > max_wl) {
				flag == -1;
				break;
			    }
			}
		    }
		    else {
			if (cur_wl < last_wl) {
			    (void)fclose(fp);
			    return FALSE;
			}
			inc_val = (cur_val - last_val) /
			    (cur_wl - last_wl);
			while (last_wl < ind_wl) {
			    last_val += inc_val;
			    last_wl++;
			}
			while (ind_wl <= cur_wl) {
			    *curve++ = last_val;
			    last_val += inc_val;
			    if (++ind_wl > max_wl) {
				flag = -1;
				break;
			    }
			}
		    }
		    last_wl = cur_wl;
		    last_val = cur_val;
		}
	}
    }
    while (ind_wl <= max_wl) {
	*curve++ = last_val;
	ind_wl++;
    }
    (void)fclose(fp);
    return TRUE;
}

/* ****************************************************************
 * CLR_blackbody (temp, curve)
 *  double      temp        (in) temperature (degrees K)
 *  double      *curve      (out) curve array
 *
 * Fills the 'curve' array with the illumination values for an
 *  ideal blackbody radiator at the specified temperature.
 *  Following the convention used for normalizing other illuminant
 *  curves, the blackbody curve is normalized so that the value
 *  at 560nm is 1.0.  The formulation is taken from Judd and
 *  Wysecki (1975) p.106.  NOTE: c1 and c2 have been adjusted
 *  to account for the wavelength to be in nm.
 */
int CLR_blackbody (temp, curve)
double  temp;
double  *curve;
{   double  norm;
    double  c1 = 3.74150E29;
    double  c2 = 1.4388E7;
    double  wl;
    int     ct;

    if (temp < 0.0) return FALSE;
    norm = (pow((double)560.0, (double)5.0) *
	(exp (c2 / (560.0 * temp)) - 1.0)) / c1;
    for (ct=max_wl-min_wl+1, wl=min_wl; --ct>=0; wl += 1.0) {
	*curve++ = (norm * c1) / (pow(wl, (double)5.0) *
	    (exp (c2 / (wl * temp)) - 1.0));
    }
    return TRUE;
}

/* ****************************************************************
 * CLR_add_spect (c1, c2, c3)
 *  double  *c1, *c2    (in)  - input curves
 *  double  *c3         (out) - output curve
 *
 * Add spectral curve 'c1' to spectral curve 'c2' to get spectral
 *  curve 'c3'.  Returns TRUE if successful, FALSE if the CLR_
 *  routines are not initialized.
 */
CLR_add_spect (c1,c2,c3)
double  *c1, *c2, *c3;
{   int     ct;
    if (!init) return FALSE;
    for (ct=max_wl-min_wl+1; --ct>=0; ) *c3++ = *c1++ + *c2++;
    return TRUE;
}

/* ****************************************************************
 * CLR_mult_spect (c1, c2, c3)
 *  double  *c1, *c2    (in)  - input curves
 *  double  *c3         (out) - output curve
 *
 * Multiply spectral curve 'c1' by spectral curve 'c2' to get
 *  spectral curve 'c3'.  Returns TRUE if successful, FALSE if
 *  the CLR_ routines are not initialized.
 */
CLR_mult_spect (c1,c2,c3)
double  *c1, *c2, *c3;
{   int     ct;
    if (!init) return FALSE;
    for (ct=max_wl-min_wl+1; --ct>=0; ) *c3++ = *c1++ * *c2++;
    return TRUE;
}

/* ****************************************************************
 * CLR_scale_spect (c1, scale, c3)
 *  double  *c1         (in)  - input curve
 *  double  scale       (in)  - scale
 *  double  *c3         (out) - output curve
 *
 * Multiply spectral curve 'c1' by 'scale' to get spectral curve
 *  'c3'.  Returns TRUE if successful, FALSE if the CLR_ routines
 *  are not initialized.
 */
CLR_scale_spect (c1,scale,c3)
double  *c1, scale, *c3;
{   int     ct;
    if (!init) return FALSE;
    for (ct=max_wl-min_wl+1; --ct>=0; ) *c3++ = *c1++ * scale;
    return TRUE;
}

/* ****************************************************************
 * double CLR_area_spect (c1)
 *  double  *c1         (in)  - input curve
 *
 * Returns the are under the spectral curve 'c1'.  Returns 0 if
 *  the CLR_ routines are not initialized.
 */
double CLR_area_spect (c1)
double  *c1;
{   int     ct;
    double  area = 0.0;
    if (!init) return FALSE;
    for (ct=max_wl-min_wl+1; --ct>=0; ) area += *c1++;
    return area;
}

/* ****************************************************************
 * CLR_XYZ CLR_spect_to_xyz(spectral)
 *  double  *spectral   (in) - the spectral curve
 *
 * Returns the sample values if successful, and a sample value
 *  of (0,0,0) if the CLR_ routines are not initialized.
 *
 * Multiplies the spectral curve by each of the sampling curves
 *  then integrates the resulting curves.  The XYZ values are then
 *  normalized such that an identity material has Y=1.
 */
CLR_XYZ CLR_spect_to_xyz (spectral)
double      *spectral;
{   CLR_XYZ     xyz;
    if (!init) {xyz.x = xyz.y = xyz.z = 0.0; return xyz;}
    (void)CLR_mult_spect(spectral, X_tristim, work_curve);
    xyz.x = xyz_scale * CLR_area_spect(work_curve);
    (void)CLR_mult_spect(spectral, Y_tristim, work_curve);
    xyz.y = xyz_scale * CLR_area_spect(work_curve);
    (void)CLR_mult_spect(spectral, Z_tristim, work_curve);
    xyz.z = xyz_scale * CLR_area_spect(work_curve);
    return xyz;
}

/* ****************************************************************
 *  CLR_xyz_to_spect(xyz,spectral)
 *  CLR_XYZ xyz;        (in)  - the XYZ value of the color
 *  double  *spectral   (out) - the reconstructed spectral curve
 *
 *  Reconstructs a spectral curve using the fitting functions
 *  described in Glassner (1989).
 */
CLR_xyz_to_spect(xyz, spectral)
CLR_XYZ     xyz;
double      *spectral;
{   double      recon[3];
    double      *r0, *r1, *r2;
    int         ct;
    recon[0] = (xyz.x * XYZ_to_recon[0][0]) +
	       (xyz.y * XYZ_to_recon[1][0]) +
	       (xyz.z * XYZ_to_recon[2][0]);
    recon[1] = (xyz.x * XYZ_to_recon[0][1]) +
	       (xyz.y * XYZ_to_recon[1][1]) +
	       (xyz.z * XYZ_to_recon[2][1]);
    recon[2] = (xyz.x * XYZ_to_recon[0][2]) +
	       (xyz.y * XYZ_to_recon[1][2]) +
	       (xyz.z * XYZ_to_recon[2][2]);
    r0 = reconstruct[0];
    r1 = reconstruct[1];
    r2 = reconstruct[2];
    for (ct=max_wl-min_wl+1; --ct>=0; )
	*spectral++ = (recon[0] * *r0++) + (recon[1] * *r1++) +
	    (recon[2] * *r2++);
    return TRUE;
}

/* ****************************************************************
 * CLR_RGB CLR_spect_to_rgb (spectral)
 *  double  *spectral   (in) - the spectral curve
 *
 * Returns the sample values if successful, and a sample value of
 *  (0,0,0) if the CLR_ routines are not initialized.
 *
 * The curve is first sampled to XYZ then transformed to RGB
 */
CLR_RGB CLR_spect_to_rgb (spectral)
double      *spectral;
{   CLR_XYZ     xyz;
    CLR_RGB     rgb;
    if (!init) {rgb.r = rgb.g = rgb.b = 0.0; return rgb;}
    xyz = CLR_spect_to_xyz (spectral);
    rgb.r = (xyz_rgb_mat[0][0] * xyz.x) +
	(xyz_rgb_mat[0][1] * xyz.y) + (xyz_rgb_mat[0][2] * xyz.z);
    rgb.g = (xyz_rgb_mat[1][0] * xyz.x) +
	(xyz_rgb_mat[1][1] * xyz.y) + (xyz_rgb_mat[1][2] * xyz.z);
    rgb.b = (xyz_rgb_mat[2][0] * xyz.x) +
	(xyz_rgb_mat[2][1] * xyz.y) + (xyz_rgb_mat[2][2] * xyz.z);
    return rgb;
}

/* ****************************************************************
 * CLR_get_CIEXYZ (X,Y,Z)
 *  double  *X,*Y,*Z    (mod) arrays to hold the curves
 *
 * Copies the XYZ sampling curves into the user supplied buffers.
 *  Returns TRUE is successful and FALSE if the CLR_ routines are
 *  not initialized.
 */
CLR_get_CIEXYZ(X,Y,Z)
double      *X,*Y,*Z;
{   int     ct;
    double  *x,*y,*z;
    if (!init) return FALSE;
    x = X_tristim; y = Y_tristim; z = Z_tristim;
    for (ct=max_wl-min_wl+1; --ct>=0; ) {
	*X++ = *x++; *Y++ = *y++; *Z++ = *z++;
    }
    return TRUE;
}

/* ****************************************************************
 * CLR_get_rgb (rgb)
 *  CLR_XYZ     rgb[4]  (mod) the primaries
 *
 * Fills 'rgb' with the primaries the CLR_ routines are initialized
 *  to.  Returns TRUE if successful and FALSE if the CLR_ routines
 *  are not initialized.
 */
CLR_get_rgb (rgb)
CLR_XYZ     *rgb;
{   int     ct;
    if (!init) return FALSE;
    for (ct=0; ct<=3; ct++) rgb[ct] = rgb_primary[ct];
    return TRUE;
}

/* ****************************************************************
 * CLR_get_min_wl ()
 *
 * Returns the minimum wavelength bound for which the CLR_ routines
 *  are initialized, returns -1 if the CLR_ routines are not
 *  initialized.
 */
CLR_get_min_wl()
{   if (!init) return -1;
    return min_wl;
}

/* ****************************************************************
 * CLR_get_max_wl ()
 *
 * Returns the minimum wavelength bound for which the CLR_ routines
 *  are initialized, returns -1 if the CLR_ routines are not
 *  initialized.
 */
CLR_get_max_wl()
{   if (!init) return -1;
    return max_wl;
}

/* ****************************************************************
 * CLR_get_xyz_rgb (mat)
 *  double        mat[3][3]     (mod) - matrix to be loaded
 *
 *  Copies the CIEXYZ to RGB transformation into the user supplied
 *   matrix.  Returns TRUE if successful and FALSE if the CLR_
 *   routines are not initialized.
 */
int CLR_get_xyz_rgb(mat)
double mat[][3];
{   if (!init) return FALSE;
    LOAD_MAT(mat, xyz_rgb_mat);
    return TRUE;
}

/* ****************************************************************
 * CLR_get_rgb_xyz (mat)
 *  double        mat[3][3]     (mod) - matrix to be loaded
 *
 *  Copies the RGB to CIEXYZ transformation into the user supplied
 *   matrix.  Returns TRUE if successful and FALSE if the CLR_
 *   routines are not initialized.
 */
int CLR_get_rgb_xyz(mat)
double  mat[][3];
{   if (!init) return FALSE;
    LOAD_MAT(mat, rgb_xyz_mat);
    return TRUE;
}

/* ****************************************************************
 * CLR_get_yiq_rgb (mat)
 *  double        mat[3][3]     (mod) - matrix to be loaded
 *
 *  Copies the YIQ to RGB transformation into the user supplied
 *   matrix.  Returns TRUE if successful and FALSE if the CLR_
 *   routines are not initialized.
 */
int CLR_get_yiq_rgb(mat)
double mat[][3];
{   if (!init) return FALSE;
    LOAD_MAT(mat, yiq_rgb_mat);
    return TRUE;
}

/* ****************************************************************
 * CLR_get_rgb_yiq (mat)
 *  double        mat[3][3]     (mod) - matrix to be loaded
 *
 *  Copies the RGB to YIQ transformation into the user supplied
 *   matrix.  Returns TRUE if successful and FALSE if the CLR_
 *   routines are not initialized.
 */
int CLR_get_rgb_yiq(mat)
double  mat[][3];
{   if (!init) return FALSE;
    LOAD_MAT(mat, rgb_yiq_mat);
    return TRUE;
}

/* ****************************************************************
 * CLR_rgb_to_aux_rgb (to, from, rgb_aux)
 *  double        to[3][3]      (mod) - matrix to aux rgb
 *  double        from[3][3]    (mod) - matrix from aux rgb
 *  CLR_XYZ       rgb_aux[4]    (in)  - the color space definition,
 *                                      3 primaries and white
 *
 * Creates the transformations between RGB color space the CLR_
 *  routines are initialized for and another RGB color space as
 *  specified in 'aux_rgb'.  The 'to' and 'from' matrices are
 *  filled with the resulting transformations.  TRUE is returned
 *  if successful, FALSE is returned if the CLR_ routines are not
 *  initialized or if there is a singularity detected when the
 *  transformation is being generated.
 *
 * The technique used is described in Sect 3.2, Colorimetry and
 *  the RGB Monitor.
 */
int CLR_rgb_to_aux_rgb(to,from,rgb_aux)
double  to[][3], from[][3];
CLR_XYZ rgb_aux[];
{   CLR_XYZ     rgb_tmp[4];
    double      rgb_xyz_aux[3][3], xyz_rgb_aux[3][3];
    int         color;
    if (!init) return FALSE;
    /* normalize the chromaticities of the auxiliary primaries
     *  and white point.
     */
    for (color=RED; color<=WHITE; color++) {
	rgb_tmp[color] = rgb_aux[color];
	rgb_tmp[color].z = 1.0 - rgb_aux[color].x - rgb_aux[color].y;
    }
    /* get the transform between XYZ and the auxiliary RGB */
    if (!clr__cspace_to_xyz(rgb_tmp, rgb_xyz_aux)) return FALSE;
    if (!CLR_t_inverse(rgb_xyz_aux,xyz_rgb_aux)) return FALSE;
    /* concatenate with the transforms for the RGB color space
     *  that the CLR_ routine set is initialized to
     */
    (void)CLR_t_concat(xyz_rgb_aux, rgb_xyz_mat, to);
    (void)CLR_t_concat(xyz_rgb_mat, rgb_xyz_aux, from);
    return TRUE;
}

/* ****************************************************************
 * CLR_LAB CLR_xyz_to_lab (xyz)
 *  CLR_XYZ     xyz     (in) - xyz color (.01<=Y<=1)
 *
 * Returns L*a*b* given XYZ.  Returns (0,0,0) if the CLR_ routines
 *  are not initialized.  This transformation is described in
 *  Section 3.3, Alternate Color Representations, Eq.(\*(sC),
 *  and is taken from Judd and Wyszecki (1975) pg.320.  The
 *  transformation is scaled for (.01 < Y < 1) for consistency
 *  within the CLR_ routine set.
 */
CLR_LAB CLR_xyz_to_lab(xyz)
CLR_XYZ     xyz;
{   CLR_LAB     lab;
    if (!init) {lab.l = lab.a = lab.b = 0.0; return lab;}
    xyz.x *= 100.0;
    xyz.y *= 100.0;
    xyz.z *= 100.0;
    lab.l = 25.0 * pow(((100.0*xyz.y)/white.y),.33333) - 16.0;
    lab.a = 500.0 *
	(pow(xyz.x/white.x,.33333) - pow(xyz.y/white.y,.33333));
    lab.b = 200.0 *
	(pow(xyz.y/white.y,.33333) - pow(xyz.z/white.z,.33333));
    return lab;
}

/* ****************************************************************
 * CLR_LUV CLR_xyz_to_luv (xyz)
 *  CLR_XYZ     xyz     (in) - xyz color (.01<=Y<=1)
 *
 * Returns L*u*v* given XYZ.  Returns (0,0,0) if the CLR_ routines
 *  are not initialized.  This transformation is described in
 *  Section 3.3, Alternate Color Representations, Eq.(\*(sD),
 *  and is taken from Judd and Wyszecki (1975) pg.328.  The
 *  transformation is scaled for (.01 < Y < 1) for consistency
 *  within the CLR_ routine set.
 */
CLR_LUV CLR_xyz_to_luv(xyz)
CLR_XYZ     xyz;
{   CLR_LUV     luv;
    double      u, v;
    if (!init) {luv.l = luv.u = luv.v = 0.0; return luv;}
    xyz.x *= 100.0;
    xyz.y *= 100.0;
    xyz.z *= 100.0;
    luv.l = 25.0 * pow(((100.0*xyz.y)/white.y),.33333) - 16.0;
    u = (4.0 * xyz.x) / (xyz.x + (15.0 * xyz.y) + (3.0 * xyz.z));
    v = (9.0 * xyz.y) / (xyz.x + (15.0 * xyz.y) + (3.0 * xyz.z));
    luv.u = 13.0 * luv.l * (u - ref_luv.u);
    luv.v = 13.0 * luv.l * (v - ref_luv.v);
    return luv;
}

/* ****************************************************************
 * CLR_t_concat (m1, m2, m3)
 *  double        m1[3][3]      (in)  - matrix
 *  double        m2[3][3]      (in)  - matrix to concat
 *  double        m3[3][3]      (in)  - concatenated matrix
 *
 * Concatenate 'm1' to 'm2' resulting in 'm3'. In use, suppose you
 *  have an XYZ to RGB and an RGB to YIQ matrix.  Concatenate the
 *  RGB to YIQ matrix to the XYZ to RGB matrix to get an XYZ to
 *  YIQ matrix.  Returns TRUE.
 */
int CLR_t_concat(m1,m2,m3)
double  m1[][3], m2[][3], m3[][3];
{   double  t1[3][3], t2[3][3];
    LOAD_MAT(t1,m1);
    LOAD_MAT(t2,m2);
    {   int ii,jj;
	for (ii=0; ii<=2; ii++)
	    for (jj=0; jj<=2; jj++)
		m3[ii][jj] = (t1[ii][0] * t2[0][jj]) +
			     (t1[ii][1] * t2[1][jj]) +
			     (t1[ii][2] * t2[2][jj]);
    }
    return TRUE;
}

/* ****************************************************************
 * CLR_t_inverse (mat, inv_mat)
 *  double        mat[3][3]     (in)  - matrix to be inverted
 *  double        inv_mat[3][3] (mod) - inverted matrix
 *
 * Inverts 'mat' using Gaussian elimination.  Returns TRUE if
 *  successful and FALSE if there is a singularity.
 */
int CLR_t_inverse (mat, inv_mat)
double  mat[][3];
double  inv_mat[][3];
{   int     ii, jj, kk;
    double  tmp_mat[3][3], tmp_d;

    for (ii=0; ii<=2; ii++)
	for (jj=0; jj<=2; jj++) {
	    tmp_mat[ii][jj] = mat[ii][jj];
	    inv_mat[ii][jj] = (ii==jj ? 1.0 : 0.0);
	}

    for (ii=0; ii<=2; ii++) {
	for (jj=ii+1, kk=ii; jj<=2; jj++)
	    if (fabs(tmp_mat[jj][ii]) > fabs(tmp_mat[kk][ii]))
		kk = jj;

	/* check for singularity */
	if (tmp_mat[kk][ii] == 0.0) return FALSE;

	/* pivot - switch rows kk and ii */
	if (kk != ii)
	    for (jj=0; jj<=2; jj++) {
		tmp_d = tmp_mat[ii][jj];
		tmp_mat[ii][jj] = tmp_mat[kk][jj];
		tmp_mat[kk][jj] = tmp_d;
		tmp_d = inv_mat[ii][jj];
		inv_mat[ii][jj] = inv_mat[kk][jj];
		inv_mat[kk][jj] = tmp_d;
	    }

	/* normalize the row - make the diagonal 1 */
	for (tmp_d = 1.0 / tmp_mat[ii][ii], jj=0; jj<=2; jj++) {
	    tmp_mat[ii][jj] *= tmp_d;
	    inv_mat[ii][jj] *= tmp_d;
	}

	/* zero the non-diagonal terms in this column */
	for (jj=0; jj<=2; jj++)
	    if (jj != ii)
		for (tmp_d = -tmp_mat[jj][ii], kk=0; kk<=2; kk++) {
		    tmp_mat[jj][kk] += tmp_mat[ii][kk] * tmp_d;
		    inv_mat[jj][kk] += inv_mat[ii][kk] * tmp_d;
		}
    }

    return TRUE;
}

/* ****************************************************************
 * clr__cspace_to_xyz (cspace, t_mat)
 *  CLR_XYZ       cspace[4]   (in)  - the color space definition,
 *                                      3 primaries and white
 *  double        t_mat[3][3] (mod) - the color transformation
 *
 * Builds the transformation from a set of primaries to the CIEXYZ
 *  color space.  This is the basis for the generation of the color
 *  transformations in the CLR_ routine set.  The method used is
 *  that detailed in Sect 3.2 Colorimetry and the RGB monitor.
 *  Returns FALSE if there is a singularity.
 */
static int clr__cspace_to_xyz (cspace, t_mat)
CLR_XYZ     cspace[];
double      t_mat[][3];
{   int     ii, jj, kk, tmp_i, ind[3];
    double  mult, white[3], scale[3];

    /* normalize the white point to Y=1 */
    if (cspace[WHITE].y <= 0.0) return FALSE;
    white[0] = cspace[WHITE].x / cspace[WHITE].y;
    white[1] = 1.0;
    white[2] = cspace[WHITE].z / cspace[WHITE].y;

    for (ii=0; ii<=2; ii++) {
	t_mat[0][ii] = cspace[ii].x;
	t_mat[1][ii] = cspace[ii].y;
	t_mat[2][ii] = cspace[ii].z;
	ind[ii] = ii;
    }

    /* gaussian elimination  with partial pivoting */
    for (ii=0; ii<2; ii++) {
	for (jj=ii+1; jj<=2; jj++)
	    if (fabs(t_mat[ind[jj]][ii]) >
		    fabs(t_mat[ind[ii]][ii])) {
		tmp_i=ind[jj]; ind[jj]=ind[ii]; ind[ii]=tmp_i;
	    }
	if (t_mat[ind[ii]][ii] == 0.0) return FALSE;

	for (jj=ii+1; jj<=2; jj++) {
	    mult = t_mat[ind[jj]][ii] / t_mat[ind[ii]][ii];
	    for (kk=ii+1; kk<=2; kk++)
		t_mat[ind[jj]][kk] -= t_mat[ind[ii]][kk] * mult;
	    white[ind[jj]] -= white[ind[ii]] * mult;
	}
    }
    if (t_mat[ind[2]][2] == 0.0) return FALSE;

    /* back substitution to solve for scale */
    scale[ind[2]] = white[ind[2]] / t_mat[ind[2]][2];
    scale[ind[1]] = (white[ind[1]] - (t_mat[ind[1]][2] *
	scale[ind[2]])) / t_mat[ind[1]][1];
    scale[ind[0]] = (white[ind[0]] - (t_mat[ind[0]][1] *
	scale[ind[1]]) - (t_mat[ind[0]][2] * scale[ind[2]])) /
	t_mat[ind[0]][0];

    /* build matrix */
    for (ii=0; ii<=2; ii++) {
	t_mat[0][ii] = cspace[ii].x * scale[ii];
	t_mat[1][ii] = cspace[ii].y * scale[ii];
	t_mat[2][ii] = cspace[ii].z * scale[ii];
    }

    return TRUE;
}

/* ****************************************************************
 * CLR_exit()
 *
 * Completes use of the CLR_ routine set and frees any
 *   allocated space
 */
CLR_exit()
{
    if (!init) return TRUE;
    (void)CLR_exit_samples();
    if (X_tristim != NULL) free((char *)X_tristim);
    if (Y_tristim != NULL) free((char *)Y_tristim);
    if (Z_tristim != NULL) free((char *)Z_tristim);
    if (work_curve != NULL) free((char *)work_curve);
    X_tristim = Y_tristim = Z_tristim = work_curve = NULL;
    init = FALSE;
    return TRUE;
}
/* ************************************************************* */
use of the CLR_ routine set and frees any
 *   allocated space
 */
CLR_exit()
{
    if (!init) return TRUE;
    (void)CLR_exit_samples();
    if (X_tristim != NULL) free((char *)X_tristim);
    if (Y_tristim != NULL) free((char *)Y_tristim);
    if (Z_tristim != NUclr_clip.c
 *                           clr_clip.c
 * ****************************************************************
 *  MODULE PURPOSE:
 *      This module contains routines for bringing colors outside
 *      the displayable gamut into the displayable gamut.
 *
 *  MODULE CONTENTS:
 *      CLR_clamp_rgb   - clamps rgb values to 0.0-1.0 range
 *      CLR_scale_rgb   - scales rgb values to 0.0-1.0 range
 *      CLR_clip_rgb    - clips rgb values to 0.0-1.0 range
 */
#include <stdio.h>
#include <math.h>
#include "clr.h"

/* define the max and min scaling values for equal intensity
 *  clipping.  These are defined to be slightly inside of the
 *  0 to 1 range to avoid numerical problems that occur when
 *  colors are on or very close to the clipping boundary.
 */
#define MAX_CLIP    0.9999  /* 1 - MAX_CLIP > 1 res step */
#define MIN_CLIP    0.0001  /* less than 1 res step */

/* ****************************************************************
 * CLR_clamp_rgb (rgb)
 *  CLR_RGB     rgb     (in)  - the input rgb
 *
 * Returns clamped color.  Values greater than 1 are  clamped to 1,
 *  values less than 0 are clamped to 0.
 */
CLR_RGB CLR_clamp_rgb(in_rgb)
CLR_RGB     in_rgb;
{   CLR_RGB     rgb;
    rgb = in_rgb;
    if (rgb.r < 0.0) rgb.r = 0.0;
    else if (rgb.r > 1.0) rgb.r = 1.0;
    if (rgb.g < 0.0) rgb.g = 0.0;
    else if (rgb.g > 1.0) rgb.g = 1.0;
    if (rgb.b < 0.0) rgb.b = 0.0;
    else if (rgb.b > 1.0) rgb.b = 1.0;
    return rgb;
}

/* ****************************************************************
 * CLR_scale_rgb (rgb)
 *  CLR_RGB     rgb     (in)  - the input rgb
 *
 * Returns scaled color.  Values less than 0 are clamped to zero.
 *  If any values are greater than 1, all color components are
 *  scaled so the offending value is equal to 1.
 */
CLR_RGB CLR_scale_rgb(in_rgb)
CLR_RGB     in_rgb;
{   double      scale=1.0;
    double      tmp_scale;
    CLR_RGB     rgb;
    rgb = in_rgb;
    if (rgb.r < 0.0) rgb.r = 0.0;
    else if ((rgb.r > 1.0) && ((tmp_scale = (1.0 / rgb.r))
	< scale)) scale = tmp_scale;
    if (rgb.g < 0.0) rgb.g = 0.0;
    else if ((rgb.g > 1.0) && ((tmp_scale = (1.0 / rgb.g))
	< scale)) scale = tmp_scale;
    if (rgb.b < 0.0) rgb.b = 0.0;
    else if ((rgb.b > 1.0) && ((tmp_scale = (1.0 / rgb.b))
	< scale)) scale = tmp_scale;
    if (scale < 1.0) {
	rgb.r *= scale;
	rgb.g *= scale;
	rgb.b *= scale;
    }
    return rgb;
}

/* ****************************************************************
 * CLR_RGB CLR_clip_rgb(in_rgb)
 *  CLR_RGB     in_rgb  (in) - the input rgb
 *
 * Returns the clipped color - clipping is by desaturating the
 *  color by shifting it towards the neutral axis in a plane
 *  perpendicular to the neutral axis.  The neutral axis is taken
 *  as the vector from the monitor black to the monitor white.
 */
CLR_RGB CLR_clip_rgb(in_rgb)
CLR_RGB     in_rgb;
{   CLR_RGB     diff, out_rgb;
    double      axis_rgb, diff_mult, tmp_mult;

    /* check to see if clipping is required
     */
    if ((in_rgb.r < 0.0) || (in_rgb.r > 1.0) ||
	(in_rgb.g < 0.0) || (in_rgb.g > 1.0) ||
	(in_rgb.b < 0.0) || (in_rgb.b > 1.0)) {
	/* clipping is required, determine the distance from
	 *  the origin to the equal intensity plane containing
	 *  the color.  The distance is normalized the origin
	 *  at color (0,0,0) and a distance of 1 at (1,1,1)
	 */
	axis_rgb = (in_rgb.r + in_rgb.g + in_rgb.b) * .333333;
	/* check for the intensity plane of the color being
	 *  outside the displayable range -- if it is, set
	 *  color to either black or white.
	 */
	if (axis_rgb <= MIN_CLIP)
	    /* this is not a visible color -- it should not
	     *  have been computed
	     */
	    out_rgb.r = out_rgb.g = out_rgb.b = 0.0;
	else if (axis_rgb >= MAX_CLIP)
	    /* This is way beyond white in intensity, set it
	     *  to the white point.
	     */
	    out_rgb.r = out_rgb.g = out_rgb.b = 1.0;
	else {
	    /* the intensity plane is within the displayable
	     *  range.  Compute the vector from the neutral
	     *  axis to the color on it's intensity plane.
	     *  The intersection of the neutral axis and the
	     *  intensity plane is at r=g=b=axis_rgb.
	     */
	    diff.r = in_rgb.r - axis_rgb;
	    diff.g = in_rgb.g - axis_rgb;
	    diff.b = in_rgb.b - axis_rgb;
	    /* determine the relative length of the vector
	     *  to the edge of the displayable color gamut.
	     */
	    diff_mult = 1.0;
	    if (in_rgb.r > 1.0) {
		if ((tmp_mult = (MAX_CLIP - axis_rgb) / diff.r)
		    < diff_mult) diff_mult = tmp_mult;
	    } else if (in_rgb.r < 0.0) {
		if ((tmp_mult = (MIN_CLIP - axis_rgb) / diff.r)
		    < diff_mult) diff_mult = tmp_mult;
	    }

	    if (in_rgb.g > 1.0) {
		if ((tmp_mult = (MAX_CLIP - axis_rgb) / diff.g)
		    < diff_mult) diff_mult = tmp_mult;
	    } else if (in_rgb.g < 0.0) {
		if ((tmp_mult = (MIN_CLIP - axis_rgb) / diff.g)
		    < diff_mult) diff_mult = tmp_mult;
	    }

	    if (in_rgb.b > 1.0) {
		if ((tmp_mult = (MAX_CLIP - axis_rgb) / diff.b)
		    < diff_mult) diff_mult = tmp_mult;
	    } else if (in_rgb.b < 0.0) {
		if ((tmp_mult = (MIN_CLIP - axis_rgb) / diff.b)
		    < diff_mult) diff_mult = tmp_mult;
	    }

	    /* determine the location of the color at the
	     *  edge of the displayable color gamut.
	     */
	    out_rgb.r = axis_rgb + (diff.r * diff_mult);
	    out_rgb.g = axis_rgb + (diff.g * diff_mult);
	    out_rgb.b = axis_rgb + (diff.b * diff_mult);
	}
	return out_rgb;
    }
    else
	return in_rgb;
}
/* ************************************************************* */
;
	    } else if (in_rgb.b < 0.0) {
		if ((tmp_mulclr_sample.c
 *                         clr_sample.c
 * ****************************************************************
 *  MODULE PURPOSE:
 *      This module contains the routines for spectral sampling.
 *      The operations include defining the sampling space,
 *      sampling spectral curves, and transformations from spectral
 *      sample space to RGB or XYZ.
 *
 *  MODULE CONTENTS:
 *      CLR_init_samples    - initialize spectral sampling
 *      CLR_num_samples     - returns the number of samples
 *      CLR_spect_to_sample - sample a spectral curve
 *      CLR_get_sample_rgb  - get the sample to RGB matrix
 *      CLR_get_sample_xyz  - get the sample to XYZ matrix
 *      CLR_reconstruct     - reconstruct a spectral curve
 *      CLR_exit_samples    - finish with spectral sampling
 *
 *  NOTES:
 *      > The CLR_ routines must be initialized (CLR_init()) before
 *          using any of the sampling routines.
 *
 *      > When the CLR_SAMPLE_HALL method is selected, sampling
 *          uses abutting, non-overlapping, box sample and
 *          reconstruction functions are described in Section
 *          3.5.2 Spectral Sampling Approaches, and in Hall (1983).
 *          This provides continuous sampling between the low bound
 *          of the first sample and the high bound of the last
 *          sample.  For applications requiring discrete isolated
 *          samples, user modification of these routines
 *          is required.
 *
 *      > When the CLR_SAMPLE_MEYER method is used, 4 samples are
 *          used as described in Meyer (1988).
 *
 *      > When using CLR_SAMPLE_LIN_RAMP method is used, sampling
 *          uses box convolution filters and reconstruction uses
 *          overlapping linear ramped triangles.
 */
#include <stdio.h>
#include "clr.h"

static int      sample_type = -1;
static int      samples = 0;
static double   *sample_func = NULL;
static double   *reconst_func = NULL;
static double   XYZ_to_ACC[3][3] = {{-0.0177,   1.0090, 0.0073},
				    {-1.5370,   1.0821, 0.3209},
				    { 0.1946,  -0.2045, 0.5264}};
static double   ACC_to_XYZ[3][3];
static double   samp_to_ACC[3][4] =
		    {{0.00000,  0.18892,    0.67493,    0.19253},
		     {0.00000,  0.31824,    0.00000,   -0.46008},
		     {0.54640,  0.00000,    0.00000,    0.00000}};

/* These are the bounds for the Hall method optimized for the
 *  Macbeth Colorchecker chart illuminated by D5500, D6500, D7500,
 *  and standard illuminant C
 */
static int      Hall_bounds[10][11] = {
    {544, 627, 000, 000, 000, 000, 000, 000, 000, 000, 000},
    {407, 535, 652, 000, 000, 000, 000, 000, 000, 000, 000},
    {414, 494, 580, 658, 000, 000, 000, 000, 000, 000, 000},
    {414, 494, 562, 595, 656, 000, 000, 000, 000, 000, 000},
    {418, 479, 511, 563, 595, 654, 000, 000, 000, 000, 000},
    {418, 479, 510, 555, 581, 604, 655, 000, 000, 000, 000},
    {419, 472, 494, 517, 556, 580, 604, 655, 000, 000, 000},
    {419, 473, 495, 517, 552, 573, 591, 613, 659, 000, 000},
    {419, 468, 486, 504, 522, 552, 571, 588, 609, 656, 000},
    {415, 427, 473, 494, 515, 545, 564, 580, 595, 615, 659} };

extern char     *malloc();

/* ****************************************************************
 * CLR_init_samples (method, num_samples, sample_bounds)
 *  int     method          (in) - sampling method:
 *                              CLR_SAMPLE_MEYER    Meyer (1988)
 *                              CLR_SAMPLE_HALL     Hall (1983)
 *  int     num_samples     (in) - number of sample functions
 *  int     sample_bounds[] (in) - boundaries of the sampling
 *                              functions.  There must be
 *                              num_samples+1 bounds arranged in
 *                              ascending order in this array. If
 *                              a NULL pointer is passed in and
 *                              num_samples <= 10, then a default
 *                              sampling is used.
 *
 * For the CLR_SAMPLE_HALL method the bound wavelength is included
 *  in the sampling function.  For example, using 3 samples with
 *  bounds at (411, 491, 571, 651), the actual samples are 411-490,
 *  491-570, and 571-650.
 *
 * The CLR_SAMPLE_MEYER method uses a prescribed sampling with 4
 *  samples.  The num_samples and sample_bounds arguments are
 *  ignored.
 *
 * Returns TRUE if successful, FALSE if sample bounds are not valid
 *  or sampling is previously initialized to some other value.
 */
CLR_init_samples (method, num_samples, sample_bounds)
int     method;
int     num_samples;
int     *sample_bounds;
{   int     ct;

    CLR_exit_samples ();
    if (method == CLR_SAMPLE_MEYER) {
	samples = 4;
	sample_type = method;
	CLR_t_inverse (XYZ_to_ACC, ACC_to_XYZ);
    }
    else if (method == CLR_SAMPLE_HALL) {
	/* build a set of sampling and reconstruction curves
	 */
	int     min_wl, max_wl, cur_wl;
	double  *cur_s, *cur_r, fill;

	if (num_samples <= 0) return FALSE;
	if (sample_bounds == NULL)
	    sample_bounds = Hall_bounds[num_samples - 1];
	if ((min_wl = CLR_get_min_wl()) < 0) return FALSE;
	max_wl = CLR_get_max_wl();

	if ((sample_func = (double *)malloc((unsigned)(sizeof(double) *
	    num_samples * (max_wl - min_wl + 1)))) == NULL) goto error;
	if ((reconst_func = (double *)malloc((unsigned)(sizeof(double) *
	    num_samples * (max_wl - min_wl + 1)))) == NULL) goto error;

	cur_s = sample_func;
	cur_r = reconst_func;
	for (ct=0; ct<num_samples; ct++) {
	    if (ct == 0) fill = 1.0;
	    else fill = 0.0;
	    for (cur_wl=min_wl ;
		(cur_wl<sample_bounds[ct]) && (cur_wl<=max_wl);
		cur_wl++, *cur_s++ = 0.0, *cur_r++ = fill) ;

	    fill = 1.0 / (sample_bounds[ct+1] - sample_bounds[ct]);
	    for ( ;(cur_wl<sample_bounds[ct+1]) && (cur_wl<=max_wl);
		cur_wl++, *cur_s++ = fill, *cur_r++ = 1.0) ;

	    if (ct == (num_samples-1)) fill = 1.0;
	    else fill = 0.0;
	    for ( ;cur_wl<=max_wl;
		cur_wl++, *cur_s++ = 0.0, *cur_r++ = fill) ;
	}
	samples = num_samples;
	sample_type = method;
    }
    else if (method == CLR_SAMPLE_LIN_RAMP) {
	int     min_wl, max_wl, cur_wl, mid_wl[3];
	double  *cur_s, *cur_r, fill;
	double  r, r_inc;

	if (num_samples <= 0) return FALSE;
	if ((min_wl = CLR_get_min_wl()) < 0) return FALSE;
	max_wl = CLR_get_max_wl();

	if ((sample_func = (double *)malloc((unsigned)(sizeof(double) *
	    num_samples * (max_wl - min_wl + 1)))) == NULL) goto error;
	if ((reconst_func = (double *)malloc((unsigned)(sizeof(double) *
	    num_samples * (max_wl - min_wl + 1)))) == NULL) goto error;

	cur_s = sample_func;
	for (ct=0; ct<num_samples; ct++) {
	    for (cur_wl=min_wl ;
		(cur_wl<sample_bounds[ct]) && (cur_wl<=max_wl);
		cur_wl++, *cur_s++ = 0.0) ;

	    fill = 1.0 / (sample_bounds[ct+1] - sample_bounds[ct]);
	    for ( ;(cur_wl<sample_bounds[ct+1]) && (cur_wl<=max_wl);
		cur_wl++, *cur_s++ = fill) ;

	    for ( ;cur_wl<=max_wl; cur_wl++, *cur_s++ = 0.0) ;
	}

	cur_r = reconst_func;
	mid_wl[0] = min_wl;
	mid_wl[1] = (sample_bounds[0] + sample_bounds[1]) / 2;
	for (ct=0; ct<num_samples; ct++, mid_wl[0]=mid_wl[1],
		mid_wl[1]=mid_wl[2]) {
	    for (cur_wl=min_wl ;
		(cur_wl<mid_wl[0]) && (cur_wl<=max_wl);
		cur_wl++, *cur_r++ = 0.0) ;

	    if (ct == 0) {r = 1.0; r_inc = 0.0;}
	    else {
		r = 0.0;
		r_inc = 1.0 / (mid_wl[1]-cur_wl);
	    }
	    for ( ;(cur_wl<mid_wl[1]) && (cur_wl<=max_wl);
		cur_wl++, *cur_r++ = r, r += r_inc) ;

	    if (ct == (num_samples-1)) {
		mid_wl[2] = max_wl + 1;
		r = 1.0; r_inc = 0.0;
	    }
	    else {
		mid_wl[2] = (sample_bounds[ct+1] + sample_bounds[ct+2]) / 2;
		r = 1.0;
		r_inc = -(1.0 / (mid_wl[2]-cur_wl));
	    }

	    for ( ;(cur_wl<mid_wl[2]) && (cur_wl<=max_wl);
		cur_wl++, *cur_r++ = r, r += r_inc) ;

	    for ( ;cur_wl<=max_wl; cur_wl++, *cur_r++ = 0.0) ;
	}
	samples = num_samples;
	sample_type = method;
    }
    else {
	goto error;
    }
    return TRUE;

error:
    (void)CLR_exit_samples();
    return FALSE;
}

/* ****************************************************************
 * CLR_num_samples()
 *
 * Returns the number of samples for which sampling is initialized.
 *  Returns 0 if sampling is not initialized.
 */
CLR_num_samples()
{   return samples;
}


/* ****************************************************************
 * CLR_spect_to_sample (spectral, sample)
 *  double  *spectral   (in)  - spectral curve to be sampled
 *  double  *sample     (mod) - array to receive the sampled
 *                          values.
 * Samples 'spectral' and loads the sample values into 'sample'.
 *  Returns TRUE if successful and FALSE if CLR_ or the sampling
 *  has not been initialized
 */
CLR_spect_to_sample (spectral, sample)
double      *spectral;
double      *sample;
{   int     ct, min_wl, max_wl, cur_samp;

    if (samples <= 0) return FALSE;
    if ((min_wl = CLR_get_min_wl()) < 0) return FALSE;
    max_wl = CLR_get_max_wl();

    if (sample_type == CLR_SAMPLE_MEYER) {
	/* sample at 456.4nm, 490.9nm, 557.7nm, and 631.4nm
	 */
	if ((min_wl > 456) || (max_wl < 457)) *sample++ = 0.0;
	else *sample++ = spectral[456-min_wl] + (0.4 *
		(spectral[457-min_wl] - spectral[456-min_wl]));

	if ((min_wl > 490) || (max_wl < 491)) *sample++ = 0.0;
	else *sample++ = spectral[490-min_wl] + (0.9 *
		(spectral[491-min_wl] - spectral[490-min_wl]));

	if ((min_wl > 557) || (max_wl < 558)) *sample++ = 0.0;
	else *sample++ = spectral[557-min_wl] + (0.7 *
		(spectral[558-min_wl] - spectral[557-min_wl]));

	if ((min_wl > 631) || (max_wl < 632)) *sample++ = 0.0;
	else *sample++ = spectral[631-min_wl] + (0.4 *
		(spectral[632-min_wl] - spectral[631-min_wl]));
    }
    else if ((sample_type == CLR_SAMPLE_HALL) ||
	     (sample_type == CLR_SAMPLE_LIN_RAMP)) {
	double  *cur_s, *spec;

	cur_s = sample_func;
	for (cur_samp=0;
		cur_samp<samples; cur_samp++, sample++) {
	    for (spec=spectral, *sample=0.0, ct=max_wl-min_wl+1;
		    --ct>=0; ) *sample += *spec++ * *cur_s++;
	}
    }
    else
	return FALSE;

    return TRUE;
}

/* ****************************************************************
 * CLR_get_sample_rgb (matrix)
 *  double  *matrix     (mod) - matrix to be filled.
 *
 * Returns the matrix for conversion from the sampled space to the
 *  RGB the CLR_ routines have been initialized for.  The matrix
 *  is a 3 x num_samples matrix.
 */
CLR_get_sample_rgb(matrix)
double      *matrix;
{
    double      *xyz = NULL;
    double      xyz_rgb[3][3];
    int         ct;

    /* get the XYZ matrix, then transform it into RGB
     */
    if ((xyz = (double *)malloc((unsigned)(3 *
	sizeof(double) * samples))) == NULL) goto error;
    if (!CLR_get_sample_xyz(xyz)) goto error;
    if (!CLR_get_xyz_rgb(xyz_rgb)) goto error;

    for (ct=0; ct<samples; ct++, matrix++, xyz++) {
	matrix[0] = (xyz_rgb[0][0] * xyz[0]) +
		    (xyz_rgb[0][1] * xyz[samples]) +
		    (xyz_rgb[0][2] * xyz[2*samples]);
	matrix[samples] = (xyz_rgb[1][0] * xyz[0]) +
		    (xyz_rgb[1][1] * xyz[samples]) +
		    (xyz_rgb[1][2] * xyz[2*samples]);
	matrix[2*samples] = (xyz_rgb[2][0] * xyz[0]) +
		    (xyz_rgb[2][1] * xyz[samples]) +
		    (xyz_rgb[2][2] * xyz[2*samples]);
    }

    free((char *)xyz);
    return TRUE;

error:
    if (xyz != NULL) free((char *)xyz);
    return FALSE;
}

/* ****************************************************************
 * CLR_get_sample_xyz (matrix)
 *  double  *matrix     (mod) - matrix to be filled.
 *
 * Returns the matrix for conversion from the sampled space to
 *  CIEXYZ. The matrix is a 3 x num_samples matrix.
 */
CLR_get_sample_xyz(matrix)
double      *matrix;
{
    int     min_wl, max_wl, cur_wl, cur_samp;
    double  *cur_r, fill, *samp_mat;
    CLR_XYZ xyz;

    if (samples <= 0) return FALSE;
    if (sample_type == CLR_SAMPLE_MEYER) {
	/* concatenate the sample to ACC matrix with the ACC_to_XYZ
	 *  matrix.  The divide by 1.057863 is a normalization so
	 *  than an identity curve has a Y value of 1.0 following
	 *  the conventions used in the CLR_routines.
	 */
	samp_mat = samp_to_ACC[0];
	for (cur_samp=0; cur_samp<samples; cur_samp++,
		matrix++, samp_mat++) {
	    matrix[0] = ((ACC_to_XYZ[0][0] * samp_mat[0]) +
		(ACC_to_XYZ[0][1] * samp_mat[samples]) +
		(ACC_to_XYZ[0][2] * samp_mat[2*samples])) /
		1.057863;
	    matrix[samples] = ((ACC_to_XYZ[1][0] * samp_mat[0]) +
		(ACC_to_XYZ[1][1] * samp_mat[samples]) +
		(ACC_to_XYZ[1][2] * samp_mat[2*samples])) /
		1.057863;
	    matrix[2*samples] = ((ACC_to_XYZ[2][0] * samp_mat[0]) +
		(ACC_to_XYZ[2][1] * samp_mat[samples]) +
		(ACC_to_XYZ[2][2] * samp_mat[2*samples])) /
		1.057863;
	}
    }
    else if ((sample_type == CLR_SAMPLE_HALL) ||
	     (sample_type == CLR_SAMPLE_LIN_RAMP)) {
	min_wl = CLR_get_min_wl();
	max_wl = CLR_get_max_wl();

	cur_r = reconst_func;
	for (cur_samp=0; cur_samp<samples;
		cur_samp++, matrix++, cur_r += (max_wl-min_wl+1)) {
	    xyz = CLR_spect_to_xyz (cur_r);
	    matrix[0] = xyz.x;
	    matrix[samples] = xyz.y;
	    matrix[2*samples] = xyz.z;
	}
    }
    return TRUE;
}

/* ****************************************************************
 * CLR_reconstruct (sample, spectral)
 *  double  *sample     (in)  - the sample values
 *  double  *spectral   (mod) - the reconstructed spectral curve
 *
 * Reconstructs a spectral curve from the sample values.  The
 *  reconstruction functions are box functions resulting a a step
 *  function as the reconstructed curve. Spectral curves can be
 *  reconstructed for Hall sampling only.
 */
CLR_reconstruct (sample, spectral)
double      *sample, *spectral;
{   int     min_wl, max_wl, cur_samp, ct;
    double  *spec, *cur_r;
    if (samples <= 0) return FALSE;
    if ((sample_type == CLR_SAMPLE_HALL) ||
	    (sample_type == CLR_SAMPLE_LIN_RAMP)) {
	min_wl = CLR_get_min_wl();
	max_wl = CLR_get_max_wl();

	for (spec=spectral, ct=max_wl-min_wl+1; --ct>=0; )
	    *spec++ = 0.0;
	cur_r = reconst_func;
	for (cur_samp=0; cur_samp<samples; cur_samp++, sample++) {
	    for (spec=spectral, ct=max_wl-min_wl+1; --ct>=0; )
		*spec++ += *sample * *cur_r++;
	}
    }
    else return FALSE;
    return TRUE;
}

/* ****************************************************************
 * CLR_exit_samples()
 *
 * Complete use of spectral sampling, free any allocated space.
 */
CLR_exit_samples ()
{   sample_type = -1;
    samples = 0;
    if (sample_func != NULL) {
	free((char *)sample_func);
	sample_func = NULL;
    }
    if (reconst_func != NULL) {
	free((char *)reconst_func);
	reconst_func = NULL;
    }
    return TRUE;
}
; --ct>=0; )
		*spec++ += *sample * *cur_r++;
	}
    }
    else return FALSE;
    return TRUE;
}

/* ****************************************************************
 * CLR_exit_samples()
 *
 * Complete use of spectral sampling, free any allocated space.
 */
CLR_exit_samples ()
{   sample_type = -1;
    samples = 0;
    if (sample_func != NULL) {
	free((char *)sample_func);
	sampillum_mod.h
 *                         illum_mod.h
 * ****************************************************************
 * include file for the illumination models
 */
#ifndef ILLUM_H
#define ILLUM_H
#include "geo.h"

/* The material structure maintains the description of a material
 *  interface.  An interface between a conductor or dielectric and
 *  air is characterized by loading the properties of the material
 *  and an index of refraction of 1 for the outside material.  An
 *  interface between two materials is characterized by generating
 *  a pseudo-material as described in appendix III.5.1, Approxima-
 *  tion Methodology.
 *
 * In filling the material structure, the reflected direction is
 *  the 'outside' of the material.  That is, for an interface
 *  between air and glass, for example, the the reflected direction
 *  or 'outside' is air (Ar_spectral = NULL, nr=1.0), and the
 *  transmitted direction is glass (nt=1.5, etc.)
 *
 * Individual spectral components of the material are characterized
 *  by the sampled spectral values and a multiplier to scale these
 *  values (as discussed in appendix II.1.5, Selecting Ka, Ka, Ks,
 *  Ft and Fr)
 */

typedef struct {
    double  Ka_scale;       /* ambient multiplier */
    double  *Ka_spectral;   /* sampled ambient spectral curve */
    double  Kd_scale;       /* diffuse multiplier */
    double  *Kd_spectral;   /* sampled diffuse spectral curve */
    double  Ks_scale;       /* specular multiplier */
    double  *Ks_spectral;   /* sampled specular spectral curve */
    double  Kt_scale;       /* transmitted multiplier            */
    double  *Kt_spectral;   /* sampled specular transmitted      */
			    /*  curve.  The Kt_ properties are   */
			    /* used for the Whitted model only   */
    double  *Ar_spectral;   /* sampled filter spectral curve.    */
    double  *At_spectral;   /* sampled filter spectral curve     */
			    /*  These are used in the Hall model */
			    /*  only.  Filter attenuation is     */
			    /*  ignored if a NULL pointer is     */
			    /*  specified.                       */
    double  beta;           /* roughness indicator */
    double  (*D)();         /* microfacet distribution */
    double  D_coeff;        /* the coefficient for the           */
			    /*  microfacet distribution function */
			    /*  computed from by microfacet      */
			    /*  distribution init function       */
    double  (*G)();         /* geometric attenuation function */
    double  G_coeff;        /* the coefficient for the geometric */
			    /*  attenuation function (m_2 for    */
			    /*  the Sancer function, unused for  */
			    /*  the Torrance-Sparrow function)   */
    double  Ro;             /* average reflectance */
    double  nr;             /* index of refraction (incident) */
    double  nt;             /* index of refraction (transmitted) */
    double  k;              /* absorption coefficient */
    int     conductor;      /* flags the specular material as a  */
			    /*  conductor */
    int     transparent;    /* flags whether the material is     */
			    /*  transparent --- note, composite  */
			    /*  materials have a dielectric      */
			    /*  specular component but may not   */
			    /*  be transparent                   */
    int     r_is_air;       /* flags that the 'outside' or       */
			    /*  reflect side of the interface is */
			    /*  air                              */
} ILLUM_MATL;

typedef struct {
    double  I_scale;        /* illumination multiplier */
    double  *I_spectral;    /* sampled source spectral curve */
    POINT3  center;         /* center of the light source */
} ILLUM_LGT;

int     IM_init();
double  *IM_bouknight();
double  *IM_phong();
double  *IM_blinn();
double  *IM_whitted();
double  *IM_hall();
int     IM_exit();

#endif CLR_H
/* ************************************************************* */
is */
			    /*  air                              */
} ILLUM_MATL;

typedef struct {
    double  I_scale;        /* illumination muillum_mod.c
 *                         illum_mod.c
 * ****************************************************************
 *  MODULE PURPOSE:
 *      This module contains the source code for the illumination
 *      models discussed for in Section 4.2, Incremental Shading,
 *      Empirical Models, and in Section 4.3, Ray Tracing,
 *      Transitional Models.
 *
 *  MODULE CONTENTS:
 *      IM_init         - initialize the illumination models
 *      IM_bouknight    - Bouknight (1970) illumination model
 *      IM_phong        - Phong (1975) illumination model
 *      IM_blinn        - Blinn (1976) illumination model
 *      IM_whitted      - Whitted (1980) illumination model
 *      IM_hall         - Hall (1983) illumination model
 *      IM_exit         - finish with the illumination models
 *
 *  NOTES:
 *      > The illumination model is called once a
 *          point on a surface has been identified and the list
 *          of illuminating light sources has been generated.
 *
 *      > There exists a routine that the illumination models can
 *          call to get a color from any direction.  Specifically
 *          this is used for inquiring about the reflected or
 *          transmitted directions in the ray tracing models.
 *          This routine is passed the view vector for which the
 *          color is required.
 *
 *      > a common calling convention is used for ease in
 *          interchanging the illumination model routines.  Each
 *          routine is passed the location and orientation of the
 *          surface, a view vector, an interface material, a
 *          description of the lighting, and an array to receive
 *          the computed color.
 *
 *          The orientation of the surface is given by the surface
 *          normal which is ALWAYS directed to the 'outside' or
 *          reflected side of the material.
 *
 *          The view vector is specified by start position and
 *          direction vector.  During visibility computations the
 *          view vector is typically directed towards the surface.
 *          The direction cosines MUST be negated prior to calling
 *          the illumination model for consistency with the vector
 *          conventions used.
 *
 *          See 'illum_mod.h' for material details.
 *
 *          The light vector is a list of pointers to ILLUM_LGT
 *          structures terminated by a NULL pointer.  The first
 *          entry is taken as the ambient illumination.  Only
 *          light that is incident from the air side of a material
 *          can provide illumination.
 *
 *      > These models assume that the material structure is
 *          correctly loaded and that the surface is facing the
 *          viewer (N.V > 0) for illumination models that do not
 *          consider transparency.
 */
#include <stdio.h>
#include <math.h>
#include "illum_mod.h"
#include "F.h"

static int      num_samples = 0;
static int      (*get_color)() = NULL;
static double   *Fr_buffer = NULL;
static double   *Ft_buffer = NULL;

/* ****************************************************************
 * int IM_init (num_clr_samples, get_clr_routine)
 *  int     num_clr_samples         (in) - number of color samples
 *  int     (*get_clr_routine)()    (in) - routine to call to get
 *                                    the color from some direction
 *
 * Initializes the illumination model routine set, returns TRUE
 *  if successful and FALSE upon failure.
 */
IM_init (num_clr_samples, get_clr_routine)
int     num_clr_samples, (*get_clr_routine)();
{   char    *malloc();
    if (((num_samples = num_clr_samples) <= 0) ||
	  ((Fr_buffer = (double *)malloc((unsigned)(num_samples *
	    sizeof(double)))) == NULL) ||
	  ((Ft_buffer = (double *)malloc((unsigned)(num_samples *
	    sizeof(double)))) == NULL)) {
	(void)IM_exit();
	return FALSE;
    }
    get_color = get_clr_routine;
    return TRUE;
}

/* ****************************************************************
 * double *IM_bouknight (surface, V, matl, lgts, color)
 *  LINE        *surface    (in)  - surface for color computation
 *  LINE        *V          (in)  - view vector
 *  ILLUM_MATL  *matl       (in)  - material properties
 *  ILLUM_LGT   **lgts      (in)  - illuminating sources
 *  double      *color      (mod) - array to receive the color
 *
 * Evaluates the color using the Bouknight (1970) illumination
 *  model as described in Eq.(\*(sE).  Returns 'color' upon
 *  success and NULL upon failure.
 */
double *IM_bouknight (surface, V, matl, lgts, color)
LINE        *surface, *V;
ILLUM_MATL  *matl;
ILLUM_LGT   **lgts;
double      *color;
{   int         ct;
    double      N_dot_L;
    LINE        L;

    /* load the ambient illumination */
    for (ct=0; ct<num_samples; ct++) {
      color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
	(*lgts)->I_scale * (*lgts)->I_spectral[ct];
    }
    lgts++;

    /* load the diffuse component of the illumination.  Loop
     *  through the lights and compute (N.L).  If it is positive
     *  then the surface is illuminated.
     */
    while (*lgts != NULL) {
      if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
	  && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)) {

	/* The surface is illuminated by this light.  Loop through
	 *  the color samples and sum the diffuse contribution for
	 *  this light to the the color.
	 */
	for (ct=0; ct<num_samples; ct++) {
	  color[ct] += matl->Kd_scale * matl->Kd_spectral[ct]
	    * N_dot_L * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
	}
      }
      lgts++;
    }

    return color;
}

/* ****************************************************************
 * double *IM_phong (surface, V, matl, lgts, color)
 *  LINE        *surface    (in)  - surface for color computation
 *  LINE        *V          (in)  - view vector
 *  ILLUM_MATL  *matl       (in)  - material properties
 *  ILLUM_LGT   **lgts      (in)  - illuminating sources
 *  double      *color      (mod) - array to receive the color
 *
 * Evaluates the color using the Phong (1975) illumination
 *  model as described in Eq.(\*(rU).  Returns 'color' upon
 *  success and NULL upon failure.
 *
 * The actual Phong model results when the microfacet distribution
 *  D_phong() is used, and matl->Ks_spectral is the identity
 *  material.
 *
 * Using the microfacet distribution D_blinn() gives Blinn's
 *  interpretation of the Phong model per Eq.(\*(rI).
 */
double *IM_phong (surface, V, matl, lgts, color)
LINE        *surface, *V;
ILLUM_MATL  *matl;
ILLUM_LGT   **lgts;
double      *color;
{   int         ct;
    double      N_dot_L, D;
    LINE        L;

    /* load the ambient illumination */
    for (ct=0; ct<num_samples; ct++) {
      color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
	(*lgts)->I_scale * (*lgts)->I_spectral[ct];
    }
    lgts++;

    /* load the diffuse and specular illumination components.
     *  Loop through the lights and compute (N.L).  If it is
     *  positive then the surface is illuminated.
     */
    while (*lgts != NULL) {
      if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
	  && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)) {

	/* The surface is illuminated.  Compute the microfacet
	 *  distribution function.
	 */
	D = (*(matl->D))(&(surface->dir), &(L.dir), &(V->dir),
	  matl->D_coeff);

	/* Loop through the color samples and sum the diffuse
	 *  and specular contribution for this light to the the
	 *  color.
	 */
	for (ct=0; ct<num_samples; ct++) {
	  color[ct] +=
	    ((N_dot_L * matl->Kd_scale * matl->Kd_spectral[ct])
	    + (D * matl->Ks_scale * matl->Ks_spectral[ct]))
	    * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
	}
      }
      lgts++;
    }

    return color;
}

/* ****************************************************************
 * double *IM_blinn (surface, V, matl, lgts, color)
 *  LINE        *surface    (in)  - surface for color computation
 *  LINE        *V          (in)  - view vector
 *  ILLUM_MATL  *matl       (in)  - material properties
 *  ILLUM_LGT   **lgts      (in)  - illuminating sources
 *  double      *color      (mod) - array to receive the color
 *
 * Evaluates the color using the Blinn (1977) illumination
 *  model as described in Eq.(\*(rV).  Returns 'color' upon
 *  success and NULL upon failure.  The microfacet distribution
 *  functions D_blinn(), D_gaussian(), and D_reitz are the three
 *  functions presented by Blinn.  The geometric attenuation
 *  function G_torrance() is the attenuation function used by Blinn.
 *  If matl->G is NULL then the geometric attenuation is omitted.
 *  The Fresnel reflectance is approximated using the Cook (1983)
 *  technique.
 */
double *IM_blinn (surface, V, matl, lgts, color)
LINE        *surface, *V;
ILLUM_MATL  *matl;
ILLUM_LGT   **lgts;
double      *color;
{   int         ct;
    double      N_dot_L, N_dot_V, D, G, *Fr;
    LINE        L;
    DIR_VECT    *T, *H;

    /* load the ambient illumination */
    for (ct=0; ct<num_samples; ct++) {
      color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
	(*lgts)->I_scale * (*lgts)->I_spectral[ct];
    }
    lgts++;

    /* load the diffuse and specular illumination components.
     *  Loop through the lights and compute (N.L).  If it is
     *  positive then the surface is illuminated.
     */
    while (*lgts != NULL) {
      if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
	  && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)) {

	/* The surface is illuminated.  Compute the microfacet
	 *  distribution, geometric attenuation, Fresnel
	 *  reflectance and (N.V) for the specular function.
	 */
	D = (*(matl->D))(&(surface->dir), &(L.dir), &(V->dir),
	  matl->D_coeff);
	if (matl->G == NULL)
	  G = 1.0;
	else
	  G = (*(matl->G))(&(surface->dir), &(L.dir),
	    &(V->dir), matl->G_coeff);
	H = geo_H(&(L.dir), &(V->dir));
	if (matl->conductor) {
	  Fr = F_approx_Fr(H, &(L.dir), matl->Ro, matl->nt,
	   matl->k, num_samples, matl->Ks_spectral, Fr_buffer);
	}
	else {
	  T = geo_rfr(&(V->dir),H, matl->nr, matl->nt);
	  Fr = F_approx_Fr_Ft(H, &(L.dir), T,
	    matl->Ro, matl->nr, matl->nt, num_samples,
	    matl->Ks_spectral, Fr_buffer, Ft_buffer);
	}

	/* Loop through the color samples and sum the diffuse
	 *  and specular contribution for this light to the the
	 *  color. Note the threshold on N_dot_V to prevent
	 *  divide by zero at grazing.
	 */
	if ((N_dot_V=geo_dot(&(surface->dir),&(V->dir))) > 0.0001){
	    for (ct=0; ct<num_samples; ct++) {
	      color[ct] +=
		((N_dot_L * matl->Kd_scale * matl->Kd_spectral[ct])
		+ ((D * G * matl->Ks_scale * Fr[ct]) / N_dot_V))
		* (*lgts)->I_scale * (*lgts)->I_spectral[ct];
	    }
	}
      }
      lgts++;
    }

    return color;
}

/* ****************************************************************
 * double *IM_whitted (surface, V, matl, lgts, color)
 *  LINE        *surface    (in)  - surface for color computation
 *  LINE        *V          (in)  - view vector
 *  ILLUM_MATL  *matl       (in)  - material properties
 *  ILLUM_LGT   *lgts       (in)  - illuminating sources
 *  double      *color      (mod) - array to receive the color
 *
 * Evaluates the color using the Whitted (1980) illumination
 *  model as described in Eq.(\*(e6).  Returns 'color' upon
 *  success and NULL upon failure.
 *
 * The actual Whitted model results when the microfacet
 *  distribution D_blinn() is used, and when both matl->Ks_spectral
 *  and matl->Kt_spectral are the identity material.
 *
 * The matl->Kt_scale and matl->Kt_spectral are required for this
 *  illumination model only.
 */
double *IM_whitted (surface, V, matl, lgts, color)
LINE        *surface, *V;
ILLUM_MATL  *matl;
ILLUM_LGT   **lgts;
double      *color;
{   int         inside = FALSE;
    int         ct;
    double      N_dot_L, D;
    LINE        L;

    /* figure out whether we are on the reflected or transmitted
     *  side of the material interface (outside or inside).  If
     *  we are inside a material, then there is no illumination
     *  from lights and such - skip directly to reflection and
     *  refraction contribution.
     */
    if ((geo_dot(&(surface->dir),&(V->dir)) < 0) ||
	(!matl->r_is_air)) {
	for (ct=0; ct<num_samples; ct++) color[ct] = 0.0;
	inside = TRUE;
	goto rfl_rfr;
    }

    /* If we are at interface between materials, neither of which
     *  is air, then skip directly to reflection and refraction
     */
    if (!matl->r_is_air) goto rfl_rfr;

    /* load the ambient illumination */
    for (ct=0; ct<num_samples; ct++) {
      color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
	(*lgts)->I_scale * (*lgts)->I_spectral[ct];
    }
    lgts++;

    /* load the diffuse and specular illumination components.
     *  Loop through the lights and compute (N.L).  If it is
     *  positive then the surface is illuminated.
     */
    while (*lgts != NULL) {
      if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
	  && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)) {

	/* The surface is illuminated.  Compute the microfacet
	 *  distribution function.
	 */
	D = (*(matl->D))(&(surface->dir), &(L.dir), &(V->dir),
	  matl->D_coeff);

	/* Loop through the color samples and sum the diffuse
	 *  and specular contribution for this light to the the
	 *  color.
	 */
	for (ct=0; ct<num_samples; ct++) {
	  color[ct] +=
	    ((N_dot_L * matl->Kd_scale * matl->Kd_spectral[ct])
	    + (D * matl->Ks_scale * matl->Ks_spectral[ct]))
	    * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
	}
      }
      lgts++;
    }

    /* compute the contribution from the reflection and
     *  refraction directions.  Get a buffer to hold the
     *  computed colors, then process the reflected direction
     *  and the refracted direction.
     */
rfl_rfr:
    if (get_color != NULL) {
      char      *malloc();
      double    *Ir_or_It;
      LINE      V_new;
      DIR_VECT  *T;

      if ((Ir_or_It = (double *)malloc((unsigned)(num_samples *
	sizeof(double)))) == NULL) return color;

      /* get the reflected vector then ask for the color from
       *  the reflected direction.  If there is a color, then
       *  sum it with the current color
       */
      V_new.start = surface->start;
      V_new.dir = *(geo_rfl(&(V->dir),&(surface->dir)));
      if ((*get_color)(&V_new, Ir_or_It) != NULL)
	for (ct=0; ct<num_samples; ct++) color[ct] +=
	  Ir_or_It[ct] * matl->Ks_scale * matl->Ks_spectral[ct];

      /* if the material is transparent then get the refracted
       *  vector and ask for the color from the refracted
       *  direction.  If there is a color, then sum it with
       *  the current color
       */
      if (matl->transparent) {
	V_new.start = surface->start;
	if (inside)
	  T = geo_rfr(&(V->dir),&(surface->dir),
	    matl->nt, matl->nr);
	else
	  T = geo_rfr(&(V->dir),&(surface->dir),
	    matl->nr, matl->nt);
	if (T != NULL) {
	  V_new.dir = *T;
	  if ((*get_color)(&V_new, Ir_or_It) != NULL)
	    for (ct=0; ct<num_samples; ct++) color[ct] +=
	      Ir_or_It[ct] * matl->Kt_scale *
	      matl->Kt_spectral[ct];
	}
      }
      (void)free((char *)Ir_or_It);
    }

    return color;
}

/* ****************************************************************
 * double *IM_hall (surface, V, matl, lgts, color)
 *  LINE        *surface    (in)  - surface for color computation
 *  LINE        *V          (in)  - view vector
 *  ILLUM_MATL  *matl       (in)  - material properties
 *  ILLUM_LGT   *lgts       (in)  - illuminating sources
 *  double      *color      (mod) - array to receive the color
 *
 * Evaluates the color using the Hall (1983) illumination
 *  model as described in Eq.(\*(sF).  Returns 'color' upon
 *  success and NULL upon failure.
 *
 * The actual Hall model results when the microfacet
 *  distribution D_blinn() is used, and matl->D = NULL.
 *
 * The transmittance is computed from the reflectance, so
 *  matl->Kt_scale and matl->Kt_spectral are not used in the model.
 */
double *IM_hall (surface, V, matl, lgts, color)
LINE        *surface, *V;
ILLUM_MATL  *matl;
ILLUM_LGT   **lgts;
double      *color;
{   int         inside = FALSE;
    int         ct;
    double      N_dot_L, D, G, *Fr;
    LINE        L;
    DIR_VECT    *T, *H, *Ht, pseudo_V;

    /* figure out whether we are on the reflected or transmitted
     *  side of the material interface (outside or inside).  If
     *  we are inside a material, then there may be illumination
     *  from outside.
     */
    if (geo_dot(&(surface->dir),&(V->dir)) < 0) {
	for (ct=0; ct<num_samples; ct++) color[ct] = 0.0;
	inside = TRUE;
    }

    /* If we are at interface between materials, neither of which
     *  is air, then skip directly to reflection and refraction
     */
    if (!matl->r_is_air) goto rfl_rfr;

    /* load the ambient illumination if we are not inside
     *  inside the material
     */
    if (!inside) {
      for (ct=0; ct<num_samples; ct++) {
	color[ct] = matl->Ka_scale * matl->Ka_spectral[ct] *
	  (*lgts)->I_scale * (*lgts)->I_spectral[ct];
      }
    }
    lgts++;

    /* load the diffuse and specular illumination components.
     *  Loop through the lights and compute (N.L).  If it is
     *  positive then the surface is illuminated.  If it is
     *  negative, then there may be transmitted illumination.
     */
    while (*lgts != NULL) {
      if ((geo_line(&(surface->start),&((*lgts)->center),&L) > 0)
	  && ((N_dot_L=geo_dot(&(surface->dir),&(L.dir))) > 0)
	  && !inside) {

	/* The surface is illuminated.  Compute the microfacet
	 *  distribution, geometric attenuation, Fresnel
	 *  reflectance and (N.V) for the specular function.
	 */
	D = (*(matl->D))(&(surface->dir), &(L.dir), &(V->dir),
	  matl->D_coeff);
	if (matl->G == NULL)
	  G = 1.0;
	else
	  G = (*(matl->G))(&(surface->dir), &(L.dir),
	    &(V->dir), matl->G_coeff);
	H = geo_H(&(L.dir), &(V->dir));
	if (matl->conductor) {
	  Fr = F_approx_Fr(H, &(L.dir), matl->Ro, matl->nt,
	    matl->k, num_samples, matl->Ks_spectral, Fr_buffer);
	}
	else {
	  T = geo_rfr(&(L.dir), H, matl->nr, matl->nt);
	  Fr = F_approx_Fr_Ft(H, &(L.dir), T,
	    matl->Ro, matl->nr, matl->nt, num_samples,
	    matl->Ks_spectral, Fr_buffer, Ft_buffer);
	}


	/* Loop through the color samples and sum the diffuse
	 *  and specular contribution for this light to the the
	 *  color.
	 */
	for (ct=0; ct<num_samples; ct++) {
	  color[ct] +=
	    ((N_dot_L * matl->Kd_scale * matl->Kd_spectral[ct])
	    + (D * G * matl->Ks_scale * Fr[ct]))
	    * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
	}
      }
      else if ((N_dot_L > 0) && inside) {
	/* We are inside and the light is outside.  Compute
	 *  the transmitted contribution from the light
	 */
	if ((Ht = geo_Ht(&(L.dir),&(V->dir),matl->nr,
	    matl->nt)) != NULL) {
	  /* The microfacet distribution functions could
	   *  only be equated when cast in terms of the primary
	   *  vectors L, V, and N.  A pseudo_V vector is required
	   *  so that any of the distribution functions can be
	   *  applied.  Ht is the vector bisector between of the
	   *  angle between L and pseudo_V, thus pseudo_V can be
	   *  computed by reflecting L about Ht. Refer to the
	   *  text for details.
	   */
	  pseudo_V = *(geo_rfl(&(L.dir), Ht));
	  D = (*(matl->D))(&(surface->dir), &(L.dir),
	    &pseudo_V, matl->D_coeff);
	  Fr = F_approx_Fr_Ft(Ht, &(L.dir), &(V->dir),
	    matl->Ro, matl->nr, matl->nt, num_samples,
	    matl->Ks_spectral, Fr_buffer, Ft_buffer);
	  if (matl->G == NULL)
	    G = 1.0;
	  else {
	    /* To include the geometric attenuation, the view
	     *  vector direction must be reversed so that it
	     *  is to the same side of the surface as the
	     *  normal, see text for details.
	     */
	    pseudo_V.i = -V->dir.i;
	    pseudo_V.j = -V->dir.j;
	    pseudo_V.k = -V->dir.k;
	    G = (*(matl->G))(&(surface->dir), &(L.dir),
	      &pseudo_V, matl->G_coeff);
	  }
	  for (ct=0; ct<num_samples; ct++) {
	    color[ct] += (D * G * matl->Ks_scale * Ft_buffer[ct])
	      * (*lgts)->I_scale * (*lgts)->I_spectral[ct];
	  }
	}
      }
      lgts++;
    }

    /* compute the contribution from the reflection and
     *  refraction directions.  Get a buffer to hold the
     *  computed colors, then process the reflected direction
     *  and the refracted direction.
     */
rfl_rfr:
    if (get_color != NULL) {
      char      *malloc();
      double    *Ir_or_It;
      LINE      V_new;
      DIR_VECT  pseudo_N;

      if ((Ir_or_It = (double *)malloc((unsigned)(num_samples *
	sizeof(double)))) == NULL) return color;

      /* Determine the Fresnel reflectance and transmittance.
       *  If we are inside the material, then a pseudo normal
       *  is required that faces to the same side of the
       *  interface as the view vector.
       */
      if (matl->conductor) {
	Fr = F_approx_Fr(&(surface->dir), &(V->dir), matl->Ro,
	  matl->nt, matl->k, num_samples, matl->Ks_spectral,
	  Fr_buffer);
      }
      else if (inside) {
	T = geo_rfr(&(V->dir),&(surface->dir),
	    matl->nt, matl->nr);
	pseudo_N.i = -surface->dir.i;
	pseudo_N.j = -surface->dir.j;
	pseudo_N.k = -surface->dir.k;
	Fr = F_approx_Fr_Ft(&pseudo_N, &(V->dir), T,
	    matl->Ro, matl->nt, matl->nr, num_samples,
	    matl->Ks_spectral, Fr_buffer, Ft_buffer);
      }
      else {
	T = geo_rfr(&(V->dir),&(surface->dir),
	    matl->nr, matl->nt);
	Fr = F_approx_Fr_Ft(&(surface->dir), &(V->dir),
	    T, matl->Ro, matl->nr, matl->nt, num_samples,
	    matl->Ks_spectral, Fr_buffer, Ft_buffer);
      }

      /* get the reflected vector then ask for the color from
       *  the reflected direction.  If there is a color, then
       *  sum it with the current color
       */
      V_new.start = surface->start;
      V_new.dir = *(geo_rfl(&(V->dir),&(surface->dir)));
      if ((*get_color)(&V_new, Ir_or_It) != NULL) {
	for (ct=0; ct<num_samples; ct++) color[ct] +=
	  Ir_or_It[ct] * matl->Ks_scale * Fr[ct];
      }

      /* if the material is transparent then get the refracted
       *  vector and ask for the color from the refracted
       *  direction.  If there is a color, then sum it with
       *  the current color
       */
      if (matl->transparent && (T != NULL)) {
	V_new.start = surface->start;
	V_new.dir = *T;
	if ((*get_color)(&V_new, Ir_or_It) != NULL)
	  for (ct=0; ct<num_samples; ct++) color[ct] +=
	    Ir_or_It[ct] * matl->Kt_scale * Ft_buffer[ct];
      }
      (void)free((char *)Ir_or_It);
    }

    /* If we are inside a material that has a filter attenuation
     *  then apply the attenuation to the color.
     */
    if ( ((!inside) && ((Fr = matl->Ar_spectral) != NULL)) ||
	 ((inside) && ((Fr = matl->At_spectral) != NULL)) ) {
	double      dist;
	dist = sqrt ( ((surface->start.x - V->start.x) *
		       (surface->start.x - V->start.x)) +
		      ((surface->start.y - V->start.y) *
		       (surface->start.y - V->start.y)) +
		      ((surface->start.z - V->start.z) *
		       (surface->start.z - V->start.z)) );
	for (ct=0; ct<num_samples; ct++)
	    color[ct] *= pow (Fr[ct], dist);
    }

    return color;
}

/* ****************************************************************
 * int IM_exit ()
 *
 * Finishes use of the illumination models routines.
 */
IM_exit()
{
    if (Fr_buffer != NULL) {
	(void)free((char *)Fr_buffer); Fr_buffer = NULL;
    }
    if (Ft_buffer != NULL) {
	(void)free((char *)Ft_buffer); Ft_buffer = NULL;
    }
    num_samples = 0;
    get_color = NULL;
    return TRUE;
}
/* ************************************************************* */
t] *= pow (Fr[ct], dist);
Spectral/D5500.ls
#   Light Source:   standard CIE D5500 illuminant
#   Source:
#       Judd and Wyszecki
#       Color in Business, Science, and Industry
#       table 2.4 pg.116
#   Notes:
#       normalized to 1.00 at 560nm
#
300 0.0002
310 0.0210
320 0.1120
330 0.2060
340 0.2390
350 0.2780
360 0.3060
370 0.3430
380 0.3260
390 0.3810
400 0.6100
410 0.6860
420 0.7160
430 0.6790
440 0.8560
450 0.9800
460 1.0050
470 0.9990
480 1.0270
490 0.9810
500 1.0070
510 1.0070
520 1.0000
530 1.0420
540 1.0210
550 1.0300
560 1.0000
570 0.9720
580 0.9770
590 0.9140
600 0.9440
610 0.9510
620 0.9420
630 0.9040
640 0.9230
650 0.8890
660 0.9030
670 0.9390
680 0.9000
690 0.7970
700 0.8280
710 0.8480
720 0.7020
730 0.7930
740 0.8500
750 0.7190
760 0.5280
770 0.7590
780 0.7180
790 0.7290
800 0.6730
810 0.5870
820 0.6500
830 0.6830
.3430
380 0.3260
390 0.3810
400 0.6100
410 0.6860
420 0.7160
430 0.6790
440 0.8560
450 0.9800
460 1.0050
470 0.9990
480 1.0270
490 0.9810
500 1.0070
510 1.0070
520 1.0000
530 1.0420
540 1.0210
550 1.0300
560 1.0000
57Spectral/D6500.ls
#   Light Source:   standard CIE illuminant D6500
#   Source:
#       Judd and Wyszecki
#       Color in Business, Science, and Industry
#       table 2.1 pg.108-109
#   Notes:
#       normalized to 1.00 at 560nm
#
300 0.0003
310 0.0330
320 0.2020
330 0.3710
340 0.3990
350 0.4490
360 0.4660
370 0.5210
380 0.5000
390 0.5460
400 0.8280
410 0.9150
420 0.9340
430 0.8670
440 1.0490
450 1.1700
460 1.1780
470 1.1490
480 1.1590
490 1.0880
500 1.0940
510 1.0780
520 1.0480
530 1.0770
540 1.0440
550 1.0400
560 1.0000
570 0.9630
580 0.9580
590 0.8870
600 0.9000
610 0.8960
620 0.8770
630 0.8330
640 0.8370
650 0.8000
660 0.8020
670 0.8230
680 0.7830
690 0.6970
700 0.7160
710 0.7430
720 0.6160
730 0.6990
740 0.7510
750 0.6360
760 0.4640
770 0.6680
780 0.6340
790 0.6430
800 0.5950
810 0.5200
820 0.5740
830 0.6030
.5210
380 0.5000
390 0.5460
400 0.8280
410 0.9150
420 0.9340
430 0.8670
440 1.0490
450 1.1700
460 1.1780
470 1.1490
480 1.1590
490 1.0880
500 1.0940
510 1.0780
520 1.0480
530 1.0770
540 1.0440
550 1.0400
560 1.000Spectral/D7500.ls
#   Light Source:   standard CIE D7500 illuminant
#   Source:
#       Judd and Wyszecki
#       Color in Business, Science, and Industry
#       table 2.4 pg.116
#   Notes:
#       normalized to 1.00 at 560nm
#
300 0.0004
310 0.0510
320 0.2980
330 0.5490
340 0.5730
350 0.6270
360 0.6300
370 0.7030
380 0.6670
390 0.7000
400 1.0190
410 1.1190
420 1.1280
430 1.0310
440 1.2120
450 1.3300
460 1.3240
470 1.2730
480 1.2680
490 1.1780
500 1.1660
510 1.1370
520 1.0870
530 1.1040
540 1.0630
550 1.0490
560 1.0000
570 0.9560
580 0.9420
590 0.8700
600 0.8720
610 0.8610
620 0.8360
630 0.7870
640 0.7840
650 0.7480
660 0.7430
670 0.7540
680 0.7160
690 0.6390
700 0.6510
710 0.6810
720 0.5640
730 0.6420
740 0.6920
750 0.5860
760 0.4260
770 0.6140
780 0.5830
790 0.5910
800 0.5470
810 0.4790
820 0.5290
830 0.5550
.7030
380 0.6670
390 0.7000
400 1.0190
410 1.1190
420 1.1280
430 1.0310
440 1.2120
450 1.3300
460 1.3240
470 1.2730
480 1.2680
490 1.1780
500 1.1660
510 1.1370
520 1.0870
530 1.1040
540 1.0630
550 1.0490
560 1.0000
57Spectral/calcite_E.p
#   Material:       Calcite - CaCO3 - ray E
#   Type            486/791
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.48641
434 0.039270
482 0.038822
589 0.038270
656 0.038042
     Calcite - CaCO3 - ray E
#   Type            486/791
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.48641
434 0.039270
482 0.038822
589 0.038Spectral/calcite_O.p
#   Material:       Calcite - CaCO3 - ray O
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.65836
434 0.063747
482 0.062666
589 0.061334
656 0.060776
38Spectral/chromium.pl
#   Material:       Chromium
#   Source:
#     reflectance:  Thermophysical Properties of High Temperature Solid
#                       Materials (1967) v.1, p474, curve #2
#     n,k:
#   Conditions:     electroplated, base material not specified
#   Temperature:    298 K
#   Incident angle: 6-9 deg
#   Solid angle:    2pi
#
conductor
300 0.485
400 0.570
500 0.585
600 0.570
700 0.565
800 0.575
900 0.585
1000 0.575
1100 0.580
1200 0.600
1300 0.635
1400 0.675
63747
482 0.062666
589 0.061334
656 0.060776
38Spectral/copper.ar
#   Material:       Copper
#   Source:
#     reflectance:  Thermophysical Properties of Matter (1970)
#                       v.7, p.162, curve #4 (from Cook's thesis)
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 0.617
k 2.630
380 0.070
420 0.095
460 0.140
500 0.205
540 0.265
560 0.335
580 0.490
600 0.615
620 0.670
660 0.725
700 0.755
perties of Matter (1970)
#                       v.7, p.162, curve #4 (from Cook's thesis)
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 0.617
k 2.630
380 0.070
420 0.095
460 0.140
5Spectral/copper.ep
#   Material:       Copper
#   Source:
#     reflectance:  Thermophysical Properties of High Temperature Solid
#                       Materials (1967) v.1, p474, curve #7
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     electropolished
#   Temperature:    298 K
#   Incident angle: normal
#   Solid angle:    2pi
#
conductor
n 0.617
k 2.630
400 0.525
500 0.590
540 0.640
700 0.970
800 0.970
900 0.967
1000 0.973
tance:  Thermophysical Properties of High Temperature Solid
#                       Materials (1967) v.1, p474, curve #7
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     electropolished
#   Temperature:    298 K
#   Incident angle: normal
#   Solid angle:    2pi
#
conductor
n 0.617
k 2.630
400 0.525
500 0.59Spectral/copper.ox
#   Material:       Copper
#   Source:
#     reflectance:  Thermophysical Properties of High Temperature Solid
#                       Materials (1967) v.1, p474, curve #3
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     fed spec QQ-C-576 or QQ-C-502; polished with
#                       fine compounds on a buffing wheel,  oxidized in
#                       air at red heat for 30min
#   Temperature:    298 K
#   Incident angle: 6-9 deg
#   Solid angle:    2pi
#
conductor
n 0.617
k 2.630
300 0.100
340 0.120
420 0.140
500 0.130
640 0.115
700 0.118
760 0.130
800 0.160
820 0.190
860 0.310
885 0.370
900 0.410
960 0.455
1020 0.490
1210 0.540
iginal source R.S.Minor)
#                       measured at 589nm
#   Conditions:     fed spec QQ-C-576 or QQ-C-502; polished with
#                       fine compounds on a buffing wheel,  oxidized in
#                     Spectral/copper.p1
#   Material:       Copper
#   Source:
#     reflectance:  Thermophysical Properties of High Temperature Solid
#                       Materials (1967) v.1, p474, curve #2
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     fed spec QQ-C-576 or QQ-C-502; polished with
#                       fine compounds on a buffing wheel
#   Temperature:    298 K
#   Incident angle: 6-9 deg
#   Solid angle:    2pi
#
conductor
n 0.617
k 2.630
300 0.060
340 0.050
380 0.060
420 0.100
460 0.140
500 0.200
520 0.250
540 0.300
560 0.400
580 0.450
595 0.500
610 0.600
640 0.650
690 0.700
760 0.740
860 0.770
950 0.820
1010 0.840
1100 0.835
     table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     fed spec QQ-C-576 or QQ-C-502; polished with
#                       fine compounds on a buffing wheel
#   Temperature:    298 K
#   IncideSpectral/copper.p2
#   Material:       Copper
#   Source:
#     reflectance:  Thermophysical Properties of High Temperature Solid
#                       Materials (1967) v.1, p474, curve #8
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     roughened to 1.25 micro-inch
#   Temperature:    298 K
#   Incident angle: normal
#   Solid angle:    2pi
#
conductor
n 0.617
k 2.630
410 0.420
510 0.440
550 0.480
800 0.800
900 0.815
1000 0.825
1200 0.830
physical Properties of High Temperature Solid
#                       Materials (1967) v.1, p474, curve #8
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 25A, p538 (original source R.S.Minor)
#                       measured at 589nm
#   Conditions:     roughened to 1.25 micro-inch
#   Temperature:    298 K
#   Incident angle: normal
#   Solid angle:    2pi
#
conductor
n 0.617
k 2.630
410 0Spectral/diamond.p
#   Material:       Diamond
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23A Reffractive Index for Several Solids.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 2.4172
410 0.177773
470 0.175782
550 0.173246
580 0.171997
610 0.171685
660 0.170974
e:    2pi
#
conductor
n 0.617
k 2.630
410 0Spectral/glass_BF.p
#   Material:       Glass - Barium Flint
#   Type            591/605
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23B - Refractive Indices and Dispersions
#                       for Several Common Types of Optical Glass
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.59144
399 0.054445
434 0.053756
482 0.053016
509 0.052769
533 0.052522
589 0.052088
644 0.051751
656 0.051686
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23B - Refractive Indices and Dispersions
#                       for Several Common Types of Optical Glass
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.59144
399 0.054445
434 0.05Spectral/glass_BSC1.p
#   Material:       Glass - Borosilicate Crown - 1
#   Type            500/664
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.50000
434 0.041204
482 0.040679
589 0.040000
656 0.039714
Glass - Borosilicate Crown - 1
#   Type            500/664
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.50000
434 0.041204
482 0.040679
589 Spectral/glass_BSC2.p
#   Material:       Glass - Borosilicate Crown - 2
#   Type            517/645
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.51462
434 0.043503
482 0.042924
589 0.042191
656 0.041882
Glass - Borosilicate Crown - 2
#   Type            517/645
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.51462
434 0.043503
482 0.042924
589 Spectral/glass_BSC3.p
#   Material:       Glass - Borosilicate Crown - 3
#   Type            511/634
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23B - Refractive Indices and
#                       Dispersions for Several Common Types of
#                       Optical Glass
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.51124
399 0.043291
434 0.042757
482 0.042178
509 0.041975
533 0.041784
589 0.041445
644 0.041178
656 0.041134
uted from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23B - Refractive Indices and
#                       Dispersions for Several Common Types of
#                       Optical Glass
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2Spectral/glass_CG.p
#   Material:       Creown Glass
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23A Reffractive Index for Several Solids.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 1.5225
410 0.044935
470 0.044015
550 0.043362
580 0.042905
610 0.042788
660 0.042580
ctric
#   index of refraction at 589.2Spectral/glass_DF2.p
#   Material:       Glass - Dense Flint - 2
#   Type            617/366
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.61700
434 0.058662
482 0.057244
589 0.055586
656 0.054920
       Glass - Dense Flint - 2
#   Type            617/366
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.61700
434 0.058662
482 0.057244
589 0.05558Spectral/glass_DF5.p
#   Material:       Dense Flint
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23A Reffractive Index for Several Solids.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 1.6670
410 0.066931
470 0.064889
550 0.063505
580 0.062547
610 0.062266
660 0.061844
0
434 0.058662
482 0.057244
589 0.05558Spectral/glass_EDF1.p
#   Material:       Glass - Extra Dense Flint - 1
#   Type:           649/338
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.64900
434 0.063612
482 0.061943
589 0.060024
656 0.059267
 Glass - Extra Dense Flint - 1
#   Type:           649/338
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.64900
434 0.063612
482 0.061943
589 0Spectral/glass_EDF3.p
#   Material:       Glass - Extra Dense Flint - 3
#   Type:           720/291
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.72000
434 0.074848
482 0.072623
589 0.070069
656 0.069073
 Glass - Extra Dense Flint - 3
#   Type:           720/291
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.72000
434 0.074848
482 0.072623
589 0Spectral/glass_FQ.p
#   Material:       Glass - Fused Quartz
#   Type:           458/676
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23B - Refractive Indices and Dispersions
#                       for Several Common Types of Optical Glass
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.45845
399 0.036245
434 0.035822
482 0.035360
509 0.035202
533 0.035049
589 0.034775
644 0.034564
656 0.034522
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23B - Refractive Indices and Dispersions
#                       for Several Common Types of Optical Glass
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.45845
399 0.036245
434 0.03Spectral/glass_LBC1.p
#   Material:       Glass - Light Barium Crown - 1
#   Type:           541/599
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.54100
434 0.046851
482 0.046169
589 0.045330
656 0.044972
Glass - Light Barium Crown - 1
#   Type:           541/599
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.54100
434 0.046851
482 0.046169
589 Spectral/glass_LF1.p
#   Material:       Glass - Light Flint - 1
#   Type:           576/412
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.57600
434 0.052492
482 0.051358
589 0.049998
656 0.049470
       Glass - Light Flint - 1
#   Type:           576/412
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.57600
434 0.052492
482 0.051358
589 0.04999Spectral/glass_LF2.p
#   Material:       Glass - Light Flint - 2
#   Type:           575/411
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.57500
434 0.052436
482 0.051214
589 0.049863
656 0.049325
       Glass - Light Flint - 2
#   Type:           575/411
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.57500
434 0.052436
482 0.051214
589 0.04986Spectral/glass_LF3.p
#   Material:       Light Flint
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23A Reffractive Index for Several Solids.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 1.5875
410 0.053801
470 0.052709
550 0.052028
580 0.051553
610 0.051445
660 0.051214
0
434 0.052436
482 0.051214
589 0.04986Spectral/glass_SC.p
#   Material:       Glass - Spectacle Crown
#   Type:           523/587
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.52300
434 0.044455
482 0.043797
589 0.042970
656 0.042635
       Glass - Spectacle Crown
#   Type:           523/587
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.52300
434 0.044455
482 0.043797
589 0.04297Spectral/glass_SrTiO3.p
#   Material:       Glass - Strontium titanate - SrTiO3
#   Type:           412/345
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 2.41208
410 0.201769
434 0.193917
470 0.185155
482 0.182612
550 0.174664
580 0.171969
589 0.171270
610 0.169222
656 0.165676
660 0.165838
eflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 2.41208
410 0.201769
434 0.193917Spectral/glass_TC.p
#   Material:       Glass - telescopic Crown
#   Type:           531/516
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23B - Refractive Indices and Dispersions
#                       for Several Common Types of Optical Glass
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.52704
399 0.045521
434 0.044922
482 0.044282
509 0.044076
533 0.043870
589 0.043497
644 0.043218
656 0.043154
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23B - Refractive Indices and Dispersions
#                       for Several Common Types of Optical Glass
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.52704
399 0.045521
434 Spectral/glass_TF.p
#   Material:       Glass - telescopic flint
#   Type:           531/516
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.53050
434 0.045698
482 0.044922
589 0.043950
656 0.043573
      Glass - telescopic flint
#   Type:           531/516
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.53050
434 0.045698
482 0.044922
589 0.0439Spectral/glass_VDF.p
#   Material:       Glass - Very Dense Flint
#   Type:           890/223
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.89000
434 0.104298
482 0.099120
589 0.094838
656 0.093217
      Glass - Very Dense Flint
#   Type:           890/223
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix III - Refractive Indicies and Dispersions
#                       for optical glass.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.89000
434 0.104298
482 0.099120
589 0.0948Spectral/gold.p
#   Material:       Gold
#   Source:
#     reflectance:  Thermophysical Properties of Matter (1970)
#                       v.7, p.1015, anaylzed curve (from Cook's thesis)
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     polished
#   Temperature:    298 K
#   Incident angle: 0
#   Solid angle:    2pi
#
conductor
n 0.366
k 2.85
350 0.37
400 0.38
450 0.37
500 0.40
550 0.80
600 0.86
650 0.88
700 0.91
800 0.91
      Gold
#   Source:
#     reflectance:  Thermophysical Properties of Matter (1970)
#                       v.7, p.1015, anaylzed curve (from Cook's thesis)
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     polished
#   Temperature:    298 K
#   Incident angle: 0
#   Solid angle:    2pi
#
conductor
n 0.366
k 2.85
350 0.37
400 0.38
450 0.37
500 0.40
550 0.80
600 0.86
650 0.88
70Spectral/ice.p
#   Material:       Ice
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23A Reffractive Index for Several Solids.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 1.3087
410 0.018718
470 0.018373
550 0.018110
580 0.017879
610 0.017809
660 0.017609
450 0.37
500 0.40
550 0.80
600 0.86
650 0.88
70Spectral/identity
#   Material:       Identity material -- perfect reflector
#   Source:         Hypothetical reference
#
conductor
0    1.0
2000 1.0
of Optics, Jenkins and White (1976)
#                       Table 23A Reffractive Index for Several Solids.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 1.3087
410 0.018718
470 0.018373
550 0.018110
580 0.017879
610 0.017809
660 0.017609
450 0.37
500 0.40
550 0.80
600 0.86
650 0.88
70Spectral/illum_A.ls
#   Light Source:   standard CIE illuminant A
#   Source:
#       Judd and Wyszecki
#       Color in Business, Science, and Industry
#       table 2.1 pg.108-109
#   Notes:
#       normalized to 1.00 at 560nm
#
300 0.0093
310 0.0136
320 0.0193
330 0.0266
340 0.0359
350 0.0474
360 0.0614
370 0.0782
380 0.0980
390 0.1209
400 0.1471
410 0.1768
420 0.2099
430 0.2467
440 0.2870
450 0.3309
460 0.3781
470 0.4287
480 0.4824
490 0.5391
500 0.5986
510 0.6606
520 0.7250
530 0.7913
540 0.8595
550 0.9291
560 1.0000
570 1.0718
580 1.1444
590 1.2173
600 1.2904
610 1.3635
620 1.4362
630 1.5084
640 1.5798
650 1.6503
660 1.7196
670 1.7877
680 1.8543
690 1.9193
700 1.9826
710 2.0441
720 2.1036
730 2.1612
740 2.2167
750 2.2700
760 2.3212
770 2.3701
780 2.4168
.0193
330 0.0266
340 0.0359
350 0.0474
360 0.0614
370 0.0782
380 0.0980
390 0.1209
400 0.1471
410 0.1768
420 0.2099
430 0.2467
440 0.2870
450 0.3309
460 0.3781
470 0.4287
480 0.4824
490 0.5391
500 0.5986
510 0.6606
520 0.7250
530 0.7913
540 0.8595
550 0.9291
560 1.0000
57Spectral/illum_B.ls
#   Light Source:   standard CIE illuminant B
#   Source:
#       Judd and Wyszecki
#       Color in Business, Science, and Industry
#       table 2.1 pg.108-109
#   Notes:
#       normalized to 1.00 at 560nm
#
320 0.0002
330 0.0050
340 0.0240
350 0.0560
360 0.0960
370 0.1520
380 0.2240
390 0.3130
400 0.4130
410 0.5210
420 0.6320
430 0.7310
440 0.8080
450 0.8540
460 0.8830
470 0.9200
480 0.9520
490 0.9650
500 0.9420
510 0.9070
520 0.8950
530 0.9220
540 0.9690
550 1.0100
560 1.0280
570 1.0260
580 1.0100
590 0.9920
600 0.9800
610 0.9850
620 0.9970
630 1.0100
640 1.0220
650 1.0390
660 1.0500
670 1.0490
680 1.0390
690 1.0160
700 0.9910
710 0.9620
720 0.9290
730 0.8940
740 0.8690
750 0.8520
760 0.8470
770 0.8540
0nm
#
320 0.0002
330 0.0050
340 0.0240
350 0.0560
360 0.0960
370 0.1520
380 0.2240
390 0.3130
400 0.4130
410 0.5210
420 0.6320
430 0.7310
440 0.8080
450 0.8540
460 0.8830
470 0.9200
480 0.9520
490 0.9650
500 0.9420
510 0.9070
520 0.8950
530 0.9220
540 0.9690
550 1.0100
560 1.0280
570 1.0260
580 1.0100
59Spectral/illum_C.ls
#   Light Source:   standard CIE illuminant C
#   Source:
#       Judd and Wyszecki
#       Color in Business, Science, and Industry
#       table 2.1 pg.108-109
#   Notes:
#       normalized to 1.00 at 560nm
#
300 0.0000
310 0.0000
320 0.0002
330 0.0040
340 0.0270
350 0.0700
360 0.1290
370 0.2140
380 0.3300
390 0.4740
400 0.6330
410 0.8060
420 1.0580
430 1.1240
440 1.2150
450 1.2400
460 1.2310
470 1.2380
480 1.2390
490 1.2070
500 1.1210
510 1.0230
520 0.9690
530 0.9800
540 1.0210
550 1.0520
560 1.0530
570 1.0230
580 0.9780
590 0.9320
600 0.8970
610 0.8840
620 0.8810
630 0.8800
640 0.8780
650 0.8820
660 0.8790
670 0.8630
680 0.8400
690 0.8020
700 0.7630
710 0.7240
720 0.6830
730 0.6440
740 0.6150
750 0.5920
760 0.5810
770 0.5820
.0000
320 0.0002
330 0.0040
340 0.0270
350 0.0700
360 0.1290
370 0.2140
380 0.3300
390 0.4740
400 0.6330
410 0.8060
420 1.0580
430 1.1240
440 1.2150
450 1.2400
460 1.2310
470 1.2380
480 1.2390
490 1.2070
500 1.1210
510 1.0230
520 0.9690
530 0.9800
540 1.0210
550 1.0520
560 1.0530
57Spectral/macbeth.1
#   Material:       Macbeth ColorCheckser - path 1 - dark skin
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0573
385 0.0625
390 0.0671
395 0.0707
400 0.0730
405 0.0739
410 0.0736
415 0.0723
420 0.0705
425 0.0682
430 0.0659
435 0.0636
440 0.0615
445 0.0597
450 0.0582
455 0.0573
460 0.0569
465 0.0569
470 0.0574
475 0.0582
480 0.0593
485 0.0607
490 0.0623
495 0.0641
500 0.0661
505 0.0681
510 0.0702
515 0.0724
520 0.0747
525 0.0769
530 0.0791
535 0.0811
540 0.0830
545 0.0844
550 0.0861
555 0.0888
560 0.0929
565 0.0979
570 0.1035
575 0.1094
580 0.1154
585 0.1214
590 0.1274
595 0.1334
600 0.1393
605 0.1451
610 0.1507
615 0.1561
620 0.1615
625 0.1669
630 0.1725
635 0.1783
640 0.1845
645 0.1913
650 0.1988
655 0.2070
660 0.2158
665 0.2251
670 0.2348
675 0.2446
680 0.2546
685 0.2646
690 0.2746
695 0.2847
700 0.2947
.0574
475 0.0582
480 0.0593
485 0.0607
490 0.0623
495 0.0641
500 0.0661
505 0.0681
510 0.0702
515 0.0724
520 0.0747
525 0.0769
530 0.0791
535 0.0811
540 0.0830
545 0.0844
550 0.0861
555 0.0888
560 0.0929
565 0.0979
570 0.1035
575 0.1094
580 0Spectral/macbeth.10
#   Material:       Macbeth ColorCheckser - patch 10 - deep purple
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1070
385 0.1281
390 0.1506
395 0.1749
400 0.1990
405 0.2181
410 0.2301
415 0.2344
420 0.2325
425 0.2263
430 0.2171
435 0.2057
440 0.1926
445 0.1785
450 0.1639
455 0.1497
460 0.1366
465 0.1248
470 0.1142
475 0.1049
480 0.0968
485 0.0897
490 0.0836
495 0.0784
500 0.0738
505 0.0699
510 0.0666
515 0.0638
520 0.0615
525 0.0592
530 0.0580
535 0.0568
540 0.0558
545 0.0550
550 0.0544
555 0.0539
560 0.0535
565 0.0533
570 0.0532
575 0.0535
580 0.0542
585 0.0554
590 0.0572
595 0.0601
600 0.0645
605 0.0706
610 0.0788
615 0.0899
620 0.1004
625 0.1123
630 0.1248
635 0.1378
640 0.1513
645 0.1654
650 0.1798
655 0.1945
660 0.2090
665 0.2233
670 0.2378
675 0.2530
680 0.2697
685 0.2890
690 0.3109
695 0.3343
700 0.3581
705 0.3821
710 0.4061
.0968
485 0.0897
490 0.0836
495 0.0784
500 0.0738
505 0.0699
510 0.0666
515 0.0638
520 0.0615
525 0.0592
530 0.0580
535 0.0568
540 0.0558
545 0.0550
550 0.0544
555 0.0539
560 0.0535
565 0.0533
570 0.0532
575 0.0535
5Spectral/macbeth.11
#   Material:       Macbeth ColorCheckser - patch 11 - strong yellow green
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0523
385 0.0538
390 0.0552
395 0.0565
400 0.0577
405 0.0588
410 0.0597
415 0.0604
420 0.0610
425 0.0615
430 0.0622
435 0.0633
440 0.0650
445 0.0678
450 0.0716
455 0.0768
460 0.0836
465 0.0926
470 0.1039
475 0.1176
480 0.1341
485 0.1545
490 0.1806
495 0.2144
500 0.2552
505 0.2997
510 0.3487
515 0.4004
520 0.4475
525 0.4820
530 0.5114
535 0.5311
540 0.5405
545 0.5447
550 0.5446
555 0.5401
560 0.5316
565 0.5204
570 0.5079
575 0.4943
580 0.4793
585 0.4628
590 0.4449
595 0.4260
600 0.4065
605 0.3880
610 0.3718
615 0.3586
620 0.3487
625 0.3419
630 0.3373
635 0.3342
640 0.3320
645 0.3306
650 0.3298
655 0.3298
660 0.3308
665 0.3334
670 0.3381
675 0.3451
680 0.3537
685 0.3623
690 0.3699
695 0.3767
700 0.3831
.1039
475 0.1176
480 0.1341
485 0.1545
490 0.1806
495 0.2144
500 0.2552
505 0.2997
510 0.3487
515 0.4004
520 0.4475
525 0.4820
530 0.5114
535 0.5311
540 0.5405
545 0.5447
550 0.5446
555 0.5401
560 0.5316
565 0.5204
570 0.5079
575 Spectral/macbeth.12
#   Material:       Macbeth ColorCheckser - patch 12 - strong orange yellow
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0538
385 0.0552
390 0.0564
395 0.0576
400 0.0585
405 0.0591
410 0.0595
415 0.0592
420 0.0596
425 0.0594
430 0.0591
435 0.0590
440 0.0592
445 0.0602
450 0.0619
455 0.0646
460 0.0679
465 0.0720
470 0.0766
475 0.0817
480 0.0873
485 0.0934
490 0.0999
495 0.1070
500 0.1147
505 0.1235
510 0.1337
515 0.1460
520 0.1619
525 0.1831
530 0.2107
535 0.2460
540 0.2918
545 0.3420
550 0.3870
555 0.4292
560 0.4692
565 0.5054
570 0.5350
575 0.5577
580 0.5761
585 0.5916
590 0.6049
595 0.6165
600 0.6265
605 0.6347
610 0.6415
615 0.6471
620 0.6518
625 0.6561
630 0.6599
635 0.6633
640 0.6665
645 0.6694
650 0.6720
655 0.6745
660 0.6767
665 0.6786
670 0.6804
675 0.6819
680 0.6833
685 0.6847
690 0.6860
695 0.6875
700 0.6890
705 0.6907
710 0.6924
.0873
485 0.0934
490 0.0999
495 0.1070
500 0.1147
505 0.1235
510 0.1337
515 0.1460
520 0.1619
525 0.1831
530 0.2107
535 0.2460
540 0.2918
545 0.3420
550 0.3870
555 0.4292
560 0.4692
565 0.5054
570 0.5350
575Spectral/macbeth.13
#   Material:       Macbeth ColorCheckser - patch 13 - vivid purplish blue
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1092
385 0.1316
390 0.1593
395 0.1861
400 0.2111
405 0.2331
410 0.2514
415 0.2669
420 0.2811
425 0.2944
430 0.3063
435 0.3162
440 0.3236
445 0.3279
450 0.3286
455 0.3258
460 0.3190
465 0.3078
470 0.2913
475 0.2695
480 0.2436
485 0.2164
490 0.1903
495 0.1666
500 0.1456
505 0.1273
510 0.1116
515 0.0983
520 0.0873
525 0.0782
530 0.0708
535 0.0648
540 0.0599
545 0.0561
550 0.0531
555 0.0509
560 0.0493
565 0.0482
570 0.0475
575 0.0470
580 0.0467
585 0.0464
590 0.0462
595 0.0459
600 0.0456
605 0.0453
610 0.0451
615 0.0449
620 0.0448
625 0.0450
630 0.0452
635 0.0458
640 0.0466
645 0.0476
650 0.0490
655 0.0508
660 0.0530
665 0.0556
670 0.0587
675 0.0623
680 0.0664
685 0.0710
690 0.0758
695 0.0809
700 0.0861
.2913
475 0.2695
480 0.2436
485 0.2164
490 0.1903
495 0.1666
500 0.1456
505 0.1273
510 0.1116
515 0.0983
520 0.0873
525 0.0782
530 0.0708
535 0.0648
540 0.0599
545 0.0561
550 0.0531
555 0.0509
560 0.0493
565 0.0482
570 0.0475
575 Spectral/macbeth.14
#   Material:       Macbeth ColorCheckser - patch 14 strong yellowish green
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0568
385 0.0571
390 0.0575
395 0.0580
400 0.0586
405 0.0594
410 0.0604
415 0.0616
420 0.0629
425 0.0644
430 0.0660
435 0.0677
440 0.0696
445 0.0716
450 0.0741
455 0.0774
460 0.0817
465 0.0874
470 0.0949
475 0.1039
480 0.1143
485 0.1261
490 0.1391
495 0.1535
500 0.1699
505 0.1897
510 0.2172
515 0.2552
520 0.2931
525 0.3297
530 0.3532
535 0.3606
540 0.3575
545 0.3479
550 0.3337
555 0.3164
560 0.2971
565 0.2771
570 0.2561
575 0.2330
580 0.2082
585 0.1849
590 0.1646
595 0.1466
600 0.1303
605 0.1160
610 0.1047
615 0.0961
620 0.0896
625 0.0846
630 0.0806
635 0.0774
640 0.0748
645 0.0727
650 0.0713
655 0.0703
660 0.0699
665 0.0700
670 0.0707
675 0.0719
680 0.0736
685 0.0758
690 0.0783
695 0.0811
700 0.0841
.0949
475 0.1039
480 0.1143
485 0.1261
490 0.1391
495 0.1535
500 0.1699
505 0.1897
510 0.2172
515 0.2552
520 0.2931
525 0.3297
530 0.3532
535 0.3606
540 0.3575
545 0.3479
550 0.3337
555 0.3164
560 0.2971
565 0.2771
570 0.2561
575Spectral/macbeth.15
#   Material:       Macbeth ColorCheckser - patch 15 - strong red
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0449
385 0.0560
390 0.0570
395 0.0579
400 0.0588
405 0.0596
410 0.0603
415 0.0608
420 0.0612
425 0.0615
430 0.0615
435 0.0614
440 0.0612
445 0.0609
450 0.0605
455 0.0600
460 0.0596
465 0.0592
470 0.0588
475 0.0586
480 0.0583
485 0.0580
490 0.0576
495 0.0570
500 0.0562
505 0.0551
510 0.0539
515 0.0526
520 0.0511
525 0.0499
530 0.0491
535 0.0490
540 0.0495
545 0.0508
550 0.0526
555 0.0552
560 0.0586
565 0.0633
570 0.0695
575 0.0775
580 0.0880
585 0.1018
590 0.1229
595 0.1587
600 0.2055
605 0.2677
610 0.3288
615 0.4053
620 0.4735
625 0.5477
630 0.5978
635 0.6282
640 0.6533
645 0.6759
650 0.6946
655 0.7093
660 0.7202
665 0.7279
670 0.7337
675 0.7384
680 0.7425
685 0.7464
690 0.7503
695 0.7542
700 0.7581
.0588
475 0.0586
480 0.0583
485 0.0580
490 0.0576
495 0.0570
500 0.0562
505 0.0551
510 0.0539
515 0.0526
520 0.0511
525 0.0499
530 0.0491
535 0.0490
540 0.0495
545 0.0508
550 0.0526
555 0.0552
560 0.0586
565 0.0633
570 0.0695
575 0.0775
58Spectral/macbeth.16
#   Material:       Macbeth ColorCheckser - patch 16 - vivid yellow
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0440
385 0.0453
390 0.0466
395 0.0479
400 0.0490
405 0.0500
410 0.0509
415 0.0516
420 0.0522
425 0.0526
430 0.0531
435 0.0536
440 0.0544
445 0.0556
450 0.0574
455 0.0600
460 0.0640
465 0.0700
470 0.0785
475 0.0908
480 0.1085
485 0.1337
490 0.1681
495 0.2099
500 0.2560
505 0.3039
510 0.3548
515 0.4033
520 0.4446
525 0.4838
530 0.5216
535 0.5554
540 0.5841
545 0.6076
550 0.6262
555 0.6416
560 0.6551
565 0.6676
570 0.6792
575 0.6897
580 0.6990
585 0.7072
590 0.7141
595 0.7201
600 0.7252
605 0.7297
610 0.7338
615 0.7375
620 0.7409
625 0.7441
630 0.7471
635 0.7499
640 0.7526
645 0.7550
650 0.7572
655 0.7592
660 0.7610
665 0.7625
670 0.7638
675 0.7649
680 0.7660
685 0.7669
690 0.7677
695 0.7685
700 0.7676
.0785
475 0.0908
480 0.1085
485 0.1337
490 0.1681
495 0.2099
500 0.2560
505 0.3039
510 0.3548
515 0.4033
520 0.4446
525 0.4838
530 0.5216
535 0.5554
540 0.5841
545 0.6076
550 0.6262
555 0.6416
560 0.6551
565 0.6676
570 0.6792
575 0.6897
Spectral/macbeth.17
#   Material:       Macbeth ColorCheckser - patch 17 - strong reddish purple
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1420
385 0.1743
390 0.2129
395 0.2642
400 0.3221
405 0.3475
410 0.3614
415 0.3695
420 0.3724
425 0.3702
430 0.3640
435 0.3548
440 0.3434
445 0.3305
450 0.3163
455 0.3018
460 0.2880
465 0.2747
470 0.2617
475 0.2485
480 0.2350
485 0.2213
490 0.2074
495 0.1934
500 0.1800
505 0.1672
510 0.1548
515 0.1418
520 0.1276
525 0.1140
530 0.1049
535 0.1007
540 0.0991
545 0.0986
550 0.0984
555 0.0982
560 0.0986
565 0.1014
570 0.1087
575 0.1238
580 0.1479
585 0.1808
590 0.2232
595 0.2748
600 0.3350
605 0.3907
610 0.4506
615 0.5018
620 0.5387
625 0.5691
630 0.5943
635 0.6137
640 0.6285
645 0.6404
650 0.6503
655 0.6586
660 0.6656
665 0.6717
670 0.6769
675 0.6816
680 0.6856
685 0.6893
690 0.6927
695 0.6958
700 0.6983
.2617
475 0.2485
480 0.2350
485 0.2213
490 0.2074
495 0.1934
500 0.1800
505 0.1672
510 0.1548
515 0.1418
520 0.1276
525 0.1140
530 0.1049
535 0.1007
540 0.0991
545 0.0986
550 0.0984
555 0.0982
560 0.0986
565 0.1014
570 0.1087
57Spectral/macbeth.18
#   Material:       Macbeth ColorCheckser - patch 18 - strong greenish purple
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1105
385 0.1340
390 0.1584
395 0.1848
400 0.2113
405 0.2337
410 0.2501
415 0.2624
420 0.2732
425 0.2844
430 0.2968
435 0.3104
440 0.3247
445 0.3397
450 0.3551
455 0.3712
460 0.3880
465 0.4057
470 0.4234
475 0.4389
480 0.4502
485 0.4557
490 0.4557
495 0.4507
500 0.4413
505 0.4278
510 0.4107
515 0.3908
520 0.3689
525 0.3451
530 0.3194
535 0.2927
540 0.2661
545 0.2399
550 0.2122
555 0.1838
560 0.1600
565 0.1410
570 0.1257
575 0.1134
580 0.1036
585 0.0957
590 0.0892
595 0.0839
600 0.0797
605 0.0765
610 0.0742
615 0.0727
620 0.0719
625 0.0717
630 0.0718
635 0.0723
640 0.0730
645 0.0737
650 0.0745
655 0.0753
660 0.0760
665 0.0766
670 0.0769
675 0.0769
680 0.0766
685 0.0758
690 0.0748
695 0.0735
700 0.0720
.4234
475 0.4389
480 0.4502
485 0.4557
490 0.4557
495 0.4507
500 0.4413
505 0.4278
510 0.4107
515 0.3908
520 0.3689
525 0.3451
530 0.3194
535 0.2927
540 0.2661
545 0.2399
550 0.2122
555 0.1838
560 0.1600
565 0.1410
570 0.1257
5Spectral/macbeth.19
#   Material:       Macbeth ColorCheckser - patch 19 - white
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1492
385 0.1991
390 0.2582
395 0.3354
400 0.4058
405 0.5007
410 0.6117
415 0.7419
420 0.8193
425 0.8557
430 0.8786
435 0.8914
440 0.8988
445 0.9035
450 0.9067
455 0.9088
460 0.9103
465 0.9112
470 0.9117
475 0.9117
480 0.9112
485 0.9104
490 0.9092
495 0.9078
500 0.9063
505 0.9048
510 0.9035
515 0.9023
520 0.9012
525 0.9003
530 0.8995
535 0.8988
540 0.8983
545 0.8978
550 0.8973
555 0.8970
560 0.8966
565 0.8963
570 0.8961
575 0.8959
580 0.8958
585 0.8958
590 0.8958
595 0.8960
600 0.8962
605 0.8966
610 0.8970
615 0.8975
620 0.8981
625 0.8987
630 0.8993
635 0.8998
640 0.9001
645 0.9001
650 0.8994
655 0.8981
660 0.8965
665 0.8950
670 0.8937
675 0.8930
680 0.8928
685 0.8929
690 0.8934
695 0.8940
700 0.8947
705 0.8955
.9117
480 0.9112
485 0.9104
490 0.9092
495 0.9078
500 0.9063
505 0.9048
510 0.9035
515 0.9023
520 0.9012
525 0.9003
530 0.8995
535 0.8988
540 0.8983
545 0.8978
550 0.8973
555 0.8970
560 0.8966
565 0.8963
570 0.8961
575 0.8959
580 0.8Spectral/macbeth.2
#   Material:       Macbeth ColorCheckser - patch 2 - light skin
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1207
385 0.1394
390 0.1574
395 0.1726
400 0.1837
405 0.1914
410 0.1968
415 0.2002
420 0.2024
425 0.2041
430 0.2065
435 0.2103
440 0.2160
445 0.2234
450 0.2322
455 0.2425
460 0.2544
465 0.2680
470 0.2828
475 0.2977
480 0.3113
485 0.3227
490 0.3308
495 0.3356
500 0.3378
505 0.3379
510 0.3356
515 0.3298
520 0.3194
525 0.3056
530 0.2930
535 0.2860
540 0.2852
545 0.2862
550 0.2857
555 0.2825
560 0.2799
562 0.2802
565 0.2832
570 0.2984
575 0.3261
580 0.3641
585 0.4092
590 0.4439
595 0.4703
600 0.4909
605 0.5058
610 0.5182
615 0.5287
620 0.5381
625 0.5468
630 0.5552
635 0.5635
640 0.5720
645 0.5810
650 0.5906
655 0.6012
660 0.6128
665 0.6256
670 0.6395
675 0.6544
680 0.6705
685 0.6877
690 0.7057
695 0.7243
700 0.7431
.2977
480 0.3113
485 0.3227
490 0.3308
495 0.3356
500 0.3378
505 0.3379
510 0.3356
515 0.3298
520 0.3194
525 0.3056
530 0.2930
535 0.2860
540 0.2852
545 0.2862
550 0.2857
555 0.2825
560 0.2799
562 0.2802
565 0.2832
570 0.2984
575Spectral/macbeth.20
#   Material:       Macbeth ColorCheckser - patch 20 - light gray
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1596
385 0.1944
390 0.2434
395 0.3314
400 0.4189
405 0.4856
410 0.5430
415 0.5729
420 0.5875
425 0.5952
430 0.5993
435 0.6012
440 0.6018
445 0.6016
450 0.6009
455 0.5999
460 0.5989
465 0.5979
470 0.5971
475 0.5964
480 0.5957
485 0.5952
490 0.5948
495 0.5944
500 0.5941
505 0.5938
510 0.5935
515 0.5932
520 0.5929
525 0.5925
530 0.5920
535 0.5915
540 0.5908
545 0.5902
550 0.5897
555 0.5893
560 0.5892
565 0.5893
570 0.5898
575 0.5906
580 0.5915
585 0.5925
590 0.5933
595 0.5940
600 0.5942
605 0.5940
610 0.5934
615 0.5926
620 0.5915
625 0.5904
630 0.5893
635 0.5883
640 0.5876
645 0.5871
650 0.5867
655 0.5865
660 0.5863
665 0.5860
670 0.5856
675 0.5849
680 0.5839
685 0.5828
690 0.5814
695 0.5800
700 0.5785
.5971
475 0.5964
480 0.5957
485 0.5952
490 0.5948
495 0.5944
500 0.5941
505 0.5938
510 0.5935
515 0.5932
520 0.5929
525 0.5925
530 0.5920
535 0.5915
540 0.5908
545 0.5902
550 0.5897
555 0.5893
560 0.5892
565 0.5893
570 0.5898
575 0.5906
58Spectral/macbeth.21
#   Material:       Macbeth ColorCheckser - patch 21 - light-medium gray
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1496
385 0.1726
390 0.2032
395 0.2486
400 0.2926
405 0.3331
410 0.3514
415 0.3601
420 0.3655
425 0.3691
430 0.3717
435 0.3735
440 0.3746
445 0.3751
450 0.3751
455 0.3747
460 0.3739
465 0.3728
470 0.3715
475 0.3700
480 0.3686
485 0.3674
490 0.3664
495 0.3660
500 0.3660
505 0.3665
510 0.3672
515 0.3679
520 0.3686
525 0.3690
530 0.3691
535 0.3689
540 0.3685
545 0.3680
550 0.3676
555 0.3674
560 0.3675
565 0.3679
570 0.3685
575 0.3692
580 0.3700
585 0.3708
590 0.3715
595 0.3721
600 0.3725
605 0.3727
610 0.3727
615 0.3725
620 0.3721
625 0.3715
630 0.3707
635 0.3698
640 0.3687
645 0.3676
650 0.3665
655 0.3653
660 0.3641
665 0.3629
670 0.3617
675 0.3605
680 0.3593
685 0.3582
690 0.3571
695 0.3560
700 0.3549
.3715
475 0.3700
480 0.3686
485 0.3674
490 0.3664
495 0.3660
500 0.3660
505 0.3665
510 0.3672
515 0.3679
520 0.3686
525 0.3690
530 0.3691
535 0.3689
540 0.3685
545 0.3680
550 0.3676
555 0.3674
560 0.3675
565 0.3679
570 0.3685
575 0.Spectral/macbeth.22
#   Material:       Macbeth ColorCheckser - patch 22 - medium gray
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1096
385 0.1247
390 0.1415
395 0.1609
400 0.1774
405 0.1874
410 0.1929
415 0.1962
420 0.1982
425 0.1996
430 0.2007
435 0.2014
440 0.2019
445 0.2023
450 0.2024
455 0.2025
460 0.2024
465 0.2024
470 0.2023
475 0.2023
480 0.2023
485 0.2024
490 0.2026
495 0.2027
500 0.2029
505 0.2030
510 0.2030
515 0.2030
520 0.2029
525 0.2026
530 0.2022
535 0.2018
540 0.2014
545 0.2010
550 0.2008
555 0.2008
560 0.2010
565 0.2013
570 0.2016
575 0.2020
580 0.2024
585 0.2027
590 0.2029
595 0.2030
600 0.2029
605 0.2026
610 0.2021
615 0.2016
620 0.2011
625 0.2007
630 0.2006
635 0.2006
640 0.2006
645 0.2007
650 0.2006
655 0.2001
660 0.1993
665 0.1982
670 0.1972
675 0.1966
680 0.1965
685 0.1967
690 0.1971
695 0.1976
700 0.1982
.2023
475 0.2023
480 0.2023
485 0.2024
490 0.2026
495 0.2027
500 0.2029
505 0.2030
510 0.2030
515 0.2030
520 0.2029
525 0.2026
530 0.2022
535 0.2018
540 0.2014
545 0.2010
550 0.2008
555 0.2008
560 0.2010
565 0.2013
570 0.2016
575 0.2020
5Spectral/macbeth.23
#   Material:       Macbeth ColorCheckser - patch 23 - dark gray
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0721
385 0.0770
390 0.0816
395 0.0857
400 0.0890
405 0.0916
410 0.0935
415 0.0948
420 0.0957
425 0.0963
430 0.0967
435 0.0969
440 0.0969
445 0.0968
450 0.0967
455 0.0964
460 0.0962
465 0.0959
470 0.0957
475 0.0956
480 0.0955
485 0.0954
490 0.0954
495 0.0953
500 0.0953
505 0.0953
510 0.0953
515 0.0953
520 0.0952
525 0.0951
530 0.0950
535 0.0948
540 0.0946
545 0.0945
550 0.0945
555 0.0945
560 0.0947
565 0.0949
570 0.0952
575 0.0955
580 0.0959
585 0.0963
590 0.0967
595 0.0970
600 0.0973
605 0.0975
610 0.0976
615 0.0976
620 0.0974
625 0.0973
630 0.0970
635 0.0968
640 0.0966
645 0.0965
650 0.0964
655 0.0964
660 0.0964
665 0.0965
670 0.0965
675 0.0965
680 0.0966
685 0.0966
690 0.0966
695 0.0966
700 0.0966
.0957
475 0.0956
480 0.0955
485 0.0954
490 0.0954
495 0.0953
500 0.0953
505 0.0953
510 0.0953
515 0.0953
520 0.0952
525 0.0951
530 0.0950
535 0.0948
540 0.0946
545 0.0945
550 0.0945
555 0.0945
560 0.0947
565 0.0949
570 0.0952
575 0.0955
580Spectral/macbeth.24
#   Material:       Macbeth ColorCheckser - patch 24 - black
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0301
385 0.0309
390 0.0319
395 0.0329
400 0.0338
405 0.0346
410 0.0353
415 0.0358
420 0.0361
425 0.0362
430 0.0360
435 0.0357
440 0.0353
445 0.0348
450 0.0342
455 0.0338
460 0.0334
465 0.0332
470 0.0331
475 0.0330
480 0.0331
485 0.0332
490 0.0333
495 0.0334
500 0.0336
505 0.0337
510 0.0338
515 0.0339
520 0.0340
525 0.0340
530 0.0340
535 0.0338
540 0.0337
545 0.0335
550 0.0334
555 0.0333
560 0.0332
565 0.0332
570 0.0333
575 0.0335
580 0.0336
585 0.0338
590 0.0340
595 0.0345
600 0.0344
605 0.0345
610 0.0347
615 0.0348
620 0.0350
625 0.0351
630 0.0353
635 0.0354
640 0.0355
645 0.0357
650 0.0359
655 0.0360
660 0.0362
665 0.0364
670 0.0367
675 0.0369
680 0.0371
685 0.0374
690 0.0376
695 0.0379
700 0.0381
.0331
475 0.0330
480 0.0331
485 0.0332
490 0.0333
495 0.0334
500 0.0336
505 0.0337
510 0.0338
515 0.0339
520 0.0340
525 0.0340
530 0.0340
535 0.0338
540 0.0337
545 0.0335
550 0.0334
555 0.0333
560 0.0332
565 0.0332
570 0.0333
575 0.0335
580 0.0Spectral/macbeth.3
#   Material:       Macbeth ColorCheckser - patch 3 - blue sky
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1428
385 0.1879
390 0.2318
395 0.2733
400 0.3106
405 0.3369
410 0.3525
415 0.3607
420 0.3638
425 0.3641
430 0.3629
435 0.3609
440 0.3585
445 0.3556
450 0.3522
455 0.3483
460 0.3439
465 0.3389
470 0.3329
475 0.3260
480 0.3182
485 0.3095
490 0.3005
495 0.2914
500 0.2826
505 0.2744
510 0.2665
515 0.2591
520 0.2520
525 0.2453
530 0.2389
535 0.2328
540 0.2269
545 0.2211
550 0.2155
555 0.2105
560 0.2064
565 0.2030
570 0.1995
575 0.1948
580 0.1891
585 0.1838
590 0.1798
595 0.1772
600 0.1755
605 0.1742
610 0.1728
615 0.1712
620 0.1692
625 0.1669
630 0.1644
635 0.1615
640 0.1583
645 0.1549
650 0.1513
655 0.1478
660 0.1443
665 0.1412
670 0.1386
675 0.1363
680 0.1341
685 0.1318
690 0.1294
695 0.1268
700 0.1248
.3329
475 0.3260
480 0.3182
485 0.3095
490 0.3005
495 0.2914
500 0.2826
505 0.2744
510 0.2665
515 0.2591
520 0.2520
525 0.2453
530 0.2389
535 0.2328
540 0.2269
545 0.2211
550 0.2155
555 0.2105
560 0.2064
565 0.2030
570 0.1995
575 0.1948
580 0Spectral/macbeth.4
#   Material:       Macbeth ColorCheckser - patch 4 - foliage
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0573
385 0.0570
390 0.0568
395 0.0566
400 0.0564
405 0.0563
410 0.0563
415 0.0564
420 0.0565
425 0.0567
430 0.0569
435 0.0571
440 0.0572
445 0.0572
450 0.0571
455 0.0569
460 0.0568
465 0.0568
470 0.0570
475 0.0575
480 0.0584
485 0.0599
490 0.0623
495 0.0659
500 0.0714
505 0.0799
510 0.0935
515 0.1164
520 0.1439
525 0.1664
530 0.1827
535 0.1922
537 0.1939
540 0.1941
545 0.1886
550 0.1788
555 0.1674
560 0.1564
565 0.1472
570 0.1396
575 0.1333
580 0.1279
585 0.1232
590 0.1191
595 0.1154
600 0.1121
605 0.1093
610 0.1070
615 0.1051
620 0.1036
625 0.1025
630 0.1017
635 0.1012
640 0.1010
645 0.1010
650 0.1014
655 0.1021
660 0.1033
665 0.1052
670 0.1081
675 0.1126
680 0.1195
685 0.1296
690 0.1425
695 0.1573
700 0.1734
705 0.1900
.0584
485 0.0599
490 0.0623
495 0.0659
500 0.0714
505 0.0799
510 0.0935
515 0.1164
520 0.1439
525 0.1664
530 0.1827
535 0.1922
537 0.1939
540 0.1941
545 0.1886
550 0.1788
555 0.1674
560 0.1564
565 0.1472
570 0.1396
575 0.Spectral/macbeth.5
#   Material:       Macbeth ColorCheckser - patch 5 - blue flower
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1329
385 0.1796
390 0.2256
395 0.2792
400 0.3668
405 0.4116
410 0.4340
415 0.4464
420 0.4531
425 0.4562
430 0.4566
435 0.4551
440 0.4522
445 0.4482
450 0.4433
455 0.4377
460 0.4312
465 0.4239
470 0.4160
475 0.4074
480 0.3984
485 0.3890
490 0.3793
495 0.3690
500 0.3573
505 0.3423
510 0.3229
515 0.3005
520 0.2792
525 0.2609
530 0.2446
535 0.2310
540 0.2222
545 0.2162
550 0.2115
555 0.2078
560 0.2052
565 0.2037
570 0.2042
575 0.2078
580 0.2146
585 0.2229
590 0.2303
595 0.2364
600 0.2411
605 0.2443
610 0.2461
615 0.2471
620 0.2478
625 0.2488
630 0.2510
635 0.2560
640 0.2663
645 0.2852
650 0.3133
655 0.3465
660 0.3818
665 0.4144
670 0.4426
675 0.4660
680 0.4836
685 0.4949
690 0.5022
695 0.5076
700 0.5122
.4160
475 0.4074
480 0.3984
485 0.3890
490 0.3793
495 0.3690
500 0.3573
505 0.3423
510 0.3229
515 0.3005
520 0.2792
525 0.2609
530 0.2446
535 0.2310
540 0.2222
545 0.2162
550 0.2115
555 0.2078
560 0.2052
565 0.2037
570 0.2042
575 0.2078
58Spectral/macbeth.6
#   Material:       Macbeth ColorCheckser - patch 6 - bluish green
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1465
385 0.1759
390 0.2079
395 0.2428
400 0.2760
405 0.3029
410 0.3215
415 0.3329
420 0.3405
425 0.3465
430 0.3524
435 0.3593
440 0.3673
445 0.3766
450 0.3872
455 0.3997
460 0.4147
465 0.4343
470 0.4600
475 0.4906
480 0.5206
485 0.5467
490 0.5677
495 0.5828
500 0.5917
505 0.5952
510 0.5944
515 0.5903
520 0.5836
525 0.5750
530 0.5649
535 0.5533
540 0.5402
545 0.5252
550 0.5077
555 0.4869
560 0.4629
565 0.4378
570 0.4142
575 0.3914
580 0.3679
585 0.3424
590 0.3169
595 0.2950
600 0.2773
605 0.2631
610 0.2515
615 0.2422
620 0.2348
625 0.2290
630 0.2246
635 0.2214
640 0.2192
645 0.2179
650 0.2175
655 0.2180
660 0.2195
665 0.2219
670 0.2252
675 0.2294
680 0.2344
685 0.2399
690 0.2459
695 0.2522
700 0.2573
.4600
475 0.4906
480 0.5206
485 0.5467
490 0.5677
495 0.5828
500 0.5917
505 0.5952
510 0.5944
515 0.5903
520 0.5836
525 0.5750
530 0.5649
535 0.5533
540 0.5402
545 0.5252
550 0.5077
555 0.4869
560 0.4629
565 0.4378
570 0.4142
575 0.3914
5Spectral/macbeth.7
#   Material:       Macbeth ColorCheckser - patch 7 - orange
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.0572
385 0.0575
390 0.0578
395 0.0581
400 0.0585
405 0.0589
410 0.0594
415 0.0599
420 0.0605
425 0.0611
430 0.0616
435 0.0621
440 0.0624
445 0.0625
450 0.0623
455 0.0620
460 0.0616
465 0.0613
470 0.0613
475 0.0616
480 0.0624
485 0.0636
490 0.0654
495 0.0678
500 0.0709
505 0.0747
510 0.0796
515 0.0860
520 0.0944
525 0.1051
530 0.1184
535 0.1342
540 0.1527
545 0.1742
550 0.1984
555 0.2244
560 0.2515
565 0.2802
570 0.3113
575 0.3469
580 0.3889
585 0.4322
590 0.4695
595 0.5010
600 0.5276
605 0.5487
610 0.5640
615 0.5750
620 0.5833
625 0.5902
630 0.5960
635 0.6012
640 0.6059
645 0.6102
650 0.6143
655 0.6182
660 0.6218
665 0.6248
670 0.6273
675 0.6292
680 0.6308
685 0.6323
690 0.6337
695 0.6353
700 0.6373
705 0.6396
710 0.6422
.0624
485 0.0636
490 0.0654
495 0.0678
500 0.0709
505 0.0747
510 0.0796
515 0.0860
520 0.0944
525 0.1051
530 0.1184
535 0.1342
540 0.1527
545 0.1742
550 0.1984
555 0.2244
560 0.2515
565 0.2802
570 0.3113
575 0.3469
580 0.3Spectral/macbeth.8
#   Material:       Macbeth ColorCheckser - patch 8 - purplish blue
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1184
385 0.1343
390 0.1643
395 0.2173
400 0.2587
405 0.2922
410 0.3193
415 0.3389
420 0.3529
425 0.3642
430 0.3747
435 0.3856
440 0.3960
445 0.4035
450 0.4064
455 0.4049
460 0.3992
465 0.3889
470 0.3733
475 0.3536
480 0.3330
485 0.3125
490 0.2898
495 0.2628
500 0.2367
505 0.2151
510 0.1951
515 0.1761
520 0.1603
525 0.1482
530 0.1389
535 0.1311
540 0.1243
545 0.1181
550 0.1123
555 0.1069
560 0.1022
565 0.0982
570 0.0950
575 0.0929
580 0.0917
585 0.0911
590 0.0909
595 0.0908
600 0.0907
605 0.0905
610 0.0902
615 0.0902
620 0.0905
625 0.0915
630 0.0933
635 0.0961
640 0.0997
645 0.1041
650 0.1092
655 0.1149
660 0.1206
665 0.1256
670 0.1289
675 0.1303
680 0.1299
685 0.1280
690 0.1253
695 0.1219
700 0.1182
705 0.1142
.3536
480 0.3330
485 0.3125
490 0.2898
495 0.2628
500 0.2367
505 0.2151
510 0.1951
515 0.1761
520 0.1603
525 0.1482
530 0.1389
535 0.1311
540 0.1243
545 0.1181
550 0.1123
555 0.1069
560 0.1022
565 0.0982
570 0.0950
575 0.0929
Spectral/macbeth.9
#   Material:       Macbeth ColorCheckser - patch 9 - moderate red
#   Source:
#     reflectance:  McCamy, C. S., H. Marcus, and J. G. Davidson (1976)
#                       ``A Color Rendition Chart,''
#                       Journal of Applied Photographic Engineering,
#                       vol. 11, no. 3, pp. 95-99.
#                   Scanned data from article by:
#                       Program of Computer Graphics
#                       Cornell University
#     n,k:          unknown
#   Conditions:
#   Temperature:
#   Incident angle:
#   Solid angle:
#
dielectric
380 0.1125
385 0.1242
390 0.1351
395 0.1442
400 0.1506
405 0.1530
410 0.1514
415 0.1475
420 0.1430
425 0.1393
430 0.1368
435 0.1352
440 0.1342
445 0.1334
450 0.1327
455 0.1318
460 0.1306
465 0.1289
470 0.1267
475 0.1240
480 0.1210
485 0.1176
490 0.1141
495 0.1105
500 0.1069
505 0.1035
510 0.1004
515 0.0977
520 0.0956
525 0.0942
530 0.0935
535 0.0937
540 0.0948
545 0.0966
550 0.0992
555 0.1026
560 0.1069
565 0.1122
570 0.1192
575 0.1297
580 0.1519
585 0.1929
590 0.2395
595 0.2973
600 0.3625
605 0.4191
610 0.4761
615 0.5123
620 0.5338
625 0.5484
630 0.5584
635 0.5653
640 0.5697
645 0.5725
650 0.5742
655 0.5752
660 0.5757
665 0.5757
670 0.5756
675 0.5752
680 0.5748
685 0.5742
690 0.5735
695 0.5728
700 0.5720
.1267
475 0.1240
480 0.1210
485 0.1176
490 0.1141
495 0.1105
500 0.1069
505 0.1035
510 0.1004
515 0.0977
520 0.0956
525 0.0942
530 0.0935
535 0.0937
540 0.0948
545 0.0966
550 0.0992
555 0.1026
560 0.1069
565 0.1122
570 0.1192
575 0.1297
5Spectral/nickel.ar1
#   Material:       Nickel
#   Source:
#     reflectance:  Thermophysical Properties of Matter (1970)
#                       v.7, p.456, curve #2 (from Cook's thesis)
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     Grade A pure, as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 1.79
k 3.33
380 0.310
420 0.355
460 0.405
500 0.445
540 0.480
580 0.510
620 0.530
660 0.550
700 0.565
rce:
#     reflectance:  Thermophysical Properties of Matter (1970)
#                       v.7, p.456, curve #2 (from Cook's thesis)
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     Grade A pure, as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 1.79
k 3.33
380 0.310
420 0.355
460 0.405
500 0.445
540 0.480
580 Spectral/nickel.ar2
#   Material:       Nickel
#   Source:
#     reflectance:  Thermophysical Properties of High Temperature Solid
#                       Materials (1967) v.1, p718, curve #3
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     Commercial Grade A pure, as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 1.79
k 3.33
300 0.210
380 0.307
460 0.390
480 0.420
600 0.500
700 0.550
800 0.600
860 0.630
900 0.640
950 0.650
1100 0.670
1150 0.700
1300 0.740
1400 0.760
1600 0.780
1700 0.800
          Materials (1967) v.1, p718, curve #3
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     Commercial Grade A pure, as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 1.79
k 3.33
300 0.210
380 0.307
460 0.390
480 0.420Spectral/quartz.p
#   Material:       Quartz
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23A Reffractive Index for Several Solids.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 1.5438
410 0.047451
470 0.046653
550 0.046096
580 0.045700
610 0.045620
660 0.045462
3.33
300 0.210
380 0.307
460 0.390
480 0.420Spectral/quartz_E.p
#   Material:       Quartz - SiO2 - ray E
#   Type            553/687
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.55336
434 0.048306
482 0.047715
589 0.046967
656 0.046644
       Quartz - SiO2 - ray E
#   Type            553/687
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.55336
434 0.048306
482 0.047715
589 0.04696Spectral/quartz_O.p
#   Material:       Quartz - SiO2 - ray O
#   Type            544/700
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.54425
434 0.047047
482 0.046478
589 0.045759
656 0.045449
       Quartz - SiO2 - ray O
#   Type            544/700
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.54425
434 0.047047
482 0.046478
589 0.04575Spectral/rutile_E.p
#   Material:       Rutile - TiO3 - ray O
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 24A Refractive Incices for Three Gemstones
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 2.917
410 0.289558
470 0.261734
550 0.244090
580 0.239517
610 0.235933
660 0.231288
7047
482 0.046478
589 0.04575Spectral/rutile_O.p
#   Material:       Rutile - TiO3 - ray O
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 24A Refractive Incices for Three Gemstones
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 2.621
410 0.246865
470 0.219765
550 0.204354
580 0.200405
610 0.197119
660 0.193265
7047
482 0.046478
589 0.04575Spectral/void
#   Material:       Identity material -- perfect void
#   Source:         Hypothetical reference
#
conductor
0    0
2000 0
n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       table 24A Refractive Incices for Three Gemstones
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 2.621
410 0.246865
470 0.219765
550 0.204354
580 0.200405
610 0.197119
660 0.193265
7047
482 0.046478
589 0.04575
620 0.530
660 0.550
700 0.565
rce:
#     reflectance:  Thermophysical Properties of Matter (1970)
#                       v.7, p.456, curve #2 (from Cook's thesis)
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     Grade A pure, as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 1.79
k 3.33
380 0.310
420 0.355
460 0.405
500 0.445
540 0.480
580 Spectral/nickel.ar2
#   Material:       Nickel
#   Source:
#     reflectance:  Thermophysical Properties of High Temperature Solid
#                       Materials (1967) v.1, p718, curve #3
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     Commercial Grade A pure, as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 1.79
k 3.33
300 0.210
380 0.307
460 0.390
480 0.420
600 0.500
700 0.550
800 0.600
860 0.630
900 0.640
950 0.650
1100 0.670
1150 0.700
1300 0.740
1400 0.760
1600 0.780
1700 0.800
          Materials (1967) v.1, p718, curve #3
#     n,k:          Light, R.W.Ditchburn
#                       table 15-1, p.542
#                       measurement wavelength not specified
#   Conditions:     Commercial Grade A pure, as received
#   Temperature:    298 K
#   Incident angle: 9
#   Solid angle:    2pi
#
conductor
n 1.79
k 3.33
300 0.210
380 0.307
460 0.390
480 0.420Spectral/quartz.p
#   Material:       Quartz
#   Type
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Table 23A Reffractive Index for Several Solids.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 580nm
n 1.5438
410 0.047451
470 0.046653
550 0.046096
580 0.045700
610 0.045620
660 0.045462
3.33
300 0.210
380 0.307
460 0.390
480 0.420Spectral/quartz_E.p
#   Material:       Quartz - SiO2 - ray E
#   Type            553/687
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.55336
434 0.048306
482 0.047715
589 0.046967
656 0.046644
       Quartz - SiO2 - ray E
#   Type            553/687
#   Source:
#     reflectance:  Computed from index of refraction
#     n,k:          Fundamentals of Optics, Jenkins and White (1976)
#                       Appendix IV - Refractive Indicies and Dispersions
#                       for optical crystals.
#   Conditions:     polished
#   Temperature:
#   Incident angle: normal
#   Solid angle:
#
dielectric
#   index of refraction at 589.2nm
n 1.55336
434 0.048306
482 0.047715
589 0.04696
