This is my vector function library. I wrote it because I needed easy vector support for some of my other projects. I tried to optimize these functions for speed without sacrificing ease of use. I'm also working on a matrix function library as a companion to this vector lib.
Code:
; [VxE]'s vector function library [10-10-08]
; A collection of vector functions
; A formal vector has the format
; ##, ##, ##, ##;
; where ## indicates a real number.
; Formalized vector format is used internally to
; increase efficiency. Formalization optimizations
; are not strictly for internal use, if your input
; vectors are in the formal format, you may specify
; 1 as the second parameter to increase performance.
; Supported operations currently include:
; Float Vect_Item( Vector Index )
; Float Vect_Len( Vector )
; Float Vect_Dot( Vector . Vector )
; Float Vect_Angle(["deg"] Vector . Vector )
; Vector Vect_Formal( string )
; Vector Vect_Norm( Vector )
; Vector Vect_Sub( Vector ... Vector )
; Vector Vect_Add( Vector ... Vector )
; Vector Vect_Mult( Vector . Float )
; Vector Vect_Proj( Vector . Vector )
; Vector Vect_Reflect( Vector . Vector )
; Vector Vect_Cross( Vector . Vector )
;
; Returns the Nth member of the input vector
; NOTE: seperate the vector and the index
; by a semicolor or a newline character
; When using a formal vector, you may
; simply use the following format:
; result := Vect_Item( FormalVector . 3, 1 )
; This function will probably never be used
; because you can just use StringSplit and
; put all of the vector's members into vars
Vect_Item(vect, formal=0)
{
IfNotEqual, formal, 1
{
Loop, Parse, vect, `;`n, %A_Space%%A_Tab%`r`,
IfLess, A_Index, 3, SetEnv, factor%A_Index%, % Vect_Formal(A_LoopField)
Else Break
factor2 := SubStr(Factor2, 1, -1+InStr(Factor2, ","))
} Else StringSplit, factor, vect, `;, %A_Space%%A_Tab%`r`n`,
Loop, Parse, factor1, `,, %A_Space%`;
IfEqual, A_Index, % Round( factor2 ), return %A_LoopField%
return ""
}
; Returns the absolute length of the input vector
Vect_Len(vect, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, SetEnv, vect, % Vect_Formal(vect)
Loop, Parse, vect, `,, %A_Space%`;
sum += A_LoopField**2
SetFormat, Float, %off%
return sqrt(sum)
}
; returns the dot product of the first two vectors.
; the dot product is an easy test of orthogonality
; if the dot product is zero, the two vectors are
; orthogonal (at a right-angle to each other)
Vect_Dot(vlist, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect := Vect_Formal(A_LoopField)), continue
u++
StringSplit, v%u%d, vect, `,, %A_Space%%A_Tab%`r`;
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
{
IfEqual, A_LoopField,, Continue
u++
StringSplit, v%u%d, A_LoopField, `,, `n%A_Space%
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
Loop %vorder%
EnvAdd, acc, v1d%A_Index% * v2d%A_Index%
SetFormat, Float, %off%
return acc + 0
}
; returns the angle between the first two vectors in vlist
; Essentially, return the arccosine of the dot product of the
; normals of two vectors.
; default return value is in radians, specify "deg" before
; any vectors to return the angle in degrees. Example:
; ResultInDegrees := Vect_Angle( "deg" Vector1 Vector2 )
Vect_Angle(vlist, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
deg := u := 1
IfEqual,True, % InStr(vlist,"deg")
SetEnv,vlist, % SubStr(vlist,53.3-deg:=45/ATan(1))
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect%u% := Vect_Formal(A_LoopField)), continue
else u++
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
IfEqual, A_LoopField,, Continue
else vect%u% := A_LoopField ";", u++
angle := ACos( Vect_Dot( Vect_Norm( vect1, 1 ) . Vect_Norm( vect2, 1 ), 1 ) )
SetFormat, Float, %off%
return angle * deg
}
; Formalize a string into a vector by parsing through
; any numeric characters (includes '-' and '.') and
; appending them according to the following rules:
; 0. Characters listed in the second parameter
; are ignored (for instance: commas in large numbers)
; 1. The first semicolon or newline reached marks
; the end of the input string. This is to stop multiple
; vectors from being formalized into a frankenvector
; 2. all non-numeric chars become item breaks
; 3. an item break preceeds every minus sign
; 4. within an unbroken numeric string, the second
; decimal point is dropped along with all chars
; following it until the next item break is reached
; 5. There are no empty vector members unless the
; input contains no numeric characters.
; NOTE: If there is a way to do this in one line
; with regex, I'm not interested! Either release
; it or append it to this library yourself.
Vect_Formal(string, ignorechars="")
{
Loop, Parse, string, , %ignorechars%
If A_LoopField !=
IfEqual, A_LoopField, `;, break
else nv .= (u := InStr("-.1234567890", A_LoopField)) ? (u
=1 ? " " : "") A_LoopField : " "
Loop, Parse, nv, %A_Space%
IfEqual, A_LoopField,, Continue
Else IfNotEqual, A_LoopField, -
{
stringsplit, q, A_LoopField, .
vect .= ", " q1 + 0 (q0>1 ? "." q2 : "")
}
return StrLen(vect) > 3 ? SubStr(vect,3) ";" : ""
}
; Returns the normal of the input vector
Vect_Norm(vect, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, SetEnv, vect, % Vect_Formal(vect)
len := Vect_Len(vect, 1)
Loop, Parse, vect, `,, %A_Space%`;
nv .= ", " A_LoopField / len
SetFormat, Float, %off%
return substr(nv,3) ";"
}
; Returns the sum of the first vector and the negatives
; of all subsequent vectors in the list
Vect_Sub(vlist, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect := Vect_Formal(A_LoopField)), continue
u++
StringSplit, v%u%d, vect, `,, %A_Space%%A_Tab%`r`;
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
{
IfEqual, A_LoopField,, Continue
u++
StringSplit, v%u%d, A_LoopField, `,, `n%A_Space%
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
Loop %vorder%
{
v := A_Index
Loop %u%
IfEqual, A_Index, 1, SetEnv, acc, % v1d%v%
else EnvSub, acc, % v%A_Index%d%v%
nv .= ", " acc
}
SetFormat, Float, %off%
return substr(nv,3) ";"
}
; Adds the vectors in the list.
Vect_Add(vlist, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect := Vect_Formal(A_LoopField)), continue
u++
StringSplit, v%u%d, vect, `,, %A_Space%%A_Tab%`r`;
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
{
IfEqual, A_LoopField,, Continue
u++
StringSplit, v%u%d, A_LoopField, `,, `n%A_Space%
IfLess, vorder, % v%u%d0, SetEnv, vorder, % v%u%d0
}
Loop %vorder%
{
v := A_Index
acc := 0
Loop %u%
EnvAdd, acc, % v%A_Index%d%v%
nv .= ", " acc
}
SetFormat, Float, %off%
return substr(nv,3) ";"
}
; Returns the input vector with length
; multiplitd by a factor. NOTE: seperate
; the vector and the factor by a
; semicolor or a newline character
; When using a formal vector, you may
; simply use the following format:
; result := Vect_Mult( FormalVector . 3, 1 )
Vect_Mult(vect, formal=0)
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1
{
Loop, Parse, vect, `;`n, %A_Space%%A_Tab%`r`,
IfLess, A_Index, 3, SetEnv, factor%A_Index%, % Vect_Formal(A_LoopField)
Else Break
factor2 := SubStr(Factor2, 1, -1+InStr(Factor2, ","))
} Else StringSplit, factor, vect, `;, %A_Space%%A_Tab%`r`n`,
Loop, Parse, factor1, `,, %A_Space%`;
nv .= ", " A_LoopField * factor2
SetFormat, Float, %off%
return substr(nv,3) ";"
}
; returns a vector describing the projection A onto B
; where A is the first vector in the list and B is the next
; The result is linearly dependant to vector B
Vect_Proj(vlist, formal=0)
{
u=1
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect%u% := Vect_Formal(A_LoopField)), continue
else u++
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
IfEqual, A_LoopField,, Continue
else vect%u% := A_LoopField ";", u++
return Vect_Mult(vect2 . Vect_Dot(vect1 . vect2, 1) / (Vect_Len(vect2, 1)**2), 1)
}
; if the second vector is normal to a plane, and
; the first vector is not paralell to that plane
; then this function returns the first vector
; after it bounces off the plane
; In essense, this function subtracts twice the
; projection of v1 onto v2 from v1, clear?
; Also, the angle between a vector and its reflection
; is exactly equal to twice the angle betwen that
; vector and the normal it is being reflected from
; so, Vect_Angle( v1 v2 ) is the same as
; Vect_Angle( Vect_Reflect( v1 v2 ) v2 )
Vect_Reflect(vlist, formal=0)
{
u=1
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect%u% := Vect_Formal(A_LoopField)), continue
else u++
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
IfEqual, A_LoopField,, Continue
else vect%u% := A_LoopField ";", u++
vx := Vect_Proj(vect1 . vect2, 1)
return Vect_Sub(vect1 . vx . vx, 1)
}
; returns a vector that is the cross-product of
; the first two vectors in vlist provided that
; those two vectors are independant and are in R3
; Note: the cross product in Rn requires n-1 vectors
; and is produced by calculating the determinants of
; n square matrices of size (n-1)x(n-1)
; For example, in R5...
;
; v1 = v1a, v1b, v1c, v1d, v1e
; v2 = v2a, v2b, v2c, v2d, v2e
; v3 = v3a, v3b, v3c, v3d, v3e
; v4 = v4a, v4b, v4c, v4d, v4e
; Cross = I, J, K, L, M
; I = Det( v1b, v1c, v1d, v1e; v2b, v2c, v2d, v2e; v3b, v3c, v3d, v3e; v4b, v4c, v4d, v4e)
; J = Det( v1c, v1d, v1e, v1a; v2c, v2d, v2e, v2a; v3c, v3d, v3e, v3a; v4c, v4d, v4e, v4a)
; K = Det( v1d, v1e, v1a, v1b; v2d, v2e, v2a, v2b; v3d, v3e, v3a, v3b; v4d, v4e, v4a, v4b)
; L = Det( v1e, v1a, v1b, v1c; v2e, v2a, v2b, v2c; v3e, v3a, v3b, v3c; v4e, v4a, v4b, v4c)
; M = Det( v1a, v1b, v1c, v1d; v2a, v2b, v2c, v2d; v3a, v3b, v3c, v3d; v4a, v4b, v4c, v4d)
; where Det() is the determinate of the specified matrix
; And for large n's, the fastest way to get the determinant
; is through row-reduction to row-echelon form and then
; multiply along the diagonal. Since row-reduction would
; have a big-O notation of O(n²) whereas finding
; the determinate by laplace expansion would have a
; big-O of O(n!) (and that's really bad!!)
;
Vect_Cross(vlist, formal=0) ; WARNING: R3 supported only!!!!
{
off := A_FormatFloat
SetFormat, Float, 0.15
IfNotEqual, formal, 1, Loop, Parse, vlist, `;`n, %A_Space%%A_Tab%`r`,
{
IfEqual, A_LoopField,, Continue
IfEqual, True, % !(vect := Vect_Formal(A_LoopField)), continue
u++
StringSplit, v%u%d, vect, `,, %A_Space%%A_Tab%`r`;
}
else Loop, Parse, vlist, `;`n, `r%A_Space%%A_Tab%
{
IfEqual, A_LoopField,, Continue
u++
StringSplit, v%u%d, A_LoopField, `,, `n%A_Space%
}
vr := (v1d2 * v2d3 - v1d3 * v2d2) ", "
vr .= (v1d3 * v2d1 - v1d1 * v2d3) ", "
vr .= (v1d1 * v2d2 - v1d2 * v2d1) ";"
SetFormat, Float, %off%
return vr
}