220 lines
5.5 KiB
C++
220 lines
5.5 KiB
C++
// ColorMap class implementation
|
|
//
|
|
// A ubiquitous color map with contrast and brightness management.
|
|
//
|
|
// QATSH Copyright 2009 Jean-Philippe MEURET <jpmeuret@free.fr>
|
|
|
|
#include "ColorMap.h"
|
|
|
|
#include "ATSMath.h"
|
|
#include <iostream>
|
|
|
|
|
|
ColorMap::ColorMap()
|
|
: _eStyle(eStyleNumber), _dMinValue(0.0), _dMaxValue(0.0), _dContrast(0.0), _dBrightness(0.0)
|
|
{
|
|
}
|
|
|
|
ColorMap::ColorMap(ColorMap::EStyle eStyle, int nColors,
|
|
double dMinValue, double dMaxValue, double dContrast, double dBrightness)
|
|
{
|
|
reset(eStyle, nColors, dMinValue, dMaxValue, dContrast, dBrightness);
|
|
}
|
|
|
|
void ColorMap::reset(ColorMap::EStyle eStyle, int nColors,
|
|
double dMinValue, double dMaxValue, double dContrast, double dBrightness)
|
|
{
|
|
// Check requested temperature, nb of colors, value interval and contrast :
|
|
// no reset if any issue.
|
|
eStyle = (eStyle >= 0 && eStyle < eStyleNumber) ? eStyle : eStyleNumber;
|
|
if (eStyle == eStyleNumber)
|
|
return;
|
|
|
|
nColors = (nColors > 1 && nColors <= 256) ? nColors : 0;
|
|
if (nColors == 0)
|
|
return;
|
|
|
|
if (dMinValue >= dMaxValue)
|
|
return;
|
|
|
|
if (dContrast < 0 || dContrast > 1)
|
|
return;
|
|
|
|
if (dBrightness < 0 || dBrightness > 1)
|
|
return;
|
|
|
|
// Save scale properties.
|
|
_eStyle = eStyle;
|
|
_dMinValue = dMinValue;
|
|
_dMaxValue = dMaxValue;
|
|
_dContrast = dContrast;
|
|
_dBrightness = dBrightness;
|
|
|
|
// Clear color list.
|
|
_qlColors.clear();
|
|
|
|
// std::cout << "ColorMap::reset(contrast=" << _dContrast
|
|
// << ", brightness=" << _dBrightness<< std::endl;
|
|
|
|
// Fill-in color list according to requested properties.
|
|
switch(_eStyle)
|
|
{
|
|
// Medium : Use partial hue scale from 239=blue=cold to 0=red=hot)
|
|
case eStyleStandard:
|
|
{
|
|
double dT, dH, dV;
|
|
const double dContPower = pow(2.0, 10.0 * (_dContrast - 0.5));
|
|
const double dBrightPower = pow(2.0, -4.0);
|
|
|
|
for (int nColorInd = 0; nColorInd < nColors; nColorInd++)
|
|
{
|
|
dT = pow(nColorInd / (double)(nColors - 1), dContPower);
|
|
dH = (1.0 - dT) * 240.0 / 360;
|
|
dV = (0.5 + 0.5 * _dBrightness) * pow(dT, dBrightPower);
|
|
_qlColors.append(QColor::fromHsvF(dH, 1.0, dV));
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
// Cold : from ??? = cold to ??? = hot
|
|
case eStyleCold:
|
|
{
|
|
double dT, dR, dG, dB;
|
|
const double dBright = 0.25 + 0.75 * _dBrightness;
|
|
const double dContPower = pow(2.0, 10.0 * (_dContrast - 0.5));
|
|
|
|
for (int nColorInd = 0; nColorInd < nColors; nColorInd++)
|
|
{
|
|
dT = pow(nColorInd / (double)(nColors - 1), dContPower);
|
|
dR = dBright * dT;
|
|
dG = dBright * (1.0 - dT);
|
|
dB = dBright * 1.0;
|
|
_qlColors.append(QColor::fromRgbF(dR, dG, dB));
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Hot : from red = cold to yellowish white = hot
|
|
case eStyleHot:
|
|
{
|
|
double dR, dG, dB;
|
|
const int nStop = (int)floor(3.0 * nColors / 8.0);
|
|
const double dBright = 0.25 + 0.75 * _dBrightness;
|
|
const double dContPower = pow(2.0, 10.0 * (_dContrast - 0.5));
|
|
|
|
for (int nColorInd = 0; nColorInd < nColors; nColorInd++)
|
|
{
|
|
if (nColorInd < nStop)
|
|
{
|
|
dR = dBright * pow(nColorInd / (double)nStop, dContPower);
|
|
dG = dBright * 0.0;
|
|
dB = dBright * 0.0;
|
|
}
|
|
else if (nColorInd >= 2*nStop)
|
|
{
|
|
dR = dBright * 1.0;
|
|
dG = dBright * 1.0;
|
|
dB = dBright * pow((nColorInd - 2*nStop + 1) / (double)nStop, dContPower);
|
|
}
|
|
else
|
|
{
|
|
dR = dBright * 1.0;
|
|
dG = dBright * pow((nColorInd - nStop + 1) / (double)nStop, dContPower);
|
|
dB = dBright * 0.0;
|
|
}
|
|
_qlColors.append(QColor::fromRgbF(dR, dG, dB));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: // Should never get there
|
|
break;
|
|
}
|
|
}
|
|
|
|
int ColorMap::nbColors() const
|
|
{
|
|
return _qlColors.size();
|
|
}
|
|
|
|
void ColorMap::setNbColors(int nColors)
|
|
{
|
|
reset(_eStyle, nColors, _dMinValue, _dMaxValue, _dContrast, _dBrightness);
|
|
}
|
|
|
|
ColorMap::EStyle ColorMap::style() const
|
|
{
|
|
return _eStyle;
|
|
}
|
|
|
|
void ColorMap::setStyle(EStyle eStyle)
|
|
{
|
|
reset(eStyle, _qlColors.size(), _dMinValue, _dMaxValue, _dContrast, _dBrightness);
|
|
}
|
|
|
|
double ColorMap::minValue() const
|
|
{
|
|
return _dMinValue;
|
|
}
|
|
|
|
void ColorMap::setMinValue(double dValue)
|
|
{
|
|
reset(_eStyle, _qlColors.size(), dValue, _dMaxValue, _dContrast, _dBrightness);
|
|
}
|
|
|
|
double ColorMap::maxValue() const
|
|
{
|
|
return _dMaxValue;
|
|
}
|
|
|
|
void ColorMap::setMaxValue(double dValue)
|
|
{
|
|
reset(_eStyle, _qlColors.size(), _dMinValue, dValue, _dContrast, _dBrightness);
|
|
}
|
|
|
|
double ColorMap::contrast() const
|
|
{
|
|
return _dContrast;
|
|
}
|
|
|
|
void ColorMap::setContrast(double dValue)
|
|
{
|
|
reset(_eStyle, _qlColors.size(), _dMinValue, _dMaxValue, dValue, _dBrightness);
|
|
}
|
|
|
|
double ColorMap::brightness() const
|
|
{
|
|
return _dBrightness;
|
|
}
|
|
|
|
void ColorMap::setBrightness(double dValue)
|
|
{
|
|
reset(_eStyle, _qlColors.size(), _dMinValue, _dMaxValue, _dContrast, dValue);
|
|
}
|
|
|
|
// TODO: Contrast feature, through a contrast function to apply to input values
|
|
// before querying for the color.
|
|
// Normal contrast => linear function : y = a + b.x^1
|
|
// High contrast => exponential / square function : y = a + b.x^n
|
|
// Low contrast => logarythmic / square root function : y = a + b.x^1/n
|
|
|
|
QColor ColorMap::color(double dValue) const
|
|
{
|
|
//if (_eStyle == eStyleNumber || _dMinValue >= _dMaxValue)
|
|
//return QColor(Qt::black);
|
|
|
|
if (dValue < _dMinValue)
|
|
return _qlColors[0];
|
|
else if (dValue >= _dMaxValue)
|
|
return _qlColors[_qlColors.size() - 1];
|
|
else
|
|
return _qlColors[(int)floor(_qlColors.size() * (dValue - _dMinValue)
|
|
/ (_dMaxValue - _dMinValue))];
|
|
}
|
|
|
|
const QColor& ColorMap::color(int nIndex) const
|
|
{
|
|
return _qlColors[nIndex];
|
|
}
|