@viamrobotics/motion-tools 1.32.0 → 1.33.1
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 +17 -11
- package/dist/components/App.svelte.d.ts +14 -7
- package/dist/components/Entities/Entities.svelte +18 -25
- package/dist/components/Entities/Entities.svelte.d.ts +2 -17
- package/dist/components/Entities/Label.svelte +79 -13
- package/dist/components/Entities/Label.svelte.d.ts +2 -1
- package/dist/components/Entities/Labels.svelte +36 -0
- package/dist/components/Entities/Labels.svelte.d.ts +3 -0
- package/dist/components/Entities/LineDots.svelte +8 -3
- package/dist/components/Entities/labelLayout/applyTeleports.d.ts +9 -0
- package/dist/components/Entities/labelLayout/applyTeleports.js +39 -0
- package/dist/components/Entities/labelLayout/buildNeighborhood.d.ts +8 -0
- package/dist/components/Entities/labelLayout/buildNeighborhood.js +26 -0
- package/dist/components/Entities/labelLayout/cameraHash.d.ts +8 -0
- package/dist/components/Entities/labelLayout/cameraHash.js +25 -0
- package/dist/components/Entities/labelLayout/cost.d.ts +44 -0
- package/dist/components/Entities/labelLayout/cost.js +126 -0
- package/dist/components/Entities/labelLayout/createLabelLayout.d.ts +27 -0
- package/dist/components/Entities/labelLayout/createLabelLayout.js +194 -0
- package/dist/components/Entities/labelLayout/geometry.d.ts +20 -0
- package/dist/components/Entities/labelLayout/geometry.js +151 -0
- package/dist/components/Entities/labelLayout/labelStore.svelte.d.ts +17 -0
- package/dist/components/Entities/labelLayout/labelStore.svelte.js +28 -0
- package/dist/components/Entities/labelLayout/measure.d.ts +13 -0
- package/dist/components/Entities/labelLayout/measure.js +42 -0
- package/dist/components/Entities/labelLayout/slots.d.ts +11 -0
- package/dist/components/Entities/labelLayout/slots.js +47 -0
- package/dist/components/Entities/labelLayout/solve.d.ts +11 -0
- package/dist/components/Entities/labelLayout/solve.js +93 -0
- package/dist/components/Entities/labelLayout/spatialHash.d.ts +15 -0
- package/dist/components/Entities/labelLayout/spatialHash.js +53 -0
- package/dist/components/Entities/labelLayout/types.d.ts +105 -0
- package/dist/components/Entities/labelLayout/types.js +19 -0
- package/dist/components/Entities/labelLayout/writeBack.d.ts +20 -0
- package/dist/components/Entities/labelLayout/writeBack.js +51 -0
- package/dist/components/Scene.svelte +42 -48
- package/dist/components/SceneProviders.svelte +0 -3
- package/dist/components/SelectedTransformControls.svelte +65 -47
- package/dist/components/overlay/Details.svelte +198 -224
- package/dist/components/overlay/Details.svelte.d.ts +1 -1
- package/dist/components/overlay/Popover.svelte +6 -4
- package/dist/components/overlay/Popover.svelte.d.ts +6 -2
- package/dist/components/overlay/dashboard/Button.svelte +7 -2
- package/dist/components/overlay/dashboard/Button.svelte.d.ts +2 -1
- package/dist/components/overlay/details/AxesHelperDetails.svelte +32 -0
- package/dist/components/overlay/details/AxesHelperDetails.svelte.d.ts +7 -0
- package/dist/components/overlay/details/ColorDetails.svelte +35 -0
- package/dist/components/overlay/details/ColorDetails.svelte.d.ts +7 -0
- package/dist/components/overlay/details/GeometryDetails.svelte +104 -0
- package/dist/components/overlay/details/GeometryDetails.svelte.d.ts +7 -0
- package/dist/components/overlay/details/LineDetails/LineDetails.svelte +196 -0
- package/dist/components/overlay/details/LineDetails/LineDetails.svelte.d.ts +7 -0
- package/dist/components/overlay/details/LineDetails/linePositions.d.ts +3 -0
- package/dist/components/overlay/details/LineDetails/linePositions.js +30 -0
- package/dist/components/overlay/details/OpacityDetails.svelte +44 -0
- package/dist/components/overlay/details/OpacityDetails.svelte.d.ts +7 -0
- package/dist/components/overlay/details/PoseDetails.svelte +189 -0
- package/dist/components/overlay/details/PoseDetails.svelte.d.ts +14 -0
- package/dist/components/overlay/settings/ConnectionSettings.svelte +42 -0
- package/dist/components/overlay/settings/ConnectionSettings.svelte.d.ts +18 -0
- package/dist/components/overlay/settings/DebugSettings.svelte +13 -0
- package/dist/components/{xr/frame-configure/Controllers.svelte.d.ts → overlay/settings/DebugSettings.svelte.d.ts} +3 -3
- package/dist/components/overlay/settings/PointcloudSettings.svelte +61 -0
- package/dist/components/overlay/settings/PointcloudSettings.svelte.d.ts +3 -0
- package/dist/components/overlay/settings/SceneSettings.svelte +110 -0
- package/dist/components/overlay/settings/SceneSettings.svelte.d.ts +18 -0
- package/dist/components/overlay/settings/Settings.svelte +27 -312
- package/dist/components/overlay/settings/Settings.svelte.d.ts +8 -1
- package/dist/components/overlay/settings/Tabs.svelte +5 -3
- package/dist/components/overlay/settings/Tabs.svelte.d.ts +3 -3
- package/dist/components/overlay/settings/VisionSettings.svelte +31 -0
- package/dist/components/overlay/settings/VisionSettings.svelte.d.ts +3 -0
- package/dist/components/overlay/settings/WeblabSettings.svelte +27 -0
- package/dist/components/overlay/settings/WeblabSettings.svelte.d.ts +18 -0
- package/dist/components/overlay/settings/WidgetSettings.svelte +49 -0
- package/dist/components/overlay/settings/WidgetSettings.svelte.d.ts +3 -0
- package/dist/components/overlay/widgets/FramePov.svelte +1 -12
- package/dist/ecs/traits.d.ts +1 -1
- package/dist/ecs/traits.js +1 -1
- package/dist/hooks/useWorldState.svelte.js +39 -50
- package/dist/{components/xr → plugins/XR}/ArmTeleop.svelte +3 -5
- package/dist/plugins/XR/DebugPanel.svelte +29 -0
- package/dist/plugins/XR/DebugPanel.svelte.d.ts +3 -0
- package/dist/plugins/XR/OriginMarker.svelte +341 -0
- package/dist/plugins/XR/PendingEditsPanel.svelte +60 -0
- package/dist/plugins/XR/PendingEditsPanel.svelte.d.ts +18 -0
- package/dist/plugins/XR/WristDisplay.svelte +60 -0
- package/dist/plugins/XR/WristDisplay.svelte.d.ts +19 -0
- package/dist/{components/xr → plugins/XR}/XR.svelte +69 -23
- package/dist/plugins/XR/XRPlugins.svelte +9 -0
- package/dist/plugins/XR/XRPlugins.svelte.d.ts +26 -0
- package/dist/plugins/XR/XRSettings.svelte +240 -0
- package/dist/plugins/XR/XRSettings.svelte.d.ts +3 -0
- package/dist/{components/xr → plugins/XR}/XRToast.svelte +6 -9
- package/dist/plugins/XR/debug.svelte.d.ts +7 -0
- package/dist/plugins/XR/debug.svelte.js +13 -0
- package/dist/plugins/XR/frame-configure/Controllers.svelte +413 -0
- package/dist/plugins/XR/teleop/Controllers.svelte.d.ts +3 -0
- package/dist/{components/xr → plugins/XR}/useAnchors.svelte.d.ts +4 -0
- package/dist/{components/xr → plugins/XR}/useAnchors.svelte.js +22 -0
- package/dist/plugins/XR/useOrigin.svelte.d.ts +24 -0
- package/dist/plugins/XR/useOrigin.svelte.js +50 -0
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.js +2 -0
- package/dist/three/OBBHelper.js +1 -0
- package/package.json +3 -1
- package/dist/components/xr/OriginMarker.svelte +0 -151
- package/dist/components/xr/XRControllerSettings.svelte +0 -242
- package/dist/components/xr/XRControllerSettings.svelte.d.ts +0 -3
- package/dist/components/xr/frame-configure/Controllers.svelte +0 -6
- package/dist/components/xr/useOrigin.svelte.d.ts +0 -9
- package/dist/components/xr/useOrigin.svelte.js +0 -27
- /package/dist/{components/xr → plugins/XR}/ArmTeleop.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/BentPlaneGeometry.svelte +0 -0
- /package/dist/{components/xr → plugins/XR}/BentPlaneGeometry.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/CameraFeed.svelte +0 -0
- /package/dist/{components/xr → plugins/XR}/CameraFeed.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/JointLimitsWidget.svelte +0 -0
- /package/dist/{components/xr → plugins/XR}/JointLimitsWidget.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/OriginMarker.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/PointDistance.svelte +0 -0
- /package/dist/{components/xr → plugins/XR}/PointDistance.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/XR.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/XRConfigPanel.svelte +0 -0
- /package/dist/{components/xr → plugins/XR}/XRConfigPanel.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/XRToast.svelte.d.ts +0 -0
- /package/dist/{components/xr/teleop → plugins/XR/frame-configure}/Controllers.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/math.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/math.js +0 -0
- /package/dist/{components/xr → plugins/XR}/teleop/Controllers.svelte +0 -0
- /package/dist/{components/xr → plugins/XR}/toasts.svelte.d.ts +0 -0
- /package/dist/{components/xr → plugins/XR}/toasts.svelte.js +0 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { useXR } from '@threlte/xr'
|
|
8
8
|
import { provideToast, ToastContainer } from '@viamrobotics/prime-core'
|
|
9
9
|
import { primeTheme } from '@viamrobotics/tweakpane-config'
|
|
10
|
-
import { onMount, type Snippet } from 'svelte'
|
|
10
|
+
import { type Component, onMount, type Snippet } from 'svelte'
|
|
11
11
|
import { ThemeUtils } from 'svelte-tweakpane-ui'
|
|
12
12
|
|
|
13
13
|
import type { FragmentInfo } from '../hooks/usePartConfig.svelte'
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
import Details from './overlay/Details.svelte'
|
|
18
18
|
import TreeContainer from './overlay/left-pane/TreeContainer.svelte'
|
|
19
19
|
import Settings from './overlay/settings/Settings.svelte'
|
|
20
|
-
import XR from './xr/XR.svelte'
|
|
21
20
|
import { provideWorld, traits, useQuery } from '../ecs'
|
|
22
21
|
import { type CameraPose, provideCameraControls } from '../hooks/useControls.svelte'
|
|
23
22
|
import { provideEnvironment } from '../hooks/useEnvironment.svelte'
|
|
@@ -51,7 +50,20 @@
|
|
|
51
50
|
localConfigProps?: LocalConfigProps
|
|
52
51
|
|
|
53
52
|
/**
|
|
54
|
-
*
|
|
53
|
+
* Allows adding additional tabs to the settings panel
|
|
54
|
+
*/
|
|
55
|
+
settingsTabs?: {
|
|
56
|
+
label: string
|
|
57
|
+
component: Component
|
|
58
|
+
}[]
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Allows setting the initial camera pose
|
|
62
|
+
*/
|
|
63
|
+
cameraPose?: CameraPose
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Snippet for Three.js objects
|
|
55
67
|
*/
|
|
56
68
|
children?: Snippet
|
|
57
69
|
|
|
@@ -64,11 +76,6 @@
|
|
|
64
76
|
* Snippet to inject items into the details panel
|
|
65
77
|
*/
|
|
66
78
|
details?: Snippet<[{ entity: Entity }]>
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Allows setting the initial camera pose
|
|
70
|
-
*/
|
|
71
|
-
cameraPose?: CameraPose
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
let {
|
|
@@ -76,6 +83,7 @@
|
|
|
76
83
|
inputBindingsEnabled = true,
|
|
77
84
|
localConfigProps,
|
|
78
85
|
cameraPose,
|
|
86
|
+
settingsTabs,
|
|
79
87
|
children: appChildren,
|
|
80
88
|
dashboard,
|
|
81
89
|
details,
|
|
@@ -124,8 +132,6 @@
|
|
|
124
132
|
{@render appChildren?.()}
|
|
125
133
|
</Scene>
|
|
126
134
|
|
|
127
|
-
<XR {@attach domPortal(root)} />
|
|
128
|
-
|
|
129
135
|
{#if settings.current.renderSubEntityHoverDetail}
|
|
130
136
|
<HoveredEntities />
|
|
131
137
|
{/if}
|
|
@@ -166,7 +172,7 @@
|
|
|
166
172
|
|
|
167
173
|
<PortalTarget id="dom" />
|
|
168
174
|
|
|
169
|
-
<Settings />
|
|
175
|
+
<Settings {settingsTabs} />
|
|
170
176
|
<Logs />
|
|
171
177
|
<AddFrames />
|
|
172
178
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Struct } from '@viamrobotics/sdk';
|
|
2
2
|
import type { Entity } from 'koota';
|
|
3
|
-
import { type Snippet } from 'svelte';
|
|
3
|
+
import { type Component, type Snippet } from 'svelte';
|
|
4
4
|
import type { FragmentInfo } from '../hooks/usePartConfig.svelte';
|
|
5
5
|
import { type CameraPose } from '../hooks/useControls.svelte';
|
|
6
6
|
interface LocalConfigProps {
|
|
@@ -14,7 +14,18 @@ interface Props {
|
|
|
14
14
|
inputBindingsEnabled?: boolean;
|
|
15
15
|
localConfigProps?: LocalConfigProps;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Allows adding additional tabs to the settings panel
|
|
18
|
+
*/
|
|
19
|
+
settingsTabs?: {
|
|
20
|
+
label: string;
|
|
21
|
+
component: Component;
|
|
22
|
+
}[];
|
|
23
|
+
/**
|
|
24
|
+
* Allows setting the initial camera pose
|
|
25
|
+
*/
|
|
26
|
+
cameraPose?: CameraPose;
|
|
27
|
+
/**
|
|
28
|
+
* Snippet for Three.js objects
|
|
18
29
|
*/
|
|
19
30
|
children?: Snippet;
|
|
20
31
|
/**
|
|
@@ -27,11 +38,7 @@ interface Props {
|
|
|
27
38
|
details?: Snippet<[{
|
|
28
39
|
entity: Entity;
|
|
29
40
|
}]>;
|
|
30
|
-
/**
|
|
31
|
-
* Allows setting the initial camera pose
|
|
32
|
-
*/
|
|
33
|
-
cameraPose?: CameraPose;
|
|
34
41
|
}
|
|
35
|
-
declare const App:
|
|
42
|
+
declare const App: Component<Props, {}, "">;
|
|
36
43
|
type App = ReturnType<typeof App>;
|
|
37
44
|
export default App;
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
import { Not, Or } from 'koota'
|
|
3
3
|
|
|
4
4
|
import { traits, useQuery } from '../../ecs'
|
|
5
|
+
import { useSettings } from '../../hooks/useSettings.svelte'
|
|
5
6
|
|
|
6
7
|
import Arrows from './Arrows/ArrowGroups.svelte'
|
|
7
8
|
import Frame from './Frame.svelte'
|
|
8
9
|
import Geometry from './Geometry.svelte'
|
|
9
10
|
import GLTF from './GLTF.svelte'
|
|
10
|
-
import
|
|
11
|
+
import Labels from './Labels.svelte'
|
|
11
12
|
import Line from './Line.svelte'
|
|
12
13
|
import Points from './Points.svelte'
|
|
13
14
|
import Pose from './Pose.svelte'
|
|
@@ -60,56 +61,48 @@
|
|
|
60
61
|
const points = useQuery(traits.Points)
|
|
61
62
|
const lines = useQuery(traits.LinePositions)
|
|
62
63
|
const gltfs = useQuery(traits.GLTF)
|
|
64
|
+
|
|
65
|
+
const settings = useSettings()
|
|
66
|
+
|
|
67
|
+
const enableLabels = $derived(settings.current.enableLabels)
|
|
63
68
|
</script>
|
|
64
69
|
|
|
65
70
|
{#each machineFramesEntities.current as entity (entity)}
|
|
66
71
|
<Pose {entity}>
|
|
67
|
-
<Frame {entity}
|
|
68
|
-
<Label text={entity.get(traits.Name)} />
|
|
69
|
-
</Frame>
|
|
72
|
+
<Frame {entity} />
|
|
70
73
|
</Pose>
|
|
71
74
|
{/each}
|
|
72
75
|
|
|
73
76
|
{#each resourceGeometriesEntities.current as entity (entity)}
|
|
74
|
-
<Geometry {entity}
|
|
75
|
-
<Label text={entity.get(traits.Name)} />
|
|
76
|
-
</Geometry>
|
|
77
|
+
<Geometry {entity} />
|
|
77
78
|
{/each}
|
|
78
79
|
|
|
79
80
|
{#each worldStateEntities.current as entity (entity)}
|
|
80
|
-
<Frame {entity}
|
|
81
|
-
<Label text={entity.get(traits.Name)} />
|
|
82
|
-
</Frame>
|
|
81
|
+
<Frame {entity} />
|
|
83
82
|
{/each}
|
|
84
83
|
|
|
85
84
|
{#each drawServiceEntities.current as entity (entity)}
|
|
86
|
-
<Frame {entity}
|
|
87
|
-
<Label text={entity.get(traits.Name)} />
|
|
88
|
-
</Frame>
|
|
85
|
+
<Frame {entity} />
|
|
89
86
|
{/each}
|
|
90
87
|
|
|
91
88
|
{#each meshEntities.current as entity (entity)}
|
|
92
|
-
<Frame {entity}
|
|
93
|
-
<Label text={entity.get(traits.Name)} />
|
|
94
|
-
</Frame>
|
|
89
|
+
<Frame {entity} />
|
|
95
90
|
{/each}
|
|
96
91
|
|
|
97
92
|
{#each points.current as entity (entity)}
|
|
98
|
-
<Points {entity}
|
|
99
|
-
<Label text={entity.get(traits.Name)} />
|
|
100
|
-
</Points>
|
|
93
|
+
<Points {entity} />
|
|
101
94
|
{/each}
|
|
102
95
|
|
|
103
96
|
{#each lines.current as entity (entity)}
|
|
104
|
-
<Line {entity}
|
|
105
|
-
<Label text={entity.get(traits.Name)} />
|
|
106
|
-
</Line>
|
|
97
|
+
<Line {entity} />
|
|
107
98
|
{/each}
|
|
108
99
|
|
|
109
100
|
{#each gltfs.current as entity (entity)}
|
|
110
|
-
<GLTF {entity}
|
|
111
|
-
<Label text={entity.get(traits.Name)} />
|
|
112
|
-
</GLTF>
|
|
101
|
+
<GLTF {entity} />
|
|
113
102
|
{/each}
|
|
114
103
|
|
|
115
104
|
<Arrows />
|
|
105
|
+
|
|
106
|
+
{#if enableLabels}
|
|
107
|
+
<Labels />
|
|
108
|
+
{/if}
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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 Entities: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
-
[evt: string]: CustomEvent<any>;
|
|
16
|
-
}, {}, {}, string>;
|
|
17
|
-
type Entities = InstanceType<typeof Entities>;
|
|
1
|
+
declare const Entities: import("svelte").Component<Record<string, never>, {}, "">;
|
|
2
|
+
type Entities = ReturnType<typeof Entities>;
|
|
18
3
|
export default Entities;
|
|
@@ -1,25 +1,91 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Entity } from 'koota'
|
|
3
|
+
|
|
4
|
+
import { useThrelte } from '@threlte/core'
|
|
2
5
|
import { HTML } from '@threlte/extras'
|
|
6
|
+
import { untrack } from 'svelte'
|
|
7
|
+
import { Group } from 'three'
|
|
8
|
+
|
|
9
|
+
import { traits, useTag, useTrait } from '../../ecs'
|
|
3
10
|
|
|
4
|
-
import {
|
|
11
|
+
import { labels } from './labelLayout/labelStore.svelte'
|
|
5
12
|
|
|
6
13
|
interface Props {
|
|
7
|
-
|
|
14
|
+
entity: Entity
|
|
8
15
|
}
|
|
9
16
|
|
|
10
|
-
let {
|
|
17
|
+
let { entity }: Props = $props()
|
|
18
|
+
|
|
19
|
+
const { invalidate } = useThrelte()
|
|
20
|
+
|
|
21
|
+
const matrix = useTrait(() => entity, traits.WorldMatrix)
|
|
22
|
+
const name = useTrait(() => entity, traits.Name)
|
|
23
|
+
const color = useTrait(() => entity, traits.Color)
|
|
24
|
+
const selected = useTag(() => entity, traits.Selected)
|
|
25
|
+
|
|
26
|
+
let element = $state.raw<HTMLElement>()
|
|
27
|
+
|
|
28
|
+
$effect(() => {
|
|
29
|
+
const el = element
|
|
30
|
+
|
|
31
|
+
if (!el) return
|
|
32
|
+
|
|
33
|
+
return untrack(() => {
|
|
34
|
+
labels.add(el)
|
|
35
|
+
return () => labels.remove(el)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Re-measure when the label text changes (its width drives slot geometry).
|
|
40
|
+
$effect(() => {
|
|
41
|
+
if (name.current) {
|
|
42
|
+
untrack(() => labels.touch())
|
|
43
|
+
}
|
|
44
|
+
})
|
|
11
45
|
|
|
12
|
-
|
|
46
|
+
let ref = $state<Group>()
|
|
13
47
|
|
|
14
|
-
|
|
48
|
+
$effect(() => {
|
|
49
|
+
if (matrix.current && ref) {
|
|
50
|
+
ref.matrix.copy(matrix.current)
|
|
51
|
+
ref.updateMatrixWorld()
|
|
52
|
+
invalidate()
|
|
53
|
+
}
|
|
54
|
+
})
|
|
15
55
|
</script>
|
|
16
56
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
57
|
+
<HTML
|
|
58
|
+
center
|
|
59
|
+
zIndexRange={[3, 0]}
|
|
60
|
+
matrixAutoUpdate={false}
|
|
61
|
+
bind:ref
|
|
62
|
+
>
|
|
63
|
+
<div
|
|
64
|
+
class="label relative h-0 w-0"
|
|
65
|
+
bind:this={element}
|
|
22
66
|
>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
67
|
+
<svg class="link pointer-events-none absolute top-0 left-0 overflow-visible">
|
|
68
|
+
<line class="stroke-gray-9 stroke-1" />
|
|
69
|
+
</svg>
|
|
70
|
+
<div
|
|
71
|
+
class="dot border-gray-9 pointer-events-none absolute -top-1 -left-0 z-1 h-2 w-2 -translate-1/2 rounded-full border"
|
|
72
|
+
></div>
|
|
73
|
+
<button
|
|
74
|
+
class={[
|
|
75
|
+
'border-gray-9 text absolute z-2 border px-2 py-1 text-xs text-nowrap',
|
|
76
|
+
{
|
|
77
|
+
'bg-gray-9 text-white': selected.current,
|
|
78
|
+
'bg-white': !selected.current,
|
|
79
|
+
},
|
|
80
|
+
]}
|
|
81
|
+
style={color.current
|
|
82
|
+
? `border-color-left: rgb(${color.current.r}, ${color.current.g}, ${color.current.b})`
|
|
83
|
+
: undefined}
|
|
84
|
+
onclick={() => {
|
|
85
|
+
entity.add(traits.Selected)
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
{name.current}
|
|
89
|
+
</button>
|
|
90
|
+
</div>
|
|
91
|
+
</HTML>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useTask, useThrelte } from '@threlte/core'
|
|
3
|
+
|
|
4
|
+
import { traits, useQuery } from '../../ecs'
|
|
5
|
+
|
|
6
|
+
import Label from './Label.svelte'
|
|
7
|
+
import { createLabelLayout } from './labelLayout/createLabelLayout'
|
|
8
|
+
import { labels } from './labelLayout/labelStore.svelte'
|
|
9
|
+
|
|
10
|
+
const { camera, invalidate, size } = useThrelte()
|
|
11
|
+
|
|
12
|
+
const entities = useQuery(traits.Name)
|
|
13
|
+
|
|
14
|
+
const layout = createLabelLayout({ camera, size, invalidate, labels })
|
|
15
|
+
|
|
16
|
+
// Wake the on-demand render loop when labels are added/removed or their text
|
|
17
|
+
// changes, so the engine re-solves even while the camera is still. Reading
|
|
18
|
+
// `version` registers the reactive dependency.
|
|
19
|
+
$effect(() => {
|
|
20
|
+
if (labels.version >= 0) invalidate()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// `autoInvalidate: false` — the engine drives its own invalidation (camera
|
|
24
|
+
// motion, the version effect above, and while animating), so the task can run
|
|
25
|
+
// without pinning the on-demand Canvas to render every frame.
|
|
26
|
+
useTask(
|
|
27
|
+
(delta) => {
|
|
28
|
+
layout.frame(delta)
|
|
29
|
+
},
|
|
30
|
+
{ autoInvalidate: false }
|
|
31
|
+
)
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
{#each entities.current as entity (entity)}
|
|
35
|
+
<Label {entity} />
|
|
36
|
+
{/each}
|
|
@@ -41,9 +41,15 @@
|
|
|
41
41
|
|
|
42
42
|
$effect(() => {
|
|
43
43
|
if (!positions) return
|
|
44
|
+
// Track the IDs `addInstance` returns rather than assuming they're a
|
|
45
|
+
// sequential 0..N-1 range — when positions changes (e.g. a line gizmo
|
|
46
|
+
// being placed), cleanup-by-index would target slots that were never
|
|
47
|
+
// allocated for this effect run and throw "Invalid instanceId".
|
|
48
|
+
const instances: number[] = []
|
|
44
49
|
for (let i = 0, l = positions.length; i < l; i += 3) {
|
|
45
50
|
const dotIndex = i / 3
|
|
46
51
|
const instance = mesh.addInstance(geometryID)
|
|
52
|
+
instances.push(instance)
|
|
47
53
|
matrix.makeTranslation(positions[i + 0], positions[i + 1], positions[i + 2])
|
|
48
54
|
matrix.scale(vec3.setScalar(scale))
|
|
49
55
|
mesh.setMatrixAt(instance, matrix)
|
|
@@ -55,9 +61,8 @@
|
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
return () => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
mesh.deleteInstance(i)
|
|
64
|
+
for (const instance of instances) {
|
|
65
|
+
mesh.deleteInstance(instance)
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-solve teleport handling: decides which nodes snap to their solved target
|
|
3
|
+
* this frame versus ease toward it. A big camera jump (large median anchor
|
|
4
|
+
* displacement) or a brand-new node (no prior anchor) places labels in their
|
|
5
|
+
* final spot rather than gliding across the screen; everything else eases.
|
|
6
|
+
* Always rolls each node's prevAx/prevAy forward for the next solve's comparison.
|
|
7
|
+
*/
|
|
8
|
+
import type { LabelNode, SolverConfig } from './types';
|
|
9
|
+
export declare const applyTeleports: (nodes: LabelNode[], width: number, height: number, config: SolverConfig) => void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-solve teleport handling: decides which nodes snap to their solved target
|
|
3
|
+
* this frame versus ease toward it. A big camera jump (large median anchor
|
|
4
|
+
* displacement) or a brand-new node (no prior anchor) places labels in their
|
|
5
|
+
* final spot rather than gliding across the screen; everything else eases.
|
|
6
|
+
* Always rolls each node's prevAx/prevAy forward for the next solve's comparison.
|
|
7
|
+
*/
|
|
8
|
+
/** The radius of a node's outermost slot (0 if it has none). */
|
|
9
|
+
const maxSlotRadius = (node) => {
|
|
10
|
+
const last = node.slots.at(-1);
|
|
11
|
+
return last ? last.radius : 0;
|
|
12
|
+
};
|
|
13
|
+
export const applyTeleports = (nodes, width, height, config) => {
|
|
14
|
+
const diag = Math.hypot(width, height);
|
|
15
|
+
const displacements = [];
|
|
16
|
+
for (const node of nodes) {
|
|
17
|
+
if (!Number.isNaN(node.prevAx)) {
|
|
18
|
+
displacements.push(Math.hypot(node.ax - node.prevAx, node.ay - node.prevAy));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
let snapAll = displacements.length === 0;
|
|
22
|
+
if (!snapAll) {
|
|
23
|
+
displacements.sort((a, b) => a - b);
|
|
24
|
+
const median = displacements[displacements.length >> 1];
|
|
25
|
+
if (median > config.teleportFrac * diag)
|
|
26
|
+
snapAll = true;
|
|
27
|
+
}
|
|
28
|
+
for (const node of nodes) {
|
|
29
|
+
const ownJump = !Number.isNaN(node.prevAx) &&
|
|
30
|
+
Math.hypot(node.ax - node.prevAx, node.ay - node.prevAy) > maxSlotRadius(node) * 3;
|
|
31
|
+
if (snapAll || ownJump) {
|
|
32
|
+
node.cx = node.tx;
|
|
33
|
+
node.cy = node.ty;
|
|
34
|
+
node.settled = true;
|
|
35
|
+
}
|
|
36
|
+
node.prevAx = node.ax;
|
|
37
|
+
node.prevAy = node.ay;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds each node's pruned, symmetric interaction set for one solve. The cell
|
|
3
|
+
* size is chosen so any two labels whose boxes could possibly interact share or
|
|
4
|
+
* border a cell, so the 3x3 query around each node finds every candidate.
|
|
5
|
+
*/
|
|
6
|
+
import type { SpatialHash } from './spatialHash';
|
|
7
|
+
import type { LabelNode, SolverConfig } from './types';
|
|
8
|
+
export declare const buildNeighborhood: (grid: SpatialHash, nodes: LabelNode[], config: SolverConfig) => void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds each node's pruned, symmetric interaction set for one solve. The cell
|
|
3
|
+
* size is chosen so any two labels whose boxes could possibly interact share or
|
|
4
|
+
* border a cell, so the 3x3 query around each node finds every candidate.
|
|
5
|
+
*/
|
|
6
|
+
export const buildNeighborhood = (grid, nodes, config) => {
|
|
7
|
+
const maxRingMult = Math.max(...config.ringRadiiCrowded);
|
|
8
|
+
// Cell size so any two labels whose boxes could interact share/border a cell.
|
|
9
|
+
let cell = 1;
|
|
10
|
+
for (const node of nodes) {
|
|
11
|
+
const halfDiag = Math.hypot(node.w / 2, node.h / 2);
|
|
12
|
+
const support = Math.max(node.w, node.h) / 2;
|
|
13
|
+
const outer = (support + node.dotR + config.dotPadding) * maxRingMult;
|
|
14
|
+
cell = Math.max(cell, 2 * (halfDiag + outer));
|
|
15
|
+
}
|
|
16
|
+
grid.build(nodes, cell);
|
|
17
|
+
// Symmetric neighbourhoods so the solver's incremental bookkeeping stays exact.
|
|
18
|
+
for (const node of nodes)
|
|
19
|
+
node.neighbors = grid.queryNeighbors(node, config.maxNeighbors);
|
|
20
|
+
for (const a of nodes) {
|
|
21
|
+
for (const b of a.neighbors) {
|
|
22
|
+
if (!b.neighbors.includes(a))
|
|
23
|
+
b.neighbors.push(a);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cheap, exact change-signal for the camera. Hashing the view + projection
|
|
3
|
+
* matrices (as raw float bits) plus the viewport size catches every pan, orbit,
|
|
4
|
+
* dolly, zoom, resize, and perspective/orthographic swap with no epsilon to tune.
|
|
5
|
+
* Computed every frame; the layout only re-solves when the hash changes.
|
|
6
|
+
*/
|
|
7
|
+
import type { Camera } from 'three';
|
|
8
|
+
export declare function cameraMatrixHash(camera: Camera, width: number, height: number): number;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cheap, exact change-signal for the camera. Hashing the view + projection
|
|
3
|
+
* matrices (as raw float bits) plus the viewport size catches every pan, orbit,
|
|
4
|
+
* dolly, zoom, resize, and perspective/orthographic swap with no epsilon to tune.
|
|
5
|
+
* Computed every frame; the layout only re-solves when the hash changes.
|
|
6
|
+
*/
|
|
7
|
+
const f32 = new Float32Array(1);
|
|
8
|
+
const i32 = new Int32Array(f32.buffer);
|
|
9
|
+
function bits(value) {
|
|
10
|
+
f32[0] = value;
|
|
11
|
+
return i32[0];
|
|
12
|
+
}
|
|
13
|
+
export function cameraMatrixHash(camera, width, height) {
|
|
14
|
+
camera.updateMatrixWorld();
|
|
15
|
+
const view = camera.matrixWorldInverse.elements;
|
|
16
|
+
const proj = camera.projectionMatrix.elements;
|
|
17
|
+
let h = 2166136261 >>> 0;
|
|
18
|
+
for (let i = 0; i < 16; i++)
|
|
19
|
+
h = Math.imul(h ^ bits(view[i]), 16777619) >>> 0;
|
|
20
|
+
for (let i = 0; i < 16; i++)
|
|
21
|
+
h = Math.imul(h ^ bits(proj[i]), 16777619) >>> 0;
|
|
22
|
+
h = Math.imul(h ^ bits(width), 16777619) >>> 0;
|
|
23
|
+
h = Math.imul(h ^ bits(height), 16777619) >>> 0;
|
|
24
|
+
return h >>> 0;
|
|
25
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost function for a candidate label placement. Lower is better.
|
|
3
|
+
*
|
|
4
|
+
* The hierarchy `lineBox >> boxDot > boxBox > lineLine >> stick > spread > len`
|
|
5
|
+
* is near-lexicographic: a single leader passing under another label always
|
|
6
|
+
* outranks fixing every overlap a node could have against its <=24 neighbors,
|
|
7
|
+
* so the optimizer eliminates crossings first (the user's top priority), then
|
|
8
|
+
* dot coverage, then box overlaps, then tidies the radial fan.
|
|
9
|
+
*/
|
|
10
|
+
import type { LabelNode, SolverConfig } from './types';
|
|
11
|
+
export declare const W: {
|
|
12
|
+
/** DOMINANT — a leader passing under ANOTHER label's box. Requirement #1. */
|
|
13
|
+
lineBox: number;
|
|
14
|
+
/** Our box covering another node's dot. Worse than a box overlap. Requirement #4. */
|
|
15
|
+
boxDot: number;
|
|
16
|
+
/** Two label boxes overlapping. Requirement #2. */
|
|
17
|
+
boxBox: number;
|
|
18
|
+
/** Two leaders crossing — thin lines, mild. Supports the radial fan. */
|
|
19
|
+
lineLine: number;
|
|
20
|
+
/** Sticky bonus for staying on the previous slot (anti flip-flop). Requirement #6. */
|
|
21
|
+
stick: number;
|
|
22
|
+
/** Penalty when a slot's angle nearly coincides with a neighbor's leader angle. */
|
|
23
|
+
spread: number;
|
|
24
|
+
/** Outward-fan preference: cheaper to point away from the local cluster. Requirement #3. */
|
|
25
|
+
radial: number;
|
|
26
|
+
/** Leader length — keep labels close to their dot. Small. */
|
|
27
|
+
len: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The geometric "bad" terms only (leader-under-box, box-over-dot, box-box,
|
|
31
|
+
* leader-leader, angular spread) for placing `node` at slot `si` against its
|
|
32
|
+
* committed neighbors. This is the local-search objective AND its termination
|
|
33
|
+
* guard — exactly 0 when the node has no crossings/overlaps. The solver both
|
|
34
|
+
* selects and accepts moves on this value so it can never lock a node at a
|
|
35
|
+
* placement whose conflict another available slot would reduce.
|
|
36
|
+
*/
|
|
37
|
+
export declare function evalConflict(node: LabelNode, si: number, neighbors: LabelNode[], config: SolverConfig): number;
|
|
38
|
+
/**
|
|
39
|
+
* Conflict-independent "tidiness" of a slot: short leaders, pointing away from
|
|
40
|
+
* the local cluster centroid (radial fan-out), with a bonus for staying put.
|
|
41
|
+
* Used only as a tie-break among slots of equal conflict, never to override a
|
|
42
|
+
* conflict reduction.
|
|
43
|
+
*/
|
|
44
|
+
export declare function placementBias(node: LabelNode, si: number): number;
|