0
\$\begingroup\$

I want to connect two spheres with the coordinates [ax, ay, az] and [bx, by, bz] with an cylinder which is placed in between the spheres an rotated. For the placement I calculate the center of the two spheres and place the cylinder there. The cylinder spawns parallel to the Z-Axis. Since I want to connect the spheres, I thought I first need to rotate either on the Y- or X-Axis and then on the Z-Axis.

I learned that combination of rotations is easiest with quaternions since you only have to multiply them, so I decided to use quaternions. So I get the angle of the first rotation(on Y- or X-Axis) and get the quaternion of this rotation, and then I get the angle of the second rotation on the Z-Axis and the quaternion of this rotation. Then I multiply them in order to get the combined rotation, and use the resulting quaternion on the cylinder.

This seems to work, but here is the problem: the more I get to the +-45° angle between the two spheres, the more I get something which looks like an offset to me. Furthermore, the greater the distance between the points is, the more offset I get. I checked the two quaternions which are multiplied, and both are indeed a +-45° rotation about the correct axis (in this example -45° in radians)

quaternions

Since this is very hard to explain with words, here are some screenshots:

This is the 45° rotation: 45deg_rotation

This is with more distant points: 45°_moredistance

The red dot on the ground marks the x-axis, the blue dot marks the y-axis.The simulation was started without gravity.

So my question is, why is this happening? And if anyone knows why, how can I fix it? Since I am new to game development and orientation in R3, this could be something obvious to an experienced developer.

I am using c++ with the Open Dynamics Engine and the drawstuff library for my program.

EDIT: This is the resulting quaternion result_quaternion

EDIT2: I add the calculation of the angles. This whole thing is in an for-loop.

        /* lengths[i][j] is the distance between two points, taking the coordinates of point 1 minus the coordinates of point 0

        For example:
        lenghts[0][0] is the distance of the x-coordinates of the first two points which should be connected
        lengths[0][1] is the distance of the y-coordinates of the first two points which should be connected
        lengths[0][2] is the distance of the z-coordinates of the first two points which should be connected
        */
        angle = -pioff + atan(lengths[i][2] / lengths[i][1]); // pioff is M_PI_2 = pi/2 = 1.5708
        if(isnan(angle))
        {
            angle = 0.0;
        }
        dQFromAxisAndAngle(Q1, 1, 0, 0, angle); // get quaternion1 of angle
        cout << "   quat Q1: " << Q1[0] << " + i" << Q1[1] << " + j"  << Q1[2] << " + k"  << Q1[3] << endl;
        cout << "   angle Q1: " << angle << endl;
        cout << "   x axis: angle = -" << pioff << " + atan(" << lengths[i][2] << " / " << lengths[i][1] << ") = " << angle << endl;
        angle = -atan(lengths[i][0] / lengths[i][1]);
        if(isnan(angle))
        {
            angle = 0.0;
        }
        dQFromAxisAndAngle(Q2, 0, 0, 1, angle); // get quaternion2 of angle
        cout << "   quat Q2: " << Q2[0] << " + i" << Q2[1] << " + j"  << Q2[2] << " + k"  << Q2[3] << endl;
        cout << "   angle Q2: " << angle << endl;
        printf("   connect xyz1(%f/%f/%f) and xyz2(%f/%f/%f)\n", xyz1[i][0] + c[0], xyz1[i][1] + c[1], xyz1[i][2] + c[2], xyz2[i][0] + c[0], xyz2[i][1] + c[1], xyz2[i][2] + c[2]);
        cout << "   z axis: angle = - atan(" << lengths[i][1] << " / " << lengths[i][0] << ") = " << angle << endl;

This is the output I get. Lenght[][] output are the elements of lengths[i][j] calculation

\$\endgroup\$
0
\$\begingroup\$

For something like this, I'd be tempted to use a rotation matrix, if that's an option.

// Form a unit vector pointing along the line between the spheres.
zDirection = normalize(sphere2Center - sphere1Center)

// Expand this into an orthonormal basis
// (3 mutually perpendicular unit vectors)
if(abs(dot(zDirection, (0, 1, 0)) < 1)
    xDirection = normalize(cross((0, 1, 0), zDirection))
else
    xDirection = (1, 0, 0)

yDirection = cross(zDirection, xDirection)

// These vectors become the columns of your rotation matrix
// (or rows, if you multiply with the vector on the left)
rotationMatrix.columns[0] = xDirection
rotationMatrix.columns[1] = yDirection
rotationMatrix.columns[2] = zDirection

If you prefer to work with quaternions, you can convert these basis vectors into a quaternion format before proceeding, rather than using the rotation matrix directly.

\$\endgroup\$
  • \$\begingroup\$ thanks, I will try this and edit the post to show the calculation. However, when I use the Rotation Matrix function provided by the angle, this still doesnt work for some reason. \$\endgroup\$ – MKoch Dec 31 '17 at 17:54
  • \$\begingroup\$ The format you store your orientation in won't make a difference in a case like this. The wrong rotation expressed as a quaternion or a matrix is still the wrong rotation. So your error isn't in the quaternions themselves, but how you're deriving or composing your rotation angles. \$\endgroup\$ – DMGregory Dec 31 '17 at 18:05
  • \$\begingroup\$ But since I get the correct angle and the correct quaternion, i dont see what is wrong. Am I missing something obvious? \$\endgroup\$ – MKoch Dec 31 '17 at 18:30
  • \$\begingroup\$ When you achieve a result that appears impossible, check your assumptions. In this case, the assumption that the angles as you've calculated should yield the correct orientation is incorrect. 3D orientations resist being broken down axis-by-axis, so we need to proceed very carefully, and the intuitive approach is often wrong. \$\endgroup\$ – DMGregory Dec 31 '17 at 18:34
  • \$\begingroup\$ Okay, good to know, thanks for the information! I didnt know that you cant break down axis-by-axis \$\endgroup\$ – MKoch Dec 31 '17 at 19:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.