@viamrobotics/motion-tools 0.1.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 (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -0
  3. package/dist/WorldObject.d.ts +33 -0
  4. package/dist/WorldObject.js +18 -0
  5. package/dist/assert.d.ts +14 -0
  6. package/dist/assert.js +21 -0
  7. package/dist/color.d.ts +8 -0
  8. package/dist/color.js +14 -0
  9. package/dist/components/App.svelte +63 -0
  10. package/dist/components/App.svelte.d.ts +8 -0
  11. package/dist/components/AxesHelper.svelte +17 -0
  12. package/dist/components/AxesHelper.svelte.d.ts +4 -0
  13. package/dist/components/BentPlaneGeometry.svelte +52 -0
  14. package/dist/components/BentPlaneGeometry.svelte.d.ts +29 -0
  15. package/dist/components/Camera.svelte +45 -0
  16. package/dist/components/Camera.svelte.d.ts +5 -0
  17. package/dist/components/CameraControls.svelte +82 -0
  18. package/dist/components/CameraControls.svelte.d.ts +9 -0
  19. package/dist/components/Details.svelte +161 -0
  20. package/dist/components/Details.svelte.d.ts +3 -0
  21. package/dist/components/Detections.svelte +41 -0
  22. package/dist/components/Detections.svelte.d.ts +3 -0
  23. package/dist/components/DetectionsPlane.svelte +23 -0
  24. package/dist/components/DetectionsPlane.svelte.d.ts +21 -0
  25. package/dist/components/DomPortal.svelte +20 -0
  26. package/dist/components/DomPortal.svelte.d.ts +5 -0
  27. package/dist/components/Focus.svelte +49 -0
  28. package/dist/components/Focus.svelte.d.ts +3 -0
  29. package/dist/components/Frame.svelte +112 -0
  30. package/dist/components/Frame.svelte.d.ts +16 -0
  31. package/dist/components/Frames.svelte +54 -0
  32. package/dist/components/Frames.svelte.d.ts +18 -0
  33. package/dist/components/Pointcloud.svelte +55 -0
  34. package/dist/components/Pointcloud.svelte.d.ts +10 -0
  35. package/dist/components/Pointclouds.svelte +21 -0
  36. package/dist/components/Pointclouds.svelte.d.ts +18 -0
  37. package/dist/components/Pose.svelte +19 -0
  38. package/dist/components/Pose.svelte.d.ts +12 -0
  39. package/dist/components/RefreshRate.svelte +47 -0
  40. package/dist/components/RefreshRate.svelte.d.ts +8 -0
  41. package/dist/components/Scene.svelte +81 -0
  42. package/dist/components/Scene.svelte.d.ts +7 -0
  43. package/dist/components/SceneProviders.svelte +41 -0
  44. package/dist/components/SceneProviders.svelte.d.ts +9 -0
  45. package/dist/components/Selected.svelte +44 -0
  46. package/dist/components/Selected.svelte.d.ts +3 -0
  47. package/dist/components/Shapes.svelte +49 -0
  48. package/dist/components/Shapes.svelte.d.ts +18 -0
  49. package/dist/components/StaticGeometries.svelte +79 -0
  50. package/dist/components/StaticGeometries.svelte.d.ts +18 -0
  51. package/dist/components/Tree/Settings.svelte +54 -0
  52. package/dist/components/Tree/Settings.svelte.d.ts +18 -0
  53. package/dist/components/Tree/Tree.svelte +204 -0
  54. package/dist/components/Tree/Tree.svelte.d.ts +10 -0
  55. package/dist/components/Tree/TreeContainer.svelte +70 -0
  56. package/dist/components/Tree/TreeContainer.svelte.d.ts +3 -0
  57. package/dist/components/Tree/buildTree.d.ts +11 -0
  58. package/dist/components/Tree/buildTree.js +29 -0
  59. package/dist/components/Tree/useExpanded.svelte.d.ts +5 -0
  60. package/dist/components/Tree/useExpanded.svelte.js +21 -0
  61. package/dist/components/WorldObject.svelte +27 -0
  62. package/dist/components/WorldObject.svelte.d.ts +11 -0
  63. package/dist/components/XR.svelte +20 -0
  64. package/dist/components/XR.svelte.d.ts +3 -0
  65. package/dist/components/models/README.md +5 -0
  66. package/dist/components/null-states/Connection.svelte +0 -0
  67. package/dist/components/null-states/Connection.svelte.d.ts +26 -0
  68. package/dist/components/portal/Portal.svelte +25 -0
  69. package/dist/components/portal/Portal.svelte.d.ts +8 -0
  70. package/dist/components/portal/PortalTarget.svelte +18 -0
  71. package/dist/components/portal/PortalTarget.svelte.d.ts +6 -0
  72. package/dist/components/portal/index.d.ts +2 -0
  73. package/dist/components/portal/index.js +2 -0
  74. package/dist/components/portal/usePortalContext.svelte.d.ts +5 -0
  75. package/dist/components/portal/usePortalContext.svelte.js +8 -0
  76. package/dist/components/xr/CameraFeed.svelte +81 -0
  77. package/dist/components/xr/CameraFeed.svelte.d.ts +6 -0
  78. package/dist/components/xr/Controllers.svelte +71 -0
  79. package/dist/components/xr/Controllers.svelte.d.ts +3 -0
  80. package/dist/components/xr/Draggable.svelte +101 -0
  81. package/dist/components/xr/Draggable.svelte.d.ts +11 -0
  82. package/dist/components/xr/HandCollider.svelte +19 -0
  83. package/dist/components/xr/HandCollider.svelte.d.ts +18 -0
  84. package/dist/components/xr/Hands.svelte +24 -0
  85. package/dist/components/xr/Hands.svelte.d.ts +18 -0
  86. package/dist/components/xr/OriginMarker.svelte +100 -0
  87. package/dist/components/xr/OriginMarker.svelte.d.ts +3 -0
  88. package/dist/components/xr/PointDistance.svelte +52 -0
  89. package/dist/components/xr/PointDistance.svelte.d.ts +3 -0
  90. package/dist/hooks/index.d.ts +1 -0
  91. package/dist/hooks/index.js +1 -0
  92. package/dist/hooks/useConnectionConfigs.svelte.d.ts +17 -0
  93. package/dist/hooks/useConnectionConfigs.svelte.js +40 -0
  94. package/dist/hooks/useControls.svelte.d.ts +7 -0
  95. package/dist/hooks/useControls.svelte.js +16 -0
  96. package/dist/hooks/useDraggable.svelte.d.ts +2 -0
  97. package/dist/hooks/useDraggable.svelte.js +25 -0
  98. package/dist/hooks/useFrames.svelte.d.ts +9 -0
  99. package/dist/hooks/useFrames.svelte.js +50 -0
  100. package/dist/hooks/useGeometries.svelte.d.ts +7 -0
  101. package/dist/hooks/useGeometries.svelte.js +58 -0
  102. package/dist/hooks/useMotionClient.svelte.d.ts +8 -0
  103. package/dist/hooks/useMotionClient.svelte.js +31 -0
  104. package/dist/hooks/useObjectEvents.svelte.d.ts +9 -0
  105. package/dist/hooks/useObjectEvents.svelte.js +31 -0
  106. package/dist/hooks/useObjects.svelte.d.ts +7 -0
  107. package/dist/hooks/useObjects.svelte.js +33 -0
  108. package/dist/hooks/usePartID.svelte.d.ts +6 -0
  109. package/dist/hooks/usePartID.svelte.js +14 -0
  110. package/dist/hooks/usePersistentUUIDs.svelte.d.ts +5 -0
  111. package/dist/hooks/usePersistentUUIDs.svelte.js +14 -0
  112. package/dist/hooks/usePointclouds.svelte.d.ts +7 -0
  113. package/dist/hooks/usePointclouds.svelte.js +58 -0
  114. package/dist/hooks/usePose.svelte.d.ts +3 -0
  115. package/dist/hooks/usePose.svelte.js +44 -0
  116. package/dist/hooks/usePoses.svelte.d.ts +12 -0
  117. package/dist/hooks/usePoses.svelte.js +63 -0
  118. package/dist/hooks/useRefreshRates.svelte.d.ts +5 -0
  119. package/dist/hooks/useRefreshRates.svelte.js +23 -0
  120. package/dist/hooks/useSelection.svelte.d.ts +40 -0
  121. package/dist/hooks/useSelection.svelte.js +92 -0
  122. package/dist/hooks/useShapes.svelte.d.ts +17 -0
  123. package/dist/hooks/useShapes.svelte.js +264 -0
  124. package/dist/hooks/useStaticGeometries.svelte.d.ts +9 -0
  125. package/dist/hooks/useStaticGeometries.svelte.js +37 -0
  126. package/dist/hooks/useVisibility.svelte.d.ts +5 -0
  127. package/dist/hooks/useVisibility.svelte.js +22 -0
  128. package/dist/hooks/xr/useAnchors.svelte.d.ts +0 -0
  129. package/dist/hooks/xr/useAnchors.svelte.js +1 -0
  130. package/dist/index.d.ts +1 -0
  131. package/dist/index.js +1 -0
  132. package/dist/keybindings.d.ts +12 -0
  133. package/dist/keybindings.js +12 -0
  134. package/dist/loaders/pcd/index.d.ts +4 -0
  135. package/dist/loaders/pcd/index.js +13 -0
  136. package/dist/loaders/pcd/worker.d.ts +1 -0
  137. package/dist/loaders/pcd/worker.js +26 -0
  138. package/dist/three/AxesHelper.d.ts +5 -0
  139. package/dist/three/AxesHelper.js +35 -0
  140. package/dist/three/BatchedArrow.d.ts +30 -0
  141. package/dist/three/BatchedArrow.js +126 -0
  142. package/dist/three/BoxHelper.d.ts +50 -0
  143. package/dist/three/BoxHelper.js +134 -0
  144. package/dist/three/CapsuleGeometry.d.ts +10 -0
  145. package/dist/three/CapsuleGeometry.js +17 -0
  146. package/dist/transform.d.ts +11 -0
  147. package/dist/transform.js +65 -0
  148. package/package.json +110 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Viam, INC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ ## Getting started
2
+
3
+ 1. [Install pnpm](https://pnpm.io/installation)
4
+ 2. [Install bun](https://bun.sh/docs/installation)
5
+ 3. Install dependencies: `pnpm i`
6
+ 4. Run local app server: `pnpm dev`
7
+
8
+ To visit the visualizer, go to `http://localhost:5173/`
9
+
10
+ Open the machine config page (bottom right) and enter in connection details to visualize a specific machine.
11
+
12
+ ## Todo
13
+
14
+ - animated sequence
15
+ - double click to set trackball center
16
+ - Give error logs
17
+ - default pointcloud color
18
+ - remote IP access
19
+ - ortho points are messed up size-wise
20
+ - geometries parented to parent
@@ -0,0 +1,33 @@
1
+ import type { Geometry, Pose } from '@viamrobotics/sdk';
2
+ import { Box3, Object3D, Vector3 } from 'three';
3
+ export type PointsGeometry = {
4
+ case: 'points';
5
+ value: Float32Array;
6
+ };
7
+ export type LinesGeometry = {
8
+ case: 'line';
9
+ value: Float32Array;
10
+ };
11
+ export type Geometries = Geometry['geometryType'] | PointsGeometry | LinesGeometry;
12
+ export type Metadata = {
13
+ colors?: Float32Array;
14
+ color?: string;
15
+ gltf?: {
16
+ scene: Object3D;
17
+ };
18
+ points?: Vector3[];
19
+ batched?: {
20
+ id: number;
21
+ name: string;
22
+ };
23
+ getBoundingBoxAt?: (box: Box3) => void;
24
+ };
25
+ export declare class WorldObject<T extends Geometries = Geometries> {
26
+ uuid: string;
27
+ name: string;
28
+ referenceFrame: string;
29
+ pose: Pose;
30
+ geometry?: T;
31
+ metadata: Metadata;
32
+ constructor(name: string, pose?: Pose, parent?: string, geometry?: T, metadata?: Metadata);
33
+ }
@@ -0,0 +1,18 @@
1
+ import { Box3, MathUtils, Object3D, Vector3 } from 'three';
2
+ import { createPose } from './transform';
3
+ export class WorldObject {
4
+ uuid;
5
+ name;
6
+ referenceFrame;
7
+ pose;
8
+ geometry;
9
+ metadata;
10
+ constructor(name, pose, parent = 'world', geometry, metadata) {
11
+ this.uuid = MathUtils.generateUUID();
12
+ this.name = name;
13
+ this.referenceFrame = parent;
14
+ this.pose = pose ?? createPose();
15
+ this.geometry = geometry;
16
+ this.metadata = metadata ?? {};
17
+ }
18
+ }
@@ -0,0 +1,14 @@
1
+ /** Assertion functions for runtime and type safety. */
2
+ export declare class AssertionError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /**
6
+ * Assert that a value is defined.
7
+ *
8
+ * @example
9
+ * const stringify = (value: number | undefined): number => {
10
+ * assertExists(value)
11
+ * return `${value}` // TS now knows that value is of type `number`
12
+ * }
13
+ */
14
+ export declare const assertExists: <T>(value: T, message: string) => asserts value is NonNullable<T>;
package/dist/assert.js ADDED
@@ -0,0 +1,21 @@
1
+ /** Assertion functions for runtime and type safety. */
2
+ export class AssertionError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = 'AssertionError';
6
+ }
7
+ }
8
+ /**
9
+ * Assert that a value is defined.
10
+ *
11
+ * @example
12
+ * const stringify = (value: number | undefined): number => {
13
+ * assertExists(value)
14
+ * return `${value}` // TS now knows that value is of type `number`
15
+ * }
16
+ */
17
+ export const assertExists = (value, message) => {
18
+ if (value === null || value === undefined) {
19
+ throw new AssertionError(message);
20
+ }
21
+ };
@@ -0,0 +1,8 @@
1
+ import { Color, type ColorRepresentation } from 'three';
2
+ /**
3
+ * Darkens a THREE.Color by a given percentage while preserving hue.
4
+ * @param color The original THREE.Color instance.
5
+ * @param percent The percentage to darken (0-100).
6
+ * @returns A new THREE.Color instance with the darkened color.
7
+ */
8
+ export declare const darkenColor: (value: ColorRepresentation, percent: number) => Color;
package/dist/color.js ADDED
@@ -0,0 +1,14 @@
1
+ import { Color } from 'three';
2
+ /**
3
+ * Darkens a THREE.Color by a given percentage while preserving hue.
4
+ * @param color The original THREE.Color instance.
5
+ * @param percent The percentage to darken (0-100).
6
+ * @returns A new THREE.Color instance with the darkened color.
7
+ */
8
+ export const darkenColor = (value, percent) => {
9
+ const color = new Color(value);
10
+ const hsl = { h: 0, s: 0, l: 0 };
11
+ color.getHSL(hsl);
12
+ hsl.l = Math.max(0, hsl.l * (1 - percent / 100));
13
+ return color.setHSL(hsl.h, hsl.s, hsl.l);
14
+ };
@@ -0,0 +1,63 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+ import { Canvas } from '@threlte/core'
4
+ import { XRButton } from '@threlte/xr'
5
+ import Scene from './Scene.svelte'
6
+ import TreeContainer from './Tree/TreeContainer.svelte'
7
+ import Details from './Details.svelte'
8
+ import SceneProviders from './SceneProviders.svelte'
9
+ import DomPortal from './DomPortal.svelte'
10
+ import { PersistedState } from 'runed'
11
+ import XR from './XR.svelte'
12
+ import { World } from '@threlte/rapier'
13
+ import { createPartIDContext } from '../hooks/usePartID.svelte'
14
+
15
+ interface Props {
16
+ partID?: string
17
+ children?: Snippet
18
+ }
19
+
20
+ let { partID = '', children: appChildren }: Props = $props()
21
+
22
+ createPartIDContext(() => partID)
23
+
24
+ const enableXR = new PersistedState('enable-xr', false)
25
+ </script>
26
+
27
+ <svelte:window
28
+ onkeydown={(event) => {
29
+ if (event.ctrlKey && event.key.toLowerCase() === 'a') {
30
+ enableXR.current = !enableXR.current
31
+ }
32
+ }}
33
+ />
34
+
35
+ <Canvas renderMode="always">
36
+ <World>
37
+ <SceneProviders>
38
+ {#snippet children({ focus })}
39
+ <Scene>
40
+ {@render appChildren?.()}
41
+
42
+ {#if enableXR.current}
43
+ <XR />
44
+ {/if}
45
+ </Scene>
46
+
47
+ <DomPortal>
48
+ <Details />
49
+ </DomPortal>
50
+
51
+ {#if !focus}
52
+ <DomPortal>
53
+ <TreeContainer />
54
+ </DomPortal>
55
+ {/if}
56
+ {/snippet}
57
+ </SceneProviders>
58
+ </World>
59
+ </Canvas>
60
+
61
+ {#if enableXR.current}
62
+ <XRButton mode="immersive-ar" />
63
+ {/if}
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ partID?: string;
4
+ children?: Snippet;
5
+ }
6
+ declare const App: import("svelte").Component<Props, {}, "">;
7
+ type App = ReturnType<typeof App>;
8
+ export default App;
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import { T, type Props as ThrelteProps } from '@threlte/core'
3
+ import { AxesHelper } from '../three/AxesHelper'
4
+ import { meshBounds } from '@threlte/extras'
5
+ interface Props extends ThrelteProps<AxesHelper> {
6
+ size?: number
7
+ thickness?: number
8
+ }
9
+
10
+ let { size, thickness, ...rest }: Props = $props()
11
+ </script>
12
+
13
+ <T
14
+ is={new AxesHelper(size, thickness)}
15
+ raycast={meshBounds}
16
+ {...rest}
17
+ />
@@ -0,0 +1,4 @@
1
+ import { AxesHelper } from '../three/AxesHelper';
2
+ declare const AxesHelper: any;
3
+ type AxesHelper = ReturnType<typeof AxesHelper>;
4
+ export default AxesHelper;
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import { Vector2, PlaneGeometry } from 'three'
3
+ import { T } from '@threlte/core'
4
+ import type { BufferAttribute } from 'three'
5
+
6
+ export let args: [
7
+ radius: number,
8
+ width?: number | undefined,
9
+ height?: number | undefined,
10
+ widthSegments?: number | undefined,
11
+ heightSegments?: number | undefined,
12
+ ]
13
+
14
+ class BentPlaneGeometry extends PlaneGeometry {
15
+ constructor(radius: number, ...args: ConstructorParameters<typeof PlaneGeometry>) {
16
+ super(...args)
17
+
18
+ const p = this.parameters
19
+ const hw = p.width * 0.5
20
+ const a = new Vector2(-hw, 0)
21
+ const b = new Vector2(0, radius)
22
+ const c = new Vector2(hw, 0)
23
+ const ab = new Vector2().subVectors(a, b)
24
+ const bc = new Vector2().subVectors(b, c)
25
+ const ac = new Vector2().subVectors(a, c)
26
+ const r = (ab.length() * bc.length() * ac.length()) / (2 * Math.abs(ab.cross(ac)))
27
+ const center = new Vector2(0, radius - r)
28
+ const baseV = new Vector2().subVectors(a, center)
29
+ const baseAngle = baseV.angle() - Math.PI * 0.5
30
+ const arc = baseAngle * 2
31
+ const uv = this.attributes.uv as BufferAttribute
32
+ const pos = this.attributes.position as BufferAttribute
33
+ const mainV = new Vector2()
34
+
35
+ for (let i = 0; i < uv.count; i += 1) {
36
+ const uvRatio = 1 - uv.getX(i)
37
+ const y = pos.getY(i)
38
+ mainV.copy(c).rotateAround(center, arc * uvRatio)
39
+ pos.setXYZ(i, mainV.x, y, -mainV.y)
40
+ }
41
+
42
+ pos.needsUpdate = true
43
+ }
44
+ }
45
+ </script>
46
+
47
+ <T
48
+ is={BentPlaneGeometry}
49
+ {args}
50
+ >
51
+ <slot />
52
+ </T>
@@ -0,0 +1,29 @@
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: Props & {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
15
+ default: any;
16
+ } ? Props extends Record<string, never> ? any : {
17
+ children?: any;
18
+ } : {});
19
+ declare const BentPlaneGeometry: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
20
+ args: [radius: number, width?: number | undefined, height?: number | undefined, widthSegments?: number | undefined, heightSegments?: number | undefined];
21
+ }, {
22
+ default: {};
23
+ }>, {
24
+ [evt: string]: CustomEvent<any>;
25
+ }, {
26
+ default: {};
27
+ }, {}, string>;
28
+ type BentPlaneGeometry = InstanceType<typeof BentPlaneGeometry>;
29
+ export default BentPlaneGeometry;
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import { PersistedState } from 'runed'
3
+ import { T } from '@threlte/core'
4
+ import { PerspectiveCamera, OrthographicCamera } from 'three'
5
+
6
+ let { children, ...rest } = $props()
7
+
8
+ const mode = new PersistedState<'perspective' | 'orthographic'>('camera-type', 'perspective')
9
+
10
+ const perspective = new PerspectiveCamera()
11
+ perspective.near = 0.01
12
+ perspective.up.set(0, 0, 1)
13
+
14
+ const orthographic = new OrthographicCamera()
15
+ orthographic.near = -100
16
+ orthographic.far = 100
17
+ orthographic.up.set(0, 0, 1)
18
+ orthographic.zoom = 200
19
+ </script>
20
+
21
+ <svelte:window
22
+ onkeydown={({ key }) => {
23
+ if (key.toLowerCase() === 'c') {
24
+ mode.current = mode.current === 'perspective' ? 'orthographic' : 'perspective'
25
+ }
26
+ }}
27
+ />
28
+
29
+ {#if mode.current === 'perspective'}
30
+ <T
31
+ is={perspective}
32
+ makeDefault
33
+ {...rest}
34
+ >
35
+ {@render children?.()}
36
+ </T>
37
+ {:else if mode.current === 'orthographic'}
38
+ <T
39
+ is={orthographic}
40
+ makeDefault
41
+ {...rest}
42
+ >
43
+ {@render children?.()}
44
+ </T>
45
+ {/if}
@@ -0,0 +1,5 @@
1
+ declare const Camera: import("svelte").Component<{
2
+ children: any;
3
+ } & Record<string, any>, {}, "">;
4
+ type Camera = ReturnType<typeof Camera>;
5
+ export default Camera;
@@ -0,0 +1,82 @@
1
+ <script
2
+ module
3
+ lang="ts"
4
+ >
5
+ import {
6
+ Box3,
7
+ Matrix4,
8
+ PerspectiveCamera,
9
+ Quaternion,
10
+ Raycaster,
11
+ Sphere,
12
+ Spherical,
13
+ Vector2,
14
+ Vector3,
15
+ Vector4,
16
+ } from 'three'
17
+ import CameraControls from 'camera-controls'
18
+ import { T, useTask, useThrelte } from '@threlte/core'
19
+ import type { Snippet } from 'svelte'
20
+ import { useTransformControls } from '../hooks/useControls.svelte'
21
+
22
+ let installed = false
23
+
24
+ const install = () => {
25
+ CameraControls.install({
26
+ THREE: {
27
+ Box3,
28
+ Matrix4,
29
+ Quaternion,
30
+ Raycaster,
31
+ Sphere,
32
+ Spherical,
33
+ Vector2,
34
+ Vector3,
35
+ Vector4,
36
+ },
37
+ })
38
+ installed = true
39
+ }
40
+ </script>
41
+
42
+ <script lang="ts">
43
+ interface Props {
44
+ ref?: CameraControls
45
+ children?: Snippet
46
+ }
47
+
48
+ let { ref = $bindable(), children }: Props = $props()
49
+
50
+ if (!installed) {
51
+ install()
52
+ }
53
+
54
+ const { camera, dom, invalidate } = useThrelte()
55
+ const transformControls = useTransformControls()
56
+
57
+ const controls = new CameraControls(camera.current as PerspectiveCamera, dom)
58
+
59
+ $effect.pre(() => {
60
+ controls.camera = $camera as PerspectiveCamera
61
+ })
62
+
63
+ $effect.pre(() => {
64
+ controls.enabled = !transformControls.active
65
+ })
66
+
67
+ useTask(
68
+ (delta) => {
69
+ if (controls.update(delta)) {
70
+ invalidate()
71
+ }
72
+ },
73
+ { autoInvalidate: false }
74
+ )
75
+ </script>
76
+
77
+ <T
78
+ is={controls}
79
+ bind:ref
80
+ >
81
+ {@render children?.()}
82
+ </T>
@@ -0,0 +1,9 @@
1
+ import CameraControls from 'camera-controls';
2
+ import type { Snippet } from 'svelte';
3
+ interface Props {
4
+ ref?: CameraControls;
5
+ children?: Snippet;
6
+ }
7
+ declare const CameraControls: import("svelte").Component<Props, {}, "ref">;
8
+ type CameraControls = ReturnType<typeof CameraControls>;
9
+ export default CameraControls;
@@ -0,0 +1,161 @@
1
+ <script lang="ts">
2
+ import { useSelectedObject, useFocusedObject, useFocused } from '../hooks/useSelection.svelte'
3
+ import { Check, Copy } from 'lucide-svelte'
4
+ import { Button, Icon } from '@viamrobotics/prime-core'
5
+
6
+ const focused = useFocused()
7
+ const selectedObject = useSelectedObject()
8
+ const focusedObject = useFocusedObject()
9
+ const object = $derived(focusedObject.current ?? selectedObject.current)
10
+
11
+ let copied = $state(false)
12
+ </script>
13
+
14
+ {#if object}
15
+ {@const { geometry, pose } = object}
16
+ <div class="border-medium bg-extralight fixed top-0 right-0 z-10 m-2 w-60 border p-2 text-xs">
17
+ <div class="flex items-center justify-between gap-2 pb-2">
18
+ <div class="flex items-center gap-1">
19
+ <button>
20
+ <Icon name="drag" />
21
+ </button>
22
+ {object.name}
23
+ </div>
24
+ </div>
25
+
26
+ <div class="border-medium -mx-2 w-[100%+0.5rem] border-b"></div>
27
+
28
+ <h3 class="text-subtle-2 flex justify-between py-2">
29
+ Details
30
+
31
+ <button
32
+ onclick={async () => {
33
+ navigator.clipboard.writeText(JSON.stringify($state.snapshot(object)))
34
+ copied = true
35
+ setTimeout(() => (copied = false), 1000)
36
+ }}
37
+ >
38
+ {#if copied}
39
+ <Check size={14} />
40
+ {:else}
41
+ <Copy size={14} />
42
+ {/if}
43
+ </button>
44
+ </h3>
45
+
46
+ <div class="flex flex-col gap-2.5">
47
+ {#if pose}
48
+ <div>
49
+ <strong class="font-semibold">position</strong>
50
+ <div class="flex gap-3">
51
+ <div>
52
+ <span class="text-subtle-2">x</span>
53
+ {pose.x !== undefined ? pose.x.toFixed(2) : '-'}
54
+ </div>
55
+ <div>
56
+ <span class="text-subtle-2">y</span>
57
+ {pose.y !== undefined ? pose.y.toFixed(2) : '-'}
58
+ </div>
59
+ <div>
60
+ <span class="text-subtle-2">z</span>
61
+ {pose.z !== undefined ? pose.z.toFixed(2) : '-'}
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <div>
67
+ <strong class="font-semibold">orientation</strong>
68
+ <div class="flex gap-3">
69
+ <div>
70
+ <span class="text-subtle-2">x</span>
71
+ {pose.oX !== undefined ? pose.oX.toFixed(2) : '-'}
72
+ </div>
73
+ <div>
74
+ <span class="text-subtle-2">y</span>
75
+ {pose.oY !== undefined ? pose.oY.toFixed(2) : '-'}
76
+ </div>
77
+ <div>
78
+ <span class="text-subtle-2">z</span>
79
+ {pose.oZ !== undefined ? pose.oZ.toFixed(2) : '-'}
80
+ </div>
81
+ <div>
82
+ <span class="text-subtle-2">th</span>
83
+ {pose.theta !== undefined ? pose.theta.toFixed(2) : '-'}
84
+ </div>
85
+ </div>
86
+ </div>
87
+ {/if}
88
+
89
+ {#if geometry}
90
+ {#if geometry.case === 'box'}
91
+ {@const { dimsMm } = geometry.value}
92
+ <div>
93
+ <strong class="font-semibold">dimensions</strong>
94
+ <div class="flex gap-3">
95
+ <div>
96
+ <span class="text-subtle-2">x</span>
97
+ {dimsMm?.x ? dimsMm.x.toFixed(2) : '-'}
98
+ </div>
99
+ <div>
100
+ <span class="text-subtle-2">y</span>
101
+ {dimsMm?.y ? dimsMm.y.toFixed(2) : '-'}
102
+ </div>
103
+ <div>
104
+ <span class="text-subtle-2">z</span>
105
+ {dimsMm?.z ? dimsMm.z.toFixed(2) : '-'}
106
+ </div>
107
+ </div>
108
+ </div>
109
+ {:else if geometry.case === 'capsule'}
110
+ {@const { value } = geometry}
111
+ <div>
112
+ <strong class="font-semibold">dimensions</strong>
113
+ <div class="flex gap-3">
114
+ <div>
115
+ <span class="text-subtle-2">r</span>
116
+ {value.radiusMm ? value.radiusMm.toFixed(2) : '-'}
117
+ </div>
118
+ <div>
119
+ <span class="text-subtle-2">l</span>
120
+ {value.lengthMm ? value.lengthMm.toFixed(2) : '-'}
121
+ </div>
122
+ </div>
123
+ </div>
124
+ {:else if geometry.case === 'sphere'}
125
+ <div class="flex justify-between">
126
+ <div>
127
+ <strong class="font-semibold">dimensions</strong>
128
+ <div class="flex gap-3">
129
+ <div>
130
+ <span class="text-subtle-2">r</span>
131
+ {geometry.value.radiusMm.toFixed(2)}
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ {/if}
137
+ {/if}
138
+ </div>
139
+
140
+ <h3 class="text-subtle-2 pt-3 pb-2">Actions</h3>
141
+
142
+ {#if focused.current}
143
+ <Button
144
+ class="w-full"
145
+ icon="arrow-left"
146
+ variant="dark"
147
+ onclick={() => focused.set()}
148
+ >
149
+ Exit object view
150
+ </Button>
151
+ {:else}
152
+ <Button
153
+ class="w-full"
154
+ icon="image-filter-center-focus"
155
+ onclick={() => focused.set(object.uuid)}
156
+ >
157
+ Enter object view
158
+ </Button>
159
+ {/if}
160
+ </div>
161
+ {/if}
@@ -0,0 +1,3 @@
1
+ declare const Details: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type Details = ReturnType<typeof Details>;
3
+ export default Details;