;==============================================================
;! 
;! Title: VEC3 Macro Lib - Cross-Platform
;! Author: Gustavo J. Fiorenza (aim: gushhfx)
;! Date: 10/05/2009 - Rev: 23
;!
;! ToDo: projection, perpendicular, math constants, 
;!			 point helpers.
;!
;! History:
;!	
;!	10/05/2009: Fixed small typo, added angle helpers
;!	24/01/2009: Added min, max. Fixed Lerp.
;!	18/01/2009: Fixed structure, made FMOD compliant with VEC3_TYPE
;!							Refactored FastSine() and PIWRAP().
;!	12/01/2009: Optimized/Fixed the PIWRAP macro with FMOD.
;!							Added resolution switch For the quadratic curve.
;!	11/01/2009: Added wrap, clamp, lerp, string. Fixed some bugs. 
;!							Refactored the vec3 structure. Added approximation
;!							method for sine and cosine functions.
;!	10/01/2009: Library created.
;! 
;==============================================================
 
 
#VEC3 														= #True 				; to assert whether the lib has been included or not. do not change.
#VEC3_FASTSINE_APPROXIMATE				= 1 						; change whether you want to approximate sine/cosine or not.
#VEC3_FASTSINE_EXTRA_PRECISION		=	0							; 1 for high resolution, 0 for low resolution.
 
 
Macro VEC3_TYPE			:	f			:	EndMacro		; change to d for doubles.
Macro VEC3_TYPE_ASM	:	dword	:	EndMacro		; use qword for doubles. dword for singles.	
 
 
Structure VEC3											; updated structure, it's now user-friendly (better code readability)
	StructureUnion
		angle.VEC3_TYPE
		direction.VEC3_TYPE
		tail.VEC3_TYPE
		x.VEC3_TYPE
		u.VEC3_TYPE
		r.VEC3_TYPE
		i.VEC3_TYPE
	EndStructureUnion
	StructureUnion
		length.VEC3_TYPE
		magnitude.VEC3_TYPE
		head.VEC3_TYPE
		y.VEC3_TYPE
		v.VEC3_TYPE
		g.VEC3_TYPE
		j.VEC3_TYPE
	EndStructureUnion
	StructureUnion
		z.VEC3_TYPE
		w.VEC3_TYPE
		b.VEC3_TYPE
		k.VEC3_TYPE
	EndStructureUnion
EndStructure
 
 
Macro VEC3_RAD2DEG(_n_) 	: (_n_ * 57.295779513082323 ) : EndMacro
Macro VEC3_DEG2RAD(_n_) 	: (_n_ * 0.0174532925199432 ) : EndMacro
 
 
;###
 
CompilerIf #VEC3_FASTSINE_APPROXIMATE = #True
 
	; this method clocks about 2x+ as fast as the fixed functionality of your processor.
	; note: we do have the function overhead in this case but it's minimal.
	;				the PIWRAP macro is only used once every step so it's not an issue either.
	;				Should you disable the call; keep the radian range in mind. 
	;				If something goes wrong that could well be the culprit.
	; 			Read more about this method on: http://en.wikipedia.org/wiki/Quadratic_curve
 
	Procedure.VEC3_TYPE fmod( x.VEC3_TYPE, y.VEC3_TYPE )
	  EnableASM
		  FLD VEC3_TYPE_ASM[p.v_y]
		  FLD VEC3_TYPE_ASM[p.v_x]
		  !NOT_READY:
			  FPREM1    ; we use fprem1 instead of fprem to avoid another branch in this case, plus it's the standard call.
			  FSTSW AX
			  TEST AH, 100b
		  !JNZ NOT_READY
		  FSTP ST1
	  DisableASM
	  ProcedureReturn
	EndProcedure
 
	Macro PIWRAP( x )
		fmod(x, #PI*2)
	EndMacro
 
	Procedure.VEC3_TYPE FastSine( x.VEC3_TYPE ) ; unclean. could be optimized even further.
 
		Define.VEC3_TYPE curve
		x = PIWRAP(x) ; the caveat.
 
		#FASTSINE_B = 4/#PI
		#FASTSINE_C = -4/(#PI*#PI)
		curve = #FASTSINE_B * x + #FASTSINE_C * x * Abs(x)
 
		CompilerIf #VEC3_FASTSINE_EXTRA_PRECISION = #True
			#FASTSINE_P = 0.225
			curve = #FASTSINE_P * (curve * Abs(curve) - curve) + curve
		CompilerEndIf
 
		ProcedureReturn curve
 
	EndProcedure
 
	Macro VEC3_COS( n )
		FastSine( n + (#PI/2.0) ) ; adding constant expression, solved by the preprocessor - no speed overhead here.
	EndMacro
 
	Macro VEC3_SIN( n )
		FastSine( n )
	EndMacro
 
CompilerElse
 
	Macro VEC3_COS( n )
		Cos( n )
	EndMacro
	Macro VEC3_SIN( n )
		Sin( n )
	EndMacro
 
CompilerEndIf
 
;###
 
																	; some useful global pre-defined variables	
Global VEC3_NULL.VEC3							; 	the null vector
Global VEC3_TEMP.VEC3							; 	internal use for temporary actions
Global VEC3_TEMP_TYPE.VEC3_TYPE		; 	a temporary floating point value
 
 
 
Macro VEC3_DEBUG( a ) 														: Debug "["+StrF(a\x)+", "+StrF(a\y)+", "+StrF(a\z)+"]"	 							: EndMacro
Macro VEC3_DEBUG3( a, b=VEC3_NULL, c=VEC3_NULL )	:	VEC3_DEBUG(a) : VEC3_DEBUG(b) : VEC3_DEBUG(c) : Debug ""						:	EndMacro
Macro VEC3_STRING( v )														:	"[{"+StrF(v\x)+"}, {"+StrF(v\y)+"}, {"+StrF(v\z)+"}]"								:	EndMacro
 
Macro VEC3_SET( v, _x=0.0, _y=0.0, _z=0.0 )				: v\x = _x : v\y = _y : v\z = _z																			: EndMacro 	; call with no param to clear
Macro VEC3_ADDC( v, _x=0.0, _y=0.0, _z=0.0 )			: v\x + _x : v\y + _y : v\z + _z																			: EndMacro 	; find a better name, C stands for "component/s". I blame the lack of function overloading on this one.
Macro VEC3_COMPARE( a, b )												: 1 -( (a\x <> b\x) Or (a\y <> b\y) Or (a\z <> b\z) )									: EndMacro 	; 1 if equal
 
Macro VEC3_ADD( a, b, r )													: r\x = a\x + b\x : r\y = a\y + b\y : r\z = a\z + b\z									: EndMacro
Macro VEC3_SUBTRACT( a, b, r ) 										: r\x = a\x - b\x : r\y = a\y - b\y : r\z = a\z - b\z  								: EndMacro
Macro VEC3_DIVIDE( a, b, r )											: r\x = a\x / b\x : r\y = a\y / b\y : r\z = a\z / b\z									: EndMacro
Macro VEC3_MULTIPLY( a, b, r )										: r\x = a\x * b\x : r\y = a\y * b\y : r\z = a\z * b\z									: EndMacro
 
Macro VEC3_COPY( a, r )														: r\x = a\x : r\y = a\y : r\z = a\z																		: EndMacro
Macro VEC3_SCALE( a, s, r )												: r\x = a\x * s : r\y = a\y * s : r\z = a\z * s												: EndMacro
Macro VEC3_NEGATE( a, b, r )											: r\x = b\x - a\x : r\y = b\y - a\y : r\z = b\z - a\z									: EndMacro
Macro VEC3_MA( v, s, b, r )												:	r\x = v\x + b\x * s : r\y = v\y + b\y * s : r\z = v\z + b\z * s			:	EndMacro
 
Macro VEC3_DOTPRODUCT( a, b ) 										: a\x * b\x + a\y * b\y + a\z * b\z  																	: EndMacro
Macro VEC3_LENGTH( v )														: Sqr( v\x * v\x + v\y * v\y + v\z * v\z )														: EndMacro
Macro VEC3_LENGTHSQUARED( v )											: v\x * v\x + v\y * v\y + v\z * v\z																		: EndMacro
 
 
Macro VEC3_CLEAR( a )															: VEC3_SET( a )																												: EndMacro 	; deprecated, legacy call.
Macro VEC3_SETADD( v, _x=0.0, _y=0.0, _z=0.0 )		: VEC3_ADDC( v, _x, _y, _z )																					: EndMacro 	; deprecated, legacy call.
 
 
Macro VEC3_ENDPOINT( v, p )
 
	p\x + VEC3_COS(v\x) * v\y
	p\y + VEC3_SIN(v\x) * v\y
 
EndMacro
 
Macro VEC3_DISTANCE( p1, p2, distance )
 
	VEC3_SUBTRACT( p1, p2, VEC3_TEMP )
	distance = VEC3_LENGTH( VEC3_TEMP )
 
EndMacro
 
Macro VEC3_DISTANCESQUARED( p1, p2, distance )
 
	VEC3_SUBTRACT( p1, p2, VEC3_TEMP )
	distance = VEC3_LENGTHSQUARED( VEC3_TEMP )
 
EndMacro
 
Macro VEC3_CROSSPRODUCT( v1, v2, cross )
 
	cross\x = v1\y * v2\z - v1\z * v2\y
	cross\y = v1\z * v2\x	- v1\x * v2\z
	cross\z = v1\x * v2\y - v1\y * v2\x
 
EndMacro
 
Macro VEC3_NORMALIZE( v, length=VEC3_TEMP_TYPE )
 
	length = 1.0 / Sqr( VEC3_DOTPRODUCT( v, v ) )
	VEC3_SCALE( v, length, v )
 
EndMacro
 
Macro VEC3_WRAP( v, v_min, v_max, offset = 0.0 ) ; wraps around the min and max vectors (useful if you want to wrap a point within a certain region)
 
	If v\x < v_min\x + offset
		v\x = v_max\x - offset
	EndIf
	If v\x > v_max\x - offset
		v\x = v_min\x + offset
	EndIf
 
	If v\y < v_min\y + offset
		v\y = v_max\y - offset
	EndIf
	If v\y > v_max\y - offset
		v\y = v_min\y + offset
	EndIf
 
	If v\z < v_min\z + offset
		v\z = v_max\z + offset
	EndIf
	If v\z > v_max\z - offset
		v\z = v_min\z + offset
	EndIf
 
EndMacro
 
Macro VEC3_CLAMP( v, v_min, v_max, offset = 0.0 ) ; wraps around the min and max vectors (useful if you want to clamp a point within a certain region)
 
	If v\x < v_min\x + offset
		v\x = v_min\x + offset
	EndIf
	If v\x > v_max\x - offset
		v\x = v_max\x - offset
	EndIf
 
	If v\y < v_min\y + offset
		v\y = v_min\y + offset
	EndIf
	If v\y > v_max\y - offset
		v\y = v_max\y - offset
	EndIf
 
	If v\z < v_min\z + offset
		v\z = v_min\z + offset
	EndIf
	If v\z > v_max\z - offset
		v\z = v_max\z - offset
	EndIf
 
EndMacro
 
Macro VEC3_LERP( a, b, r, blend ) ; vectors a and b interpolated into vector c.
 
	r\x = ( blend * (b\x - a\x) + a\x )
	r\y = ( blend * (b\y - a\y) + a\y )
	r\z = ( blend * (b\z - a\z) + a\z )
 
EndMacro
 
Macro VEC3_MIN( dst, src )
 
	If dst\x < src\x
		dst\x = dst\x
	Else
		dst\x = src\x
	EndIf
 
	If dst\y < src\y
		dst\y = dst\y
	Else
		dst\y = src\y
	EndIf
 
EndMacro
 
Macro VEC3_MAX( dst, src )
 
	If dst\x > src\x
		dst\x = dst\x
	Else
		dst\x = src\x
	EndIf
 
	If dst\y > src\y
		dst\y = dst\y
	Else
		dst\y = src\y
	EndIf
 
EndMacro
 
;---
 
Macro VEC3_ANGLE_RAD2DEG(_n_) 	: (_n_ * 57.295779513082323 ) : EndMacro
Macro VEC3_ANGLE_DEG2RAD(_n_) 	: (_n_ * 0.0174532925199432 ) : EndMacro
 
Procedure.VEC3_TYPE VEC3_ATAN2( x.VEC3_TYPE, y.VEC3_TYPE )
	EnableASM
		FLD VEC3_TYPE_ASM[p.v_y]
		FLD VEC3_TYPE_ASM[p.v_x]
		FPATAN
	DisableASM
	ProcedureReturn
EndProcedure
 
Procedure.VEC3_TYPE VEC3_ANGLE_BETWEEN( *a.VEC3, *b.VEC3 )
 
	Define.VEC3_TYPE opp = -(*b\y - *a\y)
	Define.VEC3_TYPE adj =  (*b\x - *a\x)
 
	ProcedureReturn VEC3_ATAN2( opp, adj ) + #PI/2.0
 
EndProcedure
 
Procedure VEC3_SIGN( x.VEC3_TYPE )
  ProcedureReturn (x > 0 Or 0) - (x < 0 Or 0)
EndProcedure 
 
Procedure.VEC3_TYPE VEC3_CURVE( new.VEC3_TYPE, old.VEC3_TYPE, increments.VEC3_TYPE )
 
	Define.i sign 				= VEC3_SIGN( old - new )
	Define.VEC3_TYPE slip	= ( old - new ) / increments
 
	old = ( old - slip )
 
	If sign <> VEC3_SIGN( old - new )
		ProcedureReturn new
	Else
		ProcedureReturn old
	EndIf
 
EndProcedure
 
;---
 
 
 
 
; IDE Options = PureBasic 4.30 (Windows - x86)
; CursorPosition = 25
; Folding = ---2----
; EnableCompileCount = 94
; EnableBuildCount = 0