Aligning CategoryAxis and NumberAxis - javafx

I'm currently stacking two different charts (BarChart and LineChart) using JavaFX.
The LineChart has been divided into three areas to display where the points from the BarChart will fall into.
I am having a problem with my current code where the CategoryAxis from the BarChart and the NumberAxis from the LineChart is not aligning well on the xAxis.
This is what it currently looks like:
As you can see (click on the image to zoom in), some of the values along the xAxis does not align. I am unable to use the same NumberAxis for the LineChart and BarChart because BarChart only supports CategoryAxis. Is there any way to fix this so that the values along the xAxis aligns?
Code:
NumberAxis xAxis = new NumberAxis(0, 6, 1);
xAxis.setLabel("X");
NumberAxis yAxis = new NumberAxis(-5, 5, 0.5);
yAxis.setLabel("Y");
XYChart.Series<Number, Number> dataSeries1 = new XYChart.Series<>();
XYChart.Series<Number, Number> dataSeries2 = new XYChart.Series<>();
dataSeries1.setName("2SD");
dataSeries2.setName("3SD");
dataSeries1.getData().add(new XYChart.Data<>(0, 0.6));
dataSeries1.getData().add(new XYChart.Data<>(1, 0.9));
dataSeries1.getData().add(new XYChart.Data<>(2, 1.7));
dataSeries1.getData().add(new XYChart.Data<>(3, 1.8));
dataSeries1.getData().add(new XYChart.Data<>(4, 1.8));
dataSeries1.getData().add(new XYChart.Data<>(5, 2.1));
dataSeries1.getData().add(new XYChart.Data<>(6, 2.3));
dataSeries2.getData().add(new XYChart.Data<>(0, 1.0));
dataSeries2.getData().add(new XYChart.Data<>(1, 1.3));
dataSeries2.getData().add(new XYChart.Data<>(2, 2.4));
dataSeries2.getData().add(new XYChart.Data<>(3, 2.5));
dataSeries2.getData().add(new XYChart.Data<>(4, 2.7));
dataSeries2.getData().add(new XYChart.Data<>(5, 3.1));
dataSeries2.getData().add(new XYChart.Data<>(6, 3.3));
final LineChart<Number, Number> lineChart =
new LineChart<Number, Number>(xAxis, yAxis, FXCollections.observableArrayList(dataSeries2, dataSeries1)) {
#Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
XYChart.Series a = getData().get(0);
XYChart.Series b = getData().get(1);
ObservableList<XYChart.Data<Number, Number>> aData = a.getData();
ObservableList<XYChart.Data<Number, Number>> bData = b.getData();
for(int i = 0; i < aData.size()-1; i++){
// Color in the area between 2SD and 3SD
double x = getXAxis().getDisplayPosition(aData.get(i).getXValue());
double y = getYAxis().getDisplayPosition(bData.get(i).getYValue());
double x2 = getXAxis().getDisplayPosition(aData.get((i+1)).getXValue());
double y2 = getYAxis().getDisplayPosition(bData.get((i+1)).getYValue());
Polygon middlePolygon = new Polygon();
middlePolygon.getPoints().addAll(x,y,
x, getYAxis().getDisplayPosition(aData.get(i).getYValue()),
x2,getYAxis().getDisplayPosition(aData.get((i+1)).getYValue()),
x2,y2);
getPlotChildren().add(middlePolygon);
middlePolygon.toFront();
middlePolygon.setFill(Color.ORANGE);
// Color in the area above 3SD
double y3 = getXAxis().getDisplayPosition(0);
double y4 = getXAxis().getDisplayPosition(0);
Polygon topPolygon = new Polygon();
topPolygon.getPoints().addAll(x,y3,
x, getYAxis().getDisplayPosition(aData.get(i).getYValue()),
x2,getYAxis().getDisplayPosition(aData.get(i+1).getYValue()),
x2,y4);
getPlotChildren().add(topPolygon);
topPolygon.toFront();
topPolygon.setFill(Color.RED);
// Color in the area below 2SD
double xb = getXAxis().getDisplayPosition(bData.get(i).getXValue());
double yb = getYAxis().getDisplayPosition(-10);
double xb2 = getXAxis().getDisplayPosition(bData.get(i+1).getXValue());
double yb2 = getYAxis().getDisplayPosition(-10);
Polygon bottomPolygon = new Polygon();
bottomPolygon.getPoints().addAll(xb,yb,
xb, getYAxis().getDisplayPosition(bData.get(i).getYValue()),
xb2,getYAxis().getDisplayPosition(bData.get((i+1)).getYValue()),
xb2,yb2
);
getPlotChildren().add(bottomPolygon);
bottomPolygon.toFront();
bottomPolygon.setFill(Color.GREEN);
}
}
};
lineChart.setCreateSymbols(false);
CategoryAxis xAxis2 = new CategoryAxis();
xAxis2.setLabel("X");
BarChart<String, Number> barChart = new BarChart<>(xAxis2, yAxis);
barChart.setLegendVisible(false);
barChart.setAnimated(false);
//barChart.setAlternativeRowFillVisible(false);
//barChart.setAlternativeColumnFillVisible(false);
//barChart.setHorizontalGridLinesVisible(false);
//barChart.setVerticalGridLinesVisible(false);
barChart.getXAxis().setVisible(false);
barChart.getYAxis().setVisible(false);
XYChart.Series<String, Number> barSeries = new XYChart.Series<>();
barSeries.getData().add(new XYChart.Data<>("1", 2.4));
barSeries.getData().add(new XYChart.Data<>("2", 2.7));
barSeries.getData().add(new XYChart.Data<>("3", 0.8));
barSeries.getData().add(new XYChart.Data<>("4", -3.2));
barSeries.getData().add(new XYChart.Data<>("5", -0.1));
barChart.getData().addAll(barSeries);
barChart.getStylesheets().add("sample/BarChart.css");
barChart.setPadding(new Insets(0,0,35,3));
StackPane pane = new StackPane();
pane.getChildren().addAll(lineChart, barChart);

You can make your NumberAxis start at 0.5 and end at 5.5 then the tick marks will nearly overlap. Then you tell either the NumberAxis or the CategoryAxis not to show it's tick marks and it will be fitting.
If you don't want to change the range of the NumberAxis and you are ok with the numbers not being aligned to the values of the BarChart, you can simply tell the CategoryAxis not to show it's tick marks.
The function for that is setTickLabelsVisible(boolean) and probably you also should use setTickMarkVisible(boolean).
I didn't test this.

Related

JavaFX LineChart zooming is not precise

I'm trying to implement a zoom rectange for LineChart. Here is my code :
private void doZoom(Rectangle zoomRect, LineChart<Number, Number> chart) {
Point2D zoomTopLeft = new Point2D(zoomRect.getX(), zoomRect.getY());
Point2D zoomBottomRight = new Point2D(zoomRect.getX() + zoomRect.getWidth(), zoomRect.getY() + zoomRect.getHeight());
NumberAxis yAxis = (NumberAxis) chart.getYAxis();
NumberAxis xAxis = (NumberAxis) chart.getXAxis();
xAxis.setAutoRanging(false);
yAxis.setAutoRanging(false);
var xAxisMaxBoundValuePoint = xAxis.sceneToLocal(zoomBottomRight);
var xAxisMinBoundValuePoint = xAxis.sceneToLocal(zoomTopLeft);
var xAxisScaleFactor = xAxis.getScale();
xAxis.setLowerBound(xAxisMinBoundValuePoint.getX()/xAxisScaleFactor);
xAxis.setUpperBound(xAxisMaxBoundValuePoint.getX()/xAxisScaleFactor);
var yAxisMinBoundValuePoint = yAxis.sceneToLocal(zoomBottomRight);
var yAxisMaxBoundValuePoint = yAxis.sceneToLocal(zoomTopLeft);
var yAxisScaleFactor = yAxis.getScale();
yAxis.setLowerBound(yAxisMinBoundValuePoint.getY()/yAxisScaleFactor);
yAxis.setUpperBound(yAxisMaxBoundValuePoint.getY()/yAxisScaleFactor);
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}
I see that xAxis limits are fine - the match zoom Rectange but yAxis are not precise. Upper bound is a little bit less and Lower bound is a little bit lower than expected. What is so special about it?
PS
I tried this https://gist.github.com/james-d/7252698 as an example but there seems to be a bug in case axis are not always positive...
Thanks to James_d, I've developed the following solution :
private void doZoom(Rectangle zoomRect, LineChart<Number, Number> chart) {
Point2D zoomTopLeft = new Point2D(zoomRect.getX(), zoomRect.getY());
Point2D zoomBottomRight = new Point2D(zoomRect.getX() + zoomRect.getWidth(),
zoomRect.getY() + zoomRect.getHeight());
var zoomTopLeftScene = zoomRect.localToScene(zoomTopLeft.getX(),zoomTopLeft.getY());
var zoomBottomRightScene = zoomRect.localToScene(zoomBottomRight.getX(), zoomBottomRight.getY());
NumberAxis yAxis = (NumberAxis) chart.getYAxis();
NumberAxis xAxis = (NumberAxis) chart.getXAxis();
xAxis.setAutoRanging(false);
yAxis.setAutoRanging(false);
var minXDisplay = xAxis.sceneToLocal(zoomTopLeftScene);
var maxXDisplay = xAxis.sceneToLocal(zoomBottomRightScene);
var xLowerBound = xAxis.getValueForDisplay(minXDisplay.getX());
var xUpperBound = xAxis.getValueForDisplay(maxXDisplay.getX());
xAxis.setLowerBound( xLowerBound.doubleValue() );
xAxis.setUpperBound( xUpperBound.doubleValue() );
var maxYDisplay = yAxis.sceneToLocal(zoomTopLeftScene);
var minYDisplay = yAxis.sceneToLocal(zoomBottomRightScene);
var yLowerBound = yAxis.getValueForDisplay(minYDisplay.getY());
var yUpperBound = yAxis.getValueForDisplay(maxYDisplay.getY());
yAxis.setLowerBound( yLowerBound.doubleValue() );
yAxis.setUpperBound( yUpperBound.doubleValue() );
zoomRect.setWidth(0);
zoomRect.setHeight(0);
}

Determine pixel position from a coordinate in a lineChart

I have a problem to get the pixel coordinate with the method "getDisplayPosition", each time it returns 0.0.
Here is my code :
public void start(Stage primaryStage) throws Exception
{
final NumberAxis xAxis = new NumberAxis(0.0, 200.0, 5.0);
final NumberAxis yAxis = new NumberAxis(0.0, 20000.0, 1000.0);
xAxis.setLabel("Disante (Nm)");
yAxis.setLabel("Altitude (ft)");
AreaChart<Number, Number> areaChart = new AreaChart<>(xAxis, yAxis);
Series<Number, Number> area = new XYChart.Series<>();
area.getData().add(new XYChart.Data<>(60, 4000));
area.getData().add(new XYChart.Data<>(65, 4500));
area.getData().add(new XYChart.Data<>(70, 3800));
area.getData().add(new XYChart.Data<>(75, 1000));
area.getData().add(new XYChart.Data<>(80, 1500));
areaChart.getData().add(area);
areaChart.setLegendVisible(false);
areaChart.setCreateSymbols(false);
LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis);
Series<Number, Number> line = new XYChart.Series<>();
line.getData().add(new XYChart.Data<>(0, 0));
line.getData().add(new XYChart.Data<>(15, 6000));
line.getData().add(new XYChart.Data<>(145, 6000));
line.getData().add(new XYChart.Data<>(155, 9000));
line.getData().add(new XYChart.Data<>(200, 9000));
lineChart.getData().add(line);
lineChart.setLegendVisible(false);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeColumnFillVisible(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setHorizontalGridLinesVisible(false);
lineChart.setVerticalGridLinesVisible(false);
lineChart.getXAxis().setVisible(false);
lineChart.getYAxis().setVisible(false);
lineChart.getStylesheets().add(this.getClass().getResource("/resources/chart/chart.css").toExternalForm());
polygon = new Polygon();
polygon.getPoints().addAll(new Double[] {
100.0, 3000.0,
100.0, 19000.0,
150.0, 19000.0,
150.0, 3000.0,
140.0, 5000.0,
130.0, 4500.0,
120.0, 5500.0,
110.0, 3500.0
});
// polygon.setFill(new ImagePattern(createHatch(Color.BLUE)));
Polygon polygonInChart = updateShape(xAxis, yAxis);
StackPane root = new StackPane();
root.getChildren().addAll(areaChart, polygonInChart, lineChart);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private Polygon updateShape(NumberAxis xAxis, NumberAxis yAxis)
{
List<Double> points = polygon.getPoints();
Polygon polygonInChart = new Polygon();
for (int i = 0; i < points.size(); i += 2)
{
double displayX = xAxis.getDisplayPosition(points.get(i).doubleValue());
double displayY = yAxis.getDisplayPosition(points.get(i + 1).doubleValue());
polygonInChart.getPoints().addAll(displayX, displayY);
}
return polygonInChart;
}
I want to add a polygon in the chart, so I try to find the pixel position in the screen of the polygon's coordinate and add them to the stackPane in order to have the polygon "in" the chart.

LineChart is not Auto Resize when the dataset is plot in JavaFX

I am using LineChart in JavaFX. I am using Arrays of same length as inputs to plot.
But when i include this in my application the plot is not autosize.
I have included the snapshot how it looks.
I would like to set the line width smaller and also to change the color.
Here is the code to plot this graph
public class ChartPlot extends Application {
static LineChart<Number, Number> linechart;
static double[] xArray, yArray;
public static LineChart linePlot(double[] x, double[] y) {
xArray = new double[x.length];
yArray = new double[y.length];
xArray = x;
yArray = y;
// Defining the x axis
final NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Wavelength");
// Defining the y axis
final NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Intensity");
// Creating the line chart
linechart = new LineChart<Number, Number>(xAxis, yAxis);
// Prepare XYChart.Series objects by setting data
XYChart.Series series = new XYChart.Series();
// series.setName("No of schools in an year");
// Setting the data to Line chart
for (int i = 0; i < xArray.length; i++) {
series.getData().add(new XYChart.Data(xArray[i], yArray[i]));
}
linechart.setCreateSymbols(false);
linechart.getData().add(series);
return linechart;
}
}
Please help me to resolve this .
Thanks in advance
NumerAxis need to be added with autoRange. Here is the code
public static LineChart linePlot(double[] x,double[] y)
{
xArray=new double[x.length];
yArray=new double[y.length];
xArray=x;
yArray=y;
//Defining the x axis
final NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Wavelength");
//Defining the y axis
final NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Intensity");
//Creating the line chart
linechart= new LineChart<Number,Number>(xAxis,yAxis);
linechart.getData().clear();
//Prepare XYChart.Series objects by setting data
XYChart.Series series = new XYChart.Series();
//series.setName("No of schools in an year");
//Setting the data to Line chart
for(int i = 0; i<xArray.length; i++)
{
series.getData().add(new XYChart.Data(xArray[i], yArray[i]));
}
linechart.setCreateSymbols(false);
linechart.getData().add(series);
//This is what I have Changed
//---
xAxis.setAutoRanging(true);
xAxis.setForceZeroInRange(false);
yAxis.setAutoRanging(true);
yAxis.setForceZeroInRange(false);
//---
linechart.autosize();
linechart.applyCss();
return linechart;
}
}

How to connect two points in javaFX in two-axis chart

In JavaFX line chart, a type of two-axis chart that presents data as a series of points connected by straight lines. All points are connected with one line. But I want to connect them with my order . Like there are three points p1(x1,y1), p2(x2,y2), p3(x3,y3) . In line chart, they are all connected with a single line , but I want connect p1,p3 and p1,p2 separately . How can I do that ? X-axis and Y-axis must present there .
By default JavaFX will join all points in a series. Simply solution is to use multiple series. Note you cannot share Data instances between series because Data stores a back-reference to the Node - see javafx.scene.chart.XYChart.Data.getNode()
#Override
public void start(Stage primaryStage) throws Exception {
NumberAxis xAxis1 = new NumberAxis();
NumberAxis yAxis1 = new NumberAxis();
LineChart<Number, Number> chart = new LineChart<Number, Number>(xAxis1, yAxis1);
double p1x = 0;
double p1y = 0;
double p2x = 5;
double p2y = 5;
double p3x = 3;
double p3y = 5;
Series<Number, Number> series1 = new Series<>();
series1.getData().add(new Data<>(p1x, p1y));
series1.getData().add(new Data<>(p2x, p2y));
chart.getData().add(series1);
Series<Number, Number> series2 = new Series<>();
series2.getData().add(new Data<>(p2x, p2y));
series2.getData().add(new Data<>(p3x, p3y));
chart.getData().add(series2);
primaryStage.setScene(new Scene(chart));
primaryStage.show();
}
I improved on #Adam answer to address your new probem:
#Override
public void start(Stage primaryStage) throws Exception {
NumberAxis xAxis1 = new NumberAxis();
NumberAxis yAxis1 = new NumberAxis();
LineChart<Number, Number> chart = new LineChart<Number, Number>(xAxis1, yAxis1);
Random random = new Random();
ArrayList<Series<Number, Number>> seriesContainer = new ArrayList();
for(int i = 0; i < 10; i++)
{
Series<Number, Number> series1 = new Series<>();
series1.getData().add(new XYChart.Data<>(random.nextDouble() * 100, random.nextDouble() * 100));
series1.getData().add(new XYChart.Data<>(random.nextDouble() * 100, random.nextDouble() * 100));
seriesContainer.add(series1);
}
chart.getData().addAll(seriesContainer);
primaryStage.setScene(new Scene(chart));
primaryStage.show();
}

Set Bar Chart column width size

Is there a way to set JavaFX Bar Chart column width size?
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class BarChartSample extends Application {
final static String austria = "Austria";
final static String brazil = "Brazil";
final static String france = "France";
final static String italy = "Italy";
final static String usa = "USA";
#Override public void start(Stage stage) {
stage.setTitle("Bar Chart Sample");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final BarChart<String,Number> bc =
new BarChart<String,Number>(xAxis,yAxis);
bc.setTitle("Country Summary");
xAxis.setLabel("Country");
yAxis.setLabel("Value");
XYChart.Series series1 = new XYChart.Series();
series1.setName("2003");
series1.getData().add(new XYChart.Data(austria, 25601.34));
series1.getData().add(new XYChart.Data(brazil, 20148.82));
series1.getData().add(new XYChart.Data(france, 10000));
series1.getData().add(new XYChart.Data(italy, 35407.15));
series1.getData().add(new XYChart.Data(usa, 12000));
XYChart.Series series2 = new XYChart.Series();
series2.setName("2004");
series2.getData().add(new XYChart.Data(austria, 57401.85));
series2.getData().add(new XYChart.Data(brazil, 41941.19));
series2.getData().add(new XYChart.Data(france, 45263.37));
series2.getData().add(new XYChart.Data(italy, 117320.16));
series2.getData().add(new XYChart.Data(usa, 14845.27));
XYChart.Series series3 = new XYChart.Series();
series3.setName("2005");
series3.getData().add(new XYChart.Data(austria, 45000.65));
series3.getData().add(new XYChart.Data(brazil, 44835.76));
series3.getData().add(new XYChart.Data(france, 18722.18));
series3.getData().add(new XYChart.Data(italy, 17557.31));
series3.getData().add(new XYChart.Data(usa, 92633.68));
Scene scene = new Scene(bc,800,600);
bc.getData().addAll(series1, series2, series3);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
For example there is a way to setBarGap size but I can't find a way to set the maximum width of a Bar Chart column.
Is there a way to solve this?
BarChart works on the basis that both barGap (gap between bars in the same category) and categoryGap (gap between bars in separate categories) can be set by the user, so for a given size of chart, the width of the bars is calculated internally every time a layout is requested.
To set a maximum width on every bar, we have to change either barGap or categoryGap accordingly. But we can do it programmatically, to respond to any change in the width of the chart.
Let's first calculate the bar width for an initial layout. According to BarChart.layoutPlotChildren():
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getBarGap());
double barWidth = (avilableBarSpace / bc.getData().size()) - bc.getBarGap();
In the example provided by the OP, given the default gap values (barGap=4, categoryGap=10), the resulting barWidth is 37.4.
Let's assume we want to set a limit of 40, and a minimum category gap of 10:
double maxBarWidth=40;
double minCategoryGap=10;
If the chart's width increases when the scene is resized, we could limit barWidth by increasing categoryGap:
double barWidth=0;
do{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getBarGap());
barWidth = (avilableBarSpace / bc.getData().size()) - bc.getBarGap();
if(barWidth > maxBarWidth){
avilableBarSpace=(maxBarWidth + bc.getBarGap())* bc.getData().size();
bc.setCategoryGap(catSpace-avilableBarSpace-bc.getBarGap());
}
} while(barWidth>maxBarWidth);
Note the do-while loop: as we modify categoryGap a new layout is performed and some initial values change. It takes usually two iterations to get the desired result.
We can repeat this operation as long as the chart's width keeps growing.
But if the chart's width starts decreasing, the gap between categories should be decreased too, while the bar width stays close to its maximum:
double barWidth=0;
do{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (minCategoryGap + bc.getBarGap());
barWidth = Math.min(maxBarWidth, (avilableBarSpace / bc.getData().size()) - bc.getBarGap());
avilableBarSpace=(barWidth + bc.getBarGap())* bc.getData().size();
bc.setCategoryGap(catSpace-avilableBarSpace-bc.getBarGap());
} while(barWidth < maxBarWidth && bc.getCategoryGap()>minCategoryGap);
Putting everything together, and listening to scene width changes:
...
Scene scene = new Scene(bc,800,600);
bc.getData().addAll(series1, series2, series3);
stage.setScene(scene);
stage.show();
double maxBarWidth=40;
double minCategoryGap=10;
scene.widthProperty().addListener((obs,n,n1)->{
if(bc.getData().size()==0) return;
if(n!=null && (n1.doubleValue()>n.doubleValue())){
double barWidth=0;
do{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getBarGap());
barWidth = (avilableBarSpace / bc.getData().size()) - bc.getBarGap();
if (barWidth >maxBarWidth){
avilableBarSpace=(maxBarWidth + bc.getBarGap())* bc.getData().size();
bc.setCategoryGap(catSpace-avilableBarSpace-bc.getBarGap());
}
} while(barWidth>maxBarWidth);
}
if(n!=null && (n1.doubleValue()<n.doubleValue()) && bc.getCategoryGap()>minCategoryGap){
double barWidth=0;
do{
double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (minCategoryGap + bc.getBarGap());
barWidth = Math.min(maxBarWidth, (avilableBarSpace / bc.getData().size()) - bc.getBarGap());
avilableBarSpace=(barWidth + bc.getBarGap())* bc.getData().size();
bc.setCategoryGap(catSpace-avilableBarSpace-bc.getBarGap());
} while(barWidth < maxBarWidth && bc.getCategoryGap()>minCategoryGap);
}
});
Note that in the rest of the cases, barWidth will be calculated internally.
Further to José Pereda's answer. You can set the width of the chart according to your data set. Together with the BarGap and CategoryGap setters, you can fine tune the width of the bars through calculation or trial and error. For instance, I did the following to get thinner bars. I set the width of an empty graph to 30 (for the axis and label??) and then added 100px to max and min width for every round in the game of cribbage (up to a maximum of the gridpane width) thus restricting the free space in the graph for the bars to fill.
Pane barchart1 = new Pane;
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis(0,25,1);
BarChart<String, Number> bc1 =
new BarChart<String, Number>(xAxis, yAxis);
// create Player chart
bc1.setTitle(G.getPlayerName(0));
bc1.setTitle(null);
xAxis1.setLabel("Round");
yAxis1.setLabel("Points");
XYChart.Series series1 = new XYChart.Series();
series1.setName("Pegging");
XYChart.Series series3 = new XYChart.Series();
series3.setName("Hand");
XYChart.Series series5 = new XYChart.Series();
series5.setName("Box");
for (int i = 0; i < G.getRoundsSize(); i++) {
series1.getData().add(new XYChart.Data(Integer.toString(i + 1), G.getRound(i).getPlayerRoundData(0).getPegPoints()));
series3.getData().add(new XYChart.Data(Integer.toString(i + 1), G.getRound(i).getPlayerRoundData(0).getHandPoints()));
series5.getData().add(new XYChart.Data(Integer.toString(i + 1), G.getRound(i).getPlayerRoundData(0).getBoxPoints()));
}
bc1.getData(). addAll(series1, series3, series5);
bc1.setBarGap(0);
bc1.setCategoryGap(1.0);
bc1.setMaxHeight(180);
bc1.setMinHeight(180);
if(G.getRoundsSize() < 8){
int wid = ((G.getRoundsSize() * 100) + 30);
bc1.setMaxWidth(wid);
bc1.setMinWidth(wid);
} else {
bc1.setMaxWidth(830);
bc1.setMinWidth(830);
}
barchart1.getChildren().add(bc1);
Unfortunately you cannot simply set the bar width in the Javafx BarChart. You can only set the distance between the categories CategoryGap and the bars of a category BarGap.
Since the number of categories (categoryAxis.getCategories().size()), bars per category (barChart.getData().size()) and the available space for the axis (categoryAxis.getLayoutBounds().getWidth()) are known, the CategoryGap and BarGap can be simply calculated.
Important: This needs to be recalculated, if the chart is resized.
public static void setBarWidth(BarChart<?, ?> barChart, CategoryAxis categoryAxis,
double barWidth, double availableSpace) {
int dataSeriesCount = barChart.getData().size();
int categoriesCount = categoryAxis.getCategories().size();
if (dataSeriesCount <= 1) {
barChart.setBarGap(0d);
} else {
barChart.setBarGap(5d);
}
double barWidthSum = barWidth * (categoriesCount * dataSeriesCount);
double barGapSum = barChart.getBarGap() * (dataSeriesCount - 1);
double categorySpacing = (availableSpace - barWidthSum - barGapSum) / categoriesCount;
barChart.setCategoryGap(categorySpacing);
}

Resources