Package pytilities :: Package geometry :: Module rectangle
[hide private]
[frames] | no frames]

Source Code for Module pytilities.geometry.rectangle

  1  # Copyright (C) 2010 Tim Diels <limyreth@users.sourceforge.net> 
  2  #  
  3  # This file is part of pytilities. 
  4  #  
  5  # pytilities is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  #  
 10  # pytilities is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  #  
 15  # You should have received a copy of the GNU General Public License 
 16  # along with pytilities.  If not, see <http://www.gnu.org/licenses/>. 
 17  # 
 18   
 19  __docformat__ = 'reStructuredText' 
 20   
 21  from pytilities.overloading import \ 
 22      overloaded, Overload, Param, CompositeParam 
 23  from pytilities.types import NumberType 
 24  from . import Vector, BoundVector 
25 26 # NOTE: be careful with the awesome ways python int vs float math works 27 # As with statically typed languages, even here 5 / 2 == 2. As values do still 28 # have types. So, try not to mix ints and floats. 29 # If you'd like it to autoconvert or assert for ints/floats, go decorator in 30 # this class (and vector) 31 -class Rectangle(object):
32 33 """ 34 A rectangle identified by two points. 35 36 The rectangle stores left, top, right, and bottom values. 37 38 Coordinates are based on screen coordinates. 39 40 origin top 41 +-----> x increases | 42 | left -+- right 43 v | 44 y increases bottom 45 46 Instance methods: 47 48 - `copy`: Make a shallow copy 49 - `contains`: Is a vector inside? 50 - `overlaps`: Does a rectangle overlap? 51 - `inflate`: grow (or shrink) 52 - `move_to`: move entire rectangle to a spot 53 - `move_by`: move entire rectangle by an amount 54 55 Instance properties: 56 57 - `bounds`: Read-write, all rectangle coordinates 58 - `center`: Read-write, center of the rectangle 59 - `width`: Read-write, width of the rectangle 60 - `height`: Read-write, height of the rectangle 61 - `size`: Read-write, size of the rectangle 62 - `left`: Read-write, left edge of rectangle 63 - `top`: Read-write, top edge of rectangle 64 - `right`: Read-write, right edge of rectangle 65 - `bottom`: Read-write, bottom edge of rectangle 66 - `top_left`: Read-write, top-left corner 67 - `top_right`: Read-write, top-right corner 68 - `bottom_left`: Read-write, bottom-left corner 69 - `bottom_right`: Read-write, bottom-right corner 70 71 Operators: 72 73 str(s) 74 repr(s) 75 """ 76
77 - def __init__(self, *args):
78 """ 79 Construct a rectangle. 80 81 Overloaded, parameters: 82 83 :a: 84 `left` :: float | int 85 `top` :: float | int 86 `right` :: float | int 87 `bottom` :: float | int 88 89 :b: 90 `top_left` :: Vector 91 `bottom_right` :: Vector 92 """ 93 94 self.bounds = args 95 96 self.__size = BoundVector( 97 self, "width", "height") 98 99 self.__center = BoundVector( 100 self, "__center_x", "__center_y") 101 102 self.__top_left = BoundVector( 103 self, "left", "top") 104 105 self.__top_right = BoundVector( 106 self, "right", "top") 107 108 self.__bottom_left = BoundVector( 109 self, "left", "bottom") 110 111 self.__bottom_right = BoundVector( 112 self, "right", "bottom")
113
114 - def __copy__(self):
115 return Rectangle(*(self.bounds))
116
117 - def copy(self):
118 """Returns shallow copy""" 119 return self.__copy__
120 121 @property
122 - def bounds(self):
123 """Read-write, all rectangle coordinates 124 125 Returns (left, top, right, bottom) 126 127 Overloaded, set parameters: 128 129 :a: 130 `left` :: float | int 131 `top` :: float | int 132 `right` :: float | int 133 `bottom` :: float | int 134 135 :b: 136 `top_left` :: Vector 137 `bottom_right` :: Vector 138 """ 139 return (self.left, self.top, self.right, self.bottom)
140
141 - def __set_bounds_numbers(self, left, top, right, bottom):
142 (self.left, self.top, self.right, self.bottom) = \ 143 (left, top, right, bottom) 144 145 assert self.left < self.right, \ 146 "given top_left isn't the top left corner" 147 assert self.top < self.bottom, \ 148 "given top_left isn't the top left corner"
149
150 - def __set_bounds_points(self, top_left, bottom_right):
151 (self.left, self.top) = top_left.xy 152 (self.right, self.bottom) = bottom_right.xy 153 154 assert self.left < self.right, \ 155 "given top_left isn't the top left corner" 156 assert self.top < self.bottom, \ 157 "given top_left isn't the top left corner"
158 159 @bounds.setter 160 @overloaded(( 161 Overload(__set_bounds_numbers, 162 CompositeParam("args", ( 163 Param("left", NumberType), 164 Param("top", NumberType), 165 Param("right", NumberType), 166 Param("bottom", NumberType)))), 167 Overload(__set_bounds_points, 168 CompositeParam("args", ( 169 Param("top_left"), 170 Param("bottom_right"))))))
171 - def bounds(self):
172 pass
173 174 @property
175 - def __center_x(self):
176 return (self.left + self.right) / 2
177 178 @__center_x.setter
179 - def __center_x(self, value):
180 cx = self.size.x 181 self.left = value - cx/2 182 self.right = self.left + cx # this way we maintain our size
183 184 @property
185 - def __center_y(self):
186 return (self.top + self.bottom) / 2
187 188 @__center_y.setter
189 - def __center_y(self, value):
190 cy = self.size.y 191 self.top = value - cy/2 192 self.bottom = self.top + cy # this way we maintain our size
193 194 @property
195 - def center(self):
196 """ 197 Read-write, center of the rectangle :: bound Vector 198 199 Changing the center does not change the size. 200 """ 201 return self.__center
202 203 @center.setter
204 - def center(self, v):
205 self.__center.assign(v)
206 207 @property
208 - def width(self):
209 """Read-write, width of the rectangle :: float | int""" 210 return self.right - self.left
211 212 @width.setter
213 - def width(self, value):
214 self.right = self.left + value
215 216 @property
217 - def height(self):
218 """Read-write, height of the rectangle :: float | int""" 219 return self.bottom - self.top
220 221 @height.setter
222 - def height(self, value):
223 self.bottom = self.top + value
224 225 @property
226 - def size(self):
227 """Read-write, size of the rectangle :: bound Vector""" 228 return self.__size
229 230 @size.setter
231 - def size(self, value):
232 self.__size.assign(value)
233 234 @property
235 - def left(self):
236 """Read-write, left edge of rectangle :: float | int""" 237 return self.__left
238 239 @left.setter
240 - def left(self, value):
241 self.__left = value
242 243 @property
244 - def top(self):
245 """Read-write, top edge of rectangle :: float | int""" 246 return self.__top
247 248 @top.setter
249 - def top(self, value):
250 self.__top = value
251 252 @property
253 - def right(self):
254 """Read-write, right edge of rectangle :: float | int""" 255 return self.__right
256 257 @right.setter
258 - def right(self, value):
259 self.__right = value
260 261 @property
262 - def bottom(self):
263 """Read-write, bottom edge of rectangle :: float | int""" 264 return self.__bottom
265 266 @bottom.setter
267 - def bottom(self, value):
268 self.__bottom = value
269 270 # TODO: think of way of writing bound props like size easier 271 # Call it @bound_property or something... 272 273 @property
274 - def top_left(self):
275 """Read-write, top-left corner :: bound Vector""" 276 return self.__top_left
277 278 @top_left.setter
279 - def top_left(self, value):
280 return self.__top_left.assign(value)
281 282 @property
283 - def top_right(self):
284 """Read-write, top-right corner :: bound Vector""" 285 return self.__top_right
286 287 @top_right.setter
288 - def top_right(self, value):
289 return self.__top_right.assign(value)
290 291 @property
292 - def bottom_left(self):
293 """Read-write, bottom-left corner :: bound Vector""" 294 return self.__bottom_left
295 296 @bottom_left.setter
297 - def bottom_left(self, value):
298 return self.__bottom_left.assign(value)
299 300 @property
301 - def bottom_right(self):
302 """Read-write, bottom-right corner :: bound Vector""" 303 return self.__bottom_right
304 305 @bottom_right.setter
306 - def bottom_right(self, value):
307 return self.__bottom_right.assign(value)
308
309 - def contains(self, v):
310 """ 311 Returns `True` if `v` is inside the rectangle. 312 313 Parameters: 314 315 `v` :: Vector 316 """ 317 x,y = v.xy 318 return (self.left <= x <= self.right and 319 self.top <= y <= self.bottom)
320
321 - def overlaps(self, rect):
322 """ 323 Returns `True` if `rect` overlaps with this rectangle. 324 325 Parameters: 326 327 `rect` :: Rectangle 328 """ 329 return (self.right > rect.left and self.left < rect.right and 330 self.top < rect.bottom and self.bottom > rect.top)
331
332 - def __inflate_number(self, n):
333 self.left -= n 334 self.top -= n 335 self.right += n 336 self.bottom += n
337
338 - def __inflate_vector(self, v):
339 x, y = v.xy 340 self.left -= x 341 self.top -= y 342 self.right += x 343 self.bottom += y
344 345 @overloaded(( 346 Overload(__inflate_number, 347 Param("n", NumberType)), 348 Overload(__inflate_vector, 349 Param("v"))))
350 - def inflate(self):
351 """ 352 Inflate the rectangle 353 354 Overloaded, parameters: 355 356 :a: 357 n :: number 358 Extend all sides by n points 359 :b: 360 v :: Vector 361 extend left and right sides by v.x and top and bottom sides by 362 v.y 363 """
364
365 - def move_to(self, v):
366 """ 367 Move the top_left corner to `v`, without changing size 368 369 Parameters: 370 371 `v` :: Vector 372 the spot to move to 373 """ 374 bottom_right = v + self.size 375 (self.left, self.top) = v.xy 376 (self.right, self.bottom) = bottom_right.xy
377
378 - def move_by(self, v):
379 """ 380 Move the top_left corner by `v`, without changing size 381 382 Parameters: 383 384 `v` :: Vector 385 the amount to move by 386 """ 387 self.left += v.x 388 self.right += v.x 389 self.top += v.y 390 self.bottom += v.y
391
392 - def __str__( self ):
393 return "<%s (%s,%s)-(%s,%s)>" % (self.__class__.__name__, 394 self.left, self.top, 395 self.right, self.bottom)
396
397 - def __repr__(self):
398 return "%s(%r, %r)" % (self.__class__.__name__, 399 Vector(self.left, self.top), 400 Vector(self.right, self.bottom))
401 402 # TODO: try to write code of bound props in a more succint way (using some 403 # awesome lib func/decorator/class) 404