94 lines
3.0 KiB
JavaScript
94 lines
3.0 KiB
JavaScript
export const MOTION_PROVIDER_BROWSER = "browser_camera";
|
|
export const MOTION_PROVIDER_AGENT = "motion_agent";
|
|
export const DEFAULT_MOTION_PROVIDER = MOTION_PROVIDER_BROWSER;
|
|
export const MOTION_PROVIDERS = new Set([
|
|
MOTION_PROVIDER_BROWSER,
|
|
MOTION_PROVIDER_AGENT,
|
|
]);
|
|
export const MOTION_GESTURES = new Set([
|
|
"rotate_left",
|
|
"rotate_right",
|
|
"rotate_up",
|
|
"rotate_down",
|
|
"zoom_in",
|
|
"zoom_out",
|
|
"focus_prev",
|
|
"focus_next",
|
|
"layer_prev",
|
|
"layer_next",
|
|
"confirm",
|
|
]);
|
|
|
|
export function clamp01(value, fallback = 1) {
|
|
const number = Number(value);
|
|
if (!Number.isFinite(number)) return fallback;
|
|
return Math.max(0, Math.min(1, number));
|
|
}
|
|
|
|
export function normalizeMotionProvider(value, fallback = DEFAULT_MOTION_PROVIDER) {
|
|
const normalized = String(value || "").trim();
|
|
if (normalized === "browser") return MOTION_PROVIDER_BROWSER;
|
|
if (normalized === "agent") return MOTION_PROVIDER_AGENT;
|
|
return MOTION_PROVIDERS.has(normalized) ? normalized : fallback;
|
|
}
|
|
|
|
export function normalizeGestureMessage(raw, fallbackSource = "motion-provider") {
|
|
if (!raw || typeof raw !== "object") return null;
|
|
if (raw.type !== "gesture") return null;
|
|
const gesture = String(raw.gesture || "").trim();
|
|
if (!MOTION_GESTURES.has(gesture)) return null;
|
|
return {
|
|
type: "gesture",
|
|
gesture,
|
|
phase: raw.phase || "discrete",
|
|
confidence: clamp01(raw.confidence, 0),
|
|
intensity: clamp01(raw.intensity, 1),
|
|
timestampMs: Number(raw.timestamp_ms || raw.timestampMs || Date.now()),
|
|
seq: Number(raw.seq || 0),
|
|
source: raw.source || fallbackSource,
|
|
mode: raw.mode || "single",
|
|
payload: raw.payload && typeof raw.payload === "object" ? raw.payload : {},
|
|
};
|
|
}
|
|
|
|
export function normalizeSkeletonMessage(raw, fallbackSource = "motion-provider") {
|
|
if (!raw || typeof raw !== "object" || raw.type !== "skeleton") return null;
|
|
const joints = Array.isArray(raw.joints)
|
|
? raw.joints
|
|
.map((joint) => {
|
|
const id = String(joint?.id || "").trim();
|
|
const x = Number(joint?.x);
|
|
const y = Number(joint?.y);
|
|
if (!id || !Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
return {
|
|
id,
|
|
x: clamp01(x, 0),
|
|
y: clamp01(y, 0),
|
|
confidence: clamp01(joint?.confidence, 1),
|
|
};
|
|
})
|
|
.filter(Boolean)
|
|
: [];
|
|
const bones = Array.isArray(raw.bones)
|
|
? raw.bones
|
|
.map((bone) => {
|
|
if (!Array.isArray(bone) || bone.length < 2) return null;
|
|
const from = String(bone[0] || "").trim();
|
|
const to = String(bone[1] || "").trim();
|
|
return from && to ? [from, to] : null;
|
|
})
|
|
.filter(Boolean)
|
|
: [];
|
|
return {
|
|
type: "skeleton",
|
|
timestampMs: Number(raw.timestamp_ms || raw.timestampMs || Date.now()),
|
|
source: raw.source || fallbackSource,
|
|
mode: raw.mode || "single",
|
|
cameraId: raw.camera_id || raw.cameraId || "unknown",
|
|
matchedGesture: raw.matched_gesture || raw.matchedGesture || null,
|
|
confidence: clamp01(raw.confidence, 0),
|
|
joints,
|
|
bones,
|
|
};
|
|
}
|