Polyframe
A fast, flexible, zero-cost Python library for 3D homogeneous transforms and arbitrary coordinate-system conventions.
🚀 Features
-
Arbitrary Cartesian frames
Define any right- or left-handed(x,y,z)basis via a simple enum.
Zero-cost: all “bring your own conventions” work happens at import time. -
Flexible spherical conventions
Compute azimuth/elevation, φ/θ or latitude/longitude with pluggable:- signed vs. unsigned angles
- CW vs. CCW positive rotation
- polar vs. elevation definitions
- custom “up”/“forward”/“lateral” axes
-
Effortless re-framing
Change the basis of aTransformin one call:from polyframe import FrameRegistry, Direction, Transform tr_world = Transform.from_values(translation=[1,2,3]) cs_robot = FrameRegistry.from_directions( Direction.RIGHT, Direction.FORWARD, Direction.UP ) tr_robot = tr_world.change_coordinate_system(cs_robot)
-
Quaternion & Euler support
Apply quaternion or Euler rotations directly:tr = Transform() tr_q = tr.apply_quaternion([0,0,0,1]) # [x,y,z,w] tr_e = tr.apply_euler_rotation(roll, pitch, yaw)
-
Distance & direction utilities
distance_to(),vector_to(),direction_to()
-
Numba-accelerated core routines
Under the hood, heavy linear-algebra paths (look_at, angle computations, quaternion ↔ matrix) are JIT-compiled for max throughput. -
Memory-efficient
Coordinate frames are static types; no per-instance overhead for storing conventions.
📦 Installation
pip install polyframe
🎬 Quickstart
from polyframe import FrameRegistry, Direction, Transform
# 1) identity at origin, facing +X
tr = Transform()
# 2) translate to (1,2,3)
tr = tr.apply_translation([1,2,3])
# 3) rotate via Euler angles
tr = tr.apply_euler_rotation(roll=0, pitch=45, yaw=90)
# 4) apply a quaternion
tr = tr.apply_quaternion([0,0,0,1]) # x,y,z,w
# 5) make it look at a point
tr = tr.look_at([4,5,6])
# 6) compute spherical angles
az, el, rng = tr.az_el_range_to([7,8,9])
# 7) compute φ/θ
phi, theta = tr.phi_theta_to([1,1,0])
# 8) distance/direction helpers
dist = tr.distance_to([2,2,2])
vec = tr.vector_to([2,2,2])
dirn = tr.direction_to([2,2,2])
# 9) re-frame into a robot basis
robot_cs = FrameRegistry.from_directions(
Direction.FORWARD, Direction.DOWN, Direction.LEFT
)
tr_robot = tr.change_coordinate_system(robot_cs)
🔍 Coordinate-System Conventions
Cartesian frames
Choose from any of 48 valid (x_dir,y_dir,z_dir) triples:
from polyframe import Direction, FrameRegistry
# World: X forward, Y left, Z up
world_cs = FrameRegistry.from_directions(
Direction.FORWARD,
Direction.LEFT,
Direction.UP,
)
# Robot: X right, Y forward, Z down
robot_cs = FrameRegistry.from_directions(
Direction.RIGHT,
Direction.FORWARD,
Direction.DOWN,
)
Each frame type exposes .forward / .backward / .left / .right / .up / .down unit vectors.
Spherical coordinates
- Azimuth/Elevation (
az_el_range_to) - Longitude/Latitude (
lat_lon_to) - φ/θ (polar) (
phi_theta_to)
All accept flags:
| Flag | Meaning |
|---|---|
degrees |
degrees (default) or radians |
signed_* |
signed (±180°/π) vs. unsigned (0…360°/2π) |
counterclockwise_* |
CCW-positive vs. CW-positive |
polar (φ/θ only) |
θ as polar (0…π) vs. elevation (±90°) |
flip_* |
flip sign of elevation/latitude/θ |
⚙️ API Highlights
from polyframe import Transform
# Construction
tr = Transform() # identity
tr2 = Transform.from_values(
translation=[1,2,3],
rotation=[[...]], # 3×3
scale=[2,2,2],
)
# Translation / Rotation / Scale
tr_t = tr.apply_translation([1,0,0])
tr_r = tr.apply_rotation(R) # 3×3
tr_q = tr.apply_quaternion(q) # 4-vector [x,y,z,w]
tr_e = tr.apply_euler_rotation(roll,pitch,yaw)
tr_s = tr.apply_scale([2,3,4])
# Inverse / Combine
inv = tr.inverse()
inv_ip = tr.inverse(inplace=True)
C = tr @ tr2 # compose
# Point / Vector
p2 = tr.transform_point([1,2,3])
v2 = tr.transform_vector([1,0,0])
# Re-frame
tr_new = tr.change_coordinate_system(other_frame)
# Look-at
tr_look = tr.look_at([x,y,z])
# Distance & Direction
d = tr.distance_to([x,y,z])
v = tr.vector_to([x,y,z])
dir = tr.direction_to([x,y,z])
# Angles
az, el, rng = tr.az_el_range_to([x,y,z])
phi, th = tr.phi_theta_to([x,y,z])
lat, lon = tr.lat_lon_to([x,y,z])
See the full docs.
🤝 Contributing
- Fork
- Add a branch
- Write tests under
tests/ - PR — must pass CI (including Numba builds)
📄 License
Apache 2.0 — see LICENSE.
Metadata
Release files for polyframe 0.1.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| polyframe-0.1.5.tar.gz | 30.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| polyframe-0.1.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.6 kB
Release files / polyframe-0.1.5.tar.gz
| Download URL | polyframe-0.1.5.tar.gz |
|---|---|
| Size | 30.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ca4eb56a4d99d01d8296550d1103a7355b4f9be9a398f960ca9b828ac62eabe4
|
|
BLAKE2b-256 checksum How to use checksums |
8ffdf776758974da66af1c0b918dd9c3d2c8d470ab02bd3e67dc0af0093ea4ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
Release files / polyframe-0.1.5-py3-none-any.whl
| Download URL | polyframe-0.1.5-py3-none-any.whl |
|---|---|
| Size | 21.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
83b2228b998182443bae47097cedf8c960800ee16c983f615a0e3803b8c5e335
|
|
BLAKE2b-256 checksum How to use checksums |
33e1ab33d70c86e96138f103abf8bf1cd0abc64d80f730aec049203638b4563b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|