@viamrobotics/motion-tools 0.9.3 → 0.9.5

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 (37) hide show
  1. package/README.md +2 -1
  2. package/dist/WorldObject.d.ts +20 -1
  3. package/dist/WorldObject.js +5 -0
  4. package/dist/components/App.svelte +15 -1
  5. package/dist/components/App.svelte.d.ts +1 -0
  6. package/dist/components/AxesHelper.svelte +1 -0
  7. package/dist/components/CameraControls.svelte +9 -1
  8. package/dist/components/DotSprite.svelte +48 -33
  9. package/dist/components/DotSprite.svelte.d.ts +3 -2
  10. package/dist/components/Focus.svelte +36 -3
  11. package/dist/components/Geometry.svelte +5 -1
  12. package/dist/components/MeasureTool.svelte +38 -55
  13. package/dist/components/Pointcloud.svelte +1 -3
  14. package/dist/components/PointerMissBox.svelte +7 -1
  15. package/dist/components/PointerMissBox.svelte.d.ts +2 -17
  16. package/dist/components/Scene.svelte +24 -15
  17. package/dist/components/SceneProviders.svelte +1 -2
  18. package/dist/components/Selected.svelte +1 -0
  19. package/dist/components/Tree/Settings.svelte +3 -0
  20. package/dist/components/Tree/TreeContainer.svelte +3 -1
  21. package/dist/components/Tree/buildTree.d.ts +4 -1
  22. package/dist/components/Tree/buildTree.js +23 -1
  23. package/dist/components/WorldObjects.svelte +15 -5
  24. package/dist/components/WorldState.svelte +28 -0
  25. package/dist/components/WorldState.svelte.d.ts +7 -0
  26. package/dist/components/portal/usePortalContext.svelte.js +2 -5
  27. package/dist/hooks/useMouseRaycaster.svelte.d.ts +17 -0
  28. package/dist/hooks/useMouseRaycaster.svelte.js +108 -0
  29. package/dist/hooks/useObjectEvents.svelte.js +1 -12
  30. package/dist/hooks/useSelection.svelte.js +12 -2
  31. package/dist/hooks/useSettings.svelte.d.ts +4 -2
  32. package/dist/hooks/useSettings.svelte.js +6 -2
  33. package/dist/hooks/useWorldState.svelte.d.ts +19 -0
  34. package/dist/hooks/useWorldState.svelte.js +97 -0
  35. package/package.json +42 -40
  36. package/dist/components/Labels.svelte +0 -0
  37. package/dist/components/Labels.svelte.d.ts +0 -26
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## motion-tools
2
2
 
3
- `motion-tools` is visualizer for motion-related monitoring, testing, and debugging.
3
+ `motion-tools` aims to provide a visualization interface for any spatial information using Viam's APIs. This typically means motion-related monitoring, testing, and debugging.
4
4
 
5
5
  ### Getting started
6
6
 
@@ -30,6 +30,7 @@ VITE_CONFIGS='
30
30
  "signalingAddress": "https://app.viam.com:443"
31
31
  }
32
32
  }
33
+ '
33
34
  ```
34
35
 
35
36
  ### Executing drawing commands
@@ -1,4 +1,4 @@
1
- import type { Geometry, Pose } from '@viamrobotics/sdk';
1
+ import type { Geometry, Pose, TransformWithUUID } from '@viamrobotics/sdk';
2
2
  import { BatchedMesh, Box3, Object3D, Vector3, type ColorRepresentation } from 'three';
3
3
  export type PointsGeometry = {
4
4
  case: 'points';
@@ -34,3 +34,22 @@ export declare class WorldObject<T extends Geometries = Geometries> {
34
34
  metadata: Metadata;
35
35
  constructor(name: string, pose?: Pose, parent?: string, geometry?: T, metadata?: Metadata);
36
36
  }
37
+ export declare const fromTransform: (transform: TransformWithUUID) => WorldObject<{
38
+ case: undefined;
39
+ value?: undefined;
40
+ } | {
41
+ case: "sphere";
42
+ value: import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Sphere>;
43
+ } | {
44
+ case: "box";
45
+ value: import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").RectangularPrism>;
46
+ } | {
47
+ case: "capsule";
48
+ value: import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Capsule>;
49
+ } | {
50
+ case: "mesh";
51
+ value: import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Mesh>;
52
+ } | {
53
+ case: "pointcloud";
54
+ value: import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").PointCloud>;
55
+ }>;
@@ -16,3 +16,8 @@ export class WorldObject {
16
16
  this.metadata = metadata ?? {};
17
17
  }
18
18
  }
19
+ export const fromTransform = (transform) => {
20
+ const metadata = { ...transform.metadata?.fields };
21
+ const worldObject = new WorldObject(transform.referenceFrame, transform.poseInObserverFrame?.pose, transform.poseInObserverFrame?.referenceFrame, transform.physicalObject?.geometryType, metadata);
22
+ return worldObject;
23
+ };
@@ -1,6 +1,8 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte'
3
3
  import { Canvas } from '@threlte/core'
4
+ import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools'
5
+
4
6
  import Scene from './Scene.svelte'
5
7
  import TreeContainer from './Tree/TreeContainer.svelte'
6
8
  import Details from './Details.svelte'
@@ -10,19 +12,31 @@
10
12
  import { createPartIDContext } from '../hooks/usePartID.svelte'
11
13
  import Dashboard from './dashboard/Dashboard.svelte'
12
14
  import { domPortal } from '../portal'
15
+ import { provideSettings } from '../hooks/useSettings.svelte'
13
16
 
14
17
  interface Props {
15
18
  partID?: string
19
+ enableKeybindings?: boolean
16
20
  children?: Snippet
17
21
  }
18
22
 
19
- let { partID = '', children: appChildren }: Props = $props()
23
+ let { partID = '', enableKeybindings = true, children: appChildren }: Props = $props()
24
+
25
+ const settings = provideSettings()
26
+
27
+ $effect(() => {
28
+ settings.current.enableKeybindings = enableKeybindings
29
+ })
20
30
 
21
31
  createPartIDContext(() => partID)
22
32
 
23
33
  let root = $state.raw<HTMLElement>()
24
34
  </script>
25
35
 
36
+ {#if settings.current.enableQueryDevtools}
37
+ <SvelteQueryDevtools initialIsOpen />
38
+ {/if}
39
+
26
40
  <div
27
41
  class="relative h-full w-full overflow-hidden"
28
42
  bind:this={root}
@@ -1,6 +1,7 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  interface Props {
3
3
  partID?: string;
4
+ enableKeybindings?: boolean;
4
5
  children?: Snippet;
5
6
  }
6
7
  declare const App: import("svelte").Component<Props, {}, "">;
@@ -66,6 +66,7 @@
66
66
  is={line}
67
67
  {...rest}
68
68
  raycast={() => null}
69
+ bvh={{ enabled: false }}
69
70
  >
70
71
  <T is={geometry} />
71
72
  <T
@@ -1,14 +1,19 @@
1
1
  <script lang="ts">
2
+ import { MathUtils } from 'three'
2
3
  import { CameraControls, type CameraControlsRef, Gizmo } from '@threlte/extras'
3
4
  import { useTransformControls } from '../hooks/useControls.svelte'
4
5
  import KeyboardControls from './KeyboardControls.svelte'
5
6
  import Portal from './portal/Portal.svelte'
6
7
  import Button from './dashboard/Button.svelte'
7
8
  import { useDrawAPI } from '../hooks/useDrawAPI.svelte'
9
+ import { useSettings } from '../hooks/useSettings.svelte'
8
10
 
11
+ const settings = useSettings()
9
12
  const drawAPI = useDrawAPI()
10
13
  const transformControls = useTransformControls()
11
14
 
15
+ const enableKeybindings = $derived(settings.current.enableKeybindings)
16
+
12
17
  let ref = $state.raw<CameraControlsRef>()
13
18
 
14
19
  $effect(() => {
@@ -22,6 +27,7 @@
22
27
 
23
28
  $effect(() => {
24
29
  if (ref) {
30
+ ;(window as unknown as { MathUtils: typeof MathUtils }).MathUtils = MathUtils
25
31
  ;(window as unknown as { cameraControls: CameraControlsRef }).cameraControls = ref
26
32
  }
27
33
  })
@@ -45,7 +51,9 @@
45
51
  enabled={!transformControls.active}
46
52
  >
47
53
  {#snippet children({ ref }: { ref: CameraControlsRef })}
48
- <KeyboardControls cameraControls={ref} />
54
+ {#if enableKeybindings}
55
+ <KeyboardControls cameraControls={ref} />
56
+ {/if}
49
57
  <Gizmo />
50
58
  {/snippet}
51
59
  </CameraControls>
@@ -1,44 +1,59 @@
1
- <script
2
- module
3
- lang="ts"
4
- >
1
+ <script lang="ts">
5
2
  import { T, type Props as ThrelteProps } from '@threlte/core'
6
- import { CanvasTexture, type Sprite, type ColorRepresentation } from 'three'
7
-
8
- const size = 128
9
- const canvas = new OffscreenCanvas(size, size)
10
- const ctx = canvas.getContext('2d')
3
+ import type { ColorRepresentation, Vector3Tuple, Group } from 'three'
4
+ import { HTML } from '@threlte/extras'
11
5
 
12
- if (ctx) {
13
- ctx.clearRect(0, 0, size, size)
14
- ctx.beginPath()
15
- ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2)
16
- ctx.fillStyle = 'white'
17
- ctx.fill()
18
- }
19
-
20
- const map = new CanvasTexture(canvas)
21
- </script>
22
-
23
- <script lang="ts">
24
- interface Props extends ThrelteProps<typeof Sprite> {
6
+ interface Props extends ThrelteProps<typeof Group> {
7
+ position: Vector3Tuple
25
8
  color?: ColorRepresentation
26
9
  opacity?: number
27
10
  }
28
11
 
29
- let { color, opacity = 1, ref = $bindable(), ...rest }: Props = $props()
12
+ let { position, color, opacity = 1, ref = $bindable(), ...rest }: Props = $props()
30
13
  </script>
31
14
 
32
- <T.Sprite
15
+ <T.Group
33
16
  bind:ref
34
- scale={0.05}
35
17
  {...rest}
18
+ {position}
36
19
  >
37
- <T.SpriteMaterial
38
- transparent
39
- depthTest={false}
40
- {map}
41
- {opacity}
42
- color={color ?? 'black'}
43
- />
44
- </T.Sprite>
20
+ <T.Mesh
21
+ bvh={{ enabled: false }}
22
+ raycast={() => null}
23
+ scale={0.01}
24
+ renderOrder={1}
25
+ >
26
+ <T.SphereGeometry />
27
+ <T.MeshBasicMaterial
28
+ color={color ?? 'black'}
29
+ transparent
30
+ depthTest={false}
31
+ {opacity}
32
+ />
33
+ </T.Mesh>
34
+
35
+ <HTML
36
+ class="pointer-events-none mb-2 w-16 -translate-x-1/2 -translate-y-[calc(100%+10px)] border border-black bg-white px-1 py-0.5 text-xs text-wrap"
37
+ >
38
+ <div class="flex justify-between">
39
+ <span class="text-subtle-2">x</span>
40
+ <div>
41
+ {position[0].toFixed(2)}<span class="text-subtle-2">m</span>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="flex justify-between">
46
+ <span class="text-subtle-2">y</span>
47
+ <div>
48
+ {position[1].toFixed(2)}<span class="text-subtle-2">m</span>
49
+ </div>
50
+ </div>
51
+
52
+ <div class="flex justify-between">
53
+ <span class="text-subtle-2">z</span>
54
+ <div>
55
+ {position[2].toFixed(2)}<span class="text-subtle-2">m</span>
56
+ </div>
57
+ </div>
58
+ </HTML>
59
+ </T.Group>
@@ -1,6 +1,7 @@
1
1
  import { type Props as ThrelteProps } from '@threlte/core';
2
- import { type Sprite, type ColorRepresentation } from 'three';
3
- interface Props extends ThrelteProps<typeof Sprite> {
2
+ import type { ColorRepresentation, Vector3Tuple, Group } from 'three';
3
+ interface Props extends ThrelteProps<typeof Group> {
4
+ position: Vector3Tuple;
4
5
  color?: ColorRepresentation;
5
6
  opacity?: number;
6
7
  }
@@ -2,7 +2,10 @@
2
2
  import { T } from '@threlte/core'
3
3
  import { TrackballControls, Gizmo } from '@threlte/extras'
4
4
  import { Box3, type Object3D, Vector3 } from 'three'
5
+ import { TrackballControls as ThreeTrackballControls } from 'three/examples/jsm/controls/TrackballControls.js'
5
6
  import Camera from './Camera.svelte'
7
+ import Portal from './portal/Portal.svelte'
8
+ import Button from './dashboard/Button.svelte'
6
9
 
7
10
  interface Props {
8
11
  object3d: Object3D
@@ -16,6 +19,8 @@
16
19
  let center = $state.raw<[number, number, number]>([0, 0, 0])
17
20
  let size = $state.raw<[number, number, number]>([0, 0, 0])
18
21
 
22
+ let controls = $state.raw<ThreeTrackballControls>()
23
+
19
24
  $effect.pre(() => {
20
25
  box.setFromObject(object3d)
21
26
  size = box.getSize(vec).toArray()
@@ -23,11 +28,39 @@
23
28
  })
24
29
  </script>
25
30
 
31
+ <Portal id="dashboard">
32
+ <fieldset>
33
+ <Button
34
+ active
35
+ icon="camera-outline"
36
+ description="Reset camera"
37
+ onclick={() => {
38
+ controls?.reset()
39
+ }}
40
+ />
41
+ </fieldset>
42
+ </Portal>
43
+
26
44
  <Camera position={[size[0] + 1, size[0] + 1, size[0] + 1]}>
27
- <TrackballControls target={center}>
45
+ <TrackballControls
46
+ bind:ref={controls}
47
+ target={center}
48
+ >
28
49
  <Gizmo />
29
50
  </TrackballControls>
30
51
  </Camera>
31
52
 
32
- <T is={object3d} />
33
- <T.BoxHelper args={[object3d, 'red']} />
53
+ <T
54
+ is={object3d}
55
+ bvh={{
56
+ enabled: object3d.type === 'Points',
57
+ maxDepth: 40,
58
+ maxLeafTris: 20,
59
+ }}
60
+ />
61
+
62
+ <T.BoxHelper
63
+ args={[object3d, 'red']}
64
+ bvh={{ enabled: false }}
65
+ raycast={() => null}
66
+ />
@@ -62,6 +62,7 @@
62
62
  {name}
63
63
  {uuid}
64
64
  {...rest}
65
+ bvh={{ enabled: false }}
65
66
  >
66
67
  {#if geometry?.case === 'mesh'}
67
68
  {@const mesh = geometry.value.mesh as Uint8Array<ArrayBuffer>}
@@ -112,7 +113,10 @@
112
113
  />
113
114
 
114
115
  {#if geo}
115
- <T.LineSegments raycast={() => null}>
116
+ <T.LineSegments
117
+ raycast={() => null}
118
+ bvh={{ enabled: false }}
119
+ >
116
120
  <T.EdgesGeometry args={[geo, 0]} />
117
121
  <T.LineBasicMaterial color={darkenColor(color, 10)} />
118
122
  </T.LineSegments>
@@ -1,41 +1,39 @@
1
1
  <script lang="ts">
2
2
  import { untrack } from 'svelte'
3
- import { Raycaster, Vector2, Vector3, type Intersection } from 'three'
4
- import { T, useThrelte, useTask } from '@threlte/core'
5
- import { HTML, MeshLineGeometry, MeshLineMaterial, useInteractivity } from '@threlte/extras'
3
+ import { Vector3, type Intersection } from 'three'
4
+ import { T } from '@threlte/core'
5
+ import { HTML, MeshLineGeometry, MeshLineMaterial } from '@threlte/extras'
6
6
  import { useSettings } from '../hooks/useSettings.svelte'
7
7
  import Button from './dashboard/Button.svelte'
8
8
  import Portal from './portal/Portal.svelte'
9
9
  import DotSprite from './DotSprite.svelte'
10
+ import { useMouseRaycaster } from '../hooks/useMouseRaycaster.svelte'
11
+ import { useFocused } from '../hooks/useSelection.svelte'
10
12
 
13
+ const focus = useFocused()
11
14
  const settings = useSettings()
12
- const { camera } = useThrelte()
13
- const interactivity = useInteractivity()
14
- const raycaster = new Raycaster()
15
15
 
16
16
  const htmlPosition = new Vector3()
17
- const pointerDown = new Vector2()
18
- const pointerUp = new Vector2()
19
17
 
20
18
  let step: 'idle' | 'p1' | 'p2' = 'idle'
21
19
 
22
- let intersection: Intersection | undefined
20
+ let intersection = $state.raw<Intersection>()
23
21
  let p1 = $state.raw<Vector3>()
24
22
  let p2 = $state.raw<Vector3>()
25
23
 
26
24
  const enabled = $derived(settings.current.enableMeasure)
27
25
 
28
- const onpointerdown = (event: PointerEvent) => {
29
- pointerDown.set(event.clientX, event.clientY)
30
- }
31
-
32
- const onpointerup = (event: PointerEvent) => {
33
- pointerUp.set(event.clientX, event.clientY)
26
+ const { onclick, onmove, raycaster } = useMouseRaycaster(() => ({
27
+ enabled,
28
+ }))
29
+ raycaster.firstHitOnly = true
30
+ raycaster.params.Points.threshold = 0.005
34
31
 
35
- if (pointerDown.distanceToSquared(pointerUp) > 0.1) {
36
- return
37
- }
32
+ onmove((event) => {
33
+ intersection = event.intersections[0]
34
+ })
38
35
 
36
+ onclick(() => {
39
37
  if (step === 'idle' && intersection) {
40
38
  p1 = intersection.point.clone()
41
39
  step = 'p1'
@@ -47,38 +45,18 @@
47
45
  p2 = undefined
48
46
  step = 'idle'
49
47
  }
50
- }
51
-
52
- const { start, stop } = useTask(
53
- () => {
54
- if (interactivity.hovered.size === 0) {
55
- return
56
- }
57
-
58
- for (const [, event] of interactivity.hovered) {
59
- raycaster.setFromCamera(interactivity.pointer.current, camera.current)
60
- intersection = raycaster.intersectObject(event.object)[0]
61
- }
62
- },
63
- { autoStart: false }
64
- )
65
-
66
- $effect(() => {
67
- if (!enabled) {
68
- untrack(() => {
69
- p1 = undefined
70
- p2 = undefined
71
- step = 'idle'
72
- })
73
- }
74
48
  })
75
49
 
50
+ const clear = () => {
51
+ p1 = undefined
52
+ p2 = undefined
53
+ step = 'idle'
54
+ }
55
+
76
56
  $effect(() => {
77
- if (enabled) {
78
- start()
79
- } else {
80
- stop()
81
- }
57
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
58
+ ;(focus.current, enabled)
59
+ untrack(() => clear())
82
60
  })
83
61
  </script>
84
62
 
@@ -96,12 +74,11 @@
96
74
  </fieldset>
97
75
  </Portal>
98
76
 
99
- <svelte:window
100
- onpointerdown={enabled ? onpointerdown : undefined}
101
- onpointerup={enabled ? onpointerup : undefined}
102
- />
103
-
104
77
  {#if enabled}
78
+ {#if intersection}
79
+ <DotSprite position={intersection?.point.toArray()} />
80
+ {/if}
81
+
105
82
  {#if p1}
106
83
  <DotSprite position={p1.toArray()} />
107
84
  {/if}
@@ -111,12 +88,18 @@
111
88
  {/if}
112
89
 
113
90
  {#if p1 && p2}
114
- <T.Mesh>
91
+ <T.Mesh
92
+ raycast={() => null}
93
+ bvh={{ enabled: false }}
94
+ renderOrder={1}
95
+ >
115
96
  <MeshLineGeometry points={[p1, p2]} />
116
97
  <MeshLineMaterial
117
- width={0.015}
98
+ width={2.5}
118
99
  depthTest={false}
119
100
  color="black"
101
+ attenuate={false}
102
+ transparent
120
103
  />
121
104
  </T.Mesh>
122
105
  <HTML
@@ -124,7 +107,7 @@
124
107
  position={htmlPosition.lerpVectors(p1, p2, 0.5).toArray()}
125
108
  >
126
109
  <div class="border border-black bg-white px-1 py-0.5 text-xs">
127
- {p1.distanceTo(p2).toFixed(2)}m
110
+ {p1.distanceTo(p2).toFixed(2)}<span class="text-subtle-2">m</span>
128
111
  </div>
129
112
  </HTML>
130
113
  {/if}
@@ -6,11 +6,9 @@
6
6
  PointsMaterial,
7
7
  OrthographicCamera,
8
8
  } from 'three'
9
-
10
9
  import { T, useTask, useThrelte } from '@threlte/core'
11
10
  import type { WorldObject } from '../WorldObject'
12
11
  import { useObjectEvents } from '../hooks/useObjectEvents.svelte'
13
- import { meshBounds } from '@threlte/extras'
14
12
  import { poseToObject3d } from '../transform'
15
13
  import { useSettings } from '../hooks/useSettings.svelte'
16
14
  import type { Snippet } from 'svelte'
@@ -85,8 +83,8 @@
85
83
  is={points}
86
84
  name={object.name}
87
85
  uuid={object.uuid}
88
- raycast={meshBounds}
89
86
  {...events}
87
+ bvh={{ maxDepth: 40, maxLeafTris: 20 }}
90
88
  >
91
89
  <T is={geometry} />
92
90
  <T is={material} />
@@ -1,19 +1,25 @@
1
1
  <script lang="ts">
2
- import { BackSide, Vector3 } from 'three'
2
+ import { BackSide, Mesh, Vector3 } from 'three'
3
3
  import { T, useThrelte } from '@threlte/core'
4
4
  import { MeshDiscardMaterial } from '@threlte/extras'
5
5
  import { useSelected } from '../hooks/useSelection.svelte'
6
6
  import { useTransformControls } from '../hooks/useControls.svelte'
7
+ import { useSettings } from '../hooks/useSettings.svelte'
7
8
 
8
9
  const { camera } = useThrelte()
10
+ const settings = useSettings()
9
11
  const selected = useSelected()
10
12
  const transformControls = useTransformControls()
11
13
  const cameraDown = new Vector3()
12
14
 
15
+ const enabled = $derived(!settings.current.enableMeasure)
16
+
13
17
  const size = 1_000
14
18
  </script>
15
19
 
16
20
  <T.Mesh
21
+ raycast={enabled ? Mesh.prototype.raycast : () => null}
22
+ bvh={{ enabled: false }}
17
23
  onpointerdown={() => {
18
24
  cameraDown.copy(camera.current.position)
19
25
  }}
@@ -1,18 +1,3 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- declare const PointerMissBox: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type PointerMissBox = InstanceType<typeof PointerMissBox>;
1
+ declare const PointerMissBox: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type PointerMissBox = ReturnType<typeof PointerMissBox>;
18
3
  export default PointerMissBox;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { Vector3 } from 'three'
3
3
  import { T } from '@threlte/core'
4
- import { Grid, interactivity, PerfMonitor } from '@threlte/extras'
4
+ import { Grid, interactivity, PerfMonitor, bvh } from '@threlte/extras'
5
5
  import { PortalTarget } from './portal'
6
6
  import WorldObjects from './WorldObjects.svelte'
7
7
  import Selected from './Selected.svelte'
@@ -24,7 +24,11 @@
24
24
 
25
25
  let { children }: Props = $props()
26
26
 
27
- interactivity({
27
+ const settings = useSettings()
28
+ const focusedObject3d = useFocusedObject3d()
29
+ const origin = useOrigin()
30
+
31
+ const { raycaster, enabled } = interactivity({
28
32
  filter: (items) => {
29
33
  const item = items.find((item) => {
30
34
  return item.object.visible === undefined || item.object.visible === true
@@ -33,12 +37,14 @@
33
37
  return item ? [item] : []
34
38
  },
35
39
  })
40
+ $effect.pre(() => {
41
+ enabled.set(!settings.current.enableMeasure)
42
+ })
43
+ raycaster.firstHitOnly = true
36
44
 
37
- const settings = useSettings()
38
- const focusedObject3d = useFocusedObject3d()
39
- const origin = useOrigin()
45
+ bvh(() => ({ helper: false }))
40
46
 
41
- const object3d = $derived(focusedObject3d.current)
47
+ const focusedObject = $derived(focusedObject3d.current)
42
48
 
43
49
  const { isPresenting } = useXR()
44
50
  </script>
@@ -52,8 +58,11 @@
52
58
  rotation.x={$isPresenting ? -Math.PI / 2 : 0}
53
59
  rotation.z={origin.rotation}
54
60
  >
55
- {#if object3d}
56
- <Focus {object3d} />
61
+ <PointerMissBox />
62
+ <MeasureTool />
63
+
64
+ {#if focusedObject}
65
+ <Focus object3d={focusedObject} />
57
66
  {:else}
58
67
  {#if !$isPresenting}
59
68
  <Camera position={[3, 3, 3]}>
@@ -61,18 +70,13 @@
61
70
  </Camera>
62
71
  {/if}
63
72
 
64
- <PortalTarget id="world" />
65
-
66
- <MeasureTool />
67
73
  <StaticGeometries />
68
-
69
- <WorldObjects />
70
- <PointerMissBox />
71
-
72
74
  <Selected />
73
75
 
74
76
  {#if !$isPresenting && settings.current.grid}
75
77
  <Grid
78
+ raycast={() => null}
79
+ bvh={{ enabled: false }}
76
80
  plane="xy"
77
81
  sectionColor="#333"
78
82
  infiniteGrid
@@ -84,6 +88,11 @@
84
88
  {/if}
85
89
  {/if}
86
90
 
91
+ <T.Group attach={focusedObject ? false : undefined}>
92
+ <PortalTarget id="world" />
93
+ <WorldObjects />
94
+ </T.Group>
95
+
87
96
  {@render children?.()}
88
97
 
89
98
  <T.DirectionalLight position={[3, 3, 3]} />
@@ -13,7 +13,6 @@
13
13
  import { provideObjects } from '../hooks/useObjects.svelte'
14
14
  import { provideMotionClient } from '../hooks/useMotionClient.svelte'
15
15
  import { provideLogs } from '../hooks/useLogs.svelte'
16
- import { provideSettings } from '../hooks/useSettings.svelte'
17
16
  import { provideOrigin } from './xr/useOrigin.svelte'
18
17
 
19
18
  interface Props {
@@ -24,7 +23,6 @@
24
23
 
25
24
  const partID = usePartID()
26
25
 
27
- provideSettings()
28
26
  provideTransformControls()
29
27
  provideVisibility()
30
28
  provideMachineSettings()
@@ -39,6 +37,7 @@
39
37
  providePointclouds(() => partID.current)
40
38
  provideMotionClient(() => partID.current)
41
39
  provideObjects()
40
+
42
41
  const { focus } = provideSelection()
43
42
  </script>
44
43
 
@@ -53,4 +53,5 @@
53
53
  <T
54
54
  is={box}
55
55
  raycast={() => null}
56
+ bvh={{ enabled: false }}
56
57
  />