fix: guard hand landmark visualization

This commit is contained in:
Tom Boullay
2026-04-29 09:52:46 +02:00
parent cc78420d9c
commit 28e3ac4c06
6 changed files with 110 additions and 1 deletions
@@ -0,0 +1,77 @@
import { useHandTrackingSnapshot } from "@/hooks/useHandTrackingSnapshot";
const HAND_CONNECTIONS: Array<[number, number]> = [
[0, 1],
[1, 2],
[2, 3],
[3, 4],
[0, 5],
[5, 6],
[6, 7],
[7, 8],
[5, 9],
[9, 10],
[10, 11],
[11, 12],
[9, 13],
[13, 14],
[14, 15],
[15, 16],
[13, 17],
[17, 18],
[18, 19],
[19, 20],
[0, 17],
];
export function HandTrackingVisualizer(): React.JSX.Element | null {
const { hands, status } = useHandTrackingSnapshot();
if (status === "idle" || hands.length === 0) {
return null;
}
return (
<svg className="hand-tracking-visualizer" aria-hidden="true">
{hands.map((hand, handIndex) => {
const landmarks = hand.landmarks ?? [];
if (landmarks.length === 0) return null;
const color = hand.isPinch ? "#facc15" : "#38bdf8";
return (
<g key={`${hand.handedness}-${handIndex}`}>
{HAND_CONNECTIONS.map(([from, to]) => {
const fromPoint = landmarks[from];
const toPoint = landmarks[to];
if (!fromPoint || !toPoint) return null;
return (
<line
key={`${from}-${to}`}
x1={`${(1 - fromPoint.x) * 100}%`}
y1={`${fromPoint.y * 100}%`}
x2={`${(1 - toPoint.x) * 100}%`}
y2={`${toPoint.y * 100}%`}
stroke={color}
strokeWidth="2"
strokeLinecap="round"
/>
);
})}
{landmarks.map((landmark, landmarkIndex) => (
<circle
key={landmarkIndex}
cx={`${(1 - landmark.x) * 100}%`}
cy={`${landmark.y * 100}%`}
r={landmarkIndex === 8 ? 5 : 3}
fill={landmarkIndex === 8 ? "#ffffff" : color}
/>
))}
</g>
);
})}
</svg>
);
}
+10
View File
@@ -418,6 +418,16 @@ canvas {
color: #fca5a5;
}
.hand-tracking-visualizer {
position: fixed;
inset: 0;
z-index: 15;
width: 100vw;
height: 100vh;
pointer-events: none;
filter: drop-shadow(0 0 8px rgba(56, 189, 248, 0.55));
}
/* Editor page */
.editor-container {
position: fixed;
+2
View File
@@ -3,6 +3,7 @@ import { Canvas } from "@react-three/fiber";
import { Crosshair } from "@/components/ui/Crosshair";
import { HandTrackingOverlay } from "@/components/ui/HandTrackingOverlay";
import { HandTrackingProvider } from "@/components/ui/HandTrackingProvider";
import { HandTrackingVisualizer } from "@/components/ui/HandTrackingVisualizer";
import { InteractPrompt } from "@/components/ui/InteractPrompt";
import { DebugPerf } from "@/components/debug/DebugPerf";
import { World } from "@/world/World";
@@ -18,6 +19,7 @@ export function HomePage(): React.JSX.Element {
</Canvas>
<Crosshair />
<InteractPrompt />
<HandTrackingVisualizer />
<HandTrackingOverlay />
</HandTrackingProvider>
);
+7
View File
@@ -1,7 +1,14 @@
export interface HandTrackingLandmark {
x: number;
y: number;
z: number;
}
export interface HandTrackingHand {
x: number;
y: number;
z: number;
landmarks: HandTrackingLandmark[];
handedness: string;
isPinch: boolean;
pinchDistance: number;