Introduction
A class that interpolates between a single value (an unsigned 16 bit integer) and an RGB color value on a color scale that can be defined.
Source code
Due to the simplicity of the class, I'm displaying it here so that you don't have to download a zip file and extract that. This code should go into a file called Gradient.h.
#ifndef GRADIENT_H_
#define GRADIENT_H_
#include <vector>
typedef struct rgb {
uint8_t red;
uint8_t green;
uint8_t blue;
bool operator==(rgb lhr)
{
return this == &lhr;
}
}rgb;
static rgb INVALID_COLOR = {0, 0, 0};
class Gradient
{
private:
uint16_t m_min;
rgb m_minColor;
rgb m_minOutlierColor;
uint16_t m_max;
rgb m_maxColor;
rgb m_maxOutlierColor;
std::vector<rgb> m_stops;
rgb interpolate(rgb c1, rgb c2, float normalized_value){
if( normalized_value <= 0.0 ){ return c1; }
if( normalized_value >= 1.0 ){ return c2; }
uint8_t red = (uint8_t)((1.0-normalized_value)*c1.red +
normalized_value*c2.red);
uint8_t green = (uint8_t)((1.0-normalized_value)*c1.green +
normalized_value*c2.green);
uint8_t blue = (uint8_t)((1.0-normalized_value)*c1.blue +
normalized_value*c2.blue);
return (rgb){red, green, blue};
}
public:
Gradient(){}
void initialize(uint16_t min, uint16_t max, std::vector<rgb> stops,
rgb minOutlierColor=INVALID_COLOR, rgb maxOutlierColor=INVALID_COLOR){
m_min = min;
m_max = max;
m_stops = stops;
m_minOutlierColor = minOutlierColor;
m_maxOutlierColor = maxOutlierColor;
}
Gradient(uint16_t min, uint16_t max, std::vector<rgb> stops,
rgb minOutlierColor=INVALID_COLOR, rgb maxOutlierColor=INVALID_COLOR){
initialize(min, max, stops, minOutlierColor, maxOutlierColor);
}
virtual ~Gradient(){}
rgb getRgb(uint16_t value){
if( value < m_min ){ return m_minOutlierColor ==
INVALID_COLOR ? m_stops.front() : m_minOutlierColor; }
if( value > m_max ){ return m_maxOutlierColor ==
INVALID_COLOR ? m_stops.back() : m_maxOutlierColor; }
uint16_t range = m_max - m_min;
uint16_t v = value - m_min;
float step = range / (float)(m_stops.size()-1);
int bin = (int)(v / step);
float normalized_v = (v - bin*step) / step;
return interpolate(m_stops[bin], m_stops[bin+1], normalized_v);
}
};
#endif
Using the code
Here's an example of the code in action:
#include <Gradient.h>
[...]
std::vector<rgb> stops;
stops.push_back((rgb){255,255,255});
stops.push_back((rgb){255,0,0});
stops.push_back((rgb){255,255,0});
stops.push_back((rgb){0,255,0});
stops.push_back((rgb){0,0,255});
stops.push_back((rgb){75,25,150});
Gradient grad;
grad.initialize(0x000f, 0xfff0, stops, (rgb){0,0,0}, (rgb){0,0,0});
rgb color = grad.getRgb(123456);
Known bugs
The Gradient
class currently only works for interpolating uint16_t
integral values to color. It's quite possible that this could be made more flexible (for example, by making the class a template class). There are some other potential bugs in it that have to do with an integer overflow, so any comments or improvements on the code are welcome.