@wafertools/wafermap 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +367 -0
- package/README.md +46 -3
- package/dist/packages/canvas-adapter/canvasTheme.d.ts +4 -0
- package/dist/packages/canvas-adapter/canvasTheme.js +1 -1
- package/dist/packages/canvas-adapter/charts/barPanel.d.ts +16 -0
- package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
- package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
- package/dist/packages/canvas-adapter/charts/boxplot.d.ts +13 -1
- package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
- package/dist/packages/canvas-adapter/charts/capability.d.ts +10 -10
- package/dist/packages/canvas-adapter/charts/capability.js +1 -1
- package/dist/packages/canvas-adapter/charts/chartShell.d.ts +337 -3
- package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
- package/dist/packages/canvas-adapter/charts/correlation.d.ts +8 -9
- package/dist/packages/canvas-adapter/charts/correlation.js +2 -1
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +50 -0
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.js +1 -0
- package/dist/packages/canvas-adapter/charts/histogram.d.ts +12 -1
- package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
- package/dist/packages/canvas-adapter/charts/regionYieldDiagram.js +1 -1
- package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
- package/dist/packages/canvas-adapter/charts/testPassRate.d.ts +20 -0
- package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -0
- package/dist/packages/canvas-adapter/charts/trend.d.ts +30 -0
- package/dist/packages/canvas-adapter/charts/trend.js +1 -0
- package/dist/packages/canvas-adapter/dieList.js +7 -7
- package/dist/packages/canvas-adapter/identityHeader.d.ts +46 -0
- package/dist/packages/canvas-adapter/identityHeader.js +1 -0
- package/dist/packages/canvas-adapter/index.d.ts +1 -1
- package/dist/packages/canvas-adapter/insightsTab.d.ts +27 -3
- package/dist/packages/canvas-adapter/insightsTab.js +1 -1
- package/dist/packages/canvas-adapter/maplessSummary.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +15 -2
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -20
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +72 -17
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +219 -33
- package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
- package/dist/packages/canvas-adapter/toCanvas.d.ts +6 -0
- package/dist/packages/canvas-adapter/toCanvas.js +1 -1
- package/dist/packages/canvas-adapter/toolbar.d.ts +310 -6
- 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 +98 -35
- 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.d.ts +0 -4
- package/dist/packages/canvas-adapter/warnings.js +1 -1
- package/dist/packages/core/utils.d.ts +11 -0
- package/dist/packages/core/utils.js +1 -1
- package/dist/packages/renderer/buildView.d.ts +34 -1
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.d.ts +104 -5
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/renderer/colorSchemes.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/binPareto.d.ts +15 -0
- package/dist/packages/stats/binPareto.js +1 -1
- package/dist/packages/stats/clusterDetection.js +1 -1
- package/dist/packages/stats/connectedComponents.d.ts +14 -0
- package/dist/packages/stats/connectedComponents.js +1 -0
- package/dist/packages/stats/correlation.d.ts +26 -0
- package/dist/packages/stats/correlation.js +1 -1
- package/dist/packages/stats/facets.js +1 -1
- package/dist/packages/stats/filterFindings.d.ts +18 -0
- package/dist/packages/stats/filterFindings.js +1 -1
- package/dist/packages/stats/findingsNarrative.js +1 -1
- package/dist/packages/stats/histogram.d.ts +19 -1
- package/dist/packages/stats/histogram.js +1 -1
- package/dist/packages/stats/index.d.ts +6 -0
- package/dist/packages/stats/index.js +1 -1
- package/dist/packages/stats/mergeTestDefs.d.ts +44 -0
- package/dist/packages/stats/mergeTestDefs.js +1 -0
- package/dist/packages/stats/metadataColumns.d.ts +20 -4
- package/dist/packages/stats/metadataColumns.js +1 -1
- package/dist/packages/stats/patternClassification.js +1 -1
- package/dist/packages/stats/renderFindingsReport.js +14 -14
- package/dist/packages/stats/renderSummaryReport.js +32 -29
- package/dist/packages/stats/reportHtml.js +38 -13
- package/dist/packages/stats/testPassRate.d.ts +85 -0
- package/dist/packages/stats/testPassRate.js +1 -0
- package/dist/packages/stats/trend.d.ts +40 -0
- package/dist/packages/stats/trend.js +1 -0
- package/dist/packages/stats/types.d.ts +117 -6
- package/llms.txt +6 -0
- package/package.json +7 -5
- package/dist/packages/canvas-adapter/metadataBadge.d.ts +0 -30
- package/dist/packages/canvas-adapter/metadataBadge.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const USER_GUIDE_HTML=`<div class="wmap-guide">
|
|
2
2
|
<style>
|
|
3
|
-
.wmap-guide{font-family:system-ui,-apple-system,sans-serif;font-size:14px;line-height:1.65;color:var(--wmap-text,#1a1a1a);padding:24px 32px;max-width:var(--wmap-guide-reading-width,720px);margin:0 auto;overflow-y:auto;height:100%;box-sizing:border-box}
|
|
3
|
+
.wmap-guide{font-family:system-ui,-apple-system,sans-serif;font-size:14px;line-height:1.65;color:var(--wmap-text,#1a1a1a);background:var(--wmap-surface,#fff);padding:24px 32px;max-width:var(--wmap-guide-reading-width,720px);margin:0 auto;overflow-y:auto;height:100%;box-sizing:border-box}
|
|
4
4
|
.wmap-guide h1{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-size:1.35em;font-weight:700;margin:0 0 18px;padding-bottom:10px;border-bottom:2px solid var(--wmap-border,#e2e5ea);color:var(--wmap-text,#111)}
|
|
5
5
|
.wmap-guide-version{font-size:11px;font-weight:400;color:var(--wmap-text-muted,#888);white-space:nowrap}
|
|
6
6
|
.wmap-guide h2{font-size:1.1em;font-weight:700;margin:28px 0 10px;padding-bottom:6px;border-bottom:1px solid var(--wmap-border,#e9eaec);color:var(--wmap-text,#1a1a1a)}
|
|
@@ -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-
|
|
75
|
+
<h1 id="wafer-map-user-guide">Wafer Map \u2014 User Guide<span class="wmap-guide-version" title="Built 2026-09-09T07:38:59.875Z">v0.27.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
|
|
@@ -250,7 +250,7 @@ verdicts, as an alternative to spec-limit judgement.</p>
|
|
|
250
250
|
<p>The toolbar sits at the top-right of the map and is always visible. Controls that
|
|
251
251
|
are not applicable to the current mode are hidden automatically. (Hovering the map
|
|
252
252
|
shows die tooltips \u2014 a separate thing from the toolbar, which is always present.)</p>
|
|
253
|
-
<p><img src="data:image/png;base64,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" alt="Single-map toolbar"></p>
|
|
253
|
+
<p><img src="data:image/png;base64,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" alt="Single-map toolbar"></p>
|
|
254
254
|
<h3 id="single-map">Single map</h3>
|
|
255
255
|
<table>
|
|
256
256
|
<thead>
|
|
@@ -333,7 +333,7 @@ shows die tooltips \u2014 a separate thing from the toolbar, which is always pre
|
|
|
333
333
|
<tr>
|
|
334
334
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xNSAzaDZ2NiIvPjxwYXRoIGQ9Ik05IDIxSDN2LTYiLz48cGF0aCBkPSJNMjEgM2wtNyA3Ii8+PHBhdGggZD0iTTMgMjFsNy03Ii8+PC9zdmc+Cg==" width="20" height="20"></td>
|
|
335
335
|
<td>Expand</td>
|
|
336
|
-
<td>Opens the map in an enlarged modal overlay. A maximise button in the modal grows it to fill the window (or press <strong>F</strong>). Press <strong>Esc</strong> or click outside to close. Useful for detailed inspection without changing the main view.
|
|
336
|
+
<td>Opens the map in an enlarged modal overlay. A maximise button in the modal grows it to fill the window (or press <strong>F</strong>). Press <strong>Esc</strong> or click outside to close. Useful for detailed inspection without changing the main view. Works in the Insights view too, where it opens the whole chart suite in a wide modal \u2014 useful because those charts interact and are best read side by side. Individual charts also have their own expand button.</td>
|
|
337
337
|
</tr>
|
|
338
338
|
<tr>
|
|
339
339
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMS44IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik0xMy45OTcgNGEyIDIgMCAwIDEgMS43NiAxLjA1bC40ODYuOUEyIDIgMCAwIDAgMTguMDAzIDdIMjBhMiAyIDAgMCAxIDIgMnY5YTIgMiAwIDAgMS0yIDJINGEyIDIgMCAwIDEtMi0yVjlhMiAyIDAgMCAxIDItMmgxLjk5N2EyIDIgMCAwIDAgMS43NTktMS4wNDhsLjQ4OS0uOTA0QTIgMiAwIDAgMSAxMC4wMDQgNHoiLz48Y2lyY2xlIGN4PSIxMiIgY3k9IjEzIiByPSIzIi8+PC9zdmc+Cg==" width="20" height="20"></td>
|
|
@@ -343,7 +343,7 @@ shows die tooltips \u2014 a separate thing from the toolbar, which is always pre
|
|
|
343
343
|
<tr>
|
|
344
344
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNOCAydjQiLz48cGF0aCBkPSJNMTIgMnY0Ii8+PHBhdGggZD0iTTE2IDJ2NCIvPjxyZWN0IHdpZHRoPSIxNiIgaGVpZ2h0PSIxOCIgeD0iNCIgeT0iNCIgcng9IjIiLz48cGF0aCBkPSJNOCAxMGg2Ii8+PHBhdGggZD0iTTggMTRoOCIvPjxwYXRoIGQ9Ik04IDE4aDUiLz48L3N2Zz4K" width="20" height="20"></td>
|
|
345
345
|
<td>Findings</td>
|
|
346
|
-
<td>Opens or closes the
|
|
346
|
+
<td>Opens or closes the Summary panel (see <a href="#6-summary-panel" onclick="(function(e){e.preventDefault();var g=e.target.closest('.wmap-guide');var el=g&&g.querySelector('[id=\\'6-summary-panel\\']');if(el)el.scrollIntoView({behavior:'smooth'});})(event)">Section 6</a>).</td>
|
|
347
347
|
</tr>
|
|
348
348
|
<tr>
|
|
349
349
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMTAuMjkgMy44NiAxLjgyIDE4YTIgMiAwIDAgMCAxLjcxIDNoMTYuOTRhMiAyIDAgMCAwIDEuNzEtM0wxMy43MSAzLjg2YTIgMiAwIDAgMC0zLjQyIDB6Ii8+PHBhdGggZD0iTTEyIDl2NCIvPjxwYXRoIGQ9Ik0xMiAxN2guMDEiLz48L3N2Zz4K" width="20" height="20"></td>
|
|
@@ -485,7 +485,7 @@ failure category across the wafer.</p>
|
|
|
485
485
|
<tr>
|
|
486
486
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNOCAydjQiLz48cGF0aCBkPSJNMTIgMnY0Ii8+PHBhdGggZD0iTTE2IDJ2NCIvPjxyZWN0IHdpZHRoPSIxNiIgaGVpZ2h0PSIxOCIgeD0iNCIgeT0iNCIgcng9IjIiLz48cGF0aCBkPSJNOCAxMGg2Ii8+PHBhdGggZD0iTTggMTRoOCIvPjxwYXRoIGQ9Ik04IDE4aDUiLz48L3N2Zz4K" width="20" height="20"></td>
|
|
487
487
|
<td>Findings</td>
|
|
488
|
-
<td>Opens or closes the lot-level
|
|
488
|
+
<td>Opens or closes the lot-level Summary panel.</td>
|
|
489
489
|
</tr>
|
|
490
490
|
<tr>
|
|
491
491
|
<td><img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiMzNzQxNTEiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNMyAzdjE4aDE4Ii8+PHBhdGggZD0iTTE4IDE3VjkiLz48cGF0aCBkPSJNMTMgMTdWNSIvPjxwYXRoIGQ9Ik04IDE3di0zIi8+PC9zdmc+Cg==" width="20" height="20"></td>
|
|
@@ -600,26 +600,52 @@ findings (ring, quadrant, sector), the panel groups them together \u2014 the pat
|
|
|
600
600
|
classification is the primary finding and the regional findings provide
|
|
601
601
|
supporting detail.</p>
|
|
602
602
|
<hr>
|
|
603
|
-
<h2 id="6-
|
|
604
|
-
<p>The
|
|
605
|
-
|
|
606
|
-
dies right there. Open it from the toolbar
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
<
|
|
612
|
-
|
|
613
|
-
<
|
|
614
|
-
|
|
603
|
+
<h2 id="6-summary-panel">6. Summary panel</h2>
|
|
604
|
+
<p>The Summary panel docks next to the map and gives you the wafer or lot at a
|
|
605
|
+
glance, with findings sitting directly under the headline numbers so a clicked
|
|
606
|
+
finding can highlight the affected dies right there. Open it from the toolbar.</p>
|
|
607
|
+
<div data-wmap-demo="summary-panel" class="wmap-demo"></div><p><em>The Summary panel open alongside a single wafer. Click any finding to
|
|
608
|
+
highlight the affected dies.</em></p>
|
|
609
|
+
<p>Sections, top to bottom:</p>
|
|
610
|
+
<ul>
|
|
611
|
+
<li><strong>Summary / Lot Summary</strong> \u2014 die and wafer counts, yield, and the population
|
|
612
|
+
the rest of the panel is computed over. On a lot, "Mean wafer yield" is an
|
|
613
|
+
<em>unweighted</em> mean of each wafer's own yield; it is deliberately not the same
|
|
614
|
+
statistic as the bin breakdown's pass-bin share, which weights every die
|
|
615
|
+
equally. The two agree only when die counts are even across the lot.</li>
|
|
616
|
+
<li><strong>Findings</strong> \u2014 detected anomalies, most severe first. Severity chips narrow
|
|
617
|
+
the list; Kind and Region dropdowns appear once there are enough findings to
|
|
618
|
+
be worth narrowing.</li>
|
|
619
|
+
<li><strong>Bin breakdown</strong> \u2014 bars as a share of dies, pass bins first and then failing
|
|
620
|
+
bins by descending count, so the dominant failure mode is at the top. It
|
|
621
|
+
follows the map's plot mode: a soft-bin map gets a soft-bin breakdown. When a
|
|
622
|
+
wafer carries both bin types, a <strong>Hard / Soft</strong> selector in the section header
|
|
623
|
+
overrides that.</li>
|
|
624
|
+
<li><strong>Region yield</strong> \u2014 ring yield by default, with a <strong>Ring / Quadrant</strong> selector.
|
|
625
|
+
Ring is the default because edge roll-off is the pattern that dominates real
|
|
626
|
+
wafer maps; a genuinely asymmetric quadrant is reported as a finding above,
|
|
627
|
+
with a significance test behind it.</li>
|
|
628
|
+
<li><strong>Wafer yield</strong> (lot only) \u2014 one bar per wafer with the lot median marked.
|
|
629
|
+
Wafers are in slot order by default, since that is what makes a
|
|
630
|
+
slot-correlated pattern visible; a <strong>Slot / Yield</strong> selector re-sorts. Wafers
|
|
631
|
+
far below the rest of the lot are labelled "low outlier".</li>
|
|
632
|
+
<li><strong>Test values</strong> \u2014 per test: mean, <strong>Ppk</strong>, and spec yield. Ppk (not Cpk)
|
|
633
|
+
because it measures against the <em>overall</em> spread, including wafer-to-wafer
|
|
634
|
+
variation, which is what the dies actually ship against. The full descriptive
|
|
635
|
+
statistics \u2014 min, quartiles, median, max, \u03C3, both spec limits, and all four
|
|
636
|
+
capability indices \u2014 are in the summary report and the CSV export.</li>
|
|
637
|
+
<li><strong>Functional tests</strong> \u2014 pass/fail counts and pass rate per functional test.</li>
|
|
638
|
+
</ul>
|
|
639
|
+
<p>Every section header can be collapsed, and stays collapsed as the panel
|
|
640
|
+
re-renders.</p>
|
|
615
641
|
<p>A <strong>Summary report</strong> button (when present) opens a printable full-detail
|
|
616
|
-
report
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
dialog.</p>
|
|
642
|
+
report \u2014 yield, bin breakdown, ring and quadrant statistics, the full per-test
|
|
643
|
+
table with Cp/Cpk/Pp/Ppk, and the findings list \u2014 and can be saved as a PDF
|
|
644
|
+
from your browser's print dialog.</p>
|
|
620
645
|
<h3 id="why-some-findings-name-two-bins">Why some findings name two bins</h3>
|
|
621
|
-
<p>A finding may read <strong>"hard bin
|
|
646
|
+
<p>A finding may read <strong>"hard bin and soft bin 3 (Fail) (same dies)"</strong>. That is one
|
|
622
647
|
group of dies counted in two bin spaces, not two separate groups added together.
|
|
648
|
+
(When the two bins carry different names, both are spelled out in full.)
|
|
623
649
|
Hard and soft bins are independent numbering systems, so this wording appears
|
|
624
650
|
only when the two happen to cover exactly the same dies \u2014 reporting it twice
|
|
625
651
|
would look like two independent problems.</p>
|
|
@@ -629,9 +655,11 @@ itself: "pass-bin occurrence is 22 points lower" and "yield is 22
|
|
|
629
655
|
are the same sentence.</p>
|
|
630
656
|
<p>Nothing is discarded \u2014 an application reading the findings programmatically
|
|
631
657
|
still receives every one of them.</p>
|
|
632
|
-
<p>For <strong>lot-level views</strong> (gallery), the
|
|
633
|
-
|
|
634
|
-
|
|
658
|
+
<p>For <strong>lot-level views</strong> (gallery), the panel shows lot-level findings, and the
|
|
659
|
+
<strong>Wafer Yield</strong> section badges each wafer with its own findings count \u2014 click a
|
|
660
|
+
row to open that wafer. There is no separate wafers tab: every wafer appears in
|
|
661
|
+
that one list, including the ones with no findings, which is what lets you see a
|
|
662
|
+
low-yielding wafer that nothing flagged.</p>
|
|
635
663
|
<hr>
|
|
636
664
|
<h2 id="7-lot-stack-maps">7. Lot-stack maps</h2>
|
|
637
665
|
<p>A lot-stack map combines multiple individual wafer results into a single
|
|
@@ -715,16 +743,51 @@ hides the other's toolbar button, since Findings has nothing to highlight
|
|
|
715
743
|
against once the map is replaced.</p>
|
|
716
744
|
<p>Insights is organized into three sub-tabs:</p>
|
|
717
745
|
<ul>
|
|
718
|
-
<li><strong>Overview</strong> \u2014 a
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
746
|
+
<li><p><strong>Overview</strong> \u2014 a <strong>test pass rate</strong> chart showing which test fails most (and,
|
|
747
|
+
with "Group by" active, whether it fails more in one split than another). It
|
|
748
|
+
offers up to three ways of judging, whichever the data supports: <strong>Spec
|
|
749
|
+
limits</strong> (the value against its limits), <strong>Tester flag</strong> (the pass/fail the
|
|
750
|
+
tester itself recorded, which a parametric test carries whether or not limits
|
|
751
|
+
were exported), and <strong>Functional</strong> for pass/fail-only tests. The two
|
|
752
|
+
parametric views can disagree \u2014 guard bands and dynamic limits routinely cause
|
|
753
|
+
it \u2014 so the card reports how many dies the two judged differently rather than
|
|
754
|
+
picking one for you. Alongside it, headline tiles stating the population (wafers, dies analysed,
|
|
755
|
+
and for a lot the mean wafer yield \u2014 an <em>unweighted</em> mean of each wafer's own
|
|
756
|
+
yield, not the die-weighted figure), a yield bar labelled with the pass bins
|
|
757
|
+
actually in use and marked with the median, a hard/soft bin pareto, and
|
|
758
|
+
ring/quadrant regional yield plus the full per-test statistics table.</p>
|
|
759
|
+
</li>
|
|
760
|
+
<li><p><strong>Distributions</strong> \u2014 process capability (Cp/Cpk/Pp/Ppk for tests with both a
|
|
722
761
|
lower and upper spec limit; tests missing a spec still appear, normalized
|
|
723
|
-
onto their own range and sorted by variability), a test-value box plot,
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
762
|
+
onto their own range and sorted by variability), a test-value box plot, a
|
|
763
|
+
value histogram, and a <strong>wafer-to-wafer trend</strong> \u2014 one point per wafer at its
|
|
764
|
+
mean, \xB11\u03C3 whiskers, the lot mean dashed across, and spec limits where the
|
|
765
|
+
test has them. The trend is always in slot order, deliberately: drift across
|
|
766
|
+
a cassette only reads in the physical sequence, so there is no sort control
|
|
767
|
+
to destroy it. Clicking a capability box drives the box plot, histogram and
|
|
768
|
+
trend onto that same test automatically.</p>
|
|
769
|
+
<p>The box plot, histogram and trend share one row of axis controls, kept in
|
|
770
|
+
sync across all three so switching between them never re-reads the same
|
|
771
|
+
data on a different scale: <strong>Axis includes limits</strong> widens the axis to cover
|
|
772
|
+
LSL/USL even where the data sits well clear of them (on by default only when
|
|
773
|
+
doing so still leaves the data at least a third of the axis \u2014 otherwise a
|
|
774
|
+
generous spec window would squash a perfectly capable distribution into a
|
|
775
|
+
sliver), and <strong>Clip outliers</strong> narrows the axis to a robust range (the
|
|
776
|
+
Tukey fence, 1.5\xD7 IQR beyond Q1/Q3) so a handful of extreme values don't
|
|
777
|
+
stretch the axis until the rest of the distribution reads as a flat line.
|
|
778
|
+
Clipping affects the AXIS only \u2014 every reported statistic (mean, \u03C3, Cpk,
|
|
779
|
+
the box's own five-number summary) is computed from every value; nothing is
|
|
780
|
+
excluded from the numbers, and the panel states how many points sit outside
|
|
781
|
+
the visible range when some do. The box plot additionally has its own
|
|
782
|
+
<strong>Log scale</strong> toggle, independent of the shared pair above (available once
|
|
783
|
+
every plotted value is positive) \u2014 the histogram and trend charts do not
|
|
784
|
+
offer one.</p>
|
|
785
|
+
</li>
|
|
786
|
+
<li><p><strong>Correlation</strong> \u2014 a test-to-test correlation matrix and a die-level scatter
|
|
787
|
+
plot, both stating the sample size the coefficients are computed over.
|
|
788
|
+
Clicking a matrix cell drives the scatter plot onto that pair, where <code>r</code> and
|
|
789
|
+
<code>n</code> for that pair are printed and update as you filter the legend.</p>
|
|
790
|
+
</li>
|
|
728
791
|
</ul>
|
|
729
792
|
<p>In a gallery with more than one wafer, a <strong>Group by</strong> control appears whenever
|
|
730
793
|
wafer metadata (lot, product, test program, temperature, split, or a custom
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const WMAP_VERSION = "0.
|
|
2
|
-
export declare const WMAP_BUILD_TIME = "2026-
|
|
1
|
+
export declare const WMAP_VERSION = "0.27.0";
|
|
2
|
+
export declare const WMAP_BUILD_TIME = "2026-09-09T07:40:19.349Z";
|
|
3
3
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const WMAP_VERSION="0.
|
|
1
|
+
export const WMAP_VERSION="0.27.0",WMAP_BUILD_TIME="2026-09-09T07:40:19.349Z";
|
|
@@ -42,10 +42,6 @@ export declare function collectWarnings(sources: {
|
|
|
42
42
|
statsSummary?: StatsSummary | null;
|
|
43
43
|
lotStatsSummary?: LotStatsSummary | null;
|
|
44
44
|
}): WaferWarning[];
|
|
45
|
-
/**
|
|
46
|
-
* The Summary panel banner. One block per severity present, so an "dies may be
|
|
47
|
-
* mis-positioned" error is never visually flattened into an advisory.
|
|
48
|
-
*/
|
|
49
45
|
export declare function buildWarningsBanner(warnings: WaferWarning[], ownerDocument?: Document): HTMLDivElement;
|
|
50
46
|
/**
|
|
51
47
|
* The popup opened by the toolbar's warning indicator.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CLR as
|
|
1
|
+
import{SHADOW as O,TRACKING as w,SPACE as s,RADIUS as E,FONT as y,CLR as l,Z_BASE as B}from"./toolbar.js";const $={error:0,warning:1,info:2};export function severityOf(e){return e.severity??"warning"}export function collectWarnings(e){const n=[],i=new Set,o=t=>{if(!t?.message)return;const r=`${t.code}\0${t.message}`;i.has(r)||(i.add(r),n.push(t))};for(const t of e.result?.warnings??[])o(t);for(const t of e.statsSummary?.stats.warnings??[])o(t);for(const t of e.lotStatsSummary?.perWafer??[])for(const r of t.summary.stats.warnings??[])o(r);return n.sort((t,r)=>$[severityOf(t)]-$[severityOf(r)])}function v(e){return e==="error"?{bg:l.errBg,border:l.errBorder,text:l.errText}:{bg:l.warnBg,border:l.warnBorder,text:l.warnText}}function C(e){return e==="error"?"\u26D4":e==="info"?"\u2139":"\u26A0"}const k={"inferred-pitch":"Die pitch inferred","partial-coverage":"Partial wafer coverage","geometry-conflict":"Geometry conflict","edge-exclusion-exceeds-radius":"Edge exclusion exceeds radius","test-count-capped":"Test analysis skipped"};function S(e){const n=e.code?k[e.code]:void 0;if(n)return n;const i=e.message.split(/(?<=\.)\s/)[0]??e.message;return i.length>60?`${i.slice(0,57)}\u2026`:i}export function buildWarningsBanner(e,n=document){const i=n.createElement("div");Object.assign(i.style,{marginBottom:s.lg});for(const o of e){const t=severityOf(o),r=v(t),m=n.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=n.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=n.createElement("span");Object.assign(g.style,{flex:"1",minWidth:"0"}),g.textContent=`${C(t)} ${S(o)}`;const d=n.createElement("span");Object.assign(d.style,{flexShrink:"0"}),a.append(g,d);const p=n.createElement("div");Object.assign(p.style,{padding:`0 ${s.lg} ${s.sm}`}),p.textContent=o.message;let c=!1;const f=()=>{d.textContent=c?"\u25B4":"\u25BE",p.style.display=c?"block":"none",a.setAttribute("aria-expanded",String(c)),a.setAttribute("aria-label",`${S(o)}. ${c?"Click to collapse.":"Click to expand for detail."}`)};f(),a.addEventListener("click",()=>{c=!c,f()}),a.addEventListener("mouseenter",()=>{d.style.opacity="0.65"}),a.addEventListener("mouseleave",()=>{d.style.opacity="1"}),m.append(a,p),i.appendChild(m)}return i}export function buildWarningsMenuEl(e,n,i=window){const o=i.document,t=o.createElement("div"),r=280,a=e.left+r<=(i.innerWidth??1/0)?e.left:Math.max(4,e.right-r);Object.assign(t.style,{position:"fixed",top:`${e.bottom+4}px`,left:`${a}px`,background:l.menuBg,border:`1px solid ${l.menuBorder}`,borderRadius:E.control,boxShadow:O.menu,zIndex:B,width:`${r}px`,maxHeight:"320px",overflowY:"auto",padding:"4px 0",pointerEvents:"auto"}),t.setAttribute("role","dialog"),t.setAttribute("aria-label","Data warnings");const g=o.createElement("div");Object.assign(g.style,{padding:`${s.sm} ${s.xl} ${s.xs}`,fontSize:y.body,fontWeight:"700",letterSpacing:w,textTransform:"uppercase",color:l.label}),g.textContent=n.length===1?"1 warning":`${n.length} warnings`,t.appendChild(g);for(const d of n){const p=severityOf(d),c=v(p),f=o.createElement("div");Object.assign(f.style,{display:"flex",gap:s.md,padding:`${s.md} ${s.xl}`,fontSize:y.body,lineHeight:"1.5",color:l.text,borderTop:`1px solid ${l.menuBorder}`,alignItems:"flex-start"});const u=o.createElement("span");Object.assign(u.style,{color:c.text,flexShrink:"0"}),u.textContent=C(p),f.appendChild(u);const x=o.createElement("div"),b=o.createElement("div");Object.assign(b.style,{fontWeight:"700",color:c.text,marginBottom:s.xxs}),b.textContent=d.code,x.appendChild(b);const h=o.createElement("div");h.textContent=d.message,x.appendChild(h),f.appendChild(x),t.appendChild(f)}return t}
|
|
@@ -57,4 +57,15 @@ export declare function percentile98(values: number[]): number;
|
|
|
57
57
|
* For pitch estimation, values should be pre-rounded to absorb noise.
|
|
58
58
|
*/
|
|
59
59
|
export declare function modeOf(values: number[]): number | null;
|
|
60
|
+
/**
|
|
61
|
+
* CSV field escaper — quoting plus the formula-injection guard above.
|
|
62
|
+
*
|
|
63
|
+
* Lives here rather than in `canvas-adapter/summaryPanel.ts`, its original home:
|
|
64
|
+
* it is a pure string function with no DOM, and it now has consumers in the
|
|
65
|
+
* summary panel, the die list AND the correlation chart. Importing summaryPanel
|
|
66
|
+
* from a chart to reach it would have inverted the dependency (summaryPanel
|
|
67
|
+
* already imports charts/chartShell), so the third consumer made the wrong home
|
|
68
|
+
* obvious. Every CSV this library writes must go through it.
|
|
69
|
+
*/
|
|
70
|
+
export declare function csvField(value: string): string;
|
|
60
71
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function compareNatural(t,n){return t.localeCompare(n,"en",{numeric:!0})}export function prettyKey(t){return t.replace(/([A-Z])/g," $1").replace(/_/g," ").trim().replace(/^./,n=>n.toUpperCase())}export function clamp01(t){return Math.max(0,Math.min(1,t))}export function mean(t){if(t.length===0)return NaN;let n=0;for(const
|
|
1
|
+
export function compareNatural(t,n){return t.localeCompare(n,"en",{numeric:!0})}export function prettyKey(t){return t.replace(/([A-Z])/g," $1").replace(/_/g," ").trim().replace(/^./,n=>n.toUpperCase())}export function clamp01(t){return Math.max(0,Math.min(1,t))}export function mean(t){if(t.length===0)return NaN;let n=0;for(const r of t)n+=r;return n/t.length}export function medianOfSorted(t){const n=t.length;if(n===0)return 0;const r=Math.floor(n/2);return n%2===1?t[r]:(t[r-1]+t[r])/2}export function median(t){return medianOfSorted([...t].sort((n,r)=>n-r))}export function percentile98(t){if(t.length===0)return 0;const n=[...t].sort((o,e)=>o-e),r=Math.floor(n.length*.98);return n[Math.min(r,n.length-1)]}export function modeOf(t){if(!t.length)return null;const n=new Map;for(const e of t)n.set(e,(n.get(e)??0)+1);let r=0,o=t[0];for(const[e,c]of n)c>r&&(r=c,o=e);return o}const i=/^[=+\-@]/;export function csvField(t){const n=i.test(t)&&Number.isNaN(Number(t))?`'${t}`:t;return/[",\n\r]/.test(n)?`"${n.replace(/"/g,'""')}"`:n}
|
|
@@ -75,14 +75,46 @@ export interface ViewOverlay {
|
|
|
75
75
|
lineWidth: number;
|
|
76
76
|
fill?: string;
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* A renderer-agnostic description of one drawn wafer map: everything `toCanvas`
|
|
80
|
+
* needs, in physical (mm) coordinates, with no canvas or DOM dependency.
|
|
81
|
+
*
|
|
82
|
+
* This is the contract between `buildView` and a renderer — see the `pipeline`
|
|
83
|
+
* example for the low-level path. Hosts using `buildWaferMap` + `renderWaferMap`
|
|
84
|
+
* never need it: that pipeline builds and re-builds the view internally, and the
|
|
85
|
+
* copy hung on `WaferMapResult.view` is marked `@internal` precisely because
|
|
86
|
+
* reading it there means reaching past the API that owns it.
|
|
87
|
+
*
|
|
88
|
+
* Treat the geometry arrays as read-only. `dies` and `hoverPoints` are
|
|
89
|
+
* **index-parallel** — `hoverPoints[i]` is the transformed centre of `dies[i]` —
|
|
90
|
+
* and code that resolves a die from a screen position depends on that staying
|
|
91
|
+
* true.
|
|
92
|
+
*/
|
|
78
93
|
export interface View {
|
|
94
|
+
/** Die rectangles to fill, in draw order, already carrying their resolved colour. */
|
|
79
95
|
rectangles: ViewRect[];
|
|
96
|
+
/**
|
|
97
|
+
* Transformed centre of each die, index-parallel with {@link dies}. Separate
|
|
98
|
+
* from `rectangles` because a die always has a centre for hit-testing and
|
|
99
|
+
* viewport fitting even when it is not drawn (a partial die with
|
|
100
|
+
* `showPartialDies: false`).
|
|
101
|
+
*/
|
|
80
102
|
hoverPoints: ViewHoverPoint[];
|
|
103
|
+
/** Text to draw — die labels and any axis/indicator strings the view generated. */
|
|
81
104
|
texts: ViewText[];
|
|
105
|
+
/** Non-die geometry: wafer outline, notch, ring/quadrant boundaries, reticle grid, XY indicator. */
|
|
82
106
|
overlays: ViewOverlay[];
|
|
107
|
+
/** The plot mode this view was built for. Colours in `rectangles` follow it. */
|
|
83
108
|
plotMode: PlotMode;
|
|
109
|
+
/** Name of the colour scheme used, as registered with `registerColorScheme`. */
|
|
84
110
|
colorScheme: string;
|
|
111
|
+
/** Wafer-level metadata carried through from the wafer, or `null`. */
|
|
85
112
|
metadata: WaferMetadata | null;
|
|
113
|
+
/**
|
|
114
|
+
* The positioned dies this view was built from, index-parallel with
|
|
115
|
+
* {@link hoverPoints}. Original grid coordinates are preserved on each die —
|
|
116
|
+
* never derive display coordinates from these.
|
|
117
|
+
*/
|
|
86
118
|
dies: Die[];
|
|
87
119
|
/** Actual [min, max] of the value data used for color normalization. */
|
|
88
120
|
valueRange: [number, number];
|
|
@@ -370,7 +402,6 @@ export interface HoverTextOptions {
|
|
|
370
402
|
aggrMethod?: string;
|
|
371
403
|
/** Number of wafers in the stack, used to annotate a count with its share of the lot. */
|
|
372
404
|
lotSize?: number;
|
|
373
|
-
waferMeta?: WaferMetadata | null;
|
|
374
405
|
/** Active test number (value mode only) — leads the tooltip and gets an out-of-spec note. */
|
|
375
406
|
activeTest?: number;
|
|
376
407
|
/** Reticle geometry, when configured — appended as a "Reticle (col, row)" line below Die (x, y). */
|
|
@@ -425,6 +456,8 @@ export declare function generateTextOverlay(dies: PositionedDie[], txCoords: Flo
|
|
|
425
456
|
/** Active `die.metadata` key + its resolved value→color map, `'metadata'` mode only. */
|
|
426
457
|
activeMetadataKey?: string;
|
|
427
458
|
metadataColorMap?: Map<string, string> | null;
|
|
459
|
+
/** Mirrors `View.allIntegerValues` — see the colorbar's own `tickFmt` override in toCanvas.ts. */
|
|
460
|
+
allIntegerValues?: boolean;
|
|
428
461
|
}): ViewText[];
|
|
429
462
|
/**
|
|
430
463
|
* Build a renderer-agnostic scene from a wafer, dies, and display options.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hasPosition as Zt}from"../core/dies.js";import{getReticleCell as te}from"../core/reticle.js";import{metadataCategoricalValue as ee,metadataDisplayValue as ne}from"../core/metadata.js";import{affineRotation as dt,affineMirror as Rt,affineCompose as it,affinePoint as Y,affineVector as ht,affineSwapsAxes as ae}from"../core/transforms.js";import{contrastTextColor as G,SPEC_PASS_FILL as pt,SPEC_FAIL_LOW as ie,SPEC_FAIL_HIGH as mt}from"./colorMap.js";import{getColorScheme as se}from"./colorSchemes.js";import{getDieTestValue as R,getTestPassStatus as Q,isParametricTest as yt}from"./buildWaferMap.js";import{fmt as gt,fmtColorbarAxis as xt,fmtAggregationMethod as Ct}from"./fmt.js";import{metadataValueColor as oe}from"./colorMap.js";import{compareNatural as re,clamp01 as Nt}from"../core/utils.js";import{prettyKey as Ht}from"../core/utils.js";export function resolveTestNumber(t,e){return e?.length?{testNumber:(findTestDef(e,t)??e[0]).testNumber}:{testNumber:t}}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 le="#d3d6db",Ot="#e8e9ea",ce="#eceef0",j="#d6d9dd";function st(t,e){return e?ee(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 ue(t,e,a){const{x:i,y:s}=t.center,n=dt(t.orientation,i,s),o=it(Rt(e?.x??!1,e?.y??!1,i,s),dt(t.orientation,i,s)),r=it(Rt(a?.flipX??!1,a?.flipY??!1,i,s),dt(a?.rotation??0,i,s));return{bakedToScreen:r,physicalToScreen:it(r,n),gridToScreen:it(r,o)}}function W(t,e=!1){return{points:[t],closed:e}}function fe(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),u=s.length/2,x=n-a.x,y=o-a.y;return s.type==="bottom"&&y<-r?(o=a.y-r,n=a.x+Math.max(-u,Math.min(u,x))):s.type==="top"&&y>r?(o=a.y+r,n=a.x+Math.max(-u,Math.min(u,x))):s.type==="left"&&x<-r?(n=a.x-r,o=a.y+Math.max(-u,Math.min(u,y))):s.type==="right"&&x>r&&(n=a.x+r,o=a.y+Math.max(-u,Math.min(u,y))),{x:n,y:o}}function At(t,e){return t.map(e).join(" / ")}function de(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 pe(t,e,a){if(e?.length){const i=e.flatMap(s=>{const n=s.testNumber;if(!yt(s)){const u=Q(t,n,s);return u===void 0?[]:[{key:n,label:s.name,value:u?"Pass":"Fail"}]}const o=R(t,n);if(o===void 0)return[];const r=t.testPass?.[n]===!1?" <i>(recorded fail)</i>":"";return[{key:n,label:s.name,value:`${gt(o,s.unit,a)}${r}`}]});if(i.length)return i}return t.testValues&&Object.keys(t.testValues).length>0?Object.entries(t.testValues).map(([i,s])=>({key:Number(i),label:`Test ${i}`,value:gt(s,void 0,a)})):t.testPass&&Object.keys(t.testPass).length>0?Object.entries(t.testPass).map(([i,s])=>({key:Number(i),label:`Test ${i}`,value:s?"Pass":"Fail"})):[]}export function buildHoverText(t,e,a={}){const{testDefs:i,hbinDefs:s,sbinDefs:n,fallbackFormat:o,aggrMethod:r,lotSize:u,waferMeta:x,activeTest:y,reticleConfig:B,metadataFields:V}=a,v=s?new Map(s.map(c=>[c.bin,c])):null,T=n?new Map(n.map(c=>[c.bin,c])):null,m=[`Die (${t.x}, ${t.y})`];if(B&&Zt(t)){const c=te(t,B);m.push(`Reticle (${c.column}, ${c.row})`)}if(e==="stackedValues"){const c=R(t,0);if(c!==void 0){const f=i?.[0],h=f?.testNumber,g=f?.name??(h!=null?`Test ${h}`:"Value"),M=r?` (${r})`:"";m.push(`${g}${M}: ${gt(c,f?.unit,o)}`)}}else if(e==="stackedBins"||e==="stackedSoftBins"){const c=R(t,0),f=e==="stackedSoftBins"?t.sbin:t.hbin;if(c!==void 0){const h=e==="stackedSoftBins"?T:v,g=f!==void 0?h?.get(f)?.name?`${f} \xB7 ${h.get(f).name}`:`Bin ${f}`:"Bin";let M;if(r==="percent")M=`${c.toFixed(0)}%`;else{const H=u?` (${(c/u*100).toFixed(0)}% of lot)`:"";M=`${c}${H}`}const p=r?` [${Ct(r)}]`:"";m.push(`${g}: ${M}${p}`)}}else{const c=pe(t,i,o);if(e==="value"&&c.length){const{testNumber:f}=resolveTestNumber(y??0,i),h=findTestDef(i,f),g=R(t,f);let M=c.findIndex(E=>E.key===f);M<0&&(M=0);const p=c[M];let H=`<b>${p.label}: ${p.value}</b>`;if(p.key===f&&h){const E=classifySpec(g,h);(E==="failLow"||E==="failHigh")&&(H+=" <i>(out of spec)</i>")}m.push(H);const A=c.length-1;A>0&&m.push(`<i>+${A} more test${A===1?"":"s"}</i>`)}else if(c.length){const f=c.length;m.push(`<i>${f} test result${f===1?"":"s"} recorded</i>`)}if(t.hbin!==void 0||t.sbin!==void 0){const f=[];if(t.hbin!==void 0){const h=v?.get(t.hbin)?.name,g=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);f.push(`HBin: ${g}`)}if(t.sbin!==void 0){const h=T?.get(t.sbin)?.name,g=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);f.push(`SBin: ${g}`)}m.push(f.join(" \xB7 "))}}t.retestCount!==void 0&&m.push(`Retests: ${t.retestCount}`),t.siteNum!==void 0&&m.push(`Site: ${t.siteNum}`),t.partId!==void 0&&m.push(`Part ID: ${t.partId}`),t.partial&&m.push("<i>partial die</i>"),t.probeIndex!==void 0&&m.push(`Probe: #${t.probeIndex}`);const S={...x??{},...t.metadata??{}};for(const[c,f]of Object.entries(S)){const h=ne(f);if(h===void 0)continue;const g=V?.find(M=>M.key===c)?.label??Ht(c);m.push(`${g}: ${h}`)}return m.join("<br>")}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?Ht(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}=xt(o,n?.name,n?.unit,e),u=r||"Value",x=t.aggrMethod?` \xB7 ${Ct(t.aggrMethod)}`:"";return{primary:`${u}${x}`,secondary:i}}default:{const n=findTestDef(t.testDefs,t.activeTest),o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:r}=xt(o,n?.name,n?.unit,e),u=r||n?.name;if(t.passFailDisplay!=="off"){const x=n?.testNumber??t.activeTest,y=u?`${u} \xB7 #${x}`:`Test ${x}`,B=t.passFailDisplay==="spec"?"Spec pass/fail":n!==void 0&&!yt(n)?"Functional pass/fail":"Tester pass/fail";return{primary:y,secondary:B}}return{primary:u??`Test ${t.activeTest}`,secondary:""}}}}export function generateTextOverlay(t,e,a){const{plotMode:i,colorFns:s,normalize:n,activeTest:o,valueRange:r,testDefs:u,fallbackFormat:x,passFailDisplay:y="off",activeMetadataKey:B,metadataColorMap:V}=a,v=findTestDef(u,o),{tickFmt:T}=xt(r[1],v?.name,v?.unit,x),{testNumber:m}=resolveTestNumber(o,u);return t.flatMap((S,c)=>{let f="",h="#111111";if(i==="value"&&y==="test"){const p=Q(S,m,v);if(p===void 0)return[];f=p?"P":"F",h=G(p?pt:mt)}else if(i==="value"){const p=R(S,m);if(p===void 0)return[];f=At([p],T),h=G(s.forValue(n(p)))}else if(i==="hardBin"||i==="softBin"){const p=i==="softBin"?S.sbin:S.hbin;if(p===void 0)return[];f=String(p),h=G(s.forBin(p))}else if(i==="metadata"){const p=st(S,B);if(p===void 0)return[];f=p,h=G(V?.get(f)??j)}else{const p=R(S,0);if(p===void 0)return[];f=At([p],T),h=G(s.forValue(n(p)))}const g=e?e[c*2]:S.physX,M=e?e[c*2+1]:S.physY;return[{x:g,y:M,text:f,fontSize:he(S,f),color:h,align:"center"}]})}function Et(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function me(t,e,a=720){const{center:i,radius:s,notch:n}=t,o=m=>Y(e,m.x,m.y);if(!(n!==void 0&&t.diameter>150)){const m=[];for(let S=0;S<=a;S++){const c=2*Math.PI*S/a;m.push(o(fe(t,c)))}return[{kind:"wafer-boundary",...W(m,!0),lineColor:"#888888",lineWidth:1}]}const u=1.25,x=n.length,y=Et(n.type),B=Math.atan2(x,s),V=s-u,v=[];let T=!1;for(let m=0;m<=a;m++){const S=2*Math.PI*m/a;let c=S-y;for(;c>Math.PI;)c-=2*Math.PI;for(;c<-Math.PI;)c+=2*Math.PI;if(Math.abs(c)<=B){if(!T){T=!0;const f={x:i.x+s*Math.cos(y-B),y:i.y+s*Math.sin(y-B)},h={x:i.x+V*Math.cos(y),y:i.y+V*Math.sin(y)},g={x:i.x+s*Math.cos(y+B),y:i.y+s*Math.sin(y+B)};v.push(o(f),o(h),o(g))}continue}v.push(o({x:i.x+s*Math.cos(S),y:i.y+s*Math.sin(S)}))}return[{kind:"wafer-boundary",...W(v,!0),lineColor:"#888888",lineWidth:1}]}function ye(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,u=[];for(let x=0;x<=i;x++){const y=2*Math.PI*x/i,B={x:t.center.x+r*Math.cos(y),y:t.center.y+r*Math.sin(y)};u.push(Y(e,B.x,B.y))}s.push({kind:"ring-boundary",...W(u,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function ge(t,e,a,i){const{x:s,y:n}=t.center,o=t.radius,r=(S,c)=>Y(e,S,c),u=a-s,x=Math.sqrt(Math.max(0,o*o-u*u)),y=r(a,n-x),B=r(a,n+x),V=i-n,v=Math.sqrt(Math.max(0,o*o-V*V)),T=r(s-v,i),m=r(s+v,i);return[{kind:"quadrant-boundary",...W([y,B]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...W([T,m]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function xe(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])=>Y(e,o,r))],closed:!0,lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5,fill:"rgba(0,0,0,0)"}})}function be(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",...W(e.map(a=>({x:a.physX,y:a.physY}))),lineColor:"rgba(220,80,0,0.55)",lineWidth:1}]:[]}function Me(t,e,a,i,s,n,o,r,u,x,y,B,V,v,T="off",m,S,c){const f=e.width-o,h=e.height-o,g=n?h:f,M=n?f:h,p=$=>s==="softBin"?$.sbin:$.hbin,H=$=>st($,m);if(e.partial){t.push({x:a,y:i,width:g,height:M,fill:le,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:i,width:g,height:M,fill:ce,type:"stacked",metadata:e.metadata});return}if(u!==void 0&&(s==="hardBin"||s==="softBin")&&p(e)!==u){t.push({x:a,y:i,width:g,height:M,fill:Ot,type:"hardBin",metadata:e.metadata});return}if(c!==void 0&&s==="metadata"&&H(e)!==c){t.push({x:a,y:i,width:g,height:M,fill:Ot,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(T==="test"){const J=Q(e,y,v),ot=J===void 0?j:J?pt:mt;t.push({x:a,y:i,width:g,height:M,fill:ot,type:"value",metadata:e.metadata});return}const $=R(e,y),D=classifySpec($,v);let _,U;$===void 0?_=j:T==="spec"?_=D==="failLow"?ie:D==="failHigh"?mt:pt:(_=r.forValue(x($)),(D==="failLow"||D==="failHigh")&&(U=D)),t.push({x:a,y:i,width:g,height:M,fill:_,type:"value",specMark:U,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const $=p(e),D=$!=null?r.forBin($):j,_=$!=null&&!V.has($)?!0:void 0;t.push({x:a,y:i,width:g,height:M,fill:D,type:s,binFail:_,metadata:e.metadata});return}if(s==="metadata"){const $=H(e),D=$!==void 0?S?.get($)??j:j;t.push({x:a,y:i,width:g,height:M,fill:D,type:"metadata",metadata:e.metadata});return}const A=R(e,0),E=A!==void 0?r.forValue(x(A)):j;t.push({x:a,y:i,width:g,height:M,fill:E,type:"value",metadata:e.metadata})}function Se(t,e,a){const i=t.radius*.15,s=ht(e,i,0),n=ht(e,0,i),o=s.x+n.x>=0?-1:1,r=s.y+n.y>=0?-1:1,u={x:t.center.x+o*t.radius*.9,y:t.center.y+r*t.radius*.9},x={x:u.x+s.x,y:u.y+s.y},y={x:u.x+n.x,y:u.y+n.y};return a.push({x:x.x+s.x*.35,y:x.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",...W([u,x]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...W([u,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:u=!1,ringCount:x=4,showRingBoundaries:y=!1,showQuadrantBoundaries:B=!1,showXYIndicator:V=!1,dieGap:v=1,colorScheme:T="default",passBins:m=[1],highlightBin:S,highlightMetadataValue:c,interactiveTransform:f,valueRange:h,testDefs:g,activeTest:M=0,activeMetadataKey:p,fallbackFormat:H="engineering",aggregationMethod:A,lotSize:E,dataAxisFlip:$,isLotStack:D=!1,logScale:_,colorbarRangeMode:U="spec",passFailDisplay:J,showPartialDies:ot=!0}=a,bt=J??"off",Mt=i?.hbinDefs,St=i?.sbinDefs,$t=i?.metadataFields,_t=$t?.find(l=>l.key===p),wt={x:($?.x??!1)!==(f?.flipX??!1),y:($?.y??!1)!==(f?.flipY??!1)},Wt=Mt?new Map(Mt.map(l=>[l.bin,l])):null,jt=St?new Map(St.map(l=>[l.bin,l])):null,rt=n==="softBin"?jt:Wt,lt=se(T),zt=new Set(m),kt={forValue:de(lt.forValue),forBin:T==="custom"&&rt?l=>rt.get(l)?.color??lt.forBin(l):lt.forBin};let{testNumber:O}=resolveTestNumber(M,g);if(!g?.length&&n==="value"&&!e.some(d=>d.testValues&&O in d.testValues)){const d=e.reduce((b,F)=>{if(!F.testValues)return b;const k=Object.keys(F.testValues).map(Number),q=k.length?Math.min(...k):void 0;return q!==void 0&&(b===void 0||q<b)?q:b},void 0);d!==void 0&&(O=d)}let Z;if(Array.isArray(h))Z=h;else if(h){const{testNumber:l}=resolveTestNumber(h.test,g);Z=l===O?h.range:void 0}const I=findTestDef(g,O),Bt=n==="value"&&I!==void 0&&!yt(I),Kt=I!==void 0&&(I.limitLow!==void 0||I.limitHigh!==void 0);let C="off";n==="value"&&(Bt?C="test":bt==="spec"&&Kt?C="spec":bt==="test"&&e.some(l=>Q(l,O,I)!==void 0)&&(C="test"));const vt=C==="spec"?"spec":U;let P,N,Tt=!1;if(Z)[P,N]=Z;else{const l=n==="stackedValues"||n==="stackedBins"||n==="stackedSoftBins",d=!l&&n==="value"&&vt==="spec"&&I&&(I.limitLow!==void 0||I.limitHigh!==void 0);let b=1/0,F=-1/0,k=!0;for(const q of e){const X=l?R(q,0):R(q,O);X!==void 0&&(X<b&&(b=X),X>F&&(F=X),Number.isInteger(X)||(k=!1))}isFinite(b)||(k=!1),Tt=k,d?(P=I.limitLow!==void 0?I.limitLow:isFinite(b)?b:0,N=I.limitHigh!==void 0?I.limitHigh:isFinite(F)?F:1):(P=isFinite(b)?b:0,N=isFinite(F)?F:1)}P===N&&(N=P+1);const It=Bt?!1:_??I?.logScale??!1,Ft=It&&P>0&&N>0,qt=Ft;let tt;if(Ft){const l=Math.log10(P),d=Math.log10(N)-l;tt=b=>b<=0?0:Nt((Math.log10(b)-l)/d)}else tt=l=>Nt((l-P)/(N-P));const L=ue(t,$,f);let Lt=null;if(t.notch){const l=Et(t.notch.type),d=ht(L.physicalToScreen,Math.cos(l)*t.radius,Math.sin(l)*t.radius),b=Math.hypot(d.x,d.y);b>0&&(Lt={x:d.x/b,y:d.y/b})}let z=1/0;for(let l=0;l<e.length;l++){const d=e[l];d.width<z&&(z=d.width),d.height<z&&(z=d.height)}const Xt=z!==1/0?Math.min(v,z*.12):v,Vt=[],ct=[],Yt=L.bakedToScreen.a===1&&L.bakedToScreen.b===0&&L.bakedToScreen.c===0&&L.bakedToScreen.d===1&&L.bakedToScreen.e===0&&L.bakedToScreen.f===0;let w=null;if(!Yt){w=new Float64Array(e.length*2);for(let l=0;l<e.length;l++){const d=Y(L.bakedToScreen,e[l].physX,e[l].physY);w[l*2]=d.x,w[l*2+1]=d.y}}const Gt=ae(L.gridToScreen),Qt=n==="hardBin"||n==="softBin"||n==="stackedBins"||n==="stackedSoftBins",Ut=n==="softBin"||n==="stackedSoftBins";let et=Qt?new Map:void 0;const ut=n==="value"&&C==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,ft=n==="value"&&C==="test"?{pass:0,fail:0}:void 0,Dt=n==="metadata";let nt=Dt?new Map:void 0,at=null;if(Dt&&p){const l=new Set;for(const b of e){if(b.partial||b.edgeExcluded)continue;const F=st(b,p);F!==void 0&&l.add(F)}const d=[...l].sort(re);at=new Map(d.map((b,F)=>[b,_t?.values?.find(k=>k.value===b)?.color??oe(F)]))}for(let l=0;l<e.length;l++){const d=e[l],b=w?w[l*2]:d.physX,F=w?w[l*2+1]:d.physY;if(ct.push({x:b,y:F}),et&&!d.partial&&!d.edgeExcluded){const k=Ut?d.sbin:d.hbin;k!=null&&et.set(k,(et.get(k)??0)+1)}if(ut&&!d.partial&&!d.edgeExcluded){const k=classifySpec(R(d,O),I);k&&ut[k]++}if(ft&&!d.partial&&!d.edgeExcluded){const k=Q(d,O,I);k!==void 0&&ft[k?"pass":"fail"]++}if(nt&&!d.partial&&!d.edgeExcluded){const k=st(d,p);k!==void 0&&nt.set(k,(nt.get(k)??0)+1)}d.partial&&!ot||Me(Vt,d,b,F,n,Gt,Xt,kt,S,tt,O,rt,zt,I,C,p,at,c)}const Pt=o?generateTextOverlay(e,w,{plotMode:n,colorFns:kt,normalize:tt,activeTest:M,valueRange:[P,N],testDefs:g,fallbackFormat:H,passFailDisplay:C,activeMetadataKey:p,metadataColorMap:at}):[],K=me(t,L.physicalToScreen);y&&K.push(...ye(t,L.physicalToScreen,x)),B&&K.push(...ge(t,L.physicalToScreen,t.center.x,t.center.y)),r&&K.push(...xe(s,L.gridToScreen)),u&&K.push(...be(e)),V&&K.push(...Se(t,L.gridToScreen,Pt));const Jt=ct.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:Vt,hoverPoints:ct,texts:Pt,overlays:K,plotMode:n,colorScheme:T,metadata:t.metadata??null,dies:e,valueRange:[P,N],allIntegerValues:Tt,testDefs:g,activeTest:M,logScale:qt,logScaleRequested:It,aggrMethod:A,lotSize:E,axisFlip:wt,dataAxisFlip:{x:$?.x??!1,y:$?.y??!1},gridToScreen:L.gridToScreen,rotation:((t.orientation+(f?.rotation??0))%360+360)%360,hasReticle:s.length>0,isLotStack:D,passFailDisplay:C,colorBySpec:C==="spec",colorbarRangeMode:vt,waferCenter:t.center,waferRadius:t.radius,notchDir:Lt,binCounts:et,metadataCounts:nt,metadataColorMap:at,activeMetadataKey:p,metadataFields:$t,specCounts:ut,passFailCounts:ft,dieBounds:Jt}}export{getDieKey}from"../core/dies.js";
|
|
1
|
+
import{hasPosition as Zt}from"../core/dies.js";import{getReticleCell as te}from"../core/reticle.js";import{metadataCategoricalValue as ee,metadataDisplayValue as ne}from"../core/metadata.js";import{affineRotation as ht,affineMirror as Rt,affineCompose as at,affinePoint as q,affineVector as mt,affineSwapsAxes as ae}from"../core/transforms.js";import{contrastTextColor as X,SPEC_PASS_FILL as pt,SPEC_FAIL_LOW as ie,SPEC_FAIL_HIGH as yt}from"./colorMap.js";import{getColorScheme as se}from"./colorSchemes.js";import{getDieTestValue as P,getTestPassStatus as Y,isParametricTest as gt}from"./buildWaferMap.js";import{fmt as bt,fmtColorbarAxis as xt,fmtAggregationMethod as Ct}from"./fmt.js";import{metadataValueColor as oe}from"./colorMap.js";import{compareNatural as re,clamp01 as Nt}from"../core/utils.js";import{prettyKey as Ht}from"../core/utils.js";export function resolveTestNumber(t,e){return e?.length?{testNumber:(findTestDef(e,t)??e[0]).testNumber}:{testNumber:t}}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 le="#d3d6db",Ot="#e8e9ea",ce="#aab0ba",j="#d6d9dd";function it(t,e){return e?ee(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 ue(t,e,a){const{x:i,y:s}=t.center,n=ht(t.orientation,i,s),o=at(Rt(e?.x??!1,e?.y??!1,i,s),ht(t.orientation,i,s)),c=at(Rt(a?.flipX??!1,a?.flipY??!1,i,s),ht(a?.rotation??0,i,s));return{bakedToScreen:c,physicalToScreen:at(c,n),gridToScreen:at(c,o)}}function W(t,e=!1){return{points:[t],closed:e}}function fe(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 c=Math.sqrt(i**2-(s.length/2)**2),u=s.length/2,y=n-a.x,p=o-a.y;return s.type==="bottom"&&p<-c?(o=a.y-c,n=a.x+Math.max(-u,Math.min(u,y))):s.type==="top"&&p>c?(o=a.y+c,n=a.x+Math.max(-u,Math.min(u,y))):s.type==="left"&&y<-c?(n=a.x-c,o=a.y+Math.max(-u,Math.min(u,p))):s.type==="right"&&y>c&&(n=a.x+c,o=a.y+Math.max(-u,Math.min(u,p))),{x:n,y:o}}function At(t,e){return t.map(e).join(" / ")}function de(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 me(t,e,a){if(e?.length){const i=e.flatMap(s=>{const n=s.testNumber;if(!gt(s)){const u=Y(t,n,s);return u===void 0?[]:[{key:n,label:s.name,value:u?"Pass":"Fail"}]}const o=P(t,n);if(o===void 0)return[];const c=t.testPass?.[n]===!1?" <i>(recorded fail)</i>":"";return[{key:n,label:s.name,value:`${bt(o,s.unit,a)}${c}`}]});if(i.length)return i}return t.testValues&&Object.keys(t.testValues).length>0?Object.entries(t.testValues).map(([i,s])=>({key:Number(i),label:`Test ${i}`,value:bt(s,void 0,a)})):t.testPass&&Object.keys(t.testPass).length>0?Object.entries(t.testPass).map(([i,s])=>({key:Number(i),label:`Test ${i}`,value:s?"Pass":"Fail"})):[]}export function buildHoverText(t,e,a={}){const{testDefs:i,hbinDefs:s,sbinDefs:n,fallbackFormat:o,aggrMethod:c,lotSize:u,activeTest:y,reticleConfig:p,metadataFields:B}=a,D=s?new Map(s.map(f=>[f.bin,f])):null,T=n?new Map(n.map(f=>[f.bin,f])):null,b=[`Die (${t.x}, ${t.y})`];if(p&&Zt(t)){const f=te(t,p);b.push(`Reticle (${f.column}, ${f.row})`)}if(e==="stackedValues"){const f=P(t,0);if(f!==void 0){const d=i?.[0],h=d?.testNumber,S=d?.name??(h!=null?`Test ${h}`:"Value"),m=c?` (${c})`:"";b.push(`${S}${m}: ${bt(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,S=d!==void 0?h?.get(d)?.name?`${d} \xB7 ${h.get(d).name}`:`Bin ${d}`:"Bin";let m;if(c==="percent")m=`${f.toFixed(0)}%`;else{const I=u?` (${(f/u*100).toFixed(0)}% of lot)`:"";m=`${f}${I}`}const M=c?` [${Ct(c)}]`:"";b.push(`${S}: ${m}${M}`)}}else{const f=me(t,i,o);if(e==="value"&&f.length){const{testNumber:d}=resolveTestNumber(y??0,i),h=findTestDef(i,d),S=P(t,d);let m=f.findIndex(x=>x.key===d);m<0&&(m=0);const M=f[m];let I=`<b>${M.label}: ${M.value}</b>`;if(M.key===d&&h){const x=classifySpec(S,h);(x==="failLow"||x==="failHigh")&&(I+=" <i>(out of spec)</i>")}b.push(I);const N=f.length-1;N>0&&b.push(`<i>+${N} more test${N===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,S=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);d.push(`HBin: ${S}`)}if(t.sbin!==void 0){const h=T?.get(t.sbin)?.name,S=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);d.push(`SBin: ${S}`)}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 v={...t.metadata??{}};for(const[f,d]of Object.entries(v)){const h=ne(d);if(h===void 0)continue;const S=B?.find(m=>m.key===f)?.label??Ht(f);b.push(`${S}: ${h}`)}return b.join("<br>")}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(c=>c.key===t.activeMetadataKey),o=t.activeMetadataKey?Ht(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:c}=xt(o,n?.name,n?.unit,e),u=c||"Value",y=t.aggrMethod?` \xB7 ${Ct(t.aggrMethod)}`:"";return{primary:`${u}${y}`,secondary:i}}default:{const n=findTestDef(t.testDefs,t.activeTest),o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:c}=xt(o,n?.name,n?.unit,e),u=c||n?.name;if(t.passFailDisplay!=="off"){const y=n?.testNumber??t.activeTest,p=u?`${u} \xB7 #${y}`:`Test ${y}`,B=t.passFailDisplay==="spec"?"Spec pass/fail":n!==void 0&&!gt(n)?"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:i,colorFns:s,normalize:n,activeTest:o,valueRange:c,testDefs:u,fallbackFormat:y,passFailDisplay:p="off",activeMetadataKey:B,metadataColorMap:D,allIntegerValues:T}=a,b=findTestDef(u,o),{tickFmt:v}=xt(c[1],b?.name,b?.unit,y),f=T?h=>String(Math.round(h)):v,{testNumber:d}=resolveTestNumber(o,u);return t.flatMap((h,S)=>{let m="",M="#111111";if(i==="value"&&p==="test"){const x=Y(h,d,b);if(x===void 0)return[];m=x?"P":"F",M=X(x?pt:yt)}else if(i==="value"){const x=P(h,d);if(x===void 0)return[];m=At([x],f),M=X(s.forValue(n(x)))}else if(i==="hardBin"||i==="softBin"){const x=i==="softBin"?h.sbin:h.hbin;if(x===void 0)return[];m=String(x),M=X(s.forBin(x))}else if(i==="metadata"){const x=it(h,B);if(x===void 0)return[];m=x,M=X(D?.get(m)??j)}else{const x=P(h,0);if(x===void 0)return[];m=At([x],f),M=X(s.forValue(n(x)))}const I=e?e[S*2]:h.physX,N=e?e[S*2+1]:h.physY;return[{x:I,y:N,text:m,fontSize:he(h,m),color:M,align:"center"}]})}function Et(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function pe(t,e,a=720){const{center:i,radius:s,notch:n}=t,o=v=>q(e,v.x,v.y);if(!(n!==void 0&&t.diameter>150)){const v=[];for(let f=0;f<=a;f++){const d=2*Math.PI*f/a;v.push(o(fe(t,d)))}return[{kind:"wafer-boundary",...W(v,!0),lineColor:"#888888",lineWidth:1}]}const u=1.25,y=n.length,p=Et(n.type),B=Math.atan2(y,s),D=s-u,T=[];let b=!1;for(let v=0;v<=a;v++){const f=2*Math.PI*v/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:i.x+s*Math.cos(p-B),y:i.y+s*Math.sin(p-B)},S={x:i.x+D*Math.cos(p),y:i.y+D*Math.sin(p)},m={x:i.x+s*Math.cos(p+B),y:i.y+s*Math.sin(p+B)};T.push(o(h),o(S),o(m))}continue}T.push(o({x:i.x+s*Math.cos(f),y:i.y+s*Math.sin(f)}))}return[{kind:"wafer-boundary",...W(T,!0),lineColor:"#888888",lineWidth:1}]}function ye(t,e,a,i=360){const s=[],n=Math.max(1,Math.floor(a));for(let o=1;o<n;o++){const c=t.radius*o/n,u=[];for(let y=0;y<=i;y++){const p=2*Math.PI*y/i,B={x:t.center.x+c*Math.cos(p),y:t.center.y+c*Math.sin(p)};u.push(q(e,B.x,B.y))}s.push({kind:"ring-boundary",...W(u,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function ge(t,e,a,i){const{x:s,y:n}=t.center,o=t.radius,c=(f,d)=>q(e,f,d),u=a-s,y=Math.sqrt(Math.max(0,o*o-u*u)),p=c(a,n-y),B=c(a,n+y),D=i-n,T=Math.sqrt(Math.max(0,o*o-D*D)),b=c(s-T,i),v=c(s+T,i);return[{kind:"quadrant-boundary",...W([p,B]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...W([b,v]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function be(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,c])=>q(e,o,c))],closed:!0,lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5,fill:"rgba(0,0,0,0)"}})}function xe(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",...W(e.map(a=>({x:a.physX,y:a.physY}))),lineColor:"rgba(220,80,0,0.55)",lineWidth:1}]:[]}function Me(t,e,a,i,s,n,o,c,u,y,p,B,D,T,b="off",v,f,d){const h=e.width-o,S=e.height-o,m=n?S:h,M=n?h:S,I=k=>s==="softBin"?k.sbin:k.hbin,N=k=>it(k,v);if(e.partial){t.push({x:a,y:i,width:m,height:M,fill:le,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:i,width:m,height:M,fill:ce,type:"stacked",metadata:e.metadata});return}if(u!==void 0&&(s==="hardBin"||s==="softBin")&&I(e)!==u){t.push({x:a,y:i,width:m,height:M,fill:Ot,type:"hardBin",metadata:e.metadata});return}if(d!==void 0&&s==="metadata"&&N(e)!==d){t.push({x:a,y:i,width:m,height:M,fill:Ot,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(b==="test"){const Q=Y(e,p,T),ot=Q===void 0?j:Q?pt:yt;t.push({x:a,y:i,width:m,height:M,fill:ot,type:"value",metadata:e.metadata});return}const k=P(e,p),R=classifySpec(k,T);let _,G;k===void 0?_=j:b==="spec"?_=R==="failLow"?ie:R==="failHigh"?yt:pt:(_=c.forValue(y(k)),(R==="failLow"||R==="failHigh")&&(G=R)),t.push({x:a,y:i,width:m,height:M,fill:_,type:"value",specMark:G,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const k=I(e),R=k!=null?c.forBin(k):j,_=k!=null&&!D.has(k)?!0:void 0;t.push({x:a,y:i,width:m,height:M,fill:R,type:s,binFail:_,metadata:e.metadata});return}if(s==="metadata"){const k=N(e),R=k!==void 0?f?.get(k)??j:j;t.push({x:a,y:i,width:m,height:M,fill:R,type:"metadata",metadata:e.metadata});return}const x=P(e,0),st=x!==void 0?c.forValue(y(x)):j;t.push({x:a,y:i,width:m,height:M,fill:st,type:"value",metadata:e.metadata})}function Se(t,e,a){const i=t.radius*.15,s=mt(e,i,0),n=mt(e,0,i),o=s.x+n.x>=0?-1:1,c=s.y+n.y>=0?-1:1,u={x:t.center.x+o*t.radius*.9,y:t.center.y+c*t.radius*.9},y={x:u.x+s.x,y:u.y+s.y},p={x:u.x+n.x,y:u.y+n.y};return a.push({x:y.x+s.x*.35,y:y.y+s.y*.35,text:"+X",fontSize:10,color:"#cc3300",align:"center",role:"indicator"},{x:p.x+n.x*.35,y:p.y+n.y*.35,text:"+Y",fontSize:10,color:"#0044cc",align:"center",role:"indicator"}),[{kind:"xy-indicator",...W([u,y]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...W([u,p]),lineColor:"#0044cc",lineWidth:2}]}export function buildView(t,e,a={},i){const s=a.reticles??[],{plotMode:n="value",showDieLabels:o=!1,showReticle:c=!1,showProbePath:u=!1,ringCount:y=4,showRingBoundaries:p=!1,showQuadrantBoundaries:B=!1,showXYIndicator:D=!1,dieGap:T=1,colorScheme:b="default",passBins:v=[1],highlightBin:f,highlightMetadataValue:d,interactiveTransform:h,valueRange:S,testDefs:m,activeTest:M=0,activeMetadataKey:I,fallbackFormat:N="engineering",aggregationMethod:x,lotSize:st,dataAxisFlip:k,isLotStack:R=!1,logScale:_,colorbarRangeMode:G="spec",passFailDisplay:Q,showPartialDies:ot=!0}=a,Mt=Q??"off",St=i?.hbinDefs,kt=i?.sbinDefs,$t=i?.metadataFields,_t=$t?.find(l=>l.key===I),wt={x:(k?.x??!1)!==(h?.flipX??!1),y:(k?.y??!1)!==(h?.flipY??!1)},Wt=St?new Map(St.map(l=>[l.bin,l])):null,jt=kt?new Map(kt.map(l=>[l.bin,l])):null,rt=n==="softBin"?jt:Wt,lt=se(b),zt=new Set(v),Bt={forValue:de(lt.forValue),forBin:b==="custom"&&rt?l=>rt.get(l)?.color??lt.forBin(l):lt.forBin};let{testNumber:H}=resolveTestNumber(M,m);if(!m?.length&&n==="value"&&!e.some(r=>r.testValues&&H in r.testValues)){const r=e.reduce((g,V)=>{if(!V.testValues)return g;const $=Object.keys(V.testValues).map(Number),E=$.length?Math.min(...$):void 0;return E!==void 0&&(g===void 0||E<g)?E:g},void 0);r!==void 0&&(H=r)}let U;if(Array.isArray(S))U=S;else if(S){const{testNumber:l}=resolveTestNumber(S.test,m);U=l===H?S.range:void 0}const F=findTestDef(m,H),vt=n==="value"&&F!==void 0&&!gt(F),Kt=F!==void 0&&(F.limitLow!==void 0||F.limitHigh!==void 0);let O="off";n==="value"&&(vt?O="test":Mt==="spec"&&Kt?O="spec":Mt==="test"&&e.some(l=>Y(l,H,F)!==void 0)&&(O="test"));const Tt=O==="spec"?"spec":G,ct=n==="stackedValues"||n==="stackedBins"||n==="stackedSoftBins";let C,A,J=!1;if(U){[C,A]=U;let l=!0,r=!1;for(const g of e){const V=ct?P(g,0):P(g,H);if(V!==void 0&&(r=!0,!Number.isInteger(V))){l=!1;break}}J=r&&l}else{const l=!ct&&n==="value"&&Tt==="spec"&&F&&(F.limitLow!==void 0||F.limitHigh!==void 0);let r=1/0,g=-1/0,V=!0;for(const $ of e){const E=ct?P($,0):P($,H);E!==void 0&&(E<r&&(r=E),E>g&&(g=E),Number.isInteger(E)||(V=!1))}isFinite(r)||(V=!1),J=V,l?(C=F.limitLow!==void 0?F.limitLow:isFinite(r)?r:0,A=F.limitHigh!==void 0?F.limitHigh:isFinite(g)?g:1):(C=isFinite(r)?r:0,A=isFinite(g)?g:1)}C===A&&(A=C+1);const It=vt?!1:_??F?.logScale??!1,Ft=It&&C>0&&A>0,qt=Ft;let Z;if(Ft){const l=Math.log10(C),r=Math.log10(A)-l;Z=g=>g<=0?0:Nt((Math.log10(g)-l)/r)}else Z=l=>Nt((l-C)/(A-C));const L=ue(t,k,h);let Vt=null;if(t.notch){const l=Et(t.notch.type),r=mt(L.physicalToScreen,Math.cos(l)*t.radius,Math.sin(l)*t.radius),g=Math.hypot(r.x,r.y);g>0&&(Vt={x:r.x/g,y:r.y/g})}let z=1/0;for(let l=0;l<e.length;l++){const r=e[l];r.width<z&&(z=r.width),r.height<z&&(z=r.height)}const Xt=z!==1/0?Math.min(T,z*.12):T,Lt=[],ut=[],Yt=L.bakedToScreen.a===1&&L.bakedToScreen.b===0&&L.bakedToScreen.c===0&&L.bakedToScreen.d===1&&L.bakedToScreen.e===0&&L.bakedToScreen.f===0;let w=null;if(!Yt){w=new Float64Array(e.length*2);for(let l=0;l<e.length;l++){const r=q(L.bakedToScreen,e[l].physX,e[l].physY);w[l*2]=r.x,w[l*2+1]=r.y}}const Gt=ae(L.gridToScreen),Qt=n==="hardBin"||n==="softBin"||n==="stackedBins"||n==="stackedSoftBins",Ut=n==="softBin"||n==="stackedSoftBins";let tt=Qt?new Map:void 0;const ft=n==="value"&&O==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,dt=n==="value"&&O==="test"?{pass:0,fail:0}:void 0,Dt=n==="metadata";let et=Dt?new Map:void 0,nt=null;if(Dt&&I){const l=new Set;for(const g of e){if(g.partial||g.edgeExcluded)continue;const V=it(g,I);V!==void 0&&l.add(V)}const r=[...l].sort(re);nt=new Map(r.map((g,V)=>[g,_t?.values?.find($=>$.value===g)?.color??oe(V)]))}for(let l=0;l<e.length;l++){const r=e[l],g=w?w[l*2]:r.physX,V=w?w[l*2+1]:r.physY;if(ut.push({x:g,y:V}),tt&&!r.partial&&!r.edgeExcluded){const $=Ut?r.sbin:r.hbin;$!=null&&tt.set($,(tt.get($)??0)+1)}if(ft&&!r.partial&&!r.edgeExcluded){const $=classifySpec(P(r,H),F);$&&ft[$]++}if(dt&&!r.partial&&!r.edgeExcluded){const $=Y(r,H,F);$!==void 0&&dt[$?"pass":"fail"]++}if(et&&!r.partial&&!r.edgeExcluded){const $=it(r,I);$!==void 0&&et.set($,(et.get($)??0)+1)}r.partial&&!ot||Me(Lt,r,g,V,n,Gt,Xt,Bt,f,Z,H,rt,zt,F,O,I,nt,d)}const Pt=o?generateTextOverlay(e,w,{plotMode:n,colorFns:Bt,normalize:Z,activeTest:M,valueRange:[C,A],testDefs:m,fallbackFormat:N,passFailDisplay:O,activeMetadataKey:I,metadataColorMap:nt,allIntegerValues:J}):[],K=pe(t,L.physicalToScreen);p&&K.push(...ye(t,L.physicalToScreen,y)),B&&K.push(...ge(t,L.physicalToScreen,t.center.x,t.center.y)),c&&K.push(...be(s,L.gridToScreen)),u&&K.push(...xe(e)),D&&K.push(...Se(t,L.gridToScreen,Pt));const Jt=ut.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:Lt,hoverPoints:ut,texts:Pt,overlays:K,plotMode:n,colorScheme:b,metadata:t.metadata??null,dies:e,valueRange:[C,A],allIntegerValues:J,testDefs:m,activeTest:M,logScale:qt,logScaleRequested:It,aggrMethod:x,lotSize:st,axisFlip:wt,dataAxisFlip:{x:k?.x??!1,y:k?.y??!1},gridToScreen:L.gridToScreen,rotation:((t.orientation+(h?.rotation??0))%360+360)%360,hasReticle:s.length>0,isLotStack:R,passFailDisplay:O,colorBySpec:O==="spec",colorbarRangeMode:Tt,waferCenter:t.center,waferRadius:t.radius,notchDir:Vt,binCounts:tt,metadataCounts:et,metadataColorMap:nt,activeMetadataKey:I,metadataFields:$t,specCounts:ft,passFailCounts:dt,dieBounds:Jt}}export{getDieKey}from"../core/dies.js";
|