@wafertools/wafermap 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/dist/packages/canvas-adapter/maplessSummary.d.ts +3 -0
- package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +7 -0
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/toCanvas.js +1 -1
- package/dist/packages/canvas-adapter/toolbar.d.ts +2 -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 +3 -3
- package/dist/packages/canvas-adapter/version.d.ts +2 -2
- package/dist/packages/canvas-adapter/version.js +1 -1
- package/dist/packages/renderer/buildView.d.ts +15 -0
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/colorSchemes.d.ts +13 -0
- package/dist/packages/renderer/colorSchemes.js +1 -1
- package/package.json +1 -1
|
@@ -128,6 +128,13 @@ export interface View {
|
|
|
128
128
|
binColorScheme: string;
|
|
129
129
|
/** Value gradient used for value and stacked modes, as registered with `registerValueColorScheme`. */
|
|
130
130
|
valueColorScheme: string;
|
|
131
|
+
/**
|
|
132
|
+
* Whether that gradient was flipped (high value takes the gradient's low-end
|
|
133
|
+
* colour). Any surface colouring by value must pass this to
|
|
134
|
+
* `resolveValueColorFn` alongside {@link valueColorScheme}, or it will
|
|
135
|
+
* disagree with the die fills.
|
|
136
|
+
*/
|
|
137
|
+
reverseValueScheme: boolean;
|
|
131
138
|
/**
|
|
132
139
|
* Every bin's resolved colour, both bin types — the single source for bin
|
|
133
140
|
* colour. Die fills are built from it, and legends, panels and charts must
|
|
@@ -282,6 +289,14 @@ export interface ViewOptions {
|
|
|
282
289
|
binColorScheme?: string;
|
|
283
290
|
/** Value gradient for `value` and the stacked modes — any name registered via `registerValueColorScheme()`. Default `'default'`. */
|
|
284
291
|
valueColorScheme?: string;
|
|
292
|
+
/**
|
|
293
|
+
* Flip the value gradient so the high end takes its low-end colour. Default
|
|
294
|
+
* false: every built-in reads low = dark, high = light. Set this for a
|
|
295
|
+
* parameter where low is the notable end, or for monochrome print output
|
|
296
|
+
* where more ink should mean more. Applies to the dies, the colorbar and the
|
|
297
|
+
* mapless summary together.
|
|
298
|
+
*/
|
|
299
|
+
reverseValueScheme?: boolean;
|
|
285
300
|
/** Honour `BinDef.color` where a bin definition supplies one. Default true. See `resolveBinColors`. */
|
|
286
301
|
useDefinedBinColors?: boolean;
|
|
287
302
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hasPosition as ie}from"../core/dies.js";import{getReticleCell as se}from"../core/reticle.js";import{metadataCategoricalValue as oe,metadataDisplayValue as re}from"../core/metadata.js";import{affineRotation as mt,affineMirror as Lt,affineCompose as it,affinePoint as Y,affineVector as pt,affineSwapsAxes as le}from"../core/transforms.js";import{contrastTextColor as G,SPEC_PASS_FILL as yt,SPEC_FAIL_LOW as ce,SPEC_FAIL_HIGH as gt}from"./colorMap.js";import{getValueColorScheme as ue}from"./colorSchemes.js";import{resolveBinColors as fe,binColorsCover as de}from"./binColors.js";import{NO_DATA_FILL as j}from"./colorMap.js";import{diePassStatus as he}from"../core/dies.js";import{getDieTestValue as L,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 me}from"./colorMap.js";import{compareNatural as pe,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=st(n,e);s!==void 0&&a.add(s)}return[...a].sort(pe)}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 ye="#d3d6db",Ot="#e8e9ea",ge="#aab0ba";function st(t,e){return e?oe(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 be(t,e,a){const{x:n,y:s}=t.center,i=mt(t.orientation,n,s),o=it(Lt(e?.x??!1,e?.y??!1,n,s),mt(t.orientation,n,s)),l=it(Lt(a?.flipX??!1,a?.flipY??!1,n,s),mt(a?.rotation??0,n,s));return{bakedToScreen:l,physicalToScreen:it(l,i),gridToScreen:it(l,o)}}function z(t,e=!1){return{points:[t],closed:e}}function xe(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),u=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(-u,Math.min(u,g))):s.type==="top"&&p>l?(o=a.y+l,i=a.x+Math.max(-u,Math.min(u,g))):s.type==="left"&&g<-l?(i=a.x-l,o=a.y+Math.max(-u,Math.min(u,p))):s.type==="right"&&g>l&&(i=a.x+l,o=a.y+Math.max(-u,Math.min(u,p))),{x:i,y:o}}function Ht(t,e){return t.map(e).join(" / ")}function Me(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 Se(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 ke(t,e,a){if(e?.length){const n=e.flatMap(s=>{const i=s.testNumber;if(!bt(s)){const u=Q(t,i,s);return u===void 0?[]:[{key:i,label:s.name,value:u?"Pass":"Fail"}]}const o=L(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:u,activeTest:g,reticleConfig:p,metadataFields:B}=a,D=s?new Map(s.map(f=>[f.bin,f])):null,T=i?new Map(i.map(f=>[f.bin,f])):null,b=[`Die (${t.x}, ${t.y})`];if(p&&ie(t)){const f=se(t,p);b.push(`Reticle (${f.column}, ${f.row})`)}if(e==="stackedValues"){const f=L(t,0);if(f!==void 0){const d=n?.[0],h=d?.testNumber,k=d?.name??(h!=null?`Test ${h}`:"Value"),m=l?` (${l})`:"";b.push(`${k}${m}: ${xt(f,d?.unit,o)}`)}}else if(e==="stackedBins"||e==="stackedSoftBins"){const f=L(t,0),d=e==="stackedSoftBins"?t.sbin:t.hbin;if(f!==void 0){const h=e==="stackedSoftBins"?T:D,k=d!==void 0?h?.get(d)?.name?`${d} \xB7 ${h.get(d).name}`:`Bin ${d}`:"Bin";let m;if(l==="percent")m=`${f.toFixed(0)}%`;else{const C=u?` (${(f/u*100).toFixed(0)}% of stacked wafers)`:"";m=`${f}${C}`}const x=l?` [${Pt(l)}]`:"";b.push(`${k}: ${m}${x}`)}}else{const f=ke(t,n,o);if(e==="value"&&f.length){const{testNumber:d}=resolveTestNumber(g??0,n),h=findTestDef(n,d),k=L(t,d);let m=f.findIndex(y=>y.key===d);m<0&&(m=0);const x=f[m];let C=`<b>${x.label}: ${x.value}</b>`;if(x.key===d&&h){const y=classifySpec(k,h);(y==="failLow"||y==="failHigh")&&(C+=" <i>(out of spec)</i>")}b.push(C);const R=f.length-1;R>0&&b.push(`<i>+${R} more test${R===1?"":"s"}</i>`)}else if(f.length){const d=f.length;b.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,k=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);d.push(`HBin: ${k}`)}if(t.sbin!==void 0){const h=T?.get(t.sbin)?.name,k=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);d.push(`SBin: ${k}`)}b.push(d.join(" \xB7 "))}}t.retestCount!==void 0&&b.push(`Retests: ${t.retestCount}`),t.siteNum!==void 0&&b.push(`Site: ${t.siteNum}`),t.partId!==void 0&&b.push(`Part ID: ${t.partId}`),t.partial&&b.push("<i>partial die</i>"),t.probeIndex!==void 0&&b.push(`Probe: #${t.probeIndex}`);const $={...t.metadata??{}};for(const[f,d]of Object.entries($)){const h=re(d);if(h===void 0)continue;const k=B?.find(m=>m.key===f)?.label??Nt(f);b.push(`${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?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),u=l||"Value",g=t.aggrMethod?` \xB7 ${Pt(t.aggrMethod)}`:"";return{primary:`${u}${g}`,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),u=l||i?.name;if(t.passFailDisplay!=="off"){const g=i?.testNumber??t.activeTest,p=u?`${u} \xB7 #${g}`:`Test ${g}`,B=t.passFailDisplay==="spec"?"Spec pass/fail":i!==void 0&&!bt(i)?"Functional pass/fail":"Tester pass/fail";return{primary:p,secondary:B}}return{primary:u??`Test ${t.activeTest}`,secondary:""}}}}export function generateTextOverlay(t,e,a){const{plotMode:n,colorFns:s,normalize:i,activeTest:o,valueRange:l,testDefs:u,fallbackFormat:g,passFailDisplay:p="off",activeMetadataKey:B,metadataColorMap:D,allIntegerValues:T}=a,b=findTestDef(u,o),{tickFmt:$}=Mt(l[1],b?.name,b?.unit,g),f=T?h=>String(Math.round(h)):$,{testNumber:d}=resolveTestNumber(o,u);return t.flatMap((h,k)=>{let m="",x="#111111";if(n==="value"&&p==="test"){const y=Q(h,d,b);if(y===void 0)return[];m=y?"P":"F",x=G(y?yt:gt)}else if(n==="value"){const y=L(h,d);if(y===void 0)return[];m=Ht([y],f),x=G(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=G(s.forBin(y))}else if(n==="metadata"){const y=st(h,B);if(y===void 0)return[];m=y,x=G(D?.get(m)??j)}else{const y=L(h,0);if(y===void 0)return[];m=Ht([y],f),x=G(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:Se(h,m),color:x,align:"center"}]})}function At(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function ve(t,e,a=720){const{center:n,radius:s,notch:i}=t,o=$=>Y(e,$.x,$.y);if(!(i!==void 0&&t.diameter>150)){const $=[];for(let f=0;f<=a;f++){const d=2*Math.PI*f/a;$.push(o(xe(t,d)))}return[{kind:"wafer-boundary",...z($,!0),lineColor:"#888888",lineWidth:1}]}const u=1.25,g=i.length,p=At(i.type),B=Math.atan2(g,s),D=s-u,T=[];let b=!1;for(let $=0;$<=a;$++){const f=2*Math.PI*$/a;let d=f-p;for(;d>Math.PI;)d-=2*Math.PI;for(;d<-Math.PI;)d+=2*Math.PI;if(Math.abs(d)<=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(f),y:n.y+s*Math.sin(f)}))}return[{kind:"wafer-boundary",...z(T,!0),lineColor:"#888888",lineWidth:1}]}function $e(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,u=[];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)};u.push(Y(e,B.x,B.y))}s.push({kind:"ring-boundary",...z(u,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function Be(t,e,a,n){const{x:s,y:i}=t.center,o=t.radius,l=(f,d)=>Y(e,f,d),u=a-s,g=Math.sqrt(Math.max(0,o*o-u*u)),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",...z([p,B]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...z([b,$]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function Te(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 Ie(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 Ve(t,e,a,n,s,i,o,l,u,g,p,B,D,T,b="off",$,f,d){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=>st(S,$);if(e.partial){t.push({x:a,y:n,width:m,height:x,fill:ye,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:n,width:m,height:x,fill:ge,type:"stacked",metadata:e.metadata});return}if(u!==void 0&&(s==="hardBin"||s==="softBin")&&C(e)!==u){t.push({x:a,y:n,width:m,height:x,fill:Ot,type:"hardBin",metadata:e.metadata});return}if(d!==void 0&&s==="metadata"&&R(e)!==d){t.push({x:a,y:n,width:m,height:x,fill:Ot,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(b==="test"){const w=Q(e,p,T),ot=w===void 0?j:w?yt:gt;t.push({x:a,y:n,width:m,height:x,fill:ot,type:"value",metadata:e.metadata});return}const S=L(e,p),N=classifySpec(S,T);let _,U;S===void 0?_=j:b==="spec"?_=N==="failLow"?ce:N==="failHigh"?gt:yt:(_=l.forValue(g(S)),(N==="failLow"||N==="failHigh")&&(U=N)),t.push({x:a,y:n,width:m,height:x,fill:_,type:"value",specMark:U,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const S=C(e),N=S!=null?l.forBin(S):j,_=S!=null&&he(e,D)===!1?!0:void 0;t.push({x:a,y:n,width:m,height:x,fill:N,type:s,binFail:_,metadata:e.metadata});return}if(s==="metadata"){const S=R(e),N=S!==void 0?f?.get(S)??j:j;t.push({x:a,y:n,width:m,height:x,fill:N,type:"metadata",metadata:e.metadata});return}const y=L(e,0),X=y!==void 0?l.forValue(g(y)):j;t.push({x:a,y:n,width:m,height:x,fill:X,type:"value",metadata:e.metadata})}function Ce(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,u={x:t.center.x+o*t.radius*.9,y:t.center.y+l*t.radius*.9},g={x:u.x+s.x,y:u.y+s.y},p={x:u.x+i.x,y:u.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",...z([u,g]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...z([u,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:u=!1,ringCount:g=4,showRingBoundaries:p=!1,showQuadrantBoundaries:B=!1,showXYIndicator:D=!1,dieGap:T=1,binColorScheme:b="default",valueColorScheme:$="default",useDefinedBinColors:f=!0,binColors:d,passBins:h=[1],highlightBin:k,highlightMetadataValue:m,interactiveTransform:x,valueRange:C,metadataValueOrder:R,testDefs:y,activeTest:X=0,activeMetadataKey:S,fallbackFormat:N="engineering",aggregationMethod:_,lotSize:U,dataAxisFlip:w,isLotStack:ot=!1,logScale:Et,colorbarRangeMode:_t="spec",passFailDisplay:wt,showPartialDies:Wt=!0}=a,St=wt??"off",rt=n?.hbinDefs,lt=n?.sbinDefs,kt=n?.metadataFields,jt=kt?.find(c=>c.key===S),zt={x:(w?.x??!1)!==(x?.flipX??!1),y:(w?.y??!1)!==(x?.flipY??!1)},Kt=rt?new Map(rt.map(c=>[c.bin,c])):null,qt=lt?new Map(lt.map(c=>[c.bin,c])):null,Xt=i==="softBin"?qt:Kt,Yt=new Set(h),ct=d&&de(d,e)?d:fe(e,{passBins:h,binColorScheme:b,hbinDefs:rt,sbinDefs:lt,useDefinedBinColors:f}),Gt=i==="softBin"?ct.soft:ct.hard,vt={forValue:Me(ue($).forValue),forBin:c=>Gt.get(c)??j};let{testNumber:H}=resolveTestNumber(X,y);if(!y?.length&&i==="value"&&!e.some(r=>r.testValues&&H in r.testValues)){const r=e.reduce((M,V)=>{if(!V.testValues)return M;const v=Object.keys(V.testValues).map(Number),P=v.length?Math.min(...v):void 0;return P!==void 0&&(M===void 0||P<M)?P:M},void 0);r!==void 0&&(H=r)}let J;if(Array.isArray(C))J=C;else if(C){const{testNumber:c}=resolveTestNumber(C.test,y);J=c===H?C.range:void 0}const I=findTestDef(y,H),$t=i==="value"&&I!==void 0&&!bt(I),Qt=I!==void 0&&(I.limitLow!==void 0||I.limitHigh!==void 0);let A="off";i==="value"&&($t?A="test":St==="spec"&&Qt?A="spec":St==="test"&&e.some(c=>Q(c,H,I)!==void 0)&&(A="test"));const Bt=A==="spec"?"spec":_t,ut=i==="stackedValues"||i==="stackedBins"||i==="stackedSoftBins";let O,E,Z=!1;if(J){[O,E]=J;let c=!0,r=!1;for(const M of e){const V=ut?L(M,0):L(M,H);if(V!==void 0&&(r=!0,!Number.isInteger(V))){c=!1;break}}Z=r&&c}else{const c=!ut&&i==="value"&&Bt==="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 P=ut?L(v,0):L(v,H);P!==void 0&&(P<r&&(r=P),P>M&&(M=P),Number.isInteger(P)||(V=!1))}isFinite(r)||(V=!1),Z=V,c?(O=I.limitLow!==void 0?I.limitLow:isFinite(r)?r:0,E=I.limitHigh!==void 0?I.limitHigh:isFinite(M)?M:1):(O=isFinite(r)?r:0,E=isFinite(M)?M:1)}O===E&&(E=O+1);const Tt=$t?!1:Et??I?.logScale??!1,It=Tt&&O>0&&E>0,Ut=It;let tt;if(It){const c=Math.log10(O),r=Math.log10(E)-c;tt=M=>M<=0?0:Rt((Math.log10(M)-c)/r)}else tt=c=>Rt((c-O)/(E-O));const F=be(t,w,x);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),M=Math.hypot(r.x,r.y);M>0&&(Vt={x:r.x/M,y:r.y/M})}let K=1/0;for(let c=0;c<e.length;c++){const r=e[c];r.width<K&&(K=r.width),r.height<K&&(K=r.height)}const Jt=K!==1/0?Math.min(T,K*.12):T,Ct=[],ft=[],Zt=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(!Zt){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 te=le(F.gridToScreen),ee=i==="hardBin"||i==="softBin"||i==="stackedBins"||i==="stackedSoftBins",ne=i==="softBin"||i==="stackedSoftBins";let et=ee?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 nt=Ft?new Map:void 0,at=null;if(Ft&&S){const c=collectMetadataValues(e,S),r=R?.key===S?R.values:void 0,M=r?[...r,...c.filter(V=>!r.includes(V))]:c;at=new Map(M.map((V,v)=>[V,jt?.values?.find(P=>P.value===V)?.color??me(v)]))}for(let c=0;c<e.length;c++){const r=e[c],M=W?W[c*2]:r.physX,V=W?W[c*2+1]:r.physY;if(ft.push({x:M,y:V}),et&&!r.partial&&!r.edgeExcluded){const v=ne?r.sbin:r.hbin;v!=null&&et.set(v,(et.get(v)??0)+1)}if(dt&&!r.partial&&!r.edgeExcluded){const v=classifySpec(L(r,H),I);v&&dt[v]++}if(ht&&!r.partial&&!r.edgeExcluded){const v=Q(r,H,I);v!==void 0&&ht[v?"pass":"fail"]++}if(nt&&!r.partial&&!r.edgeExcluded){const v=st(r,S);v!==void 0&&nt.set(v,(nt.get(v)??0)+1)}r.partial&&!Wt||Ve(Ct,r,M,V,i,te,Jt,vt,k,tt,H,Xt,Yt,I,A,S,at,m)}const Dt=o?generateTextOverlay(e,W,{plotMode:i,colorFns:vt,normalize:tt,activeTest:X,valueRange:[O,E],testDefs:y,fallbackFormat:N,passFailDisplay:A,activeMetadataKey:S,metadataColorMap:at,allIntegerValues:Z}):[],q=ve(t,F.physicalToScreen);p&&q.push(...$e(t,F.physicalToScreen,g)),B&&q.push(...Be(t,F.physicalToScreen,t.center.x,t.center.y)),l&&q.push(...Te(s,F.gridToScreen)),u&&q.push(...Ie(e)),D&&q.push(...Ce(t,F.gridToScreen,Dt));const ae=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:q,plotMode:i,binColorScheme:b,valueColorScheme:$,binColors:ct,metadata:t.metadata??null,dies:e,valueRange:[O,E],allIntegerValues:Z,testDefs:y,activeTest:X,logScale:Ut,logScaleRequested:Tt,aggrMethod:_,lotSize:U,axisFlip:zt,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:ot,passFailDisplay:A,colorBySpec:A==="spec",colorbarRangeMode:Bt,waferCenter:t.center,waferRadius:t.radius,notchDir:Vt,binCounts:et,metadataCounts:nt,metadataColorMap:at,activeMetadataKey:S,metadataFields:kt,specCounts:dt,passFailCounts:ht,dieBounds:ae}}export{getDieKey}from"../core/dies.js";
|
|
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";
|
|
@@ -23,6 +23,19 @@ export declare function listValueColorSchemes(): Array<{
|
|
|
23
23
|
name: string;
|
|
24
24
|
label: string;
|
|
25
25
|
}>;
|
|
26
|
+
/**
|
|
27
|
+
* The colour function a value surface must actually use: the named gradient,
|
|
28
|
+
* flipped when `reversed`.
|
|
29
|
+
*
|
|
30
|
+
* **Every surface that colours by value resolves through this** — die fills
|
|
31
|
+
* (`buildView`), the colorbar (`toCanvas`) and the mapless summary's bars, which
|
|
32
|
+
* each look the gradient up independently. Applying the flip at only some of
|
|
33
|
+
* them would show one reading as two different colours on the same screen, with
|
|
34
|
+
* the colorbar — the thing an engineer reads the map against — the most likely
|
|
35
|
+
* to be missed. Reversing is a flag rather than a second registered entry per
|
|
36
|
+
* ramp so the menu stays one list and a host's own gradient gets it for free.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveValueColorFn(name?: string, reversed?: boolean): (t: number) => string;
|
|
26
39
|
export interface BinColorScheme {
|
|
27
40
|
/** Human-readable display name. */
|
|
28
41
|
label: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{VIRIDIS as
|
|
1
|
+
import{VIRIDIS as f,lerpKp as a,valueToGreyscale as i}from"./colorMap.js";const o=new Map;export function registerValueColorScheme(e,l){o.set(e,l)}export function getValueColorScheme(e){return o.get(e??"default")??o.get("default")}export function listValueColorSchemes(){return[...o.entries()].map(([e,l])=>({name:e,label:l.label}))}export function resolveValueColorFn(e,l){const{forValue:t}=getValueColorScheme(e);return l?n=>t(1-n):t}const r=new Map;export function registerBinColorScheme(e,l){if(!l.pass.length||!l.fail.length)throw new Error(`registerBinColorScheme('${e}'): 'pass' and 'fail' must each list at least one colour`);r.set(e,l)}export function getBinColorScheme(e){return r.get(e??"default")??r.get("default")}export function listBinColorSchemes(){return[...r.entries()].map(([e,l])=>({name:e,label:l.label}))}registerValueColorScheme("default",{label:"Default (Viridis)",forValue:e=>a(f,e)});const s=[[0,32,77],[54,68,130],[107,107,145],[180,154,108],[253,228,32]];registerValueColorScheme("cividis",{label:"Cividis (colour-blind safe)",forValue:e=>a(s,e)}),registerValueColorScheme("greyscale",{label:"Greyscale",forValue:e=>i(e)});const c=[[13,8,135],[126,3,168],[204,71,120],[248,149,64],[240,249,33]];registerValueColorScheme("plasma",{label:"Plasma",forValue:e=>a(c,e)});const u=[[0,0,4],[87,16,110],[188,55,84],[249,142,9],[252,255,164]];registerValueColorScheme("inferno",{label:"Inferno",forValue:e=>a(u,e)});const b=[[11,4,5],[43,28,53],[62,53,107],[59,86,152],[53,123,163],[53,159,171],[75,194,173],[153,221,182],[222,245,229]];registerValueColorScheme("mako",{label:"Mako",forValue:e=>a(b,e)});const d=[[46,204,113],[241,196,15],[231,76,60]];registerValueColorScheme("traffic",{label:"Traffic (Green\u2013Yellow\u2013Red)",forValue:e=>a(d,e)});const p=[[0,0,128],[0,0,255],[0,255,255],[255,255,0],[255,0,0],[128,0,0]];registerValueColorScheme("jet",{label:"Jet (MATLAB rainbow)",forValue:e=>a(p,e)}),registerBinColorScheme("default",{label:"Default",pass:["#2ca02c","#98df8a","#1b5e20"],fail:["#d62728","#332288","#e69f00","#000000","#0072b2","#654522","#ff9d9a","#aa3377","#499894","#e377c2","#f0e442","#225555","#79706e","#33bbee","#ee7733","#999933","#9467bd","#663333","#cc6677"]}),registerBinColorScheme("accessible",{label:"Colour-blind safe",pass:["#009e73","#44bb99"],fail:["#d55e00","#000000","#604e97","#882d17","#f3c300","#222255","#225555","#a1caf1","#9467bd","#8c564b","#b07aa1","#eedd88","#f38400","#663333"]});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wafertools/wafermap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Interactive wafer map visualization and yield analysis for semiconductor test data. Hard bins, soft bins, test values, retests, edge exclusion, reticle overlays, spatial statistics, failure clustering, and lot-level trend analysis — pure ES modules, no runtime dependencies, no server required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|