@viamrobotics/motion-tools 0.14.6 → 0.14.7

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.
@@ -29,5 +29,6 @@ export declare class FrameConfigUpdater {
29
29
  setFrameParent: (parentName: string) => void;
30
30
  deleteFrame: () => void;
31
31
  setGeometryType: (type: "none" | "box" | "sphere" | "capsule") => void;
32
+ private sanatizeFloatValue;
32
33
  }
33
34
  export {};
@@ -14,6 +14,11 @@ export class FrameConfigUpdater {
14
14
  const object = this.object();
15
15
  if (!object)
16
16
  return;
17
+ x = this.sanatizeFloatValue(x);
18
+ y = this.sanatizeFloatValue(y);
19
+ z = this.sanatizeFloatValue(z);
20
+ if (x === undefined && y === undefined && z === undefined)
21
+ return;
17
22
  object.localEditedPose.x = x ?? object.localEditedPose.x;
18
23
  object.localEditedPose.y = y ?? object.localEditedPose.y;
19
24
  object.localEditedPose.z = z ?? object.localEditedPose.z;
@@ -31,6 +36,12 @@ export class FrameConfigUpdater {
31
36
  const object = this.object();
32
37
  if (!object)
33
38
  return;
39
+ oX = this.sanatizeFloatValue(oX);
40
+ oY = this.sanatizeFloatValue(oY);
41
+ oZ = this.sanatizeFloatValue(oZ);
42
+ theta = this.sanatizeFloatValue(theta);
43
+ if (oX === undefined && oY === undefined && oZ === undefined && theta === undefined)
44
+ return;
34
45
  object.localEditedPose.oX = oX ?? object.localEditedPose.oX;
35
46
  object.localEditedPose.oY = oY ?? object.localEditedPose.oY;
36
47
  object.localEditedPose.oZ = oZ ?? object.localEditedPose.oZ;
@@ -52,6 +63,11 @@ export class FrameConfigUpdater {
52
63
  let geometryObject;
53
64
  if (geometry?.type === 'box') {
54
65
  const currentGeometry = object.geometry?.geometryType.value;
66
+ geometry.x = this.sanatizeFloatValue(geometry.x);
67
+ geometry.y = this.sanatizeFloatValue(geometry.y);
68
+ geometry.z = this.sanatizeFloatValue(geometry.z);
69
+ if (geometry.x === undefined && geometry.y === undefined && geometry.z === undefined)
70
+ return;
55
71
  geometryObject = {
56
72
  type: 'box',
57
73
  x: geometry.x ?? currentGeometry?.dimsMm?.x,
@@ -61,6 +77,9 @@ export class FrameConfigUpdater {
61
77
  }
62
78
  else if (geometry?.type === 'sphere') {
63
79
  const currentGeometry = object.geometry?.geometryType.value;
80
+ geometry.r = this.sanatizeFloatValue(geometry.r);
81
+ if (geometry.r === undefined)
82
+ return;
64
83
  geometryObject = {
65
84
  type: 'sphere',
66
85
  r: geometry.r ?? currentGeometry?.radiusMm,
@@ -68,6 +87,10 @@ export class FrameConfigUpdater {
68
87
  }
69
88
  else if (geometry?.type === 'capsule') {
70
89
  const currentGeometry = object.geometry?.geometryType.value;
90
+ geometry.r = this.sanatizeFloatValue(geometry.r);
91
+ geometry.l = this.sanatizeFloatValue(geometry.l);
92
+ if (geometry.r === undefined && geometry.l === undefined)
93
+ return;
71
94
  geometryObject = {
72
95
  type: 'capsule',
73
96
  r: geometry.r ?? currentGeometry?.radiusMm,
@@ -113,4 +136,12 @@ export class FrameConfigUpdater {
113
136
  }, { type: 'capsule', r: 20, l: 100 });
114
137
  }
115
138
  };
139
+ sanatizeFloatValue = (value) => {
140
+ if (value === undefined)
141
+ return undefined;
142
+ const num = parseFloat(value.toFixed(2));
143
+ if (isNaN(num))
144
+ return undefined;
145
+ return num;
146
+ };
116
147
  }
@@ -55,6 +55,7 @@
55
55
  const isFrameNode = $derived(
56
56
  frames.current.find((frame) => frame.name === object?.name) !== undefined
57
57
  )
58
+ const showEditFrameOptions = $derived(isFrameNode && partConfig.hasEditPermissions)
58
59
  let copied = $state(false)
59
60
 
60
61
  const draggable = useDraggable('details')
@@ -215,7 +216,7 @@
215
216
 
216
217
  {#if object}
217
218
  <div
218
- class="border-medium bg-extralight absolute top-0 right-0 z-1000 m-2 {isFrameNode
219
+ class="border-medium bg-extralight absolute top-0 right-0 z-1000 m-2 {showEditFrameOptions
219
220
  ? 'w-80'
220
221
  : 'w-60'} border p-2 text-xs"
221
222
  style:transform="translate({draggable.current.x}px, {draggable.current.y}px)"
@@ -303,7 +304,7 @@
303
304
  {/if}
304
305
 
305
306
  <WeblabActive experiment="MOTION_TOOLS_EDIT_FRAME">
306
- {@const ParentFrame = isFrameNode ? DropDownField : ImmutableField}
307
+ {@const ParentFrame = showEditFrameOptions ? DropDownField : ImmutableField}
307
308
 
308
309
  <div>
309
310
  <strong class="font-semibold">parent frame</strong>
@@ -318,7 +319,7 @@
318
319
  </div>
319
320
 
320
321
  {#if localPose}
321
- {@const PoseAttribute = isFrameNode ? MutableField : ImmutableField}
322
+ {@const PoseAttribute = showEditFrameOptions ? MutableField : ImmutableField}
322
323
  <div>
323
324
  <strong class="font-semibold">local position</strong>
324
325
 
@@ -349,7 +350,7 @@
349
350
 
350
351
  <div>
351
352
  <strong class="font-semibold">local orientation</strong>
352
- <div class="flex {isFrameNode ? 'gap-2' : 'gap-3'}">
353
+ <div class="flex {showEditFrameOptions ? 'gap-2' : 'gap-3'}">
353
354
  {@render PoseAttribute({
354
355
  label: 'x',
355
356
  ariaLabel: 'local orientation x coordinate',
@@ -382,7 +383,7 @@
382
383
  </div>
383
384
  {/if}
384
385
 
385
- {#if isFrameNode}
386
+ {#if showEditFrameOptions}
386
387
  <div>
387
388
  <strong class="font-semibold">geometry</strong>
388
389
  <div class="grid grid-cols-4 gap-1">
@@ -410,7 +411,7 @@
410
411
  </div>
411
412
  {/if}
412
413
  {#if geometryType !== 'none'}
413
- {@const GeometryAttribute = isFrameNode ? MutableField : ImmutableField}
414
+ {@const GeometryAttribute = showEditFrameOptions ? MutableField : ImmutableField}
414
415
  {#if geometryType === 'box'}
415
416
  {@const { dimsMm } = object?.geometry?.geometryType.value as {
416
417
  dimsMm: { x: number; y: number; z: number }
@@ -563,7 +564,7 @@
563
564
  {/if}
564
565
 
565
566
  <WeblabActive experiment="MOTION_TOOLS_EDIT_FRAME">
566
- {#if isFrameNode}
567
+ {#if showEditFrameOptions}
567
568
  <Button
568
569
  variant="danger"
569
570
  class="mt-2 w-full"
@@ -14,6 +14,7 @@
14
14
  import AddFrames from './AddFrames.svelte'
15
15
  import { useEnvironment } from '../../hooks/useEnvironment.svelte'
16
16
  import { usePartID } from '../../hooks/usePartID.svelte'
17
+ import { usePartConfig } from '../../hooks/usePartConfig.svelte'
17
18
  const { ...rest } = $props()
18
19
 
19
20
  provideTreeExpandedContext()
@@ -24,6 +25,7 @@
24
25
  const draggable = useDraggable('treeview')
25
26
  const worldStates = useWorldStates()
26
27
  const environment = useEnvironment()
28
+ const partConfig = usePartConfig()
27
29
 
28
30
  let rootNode = $state<TreeNode>({
29
31
  id: 'world',
@@ -58,7 +60,7 @@
58
60
  />
59
61
  {/key}
60
62
 
61
- {#if environment.current.isStandalone && partID.current}
63
+ {#if environment.current.isStandalone && partID.current && partConfig.hasEditPermissions}
62
64
  <AddFrames />
63
65
  {/if}
64
66
 
@@ -24,11 +24,13 @@ interface PartConfigContext {
24
24
  componentNameToFragmentId: Record<string, string>;
25
25
  localPartConfig: Struct;
26
26
  isDirty: boolean;
27
+ hasEditPermissions: boolean;
27
28
  }
28
29
  export declare const providePartConfig: (params: PartConfigParams) => void;
29
30
  export declare const usePartConfig: () => PartConfigContext;
30
31
  interface LocalPartConfig {
31
32
  isDirty: () => boolean;
33
+ hasEditPermissions: () => boolean;
32
34
  getLocalPartConfig: () => Struct;
33
35
  setLocalPartConfig: (config: Struct) => void;
34
36
  componentNameToFragmentId: () => Record<string, string>;
@@ -48,6 +50,7 @@ export declare class AppEmbeddedPartConfig implements LocalPartConfig {
48
50
  getLocalPartConfig(): Struct;
49
51
  setLocalPartConfig(config: Struct): void;
50
52
  componentNameToFragmentId(): Record<string, string>;
53
+ hasEditPermissions(): boolean;
51
54
  }
52
55
  interface StandalonePartConfigProps {
53
56
  viamClient: () => ViamClient | undefined;
@@ -56,6 +59,7 @@ interface StandalonePartConfigProps {
56
59
  export declare class StandalonePartConfig implements LocalPartConfig {
57
60
  private _standalonePartConfigProps;
58
61
  private _isDirty;
62
+ private _hasEditPermissions;
59
63
  private _networkPartConfig;
60
64
  private _localPartConfig;
61
65
  private _partName;
@@ -64,6 +68,7 @@ export declare class StandalonePartConfig implements LocalPartConfig {
64
68
  getLocalPartConfig(): Struct;
65
69
  setLocalPartConfig(config: Struct): void;
66
70
  isDirty(): boolean;
71
+ hasEditPermissions(): boolean;
67
72
  componentNameToFragmentId(): Record<string, string>;
68
73
  saveLocalPartConfig(): Promise<void>;
69
74
  resetLocalPartConfig(): Promise<void>;
@@ -209,6 +209,9 @@ export const providePartConfig = (params) => {
209
209
  get isDirty() {
210
210
  return _localPartConfig.isDirty();
211
211
  },
212
+ get hasEditPermissions() {
213
+ return _localPartConfig.hasEditPermissions();
214
+ },
212
215
  });
213
216
  };
214
217
  export const usePartConfig = () => {
@@ -231,10 +234,14 @@ export class AppEmbeddedPartConfig {
231
234
  componentNameToFragmentId() {
232
235
  return this._appEmbeddedPartConfigProps.getComponentToFragId();
233
236
  }
237
+ hasEditPermissions() {
238
+ return true;
239
+ }
234
240
  }
235
241
  export class StandalonePartConfig {
236
242
  _standalonePartConfigProps;
237
243
  _isDirty = $state(false);
244
+ _hasEditPermissions = $state(false);
238
245
  _networkPartConfig = $state();
239
246
  _localPartConfig = $state();
240
247
  _partName = $state();
@@ -246,6 +253,11 @@ export class StandalonePartConfig {
246
253
  const partResponse = await standalonePartConfigProps
247
254
  .viamClient()
248
255
  ?.appClient.getRobotPart(standalonePartConfigProps.partID());
256
+ if (JSON.parse(partResponse?.configJson ?? 'null') === null) {
257
+ // no config returned here indicates this api key has no permission to update config
258
+ return;
259
+ }
260
+ this._hasEditPermissions = true;
249
261
  const configJson = JSON.parse(partResponse?.configJson ?? '{}');
250
262
  this._networkPartConfig = Struct.fromJson(configJson);
251
263
  this._localPartConfig = Struct.fromJson(configJson);
@@ -292,6 +304,9 @@ export class StandalonePartConfig {
292
304
  isDirty() {
293
305
  return this._isDirty;
294
306
  }
307
+ hasEditPermissions() {
308
+ return this._hasEditPermissions;
309
+ }
295
310
  componentNameToFragmentId() {
296
311
  return this._componentNameToFragmentId ?? {};
297
312
  }
package/dist/transform.js CHANGED
@@ -27,7 +27,7 @@ export const createPoseFromFrame = (frame) => {
27
27
  else if (frame.orientation.type === 'ov_radians') {
28
28
  ov.copy(frame.orientation.value);
29
29
  }
30
- else if (frame.orientation.type === 'ov_degrees') {
30
+ else {
31
31
  const th = MathUtils.degToRad(frame.orientation.value.th);
32
32
  ov.set(frame.orientation.value.x, frame.orientation.value.y, frame.orientation.value.z, th);
33
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viamrobotics/motion-tools",
3
- "version": "0.14.6",
3
+ "version": "0.14.7",
4
4
  "description": "Motion visualization with Viam",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",