Algorithm: How do I fade from Red to Green via Yellow using RGB values?

Viewed 69045

I want to display a color based on a value from 0 to 100. At one end (100), it's pure Red, the other end (0), pure Green. In the middle (50), I want it to be yellow.

And I want the colors to fade gradually from one to another, such that at 75, the color is half red and half yellow, etc.

How do I program the RGB values to reflect this fading? Thanks.

17 Answers

This method (in c#, but can be easily translated to other languages) will take a percentage and list of colors and return the color on the gradient based on your percentage. When you pass in the colors, they need to be in order from 0 value to 100 value (so you would want to pass Green, Yellow, Red - in that order). If you find that you want different or more colors in the middle, just add them to the list of colors you pass in the order you want them to appear.

public Color ColorBasedOnPercent(decimal percent, params Color[] colors)
    {
        if (colors.Length == 0)
        {
            //I am using Transparent as my default color if nothing was passed
            return Color.Transparent;
        }
        if (percent > 1)
        {
            percent = percent / 100;
        }

        //find the two colors within your list of colors that the percent should fall between
        var colorRangeIndex = (colors.Length - 1) * percent;
        var minColorIndex = (int)Math.Truncate(colorRangeIndex);
        var maxColorIndex = minColorIndex + 1;
        var minColor = colors[minColorIndex];

        if (maxColorIndex < colors.Length)
        {
            var maxColor = colors[maxColorIndex];

            //get the differences between all the color values for the two colors you are fading between
            var aScale = maxColor.A - minColor.A;
            var redScale = maxColor.R - minColor.R;
            var greenScale = maxColor.G - minColor.G;
            var blueScale = maxColor.B - minColor.B;

            //the decimal distance of how "far" this color should be from the minColor in the range
            var gradientPct = colorRangeIndex - minColorIndex;

            //for each piece of the color (ARGB), add a percentage(gradientPct) of the distance between the two colors
            int getRGB(int originalRGB, int scale) => (int)Math.Round(originalRGB + (scale * gradientPct));

            return Color.FromArgb(getRGB(minColor.A, aScale), getRGB(minColor.R, redScale), getRGB(minColor.G, greenScale), getRGB(minColor.B, blueScale));
        }
        return minColor;
    }

I mapped values of CPU speed between 1400 MHz and 3500 MHz to rgb() values to get from green -> yellow -> red with this function

function green_yellow_red(core_MHz, core_id){
  var core_color = ~~core_MHz.map(1400, 3500, 0, 510)

  if(core_color < 255){
    $('#cpu_core_'+core_id).css('background', 'rgb('+core_color+',255 , 0)')
  }else{
    core_color-=255
    $('#cpu_core_'+core_id).css('background', 'rgb(255 ,'+ (255-core_color) +', 0)')
  }
}

More of the same. Just Delphi Pascal coded and simplified+locked to semaphore colors (red/yellow/green).

rectangle1.Fill.Color:=DefineColorSemaphore(newcolor);

function TForm1.DefineColorSemaphore(valperc:integer):TAlphaColor;
var
   vcol: TAlphaColorRec;
begin
  vcol.B := 0;    // blue:  always 0
  vcol.A := 255;  // alpha: 255=no
  if (valperc < 50) then
    begin
       // starts @ RGB=255,0,0 and increases G 0->255
       vcol.R := 255;
       vcol.G := trunc(255*valperc/50);
    end
    else
    begin
       // starts @ RGB=255,255,0 and decreases R 255->0
       vcol.R := 255-trunc(255* (valperc - 50)/50);
       vcol.G := 255;
    end;
  result:= TAlphaColor(vcol);
end;

Recently I was planning to do samething using Javascript. I have followed two steps.

  1. First scale the value in 0-1 range(scaledValue = (value - min) / (max - min))
  2. Then increase value of Red channel if the value is less than or equal 0.5 and then decrease value of Green channel if the value is greater than 0.5

Here is the code I used for this purpose.

var blue = 0.0, red = 0.0, green = 0.0;

if(scaledValue <= 0.5)
{
    red = (scaledValue * 2) * 255.0;
    green = 255.0;
    blue = 0;
}else
{
    red = 255.0;
    green = 255.0 + 255.0 - ((scaledValue  * 2)* 255);
    blue = 0;
}

A sample code for showing this works in C++

This solution is inspired by @jterrace answer.

// To fill an OwnerDraw control with red-yellow-green gradient upon WM_DRAWITEM

int cx = lpDis->rcItem.right - lpDis->rcItem.left;

for (int x = 0; x < cx; x++)
{
    COLORREF cr;

    double d = 255 * 2 * (double)x/(double)cx;

    cr = x <= cx/2 ?    RGB(255,     d,   0) :
                        RGB(255 - d, 255, 0);

    HPEN hPen = CreatePen(PS_SOLID, 1, cr);
    HPEN hOldPen = (HPEN)SelectObject(lpDis->hDC, hPen);
    MoveToEx(lpDis->hDC, x, lpDis->rcItem.top, NULL);
    LineTo(lpDis->hDC, x, lpDis->rcItem.bottom);
    SelectObject(lpDis->hDC, hOldPen);
    DeleteObject(hPen);
}

Illustration

If you are on python then this might help...


def rgb(p):# <-- percentage as parameter
    #Starting with color red
    d = [255,0,0]
    #formula for finding green value by percentage
    d[1] = int((510*p)/100)
    print(d[1])
    #if green value more than 255
    #set green value 255
    #reduce the red value from remaining green value
    if d[1]>255:
        d[0] -= d[1]-255
        d[1] = 255
        
    return d

print(rgb(0))
Related