@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.
Files changed (77) hide show
  1. package/dist/attribute.d.ts +3 -2
  2. package/dist/attribute.js +24 -16
  3. package/dist/buf/draw/v1/drawing_pb.d.ts +33 -16
  4. package/dist/buf/draw/v1/drawing_pb.js +35 -17
  5. package/dist/buf/draw/v1/metadata_pb.d.ts +44 -3
  6. package/dist/buf/draw/v1/metadata_pb.js +54 -3
  7. package/dist/buf/draw/v1/scene_pb.d.ts +6 -6
  8. package/dist/buf/draw/v1/scene_pb.js +7 -7
  9. package/dist/buffer.d.ts +54 -45
  10. package/dist/buffer.js +91 -57
  11. package/dist/color.d.ts +1 -2
  12. package/dist/color.js +5 -12
  13. package/dist/components/App.svelte +18 -3
  14. package/dist/components/App.svelte.d.ts +15 -2
  15. package/dist/components/Entities/Arrows/ArrowGroups.svelte +5 -6
  16. package/dist/components/Entities/Arrows/Arrows.svelte +9 -0
  17. package/dist/components/Entities/Entities.svelte +18 -1
  18. package/dist/components/Entities/Frame.svelte +7 -1
  19. package/dist/components/Entities/GLTF.svelte +13 -2
  20. package/dist/components/Entities/Line.svelte +46 -18
  21. package/dist/components/Entities/LineDots.svelte +38 -8
  22. package/dist/components/Entities/LineDots.svelte.d.ts +2 -2
  23. package/dist/components/Entities/LineGeometry.svelte +2 -1
  24. package/dist/components/Entities/LineGeometry.svelte.d.ts +2 -0
  25. package/dist/components/Entities/Mesh.svelte +8 -1
  26. package/dist/components/Entities/Points.svelte +22 -11
  27. package/dist/components/Entities/hooks/useEntityEvents.svelte.js +6 -2
  28. package/dist/components/FileDrop/FileDrop.svelte +5 -1
  29. package/dist/components/KeyboardControls.svelte +2 -10
  30. package/dist/components/PCD.svelte +11 -4
  31. package/dist/components/PCD.svelte.d.ts +3 -1
  32. package/dist/components/SceneProviders.svelte +2 -0
  33. package/dist/components/Selected.svelte +2 -12
  34. package/dist/components/Selection/Ellipse.svelte +1 -0
  35. package/dist/components/Selection/Lasso.svelte +2 -0
  36. package/dist/components/Selection/Tool.svelte +7 -56
  37. package/dist/components/Selection/Tool.svelte.d.ts +2 -2
  38. package/dist/components/Selection/useSelectionPlugin.svelte.d.ts +8 -0
  39. package/dist/components/Selection/useSelectionPlugin.svelte.js +24 -0
  40. package/dist/components/Snapshot.svelte +4 -2
  41. package/dist/components/overlay/AddRelationship.svelte +1 -2
  42. package/dist/components/overlay/AddRelationship.svelte.d.ts +1 -1
  43. package/dist/components/overlay/Details.svelte +12 -12
  44. package/dist/components/overlay/Details.svelte.d.ts +8 -1
  45. package/dist/components/overlay/settings/Settings.svelte +8 -1
  46. package/dist/components/xr/XR.svelte +1 -1
  47. package/dist/draw.d.ts +13 -0
  48. package/dist/draw.js +428 -0
  49. package/dist/ecs/traits.d.ts +31 -13
  50. package/dist/ecs/traits.js +25 -8
  51. package/dist/geometry.js +3 -0
  52. package/dist/hooks/useDrawAPI.svelte.js +61 -24
  53. package/dist/hooks/useDrawService.svelte.d.ts +12 -0
  54. package/dist/hooks/useDrawService.svelte.js +240 -0
  55. package/dist/hooks/usePointcloudObjects.svelte.js +7 -2
  56. package/dist/hooks/usePointclouds.svelte.js +7 -2
  57. package/dist/hooks/useSettings.svelte.d.ts +2 -1
  58. package/dist/hooks/useSettings.svelte.js +1 -1
  59. package/dist/hooks/useWorldState.svelte.js +5 -52
  60. package/dist/index.d.ts +8 -0
  61. package/dist/index.js +9 -0
  62. package/dist/lib.d.ts +2 -0
  63. package/dist/lib.js +2 -0
  64. package/dist/loaders/pcd/index.d.ts +1 -1
  65. package/dist/loaders/pcd/messages.d.ts +2 -2
  66. package/dist/loaders/pcd/worker.inline.d.ts +1 -1
  67. package/dist/loaders/pcd/worker.inline.js +229 -187
  68. package/dist/loaders/pcd/worker.js +2 -2
  69. package/dist/metadata.d.ts +9 -15
  70. package/dist/metadata.js +45 -9
  71. package/dist/plugins/bvh.svelte.js +6 -2
  72. package/dist/snapshot.d.ts +3 -9
  73. package/dist/snapshot.js +11 -204
  74. package/dist/three/InstancedArrows/InstancedArrows.js +3 -2
  75. package/package.json +14 -11
  76. package/dist/components/xr/Hands.svelte +0 -23
  77. 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, type ColorRepresentation, Matrix4, SphereGeometry, Vector3 } from 'three'
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
- color: ColorRepresentation
18
+ colors: Uint8Array
19
+ opacity: number
7
20
  positions: Float32Array
8
21
  scale: number
9
22
  }
10
23
 
11
- let { color, positions, scale }: Props = $props()
24
+ let { colors, opacity, positions, scale }: Props = $props()
12
25
 
13
- const geometry = new SphereGeometry(1, 16, 16)
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 {color} />
70
+ <T.MeshBasicMaterial
71
+ color={meshColor}
72
+ transparent={opacity < 1}
73
+ {opacity}
74
+ />
45
75
  </T>
@@ -1,6 +1,6 @@
1
- import { type ColorRepresentation } from 'three';
2
1
  interface Props {
3
- color: ColorRepresentation;
2
+ colors: Uint8Array;
3
+ opacity: number;
4
4
  positions: Float32Array;
5
5
  scale: number;
6
6
  }
@@ -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 color = useTrait(() => entity, traits.Color)
29
- const opacity = useTrait(() => entity, traits.Opacity)
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 (color.current) {
50
- material.color.setRGB(color.current.r, color.current.g, color.current.b)
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
- const { start, stop } = useTask(
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
- autoStart: false,
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
- if (currentEntity && currentEntity.has(traits.Hovered)) {
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, result.pcd.colors)
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
- const { start, stop } = useTask(
68
+ useTask(
69
69
  (delta) => {
70
70
  const dt = delta * 1000
71
71
 
@@ -129,19 +129,11 @@
129
129
  }
130
130
  },
131
131
  {
132
- autoStart: false,
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
- oncreate?: (positions: Float32Array, colors: Uint8Array | null) => void
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
- oncreate?: (positions: Float32Array, colors: Uint8Array | null) => void;
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
- const { start, stop } = useTask(
21
+ useTask(
22
22
  () => {
23
23
  if (object === undefined) {
24
24
  return
@@ -39,20 +39,10 @@
39
39
  invalidate()
40
40
  },
41
41
  {
42
- autoStart: false,
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}
@@ -171,6 +171,7 @@
171
171
 
172
172
  for (const pointsEntity of world.query(
173
173
  traits.Points,
174
+ traits.SelectToolInteractionLayer,
174
175
  Not(selectionTraits.SelectionEnclosedPoints)
175
176
  )) {
176
177
  const geometry = pointsEntity.get(traits.BufferGeometry)
@@ -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 { BufferGeometry } from 'three'
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 * as selectionTraits from './traits'
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, onSelection }: Props = $props()
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
- const pcd = createBinaryPCD(positions)
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
- /** Fires when the user has committed to a lasso selection */
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, destroyEntities, spawnSnapshotEntities } from '../snapshot'
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
- destroyEntities(world, entities)
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'
@@ -1,4 +1,4 @@
1
- import type { Entity } from 'koota';
1
+ import { type Entity } from 'koota';
2
2
  interface Props {
3
3
  entity: Entity | undefined;
4
4
  }