@wafertools/wafermap 0.29.0 → 0.30.1
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/AGENTS.md +26 -17
- package/CHANGELOG.md +410 -0
- package/README.md +6 -6
- package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
- package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
- package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
- package/dist/packages/canvas-adapter/charts/capability.js +1 -1
- package/dist/packages/canvas-adapter/charts/correlation.js +2 -2
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +1 -0
- package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
- package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
- package/dist/packages/canvas-adapter/charts/trend.js +1 -1
- package/dist/packages/canvas-adapter/deprecated.d.ts +7 -0
- package/dist/packages/canvas-adapter/deprecated.js +1 -0
- package/dist/packages/canvas-adapter/dieList.d.ts +1 -3
- package/dist/packages/canvas-adapter/dieList.js +7 -7
- package/dist/packages/canvas-adapter/exportName.d.ts +68 -0
- package/dist/packages/canvas-adapter/exportName.js +1 -0
- package/dist/packages/canvas-adapter/icons.js +2 -2
- package/dist/packages/canvas-adapter/index.d.ts +2 -3
- package/dist/packages/canvas-adapter/index.js +1 -1
- package/dist/packages/canvas-adapter/insightsTab.d.ts +0 -1
- package/dist/packages/canvas-adapter/insightsTab.js +1 -1
- package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +23 -40
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +96 -104
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +31 -13
- package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
- package/dist/packages/canvas-adapter/toCanvas.d.ts +20 -12
- package/dist/packages/canvas-adapter/toCanvas.js +1 -1
- package/dist/packages/canvas-adapter/toolbar.d.ts +6 -0
- package/dist/packages/canvas-adapter/toolbar.js +2 -2
- package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.js +26 -15
- package/dist/packages/canvas-adapter/version.d.ts +2 -2
- package/dist/packages/canvas-adapter/version.js +1 -1
- package/dist/packages/canvas-adapter/warnings.js +1 -1
- package/dist/packages/core/deprecated.d.ts +60 -0
- package/dist/packages/core/deprecated.js +1 -0
- package/dist/packages/core/index.d.ts +1 -3
- package/dist/packages/core/index.js +1 -1
- package/dist/packages/core/passBins.d.ts +33 -0
- package/dist/packages/core/passBins.js +1 -0
- package/dist/packages/core/utils.d.ts +12 -0
- package/dist/packages/core/utils.js +1 -1
- package/dist/packages/core/waferLabel.d.ts +35 -0
- package/dist/packages/core/waferLabel.js +1 -0
- package/dist/packages/renderer/binColors.d.ts +105 -13
- package/dist/packages/renderer/binColors.js +1 -1
- package/dist/packages/renderer/buildView.d.ts +8 -9
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.d.ts +76 -17
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/renderer/colorMap.d.ts +4 -3
- package/dist/packages/renderer/colorSchemes.d.ts +8 -6
- package/dist/packages/renderer/deprecate.d.ts +31 -0
- package/dist/packages/renderer/deprecate.js +1 -0
- package/dist/packages/renderer/deprecated.d.ts +45 -0
- package/dist/packages/renderer/deprecated.js +1 -0
- package/dist/packages/renderer/index.d.ts +4 -4
- package/dist/packages/renderer/index.js +1 -1
- package/dist/packages/stats/analyzeWaferLot.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/binPareto.d.ts +6 -1
- package/dist/packages/stats/capability.d.ts +16 -1
- package/dist/packages/stats/capability.js +1 -1
- package/dist/packages/stats/clusterDetection.d.ts +0 -2
- package/dist/packages/stats/deprecated.d.ts +82 -0
- package/dist/packages/stats/deprecated.js +1 -0
- package/dist/packages/stats/index.d.ts +5 -14
- package/dist/packages/stats/index.js +1 -1
- package/dist/packages/stats/normalizeInput.d.ts +10 -0
- package/dist/packages/stats/normalizeInput.js +1 -0
- package/dist/packages/stats/regions.d.ts +9 -2
- package/dist/packages/stats/regions.js +1 -1
- package/dist/packages/stats/renderFindingsReport.js +1 -1
- package/dist/packages/stats/renderSummaryReport.d.ts +42 -1
- package/dist/packages/stats/renderSummaryReport.js +34 -34
- package/dist/packages/stats/reportHtml.d.ts +0 -1
- package/dist/packages/stats/reportHtml.js +10 -10
- package/dist/packages/stats/summaryFigures.d.ts +47 -0
- package/dist/packages/stats/summaryFigures.js +1 -0
- package/dist/packages/stats/types.d.ts +118 -43
- package/dist/packages/stats/yield.d.ts +12 -6
- package/dist/packages/stats/yield.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hasPosition as se}from"../core/dies.js";import{getReticleCell as oe}from"../core/reticle.js";import{metadataCategoricalValue as re,metadataDisplayValue as le}from"../core/metadata.js";import{affineRotation as mt,affineMirror as Lt,affineCompose as st,affinePoint as Y,affineVector as pt,affineSwapsAxes as ce}from"../core/transforms.js";import{contrastTextColor as G,SPEC_PASS_FILL as yt,SPEC_FAIL_LOW as ue,SPEC_FAIL_HIGH as gt}from"./colorMap.js";import{resolveValueColorFn as fe}from"./colorSchemes.js";import{resolveBinColors as de,binColorsCover as he}from"./binColors.js";import{NO_DATA_FILL as j}from"./colorMap.js";import{diePassStatus as me}from"../core/dies.js";import{getDieTestValue as R,getTestPassStatus as Q,isParametricTest as bt}from"./buildWaferMap.js";import{fmt as xt,fmtColorbarAxis as Mt,fmtAggregationMethod as Pt}from"./fmt.js";import{metadataValueColor as pe}from"./colorMap.js";import{compareNatural as ye,clamp01 as Rt}from"../core/utils.js";import{prettyKey as Nt}from"../core/utils.js";export function resolveTestNumber(t,e){return e?.length?{testNumber:(findTestDef(e,t)??e[0]).testNumber}:{testNumber:t}}export function collectMetadataValues(t,e){const a=new Set;for(const n of t){if(n.partial||n.edgeExcluded)continue;const s=ot(n,e);s!==void 0&&a.add(s)}return[...a].sort(ye)}export function findTestDef(t,e){return t?.find(a=>a.testNumber===e)}export function getUniqueTestNumbers(t){return[...new Set(t.flatMap(e=>e.testValues?Object.keys(e.testValues).map(Number):[]))].sort((e,a)=>e-a)}const ge="#d3d6db",Ot="#e8e9ea",be="#aab0ba";function ot(t,e){return e?re(t.metadata?.[e]):void 0}export function classifySpec(t,e){return t===void 0?null:e?.limitLow!==void 0&&t<e.limitLow?"failLow":e?.limitHigh!==void 0&&t>e.limitHigh?"failHigh":"pass"}function xe(t,e,a){const{x:n,y:s}=t.center,i=mt(t.orientation,n,s),o=st(Lt(e?.x??!1,e?.y??!1,n,s),mt(t.orientation,n,s)),l=st(Lt(a?.flipX??!1,a?.flipY??!1,n,s),mt(a?.rotation??0,n,s));return{bakedToScreen:l,physicalToScreen:st(l,i),gridToScreen:st(l,o)}}function z(t,e=!1){return{points:[t],closed:e}}function Me(t,e){const{center:a,radius:n,notch:s}=t;let i=a.x+n*Math.cos(e),o=a.y+n*Math.sin(e);if(!s)return{x:i,y:o};const l=Math.sqrt(n**2-(s.length/2)**2),f=s.length/2,y=i-a.x,p=o-a.y;return s.type==="bottom"&&p<-l?(o=a.y-l,i=a.x+Math.max(-f,Math.min(f,y))):s.type==="top"&&p>l?(o=a.y+l,i=a.x+Math.max(-f,Math.min(f,y))):s.type==="left"&&y<-l?(i=a.x-l,o=a.y+Math.max(-f,Math.min(f,p))):s.type==="right"&&y>l&&(i=a.x+l,o=a.y+Math.max(-f,Math.min(f,p))),{x:i,y:o}}function Ht(t,e){return t.map(e).join(" / ")}function Se(t,e=256){const a=Array.from({length:e},(n,s)=>t(s/(e-1)));return n=>a[Math.min(e-1,Math.max(0,Math.round(n*(e-1))))]}function ke(t,e){const a=Math.max(1,Math.min(t.width,t.height)),n=t.width/Math.max(e.length,1);return Math.max(8,Math.min(16,Math.round(Math.min(a*.55,n*1.8))))}function ve(t,e,a){if(e?.length){const n=e.flatMap(s=>{const i=s.testNumber;if(!bt(s)){const f=Q(t,i,s);return f===void 0?[]:[{key:i,label:s.name,value:f?"Pass":"Fail"}]}const o=R(t,i);if(o===void 0)return[];const l=t.testPass?.[i]===!1?" <i>(recorded fail)</i>":"";return[{key:i,label:s.name,value:`${xt(o,s.unit,a)}${l}`}]});if(n.length)return n}return t.testValues&&Object.keys(t.testValues).length>0?Object.entries(t.testValues).map(([n,s])=>({key:Number(n),label:`Test ${n}`,value:xt(s,void 0,a)})):t.testPass&&Object.keys(t.testPass).length>0?Object.entries(t.testPass).map(([n,s])=>({key:Number(n),label:`Test ${n}`,value:s?"Pass":"Fail"})):[]}export function buildHoverText(t,e,a={}){const{testDefs:n,hbinDefs:s,sbinDefs:i,fallbackFormat:o,aggrMethod:l,lotSize:f,activeTest:y,reticleConfig:p,metadataFields:$}=a,D=s?new Map(s.map(u=>[u.bin,u])):null,I=i?new Map(i.map(u=>[u.bin,u])):null,g=[`Die (${t.x}, ${t.y})`];if(p&&se(t)){const u=oe(t,p);g.push(`Reticle (${u.column}, ${u.row})`)}if(e==="stackedValues"){const u=R(t,0);if(u!==void 0){const d=n?.[0],h=d?.testNumber,S=d?.name??(h!=null?`Test ${h}`:"Value"),m=l?` (${l})`:"";g.push(`${S}${m}: ${xt(u,d?.unit,o)}`)}}else if(e==="stackedBins"||e==="stackedSoftBins"){const u=R(t,0),d=e==="stackedSoftBins"?t.sbin:t.hbin;if(u!==void 0){const h=e==="stackedSoftBins"?I:D,S=d!==void 0?h?.get(d)?.name?`${d} \xB7 ${h.get(d).name}`:`Bin ${d}`:"Bin";let m;if(l==="percent")m=`${u.toFixed(0)}%`;else{const L=f?` (${(u/f*100).toFixed(0)}% of stacked wafers)`:"";m=`${u}${L}`}const M=l?` [${Pt(l)}]`:"";g.push(`${S}: ${m}${M}`)}}else{const u=ve(t,n,o);if(e==="value"&&u.length){const{testNumber:d}=resolveTestNumber(y??0,n),h=findTestDef(n,d),S=R(t,d);let m=u.findIndex(b=>b.key===d);m<0&&(m=0);const M=u[m];let L=`<b>${M.label}: ${M.value}</b>`;if(M.key===d&&h){const b=classifySpec(S,h);(b==="failLow"||b==="failHigh")&&(L+=" <i>(out of spec)</i>")}g.push(L);const P=u.length-1;P>0&&g.push(`<i>+${P} more test${P===1?"":"s"}</i>`)}else if(u.length){const d=u.length;g.push(`<i>${d} test result${d===1?"":"s"} recorded</i>`)}if(t.hbin!==void 0||t.sbin!==void 0){const d=[];if(t.hbin!==void 0){const h=D?.get(t.hbin)?.name,S=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);d.push(`HBin: ${S}`)}if(t.sbin!==void 0){const h=I?.get(t.sbin)?.name,S=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);d.push(`SBin: ${S}`)}g.push(d.join(" \xB7 "))}}t.retestCount!==void 0&&g.push(`Retests: ${t.retestCount}`),t.siteNum!==void 0&&g.push(`Site: ${t.siteNum}`),t.partId!==void 0&&g.push(`Part ID: ${t.partId}`),t.partial&&g.push("<i>partial die</i>"),t.probeIndex!==void 0&&g.push(`Probe: #${t.probeIndex}`);const v={...t.metadata??{}};for(const[u,d]of Object.entries(v)){const h=le(d);if(h===void 0)continue;const S=$?.find(m=>m.key===u)?.label??Nt(u);g.push(`${S}: ${h}`)}return g.join("<br>")}export function buildMapTitle(t,e="engineering",a){const n=t.isLotStack&&t.lotSize!==void 0?`stacked (${t.lotSize} ${t.lotSize===1?"wafer":"wafers"})`:"stacked",s=i=>{const o=a?.length===1?a[0]:void 0;return o===void 0?`${i} Bin`:o.name?`${i} Bin ${o.bin} \xB7 ${o.name}`:`${i} Bin ${o.bin}`};switch(t.plotMode){case"hardBin":return{primary:"Hard Bin",secondary:""};case"softBin":return{primary:"Soft Bin",secondary:""};case"stackedBins":return{primary:s("Hard"),secondary:n};case"stackedSoftBins":return{primary:s("Soft"),secondary:n};case"metadata":{const i=t.metadataFields?.find(l=>l.key===t.activeMetadataKey),o=t.activeMetadataKey?Nt(t.activeMetadataKey):"Metadata";return{primary:i?.label??o,secondary:""}}case"stackedValues":{const i=t.testDefs?.[0],o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=Mt(o,i?.name,i?.unit,e),f=l||"Value",y=t.aggrMethod?` \xB7 ${Pt(t.aggrMethod)}`:"";return{primary:`${f}${y}`,secondary:n}}default:{const i=findTestDef(t.testDefs,t.activeTest),o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=Mt(o,i?.name,i?.unit,e),f=l||i?.name;if(t.passFailDisplay!=="off"){const y=i?.testNumber??t.activeTest,p=f?`${f} \xB7 #${y}`:`Test ${y}`,$=t.passFailDisplay==="spec"?"Spec pass/fail":i!==void 0&&!bt(i)?"Functional pass/fail":"Tester pass/fail";return{primary:p,secondary:$}}return{primary:f??`Test ${t.activeTest}`,secondary:""}}}}export function generateTextOverlay(t,e,a){const{plotMode:n,colorFns:s,normalize:i,activeTest:o,valueRange:l,testDefs:f,fallbackFormat:y,passFailDisplay:p="off",activeMetadataKey:$,metadataColorMap:D,allIntegerValues:I}=a,g=findTestDef(f,o),{tickFmt:v}=Mt(l[1],g?.name,g?.unit,y),u=I?h=>String(Math.round(h)):v,{testNumber:d}=resolveTestNumber(o,f);return t.flatMap((h,S)=>{let m="",M="#111111";if(n==="value"&&p==="test"){const b=Q(h,d,g);if(b===void 0)return[];m=b?"P":"F",M=G(b?yt:gt)}else if(n==="value"){const b=R(h,d);if(b===void 0)return[];m=Ht([b],u),M=G(s.forValue(i(b)))}else if(n==="hardBin"||n==="softBin"){const b=n==="softBin"?h.sbin:h.hbin;if(b===void 0)return[];m=String(b),M=G(s.forBin(b))}else if(n==="metadata"){const b=ot(h,$);if(b===void 0)return[];m=b,M=G(D?.get(m)??j)}else{const b=R(h,0);if(b===void 0)return[];m=Ht([b],u),M=G(s.forValue(i(b)))}const L=e?e[S*2]:h.physX,P=e?e[S*2+1]:h.physY;return[{x:L,y:P,text:m,fontSize:ke(h,m),color:M,align:"center"}]})}function At(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function $e(t,e,a=720){const{center:n,radius:s,notch:i}=t,o=v=>Y(e,v.x,v.y);if(!(i!==void 0&&t.diameter>150)){const v=[];for(let u=0;u<=a;u++){const d=2*Math.PI*u/a;v.push(o(Me(t,d)))}return[{kind:"wafer-boundary",...z(v,!0),lineColor:"#888888",lineWidth:1}]}const f=1.25,y=i.length,p=At(i.type),$=Math.atan2(y,s),D=s-f,I=[];let g=!1;for(let v=0;v<=a;v++){const u=2*Math.PI*v/a;let d=u-p;for(;d>Math.PI;)d-=2*Math.PI;for(;d<-Math.PI;)d+=2*Math.PI;if(Math.abs(d)<=$){if(!g){g=!0;const h={x:n.x+s*Math.cos(p-$),y:n.y+s*Math.sin(p-$)},S={x:n.x+D*Math.cos(p),y:n.y+D*Math.sin(p)},m={x:n.x+s*Math.cos(p+$),y:n.y+s*Math.sin(p+$)};I.push(o(h),o(S),o(m))}continue}I.push(o({x:n.x+s*Math.cos(u),y:n.y+s*Math.sin(u)}))}return[{kind:"wafer-boundary",...z(I,!0),lineColor:"#888888",lineWidth:1}]}function Be(t,e,a,n=360){const s=[],i=Math.max(1,Math.floor(a));for(let o=1;o<i;o++){const l=t.radius*o/i,f=[];for(let y=0;y<=n;y++){const p=2*Math.PI*y/n,$={x:t.center.x+l*Math.cos(p),y:t.center.y+l*Math.sin(p)};f.push(Y(e,$.x,$.y))}s.push({kind:"ring-boundary",...z(f,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function Te(t,e,a,n){const{x:s,y:i}=t.center,o=t.radius,l=(u,d)=>Y(e,u,d),f=a-s,y=Math.sqrt(Math.max(0,o*o-f*f)),p=l(a,i-y),$=l(a,i+y),D=n-i,I=Math.sqrt(Math.max(0,o*o-D*D)),g=l(s-I,n),v=l(s+I,n);return[{kind:"quadrant-boundary",...z([p,$]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...z([g,v]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function Ie(t,e){return t.map(a=>{const n=a.width/2,s=a.height/2;return{kind:"reticle",points:[[[a.x-n,a.y-s],[a.x+n,a.y-s],[a.x+n,a.y+s],[a.x-n,a.y+s]].map(([o,l])=>Y(e,o,l))],closed:!0,lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5,fill:"rgba(0,0,0,0)"}})}function Ve(t){const e=t.filter(a=>a.probeIndex!==void 0).sort((a,n)=>(a.probeIndex??0)-(n.probeIndex??0));return e.length?[{kind:"probe-path",...z(e.map(a=>({x:a.physX,y:a.physY}))),lineColor:"rgba(220,80,0,0.55)",lineWidth:1}]:[]}function Ce(t,e,a,n,s,i,o,l,f,y,p,$,D,I,g="off",v,u,d){const h=e.width-o,S=e.height-o,m=i?S:h,M=i?h:S,L=B=>s==="softBin"?B.sbin:B.hbin,P=B=>ot(B,v);if(e.partial){t.push({x:a,y:n,width:m,height:M,fill:ge,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:n,width:m,height:M,fill:be,type:"stacked",metadata:e.metadata});return}if(f!==void 0&&(s==="hardBin"||s==="softBin")&&L(e)!==f){t.push({x:a,y:n,width:m,height:M,fill:Ot,type:"hardBin",metadata:e.metadata});return}if(d!==void 0&&s==="metadata"&&P(e)!==d){t.push({x:a,y:n,width:m,height:M,fill:Ot,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(g==="test"){const J=Q(e,p,I),K=J===void 0?j:J?yt:gt;t.push({x:a,y:n,width:m,height:M,fill:K,type:"value",metadata:e.metadata});return}const B=R(e,p),T=classifySpec(B,I);let w,U;B===void 0?w=j:g==="spec"?w=T==="failLow"?ue:T==="failHigh"?gt:yt:(w=l.forValue(y(B)),(T==="failLow"||T==="failHigh")&&(U=T)),t.push({x:a,y:n,width:m,height:M,fill:w,type:"value",specMark:U,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const B=L(e),T=B!=null?l.forBin(B):j,w=B!=null&&me(e,D)===!1?!0:void 0;t.push({x:a,y:n,width:m,height:M,fill:T,type:s,binFail:w,metadata:e.metadata});return}if(s==="metadata"){const B=P(e),T=B!==void 0?u?.get(B)??j:j;t.push({x:a,y:n,width:m,height:M,fill:T,type:"metadata",metadata:e.metadata});return}const b=R(e,0),_=b!==void 0?l.forValue(y(b)):j;t.push({x:a,y:n,width:m,height:M,fill:_,type:"value",metadata:e.metadata})}function Fe(t,e,a){const n=t.radius*.15,s=pt(e,n,0),i=pt(e,0,n),o=s.x+i.x>=0?-1:1,l=s.y+i.y>=0?-1:1,f={x:t.center.x+o*t.radius*.9,y:t.center.y+l*t.radius*.9},y={x:f.x+s.x,y:f.y+s.y},p={x:f.x+i.x,y:f.y+i.y};return a.push({x:y.x+s.x*.35,y:y.y+s.y*.35,text:"+X",fontSize:10,color:"#cc3300",align:"center",role:"indicator"},{x:p.x+i.x*.35,y:p.y+i.y*.35,text:"+Y",fontSize:10,color:"#0044cc",align:"center",role:"indicator"}),[{kind:"xy-indicator",...z([f,y]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...z([f,p]),lineColor:"#0044cc",lineWidth:2}]}export function buildView(t,e,a={},n){const s=a.reticles??[],{plotMode:i="value",showDieLabels:o=!1,showReticle:l=!1,showProbePath:f=!1,ringCount:y=4,showRingBoundaries:p=!1,showQuadrantBoundaries:$=!1,showXYIndicator:D=!1,dieGap:I=1,binColorScheme:g="default",valueColorScheme:v="default",reverseValueScheme:u=!1,useDefinedBinColors:d=!0,binColors:h,passBins:S=[1],highlightBin:m,highlightMetadataValue:M,interactiveTransform:L,valueRange:P,metadataValueOrder:b,testDefs:_,activeTest:B=0,activeMetadataKey:T,fallbackFormat:w="engineering",aggregationMethod:U,lotSize:J,dataAxisFlip:K,isLotStack:Et=!1,logScale:_t,colorbarRangeMode:wt="spec",passFailDisplay:Wt,showPartialDies:jt=!0}=a,St=Wt??"off",rt=n?.hbinDefs,lt=n?.sbinDefs,kt=n?.metadataFields,zt=kt?.find(c=>c.key===T),Kt={x:(K?.x??!1)!==(L?.flipX??!1),y:(K?.y??!1)!==(L?.flipY??!1)},qt=rt?new Map(rt.map(c=>[c.bin,c])):null,Xt=lt?new Map(lt.map(c=>[c.bin,c])):null,Yt=i==="softBin"?Xt:qt,Gt=new Set(S),ct=h&&he(h,e)?h:de(e,{passBins:S,binColorScheme:g,hbinDefs:rt,sbinDefs:lt,useDefinedBinColors:d}),Qt=i==="softBin"?ct.soft:ct.hard,vt={forValue:Se(fe(v,u)),forBin:c=>Qt.get(c)??j};let{testNumber:H}=resolveTestNumber(B,_);if(!_?.length&&i==="value"&&!e.some(r=>r.testValues&&H in r.testValues)){const r=e.reduce((x,C)=>{if(!C.testValues)return x;const k=Object.keys(C.testValues).map(Number),N=k.length?Math.min(...k):void 0;return N!==void 0&&(x===void 0||N<x)?N:x},void 0);r!==void 0&&(H=r)}let Z;if(Array.isArray(P))Z=P;else if(P){const{testNumber:c}=resolveTestNumber(P.test,_);Z=c===H?P.range:void 0}const V=findTestDef(_,H),$t=i==="value"&&V!==void 0&&!bt(V),Ut=V!==void 0&&(V.limitLow!==void 0||V.limitHigh!==void 0);let A="off";i==="value"&&($t?A="test":St==="spec"&&Ut?A="spec":St==="test"&&e.some(c=>Q(c,H,V)!==void 0)&&(A="test"));const Bt=A==="spec"?"spec":wt,ut=i==="stackedValues"||i==="stackedBins"||i==="stackedSoftBins";let O,E,tt=!1;if(Z){[O,E]=Z;let c=!0,r=!1;for(const x of e){const C=ut?R(x,0):R(x,H);if(C!==void 0&&(r=!0,!Number.isInteger(C))){c=!1;break}}tt=r&&c}else{const c=!ut&&i==="value"&&Bt==="spec"&&V&&(V.limitLow!==void 0||V.limitHigh!==void 0);let r=1/0,x=-1/0,C=!0;for(const k of e){const N=ut?R(k,0):R(k,H);N!==void 0&&(N<r&&(r=N),N>x&&(x=N),Number.isInteger(N)||(C=!1))}isFinite(r)||(C=!1),tt=C,c?(O=V.limitLow!==void 0?V.limitLow:isFinite(r)?r:0,E=V.limitHigh!==void 0?V.limitHigh:isFinite(x)?x:1):(O=isFinite(r)?r:0,E=isFinite(x)?x:1)}O===E&&(E=O+1);const Tt=$t?!1:_t??V?.logScale??!1,It=Tt&&O>0&&E>0,Jt=It;let et;if(It){const c=Math.log10(O),r=Math.log10(E)-c;et=x=>x<=0?0:Rt((Math.log10(x)-c)/r)}else et=c=>Rt((c-O)/(E-O));const F=xe(t,K,L);let Vt=null;if(t.notch){const c=At(t.notch.type),r=pt(F.physicalToScreen,Math.cos(c)*t.radius,Math.sin(c)*t.radius),x=Math.hypot(r.x,r.y);x>0&&(Vt={x:r.x/x,y:r.y/x})}let q=1/0;for(let c=0;c<e.length;c++){const r=e[c];r.width<q&&(q=r.width),r.height<q&&(q=r.height)}const Zt=q!==1/0?Math.min(I,q*.12):I,Ct=[],ft=[],te=F.bakedToScreen.a===1&&F.bakedToScreen.b===0&&F.bakedToScreen.c===0&&F.bakedToScreen.d===1&&F.bakedToScreen.e===0&&F.bakedToScreen.f===0;let W=null;if(!te){W=new Float64Array(e.length*2);for(let c=0;c<e.length;c++){const r=Y(F.bakedToScreen,e[c].physX,e[c].physY);W[c*2]=r.x,W[c*2+1]=r.y}}const ee=ce(F.gridToScreen),ne=i==="hardBin"||i==="softBin"||i==="stackedBins"||i==="stackedSoftBins",ae=i==="softBin"||i==="stackedSoftBins";let nt=ne?new Map:void 0;const dt=i==="value"&&A==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,ht=i==="value"&&A==="test"?{pass:0,fail:0}:void 0,Ft=i==="metadata";let at=Ft?new Map:void 0,it=null;if(Ft&&T){const c=collectMetadataValues(e,T),r=b?.key===T?b.values:void 0,x=r?[...r,...c.filter(C=>!r.includes(C))]:c;it=new Map(x.map((C,k)=>[C,zt?.values?.find(N=>N.value===C)?.color??pe(k)]))}for(let c=0;c<e.length;c++){const r=e[c],x=W?W[c*2]:r.physX,C=W?W[c*2+1]:r.physY;if(ft.push({x,y:C}),nt&&!r.partial&&!r.edgeExcluded){const k=ae?r.sbin:r.hbin;k!=null&&nt.set(k,(nt.get(k)??0)+1)}if(dt&&!r.partial&&!r.edgeExcluded){const k=classifySpec(R(r,H),V);k&&dt[k]++}if(ht&&!r.partial&&!r.edgeExcluded){const k=Q(r,H,V);k!==void 0&&ht[k?"pass":"fail"]++}if(at&&!r.partial&&!r.edgeExcluded){const k=ot(r,T);k!==void 0&&at.set(k,(at.get(k)??0)+1)}r.partial&&!jt||Ce(Ct,r,x,C,i,ee,Zt,vt,m,et,H,Yt,Gt,V,A,T,it,M)}const Dt=o?generateTextOverlay(e,W,{plotMode:i,colorFns:vt,normalize:et,activeTest:B,valueRange:[O,E],testDefs:_,fallbackFormat:w,passFailDisplay:A,activeMetadataKey:T,metadataColorMap:it,allIntegerValues:tt}):[],X=$e(t,F.physicalToScreen);p&&X.push(...Be(t,F.physicalToScreen,y)),$&&X.push(...Te(t,F.physicalToScreen,t.center.x,t.center.y)),l&&X.push(...Ie(s,F.gridToScreen)),f&&X.push(...Ve(e)),D&&X.push(...Fe(t,F.gridToScreen,Dt));const ie=ft.length>0?{minX:t.center.x-t.radius,maxX:t.center.x+t.radius,minY:t.center.y-t.radius,maxY:t.center.y+t.radius}:null;return{rectangles:Ct,hoverPoints:ft,texts:Dt,overlays:X,plotMode:i,binColorScheme:g,valueColorScheme:v,reverseValueScheme:u,binColors:ct,metadata:t.metadata??null,dies:e,valueRange:[O,E],allIntegerValues:tt,testDefs:_,activeTest:B,logScale:Jt,logScaleRequested:Tt,aggrMethod:U,lotSize:J,axisFlip:Kt,dataAxisFlip:{x:K?.x??!1,y:K?.y??!1},gridToScreen:F.gridToScreen,rotation:((t.orientation+(L?.rotation??0))%360+360)%360,hasReticle:s.length>0,isLotStack:Et,passFailDisplay:A,colorBySpec:A==="spec",colorbarRangeMode:Bt,waferCenter:t.center,waferRadius:t.radius,notchDir:Vt,binCounts:nt,metadataCounts:at,metadataColorMap:it,activeMetadataKey:T,metadataFields:kt,specCounts:dt,passFailCounts:ht,dieBounds:ie}}export{getDieKey}from"../core/dies.js";
|
|
1
|
+
import{hasPosition as oe}from"../core/dies.js";import{getReticleCell as re}from"../core/reticle.js";import{metadataCategoricalValue as le,metadataDisplayValue as ce}from"../core/metadata.js";import{affineRotation as pt,affineMirror as Rt,affineCompose as st,affinePoint as G,affineVector as yt,affineSwapsAxes as ue}from"../core/transforms.js";import{contrastTextColor as Q,SPEC_PASS_FILL as gt,SPEC_FAIL_LOW as fe,SPEC_FAIL_HIGH as bt}from"./colorMap.js";import{resolveValueColorFn as de}from"./colorSchemes.js";import{resolveBinColors as he,binColorsCover as me}from"./binColors.js";import{NO_DATA_FILL as z}from"./colorMap.js";import{diePassStatus as pe}from"../core/dies.js";import{getDieTestValue as P,getTestPassStatus as U,isParametricTest as xt}from"./buildWaferMap.js";import{fmt as Mt,fmtColorbarAxis as St,fmtAggregationMethod as Nt}from"./fmt.js";import{metadataValueColor as ye}from"./colorMap.js";import{compareNatural as ge,clamp01 as Ht,escHtml as O}from"../core/utils.js";import{prettyKey as Ot}from"../core/utils.js";export function resolveTestNumber(t,e){return e?.length?{testNumber:(findTestDef(e,t)??e[0]).testNumber}:{testNumber:t}}export function collectMetadataValues(t,e){const a=new Set;for(const n of t){if(n.partial||n.edgeExcluded)continue;const s=ot(n,e);s!==void 0&&a.add(s)}return[...a].sort(ge)}export function findTestDef(t,e){return t?.find(a=>a.testNumber===e)}export function getUniqueTestNumbers(t){return[...new Set(t.flatMap(e=>e.testValues?Object.keys(e.testValues).map(Number):[]))].sort((e,a)=>e-a)}const be="#d3d6db",At="#e8e9ea",xe="#aab0ba";function ot(t,e){return e?le(t.metadata?.[e]):void 0}export function classifySpec(t,e){return t===void 0?null:e?.limitLow!==void 0&&t<e.limitLow?"failLow":e?.limitHigh!==void 0&&t>e.limitHigh?"failHigh":"pass"}function Me(t,e,a){const{x:n,y:s}=t.center,i=pt(t.orientation,n,s),o=st(Rt(e?.x??!1,e?.y??!1,n,s),pt(t.orientation,n,s)),l=st(Rt(a?.flipX??!1,a?.flipY??!1,n,s),pt(a?.rotation??0,n,s));return{bakedToScreen:l,physicalToScreen:st(l,i),gridToScreen:st(l,o)}}function K(t,e=!1){return{points:[t],closed:e}}function Se(t,e){const{center:a,radius:n,notch:s}=t;let i=a.x+n*Math.cos(e),o=a.y+n*Math.sin(e);if(!s)return{x:i,y:o};const l=Math.sqrt(n**2-(s.length/2)**2),d=s.length/2,g=i-a.x,p=o-a.y;return s.type==="bottom"&&p<-l?(o=a.y-l,i=a.x+Math.max(-d,Math.min(d,g))):s.type==="top"&&p>l?(o=a.y+l,i=a.x+Math.max(-d,Math.min(d,g))):s.type==="left"&&g<-l?(i=a.x-l,o=a.y+Math.max(-d,Math.min(d,p))):s.type==="right"&&g>l&&(i=a.x+l,o=a.y+Math.max(-d,Math.min(d,p))),{x:i,y:o}}function Et(t,e){return t.map(e).join(" / ")}function ke(t,e=256){const a=Array.from({length:e},(n,s)=>t(s/(e-1)));return n=>a[Math.min(e-1,Math.max(0,Math.round(n*(e-1))))]}function ve(t,e){const a=Math.max(1,Math.min(t.width,t.height)),n=t.width/Math.max(e.length,1);return Math.max(8,Math.min(16,Math.round(Math.min(a*.55,n*1.8))))}function $e(t,e,a){if(e?.length){const n=e.flatMap(s=>{const i=s.testNumber;if(!xt(s)){const l=U(t,i,s);return l===void 0?[]:[{key:i,label:s.name,value:l?"Pass":"Fail"}]}const o=P(t,i);return o===void 0?[]:[{key:i,label:s.name,value:Mt(o,s.unit,a),recordedFail:t.testPass?.[i]===!1}]});if(n.length)return n}return t.testValues&&Object.keys(t.testValues).length>0?Object.entries(t.testValues).map(([n,s])=>({key:Number(n),label:`Test ${n}`,value:Mt(s,void 0,a)})):t.testPass&&Object.keys(t.testPass).length>0?Object.entries(t.testPass).map(([n,s])=>({key:Number(n),label:`Test ${n}`,value:s?"Pass":"Fail"})):[]}export function buildHoverText(t,e,a={}){const{testDefs:n,hbinDefs:s,sbinDefs:i,fallbackFormat:o,aggrMethod:l,lotSize:d,activeTest:g,reticleConfig:p,metadataFields:B}=a,D=s?new Map(s.map(u=>[u.bin,u])):null,T=i?new Map(i.map(u=>[u.bin,u])):null,b=[O(`Die (${t.x}, ${t.y})`)];if(p&&oe(t)){const u=re(t,p);b.push(O(`Reticle (${u.column}, ${u.row})`))}if(e==="stackedValues"){const u=P(t,0);if(u!==void 0){const f=n?.[0],h=f?.testNumber,k=f?.name??(h!=null?`Test ${h}`:"Value"),m=l?` (${l})`:"";b.push(O(`${k}${m}: ${Mt(u,f?.unit,o)}`))}}else if(e==="stackedBins"||e==="stackedSoftBins"){const u=P(t,0),f=e==="stackedSoftBins"?t.sbin:t.hbin;if(u!==void 0){const h=e==="stackedSoftBins"?T:D,k=f!==void 0?h?.get(f)?.name?`${f} \xB7 ${h.get(f).name}`:`Bin ${f}`:"Bin";let m;if(l==="percent")m=`${u.toFixed(0)}%`;else{const C=d?` (${(u/d*100).toFixed(0)}% of stacked wafers)`:"";m=`${u}${C}`}const x=l?` [${Nt(l)}]`:"";b.push(O(`${k}: ${m}${x}`))}}else{const u=$e(t,n,o);if(e==="value"&&u.length){const{testNumber:f}=resolveTestNumber(g??0,n),h=findTestDef(n,f),k=P(t,f);let m=u.findIndex(y=>y.key===f);m<0&&(m=0);const x=u[m];let C=`<b>${O(`${x.label}: ${x.value}`)}${x.recordedFail?" <i>(recorded fail)</i>":""}</b>`;if(x.key===f&&h){const y=classifySpec(k,h);(y==="failLow"||y==="failHigh")&&(C+=" <i>(out of spec)</i>")}b.push(C);const R=u.length-1;R>0&&b.push(`<i>+${R} more test${R===1?"":"s"}</i>`)}else if(u.length){const f=u.length;b.push(`<i>${f} test result${f===1?"":"s"} recorded</i>`)}if(t.hbin!==void 0||t.sbin!==void 0){const f=[];if(t.hbin!==void 0){const h=D?.get(t.hbin)?.name,k=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);f.push(`HBin: ${k}`)}if(t.sbin!==void 0){const h=T?.get(t.sbin)?.name,k=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);f.push(`SBin: ${k}`)}b.push(f.map(O).join(" \xB7 "))}}t.retestCount!==void 0&&b.push(O(`Retests: ${t.retestCount}`)),t.siteNum!==void 0&&b.push(O(`Site: ${t.siteNum}`)),t.partId!==void 0&&b.push(O(`Part ID: ${t.partId}`)),t.partial&&b.push("<i>partial die</i>"),t.probeIndex!==void 0&&b.push(O(`Probe: #${t.probeIndex}`));const $={...t.metadata??{}};for(const[u,f]of Object.entries($)){const h=ce(f);if(h===void 0)continue;const k=B?.find(m=>m.key===u)?.label??Ot(u);b.push(O(`${k}: ${h}`))}return b.join("<br>")}export function buildMapTitle(t,e="engineering",a){const n=t.isLotStack&&t.lotSize!==void 0?`stacked (${t.lotSize} ${t.lotSize===1?"wafer":"wafers"})`:"stacked",s=i=>{const o=a?.length===1?a[0]:void 0;return o===void 0?`${i} Bin`:o.name?`${i} Bin ${o.bin} \xB7 ${o.name}`:`${i} Bin ${o.bin}`};switch(t.plotMode){case"hardBin":return{primary:"Hard Bin",secondary:""};case"softBin":return{primary:"Soft Bin",secondary:""};case"stackedBins":return{primary:s("Hard"),secondary:n};case"stackedSoftBins":return{primary:s("Soft"),secondary:n};case"metadata":{const i=t.metadataFields?.find(l=>l.key===t.activeMetadataKey),o=t.activeMetadataKey?Ot(t.activeMetadataKey):"Metadata";return{primary:i?.label??o,secondary:""}}case"stackedValues":{const i=t.testDefs?.[0],o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=St(o,i?.name,i?.unit,e),d=l||"Value",g=t.aggrMethod?` \xB7 ${Nt(t.aggrMethod)}`:"";return{primary:`${d}${g}`,secondary:n}}default:{const i=findTestDef(t.testDefs,t.activeTest),o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=St(o,i?.name,i?.unit,e),d=l||i?.name;if(t.passFailDisplay!=="off"){const g=i?.testNumber??t.activeTest,p=d?`${d} \xB7 #${g}`:`Test ${g}`,B=t.passFailDisplay==="spec"?"Spec pass/fail":i!==void 0&&!xt(i)?"Functional pass/fail":"Tester pass/fail";return{primary:p,secondary:B}}return{primary:d??`Test ${t.activeTest}`,secondary:""}}}}export function generateTextOverlay(t,e,a){const{plotMode:n,colorFns:s,normalize:i,activeTest:o,valueRange:l,testDefs:d,fallbackFormat:g,passFailDisplay:p="off",activeMetadataKey:B,metadataColorMap:D,allIntegerValues:T}=a,b=findTestDef(d,o),{tickFmt:$}=St(l[1],b?.name,b?.unit,g),u=T?h=>String(Math.round(h)):$,{testNumber:f}=resolveTestNumber(o,d);return t.flatMap((h,k)=>{let m="",x="#111111";if(n==="value"&&p==="test"){const y=U(h,f,b);if(y===void 0)return[];m=y?"P":"F",x=Q(y?gt:bt)}else if(n==="value"){const y=P(h,f);if(y===void 0)return[];m=Et([y],u),x=Q(s.forValue(i(y)))}else if(n==="hardBin"||n==="softBin"){const y=n==="softBin"?h.sbin:h.hbin;if(y===void 0)return[];m=String(y),x=Q(s.forBin(y))}else if(n==="metadata"){const y=ot(h,B);if(y===void 0)return[];m=y,x=Q(D?.get(m)??z)}else{const y=P(h,0);if(y===void 0)return[];m=Et([y],u),x=Q(s.forValue(i(y)))}const C=e?e[k*2]:h.physX,R=e?e[k*2+1]:h.physY;return[{x:C,y:R,text:m,fontSize:ve(h,m),color:x,align:"center"}]})}function _t(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function Be(t,e,a=720){const{center:n,radius:s,notch:i}=t,o=$=>G(e,$.x,$.y);if(!(i!==void 0&&t.diameter>150)){const $=[];for(let u=0;u<=a;u++){const f=2*Math.PI*u/a;$.push(o(Se(t,f)))}return[{kind:"wafer-boundary",...K($,!0),lineColor:"#888888",lineWidth:1}]}const d=1.25,g=i.length,p=_t(i.type),B=Math.atan2(g,s),D=s-d,T=[];let b=!1;for(let $=0;$<=a;$++){const u=2*Math.PI*$/a;let f=u-p;for(;f>Math.PI;)f-=2*Math.PI;for(;f<-Math.PI;)f+=2*Math.PI;if(Math.abs(f)<=B){if(!b){b=!0;const h={x:n.x+s*Math.cos(p-B),y:n.y+s*Math.sin(p-B)},k={x:n.x+D*Math.cos(p),y:n.y+D*Math.sin(p)},m={x:n.x+s*Math.cos(p+B),y:n.y+s*Math.sin(p+B)};T.push(o(h),o(k),o(m))}continue}T.push(o({x:n.x+s*Math.cos(u),y:n.y+s*Math.sin(u)}))}return[{kind:"wafer-boundary",...K(T,!0),lineColor:"#888888",lineWidth:1}]}function Te(t,e,a,n=360){const s=[],i=Math.max(1,Math.floor(a));for(let o=1;o<i;o++){const l=t.radius*o/i,d=[];for(let g=0;g<=n;g++){const p=2*Math.PI*g/n,B={x:t.center.x+l*Math.cos(p),y:t.center.y+l*Math.sin(p)};d.push(G(e,B.x,B.y))}s.push({kind:"ring-boundary",...K(d,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function Ie(t,e,a,n){const{x:s,y:i}=t.center,o=t.radius,l=(u,f)=>G(e,u,f),d=a-s,g=Math.sqrt(Math.max(0,o*o-d*d)),p=l(a,i-g),B=l(a,i+g),D=n-i,T=Math.sqrt(Math.max(0,o*o-D*D)),b=l(s-T,n),$=l(s+T,n);return[{kind:"quadrant-boundary",...K([p,B]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...K([b,$]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function Ve(t,e){return t.map(a=>{const n=a.width/2,s=a.height/2;return{kind:"reticle",points:[[[a.x-n,a.y-s],[a.x+n,a.y-s],[a.x+n,a.y+s],[a.x-n,a.y+s]].map(([o,l])=>G(e,o,l))],closed:!0,lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5,fill:"rgba(0,0,0,0)"}})}function Ce(t){const e=t.filter(a=>a.probeIndex!==void 0).sort((a,n)=>(a.probeIndex??0)-(n.probeIndex??0));return e.length?[{kind:"probe-path",...K(e.map(a=>({x:a.physX,y:a.physY}))),lineColor:"rgba(220,80,0,0.55)",lineWidth:1}]:[]}function Fe(t,e,a,n,s,i,o,l,d,g,p,B,D,T,b="off",$,u,f){const h=e.width-o,k=e.height-o,m=i?k:h,x=i?h:k,C=S=>s==="softBin"?S.sbin:S.hbin,R=S=>ot(S,$);if(e.partial){t.push({x:a,y:n,width:m,height:x,fill:be,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:n,width:m,height:x,fill:xe,type:"stacked",metadata:e.metadata});return}if(d!==void 0&&(s==="hardBin"||s==="softBin")&&C(e)!==d){t.push({x:a,y:n,width:m,height:x,fill:At,type:"hardBin",metadata:e.metadata});return}if(f!==void 0&&s==="metadata"&&R(e)!==f){t.push({x:a,y:n,width:m,height:x,fill:At,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(b==="test"){const W=U(e,p,T),rt=W===void 0?z:W?gt:bt;t.push({x:a,y:n,width:m,height:x,fill:rt,type:"value",metadata:e.metadata});return}const S=P(e,p),N=classifySpec(S,T);let w,J;S===void 0?w=z:b==="spec"?w=N==="failLow"?fe:N==="failHigh"?bt:gt:(w=l.forValue(g(S)),(N==="failLow"||N==="failHigh")&&(J=N)),t.push({x:a,y:n,width:m,height:x,fill:w,type:"value",specMark:J,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const S=C(e),N=S!=null?l.forBin(S):z,w=S!=null&&pe(e,D)===!1?!0:void 0;t.push({x:a,y:n,width:m,height:x,fill:N,type:s,binFail:w,metadata:e.metadata});return}if(s==="metadata"){const S=R(e),N=S!==void 0?u?.get(S)??z:z;t.push({x:a,y:n,width:m,height:x,fill:N,type:"metadata",metadata:e.metadata});return}const y=P(e,0),Y=y!==void 0?l.forValue(g(y)):z;t.push({x:a,y:n,width:m,height:x,fill:Y,type:"value",metadata:e.metadata})}function De(t,e,a){const n=t.radius*.15,s=yt(e,n,0),i=yt(e,0,n),o=s.x+i.x>=0?-1:1,l=s.y+i.y>=0?-1:1,d={x:t.center.x+o*t.radius*.9,y:t.center.y+l*t.radius*.9},g={x:d.x+s.x,y:d.y+s.y},p={x:d.x+i.x,y:d.y+i.y};return a.push({x:g.x+s.x*.35,y:g.y+s.y*.35,text:"+X",fontSize:10,color:"#cc3300",align:"center",role:"indicator"},{x:p.x+i.x*.35,y:p.y+i.y*.35,text:"+Y",fontSize:10,color:"#0044cc",align:"center",role:"indicator"}),[{kind:"xy-indicator",...K([d,g]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...K([d,p]),lineColor:"#0044cc",lineWidth:2}]}export function buildView(t,e,a={},n){const s=a.reticles??[],{plotMode:i="value",showDieLabels:o=!1,showReticle:l=!1,showProbePath:d=!1,ringCount:g=4,showRingBoundaries:p=!1,showQuadrantBoundaries:B=!1,showXYIndicator:D=!1,binColorScheme:T="default",valueColorScheme:b="default",reverseValueScheme:$=!1,useDefinedBinColors:u=!0,binColors:f,passBins:h=[1],highlightBin:k,highlightMetadataValue:m,interactiveTransform:x,valueRange:C,metadataValueOrder:R,testDefs:y,activeTest:Y=0,activeMetadataKey:S,fallbackFormat:N="engineering",aggregationMethod:w,lotSize:J,dataAxisFlip:W,isLotStack:rt=!1,logScale:wt,colorbarRangeMode:Wt="spec",passFailDisplay:jt,showPartialDies:zt=!0}=a,kt=jt??"off",lt=n?.hbinDefs,ct=n?.sbinDefs,vt=n?.metadataFields,Kt=vt?.find(c=>c.key===S),qt={x:(W?.x??!1)!==(x?.flipX??!1),y:(W?.y??!1)!==(x?.flipY??!1)},Xt=lt?new Map(lt.map(c=>[c.bin,c])):null,Yt=ct?new Map(ct.map(c=>[c.bin,c])):null,Gt=i==="softBin"?Yt:Xt,Qt=new Set(h),ut=f&&me(f,e)?f:he(e,{passBins:h,binColorScheme:T,hbinDefs:lt,sbinDefs:ct,useDefinedBinColors:u}),Ut=i==="softBin"?ut.soft:ut.hard,$t={forValue:ke(de(b,$)),forBin:c=>Ut.get(c)??z};let{testNumber:A}=resolveTestNumber(Y,y);if(!y?.length&&i==="value"&&!e.some(r=>r.testValues&&A in r.testValues)){const r=e.reduce((M,V)=>{if(!V.testValues)return M;const v=Object.keys(V.testValues).map(Number),L=v.length?Math.min(...v):void 0;return L!==void 0&&(M===void 0||L<M)?L:M},void 0);r!==void 0&&(A=r)}let Z;if(Array.isArray(C))Z=C;else if(C){const{testNumber:c}=resolveTestNumber(C.test,y);Z=c===A?C.range:void 0}const I=findTestDef(y,A),Bt=i==="value"&&I!==void 0&&!xt(I),Jt=I!==void 0&&(I.limitLow!==void 0||I.limitHigh!==void 0);let _="off";i==="value"&&(Bt?_="test":kt==="spec"&&Jt?_="spec":kt==="test"&&e.some(c=>U(c,A,I)!==void 0)&&(_="test"));const Tt=_==="spec"?"spec":Wt,ft=i==="stackedValues"||i==="stackedBins"||i==="stackedSoftBins";let H,E,tt=!1;if(Z){[H,E]=Z;let c=!0,r=!1;for(const M of e){const V=ft?P(M,0):P(M,A);if(V!==void 0&&(r=!0,!Number.isInteger(V))){c=!1;break}}tt=r&&c}else{const c=!ft&&i==="value"&&Tt==="spec"&&I&&(I.limitLow!==void 0||I.limitHigh!==void 0);let r=1/0,M=-1/0,V=!0;for(const v of e){const L=ft?P(v,0):P(v,A);L!==void 0&&(L<r&&(r=L),L>M&&(M=L),Number.isInteger(L)||(V=!1))}isFinite(r)||(V=!1),tt=V,c?(H=I.limitLow!==void 0?I.limitLow:isFinite(r)?r:0,E=I.limitHigh!==void 0?I.limitHigh:isFinite(M)?M:1):(H=isFinite(r)?r:0,E=isFinite(M)?M:1)}H===E&&(E=H+1);const It=Bt?!1:wt??I?.logScale??!1,Vt=It&&H>0&&E>0,Zt=Vt;let et;if(Vt){const c=Math.log10(H),r=Math.log10(E)-c;et=M=>M<=0?0:Ht((Math.log10(M)-c)/r)}else et=c=>Ht((c-H)/(E-H));const F=Me(t,W,x);let Ct=null;if(t.notch){const c=_t(t.notch.type),r=yt(F.physicalToScreen,Math.cos(c)*t.radius,Math.sin(c)*t.radius),M=Math.hypot(r.x,r.y);M>0&&(Ct={x:r.x/M,y:r.y/M})}let q=1/0;for(let c=0;c<e.length;c++){const r=e[c];r.width<q&&(q=r.width),r.height<q&&(q=r.height)}const Ft=1,te=q!==1/0?Math.min(Ft,q*.12):Ft,Dt=[],dt=[],ee=F.bakedToScreen.a===1&&F.bakedToScreen.b===0&&F.bakedToScreen.c===0&&F.bakedToScreen.d===1&&F.bakedToScreen.e===0&&F.bakedToScreen.f===0;let j=null;if(!ee){j=new Float64Array(e.length*2);for(let c=0;c<e.length;c++){const r=G(F.bakedToScreen,e[c].physX,e[c].physY);j[c*2]=r.x,j[c*2+1]=r.y}}const ne=ue(F.gridToScreen),ae=i==="hardBin"||i==="softBin"||i==="stackedBins"||i==="stackedSoftBins",ie=i==="softBin"||i==="stackedSoftBins";let nt=ae?new Map:void 0;const ht=i==="value"&&_==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,mt=i==="value"&&_==="test"?{pass:0,fail:0}:void 0,Pt=i==="metadata";let at=Pt?new Map:void 0,it=null;if(Pt&&S){const c=collectMetadataValues(e,S),r=R?.key===S?R.values:void 0,M=r?[...r,...c.filter(V=>!r.includes(V))]:c;it=new Map(M.map((V,v)=>[V,Kt?.values?.find(L=>L.value===V)?.color??ye(v)]))}for(let c=0;c<e.length;c++){const r=e[c],M=j?j[c*2]:r.physX,V=j?j[c*2+1]:r.physY;if(dt.push({x:M,y:V}),nt&&!r.partial&&!r.edgeExcluded){const v=ie?r.sbin:r.hbin;v!=null&&nt.set(v,(nt.get(v)??0)+1)}if(ht&&!r.partial&&!r.edgeExcluded){const v=classifySpec(P(r,A),I);v&&ht[v]++}if(mt&&!r.partial&&!r.edgeExcluded){const v=U(r,A,I);v!==void 0&&mt[v?"pass":"fail"]++}if(at&&!r.partial&&!r.edgeExcluded){const v=ot(r,S);v!==void 0&&at.set(v,(at.get(v)??0)+1)}r.partial&&!zt||Fe(Dt,r,M,V,i,ne,te,$t,k,et,A,Gt,Qt,I,_,S,it,m)}const Lt=o?generateTextOverlay(e,j,{plotMode:i,colorFns:$t,normalize:et,activeTest:Y,valueRange:[H,E],testDefs:y,fallbackFormat:N,passFailDisplay:_,activeMetadataKey:S,metadataColorMap:it,allIntegerValues:tt}):[],X=Be(t,F.physicalToScreen);p&&X.push(...Te(t,F.physicalToScreen,g)),B&&X.push(...Ie(t,F.physicalToScreen,t.center.x,t.center.y)),l&&X.push(...Ve(s,F.gridToScreen)),d&&X.push(...Ce(e)),D&&X.push(...De(t,F.gridToScreen,Lt));const se=dt.length>0?{minX:t.center.x-t.radius,maxX:t.center.x+t.radius,minY:t.center.y-t.radius,maxY:t.center.y+t.radius}:null;return{rectangles:Dt,hoverPoints:dt,texts:Lt,overlays:X,plotMode:i,binColorScheme:T,valueColorScheme:b,reverseValueScheme:$,binColors:ut,metadata:t.metadata??null,dies:e,valueRange:[H,E],allIntegerValues:tt,testDefs:y,activeTest:Y,logScale:Zt,logScaleRequested:It,aggrMethod:w,lotSize:J,axisFlip:qt,dataAxisFlip:{x:W?.x??!1,y:W?.y??!1},gridToScreen:F.gridToScreen,rotation:((t.orientation+(x?.rotation??0))%360+360)%360,hasReticle:s.length>0,isLotStack:rt,passFailDisplay:_,colorbarRangeMode:Tt,waferCenter:t.center,waferRadius:t.radius,notchDir:Ct,binCounts:nt,metadataCounts:at,metadataColorMap:it,activeMetadataKey:S,metadataFields:vt,specCounts:ht,passFailCounts:mt,dieBounds:se}}export{getDieKey}from"../core/dies.js";
|
|
@@ -301,6 +301,18 @@ export interface WaferMapInputBase {
|
|
|
301
301
|
* Set to an empty array to suppress yield calculation.
|
|
302
302
|
*/
|
|
303
303
|
passBins?: number[];
|
|
304
|
+
/**
|
|
305
|
+
* Equal-radius rings the wafer is divided into for ring overlays, ring yield and
|
|
306
|
+
* ring findings. Default 4. Set it here, once: the result carries it
|
|
307
|
+
* (`WaferMapResult.ringCount`) and the renderers, analysis, panels and reports all
|
|
308
|
+
* read it, so "Ring 2" names the same dies everywhere. A whole number of at least
|
|
309
|
+
* 1; anything else is corrected and reported as an `analysis-option-corrected`
|
|
310
|
+
* warning. No upper bound, deliberately: a fine banding is still gated by the
|
|
311
|
+
* analysis's minimum region size — at 40 rings on a 561-die wafer the ring
|
|
312
|
+
* findings still carry 16–148 dies each, and a die-count-derived cap rejected
|
|
313
|
+
* 3 rings on 28-die wafers that produce correct findings.
|
|
314
|
+
*/
|
|
315
|
+
ringCount?: number;
|
|
304
316
|
/**
|
|
305
317
|
* Wafer diameters (mm) treated as standard when sanity-checking an *inferred*
|
|
306
318
|
* diameter — see {@link STANDARD_WAFER_DIAMETERS_MM} for the default
|
|
@@ -337,7 +349,7 @@ export interface WaferMapInputBase {
|
|
|
337
349
|
*/
|
|
338
350
|
retestPolicy?: 'last' | 'first' | 'best' | 'worst';
|
|
339
351
|
/**
|
|
340
|
-
* Named test definitions — one per
|
|
352
|
+
* Named test definitions — one per test number in `die.testValues`.
|
|
341
353
|
* When provided, tooltips show `"Idsat: 1.23 A"` instead of `"Values: 1.23"`,
|
|
342
354
|
* and the mode selector offers a per-test dropdown entry.
|
|
343
355
|
*/
|
|
@@ -381,19 +393,40 @@ export interface WaferMapInputSingle extends WaferMapInputBase {
|
|
|
381
393
|
/** Per-die test results from the prober. */
|
|
382
394
|
results?: DieResult[];
|
|
383
395
|
lotStack?: never;
|
|
396
|
+
layout?: never;
|
|
384
397
|
}
|
|
385
398
|
/** Lot-stack input — collapse results from multiple wafers into a single aggregated map. */
|
|
386
399
|
export interface WaferMapInputLotStack extends WaferMapInputBase {
|
|
387
400
|
/** Lot-level stacking — collapse results from several wafers into a single map. */
|
|
388
401
|
lotStack: LotStackConfig;
|
|
389
402
|
results?: never;
|
|
403
|
+
layout?: never;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Layout input — a wafer's die sites with no test data: gross die per wafer,
|
|
407
|
+
* reticle and step planning, the expected die map before data exists.
|
|
408
|
+
*
|
|
409
|
+
* Requires `waferConfig.diameter` and `dieConfig.width`/`height`. The map holds
|
|
410
|
+
* every die site lying **fully** on the wafer (notch and flat included), which is
|
|
411
|
+
* the gross die count and the set of sites a prober can step to — so, like a map
|
|
412
|
+
* built from results, it never contains edge-straddling dies. Die `(0, 0)` is the
|
|
413
|
+
* site centred on the wafer, and `x`/`y` count sites in the directions
|
|
414
|
+
* `dieConfig.xAxisDirection`/`yAxisDirection` give. `waferConfig.center` is
|
|
415
|
+
* ignored: the layout defines the centre. `edgeExclusion`, `reticleConfig` and
|
|
416
|
+
* orientation apply as for any map.
|
|
417
|
+
*/
|
|
418
|
+
export interface WaferMapInputLayout extends WaferMapInputBase {
|
|
419
|
+
layout: true;
|
|
420
|
+
results?: never;
|
|
421
|
+
lotStack?: never;
|
|
390
422
|
}
|
|
391
423
|
/**
|
|
392
|
-
* Input accepted by {@link buildWaferMap}
|
|
393
|
-
*
|
|
394
|
-
*
|
|
424
|
+
* Input accepted by {@link buildWaferMap}: one wafer's results
|
|
425
|
+
* ({@link WaferMapInputSingle}), an aggregated lot stack
|
|
426
|
+
* ({@link WaferMapInputLotStack}), or a die layout with no data
|
|
427
|
+
* ({@link WaferMapInputLayout}). Combining them is a type error.
|
|
395
428
|
*/
|
|
396
|
-
export type WaferMapInput = WaferMapInputSingle | WaferMapInputLotStack;
|
|
429
|
+
export type WaferMapInput = WaferMapInputSingle | WaferMapInputLotStack | WaferMapInputLayout;
|
|
397
430
|
/** Options forwarded to {@link buildView}. */
|
|
398
431
|
export interface WaferMapOptions extends ViewOptions {
|
|
399
432
|
debug?: boolean;
|
|
@@ -460,11 +493,11 @@ export interface WaferWarning {
|
|
|
460
493
|
* - `'test-count-capped'` — raised by `analyzeWaferMap`: more tests were found in
|
|
461
494
|
* the die data than the analysis cap allows, so test-value analysis was skipped
|
|
462
495
|
* entirely and NO test findings were produced. Pass `testNumbers` to scope it.
|
|
463
|
-
* - `'analysis-option-corrected'` — raised by `analyzeWaferMap
|
|
496
|
+
* - `'analysis-option-corrected'` — raised by `analyzeWaferMap` (or `buildWaferMap`, for `ringCount`): a numeric option
|
|
464
497
|
* was outside the range that can produce a meaningful analysis and was clamped.
|
|
465
498
|
* The message names each correction. Only reachable from untyped callers now
|
|
466
|
-
* that the statistical thresholds are internal, but a wrong `
|
|
467
|
-
* `
|
|
499
|
+
* that the statistical thresholds are internal, but a wrong `sectorCount`, or a
|
|
500
|
+
* wrong `ringCount` on `buildWaferMap`, still gets here from TypeScript.
|
|
468
501
|
* - `'edge-exclusion-exceeds-radius'` — `waferConfig.edgeExclusion` is larger than
|
|
469
502
|
* the resolved wafer radius (most likely when the diameter was itself inferred
|
|
470
503
|
* from sparse/partial data). The excluded band is clamped to the whole wafer
|
|
@@ -473,11 +506,30 @@ export interface WaferWarning {
|
|
|
473
506
|
* some bins are drawn in the same colour as another bin (more bins than the
|
|
474
507
|
* bin colour scheme has distinct colours, or a `BinDef.color` repeats one).
|
|
475
508
|
* Every die is drawn correctly; colour alone just cannot separate those bins.
|
|
509
|
+
* - `'pass-bins-mixed'` — raised by `renderWaferGallery`: the wafers shown were
|
|
510
|
+
* built with different pass bins, and some hard bins pass on one wafer and fail
|
|
511
|
+
* on another. Each wafer's verdicts and yield are its own and correct; a bin
|
|
512
|
+
* has one colour and one legend row, so those bins show as failing there.
|
|
513
|
+
* - `'ring-count-mixed'` — raised by `renderWaferGallery`: its wafers were built with
|
|
514
|
+
* different `ringCount`s. Each card and each wafer's findings use their own; the
|
|
515
|
+
* lot-level ring figures (Summary panel, report, Insights) use the count named in
|
|
516
|
+
* the message.
|
|
517
|
+
*
|
|
518
|
+
* - `'input-values-not-numbers'` — raised by `buildWaferMap`: bins or test values
|
|
519
|
+
* were given as text (or verdicts as something other than true/false), as a
|
|
520
|
+
* CSV parser produces. They are not converted, so those dies are judged and
|
|
521
|
+
* plotted wrongly — a bin of "1" is not pass bin 1. The message counts them.
|
|
522
|
+
* - `'input-field-removed'` — raised by `buildWaferMap`: the input used a name
|
|
523
|
+
* removed in an earlier release (`data`, `die`, `stack`, `values`,
|
|
524
|
+
* `TestDef.index`, `dieConfig.origin`, `waferConfig.flat`,
|
|
525
|
+
* `reticleConfig.anchor`, `lotStack.aggr`). It was not honoured, so what it
|
|
526
|
+
* described — for `data` and `values`, the data itself — is missing from the
|
|
527
|
+
* map. The message names each one and its replacement.
|
|
476
528
|
*
|
|
477
529
|
* The union is intentionally open to string so future advisory codes can be
|
|
478
530
|
* added without a breaking change; switch with a `default` branch.
|
|
479
531
|
*/
|
|
480
|
-
code: 'partial-coverage' | 'geometry-conflict' | 'non-standard-diameter' | 'diameter-exceeds-die-extent' | 'test-count-capped' | 'edge-exclusion-exceeds-radius' | 'analysis-option-corrected' | (string & {});
|
|
532
|
+
code: 'partial-coverage' | 'geometry-conflict' | 'non-standard-diameter' | 'diameter-exceeds-die-extent' | 'test-count-capped' | 'edge-exclusion-exceeds-radius' | 'analysis-option-corrected' | 'bin-colors-shared' | 'pass-bins-mixed' | 'ring-count-mixed' | 'input-field-removed' | 'input-values-not-numbers' | (string & {});
|
|
481
533
|
/** Human-readable explanation, suitable for direct display. */
|
|
482
534
|
message: string;
|
|
483
535
|
/**
|
|
@@ -562,14 +614,6 @@ export interface WaferMapResult {
|
|
|
562
614
|
grid: {
|
|
563
615
|
confidence: number;
|
|
564
616
|
};
|
|
565
|
-
/**
|
|
566
|
-
* @deprecated Use the promoted top-level `WaferMapResult.warnings` instead —
|
|
567
|
-
* it is always present (empty when none) and carries structured
|
|
568
|
-
* `{ code, message, confidence? }` entries you can branch on. This raw
|
|
569
|
-
* string array is retained for backward compatibility and mirrors the
|
|
570
|
-
* `message` of each structured warning.
|
|
571
|
-
*/
|
|
572
|
-
warnings?: string[];
|
|
573
617
|
};
|
|
574
618
|
/**
|
|
575
619
|
* Structured non-fatal advisories raised while inferring geometry from data.
|
|
@@ -599,6 +643,21 @@ export interface WaferMapResult {
|
|
|
599
643
|
/** `filledDies / totalDies` in [0, 1] — `0` when there are no positioned dies, even if `unpositionedDies` is large. */
|
|
600
644
|
ratio: number;
|
|
601
645
|
};
|
|
646
|
+
/**
|
|
647
|
+
* The bins that pass, as given to `buildWaferMap` (default `[1]`) — the ones
|
|
648
|
+
* `yield` was computed with. Carried so everything downstream judges pass/fail
|
|
649
|
+
* the same way: `renderWaferMap`, `renderWaferGallery` (per wafer) and
|
|
650
|
+
* `analyzeWaferMap` all read it, and none of them has a `passBins` option: this
|
|
651
|
+
* is the one place pass bins are set. A map not built by `buildWaferMap`
|
|
652
|
+
* states them in this same field.
|
|
653
|
+
*/
|
|
654
|
+
passBins: number[];
|
|
655
|
+
/**
|
|
656
|
+
* Rings the wafer is divided into (`WaferMapInput.ringCount`, default 4). The
|
|
657
|
+
* renderers, `analyzeWaferMap`, panels and reports read it and none has a ring
|
|
658
|
+
* count option of its own, so ring boundaries and ring findings always agree.
|
|
659
|
+
*/
|
|
660
|
+
ringCount: number;
|
|
602
661
|
/** Yield statistics computed against `passBins`. */
|
|
603
662
|
yield: YieldSummary;
|
|
604
663
|
/** Generated reticle geometry — pass as `viewOptions.reticles` to `renderWaferMap` to show the reticle overlay. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createWafer as j}from"../core/wafer.js";import{isYieldEligibleDie as ce,getDieKey as V,hasPosition as E}from"../core/dies.js";import{applyOrientation as q,transformDies as le}from"../core/transforms.js";import{affineRotation as ue,affineMirror as he,affineCompose as pe,affinePoint as me}from"../core/transforms.js";import{inferWaferFromXY as ge}from"../core/inference/wafer.js";import{resolveGridPitch as ye}from"../core/inference/pitch.js";import{assignGridIndices as we}from"../core/inference/grid.js";import{generateReticleGrid as xe}from"../core/reticle.js";import{buildView as H}from"./buildView.js";import{modeOf as De}from"../core/utils.js";import{aggregateValues as U,aggregateBinCounts as be}from"../core/aggregates.js";export function isParametricTest(t){return t?.testType!=="F"}function Me(t){if(!Array.isArray(t)&&"results"in t&&t.results!==void 0&&"lotStack"in t&&t.lotStack!==void 0)throw new Error("buildWaferMap: pass either `results` or `lotStack`, not both.");return Array.isArray(t)?{results:t,waferOpts:void 0,dieOpts:void 0,explicitDies:void 0,reticleOpts:void 0,lotStackOpts:void 0,passBins:[1],standardDiameters:void 0,testDefs:void 0,hbinDefs:void 0,sbinDefs:void 0,metadataFields:void 0,retestPolicy:"last",edgeDieYieldMode:"exclude"}:{results:t.results??[],waferOpts:t.waferConfig,dieOpts:t.dieConfig,explicitDies:t.dies,reticleOpts:t.reticleConfig,lotStackOpts:t.lotStack,passBins:t.passBins??[1],standardDiameters:t.standardDiameters,testDefs:t.testDefs?.length?t.testDefs:void 0,hbinDefs:t.hbinDefs,sbinDefs:t.sbinDefs,metadataFields:t.metadataFields,retestPolicy:t.retestPolicy??"last",edgeDieYieldMode:t.edgeDieYieldMode??"exclude"}}function Oe(t,n){return n?.coordinateOrigin?n.coordinateOrigin:{type:"center"}}function Re(t,n,e){if(n.type==="custom"&&n.offset)return{offsetX:n.offset.x,offsetY:n.offset.y};if(n.type!=="center"){let u=1/0,c=-1/0,a=1/0,l=-1/0;for(const s of t)s.x<u&&(u=s.x),s.x>c&&(c=s.x),s.y<a&&(a=s.y),s.y>l&&(l=s.y);return{offsetX:Math.round((c+u)/2),offsetY:Math.round((l+a)/2)}}return{offsetX:e.offsetX,offsetY:e.offsetY}}function Se(t,n,e,u,c,a){if(a)return{colMidX:(Math.round(a.x)-n)*u,colMidY:(Math.round(a.y)-e)*c,anchored:!0};if(t.length===0)return{colMidX:0,colMidY:0,anchored:!1};let l=1/0,s=-1/0,h=1/0,p=-1/0;for(const i of t){const f=Math.round(i.x)-n,r=Math.round(i.y)-e;f<l&&(l=f),f>s&&(s=f),r<h&&(h=r),r>p&&(p=r)}return{colMidX:(l+s)/2*u,colMidY:(h+p)/2*c,anchored:!1}}function Ee(t,n,e){let u=1/0,c=-1/0,a=1/0,l=-1/0,s=0,h=0;for(const f of t){const r=Math.round(f.x)-n,d=Math.round(f.y)-e;s+=r,h+=d,r<u&&(u=r),r>c&&(c=r),d<a&&(a=d),d>l&&(l=d)}const p=t.length,i=(f,r,d)=>{const m=(d-r)/2;return m<=0?0:Math.abs(f/p-(r+d)/2)/m};return i(s,u,c)>.11||i(h,a,l)>.11}function ve(t,n){let e=t?.xAxisDirection==="left",u=t?.yAxisDirection==="down";return(n.type==="UL"||n.type==="UR")&&(u=!0),(n.type==="LR"||n.type==="UR")&&(e=!0),{flipX:e,flipY:u}}function ke(t,n){const{method:e,targetBin:u}=t,c=t.results.map(a=>a.filter(E));if(e==="mean"||e==="median"||e==="stddev"||e==="min"||e==="max"||e==="count"){const a=new Set((n??[]).filter(i=>!isParametricTest(i)).map(i=>i.testNumber)),l=new Set;for(const i of c)for(const f of i)if(f.testValues)for(const r of Object.keys(f.testValues)){const d=Number(r);a.has(d)||l.add(d)}if(l.size===0)return U(c,e);const s=[...l],h=s.map(i=>U(c,e,i)),p=new Map;for(let i=0;i<s.length;i++){const f=s[i];for(const r of h[i]){const d=V(r);p.has(d)||p.set(d,{template:r,testValues:{}});const m=p.get(d),g=r.testValues?.[0];g!==void 0&&(m.testValues[f]=g)}}return[...p.values()].map(({template:i,testValues:f})=>({...i,testValues:Object.keys(f).length>0?f:void 0}))}if(e==="countBin"||e==="percent"){if(u===void 0)return[];const a=be(c,u,"hard");if(e==="countBin")return a;const l=c.length;return a.map(s=>({...s,testValues:{0:l>0?(s.testValues?.[0]??0)/l*100:0}}))}if(e==="mode"){const a=new Map;for(const s of c)for(const h of s){const p=V(h);a.has(p)||a.set(p,[]),a.get(p).push(h)}const l=[];for(const[s,h]of a){const p=s.split(","),i=Number(p[0]),f=Number(p[1]),r=h.map(m=>m.hbin).filter(m=>m!==void 0),d=De(r);d!==null&&l.push({x:i,y:f,hbin:d})}return l}return[]}function J(t){const n=t.filter(E),e=n.length,u=n.filter(a=>a.edgeExcluded).length,c=n.filter(a=>dieHasTestData(a)||a.hbin!==void 0||a.sbin!==void 0).length;return{filledDies:c,totalDies:e,edgeExcludedDies:u,unpositionedDies:t.length-n.length,ratio:e>0?c/e:0}}function Q(t,n,e="exclude"){const u=new Set(n),a=t.filter(d=>!d.partial).filter(d=>d.edgeExcluded).length,l=t.filter(d=>d.partial).length;let s=0,h=0,p=!1;for(const d of t){if(!ce(d))continue;const m=d.hbin??d.sbin;m!==void 0&&(p=!0,u.has(m)?s++:h++)}const i=s+h,f=p&&i>0?s/i*100:null;let r=null;if(e==="denominator-only"){const d=i+a;r=p&&d>0?s/d*100:null}return{passDies:s,failDies:h,edgeExcludedDies:a,partialDies:l,totalDies:i,yieldPercent:f,yieldPercentGross:r}}function Z(t,n,e,u,c,a=0,l=0,s=0,h=0,p=0,i=!1,f=!1){if(!t)return[];const r=t.anchorDie??{x:0,y:0},d=xe(n,{width:t.width,height:t.height,diePitchX:u,diePitchY:c,anchorDie:{x:r.x-a,y:r.y-l},gridOrigin:{x:-s,y:-h}}),m=pe(he(i,f,n.center.x,n.center.y),ue(p,n.center.x,n.center.y));return d.filter(g=>{const v=g.width/2,D=g.height/2,b=[[g.x-v,g.y-D],[g.x+v,g.y-D],[g.x+v,g.y+D],[g.x-v,g.y+D]].map(([w,x])=>me(m,w,x)),k=Math.min(...b.map(w=>w.x)),A=Math.max(...b.map(w=>w.x)),F=Math.min(...b.map(w=>w.y)),P=Math.max(...b.map(w=>w.y));return e.some(w=>w.physX>=k&&w.physX<A&&w.physY>=F&&w.physY<P)})}function Ae(t,n,e){const c=Math.max(0,n.radius-e)**2;return t.map(a=>{const l=a.physX-n.center.x,s=a.physY-n.center.y;return l*l+s*s>c?{...a,edgeExcluded:!0}:a})}function Ce(t,n,e){const u=t.filter(E),c=t.filter(i=>!E(i)),a=new Map;for(const i of u){let f=a.get(i.x);f||(f=new Map,a.set(i.x,f)),f.set(i.y,(f.get(i.y)??0)+1)}const l=new Set(e);function s(i,f){const r=i.hbin,d=f.hbin;if(r===void 0||d===void 0)return!1;const m=l.has(r),g=l.has(d);return n==="best"?g!==m?g:d<r:g!==m?m:d>r}const h=new Map;for(const i of u){const f=V(i),r=h.get(f);n==="first"&&r||(n==="best"||n==="worst")&&r&&!s(r,i)||h.set(f,i)}return[...Array.from(h.values()).map(i=>{const r=a.get(i.x)?.get(i.y)??1;return r>1?{...i,retestCount:r}:i}),...c]}export function getDieTestValue(t,n){return t.testValues?.[n]}export function getTestPassStatus(t,n,e){const u=t.testPass?.[n];if(u!==void 0)return u;if(e!==void 0&&!isParametricTest(e)){const c=t.testValues?.[n];if(c===0||c===1)return c===1}}export function dieHasTestData(t){return t.testValues!==void 0&&Object.keys(t.testValues).length>0||t.testPass!==void 0&&Object.keys(t.testPass).length>0}function L(t,n){const e={};return n.hbin!==void 0&&(e.hbin=n.hbin),n.sbin!==void 0&&(e.sbin=n.sbin),n.retestCount!==void 0&&(e.retestCount=n.retestCount),n.siteNum!==void 0&&(e.siteNum=n.siteNum),n.partId!==void 0&&(e.partId=n.partId),n.metadata!==void 0&&(e.metadata=n.metadata),n.testPass!==void 0&&(e.testPass=n.testPass),n.testValues!==void 0&&(e.testValues=n.testValues),{...t,...e}}function ee(t,n){return n.plotMode?n.plotMode:t.some(dieHasTestData)?"value":"hardBin"}function te(t){return(t.warnings??[]).map(n=>{const e=Ie(n);return{code:e,message:n,severity:e==="non-standard-diameter"||e==="diameter-exceeds-die-extent"?"warning":"error",confidence:t.wafer.confidence}})}function Ie(t){return t.includes(ne)?"geometry-conflict":t.includes(ie)?"non-standard-diameter":t.includes(se)?"diameter-exceeds-die-extent":"partial-coverage"}const ne="do not fit inside the supplied",ie="Inferred wafer diameter",se="The probed dies reach only",Fe=.75,Pe=20;export const STANDARD_WAFER_DIAMETERS_MM=[100,125,150,200,300];const _e=.02;function oe(t,n){return n.reduce((e,u)=>Math.abs(u-t)<Math.abs(e-t)?u:e)}function Ye(t,n){if(!n.length)return!0;const e=oe(t,n);return Math.abs(e-t)/e<=_e}function $e(t){return t===void 0?STANDARD_WAFER_DIAMETERS_MM:t.filter(n=>Number.isFinite(n)&&n>0)}export function buildWaferMap(t,n){const e=Me(t),{debug:u,...c}=n??{},a=e.lotStackOpts?ke(e.lotStackOpts,e.testDefs):e.results;for(let o=0;o<Math.min(5,a.length);o++){const y=a[o];if(typeof y.x=="string"||typeof y.y=="string")throw new TypeError("buildWaferMap: x and y must be numbers, received strings. Did you forget to cast CSV values? e.g. { x: +row.x, y: +row.y }")}for(const o of a)if(o.x==null!=(o.y==null))throw new TypeError(`buildWaferMap: die has only one of x/y set (x=${String(o.x)}, y=${String(o.y)}). A die must be either fully positioned (both x and y) or fully unpositioned (neither) \u2014 omit both to mark a die as having no reported position.`);const l=Ce(a,e.retestPolicy,e.passBins),s={wafer:{confidence:1,method:"provided"},diePitch:{confidence:1,units:"mm"},grid:{confidence:1}};if(e.explicitDies){let o=e.explicitDies;const y=l.filter(E),R=l.filter(T=>!E(T));if(y.length>0){const T=new Map(y.map(S=>[V(S),S]));o=o.map(S=>{const G=T.get(V(S));return G?L(S,G):S})}const $=e.waferOpts?.diameter??300,I=j({diameter:$,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});I.orientation!==0&&(o=q(o,I));const z=Z(e.reticleOpts,I,o,1,1,0,0,0,0,I.orientation),de=c.showReticle??e.reticleOpts!==void 0,B=H(I,o,{...c,reticles:z,showReticle:de,plotMode:ee(l,c),testDefs:e.testDefs,isLotStack:!1},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),fe=R.map((T,S)=>L({id:`unpositioned_${S}`,width:o[0]?.width??1,height:o[0]?.height??1},T)),W=[...o,...fe];return{wafer:I,dies:W,view:B,reticleConfig:e.reticleOpts,units:"mm",inference:s,warnings:te(s),plotMode:B.plotMode,metadata:B.metadata,isLotStack:!1,dataCoverage:J(W),yield:Q(W,e.passBins,e.edgeDieYieldMode),reticles:z,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields}}const h=l.filter(E),p=l.filter(o=>!E(o)),i=h.map(o=>({x:o.x,y:o.y})),f=ye(i,e.dieOpts,e.waferOpts?.diameter);s.diePitch={confidence:f.confidence,units:f.units};const{pitchX:r,pitchY:d}=f,m=f.units,g=Oe(h,e.dieOpts),v=we(i);s.grid={confidence:v.confidence};const{offsetX:D,offsetY:b}=Re(i,g,v),{flipX:k,flipY:A}=ve(e.dieOpts,g),{colMidX:F,colMidY:P,anchored:w}=Se(i,D,b,r,d,e.waferOpts?.center);let x=e.waferOpts?.diameter;const _=i.map(o=>({x:(Math.round(o.x)-D)*r-F,y:(Math.round(o.y)-b)*d-P})),C=_.length>0?Math.max(..._.map(({x:o,y})=>Math.hypot(Math.abs(o)+r/2,Math.abs(y)+d/2))):0;if(x===void 0)if(i.length>0)if(m==="mm"){const o=ge(_,{minRadius:C});x=o.diameter,s.wafer={confidence:o.confidence,method:o.method};const y=$e(e.standardDiameters);if(!Ye(x,y)){const R=oe(x,y);(s.warnings??=[]).push(`${ie} ${x.toFixed(1)} mm is not a standard wafer size (nearest is ${R} mm). The diameter was derived from the die extent, which assumes the probed grid reaches the wafer edge \u2014 a non-standard result usually means it does not, because the outer dies were never probed. Every die is then placed against a wafer that is too small, which moves dies between rings and changes ring and edge findings. Supply waferConfig.diameter.`)}}else x=C*2,s.wafer={confidence:f.confidence*.8,method:"extent"};else x=m==="mm"?300:30,s.wafer={confidence:0,method:"default"};if(!w&&i.length>0&&Ee(i,D,b)&&(s.wafer.method="inferred-partial",(s.warnings??=[]).push("Wafer geometry was inferred from die positions alone. The data does not span a full symmetric wafer, so the inferred diameter and centre may be wrong and dies may be mis-positioned relative to the true wafer boundary. Supply waferConfig.diameter and waferConfig.center (the prober coordinate of the wafer centre) to position partial data correctly.")),e.waferOpts?.diameter!==void 0&&e.waferOpts.center===void 0&&C>0&&i.length>=Pe){const o=C/(x/2);o<Fe&&(s.warnings??=[]).push(`${se} ${(o*100).toFixed(0)}% of the ${x} mm wafer's radius. Either the diameter is larger than the real one, or this is a partial map covering only part of the wafer. Ring bands are equal-radius, so the outer rings are mostly or entirely empty and ring, quadrant and edge findings describe the assumed wafer rather than the probed area. Check waferConfig.diameter, or drop it and let the diameter be inferred from the die extent.`)}if(e.waferOpts?.diameter!==void 0){const o=e.dieOpts?.width!==void 0&&e.dieOpts?.height!==void 0;if(o&&C>x/2+1e-9){const y=_.filter(({x:R,y:$})=>Math.hypot(Math.abs(R)+r/2,Math.abs($)+d/2)>x/2+1e-9).length;(s.warnings??=[]).push(`${y} of ${_.length} probed die positions ${ne} ${x} mm wafer at the supplied die pitch of ${r} \xD7 ${d} mm. A die with test results is a real prober position and is always fully on the wafer, so one of the two supplied values must be wrong: containing these dies at this pitch would need a diameter of at least ${(C*2).toFixed(1)} mm. Check waferConfig.diameter and dieConfig.width/height against the real device.`)}}const O=j({diameter:x,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});let M=h.map(o=>{const y=Math.round(o.x)-D,R=Math.round(o.y)-b,$={id:`${y}_${R}`,x:y,y:R,physX:y*r-F,physY:R*d-P,width:r,height:d,insideWafer:!0,partial:!1};return L($,o)});(D!==0||b!==0)&&(M=M.map(o=>({...o,x:o.x+D,y:o.y+b,id:`${o.x+D}_${o.y+b}`}))),M=q(M,O),(k||A)&&(M=le(M,{flipX:k,flipY:A},O.center));const X=[];e.waferOpts?.edgeExclusion&&e.waferOpts.edgeExclusion>0&&(M=Ae(M,O,e.waferOpts.edgeExclusion),e.waferOpts.edgeExclusion>O.radius&&X.push({code:"edge-exclusion-exceeds-radius",message:`edgeExclusion (${e.waferOpts.edgeExclusion}mm) exceeds the resolved wafer radius (${O.radius}mm) \u2014 the entire wafer is excluded.`,severity:"warning"}));const K=Z(e.reticleOpts,O,M,r,d,D,b,F,P,O.orientation,k,A),re=c.showReticle??e.reticleOpts!==void 0,Y=H(O,M,{...c,reticles:K,showReticle:re,plotMode:ee(l,c),testDefs:e.testDefs,dataAxisFlip:{x:k,y:A},isLotStack:e.lotStackOpts!==void 0,aggregationMethod:e.lotStackOpts?.method,lotSize:e.lotStackOpts?.results.length},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),ae=p.map((o,y)=>L({id:`unpositioned_${y}`,width:r,height:d},o)),N=[...M,...ae];return{wafer:O,dies:N,view:Y,reticleConfig:e.reticleOpts,units:m,inference:s,warnings:[...te(s),...X],plotMode:Y.plotMode,metadata:Y.metadata,isLotStack:e.lotStackOpts!==void 0,dataCoverage:J(N),yield:Q(N,e.passBins,e.edgeDieYieldMode),reticles:K,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields,aggrMethod:Y.aggrMethod,lotSize:Y.lotSize}}
|
|
1
|
+
import{createWafer as j}from"../core/wafer.js";import{isYieldEligibleDie as le,getDieKey as T,hasPosition as A}from"../core/dies.js";import{applyOrientation as H,transformDies as ue,clipDiesToWafer as he}from"../core/transforms.js";import{generateDies as me}from"../core/dies.js";import{affineRotation as ge,affineMirror as pe,affineCompose as ye,affinePoint as be}from"../core/transforms.js";import{inferWaferFromXY as we}from"../core/inference/wafer.js";import{resolveGridPitch as xe}from"../core/inference/pitch.js";import{assignGridIndices as De}from"../core/inference/grid.js";import{generateReticleGrid as Me}from"../core/reticle.js";import{buildView as U}from"./buildView.js";import{modeOf as Ce}from"../core/utils.js";import{aggregateValues as J,aggregateBinCounts as ve}from"../core/aggregates.js";export function isParametricTest(t){return t?.testType!=="F"}function Oe(t){const n=(l,s)=>({ringCount:l,ringCountWarning:{code:"analysis-option-corrected",severity:"warning",message:`ringCount=${String(t)} ${s} (using ${l}).`}});if(t===void 0)return{ringCount:4,ringCountWarning:void 0};if(typeof t!="number"||!Number.isFinite(t))return n(4,"is not a finite number");const e=Math.max(1,Math.round(t));return e===t?{ringCount:t,ringCountWarning:void 0}:n(e,"is not a whole number of at least 1")}function Q(t){const n=Array.isArray(t)?{}:t,e=Array.isArray(n.lotStack?.results)?n.lotStack.results:[],l=(Array.isArray(t)?[t]:[n.results,...e]).filter(a=>Array.isArray(a)),s={hbin:0,sbin:0,testValues:0,testPass:0};let r;const h=(a,i)=>{s[a]++,r??=`${a} ${JSON.stringify(i)}`};for(const a of l){const i=new Set,f=new Set;for(let p=0;p<a.length;p+=p<50?1:100){const m=a[p];if(!(m===null||typeof m!="object")){if(m.testValues&&typeof m.testValues=="object")for(const D of Object.keys(m.testValues))i.add(D);if(m.testPass&&typeof m.testPass=="object")for(const D of Object.keys(m.testPass))f.add(D)}}const u=[...i],d=[...f];for(const p of a){if(p===null||typeof p!="object")continue;const m=p;m.hbin!=null&&typeof m.hbin!="number"&&h("hbin",m.hbin),m.sbin!=null&&typeof m.sbin!="number"&&h("sbin",m.sbin);const D=m.testValues;if(D&&typeof D=="object")for(let y=0;y<u.length;y++){const b=D[u[y]];b!=null&&typeof b!="number"&&h("testValues",b)}const C=m.testPass;if(C&&typeof C=="object")for(let y=0;y<d.length;y++){const b=C[d[y]];b!=null&&typeof b!="boolean"&&h("testPass",b)}}}const o=[];if(s.hbin&&o.push(`${s.hbin} hard bin${s.hbin===1?"":"s"}`),s.sbin&&o.push(`${s.sbin} soft bin${s.sbin===1?"":"s"}`),s.testValues&&o.push(`${s.testValues} test value${s.testValues===1?"":"s"}`),s.testPass&&o.push(`${s.testPass} pass/fail verdict${s.testPass===1?"":"s"}`),!o.length)return;const g=`buildWaferMap received ${o.join(", ")} of the wrong type (for example ${r}). Bins and test values must be numbers and verdicts true/false \u2014 a bin of "1" is not pass bin 1, so those dies count as fails and yield is wrong, and a test value given as text is not plotted or analysed correctly. Convert them with Number() (and verdicts to booleans) and rebuild.`;return console.warn(`[wafermap] ${g}`),{code:"input-values-not-numbers",message:g,severity:"error"}}function Z(t){const n=(a,i)=>a!==null&&typeof a=="object"&&a[i]!==void 0,e=Array.isArray(t)?{}:t,l=[],s=(a,i,f)=>{a&&l.push(`\`${i}\` (now \`${f}\`)`)};s(n(e,"data"),"data","results"),s(n(e,"die"),"die","dieConfig"),s(n(e,"stack"),"stack","lotStack"),s(n(e.dieConfig,"origin"),"dieConfig.origin","dieConfig.coordinateOrigin"),s(n(e.waferConfig,"flat"),"waferConfig.flat","waferConfig.notch"),s(n(e.reticleConfig,"anchor"),"reticleConfig.anchor","reticleConfig.anchorDie"),s(n(e.lotStack,"aggr"),"lotStack.aggr","lotStack.method"),s(Array.isArray(e.testDefs)&&e.testDefs.some(a=>n(a,"index")&&!n(a,"testNumber")),"TestDef.index","TestDef.testNumber");const r=n(e.lotStack,"results")?e.lotStack.results:[],h=(Array.isArray(t)?[t]:[e.results,...Array.isArray(r)?r:[]]).filter(a=>Array.isArray(a));if(s(h.some(a=>a.slice(0,100).some(i=>n(i,"values"))),"values","testValues"),!l.length)return;const o=l.length>1,g=`buildWaferMap ignored ${l.join(", ")}: ${o?"these names were":"that name was"} removed in an earlier release, so what ${o?"they":"it"} described is not on this map. Rename and rebuild.`;return console.warn(`[wafermap] ${g}`),{code:"input-field-removed",message:g,severity:"error"}}function Se(t){if(t.results!==void 0||t.lotStack!==void 0)throw new Error("buildWaferMap: pass `layout`, `results` or `lotStack` \u2014 only one.");const n=t.waferConfig?.diameter,e=t.dieConfig?.width,l=t.dieConfig?.height;if(!(n>0&&e>0&&l>0))throw new Error("buildWaferMap: `layout` needs waferConfig.diameter and dieConfig.width and height, all positive.");const s=j({diameter:n,notch:t.waferConfig?.notch,orientation:0}),r=he(me(s,{width:e,height:l}),s,{width:e,height:l}).filter(i=>!i.partial),{flipX:h,flipY:o}=ee(t.dieConfig,{type:"center"}),{layout:g,...a}=t;return{...a,results:r.map(i=>({x:h?-i.x:i.x,y:o?-i.y:i.y})),waferConfig:{...t.waferConfig,center:{x:0,y:0}},dieConfig:{...t.dieConfig,coordinateOrigin:{type:"center"}}}}function ke(t){if(!Array.isArray(t)&&"results"in t&&t.results!==void 0&&"lotStack"in t&&t.lotStack!==void 0)throw new Error("buildWaferMap: pass either `results` or `lotStack`, not both.");return Array.isArray(t)?{results:t,waferOpts:void 0,dieOpts:void 0,explicitDies:void 0,reticleOpts:void 0,lotStackOpts:void 0,passBins:[1],ringCount:4,ringCountWarning:void 0,removedFieldWarning:Z(t),nonNumericWarning:Q(t),standardDiameters:void 0,testDefs:void 0,hbinDefs:void 0,sbinDefs:void 0,metadataFields:void 0,retestPolicy:"last",edgeDieYieldMode:"exclude"}:{results:t.results??[],waferOpts:t.waferConfig,dieOpts:t.dieConfig,explicitDies:t.dies,reticleOpts:t.reticleConfig,lotStackOpts:t.lotStack,passBins:t.passBins??[1],...Oe(t.ringCount),removedFieldWarning:Z(t),nonNumericWarning:Q(t),standardDiameters:t.standardDiameters,testDefs:t.testDefs?.length?t.testDefs:void 0,hbinDefs:t.hbinDefs,sbinDefs:t.sbinDefs,metadataFields:t.metadataFields,retestPolicy:t.retestPolicy??"last",edgeDieYieldMode:t.edgeDieYieldMode??"exclude"}}function Ae(t,n){return n?.coordinateOrigin?n.coordinateOrigin:{type:"center"}}function Re(t,n,e){if(n.type==="custom"&&n.offset)return{offsetX:n.offset.x,offsetY:n.offset.y};if(n.type!=="center"){let l=1/0,s=-1/0,r=1/0,h=-1/0;for(const o of t)o.x<l&&(l=o.x),o.x>s&&(s=o.x),o.y<r&&(r=o.y),o.y>h&&(h=o.y);return{offsetX:Math.round((s+l)/2),offsetY:Math.round((h+r)/2)}}return{offsetX:e.offsetX,offsetY:e.offsetY}}function $e(t,n,e,l,s,r){if(r)return{colMidX:(Math.round(r.x)-n)*l,colMidY:(Math.round(r.y)-e)*s,anchored:!0};if(t.length===0)return{colMidX:0,colMidY:0,anchored:!1};let h=1/0,o=-1/0,g=1/0,a=-1/0;for(const i of t){const f=Math.round(i.x)-n,u=Math.round(i.y)-e;f<h&&(h=f),f>o&&(o=f),u<g&&(g=u),u>a&&(a=u)}return{colMidX:(h+o)/2*l,colMidY:(g+a)/2*s,anchored:!1}}function We(t,n,e){let l=1/0,s=-1/0,r=1/0,h=-1/0,o=0,g=0;for(const f of t){const u=Math.round(f.x)-n,d=Math.round(f.y)-e;o+=u,g+=d,u<l&&(l=u),u>s&&(s=u),d<r&&(r=d),d>h&&(h=d)}const a=t.length,i=(f,u,d)=>{const p=(d-u)/2;return p<=0?0:Math.abs(f/a-(u+d)/2)/p};return i(o,l,s)>.11||i(g,r,h)>.11}function ee(t,n){let e=t?.xAxisDirection==="left",l=t?.yAxisDirection==="down";return(n.type==="UL"||n.type==="UR")&&(l=!0),(n.type==="LR"||n.type==="UR")&&(e=!0),{flipX:e,flipY:l}}function Ee(t,n){const{method:e,targetBin:l}=t,s=t.results.map(r=>r.filter(A));if(e==="mean"||e==="median"||e==="stddev"||e==="min"||e==="max"||e==="count"){const r=new Set((n??[]).filter(i=>!isParametricTest(i)).map(i=>i.testNumber)),h=new Set;for(const i of s)for(const f of i)if(f.testValues)for(const u of Object.keys(f.testValues)){const d=Number(u);r.has(d)||h.add(d)}if(h.size===0)return J(s,e);const o=[...h],g=o.map(i=>J(s,e,i)),a=new Map;for(let i=0;i<o.length;i++){const f=o[i];for(const u of g[i]){const d=T(u);a.has(d)||a.set(d,{template:u,testValues:{}});const p=a.get(d),m=u.testValues?.[0];m!==void 0&&(p.testValues[f]=m)}}return[...a.values()].map(({template:i,testValues:f})=>({...i,testValues:Object.keys(f).length>0?f:void 0}))}if(e==="countBin"||e==="percent"){if(l===void 0)return[];const r=ve(s,l,"hard");if(e==="countBin")return r;const h=s.length;return r.map(o=>({...o,testValues:{0:h>0?(o.testValues?.[0]??0)/h*100:0}}))}if(e==="mode"){const r=new Map;for(const o of s)for(const g of o){const a=T(g);r.has(a)||r.set(a,[]),r.get(a).push(g)}const h=[];for(const[o,g]of r){const a=o.split(","),i=Number(a[0]),f=Number(a[1]),u=g.map(p=>p.hbin).filter(p=>p!==void 0),d=Ce(u);d!==null&&h.push({x:i,y:f,hbin:d})}return h}return[]}function te(t){const n=t.filter(A),e=n.length,l=n.filter(r=>r.edgeExcluded).length,s=n.filter(r=>dieHasTestData(r)||r.hbin!==void 0||r.sbin!==void 0).length;return{filledDies:s,totalDies:e,edgeExcludedDies:l,unpositionedDies:t.length-n.length,ratio:e>0?s/e:0}}function ne(t,n,e="exclude"){const l=new Set(n),r=t.filter(d=>!d.partial).filter(d=>d.edgeExcluded).length,h=t.filter(d=>d.partial).length;let o=0,g=0,a=!1;for(const d of t){if(!le(d))continue;const p=d.hbin??d.sbin;p!==void 0&&(a=!0,l.has(p)?o++:g++)}const i=o+g,f=a&&i>0?o/i*100:null;let u=null;if(e==="denominator-only"){const d=i+r;u=a&&d>0?o/d*100:null}return{passDies:o,failDies:g,edgeExcludedDies:r,partialDies:h,totalDies:i,yieldPercent:f,yieldPercentGross:u}}function se(t,n,e,l,s,r=0,h=0,o=0,g=0,a=0,i=!1,f=!1){if(!t)return[];const u=t.anchorDie??{x:0,y:0},d=Me(n,{width:t.width,height:t.height,diePitchX:l,diePitchY:s,anchorDie:{x:u.x-r,y:u.y-h},gridOrigin:{x:-o,y:-g}}),p=ye(pe(i,f,n.center.x,n.center.y),ge(a,n.center.x,n.center.y));return d.filter(m=>{const D=m.width/2,C=m.height/2,y=[[m.x-D,m.y-C],[m.x+D,m.y-C],[m.x+D,m.y+C],[m.x-D,m.y+C]].map(([w,B])=>be(p,w,B)),b=Math.min(...y.map(w=>w.x)),R=Math.max(...y.map(w=>w.x)),$=Math.min(...y.map(w=>w.y)),F=Math.max(...y.map(w=>w.y));return e.some(w=>w.physX>=b&&w.physX<R&&w.physY>=$&&w.physY<F)})}function Fe(t,n,e){const s=Math.max(0,n.radius-e)**2;return t.map(r=>{const h=r.physX-n.center.x,o=r.physY-n.center.y;return h*h+o*o>s?{...r,edgeExcluded:!0}:r})}function Pe(t,n,e){const l=t.filter(A),s=t.filter(i=>!A(i)),r=new Map;for(const i of l){let f=r.get(i.x);f||(f=new Map,r.set(i.x,f)),f.set(i.y,(f.get(i.y)??0)+1)}const h=new Set(e);function o(i,f){const u=i.hbin,d=f.hbin;if(u===void 0||d===void 0)return!1;const p=h.has(u),m=h.has(d);return n==="best"?m!==p?m:d<u:m!==p?p:d>u}const g=new Map;for(const i of l){const f=T(i),u=g.get(f);n==="first"&&u||(n==="best"||n==="worst")&&u&&!o(u,i)||g.set(f,i)}return[...Array.from(g.values()).map(i=>{const u=r.get(i.x)?.get(i.y)??1;return u>1?{...i,retestCount:u}:i}),...s]}export function getDieTestValue(t,n){return t.testValues?.[n]}export function getTestPassStatus(t,n,e){const l=t.testPass?.[n];if(l!==void 0)return l;if(e!==void 0&&!isParametricTest(e)){const s=t.testValues?.[n];if(s===0||s===1)return s===1}}export function dieHasTestData(t){return t.testValues!==void 0&&Object.keys(t.testValues).length>0||t.testPass!==void 0&&Object.keys(t.testPass).length>0}function _(t,n){const e={};return n.hbin!==void 0&&(e.hbin=n.hbin),n.sbin!==void 0&&(e.sbin=n.sbin),n.retestCount!==void 0&&(e.retestCount=n.retestCount),n.siteNum!==void 0&&(e.siteNum=n.siteNum),n.partId!==void 0&&(e.partId=n.partId),n.metadata!==void 0&&(e.metadata=n.metadata),n.testPass!==void 0&&(e.testPass=n.testPass),n.testValues!==void 0&&(e.testValues=n.testValues),{...t,...e}}function ie(t,n){return n.plotMode?n.plotMode:t.some(dieHasTestData)?"value":"hardBin"}function oe(t,n){return t.map(({code:e,message:l})=>({code:e,message:l,severity:e==="non-standard-diameter"||e==="diameter-exceeds-die-extent"?"warning":"error",confidence:n.wafer.confidence}))}const Ie="do not fit inside the supplied",Ne="Inferred wafer diameter",Ve="The probed dies reach only",Te=.75,_e=20;export const STANDARD_WAFER_DIAMETERS_MM=[100,125,150,200,300];const Be=.02;function re(t,n){return n.reduce((e,l)=>Math.abs(l-t)<Math.abs(e-t)?l:e)}function Ye(t,n){if(!n.length)return!0;const e=re(t,n);return Math.abs(e-t)/e<=Be}function Le(t){return t===void 0?STANDARD_WAFER_DIAMETERS_MM:t.filter(n=>Number.isFinite(n)&&n>0)}export function buildWaferMap(t,n){const e=ke(!Array.isArray(t)&&t.layout?Se(t):t),{debug:l,...s}=n??{},r=e.lotStackOpts?Ee(e.lotStackOpts,e.testDefs):e.results;for(let c=0;c<Math.min(5,r.length);c++){const x=r[c];if(typeof x.x=="string"||typeof x.y=="string")throw new TypeError("buildWaferMap: x and y must be numbers, received strings. Did you forget to cast CSV values? e.g. { x: +row.x, y: +row.y }")}for(const c of r)if(c.x==null!=(c.y==null))throw new TypeError(`buildWaferMap: die has only one of x/y set (x=${String(c.x)}, y=${String(c.y)}). A die must be either fully positioned (both x and y) or fully unpositioned (neither) \u2014 omit both to mark a die as having no reported position.`);const h=Pe(r,e.retestPolicy,e.passBins),o={wafer:{confidence:1,method:"provided"},diePitch:{confidence:1,units:"mm"},grid:{confidence:1}},g=[];if(e.explicitDies){let c=e.explicitDies;const x=h.filter(A),S=h.filter(V=>!A(V));if(x.length>0){const V=new Map(x.map(k=>[T(k),k]));c=c.map(k=>{const q=V.get(T(k));return q?_(k,q):k})}const N=e.waferOpts?.diameter??300,E=j({diameter:N,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});E.orientation!==0&&(c=H(c,E));const G=se(e.reticleOpts,E,c,1,1,0,0,0,0,E.orientation),fe=s.showReticle??e.reticleOpts!==void 0,L=U(E,c,{...s,reticles:G,showReticle:fe,plotMode:ie(h,s),testDefs:e.testDefs,passBins:e.passBins,ringCount:e.ringCount,isLotStack:!1},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),ce=S.map((V,k)=>_({id:`unpositioned_${k}`,width:c[0]?.width??1,height:c[0]?.height??1},V)),X=[...c,...ce];return{wafer:E,dies:X,view:L,reticleConfig:e.reticleOpts,units:"mm",inference:o,warnings:[...e.removedFieldWarning?[e.removedFieldWarning]:[],...e.nonNumericWarning?[e.nonNumericWarning]:[],...oe(g,o),...e.ringCountWarning?[e.ringCountWarning]:[]],plotMode:L.plotMode,metadata:L.metadata,isLotStack:!1,dataCoverage:te(X),passBins:e.passBins,ringCount:e.ringCount,yield:ne(X,e.passBins,e.edgeDieYieldMode),reticles:G,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields}}const a=h.filter(A),i=h.filter(c=>!A(c)),f=a.map(c=>({x:c.x,y:c.y})),u=xe(f,e.dieOpts,e.waferOpts?.diameter);o.diePitch={confidence:u.confidence,units:u.units};const{pitchX:d,pitchY:p}=u,m=u.units,D=Ae(a,e.dieOpts),C=De(f);o.grid={confidence:C.confidence};const{offsetX:y,offsetY:b}=Re(f,D,C),{flipX:R,flipY:$}=ee(e.dieOpts,D),{colMidX:F,colMidY:w,anchored:B}=$e(f,y,b,d,p,e.waferOpts?.center);let M=e.waferOpts?.diameter;const P=f.map(c=>({x:(Math.round(c.x)-y)*d-F,y:(Math.round(c.y)-b)*p-w})),W=P.length>0?Math.max(...P.map(({x:c,y:x})=>Math.hypot(Math.abs(c)+d/2,Math.abs(x)+p/2))):0;if(M===void 0)if(f.length>0)if(m==="mm"){const c=we(P,{minRadius:W});M=c.diameter,o.wafer={confidence:c.confidence,method:c.method};const x=Le(e.standardDiameters);if(!Ye(M,x)){const S=re(M,x);g.push({code:"non-standard-diameter",message:`${Ne} ${M.toFixed(1)} mm is not a standard wafer size (nearest is ${S} mm). The diameter was derived from the die extent, which assumes the probed grid reaches the wafer edge \u2014 a non-standard result usually means it does not, because the outer dies were never probed. Every die is then placed against a wafer that is too small, which moves dies between rings and changes ring and edge findings. Supply waferConfig.diameter.`})}}else M=W*2,o.wafer={confidence:u.confidence*.8,method:"extent"};else M=m==="mm"?300:30,o.wafer={confidence:0,method:"default"};if(!B&&f.length>0&&We(f,y,b)&&(o.wafer.method="inferred-partial",g.push({code:"partial-coverage",message:"Wafer geometry was inferred from die positions alone. The data does not span a full symmetric wafer, so the inferred diameter and centre may be wrong and dies may be mis-positioned relative to the true wafer boundary. Supply waferConfig.diameter and waferConfig.center (the prober coordinate of the wafer centre) to position partial data correctly."})),e.waferOpts?.diameter!==void 0&&e.waferOpts.center===void 0&&W>0&&f.length>=_e){const c=W/(M/2);c<Te&&g.push({code:"diameter-exceeds-die-extent",message:`${Ve} ${(c*100).toFixed(0)}% of the ${M} mm wafer's radius. Either the diameter is larger than the real one, or this is a partial map covering only part of the wafer. Ring bands are equal-radius, so the outer rings are mostly or entirely empty and ring, quadrant and edge findings describe the assumed wafer rather than the probed area. Check waferConfig.diameter, or drop it and let the diameter be inferred from the die extent.`})}if(e.waferOpts?.diameter!==void 0){const c=e.dieOpts?.width!==void 0&&e.dieOpts?.height!==void 0;if(c&&W>M/2+1e-9){const x=P.filter(({x:S,y:N})=>Math.hypot(Math.abs(S)+d/2,Math.abs(N)+p/2)>M/2+1e-9).length;g.push({code:"geometry-conflict",message:`${x} of ${P.length} probed die positions ${Ie} ${M} mm wafer at the supplied die pitch of ${d} \xD7 ${p} mm. A die with test results is a real prober position and is always fully on the wafer, so one of the two supplied values must be wrong: containing these dies at this pitch would need a diameter of at least ${(W*2).toFixed(1)} mm. Check waferConfig.diameter and dieConfig.width/height against the real device.`})}}const O=j({diameter:M,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});let v=a.map(c=>{const x=Math.round(c.x)-y,S=Math.round(c.y)-b,N={id:`${x}_${S}`,x,y:S,physX:x*d-F,physY:S*p-w,width:d,height:p,insideWafer:!0,partial:!1};return _(N,c)});(y!==0||b!==0)&&(v=v.map(c=>({...c,x:c.x+y,y:c.y+b,id:`${c.x+y}_${c.y+b}`}))),v=H(v,O),(R||$)&&(v=ue(v,{flipX:R,flipY:$},O.center));const K=[];e.waferOpts?.edgeExclusion&&e.waferOpts.edgeExclusion>0&&(v=Fe(v,O,e.waferOpts.edgeExclusion),e.waferOpts.edgeExclusion>O.radius&&K.push({code:"edge-exclusion-exceeds-radius",message:`edgeExclusion (${e.waferOpts.edgeExclusion}mm) exceeds the resolved wafer radius (${O.radius}mm) \u2014 the entire wafer is excluded.`,severity:"warning"}));const z=se(e.reticleOpts,O,v,d,p,y,b,F,w,O.orientation,R,$),ae=s.showReticle??e.reticleOpts!==void 0,I=U(O,v,{...s,reticles:z,showReticle:ae,plotMode:ie(h,s),testDefs:e.testDefs,passBins:e.passBins,ringCount:e.ringCount,dataAxisFlip:{x:R,y:$},isLotStack:e.lotStackOpts!==void 0,aggregationMethod:e.lotStackOpts?.method,lotSize:e.lotStackOpts?.results.length},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),de=i.map((c,x)=>_({id:`unpositioned_${x}`,width:d,height:p},c)),Y=[...v,...de];return{wafer:O,dies:Y,view:I,reticleConfig:e.reticleOpts,units:m,inference:o,warnings:[...e.removedFieldWarning?[e.removedFieldWarning]:[],...e.nonNumericWarning?[e.nonNumericWarning]:[],...oe(g,o),...K,...e.ringCountWarning?[e.ringCountWarning]:[]],plotMode:I.plotMode,metadata:I.metadata,isLotStack:e.lotStackOpts!==void 0,dataCoverage:te(Y),passBins:e.passBins,ringCount:e.ringCount,yield:ne(Y,e.passBins,e.edgeDieYieldMode),reticles:z,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields,aggrMethod:I.aggrMethod,lotSize:I.lotSize}}
|
|
@@ -17,9 +17,10 @@ export declare const NO_DATA_FILL = "#d6d9dd";
|
|
|
17
17
|
* Indices 1–63 are perceptually spread colours generated via golden-angle
|
|
18
18
|
* HSL stepping.
|
|
19
19
|
*
|
|
20
|
-
* No longer used for bins: bin colours
|
|
21
|
-
*
|
|
22
|
-
* list is what made two different bins share a
|
|
20
|
+
* No longer used for bins: bin colours come from a registered bin palette,
|
|
21
|
+
* indexed by bin number and pass/fail, in `resolveBinColors` (binColors.ts).
|
|
22
|
+
* Hashing a bin number into this list is what made two different bins share a
|
|
23
|
+
* colour.
|
|
23
24
|
*/
|
|
24
25
|
export declare const BIN_PALETTE: readonly string[];
|
|
25
26
|
/** Wang hash — maps any integer to a well-distributed unsigned 32-bit value. Shared with colorSchemes. */
|
|
@@ -40,15 +40,17 @@ export interface BinColorScheme {
|
|
|
40
40
|
/** Human-readable display name. */
|
|
41
41
|
label: string;
|
|
42
42
|
/**
|
|
43
|
-
* Colours for passing bins,
|
|
44
|
-
*
|
|
43
|
+
* Colours for passing bins, indexed by bin number: pass bin 1 takes the first,
|
|
44
|
+
* pass bin 2 the second, wrapping round. Convention is greens: an engineer
|
|
45
|
+
* reads green as pass before reading the legend.
|
|
45
46
|
*/
|
|
46
47
|
pass: readonly string[];
|
|
47
48
|
/**
|
|
48
|
-
* Colours for failing bins,
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* Colours for failing bins, indexed by bin number: fail bin 2 takes the
|
|
50
|
+
* first, bin 3 the second, wrapping round. Put the most distinct colours at
|
|
51
|
+
* the front — the low bin numbers most programs use land there. Must not
|
|
52
|
+
* resemble the pass colours — a fail bin that reads as a pass is the most
|
|
53
|
+
* expensive misreading a wafer map can produce.
|
|
52
54
|
*/
|
|
53
55
|
fail: readonly string[];
|
|
54
56
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The release that deletes every export deprecated through this helper. Named in
|
|
3
|
+
* each notice and each `@deprecated` tag, and held to by tests/deprecations.test.mjs,
|
|
4
|
+
* which fails once the changelog or package.json reaches it with them still here.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEPRECATED_REMOVAL_VERSION = "0.31.0";
|
|
7
|
+
/**
|
|
8
|
+
* @internal Every name deprecated through this module, and whether it is a function
|
|
9
|
+
* (which logs a notice) or a value (which cannot). tests/deprecations.test.mjs pins
|
|
10
|
+
* it to the announced list, so a deprecation cannot be added or dropped silently.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEPRECATED_EXPORTS: Map<string, "function" | "value">;
|
|
13
|
+
/**
|
|
14
|
+
* @internal Log `message` once per `key` for the life of the page — the one
|
|
15
|
+
* copy of the once-only rule, for export deprecations (`deprecated`) and for
|
|
16
|
+
* notices about an option whose behaviour is changing, which is not an export
|
|
17
|
+
* and so is not registered in DEPRECATED_EXPORTS or held to its removal.
|
|
18
|
+
*/
|
|
19
|
+
export declare function noticeOnce(key: string, message: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* @internal Wrap `fn` so its first call logs one notice naming `name` and `advice`.
|
|
22
|
+
* The wrapper has exactly `fn`'s type — generics and overloads included — so a
|
|
23
|
+
* deprecated export's signature does not change until it is removed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function deprecated<F extends (...args: never[]) => unknown>(fn: F, name: string, advice: string): F;
|
|
26
|
+
/**
|
|
27
|
+
* @internal Register a deprecated constant. A value cannot log on use, so its
|
|
28
|
+
* `@deprecated` tag and the changelog are the only announcement.
|
|
29
|
+
*/
|
|
30
|
+
export declare function deprecatedValue<T>(value: T, name: string): T;
|
|
31
|
+
//# sourceMappingURL=deprecate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEPRECATED_REMOVAL_VERSION="0.31.0",DEPRECATED_EXPORTS=new Map;const n=new Set;export function noticeOnce(t,e){n.has(t)||(n.add(t),console.warn(`[wafermap] ${e}`))}export function deprecated(t,e,r){DEPRECATED_EXPORTS.set(e,"function");const o=t;return((...c)=>(noticeOnce(e,`${e} is deprecated and will be removed in ${DEPRECATED_REMOVAL_VERSION}. ${r}`),o(...c)))}export function deprecatedValue(t,e){return DEPRECATED_EXPORTS.set(e,"value"),t}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { valueToViridis as valueToViridisImpl, valueToGreyscale as valueToGreyscaleImpl } from './colorMap.js';
|
|
2
|
+
import { getValueColorScheme as getValueColorSchemeImpl } from './colorSchemes.js';
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Removed in 0.31.0. Use `resolveValueColorFn('default')`, which returns the same
|
|
5
|
+
* colours through the read-path that also applies `reverseValueScheme`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const valueToViridis: typeof valueToViridisImpl;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Removed in 0.31.0. Use `resolveValueColorFn('greyscale')`, which returns the same
|
|
10
|
+
* colours through the read-path that also applies `reverseValueScheme`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const valueToGreyscale: typeof valueToGreyscaleImpl;
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated Removed in 0.31.0. Colour by value with `resolveValueColorFn(name, reversed)`, and list
|
|
15
|
+
* names and labels with `listValueColorSchemes()`. `forValue` ignores
|
|
16
|
+
* `reverseValueScheme`, so a surface built on it can show a reading in a
|
|
17
|
+
* different colour from the map.
|
|
18
|
+
*/
|
|
19
|
+
export declare const getValueColorScheme: typeof getValueColorSchemeImpl;
|
|
20
|
+
import { buildView as buildViewImpl, buildHoverText as buildHoverTextImpl, buildMapTitle as buildMapTitleImpl } from './buildView.js';
|
|
21
|
+
import { resolveBinColors as resolveBinColorsImpl } from './binColors.js';
|
|
22
|
+
import { getBinColorScheme as getBinColorSchemeImpl } from './colorSchemes.js';
|
|
23
|
+
import { contrastTextColor as contrastTextColorImpl } from './colorMap.js';
|
|
24
|
+
import { dieHasTestData as dieHasTestDataImpl, isParametricTest as isParametricTestImpl, getDieTestValue as getDieTestValueImpl } from './buildWaferMap.js';
|
|
25
|
+
/** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
|
|
26
|
+
export declare const buildView: typeof buildViewImpl;
|
|
27
|
+
/** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
|
|
28
|
+
export declare const buildHoverText: typeof buildHoverTextImpl;
|
|
29
|
+
/** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
|
|
30
|
+
export declare const buildMapTitle: typeof buildMapTitleImpl;
|
|
31
|
+
/** @deprecated Removed in 0.31.0. Read the colours a map draws with `binColorsForMaps(results)`, which takes each map's own pass bins, or with `getBinColors()` on a WaferMapController or GalleryController. */
|
|
32
|
+
export declare const resolveBinColors: typeof resolveBinColorsImpl;
|
|
33
|
+
/** @deprecated Removed in 0.31.0. A raw palette lookup bypasses the bin colour rule. Read the colours a map draws with `binColorsForMaps(results)`, or `getBinColors()` on a map or gallery controller. */
|
|
34
|
+
export declare const getBinColorScheme: typeof getBinColorSchemeImpl;
|
|
35
|
+
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
36
|
+
export declare const contrastTextColor: typeof contrastTextColorImpl;
|
|
37
|
+
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
38
|
+
export declare const dieHasTestData: typeof dieHasTestDataImpl;
|
|
39
|
+
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
40
|
+
export declare const isParametricTest: typeof isParametricTestImpl;
|
|
41
|
+
/** @deprecated Removed in 0.31.0. Read die.testValues[testNumber]. */
|
|
42
|
+
export declare const getDieTestValue: typeof getDieTestValueImpl;
|
|
43
|
+
/** @deprecated Removed in 0.31.0. Pass the sizes you want as buildWaferMap's `standardDiameters`; the default ladder is [100, 125, 150, 200, 300]. */
|
|
44
|
+
export declare const STANDARD_WAFER_DIAMETERS_MM: readonly number[];
|
|
45
|
+
//# sourceMappingURL=deprecated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{valueToViridis as a,valueToGreyscale as l}from"./colorMap.js";import{getValueColorScheme as s}from"./colorSchemes.js";import{deprecated as e}from"./deprecate.js";export const valueToViridis=e(a,"valueToViridis","Use resolveValueColorFn('default'), which returns the same colours."),valueToGreyscale=e(l,"valueToGreyscale","Use resolveValueColorFn('greyscale'), which returns the same colours."),getValueColorScheme=e(s,"getValueColorScheme","Colour by value with resolveValueColorFn(name, reversed) \u2014 forValue ignores reverseValueScheme \u2014 and list names and labels with listValueColorSchemes().");import{deprecatedValue as i}from"./deprecate.js";import{buildView as n,buildHoverText as p,buildMapTitle as u}from"./buildView.js";import{resolveBinColors as c}from"./binColors.js";import{getBinColorScheme as m}from"./colorSchemes.js";import{contrastTextColor as d}from"./colorMap.js";import{dieHasTestData as T,isParametricTest as h,getDieTestValue as C,STANDARD_WAFER_DIAMETERS_MM as I}from"./buildWaferMap.js";const o="If you depend on it, say so at https://github.com/wafertools/wafermap/issues.",r="It belongs to the low-level drawing pipeline, which is being withdrawn: build with buildWaferMap and draw with renderWaferMap or renderWaferGallery.",t="It is an internal helper that was exported by accident; buildWaferMap, analyzeWaferMap and the renderers already apply it.",V="Read die.testValues[testNumber].";export const buildView=e(n,"buildView",`${r} ${o}`),buildHoverText=e(p,"buildHoverText",`${r} ${o}`),buildMapTitle=e(u,"buildMapTitle",`${r} ${o}`),resolveBinColors=e(c,"resolveBinColors",`Read the colours a map draws with binColorsForMaps(results), which takes each map's own pass bins, or with getBinColors() on a WaferMapController or GalleryController. ${o}`),getBinColorScheme=e(m,"getBinColorScheme",`A raw palette lookup bypasses the bin colour rule. Read the colours a map draws with binColorsForMaps(results), or getBinColors() on a map or gallery controller. ${o}`),contrastTextColor=e(d,"contrastTextColor",`${t} ${o}`),dieHasTestData=e(T,"dieHasTestData",`${t} ${o}`),isParametricTest=e(h,"isParametricTest",`${t} ${o}`),getDieTestValue=e(C,"getDieTestValue",`${V} ${o}`),STANDARD_WAFER_DIAMETERS_MM=i(I,"STANDARD_WAFER_DIAMETERS_MM");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { getDieKey } from './buildView.js';
|
|
2
2
|
export type { View, MapTitleParts, PlotMode, ViewRect, ViewText, ViewHoverPoint, ViewOverlay, ViewOptions, } from './buildView.js';
|
|
3
3
|
export * from './buildWaferMap.js';
|
|
4
4
|
export type { WaferMetadata, DieMetadata } from '../core/metadata.js';
|
|
5
|
-
export { valueToViridis, valueToGreyscale, contrastTextColor, } from './colorMap.js';
|
|
6
5
|
export * from './colorSchemes.js';
|
|
7
|
-
export {
|
|
8
|
-
export
|
|
6
|
+
export type { BinColors, BinColorOptions, BinColorSource, MapBinColorOptions } from './binColors.js';
|
|
7
|
+
export { binColorsForMaps } from './binColors.js';
|
|
8
|
+
export { valueToViridis, valueToGreyscale, getValueColorScheme, buildView, buildHoverText, buildMapTitle, resolveBinColors, getBinColorScheme, contrastTextColor, dieHasTestData, isParametricTest, getDieTestValue, STANDARD_WAFER_DIAMETERS_MM, } from './deprecated.js';
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{getDieKey}from"./buildView.js";export*from"./buildWaferMap.js";export*from"./colorSchemes.js";export{binColorsForMaps}from"./binColors.js";export{valueToViridis,valueToGreyscale,getValueColorScheme,buildView,buildHoverText,buildMapTitle,resolveBinColors,getBinColorScheme,contrastTextColor,dieHasTestData,isParametricTest,getDieTestValue,STANDARD_WAFER_DIAMETERS_MM}from"./deprecated.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{analyzeWaferMap as
|
|
1
|
+
import{analyzeWaferMap as g}from"./analyzeWaferMap.js";import{normalizeInput as h}from"./normalizeInput.js";import{poolCapability as b,poolRegionYield as w,poolPassRates as v}from"./summaryFigures.js";import{mergeTestDefs as I}from"./mergeTestDefs.js";import{median as p}from"../core/utils.js";import{describeWaferPopulation as S,populationStat as k}from"./population.js";const P=1.3,R=2;function x(f,c){const o={info:0,notable:1,unusual:2};return o[f]>=o[c]?f:c}function z(f){const c=new Map;for(const t of f){const i=new Set,l=new Set(t.summary.findings.flatMap(n=>n.absorbedIds??[]));for(const n of t.summary.findings){if(l.has(n.id))continue;const s=[n.variable.kind,n.variable.index??"",n.variable.bin??"",n.comparison.family,n.comparison.left,n.effect.direction].join("|");if(i.has(s))continue;i.add(s);const a=c.get(s)??{finding:n,waferIndices:[],severity:n.severity};a.waferIndices.push(t.waferIndex),a.severity=x(a.severity,n.severity),c.set(s,a)}}const o=[];for(const[t,i]of c){if(i.waferIndices.length<R)continue;const l=i.waferIndices.length/Math.max(1,f.length),n=l>=.6||i.severity==="unusual"?"unusual":"notable",s=i.finding;o.push({id:`lot-repeat:${t}`,level:"lot",severity:n,variable:{...s.variable},comparison:{...s.comparison},effect:{direction:s.effect.direction,absoluteDelta:s.effect.absoluteDelta,relativeDelta:s.effect.relativeDelta,effectSize:s.effect.effectSize},stats:{method:"wafer-finding-frequency",sampleSizeLeft:i.waferIndices.length,sampleSizeRight:f.length-i.waferIndices.length},summary:`${s.summary} \u2014 seen on ${i.waferIndices.length}/${f.length} wafers (${Math.round(l*100)}%)`,highlight:{kind:"wafer",waferIndices:i.waferIndices}})}return o}function $(f,c){const o=k(c,"median"),t=f.map(a=>({waferIndex:a.waferIndex,yieldPercent:a.summary.stats.yieldPercent})).filter(a=>a.yieldPercent!==null);if(t.length<3)return[];const i=t.map(a=>a.yieldPercent),l=p(i),n=p(i.map(a=>Math.abs(a-l)));if(!Number.isFinite(n)||n===0)return[];const s=[];for(const a of t){const u=.6745*(a.yieldPercent-l)/n;if(Math.abs(u)<P)continue;const e=a.yieldPercent-l;s.push({id:`inter-wafer:yield:${a.waferIndex}`,level:"inter-wafer",severity:Math.abs(u)>=2?"unusual":"notable",variable:{kind:"yield",label:"Yield"},comparison:{family:"wafer",left:`Wafer ${a.waferIndex+1}`,right:o.charAt(0).toUpperCase()+o.slice(1)},effect:{direction:e>0?"higher":"lower",absoluteDelta:e,relativeDelta:l===0?void 0:e/l,effectSize:u},stats:{method:"mad-z-score",sampleSizeLeft:1,sampleSizeRight:t.length-1},summary:`Wafer ${a.waferIndex+1} yield is ${Math.abs(e).toFixed(1)} percentage points ${e>0?"higher":"lower"} than the ${o}`,highlight:{kind:"wafer",waferIndices:[a.waferIndex]}})}return s}export function analyzeWaferLot(f,c={}){const o=f.map(h),t=o.map((e,r)=>({waferIndex:r,summary:c.perWaferSummaries?.[r]??g(e,c)})),i=[...z(t),...$(t,S(t.map(e=>e.summary.wafer)))].sort((e,r)=>{const d=e.severity==="unusual"?2:e.severity==="notable"?1:0,y=r.severity==="unusual"?2:r.severity==="notable"?1:0;return d!==y?y-d:e.summary.localeCompare(r.summary,"en")}),l=t.map(e=>e.summary.wafer).filter(e=>!!e),n={},s=[];if(l.length>0){const e=new Set;for(const r of l)for(const d of Object.keys(r))e.add(d);e.delete("wafer"),e.delete("waferId");for(const r of e){const d=l.filter(m=>r in m).map(m=>m[r]);d.every(m=>m===d[0])?n[r]=d[0]:s.push(r)}}const a=t.map(({waferIndex:e,summary:r})=>({waferIndex:e,yieldPercent:r.stats.yieldPercent})),u=t.map(({waferIndex:e,summary:r})=>r.stats.perTestStats?.length?{waferIndex:e,tests:r.stats.perTestStats}:null).filter(e=>e!==null);return{level:"lot",hasNotableFindings:i.some(e=>e.severity!=="info")||t.some(e=>e.summary.hasNotableFindings),findings:i,lot:Object.keys(n).length>0?n:void 0,...s.length>0?{mixedIdentityFields:s}:{},stats:{waferCount:f.length,...M(o,t.map(e=>e.summary))},lotYieldSeries:a,perWafer:t,...u.length>0?{perWaferTestStats:u}:{}}}function M(f,c){const o={...v(c)},t=b(c,I(f).defs);t&&(o.capability=t);const i=w(c,f.map(l=>l.ringCount));return i&&(o.regionYield=i),o}
|