jfree / jfreechart-fx

Extensions for JFreeChart to support JavaFX client applications.
http://www.jfree.org/jfreechart/
GNU Lesser General Public License v2.1
109 stars 20 forks source link

Heavy performance loss using CrosshairOverlayFX #5

Open team172011 opened 6 years ago

team172011 commented 6 years ago

I am happy to see that there is a JavaFX wrapper for JFreeChart!

After moving with my charting application from Swing to JavaFX i discovered that the CrosshairOverlayFX is delayed on my CombinedXYPlot with 750 candlesticks as DefaultHighLowDataset. Adding more data to the chart (XYPlots as sub plots and TimeSeriesCollection data of the same size) makes the cross hair overlay so slow that it cannot be used seriously. I have enable caching in the ChartCanvas Region but it does not change anything. Are there other possibilities to improve performance of the overlay? In swing the cross hair overlay worked perfectly and very fast with exact the same settings and data

jfree commented 6 years ago

In the Swing version (ChartPanel) there is the option to draw the chart to an offscreen buffer (BufferedImage) so if the chart doesn't change, then drawing the overlay just requires copying the chart image to the panel then drawing the overlay - this is quick.

But in the JavaFX version (ChartCanvas) everything is redrawn from scratch (chart first, overlay(s) second). I could implement the same offscreen buffer approach within ChartCanvas, but the Canvas is already buffered, so I'm thinking another approach might be to employ a second Canvas (transparent) to draw the overlay...I didn't try it yet, but I can't think why it wouldn't work.

team172011 commented 6 years ago

Thanks for your answer. I will try to apply your approach and make a PR if it works fine.

team172011 commented 6 years ago

Maybe it is not a qualified approach for a library, but its simple, fast and independent of the draw() function. I have added an EventHandler to the ChartViewer class that draws a crosshair with help of two JavaFX Lines:


public class TaChartViewer extends Region {

    private TaChartCanvas canvas;

    private final Line xCrosshair; // javaFX objects
    private final Line yCrosshair;
    private final Label xLabel;
    private final Label yLabel;

    // (other code ...)

    public ChartViewer(JFreeChart chart, boolean contextMenuEnabled) {
        // (other code ...)
        this.xCrosshair = new Line(0,0,this.getPrefWidth(),0);
        this.yCrosshair = new Line(0,0,0,this.getPrefHeight());
        this.xCrosshair.setMouseTransparent(true);
        this.yCrosshair.setMouseTransparent(true);
        this.getChildren().add(xCrosshair);
        this.getChildren().add(yCrosshair);
        this.xLabel = new Label("");
        this.yLabel = new Label("");
        this.yLabel.setMouseTransparent(true);
        this.xLabel.setMouseTransparent(true);
        this.getChildren().add(xLabel);
        this.getChildren().add(yLabel);

        this.setOnMouseMoved( e ->{
                final double x = e.getX();
                final double y = e.getY();

                // do some stuff like in the paintOverlay function of CrosshairOverlay
                Rectangle2D dataArea = getCanvas().getRenderingInfo().getPlotInfo().getDataArea();

                if(x > dataArea.getMinX() && y > dataArea.getMinY()
                        && x < dataArea.getMaxX() && y < dataArea.getMaxY()) {
                    setCrosshairVisible(true);
                    XYPlot plot = (XYPlot) getCanvas().getChart().getPlot();

                    org.jfree.chart.axis.ValueAxis xAxis = plot.getDomainAxis();
                    RectangleEdge xAxisEdge = plot.getDomainAxisEdge();

                    xCrosshair.setStartY(dataArea.getMinY());
                    xCrosshair.setStartX(x);
                    xCrosshair.setEndY(dataArea.getMaxY());
                    xCrosshair.setEndX(x);
                    xLabel.setLayoutX(x);
                    xLabel.setLayoutY(dataArea.getMinY());

                    double value = xAxis.java2DToValue(e.getX(), dataArea, xAxisEdge);
                    xLabel.setText(value);

                    org.jfree.chart.axis.ValueAxis yAxis = plot.getRangeAxis();
                    RectangleEdge yAxisEdge = plot.getRangeAxisEdge();
                    Rectangle2D subDataArea =
                    getCanvas().getRenderingInfo().getPlotInfo().getSubplotInfo(0).getDataArea();

                    yCrosshair.setStartY(y);
                    yCrosshair.setStartX(dataArea.getMinX());
                    yCrosshair.setEndX(dataArea.getMaxX());
                    yCrosshair.setEndY(y);
                    yLabel.setLayoutY(y);
                    yLabel.setLayoutX(dataArea.getMinX());
                   yLabel.setText(yAxis.java2DToValue(y, subDataArea, yAxisEdge));
                } else {
                    setCrosshairVisible(false);
                }
            });
    }

    // (other code...)