fix: guard hand landmark visualization
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user