added a more robust box selection algorithm that needs testing

This commit is contained in:
Nickiel12 2024-05-29 12:38:12 -05:00
parent bc5d2bc84e
commit f6b9c50e7e
3 changed files with 83 additions and 35 deletions

View file

@ -187,7 +187,6 @@ async fn listen_to_messages(
Ok(None) => { Ok(None) => {
info!("Recieved an empty message from the remote computer: Aborting"); info!("Recieved an empty message from the remote computer: Aborting");
break; break;
} }
Err(e) => { Err(e) => {
error!("Got an error on while recieving from remote computer: {e}"); error!("Got an error on while recieving from remote computer: {e}");

View file

@ -1,4 +1,7 @@
use std::sync::{Arc, Mutex}; use std::{
cmp::Ordering,
sync::{Arc, Mutex},
};
use gtk::{ use gtk::{
gdk::Paintable, gdk::Paintable,
@ -6,10 +9,10 @@ use gtk::{
AspectFrame, Box, DrawingArea, EventControllerMotion, GestureClick, Label, Overlay, Picture, AspectFrame, Box, DrawingArea, EventControllerMotion, GestureClick, Label, Overlay, Picture,
}; };
use tracing::info;
use crate::remote_sources::TrackerState; use crate::remote_sources::TrackerState;
use super::NormalizedBoxCoords;
pub struct LiveViewPanel { pub struct LiveViewPanel {
top_level: gtk::Box, top_level: gtk::Box,
@ -110,20 +113,9 @@ impl LiveViewPanel {
let y_coord = y_coord as f32 / y_size as f32; let y_coord = y_coord as f32 / y_size as f32;
if let Ok(mut ts) = tracker_state.lock() { if let Ok(mut ts) = tracker_state.lock() {
let mut select: Option<u32> = None; ts.highlighted_id = calc_box_under_mouse(&ts.identity_boxes, x_coord, y_coord);
for nb in ts.identity_boxes.iter() {
if nb.x1 < x_coord as f32
&& nb.y1 < y_coord as f32
&& nb.x2 > x_coord as f32
&& nb.y2 > y_coord as f32
{
select = Some(nb.id);
}
}
if let Some(new_id) = select {
ts.tracking_id = new_id;
}
} }
} }
fn motion_callback( fn motion_callback(
@ -134,21 +126,13 @@ impl LiveViewPanel {
) { ) {
let x_size = overlay.size(gtk::Orientation::Horizontal); let x_size = overlay.size(gtk::Orientation::Horizontal);
let y_size = overlay.size(gtk::Orientation::Vertical); let y_size = overlay.size(gtk::Orientation::Vertical);
let x_coord = x_coord as f32 / x_size as f32; let x_coord = x_coord as f32 / x_size as f32;
let y_coord = y_coord as f32 / y_size as f32; let y_coord = y_coord as f32 / y_size as f32;
if let Ok(mut ts) = tracker_state.lock() { if let Ok(mut ts) = tracker_state.lock() {
let mut highlighted_id: Option<u32> = None; if let Some(v) = calc_box_under_mouse(&ts.identity_boxes, x_coord, y_coord) {
for nb in ts.identity_boxes.iter() { ts.tracking_id = v;
if nb.x1 < x_coord as f32
&& nb.y1 < y_coord as f32
&& nb.x2 > x_coord as f32
&& nb.y2 > y_coord as f32
{
highlighted_id = Some(nb.id);
}
} }
ts.highlighted_id = highlighted_id;
} }
} }
@ -164,3 +148,65 @@ impl LiveViewPanel {
self.picture.set_paintable(Some(new_paintable)); self.picture.set_paintable(Some(new_paintable));
} }
} }
fn calc_box_under_mouse(boxes: &Vec<NormalizedBoxCoords>, x_coord: f32, y_coord: f32) -> Option<u32> {
let mut mouse_over: Vec<NormalizedBoxCoords> = vec![];
for nb in boxes.iter() {
if nb.x1 < x_coord as f32
&& nb.y1 < y_coord as f32
&& nb.x2 > x_coord as f32
&& nb.y2 > y_coord as f32
{
mouse_over.push(nb.clone());
}
}
if mouse_over.len() == 1 {
// the length is one, why would the unwrap fail
let new_id = mouse_over.first().expect("Length one vec had no first");
return Some(new_id.id);
} else if mouse_over.len() > 1 {
let mut x_coords = mouse_over
.iter()
.flat_map(|b| [b.x1, b.x2])
.collect::<Vec<f32>>();
x_coords.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mid = x_coords.len() / 2;
let median_two_x = &x_coords[mid - 1..mid + 1];
let mut y_coords = mouse_over
.iter()
.flat_map(|b| [b.y1, b.y2])
.collect::<Vec<f32>>();
y_coords.sort_by(|a, b| a.partial_cmp(b).unwrap());
let median_two_y = &y_coords[mid - 1..mid + 1];
let overlap_area =
(median_two_x[1] - median_two_x[0]) * (median_two_y[1] - median_two_y[0]);
mouse_over.sort_by(|a, b| {
// a is valid for cutting in line if the
// overlap area is greater than 75% of a's area
let a_overlap = overlap_area >= 0.75 * a.area();
let b_overlap = overlap_area >= 0.75 * b.area();
match (a_overlap, b_overlap) {
// if the areas are not valid for cutting in line or both are
// do the sorting based on the id, and let the hover affect
// force the user to decide
(true, true) | (false, false) => {
if a.id > b.id {
Ordering::Greater
} else {
Ordering::Less
}
}
(false, true) => Ordering::Less,
(true, false) => Ordering::Greater,
}
});
}
mouse_over.iter().map(|x| x.id).collect::<Vec<u32>>().first().copied()
}

View file

@ -30,7 +30,7 @@ pub enum GuiUpdate {
UpdatePaintable(gstreamer::Element), UpdatePaintable(gstreamer::Element),
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct BoxCoords { pub struct BoxCoords {
pub id: u32, pub id: u32,
pub x1: u32, pub x1: u32,
@ -49,7 +49,7 @@ impl Display for BoxCoords {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct NormalizedBoxCoords { pub struct NormalizedBoxCoords {
pub id: u32, pub id: u32,
pub x1: f32, pub x1: f32,
@ -68,6 +68,10 @@ impl NormalizedBoxCoords {
y2: (self.y2 * height as f32) as u32, y2: (self.y2 * height as f32) as u32,
} }
} }
fn area(&self) -> f32 {
(self.x2 - self.x1) * (self.y2 - self.y1)
}
} }
impl Display for NormalizedBoxCoords { impl Display for NormalizedBoxCoords {
@ -101,7 +105,7 @@ pub fn on_activate(app: &Application) {
}; };
let menubar = gio::Menu::new(); let menubar = gio::Menu::new();
menubar.append_submenu(Some("Connections"), &connections_menu); menubar.append_submenu(Some("Settings"), &connections_menu);
menubar menubar
}; };
@ -128,10 +132,10 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
.build(); .build();
let rt = runtime.clone(); let rt = runtime.clone();
let config_modal_config = config.clone(); let connections_modal_config = config.clone();
let connections_activate = gio::ActionEntry::builder("connections") let connections_activate = gio::ActionEntry::builder("connections")
.activate(clone!(@weak window => move |app: &gtk::Application, _, _| { .activate(clone!(@weak window => move |app: &gtk::Application, _, _| {
let connections_modal = settings_modal::ConnectionsModal::new(&app, &window, &rt, &config_modal_config); let connections_modal = settings_modal::ConnectionsModal::new(&app, &window, &rt, &connections_modal_config);
connections_modal.window.present(); connections_modal.window.present();
})) }))
@ -169,7 +173,6 @@ pub fn build_ui(app: &Application, config: Arc<RwLock<AppConfig>>, runtime: Hand
let control_panel = ControlPanel::new(tracker_state.clone()); let control_panel = ControlPanel::new(tracker_state.clone());
control_panel.connect_button_callbacks(to_mec.clone()); control_panel.connect_button_callbacks(to_mec.clone());
// left_box.append(&conn_status_label);
left_box.append(control_panel.get_top_level()); left_box.append(control_panel.get_top_level());
main_box.append(&left_box); main_box.append(&left_box);