/*******************************************************************************
 * Copyright (c) 2015-2016 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.javafx.rtplot;

import java.awt.Color;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.csstudio.javafx.rtplot.internal.ImagePlot;
import org.diirt.util.array.ArrayDouble;
import org.diirt.util.array.ListDouble;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.stage.Stage;

/** @author Kay Kasemir
 */
@SuppressWarnings("nls")
public class ImagePlotDemo extends Application
{
    private static final int WIDTH = 600, HEIGHT = 400;

    private static final ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();

    private static final long start = System.currentTimeMillis();

    // Violates the idea of ColorMappingFunction because it creates a Color() per invocation.
    // OK for test because it's the easiest way to create a 'rainbow'
    /** Rainbow color mapping */
    public final static ColorMappingFunction RAINBOW = value -> new Color(Color.HSBtoRGB((float)value, 1.0f, 1.0f)).getRGB();

    private ListDouble computeData()
    {
        final long now = System.currentTimeMillis();

        final double phase = (now - start)/1000.0;

        final double[] data = new double[WIDTH * HEIGHT];
        int i = 0;
        for (int y=0; y<HEIGHT; ++y)
        {
            final double dy = y - HEIGHT/2;
            final double dy2 = dy*dy;
            for (int x=0; x<WIDTH; ++x)
            {
                final double dx = x - WIDTH/2;
                final double r = Math.sqrt(dx*dx + dy2);
                data[i++] = Math.exp(-r/(WIDTH/2)) * (1.0 + Math.cos(2*Math.PI*(r/(WIDTH/6) - phase)));
            }
        }
        return new ArrayDouble(data);
    }

    volatile boolean show_colorbar = true;

    @Override
    public void start(final Stage stage) throws Exception
    {
        final Level level = Level.CONFIG;
        Logger.getLogger("").setLevel(level);
        for (Handler handler : Logger.getLogger("").getHandlers())
            handler.setLevel(level);

        final ImagePlot plot = new ImagePlot(true);

        plot.setColorMapping(RAINBOW);

        plot.setAutoscale(true);
        plot.setValueRange(0.0, 2.0);

        plot.getXAxis().setGridVisible(true);
        plot.getYAxis().setGridVisible(true);
        plot.getYAxis().setScaleFont(Font.font("Liberation Sans", FontPosture.ITALIC, 25));

        timer.scheduleAtFixedRate(() -> plot.setValue(WIDTH, HEIGHT, computeData(), false),
                                  200, 100, TimeUnit.MILLISECONDS);

        timer.scheduleAtFixedRate(() ->
        {
            show_colorbar = ! show_colorbar;
            plot.showColorMap(show_colorbar);
        }, 5000, 5000, TimeUnit.MILLISECONDS);

		final Pane root = new Pane(plot);
		final ChangeListener<? super Number> resize_listener = (p, o, n) -> plot.setSize(root.getWidth(), root.getHeight());
		root.widthProperty().addListener(resize_listener);
		root.heightProperty().addListener(resize_listener);

        final Scene scene = new Scene(root, 800, 600);
        stage.setScene(scene);
        stage.setTitle("Image Plot Demo");
        stage.show();

        stage.setOnCloseRequest((event) ->
        {
            timer.shutdown();
            try
            {
                timer.awaitTermination(2, TimeUnit.SECONDS);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            plot.dispose();
        });
    }

    public static void main(final String[] args)
    {
        launch(args);
    }
}
@@ -764,24 +764,60 @@
 .Logger;%0D%0A%0D%0A
+import org.csstudio.javafx.Styles;%0D%0A
 import org.c
@@ -1023,24 +1023,61 @@
 eListener;%0D%0A
+import javafx.geometry.Rectangle2D;%0D%0A
 import javaf
@@ -3288,24 +3288,713 @@
 C, 25));%0D%0A%0D%0A
+        final RegionOfInterest roi = plot.addROI(%22R.O.I.%22, javafx.scene.paint.Color.BLUEVIOLET, true);%0D%0A        roi.setRegion(new Rectangle2D(20, 40, 20, 10));%0D%0A%0D%0A        plot.setListener(new RTImagePlotListener()%0D%0A        %7B%0D%0A%0D%0A            @Override%0D%0A            public void changedCursorLocation(double x, double y, int xi,%0D%0A                    int yi, double value)%0D%0A            %7B%0D%0A                System.out.println(%22Cursor at %22 + x + %22, %22 + y);%0D%0A            %7D%0D%0A%0D%0A            @Override%0D%0A            public void changedROI(int index, String name, Rectangle2D region)%0D%0A            %7B%0D%0A                System.out.println(%22ROI %22 + name + %22 now at %22 + region);%0D%0A            %7D%0D%0A        %7D);%0D%0A%0D%0A
         time
@@ -4660,24 +4660,62 @@
 800, 600);%0D%0A
+        Styles.setSceneStyle(scene);%0D%0A
         stag

Add ROI to demo