Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ publish = false
repository = "https://github.com/nmrtist/plotx"

[workspace.dependencies]
nmr = "=0.1.0"
colorous = "1"
num-complex = "0.4"
nalgebra = { version = "0.35", default-features = false, features = ["std"] }
rustfft = "6.4"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = { version = "1.0", features = ["float_roundtrip"] }
semver = "1"
Expand Down Expand Up @@ -123,7 +123,10 @@ opt-level = 3
[profile.dev.package.plotx-processing]
opt-level = 3

# rustfft is the only third-party dependency that needs optimized debug code.
[profile.dev.package.nmr]
opt-level = 3

# Optimize the shared FFT backend as well as its callers.
[profile.dev.package.rustfft]
opt-level = 2

Expand Down
1 change: 1 addition & 0 deletions crates/app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ default = []
datafusion = ["plotx-core/datafusion"]

[dependencies]
nmr.workspace = true
plotx-core.workspace = true
plotx-analysis.workspace = true
plotx-io.workspace = true
Expand Down
9 changes: 6 additions & 3 deletions crates/app/src/shot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,9 @@ fn setup(app: &mut PlotxApp) {
let data = synthetic_fid();
let action = Action::insert_dataset_with_default_canvas(
app,
Dataset::Nmr(Box::new(NmrDataset::load(data))),
Dataset::Nmr(Box::new(
NmrDataset::load(data).expect("the synthetic FID is valid"),
)),
"Canvas 1 — synthetic".to_owned(),
DEFAULT_CANVAS_SIZE_MM,
);
Expand Down Expand Up @@ -553,7 +555,7 @@ fn line_fit(app: &mut PlotxApp, ctx: &egui::Context) -> Result<(), String> {

fn symmetry_setup(app: &mut PlotxApp) -> Result<(), String> {
*app = PlotxApp::new_with_settings(Settings::default());
let mut dataset = Nmr2DDataset::load(synthetic_cosy());
let mut dataset = Nmr2DDataset::load(synthetic_cosy()).expect("valid synthetic 2D acquisition");
let ids = dataset
.peaks
.add_pair(
Expand Down Expand Up @@ -587,7 +589,8 @@ fn symmetry_setup(app: &mut PlotxApp) -> Result<(), String> {

fn region_result(app: &mut PlotxApp) {
*app = PlotxApp::new_with_settings(Settings::default());
let mut dataset = Nmr2DDataset::load(synthetic_series());
let mut dataset =
Nmr2DDataset::load(synthetic_series()).expect("valid synthetic 2D acquisition");
dataset.region_analysis.regions.push(Region {
id: RegionId::new(0),
lo: 4.65,
Expand Down
5 changes: 3 additions & 2 deletions crates/app/src/shot/craft_shot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ pub(super) fn setup(app: &mut PlotxApp, ctx: &egui::Context) -> Result<(), Strin
.clone();
let mut params = CraftParams::conventional();
params.maximum_model_order = 8;
let invocation = CraftInvocation::acquisition(&data, params);
let result = process_craft_cancellable(&data, &invocation, &|| false)
let fid = data.craft_fid().map_err(|error| error.to_string())?;
let invocation = CraftInvocation::acquisition(&fid, params);
let result = process_craft_cancellable(&fid, &invocation, &|| false)
.map_err(|error| format!("CRAFT screenshot analysis failed: {error}"))?;
let nmr = app.doc.datasets[0]
.as_nmr_mut()
Expand Down
11 changes: 8 additions & 3 deletions crates/app/src/ui/canvas/craft_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ pub(crate) fn handle_craft_region_drag(
}
let acquired_bounds = nmr.spectrum().unwrap().ppm_bounds();
let dataset_id = nmr.resource_id;
let observe_freq = nmr.data.observe_freq_mhz.max(f64::MIN_POSITIVE);
let point_step =
nmr.data.spectral_width_hz.abs() / observe_freq / nmr.data.points.len().max(1) as f64;
let Some(reference) = nmr.craft_reference() else {
return;
};
let Some(width) = nmr.data.axes()[0].spectral_width_hz else {
return;
};
let observe_freq = reference.reference_frequency_mhz;
let point_step = width / observe_freq / nmr.data.len() as f64;
let suggestions = app
.session
.ui
Expand Down
9 changes: 5 additions & 4 deletions crates/app/src/ui/canvas/craft_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ pub(crate) fn handle_and_paint_craft_result(
dataset,
run,
stored,
nmr,
plot,
figure,
painter,
Expand Down Expand Up @@ -119,7 +118,6 @@ struct CraftRangePaintContext<'a> {
dataset: plotx_core::state::DatasetId,
run: plotx_core::state::CraftRunId,
stored: &'a plotx_core::state::StoredCraftRun,
nmr: &'a plotx_core::state::NmrDataset,
plot: PlotRect,
figure: &'a plotx_figure::Figure,
painter: &'a egui::Painter,
Expand All @@ -132,7 +130,6 @@ fn paint_craft_ranges(context: CraftRangePaintContext<'_>) {
dataset,
run,
stored,
nmr,
plot,
figure,
painter,
Expand All @@ -143,7 +140,11 @@ fn paint_craft_ranges(context: CraftRangePaintContext<'_>) {
.invocation
.reference
.effective_carrier_ppm();
let observe = nmr.data.observe_freq_mhz;
let observe = stored
.provenance
.invocation
.reference
.reference_frequency_mhz;
let modeling = stored
.diagnostics
.modeling_windows
Expand Down
71 changes: 52 additions & 19 deletions crates/app/src/ui/canvas/cursors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,13 +362,29 @@ fn inspect_text(app: &PlotxApp, dataset: usize, point: CursorPoint) -> String {
return String::new();
};
match data {
Dataset::Nmr(_) => format!("x {:.4} ppm · I {}", point.x, fmt_number(point.intensity)),
Dataset::Nmr2D(_) => format!(
"F2 {:.4} ppm · F1 {:.4} ppm · I {}",
point.x,
point.y.unwrap_or_default(),
fmt_number(point.intensity),
),
Dataset::Nmr(n) => {
let unit = n
.spectrum()
.map_or("s", |s| plotx_processing::axis_unit_label(Some(s.unit)));
format!(
"x {:.4} {unit} · I {}",
point.x,
fmt_number(point.intensity)
)
}
Dataset::Nmr2D(n) => {
let Processed2D::Ft(s) = &n.processed else {
return String::new();
};
format!(
"F2 {:.4} {} · F1 {:.4} {} · I {}",
point.x,
s.direct.unit_label(),
point.y.unwrap_or_default(),
s.indirect.unit_label(),
fmt_number(point.intensity)
)
}
_ => String::new(),
}
}
Expand All @@ -380,24 +396,41 @@ fn delta_text(app: &PlotxApp, dataset: usize, delta: CursorDelta) -> String {
let dx = delta.second.x - delta.first.x;
let di = delta.second.intensity - delta.first.intensity;
match data {
Dataset::Nmr(nmr) => format!(
"Δx {} ppm ({} Hz) · ΔI {}",
fmt_delta(dx),
fmt_delta(dx * nmr.data.observe_freq_mhz),
fmt_number(di),
),
Dataset::Nmr(nmr) => {
if let Some(spectrum) = nmr.spectrum() {
if spectrum.unit == nmr::axis::AxisUnit::Hertz {
format!("Δx {} Hz · ΔI {}", fmt_delta(dx), fmt_number(di))
} else {
let hz = nmr
.native_processed
.reference_frequency_mhz(0)
.map(|frequency| format!(" ({} Hz)", fmt_delta(dx * frequency)))
.unwrap_or_default();
format!("Δx {} ppm{hz} · ΔI {}", fmt_delta(dx), fmt_number(di))
}
} else {
format!("Δt {} s · ΔI {}", fmt_delta(dx), fmt_number(di))
}
}
Dataset::Nmr2D(nmr) => {
let Processed2D::Ft(spectrum) = &nmr.processed else {
return String::new();
};
let dy = delta.second.y.unwrap_or_default() - delta.first.y.unwrap_or_default();
let axis_delta = |value: f64, meta: &plotx_processing::AxisMeta| {
let unit = match meta.unit {
Some(nmr::axis::AxisUnit::Second) => "s",
Some(nmr::axis::AxisUnit::Hertz) => "Hz",
Some(nmr::axis::AxisUnit::Ppm) => "ppm",
_ => "",
};
format!("{} {unit}", fmt_delta(value))
};
format!(
"ΔF2 {} ppm ({} Hz) · ΔF1 {} ppm ({} Hz) · ΔI {}",
fmt_delta(dx),
fmt_delta(dx * spectrum.direct.observe_freq_mhz),
fmt_delta(dy),
fmt_delta(dy * spectrum.indirect.observe_freq_mhz),
fmt_number(di),
"ΔF2 {} · ΔF1 {} · ΔI {}",
axis_delta(dx, &spectrum.direct),
axis_delta(dy, &spectrum.indirect),
fmt_number(di)
)
}
_ => String::new(),
Expand Down
2 changes: 1 addition & 1 deletion crates/app/src/ui/canvas/mod_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ fn phase_editor_open_drives_on_plot_pivot() {
let mut app = PlotxApp::new();
app.doc
.datasets
.push(Dataset::Nmr(Box::new(NmrDataset::load(data))));
.push(Dataset::Nmr(Box::new(NmrDataset::load(data).unwrap())));
let mut canvas = CanvasDocument::new("page".to_owned(), [200.0, 200.0]);
let id = canvas.allocate_object_id();
let obj = app.build_plot_object(
Expand Down
2 changes: 1 addition & 1 deletion crates/app/src/ui/canvas/reference_pick_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn synthetic_app() -> PlotxApp {
let mut app = PlotxApp::new();
app.doc
.datasets
.push(Dataset::Nmr(Box::new(NmrDataset::load(data))));
.push(Dataset::Nmr(Box::new(NmrDataset::load(data).unwrap())));
app.focus_single(0);
app
}
Expand Down
30 changes: 23 additions & 7 deletions crates/app/src/ui/canvas/slices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,30 @@ pub(crate) fn paint_slice(
};

// A Row cut runs along F2 (the plot's x-axis); a Column cut along F1 (y).
let (slice, along_x, mode) = match &n.processed {
Processed2D::Ft(s) => (
s.slice(cursor.kind, cursor.index),
cursor.kind == SliceKind::Row,
DisplayMode::Real,
),
Processed2D::Stack(s) => (s.slice(cursor.index), true, DisplayMode::Real),
let kind = if matches!(n.processed, Processed2D::Stack(_)) {
SliceKind::Row
} else {
cursor.kind
};
let (_, slice) = match plotx_processing::slice::extract(
&n.native_processed,
kind,
plotx_processing::slice::Reduction::Slice(cursor.index),
) {
Ok(output) => output,
Err(error) => {
painter.text(
Pos2::new(plot.left + 8.0, plot.top + 8.0),
egui::Align2::LEFT_TOP,
format!("Slice unavailable: {error}"),
egui::FontId::proportional(12.0),
SLICE_COLOR,
);
return;
}
};
let along_x = kind == SliceKind::Row;
let mode = DisplayMode::Real;

if let Some(position) = slice.position {
if along_x {
Expand Down
2 changes: 1 addition & 1 deletion crates/app/src/ui/clipboard_figure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ mod tests {
let mut app = plotx_core::state::PlotxApp::new();
let action = Action::insert_dataset_with_default_canvas(
&app,
Dataset::Nmr(Box::new(NmrDataset::load(data))),
Dataset::Nmr(Box::new(NmrDataset::load(data).unwrap())),
"probe".to_owned(),
DEFAULT_CANVAS_SIZE_MM,
);
Expand Down
1 change: 1 addition & 0 deletions crates/app/src/ui/command_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ fn execute_inner(
}
CommandId::OpenFile => super::file_dialogs::open_file(app),
CommandId::OpenFolder => super::file_dialogs::open_folder(app),
CommandId::ImportNmrSampling => super::file_dialogs::nmr_sampling::open(app),
CommandId::RunBatchWorkflow => super::batch_workflow::AutomationUi::request_open(ctx),
CommandId::RunScientificScript => {
super::batch_workflow::AutomationUi::request_run_script(ctx)
Expand Down
Loading
Loading