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 microcad_core::*;
22use thiserror::Error;
23
24use crate::{
25 builtin::{BuiltinWorkbenchKind, BuiltinWorkpiece, BuiltinWorkpieceOutput},
26 model::*,
27 tree_display::FormatTree,
28};
29
30#[derive(Debug, Error)]
32pub enum RenderError {
33 #[error("Invalid output type: {0}")]
35 InvalidOutputType(OutputType),
36
37 #[error("Nothing to render")]
39 NothingToRender,
40}
41
42pub type RenderResult<T> = Result<T, RenderError>;
44
45pub trait RenderWithContext<T> {
47 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<T>;
49}
50
51impl Element {
52 pub fn get_affine_transform(&self) -> RenderResult<Option<AffineTransform>> {
54 match &self {
55 Element::BuiltinWorkpiece(builtin_workpiece) => match builtin_workpiece.kind {
56 BuiltinWorkbenchKind::Transform => match builtin_workpiece.call()? {
57 BuiltinWorkpieceOutput::Transform(affine_transform) => {
58 Ok(Some(affine_transform))
59 }
60 _ => unreachable!(),
61 },
62 _ => Ok(None),
63 },
64 _ => Ok(None),
65 }
66 }
67}
68
69impl ModelInner {
70 pub fn resolution(&self) -> RenderResolution {
72 let output = self.output.as_ref().expect("Some render output.");
73 output
74 .resolution
75 .as_ref()
76 .expect("Some resolution.")
77 .clone()
78 }
79}
80
81impl Model {
82 pub fn prerender(&self, resolution: RenderResolution) -> RenderResult<()> {
86 pub fn create_render_output(model: &Model) -> RenderResult<()> {
87 let output = RenderOutput::new(model)?;
88 {
89 let mut model_ = model.borrow_mut();
90 model_.output = Some(output);
91 };
92
93 model
94 .borrow()
95 .children
96 .iter()
97 .try_for_each(create_render_output)
98 }
99
100 pub fn set_world_matrix(model: &Model, matrix: Mat4) -> RenderResult<()> {
101 let world_matrix = {
102 let mut model_ = model.borrow_mut();
103 let output = model_.output.as_mut().expect("Output");
104 let world_matrix = matrix * output.local_matrix().unwrap_or(Mat4::identity());
105 output.set_world_matrix(world_matrix);
106 world_matrix
107 };
108
109 model
110 .borrow()
111 .children
112 .iter()
113 .try_for_each(|model| set_world_matrix(model, world_matrix))
114 }
115
116 pub fn set_resolution(model: &Model, resolution: RenderResolution) {
118 let resolution = match model.borrow().attributes().get_resolution() {
119 Some(resolution_attribute) => RenderResolution {
120 linear: match resolution_attribute {
121 ResolutionAttribute::Absolute(linear) => linear,
122 ResolutionAttribute::Relative(factor) =>
123 {
125 resolution.linear / factor
126 }
127 },
128 },
129 None => resolution,
130 };
131
132 let new_resolution = {
133 let mut model_ = model.borrow_mut();
134 let output = model_.output.as_mut().expect("Output");
135 let resolution = resolution * output.local_matrix().unwrap_or(Mat4::identity());
136 output.set_resolution(resolution.clone());
137 resolution
138 };
139
140 model.borrow().children.iter().for_each(|model| {
141 set_resolution(model, new_resolution.clone());
142 });
143 }
144
145 create_render_output(self)?;
147
148 set_world_matrix(self, Mat4::identity())?;
150
151 set_resolution(self, resolution);
153
154 log::trace!("Finished prerender:\n{}", FormatTree(self));
155
156 Ok(())
157 }
158}
159
160impl CalcBounds2D for Model {
161 fn calc_bounds_2d(&self) -> Bounds2D {
162 let self_ = self.borrow();
163 match &self_.output().geometry {
164 Some(GeometryOutput::Geometry2D(geometry)) => geometry.bounds.clone(),
165 Some(GeometryOutput::Geometry3D(_)) => Bounds2D::default(),
166 None => Bounds2D::default(),
167 }
168 }
169}
170
171impl RenderWithContext<Geometry2DOutput> for Model {
177 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
178 context.with_model(self.clone(), |context| {
179 let model = context.model();
180 let geometry: Geometry2DOutput = {
181 let model_ = model.borrow();
182 let output = model.render_output_type();
183 match output {
184 OutputType::Geometry2D => {
185 match model_.element() {
186 Element::BuiltinWorkpiece(builtin_workpiece) => {
188 Ok(builtin_workpiece.render_with_context(context)?)
189 }
190 _ => Ok(model_.children.render_with_context(context)?),
191 }
192 }
193 output_type => Err(RenderError::InvalidOutputType(output_type)),
194 }
195 }?;
196
197 self.borrow_mut()
198 .output_mut()
199 .set_geometry(GeometryOutput::Geometry2D(geometry.clone()));
200 Ok(geometry)
201 })
202 }
203}
204
205impl RenderWithContext<Geometry3DOutput> for Model {
211 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
212 context.with_model(self.clone(), |context| {
213 let model = context.model();
214 let geometry: Geometry3DOutput = {
215 let model_ = model.borrow();
216 let output = model.render_output_type();
217 match output {
218 OutputType::Geometry3D => {
219 match model_.element() {
220 Element::BuiltinWorkpiece(builtin_workpiece) => {
222 builtin_workpiece.render_with_context(context)
223 }
224 _ => model_.children.render_with_context(context),
225 }
226 }
227 output_type => Err(RenderError::InvalidOutputType(output_type)),
228 }
229 }?;
230
231 self.borrow_mut()
232 .output_mut()
233 .set_geometry(GeometryOutput::Geometry3D(geometry.clone()));
234 Ok(geometry)
235 })
236 }
237}
238
239impl RenderWithContext<Model> for Model {
240 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Model> {
241 match self.render_output_type() {
242 OutputType::Geometry2D => {
243 let _: Geometry2DOutput = self.render_with_context(context)?;
244 }
245 OutputType::Geometry3D => {
246 let _: Geometry3DOutput = self.render_with_context(context)?;
247 }
248 _ => {
249 return Err(RenderError::NothingToRender);
250 }
251 }
252 log::trace!("Finished render:\n{}", FormatTree(self));
253
254 Ok(self.clone())
255 }
256}
257
258impl RenderWithContext<Geometries2D> for Models {
259 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometries2D> {
260 let mut geometries = Vec::new();
261 for model in self.iter() {
262 let geo: Geometry2DOutput = model.render_with_context(context)?;
263 geometries.push(Rc::new(geo.inner.clone()));
264 }
265 Ok(geometries.into_iter().collect())
266 }
267}
268
269impl RenderWithContext<Geometry2DOutput> for Models {
270 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
271 match self.len() {
272 0 => Err(RenderError::NothingToRender),
273 1 => self.first().expect("One item").render_with_context(context),
274 _ => Ok(Rc::new(
275 Geometry2D::Collection(self.render_with_context(context)?).into(),
276 )),
277 }
278 }
279}
280
281impl RenderWithContext<Geometries3D> for Models {
282 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometries3D> {
283 let mut geometries = Vec::new();
284 for model in self.iter() {
285 let geo: Geometry3DOutput = model.render_with_context(context)?;
286 geometries.push(Rc::new(geo.inner.clone()));
287 }
288 Ok(geometries.into_iter().collect())
289 }
290}
291
292impl RenderWithContext<Geometry3DOutput> for Models {
293 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
294 match self.len() {
295 0 => Err(RenderError::NothingToRender),
296 1 => self.first().expect("One item").render_with_context(context),
297 _ => Ok(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 match self.call()? {
307 BuiltinWorkpieceOutput::Primitive2D(primitive) => {
308 primitive.render_with_context(context)
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 Ok(Rc::new(output.transformed_2d(&transform.mat2d())))
315 }
316 BuiltinWorkpieceOutput::Operation(operation) => operation.process_2d(context),
317 _ => unreachable!(),
318 }
319 }
320}
321
322impl RenderWithContext<Geometry3DOutput> for BuiltinWorkpiece {
323 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
324 match self.call()? {
325 BuiltinWorkpieceOutput::Primitive3D(primitive) => {
326 primitive.render_with_context(context)
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 Ok(Rc::new(output.transformed_3d(&transform.mat3d())))
333 }
334 BuiltinWorkpieceOutput::Operation(operation) => operation.process_3d(context),
335 _ => unreachable!(),
336 }
337 }
338}