ucs: dynamic UCS on planar solid faces

With dynamic UCS on (UCSDETECT 1, toggled with F6), a command's first
point picked on a planar face of a solid sets the UCS to that face for
the rest of the command, so 2D commands draw on the face (#1711). An
unsnapped first point lands on the face itself; a snapped point off the
face's plane belongs to other geometry and leaves the UCS alone. The
previous UCS comes back when the command ends. UCS commands themselves
are left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-15 11:40:39 +03:00
1 parent 19ecf78bd9
commit cccea4008b
33 files changed
+193

No files matched your search

+2
View File
@@ -779,6 +779,7 @@ impl OpenCADStudio {
polar_increment_deg: self.polar_increment_deg,
zoom_wheel_reversed: self.zoom_wheel_reversed,
navigation_style: self.navigation_style,
dynamic_ucs: self.dynamic_ucs,
touchpad_pan: self.touchpad_pan,
ui_scale_percent: self.ui_scale_percent,
zoom_factor: self.zoom_factor,
@@ -915,6 +916,7 @@ impl OpenCADStudio {
self.polar_increment_deg = s.polar_increment_deg;
self.zoom_wheel_reversed = s.zoom_wheel_reversed;
self.navigation_style = s.navigation_style;
self.dynamic_ucs = s.dynamic_ucs;
self.touchpad_pan = s.touchpad_pan;
self.ui_scale_percent = crate::app::settings::clamp_ui_scale(s.ui_scale_percent);
self.zoom_factor = crate::app::settings::clamp_zoom_factor(s.zoom_factor);
+32
View File
@@ -374,6 +374,7 @@ impl OpenCADStudio {
// document's selection.
let before = self.tabs.get(self.active_tab).map(|tab| tab.id);
let task = self.update_message(msg);
self.restore_dynamic_ucs_after_command();
if self.tabs.get(self.active_tab).map(|tab| tab.id) != before {
self.sync_underlay_tab();
}
@@ -396,6 +397,25 @@ impl OpenCADStudio {
task
}
/// A dynamic UCS lasts for the command that set it: once that tab's
/// command has ended, the UCS it replaced comes back.
fn restore_dynamic_ucs_after_command(&mut self) {
let Some((tab_id, _)) = self.dynamic_ucs_restore else {
return;
};
let Some(i) = self.tabs.iter().position(|tab| tab.id == tab_id) else {
self.dynamic_ucs_restore = None;
return;
};
if self.tabs[i].active_cmd.is_some() {
return;
}
if let Some((_, saved)) = self.dynamic_ucs_restore.take() {
self.tabs[i].active_ucs = saved;
self.tabs[i].sync_ucs_to_scene();
}
}
/// The Frames list on the ribbon shows the active drawing's frame state
/// (FRAME), however it was changed: the list, a command, undo or another
/// drawing.
@@ -9245,6 +9265,18 @@ impl OpenCADStudio {
Task::none()
}
Message::ToggleDynamicUcs => {
self.dynamic_ucs = !self.dynamic_ucs;
self.command_line.push_output(if self.dynamic_ucs {
crate::t!("Dynamic UCS: on")
} else {
crate::t!("Dynamic UCS: off")
}
.as_ref());
self.persist_settings_if_changed();
Task::none()
}
Message::NavigationStyleChanged(style) => {
self.navigation_style = style;
self.persist_settings_if_changed();
+6
View File
@@ -4325,6 +4325,7 @@ impl OpenCADStudio {
};
let snap_taken = self.tabs[i].snap_result.take();
let snapped = snap_taken.is_some();
let tangent_obj_at_click = snap_taken.and_then(|s| s.tangent_obj);
let world_pt = {
@@ -5019,6 +5020,11 @@ impl OpenCADStudio {
self.finish_command_click(i);
return Task::none();
}
// An unsnapped first point on a solid face lands on the face.
let world_pt = match self.apply_dynamic_ucs(snapped.then_some(world_pt)) {
Some(on_face) if !snapped => on_face,
_ => world_pt,
};
self.last_point = Some(world_pt);
// The one-shot snap override is spent by this pick —
// restore the running osnap configuration (#337).