3

I would like to perform a simple affine transformation to a LineSeries

Unfortunately I cannot simply do this:

foreach (var point in myLineSeries.Points)
{
    point.X = point.X * Math.Cos(angle_rad) + point.Y ...
    point.Y = ...
}

...as the properties X and Y of a DataPoint do not have a public set accessor

Is there a way to perform such operations (simple rotations/translations) on LineSeries without having to call Clear on it and then Add new transformed DataPoints (which doesn't seem very elegant to me)

jsanalytics
  • 13,058
  • 4
  • 22
  • 43
harveyAJ
  • 833
  • 1
  • 11
  • 26

1 Answers1

1

Rather than manipulating points directly in your LineSeries, use MVVM instead: do your calculations in your View Model and then bind results to your View.

enter image description here

ViewModel:

public class MyViewModel
{
    public ObservableCollection<DataPoint> Data1 { get; set; }
    public ObservableCollection<DataPoint> Data2 { get; set; }

    public MyViewModel()
    {
        Data1 = new ObservableCollection<DataPoint>();
        Data2 = new ObservableCollection<DataPoint>();

        double pi = Math.PI;
        double a = pi / 4; // rotation angle
        double fw = 5; // wave frequency
        double fs = 100 * fw; // sampling rate
        double te = 1; // end time
        int size = (int)(fs * te); 

        // do your calculations
        var x1 = Enumerable.Range(0, size).Select(p => p / fs).ToArray();
        var y1 = x1.Select(p => Math.Sin(2*pi*fw*p)).ToArray();

        //populate your Model (original data)
        for (int i = 0; i < y1.Length; i++)
            Data1.Add(new DataPoint(x1[i], y1[i]));

        // transform original data
        var x2 = Data1.Select(p => p.X * Math.Cos(a) - p.Y * Math.Sin(a)).ToArray();
        var y2 = Data1.Select(p => p.X * Math.Sin(a) + p.Y * Math.Cos(a)).ToArray();

        // populate your Model (transformed data)
        for (int i = 0; i < y2.Length; i++)
            Data2.Add(new DataPoint(x2[i], y2[i]));
    }
}

XAML:

<Window x:Class="WpfApp93.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:oxy="http://oxyplot.org/wpf"
        xmlns:local="clr-namespace:WpfApp93"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="350">

    <Window.DataContext>
        <local:MyViewModel/>
    </Window.DataContext>

    <Grid>
        <oxy:Plot>
            <oxy:LineSeries ItemsSource="{Binding Data1}"
                            DataFieldX="X"
                            DataFieldY="Y"/>
            <oxy:LineSeries ItemsSource="{Binding Data2}"
                            DataFieldX="X"
                            DataFieldY="Y"/>
        </oxy:Plot>
    </Grid>
</Window>
jsanalytics
  • 13,058
  • 4
  • 22
  • 43
  • 1
    Great stuff! I'll try that when I get to my workstation... so essentially I have to make a copy of the data. Any reasons why Oxyplot developers have made DataPoint immutable? – harveyAJ Dec 19 '17 at 12:25
  • Most likely just to really keep it simple. Adding a **set** would probably require to also provide _change notification_ as well, in order to keep up with MVVM. – jsanalytics Dec 19 '17 at 12:46
  • 1
    _so essentially I have to make a copy of the data_ => not necessarily... here a copy is made because, in a **rotation transform**, in order to calculate `x2`, both `x1` and `y1` are required. Similarly, in order to calculate `y2`, both `x1` and `y1` are required as well. But for instance, in a **translation transform**, you might be able to do it in one step. Also, we wanted to show both results, the original and the transformed one. Not to mention that, IMHO, it makes for a cleaner, more readable code. – jsanalytics Dec 19 '17 at 13:00
  • 1
    In my app the user clicks a button and the content rotates from one coordinate system to another, you can click again to go back to to the original data. Thing is, I was not keeping a copy of the original data, so I re-calculated rotated coordinates every time. So not only is it a cleaner approach but it's also more efficient in my case. Also, I'm shamelessly copying your nice Linq queries and mvvm approach (I was doing all the manipulation in code behind!) thanks a lot! – harveyAJ Dec 19 '17 at 14:54
  • You're very welcome! Glad it was helpful to you, thanks! – jsanalytics Dec 19 '17 at 15:20