This release is a pre-release and may not be stable for production use.
SpinStep
SpinStep is a quaternion-driven traversal framework for trees and orientation-based data structures. Instead of traversing by position or order, SpinStep uses 3D rotation math to navigate trees based on orientation — making it ideal for robotics, spatial reasoning, 3D scene graphs, and anywhere quaternion math naturally applies.
Overview
SpinStep provides two traversal modes:
- Continuous traversal — apply a single rotation step at each node and visit children whose orientation falls within an angular threshold (
QuaternionDepthIterator). - Discrete traversal — try every rotation from a predefined symmetry group or custom set and visit reachable children (
DiscreteQuaternionIteratorwithDiscreteOrientationSet).
Both modes are built on a simple Node class that stores a name, a unit quaternion orientation [x, y, z, w], and a list of children.
Installation
Requirements: Python 3.9+
Install from source:
git clone https://github.com/VoxleOne/SpinStep.git
cd SpinStep
pip install .
Or install in editable mode for development:
pip install -e ".[dev]"
Quick Start
from spinstep import Node, QuaternionDepthIterator
# Build a small tree with quaternion orientations [x, y, z, w]
root = Node("root", [0, 0, 0, 1], [
Node("child", [0.2588, 0, 0, 0.9659]) # ~30° rotation around Z
])
# Traverse using a 30° rotation step
iterator = QuaternionDepthIterator(root, [0.2588, 0, 0, 0.9659])
for node in iterator:
print("Visited:", node.name)
# Output:
# Visited: root
# Visited: child
Core Concepts
Node
A tree node with a quaternion-based orientation. Each node stores a name, a unit quaternion [x, y, z, w] (automatically normalised), and a list of children.
from spinstep import Node
root = Node("root", [0, 0, 0, 1])
child = Node("child", [0, 0, 0.3827, 0.9239]) # ~45° around Z
root.children.append(child)
QuaternionDepthIterator
Depth-first iterator that applies a continuous rotation step at each node. Only children within an angular threshold of the rotated state are visited.
from spinstep import Node, QuaternionDepthIterator
root = Node("root", [0, 0, 0, 1], [
Node("close", [0.2588, 0, 0, 0.9659]), # ~30° — will be visited
Node("far", [0.7071, 0, 0, 0.7071]), # ~90° — too far
])
for node in QuaternionDepthIterator(root, [0.2588, 0, 0, 0.9659]):
print(node.name)
# Output:
# root
# close
DiscreteOrientationSet
A set of discrete quaternion orientations with spatial querying. Comes with factory methods for common symmetry groups.
from spinstep import DiscreteOrientationSet
cube_set = DiscreteOrientationSet.from_cube() # 24 orientations
icosa_set = DiscreteOrientationSet.from_icosahedron() # 60 orientations
grid_set = DiscreteOrientationSet.from_sphere_grid(200) # 200 Fibonacci-sampled
DiscreteQuaternionIterator
Depth-first iterator that tries every rotation in a DiscreteOrientationSet at each node. Children reachable by any rotation within the angular threshold are visited.
import numpy as np
from spinstep import Node, DiscreteOrientationSet, DiscreteQuaternionIterator
root = Node("root", [0, 0, 0, 1], [
Node("child1", [0, 0, 0.3827, 0.9239]),
Node("child2", [0, 0.7071, 0, 0.7071]),
])
orientation_set = DiscreteOrientationSet.from_cube()
it = DiscreteQuaternionIterator(root, orientation_set, angle_threshold=np.pi / 4)
for node in it:
print(node.name)
# Output:
# root
# child1
# child2
Examples
Continuous Traversal with Custom Threshold
import numpy as np
from spinstep import Node, QuaternionDepthIterator
root = Node("origin", [0, 0, 0, 1], [
Node("alpha", [0.1305, 0, 0, 0.9914]), # ~15°
])
# Use explicit angle threshold of 20° (in radians)
it = QuaternionDepthIterator(root, [0.1305, 0, 0, 0.9914], angle_threshold=np.deg2rad(20))
print([n.name for n in it])
# Output: ['origin', 'alpha']
Query Orientations by Angle
import numpy as np
from spinstep import DiscreteOrientationSet
dos = DiscreteOrientationSet.from_cube()
indices = dos.query_within_angle([0, 0, 0, 1], np.deg2rad(10))
print(f"Found {len(indices)} orientations within 10° of identity")
Optional Dependencies
SpinStep works out of the box with NumPy and SciPy. Two optional dependencies unlock additional features:
| Package | Install | Feature |
|---|---|---|
| CuPy | pip install cupy-cuda12x |
GPU-accelerated orientation storage and angular distance computation |
| healpy | pip install healpy |
HEALPix-based unique relative spin detection via get_unique_relative_spins() |
GPU example:
import numpy as np
from spinstep import DiscreteOrientationSet
orientations = np.random.randn(1000, 4)
# Store orientations on GPU (requires CuPy)
gpu_set = DiscreteOrientationSet(orientations, use_cuda=True)
Development
# Install with development dependencies
pip install -e ".[dev]"
# Run tests
python -m pytest tests/ -v
# Run linter
ruff check spinstep/
Documentation
Full documentation is available in the docs/ directory:
License
MIT — see LICENSE for details.
Metadata
Release files for spinstep 0.6.0a0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| spinstep-0.6.0a0.tar.gz | 38.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| spinstep-0.6.0a0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 88.4 kB
Release files / spinstep-0.6.0a0.tar.gz
| Download URL | spinstep-0.6.0a0.tar.gz |
|---|---|
| Size | 38.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
00fbf06d14070ed3d1e0daea25383b83a11224f5d8c2a3b261315d3970fa0474
|
|
BLAKE2b-256 checksum How to use checksums |
6abc5dc90a07ed0a2d3c4e732a622deafa86262bd10c02986bfc4989c3b0aeee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 14, 2026.
Transparency logRelease files / spinstep-0.6.0a0-py3-none-any.whl
| Download URL | spinstep-0.6.0a0-py3-none-any.whl |
|---|---|
| Size | 50.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8be4fd4567efdca86cfcd23ba4dd5b906aa9eaa3afcc1daa1148a2693ddfee63
|
|
BLAKE2b-256 checksum How to use checksums |
4a6d54407dc522c662b7f25e0dc7de8e02ca7ed9f7d13686d7f5efd8ef97512c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 14, 2026.
Transparency log