Files
cadcraft/crates/engine/src/cmd/modify/transform.rs
T
Dylan M. Taylor 2721aa8982 Enforce Rust coding standards via Clippy (#2239)
* chore(clippy): enforce workspace Rust quality gates

* refactor(doc): use explicit entity imports

* refactor(constraints): split residual evaluation

* refactor(fonts): split mtext layout

* refactor(render): use explicit entity imports

* fix(fonts): retain height for empty paragraphs

* fix(doc): repair dimension and dynamic block links

* refactor(render): split entity drawing helpers

* docs: document workspace lint inheritance

* refactor(render): isolate dimension geometry

* refactor(engine): use explicit imports

* refactor(engine): split complex edit paths into helpers

* refactor(io): split DXF read and write helpers

* refactor(ui): split canvas interaction and drawing

* refactor(ui): split command and drawing helpers

* test(i18n): split locale preference checks

* fix(ui): use current egui scope id API

* fix(dim): preserve text offset and ellipse grip behavior
2026-10-27 15:55:09 -04:00

107 lines
3.7 KiB
Rust

use super::{Entity, Handle, Mat3, Result, Session, Value, Vec2, bad, bool_or, f64_req, json, point_param, point_req, targets};
// ---------------- core operations ----------------
pub(crate) fn transform_entities(s: &mut Session, hs: &[Handle], m: &Mat3, copy: bool) -> Result<Vec<Handle>> {
let d = s.doc_mut()?;
let mut out = Vec::new();
for h in hs {
let Some(sp) = d.space_of(*h) else { continue };
if copy {
let Some(e) = d.entity(*h).map(|e| (**e).clone()) else { continue };
let nh = d.new_handle();
let mut ne = Entity { handle: nh, ..e };
ne.kind.transform(m);
if let Some(st) = d.space_mut(&sp) {
st.push(ne);
out.push(nh);
}
} else {
let locked = d.entity(*h).and_then(|e| d.layer(&e.common.layer)).is_some_and(|l| l.locked);
if locked {
continue;
}
d.modify_entity(*h, |e| e.kind.transform(m))?;
out.push(*h);
}
}
Ok(out)
}
pub(super) fn erase(s: &mut Session, hs: &[Handle]) -> Result<usize> {
let d = s.doc_mut()?;
let mut n = 0;
for h in hs {
let locked = d.entity(*h).and_then(|e| d.layer(&e.common.layer)).is_some_and(|l| l.locked);
if !locked && d.remove_entity(*h).is_some() {
n += 1;
}
}
s.set_selection(Vec::new());
Ok(n)
}
pub(super) fn run_erase(s: &mut Session, p: &Value) -> Result<Value> {
let hs = targets(s, p)?;
let n = erase(s, &hs)?;
Ok(json!({ "erased": n }))
}
pub(super) fn delta_of(cmd: &str, p: &Value) -> Result<Vec2> {
if let Some(d) = point_param(p, "delta") {
return Ok(d);
}
let a = point_req(cmd, p, "from")?;
let b = point_req(cmd, p, "to")?;
Ok(b - a)
}
pub(super) fn run_move(s: &mut Session, p: &Value) -> Result<Value> {
let hs = targets(s, p)?;
let d = delta_of("move", p)?;
let moved = transform_entities(s, &hs, &Mat3::translate(d), false)?;
Ok(json!({ "moved": moved.len() }))
}
pub(super) fn run_copy(s: &mut Session, p: &Value) -> Result<Value> {
let hs = targets(s, p)?;
let d = delta_of("copy", p)?;
let n = p.get("count").and_then(Value::as_u64).unwrap_or(1).clamp(1, 10_000);
let mut out = Vec::new();
for k in 1..=n {
out.extend(transform_entities(s, &hs, &Mat3::translate(d * k as f64), true)?.into_iter().map(|h| h.hex()));
}
Ok(json!({ "handles": out }))
}
pub(super) fn run_rotate(s: &mut Session, p: &Value) -> Result<Value> {
let hs = targets(s, p)?;
let base = point_req("rotate", p, "base")?;
let a = f64_req("rotate", p, "angle")?.to_radians();
let r = transform_entities(s, &hs, &Mat3::rotate_about(base, a), bool_or(p, "copy", false))?;
Ok(json!({ "handles": r.iter().map(|h| h.hex()).collect::<Vec<_>>() }))
}
pub(super) fn run_scale(s: &mut Session, p: &Value) -> Result<Value> {
let hs = targets(s, p)?;
let base = point_req("scale", p, "base")?;
let f = f64_req("scale", p, "factor")?;
if f <= 0.0 {
return Err(bad("scale", "factor must be positive"));
}
let r = transform_entities(s, &hs, &Mat3::scale_about(base, f), bool_or(p, "copy", false))?;
Ok(json!({ "handles": r.iter().map(|h| h.hex()).collect::<Vec<_>>() }))
}
pub(super) fn run_mirror(s: &mut Session, p: &Value) -> Result<Value> {
let hs = targets(s, p)?;
let a = point_req("mirror", p, "p1")?;
let b = point_req("mirror", p, "p2")?;
if a.near(b, 1e-12) {
return Err(bad("mirror", "mirror line points coincide"));
}
let erase_src = bool_or(p, "erase", false);
let r = transform_entities(s, &hs, &Mat3::mirror(a, b), !erase_src)?;
Ok(json!({ "handles": r.iter().map(|h| h.hex()).collect::<Vec<_>>() }))
}