csharp/ABTSoftware/SciChart.Wpf.Examples/Examples/SciChart.Examples/Examples/Charts3D/CreateRealtime3DCharts/CreateRealTime3DUniformMeshChart.xaml.cs

CreateRealTime3DUniformMeshChart.xaml.cs
// *************************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2021. All rights reserved.
//  
// Web: http://www.scichart.com
//   Support: [email protected]
//   Sales:   [email protected]
// 
// CreateRealTime3DUniformMeshChart.xaml.cs is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use. 
// 
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied. 
// *************************************************************************************
using System;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using SciChart.Charting3D.Model;
using SciChart.Charting3D.RenderableSeries;
using SciChart.Data.Model;

namespace SciChart.Examples.Examples.Charts3D.CreateRealtime3DCharts
{
    /// 
    /// Interaction logic for CreateRealTime3DSurfaceMeshChart.xaml
    /// 
    public partial clast CreateRealTime3DSurfaceMeshChart : UserControl
    {
        private Timer _timer;
        private bool _processingUpdate;
        private object _syncRoot = new object();

        public CreateRealTime3DSurfaceMeshChart()
        {
            InitializeComponent();

            this.Loaded += (s, e) =>
            {
                this.StartButton.IsChecked = true;
                this.OnStart();
            };

            this.Unloaded += (s, e) =>
            {
                this.OnStop();
            };
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            OnStart(); 
        }

        private void PauseButton_Click(object sender, RoutedEventArgs e)
        {
            OnStop();
        }        

        private void DataCombo_OnSelectionChanged(object sender, EventArgs e)
        {
            OnStart();
        }

        private void OnStart()
        {
            if (!IsLoaded) return;

            string whatData = (string)DataCombo.SelectedItem;
            int w = 0, h = 0;
            if (whatData == "3D Sinc 10 x 10") w = h = 10;
            if (whatData == "3D Sinc 50 x 50") w = h = 50;
            if (whatData == "3D Sinc 100 x 100") w = h = 100;
            if (whatData == "3D Sinc 500 x 500") w = h = 500;
            if (whatData == "3D Sinc 1k x 1k") w = h = 1000;

            lock (_syncRoot)
            {
                OnStop();
            }

            var dataSeries = new UniformGridDataSeries3D(w, h)
            {
                StartX = 0, 
                StartZ = 0, 
                StepX = 10 / (w - 1d), 
                StepZ = 10 / (h - 1d),
                SeriesName = "Realtime Surface Mesh",
            };

            var frontBuffer = dataSeries.InternalArray;
            var backBuffer = new GridData(w, h).InternalArray;

            int frames = 0;            
            _timer = new Timer();
            _timer.Interval = 20;
            _timer.Elapsed += (s, arg) =>
            {
                lock (_syncRoot)
                {
                    double wc = w*0.5, hc = h*0.5;
                    double freq = Math.Sin(frames++*0.1)*0.1 + 0.1;

                    // Each set of dataSeries[i,j] schedules a redraw when the next Render event fires. Therefore, we suspend updates so that we can update the chart once
                    // Data generation (Sin, Sqrt below) is expensive. We parallelize it by using Parallel.For for the outer loop
                    //  Equivalent of "for (int j = 0; j < h; j++)"
                    // This will result in more CPU usage, but we wish to demo the performance of the actual rendering, not the slowness of generating test data! :)
                    Parallel.For(0, h, i =>
                    {
                        var buf = frontBuffer;
                        for (int j = 0; j < w; j++)
                        {
                            // 3D Sinc function from http://www.mathworks.com/matlabcentral/cody/problems/1305-creation-of-2d-sinc-surface
                            // sin(pi*R*freq)/(pi*R*freq)
                            // R is distance from centre

                            double radius = Math.Sqrt((wc - i)*(wc - i) + (hc - j)*(hc - j));
                            var d = Math.PI*radius*freq;
                            var value = Math.Sin(d)/d;
                            buf[i][j] = double.IsNaN(value) ? 1.0 : value;
                        }
                    });

                    using (dataSeries.SuspendUpdates(false, true))
                    {
                        dataSeries.CopyFrom(frontBuffer);
                        var temp = backBuffer;
                        backBuffer = frontBuffer;
                        frontBuffer = temp;
                    }
                }
            };
            SurfaceMesh.DataSeries = dataSeries;
            _timer.Start();
            StartButton.IsEnabled = false;
            PauseButton.IsEnabled = true;
        }

        private void OnStop()
        {
            if (_timer != null)
            {
                _timer.Stop();
                StartButton.IsEnabled = true;
                PauseButton.IsEnabled = false;
            }
        }

        private void ColorMapCombo_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!IsLoaded) return;

            // Valid combinations check. If palette is a LinearGradientBrush and we are in texture mode, switch to heightmap mode
            if (((BrushColorPalette)ColorMapCombo.SelectedItem).Brush is LinearGradientBrush && 
                (MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.Textured) || MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.TexturedSolidCells)))
            {
                MeshPaletteModeCombo.SelectedItem = MeshPaletteMode.HeightMapInterpolated;
            }
            // Valid combinations check. If palette is a TextureBrush and we are not in texture mode, switch to texture mode
            else if (((BrushColorPalette)ColorMapCombo.SelectedItem).Brush is VisualBrush && 
                (MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.HeightMapInterpolated) || MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.HeightMapSolidCells)))
            {
                MeshPaletteModeCombo.SelectedItem = MeshPaletteMode.Textured;
            }
        }

        private void MeshPaletteModeCombo_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!IsLoaded) return;

            // Valid combinations check. If mesh palette is a heightmap, then choose a gradient brush for the palette
            if ((MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.HeightMapInterpolated) || MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.HeightMapSolidCells))
                && ((BrushColorPalette)ColorMapCombo.SelectedItem).Brush is VisualBrush)
            {
                ColorMapCombo.SelectedIndex = 0;
            }
            // Valid combinations check. If mesh palette is textured, then choose a texture brush for the palette
            else if (MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.Textured) ||
                MeshPaletteModeCombo.SelectedItem.Equals(MeshPaletteMode.TexturedSolidCells))
            {
                ColorMapCombo.SelectedIndex = ColorMapCombo.Items.Count - 1;
            }
        }
    }
}