Skip to main content

geop_ops_model/placed/
mod.rs

1//! Parts placed in a part: the part another program file builds, placed
2//! as a node of the part placing it (see [`geop_core_program::placed`]) by
3//! the step that places it, and drawn as a part is drawn ([`Drawn`]) —
4//! read here, wherever they are placed ([`placed_parts`], [`locate`]), with
5//! their solids ([`solids`]), the bill of materials they make ([`bom`]),
6//! where an exploded view draws them ([`explode`]) and the motions that
7//! drive their joints ([`motion`]).
8//!
9//! Where an instance is, is the program's state: the pose state of its
10//! [`Placement`], or a fixed motion after another instance, as a pattern's
11//! copy goes. The parts placed in a placed part are the placing program's
12//! to move too: their poses are its state, behind the instance's name
13//! (`/bolt:nut.pose`), and the placed part is built with them. A placed
14//! part is built from its program, its inputs and the state under its
15//! name, and drawn as it is built: moving it builds and draws nothing
16//! again, and a change in it builds and draws again only what reads it.
17//! Equal builds — every screw of a size — are one.
18
19pub mod bom;
20pub mod explode;
21pub mod motion;
22pub mod solids;
23
24use geop_core_program::{
25    element::{ElementReference, HasElements, Kind, whole},
26    kept::Keeper,
27    placed::{INSTANCE_SEPARATOR, Located as LocatedName, instance_path, split_instance_path},
28    reader::Reader,
29    target::{Target, TargetReference},
30};
31use std::rc::Rc;
32
33use geop_core_editor::view::PartView;
34use geop_core_math::{
35    geop_error::{GeopError, GeopResult, WithContext},
36    primitives::{Pose, TriangleFace},
37};
38
39use crate::{
40    SolidMeshes,
41    parameters::{Factor, Instance, Placement, compose, instance, instances, placement},
42    read_model,
43};
44
45/// A placed part as drawn where it is placed: its own part's view, without
46/// its sketches, which are what it was drawn with. Its key is what the view
47/// shows, hashed: a viewer keeps a view by it, so that it is sent once
48/// however often the part placing it is drawn, and parts drawn alike —
49/// every screw of a size — share it.
50#[derive(Clone, Debug)]
51pub struct Drawn {
52    pub key: String,
53    pub view: Rc<PartView>,
54}
55
56/// The drawing of the same part.
57impl PartialEq for Drawn {
58    fn eq(&self, other: &Self) -> bool {
59        self.key == other.key
60    }
61}
62
63/// Each placed part's own; kept between processes, its view as views are
64/// kept ([`geop_core_editor::view::kept`]), so that a placed part reopened
65/// is drawn without meshing it again.
66impl Target for Drawn {
67    const KEEPER: Option<Keeper<Self>> = Some(Keeper {
68        write: |drawn, pool| {
69            let view = geop_core_editor::view::kept::put(&drawn.view, pool)?;
70            serde_json::to_string(&(&drawn.key, view))
71                .map_err(|e| GeopError::new(format!("writing a kept drawing: {e}")))
72        },
73        read: |text, pool| {
74            let (key, view): (String, String) = serde_json::from_str(text)
75                .map_err(|e| GeopError::new(format!("reading a kept drawing: {e}")))?;
76            let view = geop_core_editor::view::kept::get(&view, pool)?;
77            Ok(Drawn { key, view })
78        },
79    });
80}
81
82/// Read whole.
83impl Kind for Drawn {
84    const NAME: &'static str = "drawn";
85}
86
87impl HasElements<Drawn> for Drawn {
88    type Element<'a> = &'a Drawn;
89
90    fn element(&self, name: &str) -> GeopResult<&Drawn> {
91        whole(self, name)
92    }
93}
94
95/// The drawing of the instance `name`: what the step placing it produces.
96pub fn drawn_target(name: &str) -> TargetReference<Drawn> {
97    TargetReference::new(format!("drawn:{name}"))
98}
99
100/// The drawing of the part placed at `path` in the part `reader` reads.
101pub fn drawn<'a>(reader: &Reader<'a>, path: &str) -> GeopResult<&'a Drawn> {
102    let (outer, name) = split_instance_path(path);
103    let level = match outer.is_empty() {
104        true => reader.clone(),
105        false => reader.placed_part(outer)?,
106    };
107    (level.retrieve_element(&ElementReference::whole(drawn_target(name))))
108        .map_err(|e| e.with_context(format!("the drawing of the placed part {path:?}")))
109}
110
111/// A part placed in the part read, however deep: its instance, its part,
112/// its pose in the part read, and the state that pose is, if it is the
113/// part read's to move.
114pub struct Located<'a> {
115    pub instance: &'a Instance,
116    pub part: Reader<'a>,
117    pub world: Pose,
118    /// Where it is in the part it is placed in.
119    pub local: Pose,
120    pub state: Option<String>,
121}
122
123/// The part placed at `path` — `bolt`, or `asm/bolt` for one placed in a
124/// placed part — of the part `reader` reads, where the state has it.
125pub fn locate<'a>(reader: &Reader<'a>, path: &str) -> GeopResult<Located<'a>> {
126    let ctx = |e: GeopError| e.with_context(format!("the placed part {path:?}"));
127    let (outer, name) = split_instance_path(path);
128    let level = match outer.is_empty() {
129        true => reader.clone(),
130        false => reader.placed_part(outer).with_context(&ctx)?,
131    };
132    let instance = instance(&level, name).with_context(&ctx)?;
133    let part = level.placed_part(name).with_context(&ctx)?;
134    let factors = placement(reader, path).with_context(&ctx)?;
135    let outside = match outer.is_empty() {
136        true => 0,
137        false => placement(reader, outer).with_context(&ctx)?.len(),
138    };
139    // A copy is moved by what it copies, not by a state of its own.
140    let state = match (&instance.placement, factors.last()) {
141        (Placement::State(_), Some(Factor::State { state, .. })) => Some(state.clone()),
142        _ => None,
143    };
144    let own = factors.get(outside..).unwrap_or_default();
145    Ok(Located {
146        instance,
147        part,
148        world: compose(&factors, &mut |_, pose| pose),
149        local: compose(own, &mut |_, pose| pose),
150        state,
151    })
152}
153
154/// The state that moves the part placed at `path` — as [`Located::state`]
155/// has it, if the part read moves it at all. Reads no state: which state
156/// moves a part does not depend on where it is.
157pub fn moved_by(reader: &Reader, path: &str) -> GeopResult<Option<String>> {
158    let mut level = reader.clone();
159    let mut prefix = String::new();
160    let mut state = None;
161    for name in path.split(INSTANCE_SEPARATOR) {
162        match &instance(&level, name)?.placement {
163            Placement::State(own) => state = Some(LocatedName::in_instance(&prefix, own).key()),
164            Placement::After { .. } => return Ok(None),
165        }
166        prefix = instance_path(&prefix, name);
167        level = level.placed_part(name)?;
168    }
169    Ok(state)
170}
171
172/// A part placed in the part a reader reads, however deep.
173pub struct Placed<'a> {
174    pub name: String,
175    pub located: Located<'a>,
176    /// The part it is placed in, by index among them all, if not the
177    /// part read itself.
178    pub parent: Option<usize>,
179}
180
181/// Every part placed in the part `reader` reads, and in those, however
182/// deep: each before those placed in it, in the order the steps placed
183/// them.
184pub fn placed_parts<'a>(reader: &Reader<'a>) -> GeopResult<Vec<Placed<'a>>> {
185    fn within<'a>(
186        outer: &Reader<'a>,
187        level: &Reader<'a>,
188        prefix: &str,
189        parent: Option<usize>,
190        out: &mut Vec<Placed<'a>>,
191    ) -> GeopResult<()> {
192        for (name, _) in instances(level)? {
193            let path = instance_path(prefix, name);
194            let located = locate(outer, &path)?;
195            let inside = located.part.clone();
196            out.push(Placed {
197                name: path.clone(),
198                located,
199                parent,
200            });
201            let index = Some(out.len() - 1);
202            within(outer, &inside, &path, index, out)?;
203        }
204        Ok(())
205    }
206    let mut out = Vec::new();
207    within(reader, reader, "", None, &mut out)?;
208    Ok(out)
209}
210
211/// Every solid of the part `reader` reads, meshed `quality` fine: its own,
212/// and those of every part placed in it, however deep — each where it is,
213/// named behind its name in the part read: `/pin:extrude(pin)`.
214pub fn solid_meshes(reader: &Reader, quality: usize) -> GeopResult<SolidMeshes> {
215    let own = |reader: &Reader| read_model(reader)?.solid_meshes(quality);
216    let mut out = own(reader)?;
217    let parts = placed_parts(reader)?;
218    for (at, placed) in parts.iter().enumerate() {
219        geop_core_program::watch::checkpoint("meshing placed parts", at, parts.len())?;
220        let meshes = own(&placed.located.part)?;
221        let motion = placed.located.world.motion();
222        for (name, triangles) in meshes.iter() {
223            let moved = (triangles.iter())
224                .map(|t| TriangleFace {
225                    a: motion.apply(&t.a),
226                    b: motion.apply(&t.b),
227                    c: motion.apply(&t.c),
228                    normal: motion.rotate(&t.normal),
229                    vertex_normals: t.vertex_normals.map(|n| n.map(|v| motion.rotate(&v))),
230                })
231                .collect();
232            out.push((LocatedName::in_instance(&placed.name, name).key(), moved));
233        }
234    }
235    Ok(out)
236}