@viamrobotics/motion-tools 0.16.4 → 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 +8 -0
- package/dist/components/Geometry.svelte +75 -33
- package/dist/components/RefreshRate.svelte +73 -32
- package/dist/components/RefreshRate.svelte.d.ts +12 -0
- package/dist/components/SceneProviders.svelte +2 -0
- package/dist/components/Tree/Settings.svelte +30 -0
- package/dist/components/xr/XR.svelte.d.ts +4 -3
- package/dist/hooks/use3DModels.svelte.d.ts +7 -0
- package/dist/hooks/use3DModels.svelte.js +59 -0
- package/dist/hooks/useGeometries.svelte.js +4 -3
- package/dist/hooks/usePointclouds.svelte.js +4 -3
- package/dist/hooks/usePose.svelte.js +4 -3
- package/dist/hooks/useSettings.svelte.d.ts +2 -0
- package/dist/hooks/useSettings.svelte.js +3 -0
- package/dist/hooks/useWeblabs.svelte.d.ts +1 -0
- package/dist/hooks/useWeblabs.svelte.js +9 -1
- package/package.json +2 -2
- package/dist/WorldObject.svelte.d.ts +0 -65
- package/dist/geometry.d.ts +0 -4
- package/dist/hooks/usePose.svelte.d.ts +0 -3
- package/dist/hooks/useWorldState.svelte.d.ts +0 -22
- package/dist/transform.d.ts +0 -15
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
|
|
@@ -10,7 +10,14 @@
|
|
|
10
10
|
import type { WorldObject } from '../WorldObject.svelte'
|
|
11
11
|
import { PLYLoader } from 'three/addons/loaders/PLYLoader.js'
|
|
12
12
|
|
|
13
|
+
import { WEBLABS_EXPERIMENTS } from '../hooks/useWeblabs.svelte'
|
|
14
|
+
import { useSettings } from '../hooks/useSettings.svelte'
|
|
15
|
+
import { useWeblabs } from '../hooks/useWeblabs.svelte'
|
|
16
|
+
import { use3DModels } from '../hooks/use3DModels.svelte'
|
|
17
|
+
const settings = useSettings()
|
|
13
18
|
const plyLoader = new PLYLoader()
|
|
19
|
+
const weblabs = useWeblabs()
|
|
20
|
+
const componentModels = use3DModels()
|
|
14
21
|
|
|
15
22
|
interface Props extends ThrelteProps<Group> {
|
|
16
23
|
uuid: string
|
|
@@ -33,9 +40,28 @@
|
|
|
33
40
|
...rest
|
|
34
41
|
}: Props = $props()
|
|
35
42
|
|
|
43
|
+
const gltfModel = $derived.by(() => {
|
|
44
|
+
const [componentName, id] = name.split(':')
|
|
45
|
+
if (!componentName || !id) {
|
|
46
|
+
return undefined
|
|
47
|
+
}
|
|
48
|
+
return componentModels.current?.[componentName]?.[id]
|
|
49
|
+
})
|
|
50
|
+
|
|
36
51
|
const type = $derived(geometry?.geometryType?.case)
|
|
37
52
|
const color = $derived(overrideColor ?? metadata.color ?? colors.default)
|
|
38
53
|
|
|
54
|
+
const renderModels = $derived(
|
|
55
|
+
(settings.current.renderArmModels === 'model' ||
|
|
56
|
+
settings.current.renderArmModels === 'colliders+model') &&
|
|
57
|
+
gltfModel
|
|
58
|
+
)
|
|
59
|
+
const renderPrimitives = $derived(
|
|
60
|
+
settings.current.renderArmModels === 'colliders' ||
|
|
61
|
+
settings.current.renderArmModels === 'colliders+model' ||
|
|
62
|
+
!gltfModel
|
|
63
|
+
)
|
|
64
|
+
|
|
39
65
|
const group = new Group()
|
|
40
66
|
const mesh = $derived.by(() => {
|
|
41
67
|
if (type === undefined) {
|
|
@@ -50,6 +76,16 @@
|
|
|
50
76
|
return result
|
|
51
77
|
})
|
|
52
78
|
|
|
79
|
+
$effect.pre(() => {
|
|
80
|
+
if (
|
|
81
|
+
weblabs.isActive(WEBLABS_EXPERIMENTS.MOTION_TOOLS_RENDER_ARM_MODELS) &&
|
|
82
|
+
renderModels &&
|
|
83
|
+
!renderPrimitives
|
|
84
|
+
) {
|
|
85
|
+
geo = undefined
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
53
89
|
$effect.pre(() => {
|
|
54
90
|
if (geometry?.center && mesh) {
|
|
55
91
|
poseToObject3d(geometry.center, mesh)
|
|
@@ -103,39 +139,45 @@
|
|
|
103
139
|
{uuid}
|
|
104
140
|
bvh={{ enabled: false }}
|
|
105
141
|
>
|
|
106
|
-
{#if
|
|
107
|
-
<T
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
{
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
{#if weblabs.isActive(WEBLABS_EXPERIMENTS.MOTION_TOOLS_RENDER_ARM_MODELS) && renderModels}
|
|
143
|
+
<T is={gltfModel} />
|
|
144
|
+
{/if}
|
|
145
|
+
|
|
146
|
+
{#if !weblabs.isActive(WEBLABS_EXPERIMENTS.MOTION_TOOLS_RENDER_ARM_MODELS) || renderPrimitives}
|
|
147
|
+
{#if geometry.geometryType.case === 'bufferGeometry'}
|
|
148
|
+
<T
|
|
149
|
+
is={geometry.geometryType.value}
|
|
150
|
+
{oncreate}
|
|
151
|
+
/>
|
|
152
|
+
{:else if geometry.geometryType.case === 'mesh'}
|
|
153
|
+
{@const mesh = geometry.geometryType.value.mesh}
|
|
154
|
+
{@const meshGeometry = parsePlyInput(mesh)}
|
|
155
|
+
<T
|
|
156
|
+
is={meshGeometry}
|
|
157
|
+
{oncreate}
|
|
158
|
+
/>
|
|
159
|
+
{:else if geometry.geometryType.case === 'line' && metadata.points}
|
|
160
|
+
<MeshLineGeometry points={metadata.points} />
|
|
161
|
+
{:else if geometry.geometryType.case === 'box'}
|
|
162
|
+
{@const dimsMm = geometry.geometryType.value.dimsMm ?? { x: 0, y: 0, z: 0 }}
|
|
163
|
+
<T.BoxGeometry
|
|
164
|
+
args={[dimsMm.x * 0.001, dimsMm.y * 0.001, dimsMm.z * 0.001]}
|
|
165
|
+
{oncreate}
|
|
166
|
+
/>
|
|
167
|
+
{:else if geometry.geometryType.case === 'sphere'}
|
|
168
|
+
{@const radiusMm = geometry.geometryType.value.radiusMm ?? 0}
|
|
169
|
+
<T.SphereGeometry
|
|
170
|
+
args={[radiusMm * 0.001]}
|
|
171
|
+
{oncreate}
|
|
172
|
+
/>
|
|
173
|
+
{:else if geometry.geometryType.case === 'capsule'}
|
|
174
|
+
{@const { lengthMm, radiusMm } = geometry.geometryType.value}
|
|
175
|
+
<T
|
|
176
|
+
is={CapsuleGeometry}
|
|
177
|
+
args={[radiusMm * 0.001, lengthMm * 0.001]}
|
|
178
|
+
{oncreate}
|
|
179
|
+
/>
|
|
180
|
+
{/if}
|
|
139
181
|
{/if}
|
|
140
182
|
|
|
141
183
|
{#if geometry.geometryType.case === 'line'}
|
|
@@ -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
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
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
|
-
</
|
|
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, {}, "">;
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { provideArrows } from '../hooks/useArrows.svelte'
|
|
24
24
|
import { provideFramelessComponents } from '../hooks/useFramelessComponents.svelte'
|
|
25
25
|
import { provideResourceByName } from '../hooks/useResourceByName.svelte'
|
|
26
|
+
import { provide3DModels } from '../hooks/use3DModels.svelte'
|
|
26
27
|
|
|
27
28
|
interface Props {
|
|
28
29
|
cameraPose?: CameraPose
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
provideResourceByName(() => partID.current)
|
|
48
49
|
provideFrames(() => partID.current)
|
|
49
50
|
provideGeometries(() => partID.current)
|
|
51
|
+
provide3DModels(() => partID.current)
|
|
50
52
|
providePointclouds(() => partID.current)
|
|
51
53
|
provideMotionClient(() => partID.current)
|
|
52
54
|
provideArmClient(() => partID.current)
|
|
@@ -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'
|
|
@@ -7,7 +8,10 @@
|
|
|
7
8
|
import { useResourceNames } from '@viamrobotics/svelte-sdk'
|
|
8
9
|
import { usePartID } from '../../hooks/usePartID.svelte'
|
|
9
10
|
import { RefreshRates, useMachineSettings } from '../../hooks/useMachineSettings.svelte'
|
|
11
|
+
import WeblabActive from '../weblab/WeblabActive.svelte'
|
|
12
|
+
import { WEBLABS_EXPERIMENTS } from '../../hooks/useWeblabs.svelte'
|
|
10
13
|
|
|
14
|
+
const queryClient = useQueryClient()
|
|
11
15
|
const partID = usePartID()
|
|
12
16
|
const cameras = useResourceNames(() => partID.current, 'camera')
|
|
13
17
|
const settings = useSettings()
|
|
@@ -26,10 +30,16 @@
|
|
|
26
30
|
id={RefreshRates.poses}
|
|
27
31
|
label="Poses"
|
|
28
32
|
allowLive
|
|
33
|
+
onManualRefetch={() => {
|
|
34
|
+
queryClient.refetchQueries({ queryKey: ['getPose', 'getGeometries'], exact: false })
|
|
35
|
+
}}
|
|
29
36
|
/>
|
|
30
37
|
<RefreshRate
|
|
31
38
|
id={RefreshRates.pointclouds}
|
|
32
39
|
label="Pointclouds"
|
|
40
|
+
onManualRefetch={() => {
|
|
41
|
+
queryClient.refetchQueries({ queryKey: ['getPointCloud'], exact: false })
|
|
42
|
+
}}
|
|
33
43
|
/>
|
|
34
44
|
<div>
|
|
35
45
|
<div>Enabled pointcloud cameras</div>
|
|
@@ -167,6 +177,26 @@
|
|
|
167
177
|
<label class="flex items-center justify-between gap-2">
|
|
168
178
|
Render stats <Switch bind:on={settings.current.renderStats} />
|
|
169
179
|
</label>
|
|
180
|
+
<WeblabActive experiment={WEBLABS_EXPERIMENTS.MOTION_TOOLS_RENDER_ARM_MODELS}>
|
|
181
|
+
<label class="flex items-center justify-between gap-2">
|
|
182
|
+
Render Arm Models
|
|
183
|
+
<Select
|
|
184
|
+
value={settings.current.renderArmModels}
|
|
185
|
+
onchange={(event: InputEvent) => {
|
|
186
|
+
if (event.target instanceof HTMLSelectElement) {
|
|
187
|
+
settings.current.renderArmModels = event.target.value as
|
|
188
|
+
| 'colliders'
|
|
189
|
+
| 'colliders+model'
|
|
190
|
+
| 'model'
|
|
191
|
+
}
|
|
192
|
+
}}
|
|
193
|
+
>
|
|
194
|
+
<option value="colliders">Colliders</option>
|
|
195
|
+
<option value="colliders+model">Colliders + Model</option>
|
|
196
|
+
<option value="model">Model</option>
|
|
197
|
+
</Select>
|
|
198
|
+
</label>
|
|
199
|
+
</WeblabActive>
|
|
170
200
|
</div>
|
|
171
201
|
</div>
|
|
172
202
|
</Drawer>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { XR } from '@threlte/xr';
|
|
2
|
+
declare const XR: import("svelte").Component<Record<string, any>, {}, "">;
|
|
3
|
+
type XR = ReturnType<typeof XR>;
|
|
4
|
+
export default XR;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ArmClient } from '@viamrobotics/sdk';
|
|
2
|
+
import { createResourceClient, useResourceNames } from '@viamrobotics/svelte-sdk';
|
|
3
|
+
import { getContext, setContext } from 'svelte';
|
|
4
|
+
import { useWeblabs, WEBLABS_EXPERIMENTS } from './useWeblabs.svelte';
|
|
5
|
+
import { useSettings } from './useSettings.svelte';
|
|
6
|
+
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
7
|
+
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
|
8
|
+
const gltfLoader = new GLTFLoader();
|
|
9
|
+
const dracoLoader = new DRACOLoader();
|
|
10
|
+
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');
|
|
11
|
+
gltfLoader.setDRACOLoader(dracoLoader);
|
|
12
|
+
const key = Symbol('3d-models-context');
|
|
13
|
+
export const provide3DModels = (partID) => {
|
|
14
|
+
const weblabs = useWeblabs();
|
|
15
|
+
const settings = useSettings();
|
|
16
|
+
const current = $state.raw({});
|
|
17
|
+
const arms = useResourceNames(partID, 'arm');
|
|
18
|
+
const armClients = $derived(arms.current.map((arm) => createResourceClient(ArmClient, partID, () => arm.name)));
|
|
19
|
+
const clients = $derived(armClients.filter((client) => {
|
|
20
|
+
return arms.current.some((arm) => arm.name === client.current?.name);
|
|
21
|
+
}));
|
|
22
|
+
$effect(() => {
|
|
23
|
+
const fetch3DModels = async () => {
|
|
24
|
+
for (const client of clients) {
|
|
25
|
+
if (!client.current)
|
|
26
|
+
continue;
|
|
27
|
+
try {
|
|
28
|
+
const models = await client.current.get3DModels();
|
|
29
|
+
if (!(client.current.name in current)) {
|
|
30
|
+
current[client.current.name] = {};
|
|
31
|
+
}
|
|
32
|
+
for (const [id, model] of Object.entries(models)) {
|
|
33
|
+
const arrayBuffer = model.mesh.buffer.slice(model.mesh.byteOffset, model.mesh.byteOffset + model.mesh.byteLength);
|
|
34
|
+
const gltfModel = await gltfLoader.parseAsync(arrayBuffer, '');
|
|
35
|
+
current[client.current.name][id] = gltfModel.scene;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// some arms may not implement this api yet
|
|
40
|
+
console.warn(`${client.current.name} returned an error: ${error} when getting 3D models`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const shouldFetchModels = settings.current.isLoaded &&
|
|
45
|
+
(settings.current.renderArmModels === 'model' ||
|
|
46
|
+
settings.current.renderArmModels === 'colliders+model');
|
|
47
|
+
if (weblabs.isActive(WEBLABS_EXPERIMENTS.MOTION_TOOLS_RENDER_ARM_MODELS) && shouldFetchModels) {
|
|
48
|
+
fetch3DModels();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
setContext(key, {
|
|
52
|
+
get current() {
|
|
53
|
+
return current;
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
export const use3DModels = () => {
|
|
58
|
+
return getContext(key);
|
|
59
|
+
};
|
|
@@ -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 !==
|
|
36
|
-
refetchInterval: interval ===
|
|
37
|
-
queryKey: ['partID', partID(), client.current?.name
|
|
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 !==
|
|
24
|
+
enabled: interval !== RefetchRates.OFF &&
|
|
24
25
|
cameraClient.current !== undefined &&
|
|
25
26
|
disabledCameras.get(name) !== true,
|
|
26
|
-
refetchInterval: interval ===
|
|
27
|
-
queryKey: ['partID', partID(), name
|
|
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 !==
|
|
25
|
+
enabled: interval !== RefetchRates.OFF &&
|
|
25
26
|
client.current !== undefined &&
|
|
26
27
|
environment.current.viewerMode === 'monitor',
|
|
27
|
-
refetchInterval: interval ===
|
|
28
|
-
queryKey: ['partID', partID.current, client.current?.name,
|
|
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');
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
interface Settings {
|
|
2
|
+
isLoaded: boolean;
|
|
2
3
|
cameraMode: 'orthographic' | 'perspective';
|
|
3
4
|
transforming: boolean;
|
|
4
5
|
snapping: boolean;
|
|
@@ -18,6 +19,7 @@ interface Settings {
|
|
|
18
19
|
enableXR: boolean;
|
|
19
20
|
enableArmPositionsWidget: boolean;
|
|
20
21
|
renderStats: boolean;
|
|
22
|
+
renderArmModels: 'colliders' | 'colliders+model' | 'model';
|
|
21
23
|
}
|
|
22
24
|
interface Context {
|
|
23
25
|
current: Settings;
|
|
@@ -2,6 +2,7 @@ import { get, set } from 'idb-keyval';
|
|
|
2
2
|
import { getContext, setContext } from 'svelte';
|
|
3
3
|
const key = Symbol('dashboard-context');
|
|
4
4
|
const defaults = () => ({
|
|
5
|
+
isLoaded: false,
|
|
5
6
|
cameraMode: 'perspective',
|
|
6
7
|
transforming: false,
|
|
7
8
|
snapping: false,
|
|
@@ -21,6 +22,7 @@ const defaults = () => ({
|
|
|
21
22
|
enableXR: false,
|
|
22
23
|
enableArmPositionsWidget: false,
|
|
23
24
|
renderStats: false,
|
|
25
|
+
renderArmModels: 'colliders+model',
|
|
24
26
|
});
|
|
25
27
|
export const provideSettings = () => {
|
|
26
28
|
let settings = $state(defaults());
|
|
@@ -30,6 +32,7 @@ export const provideSettings = () => {
|
|
|
30
32
|
settings = { ...settings, ...response };
|
|
31
33
|
}
|
|
32
34
|
settingsLoaded = true;
|
|
35
|
+
settings.isLoaded = true;
|
|
33
36
|
});
|
|
34
37
|
$effect(() => {
|
|
35
38
|
if (settingsLoaded) {
|
|
@@ -2,6 +2,7 @@ import { getContext, setContext } from 'svelte';
|
|
|
2
2
|
import { SvelteSet } from 'svelte/reactivity';
|
|
3
3
|
export const WEBLABS_EXPERIMENTS = {
|
|
4
4
|
MOTION_TOOLS_EDIT_FRAME: 'MOTION_TOOLS_EDIT_FRAME',
|
|
5
|
+
MOTION_TOOLS_RENDER_ARM_MODELS: 'MOTION_TOOLS_RENDER_ARM_MODELS',
|
|
5
6
|
};
|
|
6
7
|
export const WEBLABS_CONTEXT_KEY = Symbol('weblabs-context');
|
|
7
8
|
const getCookie = (name) => {
|
|
@@ -54,5 +55,12 @@ export const provideWeblabs = () => {
|
|
|
54
55
|
setContext(WEBLABS_CONTEXT_KEY, createWeblabs());
|
|
55
56
|
};
|
|
56
57
|
export const useWeblabs = () => {
|
|
57
|
-
|
|
58
|
+
const context = getContext(WEBLABS_CONTEXT_KEY);
|
|
59
|
+
if (!context) {
|
|
60
|
+
return {
|
|
61
|
+
load: () => { },
|
|
62
|
+
isActive: () => false,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return context;
|
|
58
66
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viamrobotics/motion-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Motion visualization with Viam",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@typescript-eslint/eslint-plugin": "8.42.0",
|
|
38
38
|
"@typescript-eslint/parser": "8.42.0",
|
|
39
39
|
"@viamrobotics/prime-core": "0.1.5",
|
|
40
|
-
"@viamrobotics/sdk": "0.
|
|
40
|
+
"@viamrobotics/sdk": "0.55.0",
|
|
41
41
|
"@viamrobotics/svelte-sdk": "0.7.1",
|
|
42
42
|
"@vitejs/plugin-basic-ssl": "2.1.0",
|
|
43
43
|
"@zag-js/svelte": "1.22.1",
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { Geometry, PlainMessage, Pose, Struct, TransformWithUUID } from '@viamrobotics/sdk';
|
|
2
|
-
import { BatchedMesh, Color, Object3D, Vector3, type BufferGeometry } from 'three';
|
|
3
|
-
import type { ValueOf } from 'type-fest';
|
|
4
|
-
import type { OBB } from 'three/addons/math/OBB.js';
|
|
5
|
-
export type PointsGeometry = {
|
|
6
|
-
center: undefined;
|
|
7
|
-
geometryType: {
|
|
8
|
-
case: 'points';
|
|
9
|
-
value: Float32Array<ArrayBuffer>;
|
|
10
|
-
};
|
|
11
|
-
};
|
|
12
|
-
export type LinesGeometry = {
|
|
13
|
-
center: undefined;
|
|
14
|
-
geometryType: {
|
|
15
|
-
case: 'line';
|
|
16
|
-
value: Float32Array;
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
export type ThreeBufferGeometry = {
|
|
20
|
-
center: undefined;
|
|
21
|
-
geometryType: {
|
|
22
|
-
case: 'bufferGeometry';
|
|
23
|
-
value: BufferGeometry;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
export type Geometries = Geometry | PointsGeometry | LinesGeometry | ThreeBufferGeometry;
|
|
27
|
-
export declare const SupportedShapes: {
|
|
28
|
-
readonly points: "points";
|
|
29
|
-
readonly line: "line";
|
|
30
|
-
readonly arrow: "arrow";
|
|
31
|
-
};
|
|
32
|
-
export type Metadata = {
|
|
33
|
-
colors?: Float32Array;
|
|
34
|
-
color?: Color;
|
|
35
|
-
opacity?: number;
|
|
36
|
-
gltf?: {
|
|
37
|
-
scene: Object3D;
|
|
38
|
-
};
|
|
39
|
-
points?: Vector3[];
|
|
40
|
-
pointSize?: number;
|
|
41
|
-
lineWidth?: number;
|
|
42
|
-
lineDotColor?: Color;
|
|
43
|
-
batched?: {
|
|
44
|
-
id: number;
|
|
45
|
-
object: BatchedMesh;
|
|
46
|
-
};
|
|
47
|
-
shape?: ValueOf<typeof SupportedShapes>;
|
|
48
|
-
getBoundingBoxAt?: (box: OBB) => void;
|
|
49
|
-
};
|
|
50
|
-
export declare const isMetadataKey: (key: string) => key is keyof Metadata;
|
|
51
|
-
export declare class WorldObject<T extends Geometries = Geometries> {
|
|
52
|
-
uuid: string;
|
|
53
|
-
name: string;
|
|
54
|
-
referenceFrame: string | undefined;
|
|
55
|
-
pose: PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>;
|
|
56
|
-
geometry?: T;
|
|
57
|
-
metadata: Metadata;
|
|
58
|
-
localEditedPose: PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>;
|
|
59
|
-
constructor(name?: string, pose?: Pose, parent?: string, geometry?: T, metadata?: Metadata);
|
|
60
|
-
toJSON(): Omit<WorldObject, 'toJSON' | 'fromJSON' | 'metadata'>;
|
|
61
|
-
fromJSON(json: WorldObject): this;
|
|
62
|
-
}
|
|
63
|
-
export declare const parseMetadata: (fields?: PlainMessage<Struct>["fields"]) => Metadata;
|
|
64
|
-
export declare const fromTransform: (transform: TransformWithUUID) => WorldObject<PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Geometry>>;
|
|
65
|
-
export declare const determinePose: (object: WorldObject, pose: Pose | undefined) => Pose;
|
package/dist/geometry.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { Geometry } from '@viamrobotics/sdk';
|
|
2
|
-
import type { Frame } from './frame';
|
|
3
|
-
export declare const createGeometry: (geometryType?: Geometry["geometryType"], label?: string) => Geometry;
|
|
4
|
-
export declare const createGeometryFromFrame: (frame: Frame) => import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Geometry> | undefined;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type TransformWithUUID, ResourceName } from '@viamrobotics/sdk';
|
|
2
|
-
interface Context {
|
|
3
|
-
names: ResourceName[];
|
|
4
|
-
current: Record<string, ReturnType<typeof createWorldState>>;
|
|
5
|
-
}
|
|
6
|
-
export declare const provideWorldStates: () => void;
|
|
7
|
-
export declare const useWorldStates: () => Context;
|
|
8
|
-
export declare const useWorldState: (resourceName: () => string) => {
|
|
9
|
-
readonly name: string;
|
|
10
|
-
readonly transforms: TransformWithUUID[];
|
|
11
|
-
readonly worldObjects: import("../WorldObject.svelte").WorldObject<import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Geometry>>[];
|
|
12
|
-
readonly listUUIDs: import("@tanstack/svelte-query").QueryObserverResult<string[]>;
|
|
13
|
-
readonly getTransforms: import("@tanstack/svelte-query").QueryObserverResult<TransformWithUUID>[] | undefined;
|
|
14
|
-
};
|
|
15
|
-
declare const createWorldState: (partID: () => string, resourceName: () => string) => {
|
|
16
|
-
readonly name: string;
|
|
17
|
-
readonly transforms: TransformWithUUID[];
|
|
18
|
-
readonly worldObjects: import("../WorldObject.svelte").WorldObject<import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Geometry>>[];
|
|
19
|
-
readonly listUUIDs: import("@tanstack/svelte-query").QueryObserverResult<string[]>;
|
|
20
|
-
readonly getTransforms: import("@tanstack/svelte-query").QueryObserverResult<TransformWithUUID>[] | undefined;
|
|
21
|
-
};
|
|
22
|
-
export {};
|
package/dist/transform.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Geometry, Pose } from '@viamrobotics/sdk';
|
|
2
|
-
import { type Object3D, Matrix4, Quaternion, Vector3 } from 'three';
|
|
3
|
-
import type { Frame } from './frame';
|
|
4
|
-
export declare const createPose: (pose?: Partial<Pose>) => Pose;
|
|
5
|
-
export declare const createPoseFromFrame: (frame: Partial<Frame>) => Pose;
|
|
6
|
-
export declare const quaternionToPose: (quaternion: Quaternion, pose: Partial<Pose>) => void;
|
|
7
|
-
export declare const vector3ToPose: (vec3: Vector3, pose: Partial<Pose>) => void;
|
|
8
|
-
export declare const object3dToPose: (object3d: Object3D, pose: Partial<Pose>) => Partial<import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>>;
|
|
9
|
-
export declare const poseToQuaternion: (pose: Partial<Pose>, quaternion: Quaternion) => void;
|
|
10
|
-
export declare const poseToVector3: (pose: Partial<Pose>, vec3: Vector3) => void;
|
|
11
|
-
export declare const poseToObject3d: (pose: Partial<Pose>, object3d: Object3D) => void;
|
|
12
|
-
export declare const poseToDirection: (pose: Pose) => Vector3;
|
|
13
|
-
export declare const scaleToDimensions: (scale: Vector3, geometry: Geometry["geometryType"]) => void;
|
|
14
|
-
export declare const poseToMatrix: (pose: Pose) => Matrix4;
|
|
15
|
-
export declare const matrixToPose: (matrix: Matrix4) => import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>;
|