feat(ucs): build axis-aligned shapes in the active UCS

Commands that construct geometry with implicit axis directions ignored
the UCS, so a rectangle drawn under a rotated UCS came out world-aligned.
Give commands the UCS and build square to it:

- CadCommand::set_ucs(Mat4) (UCS→wire affine, default no-op); the host
  pushes the current UCS via App::push_ucs_to_cmd before each point /
  preview / text dispatch (DocumentTab::ucs_wire_affine, identity outside
  model space). New axis-aligned commands just override set_ucs.
- RECT (two-corner) and RECT_CEN (center) place their derived corners on
  the UCS axes (ucs_box_corners / ucs_box_around_center).
- ARRAYRECT row/column offsets run along the UCS axes.

RECT_ROT, POLYGON, and ELLIPSE already take their orientation from picked
points, so they need no change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-22 17:30:42 +03:00
1 parent d63602161f
commit 0c2a97e17e
5 files changed
+128 -35

No files matched your search

+20 -8
View File
@@ -14,7 +14,7 @@
// 1. Center point → 2. Item count (text) → 3. Total angle in degrees (text)
use acadrust::Handle;
use glam::Vec3;
use glam::{Mat4, Vec3};
use crate::command::{CadCommand, CmdResult, EntityTransform};
use crate::modules::draw::defaults;
@@ -62,6 +62,7 @@ pub struct ArrayRectCommand {
default_cols: u32,
default_row_sp: f32,
default_col_sp: f32,
ucs: Mat4,
}
impl ArrayRectCommand {
@@ -74,20 +75,27 @@ impl ArrayRectCommand {
default_cols: defaults::get_array_cols() as u32,
default_row_sp: defaults::get_array_row_sp(),
default_col_sp: defaults::get_array_col_sp(),
ucs: Mat4::IDENTITY,
}
}
fn build_transforms(rows: u32, cols: u32, row_sp: f32, col_sp: f32) -> Vec<EntityTransform> {
/// Row/column offsets run along the active UCS axes (`ucs` rotates the
/// world-frame grid step), identity = world.
fn build_transforms(
rows: u32,
cols: u32,
row_sp: f32,
col_sp: f32,
ucs: Mat4,
) -> Vec<EntityTransform> {
let mut t = Vec::new();
for r in 0..rows {
for c in 0..cols {
if r == 0 && c == 0 {
continue;
}
t.push(EntityTransform::Translate(Vec3::new(
col_sp * c as f32,
row_sp * r as f32,
0.0,
t.push(EntityTransform::Translate(ucs.transform_vector3(
Vec3::new(col_sp * c as f32, row_sp * r as f32, 0.0),
)));
}
}
@@ -100,6 +108,10 @@ impl CadCommand for ArrayRectCommand {
"ARRAYRECT"
}
fn set_ucs(&mut self, ucs: Mat4) {
self.ucs = ucs;
}
fn prompt(&self) -> String {
match self.step {
RectStep::Rows => format!("ARRAYRECT Enter row count <{}>:", self.default_rows),
@@ -176,7 +188,7 @@ impl CadCommand for ArrayRectCommand {
};
Some(CmdResult::BatchCopy(
self.handles.clone(),
Self::build_transforms(rows, cols, row_sp, col_sp),
Self::build_transforms(rows, cols, row_sp, col_sp, self.ucs),
))
}
}
@@ -201,7 +213,7 @@ impl CadCommand for ArrayRectCommand {
}
RectStep::ColSp { rows, cols, row_sp } => (rows, cols, row_sp, self.default_col_sp),
};
Self::build_transforms(rows, cols, row_sp, col_sp)
Self::build_transforms(rows, cols, row_sp, col_sp, self.ucs)
.iter()
.flat_map(|t| {
if let EntityTransform::Translate(delta) = t {