Hi,
I am following this https://www.scichart.com/example/android-chart-custom-series-spline-line/ to implement spline for a realtime mountain series but the thing is the render series is shown for an instant and then disappears. And also I am not able to get the areaStyle to work. Please advice.
import android.content.Context
import android.util.DisplayMetrics
import com.scichart.charting.model.dataSeries.IDataSeries
import com.scichart.charting.visuals.pointmarkers.IPointMarker
import com.scichart.charting.visuals.renderableSeries.BaseMountainRenderableSeries
import com.scichart.charting.visuals.renderableSeries.ISeriesDrawingManager
import com.scichart.charting.visuals.renderableSeries.data.ISeriesRenderPassData
import com.scichart.charting.visuals.renderableSeries.data.MountainRenderPassData
import com.scichart.charting.visuals.renderableSeries.hitTest.*
import com.scichart.core.framework.SmartPropertyBoolean
import com.scichart.core.framework.SmartPropertyInteger
import com.scichart.core.model.FloatValues
import com.scichart.core.utility.SciChartDebugLogger
import com.scichart.drawing.common.DrawingContextFactory
import com.scichart.drawing.common.IAssetManager2D
import com.scichart.drawing.common.IRenderContext2D
import com.scichart.drawing.common.LinearGradientBrushStyle
import com.scichart.extensions.builders.PenStyleBuilder.SolidPenStyleBuilder
class BezelMountainSeries(currentRenderPassData: MountainRenderPassData, hitProvider: IHitProvider, nearestPointProvider: INearestPointProvider) :
BaseMountainRenderableSeries(currentRenderPassData, hitProvider, nearestPointProvider) {
protected val isSplineEnabledProperty = SmartPropertyBoolean(SmartPropertyBoolean.IPropertyChangeListener { oldValue, newValue -> invalidateElement() }, true)
protected val upSampleFactorProperty = SmartPropertyInteger(SmartPropertyInteger.IPropertyChangeListener { oldValue, newValue -> invalidateElement() }, 10)
private val splineXCoords = FloatValues()
private val splineYCoords = FloatValues()
var isSplineEnabled: Boolean
get() = isSplineEnabledProperty.value
set(isSplineEnabled) = isSplineEnabledProperty.setStrongValue(isSplineEnabled)
var upSampleFactor: Int
get() = upSampleFactorProperty.value
set(upSampleFactor) = upSampleFactorProperty.setStrongValue(upSampleFactor)
init {
seriesInfoProvider = LerpXySeriesInfoProvider()
}
override fun disposeCachedData() {
super.disposeCachedData()
splineXCoords.disposeItems()
splineYCoords.disposeItems()
}
override fun internalDraw(renderContext: IRenderContext2D, assetManager: IAssetManager2D, renderPassData: ISeriesRenderPassData) {
// Don't draw transparent series
val opacity = opacity
if (opacity == 0f) return
val strokeStyle = strokeStyle
if (strokeStyle == null || !strokeStyle.isVisible) return
val currentRenderPassData = renderPassData as MountainRenderPassData
computeSplineSeries(splineXCoords, splineYCoords, currentRenderPassData, isSplineEnabled, upSampleFactor)
val linesStripDrawingContext = DrawingContextFactory.LINES_STRIP_DRAWING_CONTEXT
val pen = assetManager.createPen(strokeStyle, opacity)
val digitalLine = currentRenderPassData.isDigitalLine
val closeGaps = currentRenderPassData.closeGaps
val drawingManager = services.getService(ISeriesDrawingManager::class.java)
drawingManager.beginDraw(renderContext, currentRenderPassData)
drawingManager.iterateLines(linesStripDrawingContext, pen, splineXCoords, splineYCoords, digitalLine, closeGaps)
drawingManager.endDraw()
drawPointMarkers(renderContext, assetManager, currentRenderPassData.xCoords, currentRenderPassData.yCoords)
}
/**
* Cubic Spline interpolation: http://www.codeproject.com/Articles/560163/Csharp-Cubic-Spline-Interpolation
*/
private fun computeSplineSeries(splineXCoords: FloatValues, splineYCoords: FloatValues, currentRenderPassData: MountainRenderPassData, isSplineEnabled: Boolean, upSampleFactor: Int) {
if (!isSplineEnabled) return
// Spline enabled
val size = currentRenderPassData.pointsCount()
val splineSize = size * 5
splineXCoords.setSize(splineSize)
splineYCoords.setSize(splineSize)
val x = currentRenderPassData.xCoords.itemsArray
val y = currentRenderPassData.yCoords.itemsArray
val xs = splineXCoords.itemsArray
val stepSize = (x[size - 1] - x[0]) / (splineSize - 1)
// set spline xCo-ordinates
for (i in 0 until splineSize) {
xs[i] = x[0] + i * stepSize
}
var ys = FloatArray(0)
try {
val cubicSpline = CubicSpline()
ys = cubicSpline.fitAndEval(x, y, size, xs, java.lang.Float.NaN, java.lang.Float.NaN, true)
} catch (e: Exception) {
SciChartDebugLogger.instance().handleException(e)
}
// copy spline yCo-ordinates
System.arraycopy(ys, 0, splineYCoords.itemsArray, 0, splineSize)
}
class BezelMountainRenderableSeriesBuilder(context: Context) {
private val displayMetrics: DisplayMetrics = context.resources.displayMetrics
private val renderableSeries: BezelMountainSeries = BezelMountainSeries(MountainRenderPassData(), CompositeHitProvider(PointMarkerHitProvider(), MountainHitProvider()), NearestXyPointProvider())
fun withDataSeries(dataSeries: IDataSeries<*, *>): BezelMountainRenderableSeriesBuilder {
renderableSeries.dataSeries = dataSeries
return this
}
fun withStrokeStyle(seriesColor: Int, strokeThickness: Float, antiAliasing: Boolean): BezelMountainRenderableSeriesBuilder {
renderableSeries.strokeStyle = SolidPenStyleBuilder(displayMetrics).withThickness(strokeThickness).withColor(seriesColor).withAntiAliasing(antiAliasing).build()
return this
}
fun withAreaStyle(x0 : Float, y0 : Float, x1 : Float , y1 : Float, startColor : Int , endColor : Int) : BezelMountainRenderableSeriesBuilder {
renderableSeries.areaStyle = LinearGradientBrushStyle(x0, y0, x1, y1, startColor, endColor)
return this
}
fun withPointMarker(pointMarker: IPointMarker): BezelMountainRenderableSeriesBuilder {
renderableSeries.pointMarker = pointMarker
return this
}
fun withUpSampleFactor(upSampleFactor: Int): BezelMountainRenderableSeriesBuilder {
this.renderableSeries.upSampleFactor = upSampleFactor
return this
}
fun withIsSplineEnabled(isSplineEnabled: Boolean): BezelMountainRenderableSeriesBuilder {
this.renderableSeries.isSplineEnabled = isSplineEnabled
return this
}
fun build(): BezelMountainSeries {
return renderableSeries
}
}
}
The CubicSpline and TriDiagonalMatrixF implementation are the same as the example with no alterations.
- Vidya Sagar Mohanraj asked 6 years ago
- last edited 6 years ago
- You must login to post comments
Hi Vidya
To draw mountain series you need to use iterateMountainArea() method from drawing manager which draws filled area. So you’ll need to modify your internalDraw and add this call:
override fun internalDraw(renderContext: IRenderContext2D, assetManager: IAssetManager2D, renderPassData: ISeriesRenderPassData) {
// Don't draw transparent series
val opacity = opacity
if (opacity == 0f) return
val strokeStyle = strokeStyle
if (strokeStyle == null || !strokeStyle.isVisible) return
val currentRenderPassData = renderPassData as MountainRenderPassData
computeSplineSeries(splineXCoords, splineYCoords, currentRenderPassData, isSplineEnabled, upSampleFactor)
val linesStripDrawingContext = DrawingContextFactory.LINES_STRIP_DRAWING_CONTEXT
val triangleStripDrawingContex = DrawingContextFactory.TRIANGLES_STRIP_DRAWING_CONTEXT
val pen = assetManager.createPen(strokeStyle, opacity)
val brush = assetManager.createBrush(SolidBrushStyle(strokeStyle.color))
val digitalLine = currentRenderPassData.isDigitalLine
val closeGaps = currentRenderPassData.closeGaps
val drawingManager = services.getService(ISeriesDrawingManager::class.java)
drawingManager.beginDraw(renderContext, currentRenderPassData)
drawingManager.iterateMountainArea(triangleStripDrawingContex, brush, splineXCoords, splineYCoords, digitalLine, closeGaps, getYZeroCoord(renderPassData.yCoordinateCalculator))
drawingManager.iterateLines(linesStripDrawingContext, pen, splineXCoords, splineYCoords, digitalLine, closeGaps)
drawingManager.endDraw()
drawPointMarkers(renderContext, assetManager, currentRenderPassData.xCoords, currentRenderPassData.yCoords)
}
Also I noticed that your code throws exception which could be the cause of the missing series – it looks that it’s cause by wrong format string in CubicSpline implementation and the debug = true which you pass in fitAndEval() call. If you pass debug = false everything should be OK
ys = cubicSpline.fitAndEval(x, y, size, xs, java.lang.Float.NaN, java.lang.Float.NaN, false)
Can you try it?
Best regards,
Yura
- Yura Khariton answered 6 years ago
-
Thanks Yura. I will try that. The debug with false also gives the same result. I have narrowed it down to something to do with the visibleRange. When I start populating the series the series doesn’t appear it remains the same even when I recreate it but when the scrolling starts and I recreate the graph it doesn’t disappear. I also found that I keep getting “The X values to evaluate must be sorted” I am not sure why this stops on recreating after the condition is met. I am also seeing multiple index out of bounds at `(1 – t) * yOrig!![j] + t * yOrig!![j + 1] + t * (1 – t) * (a[j] * (1 – t) + b[j] * t)` in the evalSpline.
-
Well you should understand that SplineRenderableSeries was created as example which shows how to create custom renderable series. Code which you used as base provided as is without any guaranties (it isn’t production ready, may contain bugs because it shouldn’t cover all possible cases which would make code more complicated and we try to make example as simple as possible), you can modify it as you want but creation custom series for you isn’t covered by our support policy. If you find some bug in our examples (like this issue with sorted x values) please report it by creating ticket with detailed steps to reproduce.
- You must login to post comments
Please login first to submit.