Skip to content

possible bug in getEuler / getOrientation / getRoll #679

Closed
companje opened this Issue Jul 24, 2011 · 10 comments

4 participants

@companje

I think there's a bug in ofQuaternion in of007. When i use ofNode's setOrientation() or ofNode setRoll I can rotate my ofNode object around the Z axis. However when I try to obtain the current rotation around the Z axis it will always give a value like -180, 0 or 180.

Rick

@structuresound

Ha! 3 years, I'm getting the same issue in 2014

@structuresound

yes, the getEuler math is bad. It is returning Eurler angles based on the Quaternion, but they're not necessarily expressed in a single axis (moreover the axis you want), so a Z rotation may show up as a combination of other rotations that don't actually involve the Z axis. Better to set all rotations as a quat and keep track of you're own Eulers on the side. This is a very old comment / problem, but if I get a better solution I'll post it here . . .

@Ahbee
Ahbee commented Mar 21, 2014

I think your right. I tried this code and both cones are oriented correctly

void ofApp::draw(){
    ofEnableDepthTest();
    ofConePrimitive cone;
    cone.setScale(2);
    // set cone to random orientation
    cone.roll(20);
    cone.pan(-34);
    cone.tilt(50);


    easyCam.begin();
    // draw flat plane and axis for a reference
    ofDrawAxis(1000);
    ofPlanePrimitive plane;
    plane.tilt(-90);
    plane.setScale(10);
    ofSetColor(ofColor::pink);
    plane.draw();

    // draw first cone
    cone.setPosition(-100, 0, 0);
    cone.drawAxes(50);
    ofSetColor(ofColor::white);
    cone.draw();
    ofSetColor(ofColor::black);
    cone.drawWireframe();

    // draw second cone
    cone.setPosition(100, 0, 0);
    // replace getOrientationQuat() with getOrientationEuler() for error
    cone.setOrientation(cone.getOrientationQuat());
    cone.drawAxes(50);
    ofSetColor(ofColor::white);
    cone.draw();
    ofSetColor(ofColor::black);
    cone.drawWireframe();

    easyCam.end();

}

this is the result
screen

but when I replaced getOrientationQuat() with getOrientationEuler().I got this
screen2
the orientation of the second cones is messed up

@Ahbee Ahbee pushed a commit that referenced this issue Mar 21, 2014
Ahbee fixes issue #679
bank = x-axis, heading = y axis, attitude = z axis.
2a1fad3
@Ahbee
Ahbee commented Mar 21, 2014

I think the guy who implemented ofQuaternion::getEuler() made a typo. There is a link in the comments to this: http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm. I looked at the openframeworks code and the code in the link. openframeworks confuses bank with attitude. see the standards http://www.euclideanspace.com/maths/standards/index.htm. I changed that in the pull request. Also the standard rotation order is here openframeworks also messes that up.

@tgfrerer
openFrameworks member

makes sense! i think especially with methods like these (which can have more than one mathematical meaning) it'd be good to have the return values explained in a comment or documentation - and i think the comment you put in is helpful, too =)

@structuresound

Re: (which can have more than one mathematical meaning)

yes! it seems like expecting the Euler angles you are looking for from a Quat is bad practice given multiple representations. Something I am just learning. Instead it seems best practices for a "getRoll()" type function (which is how this convo started) would be asking for a single axis of rotation based on a comparison vector.

something like:

float getRotationAboutVector(ofVec3f vector, ofVec4f quat);

so getRoll() would internally supply the correct vec3 (node's up vector), so that we get the rotation in the correct euler axis and it doesn't end up as a composite of 3 axis that may or may not be correct.

@structuresound

OK so I've ported that Java code to oF / C, it works to a certain point, but can't sort out negative rotations without some crossproduct math that I'm not sure how to implement. It calculates the shortest positive rotation. I think the moral of my story is just stop using Eulers for anything, and get hip to quats / mats.

Here's code in case it's useful to someone.

void getOrthonormals(ofVec3f normal, ofVec3f *orthonormal1, ofVec3f *orthonormal2)
{
    ofMatrix4x4 OrthoX = ofMatrix4x4().newRotationMatrix(90, ofVec3f(1,0,0));
    ofMatrix4x4 OrthoY = ofMatrix4x4().newRotationMatrix(90, ofVec3f(0,1,0));

    ofVec3f w = transformByMatrix(normal, &OrthoX);

    float dot = normal.dot(w);

    if (fabsf(dot) > 0.6)
    {
        w = transformByMatrix(normal, &OrthoY);
        OrthoY * normal;
    }
    w.normalize();

    *orthonormal1 = normal.cross(w);
    orthonormal1->normalize();
    *orthonormal2 = normal.cross(*orthonormal1);
    orthonormal2->normalize();
}

float getQuaternionTwist(ofQuaternion q, ofVec3f axis)
{
    axis.normalize();

    //get the plane the axis is a normal of
    ofVec3f orthonormal1, orthonormal2;

    getOrthonormals(axis, &orthonormal1, &orthonormal2);

    ofVec3f transformed = orthonormal1 * q;

    //project transformed vector onto plane
    ofVec3f flattened = transformed - transformed.dot(axis) * axis;
    flattened.normalize();


    //get angle between original vector and projected transform to get angle around normal
    float a = (float)acosf(orthonormal1.dot(flattened));

    return a;

}

ofVec3f transformByMatrix(ofVec3f v, ofMatrix4x4* m)
{
    ofVec3f result;
    for ( int i = 0; i < 4; ++i )
        result[i] = v[0] * m->_mat[0][i] + v[1] * m->_mat[1][i] + v[2] + m->_mat[2][i] + v[3] * m->_mat[3][i];
    result[0] = result[0]/result[3];
    result[1] = result[1]/result[3];
    result[2] = result[2]/result[3];
    return result;
}
@tgfrerer
openFrameworks member
@Ahbee
Ahbee commented Mar 21, 2014

@structuresound, I think you should open a new issue for this, because what your asking for is a feature unrelated to the original bug. Start a new issue and say you want these functions implemneted.

ofQuaternion::ofQuaternion(const ofQuaternion &twist, const ofQuaternion &swing);
ofQuaternion ofQuaternion::makeRotate(const ofQuaternion &twist, const ofQuaternion &swing);
ofQuaternion ofQuaternion::getTwist(const ofVec3f &twistAxis,const ofVec3f &swingAxis) const;
ofQuaternion ofQuaternion::getSwing(const ofVec3f &twistAxis,const ofVec3f &swingAxis) const;
@arturoc arturoc pushed a commit that closed this issue Apr 8, 2014
Ahbee fixes #679
bank = xAxis,heading =  yAxis, attitude = zAxis .
The rotation order is Y,Z,X; OpenFrameworks works in row Major so, the
Quaternion multiplication order is- xRotation * zRotation * yRotation.
ddd9900
@arturoc arturoc closed this in ddd9900 Apr 8, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.