I'm working on some coordinates function to my canvas in HTML5, and I want to make a function which can move an object by degrees.
My dream is to make a function which works like this:
box.x=10;
box.y=10;
// now the box has the coordinates (10,10)
moveTheBoxWithThisAmountOfDistance=10;
degreesToMoveTheBox=90;
box.moveByDegrees(moveTheBoxWithThisAmountOfDistance,degreesToMoveTheBox);
// now the box.x would be 20 and box.y wouldn't be changed because
// we only move it to the right (directional 90 degrees)
I hope this makes any sense!
So my question is:
How does the mathematical expression look like when I have to turn a degree into to coordinates?
You use sin and cos to convert an angle and a distance into coordinates:
function moveByDegrees(distance, angle) {
var rad = angle * Math.pi / 180;
this.x += Math.cos(rad) * distance;
this.y += Math.sin(rad) * distance;
}
That's it: http://en.wikipedia.org/wiki/Rotation_matrix , all the math is described :)
Also beware that if you need multiple sequential rotations (i.e. you do continuous animation), it's better to recompute x' and y' from initial ones and not just previous. Not doing so will result in rounding errors accumulation, and after some thousand rotations the result will become too rough.
Related
I am trying to move an object smoothly from point A to point B using HTML canvas and regular javascript.
Point A is a set of coordinates
Point B is in the case the cursor location.
I made a jsfiddle of what I have so far: https://jsfiddle.net/as9fhmw8/
while(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
ctx.save();
ctx.beginPath();
ctx.translate(projectile.x, projectile.y);
ctx.arc(0,0,5,0,2*Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.stroke();
ctx.restore();
if(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
var stepsize = (projectile.mouseX - projectile.x) / (projectile.y - projectile.mouseY);
projectile.x += (stepsize + 1);
}
if(projectile.mouseY < projectile.y)
{
var stepsize = (projectile.y - projectile.mouseY) / (projectile.mouseX - projectile.x);
projectile.y -= (stepsize + 1);
}
}
Essentially what I can't figure out to do is to make the while loop slower (so that it appears animated in stead of just going through every iteration and showing the result).
I also can't figure out how to prevent the Arc from duplicating so that it creates a line that is permanent, instead of appearing to move from point a to point b.
Smooth animation here is really about determining how far to move your object for each iteration of the loop.
There is a little math involved here, but it's not too bad.
Velocity
Velocity in your case is just the speed at which your particles travel in any given direction over a period of time. If you want your particle to travel 200px over the course of 4 seconds, then the velocity would be 50px / second.
With this information, you can easily determine how many pixels to move (animate) a particle given some arbitrary length of time.
pixels = pixelsPerSecond * seconds
This is great to know how many pixels to move, but doesn't translate into individual X and Y coordinates. That's where vectors come in.
Vectors
A vector in mathematics is a measurement of both direction and magnitude. For our purposes, it's like combining our velocity with an angle (47°).
One of the great properties of vectors is it can be broken down into it's individual X and Y components (for 2-Dimensional space).
So if we wanted to move our particle at 50px / second at a 47° angle, we could calculate a vector for that like so:
function Vector(magnitude, angle){
var angleRadians = (angle * Math.PI) / 180;
this.magnitudeX = magnitude * Math.cos(angleRadians);
this.magnitudeY = magnitude * Math.sin(angleRadians);
}
var moveVector = new Vector(50, 47);
The wonderful thing about this is that these values can simply be added to any set of X and Y coordinates to move them based on your velocity calculation.
Mouse Move Vector
Modeling your objects in this way has the added benefit of making things nice and mathematically consistent. The distance between your particle and the mouse is just another vector.
We can back calculate both the distance and angle using a little bit more math. Remember that guy Pythagoras? Turns out he was pretty smart.
function distanceAndAngleBetweenTwoPoints(x1, y1, x2, y2){
var x = x2 - x1,
y = y2 - y1;
return {
// x^2 + y^2 = r^2
distance: Math.sqrt(x * x + y * y),
// convert from radians to degrees
angle: Math.atan2(y, x) * 180 / Math.PI
}
}
var mouseCoords = getMouseCoords();
var data = distanceAndAngleBetweenTwoPoints(particle.x, particle.y, mouse.x, mouse.y);
//Spread movement out over three seconds
var velocity = data.distance / 3;
var toMouseVector = new Vector(velocity, data.angle);
Smoothly Animating
Animating your stuff around the screen in a way that isn't jerky means doing the following:
Run your animation loop as fast as possible
Determine how much time has passed since last time
Move each item based on elapsed time.
Re-paint the screen
For the animation loop, I would use the requestAnimationFrame API instead of setInterval as it will have better overall performance.
Clearing The Screen
Also when you re-paint the screen, just draw a big rectangle over the entire thing in whatever background color you want before re-drawing your items.
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
Putting It All Together
Here is a Fiddle demonstrating all these techniques: https://jsfiddle.net/jwcarroll/2r69j1ok/3/
Here's an image to demonstrate the question:
Let's say I have Point A at [0,0], and Point B at [50, 30]. I want to find the coordinates of Point X, along a circle of radius 15, with an origin at Point A, which is also on a line between Point A and Point B.
Pointers on the best method to do this?
Since this has been tagged JavaScript, here's a simple implementation:
// disclaimer: code written in browser
function Point2D(x, y) {
this.x = x;
this.y = y;
}
function findCircleInteresction(center, radius, target) {
var vector = new Point2D(target.x - center.x, target.y - target.y);
var length = Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2));
var normal = new Point2D(vector.x / length, vector.y / length);
var result = new Point2D(center.x + (normal.x * radius), center.y + (normal.y * radius));
return result;
}
findCircleInteresction(new Point2D(0, 0), 15, new Point2D(50, 30));
Point2D is just a class to make objects with x and y properties.
findCircleInteresction takes three parameters:
- center the center of the circle
- radius the radius of the circle
- target a point outside the circle
In findCircleInteresction:
- calculate the vector between the center and the target
- get the length of the resulting vector
- compute the normal (normalized) of the vector
- find the point where the vector intersects with the circle by adding the center of the circle plus the normalized vector components multiplied by the radius of the circle
This code could be heavily optimized and it's untested but I think it illustrated the idea.
You would want to think of this as two overlapping triangles, one with sides Bx-Ax and By-Ay. What you want is to find the coordinates of X, which would specifically be a triangle with sides Xx-Ax and Xy-Ay but with known hypotenuse R, which is your radius of the circle. Notice that the angle for both triangles are equal in respect to the x-coordinates-axis.
So to get the angle of the triangle, take the arctan(By-Ay/Bx-Ax) Now with that angle, call it T, you can solve for the smaller legs with your know radius R.
To get the x coordinate you would take Rcos(T)
To get the y coordinate you would take Rsin(T)
Bringing it all together you have that Xx = Rcos(T) and Xy = Rsin(T)
If you are not willing to use a Math library, which this method would use, you can use ratio's (as Pointy commented)
In order to better understand how trigonometry works in game development, I've been creating little javascript snippets on CodePen.
I managed to create an example that uses Math.atan2() to point a pixel-art shotgun at the mouse cursor.
Now, I am trying to accomplish the same exact thing using the Math.atan() function but it isn't functioning properly.
Here is the logic code I am using:
canvas.onmousemove = function(event) {
Mouse = {
x: event.pageX,
y: event.pageY
}
// These length variables use the distance formula
var opposite_length = Math.sqrt((Mouse.x - Mouse.x) * (Mouse.x - Mouse.x) + (Mouse.y - y) * (Mouse.y - y));
var adj_length = Math.sqrt((Mouse.x - x) * (Mouse.x - x) + (y - y) * (y - y));
var angle_in_radians = Math.atan(opposite_length / adj_length);
//var angle_in_radians = Math.atan2(Mouse.y - y, Mouse.x - x);
angle = angle_in_radians * (180 / Math.PI);
}
The in my draw() function, I rotate the gun to the angle var using:
cxt.rotate(angle*(Math.PI/180));
If you uncomment the line that starts as // var angle_in_radians, everything will suddenly work.
So, atan2 is working, but atan is producing the result I want.
I know that opposite_length and adj_length are accurate, because when i console.log() them, they are the correct values.
You can check out the code being used on CodePen for a live example.
There's a lot of initialization stuff but you only really need to focus on the canvas.onmousemove = function(event) section, starting on line 50. You can also check out my draw function on line 68.
Note that your atan computation is equivalent to
atan2( abs(mouse.y-y), abs(mouse.x-x) )
The screen coordinates have the opposite orientation to the cartesian coordinates. To get a cartesian angle from screen coordinates, use
atan2( y-mouse.y, mouse.x-x )
I need to know angle of rotation in polar coordinates using X and Y from cartesian coordinates.
How to do it in JS without a lot of IF statements? I know that I can do it using ,
but I think that it will be bad for performance, because it is in animation cycle.
Javascript comes with a built in function that does what is represented in the image: Math.atan2()
Math.atan2() takes y, x as arguments and returns the angle in radians.
For example:
x = 3
y = 4
Math.atan2(y, x) //Notice that y is first!
//returns 0.92729521... radians, which is 53.1301... degrees
I wrote this function to convert from Cartesian Coordinates to Polar Coordinates, returning the distance and the angle (in radians):
function cartesian2Polar(x, y){
distance = Math.sqrt(x*x + y*y)
radians = Math.atan2(y,x) //This takes y first
polarCoor = { distance:distance, radians:radians }
return polarCoor
}
You can use it like this to get the angle in radians:
cartesian2Polar(5,5).radians
Lastly, if you need degrees, you can convert radians to degrees like this
degrees = radians * (180/Math.PI)
I'm kinda confused with this one.
I have an object and I know it's velocities on axis x and y. My problem is how to determine the angle at which it's moving.
function Object(){
this.velocity = {x: 5, y: 1};
}
Basically I Know that a vector's direction is x_projectioncos(deg) + y_projectionsin(deg), but I don't know how to get those projections since I only have the velocity, as I said I'm really confused.
#EDIT:
in addition to the accepted answer, here's what I did to get a full 360 degree spectrum
var addDeg = 0;
if(obj.velocity.x<0)
addDeg = obj.velocity.y>=0 ? 180 : 270;
else if(obj.velocity.y<=0) addDeg = 360;
deg = Math.abs(Math.abs(Math.atan(obj.velocity.y/obj.velocity.x)*180/Math.PI)-addDeg)
I don't know how to get those projections since I only have the
velocity
Actually, what you seem to be missing is that you already have the projections. That's what x and y are.
x is speed * cos(angle)
y is speed * sin(angle)
So y/x = sin(angle)/cos(angle) which is tan(angle) so angle=arctan(y/x).
That's the angle rotating anti-clockwise starting from the x axis (with x pointing right and y pointing up).
Find the angle between that vector and (1,0) (Right horizontal positive direction).
The math is:
A = (5,1)
B = (1,0)
A.B = |A||B|cos(angle) -> angle = arccos((|A||B|)/(A.B))
Dot product, check geometric definition
Edit:
Another option is to use the cross product formula:
|AxB| = |A||B|sin(angle) -> angle = arcsin((|A||B|)/(|AxB|))
It will give you the angle you need.
There is an easier way to get full 360 degrees. What you're looking for, is Math.atan2:
deg = Math.atan2(obj.velocity.y,obj.velocity.x)*180/Math.PI;