microcad_lang/render/
mod.rs1mod attribute;
7mod cache;
8mod context;
9mod output;
10
11use std::rc::Rc;
12
13pub use attribute::*;
14pub use cache::*;
15pub use context::*;
16use microcad_lang_base::FormatTree;
17pub use output::*;
18
19use cgmath::SquareMatrix;
20use microcad_core::*;
21use miette::Diagnostic;
22use thiserror::Error;
23
24use crate::{
25 builtin::{BuiltinWorkbenchKind, BuiltinWorkpiece, BuiltinWorkpieceOutput},
26 model::*,
27};
28
29#[derive(Debug, Error, Diagnostic)]
31pub enum RenderError {
32 #[error("Invalid output type: {0}")]
34 InvalidOutputType(OutputType),
35
36 #[error("Nothing to render")]
38 NothingToRender,
39}
40
41pub type RenderResult<T> = Result<T, RenderError>;
43
44pub trait RenderWithContext<T> {
46 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<T>;
48}
49
50impl Element {
51 pub fn get_affine_transform(&self) -> RenderResult<Option<AffineTransform>> {
53 match &self {
54 Element::BuiltinWorkpiece(builtin_workpiece) => match builtin_workpiece.kind {
55 BuiltinWorkbenchKind::Transform => match builtin_workpiece.call()? {
56 BuiltinWorkpieceOutput::Transform(affine_transform) => {
57 Ok(Some(affine_transform))
58 }
59 _ => unreachable!(),
60 },
61 _ => Ok(None),
62 },
63 _ => Ok(None),
64 }
65 }
66}
67
68impl ModelInner {
69 pub fn resolution(&self) -> RenderResolution {
71 let output = self.output.as_ref().expect("Some render output.");
72 output
73 .resolution
74 .as_ref()
75 .expect("Some resolution.")
76 .clone()
77 }
78}
79
80impl Model {
81 pub fn prerender(&self, resolution: RenderResolution) -> RenderResult<usize> {
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 resolution = match model.borrow().attributes().get_resolution() {
118 Some(resolution_attribute) => RenderResolution {
119 linear: match resolution_attribute {
120 ResolutionAttribute::Absolute(linear) => linear,
121 ResolutionAttribute::Relative(factor) =>
122 {
124 resolution.linear / factor
125 }
126 },
127 },
128 None => resolution,
129 };
130
131 let new_resolution = {
132 let mut model_ = model.borrow_mut();
133 let output = model_.output.as_mut().expect("Output");
134 let resolution = resolution * output.local_matrix().unwrap_or(Mat4::identity());
135 output.set_resolution(resolution.clone());
136 resolution
137 };
138
139 model.borrow().children.iter().for_each(|model| {
140 set_resolution(model, new_resolution.clone());
141 });
142 }
143
144 create_render_output(self)?;
146
147 set_world_matrix(self, Mat4::identity())?;
149
150 set_resolution(self, resolution);
152
153 log::trace!("Finished prerender:\n{}", FormatTree(self));
154
155 Ok(self
156 .descendants()
157 .filter(|model| !model.has_no_output())
158 .count())
159 }
160}
161
162impl CalcBounds2D for Model {
163 fn calc_bounds_2d(&self) -> Bounds2D {
164 let self_ = self.borrow();
165 match &self_.output().geometry {
166 Some(GeometryOutput::Geometry2D(geometry)) => geometry.bounds.clone(),
167 Some(GeometryOutput::Geometry3D(_)) => Bounds2D::default(),
168 None => Bounds2D::default(),
169 }
170 }
171}
172
173impl RenderWithContext<Geometry2DOutput> for Model {
179 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
180 context.with_model(self.clone(), |context| {
181 let model = context.model();
182 let geometry: Geometry2DOutput = {
183 let model_ = model.borrow();
184 let output = model.render_output_type();
185 match output {
186 OutputType::Geometry2D => {
187 match model_.element() {
188 Element::BuiltinWorkpiece(builtin_workpiece) => {
190 Ok(builtin_workpiece.render_with_context(context)?)
191 }
192 _ => Ok(model_.children.render_with_context(context)?),
193 }
194 }
195 output_type => Err(RenderError::InvalidOutputType(output_type)),
196 }
197 }?;
198
199 self.borrow_mut()
200 .output_mut()
201 .set_geometry(GeometryOutput::Geometry2D(geometry.clone()));
202 Ok(geometry)
203 })
204 }
205}
206
207impl RenderWithContext<Geometry3DOutput> for Model {
213 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
214 context.with_model(self.clone(), |context| {
215 let model = context.model();
216 let geometry: Geometry3DOutput = {
217 let model_ = model.borrow();
218 let output = model.render_output_type();
219 match output {
220 OutputType::Geometry3D => {
221 match model_.element() {
222 Element::BuiltinWorkpiece(builtin_workpiece) => {
224 builtin_workpiece.render_with_context(context)
225 }
226 _ => model_.children.render_with_context(context),
227 }
228 }
229 output_type => Err(RenderError::InvalidOutputType(output_type)),
230 }
231 }?;
232
233 self.borrow_mut()
234 .output_mut()
235 .set_geometry(GeometryOutput::Geometry3D(geometry.clone()));
236 Ok(geometry)
237 })
238 }
239}
240
241impl RenderWithContext<Model> for Model {
242 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Model> {
243 match self.render_output_type() {
244 OutputType::Geometry2D => {
245 let _: Geometry2DOutput = self.render_with_context(context)?;
246 }
247 OutputType::Geometry3D => {
248 let _: Geometry3DOutput = self.render_with_context(context)?;
249 }
250 _ => {
251 return Err(RenderError::NothingToRender);
252 }
253 }
254 log::trace!("Finished render:\n{}", FormatTree(self));
255
256 Ok(self.clone())
257 }
258}
259
260impl RenderWithContext<Geometries2D> for Models {
261 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometries2D> {
262 let mut geometries = Vec::new();
263 for model in self.iter() {
264 let geo: Geometry2DOutput = model.render_with_context(context)?;
265 geometries.push(Rc::new(geo.inner.clone()));
266 }
267 Ok(geometries.into_iter().collect())
268 }
269}
270
271impl RenderWithContext<Geometry2DOutput> for Models {
272 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
273 match self.len() {
274 0 => Err(RenderError::NothingToRender),
275 1 => self.first().expect("One item").render_with_context(context),
276 _ => Ok(Rc::new(
277 Geometry2D::Collection(self.render_with_context(context)?).into(),
278 )),
279 }
280 }
281}
282
283impl RenderWithContext<Geometries3D> for Models {
284 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometries3D> {
285 let mut geometries = Vec::new();
286 for model in self.iter() {
287 let geo: Geometry3DOutput = model.render_with_context(context)?;
288 geometries.push(Rc::new(geo.inner.clone()));
289 }
290 Ok(geometries.into_iter().collect())
291 }
292}
293
294impl RenderWithContext<Geometry3DOutput> for Models {
295 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
296 match self.len() {
297 0 => Err(RenderError::NothingToRender),
298 1 => self.first().expect("One item").render_with_context(context),
299 _ => Ok(Rc::new(
300 Geometry3D::Collection(self.render_with_context(context)?).into(),
301 )),
302 }
303 }
304}
305
306impl RenderWithContext<Geometry2DOutput> for BuiltinWorkpiece {
307 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
308 match self.call()? {
309 BuiltinWorkpieceOutput::Primitive2D(primitive) => {
310 primitive.render_with_context(context)
311 }
312 BuiltinWorkpieceOutput::Transform(transform) => {
313 let model = context.model();
314 let model_ = model.borrow();
315 let output: Geometry2DOutput = model_.children.render_with_context(context)?;
316 Ok(Rc::new(output.transformed_2d(&transform.mat2d())))
317 }
318 BuiltinWorkpieceOutput::Operation(operation) => operation.process_2d(context),
319 _ => unreachable!(),
320 }
321 }
322}
323
324impl RenderWithContext<Geometry3DOutput> for BuiltinWorkpiece {
325 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
326 match self.call()? {
327 BuiltinWorkpieceOutput::Primitive3D(primitive) => {
328 primitive.render_with_context(context)
329 }
330 BuiltinWorkpieceOutput::Transform(transform) => {
331 let model = context.model();
332 let model_ = model.borrow();
333 let output: Geometry3DOutput = model_.children.render_with_context(context)?;
334 Ok(Rc::new(output.transformed_3d(&transform.mat3d())))
335 }
336 BuiltinWorkpieceOutput::Operation(operation) => operation.process_3d(context),
337 _ => unreachable!(),
338 }
339 }
340}