Kernel: a body moved by a non-rigid matrix keeps each face's source and paint on that face

transform_matrix moves a body by a matrix that is not a rigid motion through SMLib, which
numbers the moved body's faces afresh, then attached the input's face sources and paint in the
input's face order. Planar certificates landed on the wrong faces (a reflected, sheared plate's
hole walls and caps no longer lay on their certified planes). Each moved face now takes the
source and paint of the face its history names; a face with no known origin takes no source,
and with no history at all the paint stays as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-28 22:16:26 -04:00
committed by Irakli Kourtis
parent de16a6b99a
commit d91d3f2a67
2 changed files with 41 additions and 5 deletions
+8 -1
View File
@@ -100,7 +100,14 @@ fn construction_planes_follow_reflected_and_sheared_affine_images() {
[[-1., 0., 0., 0.], [0.4, 2., 0., 0.], [0., 0.3, 1., 0.], [8., 9., 10., 1.]],
] {
let placed = transform_matrix(&body, m).unwrap();
for (original, transformed) in body.face_sources().unwrap().iter().zip(placed.face_sources().unwrap()) {
// SMLib numbers the placed faces afresh: each is matched with the face its history names.
let history = placed.history().unwrap();
let originals = body.face_sources().unwrap();
for (f, transformed) in placed.face_sources().unwrap().iter().enumerate() {
let [crate::history::FaceOrigin::Face { operand: 0, face }] = history.faces[f].origins.as_slice() else {
panic!("face {f} has no one origin")
};
let original = &originals[*face];
let (
Some(FaceSource::Box { producer: a, axis: aa, high: ah, plane: ap }),
Some(FaceSource::Box { producer: b, axis: ba, high: bh, plane: bp }),
+33 -4
View File
@@ -141,15 +141,44 @@ pub fn transform_matrix(body: &Body, m: [[f64; 4]; 4]) -> Result<Body> {
return placed.with_history(history, &[body]);
}
let sources = body.transformed_sources(m)?;
let placed = match Rigid::of_matrix(&m) {
let (placed, sources, paint) = match Rigid::of_matrix(&m) {
Some(motion) => {
let moved = Body::from_record(body.record().moved(&motion))?;
let history = History::identity(&moved);
moved.with_history(history, &[body])?
(moved.with_history(history, &[body])?, sources, body.paint().cloned())
}
None => {
// SMLib numbers the moved body's faces afresh: each face's source and paint follow it
// by the history (a face whose origin is not known keeps no source; with no history
// the paint stays as it was).
let placed = transformed(body, &m)?;
let origin: Vec<Option<usize>> = (0..placed.face_count()).map(|f| moved_from(placed.history(), f)).collect();
let sources = sources.map(|s| std::sync::Arc::new(origin.iter().map(|o| o.and_then(|j| s.get(j).cloned().flatten())).collect()));
let paint = body.paint().cloned().map(|mut p| {
if placed.history().is_none() {
return p;
}
p.faces = p
.faces
.iter()
.flat_map(|fp| {
origin.iter().enumerate().filter(|(_, o)| **o == Some(fp.face)).map(|(face, _)| crate::FacePaint { face, ..fp.clone() })
})
.collect();
p
});
(placed, sources, paint)
}
None => transformed(body, &m)?,
};
Ok(placed.with_color(body.color()).with_paint(body.paint().cloned()).with_face_sources(sources).inherit_regularization_protection(body))
Ok(placed.with_color(body.color()).with_paint(paint).with_face_sources(sources).inherit_regularization_protection(body))
}
/// The face of the operand that face `f` of a moved body is, by the move's history.
fn moved_from(history: Option<&History>, f: usize) -> Option<usize> {
match history?.faces.get(f)?.origins.as_slice() {
[crate::history::FaceOrigin::Face { operand: 0, face }] => Some(*face),
_ => None,
}
}
/// `body` moved by a matrix that is not a rigid motion, in SMLib, its history from SMLib's tags.