diff --git a/crates/kernel/src/native/adapter.rs b/crates/kernel/src/native/adapter.rs index bb443142a6..fc72665e8f 100644 --- a/crates/kernel/src/native/adapter.rs +++ b/crates/kernel/src/native/adapter.rs @@ -308,8 +308,11 @@ fn compatible(body: &b::Brep) -> Result<()> { body.faces.iter().try_for_each(|f| compatible_surface(&f.surface)) } +/// The solid of a record: validated, its geometry checked against its topology (truck builds +/// edges and faces from it unchecked), then built as the kernel built it. pub(super) fn decode(body: b::Brep) -> Result { body.validate().map_err(invalid)?; + body.check_geometry().map_err(invalid)?; compatible(&body)?; let b::Brep { shells, faces, edges, vertices, .. } = body; let vertices: Vec = vertices.into_iter().map(|v| mt::Vertex::new(p(v.point))).collect(); diff --git a/crates/kernel/src/native/adapter_tests.rs b/crates/kernel/src/native/adapter_tests.rs index 1411a7a772..fb68adaf57 100644 --- a/crates/kernel/src/native/adapter_tests.rs +++ b/crates/kernel/src/native/adapter_tests.rs @@ -131,6 +131,18 @@ fn records_truck_cannot_hold_are_refused() { assert!(decode(vertex_loop).is_err()); } +/// Truck builds edges and faces unchecked: a record whose geometry misses its topology is +/// refused before it gets there. +#[test] +fn records_whose_geometry_misses_their_topology_are_refused() { + let cube = crate::box_solid(Vec3::ZERO, Vec3::new(4., 3., 1.)).unwrap(); + let mut moved = encode(&cube).unwrap(); + moved.vertices[0].point[0] += 0.1; + moved.validate().unwrap(); + let error = decode(moved).err().unwrap().to_string(); + assert!(error.contains("misses its"), "{error}"); +} + #[test] fn intersections_and_revolutions_reload_exactly() { let cyl = crate::cylinder(Vec3::ZERO, Vec3::Z, 5., 40.).unwrap();