Differences between revisions 10 and 16 (spanning 6 versions)
Revision 10 as of 2004-11-26 19:15:59
Size: 4826
Editor: rba-cache1-vif1
Comment: Added rotation methods for Point()
Revision 16 as of 2008-11-15 13:59:40
Size: 7944
Editor: localhost
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]]

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!

See Also

PointsAndRectangles (last edited 2008-11-15 13:59:40 by localhost)

Unable to edit the page? See the FrontPage for instructions.