@viamrobotics/motion-tools 0.19.0 → 0.19.2
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/components/Geometry.svelte +1 -23
- package/dist/components/Label.svelte +2 -2
- package/dist/components/Label.svelte.d.ts +1 -1
- package/dist/components/Pose.svelte +1 -1
- package/dist/components/Pose.svelte.d.ts +1 -1
- package/dist/components/Scene.svelte +4 -1
- package/dist/frame.d.ts +2 -0
- package/dist/frame.js +14 -0
- package/dist/geometry.js +18 -0
- package/dist/hooks/use3DModels.svelte.js +9 -3
- package/dist/hooks/useDrawAPI.svelte.js +97 -46
- package/dist/hooks/useFrames.svelte.js +41 -69
- package/dist/hooks/usePose.svelte.js +1 -1
- package/dist/loaders/pcd/worker.d.ts +1 -1
- package/dist/ply.d.ts +2 -0
- package/dist/ply.js +18 -0
- package/dist/transform.js +6 -4
- package/package.json +4 -3
|
@@ -14,9 +14,7 @@ and should remain pure, i.e. no hooks should be used.
|
|
|
14
14
|
import { colors, darkenColor } from '../color'
|
|
15
15
|
import AxesHelper from './AxesHelper.svelte'
|
|
16
16
|
import type { WorldObject } from '../WorldObject.svelte'
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
const plyLoader = new PLYLoader()
|
|
17
|
+
import { parsePlyInput } from '../ply'
|
|
20
18
|
|
|
21
19
|
interface Props extends ThrelteProps<Group> {
|
|
22
20
|
uuid: string
|
|
@@ -75,26 +73,6 @@ and should remain pure, i.e. no hooks should be used.
|
|
|
75
73
|
const oncreate = (ref: BufferGeometry) => {
|
|
76
74
|
geo = ref
|
|
77
75
|
}
|
|
78
|
-
|
|
79
|
-
const parsePlyInput = (mesh: string | Uint8Array): BufferGeometry => {
|
|
80
|
-
// Case 1: already a base64 or ASCII string
|
|
81
|
-
if (typeof mesh === 'string') {
|
|
82
|
-
return plyLoader.parse(atob(mesh))
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Case 2: detect text vs binary PLY in Uint8Array
|
|
86
|
-
const header = new TextDecoder().decode(mesh.slice(0, 50))
|
|
87
|
-
const isAscii = header.includes('format ascii')
|
|
88
|
-
|
|
89
|
-
// Case 3: text-mode PLY → decode bytes to string
|
|
90
|
-
if (isAscii) {
|
|
91
|
-
const text = new TextDecoder().decode(mesh)
|
|
92
|
-
return plyLoader.parse(text)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Case 4: binary PLY → pass ArrayBuffer directly
|
|
96
|
-
return plyLoader.parse(mesh.buffer as ArrayBuffer)
|
|
97
|
-
}
|
|
98
76
|
</script>
|
|
99
77
|
|
|
100
78
|
<T
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { useSettings } from '../hooks/useSettings.svelte'
|
|
5
5
|
|
|
6
6
|
interface Props {
|
|
7
|
-
text
|
|
7
|
+
text?: string
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
let { text }: Props = $props()
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
const labels = $derived(settings.current.enableLabels)
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
|
-
{#if labels}
|
|
17
|
+
{#if labels && text}
|
|
18
18
|
<HTML
|
|
19
19
|
center
|
|
20
20
|
zIndexRange={[100, 0]}
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
return item ? [item] : []
|
|
38
38
|
},
|
|
39
39
|
})
|
|
40
|
-
$effect
|
|
40
|
+
$effect(() => {
|
|
41
41
|
enabled.set(!settings.current.enableMeasure)
|
|
42
42
|
})
|
|
43
43
|
raycaster.firstHitOnly = true
|
|
@@ -89,7 +89,10 @@
|
|
|
89
89
|
{/if}
|
|
90
90
|
|
|
91
91
|
<T.Group attach={focusedObject ? false : undefined}>
|
|
92
|
+
<!-- Capture "default" portals if "world" is not explicit -->
|
|
93
|
+
<PortalTarget />
|
|
92
94
|
<PortalTarget id="world" />
|
|
95
|
+
|
|
93
96
|
<WorldObjects />
|
|
94
97
|
</T.Group>
|
|
95
98
|
|
package/dist/frame.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Transform } from '@viamrobotics/sdk';
|
|
1
2
|
import type { ValueOf } from 'type-fest';
|
|
2
3
|
type FrameGeometryMap = {
|
|
3
4
|
none: {
|
|
@@ -73,4 +74,5 @@ export interface Frame<T extends FrameGeometry = FrameGeometry, K extends FrameO
|
|
|
73
74
|
geometry?: FrameGeometryMap[T];
|
|
74
75
|
}
|
|
75
76
|
export declare const createFrame: <T extends FrameGeometry = "box", K extends FrameOrientation = "ov_degrees">(geometry?: FrameGeometryMap[T]) => Frame<T>;
|
|
77
|
+
export declare const createTransformFromFrame: (name: string, frame: Partial<Frame>) => Transform;
|
|
76
78
|
export {};
|
package/dist/frame.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// TODO: replace with types exported from the sdk when created
|
|
2
|
+
import { UuidTool } from 'uuid-tool';
|
|
3
|
+
import { createPoseFromFrame } from './transform';
|
|
4
|
+
import { createGeometryFromFrame } from './geometry';
|
|
2
5
|
export const createFrame = (geometry) => {
|
|
3
6
|
return {
|
|
4
7
|
parent: 'world',
|
|
@@ -10,3 +13,14 @@ export const createFrame = (geometry) => {
|
|
|
10
13
|
geometry: (geometry ?? { type: 'box', x: 100, y: 100, z: 100 }),
|
|
11
14
|
};
|
|
12
15
|
};
|
|
16
|
+
export const createTransformFromFrame = (name, frame) => {
|
|
17
|
+
return {
|
|
18
|
+
uuid: new Uint8Array(UuidTool.toBytes(UuidTool.newUuid())),
|
|
19
|
+
referenceFrame: name,
|
|
20
|
+
poseInObserverFrame: {
|
|
21
|
+
referenceFrame: frame.parent ?? 'world',
|
|
22
|
+
pose: createPoseFromFrame(frame),
|
|
23
|
+
},
|
|
24
|
+
physicalObject: createGeometryFromFrame(frame),
|
|
25
|
+
};
|
|
26
|
+
};
|
package/dist/geometry.js
CHANGED
|
@@ -40,3 +40,21 @@ export const createGeometryFromFrame = (frame) => {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
+
export const createBox = (box) => {
|
|
44
|
+
return {
|
|
45
|
+
x: (box?.dimsMm?.x ?? 0) * 0.001,
|
|
46
|
+
y: (box?.dimsMm?.y ?? 0) * 0.001,
|
|
47
|
+
z: (box?.dimsMm?.z ?? 0) * 0.001,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export const createCapsule = (capsule) => {
|
|
51
|
+
return {
|
|
52
|
+
r: (capsule?.radiusMm ?? 0) * 0.001,
|
|
53
|
+
l: (capsule?.lengthMm ?? 0) * 0.001,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export const createSphere = (sphere) => {
|
|
57
|
+
return {
|
|
58
|
+
r: (sphere?.radiusMm ?? 0) * 0.001,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
@@ -25,14 +25,20 @@ export const provide3DModels = (partID) => {
|
|
|
25
25
|
if (!client.current)
|
|
26
26
|
continue;
|
|
27
27
|
try {
|
|
28
|
+
const geometries = await client.current.getGeometries();
|
|
29
|
+
if (geometries.length === 0) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const geometryLabel = geometries[0].label;
|
|
33
|
+
const prefix = geometryLabel.split(':')[0];
|
|
28
34
|
const models = await client.current.get3DModels();
|
|
29
|
-
if (!(
|
|
30
|
-
current[
|
|
35
|
+
if (!(prefix in current)) {
|
|
36
|
+
current[prefix] = {};
|
|
31
37
|
}
|
|
32
38
|
for (const [id, model] of Object.entries(models)) {
|
|
33
39
|
const arrayBuffer = model.mesh.buffer.slice(model.mesh.byteOffset, model.mesh.byteOffset + model.mesh.byteLength);
|
|
34
40
|
const gltfModel = await gltfLoader.parseAsync(arrayBuffer, '');
|
|
35
|
-
current[
|
|
41
|
+
current[prefix][id] = gltfModel.scene;
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
catch (error) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
2
|
import { Color, MathUtils, Quaternion, Vector3, Vector4 } from 'three';
|
|
3
3
|
import { NURBSCurve } from 'three/addons/curves/NURBSCurve.js';
|
|
4
|
+
import { UuidTool } from 'uuid-tool';
|
|
4
5
|
import { parsePcdInWorker } from '../loaders/pcd';
|
|
5
6
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
6
7
|
import { WorldObject } from '../WorldObject.svelte';
|
|
@@ -11,6 +12,13 @@ import { useCameraControls } from './useControls.svelte';
|
|
|
11
12
|
import { useThrelte } from '@threlte/core';
|
|
12
13
|
import { OrientationVector } from '../three/OrientationVector';
|
|
13
14
|
import { useLogs } from './useLogs.svelte';
|
|
15
|
+
const bufferTypes = {
|
|
16
|
+
DRAW_POINTS: 0,
|
|
17
|
+
DRAW_POSES: 1,
|
|
18
|
+
DRAW_LINE: 2,
|
|
19
|
+
DRAW_PCD: 3,
|
|
20
|
+
DRAW_GLTF: 4,
|
|
21
|
+
};
|
|
14
22
|
const axis = new Vector3();
|
|
15
23
|
const quaternion = new Quaternion();
|
|
16
24
|
const ov = new OrientationVector();
|
|
@@ -43,8 +51,15 @@ class Float32Reader {
|
|
|
43
51
|
offset = 0;
|
|
44
52
|
buffer = new ArrayBuffer();
|
|
45
53
|
view = new DataView(this.buffer);
|
|
54
|
+
header = { requestID: '', type: -1 };
|
|
46
55
|
async init(data) {
|
|
47
56
|
this.buffer = await data.arrayBuffer();
|
|
57
|
+
this.header = {
|
|
58
|
+
requestID: UuidTool.toString([...new Uint8Array(this.buffer.slice(0, 16))]),
|
|
59
|
+
type: new DataView(this.buffer).getFloat32(16, true),
|
|
60
|
+
};
|
|
61
|
+
// Slice away the request header and leave the body
|
|
62
|
+
this.buffer = this.buffer.slice(20);
|
|
48
63
|
this.view = new DataView(this.buffer);
|
|
49
64
|
return this;
|
|
50
65
|
}
|
|
@@ -77,6 +92,9 @@ export const provideDrawAPI = () => {
|
|
|
77
92
|
const origin = new Vector3();
|
|
78
93
|
const loader = new GLTFLoader();
|
|
79
94
|
const batchedArrow = useArrows();
|
|
95
|
+
const sendResponse = (response) => {
|
|
96
|
+
ws.send(JSON.stringify(response));
|
|
97
|
+
};
|
|
80
98
|
const drawFrames = async (data) => {
|
|
81
99
|
for (const frame of data) {
|
|
82
100
|
const name = frame.name || frame.id || '';
|
|
@@ -204,7 +222,6 @@ export const provideDrawAPI = () => {
|
|
|
204
222
|
},
|
|
205
223
|
}));
|
|
206
224
|
}
|
|
207
|
-
invalidate();
|
|
208
225
|
};
|
|
209
226
|
const drawPoints = async (reader) => {
|
|
210
227
|
// Read label length
|
|
@@ -404,56 +421,90 @@ export const provideDrawAPI = () => {
|
|
|
404
421
|
logs.add(`Drawing server error: ${JSON.stringify(event)}`, 'error');
|
|
405
422
|
};
|
|
406
423
|
const onMessage = async (event) => {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
424
|
+
let operation = 'UNKNOWN';
|
|
425
|
+
let requestID = '';
|
|
426
|
+
try {
|
|
427
|
+
if (typeof event.data === 'object' && 'arrayBuffer' in event.data) {
|
|
428
|
+
const reader = await new Float32Reader().init(event.data);
|
|
429
|
+
requestID = reader.header.requestID;
|
|
430
|
+
const { type } = reader.header;
|
|
431
|
+
if (type === bufferTypes.DRAW_POINTS) {
|
|
432
|
+
operation = 'DrawPoints';
|
|
433
|
+
drawPoints(reader);
|
|
434
|
+
}
|
|
435
|
+
else if (type === bufferTypes.DRAW_POSES) {
|
|
436
|
+
operation = 'DrawPoses';
|
|
437
|
+
drawPoses(reader);
|
|
438
|
+
}
|
|
439
|
+
else if (type === bufferTypes.DRAW_LINE) {
|
|
440
|
+
operation = 'DrawLine';
|
|
441
|
+
drawLine(reader);
|
|
442
|
+
}
|
|
443
|
+
else if (type === bufferTypes.DRAW_PCD) {
|
|
444
|
+
operation = 'DrawPCD';
|
|
445
|
+
drawPCD(reader.buffer);
|
|
446
|
+
}
|
|
447
|
+
else if (type === bufferTypes.DRAW_GLTF) {
|
|
448
|
+
operation = 'DrawGLTF';
|
|
449
|
+
drawGLTF(reader.buffer);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
throw new Error('Invalid buffer');
|
|
453
|
+
}
|
|
421
454
|
}
|
|
422
455
|
else {
|
|
423
|
-
|
|
456
|
+
const [error, data] = tryParse(event.data);
|
|
457
|
+
if (error) {
|
|
458
|
+
logs.add(`Failed to parse JSON from drawing server: ${JSON.stringify(error)}`, 'error');
|
|
459
|
+
throw new Error(`Failed to parse JSON from drawing server: ${JSON.stringify(error)}`);
|
|
460
|
+
}
|
|
461
|
+
if (!data) {
|
|
462
|
+
throw new Error('No drawing data sent to client.');
|
|
463
|
+
}
|
|
464
|
+
requestID = data.requestID;
|
|
465
|
+
if ('setCameraPose' in data) {
|
|
466
|
+
operation = 'SetCameraPose';
|
|
467
|
+
cameraControls.setPose({
|
|
468
|
+
position: [data.Position.X, data.Position.Y, data.Position.Z],
|
|
469
|
+
lookAt: [data.LookAt.X, data.LookAt.Y, data.LookAt.Z],
|
|
470
|
+
}, data.Animate);
|
|
471
|
+
}
|
|
472
|
+
else if ('geometries' in data) {
|
|
473
|
+
operation = 'DrawGeometries';
|
|
474
|
+
drawGeometries(data.geometries, data.colors, data.parent);
|
|
475
|
+
}
|
|
476
|
+
else if ('geometry' in data) {
|
|
477
|
+
operation = 'DrawGeometry';
|
|
478
|
+
drawGeometry(data.geometry, data.color);
|
|
479
|
+
}
|
|
480
|
+
else if ('frames' in data) {
|
|
481
|
+
operation = 'DrawFrames';
|
|
482
|
+
drawFrames(data.frames);
|
|
483
|
+
}
|
|
484
|
+
else if ('Knots' in data) {
|
|
485
|
+
operation = 'DrawNurbs';
|
|
486
|
+
drawNurbs(data, data.Color);
|
|
487
|
+
}
|
|
488
|
+
else if ('remove' in data) {
|
|
489
|
+
operation = 'Remove';
|
|
490
|
+
remove(data.names);
|
|
491
|
+
}
|
|
492
|
+
else if ('removeAll' in data) {
|
|
493
|
+
operation = 'RemoveAll';
|
|
494
|
+
removeAll();
|
|
495
|
+
}
|
|
424
496
|
}
|
|
497
|
+
sendResponse({ code: 200, requestID, message: `${operation} succeeded.` });
|
|
425
498
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
cameraControls.setPose({
|
|
434
|
-
position: [data.Position.X, data.Position.Y, data.Position.Z],
|
|
435
|
-
lookAt: [data.LookAt.X, data.LookAt.Y, data.LookAt.Z],
|
|
436
|
-
}, data.Animate);
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
if ('geometries' in data) {
|
|
440
|
-
return drawGeometries(data.geometries, data.colors, data.parent);
|
|
441
|
-
}
|
|
442
|
-
if ('geometry' in data) {
|
|
443
|
-
return drawGeometry(data.geometry, data.color);
|
|
444
|
-
}
|
|
445
|
-
if ('frames' in data) {
|
|
446
|
-
return drawFrames(data.frames);
|
|
447
|
-
}
|
|
448
|
-
if ('Knots' in data) {
|
|
449
|
-
return drawNurbs(data, data.Color);
|
|
450
|
-
}
|
|
451
|
-
if ('remove' in data) {
|
|
452
|
-
return remove(data.names);
|
|
453
|
-
}
|
|
454
|
-
if ('removeAll' in data) {
|
|
455
|
-
return removeAll();
|
|
499
|
+
catch (error) {
|
|
500
|
+
logs.add(`${error}`, 'error');
|
|
501
|
+
sendResponse({
|
|
502
|
+
code: 500,
|
|
503
|
+
requestID,
|
|
504
|
+
message: `${operation} failed. Reason: ${error}`,
|
|
505
|
+
});
|
|
456
506
|
}
|
|
507
|
+
invalidate();
|
|
457
508
|
};
|
|
458
509
|
const connect = () => {
|
|
459
510
|
if (BACKEND_IP && BUN_SERVER_PORT) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { getContext, setContext, untrack } from 'svelte';
|
|
2
|
+
import { Transform } from '@viamrobotics/sdk';
|
|
2
3
|
import { useRobotClient, createRobotQuery, useMachineStatus } from '@viamrobotics/svelte-sdk';
|
|
3
4
|
import { WorldObject } from '../WorldObject.svelte';
|
|
4
5
|
import { useLogs } from './useLogs.svelte';
|
|
5
6
|
import { resourceNameToColor } from '../color';
|
|
7
|
+
import { createTransformFromFrame } from '../frame';
|
|
6
8
|
import { usePartConfig } from './usePartConfig.svelte';
|
|
7
9
|
import { useEnvironment } from './useEnvironment.svelte';
|
|
8
|
-
import { createPoseFromFrame } from '../transform';
|
|
9
|
-
import { createGeometryFromFrame } from '../geometry';
|
|
10
10
|
import { useResourceByName } from './useResourceByName.svelte';
|
|
11
11
|
import { usePersistentUUIDs } from './usePersistentUUIDs.svelte';
|
|
12
12
|
const key = Symbol('frames-context');
|
|
@@ -20,7 +20,7 @@ export const provideFrames = (partID) => {
|
|
|
20
20
|
const partConfig = usePartConfig();
|
|
21
21
|
const environment = useEnvironment();
|
|
22
22
|
const { updateUUIDs } = usePersistentUUIDs();
|
|
23
|
-
$effect
|
|
23
|
+
$effect(() => {
|
|
24
24
|
if (revision) {
|
|
25
25
|
untrack(() => query.refetch());
|
|
26
26
|
}
|
|
@@ -33,7 +33,7 @@ export const provideFrames = (partID) => {
|
|
|
33
33
|
logs.add(`Frames: ${query.error.message}`, 'error');
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
|
-
$effect
|
|
36
|
+
$effect(() => {
|
|
37
37
|
if (partConfig.isDirty) {
|
|
38
38
|
environment.current.viewerMode = 'edit';
|
|
39
39
|
}
|
|
@@ -42,40 +42,33 @@ export const provideFrames = (partID) => {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
const machineFrames = $derived.by(() => {
|
|
45
|
-
const
|
|
45
|
+
const frames = {};
|
|
46
46
|
for (const { frame } of query.data ?? []) {
|
|
47
47
|
if (frame === undefined) {
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
const frameName = frame.referenceFrame ? frame.referenceFrame : 'Unnamed frame';
|
|
52
|
-
const color = resourceNameToColor(resourceName);
|
|
53
|
-
objects[frameName] = new WorldObject(frameName, frame.poseInObserverFrame?.pose, frame.poseInObserverFrame?.referenceFrame, frame.physicalObject, color ? { color } : undefined);
|
|
50
|
+
frames[frame.referenceFrame] = frame;
|
|
54
51
|
}
|
|
55
|
-
return
|
|
52
|
+
return frames;
|
|
56
53
|
});
|
|
57
|
-
const [configFrames,
|
|
54
|
+
const [configFrames, configUnsetFrameNames] = $derived.by(() => {
|
|
58
55
|
const components = partConfig.localPartConfig.toJson().components;
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
unsetObjects.push(component.name);
|
|
56
|
+
const results = {};
|
|
57
|
+
const unsetResults = [];
|
|
58
|
+
for (const { name, frame } of components ?? []) {
|
|
59
|
+
if (!frame) {
|
|
60
|
+
unsetResults.push(name);
|
|
65
61
|
continue;
|
|
66
62
|
}
|
|
67
|
-
|
|
68
|
-
const geometry = createGeometryFromFrame(component.frame);
|
|
69
|
-
const worldObject = new WorldObject(component.name, pose, component.frame.parent, geometry);
|
|
70
|
-
objects.push(worldObject);
|
|
63
|
+
results[name] = createTransformFromFrame(name, frame);
|
|
71
64
|
}
|
|
72
|
-
return [
|
|
65
|
+
return [results, unsetResults];
|
|
73
66
|
});
|
|
74
|
-
const [fragmentFrames,
|
|
67
|
+
const [fragmentFrames, fragmentUnsetFrameNames] = $derived.by(() => {
|
|
75
68
|
const { fragment_mods: fragmentMods = [] } = partConfig.localPartConfig.toJson() ?? {};
|
|
76
69
|
const fragmentDefinedComponents = Object.keys(partConfig.componentNameToFragmentId);
|
|
77
|
-
const
|
|
78
|
-
const
|
|
70
|
+
const results = {};
|
|
71
|
+
const unsetResults = [];
|
|
79
72
|
// deal with fragment defined components
|
|
80
73
|
for (const fragmentComponentName of fragmentDefinedComponents || []) {
|
|
81
74
|
const fragmentId = partConfig.componentNameToFragmentId[fragmentComponentName];
|
|
@@ -86,60 +79,39 @@ export const provideFrames = (partID) => {
|
|
|
86
79
|
const setComponentModIndex = fragmentMod.mods.findLastIndex((mod) => mod['$set']?.[`components.${fragmentComponentName}.frame`] !== undefined);
|
|
87
80
|
const unsetComponentModIndex = fragmentMod.mods.findLastIndex((mod) => mod['$unset']?.[`components.${fragmentComponentName}.frame`] !== undefined);
|
|
88
81
|
if (setComponentModIndex < unsetComponentModIndex) {
|
|
89
|
-
|
|
82
|
+
unsetResults.push(fragmentComponentName);
|
|
90
83
|
}
|
|
91
84
|
else if (unsetComponentModIndex < setComponentModIndex) {
|
|
92
85
|
const frameData = fragmentMod.mods[setComponentModIndex]['$set'][`components.${fragmentComponentName}.frame`];
|
|
93
|
-
|
|
94
|
-
const geometry = createGeometryFromFrame(frameData);
|
|
95
|
-
const worldObject = new WorldObject(fragmentComponentName, pose, frameData.parent, geometry);
|
|
96
|
-
objects.push(worldObject);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return [objects, unsetObjects];
|
|
100
|
-
});
|
|
101
|
-
$effect.pre(() => {
|
|
102
|
-
for (const frame of configFrames) {
|
|
103
|
-
const result = machineFrames[frame.name];
|
|
104
|
-
if (result) {
|
|
105
|
-
result.referenceFrame = frame.referenceFrame;
|
|
106
|
-
result.localEditedPose = frame.pose;
|
|
107
|
-
result.geometry = frame.geometry;
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
machineFrames[frame.name] = frame;
|
|
86
|
+
results[fragmentComponentName] = createTransformFromFrame(fragmentComponentName, frameData);
|
|
111
87
|
}
|
|
112
88
|
}
|
|
89
|
+
return [results, unsetResults];
|
|
113
90
|
});
|
|
114
|
-
$
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
machineFrames[frame.name] = frame;
|
|
124
|
-
}
|
|
91
|
+
const frames = $derived.by(() => {
|
|
92
|
+
const result = {
|
|
93
|
+
...machineFrames,
|
|
94
|
+
...configFrames,
|
|
95
|
+
...fragmentFrames,
|
|
96
|
+
};
|
|
97
|
+
// Remove frames that have just been deleted locally for optimistic updates
|
|
98
|
+
for (const name of configUnsetFrameNames) {
|
|
99
|
+
delete result[name];
|
|
125
100
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
delete machineFrames[name];
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
$effect.pre(() => {
|
|
133
|
-
for (const name of fragmentUnsetFrames) {
|
|
134
|
-
delete machineFrames[name];
|
|
101
|
+
// Remove frames that have been removed by fragment overrides
|
|
102
|
+
for (const name of fragmentUnsetFrameNames) {
|
|
103
|
+
delete result[name];
|
|
135
104
|
}
|
|
105
|
+
return result;
|
|
136
106
|
});
|
|
137
107
|
const current = $derived.by(() => {
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
108
|
+
const results = [];
|
|
109
|
+
for (const frame of Object.values(frames)) {
|
|
110
|
+
const resourceName = resourceByName.current[frame.referenceFrame];
|
|
111
|
+
const frameName = frame.referenceFrame ? frame.referenceFrame : 'Unnamed frame';
|
|
112
|
+
const color = resourceNameToColor(resourceName);
|
|
113
|
+
results.push(new WorldObject(frameName, frame.poseInObserverFrame?.pose, frame.poseInObserverFrame?.referenceFrame, frame.physicalObject, color ? { color } : undefined));
|
|
114
|
+
}
|
|
143
115
|
updateUUIDs(results);
|
|
144
116
|
return results;
|
|
145
117
|
});
|
|
@@ -20,7 +20,7 @@ export const usePose = (name, parent) => {
|
|
|
20
20
|
const currentParent = $derived(parent());
|
|
21
21
|
const resourceByName = useResourceByName();
|
|
22
22
|
const { addQueryToRefetch } = useRefetchPoses();
|
|
23
|
-
const resource = $derived(resourceByName.current[currentName]);
|
|
23
|
+
const resource = $derived(currentName ? resourceByName.current[currentName] : undefined);
|
|
24
24
|
const parentResource = $derived(currentParent ? resourceByName.current[currentParent] : undefined);
|
|
25
25
|
const environment = useEnvironment();
|
|
26
26
|
const frames = useFrames();
|
package/dist/ply.d.ts
ADDED
package/dist/ply.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PLYLoader } from 'three/addons/loaders/PLYLoader.js';
|
|
2
|
+
const plyLoader = new PLYLoader();
|
|
3
|
+
export const parsePlyInput = (mesh) => {
|
|
4
|
+
// Case 1: already a base64 or ASCII string
|
|
5
|
+
if (typeof mesh === 'string') {
|
|
6
|
+
return plyLoader.parse(atob(mesh));
|
|
7
|
+
}
|
|
8
|
+
// Case 2: detect text vs binary PLY in Uint8Array
|
|
9
|
+
const header = new TextDecoder().decode(mesh.slice(0, 50));
|
|
10
|
+
const isAscii = header.includes('format ascii');
|
|
11
|
+
// Case 3: text-mode PLY → decode bytes to string
|
|
12
|
+
if (isAscii) {
|
|
13
|
+
const text = new TextDecoder().decode(mesh);
|
|
14
|
+
return plyLoader.parse(text);
|
|
15
|
+
}
|
|
16
|
+
// Case 4: binary PLY → pass ArrayBuffer directly
|
|
17
|
+
return plyLoader.parse(mesh.buffer);
|
|
18
|
+
};
|
package/dist/transform.js
CHANGED
|
@@ -66,12 +66,14 @@ export const object3dToPose = (object3d, pose) => {
|
|
|
66
66
|
return pose;
|
|
67
67
|
};
|
|
68
68
|
export const poseToQuaternion = (pose, quaternion) => {
|
|
69
|
-
const th = MathUtils.degToRad(pose
|
|
70
|
-
ov.set(pose
|
|
71
|
-
|
|
69
|
+
const th = MathUtils.degToRad(pose?.theta ?? 0);
|
|
70
|
+
ov.set(pose?.oX, pose?.oY, pose?.oZ, th);
|
|
71
|
+
if (quaternion) {
|
|
72
|
+
ov.toQuaternion(quaternion);
|
|
73
|
+
}
|
|
72
74
|
};
|
|
73
75
|
export const poseToVector3 = (pose, vec3) => {
|
|
74
|
-
vec3
|
|
76
|
+
vec3?.set(pose?.x ?? 0, pose?.y ?? 0, pose?.z ?? 0).multiplyScalar(0.001);
|
|
75
77
|
};
|
|
76
78
|
export const poseToObject3d = (pose, object3d) => {
|
|
77
79
|
poseToVector3(pose, object3d.position);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viamrobotics/motion-tools",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "Motion visualization with Viam",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"svelte-virtuallists": ">=1"
|
|
90
90
|
},
|
|
91
91
|
"engines": {
|
|
92
|
-
"node": ">=22.
|
|
92
|
+
"node": ">=22.12.0"
|
|
93
93
|
},
|
|
94
94
|
"svelte": "./dist/index.js",
|
|
95
95
|
"types": "./dist/index.d.ts",
|
|
@@ -117,7 +117,8 @@
|
|
|
117
117
|
"!dist/**/*.spec.*"
|
|
118
118
|
],
|
|
119
119
|
"dependencies": {
|
|
120
|
-
"@tanstack/svelte-query-devtools": "^6.0.2"
|
|
120
|
+
"@tanstack/svelte-query-devtools": "^6.0.2",
|
|
121
|
+
"uuid-tool": "^2.0.3"
|
|
121
122
|
},
|
|
122
123
|
"scripts": {
|
|
123
124
|
"dev": "tsx server/check-bun && bun run server/server.ts",
|