c++ - Vector2D math not giving me the right results - c++

I'm trying to create a vector2D class for my game but I think I'm getting the math wrong.
When I create a new vector2d object it automatically sets its x and y to 1, 1 in the constructor.
Vector2D vec;
std::cout << " x: " << vec.GetX() << " y: " << vec.GetY() << " angle rad: " << vec.GetAngleRad() << " magnitude: " << vec.GetMagnitude() << std::endl;
system("pause");
return 0;
and it outputs:
x: 1
y: 1
angle in rad: 0.785398
magnitude: 1.41421
(which is exactly what i expect)
but the problem is when I parse anything to the setAngle funciton, I get some wired results.
For example:
Vector2D vec;
vec.SetAngleRad(3);
std::cout << " x: " << vec.GetX() << " y: " << vec.GetY() << " angle rad: " << vec.GetAngleRad() << " magnitude: " << vec.GetMagnitude() << std::endl;
system("pause");
return 0;
I would expect it to output angle in rad: 3
but instead I get
angle in rad: 0.141593.
This is the vector2D class (I've tried to comment my code so you can see my what I was thinking when I wrote it):
#include "Vector2D.h"
Vector2D::Vector2D():
_x(1.0f),
_y(1.0f)
{
}
Vector2D::~Vector2D()
{
}
void Vector2D::SetX(float x)
{
_x = x;
}
float Vector2D::GetX()
{
return _x;
}
void Vector2D::SetY(float y)
{
_y = y;
}
float Vector2D::GetY()
{
return _y;
}
void Vector2D::SetAngleRad(float angle)
{
float hypotenuse = GetMagnitude();
SetX( cos(angle) * hypotenuse); // cos of angle = x / hypotenuse
// so x = cos of angle * hypotenuse
SetY( sin(angle) * hypotenuse); //sin of angle = y / hypotenuse
// so y = sin of angle * hypotenuse
}
float Vector2D::GetAngleRad()
{
float hypotenuse = GetMagnitude();
return asin( _y / hypotenuse ); // if sin of angle A = y / hypotenuse
// then asin of y / hypotenuse = angle
}
void Vector2D::SetMagnitude(float magnitude)
{
float angle = GetAngleRad();
float hypotenuse = GetMagnitude();
SetX( (cos(angle) * hypotenuse) * magnitude ); // cos of angle = x / hypotenuse
// so cos of angle * hypotenuse = x
// multiplied by the new magnitude
SetY( (sin(angle) * hypotenuse) * magnitude); //sin of angle = y / hypotenuse
// so sin of angle * hypotenuse = y
// multipied by the new magnitude
}
float Vector2D::GetMagnitude()
{
return sqrt( (_x * _x) + (_y * _y) ); // a^2 + b^2 = c^2
//so c = sqrt( a^2 + b^2 )
}
So I'd really appreciate it if someone could explain to me what I'm doing wrong here :)

To get angle in full circle range, you have to use both y and x components with atan2 function
return atan2( _y, _x );
Note result range -Pi..Pi and correct negative one by +2*Pi if you need range 0..2*Pi
Another issue: :SetMagnitude method really multiplies current magnitude by magnitude multiplier, while name assumes that method should set it (so vector length 2 after applying SetMagnitude(2) will have magnitude 4)).
So it would better to remove *hypotenuse multiplication (or change method name)

Related

Getting wrong values when rotating vector around axis [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm trying to add rotation functions to my class, to rotate around the X, Y, Z - axis, but the output is not exactly what i expected
I made sure that my formulas are correct, they seem to be correct, but i don't know. i took them from this : Rotating a Vector in 3D Space
#include <iostream>
using namespace std;
#include <math.h>
// Vector class, to handle all the vector operations for us
// Thanks to : https://stackoverflow.com/questions/14607640/rotating-a-vector-in-3d-space
class cVector
{
public:
float x;
float y;
float z;
// Constructor
cVector();
cVector(float x1, float y1, float z1);
// returns the vector's magnitude
float Magnitude();
// Normalize ( change length to 1, while keeping the same direction)
void Normalize();
// Rotate around the Axis
void RotateX(float angle);
void RotateY(float angle);
void RotateZ(float angle);
// TODO : Add operators for Addition & Substraction
// Addition
cVector operator+(cVector const& v1) const
{
return cVector(x + v1.x,
y + v1.y,
z + v1.z);
}
void operator+=(cVector const& v1)
{
x += v1.x;
y += v1.y;
z += v1.z;
}
// Substraction
cVector operator-(cVector const& v1) const
{
return cVector(x - v1.x,
y - v1.y,
z - v1.z);
}
void operator-=(cVector const& v1)
{
x -= v1.x;
y -= v1.y;
z -= v1.z;
}
// Multiplication
void operator*=(const float scalar)
{
x *= scalar;
y *= scalar;
z *= scalar;
}
cVector operator*(const float scalar) const
{
return cVector(x * scalar,
y * scalar,
z * scalar);
}
// Division
void operator/=(const float scalar)
{
x /= scalar;
y /= scalar;
z /= scalar;
}
cVector operator/(const float scalar) const
{
return cVector(x / scalar,
y / scalar,
z / scalar);
}
};
// Constructor
cVector::cVector()
{
}
cVector::cVector(float x1, float y1, float z1)
{
x = x1;
y = y1;
z = z1;
}
// returns the vector's magnitude
float cVector::Magnitude()
{
return sqrt((x * x) + (y * y) + (z * z));
}
// Normalize ( change length to 1, while keeping the same direction)
void cVector::Normalize()
{
float flMagnitude = Magnitude();
// We devide the coordinates by the magnitude
x /= flMagnitude;
y /= flMagnitude;
z /= flMagnitude;
}
// Rotate around the Axis
void cVector::RotateX(float angle)
{
y = y * cos(angle) - z * sin(angle);
z = y * sin(angle) + z * cos(angle);
}
void cVector::RotateY(float angle)
{
x = (x * cos(angle)) + (z * sin(angle));
z = (-x * sin(angle)) + (z * cos(angle));
}
void cVector::RotateZ(float angle)
{
x = x * cos(angle) - y * sin(angle);
y = x * sin(angle) + y * cos(angle);
}
void PrintVector(cVector vec)
{
cout << "X : " << vec.x << " Y : " << vec.y << " Z : " << vec.z << endl;
}
// TODO : Add operators for Addition & Substraction
int main()
{
cout << "Hello world!" << endl;
cVector vec(10, 0, 0);
vec.RotateZ(1.57f);
PrintVector(vec);
cin.get();
return 0;
}
I expect the method to keep the same magnitude of the vector, and return ( 0, 10, 0) since i'm rotating by pi/2 , but that's not what i'm getting. apparently if i rotate by pi, i get a good result, but other than that, it doesn't work.
First in your Rotation for example in RotateZ you should save the x in some temporary because if you modify it & then try to use it for the y it's obviously gonna cause you an error, ie you should do something like this
void cVector::RotateZ(float angle)
{
float temp = x;
x = x * cos(angle) - y * sin(angle);
y = temp * sin(angle) + y * cos(angle);
}
Second the value of pi you are given is way too over-rounded so the values are false
you can do something like for you pi value
const float Pi = 3.1415926535;

Dividing circle into n equal parts to get coordinates of each dividing point

I have read this and I tried to implement it in C++, but the output is quite different. I have no idea what is wrong.
The code I used:
double cordinate_print()
{
int x, y;
int number_of_chunks = 5;
double angle=0;
double x_p[5] ; // number of chunks
double y_p[5]; // number of chunks
//double x0, y0 = radious;
double rad = 150;
for (int i = 0; i < number_of_chunks; i++)
{
angle = i * (360 / number_of_chunks);
float degree = (angle * 180 / M_PI);
x_p[i] = 0 + rad * cos(degree);
y_p[i] = 0 + rad * sin(degree);
//printf("x-> %d y-> %d \n", x_p[i], y_p[i]);
cout << "x -> " << x_p[i] << "y -> " << y_p[i] << "\n";
}
//printing x and y values
printf("\n \n");
return 0;
}
Output
x -> 150 y -> 0
x -> -139.034 y -> -56.2983
x -> 107.74 y -> 104.365
x -> -60.559 y -> -137.232
x -> 4.77208 y -> 149.924
The correct output
(150,0)
(46,142)
(-121,88)
(-121,-88)
(46,-142)
Issue with the conversion of degree into radian
float degree = (angle * 180 / M_PI);
The correct conversion formula is
float radian = (angle * M_PI / 180);
Also as mentioned in the comment use the good name to avoid any confusion.
Since your default angles are in degrees, you need to convert them to radians first before using sin() and cos(), then multiplying it to the radius.
double cordinate_print()
{
int number_of_chunks = 5;
double degrees = 0; // <-- correction
double x_p[5]; // number of chunks
double y_p[5]; // number of chunks
double radius = 150; // <-- correction
for (int i = 0; i < number_of_chunks; i++)
{
degrees = i * (360 / number_of_chunks); // <-- correction
float radian = (degrees * (M_PI / 180)); // <-- correction
x_p[i] = radius * cos(radian); // <-- correction
y_p[i] = radius * sin(radian); // <-- correction
cout << "x -> " << x_p[i] << "y -> " << y_p[i] << "\n";
}
//printing x and y values
printf("\n \n");
return 0;
}

Projectile not going downwards on negative angle

I'm building a small Physics engine that fires a projectile on a set of launch parameters by the user (angle, height, time interval and initial velocity), then displays some information such as the total distance or angle at every time interval that it's in the air:
bool heightCheck = false;
double theta;
double initialVelocity, velocity;
double yNew = 0.0, xNew, xOld = 0.0, yOld = 0.0;
const double time = 0.1;
const double gravitiyHalf = 9.8 / 2;
double velocityX = 0.0, velocityY = 0.0;
double angle = 0.0;
double totalT = 0;
double maxHeight = 0.0;
double thetaDegrees = 0;
#define PI 3.14159265l // constant for PI
cout << "Insert a lanuch Angle (theta): ";
cin >> thetaDegrees;
cout << "Insert a launch height: ";
cin >> yOld;
cout << "Insert an initial velocity: ";
cin >> initialVelocity;
cout << "Time (DeltaT) in seconds: ";
cin >> totalT;
for (double deltaTime = 0.0; deltaTime < totalT; deltaTime += 0.1) {
const double squared = deltaTime * deltaTime; // squared constant for deltaTime squared
theta = thetaDegrees * PI / 180; // converts theta to a degrees value
velocityX = initialVelocity * cos(theta); // calculates Vx
velocityY = initialVelocity * sin(theta); // calculates Vy
// apply initialV to velocity
velocity = initialVelocity + 9.8 * time;
xNew = xOld + velocity * time; // works out displacement for X
yNew = yOld + velocity * deltaTime - gravitiyHalf / 0.5 * (squared); // calculates Y
velocityY = velocity - 9.8 * deltaTime; // includes gravity to Y
angle = atan2(yNew, xNew) * 180 / PI; // convert angle to degrees
cout << "\nHeight: " << yNew << endl;
cout << "Distance in Meters: " << xNew << "m" << endl;
cout << "Angle: " << angle << endl;
cout << "Time: " << deltaTime << "s " << endl;
if (heightCheck == false) {
maxHeight = yOld;
// keep maxheight equal to previous height
}
if (yNew < yOld && heightCheck == false) {
heightCheck = true;
// if projectile is going down, trigger maxheight
}
cout << "Maximum height: " << maxHeight << endl;
if ((yNew < 0) || (deltaTime == totalT)) {
getchar(); // stops if delta t = total T or projectile landed
}
yOld = yNew; // refresh x & y
xOld = xNew;
}
If I enter the following values at the start of my program:
theteDegrees = 45
yOld = 0
initialVelocity = 20
totalT = 10
My program displays the expected results that show my projectile going up, then down. However, if I enter the same values expect -40 for thetaDegrees, my projectile should head straight down, instead it just goes up and then down again.
Where have I gone wrong in my code?
As Igor said, during distance calculation over X and Y you've not taken velocityX and velocityY into consideration.
xNew = xOld + velocity * time; // works out displacement for X
yNew = yOld + velocity * deltaTime - gravitiyHalf / 0.5 * (squared); // calculates Y
There is some redundant calculations involved. A more simplistic version could be like this.
theta = thetaDegrees * PI / 180; // converts theta to a degrees value
velocityX = initialVelocity * cos(theta); // calculates Vx
velocityY = initialVelocity * sin(theta); // calculates Vy
//cout<<velocityX<<endl<<velocityY<<endl;
for (double deltaTime = 0.0; deltaTime < totalT; deltaTime += 0.1) {
const double squared = deltaTime * deltaTime; // squared constant for deltaTime squared
xNew = xOld + velocityX * time; // works out displacement for X
yNew = yOld + velocityY * deltaTime - gravitiyHalf / 0.5 * (squared); // calculates Y
velocityY = velocityY - 9.8 * deltaTime; // includes gravity to Y
angle = atan2(yNew, xNew) * 180 / PI; // convert angle to degrees
cout << "\nHeight: " << yNew << endl;
cout << "Distance in Meters: " << xNew << "m" << endl;
cout << "Angle: " << angle << endl;
cout << "Time: " << deltaTime << "s " << endl;
if (heightCheck == false) {
maxHeight = yOld;
// keep maxheight equal to previous height
}
if (yNew < yOld && heightCheck == false) {
heightCheck = true;
// if projectile is going down, trigger maxheight
}
cout << "Maximum height: " << maxHeight << endl;
if ((yNew < 0) || (deltaTime == totalT)) {
getchar(); // stops if delta t = total T or projectile landed
}
yOld = yNew; // refresh x & y
xOld = xNew;
}
You either have to change
xNew = xOld + velocityX * time; // works out displacement for X
to
xNew = xOld + velocityX * deltaTime; // works out displacement for X
or remove
xOld = xNew;
at the end, as leaving both unchanged would give you a quadratically speeding x coordinate, not a linear motion of constant velocity.

Issue inverting 2D matrix

I am working on a function in a class to store images. The purpose of this function is the perform linear transformations using a 2D matrix (I don't plan on supporting translation). The way I wanted to do it was to test transform the coordinates of the corners of the original image and use the extreme values to calculate the necessary size for the resulting transformed image.
One I have the new image I wanted to loop over all the pixels in the new image and use the inverse transform the calculate where on the original image the new pixels should get their color from (using bilinear interpolation).
I started writing the following function. It does not yet do anything to the image. The issue is that when I pass a set of coordinates through the transform function and then pass those new coordinates through the inverse function I expect to get the original values back. However I am not getting back the original values.
Image Image::matrix_transform(float m11, float m12, float m21, float m22) const
{
float det = m11 * m22 - m12 * m21;
img_assert(det, "Matrix not invertible.");
//inverse matrix values
float im11 = m22 / det;
float im22 = m11 / det;
float im21 = -m21 / det;
float im12 = -m12 / det;
//transformation
const auto t = [m11, m12, m21, m22](float& x, float& y)
{
x = x * m11 + y * m12;
y = x * m21 + y * m22;
};
//inverse
const auto ti = [im11, im12, im21, im22](float& x, float& y)
{
x = x * im11 + y * im12;
y = x * im21 + y * im22;
};
float x00 = 0.0f, y00 = 0.0f;
float x11 = w, y11 = h;
float x10 = 0.0f, y10 = h;
float x01 = w, y01 = 0.0f;
std::cout << x00 << " " << y00 << std::endl;
std::cout << x01 << " " << y01 << std::endl;
std::cout << x10 << " " << y10 << std::endl;
std::cout << x11 << " " << y11 << std::endl;
t(x00, y00);
t(x11, y11);
t(x10, y10);
t(x01, y01);
ti(x00, y00);
ti(x11, y11);
ti(x10, y10);
ti(x01, y01);
std::cout << x00 << " " << y00 << std::endl;
std::cout << x01 << " " << y01 << std::endl;
std::cout << x10 << " " << y10 << std::endl;
std::cout << x11 << " " << y11 << std::endl;
return *this;
}
Here is how I am calling the function (a simple rotation matrix).
img.matrix_transform(cos(angle), -sin(angle), sin(angle), cos(angle));
Here is the output I get...
0 0
2500 0
0 1655
2500 1655
0 0
1975.95 -722.386
-743.978 629.455
1231.97 -92.9314
I was expecting the top set of coordinates to match the bottom.
Here
x = x * m11 + y * m12;
y = x * m21 + y * m22;
the second assignment uses the already modified x value. Change it to
auto newx = x * m11 + y * m12;
y = x * m21 + y * m22;
x = newx;

Drawing polygons using random points?

I am trying to generate a set of points that I will connect to make polygon. The data has to be generated in a systematic way.
I am trying to generate the point set by randomly deriving radial coordinate r and evenly incrementing angular coordinate theta such that all the points are linked orderly without crossing with each other. I followed the correct formulas and I increment the angle but the data comes out negative because of sin and cos. I wanted to know if I'm doing this correctly.
struct Point2D {
int x;
int y;
};
Point2D poly[10];
int N = 80;
int x = (rand() % N + 1) * 5;
int y = (rand() % N + 1) * 5;
int r = sqrt(x*x + y*y);
int theta = int(atan ((float)y/(float)x) * 180 / PI);
cout << "x " << x << " y " << y << " r " << r << " theta " << theta << endl;
for (int i = 0; i < 10; i++) {
Point2D p;
p.x = x;
p.y = y;
poly[i] = p;
theta += 20;
x = r * sin(theta);
y = r * cos(theta);
cout << "x " << x << " y " << y << endl;
}
sin and cos return points on a unit circle centered around (0, 0), as paddy pointed out. To have no negative values in the points on your own polygon, you'll need to shift the origin of that circle. You're already changing its size, with r * sin(theta); you can accomplish a minimum translation with:
x = r * cos(theta) + r;
y = r * cos(theta) + r;
When I make this change to your program, I don't get negative values anymore.
Having said that, I suspect that you're not incrementing theta the way you intend. If you're trying to divide the circle into 10 equal angles, then theta should be a float or double and incremented like this:
theta += (2 * M_PI / 10);
theta is in radians, so 2 * M_PI is once around the unit circle.