@viamrobotics/motion-tools 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,3 +72,11 @@ VITE_CONFIGS='
72
72
  The visualizer includes a golang package that allows executing commands to the visualizer.
73
73
 
74
74
  The list of available commands [can be found here](https://pkg.go.dev/github.com/viam-labs/motion-tools@v0.9.0/client/client).
75
+
76
+ ### Programmatic camera control
77
+
78
+ It is possible to programmatically move the viewer camera and even modify the camera settings during runtime.
79
+
80
+ To do this, open the Javascript console while using the visualizer and call methods or set properties on the `cameraControls` object.
81
+
82
+ The following APIs are available: https://github.com/yomotsu/camera-controls?tab=readme-ov-file#properties
@@ -1,5 +1,22 @@
1
+ <script
2
+ module
3
+ lang="ts"
4
+ >
5
+ export const RefetchRates = {
6
+ OFF: -1,
7
+ MANUAL: 0,
8
+ FPS_60: 17,
9
+ FPS_30: 33,
10
+ MS_500: 500,
11
+ MS_1000: 1000,
12
+ MS_2000: 2000,
13
+ MS_5000: 5000,
14
+ MS_10000: 10_000,
15
+ } as const
16
+ </script>
17
+
1
18
  <script lang="ts">
2
- import { Select } from '@viamrobotics/prime-core'
19
+ import { Select, IconButton } from '@viamrobotics/prime-core'
3
20
  import { useMachineSettings } from '../hooks/useMachineSettings.svelte'
4
21
  import type { Snippet } from 'svelte'
5
22
 
@@ -7,46 +24,70 @@
7
24
  id: string
8
25
  label: string
9
26
  allowLive?: boolean
27
+ onManualRefetch: () => void
10
28
  children?: Snippet
11
29
  }
12
30
 
13
- let { id, label, allowLive = false, children }: Props = $props()
31
+ let { id, label, allowLive = false, onManualRefetch, children }: Props = $props()
14
32
 
15
33
  const { refreshRates } = useMachineSettings()
16
- const rate = $derived(refreshRates.get(id))
34
+ const rate = $derived(refreshRates.get(id) ?? RefetchRates.MANUAL)
17
35
  </script>
18
36
 
19
37
  <label class="flex flex-col gap-1">
20
38
  {label}
21
- <Select
22
- onchange={(event: InputEvent) => {
23
- if (event.target instanceof HTMLSelectElement) {
24
- const { value } = event.target
25
- refreshRates.set(id, Number.parseInt(value, 10))
26
- }
27
- }}
28
- value={String(rate ?? '')}
29
- >
30
- {#if children}
31
- {@render children()}
32
- {:else}
33
- <option value="-1">Do not fetch</option>
34
- <option value="0">Do not refresh</option>
35
- {#if allowLive}
36
- <option value="17">60fps</option>
37
- <option value="33">30fps</option>
39
+ <div class="flex items-center gap-1">
40
+ <Select
41
+ style="
42
+ -webkit-appearance: none;
43
+ -moz-appearance: none;
44
+ appearance: none;
45
+ "
46
+ onchange={(event: InputEvent) => {
47
+ if (event.target instanceof HTMLSelectElement) {
48
+ const { value } = event.target
49
+ refreshRates.set(id, Number.parseInt(value, 10))
50
+ }
51
+ }}
52
+ value={String(rate)}
53
+ >
54
+ {#if children}
55
+ {@render children()}
56
+ {:else}
57
+ <option value={String(RefetchRates.OFF)}>Do not fetch</option>
58
+ <option value={String(RefetchRates.MANUAL)}>Manual</option>
59
+ {#if allowLive}
60
+ <option value={String(RefetchRates.FPS_60)}>60fps</option>
61
+ <option value={String(RefetchRates.FPS_30)}>30fps</option>
62
+ {/if}
63
+ <option value={String(RefetchRates.MS_500)}>Refresh every 0.5 second</option>
64
+ <option value={String(RefetchRates.MS_1000)}>Refresh every second</option>
65
+ <option value={String(RefetchRates.MS_2000)}>Refresh every 2 seconds</option>
66
+ <option value={String(RefetchRates.MS_5000)}>Refresh every 5 seconds</option>
67
+ <option value={String(RefetchRates.MS_10000)}>Refresh every 10 seconds</option>
38
68
  {/if}
39
- <option value="500">Refresh every 0.5 second</option>
40
- <option value="1000">Refresh every second</option>
41
- <option value="2000">Refresh every 2 seconds</option>
42
- <option value="5000">Refresh every 5 seconds</option>
43
- <option value="10000">Refresh every 10 seconds</option>
69
+ </Select>
70
+
71
+ {#if rate === RefetchRates.MANUAL}
72
+ <IconButton
73
+ icon="refresh"
74
+ label="refetch"
75
+ variant="secondary"
76
+ cx="border-light border"
77
+ onclick={() => {
78
+ onManualRefetch()
79
+ }}
80
+ />
81
+ {:else}
82
+ <IconButton
83
+ icon={rate === RefetchRates.OFF ? 'play-circle-outline' : 'pause'}
84
+ label="pause"
85
+ variant="secondary"
86
+ cx="border-light border"
87
+ onclick={() => {
88
+ refreshRates.set(id, RefetchRates.MANUAL)
89
+ }}
90
+ />
44
91
  {/if}
45
- </Select>
92
+ </div>
46
93
  </label>
47
-
48
- <style>
49
- label :global svg {
50
- display: none;
51
- }
52
- </style>
@@ -1,8 +1,20 @@
1
+ export declare const RefetchRates: {
2
+ readonly OFF: -1;
3
+ readonly MANUAL: 0;
4
+ readonly FPS_60: 17;
5
+ readonly FPS_30: 33;
6
+ readonly MS_500: 500;
7
+ readonly MS_1000: 1000;
8
+ readonly MS_2000: 2000;
9
+ readonly MS_5000: 5000;
10
+ readonly MS_10000: 10000;
11
+ };
1
12
  import type { Snippet } from 'svelte';
2
13
  interface Props {
3
14
  id: string;
4
15
  label: string;
5
16
  allowLive?: boolean;
17
+ onManualRefetch: () => void;
6
18
  children?: Snippet;
7
19
  }
8
20
  declare const RefreshRate: import("svelte").Component<Props, {}, "">;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { Select, Switch, Input } from '@viamrobotics/prime-core'
3
+ import { useQueryClient } from '@tanstack/svelte-query'
3
4
  import RefreshRate from '../RefreshRate.svelte'
4
5
  import { useMotionClient } from '../../hooks/useMotionClient.svelte'
5
6
  import Drawer from './Drawer.svelte'
@@ -10,6 +11,7 @@
10
11
  import WeblabActive from '../weblab/WeblabActive.svelte'
11
12
  import { WEBLABS_EXPERIMENTS } from '../../hooks/useWeblabs.svelte'
12
13
 
14
+ const queryClient = useQueryClient()
13
15
  const partID = usePartID()
14
16
  const cameras = useResourceNames(() => partID.current, 'camera')
15
17
  const settings = useSettings()
@@ -28,10 +30,16 @@
28
30
  id={RefreshRates.poses}
29
31
  label="Poses"
30
32
  allowLive
33
+ onManualRefetch={() => {
34
+ queryClient.refetchQueries({ queryKey: ['getPose', 'getGeometries'], exact: false })
35
+ }}
31
36
  />
32
37
  <RefreshRate
33
38
  id={RefreshRates.pointclouds}
34
39
  label="Pointclouds"
40
+ onManualRefetch={() => {
41
+ queryClient.refetchQueries({ queryKey: ['getPointCloud'], exact: false })
42
+ }}
35
43
  />
36
44
  <div>
37
45
  <div>Enabled pointcloud cameras</div>
@@ -10,6 +10,7 @@ import { useLogs } from './useLogs.svelte';
10
10
  import { resourceColors } from '../color';
11
11
  import { Color } from 'three';
12
12
  import { useFrames } from './useFrames.svelte';
13
+ import { RefetchRates } from '../components/RefreshRate.svelte';
13
14
  const key = Symbol('geometries-context');
14
15
  export const provideGeometries = (partID) => {
15
16
  const frames = useFrames();
@@ -32,9 +33,9 @@ export const provideGeometries = (partID) => {
32
33
  const results = [];
33
34
  for (const client of clients) {
34
35
  const options = queryOptions({
35
- enabled: interval !== -1 && client.current !== undefined,
36
- refetchInterval: interval === 0 ? false : interval,
37
- queryKey: ['partID', partID(), client.current?.name, 'getGeometries'],
36
+ enabled: interval !== RefetchRates.OFF && client.current !== undefined,
37
+ refetchInterval: interval === RefetchRates.MANUAL ? false : interval,
38
+ queryKey: ['getGeometries', 'partID', partID(), client.current?.name],
38
39
  queryFn: async () => {
39
40
  if (!client.current) {
40
41
  throw new Error('No client');
@@ -8,6 +8,7 @@ import { RefreshRates, useMachineSettings } from './useMachineSettings.svelte';
8
8
  import { WorldObject } from '../WorldObject.svelte';
9
9
  import { usePersistentUUIDs } from './usePersistentUUIDs.svelte';
10
10
  import { useLogs } from './useLogs.svelte';
11
+ import { RefetchRates } from '../components/RefreshRate.svelte';
11
12
  const key = Symbol('pointcloud-context');
12
13
  export const providePointclouds = (partID) => {
13
14
  const logs = useLogs();
@@ -20,11 +21,11 @@ export const providePointclouds = (partID) => {
20
21
  for (const cameraClient of clients) {
21
22
  const name = cameraClient.current?.name ?? '';
22
23
  const options = queryOptions({
23
- enabled: interval !== -1 &&
24
+ enabled: interval !== RefetchRates.OFF &&
24
25
  cameraClient.current !== undefined &&
25
26
  disabledCameras.get(name) !== true,
26
- refetchInterval: interval === 0 ? false : interval,
27
- queryKey: ['partID', partID(), name, 'getPointCloud'],
27
+ refetchInterval: interval === RefetchRates.MANUAL ? false : interval,
28
+ queryKey: ['getPointCloud', 'partID', partID(), name],
28
29
  queryFn: async () => {
29
30
  if (!cameraClient.current) {
30
31
  throw new Error('No camera client');
@@ -9,6 +9,7 @@ import { useEnvironment } from './useEnvironment.svelte';
9
9
  import { observe } from '@threlte/core';
10
10
  import { untrack } from 'svelte';
11
11
  import { useFrames } from './useFrames.svelte';
12
+ import { RefetchRates } from '../components/RefreshRate.svelte';
12
13
  export const usePose = (name, parent) => {
13
14
  const { refreshRates } = useMachineSettings();
14
15
  const partID = usePartID();
@@ -21,11 +22,11 @@ export const usePose = (name, parent) => {
21
22
  const client = createResourceClient(MotionClient, () => partID.current, () => motionClient.current ?? '');
22
23
  const interval = $derived(refreshRates.get(RefreshRates.poses));
23
24
  const options = $derived(queryOptions({
24
- enabled: interval !== -1 &&
25
+ enabled: interval !== RefetchRates.OFF &&
25
26
  client.current !== undefined &&
26
27
  environment.current.viewerMode === 'monitor',
27
- refetchInterval: interval === 0 ? false : interval,
28
- queryKey: ['partID', partID.current, client.current?.name, 'getPose', name(), parent()],
28
+ refetchInterval: interval === RefetchRates.MANUAL ? false : interval,
29
+ queryKey: ['getPose', 'partID', partID.current, client.current?.name, name(), parent()],
29
30
  queryFn: async () => {
30
31
  if (!client.current) {
31
32
  throw new Error('No client');
@@ -55,5 +55,12 @@ export const provideWeblabs = () => {
55
55
  setContext(WEBLABS_CONTEXT_KEY, createWeblabs());
56
56
  };
57
57
  export const useWeblabs = () => {
58
- return getContext(WEBLABS_CONTEXT_KEY);
58
+ const context = getContext(WEBLABS_CONTEXT_KEY);
59
+ if (!context) {
60
+ return {
61
+ load: () => { },
62
+ isActive: () => false,
63
+ };
64
+ }
65
+ return context;
59
66
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viamrobotics/motion-tools",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Motion visualization with Viam",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",