Skip to content

Commit

Permalink
Rewrite with more ordinal values
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexisBRENON committed Apr 14, 2023
1 parent 27a9078 commit 9b434ac
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 68 deletions.
6 changes: 3 additions & 3 deletions src/ewmh_m2m/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ def main():
arg_parser.add_argument(
"--direction", "-d",
action="store",
type=Ordinal.get,
choices=list(Ordinal),
default=Ordinal.NEXT.name.capitalize(),
type=Ordinal.__getitem__,
choices=[o.name for o in sorted(Ordinal, key=lambda o: o.value)],
default=Ordinal.EAST_SOUTHEAST.name,
help="Direction in which to move the window (default: %(default)s)")
arg_parser.add_argument("--no-wrap", "-W", action="store_true", help="Do not go back if no screen found.")

Expand Down
25 changes: 25 additions & 0 deletions src/ewmh_m2m/geometry.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import math
import typing
from ewmh_m2m.ordinal import Ordinal


class Geometry:
"""Data class to manipulate rectangles defined as (x, y, w, h)"""
def __init__(self, x: float = 0, y: float = 0, w: float = 0, h: float = 0):
Expand Down Expand Up @@ -39,6 +44,26 @@ def vertically_overlap(self, other) -> bool:
def overlap(self, other) -> bool:
return self.horizontally_overlap(other) and self.vertically_overlap(other)

def directions_to(self, other: "Geometry") -> typing.Collection[Ordinal]:
result = set(Ordinal)
if self.horizontally_overlap(other):
result &= {o for o in Ordinal if abs(o.sin) < 0.5}
elif self.vertically_overlap(other):
result &= {o for o in Ordinal if abs(o.cos) < 0.5}
else:
result -= {o for o in Ordinal if o.value % 90 == 0}

if other.x > self.x:
result &= {o for o in Ordinal if o.cos >= 0 }
if other.y < self.y:
result &= {o for o in Ordinal if o.sin >= 0 }
if other.x < self.x:
result &= {o for o in Ordinal if o.cos <= 0 }
if other.y > self.y:
result &= {o for o in Ordinal if o.sin <= 0 }

return result

def __eq__(self, other):
return list(self) == list(other)

Expand Down
58 changes: 23 additions & 35 deletions src/ewmh_m2m/ordinal.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,32 @@
from enum import Enum
import math

_HEXADAN = 360 / 16

class Ordinal(Enum):
NORTH = 0
EAST = 1
SOUTH = 2
WEST = 3
NEXT = 4
PREV = 5
EAST = 0 * _HEXADAN
NORTH = 4 * _HEXADAN
WEST = 8 * _HEXADAN
SOUTH = 12 * _HEXADAN

@classmethod
def get(cls, v: str):
if v.upper()[0] == 'E':
return Ordinal.EAST
if v.upper()[0] == 'W':
return Ordinal.WEST
if v.upper()[0:2] == 'NO':
return Ordinal.NORTH
if v.upper()[0] == 'S':
return Ordinal.SOUTH
if v.upper()[0:2] == 'NE':
return Ordinal.NEXT
if v.upper()[0] == 'P':
return Ordinal.PREV
raise TypeError("No direction match with '{}'".format(v))
NORTHEAST = 2 * _HEXADAN
NORTHWEST = 6 * _HEXADAN
SOUTHWEST = 10 * _HEXADAN
SOUTHEAST = 14 * _HEXADAN

def __str__(self):
return self.name
EAST_NORTHEAST = 1 * _HEXADAN
NORTH_NORTHEAST = 3 * _HEXADAN
NORTH_NORTHWEST = 5 * _HEXADAN
WEST_NORTHWEST = 7 * _HEXADAN
WEST_SOUTHWEST = 9 * _HEXADAN
SOUTH_SOUTHWEST = 11 * _HEXADAN
SOUTH_SOUTHEAST = 13 * _HEXADAN
EAST_SOUTHEAST = 15 * _HEXADAN

def __init__(self, value) -> None:
self.sin = round(math.sin(math.radians(value)), 6)
self.cos = round(math.cos(math.radians(value)), 6)

@property
def opposite(self):
if self is Ordinal.NORTH:
return Ordinal.SOUTH
if self is Ordinal.SOUTH:
return Ordinal.NORTH
if self is Ordinal.EAST:
return Ordinal.WEST
if self is Ordinal.WEST:
return Ordinal.EAST
if self is Ordinal.NEXT:
return Ordinal.PREV
if self is Ordinal.PREV:
return Ordinal.NEXT
return Ordinal((self.value + 180) % 360)
26 changes: 12 additions & 14 deletions src/ewmh_m2m/screen.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import math
from typing import Set, Iterable, Dict, List, Optional

import xpybutil.xinerama
Expand All @@ -17,20 +18,18 @@ def get_sibling_screens(current: Geometry, screens: Iterable[Geometry]) -> Dict[
Each list is ordered from the nearest screen to the furthest one.
"""
horizontal_screens = [g for g in screens if current.horizontally_overlap(g)]
vertical_screens = [g for g in screens if current.vertically_overlap(g)]

screens_list = list(screens)
current_index = screens_list.index(current)
ordered_screens = screens_list[:current_index] + screens_list[current_index + 1:] if (current_index > 0) else screens_list[1:]

directions = [
(g, current.directions_to(g)) for g in screens if g != current
]
return {
Ordinal.SOUTH: sorted([g for g in vertical_screens if g.y > current.y], key=lambda g: g.y),
Ordinal.NORTH: sorted([g for g in vertical_screens if g.y < current.y], key=lambda g: -1 * g.y),
Ordinal.EAST: sorted([g for g in horizontal_screens if g.x > current.x], key=lambda g: g.x),
Ordinal.WEST: sorted([g for g in horizontal_screens if g.x < current.x], key=lambda g: -1 * g.x),
Ordinal.NEXT: ordered_screens,
Ordinal.PREV: ordered_screens.reverse()
o: sorted(
[g for g, dirs in directions if o in dirs],
key=lambda g: (
math.hypot(g.x - current.x, g.y - current.y),
math.copysign(g.x, o.cos) if abs(o.cos) > abs(o.sin) else math.copysign(g.y, o.sin)
)
)
for o in Ordinal
}


Expand All @@ -43,4 +42,3 @@ def get_sibling_screen(siblings: Dict[Ordinal, List[Geometry]],
if not no_wrap and siblings[direction.opposite]:
return siblings[direction.opposite][-1]
return None

13 changes: 13 additions & 0 deletions tests/test_geometry.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from ewmh_m2m.geometry import Geometry
from ewmh_m2m.ordinal import Ordinal


class TestGeometry:
Expand Down Expand Up @@ -47,3 +48,15 @@ def test_overlap(self):

assert g1.overlap(g2)
assert g2.overlap(g1)

def test_directions_aligned(self):
g1 = Geometry(0, 0, 1, 1)
g2 = Geometry(1, 0, 1, 1)

assert g1.directions_to(g2) == {Ordinal.EAST, Ordinal.EAST_NORTHEAST, Ordinal.EAST_SOUTHEAST}

def test_directions_not_aligned(self):
g1 = Geometry(0, 0, 1, 1)
g2 = Geometry(1, 1, 1, 1)

assert g1.directions_to(g2) == {Ordinal.SOUTH_SOUTHEAST, Ordinal.SOUTHEAST, Ordinal.EAST_SOUTHEAST}
40 changes: 24 additions & 16 deletions tests/test_screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,8 @@ def test_siblings_horizontal(self):

siblings = ewmh_m2m.screen.get_sibling_screens(current, screens)

assert siblings == {
Ordinal.SOUTH: [], Ordinal.NORTH: [],
Ordinal.EAST: [Geometry(30, 0, 10, 10), Geometry(40, 0, 10, 10)],
Ordinal.WEST: [Geometry(10, 0, 10, 10), Geometry(0, 0, 10, 10)]
}
assert siblings[Ordinal.EAST] == [Geometry(30, 0, 10, 10), Geometry(40, 0, 10, 10)]
assert siblings[Ordinal.WEST] == [Geometry(10, 0, 10, 10), Geometry(0, 0, 10, 10)]

def test_siblings_vertical(self):
current = Geometry(0, 20, 10, 10)
Expand All @@ -50,11 +47,8 @@ def test_siblings_vertical(self):

siblings = ewmh_m2m.screen.get_sibling_screens(current, screens)

assert siblings == {
Ordinal.EAST: [], Ordinal.WEST: [],
Ordinal.SOUTH: [Geometry(0, 30, 10, 10), Geometry(0, 40, 10, 10)],
Ordinal.NORTH: [Geometry(0, 10, 10, 10), Geometry(0, 0, 10, 10)]
}
assert siblings[Ordinal.SOUTH] == [Geometry(0, 30, 10, 10), Geometry(0, 40, 10, 10)]
assert siblings[Ordinal.NORTH] == [Geometry(0, 10, 10, 10), Geometry(0, 0, 10, 10)]

def test_siblings(self):
screens = sorted(
Expand All @@ -64,12 +58,26 @@ def test_siblings(self):

siblings = ewmh_m2m.screen.get_sibling_screens(current, screens)

assert siblings == {
Ordinal.NORTH: [Geometry(2, 1, 1, 1), Geometry(2, 0, 1, 1)],
Ordinal.EAST: [Geometry(3, 2, 1, 1), Geometry(4, 2, 1, 1)],
Ordinal.SOUTH: [Geometry(2, 3, 1, 1), Geometry(2, 4, 1, 1)],
Ordinal.WEST: [Geometry(1, 2, 1, 1), Geometry(0, 2, 1, 1)]
}
assert siblings[Ordinal.NORTH] == [Geometry(2, 1, 1, 1), Geometry(2, 0, 1, 1)]
assert siblings[Ordinal.EAST] == [Geometry(3, 2, 1, 1), Geometry(4, 2, 1, 1)]
assert siblings[Ordinal.SOUTH] == [Geometry(2, 3, 1, 1), Geometry(2, 4, 1, 1)]
assert siblings[Ordinal.WEST] == [Geometry(1, 2, 1, 1), Geometry(0, 2, 1, 1)]

assert siblings[Ordinal.NORTHEAST] == [Geometry(3, 1, 1, 1), Geometry(3, 0, 1, 1), Geometry(4, 1, 1, 1), Geometry(4, 0, 1, 1)]
assert siblings[Ordinal.NORTHWEST] == [Geometry(1, 1, 1, 1), Geometry(1, 0, 1, 1), Geometry(0, 1, 1, 1), Geometry(0, 0, 1, 1)]
assert siblings[Ordinal.SOUTHWEST] == [Geometry(1, 3, 1, 1), Geometry(1, 4, 1, 1), Geometry(0, 3, 1, 1), Geometry(0, 4, 1, 1)]
assert siblings[Ordinal.SOUTHEAST] == [Geometry(3, 3, 1, 1), Geometry(3, 4, 1, 1), Geometry(4, 3, 1, 1), Geometry(4, 4, 1, 1)]

assert set(siblings[Ordinal.EAST_NORTHEAST]) == set(siblings[Ordinal.EAST] + siblings[Ordinal.NORTHEAST])
assert set(siblings[Ordinal.NORTH_NORTHEAST]) == set(siblings[Ordinal.NORTH] + siblings[Ordinal.NORTHEAST])
assert set(siblings[Ordinal.NORTH_NORTHWEST]) == set(siblings[Ordinal.NORTH] + siblings[Ordinal.NORTHWEST])
assert set(siblings[Ordinal.WEST_NORTHWEST]) == set(siblings[Ordinal.WEST] + siblings[Ordinal.NORTHWEST])
assert set(siblings[Ordinal.WEST_SOUTHWEST]) == set(siblings[Ordinal.WEST] + siblings[Ordinal.SOUTHWEST])
assert set(siblings[Ordinal.SOUTH_SOUTHWEST]) == set(siblings[Ordinal.SOUTH] + siblings[Ordinal.SOUTHWEST])
assert set(siblings[Ordinal.SOUTH_SOUTHEAST]) == set(siblings[Ordinal.SOUTH] + siblings[Ordinal.SOUTHEAST])
assert set(siblings[Ordinal.EAST_SOUTHEAST]) == set(siblings[Ordinal.EAST] + siblings[Ordinal.SOUTHEAST])



def test_siblings_gh_issue_14(self):
"""
Expand Down

0 comments on commit 9b434ac

Please sign in to comment.