famedoro
famedoro

Reputation: 1241

Javafx 2 chart zoom

When I do select a chart region my zoom get a "little switch". In example if do select from tick 3 up to tick 6 I obtain as lower bound:3.67 and upper bound 6.74.

How to fix it ?

Any help really appreciated.

package testjavafxzoom;

import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;

public class Zoom01 extends Application {

Path path;//Add path for freehand
BorderPane pane;
Rectangle rect;
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
SimpleDoubleProperty rectX = new SimpleDoubleProperty();
SimpleDoubleProperty rectY = new SimpleDoubleProperty();

@Override
public void start(Stage stage) {

    System.out.println("Java Version             : " + com.sun.javafx.runtime.VersionInfo.getVersion());
    System.out.println("Java getHudsonBuildNumber: " + com.sun.javafx.runtime.VersionInfo.getHudsonBuildNumber());
    System.out.println("Java getReleaseMilestone : " + com.sun.javafx.runtime.VersionInfo.getReleaseMilestone());
    System.out.println("Java getRuntimeVersion   : " + com.sun.javafx.runtime.VersionInfo.getRuntimeVersion());

    stage.setTitle("Lines plot");

    //final CategoryAxis xAxis = new CategoryAxis();
    final NumberAxis xAxis = new NumberAxis(1, 12, 1);
    final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);

    yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {

        @Override
        public String toString(Number object) {
            return String.format("%7.5f", object);
        }
    });


    //final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
    final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);

    //lineChart.setTitle("Stock quotes");
    lineChart.setCreateSymbols(false);
    lineChart.setAlternativeRowFillVisible(false);
    lineChart.setAnimated(true);

    XYChart.Series series1 = new XYChart.Series();
    //series1.setName("Stock 1");
    series1.getData().add(new XYChart.Data(1, 0.53185));
    series1.getData().add(new XYChart.Data(2, 0.532235));
    series1.getData().add(new XYChart.Data(3, 0.53234));
    series1.getData().add(new XYChart.Data(4, 0.538765));
    series1.getData().add(new XYChart.Data(5, 0.53442));
    series1.getData().add(new XYChart.Data(6, 0.534658));
    series1.getData().add(new XYChart.Data(7, 0.53023));
    series1.getData().add(new XYChart.Data(8, 0.53001));
    series1.getData().add(new XYChart.Data(9, 0.53589));
    series1.getData().add(new XYChart.Data(10, 0.53476));
    series1.getData().add(new XYChart.Data(11, 0.530123));
    series1.getData().add(new XYChart.Data(12, 0.53035));


    pane = new BorderPane();
    pane.setCenter(lineChart);
    //Scene scene = new Scene(lineChart, 800, 600);
    Scene scene = new Scene(pane, 800, 600);
    lineChart.getData().addAll(series1);

    stage.setScene(scene);        

    path = new Path();
    path.setStrokeWidth(1);
    path.setStroke(Color.BLACK);

    scene.setOnMouseClicked(mouseHandler);
    scene.setOnMouseDragged(mouseHandler);
    scene.setOnMouseEntered(mouseHandler);
    scene.setOnMouseExited(mouseHandler);
    scene.setOnMouseMoved(mouseHandler);
    scene.setOnMousePressed(mouseHandler);
    scene.setOnMouseReleased(mouseHandler);

    //root.getChildren().add(lineChart);
    pane.getChildren().add(path);

    rect = new Rectangle();
    rect.setFill(Color.web("blue", 0.1));
    rect.setStroke(Color.BLUE);
    rect.setStrokeDashOffset(50);

    rect.widthProperty().bind(rectX.subtract(rectinitX));
    rect.heightProperty().bind(rectY.subtract(rectinitY));
    pane.getChildren().add(rect);

    stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {

    @Override
    public void handle(MouseEvent mouseEvent) {

        if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
            rect.setX(mouseEvent.getX());
            rect.setY(mouseEvent.getY());
            rectinitX.set(mouseEvent.getX());
            rectinitY.set(mouseEvent.getY());
        } else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
            rectX.set(mouseEvent.getX());
            rectY.set(mouseEvent.getY());
        } else if (mouseEvent.getEventType() == MouseEvent.MOUSE_RELEASED) {

            System.out.println("Zoom bounds : [" + rectinitX.get()+", "+rectinitY.get()+"] ["+ rectX.get()+", "+rectY.get()+"]");                
            System.out.println("TODO: Determine bound ranges according these zoom coordinates.\n");

            // TODO: Determine bound ranges according this zoom coordinates.
            //LineChart<String, Number> lineChart = (LineChart<String, Number>) pane.getCenter();
            LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();

            // Zoom in Y-axis by changing bound range.
            NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
            yAxis.setLowerBound(0.532);
            yAxis.setUpperBound(0.538);

            // Zoom in X-axis by removing first and last data values.
            // Note: Maybe better if categoryaxis is replaced by numberaxis then setting the
            // LowerBound and UpperBound will be avaliable.
            /*
            XYChart.Series series1 = lineChart.getData().get(0);
            if (!series1.getData().isEmpty()) {
                series1.getData().remove(0);
                series1.getData().remove(series1.getData().size() - 1);
            }
            */

            NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
            System.out.println("(a) xAxis.getLowerBound() "+xAxis.getLowerBound()+" "+xAxis.getUpperBound());
            double Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());
            double newXlower, newXupper;
            newXlower = (rectinitX.get()/Tgap)+xAxis.getLowerBound();

            newXupper = (rectX.get()/Tgap)+xAxis.getLowerBound();
            if (newXupper > xAxis.getUpperBound())
                newXupper = xAxis.getUpperBound();

            xAxis.setLowerBound( newXlower );
            xAxis.setUpperBound( newXupper );

            System.out.println("(b) xAxis.getLowerBound() "+xAxis.getLowerBound()+" "+xAxis.getUpperBound());

            // Hide the rectangle
            rectX.set(0);
            rectY.set(0);
        }
    }
   };

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

Upvotes: 3

Views: 6581

Answers (1)

Sergey Grinev
Sergey Grinev

Reputation: 34498

You attached mouse event to Scene. So mouseEvent.getX() gives you coordinates relative to scene. But your formula uses them as coordinates relative to xAxis node. You need to take into account xAxis location on scene.

Introduce next method:

// summ layout shift against parent until we ascend to scene
private static double getSceneShift(Node node) {
    double shift = 0;
    do { 
        shift += node.getLayoutX(); 
        node = node.getParent();
    } while (node != null);
    return shift;
}

and change your calculations accordingly:

        double xAxisShift = getSceneShift(xAxis);
        newXlower = ((rectinitX.get() - xAxisShift) / Tgap) + xAxis.getLowerBound();
        newXupper = ((rectX.get() - xAxisShift) / Tgap) + xAxis.getLowerBound();

Upvotes: 5

Related Questions