Skip to content

Navigation integration guide

To add Rotatrix navigation, connect your application’s camera and scene APIs to the OpenAxis SDK. Your integration can live in a plugin or in the application’s own code. Rotatrix computes navigation; your application applies the resulting poses and renders the scene.

This guide follows the Python, C#, TypeScript and C++ reference integrations. Start with the Navigation quickstart to run a demo and try its controls, then use the matching language tab below to follow its implementation. See SDK installation for adding the SDK to your own project.

The application owns rendering, camera, cursor, selection, picking and pivot drawing. Its integration supplies adapters, scheduling, connection and cleanup. The entry point attaches after startup and detaches before destroying the window.

SourceResponsibility
application.pyNative application API and rendering
integration.pyOpenAxis integration
main.pyStartup and shutdown

MyApplication exposes get_camera(), set_camera(), get_viewport_size() and set_pivot_marker() for the integration to call.

The SDK handles communication and navigation requests. Integration code connects those requests to an application’s camera and scene API. The main pieces are:

ComponentProvided byRole
OpenAxisClientSDKExchanges messages with Rotatrix over WebSocket.
NavigationSessionSDKHandles navigation requests, asks for scene information, and applies camera updates through the adapter.
ApplicationHost applicationOwns the camera and scene and exposes the API used by the integration.
Navigation adapterIntegrationImplements the callbacks of NavigationSession by calling the application API.
SchedulerIntegrationQueues SDK work on the application’s thread, where its API can safely be called.
OpenAxisConnectionManagerSDKKeeps the client connected and reannounces application metadata.

The client carries the messages; the NavigationSession interprets the navigation requests; the adapter reads or changes application state. The scheduler lets the session perform that work on the application thread.

A query travels from the server through the client and session to the adapter, which reads the application and returns scene facts. A resulting camera pose travels through the same components to update the native camera. The integration owns these components from application startup until shutdown; the connection manager handles reconnects during that lifetime.

The demos implement these roles with MyApplication, MyNavigationAdapter and MyApplicationScheduler. At startup, MyOpenAxisIntegration creates the client, adapter and scheduler, then supplies them to a NavigationSession:

The demo creates the application and integration, then starts the integration once the application’s event loop is running. url is the Rotatrix WebSocket address, which defaults to ws://127.0.0.1:6607:

async def main(url):
app = MyApplication()
integration = MyOpenAxisIntegration(app, url)
# The application starts its event loop before loading the integration.
# Without OpenAxis, this is simply: await app.run()
await app.run(on_started=integration.start, on_stopping=integration.stop)

Inside MyOpenAxisIntegration.start(), self.app is the running demo and self.url is the server address:

app, url = self.app, self.url
client = OpenAxisClient(client_name='python-demo-3d-app', url=url,
target=Target(pid=current_process_id(), app='python-demo-3d-app'))
adapter = MyNavigationAdapter(app)
scheduler = MyApplicationScheduler(asyncio.get_running_loop())
session = NavigationSession(client, adapter, scheduler,
# Additional options omitted.
)

The native demos set target.pid with the SDK process identity helper so Rotatrix can match the connection to the foreground application instance. The helper returns a string and includes the PID namespace on Linux, including inside Flatpak. See the target declaration contract for wire format and matching rules.

The demos start the connection automatically and stop it when the application closes. See connection recovery and shutdown when adapting that lifecycle to your application.

MyNavigationAdapter connects the SDK’s navigation operations to the demo’s application API. It provides the methods the NavigationSession calls to read and update the camera, display a pivot, and request scene information. The demo supplies this adapter because the SDK does not know the application’s camera or scene API.

NavigationSession calls the adapter on the application thread to read the camera, apply a pose received from Rotatrix, or display a pivot. The application may constrain a requested camera pose. Returning the resulting pose lets NavigationSession continue navigation from the camera’s actual state.

The adapter’s context keeps navigation tied to the view it started in. A user may switch views or close a document while a camera update is pending. Checking the context lets NavigationSession discard work for a view that is no longer valid, rather than applying it to whichever view is now active.

class MyNavigationAdapter:
def __init__(self, app):
self.app = app
def capture_context(self):
return self.app if self.app.alive else None
def is_current(self, context):
return context is self.app and context.alive
def read(self, context):
camera = context.get_camera()
return CameraPose(t=camera.position, r=camera.rotation,
fov=camera.vertical_fov, ortho_extent=camera.vertical_span)
def begin_query(self, context):
return MyQueryCapture(context, self.read(context))
def apply_camera(self, context, desired, navigation, pivot):
context.set_camera(MyCamera(desired.t, desired.r, desired.fov, desired.ortho_extent))
return WriteResult(True, self.read(context))
def show_pivot(self, context, point):
context.set_pivot_marker(point)

In apply_camera(), context is the captured application and desired is the SDK’s requested CameraPose. MyCamera is the application’s camera type; WriteResult reports whether the write succeeded and the resulting camera pose.

The demos use OpenAxis world conventions; your application may need coordinate conversion.

Rotatrix needs information about the application’s scene to calculate navigation: the camera’s current pose, the viewport’s proportions, and the geometry under the cursor. It requests this information through scene queries.

In Python/C#/TypeScript, a query capture holds the camera, viewport and cursor state used to answer a query. NavigationSession obtains the capture through the adapter on the application thread, then asks its resolver for each requested fact. The SDK sends the responses to Rotatrix.

The excerpts below introduce camera and orientation responses; some also include viewport responses. The complete implementations also answer picking and bounds queries and record diagnostics; those details are omitted here.

The adapter’s begin_query() passes its current camera reading to the capture. resolve(name) returns the requested value, or UNAVAILABLE when the demo cannot provide it:

class MyQueryCapture:
def __init__(self, app, camera):
self.app, self.camera = app, camera
self.width, self.height = app.get_viewport_size()
self.cursor = app.get_cursor_position()
def initial_camera_observation(self):
return self.camera
def resolve(self, name):
if name == 'world.orientation':
return {'forward': (0, 0, -1), 'up': (0, 1, 0), 'handedness': 'right'}
if name == 'camera.pose':
return self.camera.value()
if name == 'navigation.translation_scale':
# World units per quarter ball turn in Rotatrix at unit gain.
return 4.0
if name == 'viewport.aspect':
return self.width / self.height
if name == 'viewport.cursor':
return UNAVAILABLE if self.cursor is None else (
2*self.cursor[0]/self.width - 1, 1 - 2*self.cursor[1]/self.height)
# Picking and bounds responses omitted.
return UNAVAILABLE

The picking recipe covers geometry queries, including model and selection bounds, and the resulting pivot display. The diagnostics recipe shows how the integration records query activity.

In your application, verify that orbit and pan move the camera in the expected direction. Test perspective and orthographic views where supported, resize the viewport, and confirm that closing or replacing it stops writes to the old target. Use the integration checklist for the full host checks.

If the camera does not respond, check the connection using the connection recipe, then use diagnostics to inspect queries and camera writes.

Adapt the demonstrated camera and scene API calls to your application. Consult coordinates when converting native camera values and adapter contracts for exact callback requirements.

Choose the follow-up that matches your next task:

Use the integration checklist when adapting these application API calls to your own host.