------ All content © 2008 DigiPen ( USA ) Corporation, all rights reserved. ------
-- Author: Hamza M. Hutchinson
-- Updated: 10/17/2008


PI           = 3.1415926535897932384626433832795
HALF_PI      = PI / 2
TWO_PI       = PI * 2
QUARTER_PI   = PI / 4
PI_OVER_SIX  = PI / 6
DEG_TO_RAD   = 0.017453292519943295769236907684886
ROOT_TWO     = 1.414213562373095048801688724209698
ROOT_HALF    = 0.707106781186547524401

function Approx( one, two, val )
  val = nil or .0001
  if( math.abs( one - two ) <= val ) then
    return true
  else
    return false
  end
end

function ClampMin( v, min )
  if v < min then v = min end
  return v
end

function ClampMax( v, max )
  if v > max then v = max end
  return v
end

function Clamp( v, min, max )
  v = ClampMin( v, min )
  v = ClampMax( v, max )
  return v
end

function Wrap( f, max )
  if f < 0 then f = f + max end
  if f > max then f = f - max end
  return f
end

--
-- vector 3 functions
--
vec3 = {}

function vec3.print( v )
  print( "Cartesian: ", v.x, ", ", v.y, ", ", v.z )
  if( v.r )
  then
    print( "Spherical: ", v.r, ", ", v.theta, ", ", v.phi )
  end
end

function vec3.construct( vx, vy, vz )
  return { x = vx, y = vy, z = vz }
end

function vec3.assign3( v1, x, y, z )
  v1.x = x v1.y = y v1.z = z
  return v1
end

function vec3.copyThis( v1, v2 )
  v1 = v2
  return v1
end

function vec3.equal( v1, v2 )
  return( ( v1.x == v2.x ) and ( v1.y == v2.y ) and ( v1.z == v2.z ) )
end

function vec3.negate( v )
  return { x = -v.x, y = -v.y, z = -v.z }
end

function vec3.negateThis( v )
  v.x = -v.x v.y = -v.y v.z = -v.z
  return v
end

function vec3.add( v1, v2 )
  return { x = v1.x + v2.x, y = v1.y + v2.y, z = v1.z + v2.z }
end

function vec3.addThis( v1, v2 )
  v1.x = v1.x + v2.x 
  v1.y = v1.y + v2.y 
  v1.z = v1.z + v2.z
  return v1
end

function vec3.addTwo( v1, v2, v3 )
  v3.x = v1.x + v2.x 
  v3.y = v1.y + v2.y 
  v3.z = v1.z + v2.z
  return v3
end

function vec3.subtract( v1, v2 )
  return { x = v1.x - v2.x, y = v1.y - v2.y, z = v1.z - v2.z }
end

function vec3.subtractThis( v1, v2 )
  v1.x = v1.x - v2.x 
  v1.y = v1.y - v2.y 
  v1.z = v1.z - v2.z
  return v1
end

function vec3.subtractTwo( v1, v2, v3 )
  v3.x = v1.x - v2.x 
  v3.y = v1.y - v2.y 
  v3.z = v1.z - v2.z
  return v3
end

--vector times scaler
function vec3.multiply( v, s )
  return { x = v.x * s, y = v.y * s, z = v.z * s }
end

function vec3.multiplyThis( v, s )
  v.x = v.x * s
  v.y = v.y * s
  v.z = v.z * s
  return v
end

function vec3.multiplyTwo( v1, s, v2 )
  v2.x = v1.x * s
  v2.y = v1.y * s
  v2.z = v1.z * s
  return v2
end

--vector divided by scaler
function vec3.divide( v, s )
  return { x = v.x / s, y = v.y / s, z = v.z / s }
end

function vec3.divideThis( v, s )
  v.x = v.x / s
  v.y = v.y / s
  v.z = v.z / s
  return v
end

function vec3.divideTwo( v1, s, v2 )
  v2.x = v1.x / s
  v2.y = v1.y / s
  v2.z = v1.z / s
  return v2
end

function vec3.dot( v1, v2 )
  return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z
end

function vec3.cross( v1, v2 )
  local c = {}
  c.x = v1.y * v2.z - v1.z * v2.y
  c.y = v1.z * v2.x - v1.x * v2.z
  c.z = v1.x * v2.y - v1.y * v2.x
  return c
end

function vec3.toSpherical( v )
  v.r       = vec3.magnitude( v )
  v.phi     = math.asin( v.y / v.r )
  if( Approx( v.x, 0 ) )
  then
    if( v.z > 0 )
    then
      v.theta = 1.5 * PI
    else
      v.theta = HALF_PI
    end
  elseif( v.x > 0 )
  then
    v.theta = Wrap( math.atan( -v.z / v.x ) + TWO_PI, TWO_PI )
  else
    v.theta = math.atan( -v.z / v.x ) + PI
  end
end

function vec3.toCartesian( v )
  local cosPhi = math.cos( v.phi )
  v.x = v.r * cosPhi * math.cos( v.theta )
  v.y = v.r * math.sin( v.phi )
  v.z = v.r * cosPhi * math.sin( v.theta )
end

function vec3.magnitudeSq( v )
  return vec3.dot( v, v )
end

function vec3.magnitude( v )
  return math.sqrt( vec3.magnitudeSq( v ) )
end

function vec3.normalize( v )
  local mag = vec3.magnitude( v )
  if mag ~= 0 then
    return vec3.divideThis( v, mag )
  end
  return v
end
