Converting Scalars to RGB Colormap
We can visualize variation of mesh-based scalars by converting each scalar to a color with the help of some colormap. A commonly used system maps the lowest value to blue, the highest to red, and the intermediate values to shades of green, yellow, and orange. This gives a “short” rainbow gradation. An alternate maps the lowest value to purple, which then gives us the full rainbow. In this article I’ll show you how to convert a scalar to the corresponding RGB pair to obtain few different colormaps:grayscale, short rainbow, long rainbow, and yellow to red. You can see an interactive demo in the follow up article on a 2D data plotter.
Grayscale
The first step is to normalize our scalar value s to the range [0:1]. This can be done using \(f=(s-s_{min})/(s_{max}-s_{min})\). We next convert to grayscale by simply multiplying this value by 255 and casting to int, g=(int)f*255. The color value is then simply RGB(g,g,g). In the above demo, I am using a for loop to iterate over the width of the canvas. The code to plot the grayscale bar is:
for (var i=0;i<w;i++) { var f=i/w; //convert to [0,1] /*plot grayscale*/ var g=Math.round(f*255); ctx.fillStyle = "rgb("+g+","+g+","+g+")"; ctx.fillRect(i,0,1,20); } /*add legend*/ ctx.font="10px serif"; ctx.fillStyle="#FFF"; ctx.fillText("Grayscale",5,15);
Short Rainbow
My motivation for the short rainbow algorithm was the Wikipedia article on converting HSL to RGB. To convert a scalar to a rainbow, we divide the scalar into four groups. Then based on which group we are in, we perform one of the following linear interpolations:
- Group 1: Red is maximum (255), blue is zero, and green varies linearly from 0 to 255.
- Group 2: Green is kept maximum and blue is still zero. Red varies linearly from 255 to 0.
- Group 3: Keep red at zero and green and maximum. Blue varies linearly from 0 to 255.
- Group 4: Red is kept at zero and blue at maximum. Green varies linearly from 255 to 0.
This ordering follows the image in the Wikipedia article. One downside of this approach is that it will vary from red to blue as f varies from 0 to 1. Typically, we want the opposite, with 0 mapping to blue and 1 to red. So we simply invert the scalar as a=1-f. There are many nifty ways to implement the mapping but I went for simplicity and just used a case. The code is shown below.
/*plot short rainbow RGB*/ var a=(1-f)/0.25; //invert and group var X=Math.floor(a); //this is the integer part var Y=Math.floor(255*(a-X)); //fractional part from 0 to 255 switch(X) { case 0: r=255;g=Y;b=0;break; case 1: r=255-Y;g=255;b=0;break; case 2: r=0;g=255;b=Y;break; case 3: r=0;g=255-Y;b=255;break; case 4: r=0;g=0;b=255;break; } ctx.fillStyle = "rgb("+r+","+g+","+b+")"; ctx.fillRect(i,30,1,20);
Long Rainbow
The long rainbow is similar to the short rainbow, except that we divide into 5 groups, with the final one consisting of blue at maximum, green at zero, and red increasing linearly from 0 to 255. The code is below
/*convert to long rainbow RGB*/ var a=(1-f)/0.2; var X=Math.floor(a); var Y=Math.floor(255*(a-X)); switch(X) { case 0: r=255;g=Y;b=0;break; case 1: r=255-Y;g=255;b=0;break; case 2: r=0;g=255;b=Y;break; case 3: r=0;g=255-Y;b=255;break; case 4: r=Y;g=0;b=255;break; case 5: r=255;g=0;b=255;break; } ctx.fillStyle = "rgb("+r+","+g+","+b+")"; ctx.fillRect(i,60,1,20);
Yellow to Red
Finally, the yellow to red map is basically a colored variant of the grayscale map. Its advantage over the rainbow is that it avoids gradient artifacts. We simply keep red at maximum, and vary yellow from 0 to 255, as shown below
/*convert to yellow to red*/ var a=(1-f); var Y=Math.floor(255*a); r=255;g=Y;b=0; ctx.fillStyle = "rgb("+r+","+g+","+b+")"; ctx.fillRect(i,90,1,20);
Hi
It seems to me that case “5” for long rainbow is wrong, because the text says red increases linearly but the code just make red=255 not red=255-Y
And what is weirdest is that I have been given a colormap generated by some people I do not directly know, and I do not know what software they use, but coincidently the last part of their long rainbow color scale is a static pink, not actually changing in the intensity of red nor anything.
now I got it, the final group is the case 4. the case 5 is just a final color, not a final group