4

I "stole" some code from here to have an AreaChart with "smooth lines" which I use in my FXML and it works:

    <SmoothedAreaChart fx:id="chart" legendVisible="false"
        title="Tree depth by line" animated="false">
        <xAxis>
            <NumberAxis fx:id="xAxis" tickUnit="1.0" autoRanging="false"
                minorTickVisible="false" forceZeroInRange="false"
                label="Line number"/>
        </xAxis>
        <yAxis>
            <NumberAxis fx:id="yAxis" minorTickVisible="false"
                tickUnit="1.0" forceZeroInRange="true" label="Tree depth"/>
        </yAxis>
    </SmoothedAreaChart>

The problem I have however is with the Y axis.

I set the tickUnit to 1.0 because I want, well, integral units, but it doesn't quite work:

enter image description here

If, in the definition of yAxis, I set autoRanging to false then the graph is cropped (I set the upper bound by hand when I fill the graph):

enter image description here

And, well, there are too many tick marks as well.

Basically, I'd like the behavior of both:

  • that the autoranging takes place,
  • but that the tick marks always be integral numbers.

Is there a possibility to do this, or do I need to code my own/find an implementation of Axis which does that?

fge
  • 119,121
  • 33
  • 254
  • 329
  • Maybe set your own [Formatter](http://docs.oracle.com/javase/8/javafx/api/javafx/scene/chart/ValueAxis.html#tickLabelFormatterProperty) and just don't print out values that aren't integral. You'll have less labels and integral values. Other than that, I think you'll have to write your own. – brian Feb 08 '15 at 17:32
  • @brian well, I came up with a hack around it. And that, well, a hack. But it works... – fge Feb 08 '15 at 18:27

2 Answers2

1

OK, well, I found a solution, sort of.

I don't specify the tick unit in the FXML anymore; instead I do this in the code where I also set the maximum value of the y axis (and autoranging is set to false):

display.yAxis.setUpperBound(maxDepth);
final int tickUnit = maxDepth / 15;
display.yAxis.setTickUnit(Math.max(tickUnit, 1));

That's kind of a hack, of course, and I sure wish it were in the API to start with. Or maybe it is and I didn't see it...

fge
  • 119,121
  • 33
  • 254
  • 329
  • But you lose autoranging. The formatter is very simple. I'll add a little toy program. – brian Feb 08 '15 at 20:08
  • @brian I can set the maximum value by hand since I know it... Well, I'll try and see with your solution and see how it goes. – fge Feb 08 '15 at 20:13
  • One thing to consider is autoranging is very slow. If you know the max, min then you can use your solution. You may want to check the height before dividing by 15. That's why autoranging is slow, it calculates the size of every label and checks if it will fit. It does the same to find tick unit. – brian Feb 08 '15 at 20:17
0

Here you can see just a few lines to format the numbers however you want:

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.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class AxisFormatter extends Application {
    final NumberAxis xAxis = new NumberAxis();
    final NumberAxis yAxis = new NumberAxis();
    final LineChart<Number, Number> chart = new LineChart<>(xAxis, yAxis);
    double i = 0d;

    final StringConverter<Number> converter =  new StringConverter<Number>() {
        @Override public String toString(Number object) {
            //just an untested idea for isInteger
            return object.doubleValue() == object.intValue() 
                    ? object.intValue()+""
                    :"";
        }
        @Override public Number fromString(String string) { return 0;}
    };

    @Override public void start(Stage primaryStage) {
        
        chart.getData().add(new XYChart.Series<>());
        
        while (i++ < 10d)
            chart.getData().get(0).getData().add(new XYChart.Data<>(i,i/2));
        
        Button btn1 = new Button("increase max y value");
        btn1.setOnAction((evt)->{
            chart.getData().get(0).getData().add(new XYChart.Data<>(i++,i/2));
        });

        Button btn2 = new Button("change formatter");
        btn2.setOnAction((evt)->{
            yAxis.setTickLabelFormatter(yAxis.getTickLabelFormatter() == converter
                ? null : converter);
        });
        
        yAxis.setTickLabelFormatter(converter);
        
        VBox root = new VBox(chart, new HBox(5,btn1,btn2));
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
    
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
brian
  • 10,619
  • 4
  • 21
  • 79