WIP: clip mask drawing

This commit is contained in:
Ruben van de Ven 2025-07-04 16:12:33 +02:00
parent 65acde713f
commit 9ef4c6745d
3 changed files with 136 additions and 48 deletions

View file

@ -13,7 +13,7 @@ use nannou_laser::DacId;
use nannou_laser::{self as laser};
use serde_json::Result;
use trap_rust::trap::filters::PointFilters;
use trap_rust::trap::laser::{shape_rect, LaserPoints, LaserSpace, StreamSource, STREAM_SOURCES, TMP_DESK_CLUBMAX};
use trap_rust::trap::laser::{shape_rect, LaserPoints, LaserSpace, StreamSource, STREAM_SOURCES, TMP_DESK_CLUBMAX, Corner};
use trap_rust::trap::tracks::CoordinateSpace;
use trap_rust::trap::{laser::{python_cv_h_into_mat3, LaserModel, TMP_PYTHON_LASER_H, DacConfig}, tracks::{RenderableLines}};
use zmq::Socket;
@ -50,28 +50,7 @@ pub struct StreamConfig{
type StreamConfigMap = HashMap<DacId, StreamConfig>;
type StreamMap = HashMap<DacId, laser::FrameStream<LaserModel>>;
#[derive(Debug, Clone)]
enum Corner{
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
impl Corner {
pub fn in_laser_space() -> Vec<[f32; 2]>{
vec!([-1.,1.], [1.,1.], [-1.,-1.], [1., -1.])
}
fn index(&self) -> usize {
match self {
Self::TopLeft => 0,
Self::TopRight => 1,
Self::BottomLeft => 2,
Self::BottomRight => 3,
}
}
}
struct GuiModel {
// A handle to the laser API used for spawning streams and detecting DACs.
@ -98,6 +77,7 @@ struct GuiModel {
canvas_scale: f32,
canvas_translate: Vec2,
canvas_dragging_corner: Option<Corner>,
preview_dragging_point: Option<usize>,
// canvas_transform: Translation2D<f32, ScreenSpace, ScreenSpace>,
// dragging: bool,
}
@ -322,13 +302,15 @@ fn model(app: &App) -> GuiModel {
.view(view_line_canvas)
.build()
.unwrap();
let w_id_laserpreview = app
.new_window()
.size(1024, 1024)
// .key_pressed(key_pressed)
// .mouse_wheel(canvas_zoom)
.mouse_pressed(laser_preview_mouse_pressed)
.mouse_released(laser_mouse_released)
.mouse_moved(laser_mouse_moved)
.view(view_laser_preview)
.build()
.unwrap();
@ -413,6 +395,7 @@ fn model(app: &App) -> GuiModel {
canvas_scale: 25.,
canvas_translate: Vec2::new(-300.,100.),
canvas_dragging_corner: None,
preview_dragging_point: None,
// canvas_transform: Transform2D
// dimming_factor: 1.,
}
@ -935,6 +918,9 @@ fn view_line_canvas(app: &App, model: &GuiModel, frame: Frame) {
draw.to_frame(app, &frame).unwrap();
}
const LASER_PREVIEW_WIDTH: f32 = 1024.;
const LASER_PREVIEW_HEIGHT: f32 = 1024.;
// preview the selected laser, to draw clip mask
fn view_laser_preview(app: &App, model: &GuiModel, frame: Frame) {
// get canvas to draw on
@ -945,8 +931,8 @@ fn view_laser_preview(app: &App, model: &GuiModel, frame: Frame) {
let win = app.window_rect();
let w = 1024.;
let h = 1024.;
let w = LASER_PREVIEW_WIDTH;
let h = LASER_PREVIEW_HEIGHT;
let hh = h / 2.;
let hw = w / 2.;
@ -963,9 +949,12 @@ fn view_laser_preview(app: &App, model: &GuiModel, frame: Frame) {
},
Some(dac_id) => {
// let stream = model.laser_streams.get(&dac_id); //.expect("Selected stream not found in configs");
// 1. get config for laser
let config = model.per_laser_config.get(&dac_id).expect("Selected stream not found in configs");
draw.text(&format!("{:?}", dac_id))
// 2 draw identifier of laser
draw.text(&format!("{} {:?}", config.name, dac_id))
.h(win_rect.h())
.font_size(10)
.align_text_bottom()
@ -973,6 +962,21 @@ fn view_laser_preview(app: &App, model: &GuiModel, frame: Frame) {
.color(WHITE)
.w(win_rect.w());
// 3. clipping mask + its anchor points
let clip_points: Vec<[f32;2]> = Corner::in_laser_space().iter().map(|p| {
[p[0] * hw, p[1] * hh]
}).collect();
draw.polygon()
.color(srgba(1.,1.,1.,0.))
.stroke(PINK)
.stroke_weight(thickness)
.join_round()
.points(clip_points);
// 4. current shape of the laser
let current_points: LaserPoints = (&model.current_lines).into();
let space = &model.current_lines.space;
@ -1001,34 +1005,59 @@ fn view_laser_preview(app: &App, model: &GuiModel, frame: Frame) {
}
}
draw.to_frame(app, &frame).unwrap();
draw.to_frame(app, &frame).unwrap();
}
fn laser_preview_mouse_pressed(app: &App, _model: &mut GuiModel, button: MouseButton) {
fn laser_preview_mouse_pressed(app: &App, model: &mut GuiModel, button: MouseButton) {
let dac_id = match &model.selected_stream {
None => return,
Some(d) => d,
};
if button != MouseButton::Left {
// ignore
// TODO: right click remove point within Margin
return;
}
let half_w = (1024 / 2) as f32;
let half_h = (1024 / 2) as f32;
let half_w = LASER_PREVIEW_WIDTH / 2.;
let half_h = LASER_PREVIEW_HEIGHT / 2.;
let laser_x = app.mouse.x / half_w;
let laser_y = app.mouse.y / half_h;
// if x > 1. || x < -1. || y > 1. || y < -1. {
// println!("Click outside of canvas: {} {}", x, y);
// return
// }
// 1. check if empty. it not check if one point is close
// 2a. if closest point is within selection margin, select that item.
// 2b. if not, if close point is found, if point has neighbours (i.e. len of vec > 1), and find which of neighbours is closest. Insert point between these. Select new point
// 2c. if no closest point is found (thus Vec is empty), create a point under cursor. Select that new point.
let x = app.mouse.x / half_w;
let y = app.mouse.y / half_h;
if x > 1. || x < -1. || y > 1. || y < -1. {
println!("Click outside of canvas: {} {}", x, y);
return
}
}
fn laser_mouse_moved(app: &App, model: &mut GuiModel, pos: Point2) {
let half_w = LASER_PREVIEW_WIDTH / 2.;
let half_h = LASER_PREVIEW_HEIGHT / 2.;
let laser_x = app.mouse.x / half_w;
let laser_y = app.mouse.y / half_h;
// 1. Move selected point to laser_x, laser_y
}
fn laser_mouse_released(_app: &App, model: &mut GuiModel, _button: MouseButton) {
// deselect point
model.preview_dragging_point = None;
}
fn draw_grid(draw: &Draw, win: &Rect, step: f32, weight: f32) {
let step_by = || (0..).map(|i| i as f32 * step);
let r_iter = step_by().take_while(|&f| f < win.right());
@ -1072,8 +1101,6 @@ fn style() -> egui::Style {
style
}
fn mouse_moved(_app: &App, _model: &mut GuiModel, _pos: Point2) {
}
fn laser_corners_to_world(filters: &PointFilters) -> Vec<[f32;2]> {
let corners_raw: Vec<[f32; 2]> = Corner::in_laser_space();
@ -1169,8 +1196,8 @@ fn map_mouse_pressed(app: &App, model: &mut GuiModel, button: MouseButton) {
match i {
0 => selected = Some(Corner::TopLeft),
1 => selected = Some(Corner::TopRight),
2 => selected = Some(Corner::BottomLeft),
3 => selected = Some(Corner::BottomRight),
2 => selected = Some(Corner::BottomRight),
3 => selected = Some(Corner::BottomLeft),
_ => selected = None,
}
break;

View file

@ -1,6 +1,6 @@
use bevy::prelude::*; // for glam::f32::Mat3
use crate::trap::{laser::{apply_homography_matrix, LaserPoints}, tracks::CoordinateSpace};
use crate::trap::{laser::{apply_homography_matrix, Corner, LaserPoints}, tracks::CoordinateSpace};
use nannou_laser::{self as laser, Point};
use serde::{Deserialize, Serialize};
@ -22,6 +22,12 @@ pub struct CropFilter {
pub enabled: bool
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ClipFilter {
pub enabled: bool,
pub mask: Vec<[f32; 2]>
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DimFilter {
pub intensity: f32
@ -59,6 +65,7 @@ pub struct PointFilters{
pub scale: ScaleFilter,
pub pincushion: PincushionFilter,
pub crop: CropFilter,
pub clip: ClipFilter,
}
// list of enums deprecated in favour of struct
@ -128,6 +135,7 @@ impl Default for PointFilters {
scale: ScaleFilter { factor: 1. },
pincushion: PincushionFilter{k_x: 0.,k_x2: 0., k_y: 0., k_y2: 0.},
crop: CropFilter{ enabled: true },
clip: ClipFilter{ enabled: false, mask: Corner::in_laser_space() },
}
}
}
@ -146,8 +154,8 @@ impl Filter for HomographyFilter {
};
// also converts from world space to laser space (origin in center)
let s = 0xFFF as f32 / 2.;
let normalised_pos: [f32;2] = [new_position[0]/s - 1., new_position[1]/s - 1.];
// let s = 0xFFF as f32 / 2.;
// let normalised_pos: [f32;2] = [new_position[0]/s - 1., new_position[1]/s - 1.];
laser::Point {
position: new_position,
.. point.clone()
@ -322,7 +330,7 @@ impl Filter for CropFilter {
}
fn reverse(&self, points: &LaserPoints) -> LaserPoints{
// we cannot really add points, can we
// we cannot really conjure up points, can we
return LaserPoints{
points: points.points.clone(),
space: points.space,
@ -330,6 +338,36 @@ impl Filter for CropFilter {
}
}
impl Filter for ClipFilter {
fn apply(&self, points: &LaserPoints) -> LaserPoints {
if !self.enabled {
// don't modify if disabled
return LaserPoints{
points: points.points.clone(),
space: points.space,
};
}
// TODO
return LaserPoints{
points: points.points.clone(),
space: points.space,
};
}
fn reverse(&self, points: &LaserPoints) -> LaserPoints{
// we cannot really conjure up points, can we
return LaserPoints{
points: points.points.clone(),
space: points.space,
};
}
}
fn change_brightness(points: &LaserPoints, intensity: f32) -> LaserPoints{
let new_points = points.points.iter().map(|point| {
let mut color = point.color.clone();

View file

@ -307,4 +307,27 @@ impl StreamSource{
_ => LaserPoints::default(), // empty set
}
}
}
#[derive(Debug, Clone)]
pub enum Corner{
TopLeft,
TopRight,
BottomRight,
BottomLeft,
}
impl Corner {
pub fn in_laser_space() -> Vec<[f32; 2]>{
vec!([-1.,1.], [1.,1.], [1., -1.], [-1.,-1.])
}
pub fn index(&self) -> usize {
match self {
Self::TopLeft => 0,
Self::TopRight => 1,
Self::BottomRight => 2,
Self::BottomLeft => 3,
}
}
}