"""Vector.py Robust Vector class for Python Some functions are designed to work with POV-Ray """ __author__ = "Josh English (english@spiritone.com)" __version__ = "1.22" __date__ = "2004/07/19" __history__ = """ 1.1 - added component-wise addition and multiplication 1.2 - added features to in init function - added a unit test framework for the Vector class - added promotion of vectors (permanently) in addition, subtraction, and component-wise multiplication 1.21 - added a component-wise division method 1.22 - fixed problems with magnitude (it used to return 1 for 0-length vectors). it now raises an error fixed vector.norm and vector.normalize to return """ class VectorError(Exception): pass class InputError(VectorError): pass def Determinant(a,b,c,d): return a*d-b*c class Vector: def __init__(self,*initlist): initlist = list(initlist) if len(initlist)==0: self.data=[0,0,0] elif len(initlist)==1 and type(initlist[0])==type([]): self.data=initlist[0] elif len(initlist)==1 and type(initlist[0])==type(()): self.data = list(initlist[0]) else: self.data = initlist try: self.data = map(float,self.data) except: raise InputError, self.data def __repr__(self): return "%s:%s" % (self.__class__.__name__,repr(self.data)) def promote(self,d): """promotes the vector to a higher dimension, padding the right with 0's""" if d > len(self): self.data.extend([0]*(d-len(self))) def __len__(self): return len(self.data) def __add__(self,other): from operator import add if isinstance(other,Vector): if len(self) < len(other): self.promote(len(other)) elif len(self) < len(other): other.promote(len(self)) else: other = [other]*len(self) return self.__class__(map(add,self,other)) def __sub__(self,other): from operator import sub if isinstance(other,Vector): if len(self) < len(other): self.promote(len(other)) elif len(self) < len(other): other.promote(len(self)) else: other = [other]*len(self) return self.__class__(map(sub,self,other)) def __mul__(self,n): from operator import mul if isinstance(n,Vector): if len(self) < len(n): self.promote(len(n)) if len(self) > len(n): n.promote(len(self)) return self.__class__(map(mul,self,n)) else: return self.__class__([i*n for i in self.data]) def __rmul__(self,n): return self.__mul__(n) def __div__(self,n): from operator import div if isinstance(n,Vector): if len(self) < len(n):self.promote(len(n)) if len(self) > len(n):n.promote(len(self)) try: return self.__class__(map(div,self,n)) except ZeroDivisionError: raise VectorError, "Cannot divide by zero" else: if n: return self.__class__([i/n for i in self.data]) else: raise VectorError, "Cannot divide by zero" def __rdiv__(self,n): return self.__div__(n) def __eq__(self,other): from operator import eq if isinstance(other,Vector): if len(other)==len(self): #res = reduce(and_,map(eq,self.data,other.data)) res = eq(self.data,other.data) else: res = 0 else: res=0 return res ### The __ne__ method must exist for unit testing. def __ne__(self,other): return not self.__eq__(other) def __getitem__(self,obj): return self.data.__getitem__(obj) def string(self): return repr(self.data).replace('[','<').replace(']','>') def mag(self): """vec.mag() Returns the magnitude of the vector. """ res = reduce(lambda a,b:a+b,map(lambda a:a*a,self.data)) #if not res: raise VectorError, "Zero-Vector has no magnitude" return pow(res,0.5) def norm(self): """vec.norm() Returns a normalized equivalent of the vector. """ m = self.mag() if m: return self.__class__([i/m for i in self.data]) else: raise VectorError, "Cannot Normalize zero-length vector" # or self.__class__(map(lambda i:i/m,self.data)) def normalize(self): """vec.normalize() Normalizes the vector, changing it's values. """ m = self.mag() if m: self.data = [i/m for i in self.data] else: raise VectorError, "Cannot normalize zero-length vector" def vmul(self,other): """vec.vmul(other) Does component-wise multiplication""" from operator import mul if isinstance(other,Vector): if len(self.data)==len(other.data): return self.__class__(map(mul,self,other)) else: raise VectorError, "Vector dimensions must be equal in component-wise multiplication" else: raise VectorError, "Can't compute component-wise multiplication with non-vector" def dot(self,other): """vec.vdot(vec) Finds the dot product bewteen two vectors. """ if isinstance(other,Vector): if len(self.data) == len(other.data): res = 0 for i in range(len(self.data)): res += self.data[i]*other.data[i] return res else: raise VectorError, "Cannot compute dot product of vectors with different dimensions" else: raise VectorError, "Cannot compute dot product of vectors with non-vector" def cross(self,other): if isinstance(other,Vector): if len(self.data)==len(other.data)==3: r0=self.data[1]*other.data[2]-self.data[2]*other.data[1] r1=self.data[2]*other.data[0]-self.data[0]*other.data[2] r2=self.data[0]*other.data[1]-self.data[1]*other.data[0] return self.__class__([r0,r1,r2]) else: raise VectorError, "Cross products can only be calculated with d3 vectors" def angle(self,other): """Returns an angle in degrees""" from math import acos,pi if isinstance(other,Vector): return acos(vdot(self.norm(),other.norm()))*180/pi else: return None def scalar(self,other): """v.scalar(u) Finds the scalar projection of u onto v. """ if isinstance(other,Vector): return self.dot(other)/self.mag() else: return self def project(self,other): """v.project(u) Finds the vector projection of u onto v """ if isinstance(other,Vector): return self.scalar(other)*self.norm() else: return self def cmin(self,other): """v.cmin(other) Returns a new vector after component wise minimum comparision. """ if isinstance(other,Vector): if len(self.data)==len(other.data): res = [] for i in range(len(self.data)): res.append(min(self.data[i],other.data[i])) return self.__class__(res) else: return self else: return self def cmax(self,other): """v.cmax(other) Returns a new vector after component wise minimum comparision. """ if isinstance(other,Vector): if len(self.data)==len(other.data): res = [] for i in range(len(self.data)): res.append(max(self.data[i],other.data[i])) return self.__class__(res) else: return self else: return self def povstring(self): """returns a string representation of self fit for POV-Ray.""" s0 = '%.6f ' * len(self) % tuple(self.data) return '<%s>' % (', '.join(s0.split())) def vdot(v1,v2): if len(v1)==len(v2): res = 0 for i in range(len(v1)): res += v1.data[i]*v2.data[i] return res def vcross(v1,v2): if len(v1)==len(v2)==3: return v1.cross(v2) else: raise VectorError, "Cross products can only be calculated with d3 vectors"