I have a ScalaFX LineChart
whose XAxis
is time. The data is dynamic, being updated in an AnimationTimer
. As time progresses the plot moves to the right but the plot always starts at 0, so the visible domain increases, compressing the data points.
Here is the Stage
:
new Stage {
title = plotTitle
scene = new Scene(600, 600) {
val series = new XYChart.Series[Number, Number]()
series.setName(s"Simple")
val xAxis = NumberAxis("Time (s)")
val yAxis = NumberAxis("Amplitude")
val lineChart = new LineChart(xAxis, yAxis)
lineChart.setAnimated(false)
lineChart.getData.add(series)
lineChart.setCreateSymbols(false)
root = lineChart
var index = 0
val timer = AnimationTimer { time =>
series.getData.add(XYChart.Data[Number, Number](index, math.cos(index)))
if (series.getData.size > 100) series.getData.remove(0, series.getData.size() - 100)
xAxis.lowerBound.value = math.max(index - 100, 0) // this has no effect
xAxis.upperBound.value = math.max(index, 100) // this has no effect
index += 1
}
timer.start()
}
}
How do I update the lower/upper bounds of the XAxis
in the AnimationTimer
update? I tried setting the XAxis.lower/upperBound
values during the animation update but it has no effect. I have tried other things to no avail. There are JavaFX solutions I've seen but I can't translate it to ScalaFX because there is not a one-to-one mapping of types.
You can specify an explicit lower bound in a few ways, but all require (to the best of my knowledge) manually also setting an upper bound and tick unit, and disabling auto-ranging:
NumberAxis
constructor for the X-axis. Axes created in this way are automatically not auto-ranging.lowerBound
, upperBound
and tickUnit
properties of the X-axis NumberAxis
to fixed values. This also requires setting the X-axis's autoRanging = false
.DoubleProperty
instances to the lowerBound
, upperBound
and tickUnit
properties of the X-axis NumberAxis
. This allows the bounds and tick unit to be dynamically adjusted, by changing the value of the associated property. This also requires setting autoRanging = false
.Here's an example of the first option:
new Stage {
title = plotTitle
scene = new Scene(600, 600) {
val series = new XYChart.Series[Number, Number]()
series.setName(s"Simple")
// Specify an X-axis with fixed upper & lower bounds, and tick unit
// (separation of major tick values).
//
// Note: This is automatically not an auto-ranging axis.
val xAxis = NumberAxis("Time (s)", 115.0, 225.0, 25.0)
val yAxis = NumberAxis("Amplitude")
val lineChart = new LineChart(xAxis, yAxis)
lineChart.setAnimated(false)
lineChart.getData.add(series)
lineChart.setCreateSymbols(false)
root = lineChart
var index = 0
val timer = AnimationTimer { time =>
series.getData.add(XYChart.Data[Number, Number](index, math.cos(index)))
if (series.getData.size > 100) series.getData.remove(0, series.getData.size() - 100)
index += 1
}
timer.start()
}
}
In this example, I'm combining the second and third options to use properties for the lower & upper bounds, with a fixed tick unit.
new Stage {
title = plotTitle
scene = new Scene(600, 600) {
val series = new XYChart.Series[Number, Number]()
series.setName(s"Simple")
// Note: This axis is automatically auto-ranging.
val xAxis = NumberAxis("Time (s)")
val yAxis = NumberAxis("Amplitude")
// Disable auto-ranging of the xAxis.
xAxis.autoRanging = false
// Create a double property whose value will be used as the lower bound of the
// X-axis. If the value of this property is changed, the chart will update
// accordingly, automatically. Make this 0 initially.
val xLowBound = DoubleProperty(0.0)
// Bind this property to the lowerBound of the X-axis
xAxis.lowerBound <== xLowBound
// Repeat for the upper bound. 100, initially.
val xUpBound = DoubleProperty(100.0)
xAxis.upperBound <== xUpBound
// Set the tick unit to a fixed value.
xAxis.tickUnit = 25.0
val lineChart = new LineChart(xAxis, yAxis)
lineChart.setAnimated(false)
lineChart.getData.add(series)
lineChart.setCreateSymbols(false)
root = lineChart
var index = 0
val timer = AnimationTimer { time =>
series.getData.add(XYChart.Data[Number, Number](index, math.cos(index)))
if (series.getData.size > 100) series.getData.remove(0, series.getData.size() - 100)
// Dynamically update the lower & upper bounds.
xLowBound.value = math.max(index - 100, 0)
xUpBound.value = math.max(index, 100)
index += 1
}
timer.start()
}
}
Let me know if this does the trick for you.