Size: 4856
Comment: Removes tabs to make indenting correct within Point().Rotate()
|
← Revision 16 as of 2008-11-15 13:59:40 ⇥
Size: 7944
Comment: converted to 1.6 markup
|
Deletions are marked like this. | Additions are marked like this. |
Line 3: | Line 3: |
<<TableOfContents>> |
|
Line 9: | Line 11: |
WxPython supports wxPoint and wxRect, but it lacks many basic functions (such as, say, adding two points together to produce a third point..!) | WxPython supports wxPoint and wxRect, but it lacks many basic functions (such as, say, adding two points together to produce a third point..!) (See: [[http://wiki.wxpython.org/index.cgi/wx_2eRect|wxPyWiki:wx.Rect]]) |
Line 17: | Line 19: |
# Code is Public Domain. import math # required because of 'sqrt' in 'distanceTo' function # also tan and atan for rotations. def normalize(x1, y1, x2, y2): '''I am not sure what this is for, care to comment?''' return min(x1,x2), min(y1,y2), max(x1,x2), max(y1,y2) |
"""Point and Rectangle classes. This code is in the public domain. Point -- point with (x,y) coordinates Rect -- two points, forming a rectangle """ import math |
Line 27: | Line 31: |
def __init__(self, x, y): | """A point identified by (x,y) coordinates. supports: +, -, *, /, str, repr length -- calculate length of vector to point from origin distance_to -- calculate distance between two points as_tuple -- construct tuple (x,y) clone -- construct a duplicate integerize -- convert x & y to integers floatize -- convert x & y to floats move_to -- reset x & y slide -- move (in place) +dx, +dy, as spec'd by point slide_xy -- move (in place) +dx, +dy rotate -- rotate around the origin rotate_about -- rotate around another point """ def __init__(self, x=0.0, y=0.0): |
Line 30: | Line 52: |
def __add__(self, other): return Point(self.x+other.x, self.y+other.y) def __sub__(self, other): return Point(self.x-other.x, self.y-other.y) |
def __add__(self, p): """Point(x1+x2, y1+y2)""" return Point(self.x+p.x, self.y+p.y) def __sub__(self, p): """Point(x1-x2, y1-y2)""" return Point(self.x-p.x, self.y-p.y) |
Line 35: | Line 62: |
"""Point(x1*x2, y1*y2)""" | |
Line 36: | Line 64: |
Line 37: | Line 66: |
"""Point(x1/x2, y1/y2)""" | |
Line 38: | Line 68: |
Line 40: | Line 71: |
Line 42: | Line 74: |
def XY(self): '''Returns a tuple (x,y).''' return self.x,self.y def Clone(self): '''Returns a full copy of this point.''' |
def length(self): return math.sqrt(self.x**2 + self.y**2) def distance_to(self, p): """Calculate the distance between two points.""" return (self - p).length() def as_tuple(self): """(x, y)""" return (self.x, self.y) def clone(self): """Return a full copy of this point.""" |
Line 48: | Line 89: |
def Integerize(self): '''Rounds co-ordinate values to integers.''' |
def integerize(self): """Convert co-ordinate values to integers.""" |
Line 52: | Line 94: |
def Floatize(self): '''Converts co-ordinate values to floating point (is this necessary?)''' |
def floatize(self): """Convert co-ordinate values to floats.""" |
Line 56: | Line 99: |
def DistanceTo(self,pt): '''Distance between 2 points.''' dy = self.y - pt.y dx = self.x - pt.x return sqrt(dy*dy + dx*dx) def Move(newx,newy): '''Changes coordinates to those given.''' self.x = newx self.y = newy def MoveDelta(dx,dy): '''Move to new (x+dx,y+dy)''' |
def move_to(self, x, y): """Reset x & y coordinates.""" self.x = x self.y = y def slide(self, p): '''Move to new (x+dx,y+dy). Can anyone think up a better name for this function? slide? shift? delta? move_by? ''' self.x = self.x + p.x self.y = self.y + p.y def slide_xy(self, dx, dy): '''Move to new (x+dx,y+dy). Can anyone think up a better name for this function? slide? shift? delta? move_by? ''' |
Line 69: | Line 122: |
def Rotate(self,theta): '''Clockwise rotation about origin, theta in Degrees. Returns the new position as Point()''' # Err..this math is not exactly readable. # It is elementary though, involving only Pythogoras and basic trig. r2 = sqr(self.x)+sqr(self.y) tmp = math.tan(theta*math.pi/90 + math.atan(self.x/self.y)) ret = Point() ret.x = math.sqrt(r2/(1+tmp*tmp)) ret.y = ret.x * tmp return ret def RotateAbout(self,OtherPoint,theta): '''Clockwise rotation about another point. Returns a Point() at new position''' tmpx = OtherPoint.x tmpy = OtherPoint.y self.MoveDelta(-tmpx,-tmpy) tmpPoint = self.Rotate(theta) tmpPoint.MoveDelta(tmpx,tmpy) return tmpPoint |
def rotate(self, rad): """Rotate counter-clockwise by rad radians. Positive y goes *up,* as in traditional mathematics. Interestingly, you can use this in y-down computer graphics, if you just remember that it turns clockwise, rather than counter-clockwise. The new position is returned as a new Point. """ s, c = [f(rad) for f in (math.sin, math.cos)] x, y = (c*self.x - s*self.y, s*self.x + c*self.y) return Point(x,y) def rotate_about(self, p, theta): """Rotate counter-clockwise around a point, by theta degrees. Positive y goes *up,* as in traditional mathematics. The new position is returned as a new Point. """ result = self.clone() result.slide(-p.x, -p.y) result.rotate(theta) result.slide(p.x, p.y) return result |
Line 90: | Line 153: |
"""A rectangle identified by two points. The rectangle stores left, top, right, and bottom values. Coordinates are based on screen coordinates. origin top +-----> x increases | | left -+- right v | y increases bottom set_points -- reset rectangle coordinates contains -- is a point inside? overlaps -- does a rectangle overlap? top_left -- get top-left corner bottom_right -- get bottom-right corner expanded_by -- grow (or shrink) """ |
|
Line 91: | Line 175: |
self.Set(pt1, pt2) def Contains(self, pt): x,y = pt.XY() return self.left <= x <= self.right and self.top <= y <= self.bottom def Set( self, pt1, pt2 ): extrema = normalize(pt1.x, pt1.y, pt2.x, pt2.y) self.left, self.top, self.right, self.bottom = extrema def Overlaps(self, other): return (self.right > other.left and self.left < other.right and self.top < other.bottom and self.bottom > other.top) def GetTL(self): |
"""Initialize a rectangle from two points.""" self.set_points(pt1, pt2) def set_points(self, pt1, pt2): """Reset the rectangle coordinates.""" (x1, y1) = pt1.as_tuple() (x2, y2) = pt2.as_tuple() self.left = min(x1, x2) self.top = min(y1, y2) self.right = max(x1, x2) self.bottom = max(y1, y2) def contains(self, pt): """Return true if a point is inside the rectangle.""" x,y = pt.as_tuple() return (self.left <= x <= self.right and self.top <= y <= self.bottom) def overlaps(self, other): """Return true if a rectangle overlaps this rectangle.""" return (self.right > other.left and self.left < other.right and self.top < other.bottom and self.bottom > other.top) def top_left(self): """Return the top-left corner as a Point.""" |
Line 103: | Line 201: |
def GetBR(self): | def bottom_right(self): """Return the bottom-right corner as a Point.""" |
Line 105: | Line 205: |
def ExpandedBy(self, n): p1 = Point(self.left-n, self.top+n) |
def expanded_by(self, n): """Return a rectangle with extended borders. Create a new rectangle that is wider and taller than the immediate one. All sides are extended by "n" points. """ p1 = Point(self.left-n, self.top-n) |
Line 109: | Line 215: |
def TransformedByFunction(self, foo): p1 = Point(self.left, self.top) p2 = Point(self.right, self.bottom) return Rect(foo(p1), foo(p2)) |
|
Line 116: | Line 219: |
Line 122: | Line 226: |
== Historical Note == It seems that [[http://www.informatik.hu-berlin.de/Themen/manuals/python/python-texinfo/top.html|Python version 1.0.2]] had [[http://www.informatik.hu-berlin.de/Themen/manuals/python/python-texinfo/module_index.html|standard module]] [[http://www.informatik.hu-berlin.de/Themen/manuals/python/python-texinfo/rect.html|rect!]] == See Also == * [[http://en.wikipedia.org/wiki/Cartesian_coordinate_system|Wikipedia:Cartesian coordinate system]] * [[http://en.wikipedia.org/wiki/Vector_%28spatial%29|Wikipedia:Vector]] |
Contents
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
35 def __add__(self, p):
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
105 def rotate(self, rad):
106 """Rotate counter-clockwise by rad radians.
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
120 def rotate_about(self, p, theta):
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!