Search code examples
javacolorsscale

java: represent range of float values on a color scale


I'm trying to create a function that will translate float values to a color. I created a simple linear scale:

float value;
float maxValue;

float scaleStep = maxValue / 5;

if (value < scaleStep) {
    color = blue
}

if (value > scaleStep && value <= scaleStep * 2) {
    color = green
}

if (value > scaleStep * 2 && value <= scaleStep * 3) {
    color = yellow
}

if (value > scaleStep * 3 && value <= scaleStep * 4) {
    color = orange
}

if (value > scaleStep * 4 && value <= scaleStep * 5) {
    color = red
}

but since most (but not all) of the values from the sets that I'm trying to represent are in close proximity from one particular value, graphical representation using linear scale isn't very useful (almost everything is translated to one color).

How can I create a non-linear scale so that differences between the values are more visible?


Solution

  • Interpolation is what you want. Interpolation generates samples between known samples in a dataset.

    Here, your known samples are your colors; blue, green, yellow, orange, and red. The colors between those known colors are what you're looking for.

    Here's a link to a nice visualizer of interpolation functions.

    And here's a few interpolation functions for your convenience. Play with them, find the one that works best for you!

    public float linearInterpolation(float start, float end, float normalizedValue) {
        return start + (end - start) * normalizedValue;
    }
    
    public float sinInterpolation(float start, float end, float normalizedValue){
        return (start+(end-start)* (1 - Math.cos(normalizedValue * Math.PI)) / 2;
    }
    
    //usage
    linearInterpolation(red, green, .5f);//halfway between red and green.
    //same with other demonstrations.
    

    Edit:

    Here, start and end refer to a starting and ending sample. The normalizedValue is some value between [0, 1] inclusive (that means it can equal exactly 0 or 1, or any value in between 0 and 1. That's what the term normalized means typically.)

    So, for you, start and end will be two colors, and normalizedValue will represent how near you are to the starting or ending color.

    Take linearInterpolation for example.

    red = 1;
    green = 2;
    float midway = 1 + (2 - 1) * .5;
    //midway = 1.5, which is halfway between red and green.
    float allRed = 1 + (2 - 1) * 0;
    //allRed = 1, which is the value of red (or start)
    float allGreen = 1 + (2 - 1) * 1;
    //allGreen = 2, which is the value of green (or end)
    

    So, for linear interpolation, the closer the normalizedValue is to 1, the nearer the returned value it is to end. The closer normalizedValue is to 0, the closer the returned value is to start.

    This isn't necessarily true for other interpolation functions. You can think linear interpolation as a simple line segment connecting values. Want a value halfway between those segments? Use a normalized value of .5, viola!

    Other functions might have steeper slopes, or even oscillate between start and end!

    Try and stop thinking in terms of color, and start thinking more abstractly. Colors are a certain distance apart. Interpolation helps you define what values lie in the distance between them.