Rotating a Vector

Hi,

Would someone explain the trigonometry in the Vector.rotate() function on page 114 and the 3d rotation functions on pages 145-152 of Robert Penner’s Programming Flash MX?

Thanks,
Tim

I don’t have that book, but if you can post the code I can try to explain it for you.

-Al

You should find your answer in the Best Of Kirupa forum. brandon Williams has a very comprehensive tute about vectors and trigonometry.

pom (-:

I tried the tute but it doesn’t explain how to rotate a vector. If anyone has the book, please help!

is it a 3d vector you are trying to rotate? You mentioned the ‘rotation functions’ so I assume they’re there…

Id have to see some code to know what you need

how are you handling it? What kind of matrix functions do you have defined?

How does this function work?? I don’t get the trigonometry:

Vector.prototype.rotate=function(angle){
var ca=Math.cosD(angle);
var sa=Math.sinD(angle);
with(this){
var rx=xca-ysa;
var ry=xsa+yca;
x=rx;
y=ry;
}
};

I would just like you to know Im working on an explaination, its just taking me longer than I suspected :slight_smile:

thanks, i’ll be waiting

ok, looks like its basic 2D rotation from what I can tell. I dont know the definitions of Math.cosD() and Math.sinD() though I assume they are just sin and cos in degrees (as opposed to radians) since, well they should be heh.


Vector.prototype.rotate = function(angle) {
	var ca = Math.cosD(angle);
	var sa = Math.sinD(angle);
	with (this) {
		var rx = x*ca-y*sa;
		var ry = x*sa+y*ca;
		x = rx;
		y = ry;
	}
};

so this adds a rotate function to Vector instances defined simply by this class (which I would assume is in the book as well):


Vector = function(x, y) {
	this.x = x;
	this.y = y;
};

simple enough. Now, for anyone who doesnt know, a vector is a point in space typically defining a direction. Think in terms of 2D and not 3D since that is all thats essentially being done here… so clear your mind of 3D-ness and think of a 2D playingfield, or a cartesian plane, where in the middle, you are given a point of origin 0,0. Every thing on the left is -x and right is positive x. Everything above is positive y and everything below is -y (let me also point out this is in terms of Math and not Flash since the y in Flash is flipped) … actually I should make some visuals…

Cartesian plane:

Now anywhere on this plane you can have points like so

Given any one of these points, a direction can be derived from drawing a line from the origin up to that point.

this ‘direction’ is essentially our vector. You can see it only takes 2 values, an x and a y to contrive this direction of ours which is what our vector class is, just those two x and y properties wrapped within a vector instance object.

A vector isnt a direction alone though, its also a force or power in that direction. This is a reflection of the vectors length. The vector above has a greater force then say this vector.

which is, as you can probably tell by its points, 1/2 the force of the previous. Vectors like this allow an easy way to control movement of things on the screen because all you would have to do is adjust a vectors direction and force and simply add the vectors x to the objects _x and the vectors y to the objects _y and presto.

Which brings us to the point of rotating a vector… which in turn brings us to Trig. Now I should tell you (and I guess in saying this I am) to do a search and look up some decent trig sites and learn all about the trig functions and what they are and what they do. Thats all good and fine but thats confusing and is hard to understand. If you dont do that, its ok, but it doesnt mean Im going to sit here and explain it in a way thats going to make it completely easy to understand, because I dont know if I can do that :slight_smile: but Ill do my best.

Anyway, visually you think, no problem I can rotate a vector, easy-peasy…

which conceptually, sure, you can picture with little difficulty. And probably codewise you might be thinking (_rotation += 10) but finding those new x,y points which the vector will now possess is another thing all together. Thats where the rotate function (and trig) comes into play.

So Trig, what is it and what does it do? Trig (short for Trigonometry) is the branch of mathematics that deals with the relationships between the sides and the angles of triangles and the calculations based on them. Triangles? What does this have to do with rotation of vectors? Well when a vector is made, so is a triangle. Using trigonometry we can obtain just about any information we want about that vector if we think of it as a triangle:

Now using trig functions we can gather information about and even manipulate that triangle into whatever we want (more or less ;)). The functions which do that are layed out roughly in the following:


[Function]	[In Flash]	[sides (from angle)]	[x, y, radius]	[From other Functions]
sine		Math.sin(angle)	opposite/hypotenuse	y/radius	- 
cosine		Math.cos(angle)	adjacent/hypotenuse	x/radius	- 
tangent		Math.tan(angle)	opposite/adjacent	y/x		sine/cosine   
cosecant	-		hypotenuse/opposite	radius/y	1/sine
secant		-		hypotenuse/adjacent	radius/x	1/cosine
cotangen	-		adjacent/opposite	x/y		1/tangent

[Backwards]
arc sine	Math.asin(value)	Math.asin(Math.sin(angle)) = angle
arc cosine	Math.acos(value)	Math.acos(Math.cos(angle)) = angle
arc tangent	Math.atan(value)	Math.atan(Math.tan(angle)) = angle

[Special]
arc tangent2		Math.atan2(y,x) - generates an angle from an x and y
pythagorean theorem	a² + b² = c² (or opposite² + adjacent² = hypotenuse²)
^finds 1 of the sides given any of the lengths of the other 2 (ie can find the hypotenuse
(force) given the opposite (y) & adjacent (x))

Though the only ones you’ll probably ever have to worry about are sine, cosine and atan2… and the pythagorean theorem of course heh. And what are they? Well sine and cosine are special functions which when given a theta angle of any right triangle (a right triangle being a triangle with one angle of 90 degrees like the one created above, theta (a greek letter that looks like a 0 representing an angle) not being that 90 degree angle), can find the base and height of that triangle. Looking at our vector created triangle we get the following

You can see that the theta angle is created from the relation of the vector to the origin. At that angle there are two lines making up 2 sides of the triangle, the base (x) or ‘adjacent’ line and the ‘hypotenuse’ line which is the line always opposite of the right angle. This hypotenuse also serves as a radius for the circle created in the previous ‘conceptual’ vector rotation. The line ‘opposite’ of theta is the height (y). The lengths of lines ‘adjacent’ and ‘opposite’ of the theta angle make up or vector cooridinates, in this case (6,5). So, given any angle, using sine and cosine we can find our adjacent and opposite lengths. Well, not exactly, but more or less - Ill show you what I mean, but first, the angle…

The angle here is not measured in degrees which you are used to, but another unit of measurement called ‘Radians’. In terms of radians, the complete rotation of a circle (360 degrees) is made up of 2Pi radians. Pi in Flash is Math.PI, so for all the angles in a circle in Flash in radians you would use 2*Math.PI; Why use radians? Just because. LIVE WITH IT (lost wont like me saying that ;)) The thing is, its the unit of measurement used in these trigonometric functions so you will have to understand what they are. Heres a chart outlining some degree to radian comparisons:


[Degrees]	[Radians]
15		1/12 Pi 
30		1/6 Pi 
45		1/4 Pi 
90		1/2 Pi 
135		3/4 Pi 
180		Pi 
270		3/2 Pi 
360		2 Pi

If you want, you can make Math functions to make the conversion for you:


Math.toRadians = function(degrees){
	return degrees*Math.PI/180;
}
Math.toDegrees = function(radians){
	return radians*180/Math.PI;
}

Since, if you think about it, if 180 degrees is Math.PI radians, just taking that as a fraction will convert either degrees or radians into the other based on the order of that fraction. Make sense? If not dont worry, its there and you can use it :):

trace(Math.toDegrees(Math.PI)); // traces 180

The original Vector.prototype.rotate used a sinD and cosD I initially said are probably just sine and cosine in degrees. If so, heres how they would look:


Math.sinD = function(angle){
	return Math.sin(angle*Math.PI/180);
}
Math.cosD = function(angle){
	return Math.cos(angle*Math.PI/180);
}

Doing basically the same thing as toRadians, but doing it within the sine and cosine call itself (converting the passed degrees value into a radians value to be used in the sin or cos function)

continuing, now that thats all cleared up…

So, what sine and cosine really do is give a proportion of the two lines in respect to 1. This base value of 1 is part of the imaginary ‘unit circle’ which the functions sine and cosine are based around. This unit circle assumes a vector with a force (or length or radius) of 1. The reason for this is because an angle value alone cant tell you how long the vector is, just where its pointing. So sine and cosine just assume 1 and return a value based on that. Ok, so what value? Well looking at that chart, sine returns opposite/hypotenuse or y/radius and cosine returns adjacent/hypotenuse or x/radius. So you’re probably thinking, “Hmm well thats just some bloody fraction, whats that going to do for me?” Well if you remember what I just said about how sine and cosine think interms of a radius of 1, you’ll see that y/radius and x/radius are just y/1 and x/1 which are just y and x! And what are y and x? They’re the base (adjacent) and height (opposite) of the triangle which is effectively the x and y of the vector! … at least a vector with a force of 1. See this swf I made a while back to see what I mean (and to see the graphs of sine and cosine).

http://www.umbc.edu/interactive/fla/trigsincos.swf

Now, what arc tangent2 can do for us is go the other way around, it can take a y and x value and return an angle. An angle in radians of course. Yippie, we’ll leave it at that.

The Pythagorean theorem is probably one of the best known functions of those used in trigonometric calculations. What it does (as my little chart explained) is finds any right triangles side assuming 2 are already known. Now the function itself doesnt look that way, but solving for any a, b or c will do just that. Chances are if you are using this function, you are finding the hypotenuse of a triangle. This is because we all ready saw that sine and cosine can be used to find the x and y sides of a triangle given its angle, so if we have the angle (which we usually do) we find those 2 sides and then can find the hypotenuse using the good olde Pythagorean theorem. Typically this is translated into the ‘distance function’ which finds the distance between any two points, since as you can see, the hypotenuse is the distance between our two important points, the origin (0,0) and the vector point(x,y). So solving for the hypotenuse we develop the equation:

hypotenuse = √ (opposite² + adjacent²)
or in Flash terms
hypotenuse = Math.sqrt(oppositeopposite + adjacentadjacent);

Wow! So using those 4 things, sine, cosine, arc tangent2 and the Pythagorean theorem we can find every side and even the angle of any given right triangle or in Vector terms, its angle, x, y and force! Now that brings us to rotating… well not without one more diversion. Changing the force of a vector…

Changing the force of a vector (if you havent already figured it out) is only a matter of multiplying that vector my a scalar, which itself is just a number representing a force. For example, you saw how the very first vector, was twice as strong as the previous vector. Taking note of the x and y of both of those vectors you can see that the first vectors x and y are twice as large as the seconds. So to cut a vectors force in half, youd multiply it by the scalar .5 (which is 1/2). Multiplying a vector by a scalar is simply multiplying both its x and y by that number. So the vector (6,5) when multiplied by the scalar .5 becomes (3,2.5) which is what the second vector is. A function for that is:


Vector.prototype.multiply = function(scalar) {
	this.x *= scalar;
	this.y *= scalar;
};

So given a sine (y) or cosine (x) based value (remember, thats always based on 1), if you multiply that value a vectors force, you get that vectors actual y or x value.

So (FINALLY) to the function which started this question in the first place:


Vector.prototype.rotate = function(angle) {
	var ca = Math.cosD(angle);
	var sa = Math.sinD(angle);
	with (this) {
		var rx = x*ca-y*sa;
		var ry = x*sa+y*ca;
		x = rx;
		y = ry;
	}
};

This follows the formula

x’ = x * cosine(theta) + y * sine(theta)
y’ = x * sine(theta) - y * cosine(theta)

where theta is an angle of rotation and x’ and y’ are ‘x prime’ and ‘y prime’ which are the ‘new’ x and y derived by the rotation. Since it doesnt quite seem to relate directly to what Ive covered through all the previous explanation, I think the best way to actually see this in action is from a base on-axis example using or cartesian plane.

The reason for this is because with the our initial on-axis (on the x axis) vector (x,y), our x is equal to the vector force and y is 0, making it easy to work with. So right off, lets plug that in to that rotation equation:

x’ = force * cosine(theta) + 0 * sine(theta)
y’ = force * sine(theta) - 0 * cosine(theta)

which resolves to

x’ = force * cosine(theta)
y’ = force * sine(theta)

which is exactly the definition of of sine and cosine! Now the example end vector (x’,y’) in the diagram above is actually the first vector I showed, (6,5). So lets work backwards from that and that initial on-axis vector that this example is supposed to start from. First though, we need to find the rotation of our vector though, and that is done through what? Thats right, atan2 :slight_smile:

angle = Math.atan2(y,x)
angle = Math.atan2(5,6)
angle = 0.6947 radians
angle = about 40 degrees

…whats that? What does atan2 do? I was hoping you wouldnt ask :crazy: Basically, all atan2 is, is atan (arc tangent - look back at my beautiful chart :)) but using not using a tangent derived value to get the angle, instead using just the y and x. Tangent itself is after all just y/x; what atan2 does is prevents you from having to take your y divide it by x to before throwing it in (which you would do for atan). This is good not only in that it saves you time (possibly heh), but it also automatically saves you from instances where x might be 0. And why is that bad? Because you CANT DIVIDE BY ZERO!!! (My highschool math teacher would be proud!) Back to our equation though…

x’ = x * cosine(theta) + y * sine(theta)
y’ = x * sine(theta) - y * cosine(theta)

x’ = 6 * cosine(0.6947) + 5 * sine(0.6947) // remember we’re using radians
y’ = 6 * sine(0.6947) - 5 * cosine(0.6947)

x’ = 4.6093 + 3.2009
y’ = 3.8411 - 3.8411

x’ = 7.8102
y’ = 0

which would give us the axis vector of (7.8,0). Right away you can tell the 0 is right since there should be no y value if the vector is on the x axis. But what about x? How would we know if thats right? Why the Pythagorean theorem, of course! Since the vector is on the x axis, x is actually representative of the force of the vector as well. So using the Pythagorean theorem on the original vector (6,5) solving for hypotenuse (force) will give us… 7.8 - we hope… lets see:

hypotenuse = √ (opposite² + adjacent²)
hypotenuse = √ (5² + 6²)
hypotenuse = √ (25 + 36)
hypotenuse = √ 61
hypotenuse = 7.8102

And there you have it!
So basically what the

x’ = x * cosine(theta) + y * sine(theta)
y’ = x * sine(theta) - y * cosine(theta)

formula more or less does (or at least a way you can think about it) is supposes the vector to be rotated to be as if it were on an axis. Then as seen above, calculates the new vector treating that ‘on axis’ vector as a base vector from which a new vector is just added on to.

Chances are this doesnt make sense right away if you are only now getting into it. But within time and some research on your own, it should come together more and more bit by bit until you have it down.


wheeeew I completely didnt mean to spend this much time on the explanation :beam:

OK WHOA… I forgot something. Remember how I mentioned that the y axis in Flash is flipped (since a y of 0 starts at the top and increases going down)? Ok good, I knew you would. However, DONT FEAR! This will actually cause NO problems for you whatso ever. The reason? Because rotation in flash is also backwards! Look at the unit circle rotation values in the swf link. The angle starts at 0 on the right and increases moving counter-clockwise. Rotation in flash increases moving clockwise. That being the case, it basically cancels out any complications that would have been had from the flipping of the y axis. Ok, just had to clear that up :slight_smile:

Sen, that much work deserves a rating. :slight_smile:

This thread belongs in Best of Kirupa fo sho. Simply brilliant.

-Al

thanks :slight_smile:

… Ive had to go back and fix a few things (and there’s probably still some typos now) but I think it does a pretty good job
:beam:

what the … :cyclops:

[size=1]::takes the hat off::[/size]

Looks very good indeed. I put it in the BOK, and this will sooner or later become a tute (if you don’t mind, Sen).

pom :cowboy:

Wow, this definitely deserves BOK.

And as for the radians and me not liking it Senocular… you jumped to your own conclusion there :wink: I do agree with using the Radians :slight_smile:

Its the “LIVE WITH IT” part I was refering to :slight_smile:

Oh, well no, I still don’t disagree with that :wink:

You have to use radians don’t you? (not too big on the trig) And if that is the case… they do have to just live with it :slight_smile:

And if the case is that you don’t have to, well then, your tutorial goes over it <B>with</B> radians, so they have to live with it anyway :wink:

Wow, thanks a lot, i haven’t had a chance to read it yet though. Thanks again.

wow, i read that thru and it makes so much sense. i’m sure i’ll use your explanation (tutorial?) sometime in the near future. the only math i was ever good at was trigonometry. definately very cool.:smirk:

Hey sen,
That was one awesome response! That would be an awesome tutorial if you are interested.

Cheers!
Kirupa :bad: