fix: load designs off-thread and cache recent thumbnails as PNGs
This commit is contained in:
1 parent
b375c2a92a
commit
4b7e1d317d
23 files changed
+688
-29
No files matched your search
@@ -13,6 +13,8 @@ pub(crate) type Solid = mt::Solid;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Body {
|
||||
pub(crate) solid: std::sync::Arc<Solid>,
|
||||
/// Missing-face check from the orientation mesh, reusable at exactly its tolerance.
|
||||
pub(crate) coarse_missing: Option<(f64, usize)>,
|
||||
/// Set for mesh bodies (`solid` is then empty).
|
||||
pub(crate) mesh: Option<std::sync::Arc<crate::meshbody::TriMesh>>,
|
||||
/// Display colour (linear RGB 0..1) from an imported file; operations drop it.
|
||||
@@ -302,11 +304,24 @@ impl Body {
|
||||
if lo.x.is_finite() { (hi - lo).len() } else { 1.0 }
|
||||
};
|
||||
let tol = (span * 0.01).clamp(1e-3, 1.0);
|
||||
let v = guard("orient", || Ok(mesh_shells(&solid, tol).iter().map(|s| s.to_polygon().volume()).sum::<f64>()))?;
|
||||
let (v, missing) = guard("orient", || {
|
||||
let shells = mesh_shells(&solid, tol);
|
||||
let volume = shells.iter().map(|s| s.to_polygon().volume()).sum::<f64>();
|
||||
let missing =
|
||||
shells.iter().flat_map(|s| s.face_iter()).filter(|f| f.surface().is_none_or(|m| m.faces().triangle_iter().next().is_none())).count();
|
||||
Ok((volume, missing))
|
||||
})?;
|
||||
if v < 0.0 {
|
||||
solid.not();
|
||||
}
|
||||
Ok(Body { solid: std::sync::Arc::new(solid), mesh: None, color: None, paint: None, splits: None })
|
||||
Ok(Body {
|
||||
solid: std::sync::Arc::new(solid),
|
||||
coarse_missing: (v >= 0.0).then_some((tol, missing)),
|
||||
mesh: None,
|
||||
color: None,
|
||||
paint: None,
|
||||
splits: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// A deep copy of the solid that can be mutated without affecting other bodies.
|
||||
@@ -506,7 +521,13 @@ impl Body {
|
||||
if self.is_mesh() {
|
||||
return 0;
|
||||
}
|
||||
let Ok(m) = self.tessellate((self.size() * 1e-2).max(1e-3)) else { return self.face_count() };
|
||||
let tol = (self.size() * 1e-2).max(1e-3);
|
||||
if let Some((cached_tol, missing)) = self.coarse_missing
|
||||
&& cached_tol == tol
|
||||
{
|
||||
return missing;
|
||||
}
|
||||
let Ok(m) = self.tessellate(tol) else { return self.face_count() };
|
||||
let mut has = vec![false; self.face_count()];
|
||||
for f in &m.tri_face {
|
||||
if let Some(h) = has.get_mut(*f as usize) {
|
||||
|
||||
@@ -101,7 +101,14 @@ pub fn mesh_body(positions: &[Vec3], triangles: &[[u32; 3]]) -> Result<Body> {
|
||||
t.swap(1, 2);
|
||||
}
|
||||
}
|
||||
Ok(Body { solid: Arc::new(crate::body::Solid::new_unchecked(Vec::new())), mesh: Some(Arc::new(out)), color: None, paint: None, splits: None })
|
||||
Ok(Body {
|
||||
solid: Arc::new(crate::body::Solid::new_unchecked(Vec::new())),
|
||||
coarse_missing: None,
|
||||
mesh: Some(Arc::new(out)),
|
||||
color: None,
|
||||
paint: None,
|
||||
splits: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Wind every connected piece one way: a triangle that runs a side in the same direction as
|
||||
@@ -185,6 +192,13 @@ impl Body {
|
||||
t.swap(1, 2);
|
||||
}
|
||||
}
|
||||
Some(Body { solid: self.solid.clone(), mesh: Some(Arc::new(out)), color: self.color, paint: self.paint.clone(), splits: self.splits.clone() })
|
||||
Some(Body {
|
||||
solid: self.solid.clone(),
|
||||
coarse_missing: None,
|
||||
mesh: Some(Arc::new(out)),
|
||||
color: self.color,
|
||||
paint: self.paint.clone(),
|
||||
splits: self.splits.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -877,7 +877,10 @@ pub(crate) fn split_any(body: &Body, faces: &[usize], tool: &SplitTool) -> Resul
|
||||
}
|
||||
let shells = std::mem::take(&mut imp.shells);
|
||||
let solid = mt::Solid::new_unchecked(shells.into_iter().map(mt::Shell::from).collect());
|
||||
Ok((Body { solid: std::sync::Arc::new(solid), mesh: None, color: body.color, paint: None, splits: body.splits.clone() }, imp))
|
||||
Ok((
|
||||
Body { solid: std::sync::Arc::new(solid), coarse_missing: None, mesh: None, color: body.color, paint: None, splits: body.splits.clone() },
|
||||
imp,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,14 @@ impl Body {
|
||||
/// A body of faces as they are (no orientation fix: a surface has no inside).
|
||||
fn surface_body(faces: Vec<mt::Face>) -> Body {
|
||||
let shell = mt::Shell::from(faces);
|
||||
Body { solid: std::sync::Arc::new(mt::Solid::new_unchecked(vec![shell])), mesh: None, color: None, paint: None, splits: None }
|
||||
Body {
|
||||
solid: std::sync::Arc::new(mt::Solid::new_unchecked(vec![shell])),
|
||||
coarse_missing: None,
|
||||
mesh: None,
|
||||
color: None,
|
||||
paint: None,
|
||||
splits: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A body from faces: a solid when they close, else a surface.
|
||||
@@ -31,7 +38,14 @@ fn body_of(faces: Vec<mt::Face>) -> Result<Body> {
|
||||
if shell.shell_condition() == mt::ShellCondition::Closed {
|
||||
Body::new(mt::Solid::new_unchecked(vec![shell]))
|
||||
} else {
|
||||
Ok(Body { solid: std::sync::Arc::new(mt::Solid::new_unchecked(vec![shell])), mesh: None, color: None, paint: None, splits: None })
|
||||
Ok(Body {
|
||||
solid: std::sync::Arc::new(mt::Solid::new_unchecked(vec![shell])),
|
||||
coarse_missing: None,
|
||||
mesh: None,
|
||||
color: None,
|
||||
paint: None,
|
||||
splits: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1549,3 +1549,20 @@ fn raise_band_on_a_rounded_rim() {
|
||||
assert!((v - (v0 + band * 1.5)).abs() < 1e-5 * v, "outside {outside}: {v} vs {}", v0 + band * 1.5);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn orientation_mesh_reuses_missing_face_check_at_matching_tolerance() {
|
||||
for scale in [0.001, 1.0, 1000.0] {
|
||||
let body = box_solid(Vec3::ZERO, Vec3::new(10., 20., 30.) * scale).unwrap();
|
||||
let tol = (body.size() * 0.01).max(1e-3);
|
||||
let mesh = body.tessellate(tol).unwrap();
|
||||
let mut present = vec![false; body.face_count()];
|
||||
for face in mesh.tri_face {
|
||||
present[face as usize] = true;
|
||||
}
|
||||
assert_eq!(body.unmeshed_faces(), present.iter().filter(|p| !**p).count());
|
||||
if scale == 1.0 {
|
||||
assert_eq!(body.coarse_missing, Some((tol, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user