34 lines
1.7 KiB
JavaScript
34 lines
1.7 KiB
JavaScript
(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);
|