Skip to content

Instantly share code, notes, and snippets.

@p-i-
Last active August 22, 2024 13:51
Show Gist options
  • Save p-i-/f236f662adf4b6a22aba5ac7df365af8 to your computer and use it in GitHub Desktop.
Save p-i-/f236f662adf4b6a22aba5ac7df365af8 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
from collections.abc import Iterable
from math import ceil, floor
# from numbers import Number
from Coord import Coord
VERBOSE = False
def pr(*args, **kwargs):
if VERBOSE:
print(*args, **kwargs)
class Rect:
def __init__(self, *args, **kwargs):
self._t = self._b = self._l = self._r = 0
if args:
if len(args) == 1:
a = args[0]
if isinstance(a, Rect):
self.tl_br = a.tl_br
else:
# This will handle a numeric type or an iterable
# (like Coord or [3,2] or np.array([3,2]))
self.tl = self.br = Coord(args[0])
elif len(args) == 2:
# We need to be careful whether we're dealing with (t, l) or (tl, br)
a, b = args
if isinstance(a, Iterable) or isinstance(b, Iterable):
self.tl, self.br = a, b
else:
raise Exception("What are you trying to do?")
elif len(args) == 4:
self.tlbr = args
else:
assert False
for k, v in kwargs.items():
setattr(self, k, v)
def __getattr__(self, k):
# https://stackoverflow.com/questions/78899267/
if k.startswith('_'):
return
is_property = isinstance(getattr(self.__class__, k, None), property)
if is_property:
return object.__getattribute__(self, k)
if '_' in k:
return [getattr(self, u) for u in k.split('_')]
if len(k) > 1:
is_lower = k.islower()
k = k.lower()
L = [getattr(self, u) for u in k]
return Coord(L) if len(L) == 2 and is_lower else L
assert False
def __setattr__(self, k, v):
if k.startswith('_'):
object.__setattr__(self, k, v)
return
is_property = isinstance(getattr(self.__class__, k, None), property)
if is_property:
object.__setattr__(self, k, v)
return
# We need to check whether the attribute actually exists for the object, e.g. 'center' DOES exist as a property
# This should only execute if there's NO corresponding attribute
if len(k) == 1:
object.__setattr__(self, k, v)
else:
if '_' in k:
k = k.split('_')
if not isinstance(v, Iterable):
v = [v] * len(k)
for k_, v_ in zip(k, v):
pr('setting:', k_, v_)
setattr(self, k_, v_)
def _make_property(attr):
return property(
lambda self: getattr(self, f'_{attr}'),
lambda self, v: setattr(self, f'_{attr}', v)
)
# getter, setter)
t = _make_property('t')
b = _make_property('b')
l = _make_property('l')
r = _make_property('r')
@property
def h(self):
return self.b - self.t + 1
@h.setter
def h(self, v):
self.b = self.t + v - 1
@property
def w(self):
return self.r - self.l + 1
@w.setter
def w(self, v):
self.r = self.l + v - 1
# Note `R.center.y += 1` will fail. That's as it should be.
@property
def center(self):
return (self.tl + self.br) / 2
@center.setter
def center(self, v):
self += -self.center + Coord(v)
# Allow: newR = R(t=0, b=1)
def __call__(self, **D):
R = Rect(self.t, self.b, self.l, self.r)
R.set(**D)
return R
# Allow R.set(t=0)
def set(self, **D):
for k, v in D.items():
setattr(self, k, v)
return self
@property
def copy(self):
t, l, b, r = self.tlbr
return Rect(t, l, b, r)
# Allow: newR = R.new(t=0, b=1)
def spawn(self, **D):
return self.copy.set(**D)
@property
def int(self):
return Rect([int(u) for u in self.tlbr])
def __repr__(self):
return f'{self.tl:~>2} to {self.br:~>2}'
def _slice(self, fix_right_margin=False):
return tuple(
slice(
ceil(p),
None if fix_right_margin and q == 0 else floor(q) + 1
)
for (p, q) in self.tb_lr
)
@property
def slice(self):
return self._slice()
@property
def mslice(self):
return self._slice(fix_right_margin=True)
def __contains__(self, p):
return self.tl <= Coord(p) <= self.br
def __iadd__(self, u):
u = Coord(u)
self.tl += u
self.br += u
return self
def __add__(self, u):
u = Coord(u)
return Rect(tl=self.tl + u, br=self.br + u)
def __sub__(self, u):
u = Coord(u)
return Rect(tl=self.tl - Coord(u), br=self.br - u)
def __and__(self, u):
if not isinstance(u, Rect):
u = Rect(u)
t, l, b, r = self.tlbr
T, L, B, R = u.tlbr
R = Rect(t=max(t, T), l=max(l, L), b=min(b, B), r=min(r, R))
return None if R.r < R.l or R.b < R.t else R
def __or__(self, u):
if not isinstance(u, Rect):
u = Rect(u)
t, l, b, r = self.tlbr
T, L, B, R = u.tlbr
return Rect(t=min(t, T), l=min(l, L), b=max(b, B), r=max(r, R))
def __eq__(self, u):
if not isinstance(u, Rect):
u = Rect(u)
return all(p == q for p, q in zip(self, u))
# def __mul__(self, k):
# return Rect(k * self._)
def extend(self, in_place=False, *args, **kwargs):
M = Rect(*args, **kwargs)
if in_place:
self.tl -= M.tl
self.br += M.br
return self
return Rect(tl=self.tl - M.tl, br=self.br - M.br)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment