From 99366543744f7fa5ba6525d5eccf583dab58f650 Mon Sep 17 00:00:00 2001 From: Matthew MacFarquhar Date: Wed, 27 May 2026 13:56:13 -0400 Subject: [PATCH] lint & changeset --- .changeset/rare-aliens-visit.md | 5 +++ src/lib/plugins/Selection/Ellipse.svelte | 39 +++++++++++-------- src/lib/plugins/Selection/Lasso.svelte | 39 +++++++++++-------- src/lib/plugins/Selection/traits.ts | 2 + .../selectDemoPage/SelectionOverlay.svelte | 11 +++++- src/routes/select/+page.svelte | 9 +++++ 6 files changed, 72 insertions(+), 33 deletions(-) create mode 100644 .changeset/rare-aliens-visit.md diff --git a/.changeset/rare-aliens-visit.md b/.changeset/rare-aliens-visit.md new file mode 100644 index 000000000..895b53d61 --- /dev/null +++ b/.changeset/rare-aliens-visit.md @@ -0,0 +1,5 @@ +--- +'@viamrobotics/motion-tools': minor +--- + +feat: have select plugin emit multiple entities per selection (one per source entity) diff --git a/src/lib/plugins/Selection/Ellipse.svelte b/src/lib/plugins/Selection/Ellipse.svelte index b9ee0888f..b36d4dfdd 100644 --- a/src/lib/plugins/Selection/Ellipse.svelte +++ b/src/lib/plugins/Selection/Ellipse.svelte @@ -168,7 +168,7 @@ max.set(ellipseBox.maxX, ellipseBox.maxY, Number.POSITIVE_INFINITY) box3.set(min, max) - const enclosedPoints: number[] = [] + const enclosedPoints: Record = {} for (const pointsEntity of world.query( traits.Points, @@ -176,8 +176,9 @@ Not(selectionTraits.SelectionEnclosedPoints) )) { const geometry = pointsEntity.get(traits.BufferGeometry) + const name = pointsEntity.get(traits.Name) - if (!geometry) return + if (!geometry || !name) return const points = scene.getObjectByName(pointsEntity as unknown as string) @@ -202,7 +203,8 @@ getTriangleFromIndex(i, indices, positions, triangle) if (triangle.containsPoint(point)) { - enclosedPoints.push(point.x, point.y, point.z) + enclosedPoints[name] ??= [] + enclosedPoints[name].push(point.x, point.y, point.z) } } } @@ -211,19 +213,24 @@ } as ShapecastCallbacks) } - const ellipseResultGeometry = createBufferGeometry(new Float32Array(enclosedPoints)) - - world.spawn( - traits.Name('Ellipse result'), - traits.BufferGeometry(ellipseResultGeometry), - traits.Color({ r: 1, g: 0, b: 0 }), - traits.RenderOrder(999), - traits.Material({ depthTest: false }), - traits.Points, - traits.Removable, - selectionTraits.SelectionEnclosedPoints, - selectionTraits.PointsCapturedBy(ellipse) - ) + const selectionInstanceId = crypto.randomUUID() + for (const [name, points] of Object.entries(enclosedPoints)) { + const ellipseResultGeometry = createBufferGeometry(new Float32Array(points)) + + world.spawn( + traits.Name('Ellipse result'), + traits.BufferGeometry(ellipseResultGeometry), + traits.Color({ r: 1, g: 0, b: 0 }), + traits.RenderOrder(999), + traits.Material({ depthTest: false }), + traits.Points, + traits.Removable, + selectionTraits.SelectionEnclosedPoints, + selectionTraits.PointsCapturedBy(ellipse), + selectionTraits.SelectedFrom(name), + selectionTraits.SelectionInstance(selectionInstanceId) + ) + } } const onkeydown = (event: KeyboardEvent) => { diff --git a/src/lib/plugins/Selection/Lasso.svelte b/src/lib/plugins/Selection/Lasso.svelte index 3a46de681..f0d232b9c 100644 --- a/src/lib/plugins/Selection/Lasso.svelte +++ b/src/lib/plugins/Selection/Lasso.svelte @@ -149,7 +149,7 @@ max.set(lassoBox.maxX, lassoBox.maxY, Number.POSITIVE_INFINITY) box3.set(min, max) - const enclosedPoints: number[] = [] + const enclosedPoints: Record = {} for (const pointsEntity of world.query( traits.Points, @@ -157,8 +157,9 @@ Not(selectionTraits.SelectionEnclosedPoints) )) { const geometry = pointsEntity.get(traits.BufferGeometry) + const name = pointsEntity.get(traits.Name) - if (!geometry) return + if (!geometry || !name) return const points = scene.getObjectByName(pointsEntity as unknown as string) @@ -183,7 +184,8 @@ getTriangleFromIndex(i, indices, positions, triangle) if (triangle.containsPoint(point)) { - enclosedPoints.push(point.x, point.y, point.z) + enclosedPoints[name] ??= [] + enclosedPoints[name].push(point.x, point.y, point.z) } } } @@ -192,19 +194,24 @@ } as ShapecastCallbacks) } - const lassoResultGeometry = createBufferGeometry(new Float32Array(enclosedPoints)) - - world.spawn( - traits.Name('Lasso result'), - traits.BufferGeometry(lassoResultGeometry), - traits.Color({ r: 1, g: 0, b: 0 }), - traits.RenderOrder(999), - traits.Material({ depthTest: false }), - traits.Points, - traits.Removable, - selectionTraits.SelectionEnclosedPoints, - selectionTraits.PointsCapturedBy(lasso) - ) + const selectionInstanceId = crypto.randomUUID() + for (const [name, points] of Object.entries(enclosedPoints)) { + const lassoResultGeometry = createBufferGeometry(new Float32Array(points)) + + world.spawn( + traits.Name('Lasso result'), + traits.BufferGeometry(lassoResultGeometry), + traits.Color({ r: 1, g: 0, b: 0 }), + traits.RenderOrder(999), + traits.Material({ depthTest: false }), + traits.Points, + traits.Removable, + selectionTraits.SelectionEnclosedPoints, + selectionTraits.PointsCapturedBy(lasso), + selectionTraits.SelectedFrom(name), + selectionTraits.SelectionInstance(selectionInstanceId) + ) + } } const onkeydown = (event: KeyboardEvent) => { diff --git a/src/lib/plugins/Selection/traits.ts b/src/lib/plugins/Selection/traits.ts index 39acdfbc3..263e6d14d 100644 --- a/src/lib/plugins/Selection/traits.ts +++ b/src/lib/plugins/Selection/traits.ts @@ -3,6 +3,8 @@ import { relation, trait } from 'koota' export const Lasso = trait(() => true) export const Ellipse = trait(() => true) export const SelectionEnclosedPoints = trait(() => true) +export const SelectedFrom = trait(() => '') +export const SelectionInstance = trait(() => '') /** * Captured points are removable, so we want to also destroy diff --git a/src/routes/lib/components/selectDemoPage/SelectionOverlay.svelte b/src/routes/lib/components/selectDemoPage/SelectionOverlay.svelte index a15830dad..42f83aaee 100644 --- a/src/routes/lib/components/selectDemoPage/SelectionOverlay.svelte +++ b/src/routes/lib/components/selectDemoPage/SelectionOverlay.svelte @@ -7,16 +7,25 @@ import { FloatingPanel } from '$lib' import { traits } from '$lib/ecs' import { useSelectionPlugin } from '$lib/plugins' + import { SelectedFrom, SelectionInstance } from '$lib/plugins/Selection/traits' const { dom } = useThrelte() const selectionCtx = useSelectionPlugin() - const rect = new ElementRect(() => dom) $effect(() => { const entity = selectionCtx.current.at(-1) if (entity) { entity.set(traits.Color, { r: 0, g: 1, b: 0 }) + const selectionInstanceId = entity.get(SelectionInstance) + const selectionInstanceEntities = selectionCtx.current.filter( + (entity) => entity.get(SelectionInstance) === selectionInstanceId + ) + for (const selectionEntity of selectionInstanceEntities) { + const name = selectionEntity.get(traits.Name) + const selectedFrom = selectionEntity.get(SelectedFrom) + selectionEntity.set(traits.Name, `${name} (selected from ${selectedFrom})`) + } } }) diff --git a/src/routes/select/+page.svelte b/src/routes/select/+page.svelte index e3fcd5b28..fcb1bb2c3 100644 --- a/src/routes/select/+page.svelte +++ b/src/routes/select/+page.svelte @@ -12,6 +12,15 @@ {#await createRandomPcdBinary(10_000, 1) then data} +{/await} + +{#await createRandomPcdBinary(1_000, 1) then data} +