pytilities.geometry.vector

Package pytilities :: Package geometry :: Module vector |
[hide private]
[frames] | no frames]

# Source Code for Module pytilities.geometry.vector

      1  # Copyright (C) 2010 Tim Diels <limyreth@users.sourceforge.net>
      2  #
      3  # This file is part of pytilities.
      4  #
      5  # pytilities is free software: you can redistribute it and/or modify
      6  # it under the terms of the GNU General Public License as published by
      7  # the Free Software Foundation, either version 3 of the License, or
      8  # (at your option) any later version.
      9  #
     10  # pytilities is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  # GNU General Public License for more details.
     14  #
     15  # You should have received a copy of the GNU General Public License
     16  # along with pytilities.  If not, see <http://www.gnu.org/licenses/>.
     17  #
     18
     19  __docformat__ = 'reStructuredText'
     20
     21  import operator
     22  import math
     23
     24  from pytilities.types import NumberType
     25  from pytilities.delegation import delegated, delegator_factory
     26  from pytilities.overloading import overloaded, Overload, Param
     27
     28
     29 -class _Storage(object):
     30 -    def __init__(self, x, y):
     31          # Can't be a string as we must avoid nastiness. Consider for example
     32          # what would happen when you add vectors ('1', 0) and ('0', 0), results
     33          # in ('10', 0)
     34          assert not isinstance(x, basestring)
     35          assert not isinstance(y, basestring)
     36          self.x = x
     37          self.y = y
     38
     39
     40  # NOTE: be careful with the awesome ways python int vs float math works
     41  # As with statically typed languages, even here 5 / 2 == 2. As values do still
     42  # have types. So, try not to mix ints and floats.
     43  # If you'd rather not bother casting and keeping that sort of stuff in your
     44  # mind, I recommend adding another Vector that wraps round this one and casts
     45  # or asserts (if you are a bit of a performance freak ^^) all input.
     46  @delegator_factory()
     47 -class Vector(object):
     48
     49      """
     50      2D Point/Vector
     51
     52      Class fields:
     53
     54          - `INFINITY`: immutable Vector of positive infinity
     55          - `NULL`: immutable Vector (0, 0)
     56
     57      Instance methods:
     58
     59          - `move_to`: Set x, y position
     60          - `move_by`: Move vector by x, y
     61          - `assign`: Assign values of other vector to this one
     62          - `copy`: Shallow copy
     63          - `normalize`: Normalize vector
     64          - `normalized`: Get a normalized copy of this vector
     65          - `dot`: Dot product
     66          - `cross`:
     67          - `reflect`:
     68
     69      Instance properties:
     70
     71          - `x`: Read-write, x position
     72          - `y`: Read-write, y position
     73          - `length`: Read-write, length of vector
     74          - `length_squared`: Read-only, length squared
     75
     76      Operators:
     77
     78          str(s)
     79          s == v
     80          s != v
     81          s + v
     82          s += v
     83          s - v
     84          s -= v
     85          s * n
     86          s *= n
     87          s / n
     88          s /= n
     89          -s
     90              self explanatory
     91
     92          len(s)
     93              returns 2
     94
     95          iter(s)
     96              iterates over its x and y value
     97
     98          abs(s)
     99              returns length of vector
    100      """
    101
    102      @staticmethod
    103 -    def __init_delegation_profiles(profiles):
    104          profiles['default'] |= profiles['immutable']
    105
    106 -    def __init_xy(self, x, y):
    107          self.__storage = _Storage(x, y)
    108
    109 -    def __init_storage(self, storage):
    110          self.__storage = storage
    111
    112      @overloaded((
    113          Overload(__init_xy,
    114              Param("x", NumberType, default=0),
    115              Param("y", NumberType, default=0)),
    116          Overload(__init_storage,
    117              Param("storage"))))
    118 -    def __init__(self):
    119          """
    120          Constructs a 2D Vector
    121
    122          Overloaded, parameters:
    123
    124          :a:
    125              x :: float | int = 0
    126              y :: float | int = 0
    127
    128          :b:
    129              storage :: object(x, y)
    130                  an object that provides storage for the x and y values
    131          """
    132          pass
    133
    134      @delegated()
    135      @delegated("immutable", modifiers="r")
    136      @property
    137 -    def x(self):
    138          """Read-write, x position ::float | int"""
    139          return self.__storage.x
    140
    141      @x.setter
    142 -    def x(self, value):
    143          self.__storage.x = value
    144
    145      @delegated()
    146      @delegated("immutable", modifiers="r")
    147      @property
    148 -    def y(self):
    149          """Read-write, y position ::float | int"""
    150          return self.__storage.y
    151
    152      @y.setter
    153 -    def y(self, value):
    154          self.__storage.y = value
    155
    156      @delegated()
    157 -    def move_to(self, x, y):
    158          """
    159          Set x, y coords
    160
    161          Parameters:
    162
    163              `x` :: float | int
    164                  x position
    165
    166              `y` :: float | int
    167                  y position
    168          """
    169          self.x, self.y = x, y
    170
    171      @delegated()
    172 -    def move_by(self, x, y):
    173          """
    174          Move vector by (x, y)
    175
    176          Parameters:
    177
    178              `x` :: float | int
    179                  x position
    180
    181              `y` :: float | int
    182                  y position
    183          """
    184          self.x += x
    185          self.y += y
    186
    187      @delegated()
    188 -    def assign(self, v):
    189          """
    190          Assigns the values of another vector to this vector
    191
    192          v :: Vector -- the other vector
    193          """
    194          self.x = v.x
    195          self.y = v.y
    196
    197      @delegated("immutable")
    198 -    def __str__( self ):
    199          return "<%s (%s,%s)>" % (self.__class__.__name__,
    200                                   self.x, self.y)
    201
    202      @delegated("immutable")
    203 -    def __copy__(self):
    204          return Vector(self.x, self.y)
    205
    206      @delegated("immutable")
    207 -    def copy(self):
    208          """Returns shallow copy :: Vector"""
    209          return self.__copy__()
    210
    211      @delegated("immutable")
    212 -    def __eq__(self, other):
    213          """other :: Vector"""
    214          return self.x == other.x and \
    215                 self.y == other.y
    216
    217      @delegated("immutable")
    218 -    def __neq__(self, other):
    219          """other :: Vector"""
    220          return not self.__eq__(other)
    221
    222      @delegated("immutable")
    223 -    def __len__(self):
    224          return 2
    225
    226      @delegated("immutable")
    227 -    def __getitem__(self, key):
    228          return (self.x, self.y)[key]
    229
    230      @delegated()
    231 -    def __setitem__(self, key, value):
    232          (self.x, self.y)[key] = value
    233
    234      @delegated("immutable")
    235 -    def __iter__(self):
    236          """Iterates over its x and y value"""
    237          return iter((self.x, self.y))
    238
    239      @delegated("immutable")
    240 -    def __getattr__(self, name):
    241          try:
    242              return tuple([(self.x, self.y)['xy'.index(c)] \
    243                            for c in name])
    244          except ValueError:
    245              raise AttributeError, name
    246
    247      @delegated("immutable")
    248 -    def __add__(self, other):
    249          """other :: Vector"""
    250          v = self.copy()
    251          v += other
    252          return v
    253
    254      @delegated()
    255 -    def __iadd__(self, other):
    256          """other :: Vector"""
    257          self.x += other.x
    258          self.y += other.y
    259          return self
    260
    261      @delegated("immutable")
    262 -    def __sub__(self, other):
    263          return self + (-other)
    264
    265      @delegated("immutable")
    266 -    def __mul__(self, other):
    267          """other :: number"""
    268          v = self.copy()
    269          v *= other
    270          return v
    271
    272      @delegated("immutable")
    273 -    def __rmul__(self, other):
    274          """other :: number"""
    275          return self * other
    276
    277      @delegated()
    278 -    def __imul__(self, other):
    279          """other :: number"""
    280          self.x *= other
    281          self.y *= other
    282          return self
    283
    284      @delegated("immutable")
    285 -    def __div__(self, other):
    286          """other :: number"""
    287          return Vector(operator.div(self.x, other),
    288                         operator.div(self.y, other))
    289
    290      @delegated("immutable")
    291 -    def __rdiv__(self, other):
    292          """other :: number"""
    293          return Vector(operator.div(other, self.x),
    294                         operator.div(other, self.y))
    295
    296      @delegated("immutable")
    297 -    def __floordiv__(self, other):
    298          """other :: number"""
    299          return Vector(operator.floordiv(self.x, other),
    300                         operator.floordiv(self.y, other))
    301
    302      @delegated("immutable")
    303 -    def __rfloordiv__(self, other):
    304          """other :: number"""
    305          return Vector(operator.floordiv(other, self.x),
    306                         operator.floordiv(other, self.y))
    307
    308      @delegated("immutable")
    309 -    def __truediv__(self, other):
    310          """other :: number"""
    311          return Vector(operator.truediv(self.x, other),
    312                         operator.truediv(self.y, other))
    313
    314
    315      @delegated("immutable")
    316 -    def __rtruediv__(self, other):
    317          """other :: number"""
    318          return Vector(operator.truediv(other, self.x),
    319                         operator.truediv(other, self.y))
    320
    321      @delegated("immutable")
    322 -    def __neg__(self):
    323          return Vector(-self.x, -self.y)
    324
    325      @delegated("immutable")
    326 -    def __pos__(self):
    327          return self.copy()
    328
    329      @delegated("immutable")
    330 -    def __abs__(self):
    331          return math.sqrt(self.x ** 2 + \
    332                           self.y ** 2)
    333
    334      @delegated()
    335      @property
    336 -    def length(self):
    337          """
    338          Read-write, length of vector
    339
    340          Returns :: float | int
    341
    342          Set parameters:
    343
    344              value :: number
    345                  new length
    346          """
    347          return abs(self)
    348
    349      @length.setter
    350 -    def length(self, value):
    351          d = self.length * value
    352          self *= value
    353
    354      @delegated("immutable")
    355      @property
    356 -    def length_squared(self):
    357          """Read-only, length squared :: float | int"""
    358          return self.x ** 2 + \
    359                 self.y ** 2
    360
    361      @delegated()
    362 -    def normalize(self):
    363          """
    364          Normalize vector
    365
    366          Returns :: Vector
    367          """
    368          d = self.length
    369
    370          if d:
    371              self /= d
    372
    373          return self
    374
    375      @delegated("immutable")
    376 -    def normalized(self):
    377          """
    378          Get a normalized copy of this vector
    379
    380          Returns :: Vector
    381          """
    382          v = self.copy()
    383          return v.normalize()
    384
    385      @delegated("immutable")
    386 -    def dot(self, other):
    387          """
    388          Get the dot product of this vector with `other`
    389
    390          Parameters:
    391
    392              `other` :: Vector
    393                  the other vector
    394
    395          Returns :: float | int
    396          """
    397          return self.x * other.x + \
    398                 self.y * other.y
    399
    400      @delegated("immutable")
    401 -    def cross(self):
    402          # TODO: doc this ya lazy git
    403          return Vector(self.y, -self.x)
    404
    405      @delegated("immutable")
    406 -    def reflect(self, normal):
    407          # TODO: doc this ya lazy git
    408          # assume normal is normalized
    409          d = 2 * (self.x * normal.x + self.y * normal.y)
    410          return Vector(self.x - d * normal.x,
    411                         self.y - d * normal.y)
    412
    413
    414
    415  from .immutablevector import ImmutableVector
    416
    417  Vector.INFINITY = ImmutableVector(Vector(float("inf"), float("inf")))
    418  Vector.NULL = ImmutableVector(Vector(0, 0)) # saves us some vector spawning
    419