# Python maths

This is kind of a code dump for all code written today, being a vector class, unfinished matrix class and some other Python utilities.

None of this is actually maya dependent and written & tested in Eclipse Indigo with PyDev using Python 3.2.2.

Update October 15: fixed vector/matrix multiplication and added a boundingbox class with helper functions for things as getting all corners as points.

__init__.py

```'''
@package: Vmath
@author: Trevor van Hoof

Custom package with mathematical utilities
Requires python.math
'''
```

listutils.py

```'''
@package: Vmath
@author: Trevor van Hoof

List utility functions
'''

def sum( in_list ):
#collapse the list by adding all values together
out = 0
for i in in_list:
out += i
return out

def sub( in_list ):
#collapse the list by subtracting all values from 0
out = 0
for i in in_list:
out -= i
return out

def avg( in_list ):
#get the average in a list
out = 0
for i in in_list:
out += i
return out / len(in_list)
```

matrix.py

```'''
@package: Vmath
@author: Trevor van Hoof

Matrix class: mostly unfinished and untested, but vector multiplication works in Maya
'''

from math import sqrt
from math import sin
from math import cos
from Vmath.vec import Vec
from Vmath import listutils

class Matrix:
def __init__(self,*args):
self.data = []
if not len(args): #nothing given, init a 3x3 unit matrix
self.data = [1,0,0,0,1,0,0,0,1]
self.w = 3
self.h = 3
elif len(args) in [2,3]:
self.w = args[0]
self.h = args[1]
if len(args) == 2: #given are the dimensions only, init to 0
self.data = (args[0]*args[1])*[0]
elif len(args) == 3: #given are the dimensions and one or more default values
if not hasattr(args[2],'__getitem__'): #not iterable
args[2] = [args[2]]
#iterate & loop args[3] until size x,y is reached
for y in range(args[1]):
for x in range(args[0]):
self.data.append( args[2][ (y*args[0]+x)%len(args[2]) ] )
else: #given are only values, try to establish a matrix of equal dimensions, WARNING: data may be discarded
if hasattr(args[0],'__getitem__'): #is iterable
args = args[0]
d = int(sqrt(len(args)))
self.w = d
self.h = d
for x in range(d):
for y in range(d):
self.data.append(args[x*d+y])

def __getitem__(self,i):
if hasattr(i,'__getitem__') and len(i) == 2:
i = i[1] * self.w + i[0]
return self.data[i]

def __setitem__(self,i,v):
if len(i) == 2:
i = i[1] * self.w + i[0]
self.data[i] = v

def __mul__(self,other):
if other.__class__ == Vec:
#vector multiplication
out = Vec([0]*self.h)
if len(other) < self.w: #default extra coordinates to 1
other.extend([1]*(self.w-len(other)))
for y in range(self.h):
for x in range(self.w):
out[x] += other[y] * self.data[x+y*self.w]
return out

if other.__class__ == Matrix:
#allow only matrices of matching size
#if other.h != self.w and other.w != self.h:
#    return None
out = []
for j in range(self.h):
for i in range(other.w):
othercolumn = []
for k in range(other.h):
othercolumn.append(other[k*other.w+i])
out.append( listutils.sum( Vec(self.data[j*self.w:(j+1)*self.w])*Vec(othercolumn) ) )

return Matrix(self.h,other.w,out)

@classmethod
def rotation(cls,rx,ry,rz):
return cls.rotatez(rz) * cls.rotatey(ry) * cls.rotatex(rx)

@classmethod
def rotatex(cls,ro):
sx = sin(ro)
cx = cos(ro)
return Matrix( [1,0,0,0,cx,-sx,0,sx,cx] )

@classmethod
def rotatey(cls,ro):
sy = sin(ro)
cy = cos(ro)
return Matrix( [cy,0,sy,0,1,0,-sy,0,cy] )

@classmethod
def rotatez(cls,ro):
sz = sin(ro)
cz = cos(ro)
return Matrix( [cz,-sz,0,sz,cz,0,0,0,1] )

def __repr__(self):
return str(self.data)
```

vec.py

```'''
@package: Vmath
@author: Trevor van Hoof

Vector class
'''

from __future__ import division
from math import sqrt
from Vmath import listutils

class Vec():
def __init__(self,*args):
if len(args) > 1: #init with values given
self.data = list(args)
elif hasattr(args[0],'__getitem__'): #init with iterable given
self.data = args[0]
else: #init with length given
self.data = [0]*args[0]

@classmethod
def null(cls):
return cls(0,0,0)
@classmethod
def x(cls):
return cls(1,0,0)
@classmethod
def y(cls):
return cls(0,1,0)
@classmethod
def z(cls):
return cls(0,0,1)

def __getitem__(self,i):
return self.data[i]

def __setitem__(self,i,v):
self.data[i] = v

def __mul__(self,other):
other = self._getOtherAsVec(other)
out = []
for i in range(len(self.data)):
out.append(self.data[i]*other.data[i%len(other.data)])
return Vec(out)

def __div__(self,other):
return self.__truediv__(other)

def __truediv__(self,other):
other = self._getOtherAsVec(other)
out = []
for i in range(len(self.data)):
out.append(self.data[i]/other.data[i%len(other.data)])
return Vec(out)

def __floordiv__(self,other):
other = self._getOtherAsVec(other)
out = []
for i in range(len(self.data)):
out.append(self.data[i]//other.data[i%len(other.data)])
return Vec(out)

other = self._getOtherAsVec(other)
out = []
for i in range(len(self.data)):
out.append(self.data[i]+other.data[i%len(other.data)])
return Vec(out)

def __sub__(self,other):
other = self._getOtherAsVec(other)
out = []
for i in range(len(self.data)):
out.append(self.data[i]-other.data[i%len(other.data)])
return Vec(out)

def __mod__(self,other):
other = self._getOtherAsVec(other)
out = []
for i in range(len(self.data)):
out.append(self.data[i]%other.data[i%len(other.data)])
return Vec(out)

def __neg__(self):
out = []
for i in self.data:
out.append(-i)
return Vec(out)

def __abs__(self):
out = []
for i in self.data:
if i < 0:
i = -i
out.append(i)
return Vec(out)

def __invert__(self):
self.data = self.__neg__()

def __lt__(self,other):
#all values must be less
other = self._getOtherAsVec(other)
otherlen = len(other.data)
self._lengthWarning(len(self.data),otherlen)
for i in range(len(self.data)):
if self.data[i] >= other.data[i%otherlen]:
return False
return True

def __gt__(self,other):
#all values must be greater
other = self._getOtherAsVec(other)
otherlen = len(other.data)
self._lengthWarning(len(self.data),otherlen)
for i in range(len(self.data)):
if self.data[i] <= other.data[i%otherlen]:
return False
return True

def __eq__(self,other):
#all values must be equal
other = self._getOtherAsVec(other)
if len(other.data) != len(self.data):
return False
for i in range(len(self.data)):
if other.data[i] != self.data[i]:
return False
return True

def __le__(self,other):
other = self._getOtherAsVec(other)
otherlen = len(other.data)
self._lengthWarning(len(self.data),otherlen)
for i in range(len(self.data)):
if self.data[i] > other.data[i%otherlen]:
return False
return True

def __ge__(self,other):
other = self._getOtherAsVec(other)
otherlen = len(other.data)
self._lengthWarning(len(self.data),otherlen)
for i in range(len(self.data)):
if self.data[i] < other.data[i%otherlen]:
return False
return True

def __ne__(self,other):
#any value can be not equal
return not self.__eq__(other)

def _getOtherAsVec(self,other):
if type(other) != Vec:
if hasattr(other,'__getitem__'):
other = Vec(other)
else:
other = Vec([other])
return other

@classmethod
def getAsVec(self,in_data):
if type(in_data) != Vec:
if hasattr(in_data,'__getitem__'):
out = Vec(in_data)
else:
out = Vec([in_data])
else:
return in_data
return out

def _lengthWarning(self,selflen,otherlen):
if otherlen < selflen:
print('Warning: dimension mismatch, looping data on right side of Vec < Vec operation')
#Warning: too little data in X when checking Vec < X, data re-used from the beginning
elif selflen < otherlen:
print('Warning: dimension mismatch, right side of Vec < Vec had excessive dimensions ignored')
#Warning: too much data in X when checking Vec < X, extra data ignored

def __repr__(self):
return repr(self.data)

def __len__(self):
return len(self.data)

def append(self,in_data):
#WARNING: works like list append, changes internal data only
self.data.append(in_data)

def extend(self,in_data):
#WARNING: works like list extend, changes internal data only
self.data.extend(in_data[:])

def normalize(self):
#WARNING: this changes internal data, to get a copy use normalized()
self.data = self.normalized().data

def normalized(self):
out = []
m = 1 / self.mag()
for i in self.data:
out.append(i * m)
return Vec(out)

def mag(self):
return sqrt(self.sqr())

def length(self):
return self.mag()

def sqr(self):
out = 0
for i in self.data:
out += i*i
return out

def dot(self,other):
out = self.__mul__(other)
return listutils.sum( out.data )

def cross(self,other):
other = self._getOtherAsVec(other)
out = [self.data[1] * other.data[2] - other.data[1] * self.data[2],
self.data[2] * other.data[0] - other.data[2] * self.data[0],
self.data[0] * other.data[1] - other.data[0] * self.data[1]]
return Vec(out)

print(Vec(1,1,1) >= Vec(0,0,0))
```

boundingbox.py

```'''
@package: Vmath
@author: Trevor van Hoof

BoundingBox class
'''
from Vmath.vec import Vec

class BoundingBox:
'''
BoundingBox class, may be object space
'''
def __init__(self, in_min, in_max):
self.min = Vec.getAsVec(in_min)
self.max = Vec.getAsVec(in_max)
if len(self.min) != len(self.max):
raise ValueError('Bounding box min/max points have a different\
number of dimensions; creation failed')

def cornerPoints(self):
x = [self.min[0],self.max[0]]
y = [self.min[1],self.max[1]]
z = [self.min[2],self.max[2]]
out = []
for j in range(2):
for i in range(4):
out.append( Vec(x[int(i*0.5)], y[bool(i%3)], z[j]) )
return out

def center(self):
return (self.max-self.min)*0.5+self.min

def width(self):
return self.max[0]-self.min[0]

def height(self):
return self.max[1]-self.min[1]

def depth(self):
return self.max[2]-self.min[2]

def dimensions(self):
return self.max-self.min

def contains(self, in_pt):
pt = Vec.getAsVec(in_pt)

if pt > self.min and pt < self.max:
return True
return False

def extend(self, in_pt):
pt = Vec.getAsVec(in_pt)
ptlen = len(pt)

for i in range(len(self.min)):
if self.min[i] > pt[i%ptlen]:
self.min[i] = pt[i%ptlen]
elif self.max[i] < pt[i%ptlen]:
self.max[i] = pt[i%ptlen]

def asWorldSpace(self):
'''
Returns the world space bounding box of this bounding box
Return value may be identical if data already was in world space
'''
outmin = []
outmax = []
for pt in self.cornerPoints():
if not outmin:
outmin = pt
outmax = pt
continue
for i in range(3):
if pt[i] < outmin[i]:
outmin[i] = pt[i]
if pt[i] > outmax[i]:
outmax[i] = pt[i]
return BoundingBox(outmin,outmax)

def __repr__(self):
return repr(self.asList())

def asList(self):
tmp = self.min[:]
tmp.extend(self.max)
return tmp
```