We have a heatmap we want to display. The numbers that will make up the values being displayed are unknown (except that they will be positive integers). The range of numbers
man, you could probably use YUV color space and only for demonstration purposes convert it to RGB.
You want to convert your data values to a frequency of light:
The frequencies of visible light go from about 350nm (violet) to 650nm (red):
(source: gamonline.com)
The following function converts numbers in your specified range to the the range of visible light, then gets the rgb:
function DataPointToColor(Value, MinValue, MaxValue: Real): TColor;
var
r, g, b: Byte;
WaveLength: Real;
begin
WaveLength := GetWaveLengthFromDataPoint(Value, MinValue, MaxValue);
WavelengthToRGB(Wavelength, r, g, b);
Result := RGB(r, g, b);
end;
With the function i wrote off the top of my head:
function GetWaveLengthFromDataPoint(Value: Real; MinValues, MaxValues: Real): Real;
const
MinVisibleWaveLength = 350.0;
MaxVisibleWaveLength = 650.0;
begin
//Convert data value in the range of MinValues..MaxValues to the
//range 350..650
Result := (Value - MinValue) / (MaxValues-MinValues) *
(MaxVisibleWavelength - MinVisibleWavelength) +
MinVisibleWaveLength;
end;
And a function i found on the internets, that converts a wavelength into RGB:
PROCEDURE WavelengthToRGB(CONST Wavelength: Nanometers;
VAR R,G,B: BYTE);
CONST
Gamma = 0.80;
IntensityMax = 255;
VAR
Blue : DOUBLE;
factor : DOUBLE;
Green : DOUBLE;
Red : DOUBLE;
FUNCTION Adjust(CONST Color, Factor: DOUBLE): INTEGER;
BEGIN
IF Color = 0.0
THEN RESULT := 0 // Don't want 0^x = 1 for x <> 0
ELSE RESULT := ROUND(IntensityMax * Power(Color * Factor, Gamma))
END {Adjust};
BEGIN
CASE TRUNC(Wavelength) OF
380..439:
BEGIN
Red := -(Wavelength - 440) / (440 - 380);
Green := 0.0;
Blue := 1.0
END;
440..489:
BEGIN
Red := 0.0;
Green := (Wavelength - 440) / (490 - 440);
Blue := 1.0
END;
490..509:
BEGIN
Red := 0.0;
Green := 1.0;
Blue := -(Wavelength - 510) / (510 - 490)
END;
510..579:
BEGIN
Red := (Wavelength - 510) / (580 - 510);
Green := 1.0;
Blue := 0.0
END;
580..644:
BEGIN
Red := 1.0;
Green := -(Wavelength - 645) / (645 - 580);
Blue := 0.0
END;
645..780:
BEGIN
Red := 1.0;
Green := 0.0;
Blue := 0.0
END;
ELSE
Red := 0.0;
Green := 0.0;
Blue := 0.0
END;
// Let the intensity fall off near the vision limits
CASE TRUNC(Wavelength) OF
380..419: factor := 0.3 + 0.7*(Wavelength - 380) / (420 - 380);
420..700: factor := 1.0;
701..780: factor := 0.3 + 0.7*(780 - Wavelength) / (780 - 700)
ELSE factor := 0.0
END;
R := Adjust(Red, Factor);
G := Adjust(Green, Factor);
B := Adjust(Blue, Factor)
END {WavelengthToRGB};
Sample use:
Data set in the range of 10..65,000,000. And this particular data point has a value of 638,328:
color = DataPointToColor(638328, 10, 65000000);
Without knowing the range of values, there isn't much you can do to come up with a meaningful function mapping an arbitrary range of positive integers to a heat-map type range of colors.
I think you're going to have to run through your data at least once to get the min/max or know them ahead of time. Once you have that you can normalize appropriately and use any number of color schemes. The simplest solution would be to specify something like "hue" and convert from HSV to RGB.
A little late, but I was trying to do the same and found I can modify HSV to RGB to get a similar result. It's similar to the wavelength approach but by passes the need to convert to wavelength first. Just substitute H with your value (assuming a value between 0 and 1), and fix S and V to 1. I found the HSVtoRGB example here to be very helpful:
http://www.cs.rit.edu/~ncs/color/t_convert.html
However, I had to change the lines
h /= 60;
i = floor ( h );
to
h *= 5;
i = (int) h;
to get an output that goes through the whole spectrum.
Additional resource: http://www.easyrgb.com/index.php?X=MATH&H=21#text21