1pub 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#[derive(Clone, Debug)]
51pub struct Drawn {
52 pub key: String,
53 pub view: Rc<PartView>,
54}
55
56impl PartialEq for Drawn {
58 fn eq(&self, other: &Self) -> bool {
59 self.key == other.key
60 }
61}
62
63impl 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
82impl 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
95pub fn drawn_target(name: &str) -> TargetReference<Drawn> {
97 TargetReference::new(format!("drawn:{name}"))
98}
99
100pub 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
111pub struct Located<'a> {
115 pub instance: &'a Instance,
116 pub part: Reader<'a>,
117 pub world: Pose,
118 pub local: Pose,
120 pub state: Option<String>,
121}
122
123pub 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 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
154pub 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
172pub struct Placed<'a> {
174 pub name: String,
175 pub located: Located<'a>,
176 pub parent: Option<usize>,
179}
180
181pub 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
211pub 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}