¿Generar colores entre rojo y verde para un medidor de potencia?

137

Estoy escribiendo un juego Java y quiero implementar un medidor de potencia para determinar qué tan duro vas a disparar algo.

Necesito escribir una función que tome un int entre 0 y 100, y en función de qué tan alto sea ese número, devolverá un color entre Verde (0 en la escala de potencia) y Rojo (100 en la escala de potencia).

Similar a cómo funcionan los controles de volumen:
control del volumen

¿Qué operación debo hacer en los componentes Rojo, Verde y Azul de un color para generar los colores entre Verde y Rojo?

Entonces, podría ejecutar say, getColor(80)y devolverá un color anaranjado (sus valores en R, G, B) o getColor(10)que devolverá un valor RGB más verde / amarillo.

Sé que necesito aumentar los componentes de los valores R, G, B para un nuevo color, pero no sé específicamente qué sube o baja a medida que los colores cambian de Verde a Rojo.


Progreso:

Terminé usando el espacio de color HSV / HSB porque me gustaba más el gradiant (no hay marrones oscuros en el medio).

La función que utilicé fue:

public Color getColor(double power)
{
    double H = power * 0.4; // Hue (note 0.4 = Green, see huge chart below)
    double S = 0.9; // Saturation
    double B = 0.9; // Brightness

    return Color.getHSBColor((float)H, (float)S, (float)B);
}

Donde "potencia" es un número entre 0.0 y 1.0. 0.0 devolverá un rojo brillante, 1.0 devolverá un verde brillante.

Java Hue Chart:
Java Hue Chart

mmcdole
fuente
Previamente hice la misma pregunta (extremadamente similar) aquí: http://stackoverflow.com/questions/168838/color-scaling-function
Tomas Pajonk
2
¿No deberías invertir la potencia? Asumiendo que el rojo es el golpe más alto, y usted está trabajando entre 0.1 y 0.4, cuanto mayor sea el poder, menor será el H
Adriaan Davel el
¿Estás usando OpenGL? Como hay formas de establecer los puntos de un triángulo en diferentes colores y luego mezclar / degradar entre ellos. Probablemente obtendrá un mejor rendimiento pidiéndole a la tarjeta gráfica que haga el trabajo por usted. Asimismo, el código podría ser más simple / más adaptables (que si quieres un medidor de potencia extranjeros para ir de verde a azul)
DarcyThomas

Respuestas:

203

Esto debería funcionar, solo escale linealmente los valores rojo y verde. Asumiendo que su valor máximo rojo / verde / azul está 255y nestá dentro del rango0 .. 100

R = (255 * n) / 100
G = (255 * (100 - n)) / 100 
B = 0

(Modificado para matemáticas enteras, punta del sombrero para Ferrucio)

Otra forma de hacerlo sería utilizar un modelo de color HSV y cambiar el tono de 0 degrees(rojo) a 120 degrees(verde) con la saturación y el valor que le convenga. Esto debería dar un gradiente más agradable.

Aquí hay una demostración de cada técnica: el gradiente superior usa RGB, el inferior usa HSV:

http://i38.tinypic.com/29o0q4k.jpg

Paul Dixon
fuente
20
Es mucho, mucho mejor hacer esto en el espacio HSV.
DJClayworth
@DJClayworth, sí, fui con HSV.
mmcdole
+1; Iba a hacer una nueva pregunta sobre cómo mejorar un algoritmo de color que tengo para colorear un gráfico de barras en HTML / PHP ... ¡SO sugirió esta pregunta como similar y su respuesta me ayudó a solucionar el problema sin tener que hacer la pregunta! ¡Gracias!
ruega el
¿Cómo escribirías una función que funciona con cualquier valor de color? Digamos, calcule el color entre RGB 20,30,190 y RBG 69,190,10.
Kokodoko
1
misma técnica: para pasar del valor X al valor Y en N pasos, cada paso incrementa X en (YX) / N, aunque se verá mejor si se mueve en el espacio HSV en lugar de RGB
Paul Dixon
32

En la parte superior de mi cabeza, aquí está la transición de tono verde-rojo en el espacio HSV, traducida a RGB:

blue = 0.0
if 0<=power<0.5:        #first, green stays at 100%, red raises to 100%
    green = 1.0
    red = 2 * power
if 0.5<=power<=1:       #then red stays at 100%, green decays
    red = 1.0
    green = 1.0 - 2 * (power-0.5)

Los valores rojo, verde y azul en el ejemplo anterior son porcentajes, probablemente desee multiplicarlos por 255 para obtener el rango 0-255 más utilizado.

Rafał Dowgird
fuente
12

Respuesta breve de copiar y pegar ...

En Java Std:

int getTrafficlightColor(double value){
    return java.awt.Color.HSBtoRGB((float)value/3f, 1f, 1f);
}

En Android:

int getTrafficlightColor(double value){
    return android.graphics.Color.HSVToColor(new float[]{(float)value*120f,1f,1f});
}

nota: el valor es un número entre 0 y 1 que indica la condición de rojo a verde.

mschmoock
fuente
Para convertir directamente a un código de color hexadecimal, puede aprovecharString.format("#%06X", 0xFFFFFF & getTrafficlightColor(value));
ngeek
10

Si desea una representación verde-amarilla-roja como sugiere la respuesta aceptada, eche un vistazo a esto.

http://jsfiddle.net/0awncw5u/2/

function percentToRGB(percent) {
    if (percent === 100) {
        percent = 99
    }
    var r, g, b;

    if (percent < 50) {
        // green to yellow
        r = Math.floor(255 * (percent / 50));
        g = 255;

    } else {
        // yellow to red
        r = 255;
        g = Math.floor(255 * ((50 - percent % 50) / 50));
    }
    b = 0;

    return "rgb(" + r + "," + g + "," + b + ")";
}


function render(i) {
    var item = "<li style='background-color:" + percentToRGB(i) + "'>" + i + "</li>";
    $("ul").append(item);
}

function repeat(fn, times) {
    for (var i = 0; i < times; i++) fn(i);
}


repeat(render, 100);
li {
    font-size:8px;
    height:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<ul></ul>

Blowsie
fuente
Estoy obteniendo valores iguales para diferentes porcentajes usando este código ... ¿alguna idea? Por ejemplo, los siguientes porcentajes devuelven RGB 1.255,0: 0.277, 0.222, 0.111 ... los valores más altos (por ejemplo 1.0) devuelven los RGB correctos con un verde más brillante, pero dejan de desaparecer después de un umbral y solo obtengo sombras de verde en mi escala.
Patrick
8

La interpolación lineal entre verde y rojo casi debería funcionar, excepto que en el medio habrá un color marrón fangoso.

La solución más flexible y de mejor aspecto es tener un archivo de imagen en algún lugar que tenga la rampa de color exacta que desea. Y luego busque el valor de píxel allí. Esto tiene la flexibilidad de que puede ajustar el gradiente para que sea perfecto .

Si aún desea hacerlo desde el código, entonces probablemente sea mejor interpolar entre los colores verde y amarillo en el lado izquierdo, y entre amarillo y rojo en el lado derecho. En el espacio RGB, el verde es (R = 0, G = 255, B = 0), el amarillo es (R = 255, G = 255, B = 0), el rojo es (R = 255, G = 0, B = 0 ): se supone que cada componente de color va de 0 a 255.

NeARAZ
fuente
1
La sugerencia aquí de dividir el espectro en Rojo / Amarillo y Amarillo / Verde da los resultados amarillos más agradables.
fig
4

Hice una pequeña función que le da el valor entero rgb para un valor porcentual:

private int getGreenToRedGradientByValue(int currentValue, int maxValue)
{
    int r = ( (255 * currentValue) / maxValue );
    int g = ( 255 * (maxValue-currentValue) ) / maxValue;
    int b = 0;
    return ((r&0x0ff)<<16)|((g&0x0ff)<<8)|(b&0x0ff);
}

Entonces, si su valor actual es 50 y su valor máximo es 100, esta función le devolverá el color que necesita, por lo que si realiza un bucle de esta función con un valor porcentual, su valor de color pasará de verde a rojo. Perdón por la mala explicación.

Nicholas
fuente
3

La respuesta aceptada ni siquiera respondió la pregunta ...

C ++

Aquí hay un segmento de código C ++ simple de mi motor que interpola lineal y eficientemente entre tres colores arbitrarios:

const MATH::FLOAT4 color1(0.0f, 1.0f, 0.0f, 1.0f);  // Green
const MATH::FLOAT4 color2(1.0f, 1.0f, 0.0f, 1.0f);  // Yellow
const MATH::FLOAT4 color3(1.0f, 0.0f, 0.0f, 1.0f);  // Red

MATH::FLOAT4 get_interpolated_color(float interpolation_factor)
{
    const float factor_color1 = std::max(interpolation_factor - 0.5f, 0.0f);
    const float factor_color2 = 0.5f - fabs(0.5f - interpolation_factor);
    const float factor_color3 = std::max(0.5f - interpolation_factor, 0.0f);

    MATH::FLOAT4 color;

    color.x = (color1.x * factor_color1 +
               color2.x * factor_color2 +
               color3.x * factor_color3) * 2.0f;

    color.y = (color1.y * factor_color1 +
               color2.y * factor_color2 +
               color3.y * factor_color3) * 2.0f;

    color.z = (color1.z * factor_color1 +
               color2.z * factor_color2 +
               color3.z * factor_color3) * 2.0f;

    color.w = 1.0f;

    return(color);
}

Se interpolation_factorsupone que está en el rango de 0.0 ... 1.0.
Se supone que los colores están en el rango de 0.0 ... 1.0(por ejemplo, para OpenGL).

C#

Aquí está la misma función escrita en C #:

private readonly Color mColor1 = Color.FromArgb(255, 0, 255, 0);
private readonly Color mColor2 = Color.FromArgb(255, 255, 255, 0);
private readonly Color mColor3 = Color.FromArgb(255, 255, 0, 0);

private Color GetInterpolatedColor(double interpolationFactor)
{
    double interpolationFactor1 = Math.Max(interpolationFactor - 0.5, 0.0);
    double interpolationFactor2 = 0.5 - Math.Abs(0.5 - interpolationFactor);
    double interpolationFactor3 = Math.Max(0.5 - interpolationFactor, 0.0);

    return (Color.FromArgb(255,
                (byte)((mColor1.R * interpolationFactor1 +
                        mColor2.R * interpolationFactor2 +
                        mColor3.R * interpolationFactor3) * 2.0),

                (byte)((mColor1.G * interpolationFactor1 +
                        mColor2.G * interpolationFactor2 +
                        mColor3.G * interpolationFactor3) * 2.0),

                (byte)((mColor1.B * interpolationFactor1 +
                        mColor2.B * interpolationFactor2 +
                        mColor3.B * interpolationFactor3) * 2.0)));
}

Se interpolationFactorsupone que está en el rango de 0.0 ... 1.0.
Se supone que los colores están en el rango de 0 ... 255.

Tara
fuente
1
El código C # funcionó de manera brillante para mí (se agregó un
voto positivo
También para completar, así es como utilicé la función para devolver RGB a MVC razr view double n = actualValue / maxValue; // Esto da la relación 0-1 var color = GetInterpolatedColor (n); return string.Concat ("#", color.R.ToString ("X"), color.G.ToString ("X"), color.B.ToString ("X"));
DJIDave
@DJIDave: Wow, qué error tan obvio y estúpido. Lo he arreglado ¡Gracias!
Tara
2

Necesita interpolar linealmente (LERP) los componentes de color. Así es como se hace en general, dado un valor inicial v0 , un valor final v1 y la relación requerida (un flotador normalizado entre 0.0 y 1.0):

v = v0 + ratio * (v1 - v0)

Esto da v0 cuando la relación es 0.0, v1 cuando la relación es 1.0 y todo lo demás en los otros casos.

Puede hacer esto en los componentes RGB o utilizando algún otro esquema de color, como HSV o HLS. Los dos últimos serán más agradables visualmente, ya que trabajan en componentes de tono y brillo que se asignan mejor a nuestra percepción del color.

efotinis
fuente
2

Diría que quiere algo entre un segmento de línea en el espacio HSV (que tiene un amarillo ligeramente brillante en el centro) y un segmento de línea en el espacio RGB (que tiene un marrón feo en el centro). Yo usaría esto, donde power = 0dará verde, power = 50dará un amarillo ligeramente opaco y power = 100dará rojo.

blue = 0;
green = 255 * sqrt( cos ( power * PI / 200 ));
red = 255 * sqrt( sin ( power * PI / 200 )); 
Dawood ibn Kareem
fuente
2

Aquí hay una solución de copiar y pegar para la escala Swift y HSV:

El inicializador UIColor acepta tono, saturación y brillo en [0, 1], por lo que para el valor dado de [0, 1] tenemos:

let hue:        CGFloat = value / 3
let saturation: CGFloat = 1 // Or choose any
let brightness: CGFloat = 1 // Or choose any
let alpha:      CGFloat = 1 // Or choose any

let color = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha)
Stanislav Pankevich
fuente
1
import java.awt.Color;

public class ColorUtils {

    public static Color interpolate(Color start, Color end, float p) {
        float[] startHSB = Color.RGBtoHSB(start.getRed(), start.getGreen(), start.getBlue(), null);
        float[] endHSB = Color.RGBtoHSB(end.getRed(), end.getGreen(), end.getBlue(), null);

        float brightness = (startHSB[2] + endHSB[2]) / 2;
        float saturation = (startHSB[1] + endHSB[1]) / 2;

        float hueMax = 0;
        float hueMin = 0;
        if (startHSB[0] > endHSB[0]) {
            hueMax = startHSB[0];
            hueMin = endHSB[0];
        } else {
            hueMin = startHSB[0];
            hueMax = endHSB[0];
        }

        float hue = ((hueMax - hueMin) * p) + hueMin;

        return Color.getHSBColor(hue, saturation, brightness);
    }
}
mawi
fuente
1

Si necesita este tipo de gradiente (en realidad invertido) en CSS (versión mínima 2.1), también puede usar HSL.

Supongamos que está en una plantilla de AngularJs y el valor es un número de 0 a 1 y desea diseñar un elemento (fondo o color de texto) ...

hsl({{ value * 120}}, 50%, 35%)

Primer valor: grados (0 rojo, 120 verde) Segundo valor: saturación (50% es neutral) Tercer valor: ligereza (50% neutral)

Un buen articulo aqui

Teoría en Wikipedia aquí

lucabelluccini
fuente
1

Aquí está la Objective-Cversión de la solución de Simucal:

- (UIColor*) colorForCurrentLevel:(float)level
{
    double hue = level * 0.4; // Hue (note 0.4 = Green)
    double saturation = 0.9; // Saturation
    double brightness = 0.9; // Brightness

    return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
}

Donde el nivel sería un valor entre 0.0y 1.0.
¡Espero que esto ayude a alguien!

SOLDADO GUERRERO
fuente
1

En Python 2.7:

import colorsys

def get_rgb_from_hue_spectrum(percent, start_hue, end_hue):
    # spectrum is red (0.0), orange, yellow, green, blue, indigo, violet (0.9)
    hue = percent * (end_hue - start_hue) + start_hue
    lightness = 0.5
    saturation = 1
    r, g, b = colorsys.hls_to_rgb(hue, lightness, saturation)
    return r * 255, g * 255, b * 255

# from green to red:
get_rgb_from_hue_spectrum(percent, 0.3, 0.0)

# or red to green:
get_rgb_from_hue_spectrum(percent, 0.0, 0.3)

El porcentaje es, por supuesto value / max_value. O si su escala no comienza en 0 entonces (value - min_value) / (max_value - min_value).

Christopher Galpin
fuente
1

JavaScript

Estoy usando Google Visualization con gráfico de barras y quería que las barras fueran de verde a rojo según un porcentaje. Esta resultó ser la solución más limpia que encontré y funciona perfectamente.

function getGreenToRed(percent){
    r = percent<50 ? 255 : Math.floor(255-(percent*2-100)*255/100);
    g = percent>50 ? 255 : Math.floor((percent*2)*255/100);
    return 'rgb('+r+','+g+',0)';
}

Solo asegúrese de que su porcentaje sea 50 por 50% y no 0.50.

Patricio
fuente
1

He actualizado la respuesta aceptada dividiéndola en la primera y segunda mitad del rango (0,100) y sustituyendo 100 por un nivel máximo, para que el rango pueda volverse dinámico. El resultado es exactamente como con el modelo HSV, pero sigue utilizando RGB. La clave es tener el (255,255,0) en el medio para representar el color amarillo. He combinado esta idea en la respuesta aceptada y otro código de VBA, así que consíguelo en VBA y úselo en Excel. Espero que la lógica ayude y pueda usarse en otros idiomas / aplicaciones.

Sub UpdateConditionalFormatting(rng As Range)
    Dim cell As Range
    Dim max As Integer

    max = WorksheetFunction.max(rng)

    For Each cell In rng.Cells

    If cell.Value >= 0 And cell.Value < max / 2 Then
        cell.Interior.Color = RGB(255 * cell.Value / (max / 2), 255, 0)
    ElseIf cell.Value >= max / 2 And cell.Value <= max Then
        cell.Interior.Color = RGB(255, 255 * ((max) - cell.Value) / (max / 2), 0)
    End If

    Next cell
End Sub

Saludos, Pavlin

Pavlin Todorov
fuente
1

VB

Public Function GetPercentageColor( _
  ByVal iPercent As Long, Optional _
  ByVal bOpposit As Boolean) As Long
' 0->100% - Green->Yellow->Red
' bOpposit - Red->Yellow->Green

If bOpposit Then iPercent = (100 - iPercent)

Select Case iPercent
Case Is < 1: GetPercentageColor = 65280 ' RGB(0, 255, 0)
Case Is > 99: GetPercentageColor = 255  ' RGB(255, 0, 0)
Case Is < 50: GetPercentageColor = RGB(255 * iPercent / 50, 255, 0)
Case Else: GetPercentageColor = RGB(255, (255 * (100 - iPercent)) / 50, 0)
End Select

End Function
MartiniB
fuente
Si bien esto puede funcionar, sería útil si proporcionara alguna explicación
David Glickman
0

Ejemplo autocontenido

<html>
<head>
<script>
//--------------------------------------------------------------------------
function gradient(left, mid, right)
{
    var obj = {}

    var lt50 = {"r":(mid.r-left.r)/50.0,
                "g":(mid.g-left.g)/50.0,
                "b":(mid.b-left.b)/50.0}
    var gt50 = {"r":(right.r-mid.r)/50.0,
                "g":(right.g-mid.g)/50.0,
                "b":(right.b-mid.b)/50.0}

    obj.getColor = function(percent) {
        if (percent == 50.0) {
            return mid;
        }
        if (percent < 50.0) {
            return "rgb("+Math.floor(left.r+lt50.r*percent+0.5)+","+
                          Math.floor(left.g+lt50.g*percent+0.5)+","+
                          Math.floor(left.b+lt50.b*percent+0.5)+")";
        }
        var p2 = percent-50.0;
        return "rgb("+Math.floor(mid.r+gt50.r*p2+0.5)+","+
                      Math.floor(mid.g+gt50.g*p2+0.5)+","+
                      Math.floor(mid.b+gt50.b*p2+0.5)+")";
    }

    return obj;
}

//--------------------------------------------------------------------------
var g_gradient = gradient( {"r":255, "g":20, "b":20},  // Left is red
                           {"r":255, "g":255, "b":20}, // Middle is yellow
                           {"r":20, "g":255, "b":20} ); // right is green

//--------------------------------------------------------------------------
function updateColor()
{
    var percent = document.getElementById('idtext').value.length;
    var oscore = document.getElementById('idscore');

    if (percent > 100.0) {
        percent = 100.0;
    }
    if (percent < 0.0) {
        percent = 0.0;
    }
    var col = g_gradient.getColor(percent)
    oscore.style['background-color'] = col;
    oscore.innerHTML = percent + '%';
}

</script>
</head>
<body onLoad="updateColor()">
<input size='100' placeholder='type text here' id='idtext' type="text" oninput="updateColor()" />
<br />
<br />
<div id='idscore' style='text-align:center; width:200px; border-style:solid;
     border-color:black; border-width:1px; height:20px;'> </div>
</body>
</html>
Amnón
fuente
0

Esto variará de rojo a amarillo y luego a verde el
valor varía de 0 a 100

-(UIColor*) redToGreenColorWithPosition:(int) value {

    double R, G;
    if (value > 50) {
        R = (255 * (100 - value)/ 50) ;
        G = 255;
    }else {
        R = 255;
        G = (255 * (value*2)) / 100;
    }

    return [UIColor colorWithRed:R/255.0f green:G/255.0f blue:0.0f alpha:1.0f];
}
Simon Fernandes
fuente