SOURCE / PINNED RELEASE
Made of little things.
Body Journal
- Release
- e8049f8da4e2…
- Author-recorded commit
- 61c04bf0e8aa…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6c4554844c7
Archive hash verified: 4369d778da046a8b…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import type { Point3 } from './case.js';
export const handSides = ['left', 'right'] as const;
export const digits = ['thumb', 'index', 'middle', 'ring', 'pinky'] as const;
export type HandSide = typeof handSides[number];
export type Digit = typeof digits[number];
export type FingerId = `${HandSide}-${Digit}`;
/** Normalized illustration bends, not measured joint angles. Missing = open. */
export type FingerPose = Partial<Record<FingerId, Point3>>;
export const digitNames: Record<Digit, string> = { thumb: 'Thumb', index: 'Index finger', middle: 'Middle finger', ring: 'Ring finger', pinky: 'Little finger' };
export const fingerIds = handSides.flatMap(side => digits.map(digit => `${side}-${digit}` as FingerId));
export const bendNames = (digit: Digit): [string, string, string] => digit === 'thumb' ? ['Base', 'Knuckle', 'Tip'] : ['Base', 'Middle', 'Tip'];
export function parseFingerPose(value: unknown): FingerPose {
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid finger pose.');
const result: FingerPose = {};
for (const [id, bends] of Object.entries(value)) {
if (!fingerIds.includes(id as FingerId) || !Array.isArray(bends) || bends.length !== 3 || bends.some(n => typeof n !== 'number' || !Number.isFinite(n) || n < 0 || n > 1)) throw new Error('Invalid finger bend.');
if (bends.some(n => n > 0)) result[id as FingerId] = [...bends] as Point3;
}
return result;
}
export function describeFingers(pose: FingerPose = {}): string[] {
return fingerIds.flatMap(id => {
const [side, digit] = id.split('-') as [HandSide, Digit], values = pose[id];
if (!values?.some(n => n > 0)) return [];
const joints = bendNames(digit).filter((_, i) => values[i] > 0).map(n => n.toLowerCase());
return [`${side} ${digitNames[digit].toLowerCase()} bent at ${joints.join(' / ')}`];
});
}
