
--set de fonctions 2D
--utilisant les metatables
-- permet de manipuler des vecteurs 2d comme des variables avec les opérations, + - , * , tostring ...
Vector = {}
Vector_mt = { __index = Vector }
 
-- création d'un vecteur à partir d'une unit 2D, ou de coordonnées x,y.
function Vector:new(x,y)
	if (type(x)=='table') then --cas où on le crée à partir d'une unit
		return setmetatable( {x=x.Position_X, y=x.Position_Y}, Vector_mt)
	else
		return setmetatable( {x=x, y=y}, Vector_mt)
	end
end

function Vector:plus(v1)
    --virer le test pour optim ensuite
    if v1 == nil then
        if debug == nil then
            OPLua.LoadLibrary("debug")
        end
        OPLog.Log(debug.traceback("Erreur plus"))
    end
    --assert(v1 ~= nil , "Erreur dans + , paramétre nil "..debug.traceback())
	local v=Vector:new(v1.x+self.x,v1.y+self.y)
	return v
end

--produit scalaire, opération *
function Vector:dot(v)
   return self.x * v.x + self.y * v.y
end


function Vector:rotate(angle)
	local x=self.x*math.cos(angle)-self.y*math.sin(angle)
	local y=self.x*math.sin(angle)+self.y*math.cos(angle)	
	local v=Vector:new(x,y)
	return v
end

--rotation par rapport à un pt de ref
function Vector:rotAround(angle, origin)
	local x = (self.x-origin.x)*math.cos(angle) -(self.y-origin.y)*math.sin(angle) + origin.x
	local y = (self.x-origin.x)*math.sin(angle) + (self.y-origin.y)*math.cos(angle) + origin.y
	local v=Vector:new(x,y)
	return v
end

function Vector:neg(v1)
	local v=Vector:new(-self.x,-self.y)
	return v
end

function Vector:scale(s)
	local v=Vector:new(self.x*s,self.y*s)
	return v
end

function Vector:soustract(v1)
	AssertPopup(self ~=nil,"prob init vecteur")
	local v=Vector:new(-v1.x+self.x,-v1.y+self.y)
	return v
end

-- norm du vecteur
function Vector:norm(v)
	return math.sqrt(self.x^2+self.y^2)
end

--norme au carré -> moins de temps de calcul
function Vector:normsquare(v)
	return (self.x^2+self.y^2)
end

function Vector:normalize()
	local norm=self:norm()
	local v=Vector:new(self.x/norm,self.y/norm)
	return v
end

function Vector:getangle()
	return math.atan2(self.x,self.y)
end

function Vector:display()
	OPLog.Log(self:print())
end

function Vector:print()
	local s='x : '..string.sub(self.x,1,5)..' y : '..string.sub(self.y,1,5)
	return s
end

function Vector:set(ut) --on affecte les coordonnées du vector 2D à l'unit
	ut.Position_X=self.x
	ut.Position_Y=self.y
end

--définition des opérateurs
Vector_mt.__add = Vector.plus
Vector_mt.__mul = Vector.dot
Vector_mt.__unm = Vector.neg
Vector_mt.__sub = Vector.soustract
Vector_mt.__tostring = Vector.print

function GetDistance(v1, v2)
	v=v2-v1
	return v:norm()
end

function sign(n)
	if n<0 then
		return -1
	elseif n>0 then
		return 1
	else
		return 0
	end
end

--calcule le centre à partir de witdh,height
function GetCenter(ut)
	local i=Vector:new(ut.Dimension_Width/2,ut.Dimension_Height/2)
	local res=Vector:new(ut) + i
	return res
end

-- on met le centre de l'unit à cette position
function SetCenter(ut,vec)
	ut.Position_X = vec.x - ((ut.Dimension_Width /2) *ut.Scale_X)
	ut.Position_Y = vec.y - ((ut.Dimension_Height /2) * ut.Scale_Y)
end

--renvoit la position sur l'écran (en cas de scrolling)
function GetScreenCenter(ut)
	if ut.Parent == nil then
		return GetCenter(ut)
	else
		return GetCenter(ut) + Vector:new(ut.Parent)
	end
end

function GetFourCorner(ut)
	--renvoit les 4 coins du sprites
	local res={}
	local center=GetCenter(ut)
	res1=Vector:new(-ut.Dimension_Width/2,-ut.Dimension_Height/2)
	res2=Vector:new(ut.Dimension_Width/2,-ut.Dimension_Height/2)
	res3=Vector:new(ut.Dimension_Width/2,ut.Dimension_Height/2)
	res4=Vector:new(-ut.Dimension_Width/2,ut.Dimension_Height/2)
	
	res1=center+res1:rotate(ut.Rotation)
	res2=center+res2:rotate(ut.Rotation)
	res3=center+res3:rotate(ut.Rotation)
	res4=center+res4:rotate(ut.Rotation)
	res={res1,res2,res3,res4}
	
	--test=Load('testcenter')
	--test.Position_X=res3.x
	--test.Position_Y=res3.y
	return res
end


--nous renvoie la direction de theta vers beta
-- theta et beta sont tous les deux entre 0 et 2 pi
-- diff min nous sert a avoir un "palier" pour bouger
function ComputeAngleSign(theta,beta,diffmin) 
	local delta=math.abs(theta-beta)
	local d1=math.abs(theta-(beta-2*math.pi))
	local d2=math.abs(theta-(beta+2*math.pi))
	local d=math.min(delta,d1,d2)
	
	if (d<diffmin) then
		return 0,d	
	end
	if (d==d1) then -- on est a gauche du zero, on descend
		return -1,d,(beta-2*math.pi)
	elseif (d==d2) then -- on est a droite de 2*pi, on monte
		return 1,d,(beta+2*math.pi)
	else	--cas classique, l'angle le plus pres est dans l'intervalle 0-2pi
		if (theta<beta) then
			return 1,d,beta
		else
			return -1,d,beta
		end
	end
end

--P contient les coordonnées du point, R les coordonnées du rectangle.
--C1 et C2 sont deux vecteurs définissant le coin d'un rectangle
--V le vecteur entre le coin et le point p
function IsInRectangle(P,R)
	--AssertPopup(false,"fonction à revoir")
	if P.x >= R.left and P.x <= R.right then
		if P.y >= R.top and P.y <= R.bottom then
			return true
		end
	end
	
	return false
	
end

--on calcule la projection orthogonale de L sur le segment AB
--L,A,B sont des vecteurs
function ComputePointProjection(L,A,B)
	--OPLog.Log('type : '..type(A))	
	--OPLog.Log('A : '..A.x..' '..A.y..' '..A.z)
	--OPLog.Log('B : '..B.x..' '..B.y..' '..B.z)
	local vecab=B-A	
	local vec=L-A
	
	local distab=(vecab:norm())^2
	local d
	--cas particulier distance nulle
	if (distab==0) then
		d=vec:norm()
		return A,d,0
	end
	local scalar=vecab*vec
	local t=scalar/distab
	--OPLog.Log('distab '..distab)
	
	--OPLog.Log('leopard : t = '..t)
	if (t<0) then
		d=vec:norm()
		--OPLog.Log('d '..d)
		return A,d,0
	elseif (t>1) then
		vec=B-L
		d=vec:norm()
		--OPLog.Log('d '..d)
		return B,d,1
	else
		local M=A
		M=M+vecab:scale(t)
		vec=M-L
		d=vec:norm()
		--OPLog.Log('d M'..d..' '..M.x)
		return M,d,t
	end
end

--un isequal approché
function ApproxEquals(n1, n2, tolerance)
	return (math.abs(n2-n1)<=tolerance) 
end

function IsOnScreen(ut)
	if ut.Position_X>800 then
		return false
	elseif ut.Position_X<-ut.Dimension_Width then
		return false
	elseif ut.Position_Y>600 then
		return false
	elseif ut.Position_Y<-ut.Dimension_Height then
		return false
	else
		return true
	end
end

function GetRectangle( unit )
	local rect = 
	{
		up = unit.Position_Y,
		down = unit.Position_Y + unit.Dimension_Height*unit.Scale_Y,
		left = unit.Position_X,
		right = unit.Position_X + unit.Dimension_Width*unit.Scale_X
	}
	
	return rect
end


--intersection de deux rectangle donné
function IntersectRect( unit1, unit2 )
	local rect1 = GetRectangle(unit1)
	local rect2 = GetRectangle(unit2)
	
	local r1up = rect1.up
	local r1down = rect1.down
	local r1left = rect1.left
	local r1right = rect1.right
	
	local r2up = rect2.up
	local r2down = rect2.down
	local r2left = rect2.left
	local r2right = rect2.right
	
	if rect1.up >= rect2.up then
		if rect1.up > rect2.down then
			return false
		end
	end
	
	if rect1.up <= rect2.up then
		if rect1.down < rect2.up then
			return false
		end
	end
	
	if rect1.left >= rect2.left then
		if rect1.left > rect2.right then
			return false
		end
	end
	
	if rect1.left <= rect2.left then
		if rect1.right < rect2.left then
			return false
		end
	end
	
	return true
end

--fait un min,max sur la valeur en entrée
function Bound(x,valmin,valmax)
	return math.min(valmax,math.max(valmin,x))
end

--fonction pour choisir autour de quel point on fait tourner un sprite donné
--refpoint c'est par rapport au coin gauche du sprite
--mainpos c'est la position initiale à angle 0
function RotateSprite(ut,angle, mainpos,refpoint)
	ut.Rotation = angle
	-- ca se fait rapport au centre
	local VecDiff = refpoint - Vector:new(ut.Dimension_Width /2 * ut.Scale_X,ut.Dimension_Height / 2 * ut.Scale_Y)
	local newvec = VecDiff - VecDiff:rotate(angle)  
		
	ut.Position_X = mainpos.x + newvec.x
	ut.Position_Y = mainpos.y - newvec.y --attention c'est un - car on a les y qui augmentent en descendant !!
end

--TODO centraliser les deux rotatesprite
function RotateSpriteWatch(ut,angle, mainpos,refpoint)
	ut.Rotation = angle
	-- ca se fait rapport au centre
	local VecDiff = refpoint - Vector:new(ut.Dimension_Width /2,ut.Dimension_Height / 2 )
	local newvec = VecDiff - VecDiff:rotate(angle)  
		
	ut.Position_X = mainpos.x + newvec.x
	ut.Position_Y = mainpos.y + newvec.y --attention c'est un - car on a les y qui augmentent en descendant !!
end
