JavaFX 2.x; Still problems with draw on chart + zoom

a 夏天 提交于 2019-12-10 18:13:28

问题


I am still struggling with this hard problem I can't solve: the code below performs a zoom (magnify) by left mouse click & drag on the chart (from up left to down right) and restore back form down right to up left.

Right mouse click and drag performs a free hand draw: the problem is that if I draw then zoom, what drawn is displaced and it is not where I previously drawn.

How to fix this?

Thanks

Here's the code:

public class Zoom 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();

    double initXLowerBound = 0, initXUpperBound = 0, initYLowerBound = 0, initYUpperBound = 0;

    @Override
    public void start(Stage stage) {

        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<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);

        lineChart.setCreateSymbols(false);
        lineChart.setAlternativeRowFillVisible(false);
        lineChart.setAnimated(true);

        XYChart.Series series1 = new XYChart.Series();

        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);

        initXLowerBound = ((NumberAxis) lineChart.getXAxis()).getLowerBound();
        initXUpperBound = ((NumberAxis) lineChart.getXAxis()).getUpperBound();
        initYLowerBound = ((NumberAxis) lineChart.getYAxis()).getLowerBound();
        initYUpperBound = ((NumberAxis) lineChart.getYAxis()).getUpperBound();

        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();
    }
    // sum layout shift against parent until we ascend to scene


    EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {

    @Override
    public void handle(MouseEvent mouseEvent) {
        if (mouseEvent.getButton() == MouseButton.PRIMARY) {
            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) {

                if ((rectinitX.get() >= rectX.get()) && (rectinitY.get() >= rectY.get())) {
                    //Condizioni Iniziali
                    LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();

                    ((NumberAxis) lineChart.getXAxis()).setLowerBound(initXLowerBound);
                    ((NumberAxis) lineChart.getXAxis()).setUpperBound(initXUpperBound);

                    ((NumberAxis) lineChart.getYAxis()).setLowerBound(initYLowerBound);
                    ((NumberAxis) lineChart.getYAxis()).setUpperBound(initYUpperBound);

                    ZoomFreeHand(path, 1.0, 1.0, 0, 0);

                } else {
                    //Zoom In

                    double Tgap = 0;
                    double newLowerBound, newUpperBound, axisShift;
                    double xScaleFactor, yScaleFactor;
                    double xaxisShift, yaxisShift;
//                  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();
                    Tgap = yAxis.getHeight()/(yAxis.getUpperBound() - yAxis.getLowerBound());
                    axisShift = getSceneShiftY(yAxis);
                    yaxisShift = axisShift;

                    newUpperBound = yAxis.getUpperBound() - ((rectinitY.get() - axisShift) / Tgap);
                    newLowerBound = yAxis.getUpperBound() - (( rectY.get() - axisShift) / Tgap);


//                  System.out.println("(a) rectinitY.get() "+rectinitY.get()+" rectY.get() "+rectY.get());
//                  System.out.println("(a) Tgap "+Tgap+" axisShift "+axisShift+" yAxis.getLowerBound() "
//                                     + yAxis.getLowerBound()+ " " + yAxis.getUpperBound());

                    if (newUpperBound > yAxis.getUpperBound())
                        newUpperBound = yAxis.getUpperBound();

                    yScaleFactor = (yAxis.getUpperBound() - yAxis.getLowerBound())/(newUpperBound - newLowerBound);
                    yAxis.setLowerBound(newLowerBound);
                    yAxis.setUpperBound(newUpperBound);

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

                    // 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());            

                    Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());            
//                  newXlower = (rectinitX.get()/Tgap) + xAxis.getLowerBound();
//                  newXupper = (rectX.get()/Tgap)+xAxis.getLowerBound();

                    axisShift = getSceneShiftX(xAxis);                        
                    xaxisShift = axisShift;

                    newLowerBound = ((rectinitX.get() - axisShift) / Tgap) + xAxis.getLowerBound();
                    newUpperBound = ((rectX.get() - axisShift) / Tgap) + xAxis.getLowerBound();                

                    if (newUpperBound > xAxis.getUpperBound())
                        newUpperBound = xAxis.getUpperBound();

                    xScaleFactor = (xAxis.getUpperBound() - xAxis.getLowerBound())/(newUpperBound - newLowerBound);
                    xAxis.setLowerBound( newLowerBound );
                    xAxis.setUpperBound( newUpperBound );



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

                    ZoomFreeHand(path, xScaleFactor, yScaleFactor, xaxisShift, yaxisShift);
                }
                // Hide the rectangle
                rectX.set(0);
                rectY.set(0);
            }
        }// end if (mouseEvent.getButton() == MouseButton.PRIMARY)
        else if (mouseEvent.getButton() == MouseButton.SECONDARY) //free hand graphics
        {
            if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
                path.getElements().clear();
                path.getElements().add(new MoveTo(mouseEvent.getX(), mouseEvent.getY()));
            } else if (mouseEvent.getEventType()==MouseEvent.MOUSE_DRAGGED) {
                path.getElements().add(new LineTo(mouseEvent.getX(), mouseEvent.getY()));
            }
        } //end if (mouseEvent.getButton() == MouseButton.SECONDARY)
    }
};

    private static double getSceneShiftX(Node node) {
        double shift = 0;
        do { 
            shift += node.getLayoutX(); 
            node = node.getParent();
        } while (node != null);
        return shift;
    }

    private static double getSceneShiftY(Node node) {
        double shift = 0;
        do { 
            shift += node.getLayoutY(); 
            node = node.getParent();
        } while (node != null);
        return shift;
    }

    private static void ZoomFreeHand(Path path, double xScaleFactor, double yScaleFactor,
            double xaxisShift, double yaxisShift) {
/*
        ObservableList<PathElement> ListPath = path.getElements();

        if (ListPath.size() != 0) {
            MoveTo moveto;
            ((MoveTo)(ListPath.get(0))).setX(0-axisShiftX);    
            ((MoveTo)(ListPath.get(0))).setY(0-axisShiftY);
        }
*/

        path.setScaleX(xScaleFactor);
        path.setScaleY(yScaleFactor);
        path.setTranslateX(xaxisShift);
        path.setTranslateY(yaxisShift);
    }

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

}

Edit: I have had this suggestion

Try using Canvas as an overlay on top of the chart. You can then just scale the Canvas.

but have no clue about how to use canvas for this issue

Edit2: Another suggestion

It seems you coordinates are based on wrong anchors. You need to find a node which contains graph itself and work only with this node's coordinates, not axis or scene. I would even advise to add all mouse events to that node instead of scene to avoid excessive coordinates translation. Another advise would be use ScenicView tool to investigate your application and see which nodes has what coordinates and verify your math.

Edit 3: These pictures may help in better explaining my issue


回答1:


you are talking about a problem where by, when zooming, the drawing is not where you expect it to be, yes?

in the code in ZoomFreeHand, the code to shift the axes is commented out.

i would expect that, to solve the problem, you're going to have to properly shift the axes. even if the commented out code is not working for you, something there needs to properly calculate and shift the axes so that what you've drawn stays where you expect it.




回答2:


I now understand the problem a bit. Below may not help. But anyways ...

I am not sure of the exact requirement, but may be the following would give you some ideas.

Draw everything that you need zoomed inside a parent.

Pane root = new Pane();
root.getChildren().add( new Line( 10, 10, 100, 100 ) );

And let java handle the zoom on everything inside. Like the following will zoom the contents on mouse wheel.

root.setOnScroll( new ScrollHandler() );

private class ScrollHandler implements EventHandler< ScrollEvent > {
    @Override
    public void handle( ScrollEvent event ) {
        root.setScaleX( root.getScaleX() + event.getDeltaY() * .005 );
        root.setScaleY( root.getScaleY() + event.getDeltaY() * .005 );
    }
}



回答3:


I solve this problem by using,

   yAxis.setLowerBound(10);
    yAxis.setUpperBound(100);
    yAxis.setTickUnit(5);


来源:https://stackoverflow.com/questions/12405347/javafx-2-x-still-problems-with-draw-on-chart-zoom

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!