Closed Mimickal closed 4 years ago
Something helpful here to emulate the recalculation on rotation_mode
assignment:
@property
def x(self): return self[0]
@x.setter
def x(self, new_val): self[0] = float(new_val)
Some functions that will be very helpful
def euler_to_quaternion(y, p, r):
'''Angles are expected to be in radians.'''
c0, c1, c2 = cos(y / 2), cos(p / 2), cos(r / 2)
s1, s2, s3 = sin(y / 2), sin(p / 2), sin(r / 2)
return (s1*s2*c2*+ c0*c1*s3,
s1*c1*c2 + c0*s2*s3,
c0*s2*c2 - s1*c1*s3,
c0*c1*c2 - s1*s2*s3)
def quaternion_to_euler(i, j, k, w):
'''Angles returned are in radians.'''
p_sin = 2*(i * j + k * w);
# check for singularities at north and south poles
if p_sin > 0.99999999999999:
return 2 * atan2(i, w), pi / 2, 0
elif p_sin < -0.99999999999999:
return -2 * atan2(i, w), -pi / 2, 0
else:
y = atan2(2*(j*w - i*k), 1 - 2*(j**2 - k**2));
p = asin(p_sin);
r = atan2(2*(i*w - j*k), 1 - 2*(i**2 - k**2))
return y, p, r
mathutils handles almost all of this for us. There's an Euler and Quaternion class that handles all the math for converting between the two, the same way Blender does it.
The only trick right now is it seems the standalone version of mathutils we're using seems to be outdated, and doesn't yet support the Euler((1, 2, 3), 'XYZ')
constructor. Or if it does, it expects it in a way different than Blender 2.80's scripting terminal does.
Rotation in Blender is more complicated than it first appears. The rotation mode an individual object uses is set as follows:
Through a little bit of experimentation, we've discovered the following:
rotation_mode
, Blender actually recalculates the internal rotation values on the fly, presumably from the previous rotation state. For example:((0.0, 0.0, 0.0,), 'XYZ')
(1.0, 0.0, 0.0, 0.0)
.((0.0, -0.0, 1.571), 'XYZ')
(1.0, 0.0, 0.0, 0.0)
QUATERNION
(WXYZ)((0.0, -0.0, 1.571), 'XYZ')
(0.707, 0.0, 0.0, 0.707)
((0.0, -0.0, 1.571), 'XYZ')
(0.707, 0.0, 0.0, 1.0)
((0.0, -0.0, 1.911), 'XYZ')
(0.577, 0.0, 0.0, 0.816)
((0.0, -0.0, 3.293), 'XYZ')
(0.577, 0.0, 0.0, 0.816)
These quirks need to be (somewhat) accurately captured by the test mocks. Accurately enough that a script run against the mocks produces nearly-identical results to running that same script against real Blender.