To enable series coloring with MetadataProvider, you need to create a class which conforms to one of the following protocol (or possibly to all of them):
allows provide different colors for selected points.
NOTE: The MetadataProvider API is very similar to the PaletteProvider API from SciChart 2D.
A choice depends on a RenderableSeries 3D type, which MetadataProvider 3D is designed for.
Each MetadataProvider protocol declares method(s), which provides a way to update series colors for every data points.
Mentioned methods are called every time RenderableSeries 3D requires a redraw, so it expects that the colors array should be updated there correspondingly.
For the convenience, there is the SCIMetadataProvider3DBase class, which provides some basic implementation, so it’s recommended to inherit from it while implementing custom MetadataProvider. There is also some predefined MetadataProviders listed below:
class CustomMetadataProvider: SCIMetadataProvider3DBase<SCIPointLineRenderableSeries3D>, ISCIPointMetadataProvider3D, ISCIStrokeMetadataProvider3D {
init() {
super.init(seriesType: SCIPointLineRenderableSeries3D.self)
}
func updateStrokeColors(_ strokeColors: SCIUnsignedIntegerValues!, defaultStroke: UInt32) {
updateColors(colors: strokeColors)
}
func updatePointMetadata(withColors colors: SCIUnsignedIntegerValues, pointScales scales: SCIFloatValues, defaultColor: UInt32, andDefaultScale defaultScale: Float) {
updateColors(colors: colors)
let pointsCount = self.renderableSeries.currentRenderPassData.pointsCount
scales.count = pointsCount
for i in 0 ..< pointsCount {
scales.set(1, at: i)
}
}
fileprivate func updateColors(colors: SCIUnsignedIntegerValues) {
let rSeries = self.renderableSeries!
let renderPassData = rSeries.currentRenderPassData as! SCIXyzRenderPassData3D
let upperThreshold = Double(rSeries.parentSurface.camera.orbitalYaw.truncatingRemainder(dividingBy: 90))
let lowerThreshold = Double(rSeries.parentSurface.camera.orbitalPitch.truncatingRemainder(dividingBy: 90))
let pointsCount = renderPassData.pointsCount
colors.count = pointsCount;
let yValues = renderPassData.yValues!
for i in 0 ..< pointsCount {
let value = yValues.getValueAt(i)
if (value > upperThreshold) {
colors.set(0xffff0000, at: i)
} else if (value < lowerThreshold) {
colors.set(0xff00ff00, at: i)
} else {
colors.set(0xffffff00, at: i)
}
}
}
}
class CustomMetadataProvider : SCIMetadataProvider3DBase<SCIPointLineRenderableSeries3D>, IISCIPointMetadataProvider3D, IISCIStrokeMetadataProvider3D
{
public void UpdateStrokeColors(SCIUnsignedIntegerValues strokeColors, uint defaultStroke)
{
UpdateColors(strokeColors);
}
public void UpdatePointMetadata(SCIUnsignedIntegerValues colors, SCIFloatValues scales, uint defaultColor, float defaultScale)
{
UpdateColors(colors);
var pointsCount = RenderableSeries.CurrentRenderPassData.PointsCount;
scales.Count = pointsCount;
for (int i = 0; i < pointsCount; i++)
{
scales.Set(1, i);
}
}
private void UpdateColors(SCIUnsignedIntegerValues colors)
{
var renderPassData = (SCIXyzRenderPassData3D)RenderableSeries.CurrentRenderPassData;
var upperThreshold = RenderableSeries.ParentSurface.Camera.OrbitalYaw % 90;
var lowerThreshold = RenderableSeries.ParentSurface.Camera.OrbitalPitch % 90;
var pointsCount = RenderableSeries.CurrentRenderPassData.PointsCount;
colors.Count = pointsCount;
var yValues = renderPassData.YValues;
for (int i = 0; i < pointsCount; i++)
{
var value = yValues.GetValueAt(i);
if (value > upperThreshold)
{
colors.Set(0xffff0000, i);
}
else if (value < lowerThreshold)
{
colors.Set(0xff00ff00, i);
}
else
{
colors.Set(0xffffff00, i);
}
}
}
}
Once the MetadataProvider class is ready, its instances can be used to set it for a RenderableSeries via the ISCIRenderableSeries3D.metadataProvider property:
let xAxis = SCINumericAxis3D()
xAxis.growBy = SCIDoubleRange(min: 0.1, max: 0.1)
let yAxis = SCINumericAxis3D()
yAxis.growBy = SCIDoubleRange(min: 0.1, max: 0.1)
let zAxis = SCINumericAxis3D()
zAxis.growBy = SCIDoubleRange(min: 0.1, max: 0.1)
let dataSeries = SCIXyzDataSeries3D(xType: .double, yType: .double, zType: .double)
for i in 0 ..< 100 {
let x = 5 * sin(Double(i))
let y = Double(i)
let z = 5 * cos(Double(i))
dataSeries.append(x: x, y: y, z: z)
}
let pointMarker = SCISpherePointMarker3D()
pointMarker.size = 5.0
let rs = SCIPointLineRenderableSeries3D()
rs.dataSeries = dataSeries
rs.strokeThickness = 2.0
rs.pointMarker = pointMarker
rs.metadataProvider = CustomMetadataProvider()
SCIUpdateSuspender.usingWith(surface) {
self.surface.xAxis = xAxis
self.surface.yAxis = yAxis
self.surface.zAxis = zAxis
self.surface.renderableSeries.add(rs)
self.surface.chartModifiers.add(ExampleViewBase.createDefault3DModifiers())
}
var dataSeries3D = new XyzDataSeries3D<double, double, double>();
for (int i = 0; i < 100; i++)
{
double x = 5 * Math.Sin(i);
double y = i;
double z = 5 * Math.Cos(i);
dataSeries3D.Append(x, y, z);
}
var rSeries3D = new SCIPointLineRenderableSeries3D
{
DataSeries = dataSeries3D,
StrokeThickness = 2f,
PointMarker = new SCISpherePointMarker3D { Size = 5f },
MetadataProvider = new CustomMetadataProvider(),
};
using (Surface.SuspendUpdates())
{
Surface.XAxis = new SCINumericAxis3D { GrowBy = new SCIDoubleRange(0.1, 0.1) };
Surface.YAxis = new SCINumericAxis3D { GrowBy = new SCIDoubleRange(0.1, 0.1) };
Surface.ZAxis = new SCINumericAxis3D { GrowBy = new SCIDoubleRange(0.1, 0.1) };
Surface.RenderableSeries.Add(rSeries3D);
Surface.ChartModifiers.Add(CreateDefault3DModifiers());
Surface.Camera = new SCICamera3D { Position = new SCIVector3(-350, 100, -350), Target = new SCIVector3(0, 50, 0) };
}