threecrate_visualization/
interactive_viewer.rs

1//! Interactive 3D viewer with UI controls
2//! 
3//! This module provides a simplified interactive viewer for 3D data
4
5use std::sync::Arc;
6use winit::{
7    event::{Event, WindowEvent, ElementState, MouseButton},
8    event_loop::{EventLoop, ControlFlow},
9    window::WindowBuilder,
10    keyboard::Key,
11    dpi::PhysicalPosition,
12};
13
14use threecrate_core::{PointCloud, TriangleMesh, Result, Point3f, ColoredPoint3f, Error};
15use threecrate_gpu::{PointCloudRenderer, RenderConfig, PointVertex};
16use threecrate_algorithms::{ICPResult, PlaneSegmentationResult};
17use crate::camera::Camera;
18
19use nalgebra::{Vector3, Point3};
20
21/// Types of data that can be displayed
22#[derive(Debug, Clone)]
23pub enum ViewData {
24    Empty,
25    PointCloud(PointCloud<Point3f>),
26    ColoredPointCloud(PointCloud<ColoredPoint3f>),
27    Mesh(TriangleMesh),
28}
29
30/// Camera control modes
31#[derive(Debug, Clone, Copy, PartialEq)]
32pub enum CameraMode {
33    Orbit,
34    Pan,
35    Zoom,
36}
37
38/// Pipeline processing type
39#[derive(Debug, Clone, Copy, PartialEq)]
40pub enum PipelineType {
41    Cpu,
42    Gpu,
43}
44
45/// ICP algorithm parameters
46#[derive(Debug, Clone)]
47pub struct ICPParams {
48    pub max_iterations: usize,
49    pub convergence_threshold: f32,
50    pub max_correspondence_distance: f32,
51}
52
53impl Default for ICPParams {
54    fn default() -> Self {
55        Self {
56            max_iterations: 50,
57            convergence_threshold: 0.001,
58            max_correspondence_distance: 1.0,
59        }
60    }
61}
62
63/// RANSAC algorithm parameters
64#[derive(Debug, Clone)]
65pub struct RANSACParams {
66    pub max_iterations: usize,
67    pub distance_threshold: f32,
68}
69
70impl Default for RANSACParams {
71    fn default() -> Self {
72        Self {
73            max_iterations: 1000,
74            distance_threshold: 0.1,
75        }
76    }
77}
78
79/// UI state for all panels and controls (kept for future use)
80#[derive(Debug)]
81pub struct UIState {
82    pub render_panel_open: bool,
83    pub algorithm_panel_open: bool,
84    pub camera_panel_open: bool,
85    pub stats_panel_open: bool,
86    pub icp_params: ICPParams,
87    pub ransac_params: RANSACParams,
88    pub source_cloud: Option<PointCloud<Point3f>>,
89    pub target_cloud: Option<PointCloud<Point3f>>,
90    pub icp_result: Option<ICPResult>,
91    pub ransac_result: Option<PlaneSegmentationResult>,
92}
93
94impl Default for UIState {
95    fn default() -> Self {
96        Self {
97            render_panel_open: false,
98            algorithm_panel_open: false,
99            camera_panel_open: false,
100            stats_panel_open: false,
101            icp_params: ICPParams::default(),
102            ransac_params: RANSACParams::default(),
103            source_cloud: None,
104            target_cloud: None,
105            icp_result: None,
106            ransac_result: None,
107        }
108    }
109}
110
111/// Interactive 3D viewer with comprehensive UI controls
112pub struct InteractiveViewer {
113    current_data: ViewData,
114    camera: Camera,
115    camera_mode: CameraMode,
116    last_mouse_pos: Option<PhysicalPosition<f64>>,
117    mouse_pressed: bool,
118    right_mouse_pressed: bool,
119    debug_frame_count: usize,
120}
121
122impl InteractiveViewer {
123    /// Create a new interactive viewer
124    pub fn new() -> Result<Self> {
125        let camera = Camera::new(
126            Point3::new(5.0, 5.0, 5.0),
127            Point3::new(0.0, 0.0, 0.0),
128            Vector3::new(0.0, 1.0, 0.0),
129            45.0,
130            1.0,
131            0.1,
132            100.0,
133        );
134
135        Ok(Self {
136            current_data: ViewData::Empty,
137            camera,
138            camera_mode: CameraMode::Orbit,
139            last_mouse_pos: None,
140            mouse_pressed: false,
141            right_mouse_pressed: false,
142            debug_frame_count: 0,
143        })
144    }
145
146    /// Set point cloud data
147    pub fn set_point_cloud(&mut self, cloud: &PointCloud<Point3f>) {
148        self.current_data = ViewData::PointCloud(cloud.clone());
149        println!("Set point cloud with {} points", cloud.len());
150    }
151
152    /// Set colored point cloud data
153    pub fn set_colored_point_cloud(&mut self, cloud: &PointCloud<ColoredPoint3f>) {
154        self.current_data = ViewData::ColoredPointCloud(cloud.clone());
155        println!("Set colored point cloud with {} points", cloud.len());
156    }
157
158    /// Set mesh data
159    pub fn set_mesh(&mut self, mesh: &TriangleMesh) {
160        self.current_data = ViewData::Mesh(mesh.clone());
161        println!("Set mesh with {} vertices and {} faces", mesh.vertices.len(), mesh.faces.len());
162    }
163
164    /// Run the interactive viewer
165    pub fn run(mut self) -> Result<()> {
166        println!("Starting 3DCrate Interactive Viewer...");
167        
168        // Create event loop and window
169        let event_loop = EventLoop::new().map_err(|e| Error::Io(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create event loop: {}", e))))?;
170        let window = Arc::new(
171            WindowBuilder::new()
172                .with_title("3DCrate Interactive Viewer")
173                .with_inner_size(winit::dpi::LogicalSize::new(1200.0, 800.0))
174                .build(&event_loop)
175                .map_err(|e| Error::Io(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create window: {}", e))))?
176        );
177
178        // Initialize renderer
179        let config = RenderConfig::default();
180        let window_clone = window.clone();
181        let mut renderer = pollster::block_on(PointCloudRenderer::new(&window_clone, config))?;
182
183        // Update camera aspect ratio
184        let size = window.inner_size();
185        self.camera.aspect_ratio = size.width as f32 / size.height as f32;
186
187        println!("Viewer initialized successfully. Window should now be visible.");
188
189        // Main event loop
190        event_loop.run(move |event, target| {
191            target.set_control_flow(ControlFlow::Poll);
192
193            match event {
194                Event::WindowEvent { event, .. } => {
195                    match event {
196                        WindowEvent::CloseRequested => {
197                            target.exit();
198                        }
199                        WindowEvent::Resized(new_size) => {
200                            renderer.resize(new_size);
201                            self.camera.aspect_ratio = new_size.width as f32 / new_size.height as f32;
202                        }
203                        WindowEvent::MouseInput { state, button, .. } => {
204                            match button {
205                                MouseButton::Left => {
206                                    self.mouse_pressed = state == ElementState::Pressed;
207                                }
208                                MouseButton::Right => {
209                                    self.right_mouse_pressed = state == ElementState::Pressed;
210                                }
211                                _ => {}
212                            }
213                        }
214                        WindowEvent::CursorMoved { position, .. } => {
215                            if let Some(last_pos) = self.last_mouse_pos {
216                                let delta_x = position.x - last_pos.x;
217                                let delta_y = position.y - last_pos.y;
218
219                                if self.mouse_pressed {
220                                    match self.camera_mode {
221                                        CameraMode::Orbit => {
222                                            self.camera.orbit(delta_x as f32 * 0.01, delta_y as f32 * 0.01);
223                                        }
224                                        CameraMode::Pan => {
225                                            self.camera.pan(delta_x as f32 * 0.01, delta_y as f32 * 0.01);
226                                        }
227                                        _ => {}
228                                    }
229                                }
230                            }
231                            self.last_mouse_pos = Some(position);
232                        }
233                        WindowEvent::MouseWheel { delta, .. } => {
234                            let scroll_delta = match delta {
235                                winit::event::MouseScrollDelta::LineDelta(_, y) => y,
236                                winit::event::MouseScrollDelta::PixelDelta(pos) => pos.y as f32 / 100.0,
237                            };
238                            self.camera.zoom(scroll_delta * 0.1);
239                        }
240                        WindowEvent::KeyboardInput { event, .. } => {
241                            if event.state == ElementState::Pressed {
242                                match &event.logical_key {
243                                    Key::Character(c) => {
244                                        match c.as_str() {
245                                            "o" | "O" => {
246                                                self.camera_mode = CameraMode::Orbit;
247                                                println!("Switched to Orbit mode");
248                                            }
249                                            "p" | "P" => {
250                                                self.camera_mode = CameraMode::Pan;
251                                                println!("Switched to Pan mode");
252                                            }
253                                            "z" | "Z" => {
254                                                self.camera_mode = CameraMode::Zoom;
255                                                println!("Switched to Zoom mode");
256                                            }
257                                            "r" | "R" => {
258                                                self.camera.reset();
259                                                println!("Reset camera");
260                                            }
261                                            _ => {}
262                                        }
263                                    }
264                                    _ => {}
265                                }
266                            }
267                        }
268                        WindowEvent::RedrawRequested => {
269                            // Update camera matrices
270                            let view_matrix = self.camera.view_matrix();
271                            let proj_matrix = self.camera.projection_matrix();
272                            let camera_pos = self.camera.position.coords;
273                            renderer.update_camera(view_matrix, proj_matrix, camera_pos);
274
275                            // Convert current data to vertices (create quads for each point)
276                            let vertices = match &self.current_data {
277                                ViewData::PointCloud(cloud) => {
278                                    let mut vertices = Vec::new();
279                                    for point in cloud.iter() {
280                                        // Create a quad (2 triangles) for each point
281                                        let size = 0.02; // Size of each point quad
282                                        let pos = [point.x, point.y, point.z];
283                                        let color = [1.0, 1.0, 1.0];
284                                        let normal = [0.0, 0.0, 1.0];
285                                        
286                                        // Create 4 vertices for a quad
287                                        let v1 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] - size, pos[2]), color, 16.0, normal);
288                                        let v2 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] - size, pos[2]), color, 16.0, normal);
289                                        let v3 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] + size, pos[2]), color, 16.0, normal);
290                                        let v4 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] + size, pos[2]), color, 16.0, normal);
291                                        
292                                        // First triangle (v1, v2, v3)
293                                        vertices.push(v1);
294                                        vertices.push(v2);
295                                        vertices.push(v3);
296                                        
297                                        // Second triangle (v1, v3, v4)
298                                        vertices.push(v1);
299                                        vertices.push(v3);
300                                        vertices.push(v4);
301                                    }
302                                    vertices
303                                }
304                                ViewData::ColoredPointCloud(cloud) => {
305                                    let mut vertices = Vec::new();
306                                    for point in cloud.iter() {
307                                        // Create a quad (2 triangles) for each point
308                                        let size = 0.02; // Size of each point quad
309                                        let pos = [point.position.x, point.position.y, point.position.z];
310                                        let color = [
311                                            point.color[0] as f32 / 255.0,
312                                            point.color[1] as f32 / 255.0,
313                                            point.color[2] as f32 / 255.0,
314                                        ];
315                                        let normal = [0.0, 0.0, 1.0];
316                                        
317                                        // Create 4 vertices for a quad
318                                        let v1 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] - size, pos[2]), color, 16.0, normal);
319                                        let v2 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] - size, pos[2]), color, 16.0, normal);
320                                        let v3 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] + size, pos[2]), color, 16.0, normal);
321                                        let v4 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] + size, pos[2]), color, 16.0, normal);
322                                        
323                                        // First triangle (v1, v2, v3)
324                                        vertices.push(v1);
325                                        vertices.push(v2);
326                                        vertices.push(v3);
327                                        
328                                        // Second triangle (v1, v3, v4)
329                                        vertices.push(v1);
330                                        vertices.push(v3);
331                                        vertices.push(v4);
332                                    }
333                                    vertices
334                                }
335                                ViewData::Mesh(_mesh) => {
336                                    // For now, just create vertices from mesh vertices
337                                    // TODO: Proper mesh rendering
338                                    vec![]
339                                }
340                                ViewData::Empty => {
341                                    println!("No data to render - ViewData is Empty");
342                                    vec![]
343                                }
344                            };
345
346                            // Debug: Print vertex count periodically
347                            if !vertices.is_empty() {
348                                if self.debug_frame_count % 60 == 0 {  // Print every 60 frames
349                                    println!("Rendering {} vertices", vertices.len());
350                                }
351                            }
352                            self.debug_frame_count += 1;
353
354                            // Render points if we have any
355                            if !vertices.is_empty() {
356                                if let Err(e) = renderer.render(&vertices) {
357                                    eprintln!("Render error: {}", e);
358                                }
359                            }
360
361                            // Request next frame
362                            window.request_redraw();
363                        }
364                        _ => {}
365                    }
366                }
367                _ => {}
368            }
369        }).map_err(|e| Error::Io(std::io::Error::new(std::io::ErrorKind::Other, format!("Event loop error: {}", e))))?;
370
371        Ok(())
372    }
373}
374
375impl Default for InteractiveViewer {
376    fn default() -> Self {
377        Self::new().expect("Failed to create InteractiveViewer")
378    }
379}
380
381