1use 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#[derive(Debug, Clone)]
23pub enum ViewData {
24 Empty,
25 PointCloud(PointCloud<Point3f>),
26 ColoredPointCloud(PointCloud<ColoredPoint3f>),
27 Mesh(TriangleMesh),
28}
29
30#[derive(Debug, Clone, Copy, PartialEq)]
32pub enum CameraMode {
33 Orbit,
34 Pan,
35 Zoom,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq)]
40pub enum PipelineType {
41 Cpu,
42 Gpu,
43}
44
45#[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#[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#[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
111pub 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 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 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 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 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 pub fn run(mut self) -> Result<()> {
166 println!("Starting 3DCrate Interactive Viewer...");
167
168 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 let config = RenderConfig::default();
180 let window_clone = window.clone();
181 let mut renderer = pollster::block_on(PointCloudRenderer::new(&window_clone, config))?;
182
183 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 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 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 let vertices = match &self.current_data {
277 ViewData::PointCloud(cloud) => {
278 let mut vertices = Vec::new();
279 for point in cloud.iter() {
280 let size = 0.02; 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 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 vertices.push(v1);
294 vertices.push(v2);
295 vertices.push(v3);
296
297 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 let size = 0.02; 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 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 vertices.push(v1);
325 vertices.push(v2);
326 vertices.push(v3);
327
328 vertices.push(v1);
330 vertices.push(v3);
331 vertices.push(v4);
332 }
333 vertices
334 }
335 ViewData::Mesh(_mesh) => {
336 vec![]
339 }
340 ViewData::Empty => {
341 println!("No data to render - ViewData is Empty");
342 vec![]
343 }
344 };
345
346 if !vertices.is_empty() {
348 if self.debug_frame_count % 60 == 0 { println!("Rendering {} vertices", vertices.len());
350 }
351 }
352 self.debug_frame_count += 1;
353
354 if !vertices.is_empty() {
356 if let Err(e) = renderer.render(&vertices) {
357 eprintln!("Render error: {}", e);
358 }
359 }
360
361 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