@viamrobotics/motion-tools 1.19.0 → 1.21.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 (33) hide show
  1. package/dist/buf/draw/v1/metadata_pb.d.ts +39 -0
  2. package/dist/buf/draw/v1/metadata_pb.js +55 -0
  3. package/dist/buf/draw/v1/service_connect.d.ts +34 -1
  4. package/dist/buf/draw/v1/service_connect.js +34 -1
  5. package/dist/buf/draw/v1/service_pb.d.ts +136 -0
  6. package/dist/buf/draw/v1/service_pb.js +201 -0
  7. package/dist/components/Entities/Arrows/ArrowGroups.svelte +1 -0
  8. package/dist/components/Entities/Arrows/Arrows.svelte +1 -1
  9. package/dist/components/Entities/Entities.svelte +1 -0
  10. package/dist/components/Entities/Points.svelte +23 -23
  11. package/dist/components/Entities/hooks/useEntityEvents.svelte.js +18 -1
  12. package/dist/components/FileDrop/FileDrop.svelte +8 -1
  13. package/dist/components/PCD.svelte +9 -1
  14. package/dist/components/PCD.svelte.d.ts +2 -0
  15. package/dist/components/SceneProviders.svelte +2 -0
  16. package/dist/components/Snapshot.svelte +12 -7
  17. package/dist/components/overlay/AddRelationship.svelte +25 -3
  18. package/dist/components/overlay/Details.svelte +293 -227
  19. package/dist/draw.d.ts +22 -9
  20. package/dist/draw.js +75 -46
  21. package/dist/ecs/relations.js +1 -1
  22. package/dist/ecs/traits.d.ts +2 -0
  23. package/dist/ecs/traits.js +63 -0
  24. package/dist/hooks/useDrawService.svelte.d.ts +2 -0
  25. package/dist/hooks/useDrawService.svelte.js +139 -20
  26. package/dist/hooks/useRelationships.svelte.d.ts +12 -0
  27. package/dist/hooks/useRelationships.svelte.js +78 -0
  28. package/dist/hooks/useWorldState.svelte.js +10 -4
  29. package/dist/metadata.d.ts +7 -3
  30. package/dist/metadata.js +26 -2
  31. package/dist/snapshot.d.ts +6 -1
  32. package/dist/snapshot.js +10 -5
  33. package/package.json +5 -2
@@ -33,6 +33,8 @@
33
33
  const opacity = useTrait(() => entity, traits.Opacity)
34
34
  const invisible = useTrait(() => entity, traits.Invisible)
35
35
  const showAxesHelper = useTrait(() => entity, traits.ShowAxesHelper)
36
+ const renderOrder = useTrait(() => entity, traits.RenderOrder)
37
+ const materialProps = useTrait(() => entity, traits.Material)
36
38
 
37
39
  const pointSize = $derived(
38
40
  entityPointSize.current ? entityPointSize.current * 0.001 : settings.current.pointSize
@@ -61,41 +63,38 @@
61
63
  })
62
64
 
63
65
  /**
64
- * Points transparancy is very costly for the GPU, so we turn it on conservatively
66
+ * Points transparency is very costly for the GPU, so we turn it on conservatively.
67
+ * Uniform opacity (entity trait) and per-vertex RGBA alpha are both considered here
68
+ * to avoid the two sources conflicting with each other.
65
69
  */
66
70
  $effect.pre(() => {
67
- if (opacity.current !== undefined && opacity.current < 1) {
68
- material.transparent = true
69
- material.opacity = opacity.current
70
-
71
- return () => {
72
- material.transparent = false
73
- material.opacity = 1
74
- }
75
- }
76
- })
77
-
78
- $effect.pre(() => {
79
- const colors = geometry.current?.getAttribute('color')
71
+ const vertexColors = geometry.current?.getAttribute('color')
80
72
  const positions = geometry.current?.getAttribute('position')
81
73
 
82
- material.vertexColors = colors !== undefined
74
+ material.vertexColors = vertexColors !== undefined
83
75
 
84
- if (colors && positions) {
85
- const hasAlphaChannel = positions.array.length / colors.array.length === 0.75
76
+ const hasUniformOpacity = opacity.current !== undefined && opacity.current < 1
77
+ material.opacity = hasUniformOpacity ? opacity.current! : 1
86
78
 
87
- let transparent = false
79
+ let hasVertexAlpha = false
80
+ if (vertexColors && positions) {
81
+ const hasAlphaChannel = positions.array.length / vertexColors.array.length === 0.75
88
82
  if (hasAlphaChannel) {
89
- for (let i = 3, l = colors.array.length; i < l; i += 4) {
90
- if (colors.array[i] < 1) {
91
- transparent = true
83
+ for (let i = 3, l = vertexColors.array.length; i < l; i += 4) {
84
+ if (vertexColors.array[i] < 1) {
85
+ hasVertexAlpha = true
92
86
  break
93
87
  }
94
88
  }
95
89
  }
96
-
97
- material.transparent = transparent
98
90
  }
91
+
92
+ material.transparent = hasUniformOpacity || hasVertexAlpha
93
+ })
94
+
95
+ $effect.pre(() => {
96
+ material.depthTest = materialProps.current?.depthTest ?? true
97
+ material.depthWrite = materialProps.current?.depthWrite ?? true
99
98
  })
100
99
 
101
100
  $effect.pre(() => {
@@ -132,6 +131,7 @@
132
131
  name={entity}
133
132
  bvh={{ maxDepth: 40, maxLeafSize: 20 }}
134
133
  visible={invisible.current !== true}
134
+ renderOrder={renderOrder.current}
135
135
  {...events}
136
136
  >
137
137
  <T is={geometry.current} />
@@ -9,7 +9,10 @@ export const useEntityEvents = (entity) => {
9
9
  const selectedEntity = useSelectedEntity();
10
10
  const focusedEntity = useFocusedEntity();
11
11
  const cursor = useCursor();
12
+ const invisible = useTrait(entity, traits.Invisible);
12
13
  const onpointerenter = (event) => {
14
+ if (invisible.current)
15
+ return;
13
16
  event.stopPropagation();
14
17
  cursor.onPointerEnter();
15
18
  const currentEntity = entity();
@@ -31,6 +34,8 @@ export const useEntityEvents = (entity) => {
31
34
  }
32
35
  };
33
36
  const onpointermove = (event) => {
37
+ if (invisible.current)
38
+ return;
34
39
  event.stopPropagation();
35
40
  const currentEntity = entity();
36
41
  if (currentEntity?.has(traits.Hovered)) {
@@ -77,24 +82,36 @@ export const useEntityEvents = (entity) => {
77
82
  }
78
83
  };
79
84
  const ondblclick = (event) => {
85
+ if (invisible.current)
86
+ return;
80
87
  event.stopPropagation();
81
88
  const currentEntity = entity();
82
89
  focusedEntity.set(currentEntity, event.instanceId ?? event.batchId);
83
90
  };
84
91
  const onpointerdown = (event) => {
92
+ if (invisible.current)
93
+ return;
85
94
  down.copy(event.pointer);
86
95
  };
87
96
  const onclick = (event) => {
97
+ if (invisible.current)
98
+ return;
88
99
  event.stopPropagation();
89
100
  if (down.distanceToSquared(event.pointer) < 0.1) {
90
101
  const currentEntity = entity();
91
102
  selectedEntity.set(currentEntity, event.instanceId ?? event.batchId);
92
103
  }
93
104
  };
94
- const invisible = useTrait(entity, traits.Invisible);
95
105
  $effect(() => {
96
106
  if (invisible.current) {
97
107
  cursor.onPointerLeave();
108
+ const currentEntity = entity();
109
+ if (currentEntity?.has(traits.Hovered)) {
110
+ currentEntity.remove(traits.Hovered);
111
+ }
112
+ if (currentEntity?.has(traits.InstancedPose)) {
113
+ currentEntity.remove(traits.InstancedPose);
114
+ }
98
115
  }
99
116
  });
100
117
  return {
@@ -8,6 +8,7 @@
8
8
  import { traits } from '../../ecs'
9
9
  import { useWorld } from '../../ecs/useWorld'
10
10
  import { useCameraControls } from '../../hooks/useControls.svelte'
11
+ import { useRelationships } from '../../hooks/useRelationships.svelte'
11
12
  import { spawnSnapshotEntities } from '../../snapshot'
12
13
 
13
14
  import type { FileDropperSuccess } from './file-dropper'
@@ -19,12 +20,18 @@
19
20
  const world = useWorld()
20
21
  const toast = useToast()
21
22
  const cameraControls = useCameraControls()
23
+ const relationships = useRelationships()
22
24
 
23
25
  const fileDrop = useFileDrop(
24
26
  (result: FileDropperSuccess) => {
25
27
  switch (result.type) {
26
28
  case 'snapshot': {
27
- spawnSnapshotEntities(world, result.snapshot)
29
+ const spawned = spawnSnapshotEntities(world, result.snapshot)
30
+ for (const entity of spawned) {
31
+ relationships.apply(entity.entity, entity.relationships)
32
+ const uuid = entity.entity.get(traits.UUID)
33
+ if (uuid) relationships.flush(uuid)
34
+ }
28
35
 
29
36
  const { sceneCamera } = result.snapshot.sceneMetadata ?? {}
30
37
 
@@ -12,11 +12,14 @@
12
12
  data: Uint8Array
13
13
  name?: string
14
14
  renderOrder?: number
15
+ depthTest?: boolean
16
+ depthWrite?: boolean
15
17
  interactionLayers?: InteractionLayerValue[]
16
18
  oncreate?: (positions: Float32Array, colors: Uint8Array | undefined) => void
17
19
  }
18
20
 
19
- let { data, name, renderOrder, interactionLayers, oncreate }: Props = $props()
21
+ let { data, name, renderOrder, depthTest, depthWrite, interactionLayers, oncreate }: Props =
22
+ $props()
20
23
 
21
24
  const world = useWorld()
22
25
 
@@ -38,6 +41,11 @@
38
41
  if (renderOrder) {
39
42
  entityTraits.push(traits.RenderOrder(renderOrder))
40
43
  }
44
+ if (depthTest !== undefined || depthWrite !== undefined) {
45
+ entityTraits.push(
46
+ traits.Material({ depthTest: depthTest ?? true, depthWrite: depthWrite ?? true })
47
+ )
48
+ }
41
49
  if (interactionLayers?.includes('selectTool')) {
42
50
  entityTraits.push(traits.SelectToolInteractionLayer)
43
51
  }
@@ -3,6 +3,8 @@ interface Props {
3
3
  data: Uint8Array;
4
4
  name?: string;
5
5
  renderOrder?: number;
6
+ depthTest?: boolean;
7
+ depthWrite?: boolean;
6
8
  interactionLayers?: InteractionLayerValue[];
7
9
  oncreate?: (positions: Float32Array, colors: Uint8Array | undefined) => void;
8
10
  }
@@ -20,6 +20,7 @@
20
20
  import { usePartID } from '../hooks/usePartID.svelte'
21
21
  import { providePointcloudObjects } from '../hooks/usePointcloudObjects.svelte'
22
22
  import { providePointclouds } from '../hooks/usePointclouds.svelte'
23
+ import { provideRelationships } from '../hooks/useRelationships.svelte'
23
24
  import { provideResourceByName } from '../hooks/useResourceByName.svelte'
24
25
  import { provideSelection } from '../hooks/useSelection.svelte'
25
26
  import { provideWorldStates } from '../hooks/useWorldState.svelte'
@@ -41,6 +42,7 @@
41
42
 
42
43
  provideOrigin()
43
44
  provideDrawAPI()
45
+ provideRelationships()
44
46
  provideDrawService()
45
47
 
46
48
  provideResourceByName(() => partID.current)
@@ -14,17 +14,16 @@ Renders a Snapshot protobuf by spawning its transforms and drawings as entities
14
14
  ```
15
15
  -->
16
16
  <script lang="ts">
17
- import type { Entity } from 'koota'
18
-
19
17
  import { untrack } from 'svelte'
20
18
  import { onDestroy } from 'svelte'
21
19
 
22
20
  import type { Snapshot as SnapshotProto } from '../buf/draw/v1/snapshot_pb'
23
21
 
24
- import { useWorld } from '../ecs'
22
+ import { traits, useWorld } from '../ecs'
25
23
  import { useCameraControls } from '../hooks/useControls.svelte'
24
+ import { useRelationships } from '../hooks/useRelationships.svelte'
26
25
  import { useSettings } from '../hooks/useSettings.svelte'
27
- import { applySceneMetadata, spawnSnapshotEntities } from '../snapshot'
26
+ import { applySceneMetadata, type SnapshotEntity, spawnSnapshotEntities } from '../snapshot'
28
27
 
29
28
  interface Props {
30
29
  snapshot: SnapshotProto
@@ -35,8 +34,9 @@ Renders a Snapshot protobuf by spawning its transforms and drawings as entities
35
34
  const world = useWorld()
36
35
  const settings = useSettings()
37
36
  const cameraControls = useCameraControls()
37
+ const relationships = useRelationships()
38
38
 
39
- let entities: Entity[] = []
39
+ let entities: SnapshotEntity[] = []
40
40
 
41
41
  $effect(() => {
42
42
  world.id.toString()
@@ -44,6 +44,11 @@ Renders a Snapshot protobuf by spawning its transforms and drawings as entities
44
44
 
45
45
  untrack(() => {
46
46
  entities = spawnSnapshotEntities(world, snapshot)
47
+ for (const spawned of entities) {
48
+ relationships.apply(spawned.entity, spawned.relationships)
49
+ const uuid = spawned.entity.get(traits.UUID)
50
+ if (uuid) relationships.flush(uuid)
51
+ }
47
52
  })
48
53
  })
49
54
 
@@ -78,8 +83,8 @@ Renders a Snapshot protobuf by spawning its transforms and drawings as entities
78
83
  })
79
84
 
80
85
  onDestroy(() => {
81
- for (const entity of entities) {
82
- if (world.has(entity)) entity.destroy()
86
+ for (const spawned of entities) {
87
+ if (world.has(spawned.entity)) spawned.entity.destroy()
83
88
  }
84
89
  })
85
90
  </script>
@@ -4,20 +4,30 @@
4
4
 
5
5
  import { relations, traits, useQuery, useTrait } from '../../ecs'
6
6
  import { SubEntityLinkType } from '../../ecs/relations'
7
+ import { useDrawService } from '../../hooks/useDrawService.svelte'
7
8
 
8
9
  interface Props {
9
10
  entity: Entity | undefined
10
11
  }
11
12
 
12
13
  const { entity }: Props = $props()
14
+ const drawService = useDrawService()
13
15
 
14
16
  const allEntities = useQuery(traits.Name)
15
17
  const name = useTrait(() => entity, traits.Name)
18
+ const drawServiceAPI = useTrait(() => entity, traits.DrawServiceAPI)
19
+ const isServiceManaged = $derived(!!drawServiceAPI.current)
20
+
16
21
  const entityNames = $derived.by(() => {
17
22
  const currentEntityName = name.current
18
23
  return allEntities.current
19
- .map((e: Entity) => e.get(traits.Name))
20
- .filter((n: string | undefined): n is string => n !== undefined && n !== currentEntityName)
24
+ .filter((e: Entity) => {
25
+ const entityName = e.get(traits.Name)
26
+ if (!entityName || entityName === currentEntityName) return false
27
+ if (isServiceManaged) return !!e.get(traits.DrawServiceAPI)
28
+ return true
29
+ })
30
+ .map((e: Entity) => e.get(traits.Name)!)
21
31
  .toSorted()
22
32
  })
23
33
 
@@ -43,7 +53,19 @@
43
53
  const selectedEntity = allEntities.current.find(
44
54
  (e: Entity) => e.get(traits.Name) === selectedRelationshipEntity
45
55
  )
46
- if (selectedEntity) {
56
+ if (!selectedEntity) return
57
+
58
+ if (isServiceManaged) {
59
+ const sourceUuid = entity.get(traits.UUID)
60
+ const targetUuid = selectedEntity.get(traits.UUID)
61
+ if (!sourceUuid || !targetUuid) return
62
+ void drawService.createRelationship(
63
+ sourceUuid,
64
+ targetUuid,
65
+ linkType ?? '',
66
+ relationshipFormula
67
+ )
68
+ } else {
47
69
  entity.add(
48
70
  relations.SubEntityLink(selectedEntity, {
49
71
  indexMapping: relationshipFormula,