Shortest way to turn towards a target? - xna

Ive created a small tank-shooter-minigame, and is currently working on cpu control of other tanks.
A cpu tank knows the following:
Its own facing, the direction its facing
Its own position (X and Y)
Its targets position (X and Y)
On top of that ive made it so that positive y is downwards instead of upwards. positive x direction is left to right.
How do i calculate the shortest way the cpu tank need to rotate to point on its target?
Lets say tank is at (3,3), facing 90 degrees (straight left) and Im at (4,7). What would the calucations be?

I'm not sure there isn't an easier way to do this, I tend to revert to 3d vector math often, even if in 2d trigonometric solutions are better suited. Your question implies you are working in 2d, but the 3d math will still work. Hopefully, someone will come up with an easier algorithm for you, but if not, here is something.
I assume you mean the shortest way in an angular measurement. In this code, the result will be a positive angular value if the shortest way is CCW, negative if CW.
Vector2 targetPosition = new Vector2(3, 3);
Vector2 myPosition = new Vector2(4, 7);
Vector2 myFacingDirection2D = new Vector2(-1, 0);//facing straight left
Vector3 directionToTarget3D = Vector3.Normalize(new Vector3(targetPosition - myPosition, 0));
Vector3 myFacingDirection3D = Vector3.Normalize(new Vector3(myFacingDir2D, 0);
Vector3 crossResult = Vector3.Cross(myFacingDirection3D, directionToTarget3d);
float dotFactor = Vector3.Dot(myFacingDirection3D, directionToTarget3D) < 0 ? MathHelper.Pi : 0f;
float angleToTarget = (dotFactor - (float)Math.Sin(crossResult.Length()) ) * Math.Sign(crossResult.Z);
If this turns out backwards (positive/negative wise) then reverse the order of the params in the Cross function.

Related

opencv solvePnP, all axes look great except Y

I am running solvePnPRansac on an image dataset, with 2d feature points and triangulated 3d landmark points. It runs great, and the results in rotation, and in the forward and side axes, look great. The Y axis though, is completely wrong.
I am testing the output against the ground truth from the data set, and it goes up where it should go down, and drifts off the ground truth very quickly. The other axes stay locked on for much much longer.
this strikes me as strange, how can it be correct for the other axes, and wrong for one? Surely that is not possible, I would have thought that either every axis was bad, or every axis was good.
What could i possibly be doing wrong to make this happen? And how can i debug this weirdness? My PnP code is very standard:
cv::Mat inliers;
cv::Mat rvec = cv::Mat::zeros(3, 1, CV_64FC1);
int iterationsCount = 500; // number of Ransac iterations.
float reprojectionError = 2.0; //2.0 // maximum allowed distance to consider it an inlier.
float confidence = 0.95; // RANSAC successful confidence.
bool useExtrinsicGuess = false;
int flags = cv::SOLVEPNP_ITERATIVE;
int num_inliers_;
//points3D_t0
cv::solvePnPRansac(points3D_t0, points_left_t1, intrinsic_matrix, distCoeffs, rvec, translation_stereo,
useExtrinsicGuess, iterationsCount, reprojectionError, confidence,
inliers, flags);
I encountered similar problem for images taking by a drone – sometimes the Y value (camera line of sight axis – the height axis in my case) was BELOW the ground. If you think about it – for a plane view (or close to a plane) - there are two possible ‘y’ solutions : before the plane and away to the plane (below and under the ground in my case). So both are a legal solutions.

Fisheye correction formula, need explanation

After extensive search, I have not been able to find a decently explained formula for WebGL fisheye image correction. A Shadertoy at fisheye/antifisheye shows a formula
uv = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
which is literally described as "weird formula". It somehow follows Paul Burke's work on lens distortion correction here, but I do not see the connection. In my application, the formula boils down to (values are hand-tuned to my lens and webcam):
uv = centerPoint + normalize(d) * atan(r * pi) * 1/3 / atan(pi/3)
Where r is the distance of a pixel from center of the image, d is the unit vector in that direction and centerPoint is, well, the center of the image. I don't understand how can the arctangent be tied directly to the coordinates, can anyone help me get it? I do get that the part of the formula with arc tangents is calculation of pixel distance from the image center, what I do not understand is how is that computed.
Thanks!
Ok, so after some searching and asking around, I understood what happens in the formula. A diagram and basic formulas are on the image below: ]1. Since the xy' position is known - it's the target position on the texture, we calculate xy - source pixel on the fisheye image. R is the fisheye image radius. We can substitute a desired value of d depending on maximum view angle we want to achieve (since tangent function goes to infinity at 90 deg and tan(B) = 1/d, it's nice to take a reasonable value).
After transformations, we get to:
xy= atan(xy'/d)* 2R/pi
which is the theoretically correct formula for equidistant projection, which we assume is performed in the lens. The formula I referenced in the original post had something else instead of 2R/pi and it still worked because of imperfections in the lens - it most probably has some strange function we'll never know and it worked as an approximation.
There, I hope it was understandable, in case of any questions, I'll be happy to answer them :)

Mapping a vector across the y-axis if it has 4 components

I am applying a force and a torque on an node. This is my code:
myNode?.physicsBody?.applyForce(SCNVector3Make(0, -6, 4), atPosition: SCNVector3Make(0, 1, -1), impulse: true)
myNode?.physicsBody?.applyForce(SCNVector3Make(0, -2, 10), impulse: true)
myNode?.physicsBody?.applyTorque(SCNVector4Make(4, 2, 2.5, 1.6), impulse: true)
The object now falls down and moves from left to right afterwards. I want it fall down and move from right to the left(basically a reflection of the first movement across y-axis). I figured it out that there is very little I can do about the first 2 lines of code, because the force has no x-component. The last line, applyTorque, is the one I need to manipulate. How do you map across the y-axis if the vector has 4 components? I am a little rusty with math
The fuller version of the applyTorque function looks something like this:
.applyTorque(SCNVector4Make(x:, y:, z:, w:), impulse:)
So any numbers you put in the second position should be torque amounts around the y axis.
There's probably a relationship between the numbers and what they create in terms of rotational force on an object, but I've always just used trial-and-error to find what works. Sometimes it's HUGE numbers.
I am assuming that the x-axis is horizontal and the y-axis is vertical and the z-axis points straight at you (see the black arrows below):
I found evidence that this is indeed the case in SceneKit.
If
applyTorque(SCNVector4Make(x, y, z, w), impulse: boolean)
is the correct usage, then x is the amount of counter-clockwise rotation around the x-axis (see green circle arrow), and similarly for y and z. Again, this is my best guess, and it is possible that SceneKit uses clockwise rotation. Therefore, x, y, and z together determine the axis of rotation of the torsional force.
Here is a simpler way to think of it. x, y, and z create a vector in the 3D space described above. The object will rotate counter-clockwise around this vector.
w on the other hand, is the magnitude of the torque, and has nothing to do with the axis of rotation.
Your request to "map vector across the y-axis" is actually a reflection across the yz-plane. If you think about it, what you want is to rotate the opposite direction around the y-axis (negate y) and the same for z.
So the answer should be:
myNode?.physicsBody?.applyTorque(SCNVector4Make(4, -2, -2.5, 1.6), impulse: true)
According to the SceneKit documentation the SCNVector4 argument specifies the direction (x, y, z vector components) and magnitude (w vector component) of the force in newton-meters. To mirror the direction of the applied torque, all you have to do is invert the magnitude. (x, y, z, -magnitude)

using only Quaternions, How do I determine if my phone is in a "Dumping" position

A simple question really but I haven't come across it anywhere. I have an app that needs to allow the user to "dump" the contents. Basically this means any time the top of the phone is lower than the bottom of the phone (not upside down). I can grab the quaternion rotation of the phone off the gyroscope, but how do I know if the top of the phone is lower than the bottom
This is a very interesting question.
I'm not completely sure of how to do this, but I think that if you rotate the 'up vector' (negative gravity direction) by the quaternion then check its z component sign, this may give you an indication.
Psuedocode:
g = [0 0 -1] // the direction of gravity (unit vector)
G = [0 g] // this is a pure quaternion
V = q* G q // Using quaternion multiplication
v = V[1:4] // extract last 3 components from pure Quat
if (v[2] < 0.00) // Check the sign of the z component
isFaceDown = true
I think this will work, although I suspect the more proper way is to map the quaternion to the surface of a sphere. If you cant get an answer you should ask on the mathematics stackexchange.

Changing angle when ball hits paddle

I'd like to deflect a ball at an angle depending on where it hits a paddle. Right now, I'm only changing the y coordinate, which results in an uninteresting deflection. It will angle but independent on impact location against the paddle. I'd like something more fun. Speed, momentum, mass and other factors don't need to be taken into consideration. Just angle depending on impact location of paddle. I've read this Not a number error (NAN) doing collision detection in an iphone app but it seems overly complicated for what I'm looking for. Is there a simpler way to calculate the deflection?
The objects are two UIImageViews.
Well, nothing realistic but you could do something so that the outbound angle is only dependent on where on the paddle it hits.
I have never done any iPhone or objective C coding so I'll just write up something in pseudo/C code.
First I'd calculate the speed, which is the length of the speed vector, or:
double speed = sqrt(velX * velX + velY * velY); // trigonometry, a^2 + o^2 = h^2
Then we want to calculate the new angle based on where we hit the paddle. I'm going to assume that you store the X collision in impactX and the length of the paddle in paddleLength. That way we can calculate an outbound angle. First let's figure out how to calculate the range so that we get a value between -1 and 1.
double proportionOfPaddle = impactX / (double) paddleLength; // between 0 and 1
double impactRange = proportionOfPaddle * 2 - 1; // adjust to -1 and 1
Let's assume that we do not want to deflect the ball completely to the side, or 90 degrees, since that would be pretty hard to recover from. Since I'm going to use the impactRange as the new velY, I'm going to scale it down to say -0.9 to 0.9.
impactRange = impactRange * 0.9;
Now we need to calculate the velX so that the speed is constant.
double newVelX = impactRange;
double newVelY = sqrt(speed * speed - newVelX * newVelX); // trigonometry again
Now you return the newVelX and newVelY and you have an impact and speed dependent bounce.
Good luck!
(Might very well be bugs in here, and I might have inverted the X or Y, but I hope you get the general idea).
EDIT: Adding some thoughts about getting the impactX.
Let's assume you have the ball.center.x and the paddle.center.x (don't know what you call it, but let's assume that paddle.center.x will give us the center of the paddle) we should be able to calculate the impactRange from that.
We also need the ball radius (I'll assume ball.width as the diameter) and the paddle size (paddle.width?).
int ballPaddleDiff = paddle.center.x - ball.center.x;
int totalRange = paddle.width + ball.width;
The smallest value for ballPaddleDiff would be when the ball is just touching the side of the paddle. That ballPaddleDiff would then be paddle.width/2 + ball.width/2. So, the new impactRange would therefore be
double impactRange = ballPaddleDiff / (double) totalRange / 2;
You should probably check the impactRange so that it actually is between -1 and 1 so that the ball doesn't shoot off into the stars or something.
You don't necessarily want realistic, you want fun. Those aren't always one and the same. If you wanted realistic, you can't throw out speed, momentum, mass, etc. In a normal game of ping pong, the point where it hits the paddle doesn't really matter, theres not a sweet spot like on a tennis racket.
Develop a mathematical function that will return an output vector, or a velocity and a unit vector, representing the output angle and velocity of the ball, givin an input angle, velocity, impact point on the paddle, and velocity of the paddle.
We expect already that the output angle = -1 * input angle. Output velocity also would be expected to be -1 * the input velocity. So if you want to mix it up, adjust those. You could increase the output angle proportional to the distance from the center of the paddle. You could also increase the angle or the speed proportional to the velocity of the paddle when its hit.
There's a lot of ways you could do that, so I can't really tell you exactly what function you would use, you're going to have to figure that out with testing and playing. If you still need more info add more specifics to your question.
The following code (C++ but easy enough to convert to ObjC), takes an incoming 2D vector and reflects it based on a surface normal (the face of your pong bat).
You could add some random 'fun factor' by randomizing an offset that you'd either apply to 'scalar' - to change velocity, or to the surface normal, to alter the reflection angle.
I'm using this in my iPhone project, and it works fine :)
void vec2ReflectScalar(vec2& vResult, const vec2& v1, const vec2& normal, float scalar)
{
vec2 _2ndotvn;
float dotVal = vec2DotProduct(v1, normal);
vec2Scale(_2ndotvn, normal, scalar * 2.f * dotVal);
vec2Subtract(vResult, v1, _2ndotvn);
}
void vec2Reflect(vec2& vResult, const vec2& v1, const vec2& normal)
{
vec2ReflectScalar(vResult, v1, normal, 1.f);
}

Resources