From c787a3c967155c3a4f0cebaa26454e260778f588 Mon Sep 17 00:00:00 2001 From: Dongcheng Lin Date: Mon, 2 Nov 2026 21:52:59 +0900 Subject: [PATCH] fix(xrd): follow axis changes from set_xrd and schemes --- .../core/src/actions/app_impl/processing.rs | 2 +- .../actions/app_impl/processing_snapshot.rs | 7 ++ crates/core/src/state/scientific_actions.rs | 1 + crates/core/src/state/xrd_axis.rs | 13 ++- crates/core/src/state/xrd_axis_tests.rs | 79 +++++++++++++++++++ 5 files changed, 99 insertions(+), 3 deletions(-) diff --git a/crates/core/src/actions/app_impl/processing.rs b/crates/core/src/actions/app_impl/processing.rs index db6cf202..129eff44 100644 --- a/crates/core/src/actions/app_impl/processing.rs +++ b/crates/core/src/actions/app_impl/processing.rs @@ -96,7 +96,7 @@ impl PlotiumApp { return Err(format!("Dataset index {dataset} is no longer available.")); }; validate_processing_state(current, state)?; - let refit = self.follow_xrd_axis(dataset, state); + let refit = self.follow_xrd_axis(dataset, state, &[]); if let ( Some(Dataset::Nmr2D(current)), DatasetProcessingState::Nmr2D { diff --git a/crates/core/src/actions/app_impl/processing_snapshot.rs b/crates/core/src/actions/app_impl/processing_snapshot.rs index bdf4b17e..784f363d 100644 --- a/crates/core/src/actions/app_impl/processing_snapshot.rs +++ b/crates/core/src/actions/app_impl/processing_snapshot.rs @@ -22,6 +22,7 @@ impl PlotiumApp { }); } } + let mut refit = Vec::new(); for snapshot in snapshots { let index = self .doc @@ -46,6 +47,11 @@ impl PlotiumApp { ] { self.session.compute.cancel(snapshot.resource_id(), kind); } + refit.extend(self.follow_xrd_axis( + index, + &DatasetProcessingState::from_dataset(&next), + snapshots, + )); self.doc.datasets[index] = next; } for source in versions { @@ -56,6 +62,7 @@ impl PlotiumApp { self.rebuild_canvases_for(index); } } + self.refit_xrd_views(refit); Ok(()) } } diff --git a/crates/core/src/state/scientific_actions.rs b/crates/core/src/state/scientific_actions.rs index 5f55644a..bd462841 100644 --- a/crates/core/src/state/scientific_actions.rs +++ b/crates/core/src/state/scientific_actions.rs @@ -131,6 +131,7 @@ impl PlotiumApp { self.session.ui.data_selection.clone(), self.session.view, ); + self.finish_axis_overrides_edit(); let outcome = self.try_execute_action(action); self.session.active_canvas = focus.0; self.session.ui.data_selection = focus.1; diff --git a/crates/core/src/state/xrd_axis.rs b/crates/core/src/state/xrd_axis.rs index 7164c3d9..895c0ae7 100644 --- a/crates/core/src/state/xrd_axis.rs +++ b/crates/core/src/state/xrd_axis.rs @@ -8,12 +8,14 @@ impl PlotiumApp { /// Carries manual x ranges onto the new axis, so a zoomed plot keeps showing /// the same reflections, and warns when a stack ends up with mixed axes. /// - /// Returns the plots whose x view must show the whole new range once rebuilt: - /// those that showed the whole old one, and views the new axis cannot hold. + /// `pending` holds datasets installed in the same step, which count at their + /// new axes. Returns the plots whose x view must show the whole new range once + /// rebuilt: those that showed the whole old one, and views the new axis cannot hold. pub(crate) fn follow_xrd_axis( &mut self, dataset: usize, state: &DatasetProcessingState, + pending: &[Dataset], ) -> Vec<(usize, ObjectId)> { let mut refit = Vec::new(); let (Some(Dataset::Xrd(xrd)), DatasetProcessingState::Xrd(next)) = @@ -34,6 +36,13 @@ impl PlotiumApp { .datasets .iter() .filter_map(Dataset::as_xrd) + .map(|other| { + pending + .iter() + .filter_map(Dataset::as_xrd) + .find(|next| next.resource_id == other.resource_id) + .unwrap_or(other) + }) .map(|other| (other.resource_id, other.params.axis)) .collect(); let other_axis = |series: &SeriesBinding| { diff --git a/crates/core/src/state/xrd_axis_tests.rs b/crates/core/src/state/xrd_axis_tests.rs index 1aff0b08..0d2a78d8 100644 --- a/crates/core/src/state/xrd_axis_tests.rs +++ b/crates/core/src/state/xrd_axis_tests.rs @@ -1,4 +1,5 @@ use super::*; +use crate::actions::Action; use crate::operation::OperationOutcome; use plotium_processing::xrd::XrdProcessing; @@ -278,3 +279,81 @@ fn undoing_an_axis_change_under_a_manual_x_range_keeps_the_peaks_in_view() { app.undo(); peaks_stay_in_view(&mut app, "undo"); } + +fn install_axis(app: &mut PlotiumApp, axis: XrdAxis) { + let before = app.doc.datasets.to_vec(); + let mut after = before.clone(); + state_on(axis).apply_to(&mut after[0]).unwrap(); + app.execute_action(Action::InstallScientificState { before, after }); +} + +#[test] +fn installed_processing_follows_the_axis_like_a_panel_edit() { + let mut app = PlotiumApp::new_with_settings(Default::default()); + app.doc.datasets.push(peaked("a.xy")); + assert!(app.insert_dataset_canvas(0)); + for axis in [XrdAxis::D, XrdAxis::CuKAlpha] { + install_axis(&mut app, axis); + peaks_stay_in_view(&mut app, &format!("{axis:?}")); + } + app.undo(); + peaks_stay_in_view(&mut app, "undo"); +} + +#[test] +fn installed_processing_carries_a_manual_range_onto_the_axis() { + let mut app = PlotiumApp::new_with_settings(Default::default()); + app.doc.datasets.push(peaked("a.xy")); + assert!(app.insert_dataset_canvas(0)); + let plot = |app: &mut PlotiumApp| { + app.doc.canvases[0] + .objects + .iter_mut() + .find_map(|o| o.plot_mut()) + .unwrap() + .axis_overrides + .x_range + }; + app.doc.canvases[0] + .objects + .iter_mut() + .find_map(|o| o.plot_mut()) + .unwrap() + .axis_overrides + .x_range = Some(AxisRange::new(8.0, 16.0)); + install_axis(&mut app, XrdAxis::Q); + let range = plot(&mut app).unwrap(); + let expected = XrdAxis::Q.position(8.0, 0.7).unwrap(); + assert!((range.min - expected).abs() < 1e-9, "{range:?}"); + app.undo(); + let range = plot(&mut app).unwrap(); + assert!((range.min - 8.0).abs() < 1e-9, "{range:?}"); + assert!((range.max - 16.0).abs() < 1e-9, "{range:?}"); +} + +#[test] +fn a_stack_installed_together_does_not_warn_about_mixed_axes() { + let mut app = PlotiumApp::new_with_settings(Default::default()); + app.doc.datasets.push(pattern("a.xy")); + app.doc.datasets.push(pattern("b.xy")); + app.session.ui.data_selection = vec![0, 1]; + app.stack_selected_data(); + let warnings = |app: &PlotiumApp| { + app.session + .operation_history + .operations() + .filter(|report| report.outcome == OperationOutcome::Warning) + .count() + }; + let before = app.doc.datasets.to_vec(); + let mut after = before.clone(); + for dataset in &mut after { + state_on(XrdAxis::Q).apply_to(dataset).unwrap(); + } + app.execute_action(Action::InstallScientificState { before, after }); + assert_eq!(warnings(&app), 0); + app.undo(); + assert_eq!(warnings(&app), 0); + app.redo(); + assert_eq!(warnings(&app), 0); +}