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