(function(root) { 'use strict'; const valid = p => p && Number.isFinite(p.x) && Number.isFinite(p.y); const clamp = v => Math.max(0,Math.min(1,v)); function target(faces, hands, previous) { let candidates = hands.filter(h=>h.length===21 && h.every(valid)).map(h=>{ const palm = [0,5,9,13,17].map(i=>h[i]); return {tipo:'mao',x:palm.reduce((s,p)=>s+p.x,0)/5,y:palm.reduce((s,p)=>s+p.y,0)/5}; }); if (!candidates.length) candidates = faces.filter(f=>valid(f) && Number.isFinite(f.w) && Number.isFinite(f.h) && f.w>0 && f.h>0) .sort((a,b)=>b.w*b.h-a.w*a.h).map(f=>({tipo:'rosto',x:f.x+f.w/2,y:f.y+f.h/2})); if (!candidates.length) return null; if (previous && previous.tipo === candidates[0].tipo) candidates.sort((a,b)=> Math.hypot(a.x-previous.x,a.y-previous.y)-Math.hypot(b.x-previous.x,b.y-previous.y)); const t = candidates[0]; t.x=clamp(t.x); t.y=clamp(t.y); return t; } function fit(w,h,vw,vh) { const scale = Math.min(w/vw,h/vh); return {x:Math.max(0,(w-vw*scale)/2),y:Math.max(0,(h-vh*scale)/2),w:vw*scale,h:vh*scale}; } function point(p,r) { return {x:r.x+p.x*r.w,y:r.y+p.y*r.h}; } class FreshTarget { constructor() { this.value=null; this.at=-Infinity; } accept(p,now) { if (!valid(p) || !['mao','rosto'].includes(p.tipo) || p.x<0 || p.x>1 || p.y<0 || p.y>1) return false; this.value={tipo:p.tipo,x:p.x,y:p.y,atividade:p.atividade===true}; this.at=now; return true; } get(now) { return now-this.at <= 350 ? this.value : null; } clear() { this.value=null; this.at=-Infinity; } } const api={target,fit,point,FreshTarget}; root.Rastreamento=api; if(typeof module!=='undefined') module.exports=api; })(typeof globalThis!=='undefined'?globalThis:window);