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

io.fair_acc.sample.chart.OscilloscopeAxisSample 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.Timer;
import java.util.TimerTask;

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.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Slider;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
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.AbstractAxis;
import io.fair_acc.chartfx.axes.spi.DefaultNumericAxis;
import io.fair_acc.chartfx.axes.spi.OscilloscopeAxis;
import io.fair_acc.chartfx.axes.spi.format.DefaultTickUnitSupplier;
import io.fair_acc.chartfx.plugins.EditAxis;
import io.fair_acc.chartfx.renderer.datareduction.DefaultDataReducer;
import io.fair_acc.chartfx.renderer.spi.ErrorDataSetRenderer;
import io.fair_acc.chartfx.ui.geometry.Side;
import io.fair_acc.chartfx.utils.FXUtils;
import io.fair_acc.chartfx.utils.SimplePerformanceMeter;
import io.fair_acc.dataset.spi.LimitedIndexedTreeDataSet;
import io.fair_acc.dataset.utils.ProcessingProfiler;

/**
 * Simple example to illustrate the {@link io.fair_acc.chartfx.axes.spi.OscilloscopeAxis} and functional/visual difference to
 * the default {@link io.fair_acc.chartfx.axes.spi.DefaultNumericAxis} implementation
 *
 * @author rstein
 */
public class OscilloscopeAxisSample extends ChartSample {
    private static final Logger LOGGER = LoggerFactory.getLogger(OscilloscopeAxisSample.class);
    private static final String MONOSPACED = "Monospaced";
    private static final int MIN_PIXEL_DISTANCE = 1;
    private static final double AXIS_CENTRE_VALUE = 0.0;
    private static final double AXIS_CENTRE_POSITION = 0.2;
    public final LimitedIndexedTreeDataSet rollingBufferDipoleCurrent = new LimitedIndexedTreeDataSet("dipole current", RollingBufferSample.BUFFER_CAPACITY);
    public final LimitedIndexedTreeDataSet rollingBufferBeamIntensity = new LimitedIndexedTreeDataSet("beam intensity", RollingBufferSample.BUFFER_CAPACITY);
    public final XYChart chartOscilloscopeAxis = getChart(false);
    public final XYChart chartDefaultAxis = getChart(true);
    private Timer timer;

    private void generateData() {
        final long startTime = ProcessingProfiler.getTimeStamp();
        final double now = System.currentTimeMillis() / 1000.0 + 1;
        // N.B. '+1' to check for resolution

        if (rollingBufferDipoleCurrent.getDataCount() == 0) {
            for (int n = RollingBufferSample.N_SAMPLES; n > 0; n--) {
                final double t = now - n * RollingBufferSample.UPDATE_PERIOD / 1000.0;
                final double y = 25 * RollingBufferSample.rampFunctionDipoleCurrent(t);
                final double y2 = AXIS_CENTRE_VALUE + 100 * Math.cos(2.0 * Math.PI * 0.01 * t) * RollingBufferSample.rampFunctionBeamIntensity(t);
                final double ey = 1;
                rollingBufferDipoleCurrent.add(t, y, ey, ey);
                rollingBufferBeamIntensity.add(t, y2, ey, ey);
            }
        } else {
            final double t = now;
            final double y = 25 * RollingBufferSample.rampFunctionDipoleCurrent(t);
            final double y2 = AXIS_CENTRE_VALUE + 100 * Math.cos(2.0 * Math.PI * 0.01 * t) * RollingBufferSample.rampFunctionBeamIntensity(t);
            final double ey = 1;
            rollingBufferDipoleCurrent.add(t, y, ey, ey);
            rollingBufferBeamIntensity.add(t, y2, ey, ey);
        }
        ProcessingProfiler.getTimeDiff(startTime, "adding data into DataSet");
    }

    @Override
    public Node getChartPanel(final Stage primaryStage) {
        final BorderPane root = new BorderPane();

        root.setTop(getHeaderBar());
        generateData();
        root.setCenter(new VBox(chartOscilloscopeAxis, chartDefaultAxis));

        return root;
    }

    private ToolBar getHeaderBar() {
        final Button startTimer = new Button("Start Timer");

        startTimer.setOnAction(evt -> {
            if (timer == null) {
                FXUtils.runFX(() -> startTimer.setText("Stop Timer"));
                timer = new Timer("sample-update-timer", true);
                rollingBufferBeamIntensity.reset();
                rollingBufferDipoleCurrent.reset();
                timer.scheduleAtFixedRate(getTask(), 0, RollingBufferSample.UPDATE_PERIOD);
            } else {
                FXUtils.runFX(() -> startTimer.setText("Start Timer"));
                timer.cancel();
                timer = null;
            }
        });

        ToggleGroup radioGroup = new ToggleGroup();

        RadioButton tickRange1 = new RadioButton("<1,2,5>");
        tickRange1.setSelected(true);
        tickRange1.setToggleGroup(radioGroup);
        tickRange1.setOnAction(evt -> {
            for (Axis axis : chartOscilloscopeAxis.getAxes()) {
                if (!(axis instanceof OscilloscopeAxis)) {
                    continue;
                }
                ((OscilloscopeAxis) axis).setTickUnitSupplier(new DefaultTickUnitSupplier(OscilloscopeAxis.DEFAULT_MULTIPLIERS1));
                axis.requestAxisLayout();
            }
        });

        RadioButton tickRange2 = new RadioButton("<1, 1.5, 2, ..., 9.5>");
        tickRange2.setToggleGroup(radioGroup);
        tickRange2.setOnAction(evt -> {
            for (Axis axis : chartOscilloscopeAxis.getAxes()) {
                if (!(axis instanceof OscilloscopeAxis)) {
                    continue;
                }
                ((OscilloscopeAxis) axis).setTickUnitSupplier(new DefaultTickUnitSupplier(OscilloscopeAxis.DEFAULT_MULTIPLIERS2));
                axis.requestAxisLayout();
            }
        });

        Slider centreSlider = new Slider(0.0, 1.0, AXIS_CENTRE_POSITION);
        centreSlider.setMajorTickUnit(0.1);
        centreSlider.setMinorTickCount(1);
        centreSlider.setShowTickMarks(true);
        centreSlider.setShowTickLabels(true);
        centreSlider.setBlockIncrement(0.1);
        centreSlider.valueProperty().addListener((ch, o, n) -> {
            final double zeroPosition = n.doubleValue();
            for (Axis axis : chartOscilloscopeAxis.getAxes()) {
                if (!(axis instanceof OscilloscopeAxis)) {
                    continue;
                }
                ((OscilloscopeAxis) axis).setAxisZeroPosition(zeroPosition);
                chartOscilloscopeAxis.invalidate();
            }
        });

        final CheckBox forceMinRange = new CheckBox("force min. range");
        forceMinRange.selectedProperty().addListener((ch, o, n) -> {
            for (Axis axis : chartOscilloscopeAxis.getAxes()) {
                if (!(axis instanceof OscilloscopeAxis)) {
                    continue;
                }
                if (Boolean.TRUE.equals(n)) {
                    ((OscilloscopeAxis) axis).getMinRange().set(-4000, 0);
                } else {
                    ((OscilloscopeAxis) axis).getMinRange().clear();
                }
                axis.forceRedraw();
                chartOscilloscopeAxis.invalidate();
            }
        });

        final CheckBox forceMaxRange = new CheckBox("force max. range");
        forceMaxRange.selectedProperty().addListener((ch, o, n) -> {
            for (Axis axis : chartOscilloscopeAxis.getAxes()) {
                if (!(axis instanceof OscilloscopeAxis)) {
                    continue;
                }
                if (Boolean.TRUE.equals(n)) {
                    ((OscilloscopeAxis) axis).getMaxRange().set(0, 1000);
                } else {
                    ((OscilloscopeAxis) axis).getMaxRange().clear();
                }
                axis.forceRedraw();
                chartOscilloscopeAxis.invalidate();
            }
        });

        // H-Spacer
        Region spacer = new Region();
        spacer.setMinWidth(Region.USE_PREF_SIZE);
        HBox.setHgrow(spacer, Priority.ALWAYS);

        // JavaFX and Chart Performance metrics
        SimplePerformanceMeter meter = new SimplePerformanceMeter(RollingBufferSample.DEBUG_UPDATE_RATE);

        Label fxFPS = new Label();
        fxFPS.setFont(Font.font(MONOSPACED, 12));
        Label chartFPS = new Label();
        chartFPS.setFont(Font.font(MONOSPACED, 12));
        Label cpuLoadProcess = new Label();
        cpuLoadProcess.setFont(Font.font(MONOSPACED, 12));
        Label cpuLoadSystem = new Label();
        cpuLoadSystem.setFont(Font.font(MONOSPACED, 12));
        meter.fxFrameRateProperty().addListener((ch, o, n) -> {
            final String fxRate = String.format("%4.1f", meter.getFxFrameRate());
            final String actualRate = String.format("%4.1f", meter.getActualFrameRate());
            final String cpuProcess = String.format("%5.1f", meter.getProcessCpuLoad());
            final String cpuSystem = String.format("%5.1f", meter.getSystemCpuLoad());
            fxFPS.setText(String.format("%-6s: %4s %s", "JavaFX", fxRate, "FPS, "));
            chartFPS.setText(String.format("%-6s: %4s %s", "Actual", actualRate, "FPS, "));
            cpuLoadProcess.setText(String.format("%-11s: %4s %s", "Process-CPU", cpuProcess, "%"));
            cpuLoadSystem.setText(String.format("%-11s: %4s %s", "System -CPU", cpuSystem, "%"));
        });

        return new ToolBar(startTimer, new Label(" tick multipliers: "), tickRange1, tickRange2, new Label(" zero axis position: "), centreSlider, forceMinRange, forceMaxRange,
                spacer, new VBox(fxFPS, chartFPS), new VBox(cpuLoadProcess, cpuLoadSystem, meter));
    }

    private TimerTask getTask() {
        return new TimerTask() {
            private int updateCount;

            @Override
            public void run() {
                Platform.runLater(() -> {
                    generateData();

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

    private XYChart getChart(final boolean defaultAxis) {
        final DefaultNumericAxis xAxis = new DefaultNumericAxis("", "");
        xAxis.setAutoRangeRounding(false);
        xAxis.invertAxis(false);
        xAxis.setTimeAxis(true);

        final AbstractAxis yAxis1;
        final AbstractAxis yAxis2;
        if (defaultAxis) {
            yAxis1 = new DefaultNumericAxis("beam intensity", "ppp");
            yAxis2 = new DefaultNumericAxis("dipole current", "A");
            ((DefaultNumericAxis) yAxis1).setForceZeroInRange(true);
            ((DefaultNumericAxis) yAxis2).setForceZeroInRange(true);
        } else {
            yAxis1 = new OscilloscopeAxis("beam intensity", "ppp");
            ((OscilloscopeAxis) yAxis1).setAxisZeroValue(AXIS_CENTRE_VALUE);
            ((OscilloscopeAxis) yAxis1).setAxisZeroPosition(AXIS_CENTRE_POSITION);
            yAxis2 = new OscilloscopeAxis("dipole current", "A");
            ((OscilloscopeAxis) yAxis2).setAxisZeroPosition(AXIS_CENTRE_POSITION);
        }
        yAxis2.setSide(Side.RIGHT);
        //        yAxis1.setStyle("fillColor=blue");
        //        yAxis2.setStyle("strokeColor=red");
        yAxis1.setAutoRangeRounding(true);
        yAxis2.setAutoRangeRounding(true);

        final XYChart chart = new XYChart(xAxis, yAxis1);
        chart.setTitle(defaultAxis ? "Chart with DefaultNumericAxis" : "Chart with OscilloscopeAxis");
        chart.setLegendVisible(false);
        chart.getYAxis().setName(rollingBufferBeamIntensity.getName());
        final ErrorDataSetRenderer beamIntensityRenderer = (ErrorDataSetRenderer) chart.getRenderers().get(0);
        ((DefaultDataReducer) beamIntensityRenderer.getRendererDataReducer()).setMinPointPixelDistance(MIN_PIXEL_DISTANCE);
        beamIntensityRenderer.setDrawMarker(false);
        beamIntensityRenderer.getDatasets().add(rollingBufferBeamIntensity);

        final ErrorDataSetRenderer dipoleCurrentRenderer = new ErrorDataSetRenderer();
        ((DefaultDataReducer) dipoleCurrentRenderer.getRendererDataReducer()).setMinPointPixelDistance(MIN_PIXEL_DISTANCE);
        dipoleCurrentRenderer.setDrawMarker(false);
        dipoleCurrentRenderer.getAxes().add(yAxis2);
        dipoleCurrentRenderer.getDatasets().add(rollingBufferDipoleCurrent);
        chart.getRenderers().add(dipoleCurrentRenderer);

        chart.getPlugins().add(new EditAxis());

        return chart;
    }

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy