Skip to main content

geop_ops_assembly/
placed.rs

1//! Placing a part in a part ([`define_placed`]): the part another program
2//! file builds, placed as a node of the part placing it (see
3//! [`geop_core_program::placed`]) and drawn as a part is drawn ([`draw`]) —
4//! and every placed part drawn where the state has it
5//! ([`draw_instances`]). What a placed part is to the steps and outputs
6//! that read it is [`geop_ops_model::placed`]'s.
7//!
8//! A placed part is built from its program, its inputs and the state under
9//! its name, and drawn as it is built: moving it builds and draws nothing
10//! again, and a change in it builds and draws again only what reads it.
11//! Equal builds — every screw of a size — are one.
12
13use geop_core_program::{
14    element::ElementReference,
15    part::StepContext,
16    placed::{PlacedArgs, place},
17    reader::Reader,
18};
19use std::{
20    hash::{Hash, Hasher},
21    rc::Rc,
22};
23
24use geop_core_editor::view::{PartView, annotations_of};
25use geop_core_math::geop_error::{GeopError, GeopResult};
26use geop_core_rasterize::Rasterizer;
27use geop_lib_constraints::{solve::Mobility, unknown::Unknowns};
28use geop_ops_model::{
29    datums::read_datums,
30    parameters::{Instance, Placement, define_instance, read_properties},
31    placed::{
32        Drawn, drawn, drawn_target,
33        explode::{exploded, explosions},
34        placed_parts,
35    },
36    read_model,
37    sketch::PlacedSketch,
38};
39
40/// The part `reader` reads, drawn: its model, its datums — the origin
41/// first — its sketches and what its steps draw on it, in its colour. Not
42/// the parts placed in it: the part placing it draws those, however deep
43/// ([`draw_instances`]), where its state has them. A drawing of them here
44/// would read where they are, and every move of a part three levels down
45/// would draw — and mesh — every part it is placed in again.
46pub(crate) fn draw(reader: &Reader) -> GeopResult<PartView> {
47    let mut view = PartView::new();
48    read_model(reader)?.draw("model", &mut Rasterizer::default(), &mut view)?;
49    read_datums(reader)?.draw(&mut view)?;
50    for target in reader.targets_of::<PlacedSketch>() {
51        let name = target.name().to_string();
52        let reference = ElementReference::<_, PlacedSketch>::whole(target);
53        reader
54            .retrieve_element(&reference)?
55            .draw(&name, &mut view)?;
56    }
57    view.annotations = annotations_of(reader);
58    read_properties(reader)?.draw(&mut view);
59    Ok(view.finish())
60}
61
62/// Places the part `placed` says — the program in its file, its
63/// parameters set to its inputs, the steps after its own run after — as the
64/// instance `name`, at `placement` (see
65/// [`geop_core_program::placed::place`]), and draws it ([`draw`]): again
66/// only when what it is drawn from changed. A step of the part placed that
67/// fails fails this, after it is placed and drawn as far as it builds.
68pub fn define_placed(
69    ctx: &mut StepContext,
70    name: &str,
71    placed: &PlacedArgs,
72    placement: Placement,
73) -> GeopResult<()> {
74    // Where it is before it is built: a part reading the parts beside it in
75    // context reads where it is itself.
76    let instance = Instance {
77        placement,
78        file: placed.file.clone(),
79        inputs: placed.inputs.clone(),
80        then: placed.then.clone(),
81    };
82    define_instance(ctx, name, &instance)?;
83    let loaded = place(ctx, name, placed);
84    let drawn = ctx.produce(&drawn_target(name), &name.to_string(), |reader, name| {
85        // Drawn once for every instance of one build: every screw of a size.
86        let inside = reader.placed_part(name)?;
87        let drawn = inside.shared("drawn", |inside| {
88            let mut view = draw(inside)?;
89            view.sketches.clear();
90            let shown = serde_json::to_vec(&view)
91                .map_err(|e| GeopError::new(format!("the drawing of `{name}`: {e}")))?;
92            let mut key = std::hash::DefaultHasher::new();
93            shown.hash(&mut key);
94            Ok(Drawn {
95                key: format!("{:016x}", key.finish()),
96                view: Rc::new(view),
97            })
98        })?;
99        Ok((*drawn).clone())
100    });
101    // A part that failed to load is drawn as far as it was built, or not at
102    // all: why it failed is what to report, not that it is not drawn.
103    loaded.and(drawn)
104}
105
106/// Draws every part placed in the part `reader` reads, however deep, into
107/// `view`, where the state has it: each as its own part's view, which
108/// leaves out the parts placed in it — they are drawn here already.
109///
110/// One is drawn fixed that no solve moves: a copy, or one a step holds
111/// fixed. One an exploded view moves is drawn where its moves put it (see
112/// [`geop_ops_model::placed::explode`]).
113pub(crate) fn draw_instances(reader: &Reader, view: &mut PartView) -> GeopResult<()> {
114    let unknowns = Unknowns::of(reader)?;
115    let explosions = explosions(reader);
116    for placed in placed_parts(reader)? {
117        let located = placed.located;
118        let pose = exploded(&explosions, &placed.name, &located.world);
119        let origin = geop_core_math::primitives::CoordinateSystem::world_at(
120            geop_core_math::vector::Vector3::zero(),
121        );
122        let drawn = drawn(reader, &placed.name)?;
123        view.instances.push(geop_core_editor::view::ViewInstance {
124            frame: pose.motion().apply_frame(&origin)?,
125            name: placed.name,
126            component: drawn.key.clone(),
127            pose,
128            view: drawn.view.clone(),
129            fixed: (located.state.as_ref())
130                .and_then(|state| unknowns.get(state))
131                .is_none_or(|unknown| unknown.mobility == Mobility::Fixed),
132            state: located.state,
133            color: None,
134        });
135    }
136    Ok(())
137}