@viamrobotics/motion-tools 1.3.3 → 1.3.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/{Arrows.svelte → Arrows/ArrowGroups.svelte} +8 -26
- package/dist/components/Arrows/ArrowGroups.svelte.d.ts +3 -0
- package/dist/components/Arrows/Arrows.svelte +38 -0
- package/dist/components/Arrows/Arrows.svelte.d.ts +9 -0
- package/dist/components/Details.svelte +63 -12
- package/dist/components/FileDrop/FileDrop.svelte +4 -2
- package/dist/components/Geometry2.svelte +2 -1
- package/dist/components/KeyboardControls.svelte +28 -1
- package/dist/components/Scene.svelte +7 -5
- package/dist/components/StaticGeometries.svelte +2 -1
- package/dist/components/Tree/Settings.svelte +3 -3
- package/dist/components/Tree/Tree.svelte +10 -7
- package/dist/components/Tree/TreeContainer.svelte +4 -31
- package/dist/components/widgets/ArmPositions.svelte +0 -11
- package/dist/ecs/traits.d.ts +4 -0
- package/dist/ecs/traits.js +4 -0
- package/dist/hooks/useDrawAPI.svelte.js +13 -9
- package/dist/hooks/useEnvironment.svelte.d.ts +1 -1
- package/dist/hooks/useMachineSettings.svelte.d.ts +1 -1
- package/dist/hooks/useMachineSettings.svelte.js +9 -6
- package/dist/hooks/useObjectEvents.svelte.js +35 -27
- package/dist/hooks/usePointcloudObjects.svelte.js +13 -6
- package/dist/hooks/usePointclouds.svelte.js +3 -1
- package/dist/hooks/useSelection.svelte.js +12 -13
- package/dist/hooks/useWorldState.svelte.js +6 -2
- package/dist/loaders/pcd/worker.js +6 -1
- package/dist/snapshot.js +5 -4
- package/dist/three/InstancedArrows/InstancedArrows.js +2 -2
- package/package.json +8 -7
- package/dist/components/Arrows.svelte.d.ts +0 -3
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { InstancedArrows } from '../three/InstancedArrows/InstancedArrows'
|
|
5
|
-
import { traits, useWorld } from '../ecs'
|
|
2
|
+
import { InstancedArrows } from '../../three/InstancedArrows/InstancedArrows'
|
|
3
|
+
import { traits, useWorld } from '../../ecs'
|
|
6
4
|
import type { Entity } from 'koota'
|
|
7
|
-
import { STRIDE } from '
|
|
8
|
-
import { useObjectEvents } from '../hooks/useObjectEvents.svelte'
|
|
5
|
+
import { STRIDE } from '../../buffer'
|
|
9
6
|
import { SvelteMap } from 'svelte/reactivity'
|
|
10
7
|
import { Color } from 'three'
|
|
11
|
-
import
|
|
8
|
+
import Arrows from './Arrows.svelte'
|
|
12
9
|
|
|
13
10
|
const world = useWorld()
|
|
14
11
|
|
|
@@ -59,23 +56,8 @@
|
|
|
59
56
|
</script>
|
|
60
57
|
|
|
61
58
|
{#each map as [entity, arrows] (entity)}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
name={entity}
|
|
67
|
-
>
|
|
68
|
-
<T
|
|
69
|
-
is={arrows.headMesh}
|
|
70
|
-
bvh={{ enabled: false }}
|
|
71
|
-
raycast={() => null}
|
|
72
|
-
/>
|
|
73
|
-
<T
|
|
74
|
-
is={arrows.shaftMesh}
|
|
75
|
-
bvh={{ enabled: false }}
|
|
76
|
-
raycast={meshBoundsRaycast}
|
|
77
|
-
{...events}
|
|
78
|
-
/>
|
|
79
|
-
</T>
|
|
80
|
-
</Portal>
|
|
59
|
+
<Arrows
|
|
60
|
+
{entity}
|
|
61
|
+
{arrows}
|
|
62
|
+
/>
|
|
81
63
|
{/each}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { T } from '@threlte/core'
|
|
3
|
+
import { Portal } from '@threlte/extras'
|
|
4
|
+
import type { Entity } from 'koota'
|
|
5
|
+
import { traits } from '../../ecs'
|
|
6
|
+
import { useObjectEvents } from '../../hooks/useObjectEvents.svelte'
|
|
7
|
+
import type { InstancedArrows } from '../../three/InstancedArrows/InstancedArrows'
|
|
8
|
+
import { meshBoundsRaycast } from '../../three/InstancedArrows/raycast'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
entity: Entity
|
|
12
|
+
arrows: InstancedArrows
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { entity, arrows }: Props = $props()
|
|
16
|
+
|
|
17
|
+
const events = useObjectEvents(() => entity)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<Portal id={entity.get(traits.Parent)}>
|
|
21
|
+
<T
|
|
22
|
+
is={arrows}
|
|
23
|
+
name={entity}
|
|
24
|
+
>
|
|
25
|
+
<T
|
|
26
|
+
is={arrows.headMesh}
|
|
27
|
+
bvh={{ enabled: false }}
|
|
28
|
+
raycast={() => null}
|
|
29
|
+
visible={events.visible}
|
|
30
|
+
/>
|
|
31
|
+
<T
|
|
32
|
+
is={arrows.shaftMesh}
|
|
33
|
+
bvh={{ enabled: false }}
|
|
34
|
+
raycast={meshBoundsRaycast}
|
|
35
|
+
{...events}
|
|
36
|
+
/>
|
|
37
|
+
</T>
|
|
38
|
+
</Portal>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Entity } from 'koota';
|
|
2
|
+
import type { InstancedArrows } from '../../three/InstancedArrows/InstancedArrows';
|
|
3
|
+
interface Props {
|
|
4
|
+
entity: Entity;
|
|
5
|
+
arrows: InstancedArrows;
|
|
6
|
+
}
|
|
7
|
+
declare const Arrows: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type Arrows = ReturnType<typeof Arrows>;
|
|
9
|
+
export default Arrows;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
lang="ts"
|
|
4
4
|
>
|
|
5
5
|
import { OrientationVector } from '../three/OrientationVector'
|
|
6
|
-
import { Quaternion, Vector3, MathUtils
|
|
6
|
+
import { Quaternion, Vector3, MathUtils } from 'three'
|
|
7
7
|
|
|
8
8
|
const vec3 = new Vector3()
|
|
9
9
|
const quaternion = new Quaternion()
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { draggable } from '@neodrag/svelte'
|
|
15
15
|
import { Check, Copy } from 'lucide-svelte'
|
|
16
16
|
import { useTask } from '@threlte/core'
|
|
17
|
-
import { Button, Icon, Select, Input } from '@viamrobotics/prime-core'
|
|
17
|
+
import { Button, Icon, Select, Input, Tooltip } from '@viamrobotics/prime-core'
|
|
18
18
|
import {
|
|
19
19
|
useSelectedEntity,
|
|
20
20
|
useFocusedEntity,
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
import { usePartConfig } from '../hooks/usePartConfig.svelte'
|
|
26
26
|
import { FrameConfigUpdater } from '../FrameConfigUpdater.svelte'
|
|
27
27
|
import { useEnvironment } from '../hooks/useEnvironment.svelte'
|
|
28
|
-
import { traits, useTrait } from '../ecs'
|
|
28
|
+
import { traits, useTrait, useWorld } from '../ecs'
|
|
29
29
|
import { useResourceByName } from '../hooks/useResourceByName.svelte'
|
|
30
|
-
import {
|
|
30
|
+
import { useCameraControls } from '../hooks/useControls.svelte'
|
|
31
31
|
|
|
32
32
|
const { ...rest } = $props()
|
|
33
33
|
|
|
34
|
-
const
|
|
35
|
-
|
|
34
|
+
const world = useWorld()
|
|
35
|
+
const controls = useCameraControls()
|
|
36
36
|
const resourceByName = useResourceByName()
|
|
37
37
|
const frames = useFrames()
|
|
38
38
|
const partConfig = usePartConfig()
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
const box = useTrait(() => entity, traits.Box)
|
|
53
53
|
const sphere = useTrait(() => entity, traits.Sphere)
|
|
54
54
|
const capsule = useTrait(() => entity, traits.Capsule)
|
|
55
|
+
const removable = useTrait(() => entity, traits.Removable)
|
|
55
56
|
|
|
56
57
|
const framesAPI = useTrait(() => entity, traits.FramesAPI)
|
|
57
58
|
const isFrameNode = $derived(!!framesAPI.current)
|
|
@@ -230,27 +231,77 @@
|
|
|
230
231
|
{@const ScalarAttribute = showEditFrameOptions ? MutableField : ImmutableField}
|
|
231
232
|
|
|
232
233
|
<div
|
|
234
|
+
id="details-panel"
|
|
233
235
|
class="border-medium bg-extralight absolute top-0 right-0 z-10 m-2 {showEditFrameOptions
|
|
234
236
|
? 'w-80'
|
|
235
237
|
: 'w-60'} border p-2 text-xs"
|
|
236
238
|
use:draggable={{
|
|
237
239
|
bounds: 'body',
|
|
238
240
|
handle: dragElement,
|
|
239
|
-
defaultPosition: dragPosition.current,
|
|
240
|
-
onDragEnd(data) {
|
|
241
|
-
dragPosition.current = { x: data.offsetX, y: data.offsetY }
|
|
242
|
-
},
|
|
243
241
|
}}
|
|
244
242
|
{...rest}
|
|
245
243
|
>
|
|
246
244
|
<div class="flex items-center justify-between gap-2 pb-2">
|
|
247
|
-
<div class="flex items-center gap-1">
|
|
245
|
+
<div class="flex w-[80%] items-center gap-1">
|
|
248
246
|
<button bind:this={dragElement}>
|
|
249
247
|
<Icon name="drag" />
|
|
250
248
|
</button>
|
|
251
|
-
<strong>{name.current}</strong>
|
|
249
|
+
<strong class="overflow-hidden text-nowrap text-ellipsis">{name.current}</strong>
|
|
252
250
|
<span class="text-subtle-2">{resourceName?.subtype}</span>
|
|
253
251
|
</div>
|
|
252
|
+
|
|
253
|
+
{#if object3d}
|
|
254
|
+
<Tooltip
|
|
255
|
+
let:tooltipID
|
|
256
|
+
location="bottom"
|
|
257
|
+
>
|
|
258
|
+
<button
|
|
259
|
+
class="text-subtle-2"
|
|
260
|
+
aria-describedby={tooltipID}
|
|
261
|
+
onclick={() => {
|
|
262
|
+
const padding = 0.4
|
|
263
|
+
|
|
264
|
+
if (!controls.current) return
|
|
265
|
+
|
|
266
|
+
const { azimuthAngle, polarAngle } = controls.current
|
|
267
|
+
|
|
268
|
+
controls.current.fitToBox(object3d, true, {
|
|
269
|
+
paddingTop: padding,
|
|
270
|
+
paddingBottom: padding,
|
|
271
|
+
paddingLeft: padding,
|
|
272
|
+
paddingRight: padding,
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
// Preserve previous rotation
|
|
276
|
+
controls.current?.rotateAzimuthTo(azimuthAngle, true)
|
|
277
|
+
controls.current?.rotatePolarTo(polarAngle, true)
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
280
|
+
<Icon name="image-filter-center-focus" />
|
|
281
|
+
</button>
|
|
282
|
+
<p slot="description">Zoom to object</p>
|
|
283
|
+
</Tooltip>
|
|
284
|
+
{/if}
|
|
285
|
+
|
|
286
|
+
{#if removable.current}
|
|
287
|
+
<Tooltip
|
|
288
|
+
let:tooltipID
|
|
289
|
+
location="bottom"
|
|
290
|
+
>
|
|
291
|
+
<button
|
|
292
|
+
class="text-subtle-2"
|
|
293
|
+
aria-describedby={tooltipID}
|
|
294
|
+
onclick={() => {
|
|
295
|
+
if (world.has(entity)) {
|
|
296
|
+
entity.destroy()
|
|
297
|
+
}
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
<Icon name="trash-can-outline" />
|
|
301
|
+
</button>
|
|
302
|
+
<p slot="description">Remove from scene</p>
|
|
303
|
+
</Tooltip>
|
|
304
|
+
{/if}
|
|
254
305
|
</div>
|
|
255
306
|
|
|
256
307
|
<div class="border-medium -mx-2 w-[100%+0.5rem] border-b"></div>
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
traits.Name(result.name),
|
|
43
43
|
traits.BufferGeometry(geometry),
|
|
44
44
|
traits.Points,
|
|
45
|
-
traits.DroppedFile
|
|
45
|
+
traits.DroppedFile,
|
|
46
|
+
traits.Removable
|
|
46
47
|
)
|
|
47
48
|
break
|
|
48
49
|
}
|
|
@@ -50,7 +51,8 @@
|
|
|
50
51
|
world.spawn(
|
|
51
52
|
traits.Name(result.name),
|
|
52
53
|
traits.BufferGeometry(result.ply),
|
|
53
|
-
traits.DroppedFile
|
|
54
|
+
traits.DroppedFile,
|
|
55
|
+
traits.Removable
|
|
54
56
|
)
|
|
55
57
|
break
|
|
56
58
|
}
|
|
@@ -132,6 +132,7 @@
|
|
|
132
132
|
<T
|
|
133
133
|
is={mesh}
|
|
134
134
|
name={entity}
|
|
135
|
+
userData.name={name}
|
|
135
136
|
bvh={{ enabled: geometryType === 'buffer' }}
|
|
136
137
|
>
|
|
137
138
|
{#if model && renderMode.includes('model')}
|
|
@@ -184,7 +185,7 @@
|
|
|
184
185
|
opacity={opacity.current ?? 0.7}
|
|
185
186
|
/>
|
|
186
187
|
|
|
187
|
-
{#if geo}
|
|
188
|
+
{#if geo && renderMode.includes('colliders')}
|
|
188
189
|
<T.LineSegments
|
|
189
190
|
raycast={() => null}
|
|
190
191
|
bvh={{ enabled: false }}
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
import { useTask } from '@threlte/core'
|
|
4
4
|
import type { CameraControlsRef } from '@threlte/extras'
|
|
5
5
|
import { PressedKeys } from 'runed'
|
|
6
|
-
import { useFocusedEntity } from '../hooks/useSelection.svelte'
|
|
6
|
+
import { useFocusedEntity, useSelectedEntity } from '../hooks/useSelection.svelte'
|
|
7
7
|
import { useSettings } from '../hooks/useSettings.svelte'
|
|
8
|
+
import { useVisibility } from '../hooks/useVisibility.svelte'
|
|
8
9
|
|
|
9
10
|
interface Props {
|
|
10
11
|
cameraControls: CameraControlsRef
|
|
@@ -13,7 +14,12 @@
|
|
|
13
14
|
let { cameraControls }: Props = $props()
|
|
14
15
|
|
|
15
16
|
const focusedEntity = useFocusedEntity()
|
|
17
|
+
const selectedEntity = useSelectedEntity()
|
|
18
|
+
|
|
19
|
+
const entity = $derived(focusedEntity.current ?? selectedEntity.current)
|
|
20
|
+
|
|
16
21
|
const settings = useSettings()
|
|
22
|
+
const visibility = useVisibility()
|
|
17
23
|
|
|
18
24
|
const keys = new PressedKeys()
|
|
19
25
|
const meta = $derived(keys.has('meta'))
|
|
@@ -28,6 +34,7 @@
|
|
|
28
34
|
const down = $derived(keys.has('arrowdown'))
|
|
29
35
|
const right = $derived(keys.has('arrowright'))
|
|
30
36
|
const any = $derived(w || s || a || d || r || f || up || left || down || right)
|
|
37
|
+
|
|
31
38
|
const { start, stop } = useTask(
|
|
32
39
|
(delta) => {
|
|
33
40
|
const dt = delta * 1000
|
|
@@ -117,4 +124,24 @@
|
|
|
117
124
|
keys.onKeys('x', () => {
|
|
118
125
|
settings.current.enableXR = !settings.current.enableXR
|
|
119
126
|
})
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Handler for any keybindings that need to access the event object
|
|
130
|
+
*/
|
|
131
|
+
const onkeydown = (event: KeyboardEvent) => {
|
|
132
|
+
const key = event.key.toLowerCase()
|
|
133
|
+
|
|
134
|
+
if (key === 'h') {
|
|
135
|
+
if (!entity) return
|
|
136
|
+
|
|
137
|
+
event.stopImmediatePropagation()
|
|
138
|
+
|
|
139
|
+
const visible = visibility.get(entity) ?? true
|
|
140
|
+
|
|
141
|
+
visibility.set(entity, !visible)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
}
|
|
120
145
|
</script>
|
|
146
|
+
|
|
147
|
+
<svelte:window {onkeydown} />
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import MeasureTool from './MeasureTool.svelte'
|
|
18
18
|
import PointerMissBox from './PointerMissBox.svelte'
|
|
19
19
|
import BatchedArrows from './BatchedArrows.svelte'
|
|
20
|
-
import Arrows from './Arrows.svelte'
|
|
20
|
+
import Arrows from './Arrows/ArrowGroups.svelte'
|
|
21
21
|
|
|
22
22
|
interface Props {
|
|
23
23
|
children?: Snippet
|
|
@@ -30,17 +30,19 @@
|
|
|
30
30
|
const origin = useOrigin()
|
|
31
31
|
|
|
32
32
|
const { raycaster, enabled } = interactivity({
|
|
33
|
-
filter: (
|
|
34
|
-
const
|
|
35
|
-
return
|
|
33
|
+
filter: (intersections) => {
|
|
34
|
+
const match = intersections.find((intersection) => {
|
|
35
|
+
return intersection.object.visible === undefined || intersection.object.visible === true
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
return
|
|
38
|
+
return match ? [match] : []
|
|
39
39
|
},
|
|
40
40
|
})
|
|
41
|
+
|
|
41
42
|
$effect(() => {
|
|
42
43
|
enabled.set(!settings.current.enableMeasure)
|
|
43
44
|
})
|
|
45
|
+
|
|
44
46
|
raycaster.firstHitOnly = true
|
|
45
47
|
raycaster.params.Points.threshold = 0.005
|
|
46
48
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
const cameras = useResourceNames(() => partID.current, 'camera')
|
|
19
19
|
const visionServices = useResourceNames(() => partID.current, 'vision')
|
|
20
20
|
const settings = useSettings()
|
|
21
|
-
const { disabledCameras,
|
|
21
|
+
const { disabledCameras, disabledVisionServices } = useMachineSettings()
|
|
22
22
|
const geometries = useGeometries()
|
|
23
23
|
const pointclouds = usePointClouds()
|
|
24
24
|
const { refetchPoses } = useRefetchPoses()
|
|
@@ -81,9 +81,9 @@
|
|
|
81
81
|
<div class="flex items-center justify-between gap-4 py-2">
|
|
82
82
|
{visionService.name}
|
|
83
83
|
<Switch
|
|
84
|
-
on={
|
|
84
|
+
on={disabledVisionServices.get(visionService.name) !== true}
|
|
85
85
|
on:change={(event) => {
|
|
86
|
-
|
|
86
|
+
disabledVisionServices.set(visionService.name, !event.detail)
|
|
87
87
|
}}
|
|
88
88
|
/>
|
|
89
89
|
</div>
|
|
@@ -92,11 +92,14 @@
|
|
|
92
92
|
{@const { children = [] } = node}
|
|
93
93
|
<div
|
|
94
94
|
{...api.getBranchProps(nodeProps)}
|
|
95
|
-
class={
|
|
96
|
-
'
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
class={[
|
|
96
|
+
'w-full',
|
|
97
|
+
{
|
|
98
|
+
'text-disabled': !isVisible,
|
|
99
|
+
'bg-medium': selected,
|
|
100
|
+
sticky: true,
|
|
101
|
+
},
|
|
102
|
+
]}
|
|
100
103
|
>
|
|
101
104
|
<div {...api.getBranchControlProps(nodeProps)}>
|
|
102
105
|
<span
|
|
@@ -106,7 +109,7 @@
|
|
|
106
109
|
<ChevronRight size={14} />
|
|
107
110
|
</span>
|
|
108
111
|
<span
|
|
109
|
-
class="flex items-center"
|
|
112
|
+
class="flex items-center overflow-hidden text-ellipsis"
|
|
110
113
|
{...api.getBranchTextProps(nodeProps)}
|
|
111
114
|
>
|
|
112
115
|
{node.entity.get(traits.Name)}
|
|
@@ -151,7 +154,7 @@
|
|
|
151
154
|
class={{ 'flex justify-between': true, 'text-disabled': !isVisible, 'bg-medium': selected }}
|
|
152
155
|
{...api.getItemProps(nodeProps)}
|
|
153
156
|
>
|
|
154
|
-
<span class="flex items-center gap-1.5">
|
|
157
|
+
<span class="flex items-center gap-1.5 overflow-hidden text-nowrap text-ellipsis">
|
|
155
158
|
{node.entity.get(traits.Name)}
|
|
156
159
|
</span>
|
|
157
160
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Vector2Like } from 'three'
|
|
3
2
|
import { draggable } from '@neodrag/svelte'
|
|
4
3
|
import Tree from './Tree.svelte'
|
|
5
4
|
import { useSelectedEntity } from '../../hooks/useSelection.svelte'
|
|
@@ -11,16 +10,13 @@
|
|
|
11
10
|
import { useEnvironment } from '../../hooks/useEnvironment.svelte'
|
|
12
11
|
import { usePartID } from '../../hooks/usePartID.svelte'
|
|
13
12
|
import { usePartConfig } from '../../hooks/usePartConfig.svelte'
|
|
14
|
-
import { traits, useWorld } from '../../ecs'
|
|
13
|
+
import { traits, useQuery, useWorld } from '../../ecs'
|
|
15
14
|
import { IsExcluded, type Entity } from 'koota'
|
|
16
15
|
import { buildTreeNodes, type TreeNode } from './buildTree'
|
|
17
16
|
import { MIN_DIMENSIONS, useResizable } from '../../hooks/useResizable.svelte'
|
|
18
|
-
import { PersistedState } from 'runed'
|
|
19
17
|
|
|
20
18
|
const { ...rest } = $props()
|
|
21
19
|
|
|
22
|
-
const dragPosition = new PersistedState<Vector2Like | undefined>('tree-drag-position', undefined)
|
|
23
|
-
|
|
24
20
|
provideTreeExpandedContext()
|
|
25
21
|
|
|
26
22
|
let container = $state.raw<HTMLDivElement>()
|
|
@@ -38,32 +34,13 @@
|
|
|
38
34
|
|
|
39
35
|
const worldEntity = world.spawn(IsExcluded, traits.Name('World'))
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
let nodeMap = $state.raw<Record<string, TreeNode | undefined>>({})
|
|
43
|
-
|
|
44
|
-
let pending = false
|
|
45
|
-
const flush = () => {
|
|
46
|
-
if (pending) return
|
|
47
|
-
pending = true
|
|
48
|
-
|
|
49
|
-
window.setTimeout(() => {
|
|
50
|
-
const results = buildTreeNodes(world.query(traits.Name))
|
|
51
|
-
children = results.rootNodes
|
|
52
|
-
nodeMap = results.nodeMap
|
|
53
|
-
pending = false
|
|
54
|
-
})
|
|
55
|
-
}
|
|
37
|
+
const allEntities = useQuery(traits.Name)
|
|
56
38
|
|
|
57
|
-
|
|
58
|
-
world.onAdd(traits.Parent, flush)
|
|
59
|
-
world.onRemove(traits.Name, flush)
|
|
60
|
-
world.onRemove(traits.Parent, flush)
|
|
61
|
-
world.onChange(traits.Name, flush)
|
|
62
|
-
world.onChange(traits.Parent, flush)
|
|
39
|
+
const { rootNodes, nodeMap } = $derived(buildTreeNodes(allEntities.current))
|
|
63
40
|
|
|
64
41
|
const rootNode = $derived<TreeNode>({
|
|
65
42
|
entity: worldEntity,
|
|
66
|
-
children,
|
|
43
|
+
children: rootNodes,
|
|
67
44
|
})
|
|
68
45
|
|
|
69
46
|
$effect(() => {
|
|
@@ -83,10 +60,6 @@
|
|
|
83
60
|
use:draggable={{
|
|
84
61
|
bounds: 'body',
|
|
85
62
|
handle: dragElement,
|
|
86
|
-
defaultPosition: dragPosition.current,
|
|
87
|
-
onDragEnd(data) {
|
|
88
|
-
dragPosition.current = { x: Math.max(data.offsetX, 0), y: Math.max(data.offsetY, 0) }
|
|
89
|
-
},
|
|
90
63
|
}}
|
|
91
64
|
{...rest}
|
|
92
65
|
>
|
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Vector2Like } from 'three'
|
|
3
2
|
import { draggable } from '@neodrag/svelte'
|
|
4
3
|
import { formatNumeric } from '../../format'
|
|
5
4
|
import Table from '../shared/Table.svelte'
|
|
6
5
|
import { useArmClient } from '../../hooks/useArmClient.svelte'
|
|
7
6
|
import { Icon, Label, Select } from '@viamrobotics/prime-core'
|
|
8
|
-
import { PersistedState } from 'runed'
|
|
9
7
|
|
|
10
8
|
const { ...rest } = $props()
|
|
11
9
|
|
|
12
|
-
const dragPosition = new PersistedState<Vector2Like | undefined>(
|
|
13
|
-
'details-drag-position',
|
|
14
|
-
undefined
|
|
15
|
-
)
|
|
16
|
-
|
|
17
10
|
let dragElement = $state.raw<HTMLElement>()
|
|
18
11
|
|
|
19
12
|
const armClient = useArmClient()
|
|
@@ -28,10 +21,6 @@
|
|
|
28
21
|
use:draggable={{
|
|
29
22
|
bounds: 'body',
|
|
30
23
|
handle: dragElement,
|
|
31
|
-
defaultPosition: dragPosition.current,
|
|
32
|
-
onDragEnd(data) {
|
|
33
|
-
dragPosition.current = { x: data.offsetX, y: data.offsetY }
|
|
34
|
-
},
|
|
35
24
|
}}
|
|
36
25
|
{...rest}
|
|
37
26
|
>
|
package/dist/ecs/traits.d.ts
CHANGED
|
@@ -126,6 +126,10 @@ export declare const PointSize: import("koota").Trait<() => number>;
|
|
|
126
126
|
*/
|
|
127
127
|
export declare const LineWidth: import("koota").Trait<() => number>;
|
|
128
128
|
export declare const ReferenceFrame: import("koota").TagTrait;
|
|
129
|
+
/**
|
|
130
|
+
* This entity can be safetly removed from the scene by the user
|
|
131
|
+
*/
|
|
132
|
+
export declare const Removable: import("koota").TagTrait;
|
|
129
133
|
export declare const Geometry: (geometry: ViamGeometry) => import("koota").TagTrait | [import("koota").Trait<{
|
|
130
134
|
x: number;
|
|
131
135
|
y: number;
|
package/dist/ecs/traits.js
CHANGED
|
@@ -78,6 +78,10 @@ export const PointSize = trait(() => 10);
|
|
|
78
78
|
*/
|
|
79
79
|
export const LineWidth = trait(() => 5);
|
|
80
80
|
export const ReferenceFrame = trait();
|
|
81
|
+
/**
|
|
82
|
+
* This entity can be safetly removed from the scene by the user
|
|
83
|
+
*/
|
|
84
|
+
export const Removable = trait();
|
|
81
85
|
export const Geometry = (geometry) => {
|
|
82
86
|
if (geometry.geometryType.case === 'box') {
|
|
83
87
|
return Box(createBox(geometry.geometryType.value));
|
|
@@ -144,7 +144,7 @@ export const provideDrawAPI = () => {
|
|
|
144
144
|
if (frame.geometry) {
|
|
145
145
|
entityTraits.push(geometryTrait());
|
|
146
146
|
}
|
|
147
|
-
entityTraits.push(traits.Name(name), traits.Pose(pose), traits.DrawAPI, traits.ReferenceFrame);
|
|
147
|
+
entityTraits.push(traits.Name(name), traits.Pose(pose), traits.DrawAPI, traits.ReferenceFrame, traits.Removable);
|
|
148
148
|
const entity = world.spawn(...entityTraits);
|
|
149
149
|
entities.set(name, entity);
|
|
150
150
|
}
|
|
@@ -178,7 +178,7 @@ export const provideDrawAPI = () => {
|
|
|
178
178
|
if (parent && parent !== 'world') {
|
|
179
179
|
entityTraits.push(traits.Parent(parent));
|
|
180
180
|
}
|
|
181
|
-
entityTraits.push(traits.Name(data.label ?? ++geometryIndex), traits.Pose(pose), traits.Color(colorUtil.set(color)), geometryTrait(), traits.DrawAPI);
|
|
181
|
+
entityTraits.push(traits.Name(data.label ?? ++geometryIndex), traits.Pose(pose), traits.Color(colorUtil.set(color)), geometryTrait(), traits.DrawAPI, traits.Removable);
|
|
182
182
|
const entity = world.spawn(...entityTraits);
|
|
183
183
|
entities.set(name, entity);
|
|
184
184
|
};
|
|
@@ -201,7 +201,7 @@ export const provideDrawAPI = () => {
|
|
|
201
201
|
existing.set(traits.LinePositions, points);
|
|
202
202
|
return;
|
|
203
203
|
}
|
|
204
|
-
const entity = world.spawn(traits.Name(name), traits.Color(colorUtil.set(color)), traits.LinePositions(points), traits.DrawAPI);
|
|
204
|
+
const entity = world.spawn(traits.Name(name), traits.Color(colorUtil.set(color)), traits.LinePositions(points), traits.DrawAPI, traits.Removable);
|
|
205
205
|
entities.set(name, entity);
|
|
206
206
|
};
|
|
207
207
|
const vec3 = new Vector3();
|
|
@@ -211,7 +211,7 @@ export const provideDrawAPI = () => {
|
|
|
211
211
|
const nColors = reader.read();
|
|
212
212
|
const arrowHeadAtPose = reader.read();
|
|
213
213
|
const entities = [];
|
|
214
|
-
const entity = world.spawn(traits.Name(`Arrow group ${++poseIndex}`), traits.Positions(reader.readF32Array(nPoints * STRIDE.ARROWS)), traits.Colors(reader.readU8Array(nColors * STRIDE.COLORS_RGB)), traits.Arrows({ headAtPose: arrowHeadAtPose === 1 }), traits.DrawAPI);
|
|
214
|
+
const entity = world.spawn(traits.Name(`Arrow group ${++poseIndex}`), traits.Positions(reader.readF32Array(nPoints * STRIDE.ARROWS)), traits.Colors(reader.readU8Array(nColors * STRIDE.COLORS_RGB)), traits.Arrows({ headAtPose: arrowHeadAtPose === 1 }), traits.DrawAPI, traits.Removable);
|
|
215
215
|
entities.push(entity);
|
|
216
216
|
};
|
|
217
217
|
const drawPoints = async (reader) => {
|
|
@@ -251,7 +251,7 @@ export const provideDrawAPI = () => {
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
const geometry = createBufferGeometry(positions, colors);
|
|
254
|
-
world.spawn(traits.Name(label), traits.Color(colorUtil.set(r, g, b)), traits.BufferGeometry(geometry), traits.Points, traits.DrawAPI);
|
|
254
|
+
world.spawn(traits.Name(label), traits.Color(colorUtil.set(r, g, b)), traits.BufferGeometry(geometry), traits.Points, traits.DrawAPI, traits.Removable);
|
|
255
255
|
};
|
|
256
256
|
const drawLine = async (reader) => {
|
|
257
257
|
// Read label length
|
|
@@ -279,7 +279,7 @@ export const provideDrawAPI = () => {
|
|
|
279
279
|
points[i + 1] = reader.read();
|
|
280
280
|
points[i + 2] = reader.read();
|
|
281
281
|
}
|
|
282
|
-
world.spawn(traits.Name(label), traits.Color({ r, g, b }), traits.LinePositions(points), traits.PointColor({ r: dotR, g: dotG, b: dotB }), traits.DrawAPI);
|
|
282
|
+
world.spawn(traits.Name(label), traits.Color({ r, g, b }), traits.LinePositions(points), traits.PointColor({ r: dotR, g: dotG, b: dotB }), traits.DrawAPI, traits.Removable);
|
|
283
283
|
};
|
|
284
284
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
285
285
|
const drawGeometries = (geometries, colors, parent) => {
|
|
@@ -293,14 +293,16 @@ export const provideDrawAPI = () => {
|
|
|
293
293
|
const blob = new Blob([buffer], { type: 'model/gltf-binary' });
|
|
294
294
|
const url = URL.createObjectURL(blob);
|
|
295
295
|
const gltf = await loader.loadAsync(url);
|
|
296
|
-
world.spawn(traits.Name(gltf.scene.name), traits.GLTF({ source: { gltf }, animationName: '' }), traits.DrawAPI);
|
|
296
|
+
world.spawn(traits.Name(gltf.scene.name), traits.GLTF({ source: { gltf }, animationName: '' }), traits.DrawAPI, traits.Removable);
|
|
297
297
|
URL.revokeObjectURL(url);
|
|
298
298
|
};
|
|
299
299
|
const remove = (names) => {
|
|
300
300
|
for (const name of names) {
|
|
301
301
|
for (const entity of world.query(traits.DrawAPI)) {
|
|
302
302
|
if (entity.get(traits.Name) === name) {
|
|
303
|
-
|
|
303
|
+
if (world.has(entity)) {
|
|
304
|
+
entity.destroy();
|
|
305
|
+
}
|
|
304
306
|
entities.delete(name);
|
|
305
307
|
}
|
|
306
308
|
}
|
|
@@ -308,7 +310,9 @@ export const provideDrawAPI = () => {
|
|
|
308
310
|
};
|
|
309
311
|
const removeAll = () => {
|
|
310
312
|
for (const entity of world.query(traits.DrawAPI)) {
|
|
311
|
-
|
|
313
|
+
if (world.has(entity)) {
|
|
314
|
+
entity.destroy();
|
|
315
|
+
}
|
|
312
316
|
}
|
|
313
317
|
entities.clear();
|
|
314
318
|
geometryIndex = 0;
|
|
@@ -6,7 +6,7 @@ export declare const RefreshRates: {
|
|
|
6
6
|
type Context = {
|
|
7
7
|
refreshRates: SvelteMap<string, number>;
|
|
8
8
|
disabledCameras: SvelteMap<string, boolean>;
|
|
9
|
-
|
|
9
|
+
disabledVisionServices: SvelteMap<string, boolean>;
|
|
10
10
|
};
|
|
11
11
|
export declare const provideMachineSettings: () => void;
|
|
12
12
|
export declare const useMachineSettings: () => Context;
|
|
@@ -4,7 +4,7 @@ import { SvelteMap } from 'svelte/reactivity';
|
|
|
4
4
|
const key = Symbol('polling-rate-context');
|
|
5
5
|
const refreshRatesKey = 'polling-rate';
|
|
6
6
|
const disabledCamerasKey = 'disabled-cameras';
|
|
7
|
-
const
|
|
7
|
+
const disabledVisionServicesKey = 'disabled-vision-services-object-pointcloud';
|
|
8
8
|
export const RefreshRates = {
|
|
9
9
|
poses: 'poses',
|
|
10
10
|
pointclouds: 'pointclouds',
|
|
@@ -22,15 +22,15 @@ export const provideMachineSettings = () => {
|
|
|
22
22
|
[RefreshRates.pointclouds, -1],
|
|
23
23
|
]);
|
|
24
24
|
const disabledCameras = new SvelteMap();
|
|
25
|
-
const
|
|
25
|
+
const disabledVisionServices = new SvelteMap();
|
|
26
26
|
get(refreshRatesKey).then((entries) => {
|
|
27
27
|
setFromEntries(refreshRates, entries);
|
|
28
28
|
});
|
|
29
29
|
get(disabledCamerasKey).then((entries) => {
|
|
30
30
|
setFromEntries(disabledCameras, entries);
|
|
31
31
|
});
|
|
32
|
-
get(
|
|
33
|
-
setFromEntries(
|
|
32
|
+
get(disabledVisionServicesKey).then((entries) => {
|
|
33
|
+
setFromEntries(disabledVisionServices, entries);
|
|
34
34
|
});
|
|
35
35
|
$effect(() => {
|
|
36
36
|
set(refreshRatesKey, [...refreshRates.entries()]);
|
|
@@ -38,6 +38,9 @@ export const provideMachineSettings = () => {
|
|
|
38
38
|
$effect(() => {
|
|
39
39
|
set(disabledCamerasKey, [...disabledCameras.entries()]);
|
|
40
40
|
});
|
|
41
|
+
$effect(() => {
|
|
42
|
+
set(disabledVisionServicesKey, [...disabledVisionServices.entries()]);
|
|
43
|
+
});
|
|
41
44
|
setContext(key, {
|
|
42
45
|
get refreshRates() {
|
|
43
46
|
return refreshRates;
|
|
@@ -45,8 +48,8 @@ export const provideMachineSettings = () => {
|
|
|
45
48
|
get disabledCameras() {
|
|
46
49
|
return disabledCameras;
|
|
47
50
|
},
|
|
48
|
-
get
|
|
49
|
-
return
|
|
51
|
+
get disabledVisionServices() {
|
|
52
|
+
return disabledVisionServices;
|
|
50
53
|
},
|
|
51
54
|
});
|
|
52
55
|
};
|
|
@@ -3,39 +3,47 @@ import { useFocusedEntity, useSelectedEntity } from './useSelection.svelte';
|
|
|
3
3
|
import { useVisibility } from './useVisibility.svelte';
|
|
4
4
|
import { Vector2 } from 'three';
|
|
5
5
|
export const useObjectEvents = (entity) => {
|
|
6
|
+
const down = new Vector2();
|
|
6
7
|
const selectedEntity = useSelectedEntity();
|
|
7
8
|
const focusedEntity = useFocusedEntity();
|
|
8
9
|
const visibility = useVisibility();
|
|
9
|
-
const down = new Vector2();
|
|
10
|
-
const currentEntity = $derived(entity());
|
|
11
10
|
const cursor = useCursor();
|
|
11
|
+
const currentEntity = $derived(entity());
|
|
12
|
+
const visible = $derived(currentEntity ? (visibility.get(currentEntity) ?? true) : true);
|
|
13
|
+
const onpointerenter = (event) => {
|
|
14
|
+
event.stopPropagation();
|
|
15
|
+
cursor.onPointerEnter();
|
|
16
|
+
};
|
|
17
|
+
const onpointerleave = (event) => {
|
|
18
|
+
event.stopPropagation();
|
|
19
|
+
cursor.onPointerLeave();
|
|
20
|
+
};
|
|
21
|
+
const ondblclick = (event) => {
|
|
22
|
+
event.stopPropagation();
|
|
23
|
+
focusedEntity.set(currentEntity, event.instanceId ?? event.batchId);
|
|
24
|
+
};
|
|
25
|
+
const onpointerdown = (event) => {
|
|
26
|
+
down.copy(event.pointer);
|
|
27
|
+
};
|
|
28
|
+
const onclick = (event) => {
|
|
29
|
+
event.stopPropagation();
|
|
30
|
+
if (down.distanceToSquared(event.pointer) < 0.1) {
|
|
31
|
+
selectedEntity.set(currentEntity, event.instanceId ?? event.batchId);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
$effect(() => {
|
|
35
|
+
if (!visible) {
|
|
36
|
+
cursor.onPointerLeave();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
12
39
|
return {
|
|
13
40
|
get visible() {
|
|
14
|
-
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
return visibility.get(currentEntity) ?? true;
|
|
18
|
-
},
|
|
19
|
-
onpointerenter: (event) => {
|
|
20
|
-
event.stopPropagation();
|
|
21
|
-
cursor.onPointerEnter();
|
|
22
|
-
},
|
|
23
|
-
onpointerleave: (event) => {
|
|
24
|
-
event.stopPropagation();
|
|
25
|
-
cursor.onPointerLeave();
|
|
26
|
-
},
|
|
27
|
-
ondblclick: (event) => {
|
|
28
|
-
event.stopPropagation();
|
|
29
|
-
focusedEntity.set(currentEntity, event.instanceId ?? event.batchId);
|
|
30
|
-
},
|
|
31
|
-
onpointerdown: (event) => {
|
|
32
|
-
down.copy(event.pointer);
|
|
33
|
-
},
|
|
34
|
-
onclick: (event) => {
|
|
35
|
-
event.stopPropagation();
|
|
36
|
-
if (down.distanceToSquared(event.pointer) < 0.1) {
|
|
37
|
-
selectedEntity.set(currentEntity, event.instanceId ?? event.batchId);
|
|
38
|
-
}
|
|
41
|
+
return visible;
|
|
39
42
|
},
|
|
43
|
+
onpointerenter,
|
|
44
|
+
onpointerleave,
|
|
45
|
+
ondblclick,
|
|
46
|
+
onpointerdown,
|
|
47
|
+
onclick,
|
|
40
48
|
};
|
|
41
49
|
};
|
|
@@ -13,7 +13,7 @@ const key = Symbol('pointcloud-object-context');
|
|
|
13
13
|
export const providePointcloudObjects = (partID) => {
|
|
14
14
|
const world = useWorld();
|
|
15
15
|
const environment = useEnvironment();
|
|
16
|
-
const { refreshRates,
|
|
16
|
+
const { refreshRates, disabledVisionServices } = useMachineSettings();
|
|
17
17
|
const services = useResourceNames(partID, 'vision');
|
|
18
18
|
const clients = $derived(services.current.map((service) => createResourceClient(VisionClient, partID, () => service.name)));
|
|
19
19
|
const propQueries = $derived(clients.map((client) => [
|
|
@@ -31,7 +31,8 @@ export const providePointcloudObjects = (partID) => {
|
|
|
31
31
|
if (environment.current.viewerMode === 'monitor' &&
|
|
32
32
|
fetchedPropQueries &&
|
|
33
33
|
client.current?.name &&
|
|
34
|
-
interval !== RefetchRates.OFF
|
|
34
|
+
interval !== RefetchRates.OFF &&
|
|
35
|
+
disabledVisionServices.get(client.current?.name) !== true) {
|
|
35
36
|
results.push(client);
|
|
36
37
|
}
|
|
37
38
|
}
|
|
@@ -47,8 +48,8 @@ export const providePointcloudObjects = (partID) => {
|
|
|
47
48
|
$effect(() => {
|
|
48
49
|
for (const [name, query] of propQueries) {
|
|
49
50
|
if (name && query.data?.objectPointCloudsSupported === false) {
|
|
50
|
-
if (
|
|
51
|
-
|
|
51
|
+
if (disabledVisionServices.get(name) === undefined) {
|
|
52
|
+
disabledVisionServices.set(name, true);
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
}
|
|
@@ -83,7 +84,11 @@ export const providePointcloudObjects = (partID) => {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
Promise.allSettled(responses.map(async ([name, pointcloudObjects]) => {
|
|
86
|
-
const pointclouds = await Promise.all(pointcloudObjects
|
|
87
|
+
const pointclouds = await Promise.all(pointcloudObjects
|
|
88
|
+
.filter((value) => value !== undefined)
|
|
89
|
+
.map((value) => {
|
|
90
|
+
return parsePcdInWorker(new Uint8Array(value.pointCloud));
|
|
91
|
+
}));
|
|
87
92
|
return {
|
|
88
93
|
name,
|
|
89
94
|
pointclouds,
|
|
@@ -156,7 +161,9 @@ export const providePointcloudObjects = (partID) => {
|
|
|
156
161
|
// Clean up old entities
|
|
157
162
|
for (const [label, entity] of entities) {
|
|
158
163
|
if (!active[label]) {
|
|
159
|
-
|
|
164
|
+
if (world.has(entity)) {
|
|
165
|
+
entity.destroy();
|
|
166
|
+
}
|
|
160
167
|
entities.delete(label);
|
|
161
168
|
}
|
|
162
169
|
}
|
|
@@ -117,7 +117,9 @@ export const providePointclouds = (partID) => {
|
|
|
117
117
|
// Clean up old entities
|
|
118
118
|
for (const [name, entity] of entities) {
|
|
119
119
|
if (!queryMap[name]?.data) {
|
|
120
|
-
|
|
120
|
+
if (world.has(entity)) {
|
|
121
|
+
entity.destroy();
|
|
122
|
+
}
|
|
121
123
|
entities.delete(name);
|
|
122
124
|
}
|
|
123
125
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getContext, setContext } from 'svelte';
|
|
1
|
+
import { useThrelte } from '@threlte/core';
|
|
2
|
+
import { getContext, setContext, untrack } from 'svelte';
|
|
3
3
|
import { Object3D } from 'three';
|
|
4
4
|
import { traits, useWorld } from '../ecs';
|
|
5
|
+
import { useEnvironment } from './useEnvironment.svelte';
|
|
5
6
|
const selectedKey = Symbol('selected-frame-context');
|
|
6
7
|
const focusedKey = Symbol('focused-frame-context');
|
|
7
8
|
const focusedObject3dKey = Symbol('focused-object-3d-context');
|
|
8
9
|
export const provideSelection = () => {
|
|
9
10
|
const world = useWorld();
|
|
10
11
|
const { scene } = useThrelte();
|
|
12
|
+
const environment = useEnvironment();
|
|
11
13
|
let selected = $state.raw();
|
|
12
14
|
let selectedInstance = $state();
|
|
13
15
|
let focused = $state.raw();
|
|
@@ -46,18 +48,15 @@ export const provideSelection = () => {
|
|
|
46
48
|
},
|
|
47
49
|
};
|
|
48
50
|
setContext(focusedKey, focusedEntityContext);
|
|
49
|
-
const focusedObject3d = $derived.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const focusedObject3d = $derived(focused ? scene.getObjectByName(focused)?.clone() : undefined);
|
|
52
|
+
$effect(() => {
|
|
53
|
+
const previousMode = untrack(() => environment.current.viewerMode);
|
|
54
|
+
if (focusedObject3d) {
|
|
55
|
+
environment.current.viewerMode = 'focus';
|
|
56
|
+
return () => {
|
|
57
|
+
environment.current.viewerMode = previousMode;
|
|
58
|
+
};
|
|
53
59
|
}
|
|
54
|
-
const object = scene.getObjectByName(name)?.clone();
|
|
55
|
-
object?.traverse((child) => {
|
|
56
|
-
if (isInstanceOf(child, 'LineSegments')) {
|
|
57
|
-
child.raycast = () => null;
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
return object;
|
|
61
60
|
});
|
|
62
61
|
setContext(focusedObject3dKey, {
|
|
63
62
|
get current() {
|
|
@@ -71,7 +71,9 @@ const createWorldState = (client) => {
|
|
|
71
71
|
const entity = entities.get(uuid);
|
|
72
72
|
if (!entity)
|
|
73
73
|
return;
|
|
74
|
-
|
|
74
|
+
if (world.has(entity)) {
|
|
75
|
+
entity.destroy();
|
|
76
|
+
}
|
|
75
77
|
entities.delete(uuid);
|
|
76
78
|
};
|
|
77
79
|
const updateEntity = (transform, changes) => {
|
|
@@ -202,7 +204,9 @@ const createWorldState = (client) => {
|
|
|
202
204
|
});
|
|
203
205
|
return () => {
|
|
204
206
|
for (const [, entity] of entities) {
|
|
205
|
-
|
|
207
|
+
if (world.has(entity)) {
|
|
208
|
+
entity.destroy();
|
|
209
|
+
}
|
|
206
210
|
}
|
|
207
211
|
};
|
|
208
212
|
};
|
|
@@ -9,7 +9,12 @@ self.onmessage = async (event) => {
|
|
|
9
9
|
try {
|
|
10
10
|
const pcd = loader.parse(data.buffer);
|
|
11
11
|
if (pcd.geometry) {
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Positions is _usually_ defined. However, we have experienced parsing PCDs from Viam APIs that
|
|
14
|
+
* result in the Three.js parser not attaching this attribute, throwing errors downstream.
|
|
15
|
+
*/
|
|
16
|
+
const positions = pcd.geometry.attributes.position?.array ??
|
|
17
|
+
new Float32Array(0);
|
|
13
18
|
const colors = pcd.geometry.attributes.color?.array ?? null;
|
|
14
19
|
postMessage({ positions, colors, id }, colors ? [positions.buffer, colors.buffer] : [positions.buffer]);
|
|
15
20
|
}
|
package/dist/snapshot.js
CHANGED
|
@@ -83,6 +83,7 @@ const spawnTransformEntity = (world, transform) => {
|
|
|
83
83
|
traits.Geometry(transform.physicalObject ?? Geometry.fromJson({})),
|
|
84
84
|
traits.Center(transform.physicalObject?.center),
|
|
85
85
|
traits.SnapshotAPI,
|
|
86
|
+
traits.Removable,
|
|
86
87
|
];
|
|
87
88
|
const poseInFrame = transform.poseInObserverFrame;
|
|
88
89
|
entityTraits.push(traits.Pose(poseInFrame?.pose));
|
|
@@ -117,7 +118,7 @@ const spawnEntitiesFromDrawing = (world, drawing) => {
|
|
|
117
118
|
if (colors) {
|
|
118
119
|
entityTraits.push(traits.Colors(colors));
|
|
119
120
|
}
|
|
120
|
-
const entity = world.spawn(...entityTraits, traits.
|
|
121
|
+
const entity = world.spawn(...entityTraits, traits.Arrows({ headAtPose: true }), traits.Instances({ count: poses.length / STRIDE.ARROWS }), traits.SnapshotAPI, traits.Removable);
|
|
121
122
|
entities.push(entity);
|
|
122
123
|
}
|
|
123
124
|
else if (geometryType?.case === 'model') {
|
|
@@ -129,7 +130,7 @@ const spawnEntitiesFromDrawing = (world, drawing) => {
|
|
|
129
130
|
if (parent) {
|
|
130
131
|
rootEntityTraits.push(traits.Parent(parent));
|
|
131
132
|
}
|
|
132
|
-
const rootEntity = world.spawn(...rootEntityTraits, traits.SnapshotAPI);
|
|
133
|
+
const rootEntity = world.spawn(...rootEntityTraits, traits.SnapshotAPI, traits.Removable);
|
|
133
134
|
entities.push(rootEntity);
|
|
134
135
|
let i = 1;
|
|
135
136
|
for (const asset of geometryType.value.assets) {
|
|
@@ -152,7 +153,7 @@ const spawnEntitiesFromDrawing = (world, drawing) => {
|
|
|
152
153
|
animationName: geometryType.value.animationName ?? '',
|
|
153
154
|
}));
|
|
154
155
|
}
|
|
155
|
-
const entity = world.spawn(...entityTraits, traits.SnapshotAPI);
|
|
156
|
+
const entity = world.spawn(...entityTraits, traits.SnapshotAPI, traits.Removable);
|
|
156
157
|
entities.push(entity);
|
|
157
158
|
}
|
|
158
159
|
}
|
|
@@ -227,7 +228,7 @@ const spawnEntitiesFromDrawing = (world, drawing) => {
|
|
|
227
228
|
}
|
|
228
229
|
entityTraits.push(traits.LinePositions(points));
|
|
229
230
|
}
|
|
230
|
-
const entity = world.spawn(...entityTraits, traits.SnapshotAPI);
|
|
231
|
+
const entity = world.spawn(...entityTraits, traits.SnapshotAPI, traits.Removable);
|
|
231
232
|
entities.push(entity);
|
|
232
233
|
}
|
|
233
234
|
return entities;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RawShaderMaterial, FrontSide, Group, InstancedBufferAttribute, DynamicDrawUsage, Mesh, BufferGeometry, InstancedInterleavedBuffer, InterleavedBufferAttribute, Material, Color, Vector3, Box3, } from 'three';
|
|
2
|
-
import vertexShader from './vertex.glsl
|
|
3
|
-
import fragmentShader from './fragment.glsl
|
|
2
|
+
import vertexShader from './vertex.glsl';
|
|
3
|
+
import fragmentShader from './fragment.glsl';
|
|
4
4
|
import { createHeadGeometry, createShaftGeometry, toInstanced } from './geometry';
|
|
5
5
|
import { computeBoundingBox } from './box';
|
|
6
6
|
const defaults = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viamrobotics/motion-tools",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "Motion visualization with Viam",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@skeletonlabs/skeleton": "3.2.0",
|
|
18
18
|
"@skeletonlabs/skeleton-svelte": "1.5.1",
|
|
19
19
|
"@sveltejs/adapter-static": "3.0.9",
|
|
20
|
-
"@sveltejs/kit": "2.
|
|
20
|
+
"@sveltejs/kit": "2.49.5",
|
|
21
21
|
"@sveltejs/package": "2.5.0",
|
|
22
22
|
"@sveltejs/vite-plugin-svelte": "6.1.4",
|
|
23
23
|
"@tailwindcss/forms": "0.5.10",
|
|
@@ -68,10 +68,14 @@
|
|
|
68
68
|
"typescript-eslint": "8.42.0",
|
|
69
69
|
"vite": "7.1.11",
|
|
70
70
|
"vite-plugin-devtools-json": "1.0.0",
|
|
71
|
+
"vite-plugin-glsl": "^1.5.5",
|
|
71
72
|
"vite-plugin-mkcert": "1.17.8",
|
|
72
73
|
"vitest": "3.2.4"
|
|
73
74
|
},
|
|
74
75
|
"peerDependencies": {
|
|
76
|
+
"@ag-grid-community/client-side-row-model": ">=32.3.0",
|
|
77
|
+
"@ag-grid-community/core": ">=32.3.0",
|
|
78
|
+
"@ag-grid-community/styles": ">=32.3.0",
|
|
75
79
|
"@dimforge/rapier3d-compat": ">=0.17",
|
|
76
80
|
"@threlte/core": ">=8",
|
|
77
81
|
"@threlte/extras": ">=9",
|
|
@@ -81,6 +85,7 @@
|
|
|
81
85
|
"@viamrobotics/sdk": ">=0.38",
|
|
82
86
|
"@viamrobotics/svelte-sdk": ">=0.1",
|
|
83
87
|
"@zag-js/collapsible": ">=1",
|
|
88
|
+
"@zag-js/dialog": ">=1.31",
|
|
84
89
|
"@zag-js/floating-panel": ">=1",
|
|
85
90
|
"@zag-js/svelte": ">=1",
|
|
86
91
|
"@zag-js/tree-view": ">=1",
|
|
@@ -89,11 +94,7 @@
|
|
|
89
94
|
"lucide-svelte": ">=0.511",
|
|
90
95
|
"runed": ">=0.28",
|
|
91
96
|
"svelte": ">=5",
|
|
92
|
-
"svelte-virtuallists": ">=1"
|
|
93
|
-
"@ag-grid-community/client-side-row-model": ">=32.3.0",
|
|
94
|
-
"@ag-grid-community/core": ">=32.3.0",
|
|
95
|
-
"@ag-grid-community/styles": ">=32.3.0",
|
|
96
|
-
"@zag-js/dialog": ">=1.31"
|
|
97
|
+
"svelte-virtuallists": ">=1"
|
|
97
98
|
},
|
|
98
99
|
"engines": {
|
|
99
100
|
"node": ">=22.12.0"
|