1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 // appleseed.foundation headers.
31 #include "foundation/image/color.h"
32 #include "foundation/image/colorspace.h"
33 #include "foundation/image/regularspectrum.h"
34 #include "foundation/math/vector.h"
35 #include "foundation/utility/gnuplotfile.h"
36 #include "foundation/utility/iostreamop.h"
37 #include "foundation/utility/test.h"
38 
39 // Standard headers.
40 #include <cstddef>
41 #include <cstdio>
42 #include <vector>
43 
44 using namespace foundation;
45 using namespace std;
46 
TEST_SUITE(Foundation_Image_ColorSpace)47 TEST_SUITE(Foundation_Image_ColorSpace)
48 {
49     void resample_and_write(
50         FILE*           file,
51         const size_t    input_count,
52         const double    input_wavelength[],
53         const double    input_spectrum[],
54         const char*     variable_name)
55     {
56         static const double output_wavelength[31] =
57         {
58             400.0, 410.0, 420.0, 430.0, 440.0, 450.0, 460.0, 470.0, 480.0, 490.0,
59             500.0, 510.0, 520.0, 530.0, 540.0, 550.0, 560.0, 570.0, 580.0, 590.0,
60             600.0, 610.0, 620.0, 630.0, 640.0, 650.0, 660.0, 670.0, 680.0, 690.0,
61             700.0
62         };
63 
64         RegularSpectrum<double, 31> output_spectrum;
65         spectrum_to_spectrum(
66             input_count, input_wavelength, input_spectrum,
67             31, output_wavelength, &output_spectrum[0]);
68 
69         fprintf(file, "const float %s[31] =\n", variable_name);
70         fprintf(file, "{\n");
71 
72         for (int i = 0; i < 31; ++i)
73         {
74             const int wavelength = 400 + i * 10;
75             const double amplitude = output_spectrum[i];
76             fprintf(file, "    %s%ff%s         // %d nm\n",
77                 amplitude >= 0.0f ? " " : "",
78                 amplitude,
79                 i < 30 ? "," : " ",
80                 wavelength);
81         }
82 
83         fprintf(file, "};\n\n");
84     }
85 
86     TEST_CASE(ResamplePBRTBasisSpectra)
87     {
88         //
89         // Input basis spectra come from PBRT v3:
90         //
91         //   https://github.com/mmp/pbrt-v3/blob/master/src/core/spectrum.cpp
92         //
93 
94         const size_t SampleCount = 32;
95 
96         static const double RGB2SpectLambda[SampleCount] =
97         {
98             380.000000, 390.967743, 401.935486, 412.903229, 423.870972, 434.838715,
99             445.806458, 456.774200, 467.741943, 478.709686, 489.677429, 500.645172,
100             511.612915, 522.580627, 533.548340, 544.516052, 555.483765, 566.451477,
101             577.419189, 588.386902, 599.354614, 610.322327, 621.290039, 632.257751,
102             643.225464, 654.193176, 665.160889, 676.128601, 687.096313, 698.064026,
103             709.031738, 720.000000f
104         };
105 
106         static const double RGBRefl2SpectWhite[SampleCount] =
107         {
108             1.0618958571272863e+00, 1.0615019980348779e+00, 1.0614335379927147e+00,
109             1.0622711654692485e+00, 1.0622036218416742e+00, 1.0625059965187085e+00,
110             1.0623938486985884e+00, 1.0624706448043137e+00, 1.0625048144827762e+00,
111             1.0624366131308856e+00, 1.0620694238892607e+00, 1.0613167586932164e+00,
112             1.0610334029377020e+00, 1.0613868564828413e+00, 1.0614215366116762e+00,
113             1.0620336151299086e+00, 1.0625497454805051e+00, 1.0624317487992085e+00,
114             1.0625249140554480e+00, 1.0624277664486914e+00, 1.0624749854090769e+00,
115             1.0625538581025402e+00, 1.0625326910104864e+00, 1.0623922312225325e+00,
116             1.0623650980354129e+00, 1.0625256476715284e+00, 1.0612277619533155e+00,
117             1.0594262608698046e+00, 1.0599810758292072e+00, 1.0602547314449409e+00,
118             1.0601263046243634e+00, 1.0606565756823634e+00f};
119 
120         static const double RGBRefl2SpectCyan[SampleCount] =
121         {
122             1.0414628021426751e+00,  1.0328661533771188e+00,  1.0126146228964314e+00,
123             1.0350460524836209e+00,  1.0078661447098567e+00,  1.0422280385081280e+00,
124             1.0442596738499825e+00,  1.0535238290294409e+00,  1.0180776226938120e+00,
125             1.0442729908727713e+00,  1.0529362541920750e+00,  1.0537034271160244e+00,
126             1.0533901869215969e+00,  1.0537782700979574e+00,  1.0527093770467102e+00,
127             1.0530449040446797e+00,  1.0550554640191208e+00,  1.0553673610724821e+00,
128             1.0454306634683976e+00,  6.2348950639230805e-01,  1.8038071613188977e-01,
129             -7.6303759201984539e-03, -1.5217847035781367e-04, -7.5102257347258311e-03,
130             -2.1708639328491472e-03, 6.5919466602369636e-04,  1.2278815318539780e-02,
131             -4.4669775637208031e-03, 1.7119799082865147e-02,  4.9211089759759801e-03,
132             5.8762925143334985e-03,  2.5259399415550079e-02f
133         };
134 
135         static const double RGBRefl2SpectMagenta[SampleCount] =
136         {
137             9.9422138151236850e-01,  9.8986937122975682e-01, 9.8293658286116958e-01,
138             9.9627868399859310e-01,  1.0198955019000133e+00, 1.0166395501210359e+00,
139             1.0220913178757398e+00,  9.9651666040682441e-01, 1.0097766178917882e+00,
140             1.0215422470827016e+00,  6.4031953387790963e-01, 2.5012379477078184e-03,
141             6.5339939555769944e-03,  2.8334080462675826e-03, -5.1209675389074505e-11,
142             -9.0592291646646381e-03, 3.3936718323331200e-03, -3.0638741121828406e-03,
143             2.2203936168286292e-01,  6.3141140024811970e-01, 9.7480985576500956e-01,
144             9.7209562333590571e-01,  1.0173770302868150e+00, 9.9875194322734129e-01,
145             9.4701725739602238e-01,  8.5258623154354796e-01, 9.4897798581660842e-01,
146             9.4751876096521492e-01,  9.9598944191059791e-01, 8.6301351503809076e-01,
147             8.9150987853523145e-01,  8.4866492652845082e-01f
148         };
149 
150         static const double RGBRefl2SpectYellow[SampleCount] =
151         {
152             5.5740622924920873e-03,  -4.7982831631446787e-03, -5.2536564298613798e-03,
153             -6.4571480044499710e-03, -5.9693514658007013e-03, -2.1836716037686721e-03,
154             1.6781120601055327e-02,  9.6096355429062641e-02,  2.1217357081986446e-01,
155             3.6169133290685068e-01,  5.3961011543232529e-01,  7.4408810492171507e-01,
156             9.2209571148394054e-01,  1.0460304298411225e+00,  1.0513824989063714e+00,
157             1.0511991822135085e+00,  1.0510530911991052e+00,  1.0517397230360510e+00,
158             1.0516043086790485e+00,  1.0511944032061460e+00,  1.0511590325868068e+00,
159             1.0516612465483031e+00,  1.0514038526836869e+00,  1.0515941029228475e+00,
160             1.0511460436960840e+00,  1.0515123758830476e+00,  1.0508871369510702e+00,
161             1.0508923708102380e+00,  1.0477492815668303e+00,  1.0493272144017338e+00,
162             1.0435963333422726e+00,  1.0392280772051465e+00f
163         };
164 
165         static const double RGBRefl2SpectRed[SampleCount] =
166         {
167             1.6575604867086180e-01,  1.1846442802747797e-01,  1.2408293329637447e-01,
168             1.1371272058349924e-01,  7.8992434518899132e-02,  3.2205603593106549e-02,
169             -1.0798365407877875e-02, 1.8051975516730392e-02,  5.3407196598730527e-03,
170             1.3654918729501336e-02,  -5.9564213545642841e-03, -1.8444365067353252e-03,
171             -1.0571884361529504e-02, -2.9375521078000011e-03, -1.0790476271835936e-02,
172             -8.0224306697503633e-03, -2.2669167702495940e-03, 7.0200240494706634e-03,
173             -8.1528469000299308e-03, 6.0772866969252792e-01,  9.8831560865432400e-01,
174             9.9391691044078823e-01,  1.0039338994753197e+00,  9.9234499861167125e-01,
175             9.9926530858855522e-01,  1.0084621557617270e+00,  9.8358296827441216e-01,
176             1.0085023660099048e+00,  9.7451138326568698e-01,  9.8543269570059944e-01,
177             9.3495763980962043e-01,  9.8713907792319400e-01f
178         };
179 
180         static const double RGBRefl2SpectGreen[SampleCount] =
181         {
182             2.6494153587602255e-03,  -5.0175013429732242e-03, -1.2547236272489583e-02,
183             -9.4554964308388671e-03, -1.2526086181600525e-02, -7.9170697760437767e-03,
184             -7.9955735204175690e-03, -9.3559433444469070e-03, 6.5468611982999303e-02,
185             3.9572875517634137e-01,  7.5244022299886659e-01,  9.6376478690218559e-01,
186             9.9854433855162328e-01,  9.9992977025287921e-01,  9.9939086751140449e-01,
187             9.9994372267071396e-01,  9.9939121813418674e-01,  9.9911237310424483e-01,
188             9.6019584878271580e-01,  6.3186279338432438e-01,  2.5797401028763473e-01,
189             9.4014888527335638e-03,  -3.0798345608649747e-03, -4.5230367033685034e-03,
190             -6.8933410388274038e-03, -9.0352195539015398e-03, -8.5913667165340209e-03,
191             -8.3690869120289398e-03, -7.8685832338754313e-03, -8.3657578711085132e-06,
192             5.4301225442817177e-03,  -2.7745589759259194e-03f
193         };
194 
195         static const double RGBRefl2SpectBlue[SampleCount] =
196         {
197             9.9209771469720676e-01,  9.8876426059369127e-01,  9.9539040744505636e-01,
198             9.9529317353008218e-01,  9.9181447411633950e-01,  1.0002584039673432e+00,
199             9.9968478437342512e-01,  9.9988120766657174e-01,  9.8504012146370434e-01,
200             7.9029849053031276e-01,  5.6082198617463974e-01,  3.3133458513996528e-01,
201             1.3692410840839175e-01,  1.8914906559664151e-02,  -5.1129770932550889e-06,
202             -4.2395493167891873e-04, -4.1934593101534273e-04, 1.7473028136486615e-03,
203             3.7999160177631316e-03,  -5.5101474906588642e-04, -4.3716662898480967e-05,
204             7.5874501748732798e-03,  2.5795650780554021e-02,  3.8168376532500548e-02,
205             4.9489586408030833e-02,  4.9595992290102905e-02,  4.9814819505812249e-02,
206             3.9840911064978023e-02,  3.0501024937233868e-02,  2.1243054765241080e-02,
207             6.9596532104356399e-03,  4.1733649330980525e-03f
208         };
209 
210         static const double RGBIllum2SpectWhite[SampleCount] =
211         {
212             1.1565232050369776e+00, 1.1567225000119139e+00, 1.1566203150243823e+00,
213             1.1555782088080084e+00, 1.1562175509215700e+00, 1.1567674012207332e+00,
214             1.1568023194808630e+00, 1.1567677445485520e+00, 1.1563563182952830e+00,
215             1.1567054702510189e+00, 1.1565134139372772e+00, 1.1564336176499312e+00,
216             1.1568023181530034e+00, 1.1473147688514642e+00, 1.1339317140561065e+00,
217             1.1293876490671435e+00, 1.1290515328639648e+00, 1.0504864823782283e+00,
218             1.0459696042230884e+00, 9.9366687168595691e-01, 9.5601669265393940e-01,
219             9.2467482033511805e-01, 9.1499944702051761e-01, 8.9939467658453465e-01,
220             8.9542520751331112e-01, 8.8870566693814745e-01, 8.8222843814228114e-01,
221             8.7998311373826676e-01, 8.7635244612244578e-01, 8.8000368331709111e-01,
222             8.8065665428441120e-01, 8.8304706460276905e-01f
223         };
224 
225         static const double RGBIllum2SpectCyan[SampleCount] =
226         {
227             1.1334479663682135e+00,  1.1266762330194116e+00,  1.1346827504710164e+00,
228             1.1357395805744794e+00,  1.1356371830149636e+00,  1.1361152989346193e+00,
229             1.1362179057706772e+00,  1.1364819652587022e+00,  1.1355107110714324e+00,
230             1.1364060941199556e+00,  1.1360363621722465e+00,  1.1360122641141395e+00,
231             1.1354266882467030e+00,  1.1363099407179136e+00,  1.1355450412632506e+00,
232             1.1353732327376378e+00,  1.1349496420726002e+00,  1.1111113947168556e+00,
233             9.0598740429727143e-01,  6.1160780787465330e-01,  2.9539752170999634e-01,
234             9.5954200671150097e-02,  -1.1650792030826267e-02, -1.2144633073395025e-02,
235             -1.1148167569748318e-02, -1.1997606668458151e-02, -5.0506855475394852e-03,
236             -7.9982745819542154e-03, -9.4722817708236418e-03, -5.5329541006658815e-03,
237             -4.5428914028274488e-03, -1.2541015360921132e-02f
238         };
239 
240         static const double RGBIllum2SpectMagenta[SampleCount] =
241         {
242             1.0371892935878366e+00,  1.0587542891035364e+00,  1.0767271213688903e+00,
243             1.0762706844110288e+00,  1.0795289105258212e+00,  1.0743644742950074e+00,
244             1.0727028691194342e+00,  1.0732447452056488e+00,  1.0823760816041414e+00,
245             1.0840545681409282e+00,  9.5607567526306658e-01,  5.5197896855064665e-01,
246             8.4191094887247575e-02,  8.7940070557041006e-05,  -2.3086408335071251e-03,
247             -1.1248136628651192e-03, -7.7297612754989586e-11, -2.7270769006770834e-04,
248             1.4466473094035592e-02,  2.5883116027169478e-01,  5.2907999827566732e-01,
249             9.0966624097105164e-01,  1.0690571327307956e+00,  1.0887326064796272e+00,
250             1.0637622289511852e+00,  1.0201812918094260e+00,  1.0262196688979945e+00,
251             1.0783085560613190e+00,  9.8333849623218872e-01,  1.0707246342802621e+00,
252             1.0634247770423768e+00,  1.0150875475729566e+00f
253         };
254 
255         static const double RGBIllum2SpectYellow[SampleCount] =
256         {
257             2.7756958965811972e-03,  3.9673820990646612e-03,  -1.4606936788606750e-04,
258             3.6198394557748065e-04,  -2.5819258699309733e-04, -5.0133191628082274e-05,
259             -2.4437242866157116e-04, -7.8061419948038946e-05, 4.9690301207540921e-02,
260             4.8515973574763166e-01,  1.0295725854360589e+00,  1.0333210878457741e+00,
261             1.0368102644026933e+00,  1.0364884018886333e+00,  1.0365427939411784e+00,
262             1.0368595402854539e+00,  1.0365645405660555e+00,  1.0363938240707142e+00,
263             1.0367205578770746e+00,  1.0365239329446050e+00,  1.0361531226427443e+00,
264             1.0348785007827348e+00,  1.0042729660717318e+00,  8.4218486432354278e-01,
265             7.3759394894801567e-01,  6.5853154500294642e-01,  6.0531682444066282e-01,
266             5.9549794132420741e-01,  5.9419261278443136e-01,  5.6517682326634266e-01,
267             5.6061186014968556e-01,  5.8228610381018719e-01f
268         };
269 
270         static const double RGBIllum2SpectRed[SampleCount] =
271         {
272             5.4711187157291841e-02,  5.5609066498303397e-02,  6.0755873790918236e-02,
273             5.6232948615962369e-02,  4.6169940535708678e-02,  3.8012808167818095e-02,
274             2.4424225756670338e-02,  3.8983580581592181e-03,  -5.6082252172734437e-04,
275             9.6493871255194652e-04,  3.7341198051510371e-04,  -4.3367389093135200e-04,
276             -9.3533962256892034e-05, -1.2354967412842033e-04, -1.4524548081687461e-04,
277             -2.0047691915543731e-04, -4.9938587694693670e-04, 2.7255083540032476e-02,
278             1.6067405906297061e-01,  3.5069788873150953e-01,  5.7357465538418961e-01,
279             7.6392091890718949e-01,  8.9144466740381523e-01,  9.6394609909574891e-01,
280             9.8879464276016282e-01,  9.9897449966227203e-01,  9.8605140403564162e-01,
281             9.9532502805345202e-01,  9.7433478377305371e-01,  9.9134364616871407e-01,
282             9.8866287772174755e-01,  9.9713856089735531e-01f
283         };
284 
285         static const double RGBIllum2SpectGreen[SampleCount] =
286         {
287             2.5168388755514630e-02,  3.9427438169423720e-02,  6.2059571596425793e-03,
288             7.1120859807429554e-03,  2.1760044649139429e-04,  7.3271839984290210e-12,
289             -2.1623066217181700e-02, 1.5670209409407512e-02,  2.8019603188636222e-03,
290             3.2494773799897647e-01,  1.0164917292316602e+00,  1.0329476657890369e+00,
291             1.0321586962991549e+00,  1.0358667411948619e+00,  1.0151235476834941e+00,
292             1.0338076690093119e+00,  1.0371372378155013e+00,  1.0361377027692558e+00,
293             1.0229822432557210e+00,  9.6910327335652324e-01,  -5.1785923899878572e-03,
294             1.1131261971061429e-03,  6.6675503033011771e-03,  7.4024315686001957e-04,
295             2.1591567633473925e-02,  5.1481620056217231e-03,  1.4561928645728216e-03,
296             1.6414511045291513e-04,  -6.4630764968453287e-03, 1.0250854718507939e-02,
297             4.2387394733956134e-02,  2.1252716926861620e-02f
298         };
299 
300         static const double RGBIllum2SpectBlue[SampleCount] =
301         {
302             1.0570490759328752e+00,  1.0538466912851301e+00,  1.0550494258140670e+00,
303             1.0530407754701832e+00,  1.0579930596460185e+00,  1.0578439494812371e+00,
304             1.0583132387180239e+00,  1.0579712943137616e+00,  1.0561884233578465e+00,
305             1.0571399285426490e+00,  1.0425795187752152e+00,  3.2603084374056102e-01,
306             -1.9255628442412243e-03, -1.2959221137046478e-03, -1.4357356276938696e-03,
307             -1.2963697250337886e-03, -1.9227081162373899e-03, 1.2621152526221778e-03,
308             -1.6095249003578276e-03, -1.3029983817879568e-03, -1.7666600873954916e-03,
309             -1.2325281140280050e-03, 1.0316809673254932e-02,  3.1284512648354357e-02,
310             8.8773879881746481e-02,  1.3873621740236541e-01,  1.5535067531939065e-01,
311             1.4878477178237029e-01,  1.6624255403475907e-01,  1.6997613960634927e-01,
312             1.5769743995852967e-01,  1.9069090525482305e-01f
313         };
314 
315         FILE* file = fopen("unit tests/outputs/test_colorspace_basis_spectra.cpp", "wt");
316 
317         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBRefl2SpectWhite,    "RGBToSpectrumWhiteReflectanceTab");
318         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBRefl2SpectCyan,     "RGBToSpectrumCyanReflectanceTab");
319         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBRefl2SpectMagenta,  "RGBToSpectrumMagentaReflectanceTab");
320         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBRefl2SpectYellow,   "RGBToSpectrumYellowReflectanceTab");
321         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBRefl2SpectRed,      "RGBToSpectrumRedReflectanceTab");
322         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBRefl2SpectGreen,    "RGBToSpectrumGreenReflectanceTab");
323         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBRefl2SpectBlue,     "RGBToSpectrumBlueReflectanceTab");
324 
325         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBIllum2SpectWhite,   "RGBToSpectrumWhiteIlluminanceTab");
326         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBIllum2SpectCyan,    "RGBToSpectrumCyanIlluminanceTab");
327         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBIllum2SpectMagenta, "RGBToSpectrumMagentaIlluminanceTab");
328         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBIllum2SpectYellow,  "RGBToSpectrumYellowIlluminanceTab");
329         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBIllum2SpectRed,     "RGBToSpectrumRedIlluminanceTab");
330         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBIllum2SpectGreen,   "RGBToSpectrumGreenIlluminanceTab");
331         resample_and_write(file, SampleCount, RGB2SpectLambda, RGBIllum2SpectBlue,    "RGBToSpectrumBlueIlluminanceTab");
332 
333         fclose(file);
334     }
335 
336 #pragma warning (push)
337 #pragma warning (disable : 4723)    // potential division by 0
338 
339     TEST_CASE(TestHSVToLinearRGBConversion)
340     {
341         EXPECT_FEQ(Color3d(0.0, 0.0, 0.0), hsv_to_linear_rgb(Color3d(0.0, 0.0, 0.0)));
342         EXPECT_FEQ(Color3d(1.0, 0.0, 0.0), hsv_to_linear_rgb(Color3d(0.0, 1.0, 1.0)));
343         EXPECT_FEQ(Color3d(0.75, 0.75, 0.0), hsv_to_linear_rgb(Color3d(60.0, 1.0, 0.75)));
344         EXPECT_FEQ(Color3d(0.0, 0.5, 0.0), hsv_to_linear_rgb(Color3d(120.0, 1.0, 0.5)));
345         EXPECT_FEQ(Color3d(0.5, 0.5, 1.0), hsv_to_linear_rgb(Color3d(240.0, 0.5, 1.0)));
346         EXPECT_FEQ(Color3d(0.75, 0.25, 0.75), hsv_to_linear_rgb(Color3d(300.0, 2.0/3.0, 0.75)));
347     }
348 
349     TEST_CASE(TestLinearRGBToHSVConversion)
350     {
351         EXPECT_FEQ(Color3d(0.0, 0.0, 0.0), linear_rgb_to_hsv(Color3d(0.0, 0.0, 0.0)));
352         EXPECT_FEQ(Color3d(0.0, 1.0, 1.0), linear_rgb_to_hsv(Color3d(1.0, 0.0, 0.0)));
353         EXPECT_FEQ(Color3d(60.0, 1.0, 0.75), linear_rgb_to_hsv(Color3d(0.75, 0.75, 0.0)));
354         EXPECT_FEQ(Color3d(120.0, 1.0, 0.5), linear_rgb_to_hsv(Color3d(0.0, 0.5, 0.0)));
355         EXPECT_FEQ(Color3d(240.0, 0.5, 1.0), linear_rgb_to_hsv(Color3d(0.5, 0.5, 1.0)));
356         EXPECT_FEQ(Color3d(300.0, 2.0/3.0, 0.75), linear_rgb_to_hsv(Color3d(0.75, 0.25, 0.75)));
357     }
358 
359     TEST_CASE(TestHSLToLinearRGBConversion)
360     {
361         EXPECT_FEQ(Color3d(0.0, 0.0, 0.0), hsl_to_linear_rgb(Color3d(0.0, 0.0, 0.0)));
362         EXPECT_FEQ(Color3d(1.0, 0.0, 0.0), hsl_to_linear_rgb(Color3d(0.0, 1.0, 0.5)));
363         EXPECT_FEQ(Color3d(0.75, 0.75, 0.0), hsl_to_linear_rgb(Color3d(60.0, 1.0, 0.375)));
364         EXPECT_FEQ(Color3d(0.0, 0.5, 0.0), hsl_to_linear_rgb(Color3d(120.0, 1.0, 0.25)));
365         EXPECT_FEQ(Color3d(0.5, 0.5, 1.0), hsl_to_linear_rgb(Color3d(240.0, 1.0, 0.75)));
366         EXPECT_FEQ(Color3d(0.75, 0.25, 0.75), hsl_to_linear_rgb(Color3d(300.0, 0.5, 0.5)));
367     }
368 
369     TEST_CASE(TestLinearRGBToHSLConversion)
370     {
371         EXPECT_FEQ(Color3d(0.0, 0.0, 0.0), linear_rgb_to_hsl(Color3d(0.0, 0.0, 0.0)));
372         EXPECT_FEQ(Color3d(0.0, 1.0, 0.5), linear_rgb_to_hsl(Color3d(1.0, 0.0, 0.0)));
373         EXPECT_FEQ(Color3d(60.0, 1.0, 0.375), linear_rgb_to_hsl(Color3d(0.75, 0.75, 0.0)));
374         EXPECT_FEQ(Color3d(120.0, 1.0, 0.25), linear_rgb_to_hsl(Color3d(0.0, 0.5, 0.0)));
375         EXPECT_FEQ(Color3d(240.0, 1.0, 0.75), linear_rgb_to_hsl(Color3d(0.5, 0.5, 1.0)));
376         EXPECT_FEQ(Color3d(300.0, 0.5, 0.5), linear_rgb_to_hsl(Color3d(0.75, 0.25, 0.75)));
377     }
378 
379 #pragma warning (pop)
380 
381     TEST_CASE(TestCIEXYZToLinearRGBConversion)
382     {
383         const Color3d ciexyz(0.5, 0.7, 0.2);
384         const Color3d linear_rgb = ciexyz_to_linear_rgb(ciexyz);
385 
386         EXPECT_FEQ_EPS(
387             Color3d(0.44452747702598583, 0.83687689900398254, 0.096456110477447538),
388             linear_rgb,
389             1.0e-6);
390     }
391 
392     TEST_CASE(TestLinearRGBToCIEXYZConversion)
393     {
394         const Color3d linear_rgb(0.44452747702598583, 0.83687689900398254, 0.096456110477447538);
395         const Color3d ciexyz = linear_rgb_to_ciexyz(linear_rgb);
396 
397         EXPECT_FEQ_EPS(
398             Color3d(0.5, 0.7, 0.2),
399             ciexyz,
400             1.0e-5);
401     }
402 
403     TEST_CASE(TestCIEXYZToCIExyYConversion)
404     {
405         const Color3d ciexyz(0.5, 0.7, 0.2);
406         const Color3d ciexyy = ciexyz_to_ciexyy(ciexyz);
407 
408         EXPECT_FEQ_EPS(
409             Color3d(0.357143, 0.5, 0.7),
410             ciexyy,
411             1.0e-6);
412     }
413 
414     TEST_CASE(TestCIExyYToCIEXYZConversion)
415     {
416         const Color3d ciexyy(0.357143, 0.5, 0.7);
417         const Color3d ciexyz = ciexyy_to_ciexyz(ciexyy);
418 
419         EXPECT_FEQ_EPS(
420             Color3d(0.5, 0.7, 0.2),
421             ciexyz,
422             1.0e-5);
423     }
424 
425     TEST_CASE(TestLinearRGBTosRGBConversion)
426     {
427         const Color3d linear_rgb(0.5, 0.7, 0.2);
428         const Color3d srgb = linear_rgb_to_srgb(linear_rgb);
429 
430         EXPECT_FEQ_EPS(
431             Color3d(0.73535698305244945, 0.85430583154494000, 0.48452920448170694),
432             srgb,
433             1.0e-6);
434     }
435 
436     TEST_CASE(TestsRGBToLinearRGBConversion)
437     {
438         const Color3d srgb(0.73535698305244945, 0.85430583154494000, 0.48452920448170694);
439         const Color3d linear_rgb = srgb_to_linear_rgb(srgb);
440 
441         EXPECT_FEQ_EPS(
442             Color3d(0.5, 0.7, 0.2),
443             linear_rgb,
444             1.0e-6);
445     }
446 
447     TEST_CASE(TestFastLinearRGBTosRGBConversion)
448     {
449         const Color3f linear_rgb(0.5f, 0.7f, 0.2f);
450         const Color3f srgb = fast_linear_rgb_to_srgb(linear_rgb);
451 
452         EXPECT_FEQ_EPS(
453             Color3f(0.735361f, 0.854277f, 0.484509f),
454             srgb,
455             1.0e-5f);
456     }
457 
458     TEST_CASE(TestFastsRGBToLinearRGBConversion)
459     {
460         const Color3f srgb(0.73535698f, 0.85430583f, 0.48452920f);
461         const Color3f linear_rgb = fast_srgb_to_linear_rgb(srgb);
462 
463         EXPECT_FEQ_EPS(
464             Color3f(0.499996f, 0.69986f, 0.199976f),
465             linear_rgb,
466             1.0e-5f);
467     }
468 
469     static RegularSpectrum31f get_white_spectrum()
470     {
471         // The white color from the Cornell Box scene.
472         static const float Values[31] =
473         {
474             0.343000f, 0.591563f, 0.687000f, 0.720250f, 0.745000f, 0.751188f, 0.765000f, 0.747312f,
475             0.745000f, 0.751500f, 0.747000f, 0.735625f, 0.725000f, 0.729312f, 0.743000f, 0.739000f,
476             0.733000f, 0.724813f, 0.764000f, 0.733563f, 0.740000f, 0.751063f, 0.744000f, 0.739438f,
477             0.712000f, 0.731000f, 0.707000f, 0.740500f, 0.751000f, 0.725438f, 0.737000f
478         };
479         return RegularSpectrum31f::from_array(Values);
480     }
481 
482     TEST_CASE(TestSpectrumToCIEXYZConversion)
483     {
484         const RegularSpectrum31f spectrum = get_white_spectrum();
485         const LightingConditions lighting_conditions(IlluminantCIED65, XYZCMFCIE19312Deg);
486         const Color3f ciexyz = spectrum_to_ciexyz<float>(lighting_conditions, spectrum);
487 
488         EXPECT_FEQ_EPS(
489             Color3f(0.701480f, 0.73824f, 0.804104f),
490             ciexyz,
491             1.0e-6f);
492     }
493 
494     TEST_CASE(TestCIEXYZReflectanceToSpectrumConversion)
495     {
496         const Color3f ciexyz(0.699385f, 0.738633f, 0.790319f);
497 
498         RegularSpectrum31f spectrum;
499         ciexyz_reflectance_to_spectrum(ciexyz, spectrum);
500 
501         const float ExpectedSpectrumValues[31] =
502         {
503             0.768140f, 0.768643f, 0.768740f, 0.768869f, 0.769037f, 0.769489f, 0.770708f, 0.772643f,
504             0.775606f, 0.778692f, 0.781363f, 0.783471f, 0.785352f, 0.785766f, 0.786007f, 0.786419f,
505             0.786571f, 0.786511f, 0.786198f, 0.784935f, 0.783728f, 0.782956f, 0.782881f, 0.782790f,
506             0.782736f, 0.782879f, 0.782436f, 0.781264f, 0.780612f, 0.781049f, 0.781204f
507         };
508 
509         EXPECT_FEQ_EPS(
510             RegularSpectrum31f::from_array(ExpectedSpectrumValues),
511             spectrum,
512             1.0e-6f);
513     }
514 
515     TEST_CASE(TestSpectrumToSpectrumConversion)
516     {
517         static const float InputWavelength[RegularSpectrum31f::Samples] =
518         {
519             400.0f, 410.0f, 420.0f, 430.0f, 440.0f, 450.0f, 460.0f, 470.0f, 480.0f, 490.0f,
520             500.0f, 510.0f, 520.0f, 530.0f, 540.0f, 550.0f, 560.0f, 570.0f, 580.0f, 590.0f,
521             600.0f, 610.0f, 620.0f, 630.0f, 640.0f, 650.0f, 660.0f, 670.0f, 680.0f, 690.0f,
522             700.0f
523         };
524         const RegularSpectrum31f input_spectrum = get_white_spectrum();
525 
526         typedef RegularSpectrum<float, 4> RegularSpectrum4f;
527 
528         static const float OutputWavelength[RegularSpectrum4f::Samples] =
529         {
530             400.0f, 500.0f, 600.0f, 700.0f
531         };
532 
533         RegularSpectrum4f output_spectrum;
534         spectrum_to_spectrum(
535             input_spectrum.Samples,
536             InputWavelength,
537             &input_spectrum[0],
538             output_spectrum.Samples,
539             OutputWavelength,
540             &output_spectrum[0]);
541 
542         const float ExpectedOutputSpectrumValues[4] =
543         {
544             0.343f, 0.747f, 0.74f, 0.737f
545         };
546 
547         EXPECT_FEQ_EPS(
548             RegularSpectrum4f::from_array(ExpectedOutputSpectrumValues),
549             output_spectrum,
550             1.0e-6f);
551     }
552 
553     vector<Vector2d> zip(const double x[], const double y[], const size_t count)
554     {
555         vector<Vector2d> points(count);
556 
557         for (size_t i = 0; i < count; ++i)
558         {
559             points[i].x = x[i];
560             points[i].y = y[i];
561         }
562 
563         return points;
564     }
565 
566     TEST_CASE(GeneratePlotFilesToVisualizeSpectrumResampling)
567     {
568         // Input wavelengths for white, green and red spectra.
569         static const size_t InputSpectrumCount = 76;
570         static const double InputSpectrumWavelength[InputSpectrumCount] =
571         {
572             400.0, 404.0, 408.0, 412.0, 416.0, 420.0, 424.0, 428.0, 432.0, 436.0,
573             440.0, 444.0, 448.0, 452.0, 456.0, 460.0, 464.0, 468.0, 472.0, 476.0,
574             480.0, 484.0, 488.0, 492.0, 496.0, 500.0, 504.0, 508.0, 512.0, 516.0,
575             520.0, 524.0, 528.0, 532.0, 536.0, 540.0, 544.0, 548.0, 552.0, 556.0,
576             560.0, 564.0, 568.0, 572.0, 576.0, 580.0, 584.0, 588.0, 592.0, 596.0,
577             600.0, 604.0, 608.0, 612.0, 616.0, 620.0, 624.0, 628.0, 632.0, 636.0,
578             640.0, 644.0, 648.0, 652.0, 656.0, 660.0, 664.0, 668.0, 672.0, 676.0,
579             680.0, 684.0, 688.0, 692.0, 696.0, 700.0
580         };
581 
582         // Input white spectrum.
583         static const double InputWhiteSpectrum[InputSpectrumCount] =
584         {
585             0.343, 0.445, 0.551, 0.624, 0.665, 0.687, 0.708, 0.723, 0.715, 0.710,
586             0.745, 0.758, 0.739, 0.767, 0.777, 0.765, 0.751, 0.745, 0.748, 0.729,
587             0.745, 0.757, 0.753, 0.750, 0.746, 0.747, 0.735, 0.732, 0.739, 0.734,
588             0.725, 0.721, 0.733, 0.725, 0.732, 0.743, 0.744, 0.748, 0.728, 0.716,
589             0.733, 0.726, 0.713, 0.740, 0.754, 0.764, 0.752, 0.736, 0.734, 0.741,
590             0.740, 0.732, 0.745, 0.755, 0.751, 0.744, 0.731, 0.733, 0.744, 0.731,
591             0.712, 0.708, 0.729, 0.730, 0.727, 0.707, 0.703, 0.729, 0.750, 0.760,
592             0.751, 0.739, 0.724, 0.730, 0.740, 0.737
593         };
594 
595         // Input green spectrum.
596         static const double InputGreenSpectrum[InputSpectrumCount] =
597         {
598             0.092, 0.096, 0.098, 0.097, 0.098, 0.095, 0.095, 0.097, 0.095, 0.094,
599             0.097, 0.098, 0.096, 0.101, 0.103, 0.104, 0.107, 0.109, 0.112, 0.115,
600             0.125, 0.140, 0.160, 0.187, 0.229, 0.285, 0.343, 0.390, 0.435, 0.464,
601             0.472, 0.476, 0.481, 0.462, 0.447, 0.441, 0.426, 0.406, 0.373, 0.347,
602             0.337, 0.314, 0.285, 0.277, 0.266, 0.250, 0.230, 0.207, 0.186, 0.171,
603             0.160, 0.148, 0.141, 0.136, 0.130, 0.126, 0.123, 0.121, 0.122, 0.119,
604             0.114, 0.115, 0.117, 0.117, 0.118, 0.120, 0.122, 0.128, 0.132, 0.139,
605             0.144, 0.146, 0.150, 0.152, 0.157, 0.159
606         };
607 
608         // Input red spectrum.
609         static const double InputRedSpectrum[InputSpectrumCount] =
610         {
611             0.040, 0.046, 0.048, 0.053, 0.049, 0.050, 0.053, 0.055, 0.057, 0.056,
612             0.059, 0.057, 0.061, 0.061, 0.060, 0.062, 0.062, 0.062, 0.061, 0.062,
613             0.060, 0.059, 0.057, 0.058, 0.058, 0.058, 0.056, 0.055, 0.056, 0.059,
614             0.057, 0.055, 0.059, 0.059, 0.058, 0.059, 0.061, 0.061, 0.063, 0.063,
615             0.067, 0.068, 0.072, 0.080, 0.090, 0.099, 0.124, 0.154, 0.192, 0.255,
616             0.287, 0.349, 0.402, 0.443, 0.487, 0.513, 0.558, 0.584, 0.620, 0.606,
617             0.609, 0.651, 0.612, 0.610, 0.650, 0.638, 0.627, 0.620, 0.630, 0.628,
618             0.642, 0.639, 0.657, 0.639, 0.635, 0.642
619         };
620 
621         // Input wavelengths for camera response.
622         static const size_t InputCameraResponseCount = 73;
623         static const double InputCameraResponseWavelength[InputCameraResponseCount] =
624         {
625              380.0,  390.0,  400.0,  410.0,  420.0,  430.0,  440.0,  450.0,  460.0,  470.0,
626              480.0,  490.0,  500.0,  510.0,  520.0,  530.0,  540.0,  550.0,  560.0,  570.0,
627              580.0,  590.0,  600.0,  610.0,  620.0,  630.0,  640.0,  650.0,  660.0,  670.0,
628              680.0,  690.0,  700.0,  710.0,  720.0,  730.0,  740.0,  750.0,  760.0,  770.0,
629              780.0,  790.0,  800.0,  810.0,  820.0,  830.0,  840.0,  850.0,  860.0,  870.0,
630              880.0,  890.0,  900.0,  910.0,  920.0,  930.0,  940.0,  950.0,  960.0,  970.0,
631              980.0,  990.0, 1000.0, 1010.0, 1020.0, 1030.0, 1040.0, 1050.0, 1060.0, 1070.0,
632             1080.0, 1090.0, 1100.0
633         };
634 
635         // Input camera response.
636         static const double InputCameraResponse[InputCameraResponseCount] =
637         {
638              0.0000,  0.0000,  2.7262,  4.5846,  6.9099,  9.3752, 12.3971, 14.6141, 16.4340, 14.8158,
639             15.3257, 19.6840, 24.0263, 26.7845, 29.5855, 36.3664, 42.6439, 44.6308, 46.7100, 47.7365,
640             44.6948, 39.7790, 36.3939, 34.7148, 36.2459, 40.2827, 41.8437, 40.1703, 38.5798, 37.8546,
641             37.8727, 38.9440, 41.7185, 45.9209, 49.2163, 50.2404, 49.3917, 48.2287, 47.0943, 46.2036,
642             45.9869, 46.4835, 47.7591, 49.4473, 50.4596, 50.1918, 48.7579, 46.8426, 44.4647, 42.0801,
643             39.8541, 37.5268, 34.9810, 32.3554, 29.2819, 26.0035, 22.7539, 19.4374, 16.8918, 13.7415,
644             11.3124,  9.3019,  7.4702,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,  0.0000,
645              0.0000,  0.0000,  0.0000
646         };
647 
648         // Input wavelengths for light source emission spectrum.
649         static const size_t InputSourceSpectrumCount = 1024;
650         static const double InputSourceSpectrumWavelength[InputSourceSpectrumCount] =
651         {
652             386.0000, 386.3176, 386.6352, 386.9528, 387.2704, 387.5880, 387.9056, 388.2232, 388.5408, 388.8584,
653             389.1760, 389.4936, 389.8112, 390.1288, 390.4464, 390.7640, 391.0816, 391.3992, 391.7168, 392.0344,
654             392.3520, 392.6696, 392.9872, 393.3048, 393.6224, 393.9400, 394.2576, 394.5752, 394.8928, 395.2104,
655             395.5280, 395.8456, 396.1632, 396.4808, 396.7984, 397.1160, 397.4336, 397.7512, 398.0688, 398.3864,
656             398.7040, 399.0216, 399.3392, 399.6568, 399.9744, 400.2920, 400.6096, 400.9272, 401.2448, 401.5624,
657             401.8800, 402.1976, 402.5152, 402.8328, 403.1504, 403.4680, 403.7856, 404.1032, 404.4208, 404.7384,
658             405.0560, 405.3736, 405.6912, 406.0088, 406.3264, 406.6440, 406.9616, 407.2792, 407.5968, 407.9144,
659             408.2320, 408.5496, 408.8672, 409.1848, 409.5024, 409.8200, 410.1376, 410.4552, 410.7728, 411.0904,
660             411.4080, 411.7256, 412.0432, 412.3608, 412.6784, 412.9960, 413.3136, 413.6312, 413.9488, 414.2664,
661             414.5840, 414.9016, 415.2192, 415.5368, 415.8544, 416.1720, 416.4896, 416.8072, 417.1248, 417.4424,
662             417.7600, 418.0776, 418.3952, 418.7128, 419.0304, 419.3480, 419.6656, 419.9832, 420.3008, 420.6184,
663             420.9360, 421.2536, 421.5712, 421.8888, 422.2064, 422.5240, 422.8416, 423.1592, 423.4768, 423.7944,
664             424.1120, 424.4296, 424.7472, 425.0648, 425.3824, 425.7000, 426.0176, 426.3352, 426.6528, 426.9704,
665             427.2880, 427.6056, 427.9232, 428.2408, 428.5584, 428.8760, 429.1936, 429.5112, 429.8288, 430.1464,
666             430.4640, 430.7816, 431.0992, 431.4168, 431.7344, 432.0520, 432.3696, 432.6872, 433.0048, 433.3224,
667             433.6400, 433.9576, 434.2752, 434.5928, 434.9104, 435.2280, 435.5456, 435.8632, 436.1808, 436.4984,
668             436.8160, 437.1336, 437.4512, 437.7688, 438.0864, 438.4040, 438.7216, 439.0392, 439.3568, 439.6744,
669             439.9920, 440.3096, 440.6272, 440.9448, 441.2624, 441.5800, 441.8976, 442.2152, 442.5328, 442.8504,
670             443.1680, 443.4856, 443.8032, 444.1208, 444.4384, 444.7560, 445.0736, 445.3912, 445.7088, 446.0264,
671             446.3440, 446.6616, 446.9792, 447.2968, 447.6144, 447.9320, 448.2496, 448.5672, 448.8848, 449.2024,
672             449.5200, 449.8376, 450.1552, 450.4728, 450.7904, 451.1080, 451.4256, 451.7432, 452.0608, 452.3784,
673             452.6960, 453.0136, 453.3312, 453.6488, 453.9664, 454.2840, 454.6016, 454.9192, 455.2368, 455.5544,
674             455.8720, 456.1896, 456.5072, 456.8248, 457.1424, 457.4600, 457.7776, 458.0952, 458.4128, 458.7304,
675             459.0480, 459.3656, 459.6832, 460.0008, 460.3184, 460.6360, 460.9536, 461.2712, 461.5888, 461.9064,
676             462.2240, 462.5416, 462.8592, 463.1768, 463.4944, 463.8120, 464.1296, 464.4472, 464.7648, 465.0824,
677             465.4000, 465.7176, 466.0352, 466.3528, 466.6704, 466.9880, 467.3056, 467.6232, 467.9408, 468.2584,
678             468.5760, 468.8936, 469.2112, 469.5288, 469.8464, 470.1640, 470.4816, 470.7992, 471.1168, 471.4344,
679             471.7520, 472.0696, 472.3872, 472.7048, 473.0224, 473.3400, 473.6576, 473.9752, 474.2928, 474.6104,
680             474.9280, 475.2456, 475.5632, 475.8808, 476.1984, 476.5160, 476.8336, 477.1512, 477.4688, 477.7864,
681             478.1040, 478.4216, 478.7392, 479.0568, 479.3744, 479.6920, 480.0096, 480.3272, 480.6448, 480.9624,
682             481.2800, 481.5976, 481.9152, 482.2328, 482.5504, 482.8680, 483.1856, 483.5032, 483.8208, 484.1384,
683             484.4560, 484.7736, 485.0912, 485.4088, 485.7264, 486.0440, 486.3616, 486.6792, 486.9968, 487.3144,
684             487.6320, 487.9496, 488.2672, 488.5848, 488.9024, 489.2200, 489.5376, 489.8552, 490.1728, 490.4904,
685             490.8080, 491.1256, 491.4432, 491.7608, 492.0784, 492.3960, 492.7136, 493.0312, 493.3488, 493.6664,
686             493.9840, 494.3016, 494.6192, 494.9368, 495.2544, 495.5720, 495.8896, 496.2072, 496.5248, 496.8424,
687             497.1600, 497.4776, 497.7952, 498.1128, 498.4304, 498.7480, 499.0656, 499.3832, 499.7008, 500.0184,
688             500.3360, 500.6536, 500.9712, 501.2888, 501.6064, 501.9240, 502.2416, 502.5592, 502.8768, 503.1944,
689             503.5120, 503.8296, 504.1472, 504.4648, 504.7824, 505.1000, 505.4176, 505.7352, 506.0528, 506.3704,
690             506.6880, 507.0056, 507.3232, 507.6408, 507.9584, 508.2760, 508.5936, 508.9112, 509.2288, 509.5464,
691             509.8640, 510.1816, 510.4992, 510.8168, 511.1344, 511.4520, 511.7696, 512.0872, 512.4048, 512.7224,
692             513.0400, 513.3576, 513.6752, 513.9928, 514.3104, 514.6280, 514.9456, 515.2632, 515.5808, 515.8984,
693             516.2160, 516.5336, 516.8512, 517.1688, 517.4864, 517.8040, 518.1216, 518.4392, 518.7568, 519.0744,
694             519.3920, 519.7096, 520.0272, 520.3448, 520.6624, 520.9800, 521.2976, 521.6152, 521.9328, 522.2504,
695             522.5680, 522.8856, 523.2032, 523.5208, 523.8384, 524.1560, 524.4736, 524.7912, 525.1088, 525.4264,
696             525.7440, 526.0616, 526.3792, 526.6968, 527.0144, 527.3320, 527.6496, 527.9672, 528.2848, 528.6024,
697             528.9200, 529.2376, 529.5552, 529.8728, 530.1904, 530.5080, 530.8256, 531.1432, 531.4608, 531.7784,
698             532.0960, 532.4136, 532.7312, 533.0488, 533.3664, 533.6840, 534.0016, 534.3192, 534.6368, 534.9544,
699             535.2720, 535.5896, 535.9072, 536.2248, 536.5424, 536.8600, 537.1776, 537.4952, 537.8128, 538.1304,
700             538.4480, 538.7656, 539.0832, 539.4008, 539.7184, 540.0360, 540.3536, 540.6712, 540.9888, 541.3064,
701             541.6240, 541.9416, 542.2592, 542.5768, 542.8944, 543.2120, 543.5296, 543.8472, 544.1648, 544.4824,
702             544.8000, 545.1176, 545.4352, 545.7528, 546.0704, 546.3880, 546.7056, 547.0232, 547.3408, 547.6584,
703             547.9760, 548.2936, 548.6112, 548.9288, 549.2464, 549.5640, 549.8816, 550.1992, 550.5168, 550.8344,
704             551.1520, 551.4696, 551.7872, 552.1048, 552.4224, 552.7400, 553.0576, 553.3752, 553.6928, 554.0104,
705             554.3280, 554.6456, 554.9632, 555.2808, 555.5984, 555.9160, 556.2336, 556.5512, 556.8688, 557.1864,
706             557.5040, 557.8216, 558.1392, 558.4568, 558.7744, 559.0920, 559.4096, 559.7272, 560.0448, 560.3624,
707             560.6800, 560.9976, 561.3152, 561.6328, 561.9504, 562.2680, 562.5856, 562.9032, 563.2208, 563.5384,
708             563.8560, 564.1736, 564.4912, 564.8088, 565.1264, 565.4440, 565.7616, 566.0792, 566.3968, 566.7144,
709             567.0320, 567.3496, 567.6672, 567.9848, 568.3024, 568.6200, 568.9376, 569.2552, 569.5728, 569.8904,
710             570.2080, 570.5256, 570.8432, 571.1608, 571.4784, 571.7960, 572.1136, 572.4312, 572.7488, 573.0664,
711             573.3840, 573.7016, 574.0192, 574.3368, 574.6544, 574.9720, 575.2896, 575.6072, 575.9248, 576.2424,
712             576.5600, 576.8776, 577.1952, 577.5128, 577.8304, 578.1480, 578.4656, 578.7832, 579.1008, 579.4184,
713             579.7360, 580.0536, 580.3712, 580.6888, 581.0064, 581.3240, 581.6416, 581.9592, 582.2768, 582.5944,
714             582.9120, 583.2296, 583.5472, 583.8648, 584.1824, 584.5000, 584.8176, 585.1352, 585.4528, 585.7704,
715             586.0880, 586.4056, 586.7232, 587.0408, 587.3584, 587.6760, 587.9936, 588.3112, 588.6288, 588.9464,
716             589.2640, 589.5816, 589.8992, 590.2168, 590.5344, 590.8520, 591.1696, 591.4872, 591.8048, 592.1224,
717             592.4400, 592.7576, 593.0752, 593.3928, 593.7104, 594.0280, 594.3456, 594.6632, 594.9808, 595.2984,
718             595.6160, 595.9336, 596.2512, 596.5688, 596.8864, 597.2040, 597.5216, 597.8392, 598.1568, 598.4744,
719             598.7920, 599.1096, 599.4272, 599.7448, 600.0624, 600.3800, 600.6976, 601.0152, 601.3328, 601.6504,
720             601.9680, 602.2856, 602.6032, 602.9208, 603.2384, 603.5560, 603.8736, 604.1912, 604.5088, 604.8264,
721             605.1440, 605.4616, 605.7792, 606.0968, 606.4144, 606.7320, 607.0496, 607.3672, 607.6848, 608.0024,
722             608.3200, 608.6376, 608.9552, 609.2728, 609.5904, 609.9080, 610.2256, 610.5432, 610.8608, 611.1784,
723             611.4960, 611.8136, 612.1312, 612.4488, 612.7664, 613.0840, 613.4016, 613.7192, 614.0368, 614.3544,
724             614.6720, 614.9896, 615.3072, 615.6248, 615.9424, 616.2600, 616.5776, 616.8952, 617.2128, 617.5304,
725             617.8480, 618.1656, 618.4832, 618.8008, 619.1184, 619.4360, 619.7536, 620.0712, 620.3888, 620.7064,
726             621.0240, 621.3416, 621.6592, 621.9768, 622.2944, 622.6120, 622.9296, 623.2472, 623.5648, 623.8824,
727             624.2000, 624.5176, 624.8352, 625.1528, 625.4704, 625.7880, 626.1056, 626.4232, 626.7408, 627.0584,
728             627.3760, 627.6936, 628.0112, 628.3288, 628.6464, 628.9640, 629.2816, 629.5992, 629.9168, 630.2344,
729             630.5520, 630.8696, 631.1872, 631.5048, 631.8224, 632.1400, 632.4576, 632.7752, 633.0928, 633.4104,
730             633.7280, 634.0456, 634.3632, 634.6808, 634.9984, 635.3160, 635.6336, 635.9512, 636.2688, 636.5864,
731             636.9040, 637.2216, 637.5392, 637.8568, 638.1744, 638.4920, 638.8096, 639.1272, 639.4448, 639.7624,
732             640.0800, 640.3976, 640.7152, 641.0328, 641.3504, 641.6680, 641.9856, 642.3032, 642.6208, 642.9384,
733             643.2560, 643.5736, 643.8912, 644.2088, 644.5264, 644.8440, 645.1616, 645.4792, 645.7968, 646.1144,
734             646.4320, 646.7496, 647.0672, 647.3848, 647.7024, 648.0200, 648.3376, 648.6552, 648.9728, 649.2904,
735             649.6080, 649.9256, 650.2432, 650.5608, 650.8784, 651.1960, 651.5136, 651.8312, 652.1488, 652.4664,
736             652.7840, 653.1016, 653.4192, 653.7368, 654.0544, 654.3720, 654.6896, 655.0072, 655.3248, 655.6424,
737             655.9600, 656.2776, 656.5952, 656.9128, 657.2304, 657.5480, 657.8656, 658.1832, 658.5008, 658.8184,
738             659.1360, 659.4536, 659.7712, 660.0888, 660.4064, 660.7240, 661.0416, 661.3592, 661.6768, 661.9944,
739             662.3120, 662.6296, 662.9472, 663.2648, 663.5824, 663.9000, 664.2176, 664.5352, 664.8528, 665.1704,
740             665.4880, 665.8056, 666.1232, 666.4408, 666.7584, 667.0760, 667.3936, 667.7112, 668.0288, 668.3464,
741             668.6640, 668.9816, 669.2992, 669.6168, 669.9344, 670.2520, 670.5696, 670.8872, 671.2048, 671.5224,
742             671.8400, 672.1576, 672.4752, 672.7928, 673.1104, 673.4280, 673.7456, 674.0632, 674.3808, 674.6984,
743             675.0160, 675.3336, 675.6512, 675.9688, 676.2864, 676.6040, 676.9216, 677.2392, 677.5568, 677.8744,
744             678.1920, 678.5096, 678.8272, 679.1448, 679.4624, 679.7800, 680.0976, 680.4152, 680.7328, 681.0504,
745             681.3680, 681.6856, 682.0032, 682.3208, 682.6384, 682.9560, 683.2736, 683.5912, 683.9088, 684.2264,
746             684.5440, 684.8616, 685.1792, 685.4968, 685.8144, 686.1320, 686.4496, 686.7672, 687.0848, 687.4024,
747             687.7200, 688.0376, 688.3552, 688.6728, 688.9904, 689.3080, 689.6256, 689.9432, 690.2608, 690.5784,
748             690.8960, 691.2136, 691.5312, 691.8488, 692.1664, 692.4840, 692.8016, 693.1192, 693.4368, 693.7544,
749             694.0720, 694.3896, 694.7072, 695.0248, 695.3424, 695.6600, 695.9776, 696.2952, 696.6128, 696.9304,
750             697.2480, 697.5656, 697.8832, 698.2008, 698.5184, 698.8360, 699.1536, 699.4712, 699.7888, 700.1064,
751             700.4240, 700.7416, 701.0592, 701.3768, 701.6944, 702.0120, 702.3296, 702.6472, 702.9648, 703.2824,
752             703.6000, 703.9176, 704.2352, 704.5528, 704.8704, 705.1880, 705.5056, 705.8232, 706.1408, 706.4584,
753             706.7760, 707.0936, 707.4112, 707.7288, 708.0464, 708.3640, 708.6816, 708.9992, 709.3168, 709.6344,
754             709.9520, 710.2696, 710.5872, 710.9048
755         };
756 
757         // Input light source emission spectrum.
758         static const double InputSourceSpectrum[InputSourceSpectrumCount] =
759         {
760              4.92835727367249,  4.95023640271299,  4.87167292742142,  4.85039921142434,  4.85475580394960,
761              4.80956281547945,  4.77826218004708,  4.75116142146560,  4.69755889037721,  4.66649574802206,
762              4.65519461168247,  4.63443441820992,  4.61113952310921,  4.58537629312006,  4.50554345458709,
763              4.46331279517320,  4.45395241720818,  4.46502019801438,  4.41766996445181,  4.39838089299169,
764              4.40903414622371,  4.31906190749876,  4.29160301757074,  4.23493045638940,  4.21922691342780,
765              4.19496304110966,  4.14768160859809,  4.12731779396439,  4.09533142677633,  4.05208115732076,
766              4.00638023897947,  3.99806185170084,  3.94598963601655,  3.90530572029335,  3.89192331433258,
767              3.84189391558862,  3.80734009619861,  3.77057298996717,  3.76079115433504,  3.68718653034558,
768              3.67040429194027,  3.64547231303425,  3.60396275237111,  3.60246259721929,  3.59170544437162,
769              3.57765441323359,  3.57533983749332,  3.54385920997010,  3.55140379578212,  3.53685074292438,
770              3.54053110397631,  3.53452475146693,  3.54617625024226,  3.54812405490011,  3.54015626575630,
771              3.58280680228990,  3.62491123369037,  3.60758575570167,  3.61251344503421,  3.65544931879874,
772              3.69239884097020,  3.70221932176488,  3.73093703106271,  3.75150734877093,  3.76142521183794,
773              3.79115494300296,  3.78858795794808,  3.81544892870445,  3.79755015063810,  3.80348125873695,
774              3.81406552960722,  3.81668466357248,  3.82162623431797,  3.82318482904885,  3.82395402740146,
775              3.82329460940511,  3.81952184556189,  3.83521351421094,  3.83840922786668,  3.85216398538485,
776              3.86120934836836,  3.88787011066720,  3.90896078800000,  3.94768945105165,  3.97612358846632,
777              4.00194611026509,  4.04595967066901,  4.09217344592315,  4.12217906019002,  4.15048702534741,
778              4.18975942673645,  4.21281307693181,  4.26033120454497,  4.29281746003645,  4.33297969724406,
779              4.35829327325444,  4.38503818988945,  4.42539408537505,  4.43887952065373,  4.46264602928912,
780              4.48390515934975,  4.52602185610096,  4.53983730165631,  4.56657194479674,  4.59490684532894,
781              4.62791974589427,  4.65670045417244,  4.69110222730295,  4.72254958703293,  4.74050425047271,
782              4.76876667713555,  4.80888814368501,  4.82379462706667,  4.84422629371079,  4.86289906101570,
783              4.87858012652333,  4.89095248251457,  4.90415848219590,  4.91457056232708,  4.93948424433779,
784              4.95677458982607,  4.97895380445710,  5.01438735571817,  5.03957210674465,  5.05976462462309,
785              5.08762297092039,  5.12633430403273,  5.15713232731281,  5.21046498310257,  5.25101603039045,
786              5.30316550466633,  5.36160479302598,  5.40998542228681,  5.47363071792891,  5.52623376913842,
787              5.57903298031617,  5.62451523998544,  5.67368076570148,  5.72678803342444,  5.77418930759581,
788              5.82062094735516,  5.85697833762068,  5.89874525184282,  5.91979898588009,  5.93623534037158,
789              5.97273706916525,  5.99248443536382,  6.00596954551065,  6.02391135349251,  6.03499790070022,
790              6.05182805048050,  6.06175143381919,  6.07154863247196,  6.08413901110595,  6.10816742583396,
791              6.12409861736059,  6.14993596814711,  6.17795416929401,  6.21009036097155,  6.24720290848504,
792              6.29442103520580,  6.34025558382214,  6.39561698847542,  6.45722913719182,  6.53145793086557,
793              6.59133530430947,  6.64910616618317,  6.72089255688848,  6.80122073733940,  6.87314448884226,
794              6.93449289917520,  7.01423515606806,  7.07733481278839,  7.15026290207858,  7.22661488675360,
795              7.29606138445174,  7.35524174377133,  7.41567347210106,  7.47587213803126,  7.53222835738739,
796              7.58356426807221,  7.63308940041581,  7.69371703650559,  7.72501373906878,  7.76442579847960,
797              7.81368926436789,  7.86740380938358,  7.90421079286005,  7.94583024026008,  7.99671563708003,
798              8.04244340239606,  8.09185349728779,  8.13181093846529,  8.19753260427575,  8.23719704088131,
799              8.30605821636440,  8.34763957780890,  8.40377164008712,  8.45850190451208,  8.50689521959712,
800              8.55880378414300,  8.62199954623019,  8.67023128685909,  8.72579939081860,  8.77654327509603,
801              8.82703722960192,  8.87234745275413,  8.92298873699862,  8.96183230128869,  8.99959945199256,
802              9.02190421564648,  9.07021876389415,  9.14171865909998,  9.17648133356737,  9.23501491433004,
803              9.27081558815322,  9.33127360138348,  9.37546361810810,  9.42685055197297,  9.47173114044686,
804              9.52499601160681,  9.59225427983750,  9.63452295883245,  9.71017367730259,  9.75544957527789,
805              9.81633196341860,  9.85444975882294,  9.92005872253106,  9.97082968289184, 10.03449699174470,
806             10.09122455063550, 10.13389416965560, 10.17837658254170, 10.21773545898330, 10.26744042097590,
807             10.29056919034800, 10.34150281323410, 10.37786995379330, 10.41407219044100, 10.44484102770980,
808             10.48570812308140, 10.53218630359190, 10.56512838158260, 10.62006067151270, 10.65887457061470,
809             10.69741291849750, 10.76304496488800, 10.79931280850780, 10.85703517697970, 10.92574554866050,
810             10.98331363033000, 11.06307710332800, 11.13985746945070, 11.21504740591010, 11.30211296891140,
811             11.36699941337580, 11.45784698231140, 11.52291388418790, 11.61101817368620, 11.69846243375700,
812             11.77479062604180, 11.86833376348470, 11.93298189517650, 12.00576930240830, 12.06813240917040,
813             12.15218992011410, 12.21946030465890, 12.27984732114790, 12.35702565784200, 12.41636699285090,
814             12.46990434447820, 12.52456539849360, 12.58567800699240, 12.65352478766170, 12.69413440791780,
815             12.74686113142120, 12.80246461153120, 12.84146482477360, 12.90663700679580, 12.96117766963330,
816             13.01274242403170, 13.06615836279630, 13.11619534836710, 13.18279152205640, 13.24036511425530,
817             13.29239094969960, 13.38152016994780, 13.43294283660300, 13.50443946410150, 13.55685819057320,
818             13.61965238032380, 13.67226613256500, 13.71989509247660, 13.77979876248850, 13.83180052020310,
819             13.84430227935160, 13.89482616183360, 13.93275292471270, 13.98796853844590, 14.02120281970040,
820             14.06469501339430, 14.09613766080790, 14.14338696518080, 14.18403496186730, 14.21806607531730,
821             14.25772335788710, 14.29669029454240, 14.33194679288560, 14.37758361831550, 14.42119985137430,
822             14.44971099119800, 14.50741581408920, 14.56118017427560, 14.62092534162650, 14.68515260398560,
823             14.73491963050900, 14.80907502694460, 14.88815737354350, 14.94616320376450, 15.02558463532820,
824             15.11203094864740, 15.19717271480480, 15.27718243851370, 15.36567171899760, 15.45184153402890,
825             15.53754196944420, 15.64788249678310, 15.72509581415320, 15.81429427662460, 15.92707653156050,
826             16.01731134738170, 16.11600746350080, 16.20892471361740, 16.29225189359360, 16.39359707298030,
827             16.47066209226870, 16.55917178645840, 16.62561782023400, 16.70841038659590, 16.78676522974520,
828             16.83121370364950, 16.90970725578240, 16.98273901593150, 17.04871601788730, 17.11268063089090,
829             17.17982076876960, 17.22381424217040, 17.30314819933530, 17.35746332322000, 17.43112885625660,
830             17.48204699145510, 17.55696117862960, 17.60494634967520, 17.65502592392060, 17.71403934285840,
831             17.78598100126920, 17.85655375376110, 17.92058866864030, 17.99230099864570, 18.06707477223760,
832             18.13436354489630, 18.21311730172240, 18.28960928763870, 18.32967134578120, 18.39692296776050,
833             18.45656375462550, 18.52390272062570, 18.57529417810770, 18.62673762859450, 18.67788480420940,
834             18.75249257489740, 18.80462636945440, 18.87542569644120, 18.91158693077320, 18.96570817482920,
835             19.01692378371080, 19.04623736623600, 19.10227678132730, 19.14509502041990, 19.18970799817970,
836             19.23873108495060, 19.26780950369100, 19.31419402308830, 19.36528274806400, 19.40118097179560,
837             19.46035853227560, 19.52064162839660, 19.58170590260900, 19.63171035807460, 19.70336844700920,
838             19.77064368819760, 19.80392383408410, 19.85992006541590, 19.93738561338900, 20.02284410602150,
839             20.08669014565640, 20.15900956530060, 20.23440699499200, 20.30600184698670, 20.40066552213020,
840             20.48273233514580, 20.57931739732480, 20.65593145165050, 20.73726859510960, 20.81966265151350,
841             20.89923317324460, 20.99521486578730, 21.08979561119570, 21.16193414010030, 21.22341789026790,
842             21.27993341829660, 21.34027406739970, 21.40159850749630, 21.47436457110910, 21.55347337103140,
843             21.59119691076710, 21.62606604994440, 21.67467413647190, 21.70707069322430, 21.75858598819490,
844             21.75450883462950, 21.77856004667370, 21.80338220169380, 21.82430409207460, 21.84731432278300,
845             21.83921535624070, 21.82707643340360, 21.81788806235140, 21.83770638889470, 21.82709299095180,
846             21.82200452897680, 21.83182112073370, 21.81370576529070, 21.80076601334910, 21.81652119802770,
847             21.81694442986100, 21.82757391397910, 21.82478527187680, 21.84147779909220, 21.85287888972480,
848             21.89030789562500, 21.91890397432130, 21.93885180364230, 21.97121208961480, 22.01583652136870,
849             22.07517328845300, 22.10910723435870, 22.19019852119270, 22.27408994666450, 22.35997186695490,
850             22.40815085255450, 22.45819760947410, 22.55296946624010, 22.61233512963040, 22.68122483254160,
851             22.78230399665030, 22.83508167743760, 22.93786266495910, 23.02439055811430, 23.11940579254900,
852             23.20009585626350, 23.30795385400430, 23.36914451887960, 23.47258967592180, 23.55535974010960,
853             23.66610522886750, 23.74556169630170, 23.84982446270390, 23.90955529155090, 23.98157811043960,
854             24.07896017887990, 24.14704691615930, 24.22687315607770, 24.28862482756210, 24.34056252400220,
855             24.38992656534960, 24.45594790811740, 24.48258955643180, 24.56667776513020, 24.59445536450440,
856             24.62805700416400, 24.67003032390420, 24.67523437840420, 24.68408743469140, 24.70882462500420,
857             24.68773496316310, 24.68083688210240, 24.68884785906170, 24.69179894931380, 24.68383355606880,
858             24.68026272388530, 24.66519738431140, 24.66385015373380, 24.72241293753290, 24.81399519754810,
859             24.80832879273080, 24.82645691608680, 24.84343603710690, 24.86139493322710, 24.88725735634150,
860             24.92005801503100, 24.96863912290040, 25.03206105522360, 25.08095377821620, 25.15205603034190,
861             25.21193094780020, 25.29663104275120, 25.40483573615490, 25.48281226144420, 25.56685407258250,
862             25.68614278697970, 25.80896360074120, 25.91262556143080, 26.03463859309800, 26.16935490775920,
863             26.28109060851750, 26.44978666349210, 26.58744659856100, 26.74027759505110, 26.88720372811530,
864             27.02579688843010, 27.18921747721150, 27.38550254634900, 27.52613393343870, 27.68919558229660,
865             27.86693283355410, 28.04847885771420, 28.19197395276460, 28.37872109017280, 28.56497190383570,
866             28.68044776130840, 28.86310256322110, 29.00447106969210, 29.18116506431980, 29.33743334927930,
867             29.49194157166570, 29.65054267048530, 29.78297475865880, 29.91797056649910, 30.03569309218670,
868             30.17708406364710, 30.30409065410740, 30.45612977341240, 30.53107447317290, 30.67196082910110,
869             30.77986552522620, 30.89224657253820, 30.96622308509980, 31.07220318693750, 31.15718595861960,
870             31.20383583227280, 31.30377856140940, 31.40414808266530, 31.43611018221700, 31.52801653226750,
871             31.60102442653360, 31.72621718204230, 31.79591280500360, 31.86826527478240, 31.94853434185450,
872             32.02539482506510, 32.13974161224190, 32.20423181540680, 32.28018677692370, 32.28395726824170,
873             32.40494318511320, 32.46749445066700, 32.52980425678560, 32.63380453616110, 32.68092259650560,
874             32.77319964225100, 32.90104863150770, 33.02129329230090, 33.15087824382160, 33.16424665430360,
875             33.23137293644180, 33.28290115780930, 33.35484158302640, 33.43853487313680, 33.60929049682170,
876             33.70046828899200, 33.79738115234160, 33.85206201950270, 33.94663872932400, 33.96045104171260,
877             34.05475363448850, 34.10062428612370, 34.18215916052220, 34.25004129884530, 34.32150418867920,
878             34.39610542452530, 34.46044843156990, 34.51687045926410, 34.55209146623040, 34.59658135254390,
879             34.64960558805920, 34.70425712813580, 34.72836102434770, 34.77389830912350, 34.80967030593960,
880             34.85796100803080, 34.90914755830490, 34.92277339608200, 34.93897843389750, 34.96598147672490,
881             35.02237511781240, 35.06262194597710, 35.11140592220290, 35.14137452932910, 35.16239737127000,
882             35.20296586992050, 35.24898755877660, 35.29335977034030, 35.37355689179760, 35.37678456095530,
883             35.46886556304630, 35.52168787701290, 35.56242018045580, 35.60966168176520, 35.65975868233490,
884             35.73275359519800, 35.76218208991110, 35.86790780049770, 35.91765316548140, 36.02584644500090,
885             36.10398104619080, 36.20544100103450, 36.31996143336560, 36.43499302114470, 36.53317969266010,
886             36.67017925325560, 36.79399513857610, 36.85507056219050, 36.95126480436820, 37.05372727870340,
887             37.15059134951820, 37.29182879749320, 37.36382982923950, 37.44132457886000, 37.49166785778120,
888             37.57717129112090, 37.64735391250410, 37.71130174926090, 37.82473094660950, 37.91546830389120,
889             37.99067831268420, 38.09451284902960, 38.15758953743980, 38.23615790463980, 38.32197158067270,
890             38.43134475176400, 38.53659884487360, 38.62544847176190, 38.73075853955870, 38.81319121216220,
891             38.90295298639700, 38.95350872577000, 39.06616089019790, 39.13501614390540, 39.22624636126210,
892             39.23983187175450, 39.30548595962250, 39.36147734872170, 39.40495530656890, 39.47796375277410,
893             39.57629997171180, 39.62225843940690, 39.68200991305980, 39.73714559398590, 39.79931521717210,
894             39.85767403971500, 39.92737671585440, 40.00014684630660, 40.06188424545710, 40.11552243648740,
895             40.18301915095550, 40.23953960887440, 40.32017452534290, 40.40280466236230, 40.47402128974550,
896             40.54780868162140, 40.61686055743300, 40.66449293067440, 40.74033856641390, 40.79884863366840,
897             40.85113626978660, 40.89327993208460, 40.92709196131660, 41.02932850901520, 41.07870172640470,
898             41.15208090444430, 41.20719958249480, 41.22779665202910, 41.30340521946180, 41.37463561990510,
899             41.43581449180160, 41.53237538481520, 41.62442610703640, 41.67685766480740, 41.70551621209030,
900             41.79387528993570, 41.87249022499630, 41.96748580710450, 41.96066425184290, 42.00236919506070,
901             42.06691271868140, 42.14453462221240, 42.17232865317080, 42.21308654606010, 42.29682092288380,
902             42.28020989259000, 42.33453119996890, 42.42249257093860, 42.45259373439840, 42.52678228221080,
903             42.62360174189350, 42.65108357605410, 42.69529079131090, 42.76506482589140, 42.76866147327120,
904             42.84197399799980, 42.88348405910990, 42.93480463891800, 42.93029206285050, 42.96927781746760,
905             43.03102555127540, 43.06837620082900, 43.07944143286120, 43.11894499317990, 43.14621770363800,
906             43.21178505966750, 43.22262263267730, 43.23217246930500, 43.27929678731450, 43.33474352075870,
907             43.33186074018300, 43.38691324155310, 43.47481497408200, 43.48959909739700, 43.53385056560400,
908             43.52006236460410, 43.54288106068920, 43.58467808312320, 43.65098008442400, 43.68014893728780,
909             43.77655831776850, 43.82158431110590, 43.87133619942870, 43.89857776752220, 43.90895708958380,
910             43.93924688076010, 43.94311255617310, 44.00206298314270, 44.06016926166370, 44.08464841439000,
911             44.14974194116070, 44.16360932249060, 44.21550172914360, 44.24645163821880, 44.31413103396010,
912             44.37582255973850, 44.44290243938980, 44.46713847006880, 44.54954762719370, 44.61770085941470,
913             44.62707255364330, 44.68776382854840, 44.75644795704410, 44.82017049514460, 44.86731775028840,
914             44.93669847762410, 44.94323079360680, 45.00289235842400, 45.06074445928730, 45.10070072771480,
915             45.12535413487300, 45.18971616198560, 45.22800594544770, 45.28256242647910, 45.33033970320710,
916             45.40157892892660, 45.40990798048930, 45.45005102097290, 45.49876470897600, 45.55719413446290,
917             45.57558835135730, 45.65806900650620, 45.71135372605290, 45.73646800236430, 45.75159872871630,
918             45.78821768722850, 45.84825244733540, 45.99560549778800, 46.20313088822660, 46.22034059337770,
919             46.27787413361390, 46.32758131762610, 46.35512346817560, 46.43510848595720, 46.50490991827820,
920             46.46179852904980, 46.26014174457510, 46.23810710782930, 46.30556813310900, 46.36527013549460,
921             46.37721630907690, 46.45252810085410, 46.50466755772400, 46.55813315248220, 46.66800552130970,
922             46.76963943822540, 46.82246498060560, 46.84003287543710, 46.80448017917460, 46.83841548307860,
923             46.86048386561490, 46.91957734088020, 46.91730690365440, 46.98966653214880, 47.00517008730720,
924             47.01315112187740, 47.07074221081100, 47.05280548322350, 47.10234054509240, 47.14878533434680,
925             47.18764807236820, 47.20302242802520, 47.19090909577390, 47.24496063401410, 47.27799257806610,
926             47.24473681961440, 47.26632585935810, 47.29130144819630, 47.38028322287670, 47.36536210587930,
927             47.40946164674690, 47.42629682558840, 47.44011415765250, 47.49471639778660, 47.50993333608530,
928             47.60153139746630, 47.59098752530950, 47.60599414543740, 47.72138917242870, 47.73101049133920,
929             47.77351224566690, 47.80221279327420, 47.82379204552780, 47.89507117817290, 47.91381255065170,
930             47.95655013828690, 47.99009680892420, 48.00757694740830, 48.07195217026400, 48.11071100450270,
931             48.18321233686060, 48.20431083391930, 48.25896998104100, 48.28514079791270, 48.30760284864470,
932             48.33195778345520, 48.38468946527180, 48.40754424775460, 48.41721780513190, 48.43762980691400,
933             48.47003292390880, 48.53107971286060, 48.56893872838810, 48.62557292516310, 48.61079540288420,
934             48.61346964133380, 48.64024108367710, 48.64691454658780, 48.73450924908860, 48.71115321332660,
935             48.72454994541860, 48.65239888576690, 48.71463654728540, 48.67255147927480, 48.70777995399180,
936             48.69123070112490, 48.76049250446820, 48.71636847272490, 48.85251266457350, 48.82720687148140,
937             48.86710958576830, 48.89669398464330, 48.89202595348700, 48.90438606498260, 48.86129690970630,
938             48.98704016472160, 48.91035469778120, 48.90886968746060, 48.96240538807380, 48.98812573916050,
939             48.90082552081360, 48.98011640015980, 49.11690487416240, 49.12383559393310, 49.16479829306740,
940             49.15441032001890, 49.20513908434000, 49.19469791774490, 49.17543552886840, 49.17685633821150,
941             49.07816045299530, 49.06696566214730, 49.08073590280000, 49.08073079577200, 49.12930738996090,
942             49.20825350918470, 49.17186181058630, 49.22176095988120, 49.25749772083510, 49.22595428368510,
943             49.29514363225830, 49.26629340436610, 49.19897346191790, 49.29684832561600, 49.21207757481190,
944             49.26279980291820, 49.25745023713690, 49.16981892047690, 49.16472388085190, 49.16827588538560,
945             49.12194271618720, 49.19377189624870, 49.14953717217270, 49.13487981820580, 49.09854699671190,
946             49.08310226494570, 49.10346596782800, 49.02822031544520, 49.05569923993890, 49.00651755002290,
947             48.93481065983130, 48.99284161811070, 48.85219580565640, 48.82600862507190, 48.84196777240100,
948             48.69357868381790, 48.80868832002350, 48.85607948311330, 48.73882088809720, 48.80816651451680,
949             48.64757547013110, 48.57371357777440, 48.56579373582720, 48.50410324028500, 48.35860141223540,
950             48.24394471734060, 48.21392991778470, 48.14547473189630, 47.99672507987360, 47.98082853294720,
951             47.87383481217780, 47.81606591067890, 47.68176414032230, 47.66006320644340, 47.55384819627930,
952             47.41638676490530, 47.32727872241500, 47.23891584842230, 47.09295003421260, 47.01618839720610,
953             46.87025697008420, 46.81067613735140, 46.61253912221300, 46.48170334194630, 46.30064566476440,
954             46.17873900417390, 46.02126472107910, 45.82149354364510, 45.74013547031190, 45.59135952586340,
955             45.35455311570520, 45.19473268803280, 45.02259432367430, 44.88341894949910, 44.60207461974350,
956             44.41965592797250, 44.19481139858310, 43.95425484068060, 43.68767137468690, 43.50054933484870,
957             43.18287393218360, 42.94164176287200, 42.69301585048210, 42.38647146868720, 42.16091175320670,
958             41.86384398941410, 41.48093302599290, 41.20862279018740, 40.85976255982450, 40.51784102028600,
959             40.19085497357880, 39.89106781396070, 39.56141360761760, 39.08806529917160, 38.80399629669640,
960             38.46100812627460, 38.03595413835370, 37.74622068467920, 37.28595910233180, 36.93238223035530,
961             36.51088680226470, 36.08309041078630, 35.71388875294900, 35.31156189809560, 34.86588513381560,
962             34.50542730589920, 34.02103201416430, 33.65805797241870, 33.23401307672130, 32.76192764295640,
963             32.34575040730360, 31.95277910223730, 31.53115586525900, 31.16620917858590, 30.72240903329530,
964             30.30990604744090, 29.88332309573020, 29.48625157300980, 29.15599898784370
965         };
966 
967         // Output wavelengths.
968         static const size_t OutputCount = 31;
969         static const double OutputWavelength[OutputCount] =
970         {
971             400.0, 410.0, 420.0, 430.0, 440.0, 450.0, 460.0, 470.0, 480.0, 490.0,
972             500.0, 510.0, 520.0, 530.0, 540.0, 550.0, 560.0, 570.0, 580.0, 590.0,
973             600.0, 610.0, 620.0, 630.0, 640.0, 650.0, 660.0, 670.0, 680.0, 690.0,
974             700.0
975         };
976 
977         typedef RegularSpectrum<double, OutputCount> Spectrum;
978 
979         // White spectrum.
980         {
981             Spectrum white_spectrum;
982             spectrum_to_spectrum(
983                 InputSpectrumCount,
984                 InputSpectrumWavelength,
985                 InputWhiteSpectrum,
986                 OutputCount,
987                 OutputWavelength,
988                 &white_spectrum[0]);
989 
990             GnuplotFile plotfile;
991             plotfile.set_title("White Spectrum");
992             plotfile.set_xlabel("Wavelength (nm)");
993             plotfile
994                 .new_plot()
995                 .set_points(zip(&InputSpectrumWavelength[0], &InputWhiteSpectrum[0], InputSpectrumCount))
996                 .set_title("Input")
997                 .set_color("gray");
998             plotfile
999                 .new_plot()
1000                 .set_points(zip(&OutputWavelength[0], &white_spectrum[0], OutputCount))
1001                 .set_title("Output")
1002                 .set_color("red");
1003             plotfile.write("unit tests/outputs/test_colorspace_white.gnuplot");
1004         }
1005 
1006         // Green spectrum.
1007         {
1008             Spectrum green_spectrum;
1009             spectrum_to_spectrum(
1010                 InputSpectrumCount,
1011                 InputSpectrumWavelength,
1012                 InputGreenSpectrum,
1013                 OutputCount,
1014                 OutputWavelength,
1015                 &green_spectrum[0]);
1016 
1017             GnuplotFile plotfile;
1018             plotfile.set_title("Green Spectrum");
1019             plotfile.set_xlabel("Wavelength (nm)");
1020             plotfile
1021                 .new_plot()
1022                 .set_points(zip(&InputSpectrumWavelength[0], &InputGreenSpectrum[0], InputSpectrumCount))
1023                 .set_title("Input")
1024                 .set_color("gray");
1025             plotfile
1026                 .new_plot()
1027                 .set_points(zip(&OutputWavelength[0], &green_spectrum[0], OutputCount))
1028                 .set_title("Output")
1029                 .set_color("red");
1030             plotfile.write("unit tests/outputs/test_colorspace_green.gnuplot");
1031         }
1032 
1033         // Red spectrum.
1034         {
1035             Spectrum red_spectrum;
1036             spectrum_to_spectrum(
1037                 InputSpectrumCount,
1038                 InputSpectrumWavelength,
1039                 InputRedSpectrum,
1040                 OutputCount,
1041                 OutputWavelength,
1042                 &red_spectrum[0]);
1043 
1044             GnuplotFile plotfile;
1045             plotfile.set_title("Red Spectrum");
1046             plotfile.set_xlabel("Wavelength (nm)");
1047             plotfile
1048                 .new_plot()
1049                 .set_points(zip(&InputSpectrumWavelength[0], &InputRedSpectrum[0], InputSpectrumCount))
1050                 .set_title("Input")
1051                 .set_color("gray");
1052             plotfile
1053                 .new_plot()
1054                 .set_points(zip(&OutputWavelength[0], &red_spectrum[0], OutputCount))
1055                 .set_title("Output")
1056                 .set_color("red");
1057             plotfile.write("unit tests/outputs/test_colorspace_red.gnuplot");
1058         }
1059 
1060         // Camera response.
1061         {
1062             Spectrum camera_response;
1063             spectrum_to_spectrum(
1064                 InputCameraResponseCount,
1065                 InputCameraResponseWavelength,
1066                 InputCameraResponse,
1067                 OutputCount,
1068                 OutputWavelength,
1069                 &camera_response[0]);
1070 
1071             GnuplotFile plotfile;
1072             plotfile.set_title("Camera Response");
1073             plotfile.set_xlabel("Wavelength (nm)");
1074             plotfile
1075                 .new_plot()
1076                 .set_points(zip(&InputCameraResponseWavelength[0], &InputCameraResponse[0], InputCameraResponseCount))
1077                 .set_title("Input")
1078                 .set_color("gray");
1079             plotfile
1080                 .new_plot()
1081                 .set_points(zip(&OutputWavelength[0], &camera_response[0], OutputCount))
1082                 .set_title("Output")
1083                 .set_color("red");
1084             plotfile.write("unit tests/outputs/test_colorspace_camera.gnuplot");
1085         }
1086 
1087         // Light source emission spectrum.
1088         {
1089             Spectrum source_spectrum;
1090             spectrum_to_spectrum(
1091                 InputSourceSpectrumCount,
1092                 InputSourceSpectrumWavelength,
1093                 InputSourceSpectrum,
1094                 OutputCount,
1095                 OutputWavelength,
1096                 &source_spectrum[0]);
1097 
1098             GnuplotFile plotfile;
1099             plotfile.set_title("Light Source Emission Spectrum");
1100             plotfile.set_xlabel("Wavelength (nm)");
1101             plotfile
1102                 .new_plot()
1103                 .set_points(zip(&InputSourceSpectrumWavelength[0], &InputSourceSpectrum[0], InputSourceSpectrumCount))
1104                 .set_title("Input")
1105                 .set_color("gray");
1106             plotfile
1107                 .new_plot()
1108                 .set_points(zip(&OutputWavelength[0], &source_spectrum[0], OutputCount))
1109                 .set_title("Output")
1110                 .set_color("red");
1111             plotfile.write("unit tests/outputs/test_colorspace_light.gnuplot");
1112         }
1113     }
1114 }
1115