release: bump version to 0.50.0
This commit is contained in:
93
frontend/public/earth/js/motion-protocol.js
Normal file
93
frontend/public/earth/js/motion-protocol.js
Normal file
@@ -0,0 +1,93 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user