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::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 {
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue