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

Answered
0
0

Hi,

I ran into a problem with EllipsePointMarkers when I have a big amount of PointMarkers inside the Rendersurface. Here is a brief description of what I am trying to do:

  1. I created a custom BasePointMarker-derived class which draws Ellipses with a varying Color – depending on some threshold defined in the IPointMetaData implementation

  2. Everything works fine as long as the points are not tightly spaced between each other. That said, when I rescale my Rendersurface, the points get shifted so that the red points (who are below the threshold) will move up and suddenly green points are at minimum YValue positions.

I attached two pictures which hopefully illustrate my problem better than my words can do. Maybe somebody knows a workaround / fix for this problem. Any help is appreciated! ( I assume it has sth to do with the RescaleMode which prevents some points from being drawn to the surface and shifts others so that the Chart isn’t too dense)

Best,
Matthias

Update
Since Andrew suggested that the behavior comes from my custom code (which is hopefully true), here are the relevant parts:

DiaryView.xaml

<s:SciChartSurface Grid.Row="1" Grid.Column="0"
                       s:ThemeManager.Theme="Chrome"
                       Background="White" RenderableSeries="{s:SeriesBinding DiarySeriesViewModels}">
        <s:SciChartSurface.RenderSurface>
            <s:HighQualityRenderSurface/>
        </s:SciChartSurface.RenderSurface>
        <s:SciChartSurface.YAxis>
            <s:NumericAxis AxisAlignment="Left" DrawMajorBands="False" VisibleRange="0,900"/>
        </s:SciChartSurface.YAxis>
        <s:SciChartSurface.XAxis>
            <s:DateTimeAxis DrawMajorBands="True" VisibleRangeLimit="{Binding VisibleRangeLimit}"
                            DrawMinorGridLines="False"
                            x:Name="XAxis"
                            CursorTextFormatting="dd.MM.yyyy HH:mm"/>
        </s:SciChartSurface.XAxis>
    </s:SciChartSurface>

DiaryViewModel.cs

Property:

    public List<IRenderableSeriesViewModel> DiarySeriesViewModels { get; private set; }

inside the Constructor:

this.DiarySeriesViewModels.Add(new LineRenderableSeriesViewModel()
        {
            AntiAliasing = true,
            PointMarker = new DiaryPointMarker()
            {
                AntiAliasing = true,
                LowRatingColor = Brushes.Red,
                MidRatingColor = Brushes.Orange,
                HighRatingColor = Brushes.Green,
                Width = 10,
                Height = 10,
                StrokeThickness = 2
            },
            DataSeries = GetSampleSeries()
        });

GetSampleSeries():

private XyDataSeries<DateTime, double> GetSampleSeries()
    {
        var series = new XyDataSeries<DateTime, double>();

        var startDate = this.VisibleRangeLimit.Min.AddDays(5);
        var currentDate = startDate;
        int i = 0;
        while (currentDate < DateTime.Now)
        {
            var yval = 60*(Math.Log(i))*Math.Abs(Math.Sin(Math.PI*(i++/100.0)));
            currentDate = currentDate.AddHours(5.3);
            string comment = i%20 == 0 ? String.Format("Sample comment #{0}", i) : string.Empty;
            double rating = 100*yval/60;
            series.Append(currentDate,yval,new DiaryPointMetaData(rating,comment));
        }

        return series;
    }

DiaryPointMarker.cs: (based on an example which I found here at SciChart)

public class DiaryPointMarker : BasePointMarker
{
    private IList<IPointMetadata> _dataPointMetadata;
    IList<int> _dataPointIndexes = new List<int>();


    private IPen2D _lowStrokePen;
    private IPen2D _midStrokePen;
    private IPen2D _highStrokePen;


    private IBrush2D _lowRatingColor;
    private IBrush2D _midRatingColor;
    private IBrush2D _highRatingColor;
    private IBrush2D _noCommentColor;

    public Brush LowRatingColor { get; set; }
    public Brush MidRatingColor { get; set; }
    public Brush HighRatingColor { get; set; }

    public override void BeginBatch(IRenderContext2D context, Color? strokeColor, Color? fillColor)
    {
        _dataPointMetadata = _dataPointMetadata ?? RenderableSeries.DataSeries.Metadata;

        _dataPointIndexes = new List<int>();

        base.BeginBatch(context, strokeColor, fillColor);
    }


    public override void MoveTo(IRenderContext2D context, double x, double y, int index)
    {
        if (IsInBounds(x, y))
        {
            _dataPointIndexes.Add(index);
        }

        base.MoveTo(context, x, y, index);
    }

    public override void Draw(IRenderContext2D context, IEnumerable<Point> centers)
    {
        TryCasheResources(context);

        var markerLocations = centers.ToArray();

        for (int i = 0; i < markerLocations.Length; ++i)
        {
            var diaryMetaInfo = _dataPointMetadata[_dataPointIndexes[i]] as DiaryPointMetaData;

            var center = markerLocations[i];

            context.DrawEllipse(
                diaryMetaInfo.Rating < 60 ? _lowStrokePen : diaryMetaInfo.Rating < 80 ? _midStrokePen : _highStrokePen,
                String.IsNullOrEmpty(diaryMetaInfo.Comment) ? _noCommentColor : diaryMetaInfo.Rating < 60 ? _lowRatingColor : diaryMetaInfo.Rating < 80 ?_midRatingColor : _highRatingColor,
                center,
                Width,
                Height
            );
        }
    }

    private void TryCasheResources(IRenderContext2D context)
    {
        _lowStrokePen = _lowStrokePen ?? context.CreatePen(LowRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);
        _midStrokePen = _midStrokePen ?? context.CreatePen(MidRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);
        _highStrokePen = _highStrokePen ?? context.CreatePen(HighRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);

        _lowRatingColor = _lowRatingColor ?? context.CreateBrush(LowRatingColor);
        _midRatingColor = _midRatingColor ?? context.CreateBrush(MidRatingColor);
        _highRatingColor = _highRatingColor ?? context.CreateBrush(HighRatingColor);
        _noCommentColor = _noCommentColor ?? context.CreateBrush(Color.FromArgb(0, 0, 0, 0));
    }
}
Images
  • You must to post comments
Best Answer
1
0

Update: Clustering can now be disabled in SciChart v4.1 or above using this code:

        // C# code
        EllipsePointMarker p;
        p.PointMarkerBatchStrategy = new DefaultPointMarkerBatchStrategy();

or

       <!-- XAML code -->
       <s:EllipsePointMarker>
           <s:EllipsePointMarker.PointMarkerBatchStrategy>
                <s:DefaultPointMarkerBatchStrategy/>
           </s:EllipsePointMarker.PointMarkerBatchStrategy>
       </s:EllipsePointMarker>

Best regards,
Andrew

  • You must to post comments
Best Answer
0
0

Hi Mattias,

Thank you for reporting this. We’ve investigated it and it appears to be a resampling (clustering) issue. The reason being that during clustering we loose data points order, so there isn’t a direct match between stored indexes and the points to draw.

Unfortunately, there isn’t an out of the box solution for this yet. But it can be worked around easily. Please find the code with necessary changes below:

public class DiaryPointMarker : BasePointMarker
{
    private IList<IPointMetadata> _dataPointMetadata;
    IList<int> _dataPointIndexes = new List<int>();

    private IPen2D _lowStrokePen;
    private IPen2D _midStrokePen;
    private IPen2D _highStrokePen;

    private IBrush2D _lowRatingColor;
    private IBrush2D _midRatingColor;
    private IBrush2D _highRatingColor;
    private IBrush2D _noCommentColor;

    private List<Point> _points = new List<Point>();

    public bool UseClustering { get; set; }

    public Brush LowRatingColor { get; set; }
    public Brush MidRatingColor { get; set; }
    public Brush HighRatingColor { get; set; }

    public override void BeginBatch(IRenderContext2D context, Color? strokeColor, Color? fillColor)
    {
        _dataPointMetadata = _dataPointMetadata ?? RenderableSeries.DataSeries.Metadata;

        _dataPointIndexes.Clear();
        _points.Clear();

        base.BeginBatch(context, strokeColor, fillColor);
    }

    public override void MoveTo(IRenderContext2D context, double x, double y, int index)
    {
        if (IsInBounds(x, y))
        {
            _dataPointIndexes.Add(index);
            _points.Add(new Point(x, y));
        }

        if (UseClustering)
        {
            base.MoveTo(context, x, y, index);
        }
    }

    public override void EndBatch(IRenderContext2D context)
    {
        if (UseClustering)
        {
            base.EndBatch(context);
        }
        else
        {
            Draw(context, _points);
            context.SetPrimitvesCachingEnabled(false);
        }
    }

    public override void Draw(IRenderContext2D context, IEnumerable<Point> centers)
    {
        TryCasheResources(context);

        var markerLocations = centers.ToArray();

        for (int i = 0; i < markerLocations.Length; ++i)
        {
            var diaryMetaInfo = _dataPointMetadata[_dataPointIndexes[i]] as DiaryPointMetaData;

            var center = markerLocations[i];

            context.DrawEllipse(
                diaryMetaInfo.Rating < 60 ? _lowStrokePen : diaryMetaInfo.Rating < 80 ? _midStrokePen : _highStrokePen,
                String.IsNullOrEmpty(diaryMetaInfo.Comment) ? _noCommentColor : diaryMetaInfo.Rating < 60 ? _lowRatingColor : diaryMetaInfo.Rating < 80 ? _midRatingColor : _highRatingColor,
                center,
                Width,
                Height
            );
        }
    }

    private void TryCasheResources(IRenderContext2D context)
    {
        _lowStrokePen = _lowStrokePen ?? context.CreatePen(LowRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);
        _midStrokePen = _midStrokePen ?? context.CreatePen(MidRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);
        _highStrokePen = _highStrokePen ?? context.CreatePen(HighRatingColor.ExtractColor(), AntiAliasing, (float)StrokeThickness, Opacity);

        _lowRatingColor = _lowRatingColor ?? context.CreateBrush(LowRatingColor);
        _midRatingColor = _midRatingColor ?? context.CreateBrush(MidRatingColor);
        _highRatingColor = _highRatingColor ?? context.CreateBrush(HighRatingColor);
        _noCommentColor = _noCommentColor ?? context.CreateBrush(Color.FromArgb(0, 0, 0, 0));
    }
}

I added the UseClustering property, which you need to set to “False” in XAML to switch the clustering off.

Please try it out and let us know if it works for you,

Best regards,
Yuriy

  • You must to post comments
Showing 2 results
Your Answer

Please first to submit.