feat: smooth mocap palm cursor

This commit is contained in:
2026-05-10 18:51:43 +08:00
parent 46a254f142
commit 75bca28191
5 changed files with 256 additions and 52 deletions

View File

@@ -0,0 +1,81 @@
import { describe, expect, test } from 'vitest';
import { parseMocapPacket, resolveMocapPalmCenter } from './useMocapInput';
describe('resolveMocapPalmCenter', () => {
test('优先用手腕和四个 MCP 点加权计算掌心派生点', () => {
const center = resolveMocapPalmCenter([
{ name: 'wrist', x: 0.1, y: 0.2 },
{ name: 'index_mcp', x: 0.3, y: 0.4 },
{ name: 'middle_mcp', x: 0.5, y: 0.6 },
{ name: 'ring_mcp', x: 0.7, y: 0.8 },
{ name: 'pinky_mcp', x: 0.9, y: 1 },
{ name: 'index_finger_tip', x: 1, y: 1 },
]);
expect(center?.x).toBeCloseTo(0.44);
expect(center?.y).toBeCloseTo(0.54);
});
test('可用掌心点少于三个时不返回掌心坐标', () => {
expect(
resolveMocapPalmCenter([
{ name: 'wrist', x: 0.1, y: 0.2 },
{ name: 'index_mcp', x: 0.3, y: 0.4 },
]),
).toBeNull();
});
});
describe('parseMocapPacket', () => {
test('解析手部数据时优先把 primaryHand 定位到掌心而不是腕部或指尖', () => {
const command = parseMocapPacket({
hands: [
{
state: 'open_palm',
x: 0.01,
y: 0.02,
landmarks: [
{ name: 'wrist', x: 0.1, y: 0.2 },
{ name: 'index_mcp', x: 0.3, y: 0.4 },
{ name: 'middle_mcp', x: 0.5, y: 0.6 },
{ name: 'ring_mcp', x: 0.7, y: 0.8 },
{ name: 'pinky_mcp', x: 0.9, y: 1 },
],
},
],
});
expect(command.primaryHand?.x).toBeCloseTo(0.44);
expect(command.primaryHand?.y).toBeCloseTo(0.54);
expect(command.primaryHand).toEqual(
expect.objectContaining({
state: 'open_palm',
source: 'palm_center',
}),
);
});
test('缺少足够掌心关键点时退回 wrist landmark再退回 hand 直出坐标', () => {
const landmarkFallback = parseMocapPacket({
hands: [
{
state: 'grab',
x: 0.9,
y: 0.8,
landmarks: [{ name: 'wrist', x: 0.25, y: 0.75 }],
},
],
});
expect(landmarkFallback.primaryHand).toEqual(
expect.objectContaining({x: 0.25, y: 0.75, source: 'landmark'}),
);
const directFallback = parseMocapPacket({
hands: [{ state: 'grab', x: 0.9, y: 0.8 }],
});
expect(directFallback.primaryHand).toEqual(
expect.objectContaining({x: 0.9, y: 0.8, source: 'direct'}),
);
});
});