mirror of
https://github.com/Astatin3/rushroom.git
synced 2026-06-09 00:28:01 -06:00
Add Panes
This commit is contained in:
@@ -12,6 +12,7 @@ eframe = { version = "0.29.1", features = [
|
|||||||
"persistence",]}
|
"persistence",]}
|
||||||
|
|
||||||
egui = { version = "0.29.1", features = ["callstack", "default", "log"] }
|
egui = { version = "0.29.1", features = ["callstack", "default", "log"] }
|
||||||
|
erased-serde = "0.4.5"
|
||||||
glam = "0.29.2"
|
glam = "0.29.2"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde = { version = "1.0.215", features = ["derive"] }
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
|
|||||||
+23
-175
@@ -1,20 +1,16 @@
|
|||||||
// use egui::{FontFamily, FontId, RichText, Visuals};
|
// use egui::{FontFamily, FontId, RichText, Visuals};
|
||||||
use eframe::egui_glow;
|
// use eframe::egui_glow;
|
||||||
use std::{sync::Arc, time::Instant};
|
// use std::{sync::Arc, time::Instant};
|
||||||
use egui::{accesskit::TextAlign, mutex::Mutex, Align2, Color32, FontId, Pos2, Stroke};
|
// use egui::{accesskit::TextAlign, mutex::Mutex, Align2, Color32, FontId, Pos2, Stroke};
|
||||||
use egui_glow::glow;
|
// use egui_glow::glow;
|
||||||
|
|
||||||
use crate::{panes::Pane, point_cloud_renderer::PointRenderer, PaneManager};
|
use crate::panes::PaneManager;
|
||||||
|
|
||||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||||
// #[derive(serde::Deserialize, serde::Serialize)]
|
// #[derive(serde::Deserialize, serde::Serialize)]
|
||||||
// #[serde(default)] // if we add new fields, give them default values when deserializing old state
|
// #[serde(default)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
// #[serde(skip)]
|
pane_manager: PaneManager,
|
||||||
renderer: Arc<Mutex<PointRenderer>>,
|
|
||||||
points: Vec<(i32, i32, i32, Color32)>,
|
|
||||||
file_dialog_open: bool,
|
|
||||||
cur_path: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -31,79 +27,35 @@ impl App {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
renderer: Arc::new(Mutex::new(PointRenderer::new(cc.gl.clone(), 1_000_000))),
|
pane_manager: PaneManager::new(Some(cc)),
|
||||||
points: Vec::new(),
|
|
||||||
file_dialog_open: false,
|
|
||||||
cur_path: "./".to_string(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for App {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pane_manager: PaneManager::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl eframe::App for App {
|
impl eframe::App for App {
|
||||||
/// Called each time the UI needs repainting, which may be many times per second.
|
/// Called each time the UI needs repainting, which may be many times per second.
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
|
|
||||||
// For inspiration and more examples, go to https://emilk.github.io/egui
|
|
||||||
|
|
||||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
|
||||||
// The top panel is often a good place for a menu bar:
|
|
||||||
|
|
||||||
egui::menu::bar(ui, |ui| {
|
|
||||||
// NOTE: no File->Quit on web pages!
|
|
||||||
let is_web = cfg!(target_arch = "wasm32");
|
|
||||||
if !is_web {
|
|
||||||
ui.menu_button("File", |ui| {
|
|
||||||
if ui.button("Quit").clicked() {
|
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.add_space(16.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
egui::widgets::global_theme_preference_switch(ui);
|
|
||||||
|
|
||||||
if ui.button("Load PLY").clicked() {
|
|
||||||
self.file_dialog_open = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if self.file_dialog_open {
|
|
||||||
egui::Window::new("Load PLY File")
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.label("Enter PLY file path:");
|
|
||||||
ui.text_edit_singleline(&mut self.cur_path); // Add proper path handling
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if ui.button("Load").clicked() {
|
|
||||||
let renderer = &mut self.renderer.lock();
|
|
||||||
// Add proper path handling and error reporting
|
|
||||||
let ply = renderer.load_ply(self.cur_path.clone());
|
|
||||||
if let Err(e) = ply {
|
|
||||||
eprintln!("Failed to load PLY: {}", e);
|
|
||||||
}else{
|
|
||||||
// self.renderer.lock().camera.reset();
|
|
||||||
self.points = ply.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.file_dialog_open = false;
|
|
||||||
}
|
|
||||||
if ui.button("Cancel").clicked() {
|
|
||||||
self.file_dialog_open = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
self.pane_manager.render(ui);
|
||||||
// egui::scroll_area::ScrollArea::vertical().show(ui, |ui| {
|
// egui::scroll_area::ScrollArea::vertical().show(ui, |ui| {
|
||||||
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
// egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||||
self.custom_painting(ui.max_rect(), ui);
|
// self.custom_painting(ui.max_rect(), ui);
|
||||||
});
|
// });
|
||||||
// })
|
// })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn on_exit(&mut self, gl: Option<&glow::Context>) {
|
// fn on_exit(&mut self, gl: Option<&glow::Context>) {
|
||||||
@@ -116,108 +68,4 @@ impl eframe::App for App {
|
|||||||
// fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
// fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||||
// eframe::set_value(storage, eframe::APP_KEY, self);
|
// eframe::set_value(storage, eframe::APP_KEY, self);
|
||||||
// }
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
fn custom_painting(&mut self, max_rect: egui::Rect, ui: &mut egui::Ui) {
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
let (rect, response) =
|
|
||||||
ui.allocate_exact_size(egui::Vec2 { x: max_rect.width(), y: max_rect.height() }, egui::Sense::drag());
|
|
||||||
|
|
||||||
let input_state = ui.input(|input_state| {input_state.clone()});
|
|
||||||
|
|
||||||
// ui.painter();.
|
|
||||||
|
|
||||||
// println!("{}",response.drag_motion().x);
|
|
||||||
|
|
||||||
// let response = Box::new(response);
|
|
||||||
|
|
||||||
// let ui = ui.to_owned();
|
|
||||||
|
|
||||||
// self.anglex += response.drag_motion().x * 0.01;
|
|
||||||
// self.angley += response.drag_motion().y * 0.01;
|
|
||||||
|
|
||||||
// Clone locals so we can move them into the paint callback:
|
|
||||||
if self.points.is_empty() {
|
|
||||||
let radius = 1000i32;
|
|
||||||
for i in 0..100000 {
|
|
||||||
// let theta = (i as f32 * 0.1).sin() * std::f32::consts::PI;
|
|
||||||
// let phi = (i as f32 * 0.1).cos() * std::f32::consts::PI;
|
|
||||||
|
|
||||||
let x = (radius as f32 * (i as f32).cos()) as i32;
|
|
||||||
let y = (radius as f32 * (i as f32).sin()) as i32;
|
|
||||||
let z = (i as f32 * 0.05) as i32;
|
|
||||||
|
|
||||||
// let x = (i as f32 * 0.1) as u32;
|
|
||||||
// let y = (i as f32 * 0.1) as u32 ;
|
|
||||||
// let z = (i as f32 * 0.1) as u32;
|
|
||||||
|
|
||||||
// Color based on position
|
|
||||||
let color = Color32::from_rgba_premultiplied(
|
|
||||||
((x as f32 / radius as f32) * 255.0) as u8,
|
|
||||||
((y as f32 / radius as f32) * 255.0) as u8,
|
|
||||||
((z as f32 / radius as f32) * 255.0) as u8,
|
|
||||||
255,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.points.push((x, y, z, color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let renderer = self.renderer.clone();
|
|
||||||
renderer.lock().clear();
|
|
||||||
|
|
||||||
// let painter = ui.painter();
|
|
||||||
|
|
||||||
for &(x, y, z, color) in &self.points {
|
|
||||||
renderer.lock().add_point(x, y, z, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
let o = renderer.lock().camera.orientation.clone();
|
|
||||||
|
|
||||||
let cb = egui_glow::CallbackFn::new(move |_info, _painter| {
|
|
||||||
renderer.lock().render(rect, input_state.clone());
|
|
||||||
});
|
|
||||||
|
|
||||||
let callback = egui::PaintCallback {
|
|
||||||
rect,
|
|
||||||
callback: Arc::new(cb),
|
|
||||||
};
|
|
||||||
|
|
||||||
ui.painter().add(callback);
|
|
||||||
|
|
||||||
let pos1 = o.inverse()*glam::Vec3::X;
|
|
||||||
let pos2 = o.inverse()*glam::Vec3::Y;
|
|
||||||
let pos3 = o.inverse()*glam::Vec3::Z;
|
|
||||||
|
|
||||||
let line_length:f32 = 20.;
|
|
||||||
|
|
||||||
ui.painter().line_segment([rect.center(), rect.center() + egui::Vec2{ x: line_length*pos1.x, y: -line_length*pos1.y,}], Stroke {
|
|
||||||
width: 1.5,
|
|
||||||
color: Color32::RED,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
ui.painter().line_segment([rect.center(), rect.center() + egui::Vec2{ x: line_length*pos2.x, y: -line_length*pos2.y,}], Stroke {
|
|
||||||
width: 1.5,
|
|
||||||
color: Color32::BLUE,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
ui.painter().line_segment([rect.center(), rect.center() + egui::Vec2{ x: line_length*pos3.x, y: -line_length*pos3.y,}], Stroke {
|
|
||||||
width: 1.5,
|
|
||||||
color: Color32::GREEN,
|
|
||||||
});
|
|
||||||
|
|
||||||
let end_time = Instant::now();
|
|
||||||
|
|
||||||
println!("{}", end_time.duration_since(start_time).as_millis());
|
|
||||||
|
|
||||||
ui.painter().text(Pos2 {x:0.,y:0.}, Align2::LEFT_TOP, format!("{}",end_time.duration_since(start_time).as_millis()), FontId::monospace(12.), Color32::WHITE);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+41
-41
@@ -2,7 +2,7 @@
|
|||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||||
|
|
||||||
use rushroom::App;
|
use rushroom::App;
|
||||||
use rushroom::PaneManager;
|
// use rushroom::PaneManager;
|
||||||
|
|
||||||
// When compiling natively:
|
// When compiling natively:
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@@ -30,49 +30,49 @@ fn main() -> eframe::Result {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// When compiling to web using trunk:
|
// // When compiling to web using trunk:
|
||||||
#[cfg(target_arch = "wasm32")]
|
// #[cfg(target_arch = "wasm32")]
|
||||||
fn main() {
|
// fn main() {
|
||||||
use eframe::wasm_bindgen::JsCast as _;
|
// use eframe::wasm_bindgen::JsCast as _;
|
||||||
|
|
||||||
// Redirect `log` message to `console.log` and friends:
|
// // Redirect `log` message to `console.log` and friends:
|
||||||
eframe::WebLogger::init(log::LevelFilter::Debug).ok();
|
// eframe::WebLogger::init(log::LevelFilter::Debug).ok();
|
||||||
|
|
||||||
let web_options = eframe::WebOptions::default();
|
// let web_options = eframe::WebOptions::default();
|
||||||
|
|
||||||
wasm_bindgen_futures::spawn_local(async {
|
// wasm_bindgen_futures::spawn_local(async {
|
||||||
let document = web_sys::window()
|
// let document = web_sys::window()
|
||||||
.expect("No window")
|
// .expect("No window")
|
||||||
.document()
|
// .document()
|
||||||
.expect("No document");
|
// .expect("No document");
|
||||||
|
|
||||||
let canvas = document
|
// let canvas = document
|
||||||
.get_element_by_id("the_canvas_id")
|
// .get_element_by_id("the_canvas_id")
|
||||||
.expect("Failed to find the_canvas_id")
|
// .expect("Failed to find the_canvas_id")
|
||||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
// .dyn_into::<web_sys::HtmlCanvasElement>()
|
||||||
.expect("the_canvas_id was not a HtmlCanvasElement");
|
// .expect("the_canvas_id was not a HtmlCanvasElement");
|
||||||
|
|
||||||
let start_result = eframe::WebRunner::new()
|
// let start_result = eframe::WebRunner::new()
|
||||||
.start(
|
// .start(
|
||||||
canvas,
|
// canvas,
|
||||||
web_options,
|
// web_options,
|
||||||
Box::new(|cc| Ok(Box::new(App::new(cc)))),
|
// Box::new(|cc| Ok(Box::new(App::new(cc)))),
|
||||||
)
|
// )
|
||||||
.await;
|
// .await;
|
||||||
|
|
||||||
// Remove the loading text and spinner:
|
// // Remove the loading text and spinner:
|
||||||
if let Some(loading_text) = document.get_element_by_id("loading_text") {
|
// if let Some(loading_text) = document.get_element_by_id("loading_text") {
|
||||||
match start_result {
|
// match start_result {
|
||||||
Ok(_) => {
|
// Ok(_) => {
|
||||||
loading_text.remove();
|
// loading_text.remove();
|
||||||
}
|
// }
|
||||||
Err(e) => {
|
// Err(e) => {
|
||||||
loading_text.set_inner_html(
|
// loading_text.set_inner_html(
|
||||||
"<p> The app has crashed. See the developer console for details. </p>",
|
// "<p> The app has crashed. See the developer console for details. </p>",
|
||||||
);
|
// );
|
||||||
panic!("Failed to start eframe: {e:?}");
|
// panic!("Failed to start eframe: {e:?}");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|||||||
+633
-409
File diff suppressed because it is too large
Load Diff
+189
-22
@@ -1,11 +1,19 @@
|
|||||||
use egui::{vec2, Color32, InputState, Rect, Response};
|
use egui::{Color32, InputState, Rect};
|
||||||
use eframe::egui_glow;
|
use eframe::egui_glow;
|
||||||
use egui_glow::glow;
|
use egui_glow::glow;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use glam::{Vec3, Mat4, Quat, Vec2};
|
use glam::{Vec3, Mat4, Quat};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
// use std::path::Path;
|
||||||
use std::io::{BufReader, BufRead};
|
use std::io::{BufReader, BufRead};
|
||||||
|
use crate::panes::{Pane, PaneMode, PaneState};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use egui::FontId;
|
||||||
|
use egui::Align2;
|
||||||
|
// use egui::Pos2;
|
||||||
|
use std::time::Instant;
|
||||||
|
use egui::Stroke;
|
||||||
|
use egui::Ui;
|
||||||
|
|
||||||
// Shader sources updated for 3D rendering with fixed-point positions
|
// Shader sources updated for 3D rendering with fixed-point positions
|
||||||
const VERTEX_SHADER: &str = r#"
|
const VERTEX_SHADER: &str = r#"
|
||||||
@@ -66,13 +74,13 @@ impl Camera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
// pub fn reset(&mut self) {
|
||||||
self.position = Vec3::new(0.0, 0.0, 5.0);
|
// self.position = Vec3::new(0.0, 0.0, 5.0);
|
||||||
self.orientation = Quat::IDENTITY;
|
// self.orientation = Quat::IDENTITY;
|
||||||
self.distance = 5.0;
|
// self.distance = 5.0;
|
||||||
// self.point_size_scale = 0.1;
|
// // self.point_size_scale = 0.1;
|
||||||
self.update_view();
|
// self.update_view();
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn update(&mut self, i: InputState) {
|
pub fn update(&mut self, i: InputState) {
|
||||||
// let response = {
|
// let response = {
|
||||||
@@ -112,7 +120,7 @@ impl Camera {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Middle mouse button for camera-plane panning
|
// Middle mouse button for camera-plane panning
|
||||||
if i.pointer.middle_down() {
|
if i.pointer.primary_down() {
|
||||||
let delta = i.pointer.delta();
|
let delta = i.pointer.delta();
|
||||||
let pan_speed = self.distance * 0.001;
|
let pan_speed = self.distance * 0.001;
|
||||||
|
|
||||||
@@ -165,9 +173,9 @@ impl Camera {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_point_size_scale(&mut self, scale: f32) {
|
// pub fn set_point_size_scale(&mut self, scale: f32) {
|
||||||
self.point_size_scale = scale.clamp(0.1, 10.0);
|
// self.point_size_scale = scale.clamp(0.1, 10.0);
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +200,7 @@ pub struct PointRenderer {
|
|||||||
vao: glow::VertexArray,
|
vao: glow::VertexArray,
|
||||||
vbo: glow::Buffer,
|
vbo: glow::Buffer,
|
||||||
points: Vec<i32>,
|
points: Vec<i32>,
|
||||||
capacity: usize,
|
// capacity: usize,
|
||||||
pub camera: Camera,
|
pub camera: Camera,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +264,7 @@ impl PointRenderer {
|
|||||||
vao,
|
vao,
|
||||||
vbo,
|
vbo,
|
||||||
points: Vec::with_capacity(initial_capacity * 7),
|
points: Vec::with_capacity(initial_capacity * 7),
|
||||||
capacity: initial_capacity,
|
// capacity: initial_capacity,
|
||||||
camera: Camera::new(),
|
camera: Camera::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,11 +278,13 @@ impl PointRenderer {
|
|||||||
self.points.clear();
|
self.points.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self, rect: Rect, input_state: InputState) {
|
pub fn render(&mut self, rect: Rect, input_state: Option<InputState>) {
|
||||||
use glow::HasContext;
|
use glow::HasContext;
|
||||||
|
|
||||||
// Update camera
|
// Update camera
|
||||||
self.camera.update(input_state);
|
if let Some(i) = input_state{
|
||||||
|
self.camera.update(i);
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
self.gl.use_program(Some(self.program));
|
self.gl.use_program(Some(self.program));
|
||||||
@@ -336,7 +346,7 @@ impl PointRenderer {
|
|||||||
|
|
||||||
|
|
||||||
// Add method to load points from PLY file
|
// Add method to load points from PLY file
|
||||||
pub fn load_ply(&mut self, path: String) -> Result<(Vec<(i32, i32, i32, Color32)>), String> {
|
pub fn load_ply(&mut self, path: String) -> Result<Vec<(i32, i32, i32, Color32)>, String> {
|
||||||
let file = File::open(path).map_err(|e| format!("Failed to open file: {}", e))?;
|
let file = File::open(path).map_err(|e| format!("Failed to open file: {}", e))?;
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
let mut lines = reader.lines();
|
let mut lines = reader.lines();
|
||||||
@@ -363,7 +373,7 @@ impl PointRenderer {
|
|||||||
let mut vertex_count = 0;
|
let mut vertex_count = 0;
|
||||||
let mut has_colors = false;
|
let mut has_colors = false;
|
||||||
let mut is_binary = false;
|
let mut is_binary = false;
|
||||||
let mut in_header = true;
|
let in_header = true;
|
||||||
|
|
||||||
while in_header {
|
while in_header {
|
||||||
let line = lines.next()
|
let line = lines.next()
|
||||||
@@ -400,7 +410,7 @@ impl PointRenderer {
|
|||||||
&mut self,
|
&mut self,
|
||||||
lines: std::io::Lines<B>,
|
lines: std::io::Lines<B>,
|
||||||
header: PlyHeader,
|
header: PlyHeader,
|
||||||
) -> Result<(Vec<(i32, i32, i32, Color32)>), String> {
|
) -> Result<Vec<(i32, i32, i32, Color32)>, String> {
|
||||||
let mut vec: Vec<(i32, i32, i32, Color32)> = Vec::new();
|
let mut vec: Vec<(i32, i32, i32, Color32)> = Vec::new();
|
||||||
|
|
||||||
for line in lines.take(header.vertex_count) {
|
for line in lines.take(header.vertex_count) {
|
||||||
@@ -436,7 +446,7 @@ impl PointRenderer {
|
|||||||
// self.add_point(x, y, z, color);
|
// self.add_point(x, y, z, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((vec))
|
Ok(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -445,4 +455,161 @@ impl Drop for PointRenderer {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Clean up GPU resources
|
// Clean up GPU resources
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PointRendererPane {
|
||||||
|
renderer: Arc<Mutex<PointRenderer>>,
|
||||||
|
points: Vec<(i32, i32, i32, Color32)>,
|
||||||
|
file_dialog_open: bool,
|
||||||
|
cur_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pane for PointRendererPane {
|
||||||
|
fn new(cc: &eframe::CreationContext<'_>) -> PaneState where Self: Sized {
|
||||||
|
let mut s = Self {
|
||||||
|
renderer: Arc::new(Mutex::new(PointRenderer::new(cc.gl.clone(), 1_000_000))),
|
||||||
|
points: Vec::new(),
|
||||||
|
file_dialog_open: false,
|
||||||
|
cur_path: "./".to_string(),
|
||||||
|
};
|
||||||
|
PaneState {
|
||||||
|
id: s.name().to_string(),
|
||||||
|
mode: PaneMode::Center,
|
||||||
|
pane: Box::new(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn name(&mut self) -> &str {"Point Cloud"}
|
||||||
|
fn render(&mut self, ui: &mut Ui){
|
||||||
|
let max_rect = ui.max_rect();
|
||||||
|
|
||||||
|
let renderer = self.renderer.clone();
|
||||||
|
renderer.lock().expect("Renderer Not Initialized").clear();
|
||||||
|
|
||||||
|
|
||||||
|
if self.file_dialog_open {
|
||||||
|
egui::Window::new("Load PLY File")
|
||||||
|
.show(ui.ctx(), |ui| {
|
||||||
|
ui.label("Enter PLY file path:");
|
||||||
|
ui.text_edit_singleline(&mut self.cur_path); // Add proper path handling
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("Load").clicked() {
|
||||||
|
let renderer = &mut renderer.lock().expect("Renderer Not Initialized");
|
||||||
|
// Add proper path handling and error reporting
|
||||||
|
let ply = renderer.load_ply(self.cur_path.clone());
|
||||||
|
if let Err(e) = ply {
|
||||||
|
eprintln!("Failed to load PLY: {}", e);
|
||||||
|
}else{
|
||||||
|
// self.renderer.lock().camera.reset();
|
||||||
|
self.points = ply.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.file_dialog_open = false;
|
||||||
|
}
|
||||||
|
if ui.button("Cancel").clicked() {
|
||||||
|
self.file_dialog_open = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
let (rect, response) =
|
||||||
|
ui.allocate_exact_size(egui::Vec2 { x: max_rect.width(), y: max_rect.height() }, egui::Sense::drag());
|
||||||
|
|
||||||
|
|
||||||
|
let input_state: Option<InputState> = ui.input(|input_state|
|
||||||
|
if response.hovered() { //&& response.has_focus() {
|
||||||
|
Some(input_state.clone())
|
||||||
|
}else{None}
|
||||||
|
);
|
||||||
|
|
||||||
|
if self.points.is_empty() {
|
||||||
|
let radius = 1000i32;
|
||||||
|
for i in 0..100000 {
|
||||||
|
// let theta = (i as f32 * 0.1).sin() * std::f32::consts::PI;
|
||||||
|
// let phi = (i as f32 * 0.1).cos() * std::f32::consts::PI;
|
||||||
|
|
||||||
|
let x = (radius as f32 * (i as f32).cos()) as i32;
|
||||||
|
let y = (radius as f32 * (i as f32).sin()) as i32;
|
||||||
|
let z = (i as f32 * 0.05) as i32;
|
||||||
|
|
||||||
|
// let x = (i as f32 * 0.1) as u32;
|
||||||
|
// let y = (i as f32 * 0.1) as u32 ;
|
||||||
|
// let z = (i as f32 * 0.1) as u32;
|
||||||
|
|
||||||
|
// Color based on position
|
||||||
|
let color = Color32::from_rgba_premultiplied(
|
||||||
|
((x as f32 / radius as f32) * 255.0) as u8,
|
||||||
|
((y as f32 / radius as f32) * 255.0) as u8,
|
||||||
|
((z as f32 / radius as f32) * 255.0) as u8,
|
||||||
|
255,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.points.push((x, y, z, color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let painter = ui.painter();
|
||||||
|
|
||||||
|
for &(x, y, z, color) in &self.points {
|
||||||
|
renderer.lock().expect("Renderer Not Initialized").add_point(x, y, z, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
let o = renderer.lock().expect("Renderer Not Initialized").camera.orientation.clone();
|
||||||
|
|
||||||
|
let cb = egui_glow::CallbackFn::new(move |_info, _painter| {
|
||||||
|
renderer.lock().expect("Renderer Not Initialized").render(max_rect, input_state.clone());
|
||||||
|
});
|
||||||
|
|
||||||
|
let callback = egui::PaintCallback {
|
||||||
|
rect: max_rect,
|
||||||
|
callback: Arc::new(cb),
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.painter().add(callback);
|
||||||
|
|
||||||
|
let pos1 = o.inverse()*glam::Vec3::X;
|
||||||
|
let pos2 = o.inverse()*glam::Vec3::Y;
|
||||||
|
let pos3 = o.inverse()*glam::Vec3::Z;
|
||||||
|
|
||||||
|
let line_length:f32 = 20.;
|
||||||
|
|
||||||
|
ui.painter().line_segment([rect.center(), rect.center() + egui::Vec2{ x: line_length*pos1.x, y: -line_length*pos1.y,}], Stroke {
|
||||||
|
width: 1.5,
|
||||||
|
color: Color32::RED,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
ui.painter().line_segment([rect.center(), rect.center() + egui::Vec2{ x: line_length*pos2.x, y: -line_length*pos2.y,}], Stroke {
|
||||||
|
width: 1.5,
|
||||||
|
color: Color32::BLUE,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
ui.painter().line_segment([rect.center(), rect.center() + egui::Vec2{ x: line_length*pos3.x, y: -line_length*pos3.y,}], Stroke {
|
||||||
|
width: 1.5,
|
||||||
|
color: Color32::GREEN,
|
||||||
|
});
|
||||||
|
|
||||||
|
let end_time = Instant::now();
|
||||||
|
|
||||||
|
// println!("{}", end_time.duration_since(start_time).as_millis());
|
||||||
|
|
||||||
|
let text_size = 12.;
|
||||||
|
|
||||||
|
ui.painter().text(max_rect.min, Align2::LEFT_TOP,
|
||||||
|
format!("{} ms",end_time.duration_since(start_time).as_millis()),
|
||||||
|
FontId::monospace(text_size), Color32::WHITE);
|
||||||
|
|
||||||
|
ui.painter().text(max_rect.min + egui::Vec2 {x:0.,y:text_size}, Align2::LEFT_TOP,
|
||||||
|
format!("{} points", self.points.len()),
|
||||||
|
FontId::monospace(text_size), Color32::WHITE);
|
||||||
|
}
|
||||||
|
fn context_menu(&mut self, ui: &mut Ui) {
|
||||||
|
if ui.button("Load PLY").clicked() {
|
||||||
|
self.file_dialog_open = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user