[vJASS] [Snippet] VectorMath

Discussion in 'Submissions' started by AGD, Jul 15, 2018.

1. AGD

Joined:
Mar 29, 2016
Messages:
399
Resources:
13
Spells:
7
Tutorials:
1
JASS:
5
Resources:
13
Code
Code (vJASS):

library VectorMath /* v1.01

*/
uses /*

*/
*/
NxList        /*  https://github.com/nestharus/JASS/blob/master/jass/Data%20Structures/NxList/script.j
*/
ErrorMessage  /*  https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage/main.j

*/
//! novjass

/*
Description:

A Vector is a 1x3 matrix representation of... anything! But it is usually used in mathematics to
represent something that have components in 3D space (x, y, and z) such as a point, line, force,
velocity, accleration, etc.

This snippet is a reinvention of Anitarf's Vector library. This attempts to provide more functionality
and a better API to the users. The old Vector library also have redundant codes and in most parts, does
not adhere to the concept of DRY (Do not Repeat Yourself) in programming, making its script significantly
longer than it ought to be.

One important key feature that this snippet has is 'hooks'. Basically, it allows you to turn a Vector
into a function of one or more Vectors. This can be used to easily describe relative motions such as
Relative Orbital Motions (Ex: The moon is orbiting the Earth while the Earth itself is also orbiting the
Sun, while the Sun is also orbiting around another object) and many more.

*/

|=====|
| API |
|=====|
/*
*/
struct Vector/*

*/
static constant Vector NULL     /*  Vector(0)

- Constant unit vectors:
*/
static constant Vector X_AXIS   /*
*/
static constant Vector Y_AXIS   /*
*/
static constant Vector Z_AXIS   /*

- Fields:
*/
real x                          /*
*/
real y                          /*
*/
real z                          /*
*/
real magnitude                  /*
*/
boolean zero                    /*  Checks if the vector has zero magnitude
*/
debug boolean constant          /*  Checks if the vector is one of the constant unit vectors
*/
debug boolean allocated         /*

- Methods: You can append a negative (-) sign to the vector arguments to temporarily inverse them inside the
methods they are passed into, but you can't do this to the vector instance for whom the method is called.
For example, if you want to get the difference between two vectors, you can do "Vector.sum(vecA, -vecB)".
*/
static method   create              takes real x, real y, real z                            returns Vector/*
*/
method          destroy             takes nothing                                           returns nothing/*
- Constructor/Destructor

*/
static method   operator []         takes Vector whichVector                                returns Vector/*
- Copy Constructor
*/
static method   operator []=        takes Vector destination, Vector source                 returns nothing/*
- Overwrite Operator
*/
method          operator ==         takes Vector whichVector                                returns boolean/*
*/
method          operator !=         takes Vector whichVector                                returns boolean/*
*/
method          operator <          takes Vector whichVector                                returns boolean/*
*/
method          operator >          takes Vector whichVector                                returns boolean/*
- Relational Operators
- The == and != operators check if the two vectors have the same components
- The < and > operators compares the magnitude of the two vectors

*/
static method   getAngle            takes Vector vecA, Vector vecB                          returns real/*
- Returns the angle between two vectors in radians

*/
static method   sum                 takes Vector vecA, Vector vecB                          returns Vector/*
*/
method          add                 takes Vector whichVector                                returns this/*

*/
static method   getScaled           takes Vector whichVector, real scaleValue               returns Vector/*
*/
method          scale               takes real scaleValue                                   returns this/*

*/
static method   getDirection        takes nothing                                           returns Vector/*
- Returns the vector's unit vector
*/
method          setDirection        takes Vector whichVector                                returns this/*
- <whichVector> need not be a unit vector

*/
static method   getRotated          takes Vector whichVector, Vector axis, real radians     returns Vector/*
*/
method          rotate              takes Vector axis, real radians                         returns this/*

*/
static method   inverse             takes Vector whichVector                                returns Vector/*
- Returns the negative of this vector as a new vector
*/
method          invert              takes nothing                                           returns this/*
- Turns this vector into its negative

*/
static method   scalarProduct       takes Vector vecA, Vector vecB                          returns real/*
- Performs a dot product between two vectors (vecA.vecB)
*/
static method   vectorProduct       takes Vector vecA, Vector vecB                          returns Vector/*
- Performs a cross product between two vectors (vecA x vecB)

*/
static method   scalarTripleProduct takes Vector vecA, Vector vecB, Vector vecC             returns real/*
- Returns (vecA x vecB . vecC)
*/
static method   vectorTripleProduct takes Vector vecA, Vector vecB, Vector vecC             returns Vector/*
- Returns (vecA x vecB x vecC)

*/
static method   vectorProjection    takes Vector whichVector, Vector direction              returns Vector/*
*/
method          projectToVector     takes Vector direction                                  returns this/*
- Direction vector must not be zero

*/
static method   planeProjection     takes Vector whichVector, Vector normal                 returns Vector/*
*/
method          projectToPlane      takes Vector normal                                     returns this/*
- Normal vector must not be zero

*/
method          hook                takes Vector whichVector                                returns this/*
*/
method          unhook              takes Vector whichVector                                returns this/*
*/
method          clearHooks          takes nothing                                           returns this/*
*/
method          clearLinks          takes nothing                                           returns this/*
- Hooking a vector causes this vector to be dependent on the properties of the hooked vector, turning this vector
into a function of another vector (or vectors, since you can hook multiple vectors)
- In other words, any modification on the hooked vector will also modify this vector
- Vector(this).x = this.x + hookedVec[1].x + ... + hookedVec[N].x (Where 'this.x' is this vector's 'own x')
- clearHooks() unhooks all of this vector's hooked vectors
- clearLinks() unhooks this vector from all of its hookers

*/
//! endnovjass

private module Init
private static method onInit takes nothing returns nothing
call initTables()
call initConstantVectors()
call initObjectStack()
endmethod
endmodule

private struct Hook extends array
implement NxList
Vector data
endstruct

implement NxList
Vector data
endstruct

struct Vector extends array

private static TableArray hookTable
private static TableArray table
private static integer array recycler

private real xComponent
private real yComponent
private real zComponent

static constant method operator NULL takes nothing returns thistype
return 0
endmethod
static constant method operator X_AXIS takes nothing returns thistype
return 1
endmethod
static constant method operator Y_AXIS takes nothing returns thistype
return 2
endmethod
static constant method operator Z_AXIS takes nothing returns thistype
return 3
endmethod

debug method operator allocated takes nothing returns boolean
debug return recycler[this] == -1
debug endmethod
debug method operator constant takes nothing returns boolean
debug return (this) == (X_AXIS) or (this) == (Y_AXIS) or (this) == (Z_AXIS)
debug endmethod

private method operator sign takes nothing returns integer
if this < 0 then
return -1
endif
return 1
endmethod

private method updateX takes real value returns nothing
local real dx = value - this.xComponent
loop
endloop
set this.xComponent = value
endmethod
private method updateY takes real value returns nothing
local real dy = value - this.yComponent
loop
endloop
set this.yComponent = value
endmethod
private method updateZ takes real value returns nothing
local real dz = value - this.zComponent
loop
endloop
set this.zComponent = value
endmethod

method operator x= takes real value returns nothing
debug call ThrowError(this.constant,            "VectorMath", "x=", "thistype", this, "Attempted to edit an attribute of a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "x=", "thistype", this, "Attempted to use an unallocated instance")
call this.updateX(value)
endmethod
method operator x takes nothing returns real
debug call ThrowError(not this.allocated,       "VectorMath", "x", "thistype", this, "Attempted to use an unallocated instance")
return this.xComponent
endmethod

method operator y= takes real value returns nothing
debug call ThrowError(this.constant,            "VectorMath", "y=", "thistype", this, "Attempted to edit an attribute of a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "y=", "thistype", this, "Attempted to use an unallocated instance")
call this.updateY(value)
endmethod
method operator y takes nothing returns real
debug call ThrowError(not this.allocated,       "VectorMath", "y", "thistype", this, "Attempted to use an unallocated instance")
return this.yComponent
endmethod

method operator z= takes real value returns nothing
debug call ThrowError(this.constant,            "VectorMath", "z=", "thistype", this, "Attempted to edit an attribute of a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "z=", "thistype", this, "Attempted to use an unallocated instance")
call this.updateZ(value)
endmethod
method operator z takes nothing returns real
debug call ThrowError(not this.allocated,       "VectorMath", "x=", "thistype", this, "Attempted to use an unallocated instance")
return this.zComponent
endmethod

static method create takes real x, real y, real z returns thistype
local thistype this = recycler[0]
debug call ThrowError(this == 0,                "VectorMath", "create()", "thistype", 0, "Overflow")
set recycler[0] = recycler[this]
debug set recycler[this] = -1
set this.xComponent = x
set this.yComponent = y
set this.zComponent = z
call Hook(this).clear()
return this
endmethod

method update takes real x, real y, real z returns thistype
local real dx
local real dy
local real dz
local thistype data
debug call ThrowError(this.constant,            "VectorMath", "update()", "thistype", this, "Attempted to edit an attribute of a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "update()", "thistype", this, "Attempted to use an unallocated instance")
set dx = x - this.x
set dy = y - this.y
set dz = z - this.z
loop
set data.xComponent = data.x + dx
set data.yComponent = data.y + dy
set data.zComponent = data.z + dz
endloop
endif
set this.xComponent = x
set this.yComponent = y
set this.zComponent = z
return this
endmethod

static method operator [] takes thistype vec returns thistype
local integer sign = vec.sign
set vec = vec*sign
debug call ThrowError(not vec.allocated,        "VectorMath", "operator[]", "thistype", vec, "Attempted to use an unallocated instance")
return create(vec.x*sign, vec.y*sign, vec.z*sign)
endmethod
static method operator []= takes thistype this, thistype vec returns nothing
local integer sign = vec.sign
set vec = vec*sign
debug call ThrowError(not this.allocated,       "VectorMath", "operator[]=", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not vec.allocated,        "VectorMath", "operator[]=", "thistype", vec, "Attempted to use an unallocated instance")
call this.update(vec.x*sign, vec.y*sign, vec.z*sign)
endmethod

method operator zero takes nothing returns boolean
debug call ThrowError(not this.allocated,       "VectorMath", "zero", "thistype", this, "Attempted to use an unallocated instance")
return not (this.x != 0.00 or this.y != 0.00 or this.z != 0.00)
endmethod

static method sum takes thistype a, thistype b returns thistype
local integer signA = a.sign
local integer signB = b.sign
set a = a*signA
set b = b*signB
debug call ThrowError(not a.allocated,          "VectorMath", "sum()", "thistype", a, "Attempted to use an unallocated instance")
debug call ThrowError(not b.allocated,          "VectorMath", "sum()", "thistype", b, "Attempted to use an unallocated instance")
return create(a.x*signA + b.x*signB, a.y*signA + b.y*signB, a.z*signA + b.z*signB)
endmethod
method add takes thistype vec returns thistype
local integer sign = vec.sign
set vec = vec*sign
debug call ThrowError(this.constant,            "VectorMath", "add()", "thistype", this, "Attempted to edit an attribute of a constant vector")
return this.update(this.x + vec.x*sign, this.y + vec.y*sign, this.z + vec.z*sign)
endmethod

static method getScaled takes thistype vec, real factor returns thistype
local integer sign = vec.sign
set vec = vec*sign
debug call ThrowError(not vec.allocated,        "VectorMath", "getScaled()", "thistype", vec, "Attempted to use an unallocated instance")
set factor = factor*sign
return create(vec.x*factor, vec.y*factor, vec.z*factor)
endmethod
method scale takes real factor returns thistype
debug call ThrowError(this.constant,            "VectorMath", "scale()", "thistype", this, "Attempted to scale a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "scale()", "thistype", this, "Attempted to use an unallocated instance")
return this.update(this.x*factor, this.y*factor, this.z*factor)
endmethod

method operator magnitude takes nothing returns real
debug call ThrowError(not this.allocated,       "VectorMath", "magnitude", "thistype", this, "Attempted to use an unallocated instance")
return SquareRoot(this.x*this.x + this.y*this.y + this.z*this.z)
endmethod
method operator magnitude= takes real value returns nothing
debug call ThrowError(this.constant,            "VectorMath", "magnitude=", "thistype", this, "Attempted to edit an attribute of a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "magnitude=", "thistype", this, "Attempted to use an unallocated instance")
call this.scale(value/this.magnitude)
endmethod

method operator == takes thistype vec returns boolean
local integer signA = this.sign
local integer signB = vec.sign
set this = this*signA
set vec = vec*signB
debug call ThrowError(not this.allocated,       "VectorMath", "operator==", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not vec.allocated,        "VectorMath", "operator==", "thistype", this, "Attempted to use an unallocated instance")
return this.x*signA == vec.x*signB and this.y*signA == vec.y*signB and this.z*signA == vec.z*signB
endmethod
method operator < takes thistype vec returns boolean
set this = this*this.sign
set vec = vec*vec.sign
debug call ThrowError(not this.allocated,       "VectorMath", "operator<", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not vec.allocated,        "VectorMath", "operator<", "thistype", this, "Attempted to use an unallocated instance")
return this.x*this.x + this.y*this.y + this.z*this.z < vec.x*vec.x + vec.y*vec.y + vec.z*vec.z
endmethod

method getDirection takes nothing returns thistype
local real magnitude = this.magnitude
debug call ThrowError(not this.allocated,       "VectorMath", "getDirection()", "thistype", this, "Attempted to use an unallocated instance")
return create(this.x/magnitude, this.y/magnitude, this.z/magnitude)
endmethod

method setDirection takes thistype direction returns thistype
local integer sign = direction.sign
local real magnitude = this.magnitude
set direction = direction*sign
debug call ThrowError(this.constant,            "VectorMath", "setDirection()", "thistype", this, "Attempted to edit an attribute of a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "setDirection()", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not direction.allocated,  "VectorMath", "setDirection()", "thistype", direction, "Attempted to use an unallocated instance")
debug call ThrowError(direction.zero,           "VectorMath", "setDirection()", "thistype", direction, "The direction vector is zero")
call this.update(direction.x*sign, direction.y*sign, direction.z*sign)
set this.magnitude = magnitude
return this
endmethod

static method inverse takes thistype vec returns thistype
local integer sign = -vec.sign
set vec = -vec*sign
debug call ThrowError(not vec.allocated,        "VectorMath", "inverse()", "thistype", vec, "Attempted to use an unallocated instance")
return create(vec.x*sign, vec.y*sign, vec.z*sign)
endmethod
method invert takes nothing returns thistype this
debug call ThrowError(this.constant,            "VectorMath", "invert()", "thistype", this, "Attempted to invert a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "invert()", "thistype", this, "Attempted to use an unallocated instance")
return this.scale(-1.00)
endmethod

static method scalarProduct takes thistype a, thistype b returns real
local integer signA = a.sign
local integer signB = b.sign
set a = a*signA
set b = b*signB
debug call ThrowError(not a.allocated,          "VectorMath", "scalarProduct()", "thistype", a, "Attempted to use an unallocated instance")
debug call ThrowError(not b.allocated,          "VectorMath", "scalarProduct()", "thistype", b, "Attempted to use an unallocated instance")
return (a.x*b.x + a.y*b.y + a.z*b.z)*signA*signB
endmethod
static method vectorProduct takes thistype a, thistype b returns thistype
local integer signA = a.sign
local integer signB = b.sign
local integer sign = signA*signB
set a = a*signA
set b = b*signB
debug call ThrowError(not a.allocated,          "VectorMath", "vectorProduct()", "thistype", a, "Attempted to use an unallocated instance")
debug call ThrowError(not b.allocated,          "VectorMath", "vectorProduct()", "thistype", b, "Attempted to use an unallocated instance")
return create((a.y*b.z - a.z*b.y)*sign, (a.z*b.x - a.x*b.z)*sign, (a.x*b.y - a.y*b.x)*sign)
endmethod

static method scalarTripleProduct takes thistype a, thistype b, thistype c returns real
debug call ThrowError(recycler[a*a.sign] != -1, "VectorMath", "scalarTripleProduct()", "thistype", a*a.sign, "Attempted to use an unallocated instance")
debug call ThrowError(recycler[b*b.sign] != -1, "VectorMath", "scalarTripleProduct()", "thistype", b*b.sign, "Attempted to use an unallocated instance")
debug call ThrowError(recycler[c*c.sign] != -1, "VectorMath", "scalarTripleProduct()", "thistype", c*c.sign, "Attempted to use an unallocated instance")
return scalarProduct(vectorProduct(a, b), c)
endmethod
static method vectorTripleProduct takes thistype a, thistype b, thistype c returns thistype
local real m
local real n
local integer signB = b.sign
local integer signC = c.sign
set a = a*a.sign
set b = b*signB
set c = c*signC
debug call ThrowError(not a.allocated,          "VectorMath", "vectorTripleProduct()", "thistype", a, "Attempted to use an unallocated instance")
debug call ThrowError(not b.allocated,          "VectorMath", "vectorTripleProduct()", "thistype", b, "Attempted to use an unallocated instance")
debug call ThrowError(not c.allocated,          "VectorMath", "vectorTripleProduct()", "thistype", c, "Attempted to use an unallocated instance")
set m = (a.x*b.x + a.y*b.y + a.z*b.z)*signC
set n = (a.x*c.x + a.y*c.y + a.z*c.z)*signB
return create(b.x*n - c.x*m, b.y*n - c.y*m, b.z*n - c.z*m)
endmethod

static method getAngle takes thistype a, thistype b returns real
debug set a = a*a.sign
debug set b = b*b.sign
debug call ThrowError(not a.allocated,          "VectorMath", "getAngle()", "thistype", a, "Attempted to use an unallocated instance")
debug call ThrowError(not b.allocated,          "VectorMath", "getAngle()", "thistype", b, "Attempted to use an unallocated instance")
debug call ThrowError(a.zero or b.zero,         "VectorMath", "getAngle()", "thistype", 0, "Atleast one of the vector is zero")
return Acos(scalarProduct(a, b)/(thistype(a*a.sign).magnitude*thistype(b*b.sign).magnitude))
endmethod

method projectToVector takes thistype direction returns thistype
local real square
set direction = direction*direction.sign
debug call ThrowError(this.constant,            "VectorMath", "projectToVector()", "thistype", this, "Attempted to project a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "projectToVector()", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not direction.allocated,  "VectorMath", "projectToVector()", "thistype", direction, "Attempted to use an unallocated instance")
debug call ThrowError(direction.zero,           "VectorMath", "projectToVector()", "thistype", direction, "The direction vector is zero")
set square = ((this.x*direction.x + this.y*direction.y + this.z*direction.z)/(direction.x*direction.x + direction.y*direction.y + direction.z*direction.z))*direction.sign
return this.update(direction.x*square, direction.y*square, direction.z*square)
endmethod
method projectToPlane takes thistype normal returns thistype
local real l
set normal = normal*normal.sign
set l = (this.x*normal.x + this.y*normal.y + this.z*normal.z)/(normal.x*normal.x + normal.y*normal.y + normal.z*normal.z)
debug call ThrowError(this.constant,            "VectorMath", "projectToPlane()", "thistype", this, "Attempted to project a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "projectToPlane()", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not normal.allocated,     "VectorMath", "projectToPlane()", "thistype", normal, "Attempted to use an unallocated instance")
debug call ThrowError(normal.zero,              "VectorMath", "projectToPlane()", "thistype", normal, "The normal vector is zero")
return this.update(this.x - normal.x*l, this.y - normal.y*l, this.z - normal.z*l)
endmethod

static method vectorProjection takes thistype vec, thistype direction returns thistype
debug set vec = vec*vec.sign
debug set direction = direction*direction.sign
debug call ThrowError(not vec.allocated,        "VectorMath", "vectorProjection()", "thistype", vec, "Attempted to use an unallocated instance")
debug call ThrowError(not direction.allocated,  "VectorMath", "vectorProjection()", "thistype", direction, "Attempted to use an unallocated instance")
debug call ThrowError(direction.zero,           "VectorMath", "vectorProjection()", "thistype", direction, "The direction vector is zero")
return thistype[vec].projectToVector(direction)
endmethod
static method planeProjection takes thistype vec, thistype normal returns thistype
debug set vec = vec*vec.sign
debug set normal = normal*normal.sign
debug call ThrowError(not vec.allocated,        "VectorMath", "planeProjection()", "thistype", vec, "Attempted to use an unallocated instance")
debug call ThrowError(not normal.allocated,     "VectorMath", "planeProjection()", "thistype", normal, "Attempted to use an unallocated instance")
debug call ThrowError(normal.zero,              "VectorMath", "planeProjection()", "thistype", normal, "The normal vector is zero")
return thistype[vec].projectToPlane(normal)
endmethod

method rotate takes thistype axis, real rad returns thistype
local real xx
local real xy
local real xz
local real zx
local real zy
local real zz
local real al
local real factor
local integer sign = axis.sign
set axis = axis*sign
set al = (axis.x*axis.x + axis.y*axis.y + axis.z*axis.z)*sign
debug call ThrowError(this.constant,            "VectorMath", "rotate()", "thistype", this, "Attempted to rotate a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "rotate()", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not axis.allocated,       "VectorMath", "rotate()", "thistype", axis, "Attempted to use an unallocated instance")
debug call ThrowError(axis.zero,                "VectorMath", "rotate()", "thistype", axis, "The axis vector is zero")
set factor = (scalarProduct(this, axis)/al)
set zx = axis.x*factor
set zy = axis.y*factor
set zz = axis.z*factor
set xx = this.x - zx
set xy = this.y - zy
set xz = this.z - zz
set al = SquareRoot(al)
return this.update(xx*cos + ((axis.y*xz - axis.z*xy)/al)*sin + zx, /*
*/
xy*cos + ((axis.z*xx - axis.x*xz)/al)*sin + zy, /*
*/
xz*cos + ((axis.x*xy - axis.y*xx)/al)*sin + zz)
endmethod
static method getRotated takes thistype vec, thistype axis, real rad returns thistype
debug set vec = vec*vec.sign
debug set axis = axis*axis.sign
debug call ThrowError(not vec.allocated,        "VectorMath", "getRotated()", "thistype", vec, "Attempted to use an unallocated instance")
debug call ThrowError(not axis.allocated,       "VectorMath", "getRotated()", "thistype", axis, "Attempted to use an unallocated instance")
debug call ThrowError(axis.zero,                "VectorMath", "getRotated()", "thistype", axis, "The axis vector is zero")
endmethod

private method joint takes thistype vec returns nothing
local Hook hook = Hook(this).enqueue()
set hook.data = vec
set hookTable[this][vec] = hook
endmethod
private method unjoint takes thistype vec returns nothing
call Hook(hookTable[this][vec]).remove()
call hookTable[this].remove(vec)
endmethod

private method setupHook takes thistype vec returns nothing
local integer count = table[this][vec]
if count == 0 then
call this.joint(vec)
endif
set table[this][vec] = count + 1
endmethod
private method destroyHook takes thistype vec returns nothing
local integer count = table[this][vec] - 1
set table[this][vec] = count
if count == 0 then
call this.unjoint(vec)
endif
endmethod

private method setupHooksToList takes thistype vec returns nothing
local Hook node = Hook(vec).first
local thistype data
local integer count
loop
exitwhen node == 0
set data = node.data
set count = table[this][data]
if count == 0 then
call this.joint(data)
endif
set table[this][data] = count + table[vec][data]
set node = node.next
endloop
endmethod
private method destroyHooksToList takes thistype vec returns nothing
local Hook node = Hook(vec).first
local Hook nextNode
local thistype data
local integer count
loop
exitwhen node == 0
set nextNode = node.next
set data = node.data
set count = table[this][data] - table[vec][data]
if count == 0 then
call this.unjoint(data)
endif
set node = nextNode
endloop
endmethod

method hook takes thistype vec returns thistype
debug set vec = vec*vec.sign
debug call ThrowError(this.constant,            "VectorMath", "hook()", "thistype", this, "Attempted to hook using a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "hook()", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not vec.allocated,        "VectorMath", "hook()", "thistype", vec, "Attempted to use an unallocated instance")
debug call ThrowError(hookTable[this].has(vec), "VectorMath", "hook()", "thistype", this, "Attempted to hook an already hooked Vector")
call this.setupHook(vec)
call this.setupHooksToList(vec)
loop
exitwhen node == 0
call node.data.setupHook(vec)
call node.data.setupHooksToList(vec)
set node = node.next
endloop
endmethod
method unhook takes thistype vec returns thistype
debug set vec = vec*vec.sign
debug call ThrowError(this.constant,            "VectorMath", "unhook()", "thistype", this, "Attempted to unhook using a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "unhook()", "thistype", this, "Attempted to use an unallocated instance")
debug call ThrowError(not vec.allocated,        "VectorMath", "unhook()", "thistype", vec, "Attempted to use an unallocated instance")
debug call ThrowError(not hookTable[this].has(vec), "VectorMath", "unhook()", "thistype", this, "Attempted to unhook an unhooked Vector")
loop
exitwhen node == 0
set nextNode = node.next
call node.data.destroyHooksToList(vec)
call node.data.destroyHook(vec)
set node = nextNode
endloop
call this.destroyHooksToList(vec)
call this.destroyHook(vec)
return this
endmethod

method clearHooks takes nothing returns thistype
local Hook node = Hook(this).first
local Hook nextNode
debug call ThrowError(not this.allocated,       "VectorMath", "clearHooks()", "thistype", this, "Attempted to use an unallocated instance")
loop
exitwhen node == 0
set nextNode = node.next
call this.unhook(node.data)
set node = nextNode
endloop
return this
endmethod
method clearLinks takes nothing returns thistype
debug call ThrowError(not this.allocated,       "VectorMath", "clearLinks()", "thistype", this, "Attempted to use an unallocated instance")
loop
exitwhen node == 0
set nextNode = node.next
call node.data.unhook(this)
set node = nextNode
endloop
return this
endmethod

method destroy takes nothing returns nothing
debug call ThrowError(this.constant,            "VectorMath", "destroy()", "thistype", this, "Attempted to destroy a constant vector")
debug call ThrowError(not this.allocated,       "VectorMath", "destroy()", "thistype", this, "Double-free")
call this.clearHooks()
call Hook(this).destroy()
set this.xComponent = 0.00
set this.yComponent = 0.00
set this.zComponent = 0.00
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod

private static method initConstantVectors takes nothing returns nothing
set X_AXIS.xComponent = 1.00
set Y_AXIS.yComponent = 1.00
set Z_AXIS.zComponent = 1.00
debug set recycler[X_AXIS] = -1
debug set recycler[Y_AXIS] = -1
debug set recycler[Z_AXIS] = -1
endmethod

private static method initObjectStack takes nothing returns nothing
local integer node = 4
local integer maxIndex = JASS_MAX_ARRAY_SIZE - 2
set recycler[maxIndex] = 0
set recycler[0] = node
loop
exitwhen node == maxIndex
set recycler[node] = node + 1
set node = node + 1
endloop
endmethod

private static method initTables takes nothing returns nothing
set hookTable   = TableArray[JASS_MAX_ARRAY_SIZE - 1]
set linkTable   = TableArray[JASS_MAX_ARRAY_SIZE - 1]
set table       = TableArray[JASS_MAX_ARRAY_SIZE - 1]
endmethod

implement Init

endstruct

endlibrary

Version History

v1.00
- First Release

v1.01
- Improved debug messages
- Few bug fixes
- Uses a new allocation method to make the constant vectors TRULY constant
- Fixed few documentation errors

Last edited: Jul 20, 2018

Joined:
Mar 29, 2016
Messages:
399
Resources:
13
Spells:
7
Tutorials:
1
JASS:
5
Resources:
13
Updated

JASS Reviewer

Joined:
Mar 25, 2016
Messages:
1,286
Resources:
0
Resources:
0
I only took a look at the math functions so far.

Is there a reason why you use 'or' here?
Code (vJASS):

method operator zero takes nothing returns boolean
debug call ThrowError(not this.allocated,       "VectorMath", "zero", "thistype", this, "Attempted to use an unallocated instance")
return not (this.x != 0.00 or this.y != 0.00 or this.z != 0.00)
endmethod

The maths seems to be correct from what I can tell. At first I had a few problems with the rotate function, because I didn't know which algorithm you used.

I like that you can use negative vectors as parameters. It's a neat idea.

4. AGD

Joined:
Mar 29, 2016
Messages:
399
Resources:
13
Spells:
7
Tutorials:
1
JASS:
5
Resources:
13
I used the "not (... != ...)" instead of using '... == 0' to avoid the epsilon of the 'is equal to' operator.

JASS Reviewer

Joined:
Mar 25, 2016
Messages:
1,286
Resources:
0
Resources:
0
You are right. I assumed that '!=' is equivalent to 'not =='.

6. Almia

Joined:
Apr 24, 2012
Messages:
4,839
Resources:
35
Spells:
30
Tutorials:
4
JASS:
1
Resources:
35

vector math should be made in way that is as fast and light as possible

I see no point for the hooks

7. AGD

Joined:
Mar 29, 2016
Messages:
399
Resources:
13
Spells:
7
Tutorials:
1
JASS:
5
Resources:
13
When I said more functionality, I was only referring to the hook methods so I did not made more overkill in a way. In fact, I left out some utility functions in his lib like checking if a vector is in a sphere, cone, etc. since I thought it would fit better in an add-on lib.

As for the usefulness of the hooks, I would assert that they have many possible uses. A prime example would be in my private library UnitMotion, which I'll attach below.
In docking systems where the docks are moving, you could represent the positions of the dock and the attached objects with a vector. What you would do then is to just hook the position vector of the object into the dock's, and have a single periodic function that updates their position according to their position vector.
Another is for altering units' movement based on global (encompasses the whole map) environmental factors such as gravity, wind, water current, etc. Instead of including these factors in the calculation in your periodic functions, you just need to hook all units' velocity/acceleration vector into these forces' vectors initially (preferably at map init), and then you can already adjust freely the magnitude of these forces dynamically from time to time with a simple
`call GRAVITY.update(0.00, 0.00, GRAVITY.z*2.00)`
for example.

As for the overhead, it is very negligible. For unhooked vectors, the additional evaluation in the update methods are just at most 2 lines, with the other one being
`exitwhen node == 0`
. For hooked vectors, it will no longer be called an overhead since to replicate such behavior, you also need to do additional operations on you own.

The hooks would definitely simplify the code in some cases by a great deal.

Attached Files:

• UnitMotion.w3x
File size:
110 KB
Views:
17
Last edited: Aug 21, 2018