@wafertools/wafermap 0.28.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 +291 -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.d.ts +3 -0
- 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 +81 -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.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 +14 -10
- 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 +23 -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 +21 -6
- package/dist/packages/renderer/colorSchemes.js +1 -1
- 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
|
@@ -72,7 +72,7 @@ export const USER_GUIDE_HTML=`<div class="wmap-guide">
|
|
|
72
72
|
.wmap-guide-toc{display:none!important}
|
|
73
73
|
}
|
|
74
74
|
</style>
|
|
75
|
-
<h1 id="wafer-map-user-guide">Wafer Map \u2014 User Guide<span class="wmap-guide-version" title="Built 2026-09-
|
|
75
|
+
<h1 id="wafer-map-user-guide">Wafer Map \u2014 User Guide<span class="wmap-guide-version" title="Built 2026-09-15T15:29:09.946Z">v0.30.0</span></h1>
|
|
76
76
|
<p class="wmap-guide-online-link">This is a quick reference. <a href="https://wafertools.github.io/wafermap/user-guide/" target="_blank" rel="noopener">View the full illustrated guide online \u2197</a></p>
|
|
77
77
|
<p>This guide describes the display and analysis features of the wafer map viewer.
|
|
78
78
|
It is written for users who may be semiconductor test engineers, device engineers, and yield engineers
|
|
@@ -129,7 +129,9 @@ fail; it simply has no result recorded. Edge-excluded dies are shown dimmed and
|
|
|
129
129
|
not counted in yield calculations.</p>
|
|
130
130
|
<h3 id="14-legend-and-colorbar">1.4 Legend and colorbar</h3>
|
|
131
131
|
<p><strong>Bin modes</strong> show a discrete colour legend: one swatch per bin, with bin number
|
|
132
|
-
and name if the application has supplied bin names
|
|
132
|
+
and name if the application has supplied bin names. Passing bins are listed first,
|
|
133
|
+
then failing bins from the most dies to the fewest \u2014 the same order as the Summary
|
|
134
|
+
panel and reports.</p>
|
|
133
135
|
<p><strong>Value mode</strong> shows a continuous colorbar: the colour scale runs from the minimum
|
|
134
136
|
to maximum value, with units when available. The colorbar is informational only \u2014
|
|
135
137
|
clicking it has no effect. To switch to a pass/fail view, use the <strong>Spec pass/fail</strong>
|
|
@@ -273,7 +275,7 @@ shows die tooltips \u2014 a separate thing from the toolbar, which is always pre
|
|
|
273
275
|
<tr>
|
|
274
276
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Im0xNC42MjIgMTcuODk3LTEwLjY4LTIuOTEzIi8+PHBhdGggZD0iTTE4LjM3NiAyLjYyMmExIDEgMCAxIDEgMy4wMDIgMy4wMDJMMTcuMzYgOS42NDNhLjUuNSAwIDAgMCAwIC43MDdsLjk0NC45NDRhMi40MSAyLjQxIDAgMCAxIDAgMy40MDhsLS45NDQuOTQ0YS41LjUgMCAwIDEtLjcwNyAwTDguMzU0IDcuMzQ4YS41LjUgMCAwIDEgMC0uNzA3bC45NDQtLjk0NGEyLjQxIDIuNDEgMCAwIDEgMy40MDggMGwuOTQ0Ljk0NGEuNS41IDAgMCAwIC43MDcgMHoiLz48cGF0aCBkPSJNOSA4Yy0xLjgwNCAyLjcxLTMuOTcgMy40Ni02LjU4MyAzLjk0OGEuNTA3LjUwNyAwIDAgMC0uMzAyLjgxOWw3LjMyIDguODgzYTEgMSAwIDAgMCAxLjE4NS4yMDRDMTIuNzM1IDIwLjQwNSAxNiAxNi43OTIgMTYgMTUiLz48L3N2Zz4K" width="20" height="20"></td>
|
|
275
277
|
<td>Colour scheme</td>
|
|
276
|
-
<td>Picks the colours for the map on screen. In Hard Bin and Soft Bin modes it lists <strong>bin colours</strong> (Default, Colour-blind safe); in every other mode \u2014 including the stacked modes, which show values \u2014 it lists <strong>value colours</strong>. The two are separate choices, so changing one never resets the other. When bin definitions carry their own colours, <strong>Use colours from bin definitions</strong> turns them on or off. (<strong>Mark failing dies</strong> is in Overlays, beside the pass/fail display it belongs with.)</td>
|
|
278
|
+
<td>Picks the colours for the map on screen. In Hard Bin and Soft Bin modes it lists <strong>bin colours</strong> (Default, Colour-blind safe); in every other mode \u2014 including the stacked modes, which show values \u2014 it lists <strong>value colours</strong>. The two are separate choices, so changing one never resets the other. Value gradients run dark at the low end and bright at the high end, so on a stacked map the healthy wafer sits back and a defect ring or scratch stands out; <strong>Reverse gradient</strong> flips that when the low end is the one you care about. When bin definitions carry their own colours, <strong>Use colours from bin definitions</strong> turns them on or off. (<strong>Mark failing dies</strong> is in Overlays, beside the pass/fail display it belongs with.)</td>
|
|
277
279
|
</tr>
|
|
278
280
|
<tr>
|
|
279
281
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik01IDIxIEw1IDMiLz48cGF0aCBkPSJNMy41IDUgTDUgMyBMNi41IDUiLz48cGF0aCBkPSJNNSAyMSBMMjEgMjEiLz48cGF0aCBkPSJNMTkgMTkuNSBMMjEgMjEgTDE5IDIyLjUiLz48cGF0aCBkPSJNNiAyMCBDIDYgMTIsIDEwIDcsIDIwIDYiLz48L3N2Zz4K" width="20" height="20"></td>
|
|
@@ -367,11 +369,13 @@ failure category across the wafer.</p>
|
|
|
367
369
|
<div data-wmap-demo="bin-highlight" class="wmap-demo"></div><p><em>Bin 2 (Fail) highlighted \u2014 all other bins are dimmed.</em></p>
|
|
368
370
|
<p><strong>Bin colours</strong> \u2014 a bin's colour tells you whether it passed before you read the legend:
|
|
369
371
|
passing bins are always shades of green and failing bins never are, whichever bin numbers
|
|
370
|
-
your test program uses.
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
372
|
+
your test program uses. Each bin number keeps its colour: bin 7 is the same colour in every
|
|
373
|
+
lot, every gallery and every screenshot, however many dies it holds, so you can learn your
|
|
374
|
+
program's colours. Hard and soft bins are coloured separately, so hard bin 3 and soft bin 3
|
|
375
|
+
are different colours. A colour scheme has a limited number of colours, so bins far enough
|
|
376
|
+
apart in number repeat one \u2014 when two of them are on screen together a <strong>Data warnings</strong>
|
|
377
|
+
entry names them, and you can tell them apart with the legend, the tooltip, or by
|
|
378
|
+
highlighting one bin at a time.</p>
|
|
375
379
|
<h3 id="overlays">Overlays</h3>
|
|
376
380
|
<p>Use the <strong>Overlays</strong> menu to toggle optional display layers on and off:</p>
|
|
377
381
|
<ul>
|
|
@@ -452,7 +456,7 @@ the legend, the tooltip, or by highlighting one bin at a time.</p>
|
|
|
452
456
|
<tr>
|
|
453
457
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Im0xNC42MjIgMTcuODk3LTEwLjY4LTIuOTEzIi8+PHBhdGggZD0iTTE4LjM3NiAyLjYyMmExIDEgMCAxIDEgMy4wMDIgMy4wMDJMMTcuMzYgOS42NDNhLjUuNSAwIDAgMCAwIC43MDdsLjk0NC45NDRhMi40MSAyLjQxIDAgMCAxIDAgMy40MDhsLS45NDQuOTQ0YS41LjUgMCAwIDEtLjcwNyAwTDguMzU0IDcuMzQ4YS41LjUgMCAwIDEgMC0uNzA3bC45NDQtLjk0NGEyLjQxIDIuNDEgMCAwIDEgMy40MDggMGwuOTQ0Ljk0NGEuNS41IDAgMCAwIC43MDcgMHoiLz48cGF0aCBkPSJNOSA4Yy0xLjgwNCAyLjcxLTMuOTcgMy40Ni02LjU4MyAzLjk0OGEuNTA3LjUwNyAwIDAgMC0uMzAyLjgxOWw3LjMyIDguODgzYTEgMSAwIDAgMCAxLjE4NS4yMDRDMTIuNzM1IDIwLjQwNSAxNiAxNi43OTIgMTYgMTUiLz48L3N2Zz4K" width="20" height="20"></td>
|
|
454
458
|
<td>Colour scheme</td>
|
|
455
|
-
<td>Picks bin colours (bin modes) or value colours (other modes) for all cards
|
|
459
|
+
<td>Picks bin colours (bin modes) or value colours (other modes) for all cards, including <strong>Reverse gradient</strong>.</td>
|
|
456
460
|
</tr>
|
|
457
461
|
<tr>
|
|
458
462
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIi8+PHBvbHlnb24gcG9pbnRzPSIxNi4yNCA3Ljc2IDE0LjEyIDE0LjEyIDcuNzYgMTYuMjQgOS44OCA5Ljg4IDE2LjI0IDcuNzYiIGZpbGw9IiMzNzQxNTEiIHN0cm9rZT0ibm9uZSIvPjwvc3ZnPgo=" width="20" height="20"></td>
|
|
@@ -482,7 +486,7 @@ the legend, the tooltip, or by highlighting one bin at a time.</p>
|
|
|
482
486
|
<tr>
|
|
483
487
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjciIHg9IjMiIHk9IjMiIHJ4PSIxIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iNyIgeD0iMyIgeT0iMTQiIHJ4PSIxIi8+PHBhdGggZD0iTTE0IDRoNyIvPjxwYXRoIGQ9Ik0xNCA5aDciLz48cGF0aCBkPSJNMTQgMTVoNyIvPjxwYXRoIGQ9Ik0xNCAyMGg3Ii8+PC9zdmc+Cg==" width="20" height="20"></td>
|
|
484
488
|
<td>Legend style</td>
|
|
485
|
-
<td>
|
|
489
|
+
<td><strong>Legend on each map</strong> adds a legend to every card as well as the shared one above the grid (off by default). While it is on, bin modes also let you choose where that legend sits on each card.</td>
|
|
486
490
|
</tr>
|
|
487
491
|
<tr>
|
|
488
492
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik01IDQgTDUgMjAiLz48cGF0aCBkPSJNNSA0IEw4IDQiLz48cGF0aCBkPSJNNSAyMCBMOCAyMCIvPjxwYXRoIGQ9Ik0xOSA0IEwxOSAyMCIvPjxwYXRoIGQ9Ik0xOSA0IEwxNiA0Ii8+PHBhdGggZD0iTTE5IDIwIEwxNiAyMCIvPjxwYXRoIGQ9Ik01IDEyIEwxOSAxMiIgc3Ryb2tlLWRhc2hhcnJheT0iMyAyIi8+PC9zdmc+Cg==" width="20" height="20"></td>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const WMAP_VERSION = "0.
|
|
2
|
-
export declare const WMAP_BUILD_TIME = "2026-09-
|
|
1
|
+
export declare const WMAP_VERSION = "0.30.0";
|
|
2
|
+
export declare const WMAP_BUILD_TIME = "2026-09-15T15:32:11.763Z";
|
|
3
3
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -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}}
|
|
@@ -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
|
|
@@ -148,7 +155,7 @@ export interface View {
|
|
|
148
155
|
allIntegerValues?: boolean;
|
|
149
156
|
/** Named test definitions — populated when `testDefs` is passed to `buildWaferMap`. */
|
|
150
157
|
testDefs?: TestDef[];
|
|
151
|
-
/**
|
|
158
|
+
/** The test number being displayed in `value` plot mode — a `die.testValues` key, not a position. */
|
|
152
159
|
activeTest: number;
|
|
153
160
|
/** True when log₁₀ scale is both requested and valid (vMin > 0). */
|
|
154
161
|
logScale: boolean;
|
|
@@ -210,8 +217,6 @@ export interface View {
|
|
|
210
217
|
* - `'off'` — continuous value gradient (or a non-value plot mode).
|
|
211
218
|
*/
|
|
212
219
|
passFailDisplay: 'off' | 'spec' | 'test';
|
|
213
|
-
/** True when `passFailDisplay === 'spec'` — kept for back-compat with existing consumers. */
|
|
214
|
-
colorBySpec: boolean;
|
|
215
220
|
/** Whether the colorbar range is anchored to spec limits ('spec') or data extents ('data'). */
|
|
216
221
|
colorbarRangeMode: 'spec' | 'data';
|
|
217
222
|
/** Wafer centre in scene/display mm coordinates (the rotation pivot). */
|
|
@@ -277,11 +282,18 @@ export interface ViewOptions {
|
|
|
277
282
|
showRingBoundaries?: boolean;
|
|
278
283
|
showQuadrantBoundaries?: boolean;
|
|
279
284
|
showXYIndicator?: boolean;
|
|
280
|
-
dieGap?: number;
|
|
281
285
|
/** Bin palette for `hardBin`/`softBin` — any name registered via `registerBinColorScheme()`. Default `'default'`. */
|
|
282
286
|
binColorScheme?: string;
|
|
283
287
|
/** Value gradient for `value` and the stacked modes — any name registered via `registerValueColorScheme()`. Default `'default'`. */
|
|
284
288
|
valueColorScheme?: string;
|
|
289
|
+
/**
|
|
290
|
+
* Flip the value gradient so the high end takes its low-end colour. Default
|
|
291
|
+
* false: every built-in reads low = dark, high = light. Set this for a
|
|
292
|
+
* parameter where low is the notable end, or for monochrome print output
|
|
293
|
+
* where more ink should mean more. Applies to the dies, the colorbar and the
|
|
294
|
+
* mapless summary together.
|
|
295
|
+
*/
|
|
296
|
+
reverseValueScheme?: boolean;
|
|
285
297
|
/** Honour `BinDef.color` where a bin definition supplies one. Default true. See `resolveBinColors`. */
|
|
286
298
|
useDefinedBinColors?: boolean;
|
|
287
299
|
/**
|
|
@@ -349,9 +361,10 @@ export interface ViewOptions {
|
|
|
349
361
|
* Which bins count as passing. Default `[1]`.
|
|
350
362
|
*
|
|
351
363
|
* Sets `ViewRect.binFail` and decides which bins take the palette's pass
|
|
352
|
-
* colours (see `resolveBinColors`) — never the bin number.
|
|
353
|
-
*
|
|
354
|
-
*
|
|
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.
|
|
355
368
|
*/
|
|
356
369
|
passBins?: number[];
|
|
357
370
|
/**
|
|
@@ -359,10 +372,11 @@ export interface ViewOptions {
|
|
|
359
372
|
* Generated by `generateReticleGrid` or the `reticle` option on `buildWaferMap`.
|
|
360
373
|
*/
|
|
361
374
|
reticles?: Reticle[];
|
|
362
|
-
/** Named test definitions — one per `
|
|
375
|
+
/** Named test definitions — one per test number in `die.testValues`. */
|
|
363
376
|
testDefs?: TestDef[];
|
|
364
377
|
/**
|
|
365
|
-
*
|
|
378
|
+
* The test number to display in `value` plot mode — a `die.testValues` key
|
|
379
|
+
* (`TestDef.testNumber`), not a position. Default `0`.
|
|
366
380
|
* When `testDefs` is provided, the toolbar mode dropdown offers one item per test.
|
|
367
381
|
*/
|
|
368
382
|
activeTest?: number;
|
|
@@ -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 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";
|