@viamrobotics/motion-tools 0.14.3 → 0.14.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.
- package/dist/{Detail.svelte.d.ts → FrameConfigUpdater.svelte.d.ts} +4 -17
- package/dist/{Detail.svelte.js → FrameConfigUpdater.svelte.js} +10 -49
- package/dist/components/AxesHelper.svelte +0 -1
- package/dist/components/Details.svelte +2 -2
- package/dist/components/Geometry.svelte +35 -8
- package/dist/components/WorldObjects.svelte +15 -0
- package/dist/frame.d.ts +76 -0
- package/dist/frame.js +12 -0
- package/dist/geometry.d.ts +2 -0
- package/dist/geometry.js +8 -0
- package/dist/hooks/useDrawAPI.svelte.d.ts +1 -0
- package/dist/hooks/useDrawAPI.svelte.js +56 -0
- package/dist/hooks/useFrames.svelte.js +1 -9
- package/dist/hooks/useObjects.svelte.js +1 -0
- package/dist/hooks/usePartConfig.svelte.d.ts +2 -32
- package/dist/hooks/usePartConfig.svelte.js +46 -49
- package/dist/hooks/useStaticGeometries.svelte.js +1 -1
- package/dist/three/OrientationVector.d.ts +1 -1
- package/dist/transform.d.ts +2 -4
- package/dist/transform.js +28 -41
- package/package.json +1 -1
|
@@ -1,20 +1,14 @@
|
|
|
1
|
+
import type { Frame } from './frame';
|
|
1
2
|
import type { WorldObject } from './lib';
|
|
2
3
|
import type { Geometries } from './WorldObject.svelte';
|
|
3
4
|
import type { Pose } from '@viamrobotics/sdk';
|
|
4
5
|
type UpdateFrameCallback = {
|
|
5
|
-
(componentName: string, referenceFrame: string, pose: Pose, geometry?:
|
|
6
|
-
type: 'none' | 'box' | 'sphere' | 'capsule';
|
|
7
|
-
r?: number;
|
|
8
|
-
l?: number;
|
|
9
|
-
x?: number;
|
|
10
|
-
y?: number;
|
|
11
|
-
z?: number;
|
|
12
|
-
}): void;
|
|
6
|
+
(componentName: string, referenceFrame: string, pose: Pose, geometry?: Frame['geometry']): void;
|
|
13
7
|
};
|
|
14
8
|
type RemoveFrameCallback = {
|
|
15
9
|
(componentName: string): void;
|
|
16
10
|
};
|
|
17
|
-
export declare class
|
|
11
|
+
export declare class FrameConfigUpdater {
|
|
18
12
|
private object;
|
|
19
13
|
private referenceFrame;
|
|
20
14
|
private updateFrame;
|
|
@@ -31,14 +25,7 @@ export declare class DetailConfigUpdater {
|
|
|
31
25
|
oZ?: number;
|
|
32
26
|
theta?: number;
|
|
33
27
|
}) => void;
|
|
34
|
-
updateGeometry: (geometry:
|
|
35
|
-
type: "none" | "box" | "sphere" | "capsule";
|
|
36
|
-
r?: number;
|
|
37
|
-
l?: number;
|
|
38
|
-
x?: number;
|
|
39
|
-
y?: number;
|
|
40
|
-
z?: number;
|
|
41
|
-
}) => void;
|
|
28
|
+
updateGeometry: (geometry: Partial<Frame["geometry"]>) => void;
|
|
42
29
|
setFrameParent: (parentName: string) => void;
|
|
43
30
|
deleteFrame: () => void;
|
|
44
31
|
setGeometryType: (type: "none" | "box" | "sphere" | "capsule") => void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { createPose } from './transform';
|
|
2
|
+
export class FrameConfigUpdater {
|
|
2
3
|
object;
|
|
3
4
|
referenceFrame;
|
|
4
5
|
updateFrame;
|
|
@@ -49,7 +50,7 @@ export class DetailConfigUpdater {
|
|
|
49
50
|
if (!object)
|
|
50
51
|
return;
|
|
51
52
|
let geometryObject;
|
|
52
|
-
if (geometry
|
|
53
|
+
if (geometry?.type === 'box') {
|
|
53
54
|
const currentGeometry = object.geometry?.geometryType.value;
|
|
54
55
|
geometryObject = {
|
|
55
56
|
type: 'box',
|
|
@@ -58,14 +59,14 @@ export class DetailConfigUpdater {
|
|
|
58
59
|
z: geometry.z ?? currentGeometry?.dimsMm?.z,
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
|
-
else if (geometry
|
|
62
|
+
else if (geometry?.type === 'sphere') {
|
|
62
63
|
const currentGeometry = object.geometry?.geometryType.value;
|
|
63
64
|
geometryObject = {
|
|
64
65
|
type: 'sphere',
|
|
65
66
|
r: geometry.r ?? currentGeometry?.radiusMm,
|
|
66
67
|
};
|
|
67
68
|
}
|
|
68
|
-
else if (geometry
|
|
69
|
+
else if (geometry?.type === 'capsule') {
|
|
69
70
|
const currentGeometry = object.geometry?.geometryType.value;
|
|
70
71
|
geometryObject = {
|
|
71
72
|
type: 'capsule',
|
|
@@ -73,29 +74,13 @@ export class DetailConfigUpdater {
|
|
|
73
74
|
l: geometry.l ?? currentGeometry?.lengthMm,
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
|
-
this.updateFrame(object.name ?? '', this.referenceFrame(),
|
|
77
|
-
x: object.localEditedPose.x,
|
|
78
|
-
y: object.localEditedPose.y,
|
|
79
|
-
z: object.localEditedPose.z,
|
|
80
|
-
oX: object.localEditedPose.oX,
|
|
81
|
-
oY: object.localEditedPose.oY,
|
|
82
|
-
oZ: object.localEditedPose.oZ,
|
|
83
|
-
theta: object.localEditedPose.theta,
|
|
84
|
-
}, { ...geometryObject });
|
|
77
|
+
this.updateFrame(object.name ?? '', this.referenceFrame(), createPose(object.localEditedPose), geometryObject);
|
|
85
78
|
};
|
|
86
79
|
setFrameParent = (parentName) => {
|
|
87
80
|
const object = this.object();
|
|
88
81
|
if (!object)
|
|
89
82
|
return;
|
|
90
|
-
this.updateFrame(object.name ?? '', parentName,
|
|
91
|
-
x: object.localEditedPose.x,
|
|
92
|
-
y: object.localEditedPose.y,
|
|
93
|
-
z: object.localEditedPose.z,
|
|
94
|
-
oX: object.localEditedPose.oX,
|
|
95
|
-
oY: object.localEditedPose.oY,
|
|
96
|
-
oZ: object.localEditedPose.oZ,
|
|
97
|
-
theta: object.localEditedPose.theta,
|
|
98
|
-
});
|
|
83
|
+
this.updateFrame(object.name ?? '', parentName, createPose(object.localEditedPose));
|
|
99
84
|
};
|
|
100
85
|
deleteFrame = () => {
|
|
101
86
|
const object = this.object();
|
|
@@ -108,37 +93,13 @@ export class DetailConfigUpdater {
|
|
|
108
93
|
if (!object)
|
|
109
94
|
return;
|
|
110
95
|
if (type === 'none') {
|
|
111
|
-
this.updateFrame(object.name ?? '', this.referenceFrame(), {
|
|
112
|
-
x: object.localEditedPose.x,
|
|
113
|
-
y: object.localEditedPose.y,
|
|
114
|
-
z: object.localEditedPose.z,
|
|
115
|
-
oX: object.localEditedPose.oX,
|
|
116
|
-
oY: object.localEditedPose.oY,
|
|
117
|
-
oZ: object.localEditedPose.oZ,
|
|
118
|
-
theta: object.localEditedPose.theta,
|
|
119
|
-
}, { type: 'none' });
|
|
96
|
+
this.updateFrame(object.name ?? '', this.referenceFrame(), createPose(object.localEditedPose), { type: 'none' });
|
|
120
97
|
}
|
|
121
98
|
else if (type === 'box') {
|
|
122
|
-
this.updateFrame(object.name ?? '', this.referenceFrame(), {
|
|
123
|
-
x: object.localEditedPose.x,
|
|
124
|
-
y: object.localEditedPose.y,
|
|
125
|
-
z: object.localEditedPose.z,
|
|
126
|
-
oX: object.localEditedPose.oX,
|
|
127
|
-
oY: object.localEditedPose.oY,
|
|
128
|
-
oZ: object.localEditedPose.oZ,
|
|
129
|
-
theta: object.localEditedPose.theta,
|
|
130
|
-
}, { type: 'box', x: 100, y: 100, z: 100 });
|
|
99
|
+
this.updateFrame(object.name ?? '', this.referenceFrame(), createPose(object.localEditedPose), { type: 'box', x: 100, y: 100, z: 100 });
|
|
131
100
|
}
|
|
132
101
|
else if (type === 'sphere') {
|
|
133
|
-
this.updateFrame(object.name ?? '', this.referenceFrame(), {
|
|
134
|
-
x: object.localEditedPose.x,
|
|
135
|
-
y: object.localEditedPose.y,
|
|
136
|
-
z: object.localEditedPose.z,
|
|
137
|
-
oX: object.localEditedPose.oX,
|
|
138
|
-
oY: object.localEditedPose.oY,
|
|
139
|
-
oZ: object.localEditedPose.oZ,
|
|
140
|
-
theta: object.localEditedPose.theta,
|
|
141
|
-
}, { type: 'sphere', r: 100 });
|
|
102
|
+
this.updateFrame(object.name ?? '', this.referenceFrame(), createPose(object.localEditedPose), { type: 'sphere', r: 100 });
|
|
142
103
|
}
|
|
143
104
|
else if (type === 'capsule') {
|
|
144
105
|
this.updateFrame(object.name ?? '', this.referenceFrame(), {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import WeblabActive from './weblab/WeblabActive.svelte'
|
|
26
26
|
import { useFrames } from '../hooks/useFrames.svelte'
|
|
27
27
|
import { usePartConfig } from '../hooks/usePartConfig.svelte'
|
|
28
|
-
import {
|
|
28
|
+
import { FrameConfigUpdater } from '../FrameConfigUpdater.svelte'
|
|
29
29
|
import { useWeblabs } from '../hooks/useWeblabs.svelte'
|
|
30
30
|
|
|
31
31
|
const { ...rest } = $props()
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
|
|
60
60
|
const draggable = useDraggable('details')
|
|
61
61
|
|
|
62
|
-
const detailConfigUpdater
|
|
62
|
+
const detailConfigUpdater = new FrameConfigUpdater(
|
|
63
63
|
() => object,
|
|
64
64
|
partConfig.updateFrame,
|
|
65
65
|
partConfig.deleteFrame,
|
|
@@ -65,18 +65,38 @@
|
|
|
65
65
|
const oncreate = (ref: BufferGeometry) => {
|
|
66
66
|
geo = ref
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
const parsePlyInput = (mesh: string | Uint8Array): BufferGeometry => {
|
|
70
|
+
// Case 1: already a base64 or ASCII string
|
|
71
|
+
if (typeof mesh === 'string') {
|
|
72
|
+
return plyLoader.parse(atob(mesh))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Case 2: detect text vs binary PLY in Uint8Array
|
|
76
|
+
const header = new TextDecoder().decode(mesh.slice(0, 50))
|
|
77
|
+
const isAscii = header.includes('format ascii')
|
|
78
|
+
|
|
79
|
+
// Case 3: text-mode PLY → decode bytes to string
|
|
80
|
+
if (isAscii) {
|
|
81
|
+
const text = new TextDecoder().decode(mesh)
|
|
82
|
+
return plyLoader.parse(text)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Case 4: binary PLY → pass ArrayBuffer directly
|
|
86
|
+
return plyLoader.parse(mesh.buffer as ArrayBuffer)
|
|
87
|
+
}
|
|
68
88
|
</script>
|
|
69
89
|
|
|
70
90
|
<T
|
|
71
91
|
is={group}
|
|
72
92
|
{...rest}
|
|
73
93
|
>
|
|
74
|
-
<AxesHelper
|
|
75
|
-
width={3}
|
|
76
|
-
length={0.1}
|
|
77
|
-
/>
|
|
78
|
-
|
|
79
94
|
{#if geometry?.geometryType}
|
|
95
|
+
<AxesHelper
|
|
96
|
+
width={3}
|
|
97
|
+
length={0.1}
|
|
98
|
+
/>
|
|
99
|
+
|
|
80
100
|
<T
|
|
81
101
|
is={mesh}
|
|
82
102
|
{name}
|
|
@@ -84,8 +104,8 @@
|
|
|
84
104
|
bvh={{ enabled: false }}
|
|
85
105
|
>
|
|
86
106
|
{#if geometry.geometryType.case === 'mesh'}
|
|
87
|
-
{@const mesh = geometry.geometryType.value.mesh
|
|
88
|
-
{@const meshGeometry =
|
|
107
|
+
{@const mesh = geometry.geometryType.value.mesh}
|
|
108
|
+
{@const meshGeometry = parsePlyInput(mesh)}
|
|
89
109
|
<T
|
|
90
110
|
is={meshGeometry}
|
|
91
111
|
{oncreate}
|
|
@@ -111,7 +131,7 @@
|
|
|
111
131
|
args={[radiusMm * 0.001, lengthMm * 0.001]}
|
|
112
132
|
{oncreate}
|
|
113
133
|
/>
|
|
114
|
-
{
|
|
134
|
+
{/if}
|
|
115
135
|
|
|
116
136
|
{#if geometry.geometryType.case === 'line'}
|
|
117
137
|
<MeshLineMaterial
|
|
@@ -137,6 +157,13 @@
|
|
|
137
157
|
{/if}
|
|
138
158
|
{/if}
|
|
139
159
|
</T>
|
|
160
|
+
{:else}
|
|
161
|
+
<AxesHelper
|
|
162
|
+
{name}
|
|
163
|
+
{uuid}
|
|
164
|
+
width={3}
|
|
165
|
+
length={0.1}
|
|
166
|
+
/>
|
|
140
167
|
{/if}
|
|
141
168
|
|
|
142
169
|
{@render children?.({ ref: group })}
|
|
@@ -74,6 +74,21 @@
|
|
|
74
74
|
</Portal>
|
|
75
75
|
{/each}
|
|
76
76
|
|
|
77
|
+
{#each drawAPI.frames as object (object.uuid)}
|
|
78
|
+
<Portal id={object.referenceFrame}>
|
|
79
|
+
<Frame
|
|
80
|
+
uuid={object.uuid}
|
|
81
|
+
name={object.name}
|
|
82
|
+
pose={object.pose}
|
|
83
|
+
geometry={object.geometry}
|
|
84
|
+
metadata={object.metadata}
|
|
85
|
+
>
|
|
86
|
+
<PortalTarget id={object.name} />
|
|
87
|
+
<Label text={object.name} />
|
|
88
|
+
</Frame>
|
|
89
|
+
</Portal>
|
|
90
|
+
{/each}
|
|
91
|
+
|
|
77
92
|
{#each drawAPI.points as object (object.uuid)}
|
|
78
93
|
<Portal id={object.referenceFrame}>
|
|
79
94
|
<Pointcloud {object}>
|
package/dist/frame.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { ValueOf } from 'type-fest';
|
|
2
|
+
type FrameGeometryMap = {
|
|
3
|
+
none: {
|
|
4
|
+
type: 'none';
|
|
5
|
+
};
|
|
6
|
+
box: {
|
|
7
|
+
type: 'box';
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
z: number;
|
|
11
|
+
};
|
|
12
|
+
sphere: {
|
|
13
|
+
type: 'sphere';
|
|
14
|
+
r: number;
|
|
15
|
+
};
|
|
16
|
+
capsule: {
|
|
17
|
+
type: 'capsule';
|
|
18
|
+
r: number;
|
|
19
|
+
l: number;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export type FrameGeometry = keyof FrameGeometryMap;
|
|
23
|
+
export type FrameGeometries = ValueOf<FrameGeometryMap>;
|
|
24
|
+
type FrameOrientationMap = {
|
|
25
|
+
quaternion: {
|
|
26
|
+
type: 'quaternion';
|
|
27
|
+
value: {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
z: number;
|
|
31
|
+
w: number;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
euler_angles: {
|
|
35
|
+
type: 'euler_angles';
|
|
36
|
+
value: {
|
|
37
|
+
roll: number;
|
|
38
|
+
pitch: number;
|
|
39
|
+
yaw: number;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
ov_degrees: {
|
|
43
|
+
type: 'ov_degrees';
|
|
44
|
+
value: {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
z: number;
|
|
48
|
+
th: number;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
ov_radians: {
|
|
52
|
+
type: 'ov_radians';
|
|
53
|
+
value: {
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
z: number;
|
|
57
|
+
th: number;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
export type FrameOrientation = keyof FrameOrientationMap;
|
|
62
|
+
export type FrameOrientations = ValueOf<FrameOrientationMap>;
|
|
63
|
+
export interface Frame<T extends FrameGeometry = FrameGeometry, K extends FrameOrientation = FrameOrientation> {
|
|
64
|
+
id?: string;
|
|
65
|
+
name?: string;
|
|
66
|
+
parent: string;
|
|
67
|
+
translation: {
|
|
68
|
+
x: number;
|
|
69
|
+
y: number;
|
|
70
|
+
z: number;
|
|
71
|
+
};
|
|
72
|
+
orientation: FrameOrientationMap[K];
|
|
73
|
+
geometry?: FrameGeometryMap[T];
|
|
74
|
+
}
|
|
75
|
+
export declare const createFrame: <T extends FrameGeometry = "box", K extends FrameOrientation = "ov_degrees">(geometry?: FrameGeometryMap[T]) => Frame<T>;
|
|
76
|
+
export {};
|
package/dist/frame.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// TODO: replace with types exported from the sdk when created
|
|
2
|
+
export const createFrame = (geometry) => {
|
|
3
|
+
return {
|
|
4
|
+
parent: 'world',
|
|
5
|
+
translation: { x: 0, y: 0, z: 0 },
|
|
6
|
+
orientation: {
|
|
7
|
+
type: 'ov_degrees',
|
|
8
|
+
value: { x: 0, y: 0, z: 1, th: 0 },
|
|
9
|
+
},
|
|
10
|
+
geometry: (geometry ?? { type: 'box', x: 100, y: 100, z: 100 }),
|
|
11
|
+
};
|
|
12
|
+
};
|
package/dist/geometry.js
ADDED
|
@@ -4,6 +4,7 @@ type ConnectionStatus = 'connecting' | 'open' | 'closed';
|
|
|
4
4
|
interface Context {
|
|
5
5
|
addPoints(worldObject: WorldObject<PointsGeometry>): void;
|
|
6
6
|
points: WorldObject<PointsGeometry>[];
|
|
7
|
+
frames: WorldObject[];
|
|
7
8
|
lines: WorldObject[];
|
|
8
9
|
meshes: WorldObject[];
|
|
9
10
|
poses: WorldObject[];
|
|
@@ -5,6 +5,8 @@ import { parsePcdInWorker } from '../loaders/pcd';
|
|
|
5
5
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
6
6
|
import { WorldObject } from '../WorldObject.svelte';
|
|
7
7
|
import { useArrows } from './useArrows.svelte';
|
|
8
|
+
import { createGeometry } from '../geometry';
|
|
9
|
+
import { createPoseFromFrame } from '../transform';
|
|
8
10
|
const key = Symbol('draw-api-context-key');
|
|
9
11
|
const tryParse = (json) => {
|
|
10
12
|
try {
|
|
@@ -15,6 +17,21 @@ const tryParse = (json) => {
|
|
|
15
17
|
return;
|
|
16
18
|
}
|
|
17
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* @TODO get golang scripts to return protobufs so that we
|
|
22
|
+
* can use our types. Right now we're just marshalling JSON,
|
|
23
|
+
* leading to upper case var names and no type contract with the golang lib.
|
|
24
|
+
*/
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
const lowercaseKeys = (obj) => {
|
|
27
|
+
if (Array.isArray(obj)) {
|
|
28
|
+
return obj.map(lowercaseKeys);
|
|
29
|
+
}
|
|
30
|
+
else if (obj && typeof obj === 'object' && obj.constructor === Object) {
|
|
31
|
+
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), lowercaseKeys(v)]));
|
|
32
|
+
}
|
|
33
|
+
return obj;
|
|
34
|
+
};
|
|
18
35
|
class Float32Reader {
|
|
19
36
|
littleEndian = true;
|
|
20
37
|
offset = 0;
|
|
@@ -38,6 +55,7 @@ export const provideDrawAPI = () => {
|
|
|
38
55
|
let reconnectDelay = 200;
|
|
39
56
|
const maxReconnectDelay = 5_000;
|
|
40
57
|
let ws;
|
|
58
|
+
const frames = $state([]);
|
|
41
59
|
const points = $state([]);
|
|
42
60
|
const lines = $state([]);
|
|
43
61
|
const meshes = $state([]);
|
|
@@ -51,6 +69,32 @@ export const provideDrawAPI = () => {
|
|
|
51
69
|
const origin = new Vector3();
|
|
52
70
|
const loader = new GLTFLoader();
|
|
53
71
|
const batchedArrow = useArrows();
|
|
72
|
+
const drawFrames = async (data) => {
|
|
73
|
+
for (const frame of data) {
|
|
74
|
+
const name = frame.name || frame.id || '';
|
|
75
|
+
const pose = createPoseFromFrame(lowercaseKeys(frame));
|
|
76
|
+
const geometry = createGeometry();
|
|
77
|
+
if (frame.geometry?.type === 'box') {
|
|
78
|
+
geometry.label = `${name} geometry (box)`;
|
|
79
|
+
geometry.geometryType.case = 'box';
|
|
80
|
+
geometry.geometryType.value = {
|
|
81
|
+
dimsMm: { x: frame.geometry.x, y: frame.geometry.y, z: frame.geometry.z },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
else if (frame.geometry?.type === 'sphere') {
|
|
85
|
+
geometry.label = `${name} geometry (sphere)`;
|
|
86
|
+
geometry.geometryType.case = 'sphere';
|
|
87
|
+
geometry.geometryType.value = { radiusMm: frame.geometry.r };
|
|
88
|
+
}
|
|
89
|
+
else if (frame.geometry?.type === 'capsule') {
|
|
90
|
+
geometry.label = `${name} geometry (capsule)`;
|
|
91
|
+
geometry.geometryType.case = 'capsule';
|
|
92
|
+
geometry.geometryType.value = { lengthMm: frame.geometry.l, radiusMm: frame.geometry.r };
|
|
93
|
+
}
|
|
94
|
+
const worldObject = new WorldObject(name, pose, frame.parent ?? 'world', frame.geometry ? geometry : undefined);
|
|
95
|
+
frames.push(worldObject);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
54
98
|
const drawPCD = async (buffer) => {
|
|
55
99
|
const { positions, colors } = await parsePcdInWorker(new Uint8Array(buffer));
|
|
56
100
|
points.push(new WorldObject(`points ${++pointsIndex}`, undefined, undefined, {
|
|
@@ -256,6 +300,11 @@ export const provideDrawAPI = () => {
|
|
|
256
300
|
const remove = (names) => {
|
|
257
301
|
let index = -1;
|
|
258
302
|
for (const name of names) {
|
|
303
|
+
index = frames.findIndex((frame) => frame.name === name);
|
|
304
|
+
if (index !== -1) {
|
|
305
|
+
frames.slice(index, 1);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
259
308
|
index = points.findIndex((p) => p.name === name);
|
|
260
309
|
if (index !== -1) {
|
|
261
310
|
points.splice(index, 1);
|
|
@@ -293,6 +342,7 @@ export const provideDrawAPI = () => {
|
|
|
293
342
|
}
|
|
294
343
|
};
|
|
295
344
|
const removeAll = () => {
|
|
345
|
+
frames.splice(0, frames.length);
|
|
296
346
|
points.splice(0, points.length);
|
|
297
347
|
lines.splice(0, lines.length);
|
|
298
348
|
meshes.splice(0, meshes.length);
|
|
@@ -362,6 +412,9 @@ export const provideDrawAPI = () => {
|
|
|
362
412
|
if ('geometry' in data) {
|
|
363
413
|
return drawGeometry(data.geometry, data.color);
|
|
364
414
|
}
|
|
415
|
+
if ('frames' in data) {
|
|
416
|
+
return drawFrames(data.frames);
|
|
417
|
+
}
|
|
365
418
|
if ('Knots' in data) {
|
|
366
419
|
return drawNurbs(data, data.Color);
|
|
367
420
|
}
|
|
@@ -384,6 +437,9 @@ export const provideDrawAPI = () => {
|
|
|
384
437
|
};
|
|
385
438
|
connect();
|
|
386
439
|
setContext(key, {
|
|
440
|
+
get frames() {
|
|
441
|
+
return frames;
|
|
442
|
+
},
|
|
387
443
|
get points() {
|
|
388
444
|
return points;
|
|
389
445
|
},
|
|
@@ -54,15 +54,7 @@ export const provideFrames = (partID) => {
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
worldObject.referenceFrame = component.frame.parent;
|
|
57
|
-
worldObject.localEditedPose =
|
|
58
|
-
x: component.frame.translation.x,
|
|
59
|
-
y: component.frame.translation.y,
|
|
60
|
-
z: component.frame.translation.z,
|
|
61
|
-
oX: component.frame.orientation.value.x,
|
|
62
|
-
oY: component.frame.orientation.value.y,
|
|
63
|
-
oZ: component.frame.orientation.value.z,
|
|
64
|
-
theta: component.frame.orientation.value.th,
|
|
65
|
-
};
|
|
57
|
+
worldObject.localEditedPose = createPoseFromFrame(component.frame);
|
|
66
58
|
if (component.frame.geometry) {
|
|
67
59
|
switch (component.frame.geometry.type) {
|
|
68
60
|
case 'box':
|
|
@@ -1,29 +1,6 @@
|
|
|
1
|
+
import { type Frame } from '../frame';
|
|
1
2
|
import { Struct, Pose } from '@viamrobotics/sdk';
|
|
2
3
|
import type { ViamClient } from '@viamrobotics/sdk';
|
|
3
|
-
export interface Frame {
|
|
4
|
-
parent: string;
|
|
5
|
-
translation: {
|
|
6
|
-
x: number;
|
|
7
|
-
y: number;
|
|
8
|
-
z: number;
|
|
9
|
-
};
|
|
10
|
-
orientation: {
|
|
11
|
-
value: {
|
|
12
|
-
x: number;
|
|
13
|
-
y: number;
|
|
14
|
-
z: number;
|
|
15
|
-
th: number;
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
geometry?: {
|
|
19
|
-
type: 'none' | 'box' | 'sphere' | 'capsule';
|
|
20
|
-
x?: number;
|
|
21
|
-
y?: number;
|
|
22
|
-
z?: number;
|
|
23
|
-
r?: number;
|
|
24
|
-
l?: number;
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
4
|
export interface PartConfig {
|
|
28
5
|
components: {
|
|
29
6
|
name: string;
|
|
@@ -39,14 +16,7 @@ interface PartConfigParams {
|
|
|
39
16
|
standalonePartConfigProps?: StandalonePartConfigProps;
|
|
40
17
|
}
|
|
41
18
|
interface PartConfigContext {
|
|
42
|
-
updateFrame: (componentName: string, referenceFrame: string, pose: Pose, geometry?:
|
|
43
|
-
type: 'none' | 'box' | 'sphere' | 'capsule';
|
|
44
|
-
r?: number;
|
|
45
|
-
l?: number;
|
|
46
|
-
x?: number;
|
|
47
|
-
y?: number;
|
|
48
|
-
z?: number;
|
|
49
|
-
}) => void;
|
|
19
|
+
updateFrame: (componentName: string, referenceFrame: string, pose: Pose, geometry?: Frame['geometry']) => void;
|
|
50
20
|
saveLocalPartConfig: () => void;
|
|
51
21
|
resetLocalPartConfig: () => void;
|
|
52
22
|
deleteFrame: (componentName: string) => void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createFrame } from '../frame';
|
|
2
|
+
import { createPoseFromFrame } from '../transform';
|
|
2
3
|
import { Struct, Pose } from '@viamrobotics/sdk';
|
|
3
4
|
import { getContext, setContext } from 'svelte';
|
|
4
5
|
const key = Symbol('part-config-context');
|
|
@@ -14,15 +15,6 @@ export const providePartConfig = (params) => {
|
|
|
14
15
|
else {
|
|
15
16
|
throw new Error('No part config provided');
|
|
16
17
|
}
|
|
17
|
-
const createFrame = (componentName) => {
|
|
18
|
-
const fragmentId = _localPartConfig.componentNameToFragmentId()[componentName];
|
|
19
|
-
if (fragmentId !== undefined) {
|
|
20
|
-
createFragmentFrame(fragmentId, componentName);
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
createPartFrame(componentName);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
18
|
const createFragmentFrame = (fragmentId, componentName) => {
|
|
27
19
|
const newConfig = _localPartConfig.getLocalPartConfig().toJson();
|
|
28
20
|
if (newConfig.fragment_mods === undefined) {
|
|
@@ -39,7 +31,7 @@ export const providePartConfig = (params) => {
|
|
|
39
31
|
const modSetPath = `components.${componentName}.frame`;
|
|
40
32
|
const frame = {
|
|
41
33
|
['$set']: {
|
|
42
|
-
[modSetPath]:
|
|
34
|
+
[modSetPath]: createFrame(),
|
|
43
35
|
},
|
|
44
36
|
};
|
|
45
37
|
fragmentMod.mods.push(frame);
|
|
@@ -50,20 +42,11 @@ export const providePartConfig = (params) => {
|
|
|
50
42
|
const newConfig = _localPartConfig.getLocalPartConfig().toJson();
|
|
51
43
|
const component = newConfig?.components?.find((comp) => comp.name === componentName);
|
|
52
44
|
if (component) {
|
|
53
|
-
component.frame =
|
|
45
|
+
component.frame = createFrame();
|
|
54
46
|
}
|
|
55
47
|
const configStruct = Struct.fromJson(newConfig);
|
|
56
48
|
_localPartConfig.setLocalPartConfig(configStruct);
|
|
57
49
|
};
|
|
58
|
-
const updateFrame = (componentName, referenceFrame, framePosition, frameGeometry) => {
|
|
59
|
-
const fragmentId = _localPartConfig.componentNameToFragmentId()[componentName];
|
|
60
|
-
if (fragmentId !== undefined) {
|
|
61
|
-
updateFragmentFrame(fragmentId, componentName, referenceFrame, framePosition, frameGeometry);
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
updatePartFrame(componentName, referenceFrame, framePosition, frameGeometry);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
50
|
const updateFragmentFrame = (fragmentId, componentName, referenceFrame, framePosition, frameGeometry) => {
|
|
68
51
|
const newConfig = _localPartConfig.getLocalPartConfig().toJson();
|
|
69
52
|
if (newConfig.fragment_mods === undefined) {
|
|
@@ -124,18 +107,19 @@ export const providePartConfig = (params) => {
|
|
|
124
107
|
if (!component) {
|
|
125
108
|
return;
|
|
126
109
|
}
|
|
127
|
-
if (component
|
|
110
|
+
if (component.frame) {
|
|
111
|
+
const currentPose = createPoseFromFrame(component.frame);
|
|
128
112
|
component.frame.parent = referenceFrame;
|
|
129
113
|
component.frame.translation = {
|
|
130
|
-
x: pose.x
|
|
131
|
-
y: pose.y
|
|
132
|
-
z: pose.z
|
|
114
|
+
x: pose.x ?? currentPose.x,
|
|
115
|
+
y: pose.y ?? currentPose.y,
|
|
116
|
+
z: pose.z ?? currentPose.z,
|
|
133
117
|
};
|
|
134
118
|
component.frame.orientation.value = {
|
|
135
|
-
x: pose.oX
|
|
136
|
-
y: pose.oY
|
|
137
|
-
z: pose.oZ
|
|
138
|
-
th: pose.theta
|
|
119
|
+
x: pose.oX ?? currentPose.oX,
|
|
120
|
+
y: pose.oY ?? currentPose.oY,
|
|
121
|
+
z: pose.oZ ?? currentPose.oZ,
|
|
122
|
+
th: pose.theta ?? currentPose.theta,
|
|
139
123
|
};
|
|
140
124
|
if (geometry) {
|
|
141
125
|
if (geometry.type === 'none') {
|
|
@@ -149,15 +133,6 @@ export const providePartConfig = (params) => {
|
|
|
149
133
|
const configStruct = Struct.fromJson(newConfig);
|
|
150
134
|
_localPartConfig.setLocalPartConfig(configStruct);
|
|
151
135
|
};
|
|
152
|
-
const deleteFrame = (componentName) => {
|
|
153
|
-
const fragmentId = _localPartConfig.componentNameToFragmentId()[componentName];
|
|
154
|
-
if (fragmentId !== undefined) {
|
|
155
|
-
deleteFragmentFrame(fragmentId, componentName);
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
deletePartFrame(componentName);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
136
|
const deletePartFrame = (componentName) => {
|
|
162
137
|
const newConfig = _localPartConfig.getLocalPartConfig().toJson();
|
|
163
138
|
const component = newConfig?.components?.find((comp) => comp.name === componentName);
|
|
@@ -191,18 +166,40 @@ export const providePartConfig = (params) => {
|
|
|
191
166
|
const configStruct = Struct.fromJson(newConfig);
|
|
192
167
|
_localPartConfig.setLocalPartConfig(configStruct);
|
|
193
168
|
};
|
|
194
|
-
const saveLocalPartConfig = () => {
|
|
195
|
-
_localPartConfig.saveLocalPartConfig?.();
|
|
196
|
-
};
|
|
197
|
-
const resetLocalPartConfig = () => {
|
|
198
|
-
_localPartConfig.resetLocalPartConfig?.();
|
|
199
|
-
};
|
|
200
169
|
setContext(key, {
|
|
201
|
-
updateFrame,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
170
|
+
updateFrame: (componentName, referenceFrame, framePosition, frameGeometry) => {
|
|
171
|
+
const fragmentId = _localPartConfig.componentNameToFragmentId()[componentName];
|
|
172
|
+
if (fragmentId !== undefined) {
|
|
173
|
+
updateFragmentFrame(fragmentId, componentName, referenceFrame, framePosition, frameGeometry);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
updatePartFrame(componentName, referenceFrame, framePosition, frameGeometry);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
deleteFrame: (componentName) => {
|
|
180
|
+
const fragmentId = _localPartConfig.componentNameToFragmentId()[componentName];
|
|
181
|
+
if (fragmentId !== undefined) {
|
|
182
|
+
deleteFragmentFrame(fragmentId, componentName);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
deletePartFrame(componentName);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
createFrame: (componentName) => {
|
|
189
|
+
const fragmentId = _localPartConfig.componentNameToFragmentId()[componentName];
|
|
190
|
+
if (fragmentId !== undefined) {
|
|
191
|
+
createFragmentFrame(fragmentId, componentName);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
createPartFrame(componentName);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
saveLocalPartConfig: () => {
|
|
198
|
+
_localPartConfig.saveLocalPartConfig?.();
|
|
199
|
+
},
|
|
200
|
+
resetLocalPartConfig: () => {
|
|
201
|
+
_localPartConfig.resetLocalPartConfig?.();
|
|
202
|
+
},
|
|
206
203
|
get localPartConfig() {
|
|
207
204
|
return _localPartConfig.getLocalPartConfig();
|
|
208
205
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
2
|
import { get, set } from 'idb-keyval';
|
|
3
3
|
import { Debounced } from 'runed';
|
|
4
|
-
import { createGeometry } from '../
|
|
4
|
+
import { createGeometry } from '../geometry';
|
|
5
5
|
import { WorldObject } from '../WorldObject.svelte';
|
|
6
6
|
const key = Symbol('static-geometries-context');
|
|
7
7
|
export const provideStaticGeometries = () => {
|
|
@@ -67,7 +67,7 @@ export declare class OrientationVector {
|
|
|
67
67
|
/**
|
|
68
68
|
* Copies value of ov to this orientation vector.
|
|
69
69
|
*/
|
|
70
|
-
copy(ov:
|
|
70
|
+
copy(ov: OrientationVectorLike): this;
|
|
71
71
|
equals(orientationVector: OrientationVectorLike): boolean;
|
|
72
72
|
fromArray(array: number[], offset?: number): this;
|
|
73
73
|
toArray(array?: number[], offset?: number): number[];
|
package/dist/transform.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Geometry, Pose } from '@viamrobotics/sdk';
|
|
2
2
|
import { type Object3D, Matrix4, Quaternion, Vector3 } from 'three';
|
|
3
|
-
import type { Frame } from './
|
|
3
|
+
import type { Frame } from './frame';
|
|
4
4
|
export declare const createPose: (pose?: Pose) => Pose;
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const createPoseFromFrame: (frame: Frame) => Pose;
|
|
6
6
|
export declare const quaternionToPose: (quaternion: Quaternion, pose: Partial<Pose>) => void;
|
|
7
7
|
export declare const vector3ToPose: (vec3: Vector3, pose: Partial<Pose>) => void;
|
|
8
8
|
export declare const object3dToPose: (object3d: Object3D, pose: Partial<Pose>) => Partial<import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>>;
|
|
@@ -11,7 +11,5 @@ export declare const poseToVector3: (pose: Partial<Pose>, vec3: Vector3) => void
|
|
|
11
11
|
export declare const poseToObject3d: (pose: Partial<Pose>, object3d: Object3D) => void;
|
|
12
12
|
export declare const poseToDirection: (pose: Pose) => Vector3;
|
|
13
13
|
export declare const scaleToDimensions: (scale: Vector3, geometry: Geometry["geometryType"]) => void;
|
|
14
|
-
export declare const createPoseFromFrame: (frame: Frame) => Pose;
|
|
15
|
-
export declare const createNewFrame: () => Frame;
|
|
16
14
|
export declare const poseToMatrix: (pose: Pose) => Matrix4;
|
|
17
15
|
export declare const matrixToPose: (matrix: Matrix4) => import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>;
|
package/dist/transform.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { OrientationVector } from './three/OrientationVector';
|
|
2
|
-
import { MathUtils, Matrix4, Quaternion, Vector3 } from 'three';
|
|
2
|
+
import { Euler, MathUtils, Matrix4, Quaternion, Vector3 } from 'three';
|
|
3
|
+
const quaternion = new Quaternion();
|
|
4
|
+
const euler = new Euler();
|
|
3
5
|
const ov = new OrientationVector();
|
|
4
6
|
export const createPose = (pose) => {
|
|
5
7
|
return {
|
|
@@ -12,11 +14,31 @@ export const createPose = (pose) => {
|
|
|
12
14
|
theta: pose?.theta ?? 0,
|
|
13
15
|
};
|
|
14
16
|
};
|
|
15
|
-
export const
|
|
17
|
+
export const createPoseFromFrame = (frame) => {
|
|
18
|
+
if (frame.orientation.type === 'quaternion') {
|
|
19
|
+
quaternion.copy(frame.orientation.value);
|
|
20
|
+
ov.setFromQuaternion(quaternion);
|
|
21
|
+
}
|
|
22
|
+
else if (frame.orientation.type === 'euler_angles') {
|
|
23
|
+
euler.set(frame.orientation.value.roll, frame.orientation.value.pitch, frame.orientation.value.yaw, 'ZYX');
|
|
24
|
+
quaternion.setFromEuler(euler);
|
|
25
|
+
ov.setFromQuaternion(quaternion);
|
|
26
|
+
}
|
|
27
|
+
else if (frame.orientation.type === 'ov_radians') {
|
|
28
|
+
ov.copy(frame.orientation.value);
|
|
29
|
+
}
|
|
30
|
+
else if (frame.orientation.type === 'ov_degrees') {
|
|
31
|
+
const th = MathUtils.degToRad(frame.orientation.value.th);
|
|
32
|
+
ov.set(frame.orientation.value.x, frame.orientation.value.y, frame.orientation.value.z, th);
|
|
33
|
+
}
|
|
16
34
|
return {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
35
|
+
x: frame.translation.x ?? 0,
|
|
36
|
+
y: frame.translation.y ?? 0,
|
|
37
|
+
z: frame.translation.z ?? 0,
|
|
38
|
+
oX: ov.x,
|
|
39
|
+
oY: ov.y,
|
|
40
|
+
oZ: ov.z,
|
|
41
|
+
theta: MathUtils.radToDeg(ov.th),
|
|
20
42
|
};
|
|
21
43
|
};
|
|
22
44
|
export const quaternionToPose = (quaternion, pose) => {
|
|
@@ -49,7 +71,7 @@ export const poseToObject3d = (pose, object3d) => {
|
|
|
49
71
|
poseToQuaternion(pose, object3d.quaternion);
|
|
50
72
|
};
|
|
51
73
|
export const poseToDirection = (pose) => {
|
|
52
|
-
|
|
74
|
+
ov.set(pose.oX, pose.oY, pose.oZ, MathUtils.degToRad(pose.theta));
|
|
53
75
|
return new Vector3(ov.x, ov.y, ov.z);
|
|
54
76
|
};
|
|
55
77
|
export const scaleToDimensions = (scale, geometry) => {
|
|
@@ -67,41 +89,6 @@ export const scaleToDimensions = (scale, geometry) => {
|
|
|
67
89
|
geometry.value.radiusMm *= scale.x;
|
|
68
90
|
}
|
|
69
91
|
};
|
|
70
|
-
export const createPoseFromFrame = (frame) => {
|
|
71
|
-
return {
|
|
72
|
-
x: frame.translation.x,
|
|
73
|
-
y: frame.translation.y,
|
|
74
|
-
z: frame.translation.z,
|
|
75
|
-
oX: frame.orientation.value.x,
|
|
76
|
-
oY: frame.orientation.value.y,
|
|
77
|
-
oZ: frame.orientation.value.z,
|
|
78
|
-
theta: frame.orientation.value.th,
|
|
79
|
-
};
|
|
80
|
-
};
|
|
81
|
-
export const createNewFrame = () => {
|
|
82
|
-
return {
|
|
83
|
-
parent: 'world',
|
|
84
|
-
translation: {
|
|
85
|
-
x: 0,
|
|
86
|
-
y: 0,
|
|
87
|
-
z: 0,
|
|
88
|
-
},
|
|
89
|
-
orientation: {
|
|
90
|
-
value: {
|
|
91
|
-
x: 0,
|
|
92
|
-
y: 0,
|
|
93
|
-
z: 1,
|
|
94
|
-
th: 0,
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
geometry: {
|
|
98
|
-
type: 'box',
|
|
99
|
-
x: 100,
|
|
100
|
-
y: 100,
|
|
101
|
-
z: 100,
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
};
|
|
105
92
|
export const poseToMatrix = (pose) => {
|
|
106
93
|
const matrix = new Matrix4();
|
|
107
94
|
const poseQuaternion = new Quaternion().setFromAxisAngle(new Vector3(pose.oX, pose.oY, pose.oZ), pose.theta * (Math.PI / 180));
|