diff --git a/leenkx/blender/lnx/logicnode/lnx_sockets.py b/leenkx/blender/lnx/logicnode/lnx_sockets.py index 3ceab3e..0b752c3 100644 --- a/leenkx/blender/lnx/logicnode/lnx_sockets.py +++ b/leenkx/blender/lnx/logicnode/lnx_sockets.py @@ -158,70 +158,90 @@ class LnxRotationSocket(LnxCustomSocket): self.do_update_raw(context) @staticmethod - def convert_to_quaternion(part1,part2,param1,param2,param3): - """converts a representation of rotation into a quaternion. - ``part1`` is a vector, ``part2`` is a scalar or None, - ``param1`` is in ('Quaternion', 'EulerAngles', 'AxisAngle'), - ``param2`` is in ('Rad','Deg') for both EulerAngles and AxisAngle, - ``param3`` is a len-3 string like "XYZ", for EulerAngles """ - if param1=='Quaternion': - qx, qy, qz = part1[0], part1[1], part1[2] - qw = part2 - # need to normalize the quaternion for a rotation (having it be 0 is not an option) - ql = sqrt(qx**2+qy**2+qz**2+qw**2) - if abs(ql)<1E-5: - qx, qy, qz, qw = 0.0,0.0,0.0,1.0 + def convert_to_quaternion(vec3_val, scalar_val, mode, unit, order): + '''Converts Euler or Axis-Angle representation to a Quaternion Vector''' + + if mode == 'Quaternion': + qx, qy, qz = vec3_val[0], vec3_val[1], vec3_val[2] + qw = scalar_val + + ql = sqrt(qx**2 + qy**2 + qz**2 + qw**2) + if abs(ql) < 1E-5: + qx, qy, qz, qw = 0.0, 0.0, 0.0, 1.0 else: qx /= ql qy /= ql qz /= ql qw /= ql - return mathutils.Vector((qx,qy,qz,qw)) + return mathutils.Vector((qx, qy, qz, qw)) - elif param1 == 'AxisAngle': - if param2 == 'Deg': - angle = part2 * pi/180 - else: - angle = part2 - cang, sang = cos(angle/2), sin(angle/2) - x,y,z = part1[0], part1[1], part1[2] - veclen = sqrt(x**2+y**2+z**2) - if veclen<1E-5: - return mathutils.Vector((0.0,0.0,0.0,1.0)) - else: - return mathutils.Vector(( - x/veclen * sang, - y/veclen * sang, - z/veclen * sang, - cang - )) - else: # param1 == 'EulerAngles' - x,y,z = part1[0], part1[1], part1[2] - if param2 == 'Deg': - x *= pi/180 - y *= pi/180 - z *= pi/180 - - euler = mathutils.Euler((x, y, z), param3) - quat = euler.to_quaternion() + elif mode == 'EulerAngles': + x, y, z = vec3_val.to_tuple() + + if unit == 'Deg': + x, y, z = radians(x), radians(y), radians(z) + + angles_ordered = [0.0, 0.0, 0.0] + for i, axis in enumerate(order): + if axis == 'X': + angles_ordered[i] = x + elif axis == 'Y': + angles_ordered[i] = y + elif axis == 'Z': + angles_ordered[i] = z + eul = mathutils.Euler(angles_ordered, order) + quat = eul.to_quaternion() return mathutils.Vector((quat.x, quat.y, quat.z, quat.w)) - + + elif mode == 'AxisAngle': + axis = vec3_val.normalized().to_tuple() + angle = scalar_val + if unit == 'Deg': + angle = radians(angle) + quat = mathutils.Quaternion(axis, angle) + return mathutils.Vector((quat.x, quat.y, quat.z, quat.w)) + + print(f"Warning: Invalid mode '{mode}' in convert_to_quaternion") + return mathutils.Vector((0.0, 0.0, 0.0, 1.0)) + + def do_update_raw(self, context): - part1 = mathutils.Vector(( - self.default_value_s0, - self.default_value_s1, - self.default_value_s2, 1 - )) - part2 = self.default_value_s3 + if self.default_value_mode == 'Quaternion': + # Directly construct the quaternion vector from s0, s1, s2, s3 (x, y, z, w) + vec3_val = mathutils.Vector(( + self.default_value_s0, # X component or Euler X or Axis X + self.default_value_s1, # Y component or Euler Y or Axis Y + self.default_value_s2 # Z component or Euler Z or Axis Z + )) + scalar_val = self.default_value_s3 # W component or Axis Angle - self.default_value_raw = self.convert_to_quaternion( - part1, - self.default_value_s3, - self.default_value_mode, - self.default_value_unit, - self.default_value_order - ) + # Always call the unified conversion function + # The result will be in (x, y, z, w) order + self.default_value_raw = self.convert_to_quaternion( + vec3_val, + scalar_val, + self.default_value_mode, + self.default_value_unit, + self.default_value_order + ) + else: + # Handle EulerAngles and AxisAngle using the conversion helper + vec3_val = mathutils.Vector(( + self.default_value_s0, + self.default_value_s1, + self.default_value_s2 + )) + # s3 is used for AxisAngle angle, irrelevant for Euler + scalar_val = self.default_value_s3 + + self.default_value_raw = self.convert_to_quaternion( + vec3_val, # Vector part (Euler angles X,Y,Z or Axis X,Y,Z) + scalar_val, # Scalar part (Axis angle) + self.default_value_mode, # Mode ('EulerAngles' or 'AxisAngle') + self.default_value_unit, # Unit ('Rad' or 'Deg') + self.default_value_order # Order ('XYZ', 'ZYX', etc. - used only for Euler) + ) def draw(self, context, layout, node, text): @@ -275,9 +295,11 @@ class LnxRotationSocket(LnxCustomSocket): ('YZX','YZX','YZX'), ('ZXY','ZXY','ZXY'), ('ZYX','ZYX','ZYX')], - name='', default='XYZ' + name='', default='XYZ', + update=do_update_raw ) + default_value_s0: FloatProperty(update=do_update_raw) default_value_s1: FloatProperty(update=do_update_raw) default_value_s2: FloatProperty(update=do_update_raw)