kernel: a placed copy is planned as its original, moved, as its mesh already is

A body showing another's display (a later instance of a part an assembly
places several times) took that part's mesh, placed, but was planned for
the GPU viewport on its own: Poppy Humanoid's display planned all 247
bodies where its meshes came from 94 parts. Body::mesh_plan now plans the
original once (kept with the shared display, as its meshes are) and moves
the plan, so the copy's display is the part's triangulation, as its mesh
is. Body::unmeshed_faces needs no case of its own for a copy any more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-28 09:05:22 -04:00
committed by Irakli Kourtis
parent c1713994f9
commit d0081b235e
2 changed files with 45 additions and 14 deletions
+21 -14
View File
@@ -68,13 +68,13 @@ pub(crate) struct MeshMemo {
/// Meshes a [`MeshMemo`] keeps at most (the oldest goes first).
const MEMO_MESHES: usize = 4;
/// Meshes kept for reuse, by tolerance (see [`remembered`]).
type Remembered = std::sync::Mutex<Vec<((u64, u64), std::sync::Arc<Mesh>)>>;
/// Meshes (or their plans) kept for reuse, by tolerance (see [`remembered`]).
type Remembered<T = Mesh> = std::sync::Mutex<Vec<((u64, u64), std::sync::Arc<T>)>>;
/// The mesh at `t` from `kept`, made by `make` and kept (at most [`MEMO_MESHES`]) if it isn't
/// there. The lock is held while meshing: threads asking for the same mesh wait for it rather
/// than make it too.
fn remembered(kept: &Remembered, t: &MeshTolerance, make: impl FnOnce() -> Result<Mesh>) -> Result<std::sync::Arc<Mesh>> {
/// The mesh (or plan) at `t` from `kept`, made by `make` and kept (at most [`MEMO_MESHES`]) if
/// it isn't there. The lock is held while meshing: threads asking for the same mesh wait for it
/// rather than make it too.
fn remembered<T>(kept: &Remembered<T>, t: &MeshTolerance, make: impl FnOnce() -> Result<T>) -> Result<std::sync::Arc<T>> {
let Ok(mut made) = kept.lock() else { return make().map(std::sync::Arc::new) };
let key = t.key();
if let Some((_, m)) = made.iter().find(|(k, _)| *k == key) {
@@ -89,16 +89,21 @@ fn remembered(kept: &Remembered, t: &MeshTolerance, make: impl FnOnce() -> Resul
}
/// The display mesh of a body that several placed copies show (see `Body::display_of`): the
/// body, and its mesh once made.
/// body, and its mesh and plan once made.
#[derive(Debug)]
pub(crate) struct SharedDisplay {
pub(crate) original: Body,
meshes: Remembered,
plans: Remembered<solidcraft_geom::tess::TessPlan>,
}
impl SharedDisplay {
pub(crate) fn new(original: Body) -> Self {
SharedDisplay { original: Body { display_of: None, meshes: None, ..original }, meshes: std::sync::Mutex::new(Vec::new()) }
SharedDisplay {
original: Body { display_of: None, meshes: None, ..original },
meshes: std::sync::Mutex::new(Vec::new()),
plans: std::sync::Mutex::new(Vec::new()),
}
}
}
@@ -1028,6 +1033,13 @@ impl Body {
if self.is_mesh() {
return Err(KernelError::Invalid("a mesh body has no surfaces to mesh".into()));
}
// A placed copy is planned as its original, moved (as `Body::mesh` shows the original's
// mesh): the part is planned once for every copy.
if let Some((shared, m)) = self.display_of.as_deref()
&& brep::placement::Rigid::of_matrix(m).is_some()
{
return Ok(remembered(&shared.plans, t, || shared.original.mesh_plan_untimed(t))?.transformed(m));
}
let (tol, turn) = t.rules(self.size());
let (plan, stats) = guard("mesh", || crate::tess::plan(&self.record, tol, turn))?;
// A face filled leniently has no hole on screen, but its pcurves or seams are still wrong:
@@ -1130,12 +1142,7 @@ impl Body {
if self.is_mesh() {
return 0;
}
// A placed copy shows its original's mesh (`Body::mesh`).
let meshed = match self.display_of.as_deref() {
Some((shared, m)) if brep::placement::Rigid::of_matrix(m).is_some() => &shared.original,
_ => self,
};
let Ok(plan) = meshed.mesh_plan(&MeshTolerance::chord(coarse_tolerance(self.size()))) else { return self.face_count() };
let Ok(plan) = self.mesh_plan(&MeshTolerance::chord(coarse_tolerance(self.size()))) else { return self.face_count() };
plan.faces_with_triangles(self.face_count()).iter().filter(|h| !**h).count()
}
+24
View File
@@ -62,3 +62,27 @@ fn a_copy_with_another_solid_makes_its_own_meshes() {
let m = swapped.mesh(&crate::MeshTolerance::chord(0.5)).unwrap();
assert_eq!(m.triangles.len(), other.mesh(&crate::MeshTolerance::chord(0.5)).unwrap().triangles.len());
}
/// A placed copy that shows its original's display is planned as the original, moved: the same
/// triangles on every face, its points the original's placed (so within the original's chord of
/// the copy's surfaces), and the original planned once for every copy.
#[test]
fn a_placed_copy_is_planned_as_its_original_moved() {
let part = plate();
let share = DisplayShare::new(part.clone());
let (angle, shift) = (0.7f64, Vec3::new(50.0, -20.0, 5.0));
let (c, s) = (angle.cos(), angle.sin());
let t = [[c, s, 0., 0.], [-s, c, 0., 0.], [0., 0., 1., 0.], [shift.x, shift.y, shift.z, 1.]];
let copies: Vec<Body> = (0..2).map(|_| crate::transform(&part, shift, Vec3::ZERO, Vec3::Z, angle).unwrap().with_display_share(&share, t)).collect();
let tol = crate::MeshTolerance::display_of(part.size());
let original = part.mesh_plan(&tol).unwrap().evaluate();
for copy in &copies {
let m = copy.mesh_of(&copy.mesh_plan(&tol).unwrap()).unwrap();
assert_eq!(m.triangles, original.triangles);
assert_eq!(m.tri_face, original.tri_face);
let placed = placed_mesh(&original, &t).unwrap();
let far = m.positions.iter().zip(&placed.positions).map(|(p, q)| p.dist(*q)).fold(0.0, f64::max);
assert!(far < 1e-9, "{far}");
}
assert_eq!(share.0.plans.lock().unwrap().len(), 1, "planned once for both copies");
}