@viamrobotics/motion-tools 1.16.0 → 1.18.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/attribute.d.ts +3 -2
- package/dist/attribute.js +24 -16
- package/dist/buf/draw/v1/drawing_pb.d.ts +33 -16
- package/dist/buf/draw/v1/drawing_pb.js +35 -17
- package/dist/buf/draw/v1/metadata_pb.d.ts +44 -3
- package/dist/buf/draw/v1/metadata_pb.js +54 -3
- package/dist/buf/draw/v1/scene_pb.d.ts +6 -6
- package/dist/buf/draw/v1/scene_pb.js +7 -7
- package/dist/buffer.d.ts +54 -45
- package/dist/buffer.js +91 -57
- package/dist/color.d.ts +1 -2
- package/dist/color.js +5 -12
- package/dist/components/App.svelte +18 -3
- package/dist/components/App.svelte.d.ts +15 -2
- package/dist/components/Entities/Arrows/ArrowGroups.svelte +5 -6
- package/dist/components/Entities/Arrows/Arrows.svelte +9 -0
- package/dist/components/Entities/Entities.svelte +18 -1
- package/dist/components/Entities/Frame.svelte +7 -1
- package/dist/components/Entities/GLTF.svelte +13 -2
- package/dist/components/Entities/Line.svelte +46 -18
- package/dist/components/Entities/LineDots.svelte +38 -8
- package/dist/components/Entities/LineDots.svelte.d.ts +2 -2
- package/dist/components/Entities/LineGeometry.svelte +2 -1
- package/dist/components/Entities/LineGeometry.svelte.d.ts +2 -0
- package/dist/components/Entities/Mesh.svelte +8 -1
- package/dist/components/Entities/Points.svelte +22 -11
- package/dist/components/Entities/hooks/useEntityEvents.svelte.js +6 -2
- package/dist/components/FileDrop/FileDrop.svelte +5 -1
- package/dist/components/KeyboardControls.svelte +2 -10
- package/dist/components/PCD.svelte +11 -4
- package/dist/components/PCD.svelte.d.ts +3 -1
- package/dist/components/SceneProviders.svelte +2 -0
- package/dist/components/Selected.svelte +2 -12
- package/dist/components/Selection/Ellipse.svelte +1 -0
- package/dist/components/Selection/Lasso.svelte +2 -0
- package/dist/components/Selection/Tool.svelte +7 -56
- package/dist/components/Selection/Tool.svelte.d.ts +2 -2
- package/dist/components/Selection/useSelectionPlugin.svelte.d.ts +8 -0
- package/dist/components/Selection/useSelectionPlugin.svelte.js +24 -0
- package/dist/components/Snapshot.svelte +4 -2
- package/dist/components/overlay/AddRelationship.svelte +1 -2
- package/dist/components/overlay/AddRelationship.svelte.d.ts +1 -1
- package/dist/components/overlay/Details.svelte +12 -12
- package/dist/components/overlay/Details.svelte.d.ts +8 -1
- package/dist/components/overlay/settings/Settings.svelte +8 -1
- package/dist/components/xr/XR.svelte +1 -1
- package/dist/draw.d.ts +13 -0
- package/dist/draw.js +428 -0
- package/dist/ecs/traits.d.ts +31 -13
- package/dist/ecs/traits.js +25 -8
- package/dist/geometry.js +3 -0
- package/dist/hooks/useDrawAPI.svelte.js +61 -24
- package/dist/hooks/useDrawService.svelte.d.ts +12 -0
- package/dist/hooks/useDrawService.svelte.js +240 -0
- package/dist/hooks/usePointcloudObjects.svelte.js +7 -2
- package/dist/hooks/usePointclouds.svelte.js +7 -2
- package/dist/hooks/useSettings.svelte.d.ts +2 -1
- package/dist/hooks/useSettings.svelte.js +1 -1
- package/dist/hooks/useWorldState.svelte.js +5 -52
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +2 -0
- package/dist/loaders/pcd/index.d.ts +1 -1
- package/dist/loaders/pcd/messages.d.ts +2 -2
- package/dist/loaders/pcd/worker.inline.d.ts +1 -1
- package/dist/loaders/pcd/worker.inline.js +229 -187
- package/dist/loaders/pcd/worker.js +2 -2
- package/dist/metadata.d.ts +9 -15
- package/dist/metadata.js +45 -9
- package/dist/plugins/bvh.svelte.js +6 -2
- package/dist/snapshot.d.ts +3 -9
- package/dist/snapshot.js +11 -204
- package/dist/three/InstancedArrows/InstancedArrows.js +3 -2
- package/package.json +14 -11
- package/dist/components/xr/Hands.svelte +0 -23
- package/dist/components/xr/Hands.svelte.d.ts +0 -18
|
@@ -1,34 +1,60 @@
|
|
|
1
|
+
<script
|
|
2
|
+
lang="ts"
|
|
3
|
+
module
|
|
4
|
+
>
|
|
5
|
+
const matrix = new Matrix4()
|
|
6
|
+
const vec3 = new Vector3()
|
|
7
|
+
const threeColor = new Color()
|
|
8
|
+
const rgb = { r: 0, g: 0, b: 0 }
|
|
9
|
+
</script>
|
|
10
|
+
|
|
1
11
|
<script lang="ts">
|
|
2
12
|
import { T } from '@threlte/core'
|
|
3
|
-
import { BatchedMesh,
|
|
13
|
+
import { BatchedMesh, Color, Matrix4, SphereGeometry, Vector3 } from 'three'
|
|
14
|
+
|
|
15
|
+
import { asColor, asRGB, isSingleColor, isVertexColors, STRIDE } from '../../buffer'
|
|
4
16
|
|
|
5
17
|
interface Props {
|
|
6
|
-
|
|
18
|
+
colors: Uint8Array
|
|
19
|
+
opacity: number
|
|
7
20
|
positions: Float32Array
|
|
8
21
|
scale: number
|
|
9
22
|
}
|
|
10
23
|
|
|
11
|
-
let {
|
|
24
|
+
let { colors, opacity, positions, scale }: Props = $props()
|
|
12
25
|
|
|
13
|
-
const geometry = new SphereGeometry(
|
|
26
|
+
const geometry = new SphereGeometry(0.5, 16, 16)
|
|
14
27
|
const vertexCount = geometry.getAttribute('position').count
|
|
15
28
|
const indexCount = geometry.index?.count ?? vertexCount
|
|
16
29
|
const mesh = new BatchedMesh(5000, vertexCount, indexCount)
|
|
17
|
-
const matrix = new Matrix4()
|
|
18
|
-
const vec3 = new Vector3()
|
|
19
30
|
|
|
20
31
|
const geometryID = mesh.addGeometry(geometry)
|
|
21
32
|
|
|
33
|
+
const isPerDot = $derived(isVertexColors(colors))
|
|
34
|
+
|
|
35
|
+
const meshColor = $derived.by<[number, number, number]>(() => {
|
|
36
|
+
if (isPerDot) return [1, 1, 1]
|
|
37
|
+
if (!isSingleColor(colors)) return [0, 0, 0.55]
|
|
38
|
+
asRGB(colors, rgb)
|
|
39
|
+
return [rgb.r, rgb.g, rgb.b]
|
|
40
|
+
})
|
|
41
|
+
|
|
22
42
|
$effect(() => {
|
|
23
43
|
for (let i = 0, l = positions.length; i < l; i += 3) {
|
|
44
|
+
const dotIndex = i / 3
|
|
24
45
|
const instance = mesh.addInstance(geometryID)
|
|
25
46
|
matrix.makeTranslation(positions[i + 0], positions[i + 1], positions[i + 2])
|
|
26
47
|
matrix.scale(vec3.setScalar(scale))
|
|
27
48
|
mesh.setMatrixAt(instance, matrix)
|
|
49
|
+
|
|
50
|
+
if (isPerDot) {
|
|
51
|
+
asColor(colors, threeColor, dotIndex * STRIDE.COLORS_RGB)
|
|
52
|
+
mesh.setColorAt(instance, threeColor)
|
|
53
|
+
}
|
|
28
54
|
}
|
|
29
55
|
|
|
30
56
|
return () => {
|
|
31
|
-
for (let i = 0, l = positions.length; i < l; i += 1) {
|
|
57
|
+
for (let i = 0, l = positions.length / 3; i < l; i += 1) {
|
|
32
58
|
mesh.deleteInstance(i)
|
|
33
59
|
}
|
|
34
60
|
}
|
|
@@ -41,5 +67,9 @@
|
|
|
41
67
|
bvh={{ enabled: false }}
|
|
42
68
|
raycast={() => null}
|
|
43
69
|
>
|
|
44
|
-
<T.MeshBasicMaterial
|
|
70
|
+
<T.MeshBasicMaterial
|
|
71
|
+
color={meshColor}
|
|
72
|
+
transparent={opacity < 1}
|
|
73
|
+
{opacity}
|
|
74
|
+
/>
|
|
45
75
|
</T>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { untrack } from 'svelte'
|
|
4
4
|
import { LineGeometry } from 'three/examples/jsm/Addons.js'
|
|
5
5
|
|
|
6
|
-
let { positions } = $props()
|
|
6
|
+
let { positions, colors } = $props()
|
|
7
7
|
|
|
8
8
|
let geometry = $state.raw(new LineGeometry())
|
|
9
9
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
untrack(() => {
|
|
13
13
|
geometry = new LineGeometry()
|
|
14
14
|
geometry.setPositions(positions)
|
|
15
|
+
if (colors) geometry.setColors(colors)
|
|
15
16
|
})
|
|
16
17
|
}
|
|
17
18
|
})
|
|
@@ -5,8 +5,10 @@ type LineGeometry = {
|
|
|
5
5
|
};
|
|
6
6
|
declare const LineGeometry: import("svelte").Component<{
|
|
7
7
|
positions: any;
|
|
8
|
+
colors: any;
|
|
8
9
|
}, {}, "">;
|
|
9
10
|
import { LineGeometry } from 'three/examples/jsm/Addons.js';
|
|
10
11
|
type $$ComponentProps = {
|
|
11
12
|
positions: any;
|
|
13
|
+
colors: any;
|
|
12
14
|
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { type Snippet } from 'svelte'
|
|
7
7
|
import { BufferGeometry, Color, DoubleSide, FrontSide, Mesh } from 'three'
|
|
8
8
|
|
|
9
|
+
import { asColor } from '../../buffer'
|
|
9
10
|
import { colors, darkenColor } from '../../color'
|
|
10
11
|
import { traits, useTrait } from '../../ecs'
|
|
11
12
|
import { CapsuleGeometry } from '../../three/CapsuleGeometry'
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
|
|
27
28
|
const { invalidate } = useThrelte()
|
|
28
29
|
const name = useTrait(() => entity, traits.Name)
|
|
30
|
+
const entityColors = useTrait(() => entity, traits.Colors)
|
|
29
31
|
const entityColor = useTrait(() => entity, traits.Color)
|
|
30
32
|
const opacity = useTrait(() => entity, traits.Opacity)
|
|
31
33
|
const box = useTrait(() => entity, traits.Box)
|
|
@@ -41,6 +43,10 @@
|
|
|
41
43
|
return overrideColor
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
if (entityColors.current) {
|
|
47
|
+
return asColor(entityColors.current, colorUtil)
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
if (entityColor.current) {
|
|
45
51
|
return colorUtil.setRGB(entityColor.current.r, entityColor.current.g, entityColor.current.b)
|
|
46
52
|
}
|
|
@@ -48,6 +54,8 @@
|
|
|
48
54
|
return colors.default
|
|
49
55
|
})
|
|
50
56
|
|
|
57
|
+
const currentOpacity = $derived(opacity.current ?? 0.7)
|
|
58
|
+
|
|
51
59
|
const mesh = new Mesh()
|
|
52
60
|
|
|
53
61
|
$effect.pre(() => {
|
|
@@ -97,7 +105,6 @@
|
|
|
97
105
|
/>
|
|
98
106
|
{/if}
|
|
99
107
|
|
|
100
|
-
{@const currentOpacity = opacity.current ?? 0.7}
|
|
101
108
|
<T.MeshToonMaterial
|
|
102
109
|
{color}
|
|
103
110
|
side={bufferGeometry.current ? DoubleSide : FrontSide}
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
import { Portal } from '@threlte/extras'
|
|
7
7
|
import { OrthographicCamera, Points, PointsMaterial } from 'three'
|
|
8
8
|
|
|
9
|
+
import { asColor, isSingleColor } from '../../buffer'
|
|
9
10
|
import { traits, useTrait } from '../../ecs'
|
|
10
11
|
import { useSettings } from '../../hooks/useSettings.svelte'
|
|
11
12
|
import { poseToObject3d } from '../../transform'
|
|
12
13
|
|
|
14
|
+
import AxesHelper from '../AxesHelper.svelte'
|
|
13
15
|
import { useEntityEvents } from './hooks/useEntityEvents.svelte'
|
|
14
16
|
|
|
15
17
|
interface Props {
|
|
@@ -25,10 +27,12 @@
|
|
|
25
27
|
const parent = useTrait(() => entity, traits.Parent)
|
|
26
28
|
const pose = useTrait(() => entity, traits.Pose)
|
|
27
29
|
const geometry = useTrait(() => entity, traits.BufferGeometry)
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
+
const entityColor = useTrait(() => entity, traits.Color)
|
|
31
|
+
const colors = useTrait(() => entity, traits.Colors)
|
|
30
32
|
const entityPointSize = useTrait(() => entity, traits.PointSize)
|
|
33
|
+
const opacity = useTrait(() => entity, traits.Opacity)
|
|
31
34
|
const invisible = useTrait(() => entity, traits.Invisible)
|
|
35
|
+
const showAxesHelper = useTrait(() => entity, traits.ShowAxesHelper)
|
|
32
36
|
|
|
33
37
|
const pointSize = $derived(
|
|
34
38
|
entityPointSize.current ? entityPointSize.current * 0.001 : settings.current.pointSize
|
|
@@ -46,8 +50,11 @@
|
|
|
46
50
|
$effect.pre(() => {
|
|
47
51
|
if (geometry.current?.getAttribute('color')) {
|
|
48
52
|
material.color.set(0xffffff)
|
|
49
|
-
} else if (
|
|
50
|
-
|
|
53
|
+
} else if (entityColor.current) {
|
|
54
|
+
const { r, g, b } = entityColor.current
|
|
55
|
+
material.color.setRGB(r, g, b)
|
|
56
|
+
} else if (colors.current && isSingleColor(colors.current)) {
|
|
57
|
+
asColor(colors.current, material.color, 0)
|
|
51
58
|
} else {
|
|
52
59
|
material.color.set(settings.current.pointColor)
|
|
53
60
|
}
|
|
@@ -57,7 +64,7 @@
|
|
|
57
64
|
* Points transparancy is very costly for the GPU, so we turn it on conservatively
|
|
58
65
|
*/
|
|
59
66
|
$effect.pre(() => {
|
|
60
|
-
if (opacity.current && opacity.current < 1) {
|
|
67
|
+
if (opacity.current !== undefined && opacity.current < 1) {
|
|
61
68
|
material.transparent = true
|
|
62
69
|
material.opacity = opacity.current
|
|
63
70
|
|
|
@@ -99,23 +106,20 @@
|
|
|
99
106
|
|
|
100
107
|
const events = useEntityEvents(() => entity)
|
|
101
108
|
|
|
102
|
-
|
|
109
|
+
useTask(
|
|
103
110
|
() => {
|
|
104
111
|
// If using an orthographic camera, points need to be
|
|
105
112
|
// resized to half zoom to take up the same screen space.
|
|
106
113
|
material.size = pointSize * ((camera.current as OrthographicCamera).zoom / 2)
|
|
107
114
|
},
|
|
108
115
|
{
|
|
109
|
-
|
|
116
|
+
running: () => orthographic,
|
|
110
117
|
autoInvalidate: false,
|
|
111
118
|
}
|
|
112
119
|
)
|
|
113
120
|
|
|
114
121
|
$effect(() => {
|
|
115
|
-
if (orthographic) {
|
|
116
|
-
start()
|
|
117
|
-
} else {
|
|
118
|
-
stop()
|
|
122
|
+
if (!orthographic) {
|
|
119
123
|
material.size = pointSize
|
|
120
124
|
}
|
|
121
125
|
})
|
|
@@ -132,6 +136,13 @@
|
|
|
132
136
|
>
|
|
133
137
|
<T is={geometry.current} />
|
|
134
138
|
<T is={material} />
|
|
139
|
+
{#if showAxesHelper.current}
|
|
140
|
+
<AxesHelper
|
|
141
|
+
name={entity}
|
|
142
|
+
width={3}
|
|
143
|
+
length={0.1}
|
|
144
|
+
/>
|
|
145
|
+
{/if}
|
|
135
146
|
{@render children?.()}
|
|
136
147
|
</T>
|
|
137
148
|
</Portal>
|
|
@@ -9,10 +9,10 @@ export const useEntityEvents = (entity) => {
|
|
|
9
9
|
const selectedEntity = useSelectedEntity();
|
|
10
10
|
const focusedEntity = useFocusedEntity();
|
|
11
11
|
const cursor = useCursor();
|
|
12
|
-
const currentEntity = $derived(entity());
|
|
13
12
|
const onpointerenter = (event) => {
|
|
14
13
|
event.stopPropagation();
|
|
15
14
|
cursor.onPointerEnter();
|
|
15
|
+
const currentEntity = entity();
|
|
16
16
|
if (currentEntity && !currentEntity.has(traits.Hovered)) {
|
|
17
17
|
const hoverInfo = updateHoverInfo(currentEntity, event);
|
|
18
18
|
if (hoverInfo) {
|
|
@@ -32,7 +32,8 @@ export const useEntityEvents = (entity) => {
|
|
|
32
32
|
};
|
|
33
33
|
const onpointermove = (event) => {
|
|
34
34
|
event.stopPropagation();
|
|
35
|
-
|
|
35
|
+
const currentEntity = entity();
|
|
36
|
+
if (currentEntity?.has(traits.Hovered)) {
|
|
36
37
|
const hoverInfo = updateHoverInfo(currentEntity, event);
|
|
37
38
|
const hoverPose = createPose(hoverInfo
|
|
38
39
|
? {
|
|
@@ -67,6 +68,7 @@ export const useEntityEvents = (entity) => {
|
|
|
67
68
|
const onpointerleave = (event) => {
|
|
68
69
|
event.stopPropagation();
|
|
69
70
|
cursor.onPointerLeave();
|
|
71
|
+
const currentEntity = entity();
|
|
70
72
|
if (currentEntity?.has(traits.Hovered)) {
|
|
71
73
|
currentEntity.remove(traits.Hovered);
|
|
72
74
|
}
|
|
@@ -76,6 +78,7 @@ export const useEntityEvents = (entity) => {
|
|
|
76
78
|
};
|
|
77
79
|
const ondblclick = (event) => {
|
|
78
80
|
event.stopPropagation();
|
|
81
|
+
const currentEntity = entity();
|
|
79
82
|
focusedEntity.set(currentEntity, event.instanceId ?? event.batchId);
|
|
80
83
|
};
|
|
81
84
|
const onpointerdown = (event) => {
|
|
@@ -84,6 +87,7 @@ export const useEntityEvents = (entity) => {
|
|
|
84
87
|
const onclick = (event) => {
|
|
85
88
|
event.stopPropagation();
|
|
86
89
|
if (down.distanceToSquared(event.pointer) < 0.1) {
|
|
90
|
+
const currentEntity = entity();
|
|
87
91
|
selectedEntity.set(currentEntity, event.instanceId ?? event.batchId);
|
|
88
92
|
}
|
|
89
93
|
};
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { ToastVariant, useToast } from '@viamrobotics/prime-core'
|
|
5
5
|
|
|
6
6
|
import { createBufferGeometry } from '../../attribute'
|
|
7
|
+
import { ColorFormat } from '../../buf/draw/v1/metadata_pb'
|
|
7
8
|
import { traits } from '../../ecs'
|
|
8
9
|
import { useWorld } from '../../ecs/useWorld'
|
|
9
10
|
import { useCameraControls } from '../../hooks/useControls.svelte'
|
|
@@ -40,7 +41,10 @@
|
|
|
40
41
|
break
|
|
41
42
|
}
|
|
42
43
|
case 'pcd': {
|
|
43
|
-
const geometry = createBufferGeometry(result.pcd.positions,
|
|
44
|
+
const geometry = createBufferGeometry(result.pcd.positions, {
|
|
45
|
+
colors: result.pcd.colors,
|
|
46
|
+
colorFormat: ColorFormat.RGB,
|
|
47
|
+
})
|
|
44
48
|
|
|
45
49
|
world.spawn(
|
|
46
50
|
traits.Name(result.name),
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
return FALLBACK_SPEED
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
useTask(
|
|
69
69
|
(delta) => {
|
|
70
70
|
const dt = delta * 1000
|
|
71
71
|
|
|
@@ -129,19 +129,11 @@
|
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
|
-
|
|
132
|
+
running: () => anyKeysPressed,
|
|
133
133
|
autoInvalidate: false,
|
|
134
134
|
}
|
|
135
135
|
)
|
|
136
136
|
|
|
137
|
-
$effect.pre(() => {
|
|
138
|
-
if (anyKeysPressed) {
|
|
139
|
-
start()
|
|
140
|
-
} else {
|
|
141
|
-
stop()
|
|
142
|
-
}
|
|
143
|
-
})
|
|
144
|
-
|
|
145
137
|
keys.onKeys('escape', () => {
|
|
146
138
|
if (keys.has('escape')) {
|
|
147
139
|
focusedEntity.set()
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ConfigurableTrait, Entity } from 'koota'
|
|
3
3
|
|
|
4
|
+
import type { InteractionLayerValue } from '../ecs/traits'
|
|
5
|
+
|
|
4
6
|
import { createBufferGeometry } from '../attribute'
|
|
7
|
+
import { ColorFormat } from '../buf/draw/v1/metadata_pb'
|
|
5
8
|
import { traits, useWorld } from '../ecs'
|
|
6
9
|
import { parsePcdInWorker } from '../lib'
|
|
7
10
|
|
|
@@ -9,10 +12,11 @@
|
|
|
9
12
|
data: Uint8Array
|
|
10
13
|
name?: string
|
|
11
14
|
renderOrder?: number
|
|
12
|
-
|
|
15
|
+
interactionLayers?: InteractionLayerValue[]
|
|
16
|
+
oncreate?: (positions: Float32Array, colors: Uint8Array | undefined) => void
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
let { data, name, renderOrder, oncreate }: Props = $props()
|
|
19
|
+
let { data, name, renderOrder, interactionLayers, oncreate }: Props = $props()
|
|
16
20
|
|
|
17
21
|
const world = useWorld()
|
|
18
22
|
|
|
@@ -20,7 +24,7 @@
|
|
|
20
24
|
|
|
21
25
|
$effect(() => {
|
|
22
26
|
parsePcdInWorker(data).then(({ positions, colors }) => {
|
|
23
|
-
const geometry = createBufferGeometry(positions, colors)
|
|
27
|
+
const geometry = createBufferGeometry(positions, { colors, colorFormat: ColorFormat.RGB })
|
|
24
28
|
|
|
25
29
|
const entityTraits: ConfigurableTrait[] = [
|
|
26
30
|
traits.Name(name ?? 'Random points'),
|
|
@@ -31,10 +35,13 @@
|
|
|
31
35
|
if (renderOrder) {
|
|
32
36
|
entityTraits.push(traits.RenderOrder(renderOrder))
|
|
33
37
|
}
|
|
38
|
+
if (interactionLayers?.includes('selectTool')) {
|
|
39
|
+
entityTraits.push(traits.SelectToolInteractionLayer)
|
|
40
|
+
}
|
|
34
41
|
|
|
35
42
|
entity = world.spawn(...entityTraits)
|
|
36
43
|
|
|
37
|
-
oncreate?.(positions, colors)
|
|
44
|
+
oncreate?.(positions, colors ?? undefined)
|
|
38
45
|
})
|
|
39
46
|
|
|
40
47
|
return () => {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { InteractionLayerValue } from '../ecs/traits';
|
|
1
2
|
interface Props {
|
|
2
3
|
data: Uint8Array;
|
|
3
4
|
name?: string;
|
|
4
5
|
renderOrder?: number;
|
|
5
|
-
|
|
6
|
+
interactionLayers?: InteractionLayerValue[];
|
|
7
|
+
oncreate?: (positions: Float32Array, colors: Uint8Array | undefined) => void;
|
|
6
8
|
}
|
|
7
9
|
declare const PCD: import("svelte").Component<Props, {}, "">;
|
|
8
10
|
type PCD = ReturnType<typeof PCD>;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
provideTransformControls,
|
|
12
12
|
} from '../hooks/useControls.svelte'
|
|
13
13
|
import { provideDrawAPI } from '../hooks/useDrawAPI.svelte'
|
|
14
|
+
import { provideDrawService } from '../hooks/useDrawService.svelte'
|
|
14
15
|
import { provideFramelessComponents } from '../hooks/useFramelessComponents.svelte'
|
|
15
16
|
import { provideFrames } from '../hooks/useFrames.svelte'
|
|
16
17
|
import { provideGeometries } from '../hooks/useGeometries.svelte'
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
|
|
41
42
|
provideOrigin()
|
|
42
43
|
provideDrawAPI()
|
|
44
|
+
provideDrawService()
|
|
43
45
|
|
|
44
46
|
provideResourceByName(() => partID.current)
|
|
45
47
|
provideConfigFrames()
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
const object = $derived(selectedObject3d.current)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
useTask(
|
|
22
22
|
() => {
|
|
23
23
|
if (object === undefined) {
|
|
24
24
|
return
|
|
@@ -39,20 +39,10 @@
|
|
|
39
39
|
invalidate()
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
|
-
|
|
42
|
+
running: () => selectedEntity.current !== undefined,
|
|
43
43
|
autoInvalidate: false,
|
|
44
44
|
}
|
|
45
45
|
)
|
|
46
|
-
|
|
47
|
-
$effect.pre(() => {
|
|
48
|
-
if (selectedEntity.current) {
|
|
49
|
-
start()
|
|
50
|
-
} else {
|
|
51
|
-
stop()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
invalidate()
|
|
55
|
-
})
|
|
56
46
|
</script>
|
|
57
47
|
|
|
58
48
|
{#if selectedEntity.current}
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
world.spawn(
|
|
46
46
|
traits.LinePositions(new Float32Array([x, y, 0])),
|
|
47
47
|
traits.LineWidth(1.5),
|
|
48
|
+
traits.ScreenSpace,
|
|
48
49
|
traits.RenderOrder(999),
|
|
49
50
|
traits.Material({ depthTest: false }),
|
|
50
51
|
traits.Color({ r: 1, g: 0, b: 0 }),
|
|
@@ -152,6 +153,7 @@
|
|
|
152
153
|
|
|
153
154
|
for (const pointsEntity of world.query(
|
|
154
155
|
traits.Points,
|
|
156
|
+
traits.SelectToolInteractionLayer,
|
|
155
157
|
Not(selectionTraits.SelectionEnclosedPoints)
|
|
156
158
|
)) {
|
|
157
159
|
const geometry = pointsEntity.get(traits.BufferGeometry)
|
|
@@ -1,67 +1,35 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
3
|
|
|
4
4
|
import { useThrelte } from '@threlte/core'
|
|
5
5
|
import { Portal } from '@threlte/extras'
|
|
6
|
-
import { Button } from '@viamrobotics/prime-core'
|
|
7
6
|
import { ElementRect } from 'runed'
|
|
8
|
-
import { BufferGeometryUtils } from 'three/examples/jsm/Addons.js'
|
|
9
7
|
|
|
10
8
|
import DashboardButton from '../overlay/dashboard/Button.svelte'
|
|
11
|
-
import { traits, useWorld } from '../../ecs'
|
|
12
9
|
import { useSettings } from '../../hooks/useSettings.svelte'
|
|
13
|
-
import { createBinaryPCD } from '../../pcd'
|
|
14
10
|
|
|
15
|
-
import FloatingPanel from '../overlay/FloatingPanel.svelte'
|
|
16
11
|
import Popover from '../overlay/Popover.svelte'
|
|
17
12
|
import ToggleGroup from '../overlay/ToggleGroup.svelte'
|
|
18
13
|
import Ellipse from './Ellipse.svelte'
|
|
19
14
|
import Lasso from './Lasso.svelte'
|
|
20
|
-
import
|
|
15
|
+
import { provideSelectionPlugin } from './useSelectionPlugin.svelte'
|
|
21
16
|
|
|
22
17
|
interface Props {
|
|
23
18
|
/** Whether to auto-enable lasso mode when the component mounts */
|
|
24
19
|
enabled?: boolean
|
|
25
|
-
|
|
26
|
-
/** Fires when the user has committed to a lasso selection */
|
|
27
|
-
onSelection: (pcd: Blob) => void
|
|
20
|
+
children?: Snippet
|
|
28
21
|
}
|
|
29
22
|
|
|
30
23
|
type SelectionType = 'lasso' | 'ellipse'
|
|
31
24
|
|
|
32
|
-
let { enabled = false,
|
|
25
|
+
let { enabled = false, children }: Props = $props()
|
|
33
26
|
|
|
34
27
|
const { dom } = useThrelte()
|
|
35
|
-
const world = useWorld()
|
|
36
28
|
const settings = useSettings()
|
|
37
29
|
const isSelectionMode = $derived(settings.current.interactionMode === 'select')
|
|
38
|
-
let selectionType = $state<SelectionType>('lasso')
|
|
39
|
-
|
|
40
|
-
const onCommitClick = () => {
|
|
41
|
-
const entities = world.query(selectionTraits.SelectionEnclosedPoints)
|
|
42
|
-
|
|
43
|
-
const geometries: BufferGeometry[] = []
|
|
44
|
-
for (const entity of entities) {
|
|
45
|
-
const geometry = entity.get(traits.BufferGeometry)
|
|
46
|
-
|
|
47
|
-
if (geometry) {
|
|
48
|
-
geometries.push(geometry)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const mergedGeometry = BufferGeometryUtils.mergeGeometries(geometries)
|
|
53
|
-
const positions = mergedGeometry.getAttribute('position').array as Float32Array
|
|
54
30
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
onSelection(pcd)
|
|
58
|
-
|
|
59
|
-
for (const entity of entities) {
|
|
60
|
-
if (world.has(entity)) {
|
|
61
|
-
entity.destroy()
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
31
|
+
provideSelectionPlugin()
|
|
32
|
+
let selectionType = $state<SelectionType>('lasso')
|
|
65
33
|
|
|
66
34
|
$effect(() => {
|
|
67
35
|
if (isSelectionMode) {
|
|
@@ -122,22 +90,5 @@
|
|
|
122
90
|
{#if isSelectionMode && rect.height > 0 && rect.width > 0}
|
|
123
91
|
<Ellipse active={selectionType === 'ellipse'} />
|
|
124
92
|
<Lasso active={selectionType === 'lasso'} />
|
|
125
|
-
|
|
126
|
-
<Portal id="dom">
|
|
127
|
-
<FloatingPanel
|
|
128
|
-
isOpen
|
|
129
|
-
exitable={false}
|
|
130
|
-
title="Lasso"
|
|
131
|
-
defaultSize={{ width: 445, height: 100 }}
|
|
132
|
-
defaultPosition={{ x: rect.width / 2 - 200, y: rect.height - 10 - 100 }}
|
|
133
|
-
>
|
|
134
|
-
<div class="flex items-center gap-4 p-4 text-xs">
|
|
135
|
-
Shift + click and drag to make a lasso selection.
|
|
136
|
-
<Button
|
|
137
|
-
onclick={onCommitClick}
|
|
138
|
-
variant="success">Commit selection</Button
|
|
139
|
-
>
|
|
140
|
-
</div>
|
|
141
|
-
</FloatingPanel>
|
|
142
|
-
</Portal>
|
|
93
|
+
{@render children?.()}
|
|
143
94
|
{/if}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
interface Props {
|
|
2
3
|
/** Whether to auto-enable lasso mode when the component mounts */
|
|
3
4
|
enabled?: boolean;
|
|
4
|
-
|
|
5
|
-
onSelection: (pcd: Blob) => void;
|
|
5
|
+
children?: Snippet;
|
|
6
6
|
}
|
|
7
7
|
declare const Tool: import("svelte").Component<Props, {}, "">;
|
|
8
8
|
type Tool = ReturnType<typeof Tool>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { QueryResult, Trait } from 'koota';
|
|
2
|
+
interface SelectionPluginContext {
|
|
3
|
+
current: QueryResult<[Trait<() => boolean>]>;
|
|
4
|
+
clearSelections: () => void;
|
|
5
|
+
}
|
|
6
|
+
export declare const provideSelectionPlugin: () => SelectionPluginContext;
|
|
7
|
+
export declare const useSelectionPlugin: () => SelectionPluginContext;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
import { useQuery, useWorld } from '../../ecs';
|
|
3
|
+
import * as selectionTraits from './traits';
|
|
4
|
+
const key = Symbol('selection-plugin-context');
|
|
5
|
+
export const provideSelectionPlugin = () => {
|
|
6
|
+
const world = useWorld();
|
|
7
|
+
const entities = useQuery(selectionTraits.SelectionEnclosedPoints);
|
|
8
|
+
const ctx = setContext(key, {
|
|
9
|
+
get current() {
|
|
10
|
+
return entities.current;
|
|
11
|
+
},
|
|
12
|
+
clearSelections() {
|
|
13
|
+
for (const entity of world.query(selectionTraits.SelectionEnclosedPoints)) {
|
|
14
|
+
if (world.has(entity)) {
|
|
15
|
+
entity.destroy();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
return ctx;
|
|
21
|
+
};
|
|
22
|
+
export const useSelectionPlugin = () => {
|
|
23
|
+
return getContext(key);
|
|
24
|
+
};
|
|
@@ -24,7 +24,7 @@ Renders a Snapshot protobuf by spawning its transforms and drawings as entities
|
|
|
24
24
|
import { useWorld } from '../ecs'
|
|
25
25
|
import { useCameraControls } from '../hooks/useControls.svelte'
|
|
26
26
|
import { useSettings } from '../hooks/useSettings.svelte'
|
|
27
|
-
import { applySceneMetadata,
|
|
27
|
+
import { applySceneMetadata, spawnSnapshotEntities } from '../snapshot'
|
|
28
28
|
|
|
29
29
|
interface Props {
|
|
30
30
|
snapshot: SnapshotProto
|
|
@@ -78,6 +78,8 @@ Renders a Snapshot protobuf by spawning its transforms and drawings as entities
|
|
|
78
78
|
})
|
|
79
79
|
|
|
80
80
|
onDestroy(() => {
|
|
81
|
-
|
|
81
|
+
for (const entity of entities) {
|
|
82
|
+
if (world.has(entity)) entity.destroy()
|
|
83
|
+
}
|
|
82
84
|
})
|
|
83
85
|
</script>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Entity } from 'koota'
|
|
3
|
-
|
|
4
2
|
import { Button, Input, Select } from '@viamrobotics/prime-core'
|
|
3
|
+
import { type Entity } from 'koota'
|
|
5
4
|
|
|
6
5
|
import { relations, traits, useQuery, useTrait } from '../../ecs'
|
|
7
6
|
import { SubEntityLinkType } from '../../ecs/relations'
|