bluevoxel
bluevoxel

Reputation: 5368

Draw a smooth color scale and assign specific values to it

I'm considering a new way of implementation a simple 2D graphical matrix, where colors of its items depend on individual values assigned to those items. So far, I used for this purpose a following schema:

1) Provide an interval-based color scale reference (builded of, say, 20 blocks), to which I could assign specific range of values, e.g. 100-1000.

2) Link matrix items individual values with colors from the scale mentioned above, thus item with value 100 will be e.g BLUE, and item with value 1000 will be RED.

The problem with such solution, is that I had to build my color scale from "blocks", and so it looks like this:

enter image description here

This solution is not so bad, but I want to go further, and implement much accurate value-color matching with help of color scale as showed below:

enter image description here

Next, there will be nothing so much different of the drill described above. I will "put" this scale between values of given range (e.g. 100-1000), and depend on the individual value of the matrix item, I'll pick proper color from the scale and assign it to the given item.

But how could I draw such scale and put it in a range of a specific values trying to avoid ItemValue-To-SpecificColorBlock matching problem from my old solution?

Upvotes: 4

Views: 7594

Answers (1)

James_D
James_D

Reputation: 209408

In statistics (visualization) this is called a heat map.

To compute the color for a given value, with MIN <= value < MAX, I would interpolate it onto the range Color.BLUE.getHue() - Color.RED.getHue().

i.e.

double hue = Color.BLUE.getHue() + (Color.RED.getHue() - Color.BLUE.getHue()) * (value - MIN) / (MAX - MIN) ;

Then use the hue to create the color with full saturation and brightness:

Color color = Color.hsb(hue, 1.0, 1.0);

To create the color scale image, create a WritableImage, grab the pixel writer, iterate over all pixels, and set the color of each pixel using the formula above with the value interpolated along the x axis from 0 to the width of the image. (If you want a vertical color scale, similarly interpolate along the y-axis.)

Here's an example which draws the color scale. You can use the getColorForValue(...) method to compute the color for a given value, for displaying the matrix.

import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class HeatMap extends Application {

    private final static double MIN = 100 ;
    private final static double MAX = 1000 ;
    private final static double BLUE_HUE = Color.BLUE.getHue() ;
    private final static double RED_HUE = Color.RED.getHue() ;

    @Override
    public void start(Stage primaryStage) {
        Image colorScale = createColorScaleImage(600, 120, Orientation.HORIZONTAL);
        ImageView imageView = new ImageView(colorScale);
        StackPane root = new StackPane(imageView);
        Scene scene = new Scene(root, 800, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Color getColorForValue(double value) {
        if (value < MIN || value > MAX) {
            return Color.BLACK ;
        }
        double hue = BLUE_HUE + (RED_HUE - BLUE_HUE) * (value - MIN) / (MAX - MIN) ;
        return Color.hsb(hue, 1.0, 1.0);
    }

    private Image createColorScaleImage(int width, int height, Orientation orientation) {
        WritableImage image = new WritableImage(width, height);
        PixelWriter pixelWriter = image.getPixelWriter();
        if (orientation == Orientation.HORIZONTAL) {
            for (int x=0; x<width; x++) {
                double value = MIN + (MAX - MIN) * x / width;
                Color color = getColorForValue(value);
                for (int y=0; y<height; y++) {
                    pixelWriter.setColor(x, y, color);
                }
            }
        } else {
            for (int y=0; y<height; y++) {
                double value = MAX - (MAX - MIN) * y / height ;
                Color color = getColorForValue(value);
                for (int x=0; x<width; x++) {
                    pixelWriter.setColor(x, y, color);
                }
            }
        }
        return image ;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Upvotes: 9

Related Questions