@viamrobotics/motion-tools 1.26.1 → 1.26.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/FrameConfigUpdater.svelte.js +42 -29
- 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 +31 -15
- package/dist/components/Entities/Entities.svelte +3 -8
- package/dist/components/Entities/Frame.svelte +25 -9
- package/dist/components/Entities/Frame.svelte.d.ts +0 -2
- package/dist/components/Entities/GLTF.svelte +5 -4
- package/dist/components/Entities/Line.svelte +5 -4
- package/dist/components/Entities/Mesh.svelte +12 -18
- package/dist/components/Entities/Points.svelte +5 -4
- 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/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 +21 -7
- 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 -45
- package/dist/ecs/traits.js +57 -28
- 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 +148 -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/useFrames.svelte.js +23 -11
- package/dist/hooks/useGeometries.svelte.js +4 -2
- package/dist/hooks/usePartConfig.svelte.js +38 -3
- package/dist/hooks/useWorldState.svelte.js +10 -2
- 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,4 +1,6 @@
|
|
|
1
1
|
import { hierarchy, traits } from './ecs';
|
|
2
|
+
import { createPose, matrixToPose, poseToMatrix } from './transform';
|
|
3
|
+
const tempPose = createPose();
|
|
2
4
|
export class FrameConfigUpdater {
|
|
3
5
|
updateFrame;
|
|
4
6
|
removeFrame;
|
|
@@ -10,19 +12,22 @@ export class FrameConfigUpdater {
|
|
|
10
12
|
const { x, y, z } = position;
|
|
11
13
|
if (x === undefined && y === undefined && z === undefined)
|
|
12
14
|
return;
|
|
13
|
-
const
|
|
15
|
+
const current = entity.get(traits.EditedMatrix);
|
|
16
|
+
if (!current)
|
|
17
|
+
return;
|
|
18
|
+
matrixToPose(current, tempPose);
|
|
14
19
|
if (x !== undefined)
|
|
15
|
-
|
|
20
|
+
tempPose.x = x;
|
|
16
21
|
if (y !== undefined)
|
|
17
|
-
|
|
22
|
+
tempPose.y = y;
|
|
18
23
|
if (z !== undefined)
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
tempPose.z = z;
|
|
25
|
+
poseToMatrix(tempPose, current);
|
|
26
|
+
entity.changed(traits.EditedMatrix);
|
|
21
27
|
const name = entity.get(traits.Name);
|
|
22
28
|
const parent = hierarchy.getParentName(entity) ?? 'world';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
this.updateFrame(name, parent, updatedPose);
|
|
29
|
+
if (name) {
|
|
30
|
+
this.updateFrame(name, parent, { ...tempPose });
|
|
26
31
|
}
|
|
27
32
|
};
|
|
28
33
|
updateLocalOrientation = (entity, orientation) => {
|
|
@@ -30,27 +35,32 @@ export class FrameConfigUpdater {
|
|
|
30
35
|
if (oX === undefined && oY === undefined && oZ === undefined && theta === undefined) {
|
|
31
36
|
return;
|
|
32
37
|
}
|
|
33
|
-
const
|
|
38
|
+
const current = entity.get(traits.EditedMatrix);
|
|
39
|
+
if (!current)
|
|
40
|
+
return;
|
|
41
|
+
matrixToPose(current, tempPose);
|
|
34
42
|
if (oX !== undefined)
|
|
35
|
-
|
|
43
|
+
tempPose.oX = oX;
|
|
36
44
|
if (oY !== undefined)
|
|
37
|
-
|
|
45
|
+
tempPose.oY = oY;
|
|
38
46
|
if (oZ !== undefined)
|
|
39
|
-
|
|
47
|
+
tempPose.oZ = oZ;
|
|
40
48
|
if (theta !== undefined)
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
tempPose.theta = theta;
|
|
50
|
+
poseToMatrix(tempPose, current);
|
|
51
|
+
entity.changed(traits.EditedMatrix);
|
|
43
52
|
const name = entity.get(traits.Name);
|
|
44
53
|
const parent = hierarchy.getParentName(entity) ?? 'world';
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.updateFrame(name, parent, updatedPose);
|
|
54
|
+
if (name) {
|
|
55
|
+
this.updateFrame(name, parent, { ...tempPose });
|
|
48
56
|
}
|
|
49
57
|
};
|
|
50
58
|
updateGeometry = (entity, geometry) => {
|
|
51
59
|
const name = entity.get(traits.Name);
|
|
52
60
|
const parent = hierarchy.getParentName(entity) ?? 'world';
|
|
53
|
-
const
|
|
61
|
+
const matrix = entity.get(traits.EditedMatrix);
|
|
62
|
+
if (matrix)
|
|
63
|
+
matrixToPose(matrix, tempPose);
|
|
54
64
|
if (geometry?.type === 'box') {
|
|
55
65
|
const { x, y, z } = geometry;
|
|
56
66
|
if (x === undefined && y === undefined && z === undefined)
|
|
@@ -64,8 +74,8 @@ export class FrameConfigUpdater {
|
|
|
64
74
|
change.z = z;
|
|
65
75
|
entity.set(traits.Box, change);
|
|
66
76
|
const box = entity.get(traits.Box);
|
|
67
|
-
if (name && box &&
|
|
68
|
-
this.updateFrame(name, parent,
|
|
77
|
+
if (name && box && matrix) {
|
|
78
|
+
this.updateFrame(name, parent, { ...tempPose }, { type: 'box', ...box });
|
|
69
79
|
}
|
|
70
80
|
}
|
|
71
81
|
else if (geometry?.type === 'sphere') {
|
|
@@ -74,8 +84,8 @@ export class FrameConfigUpdater {
|
|
|
74
84
|
return;
|
|
75
85
|
entity.set(traits.Sphere, { r });
|
|
76
86
|
const sphere = entity.get(traits.Sphere);
|
|
77
|
-
if (name && sphere &&
|
|
78
|
-
this.updateFrame(name, parent,
|
|
87
|
+
if (name && sphere && matrix) {
|
|
88
|
+
this.updateFrame(name, parent, { ...tempPose }, { type: 'sphere', ...sphere });
|
|
79
89
|
}
|
|
80
90
|
}
|
|
81
91
|
else if (geometry?.type === 'capsule') {
|
|
@@ -89,16 +99,17 @@ export class FrameConfigUpdater {
|
|
|
89
99
|
change.l = l;
|
|
90
100
|
entity.set(traits.Capsule, change);
|
|
91
101
|
const capsule = entity.get(traits.Capsule);
|
|
92
|
-
if (name && capsule &&
|
|
93
|
-
this.updateFrame(name, parent,
|
|
102
|
+
if (name && capsule && matrix) {
|
|
103
|
+
this.updateFrame(name, parent, { ...tempPose }, { type: 'capsule', ...capsule });
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
};
|
|
97
107
|
setFrameParent = (entity, parentName) => {
|
|
98
108
|
const name = entity.get(traits.Name);
|
|
99
|
-
const
|
|
100
|
-
if (name &&
|
|
101
|
-
|
|
109
|
+
const matrix = entity.get(traits.EditedMatrix);
|
|
110
|
+
if (name && matrix) {
|
|
111
|
+
matrixToPose(matrix, tempPose);
|
|
112
|
+
this.updateFrame(name, parentName, { ...tempPose });
|
|
102
113
|
}
|
|
103
114
|
};
|
|
104
115
|
deleteFrame = (entity) => {
|
|
@@ -110,9 +121,11 @@ export class FrameConfigUpdater {
|
|
|
110
121
|
setGeometryType = (entity, type) => {
|
|
111
122
|
const name = entity.get(traits.Name);
|
|
112
123
|
const parent = hierarchy.getParentName(entity) ?? 'world';
|
|
113
|
-
const
|
|
114
|
-
if (!name || !
|
|
124
|
+
const matrix = entity.get(traits.EditedMatrix);
|
|
125
|
+
if (!name || !matrix)
|
|
115
126
|
return;
|
|
127
|
+
matrixToPose(matrix, tempPose);
|
|
128
|
+
const pose = { ...tempPose };
|
|
116
129
|
if (type === 'none') {
|
|
117
130
|
this.updateFrame(name, parent, pose, { type: 'none' });
|
|
118
131
|
}
|
|
@@ -855,6 +855,25 @@ export declare class Get3DModelsResponse extends Message<Get3DModelsResponse> {
|
|
|
855
855
|
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Get3DModelsResponse;
|
|
856
856
|
static equals(a: Get3DModelsResponse | PlainMessage<Get3DModelsResponse> | undefined, b: Get3DModelsResponse | PlainMessage<Get3DModelsResponse> | undefined): boolean;
|
|
857
857
|
}
|
|
858
|
+
/**
|
|
859
|
+
* @generated from message viam.common.v1.GetWorldPoseResponse
|
|
860
|
+
*/
|
|
861
|
+
export declare class GetWorldPoseResponse extends Message<GetWorldPoseResponse> {
|
|
862
|
+
/**
|
|
863
|
+
* Pose of the component in the world reference frame
|
|
864
|
+
*
|
|
865
|
+
* @generated from field: viam.common.v1.Pose pose = 1;
|
|
866
|
+
*/
|
|
867
|
+
pose?: Pose;
|
|
868
|
+
constructor(data?: PartialMessage<GetWorldPoseResponse>);
|
|
869
|
+
static readonly runtime: typeof proto3;
|
|
870
|
+
static readonly typeName = "viam.common.v1.GetWorldPoseResponse";
|
|
871
|
+
static readonly fields: FieldList;
|
|
872
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetWorldPoseResponse;
|
|
873
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetWorldPoseResponse;
|
|
874
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetWorldPoseResponse;
|
|
875
|
+
static equals(a: GetWorldPoseResponse | PlainMessage<GetWorldPoseResponse> | undefined, b: GetWorldPoseResponse | PlainMessage<GetWorldPoseResponse> | undefined): boolean;
|
|
876
|
+
}
|
|
858
877
|
/**
|
|
859
878
|
* @generated from message viam.common.v1.GetReadingsRequest
|
|
860
879
|
*/
|
|
@@ -1260,6 +1260,38 @@ export class Get3DModelsResponse extends Message {
|
|
|
1260
1260
|
return proto3.util.equals(Get3DModelsResponse, a, b);
|
|
1261
1261
|
}
|
|
1262
1262
|
}
|
|
1263
|
+
/**
|
|
1264
|
+
* @generated from message viam.common.v1.GetWorldPoseResponse
|
|
1265
|
+
*/
|
|
1266
|
+
export class GetWorldPoseResponse extends Message {
|
|
1267
|
+
/**
|
|
1268
|
+
* Pose of the component in the world reference frame
|
|
1269
|
+
*
|
|
1270
|
+
* @generated from field: viam.common.v1.Pose pose = 1;
|
|
1271
|
+
*/
|
|
1272
|
+
pose;
|
|
1273
|
+
constructor(data) {
|
|
1274
|
+
super();
|
|
1275
|
+
proto3.util.initPartial(data, this);
|
|
1276
|
+
}
|
|
1277
|
+
static runtime = proto3;
|
|
1278
|
+
static typeName = "viam.common.v1.GetWorldPoseResponse";
|
|
1279
|
+
static fields = proto3.util.newFieldList(() => [
|
|
1280
|
+
{ no: 1, name: "pose", kind: "message", T: Pose },
|
|
1281
|
+
]);
|
|
1282
|
+
static fromBinary(bytes, options) {
|
|
1283
|
+
return new GetWorldPoseResponse().fromBinary(bytes, options);
|
|
1284
|
+
}
|
|
1285
|
+
static fromJson(jsonValue, options) {
|
|
1286
|
+
return new GetWorldPoseResponse().fromJson(jsonValue, options);
|
|
1287
|
+
}
|
|
1288
|
+
static fromJsonString(jsonString, options) {
|
|
1289
|
+
return new GetWorldPoseResponse().fromJsonString(jsonString, options);
|
|
1290
|
+
}
|
|
1291
|
+
static equals(a, b) {
|
|
1292
|
+
return proto3.util.equals(GetWorldPoseResponse, a, b);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1263
1295
|
/**
|
|
1264
1296
|
* @generated from message viam.common.v1.GetReadingsRequest
|
|
1265
1297
|
*/
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
import { T } from '@threlte/core'
|
|
5
5
|
import { Portal } from '@threlte/extras'
|
|
6
|
-
import { Color, Vector3 } from 'three'
|
|
6
|
+
import { Color, Quaternion, Vector3 } from 'three'
|
|
7
7
|
|
|
8
8
|
import { hierarchy, traits, useWorld } from '../ecs'
|
|
9
9
|
import { BatchedArrow } from '../three/BatchedArrow'
|
|
10
|
+
import { OrientationVector } from '../three/OrientationVector'
|
|
10
11
|
|
|
11
12
|
const arrowBatchMap = $state<Record<string, BatchedArrow>>({
|
|
12
13
|
world: new BatchedArrow(),
|
|
@@ -18,6 +19,22 @@
|
|
|
18
19
|
const direction = new Vector3()
|
|
19
20
|
const origin = new Vector3()
|
|
20
21
|
const color = new Color()
|
|
22
|
+
const tempQuat = new Quaternion()
|
|
23
|
+
const tempScale = new Vector3()
|
|
24
|
+
const tempOv = new OrientationVector()
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Decompose the matrix directly into the arrow's direction
|
|
28
|
+
* (OV components from the rotation) and origin (translation)
|
|
29
|
+
*/
|
|
30
|
+
const decompose = (entity: Entity): boolean => {
|
|
31
|
+
const matrix = entity.get(traits.Matrix)
|
|
32
|
+
if (!matrix) return false
|
|
33
|
+
matrix.decompose(origin, tempQuat, tempScale)
|
|
34
|
+
tempOv.setFromQuaternion(tempQuat)
|
|
35
|
+
direction.set(tempOv.x, tempOv.y, tempOv.z)
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
21
38
|
|
|
22
39
|
const onAdd = (entity: Entity) => {
|
|
23
40
|
const parent = hierarchy.getParentName(entity) ?? 'world'
|
|
@@ -25,32 +42,31 @@
|
|
|
25
42
|
arrowBatchMap[parent] ??= new BatchedArrow()
|
|
26
43
|
const batched = arrowBatchMap[parent]
|
|
27
44
|
|
|
28
|
-
const pose = entity.get(traits.Pose)
|
|
29
45
|
const colorRGB = entity.get(traits.Color)
|
|
30
46
|
|
|
47
|
+
if (!decompose(entity)) {
|
|
48
|
+
direction.set(0, 0, 0)
|
|
49
|
+
origin.set(0, 0, 0)
|
|
50
|
+
}
|
|
51
|
+
|
|
31
52
|
const instanceID = batched.addArrow(
|
|
32
|
-
direction
|
|
33
|
-
origin
|
|
53
|
+
direction,
|
|
54
|
+
origin,
|
|
34
55
|
colorRGB ? color.set(colorRGB.r, colorRGB.g, colorRGB.b) : color.set('yellow')
|
|
35
56
|
)
|
|
36
57
|
|
|
37
58
|
entity.add(traits.Instance({ instanceID, meshID: batched.mesh.id }))
|
|
38
59
|
}
|
|
39
60
|
|
|
40
|
-
const
|
|
61
|
+
const onMatrixChange = (entity: Entity) => {
|
|
41
62
|
if (!entity.has(traits.Arrow)) return
|
|
42
63
|
|
|
43
64
|
const parent = hierarchy.getParentName(entity) ?? 'world'
|
|
44
65
|
const batch = arrowBatchMap[parent]
|
|
45
66
|
const instanceID = entity.get(traits.Instance)?.instanceID
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
batch?.updateArrow(
|
|
50
|
-
instanceID,
|
|
51
|
-
direction.set(pose.oX, pose.oY, pose.oZ),
|
|
52
|
-
origin.set(pose.x, pose.y, pose.z).multiplyScalar(0.001)
|
|
53
|
-
)
|
|
67
|
+
|
|
68
|
+
if (instanceID && instanceID !== -1 && decompose(entity)) {
|
|
69
|
+
batch?.updateArrow(instanceID, direction, origin)
|
|
54
70
|
}
|
|
55
71
|
}
|
|
56
72
|
|
|
@@ -81,13 +97,13 @@
|
|
|
81
97
|
$effect(() => {
|
|
82
98
|
const unsubAdd = world.onAdd(traits.Arrow, onAdd)
|
|
83
99
|
const unsubRemove = world.onRemove(traits.Instance, onInstanceRemove)
|
|
84
|
-
const
|
|
100
|
+
const unsubMatrixChange = world.onChange(traits.Matrix, onMatrixChange)
|
|
85
101
|
const unsubColorChange = world.onChange(traits.Color, onColorChange)
|
|
86
102
|
|
|
87
103
|
return () => {
|
|
88
104
|
unsubAdd()
|
|
89
105
|
unsubRemove()
|
|
90
|
-
|
|
106
|
+
unsubMatrixChange()
|
|
91
107
|
unsubColorChange()
|
|
92
108
|
}
|
|
93
109
|
})
|
|
@@ -64,14 +64,9 @@
|
|
|
64
64
|
|
|
65
65
|
{#each machineFramesEntities.current as entity (entity)}
|
|
66
66
|
<Pose {entity}>
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
{entity}
|
|
71
|
-
>
|
|
72
|
-
<Label text={entity.get(traits.Name)} />
|
|
73
|
-
</Frame>
|
|
74
|
-
{/snippet}
|
|
67
|
+
<Frame {entity}>
|
|
68
|
+
<Label text={entity.get(traits.Name)} />
|
|
69
|
+
</Frame>
|
|
75
70
|
</Pose>
|
|
76
71
|
{/each}
|
|
77
72
|
|
|
@@ -10,7 +10,6 @@ Renders a Viam Frame object
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<script lang="ts">
|
|
13
|
-
import type { Pose } from '@viamrobotics/sdk'
|
|
14
13
|
import type { Entity } from 'koota'
|
|
15
14
|
import type { Snippet } from 'svelte'
|
|
16
15
|
|
|
@@ -22,18 +21,17 @@ Renders a Viam Frame object
|
|
|
22
21
|
import { colors, resourceColors } from '../../color'
|
|
23
22
|
import { traits, useParentName, useTrait } from '../../ecs'
|
|
24
23
|
import { useResourceByName } from '../../hooks/useResourceByName.svelte'
|
|
25
|
-
import {
|
|
24
|
+
import { composeLocalMatrix } from '../../transform'
|
|
26
25
|
|
|
27
26
|
import { useEntityEvents } from './hooks/useEntityEvents.svelte'
|
|
28
27
|
import Mesh from './Mesh.svelte'
|
|
29
28
|
|
|
30
29
|
interface Props {
|
|
31
30
|
entity: Entity
|
|
32
|
-
pose?: Pose
|
|
33
31
|
children?: Snippet<[{ ref: Object3D }]>
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
let { entity,
|
|
34
|
+
let { entity, children }: Props = $props()
|
|
37
35
|
|
|
38
36
|
const { invalidate } = useThrelte()
|
|
39
37
|
const resourceByName = useResourceByName()
|
|
@@ -42,7 +40,9 @@ Renders a Viam Frame object
|
|
|
42
40
|
const parent = useParentName(() => entity)
|
|
43
41
|
const entityColors = useTrait(() => entity, traits.Colors)
|
|
44
42
|
const entityColor = useTrait(() => entity, traits.Color)
|
|
45
|
-
const
|
|
43
|
+
const matrix = useTrait(() => entity, traits.Matrix)
|
|
44
|
+
const editedMatrix = useTrait(() => entity, traits.EditedMatrix)
|
|
45
|
+
const liveMatrix = useTrait(() => entity, traits.LiveMatrix)
|
|
46
46
|
const center = useTrait(() => entity, traits.Center)
|
|
47
47
|
const invisible = useTrait(() => entity, traits.Invisible)
|
|
48
48
|
|
|
@@ -68,13 +68,29 @@ Renders a Viam Frame object
|
|
|
68
68
|
})
|
|
69
69
|
|
|
70
70
|
const group = new Group()
|
|
71
|
+
group.matrixAutoUpdate = false
|
|
71
72
|
|
|
72
|
-
const resolvedPose = $derived(pose ?? entityPose.current)
|
|
73
73
|
$effect.pre(() => {
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
if (liveMatrix.current && matrix.current && editedMatrix.current) {
|
|
75
|
+
composeLocalMatrix(liveMatrix.current, matrix.current, editedMatrix.current, group.matrix)
|
|
76
|
+
} else if (editedMatrix.current) {
|
|
77
|
+
group.matrix.copy(editedMatrix.current)
|
|
78
|
+
} else if (matrix.current) {
|
|
79
|
+
group.matrix.copy(matrix.current)
|
|
80
|
+
} else {
|
|
81
|
+
return
|
|
77
82
|
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Keep position/quaternion/scale in sync with matrix so TransformControls
|
|
86
|
+
* (which reads/writes those fields) sees the entity's actual transform on
|
|
87
|
+
* drag start. Without this, the gizmo applies its drag delta against an
|
|
88
|
+
* identity baseline and the frame snaps to identity on first onChange.
|
|
89
|
+
*/
|
|
90
|
+
group.matrix.decompose(group.position, group.quaternion, group.scale)
|
|
91
|
+
|
|
92
|
+
group.updateMatrixWorld()
|
|
93
|
+
invalidate()
|
|
78
94
|
})
|
|
79
95
|
</script>
|
|
80
96
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import type { Pose } from '@viamrobotics/sdk';
|
|
2
1
|
import type { Entity } from 'koota';
|
|
3
2
|
import type { Snippet } from 'svelte';
|
|
4
3
|
import { type Object3D } from 'three';
|
|
5
4
|
interface Props {
|
|
6
5
|
entity: Entity;
|
|
7
|
-
pose?: Pose;
|
|
8
6
|
children?: Snippet<[{
|
|
9
7
|
ref: Object3D;
|
|
10
8
|
}]>;
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
import { Group, type Object3D } from 'three'
|
|
21
21
|
|
|
22
22
|
import { traits, useParentName, useTrait } from '../../ecs'
|
|
23
|
-
import { poseToObject3d } from '../../transform'
|
|
24
23
|
|
|
25
24
|
import AxesHelper from '../AxesHelper.svelte'
|
|
26
25
|
import { useEntityEvents } from './hooks/useEntityEvents.svelte'
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
|
|
37
36
|
const name = useTrait(() => entity, traits.Name)
|
|
38
37
|
const parent = useParentName(() => entity)
|
|
39
|
-
const
|
|
38
|
+
const matrix = useTrait(() => entity, traits.Matrix)
|
|
40
39
|
const gltfTrait = useTrait(() => entity, traits.GLTF)
|
|
41
40
|
const scale = useTrait(() => entity, traits.Scale)
|
|
42
41
|
const invisible = useTrait(() => entity, traits.Invisible)
|
|
@@ -46,10 +45,12 @@
|
|
|
46
45
|
const animationName = $derived(gltfTrait.current?.animationName)
|
|
47
46
|
|
|
48
47
|
const group = new Group()
|
|
48
|
+
group.matrixAutoUpdate = false
|
|
49
49
|
|
|
50
50
|
$effect.pre(() => {
|
|
51
|
-
if (
|
|
52
|
-
|
|
51
|
+
if (matrix.current) {
|
|
52
|
+
group.matrix.copy(matrix.current)
|
|
53
|
+
group.updateMatrixWorld()
|
|
53
54
|
}
|
|
54
55
|
})
|
|
55
56
|
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
import { isVertexColors, STRIDE } from '../../buffer'
|
|
10
10
|
import { traits, useParentName, useTrait } from '../../ecs'
|
|
11
|
-
import { poseToObject3d } from '../../transform'
|
|
12
11
|
|
|
13
12
|
import AxesHelper from '../AxesHelper.svelte'
|
|
14
13
|
import { useEntityEvents } from './hooks/useEntityEvents.svelte'
|
|
@@ -25,7 +24,7 @@
|
|
|
25
24
|
const { invalidate } = useThrelte()
|
|
26
25
|
const name = useTrait(() => entity, traits.Name)
|
|
27
26
|
const parent = useParentName(() => entity)
|
|
28
|
-
const
|
|
27
|
+
const matrix = useTrait(() => entity, traits.Matrix)
|
|
29
28
|
const color = useTrait(() => entity, traits.Color)
|
|
30
29
|
const colors = useTrait(() => entity, traits.Colors)
|
|
31
30
|
const dotColors = useTrait(() => entity, traits.DotColors)
|
|
@@ -63,10 +62,12 @@
|
|
|
63
62
|
const currentOpacity = $derived(opacity.current ?? 0.7)
|
|
64
63
|
|
|
65
64
|
const mesh = new Line2()
|
|
65
|
+
mesh.matrixAutoUpdate = false
|
|
66
66
|
|
|
67
67
|
$effect.pre(() => {
|
|
68
|
-
if (
|
|
69
|
-
|
|
68
|
+
if (matrix.current) {
|
|
69
|
+
mesh.matrix.copy(matrix.current)
|
|
70
|
+
mesh.updateMatrixWorld()
|
|
70
71
|
invalidate()
|
|
71
72
|
}
|
|
72
73
|
})
|
|
@@ -137,9 +137,18 @@
|
|
|
137
137
|
renderOrder={renderOrder.current}
|
|
138
138
|
{...rest}
|
|
139
139
|
>
|
|
140
|
-
{#if box.current}
|
|
140
|
+
{#if box.current || sphere.current}
|
|
141
|
+
{@const meshGeometry = box.current ? unitBox : unitSphere}
|
|
142
|
+
{@const edgesGeometry = box.current ? unitBoxEdges : unitSphereEdges}
|
|
143
|
+
<!--
|
|
144
|
+
Switch via a derived `is` on the same <T> so `useAttach`'s effect
|
|
145
|
+
cleanup runs before the new attach. Splitting these across two
|
|
146
|
+
branches of an {#if}/{:else if} races mount-new against unmount-old:
|
|
147
|
+
the new attach saves `mesh.geometry`, then the old cleanup restores
|
|
148
|
+
it to the pre-attach value (null), leaving the mesh geometryless.
|
|
149
|
+
-->
|
|
141
150
|
<T
|
|
142
|
-
is={
|
|
151
|
+
is={meshGeometry}
|
|
143
152
|
dispose={false}
|
|
144
153
|
/>
|
|
145
154
|
<T.LineSegments
|
|
@@ -147,22 +156,7 @@
|
|
|
147
156
|
bvh={{ enabled: false }}
|
|
148
157
|
>
|
|
149
158
|
<T
|
|
150
|
-
is={
|
|
151
|
-
dispose={false}
|
|
152
|
-
/>
|
|
153
|
-
<T.LineBasicMaterial color={darkenColor(color, 10)} />
|
|
154
|
-
</T.LineSegments>
|
|
155
|
-
{:else if sphere.current}
|
|
156
|
-
<T
|
|
157
|
-
is={unitSphere}
|
|
158
|
-
dispose={false}
|
|
159
|
-
/>
|
|
160
|
-
<T.LineSegments
|
|
161
|
-
raycast={() => null}
|
|
162
|
-
bvh={{ enabled: false }}
|
|
163
|
-
>
|
|
164
|
-
<T
|
|
165
|
-
is={unitSphereEdges}
|
|
159
|
+
is={edgesGeometry}
|
|
166
160
|
dispose={false}
|
|
167
161
|
/>
|
|
168
162
|
<T.LineBasicMaterial color={darkenColor(color, 10)} />
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
import { asColor, isSingleColor } from '../../buffer'
|
|
10
10
|
import { traits, useParentName, useTrait } from '../../ecs'
|
|
11
11
|
import { useSettings } from '../../hooks/useSettings.svelte'
|
|
12
|
-
import { poseToObject3d } from '../../transform'
|
|
13
12
|
|
|
14
13
|
import AxesHelper from '../AxesHelper.svelte'
|
|
15
14
|
import { useEntityEvents } from './hooks/useEntityEvents.svelte'
|
|
@@ -25,7 +24,7 @@
|
|
|
25
24
|
const settings = useSettings()
|
|
26
25
|
|
|
27
26
|
const parent = useParentName(() => entity)
|
|
28
|
-
const
|
|
27
|
+
const matrix = useTrait(() => entity, traits.Matrix)
|
|
29
28
|
const geometry = useTrait(() => entity, traits.BufferGeometry)
|
|
30
29
|
const entityColor = useTrait(() => entity, traits.Color)
|
|
31
30
|
const colors = useTrait(() => entity, traits.Colors)
|
|
@@ -42,6 +41,7 @@
|
|
|
42
41
|
const orthographic = $derived(settings.current.cameraMode === 'orthographic')
|
|
43
42
|
|
|
44
43
|
const points = new Points()
|
|
44
|
+
points.matrixAutoUpdate = false
|
|
45
45
|
const material = points.material as PointsMaterial
|
|
46
46
|
material.toneMapped = false
|
|
47
47
|
|
|
@@ -98,8 +98,9 @@
|
|
|
98
98
|
})
|
|
99
99
|
|
|
100
100
|
$effect.pre(() => {
|
|
101
|
-
if (
|
|
102
|
-
|
|
101
|
+
if (matrix.current) {
|
|
102
|
+
points.matrix.copy(matrix.current)
|
|
103
|
+
points.updateMatrixWorld()
|
|
103
104
|
}
|
|
104
105
|
})
|
|
105
106
|
|
|
@@ -1,52 +1,45 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Pose } from '@viamrobotics/sdk'
|
|
3
2
|
import type { Entity } from 'koota'
|
|
4
3
|
import type { Snippet } from 'svelte'
|
|
5
4
|
|
|
5
|
+
import { Matrix4 } from 'three'
|
|
6
|
+
|
|
6
7
|
import { traits, useParentName, useTrait } from '../../ecs'
|
|
7
|
-
import { usePartConfig } from '../../hooks/usePartConfig.svelte'
|
|
8
8
|
import { usePose } from '../../hooks/usePose.svelte'
|
|
9
|
-
import {
|
|
9
|
+
import { poseToMatrix } from '../../transform'
|
|
10
10
|
|
|
11
11
|
interface Props {
|
|
12
12
|
entity: Entity
|
|
13
|
-
children: Snippet
|
|
13
|
+
children: Snippet
|
|
14
14
|
}
|
|
15
15
|
let { entity, children }: Props = $props()
|
|
16
16
|
|
|
17
|
-
const partConfig = usePartConfig()
|
|
18
17
|
const name = useTrait(() => entity, traits.Name)
|
|
19
18
|
const parent = useParentName(() => entity)
|
|
20
|
-
const editedPose = useTrait(() => entity, traits.EditedPose)
|
|
21
|
-
const entityPose = useTrait(() => entity, traits.Pose)
|
|
22
19
|
|
|
23
20
|
const pose = usePose(
|
|
24
21
|
() => name.current,
|
|
25
22
|
() => parent.current
|
|
26
23
|
)
|
|
27
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Mirror the robot's live kinematics-resolved pose into LiveMatrix so
|
|
27
|
+
* Frame.svelte can compose the rendered transform via
|
|
28
|
+
* `composeLocalMatrix(live, baseline, edited)`. Mutate the stored
|
|
29
|
+
* `Matrix4` in place when present and notify via `entity.changed` —
|
|
30
|
+
* allocate only on first add.
|
|
31
|
+
*/
|
|
28
32
|
$effect.pre(() => {
|
|
29
33
|
if (pose.current === undefined) return
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
const live = entity.get(traits.LiveMatrix)
|
|
36
|
+
if (live) {
|
|
37
|
+
poseToMatrix(pose.current, live)
|
|
38
|
+
entity.changed(traits.LiveMatrix)
|
|
33
39
|
} else {
|
|
34
|
-
entity.add(traits.
|
|
40
|
+
entity.add(traits.LiveMatrix(poseToMatrix(pose.current, new Matrix4())))
|
|
35
41
|
}
|
|
36
42
|
})
|
|
37
|
-
|
|
38
|
-
// Always render through the live blend: live × network⁻¹ × edited. With
|
|
39
|
-
// `edited === network` (no edits) this collapses to `live`, so the rendered
|
|
40
|
-
// pose tracks the robot's kinematics-resolved position. With edits, the
|
|
41
|
-
// formula composes the staged delta on top of live. Input handlers that
|
|
42
|
-
// drive edits (gizmo onChange, Details panel) compute `edited` such that
|
|
43
|
-
// the blend renders to the user's intent.
|
|
44
|
-
const resolvedPose = $derived.by(() => {
|
|
45
|
-
if (pose.current === undefined || partConfig.hasPendingSave) return editedPose.current
|
|
46
|
-
if (!entityPose.current || !editedPose.current) return undefined
|
|
47
|
-
|
|
48
|
-
return composeRenderedPose(pose.current, entityPose.current, editedPose.current)
|
|
49
|
-
})
|
|
50
43
|
</script>
|
|
51
44
|
|
|
52
|
-
{@render children(
|
|
45
|
+
{@render children()}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import type { Pose } from '@viamrobotics/sdk';
|
|
2
1
|
import type { Entity } from 'koota';
|
|
3
2
|
import type { Snippet } from 'svelte';
|
|
4
3
|
interface Props {
|
|
5
4
|
entity: Entity;
|
|
6
|
-
children: Snippet
|
|
7
|
-
pose: Pose | undefined;
|
|
8
|
-
}]>;
|
|
5
|
+
children: Snippet;
|
|
9
6
|
}
|
|
10
7
|
declare const Pose: import("svelte").Component<Props, {}, "">;
|
|
11
8
|
type Pose = ReturnType<typeof Pose>;
|