Feat: UCS icon — animated XYZ axis tripod in viewport bottom-left corner

Screen-space UCS icon drawn on the existing selection canvas overlay.
Axes are projected from world space so the icon rotates with the camera,
always showing the correct X/Y/Z orientation relative to the drawing plane.
Each axis has its canonical color (X=red, Y=green, Z=blue) with arrowheads.
Icon visibility controlled by show_ucs_icon flag (default on); toggled by
UCSICON ON/OFF command which also updates paper-space viewport entities.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-04-03 19:25:55 +03:00
commit 6a4813ce42
4 changed files with 129 additions and 6 deletions

View file

@ -2552,6 +2552,8 @@ impl H7CAD {
self.push_undo_snapshot(i, "UCSICON");
let visible = sub != "OFF";
let at_origin = sub == "ORIGIN";
// Update model-space icon flag.
self.show_ucs_icon = visible;
let mut count = 0usize;
for entity in self.tabs[i].scene.document.entities_mut() {
if let acadrust::EntityType::Viewport(vp) = entity {
@ -2564,7 +2566,7 @@ impl H7CAD {
}
self.tabs[i].dirty = true;
self.command_line.push_output(&format!(
"UCSICON {sub}: updated {count} viewport(s)."
"UCSICON {sub}: updated {count} viewport(s) + model space."
));
}
_ => {

View file

@ -47,6 +47,8 @@ pub(super) struct H7CAD {
polar_mode: bool,
/// Show grid lines in the viewport (F7).
show_grid: bool,
/// Show the UCS icon in the bottom-left corner of model space (UCSICON).
show_ucs_icon: bool,
/// Last point committed by a drawing command — used as ortho/polar base.
last_point: Option<glam::Vec3>,
/// OS window Id for the floating Layer Properties Manager (None when closed).
@ -313,6 +315,7 @@ impl H7CAD {
ortho_mode: false,
polar_mode: false,
show_grid: false,
show_ucs_icon: true,
last_point: None,
layer_window: None,
main_window: None,

View file

@ -58,20 +58,32 @@ impl H7CAD {
vec![]
};
let (vw, vh) = tab.scene.selection.borrow().vp_size;
let vp_bounds = iced::Rectangle { x: 0.0, y: 0.0, width: vw, height: vh };
let grid = if self.show_grid {
let (vw, vh) = tab.scene.selection.borrow().vp_size;
let bounds = iced::Rectangle { x: 0.0, y: 0.0, width: vw, height: vh };
let cam = tab.scene.camera.borrow();
let plane = grid_plane_from_camera(cam.pitch, cam.yaw);
Some(overlay::GridParams {
view_proj: cam.view_proj(bounds),
bounds,
view_proj: cam.view_proj(vp_bounds),
bounds: vp_bounds,
plane,
})
} else {
None
};
overlay::selection_overlay(sel, snap_info, grips, grid)
let ucs_icon = if self.show_ucs_icon && !is_paper {
let cam = tab.scene.camera.borrow();
Some(overlay::UcsIconParams {
view_proj: cam.view_proj(vp_bounds),
bounds: vp_bounds,
})
} else {
None
};
overlay::selection_overlay(sel, snap_info, grips, grid, ucs_icon)
};
let nav = container(overlay::nav_toolbar())