Skip to content

Instantly share code, notes, and snippets.

@james-d
Created October 31, 2013 16:29
Show Gist options
  • Save james-d/7252698 to your computer and use it in GitHub Desktop.
Save james-d/7252698 to your computer and use it in GitHub Desktop.
Example of a LineChart that can be zoomed via mouse.
import java.util.Collections;
import java.util.Random;
import javafx.application.Application;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ZoomableLineChart extends Application {
private static final int NUM_DATA_POINTS = 1000 ;
@Override
public void start(Stage primaryStage) {
final LineChart<Number, Number> chart = createChart();
final StackPane chartContainer = new StackPane();
chartContainer.getChildren().add(chart);
final Rectangle zoomRect = new Rectangle();
zoomRect.setManaged(false);
zoomRect.setFill(Color.LIGHTSEAGREEN.deriveColor(0, 1, 1, 0.5));
chartContainer.getChildren().add(zoomRect);
setUpZooming(zoomRect, chart);
final HBox controls = new HBox(10);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
final Button zoomButton = new Button("Zoom");
final Button resetButton = new Button("Reset");
zoomButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
doZoom(zoomRect, chart);
}
});
resetButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
final NumberAxis xAxis = (NumberAxis)chart.getXAxis();
xAxis.setLowerBound(0);
xAxis.setUpperBound(1000);
final NumberAxis yAxis = (NumberAxis)chart.getYAxis();
yAxis.setLowerBound(0);
yAxis.setUpperBound(1000);
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
});
final BooleanBinding disableControls =
zoomRect.widthProperty().lessThan(5)
.or(zoomRect.heightProperty().lessThan(5));
zoomButton.disableProperty().bind(disableControls);
controls.getChildren().addAll(zoomButton, resetButton);
final BorderPane root = new BorderPane();
root.setCenter(chartContainer);
root.setBottom(controls);
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private LineChart<Number, Number> createChart() {
final NumberAxis xAxis = createAxis();
final NumberAxis yAxis = createAxis();
final LineChart<Number, Number> chart = new LineChart<>(xAxis, yAxis);
chart.setAnimated(false);
chart.setCreateSymbols(false);
chart.setData(generateChartData());
return chart ;
}
private NumberAxis createAxis() {
final NumberAxis xAxis = new NumberAxis();
xAxis.setAutoRanging(false);
xAxis.setLowerBound(0);
xAxis.setUpperBound(1000);
return xAxis;
}
private ObservableList<Series<Number, Number>> generateChartData() {
final Series<Number, Number> series = new Series<>();
series.setName("Data");
final Random rng = new Random();
for (int i=0; i<NUM_DATA_POINTS; i++) {
Data<Number, Number> dataPoint = new Data<Number, Number>(i, rng.nextInt(1000));
series.getData().add(dataPoint);
}
return FXCollections.observableArrayList(Collections.singleton(series));
}
private void setUpZooming(final Rectangle rect, final Node zoomingNode) {
final ObjectProperty<Point2D> mouseAnchor = new SimpleObjectProperty<>();
zoomingNode.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
mouseAnchor.set(new Point2D(event.getX(), event.getY()));
rect.setWidth(0);
rect.setHeight(0);
}
});
zoomingNode.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
double x = event.getX();
double y = event.getY();
rect.setX(Math.min(x, mouseAnchor.get().getX()));
rect.setY(Math.min(y, mouseAnchor.get().getY()));
rect.setWidth(Math.abs(x - mouseAnchor.get().getX()));
rect.setHeight(Math.abs(y - mouseAnchor.get().getY()));
}
});
}
private void doZoom(Rectangle zoomRect, LineChart<Number, Number> chart) {
Point2D zoomTopLeft = new Point2D(zoomRect.getX(), zoomRect.getY());
Point2D zoomBottomRight = new Point2D(zoomRect.getX() + zoomRect.getWidth(), zoomRect.getY() + zoomRect.getHeight());
final NumberAxis yAxis = (NumberAxis) chart.getYAxis();
Point2D yAxisInScene = yAxis.localToScene(0, 0);
final NumberAxis xAxis = (NumberAxis) chart.getXAxis();
Point2D xAxisInScene = xAxis.localToScene(0, 0);
double xOffset = zoomTopLeft.getX() - yAxisInScene.getX() ;
double yOffset = zoomBottomRight.getY() - xAxisInScene.getY();
double xAxisScale = xAxis.getScale();
double yAxisScale = yAxis.getScale();
xAxis.setLowerBound(xAxis.getLowerBound() + xOffset / xAxisScale);
xAxis.setUpperBound(xAxis.getLowerBound() + zoomRect.getWidth() / xAxisScale);
yAxis.setLowerBound(yAxis.getLowerBound() + yOffset / yAxisScale);
yAxis.setUpperBound(yAxis.getLowerBound() - zoomRect.getHeight() / yAxisScale);
System.out.println(yAxis.getLowerBound() + " " + yAxis.getUpperBound());
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
public static void main(String[] args) {
launch(args);
}
}
@AndreNL22
Copy link

Line 150 should be as follows:

double xOffset = zoomTopLeft.getX() - (yAxisInScene.getX() - yAxis.getWidth());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment