#!/usr/local/bin/python
# coding: iso-8859-1
# Copyright © 2008 by Amos Newcombe
# 
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.

'''A class for multidimensional vectors, with arithmetic, and rotation.
'''

from math import sqrt, cos, sin, atan2

class Vector:
	'''A class that implements vectors as ordered tuples in any (integral) dimension.
	
	Note that Vector(3) returns (0, 0, 0), but Vector(3, 3) returns (3, 3).
	'''
	 
	 # ¤ Creation
	 
	def __init__(self, *tp):
# 		print '%s%s' % (self.__class__.__name__, tp)
		if len(tp) == 1:
			if   isinstance(tp[0], (tuple, list)): tp = tp[0]
			elif isinstance(tp[0], (int  , long)): tp = (0,) * tp[0]
			elif isinstance(tp[0], Vector       ): tp = tp[0].tuple()
		self.tpx = tuple([float(x) for x in tp])
	
	def tuple(self): return self.tpx
	def list(self) : return list(self.tpx)
	
	def copy(self): return self.__class__(self.tuple())
	
	# ¤ Description
	
	def __str__ (self): return str(self.tuple())
	def __repr__(self): return '%s%s' % (self.__class__.__name__, `self.tuple()`)
	def __len__ (self): return len(self.tuple())
	
	# ¤ Arithmetic
	
	def __add__(self, other):
		return self.__class__([xS + xO for (xS, xO) in zip(self.tuple(), other.tuple())])
  	
	def __sub__(self, other):
		return self.__class__([xS - xO for (xS, xO) in zip(self.tuple(), other.tuple())])
	
	def __neg__(self):
		return self.__class__([-xS for xS in self.tuple()])
	
	def __mul__(self, xMul):
		return self.__class__([xS*xMul for xS in self.tuple()])
	
	def __rmul__(self, xMul): return self.__mul__(xMul)
	
	def __div__(self, xDiv):
		return self.__class__([xS/xDiv for xS in self.tuple()])
	
	# ¤ Operations
	
	def min(self, other):
		return self.__class__([min(xS, xO) for (xS, xO) in zip(self.tuple(), other.tuple())])
	
	def max(self, other):
		return self.__class__([max(xS, xO) for (xS, xO) in zip(self.tuple(), other.tuple())])
	
	def dot(self, other):
		return sum([xS * xO for (xS, xO) in zip(self.tuple(), other.tuple())])
	
	def length(self): return sqrt(self.dot(self))
	
	def Distance   (self, other): return (other-self).length()
	def MaxDistance(self, other): return max([abs(x) for x in (other-self).tuple()])
	
	def Rotate(self, xAngle, vPerp):
# 		print 'Rotate(%s, %s)' % (xAngle, vPerp)
		return cos(xAngle)*self + sin(xAngle)*vPerp

class Vector2D(Vector):
	
	def __init__(self, tp): 
		Vector.__init__(self, tp)
		n = len(self)
		if n != 2: 
			raise ValueError, 'A %s takes 2 components, not %d' % (self.__class__.__name__, n)
	
	def Rotate(self, xAngle):
		return Vector.Rotate(self, xAngle, self.Perpendicular())
	
	def Perpendicular(self):
		xX, xY = self.tuple()
		return self.__class__((-xY, xX))
	
	def Toward(self, other):
		vDiff = other - self
		return atan2(vDiff[0], vDiff[1])
