Use @property for properties
[joel/kofoto.git] / src / packages / kofoto / rectangle.py
1 """
2 This module contains the Rectangle class.
3 """
4
5 class Rectangle:
6     """A class representing a rectangle with width and height.
7
8     The width and height are available as instance properties r.width
9     and r.height for an instance r, and also as r[0] and r[1]. The
10     rectangle can thus be cast to a (width, height) tuple with
11     tuple(r).
12
13     Other operations that are available are:
14
15     r1 == r2
16     r1 != r2
17     r1 * n   (where n is an integer)
18     n * r1   (where n is an integer)
19     r1 / n   (where n is an integer)
20     r1 // n  (where n is an integer)
21     hash(r1)
22     r.copy()
23     r1.downscaled_to(r2)
24     r1.fits_within(r2)
25     r.max()
26     r.min()
27     r1.rescaled_to(r2)
28     """
29
30     def __init__(self, width, height):
31         """Constructor.
32
33         Arguments:
34
35         width        -- The width.
36         height       -- The height.
37         """
38
39         self._width = width
40         self._height = height
41
42     # ----------------------------------
43
44     def __div__(self, factor):
45         return self.__class__(self._width / factor, self._height / factor)
46
47     def __eq__(self, other):
48         try:
49             return self._width == other[0] and self._height == other[1]
50         except (TypeError, IndexError):
51             return False
52
53     def __ne__(self, other):
54         return not (self == other)
55
56     def __floordiv__(self, factor):
57         return self.__class__(self._width // factor, self._height // factor)
58
59     def __getitem__(self, item):
60         if item == 0:
61             return self._width
62         elif item == 1:
63             return self._height
64         else:
65             raise IndexError
66
67     def __hash__(self):
68         return hash((self._width, self._height))
69
70     def __len__(self):
71         return 2
72
73     def __mul__(self, factor):
74         return self.__class__(self._width * factor, self._height * factor)
75
76     def __repr__(self):
77         return "Rectangle(%r, %r)" % (self._width, self._height)
78
79     def __rmul__(self, factor):
80         return self * factor
81
82     # ----------------------------------
83
84     @property
85     def width(self):
86         return self._width
87
88     @property
89     def height(self):
90         return self._height
91
92     # ----------------------------------
93
94     def copy(self):
95         """Get a copy of the rectangle."""
96
97         return self.__class__(self._width, self._height)
98
99     def downscaled_to(self, limit):
100         """Scale the rectangle down to fit within a given limit.
101
102         Returns the downscaled rectangle.
103         """
104
105         w = self._width
106         h = self._height
107         if w > limit[0]:
108             h = limit[0] * h // w
109             w = limit[0]
110         if h > limit[1]:
111             w = limit[1] * w // h
112             h = limit[1]
113         w = max(1, w)
114         h = max(1, h)
115         return self.__class__(w, h)
116
117     def fits_within(self, limit):
118         """Check whether the rectangle fits within a limit.
119
120         Arguments:
121
122         limit        -- A tuple (width, height) or a Rectangle instance.
123         """
124
125         return self._width <= limit[0] and self._height <= limit[1]
126
127     def max(self):
128         """Get the maximum of the rectangle's width and height."""
129
130         return max(self._width, self._height)
131
132     def min(self):
133         """Get the minimum of the rectangle's width and height."""
134
135         return min(self._width, self._height)
136
137     def rescaled_to(self, limit):
138         """Scale the rectangle up or down to fit within a given limit.
139
140         Returns the rescaled rectangle.
141         """
142
143         w = limit[0]
144         h = limit[0] * self._height // self._width
145         if h > limit[1]:
146             w = limit[1] * w // h
147             h = limit[1]
148         w = max(1, w)
149         h = max(1, h)
150         return self.__class__(w, h)