Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bba2d8d
feat: hover link relationship
mattmacf98 Feb 9, 2026
422fb44
allow specifying index mapping in ui
mattmacf98 Feb 9, 2026
2d0bfa7
move link hovers to a context
mattmacf98 Feb 9, 2026
874b1a9
clean up hover updater types
mattmacf98 Feb 9, 2026
7756b30
move hover components to their own folder
mattmacf98 Feb 9, 2026
a75bc32
update to use the useTrait hook
mattmacf98 Feb 9, 2026
30480fa
lint
mattmacf98 Feb 9, 2026
9e4265c
Merge branch 'main' into APP-15105-create-entity-relationships
mattmacf98 Feb 10, 2026
8d5af6d
stop prop on indexMapping keypress
mattmacf98 Feb 10, 2026
59f3cba
pull AddRelationship secion out of Details
mattmacf98 Feb 10, 2026
1dbc1d3
cleanup
mattmacf98 Feb 10, 2026
a9ba0a7
Merge branch 'main' into APP-15105-create-entity-relationships
mattmacf98 Feb 10, 2026
c92896d
revert package.json removals, pnpm i
mattmacf98 Feb 10, 2026
7167a87
format check
mattmacf98 Feb 10, 2026
690c224
only show relationship button for pcds and arrows for now
mattmacf98 Feb 10, 2026
4969a0a
changeset
mattmacf98 Feb 10, 2026
d2236fe
init
micheal-parks Nov 6, 2025
5c46a77
fix imports
mattmacf98 Feb 10, 2026
8e73598
Merge branch 'main' into APP-15105-create-entity-relationships
mattmacf98 Feb 12, 2026
001e983
use pooled vec3
mattmacf98 Feb 12, 2026
9a16051
address comments
mattmacf98 Feb 12, 2026
56194c7
add hover trait in and add prop for link type
mattmacf98 Feb 12, 2026
4bfe470
clean
mattmacf98 Feb 12, 2026
36e95ea
rename ox -> oX
mattmacf98 Feb 12, 2026
a0f1f64
Merge branch 'main' into APP-15105-create-entity-relationships
mattmacf98 Feb 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/many-humans-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@viamrobotics/motion-tools': minor
---

enable hoverlinking for pcds and poses
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
"@bufbuild/protobuf": "1.10.1",
"@neodrag/svelte": "^2.3.3",
"@tanstack/svelte-query-devtools": "^6.0.2",
"expr-eval": "^2.0.2",
"koota": "0.6.5",
"lodash-es": "4.17.23",
"uuid-tool": "^2.0.3"
Expand Down
65 changes: 32 additions & 33 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

159 changes: 159 additions & 0 deletions src/lib/HoverUpdater.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { Vector3 } from 'three'
import type { Entity } from 'koota'
import { traits } from '$lib/ecs'
import type { IntersectionEvent } from '@threlte/extras'

export interface HoverInfo {
index: number
position: {
x: number
y: number
z: number
}
orientation?: {
x: number
y: number
z: number
}
}

export const getClosestArrow = (positions: Float32Array, point: Vector3): HoverInfo => {
let smallestDistance = Infinity
let index = -1

for (let i = 0; i < positions.length; i += 6) {
const x = positions[i] / 1000
const y = positions[i + 1] / 1000
const z = positions[i + 2] / 1000

const distance = point.distanceToSquared(new Vector3(x, y, z))

if (distance < smallestDistance) {
smallestDistance = distance
index = i
}
}

return {
index: Math.floor(index / 6),
position: {
x: positions[index] / 1000,
y: positions[index + 1] / 1000,
z: positions[index + 2] / 1000,
},
orientation: {
x: positions[index + 3],
y: positions[index + 4],
z: positions[index + 5],
},
}
}

export const getClosestPoint = (positions: Float32Array, point: Vector3): HoverInfo => {
let smallestDistance = Infinity
let index = -1

for (let i = 0; i < positions.length; i += 3) {
const x = positions[i]
const y = positions[i + 1]
const z = positions[i + 2]

const distance = point.distanceToSquared(new Vector3(x, y, z))

if (distance < smallestDistance) {
smallestDistance = distance
index = i
}
}

return {
index: Math.floor(index / 3),
position: {
x: positions[index],
y: positions[index + 1],
z: positions[index + 2],
},
}
}

export const getPointAtIndex = (positions: Float32Array, index: number): HoverInfo | null => {
if (index < 0 || index >= positions.length / 3) {
return null
}
return {
index,
position: {
x: positions[index * 3],
y: positions[index * 3 + 1],
z: positions[index * 3 + 2],
},
}
}
export const getArrowAtIndex = (positions: Float32Array, index: number): HoverInfo | null => {
if (index < 0 || index >= positions.length / 6) {
return null
}
return {
index,
position: {
x: positions[index * 6] / 1000,
y: positions[index * 6 + 1] / 1000,
z: positions[index * 6 + 2] / 1000,
},
orientation: {
x: positions[index * 6 + 3],
y: positions[index * 6 + 4],
z: positions[index * 6 + 5],
},
}
}

export const updateHoverInfo = (
entity: Entity,
hoverEvent: IntersectionEvent<MouseEvent>
): HoverInfo | null => {
const { index, point } = hoverEvent
if (index === -1) {
return null
}

const hoverPosition = new Vector3(point.x, point.y, point.z)

let hoverInfo: HoverInfo | null = null

if (entity.has(traits.Arrows)) {
const closestArrow = getClosestArrow(
entity.get(traits.Positions) as Float32Array,
hoverPosition
)
if (closestArrow) {
hoverInfo = closestArrow
}
} else if (entity.has(traits.Points)) {
const positions = entity.get(traits.BufferGeometry)?.attributes.position.array as Float32Array
const closestPoint = getClosestPoint(positions, hoverPosition)
if (closestPoint) {
hoverInfo = closestPoint
}
}

return hoverInfo
}

export const getLinkedHoverInfo = (index: number, linkedEntity: Entity): HoverInfo | null => {
if (linkedEntity.has(traits.Arrows)) {
const closestArrow = getArrowAtIndex(linkedEntity.get(traits.Positions) as Float32Array, index)
if (closestArrow) {
return closestArrow
}
} else if (linkedEntity.has(traits.Points)) {
const positions = linkedEntity.get(traits.BufferGeometry)?.attributes.position
.array as Float32Array
const closestPoint = getPointAtIndex(positions, index)
if (closestPoint) {
return closestPoint
}
}

return null
}
Loading
Loading