SciChart Android 3D Tutorial - Plotting Realtime Data
In Previous tutorials we've showed how to Create a Simple 3D Chart and add some Zoom and Rotate interaction as well as 3D Tooltips Inspection via the Chart Modifiers 3D API.
In this SciChart Android 3D tutorial we're going to go a little further and show how to update data displayed by 3D chart in realtime.
Getting Started
This tutorial is suitable for Java and Kotlin.
Assuming you have completed previous tutorial, we will now make some changes to update the data dynamically.
Updating Data Values
In our IDataSeries3D<TX,TY,TZ>, we have some static data so far. Let's update them in real-time now.
We are going to add a Timer and schedule updating the data on timer tick.
To update data in a 3D DataSeries, we have to call one of the available Update
methods on that DataSeries.
Since we are using XyzDataSeries3D<TX,TY,TZ>, we are going to use the updateXyzAt(int index, TX xValue, TY yValue, TZ zValue) method.
But first of all, we need to adjust some previously created code and save DataSeries instance and data used in private fields, which will affect initial setup of a DataSeries a bit:
private int pointsCount = 200;
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture schedule;
private SciChartSurface3D surface;
private final DoubleValues xValues = new DoubleValues();
private final DoubleValues yValues = new DoubleValues();
private final DoubleValues zValues = new DoubleValues();
private final XyzDataSeries3D dataSeries = new XyzDataSeries3D(Double.class, Double.class, Double.class);
for (int i = 0; i < pointsCount; i++) {
xValues.add(getGaussianRandomNumber(5.0, 1.5));
yValues.add(getGaussianRandomNumber(5.0, 1.5));
zValues.add(getGaussianRandomNumber(5.0, 1.5));
}
dataSeries.append(xValues, yValues, zValues);
private int pointsCount = 200;
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture schedule;
private SciChartSurface3D surface;
private final DoubleValues xValues = new DoubleValues();
private final DoubleValues yValues = new DoubleValues();
private final DoubleValues zValues = new DoubleValues();
private final XyzDataSeries3D dataSeries = new XyzDataSeries3D(Double.class, Double.class, Double.class);
for (int i = 0; i < pointsCount; i++) {
xValues.add(getGaussianRandomNumber(5.0, 1.5));
yValues.add(getGaussianRandomNumber(5.0, 1.5));
zValues.add(getGaussianRandomNumber(5.0, 1.5));
}
dataSeries.append(xValues, yValues, zValues);
private val pointsCount = 200
private val scheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
private lateinit var schedule: ScheduledFuture<*>
private lateinit var surface: SciChartSurface3D
private val xValues = DoubleValues()
private val yValues = DoubleValues()
private val zValues = DoubleValues()
private val dataSeries = XyzDataSeries3D(Double::class.javaObjectType, Double::class.javaObjectType, Double::class.javaObjectType)
for (i in 0 until pointsCount) {
xValues.add(getGaussianRandomNumber(5.0, 1.5))
yValues.add(getGaussianRandomNumber(5.0, 1.5))
zValues.add(getGaussianRandomNumber(5.0, 1.5))
}
dataSeries.append(xValues, yValues, zValues)
private const int PointsCount = 200;
private const long TimerInterval = 10;
private Timer _timer;
private SciChartSurface3D surface;
private readonly DoubleValues xValues = new DoubleValues();
private readonly DoubleValues yValues = new DoubleValues();
private readonly DoubleValues zValues = new DoubleValues();
private readonly XyzDataSeries3D<double, double, double> dataSeries = new XyzDataSeries3D<double, double, double>();
for (int i = 0; i < PointsCount; i++)
{
xValues.Add(GetGaussianRandomNumber(5.0, 1.5));
yValues.Add(GetGaussianRandomNumber(5.0, 1.5));
zValues.Add(GetGaussianRandomNumber(5.0, 1.5));
}
dataSeries.Append(xValues, yValues, zValues);
From here, we can initialize our Timer and create an updateData
selector, with real-time updates, like follows:
schedule = scheduledExecutorService.scheduleWithFixedDelay(updateData, 0, 10, TimeUnit.MILLISECONDS);
private Random random = new Random();
private final Runnable updateData = () -> {
for (int i = 0; i < pointsCount; i++) {
double xValue = xValues.get(i) + random.nextDouble() - 0.5;
double yValue = yValues.get(i) + random.nextDouble() - 0.5;
double zValue = zValues.get(i) + random.nextDouble() - 0.5;
xValues.set(i, xValue);
yValues.set(i, yValue);
zValues.set(i, zValue);
}
UpdateSuspender.using(surface, () -> {
dataSeries.updateRangeXyzAt(0, xValues, yValues, zValues);
});
};
schedule = scheduledExecutorService.scheduleWithFixedDelay(updateData, 0, 10, TimeUnit.MILLISECONDS);
private Random random = new Random();
private final Runnable updateData = () -> {
for (int i = 0; i < pointsCount; i++) {
double xValue = xValues.get(i) + random.nextDouble() - 0.5;
double yValue = yValues.get(i) + random.nextDouble() - 0.5;
double zValue = zValues.get(i) + random.nextDouble() - 0.5;
xValues.set(i, xValue);
yValues.set(i, yValue);
zValues.set(i, zValue);
}
UpdateSuspender.using(surface, () -> {
dataSeries.updateRangeXyzAt(0, xValues, yValues, zValues);
});
};
schedule = scheduledExecutorService.scheduleWithFixedDelay(updateData, 0, 10, TimeUnit.MILLISECONDS)
private val random = Random()
private val updateData = Runnable {
for (i in 0 until pointsCount) {
val xValue = xValues.get(i) + random.nextDouble() - 0.5
val yValue = yValues.get(i) + random.nextDouble() - 0.5
val zValue = zValues.get(i) + random.nextDouble() - 0.5
xValues.set(i, xValue)
yValues.set(i, yValue)
zValues.set(i, zValue)
}
UpdateSuspender.using(surface) {
dataSeries.updateRangeXyzAt(0, xValues, yValues, zValues)
}
}
_timer = new Timer(TimerInterval) { AutoReset = true };
_timer.Elapsed += OnTick;
_timer.Start();
private Random _random = new Random();
private void OnTick(object sender, ElapsedEventArgs e)
{
lock (_syncRoot)
{
for (int i = 0; i < PointsCount; i++)
{
var xValue = xValues.Get(i) + _random.NextDouble() - 0.5;
var yValue = yValues.Get(i) + _random.NextDouble() - 0.5;
var zValue = zValues.Get(i) + _random.NextDouble() - 0.5;
xValues.Set(i, xValue);
yValues.Set(i, yValue);
zValues.Set(i, zValue);
}
using (surface.SuspendUpdates())
{
dataSeries.UpdateRangeXyzAt(0, xValues, yValues, zValues);
}
}
}
Which will result in the following Chart:
Note
Despite the chart is now real-time, it's still fully interactive, you can use modifiers from previous tutorials with ease.
Where to Go From Here?
You can download the final project from our Java and Kotlin Tutorials 3D Repository.
Of course, this is not the limit of what you can achieve with the SciChart Android 3D.
Our documentation contains lots of useful information, some of the articles you might want to read are listed below:
Finally, start exploring. The SciChart Android library and functionality is quite extensive.
You can look into our SciChart Android Examples Suite which are full of 2D and 3D examples, which are also available on our GitHub