fix: UCS FACE/OBJECT pick from the interactive prompt; reuse ocs_axes

Follow-up to #1341:
- interactive UCS offers Face / OBject; bare UCS OBJECT starts an entity pick
- one UcsPickCommand serves both FACE and OBJECT
- ucs_from_normal calls the existing arbitrary-axis helper instead of a copy
- prompt catalogue key and en-US text follow the new option list

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Fable 5.1 committed 2026-09-18 18:42:44 +03:00
1 parent 3ba4ab9e7f
commit 9978713acc
7 files changed
+50 -43

No files matched your search

+8 -4
View File
@@ -338,7 +338,7 @@ impl OpenCADStudio {
// surface point, so both entry points share the
// plane construction below.
None => {
let picker = crate::command::UcsFaceCommand;
let picker = crate::command::UcsPickCommand { face: true };
self.command_line.push_info(&picker.prompt());
self.tabs[i].active_cmd = Some(Box::new(picker));
}
@@ -386,9 +386,13 @@ impl OpenCADStudio {
})
.map(acadrust::Handle::new);
match target {
None => self
.command_line
.push_error(crate::t!("Usage: UCS OBJECT <handle>").as_ref()),
// Bare `UCS OBJECT` starts the pick; the click
// comes back through this arm with the handle.
None => {
let picker = crate::command::UcsPickCommand { face: false };
self.command_line.push_info(&picker.prompt());
self.tabs[i].active_cmd = Some(Box::new(picker));
}
Some(handle) => {
let built = self.tabs[i]
.scene
+2 -1
View File
@@ -739,9 +739,10 @@ inventory::submit!(crate::command::CommandRegistration {
"SELSIM",
// Draw a new object of the same type as the selected one. (#239)
"ADDSELECTED",
// Outline of origin planes, open sketch and solid bodies.
"BROWSER",
// Open and close a sketch: a drawing plane, a square-on view and
// snapping, held together until the profile is done.
"BROWSER",
"CREATESKETCH",
"FINISHSKETCH",
"SHEETSET",
+7 -22
View File
@@ -249,35 +249,20 @@ pub(super) fn ucs_z_axis(ucs: &Ucs) -> glam::DVec3 {
/// Build a UCS whose Z axis is `normal`, anchored at `origin`.
///
/// X and Y come from the Arbitrary Axis Algorithm, the same rule DWG uses to
/// turn an extrusion direction into an object coordinate system. Picking the
/// world axis that is *least* parallel to the normal keeps the cross product
/// well conditioned, and makes the result stable: the same face always yields
/// the same X axis, so a sketch drawn on it does not spin when the UCS is
/// rebuilt.
///
/// Returns `None` when `normal` is degenerate and defines no plane.
/// X and Y come from the arbitrary-axis algorithm in
/// [`crate::scene::view::transform::ocs_axes`], the same frame entities get
/// from their extrusion direction, so the same face always yields the same X
/// axis. Returns `None` when `normal` is degenerate and defines no plane.
pub(super) fn ucs_from_normal(origin: glam::DVec3, normal: glam::DVec3) -> Option<Ucs> {
let z = normal.normalize_or_zero();
if z.length_squared() < 1e-12 {
return None;
}
// 1/64 is the threshold the DWG spec names for "close enough to the world
// Z axis that using it as the reference would be ill conditioned".
let reference = if z.x.abs() < 1.0 / 64.0 && z.y.abs() < 1.0 / 64.0 {
glam::DVec3::Y
} else {
glam::DVec3::Z
};
let x = reference.cross(z).normalize_or_zero();
if x.length_squared() < 1e-12 {
return None;
}
let y = z.cross(x).normalize_or_zero();
let ((xx, xy, xz), (yx, yy, yz)) = crate::scene::view::transform::ocs_axes((z.x, z.y, z.z));
let mut ucs = Ucs::new("*ACTIVE*");
ucs.origin = acadrust::types::Vector3::new(origin.x, origin.y, origin.z);
ucs.x_axis = acadrust::types::Vector3::new(x.x, x.y, x.z);
ucs.y_axis = acadrust::types::Vector3::new(y.x, y.y, y.z);
ucs.x_axis = acadrust::types::Vector3::new(xx, xy, xz);
ucs.y_axis = acadrust::types::Vector3::new(yx, yy, yz);
Some(ucs)
}