SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and iOS Chart & Android Chart Components

0
0

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.

Version
2.2
  • You must to post comments
0
0

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

  • Vidya Sagar Mohanraj
    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.
  • Yura Khariton
    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 to post comments
Showing 1 result
Your Answer

Please first to submit.