{"action":"created","issue":{"url":"https://api.github.com/repos/openframeworks/openFrameworks/issues/3594","labels_url":"https://api.github.com/repos/openframeworks/openFrameworks/issues/3594/labels{/name}","comments_url":"https://api.github.com/repos/openframeworks/openFrameworks/issues/3594/comments","events_url":"https://api.github.com/repos/openframeworks/openFrameworks/issues/3594/events","html_url":"https://github.com/openframeworks/openFrameworks/issues/3594","id":55667211,"number":3594,"title":"ofQuaternion::setEuler(const ofVec3f &)","user":{"login":"elliotwoods","id":328294,"avatar_url":"https://avatars.githubusercontent.com/u/328294?v=3","gravatar_id":"","url":"https://api.github.com/users/elliotwoods","html_url":"https://github.com/elliotwoods","followers_url":"https://api.github.com/users/elliotwoods/followers","following_url":"https://api.github.com/users/elliotwoods/following{/other_user}","gists_url":"https://api.github.com/users/elliotwoods/gists{/gist_id}","starred_url":"https://api.github.com/users/elliotwoods/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elliotwoods/subscriptions","organizations_url":"https://api.github.com/users/elliotwoods/orgs","repos_url":"https://api.github.com/users/elliotwoods/repos","events_url":"https://api.github.com/users/elliotwoods/events{/privacy}","received_events_url":"https://api.github.com/users/elliotwoods/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/openframeworks/openFrameworks/labels/core","name":"core","color":"db6a1f"},{"url":"https://api.github.com/repos/openframeworks/openFrameworks/labels/feature","name":"feature","color":"622425"},{"url":"https://api.github.com/repos/openframeworks/openFrameworks/labels/fix-proposed","name":"fix-proposed","color":"31e03a"},{"url":"https://api.github.com/repos/openframeworks/openFrameworks/labels/section-3D","name":"section-3D","color":"DDDDDD"},{"url":"https://api.github.com/repos/openframeworks/openFrameworks/labels/section-internals","name":"section-internals","color":"DDDDDD"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":4,"created_at":"2015-01-27T20:02:40Z","updated_at":"2015-03-09T06:23:04Z","closed_at":null,"body":"For many applications I'm using Euler angles rather than Quaternions (where they need to be sensibly user-edited).\r\n\r\nCurrently we have an `ofVec3f ofQuaternion::getEuler()` but no `ofQuaternion::setEuler(const ofVec3f &)`.\r\n\r\nAnd I can't seem to find a function on the internet which inverts our `getEuler()` (suggesting that our `getEuler` might also be broken)."},"comment":{"url":"https://api.github.com/repos/openframeworks/openFrameworks/issues/comments/77805822","html_url":"https://github.com/openframeworks/openFrameworks/issues/3594#issuecomment-77805822","issue_url":"https://api.github.com/repos/openframeworks/openFrameworks/issues/3594","id":77805822,"user":{"login":"roymacdonald","id":974878,"avatar_url":"https://avatars.githubusercontent.com/u/974878?v=3","gravatar_id":"","url":"https://api.github.com/users/roymacdonald","html_url":"https://github.com/roymacdonald","followers_url":"https://api.github.com/users/roymacdonald/followers","following_url":"https://api.github.com/users/roymacdonald/following{/other_user}","gists_url":"https://api.github.com/users/roymacdonald/gists{/gist_id}","starred_url":"https://api.github.com/users/roymacdonald/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/roymacdonald/subscriptions","organizations_url":"https://api.github.com/users/roymacdonald/orgs","repos_url":"https://api.github.com/users/roymacdonald/repos","events_url":"https://api.github.com/users/roymacdonald/events{/privacy}","received_events_url":"https://api.github.com/users/roymacdonald/received_events","type":"User","site_admin":false},"created_at":"2015-03-09T06:23:04Z","updated_at":"2015-03-09T06:23:04Z","body":"So I did this:\r\n```\r\n#pragma once\r\n\r\n#include \"ofMain.h\"\r\n#include \"ofxGui.h\"\r\n\r\nofVec3f quatToEuler(const ofQuaternion & rotation) {\r\n\tofVec3f euler;\r\n\tconst static double PI_OVER_2 = PI * 0.5;\r\n\tconst static double EPSILON = 1e-10;\r\n\tdouble sqw, sqx, sqy, sqz;\r\n\t\r\n\t// quick conversion to Euler angles to give tilt to user\r\n\tsqw = rotation[3] * rotation[3];\r\n\tsqx = rotation[0] * rotation[0];\r\n\tsqy = rotation[1] * rotation[1];\r\n\tsqz = rotation[2] * rotation[2];\r\n\t\r\n\teuler[1] = asin(2.0 * (rotation[3] * rotation[1] - rotation[0] * rotation[2]));\r\n\tif (PI_OVER_2 - fabs(euler[1]) > EPSILON) {\r\n\t\teuler[2] = atan2(2.0 * (rotation[0] * rotation[1] + rotation[3] * rotation[2]),\r\n\t\t\t\t\t\t sqx - sqy - sqz + sqw);\r\n\t\teuler[0] = atan2(2.0 * (rotation[3] * rotation[0] + rotation[1] * rotation[2]),\r\n\t\t\t\t\t\t sqw - sqx - sqy + sqz);\r\n\t}\r\n\telse {\r\n\t\t// compute heading from local 'down' vector\r\n\t\teuler[2] = atan2(2 * rotation[1] * rotation[2] - 2 * rotation[0] * rotation[3],\r\n\t\t\t\t\t\t 2 * rotation[0] * rotation[2] + 2 * rotation[1] * rotation[3]);\r\n\t\teuler[0] = 0.0;\r\n\t\t\r\n\t\t// If facing down, reverse yaw\r\n\t\tif (euler[1] < 0)\r\n\t\t\teuler[2] = PI - euler[2];\r\n\t}\r\n\treturn euler;\r\n}\r\n\r\nofQuaternion eulerToQuat(const ofVec3f & rotationEuler) {\r\n\tofQuaternion rotation;\r\n\tfloat c1 = cos(rotationEuler[2] * 0.5);\r\n\tfloat c2 = cos(rotationEuler[1] * 0.5);\r\n\tfloat c3 = cos(rotationEuler[0] * 0.5);\r\n\tfloat s1 = sin(rotationEuler[2] * 0.5);\r\n\tfloat s2 = sin(rotationEuler[1] * 0.5);\r\n\tfloat s3 = sin(rotationEuler[0] * 0.5);\r\n\t\r\n\trotation[0] = c1*c2*s3 - s1*s2*c3;\r\n\trotation[1] = c1*s2*c3 + s1*c2*s3;\r\n\trotation[2] = s1*c2*c3 - c1*s2*s3;\r\n\trotation[3] = c1*c2*c3 + s1*s2*s3;\r\n\t\r\n\treturn rotation;\r\n}\r\n\r\nclass ofApp : public ofBaseApp{\r\n\tpublic:\r\n//--------------------------------------------------------------\r\nvoid setup(){\r\n\tofSetFrameRate(30);\r\n\tofBackground(0);\r\n\tgui.setup();\r\n\tgui.add(rotX.set(\"rotX\", 0, 0, 180));\r\n\tgui.add(rotY.set(\"rotY\", 0, 0, 180));\r\n\tgui.add(rotZ.set(\"rotZ\", 0, 0, 180));\r\n}\r\n//--------------------------------------------------------------\r\nvoid draw(){\r\n\tofQuaternion q;\r\n\tq.makeRotate(rotX, ofVec3f(1, 0, 0), rotY, ofVec3f(0, 1, 0), rotZ, ofVec3f(0, 0, 1));\r\n\t\r\n\tofVec3f e = quatToEuler(eulerToQuat(ofVec3f(rotX*DEG_TO_RAD,rotY*DEG_TO_RAD,rotZ*DEG_TO_RAD)));\r\n\t\r\n\tstring s = \"rotX: \" + ofToString(rotX) + \" rotY: \" + ofToString(rotY) + \" rotZ: \" + ofToString(rotZ) + \"\\n\";\r\n\ts += \"ofQuaternion.getEuler: \" + ofToString(q.getEuler()) + \"\\n\";\r\n\ts += \"quatToEuler: \" + ofToString(ofVec3f(e.x*RAD_TO_DEG,e.y*RAD_TO_DEG,e.z*RAD_TO_DEG));\r\n\tofDrawBitmapString(s, 20, ofGetHeight() - 50);\r\n\tgui.draw();\r\n}\r\n//--------------------------------------------------------------\r\n\tofxPanel gui;\r\n\tofParameter<float> rotX, rotY, rotZ;\r\n\t\r\n};\r\n```\r\nthe functions posted by elliot return the exact same value allways when transforming from euler to quat and the quat to euler.\r\nthe method I mentioned returns the exact same value only when one of the parameters is zero. I guess that it returns a different euler representation of the quaternion. I'll go deeper into the maths tomorrow to see what's going on.\r\nbest!\r\n\r\n"}}