Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: SpinStep CI

on:
push:
branches: [main, dev, 'features/cuda']
branches: [main, dev, 'features/cuda', 'feature/*']
pull_request:
branches: [main, dev]
branches: [main, dev, 'feature/*']

permissions:
contents: read
Expand Down
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,55 @@ and uses [Semantic Versioning](https://semver.org/).

---

## [0.5.0a0] – 2026-04-10

### Added
- **Control subsystem** (`spinstep.control`):
- `OrientationState` — observer-centered spherical state (quaternion + distance)
- `ControlCommand` — angular + radial velocity commands
- `ProportionalOrientationController` — P-controller with rate limiting
- `PIDOrientationController` — PID controller with anti-windup
- `OrientationTrajectory`, `TrajectoryInterpolator`, `TrajectoryController` —
waypoint-based trajectory tracking with SLERP interpolation
- `integrate_state()`, `compute_orientation_error()` — state utilities
- **Math library** (`spinstep.math`):
- `core` — `quaternion_multiply`, `quaternion_conjugate`, `quaternion_normalize`,
`quaternion_inverse`
- `interpolation` — `slerp` (spherical linear), `squad` (spherical cubic)
- `geometry` — `quaternion_distance`, `rotate_quaternion`,
`forward_vector_from_quaternion`, `direction_to_quaternion`,
`angle_between_directions`, `is_within_angle_threshold`
- `conversions` — `quaternion_from_euler`, `rotation_matrix_to_quaternion`,
`quaternion_from_rotvec`, `quaternion_to_rotvec`
- `analysis` — `batch_quaternion_angle`, `angular_velocity_from_quaternions`,
`get_relative_spin`, `get_unique_relative_spins`, `NodeProtocol`
- `constraints` — `clamp_rotation_angle`
- `NodeProtocol` — `typing.Protocol` for any object with `.orientation` attribute,
replacing `object` type hints in `get_relative_spin()` and
`get_unique_relative_spins()`
- `Node.add_child()` method for ergonomic tree building
- Comprehensive API stability tests (`test_api.py`) — 97 parametrized tests
covering all subpackage exports and backward compatibility
- `Typing :: Typed` classifier in `pyproject.toml`
- `Changelog` and `Documentation` project URLs
- `[[tool.mypy.overrides]]` for `scipy.*`, `sklearn.*`, `healpy.*`, `cupy.*`
- CI workflow now triggers on `feature/*` branches

### Changed
- **Traversal classes moved** to `spinstep.traversal` subpackage
(`node.py`, `continuous.py`, `discrete.py`, `discrete_iterator.py`).
Top-level imports via `from spinstep import Node` remain backward-compatible.
- **`spinstep.utils` is now a backward-compatible re-export layer**.
All 13 quaternion functions now delegate to `spinstep.math` — the single
source of truth. Direct `from spinstep.utils import …` continues to work.
- Version bumped to `0.5.0a0`

### Deprecated
- Direct imports from `spinstep.utils.quaternion_utils` and
`spinstep.utils.quaternion_math`. Use `spinstep.math` instead.

---

## [0.3.0a0] – 2026-03-26

### Added
Expand Down
19 changes: 17 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"

[project]
name = "spinstep"
version = "0.3.1a1"
description = "Quaternion-based tree traversal for orientation-aware structures."
version = "0.5.0a0"
description = "Quaternion-based orientation control and traversal in observer-centered spherical coordinates."
authors = [{ name = "Eraldo Marques", email = "eraldo.bernardo@gmail.com" }]
readme = "README.md"
license = "MIT"
Expand All @@ -20,6 +20,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering :: Mathematics",
"Typing :: Typed",
]
dependencies = [
"numpy>=1.22",
Expand All @@ -43,11 +44,16 @@ dev = [

[project.urls]
Repository = "https://github.com/VoxleOne/SpinStep"
Changelog = "https://github.com/VoxleOne/SpinStep/blob/main/CHANGELOG.md"
Documentation = "https://github.com/VoxleOne/SpinStep/tree/main/docs"

[tool.setuptools.packages.find]
include = ["spinstep*"]
exclude = ["benchmark*", "demos*", "examples*", "tests*", "docs*"]

[tool.setuptools.package-data]
spinstep = ["py.typed"]

[tool.pytest.ini_options]
testpaths = ["tests"]

Expand All @@ -57,5 +63,14 @@ line-length = 88
[tool.mypy]
strict = true

[[tool.mypy.overrides]]
module = [
"scipy.*",
"sklearn.*",
"healpy.*",
"cupy.*",
]
ignore_missing_imports = true

[tool.ruff]
line-length = 88
78 changes: 62 additions & 16 deletions spinstep/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,78 @@
# Author: Eraldo B. Marques <eraldo.bernardo@gmail.com> — Created: 2025-05-14
# See LICENSE.txt for full terms. This header must be retained in redistributions.

"""SpinStep: A quaternion-driven traversal framework.
"""SpinStep: Quaternion-based orientation control and traversal.

Provides quaternion-based tree traversal for orientation-aware structures,
supporting both continuous and discrete rotation stepping.
SpinStep uses an observer-centered spherical model where every guided
vehicle (node) is located by a quaternion (direction from the observer)
and a radial distance (layer).

Example usage::
Control layer (primary API)::

from spinstep import Node, QuaternionDepthIterator
from spinstep import (
OrientationState, ControlCommand,
ProportionalOrientationController, PIDOrientationController,
OrientationTrajectory, TrajectoryController,
integrate_state, compute_orientation_error,
slerp,
)

root = Node("root", [0, 0, 0, 1])
child = Node("child", [0, 0, 0.1, 0.995])
root.children.append(child)
Traversal layer (original tree-walking API)::

step = [0, 0, 0.05, 0.9987] # small rotation about Z
for node in QuaternionDepthIterator(root, step):
print(node.name)
from spinstep.traversal import (
Node, QuaternionDepthIterator,
DiscreteOrientationSet, DiscreteQuaternionIterator,
)

Math layer::

from spinstep.math import quaternion_multiply, quaternion_distance, slerp
"""

__version__ = "0.3.0a0"
__version__ = "0.5.0a0"

# --- control layer (primary API) ---
from .control.state import (
ControlCommand,
OrientationState,
compute_orientation_error,
integrate_state,
)
from .control.controllers import (
OrientationController,
PIDOrientationController,
ProportionalOrientationController,
)
from .control.trajectory import (
OrientationTrajectory,
TrajectoryController,
TrajectoryInterpolator,
)

# --- key math utilities at top level ---
from .math.interpolation import slerp

from .node import Node
from .traversal import QuaternionDepthIterator
from .discrete import DiscreteOrientationSet
from .discrete_iterator import DiscreteQuaternionIterator
# --- backward-compatible traversal re-exports ---
from .traversal.node import Node
from .traversal.continuous import QuaternionDepthIterator
from .traversal.discrete import DiscreteOrientationSet
from .traversal.discrete_iterator import DiscreteQuaternionIterator

__all__ = [
# control
"OrientationState",
"ControlCommand",
"integrate_state",
"compute_orientation_error",
"OrientationController",
"ProportionalOrientationController",
"PIDOrientationController",
"OrientationTrajectory",
"TrajectoryInterpolator",
"TrajectoryController",
# math
"slerp",
# traversal (backward compat)
"Node",
"QuaternionDepthIterator",
"DiscreteOrientationSet",
Expand Down
51 changes: 51 additions & 0 deletions spinstep/control/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# control/__init__.py — MIT License
# Author: Eraldo B. Marques <eraldo.bernardo@gmail.com> — Created: 2025-05-14
# See LICENSE.txt for full terms. This header must be retained in redistributions.

"""Observer-centered orientation control: state, controllers, and trajectories.

The SpinStep control model places the observer at the origin. Every
guided vehicle (node) is located by a quaternion (direction from observer)
and a radial distance (layer). Controllers produce commands with both
angular and radial velocity components.

Sub-modules:

- :mod:`~.state` — :class:`OrientationState`, :class:`ControlCommand`,
integration, error computation
- :mod:`~.controllers` — proportional and PID orientation controllers
- :mod:`~.trajectory` — waypoint trajectories and trajectory tracking
"""

__all__ = [
# state
"OrientationState",
"ControlCommand",
"integrate_state",
"compute_orientation_error",
# controllers
"OrientationController",
"ProportionalOrientationController",
"PIDOrientationController",
# trajectory
"OrientationTrajectory",
"TrajectoryInterpolator",
"TrajectoryController",
]

from .state import (
ControlCommand,
OrientationState,
compute_orientation_error,
integrate_state,
)
from .controllers import (
OrientationController,
PIDOrientationController,
ProportionalOrientationController,
)
from .trajectory import (
OrientationTrajectory,
TrajectoryController,
TrajectoryInterpolator,
)
Loading
Loading