@viamrobotics/motion-tools 1.26.1 → 1.27.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.js +42 -29
- package/dist/assert.d.ts +13 -0
- package/dist/assert.js +20 -0
- package/dist/buf/common/v1/common_pb.d.ts +19 -0
- package/dist/buf/common/v1/common_pb.js +32 -0
- package/dist/components/BatchedArrows.svelte +43 -45
- package/dist/components/Entities/Arrows/Arrows.svelte +35 -29
- package/dist/components/Entities/Entities.svelte +3 -8
- package/dist/components/Entities/Frame.svelte +31 -32
- package/dist/components/Entities/Frame.svelte.d.ts +0 -2
- package/dist/components/Entities/GLTF.svelte +27 -36
- package/dist/components/Entities/Geometry.svelte +35 -24
- package/dist/components/Entities/Line.svelte +37 -43
- package/dist/components/Entities/Mesh.svelte +12 -18
- package/dist/components/Entities/Points.svelte +25 -28
- package/dist/components/Entities/Pose.svelte +17 -24
- package/dist/components/Entities/Pose.svelte.d.ts +1 -4
- package/dist/components/Entities/hooks/useEntityEvents.svelte.js +40 -41
- package/dist/components/Scene.svelte +7 -1
- package/dist/components/SceneProviders.svelte +2 -1
- package/dist/components/SelectedTransformControls.svelte +57 -34
- package/dist/components/StaticGeometries.svelte +1 -1
- package/dist/components/hover/HoveredEntity.svelte +33 -3
- package/dist/components/hover/LinkedHoveredEntity.svelte +2 -3
- package/dist/components/overlay/Details.svelte +72 -94
- package/dist/components/overlay/__tests__/__fixtures__/entity.js +14 -17
- package/dist/components/overlay/left-pane/Tree.svelte +9 -9
- package/dist/components/overlay/left-pane/Tree.svelte.d.ts +1 -2
- package/dist/components/overlay/left-pane/TreeContainer.svelte +4 -15
- package/dist/components/overlay/left-pane/TreeNode.svelte +1 -1
- package/dist/components/overlay/left-pane/TreeNode.svelte.d.ts +1 -1
- package/dist/components/overlay/left-pane/useTree.svelte.d.ts +14 -0
- package/dist/components/overlay/left-pane/useTree.svelte.js +63 -0
- package/dist/draw.js +24 -9
- package/dist/ecs/index.d.ts +1 -0
- package/dist/ecs/index.js +1 -0
- package/dist/ecs/provideWorldMatrix.svelte.d.ts +8 -0
- package/dist/ecs/provideWorldMatrix.svelte.js +13 -0
- package/dist/ecs/traits.d.ts +41 -50
- package/dist/ecs/traits.js +57 -29
- package/dist/ecs/useTrait.svelte.d.ts +1 -6
- package/dist/ecs/useTrait.svelte.js +21 -13
- package/dist/ecs/worldMatrix.d.ts +10 -0
- package/dist/ecs/worldMatrix.js +138 -0
- package/dist/editing/FrameEditSession.js +31 -18
- package/dist/hooks/use3DModels.svelte.js +1 -1
- package/dist/hooks/useConfigFrames.svelte.js +12 -0
- package/dist/hooks/useDrawAPI.svelte.js +14 -6
- package/dist/hooks/useDrawService.svelte.js +4 -7
- package/dist/hooks/useFrames.svelte.js +23 -11
- package/dist/hooks/useGeometries.svelte.js +11 -3
- package/dist/hooks/usePartConfig.svelte.js +43 -6
- package/dist/hooks/useWorldState.svelte.js +10 -2
- package/dist/plugins/bvh.svelte.js +37 -26
- package/dist/transform.js +55 -21
- package/package.json +3 -3
- package/dist/components/overlay/left-pane/buildTree.d.ts +0 -13
- package/dist/components/overlay/left-pane/buildTree.js +0 -48
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Entity } from 'koota'
|
|
3
|
+
|
|
2
4
|
import { normalizeProps, useMachine } from '@zag-js/svelte'
|
|
3
5
|
import * as tree from '@zag-js/tree-view'
|
|
4
6
|
import { VirtualList } from 'svelte-virtuallists'
|
|
5
7
|
import { SvelteSet } from 'svelte/reactivity'
|
|
6
8
|
|
|
7
|
-
import { traits } from '../../../ecs'
|
|
9
|
+
import { relations, traits } from '../../../ecs'
|
|
8
10
|
import { useSelectedEntity } from '../../../hooks/useSelection.svelte'
|
|
9
11
|
|
|
10
|
-
import type { TreeNode as TreeNodeType } from './
|
|
12
|
+
import type { TreeNode as TreeNodeType } from './useTree.svelte'
|
|
11
13
|
|
|
12
14
|
import TreeNode from './TreeNode.svelte'
|
|
13
15
|
|
|
@@ -15,12 +17,11 @@
|
|
|
15
17
|
|
|
16
18
|
interface Props {
|
|
17
19
|
rootNode: TreeNodeType
|
|
18
|
-
nodeMap: Record<string, TreeNodeType | undefined>
|
|
19
20
|
dragElement?: HTMLElement
|
|
20
21
|
onSelectionChange?: (event: tree.SelectionChangeDetails) => void
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
let { rootNode,
|
|
24
|
+
let { rootNode, onSelectionChange, dragElement = $bindable() }: Props = $props()
|
|
24
25
|
|
|
25
26
|
const collection = $derived(
|
|
26
27
|
tree.collection<TreeNodeType>({
|
|
@@ -34,11 +35,10 @@
|
|
|
34
35
|
const expandedValues = new SvelteSet<string>()
|
|
35
36
|
|
|
36
37
|
$effect(() => {
|
|
37
|
-
let
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
node = node.parent
|
|
38
|
+
let entity: Entity | undefined = selected.current
|
|
39
|
+
while (entity) {
|
|
40
|
+
expandedValues.add(`${entity}`)
|
|
41
|
+
entity = entity.targetFor(relations.ChildOf)
|
|
42
42
|
}
|
|
43
43
|
})
|
|
44
44
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as tree from '@zag-js/tree-view';
|
|
2
|
-
import type { TreeNode as TreeNodeType } from './
|
|
2
|
+
import type { TreeNode as TreeNodeType } from './useTree.svelte';
|
|
3
3
|
interface Props {
|
|
4
4
|
rootNode: TreeNodeType;
|
|
5
|
-
nodeMap: Record<string, TreeNodeType | undefined>;
|
|
6
5
|
dragElement?: HTMLElement;
|
|
7
6
|
onSelectionChange?: (event: tree.SelectionChangeDetails) => void;
|
|
8
7
|
}
|
|
@@ -1,36 +1,26 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { type Entity, IsExcluded } from 'koota'
|
|
3
3
|
|
|
4
|
-
import { traits,
|
|
5
|
-
import { useFrames } from '../../../hooks/useFrames.svelte'
|
|
4
|
+
import { traits, useWorld } from '../../../ecs'
|
|
6
5
|
import { useSelectedEntity } from '../../../hooks/useSelection.svelte'
|
|
7
6
|
|
|
8
7
|
import FloatingPanel from '../FloatingPanel.svelte'
|
|
9
|
-
import { buildTreeNodes, type TreeNode } from './buildTree'
|
|
10
8
|
import Tree from './Tree.svelte'
|
|
11
9
|
import { provideTreeExpandedContext } from './useExpanded.svelte'
|
|
10
|
+
import { type TreeNode, useTree } from './useTree.svelte'
|
|
12
11
|
|
|
13
12
|
provideTreeExpandedContext()
|
|
14
13
|
|
|
15
14
|
const selectedEntity = useSelectedEntity()
|
|
16
|
-
|
|
17
|
-
const frames = useFrames()
|
|
18
15
|
const world = useWorld()
|
|
19
16
|
|
|
20
17
|
const worldEntity = world.spawn(IsExcluded, traits.Name('World'))
|
|
21
18
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const { rootNodes, nodeMap } = $derived.by(() => {
|
|
25
|
-
// This ensures the tree rebuilds when frame parent relationships change
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
27
|
-
frames.current
|
|
28
|
-
return buildTreeNodes(allEntities.current)
|
|
29
|
-
})
|
|
19
|
+
const tree = useTree()
|
|
30
20
|
|
|
31
21
|
const rootNode = $derived<TreeNode>({
|
|
32
22
|
entity: worldEntity,
|
|
33
|
-
children:
|
|
23
|
+
children: tree.current,
|
|
34
24
|
})
|
|
35
25
|
</script>
|
|
36
26
|
|
|
@@ -44,7 +34,6 @@
|
|
|
44
34
|
>
|
|
45
35
|
<Tree
|
|
46
36
|
{rootNode}
|
|
47
|
-
{nodeMap}
|
|
48
37
|
onSelectionChange={(event) => {
|
|
49
38
|
const value = event.selectedValue[0]
|
|
50
39
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Entity } from 'koota';
|
|
2
|
+
export interface TreeNode {
|
|
3
|
+
entity: Entity;
|
|
4
|
+
children?: TreeNode[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Reactive top-down tree built from `ChildOf` relations. Rebuilds when any
|
|
8
|
+
* named entity is added, removed, renamed, or gains/loses a `ChildOf` or
|
|
9
|
+
* `Orphan` edge. Orphans are hidden from the tree — they reappear once
|
|
10
|
+
* `provideHierarchy` resolves them to a real `ChildOf` parent.
|
|
11
|
+
*/
|
|
12
|
+
export declare const useTree: () => {
|
|
13
|
+
readonly current: TreeNode[];
|
|
14
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Not } from 'koota';
|
|
2
|
+
import { createSubscriber } from 'svelte/reactivity';
|
|
3
|
+
import { relations, traits, useWorld } from '../../../ecs';
|
|
4
|
+
const compareByName = (a, b) => (a.get(traits.Name) ?? '').localeCompare(b.get(traits.Name) ?? '');
|
|
5
|
+
const buildTree = (world) => {
|
|
6
|
+
const walk = (entity) => {
|
|
7
|
+
const node = { entity };
|
|
8
|
+
const children = world.query(relations.ChildOf(entity)).toSorted(compareByName);
|
|
9
|
+
if (children.length > 0) {
|
|
10
|
+
node.children = children.map((child) => walk(child));
|
|
11
|
+
}
|
|
12
|
+
return node;
|
|
13
|
+
};
|
|
14
|
+
const rootEntities = [];
|
|
15
|
+
for (const entity of world.query(traits.Name, Not(traits.Orphan))) {
|
|
16
|
+
if (entity.targetFor(relations.ChildOf))
|
|
17
|
+
continue;
|
|
18
|
+
rootEntities.push(entity);
|
|
19
|
+
}
|
|
20
|
+
rootEntities.sort(compareByName);
|
|
21
|
+
return rootEntities.map((entity) => walk(entity));
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Reactive top-down tree built from `ChildOf` relations. Rebuilds when any
|
|
25
|
+
* named entity is added, removed, renamed, or gains/loses a `ChildOf` or
|
|
26
|
+
* `Orphan` edge. Orphans are hidden from the tree — they reappear once
|
|
27
|
+
* `provideHierarchy` resolves them to a real `ChildOf` parent.
|
|
28
|
+
*/
|
|
29
|
+
export const useTree = () => {
|
|
30
|
+
const world = useWorld();
|
|
31
|
+
let cached;
|
|
32
|
+
let dirty = true;
|
|
33
|
+
const subscribe = createSubscriber((update) => {
|
|
34
|
+
const invalidate = () => {
|
|
35
|
+
dirty = true;
|
|
36
|
+
update();
|
|
37
|
+
};
|
|
38
|
+
const unsubs = [
|
|
39
|
+
world.onAdd(traits.Name, invalidate),
|
|
40
|
+
world.onRemove(traits.Name, invalidate),
|
|
41
|
+
world.onChange(traits.Name, invalidate),
|
|
42
|
+
world.onAdd(relations.ChildOf, invalidate),
|
|
43
|
+
world.onChange(relations.ChildOf, invalidate),
|
|
44
|
+
world.onRemove(relations.ChildOf, invalidate),
|
|
45
|
+
world.onAdd(traits.Orphan, invalidate),
|
|
46
|
+
world.onRemove(traits.Orphan, invalidate),
|
|
47
|
+
];
|
|
48
|
+
return () => {
|
|
49
|
+
for (const unsub of unsubs)
|
|
50
|
+
unsub();
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
get current() {
|
|
55
|
+
subscribe();
|
|
56
|
+
if (dirty || !cached) {
|
|
57
|
+
cached = buildTree(world);
|
|
58
|
+
dirty = false;
|
|
59
|
+
}
|
|
60
|
+
return cached;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
};
|
package/dist/draw.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Vector3, Vector4 } from 'three';
|
|
1
|
+
import { Matrix4, Vector3, Vector4 } from 'three';
|
|
2
2
|
import { NURBSCurve } from 'three/addons/curves/NURBSCurve.js';
|
|
3
3
|
import { UuidTool } from 'uuid-tool';
|
|
4
4
|
import { createBufferGeometry, preAllocateBufferGeometry, updateBufferGeometry, writeBufferGeometryRange, } from './attribute';
|
|
@@ -6,7 +6,7 @@ import { asFloat32Array, asOpacity, asRGB, inMeters, isSingleColor, isVertexColo
|
|
|
6
6
|
import { hierarchy, relations, traits } from './ecs';
|
|
7
7
|
import { parsePcdInWorker } from './loaders/pcd';
|
|
8
8
|
import { metadataFromStruct } from './metadata';
|
|
9
|
-
import { createPose } from './transform';
|
|
9
|
+
import { createPose, poseToMatrix } from './transform';
|
|
10
10
|
import { ColorFormat } from './buf/draw/v1/metadata_pb';
|
|
11
11
|
import { isPointCloud } from './geometry';
|
|
12
12
|
const vec3 = new Vector3();
|
|
@@ -38,7 +38,7 @@ const isModel = (drawing) => {
|
|
|
38
38
|
export const drawTransform = (world, { referenceFrame, poseInObserverFrame, physicalObject, metadata, uuid }, api, { removable = true } = {}) => {
|
|
39
39
|
const entityTraits = [
|
|
40
40
|
traits.Name(referenceFrame),
|
|
41
|
-
traits.
|
|
41
|
+
traits.Matrix(poseToMatrix(createPose(poseInObserverFrame?.pose), new Matrix4())),
|
|
42
42
|
api,
|
|
43
43
|
];
|
|
44
44
|
const uuidStr = uuidBytesToString(uuid);
|
|
@@ -88,7 +88,7 @@ export const drawDrawing = (world, drawing, api, { removable = true } = {}) => {
|
|
|
88
88
|
const uuidStr = uuidBytesToString(uuid);
|
|
89
89
|
if (uuidStr)
|
|
90
90
|
uuidTraits.push(traits.UUID(uuidStr));
|
|
91
|
-
const entity = world.spawn(traits.Name(referenceFrame), traits.
|
|
91
|
+
const entity = world.spawn(traits.Name(referenceFrame), traits.Matrix(poseToMatrix(createPose(poseInObserverFrame?.pose), new Matrix4())), api, ...hierarchy.parentTraits(poseInObserverFrame?.referenceFrame), ...uuidTraits);
|
|
92
92
|
if (removable)
|
|
93
93
|
entity.add(traits.Removable);
|
|
94
94
|
if (metadata?.showAxesHelper)
|
|
@@ -99,7 +99,14 @@ export const drawDrawing = (world, drawing, api, { removable = true } = {}) => {
|
|
|
99
99
|
return { entity, relationships: metadata?.relationships };
|
|
100
100
|
};
|
|
101
101
|
export const updateTransform = (entity, { poseInObserverFrame, physicalObject, metadata }, { removable = true } = {}) => {
|
|
102
|
-
entity.
|
|
102
|
+
const matrix = entity.get(traits.Matrix);
|
|
103
|
+
if (matrix) {
|
|
104
|
+
poseToMatrix(createPose(poseInObserverFrame?.pose), matrix);
|
|
105
|
+
entity.changed(traits.Matrix);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
entity.add(traits.Matrix(poseToMatrix(createPose(poseInObserverFrame?.pose), new Matrix4())));
|
|
109
|
+
}
|
|
103
110
|
hierarchy.setParent(entity, poseInObserverFrame?.referenceFrame);
|
|
104
111
|
if (physicalObject) {
|
|
105
112
|
traits.updateGeometryTrait(entity, physicalObject);
|
|
@@ -144,7 +151,14 @@ export const updateDrawing = (world, entity, drawing, { removable = true } = {})
|
|
|
144
151
|
const { poseInObserverFrame, metadata } = drawing;
|
|
145
152
|
if (!world.has(entity))
|
|
146
153
|
return { entity, relationships: metadata?.relationships };
|
|
147
|
-
entity.
|
|
154
|
+
const matrix = entity.get(traits.Matrix);
|
|
155
|
+
if (matrix) {
|
|
156
|
+
poseToMatrix(createPose(poseInObserverFrame?.pose), matrix);
|
|
157
|
+
entity.changed(traits.Matrix);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
entity.add(traits.Matrix(poseToMatrix(createPose(poseInObserverFrame?.pose), new Matrix4())));
|
|
161
|
+
}
|
|
148
162
|
hierarchy.setParent(entity, poseInObserverFrame?.referenceFrame);
|
|
149
163
|
if (metadata?.showAxesHelper)
|
|
150
164
|
entity.add(traits.ShowAxesHelper);
|
|
@@ -270,7 +284,7 @@ const drawModel = (world, model, api, { removable = true }) => {
|
|
|
270
284
|
const { animationName, assets, scale } = physicalObject.geometryType.value;
|
|
271
285
|
const baseTraits = [
|
|
272
286
|
traits.Name(referenceFrame),
|
|
273
|
-
traits.
|
|
287
|
+
traits.Matrix(poseToMatrix(createPose(poseInObserverFrame?.pose), new Matrix4())),
|
|
274
288
|
api,
|
|
275
289
|
...hierarchy.parentTraits(poseInObserverFrame?.referenceFrame),
|
|
276
290
|
];
|
|
@@ -291,8 +305,9 @@ const drawModel = (world, model, api, { removable = true }) => {
|
|
|
291
305
|
relations.ChildOf(root),
|
|
292
306
|
api,
|
|
293
307
|
];
|
|
294
|
-
if (scale)
|
|
295
|
-
subEntityTraits.push(traits.
|
|
308
|
+
if (scale) {
|
|
309
|
+
subEntityTraits.push(traits.Matrix(new Matrix4().makeScale(scale.x ?? 1, scale.y ?? 1, scale.z ?? 1)));
|
|
310
|
+
}
|
|
296
311
|
if (metadata?.invisible)
|
|
297
312
|
subEntityTraits.push(traits.Invisible);
|
|
298
313
|
if (metadata?.showAxesHelper)
|
package/dist/ecs/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { useTrait } from './useTrait.svelte';
|
|
|
4
4
|
export { useTarget } from './useTarget.svelte';
|
|
5
5
|
export { useParentName } from './useParentName.svelte';
|
|
6
6
|
export { provideHierarchy } from './provideHierarchy.svelte';
|
|
7
|
+
export { provideWorldMatrix } from './provideWorldMatrix.svelte';
|
|
7
8
|
export * as traits from './traits';
|
|
8
9
|
export * as relations from './relations';
|
|
9
10
|
export * as hierarchy from './hierarchy';
|
package/dist/ecs/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { useTrait } from './useTrait.svelte';
|
|
|
4
4
|
export { useTarget } from './useTarget.svelte';
|
|
5
5
|
export { useParentName } from './useParentName.svelte';
|
|
6
6
|
export { provideHierarchy } from './provideHierarchy.svelte';
|
|
7
|
+
export { provideWorldMatrix } from './provideWorldMatrix.svelte';
|
|
7
8
|
export * as traits from './traits';
|
|
8
9
|
export * as relations from './relations';
|
|
9
10
|
export * as hierarchy from './hierarchy';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount the world-matrix reactor: keeps `WorldMatrix` in sync with the
|
|
3
|
+
* cumulative `parent.WorldMatrix × local rendered` for every entity whose
|
|
4
|
+
* `Matrix` / `EditedMatrix` / `LiveMatrix` / `Scale` / `ChildOf` changes.
|
|
5
|
+
* Microtask-deferred so a burst of changes (e.g. one `useFrames` reconcile
|
|
6
|
+
* tick) coalesces into a single subtree walk.
|
|
7
|
+
*/
|
|
8
|
+
export declare const provideWorldMatrix: () => void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useWorld } from './useWorld';
|
|
2
|
+
import { installWorldMatrixListeners } from './worldMatrix';
|
|
3
|
+
/**
|
|
4
|
+
* Mount the world-matrix reactor: keeps `WorldMatrix` in sync with the
|
|
5
|
+
* cumulative `parent.WorldMatrix × local rendered` for every entity whose
|
|
6
|
+
* `Matrix` / `EditedMatrix` / `LiveMatrix` / `Scale` / `ChildOf` changes.
|
|
7
|
+
* Microtask-deferred so a burst of changes (e.g. one `useFrames` reconcile
|
|
8
|
+
* tick) coalesces into a single subtree walk.
|
|
9
|
+
*/
|
|
10
|
+
export const provideWorldMatrix = () => {
|
|
11
|
+
const world = useWorld();
|
|
12
|
+
$effect(() => installWorldMatrixListeners(world));
|
|
13
|
+
};
|
package/dist/ecs/traits.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { GLTF as ThreeGltf } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
2
2
|
import { Geometry as ViamGeometry } from '@viamrobotics/sdk';
|
|
3
3
|
import { type Entity } from 'koota';
|
|
4
|
-
import { BufferGeometry as ThreeBufferGeometry } from 'three';
|
|
4
|
+
import { Matrix4, BufferGeometry as ThreeBufferGeometry } from 'three';
|
|
5
5
|
export declare const Name: import("koota").Trait<() => string>;
|
|
6
6
|
export declare const UUID: import("koota").Trait<() => string>;
|
|
7
7
|
/**
|
|
@@ -12,33 +12,12 @@ export declare const UUID: import("koota").Trait<() => string>;
|
|
|
12
12
|
* adding this trait directly.
|
|
13
13
|
*/
|
|
14
14
|
export declare const Orphan: import("koota").Trait<() => string>;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
oZ: number;
|
|
22
|
-
theta: number;
|
|
23
|
-
}>;
|
|
24
|
-
export declare const EditedPose: import("koota").Trait<{
|
|
25
|
-
x: number;
|
|
26
|
-
y: number;
|
|
27
|
-
z: number;
|
|
28
|
-
oX: number;
|
|
29
|
-
oY: number;
|
|
30
|
-
oZ: number;
|
|
31
|
-
theta: number;
|
|
32
|
-
}>;
|
|
33
|
-
export declare const LivePose: import("koota").Trait<{
|
|
34
|
-
x: number;
|
|
35
|
-
y: number;
|
|
36
|
-
z: number;
|
|
37
|
-
oX: number;
|
|
38
|
-
oY: number;
|
|
39
|
-
oZ: number;
|
|
40
|
-
theta: number;
|
|
41
|
-
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Static positional offset (e.g. center of a geometry). Stored as a Pose
|
|
17
|
+
* for the rare cases that need OV+theta semantics (currently unused).
|
|
18
|
+
* Never composed through the parent chain — the `WorldMatrix` system
|
|
19
|
+
* doesn't read it.
|
|
20
|
+
*/
|
|
42
21
|
export declare const Center: import("koota").Trait<{
|
|
43
22
|
x: number;
|
|
44
23
|
y: number;
|
|
@@ -48,25 +27,42 @@ export declare const Center: import("koota").Trait<{
|
|
|
48
27
|
oZ: number;
|
|
49
28
|
theta: number;
|
|
50
29
|
}>;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Local-to-parent transform. Stored AoS — one `Matrix4` instance per entity —
|
|
32
|
+
* not as 16 SoA fields. Every consumer reads all 16 elements of one entity at
|
|
33
|
+
* a time (`Object3D.matrix.copy`, batched-mesh per-instance writes, the
|
|
34
|
+
* world-matrix walk). SoA would allocate a fresh 16-field object on every
|
|
35
|
+
* `entity.get(Matrix)`; AoS returns the `Matrix4` reference, zero allocation
|
|
36
|
+
* per read, and plugs straight into Three.js. The trade-off — losing
|
|
37
|
+
* column-iteration locality — is fine because no system iterates a single
|
|
38
|
+
* matrix element across entities.
|
|
39
|
+
*
|
|
40
|
+
* Update pattern: read the `Matrix4` and mutate in place, then call
|
|
41
|
+
* `entity.changed(Matrix)` so `onChange` listeners (the `WorldMatrix` system,
|
|
42
|
+
* etc.) fire. Allocate a fresh `Matrix4` only on add.
|
|
43
|
+
*/
|
|
44
|
+
export declare const Matrix: import("koota").Trait<() => Matrix4>;
|
|
45
|
+
/** User-staged local transform during a `FrameEditSession`. */
|
|
46
|
+
export declare const EditedMatrix: import("koota").Trait<() => Matrix4>;
|
|
47
|
+
/**
|
|
48
|
+
* Live local transform from the robot's kinematics. Composed with `Matrix`
|
|
49
|
+
* (network baseline) and `EditedMatrix` to produce the rendered transform.
|
|
50
|
+
*/
|
|
51
|
+
export declare const LiveMatrix: import("koota").Trait<() => Matrix4>;
|
|
52
|
+
/**
|
|
53
|
+
* Cumulative world-space transform — `parent.WorldMatrix × local rendered`.
|
|
54
|
+
* Maintained by `provideWorldMatrix`. Read by hover label placement,
|
|
55
|
+
* batched-mesh population, and any other consumer that needs world-space.
|
|
56
|
+
*/
|
|
57
|
+
export declare const WorldMatrix: import("koota").Trait<() => Matrix4>;
|
|
58
|
+
/**
|
|
59
|
+
* World-space transform of a hovered instance inside a points/arrows batch,
|
|
60
|
+
* paired with the instance index in the parent batched mesh.
|
|
61
|
+
*/
|
|
62
|
+
export declare const InstancedMatrix: import("koota").Trait<() => {
|
|
63
|
+
matrix: Matrix4;
|
|
59
64
|
index: number;
|
|
60
65
|
}>;
|
|
61
|
-
export declare const WorldPose: import("koota").Trait<{
|
|
62
|
-
x: number;
|
|
63
|
-
y: number;
|
|
64
|
-
z: number;
|
|
65
|
-
oX: number;
|
|
66
|
-
oY: number;
|
|
67
|
-
oZ: number;
|
|
68
|
-
theta: number;
|
|
69
|
-
}>;
|
|
70
66
|
export declare const Hovered: import("koota").Trait<() => boolean>;
|
|
71
67
|
export declare const Invisible: import("koota").Trait<() => boolean>;
|
|
72
68
|
/**
|
|
@@ -147,11 +143,6 @@ export declare const GLTF: import("koota").Trait<() => {
|
|
|
147
143
|
};
|
|
148
144
|
animationName: string;
|
|
149
145
|
}>;
|
|
150
|
-
export declare const Scale: import("koota").Trait<{
|
|
151
|
-
x: number;
|
|
152
|
-
y: number;
|
|
153
|
-
z: number;
|
|
154
|
-
}>;
|
|
155
146
|
export declare const FramesAPI: import("koota").Trait<() => boolean>;
|
|
156
147
|
export declare const GeometriesAPI: import("koota").Trait<() => boolean>;
|
|
157
148
|
export declare const DrawAPI: import("koota").Trait<() => boolean>;
|
package/dist/ecs/traits.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Geometry as ViamGeometry } from '@viamrobotics/sdk';
|
|
2
2
|
import { trait } from 'koota';
|
|
3
|
-
import { BufferGeometry as ThreeBufferGeometry } from 'three';
|
|
3
|
+
import { Matrix4, BufferGeometry as ThreeBufferGeometry } from 'three';
|
|
4
4
|
import { createBufferGeometry, updateBufferGeometry } from '../attribute';
|
|
5
5
|
import { ColorFormat } from '../buf/draw/v1/metadata_pb';
|
|
6
6
|
import { createBox, createCapsule, createSphere } from '../geometry';
|
|
@@ -16,29 +16,49 @@ export const UUID = trait(() => '');
|
|
|
16
16
|
* adding this trait directly.
|
|
17
17
|
*/
|
|
18
18
|
export const Orphan = trait(() => '');
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Static positional offset (e.g. center of a geometry). Stored as a Pose
|
|
21
|
+
* for the rare cases that need OV+theta semantics (currently unused).
|
|
22
|
+
* Never composed through the parent chain — the `WorldMatrix` system
|
|
23
|
+
* doesn't read it.
|
|
24
|
+
*/
|
|
22
25
|
export const Center = trait({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 });
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Local-to-parent transform. Stored AoS — one `Matrix4` instance per entity —
|
|
28
|
+
* not as 16 SoA fields. Every consumer reads all 16 elements of one entity at
|
|
29
|
+
* a time (`Object3D.matrix.copy`, batched-mesh per-instance writes, the
|
|
30
|
+
* world-matrix walk). SoA would allocate a fresh 16-field object on every
|
|
31
|
+
* `entity.get(Matrix)`; AoS returns the `Matrix4` reference, zero allocation
|
|
32
|
+
* per read, and plugs straight into Three.js. The trade-off — losing
|
|
33
|
+
* column-iteration locality — is fine because no system iterates a single
|
|
34
|
+
* matrix element across entities.
|
|
35
|
+
*
|
|
36
|
+
* Update pattern: read the `Matrix4` and mutate in place, then call
|
|
37
|
+
* `entity.changed(Matrix)` so `onChange` listeners (the `WorldMatrix` system,
|
|
38
|
+
* etc.) fire. Allocate a fresh `Matrix4` only on add.
|
|
39
|
+
*/
|
|
40
|
+
export const Matrix = trait(() => new Matrix4());
|
|
41
|
+
/** User-staged local transform during a `FrameEditSession`. */
|
|
42
|
+
export const EditedMatrix = trait(() => new Matrix4());
|
|
43
|
+
/**
|
|
44
|
+
* Live local transform from the robot's kinematics. Composed with `Matrix`
|
|
45
|
+
* (network baseline) and `EditedMatrix` to produce the rendered transform.
|
|
46
|
+
*/
|
|
47
|
+
export const LiveMatrix = trait(() => new Matrix4());
|
|
48
|
+
/**
|
|
49
|
+
* Cumulative world-space transform — `parent.WorldMatrix × local rendered`.
|
|
50
|
+
* Maintained by `provideWorldMatrix`. Read by hover label placement,
|
|
51
|
+
* batched-mesh population, and any other consumer that needs world-space.
|
|
52
|
+
*/
|
|
53
|
+
export const WorldMatrix = trait(() => new Matrix4());
|
|
54
|
+
/**
|
|
55
|
+
* World-space transform of a hovered instance inside a points/arrows batch,
|
|
56
|
+
* paired with the instance index in the parent batched mesh.
|
|
57
|
+
*/
|
|
58
|
+
export const InstancedMatrix = trait(() => ({
|
|
59
|
+
matrix: new Matrix4(),
|
|
31
60
|
index: -1,
|
|
32
|
-
});
|
|
33
|
-
export const WorldPose = trait({
|
|
34
|
-
x: 0,
|
|
35
|
-
y: 0,
|
|
36
|
-
z: 0,
|
|
37
|
-
oX: 0,
|
|
38
|
-
oY: 0,
|
|
39
|
-
oZ: 1,
|
|
40
|
-
theta: 0,
|
|
41
|
-
});
|
|
61
|
+
}));
|
|
42
62
|
export const Hovered = trait(() => true);
|
|
43
63
|
export const Invisible = trait(() => true);
|
|
44
64
|
/**
|
|
@@ -100,7 +120,6 @@ export const GLTF = trait(() => ({
|
|
|
100
120
|
source: { url: '' },
|
|
101
121
|
animationName: '',
|
|
102
122
|
}));
|
|
103
|
-
export const Scale = trait({ x: 1, y: 1, z: 1 });
|
|
104
123
|
export const FramesAPI = trait(() => true);
|
|
105
124
|
export const GeometriesAPI = trait(() => true);
|
|
106
125
|
export const DrawAPI = trait(() => true);
|
|
@@ -174,30 +193,39 @@ export const updateGeometryTrait = (entity, geometry) => {
|
|
|
174
193
|
return;
|
|
175
194
|
}
|
|
176
195
|
if (geometry.geometryType.case === 'box') {
|
|
196
|
+
const next = createBox(geometry.geometryType.value);
|
|
177
197
|
if (entity.has(Box)) {
|
|
178
|
-
entity.
|
|
198
|
+
const cur = entity.get(Box);
|
|
199
|
+
if (cur.x !== next.x || cur.y !== next.y || cur.z !== next.z)
|
|
200
|
+
entity.set(Box, next);
|
|
179
201
|
}
|
|
180
202
|
else {
|
|
181
203
|
entity.remove(Capsule, Sphere, BufferGeometry);
|
|
182
|
-
entity.add(Box(
|
|
204
|
+
entity.add(Box(next));
|
|
183
205
|
}
|
|
184
206
|
}
|
|
185
207
|
else if (geometry.geometryType.case === 'capsule') {
|
|
208
|
+
const next = createCapsule(geometry.geometryType.value);
|
|
186
209
|
if (entity.has(Capsule)) {
|
|
187
|
-
entity.
|
|
210
|
+
const cur = entity.get(Capsule);
|
|
211
|
+
if (cur.r !== next.r || cur.l !== next.l)
|
|
212
|
+
entity.set(Capsule, next);
|
|
188
213
|
}
|
|
189
214
|
else {
|
|
190
215
|
entity.remove(Box, Sphere, BufferGeometry);
|
|
191
|
-
entity.add(Capsule(
|
|
216
|
+
entity.add(Capsule(next));
|
|
192
217
|
}
|
|
193
218
|
}
|
|
194
219
|
else if (geometry.geometryType.case === 'sphere') {
|
|
220
|
+
const next = createSphere(geometry.geometryType.value);
|
|
195
221
|
if (entity.has(Sphere)) {
|
|
196
|
-
entity.
|
|
222
|
+
const cur = entity.get(Sphere);
|
|
223
|
+
if (cur.r !== next.r)
|
|
224
|
+
entity.set(Sphere, next);
|
|
197
225
|
}
|
|
198
226
|
else {
|
|
199
227
|
entity.remove(Box, Capsule, BufferGeometry);
|
|
200
|
-
entity.add(Sphere(
|
|
228
|
+
entity.add(Sphere(next));
|
|
201
229
|
}
|
|
202
230
|
}
|
|
203
231
|
else if (geometry.geometryType.case === 'mesh') {
|
|
@@ -6,14 +6,9 @@ type Schema = {
|
|
|
6
6
|
type TraitRecordFromSchema<T extends Schema> = T extends AoSFactory ? ReturnType<T> : {
|
|
7
7
|
[P in keyof T]: T[P] extends (...args: never[]) => unknown ? ReturnType<T[P]> : T[P];
|
|
8
8
|
};
|
|
9
|
-
/**
|
|
10
|
-
* The record of a trait.
|
|
11
|
-
* For SoA it is a snapshot of the state for a single entity.
|
|
12
|
-
* For AoS it is the state instance for a single entity.
|
|
13
|
-
*/
|
|
14
9
|
type TraitRecord<T extends Trait | Schema> = T extends Trait ? TraitRecordFromSchema<T['schema']> : TraitRecordFromSchema<T>;
|
|
15
10
|
export declare function isWorld(target: Entity | World | null | undefined): target is World;
|
|
16
11
|
export declare function useTrait<T extends Trait>(target: () => Entity | World | undefined | null, trait: T): {
|
|
17
|
-
current: TraitRecord<T> | undefined;
|
|
12
|
+
readonly current: TraitRecord<T> | undefined;
|
|
18
13
|
};
|
|
19
14
|
export {};
|
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
import { $internal as internal } from 'koota';
|
|
2
|
+
import { untrack } from 'svelte';
|
|
2
3
|
import { useWorld } from './useWorld';
|
|
3
4
|
export function isWorld(target) {
|
|
4
5
|
return typeof target?.spawn === 'function';
|
|
5
6
|
}
|
|
6
7
|
export function useTrait(target, trait) {
|
|
7
8
|
const contextWorld = useWorld();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
let value = $derived(entity?.get(trait));
|
|
9
|
+
let value = $state.raw();
|
|
10
|
+
// Version counter to force reactivity when the value reference is the same (AoS traits).
|
|
11
|
+
// Only read in the getter, never in the effect.
|
|
12
|
+
let version = $state(0);
|
|
13
13
|
$effect(() => {
|
|
14
|
-
const
|
|
14
|
+
const t = target();
|
|
15
|
+
if (!t) {
|
|
16
|
+
value = undefined;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const world = isWorld(t) ? t : contextWorld;
|
|
20
|
+
const entity = isWorld(t) ? t[internal].worldEntity : t;
|
|
21
|
+
value = entity.has(trait) ? entity.get(trait) : undefined;
|
|
22
|
+
const onChangeUnsub = world.onChange(trait, (e) => {
|
|
15
23
|
if (e === entity) {
|
|
16
24
|
value = e.get(trait);
|
|
25
|
+
untrack(() => version++);
|
|
17
26
|
}
|
|
18
27
|
});
|
|
28
|
+
const onAddUnsub = world.onAdd(trait, (e) => {
|
|
29
|
+
if (e === entity)
|
|
30
|
+
value = e.get(trait);
|
|
31
|
+
});
|
|
19
32
|
const onRemoveUnsub = world.onRemove(trait, (e) => {
|
|
20
|
-
if (e === entity)
|
|
33
|
+
if (e === entity)
|
|
21
34
|
value = undefined;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
const onChangeUnsub = world.onChange(trait, (e) => {
|
|
25
|
-
if (e === entity) {
|
|
26
|
-
value = e.get(trait);
|
|
27
|
-
}
|
|
28
35
|
});
|
|
29
36
|
return () => {
|
|
30
37
|
onChangeUnsub();
|
|
@@ -34,6 +41,7 @@ export function useTrait(target, trait) {
|
|
|
34
41
|
});
|
|
35
42
|
return {
|
|
36
43
|
get current() {
|
|
44
|
+
void version;
|
|
37
45
|
return value;
|
|
38
46
|
},
|
|
39
47
|
};
|