From f870c5bb5344e319b1d4512aabccdb72ef2bc832 Mon Sep 17 00:00:00 2001 From: Yuki Matsuzawa Date: Fri, 13 Mar 2026 14:26:45 +0900 Subject: [PATCH 1/4] Add support for dashed lines in LineSpectrumControlSlim Introduced the StrokeDashArray dependency property to allow customization of line dash patterns. Updated PenSelector and related logic to propagate and apply dash styles when rendering lines. Changes to LineBrush, LineThickness, or StrokeDashArray now correctly update the rendered output. Backward compatibility is maintained for existing usage. --- .../Chart/LineSpectrumControlSlim.cs | 31 +++++++++++++++++-- src/Common/ChartDrawing/Design/PenSelector.cs | 14 +++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/Common/ChartDrawing/Chart/LineSpectrumControlSlim.cs b/src/Common/ChartDrawing/Chart/LineSpectrumControlSlim.cs index 709e73ce4..5103b43c1 100644 --- a/src/Common/ChartDrawing/Chart/LineSpectrumControlSlim.cs +++ b/src/Common/ChartDrawing/Chart/LineSpectrumControlSlim.cs @@ -250,7 +250,7 @@ private static void OnLineBrushPropertyChanged(DependencyObject d, DependencyPro [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "")] private void OnLineBrushPropertyChanged(IBrushMapper oldValue, IBrushMapper newValue) { - Selector.Update(newValue, LineThickness); + Selector.Update(newValue, LineThickness, StrokeDashArray); } public static readonly DependencyProperty HuePropertyProperty = @@ -310,7 +310,32 @@ private static void OnLineThicknessChanged(DependencyObject d, DependencyPropert [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "")] private void OnLineThicknessChanged(double oldValue, double newValue) { - Selector.Update(LineBrush, newValue); + Selector.Update(LineBrush, newValue, StrokeDashArray); + } + + public static readonly DependencyProperty StrokeDashArrayProperty = + DependencyProperty.Register( + nameof(StrokeDashArray), + typeof(DoubleCollection), + typeof(LineSpectrumControlSlim), + new FrameworkPropertyMetadata( + null, + FrameworkPropertyMetadataOptions.AffectsRender, + OnStrokeDashArrayChanged)); + + public DoubleCollection StrokeDashArray { + get => (DoubleCollection)GetValue(StrokeDashArrayProperty); + set => SetValue(StrokeDashArrayProperty, value); + } + + private static void OnStrokeDashArrayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + var c = (LineSpectrumControlSlim)d; + c.OnStrokeDashArrayChanged((DoubleCollection)e.OldValue, (DoubleCollection)e.NewValue); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "")] + private void OnStrokeDashArrayChanged(DoubleCollection oldValue, DoubleCollection newValue) { + Selector.Update(LineBrush, LineThickness, newValue); } private PenSelector Selector { @@ -318,7 +343,7 @@ private PenSelector Selector { if (selector is null) { selector = new PenSelector(); if (!(LineBrush is null)) { - selector.Update(LineBrush, LineThickness); + selector.Update(LineBrush, LineThickness, StrokeDashArray); } } return selector; diff --git a/src/Common/ChartDrawing/Design/PenSelector.cs b/src/Common/ChartDrawing/Design/PenSelector.cs index 4d9cf500b..36ca2505f 100644 --- a/src/Common/ChartDrawing/Design/PenSelector.cs +++ b/src/Common/ChartDrawing/Design/PenSelector.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Windows.Media; using CompMs.Graphics.Base; @@ -31,11 +32,15 @@ public void Update(Pen pen) { } public void Update(IBrushMapper mapper, double thickness) { + Update(mapper, thickness, null); + } + + public void Update(IBrushMapper mapper, double thickness, DoubleCollection strokeDashArray) { if (mapper is null) { Reset(); } else { - strategy = new BrushMapStrategy(mapper, thickness); + strategy = new BrushMapStrategy(mapper, thickness, strokeDashArray); } } @@ -62,13 +67,15 @@ public Pen Get(object o) { class BrushMapStrategy : ISelectStrategy { - public BrushMapStrategy(IBrushMapper mapper, double thickness) { + public BrushMapStrategy(IBrushMapper mapper, double thickness, DoubleCollection strokeDashArray) { Mapper = mapper; Thickness = thickness; + StrokeDashArray = strokeDashArray?.ToArray(); } private readonly IBrushMapper Mapper; private readonly double Thickness; + private readonly double[] StrokeDashArray; private readonly Dictionary cache = new Dictionary(); public Pen Get(object o) { @@ -77,6 +84,9 @@ public Pen Get(object o) { } var brush = Mapper.Map(o); var pen = new Pen(brush, Thickness); + if (StrokeDashArray is { Length: > 0 }) { + pen.DashStyle = new DashStyle(new DoubleCollection(StrokeDashArray), 0d); + } pen.Freeze(); if (cache.Count > 1_000_000) { cache.Clear(); From 9226ae8810cc3066683c994bac20a3e57c69f147 Mon Sep 17 00:00:00 2001 From: Yuki Matsuzawa Date: Fri, 13 Mar 2026 14:29:48 +0900 Subject: [PATCH 2/4] Add raw spectrum overlay option to Q1Dec/Deconv views Users can now overlay the raw spectrum as a dashed, dark gray line on Q1Deconvolution and Deconvolution spectrum charts. A new "Raw (dashed)" checkbox in the UI toggles the overlay's visibility. The overlay does not show annotations and is visually distinct. Implementation includes new properties for controlling overlay visibility, line style, and annotation display, with updates to models, viewmodels, and XAML bindings to support the feature. # Conflicts: # src/MSDIAL5/MsdialGuiApp/Model/Chart/RawDecSpectrumsModel.cs --- .../Model/Chart/RawDecSpectrumsModel.cs | 23 +++++++++++++++++++ .../Model/Chart/SingleSpectrumModel.cs | 12 +++++++++- .../MsdialGuiApp/View/Chart/ExpRefView.xaml | 6 +++++ .../View/Chart/MsSpectrumView.xaml | 5 +++- .../Chart/RawDecSpectrumsViewModel.cs | 1 + .../Chart/SingleSpectrumViewModel.cs | 5 ++++ 6 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/MSDIAL5/MsdialGuiApp/Model/Chart/RawDecSpectrumsModel.cs b/src/MSDIAL5/MsdialGuiApp/Model/Chart/RawDecSpectrumsModel.cs index 6da62850c..6d846d466 100644 --- a/src/MSDIAL5/MsdialGuiApp/Model/Chart/RawDecSpectrumsModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/Model/Chart/RawDecSpectrumsModel.cs @@ -1,8 +1,11 @@ using CompMs.App.Msdial.Model.Loader; using CompMs.Common.Algorithm.Scoring; using CompMs.CommonMVVM; +using CompMs.Graphics.Design; +using Reactive.Bindings; using Reactive.Bindings.Extensions; using System; +using System.Windows.Media; namespace CompMs.App.Msdial.Model.Chart { @@ -37,17 +40,37 @@ public RawDecSpectrumsModel(SingleSpectrumModel rawSpectrumModel, SingleSpectrum HorizontalTitle = "m/z", VerticalTitle = "Relative abundance", }.AddTo(Disposables); + + IsRawSpectrumOverlayVisible = new ReactivePropertySlim(false).AddTo(Disposables); + + + var decRawOverlaySpectrumModel = rawSpectrumModel.Clone().AddTo(Disposables); + decRawOverlaySpectrumModel.IsVisible.Value = false; + decRawOverlaySpectrumModel.IsAnnotationVisible.Value = false; + decRawOverlaySpectrumModel.LineThickness.Value = 1d; + decRawOverlaySpectrumModel.StrokeDashArray.Value = new DoubleCollection { 3d, 3d }; + decRawOverlaySpectrumModel.Brush = new ConstantBrushMapper(Brushes.DarkGray); + DecRefSpectrumModels = new MsSpectrumModel(decSpectrumModel, referenceSpectrumModel, ms2ScanMatching) { GraphTitle = "Deconvolution vs. Reference", HorizontalTitle = "m/z", VerticalTitle = "Relative abundacne", }.AddTo(Disposables); + DecRefSpectrumModels.UpperSpectraModel.Insert(0, decRawOverlaySpectrumModel); + + IsRawSpectrumOverlayVisible + .Subscribe(isVisible => { + decRawOverlaySpectrumModel.IsVisible.Value = isVisible; + }) + .AddTo(Disposables); + RawLoader = loader; } public MsSpectrumModel RawRefSpectrumModels { get; } public MsSpectrumModel DecRefSpectrumModels { get; } + public ReactivePropertySlim IsRawSpectrumOverlayVisible { get; } public MultiMsmsRawSpectrumLoader? RawLoader { get; } } } diff --git a/src/MSDIAL5/MsdialGuiApp/Model/Chart/SingleSpectrumModel.cs b/src/MSDIAL5/MsdialGuiApp/Model/Chart/SingleSpectrumModel.cs index f7f07411f..41ce421d4 100644 --- a/src/MSDIAL5/MsdialGuiApp/Model/Chart/SingleSpectrumModel.cs +++ b/src/MSDIAL5/MsdialGuiApp/Model/Chart/SingleSpectrumModel.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Reactive.Linq; +using System.Windows.Media; namespace CompMs.App.Msdial.Model.Chart { @@ -24,10 +25,13 @@ public SingleSpectrumModel(ObservableMsSpectrum observableMsSpectrum, AxisProper HorizontalProperty = HorizontalPropertySelectors.GetSelector(typeof(SpectrumPeak))?.Property ?? throw new Exception("Valid PropertySelector is not found."); VerticalProperty = VerticalPropertySelectors.GetSelector(typeof(SpectrumPeak))?.Property ?? throw new Exception("Valid PropertySelector is not found."); _hueItem = hueItem; + Brush = hueItem.Brush; Labels = graphLabels; IsVisible = new ReactivePropertySlim(true).AddTo(Disposables); + IsAnnotationVisible = new ReactivePropertySlim(true).AddTo(Disposables); LineThickness = new ReactivePropertySlim(2d).AddTo(Disposables); + StrokeDashArray = new ReactivePropertySlim(null).AddTo(Disposables); } public IObservable MsSpectrum => _observableMsSpectrum.MsSpectrum; @@ -38,12 +42,14 @@ public SingleSpectrumModel(ObservableMsSpectrum observableMsSpectrum, AxisProper public IObservable> VerticalAxis => VerticalPropertySelectors.AxisItemSelector.GetAxisItemAsObservable().SkipNull().Select(item => item.AxisManager); public AxisItemSelector VerticalAxisItemSelector => VerticalPropertySelectors.AxisItemSelector; public string VerticalProperty { get; } - public IBrushMapper Brush => _hueItem.Brush; + public IBrushMapper Brush { get; set; } public string HueProperty => _hueItem.Property; public GraphLabels Labels { get; } public ReadOnlyReactivePropertySlim SpectrumLoaded => _observableMsSpectrum.Loaded; public ReactivePropertySlim IsVisible { get; } + public ReactivePropertySlim IsAnnotationVisible { get; } public ReactivePropertySlim LineThickness { get; } + public ReactivePropertySlim StrokeDashArray { get; } public IObservable CanSave => _observableMsSpectrum.CanSave; @@ -81,5 +87,9 @@ private SingleSpectrumModel CreateFromMsSpectrum(ObservableMsSpectrum msSpectrum spectrumModel.Disposables.Add(msSpectrum); return spectrumModel; } + + public SingleSpectrumModel Clone() { + return new SingleSpectrumModel(_observableMsSpectrum, HorizontalPropertySelectors, VerticalPropertySelectors, _hueItem, Labels); + } } } diff --git a/src/MSDIAL5/MsdialGuiApp/View/Chart/ExpRefView.xaml b/src/MSDIAL5/MsdialGuiApp/View/Chart/ExpRefView.xaml index 6aa1466e4..9bd150aac 100644 --- a/src/MSDIAL5/MsdialGuiApp/View/Chart/ExpRefView.xaml +++ b/src/MSDIAL5/MsdialGuiApp/View/Chart/ExpRefView.xaml @@ -56,6 +56,12 @@ ToolTip="Show deconvoluted spectra vs. reference spectra" Style="{StaticResource IconRadioButton}"/> + +