microcad_lang/render/
output.rs1use std::{
7 hash::{Hash, Hasher},
8 rc::Rc,
9};
10
11use microcad_core::*;
12
13use crate::{model::*, render::*};
14
15pub type Geometry2DOutput = Rc<WithBounds2D<Geometry2D>>;
17
18pub type Geometry3DOutput = Rc<WithBounds3D<Geometry3D>>;
20
21#[derive(Debug, Clone, derive_more::From)]
23pub enum GeometryOutput {
24 Geometry2D(Geometry2DOutput),
26 Geometry3D(Geometry3DOutput),
28}
29
30impl GeometryOutput {
31 pub fn ground_radius(&self) -> Length {
33 let mut bounds = match &self {
34 GeometryOutput::Geometry2D(geo2d) => geo2d.bounds.clone(),
35 GeometryOutput::Geometry3D(geo3d) => {
36 Bounds2D::new(geo3d.bounds.min.truncate(), geo3d.bounds.max.truncate())
37 }
38 };
39 bounds.extend_by_point(Vec2::new(0.0, 0.0));
40 Length::mm(bounds.radius())
41 }
42
43 pub fn scene_radius(&self) -> Length {
45 let mut bounds = match &self {
46 GeometryOutput::Geometry2D(geo2d) => {
47 Bounds3D::new(geo2d.bounds.min.extend(0.0), geo2d.bounds.max.extend(0.0))
48 }
49 GeometryOutput::Geometry3D(geo3d) => geo3d.bounds.clone(),
50 };
51 bounds.extend_by_point(Vec3::new(0.0, 0.0, 0.0));
52 Length::mm(bounds.radius())
53 }
54}
55
56impl From<Geometry2D> for GeometryOutput {
57 fn from(geo: Geometry2D) -> Self {
58 Self::Geometry2D(Rc::new(geo.into()))
59 }
60}
61
62impl From<Geometry3D> for GeometryOutput {
63 fn from(geo: Geometry3D) -> Self {
64 Self::Geometry3D(Rc::new(geo.into()))
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct RenderOutput {
71 pub output_type: OutputType,
73 pub local_matrix: Option<Mat4>,
75 pub world_matrix: Option<Mat4>,
77 pub resolution: Option<RenderResolution>,
79 pub geometry: Option<GeometryOutput>,
81 pub attributes: RenderAttributes,
83 hash: HashId,
85}
86
87impl RenderOutput {
88 pub fn new(model: &Model) -> RenderResult<Self> {
90 let output_type = model.deduce_output_type();
91 let mut hasher = rustc_hash::FxHasher::default();
92 model.hash(&mut hasher);
93 let hash = hasher.finish();
94 let local_matrix = model
95 .borrow()
96 .element
97 .get_affine_transform()?
98 .map(|affine_transform| affine_transform.mat3d());
99
100 Ok(RenderOutput {
101 output_type,
102 local_matrix,
103 world_matrix: None,
104 resolution: None,
105 geometry: None,
106 attributes: model.into(),
107 hash,
108 })
109 }
110
111 pub fn set_world_matrix(&mut self, m: Mat4) {
113 self.world_matrix = Some(m);
114 }
115
116 pub fn set_geometry(&mut self, geo: GeometryOutput) {
118 self.geometry = Some(geo)
119 }
120
121 pub fn resolution(&self) -> &Option<RenderResolution> {
123 &self.resolution
124 }
125
126 pub fn set_resolution(&mut self, render_resolution: RenderResolution) {
128 self.resolution = Some(render_resolution);
129 }
130
131 pub fn local_matrix(&self) -> Option<Mat4> {
133 self.local_matrix
134 }
135
136 pub fn ground_radius(&self) -> Length {
138 self.geometry
139 .as_ref()
140 .map(|geo| geo.ground_radius())
141 .unwrap_or_default()
142 }
143
144 pub fn scene_radius(&self) -> Length {
146 self.geometry
147 .as_ref()
148 .map(|geo| geo.scene_radius())
149 .unwrap_or_default()
150 }
151}
152
153impl std::fmt::Display for RenderOutput {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 write!(
156 f,
157 "{output_type} ({hash:X}): {geo} {resolution}",
158 output_type = match self.output_type {
159 OutputType::Geometry2D => "2D",
160 OutputType::Geometry3D => "3D",
161 OutputType::InvalidMixed => "Mixed",
162 OutputType::NotDetermined => "?",
163 },
164 hash = self.computed_hash(),
165 geo = match &self.geometry {
166 Some(GeometryOutput::Geometry2D(geo)) => geo.name(),
167 Some(GeometryOutput::Geometry3D(geo)) => geo.name(),
168 None => "",
169 },
170 resolution = match &self.resolution {
171 Some(resolution) => resolution.to_string(),
172 None => "".to_string(),
173 },
174 )?;
175 Ok(())
176 }
177}
178
179impl ComputedHash for RenderOutput {
180 fn computed_hash(&self) -> HashId {
181 self.hash
182 }
183}
184
185impl CalcBounds2D for RenderOutput {
186 fn calc_bounds_2d(&self) -> Bounds2D {
187 match &self.geometry {
188 Some(GeometryOutput::Geometry2D(output)) => output.bounds.clone(),
189 _ => Bounds2D::default(),
190 }
191 }
192}
193
194impl CalcBounds3D for RenderOutput {
195 fn calc_bounds_3d(&self) -> Bounds3D {
196 match &self.geometry {
197 Some(GeometryOutput::Geometry3D(output)) => output.bounds.clone(),
198 _ => Bounds3D::default(),
199 }
200 }
201}