Source code for linkforge.blender.utils.transform_utils

"""Utilities for managing object transforms and parenting."""

from __future__ import annotations

from typing import Any

from linkforge.core import Transform, Vector3
from linkforge.core._utils.math_utils import clean_float

try:
    from mathutils import Matrix
except ImportError:
    Matrix = None  # type: ignore[assignment,misc]


[docs] def matrix_to_transform(matrix: Any) -> Transform: """Convert a Blender 4x4 matrix to a Core Transform. Args: matrix: Blender mathutils.Matrix (4x4) Returns: Core Transform with XYZ position and RPY rotation. """ if matrix is None or Matrix is None: return Transform.identity() translation = matrix.to_translation() rotation = matrix.to_euler("XYZ") return Transform( xyz=Vector3( clean_float(translation.x), clean_float(translation.y), clean_float(translation.z), ), rpy=Vector3( clean_float(rotation.x), clean_float(rotation.y), clean_float(rotation.z), ), )
[docs] def set_parent_keep_transform(child_obj: Any, parent_obj: Any) -> None: """Set object parent while preserving its world transform (visual location/rotation). This matches standard Blender 'Object (Keep Transform)' behavior by setting matrix_parent_inverse to the inverse of the parent's world matrix. Args: child_obj: The Blender object to be parented parent_obj: The Blender object to become the parent """ if not child_obj or not parent_obj: return # Store current world state pk_mw = child_obj.matrix_world.copy() # Apply parenting child_obj.parent = parent_obj # Set the inverse matrix to cancel out the parent's current world transform. # This effectively isolates the child from the parent's scale. child_obj.matrix_parent_inverse = parent_obj.matrix_world.inverted() # Restore world transform to ensure no drift child_obj.matrix_world = pk_mw
[docs] def clear_parent_keep_transform(child_obj: Any) -> None: """Clear object parent while preserving its world transform. Args: child_obj: The Blender object to unparent """ if not child_obj: return # Store current world state pk_mw = child_obj.matrix_world.copy() # Remove parent child_obj.parent = None # Restore world transform (since parent is now None, World == Local) child_obj.matrix_world = pk_mw