@wafertools/wafermap 0.29.0 → 0.30.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/AGENTS.md +13 -14
- package/CHANGELOG.md +235 -0
- package/README.md +5 -5
- package/dist/packages/canvas-adapter/deprecated.d.ts +10 -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/index.d.ts +1 -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 +1 -40
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +74 -104
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +11 -8
- 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.js +2 -2
- package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.js +12 -8
- 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 +64 -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/waferLabel.d.ts +25 -0
- package/dist/packages/core/waferLabel.js +1 -0
- package/dist/packages/renderer/binColors.d.ts +67 -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 +47 -13
- 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 +24 -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 +2 -3
- package/dist/packages/renderer/index.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/binPareto.d.ts +6 -1
- package/dist/packages/stats/clusterDetection.d.ts +0 -2
- package/dist/packages/stats/deprecated.d.ts +87 -0
- package/dist/packages/stats/deprecated.js +1 -0
- package/dist/packages/stats/index.d.ts +3 -14
- package/dist/packages/stats/index.js +1 -1
- package/dist/packages/stats/regions.d.ts +7 -2
- package/dist/packages/stats/regions.js +1 -1
- package/dist/packages/stats/renderSummaryReport.d.ts +3 -0
- package/dist/packages/stats/renderSummaryReport.js +34 -34
- package/dist/packages/stats/types.d.ts +0 -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
|
-
export const WMAP_VERSION="0.
|
|
1
|
+
export const WMAP_VERSION="0.30.0",WMAP_BUILD_TIME="2026-09-15T15:32:11.763Z";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SHADOW as
|
|
1
|
+
import{SHADOW as S,TRACKING as O,SPACE as s,RADIUS as E,FONT as y,CLR as d,Z_BASE as B}from"./toolbar.js";const v={error:0,warning:1,info:2};export function severityOf(t){return t.severity??"warning"}export function collectWarnings(t){const o=[],i=new Set,n=e=>{if(!e?.message)return;const r=`${e.code}\0${e.message}`;i.has(r)||(i.add(r),o.push(e))};for(const e of t.result?.warnings??[])n(e);for(const e of t.statsSummary?.stats.warnings??[])n(e);for(const e of t.lotStatsSummary?.perWafer??[])for(const r of e.summary.stats.warnings??[])n(r);for(const e of t.extra??[])n(e);return o.sort((e,r)=>v[severityOf(e)]-v[severityOf(r)])}function $(t){return t==="error"?{bg:d.errBg,border:d.errBorder,text:d.errText}:{bg:d.warnBg,border:d.warnBorder,text:d.warnText}}function w(t){return t==="error"?"\u26D4":t==="info"?"\u2139":"\u26A0"}export const SHORT_LABEL={"partial-coverage":"Partial wafer coverage","geometry-conflict":"Geometry conflict","non-standard-diameter":"Non-standard wafer diameter","diameter-exceeds-die-extent":"Diameter exceeds die extent","edge-exclusion-exceeds-radius":"Edge exclusion exceeds radius","test-count-capped":"Test analysis skipped","test-def-collision":"Test definitions disagree","analysis-option-corrected":"Analysis option corrected","bin-colors-shared":"Bins share a colour","pass-bins-mixed":"Pass bins differ by wafer","ring-count-mixed":"Ring counts differ by wafer","input-field-removed":"Removed input name ignored"};function C(t){const o=t.code?SHORT_LABEL[t.code]:void 0;if(o)return o;const i=t.message.split(/(?<=\.)\s/)[0]??t.message;return i.length>60?`${i.slice(0,57)}\u2026`:i}export function buildWarningsBanner(t,o=document){const i=o.createElement("div");Object.assign(i.style,{marginBottom:s.lg});for(const n of t){const e=severityOf(n),r=$(e),m=o.createElement("div");Object.assign(m.style,{background:r.bg,border:`1px solid ${r.border}`,borderRadius:E.control,marginBottom:s.sm,fontSize:y.body,color:r.text,lineHeight:"1.5"}),m.setAttribute("role","status");const a=o.createElement("button");a.type="button",Object.assign(a.style,{display:"flex",alignItems:"center",gap:s.sm,width:"100%",background:"none",border:"none",font:"inherit",color:"inherit",textAlign:"left",padding:`${s.sm} ${s.lg}`,cursor:"pointer"});const g=o.createElement("span");Object.assign(g.style,{flex:"1",minWidth:"0"}),g.textContent=`${w(e)} ${C(n)}`;const l=o.createElement("span");Object.assign(l.style,{flexShrink:"0"}),a.append(g,l);const p=o.createElement("div");Object.assign(p.style,{padding:`0 ${s.lg} ${s.sm}`}),p.textContent=n.message;let c=!1;const f=()=>{l.textContent=c?"\u25B4":"\u25BE",p.style.display=c?"block":"none",a.setAttribute("aria-expanded",String(c)),a.setAttribute("aria-label",`${C(n)}. ${c?"Click to collapse.":"Click to expand for detail."}`)};f(),a.addEventListener("click",()=>{c=!c,f()}),a.addEventListener("mouseenter",()=>{l.style.opacity="0.65"}),a.addEventListener("mouseleave",()=>{l.style.opacity="1"}),m.append(a,p),i.appendChild(m)}return i}export function buildWarningsMenuEl(t,o,i=window){const n=i.document,e=n.createElement("div"),r=280,a=t.left+r<=(i.innerWidth??1/0)?t.left:Math.max(4,t.right-r);Object.assign(e.style,{position:"fixed",top:`${t.bottom+4}px`,left:`${a}px`,background:d.menuBg,border:`1px solid ${d.menuBorder}`,borderRadius:E.control,boxShadow:S.menu,zIndex:B,width:`${r}px`,maxHeight:"320px",overflowY:"auto",padding:"4px 0",pointerEvents:"auto"}),e.setAttribute("role","dialog"),e.setAttribute("aria-label","Data warnings");const g=n.createElement("div");Object.assign(g.style,{padding:`${s.sm} ${s.xl} ${s.xs}`,fontSize:y.body,fontWeight:"700",letterSpacing:O,textTransform:"uppercase",color:d.label}),g.textContent=o.length===1?"1 warning":`${o.length} warnings`,e.appendChild(g);for(const l of o){const p=severityOf(l),c=$(p),f=n.createElement("div");Object.assign(f.style,{display:"flex",gap:s.md,padding:`${s.md} ${s.xl}`,fontSize:y.body,lineHeight:"1.5",color:d.text,borderTop:`1px solid ${d.menuBorder}`,alignItems:"flex-start"});const x=n.createElement("span");Object.assign(x.style,{color:c.text,flexShrink:"0"}),x.textContent=w(p),f.appendChild(x);const u=n.createElement("div"),b=n.createElement("div");Object.assign(b.style,{fontWeight:"700",color:c.text,marginBottom:s.xxs}),b.textContent=l.code,u.appendChild(b);const h=n.createElement("div");h.textContent=l.message,u.appendChild(h),f.appendChild(u),e.appendChild(f)}return e}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createWafer as createWaferImpl } from './wafer.js';
|
|
2
|
+
import { generateDies as generateDiesImpl, isPositionedDie as isPositionedDieImpl } from './dies.js';
|
|
3
|
+
import { clipDiesToWafer as clipDiesToWaferImpl, applyOrientation as applyOrientationImpl, transformDies as transformDiesImpl, mapDataToDies as mapDataToDiesImpl, isInsideWafer as isInsideWaferImpl, affineIdentity as affineIdentityImpl, affineRotation as affineRotationImpl, affineMirror as affineMirrorImpl, affineCompose as affineComposeImpl, affineInvert as affineInvertImpl, affinePoint as affinePointImpl, affineVector as affineVectorImpl, affineSwapsAxes as affineSwapsAxesImpl } from './transforms.js';
|
|
4
|
+
import { applyProbeSequence as applyProbeSequenceImpl } from './probe.js';
|
|
5
|
+
import { generateReticleGrid as generateReticleGridImpl, getReticleCell as getReticleCellImpl } from './reticle.js';
|
|
6
|
+
import { classifyDie as classifyDieImpl, getRingLabel as getRingLabelImpl } from './classify.js';
|
|
7
|
+
import { aggregateValues as aggregateValuesImpl, aggregateBinCounts as aggregateBinCountsImpl, getUniqueBins as getUniqueBinsImpl } from './aggregates.js';
|
|
8
|
+
import { metadataDisplayValue as metadataDisplayValueImpl, metadataCategoricalValue as metadataCategoricalValueImpl } from './metadata.js';
|
|
9
|
+
import { resolveGridPitch as resolveGridPitchImpl } from './inference/pitch.js';
|
|
10
|
+
/** @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`. */
|
|
11
|
+
export declare const createWafer: typeof createWaferImpl;
|
|
12
|
+
/** @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`. */
|
|
13
|
+
export declare const generateDies: typeof generateDiesImpl;
|
|
14
|
+
/** @deprecated Removed in 0.31.0. Use `hasPosition` to test whether a die has a position. */
|
|
15
|
+
export declare const isPositionedDie: typeof isPositionedDieImpl;
|
|
16
|
+
/** @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`. */
|
|
17
|
+
export declare const clipDiesToWafer: typeof clipDiesToWaferImpl;
|
|
18
|
+
/** @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`. */
|
|
19
|
+
export declare const applyOrientation: typeof applyOrientationImpl;
|
|
20
|
+
/** @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`. */
|
|
21
|
+
export declare const transformDies: typeof transformDiesImpl;
|
|
22
|
+
/** @deprecated Removed in 0.31.0. `buildWaferMap` maps results onto dies. */
|
|
23
|
+
export declare const mapDataToDies: typeof mapDataToDiesImpl;
|
|
24
|
+
/** @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`. */
|
|
25
|
+
export declare const isInsideWafer: typeof isInsideWaferImpl;
|
|
26
|
+
/** @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`. */
|
|
27
|
+
export declare const affineIdentity: typeof affineIdentityImpl;
|
|
28
|
+
/** @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`. */
|
|
29
|
+
export declare const affineRotation: typeof affineRotationImpl;
|
|
30
|
+
/** @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`. */
|
|
31
|
+
export declare const affineMirror: typeof affineMirrorImpl;
|
|
32
|
+
/** @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`. */
|
|
33
|
+
export declare const affineCompose: typeof affineComposeImpl;
|
|
34
|
+
/** @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`. */
|
|
35
|
+
export declare const affineInvert: typeof affineInvertImpl;
|
|
36
|
+
/** @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`. */
|
|
37
|
+
export declare const affinePoint: typeof affinePointImpl;
|
|
38
|
+
/** @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`. */
|
|
39
|
+
export declare const affineVector: typeof affineVectorImpl;
|
|
40
|
+
/** @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`. */
|
|
41
|
+
export declare const affineSwapsAxes: typeof affineSwapsAxesImpl;
|
|
42
|
+
/** @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`. */
|
|
43
|
+
export declare const applyProbeSequence: typeof applyProbeSequenceImpl;
|
|
44
|
+
/** @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`. */
|
|
45
|
+
export declare const generateReticleGrid: typeof generateReticleGridImpl;
|
|
46
|
+
/** @deprecated Removed in 0.31.0. With `reticleConfig` set, each die's tooltip shows its reticle cell. */
|
|
47
|
+
export declare const getReticleCell: typeof getReticleCellImpl;
|
|
48
|
+
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
49
|
+
export declare const classifyDie: typeof classifyDieImpl;
|
|
50
|
+
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
51
|
+
export declare const getRingLabel: typeof getRingLabelImpl;
|
|
52
|
+
/** @deprecated Removed in 0.31.0. Stack wafers with `buildWaferMap`'s `lotStack` option. */
|
|
53
|
+
export declare const aggregateValues: typeof aggregateValuesImpl;
|
|
54
|
+
/** @deprecated Removed in 0.31.0. Stack wafers with `buildWaferMap`'s `lotStack` option. */
|
|
55
|
+
export declare const aggregateBinCounts: typeof aggregateBinCountsImpl;
|
|
56
|
+
/** @deprecated Removed in 0.31.0. Stack wafers with `buildWaferMap`'s `lotStack` option. */
|
|
57
|
+
export declare const getUniqueBins: typeof getUniqueBinsImpl;
|
|
58
|
+
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
59
|
+
export declare const metadataDisplayValue: typeof metadataDisplayValueImpl;
|
|
60
|
+
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
61
|
+
export declare const metadataCategoricalValue: typeof metadataCategoricalValueImpl;
|
|
62
|
+
/** @deprecated Removed in 0.31.0. `buildWaferMap` resolves the die pitch itself; each built die's width and height carry it. */
|
|
63
|
+
export declare const resolveGridPitch: typeof resolveGridPitchImpl;
|
|
64
|
+
//# sourceMappingURL=deprecated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{deprecated as e}from"../renderer/deprecate.js";import{createWafer as s}from"./wafer.js";import{generateDies as r,isPositionedDie as n}from"./dies.js";import{clipDiesToWafer as p,applyOrientation as l,transformDies as f,mapDataToDies as c,isInsideWafer as m,affineIdentity as g,affineRotation as I,affineMirror as d,affineCompose as $,affineInvert as D,affinePoint as u,affineVector as x,affineSwapsAxes as h}from"./transforms.js";import{applyProbeSequence as C}from"./probe.js";import{generateReticleGrid as y,getReticleCell as P}from"./reticle.js";import{classifyDie as V,getRingLabel as W}from"./classify.js";import{aggregateValues as w,aggregateBinCounts as b,getUniqueBins as R}from"./aggregates.js";import{metadataDisplayValue as A,metadataCategoricalValue as E}from"./metadata.js";import{resolveGridPitch as T}from"./inference/pitch.js";const a="If you depend on it, say so at https://github.com/wafertools/wafermap/issues.",t="It belongs to the low-level drawing pipeline, which is being withdrawn: build with buildWaferMap and draw with renderWaferMap or renderWaferGallery.",S="Use hasPosition to test whether a die has a position.",M="buildWaferMap maps results onto dies.",_="With reticleConfig set, each die's tooltip shows its reticle cell.",i="It is an internal helper that was exported by accident; buildWaferMap, analyzeWaferMap and the renderers already apply it.",o="Stack wafers with buildWaferMap's lotStack option.",v="buildWaferMap resolves the die pitch itself; each built die's width and height carry it.";export const createWafer=e(s,"createWafer",`${t} ${a}`),generateDies=e(r,"generateDies",`${t} ${a}`),isPositionedDie=e(n,"isPositionedDie",`${S} ${a}`),clipDiesToWafer=e(p,"clipDiesToWafer",`${t} ${a}`),applyOrientation=e(l,"applyOrientation",`${t} ${a}`),transformDies=e(f,"transformDies",`${t} ${a}`),mapDataToDies=e(c,"mapDataToDies",`${M} ${a}`),isInsideWafer=e(m,"isInsideWafer",`${t} ${a}`),affineIdentity=e(g,"affineIdentity",`${t} ${a}`),affineRotation=e(I,"affineRotation",`${t} ${a}`),affineMirror=e(d,"affineMirror",`${t} ${a}`),affineCompose=e($,"affineCompose",`${t} ${a}`),affineInvert=e(D,"affineInvert",`${t} ${a}`),affinePoint=e(u,"affinePoint",`${t} ${a}`),affineVector=e(x,"affineVector",`${t} ${a}`),affineSwapsAxes=e(h,"affineSwapsAxes",`${t} ${a}`),applyProbeSequence=e(C,"applyProbeSequence",`${t} ${a}`),generateReticleGrid=e(y,"generateReticleGrid",`${t} ${a}`),getReticleCell=e(P,"getReticleCell",`${_} ${a}`),classifyDie=e(V,"classifyDie",`${i} ${a}`),getRingLabel=e(W,"getRingLabel",`${i} ${a}`),aggregateValues=e(w,"aggregateValues",`${o} ${a}`),aggregateBinCounts=e(b,"aggregateBinCounts",`${o} ${a}`),getUniqueBins=e(R,"getUniqueBins",`${o} ${a}`),metadataDisplayValue=e(A,"metadataDisplayValue",`${i} ${a}`),metadataCategoricalValue=e(E,"metadataCategoricalValue",`${i} ${a}`),resolveGridPitch=e(T,"resolveGridPitch",`${v} ${a}`);
|
|
@@ -5,9 +5,7 @@ export * from './metadata.js';
|
|
|
5
5
|
export * from './probe.js';
|
|
6
6
|
export * from './reticle.js';
|
|
7
7
|
export type { DataRow, MapOptions, TransformOptions, Affine, CoordFrame } from './transforms.js';
|
|
8
|
-
export { isInsideWafer, clipDiesToWafer, applyOrientation, transformDies, mapDataToDies } from './transforms.js';
|
|
9
|
-
export { affineIdentity, affineRotation, affineMirror, affineCompose, affineInvert, affinePoint, affineVector, affineSwapsAxes, } from './transforms.js';
|
|
10
8
|
export * from './wafer.js';
|
|
11
9
|
export type { PitchResult } from './inference/pitch.js';
|
|
12
|
-
export { resolveGridPitch } from './
|
|
10
|
+
export { createWafer, generateDies, isPositionedDie, clipDiesToWafer, applyOrientation, transformDies, mapDataToDies, isInsideWafer, affineIdentity, affineRotation, affineMirror, affineCompose, affineInvert, affinePoint, affineVector, affineSwapsAxes, applyProbeSequence, generateReticleGrid, getReticleCell, classifyDie, getRingLabel, aggregateValues, aggregateBinCounts, getUniqueBins, metadataDisplayValue, metadataCategoricalValue, resolveGridPitch, } from './deprecated.js';
|
|
13
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./aggregates.js";export*from"./classify.js";export*from"./dies.js";export*from"./metadata.js";export*from"./probe.js";export*from"./reticle.js";export{
|
|
1
|
+
export*from"./aggregates.js";export*from"./classify.js";export*from"./dies.js";export*from"./metadata.js";export*from"./probe.js";export*from"./reticle.js";export*from"./wafer.js";export{createWafer,generateDies,isPositionedDie,clipDiesToWafer,applyOrientation,transformDies,mapDataToDies,isInsideWafer,affineIdentity,affineRotation,affineMirror,affineCompose,affineInvert,affinePoint,affineVector,affineSwapsAxes,applyProbeSequence,generateReticleGrid,getReticleCell,classifyDie,getRingLabel,aggregateValues,aggregateBinCounts,getUniqueBins,metadataDisplayValue,metadataCategoricalValue,resolveGridPitch}from"./deprecated.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which bins pass, per wafer — and how to name a set of them.
|
|
3
|
+
*
|
|
4
|
+
* The rule this file exists for: `[1]` is a default only where data ENTERS the
|
|
5
|
+
* library (`buildWaferMap`'s input). From there the value travels on the result
|
|
6
|
+
* (`WaferMapResult.passBins`), and every display, analysis and report reads it —
|
|
7
|
+
* per wafer, because a gallery can hold wafers from programs with different pass
|
|
8
|
+
* bins. Every downstream surface used to default `passBins` to `[1]` on its own
|
|
9
|
+
* unless the caller repeated it, so a host that told `buildWaferMap` bins 1 and 2
|
|
10
|
+
* pass saw bin 2 drawn, sorted and totalled as a failure everywhere else — with
|
|
11
|
+
* `result.yield`, the one figure that did use them, disagreeing beside it.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately internal (not re-exported from `core/index.ts`).
|
|
14
|
+
*/
|
|
15
|
+
/** The industry convention, applied ONLY to input that states no pass bins. */
|
|
16
|
+
export declare const INPUT_DEFAULT_PASS_BINS: readonly number[];
|
|
17
|
+
/**
|
|
18
|
+
* One wafer's pass bins: its own (carried from `buildWaferMap`), else the
|
|
19
|
+
* caller's default for items built without any, else the input convention.
|
|
20
|
+
*/
|
|
21
|
+
export declare function itemPassBins(item: {
|
|
22
|
+
passBins?: readonly number[];
|
|
23
|
+
} | null | undefined, fallback?: readonly number[]): readonly number[];
|
|
24
|
+
/** Same bins, in any order. */
|
|
25
|
+
export declare function sameBins(a: readonly number[], b: readonly number[]): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Names the pass bins in use, for a yield label: "bin 1", "bins 1, 2", or —
|
|
28
|
+
* when the wafers shown disagree — "per wafer: bin 1 · bins 1, 2". A label that
|
|
29
|
+
* named one wafer's set while the figure pooled wafers judged differently would
|
|
30
|
+
* be the exact mislabelled population the library must never show.
|
|
31
|
+
*/
|
|
32
|
+
export declare function passBinsLabel(sets: Iterable<readonly number[]>): string;
|
|
33
|
+
//# sourceMappingURL=passBins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const INPUT_DEFAULT_PASS_BINS=[1];export function itemPassBins(s,e=INPUT_DEFAULT_PASS_BINS){return s?.passBins??e}export function sameBins(s,e){const t=new Set(s),n=new Set(e);return t.size===n.size&&[...t].every(i=>n.has(i))}export function passBinsLabel(s){const e=[];for(const n of s)e.some(i=>sameBins(i,n))||e.push(n);const t=n=>n.length===0?"no bins":n.length===1?`bin ${n[0]}`:`bins ${n.join(", ")}`;return e.length<=1?t(e[0]??INPUT_DEFAULT_PASS_BINS):`per wafer: ${e.map(t).join(" \xB7 ")}`}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type WaferMetadata } from './metadata.js';
|
|
2
|
+
/**
|
|
3
|
+
* The name to show for one wafer among several — a gallery card's header, a
|
|
4
|
+
* findings or yield list row, a report's Wafer column, a die list's Wafer
|
|
5
|
+
* column, a detached window's title.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately internal (not re-exported from `core/index.ts`): it is the one
|
|
8
|
+
* rule for every surface that names a wafer, not an API a host needs.
|
|
9
|
+
*
|
|
10
|
+
* Every one of those surfaces used to supply its own fallback for an item with
|
|
11
|
+
* no `label`, and they disagreed: the gallery card header was blank, the
|
|
12
|
+
* findings list and reports said `W3` (a position, which reads as a wafer ID
|
|
13
|
+
* and need not match one), a detached window said "Wafer map", and the
|
|
14
|
+
* Insights tab used the wafer ID. The wafer ID was on the item the whole time.
|
|
15
|
+
*
|
|
16
|
+
* Order: the caller's `label` (a blank one counts as absent), then the wafer's
|
|
17
|
+
* own `metadata.waferId`, then a position that says plainly it is not an ID.
|
|
18
|
+
*/
|
|
19
|
+
export declare function waferDisplayLabel(item: {
|
|
20
|
+
label?: string;
|
|
21
|
+
wafer?: {
|
|
22
|
+
metadata?: WaferMetadata | null;
|
|
23
|
+
} | null;
|
|
24
|
+
} | null | undefined, index: number): string;
|
|
25
|
+
//# sourceMappingURL=waferLabel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{metadataDisplayValue as r}from"./metadata.js";export function waferDisplayLabel(a,e){return(a?.label?.trim()||void 0)??r(a?.wafer?.metadata?.waferId)??`Wafer ${e+1} (no ID)`}
|
|
@@ -12,15 +12,26 @@ export interface BinColors {
|
|
|
12
12
|
hard: Map<number, string>;
|
|
13
13
|
soft: Map<number, string>;
|
|
14
14
|
/**
|
|
15
|
-
* Bins drawn in a colour that another bin of the same type also has —
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* colour alone. Ascending bin number.
|
|
15
|
+
* Bins drawn in a colour that another bin of the same type also has — two
|
|
16
|
+
* bins present whose numbers land on the same palette slot (they differ by a
|
|
17
|
+
* multiple of the palette's length), or a defined colour that duplicates one.
|
|
18
|
+
* Empty when every bin is distinguishable by colour alone. Ascending bin number.
|
|
19
19
|
*/
|
|
20
20
|
shared: {
|
|
21
21
|
hard: number[];
|
|
22
22
|
soft: number[];
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Bins that pass, per type — the verdict the colours were chosen with. Hard:
|
|
26
|
+
* the bins in `passBins`. Soft: bins whose every die passes. Read this, never
|
|
27
|
+
* `passBins`, to order, total or judge SOFT bins: `passBins` holds hard-bin
|
|
28
|
+
* numbers, so testing a soft bin against it treats soft bin 1 as a pass and
|
|
29
|
+
* soft bin 100 as a fail whatever their dies did.
|
|
30
|
+
*/
|
|
31
|
+
pass: {
|
|
32
|
+
hard: Set<number>;
|
|
33
|
+
soft: Set<number>;
|
|
34
|
+
};
|
|
24
35
|
}
|
|
25
36
|
export interface BinColorOptions {
|
|
26
37
|
/** Which bins pass. Default `[1]`. Must match the `passBins` behind the yield figure. */
|
|
@@ -40,26 +51,69 @@ export interface BinColorOptions {
|
|
|
40
51
|
* Assign every bin present in `dies` a colour — the ONE rule for bin colour,
|
|
41
52
|
* shared by the map, its legends, the summary panels and the Insights charts.
|
|
42
53
|
*
|
|
43
|
-
* Why not a function of the bin number, as it used to be:
|
|
44
54
|
* - **Pass/fail comes from `passBins`, never the number.** Passing bins take
|
|
45
55
|
* the palette's pass colours (greens) and failing bins its fail colours, so
|
|
46
|
-
* with `passBins: [1, 3]` bin 3 is green and a failing bin
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* the most distinct
|
|
51
|
-
*
|
|
52
|
-
*
|
|
56
|
+
* with `passBins: [1, 3]` bin 3 is green and a failing bin 1 is not.
|
|
57
|
+
* - **Which colour is keyed by bin number, never by die count.** A pass bin
|
|
58
|
+
* takes `pass[(bin − 1) mod n]` and a fail bin `fail[(bin − 2) mod n]`: bin 1
|
|
59
|
+
* (the conventional pass bin) and bin 2 (the conventional first fail bin)
|
|
60
|
+
* land on the front, most distinct colour of each list, and the low bin
|
|
61
|
+
* numbers most programs use get the clearest colours. So bin 7 is the same
|
|
62
|
+
* colour in every lot, gallery subset and screenshot of a program, which is
|
|
63
|
+
* how engineers learn to read a bin map. This used to rank bins by die count,
|
|
64
|
+
* which gave "the biggest fail bin" a colour rather than any bin — two lots of
|
|
65
|
+
* one program drew the same bin in different colours. Before that it hashed
|
|
66
|
+
* the number, which collided two of the first sixteen soft bins; a modular
|
|
67
|
+
* index collides only at a palette's length apart, and `shared` names those.
|
|
68
|
+
* - **The slot counts from the bin number alone**, not its position among the
|
|
69
|
+
* pass or fail bins, so changing `passBins` recolours only the bins whose
|
|
70
|
+
* verdict changed.
|
|
53
71
|
* - **Soft bins are pass-aware too.** A soft bin passes when every die
|
|
54
72
|
* carrying it passes (by the same per-die rule yield uses); otherwise, or
|
|
55
73
|
* when no die says, it takes a fail colour. The library has the data to
|
|
56
74
|
* decide, so the caller never supplies it.
|
|
75
|
+
* - **Soft bins read the palette shifted by half its length**, so hard bin n
|
|
76
|
+
* and soft bin n are different colours by default. They are separate number
|
|
77
|
+
* spaces and are never on screen together, but a shared colour invites
|
|
78
|
+
* reading one as the other.
|
|
57
79
|
*
|
|
58
80
|
* A gallery resolves ONCE over every wafer it shows and hands the result to every
|
|
59
|
-
* card (`ViewOptions.binColors`)
|
|
81
|
+
* card (`ViewOptions.binColors`): a `BinDef.color` supplied by one item then
|
|
82
|
+
* colours that bin on every card, and `shared` describes the whole gallery.
|
|
60
83
|
* A lone map resolves over its own dies.
|
|
61
84
|
*/
|
|
62
85
|
export declare function resolveBinColors(dies: Iterable<Die>, options?: BinColorOptions): BinColors;
|
|
86
|
+
/** One wafer's dies and the pass bins that wafer was built with. */
|
|
87
|
+
export interface BinPassGroup {
|
|
88
|
+
dies: Iterable<Die>;
|
|
89
|
+
/** Omitted ⇒ the input convention `[1]` — pass `WaferMapResult.passBins`. */
|
|
90
|
+
passBins?: readonly number[];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `resolveBinColors` over several wafers that may carry DIFFERENT pass bins — a
|
|
94
|
+
* gallery of wafers from more than one test program. Each die is judged by its
|
|
95
|
+
* own wafer's `passBins`, and a bin passes only when every die carrying it
|
|
96
|
+
* passes (for one wafer that is exactly "the bin is in `passBins`").
|
|
97
|
+
*
|
|
98
|
+
* `mixedHardBins` names hard bins that pass on some wafers and fail on others.
|
|
99
|
+
* One colour and one legend row cannot be right for both, so the gallery states
|
|
100
|
+
* it (`mixedPassBinsWarning`) rather than letting either wafer's verdict
|
|
101
|
+
* silently stand for all of them.
|
|
102
|
+
*/
|
|
103
|
+
export declare function resolveBinColorsByWafer(groups: Iterable<BinPassGroup>, options?: Omit<BinColorOptions, 'passBins'>): {
|
|
104
|
+
colors: BinColors;
|
|
105
|
+
mixedHardBins: number[];
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Which hard and soft bins pass — `BinColors.pass` without the colours, for a
|
|
109
|
+
* caller that has dies but no resolved assignment (the summary report). The same
|
|
110
|
+
* tally `resolveBinColors` uses, so the two cannot disagree about a verdict.
|
|
111
|
+
*/
|
|
112
|
+
export declare function binPassSets(dies: Iterable<Die>, passBins?: readonly number[]): BinColors['pass'];
|
|
113
|
+
/** `binPassSets` over wafers that each carry their own pass bins. */
|
|
114
|
+
export declare function binPassSetsByWafer(groups: Iterable<BinPassGroup>): BinColors['pass'];
|
|
115
|
+
/** The `pass-bins-mixed` advisory for `resolveBinColorsByWafer`'s `mixedHardBins`, or null. */
|
|
116
|
+
export declare function mixedPassBinsWarning(bins: readonly number[]): WaferWarning | null;
|
|
63
117
|
/**
|
|
64
118
|
* True when `colors` has an entry for every bin on these dies — i.e. it was
|
|
65
119
|
* resolved over a population that includes them. `buildView` checks this
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{diePassStatus as
|
|
1
|
+
import{diePassStatus as g}from"../core/dies.js";import{INPUT_DEFAULT_PASS_BINS as m}from"../core/passBins.js";import{getBinColorScheme as B}from"./colorSchemes.js";export function resolveBinColors(s,n={}){return resolveBinColorsByWafer([{dies:s,passBins:n.passBins}],n).colors}export function resolveBinColorsByWafer(s,n={}){const e=B(n.binColorScheme),i=n.useDefinedBinColors??!0,o=h(s),f=b(o.hardBins,a=>o.pass.hard.has(a),e,i?n.hbinDefs:void 0,!1),t=b(o.softBins,a=>o.pass.soft.has(a),e,i?n.sbinDefs:void 0,!0);return{colors:{hard:f.colors,soft:t.colors,shared:{hard:f.shared,soft:t.shared},pass:o.pass},mixedHardBins:o.mixedHardBins}}export function binPassSets(s,n){return h([{dies:s,passBins:n}]).pass}export function binPassSetsByWafer(s){return h(s).pass}function h(s){const n=new Map,e=new Map,i=new Set;for(const t of s){const a=new Set(t.passBins??m);for(const l of t.dies){const r=g(l,a)===!0;l.hbin!=null&&(n.set(l.hbin,(n.get(l.hbin)??!0)&&r),r&&i.add(l.hbin)),l.sbin!=null&&e.set(l.sbin,(e.get(l.sbin)??!0)&&r)}}const o=t=>new Set([...t].filter(([,a])=>a).map(([a])=>a)),f={hard:o(n),soft:o(e)};return{hardBins:[...n.keys()],softBins:[...e.keys()],pass:f,mixedHardBins:[...i].filter(t=>!f.hard.has(t)).sort((t,a)=>t-a)}}function u(s){return s.length>8?`${s.slice(0,8).join(", ")} and ${s.length-8} more`:s.join(", ")}export function mixedPassBinsWarning(s){if(!s.length)return null;const n=s.length===1;return{code:"pass-bins-mixed",severity:"warning",message:`Hard ${n?"bin":"bins"} ${u(s)} ${n?"passes":"pass"} on some wafers and ${n?"fails":"fail"} on others: these wafers were built with different pass bins. Each wafer's own yield and die verdicts are correct, but a bin has one colour and one legend row, so ${n?"it is":"they are"} shown as failing there.`}}export function binColorsCover(s,n){if(!s.pass)return!1;for(const e of n)if(e.hbin!=null&&!s.hard.has(e.hbin)||e.sbin!=null&&!s.soft.has(e.sbin))return!1;return!0}export function binColorWarning(s,n){const e=n==="softBin"?"soft":n==="hardBin"?"hard":null;if(!e)return null;const i=s.shared[e];if(!i.length)return null;const o=u(i);return{code:"bin-colors-shared",severity:"warning",message:`${e==="hard"?"Hard":"Soft"} bins ${o} are drawn in a colour another bin also has, so colour alone cannot tell them apart. There are more bins than the colour scheme has distinct colours (or a bin definition repeats one). Use the legend, the tooltip, or highlight one bin at a time.`}}const w=1,S=2;function p(s,n,e,i){const o=s.length,f=i?Math.floor(o/2):0;return s[((n-e+f)%o+o)%o]}function b(s,n,e,i,o){const f=new Map;for(const r of i??[])r.color&&f.set(r.bin,r.color);const t=new Map;for(const r of s)t.set(r,f.get(r)??(n(r)?p(e.pass,r,w,o):p(e.fail,r,S,o)));const a=new Map;for(const[r,c]of t){const d=c.toLowerCase();a.set(d,[...a.get(d)??[],r])}const l=[...a.values()].filter(r=>r.length>1).flat().sort((r,c)=>r-c);return{colors:t,shared:l}}
|
|
@@ -155,7 +155,7 @@ export interface View {
|
|
|
155
155
|
allIntegerValues?: boolean;
|
|
156
156
|
/** Named test definitions — populated when `testDefs` is passed to `buildWaferMap`. */
|
|
157
157
|
testDefs?: TestDef[];
|
|
158
|
-
/**
|
|
158
|
+
/** The test number being displayed in `value` plot mode — a `die.testValues` key, not a position. */
|
|
159
159
|
activeTest: number;
|
|
160
160
|
/** True when log₁₀ scale is both requested and valid (vMin > 0). */
|
|
161
161
|
logScale: boolean;
|
|
@@ -217,8 +217,6 @@ export interface View {
|
|
|
217
217
|
* - `'off'` — continuous value gradient (or a non-value plot mode).
|
|
218
218
|
*/
|
|
219
219
|
passFailDisplay: 'off' | 'spec' | 'test';
|
|
220
|
-
/** True when `passFailDisplay === 'spec'` — kept for back-compat with existing consumers. */
|
|
221
|
-
colorBySpec: boolean;
|
|
222
220
|
/** Whether the colorbar range is anchored to spec limits ('spec') or data extents ('data'). */
|
|
223
221
|
colorbarRangeMode: 'spec' | 'data';
|
|
224
222
|
/** Wafer centre in scene/display mm coordinates (the rotation pivot). */
|
|
@@ -284,7 +282,6 @@ export interface ViewOptions {
|
|
|
284
282
|
showRingBoundaries?: boolean;
|
|
285
283
|
showQuadrantBoundaries?: boolean;
|
|
286
284
|
showXYIndicator?: boolean;
|
|
287
|
-
dieGap?: number;
|
|
288
285
|
/** Bin palette for `hardBin`/`softBin` — any name registered via `registerBinColorScheme()`. Default `'default'`. */
|
|
289
286
|
binColorScheme?: string;
|
|
290
287
|
/** Value gradient for `value` and the stacked modes — any name registered via `registerValueColorScheme()`. Default `'default'`. */
|
|
@@ -364,9 +361,10 @@ export interface ViewOptions {
|
|
|
364
361
|
* Which bins count as passing. Default `[1]`.
|
|
365
362
|
*
|
|
366
363
|
* Sets `ViewRect.binFail` and decides which bins take the palette's pass
|
|
367
|
-
* colours (see `resolveBinColors`) — never the bin number.
|
|
368
|
-
*
|
|
369
|
-
*
|
|
364
|
+
* colours (see `resolveBinColors`) — never the bin number. `buildWaferMap` and
|
|
365
|
+
* the renderers set this from `WaferMapResult.passBins`, so a map agrees with
|
|
366
|
+
* its own yield figure; only a hand-rolled pipeline calling `buildView`
|
|
367
|
+
* directly supplies it.
|
|
370
368
|
*/
|
|
371
369
|
passBins?: number[];
|
|
372
370
|
/**
|
|
@@ -374,10 +372,11 @@ export interface ViewOptions {
|
|
|
374
372
|
* Generated by `generateReticleGrid` or the `reticle` option on `buildWaferMap`.
|
|
375
373
|
*/
|
|
376
374
|
reticles?: Reticle[];
|
|
377
|
-
/** Named test definitions — one per `
|
|
375
|
+
/** Named test definitions — one per test number in `die.testValues`. */
|
|
378
376
|
testDefs?: TestDef[];
|
|
379
377
|
/**
|
|
380
|
-
*
|
|
378
|
+
* The test number to display in `value` plot mode — a `die.testValues` key
|
|
379
|
+
* (`TestDef.testNumber`), not a position. Default `0`.
|
|
381
380
|
* When `testDefs` is provided, the toolbar mode dropdown offers one item per test.
|
|
382
381
|
*/
|
|
383
382
|
activeTest?: number;
|
|
@@ -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 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 it,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 P,getTestPassStatus as Q,isParametricTest as bt}from"./buildWaferMap.js";import{fmt as xt,fmtColorbarAxis as Mt,fmtAggregationMethod as Rt}from"./fmt.js";import{metadataValueColor as pe}from"./colorMap.js";import{compareNatural as ye,clamp01 as Nt}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=st(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",Ht="#e8e9ea",be="#aab0ba";function st(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=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 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),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 At(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 u=Q(t,i,s);return u===void 0?[]:[{key:i,label:s.name,value:u?"Pass":"Fail"}]}const o=P(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&&se(t)){const f=oe(t,p);b.push(`Reticle (${f.column}, ${f.row})`)}if(e==="stackedValues"){const f=P(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=P(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?` [${Rt(l)}]`:"";b.push(`${k}: ${m}${x}`)}}else{const f=ve(t,n,o);if(e==="value"&&f.length){const{testNumber:d}=resolveTestNumber(g??0,n),h=findTestDef(n,d),k=P(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=le(d);if(h===void 0)continue;const k=B?.find(m=>m.key===f)?.label??Ot(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?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}=Mt(o,i?.name,i?.unit,e),u=l||"Value",g=t.aggrMethod?` \xB7 ${Rt(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=P(h,d);if(y===void 0)return[];m=At([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=P(h,0);if(y===void 0)return[];m=At([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:ke(h,m),color:x,align:"center"}]})}function Et(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=$=>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(Me(t,d)))}return[{kind:"wafer-boundary",...z($,!0),lineColor:"#888888",lineWidth:1}]}const u=1.25,g=i.length,p=Et(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 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,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 Te(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 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,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:ge,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:n,width:m,height:x,fill:be,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:Ht,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:Ht,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=P(e,p),N=classifySpec(S,T);let _,U;S===void 0?_=j:b==="spec"?_=N==="failLow"?ue: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&&me(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=P(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 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,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,binColorScheme:T="default",valueColorScheme:b="default",reverseValueScheme:$=!1,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:_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===S),Kt={x:(w?.x??!1)!==(x?.flipX??!1),y:(w?.y??!1)!==(x?.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(h),ct=d&&he(d,e)?d:de(e,{passBins:h,binColorScheme:T,hbinDefs:rt,sbinDefs:lt,useDefinedBinColors:f}),Qt=i==="softBin"?ct.soft:ct.hard,vt={forValue:Se(fe(b,$)),forBin:c=>Qt.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),L=v.length?Math.min(...v):void 0;return L!==void 0&&(M===void 0||L<M)?L: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),Ut=I!==void 0&&(I.limitLow!==void 0||I.limitHigh!==void 0);let E="off";i==="value"&&($t?E="test":St==="spec"&&Ut?E="spec":St==="test"&&e.some(c=>Q(c,H,I)!==void 0)&&(E="test"));const Bt=E==="spec"?"spec":wt,ut=i==="stackedValues"||i==="stackedBins"||i==="stackedSoftBins";let O,A,Z=!1;if(J){[O,A]=J;let c=!0,r=!1;for(const M of e){const V=ut?P(M,0):P(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 L=ut?P(v,0):P(v,H);L!==void 0&&(L<r&&(r=L),L>M&&(M=L),Number.isInteger(L)||(V=!1))}isFinite(r)||(V=!1),Z=V,c?(O=I.limitLow!==void 0?I.limitLow:isFinite(r)?r:0,A=I.limitHigh!==void 0?I.limitHigh:isFinite(M)?M:1):(O=isFinite(r)?r:0,A=isFinite(M)?M:1)}O===A&&(A=O+1);const Tt=$t?!1:_t??I?.logScale??!1,It=Tt&&O>0&&A>0,Jt=It;let tt;if(It){const c=Math.log10(O),r=Math.log10(A)-c;tt=M=>M<=0?0:Nt((Math.log10(M)-c)/r)}else tt=c=>Nt((c-O)/(A-O));const F=xe(t,w,x);let Vt=null;if(t.notch){const c=Et(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 Ct=1,Zt=K!==1/0?Math.min(Ct,K*.12):Ct,Ft=[],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 et=ne?new Map:void 0;const dt=i==="value"&&E==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,ht=i==="value"&&E==="test"?{pass:0,fail:0}:void 0,Dt=i==="metadata";let nt=Dt?new Map:void 0,at=null;if(Dt&&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,zt?.values?.find(L=>L.value===V)?.color??pe(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=ae?r.sbin:r.hbin;v!=null&&et.set(v,(et.get(v)??0)+1)}if(dt&&!r.partial&&!r.edgeExcluded){const v=classifySpec(P(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&&!jt||Ce(Ft,r,M,V,i,ee,Zt,vt,k,tt,H,Yt,Gt,I,E,S,at,m)}const Pt=o?generateTextOverlay(e,W,{plotMode:i,colorFns:vt,normalize:tt,activeTest:X,valueRange:[O,A],testDefs:y,fallbackFormat:N,passFailDisplay:E,activeMetadataKey:S,metadataColorMap:at,allIntegerValues:Z}):[],q=$e(t,F.physicalToScreen);p&&q.push(...Be(t,F.physicalToScreen,g)),B&&q.push(...Te(t,F.physicalToScreen,t.center.x,t.center.y)),l&&q.push(...Ie(s,F.gridToScreen)),u&&q.push(...Ve(e)),D&&q.push(...Fe(t,F.gridToScreen,Pt));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:Ft,hoverPoints:ft,texts:Pt,overlays:q,plotMode:i,binColorScheme:T,valueColorScheme:b,reverseValueScheme:$,binColors:ct,metadata:t.metadata??null,dies:e,valueRange:[O,A],allIntegerValues:Z,testDefs:y,activeTest:X,logScale:Jt,logScaleRequested:Tt,aggrMethod:_,lotSize:U,axisFlip:Kt,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:E,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:ie}}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
|
*/
|
|
@@ -460,11 +472,11 @@ export interface WaferWarning {
|
|
|
460
472
|
* - `'test-count-capped'` — raised by `analyzeWaferMap`: more tests were found in
|
|
461
473
|
* the die data than the analysis cap allows, so test-value analysis was skipped
|
|
462
474
|
* entirely and NO test findings were produced. Pass `testNumbers` to scope it.
|
|
463
|
-
* - `'analysis-option-corrected'` — raised by `analyzeWaferMap
|
|
475
|
+
* - `'analysis-option-corrected'` — raised by `analyzeWaferMap` (or `buildWaferMap`, for `ringCount`): a numeric option
|
|
464
476
|
* was outside the range that can produce a meaningful analysis and was clamped.
|
|
465
477
|
* The message names each correction. Only reachable from untyped callers now
|
|
466
|
-
* that the statistical thresholds are internal, but a wrong `
|
|
467
|
-
* `
|
|
478
|
+
* that the statistical thresholds are internal, but a wrong `sectorCount`, or a
|
|
479
|
+
* wrong `ringCount` on `buildWaferMap`, still gets here from TypeScript.
|
|
468
480
|
* - `'edge-exclusion-exceeds-radius'` — `waferConfig.edgeExclusion` is larger than
|
|
469
481
|
* the resolved wafer radius (most likely when the diameter was itself inferred
|
|
470
482
|
* from sparse/partial data). The excluded band is clamped to the whole wafer
|
|
@@ -473,11 +485,26 @@ export interface WaferWarning {
|
|
|
473
485
|
* some bins are drawn in the same colour as another bin (more bins than the
|
|
474
486
|
* bin colour scheme has distinct colours, or a `BinDef.color` repeats one).
|
|
475
487
|
* Every die is drawn correctly; colour alone just cannot separate those bins.
|
|
488
|
+
* - `'pass-bins-mixed'` — raised by `renderWaferGallery`: the wafers shown were
|
|
489
|
+
* built with different pass bins, and some hard bins pass on one wafer and fail
|
|
490
|
+
* on another. Each wafer's verdicts and yield are its own and correct; a bin
|
|
491
|
+
* has one colour and one legend row, so those bins show as failing there.
|
|
492
|
+
* - `'ring-count-mixed'` — raised by `renderWaferGallery`: its wafers were built with
|
|
493
|
+
* different `ringCount`s. Each card and each wafer's findings use their own; the
|
|
494
|
+
* lot-level ring figures (Summary panel, report, Insights) use the count named in
|
|
495
|
+
* the message.
|
|
496
|
+
*
|
|
497
|
+
* - `'input-field-removed'` — raised by `buildWaferMap`: the input used a name
|
|
498
|
+
* removed in an earlier release (`data`, `die`, `stack`, `values`,
|
|
499
|
+
* `TestDef.index`, `dieConfig.origin`, `waferConfig.flat`,
|
|
500
|
+
* `reticleConfig.anchor`, `lotStack.aggr`). It was not honoured, so what it
|
|
501
|
+
* described — for `data` and `values`, the data itself — is missing from the
|
|
502
|
+
* map. The message names each one and its replacement.
|
|
476
503
|
*
|
|
477
504
|
* The union is intentionally open to string so future advisory codes can be
|
|
478
505
|
* added without a breaking change; switch with a `default` branch.
|
|
479
506
|
*/
|
|
480
|
-
code: 'partial-coverage' | 'geometry-conflict' | 'non-standard-diameter' | 'diameter-exceeds-die-extent' | 'test-count-capped' | 'edge-exclusion-exceeds-radius' | 'analysis-option-corrected' | (string & {});
|
|
507
|
+
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' | (string & {});
|
|
481
508
|
/** Human-readable explanation, suitable for direct display. */
|
|
482
509
|
message: string;
|
|
483
510
|
/**
|
|
@@ -562,14 +589,6 @@ export interface WaferMapResult {
|
|
|
562
589
|
grid: {
|
|
563
590
|
confidence: number;
|
|
564
591
|
};
|
|
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
592
|
};
|
|
574
593
|
/**
|
|
575
594
|
* Structured non-fatal advisories raised while inferring geometry from data.
|
|
@@ -599,6 +618,21 @@ export interface WaferMapResult {
|
|
|
599
618
|
/** `filledDies / totalDies` in [0, 1] — `0` when there are no positioned dies, even if `unpositionedDies` is large. */
|
|
600
619
|
ratio: number;
|
|
601
620
|
};
|
|
621
|
+
/**
|
|
622
|
+
* The bins that pass, as given to `buildWaferMap` (default `[1]`) — the ones
|
|
623
|
+
* `yield` was computed with. Carried so everything downstream judges pass/fail
|
|
624
|
+
* the same way: `renderWaferMap`, `renderWaferGallery` (per wafer) and
|
|
625
|
+
* `analyzeWaferMap` all read it, and none of them has a `passBins` option: this
|
|
626
|
+
* is the one place pass bins are set. A map not built by `buildWaferMap`
|
|
627
|
+
* states them in this same field.
|
|
628
|
+
*/
|
|
629
|
+
passBins: number[];
|
|
630
|
+
/**
|
|
631
|
+
* Rings the wafer is divided into (`WaferMapInput.ringCount`, default 4). The
|
|
632
|
+
* renderers, `analyzeWaferMap`, panels and reports read it and none has a ring
|
|
633
|
+
* count option of its own, so ring boundaries and ring findings always agree.
|
|
634
|
+
*/
|
|
635
|
+
ringCount: number;
|
|
602
636
|
/** Yield statistics computed against `passBins`. */
|
|
603
637
|
yield: YieldSummary;
|
|
604
638
|
/** Generated reticle geometry — pass as `viewOptions.reticles` to `renderWaferMap` to show the reticle overlay. */
|