forked from 1b-agent/opencadstudio-upstream
Merge pull request #1553 from ramox81/codex/pointcloud-attach
Point clouds: drawn from their scans, attached, cropped and stylized. The fork's codec patch is resolved to the upstream pins: the point cloud records landed in opencadcodec 5ef9376, carried through opencadkernel 68eeda1 and opencadgraph e204966. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
3dcebf0dc6
81 files changed
+9162
-305
No files matched your search
@@ -2369,6 +2369,18 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
return self.on_dynamic_dimension_choice(field, &value);
|
||||
}
|
||||
let i = self.active_tab;
|
||||
if field == "ext_pcx_locked" {
|
||||
let handles = self.point_cloud_lock_targets(i);
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(codec::EntityType::Extended(extended)) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::entities::traits::PropertyEditable::apply_geom_prop(extended, field, &value);
|
||||
}
|
||||
});
|
||||
self.tabs[i].properties.edit_choice_open = false;
|
||||
return Task::none();
|
||||
}
|
||||
let handles = self.property_target_handles(i);
|
||||
// The Annotative Yes/No list drives the per-object annotative toggle
|
||||
// (MTEXT's own flag, the annotation context for the rest).
|
||||
@@ -3607,6 +3619,14 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
};
|
||||
// Underlay rows: reject what the reference rejects, and
|
||||
// write a Width / Height as the scale that produces it.
|
||||
if field.starts_with("ext_pcx_") {
|
||||
if let Err(message) =
|
||||
crate::entities::extended::validate_point_cloud_property(field, &val)
|
||||
{
|
||||
self.command_line.push_error(message);
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
if field.starts_with("ul_") {
|
||||
if let Err(message) =
|
||||
crate::entities::underlay::validate_property(field, &val)
|
||||
|
||||
@@ -630,6 +630,9 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||
PanelId::NodeGraph => {
|
||||
self.show_node_graph = false;
|
||||
}
|
||||
PanelId::PointCloudManager => {
|
||||
self.pc_manager.show = false;
|
||||
}
|
||||
PanelId::Properties => {
|
||||
self.show_properties = false;
|
||||
self.ribbon.set_properties(false);
|
||||
@@ -734,6 +737,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||
PanelId::ExternalReferences => self.show_external_references,
|
||||
PanelId::Browser => self.show_browser,
|
||||
PanelId::NodeGraph => self.show_node_graph,
|
||||
PanelId::PointCloudManager => self.pc_manager.show,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,6 +1021,8 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||
}
|
||||
}
|
||||
self.tabs[i].scene.reseed_underlays();
|
||||
// Point clouds redraw as unloaded (box and saved path).
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
}
|
||||
XrefPaletteOp::Reload => {
|
||||
let base_dir: std::path::PathBuf = host
|
||||
@@ -1058,6 +1064,18 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
||||
self.tabs[i].xref_unloaded.remove(key);
|
||||
}
|
||||
self.tabs[i].scene.reseed_underlays();
|
||||
} else if *kind == crate::io::xref_model::RefKind::PointCloud {
|
||||
// A point cloud reloads by setting its definition loaded.
|
||||
for (key, row_name, row_kind, _) in &picked {
|
||||
if row_kind == kind && row_name == name {
|
||||
crate::io::xref::set_point_cloud_loaded(
|
||||
&mut self.tabs[i].scene.document,
|
||||
codec::types::Handle::new(*key),
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
} else if *kind != crate::io::xref_model::RefKind::DwgXref {
|
||||
self.command_line.push_error(crate::tf!(
|
||||
"{}: reload applies to drawing references only.",
|
||||
|
||||
@@ -1429,6 +1429,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
message @ (Message::AttachPick
|
||||
| Message::UnderlayAttachPick(_)
|
||||
| Message::PointCloudAttachPick
|
||||
| Message::AttachPickResult(_)
|
||||
| Message::XrefAttach(_)
|
||||
| Message::XrefAttachBrowseResult(_)) => self.update_xref_attach(message),
|
||||
@@ -1805,6 +1806,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
Message::PdfDialog(message) => self.update_pdf_dialog(message),
|
||||
Message::PcManager(message) => self.update_pc_manager(message),
|
||||
Message::RibbonSelectTab(idx) => {
|
||||
self.ribbon.select(idx);
|
||||
Task::none()
|
||||
@@ -8494,6 +8496,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);
|
||||
}
|
||||
|
||||
+101
-2
@@ -167,6 +167,29 @@ fn cursor_on_projected_axis(
|
||||
.then_some(origin + direction * distance as f64)
|
||||
}
|
||||
|
||||
/// The point under `cursor` on the plane through `origin` with `normal`.
|
||||
fn cursor_on_plane(
|
||||
cursor: Point,
|
||||
bounds: iced::Rectangle,
|
||||
view: glam::Mat4,
|
||||
eye: glam::DVec3,
|
||||
origin: glam::DVec3,
|
||||
normal: glam::DVec3,
|
||||
) -> Option<glam::DVec3> {
|
||||
let inverse = view.as_dmat4().inverse();
|
||||
let x = cursor.x as f64 / bounds.width as f64 * 2.0 - 1.0;
|
||||
let y = 1.0 - cursor.y as f64 / bounds.height as f64 * 2.0;
|
||||
let near = inverse.project_point3(glam::DVec3::new(x, y, 0.0));
|
||||
let far = inverse.project_point3(glam::DVec3::new(x, y, 1.0));
|
||||
let direction = far - near;
|
||||
let along = direction.dot(normal);
|
||||
if along.abs() <= 1e-12 {
|
||||
return None;
|
||||
}
|
||||
let t = ((origin - eye) - near).dot(normal) / along;
|
||||
t.is_finite().then(|| eye + near + direction * t)
|
||||
}
|
||||
|
||||
fn is_added_polyline_vertex(
|
||||
original: &AcadEntityType,
|
||||
current: &AcadEntityType,
|
||||
@@ -635,6 +658,36 @@ impl OpenCADStudio {
|
||||
.find(|(owner, grip)| *owner == handle && grip.id == grip_id)
|
||||
.and_then(|(_, grip)| grip.axis);
|
||||
|
||||
// A move-gizmo part moves its cloud alone, along its axis or plane.
|
||||
let gizmo_shape = self.tabs[i]
|
||||
.selected_grip_handles
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(self.tabs[i].selected_grips.iter())
|
||||
.find(|(owner, grip)| *owner == handle && grip.id == grip_id)
|
||||
.map(|(_, grip)| grip.shape)
|
||||
.filter(|shape| {
|
||||
matches!(
|
||||
shape,
|
||||
crate::scene::model::object::GripShape::GizmoAxis(_)
|
||||
| crate::scene::model::object::GripShape::GizmoPlane(_)
|
||||
)
|
||||
});
|
||||
if let Some(shape) = gizmo_shape {
|
||||
self.tabs[i].hot_grips.clear();
|
||||
let mut edit = GripEdit::single(handle, grip_id, true, world);
|
||||
edit.axis = axis;
|
||||
edit.gizmo = true;
|
||||
if let crate::scene::model::object::GripShape::GizmoPlane(k) = shape {
|
||||
let (a, b) = crate::scene::pick::grip::gizmo_plane_axes(k);
|
||||
edit.plane = Some(
|
||||
crate::scene::pick::grip::gizmo_axis(a)
|
||||
.cross(crate::scene::pick::grip::gizmo_axis(b)),
|
||||
);
|
||||
}
|
||||
return edit;
|
||||
}
|
||||
|
||||
let clicked_is_hot = self.tabs[i].hot_grips.contains(&(handle, grip_id));
|
||||
|
||||
let mut targets: Vec<GripTarget> = if clicked_is_hot {
|
||||
@@ -716,6 +769,9 @@ impl OpenCADStudio {
|
||||
axis,
|
||||
targets,
|
||||
rectangle_frame: None,
|
||||
plane: None,
|
||||
grab: None,
|
||||
gizmo: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,6 +983,10 @@ impl OpenCADStudio {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(crate::tf!("Visual style: {label}").as_ref());
|
||||
// The point cloud move gizmo follows the visual style.
|
||||
if i == self.active_tab {
|
||||
self.refresh_selected_grips();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
@@ -1599,6 +1659,28 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(normal) = grip.plane {
|
||||
if snap_hit.is_none_or(|hit| hit.snap_type == crate::snap::SnapType::Grid) {
|
||||
snapped = cursor_on_plane(p, bounds, view_rot, eye, grip.origin_world, normal)
|
||||
.unwrap_or(snapped);
|
||||
}
|
||||
}
|
||||
if grip.gizmo {
|
||||
// The cloud follows the cursor by the offset it was grabbed
|
||||
// at (measured on the press; the first move as a fallback).
|
||||
let grab = match grip.grab {
|
||||
Some(grab) => grab,
|
||||
None => {
|
||||
let grab = snapped - grip.origin_world;
|
||||
if let Some(active) = self.tabs[i].active_grip.as_mut() {
|
||||
active.grab = Some(grab);
|
||||
}
|
||||
grab
|
||||
}
|
||||
};
|
||||
snapped -= grab;
|
||||
}
|
||||
|
||||
let snap_ms = snap_started.elapsed().as_secs_f64() * 1000.0;
|
||||
// The overlay builds the active tracking guide from `otrack_active.base`
|
||||
// to `last_cursor_world`. Keep it synchronized with the actual point used
|
||||
@@ -3557,8 +3639,25 @@ impl OpenCADStudio {
|
||||
self.grip_popup = None;
|
||||
return Task::none();
|
||||
}
|
||||
self.tabs[i].active_grip =
|
||||
Some(self.grip_edit_for_hit(i, handle, grip_id, is_translate, world));
|
||||
let mut edit = self.grip_edit_for_hit(i, handle, grip_id, is_translate, world);
|
||||
if edit.gizmo {
|
||||
// Where the gizmo part was grabbed: the drag keeps
|
||||
// that offset from the cloud's insertion point.
|
||||
let (view_rot, eye) = match &edit_cam {
|
||||
Some(cam) => (cam.view_proj_rte(bounds), cam.eye()),
|
||||
None => {
|
||||
let cam = self.tabs[i].scene.camera.borrow();
|
||||
(cam.view_proj_rte(bounds), cam.eye())
|
||||
}
|
||||
};
|
||||
let grabbed = match (edit.axis, edit.plane) {
|
||||
(Some(axis), _) => cursor_on_projected_axis(p, bounds, view_rot, eye, world, axis),
|
||||
(_, Some(normal)) => cursor_on_plane(p, bounds, view_rot, eye, world, normal),
|
||||
_ => None,
|
||||
};
|
||||
edit.grab = grabbed.map(|point| point - world);
|
||||
}
|
||||
self.tabs[i].active_grip = Some(edit);
|
||||
self.grip_hover = None;
|
||||
self.grip_popup = None;
|
||||
self.tabs[i]
|
||||
|
||||
Reference in new issue
Block a user