/// <summary>
 /// Reset the range to the empty state
 /// </summary>
 public virtual void Reset()
 {
     var oldRange = new Range(Min, Max);
     Minimum = double.NaN;
     Maximum = double.NaN;
     OnRangeChanged(oldRange);
 }
 public virtual void ForceUpdate(double min, double max)
 {
     var oldRange = new Range(Min, Max);
     if (!double.IsNaN(min) && !double.IsInfinity(min) && (double.IsNaN(Minimum) || (Math.Abs(Minimum - min) > double.Epsilon))) Minimum = min;
     if (!double.IsNaN(max) && !double.IsInfinity(max) && (double.IsNaN(Maximum) || (Math.Abs(Maximum - max) > double.Epsilon))) Maximum = max;
     OnRangeChanged(oldRange);
 }
 public RangeAnimation(Range from, Range to, Duration duration, FillBehavior fillBehavior = FillBehavior.Stop, IEasingFunction easingFunction = null)
 {
     From = from;
     To = to;
     Duration = duration;
     FillBehavior = fillBehavior;
     EasingFunction = easingFunction;
 }
 //[UsedImplicitly] PropertyObserver<DataAxisViewModel> _propertyObserver;
 public DataAxisViewModel()
 {
     DataRange = new RangeCollection();
     Label = "Axis";
     AxisTicks = new ObservableList<DataAxisTick>();
     AxisMarkers = new ObservableList<DataAxisTick>();
     VisibleRange = new Range(1, 2);
 }
 protected void OnRangeChanged(Range oldRange)
 {
     var handlers = RangeChanged;
     if (handlers == null) return;
     foreach (EventHandler<NotifyRangeChangedEventArgs> handler in handlers.GetInvocationList())
     {
         if (handler.Target is DispatcherObject)
         {
             var localHandler = handler;
             ((DispatcherObject)handler.Target).Dispatcher.InvokeIfRequired(() => localHandler(this, new NotifyRangeChangedEventArgs(oldRange)));
         }
         else handler(this, new NotifyRangeChangedEventArgs(oldRange));
     }
 }
 public virtual void Update(double min, double max)
 {
     var isChanged = false;
     var oldRange = new Range(Min, Max);
     if (!double.IsNaN(min) && !double.IsInfinity(min) && (double.IsNaN(Minimum) || (Math.Abs(Minimum - min) > double.Epsilon)))
     {
         Minimum = min;
         isChanged = true;
     }
     if (!double.IsNaN(max) && !double.IsInfinity(max) && (double.IsNaN(Maximum) || (Math.Abs(Maximum - max) > double.Epsilon)))
     {
         Maximum = max;
         isChanged = true;
     }
     if (isChanged) OnRangeChanged(oldRange);
 }
 public virtual void Clear()
 {
     var oldRange = new Range(this);
     Minimum = double.NaN;
     Maximum = double.NaN;
     OnRangeChanged(oldRange);
 }
 void FromPropertyChanged() { _from = new Range(From); }
        public RadialViewModel()
        {
            FullRange = new Range(0, 200);
            StatisticalRange = new Range(75, 125);
            AnimationTargetRange = new Range();
            AnimationTime = TimeSpan.FromSeconds(0);

            AxisSeriesViewModel.DataSeriesCollection.Add(_imageSeriesViewModel);
            AxisSeriesViewModel.DataSeriesCollection.Add(_bottomProfileViewModel);
            AxisSeriesViewModel.XAxis.Label = "Range (m)";
            AxisSeriesViewModel.XAxisTicks = null;
            AxisSeriesViewModel.YAxis.IsInverted = true;
            AxisSeriesViewModel.YAxis.Label = "Depth (m)";
            AxisSeriesViewModel.YAxisTicks = null;
            _fourAxisSeriesObserver = new PropertyObserver<FourAxisSeriesViewModel>(AxisSeriesViewModel)
                .RegisterHandler(a => a.IsMouseOver, UpdateStatusProperties);
            _xAxisPropertyObserver = new PropertyObserver<DataAxisViewModel>(AxisSeriesViewModel.XAxis)
                .RegisterHandler(x => x.MouseDataLocation, UpdateStatusProperties);
            _yAxisPropertyObserver = new PropertyObserver<DataAxisViewModel>(AxisSeriesViewModel.YAxis)
                .RegisterHandler(y => y.MouseDataLocation, UpdateStatusProperties);
            _instanceObservers.Add(Observable.FromEventPattern<PropertyChangedEventArgs>(AxisSeriesViewModel.YAxis, "PropertyChanged")
                                       .ObserveOn(TaskPoolScheduler.Default)
                                       .Where(e => e.EventArgs.PropertyName == "VisibleRange")
                                       .Select(e => AxisSeriesViewModel.YAxis.VisibleRange)
                                       .Throttle(TimeSpan.FromMilliseconds(100))
                                       .Subscribe(visibleRange =>
                                       {
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Entering VisibleRangeChanged", DateTime.Now));
                                           //Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Visible range on Y axis changed to {1}", DateTime.Now, visibleRange == null ? "(null)" : visibleRange.ToString()));
                                           if (visibleRange == null)
                                           {
                                               Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving (1) VisibleRangeChanged", DateTime.Now));
                                               return;
                                           }
                                           CalculateBottomProfileGeometry();
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving VisibleRangeChanged", DateTime.Now));
                                       }, e =>
                                       {
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: VisibleRangeChanged exception: {1}", DateTime.Now, e.Message));
                                           Debugger.Break();
                                       }));
            _instanceObservers.Add(Observable.FromEventPattern<PropertyChangedEventArgs>(this, "PropertyChanged")
                                       .ObserveOn(TaskPoolScheduler.Default)
                                       .Where(e => e.EventArgs.PropertyName == "AxisRange")
                                       .Select(e => AxisRange)
                                       .DistinctUntilChanged()
                                       .Subscribe(axisRange =>
                                       {
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Entering AxisRangeChanged", DateTime.Now));
                                           if (_axisRangeObserver != null)
                                           {
                                               _instanceObservers.Remove(_axisRangeObserver);
                                               _axisRangeObserver.Dispose();
                                           }
                                           if (axisRange == null)
                                           {
                                               Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving (1) AxisRangeChanged", DateTime.Now));
                                               return;
                                           }
                                           _axisRangeObserver = axisRange.ObserveOn(TaskPoolScheduler.Default).Subscribe(r =>
                                           {
                                               if (FullRange == null) FullRange = new Range(axisRange);
                                               else FullRange.Update(axisRange);
                                           });
                                           _instanceObservers.Add(_axisRangeObserver);
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving AxisRangeChanged", DateTime.Now));
                                       }, e =>
                                       {
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: AxisRangeChanged exception: {1}", DateTime.Now, e.Message));
                                           Debugger.Break();
                                       }));
            _instanceObservers.Add(Observable.FromEventPattern<PropertyChangedEventArgs>(AxisSeriesViewModel, "PropertyChanged")
                                       .ObserveOn(TaskPoolScheduler.Default)
                                       .Where(e => (e.EventArgs.PropertyName == "ActualWidth" || e.EventArgs.PropertyName == "ActualHeight"))
                                       .Select(e => new { AxisSeriesViewModel.ActualWidth, AxisSeriesViewModel.ActualHeight })
                                       .Sample(TimeSpan.FromMilliseconds(100))
                                       .DistinctUntilChanged()
                                       .Subscribe(e =>
                                       {
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Entering ActualHeightWidthChanged", DateTime.Now));
                                           Render();
                                           //CalculateBottomProfileGeometry();
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving ActualHeightWidthChanged", DateTime.Now));
                                       }, e =>
                                       {
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: ActualHeightWidthChanged exception: {1}", DateTime.Now, e.Message));
                                           Debugger.Break();
                                       }));
            _instanceObservers.Add(Observable.FromEventPattern<PropertyChangedEventArgs>(this, "PropertyChanged")
                                       .ObserveOn(TaskPoolScheduler.Default)
                                       .Where(e => e.EventArgs.PropertyName == "Radial")
                                       .Subscribe(e =>
                                       {
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Entering RadialChanged", DateTime.Now));
                                           _radialObservers.ForEach(o => o.Dispose());
                                           _radialObservers.Clear();
                                           WriteableBitmapVisibility = Visibility.Collapsed;
                                           if (Radial == null || !Radial.IsCalculated)
                                           {
                                               WriteableBitmapVisibility = Visibility.Collapsed;
                                               if (Radial == null) WaitToRenderText = "Please wait...";
                                               else
                                               {
                                                   if (!string.IsNullOrEmpty(Radial.Errors))
                                                   {
                                                       var sb = new StringBuilder();
                                                       sb.AppendLine("This radial was not calculated due to the following error(s):");
                                                       sb.AppendLine(Radial.Errors);
                                                       WaitToRenderText = sb.ToString();
                                                   }
                                                   else WaitToRenderText = "This radial has not yet been calculated";
                                               }
                                               Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving (1) RadialChanged", DateTime.Now));
                                               return;
                                           }
                                           try
                                           {
                                               _shadeFile = ShadeFile.Read(Radial.BasePath + ".shd", (float)Radial.Bearing);
                                               _shadeFile.BottomDepths = Radial.BottomDepths;
                                               _imageSeriesViewModel.Top = Radial.Depths.First();
                                               _imageSeriesViewModel.Left = Radial.Ranges.First();
                                               _imageSeriesViewModel.Bottom = Radial.Depths.Last();
                                               _imageSeriesViewModel.Right = Radial.Ranges.Last();
                                               StatisticalRange.Update(_shadeFile.StatMin, _shadeFile.StatMax);
                                               ColorMapViewModel.Range.Update(StatisticalRange);
                                               AxisSeriesViewModel.XAxis.DataRange.Update(_imageSeriesViewModel.Left, _imageSeriesViewModel.Right);
                                               AxisSeriesViewModel.YAxis.DataRange.Update(_imageSeriesViewModel.Top, _imageSeriesViewModel.Bottom);
                                               _radialObservers.Add(ColorMapViewModel.Range.ObserveOn(TaskPoolScheduler.Default).Sample(TimeSpan.FromMilliseconds(50)).Subscribe(r => Render()));
                                               Render();
                                               CalculateBottomProfileGeometry();
                                           }
                                           catch (Exception ex)
                                           {
                                               WriteableBitmapVisibility = Visibility.Collapsed;
                                               WaitToRenderText = ex.Message;
                                               _shadeFile = null;
                                           }
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving RadialChanged", DateTime.Now));
                                       }, e =>
                                       {
                                           Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: RadialChanged exception: {1}", DateTime.Now, e.Message));
                                           Debugger.Break();
                                       }));
            _instanceObservers.Add(
                ColorMapViewModel.Range.ObserveOn(TaskPoolScheduler.Default).Subscribe(
                    r =>
                    {
                        Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Entering ColorMapViewModel.RangeChanged", DateTime.Now));
                        _dispatcher.InvokeInBackgroundIfRequired(() =>
                        {
                            AxisMarkers.Clear();
                            if (ColorMapViewModel.Range != FullRange)
                            {
                                AxisMarkers.Add(new DataAxisTick(ColorMapViewModel.Range.Min, string.Format("{0:0}", ColorMapViewModel.Range.Min), true, Brushes.White, Brushes.Black, Brushes.Black));
                                AxisMarkers.Add(new DataAxisTick(ColorMapViewModel.Range.Max, string.Format("{0:0}", ColorMapViewModel.Range.Max), true, Brushes.White, Brushes.Black, Brushes.Black));
                            }
                            Debug.WriteLine("{0:HH:mm:ss.fff} ColorMapViewModel.Range changed to {1}",
                                            DateTime.Now,
                                            r);
                        });
                        Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving ColorMapViewModel.RangeChanged", DateTime.Now));
                    }, e =>
                    {
                        Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: ColorMapViewModel.RangeChanged exception: {1}", DateTime.Now, e.Message));
                        Debugger.Break();
                    }));
            _displayQueue
                .ObserveOn(TaskPoolScheduler.Default)
                .Subscribe(result =>
                {
                    Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Entering DisplayQueue", DateTime.Now));
                    _dispatcher.InvokeInBackgroundIfRequired(() =>
                    {
                        var pixelBuffer = result.Item1;
                        var renderRect = result.Item2;
                        var sequenceNumber = result.Item3;
                        //Debug.WriteLine(string.Format("Got completed event for sequence number {0} completed", sequenceNumber));
                        if (sequenceNumber < _displayedSequenceNumber)
                        {
                            Debug.WriteLine("Discarding image buffer: Old image sequence");
                            Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving (1) DisplayQueue", DateTime.Now));
                            return;
                        }
                        if (WriteableBitmap == null || Math.Abs(renderRect.Width - WriteableBitmap.Width) > double.Epsilon || Math.Abs(renderRect.Height - WriteableBitmap.Height) > double.Epsilon) WriteableBitmap = new WriteableBitmap(renderRect.Width, renderRect.Height, 96, 96, PixelFormats.Bgr32, null);
                        _displayedSequenceNumber = sequenceNumber;
                        WriteableBitmap.Lock();
                        WriteableBitmap.WritePixels(renderRect, pixelBuffer, WriteableBitmap.BackBufferStride, 0, 0);
                        WriteableBitmap.AddDirtyRect(renderRect);
                        OnPropertyChanged("WriteableBitmap");
                        WriteableBitmap.Unlock();
                        WriteableBitmapVisibility = Visibility.Visible;
                        _imageSeriesViewModel.ImageSource = WriteableBitmap;
                    });
                    Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: Leaving DisplayQueue", DateTime.Now));
                }, e =>
                {
                    Debug.WriteLine(string.Format("{0:HH:mm:ss.fff} RadialViewModel: DisplayQueue exception: {1}", DateTime.Now, e.Message));
                    Debugger.Break();
                });
        }
 GeneralTransform CreateAxisTransform(Range visbleRange, Size newSize, bool includeSwapTransform, bool isInverted, bool isLogarithmic)
 {
     double tickDirectionScale;
     double originScale;
     double axisDirectionTranslation;
     double tickDirectionTranslation;
     double axisLength;
     switch (AxisLocation)
     {
         case AxisLocation.Top:
             tickDirectionScale = -1.0;
             originScale = 1.0;
             axisDirectionTranslation = isInverted ? newSize.Width - (StrokeWeight / 2): StrokeWeight / 2;
             tickDirectionTranslation = newSize.Height - (StrokeWeight / 2);
             axisLength = newSize.Width;
             break;
         case AxisLocation.Bottom:
             tickDirectionScale = 1.0;
             originScale = 1.0;
             axisDirectionTranslation = isInverted ? newSize.Width - (StrokeWeight / 2): StrokeWeight / 2;
             tickDirectionTranslation = StrokeWeight / 2;
             axisLength = newSize.Width;
             break;
         case AxisLocation.Left:
             tickDirectionScale = -1.0;
             originScale = -1.0;
             axisDirectionTranslation = isInverted ? StrokeWeight / 2 : newSize.Height - (StrokeWeight / 2);
             tickDirectionTranslation = newSize.Width - (StrokeWeight / 2);
             axisLength = newSize.Height;
             break;
         case AxisLocation.Right:
             tickDirectionScale = 1.0;
             originScale = -1.0;
             axisDirectionTranslation = isInverted ? StrokeWeight / 2 : newSize.Height - (StrokeWeight / 2);
             tickDirectionTranslation = StrokeWeight / 2;
             axisLength = newSize.Height;
             break;
         default:
             throw new ApplicationException("DataAxis: Unknown AxisLocation value.");
     }
     if (isInverted) originScale *= -1;
     // The intent of this transform is to make every axis draw the same as a Bottom axis (i.e. the StartValue is at 
     // transformed-X of 0, and the axis line is drawn from top left to top right, axis ticks from top to tickLength)
     var result = new GeneralTransformGroup();
     // If this is a logarithmic axis, take the log of the data point.
     // If the data point is zero or negative, an exception will be thrown
     if (isLogarithmic) result.Children.Add(new LogTransform(10));
     // Reduce the data point by the minimum visible value, which normalizes the data with respect to the axis origin
     result.Children.Add(new TranslateTransform(-visbleRange.Min, 0));
     // Scale the data by an amount that will place the maximum visible value at the end of the axis
     // Takes axis direction (and partially axis inversion) into account with originScale, which is set according to those parameters
     result.Children.Add(new ScaleTransform(originScale * ((axisLength - StrokeWeight) / visbleRange.Size), tickDirectionScale));
     // Shift the result so that the data point is now located at the point along the axis that corresponds to the original data value
     result.Children.Add(new TranslateTransform(axisDirectionTranslation, tickDirectionTranslation));
     // if the includeSwapTransform parameter is true, then swap the X and Y values produced if this is a Left or Right axis
     if (includeSwapTransform && AxisLocation == AxisLocation.Left || AxisLocation == AxisLocation.Right) result.Children.Add(new SwapTransform());
     return result;
 }