Algorithm to convert any positive integer to an RGB value

后端 未结 10 1296
没有蜡笔的小新
没有蜡笔的小新 2020-12-04 13:49

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

10条回答
  •  失恋的感觉
    2020-12-04 14:24

    You want to convert your data values to a frequency of light:

    • lower wavelength = cooler colors = blueish
    • higher wavelength = warmer colors = redder

    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);
    

提交回复
热议问题