Gradient.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #include "Gradient.h"
  2. #include "CubicSpline.h"
  3. #include <cmath>
  4. #include <algorithm>
  5. #include <functional>
  6. #include <QtXml/QDomDocument>
  7. #include <QFile>
  8. Gradient::Gradient(void) :
  9. max{ 1.0 }
  10. {
  11. }
  12. Gradient::Gradient(std::vector<std::pair<RGBColor, float>> colors, bool repeat, int precalcSteps) :
  13. repeat{ repeat }
  14. {
  15. if(colors.empty() || colors.size() < 2)
  16. return;
  17. std::sort(colors.begin(), colors.end(),
  18. [] (const auto& a, const auto& b) {
  19. return a.second < b.second;
  20. });
  21. max = colors.at(colors.size() - 1).second;
  22. std::vector<std::pair<RGBColorf, float>> linearColors;
  23. std::transform(colors.begin(), colors.end(), std::back_inserter(linearColors),
  24. [] (auto c) { return c; });
  25. std::vector<std::pair<float, float>> rs;
  26. std::vector<std::pair<float, float>> gs;
  27. std::vector<std::pair<float, float>> bs;
  28. std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(rs),
  29. [] (auto p) { return std::pair{ p.second, p.first.r }; });
  30. std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(gs),
  31. [] (auto p) { return std::pair{ p.second, p.first.g }; });
  32. std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(bs),
  33. [] (auto p) { return std::pair{ p.second, p.first.b }; });
  34. CubicSpline rsp(rs, false);
  35. CubicSpline gsp(gs, false);
  36. CubicSpline bsp(bs, false);
  37. if(precalcSteps <= 0) {
  38. precalcSteps = int(max * 15) + 10;
  39. }
  40. for (int i = 0; i < precalcSteps; i++) {
  41. float position = i * max / precalcSteps;
  42. RGBColorf at = {
  43. rsp.interpolateAt(position),
  44. gsp.interpolateAt(position),
  45. bsp.interpolateAt(position)
  46. };
  47. this->colors.push_back(at);
  48. }
  49. }
  50. Gradient Gradient::defaultGradient(void)
  51. {
  52. QFile res(":/gradients/default");
  53. res.open(QIODevice::ReadOnly);
  54. QString str = QString::fromUtf8(res.readAll());
  55. return readXml(str);
  56. }
  57. Gradient Gradient::readXml(const QString& xml)
  58. {
  59. QDomDocument xsr;
  60. xsr.setContent(xml);
  61. auto elem = xsr.documentElement();
  62. auto repeatAttr = elem.attributeNode("repeat");
  63. bool repeat = !repeatAttr.isNull() && repeatAttr.value().toLower() == "true";
  64. auto colors = xsr.elementsByTagName("color");
  65. std::vector<std::pair<RGBColor, float>> colorArr;
  66. for (int i = 0; i < colors.length(); ++i) {
  67. auto child = colors.item(i).toElement();
  68. uint8_t r = uint8_t(child.attributeNode("r").value().toInt());
  69. uint8_t g = uint8_t(child.attributeNode("g").value().toInt());
  70. uint8_t b = uint8_t(child.attributeNode("b").value().toInt());
  71. float p = child.attributeNode("p").value().toInt();
  72. //printf("rgb (%s): %d, %d, %d\n", child.text().toUtf8().data(), r, g, b);
  73. colorArr.push_back({ { r, g, b }, p });
  74. }
  75. return Gradient(std::move(colorArr), repeat);
  76. }
  77. RGBColor Gradient::get(float x) const
  78. {
  79. if (colors.empty() || std::isnan(x) || std::isinf(x))
  80. return RGBColor();
  81. /*const auto [left, right, lerp] = getNeighbors(x);
  82. RGBColor lerped = lerpColors(left, right, lerp);
  83. return lerped;*/
  84. if (x < 0)
  85. return colors[0];
  86. if (x > this->max) {
  87. if (repeat)
  88. x = ::fmodf(x, this->max);
  89. else
  90. x = this->max;
  91. }
  92. float pos = x * colors.size() / max;
  93. if (pos < 0) {
  94. pos = 0;
  95. }
  96. if (pos > colors.size() - 1) {
  97. pos = colors.size() - 1;
  98. }
  99. int left = int(pos);
  100. int right = int(pos + 1);
  101. float lerp = pos - left;
  102. if (lerp < 1e-5f) {
  103. return colors[left];
  104. }
  105. else {
  106. return lerpColors(colors[left], colors[right], lerp);
  107. }
  108. }
  109. RGBColorf Gradient::lerpColors(RGBColorf a, RGBColorf b, float val)
  110. {
  111. return RGBColorf {
  112. b.r * val + a.r * (1 - val),
  113. b.g * val + a.g * (1 - val),
  114. b.b * val + a.b * (1 - val)
  115. };
  116. }
  117. RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val)
  118. {
  119. return RGBColor{ lerpColors(RGBColorf{ a }, RGBColorf{ b }, val) };
  120. }
  121. /*std::tuple<RGBColor, RGBColor, float> Gradient::getNeighbors(float x) const
  122. {
  123. for (auto it = colors.begin(); it != colors.end(); ++it) {
  124. if (it->second > x) {
  125. if (it == colors.begin()) {
  126. return { it->first, it->first, 0 };
  127. }
  128. else {
  129. float lerp = (x - (it - 1)->second) / (it->second - (it - 1)->second);
  130. return { (it - 1)->first, it->first, lerp };
  131. }
  132. }
  133. }
  134. return { (colors.end() - 1)->first, (colors.end() - 1)->first, 0 };
  135. }*/