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