How to evenly distribute resistance on a 3D Vector? - math

I am making a game in Godot where different places will have different resistance against movement, and I use this code:`
direction.x = move_toward(direction.x, 0, resistance * delta)
direction.z = move_toward(direction.z, 0, resistance * delta)`
So if my velocity is for say Vector3(14.14,0,0), I'm moving along the X axis by 14.14, but the resistance is 3, my speed will be 11.14 then.
But if my velocity is Vector3(10,0,10), I'm moving along the X axis by 10 and Z by 10 and ill be moving at a speed of 14.14, but the resistance will make the vector this: Vector3(7,0,7) and instead of my speed being 11.14 its 9.89. How would I fix this?

It appears you want to keep the same direction but change the length of the vector.
The shorthand for that is limit_length. So, to make the length shorter by the amount you want, you can do this:
var length := vector.length() - resistance * delta
vector = vector.limit_length(length)
Well, except that when the length we input is negative it gives you a vector in the opposite direction, which I suppose you don't want. We can fix that with move_toward:
var length := move_toward(vector.length(), 0.0, resistance * delta)
vector = vector.limit_length(length)
Or, since we know the value will be decreasing (the length of a vector is never negative), we can do this:
var length := max(vector.length() - resistance * delta, 0.0)
vector = vector.limit_length(length)
Note that limit_length was added in Godot 3.5.
In case you need it, this:
vector = vector.limit_length(length)
Is equivalent to this:
var old_length := vector.length()
if old_length > 0.0 and old_length > length:
vector = vector * length / old_length
Put that toghether:
var length := max(vector.length() - resistance * delta, 0.0)
var old_length := vector.length()
if old_length > 0.0 and old_length > length:
vector = vector * length / old_length
We can simplify a bit:
var old_length := vector.length()
if old_length > 0.0:
var length := max(old_length - resistance * delta, 0.0)
vector = vector * length / old_length
I'm not suggesting this is better (I expect it to be worse performance). Instead I want to demystify limit_length and move_toward. Plus this is what you would do before Godot 3.5.

Related

How to draw a circular sector in the Julia language?

I am new to the Julia language and need to draw a circular sector on an image (2-dimensional UInt8 array for gray version or 3-dimensional UInt8 array for an RGB version). Afterwards this image is to be used as a mask to select data in other arrays, so I need the result, not as an image object, but as an array of booleans or integers.
There is the way to draw a circle by means of the ImageDraw package:
draw!(img, Ellipse(CirclePointRadius(350,200,100), fill = tue))
but found no way to provide a start and end angle.
You can use Luxor.jl's pie or sector function:
julia> begin
img = readpng("/path/Images/deepam.png")
Drawing(img.width, img.height, "sector-on-img.png")
placeimage(img)
origin()
sethue("orange")
pie(0, 0, 100, π/2, π, :fill)
sethue("olive")
sector(25, 125, 3π/2, 0, 15, :fill)
finish()
end
true
Result:
(Original png image scaled down, for comparison:
)
I think Julia is a great language, because (among other things) all libraries are implemented in the same language and you have ease acces to their sources.
And in this way, I have been able to modify the ellipse2d.jl script of the ImageDraw library.
The modification consits of adding another definition of the draw! funciton for ellipse objects (multiple dispatch of Julia is also great) that accepts a start and end angle.
I think the best way could be to define new objects, ellipse_sector and circle_sector, which would be the same as the ellipse and circle objects but with two more members: start_angle and end_angle. Then the correspondent drawing functions should be implemented. I would like to write to the ImageDraw package developers in order to make this suggestion or even offer me to make these changes, but I do not know the manage of github.
My solution, instead, does not modify any existing object, just adds a method to the draw! function that accpets two more arguments: startAngle and endAngle.
Here is the code, to be copied to the end of the ellipse2d.jl script:
function draw!(img::AbstractArray{T, 2}, ellipse::Ellipse, startAng::Real, endAng::Real, color::T) where T<:Colorant
# Solution to find out if an angle lies between two given ones, borrowed from:
# https://stackoverflow.com/questions/11406189/determine-if-angle-lies-between-2-other-angles/11412077#11412077
# Make all angles to lie in [0, 2π)
# rem2pi(ϕ, RoundNearest) returns the remainder of the division by 2π in the range [−π,π]
# mod2pi returns the remainder of the division by 2π in the range [0,2π)
Angle1 = mod2pi(startAng)
Angle2 = mod2pi(endAng)
# make the angle from angle1 to angle2 to be <= 180 degrees
rAngle = mod2pi( mod2pi(Angle2 - Angle1) + 2π)
if rAngle >= π
Angle1, Angle2 = Angle2, Angle1 # Swaps the values
end # if
ys = Int[]
xs = Int[]
break_point = 0
if ellipse.fill == false
break_point = ((ellipse.ρy - ellipse.thickness) / ellipse.ρy) ^ 2 + ((ellipse.ρx - ellipse.thickness) / ellipse.ρx) ^ 2
end
for i in ellipse.center.y - ellipse.ρy : ellipse.center.y + ellipse.ρy
for j in ellipse.center.x - ellipse.ρx: ellipse.center.x + ellipse.ρx
y = i - ellipse.center.y
x = j - ellipse.center.x
val = (x / ellipse.ρy) ^ 2 + (y / ellipse.ρx) ^ 2
# atan(y, x) returns the angle in the correct quadrant [−π,π], not like atan(y/x)
# But make it to be in the range [0, 2π)by means of mod2pi()
ang = mod2pi( atan(y, x) )
# Test if the angle lies betwen the startAngle and the endAngle
if (Angle1 <= Angle2)
AngleIsBetween = ang >= Angle1 && ang <= Angle2
else
AngleIsBetween = ang >= Angle1 || ang <= Angle2
end # if
if val < 1 && val >= break_point && AngleIsBetween
push!(ys, i)
push!(xs, j)
end
end
end
for (yi, xi) in zip(ys, xs)
drawifinbounds!(img, yi, xi, color)
end
img
end

Calculate bessel function in MATLAB using Jm+1=2mj(m) -j(m-1) formula

I tried to implement bessel function using that formula, this is the code:
function result=Bessel(num);
if num==0
result=bessel(0,1);
elseif num==1
result=bessel(1,1);
else
result=2*(num-1)*Bessel(num-1)-Bessel(num-2);
end;
But if I use MATLAB's bessel function to compare it with this one, I get too high different values.
For example if I type Bessel(20) it gives me 3.1689e+005 as result, if instead I type bessel(20,1) it gives me 3.8735e-025 , a totally different result.
such recurrence relations are nice in mathematics but numerically unstable when implementing algorithms using limited precision representations of floating-point numbers.
Consider the following comparison:
x = 0:20;
y1 = arrayfun(#(n)besselj(n,1), x); %# builtin function
y2 = arrayfun(#Bessel, x); %# your function
semilogy(x,y1, x,y2), grid on
legend('besselj','Bessel')
title('J_\nu(z)'), xlabel('\nu'), ylabel('log scale')
So you can see how the computed values start to differ significantly after 9.
According to MATLAB:
BESSELJ uses a MEX interface to a Fortran library by D. E. Amos.
and gives the following as references for their implementation:
D. E. Amos, "A subroutine package for Bessel functions of a complex
argument and nonnegative order", Sandia National Laboratory Report,
SAND85-1018, May, 1985.
D. E. Amos, "A portable package for Bessel functions of a complex
argument and nonnegative order", Trans. Math. Software, 1986.
The forward recurrence relation you are using is not stable. To see why, consider that the values of BesselJ(n,x) become smaller and smaller by about a factor 1/2n. You can see this by looking at the first term of the Taylor series for J.
So, what you're doing is subtracting a large number from a multiple of a somewhat smaller number to get an even smaller number. Numerically, that's not going to work well.
Look at it this way. We know the result is of the order of 10^-25. You start out with numbers that are of the order of 1. So in order to get even one accurate digit out of this, we have to know the first two numbers with at least 25 digits precision. We clearly don't, and the recurrence actually diverges.
Using the same recurrence relation to go backwards, from high orders to low orders, is stable. When you start with correct values for J(20,1) and J(19,1), you can calculate all orders down to 0 with full accuracy as well. Why does this work? Because now the numbers are getting larger in each step. You're subtracting a very small number from an exact multiple of a larger number to get an even larger number.
You can just modify the code below which is for the Spherical bessel function. It is well tested and works for all arguments and order range. I am sorry it is in C#
public static Complex bessel(int n, Complex z)
{
if (n == 0) return sin(z) / z;
if (n == 1) return sin(z) / (z * z) - cos(z) / z;
if (n <= System.Math.Abs(z.real))
{
Complex h0 = bessel(0, z);
Complex h1 = bessel(1, z);
Complex ret = 0;
for (int i = 2; i <= n; i++)
{
ret = (2 * i - 1) / z * h1 - h0;
h0 = h1;
h1 = ret;
if (double.IsInfinity(ret.real) || double.IsInfinity(ret.imag)) return double.PositiveInfinity;
}
return ret;
}
else
{
double u = 2.0 * abs(z.real) / (2 * n + 1);
double a = 0.1;
double b = 0.175;
int v = n - (int)System.Math.Ceiling((System.Math.Log(0.5e-16 * (a + b * u * (2 - System.Math.Pow(u, 2)) / (1 - System.Math.Pow(u, 2))), 2)));
Complex ret = 0;
while (v > n - 1)
{
ret = z / (2 * v + 1.0 - z * ret);
v = v - 1;
}
Complex jnM1 = ret;
while (v > 0)
{
ret = z / (2 * v + 1.0 - z * ret);
jnM1 = jnM1 * ret;
v = v - 1;
}
return jnM1 * sin(z) / z;
}
}

2d game : fire at a moving target by predicting intersection of projectile and unit

Okay, this all takes place in a nice and simple 2D world... :)
Suppose I have a static object A at position Apos, and a linearly moving object B at Bpos with bVelocity, and an ammo round with velocity Avelocity...
How would I find out the angle that A has to shoot, to hit B, taking into account B's linear velocity and the speed of A's ammo ?
Right now the aim's at the current position of the object, which means that by the time my projectile gets there the unit has moved on to safer positions :)
I wrote an aiming subroutine for xtank a while back. I'll try to lay out how I did it.
Disclaimer: I may have made one or more silly mistakes anywhere in here; I'm just trying to reconstruct the reasoning with my rusty math skills. However, I'll cut to the chase first, since this is a programming Q&A instead of a math class :-)
How to do it
It boils down to solving a quadratic equation of the form:
a * sqr(x) + b * x + c == 0
Note that by sqr I mean square, as opposed to square root. Use the following values:
a := sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)
b := 2 * (target.velocityX * (target.startX - cannon.X)
+ target.velocityY * (target.startY - cannon.Y))
c := sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)
Now we can look at the discriminant to determine if we have a possible solution.
disc := sqr(b) - 4 * a * c
If the discriminant is less than 0, forget about hitting your target -- your projectile can never get there in time. Otherwise, look at two candidate solutions:
t1 := (-b + sqrt(disc)) / (2 * a)
t2 := (-b - sqrt(disc)) / (2 * a)
Note that if disc == 0 then t1 and t2 are equal.
If there are no other considerations such as intervening obstacles, simply choose the smaller positive value. (Negative t values would require firing backward in time to use!)
Substitute the chosen t value back into the target's position equations to get the coordinates of the leading point you should be aiming at:
aim.X := t * target.velocityX + target.startX
aim.Y := t * target.velocityY + target.startY
Derivation
At time T, the projectile must be a (Euclidean) distance from the cannon equal to the elapsed time multiplied by the projectile speed. This gives an equation for a circle, parametric in elapsed time.
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr(t * projectile_speed)
Similarly, at time T, the target has moved along its vector by time multiplied by its velocity:
target.X == t * target.velocityX + target.startX
target.Y == t * target.velocityY + target.startY
The projectile can hit the target when its distance from the cannon matches the projectile's distance.
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr(target.X - cannon.X) + sqr(target.Y - cannon.Y)
Wonderful! Substituting the expressions for target.X and target.Y gives
sqr(projectile.X - cannon.X) + sqr(projectile.Y - cannon.Y)
== sqr((t * target.velocityX + target.startX) - cannon.X)
+ sqr((t * target.velocityY + target.startY) - cannon.Y)
Substituting the other side of the equation gives this:
sqr(t * projectile_speed)
== sqr((t * target.velocityX + target.startX) - cannon.X)
+ sqr((t * target.velocityY + target.startY) - cannon.Y)
... subtracting sqr(t * projectile_speed) from both sides and flipping it around:
sqr((t * target.velocityX) + (target.startX - cannon.X))
+ sqr((t * target.velocityY) + (target.startY - cannon.Y))
- sqr(t * projectile_speed)
== 0
... now resolve the results of squaring the subexpressions ...
sqr(target.velocityX) * sqr(t)
+ 2 * t * target.velocityX * (target.startX - cannon.X)
+ sqr(target.startX - cannon.X)
+ sqr(target.velocityY) * sqr(t)
+ 2 * t * target.velocityY * (target.startY - cannon.Y)
+ sqr(target.startY - cannon.Y)
- sqr(projectile_speed) * sqr(t)
== 0
... and group similar terms ...
sqr(target.velocityX) * sqr(t)
+ sqr(target.velocityY) * sqr(t)
- sqr(projectile_speed) * sqr(t)
+ 2 * t * target.velocityX * (target.startX - cannon.X)
+ 2 * t * target.velocityY * (target.startY - cannon.Y)
+ sqr(target.startX - cannon.X)
+ sqr(target.startY - cannon.Y)
== 0
... then combine them ...
(sqr(target.velocityX) + sqr(target.velocityY) - sqr(projectile_speed)) * sqr(t)
+ 2 * (target.velocityX * (target.startX - cannon.X)
+ target.velocityY * (target.startY - cannon.Y)) * t
+ sqr(target.startX - cannon.X) + sqr(target.startY - cannon.Y)
== 0
... giving a standard quadratic equation in t. Finding the positive real zeros of this equation gives the (zero, one, or two) possible hit locations, which can be done with the quadratic formula:
a * sqr(x) + b * x + c == 0
x == (-b ± sqrt(sqr(b) - 4 * a * c)) / (2 * a)
+1 on Jeffrey Hantin's excellent answer here. I googled around and found solutions that were either too complex or not specifically about the case I was interested in (simple constant velocity projectile in 2D space.) His was exactly what I needed to produce the self-contained JavaScript solution below.
The one point I would add is that there are a couple special cases you have to watch for in addition to the discriminant being negative:
"a == 0": occurs if target and projectile are traveling the same speed. (solution is linear, not quadratic)
"a == 0 and b == 0": if both target and projectile are stationary. (no solution unless c == 0, i.e. src & dst are same point.)
Code:
/**
* Return the firing solution for a projectile starting at 'src' with
* velocity 'v', to hit a target, 'dst'.
*
* #param ({x, y}) src position of shooter
* #param ({x, y, vx, vy}) dst position & velocity of target
* #param (Number) v speed of projectile
*
* #return ({x, y}) Coordinate at which to fire (and where intercept occurs). Or `null` if target cannot be hit.
*/
function intercept(src, dst, v) {
const tx = dst.x - src.x;
const ty = dst.y - src.y;
const tvx = dst.vx;
const tvy = dst.vy;
// Get quadratic equation components
const a = tvx * tvx + tvy * tvy - v * v;
const b = 2 * (tvx * tx + tvy * ty);
const c = tx * tx + ty * ty;
// Solve quadratic
const ts = quad(a, b, c); // See quad(), below
// Find smallest positive solution
let sol = null;
if (ts) {
const t0 = ts[0];
const t1 = ts[1];
let t = Math.min(t0, t1);
if (t < 0) t = Math.max(t0, t1);
if (t > 0) {
sol = {
x: dst.x + dst.vx * t,
y: dst.y + dst.vy * t
};
}
}
return sol;
}
/**
* Return solutions for quadratic
*/
function quad(a, b, c) {
let sol = null;
if (Math.abs(a) < 1e-6) {
if (Math.abs(b) < 1e-6) {
sol = Math.abs(c) < 1e-6 ? [0, 0] : null;
} else {
sol = [-c / b, -c / b];
}
} else {
let disc = b * b - 4 * a * c;
if (disc >= 0) {
disc = Math.sqrt(disc);
a = 2 * a;
sol = [(-b - disc) / a, (-b + disc) / a];
}
}
return sol;
}
// For example ...
const sol = intercept(
{x:2, y:4}, // Starting coord
{x:5, y:7, vx: 2, vy:1}, // Target coord and velocity
5 // Projectile velocity
)
console.log('Fire at', sol)
First rotate the axes so that AB is vertical (by doing a rotation)
Now, split the velocity vector of B into the x and y components (say Bx and By). You can use this to calculate the x and y components of the vector you need to shoot at.
B --> Bx
|
|
V
By
Vy
^
|
|
A ---> Vx
You need Vx = Bx and Sqrt(Vx*Vx + Vy*Vy) = Velocity of Ammo.
This should give you the vector you need in the new system. Transform back to old system and you are done (by doing a rotation in the other direction).
Jeffrey Hantin has a nice solution for this problem, though his derivation is overly complicated. Here's a cleaner way of deriving it with some of the resultant code at the bottom.
I'll be using x.y to represent vector dot product, and if a vector quantity is squared, it means I am dotting it with itself.
origpos = initial position of shooter
origvel = initial velocity of shooter
targpos = initial position of target
targvel = initial velocity of target
projvel = velocity of the projectile relative to the origin (cause ur shooting from there)
speed = the magnitude of projvel
t = time
We know that the position of the projectile and target with respect to t time can be described with some equations.
curprojpos(t) = origpos + t*origvel + t*projvel
curtargpos(t) = targpos + t*targvel
We want these to be equal to each other at some point (the point of intersection), so let's set them equal to each other and solve for the free variable, projvel.
origpos + t*origvel + t*projvel = targpos + t*targvel
turns into ->
projvel = (targpos - origpos)/t + targvel - origvel
Let's forget about the notion of origin and target position/velocity. Instead, let's work in relative terms since motion of one thing is relative to another. In this case, what we now have is relpos = targetpos - originpos and relvel = targetvel - originvel
projvel = relpos/t + relvel
We don't know what projvel is, but we do know that we want projvel.projvel to be equal to speed^2, so we'll square both sides and we get
projvel^2 = (relpos/t + relvel)^2
expands into ->
speed^2 = relvel.relvel + 2*relpos.relvel/t + relpos.relpos/t^2
We can now see that the only free variable is time, t, and then we'll use t to solve for projvel. We'll solve for t with the quadratic formula. First separate it out into a, b and c, then solve for the roots.
Before solving, though, remember that we want the best solution where t is smallest, but we need to make sure that t is not negative (you can't hit something in the past)
a = relvel.relvel - speed^2
b = 2*relpos.relvel
c = relpos.relpos
h = -b/(2*a)
k2 = h*h - c/a
if k2 < 0, then there are no roots and there is no solution
if k2 = 0, then there is one root at h
if 0 < h then t = h
else, no solution
if k2 > 0, then there are two roots at h - k and h + k, we also know r0 is less than r1.
k = sqrt(k2)
r0 = h - k
r1 = h + k
we have the roots, we must now solve for the smallest positive one
if 0<r0 then t = r0
elseif 0<r1 then t = r1
else, no solution
Now, if we have a t value, we can plug t back into the original equation and solve for the projvel
projvel = relpos/t + relvel
Now, to the shoot the projectile, the resultant global position and velocity for the projectile is
globalpos = origpos
globalvel = origvel + projvel
And you're done!
My implementation of my solution in Lua, where vec*vec represents vector dot product:
local function lineartrajectory(origpos,origvel,speed,targpos,targvel)
local relpos=targpos-origpos
local relvel=targvel-origvel
local a=relvel*relvel-speed*speed
local b=2*relpos*relvel
local c=relpos*relpos
if a*a<1e-32 then--code translation for a==0
if b*b<1e-32 then
return false,"no solution"
else
local h=-c/b
if 0<h then
return origpos,relpos/h+targvel,h
else
return false,"no solution"
end
end
else
local h=-b/(2*a)
local k2=h*h-c/a
if k2<-1e-16 then
return false,"no solution"
elseif k2<1e-16 then--code translation for k2==0
if 0<h then
return origpos,relpos/h+targvel,h
else
return false,"no solution"
end
else
local k=k2^0.5
if k<h then
return origpos,relpos/(h-k)+targvel,h-k
elseif -k<h then
return origpos,relpos/(h+k)+targvel,h+k
else
return false,"no solution"
end
end
end
end
Following is polar coordinate based aiming code in C++.
To use with rectangular coordinates you would need to first convert the targets relative coordinate to angle/distance, and the targets x/y velocity to angle/speed.
The "speed" input is the speed of the projectile. The units of the speed and targetSpeed are irrelevent, as only the ratio of the speeds are used in the calculation. The output is the angle the projectile should be fired at and the distance to the collision point.
The algorithm is from source code available at http://www.turtlewar.org/ .
// C++
static const double pi = 3.14159265358979323846;
inline double Sin(double a) { return sin(a*(pi/180)); }
inline double Asin(double y) { return asin(y)*(180/pi); }
bool/*ok*/ Rendezvous(double speed,double targetAngle,double targetRange,
double targetDirection,double targetSpeed,double* courseAngle,
double* courseRange)
{
// Use trig to calculate coordinate of future collision with target.
// c
//
// B A
//
// a C b
//
// Known:
// C = distance to target
// b = direction of target travel, relative to it's coordinate
// A/B = ratio of speed and target speed
//
// Use rule of sines to find unknowns.
// sin(a)/A = sin(b)/B = sin(c)/C
//
// a = asin((A/B)*sin(b))
// c = 180-a-b
// B = C*(sin(b)/sin(c))
bool ok = 0;
double b = 180-(targetDirection-targetAngle);
double A_div_B = targetSpeed/speed;
double C = targetRange;
double sin_b = Sin(b);
double sin_a = A_div_B*sin_b;
// If sin of a is greater than one it means a triangle cannot be
// constructed with the given angles that have sides with the given
// ratio.
if(fabs(sin_a) <= 1)
{
double a = Asin(sin_a);
double c = 180-a-b;
double sin_c = Sin(c);
double B;
if(fabs(sin_c) > .0001)
{
B = C*(sin_b/sin_c);
}
else
{
// Sin of small angles approach zero causing overflow in
// calculation. For nearly flat triangles just treat as
// flat.
B = C/(A_div_B+1);
}
// double A = C*(sin_a/sin_c);
ok = 1;
*courseAngle = targetAngle+a;
*courseRange = B;
}
return ok;
}
Here's an example where I devised and implemented a solution to the problem of predictive targeting using a recursive algorithm: http://www.newarteest.com/flash/targeting.html
I'll have to try out some of the other solutions presented because it seems more efficient to calculate it in one step, but the solution I came up with was to estimate the target position and feed that result back into the algorithm to make a new more accurate estimate, repeating several times.
For the first estimate I "fire" at the target's current position and then use trigonometry to determine where the target will be when the shot reaches the position fired at. Then in the next iteration I "fire" at that new position and determine where the target will be this time. After about 4 repeats I get within a pixel of accuracy.
I just hacked this version for aiming in 2d space, I didn't test it very thoroughly yet but it seems to work. The idea behind it is this:
Create a vector perpendicular to the vector pointing from the muzzle to the target.
For a collision to occur, the velocities of the target and the projectile along this vector (axis) should be the same!
Using fairly simple cosine stuff I arrived at this code:
private Vector3 CalculateProjectileDirection(Vector3 a_MuzzlePosition, float a_ProjectileSpeed, Vector3 a_TargetPosition, Vector3 a_TargetVelocity)
{
// make sure it's all in the horizontal plane:
a_TargetPosition.y = 0.0f;
a_MuzzlePosition.y = 0.0f;
a_TargetVelocity.y = 0.0f;
// create a normalized vector that is perpendicular to the vector pointing from the muzzle to the target's current position (a localized x-axis):
Vector3 perpendicularVector = Vector3.Cross(a_TargetPosition - a_MuzzlePosition, -Vector3.up).normalized;
// project the target's velocity vector onto that localized x-axis:
Vector3 projectedTargetVelocity = Vector3.Project(a_TargetVelocity, perpendicularVector);
// calculate the angle that the projectile velocity should make with the localized x-axis using the consine:
float angle = Mathf.Acos(projectedTargetVelocity.magnitude / a_ProjectileSpeed) / Mathf.PI * 180;
if (Vector3.Angle(perpendicularVector, a_TargetVelocity) > 90.0f)
{
angle = 180.0f - angle;
}
// rotate the x-axis so that is points in the desired velocity direction of the projectile:
Vector3 returnValue = Quaternion.AngleAxis(angle, -Vector3.up) * perpendicularVector;
// give the projectile the correct speed:
returnValue *= a_ProjectileSpeed;
return returnValue;
}
I made a public domain Unity C# function here:
http://ringofblades.com/Blades/Code/PredictiveAim.cs
It is for 3D, but you can easily modify this for 2D by replacing the Vector3s with Vector2s and using your down axis of choice for gravity if there is gravity.
In case the theory interests you, I walk through the derivation of the math here:
http://www.gamasutra.com/blogs/KainShin/20090515/83954/Predictive_Aim_Mathematics_for_AI_Targeting.php
I've seen many ways to solve this problem mathematically, but this was a component relevant to a project my class was required to do in high school, and not everyone in this programming class had a background with calculus, or even vectors for that matter, so I created a way to solve this problem with more of a programming approach. The point of intersection will be accurate, although it may hit 1 frame later than in the mathematical computations.
Consider:
S = shooterPos, E = enemyPos, T = targetPos, Sr = shooter range, D = enemyDir
V = distance from E to T, P = projectile speed, Es = enemy speed
In the standard implementation of this problem [S,E,P,Es,D] are all givens and you are solving either to find T or the angle at which to shoot so that you hit T at the proper timing.
The main aspect of this method of solving the problem is to consider the range of the shooter as a circle encompassing all possible points that can be shot at any given time. The radius of this circle is equal to:
Sr = P*time
Where time is calculated as an iteration of a loop.
Thus to find the distance an enemy travels given the time iteration we create the vector:
V = D*Es*time
Now, to actually solve the problem we want to find a point at which the distance from the target (T) to our shooter (S) is less than the range of our shooter (Sr). Here is somewhat of a pseudocode implementation of this equation.
iteration = 0;
while(TargetPoint.hasNotPassedShooter)
{
TargetPoint = EnemyPos + (EnemyMovementVector)
if(distanceFrom(TargetPoint,ShooterPos) < (ShooterRange))
return TargetPoint;
iteration++
}
Basically , intersection concept is not really needed here, As far as you are using projectile motion, you just need to hit at a particular angle and instantiate at the time of shooting so that you get the exact distance of your target from the Source and then once you have the distance, you can calculate the appropriate velocity with which it should shot in order to hit the Target.
The following link makes teh concept clear and is considered helpful, might help:
Projectile motion to always hit a moving target
I grabbed one of the solutions from here, but none of them take into account movement of the shooter. If your shooter is moving, you might want to take that into account (as the shooter's velocity should be added to your bullet's velocity when you fire). Really all you need to do is subtract your shooter's velocity from the target's velocity. So if you're using broofa's code above (which I would recommend), change the lines
tvx = dst.vx;
tvy = dst.vy;
to
tvx = dst.vx - shooter.vx;
tvy = dst.vy - shooter.vy;
and you should be all set.

Finding quaternion representing the rotation from one vector to another

I have two vectors u and v. Is there a way of finding a quaternion representing the rotation from u to v?
Quaternion q;
vector a = crossproduct(v1, v2);
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2);
Don't forget to normalize q.
Richard is right about there not being a unique rotation, but the above should give the "shortest arc," which is probably what you need.
Half-Way Vector Solution
I came up with the solution that I believe Imbrondir was trying to present (albeit with a minor mistake, which was probably why sinisterchipmunk had trouble verifying it).
Given that we can construct a quaternion representing a rotation around an axis like so:
q.w == cos(angle / 2)
q.x == sin(angle / 2) * axis.x
q.y == sin(angle / 2) * axis.y
q.z == sin(angle / 2) * axis.z
And that the dot and cross product of two normalized vectors are:
dot == cos(theta)
cross.x == sin(theta) * perpendicular.x
cross.y == sin(theta) * perpendicular.y
cross.z == sin(theta) * perpendicular.z
Seeing as a rotation from u to v can be achieved by rotating by theta (the angle between the vectors) around the perpendicular vector, it looks as though we can directly construct a quaternion representing such a rotation from the results of the dot and cross products; however, as it stands, theta = angle / 2, which means that doing so would result in twice the desired rotation.
One solution is to compute a vector half-way between u and v, and use the dot and cross product of u and the half-way vector to construct a quaternion representing a rotation of twice the angle between u and the half-way vector, which takes us all the way to v!
There is a special case, where u == -v and a unique half-way vector becomes impossible to calculate. This is expected, given the infinitely many "shortest arc" rotations which can take us from u to v, and we must simply rotate by 180 degrees around any vector orthogonal to u (or v) as our special-case solution. This is done by taking the normalized cross product of u with any other vector not parallel to u.
Pseudo code follows (obviously, in reality the special case would have to account for floating point inaccuracies -- probably by checking the dot products against some threshold rather than an absolute value).
Also note that there is no special case when u == v (the identity quaternion is produced -- check and see for yourself).
// N.B. the arguments are _not_ axis and angle, but rather the
// raw scalar-vector components.
Quaternion(float w, Vector3 xyz);
Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
// It is important that the inputs are of equal length when
// calculating the half-way vector.
u = normalized(u);
v = normalized(v);
// Unfortunately, we have to check for when u == -v, as u + v
// in this case will be (0, 0, 0), which cannot be normalized.
if (u == -v)
{
// 180 degree rotation around any orthogonal vector
return Quaternion(0, normalized(orthogonal(u)));
}
Vector3 half = normalized(u + v);
return Quaternion(dot(u, half), cross(u, half));
}
The orthogonal function returns any vector orthogonal to the given vector. This implementation uses the cross product with the most orthogonal basis vector.
Vector3 orthogonal(Vector3 v)
{
float x = abs(v.x);
float y = abs(v.y);
float z = abs(v.z);
Vector3 other = x < y ? (x < z ? X_AXIS : Z_AXIS) : (y < z ? Y_AXIS : Z_AXIS);
return cross(v, other);
}
Half-Way Quaternion Solution
This is actually the solution presented in the accepted answer, and it seems to be marginally faster than the half-way vector solution (~20% faster by my measurements, though don't take my word for it). I'm adding it here in case others like myself are interested in an explanation.
Essentially, instead of calculating a quaternion using a half-way vector, you can calculate the quaternion which results in twice the required rotation (as detailed in the other solution), and find the quaternion half-way between that and zero degrees.
As I explained before, the quaternion for double the required rotation is:
q.w == dot(u, v)
q.xyz == cross(u, v)
And the quaternion for zero rotation is:
q.w == 1
q.xyz == (0, 0, 0)
Calculating the half-way quaternion is simply a matter of summing the quaternions and normalizing the result, just like with vectors. However, as is also the case with vectors, the quaternions must have the same magnitude, otherwise the result will be skewed towards the quaternion with the larger magnitude.
A quaternion constructed from the dot and cross product of two vectors will have the same magnitude as those products: length(u) * length(v). Rather than dividing all four components by this factor, we can instead scale up the identity quaternion. And if you were wondering why the accepted answer seemingly complicates matters by using sqrt(length(u) ^ 2 * length(v) ^ 2), it's because the squared length of a vector is quicker to calculate than the length, so we can save one sqrt calculation. The result is:
q.w = dot(u, v) + sqrt(length_2(u) * length_2(v))
q.xyz = cross(u, v)
And then normalize the result. Pseudo code follows:
Quaternion get_rotation_between(Vector3 u, Vector3 v)
{
float k_cos_theta = dot(u, v);
float k = sqrt(length_2(u) * length_2(v));
if (k_cos_theta / k == -1)
{
// 180 degree rotation around any orthogonal vector
return Quaternion(0, normalized(orthogonal(u)));
}
return normalized(Quaternion(k_cos_theta + k, cross(u, v)));
}
The problem as stated is not well-defined: there is not a unique rotation for a given pair of vectors. Consider the case, for example, where u = <1, 0, 0> and v = <0, 1, 0>. One rotation from u to v would be a pi / 2 rotation around the z-axis. Another rotation from u to v would be a pi rotation around the vector <1, 1, 0>.
I'm not much good on Quaternion. However I struggled for hours on this, and could not make Polaris878 solution work. I've tried pre-normalizing v1 and v2. Normalizing q. Normalizing q.xyz. Yet still I don't get it. The result still didn't give me the right result.
In the end though I found a solution that did. If it helps anyone else, here's my working (python) code:
def diffVectors(v1, v2):
""" Get rotation Quaternion between 2 vectors """
v1.normalize(), v2.normalize()
v = v1+v2
v.normalize()
angle = v.dot(v2)
axis = v.cross(v2)
return Quaternion( angle, *axis )
A special case must be made if v1 and v2 are paralell like v1 == v2 or v1 == -v2 (with some tolerance), where I believe the solutions should be Quaternion(1, 0,0,0) (no rotation) or Quaternion(0, *v1) (180 degree rotation)
Why not represent the vector using pure quaternions? It's better if you normalize them first perhaps.
q1 = (0 ux uy uz)'
q2 = (0 vx vy vz)'
q1 qrot = q2
Pre-multiply with q1-1
qrot = q1-1 q2
where q1-1 = q1conj / qnorm
This is can be thought of as "left division".
Right division, which is not what you want is:
qrot,right = q2-1 q1
From algorithm point of view , the fastest solution looks in pseudocode
Quaternion shortest_arc(const vector3& v1, const vector3& v2 )
{
// input vectors NOT unit
Quaternion q( cross(v1, v2), dot(v1, v2) );
// reducing to half angle
q.w += q.magnitude(); // 4 multiplication instead of 6 and more numerical stable
// handling close to 180 degree case
//... code skipped
return q.normalized(); // normalize if you need UNIT quaternion
}
Be sure that you need unit quaternions (usualy, it is required for interpolation).
NOTE:
Nonunit quaternions can be used with some operations faster than unit.
Some of the answers don't seem to consider possibility that cross product could be 0. Below snippet uses angle-axis representation:
//v1, v2 are assumed to be normalized
Vector3 axis = v1.cross(v2);
if (axis == Vector3::Zero())
axis = up();
else
axis = axis.normalized();
return toQuaternion(axis, ang);
The toQuaternion can be implemented as follows:
static Quaternion toQuaternion(const Vector3& axis, float angle)
{
auto s = std::sin(angle / 2);
auto u = axis.normalized();
return Quaternion(std::cos(angle / 2), u.x() * s, u.y() * s, u.z() * s);
}
If you are using Eigen library, you can also just do:
Quaternion::FromTwoVectors(from, to)
Working just with normalized quaternions, we can express Joseph Thompson's answer in the follwing terms.
Let q_v = (0, u_x, v_y, v_z) and q_w = (0, v_x, v_y, v_z) and consider
q = q_v * q_w = (-u dot v, u x v).
So representing q as q(q_0, q_1, q_2, q_3) we have
q_r = (1 - q_0, q_1, q_2, q_3).normalize()
According to the derivation of the quaternion rotation between two angles, one can rotate a vector u to vector v with
function fromVectors(u, v) {
d = dot(u, v)
w = cross(u, v)
return Quaternion(d + sqrt(d * d + dot(w, w)), w).normalize()
}
If it is known that the vectors u to vector v are unit vectors, the function reduces to
function fromUnitVectors(u, v) {
return Quaternion(1 + dot(u, v), cross(u, v)).normalize()
}
Depending on your use-case, handling the cases when the dot product is 1 (parallel vectors) and -1 (vectors pointing in opposite directions) may be needed.
The Generalized Solution
function align(Q, u, v)
U = quat(0, ux, uy, uz)
V = quat(0, vx, vy, vz)
return normalize(length(U*V)*Q - V*Q*U)
To find the quaternion of smallest rotation which rotate u to v, use
align(quat(1, 0, 0, 0), u, v)
Why This Generalization?
R is the quaternion closest to Q which will rotate u to v. More importantly, R is the quaternion closest to Q whose local u direction points in same direction as v.
This can be used to give you all possible rotations which rotate from u to v, depending on the choice of Q. If you want the minimal rotation from u to v, as the other solutions give, use Q = quat(1, 0, 0, 0).
Most commonly, I find that the real operation you want to do is a general alignment of one axis with another.
// If you find yourself often doing something like
quatFromTo(toWorldSpace(Q, localFrom), worldTo)*Q
// you should instead consider doing
align(Q, localFrom, worldTo)
Example
Say you want the quaternion Y which only represents Q's yaw, the pure rotation about the y axis. We can compute Y with the following.
Y = align(quat(Qw, Qx, Qy, Qz), vec(0, 1, 0), vec(0, 1, 0))
// simplifies to
Y = normalize(quat(Qw, 0, Qy, 0))
Alignment as a 4x4 Projection Matrix
If you want to perform the same alignment operation repeatedly, because this operation is the same as the projection of a quaternion onto a 2D plane embedded in 4D space, we can represent this operation as the multiplication with 4x4 projection matrix, A*Q.
I = mat4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1)
A = I - leftQ(V)*rightQ(U)/length(U*V)
// which expands to
A = mat4(
1 + ux*vx + uy*vy + uz*vz, uy*vz - uz*vy, uz*vx - ux*vz, ux*vy - uy*vx,
uy*vz - uz*vy, 1 + ux*vx - uy*vy - uz*vz, uy*vx + ux*vy, uz*vx + ux*vz,
uz*vx - ux*vz, uy*vx + ux*vy, 1 - ux*vx + uy*vy - uz*vz, uz*vy + uy*vz,
ux*vy - uy*vx, uz*vx + ux*vz, uz*vy + uy*vz, 1 - ux*vx - uy*vy + uz*vz)
// A can be applied to Q with the usual matrix-vector multiplication
R = normalize(A*Q)
//LeftQ is a 4x4 matrix which represents the multiplication on the left
//RightQ is a 4x4 matrix which represents the multiplication on the Right
LeftQ(w, x, y, z) = mat4(
w, -x, -y, -z,
x, w, -z, y,
y, z, w, -x,
z, -y, x, w)
RightQ(w, x, y, z) = mat4(
w, -x, -y, -z,
x, w, z, -y,
y, -z, w, x,
z, y, -x, w)

Math - mapping numbers

How do I map numbers, linearly, between a and b to go between c and d.
That is, I want numbers between 2 and 6 to map to numbers between 10 and 20... but I need the generalized case.
My brain is fried.
If your number X falls between A and B, and you would like Y to fall between C and D, you can apply the following linear transform:
Y = (X-A)/(B-A) * (D-C) + C
That should give you what you want, although your question is a little ambiguous, since you could also map the interval in the reverse direction. Just watch out for division by zero and you should be OK.
Divide to get the ratio between the sizes of the two ranges, then subtract the starting value of your inital range, multiply by the ratio and add the starting value of your second range. In other words,
R = (20 - 10) / (6 - 2)
y = (x - 2) * R + 10
This evenly spreads the numbers from the first range in the second range.
It would be nice to have this functionality in the java.lang.Math class, as this is such a widely required function and is available in other languages.
Here is a simple implementation:
final static double EPSILON = 1e-12;
public static double map(double valueCoord1,
double startCoord1, double endCoord1,
double startCoord2, double endCoord2) {
if (Math.abs(endCoord1 - startCoord1) < EPSILON) {
throw new ArithmeticException("/ 0");
}
double offset = startCoord2;
double ratio = (endCoord2 - startCoord2) / (endCoord1 - startCoord1);
return ratio * (valueCoord1 - startCoord1) + offset;
}
I am putting this code here as a reference for future myself and may be it will help someone.
As an aside, this is the same problem as the classic convert celcius to farenheit where you want to map a number range that equates 0 - 100 (C) to 32 - 212 (F).
https://rosettacode.org/wiki/Map_range
[a1, a2] => [b1, b2]
if s in range of [a1, a2]
then t which will be in range of [b1, b2]
t= b1 + ((s- a1) * (b2-b1))/ (a2-a1)
In addition to #PeterAllenWebb answer, if you would like to reverse back the result use the following:
reverseX = (B-A)*(Y-C)/(D-C) + A
Each unit interval on the first range takes up (d-c)/(b-a) "space" on the second range.
Pseudo:
var interval = (d-c)/(b-a)
for n = 0 to (b - a)
print c + n*interval
How you handle the rounding is up to you.
if your range from [a to b] and you want to map it in [c to d] where x is the value you want to map
use this formula (linear mapping)
double R = (d-c)/(b-a)
double y = c+(x*R)+R
return(y)
Where X is the number to map from A-B to C-D, and Y is the result:
Take the linear interpolation formula, lerp(a,b,m)=a+(m*(b-a)), and put C and D in place of a and b to get Y=C+(m*(D-C)). Then, in place of m, put (X-A)/(B-A) to get Y=C+(((X-A)/(B-A))*(D-C)). This is an okay map function, but it can be simplified. Take the (D-C) piece, and put it inside the dividend to get Y=C+(((X-A)*(D-C))/(B-A)). This gives us another piece we can simplify, (X-A)*(D-C), which equates to (X*D)-(X*C)-(A*D)+(A*C). Pop that in, and you get Y=C+(((X*D)-(X*C)-(A*D)+(A*C))/(B-A)). The next thing you need to do is add in the +C bit. To do that, you multiply C by (B-A) to get ((B*C)-(A*C)), and move it into the dividend to get Y=(((X*D)-(X*C)-(A*D)+(A*C)+(B*C)-(A*C))/(B-A)). This is redundant, containing both a +(A*C) and a -(A*C), which cancel each other out. Remove them, and you get a final result of: Y=((X*D)-(X*C)-(A*D)+(B*C))/(B-A)
TL;DR: The standard map function, Y=C+(((X-A)/(B-A))*(D-C)), can be simplified down to Y=((X*D)-(X*C)-(A*D)+(B*C))/(B-A)
int srcMin = 2, srcMax = 6;
int tgtMin = 10, tgtMax = 20;
int nb = srcMax - srcMin;
int range = tgtMax - tgtMin;
float rate = (float) range / (float) nb;
println(srcMin + " > " + tgtMin);
float stepF = tgtMin;
for (int i = 1; i < nb; i++)
{
stepF += rate;
println((srcMin + i) + " > " + (int) (stepF + 0.5) + " (" + stepF + ")");
}
println(srcMax + " > " + tgtMax);
With checks on divide by zero, of course.

Resources