Author Topic: Working with vectors in NWN.  (Read 1053 times)

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Working with vectors in NWN.
« on: October 02, 2013, 04:14:34 am »


               The Vector data type in nwn, is a structure of three floats that represents offsets on the x,y and z axises.    For this tutorial I am going to do what I normally do and ignore the z axis. It is important to note however that there are times when it can not be ignored and  you will get odd results with you vectors by ignoring it.  Once case in point would be if you are calculating the position to create a placeable at.  If you ever find you created placeable floating in the air or buried in the ground you will need to start taking note of the z axis.  So I am treating the vectors in this thread as if they are 2D,  Just keep in mind, when you start working with vectors, that the third dimension is there just encase things start acting funny.  


Looking at a vector.

vector vA = Vector ( 1,2);

'Image

In the diagram above you can see that   vA   graphically drawn as an arrow with  the head 1 unit over and 2 units up from where it begins.   The arrow points in a direction that can be converted into an angle using the VectorToAngle function.   The length of the arrow could be found using the VectorMagnitude function.    

Here is a second vector.    

vector vB = vector (2,1);


'Image


Adding vectors.  

When adding vectors the x's are added together and the y's are added together to get the sum of the vectors.    It can be visualized graphically by placing the tail of one vector to the head of the other with the sum being the vector that would connect the tail of the first to the head of the second.   It look like this.  

'Image


Negative Vectors.    


A negative vector runs in the opposite direction from the original
( 180 degrees out).

'Image

Here is the subtraction of the same two vectors we added earlier.
Note that -vB runs in the opposite direction that vB ran.  

'Image


Now even though the above diagram is a 100% valid way of representing the subtraction,  I do not find it very useful to visualize it that way.  I will get into the reason why shortly but lets look at the subtraction a different way first. We all know that in basic math, or I hope we all do,  that the order of  subtracting and adding does not effect the results.    5 -2  is the same thing as -2 + 5.   It works the same way with vectors.  In the next diagram I am going to change the order I draw the vectors in,  It will not change the value of the results,  it will just display them with a different visual.  


'Image


The reason this visual is better when working in NWN is because 90% of the time, when you are subtracting vectors, you are looking for the vector between two positions.   A position of course is nothing but a vector that has its tail set a (0,0).    


For example.  We have a guard standing watch in a town that has rules on unsheathed weapons.   Anytime a person is detected, we want to the guard to turn to face them and  we will also determine how far away they are, just encase the guard has to react to a weapons violation.  

'Image


VectorToAngle returns the angle the vector is pointing in, setting the facing of the guard to that angle will make him turn towards the target.  


VectorMagnitude  will return the length of the vector,   In the case above will be the distance between oTarget and oGuard.  


Scaling  


When a vector is multiplied by a number both the x and y elements get multiplied by that number.  Since the ratio of x:y  remains the same, the angle of the vector does not change only the length is effected.

 'Image
               
               

               


                     Modifié par Lightfoot8, 02 octobre 2013 - 03:23 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #1 on: October 02, 2013, 04:15:06 am »


               reserved
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #2 on: October 02, 2013, 01:50:04 pm »


               *claps*
Nice work, thanks. '<img'>

Funky
               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #3 on: October 03, 2013, 07:40:02 pm »


               Very nice!
thanks LF.
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #4 on: October 11, 2013, 12:52:34 am »


               

Given that this thread is taking its time, I'll present taking the angle between two vectors. Ignoring the height (z-axis) there are two ways of directly calculating the angle:


The first one is to obtain the supplement of the supplement of the angle formed (alternating back and forth between the angle formed by the vectors and the angle formed when one of the vectors is reversed in direction). The two uses of an absolute value keep the result between 0.0 and 180.0.



float AngleBetweenVectors(vector A, vector B)
{
return fabs(fabs(VectorToAngle(-1.0 * A) - VectorToAngle(B)) - 180.0);
}

The second way is to use the dot product. The dot product multiplies the components of the vectors together, and adds them up to find a value related to the magnitude of the vectors and the cosine of the angle formed.



float AngleBetweenVectors(vector A, vector B)
{
float fMagA = sqrt(A.x * A.x + A.y * A.y);
float fMagB = sqrt(B.x * B.x + B.y * B.y);
if(fMagA * fMagB != 0.0) return acos((A.x * B.x + A.y * B.y)/(fMagA * fMagB));
return 0.0;
}

These two approaches will return nominally the same answer (the combination of the square root and the arc-cosine in the second calculation can cause the float value produced to be rounded slightly off the float produced by the first method), however the second approach will given a angle of 0.0 if either vector has a zero magnitude (the first method interprets a vector with a zero magnitude as facing East, that is having the same direction as Vector(1.0, 0.0, 0.0)). The reason that VectorMagnitude() was not used in the second approach is that if either vector has a z-axis component then we aren't actually calculating the angle between the vectors but rather the angle between their projections on the x-y plane. This calculation is often desired as vectors are more likely used to determine direction of travel rather than spatial arrangements. However if one wants the true angle between two vectors (including the z-axis term) then the dot product approach is the only solution as VectorToAngle() only deals with the x-y plane.



float AngleBetweenVectors(vector A, vector B)
{
float fMagA = VectorMagnitude(A);
float fMagB = VectorMagnitude(B);
if(fMagA * fMagB != 0.0) return acos((A.x * B.x + A.y * B.y + A.z * B.z)/(fMagA * fMagB));
return 0.0;
}


               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #5 on: June 21, 2014, 05:21:43 pm »


               

Apparently a change in this forum's BBC code has rendered all my "B)" expressions as the smiley face 'B)'.  I have reformatted the above post to get rid of this silliness.



               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #6 on: June 21, 2014, 05:49:54 pm »


               

its also worthwhile to bump a useful thread



               
               

               
            

Legacy_SKIPPNUTTZ

  • Full Member
  • ***
  • Posts: 148
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #7 on: July 05, 2014, 04:00:30 pm »


               

Thanks a lot lightfoot, great post. I started looking into vectors a lot recently trying to find an intelligent exploit free teleport spell which has line of sight and can't change Z axis.



               
               

               
            

Legacy_Pstemarie

  • Hero Member
  • *****
  • Posts: 4368
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #8 on: December 13, 2014, 09:24:07 pm »


               

My brain is sore.



               
               

               
            

Legacy_ShadowM

  • Hero Member
  • *****
  • Posts: 1373
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #9 on: December 13, 2014, 11:31:46 pm »


               

//Move placable along selected Axis
//Negative amounts move in opposite direction
//Example -10 on Z will lower the placable.
//1 = X
//2 = Y
//3 = Z
void MovePlacable(object oTarget,int iAxis,int iAmount);
void MovePlacable(object oTarget,int iAxis,int iAmount)
{
int iStatic =GetUseableFlag(oTarget);

string sGRef =GetResRef(oTarget);
float fAmount =IntToFloat(iAmount);
//We want to keep the numbers low to be more precise when needed.
float fAdjust = fAmount /10;
float fAdjustx;
float fAdjusty;
float fAdjustz;

switch(iAxis)
{
case 1:fAdjustx=fAdjust;break;
case 2:fAdjusty=fAdjust;break;
case 3:fAdjustz=fAdjust;break;
}

object oGArea     = GetArea(oTarget);
vector vGPosition = GetPosition(oTarget);
float  fGfacing   = GetFacing(oTarget);

     float vX = vGPosition.x+fAdjustx;
     float vY = vGPosition.y+fAdjusty;;
     float vZ = vGPosition.z+fAdjustz;;

  vector vNewPos = Vector(vX, vY,vZ);

location LNew = Location(oGArea,vNewPos,fGfacing);
object oNew = CreateObject(OBJECT_TYPE_PLACEABLE,sGRef,LNew,FALSE);
if(iStatic)SetUseableFlag(oTarget,1);
DestroyObject(oTarget,0.5);

}
 

               
               

               
            

Legacy_Tarot Redhand

  • Hero Member
  • *****
  • Posts: 4165
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #10 on: December 16, 2014, 04:15:07 pm »


               
Thank you so much for this thread. I was just about to post something along these lines myself, so you've saved me a ton of work. There are a few small things missing which I will add.

 

In mathematics a vector is a set of ordered real numbers (a real number is simply a number that has a fractional part). In the Cartesian coordinate system they are used to define points in space, either 2D (x, y) or 3D (x, y, z). From this point on I will, for simplicity's sake deal with just 2D vectors. The coordinates 0,0 (or 0,0,0 in 3D) has a special name - it is called the origin and all vectors are defined relative to it. This is important when things such as rotation are considered. As the position of each object in an area in NwN is defined by a 3D vector so there is an origin - the extreme South-West corner of the area.

 

There are three main operations that can be carried out on vectors - addition, subtraction and multiplication (Note vectors may never have division applied to them, but there is a workaround). There are 2 different ways (that I know of - true mathematicians may know more) that these operations may be carried out. Scalar operations are those where a single number is applied to a vector. You can have scalar addition, scalar subtraction and scalar multiplication.

 

Examples where v is a vector and s is a single real number.

 

addition -         v = (v.x + s, v.y + s)

subtraction -    v = (v.x - s, v.y - s)

multiplication - v = (v.x * s, v.y * s)

 

Note it is possible to simulate scalar division by performing scalar multiplication. The trick is to use the reciprocal (don't panic explanation follows) of the divisor. The reciprocal is calculated by dividing the number 1.0 by (in this case) the divisor. Say you want to divide a vector by 2 you find the reciprocal of 2 (i.e. 1.0 / 2.0 = 0.5) and then perform scalar multiplication using that number.

 

As the operations involving 2 vectors have already been more than adequately explained I will move on.

 

Vectors can be considered to be straight lines drawn from the origin to the coordinates that are stored in the vector variable. As such they have a another property that can be calculated - their length. This is a relatively simple thing to calculate (note I writing the following equation in this manner due to lack of symbols) - length = square root((x * X) + (y * Y)).

 

Finally rotation. Through the use of trigonometry it is possible to rotate a vector around the origin. Now as that stands it's not very useful in NwN. However, by combining vector operations (i.e. operations involving two vectors) it is possible to make a vector orbit a different point in space. What you need to do is (the explanation is more complicated than the actuality) to move the vector you want to orbit to the location it would occupy if the point to be orbited was actually the origin, perform the rotation and then move the rotated vector back by the same amount as in your original move. Simply put ->

 

O is the vector to orbit

s is the vector to do the orbiting

a is the angle of rotation (how many degrees to travel)

 

move

s = s - o

 

rotate

s.x = s.x * Cosine(a) + s.y * Sine(a)

s.y = s.x * Sine(a) + s.y * Cosine(a)

 

move back

s = s + o

 

or in NwN script (the 4 variable declarations at the start are for illustrative purposes only)



    float fAngle;
    vector vStartPosition = GetPositionFromLocation(lcurrent);
    vector vCentrePos = GetPositionFromLocation(lcentre);
    vector vNewPosition;
 
//theoretically move vector to centre
 
    float fOldX = vStartPosition.x - vCentrePos.x;
    float fOldY = vStartPosition.y - vCentrePos.y;
 
//rotate about the origin (0,0)
 
    float fSine = sin(fAngle);
    float fCosine = cos(fAngle);
    float fWorkingX = fOldX * fCosine - fOldY * fSine;
    float fWorkingY = fOldX * fSine + fOldY * fCosine;
 
//theoretically move vector to new position
 
    vNewPosition.x = fWorkingX + vCentrePos.x;
    vNewPosition.y = fWorkingY + vCentrePos.y;


Be warned, unless you are careful the above code fragment could place the vector outside of the area which in turn may well crash NwN.

 

TR


               
               

               
            

Legacy_Tarot Redhand

  • Hero Member
  • *****
  • Posts: 4165
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #11 on: January 01, 2015, 10:08:56 am »


               
There is one other operation that I know about, that works with vectors. That is the Vector Product. While I can’t see any use for this in NwN, I am presenting it here for completeness. However, this one only works with 3d vectors and requires a little explanation of a further property of vectors. As I am not a mathematician, please forgive any discrepancies that may occur in this explanation.

 

First the dense bit. Given any three vectors in 3d space, as long as they do not constitute points along a single line, we can use those vectors to define a plane. In this instance a plane is not the living quarters of Gods, Demons and Monsters, but is in fact a flat surface. Think of it this way. Take a 2d drawing on a piece of paper and place it into 3d space at any angle you choose. Now the paper has length and width (x & y) but no thickness. The picture on the paper lies in the plane defined by the paper. On a side note, most of the placeable custom content that I have produced over the years are just textures on planes.

 

In the previous paragraph I said that three vectors are needed to define a plane. However, it is possible to define a plane using only two arbitrary vectors if we use the origin (0, 0, 0) as the third vector.

 

OK so onto the Vector Product. The vector product takes two vectors and creates a new vector from them. So if we have three vectors A, B and C we would write this as – C = A x B (pronounced A cross B, which is why the Vector Product is sometimes called the Cross Product). The new vector produced is at 90 degrees from the plane defined by A, B and the origin. This means that the dot products of A . C and B . C are both zero (0).

 

Given the complexity of the forgoing you could be forgiven for thinking that the calculations required must be horrendous. Fortunately, this is not so. If A is the vector (XA, YA, ZA) and B is the vector (XB, YB, ZB) then C = A x B = (XC, YC, ZC), which is calculated as follows –

 

XC = YA * ZB - ZA * YB

YC = ZA * XB - XB * ZB

ZC = XA * YB - YA * XB

 

One final thing to note is that, unlike the dot product, the order in which the two vectors are multiplied together is important – the result of A x B is different to the result of B x A. In fact A x B points in the opposite direction (i.e. by 180 degrees) to B x A (i.e. A x B = -(B x A)).

 

Well, I hope at least one person understands this.

 

Happy New Year.

 

TR


               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #12 on: January 03, 2015, 11:05:24 pm »


               


 


OK so onto the Vector Product. The vector product takes two vectors and creates a new vector from them. So if we have three vectors A, B and C we would write this as – C = A x B (pronounced A cross B, which is why the Vector Product is sometimes called the Cross Product). The new vector produced is at 90 degrees from the plane defined by A, B and the origin. This means that the dot products of A . C and B . C are both zero (0).
 



 


The interesting thing about the dot and cross product (and three component vector analysis) is that they were derived from a four dimensional mathematical construct - the quaternions.  Quaternions are normal numbers that contain six different values whose square is negative one (i, j, k, -i, -j, -k).  A quaternion is expressed as a + bi + cj + dk where a,b,c,d are real numbers.  The imaginary components i,j,k are such that i * j = k, j * k = i, and k * i = j, note that multiplication is associative but not commutative, (thus i * j = k but j * i = -k).  Multiplying two quaternions without the first part (that is bi + cj + dk) will yield the cross product minus the dot product.  Quaternions preserve magnitude that is the magnitude of one quaternion multiplied by the magnitude of another quaternion is equal to the magnitude of their products.


               
               

               
            

Legacy_Tarot Redhand

  • Hero Member
  • *****
  • Posts: 4165
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #13 on: January 03, 2015, 11:48:40 pm »


               

As I mentioned in a previous post, I am not a mathematician, so if I am wrong please forgive me. Having said that, I am puzzled by your definition of quaternions. As far as I knew a quaternion is a number that consists of four parts (1 real and 3 imaginary) and not six. I first came across them in relation to fractals and versions of the Julia and Mandelbrot sets. My understanding of a quaternion is the same as the one presented by wikipedia here.


 


There is also number system that consists of 8 parts - the octonions (wikipedia page on). I only mention this because fractint (home page) uses both number systems to generate the classic fractal images and I have been exploring the idea of incorporating certain fractals in images.


 


TR



               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Working with vectors in NWN.
« Reply #14 on: January 04, 2015, 12:51:00 am »


               


 As far as I knew a quaternion is a number that consists of four parts (1 real and 3 imaginary) and not six.


 


TR




 


Correct, a quaternion as mentioned above is a + bi + cj + dk (four parts).  However, six values (i, j, k, and their negatives) have a square of negative one.