
spatial-transform
Lightweight libary for creating hierarchies in a 3D space, like Unity, Unreal, Blender or any other 3D application.
Properties like positions, rotations, directions and scales can be easily accessed and are calculated based on the parents space for the world space. Individual transforms can be attatched and detatched at any point and have some more comfort methods for easy modifications.
Why and intention
This libary is a side product of my master thesis, in order to extract conveniently local and world data features from a humanoid skeleton hierarchy. I could not find any libary that could do that, without bloat or the features I required for extraction or modification.
Installation
pip install spatial-transform
Notes
Pose
is the class for all local space properties and operations. There is no awareness about other related space or hierarchy.Transform
extend the Pose
class to add hierarchical wareness and provides additional properties and methods for the world space.Euler
is a class with static members only for converting euler angle into quaternions or matrices. It supports diffrent rotation orders and can be used to convert between- The package PyGLM is used for matrix, quaternion and vector calculations.
- Same coordination space as openGL and GLM is used. Which is: Right-Handed, - Y+ is up, Z- is forward and positive rotations are counter clockwise.
Examples
Create and attach transforms
from SpatialTransform import Transform, Euler
hips = Transform('Hips', position=(0,2,0))
LeftLegUpper = Transform('LeftLegUpper', position=(+0.2,0,0))
LeftLegLower = Transform('LeftLegLower', position=(0,-1,0))
LeftLegFoot = Transform('LeftLegFoot', position=(0,-1,0))
RightLegUpper = Transform('RightLegUpper', position=(-0.2,0,0))
RightLegLower = Transform('RightLegLower', position=(0,-1,0))
RightLegFoot = Transform('RightLegFoot', position=(0,-1,0))
hips.attach(LeftLegUpper)
LeftLegUpper.attach(LeftLegLower)
LeftLegLower.attach(LeftLegFoot)
hips.attach(RightLegUpper)
RightLegUpper.attach(RightLegLower)
RightLegLower.attach(RightLegFoot)
hips.printTree()
print('\nWorld positions, local positions, joint directions:')
for item, index, depth in hips.layout():
print(f'{item.PositionWorld} {item.Position} {item.ForwardWorld} {item.Name}')
Interacting with transforms
from SpatialTransform import Transform
root = Transform()
root.PositionWorld = (1,2,3)
root.Scale = .1
root.RotationWorld = (1, 0, 0, 0)
root.setEuler((0, 90, 0))
root.getEuler(order='ZYX')
root.lookAtWorld((1, 1, 1))
root.clearParent(keep=['position', 'rotation', 'scale'])
root.clearChildren(keep=['position', 'rotation', 'scale'])
root.applyPosition()
root.applyRotation(recursive=True)
root.appyScale(recursive=True)
root.pointToWorld((5,4,3))
root.directionToLocal((2,3,4))
Fluent interface usage
from SpatialTransform import Transform
hips = Transform('Hips', position=(0,2,0)).attach(
Transform('LeftLegUpper', position=(+0.2,0,0)).attach(
Transform('LeftLegLower', position=(0,-1,0)).attach(
Transform('LeftLegFoot', position=(0,-1,0))
)
),
Transform('RightLegUpper', position=(-0.2,0,0)).attach(
Transform('RightLegLower', position=(0,-1,0)).attach(
Transform('RightLegFoot', position=(0,-1,0))
)
)
)
feets = hips.setEuler((0, 180, 0)).applyRotation().filter('Foot')
hips.printTree()
print('\nPositions:')
for item, index, depth in hips.layout():
print(f'{item.PositionWorld} {item.Position} {item.Name}')
Euler angles conversions
from SpatialTransform import Euler
matrix = Euler.toMatFrom((1, 2, .5), order='YZX', extrinsic=True)
quaternion = Euler.toQuatFrom((1, 2, .5), order='YZX', extrinsic=True)
angles1 = Euler.fromMatTo(matrix, order='XYZ', extrinsic=False)
angles2 = Euler.fromQuatTo(quaternion, order='XYZ', extrinsic=False)
print(angles1 - angles2)