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
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Keep this example small, browser-native, and easy to inspect.
## Runtime and dependencies

- GitHub Pages output must be self-contained at runtime. Keep Three.js and the built SDK copied into the assembled artifact by `scripts/assemble-site.mjs`.
- Keep application-side tracking buffering bounded: one pending frame per Scene, continuous state latest-wins, transient cluster/zone edges conflated rather than queued as full frames.
- Keep Three.js point-cloud buffers owned by the renderer. Do not attach SDK point arrays directly because they may be views into a complete decompressed WebSocket payload; reuse capacity and dispose replaced geometries.
- Avoid adding dependencies for behavior that can stay simple and local.

## Validation
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ The example is deliberately both a **reference integration** and a **debug viewe

The page includes a **Simulate data** toggle, so the UI and rendering can be tested without an Augmenta server. The synthetic setup uses the same World → Scene → Zone hierarchy as a live setup. While active, the button reads **Simulating** and clicking it again stops the simulation.

Connection settings, display toggles, the preferred Scene, sidebar state, section/Advanced fold state, and the current camera position/orbit target are stored in browser `localStorage` and restored on refresh. The bottom-left QR code opens the same page with the current Augmenta address, port, protocol and downsample embedded in the URL. Those shared URL values are a tab-local override. Editing one connection field updates only that field in this browser's saved defaults; the other QR-provided values stay temporary and independent. Transient runtime state such as connection status, simulation state, received tracking data, and debug contents is intentionally not persisted.
Connection settings, display toggles, the preferred Scene, sidebar state, main-section fold state, the Connection Advanced fold state, and the current camera position/orbit target are stored in browser `localStorage` and restored on refresh. The bottom-left QR code opens the same page with the current Augmenta address, port, protocol and downsample embedded in the URL. Those shared URL values are a tab-local override. Editing one connection field updates only that field in this browser's saved defaults; the other QR-provided values stay temporary and independent. Transient runtime state such as connection status, simulation state, received tracking data, and debug contents is intentionally not persisted.

> GitHub Pages is served over HTTPS. The example still tries `ws://` first, then `wss://`, because local Augmenta outputs commonly expose a plain WebSocket endpoint.

## What it shows

The viewer requests the richest practical uncompressed stream from the Augmenta WebSocket Output:
The viewer requests the richest practical Zstd-compressed stream from the Augmenta WebSocket Output:

- automatic protocol selection: try V3 first, then reconnect with the server-reported V2/V3 parser when needed;
- clusters and stable IDs / UUIDs;
Expand All @@ -39,7 +39,9 @@ Three.js renders:
- a Pleiades-style orbit camera, 1 × 1 m floor grid and axes;
- the Pleiades-style view cube: a bare draggable cube in perspective and an **Ortho** panel with Front/Back/Left/Right/Top/Bottom shortcuts in orthographic mode. The panel × returns to the remembered perspective view. Its labels follow the Three.js world axes directly: Front = +Z, Back = -Z, Right = +X, Left = -X, Top = +Y, Bottom = -Y.

Mouse navigation follows the Pleiades viewer philosophy: left-drag orbits, right-drag pans parallel to the floor, and middle-drag/wheel zooms. The perspective camera can continue roughly 90° past the horizontal floor view, stopping only near the opposite pole, while broad finite distance/zoom limits prevent runaway navigation. The ViewCube itself can also be dragged to orbit the camera directly with the same horizontal/vertical drag directions as the main Three.js orbit controls; a short click on a cube face switches to the matching orthographic view, while pointer capture begins only after the gesture becomes a drag. The ViewCube follows one simple projection rule: perspective uses only the bare cube, while orthographic mode uses the full **Ortho** panel and view shortcuts. With the menu unfolded, the perspective cube remains visible; with the menu folded, it appears only after the camera actually starts moving during a drag and fades out gently afterward, so a simple click never flashes the cube. The Ortho panel stays visible regardless of menu state. Switching between orthographic presets updates the camera and selected button immediately, while the cube keeps a short 3D rotation animation for orientation feedback. Entering Ortho from perspective follows Pleiades' direct position/target framing motion, with the same 320 ms cubic-bezier timing as the cube and a projection-scale interpolation that starts from the current perspective framing. While Ortho is active, **Tab** cycles through the method views in their displayed order — Top, Front, Right, Left, Back, Bottom — while **Shift+Tab** goes in reverse; **Escape** exits Ortho without folding the main menu. There is no separate ISO shortcut anymore: the panel × exits orthographic mode and returns to the remembered perspective composition. Dragging out of Ortho is different by design: it keeps the current camera radius, target and direction and matches the orthographic framing with perspective optical zoom. This mirrors Pleiades' radius-based orthographic framing and avoids moving the camera in or out during the projection switch. In Ortho, pan and zoom stay orthographic. Dragging the ViewCube or main 3D view out of Ortho converts the current view to matching perspective framing as soon as the drag becomes a real orbit, then continues the same drag in perspective. Folding the menu still hides the QR code. The Ortho panel × returns to the last perspective composition (including its orbit, pan and zoom) rather than resetting it; **Reset camera** explicitly replaces that remembered perspective view with the automatic home framing, so a later full setup can frame its new bounds normally. The selected projection, orthographic zoom, camera position and orbit target are persisted after navigation and restored on refresh; setup updates do not overwrite a restored/user-controlled view. Without a saved/user-controlled view, a successful connection setup initializes the same centered framing used by **Reset camera**. **Simulate data**, **Reset camera**, or a left-button double-click reframes the displayed setup. The sidebar uses two lightweight levels of disclosure: each main section can be folded to a compact status row, and each section can expose a denser **Advanced** subsection for less-used controls. In **Connection**, the server address, Connect/Simulate controls, and live connection log stay primary while Port, Protocol and Point downsample live under Advanced. **Display** keeps the Scene plus common visibility toggles primary, with **Reset camera** beside the Scene label and Velocity vectors under Advanced. **Live debug data** stays readable as the main content with a lightweight **Clear data** action above the inspector. While the Live debug data section is folded, only its compact summary is refreshed; the detailed debug HTML is not rebuilt until the section is opened again. While interacting with Frame / Objects / Zones / Control disclosures, debug DOM refreshes briefly pause so a live 4 Hz update cannot replace the clicked disclosure between pointer-down and click. The Display panel exposes **All scenes** plus every Scene received in the current World, with independent **Scenes** and **Zones** visibility toggles. On desktop, the translucent panel overlays the 3D view: use the edge arrow to hide/show it, or drag its left edge to resize it. On phone/touch layouts, the same control becomes a small horizontal handle at the viewer/menu boundary; folding the menu expands the viewer to the full viewport while keeping the handle available to reopen it. **Escape** folds the panel. **H** toggles the viewer title/subtitle as a transient display shortcut; unlike the other UI/display preferences, this title visibility is intentionally not persisted. On desktop, after 3 seconds without mouse movement or keyboard input, the handle fades whether the panel is unfolded or folded; activity brings it back quickly. On phone/touch layouts, the handle stays visible so the folded menu can always be reopened. When folded, the sidebar and QR code are hidden and removed from interaction; the ViewCube follows the contextual perspective/orthographic rules above. The camera projection follows the panel width so the orbit target remains centered in the unobscured part of the view.
Mouse navigation follows the Pleiades viewer philosophy: left-drag orbits, right-drag pans parallel to the floor, and middle-drag/wheel zooms. The perspective camera can continue roughly 90° past the horizontal floor view, stopping only near the opposite pole, while broad finite distance/zoom limits prevent runaway navigation. The ViewCube itself can also be dragged to orbit the camera directly with the same horizontal/vertical drag directions as the main Three.js orbit controls; a short click on a cube face switches to the matching orthographic view, while pointer capture begins only after the gesture becomes a drag. The ViewCube follows one simple projection rule: perspective uses only the bare cube, while orthographic mode uses the full **Ortho** panel and view shortcuts. With the menu unfolded, the perspective cube remains visible; with the menu folded, it appears only after the camera actually starts moving during a drag and fades out gently afterward, so a simple click never flashes the cube. The Ortho panel stays visible regardless of menu state. Switching between orthographic presets updates the camera and selected button immediately, while the cube keeps a short 3D rotation animation for orientation feedback. Entering Ortho from perspective follows Pleiades' direct position/target framing motion, with the same 320 ms cubic-bezier timing as the cube and a projection-scale interpolation that starts from the current perspective framing. While Ortho is active, **Tab** cycles through the method views in their displayed order — Top, Front, Right, Left, Back, Bottom — while **Shift+Tab** goes in reverse; **Escape** exits Ortho without folding the main menu. There is no separate ISO shortcut anymore: the panel × exits orthographic mode and returns to the remembered perspective composition. Dragging out of Ortho is different by design: it keeps the current camera radius, target and direction and matches the orthographic framing with perspective optical zoom. This mirrors Pleiades' radius-based orthographic framing and avoids moving the camera in or out during the projection switch. In Ortho, pan and zoom stay orthographic. Dragging the ViewCube or main 3D view out of Ortho converts the current view to matching perspective framing as soon as the drag becomes a real orbit, then continues the same drag in perspective. Folding the menu still hides the QR code. The Ortho panel × returns to the last perspective composition (including its orbit, pan and zoom) rather than resetting it; **Reset camera** explicitly replaces that remembered perspective view with the automatic home framing, so a later full setup can frame its new bounds normally. The selected projection, orthographic zoom, camera position and orbit target are persisted after navigation and restored on refresh; setup updates do not overwrite a restored/user-controlled view. Without a saved/user-controlled view, a successful connection setup initializes the same centered framing used by **Reset camera**. **Simulate data**, **Reset camera**, or a left-button double-click reframes the displayed setup. The sidebar keeps the main sections foldable to compact status rows. **Connection** also exposes an **Advanced** subsection for less-used transport controls. In **Connection**, the server address, Connect/Simulate controls, and live connection log stay primary while Port, Protocol and Point downsample live under Advanced. **Display** keeps the Scene plus all visibility toggles primary, including **Velocity vectors**, with **Reset camera** beside the Scene label. **Live debug data** stays readable as the main content with a lightweight **Clear data** action above the inspector. While the Live debug data section is folded, only its compact summary is refreshed; the detailed debug HTML is not rebuilt until the section is opened again. While interacting with Frame / Objects / Zones / Control disclosures, debug DOM refreshes briefly pause so a live 4 Hz update cannot replace the clicked disclosure between pointer-down and click. The Display panel exposes **All scenes** plus every Scene received in the current World, with independent **Scenes** and **Zones** visibility toggles. On desktop, the translucent panel overlays the 3D view: use the edge arrow to hide/show it, or drag its left edge to resize it. On phone/touch layouts, the same control becomes a small horizontal handle at the viewer/menu boundary; folding the menu expands the viewer to the full viewport while keeping the handle available to reopen it. **Escape** folds the panel. **H** toggles the viewer title/subtitle as a transient display shortcut; unlike the other UI/display preferences, this title visibility is intentionally not persisted. On desktop, after 3 seconds without mouse movement or keyboard input, the handle fades whether the panel is unfolded or folded; activity brings it back quickly. On phone/touch layouts, the handle stays visible so the folded menu can always be reopened. When folded, the sidebar and QR code are hidden and removed from interaction; the ViewCube follows the contextual perspective/orthographic rules above. The camera projection follows the panel width so the orbit target remains centered in the unobscured part of the view.

Tracking-frame delivery is also bounded at the renderer boundary. The WebSocket/SDK parser can receive data faster than the browser display loop, so the viewer keeps at most one pending frame per Scene. Continuous state is latest-wins, while cluster transition states and zone enter/leave counters are conflated into the newest pending frame. Replaced frames never keep their old point clouds alive. Point-cloud rendering uses owned reusable GPU buffers with draw ranges, so SDK views into decompressed WebSocket payloads are not retained by Three.js and small point-count fluctuations do not force a buffer allocation every frame.

Partial setup updates are merged into the cached hierarchy before rendering, so a Scene update that omits its Zone children does not erase them. Updates that race ahead of the initial full setup are ignored rather than promoted to an incomplete root. When a specific Scene is selected, live setup updates still render from the World root so ancestor transforms remain intact. Zone-event caches are pruned against the latest setup hierarchy so renamed/removed addresses do not accumulate stale visual state. A zone stops rendering 50 ms after its live event heartbeat disappears, so disabled zones vanish quickly while their setup definition remains available. Scene selection and scene-size debug information therefore always use the latest valid merged setup.

Expand Down Expand Up @@ -104,7 +106,7 @@ The example requests:
streamClusters: true,
streamClusterPoints: true,
streamZonePoints: true,
useCompression: false,
useCompression: true,
displayPointIntensity: true,
boxRotationMode: RotationMode.Quaternions,
axisTransform: {
Expand All @@ -118,7 +120,7 @@ The example requests:
}
```

Compression is disabled because the browser example intentionally stays dependency-free. Applications that need compressed streams can provide the SDK with a Zstd decompressor.
Compression is enabled by default. The example initializes `zstddec` before opening the Augmenta WebSocket and injects its synchronous decoder into the JavaScript SDK. The GitHub Pages artifact vendors the decoder (including its WebAssembly payload), so the deployed viewer remains self-contained at runtime.

The JS SDK parses velocity directly from the binary cluster property at the same offset as the C++ and C# SDKs. It does not reconstruct velocity from positions; if Augmenta sends a zero vector, the example intentionally displays that zero vector rather than inventing a replacement.

Expand Down Expand Up @@ -150,6 +152,7 @@ This separation keeps the SDK reusable by non-Three.js applications and keeps re
│ ├── connection.js # WebSocket/retry/protocol controller
│ ├── setup-store.js # Setup cache + partial update merging
│ ├── motion.js # Consumer-side velocity → speed helper
│ ├── frame-buffer.js # Bounded latest-frame conflation + transient edges
│ ├── qr.js # QR rendering + copy interaction
│ ├── view-cube.js # ViewCube UI, Ortho shortcuts + keyboard navigation
│ ├── view-transition.js # Shared camera/ViewCube transition timing + easing
Expand All @@ -162,6 +165,9 @@ This separation keeps the SDK reusable by non-Three.js applications and keeps re
│ └── styles.css # Debug UI
├── tests/
│ ├── connection-targets.test.mjs # Hostname/IP fallback ordering
│ ├── frame-buffer.test.mjs # Latest-state + transient-event conflation
│ ├── main-ui-state.test.mjs # Removed UI-state regression guard
│ ├── ui-contract.test.mjs # UI map/DOM selector smoke tests
│ ├── demo.test.mjs # Synthetic World/Scene hierarchy
│ ├── setup-store.test.mjs # Partial setup update regression tests
│ ├── share-link.test.mjs # Shared connection URL regression tests
Expand Down
Loading
Loading