Path Tracer
spd.hpp
1 #pragma once
2 
3 #include <color/specdata.hpp>
4 #include <color/specutils.hpp>
5 #include <color/wave.hpp>
6 #include <common.hpp>
7 #include <math3d/vec3.hpp>
8 #include <utils.hpp>
9 
10 using namespace ptracey;
11 namespace ptracey {
12 
13 class spd {
14 public:
15  WaveLength wave_start;
16  WaveLength wave_end;
17  std::map<WaveLength, Power> wavelength_power;
18 
19 public:
20  // static methods
21  static spd random() { return spd(); }
22  static spd random(Real mn, Real mx) {
23  auto ss = spd();
24  auto pw = ss.powers();
25  std::vector<Real> pws;
26  for (uint i = 0; i < (uint)pw.size(); i++) {
27  pws.push_back(random_real(mn, mx));
28  }
29  auto power_spd = sampled_wave<Power>(pws);
30  auto wlength = ss.wavelengths();
31  ss = spd(power_spd, wlength);
32  return ss;
33  }
34 
35  static spd zeros_like(const spd &s) {
36  auto waves = s.wavelengths();
37  std::vector<Power> pws(waves.size(), 0.0);
38  sampled_wave<Power> powers(pws);
39  auto sp = spd(powers, waves);
40  return sp;
41  }
42 
43 public:
44  // ----------------- Start Constructors ---------------
45  spd(uint size = 471 / SPD_STRIDE) {
46  wavelength_power.clear();
47  std::vector<Power> powers;
48  for (uint i = 0; i < size; i++) {
49  powers.push_back(0.0);
50  }
51  auto power_spd = sampled_wave<Power>(powers);
52  auto ws = linspace<WaveLength>(VISIBLE_LAMBDA_START,
53  VISIBLE_LAMBDA_END,
54  powers.size());
55  for (uint i = 0; i < powers.size(); i++) {
56  auto pw = make_pair(ws[i], powers[i]);
57  wavelength_power.insert(pw);
58  }
59  }
60  spd(const sampled_wave<Power> &sampled_powers,
61  WaveLength wstart)
62  : wave_start(wstart) {
63  wavelength_power.clear();
64  for (uint i = 0; i < sampled_powers.size(); i++) {
65  Power power = sampled_powers[i];
66  WaveLength wavelength =
67  i + static_cast<WaveLength>(wstart);
68  auto pw = make_pair(wavelength, power);
69  wavelength_power.insert(pw);
70  }
71  wave_end = wave_start + static_cast<WaveLength>(
72  sampled_powers.size() - 1);
73  }
74  spd(const sampled_wave<Power> &sampled_powers,
75  const std::vector<WaveLength> &wlengths) {
76  wavelength_power.clear();
77  COMP_CHECK(
78  sampled_powers.size() == (uint)wlengths.size(),
79  sampled_powers.size(), (uint)wlengths.size());
80  for (auto wl : wlengths) {
81  if (wl < 0) {
82  throw std::runtime_error(
83  "wavelength can not be less than 0");
84  }
85  }
86  std::map<WaveLength, Power> temp;
87  for (uint i = 0; i < (uint)wlengths.size(); i++) {
88  temp.insert(
89  make_pair(wlengths[i], sampled_powers[i]));
90  }
91  std::vector<WaveLength> nwlengths(wlengths.size());
92 
93  std::copy(wlengths.begin(), wlengths.end(),
94  nwlengths.begin());
95 
96  std::sort(nwlengths.begin(), nwlengths.end(),
97  [](auto i, auto j) { return i < j; });
98 
99  wave_start = nwlengths[0];
100  wave_end = nwlengths[wlengths.size() - 1];
101  for (uint i = 0; i < nwlengths.size(); i++) {
102  auto pw =
103  make_pair(nwlengths[i], temp.at(nwlengths[i]));
104  wavelength_power.insert(pw);
105  }
106  }
125  spd(uint wave_range, const std::function<WaveLength(uint)>
126  &wavelength_generator,
127  const std::function<Power(WaveLength)>
128  &power_generator) {
129  wave_start = wavelength_generator(0);
130  wave_end = wavelength_generator(wave_range - 1);
131  wavelength_power.clear();
132  for (uint i = 1; i < wave_range; i++) {
133  WaveLength wave = wavelength_generator(i);
134  Power power_value = power_generator(wave);
135  wavelength_power.insert(make_pair(wave, power_value));
136  }
137  }
138  template <typename V>
139  void fill_with_stride(std::vector<V> &dest,
140  const std::vector<V> &srcv,
141  unsigned int stride) {
142  for (unsigned int i = 0; i < (uint)srcv.size();
143  i += stride) {
144  dest.push_back(srcv[i]);
145  }
146  }
147  spd(const path &csv_path,
148  const std::string &wave_col_name = "wavelength",
149  const std::string &power_col_name = "power",
150  const char &sep = ',',
151  const unsigned int stride = SPD_STRIDE,
152  const std::function<WaveLength(WaveLength)>
153  &wave_transform = [](auto j) { return j; },
154  const std::function<Power(Power)> &power_transform =
155  [](auto j) { return j; }) {
156  wavelength_power.clear();
157  path csv_path_abs = RUNTIME_PATH / csv_path;
158  rapidcsv::Document doc(csv_path_abs.string(),
159  rapidcsv::LabelParams(0, -1),
161  std::vector<Power> temp;
162  try {
163  temp = doc.GetColumn<Power>(power_col_name);
164  } catch (const std::out_of_range &err) {
165  throw std::runtime_error(std::string(err.what()) +
166  " :: " + power_col_name +
167  " in file :: " +
168  csv_path_abs.string());
169  }
170  std::vector<Power> powers;
171  fill_with_stride<Power>(powers, temp, stride);
172  std::vector<WaveLength> tempv;
173  try {
174  tempv = doc.GetColumn<WaveLength>(wave_col_name);
175  } catch (const std::out_of_range &error) {
176  throw std::runtime_error(std::string(error.what()) +
177  " :: " + wave_col_name +
178  " in file :: " +
179  csv_path_abs.string());
180  }
181  COMP_CHECK(tempv.size() == temp.size(), tempv.size(),
182  temp.size());
183  std::vector<WaveLength> wlengths;
184  fill_with_stride<WaveLength>(wlengths, tempv, stride);
185 
186  wavelength_power.clear();
187  COMP_CHECK(powers.size() == wlengths.size(),
188  powers.size(), wlengths.size());
189  for (auto wl : wlengths) {
190  if (wl < 0) {
191  throw std::runtime_error(
192  "wavelength can not be less than 0");
193  }
194  }
195  std::map<WaveLength, Power> tempwp;
196  for (uint i = 0; i < (uint)wlengths.size(); i++) {
197  auto wave_value = wave_transform(wlengths[i]);
198  auto power_value = power_transform(powers[i]);
199  tempwp.insert(make_pair(wave_value, power_value));
200  }
201  std::sort(wlengths.begin(), wlengths.end(),
202  [](auto i, auto j) { return i < j; });
203  wave_start = wlengths[0];
204  wave_end = wlengths[wlengths.size() - 1];
205  for (uint i = 0; i < wlengths.size(); i++) {
206  auto pw =
207  make_pair(wlengths[i], tempwp.at(wlengths[i]));
208  wavelength_power.insert(pw);
209  }
210  }
211  // ------------------- End Constructors -----------------
212 public:
213  // ------------------- Start Methods --------------------
214 
215  void resample(const spd &s);
216  void resample(const WaveLength &waveLStart,
217  const WaveLength &waveLEnd,
218  const uint &outSize);
219  spd resample_c(const spd &s) const;
220  spd resample_c(const uint &outSize) const;
221  spd resample_c(const WaveLength &waveLStart,
222  const WaveLength &waveLEnd,
223  const uint &outSize) const;
224 
225  sampled_wave<Power> powers() const {
226  std::vector<Power> ps;
227  for (auto pw : wavelength_power) {
228  ps.push_back(pw.second);
229  }
230  return sampled_wave<Power>(ps);
231  }
232  std::vector<WaveLength> wavelengths() const {
233  std::vector<WaveLength> ps;
234  for (auto pw : wavelength_power) {
235  ps.push_back(pw.first);
236  }
237  std::sort(ps.begin(), ps.end(),
238  [](auto i, auto j) { return i < j; });
239  return ps;
240  }
241  spd interpolate(Real low = 0.0,
242  Real high = FLT_MAX) const {
243  sampled_wave<Power> normal_power = powers();
244  auto normal_power2 =
245  normal_power.interpolate(low, high);
246  auto wlengths = wavelengths();
247  auto ss = spd(normal_power2, wlengths);
248  return ss;
249  }
250  spd clamp(Power low = 0.0, Power high = FLT_MAX) const {
251  sampled_wave<Power> normal_power = powers();
252  std::vector<Power> nvs;
253  for (auto np : normal_power.values) {
254  nvs.push_back(dclamp<Power>(np, low, high));
255  }
256  auto normal_power2 = sampled_wave<Power>(nvs);
257  auto wlengths = wavelengths();
258  auto ss = spd(normal_power2, wlengths);
259  return ss;
260  }
261  spd normalized() const { return interpolate(0.0, 1.0); }
262  void normalize() {
263  auto spd = normalized();
264  *this = spd;
265  }
266  int min_wave() const {
267  auto it = std::min_element(
268  wavelength_power.begin(), wavelength_power.end(),
269  [](const auto &k1, const auto &k2) {
270  return k1.first < k2.first;
271  });
272  auto wavel =
273  it == wavelength_power.end() ? -1.0 : it->first;
274  return wavel;
275  }
276  int max_wave() const {
277  auto it = std::max_element(
278  wavelength_power.begin(), wavelength_power.end(),
279  [](const auto &k1, const auto &k2) {
280  return k1.first < k2.first;
281  });
282  auto wavel =
283  it == wavelength_power.end() ? -1.0 : it->first;
284  return wavel;
285  }
286  Power min_power() const {
287  auto it = std::min_element(
288  wavelength_power.begin(), wavelength_power.end(),
289  [](const auto &k1, const auto &k2) {
290  return k1.second < k2.second;
291  });
292  auto p =
293  it == wavelength_power.end() ? -1.0 : it->second;
294  return p;
295  }
296  Power max_power() const {
297  auto it = std::max_element(
298  wavelength_power.begin(), wavelength_power.end(),
299  [](const auto &k1, const auto &k2) {
300  return k1.second < k2.second;
301  });
302  auto p =
303  it == wavelength_power.end() ? -1.0 : it->second;
304  return p;
305  }
306  uint size() const {
307  return (uint)wavelength_power.size();
308  }
309  Power integrate(const spd &nspd) const {
310  COMP_CHECK(size() == nspd.size(), size(), nspd.size());
311  WaveLength x1 = min_wave();
312  WaveLength x2 = max_wave();
313 
314  Power stepsize = (x2 - x1) / static_cast<Power>(size());
315  Power val = 0.0;
316  auto waves = wavelengths();
317  for (unsigned int i = 0; i < size(); i++) {
318  auto pw = wavelength_power.at(waves[i]);
319  auto v = nspd[waves[i]];
320  val += pw * v;
321  }
322  return val * stepsize;
323  }
324  void apply(Power pvalue,
325  const std::function<Power(Power, Power)> &fn) {
326  for (auto &pr : wavelength_power) {
327  pr.second = fn(pr.second, pvalue);
328  }
329  }
330  spd apply_c(
331  Power pvalue,
332  const std::function<Power(Power, Power)> &fn) const {
333  std::vector<WaveLength> waves;
334  std::vector<Power> powers;
335  for (auto &pr : wavelength_power) {
336  Power p = fn(pr.second, pvalue);
337  waves.push_back(pr.first);
338  powers.push_back(p);
339  }
340  auto sw = sampled_wave<Power>(powers);
341  auto sp = spd(sw, waves);
342  return sp;
343  }
344  bool apply(WaveLength wave_length, Power pvalue,
345  const std::function<Power(Power, Power)> &fn) {
346  if (in(wave_length)) {
347  Power power_value = wavelength_power.at(wave_length);
348  wavelength_power[wave_length] =
349  fn(power_value, pvalue);
350  return true;
351  }
352  return false;
353  }
354  bool apply(const spd &s,
355  const std::function<spd(spd, spd)> &fn,
356  spd &ss) const {
357  auto waves = wavelengths();
358  auto swaves = s.wavelengths();
359  auto res = *this;
360  if (waves == swaves) {
361  ss = fn(res, s);
362  return true;
363  }
364  return false;
365  }
380  spd rapply(const spd &s,
381  const std::function<spd(spd, spd)> &eqfn,
382  const std::function<bool(
383  spd, WaveLength, Power)> &uneqfn) const {
384  spd ss;
385  if (apply(s, eqfn, ss)) {
386  return ss;
387  }
388  auto swaves = s.wavelengths();
389  for (const auto &w : swaves) {
390  auto spower = s[w];
391  if (!uneqfn(ss, w, spower))
392  ss.update(w, spower);
393  }
394  return ss;
395  }
396  Power interpolate_wave_power(WaveLength wl) const {
397  //
398  auto waves = wavelengths();
399  auto wsize = (int)waves.size();
400  auto lower =
401  std::lower_bound(waves.begin(), waves.end(), wl);
402  if (lower != waves.end()) {
403  auto index = std::distance(waves.begin(), lower);
404  if (index == 0) {
405  throw std::runtime_error(
406  "wavelength is below the available range");
407  }
408  auto index2 = index - 1;
409  Power p1 = wavelength_power.at(waves[index]);
410  Power p2 = wavelength_power.at(waves[index2]);
411  return (p1 + p2) / 2.0;
412  }
413 
414  throw std::runtime_error(
415  "wavelength is above the available range");
416  }
417  Power operator[](WaveLength wave_length) const {
418  if (in(wave_length)) {
419  Power p = wavelength_power.at(wave_length);
420  return p;
421  }
422  return interpolate_wave_power(wave_length);
423  }
424  bool in(WaveLength wave_length) const {
425  auto it = wavelength_power.find(wave_length);
426  if (it != wavelength_power.end()) {
427  return true;
428  }
429  return false;
430  }
431  void insert(WaveLength wave_length, Power pvalue) {
432  if (!in(wave_length)) {
433  wavelength_power.insert(
434  make_pair(wave_length, pvalue));
435  }
436  }
437  void update(WaveLength wave_length, Power pvalue) {
438  wavelength_power.insert_or_assign(wave_length, pvalue);
439  }
440  void add(WaveLength wave_length, Power pvalue) {
441  bool res =
442  apply(wave_length, pvalue,
443  [](Power i, Power j) { return i + j; });
444  if (!res) {
445  update(wave_length, pvalue);
446  }
447  }
448  void scale(Power pvalue) {
449  apply(pvalue, [](Power i, Power j) { return i * j; });
450  }
451  spd operator*(Power pvalue) const {
452  return apply_c(pvalue,
453  [](auto i, auto j) { return i * j; });
454  }
455  spd &operator*=(Power pvalue) {
456  apply(pvalue, [](Power i, Power j) { return i * j; });
457  return *this;
458  }
459  friend spd operator*(Power pvalue, const spd &s) {
460  return s * pvalue;
461  }
462  spd operator-(Power pvalue) const {
463  return apply_c(pvalue,
464  [](auto i, auto j) { return i - j; });
465  }
466  friend spd operator-(Power pvalue, const spd &s) {
467  return s - pvalue;
468  }
469 
470  spd operator+(Power pvalue) const {
471  return apply_c(pvalue,
472  [](auto i, auto j) { return i + j; });
473  }
474  friend spd operator+(Power pvalue, const spd &s) {
475  return s + pvalue;
476  }
477  spd &operator+=(Power pvalue) {
478  apply(pvalue, [](Power i, Power j) { return i + j; });
479  return *this;
480  }
481  spd &operator+=(const spd &s) {
482  auto resp = apply(s,
483  [](spd i, spd j) {
484  auto pwrs = i.powers() + j.powers();
485  return spd(pwrs, i.wavelengths());
486  },
487  *this);
488  if (resp) {
489  return *this;
490  }
491  throw std::runtime_error("spd's don't match");
492  }
493  //
494 };
495 
496 auto rho_rspd = spd(CSV_PARENT / "rho-r-2012.csv",
497  WCOL_NAME, PCOL_NAME, SEP, SPD_STRIDE);
498 
499 static spd rho_r = rho_rspd.normalized();
500 
501 auto rho_gspd = spd(CSV_PARENT / "rho-g-2012.csv",
502  WCOL_NAME, PCOL_NAME, SEP, SPD_STRIDE);
503 
504 static spd rho_g = rho_gspd;
505 
506 auto rho_bspd = spd(CSV_PARENT / "rho-b-2012.csv",
507  WCOL_NAME, PCOL_NAME, SEP, SPD_STRIDE);
508 
509 static spd rho_b = rho_bspd;
510 
511 auto cie_xbarspd =
512  spd(CSV_PARENT / "cie-x-bar-1964.csv", WCOL_NAME,
513  PCOL_NAME, SEP, SPD_STRIDE);
514 
515 static spd cie_xbar_spd = cie_xbarspd.normalized();
516 
517 auto cie_ybarspd =
518  spd(CSV_PARENT / "cie-y-bar-1964.csv", WCOL_NAME,
519  PCOL_NAME, SEP, SPD_STRIDE);
520 
521 static spd cie_ybar_spd = cie_ybarspd.normalized();
522 
523 auto cie_zbarspd =
524  spd(CSV_PARENT / "cie-z-bar-1964.csv", WCOL_NAME,
525  PCOL_NAME, SEP, SPD_STRIDE);
526 
527 static spd cie_zbar_spd = cie_zbarspd.normalized();
528 
529 auto stand_d65 = spd(CSV_PARENT / "cie-d65-standard.csv",
530  WCOL_NAME, PCOL_NAME, SEP, SPD_STRIDE);
531 
532 static spd standard_d65 = stand_d65.normalized();
533 //
534 
535 Power get_cie_val(const spd &qlambda, const spd &cie_bar) {
536  Power sum = 0.0;
537  auto qwaves = qlambda.wavelengths();
538  auto qmaxwave = qlambda.max_wave();
539  auto qminwave = qlambda.min_wave();
540  auto qwsize = (uint)qwaves.size();
541  auto cie = cie_bar.resample_c(qminwave, qmaxwave, qwsize);
542  for (const auto &wave : qwaves) {
543  sum += qlambda[wave] * cie[wave];
544  }
545  auto scale = (qmaxwave - qminwave) / Real(qwsize);
546  return scale * sum;
547 }
548 
549 Power get_cie_val(const spd &qlambda, const spd &rlambda,
550  const spd &cie_bar) {
551  Power sum = 0.0;
552  auto rmaxwave = rlambda.max_wave();
553  auto rminwave = rlambda.min_wave();
554  auto rwaves = rlambda.wavelengths();
555  auto rwsize = (uint)rwaves.size();
556  spd q_lambda =
557  qlambda.resample_c(rminwave, rmaxwave, rwsize);
558  spd cie = cie_bar.resample_c(rminwave, rmaxwave, rwsize);
559  auto waves = qlambda.wavelengths();
560  for (const auto &wave : rwaves) {
561  sum += q_lambda[wave] * rlambda[wave] * cie[wave];
562  }
563  auto scale =
564  (rmaxwave - rminwave) / Real(CIE_Y_integral * rwsize);
565 
566  return scale * sum;
567 }
568 
569 Power get_cie_x(const spd &qlambda) {
570  return get_cie_val(qlambda, cie_xbar_spd);
571 }
572 
573 Power get_cie_x(const spd &qlambda, const spd &rlambda) {
574  return get_cie_val(qlambda, rlambda, cie_xbar_spd);
575 }
576 
577 Power get_cie_y(const spd &qlambda) {
578  return get_cie_val(qlambda, cie_ybar_spd);
579 }
580 
581 Power get_cie_y(const spd &qlambda, const spd &rlambda) {
582  return get_cie_val(qlambda, rlambda, cie_ybar_spd);
583 }
584 
585 Power get_cie_z(const spd &qlambda) {
586  return get_cie_val(qlambda, cie_zbar_spd);
587 }
588 
589 Power get_cie_z(const spd &qlambda, const spd &rlambda) {
590  return get_cie_val(qlambda, rlambda, cie_zbar_spd);
591 }
592 
593 void get_cie_values(const spd &qlambda, Power &x, Power &y,
594  Power &z) {
595  x = get_cie_x(qlambda);
596  y = get_cie_y(qlambda);
597  z = get_cie_z(qlambda);
598 }
599 
600 void get_cie_values(const spd &qlambda, const spd &rlambda,
601  Power &x, Power &y, Power &z) {
602  x = get_cie_x(qlambda, rlambda);
603  y = get_cie_y(qlambda, rlambda);
604  z = get_cie_z(qlambda, rlambda);
605 }
606 
607 void get_cie_values(const spd &qlambda, vec3 &xyz) {
608  Power x, y, z;
609  get_cie_values(qlambda, x, y, z);
610  xyz = vec3(x, y, z);
611  Power sum = xyz.sum();
612  if (sum != 0) {
613  xyz = xyz / sum;
614  }
615 }
616 
617 void get_cie_values(const spd &qlambda, const spd &rlambda,
618  vec3 &xyz) {
619  Power x, y, z;
620  get_cie_values(qlambda, rlambda, x, y, z);
621  xyz = vec3(x, y, z);
622  Power sum = xyz.sum();
623  if (sum != 0) {
624  xyz = xyz / sum;
625  }
626 }
627 
628 inline std::ostream &operator<<(std::ostream &out,
629  const spd &ss) {
630  auto waves = ss.wavelengths();
631  auto powers = ss.powers();
632  return out << "spectrum power distribution: " << std::endl
633  << "waves " << waves << std::endl
634  << "powers: " << powers << std::endl;
635 }
636 }
ptracey::spd::rapply
spd rapply(const spd &s, const std::function< spd(spd, spd)> &eqfn, const std::function< bool(spd, WaveLength, Power)> &uneqfn) const
Definition: spd.hpp:380
ptracey::sampled_wave::values
std::vector< T > values
holds observed power values of the wave
Definition: wave.hpp:17
ptracey::sampled_wave
a wave sample representing a continuous wave in discreet form
Definition: wave.hpp:12
ptracey::sampled_wave::size
uint size() const
find the number of samples inside this wave
Definition: wave.hpp:486
rapidcsv::LabelParams
Datastructure holding parameters controlling which row and column should be treated as labels.
Definition: rapidcsv.hpp:276
rapidcsv::Document
Class representing a CSV document.
Definition: rapidcsv.hpp:361
ptracey::spd::spd
spd(uint wave_range, const std::function< WaveLength(uint)> &wavelength_generator, const std::function< Power(WaveLength)> &power_generator)
Definition: spd.hpp:125
ptracey::vec3
Definition: vec3.hpp:7
rapidcsv::SeparatorParams
Datastructure holding parameters controlling how the CSV data fields are separated.
Definition: rapidcsv.hpp:307
ptracey::spd
Definition: spd.hpp:13