@viamrobotics/motion-tools 0.1.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/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/WorldObject.d.ts +33 -0
- package/dist/WorldObject.js +18 -0
- package/dist/assert.d.ts +14 -0
- package/dist/assert.js +21 -0
- package/dist/color.d.ts +8 -0
- package/dist/color.js +14 -0
- package/dist/components/App.svelte +63 -0
- package/dist/components/App.svelte.d.ts +8 -0
- package/dist/components/AxesHelper.svelte +17 -0
- package/dist/components/AxesHelper.svelte.d.ts +4 -0
- package/dist/components/BentPlaneGeometry.svelte +52 -0
- package/dist/components/BentPlaneGeometry.svelte.d.ts +29 -0
- package/dist/components/Camera.svelte +45 -0
- package/dist/components/Camera.svelte.d.ts +5 -0
- package/dist/components/CameraControls.svelte +82 -0
- package/dist/components/CameraControls.svelte.d.ts +9 -0
- package/dist/components/Details.svelte +161 -0
- package/dist/components/Details.svelte.d.ts +3 -0
- package/dist/components/Detections.svelte +41 -0
- package/dist/components/Detections.svelte.d.ts +3 -0
- package/dist/components/DetectionsPlane.svelte +23 -0
- package/dist/components/DetectionsPlane.svelte.d.ts +21 -0
- package/dist/components/DomPortal.svelte +20 -0
- package/dist/components/DomPortal.svelte.d.ts +5 -0
- package/dist/components/Focus.svelte +49 -0
- package/dist/components/Focus.svelte.d.ts +3 -0
- package/dist/components/Frame.svelte +112 -0
- package/dist/components/Frame.svelte.d.ts +16 -0
- package/dist/components/Frames.svelte +54 -0
- package/dist/components/Frames.svelte.d.ts +18 -0
- package/dist/components/Pointcloud.svelte +55 -0
- package/dist/components/Pointcloud.svelte.d.ts +10 -0
- package/dist/components/Pointclouds.svelte +21 -0
- package/dist/components/Pointclouds.svelte.d.ts +18 -0
- package/dist/components/Pose.svelte +19 -0
- package/dist/components/Pose.svelte.d.ts +12 -0
- package/dist/components/RefreshRate.svelte +47 -0
- package/dist/components/RefreshRate.svelte.d.ts +8 -0
- package/dist/components/Scene.svelte +81 -0
- package/dist/components/Scene.svelte.d.ts +7 -0
- package/dist/components/SceneProviders.svelte +41 -0
- package/dist/components/SceneProviders.svelte.d.ts +9 -0
- package/dist/components/Selected.svelte +44 -0
- package/dist/components/Selected.svelte.d.ts +3 -0
- package/dist/components/Shapes.svelte +49 -0
- package/dist/components/Shapes.svelte.d.ts +18 -0
- package/dist/components/StaticGeometries.svelte +79 -0
- package/dist/components/StaticGeometries.svelte.d.ts +18 -0
- package/dist/components/Tree/Settings.svelte +54 -0
- package/dist/components/Tree/Settings.svelte.d.ts +18 -0
- package/dist/components/Tree/Tree.svelte +204 -0
- package/dist/components/Tree/Tree.svelte.d.ts +10 -0
- package/dist/components/Tree/TreeContainer.svelte +70 -0
- package/dist/components/Tree/TreeContainer.svelte.d.ts +3 -0
- package/dist/components/Tree/buildTree.d.ts +11 -0
- package/dist/components/Tree/buildTree.js +29 -0
- package/dist/components/Tree/useExpanded.svelte.d.ts +5 -0
- package/dist/components/Tree/useExpanded.svelte.js +21 -0
- package/dist/components/WorldObject.svelte +27 -0
- package/dist/components/WorldObject.svelte.d.ts +11 -0
- package/dist/components/XR.svelte +20 -0
- package/dist/components/XR.svelte.d.ts +3 -0
- package/dist/components/models/README.md +5 -0
- package/dist/components/null-states/Connection.svelte +0 -0
- package/dist/components/null-states/Connection.svelte.d.ts +26 -0
- package/dist/components/portal/Portal.svelte +25 -0
- package/dist/components/portal/Portal.svelte.d.ts +8 -0
- package/dist/components/portal/PortalTarget.svelte +18 -0
- package/dist/components/portal/PortalTarget.svelte.d.ts +6 -0
- package/dist/components/portal/index.d.ts +2 -0
- package/dist/components/portal/index.js +2 -0
- package/dist/components/portal/usePortalContext.svelte.d.ts +5 -0
- package/dist/components/portal/usePortalContext.svelte.js +8 -0
- package/dist/components/xr/CameraFeed.svelte +81 -0
- package/dist/components/xr/CameraFeed.svelte.d.ts +6 -0
- package/dist/components/xr/Controllers.svelte +71 -0
- package/dist/components/xr/Controllers.svelte.d.ts +3 -0
- package/dist/components/xr/Draggable.svelte +101 -0
- package/dist/components/xr/Draggable.svelte.d.ts +11 -0
- package/dist/components/xr/HandCollider.svelte +19 -0
- package/dist/components/xr/HandCollider.svelte.d.ts +18 -0
- package/dist/components/xr/Hands.svelte +24 -0
- package/dist/components/xr/Hands.svelte.d.ts +18 -0
- package/dist/components/xr/OriginMarker.svelte +100 -0
- package/dist/components/xr/OriginMarker.svelte.d.ts +3 -0
- package/dist/components/xr/PointDistance.svelte +52 -0
- package/dist/components/xr/PointDistance.svelte.d.ts +3 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useConnectionConfigs.svelte.d.ts +17 -0
- package/dist/hooks/useConnectionConfigs.svelte.js +40 -0
- package/dist/hooks/useControls.svelte.d.ts +7 -0
- package/dist/hooks/useControls.svelte.js +16 -0
- package/dist/hooks/useDraggable.svelte.d.ts +2 -0
- package/dist/hooks/useDraggable.svelte.js +25 -0
- package/dist/hooks/useFrames.svelte.d.ts +9 -0
- package/dist/hooks/useFrames.svelte.js +50 -0
- package/dist/hooks/useGeometries.svelte.d.ts +7 -0
- package/dist/hooks/useGeometries.svelte.js +58 -0
- package/dist/hooks/useMotionClient.svelte.d.ts +8 -0
- package/dist/hooks/useMotionClient.svelte.js +31 -0
- package/dist/hooks/useObjectEvents.svelte.d.ts +9 -0
- package/dist/hooks/useObjectEvents.svelte.js +31 -0
- package/dist/hooks/useObjects.svelte.d.ts +7 -0
- package/dist/hooks/useObjects.svelte.js +33 -0
- package/dist/hooks/usePartID.svelte.d.ts +6 -0
- package/dist/hooks/usePartID.svelte.js +14 -0
- package/dist/hooks/usePersistentUUIDs.svelte.d.ts +5 -0
- package/dist/hooks/usePersistentUUIDs.svelte.js +14 -0
- package/dist/hooks/usePointclouds.svelte.d.ts +7 -0
- package/dist/hooks/usePointclouds.svelte.js +58 -0
- package/dist/hooks/usePose.svelte.d.ts +3 -0
- package/dist/hooks/usePose.svelte.js +44 -0
- package/dist/hooks/usePoses.svelte.d.ts +12 -0
- package/dist/hooks/usePoses.svelte.js +63 -0
- package/dist/hooks/useRefreshRates.svelte.d.ts +5 -0
- package/dist/hooks/useRefreshRates.svelte.js +23 -0
- package/dist/hooks/useSelection.svelte.d.ts +40 -0
- package/dist/hooks/useSelection.svelte.js +92 -0
- package/dist/hooks/useShapes.svelte.d.ts +17 -0
- package/dist/hooks/useShapes.svelte.js +264 -0
- package/dist/hooks/useStaticGeometries.svelte.d.ts +9 -0
- package/dist/hooks/useStaticGeometries.svelte.js +37 -0
- package/dist/hooks/useVisibility.svelte.d.ts +5 -0
- package/dist/hooks/useVisibility.svelte.js +22 -0
- package/dist/hooks/xr/useAnchors.svelte.d.ts +0 -0
- package/dist/hooks/xr/useAnchors.svelte.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/keybindings.d.ts +12 -0
- package/dist/keybindings.js +12 -0
- package/dist/loaders/pcd/index.d.ts +4 -0
- package/dist/loaders/pcd/index.js +13 -0
- package/dist/loaders/pcd/worker.d.ts +1 -0
- package/dist/loaders/pcd/worker.js +26 -0
- package/dist/three/AxesHelper.d.ts +5 -0
- package/dist/three/AxesHelper.js +35 -0
- package/dist/three/BatchedArrow.d.ts +30 -0
- package/dist/three/BatchedArrow.js +126 -0
- package/dist/three/BoxHelper.d.ts +50 -0
- package/dist/three/BoxHelper.js +134 -0
- package/dist/three/CapsuleGeometry.d.ts +10 -0
- package/dist/three/CapsuleGeometry.js +17 -0
- package/dist/transform.d.ts +11 -0
- package/dist/transform.js +65 -0
- package/package.json +110 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { provideFrames } from '../hooks/useFrames.svelte'
|
|
3
|
+
import { provideGeometries } from '../hooks/useGeometries.svelte'
|
|
4
|
+
import { providePointclouds } from '../hooks/usePointclouds.svelte'
|
|
5
|
+
import { providePoses } from '../hooks/usePoses.svelte'
|
|
6
|
+
import { usePartID } from '../hooks/usePartID.svelte'
|
|
7
|
+
import { provideSelection } from '../hooks/useSelection.svelte'
|
|
8
|
+
import { provideStaticGeometries } from '../hooks/useStaticGeometries.svelte'
|
|
9
|
+
import { provideVisibility } from '../hooks/useVisibility.svelte'
|
|
10
|
+
import { provideShapes } from '../hooks/useShapes.svelte'
|
|
11
|
+
import { provideRefreshRates } from '../hooks/useRefreshRates.svelte'
|
|
12
|
+
import { provideTransformControls } from '../hooks/useControls.svelte'
|
|
13
|
+
import type { Snippet } from 'svelte'
|
|
14
|
+
import { provideObjects } from '../hooks/useObjects.svelte'
|
|
15
|
+
import { provideMotionClient } from '../hooks/useMotionClient.svelte'
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
children: Snippet<[{ focus: boolean }]>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { children }: Props = $props()
|
|
22
|
+
|
|
23
|
+
const partID = usePartID()
|
|
24
|
+
|
|
25
|
+
provideTransformControls()
|
|
26
|
+
provideVisibility()
|
|
27
|
+
provideRefreshRates()
|
|
28
|
+
|
|
29
|
+
provideStaticGeometries()
|
|
30
|
+
provideShapes()
|
|
31
|
+
provideFrames(() => partID.current)
|
|
32
|
+
providePoses(() => partID.current)
|
|
33
|
+
provideGeometries(() => partID.current)
|
|
34
|
+
providePointclouds(() => partID.current)
|
|
35
|
+
provideMotionClient(() => partID.current)
|
|
36
|
+
provideObjects()
|
|
37
|
+
|
|
38
|
+
const { focus } = provideSelection()
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
{@render children({ focus: focus.current !== undefined })}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
children: Snippet<[{
|
|
4
|
+
focus: boolean;
|
|
5
|
+
}]>;
|
|
6
|
+
}
|
|
7
|
+
declare const SceneProviders: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type SceneProviders = ReturnType<typeof SceneProviders>;
|
|
9
|
+
export default SceneProviders;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Box3, Object3D } from 'three'
|
|
3
|
+
import { T, useTask, useThrelte } from '@threlte/core'
|
|
4
|
+
import { useSelectedObject } from '../hooks/useSelection.svelte'
|
|
5
|
+
import { BoxHelper } from '../three/BoxHelper'
|
|
6
|
+
|
|
7
|
+
const { scene } = useThrelte()
|
|
8
|
+
|
|
9
|
+
const box = new BoxHelper(new Object3D(), 0x000000)
|
|
10
|
+
const selected = useSelectedObject()
|
|
11
|
+
|
|
12
|
+
const { start, stop } = useTask(() => box.update(), { autoStart: false })
|
|
13
|
+
|
|
14
|
+
const box3 = new Box3()
|
|
15
|
+
|
|
16
|
+
$effect.pre(() => {
|
|
17
|
+
if (selected.current) {
|
|
18
|
+
start()
|
|
19
|
+
} else {
|
|
20
|
+
stop()
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
$effect.pre(() => {
|
|
25
|
+
if (!selected.current) {
|
|
26
|
+
box.visible = false
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
box.visible = true
|
|
31
|
+
|
|
32
|
+
if (selected.current.metadata.batched) {
|
|
33
|
+
selected.current.metadata.getBoundingBoxAt?.(box3)
|
|
34
|
+
box.setFromBox3(box3)
|
|
35
|
+
} else {
|
|
36
|
+
const object3d = scene.getObjectByProperty('uuid', selected.current.uuid)
|
|
37
|
+
if (object3d) {
|
|
38
|
+
box.setFromObject(object3d)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<T is={box} />
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { T } from '@threlte/core'
|
|
3
|
+
import { Portal, PortalTarget } from './portal'
|
|
4
|
+
import { useShapes } from '../hooks/useShapes.svelte'
|
|
5
|
+
import WorldObject from './WorldObject.svelte'
|
|
6
|
+
import Frame from './Frame.svelte'
|
|
7
|
+
|
|
8
|
+
const shapes = useShapes()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<T
|
|
12
|
+
name={shapes.object3ds.batchedArrow.object3d.name}
|
|
13
|
+
is={shapes.object3ds.batchedArrow.object3d}
|
|
14
|
+
dispose={false}
|
|
15
|
+
/>
|
|
16
|
+
|
|
17
|
+
{#each shapes.meshes as object (object.uuid)}
|
|
18
|
+
<Portal id={object.referenceFrame}>
|
|
19
|
+
<Frame
|
|
20
|
+
uuid={object.uuid}
|
|
21
|
+
name={object.name}
|
|
22
|
+
pose={object.pose}
|
|
23
|
+
geometry={object.geometry}
|
|
24
|
+
metadata={object.metadata}
|
|
25
|
+
>
|
|
26
|
+
<PortalTarget id={object.name} />
|
|
27
|
+
</Frame>
|
|
28
|
+
</Portal>
|
|
29
|
+
{/each}
|
|
30
|
+
|
|
31
|
+
{#each shapes.nurbs as object (object.uuid)}
|
|
32
|
+
<Portal id={object.referenceFrame}>
|
|
33
|
+
<Frame
|
|
34
|
+
uuid={object.uuid}
|
|
35
|
+
name={object.name}
|
|
36
|
+
pose={object.pose}
|
|
37
|
+
geometry={object.geometry}
|
|
38
|
+
metadata={object.metadata}
|
|
39
|
+
>
|
|
40
|
+
<PortalTarget id={object.name} />
|
|
41
|
+
</Frame>
|
|
42
|
+
</Portal>
|
|
43
|
+
{/each}
|
|
44
|
+
|
|
45
|
+
{#each shapes.models as object (object.uuid)}
|
|
46
|
+
<WorldObject {object}>
|
|
47
|
+
<PortalTarget id={object.name} />
|
|
48
|
+
</WorldObject>
|
|
49
|
+
{/each}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const Shapes: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type Shapes = InstanceType<typeof Shapes>;
|
|
18
|
+
export default Shapes;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { TransformControls } from '@threlte/extras'
|
|
3
|
+
import { useSelected } from '../hooks/useSelection.svelte'
|
|
4
|
+
import { useStaticGeometries } from '../hooks/useStaticGeometries.svelte'
|
|
5
|
+
import { useTransformControls } from '../hooks/useControls.svelte'
|
|
6
|
+
import { Keybindings } from '../keybindings'
|
|
7
|
+
import { PersistedState } from 'runed'
|
|
8
|
+
import { quaternionToPose, scaleToDimensions, vector3ToPose } from '../transform'
|
|
9
|
+
import { Quaternion, Vector3 } from 'three'
|
|
10
|
+
import Frame from './Frame.svelte'
|
|
11
|
+
|
|
12
|
+
type Modes = 'translate' | 'rotate' | 'scale'
|
|
13
|
+
|
|
14
|
+
const transformControls = useTransformControls()
|
|
15
|
+
const geometries = useStaticGeometries()
|
|
16
|
+
const selected = useSelected()
|
|
17
|
+
|
|
18
|
+
let mode = new PersistedState<Modes>('transform-mode', 'translate')
|
|
19
|
+
|
|
20
|
+
const quaternion = new Quaternion()
|
|
21
|
+
const vector3 = new Vector3()
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<svelte:window
|
|
25
|
+
onkeydown={(event) => {
|
|
26
|
+
if (event.metaKey || event.ctrlKey) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const key = event.key.toLowerCase()
|
|
31
|
+
|
|
32
|
+
if (key === Keybindings.ADD_GEOMETRY) {
|
|
33
|
+
geometries.add()
|
|
34
|
+
} else if (key === Keybindings.REMOVE_GEOMETRY) {
|
|
35
|
+
geometries.remove(selected.current ?? '')
|
|
36
|
+
} else if (key === Keybindings.TRANSLATE) {
|
|
37
|
+
mode.current = 'translate'
|
|
38
|
+
} else if (key === Keybindings.ROTATE) {
|
|
39
|
+
mode.current = 'rotate'
|
|
40
|
+
} else if (key === Keybindings.SCALE) {
|
|
41
|
+
mode.current = 'scale'
|
|
42
|
+
}
|
|
43
|
+
}}
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
{#each geometries.current as object (object.uuid)}
|
|
47
|
+
<Frame
|
|
48
|
+
uuid={object.uuid}
|
|
49
|
+
name={object.name}
|
|
50
|
+
pose={object.pose}
|
|
51
|
+
geometry={object.geometry}
|
|
52
|
+
metadata={object.metadata}
|
|
53
|
+
>
|
|
54
|
+
{#snippet children({ ref })}
|
|
55
|
+
{#if selected.current === ref.uuid}
|
|
56
|
+
{#key mode.current}
|
|
57
|
+
<TransformControls
|
|
58
|
+
object={ref}
|
|
59
|
+
mode={mode.current}
|
|
60
|
+
onmouseDown={() => transformControls.setActive(true)}
|
|
61
|
+
onmouseUp={() => {
|
|
62
|
+
transformControls.setActive(false)
|
|
63
|
+
|
|
64
|
+
if (mode.current === 'translate') {
|
|
65
|
+
vector3ToPose(ref.getWorldPosition(vector3), object.pose)
|
|
66
|
+
} else if (mode.current === 'rotate') {
|
|
67
|
+
quaternionToPose(ref.getWorldQuaternion(quaternion), object.pose)
|
|
68
|
+
ref.quaternion.copy(quaternion)
|
|
69
|
+
} else if (mode.current === 'scale' && object.geometry?.case === 'box') {
|
|
70
|
+
scaleToDimensions(ref.scale, object.geometry)
|
|
71
|
+
ref.scale.setScalar(1)
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
{/key}
|
|
76
|
+
{/if}
|
|
77
|
+
{/snippet}
|
|
78
|
+
</Frame>
|
|
79
|
+
{/each}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const StaticGeometries: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type StaticGeometries = InstanceType<typeof StaticGeometries>;
|
|
18
|
+
export default StaticGeometries;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { PersistedState } from 'runed'
|
|
3
|
+
import { Icon, Select } from '@viamrobotics/prime-core'
|
|
4
|
+
import RefreshRate from '../RefreshRate.svelte'
|
|
5
|
+
import { useMotionClient } from '../../hooks/useMotionClient.svelte'
|
|
6
|
+
|
|
7
|
+
const showSettings = new PersistedState('show-settings', false)
|
|
8
|
+
|
|
9
|
+
const motionClient = useMotionClient()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<button
|
|
13
|
+
class="border-medium w-full border-t p-2 text-left"
|
|
14
|
+
onclick={() => (showSettings.current = !showSettings.current)}
|
|
15
|
+
>
|
|
16
|
+
<h3 class="text-default flex items-center gap-1.5">
|
|
17
|
+
<Icon
|
|
18
|
+
name={showSettings.current ? 'unfold-more-horizontal' : 'unfold-less-horizontal'}
|
|
19
|
+
label="unfold more icon"
|
|
20
|
+
variant="ghost"
|
|
21
|
+
cx="size-6"
|
|
22
|
+
on:click={() => (showSettings.current = !showSettings.current)}
|
|
23
|
+
/>
|
|
24
|
+
Settings
|
|
25
|
+
</h3>
|
|
26
|
+
</button>
|
|
27
|
+
|
|
28
|
+
{#if showSettings.current}
|
|
29
|
+
<div class="border-medium flex flex-col gap-2 border-t p-3">
|
|
30
|
+
<RefreshRate name="Frames">
|
|
31
|
+
<option value="0">Do not fetch</option>
|
|
32
|
+
<option value="1">Fetch on reconfigure</option>
|
|
33
|
+
</RefreshRate>
|
|
34
|
+
<RefreshRate name="Pointclouds" />
|
|
35
|
+
<RefreshRate name="Geometries" />
|
|
36
|
+
<RefreshRate name="Poses" />
|
|
37
|
+
|
|
38
|
+
<label class="flex flex-col gap-1">
|
|
39
|
+
Motion client
|
|
40
|
+
<Select
|
|
41
|
+
onchange={(event: InputEvent) => {
|
|
42
|
+
if (event.target instanceof HTMLSelectElement) {
|
|
43
|
+
motionClient.set(event.target.value)
|
|
44
|
+
}
|
|
45
|
+
}}
|
|
46
|
+
value={motionClient.current}
|
|
47
|
+
>
|
|
48
|
+
{#each motionClient.names as name (name)}
|
|
49
|
+
<option>{name}</option>
|
|
50
|
+
{/each}
|
|
51
|
+
</Select>
|
|
52
|
+
</label>
|
|
53
|
+
</div>
|
|
54
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const Settings: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type Settings = InstanceType<typeof Settings>;
|
|
18
|
+
export default Settings;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as tree from '@zag-js/tree-view'
|
|
3
|
+
import { useMachine, normalizeProps } from '@zag-js/svelte'
|
|
4
|
+
import { untrack } from 'svelte'
|
|
5
|
+
import { ChevronRight, Eye, EyeOff } from 'lucide-svelte'
|
|
6
|
+
import { useVisibility } from '../../hooks/useVisibility.svelte'
|
|
7
|
+
import type { TreeNode } from './buildTree'
|
|
8
|
+
import { useExpanded } from './useExpanded.svelte'
|
|
9
|
+
import { VirtualList } from 'svelte-virtuallists'
|
|
10
|
+
import { observe } from '@threlte/core'
|
|
11
|
+
|
|
12
|
+
const visibility = useVisibility()
|
|
13
|
+
const expanded = useExpanded()
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
rootNode: TreeNode
|
|
17
|
+
selections: string[]
|
|
18
|
+
onSelectionChange?: (event: tree.SelectionChangeDetails) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { rootNode, selections, onSelectionChange }: Props = $props()
|
|
22
|
+
|
|
23
|
+
const collection = tree.collection<TreeNode>({
|
|
24
|
+
nodeToValue: (node) => node.id,
|
|
25
|
+
nodeToString: (node) => node.name,
|
|
26
|
+
rootNode,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const service = useMachine(tree.machine, {
|
|
30
|
+
collection,
|
|
31
|
+
onSelectionChange(details) {
|
|
32
|
+
onSelectionChange?.(details)
|
|
33
|
+
},
|
|
34
|
+
onExpandedChange(details) {
|
|
35
|
+
expanded.clear()
|
|
36
|
+
for (const value of details.expandedValue) {
|
|
37
|
+
expanded.add(value)
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const api = $derived(tree.connect(service, normalizeProps))
|
|
43
|
+
|
|
44
|
+
observe(
|
|
45
|
+
() => [selections],
|
|
46
|
+
() => untrack(() => api.setSelectedValue(selections))
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
observe(
|
|
50
|
+
() => [expanded],
|
|
51
|
+
() => untrack(() => api.setExpandedValue([...expanded]))
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const rootChildren = $derived(collection.rootNode.children ?? [])
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
{#snippet treeNode({
|
|
58
|
+
node,
|
|
59
|
+
indexPath,
|
|
60
|
+
api,
|
|
61
|
+
}: {
|
|
62
|
+
node: TreeNode
|
|
63
|
+
indexPath: number[]
|
|
64
|
+
api: tree.Api
|
|
65
|
+
})}
|
|
66
|
+
{@const nodeProps = { indexPath, node }}
|
|
67
|
+
{@const nodeState = api.getNodeState(nodeProps)}
|
|
68
|
+
{@const isVisible = visibility.get(node.id) ?? true}
|
|
69
|
+
{@const { selected } = nodeState}
|
|
70
|
+
|
|
71
|
+
{#if nodeState.isBranch}
|
|
72
|
+
{@const { expanded } = nodeState}
|
|
73
|
+
{@const { children = [] } = node}
|
|
74
|
+
<div
|
|
75
|
+
{...api.getBranchProps(nodeProps)}
|
|
76
|
+
class={{
|
|
77
|
+
'text-disabled': !isVisible,
|
|
78
|
+
'bg-medium': selected,
|
|
79
|
+
sticky: true,
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<div {...api.getBranchControlProps(nodeProps)}>
|
|
83
|
+
<span
|
|
84
|
+
{...api.getBranchIndicatorProps(nodeProps)}
|
|
85
|
+
class={{ 'rotate-90': expanded }}
|
|
86
|
+
>
|
|
87
|
+
<ChevronRight size={14} />
|
|
88
|
+
</span>
|
|
89
|
+
<span
|
|
90
|
+
class="flex items-center"
|
|
91
|
+
{...api.getBranchTextProps(nodeProps)}
|
|
92
|
+
>
|
|
93
|
+
{node.name}
|
|
94
|
+
</span>
|
|
95
|
+
|
|
96
|
+
<button
|
|
97
|
+
class="text-gray-6"
|
|
98
|
+
onclick={(event) => {
|
|
99
|
+
event.stopPropagation()
|
|
100
|
+
visibility.set(node.id, !isVisible)
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
{#if isVisible}
|
|
104
|
+
<Eye size={14} />
|
|
105
|
+
{:else}
|
|
106
|
+
<EyeOff size={14} />
|
|
107
|
+
{/if}
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
110
|
+
<div {...api.getBranchContentProps(nodeProps)}>
|
|
111
|
+
<div {...api.getBranchIndentGuideProps(nodeProps)}></div>
|
|
112
|
+
|
|
113
|
+
{#each children as node, index (node.id)}
|
|
114
|
+
{@render treeNode({ node, indexPath: [...indexPath, index], api })}
|
|
115
|
+
{/each}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
{:else}
|
|
119
|
+
<div
|
|
120
|
+
class={{ 'flex justify-between': true, 'text-disabled': !isVisible, 'bg-medium': selected }}
|
|
121
|
+
{...api.getItemProps(nodeProps)}
|
|
122
|
+
>
|
|
123
|
+
<span class="flex items-center gap-1.5">
|
|
124
|
+
{node.name}
|
|
125
|
+
</span>
|
|
126
|
+
|
|
127
|
+
<button
|
|
128
|
+
class="text-gray-6"
|
|
129
|
+
onclick={(event) => {
|
|
130
|
+
event.stopPropagation()
|
|
131
|
+
visibility.set(node.id, !isVisible)
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
{#if isVisible}
|
|
135
|
+
<Eye size={14} />
|
|
136
|
+
{:else}
|
|
137
|
+
<EyeOff size={14} />
|
|
138
|
+
{/if}
|
|
139
|
+
</button>
|
|
140
|
+
</div>
|
|
141
|
+
{/if}
|
|
142
|
+
{/snippet}
|
|
143
|
+
|
|
144
|
+
<div class="root-node">
|
|
145
|
+
<div {...api.getRootProps() as object}>
|
|
146
|
+
<div class="border-medium border-b p-2">
|
|
147
|
+
<h3 {...api.getLabelProps() as object}>{rootNode.name}</h3>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<div
|
|
151
|
+
{...api.getTreeProps()}
|
|
152
|
+
class="w-[240px]"
|
|
153
|
+
>
|
|
154
|
+
{#if rootChildren.length === 0}
|
|
155
|
+
<p class="text-subtle-2 px-2 py-4">No objects displayed</p>
|
|
156
|
+
{:else}
|
|
157
|
+
<VirtualList
|
|
158
|
+
class="w-full"
|
|
159
|
+
style="height:{Math.min(8, Math.max(rootChildren.length, 5)) * 32}px;"
|
|
160
|
+
items={rootChildren}
|
|
161
|
+
>
|
|
162
|
+
{#snippet vl_slot({ index, item })}
|
|
163
|
+
{@render treeNode({ node: item, indexPath: [Number(index)], api })}
|
|
164
|
+
{/snippet}
|
|
165
|
+
</VirtualList>
|
|
166
|
+
{/if}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<style>
|
|
172
|
+
:global(:root) {
|
|
173
|
+
[data-scope='tree-view'][data-part='item'],
|
|
174
|
+
[data-scope='tree-view'][data-part='branch-control'] {
|
|
175
|
+
user-select: none;
|
|
176
|
+
--padding-inline: 16px;
|
|
177
|
+
padding-inline-start: calc(var(--depth) * var(--padding-inline));
|
|
178
|
+
padding-inline-end: var(--padding-inline);
|
|
179
|
+
display: flex;
|
|
180
|
+
align-items: center;
|
|
181
|
+
gap: 8px;
|
|
182
|
+
min-height: 32px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
[data-scope='tree-view'][data-part='item-text'],
|
|
186
|
+
[data-scope='tree-view'][data-part='branch-text'] {
|
|
187
|
+
flex: 1;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
[data-scope='tree-view'][data-part='branch-content'] {
|
|
191
|
+
position: relative;
|
|
192
|
+
isolation: isolate;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
[data-scope='tree-view'][data-part='branch-indent-guide'] {
|
|
196
|
+
position: absolute;
|
|
197
|
+
content: '';
|
|
198
|
+
border-left: 1px solid #eee;
|
|
199
|
+
height: 100%;
|
|
200
|
+
translate: calc(var(--depth) * 1.25rem);
|
|
201
|
+
z-index: 1;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as tree from '@zag-js/tree-view';
|
|
2
|
+
import type { TreeNode } from './buildTree';
|
|
3
|
+
interface Props {
|
|
4
|
+
rootNode: TreeNode;
|
|
5
|
+
selections: string[];
|
|
6
|
+
onSelectionChange?: (event: tree.SelectionChangeDetails) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const Tree: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Tree = ReturnType<typeof Tree>;
|
|
10
|
+
export default Tree;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { PersistedState } from 'runed'
|
|
3
|
+
import Tree from './Tree.svelte'
|
|
4
|
+
import { fly } from 'svelte/transition'
|
|
5
|
+
import { Keybindings } from '../../keybindings'
|
|
6
|
+
import { ListTree } from 'lucide-svelte'
|
|
7
|
+
import { buildTreeNodes, type TreeNode } from './buildTree'
|
|
8
|
+
import { useSelected } from '../../hooks/useSelection.svelte'
|
|
9
|
+
import { provideTreeExpandedContext } from './useExpanded.svelte'
|
|
10
|
+
import { isEqual } from 'lodash-es'
|
|
11
|
+
import { useObjects } from '../../hooks/useObjects.svelte'
|
|
12
|
+
import Settings from './Settings.svelte'
|
|
13
|
+
|
|
14
|
+
const showTreeview = new PersistedState('show-treeview', true)
|
|
15
|
+
|
|
16
|
+
provideTreeExpandedContext()
|
|
17
|
+
|
|
18
|
+
const selected = useSelected()
|
|
19
|
+
const objects = useObjects()
|
|
20
|
+
|
|
21
|
+
let rootNode = $state<TreeNode>({
|
|
22
|
+
id: 'world',
|
|
23
|
+
name: 'World',
|
|
24
|
+
children: [],
|
|
25
|
+
href: '/',
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const nodes = $derived(buildTreeNodes(objects.current))
|
|
29
|
+
|
|
30
|
+
$effect.pre(() => {
|
|
31
|
+
if (!isEqual(rootNode.children, nodes)) {
|
|
32
|
+
rootNode.children = nodes
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<svelte:window
|
|
38
|
+
onkeydown={({ key }) => {
|
|
39
|
+
if (key === Keybindings.TREEVIEW) {
|
|
40
|
+
showTreeview.current = !showTreeview.current
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<button
|
|
46
|
+
class="fixed top-2 left-2 p-2"
|
|
47
|
+
onclick={() => (showTreeview.current = !showTreeview.current)}
|
|
48
|
+
>
|
|
49
|
+
<ListTree />
|
|
50
|
+
</button>
|
|
51
|
+
|
|
52
|
+
{#if showTreeview.current}
|
|
53
|
+
<div
|
|
54
|
+
class="bg-extralight border-medium fixed top-0 left-0 m-2 overflow-y-auto border text-xs"
|
|
55
|
+
in:fly={{ duration: 250, x: -100 }}
|
|
56
|
+
out:fly={{ duration: 250, x: -100 }}
|
|
57
|
+
>
|
|
58
|
+
{#key rootNode}
|
|
59
|
+
<Tree
|
|
60
|
+
{rootNode}
|
|
61
|
+
selections={selected.current ? [selected.current] : []}
|
|
62
|
+
onSelectionChange={(event) => {
|
|
63
|
+
selected.set(event.selectedValue[0])
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
{/key}
|
|
67
|
+
|
|
68
|
+
<Settings />
|
|
69
|
+
</div>
|
|
70
|
+
{/if}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WorldObject } from '../../WorldObject';
|
|
2
|
+
export interface TreeNode {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
children?: TreeNode[];
|
|
6
|
+
href: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Creates a tree representing parent child / relationships from a set of frames.
|
|
10
|
+
*/
|
|
11
|
+
export declare const buildTreeNodes: (objects: WorldObject[]) => TreeNode[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a tree representing parent child / relationships from a set of frames.
|
|
3
|
+
*/
|
|
4
|
+
export const buildTreeNodes = (objects) => {
|
|
5
|
+
const nodeMap = new Map();
|
|
6
|
+
const rootNodes = [];
|
|
7
|
+
for (const object of objects) {
|
|
8
|
+
const node = {
|
|
9
|
+
name: object.name,
|
|
10
|
+
id: object.uuid,
|
|
11
|
+
children: [],
|
|
12
|
+
href: `/selection/${object.name}`,
|
|
13
|
+
};
|
|
14
|
+
nodeMap.set(object.name, node);
|
|
15
|
+
if (object.referenceFrame === 'world') {
|
|
16
|
+
rootNodes.push(node);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
for (const object of objects) {
|
|
20
|
+
if (object.referenceFrame !== 'world') {
|
|
21
|
+
const parentNode = nodeMap.get(object.referenceFrame);
|
|
22
|
+
const child = nodeMap.get(object.name);
|
|
23
|
+
if (parentNode && child) {
|
|
24
|
+
parentNode.children?.push(child);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return rootNodes;
|
|
29
|
+
};
|