问题
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