JavaFx 2 - Draw line overlap axis - javafx-2

With a left mouse click then mouse moved, a line is drawned on this line chart, and also on axis.
I would like to draw line only on chart so that it does not overlap x or y axis. How to accomplish this?
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
public class LinesEdit extends Application {
Path path;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setSide(Side.RIGHT);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
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", 4));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 5));
series1.getData().add(new XYChart.Data("May", 6));
series1.getData().add(new XYChart.Data("Jun", 8));
series1.getData().add(new XYChart.Data("Jul", 12));
series1.getData().add(new XYChart.Data("Aug", 8));
series1.getData().add(new XYChart.Data("Sep", 11));
series1.getData().add(new XYChart.Data("Oct", 13));
series1.getData().add(new XYChart.Data("Nov", 10));
series1.getData().add(new XYChart.Data("Dec", 20));
BorderPane bp = new BorderPane();
bp.setCenter(lineChart);
Scene scene = new Scene(bp, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler( bp );
bp.setOnMouseClicked( mh );
bp.setOnMouseMoved( mh );
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
scene.setOnMouseDragged(mh);
scene.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
stage.show();
}
class MouseHandler implements EventHandler< MouseEvent > {
private boolean gotFirst = false;
private Line line;
private Pane pane;
private double x1, y1, x2, y2;
private LineHandler lineHandler;
public MouseHandler( Pane pane ) {
this.pane = pane;
lineHandler = new LineHandler(pane);
}
class LineHandler implements EventHandler< MouseEvent > {
double x, y;
Pane pane;
public LineHandler(Pane pane){
this.pane = pane;
}
#Override
public void handle( MouseEvent e ) {
Line l = (Line) e.getSource();
// remove line on right click
if( e.getEventType() == MouseEvent.MOUSE_PRESSED
&& e.isSecondaryButtonDown() ) {
pane.getChildren().remove( l );
} else if( e.getEventType() == MouseEvent.MOUSE_DRAGGED
&& e.isPrimaryButtonDown() ) {
double tx = e.getX();
double ty = e.getY();
double dx = tx - x;
double dy = ty - y;
l.setStartX( l.getStartX() + dx );
l.setStartY( l.getStartY() + dy );
l.setEndX( l.getEndX() + dx );
l.setEndY( l.getEndY() + dy );
x = tx;
y = ty;
} else if( e.getEventType() == MouseEvent.MOUSE_ENTERED ) {
// just to show that the line is selected
x = e.getX();
y = e.getY();
l.setStroke( Color.RED );
} else if( e.getEventType() == MouseEvent.MOUSE_EXITED ) {
l.setStroke( Color.BLACK );
}
// should not pass event to the parent
e.consume();
}
}
#Override
public void handle( MouseEvent event ) {
if( event.getEventType() == MouseEvent.MOUSE_CLICKED ) {
if( !gotFirst ) {
x1 = x2 = event.getX();
y1 = y2 = event.getY();
line = new Line( x1, y1, x2, y2 );
pane.getChildren().add( line );
gotFirst = true;
}
else {
line.setOnMouseEntered( lineHandler );
line.setOnMouseExited( lineHandler );
line.setOnMouseDragged( lineHandler );
line.setOnMousePressed( lineHandler );
// to consume the event
line.setOnMouseClicked( lineHandler );
line.setOnMouseReleased( lineHandler );
line = null;
gotFirst = false;
}
}
else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
}
}
}
}
}

The first thing to know is that many JavaFX UI-Element like charts consist of many underlying children. The part where you want to draw is in fact a Region that is part of a Pane that is child of the LineChart. I really recommend you to use ScenicView, because it shows exactly how your scene graph (including built-in UI-Components) looks like.
Back to your problem: your listeners should only apply to the Region which shows the actual representation of the data. That Region ends exactly where the x and y axis are. The following code will get you that Region and make it the target for your listeners:
//your previous code in start()...
Pane p = (Pane) lineChart.getChildrenUnmodifiable().get(1);
Region r = (Region) p.getChildren().get(0);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler(r);
r.setOnMouseClicked(mh);
r.setOnMouseMoved(mh);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
r.setOnMouseDragged(mh);
r.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
//the following code.....
The next steps are:
1. rewrite your handler methods, so that they accept a `Region` object as parameter
2. rewrite a bit of your line-setting code. Background: you cannot add objects into a `Region` because you cannot access the writable `List` of childrens. So you have to put the lines into the `Pane` object which holds the `Region`. Because every JavaFX UI-Element has its own coordinate system, you have to calculate the offset between the `Pane` and the `Region`, because the `Pane` is a bit larger. If you don't do this, your line will be drawn slightly above the mousepointer. You can get the width and height of a `Pane` and/or `Region` by calling `xyz.getBoundsInLocal().getWidth()/height()`.
UPDATE: Full Solution as requested
As requested in the comments i will show one way to solve this problem. Have a look at your GUI in this picture. It shows all graphical elements in ScenicView. As you can see, the Pane is bigger than the inner Region. Important for us is to know that the origin of all coordinate systems in JavaFX start at the upper left corner of an element. In this scenario, you have to add a line to the Pane, but in respect to the borders of the Region. In the code-snippet i showed earlier i added all of your listeners to the region, which means we get the mouse coordinates inside the coordinate system of the region. Now we have to "translate" or better "transform" these coordinates (the points you wish to set the start or endpoints of the line) into the coordinate system of the Pane (the place the line is actually placed, read above why), because we want the line to start exactly where our mouse is. There is a method you can call to get a transformation matrix: r.getLocalToParentTransform(). We need this matrix because we have to get the exact values for the x and y translation that is applied to the Region (you can see that the Region is approximatly 10 pixels moved from the Panes origin in both x and y axes)
I wrote a simple method for getting the x and y translation between the Region and the Pane: getCoordDiff(Region r, Pane p).
The rest of start() method remains unchanged (but with the changes i wrote earlier). But the handle() methods of MouseHandler and LineHandler have to be modified.
public class LinesEdit extends Application
{
Path path;
public double[] getCoordDiff(Region r, Pane p)
{
//Acquires transformation matrix and returns x and y offset/translation from parent
double[] diffs =
{ r.getLocalToParentTransform().getTx(), r.getLocalToParentTransform().getTy() };
return diffs;
}
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21, 0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setSide(Side.RIGHT);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis)
{
#Override
public String toString(Number object)
{
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
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", 4));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 5));
series1.getData().add(new XYChart.Data("May", 6));
series1.getData().add(new XYChart.Data("Jun", 8));
series1.getData().add(new XYChart.Data("Jul", 12));
series1.getData().add(new XYChart.Data("Aug", 8));
series1.getData().add(new XYChart.Data("Sep", 11));
series1.getData().add(new XYChart.Data("Oct", 13));
series1.getData().add(new XYChart.Data("Nov", 10));
series1.getData().add(new XYChart.Data("Dec", 20));
BorderPane bp = new BorderPane();
bp.setCenter(lineChart);
Scene scene = new Scene(bp, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
Pane p = (Pane) lineChart.getChildrenUnmodifiable().get(1);
Region r = (Region) p.getChildren().get(0);
LinesEdit.MouseHandler mh = new LinesEdit.MouseHandler(r);
r.setOnMouseClicked(mh);
r.setOnMouseMoved(mh);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
r.setOnMouseDragged(mh);
r.setOnMousePressed(mh);
bp.getChildren().add(path);
stage.setScene(scene);
ScenicView.show(scene);
stage.show();
}
class MouseHandler implements EventHandler<MouseEvent>
{
private boolean gotFirst = false;
private Line line;
private Region reg;
private double x1, y1, x2, y2;
private LineHandler lineHandler;
public MouseHandler(Region reg)
{
this.reg = reg;
lineHandler = new LineHandler(reg);
}
class LineHandler implements EventHandler<MouseEvent>
{
double x, y;
Region reg;
public LineHandler(Region reg)
{
this.reg = reg;
}
#Override
public void handle(MouseEvent e)
{
Line l = (Line) e.getSource();
l.setStrokeWidth(3);
// remove line on right click
if (e.getEventType() == MouseEvent.MOUSE_PRESSED && e.isSecondaryButtonDown())
{
((Pane) reg.getParent()).getChildren().remove(l);
}
else if (e.getEventType() == MouseEvent.MOUSE_DRAGGED && e.isPrimaryButtonDown())
{
double tx = e.getX();
double ty = e.getY();
double dx = tx - x;
double dy = ty - y;
l.setStartX(l.getStartX() + dx);
l.setStartY(l.getStartY() + dy);
l.setEndX(l.getEndX() + dx);
l.setEndY(l.getEndY() + dy);
x = tx;
y = ty;
}
else if (e.getEventType() == MouseEvent.MOUSE_ENTERED)
{
// just to show that the line is selected
x = e.getX();
y = e.getY();
l.setStroke(Color.RED);
}
else if (e.getEventType() == MouseEvent.MOUSE_EXITED)
{
l.setStroke(Color.BLACK);
}
// should not pass event to the parent
e.consume();
}
}
#Override
public void handle(MouseEvent event)
{
if (event.getEventType() == MouseEvent.MOUSE_CLICKED)
{
double[] diff = getCoordDiff(reg, (Pane) reg.getParent());
if (!gotFirst)
{
//add translation to start/endcoordinates
x1 = x2 = event.getX() + diff[0];
y1 = y2 = event.getY() + diff[1];
line = new Line(x1, y1, x2, y2);
line.setStrokeWidth(3);
((Pane) reg.getParent()).getChildren().add(line);
gotFirst = true;
line.setOnMouseClicked(new EventHandler<Event>()
{
#Override
public void handle(Event arg0)
{
line.setOnMouseEntered(lineHandler);
line.setOnMouseExited(lineHandler);
line.setOnMouseDragged(lineHandler);
line.setOnMousePressed(lineHandler);
// to consume the event
line.setOnMouseClicked(lineHandler);
line.setOnMouseReleased(lineHandler);
line = null;
gotFirst = false;
}
});
}
}
else
{
if (line != null)
{
double[] diff = getCoordDiff(reg, (Pane) reg.getParent());
//add translation to end coordinates
x2 = event.getX() + diff[0];
y2 = event.getY() + diff[1];
// update line
line.setEndX(x2);
line.setEndY(y2);
}
}
}
}
}
You can see the parts where i add the translation values to the lines start and endpoints. This is needed so that line really starts and ends at the points where your mouse is. I moved your code that was executed if gotFirst == true, because it prevented the user to place the line (so that it doesn't follow the cursor). Background: your cursor is now always (pixel perfect) at the end of the line , which doesn't have a 'Listener' at the moment you place it the first time. That missing listener prevents the MouseEvent-click from going to the Region. In short: the "Click" event is now always done on the line itself, thats why we need a listener before the line is finally placed.
Remaining bugs: a line cannot be placed on the yellow lines of the graph. Thats because the click-event is not triggerd on the lines. I might fix that bug at a later time, or you try yourself.

Related

Draw Graphics on bitmap

I try to use mouse up and mouse down to draw rectangles on bitmaps. But the problem is that the rectangle always delay one event. For example, I try to draw a rectangle (0,0,50,50) in the first time but there is no rectangle drawing on bitmap. I continues drawing a rect (50,50,100,100) but a rect (0,0,50,50) created (not be the rect (50,50,100,100). If I keep to draw next rects, it always delay like that. Please help me!
This is my code:
Rectangle rect = new Rectangle(0, 0, 0, 0);
int Xmouse;
int Ymouse;
public Form1()
{
InitializeComponent();
pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
this.DoubleBuffered = true;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
Xmouse = e.X;
Ymouse = e.Y;
drawOK = true;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (drawOK)
{
drawOK = false;
rect = new Rectangle(Xmouse * 3676 / 800, Ymouse * 3676 / 800, (e.X - Xmouse) * 3676 / 800, (e.Y - Ymouse) * 3676 / 800);
pictureBox1.Invalidate();
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (var g = Graphics.FromImage(pictureBox1.Image))
{
using (Pen myPen = new Pen(Color.Black, 6))
{
g.DrawRectangle(myPen, rect);
}
}
}
Try moving your draw method outside of paint into a different sub and call that sub manually from mouse up:
Rectangle rect = new Rectangle(0, 0, 0, 0);
int Xmouse;
int Ymouse;
bool drawOK;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
Xmouse = e.X;
Ymouse = e.Y;
Console.WriteLine("MouseDown({0},{1})", e.X, e.Y);
drawOK = true;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (drawOK)
{
drawOK = false;
Console.WriteLine("MouseUp({0},{1})", e.X, e.Y);
rect = new Rectangle(Xmouse , Ymouse , (e.X - Xmouse) , (e.Y - Ymouse) );
DrawRect();
pictureBox1.Invalidate();
}
}
private void DrawRect()
{
Console.WriteLine("drawing {0}", rect);
using (var g = Graphics.FromImage(pictureBox1.Image))
{
using (Pen myPen = new Pen(Color.Black, 6))
{
g.DrawRectangle(myPen, rect);
}
}
}
private void ClearPic()
{
using (var g = Graphics.FromImage(pictureBox1.Image))
{
using (Brush myPen = Brushes.White)
{
g.Clear(Color.White);
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
Image bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
pictureBox1.Image = bmp;
ClearPic();
pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
pictureBox1.MouseDown += pictureBox1_MouseDown;
pictureBox1.MouseUp += pictureBox1_MouseUp;
}

JavaFX 2.2 MouseEvent in SubScene does not work properly

I am trying to get this to work for sometime and I cannot figure out what wrong with my code. This leads me to believe there is some issues in SubScene Mouse listener. Any idea is appreciated.
Basically I have a scene contains two subscenes, one for the toolbar and one for the floor which has bunch of lines making it looks like tiles. I added mouse listeners so that when I clicked on the floor and move the mouse, the camera will move as if I am walking on the floor.
The problem is that the floor only recognize mouse event when I clicked on the intersection between the first vertical and the first horizontal line (yup, took me a while to figure that out). Mouse event should occur everywhere on entire floor.
Here is the code.
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class FloorTest extends Application {
double mousex, mousey;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
Group bargroup = new Group();
SubScene bar = new SubScene(bargroup, 300, 20, true, SceneAntialiasing.DISABLED);
bargroup.getChildren().add(btn);
Group floorgroup = new Group();
SubScene floor = new SubScene(floorgroup, 300, 250, true, SceneAntialiasing.DISABLED);
ObservableList<Node> list = floorgroup.getChildren();
for(int i = 0; i < (300/20); i++)
{
double x = i * 20;
Line line = new Line(x, 0, x, 250);
list.add(line);
}
for(int i = 0; i < (250/20); i++)
{
double y = i * 20;
Line line = new Line(0, y, 300, y);
list.add(line);
}
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setNearClip(0.1);
camera.setFarClip(10000.0);
camera.setTranslateZ(-200);
floor.setCamera(camera);
floor.setOnMousePressed((MouseEvent event) -> {
mousex = event.getSceneX();
mousey = event.getSceneY();
});
floor.setOnMouseDragged((MouseEvent event) -> {
double x = event.getSceneX();
double y = event.getSceneY();
camera.relocate(camera.getLayoutX() + (x - mousex), camera.getLayoutY() + (y - mousey));
});
Group mainroot = new Group();
mainroot.getChildren().addAll(floor, bar);
Scene scene = new Scene(mainroot, 300, 250, true);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
It seems that setting the subscene.setPickOnBounds(true) should help in proper recognition of the mouse events for the whole subscene. Tested with javafx 8.
Please try using scene instead of floor variable in event handlers
Example:
scene.setOnMousePressed((MouseEvent event) -> {
mousex = event.getSceneX();
mousey = event.getSceneY();
});
scene.setOnMouseDragged((MouseEvent event) -> {
double x = event.getSceneX();
double y = event.getSceneY();
camera.relocate(camera.getLayoutX() + (x - mousex), camera.getLayoutY() + (y - mousey));
});
That helps me

Line chart and Scatter chart overplot [duplicate]

This question already has answers here:
How to combine scatter chart with line chart to show line of regression? JavaFX
(4 answers)
Closed 8 years ago.
This code plots a LineChart and by clicking the "Add serie" button it adds a ScatterChart
It seems all ok but just move scene left/right or up/down (left mouse click and drag) both X, Y axis and charts are displaced.
How to fix this code in order to always get axis overlap as well as both charts?
Thanks.
import java.util.Set;
import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Group;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.chart.*;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
public class RescalingSeries extends Application {
StackPane mainGraphStackPane = null;
Button btnAdd;
BorderPane pane;
XYChart.Series series1 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
protected static Axis _duplicateAxis(Axis axis, Axis result) {
result.setAnimated(axis.animatedProperty().get());
result.setAutoRanging(axis.isAutoRanging());
result.setLabel(axis.getLabel());
result.setSide(axis.getSide());
result.setTickLabelFill(axis.getTickLabelFill());
result.setTickLabelFont(axis.getTickLabelFont());
result.setTickLabelGap(axis.getTickLabelGap());
result.setTickLength(axis.getTickLength());
return result;
}
protected static ValueAxis _duplicateValueAxis(ValueAxis axis, ValueAxis result) {
_duplicateAxis(axis, result);
result.setLowerBound(axis.getLowerBound());
result.setUpperBound(axis.getUpperBound());
result.setMinorTickCount(axis.getMinorTickCount());
result.setMinorTickLength(axis.getMinorTickLength());
result.setTickLabelFormatter(axis.getTickLabelFormatter());
return result;
}
/**
* Duplicate a number axis.
* #param axis The source axis.
* #return A {#code NumberAxis}, never {#code null}.
*/
public static NumberAxis duplicateNumberAxis(NumberAxis axis) {
NumberAxis result = new NumberAxis();
_duplicateValueAxis(axis, result);
result.setTickUnit(axis.getTickUnit());
result.setForceZeroInRange(axis.isForceZeroInRange());
return result;
}
/**
* Duplicate a category axis.
* #param axis The source axis.
* #return A {#code CategoryAxis}, never {#code null}.
*/
public static CategoryAxis duplicateCategoryAxis(CategoryAxis axis) {
CategoryAxis result = new CategoryAxis(axis.getCategories());
_duplicateAxis(axis, result);
result.setStartMargin(axis.getStartMargin());
result.setEndMargin(axis.getEndMargin());
result.setGapStartAndEnd(axis.gapStartAndEndProperty().get());
return result;
}
#Override
public void start(Stage stage) {
final NumberAxis xAxisLC = new NumberAxis(1, 12, 1);
final NumberAxis yAxisLC = new NumberAxis(0.53000, 0.53910, 0.0005);
yAxisLC.setSide(Side.RIGHT);
yAxisLC.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxisLC) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart = new LineChart<>(xAxisLC, yAxisLC);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(true);
lineChart.setLegendVisible(false);
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.531035));
pane = new BorderPane();
pane.setCenter(lineChart);
mainGraphStackPane = new StackPane();
mainGraphStackPane.getChildren().add(pane);
Scene scene = new Scene(mainGraphStackPane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
Group root = new Group();
btnAdd = new Button();
btnAdd.setText("Add serie");
root.getChildren().add(btnAdd);
pane.getChildren().add(root);
btnAdd.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
NumberAxis xAxisBC = duplicateNumberAxis(xAxisLC);
NumberAxis yAxisBC = duplicateNumberAxis(yAxisLC);
ScatterChart<Number, Number> scatterChart = new ScatterChart<>(xAxisBC, yAxisBC);
scatterChart.setAlternativeRowFillVisible(false);
scatterChart.setAnimated(true);
scatterChart.setLegendVisible(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data(1, 0.53185));
series2.getData().add(new XYChart.Data(2, 0.532235));
series2.getData().add(new XYChart.Data(3, 0.53234));
series2.getData().add(new XYChart.Data(4, 0.538765));
series2.getData().add(new XYChart.Data(5, 0.53442));
series2.getData().add(new XYChart.Data(6, 0.534658));
series2.getData().add(new XYChart.Data(7, 0.53023));
series2.getData().add(new XYChart.Data(8, 0.53001));
series2.getData().add(new XYChart.Data(9, 0.53589));
series2.getData().add(new XYChart.Data(10, 0.53476));
series2.getData().add(new XYChart.Data(11, 0.530123));
series2.getData().add(new XYChart.Data(12, 0.531035));
scatterChart.getData().addAll(series2);
Set<Node> chartNode = scatterChart.lookupAll(".chart-plot-background");
for(final Node chr : chartNode){
chr.setStyle("-fx-background-color: transparent;");
}
chartNode = lineChart.lookupAll(".chart-plot-background");
for(final Node chr : chartNode){
chr.setStyle("-fx-background-color: transparent");
}
mainGraphStackPane.getChildren().add(scatterChart);
xAxisBC.lowerBoundProperty().bind(xAxisLC.lowerBoundProperty());
yAxisBC.lowerBoundProperty().bind(yAxisLC.lowerBoundProperty());
}
});
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
boolean XScaling=false;
boolean YScaling=false;
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 Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());
double newXlower=xAxis.getLowerBound(), newXupper=xAxis.getUpperBound();
double newYlower=yAxis.getLowerBound(), newYupper=yAxis.getUpperBound();
double xAxisShift = xAxis.localToScene(0, 0).getX();
double yAxisShift = yAxis.localToScene(0, 0).getY();
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 && mouseEvent.getX()<xAxisShift+yAxis.getHeight() && mouseEvent.getY()<yAxisShift+yAxis.getHeight() && (XScaling==false || YScaling==false)){
//==================================================== X-Axis Moving ==================================
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 );
//===================================================== Y-Axis Moving ====================================
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);
}
//----------------------------- Re-Scale the X-Axis when dragging below it ---------------------------------
else if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getY()>yAxisShift+yAxis.getHeight() ){
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 );
}
//--------------------------------- Re-Scale the Y-Axis when dragging to the left of it --------------------------
else if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getX()> (xAxisShift + xAxis.getWidth())){
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);
}
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()){
double XX=((mouseEvent.getX() - xAxisShift) / Tgap) + xAxis.getLowerBound();
double YY=CurrentPrice;
series1.setName(String.format("%.2g%n",XX) + ", " + String.format("%.4g%n",YY));
}
}
}
};
public static void main(String[] args) {
launch(args);
}
}
Found a solution: I missed this part
xAxisBC.upperBoundProperty().bind(xAxisLC.upperBoundProperty());
yAxisBC.upperBoundProperty().bind(yAxisLC.upperBoundProperty());

How to clone X and Y axis?

I have a barChart with overplot a lineChart, two different series
barChart.getData().addAll(series1);
lineChart.getData().addAll(series2);
My question: is it possible, and how to, clone barChart axis so to get the same settings for lineChart?
What I would like to have is a perfect axis settings overlapping.
Thanks
Edit: at this link
axis clone method
I see there is a clone method Methods inherited from class java.lang.Object
How can I use this method?
Thanks all
Edit2: below is a sample class to show my problem: it plots a line chart and by clicking the Add serie button it adds a scatter chart.
As it plots yellow dots both axis overlap, but just move chart left/right or upper/dow by left mouse click and drag anywhere on the chart and axis goes out of sync.
Same problem for axis expand/compress by left mouse click and drag on X or Y axis
import java.util.Set;
import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Group;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.chart.*;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
public class RescalingSeries extends Application {
StackPane mainGraphStackPane = null;
Button btnAdd;
BorderPane pane;
XYChart.Series series1 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
protected static Axis _duplicateAxis(Axis axis, Axis result) {
result.setAnimated(axis.animatedProperty().get());
result.setAutoRanging(axis.isAutoRanging());
result.setLabel(axis.getLabel());
result.setSide(axis.getSide());
result.setTickLabelFill(axis.getTickLabelFill());
result.setTickLabelFont(axis.getTickLabelFont());
result.setTickLabelGap(axis.getTickLabelGap());
result.setTickLength(axis.getTickLength());
return result;
}
protected static ValueAxis _duplicateValueAxis(ValueAxis axis, ValueAxis result) {
_duplicateAxis(axis, result);
result.setLowerBound(axis.getLowerBound());
result.setUpperBound(axis.getUpperBound());
result.setMinorTickCount(axis.getMinorTickCount());
result.setMinorTickLength(axis.getMinorTickLength());
result.setTickLabelFormatter(axis.getTickLabelFormatter());
return result;
}
/**
* Duplicate a number axis.
* #param axis The source axis.
* #return A {#code NumberAxis}, never {#code null}.
*/
public static NumberAxis duplicateNumberAxis(NumberAxis axis) {
NumberAxis result = new NumberAxis();
_duplicateValueAxis(axis, result);
result.setTickUnit(axis.getTickUnit());
result.setForceZeroInRange(axis.isForceZeroInRange());
return result;
}
/**
* Duplicate a category axis.
* #param axis The source axis.
* #return A {#code CategoryAxis}, never {#code null}.
*/
public static CategoryAxis duplicateCategoryAxis(CategoryAxis axis) {
CategoryAxis result = new CategoryAxis(axis.getCategories());
_duplicateAxis(axis, result);
result.setStartMargin(axis.getStartMargin());
result.setEndMargin(axis.getEndMargin());
result.setGapStartAndEnd(axis.gapStartAndEndProperty().get());
return result;
}
#Override
public void start(Stage stage) {
final NumberAxis xAxisLC = new NumberAxis(1, 12, 1);
final NumberAxis yAxisLC = new NumberAxis(0.53000, 0.53910, 0.0005);
yAxisLC.setSide(Side.RIGHT);
yAxisLC.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxisLC) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart = new LineChart<>(xAxisLC, yAxisLC);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(true);
lineChart.setLegendVisible(false);
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.531035));
pane = new BorderPane();
pane.setCenter(lineChart);
mainGraphStackPane = new StackPane();
mainGraphStackPane.getChildren().add(pane);
Scene scene = new Scene(mainGraphStackPane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
Group root = new Group();
btnAdd = new Button();
btnAdd.setText("Add serie");
root.getChildren().add(btnAdd);
pane.getChildren().add(root);
btnAdd.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
NumberAxis xAxisBC = duplicateNumberAxis(xAxisLC);
NumberAxis yAxisBC = duplicateNumberAxis(yAxisLC);
ScatterChart<Number, Number> scatterChart = new ScatterChart<>(xAxisBC, yAxisBC);
scatterChart.setAlternativeRowFillVisible(false);
scatterChart.setAnimated(true);
scatterChart.setLegendVisible(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data(1, 0.53185));
series2.getData().add(new XYChart.Data(2, 0.532235));
series2.getData().add(new XYChart.Data(3, 0.53234));
series2.getData().add(new XYChart.Data(4, 0.538765));
series2.getData().add(new XYChart.Data(5, 0.53442));
series2.getData().add(new XYChart.Data(6, 0.534658));
series2.getData().add(new XYChart.Data(7, 0.53023));
series2.getData().add(new XYChart.Data(8, 0.53001));
series2.getData().add(new XYChart.Data(9, 0.53589));
series2.getData().add(new XYChart.Data(10, 0.53476));
series2.getData().add(new XYChart.Data(11, 0.530123));
series2.getData().add(new XYChart.Data(12, 0.531035));
scatterChart.getData().addAll(series2);
Set<Node> chartNode = scatterChart.lookupAll(".chart-plot-background");
for(final Node chr : chartNode){
chr.setStyle("-fx-background-color: transparent;");
}
chartNode = lineChart.lookupAll(".chart-plot-background");
for(final Node chr : chartNode){
chr.setStyle("-fx-background-color: transparent");
}
mainGraphStackPane.getChildren().add(scatterChart);
xAxisBC.lowerBoundProperty().bind(xAxisLC.lowerBoundProperty());
yAxisBC.lowerBoundProperty().bind(yAxisLC.lowerBoundProperty());
}
});
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
boolean XScaling=false;
boolean YScaling=false;
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 Tgap = xAxis.getWidth()/(xAxis.getUpperBound() - xAxis.getLowerBound());
double newXlower=xAxis.getLowerBound(), newXupper=xAxis.getUpperBound();
double newYlower=yAxis.getLowerBound(), newYupper=yAxis.getUpperBound();
double xAxisShift = xAxis.localToScene(0, 0).getX();
double yAxisShift = yAxis.localToScene(0, 0).getY();
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 && mouseEvent.getX()<xAxisShift+yAxis.getHeight() && mouseEvent.getY()<yAxisShift+yAxis.getHeight() && (XScaling==false || YScaling==false)){
//==================================================== X-Axis Moving ==================================
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 );
//===================================================== Y-Axis Moving ====================================
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);
}
//----------------------------- Re-Scale the X-Axis when dragging below it ---------------------------------
else if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getY()>yAxisShift+yAxis.getHeight() ){
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 );
}
//--------------------------------- Re-Scale the Y-Axis when dragging to the left of it --------------------------
else if(mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED && mouseEvent.getX()> (xAxisShift + xAxis.getWidth())){
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);
}
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()){
double XX=((mouseEvent.getX() - xAxisShift) / Tgap) + xAxis.getLowerBound();
double YY=CurrentPrice;
series1.setName(String.format("%.2g%n",XX) + ", " + String.format("%.4g%n",YY));
}
}
}
};
public static void main(String[] args) {
launch(args);
}
}
The
Chart, Axis, and Series
objects are not clone-able. For example I needed to include charts in two scenes. The only means to do so was to start with the original data and create every one of those elements twice.

JavaFX 2.x : Extend vertical line on sub pane(s)

This code plots a LineChart upper pane and a bar Chart in lower pane.
There is also a vertical line plotted left/right X move with pointer.
I would like to extend this vertical line also to the lower subpane, such as the attached picture
How to accomplish this?
Thanks.
Here is the code
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.*;
import javafx.scene.control.SplitPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineBuilder;
import javafx.stage.Stage;
public class XyChartInSplitOnlyCursor extends Application {
SplitPane splitPane1 = null;
BorderPane pane1;
BorderPane pane2;
Line LV;
XYChart.Series series1 = new XYChart.Series();
XYChart.Series series2 = new XYChart.Series();
#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);
xAxis.setScaleX(0);
yAxis.setAnimated(false);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart1 = new LineChart<Number, Number>(xAxis, yAxis);
lineChart1.setCreateSymbols(false);
lineChart1.setAlternativeRowFillVisible(false);
lineChart1.setAnimated(false);
lineChart1.setLegendVisible(false);
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.531035));
lineChart1.getData().addAll(series1);
pane1 = new BorderPane();
pane1.setCenter(lineChart1);
splitPane1 = new SplitPane();
splitPane1.setOrientation(Orientation.VERTICAL);
splitPane1.getItems().addAll(pane1);
splitPane1.setDividerPosition(0, 1);
final CategoryAxis xAxis2 = new CategoryAxis();
final NumberAxis yAxis2 = new NumberAxis();
yAxis2.setTickUnit(1);
yAxis2.setPrefWidth(35);
yAxis2.setMinorTickCount(10);
yAxis2.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis2){
#Override
public String toString(Number object){
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final BarChart<String, Number>BarChart = new BarChart<String, Number>(xAxis2, yAxis2);
BarChart.setAlternativeRowFillVisible(false);
BarChart.setLegendVisible(false);
BarChart.setAnimated(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data("Jan", 1));
series2.getData().add(new XYChart.Data("Feb", 3));
series2.getData().add(new XYChart.Data("Mar", 1.5));
series2.getData().add(new XYChart.Data("Apr", 3));
series2.getData().add(new XYChart.Data("May", 4.5));
series2.getData().add(new XYChart.Data("Jun", 5));
series2.getData().add(new XYChart.Data("Jul", 4));
series2.getData().add(new XYChart.Data("Aug", 8));
series2.getData().add(new XYChart.Data("Sep", 16.5));
series2.getData().add(new XYChart.Data("Oct", 13.9));
series2.getData().add(new XYChart.Data("Nov", 17));
series2.getData().add(new XYChart.Data("Dec", 20));
BarChart.getData().addAll(series2);
pane2 = new BorderPane();
pane2.setCenter(BarChart);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
splitPane1.getItems().addAll(pane2);
ObservableList<SplitPane.Divider> splitDiv = splitPane1.getDividers();
percSplit = 1/(double)(splitDiv.size()+1);
for (int i = 0; i< splitDiv.size(); i++) {
splitPane1.setDividerPosition(i, percSplit);
percSplit += 1/(double)(splitDiv.size()+1);
}
}
});
Scene scene = new Scene(splitPane1, 800, 600);
stage.setScene(scene);
pane1.setOnMouseMoved(mouseHandler);
LV=LineBuilder.create()
.startX(0)
.startY(0)
.endX(10)
.endY(.535)
.strokeWidth(1)
.stroke(Color.BLACK)
.build();
pane1.getChildren().add(LV);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED) {
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane1.getCenter();
NumberAxis yAxis = (NumberAxis) lineChart.getYAxis();
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
double newXlower=xAxis.getLowerBound(), newXupper=xAxis.getUpperBound();
double xAxisShift = getSceneShift(xAxis);
double yAxisShift = getSceneShift(yAxis);
if(mouseEvent.getX()>xAxisShift && mouseEvent.getX()<xAxisShift+xAxis.getWidth()){
LV.setStartX(mouseEvent.getX());
LV.setStartY(yAxisShift);
LV.setEndX(mouseEvent.getX());
LV.setEndY(yAxisShift+yAxis.getHeight());
}
}
}
};
private static double getSceneShift(Node node) {
double shift = 0;
do {
shift += node.getLayoutX();
node = node.getParent();
} while (node != null);
return shift;
}
public static void main(String[] args) {
launch(args);
}
}
Edit: added picture after jewelsea modify
Don't add the line to the pane in the SplitPane, instead create a Group which layers the line over the SplitPane.
Scene scene = new Scene(new Group(splitPane1, line), 800, 600);
If you want the Scene resizable, you can use a StackPane instead of a Group and bind the lines length to the scene height to get it to fill the Scene.
Here is a sample of applying using a StackPane and binding to extend the line in a resizable scene. Note, similarly to the originally posted code, the code is explicitly coded to only move the line when the mouse is inside the plot area of the top chart.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.control.SplitPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineBuilder;
import javafx.stage.Stage;
public class XyChartInSplitOnlyCursor extends Application {
SplitPane splitPane1 = null;
BorderPane pane1;
BorderPane pane2;
Line LV1, LV2;
XYChart.Series series1 = new XYChart.Series();
XYChart.Series series2 = new XYChart.Series();
#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);
xAxis.setScaleX(0);
yAxis.setAnimated(false);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart1 = new LineChart<Number, Number>(xAxis, yAxis);
lineChart1.setCreateSymbols(false);
lineChart1.setAlternativeRowFillVisible(false);
lineChart1.setAnimated(false);
lineChart1.setLegendVisible(false);
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.531035));
lineChart1.getData().addAll(series1);
pane1 = new BorderPane();
pane1.setCenter(lineChart1);
splitPane1 = new SplitPane();
splitPane1.setOrientation(Orientation.VERTICAL);
splitPane1.getItems().addAll(pane1);
splitPane1.setDividerPosition(0, 1);
final CategoryAxis xAxis2 = new CategoryAxis();
final NumberAxis yAxis2 = new NumberAxis();
yAxis2.setTickUnit(1);
yAxis2.setPrefWidth(35);
yAxis2.setMinorTickCount(10);
yAxis2.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis2) {
#Override
public String toString(Number object) {
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final BarChart<String, Number> BarChart = new BarChart<String, Number>(xAxis2, yAxis2);
BarChart.setAlternativeRowFillVisible(false);
BarChart.setLegendVisible(false);
BarChart.setAnimated(false);
XYChart.Series series2 = new XYChart.Series();
series2.getData().add(new XYChart.Data("Jan", 1));
series2.getData().add(new XYChart.Data("Feb", 3));
series2.getData().add(new XYChart.Data("Mar", 1.5));
series2.getData().add(new XYChart.Data("Apr", 3));
series2.getData().add(new XYChart.Data("May", 4.5));
series2.getData().add(new XYChart.Data("Jun", 5));
series2.getData().add(new XYChart.Data("Jul", 4));
series2.getData().add(new XYChart.Data("Aug", 8));
series2.getData().add(new XYChart.Data("Sep", 16.5));
series2.getData().add(new XYChart.Data("Oct", 13.9));
series2.getData().add(new XYChart.Data("Nov", 17));
series2.getData().add(new XYChart.Data("Dec", 20));
BarChart.getData().addAll(series2);
pane2 = new BorderPane();
pane2.setCenter(BarChart);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
splitPane1.getItems().addAll(pane2);
ObservableList<SplitPane.Divider> splitDiv = splitPane1.getDividers();
percSplit = 1 / (double) (splitDiv.size() + 1);
for (int i = 0; i < splitDiv.size(); i++) {
splitPane1.setDividerPosition(i, percSplit);
percSplit += 1 / (double) (splitDiv.size() + 1);
}
}
});
LV1 = LineBuilder.create()
.strokeWidth(2)
.stroke(Color.FORESTGREEN)
.build();
StackPane stack = new StackPane();
Pane glassPane = new Pane();
glassPane.getChildren().add(LV1);
glassPane.minWidthProperty().bind(splitPane1.widthProperty());
glassPane.minHeightProperty().bind(splitPane1.heightProperty());
glassPane.setMouseTransparent(true);
stack.getChildren().addAll(splitPane1, glassPane);
Scene scene = new Scene(stack, 800, 600);
LV1.endYProperty().bind(scene.heightProperty());
stage.setScene(scene);
pane1.setOnMouseMoved(mouseHandler);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
XYChart<Number, Number> chart1 = (XYChart<Number, Number>) pane1.getCenter();
plotLine(chart1, LV1, mouseEvent.getX() + 1);
}
};
private void plotLine(XYChart<Number, Number> chart, Line line, double x) {
Axis xAxis = chart.getXAxis(), yAxis = chart.getYAxis();
final double min = getSceneShift(xAxis);
final double max = min + xAxis.getWidth();
boolean setCrosshair = false;
if (x > min && x < min + xAxis.getWidth()) {
LV1.setStartX(x); LV1.setEndX(x);
setCrosshair = true;
} else if (x <= min){
LV1.setStartX(min); LV1.setEndX(min);
} else if (x >= max){
LV1.setStartX(max); LV1.setEndX(max);
}
if (setCrosshair) {
chart.setCursor(Cursor.CROSSHAIR);
} else {
chart.setCursor(Cursor.DEFAULT);
}
}
private static double getSceneShift(Node node) {
double shift = 0;
do {
shift += node.getLayoutX();
node = node.getParent();
} while (node != null);
return shift;
}
public static void main(String[] args) {
launch(args);
}
}

Resources