Skip to main content

microcad_lang/model/
mod.rs

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