@viamrobotics/motion-tools 1.9.0 → 1.10.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.
Files changed (94) hide show
  1. package/dist/HoverUpdater.svelte.d.ts +19 -0
  2. package/dist/HoverUpdater.svelte.js +120 -0
  3. package/dist/components/App.svelte +34 -35
  4. package/dist/components/CameraControls.svelte +1 -1
  5. package/dist/components/Focus.svelte +1 -1
  6. package/dist/components/Frame.svelte +1 -1
  7. package/dist/components/Geometry2.svelte +8 -5
  8. package/dist/components/Label.svelte +1 -1
  9. package/dist/components/MeasureTool/MeasurePoint.svelte +5 -3
  10. package/dist/components/MeasureTool/MeasureTool.svelte +11 -11
  11. package/dist/components/SceneProviders.svelte +2 -0
  12. package/dist/components/hover/HoveredEntities.svelte +23 -0
  13. package/dist/components/hover/HoveredEntity.svelte +15 -0
  14. package/dist/components/hover/HoveredEntity.svelte.d.ts +3 -0
  15. package/dist/components/hover/HoveredEntityTooltip.svelte +70 -0
  16. package/dist/components/{HoveredEntityTooltip.svelte.d.ts → hover/HoveredEntityTooltip.svelte.d.ts} +2 -2
  17. package/dist/components/hover/LinkedHoveredEntity.svelte +55 -0
  18. package/dist/components/hover/LinkedHoveredEntity.svelte.d.ts +9 -0
  19. package/dist/components/overlay/AddRelationship.svelte +131 -0
  20. package/dist/components/overlay/AddRelationship.svelte.d.ts +7 -0
  21. package/dist/components/{Details.svelte → overlay/Details.svelte} +44 -11
  22. package/dist/components/overlay/FloatingPanel.svelte +78 -0
  23. package/dist/components/overlay/FloatingPanel.svelte.d.ts +13 -0
  24. package/dist/components/{LiveUpdatesBanner.svelte → overlay/LiveUpdatesBanner.svelte} +2 -2
  25. package/dist/components/{RefreshRate.svelte → overlay/RefreshRate.svelte} +1 -1
  26. package/dist/components/overlay/ToggleGroup.svelte +56 -0
  27. package/dist/components/{Overlay → overlay}/ToggleGroup.svelte.d.ts +6 -7
  28. package/dist/components/{__tests__ → overlay/__tests__}/__fixtures__/entity.js +1 -1
  29. package/dist/components/{dashboard → overlay/dashboard}/Button.svelte +1 -1
  30. package/dist/components/{dashboard → overlay/dashboard}/Dashboard.svelte +2 -2
  31. package/dist/components/{Tree → overlay/left-pane}/AddFrames.svelte +2 -2
  32. package/dist/components/{Tree → overlay/left-pane}/Logs.svelte +1 -1
  33. package/dist/components/{Tree → overlay/left-pane}/Tree.svelte +3 -3
  34. package/dist/components/{Tree → overlay/left-pane}/TreeContainer.svelte +8 -10
  35. package/dist/components/{Tree → overlay/left-pane}/Widgets.svelte +3 -3
  36. package/dist/components/{Tree → overlay/left-pane}/buildTree.js +1 -1
  37. package/dist/components/overlay/settings/Settings.svelte +279 -0
  38. package/dist/components/overlay/settings/Tabs.svelte +54 -0
  39. package/dist/components/overlay/settings/Tabs.svelte.d.ts +12 -0
  40. package/dist/components/{widgets → overlay/widgets}/ArmPositions.svelte +4 -4
  41. package/dist/components/{widgets → overlay/widgets}/Camera.svelte +5 -5
  42. package/dist/ecs/index.d.ts +1 -0
  43. package/dist/ecs/index.js +1 -0
  44. package/dist/ecs/relations.d.ts +7 -0
  45. package/dist/ecs/relations.js +7 -0
  46. package/dist/ecs/traits.d.ts +8 -2
  47. package/dist/ecs/traits.js +11 -5
  48. package/dist/hooks/useDrawAPI.svelte.js +1 -1
  49. package/dist/hooks/useFrames.svelte.js +1 -0
  50. package/dist/hooks/useGeometries.svelte.js +1 -1
  51. package/dist/hooks/useLinked.svelte.d.ts +7 -0
  52. package/dist/hooks/useLinked.svelte.js +35 -0
  53. package/dist/hooks/useObjectEvents.svelte.js +35 -16
  54. package/dist/hooks/usePointcloudObjects.svelte.js +1 -1
  55. package/dist/hooks/usePointclouds.svelte.js +30 -43
  56. package/dist/hooks/usePose.svelte.js +1 -1
  57. package/dist/hooks/useWorldState.svelte.js +1 -1
  58. package/package.json +4 -1
  59. package/dist/components/DotSprite.svelte +0 -59
  60. package/dist/components/DotSprite.svelte.d.ts +0 -10
  61. package/dist/components/HoveredEntities.svelte +0 -19
  62. package/dist/components/HoveredEntityTooltip.svelte +0 -241
  63. package/dist/components/MeasureTool.svelte +0 -123
  64. package/dist/components/MeasureTool.svelte.d.ts +0 -3
  65. package/dist/components/Overlay/ToggleGroup.svelte +0 -60
  66. package/dist/components/Tree/Settings.svelte +0 -221
  67. package/dist/components/null-states/Connection.svelte +0 -0
  68. package/dist/components/null-states/Connection.svelte.d.ts +0 -26
  69. /package/dist/components/{HoveredEntities.svelte.d.ts → hover/HoveredEntities.svelte.d.ts} +0 -0
  70. /package/dist/components/{Details.svelte.d.ts → overlay/Details.svelte.d.ts} +0 -0
  71. /package/dist/components/{LiveUpdatesBanner.svelte.d.ts → overlay/LiveUpdatesBanner.svelte.d.ts} +0 -0
  72. /package/dist/components/{Overlay → overlay}/Popover.svelte +0 -0
  73. /package/dist/components/{Overlay → overlay}/Popover.svelte.d.ts +0 -0
  74. /package/dist/components/{RefreshRate.svelte.d.ts → overlay/RefreshRate.svelte.d.ts} +0 -0
  75. /package/dist/components/{shared → overlay}/Table.svelte +0 -0
  76. /package/dist/components/{shared → overlay}/Table.svelte.d.ts +0 -0
  77. /package/dist/components/{__tests__ → overlay/__tests__}/__fixtures__/entity.d.ts +0 -0
  78. /package/dist/components/{__tests__ → overlay/__tests__}/__fixtures__/resource.d.ts +0 -0
  79. /package/dist/components/{__tests__ → overlay/__tests__}/__fixtures__/resource.js +0 -0
  80. /package/dist/components/{dashboard → overlay/dashboard}/Button.svelte.d.ts +0 -0
  81. /package/dist/components/{dashboard → overlay/dashboard}/Dashboard.svelte.d.ts +0 -0
  82. /package/dist/components/{Tree → overlay/left-pane}/AddFrames.svelte.d.ts +0 -0
  83. /package/dist/components/{Tree → overlay/left-pane}/Drawer.svelte +0 -0
  84. /package/dist/components/{Tree → overlay/left-pane}/Drawer.svelte.d.ts +0 -0
  85. /package/dist/components/{Tree → overlay/left-pane}/Logs.svelte.d.ts +0 -0
  86. /package/dist/components/{Tree → overlay/left-pane}/Tree.svelte.d.ts +0 -0
  87. /package/dist/components/{Tree → overlay/left-pane}/TreeContainer.svelte.d.ts +0 -0
  88. /package/dist/components/{Tree → overlay/left-pane}/Widgets.svelte.d.ts +0 -0
  89. /package/dist/components/{Tree → overlay/left-pane}/buildTree.d.ts +0 -0
  90. /package/dist/components/{Tree → overlay/left-pane}/useExpanded.svelte.d.ts +0 -0
  91. /package/dist/components/{Tree → overlay/left-pane}/useExpanded.svelte.js +0 -0
  92. /package/dist/components/{Tree → overlay/settings}/Settings.svelte.d.ts +0 -0
  93. /package/dist/components/{widgets → overlay/widgets}/ArmPositions.svelte.d.ts +0 -0
  94. /package/dist/components/{widgets → overlay/widgets}/Camera.svelte.d.ts +0 -0
@@ -0,0 +1,279 @@
1
+ <script lang="ts">
2
+ import { Switch, Input } from '@viamrobotics/prime-core'
3
+ import { Portal } from '@threlte/extras'
4
+ import RefreshRate from '../RefreshRate.svelte'
5
+ import { useSettings } from '../../../hooks/useSettings.svelte'
6
+ import { useResourceNames } from '@viamrobotics/svelte-sdk'
7
+ import { usePartID } from '../../../hooks/usePartID.svelte'
8
+ import { RefreshRates, useMachineSettings } from '../../../hooks/useMachineSettings.svelte'
9
+ import { useGeometries } from '../../../hooks/useGeometries.svelte'
10
+ import { usePointClouds } from '../../../hooks/usePointclouds.svelte'
11
+ import { useThrelte } from '@threlte/core'
12
+ import { useRefetchPoses } from '../../../hooks/useRefetchPoses'
13
+ import FloatingPanel from '../FloatingPanel.svelte'
14
+ import DashboardButton from '../dashboard/Button.svelte'
15
+ import Tabs from './Tabs.svelte'
16
+ import { PersistedState } from 'runed'
17
+ import ToggleGroup from '../ToggleGroup.svelte'
18
+
19
+ const { invalidate } = useThrelte()
20
+ const partID = usePartID()
21
+ const cameras = useResourceNames(() => partID.current, 'camera')
22
+ const visionServices = useResourceNames(() => partID.current, 'vision')
23
+ const settings = useSettings()
24
+ const { disabledCameras, disabledVisionServices } = useMachineSettings()
25
+ const geometries = useGeometries()
26
+ const pointclouds = usePointClouds()
27
+ const { refetchPoses } = useRefetchPoses()
28
+
29
+ // Invalidate the renderer for any settings change
30
+ $effect(() => {
31
+ for (const key in settings.current) {
32
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
33
+ settings.current[key as keyof typeof settings.current]
34
+ }
35
+
36
+ invalidate()
37
+ })
38
+
39
+ const isOpen = new PersistedState('settings-is-open', false)
40
+ const activeTab = new PersistedState('settings-active-tab', 'Connection')
41
+ </script>
42
+
43
+ <Portal id="dashboard">
44
+ <fieldset>
45
+ <DashboardButton
46
+ active
47
+ icon="cog"
48
+ description="Settings"
49
+ onclick={() => {
50
+ isOpen.current = !isOpen.current
51
+ }}
52
+ />
53
+ </fieldset>
54
+ </Portal>
55
+
56
+ {#snippet SectionTitle(title: string)}
57
+ <h3 class="border-gray-3 border-b py-1 text-sm"><strong>{title}</strong></h3>
58
+ {/snippet}
59
+
60
+ {#snippet Connection()}
61
+ <div class="flex flex-col gap-2.5 text-xs">
62
+ {@render SectionTitle('Polling rates')}
63
+
64
+ <RefreshRate
65
+ id={RefreshRates.poses}
66
+ label="Poses"
67
+ allowLive
68
+ onManualRefetch={() => {
69
+ refetchPoses()
70
+ geometries.refetch()
71
+ }}
72
+ />
73
+ <RefreshRate
74
+ id={RefreshRates.pointclouds}
75
+ label="Pointclouds"
76
+ onManualRefetch={() => {
77
+ pointclouds.refetch()
78
+ }}
79
+ />
80
+ </div>
81
+ {/snippet}
82
+
83
+ {#snippet Pointclouds()}
84
+ <div class="flex flex-col gap-1 text-xs">
85
+ <label class="flex items-center justify-between gap-2">
86
+ Default point size
87
+
88
+ <div class="w-20">
89
+ <Input
90
+ bind:value={settings.current.pointSize}
91
+ on:keydown={(event) => event.stopImmediatePropagation()}
92
+ />
93
+ </div>
94
+ </label>
95
+
96
+ <label class="flex items-center justify-between gap-2">
97
+ Default point color
98
+
99
+ <div class="w-20">
100
+ <Input
101
+ type="color"
102
+ bind:value={settings.current.pointColor}
103
+ on:keydown={(event) => event.stopImmediatePropagation()}
104
+ />
105
+ </div>
106
+ </label>
107
+
108
+ {@render SectionTitle('Enabled cameras')}
109
+
110
+ {#each cameras.current as camera (camera)}
111
+ <div class="flex items-center justify-between py-0.5 text-xs">
112
+ {camera.name}
113
+ <Switch
114
+ on={disabledCameras.get(camera.name) !== true}
115
+ on:change={(event) => {
116
+ disabledCameras.set(camera.name, !event.detail)
117
+ }}
118
+ />
119
+ </div>
120
+ {:else}
121
+ No cameras detected
122
+ {/each}
123
+ </div>
124
+ {/snippet}
125
+
126
+ {#snippet Vision()}
127
+ <div class="text-gray-9 flex flex-col gap-1 text-xs">
128
+ {@render SectionTitle('Enabled vision services')}
129
+
130
+ {#each visionServices.current as visionService (visionService)}
131
+ <div class="flex items-center justify-between py-0.5">
132
+ {visionService.name}
133
+ <Switch
134
+ on={disabledVisionServices.get(visionService.name) !== true}
135
+ on:change={(event) => {
136
+ disabledVisionServices.set(visionService.name, !event.detail)
137
+ }}
138
+ />
139
+ </div>
140
+ {:else}
141
+ No vision services detected
142
+ {/each}
143
+ </div>
144
+ {/snippet}
145
+
146
+ {#snippet Scene()}
147
+ <div class="text-gray-9 flex flex-col gap-1 text-xs">
148
+ <label class="flex items-center justify-between gap-2 py-1">
149
+ Arm Models
150
+
151
+ <ToggleGroup
152
+ multiple
153
+ options={[
154
+ {
155
+ label: 'Colliders',
156
+ value: 'colliders',
157
+ selected: settings.current.renderArmModels.includes('colliders'),
158
+ },
159
+ {
160
+ label: 'Model',
161
+ value: 'model',
162
+ selected: settings.current.renderArmModels.includes('model'),
163
+ },
164
+ ]}
165
+ onSelect={(value) => {
166
+ settings.current.renderArmModels = (value.join('+') || 'colliders') as
167
+ | 'colliders'
168
+ | 'model'
169
+ | 'colliders+model'
170
+
171
+ console.log(settings.current.renderArmModels)
172
+ }}
173
+ />
174
+ </label>
175
+
176
+ <label class="flex items-center justify-between gap-2">
177
+ Single item hover details <Switch bind:on={settings.current.renderSubEntityHoverDetail} />
178
+ </label>
179
+
180
+ <label class="flex items-center justify-between gap-2">
181
+ Object labels <Switch bind:on={settings.current.enableLabels} />
182
+ </label>
183
+
184
+ {@render SectionTitle('Grid')}
185
+
186
+ <label class="flex items-center justify-between gap-2 py-1">
187
+ Visible <Switch bind:on={settings.current.grid} />
188
+ </label>
189
+
190
+ <label class="flex items-center justify-between gap-2">
191
+ Cell size (m)
192
+
193
+ <div class="w-20">
194
+ <Input
195
+ bind:value={settings.current.gridCellSize}
196
+ on:keydown={(event) => event.stopImmediatePropagation()}
197
+ />
198
+ </div>
199
+ </label>
200
+
201
+ <label class="flex items-center justify-between gap-2">
202
+ Section size (m)
203
+
204
+ <div class="w-20">
205
+ <Input
206
+ bind:value={settings.current.gridSectionSize}
207
+ on:keydown={(event) => event.stopImmediatePropagation()}
208
+ />
209
+ </div>
210
+ </label>
211
+
212
+ <label class="flex items-center justify-between gap-2">
213
+ Fade distance (m)
214
+
215
+ <div class="w-20">
216
+ <Input
217
+ bind:value={settings.current.gridFadeDistance}
218
+ on:keydown={(event) => event.stopImmediatePropagation()}
219
+ />
220
+ </div>
221
+ </label>
222
+
223
+ {@render SectionTitle('Lines')}
224
+
225
+ <label class="flex items-center justify-between gap-2">
226
+ Thickness
227
+
228
+ <div class="w-20">
229
+ <Input
230
+ bind:value={settings.current.lineWidth}
231
+ on:keydown={(event) => event.stopImmediatePropagation()}
232
+ />
233
+ </div>
234
+ </label>
235
+
236
+ <label class="flex items-center justify-between gap-2">
237
+ Dot size
238
+
239
+ <div class="w-20">
240
+ <Input
241
+ bind:value={settings.current.lineDotSize}
242
+ on:keydown={(event) => event.stopImmediatePropagation()}
243
+ />
244
+ </div>
245
+ </label>
246
+ </div>
247
+ {/snippet}
248
+
249
+ {#snippet Stats()}
250
+ <div class="flex w-full flex-col gap-2.5 text-xs">
251
+ <label class="flex items-center justify-between gap-2">
252
+ Query devtools <Switch bind:on={settings.current.enableQueryDevtools} />
253
+ </label>
254
+
255
+ <label class="flex items-center justify-between gap-2">
256
+ Render stats <Switch bind:on={settings.current.renderStats} />
257
+ </label>
258
+ </div>
259
+ {/snippet}
260
+
261
+ <FloatingPanel
262
+ title="Settings"
263
+ bind:isOpen={isOpen.current}
264
+ defaultSize={{ width: 460, height: 500 }}
265
+ >
266
+ <Tabs
267
+ defaultTab={activeTab.current}
268
+ items={[
269
+ { label: 'Connection', content: Connection },
270
+ { label: 'Scene', content: Scene },
271
+ { label: 'Pointclouds', content: Pointclouds },
272
+ { label: 'Vision', content: Vision },
273
+ { label: 'Stats', content: Stats },
274
+ ]}
275
+ onValueChange={(value) => {
276
+ activeTab.current = value
277
+ }}
278
+ />
279
+ </FloatingPanel>
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+ import * as tabs from '@zag-js/tabs'
4
+ import { useMachine, normalizeProps } from '@zag-js/svelte'
5
+
6
+ interface Props {
7
+ defaultTab?: string
8
+ onValueChange: (value: string) => void
9
+ items: {
10
+ label: string
11
+ content: Snippet
12
+ }[]
13
+ }
14
+
15
+ let { defaultTab, items, onValueChange }: Props = $props()
16
+
17
+ const id = $props.id()
18
+ const service = useMachine(tabs.machine, () => ({
19
+ id,
20
+ defaultValue: defaultTab,
21
+ onValueChange: (details) => onValueChange(details.value),
22
+ }))
23
+
24
+ const api = $derived(tabs.connect(service, normalizeProps))
25
+ </script>
26
+
27
+ <div
28
+ {...api.getRootProps()}
29
+ class="flex h-full gap-2 overflow-hidden"
30
+ >
31
+ <div
32
+ {...api.getListProps()}
33
+ class="bg-gray-1 flex h-full flex-col items-start p-2 text-sm"
34
+ >
35
+ {#each items as item (item.label)}
36
+ <button
37
+ {...api.getTriggerProps({ value: item.label })}
38
+ class="text-gray-8 w-full py-1 pr-8 pl-3 text-left"
39
+ class:bg-gray-2={api.focusedValue === item.label}
40
+ >
41
+ {item.label}
42
+ </button>
43
+ {/each}
44
+ </div>
45
+
46
+ {#each items as item (item.label)}
47
+ <div
48
+ {...api.getContentProps({ value: item.label })}
49
+ class="h-full w-full overflow-y-auto p-4"
50
+ >
51
+ {@render item.content()}
52
+ </div>
53
+ {/each}
54
+ </div>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ defaultTab?: string;
4
+ onValueChange: (value: string) => void;
5
+ items: {
6
+ label: string;
7
+ content: Snippet;
8
+ }[];
9
+ }
10
+ declare const Tabs: import("svelte").Component<Props, {}, "">;
11
+ type Tabs = ReturnType<typeof Tabs>;
12
+ export default Tabs;
@@ -1,8 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { draggable } from '@neodrag/svelte'
3
- import { formatNumeric } from '../../format'
4
- import Table from '../shared/Table.svelte'
5
- import { useArmClient } from '../../hooks/useArmClient.svelte'
3
+ import { formatNumeric } from '../../../format'
4
+ import Table from '../Table.svelte'
5
+ import { useArmClient } from '../../../hooks/useArmClient.svelte'
6
6
  import { Icon, Label, Select } from '@viamrobotics/prime-core'
7
7
 
8
8
  const { ...rest } = $props()
@@ -17,7 +17,7 @@
17
17
  </script>
18
18
 
19
19
  <div
20
- class="bg-extralight border-medium absolute top-0 left-0 z-1000 m-2 overflow-y-auto border text-xs"
20
+ class="bg-extralight border-medium absolute top-0 left-0 z-4 m-2 overflow-y-auto border text-xs"
21
21
  use:draggable={{
22
22
  bounds: 'body',
23
23
  handle: dragElement,
@@ -3,9 +3,9 @@
3
3
  import { Icon, Select } from '@viamrobotics/prime-core'
4
4
  import { CameraStream, useRobotClient } from '@viamrobotics/svelte-sdk'
5
5
  import { StreamClient } from '@viamrobotics/sdk'
6
- import { useSettings } from '../../hooks/useSettings.svelte'
7
- import { usePartID } from '../../hooks/usePartID.svelte'
8
- import { useEnvironment } from '../../hooks/useEnvironment.svelte'
6
+ import { useSettings } from '../../../hooks/useSettings.svelte'
7
+ import { usePartID } from '../../../hooks/usePartID.svelte'
8
+ import { useEnvironment } from '../../../hooks/useEnvironment.svelte'
9
9
 
10
10
  interface Resolution {
11
11
  width: number
@@ -107,7 +107,7 @@
107
107
  </script>
108
108
 
109
109
  <div
110
- class="bg-extralight border-medium absolute top-0 left-0 z-1000 m-2 flex resize-x flex-col overflow-hidden border text-xs"
110
+ class="bg-extralight border-medium absolute top-0 left-0 z-4 m-2 flex resize-x flex-col overflow-hidden border text-xs"
111
111
  style:width="320px"
112
112
  style:height="auto !important"
113
113
  use:draggable={{
@@ -176,7 +176,7 @@
176
176
  <!-- FPS Pill -->
177
177
  {#if fps > 0}
178
178
  <div
179
- class="absolute bottom-2 left-2 z-10 rounded-[3px] bg-black/30 px-1 py-0.5 text-right font-mono text-xs text-white"
179
+ class="absolute bottom-2 left-2 z-4 rounded-[3px] bg-black/30 px-1 py-0.5 text-right font-mono text-xs text-white"
180
180
  >
181
181
  {fps.toFixed(1)}fps
182
182
  </div>
@@ -2,3 +2,4 @@ export { provideWorld, useWorld } from './useWorld';
2
2
  export { useQuery } from './useQuery.svelte';
3
3
  export { useTrait } from './useTrait.svelte';
4
4
  export * as traits from './traits';
5
+ export * as relations from './relations';
package/dist/ecs/index.js CHANGED
@@ -2,3 +2,4 @@ export { provideWorld, useWorld } from './useWorld';
2
2
  export { useQuery } from './useQuery.svelte';
3
3
  export { useTrait } from './useTrait.svelte';
4
4
  export * as traits from './traits';
5
+ export * as relations from './relations';
@@ -0,0 +1,7 @@
1
+ export declare const SubEntityLinkType: {
2
+ readonly HoverLink: "HoverLink";
3
+ };
4
+ export declare const SubEntityLink: import("koota").Relation<import("koota").Trait<{
5
+ indexMapping: () => string;
6
+ type: string;
7
+ }>>;
@@ -0,0 +1,7 @@
1
+ import { relation } from 'koota';
2
+ export const SubEntityLinkType = {
3
+ HoverLink: 'HoverLink',
4
+ };
5
+ export const SubEntityLink = relation({
6
+ store: { indexMapping: () => 'index', type: '' },
7
+ });
@@ -30,12 +30,17 @@ export declare const Center: import("koota").Trait<{
30
30
  oZ: number;
31
31
  theta: number;
32
32
  }>;
33
- export declare const Hover: import("koota").Trait<{
34
- index: number;
33
+ export declare const InstancedPose: import("koota").Trait<{
35
34
  x: number;
36
35
  y: number;
37
36
  z: number;
37
+ oX: number;
38
+ oY: number;
39
+ oZ: number;
40
+ theta: number;
41
+ index: number;
38
42
  }>;
43
+ export declare const Hovered: import("koota").Trait<() => boolean>;
39
44
  /**
40
45
  * Represents that an entity is composed of many instances, so that the treeview and
41
46
  * details panel may display all instances
@@ -123,6 +128,7 @@ export declare const SnapshotAPI: import("koota").Trait<() => boolean>;
123
128
  * Marker trait for entities created from user-dropped files (PLY, PCD, etc.)
124
129
  */
125
130
  export declare const DroppedFile: import("koota").Trait<() => boolean>;
131
+ export declare const ShowAxesHelper: import("koota").Trait<() => boolean>;
126
132
  /**
127
133
  * Point size, in mm
128
134
  */
@@ -8,12 +8,17 @@ export const Parent = trait(() => 'world');
8
8
  export const Pose = trait({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 });
9
9
  export const EditedPose = trait({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 });
10
10
  export const Center = trait({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 });
11
- export const Hover = trait({
12
- index: -1, // Sub-entity index, -1 if not applicable
13
- x: 0, // World position X in meters
14
- y: 0, // World position Y in meters
15
- z: 0, // World position Z in meters
11
+ export const InstancedPose = trait({
12
+ x: 0,
13
+ y: 0,
14
+ z: 0,
15
+ oX: 0,
16
+ oY: 0,
17
+ oZ: 1,
18
+ theta: 0,
19
+ index: -1,
16
20
  });
21
+ export const Hovered = trait(() => true);
17
22
  /**
18
23
  * Represents that an entity is composed of many instances, so that the treeview and
19
24
  * details panel may display all instances
@@ -74,6 +79,7 @@ export const SnapshotAPI = trait(() => true);
74
79
  * Marker trait for entities created from user-dropped files (PLY, PCD, etc.)
75
80
  */
76
81
  export const DroppedFile = trait(() => true);
82
+ export const ShowAxesHelper = trait(() => true);
77
83
  // === Shape Properties ===
78
84
  /**
79
85
  * Point size, in mm
@@ -149,7 +149,7 @@ export const provideDrawAPI = () => {
149
149
  if (frame.geometry) {
150
150
  entityTraits.push(geometryTrait());
151
151
  }
152
- entityTraits.push(traits.Name(name), traits.Pose(pose), traits.DrawAPI, traits.ReferenceFrame, traits.Removable);
152
+ entityTraits.push(traits.Name(name), traits.Pose(pose), traits.DrawAPI, traits.ReferenceFrame, traits.Removable, traits.ShowAxesHelper);
153
153
  const entity = world.spawn(...entityTraits);
154
154
  entities.set(name, entity);
155
155
  }
@@ -199,6 +199,7 @@ export const provideFrames = (partID) => {
199
199
  traits.Pose(pose),
200
200
  traits.EditedPose(pose),
201
201
  traits.FramesAPI,
202
+ traits.ShowAxesHelper,
202
203
  ];
203
204
  if (parent && parent !== 'world') {
204
205
  entityTraits.push(traits.Parent(parent));
@@ -9,7 +9,7 @@ import { useResourceByName } from './useResourceByName.svelte';
9
9
  import { traits, useWorld } from '../ecs';
10
10
  import {} from 'koota';
11
11
  import { createPose } from '../transform';
12
- import { RefetchRates } from '../components/RefreshRate.svelte';
12
+ import { RefetchRates } from '../components/overlay/RefreshRate.svelte';
13
13
  import { useEnvironment } from './useEnvironment.svelte';
14
14
  const key = Symbol('geometries-context');
15
15
  const colorUtil = new Color();
@@ -0,0 +1,7 @@
1
+ import type { Entity } from 'koota';
2
+ interface LinkedEntitiesContext {
3
+ readonly current: Entity[];
4
+ }
5
+ export declare const provideLinkedEntities: () => void;
6
+ export declare const useLinkedEntities: () => LinkedEntitiesContext;
7
+ export {};
@@ -0,0 +1,35 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ import { relations, useWorld } from '../ecs';
3
+ import { useFocusedEntity, useSelectedEntity } from './useSelection.svelte';
4
+ const linkedKey = Symbol('linked-context');
5
+ export const provideLinkedEntities = () => {
6
+ const world = useWorld();
7
+ const selectedEntity = useSelectedEntity();
8
+ const focusedEntity = useFocusedEntity();
9
+ const displayEntity = $derived(selectedEntity.current ?? focusedEntity.current);
10
+ let linkedEntities = $derived(displayEntity?.targetsFor(relations.SubEntityLink) ?? []);
11
+ const unsubAdd = world.onAdd(relations.SubEntityLink, (entity, target) => {
12
+ if (entity === displayEntity) {
13
+ linkedEntities = [...linkedEntities, target];
14
+ }
15
+ });
16
+ const unsubRemove = world.onRemove(relations.SubEntityLink, (entity, target) => {
17
+ if (entity === displayEntity) {
18
+ linkedEntities = linkedEntities.filter((e) => e !== target);
19
+ }
20
+ });
21
+ $effect(() => {
22
+ return () => {
23
+ unsubAdd();
24
+ unsubRemove();
25
+ };
26
+ });
27
+ setContext(linkedKey, {
28
+ get current() {
29
+ return linkedEntities;
30
+ },
31
+ });
32
+ };
33
+ export const useLinkedEntities = () => {
34
+ return getContext(linkedKey);
35
+ };
@@ -3,6 +3,7 @@ import { useFocusedEntity, useSelectedEntity } from './useSelection.svelte';
3
3
  import { useVisibility } from './useVisibility.svelte';
4
4
  import { Vector2 } from 'three';
5
5
  import { traits } from '../ecs';
6
+ import { updateHoverInfo } from '../HoverUpdater.svelte';
6
7
  export const useObjectEvents = (entity) => {
7
8
  const down = new Vector2();
8
9
  const selectedEntity = useSelectedEntity();
@@ -14,31 +15,49 @@ export const useObjectEvents = (entity) => {
14
15
  const onpointerenter = (event) => {
15
16
  event.stopPropagation();
16
17
  cursor.onPointerEnter();
17
- if (currentEntity && !currentEntity.has(traits.Hover)) {
18
- currentEntity.add(traits.Hover({
19
- index: -1,
20
- x: event.point.x,
21
- y: event.point.y,
22
- z: event.point.z,
23
- }));
18
+ if (currentEntity && !currentEntity.has(traits.Hovered)) {
19
+ const hoverInfo = updateHoverInfo(currentEntity, event);
20
+ if (hoverInfo) {
21
+ currentEntity.add(traits.InstancedPose({
22
+ index: hoverInfo.index,
23
+ x: hoverInfo.x,
24
+ y: hoverInfo.y,
25
+ z: hoverInfo.z,
26
+ oX: hoverInfo.oX,
27
+ oY: hoverInfo.oY,
28
+ oZ: hoverInfo.oZ,
29
+ theta: hoverInfo.theta,
30
+ }));
31
+ }
32
+ currentEntity.add(traits.Hovered);
24
33
  }
25
34
  };
26
35
  const onpointermove = (event) => {
27
36
  event.stopPropagation();
28
- if (currentEntity && currentEntity.has(traits.Hover)) {
29
- currentEntity.set(traits.Hover, {
30
- index: event.index ?? -1,
31
- x: event.point.x,
32
- y: event.point.y,
33
- z: event.point.z,
34
- });
37
+ if (currentEntity && currentEntity.has(traits.Hovered)) {
38
+ const hoverInfo = updateHoverInfo(currentEntity, event);
39
+ if (hoverInfo) {
40
+ currentEntity.set(traits.InstancedPose, {
41
+ index: hoverInfo.index,
42
+ x: hoverInfo.x,
43
+ y: hoverInfo.y,
44
+ z: hoverInfo.z,
45
+ oX: hoverInfo.oX,
46
+ oY: hoverInfo.oY,
47
+ oZ: hoverInfo.oZ,
48
+ theta: hoverInfo.theta,
49
+ });
50
+ }
35
51
  }
36
52
  };
37
53
  const onpointerleave = (event) => {
38
54
  event.stopPropagation();
39
55
  cursor.onPointerLeave();
40
- if (currentEntity?.has(traits.Hover)) {
41
- currentEntity.remove(traits.Hover);
56
+ if (currentEntity?.has(traits.Hovered)) {
57
+ currentEntity.remove(traits.Hovered);
58
+ }
59
+ if (currentEntity?.has(traits.InstancedPose)) {
60
+ currentEntity.remove(traits.InstancedPose);
42
61
  }
43
62
  };
44
63
  const ondblclick = (event) => {
@@ -7,7 +7,7 @@ import { getContext, setContext } from 'svelte';
7
7
  import { traits, useWorld } from '../ecs';
8
8
  import { createBufferGeometry, updateBufferGeometry } from '../attribute';
9
9
  import { useEnvironment } from './useEnvironment.svelte';
10
- import { RefetchRates } from '../components/RefreshRate.svelte';
10
+ import { RefetchRates } from '../components/overlay/RefreshRate.svelte';
11
11
  import { createPose } from '../transform';
12
12
  const key = Symbol('pointcloud-object-context');
13
13
  export const providePointcloudObjects = (partID) => {