microcad_lang/render/
context.rs1use microcad_core::RenderResolution;
7
8use crate::{model::Model, rc::RcMut, render::*};
9
10#[derive(Default)]
14pub struct RenderContext {
15 pub model_stack: Vec<Model>,
17
18 pub cache: Option<RcMut<RenderCache>>,
20}
21
22impl RenderContext {
23 pub fn new() -> Self {
25 Self::default()
26 }
27
28 pub fn init(
30 model: &Model,
31 resolution: RenderResolution,
32 cache: Option<RcMut<RenderCache>>,
33 ) -> RenderResult<Self> {
34 model.prerender(resolution)?;
35 Ok(Self {
36 model_stack: vec![model.clone()],
37 cache,
38 })
39 }
40
41 pub fn model(&self) -> Model {
43 self.model_stack.last().expect("A model").clone()
44 }
45
46 pub fn with_model<T>(&mut self, model: Model, f: impl FnOnce(&mut RenderContext) -> T) -> T {
48 self.model_stack.push(model);
49 let result = f(self);
50 self.model_stack.pop();
51 result
52 }
53
54 pub fn update_2d<T: Into<WithBounds2D<Geometry2D>>>(
56 &mut self,
57 f: impl FnOnce(&mut RenderContext, Model) -> RenderResult<T>,
58 ) -> RenderResult<Geometry2DOutput> {
59 let model = self.model();
60 let hash = model.computed_hash();
61
62 match self.cache.clone() {
63 Some(cache) => {
64 {
65 let mut cache = cache.borrow_mut();
66 if let Some(GeometryOutput::Geometry2D(geo)) = cache.get(&hash) {
67 return Ok(geo.clone());
68 }
69 }
70 {
71 let (geo, cost) = self.call_with_cost(model, f)?;
72 let geo: Geometry2DOutput = Rc::new(geo.into());
73 let mut cache = cache.borrow_mut();
74 cache.insert_with_cost(hash, geo.clone(), cost);
75 Ok(geo)
76 }
77 }
78 None => Ok(Rc::new(f(self, model)?.into())),
79 }
80 }
81
82 pub fn update_3d<T: Into<WithBounds3D<Geometry3D>>>(
84 &mut self,
85 f: impl FnOnce(&mut RenderContext, Model) -> RenderResult<T>,
86 ) -> RenderResult<Geometry3DOutput> {
87 let model = self.model();
88 let hash = model.computed_hash();
89 match self.cache.clone() {
90 Some(cache) => {
91 {
92 let mut cache = cache.borrow_mut();
93 if let Some(GeometryOutput::Geometry3D(geo)) = cache.get(&hash) {
94 return Ok(geo.clone());
95 }
96 }
97 {
98 let (geo, cost) = self.call_with_cost(model, f)?;
99 let geo: Geometry3DOutput = Rc::new(geo.into());
100 let mut cache = cache.borrow_mut();
101 cache.insert_with_cost(hash, geo.clone(), cost);
102 Ok(geo)
103 }
104 }
105 None => Ok(Rc::new(f(self, model)?.into())),
106 }
107 }
108
109 pub fn current_resolution(&self) -> RenderResolution {
111 self.model().borrow().resolution()
112 }
113
114 fn call_with_cost<T>(
116 &mut self,
117 model: Model,
118 f: impl FnOnce(&mut RenderContext, Model) -> RenderResult<T>,
119 ) -> RenderResult<(T, f64)> {
120 use std::time::Instant;
121 let start = Instant::now();
122
123 let r = f(self, model)?;
124
125 let duration = start.elapsed();
126 Ok((r, (duration.as_nanos() as f64) / 1_000_000.0))
127 }
128}