Friday, November 10, 2017

Friday Fun L - Smooth Charts

Aloha,

If you ever worked with JavaFX charts you might know the need for smoothed line- and areacharts. 
If you search the web you will probably stumble upon an implementation over at fxexperience.com. I've used this version from Jasper for some time and it worked for me in 80% of all cases.
But sometimes you need more features or dynamic behavior and for TilesFX I've created a new version of such a chart which comes with the following features

  • line or area chart
  • smoothed or not smoothed
  • supports mouse interaction (click on the chart and get the y-value)
  • supports snap to ticks (click on the chart and the next point will be selected)
  • convenience methods
    • set visibility of symbols 
    • set series fill
    • set series stroke
    • set symbol background
    • set legend background
    • set legend text fill
    • set legend symbol background
    • set symbol size
    • get symbols (List<StackPane>)
    • get fillPath
    • get strokePath
    • get chart plot background
    • get horizontal/vertical gridlines
    • set chart plot background fill/background
    • set x-axis and y-axis tick label fill
    • set x-axis and y-axis tick mark fill
    • set x-axis and y-axis border fill

I'm not sure if these features will be useful for all of you but even if not I thought it might be worth sharing it with you. In principle most of those features I could have set using CSS but if I need to set it via code I always have to check the css styles etc. So for this reason I've added all those convenience methods :)

To smooth the chart I use a so called Catmull-Rom spline which has the advantage that it is tightly follows the control points of the chart.

The chart also comes with a property to change the number of points between the control points. Means if you just need a rough smoothing you could try a value of 2 or if you need a fine smoothing you might want to try 32 (the limit is 64 in my implementation). The default value is 16 and this gives nice results, so usually you don't need to change it. This factor depends on the size of the chart. For smaller charts even 8 gives nice results.

So here is a screenshot of the all four chart variants...





The code for this charts can be found in the Demo.java file that comes with the chart. Because in TilesFX I needed a smoothed chart which I can tweak from code without using CSS I've added a couple of convenience methods. If you use this chart as a replacement for a standard JavaFX AreaChart all the CSS stuff should also work. But if you (like me) need to tweak some things dynamically the convenience methods come in quite handy.

Here is a little video that show the mouse interactivity in action...



If the interactive property is set to true the the chart will fire a JavaFX event of type SmoothedChartEvent.DATA_SELECTED
The event contains a double property that contains the y value of the selected point. To get those events you have to add an EventHandler to the chart object as follows...


SmoothedChart<String, Number> areaChartSmoothed = new SmoothedChart<>(xAxis4, yAxis4);
areaChartSmoothed.getData().addAll(series4);
areaChartSmoothed.setSmoothed(true);
areaChartSmoothed.setChartType(ChartType.AREA);
areaChartSmoothed.setInteractive(true);
areaChartSmoothed.setSubDivisions(8);
areaChartSmoothed.setSnapToTicks(false);
areaChartSmoothed.setLegendVisible(false);

areaChartSmoothed.addEventHandler(SmoothedChartEvent.DATA_SELECTED, 
                                  e -> System.out.println(e.getValue()));

The tooltip shows different information dependent on the mode
  • snapToTicks = true  -> The name and the value of the selected Data object
  • snapToTicks = false -> The y-value of the selected point
It might be possible that you need more or different functionality so...feel free to fork the code on github as always :)

Just be aware that this class won't work in Java 9 in the current state because it makes use of the following internal api

  • com.sun.javafx.charts.Legend
  • com.sun.javafx.charts.Legend.LegendItem

These classes will be used in the following methods:

setLegendBackground() 
setLegendTextFill() 
setLegendSymbolFill()

So you could either remove those methods and it will run on JDK 9 or you have to fiddle around with some JVM command line parameters like

--add-exports javafx.controls/com.sun.javafx.charts=ALL_UNNAMED

To be honest I did not try that yet but it might work, otherwise just let me know and I will correct this blogpost :)

Well that's it for today...so keep coding... :)

No comments:

Post a Comment