@wafertools/wafermap 0.31.0 → 0.32.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 +12 -12
- package/CHANGELOG.md +172 -0
- package/README.md +3 -3
- package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
- package/dist/packages/canvas-adapter/dieList.js +7 -7
- package/dist/packages/canvas-adapter/drilldown.js +1 -1
- package/dist/packages/canvas-adapter/insightsTab.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +17 -4
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
- package/dist/packages/canvas-adapter/toCanvas.d.ts +23 -2
- 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 +25 -15
- package/dist/packages/canvas-adapter/version.d.ts +2 -2
- package/dist/packages/canvas-adapter/version.js +1 -1
- package/dist/packages/core/aggregates.js +1 -1
- package/dist/packages/core/dieTable.d.ts +137 -0
- package/dist/packages/core/dieTable.js +1 -0
- package/dist/packages/core/dies.d.ts +22 -0
- package/dist/packages/core/dies.js +1 -1
- package/dist/packages/core/utils.d.ts +14 -0
- package/dist/packages/core/utils.js +1 -1
- package/dist/packages/renderer/buildView.d.ts +3 -3
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.d.ts +29 -11
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/renderer/columnarInput.d.ts +87 -0
- package/dist/packages/renderer/columnarInput.js +1 -0
- package/dist/packages/renderer/derivedTests/apply.d.ts +8 -6
- package/dist/packages/renderer/derivedTests/apply.js +1 -1
- package/dist/packages/renderer/index.d.ts +1 -0
- package/dist/packages/renderer/spec.d.ts +3 -7
- package/dist/packages/renderer/spec.js +1 -1
- package/dist/packages/stats/analyzeWaferLot.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.d.ts +134 -2
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/boxplot.js +1 -1
- package/dist/packages/stats/capability.d.ts +7 -7
- package/dist/packages/stats/capability.js +1 -1
- package/dist/packages/stats/clusterDetection.js +1 -1
- package/dist/packages/stats/connectedComponents.js +1 -1
- package/dist/packages/stats/correlation.js +1 -1
- package/dist/packages/stats/filterFindings.d.ts +5 -0
- package/dist/packages/stats/filterFindings.js +1 -1
- package/dist/packages/stats/histogram.js +1 -1
- package/dist/packages/stats/math.d.ts +22 -6
- package/dist/packages/stats/math.js +1 -1
- package/dist/packages/stats/normalizeInput.js +1 -1
- package/dist/packages/stats/patternClassification.d.ts +3 -3
- package/dist/packages/stats/patternClassification.js +1 -1
- package/dist/packages/stats/regions.d.ts +7 -0
- package/dist/packages/stats/regions.js +1 -1
- package/dist/packages/stats/renderSummaryReport.js +18 -18
- package/dist/packages/stats/scatter.js +1 -1
- package/dist/packages/stats/sweep.js +1 -1
- package/dist/packages/stats/testPassRate.js +1 -1
- package/dist/packages/stats/trend.js +1 -1
- package/dist/packages/stats/types.d.ts +5 -0
- package/dist/packages/worker/index.js +1 -1
- package/dist/packages/worker/wafermap.worker.d.ts +3 -0
- package/dist/packages/worker/wafermap.worker.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-27T14:44:40.685Z">v0.32.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
|
|
@@ -144,7 +144,9 @@ automatically, since there is nothing to plot on a gradient.</p>
|
|
|
144
144
|
<p>Every map also shows a short <strong>title</strong> by the colorbar or legend naming what is
|
|
145
145
|
displayed \u2014 the test name (and number, in limit pass/fail mode), the bin type, or the stacked
|
|
146
146
|
wafer count.</p>
|
|
147
|
-
<p>Clicking a bin swatch in the legend filters the display to that bin
|
|
147
|
+
<p>Clicking a bin swatch in the legend filters the display to that bin: every other bin is
|
|
148
|
+
greyed out. <strong>Ctrl / Cmd + click</strong> adds or removes further bins, so several can be shown
|
|
149
|
+
together. Clicking the only bin shown again clears the filter
|
|
148
150
|
(see <a href="#3-toolbar-controls" onclick="(function(e){e.preventDefault();var g=e.target.closest('.wmap-guide');var el=g&&g.querySelector('[id=\\'3-toolbar-controls\\']');if(el)el.scrollIntoView({behavior:'smooth'});})(event)">Highlight bin</a>).</p>
|
|
149
151
|
<h3 id="15-wafers-and-dies-with-no-position-data">1.5 Wafers and dies with no position data</h3>
|
|
150
152
|
<p>Not every die necessarily has a reported grid position \u2014 some data sources supply bin or test
|
|
@@ -261,7 +263,7 @@ appears in a <strong>Derived from</strong> column instead.</p>
|
|
|
261
263
|
<p>The toolbar sits at the top-right of the map and is always visible. Controls that
|
|
262
264
|
are not applicable to the current mode are hidden automatically. (Hovering the map
|
|
263
265
|
shows die tooltips \u2014 a separate thing from the toolbar, which is always present.)</p>
|
|
264
|
-
<p><img src="data:image/png;base64,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" alt="Single-map toolbar"></p>
|
|
266
|
+
<p><img src="data:image/png;base64,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" alt="Single-map toolbar"></p>
|
|
265
267
|
<h3 id="single-map">Single map</h3>
|
|
266
268
|
<table>
|
|
267
269
|
<thead>
|
|
@@ -334,7 +336,7 @@ shows die tooltips \u2014 a separate thing from the toolbar, which is always pre
|
|
|
334
336
|
<tr>
|
|
335
337
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik01IDNhMiAyIDAgMCAwLTIgMiIvPjxwYXRoIGQ9Ik0xOSAzYTIgMiAwIDAgMSAyIDIiLz48cGF0aCBkPSJNMjEgMTlhMiAyIDAgMCAxLTIgMiIvPjxwYXRoIGQ9Ik01IDIxYTIgMiAwIDAgMS0yLTIiLz48cGF0aCBkPSJNOSAzaDEiLz48cGF0aCBkPSJNOSAyMWgxIi8+PHBhdGggZD0iTTE0IDNoMSIvPjxwYXRoIGQ9Ik0xNCAyMWgxIi8+PHBhdGggZD0iTTMgOXYxIi8+PHBhdGggZD0iTTIxIDl2MSIvPjxwYXRoIGQ9Ik0zIDE0djEiLz48cGF0aCBkPSJNMjEgMTR2MSIvPjwvc3ZnPgo=" width="20" height="20"></td>
|
|
336
338
|
<td>Box select</td>
|
|
337
|
-
<td>Click and drag to select a rectangular group of dies (see <a href="#43-box-select" onclick="(function(e){e.preventDefault();var g=e.target.closest('.wmap-guide');var el=g&&g.querySelector('[id=\\'43-box-select\\']');if(el)el.scrollIntoView({behavior:'smooth'});})(event)">Section 4.3</a>).</td>
|
|
339
|
+
<td>Click and drag to select a rectangular group of dies (see <a href="#43-box-select" onclick="(function(e){e.preventDefault();var g=e.target.closest('.wmap-guide');var el=g&&g.querySelector('[id=\\'43-box-select\\']');if(el)el.scrollIntoView({behavior:'smooth'});})(event)">Section 4.3</a>). This is the mode a map opens in.</td>
|
|
338
340
|
</tr>
|
|
339
341
|
<tr>
|
|
340
342
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMyAzdjE4aDE4Ii8+PHBhdGggZD0iTTcgMTZjMy0xIDQtOSA3LTlzMyA1IDYgNSIvPjwvc3ZnPgo=" width="20" height="20"></td>
|
|
@@ -555,18 +557,24 @@ outside the test limits, with the remaining tests summarised as "+N more te
|
|
|
555
557
|
<li><strong>Stacked modes</strong> \u2014 the single aggregated value or count at that position</li>
|
|
556
558
|
</ul>
|
|
557
559
|
<h3 id="42-zoom-and-pan">4.2 Zoom and pan</h3>
|
|
558
|
-
<p>Scroll to zoom in and out.
|
|
559
|
-
|
|
560
|
+
<p>Scroll to zoom in and out. To pan, hold <strong>Space</strong> and drag, use the arrow keys,
|
|
561
|
+
or choose Pan mode in the toolbar so that a plain drag pans. The toolbar also
|
|
562
|
+
provides dedicated <strong>Zoom mode</strong> (drag to draw a zoom region), <strong>Zoom in</strong>,
|
|
560
563
|
<strong>Zoom out</strong>, and <strong>Reset zoom</strong> buttons. Tooltips and die selection remain
|
|
561
564
|
accurate at all zoom levels.</p>
|
|
562
565
|
<h3 id="43-box-select">4.3 Box select</h3>
|
|
563
|
-
<p>
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
566
|
+
<p>Click and drag on the map to draw a selection rectangle \u2014 Box select is the mode
|
|
567
|
+
a map opens in. Click a die to select just that die, and click it again to clear
|
|
568
|
+
it. The application may display statistics or details for the selected dies.
|
|
569
|
+
This is useful for comparing a sub-region against the full wafer.</p>
|
|
570
|
+
<p>Selected dies keep their full colour and every other die is faded, with an
|
|
571
|
+
outline round the selection, so it is clear which dies are selected whatever
|
|
572
|
+
their shape \u2014 a block, a ring or scattered dies. A finding highlighted from the
|
|
573
|
+
Summary panel is shown the same way.</p>
|
|
574
|
+
<p>Use <strong>Ctrl / Cmd + click</strong> to add or remove individual dies. Press <strong>Esc</strong>, or
|
|
575
|
+
click outside the wafer, to clear the selection.</p>
|
|
576
|
+
<div data-wmap-demo="box-select" class="wmap-demo"></div><p><em>A block of dies near the centre is shown pre-selected. Drag on the map to make
|
|
577
|
+
your own selection, or Ctrl/Cmd + click individual dies.</em></p>
|
|
570
578
|
<h3 id="44-charting-dies-and-wafers">4.4 Charting dies and wafers</h3>
|
|
571
579
|
<p><strong>Right-click</strong> a population to open a menu of charts drawn from just those
|
|
572
580
|
dies. Picking one opens it in a window over the map. This is how you look at a
|
|
@@ -618,8 +626,10 @@ detected patterns on the wafer. Open it from the toolbar.</p>
|
|
|
618
626
|
<ul>
|
|
619
627
|
<li><strong>Severity</strong> \u2014 Unusual, Notable, or Info (ordered most to least significant)</li>
|
|
620
628
|
<li><strong>Description</strong> \u2014 plain-language summary of what was detected and where</li>
|
|
621
|
-
<li><strong>Click to highlight</strong> \u2014 clicking a finding
|
|
622
|
-
|
|
629
|
+
<li><strong>Click to highlight</strong> \u2014 clicking a finding fades the rest of the wafer and outlines the
|
|
630
|
+
affected dies. A finding about a bin also filters the legend to that bin, so the legend
|
|
631
|
+
always names what the finding describes; a finding about yield or a test clears the
|
|
632
|
+
filter. Changing the selection or the legend filter yourself releases the finding.</li>
|
|
623
633
|
</ul>
|
|
624
634
|
<p><strong>Severity</strong> reflects how strong the statistical evidence is. <em>Unusual</em> findings
|
|
625
635
|
have both a very low adjusted p-value and a large effect size \u2014 they are reliably
|
|
@@ -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.32.0";
|
|
2
|
+
export declare const WMAP_BUILD_TIME = "2026-09-27T14:45:45.383Z";
|
|
3
3
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const WMAP_VERSION="0.
|
|
1
|
+
export const WMAP_VERSION="0.32.0",WMAP_BUILD_TIME="2026-09-27T14:45:45.383Z";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{copyDie as p,testValue as m}from"./dieTable.js";import{minOf as M,maxOf as V}from"./utils.js";import{positionKey as h}from"./dies.js";export function aggregateValues(l,n,c=0){if(!l.length)return[];const a=new Map,o=new Map;for(const g of l)for(const t of g){const e=h(t),s=m(t,c);s!==void 0?(a.has(e)||(a.set(e,[]),o.set(e,t)),a.get(e).push(s)):o.has(e)||o.set(e,t)}const u=[];for(const[g,t]of o){const e=a.get(g);if(!e?.length){u.push(p(t,{testValues:void 0,testPass:void 0}));continue}let s;if(n==="mean")s=e.reduce((f,i)=>f+i,0)/e.length;else if(n==="median"){const f=[...e].sort((r,d)=>r-d),i=Math.floor(f.length/2);s=f.length%2?f[i]:(f[i-1]+f[i])/2}else if(n==="stddev"){if(e.length<2){u.push(p(t,{testValues:{0:0},testPass:void 0}));continue}const f=e.reduce((i,r)=>i+r,0)/e.length;s=Math.sqrt(e.reduce((i,r)=>i+(r-f)**2,0)/(e.length-1))}else n==="min"?s=M(e):n==="max"?s=V(e):s=e.length;u.push(p(t,{testValues:{0:s},testPass:void 0}))}return u}export function aggregateBinCounts(l,n,c="hard"){if(!l.length)return[];const a=new Map;for(const o of l)for(const u of o)if((c==="soft"?u.sbin:u.hbin)===n){const t=h(u);a.set(t,(a.get(t)??0)+1)}return(l[0]??[]).map(o=>p(o,{testValues:{0:a.get(h(o))??0},testPass:void 0,...c==="soft"?{sbin:n}:{hbin:n}}))}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { Die } from './dies.js';
|
|
2
|
+
/** Verdict column entries: no verdict / fail / pass. */
|
|
3
|
+
export declare const VERDICT_NONE = -1;
|
|
4
|
+
export declare const VERDICT_FAIL = 0;
|
|
5
|
+
export declare const VERDICT_PASS = 1;
|
|
6
|
+
/** `DieTable.fields` bits: the record carried a `testValues` / `testPass` object. */
|
|
7
|
+
export declare const HAS_VALUES = 1;
|
|
8
|
+
export declare const HAS_VERDICTS = 2;
|
|
9
|
+
export interface DieTable {
|
|
10
|
+
/** Number of rows. A die's row is an index below this. */
|
|
11
|
+
readonly rows: number;
|
|
12
|
+
/** Test number → value per row; `NaN` = no value. Stored as supplied, never narrowed. */
|
|
13
|
+
readonly values: ReadonlyMap<number, Float32Array | Float64Array>;
|
|
14
|
+
/** Test number → verdict per row: −1 none, 0 fail, 1 pass. */
|
|
15
|
+
readonly verdicts: ReadonlyMap<number, Int8Array>;
|
|
16
|
+
/**
|
|
17
|
+
* Per row, which of `testValues` / `testPass` the die exposes (bits
|
|
18
|
+
* `HAS_VALUES`, `HAS_VERDICTS`): the fields its record carried, so a die
|
|
19
|
+
* with no verdicts has no `testPass`, exactly as before.
|
|
20
|
+
*/
|
|
21
|
+
readonly fields: Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
/** What the readers need of a die. A `DieResult` qualifies. */
|
|
24
|
+
export type DieData = Pick<Die, 'testValues' | 'testPass'>;
|
|
25
|
+
/** Marks a die being built as `row` of `table`, in place. `finaliseDie` completes it. */
|
|
26
|
+
export declare function linkDie<D extends object>(die: D, table: DieTable, row: number): D;
|
|
27
|
+
/** The table and row `die` is linked to, or `undefined` for a die with its own objects. */
|
|
28
|
+
export declare function dieLink(die: DieData): {
|
|
29
|
+
table: DieTable;
|
|
30
|
+
row: number;
|
|
31
|
+
} | undefined;
|
|
32
|
+
type Row = {
|
|
33
|
+
testValues?: Record<number, number>;
|
|
34
|
+
testPass?: Record<number, boolean>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A table built from row-shaped records (`DieResult`s): row `i` holds
|
|
38
|
+
* `rows[i]`'s values and verdicts. Values are copied as-is (`Float64Array`,
|
|
39
|
+
* the precision a JS number has); a missing entry is `NaN` / no verdict.
|
|
40
|
+
*/
|
|
41
|
+
export declare function tableFromRows(rows: readonly Row[]): DieTable;
|
|
42
|
+
/**
|
|
43
|
+
* A copy of `table` with more columns: `values` / `verdicts` are added (a test
|
|
44
|
+
* number already present is replaced), and `fields` is copied, so the caller
|
|
45
|
+
* can mark rows that gained data. The original table is never changed.
|
|
46
|
+
*/
|
|
47
|
+
export declare function extendTable(table: DieTable, values: ReadonlyMap<number, Float32Array | Float64Array>, verdicts: ReadonlyMap<number, Int8Array>): DieTable & {
|
|
48
|
+
fields: Uint8Array;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* One reusable die-shaped view over `table`: `at(row)` points it at a row and
|
|
52
|
+
* returns it, so a rule that takes a die (`getTestPassStatus`) can be applied
|
|
53
|
+
* row by row without an object per row. Valid until the next `at`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function rowView(table: DieTable): (row: number) => DieData;
|
|
56
|
+
/**
|
|
57
|
+
* The die a host receives, from a die built with `linkDie`: a fresh object with
|
|
58
|
+
* the same fields, a non-enumerable link, and `testValues` / `testPass` getters
|
|
59
|
+
* for the fields its record carried. A die with no link is returned as is.
|
|
60
|
+
*
|
|
61
|
+
* A die that already carries a value object its record does not replace (a
|
|
62
|
+
* host-supplied layout die with its own `testPass`, say, and a record with
|
|
63
|
+
* only values) is not linked: it gets its record's objects built onto it now,
|
|
64
|
+
* beside its own, which is exactly what it had before tables existed.
|
|
65
|
+
*/
|
|
66
|
+
export declare function finaliseDie<D extends Die>(die: D): D;
|
|
67
|
+
/**
|
|
68
|
+
* A copy of `die` with `patch` applied. For a linked die this copies the
|
|
69
|
+
* fields and the link without calling the getters: an object spread would
|
|
70
|
+
* call them and build the value objects of every die it copies (3 s and the
|
|
71
|
+
* lot's full memory on 266k dies). A patch that sets `testValues` or
|
|
72
|
+
* `testPass` gives an own-objects die (the other field built from the row).
|
|
73
|
+
*/
|
|
74
|
+
export declare function copyDie<D extends DieData>(die: D, patch: Partial<D>): D;
|
|
75
|
+
/** What `detachTables` sends beside the dies: their tables, and each die's table and row. */
|
|
76
|
+
export interface DetachedTables {
|
|
77
|
+
tables: DieTable[];
|
|
78
|
+
/** Per die, in order across every die array: its table's index in `tables` (−1 unlinked), then its row. */
|
|
79
|
+
links: Int32Array;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Prepares linked dies to be posted from a worker without their values being
|
|
83
|
+
* built and copied: takes their `testValues` / `testPass` getters off (a
|
|
84
|
+
* structured clone would call them), and returns their tables and links. Post
|
|
85
|
+
* the dies, and the result with `transfer` as the transfer list, which moves
|
|
86
|
+
* the columns without a copy; the other side calls `attachTables`.
|
|
87
|
+
*
|
|
88
|
+
* **Changes the dies in place**, and transferring empties the columns: only
|
|
89
|
+
* for dies the sender discards once they are posted.
|
|
90
|
+
*/
|
|
91
|
+
export declare function detachTables(dieArrays: ReadonlyArray<readonly Die[]>): DetachedTables & {
|
|
92
|
+
transfer: ArrayBuffer[];
|
|
93
|
+
};
|
|
94
|
+
/** The receiving half of `detachTables`: links the posted dies to their tables again, in place. */
|
|
95
|
+
export declare function attachTables(dieArrays: ReadonlyArray<readonly Die[]>, detached: DetachedTables): void;
|
|
96
|
+
/** `die`'s value for test `testNumber`, or `undefined` when it has none. */
|
|
97
|
+
export declare function testValue(die: DieData, testNumber: number): number | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* `die`'s recorded verdict for test `testNumber` (true = pass), or `undefined`.
|
|
100
|
+
* The raw record only: `getTestPassStatus` owns the functional-test fallback
|
|
101
|
+
* and is what callers use.
|
|
102
|
+
*/
|
|
103
|
+
export declare function recordedVerdict(die: DieData, testNumber: number): boolean | undefined;
|
|
104
|
+
/** True when `die` has any measured value or recorded verdict. */
|
|
105
|
+
export declare function dieHasAnyTestData(die: DieData): boolean;
|
|
106
|
+
/** True when `die` has at least one measured value. */
|
|
107
|
+
export declare function dieHasValues(die: DieData): boolean;
|
|
108
|
+
/** True when `die` has at least one recorded verdict. */
|
|
109
|
+
export declare function dieHasVerdicts(die: DieData): boolean;
|
|
110
|
+
/** `die`'s values as `[testNumber, value]`, test numbers ascending. */
|
|
111
|
+
export declare function dieValueEntries(die: DieData): Array<[number, number]>;
|
|
112
|
+
/** `die`'s recorded verdicts as `[testNumber, pass]`, test numbers ascending. */
|
|
113
|
+
export declare function dieVerdictEntries(die: DieData): Array<[number, boolean]>;
|
|
114
|
+
/**
|
|
115
|
+
* A reader of tests `testNumbers` for die after die: `read(die, out)` writes
|
|
116
|
+
* test `testNumbers[k]`'s value to `out[k]` (`NaN` when none) and returns
|
|
117
|
+
* `out`. For a linked die it is one indexed read per test: the columns are
|
|
118
|
+
* looked up once per table, not once per die. The bulk path for statistics
|
|
119
|
+
* that walk many dies × many tests.
|
|
120
|
+
*/
|
|
121
|
+
export declare function testValuesReader(testNumbers: readonly number[]): (die: DieData, out: Float64Array) => Float64Array;
|
|
122
|
+
/**
|
|
123
|
+
* {@link testValuesReader} for recorded verdicts: `read(die, out)` writes test
|
|
124
|
+
* `testNumbers[k]`'s recorded verdict to `out[k]` — `VERDICT_PASS`,
|
|
125
|
+
* `VERDICT_FAIL` or `VERDICT_NONE` — and returns `out`. The raw record only, as
|
|
126
|
+
* {@link recordedVerdict}: `testPassStatusReader` adds the functional-test
|
|
127
|
+
* fallback and is what callers use.
|
|
128
|
+
*/
|
|
129
|
+
export declare function recordedVerdictsReader(testNumbers: readonly number[]): (die: DieData, out: Int8Array) => Int8Array;
|
|
130
|
+
/**
|
|
131
|
+
* Every test number with at least one value or verdict among `dies` (only
|
|
132
|
+
* values, with `which: 'values'`), ascending. Linked dies are checked column
|
|
133
|
+
* by column per table, not die by die, and each column stops at its first hit.
|
|
134
|
+
*/
|
|
135
|
+
export declare function testsPresent(dies: readonly DieData[], which?: 'all' | 'values'): number[];
|
|
136
|
+
export {};
|
|
137
|
+
//# sourceMappingURL=dieTable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{hasAnyKey as O}from"./utils.js";const k=NaN;export const VERDICT_NONE=-1,VERDICT_FAIL=0,VERDICT_PASS=1,HAS_VALUES=1,HAS_VERDICTS=2;const a=Symbol("wmap.dieTable"),u=Symbol("wmap.dieRow");export function linkDie(e,t,s){return e[a]=t,e[u]=s,e}export function dieLink(e){const t=e[a];return t===void 0?void 0:{table:t,row:e[u]}}export function tableFromRows(e){const t=e.length,s=new Map,r=new Map,o=new Uint8Array(t),n=g(s,()=>new Float64Array(t).fill(k)),i=g(r,()=>new Int8Array(t).fill(VERDICT_NONE));for(let f=0;f<t;f++){const c=e[f].testValues;if(c){o[f]|=HAS_VALUES;const d=n(c),w=Object.values(c);for(let b=0;b<d.length;b++)d[b][f]=w[b]}const l=e[f].testPass;if(l){o[f]|=HAS_VERDICTS;const d=i(l),w=Object.values(l);for(let b=0;b<d.length;b++)d[b][f]=w[b]?VERDICT_PASS:VERDICT_FAIL}}return{rows:t,values:m(s),verdicts:m(r),fields:o}}function g(e,t){let s=[],r=[];return o=>{const n=Object.keys(o);let i=n.length===s.length;for(let f=0;i&&f<n.length;f++)i=n[f]===s[f];return i||(s=n,r=n.map(f=>{let c=e.get(f);return c===void 0&&e.set(f,c=t()),c})),r}}function m(e){return new Map([...e].map(([t,s])=>[Number(t),s]).sort((t,s)=>t[0]-s[0]))}export function extendTable(e,t,s){const r=new Map(e.values),o=new Map(e.verdicts);for(const[n,i]of t)r.set(n,i);for(const[n,i]of s)o.set(n,i);return{rows:e.rows,values:new Map([...r].sort((n,i)=>n[0]-i[0])),verdicts:new Map([...o].sort((n,i)=>n[0]-i[0])),fields:e.fields.slice()}}export function rowView(e){const t=linkDie({},e,0);return s=>(t[u]=s,t)}function v(e,t){const s={},r=[...e.values];for(let o=r.length-1;o>=0;o--){const n=r[o][1][t];Number.isNaN(n)||(s[r[o][0]]=n)}return s}function V(e,t){const s={},r=[...e.verdicts];for(let o=r.length-1;o>=0;o--){const n=r[o][1][t];n!==VERDICT_NONE&&(s[r[o][0]]=n===VERDICT_PASS)}return s}function x(){return Object.freeze(v(this[a],this[u]))}function N(){return Object.freeze(V(this[a],this[u]))}function j(e){return function(){throw new TypeError(`wafermap: die.${e} is read-only on a die built by buildWaferMap (die ${this.id}). Pass the values you want to buildWaferMap, or copy the die: { ...die, ${e}: yours }.`)}}const y={get:x,set:j("testValues"),enumerable:!0,configurable:!0},P={get:N,set:j("testPass"),enumerable:!0,configurable:!0},p=e=>({value:e,writable:!0,enumerable:!1,configurable:!0});export function finaliseDie(e){const t=e[a];if(t===void 0)return e;const s=e[u],r=t.fields[s],o=e.testValues!==void 0&&!(r&HAS_VALUES)||e.testPass!==void 0&&!(r&HAS_VERDICTS),n={};for(const i of Object.keys(e))i==="testValues"&&r&HAS_VALUES||i==="testPass"&&r&HAS_VERDICTS||(n[i]=e[i]);return o?(r&HAS_VALUES&&(n.testValues=v(t,s)),r&HAS_VERDICTS&&(n.testPass=V(t,s)),n):(Object.defineProperty(n,a,p(t)),Object.defineProperty(n,u,p(s)),r&HAS_VALUES&&Object.defineProperty(n,"testValues",y),r&HAS_VERDICTS&&Object.defineProperty(n,"testPass",P),n)}export function copyDie(e,t){const s=e[a];if(s===void 0)return{...e,...t};const r=e[u],o=s.fields[r],n={};for(const i of Object.keys(e))i!=="testValues"&&i!=="testPass"&&(n[i]=e[i]);return"testValues"in t||"testPass"in t?(o&HAS_VALUES&&!("testValues"in t)&&(n.testValues=v(s,r)),o&HAS_VERDICTS&&!("testPass"in t)&&(n.testPass=V(s,r)),Object.assign(n,t)):(Object.assign(n,t),Object.defineProperty(n,a,p(s)),Object.defineProperty(n,u,p(r)),o&HAS_VALUES&&Object.defineProperty(n,"testValues",y),o&HAS_VERDICTS&&Object.defineProperty(n,"testPass",P),n)}export function detachTables(e){const t=[],s=new Map,r=e.reduce((f,c)=>f+c.length,0),o=new Int32Array(r*2);let n=0;for(const f of e)for(const c of f){const l=dieLink(c);if(l===void 0){o[n++]=-1,o[n++]=0;continue}let d=s.get(l.table);d===void 0&&(s.set(l.table,d=t.length),t.push(l.table)),o[n++]=d,o[n++]=l.row,delete c.testValues,delete c.testPass}const i=new Set;for(const f of t){for(const c of f.values.values())i.add(c.buffer);for(const c of f.verdicts.values())i.add(c.buffer);i.add(f.fields.buffer)}return{tables:t,links:o,transfer:[...i]}}export function attachTables(e,t){let s=0;for(const r of e)for(const o of r){const n=t.links[s++],i=t.links[s++];if(n<0)continue;const f=t.tables[n],c=f.fields[i];Object.defineProperty(o,a,p(f)),Object.defineProperty(o,u,p(i)),c&HAS_VALUES&&Object.defineProperty(o,"testValues",y),c&HAS_VERDICTS&&Object.defineProperty(o,"testPass",P)}}export function testValue(e,t){const s=e[a];if(s===void 0)return e.testValues?.[t];const r=s.values.get(t)?.[e[u]];return r===void 0||Number.isNaN(r)?void 0:r}export function recordedVerdict(e,t){const s=e[a];if(s===void 0)return e.testPass?.[t];const r=s.verdicts.get(t)?.[e[u]];return r===void 0||r===VERDICT_NONE?void 0:r===VERDICT_PASS}export function dieHasAnyTestData(e){return dieHasValues(e)||dieHasVerdicts(e)}export function dieHasValues(e){const t=dieLink(e);if(t===void 0)return O(e.testValues);for(const s of t.table.values.values())if(!Number.isNaN(s[t.row]))return!0;return!1}export function dieHasVerdicts(e){const t=dieLink(e);if(t===void 0)return O(e.testPass);for(const s of t.table.verdicts.values())if(s[t.row]!==VERDICT_NONE)return!0;return!1}export function dieValueEntries(e){const t=dieLink(e);if(t===void 0){const r=e.testValues;return r===void 0?[]:Object.keys(r).map(o=>[Number(o),r[Number(o)]])}const s=[];for(const[r,o]of t.table.values){const n=o[t.row];Number.isNaN(n)||s.push([r,n])}return s}export function dieVerdictEntries(e){const t=dieLink(e);if(t===void 0){const r=e.testPass;return r===void 0?[]:Object.keys(r).map(o=>[Number(o),r[Number(o)]])}const s=[];for(const[r,o]of t.table.verdicts){const n=o[t.row];n!==VERDICT_NONE&&s.push([r,n===VERDICT_PASS])}return s}export function testValuesReader(e){let t,s=[];return function(o,n){const i=dieLink(o);if(i===void 0){const f=o.testValues;for(let c=0;c<e.length;c++){const l=f?.[e[c]];n[c]=l===void 0?NaN:l}return n}i.table!==t&&(t=i.table,s=e.map(f=>i.table.values.get(f)));for(let f=0;f<s.length;f++){const c=s[f];n[f]=c===void 0?NaN:c[i.row]}return n}}export function recordedVerdictsReader(e){let t,s=[];return function(o,n){const i=dieLink(o);if(i===void 0){const f=o.testPass;for(let c=0;c<e.length;c++){const l=f?.[e[c]];n[c]=l===void 0?VERDICT_NONE:l?VERDICT_PASS:VERDICT_FAIL}return n}i.table!==t&&(t=i.table,s=e.map(f=>i.table.verdicts.get(f)));for(let f=0;f<s.length;f++){const c=s[f];n[f]=c===void 0?VERDICT_NONE:c[i.row]}return n}}export function testsPresent(e,t="all"){const s=new Set,r=new Map;for(const o of e){const n=dieLink(o);if(n===void 0){if(o.testValues)for(const f in o.testValues)s.add(Number(f));if(t==="all"&&o.testPass)for(const f in o.testPass)s.add(Number(f));continue}let i=r.get(n.table);i===void 0&&r.set(n.table,i=[]),i.push(n.row)}for(const[o,n]of r){for(const[i,f]of o.values)!s.has(i)&&n.some(c=>!Number.isNaN(f[c]))&&s.add(i);if(t!=="values")for(const[i,f]of o.verdicts)!s.has(i)&&n.some(c=>f[c]!==VERDICT_NONE)&&s.add(i)}return[...s].sort((o,n)=>o-n)}
|
|
@@ -141,6 +141,28 @@ export declare function getDieKey(die: {
|
|
|
141
141
|
y?: number;
|
|
142
142
|
id?: string;
|
|
143
143
|
}): string;
|
|
144
|
+
/**
|
|
145
|
+
* A number identifying grid position (`x`, `y`), for neighbour lookups inside
|
|
146
|
+
* the library: cluster detection and connected components probe up to 25 cells
|
|
147
|
+
* per die, and building a `getDieKey` string for each probe was most of their
|
|
148
|
+
* cost. Exact and collision-free for integer coordinates with |x| < 2^20 and
|
|
149
|
+
* |y| < 2^31, which covers STDF's ±32767 many times over. Never shown and
|
|
150
|
+
* never stored — findings carry `getDieKey` strings.
|
|
151
|
+
*/
|
|
152
|
+
export declare function gridKey(x: number, y: number): number;
|
|
153
|
+
/**
|
|
154
|
+
* A die's `getDieKey` identity as a value that is cheap to hash: two dies get
|
|
155
|
+
* equal position keys exactly when their `getDieKey` strings are equal. Integer
|
|
156
|
+
* coordinates in STDF's range map one-to-one to {@link gridKey}; anything else
|
|
157
|
+
* keeps its `getDieKey` string. For sets and maps over many dies (stacked cards,
|
|
158
|
+
* analysis), where building a string per die was most of the work. Never shown
|
|
159
|
+
* and never stored — findings carry `getDieKey` strings.
|
|
160
|
+
*/
|
|
161
|
+
export declare function positionKey(die: {
|
|
162
|
+
x?: number;
|
|
163
|
+
y?: number;
|
|
164
|
+
id?: string;
|
|
165
|
+
}): number | string;
|
|
144
166
|
export declare function isYieldEligibleDie(die: Die, options?: DieEligibilityOptions): boolean;
|
|
145
167
|
/**
|
|
146
168
|
* Whether a die passed: its hard bin (else its soft bin) is in `passBins`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function hasPosition(
|
|
1
|
+
export function hasPosition(t){return t.x!=null&&t.y!=null}export function isPositionedDie(t){return t.x!=null&&t.y!=null}export function getDieKey(t){return hasPosition(t)?`${t.x},${t.y}`:`id:${t.id}`}export function gridKey(t,e){return t*4294967296+e}export function positionKey(t){const{x:e,y:n}=t;return Number.isInteger(e)&&Number.isInteger(n)&&Math.abs(e)<=32767&&Math.abs(n)<=32767?gridKey(e,n):getDieKey(t)}export function isYieldEligibleDie(t,e={}){return!(t.partial||!e.includeEdgeExcluded&&t.edgeExcluded)}export function diePassStatus(t,e){const n=t.hbin??t.sbin;return n===void 0?void 0:e.has(n)}export function generateDies(t,e){const{width:n,height:o,offset:s={x:0,y:0}}=e,u=e.gridSize??Math.ceil(t.radius/Math.min(n,o))+1,c=[];for(let r=-u;r<=u;r++)for(let i=-u;i<=u;i++){const l=t.center.x+i*n+s.x,x=t.center.y+r*o+s.y;c.push({id:`${i}_${r}`,x:i,y:r,physX:l,physY:x,width:n,height:o})}return c}
|
|
@@ -25,6 +25,12 @@ export declare function compareNatural(a: string, b: string): number;
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function prettyKey(key: string): string;
|
|
27
27
|
/** Clamp `v` into [0, 1] — the normalization range every colour scale expects. */
|
|
28
|
+
/**
|
|
29
|
+
* Whether `obj` has an own key. The per-die form of `Object.keys(obj).length > 0`
|
|
30
|
+
* without building the key array: on a 266k-die lot that allocation, once per
|
|
31
|
+
* die, cost seconds in WebKit's engine (the desktop app on Linux and macOS).
|
|
32
|
+
*/
|
|
33
|
+
export declare function hasAnyKey<T extends object>(obj: T | null | undefined): obj is T;
|
|
28
34
|
/**
|
|
29
35
|
* Smallest / largest of `values`, by iteration rather than `Math.min(...values)`.
|
|
30
36
|
*
|
|
@@ -135,4 +141,12 @@ export type Chunked<T> = Generator<void, T, void>;
|
|
|
135
141
|
/** Run a {@link Chunked} computation to completion without yielding, and
|
|
136
142
|
* return its result — the synchronous entry point for any chunked builder. */
|
|
137
143
|
export declare function drain<T>(work: Chunked<T>): T;
|
|
144
|
+
/** A highlight option (one value or several) as a list; `undefined` is none. */
|
|
145
|
+
export declare function asList<T>(value: T | readonly T[] | undefined): readonly T[];
|
|
146
|
+
/**
|
|
147
|
+
* The highlight after a click on `value`. A plain click shows only that value,
|
|
148
|
+
* or clears the highlight when it was the only one; an additive click
|
|
149
|
+
* (Ctrl/Cmd) adds or removes it. One value stays a plain value.
|
|
150
|
+
*/
|
|
151
|
+
export declare function toggleHighlight<T>(current: T | readonly T[] | undefined, value: T, additive: boolean): T | T[] | undefined;
|
|
138
152
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function compareNatural(t,
|
|
1
|
+
export function compareNatural(t,r){return t.localeCompare(r,"en",{numeric:!0})}export function prettyKey(t){return t.replace(/([A-Z])/g," $1").replace(/_/g," ").trim().replace(/^./,r=>r.toUpperCase())}export function hasAnyKey(t){if(t==null)return!1;for(const r in t)if(Object.hasOwn(t,r))return!0;return!1}export function minOf(t){if(t.length===0)return 0;let r=t[0];for(let e=1;e<t.length;e++)t[e]<r&&(r=t[e]);return r}export function maxOf(t){if(t.length===0)return 0;let r=t[0];for(let e=1;e<t.length;e++)t[e]>r&&(r=t[e]);return r}export function clamp01(t){return Math.max(0,Math.min(1,t))}export function mean(t){if(t.length===0)return NaN;let r=0;for(const e of t)r+=e;return r/t.length}export function medianOfSorted(t){const r=t.length;if(r===0)return 0;const e=Math.floor(r/2);return r%2===1?t[e]:(t[e-1]+t[e])/2}export function median(t){return medianOfSorted([...t].sort((r,e)=>r-e))}export function percentile98(t){if(t.length===0)return 0;const r=Float64Array.from(t);r.sort();const e=Math.floor(r.length*.98);return r[Math.min(e,r.length-1)]}export function modeOf(t){if(!t.length)return null;const r=new Map;for(const o of t)r.set(o,(r.get(o)??0)+1);let e=0,n=t[0];for(const[o,f]of r)f>e&&(e=f,n=o);return n}const i=/^[=+\-@]/;export function csvField(t){const r=i.test(t)&&Number.isNaN(Number(t))?`'${t}`:t;return/[",\n\r]/.test(r)?`"${r.replace(/"/g,'""')}"`:r}export function escHtml(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}export function arrayEqual(t,r){if(t===r)return!0;if(!t||!r||t.length!==r.length)return!1;for(let e=0;e<t.length;e++)if(t[e]!==r[e])return!1;return!0}export function mapEqual(t,r){if(t===r)return!0;if(!t||!r||t.size!==r.size)return!1;for(const[e,n]of t)if(!r.has(e)||r.get(e)!==n)return!1;return!0}export function setEqual(t,r){if(t===r)return!0;if(!t||!r||t.size!==r.size)return!1;for(const e of t)if(!r.has(e))return!1;return!0}export function drain(t){let r=t.next();for(;!r.done;)r=t.next();return r.value}export function asList(t){return t===void 0?[]:Array.isArray(t)?t:[t]}export function toggleHighlight(t,r,e){const n=asList(t),o=e?n.includes(r)?n.filter(f=>f!==r):[...n,r]:n.length===1&&n[0]===r?[]:[r];return o.length===0?void 0:o.length===1?o[0]:o}
|
|
@@ -308,9 +308,9 @@ export interface ViewOptions {
|
|
|
308
308
|
* bin present here: a stale assignment must never leave a real bin uncoloured.
|
|
309
309
|
*/
|
|
310
310
|
binColors?: BinColors;
|
|
311
|
-
highlightBin?: number;
|
|
312
|
-
/** Dim every die except
|
|
313
|
-
highlightMetadataValue?: string;
|
|
311
|
+
highlightBin?: number | number[];
|
|
312
|
+
/** Dim every die except these metadata values, `'metadata'` mode's analogue of `highlightBin`. */
|
|
313
|
+
highlightMetadataValue?: string | string[];
|
|
314
314
|
interactiveTransform?: {
|
|
315
315
|
rotation?: number;
|
|
316
316
|
flipX?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hasPosition as fe}from"../core/dies.js";import{getReticleCell as de}from"../core/reticle.js";import{metadataCategoricalValue as he,metadataDisplayValue as me}from"../core/metadata.js";import{affineRotation as bt,affineMirror as Ht,affineCompose as st,affinePoint as G,affineVector as xt,affineSwapsAxes as pe}from"../core/transforms.js";import{contrastTextColor as Q,SPEC_PASS_FILL as Mt,SPEC_FAIL_LOW as ye,SPEC_FAIL_HIGH as St}from"./colorMap.js";import{resolveValueColorFn as ge}from"./colorSchemes.js";import{resolveBinColors as be,binColorsCover as xe}from"./binColors.js";import{NO_DATA_FILL as w}from"./colorMap.js";import{diePassStatus as Me}from"../core/dies.js";import{getDieTestValue as L,getTestPassStatus as U,isParametricTest as vt}from"./buildWaferMap.js";import{classifySpec as ot}from"./spec.js";export{ot as classifySpec};import{fmt as kt,fmtColorbarAxis as $t,fmtAggregationMethod as _t}from"./fmt.js";import{metadataValueColor as Se}from"./colorMap.js";import{clamp01 as Kt,compareNatural as ve,escHtml as P,minOf as ke}from"../core/utils.js";import{prettyKey as Wt}from"../core/utils.js";import{testLabel as rt,markedTestLabel as lt,derivedTestNote as jt,isDerivedTest as $e,DERIVED_MARK as Be,DERIVED_KEY as Te}from"./testLabel.js";export function resolveTestNumber(t,e){return e?.length?{testNumber:(findTestDef(e,t)??e[0]).testNumber}:{testNumber:t}}export function collectMetadataValues(t,e){const i=new Set;for(const a of t){if(a.partial||a.edgeExcluded)continue;const s=ct(a,e);s!==void 0&&i.add(s)}return[...i].sort(ve)}export function findTestDef(t,e){return t?.find(i=>i.testNumber===e)}export function getUniqueTestNumbers(t){return[...new Set(t.flatMap(e=>e.testValues?Object.keys(e.testValues).map(Number):[]))].sort((e,i)=>e-i)}const Ie="#d3d6db",zt="#e8e9ea",De="#aab0ba";function ct(t,e){return e?he(t.metadata?.[e]):void 0}function Ve(t,e,i){const{x:a,y:s}=t.center,n=bt(t.orientation,a,s),o=st(Ht(e?.x??!1,e?.y??!1,a,s),bt(t.orientation,a,s)),l=st(Ht(i?.flipX??!1,i?.flipY??!1,a,s),bt(i?.rotation??0,a,s));return{bakedToScreen:l,physicalToScreen:st(l,n),gridToScreen:st(l,o)}}function q(t,e=!1){return{points:[t],closed:e}}function Ce(t,e){const{center:i,radius:a,notch:s}=t;let n=i.x+a*Math.cos(e),o=i.y+a*Math.sin(e);if(!s)return{x:n,y:o};const l=Math.sqrt(a**2-(s.length/2)**2),d=s.length/2,g=n-i.x,p=o-i.y;return s.type==="bottom"&&p<-l?(o=i.y-l,n=i.x+Math.max(-d,Math.min(d,g))):s.type==="top"&&p>l?(o=i.y+l,n=i.x+Math.max(-d,Math.min(d,g))):s.type==="left"&&g<-l?(n=i.x-l,o=i.y+Math.max(-d,Math.min(d,p))):s.type==="right"&&g>l&&(n=i.x+l,o=i.y+Math.max(-d,Math.min(d,p))),{x:n,y:o}}function wt(t,e){return t.map(e).join(" / ")}function Fe(t,e=256){const i=Array.from({length:e},(a,s)=>t(s/(e-1)));return a=>i[Math.min(e-1,Math.max(0,Math.round(a*(e-1))))]}function Le(t,e){const i=Math.max(1,Math.min(t.width,t.height)),a=t.width/Math.max(e.length,1);return Math.max(8,Math.min(16,Math.round(Math.min(i*.55,a*1.8))))}function Pe(t,e,i){if(e?.length){const a=e.flatMap(s=>{const n=s.testNumber;if(!vt(s)){const l=U(t,n,s);return l===void 0?[]:[{key:n,label:lt(s,n),value:l?"Pass":"Fail"}]}const o=L(t,n);return o===void 0?[]:[{key:n,label:lt(s,n),value:kt(o,s.unit,i),recordedFail:t.testPass?.[n]===!1}]});if(a.length)return a}return t.testValues&&Object.keys(t.testValues).length>0?Object.entries(t.testValues).map(([a,s])=>({key:Number(a),label:rt(void 0,Number(a)),value:kt(s,void 0,i)})):t.testPass&&Object.keys(t.testPass).length>0?Object.entries(t.testPass).map(([a,s])=>({key:Number(a),label:rt(void 0,Number(a)),value:s?"Pass":"Fail"})):[]}export function buildHoverText(t,e,i={}){const{testDefs:a,hbinDefs:s,sbinDefs:n,fallbackFormat:o,aggrMethod:l,lotSize:d,activeTest:g,reticleConfig:p,metadataFields:B}=i,F=s?new Map(s.map(f=>[f.bin,f])):null,T=n?new Map(n.map(f=>[f.bin,f])):null,b=[P(`Die (${t.x}, ${t.y})`)];if(p&&fe(t)){const f=de(t,p);b.push(P(`Reticle (${f.column}, ${f.row})`))}if(e==="stackedValues"){const f=L(t,0);if(f!==void 0){const u=a?.[0],h=u?lt(u,u.testNumber):"Value",v=l?` (${l})`:"";b.push(P(`${h}${v}: ${kt(f,u?.unit,o)}`));const m=jt(u);m&&b.push(`<i>${P(m)}</i>`)}}else if(e==="stackedBins"||e==="stackedSoftBins"){const f=L(t,0),u=e==="stackedSoftBins"?t.sbin:t.hbin;if(f!==void 0){const h=e==="stackedSoftBins"?T:F,v=u!==void 0?h?.get(u)?.name?`${u} \xB7 ${h.get(u).name}`:`Bin ${u}`:"Bin";let m;if(l==="percent")m=`${f.toFixed(0)}%`;else{const V=d?` (${(f/d*100).toFixed(0)}% of stacked wafers)`:"";m=`${f}${V}`}const x=l?` [${_t(l)}]`:"";b.push(P(`${v}: ${m}${x}`))}}else{const f=Pe(t,a,o);if(e==="value"&&f.length){const{testNumber:u}=resolveTestNumber(g??0,a),h=findTestDef(a,u),v=L(t,u);let m=f.findIndex(A=>A.key===u);m<0&&(m=0);const x=f[m];let V=`<b>${P(`${x.label}: ${x.value}`)}${x.recordedFail?" <i>(recorded fail)</i>":""}</b>`;if(x.key===u&&h){const A=ot(v,h);(A==="failLow"||A==="failHigh")&&(V+=" <i>(outside test limits)</i>")}b.push(V);const E=jt(findTestDef(a,x.key));E&&b.push(`<i>${P(E)}</i>`);const y=f.length-1;y>0&&b.push(`<i>+${y} more test${y===1?"":"s"}</i>`)}else if(f.length){const u=f.length;b.push(`<i>${u} test result${u===1?"":"s"} recorded</i>`)}if(t.hbin!==void 0||t.sbin!==void 0){const u=[];if(t.hbin!==void 0){const h=F?.get(t.hbin)?.name,v=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);u.push(`HBin: ${v}`)}if(t.sbin!==void 0){const h=T?.get(t.sbin)?.name,v=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);u.push(`SBin: ${v}`)}b.push(u.map(P).join(" \xB7 "))}}t.retestCount!==void 0&&b.push(P(`Retests: ${t.retestCount}`)),t.siteNum!==void 0&&b.push(P(`Site: ${t.siteNum}`)),t.partId!==void 0&&b.push(P(`Part ID: ${t.partId}`)),t.partial&&b.push("<i>partial die</i>"),t.probeIndex!==void 0&&b.push(P(`Probe: #${t.probeIndex}`));const $={...t.metadata??{}};for(const[f,u]of Object.entries($)){const h=me(u);if(h===void 0)continue;const v=B?.find(m=>m.key===f)?.label??Wt(f);b.push(P(`${v}: ${h}`))}return b.join("<br>")}function Bt(t){return t?.name?lt(t,t.testNumber):void 0}function Tt(t){return $e(t)?{note:`${Be} ${Te}`}:{}}export function buildMapTitle(t,e="engineering",i){const a=t.isLotStack&&t.lotSize!==void 0?`stacked (${t.lotSize} ${t.lotSize===1?"wafer":"wafers"})`:"stacked",s=n=>{const o=i?.length===1?i[0]:void 0;return o===void 0?`${n} Bin`:o.name?`${n} Bin ${o.bin} \xB7 ${o.name}`:`${n} 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:a};case"stackedSoftBins":return{primary:s("Soft"),secondary:a};case"metadata":{const n=t.metadataFields?.find(l=>l.key===t.activeMetadataKey),o=t.activeMetadataKey?Wt(t.activeMetadataKey):"Metadata";return{primary:n?.label??o,secondary:""}}case"stackedValues":{const n=t.testDefs?.[0],o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=$t(o,Bt(n),n?.unit,e),d=l||"Value",g=t.aggrMethod?` \xB7 ${_t(t.aggrMethod)}`:"";return{primary:`${d}${g}`,secondary:a,...Tt(n)}}default:{const n=findTestDef(t.testDefs,t.activeTest),o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=$t(o,Bt(n),n?.unit,e),d=l||Bt(n);if(t.passFailDisplay!=="off"){const g=n?.testNumber??t.activeTest,p=d?`${d} \xB7 #${g}`:rt(void 0,g),B=t.passFailDisplay==="spec"?"Limit pass/fail":n!==void 0&&!vt(n)?"Functional pass/fail":"Tester pass/fail";return{primary:p,secondary:B,...Tt(n)}}return{primary:d??rt(void 0,t.activeTest),secondary:"",...Tt(n)}}}}export function generateTextOverlay(t,e,i){const{plotMode:a,colorFns:s,normalize:n,activeTest:o,valueRange:l,testDefs:d,fallbackFormat:g,passFailDisplay:p="off",activeMetadataKey:B,metadataColorMap:F,allIntegerValues:T}=i,b=findTestDef(d,o),{tickFmt:$}=$t(l[1],b?.name,b?.unit,g),f=T?h=>String(Math.round(h)):$,{testNumber:u}=resolveTestNumber(o,d);return t.flatMap((h,v)=>{let m="",x="#111111";if(a==="value"&&p==="test"){const y=U(h,u,b);if(y===void 0)return[];m=y?"P":"F",x=Q(y?Mt:St)}else if(a==="value"){const y=L(h,u);if(y===void 0)return[];m=wt([y],f),x=Q(s.forValue(n(y)))}else if(a==="hardBin"||a==="softBin"){const y=a==="softBin"?h.sbin:h.hbin;if(y===void 0)return[];m=String(y),x=Q(s.forBin(y))}else if(a==="metadata"){const y=ct(h,B);if(y===void 0)return[];m=y,x=Q(F?.get(m)??w)}else{const y=L(h,0);if(y===void 0)return[];m=wt([y],f),x=Q(s.forValue(n(y)))}const V=e?e[v*2]:h.physX,E=e?e[v*2+1]:h.physY;return[{x:V,y:E,text:m,fontSize:Le(h,m),color:x,align:"center"}]})}function qt(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function Re(t,e,i=720){const{center:a,radius:s,notch:n}=t,o=$=>G(e,$.x,$.y);if(!(n!==void 0&&t.diameter>150)){const $=[];for(let f=0;f<=i;f++){const u=2*Math.PI*f/i;$.push(o(Ce(t,u)))}return[{kind:"wafer-boundary",...q($,!0),lineColor:"#888888",lineWidth:1}]}const d=1.25,g=n.length,p=qt(n.type),B=Math.atan2(g,s),F=s-d,T=[];let b=!1;for(let $=0;$<=i;$++){const f=2*Math.PI*$/i;let u=f-p;for(;u>Math.PI;)u-=2*Math.PI;for(;u<-Math.PI;)u+=2*Math.PI;if(Math.abs(u)<=B){if(!b){b=!0;const h={x:a.x+s*Math.cos(p-B),y:a.y+s*Math.sin(p-B)},v={x:a.x+F*Math.cos(p),y:a.y+F*Math.sin(p)},m={x:a.x+s*Math.cos(p+B),y:a.y+s*Math.sin(p+B)};T.push(o(h),o(v),o(m))}continue}T.push(o({x:a.x+s*Math.cos(f),y:a.y+s*Math.sin(f)}))}return[{kind:"wafer-boundary",...q(T,!0),lineColor:"#888888",lineWidth:1}]}function Ne(t,e,i,a=360){const s=[],n=Math.max(1,Math.floor(i));for(let o=1;o<n;o++){const l=t.radius*o/n,d=[];for(let g=0;g<=a;g++){const p=2*Math.PI*g/a,B={x:t.center.x+l*Math.cos(p),y:t.center.y+l*Math.sin(p)};d.push(G(e,B.x,B.y))}s.push({kind:"ring-boundary",...q(d,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function Oe(t,e,i,a){const{x:s,y:n}=t.center,o=t.radius,l=(f,u)=>G(e,f,u),d=i-s,g=Math.sqrt(Math.max(0,o*o-d*d)),p=l(i,n-g),B=l(i,n+g),F=a-n,T=Math.sqrt(Math.max(0,o*o-F*F)),b=l(s-T,a),$=l(s+T,a);return[{kind:"quadrant-boundary",...q([p,B]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...q([b,$]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function Ee(t,e){return t.map(i=>{const a=i.width/2,s=i.height/2;return{kind:"reticle",points:[[[i.x-a,i.y-s],[i.x+a,i.y-s],[i.x+a,i.y+s],[i.x-a,i.y+s]].map(([o,l])=>G(e,o,l))],closed:!0,lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5,fill:"rgba(0,0,0,0)"}})}function Ae(t){const e=t.filter(i=>i.probeIndex!==void 0).sort((i,a)=>(i.probeIndex??0)-(a.probeIndex??0));return e.length?[{kind:"probe-path",...q(e.map(i=>({x:i.physX,y:i.physY}))),lineColor:"rgba(220,80,0,0.55)",lineWidth:1}]:[]}function He(t,e,i,a,s,n,o,l,d,g,p,B,F,T,b="off",$,f,u){const h=e.width-o,v=e.height-o,m=n?v:h,x=n?h:v,V=S=>s==="softBin"?S.sbin:S.hbin,E=S=>ct(S,$);if(e.partial){t.push({x:i,y:a,width:m,height:x,fill:Ie,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:i,y:a,width:m,height:x,fill:De,type:"stacked",metadata:e.metadata});return}if(d!==void 0&&(s==="hardBin"||s==="softBin")&&V(e)!==d){t.push({x:i,y:a,width:m,height:x,fill:zt,type:"hardBin",metadata:e.metadata});return}if(u!==void 0&&s==="metadata"&&E(e)!==u){t.push({x:i,y:a,width:m,height:x,fill:zt,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(b==="test"){const j=U(e,p,T),ut=j===void 0?w:j?Mt:St;t.push({x:i,y:a,width:m,height:x,fill:ut,type:"value",metadata:e.metadata});return}const S=L(e,p),N=ot(S,T);let W,J;S===void 0?W=w:b==="spec"?W=N==="failLow"?ye:N==="failHigh"?St:Mt:(W=l.forValue(g(S)),(N==="failLow"||N==="failHigh")&&(J=N)),t.push({x:i,y:a,width:m,height:x,fill:W,type:"value",specMark:J,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const S=V(e),N=S!=null?l.forBin(S):w,W=S!=null&&Me(e,F)===!1?!0:void 0;t.push({x:i,y:a,width:m,height:x,fill:N,type:s,binFail:W,metadata:e.metadata});return}if(s==="metadata"){const S=E(e),N=S!==void 0?f?.get(S)??w:w;t.push({x:i,y:a,width:m,height:x,fill:N,type:"metadata",metadata:e.metadata});return}const y=L(e,0),A=y!==void 0?l.forValue(g(y)):w;t.push({x:i,y:a,width:m,height:x,fill:A,type:"value",metadata:e.metadata})}function _e(t,e,i){const a=t.radius*.15,s=xt(e,a,0),n=xt(e,0,a),o=s.x+n.x>=0?-1:1,l=s.y+n.y>=0?-1:1,d={x:t.center.x+o*t.radius*.9,y:t.center.y+l*t.radius*.9},g={x:d.x+s.x,y:d.y+s.y},p={x:d.x+n.x,y:d.y+n.y};return i.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+n.x*.35,y:p.y+n.y*.35,text:"+Y",fontSize:10,color:"#0044cc",align:"center",role:"indicator"}),[{kind:"xy-indicator",...q([d,g]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...q([d,p]),lineColor:"#0044cc",lineWidth:2}]}export function buildView(t,e,i={},a){const s=i.reticles??[],{plotMode:n="value",showDieLabels:o=!1,showReticle:l=!1,showProbePath:d=!1,ringCount:g=4,showRingBoundaries:p=!1,showQuadrantBoundaries:B=!1,showXYIndicator:F=!1,binColorScheme:T="default",valueColorScheme:b="default",reverseValueScheme:$=!1,useDefinedBinColors:f=!0,binColors:u,passBins:h=[1],highlightBin:v,highlightMetadataValue:m,interactiveTransform:x,valueRange:V,metadataValueOrder:E,testDefs:y,activeTest:A=0,activeMetadataKey:S,fallbackFormat:N="engineering",aggregationMethod:W,lotSize:J,dataAxisFlip:j,isLotStack:ut=!1,logScale:Xt,colorbarRangeMode:Yt="spec",passFailDisplay:Gt}=i,It=Gt??"off",ft=a?.hbinDefs,dt=a?.sbinDefs,Dt=a?.metadataFields,Qt=Dt?.find(c=>c.key===S),Ut={x:(j?.x??!1)!==(x?.flipX??!1),y:(j?.y??!1)!==(x?.flipY??!1)},Jt=ft?new Map(ft.map(c=>[c.bin,c])):null,Zt=dt?new Map(dt.map(c=>[c.bin,c])):null,te=n==="softBin"?Zt:Jt,ee=new Set(h),ht=u&&xe(u,e)?u:be(e,{passBins:h,binColorScheme:T,hbinDefs:ft,sbinDefs:dt,useDefinedBinColors:f}),ne=n==="softBin"?ht.soft:ht.hard,Vt={forValue:Fe(ge(b,$)),forBin:c=>ne.get(c)??w};let{testNumber:H}=resolveTestNumber(A,y);if(!y?.length&&n==="value"&&!e.some(r=>r.testValues&&H in r.testValues)){const r=e.reduce((M,D)=>{if(!D.testValues)return M;const k=Object.keys(D.testValues).map(Number),R=k.length?ke(k):void 0;return R!==void 0&&(M===void 0||R<M)?R:M},void 0);r!==void 0&&(H=r)}let Z;if(Array.isArray(V))Z=V;else if(V){const{testNumber:c}=resolveTestNumber(V.test,y);Z=c===H?V.range:void 0}const I=findTestDef(y,H),Ct=n==="value"&&I!==void 0&&!vt(I),ae=I!==void 0&&(I.limitLow!==void 0||I.limitHigh!==void 0);let K="off";n==="value"&&(Ct?K="test":It==="spec"&&ae?K="spec":It==="test"&&e.some(c=>U(c,H,I)!==void 0)&&(K="test"));const Ft=K==="spec"?"spec":Yt,mt=n==="stackedValues"||n==="stackedBins"||n==="stackedSoftBins";let O,_,tt=!1;if(Z){[O,_]=Z;let c=!0,r=!1;for(const M of e){const D=mt?L(M,0):L(M,H);if(D!==void 0&&(r=!0,!Number.isInteger(D))){c=!1;break}}tt=r&&c}else{const c=!mt&&n==="value"&&Ft==="spec"&&I&&(I.limitLow!==void 0||I.limitHigh!==void 0);let r=1/0,M=-1/0,D=!0;for(const k of e){const R=mt?L(k,0):L(k,H);R!==void 0&&(R<r&&(r=R),R>M&&(M=R),Number.isInteger(R)||(D=!1))}isFinite(r)||(D=!1),tt=D,c?(O=I.limitLow!==void 0?I.limitLow:isFinite(r)?r:0,_=I.limitHigh!==void 0?I.limitHigh:isFinite(M)?M:1):(O=isFinite(r)?r:0,_=isFinite(M)?M:1)}O===_&&(_=O+1);const Lt=Ct?!1:Xt??I?.logScale??!1,Pt=Lt&&O>0&&_>0,ie=Pt;let et;if(Pt){const c=Math.log10(O),r=Math.log10(_)-c;et=M=>M<=0?0:Kt((Math.log10(M)-c)/r)}else et=c=>Kt((c-O)/(_-O));const C=Ve(t,j,x);let Rt=null;if(t.notch){const c=qt(t.notch.type),r=xt(C.physicalToScreen,Math.cos(c)*t.radius,Math.sin(c)*t.radius),M=Math.hypot(r.x,r.y);M>0&&(Rt={x:r.x/M,y:r.y/M})}let X=1/0;for(let c=0;c<e.length;c++){const r=e[c];r.width<X&&(X=r.width),r.height<X&&(X=r.height)}const Nt=1,se=X!==1/0?Math.min(Nt,X*.12):Nt,Ot=[],pt=[],oe=C.bakedToScreen.a===1&&C.bakedToScreen.b===0&&C.bakedToScreen.c===0&&C.bakedToScreen.d===1&&C.bakedToScreen.e===0&&C.bakedToScreen.f===0;let z=null;if(!oe){z=new Float64Array(e.length*2);for(let c=0;c<e.length;c++){const r=G(C.bakedToScreen,e[c].physX,e[c].physY);z[c*2]=r.x,z[c*2+1]=r.y}}const re=pe(C.gridToScreen),le=n==="hardBin"||n==="softBin"||n==="stackedBins"||n==="stackedSoftBins",ce=n==="softBin"||n==="stackedSoftBins";let nt=le?new Map:void 0;const yt=n==="value"&&K==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,gt=n==="value"&&K==="test"?{pass:0,fail:0}:void 0,Et=n==="metadata";let at=Et?new Map:void 0,it=null;if(Et&&S){const c=collectMetadataValues(e,S),r=E?.key===S?E.values:void 0,M=r?[...r,...c.filter(D=>!r.includes(D))]:c;it=new Map(M.map((D,k)=>[D,Qt?.values?.find(R=>R.value===D)?.color??Se(k)]))}for(let c=0;c<e.length;c++){const r=e[c],M=z?z[c*2]:r.physX,D=z?z[c*2+1]:r.physY;if(pt.push({x:M,y:D}),nt&&!r.partial&&!r.edgeExcluded){const k=ce?r.sbin:r.hbin;k!=null&&nt.set(k,(nt.get(k)??0)+1)}if(yt&&!r.partial&&!r.edgeExcluded){const k=ot(L(r,H),I);k&&yt[k]++}if(gt&&!r.partial&&!r.edgeExcluded){const k=U(r,H,I);k!==void 0&>[k?"pass":"fail"]++}if(at&&!r.partial&&!r.edgeExcluded){const k=ct(r,S);k!==void 0&&at.set(k,(at.get(k)??0)+1)}He(Ot,r,M,D,n,re,se,Vt,v,et,H,te,ee,I,K,S,it,m)}const At=o?generateTextOverlay(e,z,{plotMode:n,colorFns:Vt,normalize:et,activeTest:A,valueRange:[O,_],testDefs:y,fallbackFormat:N,passFailDisplay:K,activeMetadataKey:S,metadataColorMap:it,allIntegerValues:tt}):[],Y=Re(t,C.physicalToScreen);p&&Y.push(...Ne(t,C.physicalToScreen,g)),B&&Y.push(...Oe(t,C.physicalToScreen,t.center.x,t.center.y)),l&&Y.push(...Ee(s,C.gridToScreen)),d&&Y.push(...Ae(e)),F&&Y.push(..._e(t,C.gridToScreen,At));const ue=pt.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:Ot,hoverPoints:pt,texts:At,overlays:Y,plotMode:n,binColorScheme:T,valueColorScheme:b,reverseValueScheme:$,binColors:ht,metadata:t.metadata??null,dies:e,valueRange:[O,_],allIntegerValues:tt,testDefs:y,activeTest:A,logScale:ie,logScaleRequested:Lt,aggrMethod:W,lotSize:J,axisFlip:Ut,dataAxisFlip:{x:j?.x??!1,y:j?.y??!1},gridToScreen:C.gridToScreen,rotation:((t.orientation+(x?.rotation??0))%360+360)%360,hasReticle:s.length>0,isLotStack:ut,passFailDisplay:K,colorbarRangeMode:Ft,waferCenter:t.center,waferRadius:t.radius,notchDir:Rt,binCounts:nt,metadataCounts:at,metadataColorMap:it,activeMetadataKey:S,metadataFields:Dt,specCounts:yt,passFailCounts:gt,dieBounds:ue}}export{getDieKey}from"../core/dies.js";
|
|
1
|
+
import{hasPosition as pe}from"../core/dies.js";import{dieValueEntries as ye,dieVerdictEntries as ge,recordedVerdict as be,testsPresent as _t}from"../core/dieTable.js";import{getReticleCell as xe}from"../core/reticle.js";import{metadataCategoricalValue as Me,metadataDisplayValue as Se}from"../core/metadata.js";import{affineRotation as bt,affineMirror as Ot,affineCompose as st,affinePoint as G,affineVector as xt,affineSwapsAxes as ve}from"../core/transforms.js";import{contrastTextColor as Q,SPEC_PASS_FILL as Mt,SPEC_FAIL_LOW as ke,SPEC_FAIL_HIGH as St}from"./colorMap.js";import{resolveValueColorFn as Be}from"./colorSchemes.js";import{resolveBinColors as $e,binColorsCover as Te}from"./binColors.js";import{NO_DATA_FILL as q}from"./colorMap.js";import{diePassStatus as Ie}from"../core/dies.js";import{getDieTestValue as L,getTestPassStatus as U,isParametricTest as vt}from"./buildWaferMap.js";import{classifySpec as ot}from"./spec.js";export{ot as classifySpec};import{fmt as kt,fmtColorbarAxis as Bt,fmtAggregationMethod as Wt}from"./fmt.js";import{metadataValueColor as De}from"./colorMap.js";import{asList as wt,clamp01 as zt,compareNatural as Ce,escHtml as R}from"../core/utils.js";import{prettyKey as Kt}from"../core/utils.js";import{testLabel as rt,markedTestLabel as lt,derivedTestNote as qt,isDerivedTest as Fe,DERIVED_MARK as Ve,DERIVED_KEY as Le}from"./testLabel.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 i of t){if(i.partial||i.edgeExcluded)continue;const s=ct(i,e);s!==void 0&&a.add(s)}return[...a].sort(Ce)}export function findTestDef(t,e){return t?.find(a=>a.testNumber===e)}export function getUniqueTestNumbers(t){return _t(t,"values")}const Re="#d3d6db",Xt="#e8e9ea",Pe="#aab0ba";function ct(t,e){return e?Me(t.metadata?.[e]):void 0}function Ne(t,e,a){const{x:i,y:s}=t.center,n=bt(t.orientation,i,s),o=st(Ot(e?.x??!1,e?.y??!1,i,s),bt(t.orientation,i,s)),r=st(Ot(a?.flipX??!1,a?.flipY??!1,i,s),bt(a?.rotation??0,i,s));return{bakedToScreen:r,physicalToScreen:st(r,n),gridToScreen:st(r,o)}}function X(t,e=!1){return{points:[t],closed:e}}function Ee(t,e){const{center:a,radius:i,notch:s}=t;let n=a.x+i*Math.cos(e),o=a.y+i*Math.sin(e);if(!s)return{x:n,y:o};const r=Math.sqrt(i**2-(s.length/2)**2),f=s.length/2,p=n-a.x,y=o-a.y;return s.type==="bottom"&&y<-r?(o=a.y-r,n=a.x+Math.max(-f,Math.min(f,p))):s.type==="top"&&y>r?(o=a.y+r,n=a.x+Math.max(-f,Math.min(f,p))):s.type==="left"&&p<-r?(n=a.x-r,o=a.y+Math.max(-f,Math.min(f,y))):s.type==="right"&&p>r&&(n=a.x+r,o=a.y+Math.max(-f,Math.min(f,y))),{x:n,y:o}}function Yt(t,e){return t.map(e).join(" / ")}function Ae(t,e=256){const a=Array.from({length:e},(i,s)=>t(s/(e-1)));return i=>a[Math.min(e-1,Math.max(0,Math.round(i*(e-1))))]}function He(t,e){const a=Math.max(1,Math.min(t.width,t.height)),i=t.width/Math.max(e.length,1);return Math.max(8,Math.min(16,Math.round(Math.min(a*.55,i*1.8))))}function _e(t,e,a){if(e?.length){const n=e.flatMap(o=>{const r=o.testNumber;if(!vt(o)){const p=U(t,r,o);return p===void 0?[]:[{key:r,label:lt(o,r),value:p?"Pass":"Fail"}]}const f=L(t,r);return f===void 0?[]:[{key:r,label:lt(o,r),value:kt(f,o.unit,a),recordedFail:be(t,r)===!1}]});if(n.length)return n}const i=ye(t);if(i.length)return i.map(([n,o])=>({key:n,label:rt(void 0,n),value:kt(o,void 0,a)}));const s=ge(t);return s.length?s.map(([n,o])=>({key:n,label:rt(void 0,n),value:o?"Pass":"Fail"})):[]}export function buildHoverText(t,e,a={}){const{testDefs:i,hbinDefs:s,sbinDefs:n,fallbackFormat:o,aggrMethod:r,lotSize:f,activeTest:p,reticleConfig:y,metadataFields:B}=a,F=s?new Map(s.map(d=>[d.bin,d])):null,T=n?new Map(n.map(d=>[d.bin,d])):null,b=[R(`Die (${t.x}, ${t.y})`)];if(y&&pe(t)){const d=xe(t,y);b.push(R(`Reticle (${d.column}, ${d.row})`))}if(e==="stackedValues"){const d=L(t,0);if(d!==void 0){const u=i?.[0],h=u?lt(u,u.testNumber):"Value",S=r?` (${r})`:"";b.push(R(`${h}${S}: ${kt(d,u?.unit,o)}`));const m=qt(u);m&&b.push(`<i>${R(m)}</i>`)}}else if(e==="stackedBins"||e==="stackedSoftBins"){const d=L(t,0),u=e==="stackedSoftBins"?t.sbin:t.hbin;if(d!==void 0){const h=e==="stackedSoftBins"?T:F,S=u!==void 0?h?.get(u)?.name?`${u} \xB7 ${h.get(u).name}`:`Bin ${u}`:"Bin";let m;if(r==="percent")m=`${d.toFixed(0)}%`;else{const D=f?` (${(d/f*100).toFixed(0)}% of stacked wafers)`:"";m=`${d}${D}`}const x=r?` [${Wt(r)}]`:"";b.push(R(`${S}: ${m}${x}`))}}else{const d=_e(t,i,o);if(e==="value"&&d.length){const{testNumber:u}=resolveTestNumber(p??0,i),h=findTestDef(i,u),S=L(t,u);let m=d.findIndex(A=>A.key===u);m<0&&(m=0);const x=d[m];let D=`<b>${R(`${x.label}: ${x.value}`)}${x.recordedFail?" <i>(recorded fail)</i>":""}</b>`;if(x.key===u&&h){const A=ot(S,h);(A==="failLow"||A==="failHigh")&&(D+=" <i>(outside test limits)</i>")}b.push(D);const E=qt(findTestDef(i,x.key));E&&b.push(`<i>${R(E)}</i>`);const g=d.length-1;g>0&&b.push(`<i>+${g} more test${g===1?"":"s"}</i>`)}else if(d.length){const u=d.length;b.push(`<i>${u} test result${u===1?"":"s"} recorded</i>`)}if(t.hbin!==void 0||t.sbin!==void 0){const u=[];if(t.hbin!==void 0){const h=F?.get(t.hbin)?.name,S=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);u.push(`HBin: ${S}`)}if(t.sbin!==void 0){const h=T?.get(t.sbin)?.name,S=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);u.push(`SBin: ${S}`)}b.push(u.map(R).join(" \xB7 "))}}t.retestCount!==void 0&&b.push(R(`Retests: ${t.retestCount}`)),t.siteNum!==void 0&&b.push(R(`Site: ${t.siteNum}`)),t.partId!==void 0&&b.push(R(`Part ID: ${t.partId}`)),t.partial&&b.push("<i>partial die</i>"),t.probeIndex!==void 0&&b.push(R(`Probe: #${t.probeIndex}`));const k={...t.metadata??{}};for(const[d,u]of Object.entries(k)){const h=Se(u);if(h===void 0)continue;const S=B?.find(m=>m.key===d)?.label??Kt(d);b.push(R(`${S}: ${h}`))}return b.join("<br>")}function $t(t){return t?.name?lt(t,t.testNumber):void 0}function Tt(t){return Fe(t)?{note:`${Ve} ${Le}`}:{}}export function buildMapTitle(t,e="engineering",a){const i=t.isLotStack&&t.lotSize!==void 0?`stacked (${t.lotSize} ${t.lotSize===1?"wafer":"wafers"})`:"stacked",s=n=>{const o=a?.length===1?a[0]:void 0;return o===void 0?`${n} Bin`:o.name?`${n} Bin ${o.bin} \xB7 ${o.name}`:`${n} 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:i};case"stackedSoftBins":return{primary:s("Soft"),secondary:i};case"metadata":{const n=t.metadataFields?.find(r=>r.key===t.activeMetadataKey),o=t.activeMetadataKey?Kt(t.activeMetadataKey):"Metadata";return{primary:n?.label??o,secondary:""}}case"stackedValues":{const n=t.testDefs?.[0],o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:r}=Bt(o,$t(n),n?.unit,e),f=r||"Value",p=t.aggrMethod?` \xB7 ${Wt(t.aggrMethod)}`:"";return{primary:`${f}${p}`,secondary:i,...Tt(n)}}default:{const n=findTestDef(t.testDefs,t.activeTest),o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:r}=Bt(o,$t(n),n?.unit,e),f=r||$t(n);if(t.passFailDisplay!=="off"){const p=n?.testNumber??t.activeTest,y=f?`${f} \xB7 #${p}`:rt(void 0,p),B=t.passFailDisplay==="spec"?"Limit pass/fail":n!==void 0&&!vt(n)?"Functional pass/fail":"Tester pass/fail";return{primary:y,secondary:B,...Tt(n)}}return{primary:f??rt(void 0,t.activeTest),secondary:"",...Tt(n)}}}}export function generateTextOverlay(t,e,a){const{plotMode:i,colorFns:s,normalize:n,activeTest:o,valueRange:r,testDefs:f,fallbackFormat:p,passFailDisplay:y="off",activeMetadataKey:B,metadataColorMap:F,allIntegerValues:T}=a,b=findTestDef(f,o),{tickFmt:k}=Bt(r[1],b?.name,b?.unit,p),d=T?h=>String(Math.round(h)):k,{testNumber:u}=resolveTestNumber(o,f);return t.flatMap((h,S)=>{let m="",x="#111111";if(i==="value"&&y==="test"){const g=U(h,u,b);if(g===void 0)return[];m=g?"P":"F",x=Q(g?Mt:St)}else if(i==="value"){const g=L(h,u);if(g===void 0)return[];m=Yt([g],d),x=Q(s.forValue(n(g)))}else if(i==="hardBin"||i==="softBin"){const g=i==="softBin"?h.sbin:h.hbin;if(g===void 0)return[];m=String(g),x=Q(s.forBin(g))}else if(i==="metadata"){const g=ct(h,B);if(g===void 0)return[];m=g,x=Q(F?.get(m)??q)}else{const g=L(h,0);if(g===void 0)return[];m=Yt([g],d),x=Q(s.forValue(n(g)))}const D=e?e[S*2]:h.physX,E=e?e[S*2+1]:h.physY;return[{x:D,y:E,text:m,fontSize:He(h,m),color:x,align:"center"}]})}function jt(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function Oe(t,e,a=720){const{center:i,radius:s,notch:n}=t,o=k=>G(e,k.x,k.y);if(!(n!==void 0&&t.diameter>150)){const k=[];for(let d=0;d<=a;d++){const u=2*Math.PI*d/a;k.push(o(Ee(t,u)))}return[{kind:"wafer-boundary",...X(k,!0),lineColor:"#888888",lineWidth:1}]}const f=1.25,p=n.length,y=jt(n.type),B=Math.atan2(p,s),F=s-f,T=[];let b=!1;for(let k=0;k<=a;k++){const d=2*Math.PI*k/a;let u=d-y;for(;u>Math.PI;)u-=2*Math.PI;for(;u<-Math.PI;)u+=2*Math.PI;if(Math.abs(u)<=B){if(!b){b=!0;const h={x:i.x+s*Math.cos(y-B),y:i.y+s*Math.sin(y-B)},S={x:i.x+F*Math.cos(y),y:i.y+F*Math.sin(y)},m={x:i.x+s*Math.cos(y+B),y:i.y+s*Math.sin(y+B)};T.push(o(h),o(S),o(m))}continue}T.push(o({x:i.x+s*Math.cos(d),y:i.y+s*Math.sin(d)}))}return[{kind:"wafer-boundary",...X(T,!0),lineColor:"#888888",lineWidth:1}]}function We(t,e,a,i=360){const s=[],n=Math.max(1,Math.floor(a));for(let o=1;o<n;o++){const r=t.radius*o/n,f=[];for(let p=0;p<=i;p++){const y=2*Math.PI*p/i,B={x:t.center.x+r*Math.cos(y),y:t.center.y+r*Math.sin(y)};f.push(G(e,B.x,B.y))}s.push({kind:"ring-boundary",...X(f,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function we(t,e,a,i){const{x:s,y:n}=t.center,o=t.radius,r=(d,u)=>G(e,d,u),f=a-s,p=Math.sqrt(Math.max(0,o*o-f*f)),y=r(a,n-p),B=r(a,n+p),F=i-n,T=Math.sqrt(Math.max(0,o*o-F*F)),b=r(s-T,i),k=r(s+T,i);return[{kind:"quadrant-boundary",...X([y,B]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...X([b,k]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function ze(t,e){return t.map(a=>{const i=a.width/2,s=a.height/2;return{kind:"reticle",points:[[[a.x-i,a.y-s],[a.x+i,a.y-s],[a.x+i,a.y+s],[a.x-i,a.y+s]].map(([o,r])=>G(e,o,r))],closed:!0,lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5,fill:"rgba(0,0,0,0)"}})}function Ke(t){const e=t.filter(a=>a.probeIndex!==void 0).sort((a,i)=>(a.probeIndex??0)-(i.probeIndex??0));return e.length?[{kind:"probe-path",...X(e.map(a=>({x:a.physX,y:a.physY}))),lineColor:"rgba(220,80,0,0.55)",lineWidth:1}]:[]}function qe(t,e,a,i,s,n,o,r,f,p,y,B,F,T,b="off",k,d,u){const h=e.width-o,S=e.height-o,m=n?S:h,x=n?h:S,D=M=>s==="softBin"?M.sbin:M.hbin,E=M=>ct(M,k);if(e.partial){t.push({x:a,y:i,width:m,height:x,fill:Re,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:i,width:m,height:x,fill:Pe,type:"stacked",metadata:e.metadata});return}if(f!==void 0&&(s==="hardBin"||s==="softBin")&&!f.has(D(e))){t.push({x:a,y:i,width:m,height:x,fill:Xt,type:"hardBin",metadata:e.metadata});return}if(u!==void 0&&s==="metadata"&&!u.has(E(e))){t.push({x:a,y:i,width:m,height:x,fill:Xt,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(b==="test"){const w=U(e,y,T),ut=w===void 0?q:w?Mt:St;t.push({x:a,y:i,width:m,height:x,fill:ut,type:"value",metadata:e.metadata});return}const M=L(e,y),P=ot(M,T);let W,J;M===void 0?W=q:b==="spec"?W=P==="failLow"?ke:P==="failHigh"?St:Mt:(W=r.forValue(p(M)),(P==="failLow"||P==="failHigh")&&(J=P)),t.push({x:a,y:i,width:m,height:x,fill:W,type:"value",specMark:J,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const M=D(e),P=M!=null?r.forBin(M):q,W=M!=null&&Ie(e,F)===!1?!0:void 0;t.push({x:a,y:i,width:m,height:x,fill:P,type:s,binFail:W,metadata:e.metadata});return}if(s==="metadata"){const M=E(e),P=M!==void 0?d?.get(M)??q:q;t.push({x:a,y:i,width:m,height:x,fill:P,type:"metadata",metadata:e.metadata});return}const g=L(e,0),A=g!==void 0?r.forValue(p(g)):q;t.push({x:a,y:i,width:m,height:x,fill:A,type:"value",metadata:e.metadata})}function Xe(t,e,a){const i=t.radius*.15,s=xt(e,i,0),n=xt(e,0,i),o=s.x+n.x>=0?-1:1,r=s.y+n.y>=0?-1:1,f={x:t.center.x+o*t.radius*.9,y:t.center.y+r*t.radius*.9},p={x:f.x+s.x,y:f.y+s.y},y={x:f.x+n.x,y:f.y+n.y};return a.push({x:p.x+s.x*.35,y:p.y+s.y*.35,text:"+X",fontSize:10,color:"#cc3300",align:"center",role:"indicator"},{x:y.x+n.x*.35,y:y.y+n.y*.35,text:"+Y",fontSize:10,color:"#0044cc",align:"center",role:"indicator"}),[{kind:"xy-indicator",...X([f,p]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...X([f,y]),lineColor:"#0044cc",lineWidth:2}]}export function buildView(t,e,a={},i){const s=a.reticles??[],{plotMode:n="value",showDieLabels:o=!1,showReticle:r=!1,showProbePath:f=!1,ringCount:p=4,showRingBoundaries:y=!1,showQuadrantBoundaries:B=!1,showXYIndicator:F=!1,binColorScheme:T="default",valueColorScheme:b="default",reverseValueScheme:k=!1,useDefinedBinColors:d=!0,binColors:u,passBins:h=[1],highlightBin:S,highlightMetadataValue:m,interactiveTransform:x,valueRange:D,metadataValueOrder:E,testDefs:g,activeTest:A=0,activeMetadataKey:M,fallbackFormat:P="engineering",aggregationMethod:W,lotSize:J,dataAxisFlip:w,isLotStack:ut=!1,logScale:Gt,colorbarRangeMode:Qt="spec",passFailDisplay:Ut}=a,It=Ut??"off",ft=i?.hbinDefs,dt=i?.sbinDefs,Dt=i?.metadataFields,Jt=Dt?.find(l=>l.key===M),Zt={x:(w?.x??!1)!==(x?.flipX??!1),y:(w?.y??!1)!==(x?.flipY??!1)},te=ft?new Map(ft.map(l=>[l.bin,l])):null,ee=dt?new Map(dt.map(l=>[l.bin,l])):null,ne=n==="softBin"?ee:te,ae=new Set(h),ht=u&&Te(u,e)?u:$e(e,{passBins:h,binColorScheme:T,hbinDefs:ft,sbinDefs:dt,useDefinedBinColors:d}),ie=n==="softBin"?ht.soft:ht.hard,Ct={forValue:Ae(Be(b,k)),forBin:l=>ie.get(l)??q};let{testNumber:H}=resolveTestNumber(A,g);if(!g?.length&&n==="value"){const l=_t(e,"values");!l.includes(H)&&l.length&&(H=l[0])}let Z;if(Array.isArray(D))Z=D;else if(D){const{testNumber:l}=resolveTestNumber(D.test,g);Z=l===H?D.range:void 0}const I=findTestDef(g,H),Ft=n==="value"&&I!==void 0&&!vt(I),se=I!==void 0&&(I.limitLow!==void 0||I.limitHigh!==void 0);let O="off";n==="value"&&(Ft?O="test":It==="spec"&&se?O="spec":It==="test"&&e.some(l=>U(l,H,I)!==void 0)&&(O="test"));const Vt=O==="spec"?"spec":Qt,mt=n==="stackedValues"||n==="stackedBins"||n==="stackedSoftBins";let N,_,tt=!1;if(Z){[N,_]=Z;let l=!0,c=!1;for(const v of e){const V=mt?L(v,0):L(v,H);if(V!==void 0&&(c=!0,!Number.isInteger(V))){l=!1;break}}tt=c&&l}else{const l=!mt&&n==="value"&&Vt==="spec"&&I&&(I.limitLow!==void 0||I.limitHigh!==void 0);let c=1/0,v=-1/0,V=!0;for(const $ of e){const K=mt?L($,0):L($,H);K!==void 0&&(K<c&&(c=K),K>v&&(v=K),Number.isInteger(K)||(V=!1))}isFinite(c)||(V=!1),tt=V,l?(N=I.limitLow!==void 0?I.limitLow:isFinite(c)?c:0,_=I.limitHigh!==void 0?I.limitHigh:isFinite(v)?v:1):(N=isFinite(c)?c:0,_=isFinite(v)?v:1)}N===_&&(_=N+1);const Lt=Ft?!1:Gt??I?.logScale??!1,Rt=Lt&&N>0&&_>0,oe=Rt;let et;if(Rt){const l=Math.log10(N),c=Math.log10(_)-l;et=v=>v<=0?0:zt((Math.log10(v)-l)/c)}else et=l=>zt((l-N)/(_-N));const C=Ne(t,w,x);let Pt=null;if(t.notch){const l=jt(t.notch.type),c=xt(C.physicalToScreen,Math.cos(l)*t.radius,Math.sin(l)*t.radius),v=Math.hypot(c.x,c.y);v>0&&(Pt={x:c.x/v,y:c.y/v})}let Y=1/0;for(let l=0;l<e.length;l++){const c=e[l];c.width<Y&&(Y=c.width),c.height<Y&&(Y=c.height)}const Nt=1,re=Y!==1/0?Math.min(Nt,Y*.12):Nt,Et=[],pt=[],le=C.bakedToScreen.a===1&&C.bakedToScreen.b===0&&C.bakedToScreen.c===0&&C.bakedToScreen.d===1&&C.bakedToScreen.e===0&&C.bakedToScreen.f===0;let z=null;if(!le){z=new Float64Array(e.length*2);for(let l=0;l<e.length;l++){const c=G(C.bakedToScreen,e[l].physX,e[l].physY);z[l*2]=c.x,z[l*2+1]=c.y}}const ce=ve(C.gridToScreen),ue=n==="hardBin"||n==="softBin"||n==="stackedBins"||n==="stackedSoftBins",fe=n==="softBin"||n==="stackedSoftBins";let nt=ue?new Map:void 0;const yt=n==="value"&&O==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,gt=n==="value"&&O==="test"?{pass:0,fail:0}:void 0,At=n==="metadata";let at=At?new Map:void 0,it=null;if(At&&M){const l=collectMetadataValues(e,M),c=E?.key===M?E.values:void 0,v=c?[...c,...l.filter(V=>!c.includes(V))]:l;it=new Map(v.map((V,$)=>[V,Jt?.values?.find(K=>K.value===V)?.color??De($)]))}const de=S===void 0?void 0:new Set(wt(S)),he=m===void 0?void 0:new Set(wt(m));for(let l=0;l<e.length;l++){const c=e[l],v=z?z[l*2]:c.physX,V=z?z[l*2+1]:c.physY;if(pt.push({x:v,y:V}),nt&&!c.partial&&!c.edgeExcluded){const $=fe?c.sbin:c.hbin;$!=null&&nt.set($,(nt.get($)??0)+1)}if(yt&&!c.partial&&!c.edgeExcluded){const $=ot(L(c,H),I);$&&yt[$]++}if(gt&&!c.partial&&!c.edgeExcluded){const $=U(c,H,I);$!==void 0&>[$?"pass":"fail"]++}if(at&&!c.partial&&!c.edgeExcluded){const $=ct(c,M);$!==void 0&&at.set($,(at.get($)??0)+1)}qe(Et,c,v,V,n,ce,re,Ct,de,et,H,ne,ae,I,O,M,it,he)}const Ht=o?generateTextOverlay(e,z,{plotMode:n,colorFns:Ct,normalize:et,activeTest:A,valueRange:[N,_],testDefs:g,fallbackFormat:P,passFailDisplay:O,activeMetadataKey:M,metadataColorMap:it,allIntegerValues:tt}):[],j=Oe(t,C.physicalToScreen);y&&j.push(...We(t,C.physicalToScreen,p)),B&&j.push(...we(t,C.physicalToScreen,t.center.x,t.center.y)),r&&j.push(...ze(s,C.gridToScreen)),f&&j.push(...Ke(e)),F&&j.push(...Xe(t,C.gridToScreen,Ht));const me=pt.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:Et,hoverPoints:pt,texts:Ht,overlays:j,plotMode:n,binColorScheme:T,valueColorScheme:b,reverseValueScheme:k,binColors:ht,metadata:t.metadata??null,dies:e,valueRange:[N,_],allIntegerValues:tt,testDefs:g,activeTest:A,logScale:oe,logScaleRequested:Lt,aggrMethod:W,lotSize:J,axisFlip:Zt,dataAxisFlip:{x:w?.x??!1,y:w?.y??!1},gridToScreen:C.gridToScreen,rotation:((t.orientation+(x?.rotation??0))%360+360)%360,hasReticle:s.length>0,isLotStack:ut,passFailDisplay:O,colorbarRangeMode:Vt,waferCenter:t.center,waferRadius:t.radius,notchDir:Pt,binCounts:nt,metadataCounts:at,metadataColorMap:it,activeMetadataKey:M,metadataFields:Dt,specCounts:yt,passFailCounts:gt,dieBounds:me}}export{getDieKey}from"../core/dies.js";
|