Search code examples
c#wpfxamlmvvmoxyplot

Affine transformations in Oxyplot


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)


Solution

  • 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>