Calculating velocity from speed and direction - c++

I have an object which wants to move from point A to point B, at a certain speed (using SFML C++).
sf::Vector2f aPos; //sf::Vector2f has two members x and y, stored as floats
sf::Vector2f bPos;
float speed; //pixels per tick
I want to use these variables to find out a velocity, with an x speed and a y speed, such that the hypotenuse of vel.x and vel.y has length speed, and has the same bearing as that from point aPos to point bPos.
sf::Vector2f vel;
What is the simplest and most effective function that I can write, which takes sf::Vector2f aPos, sf::Vector2f bPos and float speed as arguments, and returns sf::Vector2f vel
sf::Vector2f findVel(sf::Vector2f aPos, sf::Vector2f bPos, float speed) {
sf::Vector2f vel;
//the code I need
return vel;
}
Thank you in advance! :)

Taking k_g's answer, we can actually shorten it quite a bit given that Vector2f has operator overloads. It can simply become:
sf::Vector2f findVel(const sf::Vector2f& aPos, const sf::Vector2f& bPos, float speed) {
sf::Vector2f disp = bPos-aPos;
float distance = sqrt(disp.x*disp.x+disp.y*disp.y); // std::hypot(disp.x, disp.y) if C++ 11
return disp * (speed/distance);
}
This also allows us to benefit from any optimizations that might have been built into the Vector class for operations.
Note the change to const & for the input parameters; no need to make a copy of them since you aren't manipulating them.

well, we know that speed == distance/time and velocity == displacement/time.
Solving for 1/time in the first equation, we get 1/time==speed/distance. We substitute this into the second equation to get velocity == displacement*speed/distance.
Using this formula, we get the following function
sf::Vector2f findVel(sf::Vector2f aPos, sf::Vector2f bPos, float speed) {
sf::Vector2f vel;
float dispx = bPos.x-aPos.x;
float dispy = bPos.y-aPos.y;
float distance = sqrt(dispx*dispx+dispy*dispy);
vel.x = dispx*speed/distance;
vel.y = dispy*speed/distance;
return vel;
}

Related

2d Elastic Collision with Circles

I've seen there is a lot of posts about this already but I can't find one that relates to what I want to do,
I used the formula from here:
https://www.vobarian.com/collisions/2dcollisions2.pdf
As well as this one:
https://www.plasmaphysics.org.uk/programs/coll2d_cpp.htm
I think they area basically the same thing, now my problem is one of my circles is always static, and what I want is when the other circle hits it straight on, I want it to bounce back with the same speed, but these formulas have the circle stop still, presumably as it would pass it's energy to the other circle which would then move away.
I tried doing things like bounce = vel.x pre collision - vel.y post collision and add or subtract that to vel.x post collision and it kinda works but not really, the angles are wrong and depending on which direction the ball is coming from it may bounce up instead of down, left instead of right,
would probably require a lot of if/else statements to get to work at all.
Can someone suggest something?
here's the code for the function :
void Collision2(sf::CircleShape* b1, sf::CircleShape* b2, sf::Vector2f vel1,sf::Vector2f& vel2) {
//vel1 is 0,0 but i might want to use it later
//mass
float m1 = 10;
float m2 = 10;
//normal vector
sf::Vector2f nVec((b2->getPosition().x - b1->getPosition().x), (b2->getPosition().y - b1->getPosition().y));
//unit vector
sf::Vector2f uNVec(nVec / sqrt((nVec.x * nVec.x) + (nVec.y * nVec.y)));
//unit tangent vec
sf::Vector2f uTVec(-uNVec.y, uNVec.x);
float v1n = (uNVec.x * vel1.x) + (uNVec.y * vel1.y);
float v2n = (uNVec.x * vel2.x) + (uNVec.y * vel2.y);
float v1t = uTVec.x * vel1.x + uTVec.y * vel2.y;
float v2t = (uTVec.x * vel2.x) + (uTVec.y * vel2.y);
//v1t and v1n after collision
float v1tN = v1t;
float v2tN = v2t;
float v1nN = (v1n * (m1 - m2) + (2 * m2) * v2n) / (m1 + m2);
float v2nN = (v2n * (m2 - m1) + (2 * m1) * v1n) / (m1 + m2);
//new velocities
sf::Vector2f vel1N(v1nN*uNVec);
sf::Vector2f vel1tN(v1tN * uTVec);
sf::Vector2f vel2N(v2nN * uNVec);
sf::Vector2f vel2tN(v2tN * uTVec);
vel1 = (vel1N + vel1tN);
vel2 = (vel2N + vel2tN);
}
Physics part
The sources you added illustrate the physics behind it very well. when the two balls collide they transfer momentum between them. In an elastic collision this transfer keeps the energy of the system, the same.
We can think of the collision in terms of inertia and momentum, rather than starting from velocity. The kinetic energy of a body is generally p^2/(2m), so if we transfer dp from the moving body then we will have change in energy: dE = -pdp/m + dp^2/(2m) + dp^2/(2M) = 0. Here m is the moving and M is the stationary mass. Rearranging gives pdp/m = dp^2*(1/(2m) + 1/(2M)). We can consider m = M yielding p = dp (i.e. All moment is transferred (Note: this is a simplistic view, only dealing with head on collisions)). In the limit where the stationary object is massive however (M >> m) the result will be dp = 2p, simply bouncing off.
Programming
You can achieve the results by setting M to the maximum allowed float value (if I recall 1/inf == NaN in the IEEE standard so that doesn't work unfortunately). Alternatively you can do the collision within the circle by creating custom classes like:
class Circle : public sf::CircleShape{
public:
virtual collide (Circle*);
}
class StaticCircle : public Circle{
public:
collide (Circle*) override;
}
in the second one you can omit any terms where you divide by the mass of the circle, as it is in essence infinite.

Accelerate to X velocity in Y seconds

How can I get something to go from X to Xgoal velocity in Y seconds?
The goalVelocity is set to (100, 100) and it does approach it but it takes way too long to get there.
I can multiply frameDelta by some number like 20 or 100 but I want to find out what to multiply frameDelta by to get it to reach goalVelocity in some number of seconds.
velocity, goalVelocity and origin are all Vec2f and frameDelta is a float
Right now I have this code:
velocity = approach(goalVelocity, velocity, frameDelta);
origin = origin + velocity * frameDelta;
The code for approach is:
inline float approach(float flGoal, float flCurrent, float dt)
{
float flDifference = flGoal - flCurrent;
if (flDifference > dt)
return flCurrent + dt;
if (flDifference < -dt)
return flCurrent - dt;
return flGoal;
}
inline Vec2f approach(Vec2f flGoal, Vec2f flCurrent, float dt)
{
return Vec2f(approach(flGoal.x, flCurrent.x, dt), approach(flGoal.y, flCurrent.y, dt));
}
Maybe I misunderstood your Question, but acceleration is just speed difference divided by time, so just multiply dt with X/Y.
A basic physics engine should look something like
Vec2f position, velocity, acceleration;
while (true)
{
acceleration = button ? thrust : 0;
velocity += acceleration * timeDelta;
position += velocity * timeDelta
redraw space ship at position;
sleep (timeDelta);
}
if you want to go from 0 to X velocity in Y seconds, then thrust = X/Y.

Calculate bounce angle aggainst rotated line

I have been trying to implement The second answer of this question
My variables are mainly named the same as in the link.
Here they calculate the bounce angle of a point on a rotated surface with normals,
But I can't seem to do it, am I doing something wrong?
The value output I'm expecting is the new velocity after bouncing on the rotated line, currently my output is doing all crazy kind of things, bouncing to high, bouncing the other way and sometimes the right way but mostly ignoring the rotated angle of my line.
Here's my current Code:
Variables:
Top is the first point of my line
Right is the second point of my line
n is the normal
v is the velocity
dotv is n * v
dotn is n * n
Old Code:
sf::Vector2f n(-(top.y - right.y),(top.x - right.x));
sf::Vector2f dotv = sf::Vector2f(ball.getSpeed().x * n.x, ball.getSpeed().y * n.y);
sf::Vector2f dotn = sf::Vector2f(n.x*n.x,n.y*n.y);
sf::Vector2f u = sf::Vector2f(dotv.x/dotn.x,dotv.y/dotn.y);
u = sf::Vector2f(u.x*n.x,u.y*n.y);
sf::Vector2f w = ball.getSpeed() - u;
ball.setSpeed(sf::Vector2f((sf::Vector2f(w.x*0.5,w.y*0.5)-u)));
Edit:
New Code:
sf::Vector2f n(-(top.y - right.y),(top.x - right.x));
double dotv = ball.getSpeed().x*n.x + ball.getSpeed().y*n.y;
double dotn = n.x*n.x + n.y*n.y;
sf::Vector2f u = sf::Vector2f((dotv/dotn)*n.x, (dotv/dotn)*n.y);
sf::Vector2f w = ball.getSpeed() - u;
ball.setSpeed(sf::Vector2f(ball.getSpeed().x,ball.getSpeed().y) - w);
At first I made the mistake of calculating dotproducts as vectors this has been resolved, now it still gives me a strange output It fires my ball directly trough my object in the angle of the reversed normal
Any help would be greatly appreciated.
One problem I see is that you have dot products as vectors. A dot product results in a scalar (single value).
Also, to make your life a lot easier, you make functions for vector operations and even overload operators when appropriate. E.g. vector addition, subtraction. It's probably best to regular functions for dot product and cross product. Here are some examples of what I mean
class Vector2f
{
// Member of Vector2f
X& operator+=(const Vector2f& other)
{
x += other.x;
y += other.y;
return *this;
}
};
// Not member of Vector2f
inline Vector2f operator+(Vector2f a, const Vector2f& b)
{
a += b;
return a;
}
double dot(const Vector2f& a, const Vector2f& b)
{
return a.getX()*b.getX() + a.getY()*b.getY();
}

Wave vector in 2 dimensions

So I'm trying to make the player shoot a bullet that goes towards the mouse in a wavey pattern. I can get the bullet to move in a wavey pattern (albeit not really how I predicted), but not towards the mouse.
Vector2 BulletFun::sine(Vector2 vec) {
float w = (2 * PI) / 1000; // Where 1000 is the period
float waveNum = (2 * PI) / 5; // Where 5 is the wavelength
Vector2 k(0.0F, waveNum);
float t = k.dot(vec) - (w * _time);
float x = 5 * cos(t); // Where 5 is the amplitude
float y = 5 * sin(t);
Vector2 result(x, y);
return result;
}
Right now the speed isn't much of a concern, that shouldn't be too much of a problem once I have this figured out. I do get some angle change, but it seems to be reversed and only 1/8th a circle.
I'm probably miscalculating something somewhere. I just kind of learned about wave vectors.
I've tried a few other things, such as 1 dimensional travelling waves and another thing involving adjusting a normal sine wave by vec. Which had more or less the same result.
Thanks!
EDIT:
vec is the displacement from the player's location to the mouse click location. The return is a new vector that is adjusted to follow a wave pattern, BulletFun::sine is called each time the bullet receives and update.
The setup is something like this:
void Bullet::update() {
_velocity = BulletFun::sine(_displacement);
_location.add(_velocity); // add is a property of Tuple
// which Vector2 and Point2 inherit
}
In pseudocode, what you need to do is the following:
waveVector = Vector2(travelDistance,amplitude*cos(2*PI*frequency*travelDistance/unitDistance);
cosTheta = directionVector.norm().dot(waveVector.norm());
theta = acos(cosTheta);
waveVector.rotate(theta);
waveVector.translate(originPosition);
That should compute the wave vector in a traditional coordinate frame, and then rotate it to the local coordinate frame of the direction vector (where the direction vector is the local x-axis), and then translate the wave vector relative to your desired origin position of the wave beam or whatever...
This will result in a function very similar to
Vector2
BulletFun::sine(Bullet _bullet, float _amplitude, float _frequency, float _unitDistance)
{
float displacement = _bullet.getDisplacement();
float omega = 2.0f * PI * _frequency * _displacement / _unitDistance;
// Compute the wave coordinate on the traditional, untransformed
// Cartesian coordinate frame.
Vector2 wave(_displacement, _amplitude * cos(omega));
// The dot product of two unit vectors is the cosine of the
// angle between them.
float cosTheta = _bullet.getDirection().normalize().dot(wave.normalize());
float theta = acos(cosTheta);
// Translate and rotate the wave coordinate onto
// the direction vector.
wave.translate(_bullet.origin());
wave.rotate(theta);
}

Make Objects Follow Mouse

Other questions close to this topic don't seem to help me understand it very much. I'm just starting programming using Visual Studio and Direct2D and I'm having trouble understanding how to make two "eyes," which are ellipses inside of ellipses, follow my mouse.
Inside of the function void MainWindow::CalculateLayout() I'm using
const float radius3=radius/4;
const float radius3_2=radius/5;
const float x3=x-100;
const float y3=y-150;
ellipse3 = D2D1::Ellipse(D2D1::Point2F(x3, y3), radius3, radius3_2);
//left eye
const float radius4=radius/4;
const float radius4_2=radius/5;
const float x4=x+100;
const float y4=y-150;
ellipse4 = D2D1::Ellipse(D2D1::Point2F(x4, y4), radius4, radius4_2);
//right eye
const float radius5=radius/8;
const float radius5_2=radius5/2;
const float x5=x-100;
const float y5=y-150;
ellipse5 = D2D1::Ellipse(D2D1::Point2F(x5, y5), radius5, radius5_2);
// left eyeball
const float radius6=radius/8;
const float radius6_2=radius6/2;
const float x6=x+100;
const float y6=y-150;
ellipse6 = D2D1::Ellipse(D2D1::Point2F(x6, y6), radius6, radius6_2);
// right eyeball
to set up where the eyes and eyeballs are. I think that something along the line of this should be used to control where the mouse is. I am trying to do this from a blank project, not from a form. Is the solution to simply replace const float x5=x-100 with the X value of MouseMove?
You need to replace the definition of x5, but you need to do it with a formula which will bound it to stay within the eyeball.
Your formula will look something like this:
// compute the angle from the eyes to the mouse
angle = arctan( (mouseY - y) / (mouseX - x) );
// x-100 and y-150 are assumed to be the origins (center) of the eyeball
// eyeballRadius should be the radius of the eyeball, or slightly smaller (so the eyes do not extend outside of it)
x5 = (x-100) + cos(angle) * eyeballRadius;
y5 = (y-150) + sin(angle) * eyeballRadius;
Hope this helps.
To get the cross-eyed effect when the cursor is very near, you should have each eyeball compute its own angle, for example the left's would be leftAngle = arctan( (mouseY - (y-150)) / (mouseX - (x-100)) )