Zhen Ye
feat: Continuous object tracking, speed estimation, and overlay syncing
ff50694
APP.core.utils = {};
APP.core.utils.$ = (sel, root = document) => root.querySelector(sel);
APP.core.utils.$$ = (sel, root = document) => Array.from(root.querySelectorAll(sel));
APP.core.utils.clamp = (x, a, b) => Math.min(b, Math.max(a, x));
APP.core.utils.lerp = (a, b, t) => a + (b - a) * t;
APP.core.utils.now = () => performance.now();
APP.core.utils.escapeHtml = function (s) {
return String(s).replace(/[&<>"']/g, m => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[m]));
};
APP.core.utils.canvasToBlob = function (canvas, quality = 0.88) {
return new Promise((resolve, reject) => {
if (!canvas.toBlob) { reject(new Error("Canvas.toBlob not supported")); return; }
canvas.toBlob(blob => {
if (!blob) { reject(new Error("Canvas toBlob failed")); return; }
resolve(blob);
}, "image/jpeg", quality);
});
};
APP.core.utils.normBBox = function (bbox, w, h) {
const [x, y, bw, bh] = bbox;
return {
x: APP.core.utils.clamp(x, 0, w - 1),
y: APP.core.utils.clamp(y, 0, h - 1),
w: APP.core.utils.clamp(bw, 1, w),
h: APP.core.utils.clamp(bh, 1, h)
};
};
APP.core.utils.loadedScripts = new Map();
APP.core.utils.loadScriptOnce = function (key, src) {
return new Promise((resolve, reject) => {
if (APP.core.utils.loadedScripts.get(key) === "loaded") { resolve(); return; }
if (APP.core.utils.loadedScripts.get(key) === "loading") {
const iv = setInterval(() => {
if (APP.core.utils.loadedScripts.get(key) === "loaded") { clearInterval(iv); resolve(); }
if (APP.core.utils.loadedScripts.get(key) === "failed") { clearInterval(iv); reject(new Error("Script failed earlier")); }
}, 50);
return;
}
APP.core.utils.loadedScripts.set(key, "loading");
const s = document.createElement("script");
s.src = src;
s.async = true;
s.onload = () => { APP.core.utils.loadedScripts.set(key, "loaded"); resolve(); };
s.onerror = () => { APP.core.utils.loadedScripts.set(key, "failed"); reject(new Error(`Failed to load ${src}`)); };
document.head.appendChild(s);
});
};