Skip to main content

geop_ops_model/placed/
explode.rs

1//! Exploded views: placed parts drawn moved off where they are assembled,
2//! move by move. Each move ([`Explosion`]) is a step's ([`explosion`]),
3//! read in the order the steps made them ([`explosions`]), and moves the
4//! parts it names — and those placed in them — on from wherever the moves
5//! before left them ([`exploded`]). What the scene draws and a drawing
6//! shows of a placed part is where the moves put it; the state, the mates
7//! and every other read of a part's pose stay as assembled.
8
9use geop_core_math::{geop_error::GeopResult, primitives::Pose};
10use geop_core_program::{
11    element::{ElementReference, HasElements, Kind, whole},
12    placed::INSTANCE_SEPARATOR,
13    reader::Reader,
14    target::{Target, TargetReference},
15};
16
17/// One move of an explode sequence: the placed parts it moves, by their
18/// paths — a part placed in one of them moves with it — and the motion it
19/// moves them by, in the frame of the part they are placed in, after every
20/// move before it.
21#[derive(Clone, Debug, PartialEq)]
22pub struct Explosion {
23    pub parts: Vec<String>,
24    pub motion: Pose,
25}
26
27impl Explosion {
28    /// Whether it moves the part placed at `path`: that part, or one it is
29    /// placed in.
30    pub fn moves(&self, path: &str) -> bool {
31        (self.parts.iter()).any(|part| {
32            path.strip_prefix(part.as_str())
33                .is_some_and(|rest| rest.is_empty() || rest.starts_with(INSTANCE_SEPARATOR))
34        })
35    }
36}
37
38/// Each explode step's own.
39impl Target for Explosion {}
40
41/// Read whole.
42impl Kind for Explosion {
43    const NAME: &'static str = "explosion";
44}
45
46/// What the target of an explosion is named, before the step's id.
47const PREFIX: &str = "explosion:";
48
49impl HasElements<Explosion> for Explosion {
50    type Element<'a> = &'a Explosion;
51
52    fn element(&self, name: &str) -> GeopResult<&Explosion> {
53        whole(self, name)
54    }
55
56    fn names_target(name: &str) -> bool {
57        name.starts_with(PREFIX)
58    }
59}
60
61/// The move the step `step` makes.
62pub fn explosion(step: &str) -> TargetReference<Explosion> {
63    TargetReference::new(format!("{PREFIX}{step}"))
64}
65
66/// Every move the part `reader` reads is exploded by, in the order the
67/// steps made them: an explode sequence, as far as the steps run. Which
68/// there are is recorded, so a drawing of them is made again when one
69/// changes.
70pub fn explosions<'a>(reader: &Reader<'a>) -> Vec<&'a Explosion> {
71    (reader.targets_of::<Explosion>().into_iter())
72        .filter_map(|target| {
73            reader
74                .retrieve_element(&ElementReference::whole(target))
75                .ok()
76        })
77        .collect()
78}
79
80/// Where the placed part at `path`, assembled at `world`, is after the
81/// moves `explosions`: each that moves it moving it on.
82pub fn exploded(explosions: &[&Explosion], path: &str, world: &Pose) -> Pose {
83    (explosions.iter())
84        .filter(|explosion| explosion.moves(path))
85        .fold(*world, |at, explosion| explosion.motion.compose(&at))
86}