I'm struggling with a problem I thought should be easy to solve:
I'm given two points x1, x2 and a width value. How can I calculate two other points parallel to x1 and x2 so that it forms a rectangle?
I tried answers from here 1 and here 2. Though both solutions are off.
As background: This is about projecting an image into real world coordinates. Therefore I need to find the parallel line to the line I'm provided with, so that the points of both lines create a rectangle. I do not want to apply a rotation on my own.
Here is a drawing that shows what I want to achieve:
In the example you see x1, x2 and the width I'm provided with. And I'm looking for x3 and x4 so that the points form a rectangle.
I'm looking for a C++ implementation if possible.
1 https://gamedev.stackexchange.com/questions/86755/how-to-calculate-corner-positions-marks-of-a-rotated-tilted-rectangle
2 Calculating vertices of a rotated rectangle
Here is the code I've implemented. As you can see I'm using top right and top left coordinates that I'm provided with. But I'd rather find a line parallel to the provided points instead:
double distance = 77.5;//[self normalizedDistanceWithCRS:crs p1:topLeft p2:topRight];
// calculate the rotated coordinates for bottom right and bottom left with provided height
double angle = atan2(sinuTL.y - sinuTR.y, sinuTL.x - sinuTR.x); // * 180 / M_PI
double x = distance;
double y = height;
double xBRTrans = x*cos(angle) - y*sin(angle);
xBRTrans = sinuTL.x - xBRTrans;
double yBRTrans = x*sin(angle) + y*cos(angle);
yBRTrans = sinuTL.y - yBRTrans;
x = 0;
y = height;
double xBLTrans = x*cos(angle) - y*sin(angle);
xBLTrans += sinuTL.x;
double yBLTrans = x*sin(angle) + y*cos(angle);
yBLTrans = sinuTL.y - yBLTrans;
** Update **
I've adapted the code from the solution provided below, The result is still not what I expect. The two points on the left are given, the two points on the right are calculated. You can see that there is an offset (the points should be at the corner of the building. Also ignore the blue point in the middle - it's meaningless to this question):
The code:
double height = 57;
// get coords from provided input
double x1x=629434.24373957072, x1y=5476196.7595944777, x2x=629443.08914538298, x2y=5476120.1852802411;
// x2x3 = Vector from point x2 to point x3, assume x value as 1
double x2x3x = 1;
// calculate y-value, using the fact that dot-product of orthogonal vectors is 0
double x2x3y = x2x*x2x3x / (-1 * x2y);
// calculate length of vector x2x3
double length_e_vec_x2_x3 = sqrt(pow(x2x3x,2) + pow(x2x3y,2));
// stretch vector to provided witdh
x2x3x = x2x3x*height / length_e_vec_x2_x3;
x2x3y = x2x3y*height / length_e_vec_x2_x3;
// since x2x3 and x1x4 are equal, simple addition remains
double x3x, x3y, x4x, x4y;
x3x = x2x + x2x3x;
x3y = x2y + x2x3y;
x4x = x1x + x2x3x;
x4y = x1y + x2x3y;
UPDATE
Actually, all answers and also my own code work as expected. I was unfortunately blindfolded and didn't notice the issue was due to an inappropriate geo projection used for this area. So the coordinates come from WGS84 long/lat, and before the calculation is done get converted into a sinusoidal projection and later back into WGS84. The sinusoidal projection preserves the area (equal area projection) - but distorts shapes within an area. And you cannot just add some meters, and later convert back. I should have realized this earlier and was looking at the wrong place.
I'll choose the most elaborate answer as "winner". Though after testing I can say that all provided solutions actually work.
General recommonendation:
If I were you I would build classes for the vectors, and build functions for the required operations, however this example should do what you wish.
Vectors, absolute and relative coordinates:
Important note: you are working with coordinates, and this is a really simplified approach to it. If the person providing you a solution is setting a given Point to 0/0, aka the Origin, you can't just Change this. I changed the code below to adjust to the changes you did to the Input provided.
double width = 35;
// get coords from provided input
double x1x=0, x1y=0, x2x=x, x2y=y;
// x2x3 = Vector from point x2 to point x3, assume x value as 1
double x2x3x = 1;
// calculate y-value, using the fact that dot-product of orthogonal vectors is 0
double x2x3y = x2x*x2x3x / (-1 * x2y);
// calculate length of vector x2x3
double length_e_vec_x2_x3 = sqrt(pow(x2x3x,2) + pow(x2x3y,2));
// stretch vector to provided witdh
x2x3x = x2x3x*width / length_e_vec_x2_x3;
x2x3y = x2x3y*width / length_e_vec_x2_x3;
// since x2x3 and x1x4 are equal, simple addition remains
double x3x, x3y, x4x, x4y;
x3x = x2x + x2x3x;
x3y = x2y + x2x3y;
x4x = x1x + x2x3x;
x4y = x1y + x2x3y;
// check results
cout << "X1: " << x1x << "/" << x1y << endl;
cout << "X2: " << x2x << "/" << x2y << endl;
cout << "X3: " << x3x << "/" << x3y << endl;
cout << "X4: " << x4x << "/" << x4y << endl;
Output:
X1: 629434/5.4762e+06
X2: 629443/5.47612e+06
X3: 629500/5.47613e+06
X4: 629491/5.4762e+06
Verification
As mentioned in the comments of this code, the dot-product of two vectors will return 0 if those vectors are orthogonal to each other. By using this, one can verify the provided results.
Add this little amount of code to verify the results:
// verify results
cout << "Dotproduct should be 0: " << (x2x*x2x3x)+(x2y*x2x3y) << endl;
Output of verification
Dotproduct should be 0: 5.68434e-14
Which prints 0, so the code is doing what it should do.
Improvements
However since you use rather big Numbers, using a float instead of a double might help. Also converting x1 into the origin of your little system might improve it.
Finally a more suitable datastructure would be appreciated.
// using x1 as origin:
double x1x0 = 0, x1y0 = 0, x2x0 = x2x - x1x, x2y0 = x2y - x1y;
double x2x3x0 = 1;
// calculate y-value, using the fact that dot-product of orthogonal vectors is 0
double x2x3y0 = x2x0*x2x3x0 / (-1 * x2y0);
// calculate length of vector x2x3
double length_e_vec_x2_x30 = sqrt(pow(x2x3x0, 2) + pow(x2x3y0, 2));
// stretch vector to provided witdh
x2x3x0 = x2x3x0*width / length_e_vec_x2_x30;
x2x3y0 = x2x3y0*width / length_e_vec_x2_x30;
// since x2x3 and x1x4 are equal, simple addition remains
double x3x0, x3y0, x4x0, x4y0;
x3x0 = x2x0 + x2x3x0;
x3y0 = x2y0 + x2x3y0;
x4x0 = x1x0 + x2x3x0;
x4y0 = x1y0 + x2x3y0;
// check results
cout << "X1: " << x1x0 << "/" << x1y0 << endl;
cout << "X2: " << x2x0 << "/" << x2y0 << endl;
cout << "X3: " << x3x0 << "/" << x3y0 << endl;
cout << "X4: " << x4x0 << "/" << x4y0 << endl;
// verify results
cout << "Dotproduct should be 0: " << (x2x0*x2x3x0) + (x2y0*x2x3y0) << endl;
// compare results (adding offset before comparing):
cout << "X3 to X30: " << x3x0+x1x-x3x << "/" << x3y0+x1y-x3y << endl;
cout << "X4 to X40: " << x4x0 +x1x-x4x << "/" << x4y0 +x1y-x4y << endl;
Results:
X1: 0/0
X2: 8.84541/-76.5743
X3: 65.4689/-70.0335
X4: 56.6235/6.5408
Dotproduct should be 0: 5.68434e-14
X3 to X30: 0/0
X4 to X40: 0/0
Now the output using floats:
X1: 629434/5.4762e+06
X2: 629443/5.47612e+06
X3: 629500/5.47613e+06
X4: 629491/5.4762e+06
Dotproduct should be 0: 0
X1: 0/0
X2: 8.8125/-77
X3: 65.4428/-70.5188
X4: 56.6303/6.48123
Dotproduct should be 0: 0
X3 to X30: 0/0
X4 to X40: 0/0
Building the whole thing less messy:
using namespace std;
class vector2D
{
protected:
bool equal(vector2D& param) { return this->X == param.X && this->Y == param.Y; }
vector2D absAlVal() { return vector2D(abs(X), abs(Y)); }
public:
float X, Y;
vector2D(float x, float y) : X(x), Y(y) {};
vector2D() : X(0), Y(0) {};
vector2D operator+ (vector2D& param) { return vector2D(this->X+param.X,this->Y+param.Y); }
vector2D operator- (vector2D& param) { return vector2D(this->X - param.X, this->Y - param.Y); }
bool operator!=(vector2D& param) { return this->equal(param); }
vector2D getUnitVector()
{
return vector2D(this->X / this->getLength(), this->Y / this->getLength());
}
bool parallel(vector2D& param) { return (this->getUnitVector()).equal(param.getUnitVector()); }
bool colinear(vector2D& param) { return (this->getUnitVector().absAlVal()).equal(param.getUnitVector().absAlVal()); }
float dotproduct(vector2D vec)
{
return this->X * vec.X + this->Y * vec.Y;
}
vector2D dotproduct(float scalar)
{
return vector2D(this->X * scalar, this->Y * scalar);
}
float getLength(void)
{
return sqrt(pow(this->X, 2) + pow(this->Y, 2));
}
};
void main()
{
// get coords from provided input
float x1x = 629434.24373957072, x1y = 5476196.7595944777, x2x = 629443.08914538298, x2y = 5476120.1852802411;
float width = 35;
// Build vectors
vector2D X1 = vector2D(x1x, x1y), X2 = vector2D(x2x, x2y), X3, X4, X2X3, X1X2=X2-X1;
// assum x-direction for X2X3 is positive, chosing 1
X2X3.X = 1;
// calculate y-direction using dot-product
X2X3.Y = X1X2.X*X2X3.X / (-1 * X1X2.Y);
//check if assumtion is correct:
cout << "Evaluate wether vector has been build accordingly or not:" << endl;
cout << "Dotproduct of X1X2 * X2X3 should be 0 -> Result:" << X1X2.dotproduct(X2X3) << endl;
// stretch X2X3 to width
X2X3=X2X3.getUnitVector().dotproduct(width);
// Create X3 and X4 by simple addition:
X3 = X2 + X2X3;
X4 = X1 + X2X3;
// print Points:
cout << "Summary of Points X / Y coordinates:" << endl;
cout << "X1: " << X1.X << "/" << X1.Y << endl;
cout << "X2: " << X2.X << "/" << X2.Y << endl;
cout << "X3: " << X3.X << "/" << X3.Y << endl;
cout << "X4: " << X4.X << "/" << X4.Y << endl;
// compare sides
cout << "\n" << "Lenght of sides:" << endl;
cout << "X1X2: " << (X2 - X1).getLength() << " -> should be same length as X3X4" << endl;
cout << "X2X3: " << (X3 - X2).getLength() << " -> should be same length as X4X1 and with, which is:" << width << endl;
cout << "X3X4: " << (X4 - X3).getLength() << " -> should be same length as X1X2" << endl;
cout << "X4X1: " << (X1 - X4).getLength() << " -> should be same length as X2X3, which is:" << width << endl;
}
Given a vector (x, y), the direction (y, -x) is rotated by 90 degrees clockwise with respect to it. This is exactly the rotation we need to perform to obtain the direction of the side x1 -> x4 from x1 -> x2.
// input point struct
struct point { double x, y; };
// pass in output points by reference
void calculate_other_points(
const point& x1, const point& x2, // input points x1 x2
double w, // input width
point& x3, point& x4) // output points x3 x4
{
// span vector x1 -> x2
double dx = x2.x - x1.x,
dy = x2.y - x1.y;
// height
double h = hypot(dx, dy);
// perpendicular edge x1 -> x4 or x2 -> x3
double px = dy * (w / h),
py = -dx * (w / h);
// add onto x1 / x2 to obtain x3 / x4
x4.x = x1.x + px; x4.y = x1.y + py;
x3.x = x2.x + px; x3.y = x2.y + py;
}
Note that my code is similar in principle to that of the previous answer, but is somewhat more optimized, and (hopefully) fixes the direction issue.
Related
I have been making a double-pendulum simulator in C++ using Raylib and I have finished everything other than correctly implementing the equations. When doing so, however, no matter what I try, I can't seem to figure out why my implementation of the equations is not working. Most likely I am missing something fundamental but it's so hectic as is that it's difficult for me to understand. How would I implement these equations successfully? For reference:
Angular acceleration for pendulum 1 is angularA1
Angular acceleration for pendulum 2 is angularA2
I split the numerators in the equations into parts because it's simply too hard to keep track of it on one line. The problem is in main.cpp and the file generator is completely separate from the pendulum.
pendulum.h:
#ifndef DOUBLE_PENDULUM_SIM_PENDULUM_H
#define DOUBLE_PENDULUM_SIM_PENDULUM_H
class pendulum {
public:
//Pendulum length
float pLength{};
//Pendulum mass
float pMass{};
//Pendulum's x component
float x{};
//Pendulum's y component
float y{};
//Pendulum's angular displacement relative to its origin
float pAngle{};
public:
void setAngularV(float angV);
void setAngularA(float angA);
[[nodiscard]] float getX() const ;
[[nodiscard]] float getY() const ;
[[nodiscard]] float getAngle() const ;
[[nodiscard]] float getMass() const;
[[nodiscard]] float getLength() const;
void setX(float length, float angle);
void setY(float length, float angle);
void setLength(float length);
void setMass(float mass);
void setAngle(float angle);
//Default constructor
pendulum();
//Easier way to initialize pendulum
pendulum(float length, float mass, float angle) : pLength(length), pMass(mass), pAngle(angle) {}
//De-constructor to make sure pendulum objects are destroyed
~pendulum();
};
#endif //DOUBLE_PENDULUM_SIM_PENDULUM_H
pendulum.cpp:
#include "includes/pendulum.h"
#include <cmath>
#include <iostream>
#include <raylib.h>
void pendulum::setX(float length, float angle) {
x = length * sin(angle);
}
void pendulum::setY(float length, float angle) {
y = length * cos(angle);
}
float pendulum::getX() const {
return x;
}
float pendulum::getY() const {
return y;
}
void pendulum::setLength(float length) {
pLength = length;
}
void pendulum::setMass(float mass) {
pMass = mass;
}
float pendulum::getAngle() const {
return pAngle;
}
pendulum::~pendulum() {
std::cout << "\nPendulum destroyed" << std::endl;
}
void pendulum::setAngle(float angle) {
pAngle = angle * DEG2RAD;
}
float pendulum::getMass() const {
return pMass;
}
float pendulum::getLength() const {
return pLength;
}
//Default constructor
pendulum::pendulum() = default;
main.cpp:
#include <iostream>
#include <fstream>
#include "raylib.h"
#include "includes/pendulum.h"
#include "includes/GenerateFile.h"
#include <cmath>
void testPendulum();
GenerateFile generator;
pendulum pen1;
pendulum pen2;
//The universal constant for gravity, for these purposes however, it can be any value as long as it works
const float g = 1;
int main() {
//Prompt to make the initial values themselves
float uLength1, uLength2, uMass1, uMass2, uAngle1, uAngle2;
//Recommendation: 100 200 for length, 20 25 for mass, 45 0 for angle
try {
std::cout << "Please choose the length of each pendulum, starting with Pendulum 1, then Pendulum 2. Each value provided can be up to 7 decimal digits, " << "\n" << "length MUST BE greater than 50 and less than 200" << "\n";
std::cin >> uLength1 >> uLength2;
std::cout << "Please choose the mass of each pendulum, starting with Pendulum 1, then Pendulum 2. Each value provided can be up to 7 decimal digits, " << "\n" << "mass MUST BE greater than 20 and less than 100" << "\n";
std::cin >> uMass1 >> uMass2;
std::cout << "Please choose the starting angle of each pendulum, starting with Pendulum 1, then Pendulum 2. Each value provided can be up to 7 decimal digits" << "\n";
std::cin >> uAngle1 >> uAngle2;
} catch (const std::exception & e) {
std::cout << e.what();
}
//Init angular acceleration and angular velocity for pendulums
float angularV1 = 0;
float angularV2 = 0;
//Pendulum 1 settings
pen1.setMass(uMass1);
pen1.setLength(uLength1);
pen1.setAngle(uAngle1);
pen1.setX(pen1.pLength,pen1.getAngle());
pen1.setY(pen1.pLength, pen1.getAngle());
std::cout << "X coord: " << pen1.getX() << " Y coord: " << pen1.getY() << std::endl;
//Pendulum 2 settings
pen2.setMass(uMass2);
pen2.setLength(uLength2);
pen2.setAngle(uAngle2); //Can only set this once and cant anywhere else, why?
pen2.setX( pen2.pLength,pen2.getAngle());
pen2.setY( pen2.pLength,pen2.getAngle());
pen2.x = pen1.getX() + pen2.getX();
pen2.y = pen1.getY() + pen2.getY();
std::cout << "X coord: " << pen2.getX() << " Y coord: " << pen2.getY() << std::endl;
//Window settings
const double screenWidth = 1440;
const double screenHeight = 1080;
Vector2 origin{(float) screenWidth/2,(float) screenHeight/3};
InitWindow((int) screenWidth, (int) screenHeight, "Double-Pendulum-Sim");
int frameCounter = 0;
SetTargetFPS(60);
//Set coords for pendulums
float px1 = pen1.getX() + origin.x;
float py1 = pen1.getY() + origin.y;
float px2 = pen2.getX() + origin.x;
float py2 = pen2.getY() + origin.y;
//Load & start pathing
RenderTexture2D target = LoadRenderTexture((int) screenWidth, (int) screenHeight);
BeginTextureMode(target);
ClearBackground(RAYWHITE);
EndTextureMode();
/****Write data to a file*****/
//Init new Values.txt file
generator.file.open("Values.txt");
if(!generator.file) {
perror("Error finding or opening file");
} else {
std::cout << "File opened successfully" << std::endl;
}
generator.file << "Angle 1 | X_1 | Y_1 | Angle 2 | X_2 | Y_2 | Frame # " << std::endl;
//Main while loop
while (!WindowShouldClose()) {
Vector2 rod1{px1,py1};
Vector2 rod2 {px2, py2};
//Implement angular acceleration for first pendulum equations:
float num1 = -g * (2 * pen1.getMass() + pen2.getMass()) * sin(pen1.getAngle());
float num2 = -pen2.getMass() * g * sin(pen1.getAngle() - 2 * pen2.getAngle());
float num3 = -2 * sin(pen1.getAngle() - pen2.getAngle()) * pen2.getMass();
float num4 = pow(angularV2, 2) * pen2.getLength() + pow(angularV1,2) * pen1.getLength() * cos(pen1.getAngle() - pen2.getAngle());
float den1 = pen1.getLength() * (2*pen1.getMass() + pen2.getMass() - pen2.getMass() * cos(2*pen1.getAngle() - 2 * pen2.getAngle()));
float angularA1 = (num1 + num2 + num3*num4) / den1;
//Angular acceleration for second pendulum:
num1 = 2 * sin(pen1.getAngle() - pen2.getAngle());
num2 = (pow(angularV1,2.0) * pen1.getLength() * (pen1.getMass() + pen2.getMass()));
num3 = g * (pen1.getMass() + pen2.getMass()) * cos(pen1.getAngle());
num4 = pow(angularV2,2.0) * pen2.getLength() * pen2.getMass() * cos(pen1.getAngle() - pen2.getAngle());
den1 = pen2.getLength() * (2*pen1.getMass() + pen2.getMass() - pen2.getMass() * cos(2*pen1.getAngle() - 2*pen2.getAngle()));
float angularA2 = (num1 * (num2 + num3 + num4)) / den1;
/**------------------Update------------------*/
frameCounter++;
uAngle1 += angularV1;
angularV1 += angularA1;
pen1.setAngle(uAngle1); //Can only set this once and cant anywhere else, why?
pen1.setX(pen1.pLength,pen1.getAngle());
pen1.setY(pen1.pLength, pen1.getAngle());
px1 = pen1.getX() + origin.x;
py1 = pen1.getY() + origin.y;
uAngle2 += angularV2;
angularV2 += angularA2;
pen2.setAngle(uAngle2); //Can only set this once and cant anywhere else, why?
pen2.setX( pen2.pLength,pen2.getAngle());
pen2.setY( pen2.pLength,pen2.getAngle());
pen2.x = pen1.getX() + pen2.getX();
pen2.y = pen1.getY() + pen2.getY();
px2 = pen2.getX() + origin.x;
py2 = pen2.getY() + origin.y;
//Write data to file by first getting the values of the pendulums:
generator.getData(std::to_string(frameCounter),std::to_string(uAngle1),std::to_string(px1),std::to_string(px2),std::to_string(uAngle2),std::to_string(px2),std::to_string(py2));
/**---------------------------------Draw-Pendulums & Path---------------------------------- */
BeginDrawing();
BeginTextureMode(target);
DrawCircleV(rod2, 2.0f, RED);
// DrawPixelV(rod2, RED);
EndTextureMode();
DrawTextureRec(target.texture, (Rectangle){0,0, (float) target.texture.width, (float) -target.texture.height}, (Vector2){0,0}, WHITE);
ClearBackground(RAYWHITE);
DrawFPS(100, 100);
DrawLineEx(origin, rod1, 5.0f, BLACK);
DrawCircle( px1,py1,pen1.pMass,BLACK);
DrawLineEx(rod1, rod2, 5.0f, BLACK);
DrawCircle(px2,py2,pen2.pMass,BLACK);
std::cout << "Frame #: " << frameCounter << std::endl;
std::cout << "Pendulum 1 Angle: " << uAngle1 << std::endl;
std::cout << "Pendulum 2 Angle: " << uAngle2 << std::endl;
std::cout << "Pendulum 1 X & Y: " << pen1.getX() << " " << pen1.getY() << std::endl;
std::cout << "Pendulum 2 X & Y: " << pen2.getX() << " " << pen2.getY() << std::endl;
EndDrawing();
}
CloseWindow();
generator.file.close();
return 0;
}
//Test function
void testPendulum() {
try {
pen1.setMass(20.0f);
pen1.setLength(150.0f);
pen1.setAngle(0.0f);
pen1.setX(pen1.pLength,pen1.getAngle());
pen1.setY(pen1.pLength, pen1.getAngle());
std::cout << "X coord: " << pen1.getX() << " Y coord: " << pen1.getY() << std::endl;
pen2.setMass(50.0f);
pen2.setLength(150.0f);
pen2.setAngle(0.0f);
pen2.setX( pen2.pLength,pen2.getAngle());
pen2.setY( pen2.pLength,pen2.getAngle());
pen2.x = pen1.getX() + pen2.getX();
pen2.y = pen1.getY() + pen2.getY();
std::cout << "X coord: " << pen2.getX() << " Y coord: " << pen2.getY() << std::endl;
} catch (const std::exception & e) {
std::cout << e.what();
}
}
These are the Double Pendulum Equations I'm trying to use:
Be careful, you've made no attempt to use sane units in your code. You've mismatched degrees and radians in at least one spot too by doing uAngle1 += angularV1. uAngle1 is in degrees, but the angularV1 should likely be produced in radians per second.
That brings in the missing aspect of time in your numerical integration.
Numerical integration typically works by assuming velocity is linear under short time intervals.
A typical numerical integration inner loop calculation looks like this:
acceleration = netForce / mass;
velocity += acceleration * timestep;
position += velocity * timestep;
Whereas you just do:
velocity += acceleration;
position += velocity;
...which is like using a timestep of 1.
The timestep must be small. For simulations that make gradual changes, it may be sufficient to just use the frame interval (e.g.: use a timestep of 1/60th of a second for a 60 fps simulation). Some simulations will actually subdivide the frame interval into a number of simulations steps (e.g. you could perform 100 updates per frame, each with a very small timestep of ((1/60) / 100).
Whatever happens, you should reconcile your units. Make sure your gravitational constant makes sense given the scale of your world. g = 1 is likely much too large. (#Gene made an excellent comment to this effect.) Also make sure your timesteps are reasonable. An implicit timestep may be interpreted as a time interval of 1 second (instead of 1 frame) and your simulation may be running an additional 60 times faster than you expect. All this leads to large values and numerical instability in the integration calculation, which depends on small changes.
You're currently using a distance unit of pixels, but perhaps it makes more sense to use a unit of meters and then scale your objects to fit the screen with a sane camera and viewport calculation.
You're currently using a time unit of frames, but perhaps it makes more sense to use a unit of seconds and make use of either the known frames-per-second rate of your simulation, or keep track of the actual elapsed time with a clock so that the simulation doesn't stutter if frames are dropped.
You're mismatching degrees and radians. It makes sense to use degrees when interacting with the user and radians in your physics calculations, but be careful not to mix them up during your calculations.
I have to take the coordinates of the vertices of a triangle from the user and tell if it is a right-angled triangle or not. I'm using Pythagoras Theorem to Find out i.e. h * h = b * b + p * p
But surprisingly this doesn't work for some specific right-angled triangles.
Here is one such Triangle:
Vertex A: (x, y) = (1, 3)
Vertex B: (x, y) = (1, 1)
Vertex C: (x, y) = (5, 1)
It calculates perfectly, which I figured out by printing the calculation, but still doesn't work.
Then I tried by using sqrt() function from the cmath library this way:
h = sqrt(b * b + p * p)
Logically it is the same, but it worked.
I want to understand, why the earlier method is not working?
Here is a simplified version of My Code:
#include <iostream>
#include <cmath>
using namespace std;
class Vertex {
double x, y;
public:
void take_input(char obj) {
cout << endl << " Taking Coordinates of Vertex " << obj << ": " << endl;
cout << " Enter the x component: ";
cin >> x;
cout << " Enter the y component: ";
cin >> y;
}
double distance(Vertex p) {
double dist = sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));
return dist;
}
};
class Triangle {
Vertex a, b, c;
public:
void take_inp(string obj) {
cout << endl << "Taking Vertices of the Triangle " << obj << ": " << endl;
cout << " Verteces should be in a counter clockwise order (as per convention)." << endl;
a.take_input('A');
b.take_input('B');
c.take_input('C');
}
void is_rt_ang() {
double h = a.distance(c)*a.distance(c);
double bp = a.distance(b)*a.distance(b) + b.distance(c)*b.distance(c);
/*
// Strangely this attempt works which is logically the same:
double h = a.distance(c);
double bp = sqrt(a.distance(b)*a.distance(b) + b.distance(c)*b.distance(c));
*/
if (h == bp) {
cout << "Angle is 90" << endl;
cout << h << " = " << bp << endl;
cout << "It is Right-Angled" << endl;
}
else {
cout << "Angle is not 90!" << endl;
cout << h << " != " << bp << endl;
cout << "It is Not a Right-Angled" << endl;
}
}
};
int main()
{
Triangle tri1, tri2;
tri1.take_inp("tri1");
tri1.is_rt_ang();
return 0;
}
The line
double dist = sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));
in the Vertex::distance method gives you an approximation of a square root which is rarely going to coincide with an exact answer. This is because most real numbers can't be represented in floating point arithmetic.
But in given code sample you can make do without sqrt. Replace Vertex::distance method with a method
double distance_square(Vertex p) {
double dist_square = (x-p.x)*(x-p.x) + (y-p.y)*(y-p.y);
return dist_square;
}
and call it like this in Triangle::is_rt_ang:
double h = a.distance_square(c);
double bp = a.distance_square(b) + b.distance_square(c);
This solution is still flawed because floating-point multiplication is also a subject to rounding errors. But if it is guaranteed that you are going to work only with integer coordinates, you can replace all doubles in your code with ints and for them there is no problem with multiplication (besides possibly going out of bounds for large numbers).
EDIT: Also a comment on printing
It calculates perfectly, which I figured out by printing the
calculation, but still doesn't work.
When you print doubles you need to set precision manually in order to avoid rounding. If in your code I replace a line
cout << h << " != " << bp << endl;
with
cout << std::setprecision(std::numeric_limits<double>::digits10) << std::fixed << h << " != " << bp << endl;
then for example triangle from the question I get the output
Angle is not 90!
20.000000000000004 != 20.000000000000000
It is Not a Right-Angled
For this to compile you will need to add #include <limits> and #include <iomanip>.
In your is_rt_ang function you're assuming that your hypotenuse is always going to be the edge AC, but it doesn't seem like you're doing anything to verify this.
double h = a.distance(c)*a.distance(c);
double bp = a.distance(b)*a.distance(b) + b.distance(c)*b.distance(c);
You could try getting the squares of all your distances first, (AC)^2, (AB)^2, and (BC)^2, then finding the candidate for hypotenuse by taking the max value out of the three, then do something like:
bool isRightTriangle = max == (min1 + min2)
You may also be running into some kind of round-off error with floating point numbers. It is common to use a an epsilon value when comparing floating point numbers because of the inherent round-off errors with them. If you don't need floating point values maybe use an integer, or if you do need floating point values try using an epsilon value in your equalities like:
abs(h - bp) <= epsilon
You should be able to find more information about floating point values, round-off errors, and machine epsilons on the web.
Here is a link to a SO Q/A that talks about floating point math that may be a good resource for you: Is floating point math broken?
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I would like assistance understanding this code I found during a search. I have been stuck for 2 weeks trying to get it and it's holding my project back. I am honestly trying to learn as I go and not just be a script kiddie about it, but this is much more complicated than the rest of my project even though I am trying. (I just learned about auto today while trying to understand this code, for example.)
I am working on a weather app and I know the lat/lon of the radar site. I need the lat/lon of a feature the radar has detected based off of the azimuth/range that the radar tells me (example, 271 degrees and 7 nautical miles). I need to understand how I can use this code below to convert the azimuth/range to a new lat lon coordinate. I don't require the other functions, just to be able to put my variables (starting coords, azimuth and range) and get a result. The code below looks to do much more than that, and it is confusing me.
I see the following code near the end :
auto coordinate = CoordinateToCoordinate(latitude1, longitude1, angle, meters);
... Which looks to be the part I would need out of this. I see how it's calculating it but once I dig deeper I just get myself confused. I have tried hacking at the code so much that I gave up and don't even have any examples.
I would like to be able to set my variables manually (example cin>>) and have the lat and lon output into a variable that I can save to a text file. I am able to do everything myself (ingesting the starting variables and writing the result to a text file) except the actual conversion itself.
How could I get started with this using the code below?
My example variables are :
Original Latitude = 29.4214
Original Longitude = -98.0142
Azimuth from Origin = 271 degrees
Range from Origin = 6 nautical miles (I can convert to meters if needed,
in this case it's 11112 meters)
The actual unedited code is below and a copy at this link. If I get help with this I won't just copy/paste and I will come back with the completed code after I make it. I really am wanting to understand as I go, so I can be better with these advanced topics and not be constrained in the future. Code below :
#include<iostream>
#include<iomanip>
#include<cmath>
// Source: // http://w...content-available-to-author-only...o.uk/scripts/latlong.html
static const double PI = 3.14159265358979323846, earthDiameterMeters = 6371.0 * 2 * 1000;
double degreeToRadian (const double degree) { return (degree * PI / 180); };
double radianToDegree (const double radian) { return (radian * 180 / PI); };
double CoordinatesToAngle (double latitude1,
const double longitude1,
double latitude2,
const double longitude2)
{
const auto longitudeDifference = degreeToRadian(longitude2 - longitude1);
latitude1 = degreeToRadian(latitude1);
latitude2 = degreeToRadian(latitude2);
using namespace std;
const auto x = (cos(latitude1) * sin(latitude2)) -
(sin(latitude1) * cos(latitude2) * cos(longitudeDifference));
const auto y = sin(longitudeDifference) * cos(latitude2);
const auto degree = radianToDegree(atan2(y, x));
return (degree >= 0)? degree : (degree + 360);
}
double CoordinatesToMeters (double latitude1,
double longitude1,
double latitude2,
double longitude2)
{
latitude1 = degreeToRadian(latitude1);
longitude1 = degreeToRadian(longitude1);
latitude2 = degreeToRadian(latitude2);
longitude2 = degreeToRadian(longitude2);
using namespace std;
auto x = sin((latitude2 - latitude1) / 2), y = sin((longitude2 - longitude1) / 2);
#if 1
return earthDiameterMeters * asin(sqrt((x * x) + (cos(latitude1) * cos(latitude2) * y * y)));
#else
auto value = (x * x) + (cos(latitude1) * cos(latitude2) * y * y);
return earthDiameterMeters * atan2(sqrt(value), sqrt(1 - value));
#endif
}
std::pair<double,double> CoordinateToCoordinate (double latitude,
double longitude,
double angle,
double meters)
{
latitude = degreeToRadian(latitude);
longitude = degreeToRadian(longitude);
angle = degreeToRadian(angle);
meters *= 2 / earthDiameterMeters;
using namespace std;
pair<double,double> coordinate;
coordinate.first = asin((sin(latitude) * cos(meters))
+ (cos(latitude) * sin(meters) * cos(angle)));
coordinate.second = longitude + atan2((sin(angle) * sin(meters) * cos(latitude)),
cos(meters) - (sin(latitude) * sin(coordinate.first)));
coordinate.first = radianToDegree(coordinate.first);
coordinate.second = radianToDegree(coordinate.second);
return coordinate;
}
int main ()
{
using namespace std;
const auto latitude1 = 12.968460, longitude1 = 77.641308,
latitude2 = 12.967862, longitude2 = 77.653130;
cout << std::setprecision(10);
cout << "(" << latitude1 << "," << longitude1 << ") --- "
"(" << latitude2 << "," << longitude2 << ")\n";
auto angle = CoordinatesToAngle(latitude1, longitude1, latitude2, longitude2);
cout << "Angle = " << angle << endl;
auto meters = CoordinatesToMeters(latitude1, longitude1, latitude2, longitude2);
cout << "Meters = " << meters << endl;
auto coordinate = CoordinateToCoordinate(latitude1, longitude1, angle, meters);
cout << "Destination = (" << coordinate.first << "," << coordinate.second << ")\n";
}
I think you just want something like this:
#include <iostream>
std::pair<double,double> CoordinateToCoordinate (double latitude,
double longitude,
double angle,
double meters)
{
...
...
}
using namespace std;
int main() {
double lat, lon, angle, dist;
cout << "Enter lat:"; cin >> lat;
cout << "Enter lon:"; cin >> lon;
cout << "Enter angle:"; cin >> angle;
cout << "Enter dist:"; cin >> dist;
auto coordinate = CoordinateToCoordinate(lat, lon, angle, dist);
cout << "Destination = (" << coordinate.first << "," << coordinate.second << ")\n";
}
I have received that function in my code, written by someone else. I don't understand the theory behind it, but it seems to be working.
Does somebody could lead me in the right direction ?
We are calculating the slope of a road from a pointcloud with ransac.
rotation is the world to local matrix, so plane_normal_rot is the normal vector of the plane in the world.
But after that I don't understand what is going on..
ransac.getModelCoefficients(model_coefficients);
std::cout << "#############################" << std::endl;
std::cout << "PLANE MODEL: " << model_coefficients[0] << " "<< model_coefficients[1] << " "<< model_coefficients[2] << " " << model_coefficients[3];
std::cout << "#############################" << std::endl;
double a = model_coefficients[0];
double b = model_coefficients[1];
double c = model_coefficients[2];
tf::Vector3 plane_normal(a,b,c);
tf::Vector3 plane_normal_rot(0,0,0);
//tf::Matrix3x3 rotation_tr = rotation.transpose();
tf::Matrix3x3 rotation_tr = rotation;
plane_normal_rot.setX( (plane_normal.getX() * rotation_tr[0][0])
+ (plane_normal.getY() * rotation_tr[0][1])
+ (plane_normal.getZ() * rotation_tr[0][2]));
plane_normal_rot.setY( (plane_normal.getX() * rotation_tr[1][0])
+ (plane_normal.getY() * rotation_tr[1][1])
+ (plane_normal.getZ() * rotation_tr[1][2]));
plane_normal_rot.setZ( (plane_normal.getX() * rotation_tr[2][0])
+ (plane_normal.getY() * rotation_tr[2][1])
+ (plane_normal.getZ() * rotation_tr[2][2]));
//Check sign
if(plane_normal_rot.getZ() < 0)
{
plane_normal_rot *= (-1);
}
pitch = asin(plane_normal_rot.getX());
If I havn't been clear or you feel like you're missing info please tell me.
This I feel is a rather complicated problem, I hope I can fit it in to small enough of a space to make it understandable. I'm presently writing code to
simulate Ideal gas particles inside a box. I'm calculating if two particles will collide having calculated the time taken for them to reach their closest point. (using an example where they have head on collision).
In this section of code I need to find if they will collide at all for two particles, before then calculating at what time and how they collide etc.
Thus for my two paricles:
Main.cpp
Vector vp1(0,0,0);
Vector vv1(1,0,0);
Vector vp2(12,0,0);
Vector vv2(-1,0,0);
Particle Particle1(1, vp1, vv1);
Particle Particle2(1, vp2, vv2);
Particle1.timeToCollision(Particle2);
Within my program I define a particle to be:
Header File
class Particle {
private:
Vector p; //position
Vector v; //velocity
double radius; //radius
public:
Particle();
Particle(double r, const Vector Vecp, const Vector Vecv);
void setPosition(Vector);
void setVelocity(Vector);
Vector getPosition() const;
Vector getVelocity() const;
double getRadius() const;
void move(double t);
double timeToCollision(const Particle particle);
void collideParticles(Particle);
~Particle();
};
Vector is another class that in short gives x, y, z values. It also contains multiple functions for manipulating these.
And the part that I need help with, within the .cpp (Ignore the cout start and letters etc, they are simple checks where my code exits for tests.)
Given the equations:
I have already written code to do the dot product and modulus for me and:
where
s is distance travelled in time tac.
double Particle::timeToCollision(const Particle particle){
Vector r2 = particle.getPosition();
Vector r1 = p;
Vector v2 = particle.getVelocity();
Vector v1 = v;
Vector r0 = r2 - r1;
Vector v = v2 - v1;
double modv;
double tca;
double result = 0;
double bsqr;
modv = getVelocity().modulus();
cout << "start" << endl;
if(modv < 0.0000001){
cout << "a" << endl;
result = FLT_MAX;
}else{
cout << "b" << endl;
tca = ((--r0).dot(v)) / v.modulusSqr();
// -- is an overridden operator that gives the negation ( eg (2, 3, 4) to (-2, -3, -4) )
if (tca < 0) {
cout << "c" << endl;
result = FLT_MAX;
}else{
cout << "d" << endl;
Vector s(v.GetX(), v.GetY(), v.GetZ());
s.Scale(tca);
cout << getVelocity().GetX() << endl;
cout << getVelocity().GetY() << endl;
cout << getVelocity().GetZ() << endl;
double radsqr = radius * radius;
double bx = (r0.GetX() * r0.GetX() - (((r0).dot(v)) *((r0).dot(v)) / v.modulusSqr()));
double by = (r0.GetY() * r0.GetY() - (((r0).dot(v)) *((r0).dot(v)) / v.modulusSqr()));
double bz=(r0.GetZ() * r0.GetZ() - (((r0).dot(v)) * ((r0).dot(v)) / v.modulusSqr()));
if (bsqr < 4 * radsqr) {
cout << "e" << endl;
result = FLT_MAX;
} else {
}
cout << "tca: " << tca << endl;
}
}
cout << "fin" << endl;
return result;
}
I have equations for calculating several aspects, tca refers to Time of closest approach.
As written in the code I need to check if b > 4 r^2, I Have made some attempts and written the X, Y and Z components of b out. But I'm getting rubbish answers.
I just need help to establish if I've already made mistakes or the sort of direction I should be heading.
All my code prior to this works as expected and I've written multiple tests for each to check.
Please inform me in a comment for any information you feel I've left out etc.
Any help greatly appreciated.
You had several mistakes in your code. You never set result to a value different from 0 or FLT_MAX. You also never calculate bsqr. And I guess the collision happens if bsqr < 4r^2 and not the other way round. (well i do not understand why 4r^2 instead of r^2 but okay). Since you hide your vector implementation I used a common vector library. I also recommend to not use handcrafted stuff anyway. Take a look into armadillo or Eigen.
Here you go with a try in Eigen.
#include <iostream>
#include <limits>
#include <type_traits>
#include "Eigen/Dense"
struct Particle {
double radius;
Eigen::Vector3d p;
Eigen::Vector3d v;
};
template <class FloatingPoint>
std::enable_if_t<std::is_floating_point<FloatingPoint>::value, bool>
almost_equal(FloatingPoint x, FloatingPoint y, unsigned ulp=1)
{
FloatingPoint max = std::max(std::abs(x), std::abs(y));
return std::abs(x-y) <= std::numeric_limits<FloatingPoint>::epsilon()*max*ulp;
}
double timeToCollision(const Particle& left, const Particle& right){
Eigen::Vector3d r0 = right.p - left.p;
Eigen::Vector3d v = right.v - left.v;
double result = std::numeric_limits<double>::infinity();
double vv = v.dot(v);
if (!almost_equal(vv, 0.)) {
double tca = (-r0).dot(v) / vv;
if (tca >= 0) {
Eigen::Vector3d s = tca*v;
double bb = r0.dot(r0) - s.dot(s);
double radius = std::max(left.radius, right.radius);
if (bb < 4*radius*radius)
result = tca;
}
}
return result;
}
int main()
{
Eigen::Vector3d vp1 {0,0,0};
Eigen::Vector3d vv1 {1,0,0};
Eigen::Vector3d vp2 {12,0,0};
Eigen::Vector3d vv2 {-1,0,0};
Particle p1 {1, vp1, vv1};
Particle p2 {1, vp2, vv2};
std::cout << timeToCollision(p1, p2) << '\n';
}
My apologies for a very poorly worded question that was to long and bulky to make much sense of. Luckily I have found my own answer to be much easier then initially anticipated.
double Particle::timeToCollision(const Particle particle){
Vector r2=particle.getPosition();
Vector r1=p;
Vector v2=particle.getVelocity();
Vector v1=v;
Vector r0=r2-r1;
Vector v=v2-v1;
double modv;
double tca = ((--r0).dot(v)) / v.modulusSqr();
double bsqr;
double result=0;
double rColTestx=r0.GetX()+v.GetX()*tca;
double rColTesty=r0.GetY()+v.GetY()*tca;
double rColTestz=r0.GetZ()+v.GetZ()*tca;
Vector rtColTest(rColTestx, rColTesty, rColTestz);
modv=getVelocity().modulus();
cout << "start " << endl;
if(modv<0.0000001){
cout << "a" << endl;
result=FLT_MAX;
}else{
cout << "b" << endl;
if (tca < 0) {
cout << "c" << endl;
result=FLT_MAX;
}else{
cout << "d" << endl;
Vector s(v.GetX(), v.GetY(), v.GetZ());
s.Scale(tca);
cout << getVelocity().GetX() << endl;
cout << getVelocity().GetY() << endl;
cout << getVelocity().GetZ() << endl;
double radsqr= radius*radius;
bsqr=rtColTest.modulusSqr();
if (bsqr < 4*radsqr) {
cout << "e" << endl;
cout << "collision occurs" << endl;
result = FLT_MAX;
} else {
cout << "collision does not occurs" << endl;
}
}
}
cout << "fin" << endl;
return result;
}
Sorry its a large section of code. Also FLT_MAX is from the cfloat lib. I didn't stat this in my question. I found this to work for several examples I calculated on paper to check.
To be Clear, the return resultand result=0 were arbitrary. I later edit to return time but for this part didn't need or want that.