From 31ed9117143fac845f1edc0ea84050ef89123b78 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sun, 21 Jun 2026 01:52:53 +0300 Subject: [PATCH] fix(3d): place and move ACIS solids and tilted-normal blocks correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump acadrust to pick up real ACIS body transforms (so moving/copying/ rotating a 3D solid relocates its geometry) and OCS-aware INSERT placement. - body_transform: read the SAT transform record from its first 13 float tokens instead of by raw index, which skipped the matrix because the record leads with a book-keeping pointer — so the body placement was silently ignored and solids never honored their stored transform. - Insert grips: the grip lives in world space but insert_point is stored in the block's OCS, so grip display and grip-drag now round-trip through the OCS. A block whose extrusion normal isn't +Z no longer shows its grip in the wrong place or drags along the wrong axes. Co-Authored-By: Claude Opus 4.8 --- Cargo.lock | 2 +- src/entities/insert.rs | 28 +++++++++++++++------------- src/scene/convert/solid3d_tess.rs | 17 +++++++++++------ 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc3e5c5e2..2c6908a6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,7 +70,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "acadrust" version = "0.3.4" -source = "git+https://github.com/HakanSeven12/acadrust?branch=main#67db8eeeb63e429d301f7708ee8a4629ed3eb780" +source = "git+https://github.com/HakanSeven12/acadrust?branch=main#33cb8b72dbf9f3dc585619a31848b42c5acf8c81" dependencies = [ "ahash", "anyhow", diff --git a/src/entities/insert.rs b/src/entities/insert.rs index 7cb445f92..62b539d4d 100644 --- a/src/entities/insert.rs +++ b/src/entities/insert.rs @@ -1,5 +1,5 @@ use acadrust::entities::Insert; -use acadrust::types::{Transform, Vector3}; +use acadrust::types::{Matrix3, Transform, Vector3}; use acadrust::{EntityType, Handle}; use glam::Vec3; @@ -13,8 +13,10 @@ use crate::scene::convert::tessellate; use crate::scene::view::render; fn grips(ins: &Insert) -> Vec { - let p = glam::DVec3::new(ins.insert_point.x, ins.insert_point.y, ins.insert_point.z); - vec![square_grip(0, p)] + // `insert_point` is in the OCS defined by `normal`; the grip must sit at + // the world placement, so map it through the OCS. Identity for +Z. + let w = Matrix3::arbitrary_axis(ins.normal) * ins.insert_point; + vec![square_grip(0, glam::DVec3::new(w.x, w.y, w.z))] } fn properties(ins: &Insert) -> PropSection { @@ -50,18 +52,18 @@ fn apply_geom_prop(ins: &mut Insert, field: &str, value: &str) { } fn apply_grip(ins: &mut Insert, _grip_id: usize, apply: GripApply) { - match apply { - GripApply::Absolute(p) => { - ins.insert_point.x = p.x as f64; - ins.insert_point.y = p.y as f64; - ins.insert_point.z = p.z as f64; - } + // The grip works in world space, but `insert_point` is stored in the OCS + // defined by `normal`. Round-trip through the OCS so dragging a block + // whose extrusion direction isn't +Z moves along world axes. Identity OCS + // for a +Z normal, so this matches the old direct assignment there. + let ocs = Matrix3::arbitrary_axis(ins.normal); + let world = match apply { + GripApply::Absolute(p) => Vector3::new(p.x as f64, p.y as f64, p.z as f64), GripApply::Translate(d) => { - ins.insert_point.x += d.x as f64; - ins.insert_point.y += d.y as f64; - ins.insert_point.z += d.z as f64; + ocs * ins.insert_point + Vector3::new(d.x as f64, d.y as f64, d.z as f64) } - } + }; + ins.insert_point = ocs.transpose() * world; } fn apply_transform(ins: &mut Insert, t: &EntityTransform) { diff --git a/src/scene/convert/solid3d_tess.rs b/src/scene/convert/solid3d_tess.rs index 299982fd2..202644d42 100644 --- a/src/scene/convert/solid3d_tess.rs +++ b/src/scene/convert/solid3d_tess.rs @@ -191,13 +191,18 @@ fn tessellate_sat_lods( /// document has no transform (treated as identity). pub(crate) fn body_transform(sat: &SatDocument) -> Option<([f64; 9], [f64; 3], f64)> { let t = sat.records.iter().find(|r| r.entity_type == "transform")?; - let mut m = [0.0f64; 9]; - for (i, slot) in m.iter_mut().enumerate() { - *slot = t.token_float(i)?; + // The transform record's numeric payload is its first 13 float-valued + // tokens: 3×3 matrix, translation, scale. A leading book-keeping pointer + // (`$-1`) and the trailing rotate/reflect/shear flags aren't floats, so + // collecting float tokens skips them — reading by raw token index would + // be thrown off by the leading pointer. + let v: Vec = t.tokens.iter().filter_map(|tok| tok.as_float()).take(13).collect(); + if v.len() < 13 { + return None; } - let tr = [t.token_float(9)?, t.token_float(10)?, t.token_float(11)?]; - let scale = t.token_float(12).unwrap_or(1.0); - Some((m, tr, scale)) + let m = [v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]]; + let tr = [v[9], v[10], v[11]]; + Some((m, tr, v[12])) } /// Apply a body placement transform to a mesh. ACIS treats points as row