Const QuatToEulerAccuracy:Double = 0.00001

' ------------------------------------------------------------------------------------------------
' Rounds a value to given size after decimal point and converts it to a string
' ------------------------------------------------------------------------------------------------
Function Round:String(value:Float, digits:Int = 2)
	
	Local i:Int
	Local factor:Float = 10 ^ digits
	Local ints:Int = Int(Floor(value))
	Local modstring:String = (Int((value - ints) * factor))
	Local modlength:Int = Len(modstring)
	
	If modlength < digits Then
	
		For i = 1 To (digits - modlength)
			
			modstring = modstring + "0"
			
		Next
		
	EndIf
	
	Return ints + "." + modstring
	
End Function



' ------------------------------------------------------------------------------------------------
' Normalizes a value to given range
' ------------------------------------------------------------------------------------------------
Function Normalize:Float(value:Float, vmin:Float, vmax:Float, nmin:Float, nmax:Float, limit:Int = False)

	' normalize	
	Local result:Float = ((value - vmin) / (vmax - vmin)) * (nmax - nmin) + nmin

	' limit
	If limit Then
	
		If value > nmax Then result = nmax Else If value < nmin Then result = nmin

	EndIf

	Return result
	
End Function



' ------------------------------------------------------------------------------------------------
' INT round in both directions
' ------------------------------------------------------------------------------------------------
Function RoundInt:Int(x:Float)
	
	If x >= 0 Then Return Floor(x) Else Return Ceil(x)
	
End Function


' ------------------------------------------------------------------------------------------------
' Limit a float value To a given range
' ------------------------------------------------------------------------------------------------
Function LimitFloat:Float(value:Float, mn:Float, mx:Float)
	
	If value > mx Then value = mx
	If value < mn Then value = mn
	
	Return value
	
End Function



' ------------------------------------------------------------------------------------------------
' Creates a Random Array of numbers
' ------------------------------------------------------------------------------------------------
Function CreateRandomArray(size:Int)
	
	Local i:Int, n:Int, m:Int, z:Int
	Local s:Int = (size * size)

	' enumerate array		
	For i = 0 To s - 1
		
		Array[i] = i
		
	Next

	' shuffle array	
	For n = 0 To s - 2
		
		m = Rand(n, s - 1)
				
		z = Array[n]
		
		Array[n] = Array[m]
		Array[m] = z
		
	Next
	
End Function


' ------------------------------------------------------------------------------------------------
' Checks if a value is within a range and returns true if so, otherwise false
' ------------------------------------------------------------------------------------------------
Function InRange:Int(value:Float, minimum:Float, maximum:Float)

	If value >= minimum And value < maximum Then Return True
	
	Return False

End Function


' ----------------------------------------------------------------------------
' 2D Distance Calculation
' ----------------------------------------------------------------------------
Function Distance2D:Float(x1:Float,y1:Float,x2:Float,y2:Float)
	
	Local x:Float=x1-x2
	Local y:Float=y1-y2
	
	Return Sqr(x*x+y*y)
	
End Function


' ----------------------------------------------------------------------------
' 3D Distance Calculation
' ----------------------------------------------------------------------------
Function Distance3D:Float(x1:Float,y1:Float,z1:Float,x2:Float,y2:Float,z2:Float)
	
	Local x:Float=x1-x2
	Local y:Float=y1-y2
	Local z:Float=z1-z2
	
	Return Sqr(x*x+y*y+z*z)
	
End Function


' ----------------------------------------------------------------------------
' Smooth curve
' ----------------------------------------------------------------------------
Function Curve:Float(val:Float, mn:Float, mx:Float, typ:Int = 3, amp:Float = 1)

	Local olval:Float
	Local tween:Float
	Local cos1:Float
	Local dif:Float
	
	val = val - mn
	mx = mx - mn
	If amp <> 1 Then olval = val
	tween = ((val / mx) * 90)
	
	If typ <= 1 Then cos1 = Cos(tween - 90) ; val = cos1 * mx				' smooth out
	If typ = 2 Then cos1 = 1 - Cos((tween)) ; val = cos1 * mx				' smooth in
	If typ = 3 Then cos1 = Cos(tween - 90) * Sin(tween) ; val = cos1 * mx	' smooth in and out
	If amp <> 1 Then dif = olval - val ; val = olval - (dif * amp) 			' amplify
	
	Return val + mn

End Function


Function MouseZoomValue:Float(MinZoom:Float = 0.0, MaxZoom:Float = 1.0, resolution:Float = 0.1, Steps:Float = 20)

	Global zoom:Float = 1.0, target:Float = 1.0
	Local epsilon:Float, tdelta:Float

	Steps = Max(Steps, 1)
	epsilon = resolution / Steps
	target = Max(Min(target + (MouseZSpeed(resolution)), MaxZoom), MinZoom)

	tdelta = target - zoom
	If Abs(tdelta) < epsilon Then zoom = target Else zoom = zoom + tdelta / Steps

	Return zoom
	
End Function

Function MouseZSpeed:Float(speed_modifier:Float = 1.0)

	Global Lastz:Float, Newz:Float
	Lastz = Newz
	Newz = -MouseZ()
	Return (Newz - Lastz) * speed_modifier
	
End Function

Function TitiusBode:Float(planet:Int)

	Return 0.4 + (0.3 * (2 ^ (planet - 2)))

End Function

Function Turn(Ent:TEntity, X:Float, Y:Float, Z:Float, Glob:Int = False)

	Local Pitch:Float = 0.0
	Local Yaw:Float = 0.0
	Local Roll:Float = 0.0
	
	Local Quat:TQuaternion = EulerToQuat( 0.0, 0.0, 0.0 )
	Local Turn_Quat:TQuaternion = EulerToQuat( 0.0, 0.0, 0.0 )
	
	If Glob=False
	
		Quat = EulerToQuat( EntityPitch( Ent, True ), EntityYaw( Ent, True ), EntityRoll( Ent, True ) )
		Turn_Quat = EulerToQuat( X, Y, Z )
		Quat = MultiplyQuats( Quat, Turn_Quat )
		Quat = NormalizeQuat( Quat )
		QuatToEuler2( Quat.x, Quat.y, Quat.z, Quat.w, Pitch, Yaw, Roll )
		RotateEntity Ent, Pitch, Yaw, Roll
	Else
	
		RotateEntity Ent, EntityPitch( Ent )+X, EntityYaw( Ent )+Y, EntityRoll( Ent )+Z
		
	EndIf
	
End Function

Function EulerToQuat:TQuaternion(pitch:Float,yaw:Float,roll:Float)

	Local cr:Float=Cos(-roll/2.0)
	Local cp:Float=Cos(pitch/2.0)
	Local cy:Float=Cos(yaw/2.0)
	Local sr:Float=Sin(-roll/2.0)
	Local sp:Float=Sin(pitch/2.0)
	Local sy:Float=Sin(yaw/2.0)
	Local cpcy:Float=cp*cy
	Local spsy:Float=sp*sy
	Local spcy:Float=sp*cy
	Local cpsy:Float=cp*sy
	
	Local q:TQuaternion=New TQuaternion
	
	q.w:Float=cr*cpcy+sr*spsy
	q.x:Float=sr*cpcy-cr*spsy
	q.y:Float=cr*spcy+sr*cpsy
	q.z:Float=cr*cpsy-sr*spcy
	
	Return q
	
End Function

Function QuatToEuler2(x:Float,y:Float,z:Float,w:Float,pitch:Float Var,yaw:Float Var,roll:Float Var)

	Local sint:Float=(2.0*w*y)-(2.0*x*z)
	Local cost_temp:Float=1.0-(sint*sint)
	Local cost:Float

	If Abs(cost_temp)>QuatToEulerAccuracy

		cost=Sqr(cost_temp)

		Else

		cost=0.0

	EndIf

	Local sinv:Float,cosv:Float,sinf:Float,cosf:Float
	
	If Abs(cost)>QuatToEulerAccuracy
	
		sinv=((2.0*y*z)+(2.0*w*x))/cost
		cosv=(1.0-(2.0*x*x)-(2.0*y*y))/cost
		sinf=((2.0*x*y)+(2.0*w*z))/cost
		cosf=(1.0-(2.0*y*y)-(2.0*z*z))/cost
		
	Else
		
		sinv=(2.0*w*x)-(2.0*y*z)
		cosv=1.0-(2.0*x*x)-(2.0*z*z)
		sinf=0.0
		cosf=1.0
		
	EndIf
	
	pitch=ATan2(sint,cost)
	yaw=ATan2(sinf,cosf)
	roll=-ATan2(sinv,cosv)
	
End Function

Function MultiplyQuats:TQuaternion(q1:TQuaternion,q2:TQuaternion)

	Local q:TQuaternion=New TQuaternion
	
	q.w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z
	q.x = q1.w*q2.x + q1.x*q2.w + q1.y*q2.z - q1.z*q2.y
	q.y = q1.w*q2.y + q1.y*q2.w + q1.z*q2.x - q1.x*q2.z
	q.z = q1.w*q2.z + q1.z*q2.w + q1.x*q2.y - q1.y*q2.x

	Return q

End Function

Function NormalizeQuat:TQuaternion(q:TQuaternion)

	Local uv:Float=Sqr(q.w*q.w+q.x*q.x+q.y*q.y+q.z*q.z)

	q.w=q.w/uv
	q.x=q.x/uv
	q.y=q.y/uv
	q.z=q.z/uv

	Return q

End Function