Skip to content
Open
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
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,17 @@ with pyspacemouse.open(

### Custom Axis Mapping

Customize axis directions for specific coordinate conventions (ROS, OpenGL, etc.):
Choose a built-in convention for common application frames:

```python
import pyspacemouse
from pyspacemouse import AxisConvention

with pyspacemouse.open(axis_convention=AxisConvention.ROS) as device:
state = device.read()
```

For custom coordinate frames, remap or invert axes explicitly:

```python
import pyspacemouse
Expand All @@ -189,10 +199,9 @@ import pyspacemouse
specs = pyspacemouse.get_device_specs()
base = specs["SpaceNavigator"]

# Invert axes for your application
custom = pyspacemouse.modify_device_info(
base,
invert_axes=["y", "z", "roll", "yaw"], # Invert these
remap_axes={"x": "y", "y": ("x", -1), "yaw": ("yaw", -1)},
)

with pyspacemouse.open(device_spec=custom) as device:
Expand Down
15 changes: 12 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,17 @@ with pyspacemouse.open(

### Custom Axis Mapping

Customize axis directions for specific coordinate conventions (ROS, OpenGL, etc.):
Choose a built-in convention for common application frames:

```python
import pyspacemouse
from pyspacemouse import AxisConvention

with pyspacemouse.open(axis_convention=AxisConvention.ROS) as device:
state = device.read()
```

For custom coordinate frames, remap or invert axes explicitly:

```python
import pyspacemouse
Expand All @@ -189,10 +199,9 @@ import pyspacemouse
specs = pyspacemouse.get_device_specs()
base = specs["SpaceNavigator"]

# Invert axes for your application
custom = pyspacemouse.modify_device_info(
base,
invert_axes=["y", "z", "roll", "yaw"], # Invert these
remap_axes={"x": "y", "y": ("x", -1), "yaw": ("yaw", -1)},
)

with pyspacemouse.open(device_spec=custom) as device:
Expand Down
15 changes: 10 additions & 5 deletions docs/mouseApi/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ The same `axis_convention` argument is available on `open_by_path()` and
|------------|------------------|------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| `AxisConvention.HID` | `+x` right, `+y` toward the user, `+z` down | right-handed | USB HID convention; see [HID Usage Tables, 4.2 Axis Usages](https://usb.org/document-library/hid-usage-tables-17). |
| `AxisConvention.HID_Z_UP` | `+x` right, `+y` away from the user, `+z` up | right-handed | HID frame, rotated -180 degrees about +X so Z points up |
| `AxisConvention.ROS` | `+x` forward, `+y` left, `+z` up | right-handed | ROS REP 103 body frame. |
| `AxisConvention.UNITY` | `+x` right, `+y` up, `+z` forward | left-handed | Unity world/object frame. |
| `AxisConvention.LEGACY` | `x=+HID_x`, `y=-HID_y`, `z=-HID_z` | `roll=-HID_Ry`, `pitch=-HID_Rx`, `yaw=+HID_Rz` | Deprecated. Default for backward compatibility. |

For new code, select a convention that matches your need. Use `AxisConvention.LEGACY` only for existing code that
Expand Down Expand Up @@ -112,17 +114,20 @@ custom = pyspacemouse.create_device_info(

### `modify_device_info()`

Modify an existing device spec (e.g., to invert axes):
Modify an existing device spec (e.g., to remap or invert axes):

```python
specs = pyspacemouse.get_device_specs()
base = specs["SpaceNavigator"]

# Invert Y and Z for ROS conventions
ros_spec = pyspacemouse.modify_device_info(
custom = pyspacemouse.modify_device_info(
base,
name="SpaceNavigator (ROS)",
invert_axes=["y", "z", "roll", "yaw"],
name="SpaceNavigator (Custom)",
remap_axes={
"x": "y",
"y": ("x", -1),
"yaw": ("yaw", -1),
},
)
```

Expand Down
15 changes: 12 additions & 3 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,27 @@ The library applies some axis inversions to make values more intuitive:
- **Y axis**: Inverted for common conventions
- **Rotations**: pitch/roll inverted from HID spec

If your application needs different conventions (e.g., ROS, OpenGL), use the `modify_device_info()` function:
If your application needs a common coordinate convention, pass it when opening the device:

```python
import pyspacemouse
from pyspacemouse import AxisConvention

with pyspacemouse.open(axis_convention=AxisConvention.ROS) as device:
state = device.read()
```

For custom conventions, use `modify_device_info()` to remap or invert axes:

```python
import pyspacemouse

specs = pyspacemouse.get_device_specs()
base = specs["SpaceNavigator"]

# Customize axes for your application
custom = pyspacemouse.modify_device_info(
base,
invert_axes=["y", "z", "roll", "yaw"], # Axes to flip
remap_axes={"x": "y", "y": ("x", -1), "yaw": ("yaw", -1)},
)

with pyspacemouse.open(device_spec=custom) as device:
Expand Down
14 changes: 9 additions & 5 deletions pyspacemouse/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ def _create_and_open_device(
"AxisConvention.LEGACY is deprecated for built-in device specs "
"and will be removed in a future release. Pass "
"axis_convention=AxisConvention.HID_Z_UP for the recommended "
"right-handed Z-up frame, or AxisConvention.HID for raw HID axes.",
"right-handed Z-up frame, other values on AxisConvention for application-specific frames, or "

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to ignore this, as this is at the discretion of the maintainers, but why would HID_Z_UP be the recommended frame when there are multiple to choose from?
I would expect plain HID (unchanged and standardized, what is sent over the wire) as the recommendation when no specific application is "closer" associated with the library. Or simply give no recommendation to begin with.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! I think this library inherited the odd legacy convention from the pyspacenavigator implementation, and in some sense the correct version of what it was trying to do is HID Z Up: it has the same translation axes just matched with conventional rotation axes.

But if there's an opportunity to reconsider, providing the device's native axes by default feels more neutral/parsimonious. If someone had written code against the raw device they wouldn't have to think much when swapping to use the library. Someone writing fresh code against the library is likely going to think about frame conventions on an application-specific basis, and will flip to the correct one in any case.

Thoughts @JakubAndrysek?

"AxisConvention.HID for raw HID axes.",
DeprecationWarning,
stacklevel=3,
)
Expand Down Expand Up @@ -173,8 +174,9 @@ def open_by_path(
axis_convention: Coordinate convention for axis values. If None,
uses the deprecated legacy convention for backward
compatibility. Use AxisConvention.HID_Z_UP for a
right-handed Z-up frame. Mutually exclusive with
device_spec.
right-handed Z-up frame, AxisConvention.ROS for ROS,
or AxisConvention.UNITY for Unity. Mutually exclusive
with device_spec.

Returns:
SpaceMouseDevice instance (use as context manager for auto-cleanup)
Expand Down Expand Up @@ -280,8 +282,10 @@ def open(
axis_convention: Coordinate convention for axis values. If None,
uses the deprecated legacy convention for backward
compatibility. Use AxisConvention.HID_Z_UP for a
geometrically consistent right-handed Z-up frame, or
AxisConvention.HID for raw HID values (Z down).
geometrically consistent right-handed Z-up frame,
AxisConvention.ROS for ROS, AxisConvention.UNITY for
Unity, or AxisConvention.HID for raw HID values
(Z down).
Mutually exclusive with device_spec.

Returns:
Expand Down
Loading
Loading