Générer des couleurs entre le rouge et le vert pour un wattmètre?


137

J'écris un jeu Java et je veux implémenter un wattmètre pour savoir à quel point vous allez tirer quelque chose.

J'ai besoin d'écrire une fonction qui prend un int entre 0 - 100, et en fonction de la hauteur de ce nombre, elle renverra une couleur entre le vert (0 sur l'échelle de puissance) et le rouge (100 sur l'échelle de puissance).

Similaire au fonctionnement des commandes de volume:
contrôle du volume

Quelle opération dois-je effectuer sur les composants rouge, vert et bleu d'une couleur pour générer les couleurs entre le vert et le rouge?

Donc, je pourrais courir dire, getColor(80)et il renverra une couleur orangée (ses valeurs en R, V, B) ou getColor(10)qui renverra une valeur RVB plus verte / jaune.

Je sais que j'ai besoin d'augmenter les composants des valeurs R, V, B pour une nouvelle couleur, mais je ne sais pas spécifiquement ce qui augmente ou diminue lorsque les couleurs passent du vert au rouge.


Le progrès:

J'ai fini par utiliser l'espace colorimétrique HSV / HSB parce que j'aimais mieux le dégradé (pas de brun foncé au milieu).

La fonction que j'ai utilisée était:

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

Où «puissance» est un nombre compris entre 0,0 et 1,0. 0.0 renvoie un rouge vif, 1.0 renvoie un vert brillant.

Graphique de teinte Java:
Graphique de teinte Java


J'ai déjà posé la même question (extrêmement similaire) ici: http://stackoverflow.com/questions/168838/color-scaling-function
Tomas Pajonk

2
Ne devriez-vous pas inverser le pouvoir? En supposant que le rouge est le plus fort et que vous travaillez entre 0,1 et 0,4, plus la puissance est élevée, plus le H est bas
Adriaan Davel

Utilisez-vous OpenGL? Comme il existe des moyens de définir les points d'un triangle sur des couleurs différentes, puis de les mélanger / dégradés entre eux. Vous obtenez probablement de meilleures performances en demandant à la carte graphique de faire le travail à votre place. De plus, le code pourrait être plus simple / plus adaptable (par exemple, si vous voulez qu'un capteur de puissance extraterrestre passe du vert au bleu)
DarcyThomas

Réponses:


203

Cela devrait fonctionner - il suffit de mettre à l'échelle linéairement les valeurs rouge et verte. En supposant que votre valeur maximale rouge / vert / bleu est 255, et nest dans la plage0 .. 100

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

(Modifié pour les maths entiers, pointe du chapeau à Ferrucio)

Une autre façon de faire serait d'utiliser un modèle de couleur HSV et de faire passer la teinte de 0 degrees(rouge) à 120 degrees(vert) avec la saturation et la valeur qui vous conviennent. Cela devrait donner un dégradé plus agréable.

Voici une démonstration de chaque technique - le dégradé supérieur utilise RVB, le bas utilise HSV:

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


20
C'est beaucoup, beaucoup mieux de faire cela dans l'espace HSV.
DJClayworth

@DJClayworth, oui, je suis allé avec HSV.
mmcdole

+1; J'allais poser une nouvelle question sur la façon d'améliorer un algorithme de couleur que j'ai pour colorer un graphique à barres en HTML / PHP ... Alors j'ai suggéré cette question comme similaire et votre réponse m'a aidé à résoudre le problème sans avoir à poser la question! Merci!
supplie le

Comment écririez-vous une fonction qui fonctionne avec n'importe quelle valeur de couleur? Dites, calculez la couleur entre RVB 20,30,190 et RBG 69,190,10?
Kokodoko le

1
même technique - pour passer de la valeur X à la valeur Y en N étapes, à chaque étape vous incrémentez X de (YX) / N - bien que cela soit plus joli si vous vous déplacez dans l'espace HSV plutôt que RVB
Paul Dixon

32

Du haut de ma tête, voici la transition de teinte vert-rouge dans l'espace HSV, traduite en RVB:

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)

Les valeurs rouge, verte et bleue de l'exemple ci-dessus sont des pourcentages, vous voudrez probablement les multiplier par 255 pour obtenir la plage 0-255 la plus utilisée.


12

Réponse courte Copy'n'Paste ...

Sur Java Std:

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

Sur Android:

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

Remarque: la valeur est un nombre compris entre 0 et 1 indiquant la condition du rouge au vert.


Pour convertir directement en un code couleur hexadécimal, vous voudrez peut-être tirer partiString.format("#%06X", 0xFFFFFF & getTrafficlightColor(value));
ngeek

10

Si vous voulez une représentation vert-jaune-rouge comme le suggère la réponse acceptée, jetez un œil à ceci.

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>


J'obtiens des valeurs égales pour différents pourcentages en utilisant ce code ... des idées? Par exemple, les pourcentages suivants renvoient tous RVB 1,255,0: 0,277, 0,222, 0,111 ... les valeurs les plus élevées (par exemple 1,0) renvoient les RVB corrects avec un vert plus clair, mais ils cessent de se terminer après un seuil et je n'obtiens que nuances de vert sur mon échelle.
Patrick

8

L'interpolation linéaire entre le vert et le rouge devrait presque fonctionner, sauf qu'au milieu, il y aura une couleur marron boueux.

La solution la plus flexible et la plus esthétique est d'avoir un fichier image quelque part qui a la rampe de couleurs exacte que vous voulez. Et puis recherchez la valeur de pixel là-dedans. Cela a la flexibilité que vous pouvez modifier le dégradé pour qu'il soit juste .

Si vous voulez toujours le faire à partir du code, il est probablement préférable d'interpoler entre les couleurs verte et jaune sur le côté gauche et entre le jaune et le rouge sur le côté droit. Dans l'espace RVB, le vert est (R = 0, G = 255, B = 0), le jaune est (R = 255, G = 255, B = 0), le rouge est (R = 255, G = 0, B = 0) ) - cela suppose que chaque composante de couleur passe de 0 à 255.


1
La suggestion ici de diviser le spectre en rouge / jaune et jaune / vert donne les résultats jaunes les plus agréables.
fig

4

J'ai créé une petite fonction qui vous donne la valeur entière rgb pour une valeur en pourcentage:

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

Donc, si votre currentValue est de 50 et votre maxValue de 100, cette fonction renverra la couleur dont vous avez besoin, donc si vous bouclez cette fonction avec une valeur de pourcentage, votre valeur de couleur passera du vert au rouge. Désolé pour la mauvaise explication


3

La réponse acceptée n'a même pas vraiment répondu à la question ...

C ++

Voici un simple segment de code C ++ de mon moteur qui interpole de manière linéaire et efficace entre trois couleurs arbitraires:

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

On interpolation_factorsuppose que le est dans la plage de 0.0 ... 1.0.
Les couleurs sont supposées être dans la plage de 0.0 ... 1.0(par exemple pour OpenGL).

C #

Voici la même fonction écrite 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)));
}

On interpolationFactorsuppose que le est dans la plage de 0.0 ... 1.0.
Les couleurs sont supposées être de l'ordre de 0 ... 255.


1
Le code C # a fonctionné très bien pour moi (vote positif ajouté) mais il y a une faute d'orthographe dedans interpolationFactorColor2 devrait être interpolationFactor2
DJIDave

Par souci d'exhaustivité, voici comment j'ai utilisé la fonction pour renvoyer RVB à MVC razr view double n = actualValue / maxValue; // Cela donne le rapport 0-1 var color = GetInterpolatedColor (n); return string.Concat ("#", colour.R.ToString ("X"), colour.G.ToString ("X"), colour.B.ToString ("X"));
DJIDave

@DJIDave: Wow, quelle erreur évidente et stupide. Je l'ai réparé. Merci!
Tara

2

Vous devez interpoler linéairement (LERP) les composants de couleur. Voici comment cela se fait en général, étant donné une valeur de départ v0 , une valeur de fin v1 et le rapport requis (un flottant normalisé entre 0,0 et 1,0):

v = v0 + ratio * (v1 - v0)

Cela donne v0 lorsque le rapport est de 0,0, v1 lorsque le rapport est de 1,0, et tout le reste dans les autres cas.

Vous pouvez le faire soit sur les composants RVB, soit en utilisant un autre jeu de couleurs, comme HSV ou HLS. Les deux derniers seront plus agréables visuellement, car ils fonctionnent sur des composants de teinte et de luminosité qui correspondent mieux à notre perception des couleurs.


2

Je dirais que vous voulez quelque chose entre un segment de ligne dans l'espace HSV (qui a un jaune légèrement trop brillant au centre) et un segment de ligne dans l'espace RVB (qui a un vilain brun au centre). J'utiliserais ceci, où power = 0donnera du vert, power = 50donnera un jaune légèrement terne et power = 100donnera du rouge.

blue = 0;
green = 255 * sqrt( cos ( power * PI / 200 ));
red = 255 * sqrt( sin ( power * PI / 200 )); 

2

Voici une solution de copier-coller pour les échelles Swift et HSV:

L'initialiseur UIColor accepte la teinte, la saturation et la luminosité dans [0, 1] donc pour une valeur donnée de [0, 1] nous avons:

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)

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

1

Si vous avez besoin de ce type de dégradé (en fait inversé) en CSS (version minimale 2.1), vous pouvez également utiliser HSL.

Supposons que vous soyez dans un modèle AngularJs et que la valeur soit un nombre de 0 à 1 et que vous souhaitiez styliser un élément (couleur d'arrière-plan ou de texte) ...

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

Première valeur: degrés (0 rouge, 120 vert) Deuxième valeur: saturation (50% est neutre) Troisième valeur: clarté (50% neutre)

Un bel article ici

Théorie sur Wikipedia ici


1

Voici la Objective-Cversion de la solution 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];
}

Où level serait une valeur entre 0.0et 1.0.
J'espère que cela aide quelqu'un!


1

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

Le pourcentage est bien sûr value / max_value. Ou si votre échelle ne commence pas à 0 alors (value - min_value) / (max_value - min_value).


1

JavaScript

J'utilise la visualisation Google avec un graphique à barres et je voulais que les barres soient vertes à rouges en fonction d'un pourcentage. Cela s'est avéré être la solution la plus propre que j'ai trouvée et fonctionne parfaitement.

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

Assurez-vous simplement que votre pourcentage est de 50 pour 50% et non de 0,50.


1

J'ai mis à jour la réponse acceptée en la divisant en première et seconde moitié de la plage (0,100) et en remplaçant 100 par un niveau maximum, afin que la plage puisse devenir dynamique. Le résultat est exactement comme avec le modèle HSV, mais toujours en utilisant RVB. La clé est d'avoir le (255,255,0) au milieu pour représenter la couleur jaune. J'ai combiné cette idée dans la réponse acceptée et un autre code VBA, alors réalisez-la dans VBA et utilisez-la dans Excel. J'espère que la logique aide et peut être utilisée dans d'autres langages / applications.

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

Salutations, Pavlin


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

Bien que cela puisse fonctionner, il serait utile que vous fournissiez des explications
David Glickman

0

Exemple autonome

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

0

Cela variera du rouge au jaune puis au vert la
valeur varie de 0 à 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];
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.