Procrastination Incarnate
Development Director
Join Date: Feb 2004
Posts: 8,186

Vector
 This is a set of function intended for spatial geometric calculations. They can be used as a basis for projectile movement, collision detection and other similar operations in 3D space. The functions operate with "vector objects"  integers that represent the index of a vector in a set of arrays. Those arrays store the three coordinates of a vector. The system can do many vector operations, from addition to projection, as well as getting terrain normals and collision detection.
These functions were first used in my and iNfraNe's Blizzard's sports map contest entry Dwarven Downhill. 
Update: I have finished making a vJass version of this system. Vectors are implemented as a struct and all the operations that can be done with them have been greatly optimized. The vector methods should now be several times faster than the old functions. The post still contains the original nonvJass map if anyone needs it.
Vector: library Vector
struct vector
real x
real y
real z
static method create takes real x, real y, real z returns vector
local vector v = vector.allocate()
set v.x=x
set v.y=y
set v.z=z
return v
endmethod
method getLength takes nothing returns real
return SquareRoot(.x*.x + .y*.y + .z*.z)
endmethod
static method sum takes vector augend, vector addend returns vector
local vector v = vector.allocate()
set v.x = augend.x+addend.x
set v.y = augend.y+addend.y
set v.z = augend.z+addend.z
return v
endmethod
method add takes vector addend returns nothing
set this.x=this.x+addend.x
set this.y=this.y+addend.y
set this.z=this.z+addend.z
endmethod
static method difference takes vector minuend, vector subtrahend returns vector
local vector v = vector.allocate()
set v.x = minuend.xsubtrahend.x
set v.y = minuend.ysubtrahend.y
set v.z = minuend.zsubtrahend.z
return v
endmethod
method subtract takes vector subtrahend returns nothing
set this.x=this.xsubtrahend.x
set this.y=this.ysubtrahend.y
set this.z=this.zsubtrahend.z
endmethod
method scale takes real factor returns nothing
set this.x=this.x*factor
set this.y=this.y*factor
set this.z=this.z*factor
endmethod
method setLength takes real length returns nothing
local real l = SquareRoot(.x*.x + .y*.y + .z*.z)
if l == 0.0 then
debug call BJDebugMsg("vector.setLength error: The length of the vector is 0.0!")
return
endif
set l = length/l
set this.x = this.x*l
set this.y = this.y*l
set this.z = this.z*l
endmethod
static method dotProduct takes vector a, vector b returns real
return (a.x*b.x+a.y*b.y+a.z*b.z)
endmethod
static method crossProduct takes vector a, vector b returns vector
local vector v = vector.allocate()
set v.x = a.y*b.z  a.z*b.y
set v.y = a.z*b.x  a.x*b.z
set v.z = a.x*b.y  a.y*b.x
return v
endmethod
static method tripleProductScalar takes vector a, vector b, vector c returns real
return ((a.y*b.z  a.z*b.y)*c.x+(a.z*b.x  a.x*b.z)*c.y+(a.x*b.y  a.y*b.x)*c.z)
endmethod
static method tripleProductVector takes vector a, vector b, vector c returns vector
local vector v = vector.allocate()
local real n = a.x*c.x+a.y*c.y+a.z*c.z
local real m = a.x*b.x+a.y*b.y+a.z*b.z
set v.x = b.x*nc.x*m
set v.y = b.y*nc.y*m
set v.z = b.z*nc.z*m
return v
endmethod
static method projectionVector takes vector projected, vector direction returns vector
local vector v = vector.allocate()
local real l = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z
if l == 0.0 then
call v.destroy()
debug call BJDebugMsg("vector.projectionVector error: The length of the direction vector is 0.0!")
return 0
endif
set l = (projected.x*direction.x+projected.y*direction.y+projected.z*direction.z) / l
set v.x = direction.x*l
set v.y = direction.y*l
set v.z = direction.z*l
return v
endmethod
method projectVector takes vector direction returns nothing
local real l = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z
if l == 0.0 then
debug call BJDebugMsg("vector.projectVector error: The length of the direction vector is 0.0!")
return
endif
set l = (this.x*direction.x+this.y*direction.y+this.z*direction.z) / l
set this.x = direction.x*l
set this.y = direction.y*l
set this.z = direction.z*l
endmethod
static method projectionPlane takes vector projected, vector normal returns vector
local vector v = vector.allocate()
local real l = normal.x*normal.x+normal.y*normal.y+normal.z*normal.z
if l == 0.0 then
call v.destroy()
debug call BJDebugMsg("vector.projectionPlane error: The length of the normal vector is 0.0!")
return 0
endif
set l = (projected.x*normal.x+projected.y*normal.y+projected.z*normal.z) / l
set v.x = projected.x  normal.x*l
set v.y = projected.y  normal.y*l
set v.z = projected.z  normal.z*l
return v
endmethod
method projectPlane takes vector normal returns nothing
local real l = normal.x*normal.x+normal.y*normal.y+normal.z*normal.z
if l == 0.0 then
debug call BJDebugMsg("vector.projectPlane error: The length of the normal vector is 0.0!")
return
endif
set l = (this.x*normal.x+this.y*normal.y+this.z*normal.z) / l
set this.x = this.x  normal.x*l
set this.y = this.y  normal.y*l
set this.z = this.z  normal.z*l
endmethod
static method getAngle takes vector a, vector b returns real
local real l = SquareRoot(a.x*a.x + a.y*a.y + a.z*a.z)*SquareRoot(b.x*b.x + b.y*b.y + b.z*b.z)
if l == 0 then
debug call BJDebugMsg("vector.getAngle error: The length of at least one of the vectors is 0.0!")
return 0.0
endif
return Acos((a.x*b.x+a.y*b.y+a.z*b.z)/l)
endmethod
method rotate takes vector axis, real angle returns nothing
local real xx
local real xy
local real xz
local real yx
local real yy
local real yz
local real zx
local real zy
local real zz
local real al = axis.x*axis.x+axis.y*axis.y+axis.z*axis.z
local real f
local real c = Cos(angle)
local real s = Sin(angle)
if al == 0.0 then
debug call BJDebugMsg("vector.rotate error: The length of the axis vector is 0.0!")
return
endif
set f = (this.x*axis.x+this.y*axis.y+this.z*axis.z) / al
set zx = axis.x*f
set zy = axis.y*f
set zz = axis.z*f
set xx = this.xzx
set xy = this.yzy
set xz = this.zzz
set al = SquareRoot(al)
set yx = (axis.y*xz  axis.z*xy)/al
set yy = (axis.z*xx  axis.x*xz)/al
set yz = (axis.x*xy  axis.y*xx)/al
set this.x=xx*c+yx*s+zx
set this.y=xy*c+yy*s+zy
set this.z=xz*c+yz*s+zz
endmethod
private static location loc = Location(0.0,0.0)
static method createTerrainPoint takes real x, real y returns vector
local vector v = vector.allocate()
call MoveLocation(vector.loc,x,y)
set v.x=x
set v.y=y
set v.z=GetLocationZ(loc)
return v
endmethod
method getTerrainPoint takes real x, real y returns nothing
call MoveLocation(vector.loc,x,y)
set this.x=x
set this.y=y
set this.z=GetLocationZ(loc)
endmethod
static method createTerrainNormal takes real x, real y, real sampleRadius returns vector
local vector v = vector.allocate()
local real zx
local real zy
call MoveLocation(vector.loc, xsampleRadius, y)
set zx=GetLocationZ(vector.loc)
call MoveLocation(vector.loc, x+sampleRadius, y)
set zx=zxGetLocationZ(vector.loc)
call MoveLocation(vector.loc, x, ysampleRadius)
set zy=GetLocationZ(vector.loc)
call MoveLocation(vector.loc, x, y+sampleRadius)
set zy=zyGetLocationZ(vector.loc)
set sampleRadius=2*sampleRadius
set v.x = zx*sampleRadius
set v.y = zy*sampleRadius
set v.z = sampleRadius*sampleRadius
return v
endmethod
method getTerrainNormal takes real x, real y, real sampleRadius returns nothing
local real zx
local real zy
call MoveLocation(vector.loc, xsampleRadius, y)
set zx=GetLocationZ(vector.loc)
call MoveLocation(vector.loc, x+sampleRadius, y)
set zx=zxGetLocationZ(vector.loc)
call MoveLocation(vector.loc, x, ysampleRadius)
set zy=GetLocationZ(vector.loc)
call MoveLocation(vector.loc, x, y+sampleRadius)
set zy=zyGetLocationZ(vector.loc)
set sampleRadius=2*sampleRadius
set this.x = zx*sampleRadius
set this.y = zy*sampleRadius
set this.z = sampleRadius*sampleRadius
endmethod
method isInCylinder takes vector cylinderOrigin, vector cylinderHeight, real cylinderRadius returns boolean
local real l
local real x = this.xcylinderOrigin.x
local real y = this.ycylinderOrigin.y
local real z = this.zcylinderOrigin.z
if x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z < 0.0 then
return false
endif
set x = xcylinderHeight.x
set y = ycylinderHeight.y
set z = zcylinderHeight.z
if x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z > 0.0 then
return false
endif
set l = cylinderHeight.x*cylinderHeight.x+cylinderHeight.y*cylinderHeight.y+cylinderHeight.z*cylinderHeight.z
if l == 0.0 then
debug call BJDebugMsg("vector.isInCylinder error: The length of the cylinderHeight vector is 0.0!")
return false
endif
set l = (x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z) / l
set x = x  cylinderHeight.x*l
set y = y  cylinderHeight.y*l
set z = z  cylinderHeight.z*l
if x*x+y*y+z*z > cylinderRadius*cylinderRadius then
return false
endif
return true
endmethod
method isInCone takes vector coneOrigin, vector coneHeight, real coneRadius returns boolean
local real l
local real x = this.xconeOrigin.x
local real y = this.yconeOrigin.y
local real z = this.zconeOrigin.z
if x*coneHeight.x+y*coneHeight.y+z*coneHeight.z < 0.0 then
return false
endif
set l = coneHeight.x*coneHeight.x+coneHeight.y*coneHeight.y+coneHeight.z*coneHeight.z
if l == 0.0 then
debug call BJDebugMsg("vector.isInCone error: The length of the coneHeight vector is 0.0!")
return false
endif
set l = (x*coneHeight.x+y*coneHeight.y+z*coneHeight.z) / l
set x = x  coneHeight.x*l
set y = y  coneHeight.y*l
set z = z  coneHeight.z*l
if SquareRoot(x*x+y*y+z*z) > coneRadius*(1.0l) then
return false
endif
return true
endmethod
method isInSphere takes vector sphereOrigin, real sphereRadius returns boolean
if sphereRadius*sphereRadius < ((this.xsphereOrigin.x)*(this.xsphereOrigin.x)+(this.ysphereOrigin.y)*(this.ysphereOrigin.y)+(this.zsphereOrigin.z)*(this.zsphereOrigin.z)) then
return false
endif
return true
endmethod
endstruct
endlibrary
library VectorLib requires Vector
endlibrary
__________________
