@viamrobotics/motion-tools 1.13.0 → 1.14.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 (102) hide show
  1. package/dist/FrameConfigUpdater.svelte.js +1 -1
  2. package/dist/attribute.js +10 -2
  3. package/dist/buf/draw/v1/service_connect.d.ts +14 -25
  4. package/dist/buf/draw/v1/service_connect.js +14 -25
  5. package/dist/buf/draw/v1/service_pb.d.ts +61 -76
  6. package/dist/buf/draw/v1/service_pb.js +58 -111
  7. package/dist/buffer.d.ts +56 -7
  8. package/dist/buffer.js +70 -12
  9. package/dist/color.js +3 -3
  10. package/dist/components/App.svelte +1 -5
  11. package/dist/components/Camera.svelte +1 -7
  12. package/dist/components/Camera.svelte.d.ts +0 -1
  13. package/dist/components/CameraControls.svelte +2 -2
  14. package/dist/components/{Arrows → Entities/Arrows}/ArrowGroups.svelte +3 -3
  15. package/dist/components/{Arrows → Entities/Arrows}/Arrows.svelte +6 -6
  16. package/dist/components/{Arrows → Entities/Arrows}/Arrows.svelte.d.ts +1 -1
  17. package/dist/components/{Entities.svelte → Entities/Entities.svelte} +7 -3
  18. package/dist/components/Entities/Frame.svelte +86 -0
  19. package/dist/components/{Frame.svelte.d.ts → Entities/Frame.svelte.d.ts} +1 -0
  20. package/dist/components/{GLTF.svelte → Entities/GLTF.svelte} +5 -5
  21. package/dist/components/Entities/Geometry.svelte +75 -0
  22. package/dist/components/Entities/Geometry.svelte.d.ts +10 -0
  23. package/dist/components/{Label.svelte → Entities/Label.svelte} +1 -1
  24. package/dist/components/Entities/Line.svelte +90 -0
  25. package/dist/components/Entities/Mesh.svelte +130 -0
  26. package/dist/components/Entities/Mesh.svelte.d.ts +4 -0
  27. package/dist/components/{Points.svelte → Entities/Points.svelte} +5 -5
  28. package/dist/components/{Pose.svelte → Entities/Pose.svelte} +3 -3
  29. package/dist/{hooks/useObjectEvents.svelte.d.ts → components/Entities/hooks/useEntityEvents.svelte.d.ts} +1 -1
  30. package/dist/{hooks/useObjectEvents.svelte.js → components/Entities/hooks/useEntityEvents.svelte.js} +6 -6
  31. package/dist/components/FileDrop/file-names.js +6 -3
  32. package/dist/components/FileDrop/snapshot-dropper.js +8 -4
  33. package/dist/components/FileDrop/useFileDrop.svelte.js +9 -6
  34. package/dist/components/Lasso/Lasso.svelte +4 -4
  35. package/dist/components/PCD.svelte +14 -6
  36. package/dist/components/PCD.svelte.d.ts +2 -0
  37. package/dist/components/Scene.svelte +1 -3
  38. package/dist/components/Selected.svelte +2 -0
  39. package/dist/components/StaticGeometries.svelte +1 -1
  40. package/dist/components/overlay/AddRelationship.svelte +1 -1
  41. package/dist/components/overlay/LiveUpdatesBanner.svelte +4 -6
  42. package/dist/components/overlay/left-pane/buildTree.js +15 -0
  43. package/dist/components/overlay/settings/Settings.svelte +11 -13
  44. package/dist/components/overlay/widgets/Camera.svelte +11 -9
  45. package/dist/components/xr/ArmTeleop.svelte +33 -33
  46. package/dist/components/xr/CameraFeed.svelte +21 -23
  47. package/dist/components/xr/JointLimitsWidget.svelte +19 -5
  48. package/dist/components/xr/XRConfigPanel.svelte +17 -16
  49. package/dist/components/xr/XRControllerSettings.svelte +5 -4
  50. package/dist/components/xr/XRToast.svelte +11 -6
  51. package/dist/ecs/relations.d.ts +1 -0
  52. package/dist/ecs/relations.js +1 -0
  53. package/dist/ecs/traits.d.ts +2 -19
  54. package/dist/ecs/traits.js +33 -6
  55. package/dist/ecs/useQuery.svelte.js +3 -3
  56. package/dist/format.js +1 -1
  57. package/dist/hooks/use3DModels.svelte.js +36 -38
  58. package/dist/hooks/useConfigFrames.svelte.js +2 -7
  59. package/dist/hooks/useDrawAPI.svelte.js +1 -1
  60. package/dist/hooks/useFramelessComponents.svelte.js +1 -1
  61. package/dist/hooks/useFrames.svelte.js +62 -56
  62. package/dist/hooks/useGeometries.svelte.js +59 -36
  63. package/dist/hooks/useLinked.svelte.js +5 -4
  64. package/dist/hooks/usePartConfig.svelte.js +16 -17
  65. package/dist/hooks/usePointcloudObjects.svelte.js +107 -62
  66. package/dist/hooks/usePointclouds.svelte.js +50 -32
  67. package/dist/hooks/usePose.svelte.js +3 -7
  68. package/dist/hooks/useResizable.svelte.js +4 -3
  69. package/dist/hooks/useSettings.svelte.js +2 -2
  70. package/dist/hooks/useWeblabs.svelte.js +3 -2
  71. package/dist/hooks/useWorldState.svelte.js +31 -28
  72. package/dist/loaders/pcd/index.js +2 -1
  73. package/dist/loaders/pcd/worker.inline.d.ts +1 -1
  74. package/dist/loaders/pcd/worker.inline.js +1 -1
  75. package/dist/loaders/pcd/worker.js +1 -1
  76. package/dist/metadata.d.ts +22 -0
  77. package/dist/metadata.js +66 -0
  78. package/dist/snapshot.d.ts +20 -0
  79. package/dist/snapshot.js +72 -28
  80. package/dist/three/InstancedArrows/InstancedArrows.js +1 -1
  81. package/dist/three/InstancedArrows/box.js +1 -1
  82. package/dist/three/InstancedArrows/raycast.js +12 -12
  83. package/dist/three/OBBHelper.d.ts +3 -2
  84. package/dist/three/OBBHelper.js +17 -5
  85. package/package.json +19 -11
  86. package/dist/WorldObject.svelte.d.ts +0 -27
  87. package/dist/WorldObject.svelte.js +0 -114
  88. package/dist/components/Frame.svelte +0 -89
  89. package/dist/components/Geometry.svelte +0 -211
  90. package/dist/components/Geometry.svelte.d.ts +0 -19
  91. package/dist/components/Line.svelte +0 -43
  92. /package/dist/components/{Arrows → Entities/Arrows}/ArrowGroups.svelte.d.ts +0 -0
  93. /package/dist/components/{Entities.svelte.d.ts → Entities/Entities.svelte.d.ts} +0 -0
  94. /package/dist/components/{GLTF.svelte.d.ts → Entities/GLTF.svelte.d.ts} +0 -0
  95. /package/dist/components/{Label.svelte.d.ts → Entities/Label.svelte.d.ts} +0 -0
  96. /package/dist/components/{Line.svelte.d.ts → Entities/Line.svelte.d.ts} +0 -0
  97. /package/dist/components/{LineDots.svelte → Entities/LineDots.svelte} +0 -0
  98. /package/dist/components/{LineDots.svelte.d.ts → Entities/LineDots.svelte.d.ts} +0 -0
  99. /package/dist/components/{LineGeometry.svelte → Entities/LineGeometry.svelte} +0 -0
  100. /package/dist/components/{LineGeometry.svelte.d.ts → Entities/LineGeometry.svelte.d.ts} +0 -0
  101. /package/dist/components/{Points.svelte.d.ts → Entities/Points.svelte.d.ts} +0 -0
  102. /package/dist/components/{Pose.svelte.d.ts → Entities/Pose.svelte.d.ts} +0 -0
@@ -8,9 +8,6 @@ import { RefetchRates } from '../components/overlay/RefreshRate.svelte';
8
8
  import { traits, useWorld } from '../ecs';
9
9
  import { useEnvironment } from './useEnvironment.svelte';
10
10
  import { createBufferGeometry, updateBufferGeometry } from '../attribute';
11
- const typeSafeObjectFromEntries = (entries) => {
12
- return Object.fromEntries(entries);
13
- };
14
11
  const key = Symbol('pointcloud-context');
15
12
  export const providePointclouds = (partID) => {
16
13
  const environment = useEnvironment();
@@ -50,10 +47,8 @@ export const providePointclouds = (partID) => {
50
47
  */
51
48
  $effect(() => {
52
49
  for (const [name, query] of propQueries) {
53
- if (name && query.data?.supportsPcd === false) {
54
- if (disabledCameras.get(name) === undefined) {
55
- disabledCameras.set(name, true);
56
- }
50
+ if (name && query.data?.supportsPcd === false && disabledCameras.get(name) === undefined) {
51
+ disabledCameras.set(name, true);
57
52
  }
58
53
  }
59
54
  });
@@ -62,7 +57,6 @@ export const providePointclouds = (partID) => {
62
57
  refetchInterval: interval === RefetchRates.MANUAL ? false : interval,
63
58
  });
64
59
  const queries = $derived(enabledClients.map((client) => [client.current.name, createResourceQuery(client, 'getPointCloud', () => options)]));
65
- const queryMap = $derived(typeSafeObjectFromEntries(queries));
66
60
  $effect(() => {
67
61
  for (const [name, query] of queries) {
68
62
  untrack(() => {
@@ -79,39 +73,63 @@ export const providePointclouds = (partID) => {
79
73
  });
80
74
  const entities = new Map();
81
75
  $effect(() => {
76
+ const currentPartID = partID();
77
+ const activeQueryKeys = new Set();
82
78
  for (const [name, query] of queries) {
83
- untrack(() => {
84
- $effect(() => {
85
- const { data } = query;
86
- if (!data || data.length === 0)
79
+ const queryKey = `${currentPartID}:${name}`;
80
+ activeQueryKeys.add(queryKey);
81
+ $effect(() => {
82
+ const { data } = query;
83
+ let disposed = false;
84
+ const destroyEntity = () => {
85
+ const entity = entities.get(queryKey);
86
+ if (entity) {
87
+ if (world.has(entity))
88
+ entity.destroy();
89
+ entities.delete(queryKey);
90
+ }
91
+ };
92
+ if (!data || data.length === 0) {
93
+ destroyEntity();
94
+ return () => {
95
+ disposed = true;
96
+ };
97
+ }
98
+ parsePcdInWorker(data)
99
+ .then(({ positions, colors }) => {
100
+ if (disposed) {
87
101
  return;
88
- parsePcdInWorker(data)
89
- .then(({ positions, colors }) => {
90
- const existing = entities.get(name);
91
- if (existing) {
92
- const geometry = existing.get(traits.BufferGeometry);
93
- if (geometry) {
94
- updateBufferGeometry(geometry, positions, colors);
95
- return;
96
- }
102
+ }
103
+ const existing = entities.get(queryKey);
104
+ if (existing) {
105
+ const geometry = existing.get(traits.BufferGeometry);
106
+ if (geometry) {
107
+ updateBufferGeometry(geometry, positions, colors);
108
+ return;
97
109
  }
98
- const geometry = createBufferGeometry(positions, colors);
99
- const entity = world.spawn(traits.Parent(name), traits.Name(`${name} pointcloud`), traits.BufferGeometry(geometry), traits.Points);
100
- entities.set(name, entity);
101
- })
102
- .catch((error) => {
103
- logs.add(error.reason, 'error');
104
- });
110
+ }
111
+ const geometry = createBufferGeometry(positions, colors);
112
+ const entity = world.spawn(traits.Parent(name), traits.Name(`${name} pointcloud`), traits.BufferGeometry(geometry), traits.Points);
113
+ entities.set(queryKey, entity);
114
+ })
115
+ .catch((error) => {
116
+ if (disposed) {
117
+ return;
118
+ }
119
+ logs.add(error?.reason ?? error?.message ?? 'Failed to parse pointcloud', 'error');
105
120
  });
121
+ return () => {
122
+ disposed = true;
123
+ };
106
124
  });
107
125
  }
108
- // Clean up old entities
109
- for (const [name, entity] of entities) {
110
- if (!queryMap[name]?.data) {
126
+ // clean up queries that disappeared entirely
127
+ for (const [queryKey, entity] of entities) {
128
+ if (!activeQueryKeys.has(queryKey)) {
111
129
  if (world.has(entity)) {
112
130
  entity.destroy();
113
131
  }
114
- entities.delete(name);
132
+ entities.delete(queryKey);
115
133
  }
116
134
  }
117
135
  });
@@ -10,7 +10,7 @@ import { RefetchRates } from '../components/overlay/RefreshRate.svelte';
10
10
  import { useLogs } from './useLogs.svelte';
11
11
  import { useResourceByName } from './useResourceByName.svelte';
12
12
  import { useRefetchPoses } from './useRefetchPoses';
13
- const origingFrameComponentTypes = ['arm', 'gantry', 'gripper', 'base'];
13
+ const originFrameComponentTypes = new Set(['arm', 'gantry', 'gripper', 'base']);
14
14
  export const usePose = (name, parent) => {
15
15
  const environment = useEnvironment();
16
16
  const logs = useLogs();
@@ -26,12 +26,8 @@ export const usePose = (name, parent) => {
26
26
  const frames = useFrames();
27
27
  let pose = $state();
28
28
  const interval = $derived(refreshRates.get(RefreshRates.poses));
29
- const resolvedParent = $derived(origingFrameComponentTypes.includes(parentResource?.subtype ?? '')
30
- ? `${parent()}_origin`
31
- : parent());
32
- const resolvedName = $derived(origingFrameComponentTypes.includes(resource?.subtype ?? '')
33
- ? `${currentName}_origin`
34
- : currentName);
29
+ const resolvedParent = $derived(originFrameComponentTypes.has(parentResource?.subtype ?? '') ? `${parent()}_origin` : parent());
30
+ const resolvedName = $derived(originFrameComponentTypes.has(resource?.subtype ?? '') ? `${currentName}_origin` : currentName);
35
31
  const query = createRobotQuery(robotClient, 'getPose', () => [resolvedName, resolvedParent ?? 'world', []], () => ({
36
32
  enabled: interval !== RefetchRates.OFF && environment.current.viewerMode === 'monitor',
37
33
  refetchInterval: interval === RefetchRates.MANUAL ? false : interval,
@@ -28,10 +28,11 @@ export const useResizable = (name, defaultDimensions) => {
28
28
  });
29
29
  observer.observe(target);
30
30
  };
31
+ const disconnect = () => {
32
+ observer?.disconnect();
33
+ };
31
34
  $effect(() => {
32
- return () => {
33
- observer?.disconnect();
34
- };
35
+ return disconnect;
35
36
  });
36
37
  return {
37
38
  get current() {
@@ -30,11 +30,11 @@ const defaults = () => ({
30
30
  renderSubEntityHoverDetail: false,
31
31
  xrController: {
32
32
  left: {
33
- scaleFactor: 1.0,
33
+ scaleFactor: 1,
34
34
  rotationEnabled: true,
35
35
  },
36
36
  right: {
37
- scaleFactor: 1.0,
37
+ scaleFactor: 1,
38
38
  rotationEnabled: true,
39
39
  },
40
40
  },
@@ -17,6 +17,7 @@ const addCookie = (name, value, days, path = '/') => {
17
17
  date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); // days in milliseconds
18
18
  expires = '; expires=' + date.toUTCString();
19
19
  }
20
+ // eslint-disable-next-line unicorn/no-document-cookie
20
21
  document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${expires}; path=${path}`;
21
22
  };
22
23
  const getCookieExperiments = () => {
@@ -44,10 +45,10 @@ export const createWeblabs = () => {
44
45
  };
45
46
  };
46
47
  export const provideWeblabs = () => {
47
- const urlExperiment = new URLSearchParams(window.location.search).get('experiment');
48
+ const urlExperiment = new URLSearchParams(globalThis.location.search).get('experiment');
48
49
  if (urlExperiment) {
49
50
  const experimentSet = new Set([...getCookieExperiments(), urlExperiment]);
50
- addCookie('weblab_experiments', Array.from(experimentSet).join(','));
51
+ addCookie('weblab_experiments', [...experimentSet].join(','));
51
52
  }
52
53
  setContext(WEBLABS_CONTEXT_KEY, createWeblabs());
53
54
  };
@@ -1,6 +1,8 @@
1
1
  import { WorldStateStoreClient, TransformChangeType, } from '@viamrobotics/sdk';
2
2
  import { createResourceClient, createResourceQuery, createResourceStream, useResourceNames, } from '@viamrobotics/svelte-sdk';
3
- import { parseMetadata } from '../WorldObject.svelte';
3
+ import { parseMetadata } from '../metadata';
4
+ import { asColor, asOpacity, isPerVertexColors, STRIDE } from '../buffer';
5
+ import { Color } from 'three';
4
6
  import { usePartID } from './usePartID.svelte';
5
7
  import { traits, useWorld } from '../ecs';
6
8
  import { createPose } from '../transform';
@@ -9,6 +11,7 @@ import { createBox, createCapsule, createSphere } from '../geometry';
9
11
  import { parsePlyInput } from '../ply';
10
12
  import { parsePcdInWorker } from '../loaders/pcd';
11
13
  import { createBufferGeometry } from '../attribute';
14
+ const colorUtil = new Color();
12
15
  export const provideWorldStates = () => {
13
16
  const partID = usePartID();
14
17
  const resourceNames = useResourceNames(() => partID.current, 'world_state_store');
@@ -40,46 +43,43 @@ const createWorldState = (client) => {
40
43
  if (parent && parent !== 'world') {
41
44
  entityTraits.push(traits.Parent(parent));
42
45
  }
43
- if (metadata.color) {
44
- entityTraits.push(traits.Color(metadata.color));
45
- }
46
- if (metadata.colors) {
47
- entityTraits.push(traits.VertexColors(metadata.colors));
48
- }
49
46
  if (transform.physicalObject) {
50
47
  if (transform.physicalObject.geometryType.case === 'pointcloud') {
48
+ const metadataColors = metadata.colors;
51
49
  parsePcdInWorker(new Uint8Array(transform.physicalObject.geometryType.value.pointCloud)).then((pointcloud) => {
52
- // pcds are a special case since they have to be loaded in a worker and the trait will be added to the existing entity
53
50
  const entity = entities.get(transform.uuidString);
54
51
  if (!entity) {
55
52
  console.error('Entity not found to add pointcloud trait to', transform.uuidString);
56
53
  return;
57
54
  }
58
- const geometry = createBufferGeometry(pointcloud.positions, pointcloud.colors);
55
+ const numPoints = pointcloud.positions.length / STRIDE.POSITIONS;
56
+ const vertexColors = metadataColors && isPerVertexColors(metadataColors, numPoints)
57
+ ? metadataColors
58
+ : pointcloud.colors;
59
+ const geometry = createBufferGeometry(pointcloud.positions, vertexColors);
59
60
  entity.add(traits.BufferGeometry(geometry));
60
61
  entity.add(traits.Points);
62
+ if (metadataColors && !isPerVertexColors(metadataColors, numPoints)) {
63
+ asColor(metadataColors, colorUtil);
64
+ entity.add(traits.Color({ r: colorUtil.r, g: colorUtil.g, b: colorUtil.b }));
65
+ if (metadataColors.length % STRIDE.COLORS_RGBA === 0) {
66
+ entity.add(traits.Opacity(asOpacity(metadataColors)));
67
+ }
68
+ }
69
+ invalidate();
61
70
  });
62
71
  }
63
72
  else {
73
+ if (metadata.colors) {
74
+ asColor(metadata.colors, colorUtil);
75
+ entityTraits.push(traits.Color({ r: colorUtil.r, g: colorUtil.g, b: colorUtil.b }));
76
+ if (metadata.colors.length % STRIDE.COLORS_RGBA === 0) {
77
+ entityTraits.push(traits.Opacity(asOpacity(metadata.colors)));
78
+ }
79
+ }
64
80
  entityTraits.push(traits.Geometry(transform.physicalObject));
65
81
  }
66
82
  }
67
- if (metadata.shape === 'line' && metadata.points) {
68
- const { points } = metadata;
69
- const positions = new Float32Array(points.length * 3);
70
- for (let i = 0, j = 0, l = points.length * 3; i < l; i += 3, j += 1) {
71
- positions[i + 0] = points[j].x;
72
- positions[i + 1] = points[j].y;
73
- positions[i + 2] = points[j].z;
74
- }
75
- entityTraits.push(traits.LinePositions(positions), traits.PointColor(metadata.lineDotColor));
76
- }
77
- if (metadata.gltf) {
78
- entityTraits.push(traits.GLTF({ source: { gltf: metadata.gltf }, animationName: '' }));
79
- }
80
- if (metadata.shape === 'arrow') {
81
- entityTraits.push(traits.Arrow);
82
- }
83
83
  entityTraits.push(traits.Name(transform.referenceFrame), traits.Pose(pose), traits.ShowAxesHelper, traits.WorldStateStoreAPI);
84
84
  const entity = world.spawn(...entityTraits);
85
85
  entities.set(transform.uuidString, entity);
@@ -189,15 +189,17 @@ const createWorldState = (client) => {
189
189
  continue;
190
190
  }
191
191
  switch (event.changeType) {
192
- case TransformChangeType.REMOVED:
192
+ case TransformChangeType.REMOVED: {
193
193
  eventsByUUID.set(uuid, event);
194
194
  break;
195
- case TransformChangeType.ADDED:
195
+ }
196
+ case TransformChangeType.ADDED: {
196
197
  if (existing.changeType !== TransformChangeType.REMOVED) {
197
198
  eventsByUUID.set(uuid, event);
198
199
  }
199
200
  break;
200
- case TransformChangeType.UPDATED:
201
+ }
202
+ case TransformChangeType.UPDATED: {
201
203
  // merge with existing updated event
202
204
  if (existing.changeType === TransformChangeType.UPDATED) {
203
205
  existing.updatedFields ??= { paths: [] };
@@ -214,6 +216,7 @@ const createWorldState = (client) => {
214
216
  eventsByUUID.set(uuid, event);
215
217
  }
216
218
  break;
219
+ }
217
220
  }
218
221
  }
219
222
  pendingEvents.push(...eventsByUUID.values());
@@ -22,6 +22,7 @@ export const parsePcdInWorker = (data) => {
22
22
  return new Promise((resolve, reject) => {
23
23
  const id = ++requestId;
24
24
  pending.set(id, { resolve, reject });
25
- worker.postMessage({ id, data }, [data.buffer]);
25
+ const copy = new Uint8Array(data);
26
+ worker.postMessage({ id, data: copy }, [copy.buffer]);
26
27
  });
27
28
  };