microcad_lang/render/
mod.rs1mod cache;
7mod context;
8mod output;
9
10use std::rc::Rc;
11
12pub use cache::*;
13pub use context::*;
14pub use output::*;
15
16use cgmath::SquareMatrix;
17use microcad_core::*;
18use thiserror::Error;
19
20use crate::{
21 builtin::{BuiltinWorkbenchKind, BuiltinWorkpiece, BuiltinWorkpieceOutput},
22 model::*,
23 tree_display::FormatTree,
24};
25
26#[derive(Debug, Error)]
28pub enum RenderError {
29 #[error("Invalid output type: {0}")]
31 InvalidOutputType(OutputType),
32
33 #[error("Nothing to render")]
35 NothingToRender,
36}
37
38pub type RenderResult<T> = Result<T, RenderError>;
40
41pub trait Render<T> {
43 fn render(&self, context: &mut RenderContext) -> RenderResult<T>;
45}
46
47impl Element {
48 pub fn get_affine_transform(&self) -> RenderResult<Option<AffineTransform>> {
50 match &self {
51 Element::BuiltinWorkpiece(builtin_workpiece) => match builtin_workpiece.kind {
52 BuiltinWorkbenchKind::Transform => match builtin_workpiece.call()? {
53 BuiltinWorkpieceOutput::Transform(affine_transform) => {
54 Ok(Some(affine_transform))
55 }
56 _ => unreachable!(),
57 },
58 _ => Ok(None),
59 },
60 _ => Ok(None),
61 }
62 }
63}
64
65impl ModelInner {
66 pub fn resolution(&self) -> RenderResolution {
68 let output = self.output.as_ref().expect("Some render output.");
69
70 match output {
71 RenderOutput::Geometry2D { resolution, .. }
72 | RenderOutput::Geometry3D { resolution, .. } => {
73 resolution.as_ref().expect("Some resolution.").clone()
74 }
75 }
76 }
77}
78
79impl Model {
80 pub fn prerender(&self, resolution: RenderResolution) -> RenderResult<()> {
85 pub fn create_render_output(model: &Model) -> RenderResult<()> {
86 let output = RenderOutput::new(model)?;
87 {
88 let mut model_ = model.borrow_mut();
89 model_.output = Some(output);
90 };
91
92 model
93 .borrow()
94 .children
95 .iter()
96 .try_for_each(create_render_output)
97 }
98
99 pub fn set_world_matrix(model: &Model, matrix: Mat4) -> RenderResult<()> {
100 let world_matrix = {
101 let mut model_ = model.borrow_mut();
102 let output = model_.output.as_mut().expect("Output");
103 let world_matrix = matrix * output.local_matrix().unwrap_or(Mat4::identity());
104 output.set_world_matrix(world_matrix);
105 world_matrix
106 };
107
108 model
109 .borrow()
110 .children
111 .iter()
112 .try_for_each(|model| set_world_matrix(model, world_matrix))
113 }
114
115 pub fn set_resolution(model: &Model, resolution: RenderResolution) {
117 let new_resolution = {
118 let mut model_ = model.borrow_mut();
119 let output = model_.output.as_mut().expect("Output");
120
121 let resolution = resolution * output.local_matrix().unwrap_or(Mat4::identity());
122 output.set_resolution(resolution.clone());
123 resolution
124 };
125
126 model.borrow().children.iter().for_each(|model| {
127 set_resolution(model, new_resolution.clone());
128 });
129 }
130
131 create_render_output(self)?;
133
134 set_world_matrix(self, Mat4::identity())?;
136
137 set_resolution(self, resolution);
139
140 log::trace!("Finished prerender:\n{}", FormatTree(self));
141
142 Ok(())
143 }
144}
145
146impl FetchBounds2D for Model {
147 fn fetch_bounds_2d(&self) -> Bounds2D {
148 let self_ = self.borrow();
149 match self_.output() {
150 RenderOutput::Geometry2D { geometry, .. } => match geometry {
151 Some(geometry) => geometry.fetch_bounds_2d(),
152 None => Bounds2D::default(),
153 },
154 RenderOutput::Geometry3D { .. } => Bounds2D::default(),
155 }
156 }
157}
158
159impl Render<Geometry2DOutput> for Model {
165 fn render(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
166 context.with_model(self.clone(), |context| {
167 let model = context.model();
168 let geometry = {
169 let model_ = model.borrow();
170 let output = model.deduce_output_type();
171 match output {
172 OutputType::Geometry2D => {
173 match model_.element() {
174 Element::BuiltinWorkpiece(builtin_workpiece) => {
176 builtin_workpiece.render(context)
177 }
178 _ => model_.children.render(context),
179 }
180 }
181 _ => Ok(None),
182 }
183 }?;
184
185 self.borrow_mut()
186 .output_mut()
187 .set_geometry_2d(geometry.clone());
188 Ok(geometry)
189 })
190 }
191}
192
193impl Render<Geometry3DOutput> for Model {
199 fn render(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
200 context.with_model(self.clone(), |context| {
201 let model = context.model();
202 let geometry = {
203 let model_ = model.borrow();
204 let output = model.deduce_output_type();
205 match output {
206 OutputType::Geometry3D => {
207 match model_.element() {
208 Element::BuiltinWorkpiece(builtin_workpiece) => {
210 builtin_workpiece.render(context)
211 }
212 _ => model_.children.render(context),
213 }
214 }
215 _ => Ok(None),
216 }
217 }?;
218
219 self.borrow_mut()
220 .output_mut()
221 .set_geometry_3d(geometry.clone());
222 Ok(geometry)
223 })
224 }
225}
226
227impl Render<Model> for Model {
228 fn render(&self, context: &mut RenderContext) -> RenderResult<Model> {
229 match self.deduce_output_type() {
230 OutputType::Geometry2D => {
231 let _: Geometry2DOutput = self.render(context)?;
232 }
233 OutputType::Geometry3D => {
234 let _: Geometry3DOutput = self.render(context)?;
235 }
236 output_type => {
237 log::warn!("Nothing to render: {output_type}");
238 }
239 }
240 log::trace!("Finished render:\n{}", FormatTree(self));
241
242 Ok(self.clone())
243 }
244}
245
246impl Render<Geometries2D> for Models {
247 fn render(&self, context: &mut RenderContext) -> RenderResult<Geometries2D> {
248 let mut geometries = Vec::new();
249 for model in self.iter() {
250 if let Some(geo) = model.render(context)? {
251 geometries.push(geo);
252 }
253 }
254
255 Ok(geometries.into_iter().collect())
256 }
257}
258
259impl Render<Geometry2DOutput> for Models {
260 fn render(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
261 Ok(match self.len() {
262 0 => None,
263 1 => self.first().expect("One item").render(context)?,
264 _ => Some(Rc::new(Geometry2D::Collection(self.render(context)?))),
265 })
266 }
267}
268
269impl Render<Geometries3D> for Models {
270 fn render(&self, context: &mut RenderContext) -> RenderResult<Geometries3D> {
271 let mut geometries = Vec::new();
272 for model in self.iter() {
273 if let Some(geo) = model.render(context)? {
274 geometries.push(geo);
275 }
276 }
277
278 Ok(geometries.into_iter().collect())
279 }
280}
281
282impl Render<Geometry3DOutput> for Models {
283 fn render(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
284 Ok(match self.len() {
285 0 => None,
286 1 => self.first().expect("One item").render(context)?,
287 _ => Some(Rc::new(Geometry3D::Collection(self.render(context)?))),
288 })
289 }
290}
291
292impl Render<Geometry2DOutput> for BuiltinWorkpiece {
293 fn render(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
294 Ok(match self.call()? {
295 BuiltinWorkpieceOutput::Primitive2D(geo2d) => Some(Rc::new(geo2d)),
296 BuiltinWorkpieceOutput::Transform(transform) => {
297 let model = context.model();
298 let model_ = model.borrow();
299 let geometry: Geometry2DOutput = model_.children.render(context)?;
300
301 geometry.map(|geometry| {
302 Rc::new(
303 geometry.transformed_2d(&context.current_resolution(), &transform.mat2d()),
304 )
305 })
306 }
307 BuiltinWorkpieceOutput::Operation(operation) => operation.process_2d(context)?,
308 _ => None,
309 })
310 }
311}
312
313impl Render<Geometry3DOutput> for BuiltinWorkpiece {
314 fn render(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
315 Ok(match self.call()? {
316 BuiltinWorkpieceOutput::Primitive3D(geo3d) => Some(Rc::new(geo3d)),
317 BuiltinWorkpieceOutput::Transform(transform) => {
318 let model = context.model();
319 let model_ = model.borrow();
320 let geometry: Geometry3DOutput = model_.children.render(context)?;
321
322 geometry.map(|geometry| {
323 Rc::new(
324 geometry.transformed_3d(&context.current_resolution(), &transform.mat3d()),
325 )
326 })
327 }
328 BuiltinWorkpieceOutput::Operation(operation) => operation.process_3d(context)?,
329 _ => None,
330 })
331 }
332}