/// <summary>
        /// Initializes a new instance of the SpectrumPlotViewModel class.
        /// </summary>
        /// <param name="dialogService">Dialog service for opening dialogs from ViewModel.</param>
        /// <param name="fragSeqVm">Gets or sets the view model for the fragmentation sequence (fragment ion generator)</param>
        /// <param name="multiplier">How much padding should be before the lowest peak and after the highest peak?</param>
        /// <param name="autoZoomXAxis">Should this view model automatically zoom the plot?</param>
        public SpectrumPlotViewModel(IMainDialogService dialogService, IFragmentationSequenceViewModel fragSeqVm, double multiplier, bool autoZoomXAxis = true)
        {
            this.dialogService             = dialogService;
            FragmentationSequenceViewModel = fragSeqVm;
            this.autoZoomXAxis             = autoZoomXAxis;
            errorMapViewModel        = new ErrorMapViewModel(dialogService);
            ShowUnexplainedPeaks     = true;
            ShowFilteredSpectrum     = false;
            ShowDeconvolutedSpectrum = false;
            AutoAdjustYAxis          = true;
            Title = string.Empty;
            XAxis = new LinearAxis
            {
                Title        = "m/z",
                StringFormat = "0.###",
                Position     = AxisPosition.Bottom,
            };
            PlotModel = new AutoAdjustedYPlotModel(XAxis, multiplier)
            {
                IsLegendVisible = false,
                YAxis           =
                {
                    Title        = "Intensity",
                    StringFormat = "0e0"
                }
            };

            SequenceViewerViewModel = new SequenceViewerViewModel();

            ions = new LabeledIonViewModel[0];

            // When Spectrum updates, clear the filtered spectrum, deconvoluted spectrum, and filtered+deconvoluted spectrum
            this.WhenAnyValue(x => x.Spectrum)
            .Subscribe(spectrum =>
            {
                spectrumDirty                = true;
                filteredSpectrum             = null;
                deconvolutedSpectrum         = null;
                filteredDeconvolutedSpectrum = null;
            });

            // If deconvolution option has changed, the X Axis should change.
            this.WhenAnyValue(x => x.ShowDeconvolutedSpectrum)
            .Subscribe(_ => spectrumDirty = true);

            // When Spectrum or ions change, or deconvoluted or filtered spectrum are selected, update spectrum plot
            this.WhenAnyValue(
                x => x.Spectrum,
                x => x.FragmentationSequenceViewModel.LabeledIonViewModels,
                x => x.ShowDeconvolutedSpectrum,
                x => x.ShowDeconvolutedIons,
                x => x.ShowFilteredSpectrum,
                x => x.ShowUnexplainedPeaks,
                x => x.ShowOnlyTop20Peaks)
            .Where(x => x.Item1 != null && x.Item2 != null)
            .Throttle(TimeSpan.FromMilliseconds(400), RxApp.TaskpoolScheduler)
            .SelectMany(async x =>
            {
                var vms = await FragmentationSequenceViewModel.GetLabeledIonViewModels();
                return(await
                       Task.WhenAll(
                           vms.Select(
                               ion =>
                               ion.GetPeaksAsync(GetSpectrum(),
                                                 ShowDeconvolutedSpectrum || ShowDeconvolutedIons))));
            })
            .Subscribe(dataPoints =>
            {
                ions = FragmentationSequenceViewModel.LabeledIonViewModels;
                SetTerminalResidues(dataPoints);
                UpdatePlotModel(dataPoints);

                if (FragmentationSequenceViewModel is FragmentationSequenceViewModel model)
                {
                    SequenceViewerViewModel.FragmentationSequence = model;
                    SequenceViewerViewModel.SelectedSpectrum      = Spectrum as ProductSpectrum;
                }
            });           // Update plot when data changes

            this.WhenAnyValue(x => x.Spectrum).Where(spectrum => spectrum == null).Subscribe(
                _ =>
            {
                PlotModel.Annotations.Clear();
                PlotModel.ClearSeries();
                PlotModel.InvalidatePlot(true);
            });

            // Update ions when relative intensity threshold changes.
            IcParameters.Instance.WhenAnyValue(x => x.PrecursorRelativeIntensityThreshold).Subscribe(precursorRelInt =>
            {
                if (FragmentationSequenceViewModel is PrecursorSequenceIonViewModel precursorFragVm)
                {
                    precursorFragVm.RelativeIntensityThreshold = precursorRelInt;
                }
            });

            // Update plot when settings change
            IcParameters.Instance.WhenAnyValue(x => x.ProductIonTolerancePpm, x => x.IonCorrelationThreshold)
            .Throttle(TimeSpan.FromMilliseconds(400), RxApp.TaskpoolScheduler)
            .SelectMany(async x => await Task.WhenAll(ions.Select(ion => ion.GetPeaksAsync(GetSpectrum(), ShowDeconvolutedSpectrum, false))))
            .Subscribe(UpdatePlotModel);

            // When AutoAdjustYAxis changes, update value in plot model.
            this.WhenAnyValue(x => x.AutoAdjustYAxis)
            .Subscribe(autoAdjust =>
            {
                PlotModel.AutoAdjustYAxis     = autoAdjust;
                PlotModel.YAxis.IsZoomEnabled = !autoAdjust;
                PlotModel.YAxis.IsPanEnabled  = !autoAdjust;

                if (autoAdjust)
                {
                    PlotModel.XAxis.Reset();
                    PlotModel.YAxis.Reset();
                }
            });

            // Update plot axes when FeaturePlotXMin, YMin, XMax, and YMax change
            this.WhenAnyValue(x => x.XMinimum, x => x.XMaximum)
            .Throttle(TimeSpan.FromSeconds(1), RxApp.TaskpoolScheduler)
            .Where(x => !xAxis.ActualMinimum.Equals(x.Item1) || !xAxis.ActualMaximum.Equals(x.Item2))
            .Subscribe(x =>
            {
                xAxis.Zoom(x.Item1, x.Item2);
                PlotModel.InvalidatePlot(false);
            });
            this.WhenAnyValue(y => y.YMinimum, y => y.YMaximum)
            .Throttle(TimeSpan.FromSeconds(1), RxApp.TaskpoolScheduler)
            .Where(y => !PlotModel.YAxis.ActualMinimum.Equals(y.Item1) || !PlotModel.YAxis.ActualMaximum.Equals(y.Item2))
            .Subscribe(
                y =>
            {
                PlotModel.YAxis.Zoom(y.Item1, y.Item2);
                PlotModel.InvalidatePlot(false);
            });

            // Update X min and max properties when x axis is panned or zoomed
            xAxis.AxisChanged += (o, e) =>
            {
                XMinimum = Math.Round(xAxis.ActualMinimum, 3);
                XMaximum = Math.Round(xAxis.ActualMaximum, 3);
            };

            // Update Y min and max properties when Y axis is panned or zoomed
            PlotModel.YAxis.AxisChanged += (o, e) =>
            {
                YMinimum = Math.Round(PlotModel.YAxis.ActualMinimum, 3);
                YMaximum = Math.Round(PlotModel.YAxis.ActualMaximum, 3);
            };

            // Save As Image Command requests a file path from the user and then saves the spectrum plot as an image
            SaveAsImageCommand = ReactiveCommand.Create(SaveAsImageImplementation);

            // Error map command opens a new error map window and passes it the most abundant isotope peak data points
            // and the current sequence.
            OpenErrorMapCommand      = ReactiveCommand.Create(() => dialogService.OpenErrorMapWindow(errorMapViewModel));
            OpenScanSelectionCommand = ReactiveCommand.Create(OpenScanSelectionImplementation);
            SaveAsTsvCommand         = ReactiveCommand.Create(SaveAsTsvImplementation);
            SaveToClipboardCommand   = ReactiveCommand.Create(SaveToClipboardImplementation);
        }
Пример #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="XicPlotViewModel"/> class.
        /// </summary>
        /// <param name="dialogService">Dialog service </param>
        /// <param name="fragSeqVm">The view model for the fragmentation sequence (fragment ion generator)</param>
        /// <param name="lcms">LCMS run data set for this XIC plot. </param>
        /// <param name="title">Title of XIC plot </param>
        /// <param name="xaxis">XAxis for XIC plot. </param>
        /// <param name="showLegend">Should a legend be shown on the plot by default? </param>
        public XicPlotViewModel(IDialogService dialogService, IFragmentationSequenceViewModel fragSeqVm, ILcMsRun lcms, string title, LinearAxis xaxis, bool showLegend = true, AxisPosition vertAxes = AxisPosition.Left)
        {
            this.dialogService = dialogService;
            this.FragmentationSequenceViewModel = fragSeqVm;
            this.lcms           = lcms;
            this.showLegend     = showLegend;
            this.xaxis          = xaxis;
            this.pointsToSmooth = IcParameters.Instance.PointsToSmooth;
            this.PlotTitle      = title;
            this.PlotModel      = new SelectablePlotModel(xaxis, 1.05)
            {
                YAxis =
                {
                    Title        = "Intensity",
                    StringFormat = "0e0",
                    Position     = vertAxes
                }
            };

            this.ions = new LabeledIonViewModel[0];

            var retentionTimeSelectedCommand = ReactiveCommand.Create();

            retentionTimeSelectedCommand
            .Select(_ => this.PlotModel.SelectedDataPoint as XicDataPoint)
            .Where(dp => dp != null)
            .Subscribe(dp => this.SelectedScan = dp.ScanNum);
            this.RetentionTimeSelectedCommand  = retentionTimeSelectedCommand;

            var saveAsImageCommand = ReactiveCommand.Create();

            saveAsImageCommand.Subscribe(_ => this.SaveAsImage());
            this.SaveAsImageCommand = saveAsImageCommand;

            // When ShowLegend is updated, IsLegendVisible on the plot should be updated
            this.WhenAnyValue(x => x.ShowLegend).Subscribe(v =>
            {
                this.PlotModel.IsLegendVisible = v;
                this.PlotModel.InvalidatePlot(true);
            });

            // When area updates, plot title should update
            this.WhenAnyValue(x => x.Area).Subscribe(area =>
            {
                var areaStr    = string.Format(CultureInfo.InvariantCulture, "{0:0.##E0}", area);
                this.PlotTitle = string.Format("{0} (Area: {1})", title, areaStr);
            });

            // Update area when x Axis is zoomed/panned
            this.xaxis.AxisChanged += async(o, e) =>
            {
                this.Area = await this.GetCurrentAreaAsync();
            };

            // Update point marker when selected scan changes
            this.WhenAnyValue(x => x.SelectedScan, x => x.IsPlotUpdating)
            .Where(x => x.Item2)
            .Throttle(TimeSpan.FromMilliseconds(50), RxApp.TaskpoolScheduler)
            .Subscribe(x => this.PlotModel.SetPrimaryPointMarker(this.lcms.GetElutionTime(x.Item1)));

            // When point markers are toggled, change the marker type on each series
            this.WhenAnyValue(x => x.ShowPointMarkers)
            .Select(showPointMarkers => showPointMarkers ? MarkerType.Circle : MarkerType.None)
            .Subscribe(
                markerType =>
            {
                foreach (var lineSeries in this.PlotModel.Series.OfType <LineSeries>())
                {
                    lineSeries.MarkerType = markerType;
                }

                this.PlotModel.InvalidatePlot(true);
            });

            this.WhenAnyValue(
                x => x.FragmentationSequenceViewModel.LabeledIonViewModels,
                x => x.PointsToSmooth,
                x => x.IsPlotUpdating)
            .Where(x => x.Item3 && x.Item1 != null)
            .SelectMany(async x => await this.GetXicDataPointsAsync(x.Item1, this.PointsToSmooth))
            .Subscribe(xicPoints =>
            {
                this.ions = this.FragmentationSequenceViewModel.LabeledIonViewModels;
                this.UpdatePlotModel(xicPoints);
            });

            // Update ions when relative intensity threshold changes.
            IcParameters.Instance.WhenAnyValue(x => x.PrecursorRelativeIntensityThreshold).Subscribe(precRelInt =>
            {
                var precFragVm = this.FragmentationSequenceViewModel as PrecursorSequenceIonViewModel;
                if (precFragVm != null)
                {
                    precFragVm.RelativeIntensityThreshold = precRelInt;
                }
            });

            // Update plot when settings change
            IcParameters.Instance.WhenAnyValue(x => x.ProductIonTolerancePpm)
            .Where(_ => this.ions != null)
            .Throttle(TimeSpan.FromMilliseconds(250), RxApp.TaskpoolScheduler)
            .SelectMany(async x => await this.GetXicDataPointsAsync(this.ions, this.PointsToSmooth, false))
            .Subscribe(this.UpdatePlotModel);
        }