aboutsummaryrefslogtreecommitdiff
#include "colorscale.h"

#include <cmath>

const float EPSILON = 1e-3f;

Color::Color()
    : Color(0, 0, 0, 255)
{
}

Color::Color(int r, int g, int b)
    : Color(r, g, b, 255)
{
}

Color::Color(int r, int g, int b, int a)
    : r(r)
    , g(g)
    , b(b)
    , a(a)
{
}

Color::Color(float r, float g, float b)
    : Color(r, g, b, 0.0f)
{
}

Color::Color(float r, float g, float b, float a)
    : Color(static_cast<int>(round(255 * r)),
            static_cast<int>(round(255 * g)),
            static_cast<int>(round(255 * b)),
            static_cast<int>(round(255 * a)))
{
}

void Color::getRgbF(float *r, float *g, float *b) const
{
    *r = static_cast<float>(this->r) / 255.0f;
    *g = static_cast<float>(this->g) / 255.0f;
    *b = static_cast<float>(this->b) / 255.0f;
}

void Color::getRgbF(float *r, float *g, float *b, float *a) const
{
    getRgbF(r, g, b);
    *a = static_cast<float>(this->a) / 255.0f;
}

void Color::setRgb(int r, int g, int b)
{
    setRgb(r, g, b, 255);
}

void Color::setRgb(int r, int g, int b, int a)
{
    this->r = r;
    this->g = g;
    this->b = b;
    this->a = a;
}

void Color::setRgbF(float r, float g, float b)
{
    setRgb(static_cast<int>(round(255 * r)),
           static_cast<int>(round(255 * g)),
           static_cast<int>(round(255 * b)));
}

void Color::setRgbF(float r, float g, float b, float a)
{
    setRgb(static_cast<int>(round(255 * r)),
           static_cast<int>(round(255 * g)),
           static_cast<int>(round(255 * b)),
           static_cast<int>(round(255 * a)));
}

ColorScale::ColorScale(const Color &firstColor, const Color &lastColor)
    : m_colors{{firstColor, lastColor}}
{
    setExtents(0.0f, 1.0f);
}

ColorScale::ColorScale(std::initializer_list<Color> colors)
    : m_colors(colors)
{
    setExtents(0.0f, 1.0f);
}

ColorScale::ColorScale(const std::vector<Color> &colors)
    : m_colors(colors)
{
    setExtents(0.0f, 1.0f);
}

ColorScale::~ColorScale()
{
}

void ColorScale::setExtents(float min, float max)
{
    if (min >= max) {
        return;
    }

    m_min = min;
    m_max = max;
}

Color ColorScale::lerp(const Color &c1, const Color &c2, float _t)
{
    float r1, g1, b1, a1;
    float r2, g2, b2, a2;
    float t = _t;

    c1.getRgbF(&r1, &g1, &b1, &a1);
    c2.getRgbF(&r2, &g2, &b2, &a2);
    Color color;
    color.setRgbF(r1 * (1.0f - t) + r2 * t,
                  g1 * (1.0f - t) + g2 * t,
                  b1 * (1.0f - t) + b2 * t,
                  a1 * (1.0f - t) + a2 * t);
    return color;
}

Color ColorScale::color(float t) const
{
    if (t < m_min || t > m_max) {
        return Color();
    }

    // normalize t
    t = (t - m_min) / (m_max - m_min);

    // two colors, use a simpler solution
    if (m_colors.size() == 2) {
        return lerp(m_colors.front(), m_colors.back(), t);
    }

    if (fabs(t - m_min) < EPSILON) {
        return m_colors.front();
    }

    if (fabs(t - m_max) < EPSILON) {
        return m_colors.back();
    }

    // find which colors in the scale are adjacent to ours
    int i = int(t * m_colors.size());
    int j = i + 1;
    if (i >= m_colors.size() - 1) {
        return Color(m_colors.back());
    }

    // normalize t between the two colors
    float step = 1.0f / m_colors.size();
    t = (t - i*step) / (j*step - i*step);
    return lerp(m_colors[i], m_colors[j], t);
}