mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-30 22:06:49 -06:00
* 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
107 lines
3.7 KiB
Rust
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<_>>() }))
|
|
}
|