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::{
16 PointCloudRenderer, RenderConfig, PointVertex,
17 MeshRenderer, MeshRenderConfig, ShadingMode, mesh_to_gpu_mesh,
18};
19use threecrate_algorithms::{ICPResult, PlaneSegmentationResult};
20use crate::camera::Camera;
21
22use nalgebra::{Vector3, Point3};
23
24#[derive(Debug, Clone)]
26pub enum ViewData {
27 Empty,
28 PointCloud(PointCloud<Point3f>),
29 ColoredPointCloud(PointCloud<ColoredPoint3f>),
30 Mesh(TriangleMesh),
31}
32
33#[derive(Debug, Clone, Copy, PartialEq)]
35pub enum CameraMode {
36 Orbit,
37 Pan,
38 Zoom,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq)]
43pub enum PipelineType {
44 Cpu,
45 Gpu,
46}
47
48#[derive(Debug, Clone)]
50pub struct ICPParams {
51 pub max_iterations: usize,
52 pub convergence_threshold: f32,
53 pub max_correspondence_distance: f32,
54}
55
56impl Default for ICPParams {
57 fn default() -> Self {
58 Self {
59 max_iterations: 50,
60 convergence_threshold: 0.001,
61 max_correspondence_distance: 1.0,
62 }
63 }
64}
65
66#[derive(Debug, Clone)]
68pub struct RANSACParams {
69 pub max_iterations: usize,
70 pub distance_threshold: f32,
71}
72
73impl Default for RANSACParams {
74 fn default() -> Self {
75 Self {
76 max_iterations: 1000,
77 distance_threshold: 0.1,
78 }
79 }
80}
81
82#[derive(Debug)]
84pub struct UIState {
85 pub render_panel_open: bool,
86 pub algorithm_panel_open: bool,
87 pub camera_panel_open: bool,
88 pub stats_panel_open: bool,
89 pub icp_params: ICPParams,
90 pub ransac_params: RANSACParams,
91 pub source_cloud: Option<PointCloud<Point3f>>,
92 pub target_cloud: Option<PointCloud<Point3f>>,
93 pub icp_result: Option<ICPResult>,
94 pub ransac_result: Option<PlaneSegmentationResult>,
95}
96
97impl Default for UIState {
98 fn default() -> Self {
99 Self {
100 render_panel_open: false,
101 algorithm_panel_open: false,
102 camera_panel_open: false,
103 stats_panel_open: false,
104 icp_params: ICPParams::default(),
105 ransac_params: RANSACParams::default(),
106 source_cloud: None,
107 target_cloud: None,
108 icp_result: None,
109 ransac_result: None,
110 }
111 }
112}
113
114pub struct InteractiveViewer {
116 current_data: ViewData,
117 camera: Camera,
118 camera_mode: CameraMode,
119 last_mouse_pos: Option<PhysicalPosition<f64>>,
120 mouse_pressed: bool,
121 right_mouse_pressed: bool,
122 debug_frame_count: usize,
123}
124
125impl InteractiveViewer {
126 pub fn new() -> Result<Self> {
128 let camera = Camera::new(
129 Point3::new(5.0, 5.0, 5.0),
130 Point3::new(0.0, 0.0, 0.0),
131 Vector3::new(0.0, 1.0, 0.0),
132 45.0,
133 1.0,
134 0.1,
135 100.0,
136 );
137
138 Ok(Self {
139 current_data: ViewData::Empty,
140 camera,
141 camera_mode: CameraMode::Orbit,
142 last_mouse_pos: None,
143 mouse_pressed: false,
144 right_mouse_pressed: false,
145 debug_frame_count: 0,
146 })
147 }
148
149 pub fn set_point_cloud(&mut self, cloud: &PointCloud<Point3f>) {
151 self.current_data = ViewData::PointCloud(cloud.clone());
152 println!("Set point cloud with {} points", cloud.len());
153 }
154
155 pub fn set_colored_point_cloud(&mut self, cloud: &PointCloud<ColoredPoint3f>) {
157 self.current_data = ViewData::ColoredPointCloud(cloud.clone());
158 println!("Set colored point cloud with {} points", cloud.len());
159 }
160
161 pub fn set_mesh(&mut self, mesh: &TriangleMesh) {
163 self.current_data = ViewData::Mesh(mesh.clone());
164 println!("Set mesh with {} vertices and {} faces", mesh.vertices.len(), mesh.faces.len());
165 }
166
167 pub fn run(mut self) -> Result<()> {
169 println!("Starting threecrate Interactive Viewer...");
170
171 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))))?;
173 let window = Arc::new(
174 WindowBuilder::new()
175 .with_title("threecrate Interactive Viewer")
176 .with_inner_size(winit::dpi::LogicalSize::new(1200.0, 800.0))
177 .build(&event_loop)
178 .map_err(|e| Error::Io(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create window: {}", e))))?
179 );
180
181 let pc_config = RenderConfig::default();
183 let window_clone = window.clone();
184 let mut point_renderer = pollster::block_on(PointCloudRenderer::new(&window_clone, pc_config))?;
185
186 let mesh_config = MeshRenderConfig::default();
187 let mut mesh_renderer = pollster::block_on(MeshRenderer::new(&window_clone, mesh_config))?;
188
189 let size = window.inner_size();
191 self.camera.aspect_ratio = size.width as f32 / size.height as f32;
192
193 println!("Viewer initialized successfully. Window should now be visible.");
194
195 event_loop.run(move |event, target| {
197 target.set_control_flow(ControlFlow::Poll);
198
199 match event {
200 Event::WindowEvent { event, .. } => {
201 match event {
202 WindowEvent::CloseRequested => {
203 target.exit();
204 }
205 WindowEvent::Resized(new_size) => {
206 point_renderer.resize(new_size);
207 mesh_renderer.resize(new_size);
208 self.camera.aspect_ratio = new_size.width as f32 / new_size.height as f32;
209 }
210 WindowEvent::MouseInput { state, button, .. } => {
211 match button {
212 MouseButton::Left => {
213 self.mouse_pressed = state == ElementState::Pressed;
214 }
215 MouseButton::Right => {
216 self.right_mouse_pressed = state == ElementState::Pressed;
217 }
218 _ => {}
219 }
220 }
221 WindowEvent::CursorMoved { position, .. } => {
222 if let Some(last_pos) = self.last_mouse_pos {
223 let delta_x = position.x - last_pos.x;
224 let delta_y = position.y - last_pos.y;
225
226 if self.mouse_pressed {
227 match self.camera_mode {
228 CameraMode::Orbit => {
229 self.camera.orbit(delta_x as f32 * 0.01, delta_y as f32 * 0.01);
230 }
231 CameraMode::Pan => {
232 self.camera.pan(delta_x as f32 * 0.01, delta_y as f32 * 0.01);
233 }
234 _ => {}
235 }
236 }
237 }
238 self.last_mouse_pos = Some(position);
239 }
240 WindowEvent::MouseWheel { delta, .. } => {
241 let scroll_delta = match delta {
242 winit::event::MouseScrollDelta::LineDelta(_, y) => y,
243 winit::event::MouseScrollDelta::PixelDelta(pos) => pos.y as f32 / 100.0,
244 };
245 self.camera.zoom(scroll_delta * 0.1);
246 }
247 WindowEvent::KeyboardInput { event, .. } => {
248 if event.state == ElementState::Pressed {
249 match &event.logical_key {
250 Key::Character(c) => {
251 match c.as_str() {
252 "o" | "O" => {
253 self.camera_mode = CameraMode::Orbit;
254 println!("Switched to Orbit mode");
255 }
256 "p" | "P" => {
257 self.camera_mode = CameraMode::Pan;
258 println!("Switched to Pan mode");
259 }
260 "z" | "Z" => {
261 self.camera_mode = CameraMode::Zoom;
262 println!("Switched to Zoom mode");
263 }
264 "r" | "R" => {
265 self.camera.reset();
266 println!("Reset camera");
267 }
268 _ => {}
269 }
270 }
271 _ => {}
272 }
273 }
274 }
275 WindowEvent::RedrawRequested => {
276 let view_matrix = self.camera.view_matrix();
278 let proj_matrix = self.camera.projection_matrix();
279 let camera_pos = self.camera.position.coords;
280 point_renderer.update_camera(view_matrix, proj_matrix, camera_pos);
281 mesh_renderer.update_camera(view_matrix, proj_matrix, camera_pos);
282
283 let vertices = match &self.current_data {
285 ViewData::PointCloud(cloud) => {
286 let mut vertices = Vec::new();
287 for point in cloud.iter() {
288 let size = 0.02; let pos = [point.x, point.y, point.z];
291 let color = [1.0, 1.0, 1.0];
292 let normal = [0.0, 0.0, 1.0];
293
294 let v1 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] - size, pos[2]), color, 16.0, normal);
296 let v2 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] - size, pos[2]), color, 16.0, normal);
297 let v3 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] + size, pos[2]), color, 16.0, normal);
298 let v4 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] + size, pos[2]), color, 16.0, normal);
299
300 vertices.push(v1);
302 vertices.push(v2);
303 vertices.push(v3);
304
305 vertices.push(v1);
307 vertices.push(v3);
308 vertices.push(v4);
309 }
310 vertices
311 }
312 ViewData::ColoredPointCloud(cloud) => {
313 let mut vertices = Vec::new();
314 for point in cloud.iter() {
315 let size = 0.02; let pos = [point.position.x, point.position.y, point.position.z];
318 let color = [
319 point.color[0] as f32 / 255.0,
320 point.color[1] as f32 / 255.0,
321 point.color[2] as f32 / 255.0,
322 ];
323 let normal = [0.0, 0.0, 1.0];
324
325 let v1 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] - size, pos[2]), color, 16.0, normal);
327 let v2 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] - size, pos[2]), color, 16.0, normal);
328 let v3 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] + size, pos[2]), color, 16.0, normal);
329 let v4 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] + size, pos[2]), color, 16.0, normal);
330
331 vertices.push(v1);
333 vertices.push(v2);
334 vertices.push(v3);
335
336 vertices.push(v1);
338 vertices.push(v3);
339 vertices.push(v4);
340 }
341 vertices
342 }
343 ViewData::Mesh(_) => {
344 vec![]
346 }
347 ViewData::Empty => {
348 println!("No data to render - ViewData is Empty");
349 vec![]
350 }
351 };
352
353 if !vertices.is_empty() {
355 if self.debug_frame_count % 60 == 0 { println!("Rendering {} vertices", vertices.len());
357 }
358 }
359 self.debug_frame_count += 1;
360
361 match &self.current_data {
362 ViewData::PointCloud(_) | ViewData::ColoredPointCloud(_) => {
363 if !vertices.is_empty() {
364 if let Err(e) = point_renderer.render(&vertices) {
365 eprintln!("Render error: {}", e);
366 }
367 }
368 }
369 ViewData::Mesh(mesh) => {
370 if !mesh.vertices.is_empty() && !mesh.faces.is_empty() {
371 let indices: Vec<u32> = mesh
373 .faces
374 .iter()
375 .flat_map(|f| [f[0] as u32, f[1] as u32, f[2] as u32])
376 .collect();
377
378 let normals_opt = mesh.normals.as_ref().map(|n| n.as_slice());
380
381 let colors_f32: Option<Vec<[f32; 3]>> = mesh.colors.as_ref().map(|cols| {
383 cols.iter()
384 .map(|c| [c[0] as f32 / 255.0, c[1] as f32 / 255.0, c[2] as f32 / 255.0])
385 .collect()
386 });
387 let colors_opt = colors_f32.as_ref().map(|c| c.as_slice());
388
389 let gpu_mesh = mesh_to_gpu_mesh(
391 &mesh.vertices,
392 &indices,
393 normals_opt,
394 colors_opt,
395 None,
396 );
397
398 if let Err(e) = mesh_renderer.render(&gpu_mesh, ShadingMode::Flat) {
399 eprintln!("Mesh render error: {}", e);
400 }
401 }
402 }
403 ViewData::Empty => {}
404 }
405
406 window.request_redraw();
408 }
409 _ => {}
410 }
411 }
412 _ => {}
413 }
414 }).map_err(|e| Error::Io(std::io::Error::new(std::io::ErrorKind::Other, format!("Event loop error: {}", e))))?;
415
416 Ok(())
417 }
418}
419
420impl Default for InteractiveViewer {
421 fn default() -> Self {
422 Self::new().expect("Failed to create InteractiveViewer")
423 }
424}
425
426