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 RenderWithContext<T> {
43 fn render_with_context(&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 CalcBounds2D for Model {
147 fn calc_bounds_2d(&self) -> Bounds2D {
148 let self_ = self.borrow();
149 match self_.output() {
150 RenderOutput::Geometry2D { geometry, .. } => match geometry {
151 Some(geometry) => geometry.bounds.clone(),
152 None => Bounds2D::default(),
153 },
154 RenderOutput::Geometry3D { .. } => Bounds2D::default(),
155 }
156 }
157}
158
159impl RenderWithContext<Geometry2DOutput> for Model {
165 fn render_with_context(&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_with_context(context)
177 }
178 _ => model_.children.render_with_context(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 RenderWithContext<Geometry3DOutput> for Model {
199 fn render_with_context(&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_with_context(context)
211 }
212 _ => model_.children.render_with_context(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 RenderWithContext<Model> for Model {
228 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Model> {
229 match self.deduce_output_type() {
230 OutputType::Geometry2D => {
231 let _: Geometry2DOutput = self.render_with_context(context)?;
232 }
233 OutputType::Geometry3D => {
234 let _: Geometry3DOutput = self.render_with_context(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 RenderWithContext<Geometries2D> for Models {
247 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometries2D> {
248 let mut geometries = Vec::new();
249 for model in self.iter() {
250 let output: Geometry2DOutput = model.render_with_context(context)?;
251 if let Some(geo) = output {
252 geometries.push(Rc::new(geo.inner.clone()));
253 }
254 }
255
256 Ok(geometries.into_iter().collect())
257 }
258}
259
260impl RenderWithContext<Geometry2DOutput> for Models {
261 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
262 Ok(match self.len() {
263 0 => None,
264 1 => self
265 .first()
266 .expect("One item")
267 .render_with_context(context)?,
268 _ => Some(Rc::new(
269 Geometry2D::Collection(self.render_with_context(context)?).into(),
270 )),
271 })
272 }
273}
274
275impl RenderWithContext<Geometries3D> for Models {
276 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometries3D> {
277 let mut geometries = Vec::new();
278 for model in self.iter() {
279 let output: Geometry3DOutput = model.render_with_context(context)?;
280 if let Some(geo) = output {
281 geometries.push(Rc::new(geo.inner.clone()));
282 }
283 }
284
285 Ok(geometries.into_iter().collect())
286 }
287}
288
289impl RenderWithContext<Geometry3DOutput> for Models {
290 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
291 Ok(match self.len() {
292 0 => None,
293 1 => self
294 .first()
295 .expect("One item")
296 .render_with_context(context)?,
297 _ => Some(Rc::new(
298 Geometry3D::Collection(self.render_with_context(context)?).into(),
299 )),
300 })
301 }
302}
303
304impl RenderWithContext<Geometry2DOutput> for BuiltinWorkpiece {
305 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
306 Ok(match self.call()? {
307 BuiltinWorkpieceOutput::Primitive2D(renderable) => Some(Rc::new(
308 renderable.render(&context.current_resolution()).into(),
309 )),
310 BuiltinWorkpieceOutput::Transform(transform) => {
311 let model = context.model();
312 let model_ = model.borrow();
313 let output: Geometry2DOutput = model_.children.render_with_context(context)?;
314 output.map(|geometry| Rc::new(geometry.transformed_2d(&transform.mat2d())))
315 }
316 BuiltinWorkpieceOutput::Operation(operation) => operation.process_2d(context)?,
317 _ => None,
318 })
319 }
320}
321
322impl RenderWithContext<Geometry3DOutput> for BuiltinWorkpiece {
323 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
324 Ok(match self.call()? {
325 BuiltinWorkpieceOutput::Primitive3D(renderable) => Some(Rc::new(
326 renderable.render(&context.current_resolution()).into(),
327 )),
328 BuiltinWorkpieceOutput::Transform(transform) => {
329 let model = context.model();
330 let model_ = model.borrow();
331 let output: Geometry3DOutput = model_.children.render_with_context(context)?;
332 output.map(|geometry| Rc::new(geometry.transformed_3d(&transform.mat3d())))
333 }
334 BuiltinWorkpieceOutput::Operation(operation) => operation.process_3d(context)?,
335 _ => None,
336 })
337 }
338}