# Commits

committed 6ba43a1

Added documentation to primitives module. Minor fix to Circle width/height calculation.

• Participants
• Parent commits 2c2e9b1

# File djlib/primitives.py

`+""" Primitives Module`
`+`
`+Module containing basic primitives for use with graphics, physics, and other`
`+maths calculations. Each primitive has methods to aid in interactions between`
`+them, as well as instance methods for generating objects based on numerous`
`+construction data.`
`+`
`+Support Primitives:`
`+    Vector - Standard geometric vector, the heart of all game calculations.`
`+    Point - Alias for `Vector` representing position data.`
`+    Size - Alias for `Vector` representing dimension.`
`+    Entity - Base class for any object containing positional data.`
`+    Rect - Bounding Rectangle containing position and size.`
`+    Circle - Bounding Circle containing position and radius.`
`+"""`
` `
` # IMPORTS`
` import math`
` from copy import copy`
` `
` class Vector(list):`
`+    """ Standard geometric vector, the heart of all game calculations.`
`+`
`+    This Vector is designed to handle an arbitrary number of dimensions, though`
`+    2D and 3D are most likely. Internal components are stored as a `list` for `
`+    mutability reasons. Since this class is the base of most game operations,`
`+    care should be taken to ensure its methods are as efficent`
`+    as possible.`
`+`
`+    Attributes:`
`+        x,y,z - Direct component access is provided for the first 3 dimensions.`
`+    """`
` `
`     _ATTR_STRING = "xyz"`
` `
`     def __init__(self, *coords):`
`         list.__init__(self, list(coords))`
` `
`+    ############`
`+    # Operators`
`+    ############`
`+`
`     def __add__(self, other_vec):`
`+        """ Add two Vectors, returning a new Vector. """`
`         return Vector(*[x+y for x,y in zip(self, other_vec)])`
` `
`     def __iadd__(self, other_vec):`
`+        """ Add two Vectors together, modifying this instance. """`
`         for i in xrange(len(self)):`
`             self[i] += other_vec[i]`
`         return self`
` `
`     def __sub__(self, other_vec):`
`+        """ Subtract two Vectors, returning a new Vector. """`
`         return Vector(*[x-y for x,y in zip(self, other_vec)])`
` `
`     def __isub__(self, other_vec):`
`+        """ Subtract two Vectors, modifying this instance. """`
`         for i in xrange(len(self)):`
`             self[i] -= other_vec[i]`
`         return self`
` `
`     def __mul__(self, vec_or_scale):`
`+        """ Scale Vector or calculate Dot Poduct, returning a new Vector`
`+`
`+        Result of Vector multiplication changes based on type of `vec_or_scale`.`
`+        If a Vector is specify, the dot product of the two Vectors are`
`+        calculate. If a scalar value is used, the Vector is scaled.`
`+`
`+        Args:`
`+            vec_or_scale(Vector) - Second Vector to calculate Dot Product`
`+            vec_or_scale(scalar) - Any scalar value (int, float, double)`
`+        """`
`         if isinstance(vec_or_scale, Vector):`
`             return self.dot(vec_or_scale)`
`         return self.scaled(vec_or_scale)`
` `
`     def __imul__(self, scale):`
`+        """ Scale Vector, modifying this instance. """`
`         for i in xrange(len(self)):`
`             self[i] *= scale`
`         return self`
` `
`     def __neg__(self):`
`+        """ Reverse Vector, returning a new Vector. """`
`         return Vector(*[-x for x in self])`
` `
`     def __eq__(self, other_vec):`
`+        """ Return whether two Vectors are the same. """`
`         for x,y in zip(self, other_vec):`
`             if x != y:`
`                 return False`
`         return "<"+", ".join(str(x) for x in self)+">"`
` `
`     def __getattr__(self, attr):`
`-        # print "get",attr`
`+        """ Access Vector components based on _ATTR_STRING location. """`
`         return self[self._ATTR_STRING.index(attr)]`
` `
`     def __setattr__(self, attr, value):`
`-        # print "set",attr,value`
`+        """ Set Vector components based on _ATTR_STRING location. """`
`         idx = self._ATTR_STRING.find(attr)`
`         if idx < 0:`
`             list.__setattr__(self, attr, value)`
`         return hash(str(self))`
` `
`     def length(self):`
`+        """ Return length of Vector. """`
`         return math.sqrt(sum([x*x for x in self]))`
` `
`     def scaled(self, scale):`
`+        """ Scale Vector, returning new Vector. """`
`         return Vector(*[x*scale for x in self])`
` `
`     def normalized(self):`
`+        """ Normalize Vector, returning a new Vector. """`
`         length = self.length()`
`         if length:`
`             return Vector(*[x/length for x in self])`
`         return copy(self)`
` `
`     def dot(self, other_vec):`
`+        """ Calculate the Dot Product of two Vectors. """`
`         return sum([x*y for x,y in zip(self, other_vec)])`
` `
`     def args(self):`
`+        """ Return Vector components as a tuple.`
`+`
`+        Aids transition to functions expecting Vector data as individual`
`+        components. `
`+        """`
`         return tuple(self)`
`     `
`     def intArgs(self):`
`+        """ Return Vector components as a tuple, truncated to integers.`
`+`
`+        Most pygame graphical operations expect position data as integers.`
`+        This aids in using Vectors for those operations.`
`+        """`
`         return tuple([int(x) for x in self])`
` `
`     def clear(self):`
`+        """ Set Vector to origin. """`
`         for i in xrange(len(self)):`
`             self[i] = 0`
` `
`     def distanceApart(self, other_vec):`
`+        """ Calculate distance between two Vectors. """`
`         return (self - other_vec).length()`
`     `
`     def interpolate(self, other_vec, d):`
`+        """ Perform a linear interpolation along this Vector. """`
`         diff = other_vec - self`
`         return self + diff.scaled(d)`
` `
`     def copy(self):`
`+        """ Create a copy of this Vector. """`
`         return copy(self)`
`     `
` #end Vector`
` `
` class Point(Vector):`
`+    """ Alias for `Vector` representing position data.`
`+`
`+    Exists only to allow better code comprehension and introspection.`
`+    """`
`     pass`
` #end Point`
` `
` class Size(Vector):`
`+    """ Alias for `Vector` representing dimension.`
`+`
`+    Exists only to allow better code comprehension and introspection.`
`+    Overides `_ATTR_STRING` to allow component access based on `
`+    Width, Height, Length.`
`+    """`
`     _ATTR_STRING = "whl"`
` #end Size`
` `
` class Entity:`
`+    """ Base class for any object containing positional data.`
`+`
`+    Created to allow for more uniform position access across all`
`+    Bounding Volumes and more advance physical objects.`
`+    """`
`     def __init__(self, position=[0,0]):`
`         self.pos = position`
` `
` #end Entity`
` `
` class BoundingVolume(Entity):`
`+    """ Base class for various Bounding Volumes. `
`+`
`+    All Volumes should have a position, and therefore are derived from Entity.`
`+    """`
` `
`     def contains(self, entity):`
`+        """ Return whether this Volume contains another `Entity`.`
`+`
`+        Args:`
`+            entity(Entity, BoundingVolume) - Arbitrary Entity or BoundingVolume`
`+                that should be tested for containment. Support types may vary.`
`+        """`
`         return self.pos == entity.pos`
` `
`     def center(self):`
`+        """ Return Point representing the center of this Volume. """`
`         return self.pos`
` `
`     def offset(self, offset_x, offset_y):`
`+        """ Return an offset Volume that varies based on type. """`
`         return self.pos+Vector(offset_x, offset_y)`
` `
`     def width(self):`
`+        """ Return computed Width of Volume. """`
`         return 0`
` `
`     def height(self):`
`+        """ Return computed Height of Volume. """`
`         return 0`
`     `
` #end BoundingVolume`
` `
` class Rect(BoundingVolume):`
`+    """ An axix-aligned bounding Rectangle that has both a position and size. """`
` `
`     # CLASS METHODS`
`     @classmethod`
`     def fromPoints(cls, top_left, bottom_right):`
`+        """ Create Rectangle from top-left and bottom-right points. """`
`         return Rect(top_left, bottom_right - top_left)`
` `
`     @classmethod`
`     def fromPointSize(cls, vec_pos, width, height):`
`+        """ Create Rectangle from top-left Point and Size. """`
`         return Rect(vec_pos, Vector(width, height))`
` `
`     @classmethod`
`     def fromSides(cls, left, top, right, bottom):`
`+        """ Create Rectangle from left, top, right, bottom values. """`
`         return Rect(Vector(left, top), Vector(right-left, bottom-top))`
` `
`     # INSTANCE METHODS`
`         self.size = size`
` `
`     def contains(self, entity):`
`+        """ Return whether this Rect contains another `BoundingVolume`.`
`+`
`+        Args:`
`+            entity (BoundingVolume) - Supports `Vector`, `Rect`, and `Circle``
`+`
`+        Raises:`
`+            NotImplementedError - Raises on unsupported BoundingVolume type.`
`+        """`
`         bottom_right = self.pos + self.size`
`         `
`         # Vector`
`         return self.pos + self.size.scaled(0.5)`
` `
`     def corners(self):`
`+        """ Return tuple of (top-left, top-right, bottom-left, bottom-right) points. """`
`         return (self.pos.copy(), self.pos+Vector(self.size[0], 0), self.pos+Vector(0, self.size[1]), self.pos+self.size)`
` `
`     def sides(self):`
`+        """ Return tuple of (left, top, right, bottom) values. """`
`         return (self.pos[0], self.pos[1], self.pos[0]+self.size[0], self.pos[1]+self.size[1])`
` `
`     def intersects(self, rect):`
`+        """ Return whether Rect intersects or contains another Rect. """`
`+        if not isinstance(rect, Rect):`
`+            raise NotImplementedError`
`+`
`         ours = self.corners()`
`         for corner in ours:`
`             if rect.contains(corner):`
`         return False`
` `
`     def offset(self, offset_x, offset_y):`
`+        """ Return expanded/contracted Rect based on offsets. """`
`         o = Vector(offset_x, offset_y)`
`         return Rect.fromPoints(self.pos-o, self.size+o)`
` `
` #end Rect`
` `
` class Circle(BoundingVolume):`
`+    """ A bounding Circle with both position and size. """`
` `
`     # CLASS METHODS`
`     @classmethod`
`     def fromPoints(cls, center, circum):`
`+        """ Create Circle from a center and tangential `Point`. """`
`         return Circle(center, (circum - center).length())`
` `
`     @classmethod`
`     def fromPointSize(cls, position, radius):`
`+        """ Create Circle from a center position and radius. """`
`         return Circle(position, radius)`
` `
`     # INSTANCE METHODS`
`         self.radius = radius`
` `
`     def pointOnCircle(self, rad):`
`+        """ Return a tangential point on circle at specified radian. """`
`         ray = Vector(math.cos(rad), math.sin(rad)).scaled(self.radius)`
`         return self.pos + ray`
` `
`     def contains(self, entity):`
`+        """ Return whether this Rect contains another `BoundingVolume`.`
`+`
`+        Args:`
`+            entity(BoundingVolume) - Supports `Vector`, `Rect`, and `Circle` types`
`+`
`+        Raises:`
`+            NotImplementedError - Raises on unsupported BoundingVolume type.`
`+        """`
`         # Vector`
`         if isinstance(entity, Vector):`
`             return (self.pos - entity).length() <= self.radius`
`         raise NotImplementedError`
` `
`     def offset(self, offset_x, offset_y):`
`+        """ Return Circle with expanded/contracted radius. """`
`         return Circle(self.pos, offset_x+offset_y)`
` `
`     def width(self):`
`-        return self.radius`
`+        return self.radius*2`
` `
`     def height(self):`
`-        return self.radius`
`+        return self.radius*2`
`         `
` #end Circle`