From 2d4ee8d8a884856e1d924ce53c9898eeaa4ef069 Mon Sep 17 00:00:00 2001 From: Ruben van de Ven Date: Tue, 27 May 2025 10:29:01 +0200 Subject: [PATCH] cropping lines instead of pruning points --- src/bin/render_lines_gui.rs | 197 +++++++++++++++++++++++------------- src/trap/laser.rs | 3 +- 2 files changed, 127 insertions(+), 73 deletions(-) diff --git a/src/bin/render_lines_gui.rs b/src/bin/render_lines_gui.rs index ab91ba7..a427ba6 100644 --- a/src/bin/render_lines_gui.rs +++ b/src/bin/render_lines_gui.rs @@ -7,6 +7,7 @@ use bevy_nannou::prelude::DARK_GRAY; // use nannou::lyon::geom::euclid::Transform2D; use nannou::{geom::Rect, math::map_range as nannou_map_range}; use nannou::prelude::*; +use nannou_egui::egui::emath::inverse_lerp; use nannou_egui::{self, egui, Egui}; use nannou_laser::DacId; use nannou_laser::{self as laser, util::map_range}; @@ -149,10 +150,13 @@ fn zmq_receive(model: &mut Model) { for laser_stream in (&model.laser_streams).into_iter() { // let lines = get_laser_lines(version); let lines_for_laser: RenderableLines = lines.clone(); - laser_stream.send(move |laser| { + let sending = laser_stream.send(move |laser| { let laser_lines: RenderableLines = lines_for_laser; laser.current_lines = laser_lines; - }).unwrap(); + }); + if let Err(e) = sending { + println!("Error sending to laser! {e:?}"); + } } model.current_lines = lines; @@ -318,6 +322,64 @@ fn model(app: &App) -> Model { // frame.add_lines(points); // } +const LASER_MIN: f32 = -1.0; +const LASER_MAX: f32 = 1.0; +fn within_laser_bounds(position: &[f32; 2]) -> bool { + !(position[0] < LASER_MIN || position[0] > LASER_MAX || position[1] < LASER_MIN || position[1] > LASER_MAX) +} +fn interpolate_to_bounds(a: &[f32;2], b: &[f32;2]) { + //let t_a = inverse_lerp(a) +} + +// From ChatGTP: Lian-Barsky Algorithm for line segment cropping +fn clip_line_to_bounds( + p1: &[f32; 2], + p2: &[f32; 2], +) -> Option<([f32; 2], [f32; 2])> { + let min = [LASER_MIN, LASER_MIN]; + let max = [LASER_MAX, LASER_MAX]; + let dx = p2[0] - p1[0]; + let dy = p2[1] - p1[1]; + + let mut t0 = 0.0; + let mut t1 = 1.0; + + let checks = [ + (-dx, p1[0] - min[0]), // Left + (dx, max[0] - p1[0]), // Right + (-dy, p1[1] - min[1]), // Bottom + (dy, max[1] - p1[1]), // Top + ]; + + for (p, q) in checks { + if p == 0.0 { + if q < 0.0 { + return None; // Line is parallel and outside + } + } else { + let r = q / p; + if p < 0.0 { + if r > t1 { + return None; + } else if r > t0 { + t0 = r; + } + } else { + if r < t0 { + return None; + } else if r < t1 { + t1 = r; + } + } + } + } + + let clipped_p1 = [p1[0] + t0 * dx, p1[1] + t0 * dy]; + let clipped_p2 = [p1[0] + t1 * dx, p1[1] + t1 * dy]; + + Some((clipped_p1, clipped_p2)) +} + fn laser_frame_producer(model: &mut LaserModel, frame: &mut laser::Frame){ let points: LaserPoints = (&model.current_lines).into(); @@ -327,7 +389,7 @@ fn laser_frame_producer(model: &mut LaserModel, frame: &mut laser::Frame){ // dbg!(&model.config.name); let mut new_points = Vec::new(); - for point in points.into_iter() { + let projected_positions: Vec<[f32;2]> = points.iter().map(|point| { let p = point.position; let new_position = match space { CoordinateSpace::World => apply_homography_matrix(model.config.homography, &p), @@ -338,35 +400,69 @@ fn laser_frame_producer(model: &mut LaserModel, frame: &mut laser::Frame){ // let new_position = apply_homography_matrix(LASER_H, &p); // let s = 1.; // when using TMP_PYTHON_LASER_H_FOR_NANNOU -- doesn't work? let s = 0xFFF as f32 / 2.; // when using TMP_PYTHON_LASER_H - let position = [new_position[0]/s - 1., new_position[1]/s - 1.]; - // let position = [ - // map_range(new_position[0], 0. as f32, 0xFFF as f32, -1.0 , 1.0 ) as f32, - // map_range(new_position[1], 0. as f32, 0xFFF as f32, -1.0 , 1.0 ) as f32, - // ]; - - const LASER_MIN: f32 = -1.0; - const LASER_MAX: f32 = 1.0; - if position[0] < LASER_MIN || position[0] > LASER_MAX || position[1] < LASER_MIN || position[1] > LASER_MAX{ - // TODO: apply previous point, as blanked (or formally, lerp to edge) - continue; + [new_position[0]/s - 1., new_position[1]/s - 1.] + }).collect(); + + for (id, position ) in projected_positions.iter().enumerate() { + let point = points[id] ; + + let mut new_positions: Vec<[f32;2]> = Vec::new(); + + // const LASER_MIN: f32 = -1.0; + // const LASER_MAX: f32 = 1.0; + if !within_laser_bounds(position) { + let mut either = false; + if id > 0 { + let prev_position = projected_positions[id-1]; + if within_laser_bounds(&prev_position) { + either = true; + // interpolate with prev + let clip = clip_line_to_bounds(&prev_position, position); + if let Some((p1, p2)) = clip { + new_positions.push(p1); + new_positions.push(p2); + } + } + } + + if id < (projected_positions.len()-1) { + let next_position = projected_positions[id+1]; + if within_laser_bounds(&next_position) { + either = true; + // interpolate with next + let clip = clip_line_to_bounds(position, &next_position); + if let Some((p1, p2)) = clip { + new_positions.push(p1); + new_positions.push(p2); + } + } + } + + if !either { + // if neither prev nor next is withint bounds, point can be ditched + continue; + } + } else { + new_positions.push(position.clone()); } - let mut color = point.color.clone(); if model.dimming < 1.0 { color[0] *= model.dimming; color[1] *= model.dimming; color[2] *= model.dimming; } - - let new_point = laser::Point { - position, - color, - .. point - }; - new_points.push(new_point); + for position in new_positions { + // let pos: [f32; 2] = position.clone(); + let new_point = laser::Point { + position, + color, + .. point.clone() + }; + new_points.push(new_point); + } + } - // dbg!(&new_points); if new_points.len() < pointno { println!("Cropped Points {} (was: {})", new_points.len(), pointno); @@ -557,28 +653,16 @@ fn update(_app: &App, model: &mut Model, update: Update) { } } + for stream in laser_streams { + let dac = stream + .dac() + .expect("`dac` returned `None` even though one was specified during stream creation"); + ui.add(egui::Label::new(format!("{:?}", dac.id()))); + } }); } -// fn key_pressed(_app: &App, model: &mut Model, key: Key) { -// // Send a new pattern to the laser on keys 1, 2, 3 and 4. -// let new_pattern = match key { -// Key::Key1 => TestPattern::Rectangle, -// Key::Key2 => TestPattern::Triangle, -// Key::Key3 => TestPattern::Crosshair, -// Key::Key4 => TestPattern::ThreeVerticalLines, -// Key::Key5 => TestPattern::Circle, -// Key::Key6 => TestPattern::Spiral, -// _ => return, -// }; -// for stream in &model.laser_streams { -// stream -// .send(move |laser| laser_frame_producer.test_pattern = new_pattern) -// .ok(); -// } -// } - fn view_laser_settings(_app: &App, model: &Model, frame: Frame) { model.egui.draw_to_frame(&frame).unwrap(); } @@ -656,37 +740,6 @@ fn draw_grid(draw: &Draw, win: &Rect, step: f32, weight: f32) { } } -// The following functions are some custom styling preferences in an attempt to improve on the -// default egui theming. - -// fn fonts() -> egui::FontDefinitions { -// let mut fonts = egui::FontDefinitions::default(); -// let entries = [ -// ( -// egui::TextStyle::Small, -// (egui::FontFamily::Proportional, 13.0), -// ), -// ( -// egui::TextStyle::Body, -// (egui::FontFamily::Proportional, 16.0), -// ), -// ( -// egui::TextStyle::Button, -// (egui::FontFamily::Proportional, 16.0), -// ), -// ( -// egui::TextStyle::Heading, -// (egui::FontFamily::Proportional, 20.0), -// ), -// ( -// egui::TextStyle::Monospace, -// (egui::FontFamily::Monospace, 14.0), -// ), -// ]; -// fonts.families.extend(entries.iter().cloned()); -// fonts -// } - fn style() -> egui::Style { let mut style = egui::Style::default(); style.spacing = egui::style::Spacing { diff --git a/src/trap/laser.rs b/src/trap/laser.rs index 3926f12..247d18e 100644 --- a/src/trap/laser.rs +++ b/src/trap/laser.rs @@ -88,6 +88,7 @@ const LASER_H_CM: Mat3 = python_cv_h_into_mat3(TMP_DESK_CLUBMAX); impl Default for DacConfig{ fn default() -> DacConfig{ //DacConfig { name: "Unknown".into(), homography: Mat3::IDENTITY } - DacConfig { name: "Unknown".into(), homography: LASER_H_CM } + // DacConfig { name: "Unknown".into(), homography: LASER_H_CM } + DacConfig { name: "Unknown".into(), homography: LASER_H } } }