@viamrobotics/motion-tools 1.26.1 → 1.27.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 (58) hide show
  1. package/dist/FrameConfigUpdater.svelte.js +42 -29
  2. package/dist/assert.d.ts +13 -0
  3. package/dist/assert.js +20 -0
  4. package/dist/buf/common/v1/common_pb.d.ts +19 -0
  5. package/dist/buf/common/v1/common_pb.js +32 -0
  6. package/dist/components/BatchedArrows.svelte +43 -45
  7. package/dist/components/Entities/Arrows/Arrows.svelte +35 -29
  8. package/dist/components/Entities/Entities.svelte +3 -8
  9. package/dist/components/Entities/Frame.svelte +31 -32
  10. package/dist/components/Entities/Frame.svelte.d.ts +0 -2
  11. package/dist/components/Entities/GLTF.svelte +27 -36
  12. package/dist/components/Entities/Geometry.svelte +35 -24
  13. package/dist/components/Entities/Line.svelte +37 -43
  14. package/dist/components/Entities/Mesh.svelte +12 -18
  15. package/dist/components/Entities/Points.svelte +25 -28
  16. package/dist/components/Entities/Pose.svelte +17 -24
  17. package/dist/components/Entities/Pose.svelte.d.ts +1 -4
  18. package/dist/components/Entities/hooks/useEntityEvents.svelte.js +40 -41
  19. package/dist/components/Scene.svelte +7 -1
  20. package/dist/components/SceneProviders.svelte +2 -1
  21. package/dist/components/SelectedTransformControls.svelte +57 -34
  22. package/dist/components/StaticGeometries.svelte +1 -1
  23. package/dist/components/hover/HoveredEntity.svelte +33 -3
  24. package/dist/components/hover/LinkedHoveredEntity.svelte +2 -3
  25. package/dist/components/overlay/Details.svelte +72 -94
  26. package/dist/components/overlay/__tests__/__fixtures__/entity.js +14 -17
  27. package/dist/components/overlay/left-pane/Tree.svelte +9 -9
  28. package/dist/components/overlay/left-pane/Tree.svelte.d.ts +1 -2
  29. package/dist/components/overlay/left-pane/TreeContainer.svelte +4 -15
  30. package/dist/components/overlay/left-pane/TreeNode.svelte +1 -1
  31. package/dist/components/overlay/left-pane/TreeNode.svelte.d.ts +1 -1
  32. package/dist/components/overlay/left-pane/useTree.svelte.d.ts +14 -0
  33. package/dist/components/overlay/left-pane/useTree.svelte.js +63 -0
  34. package/dist/draw.js +24 -9
  35. package/dist/ecs/index.d.ts +1 -0
  36. package/dist/ecs/index.js +1 -0
  37. package/dist/ecs/provideWorldMatrix.svelte.d.ts +8 -0
  38. package/dist/ecs/provideWorldMatrix.svelte.js +13 -0
  39. package/dist/ecs/traits.d.ts +41 -50
  40. package/dist/ecs/traits.js +57 -29
  41. package/dist/ecs/useTrait.svelte.d.ts +1 -6
  42. package/dist/ecs/useTrait.svelte.js +21 -13
  43. package/dist/ecs/worldMatrix.d.ts +10 -0
  44. package/dist/ecs/worldMatrix.js +138 -0
  45. package/dist/editing/FrameEditSession.js +31 -18
  46. package/dist/hooks/use3DModels.svelte.js +1 -1
  47. package/dist/hooks/useConfigFrames.svelte.js +12 -0
  48. package/dist/hooks/useDrawAPI.svelte.js +14 -6
  49. package/dist/hooks/useDrawService.svelte.js +4 -7
  50. package/dist/hooks/useFrames.svelte.js +23 -11
  51. package/dist/hooks/useGeometries.svelte.js +11 -3
  52. package/dist/hooks/usePartConfig.svelte.js +43 -6
  53. package/dist/hooks/useWorldState.svelte.js +10 -2
  54. package/dist/plugins/bvh.svelte.js +37 -26
  55. package/dist/transform.js +55 -21
  56. package/package.json +3 -3
  57. package/dist/components/overlay/left-pane/buildTree.d.ts +0 -13
  58. package/dist/components/overlay/left-pane/buildTree.js +0 -48
@@ -17,11 +17,14 @@ export const bvh = (raycaster, options) => {
17
17
  injectPlugin('bvh', (args) => {
18
18
  const { props } = $derived(args);
19
19
  const opts = $derived(props.bvh ? { ...bvhOptions, ...props.bvh } : bvhOptions);
20
+ let computed = false;
21
+ let helper;
20
22
  $effect(() => {
21
23
  const { ref } = args;
22
- if (opts.enabled === false) {
24
+ if (computed)
25
+ return;
26
+ if (opts.enabled === false)
23
27
  return;
24
- }
25
28
  if (isInstanceOf(ref, 'Points') &&
26
29
  /**
27
30
  * This check is necessary, there are some strange cases where points are coming in from PCDs without any position data
@@ -31,28 +34,12 @@ export const bvh = (raycaster, options) => {
31
34
  ref.geometry.disposeBoundsTree = disposeBoundsTree;
32
35
  ref.raycast = acceleratedRaycast;
33
36
  computeBoundsTree.call(ref.geometry, { type: PointsBVH, ...opts });
34
- const helper = opts.helper ? new BVHHelper(ref) : undefined;
35
- if (helper)
36
- ref.add(helper);
37
- return () => {
38
- ref.raycast = Points.prototype.raycast;
39
- if (helper)
40
- ref.remove(helper);
41
- };
42
37
  }
43
38
  else if (isInstanceOf(ref, 'BatchedMesh')) {
44
39
  /* @ts-expect-error Some sort of ambient type is conflicing here, likely from @threlte/extras */
45
40
  ref.geometry.computeBoundsTree = computeBatchedBoundsTree;
46
41
  ref.geometry.disposeBoundsTree = disposeBatchedBoundsTree;
47
42
  ref.raycast = acceleratedRaycast;
48
- const helper = opts.helper ? new BVHHelper(ref) : undefined;
49
- if (helper)
50
- ref.add(helper);
51
- return () => {
52
- ref.raycast = BatchedMesh.prototype.raycast;
53
- if (helper)
54
- ref.remove(helper);
55
- };
56
43
  }
57
44
  else if (isInstanceOf(ref, 'Mesh') &&
58
45
  /**
@@ -64,15 +51,39 @@ export const bvh = (raycaster, options) => {
64
51
  ref.geometry.disposeBoundsTree = disposeBoundsTree;
65
52
  ref.raycast = acceleratedRaycast;
66
53
  computeBoundsTree.call(ref.geometry, opts);
67
- const helper = opts.helper ? new BVHHelper(ref) : undefined;
68
- if (helper)
69
- ref.add(helper);
70
- return () => {
71
- ref.raycast = Mesh.prototype.raycast;
72
- if (helper)
73
- ref.remove(helper);
74
- };
75
54
  }
55
+ else {
56
+ return;
57
+ }
58
+ if (opts.helper) {
59
+ helper = new BVHHelper(ref);
60
+ ref.add(helper);
61
+ }
62
+ computed = true;
63
+ });
64
+ $effect(() => {
65
+ const { ref } = args;
66
+ return () => {
67
+ if (!computed)
68
+ return;
69
+ if (isInstanceOf(ref, 'Points')) {
70
+ ref.geometry.disposeBoundsTree?.();
71
+ ref.raycast = Points.prototype.raycast;
72
+ }
73
+ else if (isInstanceOf(ref, 'BatchedMesh')) {
74
+ ref.geometry.disposeBoundsTree?.();
75
+ ref.raycast = BatchedMesh.prototype.raycast;
76
+ }
77
+ else if (isInstanceOf(ref, 'Mesh')) {
78
+ ref.geometry.disposeBoundsTree?.();
79
+ ref.raycast = Mesh.prototype.raycast;
80
+ }
81
+ if (helper) {
82
+ ref.remove(helper);
83
+ helper = undefined;
84
+ }
85
+ computed = false;
86
+ };
76
87
  });
77
88
  });
78
89
  };
package/dist/transform.js CHANGED
@@ -5,6 +5,20 @@ const euler = new Euler();
5
5
  const ov = new OrientationVector();
6
6
  const translation = new Vector3();
7
7
  const scale = new Vector3();
8
+ const matA = new Matrix4();
9
+ export const isPoseEqual = (a, b) => {
10
+ if (a === b)
11
+ return true;
12
+ if (!a || !b)
13
+ return false;
14
+ return (a.x === b.x &&
15
+ a.y === b.y &&
16
+ a.z === b.z &&
17
+ a.oX === b.oX &&
18
+ a.oY === b.oY &&
19
+ a.oZ === b.oZ &&
20
+ a.theta === b.theta);
21
+ };
8
22
  export const createPose = (pose) => {
9
23
  // We should only default to the 0,0,1,0 orientation vector if the entire vector component is missing
10
24
  const oZ = pose?.oX === undefined && pose?.oY === undefined && pose?.oZ === undefined ? 1 : (pose?.oZ ?? 0);
@@ -84,20 +98,31 @@ export const poseToDirection = (pose) => {
84
98
  ov.set(pose.oX, pose.oY, pose.oZ, MathUtils.degToRad(pose.theta));
85
99
  return new Vector3(ov.x, ov.y, ov.z);
86
100
  };
87
- export const poseToMatrix = (pose) => {
101
+ export const isFinitePose = (pose) => Number.isFinite(pose.x) &&
102
+ Number.isFinite(pose.y) &&
103
+ Number.isFinite(pose.z) &&
104
+ Number.isFinite(pose.oX) &&
105
+ Number.isFinite(pose.oY) &&
106
+ Number.isFinite(pose.oZ) &&
107
+ Number.isFinite(pose.theta);
108
+ /**
109
+ * Build a TRS `Matrix4` (m) from a `Pose` (mm), writing into `matrix`.
110
+ */
111
+ export const poseToMatrix = (pose, matrix) => {
88
112
  ov.set(pose.oX, pose.oY, pose.oZ, MathUtils.degToRad(pose.theta));
89
113
  ov.toQuaternion(quaternion);
90
- const matrix = new Matrix4();
91
114
  matrix.makeRotationFromQuaternion(quaternion);
92
- matrix.setPosition(pose.x, pose.y, pose.z);
115
+ matrix.setPosition(pose.x * 0.001, pose.y * 0.001, pose.z * 0.001);
93
116
  return matrix;
94
117
  };
95
- export const matrixToPose = (matrix) => {
96
- const pose = createPose();
118
+ /**
119
+ * Decompose a `Matrix4` (m) into a `Pose` (mm), writing into `pose`.
120
+ */
121
+ export const matrixToPose = (matrix, pose) => {
97
122
  matrix.decompose(translation, quaternion, scale);
98
- pose.x = translation.x;
99
- pose.y = translation.y;
100
- pose.z = translation.z;
123
+ pose.x = translation.x * 1000;
124
+ pose.y = translation.y * 1000;
125
+ pose.z = translation.z * 1000;
101
126
  ov.setFromQuaternion(quaternion);
102
127
  pose.oX = ov.x;
103
128
  pose.oY = ov.y;
@@ -105,16 +130,25 @@ export const matrixToPose = (matrix) => {
105
130
  pose.theta = MathUtils.radToDeg(ov.th);
106
131
  return pose;
107
132
  };
108
- export const composeRenderedPose = (livePose, baselinePose, editedPose) => matrixToPose(poseToMatrix(livePose)
109
- .multiply(poseToMatrix(baselinePose).invert())
110
- .multiply(poseToMatrix(editedPose)));
111
- export const composeEditedPoseForRenderedPose = (baselinePose, livePose, renderedPose) => matrixToPose(poseToMatrix(baselinePose)
112
- .multiply(poseToMatrix(livePose).invert())
113
- .multiply(poseToMatrix(renderedPose)));
114
- export const isFinitePose = (pose) => Number.isFinite(pose.x) &&
115
- Number.isFinite(pose.y) &&
116
- Number.isFinite(pose.z) &&
117
- Number.isFinite(pose.oX) &&
118
- Number.isFinite(pose.oY) &&
119
- Number.isFinite(pose.oZ) &&
120
- Number.isFinite(pose.theta);
133
+ /**
134
+ * Compose the entity's local-to-parent transform: writes
135
+ * `live × baseline⁻¹ × edited` into `out`. Mirrors the formula
136
+ * `Frame.svelte` uses to blend live kinematics with user-staged edits;
137
+ * `worldMatrix.ts` premultiplies the result by the parent's `WorldMatrix`.
138
+ */
139
+ export const composeLocalMatrix = (live, baseline, edited, out) => {
140
+ matA.copy(baseline).invert();
141
+ out.copy(live).multiply(matA).multiply(edited);
142
+ return out;
143
+ };
144
+ /**
145
+ * Inverse of `composeLocalMatrix` for the transform controls path:
146
+ * writes `baseline × live⁻¹ × target` into `out`. Solves for the
147
+ * `EditedMatrix` that, blended through `composeLocalMatrix`, renders
148
+ * to `target`.
149
+ */
150
+ export const solveEditedMatrix = (baseline, live, target, out) => {
151
+ matA.copy(live).invert();
152
+ out.copy(baseline).multiply(matA).multiply(target);
153
+ return out;
154
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viamrobotics/motion-tools",
3
- "version": "1.26.1",
3
+ "version": "1.27.0",
4
4
  "description": "Motion visualization with Viam",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -25,8 +25,8 @@
25
25
  "@testing-library/jest-dom": "6.8.0",
26
26
  "@testing-library/svelte": "5.2.8",
27
27
  "@testing-library/user-event": "^14.6.1",
28
- "@threlte/core": "8.5.13",
29
- "@threlte/extras": "9.15.2",
28
+ "@threlte/core": "8.5.14",
29
+ "@threlte/extras": "9.17.0",
30
30
  "@threlte/rapier": "3.4.1",
31
31
  "@threlte/xr": "1.6.0",
32
32
  "@types/bun": "1.2.21",
@@ -1,13 +0,0 @@
1
- import type { Entity, QueryResult, Trait } from 'koota';
2
- export interface TreeNode {
3
- entity: Entity;
4
- parent?: TreeNode;
5
- children?: TreeNode[];
6
- }
7
- /**
8
- * Creates a tree representing parent child / relationships from a set of frames.
9
- */
10
- export declare const buildTreeNodes: (entities: QueryResult<[Trait]>) => {
11
- rootNodes: TreeNode[];
12
- nodeMap: Record<string, TreeNode | undefined>;
13
- };
@@ -1,48 +0,0 @@
1
- import { hierarchy, traits } from '../../../ecs';
2
- function sortNodes(nodes) {
3
- nodes.sort((a, b) => a.entity.get(traits.Name)?.localeCompare(b.entity.get(traits.Name) ?? '') ?? 0);
4
- }
5
- /**
6
- * Creates a tree representing parent child / relationships from a set of frames.
7
- */
8
- export const buildTreeNodes = (entities) => {
9
- const nodeMap = {};
10
- const rootNodes = [];
11
- const childNodes = [];
12
- for (const entity of entities) {
13
- const parent = hierarchy.getParentName(entity);
14
- const name = entity.get(traits.Name) ?? '';
15
- const node = { entity };
16
- nodeMap[name] = node;
17
- if (!parent || parent === 'world') {
18
- rootNodes.push(node);
19
- }
20
- else {
21
- childNodes.push(node);
22
- }
23
- }
24
- for (const node of childNodes) {
25
- const parent = hierarchy.getParentName(node.entity);
26
- if (parent) {
27
- const parentNode = nodeMap[parent];
28
- node.parent = parentNode;
29
- if (parentNode) {
30
- parentNode.children ??= [];
31
- parentNode.children?.push(node);
32
- }
33
- }
34
- }
35
- for (const node of rootNodes) {
36
- if (!node.children)
37
- continue;
38
- sortNodes(node.children);
39
- }
40
- for (const node of childNodes) {
41
- if (!node.children)
42
- continue;
43
- sortNodes(node.children);
44
- }
45
- sortNodes(rootNodes);
46
- sortNodes(childNodes);
47
- return { rootNodes, nodeMap };
48
- };