// ColorMap class implementation // // A ubiquitous color map with contrast and brightness management. // // QATSH Copyright 2009 Jean-Philippe MEURET #include "ColorMap.h" #include "ATSMath.h" #include 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]; }