cropping lines instead of pruning points

This commit is contained in:
Ruben van de Ven 2025-05-27 10:29:01 +02:00
parent 3e3975eb43
commit 2d4ee8d8a8
2 changed files with 127 additions and 73 deletions

View file

@ -7,6 +7,7 @@ use bevy_nannou::prelude::DARK_GRAY;
// use nannou::lyon::geom::euclid::Transform2D; // use nannou::lyon::geom::euclid::Transform2D;
use nannou::{geom::Rect, math::map_range as nannou_map_range}; use nannou::{geom::Rect, math::map_range as nannou_map_range};
use nannou::prelude::*; use nannou::prelude::*;
use nannou_egui::egui::emath::inverse_lerp;
use nannou_egui::{self, egui, Egui}; use nannou_egui::{self, egui, Egui};
use nannou_laser::DacId; use nannou_laser::DacId;
use nannou_laser::{self as laser, util::map_range}; 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() { for laser_stream in (&model.laser_streams).into_iter() {
// let lines = get_laser_lines(version); // let lines = get_laser_lines(version);
let lines_for_laser: RenderableLines = lines.clone(); 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; let laser_lines: RenderableLines = lines_for_laser;
laser.current_lines = laser_lines; laser.current_lines = laser_lines;
}).unwrap(); });
if let Err(e) = sending {
println!("Error sending to laser! {e:?}");
}
} }
model.current_lines = lines; model.current_lines = lines;
@ -318,6 +322,64 @@ fn model(app: &App) -> Model {
// frame.add_lines(points); // 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){ fn laser_frame_producer(model: &mut LaserModel, frame: &mut laser::Frame){
let points: LaserPoints = (&model.current_lines).into(); 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); // dbg!(&model.config.name);
let mut new_points = Vec::new(); 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 p = point.position;
let new_position = match space { let new_position = match space {
CoordinateSpace::World => apply_homography_matrix(model.config.homography, &p), 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 new_position = apply_homography_matrix(LASER_H, &p);
// let s = 1.; // when using TMP_PYTHON_LASER_H_FOR_NANNOU -- doesn't work? // 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 s = 0xFFF as f32 / 2.; // when using TMP_PYTHON_LASER_H
let position = [new_position[0]/s - 1., new_position[1]/s - 1.]; [new_position[0]/s - 1., new_position[1]/s - 1.]
// let position = [ }).collect();
// 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, for (id, position ) in projected_positions.iter().enumerate() {
// ]; let point = points[id] ;
const LASER_MIN: f32 = -1.0; let mut new_positions: Vec<[f32;2]> = Vec::new();
const LASER_MAX: f32 = 1.0;
if position[0] < LASER_MIN || position[0] > LASER_MAX || position[1] < LASER_MIN || position[1] > LASER_MAX{ // const LASER_MIN: f32 = -1.0;
// TODO: apply previous point, as blanked (or formally, lerp to edge) // const LASER_MAX: f32 = 1.0;
continue; 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(); let mut color = point.color.clone();
if model.dimming < 1.0 { if model.dimming < 1.0 {
color[0] *= model.dimming; color[0] *= model.dimming;
color[1] *= model.dimming; color[1] *= model.dimming;
color[2] *= 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 { if new_points.len() < pointno {
println!("Cropped Points {} (was: {})", 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) { fn view_laser_settings(_app: &App, model: &Model, frame: Frame) {
model.egui.draw_to_frame(&frame).unwrap(); 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 { fn style() -> egui::Style {
let mut style = egui::Style::default(); let mut style = egui::Style::default();
style.spacing = egui::style::Spacing { style.spacing = egui::style::Spacing {

View file

@ -88,6 +88,7 @@ const LASER_H_CM: Mat3 = python_cv_h_into_mat3(TMP_DESK_CLUBMAX);
impl Default for DacConfig{ impl Default for DacConfig{
fn default() -> DacConfig{ fn default() -> DacConfig{
//DacConfig { name: "Unknown".into(), homography: Mat3::IDENTITY } //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 }
} }
} }