@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.
- package/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/WorldObject.d.ts +33 -0
- package/dist/WorldObject.js +18 -0
- package/dist/assert.d.ts +14 -0
- package/dist/assert.js +21 -0
- package/dist/color.d.ts +8 -0
- package/dist/color.js +14 -0
- package/dist/components/App.svelte +63 -0
- package/dist/components/App.svelte.d.ts +8 -0
- package/dist/components/AxesHelper.svelte +17 -0
- package/dist/components/AxesHelper.svelte.d.ts +4 -0
- package/dist/components/BentPlaneGeometry.svelte +52 -0
- package/dist/components/BentPlaneGeometry.svelte.d.ts +29 -0
- package/dist/components/Camera.svelte +45 -0
- package/dist/components/Camera.svelte.d.ts +5 -0
- package/dist/components/CameraControls.svelte +82 -0
- package/dist/components/CameraControls.svelte.d.ts +9 -0
- package/dist/components/Details.svelte +161 -0
- package/dist/components/Details.svelte.d.ts +3 -0
- package/dist/components/Detections.svelte +41 -0
- package/dist/components/Detections.svelte.d.ts +3 -0
- package/dist/components/DetectionsPlane.svelte +23 -0
- package/dist/components/DetectionsPlane.svelte.d.ts +21 -0
- package/dist/components/DomPortal.svelte +20 -0
- package/dist/components/DomPortal.svelte.d.ts +5 -0
- package/dist/components/Focus.svelte +49 -0
- package/dist/components/Focus.svelte.d.ts +3 -0
- package/dist/components/Frame.svelte +112 -0
- package/dist/components/Frame.svelte.d.ts +16 -0
- package/dist/components/Frames.svelte +54 -0
- package/dist/components/Frames.svelte.d.ts +18 -0
- package/dist/components/Pointcloud.svelte +55 -0
- package/dist/components/Pointcloud.svelte.d.ts +10 -0
- package/dist/components/Pointclouds.svelte +21 -0
- package/dist/components/Pointclouds.svelte.d.ts +18 -0
- package/dist/components/Pose.svelte +19 -0
- package/dist/components/Pose.svelte.d.ts +12 -0
- package/dist/components/RefreshRate.svelte +47 -0
- package/dist/components/RefreshRate.svelte.d.ts +8 -0
- package/dist/components/Scene.svelte +81 -0
- package/dist/components/Scene.svelte.d.ts +7 -0
- package/dist/components/SceneProviders.svelte +41 -0
- package/dist/components/SceneProviders.svelte.d.ts +9 -0
- package/dist/components/Selected.svelte +44 -0
- package/dist/components/Selected.svelte.d.ts +3 -0
- package/dist/components/Shapes.svelte +49 -0
- package/dist/components/Shapes.svelte.d.ts +18 -0
- package/dist/components/StaticGeometries.svelte +79 -0
- package/dist/components/StaticGeometries.svelte.d.ts +18 -0
- package/dist/components/Tree/Settings.svelte +54 -0
- package/dist/components/Tree/Settings.svelte.d.ts +18 -0
- package/dist/components/Tree/Tree.svelte +204 -0
- package/dist/components/Tree/Tree.svelte.d.ts +10 -0
- package/dist/components/Tree/TreeContainer.svelte +70 -0
- package/dist/components/Tree/TreeContainer.svelte.d.ts +3 -0
- package/dist/components/Tree/buildTree.d.ts +11 -0
- package/dist/components/Tree/buildTree.js +29 -0
- package/dist/components/Tree/useExpanded.svelte.d.ts +5 -0
- package/dist/components/Tree/useExpanded.svelte.js +21 -0
- package/dist/components/WorldObject.svelte +27 -0
- package/dist/components/WorldObject.svelte.d.ts +11 -0
- package/dist/components/XR.svelte +20 -0
- package/dist/components/XR.svelte.d.ts +3 -0
- package/dist/components/models/README.md +5 -0
- package/dist/components/null-states/Connection.svelte +0 -0
- package/dist/components/null-states/Connection.svelte.d.ts +26 -0
- package/dist/components/portal/Portal.svelte +25 -0
- package/dist/components/portal/Portal.svelte.d.ts +8 -0
- package/dist/components/portal/PortalTarget.svelte +18 -0
- package/dist/components/portal/PortalTarget.svelte.d.ts +6 -0
- package/dist/components/portal/index.d.ts +2 -0
- package/dist/components/portal/index.js +2 -0
- package/dist/components/portal/usePortalContext.svelte.d.ts +5 -0
- package/dist/components/portal/usePortalContext.svelte.js +8 -0
- package/dist/components/xr/CameraFeed.svelte +81 -0
- package/dist/components/xr/CameraFeed.svelte.d.ts +6 -0
- package/dist/components/xr/Controllers.svelte +71 -0
- package/dist/components/xr/Controllers.svelte.d.ts +3 -0
- package/dist/components/xr/Draggable.svelte +101 -0
- package/dist/components/xr/Draggable.svelte.d.ts +11 -0
- package/dist/components/xr/HandCollider.svelte +19 -0
- package/dist/components/xr/HandCollider.svelte.d.ts +18 -0
- package/dist/components/xr/Hands.svelte +24 -0
- package/dist/components/xr/Hands.svelte.d.ts +18 -0
- package/dist/components/xr/OriginMarker.svelte +100 -0
- package/dist/components/xr/OriginMarker.svelte.d.ts +3 -0
- package/dist/components/xr/PointDistance.svelte +52 -0
- package/dist/components/xr/PointDistance.svelte.d.ts +3 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useConnectionConfigs.svelte.d.ts +17 -0
- package/dist/hooks/useConnectionConfigs.svelte.js +40 -0
- package/dist/hooks/useControls.svelte.d.ts +7 -0
- package/dist/hooks/useControls.svelte.js +16 -0
- package/dist/hooks/useDraggable.svelte.d.ts +2 -0
- package/dist/hooks/useDraggable.svelte.js +25 -0
- package/dist/hooks/useFrames.svelte.d.ts +9 -0
- package/dist/hooks/useFrames.svelte.js +50 -0
- package/dist/hooks/useGeometries.svelte.d.ts +7 -0
- package/dist/hooks/useGeometries.svelte.js +58 -0
- package/dist/hooks/useMotionClient.svelte.d.ts +8 -0
- package/dist/hooks/useMotionClient.svelte.js +31 -0
- package/dist/hooks/useObjectEvents.svelte.d.ts +9 -0
- package/dist/hooks/useObjectEvents.svelte.js +31 -0
- package/dist/hooks/useObjects.svelte.d.ts +7 -0
- package/dist/hooks/useObjects.svelte.js +33 -0
- package/dist/hooks/usePartID.svelte.d.ts +6 -0
- package/dist/hooks/usePartID.svelte.js +14 -0
- package/dist/hooks/usePersistentUUIDs.svelte.d.ts +5 -0
- package/dist/hooks/usePersistentUUIDs.svelte.js +14 -0
- package/dist/hooks/usePointclouds.svelte.d.ts +7 -0
- package/dist/hooks/usePointclouds.svelte.js +58 -0
- package/dist/hooks/usePose.svelte.d.ts +3 -0
- package/dist/hooks/usePose.svelte.js +44 -0
- package/dist/hooks/usePoses.svelte.d.ts +12 -0
- package/dist/hooks/usePoses.svelte.js +63 -0
- package/dist/hooks/useRefreshRates.svelte.d.ts +5 -0
- package/dist/hooks/useRefreshRates.svelte.js +23 -0
- package/dist/hooks/useSelection.svelte.d.ts +40 -0
- package/dist/hooks/useSelection.svelte.js +92 -0
- package/dist/hooks/useShapes.svelte.d.ts +17 -0
- package/dist/hooks/useShapes.svelte.js +264 -0
- package/dist/hooks/useStaticGeometries.svelte.d.ts +9 -0
- package/dist/hooks/useStaticGeometries.svelte.js +37 -0
- package/dist/hooks/useVisibility.svelte.d.ts +5 -0
- package/dist/hooks/useVisibility.svelte.js +22 -0
- package/dist/hooks/xr/useAnchors.svelte.d.ts +0 -0
- package/dist/hooks/xr/useAnchors.svelte.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/keybindings.d.ts +12 -0
- package/dist/keybindings.js +12 -0
- package/dist/loaders/pcd/index.d.ts +4 -0
- package/dist/loaders/pcd/index.js +13 -0
- package/dist/loaders/pcd/worker.d.ts +1 -0
- package/dist/loaders/pcd/worker.js +26 -0
- package/dist/three/AxesHelper.d.ts +5 -0
- package/dist/three/AxesHelper.js +35 -0
- package/dist/three/BatchedArrow.d.ts +30 -0
- package/dist/three/BatchedArrow.js +126 -0
- package/dist/three/BoxHelper.d.ts +50 -0
- package/dist/three/BoxHelper.js +134 -0
- package/dist/three/CapsuleGeometry.d.ts +10 -0
- package/dist/three/CapsuleGeometry.js +17 -0
- package/dist/transform.d.ts +11 -0
- package/dist/transform.js +65 -0
- 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
|
+
}
|
package/dist/assert.d.ts
ADDED
|
@@ -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
|
+
};
|
package/dist/color.d.ts
ADDED
|
@@ -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,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,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,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}
|