#include "Gradient.h" #include "CubicSpline.h" #include #include #include #include #include Gradient::Gradient(void) : max{ 1.0 } { } Gradient::Gradient(std::vector> colors, bool repeat, int precalcSteps) : repeat{ repeat } { if(colors.empty() || colors.size() < 2) return; std::sort(colors.begin(), colors.end(), [] (const auto& a, const auto& b) { return a.second < b.second; }); max = colors.at(colors.size() - 1).second; std::vector> linearColors; std::transform(colors.begin(), colors.end(), std::back_inserter(linearColors), [] (auto c) { return c; }); std::vector> rs; std::vector> gs; std::vector> bs; std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(rs), [] (auto p) { return std::pair{ p.second, p.first.r }; }); std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(gs), [] (auto p) { return std::pair{ p.second, p.first.g }; }); std::transform(linearColors.begin(), linearColors.end(), std::back_inserter(bs), [] (auto p) { return std::pair{ p.second, p.first.b }; }); CubicSpline rsp(rs, false); CubicSpline gsp(gs, false); CubicSpline bsp(bs, false); if(precalcSteps <= 0) { precalcSteps = int(max * 15) + 10; } for (int i = 0; i < precalcSteps; i++) { float position = i * max / precalcSteps; RGBColorf at = { rsp.interpolateAt(position), gsp.interpolateAt(position), bsp.interpolateAt(position) }; this->colors.push_back(at); } } Gradient Gradient::defaultGradient(void) { QFile res(":/gradients/default"); res.open(QIODevice::ReadOnly); QString str = QString::fromUtf8(res.readAll()); return readXml(str); } Gradient Gradient::readXml(const QString& xml) { QDomDocument xsr; xsr.setContent(xml); auto elem = xsr.documentElement(); auto repeatAttr = elem.attributeNode("repeat"); bool repeat = !repeatAttr.isNull() && repeatAttr.value().toLower() == "true"; auto colors = xsr.elementsByTagName("color"); std::vector> colorArr; for (int i = 0; i < colors.length(); ++i) { auto child = colors.item(i).toElement(); uint8_t r = uint8_t(child.attributeNode("r").value().toInt()); uint8_t g = uint8_t(child.attributeNode("g").value().toInt()); uint8_t b = uint8_t(child.attributeNode("b").value().toInt()); float p = child.attributeNode("p").value().toInt(); //printf("rgb (%s): %d, %d, %d\n", child.text().toUtf8().data(), r, g, b); colorArr.push_back({ { r, g, b }, p }); } return Gradient(std::move(colorArr), repeat); } RGBColor Gradient::get(float x) const { if (colors.empty()) return RGBColor(); /*const auto [left, right, lerp] = getNeighbors(x); RGBColor lerped = lerpColors(left, right, lerp); return lerped;*/ if (x < 0) return colors[0]; if (x > this->max) { if (repeat) x = ::fmodf(x, this->max); } float pos = x * colors.size() / max; if (pos < 0) { pos = 0; } if (pos > colors.size() - 1) { pos = colors.size() - 1; } int left = int(pos); int right = int(pos + 1); float lerp = pos - left; if (lerp < 1e-5f) { return colors.at(left); } else { return lerpColors(colors.at(left), colors.at(right), lerp); } } RGBColorf Gradient::lerpColors(RGBColorf a, RGBColorf b, float val) { return RGBColorf { b.r * val + a.r * (1 - val), b.g * val + a.g * (1 - val), b.b * val + a.b * (1 - val) }; } RGBColor Gradient::lerpColors(RGBColor a, RGBColor b, float val) { return RGBColor{ lerpColors(RGBColorf{ a }, RGBColorf{ b }, val) }; } /*std::tuple Gradient::getNeighbors(float x) const { for (auto it = colors.begin(); it != colors.end(); ++it) { if (it->second > x) { if (it == colors.begin()) { return { it->first, it->first, 0 }; } else { float lerp = (x - (it - 1)->second) / (it->second - (it - 1)->second); return { (it - 1)->first, it->first, lerp }; } } } return { (colors.end() - 1)->first, (colors.end() - 1)->first, 0 }; }*/