geop_ops_model/placed/
solids.rs1use geop_core_program::{placed::Located, reader::Reader};
13use std::collections::HashMap;
14
15use crate::{
16 NamedModel, notes_of,
17 parameters::{Material, read_properties},
18 placed::placed_parts,
19 read_model,
20};
21use geop_core_math::{
22 geop_error::{GeopError, GeopResult, WithContext},
23 primitives::Pose,
24 scalars::Scalar,
25};
26use geop_core_topology::{SolidId, mass::MassProperties};
27
28pub struct PlacedSolid<'a> {
32 pub name: String,
33 pub instance: String,
35 pub reader: Reader<'a>,
37 pub model: std::borrow::Cow<'a, NamedModel>,
38 pub solid: SolidId,
39 pub pose: Option<Pose>,
41}
42
43impl PlacedSolid<'_> {
44 pub fn thickness(&self) -> Option<f64> {
47 let own = self.model.name_of(self.solid)?;
48 notes_of(&self.reader)?.get(own)?.thickness
49 }
50
51 pub fn material(&self) -> Option<Material> {
54 let properties = read_properties(&self.reader).ok()?;
55 properties.material().cloned()
56 }
57
58 pub fn shape_properties(&self, shapes: &mut Shapes) -> GeopResult<MassProperties> {
62 let ctx = |e: GeopError| e.with_context(format!("the shape of {}", self.name));
63 let own = shapes.of(self).with_context(&ctx)?;
64 match &self.pose {
65 Some(pose) => own.placed(pose).with_context(&ctx),
66 None => Ok(own),
67 }
68 }
69
70 pub fn mass_properties(&self, shapes: &mut Shapes) -> GeopResult<Option<MassProperties>> {
74 let Some(material) = self.material() else {
75 return Ok(None);
76 };
77 let ctx = |e: GeopError| e.with_context(format!("the mass of {}", self.name));
78 let per_mm3 = Scalar::from_f64(material.density)
79 .div(Scalar::from_f64(1e9))
80 .with_context(&ctx)?;
81 let shape = self.shape_properties(shapes)?;
82 Ok(Some(shape.with_density(per_mm3)))
83 }
84}
85
86#[derive(Default)]
90pub struct Shapes(HashMap<(*const NamedModel, SolidId), MassProperties>);
91
92impl Shapes {
93 pub fn of(&mut self, solid: &PlacedSolid) -> GeopResult<MassProperties> {
95 let key = (std::ptr::from_ref(&*solid.model), solid.solid);
96 if let Some(found) = self.0.get(&key) {
97 return Ok(*found);
98 }
99 let found = solid.model.topology().mass_properties(solid.solid)?;
100 self.0.insert(key, found);
101 Ok(found)
102 }
103}
104
105pub fn own_solids_at<'a>(
110 reader: &Reader<'a>,
111 instance: &str,
112 pose: Option<Pose>,
113) -> GeopResult<Vec<PlacedSolid<'a>>> {
114 let model = read_model(reader)?;
115 let notes = notes_of(reader);
116 let drawn_only =
117 |name: &str| (notes.and_then(|notes| notes.get(name))).is_some_and(|note| note.drawn_only);
118 (model.solid_names().into_iter())
119 .filter(|name| !drawn_only(name))
120 .map(|name| {
121 Ok(PlacedSolid {
122 solid: model.solid_id(&name)?,
123 name: Located::in_instance(instance, name.as_str()).key(),
124 instance: instance.to_string(),
125 reader: reader.clone(),
126 model: model.clone(),
127 pose,
128 })
129 })
130 .collect()
131}
132
133pub fn own_solids<'a>(reader: &Reader<'a>) -> GeopResult<Vec<PlacedSolid<'a>>> {
136 own_solids_at(reader, "", None)
137}
138
139pub fn placed_solids<'a>(reader: &Reader<'a>) -> GeopResult<Vec<PlacedSolid<'a>>> {
144 let mut out = own_solids(reader)?;
145 for placed in placed_parts(reader)? {
146 let inner = placed.located.part.clone();
147 out.extend(own_solids_at(
148 &inner,
149 &placed.name,
150 Some(placed.located.world),
151 )?);
152 }
153 Ok(out)
154}