Skip to main content

microcad_lang/model/
mod.rs

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