microcad_lang/model/
mod.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Model tree module
5
6pub mod attribute;
7pub mod builder;
8pub mod creator;
9pub mod element;
10mod inner;
11pub mod iter;
12pub mod models;
13pub mod operation;
14pub mod output_type;
15pub mod properties;
16pub mod workpiece;
17
18pub use attribute::*;
19pub use builder::*;
20pub use creator::*;
21pub use element::*;
22pub use inner::*;
23pub use iter::*;
24pub use models::*;
25pub use operation::*;
26pub use output_type::*;
27pub use properties::*;
28pub use workpiece::*;
29
30use derive_more::{Deref, DerefMut};
31
32use microcad_core::BooleanOp;
33
34use crate::{
35    diag::WriteToFile,
36    rc::RcMut,
37    render::{ComputedHash, HashId, RenderOutput},
38    src_ref::SrcReferrer,
39    syntax::Identifier,
40    tree_display::*,
41    value::Value,
42};
43
44/// A reference counted, mutable [`Model`].
45#[derive(Clone, Deref, DerefMut)]
46pub struct Model(RcMut<ModelInner>);
47
48impl Model {
49    /// Create new model from inner.
50    pub fn new(inner: RcMut<ModelInner>) -> Self {
51        Self(inner)
52    }
53
54    /// Calculate depth of the model.
55    pub fn depth(&self) -> usize {
56        self.parents().count()
57    }
58
59    /// Check if a model contains an operation element.
60    pub fn is_operation(&self) -> bool {
61        self.borrow().element.is_operation()
62    }
63
64    /// Return `true`, if model has no children.
65    pub fn is_empty(&self) -> bool {
66        self.borrow().is_empty()
67    }
68
69    /// Return `true`, if model wont produce any output
70    pub fn is_empty_model(&self) -> bool {
71        let self_ = self.borrow();
72        match self_.element.value {
73            Element::BuiltinWorkpiece(_) | Element::InputPlaceholder => false,
74            _ => self_.is_empty(),
75        }
76    }
77
78    /// Make a deep copy if this model.
79    /// TODO: isn't this a Clone?
80    pub fn make_deep_copy(&self) -> Self {
81        let copy = Self(RcMut::new(self.0.borrow().clone_content()));
82        for child in self.borrow().children.iter() {
83            copy.append(child.make_deep_copy());
84        }
85        copy
86    }
87
88    /// Return address of this model.
89    pub fn addr(&self) -> usize {
90        self.0.as_ptr().addr()
91    }
92
93    /// Check if `other` is and `self` have the same address.
94    pub fn is_same_as(&self, other: &Model) -> bool {
95        self.addr() == other.addr()
96    }
97
98    /// Remove child from this model.
99    pub fn remove_child(&self, child: &Model) {
100        let mut s = self.0.borrow_mut();
101        s.children.retain(|model| !model.is_same_as(child));
102    }
103
104    /// Detaches a model from its parent. Children are not affected.
105    pub fn detach(&self) {
106        match self.0.borrow_mut().parent {
107            Some(ref mut parent) => {
108                parent.remove_child(self);
109            }
110            None => return,
111        }
112
113        self.0.borrow_mut().parent = None;
114    }
115
116    /// Append a single model as child.
117    ///
118    /// Also tries to set the output type if it has not been determined yet.
119    pub fn append(&self, model: Model) -> Model {
120        model.borrow_mut().parent = Some(self.clone());
121
122        let mut self_ = self.0.borrow_mut();
123        self_.children.push(model.clone());
124
125        model
126    }
127
128    /// Append multiple models as children.
129    ///
130    /// Return self.
131    pub fn append_children(&self, models: Models) -> Self {
132        for model in models.iter() {
133            self.append(model.clone());
134        }
135        self.clone()
136    }
137
138    /// Short cut to generate boolean operator as binary operation with two models.
139    pub fn boolean_op(self, op: BooleanOp, other: Model) -> Model {
140        assert!(self != other, "lhs and rhs must be distinct.");
141        Models::from(vec![self.clone(), other]).boolean_op(op)
142    }
143
144    /// Replace each input placeholder with copies of `input_model`.
145    pub fn replace_input_placeholders(&self, input_model: &Model) -> Self {
146        self.descendants().for_each(|model| {
147            let mut model_ = model.borrow_mut();
148            if model_.id.is_none() && matches!(model_.element.value, Element::InputPlaceholder) {
149                let mut input_model_ = input_model.borrow_mut();
150                input_model_.parent = Some(self.clone());
151                *model_ = input_model_.clone_content();
152                model_.children = input_model_.children.clone();
153            }
154        });
155        self.clone()
156    }
157
158    /// Deduce output type from children and set it and return it.
159    pub fn deduce_output_type(&self) -> OutputType {
160        let self_ = self.borrow();
161        let mut output_type = self_.element.output_type();
162        if output_type == OutputType::NotDetermined {
163            let children = &self_.children;
164            output_type = children.deduce_output_type();
165        }
166
167        output_type
168    }
169
170    /// Get render output type. Expects a render output.
171    pub fn render_output_type(&self) -> OutputType {
172        let self_ = self.borrow();
173        match self_.output {
174            Some(RenderOutput::Geometry2D { .. }) => OutputType::Geometry2D,
175            Some(RenderOutput::Geometry3D { .. }) => OutputType::Geometry3D,
176            None => OutputType::InvalidMixed,
177        }
178    }
179
180    /// Return inner group if this model only contains a group as single child.
181    ///
182    /// This function is used when we evaluate operations like `subtract() {}` or `hull() {}`.
183    /// When evaluating these operations, we want to iterate over the group's children.
184    pub fn into_group(&self) -> Option<Model> {
185        let children = &self.borrow().children;
186        if children.len() != 1 {
187            return None;
188        }
189
190        children.first().and_then(|n| {
191            if let Element::Group = *n.0.borrow().element {
192                Some(n.clone())
193            } else {
194                None
195            }
196        })
197    }
198}
199
200/// Iterator methods.
201impl Model {
202    /// Returns an iterator of models to this model and its unnamed descendants, in tree order.
203    ///
204    /// Includes the current model.
205    pub fn descendants(&self) -> Descendants {
206        Descendants::new(self.clone())
207    }
208
209    /// Returns an iterator of models that belong to the same source file as this one
210    pub fn source_file_descendants(&self) -> SourceFileDescendants {
211        SourceFileDescendants::new(self.clone())
212    }
213
214    /// Parents iterator.
215    pub fn parents(&self) -> Parents {
216        Parents::new(self.clone())
217    }
218
219    /// Ancestors iterator.
220    pub fn ancestors(&self) -> Ancestors {
221        Ancestors::new(self.clone())
222    }
223
224    /// Get a property from this model.
225    pub fn get_property(&self, id: &Identifier) -> Option<Value> {
226        self.borrow().element.get_property(id).cloned()
227    }
228
229    /// Set a property in this model.
230    pub fn set_property(&mut self, id: Identifier, value: Value) -> Option<Value> {
231        self.borrow_mut().element.set_property(id, value)
232    }
233
234    /// Add a new property to the model.
235    pub fn add_property(&self, id: Identifier, value: Value) {
236        self.borrow_mut()
237            .element
238            .add_properties([(id, value)].into_iter().collect())
239    }
240}
241
242impl AttributesAccess for Model {
243    fn get_attributes_by_id(&self, id: &Identifier) -> Vec<Attribute> {
244        self.borrow().attributes.get_attributes_by_id(id)
245    }
246}
247
248impl PartialEq for Model {
249    fn eq(&self, other: &Self) -> bool {
250        self.addr() == other.addr()
251    }
252}
253
254impl SrcReferrer for Model {
255    fn src_ref(&self) -> crate::src_ref::SrcRef {
256        self.borrow().src_ref()
257    }
258}
259
260impl std::fmt::Display for Model {
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        write!(
263            f,
264            "{id}{element}{is_root} ->",
265            id = match &self.borrow().id {
266                Some(id) => format!("{id}: "),
267                None => String::new(),
268            },
269            element = *self.borrow().element,
270            is_root = if self.parents().next().is_some() {
271                ""
272            } else {
273                " (root)"
274            }
275        )
276    }
277}
278
279impl std::fmt::Debug for Model {
280    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281        write!(
282            f,
283            "{}",
284            crate::shorten!(format!(
285                "{id}{element}{is_root} ->",
286                id = match &self.borrow().id {
287                    Some(id) => format!("{id:?}: "),
288                    None => String::new(),
289                },
290                element = *self.borrow().element,
291                is_root = if self.parents().next().is_some() {
292                    ""
293                } else {
294                    " (root)"
295                }
296            ))
297        )
298    }
299}
300
301impl TreeDisplay for Model {
302    fn tree_print(
303        &self,
304        f: &mut std::fmt::Formatter,
305        mut tree_state: TreeState,
306    ) -> std::fmt::Result {
307        let signature = if tree_state.debug {
308            format!("{self:?}")
309        } else {
310            self.to_string()
311        };
312        let self_ = self.borrow();
313        if let Some(output) = &self_.output {
314            writeln!(f, "{:tree_state$}{signature} {output}", "",)?;
315        } else {
316            writeln!(f, "{:tree_state$}{signature}", "",)?;
317        }
318        tree_state.indent();
319        if let Some(props) = self_.get_properties() {
320            props.tree_print(f, tree_state)?;
321        }
322        self_.attributes.tree_print(f, tree_state)?;
323        self_.children.tree_print(f, tree_state)
324    }
325}
326
327impl WriteToFile for Model {}
328
329impl std::hash::Hash for Model {
330    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
331        let self_ = self.borrow();
332        self_.element().hash(state);
333        self_.children().for_each(|child| child.hash(state));
334    }
335}
336
337impl ComputedHash for Model {
338    fn computed_hash(&self) -> HashId {
339        let self_ = self.borrow();
340        self_.output().computed_hash()
341    }
342}