All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fair_acc.sample.chart.MultipleAxesSample Maven / Gradle / Ivy

Go to download

Small sample applications to showcase the features of the chart-fx library.

The newest version!
package io.fair_acc.sample.chart;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.fair_acc.chartfx.XYChart;
import io.fair_acc.chartfx.axes.Axis;
import io.fair_acc.chartfx.axes.spi.DefaultNumericAxis;
import io.fair_acc.chartfx.plugins.DataPointTooltip;
import io.fair_acc.chartfx.plugins.EditAxis;
import io.fair_acc.chartfx.plugins.ParameterMeasurements;
import io.fair_acc.chartfx.plugins.Zoomer;
import io.fair_acc.chartfx.renderer.Renderer;
import io.fair_acc.chartfx.renderer.spi.ErrorDataSetRenderer;
import io.fair_acc.chartfx.ui.geometry.Side;
import io.fair_acc.dataset.testdata.spi.CosineFunction;
import io.fair_acc.dataset.testdata.spi.GaussFunction;
import io.fair_acc.dataset.testdata.spi.RandomWalkFunction;
import io.fair_acc.dataset.testdata.spi.SineFunction;
import io.fair_acc.dataset.utils.ProcessingProfiler;

public class MultipleAxesSample extends ChartSample {
    private static final Logger LOGGER = LoggerFactory.getLogger(MultipleAxesSample.class);

    private static final int N_SAMPLES = 10_000; // default: 10000
    private static final long UPDATE_DELAY = 1000; // [ms]
    private static final long UPDATE_PERIOD = 1000; // [ms]
    private final ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
    private ScheduledFuture scheduledFuture;

    @Override
    public Node getChartPanel(final Stage primaryStage) {
        ProcessingProfiler.setVerboseOutputState(true);
        ProcessingProfiler.setLoggerOutputState(true);
        ProcessingProfiler.setDebugState(false);

        final BorderPane root = new BorderPane();

        final DefaultNumericAxis xAxis1 = new DefaultNumericAxis("x axis");
        xAxis1.setAnimated(false);
        final DefaultNumericAxis xAxis2 = new DefaultNumericAxis("x axis2");
        xAxis2.setSide(Side.TOP);
        xAxis2.setAnimated(false);
        final DefaultNumericAxis yAxis1 = new DefaultNumericAxis("y axis", "random");
        yAxis1.setAnimated(false);
        final DefaultNumericAxis yAxis2 = new DefaultNumericAxis("y axis", "sine/cosine");
        // yAxis2.setSide(Side.LEFT); // unusual but possible case
        yAxis2.setSide(Side.RIGHT);
        yAxis2.setAnimated(false);
        final DefaultNumericAxis yAxis3 = new DefaultNumericAxis("y axis", "gauss");
        yAxis3.setSide(Side.RIGHT);
        yAxis3.invertAxis(true);
        yAxis3.setAnimated(false);
        final XYChart chart = new XYChart(xAxis1, yAxis1);

        // N.B. it's important to set secondary axis on the 2nd renderer before
        // adding the renderer to the chart
        final ErrorDataSetRenderer errorRenderer1 = new ErrorDataSetRenderer();
        errorRenderer1.getAxes().add(yAxis1);
        final ErrorDataSetRenderer errorRenderer2 = new ErrorDataSetRenderer();
        errorRenderer2.getAxes().add(yAxis2);
        final ErrorDataSetRenderer errorRenderer3 = new ErrorDataSetRenderer();
        errorRenderer3.getAxes().addAll(xAxis2, yAxis3);
        chart.getRenderers().addAll(errorRenderer2, errorRenderer3);

        chart.getPlugins().add(new ParameterMeasurements());
        chart.getPlugins().add(new DataPointTooltip());
        final Zoomer zoom = new Zoomer();
        // add axes that shall be excluded from the zoom action
        zoom.omitAxisZoomList().add(yAxis3);
        // alternatively (uncomment):
        Zoomer.setOmitZoom(xAxis2, true);
        chart.getPlugins().add(zoom);
        chart.getToolBar().getChildren().add(new MyZoomCheckBox(zoom, yAxis3));
        chart.getPlugins().add(new EditAxis());

        final Button newDataSet = new Button("new DataSet");
        newDataSet.setOnAction(
                evt -> Platform.runLater(getTask(errorRenderer1, errorRenderer2, errorRenderer3)));
        final Button startTimer = new Button("timer");
        startTimer.setOnAction(evt -> {
            if (scheduledFuture == null || scheduledFuture.isCancelled()) {
                scheduledFuture = timer.scheduleAtFixedRate(
                        getTask(chart.getRenderers().get(0), errorRenderer2, errorRenderer3),
                        MultipleAxesSample.UPDATE_DELAY, MultipleAxesSample.UPDATE_PERIOD, TimeUnit.MILLISECONDS);
            } else {
                scheduledFuture.cancel(false);
            }
        });

        root.setTop(new HBox(newDataSet, startTimer));

        // generate the first set of data
        getTask(chart.getRenderers().get(0), errorRenderer2, errorRenderer3).run();

        long startTime = ProcessingProfiler.getTimeStamp();

        ProcessingProfiler.getTimeDiff(startTime, "adding data to chart");

        startTime = ProcessingProfiler.getTimeStamp();
        root.setCenter(chart);
        ProcessingProfiler.getTimeDiff(startTime, "adding chart into StackPane");

        return root;
    }

    public static Runnable getTask(final Renderer renderer1, final Renderer renderer2, final Renderer renderer3) {
        return new Runnable() {
            private int updateCount;

            @Override
            public void run() {
                Platform.runLater(() -> {
                    // setAll in order to implicitly clear previous list of
                    // 'old' data sets
                    renderer1.getDatasets().setAll(new RandomWalkFunction("random walk", MultipleAxesSample.N_SAMPLES));
                    renderer2.getDatasets().setAll(new CosineFunction("cosy", MultipleAxesSample.N_SAMPLES, true),
                            new SineFunction("siny", MultipleAxesSample.N_SAMPLES, true));
                    renderer3.getDatasets().setAll(new GaussFunction("gaussy", MultipleAxesSample.N_SAMPLES));

                    if (updateCount % 10 == 0) {
                        LOGGER.atInfo().log("update iteration #" + updateCount);
                    }
                    updateCount++;
                });
            }
        };
    }

    /**
     * @param args the command line arguments
     */
    public static void main(final String[] args) {
        Application.launch(args);
    }

    private static class MyZoomCheckBox extends CheckBox {
        /**
         * @param zoom the zoom interactor
         * @param axis to be synchronised
         */
        public MyZoomCheckBox(Zoomer zoom, Axis axis) {
            super("enable zoom for axis '" + axis.getName() + "'");
            this.setSelected(!zoom.omitAxisZoomList().contains(axis) || Zoomer.isOmitZoom(axis));
            this.selectedProperty().addListener((obj, o, n) -> {
                if (n.equals(o)) {
                    return;
                }
                if (n) {
                    zoom.omitAxisZoomList().remove(axis);
                    Zoomer.setOmitZoom(axis, false); // alternative implementation
                } else {
                    zoom.omitAxisZoomList().add(axis);
                    Zoomer.setOmitZoom(axis, true); // alternative implementation
                }
            });
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy