/* Site DTF — Reading a finished sheet from its alpha channel: pieces, gaps, waste. Extracted verbatim from the single inline script in web/index.html. Loaded as classic scripts in the order listed there: they share one global scope and run top to bottom, exactly as the original did. */ // ── Análise real da folha: lê o canal alfa, separa cada ilha de arte e mede. // Grade de 1 mm. Só roda em arquivo que o navegador abre; PDF precisa do servidor. const MM=0.1; // grade fina: 1 mm const CELULAS_MAX=1200000; // teto de custo da análise // Folha grande vira grade mais grossa. Sem isso, 57 x 480 cm dá 2,7 milhões de // células e o navegador congela antes de terminar. function passoDe(larguraCm, alturaCm){ const fino=(larguraCm/MM)*(alturaCm/MM); return fino<=CELULAS_MAX ? MM : MM*Math.sqrt(fino/CELULAS_MAX); } // Dilata a máscara em r células · usada para agrupar por distância real, não por caixa function inchar(bits,cw,ch,r){ const a=new Uint8Array(cw*ch), b=new Uint8Array(cw*ch); for(let y=0;y=0) continue; const id=n++; let ini=0,fim=0; fila[fim++]=p0; marca[p0]=id; while(ini=cw||ny>=ch) continue; const q=ny*cw+nx; if(bits[q]&&marca[q]<0){ marca[q]=id; fila[fim++]=q; } } } } return {marca,n}; } // ── O que a folha permite medir, e só isso. // Peça = pixels a menos de 2 mm entre si · letras de uma palavra ficam juntas. // Encostadas = peças que só se unem quando a folga chega a 6 mm. const R_PECA=1, R_PERTO=2; // peça: 2 mm · aviso de encostadas: 4 mm function analisarFolha(img, larguraCm){ const alturaCmEst=larguraCm*img.height/img.width; const passo=passoDe(larguraCm, alturaCmEst); const cw=Math.max(8,Math.round(larguraCm/passo)); const ch=Math.max(8,Math.round(cw*img.height/img.width)); const cv=document.createElement('canvas'); cv.width=cw; cv.height=ch; const ctx=cv.getContext('2d',{willReadFrequently:true}); ctx.clearRect(0,0,cw,ch); ctx.drawImage(img,0,0,cw,ch); const d=ctx.getImageData(0,0,cw,ch).data; const bits=new Uint8Array(cw*ch); let opacos=0; for(let i=0,j=3;i10){ bits[i]=1; opacos++; } // peças: agrupa pela distância real entre pixels const gp=grupos(inchar(bits,cw,ch,R_PECA), cw, ch); const caixas=[]; for(let p=0;pc.x1)c.x1=x; if(yc.y1)c.y1=y; c.area++; } const pecasTodas=caixas.filter(Boolean); const limRes=Math.max(1, Math.round(0.2/passo)); // 2 mm na grade em uso const residuos=pecasTodas.filter(o=>(o.x1-o.x0)!((o.x1-o.x0){ if(set.size>1) encostadas+=set.size; }); return { dpi: Math.round(img.width/(larguraCm/2.54)), artes: artes.length, residuos: residuos.length, encostadas, fundoChapado: opacos/(cw*ch) > 0.985, aproveitamento: Math.max(1,Math.min(100,Math.round(opacos/(cw*ch)*100))), alturaCm: ch*passo, grade: +(passo*10).toFixed(1) // mm por célula analisada }; }