Monday, November 28, 2011

Processing: Multi-color Gradient


Recently I created a simulation of how heat is distributed in a system using Processing. For this I wanted a way to show the varying values of heat, so I created a simple multi-color gradient that would be used to show different colors for different values of heat.

Here is the Processing sketch to show how heat is distributed. Left click to apply heat to an area, right click to remove heat (i.e. cool) to an area. You can easily see how the gradient is used.



The following is the Gradient class I used to achieve the multi-color gradient. Colors can be added to it to create the gradient. A color can then  be requested from it by using a float value.

class Gradient
{
  ArrayList colors;
  
  // Constructor
  Gradient()
  {
    colors = new ArrayList();
  }
  
  // Append a color to the gradient
  void addColor(color c)
  {
    colors.add(c);
  }
  
  // Get the gradient at a specified value
  color getGradient(float value)
  {
    // make sure there are colors to use
    if(colors.size() == 0)
      return #000000;
    
    // if its too low, use the lowest value
    if(value <= 0.0)
      return (color)(Integer) colors.get(0);
    
    // if its too high, use the highest value
    if(value >= colors.size() - 1)
      return (color)(Integer) colors.get(colors.size() -  1);
    
    // lerp between the two needed colors
    int color_index = (int)value;
    color c1 = (color)(Integer) colors.get(color_index);
    color c2 = (color)(Integer) colors.get(color_index + 1);
    
    return lerpColor(c1, c2, value - color_index);
  }
}  

In a normal, 2 color gradient, you can use lerpColor() to interpolate between those two colors. In a multi-color gradient, it is still using lerpColor() but it must first decide which two colors to interpolate between.

For example, let's say the gradient has 4 colors. A value between 0.0 and 3.0 can be requested (anything below 0.0 is assumed as 0.0, anything above 3.0 is assumed as 3.0). If a value of 2.8 is requested, we cast this as an integer (since it will always drop the fractional part) and we get 2. This gives us the color at index 2 to start with, and we want to interpolate to the color of index 3. Now we use the factional part, in this case 0.8 (this is the requested float value minus the integer part), to give us how much we must interpolate between index 2 and 3. lerpColor() is used to interpolate between index 2 and 3 by a value of .8.