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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ repository = "https://github.com/nmrtist/plotx"
colorous = "1"
num-complex = "0.4"
rustfft = "6.4"
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
semver = "1"
sha2 = "0.10"
Expand Down
27 changes: 19 additions & 8 deletions crates/app/src/ui/file_dialogs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,10 @@ pub(crate) fn load_and_note(app: &mut PlotxApp, path: &std::path::Path) {
pub(crate) fn open_file(app: &mut PlotxApp) {
if let Some(paths) = rfd::FileDialog::new()
.add_filter(
"All supported data (*.abf, *.jdf, fid, ser, *.zip)",
&["abf", "jdf", "fid", "ser", "zip"],
"All supported data (*.spm, *.pfc, *.abf, *.jdf, fid, ser, *.zip)",
&["spm", "pfc", "abf", "jdf", "fid", "ser", "zip"],
)
.add_filter("Bruker NanoScope AFM (*.spm, *.pfc)", &["spm", "pfc"])
.add_filter("Axon Binary Format 2 (*.abf)", &["abf"])
.add_filter("JEOL Delta (*.jdf)", &["jdf"])
.add_filter("Bruker TopSpin (fid, ser)", &["fid", "ser"])
Expand Down Expand Up @@ -362,7 +363,7 @@ pub(crate) fn save_project_as(app: &mut PlotxApp, include_view_snapshots: bool)

pub(crate) fn open_folder(app: &mut PlotxApp) {
if let Some(path) = rfd::FileDialog::new()
.set_title("Open a data folder (Bruker acquisition or recursive ABF2 import)")
.set_title("Open a data folder (Bruker acquisition or recursive AFM/ABF2 import)")
.pick_folder()
{
open_folder_path(app, &path);
Expand All @@ -375,13 +376,23 @@ pub(crate) fn open_folder(app: &mut PlotxApp) {
/// any file of the batch loaded, not just the last one.
fn open_folder_path(app: &mut PlotxApp, path: &std::path::Path) {
let before = app.doc.datasets.len();
let mut abf_files = Vec::new();
discovery::collect_abf_files(path, &mut abf_files);
if abf_files.is_empty() {
let mut data_files = Vec::new();
discovery::collect_data_files(path, &mut data_files);
if data_files.is_empty() {
app.load_from(path);
} else {
abf_files.sort();
for file in abf_files {
data_files.sort();
let companion_paths: std::collections::HashSet<std::path::PathBuf> = data_files
.iter()
.filter(|file| {
file.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("pfc"))
})
.filter_map(|file| plotx_io::load_path(file).ok())
.flat_map(|loaded| loaded.provenance.companion_paths)
.collect();
data_files.retain(|file| !companion_paths.contains(file));
for file in data_files {
app.load_from(&file);
}
}
Expand Down
19 changes: 12 additions & 7 deletions crates/app/src/ui/file_dialogs/discovery.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::{Path, PathBuf};

pub(super) fn collect_abf_files(folder: &Path, output: &mut Vec<PathBuf>) {
pub(super) fn collect_data_files(folder: &Path, output: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(folder) else {
return;
};
Expand All @@ -10,13 +10,18 @@ pub(super) fn collect_abf_files(folder: &Path, output: &mut Vec<PathBuf>) {
continue;
};
if kind.is_dir() && !kind.is_symlink() {
collect_abf_files(&path, output);
} else if kind.is_file()
&& path
collect_data_files(&path, output);
} else if kind.is_file() {
let extension = path
.extension()
.is_some_and(|extension| extension.eq_ignore_ascii_case("abf"))
{
output.push(path);
.and_then(|value| value.to_str())
.unwrap_or("");
if ["abf", "spm", "pfc"]
.iter()
.any(|supported| extension.eq_ignore_ascii_case(supported))
{
output.push(path);
}
}
}
}
1 change: 1 addition & 0 deletions crates/app/src/ui/tools/processing/editors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ fn raw_point_count(dataset: &Dataset) -> usize {
.and_then(|s| s.channels.first())
.map(Vec::len)
.unwrap_or(0),
Dataset::Afm(_) => 0,
}
}

Expand Down
3 changes: 3 additions & 0 deletions crates/app/src/ui/tools/processing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ fn badge(dataset: &Dataset) -> (String, bool) {
Dataset::Nmr2D(n) => n.preset.label().to_owned(),
Dataset::Table(_) => String::new(),
Dataset::Electrophysiology(_) => "Patch clamp".to_owned(),
Dataset::Afm(_) => "AFM".to_owned(),
};
(name, default)
}
Expand Down Expand Up @@ -496,6 +497,7 @@ fn set_group_delay(app: &mut PlotxApp, di: usize, on: bool) {
}
Dataset::Table(_) => {}
Dataset::Electrophysiology(_) => {}
Dataset::Afm(_) => {}
}
}

Expand All @@ -505,6 +507,7 @@ fn group_delay(dataset: &Dataset) -> bool {
Dataset::Nmr2D(n) => n.group_delay_correct,
Dataset::Table(_) => true,
Dataset::Electrophysiology(_) => true,
Dataset::Afm(_) => true,
}
}

Expand Down
21 changes: 11 additions & 10 deletions crates/core/src/actions/app_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,18 +414,19 @@ impl PlotxApp {
if *canvas_index != self.doc.canvases.len() {
return;
}
let mut canvas = CanvasDocument::new(canvas_name.clone(), *size_mm);
canvas.resource_id.clone_from(canvas_resource_id);
canvas.board_pos = crate::state::next_page_board_pos(self);
let page = canvas.size_pt();
let id = canvas.allocate_object_id();
let object = self.build_plot_object(
let mut canvas = crate::workflow::build_default_canvas_for_dataset(
&self.doc.datasets[*dataset_index],
*dataset_index,
ObjectFrame::new(0.0, 0.0, page[0], page[1]),
id,
"Plot 1".to_owned(),
canvas_name.clone(),
*size_mm,
);
canvas.objects.push(object);
canvas.resource_id.clone_from(canvas_resource_id);
canvas.board_pos = crate::state::next_page_board_pos(self);
for object in &mut canvas.objects {
if let Some(plot) = object.plot_mut() {
plot.figure.typography = self.doc.style_library.figure_typography;
}
}
self.doc.canvases.push(canvas);
self.session.active_canvas = Some(*canvas_index);
}
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/actions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub enum DatasetProcessingState {
/// through their own actions.
Table,
Electrophysiology(crate::state::ElectrophysiologyProcessing),
Afm,
}

#[derive(Clone)]
Expand Down
3 changes: 3 additions & 0 deletions crates/core/src/actions/processing_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ impl DatasetProcessingState {
},
Dataset::Table(_) => Self::Table,
Dataset::Electrophysiology(d) => Self::Electrophysiology(d.processing),
Dataset::Afm(_) => Self::Afm,
}
}

Expand Down Expand Up @@ -66,6 +67,7 @@ impl DatasetProcessingState {
data.processing = *processing;
Ok(ProcessingRebuild::Rebuilt)
}
(Dataset::Afm(_), Self::Afm) => Ok(ProcessingRebuild::Unchanged),
(dataset, state) => Err(ProcessingStateError {
dataset_kind: dataset.kind_label(),
state_kind: state.kind_label(),
Expand All @@ -79,6 +81,7 @@ impl DatasetProcessingState {
Self::Nmr2D { .. } => "NMR 2D",
Self::Table => "Data Table",
Self::Electrophysiology(_) => "Electrophysiology",
Self::Afm => "AFM",
}
}
}
Expand Down
47 changes: 47 additions & 0 deletions crates/core/src/automation/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,30 @@ impl<'a> ProjectResourceProvider<'a> {
vec!["s".to_owned()],
Vec::new(),
),
Dataset::Afm(afm) => {
let dimensions = afm.data.forces.as_ref().map_or_else(
|| {
afm.data
.images
.first()
.map_or_else(Vec::new, |image| vec![image.height, image.width])
},
|forces| {
vec![
forces.grid_height,
forces.grid_width,
forces.samples_per_curve,
]
},
);
let units = afm
.data
.images
.iter()
.map(|image| image.scale.unit.clone())
.collect();
(dimensions, units, Vec::new())
}
};
ResourceDescriptor {
resource: top_ref(&id, KIND_DATASET),
Expand Down Expand Up @@ -538,6 +562,29 @@ fn preview_dataset(
serde_json::json!({"summary": dataset.summary()}),
recording.data.sweeps.len(),
),
Dataset::Afm(afm) => {
let shape = afm.data.forces.as_ref().map_or_else(
|| {
afm.data
.images
.first()
.map_or_else(Vec::new, |image| vec![image.height, image.width])
},
|forces| {
vec![
forces.grid_height,
forces.grid_width,
forces.samples_per_curve,
]
},
);
let total = shape.iter().copied().fold(1usize, usize::saturating_mul);
(
shape,
serde_json::json!({"summary": dataset.summary()}),
total,
)
}
};
let returned = total.min(limit);
Ok(DataPreview {
Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/data_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ fn processed_data_available(dataset: &Dataset) -> bool {
})
}
Dataset::Table(_) => false,
Dataset::Afm(_) => false,
}
}

Expand Down Expand Up @@ -450,6 +451,7 @@ fn capture_processed(dataset: &Dataset) -> Result<SnapshotData, DataExportError>
})
}
Dataset::Table(_) => Err(DataExportError::ContentUnavailable),
Dataset::Afm(_) => Err(DataExportError::ContentUnavailable),
}
}

Expand Down
Loading
Loading