"""
Vector3 is a three dimensional vector class.

Below are examples of Vector3 use.

>>> from vector3 import Vector3
>>> origin = Vector3()
>>> origin
0.0, 0.0, 0.0
>>> pythagoras = Vector3( 3, 4, 0 )
>>> pythagoras
3.0, 4.0, 0.0
>>> pythagoras.magnitude()
5.0
>>> pythagoras.magnitudeSquared()
25
>>> triplePythagoras = pythagoras * 3.0
>>> triplePythagoras
9.0, 12.0, 0.0
>>> plane = pythagoras.dropAxis()
>>> plane
(3+4j)
"""

from __future__ import absolute_import
try:
	import psyco
	psyco.full()
except:
	pass
#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
import __init__

from fabmetheus_utilities import xml_simple_writer
import math
import operator


__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://forums.reprap.org/profile.php?12,28>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GPL 3.0'


class Vector3:
	'A three dimensional vector class.'
	__slots__ = ['x', 'y', 'z']

	def __init__(self, x=0.0, y=0.0, z=0.0):
		self.x = x
		self.y = y
		self.z = z

	def __abs__(self):
		'Get the magnitude of the Vector3.'
		return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z )

	magnitude = __abs__

	def __add__(self, other):
		'Get the sum of this Vector3 and other one.'
		return Vector3( self.x + other.x, self.y + other.y, self.z + other.z )

	def __copy__(self):
		'Get the copy of this Vector3.'
		return Vector3( self.x, self.y, self.z )

	__pos__ = __copy__

	copy = __copy__

	def __div__(self, other):
		'Get a new Vector3 by dividing each component of this one.'
		return Vector3( self.x / other, self.y / other, self.z / other )

	def __eq__(self, other):
		'Determine whether this vector is identical to other one.'
		if other == None:
			return False
		if other.__class__ != self.__class__:
			return False
		return self.x == other.x and self.y == other.y and self.z == other.z

	def __floordiv__(self, other):
		'Get a new Vector3 by floor dividing each component of this one.'
		return Vector3( self.x // other, self.y // other, self.z // other )

	def _getAccessibleAttribute(self, attributeName):
		'Get the accessible attribute.'
		if attributeName in globalGetAccessibleAttributeSet:
			return getattr(self, attributeName, None)
		return None

	def __hash__(self):
		'Determine whether this vector is identical to other one.'
		return self.__repr__().__hash__()

	def __iadd__(self, other):
		'Add other Vector3 to this one.'
		self.x += other.x
		self.y += other.y
		self.z += other.z
		return self

	def __idiv__(self, other):
		'Divide each component of this Vector3.'
		self.x /= other
		self.y /= other
		self.z /= other
		return self

	def __ifloordiv__(self, other):
		'Floor divide each component of this Vector3.'
		self.x //= other
		self.y //= other
		self.z //= other
		return self

	def __imul__(self, other):
		'Multiply each component of this Vector3.'
		self.x *= other
		self.y *= other
		self.z *= other
		return self

	def __isub__(self, other):
		'Subtract other Vector3 from this one.'
		self.x -= other.x
		self.y -= other.y
		self.z -= other.z
		return self

	def __itruediv__(self, other):
		'True divide each component of this Vector3.'
		self.x = operator.truediv( self.x, other )
		self.y = operator.truediv( self.y, other )
		self.z = operator.truediv( self.z, other )
		return self

	def __mul__(self, other):
		'Get a new Vector3 by multiplying each component of this one.'
		return Vector3( self.x * other, self.y * other, self.z * other )

	def __ne__(self, other):
		'Determine whether this vector is not identical to other one.'
		return not self.__eq__(other)

	def __neg__(self):
		return Vector3( - self.x, - self.y, - self.z )

	def __nonzero__(self):
		return self.x != 0 or self.y != 0 or self.z != 0

	def __repr__(self):
		'Get the string representation of this Vector3.'
		return '(%s, %s, %s)' % ( self.x, self.y, self.z )

	def __rdiv__(self, other):
		'Get a new Vector3 by dividing each component of this one.'
		return Vector3( other / self.x, other / self.y, other / self.z )

	def __rfloordiv__(self, other):
		'Get a new Vector3 by floor dividing each component of this one.'
		return Vector3( other // self.x, other // self.y, other // self.z )

	def __rmul__(self, other):
		'Get a new Vector3 by multiplying each component of this one.'
		return Vector3( self.x * other, self.y * other, self.z * other )

	def __rtruediv__(self, other):
		'Get a new Vector3 by true dividing each component of this one.'
		return Vector3( operator.truediv( other , self.x ), operator.truediv( other, self.y ), operator.truediv( other, self.z ) )

	def _setAccessibleAttribute(self, attributeName, value):
		'Set the accessible attribute.'
		if attributeName in globalSetAccessibleAttributeSet:
			setattr(self, attributeName, value)

	def __sub__(self, other):
		'Get the difference between the Vector3 and other one.'
		return Vector3( self.x - other.x, self.y - other.y, self.z - other.z )

	def __truediv__(self, other):
		'Get a new Vector3 by true dividing each component of this one.'
		return Vector3( operator.truediv( self.x, other ), operator.truediv( self.y, other ), operator.truediv( self.z, other ) )

	def cross(self, other):
		'Calculate the cross product of this vector with other one.'
		return Vector3(self.y * other.z - self.z * other.y, -self.x * other.z + self.z * other.x, self.x * other.y - self.y * other.x)

	def distance(self, other):
		'Get the Euclidean distance between this vector and other one.'
		return math.sqrt( self.distanceSquared(other) )

	def distanceSquared(self, other):
		'Get the square of the Euclidean distance between this vector and other one.'
		separationX = self.x - other.x
		separationY = self.y - other.y
		separationZ = self.z - other.z
		return separationX * separationX + separationY * separationY + separationZ * separationZ

	def dot(self, other):
		'Calculate the dot product of this vector with other one.'
		return self.x * other.x + self.y * other.y + self.z * other.z

	def dropAxis( self, which = 2 ):
		'Get a complex by removing one axis of the vector3.'
		if which == 0:
			return complex( self.y, self.z )
		if which == 1:
			return complex( self.x, self.z )
		if which == 2:
			return complex( self.x, self.y )

	def getFloatList(self):
		'Get the vector as a list of floats.'
		return [ float( self.x ), float( self.y ), float( self.z ) ]

	def getIsDefault(self):
		'Determine if this is the zero vector.'
		if self.x != 0.0:
			return False
		if self.y != 0.0:
			return False
		return self.z == 0.0

	def getNormalized(self):
		'Get the normalized Vector3.'
		magnitude = abs(self)
		if magnitude == 0.0:
			return self.copy()
		return self / magnitude

	def magnitudeSquared(self):
		'Get the square of the magnitude of the Vector3.'
		return self.x * self.x + self.y * self.y + self.z * self.z

	def maximize(self, other):
		'Maximize the Vector3.'
		self.x =max(other.x, self.x)
		self.y =max(other.y, self.y)
		self.z =max(other.z, self.z)

	def minimize(self, other):
		'Minimize the Vector3.'
		self.x =min(other.x, self.x)
		self.y =min(other.y, self.y)
		self.z =min(other.z, self.z)

	def normalize(self):
		'Scale each component of this Vector3 so that it has a magnitude of 1. If this Vector3 has a magnitude of 0, this method has no effect.'
		magnitude = abs(self)
		if magnitude != 0.0:
			self /= magnitude

	def reflect( self, normal ):
		'Reflect the Vector3 across the normal, which is assumed to be normalized.'
		distance = 2 * ( self.x * normal.x + self.y * normal.y + self.z * normal.z )
		return Vector3( self.x - distance * normal.x, self.y - distance * normal.y, self.z - distance * normal.z )

	def setToVector3(self, other):
		'Set this Vector3 to be identical to other one.'
		self.x = other.x
		self.y = other.y
		self.z = other.z

	def setToXYZ( self, x, y, z ):
		'Set the x, y, and z components of this Vector3.'
		self.x = x
		self.y = y
		self.z = z


globalGetAccessibleAttributeSet = 'x y z'.split()
globalSetAccessibleAttributeSet = globalGetAccessibleAttributeSet

"""
class Vector3:
	__slots__ = ['x', 'y', 'z']

	def __init__(self, x, y, z):
		self.x = x
		self.y = y
		self.z = z

	def __copy__(self):
		return self.__class__(self.x, self.y, self.z)

	copy = __copy__

	def __repr__(self):
		return 'Vector3(%.2f, %.2f, %.2f)' % (self.x,
											  self.y,
											  self.z)

	def __eq__(self, other):
		if isinstance(other, Vector3):
			return self.x == other.x and \
				   self.y == other.y and \
				   self.z == other.z
		else:
			assert hasattr(other, '__len__') and len(other) == 3
			return self.x == other[0] and \
				   self.y == other[1] and \
				   self.z == other[2]

	def __ne__(self, other):
		return not self.__eq__(other)

	def __nonzero__(self):
		return self.x != 0 or self.y != 0 or self.z != 0

	def __len__(self):
		return 3

	def __getitem__(self, key):
		return (self.x, self.y, self.z)[key]

	def __setitem__(self, key, value):
		l = [self.x, self.y, self.z]
		l[key] = value
		self.x, self.y, self.z = l

	def __iter__(self):
		return iter((self.x, self.y, self.z))

	def __getattr__(self, name):
		try:
			return tuple([(self.x, self.y, self.z)['xyz'.index(c)] \
						  for c in name])
		except ValueError:
			raise AttributeError, name

	if _enable_swizzle_set:
		# This has detrimental performance on ordinary setattr as well
		# if enabled
		def __setattr__(self, name, value):
			if len(name) == 1:
				object.__setattr__(self, name, value)
			else:
				try:
					l = [self.x, self.y, self.z]
					for c, v in map(None, name, value):
						l['xyz'.index(c)] = v
					self.x, self.y, self.z = l
				except ValueError:
					raise AttributeError, name


	def __add__(self, other):
		if isinstance(other, Vector3):
			# Vector + Vector -> Vector
			# Vector + Point -> Point
			# Point + Point -> Vector
			if self.__class__ is other.__class__:
				_class = Vector3
			else:
				_class = Point3
			return _class(self.x + other.x,
						  self.y + other.y,
						  self.z + other.z)
		else:
			assert hasattr(other, '__len__') and len(other) == 3
			return Vector3(self.x + other[0],
						   self.y + other[1],
						   self.z + other[2])
	__radd__ = __add__

	def __iadd__(self, other):
		if isinstance(other, Vector3):
			self.x += other.x
			self.y += other.y
			self.z += other.z
		else:
			self.x += other[0]
			self.y += other[1]
			self.z += other[2]
		return self

	def __sub__(self, other):
		if isinstance(other, Vector3):
			# Vector - Vector -> Vector
			# Vector - Point -> Point
			# Point - Point -> Vector
			if self.__class__ is other.__class__:
				_class = Vector3
			else:
				_class = Point3
			return Vector3(self.x - other.x,
						   self.y - other.y,
						   self.z - other.z)
		else:
			assert hasattr(other, '__len__') and len(other) == 3
			return Vector3(self.x - other[0],
						   self.y - other[1],
						   self.z - other[2])

   
	def __rsub__(self, other):
		if isinstance(other, Vector3):
			return Vector3(other.x - self.x,
						   other.y - self.y,
						   other.z - self.z)
		else:
			assert hasattr(other, '__len__') and len(other) == 3
			return Vector3(other.x - self[0],
						   other.y - self[1],
						   other.z - self[2])

	def __mul__(self, other):
		if isinstance(other, Vector3):
			# TODO component-wise mul/div in-place and on Vector2; docs.
			if self.__class__ is Point3 or other.__class__ is Point3:
				_class = Point3
			else:
				_class = Vector3
			return _class(self.x * other.x,
						  self.y * other.y,
						  self.z * other.z)
		else: 
			assert type(other) in (int, long, float)
			return Vector3(self.x * other,
						   self.y * other,
						   self.z * other)

	__rmul__ = __mul__

	def __imul__(self, other):
		assert type(other) in (int, long, float)
		self.x *= other
		self.y *= other
		self.z *= other
		return self

	def __div__(self, other):
		assert type(other) in (int, long, float)
		return Vector3(operator.div(self.x, other),
					   operator.div(self.y, other),
					   operator.div(self.z, other))


	def __rdiv__(self, other):
		assert type(other) in (int, long, float)
		return Vector3(operator.div(other, self.x),
					   operator.div(other, self.y),
					   operator.div(other, self.z))

	def __floordiv__(self, other):
		assert type(other) in (int, long, float)
		return Vector3(operator.floordiv(self.x, other),
					   operator.floordiv(self.y, other),
					   operator.floordiv(self.z, other))


	def __rfloordiv__(self, other):
		assert type(other) in (int, long, float)
		return Vector3(operator.floordiv(other, self.x),
					   operator.floordiv(other, self.y),
					   operator.floordiv(other, self.z))

	def __truediv__(self, other):
		assert type(other) in (int, long, float)
		return Vector3(operator.truediv(self.x, other),
					   operator.truediv(self.y, other),
					   operator.truediv(self.z, other))


	def __rtruediv__(self, other):
		assert type(other) in (int, long, float)
		return Vector3(operator.truediv(other, self.x),
					   operator.truediv(other, self.y),
					   operator.truediv(other, self.z))
	
	def __neg__(self):
		return Vector3(-self.x,
						-self.y,
						-self.z)

	__pos__ = __copy__
	
	def __abs__(self):
		return math.sqrt(self.x ** 2 + \
						 self.y ** 2 + \
						 self.z ** 2)

	magnitude = __abs__

	def magnitude_squared(self):
		return self.x ** 2 + \
			   self.y ** 2 + \
			   self.z ** 2

	def normalize(self):
		d = self.magnitude()
		if d:
			self.x /= d
			self.y /= d
			self.z /= d
		return self

	def normalized(self):
		d = self.magnitude()
		if d:
			return Vector3(self.x / d, 
						   self.y / d, 
						   self.z / d)
		return self.copy()

	def dot(self, other):
		assert isinstance(other, Vector3)
		return self.x * other.x + \
			   self.y * other.y + \
			   self.z * other.z

	def cross(self, other):
		assert isinstance(other, Vector3)
		return Vector3(self.y * other.z - self.z * other.y,
					   -self.x * other.z + self.z * other.x,
					   self.x * other.y - self.y * other.x)

	def reflect(self, normal):
		# assume normal is normalized
		assert isinstance(normal, Vector3)
		d = 2 * (self.x * normal.x + self.y * normal.y + self.z * normal.z)
		return Vector3(self.x - d * normal.x,
					   self.y - d * normal.y,
					   self.z - d * normal.z)
"""
