PCEXTRACTSECTION: Extract Section Lines dialog

Picking the cloud opens the Extract Section Lines from Point Cloud
dialog with the reference's settings and defaults: entire cross section
or perimeter only, maximum points to process (slider and field, with
the estimated time), output layer (Use Current or a layer), colour,
lines or 2D polylines with their width, minimum line length and connect
lines tolerance (each measurable by two points on screen), collinear
angle tolerance (0-10) and Preview result. Create extracts; with the
preview it asks [Accept/Settings/Undo] and Settings reopens the dialog.
The settings are kept for the session. Automation action pc_section.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-09-30 15:50:22 +03:00
1 parent 09e9e44c19
commit 331772eac2
30 files changed
+931 -20

No files matched your search

+110
View File
@@ -3,6 +3,7 @@
//! and its section objects when they start.
use super::*;
use crate::ui::window::pdf_dialogs::PdfDialogMsg;
use codec::entities::ExtendedEntityData;
use glam::DVec3;
@@ -80,6 +81,10 @@ impl OpenCADStudio {
self.extraction_clouds(i),
self.extraction_sections(i),
)),
"_PCSECTIONDLG" => {
self.open_pc_section_dialog(i);
return Some(self.finish_dispatch(cmd));
}
_ => return None,
};
self.command_line.push_info(&command.prompt());
@@ -87,3 +92,108 @@ impl OpenCADStudio {
Some(self.finish_dispatch(cmd))
}
}
impl OpenCADStudio {
/// PCEXTRACTSECTION's settings dialog, filled from the session settings.
fn open_pc_section_dialog(&mut self, i: usize) {
use crate::modules::insert::pc_extract::{has_job, settings};
use crate::ui::window::pdf_dialogs::{LineColor, PcSectionState};
if !has_job() {
return;
}
let s = settings();
let current = crate::t!("Use Current").into_owned();
let mut layers = vec![current.clone()];
let mut names: Vec<String> = self.tabs[i].scene.document.layers.iter().map(|l| l.name.clone()).collect();
names.sort_by_key(|n| n.to_lowercase());
layers.extend(names);
let short = |v: f64| {
let t = format!("{v:.4}");
t.trim_end_matches('0').trim_end_matches('.').to_string()
};
self.pc_section = Some(PcSectionState {
perimeter: s.perimeter,
max_points: s.max_points.to_string(),
layers,
layer: s.layer.clone().unwrap_or(current),
color: LineColor(s.color),
polylines: s.polylines,
width: short(s.width),
min_length: short(s.min_length),
connect: short(s.connect),
angle: short(s.angle),
preview: s.preview,
});
self.active_modal = Some(crate::app::ModalKind::PcSection);
}
pub(in crate::app) fn update_pc_section(&mut self, message: PdfDialogMsg) -> Task<Message> {
use crate::modules::insert::pc_extract::{set_settings, settings, SectionCommand, SectionDistanceCommand};
use crate::command::CadCommand;
let i = self.active_tab;
let Some(state) = self.pc_section.as_mut() else {
return Task::none();
};
match message {
PdfDialogMsg::SecPerimeter(on) => state.perimeter = on,
PdfDialogMsg::SecMaxPoints(v) => state.max_points = v,
PdfDialogMsg::SecLayer(v) => state.layer = v,
PdfDialogMsg::SecColor(c) => state.color = c,
PdfDialogMsg::SecPolylines(on) => state.polylines = on,
PdfDialogMsg::SecWidth(v) => state.width = v,
PdfDialogMsg::SecMinLength(v) => state.min_length = v,
PdfDialogMsg::SecConnect(v) => state.connect = v,
PdfDialogMsg::SecAngle(v) => state.angle = v,
PdfDialogMsg::SecPreview(on) => state.preview = on,
// Measure on screen, then back to the dialog (the extraction waits).
PdfDialogMsg::SecPick(connect) => {
self.pc_section = None;
self.active_modal = None;
let command = SectionDistanceCommand::new(connect);
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
}
PdfDialogMsg::SecCreate => {
let number = |t: &str| crate::entities::common::parse_f64(t.trim());
let (min_length, connect, width) = (number(&state.min_length), number(&state.connect), number(&state.width));
let (Some(min_length), Some(connect)) = (min_length.filter(|v| *v > 0.0), connect.filter(|v| *v > 0.0)) else {
self.command_line.push_error(crate::t!("Requires a positive number.").as_ref());
return Task::none();
};
let Some(angle) = state.angle.trim().parse::<i64>().ok().filter(|v| (0..=10).contains(v)) else {
self.command_line
.push_error("Colinear angle tolerance only accepts integer from 0 to 10.");
return Task::none();
};
let Some(max_points) = state.max_points.trim().parse::<usize>().ok().filter(|v| *v > 0) else {
self.command_line.push_error(crate::t!("Requires a positive integer.").as_ref());
return Task::none();
};
let current = crate::t!("Use Current").into_owned();
let mut s = settings();
s.perimeter = state.perimeter;
s.max_points = max_points;
s.layer = (state.layer != current).then(|| state.layer.clone());
s.color = state.color.0;
s.polylines = state.polylines;
s.width = width.filter(|v| *v >= 0.0).unwrap_or(0.0);
s.min_length = min_length;
s.connect = connect;
s.angle = angle as f64;
s.preview = state.preview;
set_settings(s);
self.pc_section = None;
self.active_modal = None;
let Some(mut command) = SectionCommand::resume() else {
return Task::none();
};
let result = command.run();
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
return self.apply_cmd_result(result);
}
_ => {}
}
Task::none()
}
}
+13
View File
@@ -378,6 +378,7 @@ impl OpenCADStudio {
}
"attach" => crate::t!("Attaches a PDF file as an underlay."),
"pointcloud" => crate::t!("Attaches a point cloud scan or project."),
"pcsection" => crate::t!("Traces the points on the kept side of a live section as lines."),
"pointcloudcolormap" => {
crate::t!("Sets the color schemes and ranges a point cloud is colored by intensity or elevation.")
}
@@ -503,6 +504,18 @@ impl OpenCADStudio {
| PdfDialogMsg::MapCurrent(_)
| PdfDialogMsg::MapApply
| PdfDialogMsg::MapOk) => return self.update_point_cloud_color_map(message),
message @ (PdfDialogMsg::SecPerimeter(_)
| PdfDialogMsg::SecMaxPoints(_)
| PdfDialogMsg::SecLayer(_)
| PdfDialogMsg::SecColor(_)
| PdfDialogMsg::SecPolylines(_)
| PdfDialogMsg::SecWidth(_)
| PdfDialogMsg::SecMinLength(_)
| PdfDialogMsg::SecConnect(_)
| PdfDialogMsg::SecAngle(_)
| PdfDialogMsg::SecPick(_)
| PdfDialogMsg::SecPreview(_)
| PdfDialogMsg::SecCreate) => return self.update_pc_section(message),
PdfDialogMsg::CloudPathType(choice) => {
let stored = self.point_cloud_attach.as_ref().map(|s| self.pdf_stored_path(&s.path, choice.0));
if let (Some(s), Some(stored)) = (self.point_cloud_attach.as_mut(), stored) {
+20
View File
@@ -75,6 +75,7 @@ pub(super) const NAMES: &[&str] = &[
"pc_manager",
"pdf_page_select",
"pc_colormap",
"pc_section",
"ribbon_tab",
"ribbon_dropdown",
"dialog_ok",
@@ -721,6 +722,24 @@ impl OpenCADStudio {
_ => return Err(failure("invalid_value", "Unknown color map edit")),
})
}
// Section extraction dialog: "min=0.01", "connect=0.02", "angle=5",
// "points=18000", "lines=1", "perimeter=1", "preview=0", "width=0".
"pc_section" => {
use crate::ui::window::pdf_dialogs::PdfDialogMsg as M;
let value = string(req, "value")?;
let (key, arg) = value.split_once('=').unwrap_or((value, ""));
Message::PdfDialog(match key {
"min" => M::SecMinLength(arg.into()),
"connect" => M::SecConnect(arg.into()),
"angle" => M::SecAngle(arg.into()),
"points" => M::SecMaxPoints(arg.into()),
"width" => M::SecWidth(arg.into()),
"lines" => M::SecPolylines(arg == "0"),
"perimeter" => M::SecPerimeter(arg != "0"),
"preview" => M::SecPreview(arg != "0"),
_ => return Err(failure("invalid_value", "Unknown section setting")),
})
}
// Point Cloud Manager: flip a row's switch, "<handle>:<row>" with
// row cloud, unassigned, scans or scan:<name>.
"pc_manager_toggle" => {
@@ -776,6 +795,7 @@ impl OpenCADStudio {
Some(crate::app::ModalKind::PdfAttach) => PdfDialogMsg::AttachOk,
Some(crate::app::ModalKind::PointCloudAttach) => PdfDialogMsg::CloudOk,
Some(crate::app::ModalKind::PointCloudColorMap) => PdfDialogMsg::MapOk,
Some(crate::app::ModalKind::PcSection) => PdfDialogMsg::SecCreate,
Some(crate::app::ModalKind::UnderlayLayers) => PdfDialogMsg::LayersOk,
Some(crate::app::ModalKind::PdfImportSettings) => PdfDialogMsg::SettingsOk,
Some(crate::app::ModalKind::PdfImportFile) => PdfDialogMsg::ImportOk,
+3
View File
@@ -499,6 +499,7 @@ pub(super) struct OpenCADStudio {
pdf_attach: Option<crate::ui::window::pdf_dialogs::PdfAttachState>,
point_cloud_attach: Option<crate::ui::window::pdf_dialogs::PointCloudAttachState>,
point_cloud_color_map: Option<crate::ui::window::pdf_dialogs::PointCloudColorMapState>,
pc_section: Option<crate::ui::window::pdf_dialogs::PcSectionState>,
underlay_layers: Option<crate::ui::window::pdf_dialogs::UnderlayLayersState>,
pdf_import_settings: Option<crate::modules::insert::pdf_import::PdfImportSettings>,
pdf_import_file: Option<crate::ui::window::pdf_dialogs::PdfImportFileState>,
@@ -1907,6 +1908,7 @@ pub enum ModalKind {
PdfAttach,
PointCloudAttach,
PointCloudColorMap,
PcSection,
UnderlayLayers,
PdfImportSettings,
PdfImportFile,
@@ -4041,6 +4043,7 @@ impl OpenCADStudio {
pdf_attach: None,
point_cloud_attach: None,
point_cloud_color_map: None,
pc_section: None,
underlay_layers: None,
pdf_import_settings: None,
pdf_import_file: None,
+5
View File
@@ -8485,6 +8485,11 @@ impl OpenCADStudio {
}
Message::CloseModal => {
// Cancelling the section settings drops the waiting extraction.
if self.active_modal == Some(super::ModalKind::PcSection) {
crate::modules::insert::pc_extract::drop_job();
self.pc_section = None;
}
if self.active_modal == Some(super::ModalKind::RecoveryPrompt) {
return self.update(Message::RecoveryDecline);
}
+5
View File
@@ -31,6 +31,7 @@ impl OpenCADStudio {
},
Some(K::PointCloudAttach) => crate::t!("Attach Point Cloud").into_owned(),
Some(K::PointCloudColorMap) => crate::t!("Point Cloud Color Map").into_owned(),
Some(K::PcSection) => crate::t!("Extract Section Lines from Point Cloud").into_owned(),
Some(K::UnderlayLayers) => crate::t!("Underlay Layers").into_owned(),
Some(K::PdfImportSettings) => crate::t!("PDF Import Settings").into_owned(),
Some(K::PdfImportFile) => crate::t!("Import PDF").into_owned(),
@@ -536,6 +537,10 @@ impl OpenCADStudio {
crate::ui::window::pdf_dialogs::view_point_cloud_color_map(state, flow)
})
}
super::super::ModalKind::PcSection => {
let state = self.pc_section.as_ref()?;
sized_flow(ex, 760, 470, |flow| crate::ui::window::pdf_dialogs::view_pc_section(state, flow))
}
super::super::ModalKind::UnderlayLayers => {
let state = self.underlay_layers.as_ref()?;
sized_flow(ex, 460, 520, |flow| {