I'm trying to make a quadcopter drone. I'm using an mpu6050 to get acceleration & angular speed and then convert them to Roll / pitch / yaw
With the acceleration, i'm also trying to get the speed & position by integration. However , i need them to be in absolute coordinate system, because the mpu6050 gives you the values in its relative coordinates system.
the origin of the new coordinate system is the starting position of the drone, and the direction is "where the drone is looking" , we assume Yaw = 0 at the beginning, and we get yaw using the gyrometer's data.
I tried to rotate the vectors using the Roll / pitch values, but that doesn't seem to work very well.
I tried with this being the gravity vector for example : (-2, -2, -1)
If i convert it to the absolute coordinate system, i should get : (0,0, 3)
#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)
{
// Calculate the sinus and cosinus
float flCos = static_cast<float>(cos(angle));
float flSin = static_cast<float>(sin(angle));
// We save the current values temporarily
float _y = y;
float _z = z;
y = _y * flCos - _z * flSin;
z = _y * flSin + _z * flCos;
}
void cVector::RotateY(float angle)
{
// Calculate the sinus and cosinus
float flCos = static_cast<float>(cos(angle));
float flSin = static_cast<float>(sin(angle));
// We save the current values temporarily
float _x = x;
float _z = z;
x = _x * flCos + _z * flSin;
z = - _x * flSin + _z * flCos;
}
void cVector::RotateZ(float angle)
{
// Calculate the sinus and cosinus
float flCos = static_cast<float>(cos(angle));
float flSin = static_cast<float>(sin(angle));
// We save the current values temporarily
float _x = x;
float _y = y;
x = _x * flCos - _y * flSin;
y = _x * flSin + _y * flCos;
}
void PrintVector(cVector vec)
{
cout << "X : " << vec.x << " Y : " << vec.y << " Z : " << vec.z << endl;
}
// TODO : Add operators for Addition & Substraction
int main()
{
cVector vec(-2, -2, -1);
// Calculate pitch / roll
float pitch = static_cast<float>(atan2( vec.y , sqrt( pow(vec.x,2) + pow(vec.z,2) ) ));
float roll = static_cast<float>(atan2(-1 * vec.x , sqrt( pow(vec.y,2) + pow(vec.z,2) ) ));
// vec.RotateY(1.570796f);
vec.RotateX(roll);
vec.RotateY(pitch);
PrintVector(vec);
cin.get();
return 0;
}
expected result ( 0, 0, 3 )
Actual results : (-0.104919 , -0.824045, -2.8827 )
Related
I tried to write a program where a vector is calculated that bisects the angle between two lines that share one common point.
For that I've come up with some code (Since this it not the only thing I'm trying to do the code is a bit longer but I've boiled down the problem to this snipped.):
#include <iostream>
#include <cmath>
// A 2D Vector class
class PVector {
public: PVector() = default;
public: PVector(double _x, double _y) : x(_x), y(_y) {};
public: double x, y;
public: PVector set(double _x, double _y) {
x = _x, y = _y;
return *this;
};
public: double getMag() const {
return sqrt(x * x + y * y);
};
public: PVector setMag(double mag) {
mag *= getMag();
return (mag == 0) ? set(0, 0) : set(x / mag, y / mag);
};
public: PVector &operator+=(const PVector &rhs) {
x += rhs.x, y += rhs.y;
return *this;
};
public: PVector operator+(const PVector &rhs) {
return PVector(*this) += rhs;
};
public: PVector &operator-=(const PVector &rhs) {
x -= rhs.x, y -= rhs.y;
return *this;
};
public: PVector operator-(const PVector &rhs) {
return PVector(*this) -= rhs;
};
public: PVector &operator*=(const double &m) {
x *= m, y *= m;
return *this;
};
};
// A function to convert a 2D vector into an angle
double vector2Angle(double x, double y) {
if (x == 0)
return (y >= 0) ? 0 : 180;
else if (y == 0)
return (x > 0) ? 90 : 270;
double angle = atan(y / x);
// bottom left (90 - 180)
if (x < 0 && y < 0)
// angle is positive (180 location)
angle = M_PI / 2;
// top left (0 - 90)
else if (x < 0)
// angle is negative (90 positive) + (0 location)
angle += M_PI / 2;
// bottom right (180 - 270)
else if (y < 0)
// angle is negative (90 positive) + (180 location)
angle += 1.5 * M_PI;
// top right (270 - 360)
else {
angle += 1.5 * M_PI;
// angle is positive
}
return angle;
};
double vector2Angle(PVector v) {
return vector2Angle(v.x, v.y);
};
int main()
{
PVector p0 = PVector(90, 90);
PVector p1 = PVector(10, 90);
PVector p2 = PVector(10, 10);
// The sum of two unit vectors must return a vector that bisects the angle between the two vectors.
std::cout << "Expected: " << (vector2Angle(p1 - p0) + vector2Angle(p1 - p2)) / 2 << std::endl;
std::cout << "Got: " << vector2Angle((p1 - p0).setMag(1) + (p1 - p2).setMag(1)) << std::endl;
return 0;
}
From intuition the output should be either 135° or 315° but the program delivers:
Expected: 135
Got: 0.785398
The first strange thing about this output is that one is in degrees although vector2Angle returns a radian value. Even stranger is the fact that both results are in different units. And at last I'm wondering where my mistake in the calculation is because 0.785... radian are about 45° and not 135°.
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;
I am trying to write some position/orientation methods for my small & simple 3d-space calculation library. But I'm stuck on the following problem.
I store 3d line as start and end points. However it should be possible to store it as start point and line's length + orientation as well (it's just a good example to test if orientation calculations works).
By orientation I mean rotation from the initial "0" orientation (which places the end at start + [0,legth,0]). So I first rotate the [0,length,0] by orientation and then add start to it to get end point.
The problem is, my orientation calculations fails somewhere. After calculating the orientation I get different ending point.
I use left-handed coordinate system with Y-axis pointing up, but I don't think it's important here.
Here's the code (I've tried to name the methods in the way you can check if the steps are ok; here's the full source code if you want to compile it yourself):
Point3D start = { 5.0f, 4.0f, 7.0f };
Point3D end = { 15.0f, 6.0f, 14.0f };
Point3D direction = (end - start);
std::wcout << L"Direction: "; direction.output();
float angle = Point3D(0.0f, 1.0f, 0.0f).getAngleToAnotherVectorInRadians(direction);
Point3D axis = direction.getCrossProduct(Point3D(0.0f, 1.0f, 0.0f)).getNormalized();
Quaternion o = Quaternion(AxisAngle(axis, angle));
std::wcout << L"\nAxisAngle: "; AxisAngle(axis, angle).output();
std::wcout << L"\nOrientation: "; o.output();
//test - end2 should be equal to end
Point3D offset(0.0f, (end - start).getLengthAsVector(), 0.0f);
offset = o.rotatePoint(offset);
std::wcout << L"\nOffset: "; offset.output();
Point3D end2 = start + offset;
std::wcout << L"\nEnd2: "; end2.output();
The code produces such output (without a comments, of course):
Direction: {10, 2, 7} //looks ok
AxisAngle: {{-0.573462, 0, 0.819232}, 1.40839}
Orientation: {-0.371272, 0, 0.530388, 0.762132}
Offset: {-10, 2, -7} //Almost! It should be {10, 2, 7}
End2: {-5, 6, -9.53674e-07} //Wrong! It should be { 15, 6, 14 }
In case that all steps are ok but there are some mistakes in methods' implementations I post here the important code for classes (so you can reproduce the problem): Point3D, AxisAngle, Quaternion.
I highly believe that problem(s) lay(s) in my main steps or in AxisAngle calculations. I think that AxisAngle to Quaternion transformation is ok (but I pass the wrong AxisAngle to Quaternion constructor).
The Point3D:
struct Point3D {
protected:
float x, y, z;
public:
Point3D() : x(0.0f), y(0.0f), z(0.0f) {}
Point3D(float x, float y, float z) : x(x), y(y), z(z) {}
void output() { std::wcout << L"{" << x << L", " << y << L", " << z << L"}"; }
Point3D operator-(const Point3D &point) const {
Point3D temp;
temp.setX(getX() - point.getX());
temp.setY(getY() - point.getY());
temp.setZ(getZ() - point.getZ());
return temp;
}
Point3D operator+ (const Point3D &value) const {
Point3D temp;
temp.setX(getX() + value.getX());
temp.setY(getY() + value.getY());
temp.setZ(getZ() + value.getZ());
return temp;
}
inline float getX() const { return x; } inline float getY() const { return y; } inline float getZ() const { return z; }
inline void setX(float x) { this->x = x; } inline void setY(float y) { this->y = y; } inline void setZ(float z) { this->z = z; }
inline float getLengthAsVector() const {
return sqrt(x*x + y*y + z*z);
}
inline Point3D getCrossProduct(const Point3D &anotherVector) const {
//based on: http://www.sciencehq.com/physics/vector-product-multiplying-vectors.html
return Point3D(
y * anotherVector.z - anotherVector.y * z,
z * anotherVector.x - anotherVector.z * x,
x * anotherVector.y - anotherVector.x * y
);
}
inline float getDotProduct(const Point3D &anotherVector) const {
//based on: https://www.ltcconline.net/greenl/courses/107/Vectors/DOTCROS.HTM
return x * anotherVector.x + y * anotherVector.y + z * anotherVector.z;
}
inline float getAngleToAnotherVectorInRadians(const Point3D &anotherVector) const {
//based on: http://math.stackexchange.com/questions/974178/how-to-calculate-the-angle-between-2-vectors-in-3d-space-given-a-preset-function
return acos(getDotProduct(anotherVector) / (getLengthAsVector() * anotherVector.getLengthAsVector()));
}
Point3D getNormalized() const {
float length = std::abs(sqrt(x*x + y*y + z*z));
Point3D result(x / length, y / length, z / length);
return result;
}
};
The AxisAngle:
class AxisAngle {
protected:
Point3D axis;
float angleInRadians;
public:
AxisAngle(const AxisAngle &other) { axis = other.axis; angleInRadians = other.angleInRadians; }
AxisAngle::AxisAngle(float x, float y, float z, float angleInRadians) {
this->axis = Point3D(x, y, z);
this->angleInRadians = angleInRadians;
}
AxisAngle::AxisAngle(const Point3D &axis, float angleInRadians) {
this->axis = axis;
this->angleInRadians = angleInRadians;
}
Point3D getAxis() const { return axis; }
float getAngleInRadians() const { return angleInRadians; }
void output() { std::wcout << L"{"; axis.output(); std::wcout << L", " << angleInRadians << L"}"; }
};
And last but not least, Quaternion:
class Quaternion {
protected:
float x; float y; float z; float w;
public:
Quaternion() { x = 0.0f; y = 0.0f; z = 0.0f; w = 1.0f; }
Quaternion(const Quaternion &other) { x = other.x; y = other.y; z = other.z; w = other.w; }
Quaternion(float x, float y, float z, float w) { this->x = x; this->y = y; this->z = z; this->w = w; }
Quaternion(const AxisAngle &axisAngle) {
Point3D axis = axisAngle.getAxis();
float angleInRadians = axisAngle.getAngleInRadians();
x = sin(angleInRadians / 2) * axis.getX();
y = sin(angleInRadians / 2) * axis.getY();
z = sin(angleInRadians / 2) * axis.getZ();
w = cos(angleInRadians / 2);
normalizeIt();
}
float getLength() const {
return sqrt(x*x + y*y + z*z + w*w);
}
void normalizeIt() {
float length = getLength();
x = x / length;
y = y / length;
z = z / length;
w = w / length;
}
Quaternion getConjugated() const {
return Quaternion(-x, -y, -z, w);
}
Quaternion multiply(Quaternion by) {
//"R" for result
float wR = w * by.getW() - x * by.getX() - y * by.getY() - z * by.getZ();
float xR = x * by.getW() + w * by.getX() + y * by.getZ() - z * by.getY();
float yR = y * by.getW() + w * by.getY() + z * by.getX() - x * by.getZ();
float zR = z * by.getW() + w * by.getZ() + x * by.getY() - y * by.getX();
return Quaternion(xR, yR, zR, wR);
}
//rotate Point3D p around [0,0,0] with this Quaternion
Point3D rotatePoint(Point3D p) const {
Quaternion temp = multiply(p).multiply(getConjugated());
return Point3D(temp.getX(), temp.getY(), temp.getZ());
//G: P' = Q(P-G)Q' + G <- to rotate P around G with Quaternion
}
Quaternion multiply(Point3D r) const {
float wR = -x * r.getX() - y * r.getY() - z * r.getZ();
float xR = w * r.getX() + y * r.getZ() - z * r.getY();
float yR = w * r.getY() + z * r.getX() - x * r.getZ();
float zR = w * r.getZ() + x * r.getY() - y * r.getX();
return Quaternion(xR, yR, zR, wR);
}
inline float getX() const { return x; } inline void setX(float x) { this->x = x; }
inline float getY() const { return y; } inline void setY(float y) { this->y = y; }
inline float getZ() const { return z; } inline void setZ(float z) { this->z = z; }
inline float getW() const { return w; } inline void setW(float w) { this->w = w; }
void output() { std::wcout << L"{" << x << L", " << y << L", " << z << L", " << w << L"}"; }
};
In case somebody would ask: I do want to use quaternions. They may not look 100% needed here, but storing 3d object's orientation as quaternion has many benefits in more complex computations (and most game engines / 3d software use it as well "under the mask").
Your axis has the wrong orientation. It should be:
Point3D axis = Point3D(0.0f, 1.0f, 0.0f).getCrossProduct(direction).getNormalized();
Use the two left-hand rules to figure out the correct order.
While trying to create my own physics engine (don't try persuading me not to), I decided to create a class for each pixel, called Particle, this system has an x and a y, and a x and y velocity, as shown below. Unfortunately, the code for calculateGravitationalVelocity doesn't abide by the laws of physics in certain situations. For example, if the x of the particle and the x of the other particle is the same, the particle will fall towards the object realistically, but when the particle gets too close, it pings off towards the positive x. I am only going to include the class source code, but I can include the source code of the other file, though it is partly written in SFML
Particle.cpp:
#include <iostream>
#include <string>
#include <math.h>
class Particle
{
private:
//Coords:
double x, y;
//Velocities:
double xVelocity = 0;
double yVelocity = 0;
//Material:
std::string material = "Generic";
//Mass:
double mass = 0;
public:
//Coords:
void setCoords(double, double);
float getCoords(char);
//Velocities:
void giveVelocity(char, float);
void setVelocity(char, float);
float getVelocity(char);
//Gravitational Velocity:
void calculateGravitationalVelocity(Particle);
//Material:
void setMaterial(std::string);
std::string getMaterial();
//Mass:
void setMass(double);
double getMass();
//Update:
void update();
};
//Coords:
void Particle::setCoords(double newX, double newY)
{
x = newX;
y = newY;
}
float Particle::getCoords(char axis)
{
if (axis == 'x')
{
//return floor(x);
return x;
}
else if (axis == 'y')
{
//return floor(y);
return y;
}
}
//Velocities:
void Particle::giveVelocity(char axis, float addedVelocity)
{
if (axis == 'x') {xVelocity = xVelocity + addedVelocity;}
else if (axis == 'y') {yVelocity = yVelocity + addedVelocity;}
}
void Particle::setVelocity(char axis, float newVelocity)
{
if (axis == 'x') {xVelocity = newVelocity;}
else if (axis == 'y') {yVelocity = newVelocity;}
}
float Particle::getVelocity(char axis)
{
if (axis == 'x') {return xVelocity;}//floor(xVelocity);}
else if (axis == 'y') {return xVelocity;}//floor(yVelocity);}
}
//Gravitational Velocity (Where the problems probably are):
void Particle::calculateGravitationalVelocity(Particle distantParticle)
{
//Physics constants:
const double pi = 3.14159265359; //Pi
const double G = 0.00000000006673; //Gravitational Constant (or Big G)
//Big Triangle Trigonometry:
//Get coords of moving particle:
double x1 = x;
double y1 = y;
//Get coords of particle with gravity:
double x2 = distantParticle.getCoords('x');
double y2 = distantParticle.getCoords('y');
if (x1 != x2)
{
//Work out the angle:
double A = atan((y2 - y1) / (x2 - x1)) * 180 / pi;
//Remove the minus sign:
A = fabs(A);
//Small Triangle Trigonometry:
//Work out the hypotenuse of the big triangle:
double hyp = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
//Work out the gravitational field (hyp of small triangle):
long double gravitationalField = G * (distantParticle.getMass() / pow(hyp, 2));
//For testing purposes:
//std::cout << "X: " << (cos(A) * gravitationalField) / 1000 << std::endl;
//std::cout << "Y: " << (sin(A) * gravitationalField) / 1000 << std::endl;
//Work out the X velocity:
xVelocity = xVelocity + (cos(A) * gravitationalField) / 1000;
//Work out the Y velocity:
yVelocity = yVelocity + (sin(A) * gravitationalField) / 1000;
}
else
{
//Work out the hypotenuse of the big triangle:
double hyp = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
//Work out the gravitational field (hyp of small triangle):
long double gravitationalField = G * (distantParticle.getMass() / pow(hyp, 2));
yVelocity = yVelocity + gravitationalField / 1000;
}
}
//Material:
void Particle::setMaterial(std::string newMaterialType)
{
material = newMaterialType;
}
std::string Particle::getMaterial()
{
return material;
}
//Mass:
void Particle::setMass(double newMass)
{
mass = newMass;
}
double Particle::getMass()
{
return mass;
}
//Update:
void Particle::update()
{
x = x + xVelocity;
y = y + yVelocity;
}
I am sorry for the very open question, and it probably goes against the rules somewhere, but I couldn't find it. The code for working out mostly uses a two triangles to make a x and y velocity. Here is an image of what I was hoping the code would do as a triangle (sorry it doesn't look great, but I like using a whiteboard):
You don't need to perform any trigonometric calculation.
...
//Get coords of particle with gravity:
double x2 = distantParticle.getCoords('x');
double y2 = distantParticle.getCoords('y');
// Get difference vector
double rx = x1 - x2;
double ry = y1 - y2;
// square of distance
double r2 = rx * rx + ry * ry;
// distance
double r = sqrt (r2);
if (r != 0) {
// normalize difference vector
double ux = rx / r;
double uy = ry / r;
// acceleration of gravity
double a = - G * distantParticle.getMass() / r2;
xVelocity += a * ux / 1000;
yVelocity += a * uy / 1000;
}
}
I am programming in OpenGL and C++. I know 2 points on 1 line (a diagonal line) and wish to rotate an object around that diagonal line. How can I go about doing this? I know how to use glrotatef to rotate it around the x, y or z axis but am not sure about this.
The x, y and z parameters to glRotate can specify any arbitrary axis, not just the x, y and z axes. To find an axis passing through your line, just subtract the end-points of the line to get an axis vector: if the two points are (x1, y1, z1) and (x2, y2, z2), the axis you need is (x2-x1, y2-y1, z2-z1).
Edit: As #chris_l pointed out, this works only if the line passes through the origin. If not, first apply a translation of (-x1, -y1, -z1) so that the line passes through the origin, then apply the above rotation, and translate it back by (x1, y1, z1).
Hey, how about doing some quaternions / vector maths? =) I've done that using small "patch" on my Vector class:
double NumBounds(double value)
{
if (fabs(value) < (1 / 1000000.0f))
return 0; else
return value;
}
class Vector
{
private:
double x, y, z;
public:
Vector(const Vector &v)
{
x = NumBounds(v.x); y = NumBounds(v.y); z = NumBounds(v.z);
}
Vector(double _x, double _y, double _z)
{
x = NumBounds(_x); y = NumBounds(_y); z = NumBounds(_z);
}
Vector Normalize()
{
if (Length() != 0)
return Vector(x / Length(), y / Length(), z / Length()); else
return *this;
}
double operator[](unsigned int index) const
{
if (index == 0)
return NumBounds(x); else
if (index == 1)
return NumBounds(y); else
if (index == 2)
return NumBounds(z); else
return 0;
}
void operator=(const Vector &v)
{
x = NumBounds(v.x); y = NumBounds(v.y); z = NumBounds(v.z);
}
Vector operator+(const Vector &v)
{
return Vector(x + v.x, y + v.y, z + v.z);
}
Vector operator-(const Vector &v)
{
return Vector(x - v.x, y - v.y, z - v.z);
}
double operator*(const Vector &v)
{
return NumBounds((x * v.x) + (y * v.y) + (z * v.z));
}
Vector operator*(double s)
{
return Vector(x * s, y * s, z * s);
}
Vector DotProduct(const Vector &v)
{
double k1 = (y * v.z) - (z * v.y);
double k2 = (z * v.x) - (x * v.z);
double k3 = (x * v.y) - (y * v.x);
return Vector(NumBounds(k1), NumBounds(k2), NumBounds(k3));
}
Vector Rotate(Vector &axis, double Angle)
{
Vector v = *this;
return ((v - axis * (axis * v)) * cos(angle)) + (axis.DotProduct(v) * sin(angle)) + (axis * (axis * v));
}
};
Using this class you can easily rotate any vector around any other one:
Vector a(1.0f, 0.0f, 0.0f), b(0.0f, 1.0f, 0.0f), c(0.0f, 0.0f, 0.0f);
c = a.Rotate(b, M_PI / 2.0f); // rotate vector a around vector b for 90 degrees (PI / 2 radians): should be Vector(0, 0, 1);
glrotate does the rotation about an axis. One method is to perform transformations which align rotation axis with one of coordinate axis, perform the rotation, then reverse the first step. If you need speed you can combine the operations into a special transformation matrix and apply them in one step. There's a description here.