fix(solid): read SAB transform Position tokens for body placement

The SAB reader tokenizes an ACIS transform record's matrix rows and
translation as Position triplets, while SAT text yields 13 individual
floats. body_transform() collected only float tokens, so every binary-SAB
solid/region (R2013+ AcDs payloads) lost its placement and rendered at
the body-local origin. Flatten Position tokens into the numeric run.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Fable 5 committed 2026-07-22 23:36:07 +03:00
1 parent 805da6428a
commit 22a7c4490c
1 file changed
+23 -13
+23 -13
View File
@@ -16,8 +16,8 @@ use std::f64::consts::TAU;
use acadrust::entities::acis::types::Sense;
use acadrust::entities::acis::{
SabReader, SatCoedge, SatConeSurface, SatDocument, SatEdge, SatEllipseCurve, SatFace,
SatIntCurve, SatLoop, SatPlaneSurface, SatPoint, SatPointer, SatSphereSurface, SatTorusSurface,
SatVertex,
SatIntCurve, SatLoop, SatPlaneSurface, SatPoint, SatPointer, SatSphereSurface, SatToken,
SatTorusSurface, SatVertex,
};
use acadrust::entities::{Body, Region, Solid3D};
@@ -896,17 +896,27 @@ 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")?;
// 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<f64> = t
.tokens
.iter()
.filter_map(|tok| tok.as_float())
.take(13)
.collect();
// The transform record's numeric payload is its first 13 numeric values:
// 3×3 matrix, translation, scale. A leading book-keeping pointer (`$-1`)
// and the trailing rotate/reflect/shear flags aren't numeric, so
// collecting numeric tokens skips them — reading by raw token index would
// be thrown off by the leading pointer. SAT text tokenizes the payload as
// 13 individual floats, but the SAB reader groups the matrix rows and the
// translation into `Position` triplets, so those must be flattened too.
let mut v: Vec<f64> = Vec::with_capacity(13);
for tok in &t.tokens {
if v.len() >= 13 {
break;
}
match tok {
SatToken::Position(x, y, z) => v.extend([*x, *y, *z]),
_ => {
if let Some(f) = tok.as_float() {
v.push(f);
}
}
}
}
if v.len() < 13 {
return None;
}