1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
import numpy as np
class Rect(object):
"""
Representation of a rectangular area in a 2D coordinate system.
Parameters
----------
*args : arguments
Can be in the form `Rect(x, y, w, h)`, `Rect(pos, size)`, or
`Rect(Rect)`.
"""
def __init__(self, *args, **kwargs):
self._pos = (0, 0)
self._size = (0, 0)
if len(args) == 1 and isinstance(args[0], Rect):
self._pos = args[0]._pos[:]
self._size = args[0]._size[:]
elif (len(args) == 1 and isinstance(args[0], (list, tuple)) and
len(args[0]) == 4):
self._pos = args[0][:2]
self._size = args[0][2:]
elif len(args) == 2:
self._pos = tuple(args[0])
self._size = tuple(args[1])
elif len(args) == 4:
self._pos = tuple(args[:2])
self._size = tuple(args[2:])
elif len(args) != 0:
raise TypeError("Rect must be instantiated with 0, 1, 2, or 4 "
"non-keyword arguments.")
self._pos = kwargs.get('pos', self._pos)
self._size = kwargs.get('size', self._size)
if len(self._pos) != 2 or len(self._size) != 2:
raise ValueError("Rect pos and size arguments must have 2 "
"elements.")
@property
def pos(self):
return tuple(self._pos)
@pos.setter
def pos(self, p):
assert len(p) == 2
self._pos = p
@property
def size(self):
return tuple(self._size)
@size.setter
def size(self, s):
assert len(s) == 2
self._size = s
@property
def width(self):
return self.size[0]
@width.setter
def width(self, w):
self.size[0] = w
@property
def height(self):
return self.size[1]
@height.setter
def height(self, h):
self.size[1] = h
@property
def left(self):
return self.pos[0]
@left.setter
def left(self, x):
self.size = (self.size[0] + (self.pos[0] - x), self.size[1])
self.pos = (x, self.pos[1])
@property
def right(self):
return self.pos[0] + self.size[0]
@right.setter
def right(self, x):
self.size = (x - self.pos[0], self.size[1])
@property
def bottom(self):
return self.pos[1]
@bottom.setter
def bottom(self, y):
self.size = (self.size[0], self.size[1] + (self.pos[1] - y))
self.pos = (self.pos[0], y)
@property
def top(self):
return self.pos[1] + self.size[1]
@top.setter
def top(self, y):
self.size = (self.size[0], y - self.pos[1])
@property
def center(self):
return (self.pos[0] + self.size[0] * 0.5,
self.pos[1] + self.size[1] * 0.5)
def padded(self, padding):
"""Return a new Rect padded (smaller) by padding on all sides
Parameters
----------
padding : float
The padding.
Returns
-------
rect : instance of Rect
The padded rectangle.
"""
return Rect(pos=(self.pos[0]+padding, self.pos[1]+padding),
size=(self.size[0]-2*padding, self.size[1]-2*padding))
def normalized(self):
"""Return a Rect covering the same area, but with height and width
guaranteed to be positive."""
return Rect(pos=(min(self.left, self.right),
min(self.top, self.bottom)),
size=(abs(self.width), abs(self.height)))
def flipped(self, x=False, y=True):
"""Return a Rect with the same bounds but with axes inverted
Parameters
----------
x : bool
Flip the X axis.
y : bool
Flip the Y axis.
Returns
-------
rect : instance of Rect
The flipped rectangle.
"""
pos = list(self.pos)
size = list(self.size)
for i, flip in enumerate((x, y)):
if flip:
pos[i] += size[i]
size[i] *= -1
return Rect(pos, size)
def __eq__(self, r):
if not isinstance(r, Rect):
return False
return (np.all(np.equal(r.pos, self.pos)) and
np.all(np.equal(r.size, self.size)))
def __add__(self, a):
""" Return this Rect translated by *a*.
"""
return self._transform_out(self._transform_in()[:, :2] + a[:2])
def contains(self, x, y):
"""Query if the rectangle contains points
Parameters
----------
x : float
X coordinate.
y : float
Y coordinate.
Returns
-------
contains : bool
True if the point is within the rectangle.
"""
return (x >= self.left and x <= self.right and
y >= self.bottom and y <= self.top)
def __repr__(self):
return "<Rect (%g, %g) (%g, %g)>" % (self.pos + self.size)
def _transform_in(self):
"""Return array of coordinates that can be mapped by Transform
classes."""
return np.array([
[self.left, self.bottom, 0, 1],
[self.right, self.top, 0, 1]])
def _transform_out(self, coords):
"""Return a new Rect from coordinates mapped after _transform_in()."""
return Rect(pos=coords[0, :2], size=coords[1, :2]-coords[0, :2])
|