Move tick label JavaFx 2 - java

Is it possible to move/shift the tick labels into the chart. Currently I see api's to hide/show tick labels is there an API that moves the tick labels inside the chart? If there isn't an API then is there a technique that I can use/apply to get this done?
Current code
public class Graph extends Application{
private NumberAxis xAxis;
private NumberAxis yAxis;
public static void main(final String[] args)
{
launch(args);
}
#Override
public void start(final Stage primaryStage) throws Exception
{
xAxis = new NumberAxis(0, 300, 20);
xAxis.setAutoRanging(false);
xAxis.setAnimated(false);
xAxis.setMinorTickVisible(false);
xAxis.setTickLabelsVisible(false);
xAxis.setTickMarkVisible(false);
yAxis = new NumberAxis(30, 240, 30);
yAxis.setAutoRanging(false);
yAxis.setAnimated(false);
yAxis.setTickMarkVisible(false);
yAxis.setMinorTickVisible(false);
yAxis.setMinorTickCount(3);
final LineChart<Number, Number> ctg = new LineChart<>(xAxis, yAxis);
ctg.setAnimated(false);
ctg.setCreateSymbols(false);
ctg.setAlternativeRowFillVisible(false);
ctg.setLegendVisible(false);
ctg.setHorizontalGridLinesVisible(true);
ctg.setVerticalGridLinesVisible(true);
Series<Number, Number> series = new LineChart.Series<>();
ctg.getData().add(series);
for (int i = 0; i < 300; i += 5) {
XYChart.Series minorYGrid = new XYChart.Series();
minorYGrid.getData().add(new XYChart.Data(i, 30));
minorYGrid.getData().add(new XYChart.Data(i, 240));
ctg.getData().add(minorYGrid);
}
for (int i = 40; i <= 240; i += 10) {
XYChart.Series minorXGrid = new XYChart.Series();
minorXGrid.getData().add(new XYChart.Data(0, i));
minorXGrid.getData().add(new XYChart.Data(500, i));
ctg.getData().add(minorXGrid);
}
final Scene scene = new Scene(ctg, 1600, 400);
scene.getStylesheets().add("resources/application.css");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Current result
Expected result

Translating a Single Axis
You can translate the y axis on the chart.
For example:
yAxis.translateXProperty().bind(
xAxis.widthProperty().divide(2)
);
To ensure the axis is displayed on top of the chart, you could set the depth buffer to true on the scene and set the z co-ordinate of the yAxis to -1.
yAxis.setTranslateZ(-1);
Translating Multiple Axes
Your "Expected result" actually has multiple vertical axes. Unfortunately there is no clone method to clone a node in JavaFX. So you will have to create a new axis and layer it on top of the chart. One way to accomplish that (which is a bit overkill and inefficient), is to create a completely new chart and layer it on top of the old one, similar to what is done in the solution to draw layers of XYCharts. The other way to do it, which is probably preferable but a bit trickier, would be just create another axis and stack it over the original chart.
Sample Code
MultiAxisChart.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class MultiAxisChart extends Application {
#Override
public void start(final Stage primaryStage) throws Exception {
final StackPane chartStack = createChartStack();
final Scene scene = new Scene(chartStack, 1600, 400, true);
primaryStage.setScene(scene);
primaryStage.show();
}
private StackPane createChartStack() {
LineChart<Number, Number> bottomChart = createChart();
LineChart<Number, Number> topChart = createChart();
bottomChart.getYAxis().translateXProperty().bind(
bottomChart.getXAxis().widthProperty().multiply(1.0/3)
);
topChart.getYAxis().translateXProperty().bind(
topChart.getXAxis().widthProperty().multiply(2.0/3)
);
bottomChart.getYAxis().setTranslateZ(-1);
topChart.getYAxis().setTranslateZ(-1);
topChart.getStylesheets().addAll(getClass().getResource(
"overlay-chart.css"
).toExternalForm());
return new StackPane(bottomChart, topChart);
}
private LineChart<Number, Number> createChart() {
NumberAxis xAxis = new NumberAxis(0, 300, 20);
xAxis.setAutoRanging(false);
xAxis.setAnimated(false);
xAxis.setMinorTickVisible(false);
xAxis.setTickLabelsVisible(false);
xAxis.setTickMarkVisible(false);
NumberAxis yAxis = new NumberAxis(30, 240, 30);
yAxis.setAutoRanging(false);
yAxis.setAnimated(false);
yAxis.setTickMarkVisible(false);
yAxis.setMinorTickVisible(false);
yAxis.setMinorTickCount(3);
final LineChart<Number, Number> ctg = new LineChart<>(xAxis, yAxis);
ctg.setAnimated(false);
ctg.setCreateSymbols(false);
ctg.setAlternativeRowFillVisible(false);
ctg.setLegendVisible(false);
ctg.setHorizontalGridLinesVisible(true);
ctg.setVerticalGridLinesVisible(true);
return ctg;
}
public static void main(final String[] args) {
launch(args);
}
}
overlay-chart.css
/** file: overlay-chart.css (place in same directory as MultiAxisChart) */
.chart-plot-background {
-fx-background-color: transparent;
}

Related

JavaFX LineChart not drawing circle?

This chart shows the problem:
I have JavaFX program that calculates data and draws a chart, but why points are not connected properly? I have tried many things, even creating two separate series, but it doesn't work.
public void createScatterChart(){
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
final SmoothedChart<Number,Number> smoothedChart = new SmoothedChart<>(xAxis, yAxis);
XYChart.Series series1 = new XYChart.Series();
XYChart.Series series2 = new XYChart.Series();
XYChart.Series series3 = new XYChart.Series();
for(int i = 0 ; i < this.r.size() ; i ++)
{
series1.getData().add(new XYChart.Data(this.r.get(i) * Math.cos(Math.toRadians(this.nodes.get(i))),this.r.get(i) * Math.sin(Math.toRadians(this.nodes.get(i)))));
//series2.getData().add(new XYChart.Data(this.r.get(i) * Math.cos(Math.toRadians(this.nodes.get(i) * this.xArray[i][0])),this.r.get(i) * Math.sin(Math.toRadians(this.nodes.get(i) * this.xArray[i][0]))));
}
smoothedChart.getData().add(series1);
smoothedChart.getData().add(series2);
Stage stage = new Stage();
Scene scene = new Scene(smoothedChart,800,600);
stage.setScene(scene);
stage.show();
}
A similar problem is examined here, in which the solution hinges on the data sort order. Looking at LineChart, SortingPolicy.NONE specifies "The data should be left in the order defined by the list in XYChart.dataProperty()."
I had to change chart from my SmoothChart to standard LineChart.
Depending on your approach to smoothing, you may encounter the kind of cubic spline artifacts examined here, which also occurs in jfreechart-fx. An approach using Bézier curves is adduced here.
As tested using synthetic data:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/72607616/230513
* #see https://stackoverflow.com/a/2510048/230513
*/
public class ChartTest extends Application {
private static final int N = 32;
#Override
public void start(Stage stage) {
var xAxis = new NumberAxis();
var yAxis = new NumberAxis();
var series = new XYChart.Series();
series.setName("Data");
for (int i = 0; i <= N; i++) {
var t = 2 * Math.PI * i / N;
var x = Math.cos(t);
var y = Math.sin(t);
series.getData().add(new XYChart.Data(x, y));
}
var chart = new LineChart<Number, Number>(xAxis, yAxis);
chart.getData().add(series);
ObservableList<LineChart.SortingPolicy> policies
= FXCollections.observableArrayList(LineChart.SortingPolicy.values());
var policy = new ChoiceBox<LineChart.SortingPolicy>(policies);
policy.setTooltip(new Tooltip("Choose a data sorting policy."));
policy.getSelectionModel().select(chart.getAxisSortingPolicy());
chart.axisSortingPolicyProperty().bind(policy.valueProperty());
Pane root = new StackPane(chart, policy);
StackPane.setAlignment(policy, Pos.TOP_RIGHT);
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

How to get coordinates of all dots in line JavaFX

I have JavaFX project and I need coordinates of each dot in the orange line from image bellow.
https://i.stack.imgur.com/A7zVa.png
I spent a lot of hours googling but I can not find a solution.
It is a simple application. I need those coordinates beacuse I need to make G CODE for CNC machine.
My Code is:
public class KS2 extends Application {
private LineChart<Number, Number> chart;
#Override
public void start(Stage primaryStage) throws Exception {
final NumberAxis xAxis = new NumberAxis(0.0, 150.0, 2);
final NumberAxis yAxis = new NumberAxis(0.0, 100.0, 2);
// Flip the axis
// yAxis.setScaleY(-1);
Rectangle r = new Rectangle(0, 0, 80, 50);
r.setFill(Color.TRANSPARENT);
r.setStrokeWidth(0.5);
r.setStroke(Color.BLACK);
this.chart = new LineChart<Number, Number>(xAxis, yAxis) {
#Override
public void layoutPlotChildren() {
super.layoutPlotChildren();
r.getTransforms().setAll(chartDisplayTransform(xAxis, yAxis));
// note nodes don't get removed from the plot children, and this method may be
// called often:
if (!getPlotChildren().contains(r)) {
getPlotChildren().add(r);
}
}
};
this.chart.setAnimated(false);
XYChart.Series series = new XYChart.Series();
series.getData().add(new XYChart.Data(54, 50));
series.getData().add(new XYChart.Data(80, 0));
//series.setName("My portfolio");
this.chart.getData().add(series);
VBox vbox = new VBox(this.chart);
Scene scene = new Scene(vbox, 400, 200);
primaryStage.setScene(scene);
primaryStage.setHeight(600);
primaryStage.setWidth(400);
primaryStage.show();
}
private Transform chartDisplayTransform(NumberAxis xAxis, NumberAxis yAxis) {
return new Affine(xAxis.getScale(), 0, xAxis.getDisplayPosition(0), 0, yAxis.getScale(),
yAxis.getDisplayPosition(0));
}
public static void main(String[] args) {
Application.launch(args);
}
}

JavaFX: Is there a way for Y axis to be positive upwards with origin at bottom-left?

The JavaFX coordinate system draws Y coords from the top of screen, and is positive downwards. I would like it to be positive upwards, and start from the bottom of screen.
There needs to be a translate, and the text nodes need to be flipped.
And with that, hopefully the drawn rectangle will be positioned the "natural" way we do it in math class. With its bottom-left at the origin, expanding to the top-right.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class FlippedExampleChart extends Application {
private LineChart<Number, Number> chart;
#Override
public void start(Stage primaryStage) throws Exception {
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
// Flip the axis
yAxis.setScaleY(-1);
// TODO How to translate to bottom of screen.
// TODO How to flip the text nodes.
this.chart = new LineChart<Number, Number>(xAxis, yAxis) {
#Override
public void layoutPlotChildren() {
super.layoutPlotChildren();
double height = yAxis.getDisplayPosition(100);
Rectangle r = new Rectangle(0, 0, 50, height);
r.setFill(Color.GREEN);
getPlotChildren().addAll(r);
}
};
this.chart.setAnimated(false);
VBox vbox = new VBox(this.chart);
Scene scene = new Scene(vbox, 400, 200);
primaryStage.setScene(scene);
primaryStage.setHeight(600);
primaryStage.setWidth(400);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
I'm assuming here the aim is to draw a shape using the coordinate system defined by the chart axes.
The easiest way is probably to transform the shape instead of the axis. You can create a utility method for this:
private Transform chartDisplayTransform(NumberAxis xAxis, NumberAxis yAxis) {
return new Affine(
xAxis.getScale(), 0, xAxis.getDisplayPosition(0),
0, yAxis.getScale(), yAxis.getDisplayPosition(0)
);
}
One other note about your code: the layoutPlotChildren() method doesn't necessarily remove nodes, so you may end up adding more rectangles than you expect with the code you posted.
Here's a version of your code that uses this method (and ensures the rectangle is only added once).
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;
public class FlippedExampleChart extends Application {
private LineChart<Number, Number> chart;
#Override
public void start(Stage primaryStage) throws Exception {
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
// Flip the axis
// yAxis.setScaleY(-1);
Rectangle r = new Rectangle(0, 0, 50, 100);
r.setFill(Color.GREEN);
this.chart = new LineChart<Number, Number>(xAxis, yAxis) {
#Override
public void layoutPlotChildren() {
super.layoutPlotChildren();
r.getTransforms().setAll(chartDisplayTransform(xAxis, yAxis));
// note nodes don't get removed from the plot children, and this method may be
// called often:
if (!getPlotChildren().contains(r)) {
getPlotChildren().add(r);
}
}
};
this.chart.setAnimated(false);
VBox vbox = new VBox(this.chart);
Scene scene = new Scene(vbox, 400, 200);
primaryStage.setScene(scene);
primaryStage.setHeight(600);
primaryStage.setWidth(400);
primaryStage.show();
}
private Transform chartDisplayTransform(NumberAxis xAxis, NumberAxis yAxis) {
return new Affine(xAxis.getScale(), 0, xAxis.getDisplayPosition(0), 0, yAxis.getScale(),
yAxis.getDisplayPosition(0));
}
public static void main(String[] args) {
Application.launch(args);
}
}
And the result:
If you have multiple nodes to treat this way, the strategy is to add them to a Group, and apply the transform to the Group:
#Override
public void start(Stage primaryStage) throws Exception {
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
Group extraNodes = new Group();
this.chart = new LineChart<Number, Number>(xAxis, yAxis) {
#Override
public void layoutPlotChildren() {
super.layoutPlotChildren();
Rectangle r1 = new Rectangle(0, 0, 50, 100);
r1.setFill(Color.GREEN);
Rectangle r2 = new Rectangle(70, 0, 30, 20);
r2.setFill(Color.AQUAMARINE);
extraNodes.getChildren().setAll(r1, r2);
extraNodes.getTransforms().setAll(chartDisplayTransform(xAxis, yAxis));
// note nodes don't get removed from the plot children, and this method may be
// called often:
if (!getPlotChildren().contains(extraNodes)) {
getPlotChildren().add(extraNodes);
}
}
};
this.chart.setAnimated(false);
VBox vbox = new VBox(this.chart);
Scene scene = new Scene(vbox, 400, 200);
primaryStage.setScene(scene);
primaryStage.setHeight(600);
primaryStage.setWidth(400);
primaryStage.show();
}
Also see this related question

Line chart symbols clipped by axis when they lie on the axis

I have a line chart with a defined axis range, and am plotting points as part of the line that lie on the axis. By default the points and the line segments that lie on the axis are clipped, as shown in the below screen shot;
My code is as below;
package com.jtech;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class ShowSymbol extends Application {
#Override
public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis(0,4,1);
final NumberAxis yAxis = new NumberAxis(0,10,2);
final LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis);
XYChart.Series series = new XYChart.Series();
final XYChart.Data d1 = new XYChart.Data(0.0, 0.0);
final XYChart.Data d2 = new XYChart.Data(0.5, 2.0);
final XYChart.Data d3 = new XYChart.Data(1.0, 2.5);
final XYChart.Data d4 = new XYChart.Data(1.5, 3.5);
final XYChart.Data d5 = new XYChart.Data(2.0, 10.0);
final XYChart.Data d6 = new XYChart.Data(2.5, 10.0);
final XYChart.Data d7 = new XYChart.Data(3.0, 0.0);
final XYChart.Data d8 = new XYChart.Data(3.5, 0.0);
final XYChart.Data d9 = new XYChart.Data(4.0, 8.0);
series.getData().addAll(d1,d2,d3,d4,d5,d6,d7,d8,d9);
lineChart.getData().add(series);
lineChart.setLegendVisible(false);
final Scene scene = new Scene(lineChart, 400, 300);
scene.getStylesheets().add(getClass().getResource("/css/show.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
where the style sheet contains;
.chart-series-line { -fx-stroke: grey; -fx-stroke-width: 4.0px; -fx-effect: null; }
.chart-line-symbol {
-fx-background-color: #860061, white;
-fx-background-insets: 0, 2;
-fx-background-radius: 0;
-fx-padding: 4px;
}
I would like to keep the axis range as specified (0->4 and 0->10), but show the complete segments of the symbols and lines that lie on them. Is this possible?
The problem you are facing is that the plot area is clipped. Code excerpt from XYChart.java:
private final Group plotArea = new Group(){
#Override public void requestLayout() {} // suppress layout requests
};
private final Group plotContent = new Group();
private final Rectangle plotAreaClip = new Rectangle();
public XYChart(Axis<X> xAxis, Axis<Y> yAxis) {
...
getChartChildren().addAll(plotBackground,plotArea,xAxis,yAxis);
...
plotArea.setClip(plotAreaClip);
...
}
A solution might be to simply disable the clip. But that most probably will give you other troubles (line overlapping, etc). Nontheless, since there is no proper API for this, here's a quick & dirty way about how you could disable the clipping:
Region chartContent = (Region) lineChart.lookup(".chart-content");
for( Node node: chartContent.getChildrenUnmodifiable()) {
if( node instanceof Group) {
Group plotArea= (Group) node;
plotArea.setClip(null);
}
}
It'll look like this then:
Bottom line: You're better off with implementing your own chart (e. g. use and change the source of LineChart and XYChart) that supports this feature. Or you file a change request for a proper solution.

JavaFx 2.x: How to draw minor grid lines

I'm unable to draw minor grid lines, it is possible to do so in JavaFx 2.2? In my case the grid and the graph moves so I cannot use a static image to generate the minor grid (live graph as shown in this example here).
Currently I see this
an example of what I'm looking for.
As suggested by #colti and a little bit of module logic I came up with this solution
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class Axis extends Application
{
public static void main(final String[] args)
{
launch(args);
}
#Override
public void start(final Stage stage) throws Exception
{
final NumberAxis x = new NumberAxis();
final NumberAxis y = new NumberAxis();
x.setAutoRanging(false);
y.setAutoRanging(false);
final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(x, y);
lineChart.setPrefSize(900, 900);
lineChart.setLegendVisible(false);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeColumnFillVisible(false);
lineChart.setAlternativeRowFillVisible(false);
final XYChart.Series seriesX = new XYChart.Series();
for (int i = 1; i < 100; i++) {
if ((i % 2) != 0) {
seriesX.getData().add(new XYChart.Data(i, 0));
seriesX.getData().add(new XYChart.Data(i, 100));
}
else {
seriesX.getData().add(new XYChart.Data(i, 100));
seriesX.getData().add(new XYChart.Data(i, 0));
}
}
final XYChart.Series seriesY = new XYChart.Series();
for (int i = 1; i < 100; i++) {
if ((i % 2) != 0) {
seriesY.getData().add(new XYChart.Data(0, i));
seriesY.getData().add(new XYChart.Data(100, i));
}
else {
seriesY.getData().add(new XYChart.Data(100, i));
seriesY.getData().add(new XYChart.Data(0, i));
}
}
lineChart.getData().add(seriesX);
lineChart.getData().add(seriesY);
lineChart.getStylesheets().add("site.css");
final Scene scene = new Scene(lineChart);
stage.setScene(scene);
stage.show();
}
}
CSS
.chart-series-line{
-fx-stroke-width: 1px;
-fx-stroke:#eedede;
-fx-effect: null;
}
.chart-vertical-grid-lines {
-fx-stroke: #cccccc;
}
.chart-horizontal-grid-lines {
-fx-stroke: #cccccc;
}

Categories

Resources