From bcc0e413a06a88edd3ce0dfb270701a42b5ab472 Mon Sep 17 00:00:00 2001 From: blackarch-lenovo Date: Sat, 8 Jul 2023 18:27:00 +0200 Subject: [PATCH 01/10] Added direction.rs Added Direction enum: * North; * East; * South; * West. This enum is used to search the neighbor Rect in a given direction. Added functions: * find_north: find the north neighbor; * find_east: find the east neighbor; * find_south: find the south neighbor; * find_west: find the west neighbor; * find_neighbor: wrapper for the above functions. This commit is related to the leftwm issue "Directional Focus and Window movement (771)". --- leftwm-layouts/src/geometry/direction.rs | 297 +++++++++++++++++++++++ leftwm-layouts/src/geometry/mod.rs | 2 + 2 files changed, 299 insertions(+) create mode 100644 leftwm-layouts/src/geometry/direction.rs diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs new file mode 100644 index 0000000..ec13290 --- /dev/null +++ b/leftwm-layouts/src/geometry/direction.rs @@ -0,0 +1,297 @@ +use super::Rect; + +/// Represents the four different direction where we can search for a neighbor +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub enum Direction { + #[default] + /// Search for neighbor starting from the top left of the current rect + /// This is the default value. + /// + /// ```txt + /// North + /// +---------+ + /// |^ | + /// | | + /// | | + /// +---------+ + /// ``` + North, + + /// Search for neighbor starting from the right top of the current rect + /// + /// ```txt + /// East + /// +---------+ + /// | >| + /// | | + /// | | + /// +---------+ + /// ``` + East, + + /// Search for neighbor starting from the bottom left of the current rect + /// + /// ```txt + /// South + /// +---------+ + /// | | + /// | | + /// |v | + /// +---------+ + /// ``` + South, + + /// Search for neighbor starting from the left top of the current rect + /// + /// ```txt + /// West + /// +---------+ + /// |< | + /// | | + /// | | + /// +---------+ + /// ``` + West, +} + +// Find the north neighbor starting from a given `Rect` with index `current` in an array of +// [`Rect`]. +fn find_north(rects: &[Rect], current: usize) -> Option { + let current_rect = rects.get(current).or_else(|| return None).unwrap(); + // We are all the way up, no neighbor available + if current_rect.y == 0 { + return None; + } + + for x in current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1 { + for y in (0..=current_rect.y + 1).rev() { + for r in 0..rects.len() { + if r != current && rects[r].contains((x, y)) { + // found north neighbor + return Some(r); + } + } + } + } + return None; +} + +// Find the east neighbor starting from a given `Rect` with index `current` in an array of +// [`Rect`]. +fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option { + let current_rect = rects.get(current).or_else(|| return None).unwrap(); + + // We are all the way right, no neighbor available + if current_rect.x + current_rect.w as i32 >= display_width as i32 { + return None; + } + + for y in current_rect.y + 1..=current_rect.y + current_rect.h as i32 - 1 { + for x in current_rect.x + current_rect.w as i32 + 1..=display_width as i32 { + for r in 0..rects.len() { + if r != current && rects[r].contains((x, y)) { + // found east neighbor + return Some(r); + } + } + } + } + return None; +} + +// Find the south neighbor starting from a given `Rect` with index `current` in an array of +// [`Rect`]. +fn find_south(rects: &[Rect], current: usize, display_height: u32) -> Option { + let current_rect = rects.get(current).or_else(|| return None).unwrap(); + + // We are at the bottom, no neighbor available + if current_rect.y + current_rect.h as i32 >= display_height as i32 { + return None; + } + + for x in current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1 { + for y in current_rect.y + current_rect.h as i32..=display_height as i32 { + for r in 0..rects.len() { + if r != current && rects[r].contains((x, y)) { + // found south neighbor + return Some(r); + } + } + } + } + + return None; +} + +// Find the west neighbor starting from a given `Rect` with index `current` in an array of +// [`Rect`]. +fn find_west(rects: &[Rect], current: usize) -> Option { + let current_rect = rects.get(current).or_else(|| return None).unwrap(); + + // We are all the way left; no neighbor available + if current_rect.x <= 0 { + return None; + } + + for x in (0..=current_rect.x - 1).rev() { + for y in current_rect.y + 1..=current_rect.y + current_rect.h as i32 - 1 { + for r in 0..rects.len() { + if r != current && rects[r].contains((x, y)) { + return Some(r); + } + } + } + } + return None; +} + +impl Direction { + /// Find the neighbor in a given direction (`North`, `East`, `South`, `West`), starting from a + /// given `Rect` identified by the index `current` in an array of [`Rect`] + pub fn find_neighbor( + rects: &[Rect], + current: usize, + direction: Direction, + container: &Rect, + ) -> Option { + if current >= rects.len() { + return None; + } + + let ret = match direction { + Direction::North => find_north(rects, current), + Direction::East => find_east(rects, current, container.w), + Direction::South => find_south(rects, current, container.h), + Direction::West => find_west(rects, current), + }; + + return ret; + } +} + +#[cfg(test)] +mod tests { + use crate::geometry::{Direction, Rect}; + + const CONTAINER: Rect = Rect { + x: 0, + y: 0, + w: 600, + h: 600, + }; + + // Test layout + // +-----------------+ + // |+---+ +---+ +---+| + // || 0 | | 3 | | 4 || + // |+---+ +---+ +---+| + // |+---+ +---+| + // || 1 | | || + // |+---+ | || + // |+---+ | 5 || + // || 2 | | || + // |+---+ +---+| + // +-----------------+ + const ARRAY: [Rect; 6] = [ + Rect { + x: 0, + y: 0, + w: 200, + h: 200, + }, + Rect { + x: 0, + y: 200, + w: 200, + h: 200, + }, + Rect { + x: 0, + y: 400, + w: 200, + h: 200, + }, + Rect { + x: 200, + y: 0, + w: 200, + h: 200, + }, + Rect { + x: 400, + y: 0, + w: 200, + h: 200, + }, + Rect { + x: 400, + y: 200, + w: 200, + h: 400, + }, + ]; + + #[test] + fn north_neighbor() { + let res = Direction::find_neighbor(&ARRAY, 0, Direction::North, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 1, Direction::North, &CONTAINER); + assert_eq!(res, Some(0)); + let res = Direction::find_neighbor(&ARRAY, 2, Direction::North, &CONTAINER); + assert_eq!(res, Some(1)); + let res = Direction::find_neighbor(&ARRAY, 3, Direction::North, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 4, Direction::North, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 5, Direction::North, &CONTAINER); + assert_eq!(res, Some(4)); + } + + #[test] + fn east_neighbor() { + let res = Direction::find_neighbor(&ARRAY, 0, Direction::East, &CONTAINER); + assert_eq!(res, Some(3)); + let res = Direction::find_neighbor(&ARRAY, 1, Direction::East, &CONTAINER); + assert_eq!(res, Some(5)); + let res = Direction::find_neighbor(&ARRAY, 2, Direction::East, &CONTAINER); + assert_eq!(res, Some(5)); + let res = Direction::find_neighbor(&ARRAY, 3, Direction::East, &CONTAINER); + assert_eq!(res, Some(4)); + let res = Direction::find_neighbor(&ARRAY, 4, Direction::East, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 5, Direction::East, &CONTAINER); + assert_eq!(res, None); + } + + #[test] + fn south_neighbor() { + let res = Direction::find_neighbor(&ARRAY, 0, Direction::South, &CONTAINER); + assert_eq!(res, Some(1)); + let res = Direction::find_neighbor(&ARRAY, 1, Direction::South, &CONTAINER); + assert_eq!(res, Some(2)); + let res = Direction::find_neighbor(&ARRAY, 2, Direction::South, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 3, Direction::South, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 4, Direction::South, &CONTAINER); + assert_eq!(res, Some(5)); + let res = Direction::find_neighbor(&ARRAY, 5, Direction::South, &CONTAINER); + assert_eq!(res, None); + } + + #[test] + fn west_neighbor() { + let res = Direction::find_neighbor(&ARRAY, 0, Direction::West, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 1, Direction::West, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 2, Direction::West, &CONTAINER); + assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 3, Direction::West, &CONTAINER); + assert_eq!(res, Some(0)); + let res = Direction::find_neighbor(&ARRAY, 4, Direction::West, &CONTAINER); + assert_eq!(res, Some(3)); + let res = Direction::find_neighbor(&ARRAY, 5, Direction::West, &CONTAINER); + assert_eq!(res, Some(1)); + } +} diff --git a/leftwm-layouts/src/geometry/mod.rs b/leftwm-layouts/src/geometry/mod.rs index 51574dc..62b52cb 100644 --- a/leftwm-layouts/src/geometry/mod.rs +++ b/leftwm-layouts/src/geometry/mod.rs @@ -1,4 +1,5 @@ mod calc; +mod direction; mod flip; mod rect; mod reserve; @@ -7,6 +8,7 @@ mod size; mod split; pub use calc::{divrem, flip, remainderless_division, rotate, split}; +pub use direction::Direction; pub use flip::Flip; pub use rect::Rect; pub use reserve::Reserve; From 8c99eb0d3aa4b033a3cea13e641749e3a9513da3 Mon Sep 17 00:00:00 2001 From: blackarch-lenovo Date: Sat, 8 Jul 2023 21:52:09 +0200 Subject: [PATCH 02/10] direction: fix clippy suggestions --- leftwm-layouts/src/geometry/direction.rs | 46 ++++++++++++------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs index ec13290..a9ca7e1 100644 --- a/leftwm-layouts/src/geometry/direction.rs +++ b/leftwm-layouts/src/geometry/direction.rs @@ -57,7 +57,7 @@ pub enum Direction { // Find the north neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_north(rects: &[Rect], current: usize) -> Option { - let current_rect = rects.get(current).or_else(|| return None).unwrap(); + let current_rect = rects.get(current).or(None).unwrap(); // We are all the way up, no neighbor available if current_rect.y == 0 { return None; @@ -65,21 +65,21 @@ fn find_north(rects: &[Rect], current: usize) -> Option { for x in current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1 { for y in (0..=current_rect.y + 1).rev() { - for r in 0..rects.len() { - if r != current && rects[r].contains((x, y)) { + for (i, r) in rects.iter().enumerate() { + if i != current && r.contains((x, y)) { // found north neighbor - return Some(r); + return Some(i); } } } } - return None; + None } // Find the east neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option { - let current_rect = rects.get(current).or_else(|| return None).unwrap(); + let current_rect = rects.get(current).or(None).unwrap(); // We are all the way right, no neighbor available if current_rect.x + current_rect.w as i32 >= display_width as i32 { @@ -88,21 +88,21 @@ fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option Option { - let current_rect = rects.get(current).or_else(|| return None).unwrap(); + let current_rect = rects.get(current).or( None).unwrap(); // We are at the bottom, no neighbor available if current_rect.y + current_rect.h as i32 >= display_height as i32 { @@ -111,22 +111,22 @@ fn find_south(rects: &[Rect], current: usize, display_height: u32) -> Option Option { - let current_rect = rects.get(current).or_else(|| return None).unwrap(); + let current_rect = rects.get(current).or(None).unwrap(); // We are all the way left; no neighbor available if current_rect.x <= 0 { @@ -135,14 +135,14 @@ fn find_west(rects: &[Rect], current: usize) -> Option { for x in (0..=current_rect.x - 1).rev() { for y in current_rect.y + 1..=current_rect.y + current_rect.h as i32 - 1 { - for r in 0..rects.len() { - if r != current && rects[r].contains((x, y)) { - return Some(r); + for (i, r) in rects.iter().enumerate() { + if i != current && r.contains((x, y)) { + return Some(i); } } } } - return None; + None } impl Direction { @@ -158,14 +158,12 @@ impl Direction { return None; } - let ret = match direction { + match direction { Direction::North => find_north(rects, current), Direction::East => find_east(rects, current, container.w), Direction::South => find_south(rects, current, container.h), Direction::West => find_west(rects, current), - }; - - return ret; + } } } From 6ff9ac07c0738ea29be02c2d492fe75f6607a7de Mon Sep 17 00:00:00 2001 From: VuiMuich Date: Sat, 8 Jul 2023 23:04:09 +0200 Subject: [PATCH 03/10] some more clippies --- leftwm-layouts/src/geometry/direction.rs | 12 ++++++------ leftwm-layouts/src/geometry/rect.rs | 4 ++-- leftwm-layouts/src/geometry/rotation.rs | 2 +- leftwm-layouts/src/geometry/size.rs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs index a9ca7e1..0acd62f 100644 --- a/leftwm-layouts/src/geometry/direction.rs +++ b/leftwm-layouts/src/geometry/direction.rs @@ -63,7 +63,7 @@ fn find_north(rects: &[Rect], current: usize) -> Option { return None; } - for x in current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1 { + for x in (current_rect.x + 1)..(current_rect.x + current_rect.w as i32) { for y in (0..=current_rect.y + 1).rev() { for (i, r) in rects.iter().enumerate() { if i != current && r.contains((x, y)) { @@ -86,7 +86,7 @@ fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option Option Option { - let current_rect = rects.get(current).or( None).unwrap(); + let current_rect = rects.get(current).or(None).unwrap(); // We are at the bottom, no neighbor available if current_rect.y + current_rect.h as i32 >= display_height as i32 { return None; } - for x in current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1 { + for x in (current_rect.x + 1)..(current_rect.x + current_rect.w as i32) { for y in current_rect.y + current_rect.h as i32..=display_height as i32 { for (i, r) in rects.iter().enumerate() { if i != current && r.contains((x, y)) { @@ -133,8 +133,8 @@ fn find_west(rects: &[Rect], current: usize) -> Option { return None; } - for x in (0..=current_rect.x - 1).rev() { - for y in current_rect.y + 1..=current_rect.y + current_rect.h as i32 - 1 { + for x in (0..current_rect.x).rev() { + for y in (current_rect.y + 1)..(current_rect.y + current_rect.h as i32) { for (i, r) in rects.iter().enumerate() { if i != current && r.contains((x, y)) { return Some(i); diff --git a/leftwm-layouts/src/geometry/rect.rs b/leftwm-layouts/src/geometry/rect.rs index c3224a9..d0d5d8f 100644 --- a/leftwm-layouts/src/geometry/rect.rs +++ b/leftwm-layouts/src/geometry/rect.rs @@ -78,7 +78,7 @@ mod tests { #[test] fn surface_area_calculation() { let rect = Rect::new(0, 0, 1920, 1080); - assert_eq!(rect.surface_area(), 2073600); + assert_eq!(rect.surface_area(), 2_073_600); } #[test] @@ -102,7 +102,7 @@ mod tests { #[test] fn center_calculation_at_rounded_position() { let rect = Rect::new(100, 100, 387, 399); - assert_eq!(rect.center(), (294, 300)) + assert_eq!(rect.center(), (294, 300)); } #[test] diff --git a/leftwm-layouts/src/geometry/rotation.rs b/leftwm-layouts/src/geometry/rotation.rs index 65792d5..5de1458 100644 --- a/leftwm-layouts/src/geometry/rotation.rs +++ b/leftwm-layouts/src/geometry/rotation.rs @@ -141,7 +141,7 @@ mod tests { Rotation::West, ]; for rotation in rotations { - assert!(!rotation.aspect_ratio_changes(&SQUARE)) + assert!(!rotation.aspect_ratio_changes(&SQUARE)); } } diff --git a/leftwm-layouts/src/geometry/size.rs b/leftwm-layouts/src/geometry/size.rs index e959161..fcada89 100644 --- a/leftwm-layouts/src/geometry/size.rs +++ b/leftwm-layouts/src/geometry/size.rs @@ -39,7 +39,7 @@ mod tests { let i = 256i32; let size = Size::Pixel(i); let absolute = size.into_absolute(1000); - assert_eq!(absolute, i) + assert_eq!(absolute, i); } #[test] From 38ff9f663087eba6fbd6c16651c2b1f59bd6a9d7 Mon Sep 17 00:00:00 2001 From: blackarch-lenovo Date: Sun, 16 Jul 2023 14:49:42 +0200 Subject: [PATCH 04/10] direction: fix doc ascii art --- leftwm-layouts/src/geometry/direction.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs index a9ca7e1..3282ac9 100644 --- a/leftwm-layouts/src/geometry/direction.rs +++ b/leftwm-layouts/src/geometry/direction.rs @@ -9,8 +9,10 @@ pub enum Direction { /// /// ```txt /// North + /// ^ + /// | /// +---------+ - /// |^ | + /// | | /// | | /// | | /// +---------+ @@ -21,8 +23,9 @@ pub enum Direction { /// /// ```txt /// East + /// -> /// +---------+ - /// | >| + /// | | /// | | /// | | /// +---------+ @@ -36,8 +39,10 @@ pub enum Direction { /// +---------+ /// | | /// | | - /// |v | + /// | | /// +---------+ + /// | + /// V /// ``` South, @@ -45,8 +50,9 @@ pub enum Direction { /// /// ```txt /// West + /// <- /// +---------+ - /// |< | + /// | | /// | | /// | | /// +---------+ From dd67ffdb16034a41fe9c94902085ed19822ddf2f Mon Sep 17 00:00:00 2001 From: blackarch-lenovo Date: Sun, 16 Jul 2023 16:11:02 +0200 Subject: [PATCH 05/10] direction: early return + generalized function --- leftwm-layouts/src/geometry/direction.rs | 100 ++++++++++++----------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs index 3282ac9..1af1beb 100644 --- a/leftwm-layouts/src/geometry/direction.rs +++ b/leftwm-layouts/src/geometry/direction.rs @@ -60,95 +60,99 @@ pub enum Direction { West, } +fn search_loops(first_loop: Vec, second_loop: Vec, rects: &[Rect], inverted: bool) -> Option { + for f in first_loop.iter() { + for s in second_loop.iter() { + for (i, r) in rects.iter().enumerate() { + if inverted { + if r.contains((*s, *f)) { + return Some(i); + } + } else { + if r.contains((*f, *s)) { + return Some(i); + } + } + } + } + } + None +} + // Find the north neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_north(rects: &[Rect], current: usize) -> Option { - let current_rect = rects.get(current).or(None).unwrap(); + let current_rect = match rects.get(current).or(None) { + Some(c) => c, + None => return None, + }; + // We are all the way up, no neighbor available - if current_rect.y == 0 { + if current_rect.y <= 0 { return None; } - for x in current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1 { - for y in (0..=current_rect.y + 1).rev() { - for (i, r) in rects.iter().enumerate() { - if i != current && r.contains((x, y)) { - // found north neighbor - return Some(i); - } - } - } - } - None + let first_loop = (current_rect.x + 1..current_rect.x + current_rect.w as i32 - 1).collect(); + let second_loop = (0..=current_rect.y as i32 - 1).rev().collect(); + + return search_loops(first_loop, second_loop, rects, false); } // Find the east neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option { - let current_rect = rects.get(current).or(None).unwrap(); + let current_rect = match rects.get(current).or(None) { + Some(c) => c, + None => return None, + }; // We are all the way right, no neighbor available if current_rect.x + current_rect.w as i32 >= display_width as i32 { return None; } - for y in current_rect.y + 1..=current_rect.y + current_rect.h as i32 - 1 { - for x in current_rect.x + current_rect.w as i32 + 1..=display_width as i32 { - for (i, r) in rects.iter().enumerate() { - if i != current && r.contains((x, y)) { - // found east neighbor - return Some(i); - } - } - } - } - None + let first_loop = (current_rect.y + 1..=current_rect.y + current_rect.h as i32 - 1).collect(); + let second_loop = (current_rect.x + current_rect.w as i32 + 1..=display_width as i32).collect(); + + return search_loops(first_loop, second_loop, rects, true); } // Find the south neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_south(rects: &[Rect], current: usize, display_height: u32) -> Option { - let current_rect = rects.get(current).or( None).unwrap(); + let current_rect = match rects.get(current).or(None) { + Some(c) => c, + None => return None, + }; // We are at the bottom, no neighbor available if current_rect.y + current_rect.h as i32 >= display_height as i32 { return None; } - for x in current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1 { - for y in current_rect.y + current_rect.h as i32..=display_height as i32 { - for (i, r) in rects.iter().enumerate() { - if i != current && r.contains((x, y)) { - // found south neighbor - return Some(i); - } - } - } - } + let first_loop = (current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1).collect(); + let second_loop = (current_rect.y + current_rect.h as i32 + 1..=display_height as i32).collect(); - None + return search_loops(first_loop, second_loop, rects, false); } // Find the west neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_west(rects: &[Rect], current: usize) -> Option { - let current_rect = rects.get(current).or(None).unwrap(); + let current_rect = match rects.get(current).or(None) { + Some(c) => c, + None => return None, + }; // We are all the way left; no neighbor available if current_rect.x <= 0 { return None; } - for x in (0..=current_rect.x - 1).rev() { - for y in current_rect.y + 1..=current_rect.y + current_rect.h as i32 - 1 { - for (i, r) in rects.iter().enumerate() { - if i != current && r.contains((x, y)) { - return Some(i); - } - } - } - } - None + let first_loop = (0..=current_rect.x + - 1).rev().collect(); + let second_loop = (current_rect.y + 1 as i32 + 1..=current_rect.y + current_rect.h as i32 -1).collect(); + + return search_loops(first_loop, second_loop, rects, false); } impl Direction { From 394f12560cff64d10f4c1d5178318866aa8c35f7 Mon Sep 17 00:00:00 2001 From: blackarch-lenovo Date: Sun, 16 Jul 2023 16:33:24 +0200 Subject: [PATCH 06/10] direction: fix clippy + fmt --- leftwm-layouts/src/geometry/direction.rs | 58 ++++++++++-------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs index 1af1beb..beeaef7 100644 --- a/leftwm-layouts/src/geometry/direction.rs +++ b/leftwm-layouts/src/geometry/direction.rs @@ -60,18 +60,21 @@ pub enum Direction { West, } -fn search_loops(first_loop: Vec, second_loop: Vec, rects: &[Rect], inverted: bool) -> Option { - for f in first_loop.iter() { - for s in second_loop.iter() { +fn search_loops( + first_loop: &Vec, + second_loop: &Vec, + rects: &[Rect], + inverted: bool, +) -> Option { + for f in first_loop { + for s in second_loop { for (i, r) in rects.iter().enumerate() { if inverted { if r.contains((*s, *f)) { return Some(i); } - } else { - if r.contains((*f, *s)) { - return Some(i); - } + } else if r.contains((*f, *s)) { + return Some(i); } } } @@ -82,10 +85,7 @@ fn search_loops(first_loop: Vec, second_loop: Vec, rects: &[Rect], inv // Find the north neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_north(rects: &[Rect], current: usize) -> Option { - let current_rect = match rects.get(current).or(None) { - Some(c) => c, - None => return None, - }; + let Some(current_rect) = rects.get(current).or(None) else { return None }; // We are all the way up, no neighbor available if current_rect.y <= 0 { @@ -93,66 +93,58 @@ fn find_north(rects: &[Rect], current: usize) -> Option { } let first_loop = (current_rect.x + 1..current_rect.x + current_rect.w as i32 - 1).collect(); - let second_loop = (0..=current_rect.y as i32 - 1).rev().collect(); + let second_loop = (0..current_rect.y).rev().collect(); - return search_loops(first_loop, second_loop, rects, false); + search_loops(&first_loop, &second_loop, rects, false) } // Find the east neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option { - let current_rect = match rects.get(current).or(None) { - Some(c) => c, - None => return None, - }; + let Some(current_rect) = rects.get(current).or(None) else { return None }; // We are all the way right, no neighbor available if current_rect.x + current_rect.w as i32 >= display_width as i32 { return None; } - let first_loop = (current_rect.y + 1..=current_rect.y + current_rect.h as i32 - 1).collect(); + let first_loop = (current_rect.y + 1..current_rect.y + current_rect.h as i32).collect(); let second_loop = (current_rect.x + current_rect.w as i32 + 1..=display_width as i32).collect(); - return search_loops(first_loop, second_loop, rects, true); + search_loops(&first_loop, &second_loop, rects, true) } // Find the south neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_south(rects: &[Rect], current: usize, display_height: u32) -> Option { - let current_rect = match rects.get(current).or(None) { - Some(c) => c, - None => return None, - }; + let Some(current_rect) = rects.get(current).or(None) else { return None }; // We are at the bottom, no neighbor available if current_rect.y + current_rect.h as i32 >= display_height as i32 { return None; } - let first_loop = (current_rect.x + 1..=current_rect.x + current_rect.w as i32 - 1).collect(); - let second_loop = (current_rect.y + current_rect.h as i32 + 1..=display_height as i32).collect(); + let first_loop = (current_rect.x + 1..current_rect.x + current_rect.w as i32).collect(); + let second_loop = + (current_rect.y + current_rect.h as i32 + 1..=display_height as i32).collect(); - return search_loops(first_loop, second_loop, rects, false); + search_loops(&first_loop, &second_loop, rects, false) } // Find the west neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_west(rects: &[Rect], current: usize) -> Option { - let current_rect = match rects.get(current).or(None) { - Some(c) => c, - None => return None, - }; + let Some(current_rect) = rects.get(current).or(None) else { return None }; // We are all the way left; no neighbor available if current_rect.x <= 0 { return None; } - let first_loop = (0..=current_rect.x + - 1).rev().collect(); - let second_loop = (current_rect.y + 1 as i32 + 1..=current_rect.y + current_rect.h as i32 -1).collect(); + let first_loop = (0..=current_rect.x + -1).rev().collect(); + let second_loop = (current_rect.y + 1..current_rect.y + current_rect.h as i32).collect(); - return search_loops(first_loop, second_loop, rects, false); + search_loops(&first_loop, &second_loop, rects, false) } impl Direction { From e56388d3694e43da9caa641c4fe1f8b91ea391f5 Mon Sep 17 00:00:00 2001 From: blackarch-lenovo Date: Mon, 17 Jul 2023 20:52:55 +0200 Subject: [PATCH 07/10] direction: fix doc + renaming --- leftwm-layouts/src/geometry/direction.rs | 86 ++++++++++++------------ 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs index beeaef7..25b319c 100644 --- a/leftwm-layouts/src/geometry/direction.rs +++ b/leftwm-layouts/src/geometry/direction.rs @@ -9,10 +9,9 @@ pub enum Direction { /// /// ```txt /// North - /// ^ - /// | + /// /// +---------+ - /// | | + /// | ^ | /// | | /// | | /// +---------+ @@ -23,9 +22,9 @@ pub enum Direction { /// /// ```txt /// East - /// -> + /// /// +---------+ - /// | | + /// | > | /// | | /// | | /// +---------+ @@ -39,10 +38,9 @@ pub enum Direction { /// +---------+ /// | | /// | | - /// | | + /// | V | /// +---------+ - /// | - /// V + /// /// ``` South, @@ -50,9 +48,9 @@ pub enum Direction { /// /// ```txt /// West - /// <- + /// /// +---------+ - /// | | + /// | < | /// | | /// | | /// +---------+ @@ -60,28 +58,6 @@ pub enum Direction { West, } -fn search_loops( - first_loop: &Vec, - second_loop: &Vec, - rects: &[Rect], - inverted: bool, -) -> Option { - for f in first_loop { - for s in second_loop { - for (i, r) in rects.iter().enumerate() { - if inverted { - if r.contains((*s, *f)) { - return Some(i); - } - } else if r.contains((*f, *s)) { - return Some(i); - } - } - } - } - None -} - // Find the north neighbor starting from a given `Rect` with index `current` in an array of // [`Rect`]. fn find_north(rects: &[Rect], current: usize) -> Option { @@ -92,10 +68,10 @@ fn find_north(rects: &[Rect], current: usize) -> Option { return None; } - let first_loop = (current_rect.x + 1..current_rect.x + current_rect.w as i32 - 1).collect(); - let second_loop = (0..current_rect.y).rev().collect(); + let right_point = (current_rect.x + 1..current_rect.x + current_rect.w as i32 - 1).collect(); + let up_points = (0..current_rect.y).rev().collect(); - search_loops(&first_loop, &second_loop, rects, false) + search_nearest_neighbor(&right_point, &up_points, rects, false) } // Find the east neighbor starting from a given `Rect` with index `current` in an array of @@ -108,10 +84,10 @@ fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option Option Option { return None; } - let first_loop = (0..=current_rect.x + -1).rev().collect(); - let second_loop = (current_rect.y + 1..current_rect.y + current_rect.h as i32).collect(); + let left_points = (0..=current_rect.x + -1).rev().collect(); + let down_points = (current_rect.y + 1..current_rect.y + current_rect.h as i32).collect(); + + search_nearest_neighbor(&left_points, &down_points, rects, false) +} - search_loops(&first_loop, &second_loop, rects, false) +fn search_nearest_neighbor( + first_direction: &Vec, + second_direction: &Vec, + rects: &[Rect], + inverted: bool, +) -> Option { + for f in first_direction { + for s in second_direction { + for (i, r) in rects.iter().enumerate() { + if inverted { + if r.contains((*s, *f)) { + return Some(i); + } + } else if r.contains((*f, *s)) { + return Some(i); + } + } + } + } + None } impl Direction { From 1a3442bfb782f7196f85a2e3aff0b772afcde5af Mon Sep 17 00:00:00 2001 From: Mariano Marciello <32301730+marianomarciello@users.noreply.github.com> Date: Mon, 17 Jul 2023 22:07:21 +0200 Subject: [PATCH 08/10] fix typo Co-authored-by: VuiMuich --- leftwm-layouts/src/geometry/direction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs index 25b319c..948ab89 100644 --- a/leftwm-layouts/src/geometry/direction.rs +++ b/leftwm-layouts/src/geometry/direction.rs @@ -68,7 +68,7 @@ fn find_north(rects: &[Rect], current: usize) -> Option { return None; } - let right_point = (current_rect.x + 1..current_rect.x + current_rect.w as i32 - 1).collect(); + let right_points = (current_rect.x + 1..current_rect.x + current_rect.w as i32 - 1).collect(); let up_points = (0..current_rect.y).rev().collect(); search_nearest_neighbor(&right_point, &up_points, rects, false) From d0b085db3628e0cde80d23ff5339c533a70ec1b2 Mon Sep 17 00:00:00 2001 From: blackarch-lenovo Date: Thu, 20 Jul 2023 19:52:40 +0200 Subject: [PATCH 09/10] rects: added helper methods --- leftwm-layouts/src/geometry/rect.rs | 108 ++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/leftwm-layouts/src/geometry/rect.rs b/leftwm-layouts/src/geometry/rect.rs index d0d5d8f..d76b530 100644 --- a/leftwm-layouts/src/geometry/rect.rs +++ b/leftwm-layouts/src/geometry/rect.rs @@ -58,6 +58,114 @@ impl Rect { && self.y <= point.1 && point.1 <= self.y + self.h as i32 } + + /// Get the top left corner point of the [`Rect`]. + /// + /// ```txt + /// O---------+ + /// | | + /// | | + /// | | + /// +---------+ + /// ``` + pub fn top_left_corner(&self) -> (i32, i32) { + (self.x, self.y) + } + + /// Get the top right corner point of the [`Rect`]. + /// + /// ```txt + /// +---------O + /// | | + /// | | + /// | | + /// +---------+ + /// ``` + pub fn top_right_corner(&self) -> (i32, i32) { + (self.x + self.w as i32, self.y) + } + + /// Get the bottom right corner point of the [`Rect`]. + /// + /// ```txt + /// +---------+ + /// | | + /// | | + /// | | + /// +---------O + /// ``` + pub fn bottom_right_corner(&self) -> (i32, i32) { + (self.x + self.w as i32, self.y + self.h as i32) + } + + /// Get the bottom left corner point of the [`Rect`]. + /// + /// ```txt + /// +---------+ + /// | | + /// | | + /// | | + /// O---------+ + /// ``` + pub fn bottom_left_corner(&self) -> (i32, i32) { + (self.x, self.y + self.h as i32) + } + + /// Get the top edge of the [`Rect`]. + /// + /// ```txt + /// ^ + /// | + /// V + /// +---------+ + /// | | + /// | | + /// | | + /// +---------+ + /// ``` + pub fn top_edge(&self) -> i32 { + self.y + } + + /// Get the right edge of the [`Rect`]. + /// + /// ```txt + /// <---------> + /// +---------+ + /// | | + /// | | + /// | | + /// +---------+ + /// ``` + pub fn right_edge(&self) -> i32 { + self.x + self.w as i32 + } + + /// Get the bottom edge of the [`Rect`]. + /// + /// ```txt + /// +---------+ ^ + /// | | | + /// | | | + /// | | | + /// +---------+ V + /// ``` + pub fn bottom_edge(&self) -> i32 { + self.y + self.h as i32 + } + + /// Get the left edge of the [`Rect`]. + /// + /// ```txt + /// <---> +---------+ + /// | | + /// | | + /// | | + /// +---------+ + /// ``` + pub fn left_edge(&self) -> i32 { + self.x + } } impl Default for Rect { From 15f25668696fc3e9a21ace3600af5f402bdb8ebc Mon Sep 17 00:00:00 2001 From: blackarch-lenovo Date: Thu, 20 Jul 2023 22:34:04 +0200 Subject: [PATCH 10/10] direction: remove inefficient nested loops The neighbor search is done using the rect coordinates; this is much more efficient compared to searching pixel by pixel. --- leftwm-layouts/src/geometry/direction.rs | 217 ++++++++++++++++++----- 1 file changed, 176 insertions(+), 41 deletions(-) diff --git a/leftwm-layouts/src/geometry/direction.rs b/leftwm-layouts/src/geometry/direction.rs index 25b319c..9302793 100644 --- a/leftwm-layouts/src/geometry/direction.rs +++ b/leftwm-layouts/src/geometry/direction.rs @@ -64,14 +64,39 @@ fn find_north(rects: &[Rect], current: usize) -> Option { let Some(current_rect) = rects.get(current).or(None) else { return None }; // We are all the way up, no neighbor available - if current_rect.y <= 0 { + if current_rect.top_edge() <= 0 { return None; } - let right_point = (current_rect.x + 1..current_rect.x + current_rect.w as i32 - 1).collect(); - let up_points = (0..current_rect.y).rev().collect(); + let mut nearest_rect: Option = None; + let mut min_x: Option = None; + let mut min_y: Option = None; - search_nearest_neighbor(&right_point, &up_points, rects, false) + for (i, r) in rects.iter().enumerate() { + if r == current_rect || // skip current rect + r.right_edge() - 1 < current_rect.left_edge() || // skip too right + r.left_edge() + 1 > current_rect.right_edge() || // skip too left + r.top_edge() + 1 > current_rect.bottom_edge() + // skip too low + { + continue; + } + + let x_distance = current_rect.left_edge() - r.right_edge(); + let y_distance = current_rect.top_edge() - r.bottom_edge(); + + find_nearest_rect( + &mut min_x, + &mut min_y, + &mut nearest_rect, + x_distance, + y_distance, + i, + true, + ); + } + + nearest_rect } // Find the east neighbor starting from a given `Rect` with index `current` in an array of @@ -80,14 +105,39 @@ fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option= display_width as i32 { + if current_rect.right_edge() >= display_width as i32 { return None; } - let down_points = (current_rect.y + 1..current_rect.y + current_rect.h as i32).collect(); - let right_points = (current_rect.x + current_rect.w as i32 + 1..=display_width as i32).collect(); + let mut nearest_rect: Option = None; + let mut min_x: Option = None; + let mut min_y: Option = None; + + for (i, r) in rects.iter().enumerate() { + if r == current_rect || // skip current rect + r.right_edge() - 1 < current_rect.right_edge() || // skip too left + r.bottom_edge() - 1 < current_rect.top_edge() || // skip too high + r.top_edge() + 1 > current_rect.bottom_edge() + // skip too low + { + continue; + } + + let x_distance = r.left_edge() - current_rect.right_edge(); + let y_distance = r.top_edge() - current_rect.bottom_edge(); + + find_nearest_rect( + &mut min_x, + &mut min_y, + &mut nearest_rect, + x_distance, + y_distance, + i, + false, + ); + } - search_nearest_neighbor(&down_points, &right_points, rects, true) + nearest_rect } // Find the south neighbor starting from a given `Rect` with index `current` in an array of @@ -100,11 +150,36 @@ fn find_south(rects: &[Rect], current: usize, display_height: u32) -> Option = None; + let mut min_x: Option = None; + let mut min_y: Option = None; - search_nearest_neighbor(&right_points, &down_points, rects, false) + for (i, r) in rects.iter().enumerate() { + if r == current_rect || // skip current rect + r.right_edge() - 1 < current_rect.left_edge() || // skip too left + r.left_edge() + 1 > current_rect.right_edge() || // skip too right + r.bottom_edge() - 1 < current_rect.top_edge() + // skip too high + { + // skip current rect + continue; + } + + let x_distance = current_rect.left_edge() - r.right_edge(); + let y_distance = r.top_edge() - current_rect.bottom_edge(); + + find_nearest_rect( + &mut min_x, + &mut min_y, + &mut nearest_rect, + x_distance, + y_distance, + i, + true, + ); + } + + nearest_rect } // Find the west neighbor starting from a given `Rect` with index `current` in an array of @@ -113,36 +188,82 @@ fn find_west(rects: &[Rect], current: usize) -> Option { let Some(current_rect) = rects.get(current).or(None) else { return None }; // We are all the way left; no neighbor available - if current_rect.x <= 0 { + if current_rect.left_edge() <= 0 { return None; } - let left_points = (0..=current_rect.x + -1).rev().collect(); - let down_points = (current_rect.y + 1..current_rect.y + current_rect.h as i32).collect(); + let mut nearest_rect: Option = None; + let mut min_x: Option = None; + let mut min_y: Option = None; + + for (i, r) in rects.iter().enumerate() { + if r == current_rect || // skip current rect + r.left_edge() + 1 > current_rect.right_edge() || // skip too right + r.bottom_edge() - 1 < current_rect.top_edge() || // skip too high + r.top_edge() + 1 > current_rect.bottom_edge() + // skip too low + { + // skip current rect + continue; + } + + let x_distance = current_rect.left_edge() - r.right_edge(); + let y_distance = r.top_edge() - current_rect.bottom_edge(); + + find_nearest_rect( + &mut min_x, + &mut min_y, + &mut nearest_rect, + x_distance, + y_distance, + i, + false, + ); + } - search_nearest_neighbor(&left_points, &down_points, rects, false) + nearest_rect } -fn search_nearest_neighbor( - first_direction: &Vec, - second_direction: &Vec, - rects: &[Rect], - inverted: bool, -) -> Option { - for f in first_direction { - for s in second_direction { - for (i, r) in rects.iter().enumerate() { - if inverted { - if r.contains((*s, *f)) { - return Some(i); - } - } else if r.contains((*f, *s)) { - return Some(i); - } - } +// Find the nearest `Rect`. If updown is true, evaluate y_distance and then x_distance. If updown +// is false, evaluate x_distance and then y_distance. +fn find_nearest_rect( + min_x: &mut Option, + min_y: &mut Option, + nearest_rect: &mut Option, + x_distance: i32, + y_distance: i32, + index: usize, + updown: bool, +) { + if min_x.is_none() { + *min_x = Some(x_distance); + *nearest_rect = Some(index); + } + + if min_y.is_none() { + *min_y = Some(y_distance); + *nearest_rect = Some(index); + } + + if updown { + if y_distance < min_y.unwrap() { + // take the nearest up/down + *min_y = Some(y_distance); + *nearest_rect = Some(index); + } else if y_distance == min_y.unwrap() && x_distance < min_x.unwrap() { + // take the left most + *min_x = Some(x_distance); + *nearest_rect = Some(index); } + } else if x_distance < min_x.unwrap() { + // take the nearest left/right + *min_x = Some(x_distance); + *nearest_rect = Some(index); + } else if x_distance == min_x.unwrap() && y_distance < min_y.unwrap() { + // take the higher + *min_y = Some(y_distance); + *nearest_rect = Some(index); } - None } impl Direction { @@ -183,14 +304,14 @@ mod tests { // |+---+ +---+ +---+| // || 0 | | 3 | | 4 || // |+---+ +---+ +---+| - // |+---+ +---+| - // || 1 | | || - // |+---+ | || + // |+---+ +---+ +---+| + // || 1 | | 6 | | || + // |+---+ +---+ | || // |+---+ | 5 || // || 2 | | || // |+---+ +---+| // +-----------------+ - const ARRAY: [Rect; 6] = [ + const ARRAY: [Rect; 7] = [ Rect { x: 0, y: 0, @@ -227,6 +348,12 @@ mod tests { w: 200, h: 400, }, + Rect { + x: 200, + y: 200, + w: 200, + h: 400, + }, ]; #[test] @@ -243,6 +370,8 @@ mod tests { assert_eq!(res, None); let res = Direction::find_neighbor(&ARRAY, 5, Direction::North, &CONTAINER); assert_eq!(res, Some(4)); + let res = Direction::find_neighbor(&ARRAY, 6, Direction::North, &CONTAINER); + assert_eq!(res, Some(3)); } #[test] @@ -250,15 +379,17 @@ mod tests { let res = Direction::find_neighbor(&ARRAY, 0, Direction::East, &CONTAINER); assert_eq!(res, Some(3)); let res = Direction::find_neighbor(&ARRAY, 1, Direction::East, &CONTAINER); - assert_eq!(res, Some(5)); + assert_eq!(res, Some(6)); let res = Direction::find_neighbor(&ARRAY, 2, Direction::East, &CONTAINER); - assert_eq!(res, Some(5)); + assert_eq!(res, Some(6)); let res = Direction::find_neighbor(&ARRAY, 3, Direction::East, &CONTAINER); assert_eq!(res, Some(4)); let res = Direction::find_neighbor(&ARRAY, 4, Direction::East, &CONTAINER); assert_eq!(res, None); let res = Direction::find_neighbor(&ARRAY, 5, Direction::East, &CONTAINER); assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 6, Direction::East, &CONTAINER); + assert_eq!(res, Some(5)); } #[test] @@ -270,11 +401,13 @@ mod tests { let res = Direction::find_neighbor(&ARRAY, 2, Direction::South, &CONTAINER); assert_eq!(res, None); let res = Direction::find_neighbor(&ARRAY, 3, Direction::South, &CONTAINER); - assert_eq!(res, None); + assert_eq!(res, Some(6)); let res = Direction::find_neighbor(&ARRAY, 4, Direction::South, &CONTAINER); assert_eq!(res, Some(5)); let res = Direction::find_neighbor(&ARRAY, 5, Direction::South, &CONTAINER); assert_eq!(res, None); + let res = Direction::find_neighbor(&ARRAY, 6, Direction::South, &CONTAINER); + assert_eq!(res, None); } #[test] @@ -290,6 +423,8 @@ mod tests { let res = Direction::find_neighbor(&ARRAY, 4, Direction::West, &CONTAINER); assert_eq!(res, Some(3)); let res = Direction::find_neighbor(&ARRAY, 5, Direction::West, &CONTAINER); + assert_eq!(res, Some(6)); + let res = Direction::find_neighbor(&ARRAY, 6, Direction::West, &CONTAINER); assert_eq!(res, Some(1)); } }