Wygenerować kolory między czerwonym a zielonym dla miernika mocy?

137

Piszę grę Java i chcę zaimplementować miernik mocy określający, jak mocno zamierzasz coś zrobić.

Muszę napisać funkcję, która przyjmuje wartość między 0-100 i na podstawie tego, jak wysoka jest ta liczba, zwróci kolor między zielonym (0 na skali potęgi) a czerwonym (100 na skali potęgi).

Podobnie jak w przypadku regulacji głośności:
kontrola głośności

Jaką operację muszę wykonać na składnikach czerwonego, zielonego i niebieskiego koloru, aby wygenerować kolory między zielonym i czerwonym?

Mogę więc uruchomić, powiedzmy, getColor(80)i zwróci pomarańczowy kolor (jego wartości w R, G, B) lub getColor(10)który zwróci bardziej zielono / żółtą wartość RGB.

Wiem, że muszę zwiększyć składowe wartości R, G, B dla nowego koloru, ale nie wiem konkretnie, co rośnie lub spada, gdy kolory zmieniają się z zielono-czerwony.


Postęp:

Skończyło się na tym, że użyłem przestrzeni kolorów HSV / HSB, ponieważ bardziej podobał mi się gradient (brak ciemnych brązów w środku).

Funkcja, której użyłem, to:

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

Gdzie „moc” to liczba z przedziału od 0,0 do 1,0. 0.0 zwróci jasnoczerwony, 1.0 zwróci jasnozielony.

Wykres odcieni Java:
Wykres odcieni Java

mmcdole
źródło
Wcześniej zadałem to samo (bardzo podobne) pytanie tutaj: http://stackoverflow.com/questions/168838/color-scaling-function
Tomas Pajonk
2
Czy nie powinieneś odwracać mocy? Zakładając, że kolor czerwony jest najwyższym trafieniem i pracujesz między 0,1 a 0,4, im wyższa moc, tym niższa wartość H
Adriaan Davel
Czy używasz OpenGL? Ponieważ istnieją sposoby ustawiania punktów trójkąta na różne kolory, a następnie mieszania / gradientu między nimi. Prawdopodobnie uzyskasz lepszą wydajność, prosząc kartę graficzną o wykonanie pracy za Ciebie. Również kod mógłby być prostszy / bardziej elastyczny (powiedzmy, jeśli chcesz, aby miernik mocy obcych
zmienił

Odpowiedzi:

203

To powinno działać - po prostu skaluj liniowo wartości czerwonego i zielonego. Zakładając, że twoja maksymalna wartość czerwonego / zielonego / niebieskiego wynosi 255i nznajduje się w zakresie0 .. 100

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

(Zmieniono dla matematyki całkowitej, czubek kapelusza do Ferrucia)

Innym sposobem byłoby użycie modelu kolorów HSV i przełączanie odcienia od 0 degrees(czerwonego) do 120 degrees(zielonego) z dowolnym nasyceniem i wartością, które Ci odpowiadają. Powinno to dać bardziej przyjemny gradient.

Oto demonstracja każdej techniki - górny gradient używa RGB, dolny używa HSV:

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

Paul Dixon
źródło
20
O wiele, dużo lepiej jest to zrobić w przestrzeni HSV.
DJClayworth
@DJClayworth, tak, poszedłem z HSV.
mmcdole
+1; Zamierzałem zadać nowe pytanie, jak ulepszyć algorytm kolorów, który mam do kolorowania wykresu słupkowego w HTML / PHP ... SO zasugerował to pytanie jako podobne, a Twoja odpowiedź pomogła mi rozwiązać problem bez konieczności zadawania go! Dzięki!
zaczyna się
Jak napisałbyś funkcję działającą z dowolną wartością koloru? Powiedzmy, oblicz kolor między RGB 20,30,190 a RBG 69,190,10?
Kokodoko,
1
ta sama technika - aby przejść od wartości X do wartości Y w N krokach, z każdym krokiem zwiększasz X o (YX) / N - chociaż będzie to wyglądać ładniej, jeśli będziesz poruszać się w przestrzeni HSV, a nie w RGB
Paul Dixon
32

Z góry mojej głowy, oto przejście zielono-czerwonego odcienia w przestrzeni HSV, przetłumaczone na 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)

Wartości czerwonego, zielonego i niebieskiego w powyższym przykładzie to wartości procentowe. Prawdopodobnie chciałbyś pomnożyć je przez 255, aby uzyskać najczęściej używany zakres 0-255.

Rafał Dowgird
źródło
12

Krótka kopia i wklej odpowiedź ...

W standardzie Java:

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

W systemie Android:

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

uwaga: wartość to liczba z przedziału od 0 do 1, wskazująca stan od czerwonego do zielonego.

mschmoock
źródło
Aby bezpośrednio przekonwertować na kod koloru szesnastkowego, możesz String.format("#%06X", 0xFFFFFF & getTrafficlightColor(value));
użyć
10

Jeśli chcesz uzyskać zielono-żółto-czerwoną reprezentację, jak sugeruje zaakceptowana odpowiedź, spójrz na to.

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
źródło
Otrzymuję równe wartości dla różnych wartości procentowych za pomocą tego kodu ... jakieś pomysły? Na przykład poniższe wartości procentowe zwracają RGB 1,255,0: 0,277, 0,222, 0,111 ... wyższe wartości (na przykład 1,0) zwracają prawidłowe RGB z jaśniejszą zielenią, ale przestają kończyć się po progu i otrzymuję tylko odcienie zieleni w mojej skali.
Patrick,
8

Liniowa interpolacja między zielonym a czerwonym prawie powinna działać, poza tym, że w środku będzie mętnobrązowy kolor.

Najbardziej elastycznym i najlepiej wyglądającym rozwiązaniem jest umieszczenie pliku obrazu w miejscu, które ma dokładnie taką skalę kolorów, jaką chcesz. Następnie wyszukaj tam wartość piksela. Zapewnia to elastyczność, dzięki której można dostosować gradient, aby był odpowiedni .

Jeśli nadal chcesz to zrobić z kodu, prawdopodobnie najlepiej jest interpolować między zielonym i żółtym kolorem po lewej stronie oraz między żółtym i czerwonym po prawej stronie. W przestrzeni RGB zielony to (R = 0, G = 255, B = 0), żółty to (R = 255, G = 255, B = 0), czerwony to (R = 255, G = 0, B = 0 ) - przy założeniu, że każdy składnik koloru przechodzi od 0 do 255.

NeARAZ
źródło
1
Sugestia podziału widma na czerwony / żółty i żółty / zielony daje najbardziej przyjemne żółte wyniki.
rys.
4

Zrobiłem małą funkcję, która podaje wartość całkowitą rgb dla wartości procentowej:

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

Więc jeśli twoja currentValue to 50, a twoja maxValue to 100, ta funkcja zwróci kolor, którego potrzebujesz, więc jeśli zapętlisz tę funkcję z wartością procentową, twoja wartość koloru zmieni się z zielonego na czerwony. Przepraszam za złe wyjaśnienie

Mikołaja
źródło
3

Przyjęta odpowiedź nie odpowiadała nawet na pytanie ...

C ++

Oto prosty segment kodu w C ++ z mojego silnika, który liniowo i wydajnie interpoluje między trzema dowolnymi kolorami:

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

interpolation_factorZakłada się w przedziale 0.0 ... 1.0.
Zakłada się, że kolory mieszczą się w zakresie 0.0 ... 1.0(np. Dla OpenGL).

DO#

Oto ta sama funkcja napisana w 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)));
}

interpolationFactorZakłada się w przedziale 0.0 ... 1.0.
Zakłada się, że kolory mieszczą się w zakresie 0 ... 255.

Tara
źródło
1
Kod C # działał dla mnie genialnie (dodano upvote), ale jest w nim błąd w pisowni interpolationFactorColor2 powinien być interpolationFactor2
DJIDave
Również dla kompletności w ten sposób wykorzystałem funkcję do zwrócenia RGB do widoku razr MVC double n = currentValue / maxValue; // To daje stosunek 0-1 var color = GetInterpolatedColor (n); return string.Concat ("#", colour.R.ToString ("X"), colour.G.ToString ("X"), colour.B.ToString ("X"));
DJIDave
@DJIDave: Wow, co za oczywisty i głupi błąd. Naprawiłem to. Dzięki!
Tara
2

Musisz interpolować liniowo (LERP) składowe koloru. Oto jak to się ogólnie robi, biorąc pod uwagę wartość początkową v0 , wartość końcową v1 i wymagany współczynnik (znormalizowany zmiennoprzecinkowy między 0,0 a 1,0):

v = v0 + ratio * (v1 - v0)

Daje to v0, gdy współczynnik wynosi 0,0, v1, gdy współczynnik wynosi 1,0, i wszystko pomiędzy w pozostałych przypadkach.

Możesz to zrobić na komponentach RGB lub przy użyciu innego schematu kolorów, takiego jak HSV lub HLS. Dwie ostatnie będą bardziej przyjemne wizualnie, ponieważ działają na komponentach odcienia i jasności, które lepiej odwzorowują naszą percepcję kolorów.

efotinis
źródło
2

Powiedziałbym, że potrzebujesz czegoś pomiędzy segmentem linii w przestrzeni HSV (który ma nieco zbyt jasny żółty pośrodku) a segmentem linii w przestrzeni RGB (który ma brzydki brąz w środku). Użyłbym tego, gdzie power = 0da zielony, power = 50da lekko matowy żółty i power = 100da czerwony.

blue = 0;
green = 255 * sqrt( cos ( power * PI / 200 ));
red = 255 * sqrt( sin ( power * PI / 200 )); 
Dawood ibn Kareem
źródło
2

Oto rozwiązanie do kopiowania i wklejania dla skali Swift i HSV:

Inicjator UIColor przyjmuje odcień, nasycenie i jasność w [0, 1], więc dla podanej wartości z [0, 1] mamy:

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
źródło
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
źródło
1

Jeśli potrzebujesz tego rodzaju gradientu (faktycznie odwróconego) w CSS (min wersja 2.1), możesz również użyć HSL.

Przypuśćmy, że jesteś w szablonie AngularJs, a wartość to liczba od 0 do 1 i chcesz nadać styl elementowi (kolor tła lub tekstu) ...

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

Pierwsza wartość: stopnie (0 czerwonych, 120 zielonych) Druga wartość: nasycenie (50% jest neutralne) Trzecia wartość: jasność (50% neutralne)

Ładny artykuł tutaj

Teoria na Wikipedii tutaj

lucabelluccini
źródło
1

Oto Objective-Cwersja rozwiązania 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];
}

Gdzie poziom byłby wartością między 0.0a 1.0.
Mam nadzieję, że to komuś pomoże!

S1LENT WARRIOR
źródło
1

W Pythonie 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)

Oczywiście procent value / max_value. Lub jeśli twoja skala nie zaczyna się od 0, wtedy (value - min_value) / (max_value - min_value).

Christophera Galpina
źródło
1

JavaScript

Używam Google Visualization z wykresem słupkowym i chciałem, aby słupki były od zielonego do czerwonego w procentach. Okazało się, że jest to najczystsze rozwiązanie, jakie znalazłem i działa idealnie.

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

Po prostu upewnij się, że Twój procent wynosi 50 na 50%, a nie 0,50.

Patrick
źródło
1

Zaktualizowałem zaakceptowaną odpowiedź, dzieląc ją na pierwszą i drugą połowę zakresu (0,100) i zastępując 100 poziomem maksymalnym, aby zakres mógł stać się dynamiczny. Rezultat jest dokładnie taki sam, jak w modelu HSV, ale nadal używa RGB. Kluczem jest umieszczenie (255,255,0) pośrodku, aby reprezentować żółty kolor. Połączyłem ten pomysł w zaakceptowanej odpowiedzi i innym kodzie VBA, więc osiągnij to w VBA i użyj w Excelu. Mam nadzieję, że logika pomaga i może być używana w innych językach / aplikacjach.

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

Pozdrawiam, Pavlin

Pavlin Todorov
źródło
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
źródło
Chociaż może to zadziałać, byłoby pomocne, gdybyś podał jakieś wyjaśnienie
David Glickman
0

Samodzielny przykład

<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>
Amnon
źródło
0

Będzie się zmieniać od czerwonego do żółtego, a następnie do zielonego, w zakresie
od 0 do 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
źródło