@rr0/ufoathome 0.142.0 → 0.143.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-embed-scene/rr0-scene.mjs +4 -4
- package/dist-embed-sighting/rr0-sighting.mjs +6 -6
- package/dist-embed-sighting-editor/GhostSearchView-B5XvBQYl.js +1 -0
- package/dist-embed-sighting-editor/rr0-sighting-editor.mjs +8 -8
- package/package.json +1 -1
- package/dist-embed-sighting-editor/GhostSearchView-BiyITIC7.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{P as w,a as W}from"./PictureFetch-X3UpM6JV.js";class T{static SIZE=360;static LIGHT=230;static DARK=80;static STANDS_OUT=12;static GROUND=.025;static WHOLE_GROUND=.08;static GROWN_DRIFT=.02;static INSIDE_WEIGHT=.4;static LINE_LENGTH=12;static LINE_FILL=.2;static STANDING_FULLY=60;static NEAREST=.08;static REACH=.08;static RING=[.04,.08];static SHARE_OF_PEAK=.15;static MAX_PAIRS=6e6;static find(c,o){const a=Math.min(1,T.SIZE/Math.max(c.width,c.height)),s=Math.max(1,Math.round(c.width*a)),i=Math.max(1,Math.round(c.height*a)),r=T.luminanceOf(c,s,i),n=T.blurred(r,s,i,Math.max(4,Math.round(Math.hypot(s,i)*T.GROUND))),e=[],l=[];for(let R=0;R<i;R++)for(let A=0;A<s;A++){const L=R*s+A,M=r[L];M>=T.LIGHT?e.push({x:A,y:R,weight:(M-T.LIGHT+5)/30}):n[L]<T.DARK&&M-n[L]>T.STANDS_OUT&&l.push({x:A,y:R,weight:Math.min(1,(M-n[L])/T.STANDING_FULLY)})}if(l.splice(0,l.length,...T.withoutLines(l,s,i)),e.splice(0,e.length,...T.withoutLines(e,s,i)),l.push(...T.insides(l,e,s,i)),e.length===0||l.length===0)return;const t=T.NEAREST*Math.hypot(s,i),f=T.peak(T.vote(e,l,s,i,t),s,i);if(f.value<=0||f.sharpness<o)return;const u={x:f.x/2,y:f.y/2},{pairs:y,matched:x}=T.pairsAt(u,e,l,r,n,s,i,t);if(y.length===0)return;const p=1/a,d=R=>({...R,x:(R.x+.5)*p-.5,y:(R.y+.5)*p-.5,radius:R.radius*p});return{centre:{x:(u.x+.5)*p-.5,y:(u.y+.5)*p-.5},pairs:y.map(R=>({ghost:d(R.ghost),light:d(R.light),residualPx:R.residualPx*p})),sharpness:f.sharpness,matched:x}}static vote(c,o,a,s,i){const r=new Float32Array(2*a*2*s),n=Math.max(1,Math.ceil(Math.sqrt(c.length*o.length/T.MAX_PAIRS))),e=i*i;for(let l=0;l<c.length;l+=n){const t=c[l];for(let f=0;f<o.length;f+=n){const u=o[f],y=u.x-t.x,x=u.y-t.y;y*y+x*x<e||(r[(t.y+u.y)*2*a+t.x+u.x]+=t.weight*u.weight)}}return r}static peak(c,o,a){const s=2*o,i=2*a,r=T.blurred(c,s,i,3);let n={x:0,y:0,value:0,sharpness:0};const e=T.REACH*Math.hypot(s,i);for(let y=Math.max(0,Math.round(i/2-e));y<Math.min(i,Math.round(i/2+e));y++)for(let x=Math.max(0,Math.round(s/2-e));x<Math.min(s,Math.round(s/2+e));x++){if(Math.hypot(x-s/2,y-i/2)>e)continue;const p=r[y*s+x];p>n.value&&(n={x,y,value:p,sharpness:0})}const l=Math.hypot(s,i),t=T.RING[0]*l,f=T.RING[1]*l;let u=0;for(let y=Math.max(0,Math.floor(n.y-f));y<Math.min(i,Math.ceil(n.y+f));y++)for(let x=Math.max(0,Math.floor(n.x-f));x<Math.min(s,Math.ceil(n.x+f));x++){const p=Math.hypot(x-n.x,y-n.y);p>t&&p<f&&(u=Math.max(u,r[y*s+x]))}return n.sharpness=u>0?n.value/u:1/0,n}static pairsAt(c,o,a,s,i,r,n,e){const l=new Float32Array(r*n);for(const M of o)l[M.y*r+M.x]=M.weight;const t=M=>{const m=Math.round(2*c.x-M.x),I=Math.round(2*c.y-M.y);let P=0;for(let N=-2;N<=2;N++)for(let D=-2;D<=2;D++){const O=m+D,h=I+N;O>=0&&h>=0&&O<r&&h<n&&(P+=l[h*r+O])}return M.weight*P},f=new Float32Array(r*n),u=new Uint8Array(r*n);for(const M of a){if(Math.hypot(M.x-(2*c.x-M.x),M.y-(2*c.y-M.y))<e)continue;const m=t(M);m<=0||(u[M.y*r+M.x]=1,f[M.y*r+M.x]=m)}let y;const x=()=>y??=T.blurred(s,r,n,Math.max(4,Math.round(Math.hypot(r,n)*T.WHOLE_GROUND))),p=new Uint8Array(r*n),d=[];for(let M=0;M<u.length;M++){if(!u[M]||p[M])continue;const m=[],I=[M];for(p[M]=1;I.length>0;){const h=I.pop();m.push(h);const E=h%r,S=(h-E)/r;for(let k=-2;k<=2;k++)for(let v=-2;v<=2;v++){const b=E+v,H=S+k;if(b<0||H<0||b>=r||H>=n)continue;const C=H*r+b;u[C]&&!p[C]&&(p[C]=1,I.push(C))}}if(m.length<3)continue;const P=m.reduce((h,E)=>h+f[E],0),N=T.spotOf(T.whole(m,s,x(),r,n),s,x(),r),D=o.filter(h=>Math.hypot(h.x-(2*c.x-N.x),h.y-(2*c.y-N.y))<=N.radius+2);if(D.length===0)continue;const O=T.spotOf(D.map(h=>h.y*r+h.x),s,i,r);d.push({pair:{ghost:N,light:O,residualPx:Math.hypot(N.x+O.x-2*c.x,N.y+O.y-2*c.y)},vote:P,size:m.length})}const R=d.reduce((M,m)=>Math.max(M,m.vote),0),A=[];let L=0;for(const{pair:M,size:m}of d.filter(I=>I.vote>=T.SHARE_OF_PEAK*R).sort((I,P)=>P.vote-I.vote))L+=m,!A.some(I=>Math.hypot(I.ghost.x-M.ghost.x,I.ghost.y-M.ghost.y)<Math.max(I.ghost.radius,M.ghost.radius))&&A.push(M);return{pairs:A.slice(0,12),matched:L}}static withoutLines(c,o,a){const s=new Int32Array(o*a).fill(-1);c.forEach((n,e)=>s[n.y*o+n.x]=e);const i=new Uint8Array(c.length),r=[];for(let n=0;n<c.length;n++){if(i[n])continue;const e=[n];i[n]=1;let l=1/0,t=-1/0,f=1/0,u=-1/0;for(let x=0;x<e.length;x++){const{x:p,y:d}=c[e[x]];l=Math.min(l,p),t=Math.max(t,p),f=Math.min(f,d),u=Math.max(u,d);for(let R=-1;R<=1;R++)for(let A=-1;A<=1;A++){const L=p+A,M=d+R;if(L<0||M<0||L>=o||M>=a)continue;const m=s[M*o+L];m>=0&&!i[m]&&(i[m]=1,e.push(m))}}const y=e.length/((t-l+1)*(u-f+1));if(!(e.length>=T.LINE_LENGTH&&y<T.LINE_FILL))for(const x of e)r.push(c[x])}return r}static insides(c,o,a,s){const i=new Uint8Array(a*s);for(const t of c)i[t.y*a+t.x]=1;for(const t of o)i[t.y*a+t.x]=2;const r=new Uint8Array(a*s),n=[],e=t=>{i[t]||r[t]||(r[t]=1,n.push(t))};for(let t=0;t<a;t++)e(t),e((s-1)*a+t);for(let t=0;t<s;t++)e(t*a),e(t*a+a-1);for(;n.length>0;){const t=n.pop(),f=t%a,u=(t-f)/a;f>0&&e(t-1),f<a-1&&e(t+1),u>0&&e(t-a),u<s-1&&e(t+a)}const l=[];for(let t=0;t<i.length;t++)!i[t]&&!r[t]&&l.push({x:t%a,y:Math.floor(t/a),weight:T.INSIDE_WEIGHT});return l}static whole(c,o,a,s,i){const r=4*Math.sqrt(c.length/Math.PI)+.06*Math.hypot(s,i);let n=0,e=0;for(const d of c)n+=d%s,e+=Math.floor(d/s);n/=c.length,e/=c.length;const l=d=>o[d]<T.LIGHT&&o[d]-a[d]>T.STANDS_OUT/2,t=new Uint8Array(s*i),f=c.filter(l);for(const d of f)t[d]=1;const u=[...f];for(;f.length>0;){const d=f.pop(),R=d%s,A=(d-R)/s;for(const[L,M]of[[1,0],[-1,0],[0,1],[0,-1]]){const m=R+L,I=A+M;if(m<0||I<0||m>=s||I>=i)continue;const P=I*s+m;t[P]||!l(P)||Math.hypot(m-n,I-e)>r||(t[P]=1,u.push(P),f.push(P))}}if(u.length<c.length)return c;let y=0,x=0;for(const d of u)y+=d%s,x+=Math.floor(d/s);return Math.hypot(y/u.length-n,x/u.length-e)<=Math.sqrt(c.length/Math.PI)+T.GROWN_DRIFT*Math.hypot(s,i)?u:c}static spotOf(c,o,a,s){let i=0,r=0,n=0,e=0,l=0;for(const f of c){const u=Math.max(1,o[f]-a[f]);i+=u,r+=u*(f%s),n+=u*Math.floor(f/s),e=Math.max(e,o[f]),l+=a[f]}const t=l/c.length;return{x:r/i,y:n/i,peak:e,contrast:e-t,background:t,radius:Math.sqrt(c.length/Math.PI),color:[e,e,e]}}static luminanceOf(c,o,a){const s=new Float32Array(o*a),i=new Float32Array(o*a),r=o/c.width,n=a/c.height;for(let e=0;e<c.height;e++){const l=Math.min(a-1,Math.floor(e*n));for(let t=0;t<c.width;t++){const f=(e*c.width+t)*4,u=l*o+Math.min(o-1,Math.floor(t*r));s[u]+=.2126*c.data[f]+.7152*c.data[f+1]+.0722*c.data[f+2],i[u]++}}for(let e=0;e<s.length;e++)s[e]/=Math.max(1,i[e]);return s}static blurred(c,o,a,s){const i=new Float64Array((o+1)*(a+1));for(let n=0;n<a;n++){let e=0;for(let l=0;l<o;l++)e+=c[n*o+l],i[(n+1)*(o+1)+l+1]=i[n*(o+1)+l+1]+e}const r=new Float32Array(o*a);for(let n=0;n<a;n++){const e=Math.max(0,n-s),l=Math.min(a,n+s+1);for(let t=0;t<o;t++){const f=Math.max(0,t-s),u=Math.min(o,t+s+1),y=i[l*(o+1)+u]-i[e*(o+1)+u]-i[l*(o+1)+f]+i[e*(o+1)+f];r[n*o+t]=y/((u-f)*(l-e))}}return r}}class g{static OTHER_SCALES=[-2,-1.8,-1.6,-1.4,-1.3,-1.2,-1.1,-.9,-.8,-.7,-.6,-.5,-.4,-.3,.3,.4,.5,.6,.7,.8];static DARK=80;static LARGE_LIGHT_CONTRAST=80;static DOMINATED=.4;static GRID_STEP=2;static GRID_REACH=.12;static ABOVE_CHANCE=1.5;static GREAT_LIGHTS=3;static LONE_REACH=.15;static GROUND_ALIKE=40;static EVEN_ALIKE=20;static CHANCE_ALONE=.5;static CROPPED=.04;static GLARE_SIZE=2;static GLARE_REACH=2.5;static SATURATED=250;static LOST_IN_GLARE=40;static OUT_OF_GLARE=2.5;static SPOT_GHOST_CONTRAST=40;static SHARP_MIRROR=1.5;static SHAPE_MATCHED=25;static MAX_SPOTS=2e3;static find(c){const o=g.searched(c);return{...o,pairs:o.pairs.map(a=>({...a,light:g.glaringOver(a.light,o.lights)}))}}static searched(c){const{width:o,height:a}=c,s=Math.hypot(o,a),i=g.spotsOf(c),r=i.reduce((h,E)=>Math.max(h,E.peak),0),n=Math.min(235,.9*r),e=g.linesOfLight(c,n),l=i.filter(h=>!e[Math.round(h.y)*o+Math.round(h.x)]),t=l.filter(h=>h.peak>=n&&(h.radius<=.025*s?h.contrast>=40:h.contrast>=g.LARGE_LIGHT_CONTRAST)),f=t.reduce((h,E)=>Math.max(h,E.radius),0),u=l.filter(h=>t.includes(h)?h.radius<=g.DOMINATED*f&&h.contrast>=g.LARGE_LIGHT_CONTRAST:h.background<g.DARK&&h.peak>=2*h.background),y={width:o,height:a,lights:t,spots:l,pairs:[],confidence:"none"},x=()=>g.mirrored(c,t,l);if(t.length===0||u.length===0)return x()??y;const p=.006*s,d=h=>h.reduce((E,S)=>E+Math.max(0,1-S.residualPx/p)**2,0),R=g.GRID_STEP*p,A=g.GRID_REACH*s,L=[];for(let h=a/2-A;h<=a/2+A;h+=R)for(let E=o/2-A;E<=o/2+A;E+=R)L.push({x:E,y:h});const M=(h,E=u)=>{let S;for(const k of[...g.voted(E,t,h,o,a),...h===-1?L:[]]){const v=g.fit(E,t,k,h,p);if(!v)continue;const b=d(v.pairs);(!S||b>S.score)&&(S={...v,scale:h,score:b})}return S};let m=M(-1);for(const h of g.OTHER_SCALES){const E=M(h),S=m?.pairs.length??0;E&&E.pairs.length>=Math.max(3,1.5*S,S+2)&&E.score>(m?.score??0)&&(m=E)}let I;if(m&&m.pairs.length>=2){const h=m.scale,E=[[-1,1],[1,-1],[-1,-1]].map(([k,v])=>M(h,u.map(b=>({...b,x:k<0?o-b.x:b.x,y:v<0?a-b.y:b.y})))?.score??0),S=E.reduce((k,v)=>k+v,0)/E.length;if(m.score>g.ABOVE_CHANCE*S){const k=Math.hypot(m.centre.x-o/2,m.centre.y-a/2)/s;if(I={width:o,height:a,lights:t,spots:l,pairs:m.pairs,centre:m.centre,scale:m.scale,cropped:k>g.CROPPED,confidence:m.pairs.length>=3?"high":"low"},I.confidence==="high")return I}}const P=l.filter(h=>u.includes(h)||h.contrast>=g.LARGE_LIGHT_CONTRAST&&h.peak>=Math.min(g.SATURATED,1.5*h.background)),N=x(),D=[...t,...l.filter(h=>h.peak>=g.SATURATED&&!t.includes(h))],O=()=>g.loneGhost(l,D,P,o,a,p,t);if(N&&N.pairs.length===1){const h=O(),E=S=>Math.hypot(S.centre.x-o/2,S.centre.y-a/2);return h&&E(h)<E(N)?h:N}return N??I??O()??y}static mirrored(c,o,a){const s=T.find(c,g.SHARP_MIRROR);if(!s)return;const{width:i,height:r}=c,n=Math.hypot(i,r),e=g.atAxis(a,o,s.centre,.006*n),l=e.length>=2?[...e,...s.pairs.filter(u=>!e.some(y=>Math.hypot(y.ghost.x-u.ghost.x,y.ghost.y-u.ghost.y)<=u.ghost.radius))]:s.pairs,t=s.centre,f=Math.hypot(t.x-i/2,t.y-r/2)/n;return{width:i,height:r,lights:o,spots:a,pairs:l,centre:t,scale:-1,cropped:f>g.CROPPED,confidence:l.length>=2||s.matched>=g.SHAPE_MATCHED?"high":"low"}}static glaringOver(c,o){let a=c;for(const s of o)s.radius>=g.GLARE_SIZE*c.radius&&s.radius>a.radius&&Math.hypot(s.x-c.x,s.y-c.y)<=g.GLARE_REACH*s.radius&&(a=s);return a}static atAxis(c,o,a,s){const i=[];for(const r of c.filter(n=>n.contrast>=g.SPOT_GHOST_CONTRAST)){const n={x:2*a.x-r.x,y:2*a.y-r.y};let e;for(const l of o){if(!g.canBeGhostOf(r,l))continue;const t=Math.max(0,Math.hypot(l.x-n.x,l.y-n.y)-l.radius);t<=s&&(!e||t<e.beyond)&&(e={light:l,beyond:t})}!e||!g.apart(r,e.light)||i.some(l=>Math.hypot(l.ghost.x-r.x,l.ghost.y-r.y)<Math.max(2,l.ghost.radius,r.radius))||i.push({ghost:r,light:{...e.light,...n},residualPx:e.beyond})}return i.filter(r=>!i.some(n=>n!==r&&n.ghost.radius<r.ghost.radius&&Math.hypot(n.ghost.x-r.ghost.x,n.ghost.y-r.ghost.y)<r.ghost.radius))}static loneGhost(c,o,a,s,i,r,n){const e=Math.hypot(s,i),l=[...o].sort((p,d)=>d.contrast*d.radius**2-p.contrast*p.radius**2).slice(0,g.GREAT_LIGHTS);let t;for(const p of l){const d={x:s-p.x,y:i-p.y},R=a.filter(A=>A!==p&&g.canBeGhostOf(A,p)&&Math.hypot(A.x-d.x,A.y-d.y)<=g.LONE_REACH*e&&g.apart(A,p));for(const A of R){const L=a.filter(I=>I.contrast>=A.contrast&&g.canBeGhostOf(I,p)&&g.groundsAlike(I,A)).length,M=Math.hypot(A.x-d.x,A.y-d.y)+r,m=L*Math.PI*M**2/(s*i);(!t||m<t.chance)&&(t={pair:{ghost:A,light:p,residualPx:0},chance:m})}}if(!t||t.chance>=g.CHANCE_ALONE)return;const{ghost:f,light:u}=t.pair,y={x:(f.x+u.x)/2,y:(f.y+u.y)/2},x=Math.hypot(y.x-s/2,y.y-i/2)/e;return{width:s,height:i,lights:n,spots:c,pairs:[t.pair],centre:y,scale:-1,cropped:x>g.CROPPED,confidence:"low"}}static groundsAlike(c,o){const a=Math.max(g.EVEN_ALIKE,c.spread??0,o.spread??0);return Math.abs(c.background-o.background)<=g.GROUND_ALIKE&&Math.abs((c.spread??0)-(o.spread??0))<=a/2}static lightCells=new WeakMap;static lightsNear(c,o){let a=g.lightCells.get(c);if(!a||a.cell!==o){const i=new Map;for(const r of c){const n=g.cellKey(Math.floor(r.x/o),Math.floor(r.y/o)),e=i.get(n);e?e.push(r):i.set(n,[r])}a={cell:o,cells:i},g.lightCells.set(c,a)}const{cells:s}=a;return(i,r)=>{const n=Math.floor(i/o),e=Math.floor(r/o),l=[];for(let t=-1;t<=1;t++)for(let f=-1;f<=1;f++){const u=s.get(g.cellKey(n+t,e+f));u&&l.push(...u)}return l}}static cellKey(c,o){return(c+5e4)*1e5+o+5e4}static linesOfLight(c,o){const{width:a,height:s,data:i}=c,r=[];for(let e=0;e<s;e++)for(let l=0;l<a;l++){const t=(e*a+l)*4;.2126*i[t]+.7152*i[t+1]+.0722*i[t+2]>=o&&r.push({x:l,y:e})}const n=new Uint8Array(a*s);for(const e of r)n[e.y*a+e.x]=1;for(const e of T.withoutLines(r,a,s))n[e.y*a+e.x]=0;return n}static apart(c,o){return Math.hypot(c.x-o.x,c.y-o.y)>=Math.max(4*c.radius,g.OUT_OF_GLARE*o.radius)}static canBeGhostOf(c,o){return c!==o&&(c.peak<o.peak-5||c.radius<=g.DOMINATED*o.radius)}static voted(c,o,a,s,i){const r=.01*Math.hypot(s,i),n=new Map;for(const l of c)for(const t of o){if(!g.canBeGhostOf(l,t))continue;const f=(l.x-a*t.x)/(1-a),u=(l.y-a*t.y)/(1-a);if(f<-.25*s||f>1.25*s||u<-.25*i||u>1.25*i||!g.apart(l,t))continue;const y=`${Math.floor(f/r)},${Math.floor(u/r)}`,x=n.get(y)??{count:0,x:0,y:0};x.count++,x.x+=f,x.y+=u,n.set(y,x)}const e=[...n.entries()].map(([l,t])=>{const[f,u]=l.split(",").map(Number);let y=0,x=0,p=0;for(let d=-1;d<=1;d++)for(let R=-1;R<=1;R++){const A=n.get(`${f+d},${u+R}`);A&&(y+=A.count,x+=A.x,p+=A.y)}return{count:y,centre:{x:x/y,y:p/y},own:t.count}});return e.sort((l,t)=>t.count-l.count||t.own-l.own),e.filter(l=>l.count>=1).slice(0,60).map(l=>l.centre)}static fit(c,o,a,s,i){let r=a,n=[];const e=g.lightsNear(o,3*i);for(const t of[3,2,1,1]){const f=[];for(const y of c){const x=r.x+(y.x-r.x)/s,p=r.y+(y.y-r.y)/s;let d,R=1/0;for(const A of e(x,p)){if(!g.canBeGhostOf(y,A))continue;const L=Math.hypot(A.x-x,A.y-p);L<R&&(R=L,d=A)}d&&R<=t*i&&g.apart(y,d)&&f.push({ghost:y,light:d,residualPx:R*Math.abs(s)})}const u=new Map;for(const y of f){const x=u.get(y.light);(!x||y.residualPx<x.residualPx)&&u.set(y.light,y)}if(n=[...u.values()],n.length===0)return;r={x:n.reduce((y,x)=>y+(x.ghost.x-s*x.light.x)/(1-s),0)/n.length,y:n.reduce((y,x)=>y+(x.ghost.y-s*x.light.y)/(1-s),0)/n.length}}for(const t of n)t.residualPx=Math.hypot(t.ghost.x-(r.x+s*(t.light.x-r.x)),t.ghost.y-(r.y+s*(t.light.y-r.y)));const l=Math.sqrt(n.reduce((t,f)=>t+f.residualPx**2,0)/n.length);return{pairs:n,centre:r,error:l}}static spotsOf(c){const{width:o,height:a,data:s}=c,i=new Float32Array(o*a);for(let t=0;t<o*a;t++)i[t]=.2126*s[t*4]+.7152*s[t*4+1]+.0722*s[t*4+2];const r=Math.max(2,Math.round(Math.hypot(o,a)/500)),n=3*r,e=[];for(let t=n;t<a-n;t++)for(let f=n;f<o-n;f++){const u=i[t*o+f];if(u<40)continue;let y=!0;for(let O=-r;O<=r&&y;O++)for(let h=-r;h<=r;h++){const E=i[(t+O)*o+f+h];if(E>u||E===u&&(O<0||O===0&&h<0)){y=!1;break}}if(!y)continue;let x=-1/0,p=0,d=n,R=0;for(const O of[n,2*n,4*n,8*n,16*n]){if(f<O||t<O||f>=o-O||t>=a-O||O>4*n&&(u<g.SATURATED||x>=g.LOST_IN_GLARE))break;const h=[];for(let S=0;S<32;S++){const k=S/32*2*Math.PI;h.push(i[Math.round(t+O*Math.sin(k))*o+Math.round(f+O*Math.cos(k))])}h.sort((S,k)=>S-k);const E=h[16];u-E>x+5&&(x=u-E,p=E,d=O,R=h[24]-h[8])}if(x<20)continue;const A=p+x/2;let L=0,M=0,m=0,I=0,P=0,N=0,D=0;for(let O=-d+1;O<d;O++)for(let h=-d+1;h<d;h++){const E=(t+O)*o+f+h;if(i[E]<A)continue;L++,M+=s[E*4],m+=s[E*4+1],I+=s[E*4+2];const S=i[E]-A+1;P+=S,N+=S*h,D+=S*O}e.push({x:f+N/P,y:t+D/P,peak:u,contrast:x,background:p,radius:Math.sqrt(L/Math.PI),color:[M/L,m/L,I/L],spread:R})}e.sort((t,f)=>f.contrast-t.contrast);const l=[];for(const t of e){if(l.length>=g.MAX_SPOTS)break;l.some(f=>f.peak>=t.peak&&Math.hypot(f.x-t.x,f.y-t.y)<Math.max(f.radius,r))||l.push(t)}return l}}class U{constructor(c,o,a){this.canvas=c,this.status=o,this.texts=a}static ANALYSIS_SIZE=1600;async search(c){this.status.textContent=this.texts.searching,this.canvas.hidden=!0;let o;try{o=await U.load(c)}catch{this.status.textContent="";return}const a=Math.min(1,U.ANALYSIS_SIZE/Math.max(o.naturalWidth,o.naturalHeight)),s=Math.round(o.naturalWidth*a),i=Math.round(o.naturalHeight*a),r=document.createElement("canvas");r.width=s,r.height=i;const n=r.getContext("2d",{willReadFrequently:!0});n.drawImage(o,0,0,s,i);const e=n.getImageData(0,0,s,i);await new Promise(t=>setTimeout(t,0));const l=g.find(e);return this.draw(o,l),this.status.textContent=this.sentence(l),l}sentence(c){const o=String(c.pairs.length);if(c.confidence==="none")return this.texts.none;const a=c.confidence==="low"?this.texts.low.replace("{count}",o):c.scale===-1?this.texts.found.replace("{count}",o):this.texts.foundScaled.replace("{count}",o).replace("{scale}",(c.scale??0).toLocaleString(void 0,{maximumFractionDigits:2}));return c.cropped?`${a} ${this.texts.cropped}`:a}draw(c,o){const a=this.canvas,s=window.devicePixelRatio||1,i=Math.max(200,a.parentElement?.clientWidth??320),r=i/o.width;a.style.width=`${i}px`,a.style.height=`${Math.round(o.height*r)}px`,a.width=Math.round(i*s),a.height=Math.round(o.height*r*s);const n=a.getContext("2d");n.setTransform(s,0,0,s,0,0),n.drawImage(c,0,0,i,o.height*r);const e=f=>[f.x*r,f.y*r],l=(f,u,y,x=[])=>{n.setLineDash(x),n.lineWidth=y+2,n.strokeStyle="rgba(0, 0, 0, 0.6)",f(),n.lineWidth=y,n.strokeStyle=u,f(),n.setLineDash([])},t=(f,u)=>()=>{const[y,x]=e(f);n.beginPath(),n.arc(y,x,u,0,2*Math.PI),n.stroke()};for(const f of o.pairs)l(()=>{const[u,y]=e(f.ghost),[x,p]=e(f.light);n.beginPath(),n.moveTo(u,y),n.lineTo(x,p),n.stroke()},"rgba(255, 255, 255, 0.85)",1,[4,4]),l(t(f.light,6),"#ffffff",1.5),l(t(f.ghost,Math.max(8,f.ghost.radius*r)),"#ffd23f",2);if(o.centre){const[f,u]=e(o.centre);if(l(()=>{n.beginPath(),n.moveTo(f-7,u),n.lineTo(f+7,u),n.moveTo(f,u-7),n.lineTo(f,u+7),n.stroke()},"#ffd23f",2),o.cropped){const[y,x]=e({x:o.width/2,y:o.height/2});l(()=>{n.beginPath(),n.moveTo(y-5,x-5),n.lineTo(y+5,x+5),n.moveTo(y+5,x-5),n.lineTo(y-5,x+5),n.stroke()},"rgba(255, 255, 255, 0.7)",1)}}a.hidden=!1}static async load(c){const o=URL.createObjectURL(await w.blob(c));try{const a=new Image;return a.src=o,await a.decode().catch(()=>{throw new W(c,{kind:"notPicture"})}),a}finally{URL.revokeObjectURL(o)}}}export{U as GhostSearchView};
|