1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Objects for convenient manipulation of points and other surface areas.""" 6 7import collections 8 9 10class Point(collections.namedtuple('Point', ['x', 'y'])): 11 """Object to represent an (x, y) point on a surface. 12 13 Args: 14 x, y: Two numeric coordinates that define the point. 15 """ 16 __slots__ = () 17 18 def __str__(self): 19 """Get a useful string representation of the object.""" 20 return '(%s, %s)' % (self.x, self.y) 21 22 def __add__(self, other): 23 """Sum of two points, e.g. p + q.""" 24 if isinstance(other, Point): 25 return Point(self.x + other.x, self.y + other.y) 26 else: 27 return NotImplemented 28 29 def __mul__(self, factor): 30 """Multiplication on the right is not implemented.""" 31 # This overrides the default behaviour of a tuple multiplied by a constant 32 # on the right, which does not make sense for a Point. 33 return NotImplemented 34 35 def __rmul__(self, factor): 36 """Multiply a point by a scalar factor on the left, e.g. 2 * p.""" 37 return Point(factor * self.x, factor * self.y) 38 39 40class Rectangle( 41 collections.namedtuple('Rectangle', ['top_left', 'bottom_right'])): 42 """Object to represent a rectangle on a surface. 43 44 Args: 45 top_left: A pair of (left, top) coordinates. Might be given as a Point 46 or as a two-element sequence (list, tuple, etc.). 47 bottom_right: A pair (right, bottom) coordinates. 48 """ 49 __slots__ = () 50 51 def __new__(cls, top_left, bottom_right): 52 if not isinstance(top_left, Point): 53 top_left = Point(*top_left) 54 if not isinstance(bottom_right, Point): 55 bottom_right = Point(*bottom_right) 56 return super(Rectangle, cls).__new__(cls, top_left, bottom_right) 57 58 def __str__(self): 59 """Get a useful string representation of the object.""" 60 return '[%s, %s]' % (self.top_left, self.bottom_right) 61 62 @property 63 def center(self): 64 """Get the point at the center of the rectangle.""" 65 return 0.5 * (self.top_left + self.bottom_right) 66 67 @classmethod 68 def FromDict(cls, d): 69 """Create a rectangle object from a dictionary. 70 71 Args: 72 d: A dictionary (or mapping) of the form, e.g., {'top': 0, 'left': 0, 73 'bottom': 1, 'right': 1}. 74 """ 75 return cls(Point(d['left'], d['top']), Point(d['right'], d['bottom'])) 76