microcad_lang/builtin/
operation.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Builtin boolean operations.
5
6use microcad_core::{BooleanOp, Geometry2D};
7
8use crate::{builtin::*, model::*, render::*, src_ref::SrcRef, value::Tuple};
9
10impl Operation for BooleanOp {
11    fn process_2d(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
12        context.update_2d(|context, model| {
13            let model = model.into_group().unwrap_or(model);
14            let model_ = model.borrow();
15            let geometries: Geometries2D = model_.children.render_with_context(context)?;
16
17            Ok(Geometry2D::MultiPolygon(geometries.boolean_op(self)))
18        })
19    }
20
21    fn process_3d(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
22        context.update_3d(|context, model| {
23            let model = model.into_group().unwrap_or(model);
24            let model_ = model.borrow();
25            let geometries: Geometries3D = model_.children.render_with_context(context)?;
26
27            Ok(Geometry3D::Manifold(geometries.boolean_op(self)))
28        })
29    }
30}
31
32/// Union operation.
33pub struct Union;
34
35impl BuiltinWorkbenchDefinition for Union {
36    fn id() -> &'static str {
37        "union"
38    }
39
40    fn kind() -> BuiltinWorkbenchKind {
41        BuiltinWorkbenchKind::Operation
42    }
43
44    fn workpiece_function() -> &'static BuiltinWorkpieceFn {
45        &|_| {
46            Ok(BuiltinWorkpieceOutput::Operation(Box::new(
47                BooleanOp::Union,
48            )))
49        }
50    }
51}
52
53/// Difference operation.
54pub struct Subtract;
55
56impl BuiltinWorkbenchDefinition for Subtract {
57    fn id() -> &'static str {
58        "subtract"
59    }
60
61    fn kind() -> BuiltinWorkbenchKind {
62        BuiltinWorkbenchKind::Operation
63    }
64
65    fn workpiece_function() -> &'static BuiltinWorkpieceFn {
66        &|_| {
67            Ok(BuiltinWorkpieceOutput::Operation(Box::new(
68                BooleanOp::Subtract,
69            )))
70        }
71    }
72}
73
74/// Intersection operation.
75pub struct Intersect;
76
77impl BuiltinWorkbenchDefinition for Intersect {
78    fn id() -> &'static str {
79        "intersect"
80    }
81
82    fn kind() -> BuiltinWorkbenchKind {
83        BuiltinWorkbenchKind::Operation
84    }
85
86    fn workpiece_function() -> &'static BuiltinWorkpieceFn {
87        &|_| {
88            Ok(BuiltinWorkpieceOutput::Operation(Box::new(
89                BooleanOp::Intersect,
90            )))
91        }
92    }
93}
94
95/// An operation that repeats a geometry n-1 times.
96pub struct Multiply;
97
98impl Operation for Multiply {
99    fn process_2d(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
100        context.update_2d(|context, model| {
101            Ok(Geometry2D::Collection(
102                model.borrow().children.render_with_context(context)?,
103            ))
104        })
105    }
106
107    fn process_3d(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
108        context.update_3d(|context, model| {
109            Ok(Geometry3D::Collection(
110                model.borrow().children.render_with_context(context)?,
111            ))
112        })
113    }
114}
115
116impl BuiltinWorkbenchDefinition for Multiply {
117    fn id() -> &'static str {
118        "multiply"
119    }
120
121    fn kind() -> BuiltinWorkbenchKind {
122        BuiltinWorkbenchKind::Operation
123    }
124
125    fn workpiece_function() -> &'static BuiltinWorkpieceFn {
126        &|_| Ok(BuiltinWorkpieceOutput::Operation(Box::new(Multiply)))
127    }
128
129    fn model(creator: Creator) -> Model {
130        let n: Integer = creator.arguments.get("n");
131        let model = ModelBuilder::new(Element::Multiplicity, SrcRef(None)).build();
132
133        model.append_children(
134            ModelBuilder::new(Element::InputPlaceholder, SrcRef(None))
135                .build()
136                .multiply(n)
137                .into(),
138        );
139
140        model
141    }
142
143    fn parameters() -> ParameterValueList {
144        [parameter!(n: Integer)].into_iter().collect()
145    }
146}
147
148impl From<BooleanOp> for BuiltinWorkpiece {
149    fn from(value: BooleanOp) -> Self {
150        match value {
151            BooleanOp::Union => Union::workpiece(Creator::new(Union::symbol(), Tuple::default())),
152            BooleanOp::Subtract => {
153                Subtract::workpiece(Creator::new(Subtract::symbol(), Tuple::default()))
154            }
155            BooleanOp::Intersect => {
156                Intersect::workpiece(Creator::new(Intersect::symbol(), Tuple::default()))
157            }
158            BooleanOp::Complement => unimplemented!(),
159        }
160    }
161}