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<usize> {
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(self
157 .descendants()
158 .filter(|model| !model.has_no_output())
159 .count())
160 }
161}
162
163impl CalcBounds2D for Model {
164 fn calc_bounds_2d(&self) -> Bounds2D {
165 let self_ = self.borrow();
166 match &self_.output().geometry {
167 Some(GeometryOutput::Geometry2D(geometry)) => geometry.bounds.clone(),
168 Some(GeometryOutput::Geometry3D(_)) => Bounds2D::default(),
169 None => Bounds2D::default(),
170 }
171 }
172}
173
174impl RenderWithContext<Geometry2DOutput> for Model {
180 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
181 context.with_model(self.clone(), |context| {
182 let model = context.model();
183 let geometry: Geometry2DOutput = {
184 let model_ = model.borrow();
185 let output = model.render_output_type();
186 match output {
187 OutputType::Geometry2D => {
188 match model_.element() {
189 Element::BuiltinWorkpiece(builtin_workpiece) => {
191 Ok(builtin_workpiece.render_with_context(context)?)
192 }
193 _ => Ok(model_.children.render_with_context(context)?),
194 }
195 }
196 output_type => Err(RenderError::InvalidOutputType(output_type)),
197 }
198 }?;
199
200 self.borrow_mut()
201 .output_mut()
202 .set_geometry(GeometryOutput::Geometry2D(geometry.clone()));
203 Ok(geometry)
204 })
205 }
206}
207
208impl RenderWithContext<Geometry3DOutput> for Model {
214 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
215 context.with_model(self.clone(), |context| {
216 let model = context.model();
217 let geometry: Geometry3DOutput = {
218 let model_ = model.borrow();
219 let output = model.render_output_type();
220 match output {
221 OutputType::Geometry3D => {
222 match model_.element() {
223 Element::BuiltinWorkpiece(builtin_workpiece) => {
225 builtin_workpiece.render_with_context(context)
226 }
227 _ => model_.children.render_with_context(context),
228 }
229 }
230 output_type => Err(RenderError::InvalidOutputType(output_type)),
231 }
232 }?;
233
234 self.borrow_mut()
235 .output_mut()
236 .set_geometry(GeometryOutput::Geometry3D(geometry.clone()));
237 Ok(geometry)
238 })
239 }
240}
241
242impl RenderWithContext<Model> for Model {
243 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Model> {
244 match self.render_output_type() {
245 OutputType::Geometry2D => {
246 let _: Geometry2DOutput = self.render_with_context(context)?;
247 }
248 OutputType::Geometry3D => {
249 let _: Geometry3DOutput = self.render_with_context(context)?;
250 }
251 _ => {
252 return Err(RenderError::NothingToRender);
253 }
254 }
255 log::trace!("Finished render:\n{}", FormatTree(self));
256
257 Ok(self.clone())
258 }
259}
260
261impl RenderWithContext<Geometries2D> for Models {
262 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometries2D> {
263 let mut geometries = Vec::new();
264 for model in self.iter() {
265 let geo: Geometry2DOutput = model.render_with_context(context)?;
266 geometries.push(Rc::new(geo.inner.clone()));
267 }
268 Ok(geometries.into_iter().collect())
269 }
270}
271
272impl RenderWithContext<Geometry2DOutput> for Models {
273 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
274 match self.len() {
275 0 => Err(RenderError::NothingToRender),
276 1 => self.first().expect("One item").render_with_context(context),
277 _ => Ok(Rc::new(
278 Geometry2D::Collection(self.render_with_context(context)?).into(),
279 )),
280 }
281 }
282}
283
284impl RenderWithContext<Geometries3D> for Models {
285 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometries3D> {
286 let mut geometries = Vec::new();
287 for model in self.iter() {
288 let geo: Geometry3DOutput = model.render_with_context(context)?;
289 geometries.push(Rc::new(geo.inner.clone()));
290 }
291 Ok(geometries.into_iter().collect())
292 }
293}
294
295impl RenderWithContext<Geometry3DOutput> for Models {
296 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
297 match self.len() {
298 0 => Err(RenderError::NothingToRender),
299 1 => self.first().expect("One item").render_with_context(context),
300 _ => Ok(Rc::new(
301 Geometry3D::Collection(self.render_with_context(context)?).into(),
302 )),
303 }
304 }
305}
306
307impl RenderWithContext<Geometry2DOutput> for BuiltinWorkpiece {
308 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry2DOutput> {
309 match self.call()? {
310 BuiltinWorkpieceOutput::Primitive2D(primitive) => {
311 primitive.render_with_context(context)
312 }
313 BuiltinWorkpieceOutput::Transform(transform) => {
314 let model = context.model();
315 let model_ = model.borrow();
316 let output: Geometry2DOutput = model_.children.render_with_context(context)?;
317 Ok(Rc::new(output.transformed_2d(&transform.mat2d())))
318 }
319 BuiltinWorkpieceOutput::Operation(operation) => operation.process_2d(context),
320 _ => unreachable!(),
321 }
322 }
323}
324
325impl RenderWithContext<Geometry3DOutput> for BuiltinWorkpiece {
326 fn render_with_context(&self, context: &mut RenderContext) -> RenderResult<Geometry3DOutput> {
327 match self.call()? {
328 BuiltinWorkpieceOutput::Primitive3D(primitive) => {
329 primitive.render_with_context(context)
330 }
331 BuiltinWorkpieceOutput::Transform(transform) => {
332 let model = context.model();
333 let model_ = model.borrow();
334 let output: Geometry3DOutput = model_.children.render_with_context(context)?;
335 Ok(Rc::new(output.transformed_3d(&transform.mat3d())))
336 }
337 BuiltinWorkpieceOutput::Operation(operation) => operation.process_3d(context),
338 _ => unreachable!(),
339 }
340 }
341}