1pub mod dynamic;
9pub mod stack;
10pub mod stl;
11
12pub use dynamic::{DynamicBackend, dynamic_to_csg, dynamic_to_sketch};
13pub use stack::on_csg_stack;
14
15use glam::DVec3;
16use rscad_core::Object;
17
18pub trait CsgBackend {
25 type CsgType;
27 type SketchType;
29
30 fn empty_csg() -> Self::CsgType;
32 fn empty_sketch() -> Self::SketchType;
33
34 fn union(a: Self::CsgType, b: Self::CsgType) -> Self::CsgType;
36 fn difference(a: Self::CsgType, b: Self::CsgType) -> Self::CsgType;
37 fn intersection(a: Self::CsgType, b: Self::CsgType) -> Self::CsgType;
38 fn xor(a: Self::CsgType, b: Self::CsgType) -> Self::CsgType;
39
40 fn union_2d(a: Self::SketchType, b: Self::SketchType) -> Self::SketchType;
42 fn difference_2d(a: Self::SketchType, b: Self::SketchType) -> Self::SketchType;
43 fn intersection_2d(a: Self::SketchType, b: Self::SketchType) -> Self::SketchType;
44 fn xor_2d(a: Self::SketchType, b: Self::SketchType) -> Self::SketchType;
45
46 fn translate(csg: Self::CsgType, offset: DVec3) -> Self::CsgType;
48 fn rotate(csg: Self::CsgType, angles: DVec3) -> Self::CsgType;
49 fn scale(csg: Self::CsgType, factors: DVec3) -> Self::CsgType;
50 fn mirror(csg: Self::CsgType, axes: DVec3) -> Self::CsgType;
51
52 fn translate_2d(sketch: Self::SketchType, offset: DVec3) -> Self::SketchType;
54 fn rotate_2d(sketch: Self::SketchType, angles: DVec3) -> Self::SketchType;
55 fn scale_2d(sketch: Self::SketchType, factors: DVec3) -> Self::SketchType;
56 fn mirror_2d(sketch: Self::SketchType, axes: DVec3) -> Self::SketchType;
57
58 fn color(csg: Self::CsgType, color: [f32; 4]) -> Self::CsgType;
60 fn color_2d(sketch: Self::SketchType, color: [f32; 4]) -> Self::SketchType;
61
62 fn extrude(
64 sketch: Self::SketchType,
65 height: f64,
66 twist: f64,
67 scale: f64,
68 slices: usize,
69 ) -> Self::CsgType;
70
71 fn revolve(sketch: Self::SketchType, angle_degrees: f64, segments: usize) -> Self::CsgType;
76}
77
78pub trait ToCsg<B: CsgBackend> {
80 fn to_csg(&self) -> B::CsgType;
81}
82
83pub trait ToSketch<B: CsgBackend> {
85 fn to_sketch(&self) -> B::SketchType;
86}
87
88#[cfg(feature = "bevy")]
90pub trait CsgToMesh: CsgBackend {
91 fn to_bevy_mesh(solid: &Self::CsgType) -> bevy_mesh::Mesh;
92}
93
94pub trait CsgToStl: CsgBackend {
96 fn write_stl_binary(
97 solid: &Self::CsgType,
98 writer: &mut dyn std::io::Write,
99 ) -> std::io::Result<()>;
100
101 fn write_stl_ascii(
102 solid: &Self::CsgType,
103 writer: &mut dyn std::io::Write,
104 ) -> std::io::Result<()>;
105}
106
107impl<B: CsgBackend, T: Object + ToCsg<B>> ToCsg<B> for rscad_core::Translate<T> {
110 fn to_csg(&self) -> B::CsgType {
111 B::translate(self.object.to_csg(), self.translation)
112 }
113}
114
115impl<B: CsgBackend, T: Object + ToSketch<B>> ToSketch<B> for rscad_core::Translate<T> {
116 fn to_sketch(&self) -> B::SketchType {
117 B::translate_2d(self.object.to_sketch(), self.translation)
118 }
119}
120
121impl<B: CsgBackend, T: Object + ToCsg<B>> ToCsg<B> for rscad_core::Scale<T> {
122 fn to_csg(&self) -> B::CsgType {
123 B::scale(self.object.to_csg(), self.scale)
124 }
125}
126
127impl<B: CsgBackend, T: Object + ToSketch<B>> ToSketch<B> for rscad_core::Scale<T> {
128 fn to_sketch(&self) -> B::SketchType {
129 B::scale_2d(self.object.to_sketch(), self.scale)
130 }
131}
132
133impl<B: CsgBackend, T: Object + ToCsg<B>> ToCsg<B> for rscad_core::Rotate<T> {
134 fn to_csg(&self) -> B::CsgType {
135 B::rotate(self.object.to_csg(), self.rotation)
136 }
137}
138
139impl<B: CsgBackend, T: Object + ToSketch<B>> ToSketch<B> for rscad_core::Rotate<T> {
140 fn to_sketch(&self) -> B::SketchType {
141 B::rotate_2d(self.object.to_sketch(), self.rotation)
142 }
143}
144
145impl<B: CsgBackend, T: Object + ToCsg<B>> ToCsg<B> for rscad_core::Mirror<T> {
146 fn to_csg(&self) -> B::CsgType {
147 B::mirror(self.object.to_csg(), self.mirror)
148 }
149}
150
151impl<B: CsgBackend, T: Object + ToSketch<B>> ToSketch<B> for rscad_core::Mirror<T> {
152 fn to_sketch(&self) -> B::SketchType {
153 B::mirror_2d(self.object.to_sketch(), self.mirror)
154 }
155}
156
157impl<B: CsgBackend, A: Object + ToCsg<B>, O: Object + ToCsg<B>> ToCsg<B>
160 for rscad_core::Union<A, O>
161{
162 fn to_csg(&self) -> B::CsgType {
163 B::union(self.this.to_csg(), self.other.to_csg())
164 }
165}
166
167impl<B: CsgBackend, A: Object + ToSketch<B>, O: Object + ToSketch<B>> ToSketch<B>
168 for rscad_core::Union<A, O>
169{
170 fn to_sketch(&self) -> B::SketchType {
171 B::union_2d(self.this.to_sketch(), self.other.to_sketch())
172 }
173}
174
175impl<B: CsgBackend, A: Object + ToCsg<B>, O: Object + ToCsg<B>> ToCsg<B>
176 for rscad_core::Difference<A, O>
177{
178 fn to_csg(&self) -> B::CsgType {
179 B::difference(self.this.to_csg(), self.other.to_csg())
180 }
181}
182
183impl<B: CsgBackend, A: Object + ToSketch<B>, O: Object + ToSketch<B>> ToSketch<B>
184 for rscad_core::Difference<A, O>
185{
186 fn to_sketch(&self) -> B::SketchType {
187 B::difference_2d(self.this.to_sketch(), self.other.to_sketch())
188 }
189}
190
191impl<B: CsgBackend, A: Object + ToCsg<B>, O: Object + ToCsg<B>> ToCsg<B>
192 for rscad_core::Intersection<A, O>
193{
194 fn to_csg(&self) -> B::CsgType {
195 B::intersection(self.this.to_csg(), self.other.to_csg())
196 }
197}
198
199impl<B: CsgBackend, A: Object + ToSketch<B>, O: Object + ToSketch<B>> ToSketch<B>
200 for rscad_core::Intersection<A, O>
201{
202 fn to_sketch(&self) -> B::SketchType {
203 B::intersection_2d(self.this.to_sketch(), self.other.to_sketch())
204 }
205}
206
207impl<B: CsgBackend, A: Object + ToCsg<B>, O: Object + ToCsg<B>> ToCsg<B> for rscad_core::Xor<A, O> {
208 fn to_csg(&self) -> B::CsgType {
209 B::xor(self.this.to_csg(), self.other.to_csg())
210 }
211}
212
213impl<B: CsgBackend, A: Object + ToSketch<B>, O: Object + ToSketch<B>> ToSketch<B>
214 for rscad_core::Xor<A, O>
215{
216 fn to_sketch(&self) -> B::SketchType {
217 B::xor_2d(self.this.to_sketch(), self.other.to_sketch())
218 }
219}
220
221impl<B: CsgBackend, A: Object + ToCsg<B>, O: Object + ToCsg<B>> ToCsg<B>
224 for rscad_core::TwoContainer<A, O>
225{
226 fn to_csg(&self) -> B::CsgType {
227 B::union(self.a.to_csg(), self.b.to_csg())
228 }
229}
230
231impl<B: CsgBackend, A: Object + ToSketch<B>, O: Object + ToSketch<B>> ToSketch<B>
232 for rscad_core::TwoContainer<A, O>
233{
234 fn to_sketch(&self) -> B::SketchType {
235 B::union_2d(self.a.to_sketch(), self.b.to_sketch())
236 }
237}
238
239impl<B: CsgBackend, T: Object + ToCsg<B>> ToCsg<B> for rscad_core::Colored<T> {
240 fn to_csg(&self) -> B::CsgType {
241 let c = [
242 self.color.r as f32 / 255.0,
243 self.color.g as f32 / 255.0,
244 self.color.b as f32 / 255.0,
245 self.color.a as f32 / 255.0,
246 ];
247 B::color(self.object.to_csg(), c)
248 }
249}
250
251impl<B: CsgBackend, T: Object + ToSketch<B>> ToSketch<B> for rscad_core::Colored<T> {
252 fn to_sketch(&self) -> B::SketchType {
253 let c = [
254 self.color.r as f32 / 255.0,
255 self.color.g as f32 / 255.0,
256 self.color.b as f32 / 255.0,
257 self.color.a as f32 / 255.0,
258 ];
259 B::color_2d(self.object.to_sketch(), c)
260 }
261}
262
263impl<B: CsgBackend> ToCsg<B> for rscad_core::Empty {
264 fn to_csg(&self) -> B::CsgType {
265 B::empty_csg()
266 }
267}
268
269impl<B: CsgBackend> ToSketch<B> for rscad_core::Empty {
270 fn to_sketch(&self) -> B::SketchType {
271 B::empty_sketch()
272 }
273}
274
275impl<B: CsgBackend, T: Object + ToCsg<B>> ToCsg<B> for Option<T> {
276 fn to_csg(&self) -> B::CsgType {
277 match self {
278 Some(inner) => inner.to_csg(),
279 None => B::empty_csg(),
280 }
281 }
282}
283
284impl<B: CsgBackend, T: Object + ToSketch<B>> ToSketch<B> for Option<T> {
285 fn to_sketch(&self) -> B::SketchType {
286 match self {
287 Some(inner) => inner.to_sketch(),
288 None => B::empty_sketch(),
289 }
290 }
291}
292
293impl<B: CsgBackend, T: Object + ToCsg<B>> ToCsg<B> for Vec<T> {
294 fn to_csg(&self) -> B::CsgType {
295 self.iter()
296 .fold(B::empty_csg(), |acc, item| B::union(acc, item.to_csg()))
297 }
298}
299
300impl<B: CsgBackend, T: Object + ToSketch<B>> ToSketch<B> for Vec<T> {
301 fn to_sketch(&self) -> B::SketchType {
302 self.iter().fold(B::empty_sketch(), |acc, item| {
303 B::union_2d(acc, item.to_sketch())
304 })
305 }
306}
307
308impl<B: CsgBackend, T: Object + ToCsg<B>> ToCsg<B> for Box<T> {
309 fn to_csg(&self) -> B::CsgType {
310 (**self).to_csg()
311 }
312}
313
314impl<B: CsgBackend, T: Object + ToSketch<B>> ToSketch<B> for Box<T> {
315 fn to_sketch(&self) -> B::SketchType {
316 (**self).to_sketch()
317 }
318}
319
320impl<B: CsgBackend, T: Object + ToCsg<B>, const N: usize> ToCsg<B> for [T; N] {
321 fn to_csg(&self) -> B::CsgType {
322 self.iter()
323 .fold(B::empty_csg(), |acc, item| B::union(acc, item.to_csg()))
324 }
325}
326
327impl<B: CsgBackend, T: Object + ToSketch<B>, const N: usize> ToSketch<B> for [T; N] {
328 fn to_sketch(&self) -> B::SketchType {
329 self.iter().fold(B::empty_sketch(), |acc, item| {
330 B::union_2d(acc, item.to_sketch())
331 })
332 }
333}
334
335impl<B: CsgBackend> ToCsg<B> for () {
336 fn to_csg(&self) -> B::CsgType {
337 B::empty_csg()
338 }
339}
340
341impl<B: CsgBackend> ToSketch<B> for () {
342 fn to_sketch(&self) -> B::SketchType {
343 B::empty_sketch()
344 }
345}
346
347impl<B: CsgBackend, T: Object + ToSketch<B>> ToCsg<B> for rscad_core::extrude::LinearExtrude<T> {
350 fn to_csg(&self) -> B::CsgType {
351 B::extrude(
352 self.object.to_sketch(),
353 self.height,
354 self.twist,
355 self.scale,
356 self.slices,
357 )
358 }
359}
360
361impl<B: CsgBackend, T: Object + ToSketch<B>> ToCsg<B> for rscad_core::extrude::RotateExtrude<T> {
364 fn to_csg(&self) -> B::CsgType {
365 B::revolve(self.object.to_sketch(), self.angle_degrees, self.fn_)
366 }
367}