Aller au contenu

Photo

Working with vectors in NWN.


  • Veuillez vous connecter pour répondre
16 réponses à ce sujet

#1
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
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 IPB

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 IPB


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 IPB


Negative Vectors.


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

Image IPB

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

Image IPB


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 IPB


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 IPB


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 IPB

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

  • OldTimeRadio, SKIPPNUTTZ et WhiteTiger aiment ceci

#2
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
reserved

#3
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
*claps*
Nice work, thanks. :)

Funky

#4
meaglyn

meaglyn
  • Members
  • 809 messages
Very nice!
thanks LF.

#5
WhiZard

WhiZard
  • Members
  • 1 204 messages

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;
}


#6
WhiZard

WhiZard
  • Members
  • 1 204 messages

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.



#7
henesua

henesua
  • Members
  • 3 864 messages

its also worthwhile to bump a useful thread



#8
SKIPPNUTTZ

SKIPPNUTTZ
  • Members
  • 80 messages

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.



#9
Pstemarie

Pstemarie
  • Members
  • 2 745 messages

My brain is sore.


  • OldTimeRadio et EzRemake aiment ceci

#10
ShadowM

ShadowM
  • Members
  • 768 messages

//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);

}
 


#11
Tarot Redhand

Tarot Redhand
  • Members
  • 2 683 messages
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


#12
Tarot Redhand

Tarot Redhand
  • Members
  • 2 683 messages
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


#13
WhiZard

WhiZard
  • Members
  • 1 204 messages

 

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.



#14
Tarot Redhand

Tarot Redhand
  • Members
  • 2 683 messages

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



#15
WhiZard

WhiZard
  • Members
  • 1 204 messages

 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.



#16
Tarot Redhand

Tarot Redhand
  • Members
  • 2 683 messages
I still do not understand what you are presenting here. I will therefore go back to basics as I understand them. This is merely to try to avoid any miscommunication. What we are talking about here are number systems. For this explanation I will deal only with three of them, starting at the simplest to the most complex. I (sorry if this caveat is getting boring by now) am not a mathematician and am presenting what I understand to be true. I may be wrong (in whole or in part) but what follows is what I believe to be true.
 
We’ll start right at the beginning with Real numbers. A real number is simply a number with a fractional part that is normally expressed as a decimal number. One thing to note is that the fractional part can be zero as in 7.0. In order to avoid any confusion, I will use the letter ‘r’ to denote when I am using a Real number as part of an example.
 
Complex numbers are numbers that have two parts. Both parts are Real numbers. Now this is where confusion can arise. The two parts have names. They are called the Real part and the imaginary part. It is my understanding that this arose because when they were first discovered the mathematicians of the day didn’t think that Complex numbers had any use in the real world. So they called the first part (the part that they knew had real world usage) Real and the part that they could see no use for (apart from calculating the square root of minus one) Imaginary. It turns out that in fact Complex numbers have many uses in fields such as engineering. These are the numbers that are manipulated in both the Julia and Mandelbrot sets to produce the classic fractal images. In the following notation I will use the letter ‘R’ to denote the real part and the letter ‘I’ to denote the imaginary part.
 
Complex Number C = C(Rr, Ir). What this says is that the complex number C consists of both a real part and an imaginary part. Both parts are Real Numbers which means that each part can be positive, negative or zero. Examples C(-1.4, 5.6), C(9.9, 0.0), C(0.0, -3.2). In order to avoid any confusion, I will use the letter ‘c’ to denote when I am using a Complex number as part of an example.
 
Quaternions are numbers that have four parts. Each part is a Real number. Again these parts are named. They are the Real part, the first imaginary part, the second imaginary part, the third imaginary part. Using standard notation these are normally written as r, i, j, k. For my illustrative notation I will use the letters ‘R’ for the real part, ‘I’ for the first imaginary part, ‘J’ for the second imaginary part and ‘K’ for the third imaginary part.
 
Quaternion Q = Q(Rr, Ir, Jr, Kr). What this says is that Quaternion Q consists of a real part and three imaginary parts. All parts are Real Numbers which means that each part can be positive, negative or zero.
 
There is a related number system, the numbers of which are called Biquaternions. Biquaternions are identical to Quaternions except that instead of each part being a Real number, they are instead Complex numbers. Or in my notation Biquaternion BQ = BQ(Rc, Ic, Jc, Kc).
 
Having explained the above I believe that you have either confused the Imaginary parts of Quaternions for Complex numbers or you are describing Biquaternions.
 
TR


#17
WhiZard

WhiZard
  • Members
  • 1 204 messages

I am not describing biquaternions.  The difference between the quaternion expression a + bi + cj + dk and the biquaternion expression a + bi + cj + dk is that for quaternions the values a,b,c, and d are real numbers, while for the biquaternions these numbers are complex numbers (adding in another pair of roots for -1).  To understand a quaternion lets first look at a complex number.  A complex number takes the form a + bi.  I can multiply two complex numbers together (a + bi) * (c + di) = e + fi by use of the distributive property of multiplication.  i and -i both have a square of -1, thus (1 + i) * (2 + i) = 1* 2 + 1* i + i * 2 + i * i = 2 + i + 2i - 1 = 1 + 3i.  Now let us say I had another complex field a + bj.  I would mathematically perform all operations for a + bj in the same way I would for a + bi.  Similarly I can create a third complex field a + bk.  A quaternion combines all three complex fields, establishing a relationship for multiplication that is ij = k, jk = i, and ki = j (this can be more simply stated as ijk = -1).  Thus a quaternion would be a + bi + cj + dk where a,b,c,d are real numbers.