1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[vJASS] [Snippet] VectorMath

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

  1. AGD

    AGD

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

    library VectorMath /* v1.01


        */
    uses /*

        */
    Table         /*  https://www.hiveworkshop.com/threads/snippet-new-table.188084/
        */
    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

        Resource Link:      https://www.hiveworkshop.com/threads/snippet-vectormath.307136/


        */
    //! 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

        private struct Link extends array
            implement NxList
            Vector data
        endstruct

        struct Vector extends array

            private static TableArray hookTable
            private static TableArray linkTable
            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
                local Link link = Link(this).first
                loop
                    exitwhen link == 0
                    set link.data.xComponent = link.data.xComponent + dx
                    set link = link.next
                endloop
                set this.xComponent = value
            endmethod
            private method updateY takes real value returns nothing
                local real dy = value - this.yComponent
                local Link link = Link(this).first
                loop
                    exitwhen link == 0
                    set link.data.yComponent = link.data.yComponent + dy
                    set link = link.next
                endloop
                set this.yComponent = value
            endmethod
            private method updateZ takes real value returns nothing
                local real dz = value - this.zComponent
                local Link link = Link(this).first
                loop
                    exitwhen link == 0
                    set link.data.zComponent = link.data.zComponent + dz
                    set link = link.next
                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()
                call Link(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
                local Link link = Link(this).first
                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")
                if link != 0 then
                    set dx = x - this.x
                    set dy = y - this.y
                    set dz = z - this.z
                    loop
                        exitwhen link == 0
                        set data = link.data
                        set data.xComponent = data.x + dx
                        set data.yComponent = data.y + dy
                        set data.zComponent = data.z + dz
                        set link = link.next
                    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 real cos = Cos(rad)
                local real sin = Sin(rad)
                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")
                return thistype[vec].rotate(axis, rad)
            endmethod

            private method joint takes thistype vec returns nothing
                local Hook hook = Hook(this).enqueue()
                local Link link = Link(vec).enqueue()
                set hook.data = vec
                set link.data = this
                set hookTable[this][vec] = hook
                set linkTable[vec][this] = link
            endmethod
            private method unjoint takes thistype vec returns nothing
                call Hook(hookTable[this][vec]).remove()
                call Link(linkTable[vec][this]).remove()
                call hookTable[this].remove(vec)
                call linkTable[vec].remove(this)
            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
                local Link node = Link(this).first
                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
                return this.add(vec)
            endmethod
            method unhook takes thistype vec returns thistype
                local Link node = Link(this).first
                local Link nextNode
                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")
                call this.add(-vec)
                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
                local Link node = Link(this).first
                local Link nextNode
                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 this.clearLinks()
                call Hook(this).destroy()
                call Link(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
  2. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    397
    Resources:
    13
    Spells:
    7
    Tutorials:
    1
    JASS:
    5
    Resources:
    13
    Updated
     
  3. Jampion

    Jampion

    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

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    397
    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.
     
  5. Jampion

    Jampion

    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

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,842
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    Anitarf's library is already an overkill, you made it worse imo

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

    I see no point for the hooks
     
  7. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    397
    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:

    Last edited: Aug 21, 2018