1use std::rc::Rc;
7
8use microcad_core::{FetchBounds2D, Geometry2D, Geometry3D, Mat3, Mat4, RenderResolution};
9
10use crate::{model::*, render::*};
11
12pub type Geometry2DOutput = Option<Rc<Geometry2D>>;
14
15pub type Geometry3DOutput = Option<Rc<Geometry3D>>;
17
18#[derive(Debug, Clone)]
20pub enum RenderOutput {
21 Geometry2D {
23 local_matrix: Option<Mat3>,
25 world_matrix: Option<Mat3>,
27 resolution: Option<RenderResolution>,
29 geometry: Geometry2DOutput,
31 },
32
33 Geometry3D {
35 local_matrix: Option<Mat4>,
37 world_matrix: Option<Mat4>,
39 resolution: Option<RenderResolution>,
41 geometry: Geometry3DOutput,
43 },
44}
45
46impl RenderOutput {
47 pub fn new(model: &Model) -> RenderResult<Self> {
49 let output_type = model.deduce_output_type();
50
51 match output_type {
52 OutputType::Geometry2D => {
53 let local_matrix = model
54 .borrow()
55 .element
56 .get_affine_transform()?
57 .map(|affine_transform| affine_transform.mat2d());
58
59 Ok(RenderOutput::Geometry2D {
60 local_matrix,
61 world_matrix: None,
62 resolution: None,
63 geometry: None,
64 })
65 }
66
67 OutputType::Geometry3D => {
68 let local_matrix = model
69 .borrow()
70 .element
71 .get_affine_transform()?
72 .map(|affine_transform| affine_transform.mat3d());
73
74 Ok(RenderOutput::Geometry3D {
75 local_matrix,
76 world_matrix: None,
77 resolution: None,
78 geometry: None,
79 })
80 }
81 output_type => Err(RenderError::InvalidOutputType(output_type)),
82 }
83 }
84
85 pub fn set_world_matrix(&mut self, m: Mat4) {
87 match self {
88 RenderOutput::Geometry2D { world_matrix, .. } => *world_matrix = Some(mat4_to_mat3(&m)),
89 RenderOutput::Geometry3D { world_matrix, .. } => {
90 *world_matrix = Some(m);
91 }
92 }
93 }
94
95 pub fn set_geometry_2d(&mut self, geo: Geometry2DOutput) {
97 match self {
98 RenderOutput::Geometry2D { geometry, .. } => *geometry = geo,
99 RenderOutput::Geometry3D { .. } => unreachable!(),
100 }
101 }
102
103 pub fn set_geometry_3d(&mut self, geo: Geometry3DOutput) {
105 match self {
106 RenderOutput::Geometry2D { .. } => unreachable!(),
107 RenderOutput::Geometry3D { geometry, .. } => *geometry = geo,
108 }
109 }
110
111 pub fn resolution(&self) -> &Option<RenderResolution> {
113 match self {
114 RenderOutput::Geometry2D { resolution, .. }
115 | RenderOutput::Geometry3D { resolution, .. } => resolution,
116 }
117 }
118
119 pub fn set_resolution(&mut self, render_resolution: RenderResolution) {
121 match self {
122 RenderOutput::Geometry2D { resolution, .. }
123 | RenderOutput::Geometry3D { resolution, .. } => *resolution = Some(render_resolution),
124 }
125 }
126
127 pub fn local_matrix(&self) -> Option<Mat4> {
129 match self {
130 RenderOutput::Geometry2D { local_matrix, .. } => {
131 local_matrix.as_ref().map(mat3_to_mat4)
132 }
133 RenderOutput::Geometry3D { local_matrix, .. } => *local_matrix,
134 }
135 }
136
137 pub fn world_matrix(&self) -> Mat4 {
139 match self {
140 RenderOutput::Geometry2D { world_matrix, .. } => {
141 mat3_to_mat4(&world_matrix.expect("World matrix"))
142 }
143 RenderOutput::Geometry3D { world_matrix, .. } => world_matrix.expect("World matrix"),
144 }
145 }
146}
147
148fn mat4_to_mat3(m: &Mat4) -> Mat3 {
149 Mat3::from_cols(m.x.truncate_n(2), m.y.truncate_n(2), m.w.truncate_n(2))
150}
151
152fn mat3_to_mat4(m: &Mat3) -> Mat4 {
153 Mat4::new(
154 m.x.x, m.x.y, 0.0, m.x.z, m.y.x, m.y.y, 0.0, m.y.z, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, )
159}
160
161impl std::fmt::Display for RenderOutput {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 match &self {
164 RenderOutput::Geometry2D {
165 local_matrix,
166 geometry,
167 ..
168 } => {
169 write!(f, "2D: ")?;
170 if local_matrix.is_none() && geometry.is_none() {
171 write!(f, "(nothing to render)")?;
172 }
173 if local_matrix.is_some() {
174 write!(f, "transform ")?;
175 }
176 if let Some(geometry) = geometry {
177 write!(
178 f,
179 "{} {}",
180 match geometry.as_ref() {
181 Geometry2D::Collection(geometries) =>
182 format!("Collection({} items)", geometries.len()),
183 geometry => geometry.name().to_string(),
184 },
185 geometry.fetch_bounds_2d()
186 )?;
187 }
188 }
189 RenderOutput::Geometry3D {
190 local_matrix,
191 geometry,
192 ..
193 } => {
194 write!(f, "3D: ")?;
195 match (geometry, local_matrix) {
196 (None, None) => write!(f, "(nothing to render)"),
197 (None, Some(_)) => {
198 write!(f, "transform")
199 }
200 (Some(geometry), None) => write!(f, "{}", geometry.name()),
201 (Some(geometry), Some(_)) => write!(f, "transformed {}", geometry.name()),
202 }?;
203 }
204 }
205
206 if let Some(resolution) = self.resolution() {
207 write!(f, " {resolution}")?
208 }
209 Ok(())
210 }
211}