@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,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { TransformControls } from '@threlte/extras'
|
|
3
|
-
import { Quaternion, Vector3 } from 'three'
|
|
3
|
+
import { Matrix4, Quaternion, Vector3 } from 'three'
|
|
4
4
|
|
|
5
5
|
import type { FrameEditSession } from '../editing/FrameEditSession'
|
|
6
6
|
|
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
import { useSelectedEntity, useSelectedObject3d } from '../hooks/useSelection.svelte'
|
|
12
12
|
import { useSettings } from '../hooks/useSettings.svelte'
|
|
13
13
|
import {
|
|
14
|
-
composeEditedPoseForRenderedPose,
|
|
15
14
|
createPose,
|
|
15
|
+
matrixToPose,
|
|
16
|
+
poseToMatrix,
|
|
16
17
|
quaternionToPose,
|
|
18
|
+
solveEditedMatrix,
|
|
17
19
|
vector3ToPose,
|
|
18
20
|
} from '../transform'
|
|
19
21
|
|
|
@@ -27,8 +29,8 @@
|
|
|
27
29
|
const mode = $derived(settings.current.transformMode)
|
|
28
30
|
const entity = $derived(selectedEntity.current)
|
|
29
31
|
const transformable = useTrait(() => entity, traits.Transformable)
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
+
const configMatrix = useTrait(() => entity, traits.Matrix)
|
|
33
|
+
const liveMatrix = useTrait(() => entity, traits.LiveMatrix)
|
|
32
34
|
const box = useTrait(() => entity, traits.Box)
|
|
33
35
|
const sphere = useTrait(() => entity, traits.Sphere)
|
|
34
36
|
const capsule = useTrait(() => entity, traits.Capsule)
|
|
@@ -42,10 +44,12 @@
|
|
|
42
44
|
// the geometry inside it.
|
|
43
45
|
const ref = $derived(selectedObject3d.current?.parent ?? selectedObject3d.current)
|
|
44
46
|
|
|
45
|
-
const activeMode = $derived.by(() => {
|
|
46
|
-
if (mode === 'none' || !transformable.current) return
|
|
47
|
+
const activeMode = $derived.by<'translate' | 'rotate' | 'scale' | undefined>(() => {
|
|
48
|
+
if (mode === 'none' || !transformable.current) return
|
|
49
|
+
|
|
47
50
|
// Scale only does anything for primitive geometries the gizmo can size.
|
|
48
|
-
if (mode === 'scale' && !hasScalableGeometry) return
|
|
51
|
+
if (mode === 'scale' && !hasScalableGeometry) return
|
|
52
|
+
|
|
49
53
|
return mode
|
|
50
54
|
})
|
|
51
55
|
const isSphereScale = $derived(activeMode === 'scale' && sphere.current !== undefined)
|
|
@@ -54,6 +58,9 @@
|
|
|
54
58
|
const quaternion = new Quaternion()
|
|
55
59
|
const vector3 = new Vector3()
|
|
56
60
|
const refPose = createPose()
|
|
61
|
+
const tempRefMatrix = new Matrix4()
|
|
62
|
+
const tempEditedMatrix = new Matrix4()
|
|
63
|
+
const tempPose = createPose()
|
|
57
64
|
|
|
58
65
|
let session: FrameEditSession | undefined
|
|
59
66
|
let scaleStart:
|
|
@@ -93,7 +100,9 @@
|
|
|
93
100
|
if (entity?.has(traits.FramesAPI)) {
|
|
94
101
|
session = sessions.begin([entity])
|
|
95
102
|
}
|
|
103
|
+
|
|
96
104
|
captureScaleStart()
|
|
105
|
+
|
|
97
106
|
environment.current.viewerMode = 'edit'
|
|
98
107
|
transformControls.setActive(true)
|
|
99
108
|
}
|
|
@@ -109,15 +118,17 @@
|
|
|
109
118
|
if (isFrameEntity) {
|
|
110
119
|
stageFrameTransform()
|
|
111
120
|
} else {
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
121
|
+
const matrix = entity.get(traits.Matrix)
|
|
122
|
+
if (matrix) {
|
|
123
|
+
matrixToPose(matrix, tempPose)
|
|
114
124
|
if (activeMode === 'translate') {
|
|
115
|
-
vector3ToPose(ref.getWorldPosition(vector3),
|
|
125
|
+
vector3ToPose(ref.getWorldPosition(vector3), tempPose)
|
|
116
126
|
} else {
|
|
117
|
-
quaternionToPose(ref.getWorldQuaternion(quaternion),
|
|
127
|
+
quaternionToPose(ref.getWorldQuaternion(quaternion), tempPose)
|
|
118
128
|
ref.quaternion.copy(quaternion)
|
|
119
129
|
}
|
|
120
|
-
|
|
130
|
+
poseToMatrix(tempPose, matrix)
|
|
131
|
+
entity.changed(traits.Matrix)
|
|
121
132
|
}
|
|
122
133
|
}
|
|
123
134
|
} else {
|
|
@@ -127,11 +138,14 @@
|
|
|
127
138
|
captureScaleStart()
|
|
128
139
|
}
|
|
129
140
|
|
|
141
|
+
// Clamp at 0 — the gizmo can produce negative scale factors when
|
|
142
|
+
// dragged past the origin, which would yield negative dimensions
|
|
143
|
+
// and a degenerate OBB.
|
|
130
144
|
if (scaleStart?.type === 'box') {
|
|
131
145
|
const next = {
|
|
132
|
-
x: scaleStart.x * ref.scale.x,
|
|
133
|
-
y: scaleStart.y * ref.scale.y,
|
|
134
|
-
z: scaleStart.z * ref.scale.z,
|
|
146
|
+
x: Math.max(0, scaleStart.x * ref.scale.x),
|
|
147
|
+
y: Math.max(0, scaleStart.y * ref.scale.y),
|
|
148
|
+
z: Math.max(0, scaleStart.z * ref.scale.z),
|
|
135
149
|
}
|
|
136
150
|
if (isFrameEntity) {
|
|
137
151
|
session?.stageGeometry(entity, { type: 'box', ...next })
|
|
@@ -139,14 +153,17 @@
|
|
|
139
153
|
entity.set(traits.Box, next)
|
|
140
154
|
}
|
|
141
155
|
} else if (scaleStart?.type === 'sphere') {
|
|
142
|
-
const next = { r: scaleStart.r * ref.scale.x }
|
|
156
|
+
const next = { r: Math.max(0, scaleStart.r * ref.scale.x) }
|
|
143
157
|
if (isFrameEntity) {
|
|
144
158
|
session?.stageGeometry(entity, { type: 'sphere', ...next })
|
|
145
159
|
} else {
|
|
146
160
|
entity.set(traits.Sphere, next)
|
|
147
161
|
}
|
|
148
162
|
} else if (scaleStart?.type === 'capsule') {
|
|
149
|
-
const next = {
|
|
163
|
+
const next = {
|
|
164
|
+
r: Math.max(0, scaleStart.r * ref.scale.x),
|
|
165
|
+
l: Math.max(0, scaleStart.l * ref.scale.y),
|
|
166
|
+
}
|
|
150
167
|
if (isFrameEntity) {
|
|
151
168
|
session?.stageGeometry(entity, { type: 'capsule', ...next })
|
|
152
169
|
} else {
|
|
@@ -165,29 +182,31 @@
|
|
|
165
182
|
transformControls.setActive(false)
|
|
166
183
|
}
|
|
167
184
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Frame.svelte renders frame entities by blending M(live) × M(config)⁻¹ × M(edited)
|
|
187
|
+
* so for the user's drag to render where they pulled the gizmo to,
|
|
188
|
+
* EditedMatrix must satisfy M(edited) = M(config) × M(live)⁻¹ × M(ref)
|
|
189
|
+
* where M(ref) is the gizmo-driven group's parent-relative matrix in mm.
|
|
190
|
+
*
|
|
191
|
+
* When live ≈ config (no kinematic offset), this collapses to
|
|
192
|
+
* M(edited) = M(ref) — the same as the naive writeback. When they diverge
|
|
193
|
+
* (e.g. an arm whose joints have moved away from its config pose), this
|
|
194
|
+
* composition is what keeps the rendering anchored to the user's pointer
|
|
195
|
+
* instead of shearing through the live × baseline⁻¹ offset.
|
|
196
|
+
*/
|
|
197
|
+
|
|
179
198
|
const stageFrameTransform = () => {
|
|
180
199
|
if (!ref || !entity) return
|
|
181
200
|
|
|
182
201
|
vector3ToPose(ref.position, refPose)
|
|
183
202
|
quaternionToPose(ref.quaternion, refPose)
|
|
184
203
|
|
|
185
|
-
const live =
|
|
186
|
-
const
|
|
204
|
+
const live = liveMatrix.current
|
|
205
|
+
const config = configMatrix.current
|
|
187
206
|
|
|
188
|
-
if (!live || !
|
|
189
|
-
// No live
|
|
190
|
-
//
|
|
207
|
+
if (!live || !config) {
|
|
208
|
+
// No live matrix available — Frame.svelte's blend short-circuits to
|
|
209
|
+
// editedMatrix, so naive writeback is correct.
|
|
191
210
|
if (activeMode === 'translate') {
|
|
192
211
|
session?.stagePose(entity, {
|
|
193
212
|
x: refPose.x,
|
|
@@ -205,7 +224,11 @@
|
|
|
205
224
|
return
|
|
206
225
|
}
|
|
207
226
|
|
|
208
|
-
|
|
227
|
+
poseToMatrix(refPose, tempRefMatrix)
|
|
228
|
+
|
|
229
|
+
solveEditedMatrix(config, live, tempRefMatrix, tempEditedMatrix)
|
|
230
|
+
matrixToPose(tempEditedMatrix, tempPose)
|
|
231
|
+
session?.stagePose(entity, { ...tempPose })
|
|
209
232
|
}
|
|
210
233
|
</script>
|
|
211
234
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { MathUtils, Quaternion, Vector3 } from 'three'
|
|
3
|
+
|
|
4
|
+
import type { HoverInfo } from '../../HoverUpdater.svelte'
|
|
5
|
+
|
|
2
6
|
import { traits, useTrait } from '../../ecs'
|
|
3
7
|
import { useFocusedEntity, useSelectedEntity } from '../../hooks/useSelection.svelte'
|
|
8
|
+
import { OrientationVector } from '../../three/OrientationVector'
|
|
4
9
|
|
|
5
10
|
import HoveredEntityTooltip from './HoveredEntityTooltip.svelte'
|
|
6
11
|
|
|
@@ -8,9 +13,34 @@
|
|
|
8
13
|
const focusedEntity = useFocusedEntity()
|
|
9
14
|
|
|
10
15
|
const displayEntity = $derived(selectedEntity.current ?? focusedEntity.current)
|
|
11
|
-
const
|
|
16
|
+
const instancedMatrix = useTrait(() => displayEntity, traits.InstancedMatrix)
|
|
17
|
+
|
|
18
|
+
// Pool: InstancedMatrix's `Matrix4` is in metres (matches Three.js).
|
|
19
|
+
// Decompose for the tooltip's display, which expects metres for position
|
|
20
|
+
// and OV+theta for orientation.
|
|
21
|
+
const translation = new Vector3()
|
|
22
|
+
const quaternion = new Quaternion()
|
|
23
|
+
const scaleVec = new Vector3()
|
|
24
|
+
const ov = new OrientationVector()
|
|
25
|
+
|
|
26
|
+
const hoverInfo = $derived.by((): HoverInfo | undefined => {
|
|
27
|
+
const data = instancedMatrix.current
|
|
28
|
+
if (!data) return undefined
|
|
29
|
+
data.matrix.decompose(translation, quaternion, scaleVec)
|
|
30
|
+
ov.setFromQuaternion(quaternion)
|
|
31
|
+
return {
|
|
32
|
+
index: data.index,
|
|
33
|
+
x: translation.x,
|
|
34
|
+
y: translation.y,
|
|
35
|
+
z: translation.z,
|
|
36
|
+
oX: ov.x,
|
|
37
|
+
oY: ov.y,
|
|
38
|
+
oZ: ov.z,
|
|
39
|
+
theta: MathUtils.radToDeg(ov.th),
|
|
40
|
+
}
|
|
41
|
+
})
|
|
12
42
|
</script>
|
|
13
43
|
|
|
14
|
-
{#if hoverInfo
|
|
15
|
-
<HoveredEntityTooltip
|
|
44
|
+
{#if hoverInfo}
|
|
45
|
+
<HoveredEntityTooltip {hoverInfo} />
|
|
16
46
|
{/if}
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { compileExpression } from 'filtrex'
|
|
5
5
|
|
|
6
|
-
import { relations, traits } from '../../ecs'
|
|
7
|
-
import { useTrait } from '../../ecs'
|
|
6
|
+
import { relations, traits, useTrait } from '../../ecs'
|
|
8
7
|
import { SubEntityLinkType } from '../../ecs/relations'
|
|
9
8
|
import { useSelectedEntity } from '../../hooks/useSelection.svelte'
|
|
10
9
|
import { useFocusedEntity } from '../../hooks/useSelection.svelte'
|
|
@@ -22,7 +21,7 @@
|
|
|
22
21
|
const focusedEntity = useFocusedEntity()
|
|
23
22
|
const displayEntity = $derived(selectedEntity.current ?? focusedEntity.current)
|
|
24
23
|
|
|
25
|
-
const displayedHoverInfo = useTrait(() => displayEntity, traits.
|
|
24
|
+
const displayedHoverInfo = useTrait(() => displayEntity, traits.InstancedMatrix)
|
|
26
25
|
|
|
27
26
|
let hoverInfo = $state.raw<HoverInfo | null>(null)
|
|
28
27
|
|
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
lang="ts"
|
|
4
4
|
>
|
|
5
5
|
import { ThemeUtils } from 'svelte-tweakpane-ui'
|
|
6
|
-
import { BufferAttribute, Euler, MathUtils, Quaternion
|
|
6
|
+
import { BufferAttribute, Euler, MathUtils, Quaternion } from 'three'
|
|
7
7
|
|
|
8
8
|
import { OrientationVector } from '../../three/OrientationVector'
|
|
9
9
|
|
|
10
|
-
const vec3 = new Vector3()
|
|
11
10
|
const quaternion = new Quaternion()
|
|
12
11
|
const ov = new OrientationVector()
|
|
13
12
|
const euler = new Euler()
|
|
14
13
|
</script>
|
|
15
14
|
|
|
16
15
|
<script lang="ts">
|
|
16
|
+
import type { Pose } from '@viamrobotics/sdk'
|
|
17
17
|
import type { Entity } from 'koota'
|
|
18
18
|
import type { Snippet } from 'svelte'
|
|
19
19
|
|
|
20
20
|
import { draggable } from '@neodrag/svelte'
|
|
21
|
-
import { isInstanceOf,
|
|
21
|
+
import { isInstanceOf, useThrelte } from '@threlte/core'
|
|
22
22
|
import { Button, Icon, Tooltip } from '@viamrobotics/prime-core'
|
|
23
23
|
import { Check, Copy } from 'lucide-svelte'
|
|
24
24
|
import {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
useSelectedEntity,
|
|
54
54
|
useSelectedObject3d,
|
|
55
55
|
} from '../../hooks/useSelection.svelte'
|
|
56
|
-
import { createPose } from '../../transform'
|
|
56
|
+
import { createPose, matrixToPose } from '../../transform'
|
|
57
57
|
|
|
58
58
|
interface Props {
|
|
59
59
|
details?: Snippet<[{ entity: Entity }]>
|
|
@@ -73,14 +73,17 @@
|
|
|
73
73
|
const environment = useEnvironment()
|
|
74
74
|
const focusedEntity = useFocusedEntity()
|
|
75
75
|
const focusedObject3d = useFocusedObject3d()
|
|
76
|
+
const linkedEntities = useLinkedEntities()
|
|
77
|
+
|
|
76
78
|
const entity = $derived(focusedEntity.current ?? selectedEntity.current)
|
|
77
79
|
const object3d = $derived(focusedObject3d.current ?? selectedObject3d.current)
|
|
78
|
-
|
|
79
|
-
const worldOrientation = $state({ x: 0, y: 0, z: 1, th: 0 })
|
|
80
|
-
const linkedEntities = useLinkedEntities()
|
|
80
|
+
|
|
81
81
|
const name = useTrait(() => entity, traits.Name)
|
|
82
82
|
const parent = useParentName(() => entity)
|
|
83
|
-
const
|
|
83
|
+
const matrix = useTrait(() => entity, traits.Matrix)
|
|
84
|
+
const editedMatrix = useTrait(() => entity, traits.EditedMatrix)
|
|
85
|
+
const worldMatrix = useTrait(() => entity, traits.WorldMatrix)
|
|
86
|
+
const center = useTrait(() => entity, traits.Center)
|
|
84
87
|
const box = useTrait(() => entity, traits.Box)
|
|
85
88
|
const sphere = useTrait(() => entity, traits.Sphere)
|
|
86
89
|
const capsule = useTrait(() => entity, traits.Capsule)
|
|
@@ -88,14 +91,27 @@
|
|
|
88
91
|
const points = useTrait(() => entity, traits.Points)
|
|
89
92
|
const arrows = useTrait(() => entity, traits.Arrows)
|
|
90
93
|
const opacity = useTrait(() => entity, traits.Opacity)
|
|
91
|
-
|
|
92
94
|
const framesAPI = useTrait(() => entity, traits.FramesAPI)
|
|
93
|
-
const
|
|
95
|
+
const geometriesAPI = useTrait(() => entity, traits.GeometriesAPI)
|
|
96
|
+
|
|
97
|
+
const localPose = $derived.by<Pose | undefined>(() => {
|
|
98
|
+
const source = editedMatrix.current ?? matrix.current
|
|
99
|
+
if (source) return matrixToPose(source, createPose())
|
|
100
|
+
if (center.current) return createPose(center.current)
|
|
101
|
+
return undefined
|
|
102
|
+
})
|
|
103
|
+
const worldPose = $derived.by<Pose | undefined>(() => {
|
|
104
|
+
if (!worldMatrix.current) return
|
|
105
|
+
|
|
106
|
+
return matrixToPose(worldMatrix.current, createPose())
|
|
107
|
+
})
|
|
94
108
|
|
|
109
|
+
const isFrameNode = $derived(!!framesAPI.current)
|
|
110
|
+
const isGeometry = $derived(!!geometriesAPI.current)
|
|
95
111
|
const showEditFrameOptions = $derived(isFrameNode && partConfig.hasEditPermissions)
|
|
96
112
|
const showRelationshipOptions = $derived(points.current || arrows.current)
|
|
97
|
-
|
|
98
113
|
const resourceName = $derived(name.current ? resourceByName.current[name.current] : undefined)
|
|
114
|
+
const displayType = $derived(isFrameNode ? resourceName?.subtype : isGeometry ? 'geometry' : '')
|
|
99
115
|
|
|
100
116
|
let geometryType = $derived.by<'box' | 'sphere' | 'capsule' | 'none'>(() => {
|
|
101
117
|
if (box.current) return 'box'
|
|
@@ -120,13 +136,8 @@
|
|
|
120
136
|
let dragElement = $state.raw<HTMLElement>()
|
|
121
137
|
|
|
122
138
|
const eulerValue = $derived.by<RotationEulerValueObject>(() => {
|
|
123
|
-
if (!localPose
|
|
124
|
-
ov.set(
|
|
125
|
-
localPose.current.oX,
|
|
126
|
-
localPose.current.oY,
|
|
127
|
-
localPose.current.oZ,
|
|
128
|
-
MathUtils.degToRad(localPose.current.theta)
|
|
129
|
-
)
|
|
139
|
+
if (!localPose) return { x: 0, y: 0, z: 0 }
|
|
140
|
+
ov.set(localPose.oX, localPose.oY, localPose.oZ, MathUtils.degToRad(localPose.theta))
|
|
130
141
|
ov.toEuler(euler)
|
|
131
142
|
return {
|
|
132
143
|
x: MathUtils.radToDeg(euler.x),
|
|
@@ -236,65 +247,28 @@
|
|
|
236
247
|
}
|
|
237
248
|
}
|
|
238
249
|
|
|
239
|
-
useTask(
|
|
240
|
-
() => {
|
|
241
|
-
object3d?.getWorldPosition(vec3)
|
|
242
|
-
if (!vec3.equals(worldPosition)) {
|
|
243
|
-
worldPosition.x = vec3.x
|
|
244
|
-
worldPosition.y = vec3.y
|
|
245
|
-
worldPosition.z = vec3.z
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
object3d?.getWorldQuaternion(quaternion)
|
|
249
|
-
ov.setFromQuaternion(quaternion)
|
|
250
|
-
|
|
251
|
-
if (!ov.equals(worldOrientation)) {
|
|
252
|
-
worldOrientation.x = ov.x
|
|
253
|
-
worldOrientation.y = ov.y
|
|
254
|
-
worldOrientation.z = ov.z
|
|
255
|
-
worldOrientation.th = ov.th
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
autoInvalidate: false,
|
|
260
|
-
running: () => object3d !== undefined,
|
|
261
|
-
}
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
$effect(() => {
|
|
265
|
-
if (entity) {
|
|
266
|
-
const worldPose = createPose({
|
|
267
|
-
x: worldPosition.x,
|
|
268
|
-
y: worldPosition.y,
|
|
269
|
-
z: worldPosition.z,
|
|
270
|
-
oX: worldOrientation.x,
|
|
271
|
-
oY: worldOrientation.y,
|
|
272
|
-
oZ: worldOrientation.z,
|
|
273
|
-
theta: MathUtils.radToDeg(worldOrientation.th),
|
|
274
|
-
})
|
|
275
|
-
if (entity.has(traits.WorldPose)) {
|
|
276
|
-
entity.set(traits.WorldPose, worldPose)
|
|
277
|
-
} else {
|
|
278
|
-
entity.add(traits.WorldPose(worldPose))
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
})
|
|
282
|
-
|
|
283
250
|
const getCopyClipboardText = () => {
|
|
284
251
|
return JSON.stringify(
|
|
285
252
|
{
|
|
286
|
-
worldPosition:
|
|
287
|
-
worldOrientation:
|
|
253
|
+
worldPosition: worldPose ? { x: worldPose.x, y: worldPose.y, z: worldPose.z } : null,
|
|
254
|
+
worldOrientation: worldPose
|
|
255
|
+
? {
|
|
256
|
+
x: worldPose.oX,
|
|
257
|
+
y: worldPose.oY,
|
|
258
|
+
z: worldPose.oZ,
|
|
259
|
+
th: MathUtils.degToRad(worldPose.theta),
|
|
260
|
+
}
|
|
261
|
+
: null,
|
|
288
262
|
localPosition: {
|
|
289
|
-
x: localPose
|
|
290
|
-
y: localPose
|
|
291
|
-
z: localPose
|
|
263
|
+
x: localPose?.x,
|
|
264
|
+
y: localPose?.y,
|
|
265
|
+
z: localPose?.z,
|
|
292
266
|
},
|
|
293
267
|
localOrientation: {
|
|
294
|
-
x: localPose
|
|
295
|
-
y: localPose
|
|
296
|
-
z: localPose
|
|
297
|
-
th: localPose
|
|
268
|
+
x: localPose?.oX,
|
|
269
|
+
y: localPose?.oY,
|
|
270
|
+
z: localPose?.oZ,
|
|
271
|
+
th: localPose?.theta,
|
|
298
272
|
},
|
|
299
273
|
geometry: {
|
|
300
274
|
type: geometryType,
|
|
@@ -350,7 +324,7 @@
|
|
|
350
324
|
>
|
|
351
325
|
<div class="flex w-[90%] items-center gap-1">
|
|
352
326
|
<strong class="overflow-hidden text-nowrap text-ellipsis">{name.current}</strong>
|
|
353
|
-
<span class="text-subtle-2">{
|
|
327
|
+
<span class="text-subtle-2">{displayType}</span>
|
|
354
328
|
</div>
|
|
355
329
|
|
|
356
330
|
{#if object3d}
|
|
@@ -438,15 +412,15 @@
|
|
|
438
412
|
<div class="flex gap-3">
|
|
439
413
|
<div>
|
|
440
414
|
<span class="text-subtle-2">x</span>
|
|
441
|
-
{(
|
|
415
|
+
{(worldPose?.x ?? 0).toFixed(2)}
|
|
442
416
|
</div>
|
|
443
417
|
<div>
|
|
444
418
|
<span class="text-subtle-2">y</span>
|
|
445
|
-
{(
|
|
419
|
+
{(worldPose?.y ?? 0).toFixed(2)}
|
|
446
420
|
</div>
|
|
447
421
|
<div>
|
|
448
422
|
<span class="text-subtle-2">z</span>
|
|
449
|
-
{(
|
|
423
|
+
{(worldPose?.z ?? 0).toFixed(2)}
|
|
450
424
|
</div>
|
|
451
425
|
</div>
|
|
452
426
|
</div>
|
|
@@ -457,19 +431,19 @@
|
|
|
457
431
|
<div class="flex gap-3">
|
|
458
432
|
<div>
|
|
459
433
|
<span class="text-subtle-2">x</span>
|
|
460
|
-
{
|
|
434
|
+
{(worldPose?.oX ?? 0).toFixed(2)}
|
|
461
435
|
</div>
|
|
462
436
|
<div>
|
|
463
437
|
<span class="text-subtle-2">y</span>
|
|
464
|
-
{
|
|
438
|
+
{(worldPose?.oY ?? 0).toFixed(2)}
|
|
465
439
|
</div>
|
|
466
440
|
<div>
|
|
467
441
|
<span class="text-subtle-2">z</span>
|
|
468
|
-
{
|
|
442
|
+
{(worldPose?.oZ ?? 0).toFixed(2)}
|
|
469
443
|
</div>
|
|
470
444
|
<div>
|
|
471
445
|
<span class="text-subtle-2">th</span>
|
|
472
|
-
{
|
|
446
|
+
{(worldPose?.theta ?? 0).toFixed(2)}
|
|
473
447
|
</div>
|
|
474
448
|
</div>
|
|
475
449
|
</div>
|
|
@@ -505,7 +479,7 @@
|
|
|
505
479
|
{/if}
|
|
506
480
|
</div>
|
|
507
481
|
|
|
508
|
-
{#if localPose
|
|
482
|
+
{#if localPose}
|
|
509
483
|
<div>
|
|
510
484
|
<strong class="font-semibold">local position</strong>
|
|
511
485
|
<span class="text-subtle-2">(mm)</span>
|
|
@@ -514,9 +488,9 @@
|
|
|
514
488
|
<div aria-label="mutable local position">
|
|
515
489
|
<Point
|
|
516
490
|
value={{
|
|
517
|
-
x: localPose.
|
|
518
|
-
y: localPose.
|
|
519
|
-
z: localPose.
|
|
491
|
+
x: localPose.x,
|
|
492
|
+
y: localPose.y,
|
|
493
|
+
z: localPose.z,
|
|
520
494
|
}}
|
|
521
495
|
on:change={handlePositionChange}
|
|
522
496
|
/>
|
|
@@ -526,17 +500,17 @@
|
|
|
526
500
|
{@render ImmutableField({
|
|
527
501
|
label: 'x',
|
|
528
502
|
ariaLabel: 'local position x coordinate',
|
|
529
|
-
value: localPose.
|
|
503
|
+
value: localPose.x,
|
|
530
504
|
})}
|
|
531
505
|
{@render ImmutableField({
|
|
532
506
|
label: 'y',
|
|
533
507
|
ariaLabel: 'local position y coordinate',
|
|
534
|
-
value: localPose.
|
|
508
|
+
value: localPose.y,
|
|
535
509
|
})}
|
|
536
510
|
{@render ImmutableField({
|
|
537
511
|
label: 'z',
|
|
538
512
|
ariaLabel: 'local position z coordinate',
|
|
539
|
-
value: localPose.
|
|
513
|
+
value: localPose.z,
|
|
540
514
|
})}
|
|
541
515
|
</div>
|
|
542
516
|
{/if}
|
|
@@ -551,10 +525,10 @@
|
|
|
551
525
|
<TabPage title="OV (deg)">
|
|
552
526
|
<Point
|
|
553
527
|
value={{
|
|
554
|
-
x: localPose.
|
|
555
|
-
y: localPose.
|
|
556
|
-
z: localPose.
|
|
557
|
-
w: localPose.
|
|
528
|
+
x: localPose.oX,
|
|
529
|
+
y: localPose.oY,
|
|
530
|
+
z: localPose.oZ,
|
|
531
|
+
w: localPose.theta,
|
|
558
532
|
}}
|
|
559
533
|
on:change={handleOrientationOVChange}
|
|
560
534
|
/>
|
|
@@ -573,22 +547,22 @@
|
|
|
573
547
|
{@render ImmutableField({
|
|
574
548
|
label: 'x',
|
|
575
549
|
ariaLabel: 'local orientation x coordinate',
|
|
576
|
-
value: localPose.
|
|
550
|
+
value: localPose.oX,
|
|
577
551
|
})}
|
|
578
552
|
{@render ImmutableField({
|
|
579
553
|
label: 'y',
|
|
580
554
|
ariaLabel: 'local orientation y coordinate',
|
|
581
|
-
value: localPose.
|
|
555
|
+
value: localPose.oY,
|
|
582
556
|
})}
|
|
583
557
|
{@render ImmutableField({
|
|
584
558
|
label: 'z',
|
|
585
559
|
ariaLabel: 'local orientation z coordinate',
|
|
586
|
-
value: localPose.
|
|
560
|
+
value: localPose.oZ,
|
|
587
561
|
})}
|
|
588
562
|
{@render ImmutableField({
|
|
589
563
|
label: 'th',
|
|
590
564
|
ariaLabel: 'local orientation theta degrees',
|
|
591
|
-
value: localPose.
|
|
565
|
+
value: localPose.theta,
|
|
592
566
|
})}
|
|
593
567
|
</div>
|
|
594
568
|
{/if}
|
|
@@ -611,6 +585,7 @@
|
|
|
611
585
|
y: box.current.y,
|
|
612
586
|
z: box.current.z,
|
|
613
587
|
}}
|
|
588
|
+
min={0}
|
|
614
589
|
on:change={handleBoxChange}
|
|
615
590
|
/>
|
|
616
591
|
</div>
|
|
@@ -622,6 +597,7 @@
|
|
|
622
597
|
<Slider
|
|
623
598
|
label="r"
|
|
624
599
|
value={sphere.current.r}
|
|
600
|
+
min={0}
|
|
625
601
|
on:change={handleSphereRChange}
|
|
626
602
|
/>
|
|
627
603
|
</div>
|
|
@@ -633,11 +609,13 @@
|
|
|
633
609
|
<Slider
|
|
634
610
|
label="r"
|
|
635
611
|
value={capsule.current.r}
|
|
612
|
+
min={0}
|
|
636
613
|
on:change={handleCapsuleRChange}
|
|
637
614
|
/>
|
|
638
615
|
<Slider
|
|
639
616
|
label="l"
|
|
640
617
|
value={capsule.current.l}
|
|
618
|
+
min={0}
|
|
641
619
|
on:change={handleCapsuleLChange}
|
|
642
620
|
/>
|
|
643
621
|
</div>
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
+
import { Matrix4 } from 'three';
|
|
1
2
|
import { hierarchy, traits } from '../../../../ecs';
|
|
3
|
+
import { createPose, poseToMatrix } from '../../../../transform';
|
|
4
|
+
// OV must be a unit vector — (0.6, 0.8, 0) magnitude 1 — so the matrix
|
|
5
|
+
// round-trip in Details.svelte returns the same components.
|
|
6
|
+
const buildMatrix = () => poseToMatrix(createPose({
|
|
7
|
+
x: 10,
|
|
8
|
+
y: 20,
|
|
9
|
+
z: 30,
|
|
10
|
+
oX: 0.6,
|
|
11
|
+
oY: 0.8,
|
|
12
|
+
oZ: 0,
|
|
13
|
+
theta: 0.4,
|
|
14
|
+
}), new Matrix4());
|
|
2
15
|
export const createEntityFixture = (world) => {
|
|
3
|
-
return world.spawn(...hierarchy.parentTraits('parent_frame'), traits.Name('Test Object'), traits.
|
|
4
|
-
x: 10,
|
|
5
|
-
y: 20,
|
|
6
|
-
z: 30,
|
|
7
|
-
oX: 0.1,
|
|
8
|
-
oY: 0.2,
|
|
9
|
-
oZ: 0.3,
|
|
10
|
-
theta: 0.4,
|
|
11
|
-
}), traits.EditedPose({
|
|
12
|
-
x: 10,
|
|
13
|
-
y: 20,
|
|
14
|
-
z: 30,
|
|
15
|
-
oX: 0.1,
|
|
16
|
-
oY: 0.2,
|
|
17
|
-
oZ: 0.3,
|
|
18
|
-
theta: 0.4,
|
|
19
|
-
}), traits.Box({ x: 0.01, y: 0.02, z: 0.03 }));
|
|
16
|
+
return world.spawn(...hierarchy.parentTraits('parent_frame'), traits.Name('Test Object'), traits.Matrix(buildMatrix()), traits.EditedMatrix(buildMatrix()), traits.Box({ x: 0.01, y: 0.02, z: 0.03 }));
|
|
20
17
|
};
|