Please note: This wiki is currently running in test mode after an attack on January 5 2013. All passwords were reset, so you will have to use the password recovery function to get a new password. To edit wiki pages, please log in first. See the wiki attack description page for more details. If you find problems, please report them to the pydotorg-www mailing list.

Points & Rectangles

A pair of classes to provide points and rectangles.

Surprisingly, I haven't been able to find a single Python module providing such primitive support.

WxPython supports wxPoint and wxRect, but it lacks many basic functions (such as, say, adding two points together to produce a third point..!) (See: wxPyWiki:wx.Rect)

This code is lacking a zillion essential features (but interpoint distance can now be calculated). I only put in the ones I needed immediately. Please add, refactor, optimize, rename stuff to be more standard, etc., as you see fit..!

If there's an actual, accessible, easy-to-include Python module, not tied to a graphics library, that does this stuff already, please write about it here! No sense in reinventing the wheel. I've looked, but haven't found one. Hence this.

```   1 """Point and Rectangle classes.
2
3 This code is in the public domain.
4
5 Point  -- point with (x,y) coordinates
6 Rect  -- two points, forming a rectangle
7 """
8
9 import math
10
11
12 class Point:
13
14     """A point identified by (x,y) coordinates.
15
16     supports: +, -, *, /, str, repr
17
18     length  -- calculate length of vector to point from origin
19     distance_to  -- calculate distance between two points
20     as_tuple  -- construct tuple (x,y)
21     clone  -- construct a duplicate
22     integerize  -- convert x & y to integers
23     floatize  -- convert x & y to floats
24     move_to  -- reset x & y
25     slide  -- move (in place) +dx, +dy, as spec'd by point
26     slide_xy  -- move (in place) +dx, +dy
27     rotate  -- rotate around the origin
28     rotate_about  -- rotate around another point
29     """
30
31     def __init__(self, x=0.0, y=0.0):
32         self.x = x
33         self.y = y
34
36         """Point(x1+x2, y1+y2)"""
37         return Point(self.x+p.x, self.y+p.y)
38
39     def __sub__(self, p):
40         """Point(x1-x2, y1-y2)"""
41         return Point(self.x-p.x, self.y-p.y)
42
43     def __mul__( self, scalar ):
44         """Point(x1*x2, y1*y2)"""
45         return Point(self.x*scalar, self.y*scalar)
46
47     def __div__(self, scalar):
48         """Point(x1/x2, y1/y2)"""
49         return Point(self.x/scalar, self.y/scalar)
50
51     def __str__(self):
52         return "(%s, %s)" % (self.x, self.y)
53
54     def __repr__(self):
55         return "%s(%r, %r)" % (self.__class__.__name__, self.x, self.y)
56
57     def length(self):
58         return math.sqrt(self.x**2 + self.y**2)
59
60     def distance_to(self, p):
61         """Calculate the distance between two points."""
62         return (self - p).length()
63
64     def as_tuple(self):
65         """(x, y)"""
66         return (self.x, self.y)
67
68     def clone(self):
69         """Return a full copy of this point."""
70         return Point(self.x, self.y)
71
72     def integerize(self):
73         """Convert co-ordinate values to integers."""
74         self.x = int(self.x)
75         self.y = int(self.y)
76
77     def floatize(self):
78         """Convert co-ordinate values to floats."""
79         self.x = float(self.x)
80         self.y = float(self.y)
81
82     def move_to(self, x, y):
83         """Reset x & y coordinates."""
84         self.x = x
85         self.y = y
86
87     def slide(self, p):
88         '''Move to new (x+dx,y+dy).
89
90         Can anyone think up a better name for this function?
91         slide? shift? delta? move_by?
92         '''
93         self.x = self.x + p.x
94         self.y = self.y + p.y
95
96     def slide_xy(self, dx, dy):
97         '''Move to new (x+dx,y+dy).
98
99         Can anyone think up a better name for this function?
100         slide? shift? delta? move_by?
101         '''
102         self.x = self.x + dx
103         self.y = self.y + dy
104
107
108         Positive y goes *up,* as in traditional mathematics.
109
110         Interestingly, you can use this in y-down computer graphics, if
111         you just remember that it turns clockwise, rather than
112         counter-clockwise.
113
114         The new position is returned as a new Point.
115         """
116         s, c = [f(rad) for f in (math.sin, math.cos)]
117         x, y = (c*self.x - s*self.y, s*self.x + c*self.y)
118         return Point(x,y)
119
121         """Rotate counter-clockwise around a point, by theta degrees.
122
123         Positive y goes *up,* as in traditional mathematics.
124
125         The new position is returned as a new Point.
126         """
127         result = self.clone()
128         result.slide(-p.x, -p.y)
129         result.rotate(theta)
130         result.slide(p.x, p.y)
131         return result
132
133
134 class Rect:
135
136     """A rectangle identified by two points.
137
138     The rectangle stores left, top, right, and bottom values.
139
140     Coordinates are based on screen coordinates.
141
142     origin                               top
143        +-----> x increases                |
144        |                           left  -+-  right
145        v                                  |
146     y increases                         bottom
147
148     set_points  -- reset rectangle coordinates
149     contains  -- is a point inside?
150     overlaps  -- does a rectangle overlap?
151     top_left  -- get top-left corner
152     bottom_right  -- get bottom-right corner
153     expanded_by  -- grow (or shrink)
154     """
155
156     def __init__(self, pt1, pt2):
157         """Initialize a rectangle from two points."""
158         self.set_points(pt1, pt2)
159
160     def set_points(self, pt1, pt2):
161         """Reset the rectangle coordinates."""
162         (x1, y1) = pt1.as_tuple()
163         (x2, y2) = pt2.as_tuple()
164         self.left = min(x1, x2)
165         self.top = min(y1, y2)
166         self.right = max(x1, x2)
167         self.bottom = max(y1, y2)
168
169     def contains(self, pt):
170         """Return true if a point is inside the rectangle."""
171         x,y = pt.as_tuple()
172         return (self.left <= x <= self.right and
173                 self.top <= y <= self.bottom)
174
175     def overlaps(self, other):
176         """Return true if a rectangle overlaps this rectangle."""
177         return (self.right > other.left and self.left < other.right and
178                 self.top < other.bottom and self.bottom > other.top)
179
180     def top_left(self):
181         """Return the top-left corner as a Point."""
182         return Point(self.left, self.top)
183
184     def bottom_right(self):
185         """Return the bottom-right corner as a Point."""
186         return Point(self.right, self.bottom)
187
188     def expanded_by(self, n):
189         """Return a rectangle with extended borders.
190
191         Create a new rectangle that is wider and taller than the
192         immediate one. All sides are extended by "n" points.
193         """
194         p1 = Point(self.left-n, self.top-n)
195         p2 = Point(self.right+n, self.bottom+n)
196         return Rect(p1, p2)
197
198     def __str__( self ):
199         return "<Rect (%s,%s)-(%s,%s)>" % (self.left,self.top,
200                                            self.right,self.bottom)
201
202     def __repr__(self):
203         return "%s(%r, %r)" % (self.__class__.__name__,
204                                Point(self.left, self.top),
205                                Point(self.right, self.bottom))
```

Historical Note

It seems that Python version 1.0.2 had standard module rect!