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

CreateRealTime3DGeoidChart.xaml.cs
// *************************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2021. All rights reserved.
//  
// Web: http://www.scichart.com
//   Support: [email protected]
//   Sales:   [email protected]
// 
// CreateRealTime3DGeoidChart.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.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using SciChart.Charting3D.Model;
using SciChart.Data.Model;

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

        public CreateRealTime3DGeoidChart()
        {
            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;

            int countU, countV;
            switch (DataCombo.SelectedIndex)
            {
                case 0:
                    countU = countV = 10;
                    break;
                case 1:
                    countU = countV = 50;
                    break;
                case 2:
                    countU = countV = 100;
                    break;
                case 3:
                    countU = countV = 500;
                    break;
                case 4:
                    countU = countV = 1000;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            lock (_syncRoot)
            {
                OnStop();
            }

            BitmapImage bitmapImage = new BitmapImage();

            // Load image from resources
            bitmapImage.BeginInit();
            bitmapImage.CacheOption = BitmapCacheOption.OnDemand;
            bitmapImage.CreateOptions = BitmapCreateOptions.DelayCreation;
            bitmapImage.DecodePixelWidth = countU;
            bitmapImage.DecodePixelHeight = countV;
            bitmapImage.UriSource = new Uri("pack://application:,,,/SciChart.Examples.ExternalDependencies;component/Resources/Images/globe_heightmap.png");
            bitmapImage.EndInit();

            // Creating Geo height (displacement) map
            var geoHeightMap = new double[countU, countV];
            int nStride = (bitmapImage.PixelWidth * bitmapImage.Format.BitsPerPixel + 7) / 8;
            int bytsPerPixel = bitmapImage.Format.BitsPerPixel / 8;
            byte[] pixelByteArray = new byte[bitmapImage.PixelWidth * nStride];
            bitmapImage.CopyPixels(pixelByteArray, nStride, 0);
            for (int v = 0; v < countV; v++)
            {
                for (var u = 0; u < countU; u++)
                {
                    int pixelIndex = v * nStride + u * bytsPerPixel;
                    var offset = pixelByteArray[pixelIndex] / 255.0f;
                    geoHeightMap[v, u] = offset;
                }
            }

            var dataSeries = new EllipsoidDataSeries3D(countU, countV)
            {
                SeriesName = "Geo Mesh",
                A = 6,
                B = 6,
                C = 6
            };

            var frontBuffer = dataSeries.InternalArray;
            var backBuffer = new GridData(countU, countV).InternalArray;

            int frames = 0;
            _timer = new DispatcherTimer();
            _timer.Interval = TimeSpan.FromMilliseconds(20);
            _timer.Tick += (s, arg) =>
            {
                lock (_syncRoot)
                {
                    double heightOffsetsScale = sliderHeightOffsetsScale.Value;
                    double freq = (Math.Sin(frames++*0.1) + 1.0) / 2.0;

                    // Each set of geoHeightMap[i,j] schedules a redraw when the next Render event fires. Therefore, we suspend updates so that we can update the chart once
                    // We parallelize it by using Parallel.For for the outer loop
                    //  Equivalent of "for (int j = 0; j < countU; 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, countV, i =>
                    {
                        var buf = frontBuffer;
                        for (int j = 0; j < countU; j++)
                        {
                            // Rotate (offset) J index
                            int rj = j + frames;
                            if (rj >= countU)
                            {
                                rj -= countU * (rj / countU);
                            }

                            buf[i][j] = geoHeightMap[i, rj] + Math.Pow(geoHeightMap[i, rj], freq * 10.0) * heightOffsetsScale;
                        }
                    });

                    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;
            }
        }
    }
}