@viamrobotics/motion-tools 1.18.1 → 1.19.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/attribute.d.ts +2 -0
- package/dist/attribute.js +47 -1
- package/dist/buf/draw/v1/metadata_pb.d.ts +39 -0
- package/dist/buf/draw/v1/metadata_pb.js +55 -0
- package/dist/chunking.d.ts +26 -0
- package/dist/chunking.js +59 -0
- package/dist/color.d.ts +1 -0
- package/dist/color.js +11 -3
- package/dist/components/Entities/Arrows/Arrows.svelte +1 -1
- package/dist/components/Entities/Mesh.svelte +21 -5
- package/dist/components/Entities/Pose.svelte +3 -1
- package/dist/components/PCD.svelte +6 -2
- package/dist/components/Scene.svelte +0 -1
- package/dist/components/overlay/Details.svelte +2 -0
- package/dist/components/overlay/left-pane/TreeNode.svelte +65 -36
- package/dist/draw.d.ts +6 -0
- package/dist/draw.js +64 -42
- package/dist/ecs/traits.d.ts +12 -2
- package/dist/ecs/traits.js +19 -1
- package/dist/hooks/useDrawAPI.svelte.js +11 -12
- package/dist/hooks/useFrames.svelte.js +60 -22
- package/dist/hooks/useGeometries.svelte.js +11 -2
- package/dist/hooks/useLogs.svelte.js +3 -3
- package/dist/hooks/usePartConfig.svelte.d.ts +4 -0
- package/dist/hooks/usePartConfig.svelte.js +29 -9
- package/dist/hooks/usePointcloudObjects.svelte.js +2 -5
- package/dist/hooks/useWorldState.svelte.js +90 -17
- package/dist/metadata.js +13 -1
- package/package.json +3 -3
package/dist/attribute.d.ts
CHANGED
|
@@ -2,3 +2,5 @@ import { BufferGeometry } from 'three';
|
|
|
2
2
|
import type { Metadata } from './metadata';
|
|
3
3
|
export declare const createBufferGeometry: (positions: Float32Array, metadata?: Metadata) => BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
|
|
4
4
|
export declare const updateBufferGeometry: (geometry: BufferGeometry, positions: Float32Array, metadata: Metadata) => void;
|
|
5
|
+
export declare const preAllocateBufferGeometry: (total: number, size: number, metadata: Metadata) => BufferGeometry;
|
|
6
|
+
export declare const writeBufferGeometryRange: (geometry: BufferGeometry, positions: Float32Array, start: number, metadata: Metadata) => void;
|
package/dist/attribute.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BufferAttribute, BufferGeometry } from 'three';
|
|
2
|
-
import { colorStride } from './buffer';
|
|
2
|
+
import { colorStride, STRIDE } from './buffer';
|
|
3
3
|
export const createBufferGeometry = (positions, metadata) => {
|
|
4
4
|
const geometry = new BufferGeometry();
|
|
5
5
|
geometry.setAttribute('position', new BufferAttribute(positions, 3));
|
|
@@ -44,3 +44,49 @@ export const updateBufferGeometry = (geometry, positions, metadata) => {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
+
export const preAllocateBufferGeometry = (total, size, metadata) => {
|
|
48
|
+
const geometry = new BufferGeometry();
|
|
49
|
+
const posAttr = new BufferAttribute(new Float32Array(total * size), size);
|
|
50
|
+
geometry.setAttribute('position', posAttr);
|
|
51
|
+
if (metadata.colors) {
|
|
52
|
+
const stride = colorStride(metadata.colorFormat) || STRIDE.COLORS_RGB;
|
|
53
|
+
const colorAttr = new BufferAttribute(new Uint8Array(total * stride), stride, true);
|
|
54
|
+
geometry.setAttribute('color', colorAttr);
|
|
55
|
+
}
|
|
56
|
+
if (metadata.opacities) {
|
|
57
|
+
const opacityAttr = new BufferAttribute(new Uint8Array(total), 1, true);
|
|
58
|
+
geometry.setAttribute('opacity', opacityAttr);
|
|
59
|
+
}
|
|
60
|
+
geometry.setDrawRange(0, 0);
|
|
61
|
+
return geometry;
|
|
62
|
+
};
|
|
63
|
+
export const writeBufferGeometryRange = (geometry, positions, start, metadata) => {
|
|
64
|
+
const chunkElements = positions.length / 3;
|
|
65
|
+
const posAttr = geometry.getAttribute('position');
|
|
66
|
+
posAttr.array.set(positions, start * 3);
|
|
67
|
+
posAttr.addUpdateRange(start * 3, chunkElements * 3);
|
|
68
|
+
posAttr.needsUpdate = true;
|
|
69
|
+
if (metadata.colors) {
|
|
70
|
+
const colorAttr = geometry.getAttribute('color');
|
|
71
|
+
if (colorAttr) {
|
|
72
|
+
const stride = colorAttr.itemSize;
|
|
73
|
+
colorAttr.array.set(metadata.colors, start * stride);
|
|
74
|
+
colorAttr.addUpdateRange(start * stride, chunkElements * stride);
|
|
75
|
+
colorAttr.needsUpdate = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (metadata.opacities) {
|
|
79
|
+
const opacityAttr = geometry.getAttribute('opacity');
|
|
80
|
+
if (opacityAttr) {
|
|
81
|
+
;
|
|
82
|
+
opacityAttr.array.set(metadata.opacities, start);
|
|
83
|
+
opacityAttr.addUpdateRange(start, chunkElements);
|
|
84
|
+
opacityAttr.needsUpdate = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const endPoint = start + chunkElements;
|
|
88
|
+
const currentEnd = geometry.drawRange.count;
|
|
89
|
+
if (endPoint > currentEnd) {
|
|
90
|
+
geometry.setDrawRange(0, endPoint);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
@@ -15,6 +15,39 @@ export declare enum ColorFormat {
|
|
|
15
15
|
*/
|
|
16
16
|
RGB = 1
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Chunks describes how a large entity is chunked for progressive delivery.
|
|
20
|
+
*
|
|
21
|
+
* @generated from message draw.v1.Chunks
|
|
22
|
+
*/
|
|
23
|
+
export declare class Chunks extends Message<Chunks> {
|
|
24
|
+
/**
|
|
25
|
+
* The number of elements per chunk.
|
|
26
|
+
*
|
|
27
|
+
* @generated from field: uint32 chunk_size = 1;
|
|
28
|
+
*/
|
|
29
|
+
chunkSize: number;
|
|
30
|
+
/**
|
|
31
|
+
* The total number of elements across all chunks.
|
|
32
|
+
*
|
|
33
|
+
* @generated from field: uint32 total = 2;
|
|
34
|
+
*/
|
|
35
|
+
total: number;
|
|
36
|
+
/**
|
|
37
|
+
* The number of bytes per element (e.g. 12 for float32 xyz positions).
|
|
38
|
+
*
|
|
39
|
+
* @generated from field: uint32 stride = 3;
|
|
40
|
+
*/
|
|
41
|
+
stride: number;
|
|
42
|
+
constructor(data?: PartialMessage<Chunks>);
|
|
43
|
+
static readonly runtime: typeof proto3;
|
|
44
|
+
static readonly typeName = "draw.v1.Chunks";
|
|
45
|
+
static readonly fields: FieldList;
|
|
46
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Chunks;
|
|
47
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Chunks;
|
|
48
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Chunks;
|
|
49
|
+
static equals(a: Chunks | PlainMessage<Chunks> | undefined, b: Chunks | PlainMessage<Chunks> | undefined): boolean;
|
|
50
|
+
}
|
|
18
51
|
/**
|
|
19
52
|
* @generated from message draw.v1.Metadata
|
|
20
53
|
*/
|
|
@@ -53,6 +86,12 @@ export declare class Metadata extends Message<Metadata> {
|
|
|
53
86
|
* @generated from field: optional bool invisible = 5;
|
|
54
87
|
*/
|
|
55
88
|
invisible?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* When present, indicates this drawing uses chunked delivery.
|
|
91
|
+
*
|
|
92
|
+
* @generated from field: optional draw.v1.Chunks chunks = 6;
|
|
93
|
+
*/
|
|
94
|
+
chunks?: Chunks;
|
|
56
95
|
constructor(data?: PartialMessage<Metadata>);
|
|
57
96
|
static readonly runtime: typeof proto3;
|
|
58
97
|
static readonly typeName = "draw.v1.Metadata";
|
|
@@ -24,6 +24,54 @@ proto3.util.setEnumType(ColorFormat, "draw.v1.ColorFormat", [
|
|
|
24
24
|
{ no: 0, name: "COLOR_FORMAT_UNSPECIFIED" },
|
|
25
25
|
{ no: 1, name: "COLOR_FORMAT_RGB" },
|
|
26
26
|
]);
|
|
27
|
+
/**
|
|
28
|
+
* Chunks describes how a large entity is chunked for progressive delivery.
|
|
29
|
+
*
|
|
30
|
+
* @generated from message draw.v1.Chunks
|
|
31
|
+
*/
|
|
32
|
+
export class Chunks extends Message {
|
|
33
|
+
/**
|
|
34
|
+
* The number of elements per chunk.
|
|
35
|
+
*
|
|
36
|
+
* @generated from field: uint32 chunk_size = 1;
|
|
37
|
+
*/
|
|
38
|
+
chunkSize = 0;
|
|
39
|
+
/**
|
|
40
|
+
* The total number of elements across all chunks.
|
|
41
|
+
*
|
|
42
|
+
* @generated from field: uint32 total = 2;
|
|
43
|
+
*/
|
|
44
|
+
total = 0;
|
|
45
|
+
/**
|
|
46
|
+
* The number of bytes per element (e.g. 12 for float32 xyz positions).
|
|
47
|
+
*
|
|
48
|
+
* @generated from field: uint32 stride = 3;
|
|
49
|
+
*/
|
|
50
|
+
stride = 0;
|
|
51
|
+
constructor(data) {
|
|
52
|
+
super();
|
|
53
|
+
proto3.util.initPartial(data, this);
|
|
54
|
+
}
|
|
55
|
+
static runtime = proto3;
|
|
56
|
+
static typeName = "draw.v1.Chunks";
|
|
57
|
+
static fields = proto3.util.newFieldList(() => [
|
|
58
|
+
{ no: 1, name: "chunk_size", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
59
|
+
{ no: 2, name: "total", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
60
|
+
{ no: 3, name: "stride", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
61
|
+
]);
|
|
62
|
+
static fromBinary(bytes, options) {
|
|
63
|
+
return new Chunks().fromBinary(bytes, options);
|
|
64
|
+
}
|
|
65
|
+
static fromJson(jsonValue, options) {
|
|
66
|
+
return new Chunks().fromJson(jsonValue, options);
|
|
67
|
+
}
|
|
68
|
+
static fromJsonString(jsonString, options) {
|
|
69
|
+
return new Chunks().fromJsonString(jsonString, options);
|
|
70
|
+
}
|
|
71
|
+
static equals(a, b) {
|
|
72
|
+
return proto3.util.equals(Chunks, a, b);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
27
75
|
/**
|
|
28
76
|
* @generated from message draw.v1.Metadata
|
|
29
77
|
*/
|
|
@@ -62,6 +110,12 @@ export class Metadata extends Message {
|
|
|
62
110
|
* @generated from field: optional bool invisible = 5;
|
|
63
111
|
*/
|
|
64
112
|
invisible;
|
|
113
|
+
/**
|
|
114
|
+
* When present, indicates this drawing uses chunked delivery.
|
|
115
|
+
*
|
|
116
|
+
* @generated from field: optional draw.v1.Chunks chunks = 6;
|
|
117
|
+
*/
|
|
118
|
+
chunks;
|
|
65
119
|
constructor(data) {
|
|
66
120
|
super();
|
|
67
121
|
proto3.util.initPartial(data, this);
|
|
@@ -74,6 +128,7 @@ export class Metadata extends Message {
|
|
|
74
128
|
{ no: 3, name: "opacities", kind: "scalar", T: 12 /* ScalarType.BYTES */, opt: true },
|
|
75
129
|
{ no: 4, name: "show_axes_helper", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true },
|
|
76
130
|
{ no: 5, name: "invisible", kind: "scalar", T: 8 /* ScalarType.BOOL */, opt: true },
|
|
131
|
+
{ no: 6, name: "chunks", kind: "message", T: Chunks, opt: true },
|
|
77
132
|
]);
|
|
78
133
|
static fromBinary(bytes, options) {
|
|
79
134
|
return new Metadata().fromBinary(bytes, options);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Entity, World } from 'koota';
|
|
2
|
+
import { ColorFormat } from './buf/draw/v1/metadata_pb';
|
|
3
|
+
import { type Metadata } from './metadata';
|
|
4
|
+
export interface EntityChunk {
|
|
5
|
+
/** Element offset (in points) where this chunk should be written. */
|
|
6
|
+
start: number;
|
|
7
|
+
/** Flat `[x, y, z, ...]` positions in meters. */
|
|
8
|
+
positions: Float32Array;
|
|
9
|
+
/** Optional colors aligned with `positions`. */
|
|
10
|
+
colors?: Uint8Array;
|
|
11
|
+
/** Optional per-vertex opacities aligned with `positions`. */
|
|
12
|
+
opacities?: Uint8Array;
|
|
13
|
+
/** `true` when the server has no more chunks for this entity. */
|
|
14
|
+
done: boolean;
|
|
15
|
+
}
|
|
16
|
+
export type ChunkFetcher = (uuid: string, start: number, signal: AbortSignal) => Promise<EntityChunk | null>;
|
|
17
|
+
export interface ChunkLoaderOptions {
|
|
18
|
+
world: World;
|
|
19
|
+
invalidate: () => void;
|
|
20
|
+
fetchChunk: ChunkFetcher;
|
|
21
|
+
colorFormat?: ColorFormat;
|
|
22
|
+
}
|
|
23
|
+
export declare const createChunkLoader: ({ world, invalidate, fetchChunk, colorFormat, }: ChunkLoaderOptions) => {
|
|
24
|
+
start(uuid: string, entity: Entity, metadata: Metadata): void;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
};
|
package/dist/chunking.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { writeBufferGeometryRange } from './attribute';
|
|
2
|
+
import { ColorFormat } from './buf/draw/v1/metadata_pb';
|
|
3
|
+
import { traits } from './ecs';
|
|
4
|
+
import {} from './metadata';
|
|
5
|
+
export const createChunkLoader = ({ world, invalidate, fetchChunk, colorFormat = ColorFormat.RGB, }) => {
|
|
6
|
+
const active = new Set();
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
const pull = async (uuid, entity, total, firstChunkEnd) => {
|
|
9
|
+
if (active.has(uuid))
|
|
10
|
+
return;
|
|
11
|
+
active.add(uuid);
|
|
12
|
+
const { signal } = controller;
|
|
13
|
+
let nextStart = firstChunkEnd;
|
|
14
|
+
try {
|
|
15
|
+
while (!signal.aborted) {
|
|
16
|
+
const chunk = await fetchChunk(uuid, nextStart, signal);
|
|
17
|
+
if (signal.aborted || !chunk)
|
|
18
|
+
break;
|
|
19
|
+
const buffer = entity.get(traits.BufferGeometry);
|
|
20
|
+
if (!buffer)
|
|
21
|
+
break;
|
|
22
|
+
writeBufferGeometryRange(buffer, chunk.positions, chunk.start, {
|
|
23
|
+
colorFormat,
|
|
24
|
+
colors: chunk.colors,
|
|
25
|
+
opacities: chunk.opacities,
|
|
26
|
+
});
|
|
27
|
+
const chunkElements = chunk.positions.length / 3;
|
|
28
|
+
nextStart = chunk.start + chunkElements;
|
|
29
|
+
entity.set(traits.ChunkProgress, { loaded: nextStart, total });
|
|
30
|
+
invalidate();
|
|
31
|
+
if (chunk.done)
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (!signal.aborted) {
|
|
37
|
+
console.error(`Chunk pull failed for entity ${uuid}:`, error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
active.delete(uuid);
|
|
42
|
+
if (world.has(entity)) {
|
|
43
|
+
entity.remove(traits.ChunkProgress);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
start(uuid, entity, metadata) {
|
|
49
|
+
const chunks = metadata.chunks;
|
|
50
|
+
if (!chunks || chunks.total <= 0)
|
|
51
|
+
return;
|
|
52
|
+
entity.add(traits.ChunkProgress({ loaded: chunks.chunkSize, total: chunks.total }));
|
|
53
|
+
void pull(uuid, entity, chunks.total, chunks.chunkSize);
|
|
54
|
+
},
|
|
55
|
+
dispose() {
|
|
56
|
+
controller.abort();
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
};
|
package/dist/color.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Color, type ColorRepresentation, type RGB } from 'three';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const darkenColor: (value: ColorRepresentation, percent: number) => Color;
|
|
10
10
|
export declare const resourceNameToColor: (resourceName?: ResourceName) => Color | undefined;
|
|
11
|
+
export declare const subtypeToColor: (subtype?: string) => Color | undefined;
|
|
11
12
|
export declare const colors: {
|
|
12
13
|
readonly default: string;
|
|
13
14
|
};
|
package/dist/color.js
CHANGED
|
@@ -55,9 +55,17 @@ export const darkenColor = (value, percent) => {
|
|
|
55
55
|
return new Color().setHSL(hsl.h, hsl.s, hsl.l);
|
|
56
56
|
};
|
|
57
57
|
export const resourceNameToColor = (resourceName) => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
if (!resourceName)
|
|
59
|
+
return undefined;
|
|
60
|
+
return subtypeToColor(resourceName.subtype);
|
|
61
|
+
};
|
|
62
|
+
export const subtypeToColor = (subtype) => {
|
|
63
|
+
if (!subtype)
|
|
64
|
+
return undefined;
|
|
65
|
+
const colorValue = resourceColors[subtype];
|
|
66
|
+
if (!colorValue)
|
|
67
|
+
return undefined;
|
|
68
|
+
return new Color(colorValue);
|
|
61
69
|
};
|
|
62
70
|
const darkness = '600';
|
|
63
71
|
export const colors = {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { T, type Props as ThrelteProps, useThrelte } from '@threlte/core'
|
|
6
6
|
import { type Snippet } from 'svelte'
|
|
7
|
-
import { BufferGeometry, Color, DoubleSide, FrontSide, Mesh } from 'three'
|
|
7
|
+
import { BufferGeometry, Color, DoubleSide, FrontSide, Material, Mesh } from 'three'
|
|
8
8
|
|
|
9
9
|
import { asColor } from '../../buffer'
|
|
10
10
|
import { colors, darkenColor } from '../../color'
|
|
@@ -56,8 +56,19 @@
|
|
|
56
56
|
|
|
57
57
|
const currentOpacity = $derived(opacity.current ?? 0.7)
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
let material = $state.raw<Material>(new Material())
|
|
60
|
+
$effect(() => {
|
|
61
|
+
const isTransparent = currentOpacity < 1
|
|
62
|
+
material.depthWrite = !isTransparent
|
|
63
|
+
material.opacity = currentOpacity
|
|
64
|
+
if (material.transparent !== isTransparent) {
|
|
65
|
+
material.transparent = isTransparent
|
|
66
|
+
material.needsUpdate = true
|
|
67
|
+
invalidate()
|
|
68
|
+
}
|
|
69
|
+
})
|
|
60
70
|
|
|
71
|
+
const mesh = new Mesh()
|
|
61
72
|
$effect.pre(() => {
|
|
62
73
|
if (center) {
|
|
63
74
|
poseToObject3d(center, mesh)
|
|
@@ -66,6 +77,11 @@
|
|
|
66
77
|
})
|
|
67
78
|
|
|
68
79
|
let geo = $state.raw<BufferGeometry>()
|
|
80
|
+
$effect.pre(() => {
|
|
81
|
+
if (!box.current && !sphere.current && !capsule.current && !bufferGeometry.current) {
|
|
82
|
+
geo = undefined
|
|
83
|
+
}
|
|
84
|
+
})
|
|
69
85
|
|
|
70
86
|
const oncreate = (bufferGeometry: BufferGeometry) => {
|
|
71
87
|
geo = bufferGeometry
|
|
@@ -108,10 +124,10 @@
|
|
|
108
124
|
<T.MeshToonMaterial
|
|
109
125
|
{color}
|
|
110
126
|
side={bufferGeometry.current ? DoubleSide : FrontSide}
|
|
111
|
-
transparent={currentOpacity < 1}
|
|
112
|
-
depthWrite={currentOpacity === 1}
|
|
113
|
-
opacity={currentOpacity}
|
|
114
127
|
depthTest={materialProps.current?.depthTest ?? true}
|
|
128
|
+
oncreate={(m) => {
|
|
129
|
+
material = m
|
|
130
|
+
}}
|
|
115
131
|
/>
|
|
116
132
|
|
|
117
133
|
<!--
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { Snippet } from 'svelte'
|
|
5
5
|
|
|
6
6
|
import { traits, useTrait } from '../../ecs'
|
|
7
|
+
import { usePartConfig } from '../../hooks/usePartConfig.svelte'
|
|
7
8
|
import { usePose } from '../../hooks/usePose.svelte'
|
|
8
9
|
import { matrixToPose, poseToMatrix } from '../../transform'
|
|
9
10
|
|
|
@@ -13,6 +14,7 @@
|
|
|
13
14
|
}
|
|
14
15
|
let { entity, children }: Props = $props()
|
|
15
16
|
|
|
17
|
+
const partConfig = usePartConfig()
|
|
16
18
|
const name = useTrait(() => entity, traits.Name)
|
|
17
19
|
const parent = useTrait(() => entity, traits.Parent)
|
|
18
20
|
const editedPose = useTrait(() => entity, traits.EditedPose)
|
|
@@ -24,7 +26,7 @@
|
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
const resolvedPose = $derived.by(() => {
|
|
27
|
-
if (pose.current === undefined) {
|
|
29
|
+
if (pose.current === undefined || partConfig.hasPendingSave) {
|
|
28
30
|
return editedPose.current
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -20,10 +20,13 @@
|
|
|
20
20
|
|
|
21
21
|
const world = useWorld()
|
|
22
22
|
|
|
23
|
-
let entity: Entity
|
|
24
|
-
|
|
25
23
|
$effect(() => {
|
|
24
|
+
let entity: Entity | undefined
|
|
25
|
+
let cancelled = false
|
|
26
|
+
|
|
26
27
|
parsePcdInWorker(data).then(({ positions, colors }) => {
|
|
28
|
+
if (cancelled) return
|
|
29
|
+
|
|
27
30
|
const geometry = createBufferGeometry(positions, { colors, colorFormat: ColorFormat.RGB })
|
|
28
31
|
|
|
29
32
|
const entityTraits: ConfigurableTrait[] = [
|
|
@@ -45,6 +48,7 @@
|
|
|
45
48
|
})
|
|
46
49
|
|
|
47
50
|
return () => {
|
|
51
|
+
cancelled = true
|
|
48
52
|
if (entity && world.has(entity)) {
|
|
49
53
|
entity.destroy()
|
|
50
54
|
}
|
|
@@ -404,6 +404,8 @@
|
|
|
404
404
|
value: parent.current ?? 'world',
|
|
405
405
|
options: configFrames.getParentFrameOptions(name.current ?? ''),
|
|
406
406
|
onChange: (value) => {
|
|
407
|
+
if (value === parent.current) return
|
|
408
|
+
traits.setParentTrait(entity, value)
|
|
407
409
|
detailConfigUpdater.setFrameParent(entity, value)
|
|
408
410
|
},
|
|
409
411
|
})}
|
|
@@ -20,11 +20,33 @@
|
|
|
20
20
|
|
|
21
21
|
const name = useTrait(() => node.entity, traits.Name)
|
|
22
22
|
const invisible = useTrait(() => node.entity, traits.Invisible)
|
|
23
|
+
const chunkProgress = useTrait(() => node.entity, traits.ChunkProgress)
|
|
24
|
+
const loading = $derived(chunkProgress.current !== undefined)
|
|
25
|
+
const progress = $derived(
|
|
26
|
+
chunkProgress.current && chunkProgress.current.total > 0
|
|
27
|
+
? chunkProgress.current.loaded / chunkProgress.current.total
|
|
28
|
+
: 0
|
|
29
|
+
)
|
|
23
30
|
|
|
24
31
|
const nodeProps = $derived({ indexPath, node })
|
|
25
32
|
const nodeState = $derived(api.getNodeState(nodeProps))
|
|
26
33
|
</script>
|
|
27
34
|
|
|
35
|
+
{#snippet progressIndicator()}
|
|
36
|
+
{#if loading}
|
|
37
|
+
<span
|
|
38
|
+
role="progressbar"
|
|
39
|
+
aria-label="Loading {Math.round(progress * 100)}%"
|
|
40
|
+
aria-valuenow={Math.round(progress * 100)}
|
|
41
|
+
aria-valuemin={0}
|
|
42
|
+
aria-valuemax={100}
|
|
43
|
+
class="border-gray-6 size-3 rounded-full border"
|
|
44
|
+
style:background="conic-gradient(var(--color-gray-6, #9c9ca4) {progress * 100}%, transparent {progress *
|
|
45
|
+
100}%)"
|
|
46
|
+
></span>
|
|
47
|
+
{/if}
|
|
48
|
+
{/snippet}
|
|
49
|
+
|
|
28
50
|
{#if nodeState.isBranch}
|
|
29
51
|
{@const { expanded } = nodeState}
|
|
30
52
|
{@const { children = [] } = node}
|
|
@@ -52,25 +74,28 @@
|
|
|
52
74
|
>
|
|
53
75
|
{name.current}
|
|
54
76
|
</span>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
<div class="flex items-center justify-end gap-1">
|
|
78
|
+
{@render progressIndicator()}
|
|
79
|
+
|
|
80
|
+
<button
|
|
81
|
+
class="text-gray-6"
|
|
82
|
+
onclick={(event) => {
|
|
83
|
+
event.stopPropagation()
|
|
84
|
+
|
|
85
|
+
if (node.entity.has(traits.Invisible)) {
|
|
86
|
+
node.entity.remove(traits.Invisible)
|
|
87
|
+
} else {
|
|
88
|
+
node.entity.add(traits.Invisible)
|
|
89
|
+
}
|
|
90
|
+
}}
|
|
91
|
+
>
|
|
92
|
+
{#if invisible.current}
|
|
93
|
+
<EyeOff size={14} />
|
|
94
|
+
{:else}
|
|
95
|
+
<Eye size={14} />
|
|
96
|
+
{/if}
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
74
99
|
</div>
|
|
75
100
|
<div {...api.getBranchContentProps(nodeProps)}>
|
|
76
101
|
<div {...api.getBranchIndentGuideProps(nodeProps)}></div>
|
|
@@ -113,23 +138,27 @@
|
|
|
113
138
|
{node.entity.get(traits.Name)}
|
|
114
139
|
</span>
|
|
115
140
|
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
node.entity.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
141
|
+
<div class="flex items-center gap-1">
|
|
142
|
+
{@render progressIndicator()}
|
|
143
|
+
|
|
144
|
+
<button
|
|
145
|
+
class="text-gray-6"
|
|
146
|
+
onclick={(event) => {
|
|
147
|
+
event.stopPropagation()
|
|
148
|
+
if (node.entity.has(traits.Invisible)) {
|
|
149
|
+
node.entity.remove(traits.Invisible)
|
|
150
|
+
} else {
|
|
151
|
+
node.entity.add(traits.Invisible)
|
|
152
|
+
}
|
|
153
|
+
}}
|
|
154
|
+
>
|
|
155
|
+
{#if invisible.current}
|
|
156
|
+
<EyeOff size={14} />
|
|
157
|
+
{:else}
|
|
158
|
+
<Eye size={14} />
|
|
159
|
+
{/if}
|
|
160
|
+
</button>
|
|
161
|
+
</div>
|
|
133
162
|
</div>
|
|
134
163
|
{/if}
|
|
135
164
|
|
package/dist/draw.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ 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 Metadata } from './metadata';
|
|
5
6
|
export type Transform = TransformWithUUID | TransformProto;
|
|
6
7
|
type Options = {
|
|
7
8
|
removable?: boolean;
|
|
@@ -9,5 +10,10 @@ type Options = {
|
|
|
9
10
|
export declare const drawTransform: (world: World, { referenceFrame, poseInObserverFrame, physicalObject, metadata }: Transform, api: Trait, options?: Options) => Entity;
|
|
10
11
|
export declare const drawDrawing: (world: World, drawing: Drawing, api: Trait, options?: Options) => Entity[];
|
|
11
12
|
export declare const updateTransform: (entity: Entity, { poseInObserverFrame, physicalObject, metadata }: Transform, options?: Options) => void;
|
|
13
|
+
export declare const updateMetadata: (entity: Entity, metadata: Metadata, { pointCloud }?: {
|
|
14
|
+
pointCloud?: boolean;
|
|
15
|
+
}) => void;
|
|
12
16
|
export declare const updateDrawing: (world: World, entities: Entity[], drawing: Drawing, api: Trait, options?: Options) => Entity[];
|
|
17
|
+
export declare const addColorTraits: (entity: Entity, colors: Uint8Array) => void;
|
|
18
|
+
export declare const setColorTraits: (entity: Entity, colors: Uint8Array) => void;
|
|
13
19
|
export {};
|