polyscope_structures/camera_view/
mod.rs1mod camera_parameters;
4
5pub use camera_parameters::*;
6
7use glam::{Mat4, Vec3, Vec4};
8use polyscope_core::pick::PickResult;
9use polyscope_core::quantity::Quantity;
10use polyscope_core::structure::{HasQuantities, RenderContext, Structure};
11use polyscope_render::CurveNetworkRenderData;
12
13pub struct CameraView {
15 name: String,
16
17 params: CameraParameters,
19
20 enabled: bool,
22 transform: Mat4,
23 quantities: Vec<Box<dyn Quantity>>,
24
25 color: Vec4,
27 widget_focal_length: f32,
28 widget_focal_length_is_relative: bool,
29 widget_thickness: f32,
30
31 render_data: Option<CurveNetworkRenderData>,
33 prepared_length_scale: f32,
35
36 fly_to_requested: bool,
38}
39
40impl CameraView {
41 pub fn new(name: impl Into<String>, params: CameraParameters) -> Self {
43 Self {
44 name: name.into(),
45 params,
46 enabled: true,
47 transform: Mat4::IDENTITY,
48 quantities: Vec::new(),
49 color: Vec4::new(0.0, 0.0, 0.0, 1.0),
50 widget_focal_length: 0.20,
51 widget_focal_length_is_relative: true,
52 widget_thickness: 0.02,
53 render_data: None,
54 prepared_length_scale: 0.0,
55 fly_to_requested: false,
56 }
57 }
58
59 pub fn from_look_at(
61 name: impl Into<String>,
62 position: Vec3,
63 look_at: Vec3,
64 up: Vec3,
65 fov_vertical_degrees: f32,
66 aspect_ratio: f32,
67 ) -> Self {
68 let look_dir = (look_at - position).normalize();
69 let params = CameraParameters::from_vectors(
70 position,
71 look_dir,
72 up,
73 fov_vertical_degrees,
74 aspect_ratio,
75 );
76 Self::new(name, params)
77 }
78
79 #[must_use]
81 pub fn params(&self) -> &CameraParameters {
82 &self.params
83 }
84
85 pub fn set_params(&mut self, params: CameraParameters) -> &mut Self {
87 self.params = params;
88 self.render_data = None; self
90 }
91
92 #[must_use]
94 pub fn color(&self) -> Vec4 {
95 self.color
96 }
97
98 pub fn set_color(&mut self, color: Vec3) -> &mut Self {
100 self.color = color.extend(1.0);
101 self.render_data = None;
103 self
104 }
105
106 #[must_use]
108 pub fn widget_focal_length(&self) -> f32 {
109 self.widget_focal_length
110 }
111
112 pub fn set_widget_focal_length(&mut self, length: f32, is_relative: bool) -> &mut Self {
114 self.widget_focal_length = length;
115 self.widget_focal_length_is_relative = is_relative;
116 self.render_data = None; self
118 }
119
120 #[must_use]
122 pub fn widget_thickness(&self) -> f32 {
123 self.widget_thickness
124 }
125
126 pub fn set_widget_thickness(&mut self, thickness: f32) -> &mut Self {
128 self.widget_thickness = thickness;
129 self.render_data = None; self
131 }
132
133 fn compute_focal_length(&self, length_scale: f32) -> f32 {
135 if self.widget_focal_length_is_relative {
136 self.widget_focal_length * length_scale
137 } else {
138 self.widget_focal_length
139 }
140 }
141
142 fn compute_radius(&self, length_scale: f32) -> f32 {
144 let focal = self.compute_focal_length(length_scale);
145 focal * self.widget_thickness
146 }
147
148 fn generate_wireframe(&self, length_scale: f32) -> (Vec<Vec3>, Vec<[u32; 2]>) {
150 let focal = self.compute_focal_length(length_scale);
151
152 let root = self.params.position();
153 let (look_dir, up_dir, right_dir) = self.params.camera_frame();
154
155 let frame_center = root + look_dir * focal;
157
158 let half_height = focal * (self.params.fov_vertical_degrees().to_radians() / 2.0).tan();
160 let half_width = self.params.aspect_ratio() * half_height;
161
162 let frame_up = up_dir * half_height;
163 let frame_right = right_dir * half_width;
164
165 let upper_left = frame_center + frame_up - frame_right;
167 let upper_right = frame_center + frame_up + frame_right;
168 let lower_left = frame_center - frame_up - frame_right;
169 let lower_right = frame_center - frame_up + frame_right;
170
171 let tri_left = frame_center + frame_up * 1.2 - frame_right * 0.7;
173 let tri_right = frame_center + frame_up * 1.2 + frame_right * 0.7;
174 let tri_top = frame_center + frame_up * 2.0;
175
176 let nodes = vec![
178 root, upper_left, upper_right, lower_left, lower_right, tri_left, tri_right, tri_top, ];
187
188 let edges = vec![
190 [0, 1],
192 [0, 2],
193 [0, 3],
194 [0, 4],
195 [1, 2],
197 [2, 4],
198 [4, 3],
199 [3, 1],
200 [5, 6],
202 [6, 7],
203 [7, 5],
204 ];
205
206 (nodes, edges)
207 }
208
209 #[must_use]
211 pub fn needs_reinit(&self, length_scale: f32) -> bool {
212 self.render_data.is_none()
213 || (self.widget_focal_length_is_relative
214 && (self.prepared_length_scale - length_scale).abs() > 1e-6)
215 }
216
217 pub fn init_render_data(
219 &mut self,
220 device: &wgpu::Device,
221 bind_group_layout: &wgpu::BindGroupLayout,
222 camera_buffer: &wgpu::Buffer,
223 queue: &wgpu::Queue,
224 length_scale: f32,
225 ) {
226 let (nodes, edges) = self.generate_wireframe(length_scale);
227
228 let edge_tail_inds: Vec<u32> = edges.iter().map(|e| e[0]).collect();
230 let edge_tip_inds: Vec<u32> = edges.iter().map(|e| e[1]).collect();
231
232 let render_data = CurveNetworkRenderData::new(
233 device,
234 bind_group_layout,
235 camera_buffer,
236 &nodes,
237 &edge_tail_inds,
238 &edge_tip_inds,
239 );
240
241 let uniforms = polyscope_render::CurveNetworkUniforms {
243 color: self.color.to_array(),
244 radius: self.compute_radius(length_scale),
245 radius_is_relative: 0, render_mode: 0, _padding: 0.0,
248 };
249 render_data.update_uniforms(queue, &uniforms);
250
251 self.render_data = Some(render_data);
252 self.prepared_length_scale = length_scale;
253 }
254
255 #[must_use]
257 pub fn render_data(&self) -> Option<&CurveNetworkRenderData> {
258 self.render_data.as_ref()
259 }
260
261 #[must_use]
264 pub fn take_fly_to_request(&mut self) -> bool {
265 let requested = self.fly_to_requested;
266 self.fly_to_requested = false;
267 requested
268 }
269
270 pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) {
272 ui.horizontal(|ui| {
274 if ui.button("fly to").clicked() {
275 self.fly_to_requested = true;
276 }
277 ui.label(format!(
278 "FoV: {:.1}° aspect: {:.2}",
279 self.params.fov_vertical_degrees(),
280 self.params.aspect_ratio()
281 ));
282 });
283
284 ui.horizontal(|ui| {
286 ui.label("Color:");
287 let mut color = [self.color.x, self.color.y, self.color.z];
288 if ui.color_edit_button_rgb(&mut color).changed() {
289 self.set_color(Vec3::new(color[0], color[1], color[2]));
290 }
291 });
292
293 ui.horizontal(|ui| {
295 ui.label("Thickness:");
296 let mut thickness = self.widget_thickness;
297 if ui
298 .add(
299 egui::DragValue::new(&mut thickness)
300 .speed(0.001)
301 .range(0.001..=0.5),
302 )
303 .changed()
304 {
305 self.set_widget_thickness(thickness);
306 }
307 });
308
309 ui.separator();
311 ui.label(format!(
312 "Position: ({:.2}, {:.2}, {:.2})",
313 self.params.position().x,
314 self.params.position().y,
315 self.params.position().z
316 ));
317 }
318}
319
320impl Structure for CameraView {
321 fn as_any(&self) -> &dyn std::any::Any {
322 self
323 }
324
325 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
326 self
327 }
328
329 fn name(&self) -> &str {
330 &self.name
331 }
332
333 fn type_name(&self) -> &'static str {
334 "CameraView"
335 }
336
337 fn bounding_box(&self) -> Option<(Vec3, Vec3)> {
338 Some((self.params.position(), self.params.position()))
341 }
342
343 fn length_scale(&self) -> f32 {
344 0.0
346 }
347
348 fn transform(&self) -> Mat4 {
349 self.transform
350 }
351
352 fn set_transform(&mut self, transform: Mat4) {
353 self.transform = transform;
354 }
356
357 fn is_enabled(&self) -> bool {
358 self.enabled
359 }
360
361 fn set_enabled(&mut self, enabled: bool) {
362 self.enabled = enabled;
363 }
364
365 fn draw(&self, _ctx: &mut dyn RenderContext) {
366 }
368
369 fn draw_pick(&self, _ctx: &mut dyn RenderContext) {
370 }
372
373 fn build_ui(&mut self, _ui: &dyn std::any::Any) {
374 }
376
377 fn build_pick_ui(&self, _ui: &dyn std::any::Any, _pick: &PickResult) {
378 }
380
381 fn clear_gpu_resources(&mut self) {
382 self.render_data = None;
383 for quantity in &mut self.quantities {
384 quantity.clear_gpu_resources();
385 }
386 }
387
388 fn refresh(&mut self) {
389 self.render_data = None;
391 for quantity in &mut self.quantities {
392 quantity.refresh();
393 }
394 }
395}
396
397impl HasQuantities for CameraView {
398 fn add_quantity(&mut self, quantity: Box<dyn Quantity>) {
399 self.quantities.push(quantity);
400 }
401
402 fn get_quantity(&self, name: &str) -> Option<&dyn Quantity> {
403 self.quantities
404 .iter()
405 .find(|q| q.name() == name)
406 .map(std::convert::AsRef::as_ref)
407 }
408
409 fn get_quantity_mut(&mut self, name: &str) -> Option<&mut Box<dyn Quantity>> {
410 self.quantities.iter_mut().find(|q| q.name() == name)
411 }
412
413 fn remove_quantity(&mut self, name: &str) -> Option<Box<dyn Quantity>> {
414 let idx = self.quantities.iter().position(|q| q.name() == name)?;
415 Some(self.quantities.remove(idx))
416 }
417
418 fn quantities(&self) -> &[Box<dyn Quantity>] {
419 &self.quantities
420 }
421}