@viamrobotics/motion-tools 1.19.1 → 1.22.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/dist/FrameConfigUpdater.svelte.d.ts +0 -1
- package/dist/FrameConfigUpdater.svelte.js +6 -24
- package/dist/buf/draw/v1/metadata_pb.d.ts +39 -0
- package/dist/buf/draw/v1/metadata_pb.js +55 -0
- package/dist/buf/draw/v1/service_connect.d.ts +34 -1
- package/dist/buf/draw/v1/service_connect.js +34 -1
- package/dist/buf/draw/v1/service_pb.d.ts +136 -0
- package/dist/buf/draw/v1/service_pb.js +201 -0
- package/dist/components/Entities/Arrows/ArrowGroups.svelte +1 -0
- package/dist/components/Entities/Arrows/Arrows.svelte +1 -1
- package/dist/components/Entities/Points.svelte +23 -23
- package/dist/components/Entities/Pose.svelte +18 -13
- package/dist/components/Entities/hooks/useEntityEvents.svelte.js +18 -1
- package/dist/components/FileDrop/FileDrop.svelte +8 -1
- package/dist/components/FileDrop/useFileDrop.svelte.js +16 -2
- package/dist/components/PCD.svelte +9 -1
- package/dist/components/PCD.svelte.d.ts +2 -0
- package/dist/components/PointerMissBox.svelte +1 -1
- package/dist/components/Scene.svelte +2 -0
- package/dist/components/SceneProviders.svelte +4 -0
- package/dist/components/SelectedTransformControls.svelte +227 -0
- package/dist/components/SelectedTransformControls.svelte.d.ts +3 -0
- package/dist/components/Snapshot.svelte +12 -7
- package/dist/components/StaticGeometries.svelte +3 -56
- package/dist/components/overlay/AddRelationship.svelte +25 -3
- package/dist/components/overlay/Details.svelte +290 -229
- package/dist/components/overlay/dashboard/Button.svelte +4 -2
- package/dist/components/overlay/dashboard/Button.svelte.d.ts +1 -1
- package/dist/components/overlay/dashboard/Dashboard.svelte +43 -33
- package/dist/draw.d.ts +22 -9
- package/dist/draw.js +71 -41
- package/dist/ecs/relations.js +1 -1
- package/dist/ecs/traits.d.ts +17 -0
- package/dist/ecs/traits.js +9 -0
- package/dist/editing/FrameEditSession.d.ts +37 -0
- package/dist/editing/FrameEditSession.js +178 -0
- package/dist/hooks/useDrawService.svelte.d.ts +2 -0
- package/dist/hooks/useDrawService.svelte.js +139 -20
- package/dist/hooks/useFrameEditSession.svelte.d.ts +15 -0
- package/dist/hooks/useFrameEditSession.svelte.js +36 -0
- package/dist/hooks/useFrames.svelte.js +37 -2
- package/dist/hooks/usePartConfig.svelte.js +10 -0
- package/dist/hooks/useRelationships.svelte.d.ts +12 -0
- package/dist/hooks/useRelationships.svelte.js +78 -0
- package/dist/hooks/useSettings.svelte.d.ts +1 -2
- package/dist/hooks/useSettings.svelte.js +1 -2
- package/dist/hooks/useWorldState.svelte.js +10 -4
- package/dist/metadata.d.ts +7 -3
- package/dist/metadata.js +26 -2
- package/dist/snapshot.d.ts +6 -1
- package/dist/snapshot.js +10 -5
- package/dist/transform.js +13 -0
- package/package.json +7 -4
|
@@ -38,40 +38,50 @@
|
|
|
38
38
|
</fieldset>
|
|
39
39
|
|
|
40
40
|
<!-- transform -->
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
41
|
+
<fieldset class="flex">
|
|
42
|
+
<Button
|
|
43
|
+
icon="mouse-pointer"
|
|
44
|
+
active={settings.current.transformMode === 'none'}
|
|
45
|
+
description="No transform controls"
|
|
46
|
+
hotkey="0"
|
|
47
|
+
onclick={() => {
|
|
48
|
+
settings.current.transformMode = 'none'
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
<Button
|
|
52
|
+
icon="cursor-move"
|
|
53
|
+
active={settings.current.transformMode === 'translate'}
|
|
54
|
+
description="Translate"
|
|
55
|
+
hotkey="1"
|
|
56
|
+
class="-ml-px"
|
|
57
|
+
onclick={() => {
|
|
58
|
+
settings.current.transformMode = 'translate'
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
<Button
|
|
62
|
+
icon="sync"
|
|
63
|
+
active={settings.current.transformMode === 'rotate'}
|
|
64
|
+
description="Rotate"
|
|
65
|
+
hotkey="2"
|
|
66
|
+
class="-ml-px"
|
|
67
|
+
onclick={() => {
|
|
68
|
+
settings.current.transformMode = 'rotate'
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
<Button
|
|
72
|
+
icon="resize"
|
|
73
|
+
active={settings.current.transformMode === 'scale'}
|
|
74
|
+
description="Scale"
|
|
75
|
+
hotkey="3"
|
|
76
|
+
class="-ml-px"
|
|
77
|
+
onclick={() => {
|
|
78
|
+
settings.current.transformMode = 'scale'
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
</fieldset>
|
|
73
82
|
|
|
74
|
-
|
|
83
|
+
<!-- snapping -->
|
|
84
|
+
{#if settings.current.transformMode !== 'none'}
|
|
75
85
|
<fieldset class="flex">
|
|
76
86
|
<Button
|
|
77
87
|
icon={settings.current.snapping ? 'magnet' : 'magnet-off'}
|
package/dist/draw.d.ts
CHANGED
|
@@ -2,18 +2,31 @@ import type { TransformWithUUID } from '@viamrobotics/sdk';
|
|
|
2
2
|
import type { Entity, Trait, World } from 'koota';
|
|
3
3
|
import type { Transform as TransformProto } from './buf/common/v1/common_pb';
|
|
4
4
|
import type { Drawing } from './buf/draw/v1/drawing_pb';
|
|
5
|
+
import type { Relationship } from './metadata';
|
|
5
6
|
import { type Metadata } from './metadata';
|
|
6
7
|
export type Transform = TransformWithUUID | TransformProto;
|
|
7
|
-
|
|
8
|
+
export declare const uuidBytesToString: (bytes: Uint8Array | undefined) => string | undefined;
|
|
9
|
+
export declare const uuidStringToBytes: (uuid: string) => Uint8Array<ArrayBuffer>;
|
|
10
|
+
interface DrawOptions {
|
|
8
11
|
removable?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const drawTransform: (world: World, { referenceFrame, poseInObserverFrame, physicalObject, metadata, uuid }: Transform, api: Trait, { removable }?: DrawOptions) => {
|
|
14
|
+
entity: Entity;
|
|
15
|
+
relationships: import("@bufbuild/protobuf").PlainMessage<import("./buf/draw/v1/metadata_pb").Relationship>[] | undefined;
|
|
9
16
|
};
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
export interface DrawingResult {
|
|
18
|
+
entity: Entity;
|
|
19
|
+
relationships: Relationship[] | undefined;
|
|
20
|
+
}
|
|
21
|
+
export declare const drawDrawing: (world: World, drawing: Drawing, api: Trait, { removable }?: DrawOptions) => DrawingResult;
|
|
22
|
+
export declare const updateTransform: (entity: Entity, { poseInObserverFrame, physicalObject, metadata }: Transform, { removable }?: DrawOptions) => {
|
|
23
|
+
entity: Entity;
|
|
24
|
+
relationships: import("@bufbuild/protobuf").PlainMessage<import("./buf/draw/v1/metadata_pb").Relationship>[] | undefined;
|
|
25
|
+
};
|
|
26
|
+
interface MetadataOptions {
|
|
14
27
|
pointCloud?: boolean;
|
|
15
|
-
}
|
|
16
|
-
export declare const
|
|
17
|
-
export declare const
|
|
18
|
-
export declare const
|
|
28
|
+
}
|
|
29
|
+
export declare const updateMetadata: (entity: Entity, metadata: Metadata, { pointCloud }?: MetadataOptions) => void;
|
|
30
|
+
export declare const updateDrawing: (world: World, entity: Entity, drawing: Drawing, { removable }?: DrawOptions) => DrawingResult;
|
|
31
|
+
export declare const updateModel: (world: World, entity: Entity, drawing: Drawing, api: Trait, { removable }?: DrawOptions) => DrawingResult;
|
|
19
32
|
export {};
|
package/dist/draw.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Vector3, Vector4 } from 'three';
|
|
2
2
|
import { NURBSCurve } from 'three/addons/curves/NURBSCurve.js';
|
|
3
|
+
import { UuidTool } from 'uuid-tool';
|
|
3
4
|
import { createBufferGeometry, preAllocateBufferGeometry, updateBufferGeometry, writeBufferGeometryRange, } from './attribute';
|
|
4
5
|
import { asFloat32Array, asOpacity, asRGB, inMeters, isSingleColor, isVertexColors, STRIDE, } from './buffer';
|
|
5
|
-
import { traits } from './ecs';
|
|
6
|
+
import { relations, traits } from './ecs';
|
|
6
7
|
import { parsePcdInWorker } from './loaders/pcd';
|
|
7
8
|
import { metadataFromStruct } from './metadata';
|
|
8
9
|
import { createPose } from './transform';
|
|
@@ -21,12 +22,28 @@ const DEFAULT_LINE_DOT_COLORS = new Uint8Array([0, 0, 139]);
|
|
|
21
22
|
const DEFAULT_POINTS_COLORS = new Uint8Array([51, 51, 51]);
|
|
22
23
|
const DEFAULT_NURBS_COLORS = new Uint8Array([0, 255, 255]);
|
|
23
24
|
const DEFAULT_OPACITY = 1;
|
|
24
|
-
export const
|
|
25
|
+
export const uuidBytesToString = (bytes) => {
|
|
26
|
+
if (!bytes || bytes.length === 0)
|
|
27
|
+
return undefined;
|
|
28
|
+
return UuidTool.toString([...bytes]);
|
|
29
|
+
};
|
|
30
|
+
export const uuidStringToBytes = (uuid) => {
|
|
31
|
+
const arr = new Uint8Array(16);
|
|
32
|
+
arr.set(UuidTool.toBytes(uuid));
|
|
33
|
+
return arr;
|
|
34
|
+
};
|
|
35
|
+
const isModel = (drawing) => {
|
|
36
|
+
return drawing.physicalObject?.geometryType?.case === 'model';
|
|
37
|
+
};
|
|
38
|
+
export const drawTransform = (world, { referenceFrame, poseInObserverFrame, physicalObject, metadata, uuid }, api, { removable = true } = {}) => {
|
|
25
39
|
const entityTraits = [
|
|
26
40
|
traits.Name(referenceFrame),
|
|
27
41
|
traits.Pose(createPose(poseInObserverFrame?.pose)),
|
|
28
42
|
api,
|
|
29
43
|
];
|
|
44
|
+
const uuidStr = uuidBytesToString(uuid);
|
|
45
|
+
if (uuidStr)
|
|
46
|
+
entityTraits.push(traits.UUID(uuidStr));
|
|
30
47
|
if (physicalObject) {
|
|
31
48
|
entityTraits.push(traits.Geometry(physicalObject));
|
|
32
49
|
const center = physicalObject.center;
|
|
@@ -36,7 +53,7 @@ export const drawTransform = (world, { referenceFrame, poseInObserverFrame, phys
|
|
|
36
53
|
else {
|
|
37
54
|
entityTraits.push(traits.ReferenceFrame);
|
|
38
55
|
}
|
|
39
|
-
if (
|
|
56
|
+
if (removable)
|
|
40
57
|
entityTraits.push(traits.Removable);
|
|
41
58
|
entityTraits.push(...traits.getParentTrait(poseInObserverFrame?.referenceFrame));
|
|
42
59
|
const parsedMetadata = metadataFromStruct(metadata?.fields);
|
|
@@ -60,23 +77,28 @@ export const drawTransform = (world, { referenceFrame, poseInObserverFrame, phys
|
|
|
60
77
|
const entity = world.spawn(...entityTraits);
|
|
61
78
|
if (pointCloud)
|
|
62
79
|
parsePointCloud(world, entity, pointCloud, parsedMetadata);
|
|
63
|
-
return entity;
|
|
80
|
+
return { entity, relationships: parsedMetadata.relationships };
|
|
64
81
|
};
|
|
65
|
-
export const drawDrawing = (world, drawing, api,
|
|
66
|
-
const { referenceFrame, poseInObserverFrame,
|
|
67
|
-
if (
|
|
68
|
-
return drawModel(world, drawing, api,
|
|
69
|
-
|
|
70
|
-
|
|
82
|
+
export const drawDrawing = (world, drawing, api, { removable = true } = {}) => {
|
|
83
|
+
const { referenceFrame, poseInObserverFrame, metadata, uuid } = drawing;
|
|
84
|
+
if (isModel(drawing)) {
|
|
85
|
+
return drawModel(world, drawing, api, { removable });
|
|
86
|
+
}
|
|
87
|
+
const uuidTraits = [];
|
|
88
|
+
const uuidStr = uuidBytesToString(uuid);
|
|
89
|
+
if (uuidStr)
|
|
90
|
+
uuidTraits.push(traits.UUID(uuidStr));
|
|
91
|
+
const entity = world.spawn(traits.Name(referenceFrame), traits.Pose(createPose(poseInObserverFrame?.pose)), api, ...traits.getParentTrait(poseInObserverFrame?.referenceFrame), ...uuidTraits);
|
|
92
|
+
if (removable)
|
|
71
93
|
entity.add(traits.Removable);
|
|
72
94
|
if (metadata?.showAxesHelper)
|
|
73
95
|
entity.add(traits.ShowAxesHelper);
|
|
74
96
|
if (metadata?.invisible)
|
|
75
97
|
entity.add(traits.Invisible);
|
|
76
98
|
applyShape(entity, drawing);
|
|
77
|
-
return
|
|
99
|
+
return { entity, relationships: metadata?.relationships };
|
|
78
100
|
};
|
|
79
|
-
export const updateTransform = (entity, { poseInObserverFrame, physicalObject, metadata },
|
|
101
|
+
export const updateTransform = (entity, { poseInObserverFrame, physicalObject, metadata }, { removable = true } = {}) => {
|
|
80
102
|
entity.set(traits.Pose, createPose(poseInObserverFrame?.pose));
|
|
81
103
|
traits.setParentTrait(entity, poseInObserverFrame?.referenceFrame);
|
|
82
104
|
if (physicalObject) {
|
|
@@ -89,13 +111,15 @@ export const updateTransform = (entity, { poseInObserverFrame, physicalObject, m
|
|
|
89
111
|
entity.remove(traits.Center);
|
|
90
112
|
}
|
|
91
113
|
}
|
|
92
|
-
|
|
114
|
+
const parsedMetadata = metadataFromStruct(metadata?.fields);
|
|
115
|
+
updateMetadata(entity, parsedMetadata, {
|
|
93
116
|
pointCloud: isPointCloud(physicalObject?.geometryType),
|
|
94
117
|
});
|
|
95
|
-
if (
|
|
118
|
+
if (removable)
|
|
96
119
|
entity.add(traits.Removable);
|
|
97
|
-
if (!
|
|
120
|
+
if (!removable)
|
|
98
121
|
entity.remove(traits.Removable);
|
|
122
|
+
return { entity, relationships: parsedMetadata.relationships };
|
|
99
123
|
};
|
|
100
124
|
export const updateMetadata = (entity, metadata, { pointCloud = false } = {}) => {
|
|
101
125
|
if (metadata.showAxesHelper)
|
|
@@ -116,20 +140,10 @@ export const updateMetadata = (entity, metadata, { pointCloud = false } = {}) =>
|
|
|
116
140
|
}
|
|
117
141
|
entity.set(traits.Opacity, asOpacity(opacities, DEFAULT_OPACITY));
|
|
118
142
|
};
|
|
119
|
-
export const updateDrawing = (world,
|
|
120
|
-
const { poseInObserverFrame,
|
|
121
|
-
if (physicalObject?.geometryType?.case === 'model') {
|
|
122
|
-
for (const entity of entities) {
|
|
123
|
-
if (world.has(entity))
|
|
124
|
-
entity.destroy();
|
|
125
|
-
}
|
|
126
|
-
return drawDrawing(world, drawing, api, options);
|
|
127
|
-
}
|
|
128
|
-
if (entities.length === 0)
|
|
129
|
-
return entities;
|
|
130
|
-
const entity = entities[0];
|
|
143
|
+
export const updateDrawing = (world, entity, drawing, { removable = true } = {}) => {
|
|
144
|
+
const { poseInObserverFrame, metadata } = drawing;
|
|
131
145
|
if (!world.has(entity))
|
|
132
|
-
return
|
|
146
|
+
return { entity, relationships: metadata?.relationships };
|
|
133
147
|
entity.set(traits.Pose, createPose(poseInObserverFrame?.pose));
|
|
134
148
|
traits.setParentTrait(entity, poseInObserverFrame?.referenceFrame);
|
|
135
149
|
if (metadata?.showAxesHelper)
|
|
@@ -140,8 +154,17 @@ export const updateDrawing = (world, entities, drawing, api, options = { removab
|
|
|
140
154
|
entity.add(traits.Invisible);
|
|
141
155
|
if (!metadata?.invisible)
|
|
142
156
|
entity.remove(traits.Invisible);
|
|
157
|
+
if (removable)
|
|
158
|
+
entity.add(traits.Removable);
|
|
159
|
+
if (!removable)
|
|
160
|
+
entity.remove(traits.Removable);
|
|
143
161
|
updateShape(entity, drawing);
|
|
144
|
-
return
|
|
162
|
+
return { entity, relationships: metadata?.relationships };
|
|
163
|
+
};
|
|
164
|
+
export const updateModel = (world, entity, drawing, api, { removable = true } = {}) => {
|
|
165
|
+
if (world.has(entity))
|
|
166
|
+
entity.destroy();
|
|
167
|
+
return drawDrawing(world, drawing, api, { removable });
|
|
145
168
|
};
|
|
146
169
|
const applyShape = (entity, { physicalObject, metadata }) => {
|
|
147
170
|
const colors = metadata?.colors;
|
|
@@ -242,32 +265,39 @@ const applyShape = (entity, { physicalObject, metadata }) => {
|
|
|
242
265
|
}
|
|
243
266
|
}
|
|
244
267
|
};
|
|
245
|
-
const drawModel = (world,
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
if (geometryType?.case !== 'model')
|
|
249
|
-
return entities;
|
|
268
|
+
const drawModel = (world, model, api, { removable = true }) => {
|
|
269
|
+
const { referenceFrame, physicalObject, poseInObserverFrame, metadata, uuid } = model;
|
|
270
|
+
const { animationName, assets, scale } = physicalObject.geometryType.value;
|
|
250
271
|
const baseTraits = [
|
|
251
272
|
traits.Name(referenceFrame),
|
|
252
273
|
traits.Pose(createPose(poseInObserverFrame?.pose)),
|
|
253
274
|
api,
|
|
254
275
|
...traits.getParentTrait(poseInObserverFrame?.referenceFrame),
|
|
255
276
|
];
|
|
277
|
+
const uuidStr = uuidBytesToString(uuid);
|
|
278
|
+
if (uuidStr)
|
|
279
|
+
baseTraits.push(traits.UUID(uuidStr));
|
|
256
280
|
if (removable)
|
|
257
281
|
baseTraits.push(traits.Removable);
|
|
258
282
|
if (metadata?.invisible)
|
|
259
283
|
baseTraits.push(traits.Invisible);
|
|
260
|
-
|
|
261
|
-
|
|
284
|
+
if (metadata?.showAxesHelper)
|
|
285
|
+
baseTraits.push(traits.ShowAxesHelper);
|
|
286
|
+
const root = world.spawn(...baseTraits, traits.ReferenceFrame);
|
|
262
287
|
let i = 1;
|
|
263
|
-
for (const asset of
|
|
288
|
+
for (const asset of assets) {
|
|
264
289
|
const subEntityTraits = [
|
|
265
290
|
traits.Name(`${referenceFrame} model ${i++}`),
|
|
266
291
|
traits.Parent(referenceFrame),
|
|
292
|
+
relations.ChildOf(root),
|
|
267
293
|
api,
|
|
268
294
|
];
|
|
269
295
|
if (scale)
|
|
270
296
|
subEntityTraits.push(traits.Scale(scale));
|
|
297
|
+
if (metadata?.invisible)
|
|
298
|
+
subEntityTraits.push(traits.Invisible);
|
|
299
|
+
if (metadata?.showAxesHelper)
|
|
300
|
+
subEntityTraits.push(traits.ShowAxesHelper);
|
|
271
301
|
if (asset.content.case === 'url') {
|
|
272
302
|
subEntityTraits.push(traits.GLTF({
|
|
273
303
|
source: { url: asset.content.value },
|
|
@@ -280,9 +310,9 @@ const drawModel = (world, { referenceFrame, poseInObserverFrame, physicalObject,
|
|
|
280
310
|
animationName: animationName ?? DEFAULT_ANIMATION_NAME,
|
|
281
311
|
}));
|
|
282
312
|
}
|
|
283
|
-
|
|
313
|
+
world.spawn(...subEntityTraits);
|
|
284
314
|
}
|
|
285
|
-
return
|
|
315
|
+
return { entity: root, relationships: metadata?.relationships };
|
|
286
316
|
};
|
|
287
317
|
const parsePointCloud = (world, entity, pointCloud, metadata) => {
|
|
288
318
|
parsePcdInWorker(new Uint8Array(pointCloud)).then((pointcloud) => {
|
|
@@ -422,7 +452,7 @@ const updateShape = (entity, { physicalObject, metadata }) => {
|
|
|
422
452
|
}
|
|
423
453
|
}
|
|
424
454
|
};
|
|
425
|
-
|
|
455
|
+
const addColorTraits = (entity, colors) => {
|
|
426
456
|
if (isVertexColors(colors)) {
|
|
427
457
|
entity.add(traits.Colors(colors));
|
|
428
458
|
}
|
|
@@ -430,7 +460,7 @@ export const addColorTraits = (entity, colors) => {
|
|
|
430
460
|
entity.add(traits.Color(asRGB(colors, rgb)));
|
|
431
461
|
}
|
|
432
462
|
};
|
|
433
|
-
|
|
463
|
+
const setColorTraits = (entity, colors) => {
|
|
434
464
|
if (isVertexColors(colors)) {
|
|
435
465
|
if (entity.has(traits.Colors))
|
|
436
466
|
entity.set(traits.Colors, colors);
|
package/dist/ecs/relations.js
CHANGED
package/dist/ecs/traits.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { type ConfigurableTrait, type Entity } from 'koota';
|
|
|
4
4
|
import { BufferGeometry as ThreeBufferGeometry } from 'three';
|
|
5
5
|
export declare const Name: import("koota").Trait<() => string>;
|
|
6
6
|
export declare const Parent: import("koota").Trait<() => string>;
|
|
7
|
+
export declare const UUID: import("koota").Trait<() => string>;
|
|
7
8
|
export declare const Pose: import("koota").Trait<{
|
|
8
9
|
x: number;
|
|
9
10
|
y: number;
|
|
@@ -22,6 +23,15 @@ export declare const EditedPose: import("koota").Trait<{
|
|
|
22
23
|
oZ: number;
|
|
23
24
|
theta: number;
|
|
24
25
|
}>;
|
|
26
|
+
export declare const LivePose: import("koota").Trait<{
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
z: number;
|
|
30
|
+
oX: number;
|
|
31
|
+
oY: number;
|
|
32
|
+
oZ: number;
|
|
33
|
+
theta: number;
|
|
34
|
+
}>;
|
|
25
35
|
export declare const Center: import("koota").Trait<{
|
|
26
36
|
x: number;
|
|
27
37
|
y: number;
|
|
@@ -77,6 +87,7 @@ export declare const Color: import("koota").Trait<{
|
|
|
77
87
|
*/
|
|
78
88
|
export declare const Material: import("koota").Trait<{
|
|
79
89
|
depthTest: boolean;
|
|
90
|
+
depthWrite: boolean;
|
|
80
91
|
}>;
|
|
81
92
|
export declare const DepthTest: import("koota").Trait<() => boolean>;
|
|
82
93
|
export declare const Arrow: import("koota").Trait<() => boolean>;
|
|
@@ -144,6 +155,12 @@ export declare const SnapshotAPI: import("koota").Trait<() => boolean>;
|
|
|
144
155
|
* Marker trait for entities created from user-dropped files (PLY, PCD, etc.)
|
|
145
156
|
*/
|
|
146
157
|
export declare const DroppedFile: import("koota").Trait<() => boolean>;
|
|
158
|
+
/**
|
|
159
|
+
* Marker trait for entities the dashboard's TransformControls may attach to —
|
|
160
|
+
* editable frames and ad-hoc custom geometries. Other entity kinds (lines,
|
|
161
|
+
* points, batched arrows, etc.) are deliberately excluded.
|
|
162
|
+
*/
|
|
163
|
+
export declare const Transformable: import("koota").Trait<() => boolean>;
|
|
147
164
|
export declare const ShowAxesHelper: import("koota").Trait<() => boolean>;
|
|
148
165
|
/**
|
|
149
166
|
* Marker trait for entities that should be rendered in screen space (CSS pixels)
|
package/dist/ecs/traits.js
CHANGED
|
@@ -8,8 +8,10 @@ import { parsePcdInWorker } from '../loaders/pcd';
|
|
|
8
8
|
import { parsePlyInput } from '../ply';
|
|
9
9
|
export const Name = trait(() => '');
|
|
10
10
|
export const Parent = trait(() => 'world');
|
|
11
|
+
export const UUID = trait(() => '');
|
|
11
12
|
export const Pose = trait({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 });
|
|
12
13
|
export const EditedPose = trait({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 });
|
|
14
|
+
export const LivePose = trait({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 });
|
|
13
15
|
export const Center = trait({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 });
|
|
14
16
|
export const InstancedPose = trait({
|
|
15
17
|
x: 0,
|
|
@@ -53,6 +55,7 @@ export const Color = trait({ r: 0, g: 0, b: 0 });
|
|
|
53
55
|
*/
|
|
54
56
|
export const Material = trait({
|
|
55
57
|
depthTest: false,
|
|
58
|
+
depthWrite: true,
|
|
56
59
|
});
|
|
57
60
|
export const DepthTest = trait(() => true);
|
|
58
61
|
export const Arrow = trait(() => true);
|
|
@@ -101,6 +104,12 @@ export const SnapshotAPI = trait(() => true);
|
|
|
101
104
|
* Marker trait for entities created from user-dropped files (PLY, PCD, etc.)
|
|
102
105
|
*/
|
|
103
106
|
export const DroppedFile = trait(() => true);
|
|
107
|
+
/**
|
|
108
|
+
* Marker trait for entities the dashboard's TransformControls may attach to —
|
|
109
|
+
* editable frames and ad-hoc custom geometries. Other entity kinds (lines,
|
|
110
|
+
* points, batched arrows, etc.) are deliberately excluded.
|
|
111
|
+
*/
|
|
112
|
+
export const Transformable = trait(() => true);
|
|
104
113
|
export const ShowAxesHelper = trait(() => true);
|
|
105
114
|
/**
|
|
106
115
|
* Marker trait for entities that should be rendered in screen space (CSS pixels)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Pose } from '@viamrobotics/sdk';
|
|
2
|
+
import type { Entity } from 'koota';
|
|
3
|
+
import type { Frame } from '../frame';
|
|
4
|
+
export type UpdateFrameFn = (componentName: string, referenceFrame: string, pose: Pose, geometry?: Frame['geometry']) => void;
|
|
5
|
+
export type DeleteFrameFn = (componentName: string) => void;
|
|
6
|
+
/**
|
|
7
|
+
* A single user gesture against one or more frames (drag, parent change, geometry tweak).
|
|
8
|
+
* Owns the affected entities until commit() or abort() runs. Snapshots their pre-gesture
|
|
9
|
+
* trait state so abort() can restore — both the ECS view and the dirty part config.
|
|
10
|
+
*
|
|
11
|
+
* Replaces the Transforming marker trait: while a session is active, useFrames asks
|
|
12
|
+
* `session.owns(entity)` instead of inspecting a per-entity flag.
|
|
13
|
+
*/
|
|
14
|
+
export declare class FrameEditSession {
|
|
15
|
+
#private;
|
|
16
|
+
private snapshots;
|
|
17
|
+
private updateFrame;
|
|
18
|
+
private deleteFrame;
|
|
19
|
+
private onClose;
|
|
20
|
+
constructor(entities: Entity[], updateFrame: UpdateFrameFn, deleteFrame: DeleteFrameFn, onClose: () => void);
|
|
21
|
+
get isClosed(): boolean;
|
|
22
|
+
owns(entity: Entity | undefined): boolean;
|
|
23
|
+
stagePose: (entity: Entity, pose: Partial<Pose>) => void;
|
|
24
|
+
stageGeometry: (entity: Entity, geometry: Frame["geometry"]) => void;
|
|
25
|
+
stageParent: (entity: Entity, parent: string) => void;
|
|
26
|
+
stageDelete: (entity: Entity) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Validate and close. Returns true on success. On invalid pose data
|
|
29
|
+
* (NaN/infinite from a degenerate gizmo state), aborts and returns false.
|
|
30
|
+
*/
|
|
31
|
+
commit: () => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Restore each owned entity's traits to its pre-session state and re-issue
|
|
34
|
+
* an updateFrame so the dirty part config matches.
|
|
35
|
+
*/
|
|
36
|
+
abort: () => void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { traits } from '../ecs';
|
|
2
|
+
import { isFinitePose } from '../transform';
|
|
3
|
+
const captureGeometry = (entity) => {
|
|
4
|
+
const box = entity.get(traits.Box);
|
|
5
|
+
if (box)
|
|
6
|
+
return { type: 'box', box: { ...box } };
|
|
7
|
+
const sphere = entity.get(traits.Sphere);
|
|
8
|
+
if (sphere)
|
|
9
|
+
return { type: 'sphere', sphere: { ...sphere } };
|
|
10
|
+
const capsule = entity.get(traits.Capsule);
|
|
11
|
+
if (capsule)
|
|
12
|
+
return { type: 'capsule', capsule: { ...capsule } };
|
|
13
|
+
return { type: 'none' };
|
|
14
|
+
};
|
|
15
|
+
const restoreGeometryTrait = (entity, snap) => {
|
|
16
|
+
if (snap.type === 'none') {
|
|
17
|
+
entity.remove(traits.Box, traits.Sphere, traits.Capsule);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (snap.type === 'box' && snap.box) {
|
|
21
|
+
entity.remove(traits.Sphere, traits.Capsule);
|
|
22
|
+
if (entity.has(traits.Box))
|
|
23
|
+
entity.set(traits.Box, snap.box);
|
|
24
|
+
else
|
|
25
|
+
entity.add(traits.Box(snap.box));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (snap.type === 'sphere' && snap.sphere) {
|
|
29
|
+
entity.remove(traits.Box, traits.Capsule);
|
|
30
|
+
if (entity.has(traits.Sphere))
|
|
31
|
+
entity.set(traits.Sphere, snap.sphere);
|
|
32
|
+
else
|
|
33
|
+
entity.add(traits.Sphere(snap.sphere));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (snap.type === 'capsule' && snap.capsule) {
|
|
37
|
+
entity.remove(traits.Box, traits.Sphere);
|
|
38
|
+
if (entity.has(traits.Capsule))
|
|
39
|
+
entity.set(traits.Capsule, snap.capsule);
|
|
40
|
+
else
|
|
41
|
+
entity.add(traits.Capsule(snap.capsule));
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const snapshotToFrameGeometry = (snap) => {
|
|
45
|
+
if (snap.type === 'box' && snap.box)
|
|
46
|
+
return { type: 'box', ...snap.box };
|
|
47
|
+
if (snap.type === 'sphere' && snap.sphere)
|
|
48
|
+
return { type: 'sphere', ...snap.sphere };
|
|
49
|
+
if (snap.type === 'capsule' && snap.capsule)
|
|
50
|
+
return { type: 'capsule', ...snap.capsule };
|
|
51
|
+
return { type: 'none' };
|
|
52
|
+
};
|
|
53
|
+
const liveGeometry = (entity) => snapshotToFrameGeometry(captureGeometry(entity));
|
|
54
|
+
/**
|
|
55
|
+
* A single user gesture against one or more frames (drag, parent change, geometry tweak).
|
|
56
|
+
* Owns the affected entities until commit() or abort() runs. Snapshots their pre-gesture
|
|
57
|
+
* trait state so abort() can restore — both the ECS view and the dirty part config.
|
|
58
|
+
*
|
|
59
|
+
* Replaces the Transforming marker trait: while a session is active, useFrames asks
|
|
60
|
+
* `session.owns(entity)` instead of inspecting a per-entity flag.
|
|
61
|
+
*/
|
|
62
|
+
export class FrameEditSession {
|
|
63
|
+
snapshots = new Map();
|
|
64
|
+
updateFrame;
|
|
65
|
+
deleteFrame;
|
|
66
|
+
onClose;
|
|
67
|
+
#closed = false;
|
|
68
|
+
constructor(entities, updateFrame, deleteFrame, onClose) {
|
|
69
|
+
this.updateFrame = updateFrame;
|
|
70
|
+
this.deleteFrame = deleteFrame;
|
|
71
|
+
this.onClose = onClose;
|
|
72
|
+
for (const entity of entities) {
|
|
73
|
+
const name = entity.get(traits.Name);
|
|
74
|
+
const editedPose = entity.get(traits.EditedPose);
|
|
75
|
+
if (!name || !editedPose)
|
|
76
|
+
continue;
|
|
77
|
+
this.snapshots.set(entity, {
|
|
78
|
+
name,
|
|
79
|
+
parent: entity.get(traits.Parent) ?? 'world',
|
|
80
|
+
editedPose: { ...editedPose },
|
|
81
|
+
geometry: captureGeometry(entity),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
get isClosed() {
|
|
86
|
+
return this.#closed;
|
|
87
|
+
}
|
|
88
|
+
owns(entity) {
|
|
89
|
+
return entity !== undefined && !this.#closed && this.snapshots.has(entity);
|
|
90
|
+
}
|
|
91
|
+
stagePose = (entity, pose) => {
|
|
92
|
+
const snap = this.snapshots.get(entity);
|
|
93
|
+
if (!snap || this.#closed)
|
|
94
|
+
return;
|
|
95
|
+
const current = entity.get(traits.EditedPose);
|
|
96
|
+
if (!current)
|
|
97
|
+
return;
|
|
98
|
+
const next = { ...current, ...pose };
|
|
99
|
+
entity.set(traits.EditedPose, next);
|
|
100
|
+
this.updateFrame(snap.name, entity.get(traits.Parent) ?? 'world', next, liveGeometry(entity));
|
|
101
|
+
};
|
|
102
|
+
stageGeometry = (entity, geometry) => {
|
|
103
|
+
const snap = this.snapshots.get(entity);
|
|
104
|
+
if (!snap || this.#closed || !geometry)
|
|
105
|
+
return;
|
|
106
|
+
if (geometry.type === 'none') {
|
|
107
|
+
entity.remove(traits.Box, traits.Sphere, traits.Capsule);
|
|
108
|
+
}
|
|
109
|
+
else if (geometry.type === 'box') {
|
|
110
|
+
const data = { x: geometry.x, y: geometry.y, z: geometry.z };
|
|
111
|
+
restoreGeometryTrait(entity, { type: 'box', box: data });
|
|
112
|
+
}
|
|
113
|
+
else if (geometry.type === 'sphere') {
|
|
114
|
+
restoreGeometryTrait(entity, { type: 'sphere', sphere: { r: geometry.r } });
|
|
115
|
+
}
|
|
116
|
+
else if (geometry.type === 'capsule') {
|
|
117
|
+
restoreGeometryTrait(entity, { type: 'capsule', capsule: { r: geometry.r, l: geometry.l } });
|
|
118
|
+
}
|
|
119
|
+
const editedPose = entity.get(traits.EditedPose);
|
|
120
|
+
if (editedPose) {
|
|
121
|
+
this.updateFrame(snap.name, entity.get(traits.Parent) ?? 'world', editedPose, geometry);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
stageParent = (entity, parent) => {
|
|
125
|
+
const snap = this.snapshots.get(entity);
|
|
126
|
+
if (!snap || this.#closed)
|
|
127
|
+
return;
|
|
128
|
+
traits.setParentTrait(entity, parent === 'world' ? undefined : parent);
|
|
129
|
+
const editedPose = entity.get(traits.EditedPose);
|
|
130
|
+
if (editedPose) {
|
|
131
|
+
this.updateFrame(snap.name, parent, editedPose, liveGeometry(entity));
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
stageDelete = (entity) => {
|
|
135
|
+
const snap = this.snapshots.get(entity);
|
|
136
|
+
if (!snap || this.#closed)
|
|
137
|
+
return;
|
|
138
|
+
this.deleteFrame(snap.name);
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Validate and close. Returns true on success. On invalid pose data
|
|
142
|
+
* (NaN/infinite from a degenerate gizmo state), aborts and returns false.
|
|
143
|
+
*/
|
|
144
|
+
commit = () => {
|
|
145
|
+
if (this.#closed)
|
|
146
|
+
return false;
|
|
147
|
+
for (const [entity] of this.snapshots) {
|
|
148
|
+
const pose = entity.get(traits.EditedPose);
|
|
149
|
+
if (pose && !isFinitePose(pose)) {
|
|
150
|
+
this.abort();
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
this.#close();
|
|
155
|
+
return true;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Restore each owned entity's traits to its pre-session state and re-issue
|
|
159
|
+
* an updateFrame so the dirty part config matches.
|
|
160
|
+
*/
|
|
161
|
+
abort = () => {
|
|
162
|
+
if (this.#closed)
|
|
163
|
+
return;
|
|
164
|
+
for (const [entity, snap] of this.snapshots) {
|
|
165
|
+
if (entity.isAlive()) {
|
|
166
|
+
entity.set(traits.EditedPose, snap.editedPose);
|
|
167
|
+
traits.setParentTrait(entity, snap.parent === 'world' ? undefined : snap.parent);
|
|
168
|
+
restoreGeometryTrait(entity, snap.geometry);
|
|
169
|
+
}
|
|
170
|
+
this.updateFrame(snap.name, snap.parent, snap.editedPose, snapshotToFrameGeometry(snap.geometry));
|
|
171
|
+
}
|
|
172
|
+
this.#close();
|
|
173
|
+
};
|
|
174
|
+
#close = () => {
|
|
175
|
+
this.#closed = true;
|
|
176
|
+
this.onClose();
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -6,6 +6,8 @@ declare const ConnectionStatus: {
|
|
|
6
6
|
type ConnectionStatusType = (typeof ConnectionStatus)[keyof typeof ConnectionStatus];
|
|
7
7
|
interface Context {
|
|
8
8
|
connectionStatus: ConnectionStatusType;
|
|
9
|
+
createRelationship: (sourceUuid: string, targetUuid: string, type: string, indexMapping?: string) => Promise<void>;
|
|
10
|
+
deleteRelationship: (sourceUuid: string, targetUuid: string) => Promise<void>;
|
|
9
11
|
}
|
|
10
12
|
export declare function provideDrawService(): void;
|
|
11
13
|
export declare function useDrawService(): Context;
|