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

0
0

Hi,
I have a 2D graph with single X-Axis and single Y-Axis. I want to have the Y-Axis always zoom to fit the data, but also want to force a minimum span, so that tiny fluxtuations in the trace dont get amplified when the trace is running at a steady value. Currently I’m using a derived ViewportManager like this:

class MinYSpanViewportManager : DefaultViewportManager
{
    public double MinSpan { get; set; } = 0.0;

    public override void AttachSciChartSurface(ISciChartSurface scs)
    {
        base.AttachSciChartSurface(scs);
        ParentSurface = scs;
    }

    public ISciChartSurface ParentSurface { get; private set; }


    protected override IRange OnCalculateNewYRange(IAxis yAxis, RenderPassInfo renderPassInfo)
    {
        if (ParentSurface.ZoomState == ZoomStates.UserZooming)
        {
            return yAxis.VisibleRange.AsDoubleRange();
        }

        var range = yAxis.GetMaximumRange().AsDoubleRange();

        var currentSpan = range.Diff;

        if (currentSpan >= MinSpan)
        {
            return yAxis.GetMaximumRange().AsDoubleRange();
        }

        double delta = MinSpan - currentSpan;

        return new DoubleRange(range.Min - delta / 2, range.Max + delta / 2);
    }
}

Is this the best way to implement this feature? I’ve seen a property on the NumericAxis called MinimalZoomConstrain, which sounds like it does what I want. But when I tried setting it to a value, the YAxis no longer automatically zoomed to fit the data, even if AutoRange=”Always” was set.

Best regards,
Jonathan

Version
5.2.0.11680
  • You must to post comments
0
0

Hi Jonathan

Yes the ViewportManager is the perfect way to override the AutoRange algorithms in SciChart.

However, what you should be doing is calling base.OnCalculateNewYRange() to get the SciChart result first, and then modifying it if it falls outside of certain parameters.

The AutoRange algorithm uses more than just Axis.GetMaximumRange(). GetMaximumRange() considers the range of ALL dataseries registered on that axis, whereas OnCalculateNewYRange() considers the range of ALL dataseries on that axis but windowed by the part of the series visible in the XAxis (if that makes sense).

Best regards,
Andrew

Images
  • You must to post comments
0
0

Hi Andrew,
Thanks for the picture, Yes that makes perfect sense. But when I tried both base.OnCalculateNewYRange() and Axis.GetMaximumRange() they return the same value, even when zoomed in on a subset of the data points.

I created a single trace graph that plots a decaying sinewave, which has 1000 data points. When the full data set is in view both methods return a DoubleRange{Min–217.2, Max=998.3,}.

But when zoomed in on a low amplitude section, both methods returned: DoubleRange{Min=-18.0, Max=17.2}. I was expecting GetMaximumRange() to still return DoubleRange{Min–217.2, Max=998.3,}.

This image shows the results of breaking the code in OnCalculateNewYRange(), when the full dataset is in view:
Full Range

This images shows the results after using the rubber band to zoom in on a subset of the data.
Zoomed in

Best regards,
Jonathan

  • You must to post comments
0
0

Hi Jonathan

This is how our built-in DefaultViewportManager. OnCalculateNewYRange is implemented.

    /// <summary>
    /// Overridden by derived types, called when the parent <see cref="SciChartSurface" /> requests a YAxis VisibleRange.
    /// The Range returned by this method will be applied to the chart on render
    /// </summary>
    /// <param name="yAxis">The YAxis</param>
    /// <param name="renderPassInfo"></param>
    /// <returns>
    /// The new VisibleRange for the YAxis
    /// </returns>
    protected override IRange OnCalculateNewYRange(IAxis yAxis, RenderPassInfo renderPassInfo)
    {
        IRange newYRange = yAxis.VisibleRange;

        if ((yAxis.AutoRange == AutoRange.Always) &&
            renderPassInfo.PointSeries != null &&
            renderPassInfo.RenderableSeries != null)
        {
            newYRange = yAxis.CalculateYRange(renderPassInfo);
        }

        return newYRange;
    }  

As you can see, calculating an AutoRange in the base class is dependent on the AutoRange flag being equal to Always, and presence of data in the RenderPassInfo struct, which is set when the chart is drawn.

So, if you’re calling the base and not getting the desired result, maybe check those flags? Alternatively, call the yAxis.CalculateYRange(renderPassInfo) yourself to get an AutoRange result.

Best regards
Andrew

  • You must to post comments
Showing 3 results
Your Answer

Please first to submit.

SciChart WPF v6 BETA released! Note pricing/licensing changes are coming. Contact us to beat the rise!Read more
+