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:
parent
d63602161f
commit
0c2a97e17e
5 changed files with 128 additions and 35 deletions
|
|
@ -195,6 +195,28 @@ impl DocumentTab {
|
||||||
self.scene.viewcube_ucs = self.ucs_xform().rotation_mat();
|
self.scene.viewcube_ucs = self.ucs_xform().rotation_mat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// UCS→render(wire)-space affine for commands that build axis-aligned
|
||||||
|
/// geometry. Columns are the UCS axes; translation is the UCS origin in wire
|
||||||
|
/// space. Identity outside model space (no UCS there).
|
||||||
|
pub(super) fn ucs_wire_affine(&self) -> glam::Mat4 {
|
||||||
|
if self.scene.current_layout != "Model" {
|
||||||
|
return glam::Mat4::IDENTITY;
|
||||||
|
}
|
||||||
|
let (o, x, y, z) = self.ucs_xform().axes();
|
||||||
|
let wo = self.scene.world_offset;
|
||||||
|
let origin = glam::Vec3::new(
|
||||||
|
o.x - wo[0] as f32,
|
||||||
|
o.y - wo[1] as f32,
|
||||||
|
o.z - wo[2] as f32,
|
||||||
|
);
|
||||||
|
glam::Mat4::from_cols(
|
||||||
|
x.extend(0.0),
|
||||||
|
y.extend(0.0),
|
||||||
|
z.extend(0.0),
|
||||||
|
origin.extend(1.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Grid origin (render/wire space) and UCS→world rotation for grid snap and
|
/// Grid origin (render/wire space) and UCS→world rotation for grid snap and
|
||||||
/// the grid overlay. Identity / origin-at-zero outside model space.
|
/// the grid overlay. Identity / origin-at-zero outside model space.
|
||||||
pub(super) fn ucs_grid_basis(&self) -> (glam::Vec3, glam::Mat4) {
|
pub(super) fn ucs_grid_basis(&self) -> (glam::Vec3, glam::Mat4) {
|
||||||
|
|
|
||||||
|
|
@ -1660,6 +1660,7 @@ impl OpenCADStudio {
|
||||||
.map(|c| c.wants_text_input())
|
.map(|c| c.wants_text_input())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
|
self.push_ucs_to_cmd(i);
|
||||||
if let Some(result) = self.tabs[i]
|
if let Some(result) = self.tabs[i]
|
||||||
.active_cmd
|
.active_cmd
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
|
@ -1687,6 +1688,7 @@ impl OpenCADStudio {
|
||||||
self.dyn_user_reshaped = false;
|
self.dyn_user_reshaped = false;
|
||||||
self.sync_dyn_fields();
|
self.sync_dyn_fields();
|
||||||
self.reset_tracking_after_point();
|
self.reset_tracking_after_point();
|
||||||
|
self.push_ucs_to_cmd(i);
|
||||||
let result = self.tabs[i].active_cmd.as_mut().map(|c| c.on_point(pt));
|
let result = self.tabs[i].active_cmd.as_mut().map(|c| c.on_point(pt));
|
||||||
if let Some(r) = result {
|
if let Some(r) = result {
|
||||||
let task = self.apply_cmd_result(r);
|
let task = self.apply_cmd_result(r);
|
||||||
|
|
@ -1728,6 +1730,7 @@ impl OpenCADStudio {
|
||||||
self.dyn_user_reshaped = false;
|
self.dyn_user_reshaped = false;
|
||||||
self.sync_dyn_fields();
|
self.sync_dyn_fields();
|
||||||
self.reset_tracking_after_point();
|
self.reset_tracking_after_point();
|
||||||
|
self.push_ucs_to_cmd(i);
|
||||||
let result = self.tabs[i].active_cmd.as_mut().map(|c| c.on_point(wcs_pt));
|
let result = self.tabs[i].active_cmd.as_mut().map(|c| c.on_point(wcs_pt));
|
||||||
if let Some(r) = result {
|
if let Some(r) = result {
|
||||||
let task = self.apply_cmd_result(r);
|
let task = self.apply_cmd_result(r);
|
||||||
|
|
@ -1744,6 +1747,7 @@ impl OpenCADStudio {
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.push_ucs_to_cmd(i);
|
||||||
if let Some(result) = self.tabs[i]
|
if let Some(result) = self.tabs[i]
|
||||||
.active_cmd
|
.active_cmd
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
|
@ -2971,6 +2975,9 @@ impl OpenCADStudio {
|
||||||
s.screen.y += tile_b.y;
|
s.screen.y += tile_b.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Give the command the current UCS before it builds its
|
||||||
|
// rubber-band preview (and, by persistence, its commit).
|
||||||
|
self.push_ucs_to_cmd(i);
|
||||||
let mut previews = if needs_structure {
|
let mut previews = if needs_structure {
|
||||||
let mut p = self.tabs[i]
|
let mut p = self.tabs[i]
|
||||||
.active_cmd
|
.active_cmd
|
||||||
|
|
@ -8347,6 +8354,16 @@ impl OpenCADStudio {
|
||||||
/// values. Locked fields use their typed buffer; the rest fall back to
|
/// values. Locked fields use their typed buffer; the rest fall back to
|
||||||
/// the live cursor-derived value. Returns `None` when the field set
|
/// the live cursor-derived value. Returns `None` when the field set
|
||||||
/// isn't one we know how to turn into a point.
|
/// isn't one we know how to turn into a point.
|
||||||
|
/// Hand the active command the current UCS (as a UCS→wire affine) so
|
||||||
|
/// axis-aligned constructions build square to the user's coordinate system.
|
||||||
|
/// No-op for commands that don't override `set_ucs`.
|
||||||
|
fn push_ucs_to_cmd(&mut self, i: usize) {
|
||||||
|
let ucs = self.tabs[i].ucs_wire_affine();
|
||||||
|
if let Some(c) = self.tabs[i].active_cmd.as_mut() {
|
||||||
|
c.set_ucs(ucs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn dyn_resolve_point(&self) -> Option<glam::Vec3> {
|
fn dyn_resolve_point(&self) -> Option<glam::Vec3> {
|
||||||
use super::document::DynComponent;
|
use super::document::DynComponent;
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
|
|
@ -8606,6 +8623,7 @@ impl OpenCADStudio {
|
||||||
self.dyn_user_reshaped = false;
|
self.dyn_user_reshaped = false;
|
||||||
self.sync_dyn_fields();
|
self.sync_dyn_fields();
|
||||||
self.reset_tracking_after_point();
|
self.reset_tracking_after_point();
|
||||||
|
self.push_ucs_to_cmd(i);
|
||||||
let result = self.tabs[i].active_cmd.as_mut().map(|c| c.on_point(pt));
|
let result = self.tabs[i].active_cmd.as_mut().map(|c| c.on_point(pt));
|
||||||
let task = result.map(|r| self.apply_cmd_result(r))?;
|
let task = result.map(|r| self.apply_cmd_result(r))?;
|
||||||
self.refresh_active_cmd_preview(i);
|
self.refresh_active_cmd_preview(i);
|
||||||
|
|
@ -8664,6 +8682,7 @@ impl OpenCADStudio {
|
||||||
self.dyn_user_reshaped = false;
|
self.dyn_user_reshaped = false;
|
||||||
self.sync_dyn_fields();
|
self.sync_dyn_fields();
|
||||||
self.reset_tracking_after_point();
|
self.reset_tracking_after_point();
|
||||||
|
self.push_ucs_to_cmd(i);
|
||||||
let result = self.tabs[i].active_cmd.as_mut().map(|c| c.on_point(pt));
|
let result = self.tabs[i].active_cmd.as_mut().map(|c| c.on_point(pt));
|
||||||
for f in self.tabs[i].dyn_fields.iter_mut() {
|
for f in self.tabs[i].dyn_fields.iter_mut() {
|
||||||
f.buffer = None;
|
f.buffer = None;
|
||||||
|
|
|
||||||
|
|
@ -416,6 +416,13 @@ pub trait CadCommand: Send {
|
||||||
/// Current prompt string to display in the command line.
|
/// Current prompt string to display in the command line.
|
||||||
fn prompt(&self) -> String;
|
fn prompt(&self) -> String;
|
||||||
|
|
||||||
|
/// Push the active UCS into the command as a UCS→render(wire)-space affine
|
||||||
|
/// (identity = plain WCS). Commands that build axis-aligned geometry (RECT,
|
||||||
|
/// rectangular ARRAY, …) override this to store it and rotate their implicit
|
||||||
|
/// axes into the UCS; most commands work purely from picked points and
|
||||||
|
/// ignore it. Called before each point / preview dispatch.
|
||||||
|
fn set_ucs(&mut self, _ucs: glam::Mat4) {}
|
||||||
|
|
||||||
/// Called when the user left-clicks in the viewport (point pick).
|
/// Called when the user left-clicks in the viewport (point pick).
|
||||||
fn on_point(&mut self, pt: Vec3) -> CmdResult;
|
fn on_point(&mut self, pt: Vec3) -> CmdResult;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,32 @@ use acadrust::{EntityType, LwPolyline};
|
||||||
use crate::command::{CadCommand, CmdResult};
|
use crate::command::{CadCommand, CmdResult};
|
||||||
use crate::modules::IconKind;
|
use crate::modules::IconKind;
|
||||||
use crate::scene::model::wire_model::WireModel;
|
use crate::scene::model::wire_model::WireModel;
|
||||||
use glam::Vec3;
|
use glam::{Mat4, Vec3};
|
||||||
|
|
||||||
|
/// Build the four corners of an axis-aligned box between opposite corners `a`
|
||||||
|
/// and `b`, axis-aligned in the active UCS (`ucs` = UCS→wire affine, identity =
|
||||||
|
/// world). The two given corners stay put; the other two are placed square to
|
||||||
|
/// the UCS axes instead of the world axes.
|
||||||
|
fn ucs_box_corners(a: Vec3, b: Vec3, ucs: Mat4) -> [Vec3; 4] {
|
||||||
|
let inv = ucs.inverse();
|
||||||
|
let au = inv.transform_point3(a);
|
||||||
|
let bu = inv.transform_point3(b);
|
||||||
|
[
|
||||||
|
ucs.transform_point3(Vec3::new(au.x, au.y, au.z)),
|
||||||
|
ucs.transform_point3(Vec3::new(bu.x, au.y, au.z)),
|
||||||
|
ucs.transform_point3(Vec3::new(bu.x, bu.y, au.z)),
|
||||||
|
ucs.transform_point3(Vec3::new(au.x, bu.y, au.z)),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Four corners of a box centred at `c` with half-extents taken from `corner`,
|
||||||
|
/// axis-aligned in the active UCS (`ucs` = UCS→wire affine, identity = world).
|
||||||
|
fn ucs_box_around_center(c: Vec3, corner: Vec3, ucs: Mat4) -> [Vec3; 4] {
|
||||||
|
let d = ucs.inverse().transform_vector3(corner - c);
|
||||||
|
let rx = ucs.transform_vector3(Vec3::new(d.x.abs(), 0.0, 0.0));
|
||||||
|
let ry = ucs.transform_vector3(Vec3::new(0.0, d.y.abs(), 0.0));
|
||||||
|
[c - rx - ry, c + rx - ry, c + rx + ry, c - rx + ry]
|
||||||
|
}
|
||||||
|
|
||||||
const TAU: f32 = std::f32::consts::TAU;
|
const TAU: f32 = std::f32::consts::TAU;
|
||||||
const PI: f32 = std::f32::consts::PI;
|
const PI: f32 = std::f32::consts::PI;
|
||||||
|
|
@ -120,11 +145,12 @@ fn angle_xy(from: Vec3, to: Vec3) -> f32 {
|
||||||
|
|
||||||
pub struct RectCommand {
|
pub struct RectCommand {
|
||||||
a: Option<Vec3>,
|
a: Option<Vec3>,
|
||||||
|
ucs: Mat4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RectCommand {
|
impl RectCommand {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { a: None }
|
Self { a: None, ucs: Mat4::IDENTITY }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,6 +158,9 @@ impl CadCommand for RectCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"RECT"
|
"RECT"
|
||||||
}
|
}
|
||||||
|
fn set_ucs(&mut self, ucs: Mat4) {
|
||||||
|
self.ucs = ucs;
|
||||||
|
}
|
||||||
fn prompt(&self) -> String {
|
fn prompt(&self) -> String {
|
||||||
if self.a.is_none() {
|
if self.a.is_none() {
|
||||||
"RECT Specify first corner:".into()
|
"RECT Specify first corner:".into()
|
||||||
|
|
@ -145,12 +174,15 @@ impl CadCommand for RectCommand {
|
||||||
self.a = Some(pt);
|
self.a = Some(pt);
|
||||||
CmdResult::NeedPoint
|
CmdResult::NeedPoint
|
||||||
}
|
}
|
||||||
Some(a) => CmdResult::CommitAndExit(make_pline(&[
|
Some(a) => {
|
||||||
[a.x as f64, a.y as f64],
|
let c = ucs_box_corners(a, pt, self.ucs);
|
||||||
[pt.x as f64, a.y as f64],
|
CmdResult::CommitAndExit(make_pline(&[
|
||||||
[pt.x as f64, pt.y as f64],
|
[c[0].x as f64, c[0].y as f64],
|
||||||
[a.x as f64, pt.y as f64],
|
[c[1].x as f64, c[1].y as f64],
|
||||||
])),
|
[c[2].x as f64, c[2].y as f64],
|
||||||
|
[c[3].x as f64, c[3].y as f64],
|
||||||
|
]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn on_enter(&mut self) -> CmdResult {
|
fn on_enter(&mut self) -> CmdResult {
|
||||||
|
|
@ -161,11 +193,12 @@ impl CadCommand for RectCommand {
|
||||||
}
|
}
|
||||||
fn on_mouse_move(&mut self, pt: Vec3) -> Option<WireModel> {
|
fn on_mouse_move(&mut self, pt: Vec3) -> Option<WireModel> {
|
||||||
let a = self.a?;
|
let a = self.a?;
|
||||||
|
let c = ucs_box_corners(a, pt, self.ucs);
|
||||||
Some(wire_loop(vec![
|
Some(wire_loop(vec![
|
||||||
[a.x, a.y, a.z],
|
[c[0].x, c[0].y, c[0].z],
|
||||||
[pt.x, a.y, a.z],
|
[c[1].x, c[1].y, c[1].z],
|
||||||
[pt.x, pt.y, a.z],
|
[c[2].x, c[2].y, c[2].z],
|
||||||
[a.x, pt.y, a.z],
|
[c[3].x, c[3].y, c[3].z],
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
|
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
|
||||||
|
|
@ -302,11 +335,12 @@ impl CadCommand for RectRotCommand {
|
||||||
|
|
||||||
pub struct RectCenCommand {
|
pub struct RectCenCommand {
|
||||||
center: Option<Vec3>,
|
center: Option<Vec3>,
|
||||||
|
ucs: Mat4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RectCenCommand {
|
impl RectCenCommand {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { center: None }
|
Self { center: None, ucs: Mat4::IDENTITY }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,6 +348,9 @@ impl CadCommand for RectCenCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"RECT_CEN"
|
"RECT_CEN"
|
||||||
}
|
}
|
||||||
|
fn set_ucs(&mut self, ucs: Mat4) {
|
||||||
|
self.ucs = ucs;
|
||||||
|
}
|
||||||
fn prompt(&self) -> String {
|
fn prompt(&self) -> String {
|
||||||
if self.center.is_none() {
|
if self.center.is_none() {
|
||||||
"RECT CEN Specify center point:".into()
|
"RECT CEN Specify center point:".into()
|
||||||
|
|
@ -328,15 +365,12 @@ impl CadCommand for RectCenCommand {
|
||||||
CmdResult::NeedPoint
|
CmdResult::NeedPoint
|
||||||
}
|
}
|
||||||
Some(c) => {
|
Some(c) => {
|
||||||
let hw = (pt.x - c.x).abs() as f64;
|
let q = ucs_box_around_center(c, pt, self.ucs);
|
||||||
let hh = (pt.y - c.y).abs() as f64;
|
|
||||||
let cx = c.x as f64;
|
|
||||||
let cy = c.y as f64;
|
|
||||||
CmdResult::CommitAndExit(make_pline(&[
|
CmdResult::CommitAndExit(make_pline(&[
|
||||||
[cx - hw, cy - hh],
|
[q[0].x as f64, q[0].y as f64],
|
||||||
[cx + hw, cy - hh],
|
[q[1].x as f64, q[1].y as f64],
|
||||||
[cx + hw, cy + hh],
|
[q[2].x as f64, q[2].y as f64],
|
||||||
[cx - hw, cy + hh],
|
[q[3].x as f64, q[3].y as f64],
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -349,13 +383,12 @@ impl CadCommand for RectCenCommand {
|
||||||
}
|
}
|
||||||
fn on_mouse_move(&mut self, pt: Vec3) -> Option<WireModel> {
|
fn on_mouse_move(&mut self, pt: Vec3) -> Option<WireModel> {
|
||||||
let c = self.center?;
|
let c = self.center?;
|
||||||
let hw = (pt.x - c.x).abs();
|
let q = ucs_box_around_center(c, pt, self.ucs);
|
||||||
let hh = (pt.y - c.y).abs();
|
|
||||||
Some(wire_loop(vec![
|
Some(wire_loop(vec![
|
||||||
[c.x - hw, c.y - hh, c.z],
|
[q[0].x, q[0].y, q[0].z],
|
||||||
[c.x + hw, c.y - hh, c.z],
|
[q[1].x, q[1].y, q[1].z],
|
||||||
[c.x + hw, c.y + hh, c.z],
|
[q[2].x, q[2].y, q[2].z],
|
||||||
[c.x - hw, c.y + hh, c.z],
|
[q[3].x, q[3].y, q[3].z],
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
|
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// 1. Center point → 2. Item count (text) → 3. Total angle in degrees (text)
|
// 1. Center point → 2. Item count (text) → 3. Total angle in degrees (text)
|
||||||
|
|
||||||
use acadrust::Handle;
|
use acadrust::Handle;
|
||||||
use glam::Vec3;
|
use glam::{Mat4, Vec3};
|
||||||
|
|
||||||
use crate::command::{CadCommand, CmdResult, EntityTransform};
|
use crate::command::{CadCommand, CmdResult, EntityTransform};
|
||||||
use crate::modules::draw::defaults;
|
use crate::modules::draw::defaults;
|
||||||
|
|
@ -62,6 +62,7 @@ pub struct ArrayRectCommand {
|
||||||
default_cols: u32,
|
default_cols: u32,
|
||||||
default_row_sp: f32,
|
default_row_sp: f32,
|
||||||
default_col_sp: f32,
|
default_col_sp: f32,
|
||||||
|
ucs: Mat4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArrayRectCommand {
|
impl ArrayRectCommand {
|
||||||
|
|
@ -74,20 +75,27 @@ impl ArrayRectCommand {
|
||||||
default_cols: defaults::get_array_cols() as u32,
|
default_cols: defaults::get_array_cols() as u32,
|
||||||
default_row_sp: defaults::get_array_row_sp(),
|
default_row_sp: defaults::get_array_row_sp(),
|
||||||
default_col_sp: defaults::get_array_col_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();
|
let mut t = Vec::new();
|
||||||
for r in 0..rows {
|
for r in 0..rows {
|
||||||
for c in 0..cols {
|
for c in 0..cols {
|
||||||
if r == 0 && c == 0 {
|
if r == 0 && c == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
t.push(EntityTransform::Translate(Vec3::new(
|
t.push(EntityTransform::Translate(ucs.transform_vector3(
|
||||||
col_sp * c as f32,
|
Vec3::new(col_sp * c as f32, row_sp * r as f32, 0.0),
|
||||||
row_sp * r as f32,
|
|
||||||
0.0,
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +108,10 @@ impl CadCommand for ArrayRectCommand {
|
||||||
"ARRAYRECT"
|
"ARRAYRECT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_ucs(&mut self, ucs: Mat4) {
|
||||||
|
self.ucs = ucs;
|
||||||
|
}
|
||||||
|
|
||||||
fn prompt(&self) -> String {
|
fn prompt(&self) -> String {
|
||||||
match self.step {
|
match self.step {
|
||||||
RectStep::Rows => format!("ARRAYRECT Enter row count <{}>:", self.default_rows),
|
RectStep::Rows => format!("ARRAYRECT Enter row count <{}>:", self.default_rows),
|
||||||
|
|
@ -176,7 +188,7 @@ impl CadCommand for ArrayRectCommand {
|
||||||
};
|
};
|
||||||
Some(CmdResult::BatchCopy(
|
Some(CmdResult::BatchCopy(
|
||||||
self.handles.clone(),
|
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),
|
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()
|
.iter()
|
||||||
.flat_map(|t| {
|
.flat_map(|t| {
|
||||||
if let EntityTransform::Translate(delta) = t {
|
if let EntityTransform::Translate(delta) = t {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue