added a more robust box selection algorithm that needs testing
This commit is contained in:
parent
bc5d2bc84e
commit
f6b9c50e7e
3 changed files with 83 additions and 35 deletions
|
@ -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}");
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
@ -59,7 +62,7 @@ impl LiveViewPanel {
|
||||||
let move_handler = EventControllerMotion::builder()
|
let move_handler = EventControllerMotion::builder()
|
||||||
.propagation_limit(gtk::PropagationLimit::SameNative)
|
.propagation_limit(gtk::PropagationLimit::SameNative)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let tracker_state_2 = tracker_state.clone();
|
let tracker_state_2 = tracker_state.clone();
|
||||||
|
|
||||||
let handler_picture = webcam_picture.clone();
|
let handler_picture = webcam_picture.clone();
|
||||||
|
@ -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()
|
||||||
|
}
|
|
@ -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: >k::Application, _, _| {
|
.activate(clone!(@weak window => move |app: >k::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);
|
||||||
|
|
Loading…
Reference in a new issue