Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javafx 2 chart and freehand zoom

This code plots an XYChart and using combination of right mouse button click and drag perform free hand draw, while with left mouse button click and drag perform Zoom In on selected area.

My problem is about zoom on freehand draw it always get translated. For example try to draw somewhere around a corner.

How can I solve this?

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

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.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()))
                {
                    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;

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


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

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


                    NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();

                    Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - 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 );                      

                    ZoomFreeHand(path, xScaleFactor, yScaleFactor, xaxisShift, yaxisShift);
                }
                // Hide the rectangle
                rectX.set(0);
                rectY.set(0);
            }
    }
    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()));
        }
    } 
  }
  };
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) {

double layX, layY;

layX = path.getLayoutX();
layY = path.getLayoutY();
path.setScaleX(xScaleFactor);
path.setScaleY(yScaleFactor);
path.setTranslateX(xaxisShift);
path.setTranslateY(yaxisShift);
  }
  public static void main(String[] args) {
    launch(args);
   }        
  }

I think it worth using something like this:

 ObservableList<PathElement> mypathElements = path.getElements();
 for (int i = 0; i < mypathElements.size(); i++) {
   final PathElement element = mypathElements.get(i);
   if (element instanceof MoveTo) {
     final MoveTo move = (MoveTo)element;
     //change move coordinates     
   } else if (element instanceof LineTo) {
   final LineTo line = (LineTo)element;                                    
   //change line coordinates
   }
 }
like image 393
famedoro Avatar asked May 04 '12 18:05

famedoro


2 Answers

Same issue as in Javafx 2 chart resize with freehand draw and same answer:

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.

like image 126
Sergey Grinev Avatar answered Nov 20 '22 12:11

Sergey Grinev


I'd recommend replace those:

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

with Node.localToScene, Node.sceneToLocal conversion methods that took points or Bounds as arguments.

like image 1
Alexander K Avatar answered Nov 20 '22 12:11

Alexander K