Point clouds: PCEXTRACTEDGE, PCEXTRACTCORNER, PCEXTRACTCENTERLINE, PCEXTRACTSECTION

Planes are found from the cloud points under the pick and grown over
the connected points on them: two planes give a line along their
intersection over the points' extent, three a point at their corner.
A cylindrical segment gives a line along its axis. A live section
object traces the points on its kept side as polylines or lines in the
section plane, with a preview and Accept/Settings/Undo;
-PCEXTRACTSECTION creates them directly.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-09-30 12:47:23 +03:00
1 parent 2e23a903a4
commit 7761652a9f
4 files changed
+1083

No files matched your search

+4
View File
@@ -15,6 +15,7 @@ mod layerprops;
mod layers;
pub(crate) mod pdf_import;
mod point_cloud;
mod pc_extract;
mod pdf_underlay;
mod pdf_dialogs;
mod xclip;
@@ -270,6 +271,9 @@ impl OpenCADStudio {
if let Some(t) = self.dispatch_pdf_underlay(cmd, i) {
return Some(t);
}
if let Some(t) = self.dispatch_pc_extract(cmd, i) {
return Some(t);
}
if let Some(t) = self.dispatch_draw(cmd, i) {
return Some(t);
}
+83
View File
@@ -0,0 +1,83 @@
//! PCEXTRACTEDGE, PCEXTRACTCORNER, PCEXTRACTCENTERLINE and
//! (-)PCEXTRACTSECTION: the commands get the drawing's shown cloud points
//! and its section objects when they start.
use super::*;
use codec::entities::ExtendedEntityData;
use glam::DVec3;
use crate::modules::insert::pc_extract::{
CenterlineCommand, Clouds, PlanesCommand, Section, SectionCommand,
};
fn dvec(v: codec::types::Vector3) -> DVec3 {
DVec3::new(v.x, v.y, v.z)
}
impl OpenCADStudio {
/// The points each point cloud shows (the renderer's placed set).
fn extraction_clouds(&self, i: usize) -> Clouds {
let document = &self.tabs[i].scene.document;
let mut clouds = Vec::new();
for entity in document.entities() {
let codec::EntityType::Extended(extended) = entity else {
continue;
};
let ExtendedEntityData::PointCloudEx(data) = &extended.data else {
continue;
};
let common = entity.common();
// The colour the renderer places with, so its cached points are reused.
let color = if matches!(common.color, codec::types::Color::ByLayer) {
document.layers.get(&common.layer).map(|layer| layer.color.clone()).unwrap_or(common.color.clone())
} else {
common.color.clone()
};
let color = color.rgb().map_or([255; 3], |(r, g, b)| [r, g, b]);
if let Some(placed) = crate::scene::model::point_cloud::placed(document, data, color, None) {
clouds.push((common.handle, placed));
}
}
Clouds(clouds)
}
fn extraction_sections(&self, i: usize) -> Vec<Section> {
self.tabs[i]
.scene
.document
.entities()
.filter_map(|entity| {
let codec::EntityType::Extended(extended) = entity else {
return None;
};
let ExtendedEntityData::SectionObject(data) = &extended.data else {
return None;
};
let (first, last) = (dvec(*data.vertices.first()?), dvec(*data.vertices.last()?));
let tangent = (last - first).normalize_or(DVec3::X);
let vertical = dvec(data.vertical_direction).normalize_or(DVec3::Z);
let viewing = vertical.cross(tangent).normalize_or(-DVec3::Z)
* if data.flags & 4 != 0 { 1.0 } else { -1.0 };
Some(Section { origin: first, viewing, tangent, live: data.flags & 1 != 0 })
})
.collect()
}
pub(super) fn dispatch_pc_extract(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
use crate::command::CadCommand;
let command: Box<dyn CadCommand> = match cmd {
"PCEXTRACTEDGE" => Box::new(PlanesCommand::new(false, self.extraction_clouds(i))),
"PCEXTRACTCORNER" => Box::new(PlanesCommand::new(true, self.extraction_clouds(i))),
"PCEXTRACTCENTERLINE" => Box::new(CenterlineCommand::new(self.extraction_clouds(i))),
"PCEXTRACTSECTION" | "-PCEXTRACTSECTION" => Box::new(SectionCommand::new(
cmd == "PCEXTRACTSECTION",
self.extraction_clouds(i),
self.extraction_sections(i),
)),
_ => return None,
};
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(command);
Some(self.finish_dispatch(cmd))
}
}