@viamrobotics/motion-tools 1.34.3 → 1.34.5
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/dist/components/App.svelte +0 -2
- package/dist/components/SceneProviders.svelte +0 -2
- package/dist/hooks/useFrames.svelte.js +1 -1
- package/dist/hooks/useGeometries.svelte.js +1 -1
- package/dist/hooks/usePointcloudObjects.svelte.js +1 -1
- package/dist/hooks/usePointclouds.svelte.js +1 -1
- package/dist/hooks/usePose.svelte.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugins/DrawService/useDrawAPI.svelte.js +1 -1
- package/dist/plugins/Logs/Logs.svelte +146 -0
- package/dist/{hooks → plugins/Logs}/useLogs.svelte.d.ts +1 -1
- package/dist/{hooks → plugins/Logs}/useLogs.svelte.js +13 -5
- package/dist/plugins/Selection/Ellipse.svelte +9 -26
- package/dist/plugins/Selection/Ellipse.svelte.d.ts +2 -1
- package/dist/plugins/Selection/Lasso.svelte +9 -26
- package/dist/plugins/Selection/Lasso.svelte.d.ts +2 -1
- package/dist/plugins/Selection/SelectionTool.svelte +18 -3
- package/dist/plugins/Selection/SelectionTool.svelte.d.ts +2 -0
- package/dist/plugins/TopDownLock/TopDownLock.svelte +25 -0
- package/dist/plugins/TopDownLock/TopDownLock.svelte.d.ts +3 -0
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.js +6 -0
- package/package.json +2 -2
- package/dist/components/overlay/Logs.svelte +0 -144
- /package/dist/{components/overlay → plugins/Logs}/Logs.svelte.d.ts +0 -0
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
import HoveredEntities from './hover/HoveredEntities.svelte'
|
|
31
31
|
import AddFrames from './overlay/AddFrames.svelte'
|
|
32
32
|
import LiveUpdatesBanner from './overlay/LiveUpdatesBanner.svelte'
|
|
33
|
-
import Logs from './overlay/Logs.svelte'
|
|
34
33
|
import ArmPositions from './overlay/widgets/ArmPositions.svelte'
|
|
35
34
|
import Camera from './overlay/widgets/Camera.svelte'
|
|
36
35
|
import FramePov from './overlay/widgets/FramePov.svelte'
|
|
@@ -173,7 +172,6 @@
|
|
|
173
172
|
<PortalTarget id="dom" />
|
|
174
173
|
|
|
175
174
|
<Settings {settingsTabs} />
|
|
176
|
-
<Logs />
|
|
177
175
|
<AddFrames />
|
|
178
176
|
</div>
|
|
179
177
|
</SceneProviders>
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
import { provideGeometries } from '../hooks/useGeometries.svelte'
|
|
14
14
|
import { provideInheritedInvisible } from '../hooks/useInheritedInvisible.svelte'
|
|
15
15
|
import { provideLinkedEntities } from '../hooks/useLinked.svelte'
|
|
16
|
-
import { provideLogs } from '../hooks/useLogs.svelte'
|
|
17
16
|
import { usePartID } from '../hooks/usePartID.svelte'
|
|
18
17
|
import { providePointcloudObjects } from '../hooks/usePointcloudObjects.svelte'
|
|
19
18
|
import { providePointclouds } from '../hooks/usePointclouds.svelte'
|
|
@@ -30,7 +29,6 @@
|
|
|
30
29
|
const partID = usePartID()
|
|
31
30
|
|
|
32
31
|
provideTransformControls()
|
|
33
|
-
provideLogs()
|
|
34
32
|
|
|
35
33
|
provideHierarchy()
|
|
36
34
|
provideWorldMatrix()
|
|
@@ -5,11 +5,11 @@ import { getContext, setContext, untrack } from 'svelte';
|
|
|
5
5
|
import { Matrix4 } from 'three';
|
|
6
6
|
import { resourceNameToColor, subtypeToColor } from '../color';
|
|
7
7
|
import { hierarchy, traits, useWorld } from '../ecs';
|
|
8
|
+
import { useLogs } from '../plugins';
|
|
8
9
|
import { createPose, isPoseEqual, poseToMatrix } from '../transform';
|
|
9
10
|
import { useConfigFrames } from './useConfigFrames.svelte';
|
|
10
11
|
import { useEnvironment } from './useEnvironment.svelte';
|
|
11
12
|
import { useFrameEditSession } from './useFrameEditSession.svelte';
|
|
12
|
-
import { useLogs } from './useLogs.svelte';
|
|
13
13
|
import { usePartConfig } from './usePartConfig.svelte';
|
|
14
14
|
import { useResourceByName } from './useResourceByName.svelte';
|
|
15
15
|
const key = Symbol('frames-context');
|
|
@@ -7,9 +7,9 @@ import { resourceColors } from '../color';
|
|
|
7
7
|
import { RefetchRates } from '../components/overlay/RefreshRate.svelte';
|
|
8
8
|
import { hierarchy, traits, useWorld } from '../ecs';
|
|
9
9
|
import { updateGeometryTrait } from '../ecs/traits';
|
|
10
|
+
import { useLogs } from '../plugins';
|
|
10
11
|
import { createPose, poseToMatrix } from '../transform';
|
|
11
12
|
import { useEnvironment } from './useEnvironment.svelte';
|
|
12
|
-
import { useLogs } from './useLogs.svelte';
|
|
13
13
|
import { useResourceByName } from './useResourceByName.svelte';
|
|
14
14
|
import { RefreshRates, useSettings } from './useSettings.svelte';
|
|
15
15
|
const key = Symbol('geometries-context');
|
|
@@ -6,9 +6,9 @@ import { ColorFormat } from '../buf/draw/v1/metadata_pb';
|
|
|
6
6
|
import { RefetchRates } from '../components/overlay/RefreshRate.svelte';
|
|
7
7
|
import { hierarchy, traits, useWorld } from '../ecs';
|
|
8
8
|
import { parsePcdInWorker } from '../lib';
|
|
9
|
+
import { useLogs } from '../plugins';
|
|
9
10
|
import { createPose } from '../transform';
|
|
10
11
|
import { useEnvironment } from './useEnvironment.svelte';
|
|
11
|
-
import { useLogs } from './useLogs.svelte';
|
|
12
12
|
import { RefreshRates, useSettings } from './useSettings.svelte';
|
|
13
13
|
const key = Symbol('pointcloud-object-context');
|
|
14
14
|
export const providePointcloudObjects = (partID) => {
|
|
@@ -6,8 +6,8 @@ import { ColorFormat } from '../buf/draw/v1/metadata_pb';
|
|
|
6
6
|
import { RefetchRates } from '../components/overlay/RefreshRate.svelte';
|
|
7
7
|
import { hierarchy, traits, useWorld } from '../ecs';
|
|
8
8
|
import { parsePcdInWorker } from '../loaders/pcd';
|
|
9
|
+
import { useLogs } from '../plugins';
|
|
9
10
|
import { useEnvironment } from './useEnvironment.svelte';
|
|
10
|
-
import { useLogs } from './useLogs.svelte';
|
|
11
11
|
import { RefreshRates, useSettings } from './useSettings.svelte';
|
|
12
12
|
const key = Symbol('pointcloud-context');
|
|
13
13
|
export const providePointclouds = (partID) => {
|
|
@@ -2,9 +2,9 @@ import { commonApi, MachineConnectionEvent, Pose } from '@viamrobotics/sdk';
|
|
|
2
2
|
import { createRobotQuery, useConnectionStatus, useRobotClient } from '@viamrobotics/svelte-sdk';
|
|
3
3
|
import { untrack } from 'svelte';
|
|
4
4
|
import { RefetchRates } from '../components/overlay/RefreshRate.svelte';
|
|
5
|
+
import { useLogs } from '../plugins';
|
|
5
6
|
import { useEnvironment } from './useEnvironment.svelte';
|
|
6
7
|
import { useFrames } from './useFrames.svelte';
|
|
7
|
-
import { useLogs } from './useLogs.svelte';
|
|
8
8
|
import { usePartID } from './usePartID.svelte';
|
|
9
9
|
import { useRefetchPoses } from './useRefetchPoses';
|
|
10
10
|
import { useResourceByName } from './useResourceByName.svelte';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @deprecated MotionTools has been renamed to Visualizer. This export will be removed in v2. */
|
|
2
2
|
export { default as MotionTools } from './components/App.svelte';
|
|
3
3
|
export { default as Visualizer } from './components/App.svelte';
|
|
4
|
+
export { useSettings } from './hooks/useSettings.svelte';
|
|
4
5
|
export { default as PCD } from './components/PCD.svelte';
|
|
5
6
|
export * as relations from './ecs/relations';
|
|
6
7
|
export * as traits from './ecs/traits';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @deprecated MotionTools has been renamed to Visualizer. This export will be removed in v2. */
|
|
2
2
|
export { default as MotionTools } from './components/App.svelte';
|
|
3
3
|
export { default as Visualizer } from './components/App.svelte';
|
|
4
|
+
export { useSettings } from './hooks/useSettings.svelte';
|
|
4
5
|
// Plugins
|
|
5
6
|
export { default as PCD } from './components/PCD.svelte';
|
|
6
7
|
// ECS
|
|
@@ -11,7 +11,7 @@ import { asRGB, STRIDE } from '../../buffer';
|
|
|
11
11
|
import { hierarchy, traits, useWorld } from '../../ecs';
|
|
12
12
|
import { createBox, createCapsule, createSphere } from '../../geometry';
|
|
13
13
|
import { useCameraControls } from '../../hooks/useControls.svelte';
|
|
14
|
-
import { useLogs } from '
|
|
14
|
+
import { useLogs } from '..';
|
|
15
15
|
import { parsePlyInput } from '../../ply';
|
|
16
16
|
import { createPose, createPoseFromFrame, poseToMatrix } from '../../transform';
|
|
17
17
|
import { useDrawConnectionConfig } from './useDrawConnectionConfig.svelte';
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Portal } from '@threlte/extras'
|
|
3
|
+
import { PersistedState } from 'runed'
|
|
4
|
+
|
|
5
|
+
import DashboardButton from '../../components/overlay/dashboard/Button.svelte'
|
|
6
|
+
import FloatingPanel from '../../components/overlay/FloatingPanel.svelte'
|
|
7
|
+
|
|
8
|
+
import { provideLogs } from './useLogs.svelte'
|
|
9
|
+
|
|
10
|
+
const logs = provideLogs()
|
|
11
|
+
|
|
12
|
+
const isOpen = new PersistedState('logs-is-open', false)
|
|
13
|
+
|
|
14
|
+
let levels = new PersistedState('logs-selected-levels', {
|
|
15
|
+
info: true,
|
|
16
|
+
warn: true,
|
|
17
|
+
error: true,
|
|
18
|
+
})
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<Portal id="dashboard">
|
|
22
|
+
<fieldset class="relative">
|
|
23
|
+
<DashboardButton
|
|
24
|
+
active={isOpen.current}
|
|
25
|
+
icon="article"
|
|
26
|
+
description="Logs"
|
|
27
|
+
onclick={() => {
|
|
28
|
+
isOpen.current = !isOpen.current
|
|
29
|
+
}}
|
|
30
|
+
/>
|
|
31
|
+
{#if logs.warnings.length > 0}
|
|
32
|
+
<span
|
|
33
|
+
class="absolute z-4 -mt-1.5 -ml-1.5 h-4 w-4 rounded-full bg-yellow-700 text-center text-[10px] text-white"
|
|
34
|
+
>
|
|
35
|
+
{logs.warnings.length}
|
|
36
|
+
</span>
|
|
37
|
+
{/if}
|
|
38
|
+
|
|
39
|
+
{#if logs.errors.length > 0}
|
|
40
|
+
<span
|
|
41
|
+
class="absolute z-4 -mt-1.5 -ml-1.5 h-4 rounded-full bg-red-700 px-1.25 text-center text-[10px] text-white"
|
|
42
|
+
>
|
|
43
|
+
{logs.errors.length}
|
|
44
|
+
</span>
|
|
45
|
+
{/if}
|
|
46
|
+
</fieldset>
|
|
47
|
+
</Portal>
|
|
48
|
+
|
|
49
|
+
<Portal id="dom">
|
|
50
|
+
<FloatingPanel
|
|
51
|
+
title="Logs"
|
|
52
|
+
bind:isOpen={isOpen.current}
|
|
53
|
+
defaultSize={{ width: 240, height: 315 }}
|
|
54
|
+
resizable
|
|
55
|
+
>
|
|
56
|
+
<div class="flex h-full flex-col">
|
|
57
|
+
<div class="flex gap-1 px-3 py-2">
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
class={[
|
|
61
|
+
'chip border px-2',
|
|
62
|
+
{
|
|
63
|
+
'border-danger-dark bg-danger-dark text-white hover:border-red-700 hover:bg-red-700':
|
|
64
|
+
levels.current.error,
|
|
65
|
+
'bg-light hover:bg-ghost-light hover:border-light border-light text-subtle-1':
|
|
66
|
+
!levels.current.error,
|
|
67
|
+
},
|
|
68
|
+
]}
|
|
69
|
+
onclick={() => {
|
|
70
|
+
levels.current.error = !levels.current.error
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
error
|
|
74
|
+
</button>
|
|
75
|
+
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
class={[
|
|
79
|
+
'chip border',
|
|
80
|
+
{
|
|
81
|
+
'border-amber-400 bg-amber-400 text-white hover:border-amber-500 hover:bg-amber-500':
|
|
82
|
+
levels.current.warn,
|
|
83
|
+
'bg-light hover:bg-ghost-light hover:border-light border-light text-subtle-1':
|
|
84
|
+
!levels.current.warn,
|
|
85
|
+
},
|
|
86
|
+
]}
|
|
87
|
+
onclick={() => {
|
|
88
|
+
levels.current.warn = !levels.current.warn
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
warn
|
|
92
|
+
</button>
|
|
93
|
+
|
|
94
|
+
<button
|
|
95
|
+
type="button"
|
|
96
|
+
class={[
|
|
97
|
+
'chip border',
|
|
98
|
+
{
|
|
99
|
+
'border-blue-400 bg-blue-400 text-white hover:border-blue-500 hover:bg-blue-500':
|
|
100
|
+
levels.current.info,
|
|
101
|
+
'bg-light hover:bg-ghost-light hover:border-light border-light text-subtle-1':
|
|
102
|
+
!levels.current.info,
|
|
103
|
+
},
|
|
104
|
+
]}
|
|
105
|
+
onclick={() => {
|
|
106
|
+
levels.current.info = !levels.current.info
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
info
|
|
110
|
+
</button>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="flex flex-col gap-2 overflow-auto px-3 pb-3 text-xs">
|
|
114
|
+
{#each logs.current as log (log.uuid)}
|
|
115
|
+
{#if levels.current[log.level]}
|
|
116
|
+
<div>
|
|
117
|
+
<div class="flex flex-wrap items-center gap-1.5">
|
|
118
|
+
<div
|
|
119
|
+
class={[
|
|
120
|
+
'h-2 w-2 rounded-full',
|
|
121
|
+
{
|
|
122
|
+
'bg-danger-dark': log.level === 'error',
|
|
123
|
+
'bg-amber-300': log.level === 'warn',
|
|
124
|
+
'bg-blue-400': log.level === 'info',
|
|
125
|
+
},
|
|
126
|
+
]}
|
|
127
|
+
></div>
|
|
128
|
+
<div class="text-subtle-2">{log.timestamp}</div>
|
|
129
|
+
</div>
|
|
130
|
+
<div>
|
|
131
|
+
{#if log.count > 1}
|
|
132
|
+
<span class="mr-1 rounded bg-green-700 px-1 py-0.5 text-xs text-white">
|
|
133
|
+
{log.count}
|
|
134
|
+
</span>
|
|
135
|
+
{/if}
|
|
136
|
+
{log.message}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
{/if}
|
|
140
|
+
{:else}
|
|
141
|
+
No logs
|
|
142
|
+
{/each}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</FloatingPanel>
|
|
146
|
+
</Portal>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { untrack } from 'svelte';
|
|
2
2
|
import { MathUtils } from 'three';
|
|
3
|
-
const key = Symbol('logs-context');
|
|
4
3
|
const MAX_LOGS = 200;
|
|
4
|
+
// Logs is a singleton. We only have one logger per app and we need to access it anywhere.
|
|
5
|
+
let context;
|
|
5
6
|
export const provideLogs = () => {
|
|
6
7
|
// Plain insertion-ordered Map keyed by `${level}|${timestamp}|${message}`
|
|
7
8
|
// drives storage; a single `$state` version counter drives reactivity.
|
|
@@ -23,7 +24,7 @@ export const provideLogs = () => {
|
|
|
23
24
|
});
|
|
24
25
|
const errors = $derived(all.filter((l) => l.level === 'error'));
|
|
25
26
|
const warnings = $derived(all.filter((l) => l.level === 'warn'));
|
|
26
|
-
|
|
27
|
+
context = {
|
|
27
28
|
get current() {
|
|
28
29
|
return all;
|
|
29
30
|
},
|
|
@@ -58,8 +59,15 @@ export const provideLogs = () => {
|
|
|
58
59
|
version++;
|
|
59
60
|
});
|
|
60
61
|
},
|
|
61
|
-
}
|
|
62
|
+
};
|
|
63
|
+
return context;
|
|
62
64
|
};
|
|
63
65
|
export const useLogs = () => {
|
|
64
|
-
return
|
|
66
|
+
// return a no-op context if the plugin isn't installed
|
|
67
|
+
return (context ?? {
|
|
68
|
+
current: [],
|
|
69
|
+
errors: [],
|
|
70
|
+
warnings: [],
|
|
71
|
+
add: () => undefined,
|
|
72
|
+
});
|
|
65
73
|
};
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
import { getTriangleBoxesFromIndices, getTriangleFromIndex, raycast } from './utils'
|
|
17
17
|
|
|
18
18
|
interface Props {
|
|
19
|
-
|
|
19
|
+
enabled?: boolean
|
|
20
|
+
selecting?: boolean
|
|
20
21
|
debug?: boolean
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
let {
|
|
24
|
+
let { enabled = false, selecting = false, debug = false }: Props = $props()
|
|
24
25
|
|
|
25
26
|
const world = useWorld()
|
|
26
27
|
const controls = useCameraControls()
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
let drawing = false
|
|
38
39
|
|
|
39
40
|
const onpointerdown = (event: PointerEvent) => {
|
|
40
|
-
if (!event.shiftKey
|
|
41
|
+
if (!selecting && !event.shiftKey) return
|
|
41
42
|
|
|
42
43
|
const { x, y } = raycast(event, camera.current)
|
|
43
44
|
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
const onpointermove = (event: PointerEvent) => {
|
|
64
|
-
if (!drawing
|
|
65
|
+
if (!drawing) return
|
|
65
66
|
|
|
66
67
|
let ellipse = world.query(selectionTraits.Ellipse).at(-1)
|
|
67
68
|
|
|
@@ -129,13 +130,13 @@
|
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
const onpointerleave = () => {
|
|
132
|
-
if (!drawing
|
|
133
|
+
if (!drawing) return
|
|
133
134
|
|
|
134
135
|
onpointerup()
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
const onpointerup = () => {
|
|
138
|
-
if (!drawing
|
|
139
|
+
if (!drawing) return
|
|
139
140
|
|
|
140
141
|
drawing = false
|
|
141
142
|
|
|
@@ -244,6 +245,8 @@
|
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
$effect(() => {
|
|
248
|
+
if (!enabled) return
|
|
249
|
+
|
|
247
250
|
globalThis.addEventListener('keydown', onkeydown)
|
|
248
251
|
globalThis.addEventListener('keyup', onkeyup)
|
|
249
252
|
dom.addEventListener('pointerdown', onpointerdown)
|
|
@@ -263,26 +266,6 @@
|
|
|
263
266
|
|
|
264
267
|
const ellipses = useQuery(selectionTraits.Ellipse)
|
|
265
268
|
|
|
266
|
-
$effect(() => {
|
|
267
|
-
if (!controls.current) return
|
|
268
|
-
|
|
269
|
-
const currentControls = controls.current
|
|
270
|
-
|
|
271
|
-
if ('minPolarAngle' in currentControls) {
|
|
272
|
-
const { minPolarAngle, maxPolarAngle } = currentControls
|
|
273
|
-
|
|
274
|
-
// Locks the camera to top down while this component is mounted
|
|
275
|
-
currentControls.polarAngle = 0
|
|
276
|
-
currentControls.minPolarAngle = 0
|
|
277
|
-
currentControls.maxPolarAngle = 0
|
|
278
|
-
|
|
279
|
-
return () => {
|
|
280
|
-
currentControls.minPolarAngle = minPolarAngle
|
|
281
|
-
currentControls.maxPolarAngle = maxPolarAngle
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
})
|
|
285
|
-
|
|
286
269
|
// On unmount, destroy all lasso related entities
|
|
287
270
|
$effect(() => {
|
|
288
271
|
return () => {
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
import { getTriangleBoxesFromIndices, getTriangleFromIndex, raycast } from './utils'
|
|
17
17
|
|
|
18
18
|
interface Props {
|
|
19
|
-
|
|
19
|
+
enabled?: boolean
|
|
20
|
+
selecting?: boolean
|
|
20
21
|
debug?: boolean
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
let {
|
|
24
|
+
let { enabled = false, selecting = false, debug = false }: Props = $props()
|
|
24
25
|
|
|
25
26
|
const world = useWorld()
|
|
26
27
|
const controls = useCameraControls()
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
let drawing = false
|
|
38
39
|
|
|
39
40
|
const onpointerdown = (event: PointerEvent) => {
|
|
40
|
-
if (!event.shiftKey
|
|
41
|
+
if (!selecting && !event.shiftKey) return
|
|
41
42
|
|
|
42
43
|
const { x, y } = raycast(event, camera.current)
|
|
43
44
|
|
|
@@ -60,7 +61,7 @@
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
const onpointermove = (event: PointerEvent) => {
|
|
63
|
-
if (!drawing
|
|
64
|
+
if (!drawing) return
|
|
64
65
|
|
|
65
66
|
let lasso = world.query(selectionTraits.Lasso).at(-1)
|
|
66
67
|
|
|
@@ -100,13 +101,13 @@
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
const onpointerleave = () => {
|
|
103
|
-
if (!drawing
|
|
104
|
+
if (!drawing) return
|
|
104
105
|
|
|
105
106
|
onpointerup()
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
const onpointerup = () => {
|
|
109
|
-
if (!drawing
|
|
110
|
+
if (!drawing) return
|
|
110
111
|
|
|
111
112
|
drawing = false
|
|
112
113
|
|
|
@@ -225,6 +226,8 @@
|
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
$effect(() => {
|
|
229
|
+
if (!enabled) return
|
|
230
|
+
|
|
228
231
|
globalThis.addEventListener('keydown', onkeydown)
|
|
229
232
|
globalThis.addEventListener('keyup', onkeyup)
|
|
230
233
|
dom.addEventListener('pointerdown', onpointerdown)
|
|
@@ -244,26 +247,6 @@
|
|
|
244
247
|
|
|
245
248
|
const lassos = useQuery(selectionTraits.Lasso)
|
|
246
249
|
|
|
247
|
-
$effect(() => {
|
|
248
|
-
if (!controls.current) return
|
|
249
|
-
|
|
250
|
-
const currentControls = controls.current
|
|
251
|
-
|
|
252
|
-
if ('minPolarAngle' in currentControls) {
|
|
253
|
-
const { minPolarAngle, maxPolarAngle } = currentControls
|
|
254
|
-
|
|
255
|
-
// Locks the camera to top down while this component is mounted
|
|
256
|
-
currentControls.polarAngle = 0
|
|
257
|
-
currentControls.minPolarAngle = 0
|
|
258
|
-
currentControls.maxPolarAngle = 0
|
|
259
|
-
|
|
260
|
-
return () => {
|
|
261
|
-
currentControls.minPolarAngle = minPolarAngle
|
|
262
|
-
currentControls.maxPolarAngle = maxPolarAngle
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
})
|
|
266
|
-
|
|
267
250
|
// On unmount, destroy all lasso related entities
|
|
268
251
|
$effect(() => {
|
|
269
252
|
return () => {
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
interface Props {
|
|
20
20
|
/** Whether to auto-enable lasso mode when the component mounts */
|
|
21
21
|
enabled?: boolean
|
|
22
|
+
|
|
23
|
+
/** Allow manually going into selection state */
|
|
24
|
+
selecting?: boolean
|
|
25
|
+
|
|
22
26
|
// TODO: remove once a Selected trait exists
|
|
23
27
|
autoSelectNewEntities?: boolean
|
|
24
28
|
children?: Snippet
|
|
@@ -26,7 +30,12 @@
|
|
|
26
30
|
|
|
27
31
|
type SelectionType = 'lasso' | 'ellipse'
|
|
28
32
|
|
|
29
|
-
let {
|
|
33
|
+
let {
|
|
34
|
+
enabled = false,
|
|
35
|
+
selecting = false,
|
|
36
|
+
autoSelectNewEntities = false,
|
|
37
|
+
children,
|
|
38
|
+
}: Props = $props()
|
|
30
39
|
|
|
31
40
|
const { dom } = useThrelte()
|
|
32
41
|
const world = useWorld()
|
|
@@ -113,7 +122,13 @@
|
|
|
113
122
|
</Portal>
|
|
114
123
|
|
|
115
124
|
{#if isSelectionMode && rect.height > 0 && rect.width > 0}
|
|
116
|
-
<Ellipse
|
|
117
|
-
|
|
125
|
+
<Ellipse
|
|
126
|
+
enabled={selectionType === 'ellipse'}
|
|
127
|
+
{selecting}
|
|
128
|
+
/>
|
|
129
|
+
<Lasso
|
|
130
|
+
enabled={selectionType === 'lasso'}
|
|
131
|
+
{selecting}
|
|
132
|
+
/>
|
|
118
133
|
{@render children?.()}
|
|
119
134
|
{/if}
|
|
@@ -2,6 +2,8 @@ import type { Snippet } from 'svelte';
|
|
|
2
2
|
interface Props {
|
|
3
3
|
/** Whether to auto-enable lasso mode when the component mounts */
|
|
4
4
|
enabled?: boolean;
|
|
5
|
+
/** Allow manually going into selection state */
|
|
6
|
+
selecting?: boolean;
|
|
5
7
|
autoSelectNewEntities?: boolean;
|
|
6
8
|
children?: Snippet;
|
|
7
9
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useCameraControls } from '../../hooks/useControls.svelte'
|
|
3
|
+
|
|
4
|
+
const controls = useCameraControls()
|
|
5
|
+
|
|
6
|
+
$effect(() => {
|
|
7
|
+
if (!controls.current) return
|
|
8
|
+
|
|
9
|
+
const currentControls = controls.current
|
|
10
|
+
|
|
11
|
+
if ('minPolarAngle' in currentControls) {
|
|
12
|
+
const { minPolarAngle, maxPolarAngle } = currentControls
|
|
13
|
+
|
|
14
|
+
// Locks the camera to top down while this component is mounted
|
|
15
|
+
currentControls.polarAngle = 0
|
|
16
|
+
currentControls.minPolarAngle = 0
|
|
17
|
+
currentControls.maxPolarAngle = 0
|
|
18
|
+
|
|
19
|
+
return () => {
|
|
20
|
+
currentControls.minPolarAngle = minPolarAngle
|
|
21
|
+
currentControls.maxPolarAngle = maxPolarAngle
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
</script>
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -3,9 +3,12 @@ export * as selectionTraits from './Selection/traits';
|
|
|
3
3
|
export * as selectionRelations from './Selection/relations';
|
|
4
4
|
export { useSelectionPlugin } from './Selection/useSelectionPlugin.svelte';
|
|
5
5
|
export { default as MeasureTool } from './MeasureTool/MeasureTool.svelte';
|
|
6
|
+
export { default as TopDownLock } from './TopDownLock/TopDownLock.svelte';
|
|
6
7
|
export { default as DrawService } from './DrawService/DrawService.svelte';
|
|
7
8
|
export { default as Skybox } from './Skybox/Skybox.svelte';
|
|
8
9
|
export { default as Debug } from './Debug/Debug.svelte';
|
|
10
|
+
export { default as Logs } from './Logs/Logs.svelte';
|
|
11
|
+
export { useLogs } from './Logs/useLogs.svelte';
|
|
9
12
|
export { default as Focus } from './Focus/Focus.svelte';
|
|
10
13
|
export { default as LLMSceneBuilder } from './LLMSceneBuilder/LLMSceneBuilder.svelte';
|
|
11
14
|
export type { InferCallback, ComponentFrameInfo } from './LLMSceneBuilder/useSceneBuilder.svelte';
|
package/dist/plugins/index.js
CHANGED
|
@@ -4,14 +4,20 @@ export * as selectionTraits from './Selection/traits';
|
|
|
4
4
|
export * as selectionRelations from './Selection/relations';
|
|
5
5
|
export { useSelectionPlugin } from './Selection/useSelectionPlugin.svelte';
|
|
6
6
|
export { default as MeasureTool } from './MeasureTool/MeasureTool.svelte';
|
|
7
|
+
export { default as TopDownLock } from './TopDownLock/TopDownLock.svelte';
|
|
7
8
|
// DrawService
|
|
8
9
|
export { default as DrawService } from './DrawService/DrawService.svelte';
|
|
9
10
|
// Skybox
|
|
10
11
|
export { default as Skybox } from './Skybox/Skybox.svelte';
|
|
11
12
|
// Debug
|
|
12
13
|
export { default as Debug } from './Debug/Debug.svelte';
|
|
14
|
+
// Logs
|
|
15
|
+
export { default as Logs } from './Logs/Logs.svelte';
|
|
16
|
+
export { useLogs } from './Logs/useLogs.svelte';
|
|
17
|
+
// Focus
|
|
13
18
|
export { default as Focus } from './Focus/Focus.svelte';
|
|
14
19
|
// LLMSceneBuilder
|
|
15
20
|
export { default as LLMSceneBuilder } from './LLMSceneBuilder/LLMSceneBuilder.svelte';
|
|
21
|
+
// XR
|
|
16
22
|
export { default as XR } from './XR/XR.svelte';
|
|
17
23
|
export { default as XRSettings } from './XR/XRSettings.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viamrobotics/motion-tools",
|
|
3
|
-
"version": "1.34.
|
|
3
|
+
"version": "1.34.5",
|
|
4
4
|
"description": "Motion visualization with Viam",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@testing-library/svelte": "5.2.8",
|
|
31
31
|
"@testing-library/user-event": "^14.6.1",
|
|
32
32
|
"@threlte/core": "8.5.14",
|
|
33
|
-
"@threlte/extras": "9.
|
|
33
|
+
"@threlte/extras": "9.21.0",
|
|
34
34
|
"@threlte/rapier": "3.4.1",
|
|
35
35
|
"@threlte/xr": "1.6.0",
|
|
36
36
|
"@types/bun": "1.2.21",
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { Portal } from '@threlte/extras'
|
|
3
|
-
import { PersistedState } from 'runed'
|
|
4
|
-
|
|
5
|
-
import { useLogs } from '../../hooks/useLogs.svelte'
|
|
6
|
-
|
|
7
|
-
import DashboardButton from './dashboard/Button.svelte'
|
|
8
|
-
import FloatingPanel from './FloatingPanel.svelte'
|
|
9
|
-
|
|
10
|
-
const logs = useLogs()
|
|
11
|
-
|
|
12
|
-
const isOpen = new PersistedState('logs-is-open', false)
|
|
13
|
-
|
|
14
|
-
let levels = new PersistedState('logs-selected-levels', {
|
|
15
|
-
info: true,
|
|
16
|
-
warn: true,
|
|
17
|
-
error: true,
|
|
18
|
-
})
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
<Portal id="dashboard">
|
|
22
|
-
<fieldset class="relative">
|
|
23
|
-
<DashboardButton
|
|
24
|
-
active={isOpen.current}
|
|
25
|
-
icon="article"
|
|
26
|
-
description="Logs"
|
|
27
|
-
onclick={() => {
|
|
28
|
-
isOpen.current = !isOpen.current
|
|
29
|
-
}}
|
|
30
|
-
/>
|
|
31
|
-
{#if logs.warnings.length > 0}
|
|
32
|
-
<span
|
|
33
|
-
class="absolute z-4 -mt-1.5 -ml-1.5 h-4 w-4 rounded-full bg-yellow-700 text-center text-[10px] text-white"
|
|
34
|
-
>
|
|
35
|
-
{logs.warnings.length}
|
|
36
|
-
</span>
|
|
37
|
-
{/if}
|
|
38
|
-
|
|
39
|
-
{#if logs.errors.length > 0}
|
|
40
|
-
<span
|
|
41
|
-
class="absolute z-4 -mt-1.5 -ml-1.5 h-4 rounded-full bg-red-700 px-1.25 text-center text-[10px] text-white"
|
|
42
|
-
>
|
|
43
|
-
{logs.errors.length}
|
|
44
|
-
</span>
|
|
45
|
-
{/if}
|
|
46
|
-
</fieldset>
|
|
47
|
-
</Portal>
|
|
48
|
-
|
|
49
|
-
<FloatingPanel
|
|
50
|
-
title="Logs"
|
|
51
|
-
bind:isOpen={isOpen.current}
|
|
52
|
-
defaultSize={{ width: 240, height: 315 }}
|
|
53
|
-
resizable
|
|
54
|
-
>
|
|
55
|
-
<div class="flex h-full flex-col">
|
|
56
|
-
<div class="flex gap-1 px-3 py-2">
|
|
57
|
-
<button
|
|
58
|
-
type="button"
|
|
59
|
-
class={[
|
|
60
|
-
'chip border px-2',
|
|
61
|
-
{
|
|
62
|
-
'border-danger-dark bg-danger-dark text-white hover:border-red-700 hover:bg-red-700':
|
|
63
|
-
levels.current.error,
|
|
64
|
-
'bg-light hover:bg-ghost-light hover:border-light border-light text-subtle-1':
|
|
65
|
-
!levels.current.error,
|
|
66
|
-
},
|
|
67
|
-
]}
|
|
68
|
-
onclick={() => {
|
|
69
|
-
levels.current.error = !levels.current.error
|
|
70
|
-
}}
|
|
71
|
-
>
|
|
72
|
-
error
|
|
73
|
-
</button>
|
|
74
|
-
|
|
75
|
-
<button
|
|
76
|
-
type="button"
|
|
77
|
-
class={[
|
|
78
|
-
'chip border',
|
|
79
|
-
{
|
|
80
|
-
'border-amber-400 bg-amber-400 text-white hover:border-amber-500 hover:bg-amber-500':
|
|
81
|
-
levels.current.warn,
|
|
82
|
-
'bg-light hover:bg-ghost-light hover:border-light border-light text-subtle-1':
|
|
83
|
-
!levels.current.warn,
|
|
84
|
-
},
|
|
85
|
-
]}
|
|
86
|
-
onclick={() => {
|
|
87
|
-
levels.current.warn = !levels.current.warn
|
|
88
|
-
}}
|
|
89
|
-
>
|
|
90
|
-
warn
|
|
91
|
-
</button>
|
|
92
|
-
|
|
93
|
-
<button
|
|
94
|
-
type="button"
|
|
95
|
-
class={[
|
|
96
|
-
'chip border',
|
|
97
|
-
{
|
|
98
|
-
'border-blue-400 bg-blue-400 text-white hover:border-blue-500 hover:bg-blue-500':
|
|
99
|
-
levels.current.info,
|
|
100
|
-
'bg-light hover:bg-ghost-light hover:border-light border-light text-subtle-1':
|
|
101
|
-
!levels.current.info,
|
|
102
|
-
},
|
|
103
|
-
]}
|
|
104
|
-
onclick={() => {
|
|
105
|
-
levels.current.info = !levels.current.info
|
|
106
|
-
}}
|
|
107
|
-
>
|
|
108
|
-
info
|
|
109
|
-
</button>
|
|
110
|
-
</div>
|
|
111
|
-
|
|
112
|
-
<div class="flex flex-col gap-2 overflow-auto px-3 pb-3 text-xs">
|
|
113
|
-
{#each logs.current as log (log.uuid)}
|
|
114
|
-
{#if levels.current[log.level]}
|
|
115
|
-
<div>
|
|
116
|
-
<div class="flex flex-wrap items-center gap-1.5">
|
|
117
|
-
<div
|
|
118
|
-
class={[
|
|
119
|
-
'h-2 w-2 rounded-full',
|
|
120
|
-
{
|
|
121
|
-
'bg-danger-dark': log.level === 'error',
|
|
122
|
-
'bg-amber-300': log.level === 'warn',
|
|
123
|
-
'bg-blue-400': log.level === 'info',
|
|
124
|
-
},
|
|
125
|
-
]}
|
|
126
|
-
></div>
|
|
127
|
-
<div class="text-subtle-2">{log.timestamp}</div>
|
|
128
|
-
</div>
|
|
129
|
-
<div>
|
|
130
|
-
{#if log.count > 1}
|
|
131
|
-
<span class="mr-1 rounded bg-green-700 px-1 py-0.5 text-xs text-white">
|
|
132
|
-
{log.count}
|
|
133
|
-
</span>
|
|
134
|
-
{/if}
|
|
135
|
-
{log.message}
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
{/if}
|
|
139
|
-
{:else}
|
|
140
|
-
No logs
|
|
141
|
-
{/each}
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
</FloatingPanel>
|
|
File without changes
|