Point clouds: drawn from their scans, placed, cropped and attached as the reference does

Scans (.rcs) and projects (.rcp) are read and their points drawn as
screen-sized dots, coloured by the cloud's stylization. The cloud's
placement carries its scale and rotation; its extents and crops stay in
the scan's coordinates. A missing scan shows the extents with the saved
path. -POINTCLOUDATTACH / POINTCLOUDATTACH, POINTCLOUDCROP and the
POINTCLOUD* settings follow the reference's prompts and messages, the
Properties list is the reference's, and a locked cloud is left alone by
edits.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-09-29 17:08:46 +03:00
1 parent 07f25f4bca
commit e9c4e501db
58 files changed
+2711 -237

No files matched your search

+12 -1
View File
@@ -615,6 +615,15 @@ impl OpenCADStudio {
crate::app::commands::pdf_import::PdfImportSource::File(import),
);
}
CmdResult::AttachPointCloud { placement, message } => {
let label = self.history_label_from_active_cmd(i, "POINTCLOUDATTACH");
self.tabs[i].scene.clear_preview_wire();
self.tabs[i].active_cmd = None;
self.tabs[i].snap_result = None;
self.command_line.push_output(&message);
self.attach_point_cloud(i, label, placement);
self.restore_pre_cmd_tangent();
}
CmdResult::AttachPdfPages { kind, path, pages } => {
let label = self.history_label_from_active_cmd(i, "PDFATTACH");
self.tabs[i].scene.clear_preview_wire();
@@ -1094,7 +1103,9 @@ impl OpenCADStudio {
}
fn handle_cancel_with_message(&mut self, message: String) -> Task<Message> {
self.command_line.push_error(&message);
for line in message.lines() {
self.command_line.push_error(line);
}
self.apply_cmd_result(CmdResult::Cancel)
}
+42
View File
@@ -490,6 +490,47 @@ impl OpenCADStudio {
StepInput::SelectionComplete(kept)
}
/// Drops locked point clouds from a completed selection when the active
/// command would move them, saying which were locked and how many went.
fn filter_locked_point_cloud_selection(&mut self, input: StepInput) -> StepInput {
let StepInput::SelectionComplete(handles) = input else {
return input;
};
let i = self.active_tab;
if !self.tabs[i]
.active_cmd
.as_ref()
.is_some_and(|command| command.selection_drops_locked_point_clouds())
{
return StepInput::SelectionComplete(handles);
}
let (dropped, kept): (Vec<Handle>, Vec<Handle>) = handles.into_iter().partition(|h| {
self.tabs[i]
.scene
.document
.get_entity(*h)
.is_some_and(crate::scene::is_locked_point_cloud)
});
for handle in &dropped {
self.tabs[i].scene.deselect_entity(*handle);
}
if !dropped.is_empty() {
let count = dropped.len();
self.command_line.push_info(&if count == 1 {
crate::t!("1 point cloud is locked.").into_owned()
} else {
crate::tf!("{count} point clouds are locked.").into_owned()
});
self.command_line.push_info(&if count == 1 {
crate::t!("1 was filtered out.").into_owned()
} else {
crate::tf!("{count} were filtered out.").into_owned()
});
self.refresh_properties();
}
StepInput::SelectionComplete(kept)
}
/// `feed_command`, also reporting whether the step actually took the input.
/// Only a `Text` token can come back unclaimed (`on_text_input` returning
/// `None`), which is what lets the caller read it as something else — a
@@ -500,6 +541,7 @@ impl OpenCADStudio {
// as the reference does while gathering.
let input = self.filter_associative_dimension_selection(input);
let input = self.filter_block_reference_selection(input);
let input = self.filter_locked_point_cloud_selection(input);
// XCLIP / CLIP learn which chosen references carry a clip already.
{
let i = self.active_tab;