3

My Math teacher asked me to write a program to plot a graph. I really don't know what I must implement, so I decided to draw a sin function. As I have some experience in Windows Phone development, I decided to write it in WPF. So I wrote the following XAML:

<Window x:Class="DemoMath.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:local="clr-namespace:DemoMath"
        mc:Ignorable="d"
        Title="MathDemo" DataContext="{Binding RelativeSource={RelativeSource Self}}" Height="301.645" Width="525">
            <StackPanel VerticalAlignment="Center" Grid.Row="1">

            <Canvas Background="CornflowerBlue" x:Name="drawingSurface" Height="128">
                <TextBlock Width="{Binding ActualWidth, ElementName=drawingSurface}" Canvas.Top="25" x:Name="placeholder"  FontSize="40" Foreground="#FFF"  Text="Graph" TextAlignment="Center" FontFamily="Segoe Print"/>
                <Polyline Points="{Binding Graph2DPoints, Converter={StaticResource PointConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Stroke="White" StrokeThickness="2" Visibility="Collapsed" x:Name="graph2D"/>
                <Polyline Visibility="Collapsed" x:Name="scale"/>
            </Canvas>
            <Grid>
                <Button x:Name="button" Click="Button_Click_1" Width="100">
                    <Button.Content>
                        <TextBlock Text="Generate" FontSize="16" FontFamily="Times New Roman"/>
                    </Button.Content>
                </Button>
            </Grid>
        </StackPanel>
</Window>

And C# code:

    public class ToPointCollection : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value!=null)
                return new PointCollection(value as ObservableCollection<Point>);
            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
    public partial class MainWindow : Window , INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        private void RaisePropertyChanged(string propertyName)
        {
            var handlers = PropertyChanged;
            handlers(this, new PropertyChangedEventArgs(propertyName));
        }
        ObservableCollection<Point> _graph2DPoints = new ObservableCollection<Point>();
        public ObservableCollection<Point> Graph2DPoints
        {
            get
            {
                return _graph2DPoints;
            }
            set
            {
                _graph2DPoints = value;
                RaisePropertyChanged("Graph2DPoints");
            }
        }
        Timer timer = new Timer() { Interval=1, AutoReset=true };
        double HeightSurface;
        public MainWindow()
        {
            InitializeComponent();
            Graph2DPoints.CollectionChanged += (s, e) => { RaisePropertyChanged("Graph2DPoints"); };
            timer.Elapsed += Timer_Elapsed;
            HeightSurface = drawingSurface.Height / 2;
        }
private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            timer.Stop();
            button.Visibility = Visibility.Collapsed;
            placeholder.Visibility = Visibility.Collapsed;

            List<Point> GraphPoints = new List<Point>(), scalePoints = new List<Point>();
            for (double phi = 0.0; phi <= 4* Math.PI; phi = phi + 0.1)
            {
                phi = Math.Round(phi, 2);
                var cosValue = -Math.Cos(phi);
                scalePoints.Add(new Point(20 + phi * 15, drawingSurface.Height / 2));
                Graph2DPoints.Add(new Point(20 + phi * 15, drawingSurface.Height / 2 + cosValue * 15));
            }
            scale.Points = new PointCollection(scalePoints);
            scale.Stroke = new SolidColorBrush(Colors.WhiteSmoke);
            scale.StrokeThickness = 1;
            graph2D.Visibility = Visibility.Visible;
            scale.Visibility = Visibility.Visible;
            graph3D.Visibility = Visibility.Collapsed;

            timer.Start();
        }
        double phase = Math.PI/2+0.1;
        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            timer.Stop();
            for (int i = 0; i < Graph2DPoints.Count; i++)
            {
                Graph2DPoints[i] = new Point(Graph2DPoints[i].X - 0.5, Graph2DPoints[i].Y);
            }
            if (Graph2DPoints[0].X < 0)
            {
                Graph2DPoints.RemoveAt(0);
                Point p = Graph2DPoints.Last();
                System.Diagnostics.Debug.WriteLine(p.X);
                Graph2DPoints.Add(new Point(p.X + 4.5, HeightSurface - Math.Cos(phase) * 15));
            }
            phase += 0.1;
            if (phase >= 5*Math.PI/2)
                phase = Math.PI / 2;

            phase = Math.Round(phase, 2);
            timer.Start();
        }
    }
}

After some time, it shows something strange, and System.Diagnostics.Debug.WriteLine(p.X); returns only one value:

enter image description here

honk
  • 8,101
  • 11
  • 72
  • 75
Fallen Angel
  • 152
  • 1
  • 3
  • 15

1 Answers1

1

Okay! Good job First of all!

What you refer as After some time is actually when the program starts to run the body for if (Graph2DPoints[0].X < 0) statement.

The reason for the weird behavior is that there is an inconsistency between these two lines:

for (int i = 0; i < Graph2DPoints.Count; i++)
        {
            Graph2DPoints[i] = new Point(Graph2DPoints[i].X - 0.5, Graph2DPoints[i].Y);
        }

and

Graph2DPoints.Add(new Point(p.X + 4.5, HeightSurface - Math.Cos(phase) * 15));

The problem is that in the top part of the code you are advancing your graph by -0.5 pixels, where as in the second part you are advancing it by 4.5 !

I advise you to set -0.5 to -4.5 and try it again! as you see that, the problem is gone. however you might complaint about the wider length of the wavelength. well there are two options for you:

1- Instead of 4.5 , use a lower value like 2.5 ! 2- Try multiplying Y values into something so it looks better.

Also there is a little glitch at the start, which you can solve it with changing the start phase.

;) have fun.

uncommon_name
  • 422
  • 2
  • 5
  • 18
  • 1
    Thanks a lot! :) That was helpful, I figured, that `double phase = Math.PI*2-1.2;` and shifting by 1.5 (I have remembered, that I was multiplying on 15, and found this value) have resloved the problem :) May I ask you one more question? Can this method of finding the Y point in 3D plane (Electro-magnetic wave propagation) be imnplemented in the same way? – Fallen Angel Jan 03 '16 at 08:52
  • you're welcome. You can use the same collection of points "Graph2DPoints" as the source for your 3dplane polyline object + use a scale transform (scaleY = -1) along with a Skew transform as RenderTransform and set (0.5 , 0.5) as the transformOrigin. – uncommon_name Jan 03 '16 at 12:24
  • Ah, OK, I understand in which way I must implement these functions. Thanks :) – Fallen Angel Jan 03 '16 at 17:15