@viamrobotics/motion-tools 0.14.11 → 0.14.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Details.svelte +5 -5
- package/dist/components/Tree/TreeContainer.svelte +7 -3
- package/dist/components/WorldObjects.svelte +2 -1
- package/dist/hooks/useSettings.svelte.js +5 -1
- package/dist/hooks/useWeblabs.svelte.d.ts +3 -0
- package/dist/hooks/useWeblabs.svelte.js +3 -0
- package/dist/three/BatchedArrow.d.ts +11 -27
- package/dist/three/BatchedArrow.js +48 -112
- package/dist/three/arrow.d.ts +7 -0
- package/dist/three/arrow.js +27 -0
- package/package.json +2 -2
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
import { usePartConfig } from '../hooks/usePartConfig.svelte'
|
|
28
28
|
import { FrameConfigUpdater } from '../FrameConfigUpdater.svelte'
|
|
29
29
|
import { useWeblabs } from '../hooks/useWeblabs.svelte'
|
|
30
|
-
|
|
30
|
+
import { WEBLABS_EXPERIMENTS } from '../hooks/useWeblabs.svelte'
|
|
31
31
|
const { ...rest } = $props()
|
|
32
32
|
|
|
33
33
|
const focused = useFocused()
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
})
|
|
105
105
|
|
|
106
106
|
const getCopyClipboardText = () => {
|
|
107
|
-
if (weblab.isActive(
|
|
107
|
+
if (weblab.isActive(WEBLABS_EXPERIMENTS.MOTION_TOOLS_EDIT_FRAME)) {
|
|
108
108
|
return JSON.stringify(
|
|
109
109
|
{
|
|
110
110
|
worldPosition: worldPosition,
|
|
@@ -303,7 +303,7 @@
|
|
|
303
303
|
</div>
|
|
304
304
|
{/if}
|
|
305
305
|
|
|
306
|
-
<WeblabActive experiment=
|
|
306
|
+
<WeblabActive experiment={WEBLABS_EXPERIMENTS.MOTION_TOOLS_EDIT_FRAME}>
|
|
307
307
|
{@const ParentFrame = showEditFrameOptions ? DropDownField : ImmutableField}
|
|
308
308
|
|
|
309
309
|
<div>
|
|
@@ -487,7 +487,7 @@
|
|
|
487
487
|
</WeblabActive>
|
|
488
488
|
|
|
489
489
|
<WeblabActive
|
|
490
|
-
experiment=
|
|
490
|
+
experiment={WEBLABS_EXPERIMENTS.MOTION_TOOLS_EDIT_FRAME}
|
|
491
491
|
renderIfActive={false}
|
|
492
492
|
>
|
|
493
493
|
{#if object.geometry}
|
|
@@ -563,7 +563,7 @@
|
|
|
563
563
|
</Button>
|
|
564
564
|
{/if}
|
|
565
565
|
|
|
566
|
-
<WeblabActive experiment=
|
|
566
|
+
<WeblabActive experiment={WEBLABS_EXPERIMENTS.MOTION_TOOLS_EDIT_FRAME}>
|
|
567
567
|
{#if showEditFrameOptions}
|
|
568
568
|
<Button
|
|
569
569
|
variant="danger"
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
import { useEnvironment } from '../../hooks/useEnvironment.svelte'
|
|
16
16
|
import { usePartID } from '../../hooks/usePartID.svelte'
|
|
17
17
|
import { usePartConfig } from '../../hooks/usePartConfig.svelte'
|
|
18
|
+
import WeblabActive from '../weblab/WeblabActive.svelte'
|
|
19
|
+
import { WEBLABS_EXPERIMENTS } from '../../hooks/useWeblabs.svelte'
|
|
18
20
|
const { ...rest } = $props()
|
|
19
21
|
|
|
20
22
|
provideTreeExpandedContext()
|
|
@@ -60,9 +62,11 @@
|
|
|
60
62
|
/>
|
|
61
63
|
{/key}
|
|
62
64
|
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
<WeblabActive experiment={WEBLABS_EXPERIMENTS.MOTION_TOOLS_EDIT_FRAME}>
|
|
66
|
+
{#if environment.current.isStandalone && partID.current && partConfig.hasEditPermissions}
|
|
67
|
+
<AddFrames />
|
|
68
|
+
{/if}
|
|
69
|
+
</WeblabActive>
|
|
66
70
|
|
|
67
71
|
<Logs />
|
|
68
72
|
<Settings />
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import { useWeblabs } from '../hooks/useWeblabs.svelte'
|
|
19
19
|
import type { WorldObject } from '../WorldObject.svelte'
|
|
20
20
|
import type { Pose as ViamPose } from '@viamrobotics/sdk'
|
|
21
|
+
import { WEBLABS_EXPERIMENTS } from '../hooks/useWeblabs.svelte'
|
|
21
22
|
const points = usePointClouds()
|
|
22
23
|
const drawAPI = useDrawAPI()
|
|
23
24
|
const frames = useFrames()
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
const weblabs = useWeblabs()
|
|
28
29
|
|
|
29
30
|
const weblabedDeterminePose = (object: WorldObject, pose: ViamPose | undefined) => {
|
|
30
|
-
if (weblabs.isActive(
|
|
31
|
+
if (weblabs.isActive(WEBLABS_EXPERIMENTS.MOTION_TOOLS_EDIT_FRAME)) {
|
|
31
32
|
return determinePose(object, pose)
|
|
32
33
|
}
|
|
33
34
|
return pose ?? object.pose
|
|
@@ -24,13 +24,17 @@ const defaults = () => ({
|
|
|
24
24
|
});
|
|
25
25
|
export const provideSettings = () => {
|
|
26
26
|
let settings = $state(defaults());
|
|
27
|
+
let settingsLoaded = $state(false);
|
|
27
28
|
get('motion-tools-settings').then((response) => {
|
|
28
29
|
if (response) {
|
|
29
30
|
settings = { ...settings, ...response };
|
|
30
31
|
}
|
|
32
|
+
settingsLoaded = true;
|
|
31
33
|
});
|
|
32
34
|
$effect(() => {
|
|
33
|
-
|
|
35
|
+
if (settingsLoaded) {
|
|
36
|
+
set('motion-tools-settings', $state.snapshot(settings));
|
|
37
|
+
}
|
|
34
38
|
});
|
|
35
39
|
const context = {
|
|
36
40
|
get current() {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
2
|
import { SvelteSet } from 'svelte/reactivity';
|
|
3
|
+
export const WEBLABS_EXPERIMENTS = {
|
|
4
|
+
MOTION_TOOLS_EDIT_FRAME: 'MOTION_TOOLS_EDIT_FRAME',
|
|
5
|
+
};
|
|
3
6
|
export const WEBLABS_CONTEXT_KEY = Symbol('weblabs-context');
|
|
4
7
|
const getCookie = (name) => {
|
|
5
8
|
const value = `; ${document.cookie}`;
|
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
import { BatchedMesh,
|
|
1
|
+
import { BatchedMesh, Vector3, Color } from 'three';
|
|
2
2
|
import type { OBB } from 'three/addons/math/OBB.js';
|
|
3
|
-
interface Arrow {
|
|
4
|
-
shaftId: number;
|
|
5
|
-
headId: number;
|
|
6
|
-
}
|
|
7
3
|
export declare class BatchedArrow {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
_idCounter: number;
|
|
16
|
-
constructor({ maxArrows, shaftWidth, material, }?: {
|
|
17
|
-
maxArrows?: number | undefined;
|
|
18
|
-
shaftWidth?: number | undefined;
|
|
19
|
-
material?: MeshBasicMaterial | undefined;
|
|
20
|
-
});
|
|
4
|
+
mesh: BatchedMesh;
|
|
5
|
+
_geometryId: number;
|
|
6
|
+
_pool: number[];
|
|
7
|
+
_ids: Set<number>;
|
|
8
|
+
_id: number;
|
|
9
|
+
_max: number;
|
|
10
|
+
constructor();
|
|
21
11
|
addArrow(direction: Vector3, origin: Vector3, length?: number, color?: Color, arrowHeadAtPose?: boolean): number;
|
|
22
|
-
|
|
23
|
-
getBoundingBoxAt(arrowId: number, target: OBB): any;
|
|
24
|
-
removeArrow(arrowId: number): void;
|
|
25
|
-
updateArrow(arrowId: number, direction: Vector3, origin: Vector3, length?: number, color?: Color, arrowHeadAtPose?: boolean): void;
|
|
12
|
+
removeArrow(instanceId: number): void;
|
|
26
13
|
clear(): void;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
_computeTransform(origin: Vector3, dir: Vector3, lengthY: number, scaleXZ?: number): Matrix4;
|
|
30
|
-
_quaternionFromDirection(dir: Vector3): import("three").Quaternion;
|
|
14
|
+
getBoundingBoxAt(instanceId: number, target: OBB): OBB;
|
|
15
|
+
updateArrow(instanceId: number, origin: Vector3, direction: Vector3, length: number, color: Color, arrowHeadAtPose: boolean): import("three").Quaternion | undefined;
|
|
31
16
|
get object3d(): BatchedMesh;
|
|
32
17
|
}
|
|
33
|
-
export {};
|
|
@@ -1,143 +1,79 @@
|
|
|
1
|
-
import { BatchedMesh,
|
|
1
|
+
import { BatchedMesh, MeshBasicMaterial, Object3D, Vector3, Color, Box3 } from 'three';
|
|
2
|
+
import { createArrowGeometry } from './arrow';
|
|
2
3
|
const black = new Color('black');
|
|
3
4
|
const axis = new Vector3();
|
|
4
5
|
const object3d = new Object3D();
|
|
5
6
|
const vec3 = new Vector3();
|
|
6
7
|
const box1 = new Box3();
|
|
7
|
-
const box2 = new Box3();
|
|
8
|
-
const box3 = new Box3();
|
|
9
|
-
const mat4_1 = new Matrix4();
|
|
10
|
-
const mat4_2 = new Matrix4();
|
|
11
8
|
const col = new Color();
|
|
12
9
|
let index = 0;
|
|
13
10
|
export class BatchedArrow {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
coneGeoId = -1;
|
|
17
|
-
shaftWidth = 0;
|
|
18
|
-
_arrows = new Map(); // arrowId -> { shaftId, headId }
|
|
19
|
-
_idToArrowId = new Map();
|
|
11
|
+
mesh;
|
|
12
|
+
_geometryId;
|
|
20
13
|
_pool = [];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.batchedMesh = new BatchedMesh(maxArrows * 2, maxVertexCount, maxIndexCount, material);
|
|
34
|
-
this.batchedMesh.name = `batched arrows ${++index}`;
|
|
35
|
-
this.batchedMesh.frustumCulled = false;
|
|
36
|
-
this.shaftWidth = shaftWidth;
|
|
37
|
-
this.shaftGeoId = this.batchedMesh.addGeometry(shaftGeo);
|
|
38
|
-
this.coneGeoId = this.batchedMesh.addGeometry(coneGeo);
|
|
14
|
+
_ids = new Set();
|
|
15
|
+
_id = 0;
|
|
16
|
+
_max = 20_000;
|
|
17
|
+
constructor() {
|
|
18
|
+
const material = new MeshBasicMaterial({ color: 0xffffff, toneMapped: false });
|
|
19
|
+
const geometry = createArrowGeometry();
|
|
20
|
+
const vertexCount = geometry.getAttribute('position').count;
|
|
21
|
+
const indexCount = geometry.index?.count ?? vertexCount;
|
|
22
|
+
this.mesh = new BatchedMesh(this._max, vertexCount, indexCount, material);
|
|
23
|
+
this.mesh.name = `Batched arrows ${++index}`;
|
|
24
|
+
this.mesh.frustumCulled = false;
|
|
25
|
+
this._geometryId = this.mesh.addGeometry(geometry);
|
|
39
26
|
}
|
|
40
27
|
addArrow(direction, origin, length = 0.1, color = black, arrowHeadAtPose = true) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (instance) {
|
|
45
|
-
;
|
|
46
|
-
({ shaftId, headId } = instance);
|
|
28
|
+
if (this.mesh.instanceCount >= this._max) {
|
|
29
|
+
this._max += 20_000;
|
|
30
|
+
this.mesh.setInstanceCount(this._max);
|
|
47
31
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this._drawArrow(shaftId, headId, direction, origin, length, color, arrowHeadAtPose);
|
|
53
|
-
const arrowId = this._idCounter++;
|
|
54
|
-
this._arrows.set(arrowId, { shaftId, headId });
|
|
55
|
-
this._idToArrowId.set(shaftId, arrowId);
|
|
56
|
-
this._idToArrowId.set(headId, arrowId);
|
|
57
|
-
return arrowId;
|
|
58
|
-
}
|
|
59
|
-
getArrowId(instanceId) {
|
|
60
|
-
return this._idToArrowId.get(instanceId);
|
|
61
|
-
}
|
|
62
|
-
getBoundingBoxAt(arrowId, target) {
|
|
63
|
-
const arrow = this._arrows.get(arrowId);
|
|
64
|
-
if (arrow) {
|
|
65
|
-
const headBox = this.batchedMesh.getBoundingBoxAt(this.coneGeoId, box1);
|
|
66
|
-
const tailBox = this.batchedMesh.getBoundingBoxAt(this.shaftGeoId, box2);
|
|
67
|
-
if (headBox && tailBox) {
|
|
68
|
-
this.batchedMesh.getMatrixAt(arrow.headId, mat4_1);
|
|
69
|
-
this.batchedMesh.getMatrixAt(arrow.shaftId, mat4_2);
|
|
70
|
-
box3.copy(headBox.applyMatrix4(mat4_1)).union(tailBox.applyMatrix4(mat4_2));
|
|
71
|
-
target.fromBox3(box3);
|
|
72
|
-
return target;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
removeArrow(arrowId) {
|
|
77
|
-
const arrow = this._arrows.get(arrowId);
|
|
78
|
-
if (!arrow)
|
|
79
|
-
return;
|
|
80
|
-
this.batchedMesh.setVisibleAt(arrow.shaftId, false);
|
|
81
|
-
this.batchedMesh.setVisibleAt(arrow.headId, false);
|
|
82
|
-
this._pool.push(arrow);
|
|
83
|
-
this._arrows.delete(arrowId);
|
|
32
|
+
const instanceId = this._pool.pop() ?? this.mesh.addInstance(this._geometryId);
|
|
33
|
+
this._ids.add(instanceId);
|
|
34
|
+
this.updateArrow(instanceId, origin, direction, length, color, arrowHeadAtPose);
|
|
35
|
+
return instanceId;
|
|
84
36
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this._drawArrow(arrow.shaftId, arrow.headId, direction, origin, length, color, arrowHeadAtPose);
|
|
37
|
+
removeArrow(instanceId) {
|
|
38
|
+
this._ids.delete(instanceId);
|
|
39
|
+
this.mesh.setVisibleAt(instanceId, false);
|
|
40
|
+
this._pool.push(instanceId);
|
|
90
41
|
}
|
|
91
42
|
clear() {
|
|
92
|
-
for (const id of this.
|
|
43
|
+
for (const id of this._ids) {
|
|
93
44
|
this.removeArrow(id);
|
|
94
45
|
}
|
|
95
46
|
}
|
|
96
|
-
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
|
|
47
|
+
getBoundingBoxAt(instanceId, target) {
|
|
48
|
+
const box = this.mesh.getBoundingBoxAt(instanceId, box1);
|
|
49
|
+
if (box) {
|
|
50
|
+
target.fromBox3(box);
|
|
51
|
+
}
|
|
52
|
+
return target;
|
|
100
53
|
}
|
|
101
|
-
|
|
54
|
+
updateArrow(instanceId, origin, direction, length, color, arrowHeadAtPose) {
|
|
102
55
|
if (arrowHeadAtPose) {
|
|
103
56
|
// Compute the base position so the arrow ends at the origin
|
|
104
57
|
origin.sub(vec3.copy(direction).multiplyScalar(length));
|
|
105
58
|
}
|
|
106
59
|
direction.normalize();
|
|
107
|
-
const headLength = length * 0.2;
|
|
108
|
-
const headWidth = headLength * 0.2;
|
|
109
|
-
// Apply shaft transform
|
|
110
|
-
const shaftMatrix = this._computeTransform(origin, direction, length - headLength, this.shaftWidth);
|
|
111
|
-
this.batchedMesh.setMatrixAt(shaftId, shaftMatrix);
|
|
112
|
-
// Compute cone position = origin + dir * length
|
|
113
|
-
const coneOrigin = vec3.copy(direction).multiplyScalar(length).add(origin);
|
|
114
|
-
const coneMatrix = this._computeTransform(coneOrigin, direction, headLength, headWidth * 4);
|
|
115
|
-
this.batchedMesh.setMatrixAt(headId, coneMatrix);
|
|
116
|
-
if (color) {
|
|
117
|
-
col.set(color);
|
|
118
|
-
this.batchedMesh.setColorAt(shaftId, col);
|
|
119
|
-
this.batchedMesh.setColorAt(headId, col);
|
|
120
|
-
}
|
|
121
|
-
this.batchedMesh.setVisibleAt(shaftId, true);
|
|
122
|
-
this.batchedMesh.setVisibleAt(headId, true);
|
|
123
|
-
}
|
|
124
|
-
_computeTransform(origin, dir, lengthY, scaleXZ = 1) {
|
|
125
60
|
object3d.position.copy(origin);
|
|
126
|
-
|
|
127
|
-
object3d.scale.set(scaleXZ, lengthY, scaleXZ);
|
|
128
|
-
object3d.updateMatrix();
|
|
129
|
-
return object3d.matrix.clone();
|
|
130
|
-
}
|
|
131
|
-
_quaternionFromDirection(dir) {
|
|
132
|
-
if (dir.y > 0.99999)
|
|
61
|
+
if (direction.y > 0.99999)
|
|
133
62
|
return object3d.quaternion.set(0, 0, 0, 1);
|
|
134
|
-
if (
|
|
63
|
+
if (direction.y < -0.99999)
|
|
135
64
|
return object3d.quaternion.set(1, 0, 0, 0);
|
|
136
|
-
axis.set(
|
|
137
|
-
const radians = Math.acos(
|
|
138
|
-
|
|
65
|
+
axis.set(direction.z, 0, -direction.x).normalize();
|
|
66
|
+
const radians = Math.acos(direction.y);
|
|
67
|
+
object3d.quaternion.setFromAxisAngle(axis, radians);
|
|
68
|
+
object3d.updateMatrix();
|
|
69
|
+
this.mesh.setMatrixAt(instanceId, object3d.matrix);
|
|
70
|
+
if (color) {
|
|
71
|
+
col.set(color);
|
|
72
|
+
this.mesh.setColorAt(instanceId, col);
|
|
73
|
+
}
|
|
74
|
+
this.mesh.setVisibleAt(instanceId, true);
|
|
139
75
|
}
|
|
140
76
|
get object3d() {
|
|
141
|
-
return this.
|
|
77
|
+
return this.mesh;
|
|
142
78
|
}
|
|
143
79
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BoxGeometry, ConeGeometry } from 'three';
|
|
2
|
+
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returns one merged geometry for an arrow (box tail + cone head)
|
|
5
|
+
*
|
|
6
|
+
* Arrow points along +Y with its base at y = 0
|
|
7
|
+
*/
|
|
8
|
+
export const createArrowGeometry = () => {
|
|
9
|
+
const length = 0.1;
|
|
10
|
+
const headLength = length * 0.2;
|
|
11
|
+
const headWidth = headLength * 0.3;
|
|
12
|
+
const tailLength = length - headLength;
|
|
13
|
+
const tailWidth = 0.001;
|
|
14
|
+
// Tail: box translated so base starts at y = 0
|
|
15
|
+
const tailGeometry = new BoxGeometry(tailWidth, tailLength, tailWidth);
|
|
16
|
+
tailGeometry.translate(0, tailLength * 0.5, 0);
|
|
17
|
+
// Head: cone centered at origin spanning [-h/2, +h/2] in y
|
|
18
|
+
const radialSegments = 5;
|
|
19
|
+
const headGeo = new ConeGeometry(headWidth * 0.5, headLength, radialSegments, 1, false);
|
|
20
|
+
// Place its center at y = shaftLength + headLength/2 so tip lands at y = shaftLength + headLength
|
|
21
|
+
headGeo.translate(0, tailLength + headLength * 0.5, 0);
|
|
22
|
+
const merged = mergeGeometries([tailGeometry, headGeo], true);
|
|
23
|
+
merged.computeVertexNormals();
|
|
24
|
+
merged.computeBoundingBox();
|
|
25
|
+
merged.computeBoundingSphere();
|
|
26
|
+
return merged;
|
|
27
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viamrobotics/motion-tools",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.12",
|
|
4
4
|
"description": "Motion visualization with Viam",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"svelte-check": "4.3.1",
|
|
61
61
|
"svelte-virtuallists": "1.4.2",
|
|
62
62
|
"tailwindcss": "4.1.13",
|
|
63
|
-
"three": "0.
|
|
63
|
+
"three": "0.180.0",
|
|
64
64
|
"three-mesh-bvh": "^0.9.1",
|
|
65
65
|
"threlte-uikit": "1.2.1",
|
|
66
66
|
"tsx": "4.20.5",
|