fix(solid3d): place ACIS solids by their body transform
ACIS keeps a solid's geometry in body-local space and records the world placement in a `transform` record (3×3 affine + translation + scale). The tessellator ignored it, so solids rendered at their local origin instead of where the drawing puts them. Apply the body transform to the mesh vertices (and rotate the normals) after tessellation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e68f95aff2
commit
876474f5aa
1 changed files with 44 additions and 1 deletions
|
|
@ -123,10 +123,14 @@ fn tessellate_sat_lods(
|
|||
facet_res: f64,
|
||||
) -> Option<MeshLodSet> {
|
||||
let configs = LodConfig::all();
|
||||
let xform = body_transform(sat);
|
||||
let mut lods: Vec<MeshModel> = Vec::with_capacity(3);
|
||||
for lod in configs {
|
||||
let scaled = scale_lod(lod, facet_res);
|
||||
if let Some(m) = tessellate_sat(sat, name.clone(), color, scaled) {
|
||||
if let Some(mut m) = tessellate_sat(sat, name.clone(), color, scaled) {
|
||||
if let Some((mat, tr, scale)) = xform {
|
||||
apply_body_transform(&mut m, &mat, &tr, scale);
|
||||
}
|
||||
lods.push(m);
|
||||
}
|
||||
}
|
||||
|
|
@ -137,6 +141,45 @@ fn tessellate_sat_lods(
|
|||
Some(MeshLodSet { lods, world_aabb })
|
||||
}
|
||||
|
||||
/// Extract the body's placement transform from the SAT document: a row-major
|
||||
/// 3×3 affine, a translation, and the uniform scale. ACIS keeps a solid's
|
||||
/// geometry in body-local space and records the placement in a `transform`
|
||||
/// record (`<3×3> <tx ty tz> <scale> rotate reflect shear`). `None` when the
|
||||
/// document has no transform (treated as identity).
|
||||
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)?;
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
/// Apply a body placement transform to a mesh. ACIS treats points as row
|
||||
/// vectors (`p' = scale·(p·M) + T`), so the 3×3 is indexed transposed relative
|
||||
/// to a column-vector multiply. Normals get the rotation only, renormalized.
|
||||
fn apply_body_transform(mesh: &mut MeshModel, m: &[f64; 9], tr: &[f64; 3], scale: f64) {
|
||||
for v in &mut mesh.verts {
|
||||
let (x, y, z) = (v[0] as f64, v[1] as f64, v[2] as f64);
|
||||
let wx = scale * (x * m[0] + y * m[3] + z * m[6]) + tr[0];
|
||||
let wy = scale * (x * m[1] + y * m[4] + z * m[7]) + tr[1];
|
||||
let wz = scale * (x * m[2] + y * m[5] + z * m[8]) + tr[2];
|
||||
*v = [wx as f32, wy as f32, wz as f32];
|
||||
}
|
||||
for n in &mut mesh.normals {
|
||||
let (x, y, z) = (n[0] as f64, n[1] as f64, n[2] as f64);
|
||||
let nx = x * m[0] + y * m[3] + z * m[6];
|
||||
let ny = x * m[1] + y * m[4] + z * m[7];
|
||||
let nz = x * m[2] + y * m[5] + z * m[8];
|
||||
let len = (nx * nx + ny * ny + nz * nz).sqrt();
|
||||
if len > 1e-9 {
|
||||
*n = [(nx / len) as f32, (ny / len) as f32, (nz / len) as f32];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Scale a LOD's segment counts by FACETRES (clamped to AutoCAD's
|
||||
/// documented [0.01, 10.0] range). 1.0 is the unchanged baseline.
|
||||
fn scale_lod(base: LodConfig, facet_res: f64) -> LodConfig {
|
||||
|
|
|
|||
Loading…
Reference in a new issue