Basic Operations on Two Spectra
Union:
PUBLIC
PROC [a, b: Spectrum]
RETURNS [newa, newb: Spectrum] ~ {
swap, ulen: INT ¬ a.length;
union: RealSequence ¬ NEW[RealSequenceRep[a.length + b.length]];
first build the union of the two wavelength lists
FOR i: INT IN [0 .. a.length) DO union[i] ¬ a[i].wavelength; ENDLOOP;
FOR i:
INT
IN [0 .. b.length)
DO
unused: BOOLEAN ¬ TRUE;
FOR j:
INT
IN [0 .. a.length)
DO
IF a[j].wavelength = b[i].wavelength THEN unused ¬ FALSE;
ENDLOOP;
IF unused
THEN {
union[ulen] ¬ b[i].wavelength;
ulen ¬ ulen + 1;
};
ENDLOOP;
now bubble-sort the wavelength list (not a bad idea for such a small list)
FOR i:
INT
IN [0 .. ulen)
DO
swap ¬ -1;
FOR j:
INT
IN [i+1 .. ulen)
DO
IF union[j] < union[i] THEN swap ¬ j;
ENDLOOP;
WHILE swap >= 0
DO
tmp: REAL;
tmp ¬ union[i]; union[i] ¬ union[swap]; union[swap] ¬ tmp;
swap ¬ -1;
FOR j:
INT
IN [i+1 .. ulen)
DO
IF union[j] < union[i] THEN swap ¬ j;
ENDLOOP;
ENDLOOP;
ENDLOOP;
and finally build the composite spectra
newa ¬ NEW[SpectrumRep[ulen]];
newb ¬ NEW[SpectrumRep[ulen]];
newa.length ¬ newb.length ¬ ulen;
FOR i:
INT
IN [0 .. ulen)
DO
newa[i].wavelength ¬ newb[i].wavelength ¬ union[i];
newa[i].amplitude ¬ Sample[a, union[i]];
newb[i].amplitude ¬ Sample[b, union[i]];
ENDLOOP;
};
Conform:
PUBLIC
PROC [a, b: Spectrum]
RETURNS [newa: Spectrum] ~ {
newa ¬ NEW[SpectrumRep[b.length]];
newa.length ¬ b.length;
FOR i:
INT
IN [0 .. b.length)
DO
newa[i].wavelength ¬ b[i].wavelength;
newa[i].amplitude ¬ Sample[a, b[i].wavelength];
ENDLOOP;
};
Add:
PUBLIC
PROC [a, b: Spectrum]
RETURNS [c: Spectrum] ~ {
newa, newb: Spectrum;
[newa, newb] ¬ Union[a, b];
c ¬ NEW[SpectrumRep[newa.length]];
c.length ¬newa.length;
FOR i:
INT
IN [0 .. a.length)
DO
c[i].wavelength ¬ newa[i].wavelength;
c[i].amplitude ¬ newa[i].amplitude + newb[i].amplitude;
ENDLOOP;
};
Sub:
PUBLIC
PROC [a, b: Spectrum]
RETURNS [c: Spectrum] ~ {
newa, newb: Spectrum;
[newa, newb] ¬ Union[a, b];
c ¬ NEW[SpectrumRep[newa.length]];
c.length ¬newa.length;
FOR i:
INT
IN [0 .. a.length)
DO
c[i].wavelength ¬ newa[i].wavelength;
c[i].amplitude ¬ newa[i].amplitude - newb[i].amplitude;
ENDLOOP;
};
Lerp:
PUBLIC
PROC [a, b: Spectrum, alpha:
REAL]
RETURNS [c: Spectrum] ~ {
newa, newb: Spectrum;
[newa, newb] ¬ Union[a, b];
c ¬ NEW[SpectrumRep[newa.length]];
c.length ¬newa.length;
FOR i:
INT
IN [0 .. a.length)
DO
c[i].wavelength ¬ newa[i].wavelength;
c[i].amplitude ¬ newa[i].amplitude + alpha * (newb[i].amplitude - newa[i].amplitude);
ENDLOOP;
};
Blend:
PUBLIC
PROC [a, b: Spectrum, aScl, bScl:
REAL]
RETURNS [c: Spectrum] ~ {
newa, newb: Spectrum;
[newa, newb] ¬ Union[a, b];
c ¬ NEW[SpectrumRep[newa.length]];
c.length ¬newa.length;
FOR i:
INT
IN [0 .. a.length)
DO
c[i].wavelength ¬ newa[i].wavelength;
c[i].amplitude ¬ (aScl * newa[i].amplitude) + (bScl * newb[i].amplitude);
ENDLOOP;
};
Filter:
PUBLIC
PROC [a, b: Spectrum]
RETURNS [c: Spectrum] ~ {
newa, newb: Spectrum;
[newa, newb] ¬ Union[a, b];
c ¬ NEW[SpectrumRep[newa.length]];
c.length ¬newa.length;
FOR i:
INT
IN [0 .. a.length)
DO
c[i].wavelength ¬ newa[i].wavelength;
c[i].amplitude ¬ newa[i].amplitude * newb[i].amplitude;
ENDLOOP;
};
Damp:
PUBLIC
PROC [a, b: Spectrum]
RETURNS [c: Spectrum] ~ {
newa, newb: Spectrum;
[newa, newb] ¬ Union[a, b];
c ¬ NEW[SpectrumRep[newa.length]];
c.length ¬newa.length;
FOR i:
INT
IN [0 .. a.length)
DO
c[i].wavelength ¬ newa[i].wavelength;
IF newb[i].amplitude # 0.0
THEN c[i].amplitude ¬ newa[i].amplitude / newb[i].amplitude
ELSE c[i].amplitude ¬ 0.0;
ENDLOOP;
};
Start Code
CIE 1931 2-degree observer matching functions from 380 to 780 nm in 5nm steps
xSamples:
ARRAY [0 .. 80]
OF
REAL ¬ [
0.0014, 0.0022, 0.0042, 0.0076, 0.0143, 0.0232, 0.0435, 0.0776, 0.1344,
0.2148, 0.2839, 0.3285, 0.3483, 0.3481, 0.3362, 0.3187, 0.2908, 0.2511,
0.1954, 0.1421, 0.0956, 0.0580, 0.0320, 0.0147, 0.0049, 0.0024, 0.0093,
0.0291, 0.0633, 0.1096, 0.1655, 0.2257, 0.2904, 0.3597, 0.4334, 0.5121,
0.5945, 0.6784, 0.7621, 0.8425, 0.9163, 0.9786, 1.0263, 1.0567, 1.0622,
1.0456, 1.0026, 0.9384, 0.8544, 0.7514, 0.6424, 0.5419, 0.4479, 0.3608,
0.2835, 0.2187, 0.1649, 0.1212, 0.0874, 0.0636, 0.0468, 0.0329, 0.0227,
0.0158, 0.0114, 0.0081, 0.0058, 0.0041, 0.0029, 0.0020, 0.0014, 0.0010,
0.0007, 0.0005, 0.0003, 0.0002, 0.0002, 0.0001, 0.0001, 0.0000, 0.0000
];
ySamples:
ARRAY [0 .. 80]
OF
REAL ¬ [
0.0000, 0.0001, 0.0001, 0.0002, 0.0004, 0.0006, 0.0012, 0.0022, 0.0040,
0.0073, 0.0116, 0.0168, 0.0230, 0.0298, 0.0380, 0.0480, 0.0600, 0.0739,
0.0910, 0.1126, 0.1390, 0.1693, 0.2080, 0.2586, 0.3230, 0.4073, 0.5030,
0.6082, 0.7100, 0.7932, 0.8620, 0.9149, 0.9540, 0.9803, 0.9950, 1.0002,
0.9950, 0.9786, 0.9520, 0.9154, 0.8700, 0.8163, 0.7570, 0.6949, 0.6310,
0.5668, 0.5030, 0.4412, 0.3810, 0.3210, 0.2650, 0.2170, 0.1750, 0.1382,
0.1070, 0.0816, 0.0610, 0.0446, 0.0320, 0.0232, 0.0170, 0.0119, 0.0082,
0.0057, 0.0041, 0.0029, 0.0021, 0.0015, 0.0010, 0.0007, 0.0005, 0.0004,
0.0003, 0.0002, 0.0001, 0.0001, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000
];
zSamples:
ARRAY [0 .. 80]
OF
REAL ¬ [
0.0065, 0.0105, 0.0201, 0.0362, 0.0679, 0.1102, 0.2074, 0.3713, 0.6456,
1.0391, 1.3856, 1.6230, 1.7471, 1.7826, 1.7721, 1.7441, 1.6692, 1.5281,
1.2876, 1.0419, 0.8130, 0.6162, 0.4652, 0.3533, 0.2720, 0.2123, 0.1582,
0.1117, 0.0782, 0.0573, 0.0422, 0.0298, 0.0203, 0.0134, 0.0087, 0.0057,
0.0039, 0.0027, 0.0021, 0.0018, 0.0017, 0.0014, 0.0011, 0.0010, 0.0008,
0.0006, 0.0003, 0.0002, 0.0002, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000
];
xSequence ¬ NEW[RealSequenceRep[81]];
ySequence ¬ NEW[RealSequenceRep[81]];
zSequence ¬ NEW[RealSequenceRep[81]];
FOR i:
INT
IN [0 .. 81)
DO
xSequence[i] ¬ xSamples[i];
ySequence[i] ¬ ySamples[i];
zSequence[i] ¬ zSamples[i];
ENDLOOP;
xBar ¬ New[firstWavelength:380, deltaWavelength: 5, numSamples: 81, amplitudes: xSequence];
yBar ¬ New[firstWavelength:380, deltaWavelength: 5, numSamples: 81, amplitudes: ySequence];
zBar ¬ New[firstWavelength:380, deltaWavelength: 5, numSamples: 81, amplitudes: zSequence];
now set up for RGB->Spectrum conversions with the three necessary spectra
currentTransformMatrix ¬ ntscXYZtoRGBMatrix;
conSequence ¬ NEW[RealSequenceRep[81]];
sinSequence ¬ NEW[RealSequenceRep[81]];
cosSequence ¬ NEW[RealSequenceRep[81]];
FOR i:
INT
IN [0 .. 81)
DO
theta: REAL ¬ 6.283185*i/80.0;
conSequence[i] ¬ 1.0;
sinSequence[i] ¬ RealFns.Sin[theta];
cosSequence[i] ¬ RealFns.Cos[theta];
ENDLOOP;
conSpectrum ¬ New[firstWavelength:380, deltaWavelength: 5, numSamples: 81, amplitudes: conSequence];
sinSpectrum ¬ New[firstWavelength:380, deltaWavelength: 5, numSamples: 81, amplitudes: sinSequence];
cosSpectrum ¬ New[firstWavelength:380, deltaWavelength: 5, numSamples: 81, amplitudes: cosSequence];
conRGB ¬ SpectrumToRGB[conSpectrum];
sinRGB ¬ SpectrumToRGB[sinSpectrum];
cosRGB ¬ SpectrumToRGB[cosSpectrum];
weightsMat ¬ [
[conRGB.R, conRGB.G, conRGB.B],
[sinRGB.R, sinRGB.G, sinRGB.B],
[cosRGB.R, cosRGB.G, cosRGB.B]
];
weightsMat ¬ G2dMatrix.Invert[weightsMat];