JavaFX 2.x : How to draw parallel line? - charts

This code below draw lines on an XY lineChart
import javafx.application.Application;
import javafx.event.EventHandler;
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 Lines 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(0.5, 9.5, 0.1);
yAxis.setTickUnit(1);
//yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
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.5));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 6.5));
series1.getData().add(new XYChart.Data("May", 4.5));
series1.getData().add(new XYChart.Data("Jun", 8.5));
series1.getData().add(new XYChart.Data("Jul", 6.5));
BorderPane bp = new BorderPane();
bp.setCenter(lineChart);
Scene scene = new Scene(bp, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
Lines.MouseHandler mh = new Lines.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;
public MouseHandler( Pane pane ) {
this.pane = pane;
}
#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 = null;
gotFirst = false;
}
}
else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
}
}
}
}
}
I would like to copy (clone) a line once drawn, such as this picture
So, first left mouse click on point 1, then second mouse click on point 2: my goal is to have now a copy of this line (same lenght, same slope) that I can place anywhere on the chart, in this example by a third left mouse click on point 3.
How to do this?
Thanks!

Related

JavaFx 2.x : How to modify a line once drawn?

This code plots a XY LineChart and (code thanks to Bhupendra) draw lines on chart by left mouse click and if mouse hover, line get selected turns to red and can be deleted or moved.
import javafx.application.Application;
import javafx.event.EventHandler;
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.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 );
}
}
}
}
}
What I would like to do now is to change line length or slope by selecting one of the end or tail points (A and B in the picture) and set new end x,y point
How to accomplish this?
Thanks.
Use the following class instead of Line ...
public class EditLine extends Parent {
private static final double radius = 5;
private Line line;
private Circle c1, c2;
private class MouseHandler implements EventHandler< MouseEvent > {
private boolean isfirst;
public MouseHandler( boolean first ) {
isfirst = first;
}
public void handle( MouseEvent event ) {
if( event.getEventType() == MouseEvent.MOUSE_ENTERED ) {
if( isfirst )
c1.setOpacity( 1 );
else c2.setOpacity( 1 );
} else if( event.getEventType() == MouseEvent.MOUSE_EXITED ) {
if( isfirst )
c1.setOpacity( 0 );
else c2.setOpacity( 0 );
} else if( event.getEventType() == MouseEvent.MOUSE_DRAGGED ) {
double x = event.getSceneX();
double y = event.getSceneY();
if( isfirst ) {
line.setStartX( x );
line.setStartY( y );
c1.setCenterX( x );
c1.setCenterY( y );
} else {
line.setEndX( x );
line.setEndY( y );
c2.setCenterX( x );
c2.setCenterY( y );
}
}
}
}
public EditLine( double x1, double y1, double x2, double y2 ) {
line = new Line( x1, y1, x2, y2 );
line.setMouseTransparent( true );
c1 = new Circle( x1, y1, radius, Color.RED );
c1.setOpacity( 0 );
MouseHandler mh1 = new MouseHandler( true );
c1.setOnMouseEntered( mh1 );
c1.setOnMouseExited( mh1 );
c1.setOnMouseDragged( mh1 );
c2 = new Circle( x2, y2, radius, Color.RED );
c2.setOpacity( 0 );
MouseHandler mh2 = new MouseHandler( false );
c2.setOnMouseEntered( mh2 );
c2.setOnMouseExited( mh2 );
c2.setOnMouseDragged( mh2 );
getChildren().addAll( c1, c2, line );
}
}

JavaFX 2.x : How to edit lines drawned on XY LineChart?

This code draw lines on a chart by left mouse click and move
import javafx.application.Application;
import javafx.event.EventHandler;
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 Lines 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.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);
Lines.MouseHandler mh = new Lines.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;
public MouseHandler( Pane pane ) {
this.pane = pane;
}
#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 = null;
gotFirst = false;
}
}
else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
}
}
}
}
}
My question is: how to edit such lines once plotted?
In example, once one or more line(s) are drawn I would like to select one of these, and by right mouse click and using a pop-up menu, delete it, modify length, (to make it shorter or longer) or change line slope.
Thanks all.
The following modifications will add functionality to move the line when dragged with left mouse button.
Also, the line will be removed when clicked with right mouse button.
This is the line handler callback class.
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();
}
}
Create the line handler in mouse handler class:
private LineHandler lineHandler;
public MouseHandler( Pane pane ) {
this.pane = pane;
lineHandler = new LineHandler(pane);
}
Add the handler to each line in the else clause of !gotFirst
} 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;
}
You can add the line remove functionality to a popup event.

JavaFx 2 draw arrows

This code plots a simple XYLine Chart
import javafx.application.Application;
import javafx.event.EventHandler;
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.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class XyChart extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("Line plot");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21,0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
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 pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I would like to draw arrows on the chart by left mouse click pressed and moved, such as this example
How to do this?
Thanks all.
You can add mouse handlers to the border pane and draw the arrow when the mouse moves.
There might be better ways, but this is what I came up with:
Add mouse handler to your border pane:
MouseHandler mh = new MouseHandler( pane );
pane.setOnMouseClicked( mh );
pane.setOnMouseMoved( mh );
The handler class could be as follows:
class MouseHandler implements EventHandler< MouseEvent > {
private boolean gotFirst = false;
private Line line, head1, head2;
private Pane pane;
private double x1, y1, x2, y2;
double phi = Math.toRadians( 40 );
double barb = 20;
public MouseHandler( Pane pane ) {
this.pane = pane;
}
#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 );
head1 = new Line( x2, y2, x2, y2 );
head2 = new Line( x2, y2, x2, y2 );
pane.getChildren().add( line );
pane.getChildren().add( head1 );
pane.getChildren().add( head2 );
gotFirst = true;
} else {
line = null;
gotFirst = false;
}
} else {
if( line != null ) {
x2 = event.getX();
y2 = event.getY();
// update line
line.setEndX( x2 );
line.setEndY( y2 );
// draw head
// http://www.coderanch.com/t/340443/GUI/java/Draw-arrow-head-end-line
double dx = x2 - x1;
double dy = y2 - y1;
double theta = Math.atan2( dy, dx );
double x, y, rho = theta + phi;
x = x2 - barb * Math.cos( rho );
y = y2 - barb * Math.sin( rho );
head1.setStartX( x2 );
head1.setStartY( y2 );
head1.setEndX( x );
head1.setEndY( y );
rho = theta - phi;
x = x2 - barb * Math.cos( rho );
y = y2 - barb * Math.sin( rho );
head2.setStartX( x2 );
head2.setStartY( y2 );
head2.setEndX( x );
head2.setEndY( y );
}
}
}
}

JavaFX 2.x : chart white borders removal

I my code below I plot two series in a SplitPane, but I have some white borders I would like to remove, so that the grids will fill all the chart space.
Here is a picture to show where I would like to remove
and this is my code
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.Chart;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.SplitPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class XyChartInSplitMove extends Application {
SplitPane splitPane1 = null;
BorderPane pane;
BorderPane pane2;
XYChart.Series series1 = new XYChart.Series();
XYChart.Series series2 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectinitY = new SimpleDoubleProperty();
#Override
public void start(Stage stage) {
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);
xAxis.setScaleX(0);
xAxis.setVisible(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);
pane = new BorderPane();
pane.setCenter(lineChart1);
splitPane1 = new SplitPane();
splitPane1.setOrientation(Orientation.VERTICAL);
splitPane1.getItems().addAll(pane);
splitPane1.setDividerPosition(0, 1);
Platform.runLater(new Runnable() {
#Override
public void run() {
double percSplit;
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);
}
}
});
//BarChart
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> barChart2 = new BarChart<String, Number>(xAxis2, yAxis2);
barChart2.setAlternativeRowFillVisible(false);
barChart2.setLegendVisible(false);
barChart2.setAnimated(false);
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));
barChart2.getData().addAll(series2);
pane2 = new BorderPane();
pane2.setCenter(barChart2);
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);
pane.setOnMouseClicked(mouseHandler);
pane.setOnMouseDragged(mouseHandler);
pane.setOnMouseEntered(mouseHandler);
pane.setOnMouseExited(mouseHandler);
pane.setOnMouseMoved(mouseHandler);
pane.setOnMouseReleased(mouseHandler);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
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 newXlower = xAxis.getLowerBound(), newXupper = xAxis.getUpperBound();
double newYlower = yAxis.getLowerBound(), newYupper = yAxis.getUpperBound();
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);
// 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);
}
rectinitX.set(mouseEvent.getX());
rectinitY.set(mouseEvent.getY());
BarChart<String, Number> barChart2 = (BarChart<String, Number>) pane2.getCenter();
double chartWidth = xAxis.getWidth();
double axisSpan = xAxis.getUpperBound() - xAxis.getLowerBound();
double displacement = (chartWidth / axisSpan) * (1 - newXlower);
for (Node node : barChart2.getChildrenUnmodifiable()) {
if (node.getClass().getEnclosingClass() == Chart.class) {
for (Node node2 : ((Parent) node).getChildrenUnmodifiable()) {
if ((node2 == barChart2.getXAxis())) {
node2.translateXProperty().set(displacement);
}
if (node2.getClass().getEnclosingClass() == XYChart.class) {
for (Node node3 : ((Parent) node2).getChildrenUnmodifiable()) {
if (node3.getClass() == Group.class) {
node3.translateXProperty().set(displacement);
}
}
}
}
}
}
}
}
};
public static void main(String[] args) {
launch(args);
}
}
To hide the upper X axis I have used xAxis.setScaleX(0); but I am not sure if this is the best way to hide X axis.
Any help really appreciated
Define a style.css:
.chart {
-fx-padding: 1px;
}
.chart-content {
-fx-padding: 0px;
}
Add this file to the stylesheets of app:
scene.getStylesheets().add(this.getClass().getResource("style.css").toExternalForm());
See the results.
Further. To hide the gap between top and bottom charts, you can move top chart to down like this, the gap appears on the top however:
...
...
stage.show();
// It should be after stage.show()
lineChart1.setTranslateY(xAxis.getHeight());
// Or give constant value if you want to put this code before the stage.show()
// lineChart1.setTranslateY(28);

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.