solid.mirror: mirror bodies of placed components where they are shown (#1896)
Thanks @NOWN-DEV.
This commit is contained in:
1 parent
9ce940b0b1
commit
2831426c51
9 files changed
+198
-8
No files matched your search
@@ -244,6 +244,21 @@ impl Document {
|
||||
m
|
||||
}
|
||||
|
||||
/// World transform of one placement of `component`: the occurrence `path` from the root
|
||||
/// (empty for the root itself). `None` when the path no longer leads to that component.
|
||||
pub fn path_transform(&self, component: u64, path: &[u64]) -> Option<Mat> {
|
||||
if path.len() > MAX_PLACEMENT_DEPTH + 1 {
|
||||
return None;
|
||||
}
|
||||
let (mut m, mut c) = (IDENTITY, 0);
|
||||
for id in path {
|
||||
let o = self.occurrences.iter().find(|o| o.id == *id && o.parent == c)?;
|
||||
m = mat_mul(&m, &o.transform);
|
||||
c = o.component;
|
||||
}
|
||||
(c == component).then_some(m)
|
||||
}
|
||||
|
||||
/// Every placement of each component: (component, occurrence path ids, world transform).
|
||||
/// A branch stops past [`MAX_PLACEMENT_DEPTH`] levels, the walk past [`MAX_PLACEMENTS`] placements.
|
||||
pub fn placements(&self) -> Vec<(u64, Vec<u64>, Mat)> {
|
||||
|
||||
@@ -682,6 +682,11 @@ pub enum FeatureKind {
|
||||
/// Join each mirrored body with its original.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
combine: bool,
|
||||
/// Where each of `bodies` is mirrored from: the occurrence path of the placement it was
|
||||
/// picked at. The body is mirrored where that placement shows it, as the feature's own
|
||||
/// component sees it. Empty in older designs, which mirror the bodies' own geometry.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
placements: Vec<Vec<u64>>,
|
||||
},
|
||||
/// A drilled hole (simple, counterbore or countersink) at a point, into the material.
|
||||
Hole {
|
||||
|
||||
+31
-4
@@ -1039,7 +1039,9 @@ fn placement_digest(doc: &Document, f: &Feature) -> u64 {
|
||||
FeatureKind::Revolve { to, .. } => to.is_some(),
|
||||
_ => false,
|
||||
};
|
||||
if to_object {
|
||||
// A mirror of placed bodies follows where they are placed.
|
||||
let placed_mirror = matches!(&f.kind, FeatureKind::Mirror { placements, .. } if !placements.is_empty());
|
||||
if to_object || placed_mirror {
|
||||
return if doc.occurrences.is_empty() && doc.body_offsets.is_empty() { 0 } else { placements_hash(doc) };
|
||||
}
|
||||
let FeatureKind::Sketch { plane, sketch } = &f.kind else { return 0 };
|
||||
@@ -3741,6 +3743,20 @@ fn mirror_matrix(pl: &Plane) -> Mat {
|
||||
|
||||
const MAX_INSTANCES: usize = 1000;
|
||||
|
||||
/// Where a mirrored body is, in the frame of the mirror's component: through the placement it
|
||||
/// was picked at and the feature's component frame. `None` when that is the body's own frame
|
||||
/// (and in older designs, which saved no placement).
|
||||
fn mirrored_placement(doc: &Document, f: &Feature, mb: &ModelBody, path: Option<&[u64]>) -> Result<Option<Mat>> {
|
||||
let Some(path) = path else { return Ok(None) };
|
||||
let owner = doc.model_body_component(mb);
|
||||
let lost = || DocError::Invalid(format!("the placement `{}` was mirrored at no longer exists", mb.name));
|
||||
let world = doc.path_transform(owner, path).ok_or_else(lost)?;
|
||||
let world = doc.model_body_offset(mb).map(|offset| crate::mat_mul(&world, offset)).unwrap_or(world);
|
||||
let inverse = crate::mat_inverse(&doc.component_transform(f.component)).ok_or_else(lost)?;
|
||||
let rel = crate::mat_mul(&inverse, &world);
|
||||
Ok((!crate::is_identity(&rel)).then_some(rel))
|
||||
}
|
||||
|
||||
/// The placements (column-major matrices) of a pattern's copies, the original excluded.
|
||||
pub fn pattern_matrices(doc: &Document, st: &ModelState, p: &crate::PatternKind) -> Result<Vec<[[f64; 4]; 4]>> {
|
||||
let (vals, _) = doc.param_values();
|
||||
@@ -4670,17 +4686,28 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::Mirror { features, plane, bodies, combine } => {
|
||||
FeatureKind::Mirror { features, plane, bodies, combine, placements } => {
|
||||
let pl = doc.resolve_plane_in(vals, plane, 0, Some(st))?;
|
||||
if bodies.is_empty() {
|
||||
return replay(doc, vals, f, st, &crate::plain(features), &[mirror_matrix(&pl)], warning);
|
||||
}
|
||||
let m = mirror_matrix(&pl);
|
||||
let mut output = 0;
|
||||
for n in bodies {
|
||||
for (k, n) in bodies.iter().enumerate() {
|
||||
let i = st.bodies.iter().position(|b| &b.name == n).ok_or_else(|| DocError::Unknown(format!("body `{n}`")))?;
|
||||
let Some(mb) = st.bodies.get(i).cloned() else { continue };
|
||||
let mirrored = moved_copy(&mb, kernel::transform_matrix(&mb.body, m)?, 1);
|
||||
// A body of a component placed elsewhere is mirrored where it is shown: the copy
|
||||
// (in this feature's component) from its placement, a joined body in its own frame.
|
||||
let placed = mirrored_placement(doc, f, &mb, placements.get(k).map(Vec::as_slice))?;
|
||||
let (copy, own) = match placed {
|
||||
Some(rel) => {
|
||||
let inverse = crate::mat_inverse(&rel).ok_or_else(|| DocError::Invalid(format!("the placement of `{n}` is singular")))?;
|
||||
let copy = crate::mat_mul(&m, &rel);
|
||||
(copy, crate::mat_mul(&inverse, ©))
|
||||
}
|
||||
None => (m, m),
|
||||
};
|
||||
let mirrored = moved_copy(&mb, kernel::transform_matrix(&mb.body, if *combine { own } else { copy })?, 1);
|
||||
if *combine {
|
||||
let joined = kernel::boolean(&mb.body, &mirrored.body, BoolOp::Union)?
|
||||
.ok_or_else(|| DocError::Invalid("the mirror join produced nothing".into()))?;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//! A Mirror of bodies keeps the placement each body was picked at (`placements`: one occurrence
|
||||
//! path per body), and mirrors the body where that placement shows it, so a body of a moved or
|
||||
//! inserted component, or of one of its further instances, is mirrored where it is seen. Older
|
||||
//! designs need no transformation: a Mirror without `placements` mirrors the bodies' own
|
||||
//! geometry as before; the last release asks for an update instead of mirroring them there.
|
||||
Reference in new issue
Block a user