Mouse position relative to origin - charts

How do I calculate the position of the mouse relative to the origin of a line chart?
The code below gets the x-position of the mouse cursor relative to the layout origin of the chart. I'd like to know the mouse position relative to the Cartesian origin instead.
public class Chart extends Application {
private NumberAxis xAxis;
private NumberAxis yAxis;
private LineChart<Number,Number> lineChart;
private Label cursorPosition;
private Label xAxisPosition;
#Override
public void start(Stage primaryStage) {
VBox root = new VBox();
xAxis = new NumberAxis("Date", 0.f, 100.f, 10.f);
yAxis = new NumberAxis("Value", 0.f, 100.f, 10.f);
lineChart = new LineChart<>(xAxis, yAxis);
Series series = new Series();
for (int ii = 1; ii <= 100; ii++) {
series.getData().add(new Data(ii, Math.random()*20.));
}
lineChart.getData().add(series);
lineChart.setOnMouseMoved(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
lineChart.setCursor(Cursor.CROSSHAIR);
cursorPosition.setText(String.valueOf(event.getX()));
}
});
lineChart.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
lineChart.setCursor(Cursor.DEFAULT);
}
});
cursorPosition = new Label();
root.getChildren().addAll(lineChart, cursorPosition,);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}

You'll have to use the chart's axes to convert between display coordinates and cartesian (value) coordinates. xAxis.getDisplayPosition(0) would, for instance, give you the x coordinate for the value 0. You can also use this to convert the current mouse position into 'values'.

Related

How to make my circle appear with classes and mouseclicks?

I am new to JavaFx and i am making a simple drawing program where i draw shapes. The problem i am having now is that i dont know how to make the circle appear on the screen when i click on the screen. So first I want to press a button that says "Circle" and then when i click on the canvas i want it to spawn. (I am switching between scenebuilder and intellij btw).
This is some of my program:
Classes:
public abstract class Shape {
double x;
double y;
double width;
double height;
public Color color = Color.WHITE;
public void creatingShapes(double x, double y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void setColor(Color color) {
this.color = color;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Color getColor() {
return color;
}
public abstract void draw(GraphicsContext g);
}
public class Circle extends Shape {
#Override
public void draw(GraphicsContext g) {
g.setFill(color);
g.fillOval(200,200,200,200);
g.fillRect(getX(),getY(),getWidth(),getHeight());
g.setStroke(Color.BLACK);
g.strokeOval(getX(),getY(),getWidth(),getHeight());
====================================================
Controller class:
public class HelloController {
#FXML
private Button logoutButton;
#FXML
private BorderPane scenePane;
Stage stage;
#FXML
private ColorPicker myColorPicker;
#FXML
private ChoiceBox<String> myChoiceBox;
#FXML
private Button circle;
#FXML
private GraphicsContext g;
private final Shape[] shapes = new Shape[500];
Canvas canvas = new Canvas(310,333);
boolean drawShape = true;
int count = 0;
private final Color currentColor = Color.BLUE;
private final String[] menuAlternatives = {"Sparning", "Testing?", "Exit?"};
public void onCircleClicked() {
circle.setOnAction((event) -> addShape(new Circle()));
}
//skapa shapes
public void addShape(Shape shape) {
shape.setColor(currentColor);
shape.creatingShapes(10,10,150,100);
shapes[count] = shape;
count++;
paintCanvas();
}
public void changeColor(ActionEvent event) {
Color myColor = myColorPicker.getValue();
scenePane.setBackground(new Background(new BackgroundFill(myColor, null, null)));
}
public void initialize() {
g = canvas.getGraphicsContext2D();
}
public void paintCanvas() {
g.setFill(Color.WHITE);
g.fillRect(0,0,400,400);
Arrays.stream(shapes, 0, count).forEach(s -> s.draw(g));
}
Placing a circle on canvas with mouse event
From Doc
public void fillOval(double x,
double y,
double w,
double h)
Fills an oval using the current fill paint.
This method will be affected by any of the global common or fill attributes
as specified in the Rendering Attributes Table.
Parameters:
x - the X coordinate of the upper left bound of the oval.
y - the Y coordinate of the upper left bound of the oval.
w - the width at the center of the oval.
h - the height at the center of the oval.
As we can see x and y params from fillOval() will put the circle at the left upper corner of its bound . to spawn at its very center , we need to subtract half widht and half height to x and y coordinates given by mouseclick event.
this is a single class javafx app you can try
App.java
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) {
Canvas canvas = new Canvas(640, 480);
GraphicsContext context = canvas.getGraphicsContext2D();
canvas.setOnMouseClicked(e -> {
double x = e.getX();
double y = e.getY();
context.setFill(Color.AQUAMARINE);
context.setStroke(Color.BLACK);
context.fillOval(x - 20, y - 20, 40, 40);
context.strokeOval(x - 20, y - 20, 40, 40);
});
var scene = new Scene(new Group(canvas), 640, 480);
stage.setScene(scene);
stage.setTitle("spawn with mouse click");
stage.show();
}
public static void main(String[] args) {
launch();
}
}

JavaFX 8 Dynamic Node scaling

I'm trying to implement a scene with a ScrollPane in which the user can drag a node around and scale it dynamically. I have the dragging and scaling with the mouse wheel working as well as a reset zoom, but I'm having trouble with the calculations to fit the node to the width of the parent.
Here is my code as an sscce.
(works) Mouse wheel will zoom in and out around the mouse pointer
(works) Left or right mouse press to drag the rectangle around
(works) Left double-click to reset the zoom
(doesn't work) Right double-click to fit the width
If I zoom in or out or change the window size, the fit to width does not work.
If anyone can help me with the calculations to fit the node to the width of the parent, I would very much appreciate it.
EDITED:
I marked the method that is not working correctly. It is fitWidth(), which is invoked by right mouse button double-clicking.
I edited the text of the question for clarity and focus
Hopefully this is more clear now.
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ZoomAndPanExample extends Application {
private ScrollPane scrollPane = new ScrollPane();
private final DoubleProperty zoomProperty = new SimpleDoubleProperty(1.0d);
private final DoubleProperty deltaY = new SimpleDoubleProperty(0.0d);
private final Group group = new Group();
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
scrollPane.setPannable(true);
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
AnchorPane.setTopAnchor(scrollPane, 10.0d);
AnchorPane.setRightAnchor(scrollPane, 10.0d);
AnchorPane.setBottomAnchor(scrollPane, 10.0d);
AnchorPane.setLeftAnchor(scrollPane, 10.0d);
AnchorPane root = new AnchorPane();
Rectangle rect = new Rectangle(80, 60);
rect.setStroke(Color.NAVY);
rect.setFill(Color.NAVY);
rect.setStrokeType(StrokeType.INSIDE);
group.getChildren().add(rect);
// create canvas
PanAndZoomPane panAndZoomPane = new PanAndZoomPane();
zoomProperty.bind(panAndZoomPane.myScale);
deltaY.bind(panAndZoomPane.deltaY);
panAndZoomPane.getChildren().add(group);
SceneGestures sceneGestures = new SceneGestures(panAndZoomPane);
scrollPane.setContent(panAndZoomPane);
panAndZoomPane.toBack();
scrollPane.addEventFilter( MouseEvent.MOUSE_CLICKED, sceneGestures.getOnMouseClickedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_PRESSED, sceneGestures.getOnMousePressedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_DRAGGED, sceneGestures.getOnMouseDraggedEventHandler());
scrollPane.addEventFilter( ScrollEvent.ANY, sceneGestures.getOnScrollEventHandler());
root.getChildren().add(scrollPane);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
class PanAndZoomPane extends Pane {
public static final double DEFAULT_DELTA = 1.3d;
DoubleProperty myScale = new SimpleDoubleProperty(1.0);
public DoubleProperty deltaY = new SimpleDoubleProperty(0.0);
private Timeline timeline;
public PanAndZoomPane() {
this.timeline = new Timeline(60);
// add scale transform
scaleXProperty().bind(myScale);
scaleYProperty().bind(myScale);
}
public double getScale() {
return myScale.get();
}
public void setScale( double scale) {
myScale.set(scale);
}
public void setPivot( double x, double y, double scale) {
// note: pivot value must be untransformed, i. e. without scaling
// timeline that scales and moves the node
timeline.getKeyFrames().clear();
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.millis(200), new KeyValue(translateXProperty(), getTranslateX() - x)),
new KeyFrame(Duration.millis(200), new KeyValue(translateYProperty(), getTranslateY() - y)),
new KeyFrame(Duration.millis(200), new KeyValue(myScale, scale))
);
timeline.play();
}
/**
* !!!! The problem is in this method !!!!
*
* The calculations are incorrect, and result in unpredictable behavior
*
*/
public void fitWidth () {
double scale = getParent().getLayoutBounds().getMaxX()/getLayoutBounds().getMaxX();
double oldScale = getScale();
double f = (scale / oldScale)-1;
double dx = getTranslateX() - getBoundsInParent().getMinX() - getBoundsInParent().getWidth()/2;
double dy = getTranslateY() - getBoundsInParent().getMinY() - getBoundsInParent().getHeight()/2;
double newX = f*dx + getBoundsInParent().getMinX();
double newY = f*dy + getBoundsInParent().getMinY();
setPivot(newX, newY, scale);
}
public void resetZoom () {
double scale = 1.0d;
double x = getTranslateX();
double y = getTranslateY();
setPivot(x, y, scale);
}
public double getDeltaY() {
return deltaY.get();
}
public void setDeltaY( double dY) {
deltaY.set(dY);
}
}
/**
* Mouse drag context used for scene and nodes.
*/
class DragContext {
double mouseAnchorX;
double mouseAnchorY;
double translateAnchorX;
double translateAnchorY;
}
/**
* Listeners for making the scene's canvas draggable and zoomable
*/
public class SceneGestures {
private DragContext sceneDragContext = new DragContext();
PanAndZoomPane panAndZoomPane;
public SceneGestures( PanAndZoomPane canvas) {
this.panAndZoomPane = canvas;
}
public EventHandler<MouseEvent> getOnMouseClickedEventHandler() {
return onMouseClickedEventHandler;
}
public EventHandler<MouseEvent> getOnMousePressedEventHandler() {
return onMousePressedEventHandler;
}
public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() {
return onMouseDraggedEventHandler;
}
public EventHandler<ScrollEvent> getOnScrollEventHandler() {
return onScrollEventHandler;
}
private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
sceneDragContext.mouseAnchorX = event.getX();
sceneDragContext.mouseAnchorY = event.getY();
sceneDragContext.translateAnchorX = panAndZoomPane.getTranslateX();
sceneDragContext.translateAnchorY = panAndZoomPane.getTranslateY();
}
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
panAndZoomPane.setTranslateX(sceneDragContext.translateAnchorX + event.getX() - sceneDragContext.mouseAnchorX);
panAndZoomPane.setTranslateY(sceneDragContext.translateAnchorY + event.getY() - sceneDragContext.mouseAnchorY);
event.consume();
}
};
/**
* Mouse wheel handler: zoom to pivot point
*/
private EventHandler<ScrollEvent> onScrollEventHandler = new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double delta = PanAndZoomPane.DEFAULT_DELTA;
double scale = panAndZoomPane.getScale(); // currently we only use Y, same value is used for X
double oldScale = scale;
panAndZoomPane.setDeltaY(event.getDeltaY());
if (panAndZoomPane.deltaY.get() < 0) {
scale /= delta;
} else {
scale *= delta;
}
double f = (scale / oldScale)-1;
double dx = (event.getX() - (panAndZoomPane.getBoundsInParent().getWidth()/2 + panAndZoomPane.getBoundsInParent().getMinX()));
double dy = (event.getY() - (panAndZoomPane.getBoundsInParent().getHeight()/2 + panAndZoomPane.getBoundsInParent().getMinY()));
panAndZoomPane.setPivot(f*dx, f*dy, scale);
event.consume();
}
};
/**
* Mouse click handler
*/
private EventHandler<MouseEvent> onMouseClickedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton().equals(MouseButton.PRIMARY)) {
if (event.getClickCount() == 2) {
panAndZoomPane.resetZoom();
}
}
if (event.getButton().equals(MouseButton.SECONDARY)) {
if (event.getClickCount() == 2) {
panAndZoomPane.fitWidth();
}
}
}
};
}
}
I found the answer. I was looking at the wrong calculations, assuming it to be related to the translations. The real culprit was the calculation for the difference in scale. I simply changed this:
double f = (scale / oldScale)-1;
to this:
double f = scale - oldScale;

JavaFX: Make dragged node visible

I want to drag and drop nodes between flowpanes. I implemented the drag and drop in this way:
public class TouchTask extends BorderPane{
setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Dragboard dragboard = startDragAndDrop(TransferMode.ANY);
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(TASK_DRAG_KEY);
dragboard.setContent(clipboardContent);
event.consume();
}
});
}
The drag and drop works fine, but the problem is, that the node is not moved during the drag and drop gesture. I wanted to implement this drag and drop so that the node has the same position as the mouse during the gesture.
I tried to implement this in the following way:
onMousePressedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// record the current mouse X and Y position on Node
mousex = event.getSceneX();
mousey = event.getSceneY();
x = getLayoutX();
y = getLayoutY();
if (isMoveToFront()) {
toFront();
}
}
});
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// Get the exact moved X and Y
double offsetX = event.getSceneX() - mousex;
double offsetY = event.getSceneY() - mousey;
x += offsetX;
y += offsetY;
double scaledX = x;
double scaledY = y;
setLayoutX(scaledX);
setLayoutY(scaledY);
// again set current Mouse x AND y position
mousex = event.getSceneX();
mousey = event.getSceneY();
event.consume();
}
});
But with this solution, the node is only moved for like 3 pixel and then it stops.

Compressing/Expanding JavaFX 2 data chart

Having a XY Line Chart I would like compress/expand data visualization both for X and Y axis by left mouse click, keep pressed and drag left/right and up/down.
Here is a chart example
and here is the code to plot sample data
public class BaseXYChart extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("Linear plot");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 22, 0.5);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis){
#Override
public String toString(Number object){
return String.format("%7.2f", object);
}
});
final LineChart<String, Number>lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 1.5));
series1.getData().add(new XYChart.Data("Mar", 2));
series1.getData().add(new XYChart.Data("Apr", 2.5));
series1.getData().add(new XYChart.Data("May", 3));
series1.getData().add(new XYChart.Data("Jun", 4));
series1.getData().add(new XYChart.Data("Jul", 6));
series1.getData().add(new XYChart.Data("Aug", 9));
series1.getData().add(new XYChart.Data("Sep", 12));
series1.getData().add(new XYChart.Data("Oct", 15));
series1.getData().add(new XYChart.Data("Nov", 20));
series1.getData().add(new XYChart.Data("Dec", 22));
BorderPane pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
How can I accomplish this? I haven't found any examples anywhere!
Thanks.
Add picture
Result after left mouse click, pressed and drag on Y Axis from top to bottom
Same result should be for X Axis to get a compressed line data by left/right mouse drag
If I understood your question correctly, perhaps you could use something like the following which will resize the chart based on clicking and dragging on the axes.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Effect;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class DraggableAxisResizableChart extends Application {
private static final int UNDEFINED = -1;
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
final LineChart<Number, Number> chart = new LineChart(
xAxis, yAxis,
FXCollections.observableArrayList(
new XYChart.Series("April", FXCollections.observableArrayList(
new XYChart.Data(0, 4), new XYChart.Data(1, 10), new XYChart.Data(2, 18), new XYChart.Data(3, 15)
))
)
);
chart.setPrefSize(400, 300);
chart.setMaxSize(400, 300);
makeXAxisDraggable(xAxis, chart);
makeYAxisDraggable(yAxis, chart);
StackPane layout = new StackPane();
layout.getChildren().add(chart);
stage.setScene(new Scene(layout, 800, 600));
stage.show();
}
private void makeXAxisDraggable(final NumberAxis xAxis, final LineChart<Number, Number> chart) {
final Delta d = new Delta();
xAxis.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
if (d.x == UNDEFINED) {
d.x = event.getSceneX();
d.y = event.getSceneY();
} else {
chart.setMaxHeight(
chart.getPrefHeight() * (
(chart.getPrefHeight() + (event.getSceneY() - d.y) * 2) / chart.getPrefHeight()
)
);
}
}
});
xAxis.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
d.x = UNDEFINED; d.y = UNDEFINED;
chart.setPrefSize(chart.getMaxWidth(), chart.getMaxHeight());
}
});
addMouseoverGlow(xAxis);
}
private void makeYAxisDraggable(final NumberAxis yAxis, final LineChart<Number, Number> chart) {
final Delta d = new Delta();
yAxis.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
if (d.x == -1) {
d.x = event.getSceneX();
d.y = event.getSceneY();
} else {
chart.setMaxWidth(
chart.getPrefWidth() * (
(chart.getPrefWidth() - (event.getSceneX() - d.x) * 2) / chart.getPrefWidth()
)
);
}
}
});
yAxis.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
d.x = UNDEFINED; d.y = UNDEFINED;
chart.setPrefSize(chart.getMaxWidth(), chart.getMaxHeight());
}
});
addMouseoverGlow(yAxis);
}
// create a glow feedback effect on a node when the mouse is hovered over it.
private void addMouseoverGlow(final Node n) {
final Effect glow = new DropShadow(10, Color.GOLDENROD);
n.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
n.setEffect(glow);
}
});
n.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
n.setEffect(null);
}
});
}
// records a relative point location.
class Delta { double x = UNDEFINED, y = UNDEFINED; }
}
An alternate implementation could use a scale on the node.
The implementation above leaves slight ghost trails as the graph is resized, so you may want to fix that up somehow, if the example proves useful.

Javafx 2 set generic class for mouse pointer plot

I have a toggle buttons group with 4 different pointers
When I select the crosshair button ( right most one ) I have as follow
Each button calls a different line pointer follower :
left most one button: it is a simple pointer without line;
second display only vertical line pointer movement linked;
third one only horizontal line pointer movement linked;
a crosshair.
I would like to create a generic class to set the selected line so I can call it in the draw routine.
This generic/abstract class "will contains" the specific line pointer follower code when the appropriate button is selected, in this way the draw routine will only refer to the generic class to plot the select pointer line follower.
The code with crosshair is:
public class JavaFXApplicationMove extends Application {
Path path;
BorderPane pane;
Rectangle rect;
Line LH;
Line LV;
XYChart.Series series1 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
SimpleDoubleProperty rectX = new SimpleDoubleProperty();
SimpleDoubleProperty rectY = new SimpleDoubleProperty();
#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);
xAxis.setAnimated(false);
yAxis.setAnimated(false);
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.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(true);
series1.setName("Stock1");
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));
pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(5);
path.setStroke(Color.RED);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
rect = new Rectangle();
rect.setFill(Color.web("yellow", 0.3));
rect.setStroke(Color.MAGENTA);
rect.setStrokeDashOffset(50);
rect.widthProperty().bind(rectX.subtract(rectinitX));
rect.heightProperty().bind(rectY.subtract(rectinitY));
pane.getChildren().add(rect);
//LH=new Line();
LH=LineBuilder.create()
.startX(0)
.startY(0)
.endX(10)
.endY(.535)
.strokeWidth(1)
.stroke(Color.BLACK)
.build();
pane.getChildren().add(LH);
LV=LineBuilder.create()
.startX(0)
.startY(0)
.endX(10)
.endY(.535)
.strokeWidth(1)
.stroke(Color.BLACK)
.build();
pane.getChildren().add(LV);
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());
LH.setStartX(0);
LH.setStartY(0);
LH.setEndX(0);
LH.setEndY(0);
LV.setStartX(0);
LV.setStartY(0);
LV.setEndX(0);
LV.setEndY(0);
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_RELEASED) {
rectX.set(mouseEvent.getX());
rectY.set(mouseEvent.getY());
// Hide the rectangle
rectX.set(0);
rectY.set(0);
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED || mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED) {
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();
NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
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=xAxis.getLowerBound(), newXupper=xAxis.getUpperBound();
double newYlower=yAxis.getLowerBound(), newYupper=yAxis.getUpperBound();
double xAxisShift = getSceneShift(xAxis);
double yAxisShift = getSceneShift(yAxis);
double yAxisStep=yAxis.getHeight()/(yAxis.getUpperBound()-yAxis.getLowerBound());
double CurrentPrice=yAxis.getUpperBound()-((mouseEvent.getY()-yAxisShift)/yAxisStep);
double Delta=0.3;
if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED){
if(rectinitX.get() < mouseEvent.getX()){
newXlower=xAxis.getLowerBound()-Delta;
newXupper=xAxis.getUpperBound()-Delta;
}
else if(rectinitX.get() > mouseEvent.getX()){
newXlower=xAxis.getLowerBound()+Delta;
newXupper=xAxis.getUpperBound()+Delta;
}
xAxis.setLowerBound( newXlower );
xAxis.setUpperBound( newXupper );
if(rectinitY.get() < mouseEvent.getY()){
newYlower=yAxis.getLowerBound()+Delta/1000;
newYupper=yAxis.getUpperBound()+Delta/1000;
}
else if(rectinitY.get() > mouseEvent.getY()){
newYlower=yAxis.getLowerBound()-Delta/1000;
newYupper=yAxis.getUpperBound()-Delta/1000;
}
yAxis.setLowerBound(newYlower);
yAxis.setUpperBound(newYupper);
}
//System.out.println("(b) xAxis.getLowerBound() "+xAxis.getLowerBound()+" "+xAxis.getUpperBound());
rectinitX.set(mouseEvent.getX());
rectinitY.set(mouseEvent.getY());
if(mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED && mouseEvent.getY()>yAxisShift && mouseEvent.getY()<yAxisShift+yAxis.getHeight() && mouseEvent.getX()>xAxisShift && mouseEvent.getX()<xAxisShift+xAxis.getWidth()){
LH.setStartX(xAxisShift);
LH.setStartY(mouseEvent.getY());
LH.setEndX(xAxisShift+xAxis.getWidth());
LH.setEndY(mouseEvent.getY());
LV.setStartX(mouseEvent.getX());
LV.setStartY(yAxisShift);
LV.setEndX(mouseEvent.getX());
LV.setEndY(yAxisShift+yAxis.getHeight());
double XX=((mouseEvent.getX() - xAxisShift) / Tgap) + xAxis.getLowerBound();
double YY=CurrentPrice;
series1.setName(String.format("%.2g%n",XX) + ", " + String.format("%.4g%n",YY));
int XLB=(int) xAxis.getLowerBound();
int XUB=(int) xAxis.getUpperBound();
}
}
}
};
private static double getSceneShift(Node node) {
double shift = 0;
do {
shift += node.getLayoutX();
node = node.getParent();
} while (node != null);
return shift;
}
private static String getHIstLOstY(XYChart.Series S,int XLowerBound,int XUpperBound) {
double ValLOst=1000000;
double ValHIst=-1000000;
for(int i=XLowerBound; i<XUpperBound; i++){
double P=GetPrice(S,i);
if(ValHIst<P){
ValHIst=P;
}
if(ValLOst>P){
ValLOst=P;
}
}
return Double.toString(ValLOst) + "," + Double.toString(ValHIst);
}
private static double GetPrice(XYChart.Series S,int IX) {
Object SVal=S.getData().get(IX);
//return SVal.toString().toLowerCase();
String Temp=SVal.toString().replaceAll("Data", "");
Temp=Temp.replace("[", "");
Temp=Temp.replace("]", "");
String[] TempArray=Temp.split(",");
return Double.parseDouble(TempArray[1]);
}
public static void main(String[] args) {
launch(args);
}
}
All your drawing routing in handed in EventHandler<MouseEvent>. So easiest way to change crosshair type would be substituting various handlers.
By going a bit deeper you'll see that every crosshair drawing is based on similar parameters and can be divided in 3 steps: start, update and end.
From what I see in your code important parameters are sceneShiftX, sceneShiftY, mouseX, mouseY. So, you need to refactor out drawing code from mouse event handler to a separate class with next methods:
public class SimpleCrosshair {
public void start(double mouseX, double mouseY);
public void update(double sceneShiftX, double sceneShiftY, double mouseX, double mouseY);
public void finish();
}
You put crosshair related code from MouseEvent.MOUSE_PRESSED block to start(), from MouseEvent.MOUSE_RELEASED to finish() and from MouseEvent.MOUSE_DRAGGED to update().
Now as you separated all your drawing data from mouse events and can introduce an interface Crosshair which will look similar to class above and make SimpleCrosshair implement Crosshair.
The rest is easy. Create classes implementing Crosshair which will do drawing for other crosshair type and introduce field private Crosshair currentCrosshair; which you can update with concrete implementation on toolbar button click.
And your mouse events handler will look like (aside from zoom logic):
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
currentCrosshair.start(mouseEvent.getX(), mouseEvent.getY());
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_RELEASED) {
currentCrosshair.finish();
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED || mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED) {
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();
NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
double xAxisShift = getSceneShift(xAxis);
double yAxisShift = getSceneShift(yAxis);
currentCrosshair.update(xAxisShift, yAxisShift, mouseEvent.getX(), mouseEvent.getY());
}
}
}