@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
@@ -16,11 +16,10 @@
16
16
  import type { Snippet } from 'svelte'
17
17
 
18
18
  import { T, type Props as ThrelteProps } from '@threlte/core'
19
- import { Portal, PortalTarget, type ThrelteGltf, useGltfAnimations } from '@threlte/extras'
19
+ import { type ThrelteGltf, useGltfAnimations } from '@threlte/extras'
20
20
  import { Group, type Object3D } from 'three'
21
21
 
22
- import { traits, useParentName, useTrait } from '../../ecs'
23
- import { poseToObject3d } from '../../transform'
22
+ import { traits, useTrait } from '../../ecs'
24
23
 
25
24
  import AxesHelper from '../AxesHelper.svelte'
26
25
  import { useEntityEvents } from './hooks/useEntityEvents.svelte'
@@ -34,11 +33,8 @@
34
33
 
35
34
  const { gltf, actions } = useGltfAnimations()
36
35
 
37
- const name = useTrait(() => entity, traits.Name)
38
- const parent = useParentName(() => entity)
39
- const pose = useTrait(() => entity, traits.Pose)
36
+ const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
40
37
  const gltfTrait = useTrait(() => entity, traits.GLTF)
41
- const scale = useTrait(() => entity, traits.Scale)
42
38
  const invisible = useTrait(() => entity, traits.Invisible)
43
39
  const showAxesHelper = useTrait(() => entity, traits.ShowAxesHelper)
44
40
  const events = useEntityEvents(() => entity)
@@ -46,10 +42,12 @@
46
42
  const animationName = $derived(gltfTrait.current?.animationName)
47
43
 
48
44
  const group = new Group()
45
+ group.matrixAutoUpdate = false
49
46
 
50
47
  $effect.pre(() => {
51
- if (pose.current) {
52
- poseToObject3d(pose.current, group)
48
+ if (worldMatrix.current) {
49
+ group.matrix.copy(worldMatrix.current)
50
+ group.updateMatrixWorld()
53
51
  }
54
52
  })
55
53
 
@@ -84,30 +82,23 @@
84
82
  })
85
83
  </script>
86
84
 
87
- <Portal id={parent.current}>
88
- <T is={group}>
89
- {#if showAxesHelper.current}
90
- <AxesHelper
91
- name={entity}
92
- width={3}
93
- length={0.1}
94
- />
95
- {/if}
96
- {#if $gltf}
97
- <T
98
- is={$gltf.scene as Object3D}
99
- scale={[scale.current?.x ?? 1, scale.current?.y ?? 1, scale.current?.z ?? 1]}
100
- name={entity}
101
- visible={invisible.current !== true}
102
- {...events}
103
- {...rest}
104
- >
105
- {@render children?.()}
106
-
107
- {#if name.current}
108
- <PortalTarget id={name.current} />
109
- {/if}
110
- </T>
111
- {/if}
112
- </T>
113
- </Portal>
85
+ <T is={group}>
86
+ {#if showAxesHelper.current}
87
+ <AxesHelper
88
+ name={entity}
89
+ width={3}
90
+ length={0.1}
91
+ />
92
+ {/if}
93
+ {#if $gltf}
94
+ <T
95
+ is={$gltf.scene as Object3D}
96
+ name={entity}
97
+ visible={invisible.current !== true}
98
+ {...events}
99
+ {...rest}
100
+ >
101
+ {@render children?.()}
102
+ </T>
103
+ {/if}
104
+ </T>
@@ -8,9 +8,9 @@ Renders a Viam Geometry object
8
8
  import type { Snippet } from 'svelte'
9
9
 
10
10
  import { T, useThrelte } from '@threlte/core'
11
- import { Portal } from '@threlte/extras'
11
+ import { Group } from 'three'
12
12
 
13
- import { traits, useParentName, useTrait } from '../../ecs'
13
+ import { traits, useTrait } from '../../ecs'
14
14
  import { use3DModels } from '../../hooks/use3DModels.svelte'
15
15
  import { useSettings } from '../../hooks/useSettings.svelte'
16
16
  import { poseToObject3d } from '../../transform'
@@ -31,7 +31,7 @@ Renders a Viam Geometry object
31
31
  const models = use3DModels()
32
32
 
33
33
  const name = useTrait(() => entity, traits.Name)
34
- const parent = useParentName(() => entity)
34
+ const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
35
35
  const center = useTrait(() => entity, traits.Center)
36
36
  const invisible = useTrait(() => entity, traits.Invisible)
37
37
 
@@ -52,6 +52,16 @@ Renders a Viam Geometry object
52
52
  return models.current[componentName]?.[id]?.clone() ?? undefined
53
53
  })
54
54
 
55
+ const group = new Group()
56
+ group.matrixAutoUpdate = false
57
+
58
+ $effect.pre(() => {
59
+ if (!worldMatrix.current) return
60
+ group.matrix.copy(worldMatrix.current)
61
+ group.updateMatrixWorld()
62
+ invalidate()
63
+ })
64
+
55
65
  $effect.pre(() => {
56
66
  if (model && center.current) {
57
67
  poseToObject3d(center.current, model)
@@ -62,24 +72,25 @@ Renders a Viam Geometry object
62
72
  const events = useEntityEvents(() => entity)
63
73
  </script>
64
74
 
65
- <Portal id={parent.current}>
66
- <T.Group visible={invisible.current !== true}>
67
- {#if model}
68
- <T
69
- is={model}
70
- name={entity}
71
- {...events}
72
- />
73
- {/if}
74
-
75
- {#if settings.current.renderArmModels.includes('colliders') || !model}
76
- <Mesh
77
- {entity}
78
- center={center.current}
79
- {...events}
80
- >
81
- {@render children?.()}
82
- </Mesh>
83
- {/if}
84
- </T.Group>
85
- </Portal>
75
+ <T
76
+ is={group}
77
+ visible={invisible.current !== true}
78
+ >
79
+ {#if model}
80
+ <T
81
+ is={model}
82
+ name={entity}
83
+ {...events}
84
+ />
85
+ {/if}
86
+
87
+ {#if settings.current.renderArmModels.includes('colliders') || !model}
88
+ <Mesh
89
+ {entity}
90
+ center={center.current}
91
+ {...events}
92
+ >
93
+ {@render children?.()}
94
+ </Mesh>
95
+ {/if}
96
+ </T>
@@ -3,12 +3,11 @@
3
3
  import type { Snippet } from 'svelte'
4
4
 
5
5
  import { T, useThrelte } from '@threlte/core'
6
- import { meshBounds, Portal, PortalTarget } from '@threlte/extras'
6
+ import { meshBounds } from '@threlte/extras'
7
7
  import { Line2, LineMaterial } from 'three/examples/jsm/Addons.js'
8
8
 
9
9
  import { isVertexColors, STRIDE } from '../../buffer'
10
- import { traits, useParentName, useTrait } from '../../ecs'
11
- import { poseToObject3d } from '../../transform'
10
+ import { traits, useTrait } from '../../ecs'
12
11
 
13
12
  import AxesHelper from '../AxesHelper.svelte'
14
13
  import { useEntityEvents } from './hooks/useEntityEvents.svelte'
@@ -24,8 +23,7 @@
24
23
 
25
24
  const { invalidate } = useThrelte()
26
25
  const name = useTrait(() => entity, traits.Name)
27
- const parent = useParentName(() => entity)
28
- const pose = useTrait(() => entity, traits.Pose)
26
+ const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
29
27
  const color = useTrait(() => entity, traits.Color)
30
28
  const colors = useTrait(() => entity, traits.Colors)
31
29
  const dotColors = useTrait(() => entity, traits.DotColors)
@@ -63,48 +61,48 @@
63
61
  const currentOpacity = $derived(opacity.current ?? 0.7)
64
62
 
65
63
  const mesh = new Line2()
64
+ mesh.matrixAutoUpdate = false
66
65
 
67
66
  $effect.pre(() => {
68
- if (pose.current) {
69
- poseToObject3d(pose.current, mesh)
67
+ if (worldMatrix.current) {
68
+ mesh.matrix.copy(worldMatrix.current)
69
+ mesh.updateMatrixWorld()
70
70
  invalidate()
71
71
  }
72
72
  })
73
73
  </script>
74
74
 
75
- <Portal id={parent.current}>
75
+ <T
76
+ is={mesh}
77
+ name={entity}
78
+ userData.name={name}
79
+ raycast={meshBounds}
80
+ renderOrder={renderOrder.current}
81
+ visible={invisible.current !== true}
82
+ {...events}
83
+ >
84
+ <LineGeometry
85
+ positions={linePositions.current}
86
+ colors={lineColors}
87
+ />
76
88
  <T
77
- is={mesh}
78
- name={entity}
79
- userData.name={name}
80
- raycast={meshBounds}
81
- renderOrder={renderOrder.current}
82
- visible={invisible.current !== true}
83
- {...events}
84
- >
85
- <LineGeometry
86
- positions={linePositions.current}
87
- colors={lineColors}
88
- />
89
- <T
90
- is={LineMaterial}
91
- color={hasVertexColors ? [1, 1, 1] : lineColor}
92
- vertexColors={hasVertexColors}
93
- transparent={currentOpacity < 1}
94
- depthWrite={currentOpacity === 1}
95
- opacity={currentOpacity}
96
- worldUnits={!screenSpace.current}
97
- linewidth={(lineWidth.current ?? 5) * (screenSpace.current ? 1 : 0.001)}
98
- depthTest={materialProps.current?.depthTest ?? true}
89
+ is={LineMaterial}
90
+ color={hasVertexColors ? [1, 1, 1] : lineColor}
91
+ vertexColors={hasVertexColors}
92
+ transparent={currentOpacity < 1}
93
+ depthWrite={currentOpacity === 1}
94
+ opacity={currentOpacity}
95
+ worldUnits={!screenSpace.current}
96
+ linewidth={(lineWidth.current ?? 5) * (screenSpace.current ? 1 : 0.001)}
97
+ depthTest={materialProps.current?.depthTest ?? true}
98
+ />
99
+ {#if showAxesHelper.current}
100
+ <AxesHelper
101
+ name={entity}
102
+ width={3}
103
+ length={0.1}
99
104
  />
100
- {#if showAxesHelper.current}
101
- <AxesHelper
102
- name={entity}
103
- width={3}
104
- length={0.1}
105
- />
106
- {/if}
107
- </T>
105
+ {/if}
108
106
 
109
107
  {#if linePositions.current && dotSize.current}
110
108
  <LineDots
@@ -115,9 +113,5 @@
115
113
  />
116
114
  {/if}
117
115
 
118
- {#if name.current}
119
- <PortalTarget id={name.current} />
120
- {/if}
121
-
122
116
  {@render children?.()}
123
- </Portal>
117
+ </T>
@@ -137,9 +137,18 @@
137
137
  renderOrder={renderOrder.current}
138
138
  {...rest}
139
139
  >
140
- {#if box.current}
140
+ {#if box.current || sphere.current}
141
+ {@const meshGeometry = box.current ? unitBox : unitSphere}
142
+ {@const edgesGeometry = box.current ? unitBoxEdges : unitSphereEdges}
143
+ <!--
144
+ Switch via a derived `is` on the same <T> so `useAttach`'s effect
145
+ cleanup runs before the new attach. Splitting these across two
146
+ branches of an {#if}/{:else if} races mount-new against unmount-old:
147
+ the new attach saves `mesh.geometry`, then the old cleanup restores
148
+ it to the pre-attach value (null), leaving the mesh geometryless.
149
+ -->
141
150
  <T
142
- is={unitBox}
151
+ is={meshGeometry}
143
152
  dispose={false}
144
153
  />
145
154
  <T.LineSegments
@@ -147,22 +156,7 @@
147
156
  bvh={{ enabled: false }}
148
157
  >
149
158
  <T
150
- is={unitBoxEdges}
151
- dispose={false}
152
- />
153
- <T.LineBasicMaterial color={darkenColor(color, 10)} />
154
- </T.LineSegments>
155
- {:else if sphere.current}
156
- <T
157
- is={unitSphere}
158
- dispose={false}
159
- />
160
- <T.LineSegments
161
- raycast={() => null}
162
- bvh={{ enabled: false }}
163
- >
164
- <T
165
- is={unitSphereEdges}
159
+ is={edgesGeometry}
166
160
  dispose={false}
167
161
  />
168
162
  <T.LineBasicMaterial color={darkenColor(color, 10)} />
@@ -3,13 +3,11 @@
3
3
  import type { Snippet } from 'svelte'
4
4
 
5
5
  import { T, useTask, useThrelte } from '@threlte/core'
6
- import { Portal } from '@threlte/extras'
7
6
  import { OrthographicCamera, Points, PointsMaterial } from 'three'
8
7
 
9
8
  import { asColor, isSingleColor } from '../../buffer'
10
- import { traits, useParentName, useTrait } from '../../ecs'
9
+ import { traits, useTrait } from '../../ecs'
11
10
  import { useSettings } from '../../hooks/useSettings.svelte'
12
- import { poseToObject3d } from '../../transform'
13
11
 
14
12
  import AxesHelper from '../AxesHelper.svelte'
15
13
  import { useEntityEvents } from './hooks/useEntityEvents.svelte'
@@ -24,8 +22,7 @@
24
22
  const { camera } = useThrelte()
25
23
  const settings = useSettings()
26
24
 
27
- const parent = useParentName(() => entity)
28
- const pose = useTrait(() => entity, traits.Pose)
25
+ const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
29
26
  const geometry = useTrait(() => entity, traits.BufferGeometry)
30
27
  const entityColor = useTrait(() => entity, traits.Color)
31
28
  const colors = useTrait(() => entity, traits.Colors)
@@ -42,6 +39,7 @@
42
39
  const orthographic = $derived(settings.current.cameraMode === 'orthographic')
43
40
 
44
41
  const points = new Points()
42
+ points.matrixAutoUpdate = false
45
43
  const material = points.material as PointsMaterial
46
44
  material.toneMapped = false
47
45
 
@@ -98,8 +96,9 @@
98
96
  })
99
97
 
100
98
  $effect.pre(() => {
101
- if (pose.current) {
102
- poseToObject3d(pose.current, points)
99
+ if (worldMatrix.current) {
100
+ points.matrix.copy(worldMatrix.current)
101
+ points.updateMatrixWorld()
103
102
  }
104
103
  })
105
104
 
@@ -125,25 +124,23 @@
125
124
  </script>
126
125
 
127
126
  {#if geometry.current}
128
- <Portal id={parent.current}>
129
- <T
130
- is={points}
131
- name={entity}
132
- bvh={{ maxDepth: 40, maxLeafSize: 20 }}
133
- visible={invisible.current !== true}
134
- renderOrder={renderOrder.current}
135
- {...events}
136
- >
137
- <T is={geometry.current} />
138
- <T is={material} />
139
- {#if showAxesHelper.current}
140
- <AxesHelper
141
- name={entity}
142
- width={3}
143
- length={0.1}
144
- />
145
- {/if}
146
- {@render children?.()}
147
- </T>
148
- </Portal>
127
+ <T
128
+ is={points}
129
+ name={entity}
130
+ bvh={{ maxDepth: 40, maxLeafSize: 20 }}
131
+ visible={invisible.current !== true}
132
+ renderOrder={renderOrder.current}
133
+ {...events}
134
+ >
135
+ <T is={geometry.current} />
136
+ <T is={material} />
137
+ {#if showAxesHelper.current}
138
+ <AxesHelper
139
+ name={entity}
140
+ width={3}
141
+ length={0.1}
142
+ />
143
+ {/if}
144
+ {@render children?.()}
145
+ </T>
149
146
  {/if}
@@ -1,52 +1,45 @@
1
1
  <script lang="ts">
2
- import type { Pose } from '@viamrobotics/sdk'
3
2
  import type { Entity } from 'koota'
4
3
  import type { Snippet } from 'svelte'
5
4
 
5
+ import { Matrix4 } from 'three'
6
+
6
7
  import { traits, useParentName, useTrait } from '../../ecs'
7
- import { usePartConfig } from '../../hooks/usePartConfig.svelte'
8
8
  import { usePose } from '../../hooks/usePose.svelte'
9
- import { composeRenderedPose } from '../../transform'
9
+ import { poseToMatrix } from '../../transform'
10
10
 
11
11
  interface Props {
12
12
  entity: Entity
13
- children: Snippet<[{ pose: Pose | undefined }]>
13
+ children: Snippet
14
14
  }
15
15
  let { entity, children }: Props = $props()
16
16
 
17
- const partConfig = usePartConfig()
18
17
  const name = useTrait(() => entity, traits.Name)
19
18
  const parent = useParentName(() => entity)
20
- const editedPose = useTrait(() => entity, traits.EditedPose)
21
- const entityPose = useTrait(() => entity, traits.Pose)
22
19
 
23
20
  const pose = usePose(
24
21
  () => name.current,
25
22
  () => parent.current
26
23
  )
27
24
 
25
+ /**
26
+ * Mirror the robot's live kinematics-resolved pose into LiveMatrix so
27
+ * Frame.svelte can compose the rendered transform via
28
+ * `composeLocalMatrix(live, baseline, edited)`. Mutate the stored
29
+ * `Matrix4` in place when present and notify via `entity.changed` —
30
+ * allocate only on first add.
31
+ */
28
32
  $effect.pre(() => {
29
33
  if (pose.current === undefined) return
30
34
 
31
- if (entity.has(traits.LivePose)) {
32
- entity.set(traits.LivePose, pose.current)
35
+ const live = entity.get(traits.LiveMatrix)
36
+ if (live) {
37
+ poseToMatrix(pose.current, live)
38
+ entity.changed(traits.LiveMatrix)
33
39
  } else {
34
- entity.add(traits.LivePose(pose.current))
40
+ entity.add(traits.LiveMatrix(poseToMatrix(pose.current, new Matrix4())))
35
41
  }
36
42
  })
37
-
38
- // Always render through the live blend: live × network⁻¹ × edited. With
39
- // `edited === network` (no edits) this collapses to `live`, so the rendered
40
- // pose tracks the robot's kinematics-resolved position. With edits, the
41
- // formula composes the staged delta on top of live. Input handlers that
42
- // drive edits (gizmo onChange, Details panel) compute `edited` such that
43
- // the blend renders to the user's intent.
44
- const resolvedPose = $derived.by(() => {
45
- if (pose.current === undefined || partConfig.hasPendingSave) return editedPose.current
46
- if (!entityPose.current || !editedPose.current) return undefined
47
-
48
- return composeRenderedPose(pose.current, entityPose.current, editedPose.current)
49
- })
50
43
  </script>
51
44
 
52
- {@render children({ pose: resolvedPose })}
45
+ {@render children()}
@@ -1,11 +1,8 @@
1
- import type { Pose } from '@viamrobotics/sdk';
2
1
  import type { Entity } from 'koota';
3
2
  import type { Snippet } from 'svelte';
4
3
  interface Props {
5
4
  entity: Entity;
6
- children: Snippet<[{
7
- pose: Pose | undefined;
8
- }]>;
5
+ children: Snippet;
9
6
  }
10
7
  declare const Pose: import("svelte").Component<Props, {}, "">;
11
8
  type Pose = ReturnType<typeof Pose>;
@@ -1,9 +1,18 @@
1
1
  import { useCursor } from '@threlte/extras';
2
- import { Vector2 } from 'three';
2
+ import { MathUtils, Matrix4, Quaternion, Vector2 } from 'three';
3
3
  import { traits, useTrait } from '../../../ecs';
4
4
  import { useFocusedEntity, useSelectedEntity } from '../../../hooks/useSelection.svelte';
5
5
  import { updateHoverInfo } from '../../../HoverUpdater.svelte';
6
- import { createPose, matrixToPose, poseToMatrix } from '../../../transform';
6
+ import { OrientationVector } from '../../../three/OrientationVector';
7
+ const tempHoverMatrix = new Matrix4();
8
+ const hoverQuat = new Quaternion();
9
+ const hoverOv = new OrientationVector();
10
+ const infoToLocalMatrix = (info, out) => {
11
+ hoverOv.set(info.oX, info.oY, info.oZ, MathUtils.degToRad(info.theta));
12
+ hoverOv.toQuaternion(hoverQuat);
13
+ out.makeRotationFromQuaternion(hoverQuat);
14
+ out.setPosition(info.x, info.y, info.z);
15
+ };
7
16
  export const useEntityEvents = (entity) => {
8
17
  const down = new Vector2();
9
18
  const selectedEntity = useSelectedEntity();
@@ -19,15 +28,18 @@ export const useEntityEvents = (entity) => {
19
28
  if (currentEntity && !currentEntity.has(traits.Hovered)) {
20
29
  const hoverInfo = updateHoverInfo(currentEntity, event);
21
30
  if (hoverInfo) {
22
- currentEntity.add(traits.InstancedPose({
31
+ infoToLocalMatrix(hoverInfo, tempHoverMatrix);
32
+ const worldMatrix = currentEntity.get(traits.WorldMatrix);
33
+ const composed = new Matrix4();
34
+ if (worldMatrix) {
35
+ composed.copy(worldMatrix).multiply(tempHoverMatrix);
36
+ }
37
+ else {
38
+ composed.copy(tempHoverMatrix);
39
+ }
40
+ currentEntity.add(traits.InstancedMatrix({
41
+ matrix: composed,
23
42
  index: hoverInfo.index,
24
- x: hoverInfo.x,
25
- y: hoverInfo.y,
26
- z: hoverInfo.z,
27
- oX: hoverInfo.oX,
28
- oY: hoverInfo.oY,
29
- oZ: hoverInfo.oZ,
30
- theta: hoverInfo.theta,
31
43
  }));
32
44
  }
33
45
  currentEntity.add(traits.Hovered);
@@ -40,34 +52,21 @@ export const useEntityEvents = (entity) => {
40
52
  const currentEntity = entity();
41
53
  if (currentEntity?.has(traits.Hovered)) {
42
54
  const hoverInfo = updateHoverInfo(currentEntity, event);
43
- const hoverPose = createPose(hoverInfo
44
- ? {
45
- x: hoverInfo.x,
46
- y: hoverInfo.y,
47
- z: hoverInfo.z,
48
- oX: 0,
49
- oY: 0,
50
- oZ: 1,
51
- theta: 0,
52
- }
53
- : undefined);
54
- const worldPose = currentEntity.get(traits.WorldPose) ?? createPose();
55
- const hoverPoseMatrix = poseToMatrix(hoverPose);
56
- const worldPoseMatrix = poseToMatrix(worldPose);
57
- const resultMatrix = worldPoseMatrix.multiply(hoverPoseMatrix);
58
- const resultPose = matrixToPose(resultMatrix);
59
- if (hoverInfo) {
60
- currentEntity.set(traits.InstancedPose, {
61
- index: hoverInfo.index,
62
- x: resultPose.x,
63
- y: resultPose.y,
64
- z: resultPose.z,
65
- oX: resultPose.oX,
66
- oY: resultPose.oY,
67
- oZ: resultPose.oZ,
68
- theta: resultPose.theta,
69
- });
55
+ if (!hoverInfo)
56
+ return;
57
+ infoToLocalMatrix(hoverInfo, tempHoverMatrix);
58
+ const instanced = currentEntity.get(traits.InstancedMatrix);
59
+ if (!instanced)
60
+ return;
61
+ const worldMatrix = currentEntity.get(traits.WorldMatrix);
62
+ if (worldMatrix) {
63
+ instanced.matrix.copy(worldMatrix).multiply(tempHoverMatrix);
64
+ }
65
+ else {
66
+ instanced.matrix.copy(tempHoverMatrix);
70
67
  }
68
+ instanced.index = hoverInfo.index;
69
+ currentEntity.changed(traits.InstancedMatrix);
71
70
  }
72
71
  };
73
72
  const onpointerleave = (event) => {
@@ -77,8 +76,8 @@ export const useEntityEvents = (entity) => {
77
76
  if (currentEntity?.has(traits.Hovered)) {
78
77
  currentEntity.remove(traits.Hovered);
79
78
  }
80
- if (currentEntity?.has(traits.InstancedPose)) {
81
- currentEntity.remove(traits.InstancedPose);
79
+ if (currentEntity?.has(traits.InstancedMatrix)) {
80
+ currentEntity.remove(traits.InstancedMatrix);
82
81
  }
83
82
  };
84
83
  const ondblclick = (event) => {
@@ -109,8 +108,8 @@ export const useEntityEvents = (entity) => {
109
108
  if (currentEntity?.has(traits.Hovered)) {
110
109
  currentEntity.remove(traits.Hovered);
111
110
  }
112
- if (currentEntity?.has(traits.InstancedPose)) {
113
- currentEntity.remove(traits.InstancedPose);
111
+ if (currentEntity?.has(traits.InstancedMatrix)) {
112
+ currentEntity.remove(traits.InstancedMatrix);
114
113
  }
115
114
  }
116
115
  });
@@ -51,7 +51,13 @@
51
51
  enabled.set(settings.current.interactionMode === 'navigate')
52
52
  })
53
53
 
54
- bvh(raycaster, () => ({ helper: false }))
54
+ const bvhEnabled = $derived(
55
+ settings.current.renderSubEntityHoverDetail ||
56
+ settings.current.interactionMode === 'measure' ||
57
+ settings.current.interactionMode === 'select'
58
+ )
59
+
60
+ bvh(raycaster, () => ({ helper: false, enabled: bvhEnabled }))
55
61
 
56
62
  const focusedObject = $derived(focusedObject3d.current)
57
63
 
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte'
3
3
 
4
- import { provideHierarchy } from '../ecs'
4
+ import { provideHierarchy, provideWorldMatrix } from '../ecs'
5
5
  import { provide3DModels } from '../hooks/use3DModels.svelte'
6
6
  import { provideArmClient } from '../hooks/useArmClient.svelte'
7
7
  import { provideArmKinematics } from '../hooks/useArmKinematics.svelte'
@@ -43,6 +43,7 @@
43
43
  provideLogs()
44
44
 
45
45
  provideHierarchy()
46
+ provideWorldMatrix()
46
47
  provideOrigin()
47
48
  provideDrawAPI()
48
49
  provideRelationships()