Algorithm Question: Need to dynamically INCREMENT from 00FF00 to FF0000 over TIME, C#/Java

StackOverflow https://stackoverflow.com/questions/668263

  •  21-08-2019
  •  | 
  •  

Question

I want to change the color Bright Green to Dark Red over time (240 hours). The best way I can see is to change the hex combo from 00FF00 to FF0000.

I don't know how to dynamically count up to FF0000 from 00FF00 for the life of me. I'm looking over a 10 day period, so most likely over 240 hours to increment.

Can anyone help me out?

I never took an algorithms class so I think that might have something to do with this problem.

If you have a better way to do this, then let me know.

I am looking for some kind of code here. Thanks guys. It can be in any language but will inevitably be converted to C#.

Was it helpful?

Solution

Just think about it in terms of the components. Even though it looks like one big hex number, it's actually three side-by-side.

At the start, red is 0, green is 255 (FF), blue is 0.
At the end, red is 255, green is 0, blue is 0.

So, every (amount of time you have / 255), increment red by 1 and decrement green by 1.

OTHER TIPS

If you are going from bright colour to bright colour as the hex values suggest, then you may wish to interpolate in HSV space, rather than RGB space. HSV space approximates how we think about colour - hue, saturation and value. RGB space approximates how the light sensitive cells in our eyes work.

The upper gradient is linear RGB interpolation from FF0000 to 00FF00. Its middle value is 7f7f00, a muddy brown.

The middle gradient is a linear interpolation in HSV space. As both FF0000 and 00FF00 are fully saturated and have the same value (brightness), the interpolation keeps the same brightness and saturation throughout, so the centre value is bright yellow ffff00.

The third alternative would be a vector rotation in RGB space, which would mean the mid-value is B4B400, ( B4 hex = 180 dec = 255 / sqrt(2) ), which is somewhere between the two effects. This is done by calculating the magnitude of each end point, then scaling the result of the RGB linear interpolation so it is the same magnitude, effectively sweeping the vector in an arc in the plane of the two colours and the origin. As we don't actually weigh different colours equally for brightness, or see linearly, it's not exact, but it does have a fairly even intensity across its sweep, whereas the HSV one is a bit lighter in the middle, as it's got two values at 100%.

removed dead Imageshack link


In Java, where you have HSB support the algorithm is simple - get the HSB of the end values, linear interpolate them as in the other RGB answers, then convert create a colour with the h,s,v values:

static Color hsvInterpolate ( float mix, Color c0, Color c1 ) {
    float[] hsv0 = new float[3];
    float[] hsv1 = new float[3];

    float alt = 1.0f - mix;

    Color.RGBtoHSB( c0.getRed(), c0.getGreen(), c0.getBlue(), hsv0 );
    Color.RGBtoHSB( c1.getRed(), c1.getGreen(), c1.getBlue(), hsv1 );

    float h = mix * hsv0 [ 0 ] +  alt * hsv1 [ 0 ];
    float s = mix * hsv0 [ 1 ] +  alt * hsv1 [ 1 ];
    float v = mix * hsv0 [ 2 ] +  alt * hsv1 [ 2 ];

    return Color.getHSBColor ( h, s, v );
}

I don't believe C# has the conversions built in, so the code isn't much use really.

static Color vectorInterpolate ( float mix, Color c0, Color c1 ) {
    float alt = 1.0f - mix;

    double x0 = c0.getRed();
    double y0 = c0.getGreen();
    double z0 = c0.getBlue();

    double x1 = c1.getRed();
    double y1 = c1.getGreen();
    double z1 = c1.getBlue();

    double mag0 = sqrt( x0*x0 + y0*y0 + z0*z0 );
    double mag1 = sqrt( x1*x1 + y1*y1 + z1*z1 );

    double x = mix * x0 + alt * x1;
    double y = mix * y0 + alt * y1;
    double z = mix * z0 + alt * z1;

    double mag  = mix * mag0 + alt * mag1;
    double scale = mag / sqrt( x*x + y*y + z*z );

    return new Color ( 
        clamp ( x * scale ),
        clamp ( y * scale ),
        clamp ( z * scale ) );
}

static int clamp ( double value ) {
    int x = (int) round ( value );

    if ( x > 255 ) return 255;
    if ( x < 0 ) return 0;
    return x;
}

You probably want to find the intersection of the vector with the edge of the RGB cube rather than simply clamping it, but in this case it doesn't matter either way.


As an addendum, it is also worth considering HSY space, which is closer to perceived brightness, as illustrated by Dave Green's cube helix colour interpolations.

time_remaining (value varies from 0 to 239)

green = 255 * (time_remaining / 239)
red = 255 - green
blue = 0

color = (red, green, blue)

here's a quick java answer from red to green (ofc you can change that) value is the current valuein time and all is the sum of the time ...

public static String progressiveColor(int value, int all){

    int red = 255 - (int)((float)(value*255)/(float)all);
    int green = (int)((float)(value*255)/(float)all);
    return String.format("#%06X", (0xFFFFFF & Color.argb(255, red, green, 0)));

}

Here is Pete's Java code translated into C# in case anyone was looking for that. It works great for my purposes (black to dark red and back).

    static Color VectorInterpolate(float mix, Color c0, Color c1)
    {
        float alt = 1.0f - mix;

        double x0 = c0.R;
        double y0 = c0.G;
        double z0 = c0.B;

        double x1 = c1.R;
        double y1 = c1.G;
        double z1 = c1.B;

        double mag0 = Math.Sqrt(x0 * x0 + y0 * y0 + z0 * z0);
        double mag1 = Math.Sqrt(x1 * x1 + y1 * y1 + z1 * z1);

        double x = mix * x0 + alt * x1;
        double y = mix * y0 + alt * y1;
        double z = mix * z0 + alt * z1;

        double mag = mix * mag0 + alt * mag1;
        double scale = mag / Math.Sqrt(x * x + y * y + z * z);

        return Color.FromRgb(Clamp(x * scale), Clamp(y * scale), Clamp(z * scale));
    }

    static byte Clamp(double value)
    {
        var x = (int)Math.Round(value);

        if (x > 255) return 255;
        if (x < 0) return 0;
        return (byte) x;
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top