Skip to main content

geop_ops_model/parameters/
instance.rs

1//! Where placed parts are: what the step that places a part (see
2//! [`geop_core_program::placed`]) says of it, as a target of its own
3//! ([`define_instance`]). The program only builds the placed part; where
4//! it goes is a value of the state, or a fixed motion after another
5//! instance, composed outermost first ([`placement`]), and where that puts
6//! it ([`compose`], [`located`]).
7
8use geop_core_math::{
9    geop_error::{GeopError, GeopResult},
10    primitives::Pose,
11    scalars::Number,
12};
13use geop_core_program::{
14    element::{ElementReference, HasElements, Kind, whole},
15    part::StepContext,
16    placed::{INSTANCE_SEPARATOR, Located, OUTSIDE, instance_path},
17    program::ProgramStep,
18    reader::Reader,
19    target::{Target, TargetReference},
20    values::{Settings, StateValue, pose_in},
21};
22
23/// Where an instance is in the part it is in.
24#[derive(Clone, Debug, PartialEq)]
25pub enum Placement {
26    /// Where the pose state `state` of the part puts it: moved by whatever
27    /// solves the state.
28    State(String),
29    /// `motion` after where the instance `of` of the same part is: a copy,
30    /// which goes with what it copies. Only where it is: what is in it is
31    /// run for itself, with its own state.
32    After { of: String, motion: Pose },
33}
34
35/// An instance placed, as its target holds it: where it is, and the file,
36/// the inputs and the steps after the file's own its part was placed from
37/// — what a copy of it places again, and what a list of the parts tells
38/// them apart by. What is in it is its placed part (see
39/// [`geop_core_program::placed::PlacedPart`]).
40#[derive(Clone, Debug, PartialEq)]
41pub struct Instance {
42    pub placement: Placement,
43    /// The file, as the program placing it names it.
44    pub file: String,
45    pub inputs: Settings,
46    /// The steps run after the file's own: a mirrored part's mirror.
47    pub then: Vec<ProgramStep>,
48}
49
50/// Defined once, by the step placing it.
51impl Target for Instance {}
52
53/// Read whole.
54impl Kind for Instance {
55    const NAME: &'static str = "instance";
56}
57
58impl HasElements<Instance> for Instance {
59    type Element<'a> = &'a Instance;
60
61    fn element(&self, name: &str) -> GeopResult<&Instance> {
62        whole(self, name)
63    }
64}
65
66/// What the target of an instance is named, before the instance's name.
67const PREFIX: &str = "instance:";
68
69/// The target of the instance `name`.
70fn instance_target(name: &str) -> TargetReference<Instance> {
71    TargetReference::new(format!("{PREFIX}{name}"))
72}
73
74/// Defines the instance `name` as `instance`. Its part is placed on its
75/// own ([`geop_core_program::placed::place`]).
76pub fn define_instance(ctx: &mut StepContext, name: &str, instance: &Instance) -> GeopResult<()> {
77    ctx.produce(&instance_target(name), instance, |_, instance| {
78        Ok(instance.clone())
79    })
80}
81
82/// The instance `name` placed in the part `reader` reads, the read
83/// recorded.
84pub fn instance<'a>(reader: &Reader<'a>, name: &str) -> GeopResult<&'a Instance> {
85    (reader.retrieve_element(&ElementReference::whole(instance_target(name))))
86        .map_err(|e| e.with_context(format!("the placed part `{name}`")))
87}
88
89/// Every instance placed in the part `reader` reads — not in its
90/// instances' parts — by name, in the order the steps placed their parts
91/// ([`geop_core_program::reader::Reader::instances`]), each read recorded.
92/// An instance whose part is placed must be defined: it is an error if it
93/// is not.
94pub fn instances<'a>(reader: &Reader<'a>) -> GeopResult<Vec<(&'a str, &'a Instance)>> {
95    (reader.instances().into_iter())
96        .map(|name| Ok((name, instance(reader, name)?)))
97        .collect()
98}
99
100/// One factor of where an instance is, as [`placement`] gives it,
101/// outermost first: the instance is where they all are, composed in order.
102#[derive(Clone, Debug, PartialEq)]
103pub enum Factor {
104    /// The value of the state `state` of the part read — named as the part
105    /// read names it, `/asm:b.pose` — which a solve moves, and by which a
106    /// constraint is differentiated.
107    State { state: String, value: StateValue },
108    /// A motion nothing in the part read moves: a copy's after what it
109    /// copies.
110    Fixed(Pose),
111}
112
113/// How deep a chain of copies may go before it is taken for a cycle.
114const COPIES: usize = 1 << 16;
115
116/// Where the instance at `path` — `bolt`, or `asm/bolt` — is in the part
117/// `reader` reads, as factors composed outermost first: the values of the
118/// state that place it and the parts it is in, read and recorded. A state
119/// of an instance's own — what places a part in it — is named as the part
120/// names it, behind the instance's path, and read so ([`Reader::given_state`]):
121/// the part's value where it gives one.
122///
123/// A path through the part `reader`'s part is placed in, read in context
124/// — `../bracket`, a part placed beside it, or `..` itself — is where that
125/// part has it, seen from where it has this one: fixed here, since nothing
126/// this part solves moves it (see [`Reader::outside`]).
127pub fn placement(reader: &Reader, path: &str) -> GeopResult<Vec<Factor>> {
128    let ctx = |e: GeopError| e.with_context(format!("where the placed part {path:?} is"));
129    let outside = (path.strip_prefix(OUTSIDE))
130        .filter(|rest| rest.is_empty() || rest.starts_with(INSTANCE_SEPARATOR));
131    if let Some(rest) = outside {
132        let (outside, me) = reader.outside().map_err(ctx)?;
133        let rest = rest.strip_prefix(INSTANCE_SEPARATOR).unwrap_or(rest);
134        let here = located(&outside, me).map_err(ctx)?;
135        let there = located(&outside, rest).map_err(ctx)?;
136        return Ok(vec![Factor::Fixed(here.inverse().compose(&there))]);
137    }
138    let mut factors = Vec::new();
139    let mut level = reader.clone();
140    let mut outer = String::new();
141    let mut names = path.split(INSTANCE_SEPARATOR).peekable();
142    while let Some(name) = names.next() {
143        factors.append(&mut placed(reader, &level, name, &outer, 0).map_err(ctx)?);
144        outer = instance_path(&outer, name);
145        // Into the part placed, for what is placed in it: where the last
146        // is needs nothing of its own part.
147        if names.peek().is_some() {
148            level = level.placed_part(name).map_err(ctx)?;
149        }
150    }
151    Ok(factors)
152}
153
154/// The factors placing the instance `name` of the part `level` reads — the
155/// part at `path` in the part `outer` reads — in that part: its state's
156/// value, named behind `path` and read so in `outer`, or a copy's motion
157/// after where what it copies is.
158fn placed(
159    outer: &Reader,
160    level: &Reader,
161    name: &str,
162    path: &str,
163    depth: usize,
164) -> GeopResult<Vec<Factor>> {
165    if depth > COPIES {
166        return Err(GeopError::new(format!(
167            "the copies of `{name}` form a cycle"
168        )));
169    }
170    Ok(match &instance(level, name)?.placement {
171        Placement::State(state) => {
172            let state = Located::in_instance(path, state.as_str()).key();
173            vec![Factor::State {
174                value: outer.retrieve_state(&state),
175                state,
176            }]
177        }
178        Placement::After { of, motion } => {
179            let mut factors = vec![Factor::Fixed(*motion)];
180            factors.append(&mut placed(outer, level, of, path, depth + 1)?);
181            factors
182        }
183    })
184}
185
186/// How the pose of each state a placement reads enters it, computed in
187/// `T`: as it is, or moved by a step to differentiate by it. Given the
188/// state's name and the pose it holds.
189pub type PoseOf<'f, T> = dyn FnMut(&str, Pose) -> Pose<T> + 'f;
190
191/// The pose an instance is at whose placement is `factors` (see
192/// [`placement`]), in `T`: theirs,
193/// composed outermost first, each state's entering as `pose_of` has it. A
194/// value that holds no pose — a state before anything solved for it —
195/// is no motion.
196pub fn compose<T: Number>(factors: &[Factor], pose_of: &mut PoseOf<'_, T>) -> Pose<T> {
197    let mut poses = factors.iter().map(|factor| match factor {
198        Factor::State { state, value } => {
199            pose_of(state, pose_in(value).unwrap_or_else(Pose::identity))
200        }
201        Factor::Fixed(motion) => motion.map(T::from_scalar),
202    });
203    // The first as it is: composing it after no motion would round it.
204    let first = poses.next().unwrap_or_else(Pose::identity);
205    poses.fold(first, |world, pose| world.compose(&pose))
206}
207
208/// Where the instance at `path` is in the part `reader` reads: none — no
209/// motion — for the part itself, at the empty path.
210pub fn located(reader: &Reader, path: &str) -> GeopResult<Pose> {
211    if path.is_empty() {
212        return Ok(Pose::identity());
213    }
214    Ok(compose(&placement(reader, path)?, &mut |_, pose| pose))
215}