Zeliax
Zeliax

Reputation: 5386

Calculating color value (r,g,b) using javascript

I have x amounts of arrays containing speed values.

The values are in m/s and I don't know how many there are and how large they are. I need to create a function to decide on what color to use.

My idea was to find the max and min value of the array (the speeds), and since I know the values are all dividable with 0.25 I wanted to count the possible amount of "steps" by doing var steps = (max - min) / 0.25

Since I have an RGB spectrum I thought I could somehow calculate what value to use, but I simply can't wrap my head around what to do.

What I want to be able to do was to have slower speeds be red'ish, medium speeds to be green'ish and fast speeds to be blue'ish.

An example could be that I have an array:

speeds = [0.5, 0.5, 0.75, 1.25, 0.50, 0.75, 1.00, 4.00, 4.50, 8.00, 7.50, 8.50, 6.50, 6.00, 5.00, 5.25, 4.75, 4.00, 3.25, 2.50, 1.25, 0.00]

Now, for each value I have I want to calculate a color where the largest values will be more intense the larger they are (in the blue spectrum - something like (0, 0, 255)) while the smaller values will be more intense (in the red spectrum - (255, 0, 0)) the lower they are. And for the middle values I thought they could more intense in the green color (0, 255, 0) if it is absolutely in the middle, and then adding either a little bit of red or blue based on which side they are leaning towards.

I have tried to look for a plugin that could do this for me but I am unable to find such and I have also tried googling for a way to do this, but without any luck.

Upvotes: 2

Views: 2402

Answers (6)

chazsolo
chazsolo

Reputation: 8429

If you aren't opposed to using a library you could check out D3.js, specifically the utility to create custom scales. An example of this can be found here

Example

You would need to set up your color scale using your speeds array as a domain and the colors as the output range:

let colors = d3.scale.linear().domain([0, Math.max(...speeds)])
  .interpolate(d3.interpolateHcl)
  .range([d3.rgb('#FF0000'), d3.rgb('#0000FF')]);

colors is now a function that, given an index as input, will output a color. After that set up, loop through the speeds array to get the corresponding color:

for (let i = 0; i < speeds.length; i++) {
  // colors(i)
}

Upvotes: 1

DavidDomain
DavidDomain

Reputation: 15293

Another option would be to simple calculate the hsl value, since you already know the exact colors you are dealing with. Converting from hsl to rgb should not be to hard, there are plenty of libraries out there that do that very well.

Here is an example.

var speeds = [0.5, 0.5, 0.75, 1.25, 0.50, 0.75, 1.00, 4.00, 4.50, 8.00, 7.50, 8.50, 6.50, 6.00, 5.00, 5.25, 4.75, 4.00, 3.25, 2.50, 1.25, 0.00];
var fragment = document.createDocumentFragment();
var list = document.querySelector('ul');
var speedsMin = Math.min(...speeds);
var speedsMax = Math.max(...speeds);
var hslMin = 0;
var hslMax = 240;

var hslValues = speeds.map(function(value) {
  return { 
    h: Math.ceil( ( (value - speedsMin) / (speedsMax - speedsMin) ) * (hslMax - hslMin) + hslMin ),
    s: 100,
    l: 50
  }
})

hslValues.forEach(function(value) {
  var item = document.createElement('li');
  var color = 'hsl(' + value.h + ',' + value.s + '%,' + value.l + '%)';
  item.style.backgroundColor = color;
  fragment.appendChild(item)
})

list.appendChild(fragment)
ul {
  list-style-type: none
  margin: 0;
  padding: 0;
}

ul li {
  width: 20px;
  height: 20px;
  border-radius: 10px;
  display: inline-block;
  margin: 0 4px
}
<ul></ul>

Upvotes: 1

ninjin
ninjin

Reputation: 1198

Suppose min is the minimum speed and max is the maximum speed, then all the speeds are between min and max:

min |---------------------------| max
               speeds

You have to partition this interval into two smaller intervals, like this:

 |-------------|--------------|
min           mid            max

You can assign to min full Red, to mid full Green, and to max full Blue:

 R             G              B
 |-------------|--------------|
min           mid            max

Now you have to compute for each speed value its color. Suppose that s is the value of one of your speeds:

r = g = b = 0;

if (s <= mid) {
    r = 255 - (s - min) / (mid - min) * 255; // r is 255 when s = min and 0 when s = mid
    g = 255 - (mid - s) / (mid - min) * 255; // g is 255 when s = mid and 0 when s = min

} else {
    b = 255 - (s - mid) / (max - mid) * 255;
    g = 255 - (max - s) / (max - mid) * 255;
}

Given your array of speeds, you can do the following:

var speeds = [0.5, 0.5, 0.75, 1.25, 0.50, 0.75, 1.00, 4.00, 4.50, 8.00, 7.50, 8.50, 6.50, 6.00, 5.00, 5.25, 4.75, 4.00, 3.25, 2.50, 1.25, 0.00]
var max = Math.max(...speeds);
var min = Math.min(...speeds);
var mid = (max - min) / 2;

var colors = speeds.map((s) => {
    var r, g, b;
    r = g = b = 0;

    if (s <= mid) {
        r = 255 - (s - min) / (mid - min) * 255;
        g = 255 - (mid - s) / (mid - min) * 255;

    } else {
        b = 255 - (s - mid) / (max - mid) * 255;
        g = 255 - (max - s) / (max - mid) * 255;
    }

    return [r, g, b];
});

console.log(colors);

The array colors will contain an [r, g, b] list for each speed in speeds.

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386520

You could calculate the color of the two areas and use the three colors for generating the gradient.

function getColor(v, min, max) {

    function getC(f, l, r) {
        return {
            r: Math.floor((1 - f) * l.r + f * r.r),
            g: Math.floor((1 - f) * l.g + f * r.g),
            b: Math.floor((1 - f) * l.b + f * r.b),
        };
    }

    var left = { r: 255, g: 0, b: 0 },
        middle = { r: 0, g: 255, b: 0 },
        right = { r: 0, g: 0, b: 255 },
        mid = (max - min) / 2;

    return v < min + mid ?
        getC((v - min) / mid, left, middle) :
        getC((v - min - mid) / mid, middle, right);
}

var speeds = [0.5, 0.5, 0.75, 1.25, 0.50, 0.75, 1.00, 4.00, 4.50, 8.00, 7.50, 8.50, 6.50, 6.00, 5.00, 5.25, 4.75, 4.00, 3.25, 2.50, 1.25, 0.00],
    min = Math.min(...speeds),
    max = Math.max(...speeds);

speeds.forEach(function (a) {
    var color = getColor(a, min, max);
    document.body.innerHTML += '<span style="color: #fff; background-color: rgb(' + color.r + ',' + color.g + ',' + color.b + ');">' + a + '</span> ';
});

Upvotes: 2

tevemadar
tevemadar

Reputation: 13205

var min...,max...;
var mid=(min+max)/2;

speeds.foreach(function(x,idx){
  var r,g,b;
  if(x<mid){
    b=0;
    g=255*(x-min)/(mid-min);
    r=255-g;
  }else{
    r=0;
    g=255*(max-x)/(max-mid);
    b=255-g;
  }
  // do something with r-g-b here.
});

The idea is something like this, but after writing I have a hard time bending my brain to verify it. I think it is a correct red->green->blue 2-segment gradient now.

Above 2-3 gradient-segments, I would just really create a palette.

var r=[];g=[];b=[];
// black-red
for(var i=0;i<256;i++){
    r.push(i);
    g.push(0);
    b.push(0);
}
// red-green
for(var i=1;i<256;i++){
    r.push(255-i);
    g.push(i);
    b.push(0);
}
// green-blue
for(var i=1;i<256;i++){
    r.push(0);
    g.push(255-i);
    b.push(i);
}
// blue-white
for(var i=1;i<256;i++){
    r.push(i);
    g.push(i);
    b.push(255);
}

Then you have a palette of 1021 elements, index is x*r.length/(max-min)|0.

A more JavaScript-ish iteration of the latter:

var colors=[];
// black-red
for(var i=0;i<256;i++)colors.push({r:i,g:0,b:0}); // black-red
for(var i=1;i<256;i++)colors.push({r:255-i,g:i,b:0}); // red-green
for(var i=1;i<256;i++)colors.push({r:0,g:255-i,b:i}); // green-blue
for(var i=1;i<256;i++)colors.push({r:i,g:i,b:255}); // blue-white

speeds.foreacy(function(x,idx){
  var color=colors[x*colors.length/(max-min)|0]; // r-g-b fields with values 0-255
  ...
}

Upvotes: 1

gryxon
gryxon

Reputation: 46

I would sort this array and after this divide your array into 3 smaller arrays. After this operation you should normalize your arrays. You can do it by multiply each element in the list using formula:

(xi - min)/(max - min).

You have to normalize your new arrays not the old one. For first array (the smallest value) you can count red intensive by k=(max-xi)*255.0 . This colors will be (k, 0, 0) . The array with the biggest speeds you colorize by formula: k=xi*255.0 [colors will be (0,0,k)]. The formula for mid values depends on your choice. (Higher speed = more green or Higher speed = less speed).

I'm sorry for complicated description. Good luck.

Upvotes: 0

Related Questions