@viamrobotics/motion-tools 1.29.0 → 1.30.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/dist/components/App.svelte +4 -10
- package/dist/components/App.svelte.d.ts +2 -1
- package/dist/components/Entities/Arrows/Arrows.svelte +1 -1
- package/dist/components/Entities/Frame.svelte +1 -1
- package/dist/components/Entities/GLTF.svelte +1 -1
- package/dist/components/Entities/Geometry.svelte +1 -1
- package/dist/components/Entities/Line.svelte +1 -1
- package/dist/components/Entities/Points.svelte +1 -1
- package/dist/components/Entities/hooks/useEntityEvents.svelte.js +1 -1
- package/dist/components/SceneProviders.svelte +2 -0
- package/dist/components/SelectedTransformControls.svelte +9 -1
- package/dist/components/overlay/AddFrames.svelte +1 -1
- package/dist/components/overlay/Details.svelte +17 -1
- package/dist/components/overlay/Logs.svelte +1 -1
- package/dist/components/overlay/dashboard/Button.svelte +1 -1
- package/dist/components/overlay/left-pane/TreeNode.svelte +3 -2
- package/dist/components/overlay/settings/Settings.svelte +1 -5
- package/dist/ecs/traits.d.ts +7 -0
- package/dist/ecs/traits.js +7 -0
- package/dist/hooks/useConfigFrames.svelte.js +3 -3
- package/dist/hooks/useFramelessComponents.svelte.js +1 -1
- package/dist/hooks/useInheritedInvisible.svelte.d.ts +3 -0
- package/dist/hooks/useInheritedInvisible.svelte.js +89 -0
- package/dist/hooks/usePartConfig.svelte.d.ts +6 -2
- package/dist/hooks/usePartConfig.svelte.js +23 -11
- package/dist/hooks/useSettings.svelte.d.ts +0 -1
- package/dist/hooks/useSettings.svelte.js +0 -1
- package/dist/plugins/Debug/Debug.svelte +5 -0
- package/dist/plugins/Debug/Debug.svelte.d.ts +18 -0
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +2 -0
- package/package.json +8 -2
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import type { Entity } from 'koota'
|
|
4
4
|
import type { Snippet } from 'svelte'
|
|
5
5
|
|
|
6
|
-
import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools'
|
|
7
6
|
import { Canvas } from '@threlte/core'
|
|
8
7
|
import { PortalTarget } from '@threlte/extras'
|
|
9
8
|
import { useXR } from '@threlte/xr'
|
|
@@ -11,6 +10,8 @@
|
|
|
11
10
|
import { primeTheme } from '@viamrobotics/tweakpane-config'
|
|
12
11
|
import { ThemeUtils } from 'svelte-tweakpane-ui'
|
|
13
12
|
|
|
13
|
+
import type { FragmentInfo } from '../hooks/usePartConfig.svelte'
|
|
14
|
+
|
|
14
15
|
import Controls from './overlay/controls/Controls.svelte'
|
|
15
16
|
import Dashboard from './overlay/dashboard/Dashboard.svelte'
|
|
16
17
|
import Details from './overlay/Details.svelte'
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
interface LocalConfigProps {
|
|
41
42
|
current: Struct
|
|
42
43
|
isDirty: boolean
|
|
43
|
-
|
|
44
|
+
componentNameToFragmentInfo: Record<string, FragmentInfo>
|
|
44
45
|
setLocalPartConfig: (config: Struct) => void
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -111,18 +112,11 @@
|
|
|
111
112
|
})
|
|
112
113
|
</script>
|
|
113
114
|
|
|
114
|
-
{#if settings.current.enableQueryDevtools}
|
|
115
|
-
<SvelteQueryDevtools initialIsOpen />
|
|
116
|
-
{/if}
|
|
117
|
-
|
|
118
115
|
<div
|
|
119
116
|
class="relative h-full w-full overflow-hidden dark:bg-white"
|
|
120
117
|
bind:this={root}
|
|
121
118
|
>
|
|
122
|
-
<Canvas
|
|
123
|
-
renderMode="on-demand"
|
|
124
|
-
dpr={[1, 2]}
|
|
125
|
-
>
|
|
119
|
+
<Canvas renderMode="on-demand">
|
|
126
120
|
<SceneProviders>
|
|
127
121
|
{#snippet children({ focus })}
|
|
128
122
|
<Scene>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Struct } from '@viamrobotics/sdk';
|
|
2
2
|
import type { Entity } from 'koota';
|
|
3
3
|
import type { Snippet } from 'svelte';
|
|
4
|
+
import type { FragmentInfo } from '../hooks/usePartConfig.svelte';
|
|
4
5
|
import { type CameraPose } from '../hooks/useControls.svelte';
|
|
5
6
|
interface LocalConfigProps {
|
|
6
7
|
current: Struct;
|
|
7
8
|
isDirty: boolean;
|
|
8
|
-
|
|
9
|
+
componentNameToFragmentInfo: Record<string, FragmentInfo>;
|
|
9
10
|
setLocalPartConfig: (config: Struct) => void;
|
|
10
11
|
}
|
|
11
12
|
interface Props {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
const { invalidate } = useThrelte()
|
|
22
22
|
const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
|
|
23
|
-
const invisible = useTrait(() => entity, traits.
|
|
23
|
+
const invisible = useTrait(() => entity, traits.InheritedInvisible)
|
|
24
24
|
const showAxesHelper = useTrait(() => entity, traits.ShowAxesHelper)
|
|
25
25
|
|
|
26
26
|
const events = useEntityEvents(() => entity)
|
|
@@ -39,7 +39,7 @@ Renders a Viam Frame object
|
|
|
39
39
|
const entityColor = useTrait(() => entity, traits.Color)
|
|
40
40
|
const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
|
|
41
41
|
const center = useTrait(() => entity, traits.Center)
|
|
42
|
-
const invisible = useTrait(() => entity, traits.
|
|
42
|
+
const invisible = useTrait(() => entity, traits.InheritedInvisible)
|
|
43
43
|
|
|
44
44
|
const events = useEntityEvents(() => entity)
|
|
45
45
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
|
|
37
37
|
const gltfTrait = useTrait(() => entity, traits.GLTF)
|
|
38
|
-
const invisible = useTrait(() => entity, traits.
|
|
38
|
+
const invisible = useTrait(() => entity, traits.InheritedInvisible)
|
|
39
39
|
const showAxesHelper = useTrait(() => entity, traits.ShowAxesHelper)
|
|
40
40
|
const events = useEntityEvents(() => entity)
|
|
41
41
|
|
|
@@ -33,7 +33,7 @@ Renders a Viam Geometry object
|
|
|
33
33
|
const name = useTrait(() => entity, traits.Name)
|
|
34
34
|
const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
|
|
35
35
|
const center = useTrait(() => entity, traits.Center)
|
|
36
|
-
const invisible = useTrait(() => entity, traits.
|
|
36
|
+
const invisible = useTrait(() => entity, traits.InheritedInvisible)
|
|
37
37
|
|
|
38
38
|
const model = $derived.by(() => {
|
|
39
39
|
if (!settings.current.renderArmModels.includes('model')) {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
const renderOrder = useTrait(() => entity, traits.RenderOrder)
|
|
35
35
|
const opacity = useTrait(() => entity, traits.Opacity)
|
|
36
36
|
const screenSpace = useTrait(() => entity, traits.ScreenSpace)
|
|
37
|
-
const invisible = useTrait(() => entity, traits.
|
|
37
|
+
const invisible = useTrait(() => entity, traits.InheritedInvisible)
|
|
38
38
|
const showAxesHelper = useTrait(() => entity, traits.ShowAxesHelper)
|
|
39
39
|
|
|
40
40
|
const events = useEntityEvents(() => entity)
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
const colors = useTrait(() => entity, traits.Colors)
|
|
29
29
|
const entityPointSize = useTrait(() => entity, traits.PointSize)
|
|
30
30
|
const opacity = useTrait(() => entity, traits.Opacity)
|
|
31
|
-
const invisible = useTrait(() => entity, traits.
|
|
31
|
+
const invisible = useTrait(() => entity, traits.InheritedInvisible)
|
|
32
32
|
const showAxesHelper = useTrait(() => entity, traits.ShowAxesHelper)
|
|
33
33
|
const renderOrder = useTrait(() => entity, traits.RenderOrder)
|
|
34
34
|
const materialProps = useTrait(() => entity, traits.Material)
|
|
@@ -18,7 +18,7 @@ export const useEntityEvents = (entity) => {
|
|
|
18
18
|
const selectedEntity = useSelectedEntity();
|
|
19
19
|
const focusedEntity = useFocusedEntity();
|
|
20
20
|
const cursor = useCursor();
|
|
21
|
-
const invisible = useTrait(entity, traits.
|
|
21
|
+
const invisible = useTrait(entity, traits.InheritedInvisible);
|
|
22
22
|
const onpointerenter = (event) => {
|
|
23
23
|
if (invisible.current)
|
|
24
24
|
return;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { provideFramelessComponents } from '../hooks/useFramelessComponents.svelte'
|
|
12
12
|
import { provideFrames } from '../hooks/useFrames.svelte'
|
|
13
13
|
import { provideGeometries } from '../hooks/useGeometries.svelte'
|
|
14
|
+
import { provideInheritedInvisible } from '../hooks/useInheritedInvisible.svelte'
|
|
14
15
|
import { provideLinkedEntities } from '../hooks/useLinked.svelte'
|
|
15
16
|
import { provideLogs } from '../hooks/useLogs.svelte'
|
|
16
17
|
import { usePartID } from '../hooks/usePartID.svelte'
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
|
|
37
38
|
provideHierarchy()
|
|
38
39
|
provideWorldMatrix()
|
|
40
|
+
provideInheritedInvisible()
|
|
39
41
|
provideOrigin()
|
|
40
42
|
|
|
41
43
|
provideRelationships()
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { useTransformControls } from '../hooks/useControls.svelte'
|
|
9
9
|
import { useEnvironment } from '../hooks/useEnvironment.svelte'
|
|
10
10
|
import { useFrameEditSession } from '../hooks/useFrameEditSession.svelte'
|
|
11
|
+
import { usePartConfig } from '../hooks/usePartConfig.svelte'
|
|
11
12
|
import { useSelectedEntity, useSelectedObject3d } from '../hooks/useSelection.svelte'
|
|
12
13
|
import { useSettings } from '../hooks/useSettings.svelte'
|
|
13
14
|
import {
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
|
|
22
23
|
const settings = useSettings()
|
|
23
24
|
const environment = useEnvironment()
|
|
25
|
+
const partConfig = usePartConfig()
|
|
24
26
|
const transformControls = useTransformControls()
|
|
25
27
|
const selectedEntity = useSelectedEntity()
|
|
26
28
|
const selectedObject3d = useSelectedObject3d()
|
|
@@ -29,14 +31,20 @@
|
|
|
29
31
|
const mode = $derived(settings.current.transformMode)
|
|
30
32
|
const entity = $derived(selectedEntity.current)
|
|
31
33
|
const transformable = useTrait(() => entity, traits.Transformable)
|
|
34
|
+
const invisible = useTrait(() => entity, traits.InheritedInvisible)
|
|
32
35
|
const configMatrix = useTrait(() => entity, traits.Matrix)
|
|
33
36
|
const liveMatrix = useTrait(() => entity, traits.LiveMatrix)
|
|
34
37
|
const box = useTrait(() => entity, traits.Box)
|
|
35
38
|
const sphere = useTrait(() => entity, traits.Sphere)
|
|
36
39
|
const capsule = useTrait(() => entity, traits.Capsule)
|
|
40
|
+
const name = useTrait(() => entity, traits.Name)
|
|
37
41
|
const hasScalableGeometry = $derived(
|
|
38
42
|
box.current !== undefined || sphere.current !== undefined || capsule.current !== undefined
|
|
39
43
|
)
|
|
44
|
+
const isFragmentComponentWithVariables = $derived(
|
|
45
|
+
name.current &&
|
|
46
|
+
Object.keys(partConfig.componentNameToFragmentInfo[name.current]?.variables ?? {}).length > 0
|
|
47
|
+
)
|
|
40
48
|
|
|
41
49
|
// Mesh sets name={entity} on its inner mesh, so useSelectedObject3d resolves
|
|
42
50
|
// to that mesh — not the parent Frame Group we actually want to drive. Walk
|
|
@@ -241,7 +249,7 @@
|
|
|
241
249
|
}
|
|
242
250
|
</script>
|
|
243
251
|
|
|
244
|
-
{#if ref && entity && activeMode}
|
|
252
|
+
{#if ref && entity && activeMode && !isFragmentComponentWithVariables && !invisible.current}
|
|
245
253
|
{#key entity}
|
|
246
254
|
<TransformControls
|
|
247
255
|
object={ref}
|
|
@@ -109,7 +109,13 @@
|
|
|
109
109
|
|
|
110
110
|
const isFrameNode = $derived(!!framesAPI.current)
|
|
111
111
|
const isGeometry = $derived(!!geometriesAPI.current)
|
|
112
|
-
const
|
|
112
|
+
const isFragmentComponentWithVariables = $derived(
|
|
113
|
+
name.current &&
|
|
114
|
+
Object.keys(partConfig.componentNameToFragmentInfo[name.current]?.variables ?? {}).length > 0
|
|
115
|
+
)
|
|
116
|
+
const showEditFrameOptions = $derived(
|
|
117
|
+
isFrameNode && partConfig.hasEditPermissions && !isFragmentComponentWithVariables
|
|
118
|
+
)
|
|
113
119
|
const showRelationshipOptions = $derived(points.current || arrows.current)
|
|
114
120
|
const resourceName = $derived(name.current ? resourceByName.current[name.current] : undefined)
|
|
115
121
|
const displayType = $derived(isFrameNode ? resourceName?.subtype : isGeometry ? 'geometry' : '')
|
|
@@ -406,6 +412,16 @@
|
|
|
406
412
|
|
|
407
413
|
<div class="border-medium -mx-2 w-[100%+0.5rem] border-b"></div>
|
|
408
414
|
|
|
415
|
+
{#if isFragmentComponentWithVariables}
|
|
416
|
+
<p
|
|
417
|
+
class="mt-2 rounded border-l-4 border-yellow-600 bg-yellow-50 px-2 py-1.5 text-yellow-900"
|
|
418
|
+
data-testid="fragment-variables-warning"
|
|
419
|
+
role="status"
|
|
420
|
+
>
|
|
421
|
+
This component is from a fragment with variables, editing frames in 3D scene is disabled
|
|
422
|
+
</p>
|
|
423
|
+
{/if}
|
|
424
|
+
|
|
409
425
|
<h3
|
|
410
426
|
class="text-subtle-2 flex justify-between py-2"
|
|
411
427
|
data-testid="details-header"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
class={[
|
|
35
35
|
className,
|
|
36
36
|
'relative block rounded-md border',
|
|
37
|
-
active ? '
|
|
37
|
+
active ? 'z-4 border-[#666] bg-[#666] text-white' : 'border-gray-5 text-gray-8 bg-white',
|
|
38
38
|
]}
|
|
39
39
|
aria-describedby={tooltipID}
|
|
40
40
|
>
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
const name = useTrait(() => node.entity, traits.Name)
|
|
22
22
|
const invisible = useTrait(() => node.entity, traits.Invisible)
|
|
23
|
+
const inheritedInvisible = useTrait(() => node.entity, traits.InheritedInvisible)
|
|
23
24
|
const chunkProgress = useTrait(() => node.entity, traits.ChunkProgress)
|
|
24
25
|
const loading = $derived(chunkProgress.current !== undefined)
|
|
25
26
|
const progress = $derived(
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
class={[
|
|
56
57
|
'w-full',
|
|
57
58
|
{
|
|
58
|
-
'text-disabled':
|
|
59
|
+
'text-disabled': inheritedInvisible.current,
|
|
59
60
|
'bg-medium': nodeState.selected,
|
|
60
61
|
sticky: true,
|
|
61
62
|
},
|
|
@@ -129,7 +130,7 @@
|
|
|
129
130
|
<div
|
|
130
131
|
class={{
|
|
131
132
|
'flex justify-between': true,
|
|
132
|
-
'text-disabled':
|
|
133
|
+
'text-disabled': inheritedInvisible.current,
|
|
133
134
|
'bg-medium': nodeState.selected,
|
|
134
135
|
}}
|
|
135
136
|
{...api.getItemProps(nodeProps)}
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
<Portal id="dashboard">
|
|
63
63
|
<fieldset>
|
|
64
64
|
<DashboardButton
|
|
65
|
-
active
|
|
65
|
+
active={isOpen.current}
|
|
66
66
|
icon="cog"
|
|
67
67
|
description="Settings"
|
|
68
68
|
onclick={() => {
|
|
@@ -278,10 +278,6 @@
|
|
|
278
278
|
|
|
279
279
|
{#snippet Stats()}
|
|
280
280
|
<div class="flex w-full flex-col gap-2.5 text-xs">
|
|
281
|
-
<label class="flex items-center justify-between gap-2">
|
|
282
|
-
Query devtools <Switch bind:on={settings.current.enableQueryDevtools} />
|
|
283
|
-
</label>
|
|
284
|
-
|
|
285
281
|
<label class="flex items-center justify-between gap-2">
|
|
286
282
|
Render stats <Switch bind:on={settings.current.renderStats} />
|
|
287
283
|
</label>
|
package/dist/ecs/traits.d.ts
CHANGED
|
@@ -65,6 +65,13 @@ export declare const InstancedMatrix: import("koota").Trait<() => {
|
|
|
65
65
|
}>;
|
|
66
66
|
export declare const Hovered: import("koota").Trait<() => boolean>;
|
|
67
67
|
export declare const Invisible: import("koota").Trait<() => boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* True when the entity itself, or any of its parents up the `ChildOf`
|
|
70
|
+
* chain, has `Invisible`. Maintained by `provideInheritedInvisible`;
|
|
71
|
+
* don't add or remove it by hand — toggle `Invisible` and the cascade
|
|
72
|
+
* follows.
|
|
73
|
+
*/
|
|
74
|
+
export declare const InheritedInvisible: import("koota").Trait<() => boolean>;
|
|
68
75
|
/**
|
|
69
76
|
* Represents that an entity is composed of many instances, so that the treeview and
|
|
70
77
|
* details panel may display all instances
|
package/dist/ecs/traits.js
CHANGED
|
@@ -61,6 +61,13 @@ export const InstancedMatrix = trait(() => ({
|
|
|
61
61
|
}));
|
|
62
62
|
export const Hovered = trait(() => true);
|
|
63
63
|
export const Invisible = trait(() => true);
|
|
64
|
+
/**
|
|
65
|
+
* True when the entity itself, or any of its parents up the `ChildOf`
|
|
66
|
+
* chain, has `Invisible`. Maintained by `provideInheritedInvisible`;
|
|
67
|
+
* don't add or remove it by hand — toggle `Invisible` and the cascade
|
|
68
|
+
* follows.
|
|
69
|
+
*/
|
|
70
|
+
export const InheritedInvisible = trait(() => true);
|
|
64
71
|
/**
|
|
65
72
|
* Represents that an entity is composed of many instances, so that the treeview and
|
|
66
73
|
* details panel may display all instances
|
|
@@ -25,12 +25,12 @@ export const provideConfigFrames = () => {
|
|
|
25
25
|
});
|
|
26
26
|
const [fragmentFrames, fragmentUnsetFrameNames] = $derived.by(() => {
|
|
27
27
|
const { fragment_mods: fragmentMods = [] } = partConfig.current;
|
|
28
|
-
const fragmentDefinedComponents = Object.keys(partConfig.
|
|
28
|
+
const fragmentDefinedComponents = Object.keys(partConfig.componentNameToFragmentInfo);
|
|
29
29
|
const results = {};
|
|
30
30
|
const unsetResults = [];
|
|
31
31
|
// deal with fragment defined components
|
|
32
32
|
for (const fragmentComponentName of fragmentDefinedComponents || []) {
|
|
33
|
-
const fragmentId = partConfig.
|
|
33
|
+
const fragmentId = partConfig.componentNameToFragmentInfo[fragmentComponentName].id;
|
|
34
34
|
const fragmentMod = fragmentMods?.find((mod) => mod.fragment_id === fragmentId);
|
|
35
35
|
if (!fragmentMod) {
|
|
36
36
|
continue;
|
|
@@ -64,7 +64,7 @@ export const provideConfigFrames = () => {
|
|
|
64
64
|
* any whose frame the user has $unset.
|
|
65
65
|
*/
|
|
66
66
|
const unsetFragmentNames = new Set(fragmentUnsetFrameNames);
|
|
67
|
-
for (const name of Object.keys(partConfig.
|
|
67
|
+
for (const name of Object.keys(partConfig.componentNameToFragmentInfo)) {
|
|
68
68
|
if (!unsetFragmentNames.has(name)) {
|
|
69
69
|
validFrames.add(name);
|
|
70
70
|
}
|
|
@@ -11,7 +11,7 @@ export const provideFramelessComponents = () => {
|
|
|
11
11
|
?.filter((component) => component.frame === undefined)
|
|
12
12
|
.map((component) => component.name) ?? [];
|
|
13
13
|
const fragmentComponentsWithNoFrame = new Set(partComponentsWIthNoFrame);
|
|
14
|
-
for (const fragmentComponentName of Object.keys(partConfig.
|
|
14
|
+
for (const fragmentComponentName of Object.keys(partConfig.componentNameToFragmentInfo)) {
|
|
15
15
|
if (frames.current.some((frame) => frame.referenceFrame === fragmentComponentName)) {
|
|
16
16
|
continue;
|
|
17
17
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {} from 'koota';
|
|
2
|
+
import { ChildOf } from '../ecs/relations';
|
|
3
|
+
import { InheritedInvisible, Invisible } from '../ecs/traits';
|
|
4
|
+
import { useWorld } from '../ecs/useWorld';
|
|
5
|
+
/**
|
|
6
|
+
* Walks up `ChildOf` and returns true if the entity itself or any
|
|
7
|
+
* ancestor has `Invisible`. Memoizes via `cache` so siblings in the
|
|
8
|
+
* same flush reuse a parent's result.
|
|
9
|
+
*/
|
|
10
|
+
const hasInherited = (entity, cache) => {
|
|
11
|
+
const cached = cache.get(entity);
|
|
12
|
+
if (cached !== undefined)
|
|
13
|
+
return cached;
|
|
14
|
+
if (!entity.isAlive())
|
|
15
|
+
return false;
|
|
16
|
+
if (entity.has(Invisible)) {
|
|
17
|
+
cache.set(entity, true);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
const parent = entity.targetFor(ChildOf);
|
|
21
|
+
const inherited = parent && parent.isAlive() ? hasInherited(parent, cache) : false;
|
|
22
|
+
cache.set(entity, inherited);
|
|
23
|
+
return inherited;
|
|
24
|
+
};
|
|
25
|
+
const flushDirty = (world, dirty) => {
|
|
26
|
+
if (dirty.size === 0)
|
|
27
|
+
return;
|
|
28
|
+
const cache = new Map();
|
|
29
|
+
const allEntities = new Set();
|
|
30
|
+
const collectChildren = (entity) => {
|
|
31
|
+
if (allEntities.has(entity))
|
|
32
|
+
return;
|
|
33
|
+
allEntities.add(entity);
|
|
34
|
+
for (const child of world.query(ChildOf(entity))) {
|
|
35
|
+
collectChildren(child);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
for (const entity of dirty) {
|
|
39
|
+
collectChildren(entity);
|
|
40
|
+
}
|
|
41
|
+
dirty.clear();
|
|
42
|
+
for (const entity of allEntities) {
|
|
43
|
+
if (!entity.isAlive()) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const hasInheritedTrait = hasInherited(entity, cache);
|
|
47
|
+
const hasTrait = entity.has(InheritedInvisible);
|
|
48
|
+
if (hasInheritedTrait && !hasTrait) {
|
|
49
|
+
entity.add(InheritedInvisible);
|
|
50
|
+
}
|
|
51
|
+
else if (!hasInheritedTrait && hasTrait) {
|
|
52
|
+
entity.remove(InheritedInvisible);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
export const addInheritedInvisibleListeners = (world) => {
|
|
57
|
+
const dirty = new Set();
|
|
58
|
+
let scheduled = false;
|
|
59
|
+
const enqueue = (entity) => {
|
|
60
|
+
dirty.add(entity);
|
|
61
|
+
if (scheduled)
|
|
62
|
+
return;
|
|
63
|
+
scheduled = true;
|
|
64
|
+
// Microtask-deferred so a burst of changes is grouped into one subtree walk.
|
|
65
|
+
queueMicrotask(() => {
|
|
66
|
+
scheduled = false;
|
|
67
|
+
flushDirty(world, dirty);
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
for (const entity of world.query(Invisible)) {
|
|
71
|
+
enqueue(entity);
|
|
72
|
+
}
|
|
73
|
+
const unsubs = [
|
|
74
|
+
world.onAdd(Invisible, enqueue),
|
|
75
|
+
world.onRemove(Invisible, enqueue),
|
|
76
|
+
world.onAdd(ChildOf, enqueue),
|
|
77
|
+
world.onChange(ChildOf, enqueue),
|
|
78
|
+
world.onRemove(ChildOf, enqueue),
|
|
79
|
+
];
|
|
80
|
+
return () => {
|
|
81
|
+
for (const unsub of unsubs) {
|
|
82
|
+
unsub();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
export const provideInheritedInvisible = () => {
|
|
87
|
+
const world = useWorld();
|
|
88
|
+
$effect(() => addInheritedInvisibleListeners(world));
|
|
89
|
+
};
|
|
@@ -11,12 +11,16 @@ export interface PartConfig {
|
|
|
11
11
|
mods: any[];
|
|
12
12
|
}[];
|
|
13
13
|
}
|
|
14
|
+
export type FragmentInfo = {
|
|
15
|
+
id: string;
|
|
16
|
+
variables: Record<string, string>;
|
|
17
|
+
};
|
|
14
18
|
interface PartConfigContext {
|
|
15
19
|
current: PartConfig;
|
|
16
20
|
isDirty: boolean;
|
|
17
21
|
hasPendingSave: boolean;
|
|
18
22
|
hasEditPermissions: boolean;
|
|
19
|
-
|
|
23
|
+
componentNameToFragmentInfo: Record<string, FragmentInfo>;
|
|
20
24
|
updateFrame: (componentName: string, referenceFrame: string, pose: Pose, geometry?: Frame['geometry']) => void;
|
|
21
25
|
deleteFrame: (componentName: string) => void;
|
|
22
26
|
createFrame: (componentName: string) => void;
|
|
@@ -30,7 +34,7 @@ export declare const usePartConfig: () => PartConfigContext;
|
|
|
30
34
|
interface AppEmbeddedPartConfigProps {
|
|
31
35
|
current: Struct;
|
|
32
36
|
isDirty: boolean;
|
|
33
|
-
|
|
37
|
+
componentNameToFragmentInfo: Record<string, FragmentInfo>;
|
|
34
38
|
setLocalPartConfig: (config: Struct) => void;
|
|
35
39
|
}
|
|
36
40
|
export {};
|
|
@@ -155,8 +155,8 @@ export const providePartConfig = (partID, params) => {
|
|
|
155
155
|
get current() {
|
|
156
156
|
return current;
|
|
157
157
|
},
|
|
158
|
-
get
|
|
159
|
-
return config.
|
|
158
|
+
get componentNameToFragmentInfo() {
|
|
159
|
+
return config.componentNameToFragmentInfo;
|
|
160
160
|
},
|
|
161
161
|
get isDirty() {
|
|
162
162
|
return config.isDirty;
|
|
@@ -168,7 +168,7 @@ export const providePartConfig = (partID, params) => {
|
|
|
168
168
|
return config.hasEditPermissions;
|
|
169
169
|
},
|
|
170
170
|
updateFrame: (componentName, referenceFrame, framePosition, frameGeometry) => {
|
|
171
|
-
const fragmentId = config.
|
|
171
|
+
const fragmentId = config.componentNameToFragmentInfo[componentName]?.id;
|
|
172
172
|
if (fragmentId === undefined) {
|
|
173
173
|
updatePartFrame(componentName, referenceFrame, framePosition, frameGeometry);
|
|
174
174
|
}
|
|
@@ -177,7 +177,7 @@ export const providePartConfig = (partID, params) => {
|
|
|
177
177
|
}
|
|
178
178
|
},
|
|
179
179
|
deleteFrame: (componentName) => {
|
|
180
|
-
const fragmentId = config.
|
|
180
|
+
const fragmentId = config.componentNameToFragmentInfo[componentName]?.id;
|
|
181
181
|
if (fragmentId === undefined) {
|
|
182
182
|
deletePartFrame(componentName);
|
|
183
183
|
}
|
|
@@ -186,7 +186,7 @@ export const providePartConfig = (partID, params) => {
|
|
|
186
186
|
}
|
|
187
187
|
},
|
|
188
188
|
createFrame: (componentName) => {
|
|
189
|
-
const fragmentId = config.
|
|
189
|
+
const fragmentId = config.componentNameToFragmentInfo[componentName]?.id;
|
|
190
190
|
if (fragmentId === undefined) {
|
|
191
191
|
createPartFrame(componentName);
|
|
192
192
|
}
|
|
@@ -244,8 +244,8 @@ const useEmbeddedPartConfig = (props) => {
|
|
|
244
244
|
get current() {
|
|
245
245
|
return props.current ?? new Struct();
|
|
246
246
|
},
|
|
247
|
-
get
|
|
248
|
-
return props.
|
|
247
|
+
get componentNameToFragmentInfo() {
|
|
248
|
+
return props.componentNameToFragmentInfo;
|
|
249
249
|
},
|
|
250
250
|
set(config) {
|
|
251
251
|
const struct = Struct.fromJson(config);
|
|
@@ -287,7 +287,16 @@ const useStandalonePartConfig = (partID) => {
|
|
|
287
287
|
const id = typeof fragmentId === 'string' ? fragmentId : fragmentId.id;
|
|
288
288
|
return createAppQuery('getFragment', () => [id], { refetchInterval: false });
|
|
289
289
|
}));
|
|
290
|
-
const
|
|
290
|
+
const fragmentIdToVariables = $derived.by(() => {
|
|
291
|
+
const results = {};
|
|
292
|
+
for (const fragment of configJSON?.fragments ?? []) {
|
|
293
|
+
const id = typeof fragment === 'string' ? fragment : fragment.id;
|
|
294
|
+
const variables = typeof fragment === 'string' ? {} : fragment.variables;
|
|
295
|
+
results[id] = variables;
|
|
296
|
+
}
|
|
297
|
+
return results;
|
|
298
|
+
});
|
|
299
|
+
const componentNameToFragmentInfo = $derived.by(() => {
|
|
291
300
|
const results = {};
|
|
292
301
|
for (const query of fragmentQueries) {
|
|
293
302
|
if (!query.data) {
|
|
@@ -300,7 +309,10 @@ const useStandalonePartConfig = (partID) => {
|
|
|
300
309
|
if (component.kind.case === 'structValue') {
|
|
301
310
|
const componentName = component.kind.value.fields['name']?.kind;
|
|
302
311
|
if (componentName.case === 'stringValue') {
|
|
303
|
-
results[componentName.value] =
|
|
312
|
+
results[componentName.value] = {
|
|
313
|
+
id: fragmentId,
|
|
314
|
+
variables: fragmentIdToVariables[fragmentId] ?? {},
|
|
315
|
+
};
|
|
304
316
|
}
|
|
305
317
|
}
|
|
306
318
|
}
|
|
@@ -340,8 +352,8 @@ const useStandalonePartConfig = (partID) => {
|
|
|
340
352
|
get hasEditPermissions() {
|
|
341
353
|
return hasEditPermissions;
|
|
342
354
|
},
|
|
343
|
-
get
|
|
344
|
-
return
|
|
355
|
+
get componentNameToFragmentInfo() {
|
|
356
|
+
return componentNameToFragmentInfo;
|
|
345
357
|
},
|
|
346
358
|
set(config) {
|
|
347
359
|
current = Struct.fromJson(config);
|
|
@@ -23,7 +23,6 @@ export interface Settings {
|
|
|
23
23
|
enableMeasureAxisY: boolean;
|
|
24
24
|
enableMeasureAxisZ: boolean;
|
|
25
25
|
enableLabels: boolean;
|
|
26
|
-
enableQueryDevtools: boolean;
|
|
27
26
|
enableArmPositionsWidget: boolean;
|
|
28
27
|
openCameraWidgets: Record<string, string[]>;
|
|
29
28
|
openFramePovWidgets: Record<string, string[]>;
|
|
@@ -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 Debug: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type Debug = InstanceType<typeof Debug>;
|
|
18
|
+
export default Debug;
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export * as selectionTraits from './Selection/traits';
|
|
|
3
3
|
export { useSelectionPlugin } from './Selection/useSelectionPlugin.svelte';
|
|
4
4
|
export { default as DrawService } from './DrawService/DrawService.svelte';
|
|
5
5
|
export { default as Skybox } from './Skybox/Skybox.svelte';
|
|
6
|
+
export { default as Debug } from './Debug/Debug.svelte';
|
package/dist/plugins/index.js
CHANGED
|
@@ -6,3 +6,5 @@ export { useSelectionPlugin } from './Selection/useSelectionPlugin.svelte';
|
|
|
6
6
|
export { default as DrawService } from './DrawService/DrawService.svelte';
|
|
7
7
|
// Skybox
|
|
8
8
|
export { default as Skybox } from './Skybox/Skybox.svelte';
|
|
9
|
+
// Debug
|
|
10
|
+
export { default as Debug } from './Debug/Debug.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viamrobotics/motion-tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "Motion visualization with Viam",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"@sveltejs/vite-plugin-svelte": "6.1.4",
|
|
23
23
|
"@tailwindcss/forms": "0.5.10",
|
|
24
24
|
"@tailwindcss/vite": "4.1.13",
|
|
25
|
+
"@tanstack/svelte-query-devtools": "6.0.2",
|
|
25
26
|
"@testing-library/jest-dom": "6.8.0",
|
|
26
27
|
"@testing-library/svelte": "5.2.8",
|
|
27
28
|
"@testing-library/user-event": "^14.6.1",
|
|
@@ -87,6 +88,7 @@
|
|
|
87
88
|
"@ag-grid-community/core": ">=32.3.0",
|
|
88
89
|
"@ag-grid-community/styles": ">=32.3.0",
|
|
89
90
|
"@dimforge/rapier3d-compat": ">=0.17",
|
|
91
|
+
"@tanstack/svelte-query-devtools": ">=6",
|
|
90
92
|
"@threlte/core": ">=8",
|
|
91
93
|
"@threlte/extras": ">=9",
|
|
92
94
|
"@threlte/rapier": ">=3",
|
|
@@ -110,6 +112,11 @@
|
|
|
110
112
|
"svelte-tweakpane-ui": ">=1.5",
|
|
111
113
|
"svelte-virtuallists": ">=1"
|
|
112
114
|
},
|
|
115
|
+
"peerDependenciesMeta": {
|
|
116
|
+
"@tanstack/svelte-query-devtools": {
|
|
117
|
+
"optional": true
|
|
118
|
+
}
|
|
119
|
+
},
|
|
113
120
|
"engines": {
|
|
114
121
|
"node": ">=22.12.0"
|
|
115
122
|
},
|
|
@@ -143,7 +150,6 @@
|
|
|
143
150
|
"@connectrpc/connect": "1.7.0",
|
|
144
151
|
"@connectrpc/connect-web": "1.7.0",
|
|
145
152
|
"@neodrag/svelte": "^2.3.3",
|
|
146
|
-
"@tanstack/svelte-query-devtools": "^6.0.2",
|
|
147
153
|
"earcut": "^3.0.2",
|
|
148
154
|
"filtrex": "^3.1.0",
|
|
149
155
|
"koota": "0.6.5",
|