Mount & Workspace Transforms
Two transforms position the device in application space. Understanding the pipeline helps you pick the right one.
Coordinate pipeline
| Stage | What it does | How to set it | Persistent? |
|---|---|---|---|
| Basis | Remaps axes (e.g., Z-up → Y-up) | session.configure.basis | Yes |
| Mount transform | Physical mounting offset of the device (arm position, rotation, scale) | configure.mount or configure.preset | Yes |
| Workspace transform | Camera / scene navigation drift | commands.set_transform or the Navigation module | Yes (sticky) |
Mount transform
The mount describes where the device physically sits — its arm offset, orientation, and scale. It's set once (or when the user changes the physical setup) and rarely updated at runtime.
Presets (factory configurations)
Instead of computing the mount yourself, pick a named preset:
| Preset | Origin placement | Arm facing |
|---|---|---|
defaults | Device base | Forward (arm front) |
arm_front | Device base | Forward |
arm_front_centered | Workspace centre | Forward |
led_front | Device base | LED side forward |
led_front_centered | Workspace centre | LED side forward |
custom | Manual — set mount yourself | — |
A preset sets the mount, basis, and workspace origin in one shot.
Setting the mount directly
- WebSocket
- HTTP
{
"inverse3": [{
"device_id": "049D",
"configure": {
"mount": {
"transform": {
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
}
}
}]
}
curl -X POST "http://localhost:10001/inverse3/049D/config/mount?session=:0" \
-H "Content-Type: application/json" \
-d '{"transform":{"position":{"x":0,"y":0,"z":0},"rotation":{"w":1,"x":0,"y":0,"z":0},"scale":{"x":1,"y":1,"z":1}}}'
mount and preset are mutually exclusiveSending both in the same message is ambiguous — the service rejects it.
Choose one: a named preset (which sets the mount for you) or an explicit
mount transform.
When sending a mount, wrap the transform: { "mount": { "transform": { … } } }.
When reading a mount from the snapshot, it's flat: { "mount": { "position": …, "rotation": …, "scale": … } }.
This is by design — commands use the unified command_data wrapper; snapshots
serialize the core::transform directly.
Workspace transform
The workspace transform is a runtime drift applied on top of the mount. Its typical use case is scene navigation — panning, rotating, or zooming the application's view while the device stays physically in place.
Both mount and workspace transforms are persistent — the service remembers the last value you sent. The difference is purpose and performance:
- Mount is designed to be set once and stay — it describes the physical setup and is optimised for infrequent updates.
- Workspace transform is designed to be streamed at high frequency (e.g., every frame during camera navigation) — the internal pipeline is optimised for this update rate.
- WebSocket
- HTTP
{
"inverse3": [{
"device_id": "049D",
"commands": {
"set_transform": {
"transform": {
"position": { "x": 1.0, "y": 0.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0, "w": 1.0 },
"scale": { "x": 1.0, "y": 1.0, "z": 1.0 }
}
}
}
}]
}
curl -X POST "http://localhost:10001/inverse3/049D/state/transform?session=:0" \
-H "Content-Type: application/json" \
-d '{"transform":{"position":{"x":1,"y":0,"z":0},"rotation":{"w":1,"x":0,"y":0,"z":0},"scale":{"x":1,"y":1,"z":1}}}'
GET returns the current transform; DELETE resets to identity.
If you need continuous locomotion (bubble navigation, rate-control drift),
use the Navigation module instead of manually
streaming set_transform every tick — it handles the physics, haptic
feedback, and workspace bounding for you.