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
5 changes: 5 additions & 0 deletions .changeset/kind-hats-shop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@viamrobotics/motion-tools': patch
---

Add plugin docs and make plugin dependencies optional
79 changes: 77 additions & 2 deletions docs/src/content/docs/plugins/draw-service.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,81 @@
---
title: <DrawService />
description: Stream draw-server output into an embedded motion-tools visualizer.
description: Stream a running draw server's output into an embedded motion-tools visualizer.
---

Documentation coming soon.
`<DrawService />` connects an embedded `<Visualizer />` to a running draw server over WebSocket so any `client/api` calls made elsewhere render in this instance live. Skip it if you only render snapshots — snapshots are entirely client-side and don't need the server.

The plugin is **headless**: it provides the connection context and starts the stream; it doesn't render anything itself.

The Connect RPC client is required for the `<DrawService />`.

## Install

```bash
pnpm add @connectrpc/connect @connectrpc/connect-web
```

## Usage

```svelte
<script lang="ts">
import { Visualizer } from '@viamrobotics/motion-tools'
import { DrawService } from '@viamrobotics/motion-tools/plugins'
</script>

<div class="h-screen w-screen">
<Visualizer>
<DrawService config={{ backendIP: 'localhost', websocketPort: '3030' }} />
</Visualizer>
</div>
```

## Props

| Prop | Type | Description |
| -------- | ---------------------------------------------- | ------------------------------------------------------------- |
| `config` | `{ backendIP: string; websocketPort: string }` | Host and port of the draw server. Default dev port is `3030`. |

The `config` prop is reactive — change it and the plugin reconnects to the new endpoint.

## Running a draw server

`<DrawService />` is a client — it needs a draw server to talk to. You have three options:

### Use the motion-tools local app

The fastest path: run motion-tools locally (`make up` or `pnpm dev`). The local app already hosts a draw server on port `3030`, and `client/api` calls to the same host land in it.

### Host the Go `DrawService` yourself

Motion-tools exports a ready-made `DrawService` from `github.com/viam-labs/motion-tools/draw` — the same Connect-RPC handler the local app uses. Wire it up to your own HTTP server:

```go
package main

import (
"log"
"net/http"

"connectrpc.com/connect"
"github.com/viam-labs/motion-tools/draw"
"github.com/viam-labs/motion-tools/draw/v1/drawv1connect"
)

func main() {
svc := draw.NewDrawService("") // "" = use os.TempDir for chunked buffers

mux := http.NewServeMux()
mux.Handle(drawv1connect.NewDrawServiceHandler(svc))

log.Fatal(http.ListenAndServe(":3030", mux))
}
```

Your `client/api` producer code then points at this server, and `<DrawService config={{ backendIP, websocketPort: '3030' }} />` in the browser subscribes to its stream.

### Implement your own server

If you need a custom backend (different language, different storage, extra side effects), the proto definitions are published under `github.com/viam-labs/motion-tools/draw/v1` (Go) and ship as `.proto` files in the source tree under `protos/draw/v1/`. Implement the `DrawService` RPCs (`AddEntity`, `UpdateEntity`, `RemoveEntity`, `StreamEntityChanges`, …) in whichever language you like; `<DrawService />` just needs a Connect-RPC server at the configured host:port.

See the [`client/api`](../../api/client-api/) reference for the producer-side calls regardless of which server you use.
64 changes: 62 additions & 2 deletions docs/src/content/docs/plugins/selection.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,66 @@
---
title: <SelectionTool />
description: Lasso-pick points and geometries inside a motion-tools visualizer.
description: Lasso- or ellipse-pick points and geometries inside a motion-tools visualizer.
---

Documentation coming soon.
`<SelectionTool />` adds a selection mode to the visualizer — a dashboard toggle plus an on-canvas lasso or ellipse tool that captures the points and geometries inside the drawn region. Selected entities are exposed via the `useSelectionPlugin` hook so you can react to them from your own UI.

Selection mode automatically switches the camera to orthographic so 2D screen-space picking maps cleanly to the scene.

The polygon-triangulation `earcut` library is required for the `<SelectionTool />`.

## Install

```bash
pnpm add earcut
```

## Usage

```svelte
<script lang="ts">
import { Visualizer } from '@viamrobotics/motion-tools'
import { SelectionTool } from '@viamrobotics/motion-tools/plugins'
</script>

<div class="h-screen w-screen">
<Visualizer>
<SelectionTool />
</Visualizer>
</div>
```

A new toggle appears in the top dashboard; clicking it enters selection mode. The popover next to it switches between **Lasso** and **Ellipse** tools.

## Props

| Prop | Type | Default | Description |
| ----------------------- | --------- | ------- | --------------------------------------------------------------------------------------------- |
| `enabled` | `boolean` | `false` | Enter selection mode automatically on mount. |
| `autoSelectNewEntities` | `boolean` | `false` | Auto-select the most recently created selection entity (sets it as the focused entity). |
| `children` | `Snippet` | — | Rendered only while selection mode is active — useful for overlays scoped to the active tool. |

## Reading the selection

Use `useSelectionPlugin` to read the current set of selected entities from anywhere inside `<Visualizer />`:

```svelte
<script lang="ts">
import { useSelectionPlugin } from '@viamrobotics/motion-tools/plugins'

const selection = useSelectionPlugin()
</script>

<p>Selected {selection.current.length} entities</p>
<button onclick={selection.clearSelections}>Clear</button>
```

The hook also exposes `clearSelections()` to remove all selection-marker entities from the world.

## Traits

For lower-level access to selection state (e.g. querying which points fall inside a selected region), the plugin exports its Koota traits under the `selectionTraits` namespace:

```ts
import { selectionTraits } from '@viamrobotics/motion-tools/plugins'
```
31 changes: 30 additions & 1 deletion docs/src/content/docs/plugins/skybox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,33 @@ title: <Skybox />
description: Render an equirectangular skybox dome around the motion-tools scene.
---

Documentation coming soon.
`<Skybox />` wraps the scene in a [grounded skybox dome](https://threejs.org/examples/?q=skybox#webgl_materials_envmaps_groundprojected) loaded from an equirectangular image, anchoring the lower hemisphere to the world XY plane so geometry sits convincingly on the ground.

Use it to add real-world context to a snapshot — a warehouse, a lab, or any 360° photo of the environment the robot operates in.

## Usage

```svelte
<script lang="ts">
import { Visualizer } from '@viamrobotics/motion-tools'
import { Skybox } from '@viamrobotics/motion-tools/plugins'
</script>

<div class="h-screen w-screen">
<Visualizer>
<Skybox url="/environment.jpg" />
</Visualizer>
</div>
```

The image should be a 2:1 equirectangular projection (the standard format for 360° photos). Drop it in your app's `public/` directory or point `url` at any reachable HTTP endpoint.

## Props

| Prop | Type | Default | Description |
| ---------- | ----------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `url` | `string` | — | URL of the equirectangular image. Required. |
| `position` | `[x: number, y: number, z: number]` | `[0, 0, height]` | World-space center of the dome. Default anchors the ground plane at world Z=0. |
| `rotation` | `[x: number, y: number, z: number]` | `[Math.PI / 2, 0, 0]` | Euler rotation in radians. Default aligns the image's vertical axis (+Y) with this scene's vertical axis (+Z); the Z component then acts as yaw. |
| `height` | `number` | `15` | Camera height above ground when the source photo was taken. Larger values magnify the lower portion of the image. |
| `radius` | `number` | `100` | Dome radius. Must exceed the scene camera's reach so the camera stays inside the dome. |
18 changes: 15 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"@ag-grid-community/core": "32.3.9",
"@ag-grid-community/styles": "32.3.9",
"@changesets/cli": "2.29.6",
"@connectrpc/connect": "1.7.0",
"@connectrpc/connect-web": "1.7.0",
"@dimforge/rapier3d-compat": "0.18.2",
"@eslint/compat": "2.0.2",
"@eslint/js": "10.0.1",
Expand Down Expand Up @@ -86,6 +88,7 @@
"@zag-js/tree-view": "1.22.1",
"camera-controls": "3.1.0",
"concurrently": "^9.2.1",
"earcut": "^3.0.2",
"esbuild": "^0.27.3",
"eslint": "10.0.2",
"eslint-config-prettier": "10.1.8",
Expand Down Expand Up @@ -121,6 +124,8 @@
"@ag-grid-community/client-side-row-model": ">=32.3.0",
"@ag-grid-community/core": ">=32.3.0",
"@ag-grid-community/styles": ">=32.3.0",
"@connectrpc/connect": ">=1",
"@connectrpc/connect-web": ">=1",
"@dimforge/rapier3d-compat": ">=0.17",
"@tanstack/svelte-query-devtools": ">=6",
"@threlte/core": ">=8",
Expand All @@ -139,6 +144,7 @@
"@zag-js/toggle-group": ">=1",
"@zag-js/tree-view": ">=1",
"camera-controls": ">=3",
"earcut": ">=3",
"idb-keyval": ">=6",
"lucide-svelte": ">=0.511",
"runed": ">=0.28",
Expand All @@ -147,8 +153,17 @@
"svelte-virtuallists": ">=1"
},
"peerDependenciesMeta": {
"@connectrpc/connect": {
"optional": true
},
"@connectrpc/connect-web": {
"optional": true
},
"@tanstack/svelte-query-devtools": {
"optional": true
},
"earcut": {
"optional": true
}
},
"pnpm": {
Expand Down Expand Up @@ -193,10 +208,7 @@
],
"dependencies": {
"@bufbuild/protobuf": "1.10.1",
"@connectrpc/connect": "1.7.0",
"@connectrpc/connect-web": "1.7.0",
"@neodrag/svelte": "^2.3.3",
"earcut": "^3.0.2",
"filtrex": "^3.1.0",
"koota": "0.6.5",
"lodash-es": "4.18.1",
Expand Down
18 changes: 9 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading