So, pymel's Quaternion class currently has some serious unit issues...
before I get into exactly what they were, and what I'd like to do to fix
them, I was wondering if many people actually used them - and if so, how -
because fixing them may break existing code.
Basically, I'm wondering: if you DO use the Quaternion class, do you
generally rely on the output of the Quaternion's component access operations
(ie, indexing, iteration, or accessing by component name: myQuat[2], for x
in myQuat, myQuat.x) being in degrees by default (assuming you have your ui
angle setting set to degrees, the default)? Do you use them, but weren't
even aware this was the case?
If you're wondering why I'm asking, here's the problem in detail, and how
I'm proposing to fix it:
Right now, all assignments are done in units of radians, regardless of what
the internal unit setting for the Quaternion is. Internally, all the data
is stored as radians as well, since it is based on MQuaternion.
However, item retrieval will convert to the Quaternion's unit; which means
that, if the internal unit is not radians, what you set will be different
than what you get... and this will often be the case, since the default
unit is generally degrees.
For instance, in this scenario:
q1 = Quaternion(1,2,3,4)
q2 = Quaterion(*q1)
...q1 and q2 will NOT be equal (as q1 will set it's value in radians, but
spit them back out - for the initialization of q2 - in degrees.
Even more confusing, if we do:
q3 = Quaterion(q1)
...then q1 and q3 WILL be equal.
Also confusing is that even if you explicitly specify a unit, inputs are
ALWAYS interpreted as being in radians... so if you do this:
q4 = Quaternion([30,60,90,120], unit='degrees')
then
list(q4) == [30,60,90,120]
will be False, because 30,60, etc will be interpreted as radians, then
converted to degrees on iteration.
I suspect that most people have simply assumed (as I did) that Quaternions
simply always work in radians - or at least have that unit by default -
since when setting them, the value is always interpreted as being in
radians,
and if you then pass the Quaternion object 'whole' into api methods, which
always expect radians, things work as expected. You only get burned if you
then try to extract individual members of the quaternion - either by
iteration
or component (ie, x,y,z,w) access - which will then be in degrees.
Unfortunately, this means that if we convert assignment values to be
interpreted in the Quaternion's unit setting, I imagine most people's code
will break.
So, we are left with 3 choices, none of which are attractive:
1) Leave things as is: assignment always in radians, extraction in the set
unit
Advantages:
If you don't change anything, you can't break existing code...
Disadvantages:
Assignment and extraction will be in different units, which is pretty
confusing for the reasons I gave above.
2) Make assignments and retrieval always be in the set unit, and leave the
default unit as the current UI unit (ie, generally degrees).
Advantages:
Assignment / extraction is consistent
Default unit behavior is similar to how it's handled with EulerRotation
Disadvantages:
Likely to break people's code, since I imagine most people just always
worked in radians with it.
3) Make assignments and retrieval always be in the set unit, but change the
default unit to always be radians (regardless of UI setting)
Advantages:
Less likely to break people's existing code than 2)
Assignment / extraction now consistent
Disadvantages:
May still break people's code, if they were relying on the default
extraction to be in degrees
Now have inconsistent default unit behavior between EulerRotation and
Quaternion
Unless there's outcry, I'll be implementing option 3...
- Paul
--
http://groups.google.com/group/python_inside_maya