Moving slowly an initial angle until it reach a final angle - math

I will try to be very descriptive with this. I'm editing a game right now and the scenario is a 3D area.
I have an initial angle, writen as a direction vector, and another vector which haves different coordinates. As we know, the angle between 2 vectors is given by the formula: Theta = ACos( DotProduct( vec1, vec2 ) / ( VectorLength( vec1 ) * VectorLength( vec2 ) ) )
So let's describe the scenario: I'm currently programming some kind of stationary weapon, a sentry gun, this thing moves slowly his "head", shooting bullets to enemies. That angle rotation thing is my problem.
Let's imagine this: I have my sentry gun on a empty 3D area, and a "enemy" spawns over there. I can currently get the direction vector of my sentry's view angle, and the direction vector between my sentry and the player. Let's guess, using the formula described, his separation angle is 45 degrees. My sentry gun thinks (calls a function) at every 0.1 seconds, and I want to move his head 5 degrees at every thinking function until it reach the the player (ie, both vectors are nearly equal), and that means it will reach the player (if player keeps on its position...) in 0.9 seconds (5 degrees from 45)
How I can move sentry's view angle slowly until it reach a target? In 2D is easily but know I'm fighting with a 3D scenario, and I'm currently lost with this.
Any help would be appreciated, and about coding, I will be grateful with a pseudocode. Thanks! (and sorry for my english)

What you need is called SLERP - spherical linear interpolation
Your starting direction vector is p0 there, goal direction is p1, Omega is your Theta, and t parameter varies in range 0..1 with needed step
Delphi example for 2D case (it is easy to control)
var
p0, p1: TPoint;
i, xx, yy: Integer;
omega, InvSinOmega, t, a0, a1: Double;
begin
P0 := Point(0, 200);
P1 := Point(200, 0);
omega := -Pi / 2;
InvSinOmega := 1.0 / Sin(omega);
Canvas.Brush.Color := clRed;
Canvas.Ellipse(120 + P0.X, 120 + P0.Y, 120 + P0.X + 7, 120 + P0.Y + 7);
Canvas.Ellipse(120 + P1.X, 120 + P1.Y, 120 + P1.X + 7, 120 + P1.Y + 7);
for i := 1 to 9 do begin
t := i / 10;
a0 := sin((1 - t) * omega) * InvSinOmega;
a1 := sin(t * omega) * InvSinOmega;
xx := Round(P0.X * a0 + P1.X * a1);
yy := Round(P0.Y * a0 + P1.Y * a1);
Canvas.Brush.Color := RGB(25 * i, 25 * i, 25 * i);
Canvas.Ellipse(120 + xx, 120 + yy, 120 + xx + 9, 120 + yy + 9);
end;

Related

Create saw fin shape in Processing

I'm trying to recreate this image using Processing. How to make the red saw fins?
My thought process given the image is symmetrical over a few axis was to break it up. I'm struggling with the "red saw fins". It looks like I have to use a bezier to create the lines, then fill them in. If I'm able to create 1 fin, then I can rotate a copy of it on the axis to spin it around. https://processing.org/reference/bezier_.html
Just play with control points of Bezier curves to provide needed form.
This is quick-made example in Delphi.
Points P0-P3 give the first side of tooth (concave), points P3-P6 give the second side of tooth.
Rin and ROut are inner and outer radii of saw, RM is middle radius used for control points' calculation.
Inside loop an is base angle for a tooth, an1 is slightly shifted to provide curvature for concave arc, an2 is ending angle (same as base angle of the next tooth) and an3 is middle angle for control points of convex arc.
var
P: array[0..6] of TPoint;
i, N, CX, CY, Rin, ROut, RM: integer;
an, an1, an2, an3: Double;
begin
Rin := 100;
ROut := 170;
RM := (RIn + ROut) div 2;
N := 8;
CX := 350;
CY := 350;
for i := 0 to 7 do begin
an := i * 2 * Pi / N;
an1 := an + 0.25 * Pi / N;
an2 := an + 2 * Pi / N;
an3 := an + Pi / N;
P[0] := Point(Round(CX + Rin * Cos(an)), Round(CY + Rin * Sin(an)));
P[1] := Point(Round(CX + RM * Cos(an1)), Round(CY + RM * Sin(an1)));
P[2] := Point(Round(CX + RM * Cos(an1)), Round(CY + RM * Sin(an1)));
P[3] := Point(Round(CX + ROut * Cos(an)), Round(CY + ROut * Sin(an)));
P[4] := Point(Round(CX + RM * Cos(an3)), Round(CY + RM * Sin(an3)));
P[5] := Point(Round(CX + RM * Cos(an3)), Round(CY + RM * Sin(an3)));
P[6] := Point(Round(CX + RIn * Cos(an2)), Round(CY + RIn * Sin(an2)));
Canvas.PolyBezier(P);
Canvas.Ellipse(CX - 3 * RIn div 4, CY - 3 * RIn div 4,
CX + 3 * RIn div 4, CY + 3 * RIn div 4);
This is a tiny picture. Enlarge it, and then load it into a tool for drawing bezier curves. You know the three fixed coordinates (the two coordinates at the circle, and the tip) and you know the general tangents along which your control points will need to be:
You also know that the control points and the on-curve points need to form a box that surrounds the actual curve (e.g. the curve cannot go outside that box) as a property of Bezier curves.
So now: you play around in finding the correct points. You can use something like Inkscape or Photoshop or Illustrator to see which curve looks good enough, and then you copy the coordinates over into your own program, or you just draw it out on a grid and find the rough coordinates simply by looking at your grid and then play around with values near to where you think.
And then when you have the missing control points, you draw it in processing as a shape:
void draw() {
// draw the inner circle with a "fat" stroke
strokeWeight(20);
// assuming center mode
ellipse(width/2, height/2, 100, 100);
// reset that stroke fatness
strokeWeight(1);
// make sure we'll be rotating about the center of the sketch
translate(width/2, height/2);
// and then start drawing eight 'teeth'
for (int i=0; i<8; i++) {
beginShape();
// we know where p1, p2, and p3 are.
vertex(p1.x, p1.y);
// and we "guessed" at c1, c2, c3, and c4.
bezierVertex(c1.x, c1.y, c2.y, c2.y, p2.x, p2.y);
bezierVertex(c3.x, c3.y, c4.y, c4.y, p3.x, p3.y);
// We leave the shape "open" in case you want both stroke and fill
endShape();
// we're drawing eight teeth, so we need to rotate by 2*PI/8 each time
rotate(0.25 * PI);
}
}

How to find the interception coordinates of a moving target in 3D space?

Assuming I have a spaceship (source); And an asteroid (target) is somewhere near it.
I know, in 3D space (XYZ vectors):
My ship's position (sourcePos) and velocity (sourceVel).
The asteroid's position (targetPos) and velocity (targetVel).
(eg. sourcePos = [30, 20, 10]; sourceVel = [30, 20, 10]; targetPos = [600, 400, 200]; targetVel = [300, 200, 100]`)
I also know that:
The ship's velocity is constant.
The asteroid's velocity is constant.
My ship's projectile speed (projSpd) is constant.
My ship's projectile trajectory, after being shot, is linear (/straight).
(eg. projSpd = 2000.00)
How can I calculate the interception coordinates I need to shoot at in order to hit the asteroid?
Notes:
This question is based on this Yahoo - Answers page.
I also searched for similar problems on Google and here on SO, but most of the answers are for 2D-space, and, of the few for 3D, neither the explanation nor the pseudo-codes explain what is doing what and/or why, so I couldn't really understand enough to apply them on my code successfully. Here are some of the pages I visited:
Danik Games Devlog, Blitz3D Forums thread, UnityAnswers, StackOverflow #1, StackOverflow #2
I really can't figure out the maths / execution-flow on the linked pages as they are, unless someone dissects it (further) into what is doing what, and why;
Provides a properly-commented pseudo-code for me to follow;
Or at least points me to links that actually explain how the equations work instead of just throwing even more random numbers and unfollowable equations in my already-confused psyche.
I find the easiest approach to these kind of problems to make sense of them first, and have a basic high school level of maths will help too.
Solving this problem is essentially solving 2 equations with 2 variables which are unknown to you:
The vector you want to find for your projectile (V)
The time of impact (t)
The variables you know are:
The target's position (P0)
The target's vector (V0)
The target's speed (s0)
The projectile's origin (P1)
The projectile's speed (s1)
Okay, so the 1st equation is basic. The impact point is the same for both the target and the projectile. It is equal to the starting point of both objects + a certain length along the line of both their vectors. This length is denoted by their respective speeds, and the time of impact. Here's the equation:
P0 + (t * s0 * V0) = P1 + (t * s0 * V)
Notice that there are two missing variables here - V & t, and so we won't be able to solve this equation right now. On to the 2nd equation.
The 2nd equation is also quite intuitive. The point of impact's distance from the origin of the projectile is equal to the speed of the projectile multiplied by the time passed:
We'll take a mathematical expression of the point of impact from the 1st equation:
P0 + (t * s0 * V0) <-- point of impact
The point of origin is P1
The distance between these two must be equal to the speed of the projectile multiplied by the time passed (distance = speed * time).
The formula for distance is: (x0 - x1)^2 + (y0 - y1)^2 = distance^2, and so the equation will look like this:
((P0.x + s0 * t * V0.x) - P1.x)^2 + ((P0.y + s0 * t * V0.y) - P1.y)^2 = (s1 * t)^2
(You can easily expand this for 3 dimensions)
Notice that here, you have an equation with only ONE unknown variable: t!. We can discover here what t is, then place it in the previous equation and find the vector V.
Let me solve you some pain by opening up this formula for you (if you really want to, you can do this yourself).
a = (V0.x * V0.x) + (V0.y * V0.y) - (s1 * s1)
b = 2 * ((P0.x * V0.x) + (P0.y * V0.y) - (P1.x * V0.x) - (P1.y * V0.y))
c = (P0.x * P0.x) + (P0.y * P0.y) + (P1.x * P1.x) + (P1.y * P1.y) - (2 * P1.x * P0.x) - (2 * P1.y * P0.y)
t1 = (-b + sqrt((b * b) - (4 * a * c))) / (2 * a)
t2 = (-b - sqrt((b * b) - (4 * a * c))) / (2 * a)
Now, notice - we will get 2 values for t here.
One or both may be negative or an invalid number. Obviously, since t denotes time, and time can't be invalid or negative, you'll need to discard these values of t.
It could very well be that both t's are bad (in which case, the projectile cannot hit the target since it's faster and out of range). It could also be that both t's are valid and positive, in which case you'll want to choose the smaller of the two (since it's preferable to hit the target sooner rather than later).
t = smallestWhichIsntNegativeOrNan(t1, t2)
Now that we've found the time of impact, let's find out what the direction the projectile should fly is. Back to our 1st equation:
P0 + (t * s0 * V0) = P1 + (t * s0 * V)
Now, t is no longer a missing variable, so we can solve this quite easily. Just tidy up the equation to isolate V:
V = (P0 - P1 + (t * s0 * V0)) / (t * s1)
V.x = (P0.x - P1.x + (t * s0 * V0.x)) / (t * s1)
V.y = (P0.y - P1.y + (t * s0 * V0.y)) / (t * s1)
And that's it, you're done!
Assign the vector V to the projectile and it will go to where the target will be rather than where it is now.
I really like this problem since it takes math equations we learnt in high school where everyone said "why are learning this?? we'll never use it in our lives!!", and gives them a pretty awesome and practical application.
I hope this helps you, or anyone else who's trying to solve this.
If you want a projectile to hit asteroid, it should be shoot at the point interceptionPos that satisfy the equation:
|interceptionPos - sourcePos| / |interceptionPos - targetPos| = projSpd / targetVel
where |x| is a length of vector x.
In other words, it would take equal amount of time for the target and the projectile to reach this point.
This problem would be solved by means of geometry and trigonometry, so let's draw it.
A will be asteroid position, S - ship, I - interception point.
Here we have:
AI = targetVel * t
SI = projSpd * t
AS = |targetPos - sourcePos|
vector AS and AI direction is defined, so you can easily calculate cosine of the SAI angle by means of simple vector math (take definitions from here and here). Then you should use the Law of cosines with the SAI angle. It will yield a quadratic equation with variable t that is easy to solve (no solutions = your projectile is slower than asteroid). Just pick the positive solution t, your point-to-shoot will be
targetPos + t * targetVel
I hope you can write a code to solve it by yourself. If you cannot get something please ask in comments.
I got a solution. Notice that the ship position, and the asteroid line (position and velocity) define a 3D plane where the intercept point lies. In my notation below | [x,y,z] | denotes the magnitude of the vector or Sqrt(x^2+y^2+z^2).
Notice that if the asteroid travels with targetSpd = |[300,200,100]| = 374.17 then to reach the intercept point (still unknown, called hitPos) will require time equal to t = |hitPos-targetPos|/targetSpd. This is the same time the projectile needs to reach the intercept point, or t = |hitPos - sourcePos|/projSpd. The two equations are used to solve for the time to intercept
t = |targetPos-sourcePos|/(projSpd - targetSpd)
= |[600,400,200]-[30,20,10]|/(2000 - |[300,200,100]|)
= 710.81 / ( 2000-374.17 ) = 0.4372
Now the location of the intetception point is found by
hitPos = targetPos + targetVel * t
= [600,400,200] + [300,200,100] * 0.4372
= [731.18, 487.45, 243.73 ]
Now that I know the hit position, I can calculate the direction of the projectile as
projDir = (hitPos-sourcePos)/|hitPos-sourcePos|
= [701.17, 467.45, 233.73]/874.52 = [0.8018, 0.5345, 0.2673]
Together the projDir and projSpd define the projectile velocity vector.
Credit to Gil Moshayof's answer, as it really was what I worked off of to build this. But they did two dimensions, and I did three, so I'll share my Unity code in case it helps anyone along. A little long winded and redundant. It helps me to read it and know what's going on.
Vector3 CalculateIntercept(Vector3 targetLocation, Vector3 targetVelocity, Vector3 interceptorLocation, float interceptorSpeed)
{
Vector3 A = targetLocation;
float Ax = targetLocation.x;
float Ay = targetLocation.y;
float Az = targetLocation.z;
float As = targetVelocity.magnitude;
Vector3 Av = Vector3.Normalize(targetVelocity);
float Avx = Av.x;
float Avy = Av.y;
float Avz = Av.z;
Vector3 B = interceptorLocation;
float Bx = interceptorLocation.x;
float By = interceptorLocation.y;
float Bz = interceptorLocation.z;
float Bs = interceptorSpeed;
float t = 0;
float a = (
Mathf.Pow(As, 2) * Mathf.Pow(Avx, 2) +
Mathf.Pow(As, 2) * Mathf.Pow(Avy, 2) +
Mathf.Pow(As, 2) * Mathf.Pow(Avz, 2) -
Mathf.Pow(Bs, 2)
);
if (a == 0)
{
Debug.Log("Quadratic formula not applicable");
return targetLocation;
}
float b = (
As * Avx * Ax +
As * Avy * Ay +
As * Avz * Az +
As * Avx * Bx +
As * Avy * By +
As * Avz * Bz
);
float c = (
Mathf.Pow(Ax, 2) +
Mathf.Pow(Ay, 2) +
Mathf.Pow(Az, 2) -
Ax * Bx -
Ay * By -
Az * Bz +
Mathf.Pow(Bx, 2) +
Mathf.Pow(By, 2) +
Mathf.Pow(Bz, 2)
);
float t1 = (-b + Mathf.Pow((Mathf.Pow(b, 2) - (4 * a * c)), (1 / 2))) / (2 * a);
float t2 = (-b - Mathf.Pow((Mathf.Pow(b, 2) - (4 * a * c)), (1 / 2))) / (2 * a);
Debug.Log("t1 = " + t1 + "; t2 = " + t2);
if (t1 <= 0 || t1 == Mathf.Infinity || float.IsNaN(t1))
if (t2 <= 0 || t2 == Mathf.Infinity || float.IsNaN(t2))
return targetLocation;
else
t = t2;
else if (t2 <= 0 || t2 == Mathf.Infinity || float.IsNaN(t2) || t2 > t1)
t = t1;
else
t = t2;
Debug.Log("t = " + t);
Debug.Log("Bs = " + Bs);
float Bvx = (Ax - Bx + (t * As + Avx)) / (t * Mathf.Pow(Bs, 2));
float Bvy = (Ay - By + (t * As + Avy)) / (t * Mathf.Pow(Bs, 2));
float Bvz = (Az - Bz + (t * As + Avz)) / (t * Mathf.Pow(Bs, 2));
Vector3 Bv = new Vector3(Bvx, Bvy, Bvz);
Debug.Log("||Bv|| = (Should be 1) " + Bv.magnitude);
return Bv * Bs;
}
I followed the problem formulation as described by Gil Moshayof's answer, but found that there was an error in the simplification of the quadratic formula. When I did the derivation by hand I got a different solution.
The following is what worked for me when finding the intersect in 2D:
std::pair<double, double> find_2D_intersect(Vector3 sourcePos, double projSpd, Vector3 targetPos, double targetSpd, double targetHeading)
{
double P0x = targetPos.x;
double P0y = targetPos.y;
double s0 = targetSpd;
double V0x = std::cos(targetHeading);
double V0y = std::sin(targetHeading);
double P1x = sourcePos.x;
double P1y = sourcePos.y;
double s1 = projSpd;
// quadratic formula
double a = (s0 * s0)*((V0x * V0x) + (V0y * V0y)) - (s1 * s1);
double b = 2 * s0 * ((P0x * V0x) + (P0y * V0y) - (P1x * V0x) - (P1y * V0y));
double c = (P0x * P0x) + (P0y * P0y) + (P1x * P1x) + (P1y * P1y) - (2 * P1x * P0x) - (2 * P1y * P0y);
double t1 = (-b + std::sqrt((b * b) - (4 * a * c))) / (2 * a);
double t2 = (-b - std::sqrt((b * b) - (4 * a * c))) / (2 * a);
double t = choose_best_time(t1, t2);
double intersect_x = P0x + t * s0 * V0x;
double intersect_y = P0y + t * s0 * V0y;
return std::make_pair(intersect_x, intersect_y);
}

How to find the angle between 2 pairs of points?

I am doing some multi-touch support that allow 2 fingers to rotate a photos. There are four points: 2 for previous fingers and 2 for current finger positions.
I calculated a new point by subtract the 2 previous fingers, and the second new point was calculate by subtracting the other 2 current finger.
Then, I calculate the angle like this:
radian1 = atan ( p1.y / p1.x );
radian2 = atan ( p2.y / p2.x );
I subtract radian2 and radian1 to get the final angle.
The problem is I can rotate the image beautifully but sometime if I rotate to certain position the photo got flipped e.g. a photo supposed in 270 but it flipped and appeared in 90 degree.
This is the javascript I have written according to the reply below:
var x1 = this.previousMousePoint.x * this.previousMousePoint2.x + this.previousMousePoint.y * this.previousMousePoint2.y;
var y1 = this.previousMousePoint.x * this.previousMousePoint2.y - this.previousMousePoint.y * this.previousMousePoint2.x;
var x2 = center.x * point.x + center.y * point.y;
var y2 = center.x * point.y - center.y * point.x;
var radian1 = Math.atan(y1 / x1);
var radian2 = Math.atan(y2 / x2);
this.anchor.matrix = this.anchor.matrix.rotate(radian2-radian1, pivot);
Is looking ok. but it's kind of slow when i try to rotate an image
The clean way to do this is use angle-subtraction formulas to get values proportional to the sine and cosine of the difference angle, and use atan() only once:
relevant formulas:
cos(a2 - a1) = cos(a1)*cos(a2) + sin(a1)*sin(a2)
sin(a2 - a1) = cos(a1)*sin(a2) - sin(a1)*cos(a2)
p1.x = cos(a1) * len(p1)
p1.y = sin(a1) * len(p1)
p2.x = cos(a2) * len(p2)
p2.y = sin(a2) * len(p2)
-> angle-subtraction: compute values proportional to sin and cos of (a2 - a1)
c12 = p1.x*p2.x + p1.y*p2.y [ = len(p1)*len(p2) * cos(a2 - a1) ]
s12 = p1.x*p2.y - p1.y*p2.x [ = len(p1)*len(p2) * sin(a2 - a1) ]
-> final result: find resulting difference angle a12 [ = a2 - a1 ]
a12 = atan(s12 / c12)
or (if you want a full 360-degree range):
a12 = atan2(s12, c12)
Also, if you want to rotate an image with the result, you may not need to convert (c12,s12) to an angle, anyway: ultimately, your image rotator will use a matrix with the sines and cosines of the resulting rotation angle. By normalizing (c12,s12), you will end up with (cos(a12), sin(a12)), which you may be able to use more directly.
Arc tan maps between 0 and pi. 270 corresponds to 3/2 * pi and will fold over to pi. May I suggest that you incrementally find the angle using the positions of the fingers instead of just starting and the ending positions.
Even if you decide not to actually rotate the figure (to reduce computation), you can still display a tilted line / box / number to indicate the tilt that the software has registered.

Computing the 3D coordinates on a unit sphere from a 2D point

I have a square bitmap of a circle and I want to compute the normals of all the pixels in that circle as if it were a sphere of radius 1:
The sphere/circle is centered in the bitmap.
What is the equation for this?
Don't know much about how people program 3D stuff, so I'll just give the pure math and hope it's useful.
Sphere of radius 1, centered on origin, is the set of points satisfying:
x2 + y2 + z2 = 1
We want the 3D coordinates of a point on the sphere where x and y are known. So, just solve for z:
z = ±sqrt(1 - x2 - y2).
Now, let us consider a unit vector pointing outward from the sphere. It's a unit sphere, so we can just use the vector from the origin to (x, y, z), which is, of course, <x, y, z>.
Now we want the equation of a plane tangent to the sphere at (x, y, z), but this will be using its own x, y, and z variables, so instead I'll make it tangent to the sphere at (x0, y0, z0). This is simply:
x0x + y0y + z0z = 1
Hope this helps.
(OP):
you mean something like:
const int R = 31, SZ = power_of_two(R*2);
std::vector<vec4_t> p;
for(int y=0; y<SZ; y++) {
for(int x=0; x<SZ; x++) {
const float rx = (float)(x-R)/R, ry = (float)(y-R)/R;
if(rx*rx+ry*ry > 1) { // outside sphere
p.push_back(vec4_t(0,0,0,0));
} else {
vec3_t normal(rx,sqrt(1.-rx*rx-ry*ry),ry);
p.push_back(vec4_t(normal,1));
}
}
}
It does make a nice spherical shading-like shading if I treat the normals as colours and blit it; is it right?
(TZ)
Sorry, I'm not familiar with those aspects of C++. Haven't used the language very much, nor recently.
This formula is often used for "fake-envmapping" effect.
double x = 2.0 * pixel_x / bitmap_size - 1.0;
double y = 2.0 * pixel_y / bitmap_size - 1.0;
double r2 = x*x + y*y;
if (r2 < 1)
{
// Inside the circle
double z = sqrt(1 - r2);
.. here the normal is (x, y, z) ...
}
Obviously you're limited to assuming all the points are on one half of the sphere or similar, because of the missing dimension. Past that, it's pretty simple.
The middle of the circle has a normal facing precisely in or out, perpendicular to the plane the circle is drawn on.
Each point on the edge of the circle is facing away from the middle, and thus you can calculate the normal for that.
For any point between the middle and the edge, you use the distance from the middle, and some simple trig (which eludes me at the moment). A lerp is roughly accurate at some points, but not quite what you need, since it's a curve. Simple curve though, and you know the beginning and end values, so figuring them out should only take a simple equation.
I think I get what you're trying to do: generate a grid of depth data for an image. Sort of like ray-tracing a sphere.
In that case, you want a Ray-Sphere Intersection test:
http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter1.htm
Your rays will be simple perpendicular rays, based off your U/V coordinates (times two, since your sphere has a diameter of 2). This will give you the front-facing points on the sphere.
From there, calculate normals as below (point - origin, the radius is already 1 unit).
Ripped off from the link above:
You have to combine two equations:
Ray: R(t) = R0 + t * Rd , t > 0 with R0 = [X0, Y0, Z0] and Rd = [Xd, Yd, Zd]
Sphere: S = the set of points[xs, ys, zs], where (xs - xc)2 + (ys - yc)2 + (zs - zc)2 = Sr2
To do this, calculate your ray (x * pixel / width, y * pixel / width, z: 1), then:
A = Xd^2 + Yd^2 + Zd^2
B = 2 * (Xd * (X0 - Xc) + Yd * (Y0 - Yc) + Zd * (Z0 - Zc))
C = (X0 - Xc)^2 + (Y0 - Yc)^2 + (Z0 - Zc)^2 - Sr^2
Plug into quadratic equation:
t0, t1 = (- B + (B^2 - 4*C)^1/2) / 2
Check discriminant (B^2 - 4*C), and if real root, the intersection is:
Ri = [xi, yi, zi] = [x0 + xd * ti , y0 + yd * ti, z0 + zd * ti]
And the surface normal is:
SN = [(xi - xc)/Sr, (yi - yc)/Sr, (zi - zc)/Sr]
Boiling it all down:
So, since we're talking unit values, and rays that point straight at Z (no x or y component), we can boil down these equations greatly:
Ray:
X0 = 2 * pixelX / width
Y0 = 2 * pixelY / height
Z0 = 0
Xd = 0
Yd = 0
Zd = 1
Sphere:
Xc = 1
Yc = 1
Zc = 1
Factors:
A = 1 (unit ray)
B
= 2 * (0 + 0 + (0 - 1))
= -2 (no x/y component)
C
= (X0 - 1) ^ 2 + (Y0 - 1) ^ 2 + (0 - 1) ^ 2 - 1
= (X0 - 1) ^ 2 + (Y0 - 1) ^ 2
Discriminant
= (-2) ^ 2 - 4 * 1 * C
= 4 - 4 * C
From here:
If discriminant < 0:
Z = ?, Normal = ?
Else:
t = (2 + (discriminant) ^ 1 / 2) / 2
If t < 0 (hopefully never or always the case)
t = -t
Then:
Z: t
Nx: Xi - 1
Ny: Yi - 1
Nz: t - 1
Boiled farther still:
Intuitively it looks like C (X^2 + Y^2) and the square-root are the most prominent figures here. If I had a better recollection of my math (in particular, transformations on exponents of sums), then I'd bet I could derive this down to what Tom Zych gave you. Since I can't, I'll just leave it as above.

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.

Resources