Vector deltas and moving in unknown areas - math

I was in need of a little math help that I can't seem to find the answer to, any links to documentation would be greatly appreciated.
Heres my situation, I have no idea where I am in this maze, but I need to move around and find my way back to the start. I was thinking of implementing a waypoint list of places i've been offset from my start at 0,0. This is a 2D cartesian plane.
I've been given 2 properties, my translation speed from 0-1 and my rotation speed from -1 to 1. -1 is very left and +1 is very right. These are speed and not angles so thats where my problem lies. If I'm given 0 as a translation speed and 0.2 I will continually turn to my right at a slow speed.
How do I figure out the offsets given these 2 variables? I can store it every time I take a 'step'.
I just need to figure out the offsets in x and y terms given the translations and rotation speeds. And the rotation to get to those points.
Any help is appreciated.

Your question is unclear on a couple of points, so I have to make some assumptions:
During each time interval, translation speed and rotational velocity are constant.
You know the values of these variables in every time interval (and you know rotational velocity in usable units, like radians per second, not just "very left").
You know initial heading.
You can maintain enough precision that roundoff error is not a problem.
Given that, there is an exact solution. First the easy part:
delta_angle = omega * delta_t
Where omega is the angular velocity. The distance traveled (maybe along a curve) is
dist = speed * delta_t
and the radius of the curve is
radius = dist / delta_angle
(This gets huge when angular velocity is near zero-- we'll deal with that in a moment.) If angle (at the beginning of the interval) is zero, defined as pointing in the +x direction, then the translation in the interval is easy, and we'll call it deta_x_0 and delta_y_0:
delta_x_0 = radius * sin(delta_angle)
delta_y_0 = radius * (1 - cos(delta_angle))
Since we want to be able to deal with very small delta_angle and very large radius, we'll expand sin and cos, and use this only when angular velocity is close to zero:
dx0 = r * sin(da) = (dist/da) * [ da - da^3/3! + da^5/5! - ...]
= dist * [ 1 - da^2/3! + da^4/5! - ...]
dy0 = r * (1-cos(da)) = (dist/da) * [ da^2/2! - da^4/4! + da^6/6! - ...]
= dist * [ da/2! - da^3/4! + da^5/6! - ...]
But angle generally isn't equal to zero, so we have to rotate these displacements:
dx = cos(angle) * dx0 - sin(angle) * dy0
dy = sin(angle) * dx0 - cos(angle) * dy0

You could do it in two stages. First work out the change of direction to get a new direction vector and then secondly work out the new position using this new direction. Something like
angle = angle + omega * delta_t;
const double d_x = cos( angle );
const double d_y = sin( angle );
x = x + d_x * delta_t * v;
y = y + d_y * delta_t * v;
where you store your current angle out at each step. ( d_x, d_y ) is the current direction vector and omega is the rotation speed that you have. delta_t is obviously your timestep and v is your speed.
This may be too naive to split it up into two distinct stages. I'm not sure I haven't really thought it through too much and haven't tested it but if it works let me know!

Related

How to calculate launch angle and launch heading to shoot into a target while moving?

I am trying to find the launch angle and launch heading to hit a specific point at the end of the trajectory. However the challenge is that the shooter is translating along the field. Therefore there is a momentum that influences the trajectory and curves it into the 3rd dimension. Again, I need to calculate the launch angle compared to the horizon and the launch heading to counter the momentum.
Although it is not a programing question let me explain it to you. To work out a projectile motion in 2D, we usually break the velocity vector into two components vx and vz and launch angle θxz. To work it out in 3D you'll just need to do the same things in 3D i.e. you'll need to resolve the velocity vector into three components vx, vy and vz along with launch angles in two direction θxz and θyz and also an angle θxy. After that, you can apply your normal equations of motion to get a proper solution. Now, the problem remains to get the velocity. That can be done by considering a frame of reference and relative velocity vectors. Do these two things and you'll get your answer in my opinion.
I am assuming that the shooter moves according to some trajectory equations
x(t) = [x[0](t), x[1](t), 0]
in the horizontal plane (that is why x[3](t) = 0 because the trajectory is always on the plane x[1], x[2]).
At time t_0 the shooter is at the point x_0 = x(t_0) and has velocity
v_0 = v(t_0) = dx/dt(t_0) = [v_0[0], v_0[1], 0]
I assume you know the magnitude of the velocity w > 0 with which the bullet is shot at time t_0 and you are looking for a directing unit vector
u = [u[0], u[1], u[2]]
1 = u[0]^2 + u[1]^2 + u[2]^2
that will guarantee that the shot will hit the stationary (I hope) target located at point
x_1 = [x_1[0], x_1[1], x_1[2]]
So the shot is fired at time t_0 from point x_0, the latter moving with speed v_0 and the magnitude of the fired bullet is w. Therefore the initial velocity of the bullet with respect to a coordinate system attached to the ground is v_0 + w*u, where u is the unknown unit vector. The vectorized differential equations of motion are
dx/dt = v
dv/dt = - g * e_3
where e_3 = [0, 0, 1] and g = 9.8. The solution of these equations, that starts from x_0 with velocity v_0 + w*u at time t_0 follows the trajectory described by the vectorized equations
x(t) = x_0 + (t - t_0) * (v_0 + w*u) - g*(t - t_0)^2 * e_3
So, there is a yet unknown moment of time t_1 at which the bullet hits the target x_1, i.e.
x_1 = x(t_1) which yields
x_1 = x_0 + (t_1 - t_0) * (v_0 + w*u) - g*(t_1 - t_0)^2 * e_3
Denote by
x_10 = x_1 - x_0 (known) i.e.
for (int i=0; i<3; i++){x_12[i] = x_1[i] - x_0[i]}
t_10 = t_1 - t_0 (unknown)
Then you get
x_10 = t_10 * (v_0 + w*u) - g * (t_10)^2 * e_3
If you rewrite the vectorized equation of the bullet hitting the target componentwise, you get the following system of four quadratic equations for the unknown variables t_10, u[0], u[1], u[2]:
x_10[0] = t_10 * ( v_0[0] + w*u[0] )
x_10[1] = t_10 * ( v_0[1] + w*u[1] )
x_10[2] = t_10 * w * u[1] - (t_10)^2 * g / 2
1 = u[0]^2 + u[1]^2 + u[2]^2
If you express the variables u[0], u[1], u[2] in terms of t_12 from the first three equations, you get
u[0] = x_10[0]/(w*t_10) - v[0]/w
u[1] = x_10[1]/(w*t_10) - v[1]/w
u[2] = ( x_10[2] + (t_10)^2 * g / 2 )/(w*t_10)
1 = u[0]^2 + u[1]^2 + u[2]^2
then you can plug the expressions for u[0], u[1], u[2] in terms of t_12 into the last equation and you get, after multiplying both sides by (t_10)^2, a degree four polynomial equation in the variable t_12. After you solve it, you will find one positive root and you can plug it back in the first three equations to calculate the coordinates of the unit vector u.

Turn rate on a player's rotation doing a 360 once it hits pi

Making a game using Golang since it seems to work quite well for games. I made the player face the mouse always, but wanted a turn rate to make certain characters turn slower than others. Here is how it calculates the turn circle:
func (p *player) handleTurn(win pixelgl.Window, dt float64) {
mouseRad := math.Atan2(p.pos.Y-win.MousePosition().Y, win.MousePosition().X-p.pos.X) // the angle the player needs to turn to face the mouse
if mouseRad > p.rotateRad-(p.turnSpeed*dt) {
p.rotateRad += p.turnSpeed * dt
} else if mouseRad < p.rotateRad+(p.turnSpeed*dt) {
p.rotateRad -= p.turnSpeed * dt
}
}
The mouseRad being the radians for the turn to face the mouse, and I'm just adding the turn rate [in this case, 2].
What's happening is when the mouse reaches the left side and crosses the center y axis, the radian angle goes from -pi to pi or vice-versa. This causes the player to do a full 360.
What is a proper way to fix this? I've tried making the angle an absolute value and it only made it occur at pi and 0 [left and right side of the square at the center y axis].
I have attached a gif of the problem to give better visualization.
Basic summarization:
Player slowly rotates to follow mouse, but when the angle reaches pi, it changes polarity which causes the player to do a 360 [counts all the back to the opposite polarity angle].
Edit:
dt is delta time, just for proper frame-decoupled changes in movement obviously
p.rotateRad starts at 0 and is a float64.
Github repo temporarily: here
You need this library to build it! [go get it]
Note beforehand: I downloaded your example repo and applied my change on it, and it worked flawlessly. Here's a recording of it:
(for reference, GIF recorded with byzanz)
An easy and simple solution would be to not compare the angles (mouseRad and the changed p.rotateRad), but rather calculate and "normalize" the difference so it's in the range of -Pi..Pi. And then you can decide which way to turn based on the sign of the difference (negative or positive).
"Normalizing" an angle can be achieved by adding / subtracting 2*Pi until it falls in the -Pi..Pi range. Adding / subtracting 2*Pi won't change the angle, as 2*Pi is exactly a full circle.
This is a simple normalizer function:
func normalize(x float64) float64 {
for ; x < -math.Pi; x += 2 * math.Pi {
}
for ; x > math.Pi; x -= 2 * math.Pi {
}
return x
}
And use it in your handleTurn() like this:
func (p *player) handleTurn(win pixelglWindow, dt float64) {
// the angle the player needs to turn to face the mouse:
mouseRad := math.Atan2(p.pos.Y-win.MousePosition().Y,
win.MousePosition().X-p.pos.X)
if normalize(mouseRad-p.rotateRad-(p.turnSpeed*dt)) > 0 {
p.rotateRad += p.turnSpeed * dt
} else if normalize(mouseRad-p.rotateRad+(p.turnSpeed*dt)) < 0 {
p.rotateRad -= p.turnSpeed * dt
}
}
You can play with it in this working Go Playground demo.
Note that if you store your angles normalized (being in the range -Pi..Pi), the loops in the normalize() function will have at most 1 iteration, so that's gonna be really fast. Obviously you don't want to store angles like 100*Pi + 0.1 as that is identical to 0.1. normalize() would produce correct result with both of these input angles, while the loops in case of the former would have 50 iterations, in the case of the latter would have 0 iterations.
Also note that normalize() could be optimized for "big" angles by using floating operations analogue to integer division and remainder, but if you stick to normalized or "small" angles, this version is actually faster.
Preface: this answer assumes some knowledge of linear algebra, trigonometry, and rotations/transformations.
Your problem stems from the usage of rotation angles. Due to the discontinuous nature of the inverse trigonometric functions, it is quite difficult (if not outright impossible) to eliminate "jumps" in the value of the functions for relatively close inputs. Specifically, when x < 0, atan2(+0, x) = +pi (where +0 is a positive number very close to zero), but atan2(-0, x) = -pi. This is exactly why you experience the difference of 2 * pi which causes your problem.
Because of this, it is often better to work directly with vectors, rotation matrices and/or quaternions. They use angles as arguments to trigonometric functions, which are continuous and eliminate any discontinuities. In our case, spherical linear interpolation (slerp) should do the trick.
Since your code measures the angle formed by the relative position of the mouse to the absolute rotation of the object, our goal boils down to rotating the object such that the local axis (1, 0) (= (cos rotateRad, sin rotateRad) in world space) points towards the mouse. In effect, we have to rotate the object such that (cos p.rotateRad, sin p.rotateRad) equals (win.MousePosition().Y - p.pos.Y, win.MousePosition().X - p.pos.X).normalized.
How does slerp come into play here? Considering the above statement, we simply have to slerp geometrically from (cos p.rotateRad, sin p.rotateRad) (represented by current) to (win.MousePosition().Y - p.pos.Y, win.MousePosition().X - p.pos.X).normalized (represented by target) by an appropriate parameter which will be determined by the rotation speed.
Now that we have laid out the groundwork, we can move on to actually calculating the new rotation. According to the slerp formula,
slerp(p0, p1; t) = p0 * sin(A * (1-t)) / sin A + p1 * sin (A * t) / sin A
Where A is the angle between unit vectors p0 and p1, or cos A = dot(p0, p1).
In our case, p0 == current and p1 == target. The only thing that remains is the calculation of the parameter t, which can also be considered as the fraction of the angle to slerp through. Since we know that we are going to rotate by an angle p.turnSpeed * dt at every time step, t = p.turnSpeed * dt / A. After substituting the value of t, our slerp formula becomes
p0 * sin(A - p.turnSpeed * dt) / sin A + p1 * sin (p.turnSpeed * dt) / sin A
To avoid having to calculate A using acos, we can use the compound angle formula for sin to simplify this further. Note that the result of the slerp operation is stored in result.
result = p0 * (cos(p.turnSpeed * dt) - sin(p.turnSpeed * dt) * cos A / sin A) + p1 * sin(p.turnSpeed * dt) / sin A
We now have everything we need to calculate result. As noted before, cos A = dot(p0, p1). Similarly, sin A = abs(cross(p0, p1)), where cross(a, b) = a.X * b.Y - a.Y * b.X.
Now comes the problem of actually finding the rotation from result. Note that result = (cos newRotation, sin newRotation). There are two possibilities:
Directly calculate rotateRad by p.rotateRad = atan2(result.Y, result.X), or
If you have access to the 2D rotation matrix, simply replace the rotation matrix with the matrix
|result.X -result.Y|
|result.Y result.X|

RigidBody2D projectile to target calculation

I am using this code to shoot a projectile in order to hit a target on the ground.
I am applying an impulse force in the x direction, which is calculated from its height above the target. I am working out the force to add by dividing the x distance by the time it will take to fall due to gravity, but this does not seem to work:
float xDist = flag.position.x - transform.position.x;
float yDist = transform.position.y - flag.position.y;
float smallY = (4/9.81f) + (0.5f*-9.81f*Mathf.Pow((4/9.81f), 2));
yDist = yDist + smallY;
float yImpactForce = Mathf.Sqrt(2*9.81f*yDist);
float xForce = xDist/(Mathf.Sqrt(2*yDist/9.81f));
Can anyone help me?
Not sure if my calculations are wrong, or whether the physics is rather different than a real life situation. Any help is greatly appreciated, thanks
EDITS: The smallY variable is used to find the distance to the peak of the projectile's trajectory
From memory
dist = (a * t2)/2
so solving for time gives:
t = sqrt(2 * dist/a)
Therefore, your initial velocity calculation should be:
float fallTime = Mathf.Sqrt(2*yDist/9.81f);
float xVel = xDist/fallTime;
At least that's in the real world.
Since you are just trying to impact a specific velocity, it would be easier to just use ForceMode.VelocityChange.
To account for the y velocity in the updated version of the question, the additional time will be, assuming a positive (upwards) y-component (from v = a * t or t = v/a, times 2 -- once for decelerating to zero and once for accelerating back to the starting velocity and height, but in the other direction):
float fallTime = Mathf.Sqrt(2*yDist/9.81f);
var yTime = 2 * (yVel / 9.81f);
falltime += yTime;
float xVel = xDist/fallTime;

How to get the "anti-clockwise" angle between two 2D vectors?

I have two vectors and I want to get the angle between those vectors, I am currently doing it with this formula :
acos(dot(v1.unitVector, v2.unitVector))
Here is what I get with it :
I would want the green angle rather than the red angle, but I don't know what formula I should use...
Thank you.
EDIT : So, hen the vectors are still in a certain position (like the first two pairs of vectors, it's ok, but whenever it is in a configuration like in the third pair, it doesn't give me the right angle anymore)
With the dot product you get always an angle that is independent of the order of the vectors and the smaller of the two possibilities.
For what you want, you need the argument function of complex numbers that is realized by the atan2 function. The angle from a=ax+i*ay to b=bx+i*by is the argument of the conjugate of a times b (rotating b backwards by the angle of a, scale not considered), which in coordinates is
(ax-i*ay) * (bx+i*by) = ax*bx+ay*by + i*(ax*by-ay*bx)
so the angle is
atan2( ax*by-ay*bx, ax*bx+ay*by ).
Adding to the accepted answer, the problem with atan2 is that, if you imagine vector a being static and vector b rotating in anti-clockwise direction, you will see the return value ranging from 0 to π, but then it suddenly turns negative and proceeds from -π to 0, which is not exactly good if you're interested in an angle increasing from 0 to 2π.
To tackle that problem, the function below conveniently maps the result from atan2 and returns a value between 0 and 2π as one would expect:
const TAU = Math.PI * 2;
/**
* Gives the angle in radians from vector a to vector b in anticlockwise direction,
* ranging from 0 to 2π.
*
* #param {Vector} a
* #param {Vector} b
* #returns {Number}
*/
static angle(a, b) {
let angle = Math.atan2(a.x * b.y - a.y * b.x, a.x * b.x + a.y * b.y);
if (angle < 0) {
angle += TAU;
}
return angle;
}
It is written in JavaScript, but it's easily portable to other languages.
Lutz already answered this correctly but let me add that I highly recommend basing modern vector math code on Geometric Algebra which raises the abstraction level dramatically.
Using GA, you can simply multiply the two vectors U and V to get a rotor. The rotor internally looks like A + Bxy where A = U dot V = |U|V|cos(angle) and Bxy = U wedge V = |U||V|sin(angle)xy (this is isomorphic to the complex numbers). Then you can return the rotor's signed CCW angle which is atan2( B, A ).
So with operator overloading, you can just type (u * v).Angle. The final calculations end up the same, but the abstraction level you think and work in is much higher.
Maybe this one is more fit:
atan2( ax*by-ay*bx, ax*bx+ay*by ) % (PI*2)
the calculation which could get the full anti-clockwise radian.

Inverse of math.atan2?

What is the inverse of the function
math.atan2
I use this in Lua where I can get the inverse of math.atan by math.tan.
But I am lost here.
EDIT
OK, let me give you more details.
I needed to calculate angle between 2 points (x1,y1) and (x2,y2) and I did,
local dy = y1-y2
local dx = x1-x2
local angle = atan2(dy,dx)* 180 / pi
Now if I have the angle, is it possible to get back dy and dx?
Given only the angle you can only derive a unit vector pointing to (dx, dy). To get the original (dx, dy) you also need to know the length of the vector (dx, dy), which I'll call len. You also have to convert the angle you derived from degrees back to radians and then use the trig equations mentioned elsewhere in this post. That is you have:
local dy = y1-y2
local dx = x1-x2
local angle = atan2(dy,dx) * 180 / pi
local len = sqrt(dx*dx + dy*dy)
Given angle (in degrees) and the vector length, len, you can derive dx and dy by:
local theta = angle * pi / 180
local dx = len * cos(theta)
local dy = len * sin(theta)
Apparently, something like this will help:
x = cos(theta)
y = sin(theta)
Simple Google search threw this up, and the guy who asked the question said it solved it.
You'll probably get the wrong numbers if you use:
local dy = y1-y2
local dx = x1-x2
local angle = atan2(dy,dx) * 180 / pi
If you are using the coordinate system where y gets bigger going down the screen and x gets bigger going to the right then you should use:
local dy = y1 - y2
local dx = x2 - x1
local angle = math.deg(math.atan2(dy, dx))
if (angle < 0) then
angle = 360 + angle
end
The reason you want to use this is because atan2 in lua will give you a number between -180 and 180. It will be correct until you hit 180 then as it should go beyond 180 (i.e. 187) it will invert it to a negative number going down from -180 to 0 as you get closer to 360. To correct for this we check to see if the angle is less than 0 and if it is we add 360 to give us the correct angle.
According this reference:
Returns the arc tangent of y/x (in radians), but uses the signs of
both parameters to find the quadrant of the result. (It also handles
correctly the case of x being zero.)
So I guess you can use math.tan to invert it also.
As atan2 works as tan-1, so the inverse could be tan, taking into consideration conversion between radian and degree

Resources