cropping lines instead of pruning points
This commit is contained in:
parent
3e3975eb43
commit
2d4ee8d8a8
2 changed files with 127 additions and 73 deletions
|
@ -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,19 +400,51 @@ 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,
|
||||
// ];
|
||||
[new_position[0]/s - 1., new_position[1]/s - 1.]
|
||||
}).collect();
|
||||
|
||||
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;
|
||||
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;
|
||||
|
@ -358,15 +452,17 @@ fn laser_frame_producer(model: &mut LaserModel, frame: &mut laser::Frame){
|
|||
color[2] *= model.dimming;
|
||||
}
|
||||
|
||||
let new_point = laser::Point {
|
||||
position,
|
||||
color,
|
||||
.. 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);
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue