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, rc::RcMut, src_ref::SrcReferrer, syntax::Identifier, tree_display::*,
36    value::Value,
37};
38
39/// A reference counted, mutable [`Model`].
40#[derive(Debug, 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    /// Calculate depth of the model.
50    pub fn depth(&self) -> usize {
51        self.parents().count()
52    }
53
54    /// Check if a model contains an operation element.
55    pub fn is_operation(&self) -> bool {
56        self.borrow().element.is_operation()
57    }
58
59    /// Make a deep copy if this model.
60    /// TODO: isn't this a Clone?
61    pub fn make_deep_copy(&self) -> Self {
62        let copy = Self(RcMut::new(self.0.borrow().clone_content()));
63        for child in self.borrow().children.iter() {
64            copy.append(child.make_deep_copy());
65        }
66        copy
67    }
68
69    /// Return address of this model.
70    pub fn addr(&self) -> usize {
71        self.0.as_ptr().addr()
72    }
73
74    /// Check if `other` is and `self` have the same address.
75    pub fn is_same_as(&self, other: &Model) -> bool {
76        self.addr() == other.addr()
77    }
78
79    /// Remove child from this model.
80    pub fn remove_child(&self, child: &Model) {
81        let mut s = self.0.borrow_mut();
82        s.children.retain(|model| !model.is_same_as(child));
83    }
84
85    /// Detaches a model from its parent. Children are not affected.
86    pub fn detach(&self) {
87        match self.0.borrow_mut().parent {
88            Some(ref mut parent) => {
89                parent.remove_child(self);
90            }
91            None => return,
92        }
93
94        self.0.borrow_mut().parent = None;
95    }
96
97    /// Append a single model as child.
98    ///
99    /// Also tries to set the output type if it has not been determined yet.
100    pub fn append(&self, model: Model) -> Model {
101        model.borrow_mut().parent = Some(self.clone());
102
103        let mut self_ = self.0.borrow_mut();
104        self_.children.push(model.clone());
105
106        model
107    }
108
109    /// Append multiple models as children.
110    ///
111    /// Return self.
112    pub fn append_children(&self, models: Models) -> Self {
113        for model in models.iter() {
114            self.append(model.clone());
115        }
116        self.clone()
117    }
118
119    /// Short cut to generate boolean operator as binary operation with two models.
120    pub fn boolean_op(self, op: BooleanOp, other: Model) -> Model {
121        assert!(self != other, "lhs and rhs must be distinct.");
122        Models::from(vec![self.clone(), other]).boolean_op(op)
123    }
124
125    /// Replace each input placeholder with copies of `input_model`.
126    pub fn replace_input_placeholders(&self, input_model: &Model) -> Self {
127        self.descendants().for_each(|model| {
128            let mut model_ = model.borrow_mut();
129            if model_.id.is_none() && matches!(model_.element.value, Element::InputPlaceholder) {
130                let mut input_model_ = input_model.borrow_mut();
131                input_model_.parent = Some(self.clone());
132                *model_ = input_model_.clone_content();
133                model_.children = input_model_.children.clone();
134            }
135        });
136        self.clone()
137    }
138
139    /// Deduce output type from children and set it and return it.
140    pub fn deduce_output_type(&self) -> OutputType {
141        let self_ = self.borrow();
142        let mut output_type = self_.element.output_type();
143        if output_type == OutputType::NotDetermined {
144            let children = &self_.children;
145            output_type = children.deduce_output_type();
146        }
147
148        output_type
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        let children = &self.borrow().children;
157        if children.len() != 1 {
158            return None;
159        }
160
161        children.first().and_then(|n| {
162            if let Element::Group = *n.0.borrow().element {
163                Some(n.clone())
164            } else {
165                None
166            }
167        })
168    }
169
170    /// A [`Model`] signature has the form `[id: ]ElementType[ = origin][ -> result_type]`.
171    pub fn signature(&self) -> String {
172        format!(
173            "{id}{element}{is_root} ->",
174            id = match &self.borrow().id {
175                Some(id) => format!("{id}: "),
176                None => String::new(),
177            },
178            element = *self.borrow().element,
179            is_root = if self.parents().next().is_some() {
180                ""
181            } else {
182                " (root)"
183            }
184        )
185    }
186}
187
188/// Iterator methods.
189impl Model {
190    /// Returns an iterator of models to this model and its unnamed descendants, in tree order.
191    ///
192    /// Includes the current model.
193    pub fn descendants(&self) -> Descendants {
194        Descendants::new(self.clone())
195    }
196
197    /// Returns an iterator of models that belong to the same source file as this one
198    pub fn source_file_descendants(&self) -> SourceFileDescendants {
199        SourceFileDescendants::new(self.clone())
200    }
201
202    /// Parents iterator.
203    pub fn parents(&self) -> Parents {
204        Parents::new(self.clone())
205    }
206
207    /// Ancestors iterator.
208    pub fn ancestors(&self) -> Ancestors {
209        Ancestors::new(self.clone())
210    }
211
212    /// Get a property from this model.
213    pub fn get_property(&self, id: &Identifier) -> Option<Value> {
214        self.borrow().element.get_property(id).cloned()
215    }
216
217    /// Set a property in this model.
218    pub fn set_property(&mut self, id: Identifier, value: Value) -> Option<Value> {
219        self.borrow_mut().element.set_property(id, value)
220    }
221
222    /// Add a new property to the model.
223    pub fn add_property(&self, id: Identifier, value: Value) {
224        self.borrow_mut()
225            .element
226            .add_properties([(id, value)].into_iter().collect())
227    }
228}
229
230impl AttributesAccess for Model {
231    fn get_attributes_by_id(&self, id: &Identifier) -> Vec<Attribute> {
232        self.borrow().attributes.get_attributes_by_id(id)
233    }
234}
235
236impl PartialEq for Model {
237    fn eq(&self, other: &Self) -> bool {
238        self.addr() == other.addr()
239    }
240}
241
242impl SrcReferrer for Model {
243    fn src_ref(&self) -> crate::src_ref::SrcRef {
244        self.borrow().src_ref()
245    }
246}
247
248/// Prints a [`Model`].
249///
250/// A [`Model`] signature has the form `[id: ]ElementType[ = origin][ -> result_type]`.
251/// The exemplary output will look like this:
252///
253/// ```custom
254/// id: Object:
255///     Object = std::geo2d::Circle(radius = 3.0mm) -> Geometry2D:
256///         Primitive = __builtin::geo2d::Circle(radius = 3.0) -> Geometry2D`
257/// ```
258impl std::fmt::Display for Model {
259    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260        write!(
261            f,
262            "{signature}",
263            signature = crate::shorten!(self.signature())
264        )
265    }
266}
267
268impl TreeDisplay for Model {
269    fn tree_print(
270        &self,
271        f: &mut std::fmt::Formatter,
272        mut tree_state: TreeState,
273    ) -> std::fmt::Result {
274        let signature = crate::shorten!(self.signature(), tree_state.shorten);
275        let self_ = self.borrow();
276        if let Some(output) = &self_.output {
277            writeln!(f, "{:tree_state$}{signature} {output}", "",)?;
278        } else {
279            writeln!(f, "{:tree_state$}{signature}", "",)?;
280        }
281        tree_state.indent();
282        if let Some(props) = self_.get_properties() {
283            props.tree_print(f, tree_state)?;
284        }
285        self_.attributes.tree_print(f, tree_state)?;
286        self_.children.tree_print(f, tree_state)
287    }
288}
289
290impl WriteToFile for Model {}