@wafertools/wafermap 0.30.1 → 0.30.2
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 +59 -10
- package/CHANGELOG.md +80 -8
- package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
- package/dist/packages/canvas-adapter/charts/chartShell.js +1 -1
- package/dist/packages/canvas-adapter/charts/correlation.js +2 -2
- package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
- package/dist/packages/canvas-adapter/charts/scatter.js +1 -1
- package/dist/packages/canvas-adapter/chunked.d.ts +45 -0
- package/dist/packages/canvas-adapter/chunked.js +1 -0
- package/dist/packages/canvas-adapter/insightsTab.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +55 -0
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +55 -9
- package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
- package/dist/packages/canvas-adapter/toCanvas.js +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.js +1 -1
- 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/utils.d.ts +55 -0
- package/dist/packages/core/utils.js +1 -1
- package/dist/packages/renderer/binColors.d.ts +14 -0
- package/dist/packages/renderer/binColors.js +1 -1
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/capability.d.ts +56 -0
- package/dist/packages/stats/capability.js +1 -1
- package/dist/packages/stats/correlation.d.ts +30 -0
- package/dist/packages/stats/correlation.js +1 -1
- package/dist/packages/stats/findingsNarrative.js +1 -1
- package/dist/packages/stats/math.d.ts +27 -2
- package/dist/packages/stats/math.js +1 -1
- package/dist/packages/stats/renderSummaryReport.js +4 -4
- package/llms.txt +12 -7
- package/package.json +2 -2
|
@@ -12,6 +12,7 @@ import { type CapabilityItem } from '../stats/capability.js';
|
|
|
12
12
|
import { type SaveTextHandler } from './toolbar.js';
|
|
13
13
|
import { type DieListDisplayOptions } from './dieList.js';
|
|
14
14
|
export { csvField } from '../core/utils.js';
|
|
15
|
+
import { type Chunked } from '../core/utils.js';
|
|
15
16
|
import type { WaferMetadata } from '../core/metadata.js';
|
|
16
17
|
export interface SummaryPanelOptions {
|
|
17
18
|
/** Which side of the content area to place the panel. Default 'right'. */
|
|
@@ -221,7 +222,20 @@ export interface CsvExportContext {
|
|
|
221
222
|
* section title and emitted as a `Population` CSV column. */
|
|
222
223
|
populationLabel?: string;
|
|
223
224
|
}
|
|
224
|
-
|
|
225
|
+
/**
|
|
226
|
+
* {@link buildTestSectionSteps}, run straight through — the entry for every
|
|
227
|
+
* caller that is not staging its render across tasks.
|
|
228
|
+
*/
|
|
229
|
+
export declare function buildTestSection(...args: Parameters<typeof buildTestSectionSteps>): HTMLDivElement | null;
|
|
230
|
+
/**
|
|
231
|
+
* @internal The Test Values table as a {@link Chunked} computation: one step per
|
|
232
|
+
* test for the descriptive statistics, plus the steps of the capability pass it
|
|
233
|
+
* delegates to. This is the single largest piece of work in either summary
|
|
234
|
+
* panel — at lot scale it scans the pooled dies once per test and sorts every
|
|
235
|
+
* test's values — so it is the one that has to be interruptible for the panel
|
|
236
|
+
* not to block the main thread for seconds at a time.
|
|
237
|
+
*/
|
|
238
|
+
export declare function buildTestSectionSteps(dies: Die[], testDefs: TestDef[] | undefined, fallbackFormat?: 'si' | 'engineering',
|
|
225
239
|
/**
|
|
226
240
|
* When supplied, each test's descriptive stats and spec-yield are read from
|
|
227
241
|
* `.perTestStats`/`.testSpecYield` (already computed once by
|
|
@@ -274,7 +288,7 @@ panel?: HTMLElement,
|
|
|
274
288
|
* information for no benefit. The CSV export always carries every column
|
|
275
289
|
* regardless.
|
|
276
290
|
*/
|
|
277
|
-
columns?: 'compact' | 'full'): HTMLDivElement | null
|
|
291
|
+
columns?: 'compact' | 'full'): Chunked<HTMLDivElement | null>;
|
|
278
292
|
/**
|
|
279
293
|
* "Functional Tests" section — one row per functional (`testType: 'F'`) test
|
|
280
294
|
* with pass/fail counts and pass rate. The functional counterpart of
|
|
@@ -368,7 +382,11 @@ notice?: FindingsNotice): HTMLDivElement | null;
|
|
|
368
382
|
* they aren't reconstructable from per-wafer quartiles alone — but
|
|
369
383
|
* `buildTestSection`'s display doesn't need them.)
|
|
370
384
|
*/
|
|
371
|
-
|
|
385
|
+
/** {@link buildLotTestSectionSteps}, run straight through. */
|
|
386
|
+
export declare function buildLotTestSection(...args: Parameters<typeof buildLotTestSectionSteps>): HTMLDivElement | null;
|
|
387
|
+
/** @internal {@link buildLotTestSection} as a {@link Chunked} computation —
|
|
388
|
+
* see {@link buildTestSectionSteps}, which does the work. */
|
|
389
|
+
export declare function buildLotTestSectionSteps(allDies: Die[], testDefs: TestDef[] | undefined, fallbackFormat?: 'si' | 'engineering', perWaferSummaries?: StatsSummary[],
|
|
372
390
|
/** Optional host hook for the "Export CSV" button — see `saveTextFile` (toolbar.ts). */
|
|
373
391
|
onSaveText?: SaveTextHandler,
|
|
374
392
|
/**
|
|
@@ -379,7 +397,7 @@ capabilityItems?: CapabilityItem[],
|
|
|
379
397
|
/** Panel element owning the collapsed state. Omit for a stateless render. */
|
|
380
398
|
panel?: HTMLElement,
|
|
381
399
|
/** See `buildTestSection`'s `columns`. Defaults to the full set. */
|
|
382
|
-
columns?: 'compact' | 'full'): HTMLDivElement | null
|
|
400
|
+
columns?: 'compact' | 'full'): Chunked<HTMLDivElement | null>;
|
|
383
401
|
export declare function createSummaryPanelEl(placement: 'right' | 'left' | 'top' | 'bottom',
|
|
384
402
|
/**
|
|
385
403
|
* Inset from the edge this panel docks against — stated by the caller,
|
|
@@ -403,8 +421,22 @@ export declare function createSummaryPanelEl(placement: 'right' | 'left' | 'top'
|
|
|
403
421
|
edgeInset: string, ownerDocument?: Document): HTMLDivElement;
|
|
404
422
|
/** Wrap the canvas + panel in a flex container according to placement. */
|
|
405
423
|
export declare function wrapWithSummaryPanel(content: HTMLElement, panel: HTMLDivElement, placement: 'right' | 'left' | 'top' | 'bottom'): HTMLDivElement;
|
|
406
|
-
/**
|
|
407
|
-
|
|
424
|
+
/**
|
|
425
|
+
* {@link renderWaferSummaryContentSteps}, run straight through — the entry for a
|
|
426
|
+
* caller that does not stage its render.
|
|
427
|
+
*/
|
|
428
|
+
export declare function renderWaferSummaryContent(...args: Parameters<typeof renderWaferSummaryContentSteps>): void;
|
|
429
|
+
/**
|
|
430
|
+
* @internal Render all wafer-level sections into a panel element, as a
|
|
431
|
+
* {@link Chunked} computation. Clears existing content.
|
|
432
|
+
*
|
|
433
|
+
* Chunked for the same reason the lot panel is: the Test Values table walks
|
|
434
|
+
* every die once per test, which on a single 400,000-die wafer is seconds of
|
|
435
|
+
* uninterruptible work on the main thread. Sections are appended as they are
|
|
436
|
+
* built, so the panel fills in. The synchronous entry above is this drained —
|
|
437
|
+
* one implementation, not a second progressive copy.
|
|
438
|
+
*/
|
|
439
|
+
export declare function renderWaferSummaryContentSteps(panel: HTMLDivElement, params: {
|
|
408
440
|
wafer: Wafer;
|
|
409
441
|
dies: Die[];
|
|
410
442
|
yieldSummary?: YieldSummary;
|
|
@@ -461,7 +493,7 @@ export declare function renderWaferSummaryContent(panel: HTMLDivElement, params:
|
|
|
461
493
|
* authoritative in neither place, is the thing being avoided in both cases.
|
|
462
494
|
*/
|
|
463
495
|
metadataShownElsewhere?: boolean;
|
|
464
|
-
}): void
|
|
496
|
+
}): Chunked<void>;
|
|
465
497
|
/** Render lot-level content into the panel. Clears existing content. */
|
|
466
498
|
/** A gallery item as the lot report reads it — every field optional, since a slot may not have loaded. */
|
|
467
499
|
type ReportItem = {
|
|
@@ -482,7 +514,21 @@ type ReportItem = {
|
|
|
482
514
|
* as the cards do. `passBins`/`ringCount` fill in only for an item built without them.
|
|
483
515
|
*/
|
|
484
516
|
export declare function reportMapsFromItems(items: ReadonlyArray<ReportItem | null | undefined>, passBins: readonly number[], ringCount: number): ReportMap[];
|
|
485
|
-
|
|
517
|
+
/**
|
|
518
|
+
* {@link renderLotSummaryContentSteps}, run straight through — the entry for a
|
|
519
|
+
* caller that is not staging the render across tasks.
|
|
520
|
+
*/
|
|
521
|
+
export declare function renderLotSummaryContent(...args: Parameters<typeof renderLotSummaryContentSteps>): void;
|
|
522
|
+
/**
|
|
523
|
+
* @internal The lot summary panel as a {@link Chunked} computation: it pools
|
|
524
|
+
* every die of every wafer and derives bin, region and per-test sections from
|
|
525
|
+
* that pool, which at lot scale is the longest single piece of work anywhere in
|
|
526
|
+
* this library — 15.3 s on a 50-wafer lot of 4,000 dies x 100 tests, enough for
|
|
527
|
+
* the browser to offer to kill the page. Each step is one section, or one test
|
|
528
|
+
* within the Test Values table, and each section is appended as it is built, so
|
|
529
|
+
* the panel fills in rather than arriving at once.
|
|
530
|
+
*/
|
|
531
|
+
export declare function renderLotSummaryContentSteps(panel: HTMLDivElement, params: {
|
|
486
532
|
lotSummary: LotStatsSummary;
|
|
487
533
|
/** `passBins` on an item is that wafer's own (`WaferMapResult.passBins`) and wins over the top-level fallback. */
|
|
488
534
|
items: Array<ReportItem & {
|
|
@@ -517,5 +563,5 @@ export declare function renderLotSummaryContent(panel: HTMLDivElement, params: {
|
|
|
517
563
|
unusual: number;
|
|
518
564
|
notable: number;
|
|
519
565
|
} | undefined;
|
|
520
|
-
}): void
|
|
566
|
+
}): Chunked<void>;
|
|
521
567
|
//# sourceMappingURL=summaryPanel.d.ts.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{waferDisplayLabel as me}from"../core/waferLabel.js";import{itemPassBins as he,passBinsLabel as Ke}from"../core/passBins.js";import{isParametricTest as Ge}from"../renderer/buildWaferMap.js";import{buildRingRegions as Ue,buildQuadrantRegions as Qe,buildRegionYieldData as Xe}from"../stats/regions.js";import{computeFunctionalYield as Ze}from"../stats/analyzeWaferMap.js";import{renderWaferReportHtml as Je,renderLotReportHtml as et}from"../stats/renderSummaryReport.js";import{buildFindingsNarrative as tt}from"../stats/findingsNarrative.js";import{filterFindings as nt}from"../stats/filterFindings.js";import{buildFacetTable as ot,prettyKey as Ce}from"../stats/facets.js";import{commonMetadata as we}from"../stats/facets.js";import{resolveMetadataColumns as it}from"../stats/metadataColumns.js";import{resolveBinColors as Se,resolveBinColorsByWafer as st}from"../renderer/binColors.js";import{NO_DATA_FILL as at}from"../renderer/colorMap.js";import{describeWaferPopulation as ve,populationLabel as rt}from"../stats/population.js";import{buildWarningsBanner as ge,collectWarnings as $e}from"./warnings.js";export{ge as buildWarningsBanner};import{fmt as ke,fmtAggregationMethod as lt,plainBinTerms as ne}from"../renderer/fmt.js";import{getUniqueTestNumbers as dt}from"../renderer/buildView.js";import{quantile as oe}from"../stats/math.js";import{buildCapabilityData as ct}from"../stats/capability.js";import{sortBinsForDisplay as pt}from"../stats/binPareto.js";import{poolFunctionalYield as ut}from"../stats/testPassRate.js";import{makeLabeledSelect as Be,makeSegmented as be}from"./charts/chartShell.js";import{SHADOW as Le,MOTION as Ne,LEADING as Q,TRACKING as re,wireControlHover as q,controlStyle as le,SPACE as y,RADIUS as _,FONT as E,CLR as B,sevColor as de,openModal as Fe,openReportModal as Ee,saveTextFile as De,wireTooltip as ie}from"./toolbar.js";import{buildDieListSection as ft}from"./dieList.js";export{csvField}from"../core/utils.js";import{medianOfSorted as mt,csvField as ce}from"../core/utils.js";import{metadataDisplayValue as ht}from"../core/metadata.js";const gt=B.panelBg,Re=`1px solid ${B.menuBorder}`,bt="12px",Y=B.label,K=B.value,Te="10px";function l(e,t,o,n=document){const i=n.createElement(e);return t&&Object.assign(i.style,t),o!==void 0&&(i.textContent=o),i}function J(e){return l("div",{fontSize:Te,fontWeight:"700",letterSpacing:re,textTransform:"uppercase",color:Y,marginBottom:y.sm},e)}const Me=new WeakMap,Ie=["regionYield","testValues","functionalTests"];function pe(e){if(!e)return{collapsed:new Set(Ie),regionMode:"ring",waferSort:"slot"};let t=Me.get(e);return t||(t={collapsed:new Set(Ie),regionMode:"ring",waferSort:"slot"},Me.set(e,t)),t}function X(e,t=!0,o,n){const i=l("div"),d=pe(n?.panel);n?.stateKey&&n.panel&&(t=!d.collapsed.has(n.stateKey));const s=document.createElement("button");s.type="button",Object.assign(s.style,{display:"flex",alignItems:"center",gap:y.xs,width:"100%",background:"none",border:"none",cursor:"pointer",padding:"0 0 6px",textAlign:"left"});const p=l("span",{fontSize:E.meta,color:Y,transition:`transform ${Ne.base}`,transform:t?"rotate(90deg)":"rotate(0deg)",display:"inline-block",lineHeight:Q.none,marginRight:"1px"},"\u25B6"),c=l("span",{fontSize:Te,fontWeight:"700",letterSpacing:re,textTransform:"uppercase",color:Y,flex:"1"},e);if(q(s,"bare"),s.setAttribute("aria-expanded",t?"true":"false"),s.appendChild(p),s.appendChild(c),o){const b=l("span",{fontSize:E.meta,fontWeight:"700",background:B.warnBg,color:B.warnText,borderRadius:_.pill,padding:"1px 5px"},o);s.appendChild(b)}const r=l("div");r.style.display=t?"block":"none";let a=t;if(s.addEventListener("click",()=>{a=!a,r.style.display=a?"block":"none",p.style.transform=a?"rotate(90deg)":"rotate(0deg)",s.setAttribute("aria-expanded",a?"true":"false"),n?.stateKey&&n.panel&&(a?d.collapsed.delete(n.stateKey):d.collapsed.add(n.stateKey))}),n?.control){const b=l("div",{display:"flex",alignItems:"center",gap:y.sm});s.style.width="auto",s.style.flex="1",b.appendChild(s);const u=()=>{r.innerHTML="",n.render?.(r)},S=n.control(u);S&&(S.style.marginBottom="6px",S.addEventListener("click",N=>N.stopPropagation()),b.appendChild(S)),i.appendChild(b)}else i.appendChild(s);return i.appendChild(r),n?.render?.(r),{outer:i,content:r}}function Ae(){return l("div",{height:"1px",background:B.separator,margin:`${bt} 0`,flexShrink:"0"})}function xe(e,t,o=B.barFill,n,i){const d=l("div",{marginBottom:"5px"}),s=l("div",{display:"flex",justifyContent:"space-between",fontSize:E.body,color:K,marginBottom:y.xxs}),p=l("span",{color:Y},e),c=l("span",{fontWeight:"600"},`${t.toFixed(1)}%`);s.appendChild(p),s.appendChild(c);const r=n!==void 0?n:Math.min(100,Math.max(0,t)),a=o,b=l("div",{position:"relative",height:"9px",background:B.bgActive,borderRadius:_.control,overflow:"hidden"}),u=l("div",{height:"100%",width:`${r}%`,background:a,borderRadius:_.control,transition:`width ${Ne.base}`});if(b.appendChild(u),i!==void 0){const S=l("div",{position:"absolute",top:"0",bottom:"0",left:`${i}%`,width:"2px",marginLeft:"-1px",background:B.value,opacity:"0.8"});b.appendChild(S)}return d.appendChild(s),d.appendChild(b),d}function We(){const e=l("div",{display:"flex",gap:y.sm,marginBottom:y.md});return e.finish=()=>{const t=e.lastElementChild;t&&Object.assign(t.style,{borderRight:"none",paddingRight:"0"})},e}function se(e,t,o){const n=l("div",{padding:`${y.xxs} ${y.md} ${y.xxs} 0`,borderRight:Re,textAlign:"center",flex:"1"}),i=l("div",{fontSize:E.heading,fontWeight:"700",color:K,lineHeight:Q.tight},e),d=l("div",{fontSize:E.body,color:Y,marginTop:y.xxs},t);return n.appendChild(i),n.appendChild(d),o&&n.appendChild(l("div",{fontSize:E.body,color:Y,opacity:"0.85"},o)),n}function Pe(e,t){const o=l("div",{display:"flex",justifyContent:"space-between",fontSize:E.body,gap:y.md,marginBottom:"3px"}),n=l("span",{color:Y,flexShrink:"0"},e),i=l("span",{color:K,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},t);return o.appendChild(n),o.appendChild(i),o}function xt(e,t){const o=l("div",{display:"flex",fontSize:E.body,gap:y.sm,marginBottom:"3px"}),n=l("span",{color:Y,flexShrink:"0"},`${e}:`),i=l("span",{color:K,fontWeight:"500",wordBreak:"break-all"},t);return o.appendChild(n),o.appendChild(i),o}export function metadataEntries(e){const t=[];for(const[o,n]of Object.entries(e)){const i=ht(n);i!==void 0&&t.push([o,i])}return t}const yt=["lot","product","testprogram","split"],ue=e=>{const t=yt.indexOf(e.toLowerCase().replace(/[^a-z]/g,""));return t===-1?Number.MAX_SAFE_INTEGER:t};export function buildFacetSummaryChips(e,t=3){if(!e.length)return null;const o=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:`${y.xs} ${y.lg}`}),n=[...e].sort((p,c)=>ue(p.key)-ue(c.key)),i=n.filter(p=>ue(p.key)!==Number.MAX_SAFE_INTEGER),d=n.filter(p=>ue(p.key)===Number.MAX_SAFE_INTEGER),s=p=>{const c=l("span",{whiteSpace:"nowrap"}),r=l("span",{color:Y},`${Ce(p.key)}: `);c.appendChild(r);const a=p.values.slice(0,t),b=p.values.length-a.length;let u=a.map(S=>S.value).join(", ");return b>0&&(u+=` +${b} more`),c.appendChild(document.createTextNode(u)),c};for(const p of i)o.appendChild(s(p));if(d.length){const p=l("span",{display:"none",flexWrap:"wrap",alignItems:"baseline",gap:`${y.xs} ${y.lg}`});for(const a of d)p.appendChild(s(a));const c=l("button",{background:"none",border:"none",padding:"0",cursor:"pointer",color:B.iconActive,fontSize:E.body,whiteSpace:"nowrap"});c.type="button",q(c,"bare");const r=a=>{p.style.display=a?"contents":"none",c.textContent=a?"less":`+${d.length} more`,c.setAttribute("aria-expanded",String(a))};r(!1),c.addEventListener("click",()=>r(c.getAttribute("aria-expanded")!=="true")),o.appendChild(p),o.appendChild(c)}return o}export function buildMetadataStripRow(e,t,o){const n=ot(e,o),i=buildFacetSummaryChips(n);if(!i&&!t)return null;const d=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 14px"});if(t){const s=l("span",{fontWeight:"500",whiteSpace:"nowrap"},t.aggrMethod?`${t.lotSize} wafers stacked \xB7 ${t.aggrMethod}`:`${t.lotSize} wafers stacked`);d.appendChild(s)}return i&&d.appendChild(i),d}const Ct={display:"flex",flexDirection:"column",gap:y.sm,background:B.menuBg,border:`1px solid ${B.menuBorder}`,borderRadius:_.container,padding:`${y.sm} ${y.lg}`,boxShadow:Le.panel,fontSize:E.body,lineHeight:Q.none,boxSizing:"border-box",width:"100%",minWidth:"0"};export function buildMetadataStripBox(e,t,o){const n=buildMetadataStripRow(e,t,o);if(!n)return null;const i=l("div",Ct);return i.appendChild(n),i}export function buildCompactMetadataRows(e){const t=metadataEntries(e);if(!t.length)return null;const o=l("div");for(const[n,i]of t)o.appendChild(xt(Ce(n),i));return o}export function buildMetadataInfoSection(e,t){const o=buildMetadataStripRow(e,t,{facetableOnly:e.length>1});if(!o)return null;const n=l("div");return n.appendChild(J("Wafer Info")),n.appendChild(o),n}export function buildYieldSection(e,t,o=[1]){const n=l("div");n.appendChild(J("Summary"));const i=We();if(i.appendChild(se(String(e.totalDies),"Total dies")),e.partialDies>0&&i.appendChild(se(String(e.partialDies),"Partial")),e.yieldPercent!==null){const d=Ke([o]);i.appendChild(se(`${e.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${d}`))}return i.finish(),n.appendChild(i),e.edgeExcludedDies>0&&n.appendChild(Pe("Edge excluded (outer zone)",String(e.edgeExcludedDies))),n}export function buildBinSection(e,t,o,n,i,d){const s=new Map;if(i)for(const[r,a]of Object.entries(i))s.set(Number(r),a);else for(const r of e){if(r.partial||r.edgeExcluded)continue;const a=o==="hard"?r.hbin:r.sbin;a!=null&&s.set(a,(s.get(a)??0)+1)}if(!s.size)return null;const p=n??Se(e,{passBins:d,...o==="hard"?{hbinDefs:t}:{sbinDefs:t}}),c=l("div");c.appendChild(J(ze(o,[...s.values()].reduce((r,a)=>r+a,0))));for(const r of He(s,t,p[o],p.pass[o]))c.appendChild(r);return c}function ze(e,t){return`${e==="hard"?"Hard":"Soft"} Bin Breakdown \u2014 % of dies (N=${t})`}function He(e,t,o,n){const i=[...e.values()].reduce((p,c)=>p+c,0),d=t?new Map(t.map(p=>[p.bin,p])):null;return pt(e.entries(),n).map(([p,c])=>{const r=d?.get(p),a=r?.name?`Bin ${p} \xB7 ${r.name}`:`Bin ${p}`,b=c/i*100,u=o.get(p)??at;return xe(`${a} (${c})`,b,u)})}export function buildBinBreakdownSection(e){const{dies:t,hbinDefs:o,sbinDefs:n,hardCounts:i,softCounts:d,plotMode:s,passBins:p=[1],panel:c}=e,r=e.binColors??Se(t,{passBins:p,hbinDefs:o,sbinDefs:n}),a=m=>{const x=m==="hard"?i:d,C=new Map;if(x){for(const[v,k]of Object.entries(x))C.set(Number(v),k);return C}for(const v of t){if(v.partial||v.edgeExcluded)continue;const k=m==="hard"?v.hbin:v.sbin;k!=null&&C.set(k,(C.get(k)??0)+1)}return C},b=a("hard"),u=a("soft"),S=b.size>0,N=u.size>0;if(!S&&!N)return null;const A=pe(c),P=s==="softBin"||s==="stackedSoftBins"?"soft":s==="hardBin"||s==="stackedBins"?"hard":void 0;let R=A.binMode??P??(S?"hard":"soft");R==="hard"&&!S&&(R="soft"),R==="soft"&&!N&&(R="hard");const H=R==="hard"?b:u,h=R==="hard"?o:n,f=[...H.values()].reduce((m,x)=>m+x,0),{outer:w}=X(ze(R,f),!0,void 0,{stateKey:"bins",panel:c,render:m=>{const x=R==="hard"?r.hard:r.soft;for(const C of He(H,h,x,r.pass[R]))m.appendChild(C)},control:S&&N&&c?()=>be([["hard","Hard"],["soft","Soft"]],R,m=>{A.binMode=m;const x=buildBinBreakdownSection(e);x&&w.replaceWith(x)},c.ownerDocument,!0):void 0});return w}export function buildLotBinSection(e,t,o,n){return buildBinSection(e,t,o,n)}export function buildRegionYieldPanelSection(e){const{diesByWafer:t,allWafers:o,ringCount:n,passBins:i,panel:d}=e,s=pe(d),p=a=>Xe(t,o,n,i,a==="ring"?Ue:Qe);if(!p("ring").length&&!p("quadrant").length)return null;const c=s.regionMode,{outer:r}=X(c==="ring"?"Ring Yield":"Quadrant Yield",!0,void 0,{stateKey:"regionYield",panel:d,render:a=>{const b=p(c);if(!b.length){a.appendChild(l("div",{fontSize:E.body,color:Y},"No positioned dies for this breakdown."));return}for(const{label:u,n:S,yieldPercent:N}of b)a.appendChild(xe(`${u} (N=${S})`,N))},control:d?()=>be([["ring","Ring"],["quadrant","Quadrant"]],c,a=>{s.regionMode=a;const b=buildRegionYieldPanelSection(e);b&&r.replaceWith(b)},d.ownerDocument,!0):void 0});return r}export function buildLotOverviewSection(e,t=[]){const o=l("div"),n=ve(e.perWafer.map(s=>s.summary.wafer));o.appendChild(J(n.lotId!==void 0?"Lot Summary":"Summary"));const i=We();i.appendChild(se(String(e.stats.waferCount),"Wafers"));const d=e.perWafer.map(s=>s.summary.stats.yieldPercent).filter(s=>s!==null);if(d.length){const s=d.reduce((p,c)=>p+c,0)/d.length;i.appendChild(se(`${s.toFixed(1)}%`,"Mean per-wafer yield"))}if(i.finish(),o.appendChild(i),t.length){const s=t.reduce((c,r)=>c+r.stats.analyzedDies,0),p=t.reduce((c,r)=>c+r.stats.excludedDies,0);(s||p)&&o.appendChild(Pe("Dies analysed",p?`${s.toLocaleString()} (${p.toLocaleString()} excluded)`:s.toLocaleString()))}return o}export function buildPerWaferYieldSection(e,t,o,n,i){const d=e.perWafer.map(f=>({waferIndex:f.waferIndex,label:me(t[f.waferIndex],f.waferIndex).replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPct:f.summary.stats.yieldPercent})).filter(f=>f.yieldPct!==null);if(!d.length)return null;const s=Math.min(...d.map(f=>f.yieldPct)),p=Math.max(...d.map(f=>f.yieldPct)),c=s===p?"":` (${s.toFixed(1)}\u2013${p.toFixed(1)}%)`,r=[...d.map(f=>f.yieldPct)].sort((f,w)=>f-w),a=mt(r),b=oe(r,.25),u=oe(r,.75),S=d.length>=5?b-1.5*(u-b):-1/0,N=pe(n),A=f=>{const w=N.waferSort==="yield"?[...d].sort((m,x)=>m.yieldPct-x.yieldPct):d;for(const{waferIndex:m,label:x,yieldPct:C}of w){const k=C<S?`${x} \xB7 low outlier`:x,T=xe(k,C,void 0,void 0,a),M=i?.(m);M?.total&&H(T,M),h(T,m,x,C,M),f.appendChild(T)}},P=` \xB7 median ${a.toFixed(1)}%`,{outer:R}=X("Wafer Yield"+c+P,!0,void 0,{stateKey:"waferYield",panel:n,render:A,control:n?f=>be([["slot","Slot"],["yield","Yield"]],N.waferSort,w=>{N.waferSort=w,f()},n.ownerDocument,!0):void 0});return R;function H(f,w){const m=w.unusual?"unusual":w.notable?"notable":"info",x=l("span",{marginLeft:y.sm,flexShrink:"0",background:de(m),color:"#fff",borderRadius:_.container,padding:"0 5px",fontSize:E.body,fontWeight:"600"},`${w.total}`);x.setAttribute("aria-hidden","true"),ie(x,`${w.total} finding${w.total===1?"":"s"}`+(w.unusual?`, ${w.unusual} unusual`:"")+(w.notable?`, ${w.notable} notable`:"")),f.firstElementChild?.firstElementChild?.appendChild(x)}function h(f,w,m,x,C){o&&(f.style.cursor="pointer",f.style.borderRadius="4px",f.style.padding="2px 3px",f.style.marginLeft="-3px",f.style.marginRight="-3px",f.setAttribute("role","button"),f.setAttribute("aria-label",`${m}, ${x.toFixed(1)}% yield`+(C?.total?`, ${C.total} finding${C.total===1?"":"s"}${C.unusual?`, ${C.unusual} unusual`:""}`:"")+" \u2014 open wafer"),f.tabIndex=0,f.addEventListener("mouseenter",()=>{f.style.background=B.bgHover}),f.addEventListener("mouseleave",()=>{f.style.background=""}),f.addEventListener("focus",()=>{f.style.background=B.bgHover}),f.addEventListener("blur",()=>{f.style.background=""}),f.addEventListener("click",()=>o(w)),f.addEventListener("keydown",v=>{(v.key==="Enter"||v.key===" ")&&(v.preventDefault(),o(w))}))}}function wt(e){const t=[...e].sort((i,d)=>i-d),o=e.reduce((i,d)=>i+d,0)/e.length,n=e.reduce((i,d)=>i+(d-o)**2,0)/e.length;return{min:t[0],max:t[t.length-1],mean:o,count:e.length,stddev:Math.sqrt(n),median:oe(t,.5),q1:oe(t,.25),q3:oe(t,.75)}}function Ye(e,t){if(!e)return[];const o=e.waferMetadata??(e.perWaferMetadata?we(e.perWaferMetadata.map(n=>({metadata:n}))):void 0);return!o||!Object.keys(o).length?[]:it({waferMetadata:o,waferKeys:"identity",waferPlacement:"csv",reservedLabels:t}).columns}function Oe(e){return e?.populationLabel?[{key:"population",label:"Population",scope:"wafer",constant:e.populationLabel,get:()=>e.populationLabel,csvOnly:!0}]:[]}export function buildTestSection(e,t,o,n,i,d,s,p,c="full"){const r=e.filter(g=>!g.partial&&!g.edgeExcluded),a=new Map((n?.perTestStats??[]).map(g=>[g.testNumber,g])),b=new Map((n?.testSpecYield??[]).map(g=>[g.testNumber,g]));let u;if(t?.length?u=t.filter(Ge).map(g=>({testNumber:g.testNumber,name:g.name,unit:g.unit,limitLow:g.limitLow,limitHigh:g.limitHigh})):u=dt(r).map(F=>({testNumber:F,name:`Test ${F}`})),!u.length)return null;const S=u.filter(g=>r.some(F=>{const O=F.testValues?.[g.testNumber];return O!==void 0&&isFinite(O)}));if(!S.length)return null;const N=S.some(g=>g.limitLow!==void 0||g.limitHigh!==void 0),A=[];for(const g of S){const F=a.get(g.testNumber),O=F&&F.stddev!==void 0&&F.median!==void 0&&F.q1!==void 0&&F.q3!==void 0;let L;if(O){const $=F;L={testNumber:g.testNumber,min:$.min,max:$.max,mean:$.mean,count:$.count,stddev:$.stddev,median:$.median,q1:$.q1,q3:$.q3}}else{const $=r.map(D=>D.testValues?.[g.testNumber]).filter(D=>D!==void 0&&isFinite(D));if(!$.length)continue;L={testNumber:g.testNumber,...wt($)}}let I=null,z=0;if(g.limitLow!==void 0||g.limitHigh!==void 0){const $=b.get(g.testNumber);if($)({yieldPercent:I,totalDies:z}=$);else{const D=r.map(j=>j.testValues?.[g.testNumber]).filter(j=>j!==void 0&&isFinite(j)),Z=D.filter(j=>g.limitLow!==void 0&&j<g.limitLow||g.limitHigh!==void 0&&j>g.limitHigh).length;z=D.length,I=D.length>0?(D.length-Z)/D.length*100:null}}A.push({entry:g,stats:L,specYieldPct:I,specN:z})}if(!A.length)return null;const P=new Map;if(s?.length&&t?.length)for(const g of ct(s,t))g.hasSpec&&P.set(g.testNumber,g.ppk);const R=P.size>0,h=new Set(A.map(g=>g.stats.count)).size===1?A[0].stats.count:null,f=l("div"),w=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:y.sm}),m=h!==null?` \xB7 N=${h.toLocaleString()}`:"",x=d?.populationLabel?`Test Values (${A.length}) \u2014 ${d.populationLabel}${m}`:`Test Values (${A.length})${m}`,C=J(x);C.style.marginBottom="0",w.appendChild(C);const v=document.createElement("button");v.type="button",v.textContent="Test values CSV",Object.assign(v.style,{...le("outlined")}),q(v),v.addEventListener("click",()=>{const g=["Test","Unit","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];R&&g.push("Ppk");const F=A.some($=>$.specYieldPct!==null&&$.specN!==$.stats.count);N&&(g.push("LSL","USL","Spec Yield %"),F&&g.push("Spec Yield N"));const O=[...Ye(d,g),...Oe(d)],I=[[...O.map($=>$.label),...g].map(ce).join(",")],z=$=>ke($,void 0,"engineering");for(const{entry:$,stats:D,specYieldPct:Z,specN:j}of A){const ae=[...O.map(te=>te.constant??""),$.name,$.unit??"",String(D.count),z(D.min),z(D.q1),z(D.median),z(D.mean),z(D.q3),z(D.max),z(D.stddev)];if(R){const te=P.get($.testNumber);ae.push(te==null?"":te.toFixed(3))}N&&(ae.push($.limitLow!==void 0?z($.limitLow):"",$.limitHigh!==void 0?z($.limitHigh):"",Z!==null?Z.toFixed(1):""),F&&ae.push($.limitLow!==void 0||$.limitHigh!==void 0?String(j):"")),I.push(ae.map(ce).join(","))}De(I.join(`
|
|
2
|
-
`),"test-values.csv","text/csv",i)}),
|
|
3
|
-
`),"functional-tests.csv","text/csv",n)}),b.appendChild(N),a.appendChild(b);const A=l("table",{width:"100%",borderCollapse:"collapse",fontSize:E.body}),P=document.createElement("thead"),R=document.createElement("tr"),H=c===null?["Test","N","Pass","Fail","Pass rate"]:["Test","Pass","Fail","Pass rate"];for(const m of H)R.appendChild(l("th",{textAlign:m==="Test"?"left":"right",fontWeight:"700",fontSize:E.body,letterSpacing:re,textTransform:"uppercase",color:Y,padding:"3px 8px 5px",borderBottom:`1px solid ${B.menuBorder}`,whiteSpace:"nowrap"},m));P.appendChild(R),A.appendChild(P);const h=document.createElement("tbody");for(const m of s){const x=document.createElement("tr"),C=(v,k="right")=>{x.appendChild(l("td",{textAlign:k,padding:`${y.xs} ${y.md}`,borderBottom:`1px solid ${B.menuBorder}`,color:K,whiteSpace:"nowrap"},v))};C(m.label,"left"),c===null&&C(String(m.totalDies)),C(String(m.passDies)),C(String(m.failDies)),C(m.passRatePercent!==null?`${m.passRatePercent.toFixed(1)}%`:"\u2014"),h.appendChild(x)}A.appendChild(h);const f=l("div",{overflowX:"auto"});if(f.appendChild(A),!d)return a.appendChild(f),a;b.remove();const{outer:w}=X(u,!0,void 0,{stateKey:"functionalTests",panel:d,render:m=>m.appendChild(f),control:()=>N});return w}export function buildLotFunctionalSection(e,t,o,n,i){const d=o?.length?{perWaferMetadata:o.map(p=>p.wafer??{}),populationLabel:`${o.length} wafer${o.length===1?"":"s"} pooled`}:void 0,s=ut(o);return s?buildFunctionalTestSection(e,t,s,n,d,i):buildFunctionalTestSection(e,t,void 0,n,d,i)}const ee={unusual:0,notable:1,info:2};function ye(e){return l("span",{display:"inline-block",width:"7px",height:"7px",borderRadius:_.pill,background:de(e),flexShrink:"0"})}function _e(e,t){let o=e.summary;return t&&(o.startsWith(`${t} has `)?o=o.slice(t.length+5):o.startsWith(`${t} `)&&(o=o.slice(t.length+1))),o=ne(o),o.length?o[0].toUpperCase()+o.slice(1):o}export function buildFindingsSection(e,t,o,n,i=!1){if(!e.length)return null;const d=e.some(h=>h.severity==="unusual"||h.severity==="notable"),s=d?e.filter(h=>h.severity!=="info").length.toString():void 0,{outer:p,content:c}=i?(()=>{const h=l("div");return{outer:h,content:h}})():X(`Findings (${e.length})`,d,s);function r(h){return h.reduce((f,w)=>ee[w.severity]<ee[f]?w.severity:f,"info")}function a(h){const f=new Map;for(const m of h){const x=`${m.comparison.family}\0${m.comparison.left}`,C=f.get(x)??[];C.push(m),f.set(x,C)}const w=[...f.entries()].map(([m,x])=>{const C=[...x].sort((v,k)=>ee[v.severity]-ee[k.severity]);return{key:m,findings:C,worst:r(C)}});return w.sort((m,x)=>ee[m.worst]-ee[x.worst]),w}function b(h,f){const m={ring:"Ring",quadrant:"Quadrant",sector:"Sector","reticle-position":"Reticle","test-site":"Test site",cluster:"Cluster","edge-arc":"Edge arc",wafer:"Wafer"}[h]??h;return f.toLowerCase().startsWith(m.toLowerCase())?f:`${m}: ${f}`}const u=e.filter(h=>h.comparison.family==="spatial-pattern"),S=new Set([...u.flatMap(h=>h.relatedIds??[]),...e.flatMap(h=>h.absorbedIds??[])]),N=e.filter(h=>h.comparison.family!=="spatial-pattern"&&!S.has(h.id)),A=a(N);function P(h,f=!1,w){const m=n===h.id,x=document.createElement("button");return x.type="button",x.dataset.wmapFinding=h.id,x.textContent=_e(h,w),ie(x,h.summary),x.setAttribute("aria-current",m?"true":"false"),Object.assign(x.style,{border:`1px solid ${B.menuBorder}`,borderLeft:`3px solid ${de(h.severity)}`,background:m?B.bgActive:B.menuBg,borderRadius:_.container,padding:f?"6px 10px":"8px 10px",textAlign:"left",fontSize:f?"10px":"11px",fontWeight:m?"600":"400",color:B.text,cursor:"pointer",width:"100%",marginBottom:y.xs}),q(x,"bare"),x.addEventListener("click",()=>o(h,x)),x}const R=ne(tt(e)??"");if(R){const h=l("div",{background:B.bgActive,borderRadius:_.container,padding:`${y.md} ${y.lg}`,marginBottom:y.md,display:"flex",gap:y.md,alignItems:"flex-start"}),f=l("span",{flex:"1",fontSize:E.body,lineHeight:Q.base,color:B.text},R);h.appendChild(f);const w=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:E.body,color:B.icon,cursor:"pointer",padding:"0",lineHeight:Q.base,whiteSpace:"nowrap"},"Detail \u25B8");w.type="button",q(w,"bare"),w.addEventListener("click",()=>{const m=Fe({title:"Findings Summary",onClose:()=>{},anchor:w}),x=l("p",{fontSize:"16px",lineHeight:Q.base,color:B.text,padding:`${y.xxl} ${y.xxxl} ${y.xxl}`,margin:"0",borderBottom:`1px solid ${B.menuBorder}`,flexShrink:"0"},R);m.contentWrap.appendChild(x);const C=l("div",{overflowY:"auto",padding:`${y.xxl} ${y.xxxl}`,flex:"1"}),v=(k,T)=>{const M=l("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:E.sub,fontWeight:"600",color:K,marginTop:y.lg,marginBottom:y.xs});return M.appendChild(ye(k)),M.appendChild(l("span",{},T)),M};for(const k of u){C.appendChild(v(k.severity,k.comparison.left)),C.appendChild(l("div",{fontSize:E.body,color:B.text,padding:"4px 0 4px 15px",marginBottom:y.xxs},ne(k.summary)));const T=e.filter(M=>k.relatedIds?.includes(M.id));for(const M of T)C.appendChild(l("div",{fontSize:E.body,color:Y,padding:"2px 0 2px 23px",marginBottom:y.xxs},ne(M.summary)))}for(const k of A){const[T,M]=k.key.split("\0");C.appendChild(v(k.worst,b(T,M)));for(const W of k.findings)C.appendChild(l("div",{fontSize:E.body,color:B.text,padding:"4px 0 4px 15px",marginBottom:y.xxs},_e(W,M)))}m.contentWrap.appendChild(C),Object.assign(m.contentWrap.style,{flexDirection:"column",overflow:"hidden"})}),h.appendChild(w),c.appendChild(h)}let H=!0;for(const h of u){H||c.appendChild(l("div",{height:"1px",background:B.separator,margin:"4px 0"})),H=!1;const f=e.filter(v=>h.relatedIds?.includes(v.id)),w=f.length>0,m=l("div",{position:"relative",marginBottom:w?"2px":"4px"}),x=n===h.id,C=document.createElement("button");if(C.type="button",C.dataset.wmapFinding=h.id,Object.assign(C.style,{border:`1px solid ${B.menuBorder}`,borderLeft:`3px solid ${de(h.severity)}`,background:x?B.bgActive:B.menuBg,borderRadius:_.container,padding:"8px 32px 8px 10px",textAlign:"left",fontSize:E.body,fontWeight:x?"600":"500",color:B.text,cursor:"pointer",width:"100%"}),C.textContent=ne(h.summary),ie(C,h.summary),C.setAttribute("aria-current",x?"true":"false"),q(C,"bare"),C.addEventListener("click",()=>o(h,C)),m.appendChild(C),w){const v=l("div",{display:"none",paddingLeft:"12px",marginBottom:y.xs});for(const M of f)v.appendChild(P(M,!0));m.appendChild(v);let k=!1;const T=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:E.body,color:B.icon,cursor:"pointer",padding:`${y.xxs} ${y.xs}`,lineHeight:Q.none},"\u25B8");q(T,"bare"),T.type="button",ie(T),T.setAttribute("aria-label","Show supporting findings"),T.setAttribute("aria-expanded","false"),T.addEventListener("click",M=>{M.stopPropagation(),k=!k,v.style.display=k?"block":"none",T.textContent=k?"\u25BE":"\u25B8";const W=k?"Hide supporting findings":"Show supporting findings";T.setAttribute("aria-label",W),T.setAttribute("aria-expanded",String(k))}),m.appendChild(T)}c.appendChild(m)}for(const h of A){H||c.appendChild(l("div",{height:"1px",background:B.separator,margin:"4px 0"})),H=!1;const[f,w]=h.key.split("\0"),m=l("div",{display:"flex",alignItems:"center",gap:y.sm,fontSize:E.body,fontWeight:"600",color:K,margin:"2px 0 4px"});m.appendChild(ye(h.worst)),m.appendChild(l("span",{},b(f,w))),c.appendChild(m);for(const x of h.findings)c.appendChild(P(x,!1,w))}return p}const St=[{value:"",label:"All kinds"},{value:"yield",label:"Yield"},{value:"hardBin",label:"Hard bin"},{value:"softBin",label:"Soft bin"},{value:"test",label:"Test value"},{value:"functionalTest",label:"Functional test"},{value:"spatialPattern",label:"Spatial pattern"}],vt=[{value:"",label:"All regions"},{value:"ring",label:"Ring"},{value:"quadrant",label:"Quadrant"},{value:"reticle-position",label:"Reticle position"},{value:"test-site",label:"Test site"},{value:"wafer",label:"Wafer"},{value:"sector",label:"Sector"},{value:"cluster",label:"Cluster"},{value:"edge-arc",label:"Edge arc"},{value:"spatial-pattern",label:"Spatial pattern"}],$t=["unusual","notable","info"],kt={unusual:"Unusual",notable:"Notable",info:"Info"};function Bt(e,t,o){const n=l("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:y.lg,marginBottom:y.lg,paddingBottom:"10px",borderBottom:`1px solid ${B.separator}`}),i={unusual:0,notable:0,info:0};for(const r of e)i[r.severity]++;const d=$t.filter(r=>i[r]>0),s=new Set(t.severity===void 0?d:Array.isArray(t.severity)?t.severity:[t.severity]),p=l("div",{display:"flex",gap:y.xs,flexWrap:"wrap"});for(const r of d){const a=document.createElement("button");a.type="button";const b=()=>{const u=s.has(r);a.setAttribute("aria-pressed",u?"true":"false"),Object.assign(a.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${B.menuBorder}`,borderRadius:_.pill,background:u?B.bgActive:"none",opacity:u?"1":"0.5",color:B.text,fontSize:E.body,padding:`${y.xxs} ${y.md}`,cursor:"pointer"}),a.dataset.on=u?"true":"false"};a.appendChild(ye(r)),a.appendChild(l("span",{},`${kt[r]} ${i[r]}`)),ie(a,()=>s.has(r)?"Click to hide these findings":"Click to show these findings"),b(),q(a,"bare"),a.addEventListener("click",()=>{s.has(r)?s.delete(r):s.add(r),t.severity=s.size===d.length?void 0:[...s],o()}),p.appendChild(a)}n.appendChild(p);const c=t.kind!==void 0||t.family!==void 0;return(e.length>=Lt||c)&&(n.appendChild(Be("Kind:",St,t.kind??"",r=>{t.kind=r||void 0,o()},{maxWidth:"130px"})),n.appendChild(Be("Region:",vt,t.family??"",r=>{t.family=r||void 0,o()},{maxWidth:"150px"}))),n}const Lt=8;function Nt(e,t){const o=l("div",{display:"flex",flexDirection:"column",gap:y.xs,padding:y.md,marginBottom:y.md,background:B.bgHover,borderRadius:_.control},void 0,t);if(o.appendChild(l("div",{fontSize:E.body,color:B.value},e.message,t)),e.detail&&o.appendChild(l("div",{fontSize:E.meta,color:Y},e.detail,t)),e.actionLabel&&e.onAction){const n=l("button",le("outlined"),e.actionLabel,t);n.type="button",n.style.alignSelf="flex-start",n.style.marginTop=y.xs,q(n),n.addEventListener("click",e.onAction),o.appendChild(n)}return o}export function buildFindingsSectionWithFilter(e,t,o,n,i,d,s){if(!e.findings.length&&!s)return null;const p=e.findings.some(N=>N.severity==="unusual"||N.severity==="notable"),c=p?e.findings.filter(N=>N.severity!=="info").length.toString():void 0,r=d?.ownerDocument??document,{outer:a,content:b}=X(`Findings (${e.findings.length})`,p||!!s,c,{stateKey:"findings",panel:d});if(s&&b.appendChild(Nt(s,r)),!e.findings.length)return a;b.appendChild(Bt(e.findings,n,i));const u=nt(e,n);if(!u.length)return b.appendChild(l("div",{color:Y,fontSize:E.body,textAlign:"center",padding:`${y.xxl} ${y.md}`},"No findings match the current filter.")),a;const S=buildFindingsSection(u,e,t,o,!0);return S&&b.appendChild(S),a}export function buildLotTestSection(e,t,o,n,i,d,s,p="full"){const c=n?.length?{perWaferMetadata:n.map(a=>a.wafer??{}),populationLabel:`${n.length} wafer${n.length===1?"":"s"} pooled`}:void 0;let r;if(n?.length&&n.every(a=>a.stats.perTestStats!==void 0)){const a=new Map;for(const b of n)for(const u of b.stats.perTestStats??[]){const S=a.get(u.testNumber);S?(S.n+=u.count,S.sum+=u.mean*u.count,S.min=Math.min(S.min,u.min),S.max=Math.max(S.max,u.max)):a.set(u.testNumber,{n:u.count,sum:u.mean*u.count,min:u.min,max:u.max})}r={perTestStats:[...a.entries()].map(([b,u])=>({testNumber:b,count:u.n,min:u.min,max:u.max,mean:u.sum/u.n}))}}if(n?.length&&n.every(a=>a.stats.testSpecYield!==void 0)){const a=new Map;for(const b of n)for(const u of b.stats.testSpecYield??[]){const S=a.get(u.testNumber);S?(S.passDies+=u.passDies,S.totalDies+=u.totalDies):a.set(u.testNumber,{passDies:u.passDies,totalDies:u.totalDies})}r={...r,testSpecYield:[...a.entries()].map(([b,u])=>({testNumber:b,totalDies:u.totalDies,yieldPercent:u.totalDies>0?u.passDies/u.totalDies*100:null}))}}return buildTestSection(e,t,o,r,i,c,d,s,p)}export function createSummaryPanelEl(e,t,o=document){const n=e==="top"||e==="bottom",i=l("div",{background:gt,color:B.text,fontVariantNumeric:"tabular-nums",border:Re,borderRadius:_.container,padding:y.xl,overflowY:"auto",overflowX:n?"auto":"hidden",flexShrink:"0",boxSizing:"border-box",fontFamily:E.family,fontSize:E.sub,boxShadow:Le.panel},void 0,o);return e==="right"&&(i.style.marginRight=t),e==="left"&&(i.style.marginLeft=t),e==="bottom"&&(i.style.marginBottom=t),n?(i.style.height="180px",i.style.width="100%"):(i.style.width="300px",i.style.maxHeight="100%"),i}export function wrapWithSummaryPanel(e,t,o){const n=o==="top"||o==="bottom",i=l("div",{display:"flex",flexDirection:n?"column":"row",gap:y.md,width:"100%",height:"100%",flex:"1 1 0",minHeight:"0",boxSizing:"border-box",alignItems:n?"flex-start":"stretch"});return e.style.flex="1 1 0",e.style.minWidth="0",e.style.minHeight="0",o==="right"||o==="bottom"?(i.appendChild(e),i.appendChild(t)):(i.appendChild(t),i.appendChild(e)),i}function je(e){return l("div",{fontSize:E.heading,fontWeight:"600",color:K,marginBottom:y.lg},e)}function fe(e,t){const o=l("button",le("outlined"),e);return o.type="button",q(o),o.addEventListener("click",t),o}function qe(...e){const t=e.filter(n=>!!n);if(!t.length)return null;const o=l("div",{display:"flex",flexWrap:"wrap",gap:y.sm,marginBottom:y.lg});for(const n of t)o.appendChild(n);return o}function Ve(e,t,o,n,i,d,s,p,c,r,a){const b=e.ownerDocument,u=Fe({title:"Die list",onClose:()=>{},anchor:e,ownerDocument:b});u.contentWrap.style.padding="14px 16px";const S=ft(t,o,{...s,title:n,onSaveText:p,waferMetadata:i,metadataFields:d,extraColumn:c,getWafer:r,ringCount:a,ownerDocument:b});S&&u.contentWrap.appendChild(S)}export function renderWaferSummaryContent(e,t){const o=e.scrollTop;e.innerHTML="";const{wafer:n,dies:i,yieldSummary:d,dataCoverage:s,hbinDefs:p,sbinDefs:c,testDefs:r,statsSummary:a,passBins:b=[1],ringCount:u=4,binColors:S,plotMode:N,fallbackFormat:A,onFindingClick:P,activeFindingId:R=null,findingsFilter:H,onFindingsFilterChange:h,findingsNotice:f,onSaveText:w,metadataFields:m,dieListOptions:x,metadataShownElsewhere:C}=t;e.appendChild(je("Wafer Summary"));const v=t.warnings??$e({statsSummary:a});v.length&&e.appendChild(ge(v,e.ownerDocument));const k=d&&s?fe("Summary report",()=>{Ee(Je({wafer:n,dies:i,yield:d,dataCoverage:s,hbinDefs:p,sbinDefs:c,testDefs:r,passBins:b,ringCount:u},a),{anchor:e})}):null,T=(x?.enabled??!0)&&i.length?fe("View die list",()=>{Ve(e,i,r,`Die list \u2014 ${i.length} dies`,n.metadata,m,x,w,void 0,()=>n,u)}):null,M=qe(k,T);M&&e.appendChild(M);const W=[],V=a?.stats.isLotStack?a.stats:void 0,G=V?.lotSize!==void 0?{lotSize:V.lotSize,aggrMethod:lt(V.aggregationMethod)}:void 0;C||W.push(buildMetadataInfoSection([{metadata:n.metadata??void 0}],G)),d&&s&&W.push(buildYieldSection(d,s,b)),a&&P&&H&&h&&W.push(buildFindingsSectionWithFilter(a,P,R,H,h,e,f)),W.push(buildBinBreakdownSection({dies:i,hbinDefs:p,sbinDefs:c,binColors:S,hardCounts:a?.stats.hardBinCounts,softCounts:a?.stats.softBinCounts,plotMode:N,passBins:b,panel:e})),W.push(buildRegionYieldPanelSection({diesByWafer:[i],allWafers:[n],ringCount:u,passBins:b,panel:e}));const U=n.metadata?{waferMetadata:n.metadata}:void 0;W.push(buildTestSection(i,r,A,a?.stats,w,U,[{dies:i}],e,"compact")),W.push(buildFunctionalTestSection(i,r,a?.stats.functionalYield,w,U,e));let g=!0;for(const F of W)F&&(g||e.appendChild(Ae()),g=!1,e.appendChild(F));e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=o}export function reportMapsFromItems(e,t,o){const n=[];return e.forEach((i,d)=>{!i?.wafer||!i.dies||n.push({...i,wafer:i.wafer,dies:i.dies,label:me(i,d),passBins:[...he(i,t)],ringCount:i.ringCount??o})}),n}export function renderLotSummaryContent(e,t){const o=e.scrollTop;e.innerHTML="";const{lotSummary:n,items:i,hbinDefs:d,sbinDefs:s,testDefs:p,passBins:c=[1],ringCount:r=4,binColors:a,plotMode:b,fallbackFormat:u,onFindingClick:S,activeFindingId:N=null,onWaferClick:A,findingsFilter:P,onFindingsFilterChange:R,findingsNotice:H,onSaveText:h,dieListOptions:f,findingsFor:w}=t;e.appendChild(je(`Summary \u2014 ${rt(ve(n.perWafer.map(L=>L.summary.wafer)))}`));const m=t.warnings??$e({lotStatsSummary:n});m.length&&e.appendChild(ge(m,e.ownerDocument));const x=fe("Summary report",()=>{Ee(et(reportMapsFromItems(i,c,r)),{anchor:e})}),C=[],v=[],k=[],T=[],M=[],W=new WeakMap,V=new WeakMap;for(let L=0;L<i.length;L++){const I=i[L];if(!I){v.push([]);continue}I.wafer&&(C.push(I.wafer),T.push(I.dies??[]),M.push(he(I,c)));const z=I.dies??[],$=me(I,L);for(const D of z)W.set(D,$),I.wafer&&V.set(D,I.wafer);v.push(z),k.push(...z)}const G=f?.enabled??!0?fe("View die list",()=>{k.length&&Ve(e,k,p,`Die list \u2014 ${k.length} dies across ${i.length} wafer${i.length===1?"":"s"}`,we(i.filter(L=>!!L).map(L=>({metadata:L.wafer?.metadata}))),i.find(L=>L?.metadataFields?.length)?.metadataFields,f,h,{label:"Wafer",get:L=>W.get(L)},L=>V.get(L),r)}):null,U=qe(x,G);U&&e.appendChild(U);const g=i.map(L=>L?.statsSummary).filter(L=>!!L),F=[buildLotOverviewSection(n,g)];S&&P&&R&&F.push(buildFindingsSectionWithFilter(n,S,N,P,R,e,H)),F.push(buildPerWaferYieldSection(n,i,A,e,w),buildBinBreakdownSection({dies:k,hbinDefs:d,sbinDefs:s,plotMode:b,passBins:c,panel:e,binColors:a??st(i.flatMap(L=>L?[{dies:L.dies??[],passBins:he(L,c)}]:[]),{hbinDefs:d,sbinDefs:s}).colors}),buildRegionYieldPanelSection({diesByWafer:T,allWafers:C,ringCount:r,passBins:L=>M[L],panel:e}),p?.length?buildLotTestSection(k,p,u,g,h,v.map(L=>({dies:L})),e,"compact"):null,p?.length?buildLotFunctionalSection(k,p,g,h,e):null);let O=!0;for(const L of F)L&&(O||e.appendChild(Ae()),O=!1,e.appendChild(L));e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=o}
|
|
1
|
+
import{waferDisplayLabel as ye}from"../core/waferLabel.js";import{itemPassBins as Ce,passBinsLabel as Je}from"../core/passBins.js";import{isParametricTest as et}from"../renderer/buildWaferMap.js";import{buildRingRegions as tt,buildQuadrantRegions as nt,buildRegionYieldData as ot}from"../stats/regions.js";import{computeFunctionalYield as it}from"../stats/analyzeWaferMap.js";import{renderWaferReportHtml as st,renderLotReportHtml as at}from"../stats/renderSummaryReport.js";import{buildFindingsNarrative as rt}from"../stats/findingsNarrative.js";import{filterFindings as lt}from"../stats/filterFindings.js";import{buildFacetTable as dt,prettyKey as ke}from"../stats/facets.js";import{commonMetadata as Le}from"../stats/facets.js";import{resolveMetadataColumns as ct}from"../stats/metadataColumns.js";import{resolveBinColors as Be,resolveBinColorsByWafer as pt}from"../renderer/binColors.js";import{NO_DATA_FILL as ut}from"../renderer/colorMap.js";import{describeWaferPopulation as Ne,populationLabel as ft}from"../stats/population.js";import{buildWarningsBanner as Se,collectWarnings as Te}from"./warnings.js";export{Se as buildWarningsBanner};import{fmt as Ee,fmtAggregationMethod as mt,plainBinTerms as le}from"../renderer/fmt.js";import{getUniqueTestNumbers as gt}from"../renderer/buildView.js";import{describeSorted as ht,quantile as Fe}from"../stats/math.js";import{pooledTestStatsSteps as bt}from"../stats/capability.js";import{sortBinsForDisplay as xt}from"../stats/binPareto.js";import{poolFunctionalYield as yt}from"../stats/testPassRate.js";import{makeLabeledSelect as De,makeSegmented as we}from"./charts/chartShell.js";import{SHADOW as Re,MOTION as Me,LEADING as oe,TRACKING as pe,wireControlHover as G,controlStyle as ue,SPACE as y,RADIUS as q,FONT as T,CLR as $,sevColor as fe,openModal as Ie,openReportModal as We,saveTextFile as Ae,wireTooltip as de}from"./toolbar.js";import{buildDieListSection as Ct}from"./dieList.js";export{csvField}from"../core/utils.js";import{csvField as me,drain as ge,maxOf as St,medianOfSorted as wt,minOf as vt}from"../core/utils.js";import{metadataDisplayValue as $t}from"../core/metadata.js";const kt=$.panelBg,Pe=`1px solid ${$.menuBorder}`,Lt="12px",_=$.label,ee=$.value,ze="10px";function l(e,t,o,n=document){const i=n.createElement(e);return t&&Object.assign(i.style,t),o!==void 0&&(i.textContent=o),i}function ae(e){return l("div",{fontSize:ze,fontWeight:"700",letterSpacing:pe,textTransform:"uppercase",color:_,marginBottom:y.sm},e)}const He=new WeakMap,Oe=["regionYield","testValues","functionalTests"];function he(e){if(!e)return{collapsed:new Set(Oe),regionMode:"ring",waferSort:"slot"};let t=He.get(e);return t||(t={collapsed:new Set(Oe),regionMode:"ring",waferSort:"slot"},He.set(e,t)),t}function ie(e,t=!0,o,n){const i=l("div"),c=he(n?.panel);n?.stateKey&&n.panel&&(t=!c.collapsed.has(n.stateKey));const s=document.createElement("button");s.type="button",Object.assign(s.style,{display:"flex",alignItems:"center",gap:y.xs,width:"100%",background:"none",border:"none",cursor:"pointer",padding:"0 0 6px",textAlign:"left"});const m=l("span",{fontSize:T.meta,color:_,transition:`transform ${Me.base}`,transform:t?"rotate(90deg)":"rotate(0deg)",display:"inline-block",lineHeight:oe.none,marginRight:"1px"},"\u25B6"),d=l("span",{fontSize:ze,fontWeight:"700",letterSpacing:pe,textTransform:"uppercase",color:_,flex:"1"},e);if(G(s,"bare"),s.setAttribute("aria-expanded",t?"true":"false"),s.appendChild(m),s.appendChild(d),o){const h=l("span",{fontSize:T.meta,fontWeight:"700",background:$.warnBg,color:$.warnText,borderRadius:q.pill,padding:"1px 5px"},o);s.appendChild(h)}const r=l("div");r.style.display=t?"block":"none";let a=t;if(s.addEventListener("click",()=>{a=!a,r.style.display=a?"block":"none",m.style.transform=a?"rotate(90deg)":"rotate(0deg)",s.setAttribute("aria-expanded",a?"true":"false"),n?.stateKey&&n.panel&&(a?c.collapsed.delete(n.stateKey):c.collapsed.add(n.stateKey))}),n?.control){const h=l("div",{display:"flex",alignItems:"center",gap:y.sm});s.style.width="auto",s.style.flex="1",h.appendChild(s);const u=()=>{r.innerHTML="",n.render?.(r)},w=n.control(u);w&&(w.style.marginBottom="6px",w.addEventListener("click",L=>L.stopPropagation()),h.appendChild(w)),i.appendChild(h)}else i.appendChild(s);return i.appendChild(r),n?.render?.(r),{outer:i,content:r}}function Ye(){return l("div",{height:"1px",background:$.separator,margin:`${Lt} 0`,flexShrink:"0"})}function ve(e,t,o=$.barFill,n,i){const c=l("div",{marginBottom:"5px"}),s=l("div",{display:"flex",justifyContent:"space-between",fontSize:T.body,color:ee,marginBottom:y.xxs}),m=l("span",{color:_},e),d=l("span",{fontWeight:"600"},`${t.toFixed(1)}%`);s.appendChild(m),s.appendChild(d);const r=n!==void 0?n:Math.min(100,Math.max(0,t)),a=o,h=l("div",{position:"relative",height:"9px",background:$.bgActive,borderRadius:q.control,overflow:"hidden"}),u=l("div",{height:"100%",width:`${r}%`,background:a,borderRadius:q.control,transition:`width ${Me.base}`});if(h.appendChild(u),i!==void 0){const w=l("div",{position:"absolute",top:"0",bottom:"0",left:`${i}%`,width:"2px",marginLeft:"-1px",background:$.value,opacity:"0.8"});h.appendChild(w)}return c.appendChild(s),c.appendChild(h),c}function _e(){const e=l("div",{display:"flex",gap:y.sm,marginBottom:y.md});return e.finish=()=>{const t=e.lastElementChild;t&&Object.assign(t.style,{borderRight:"none",paddingRight:"0"})},e}function ce(e,t,o){const n=l("div",{padding:`${y.xxs} ${y.md} ${y.xxs} 0`,borderRight:Pe,textAlign:"center",flex:"1"}),i=l("div",{fontSize:T.heading,fontWeight:"700",color:ee,lineHeight:oe.tight},e),c=l("div",{fontSize:T.body,color:_,marginTop:y.xxs},t);return n.appendChild(i),n.appendChild(c),o&&n.appendChild(l("div",{fontSize:T.body,color:_,opacity:"0.85"},o)),n}function je(e,t){const o=l("div",{display:"flex",justifyContent:"space-between",fontSize:T.body,gap:y.md,marginBottom:"3px"}),n=l("span",{color:_,flexShrink:"0"},e),i=l("span",{color:ee,textAlign:"right",fontWeight:"500",wordBreak:"break-all"},t);return o.appendChild(n),o.appendChild(i),o}function Bt(e,t){const o=l("div",{display:"flex",fontSize:T.body,gap:y.sm,marginBottom:"3px"}),n=l("span",{color:_,flexShrink:"0"},`${e}:`),i=l("span",{color:ee,fontWeight:"500",wordBreak:"break-all"},t);return o.appendChild(n),o.appendChild(i),o}export function metadataEntries(e){const t=[];for(const[o,n]of Object.entries(e)){const i=$t(n);i!==void 0&&t.push([o,i])}return t}const Nt=["lot","product","testprogram","split"],be=e=>{const t=Nt.indexOf(e.toLowerCase().replace(/[^a-z]/g,""));return t===-1?Number.MAX_SAFE_INTEGER:t};export function buildFacetSummaryChips(e,t=3){if(!e.length)return null;const o=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:`${y.xs} ${y.lg}`}),n=[...e].sort((m,d)=>be(m.key)-be(d.key)),i=n.filter(m=>be(m.key)!==Number.MAX_SAFE_INTEGER),c=n.filter(m=>be(m.key)===Number.MAX_SAFE_INTEGER),s=m=>{const d=l("span",{whiteSpace:"nowrap"}),r=l("span",{color:_},`${ke(m.key)}: `);d.appendChild(r);const a=m.values.slice(0,t),h=m.values.length-a.length;let u=a.map(w=>w.value).join(", ");return h>0&&(u+=` +${h} more`),d.appendChild(document.createTextNode(u)),d};for(const m of i)o.appendChild(s(m));if(c.length){const m=l("span",{display:"none",flexWrap:"wrap",alignItems:"baseline",gap:`${y.xs} ${y.lg}`});for(const a of c)m.appendChild(s(a));const d=l("button",{background:"none",border:"none",padding:"0",cursor:"pointer",color:$.iconActive,fontSize:T.body,whiteSpace:"nowrap"});d.type="button",G(d,"bare");const r=a=>{m.style.display=a?"contents":"none",d.textContent=a?"less":`+${c.length} more`,d.setAttribute("aria-expanded",String(a))};r(!1),d.addEventListener("click",()=>r(d.getAttribute("aria-expanded")!=="true")),o.appendChild(m),o.appendChild(d)}return o}export function buildMetadataStripRow(e,t,o){const n=dt(e,o),i=buildFacetSummaryChips(n);if(!i&&!t)return null;const c=l("div",{display:"flex",flexWrap:"wrap",alignItems:"baseline",gap:"4px 14px"});if(t){const s=l("span",{fontWeight:"500",whiteSpace:"nowrap"},t.aggrMethod?`${t.lotSize} wafers stacked \xB7 ${t.aggrMethod}`:`${t.lotSize} wafers stacked`);c.appendChild(s)}return i&&c.appendChild(i),c}const Tt={display:"flex",flexDirection:"column",gap:y.sm,background:$.menuBg,border:`1px solid ${$.menuBorder}`,borderRadius:q.container,padding:`${y.sm} ${y.lg}`,boxShadow:Re.panel,fontSize:T.body,lineHeight:oe.none,boxSizing:"border-box",width:"100%",minWidth:"0"};export function buildMetadataStripBox(e,t,o){const n=buildMetadataStripRow(e,t,o);if(!n)return null;const i=l("div",Tt);return i.appendChild(n),i}export function buildCompactMetadataRows(e){const t=metadataEntries(e);if(!t.length)return null;const o=l("div");for(const[n,i]of t)o.appendChild(Bt(ke(n),i));return o}export function buildMetadataInfoSection(e,t){const o=buildMetadataStripRow(e,t,{facetableOnly:e.length>1});if(!o)return null;const n=l("div");return n.appendChild(ae("Wafer Info")),n.appendChild(o),n}export function buildYieldSection(e,t,o=[1]){const n=l("div");n.appendChild(ae("Summary"));const i=_e();if(i.appendChild(ce(String(e.totalDies),"Total dies")),e.partialDies>0&&i.appendChild(ce(String(e.partialDies),"Partial")),e.yieldPercent!==null){const c=Je([o]);i.appendChild(ce(`${e.yieldPercent.toFixed(1)}%`,"Yield",`pass: ${c}`))}return i.finish(),n.appendChild(i),e.edgeExcludedDies>0&&n.appendChild(je("Edge excluded (outer zone)",String(e.edgeExcludedDies))),n}export function buildBinSection(e,t,o,n,i,c){const s=new Map;if(i)for(const[r,a]of Object.entries(i))s.set(Number(r),a);else for(const r of e){if(r.partial||r.edgeExcluded)continue;const a=o==="hard"?r.hbin:r.sbin;a!=null&&s.set(a,(s.get(a)??0)+1)}if(!s.size)return null;const m=n??Be(e,{passBins:c,...o==="hard"?{hbinDefs:t}:{sbinDefs:t}}),d=l("div");d.appendChild(ae(Ve(o,[...s.values()].reduce((r,a)=>r+a,0))));for(const r of qe(s,t,m[o],m.pass[o]))d.appendChild(r);return d}function Ve(e,t){return`${e==="hard"?"Hard":"Soft"} Bin Breakdown \u2014 % of dies (N=${t})`}function qe(e,t,o,n){const i=[...e.values()].reduce((m,d)=>m+d,0),c=t?new Map(t.map(m=>[m.bin,m])):null;return xt(e.entries(),n).map(([m,d])=>{const r=c?.get(m),a=r?.name?`Bin ${m} \xB7 ${r.name}`:`Bin ${m}`,h=d/i*100,u=o.get(m)??ut;return ve(`${a} (${d})`,h,u)})}export function buildBinBreakdownSection(e){const{dies:t,hbinDefs:o,sbinDefs:n,hardCounts:i,softCounts:c,plotMode:s,passBins:m=[1],panel:d}=e,r=e.binColors??Be(t,{passBins:m,hbinDefs:o,sbinDefs:n}),a=g=>{const C=g==="hard"?i:c,b=new Map;if(C){for(const[k,v]of Object.entries(C))b.set(Number(k),v);return b}for(const k of t){if(k.partial||k.edgeExcluded)continue;const v=g==="hard"?k.hbin:k.sbin;v!=null&&b.set(v,(b.get(v)??0)+1)}return b},h=a("hard"),u=a("soft"),w=h.size>0,L=u.size>0;if(!w&&!L)return null;const R=he(d),P=s==="softBin"||s==="stackedSoftBins"?"soft":s==="hardBin"||s==="stackedBins"?"hard":void 0;let F=R.binMode??P??(w?"hard":"soft");F==="hard"&&!w&&(F="soft"),F==="soft"&&!L&&(F="hard");const A=F==="hard"?h:u,x=F==="hard"?o:n,f=[...A.values()].reduce((g,C)=>g+C,0),{outer:S}=ie(Ve(F,f),!0,void 0,{stateKey:"bins",panel:d,render:g=>{const C=F==="hard"?r.hard:r.soft;for(const b of qe(A,x,C,r.pass[F]))g.appendChild(b)},control:w&&L&&d?()=>we([["hard","Hard"],["soft","Soft"]],F,g=>{R.binMode=g;const C=buildBinBreakdownSection(e);C&&S.replaceWith(C)},d.ownerDocument,!0):void 0});return S}export function buildLotBinSection(e,t,o,n){return buildBinSection(e,t,o,n)}export function buildRegionYieldPanelSection(e){const{diesByWafer:t,allWafers:o,ringCount:n,passBins:i,panel:c}=e,s=he(c),m=a=>ot(t,o,n,i,a==="ring"?tt:nt);if(!m("ring").length&&!m("quadrant").length)return null;const d=s.regionMode,{outer:r}=ie(d==="ring"?"Ring Yield":"Quadrant Yield",!0,void 0,{stateKey:"regionYield",panel:c,render:a=>{const h=m(d);if(!h.length){a.appendChild(l("div",{fontSize:T.body,color:_},"No positioned dies for this breakdown."));return}for(const{label:u,n:w,yieldPercent:L}of h)a.appendChild(ve(`${u} (N=${w})`,L))},control:c?()=>we([["ring","Ring"],["quadrant","Quadrant"]],d,a=>{s.regionMode=a;const h=buildRegionYieldPanelSection(e);h&&r.replaceWith(h)},c.ownerDocument,!0):void 0});return r}export function buildLotOverviewSection(e,t=[]){const o=l("div"),n=Ne(e.perWafer.map(s=>s.summary.wafer));o.appendChild(ae(n.lotId!==void 0?"Lot Summary":"Summary"));const i=_e();i.appendChild(ce(String(e.stats.waferCount),"Wafers"));const c=e.perWafer.map(s=>s.summary.stats.yieldPercent).filter(s=>s!==null);if(c.length){const s=c.reduce((m,d)=>m+d,0)/c.length;i.appendChild(ce(`${s.toFixed(1)}%`,"Mean per-wafer yield"))}if(i.finish(),o.appendChild(i),t.length){const s=t.reduce((d,r)=>d+r.stats.analyzedDies,0),m=t.reduce((d,r)=>d+r.stats.excludedDies,0);(s||m)&&o.appendChild(je("Dies analysed",m?`${s.toLocaleString()} (${m.toLocaleString()} excluded)`:s.toLocaleString()))}return o}export function buildPerWaferYieldSection(e,t,o,n,i){const c=e.perWafer.map(f=>({waferIndex:f.waferIndex,label:ye(t[f.waferIndex],f.waferIndex).replace(/\s*·\s*\d+(\.\d+)?%$/,""),yieldPct:f.summary.stats.yieldPercent})).filter(f=>f.yieldPct!==null);if(!c.length)return null;const s=vt(c.map(f=>f.yieldPct)),m=St(c.map(f=>f.yieldPct)),d=s===m?"":` (${s.toFixed(1)}\u2013${m.toFixed(1)}%)`,r=[...c.map(f=>f.yieldPct)].sort((f,S)=>f-S),a=wt(r),h=Fe(r,.25),u=Fe(r,.75),w=c.length>=5?h-1.5*(u-h):-1/0,L=he(n),R=f=>{const S=L.waferSort==="yield"?[...c].sort((g,C)=>g.yieldPct-C.yieldPct):c;for(const{waferIndex:g,label:C,yieldPct:b}of S){const v=b<w?`${C} \xB7 low outlier`:C,E=ve(v,b,void 0,void 0,a),D=i?.(g);D?.total&&A(E,D),x(E,g,C,b,D),f.appendChild(E)}},P=` \xB7 median ${a.toFixed(1)}%`,{outer:F}=ie("Wafer Yield"+d+P,!0,void 0,{stateKey:"waferYield",panel:n,render:R,control:n?f=>we([["slot","Slot"],["yield","Yield"]],L.waferSort,S=>{L.waferSort=S,f()},n.ownerDocument,!0):void 0});return F;function A(f,S){const g=S.unusual?"unusual":S.notable?"notable":"info",C=l("span",{marginLeft:y.sm,flexShrink:"0",background:fe(g),color:"#fff",borderRadius:q.container,padding:"0 5px",fontSize:T.body,fontWeight:"600"},`${S.total}`);C.setAttribute("aria-hidden","true"),de(C,`${S.total} finding${S.total===1?"":"s"}`+(S.unusual?`, ${S.unusual} unusual`:"")+(S.notable?`, ${S.notable} notable`:"")),f.firstElementChild?.firstElementChild?.appendChild(C)}function x(f,S,g,C,b){o&&(f.style.cursor="pointer",f.style.borderRadius="4px",f.style.padding="2px 3px",f.style.marginLeft="-3px",f.style.marginRight="-3px",f.setAttribute("role","button"),f.setAttribute("aria-label",`${g}, ${C.toFixed(1)}% yield`+(b?.total?`, ${b.total} finding${b.total===1?"":"s"}${b.unusual?`, ${b.unusual} unusual`:""}`:"")+" \u2014 open wafer"),f.tabIndex=0,f.addEventListener("mouseenter",()=>{f.style.background=$.bgHover}),f.addEventListener("mouseleave",()=>{f.style.background=""}),f.addEventListener("focus",()=>{f.style.background=$.bgHover}),f.addEventListener("blur",()=>{f.style.background=""}),f.addEventListener("click",()=>o(S)),f.addEventListener("keydown",k=>{(k.key==="Enter"||k.key===" ")&&(k.preventDefault(),o(S))}))}}function Et(e){return ht([...e].sort((t,o)=>t-o))}function Ke(e,t){if(!e)return[];const o=e.waferMetadata??(e.perWaferMetadata?Le(e.perWaferMetadata.map(n=>({metadata:n}))):void 0);return!o||!Object.keys(o).length?[]:ct({waferMetadata:o,waferKeys:"identity",waferPlacement:"csv",reservedLabels:t}).columns}function Ge(e){return e?.populationLabel?[{key:"population",label:"Population",scope:"wafer",constant:e.populationLabel,get:()=>e.populationLabel,csvOnly:!0}]:[]}export function buildTestSection(...e){return ge(buildTestSectionSteps(...e))}export function*buildTestSectionSteps(e,t,o,n,i,c,s,m,d="full"){const r=e.filter(p=>!p.partial&&!p.edgeExcluded),a=new Map((n?.perTestStats??[]).map(p=>[p.testNumber,p])),h=new Map((n?.testSpecYield??[]).map(p=>[p.testNumber,p]));let u;if(t?.length?u=t.filter(et).map(p=>({testNumber:p.testNumber,name:p.name,unit:p.unit,limitLow:p.limitLow,limitHigh:p.limitHigh})):u=gt(r).map(W=>({testNumber:W,name:`Test ${W}`})),!u.length)return null;const w=u.filter(p=>r.some(W=>{const H=W.testValues?.[p.testNumber];return H!==void 0&&isFinite(H)}));if(!w.length)return null;const L=w.some(p=>p.limitLow!==void 0||p.limitHigh!==void 0),R=[],P=p=>!!p&&p.stddev!==void 0&&p.median!==void 0&&p.q1!==void 0&&p.q3!==void 0,F=p=>p.limitLow!==void 0||p.limitHigh!==void 0,A=!!(s?.length&&t?.length),f=(A||w.some(p=>!P(a.get(p.testNumber))||F(p)&&!h.has(p.testNumber)))&&t?.length?yield*bt(s??[{dies:e}],t):void 0;for(const p of w){yield;const W=a.get(p.testNumber),H=f?.specTally.get(p.testNumber),se=f?.stats.get(p.testNumber),Y=()=>r.map(I=>I.testValues?.[p.testNumber]).filter(I=>I!==void 0&&isFinite(I));let O;if(P(W)){const I=W;O={testNumber:p.testNumber,min:I.min,max:I.max,mean:I.mean,count:I.count,stddev:I.stddev,median:I.median,q1:I.q1,q3:I.q3}}else if(se)O={testNumber:p.testNumber,...se};else{const I=Y();if(!I.length)continue;O={testNumber:p.testNumber,...Et(I)}}let B=null,M=0;if(F(p)){const I=h.get(p.testNumber);if(I)({yieldPercent:B,totalDies:M}=I);else if(H)M=H.n,B=M>0?(M-H.fail)/M*100:null;else{const Z=Y();let ne=0;for(const J of Z)(p.limitLow!==void 0&&J<p.limitLow||p.limitHigh!==void 0&&J>p.limitHigh)&&ne++;M=Z.length,B=Z.length>0?(Z.length-ne)/Z.length*100:null}}R.push({entry:p,stats:O,specYieldPct:B,specN:M})}if(!R.length)return null;const S=new Map;for(const p of(A?f?.capability:void 0)??[])p.hasSpec&&S.set(p.testNumber,p.ppk);const g=S.size>0,b=new Set(R.map(p=>p.stats.count)).size===1?R[0].stats.count:null,k=l("div"),v=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:y.sm}),E=b!==null?` \xB7 N=${b.toLocaleString()}`:"",D=c?.populationLabel?`Test Values (${R.length}) \u2014 ${c.populationLabel}${E}`:`Test Values (${R.length})${E}`,V=ae(D);V.style.marginBottom="0",v.appendChild(V);const z=document.createElement("button");z.type="button",z.textContent="Test values CSV",Object.assign(z.style,{...ue("outlined")}),G(z),z.addEventListener("click",()=>{const p=["Test","Unit","N","Min","Q1","Median","Mean","Q3","Max","StdDev"];g&&p.push("Ppk");const W=R.some(B=>B.specYieldPct!==null&&B.specN!==B.stats.count);L&&(p.push("LSL","USL","Spec Yield %"),W&&p.push("Spec Yield N"));const H=[...Ke(c,p),...Ge(c)],Y=[[...H.map(B=>B.label),...p].map(me).join(",")],O=B=>Ee(B,void 0,"engineering");for(const{entry:B,stats:M,specYieldPct:I,specN:Z}of R){const ne=[...H.map(J=>J.constant??""),B.name,B.unit??"",String(M.count),O(M.min),O(M.q1),O(M.median),O(M.mean),O(M.q3),O(M.max),O(M.stddev)];if(g){const J=S.get(B.testNumber);ne.push(J==null?"":J.toFixed(3))}L&&(ne.push(B.limitLow!==void 0?O(B.limitLow):"",B.limitHigh!==void 0?O(B.limitHigh):"",I!==null?I.toFixed(1):""),W&&ne.push(B.limitLow!==void 0||B.limitHigh!==void 0?String(Z):"")),Y.push(ne.map(me).join(","))}Ae(Y.join(`
|
|
2
|
+
`),"test-values.csv","text/csv",i)}),v.appendChild(z),k.appendChild(v);const Q=l("table",{width:"100%",borderCollapse:"collapse",fontSize:T.body}),te=document.createElement("thead"),X=document.createElement("tr"),j=["Test"];b===null&&j.push("N"),d==="full"&&j.push("Min","Q1","Median"),j.push("Mean"),d==="full"&&j.push("Q3","Max","StdDev"),g&&j.push("Ppk"),d==="full"&&L&&j.push("LSL","USL"),L&&j.push("Spec yield");for(const p of j){const W=l("th",{textAlign:p==="Test"?"left":"right",fontWeight:"600",fontSize:T.meta,letterSpacing:pe,textTransform:"uppercase",color:_,padding:"3px 8px 5px",borderBottom:`1px solid ${$.menuBorder}`,whiteSpace:"nowrap"},p);X.appendChild(W)}te.appendChild(X),Q.appendChild(te);const U=document.createElement("tbody");for(const{entry:p,stats:W,specYieldPct:H,specN:se}of R){const Y=M=>Ee(M,p.unit,o),O=document.createElement("tr"),B=(M,I="right")=>{const Z=l("td",{textAlign:I,padding:`${y.xs} ${y.md}`,borderBottom:`1px solid ${$.menuBorder}`,color:$.text,whiteSpace:"nowrap"},M);O.appendChild(Z)};if(B(p.name,"left"),b===null&&B(`${W.count}`),d==="full"&&(B(Y(W.min)),B(Y(W.q1)),B(Y(W.median))),B(Y(W.mean)),d==="full"&&(B(Y(W.q3)),B(Y(W.max)),B(Y(W.stddev))),g){const M=S.get(p.testNumber);B(M==null?"\u2014":M.toFixed(2))}d==="full"&&L&&(B(p.limitLow!==void 0?Y(p.limitLow):"\u2014"),B(p.limitHigh!==void 0?Y(p.limitHigh):"\u2014")),L&&B(H!==null?b!==null&&se===b?`${H.toFixed(1)}%`:`${H.toFixed(1)}% (N=${se})`:"\u2014"),U.appendChild(O)}Q.appendChild(U);const N=l("div",{overflowX:"auto"});if(N.appendChild(Q),!m)return k.appendChild(N),k;v.remove();const{outer:K}=ie(D,!0,void 0,{stateKey:"testValues",panel:m,render:p=>p.appendChild(N),control:()=>z});return K}export function buildFunctionalTestSection(e,t,o,n,i,c){const s=o??it(e,t);if(!s?.length)return null;const d=new Set(s.map(g=>g.totalDies)).size===1?s[0].totalDies:null,r=d!==null?` \xB7 N=${d.toLocaleString()}`:"",a=l("div"),h=l("div",{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:y.sm}),u=i?.populationLabel?`Functional Tests (${s.length}) \u2014 ${i.populationLabel}${r}`:`Functional Tests (${s.length})${r}`,w=ae(u);w.style.marginBottom="0",h.appendChild(w);const L=document.createElement("button");L.type="button",L.textContent="Functional CSV",Object.assign(L.style,{...ue("outlined")}),G(L),L.addEventListener("click",()=>{const g=["Test","N","Pass","Fail","Pass Rate %"],C=[...Ke(i,g),...Ge(i)],b=[[...C.map(k=>k.label),...g].map(me).join(",")];for(const k of s)b.push([...C.map(v=>v.constant??""),k.label,String(k.totalDies),String(k.passDies),String(k.failDies),k.passRatePercent!==null?k.passRatePercent.toFixed(1):""].map(me).join(","));Ae(b.join(`
|
|
3
|
+
`),"functional-tests.csv","text/csv",n)}),h.appendChild(L),a.appendChild(h);const R=l("table",{width:"100%",borderCollapse:"collapse",fontSize:T.body}),P=document.createElement("thead"),F=document.createElement("tr"),A=d===null?["Test","N","Pass","Fail","Pass rate"]:["Test","Pass","Fail","Pass rate"];for(const g of A)F.appendChild(l("th",{textAlign:g==="Test"?"left":"right",fontWeight:"700",fontSize:T.body,letterSpacing:pe,textTransform:"uppercase",color:_,padding:"3px 8px 5px",borderBottom:`1px solid ${$.menuBorder}`,whiteSpace:"nowrap"},g));P.appendChild(F),R.appendChild(P);const x=document.createElement("tbody");for(const g of s){const C=document.createElement("tr"),b=(k,v="right")=>{C.appendChild(l("td",{textAlign:v,padding:`${y.xs} ${y.md}`,borderBottom:`1px solid ${$.menuBorder}`,color:ee,whiteSpace:"nowrap"},k))};b(g.label,"left"),d===null&&b(String(g.totalDies)),b(String(g.passDies)),b(String(g.failDies)),b(g.passRatePercent!==null?`${g.passRatePercent.toFixed(1)}%`:"\u2014"),x.appendChild(C)}R.appendChild(x);const f=l("div",{overflowX:"auto"});if(f.appendChild(R),!c)return a.appendChild(f),a;h.remove();const{outer:S}=ie(u,!0,void 0,{stateKey:"functionalTests",panel:c,render:g=>g.appendChild(f),control:()=>L});return S}export function buildLotFunctionalSection(e,t,o,n,i){const c=o?.length?{perWaferMetadata:o.map(m=>m.wafer??{}),populationLabel:`${o.length} wafer${o.length===1?"":"s"} pooled`}:void 0,s=yt(o);return s?buildFunctionalTestSection(e,t,s,n,c,i):buildFunctionalTestSection(e,t,void 0,n,c,i)}const re={unusual:0,notable:1,info:2};function $e(e){return l("span",{display:"inline-block",width:"7px",height:"7px",borderRadius:q.pill,background:fe(e),flexShrink:"0"})}function Ue(e,t){let o=e.summary;return t&&(o.startsWith(`${t} has `)?o=o.slice(t.length+5):o.startsWith(`${t} `)&&(o=o.slice(t.length+1))),o=le(o),o.length?o[0].toUpperCase()+o.slice(1):o}export function buildFindingsSection(e,t,o,n,i=!1){if(!e.length)return null;const c=e.some(x=>x.severity==="unusual"||x.severity==="notable"),s=c?e.filter(x=>x.severity!=="info").length.toString():void 0,{outer:m,content:d}=i?(()=>{const x=l("div");return{outer:x,content:x}})():ie(`Findings (${e.length})`,c,s);function r(x){return x.reduce((f,S)=>re[S.severity]<re[f]?S.severity:f,"info")}function a(x){const f=new Map;for(const g of x){const C=`${g.comparison.family}\0${g.comparison.left}`,b=f.get(C)??[];b.push(g),f.set(C,b)}const S=[...f.entries()].map(([g,C])=>{const b=[...C].sort((k,v)=>re[k.severity]-re[v.severity]);return{key:g,findings:b,worst:r(b)}});return S.sort((g,C)=>re[g.worst]-re[C.worst]),S}function h(x,f){const g={ring:"Ring",quadrant:"Quadrant",sector:"Sector","reticle-position":"Reticle","test-site":"Test site",cluster:"Cluster","edge-arc":"Edge arc",wafer:"Wafer"}[x]??x;return f.toLowerCase().startsWith(g.toLowerCase())?f:`${g}: ${f}`}const u=e.filter(x=>x.comparison.family==="spatial-pattern"),w=new Set([...u.flatMap(x=>x.relatedIds??[]),...e.flatMap(x=>x.absorbedIds??[])]),L=e.filter(x=>x.comparison.family!=="spatial-pattern"&&!w.has(x.id)),R=a(L);function P(x,f=!1,S){const g=n===x.id,C=document.createElement("button");return C.type="button",C.dataset.wmapFinding=x.id,C.textContent=Ue(x,S),de(C,x.summary),C.setAttribute("aria-current",g?"true":"false"),Object.assign(C.style,{border:`1px solid ${$.menuBorder}`,borderLeft:`3px solid ${fe(x.severity)}`,background:g?$.bgActive:$.menuBg,borderRadius:q.container,padding:f?"6px 10px":"8px 10px",textAlign:"left",fontSize:f?"10px":"11px",fontWeight:g?"600":"400",color:$.text,cursor:"pointer",width:"100%",marginBottom:y.xs}),G(C,"bare"),C.addEventListener("click",()=>o(x,C)),C}const F=le(rt(e)??"");if(F){const x=l("div",{background:$.bgActive,borderRadius:q.container,padding:`${y.md} ${y.lg}`,marginBottom:y.md,display:"flex",gap:y.md,alignItems:"flex-start"}),f=l("span",{flex:"1",fontSize:T.body,lineHeight:oe.base,color:$.text},F);x.appendChild(f);const S=l("button",{flexShrink:"0",border:"none",background:"none",fontSize:T.body,color:$.icon,cursor:"pointer",padding:"0",lineHeight:oe.base,whiteSpace:"nowrap"},"Detail \u25B8");S.type="button",G(S,"bare"),S.addEventListener("click",()=>{const g=Ie({title:"Findings Summary",onClose:()=>{},anchor:S}),C=l("p",{fontSize:"16px",lineHeight:oe.base,color:$.text,padding:`${y.xxl} ${y.xxxl} ${y.xxl}`,margin:"0",borderBottom:`1px solid ${$.menuBorder}`,flexShrink:"0"},F);g.contentWrap.appendChild(C);const b=l("div",{overflowY:"auto",padding:`${y.xxl} ${y.xxxl}`,flex:"1"}),k=(v,E)=>{const D=l("div",{display:"flex",alignItems:"center",gap:"7px",fontSize:T.sub,fontWeight:"600",color:ee,marginTop:y.lg,marginBottom:y.xs});return D.appendChild($e(v)),D.appendChild(l("span",{},E)),D};for(const v of u){b.appendChild(k(v.severity,v.comparison.left)),b.appendChild(l("div",{fontSize:T.body,color:$.text,padding:"4px 0 4px 15px",marginBottom:y.xxs},le(v.summary)));const E=e.filter(D=>v.relatedIds?.includes(D.id));for(const D of E)b.appendChild(l("div",{fontSize:T.body,color:_,padding:"2px 0 2px 23px",marginBottom:y.xxs},le(D.summary)))}for(const v of R){const[E,D]=v.key.split("\0");b.appendChild(k(v.worst,h(E,D)));for(const V of v.findings)b.appendChild(l("div",{fontSize:T.body,color:$.text,padding:"4px 0 4px 15px",marginBottom:y.xxs},Ue(V,D)))}g.contentWrap.appendChild(b),Object.assign(g.contentWrap.style,{flexDirection:"column",overflow:"hidden"})}),x.appendChild(S),d.appendChild(x)}let A=!0;for(const x of u){A||d.appendChild(l("div",{height:"1px",background:$.separator,margin:"4px 0"})),A=!1;const f=e.filter(k=>x.relatedIds?.includes(k.id)),S=f.length>0,g=l("div",{position:"relative",marginBottom:S?"2px":"4px"}),C=n===x.id,b=document.createElement("button");if(b.type="button",b.dataset.wmapFinding=x.id,Object.assign(b.style,{border:`1px solid ${$.menuBorder}`,borderLeft:`3px solid ${fe(x.severity)}`,background:C?$.bgActive:$.menuBg,borderRadius:q.container,padding:"8px 32px 8px 10px",textAlign:"left",fontSize:T.body,fontWeight:C?"600":"500",color:$.text,cursor:"pointer",width:"100%"}),b.textContent=le(x.summary),de(b,x.summary),b.setAttribute("aria-current",C?"true":"false"),G(b,"bare"),b.addEventListener("click",()=>o(x,b)),g.appendChild(b),S){const k=l("div",{display:"none",paddingLeft:"12px",marginBottom:y.xs});for(const D of f)k.appendChild(P(D,!0));g.appendChild(k);let v=!1;const E=l("button",{position:"absolute",top:"50%",right:"8px",transform:"translateY(-50%)",border:"none",background:"none",fontSize:T.body,color:$.icon,cursor:"pointer",padding:`${y.xxs} ${y.xs}`,lineHeight:oe.none},"\u25B8");G(E,"bare"),E.type="button",de(E),E.setAttribute("aria-label","Show supporting findings"),E.setAttribute("aria-expanded","false"),E.addEventListener("click",D=>{D.stopPropagation(),v=!v,k.style.display=v?"block":"none",E.textContent=v?"\u25BE":"\u25B8";const V=v?"Hide supporting findings":"Show supporting findings";E.setAttribute("aria-label",V),E.setAttribute("aria-expanded",String(v))}),g.appendChild(E)}d.appendChild(g)}for(const x of R){A||d.appendChild(l("div",{height:"1px",background:$.separator,margin:"4px 0"})),A=!1;const[f,S]=x.key.split("\0"),g=l("div",{display:"flex",alignItems:"center",gap:y.sm,fontSize:T.body,fontWeight:"600",color:ee,margin:"2px 0 4px"});g.appendChild($e(x.worst)),g.appendChild(l("span",{},h(f,S))),d.appendChild(g);for(const C of x.findings)d.appendChild(P(C,!1,S))}return m}const Ft=[{value:"",label:"All kinds"},{value:"yield",label:"Yield"},{value:"hardBin",label:"Hard bin"},{value:"softBin",label:"Soft bin"},{value:"test",label:"Test value"},{value:"functionalTest",label:"Functional test"},{value:"spatialPattern",label:"Spatial pattern"}],Dt=[{value:"",label:"All regions"},{value:"ring",label:"Ring"},{value:"quadrant",label:"Quadrant"},{value:"reticle-position",label:"Reticle position"},{value:"test-site",label:"Test site"},{value:"wafer",label:"Wafer"},{value:"sector",label:"Sector"},{value:"cluster",label:"Cluster"},{value:"edge-arc",label:"Edge arc"},{value:"spatial-pattern",label:"Spatial pattern"}],Rt=["unusual","notable","info"],Mt={unusual:"Unusual",notable:"Notable",info:"Info"};function It(e,t,o){const n=l("div",{display:"flex",flexWrap:"wrap",alignItems:"center",gap:y.lg,marginBottom:y.lg,paddingBottom:"10px",borderBottom:`1px solid ${$.separator}`}),i={unusual:0,notable:0,info:0};for(const r of e)i[r.severity]++;const c=Rt.filter(r=>i[r]>0),s=new Set(t.severity===void 0?c:Array.isArray(t.severity)?t.severity:[t.severity]),m=l("div",{display:"flex",gap:y.xs,flexWrap:"wrap"});for(const r of c){const a=document.createElement("button");a.type="button";const h=()=>{const u=s.has(r);a.setAttribute("aria-pressed",u?"true":"false"),Object.assign(a.style,{display:"inline-flex",alignItems:"center",gap:"5px",border:`1px solid ${$.menuBorder}`,borderRadius:q.pill,background:u?$.bgActive:"none",opacity:u?"1":"0.5",color:$.text,fontSize:T.body,padding:`${y.xxs} ${y.md}`,cursor:"pointer"}),a.dataset.on=u?"true":"false"};a.appendChild($e(r)),a.appendChild(l("span",{},`${Mt[r]} ${i[r]}`)),de(a,()=>s.has(r)?"Click to hide these findings":"Click to show these findings"),h(),G(a,"bare"),a.addEventListener("click",()=>{s.has(r)?s.delete(r):s.add(r),t.severity=s.size===c.length?void 0:[...s],o()}),m.appendChild(a)}n.appendChild(m);const d=t.kind!==void 0||t.family!==void 0;return(e.length>=Wt||d)&&(n.appendChild(De("Kind:",Ft,t.kind??"",r=>{t.kind=r||void 0,o()},{maxWidth:"130px"})),n.appendChild(De("Region:",Dt,t.family??"",r=>{t.family=r||void 0,o()},{maxWidth:"150px"}))),n}const Wt=8;function At(e,t){const o=l("div",{display:"flex",flexDirection:"column",gap:y.xs,padding:y.md,marginBottom:y.md,background:$.bgHover,borderRadius:q.control},void 0,t);if(o.appendChild(l("div",{fontSize:T.body,color:$.value},e.message,t)),e.detail&&o.appendChild(l("div",{fontSize:T.meta,color:_},e.detail,t)),e.actionLabel&&e.onAction){const n=l("button",ue("outlined"),e.actionLabel,t);n.type="button",n.style.alignSelf="flex-start",n.style.marginTop=y.xs,G(n),n.addEventListener("click",e.onAction),o.appendChild(n)}return o}export function buildFindingsSectionWithFilter(e,t,o,n,i,c,s){if(!e.findings.length&&!s)return null;const m=e.findings.some(L=>L.severity==="unusual"||L.severity==="notable"),d=m?e.findings.filter(L=>L.severity!=="info").length.toString():void 0,r=c?.ownerDocument??document,{outer:a,content:h}=ie(`Findings (${e.findings.length})`,m||!!s,d,{stateKey:"findings",panel:c});if(s&&h.appendChild(At(s,r)),!e.findings.length)return a;h.appendChild(It(e.findings,n,i));const u=lt(e,n);if(!u.length)return h.appendChild(l("div",{color:_,fontSize:T.body,textAlign:"center",padding:`${y.xxl} ${y.md}`},"No findings match the current filter.")),a;const w=buildFindingsSection(u,e,t,o,!0);return w&&h.appendChild(w),a}export function buildLotTestSection(...e){return ge(buildLotTestSectionSteps(...e))}export function*buildLotTestSectionSteps(e,t,o,n,i,c,s,m="full"){const d=n?.length?{perWaferMetadata:n.map(a=>a.wafer??{}),populationLabel:`${n.length} wafer${n.length===1?"":"s"} pooled`}:void 0;let r;if(n?.length&&n.every(a=>a.stats.perTestStats!==void 0)){const a=new Map;for(const h of n)for(const u of h.stats.perTestStats??[]){const w=a.get(u.testNumber);w?(w.n+=u.count,w.sum+=u.mean*u.count,w.min=Math.min(w.min,u.min),w.max=Math.max(w.max,u.max)):a.set(u.testNumber,{n:u.count,sum:u.mean*u.count,min:u.min,max:u.max})}r={perTestStats:[...a.entries()].map(([h,u])=>({testNumber:h,count:u.n,min:u.min,max:u.max,mean:u.sum/u.n}))}}if(n?.length&&n.every(a=>a.stats.testSpecYield!==void 0)){const a=new Map;for(const h of n)for(const u of h.stats.testSpecYield??[]){const w=a.get(u.testNumber);w?(w.passDies+=u.passDies,w.totalDies+=u.totalDies):a.set(u.testNumber,{passDies:u.passDies,totalDies:u.totalDies})}r={...r,testSpecYield:[...a.entries()].map(([h,u])=>({testNumber:h,totalDies:u.totalDies,yieldPercent:u.totalDies>0?u.passDies/u.totalDies*100:null}))}}return yield*buildTestSectionSteps(e,t,o,r,i,d,c,s,m)}export function createSummaryPanelEl(e,t,o=document){const n=e==="top"||e==="bottom",i=l("div",{background:kt,color:$.text,fontVariantNumeric:"tabular-nums",border:Pe,borderRadius:q.container,padding:y.xl,overflowY:"auto",overflowX:n?"auto":"hidden",flexShrink:"0",boxSizing:"border-box",fontFamily:T.family,fontSize:T.sub,boxShadow:Re.panel},void 0,o);return e==="right"&&(i.style.marginRight=t),e==="left"&&(i.style.marginLeft=t),e==="bottom"&&(i.style.marginBottom=t),n?(i.style.height="180px",i.style.width="100%"):(i.style.width="300px",i.style.maxHeight="100%"),i}export function wrapWithSummaryPanel(e,t,o){const n=o==="top"||o==="bottom",i=l("div",{display:"flex",flexDirection:n?"column":"row",gap:y.md,width:"100%",height:"100%",flex:"1 1 0",minHeight:"0",boxSizing:"border-box",alignItems:n?"flex-start":"stretch"});return e.style.flex="1 1 0",e.style.minWidth="0",e.style.minHeight="0",o==="right"||o==="bottom"?(i.appendChild(e),i.appendChild(t)):(i.appendChild(t),i.appendChild(e)),i}function Qe(e){return l("div",{fontSize:T.heading,fontWeight:"600",color:ee,marginBottom:y.lg},e)}function xe(e,t){const o=l("button",ue("outlined"),e);return o.type="button",G(o),o.addEventListener("click",t),o}function Xe(...e){const t=e.filter(n=>!!n);if(!t.length)return null;const o=l("div",{display:"flex",flexWrap:"wrap",gap:y.sm,marginBottom:y.lg});for(const n of t)o.appendChild(n);return o}function Ze(e,t,o,n,i,c,s,m,d,r,a){const h=e.ownerDocument,u=Ie({title:"Die list",onClose:()=>{},anchor:e,ownerDocument:h});u.contentWrap.style.padding="14px 16px";const w=Ct(t,o,{...s,title:n,onSaveText:m,waferMetadata:i,metadataFields:c,extraColumn:d,getWafer:r,ringCount:a,ownerDocument:h});w&&u.contentWrap.appendChild(w)}export function renderWaferSummaryContent(...e){ge(renderWaferSummaryContentSteps(...e))}export function*renderWaferSummaryContentSteps(e,t){const o=e.scrollTop;e.innerHTML="";const{wafer:n,dies:i,yieldSummary:c,dataCoverage:s,hbinDefs:m,sbinDefs:d,testDefs:r,statsSummary:a,passBins:h=[1],ringCount:u=4,binColors:w,plotMode:L,fallbackFormat:R,onFindingClick:P,activeFindingId:F=null,findingsFilter:A,onFindingsFilterChange:x,findingsNotice:f,onSaveText:S,metadataFields:g,dieListOptions:C,metadataShownElsewhere:b}=t;e.appendChild(Qe("Wafer Summary"));const k=t.warnings??Te({statsSummary:a});k.length&&e.appendChild(Se(k,e.ownerDocument));const v=c&&s?xe("Summary report",()=>{We(st({wafer:n,dies:i,yield:c,dataCoverage:s,hbinDefs:m,sbinDefs:d,testDefs:r,passBins:h,ringCount:u},a),{anchor:e})}):null,E=(C?.enabled??!0)&&i.length?xe("View die list",()=>{Ze(e,i,r,`Die list \u2014 ${i.length} dies`,n.metadata,g,C,S,void 0,()=>n,u)}):null,D=Xe(v,E);D&&e.appendChild(D);let V=!0;const z=j=>{j&&(V||e.appendChild(Ye()),V=!1,e.appendChild(j))},Q=a?.stats.isLotStack?a.stats:void 0,te=Q?.lotSize!==void 0?{lotSize:Q.lotSize,aggrMethod:mt(Q.aggregationMethod)}:void 0;b||z(buildMetadataInfoSection([{metadata:n.metadata??void 0}],te)),c&&s&&z(buildYieldSection(c,s,h)),yield,a&&P&&A&&x&&(z(buildFindingsSectionWithFilter(a,P,F,A,x,e,f)),yield),z(buildBinBreakdownSection({dies:i,hbinDefs:m,sbinDefs:d,binColors:w,hardCounts:a?.stats.hardBinCounts,softCounts:a?.stats.softBinCounts,plotMode:L,passBins:h,panel:e})),yield,z(buildRegionYieldPanelSection({diesByWafer:[i],allWafers:[n],ringCount:u,passBins:h,panel:e})),yield;const X=n.metadata?{waferMetadata:n.metadata}:void 0;z(yield*buildTestSectionSteps(i,r,R,a?.stats,S,X,[{dies:i}],e,"compact")),yield,z(buildFunctionalTestSection(i,r,a?.stats.functionalYield,S,X,e)),e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=o}export function reportMapsFromItems(e,t,o){const n=[];return e.forEach((i,c)=>{!i?.wafer||!i.dies||n.push({...i,wafer:i.wafer,dies:i.dies,label:ye(i,c),passBins:[...Ce(i,t)],ringCount:i.ringCount??o})}),n}export function renderLotSummaryContent(...e){ge(renderLotSummaryContentSteps(...e))}export function*renderLotSummaryContentSteps(e,t){const o=e.scrollTop;e.innerHTML="";const{lotSummary:n,items:i,hbinDefs:c,sbinDefs:s,testDefs:m,passBins:d=[1],ringCount:r=4,binColors:a,plotMode:h,fallbackFormat:u,onFindingClick:w,activeFindingId:L=null,onWaferClick:R,findingsFilter:P,onFindingsFilterChange:F,findingsNotice:A,onSaveText:x,dieListOptions:f,findingsFor:S}=t;e.appendChild(Qe(`Summary \u2014 ${ft(Ne(n.perWafer.map(N=>N.summary.wafer)))}`));const g=t.warnings??Te({lotStatsSummary:n});g.length&&e.appendChild(Se(g,e.ownerDocument));const C=xe("Summary report",()=>{We(at(reportMapsFromItems(i,d,r)),{anchor:e})}),b=[],k=[],v=[],E=[],D=[],V=new WeakMap,z=new WeakMap;for(let N=0;N<i.length;N++){yield;const K=i[N];if(!K){k.push([]);continue}K.wafer&&(b.push(K.wafer),E.push(K.dies??[]),D.push(Ce(K,d)));const p=K.dies??[],W=ye(K,N);for(const H of p)V.set(H,W),K.wafer&&z.set(H,K.wafer);k.push(p);for(const H of p)v.push(H)}const Q=f?.enabled??!0?xe("View die list",()=>{v.length&&Ze(e,v,m,`Die list \u2014 ${v.length} dies across ${i.length} wafer${i.length===1?"":"s"}`,Le(i.filter(N=>!!N).map(N=>({metadata:N.wafer?.metadata}))),i.find(N=>N?.metadataFields?.length)?.metadataFields,f,x,{label:"Wafer",get:N=>V.get(N)},N=>z.get(N),r)}):null,te=Xe(C,Q);te&&e.appendChild(te);const X=i.map(N=>N?.statsSummary).filter(N=>!!N);let j=!0;const U=N=>{N&&(j||e.appendChild(Ye()),j=!1,e.appendChild(N))};U(buildLotOverviewSection(n,X)),yield,w&&P&&F&&(U(buildFindingsSectionWithFilter(n,w,L,P,F,e,A)),yield),U(buildPerWaferYieldSection(n,i,R,e,S)),yield,U(buildBinBreakdownSection({dies:v,hbinDefs:c,sbinDefs:s,plotMode:h,passBins:d,panel:e,binColors:a??pt(i.flatMap(N=>N?[{dies:N.dies??[],passBins:Ce(N,d)}]:[]),{hbinDefs:c,sbinDefs:s}).colors})),yield,U(buildRegionYieldPanelSection({diesByWafer:E,allWafers:b,ringCount:r,passBins:N=>D[N],panel:e})),yield,m?.length&&(U(yield*buildLotTestSectionSteps(v,m,u,X,x,k.map(N=>({dies:N})),e,"compact")),yield,U(buildLotFunctionalSection(v,m,X,x,e))),e.appendChild(l("div",{height:"12px",flexShrink:"0"})),e.scrollTop=o}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{fontPx as Ct}from"./toolbar.js";import{findTestDef as Ce,buildMapTitle as _e}from"../renderer/buildView.js";import{sortBinsForDisplay as ve}from"../stats/binPareto.js";import{affineInvert as to,affineVector as Be}from"../core/transforms.js";import{compareNatural as eo}from"../core/utils.js";import{resolveValueColorFn as oo}from"../renderer/colorSchemes.js";import{NO_DATA_FILL as no}from"../renderer/colorMap.js";import{SPEC_PASS_FILL as Pe,SPEC_FAIL_LOW as so,SPEC_FAIL_HIGH as De,contrastTextColor as We}from"../renderer/colorMap.js";import{fmt as Ie,fmtColorbarAxis as lo}from"../renderer/fmt.js";import{resolveCanvasTheme as io}from"./canvasTheme.js";import{hitGridDims as ao}from"./hitGrid.js";function Fe(g,e,P,b,r){g.moveTo(e,P+r*b),g.lineTo(e-b*.9,P-r*b*.6),g.lineTo(e+b*.9,P-r*b*.6),g.closePath()}const co=new Set(["value","stackedValues","stackedBins","stackedSoftBins"]),ro=new Set(["hardBin","softBin","metadata"]),J=()=>`${Ct(-2)}px system-ui, sans-serif`,Ne=3,Ut=2,fo=6,ho=20,go=()=>`600 ${Ct()}px system-ui, sans-serif`,Kt=()=>`${Ct(-1)}px system-ui, sans-serif`,mo=()=>`600 ${Ct(-1)}px system-ui, sans-serif`,qt=128,uo=()=>`${Ct(-2)}px system-ui, sans-serif`,Oe=4,D=17,H=11;export const BIN_LEGEND_W=110,BIN_LEGEND_W_COMPACT=64,BIN_LEGEND_ADAPT_COMPACT=280,BIN_LEGEND_ADAPT_FLOATING=180,BIN_LEGEND_MIN_CANVAS_W=150,BIN_LEGEND_MIN_CANVAS_H=120;const zt=28,gt=5,ft=8,Ee=15,He=12,Ye=6;export function toCanvas(g,e,P={}){return drawMapCanvas(g,e,P)}export function drawMapCanvas(g,e,P={}){const b=io(g.parentElement??g),{padding:r=16,showColorbar:G=!0,markFailingDies:X=!1,colorbarWidth:B=16,background:mt=b.background,legendPosition:Z="default",legendOffset:ut,showAxes:pt=!1,diePitchMm:w,viewport:K,activeBin:O,hoverBin:q,fallbackFormat:Tt,minRightReserve:_t,hbinDefs:nt,sbinDefs:xt,metadataFields:Mt,showTitle:U=!0}=P,W=Math.floor(g.clientWidth||g.width),Y=Math.floor(g.clientHeight||g.height),yt=W>=BIN_LEGEND_MIN_CANVAS_W&&Y>=BIN_LEGEND_MIN_CANVAS_H,St=G&&yt&&co.has(e.plotMode)&&e.passFailDisplay==="off",Lt=G&&yt&&ro.has(e.plotMode),j=G&&yt&&e.plotMode==="value"&&(e.passFailDisplay==="spec"&&e.specCounts!==void 0||e.passFailDisplay==="test"&&e.passFailCounts!==void 0),Q=Lt||j,Bt=Lt&&e.plotMode!=="metadata"&&e.binCounts?ve(e.binCounts.entries(),e.plotMode==="softBin"?e.binColors.pass.soft:e.binColors.pass.hard):[],Pt=Lt&&e.plotMode==="metadata"&&e.metadataCounts?[...e.metadataColorMap?.keys()??[...e.metadataCounts.keys()].sort(eo)].map(o=>[o,e.metadataCounts.get(o)??0]):[],v=[];if(j&&e.passFailDisplay==="test"){const o=e.passFailCounts;v.push({key:-1,color:Pe,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),v.push({key:-2,color:De,label:"Fail",compactLabel:"Fail",count:o.fail,tooltip:`Fail \xB7 ${o.fail} dies`})}else if(j){const o=e.specCounts,f=Ce(e.testDefs,e.activeTest);v.push({key:-1,color:Pe,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),f?.limitHigh!==void 0&&v.push({key:-2,color:De,label:"Fail high",compactLabel:"Fail hi",count:o.failHigh,tooltip:`Fail high \xB7 ${o.failHigh} dies`}),f?.limitLow!==void 0&&v.push({key:-3,color:so,label:"Fail low",compactLabel:"Fail lo",count:o.failLow,tooltip:`Fail low \xB7 ${o.failLow} dies`})}const Dt=j?v.length:e.plotMode==="metadata"?Pt.length:Bt.length,M=(g.ownerDocument.defaultView??window).devicePixelRatio??1;if(W<=0||Y<=0)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const k=Z!=="default"?Z:W<BIN_LEGEND_ADAPT_FLOATING?"floating":W<BIN_LEGEND_ADAPT_COMPACT?"compact":"default",ht=k==="default"||k==="compact",he=k==="left",Zt=k==="bottom",kt=k==="top",At=k==="floating",Wt=k==="compact"||k==="floating"?BIN_LEGEND_W_COMPACT:BIN_LEGEND_W,de=Math.floor((Y-2*r)/D),ge=Dt>de?de-1:Dt,me=Q&&Zt?ge*D:0,jt=Q&&kt?ge*D:0,Qt=e.plotMode==="stackedBins"||e.plotMode==="stackedSoftBins",vt=Ce(e.testDefs,e.activeTest),Ge=Qt?"Count":vt?.name??(e.activeTest!=null?`Test ${e.activeTest}`:void 0),Xe=Qt?void 0:vt?.unit,[It,Ft]=e.valueRange,{tickFmt:$e}=lo(Ft,Ge,Xe,Tt),Nt=e.allIntegerValues?o=>String(Math.round(o)):$e;let te=0;if(St){const o=g.getContext("2d");o.font=J();const f=[Nt(It),Nt(Ft)];It<0&&f.push(Nt(-Math.max(Math.abs(It),Math.abs(Ft))));for(const s of f)te=Math.max(te,o.measureText(s).width)}const ue=Math.max(ho,Math.ceil(te)+Ne+Ut+fo-r),ee=St?B+ue+8:Q&&ht?Wt:0,oe=Q&&he?Wt:0,ne=pt?32:0,Ot=pt?36:0,we=_t!=null?Math.max(ee,_t):ee,Ve=W-2*r-we-Ot-oe,pe=Y-2*r-ne-me-jt;g.width=Math.round(W*M),g.height=Math.round(Y*M);const t=g.getContext("2d");t.scale(M,M),t.fillStyle=mt,t.fillRect(0,0,W,Y);const $=e.hoverPoints;if(!$.length)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const Et=e.rectangles[0],se=Et?Et.width/2:0,le=Et?Et.height/2:0,Ht=e.dieBounds,xe=(Ht?.minX??0)-se,Ue=(Ht?.maxX??0)+se,Ke=(Ht?.minY??0)-le,Me=(Ht?.maxY??0)+le,ye=Ue-xe,be=Me-Ke;let tt,et,A;if(K)({originX:tt,originY:et,ppm:A}=K);else{const f=e.notchDir,s=f&&f.y>.1?13:0,a=f&&f.y<-.1?13:0,c=f&&f.x<-.1?13:0,h=f&&f.x>.1?13:0,d=Ve-c-h,i=pe-s-a;A=Math.min(d/ye,i/be),tt=r+Ot+oe+c+(d-ye*A)/2-xe*A,et=r+jt+s+(i-be*A)/2+Me*A}const Yt=K?.snapDist??Math.max(se,le,1)*1.5,Gt=tt+e.waferCenter.x*A,qe=et-e.waferCenter.y*A,ie=e.waferRadius*A,st=o=>{const f=Math.abs(o-qe);return f>=ie?0:Math.sqrt(ie*ie-f*f)},lt=(o,f,s,a,c,h,d=go(),i=b.text)=>{if(!o)return;t.save(),t.fillStyle=i,t.font=d,t.textAlign=a,t.textBaseline=c;const y=a==="right"?f-h:h-f;let l=o;if(y>8&&t.measureText(l).width>y){for(;l.length>1&&t.measureText(l+"\u2026").width>y;)l=l.slice(0,-1);l=l+"\u2026"}y>8&&t.fillText(l,f,s),t.restore()};t.save(),t.setTransform(A*M,0,0,-A*M,tt*M,et*M);const dt=1/A,ze=1/(A*M);if(e.rectangles.length>0){const o=new Map;for(const s of e.rectangles){const a=String(s.fill);let c=o.get(a);c||(c=[],o.set(a,c)),c.push(s)}for(const[s,a]of o){t.beginPath();for(const c of a)t.rect(c.x-c.width/2,c.y-c.height/2,c.width,c.height);t.fillStyle=s,t.fill()}t.strokeStyle="rgba(0,0,0,0.18)",t.lineWidth=.5*ze,t.beginPath();for(const s of e.rectangles)t.rect(s.x-s.width/2,s.y-s.height/2,s.width,s.height);if(t.stroke(),X){const s=e.rectangles.filter(a=>a.binFail);if(s.length>0){const a=new Map;for(const c of s){const h=We(String(c.fill));let d=a.get(h);d||(d=[],a.set(h,d)),d.push(c)}t.save(),t.lineCap="butt";for(const[c,h]of a){t.strokeStyle=c,t.globalAlpha=.55,t.lineWidth=Math.max(.6*dt,Math.min(h[0].width,h[0].height)*.1);for(const d of h){const i=d.width/2,y=d.height/2,l=Math.max(1.6*dt,Math.min(d.width,d.height)/3);t.save(),t.beginPath(),t.rect(d.x-i,d.y-y,d.width,d.height),t.clip(),t.beginPath();for(let T=-(i+y);T<=i+y;T+=l)t.moveTo(d.x-i+T,d.y-y),t.lineTo(d.x-i+T+d.height,d.y+y);t.stroke(),t.restore()}}t.restore()}}const f=e.rectangles.filter(s=>s.specMark);if(f.length>0){const s=new Map;for(const c of f){const h=We(String(c.fill)),d=`${c.specMark}|${h}`;let i=s.get(d);i||(i=[],s.set(d,i)),i.push(c)}const a=2.5*dt;t.lineJoin="round";for(const[c,h]of s){const[d,i]=c.split("|"),y=i==="#000000"?"#ffffff":"#000000",l=d==="failHigh"?1:-1,T=Math.min(h[0].width,h[0].height),I=Math.max(2.5*dt,T*.3);t.beginPath();for(const R of h)Fe(t,R.x,R.y,I,l);t.strokeStyle=y,t.lineWidth=a,t.stroke(),t.fillStyle=i,t.fill()}}}for(const o of e.overlays){t.beginPath();for(const f of o.points){for(let s=0;s<f.length;s++)s===0?t.moveTo(f[s].x,f[s].y):t.lineTo(f[s].x,f[s].y);o.closed&&t.closePath()}o.fill&&!o.fill.startsWith("rgba(0,0,0,0)")&&(t.fillStyle=o.fill,t.fill()),o.kind==="ring-boundary"||o.kind==="quadrant-boundary"||o.kind==="reticle"?(t.strokeStyle="rgba(0,0,0,0.45)",t.lineWidth=3*dt,t.stroke(),t.strokeStyle="rgba(255,255,255,0.8)",t.lineWidth=1*dt,t.stroke()):(t.strokeStyle=o.lineColor,t.lineWidth=o.lineWidth*dt,t.stroke())}if(t.restore(),e.notchDir){const o=tt+e.waferCenter.x*A,f=et-e.waferCenter.y*A,s=e.notchDir.x,a=-e.notchDir.y,c=e.waferRadius*A,h=4,d=9,i=5,y=o+s*(c+h),l=f+a*(c+h),T=y+s*d,I=l+a*d,R={x:-a,y:s},F=r+Ot+oe,E=W-r-ee,V=r+jt,at=Y-r-me-ne;t.save(),t.beginPath(),t.rect(F,V,E-F,at-V),t.clip(),t.beginPath(),t.moveTo(y,l),t.lineTo(T+R.x*i,I+R.y*i),t.lineTo(T-R.x*i,I-R.y*i),t.closePath(),t.fillStyle=b.text,t.fill(),t.restore()}t.save(),t.textAlign="center",t.textBaseline="middle";let Te=0;if(e.rectangles.length>0){let o=1;for(const h of e.texts)h.role!=="indicator"&&h.text.length>o&&(o=h.text.length);const f=e.rectangles[0].width*A,s=e.rectangles[0].height*A,a=Math.min(f,s),c=f/o;Te=Math.max(8,Math.min(64,Math.round(Math.min(a*.55,c*1.8))))}for(const o of e.texts){const f=tt+o.x*A,s=et-o.y*A;t.font=`${o.role==="indicator"?o.fontSize:Te}px system-ui, sans-serif`,t.fillStyle=o.color,t.fillText(o.text,f,s)}if(t.restore(),pt&&po(t,W,Y,tt,et,A,r,ne,Ot,w,e.gridToScreen,e.rotation,b),St){const o=oo(e.valueColorScheme,e.reverseValueScheme),f=ue,s=pe,a=Math.round(s*.75),c=r+Math.round((s-a)/2),h=W-r-B-f,d=It,i=Ft,y=i-d,l=e.plotMode==="stackedSoftBins"?xt:nt,{primary:T,secondary:I}=U?_e(e,Tt,l):{primary:"",secondary:""};if(U){const n=Math.max(c-6,r+11),m=Gt+Math.max(st(n),st(n-12))+8;lt(T,W-r,n,"right","bottom",m)}let R=c+a+4;const F=n=>Gt+Math.max(st(n),st(n+12))+8,E=e.logScale&&d>0?Math.log10(d):0,V=e.logScale&&i>0?Math.log10(i)-E:1,at=n=>e.logScale?(1-(Math.log10(n)-E)/V)*a:(1-(n-d)/y)*a;for(let n=0;n<qt;n++){const m=1-n/(qt-1),S=c+n/qt*a,p=a/qt+1;t.fillStyle=o(m),t.fillRect(h,S,B,p)}t.strokeStyle="rgba(0,0,0,0.25)",t.lineWidth=.5,t.strokeRect(h,c,B,a),t.fillStyle=b.text,t.font=J(),t.textAlign="left",t.strokeStyle="rgba(0,0,0,0.35)",t.lineWidth=.5;const L=Ne,N=36,ot=14,wt=e.logScale&&V>0?xo(d,i,a,N,ot,E,V):e.allIntegerValues?(()=>{const n=Math.max(1,Jt(y*N/a)),m=[],S=Math.ceil(d/n)*n;for(let p=S;p<=i+n*1e-6;p+=n){const u=(1-(p-d)/y)*a;u>ot&&u<a-ot&&m.push(Math.round(p))}return m})():(()=>{const n=y>0?Jt(y*N/a):0,m=[];if(n>0){const S=Math.ceil(d/n)*n;for(let p=S;p<=i+n*1e-6;p+=n){const u=(1-(p-d)/y)*a;u>ot&&u<a-ot&&m.push(p)}}return m})(),C=vt,_=Qt,x=Nt;t.textBaseline="middle";for(const n of wt){const m=c+at(n);t.beginPath(),t.moveTo(h+B,m),t.lineTo(h+B+L,m),t.stroke(),t.fillText(x(n),h+B+L+Ut,m)}if(t.beginPath(),t.moveTo(h+B,c),t.lineTo(h+B+L,c),t.stroke(),t.textBaseline="top",t.fillText(x(i),h+B+L+Ut,c),t.beginPath(),t.moveTo(h+B,c+a),t.lineTo(h+B+L,c+a),t.stroke(),t.textBaseline="bottom",t.fillText(x(d),h+B+L+Ut,c+a),!_&&C&&(C.limitLow!==void 0||C.limitHigh!==void 0)){const n=[],m=e.colorbarRangeMode==="spec";for(const[S,p]of[[C.limitLow,"LSL"],[C.limitHigh,"USL"]]){if(S===void 0||e.logScale&&S<=0)continue;const u=at(S);!m&&(u<-ot||u>a+ot)||n.push({value:S,label:p,py:Math.max(0,Math.min(a,u))})}if(n.length>0){t.save(),t.font=J(),t.textAlign="right",t.textBaseline="middle";for(const{py:S,label:p}of n){const u=c+S;m||(t.strokeStyle="rgba(255,255,255,0.85)",t.lineWidth=3,t.beginPath(),t.moveTo(h,u),t.lineTo(h+B,u),t.stroke(),t.strokeStyle="rgba(0,0,0,0.7)",t.lineWidth=1,t.beginPath(),t.moveTo(h,u),t.lineTo(h+B,u),t.stroke()),t.fillStyle=b.text,t.fillText(p,h-3,u);const ct=p==="USL"?-1:1,rt=4,bt=h-3-t.measureText(p).width-rt-4;t.beginPath(),Fe(t,bt,u,rt,ct),t.strokeStyle="#fff",t.lineWidth=2,t.lineJoin="round",t.stroke(),t.fillStyle=b.text,t.fill()}t.restore()}}if(!_){const n=e.logScale?"log\u2081\u2080":e.logScaleRequested?"linear \u2014 log n/a":null;n&&(t.save(),t.fillStyle=b.text,t.font=mo(),t.textAlign="right",t.textBaseline="top",t.fillText(n,W-r,R),t.restore(),R+=15)}U&&I&<(I,W-r,R,"right","top",F(R),Kt(),b.text)}const ae=[];let z;if(Q){const o=k==="compact",f=e.plotMode==="softBin"?xt:nt,s=f?new Map(f.map(l=>[l.bin,l])):null,a=e.plotMode==="softBin"?e.binColors.soft:e.binColors.hard,c=l=>a.get(l)??no,h=(l,T)=>{const I=t.measureText(l).width,R=o?0:t.measureText(String(T)).width,F=H+gt+I+(R?zt:0);return{labelWidth:I,countWidth:R,totalWidth:F}},d=Mt?.find(l=>l.key===e.activeMetadataKey),i=j?v.map(l=>{const T=o?l.compactLabel:l.label;return{key:l.key,color:l.color,count:l.count,label:T,tooltipLabel:l.tooltip,...h(T,l.count)}}):e.plotMode==="metadata"?Pt.map(([l,T])=>{const R=d?.values?.find(V=>V.value===l)?.label??l,F=o?l:R,E=e.metadataColorMap?.get(l)??"#d6d9dd";return{key:l,color:E,count:T,label:F,tooltipLabel:`${R} \xB7 ${T} dies`,...h(F,T)}}):Bt.map(([l,T])=>{const I=s?.get(l),R=I?.name?`${l} \xB7 ${I.name}`:`Bin ${l}`,F=o?String(l):R;return{key:l,color:c(l),count:T,label:F,tooltipLabel:`${R} \xB7 ${T} dies`,...h(F,T)}}),y=W-r*2;if(i.length>0){const l=At?ft*2+(U?Ee:0):U&&!kt?He+Ye:0,T=Math.max(1,Math.floor((Y-2*r-l)/D));let I=i.length,R=1,F=I*D,E=[],V=0;const at=Zt||kt;if(at){const C=Math.max(...i.map(x=>x.totalWidth)),_=Math.max(1,Math.min(i.length,Math.floor((y+8)/(C+8))));for(let x=_;x>=1;x--){const n=Math.ceil(i.length/x);if(n>T)continue;const m=new Array(x).fill(0);for(let p=0;p<i.length;p++){const u=p%x;m[u]=Math.max(m[u],i[p].totalWidth)}if(m.reduce((p,u)=>p+u,0)+(x-1)*8<=y||x===1){R=x,E=m,I=n,F=n*D;break}}}else I=Math.min(i.length,T),F=I*D,i.length>T&&(V=i.length-(T-1)),E=[Wt];let L,N;if(At){const C=Math.max(...i.map(u=>H+gt+t.measureText(u.label).width+zt)),_=Math.ceil(C);E=[_];const x=U?Ee:0,n=_+ft*2,m=F+x+ft*2,S=ut?.x??0,p=ut?.y??0;L=Math.min(Math.max(W-r-n+S,r),W-r-n),N=Math.min(Math.max(Y-r-m+p,r),Y-r-m),z={x:L,y:N,w:n,h:m},t.save(),t.globalAlpha=.92,t.fillStyle=b.surface,t.fillRect(L,N,n,m),t.globalAlpha=1,t.strokeStyle=b.axisLine,t.lineWidth=1,t.strokeRect(L,N,n,m),t.restore(),L+=ft,N+=ft+x}else Zt?(L=r,N=Y-r-F):kt?(L=r,N=r):he?(L=r+4,N=Math.max(r+Math.round((Y-2*r-F)/2),r+l)):(L=W-r-Wt+4,N=Math.max(r+Math.round((Y-2*r-F)/2),r+l));if(U){const C=e.plotMode==="softBin"?xt:nt,{primary:_,secondary:x}=_e(e,Tt,C),n=Ye,m=u=>Gt-Math.max(st(u),st(u-12))-8,S=u=>ht?W-r:m(u),p=At?z.y+z.h:N+F;if(kt){const u=p+n;lt(_,L,u,"left","top",Gt-Math.max(st(u),st(u+12))-8),x&<(x,L,u+16,"left","top",m(u+16),Kt(),b.text)}else if(At)lt(_,z.x+ft,z.y+ft,"left","top",z.x+z.w-ft),x&<(x,z.x,p+n,"left","top",z.x+z.w,Kt(),b.text);else{const u=Math.max(N-n,r+He);if(lt(_,L,u,"left","bottom",S(u)),x){const ct=p+n;lt(x,L,ct,"left","top",S(ct),Kt(),b.text)}}}t.save(),t.font=J();const ot=at?8:0,wt=(C,_)=>{if(t.measureText(C).width<=_)return C;let x=0,n=C.length;const m="\u2026",S=t.measureText(m).width;for(;x<n;){const p=x+n+1>>1;t.measureText(C.slice(0,p)).width+S<=_?x=p:n=p-1}return x===0?m:C.slice(0,x)+m};if(at){let C=0;for(let _=0;_<I;_++){let x=L;for(let n=0;n<R;n++){const m=i[C++];if(!m)break;const S=m.key===O,p=!S&&m.key===q,u=x,ct=x+H+gt,rt=N+_*D+D/2,bt=N+_*D+Math.round((D-H)/2),Vt=E[n]-H-gt-zt;p&&(t.fillStyle=b.hoverRow,t.fillRect(x,N+_*D,E[n],D)),t.fillStyle=m.color,t.fillRect(u,bt,H,H),t.strokeStyle=S?b.accent:"rgba(0,0,0,0.25)",t.lineWidth=S?2:.75,t.strokeRect(u,bt,H,H),t.fillStyle=S?b.accent:b.text,t.font=S?`bold ${J()}`:J(),t.textAlign="left",t.textBaseline="middle",t.fillText(wt(m.label,Vt),ct,rt),t.fillStyle=b.textMuted,t.font=J(),t.textAlign="right",t.fillText(String(m.count),x+E[n]-2,rt),ae.push({bin:m.key,x,y:N+_*D,w:E[n],h:D,label:m.tooltipLabel}),x+=E[n]+ot}}}else{const C=E[0],_=k!=="compact",x=C-H-gt-(_?zt:0),n=L+C-2;let m=i;V>0&&(m=i.slice(0,T-1));let S=N;for(const p of m){const u=p.key===O,ct=!u&&p.key===q,rt=L,bt=L+H+gt,Vt=S+D/2,Re=S+Math.round((D-H)/2),Qe=p.label;ct&&(t.fillStyle=b.hoverRow,t.fillRect(L-3,S,C+3,D)),t.fillStyle=p.color,t.fillRect(rt,Re,H,H),t.strokeStyle=u?b.accent:"rgba(0,0,0,0.25)",t.lineWidth=u?2:.75,t.strokeRect(rt,Re,H,H),t.fillStyle=u?b.accent:b.text,t.font=u?`bold ${J()}`:J(),t.textAlign="left",t.textBaseline="middle",t.fillText(wt(Qe,x),bt,Vt),(_||At)&&(t.fillStyle=b.textMuted,t.font=J(),t.textAlign="right",t.fillText(String(p.count),n,Vt)),ae.push({bin:p.key,x:L,y:S,w:C,h:D,label:p.tooltipLabel}),S+=D}V>0&&(t.fillStyle=b.textMuted,t.font=J(),t.textAlign="left",t.textBaseline="middle",t.fillText(`+ ${V} more`,L+H+gt,S+D/2))}}i.length>0&&t.restore()}const Je={originX:tt,originY:et,ppm:A,snapDist:Yt},Se=e.rectangles[0]?.width??1,Le=e.rectangles[0]?.height??1,it=e.dieBounds,Xt=it?it.minX:$.length?Math.min(...$.map(o=>o.x)):0,$t=it?it.minY:$.length?Math.min(...$.map(o=>o.y)):0,Ze=it?it.maxX:$.length?Math.max(...$.map(o=>o.x)):1,je=it?it.maxY:$.length?Math.max(...$.map(o=>o.y)):1,{cellW:ce,cellH:re,nCols:Rt,nRows:fe}=ao(Ze-Xt,je-$t,Se*1.5,Le*1.5,$.length),ke=Array.from({length:Rt*fe},()=>[]);for(let o=0;o<$.length;o++){const f=Math.floor(($[o].x-Xt)/ce),s=Math.floor(($[o].y-$t)/re),a=Number.isFinite(f)?Math.max(0,Math.min(Rt-1,f)):0,c=Number.isFinite(s)?Math.max(0,Math.min(fe-1,s)):0;ke[c*Rt+a].push(o)}function Ae(o,f,s){const a=Math.max(0,Math.floor((o-s-Xt)/ce)),c=Math.min(Rt-1,Math.floor((o+s-Xt)/ce)),h=Math.max(0,Math.floor((f-s-$t)/re)),d=Math.min(fe-1,Math.floor((f+s-$t)/re)),i=[];for(let y=h;y<=d;y++)for(let l=a;l<=c;l++)for(const T of ke[y*Rt+l])i.push(T);return i}return{hitTarget:{getDieAtPoint(o,f){const s=(o-tt)/A,a=(et-f)/A,c=Math.max(Se,Le);for(const i of Ae(s,a,c)){const y=e.rectangles[i];if(y&&Math.abs(s-y.x)<=y.width/2&&Math.abs(a-y.y)<=y.height/2)return e.dies[i]??null}let h=null,d=Yt*Yt;for(const i of Ae(s,a,Yt)){const y=$[i].x-s,l=$[i].y-a,T=y*y+l*l;T<d&&(d=T,h=e.dies[i]??null)}return h}},viewport:Je,binLegendRows:ae,legendBox:z}}function po(g,e,P,b,r,G,X,B,mt,Z,ut,pt,w){g.save(),g.font=uo(),g.fillStyle=w.text,g.strokeStyle=w.axisLine,g.lineWidth=.5;const K=P-B+4,O=X+mt-4,q=to(ut),Tt=Be(q,1,0),_t=Be(q,0,1),nt=1e-9;function xt(M){return Z?Math.abs(M.y)<nt&&Math.abs(M.x)>nt?{pitch:Z.x,sign:Math.sign(M.x)}:Math.abs(M.x)<nt&&Math.abs(M.y)>nt?{pitch:Z.y,sign:Math.sign(M.y)}:null:null}const Mt=xt(Tt),U=xt(_t);function W(M){return Mt?Math.round(M*Mt.sign/Mt.pitch):M}function Y(M){return U?Math.round(M*U.sign/U.pitch):M}function yt(M){if(!M)return Jt(50/G);const k=50/(M*G);return Math.max(1,Math.round(k))*M}const St=Mt?.pitch,Lt=U?.pitch,j=yt(St),Q=yt(Lt);g.textAlign="center",g.textBaseline="top";const Bt=Math.ceil((X-b)/G/j)*j,Pt=(e-X-b)/G;for(let M=Bt;M<=Pt;M+=j){const k=b+M*G;if(k<X||k>e-X)continue;g.beginPath(),g.moveTo(k,K-Oe),g.lineTo(k,K),g.stroke();const ht=Z?String(W(M)):Ie(M);g.fillText(ht,k,K+2)}g.textAlign="right",g.textBaseline="middle";const v=Math.ceil((r-(P-X))/G/Q)*Q,Dt=(r-X)/G;for(let M=v;M<=Dt;M+=Q){const k=r-M*G;if(k<X||k>P-X)continue;g.beginPath(),g.moveTo(O,k),g.lineTo(O+Oe,k),g.stroke();const ht=Z?String(Y(M)):Ie(M);g.fillText(ht,O-2,k)}g.restore()}function Jt(g){const e=Math.pow(10,Math.floor(Math.log10(g))),P=g/e;return(P<1.5?1:P<3.5?2:P<7.5?5:10)*e}function xo(g,e,P,b,r,G,X){const B=[],mt=X;if(mt<1){const w=Jt((e-g)*b/P);if(w>0){const K=Math.ceil(g/w)*w;for(let O=K;O<=e+w*1e-6;O+=w){if(O<=g||O>=e)continue;const q=(1-(Math.log10(O)-G)/X)*P;q>r&&q<P-r&&B.push(O)}}return B}const Z=mt<3?[1,2,5]:[1],ut=Math.floor(Math.log10(g)),pt=Math.ceil(Math.log10(e));for(let w=ut;w<=pt;w++)for(const K of Z){const O=K*Math.pow(10,w);if(O<=g||O>=e)continue;const q=(1-(Math.log10(O)-G)/X)*P;q>r&&q<P-r&&B.push(O)}return B}export{fmt,fmtColorbarAxis}from"../renderer/fmt.js";
|
|
1
|
+
import{fontPx as Ct}from"./toolbar.js";import{findTestDef as _e,buildMapTitle as Be}from"../renderer/buildView.js";import{sortBinsForDisplay as eo}from"../stats/binPareto.js";import{affineInvert as oo,affineVector as Pe}from"../core/transforms.js";import{compareNatural as no,maxOf as Ut,minOf as De}from"../core/utils.js";import{resolveValueColorFn as so}from"../renderer/colorSchemes.js";import{NO_DATA_FILL as lo}from"../renderer/colorMap.js";import{SPEC_PASS_FILL as We,SPEC_FAIL_LOW as io,SPEC_FAIL_HIGH as Oe,contrastTextColor as Ie}from"../renderer/colorMap.js";import{fmt as Fe,fmtColorbarAxis as ao}from"../renderer/fmt.js";import{resolveCanvasTheme as co}from"./canvasTheme.js";import{hitGridDims as ro}from"./hitGrid.js";function Ne(g,e,P,M,r){g.moveTo(e,P+r*M),g.lineTo(e-M*.9,P-r*M*.6),g.lineTo(e+M*.9,P-r*M*.6),g.closePath()}const fo=new Set(["value","stackedValues","stackedBins","stackedSoftBins"]),ho=new Set(["hardBin","softBin","metadata"]),J=()=>`${Ct(-2)}px system-ui, sans-serif`,Ee=3,Kt=2,go=6,mo=20,uo=()=>`600 ${Ct()}px system-ui, sans-serif`,qt=()=>`${Ct(-1)}px system-ui, sans-serif`,po=()=>`600 ${Ct(-1)}px system-ui, sans-serif`,zt=128,xo=()=>`${Ct(-2)}px system-ui, sans-serif`,He=4,D=17,H=11;export const BIN_LEGEND_W=110,BIN_LEGEND_W_COMPACT=64,BIN_LEGEND_ADAPT_COMPACT=280,BIN_LEGEND_ADAPT_FLOATING=180,BIN_LEGEND_MIN_CANVAS_W=150,BIN_LEGEND_MIN_CANVAS_H=120;const Jt=28,gt=5,ft=8,Ye=15,Ge=12,Xe=6;export function toCanvas(g,e,P={}){return drawMapCanvas(g,e,P)}export function drawMapCanvas(g,e,P={}){const M=co(g.parentElement??g),{padding:r=16,showColorbar:G=!0,markFailingDies:X=!1,colorbarWidth:B=16,background:mt=M.background,legendPosition:Z="default",legendOffset:ut,showAxes:pt=!1,diePitchMm:w,viewport:K,activeBin:N,hoverBin:q,fallbackFormat:Tt,minRightReserve:_t,hbinDefs:nt,sbinDefs:xt,metadataFields:yt,showTitle:U=!0}=P,W=Math.floor(g.clientWidth||g.width),Y=Math.floor(g.clientHeight||g.height),bt=W>=BIN_LEGEND_MIN_CANVAS_W&&Y>=BIN_LEGEND_MIN_CANVAS_H,St=G&&bt&&fo.has(e.plotMode)&&e.passFailDisplay==="off",Lt=G&&bt&&ho.has(e.plotMode),j=G&&bt&&e.plotMode==="value"&&(e.passFailDisplay==="spec"&&e.specCounts!==void 0||e.passFailDisplay==="test"&&e.passFailCounts!==void 0),Q=Lt||j,Bt=Lt&&e.plotMode!=="metadata"&&e.binCounts?eo(e.binCounts.entries(),e.plotMode==="softBin"?e.binColors.pass.soft:e.binColors.pass.hard):[],Pt=Lt&&e.plotMode==="metadata"&&e.metadataCounts?[...e.metadataColorMap?.keys()??[...e.metadataCounts.keys()].sort(no)].map(o=>[o,e.metadataCounts.get(o)??0]):[],v=[];if(j&&e.passFailDisplay==="test"){const o=e.passFailCounts;v.push({key:-1,color:We,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),v.push({key:-2,color:Oe,label:"Fail",compactLabel:"Fail",count:o.fail,tooltip:`Fail \xB7 ${o.fail} dies`})}else if(j){const o=e.specCounts,f=_e(e.testDefs,e.activeTest);v.push({key:-1,color:We,label:"Pass",compactLabel:"Pass",count:o.pass,tooltip:`Pass \xB7 ${o.pass} dies`}),f?.limitHigh!==void 0&&v.push({key:-2,color:Oe,label:"Fail high",compactLabel:"Fail hi",count:o.failHigh,tooltip:`Fail high \xB7 ${o.failHigh} dies`}),f?.limitLow!==void 0&&v.push({key:-3,color:io,label:"Fail low",compactLabel:"Fail lo",count:o.failLow,tooltip:`Fail low \xB7 ${o.failLow} dies`})}const Dt=j?v.length:e.plotMode==="metadata"?Pt.length:Bt.length,y=(g.ownerDocument.defaultView??window).devicePixelRatio??1;if(W<=0||Y<=0)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const k=Z!=="default"?Z:W<BIN_LEGEND_ADAPT_FLOATING?"floating":W<BIN_LEGEND_ADAPT_COMPACT?"compact":"default",ht=k==="default"||k==="compact",de=k==="left",jt=k==="bottom",kt=k==="top",At=k==="floating",Wt=k==="compact"||k==="floating"?BIN_LEGEND_W_COMPACT:BIN_LEGEND_W,ge=Math.floor((Y-2*r)/D),me=Dt>ge?ge-1:Dt,ue=Q&&jt?me*D:0,Qt=Q&&kt?me*D:0,vt=e.plotMode==="stackedBins"||e.plotMode==="stackedSoftBins",te=_e(e.testDefs,e.activeTest),$e=vt?"Count":te?.name??(e.activeTest!=null?`Test ${e.activeTest}`:void 0),we=vt?void 0:te?.unit,[Ot,It]=e.valueRange,{tickFmt:Ve}=ao(It,$e,we,Tt),Ft=e.allIntegerValues?o=>String(Math.round(o)):Ve;let ee=0;if(St){const o=g.getContext("2d");o.font=J();const f=[Ft(Ot),Ft(It)];Ot<0&&f.push(Ft(-Math.max(Math.abs(Ot),Math.abs(It))));for(const s of f)ee=Math.max(ee,o.measureText(s).width)}const pe=Math.max(mo,Math.ceil(ee)+Ee+Kt+go-r),oe=St?B+pe+8:Q&&ht?Wt:0,ne=Q&&de?Wt:0,se=pt?32:0,Nt=pt?36:0,Ue=_t!=null?Math.max(oe,_t):oe,Ke=W-2*r-Ue-Nt-ne,xe=Y-2*r-se-ue-Qt;g.width=Math.round(W*y),g.height=Math.round(Y*y);const t=g.getContext("2d");t.scale(y,y),t.fillStyle=mt,t.fillRect(0,0,W,Y);const $=e.hoverPoints;if(!$.length)return{hitTarget:{getDieAtPoint:()=>null},viewport:{originX:0,originY:0,ppm:1,snapDist:1},binLegendRows:[]};const Et=e.rectangles[0],le=Et?Et.width/2:0,ie=Et?Et.height/2:0,Ht=e.dieBounds,ye=(Ht?.minX??0)-le,qe=(Ht?.maxX??0)+le,ze=(Ht?.minY??0)-ie,be=(Ht?.maxY??0)+ie,Me=qe-ye,Te=be-ze;let tt,et,A;if(K)({originX:tt,originY:et,ppm:A}=K);else{const f=e.notchDir,s=f&&f.y>.1?13:0,a=f&&f.y<-.1?13:0,c=f&&f.x<-.1?13:0,h=f&&f.x>.1?13:0,d=Ke-c-h,i=xe-s-a;A=Math.min(d/Me,i/Te),tt=r+Nt+ne+c+(d-Me*A)/2-ye*A,et=r+Qt+s+(i-Te*A)/2+be*A}const Yt=K?.snapDist??Math.max(le,ie,1)*1.5,Gt=tt+e.waferCenter.x*A,Je=et-e.waferCenter.y*A,ae=e.waferRadius*A,st=o=>{const f=Math.abs(o-Je);return f>=ae?0:Math.sqrt(ae*ae-f*f)},lt=(o,f,s,a,c,h,d=uo(),i=M.text)=>{if(!o)return;t.save(),t.fillStyle=i,t.font=d,t.textAlign=a,t.textBaseline=c;const b=a==="right"?f-h:h-f;let l=o;if(b>8&&t.measureText(l).width>b){for(;l.length>1&&t.measureText(l+"\u2026").width>b;)l=l.slice(0,-1);l=l+"\u2026"}b>8&&t.fillText(l,f,s),t.restore()};t.save(),t.setTransform(A*y,0,0,-A*y,tt*y,et*y);const dt=1/A,Ze=1/(A*y);if(e.rectangles.length>0){const o=new Map;for(const s of e.rectangles){const a=String(s.fill);let c=o.get(a);c||(c=[],o.set(a,c)),c.push(s)}for(const[s,a]of o){t.beginPath();for(const c of a)t.rect(c.x-c.width/2,c.y-c.height/2,c.width,c.height);t.fillStyle=s,t.fill()}t.strokeStyle="rgba(0,0,0,0.18)",t.lineWidth=.5*Ze,t.beginPath();for(const s of e.rectangles)t.rect(s.x-s.width/2,s.y-s.height/2,s.width,s.height);if(t.stroke(),X){const s=e.rectangles.filter(a=>a.binFail);if(s.length>0){const a=new Map;for(const c of s){const h=Ie(String(c.fill));let d=a.get(h);d||(d=[],a.set(h,d)),d.push(c)}t.save(),t.lineCap="butt";for(const[c,h]of a){t.strokeStyle=c,t.globalAlpha=.55,t.lineWidth=Math.max(.6*dt,Math.min(h[0].width,h[0].height)*.1);for(const d of h){const i=d.width/2,b=d.height/2,l=Math.max(1.6*dt,Math.min(d.width,d.height)/3);t.save(),t.beginPath(),t.rect(d.x-i,d.y-b,d.width,d.height),t.clip(),t.beginPath();for(let T=-(i+b);T<=i+b;T+=l)t.moveTo(d.x-i+T,d.y-b),t.lineTo(d.x-i+T+d.height,d.y+b);t.stroke(),t.restore()}}t.restore()}}const f=e.rectangles.filter(s=>s.specMark);if(f.length>0){const s=new Map;for(const c of f){const h=Ie(String(c.fill)),d=`${c.specMark}|${h}`;let i=s.get(d);i||(i=[],s.set(d,i)),i.push(c)}const a=2.5*dt;t.lineJoin="round";for(const[c,h]of s){const[d,i]=c.split("|"),b=i==="#000000"?"#ffffff":"#000000",l=d==="failHigh"?1:-1,T=Math.min(h[0].width,h[0].height),O=Math.max(2.5*dt,T*.3);t.beginPath();for(const R of h)Ne(t,R.x,R.y,O,l);t.strokeStyle=b,t.lineWidth=a,t.stroke(),t.fillStyle=i,t.fill()}}}for(const o of e.overlays){t.beginPath();for(const f of o.points){for(let s=0;s<f.length;s++)s===0?t.moveTo(f[s].x,f[s].y):t.lineTo(f[s].x,f[s].y);o.closed&&t.closePath()}o.fill&&!o.fill.startsWith("rgba(0,0,0,0)")&&(t.fillStyle=o.fill,t.fill()),o.kind==="ring-boundary"||o.kind==="quadrant-boundary"||o.kind==="reticle"?(t.strokeStyle="rgba(0,0,0,0.45)",t.lineWidth=3*dt,t.stroke(),t.strokeStyle="rgba(255,255,255,0.8)",t.lineWidth=1*dt,t.stroke()):(t.strokeStyle=o.lineColor,t.lineWidth=o.lineWidth*dt,t.stroke())}if(t.restore(),e.notchDir){const o=tt+e.waferCenter.x*A,f=et-e.waferCenter.y*A,s=e.notchDir.x,a=-e.notchDir.y,c=e.waferRadius*A,h=4,d=9,i=5,b=o+s*(c+h),l=f+a*(c+h),T=b+s*d,O=l+a*d,R={x:-a,y:s},I=r+Nt+ne,E=W-r-oe,V=r+Qt,at=Y-r-ue-se;t.save(),t.beginPath(),t.rect(I,V,E-I,at-V),t.clip(),t.beginPath(),t.moveTo(b,l),t.lineTo(T+R.x*i,O+R.y*i),t.lineTo(T-R.x*i,O-R.y*i),t.closePath(),t.fillStyle=M.text,t.fill(),t.restore()}t.save(),t.textAlign="center",t.textBaseline="middle";let Se=0;if(e.rectangles.length>0){let o=1;for(const h of e.texts)h.role!=="indicator"&&h.text.length>o&&(o=h.text.length);const f=e.rectangles[0].width*A,s=e.rectangles[0].height*A,a=Math.min(f,s),c=f/o;Se=Math.max(8,Math.min(64,Math.round(Math.min(a*.55,c*1.8))))}for(const o of e.texts){const f=tt+o.x*A,s=et-o.y*A;t.font=`${o.role==="indicator"?o.fontSize:Se}px system-ui, sans-serif`,t.fillStyle=o.color,t.fillText(o.text,f,s)}if(t.restore(),pt&&yo(t,W,Y,tt,et,A,r,se,Nt,w,e.gridToScreen,e.rotation,M),St){const o=so(e.valueColorScheme,e.reverseValueScheme),f=pe,s=xe,a=Math.round(s*.75),c=r+Math.round((s-a)/2),h=W-r-B-f,d=Ot,i=It,b=i-d,l=e.plotMode==="stackedSoftBins"?xt:nt,{primary:T,secondary:O}=U?Be(e,Tt,l):{primary:"",secondary:""};if(U){const n=Math.max(c-6,r+11),m=Gt+Math.max(st(n),st(n-12))+8;lt(T,W-r,n,"right","bottom",m)}let R=c+a+4;const I=n=>Gt+Math.max(st(n),st(n+12))+8,E=e.logScale&&d>0?Math.log10(d):0,V=e.logScale&&i>0?Math.log10(i)-E:1,at=n=>e.logScale?(1-(Math.log10(n)-E)/V)*a:(1-(n-d)/b)*a;for(let n=0;n<zt;n++){const m=1-n/(zt-1),S=c+n/zt*a,p=a/zt+1;t.fillStyle=o(m),t.fillRect(h,S,B,p)}t.strokeStyle="rgba(0,0,0,0.25)",t.lineWidth=.5,t.strokeRect(h,c,B,a),t.fillStyle=M.text,t.font=J(),t.textAlign="left",t.strokeStyle="rgba(0,0,0,0.35)",t.lineWidth=.5;const L=Ee,F=36,ot=14,wt=e.logScale&&V>0?bo(d,i,a,F,ot,E,V):e.allIntegerValues?(()=>{const n=Math.max(1,Zt(b*F/a)),m=[],S=Math.ceil(d/n)*n;for(let p=S;p<=i+n*1e-6;p+=n){const u=(1-(p-d)/b)*a;u>ot&&u<a-ot&&m.push(Math.round(p))}return m})():(()=>{const n=b>0?Zt(b*F/a):0,m=[];if(n>0){const S=Math.ceil(d/n)*n;for(let p=S;p<=i+n*1e-6;p+=n){const u=(1-(p-d)/b)*a;u>ot&&u<a-ot&&m.push(p)}}return m})(),C=te,_=vt,x=Ft;t.textBaseline="middle";for(const n of wt){const m=c+at(n);t.beginPath(),t.moveTo(h+B,m),t.lineTo(h+B+L,m),t.stroke(),t.fillText(x(n),h+B+L+Kt,m)}if(t.beginPath(),t.moveTo(h+B,c),t.lineTo(h+B+L,c),t.stroke(),t.textBaseline="top",t.fillText(x(i),h+B+L+Kt,c),t.beginPath(),t.moveTo(h+B,c+a),t.lineTo(h+B+L,c+a),t.stroke(),t.textBaseline="bottom",t.fillText(x(d),h+B+L+Kt,c+a),!_&&C&&(C.limitLow!==void 0||C.limitHigh!==void 0)){const n=[],m=e.colorbarRangeMode==="spec";for(const[S,p]of[[C.limitLow,"LSL"],[C.limitHigh,"USL"]]){if(S===void 0||e.logScale&&S<=0)continue;const u=at(S);!m&&(u<-ot||u>a+ot)||n.push({value:S,label:p,py:Math.max(0,Math.min(a,u))})}if(n.length>0){t.save(),t.font=J(),t.textAlign="right",t.textBaseline="middle";for(const{py:S,label:p}of n){const u=c+S;m||(t.strokeStyle="rgba(255,255,255,0.85)",t.lineWidth=3,t.beginPath(),t.moveTo(h,u),t.lineTo(h+B,u),t.stroke(),t.strokeStyle="rgba(0,0,0,0.7)",t.lineWidth=1,t.beginPath(),t.moveTo(h,u),t.lineTo(h+B,u),t.stroke()),t.fillStyle=M.text,t.fillText(p,h-3,u);const ct=p==="USL"?-1:1,rt=4,Mt=h-3-t.measureText(p).width-rt-4;t.beginPath(),Ne(t,Mt,u,rt,ct),t.strokeStyle="#fff",t.lineWidth=2,t.lineJoin="round",t.stroke(),t.fillStyle=M.text,t.fill()}t.restore()}}if(!_){const n=e.logScale?"log\u2081\u2080":e.logScaleRequested?"linear \u2014 log n/a":null;n&&(t.save(),t.fillStyle=M.text,t.font=po(),t.textAlign="right",t.textBaseline="top",t.fillText(n,W-r,R),t.restore(),R+=15)}U&&O&<(O,W-r,R,"right","top",I(R),qt(),M.text)}const ce=[];let z;if(Q){const o=k==="compact",f=e.plotMode==="softBin"?xt:nt,s=f?new Map(f.map(l=>[l.bin,l])):null,a=e.plotMode==="softBin"?e.binColors.soft:e.binColors.hard,c=l=>a.get(l)??lo,h=(l,T)=>{const O=t.measureText(l).width,R=o?0:t.measureText(String(T)).width,I=H+gt+O+(R?Jt:0);return{labelWidth:O,countWidth:R,totalWidth:I}},d=yt?.find(l=>l.key===e.activeMetadataKey),i=j?v.map(l=>{const T=o?l.compactLabel:l.label;return{key:l.key,color:l.color,count:l.count,label:T,tooltipLabel:l.tooltip,...h(T,l.count)}}):e.plotMode==="metadata"?Pt.map(([l,T])=>{const R=d?.values?.find(V=>V.value===l)?.label??l,I=o?l:R,E=e.metadataColorMap?.get(l)??"#d6d9dd";return{key:l,color:E,count:T,label:I,tooltipLabel:`${R} \xB7 ${T} dies`,...h(I,T)}}):Bt.map(([l,T])=>{const O=s?.get(l),R=O?.name?`${l} \xB7 ${O.name}`:`Bin ${l}`,I=o?String(l):R;return{key:l,color:c(l),count:T,label:I,tooltipLabel:`${R} \xB7 ${T} dies`,...h(I,T)}}),b=W-r*2;if(i.length>0){const l=At?ft*2+(U?Ye:0):U&&!kt?Ge+Xe:0,T=Math.max(1,Math.floor((Y-2*r-l)/D));let O=i.length,R=1,I=O*D,E=[],V=0;const at=jt||kt;if(at){const C=Ut(i.map(x=>x.totalWidth)),_=Math.max(1,Math.min(i.length,Math.floor((b+8)/(C+8))));for(let x=_;x>=1;x--){const n=Math.ceil(i.length/x);if(n>T)continue;const m=new Array(x).fill(0);for(let p=0;p<i.length;p++){const u=p%x;m[u]=Math.max(m[u],i[p].totalWidth)}if(m.reduce((p,u)=>p+u,0)+(x-1)*8<=b||x===1){R=x,E=m,O=n,I=n*D;break}}}else O=Math.min(i.length,T),I=O*D,i.length>T&&(V=i.length-(T-1)),E=[Wt];let L,F;if(At){const C=Ut(i.map(u=>H+gt+t.measureText(u.label).width+Jt)),_=Math.ceil(C);E=[_];const x=U?Ye:0,n=_+ft*2,m=I+x+ft*2,S=ut?.x??0,p=ut?.y??0;L=Math.min(Math.max(W-r-n+S,r),W-r-n),F=Math.min(Math.max(Y-r-m+p,r),Y-r-m),z={x:L,y:F,w:n,h:m},t.save(),t.globalAlpha=.92,t.fillStyle=M.surface,t.fillRect(L,F,n,m),t.globalAlpha=1,t.strokeStyle=M.axisLine,t.lineWidth=1,t.strokeRect(L,F,n,m),t.restore(),L+=ft,F+=ft+x}else jt?(L=r,F=Y-r-I):kt?(L=r,F=r):de?(L=r+4,F=Math.max(r+Math.round((Y-2*r-I)/2),r+l)):(L=W-r-Wt+4,F=Math.max(r+Math.round((Y-2*r-I)/2),r+l));if(U){const C=e.plotMode==="softBin"?xt:nt,{primary:_,secondary:x}=Be(e,Tt,C),n=Xe,m=u=>Gt-Math.max(st(u),st(u-12))-8,S=u=>ht?W-r:m(u),p=At?z.y+z.h:F+I;if(kt){const u=p+n;lt(_,L,u,"left","top",Gt-Math.max(st(u),st(u+12))-8),x&<(x,L,u+16,"left","top",m(u+16),qt(),M.text)}else if(At)lt(_,z.x+ft,z.y+ft,"left","top",z.x+z.w-ft),x&<(x,z.x,p+n,"left","top",z.x+z.w,qt(),M.text);else{const u=Math.max(F-n,r+Ge);if(lt(_,L,u,"left","bottom",S(u)),x){const ct=p+n;lt(x,L,ct,"left","top",S(ct),qt(),M.text)}}}t.save(),t.font=J();const ot=at?8:0,wt=(C,_)=>{if(t.measureText(C).width<=_)return C;let x=0,n=C.length;const m="\u2026",S=t.measureText(m).width;for(;x<n;){const p=x+n+1>>1;t.measureText(C.slice(0,p)).width+S<=_?x=p:n=p-1}return x===0?m:C.slice(0,x)+m};if(at){let C=0;for(let _=0;_<O;_++){let x=L;for(let n=0;n<R;n++){const m=i[C++];if(!m)break;const S=m.key===N,p=!S&&m.key===q,u=x,ct=x+H+gt,rt=F+_*D+D/2,Mt=F+_*D+Math.round((D-H)/2),Vt=E[n]-H-gt-Jt;p&&(t.fillStyle=M.hoverRow,t.fillRect(x,F+_*D,E[n],D)),t.fillStyle=m.color,t.fillRect(u,Mt,H,H),t.strokeStyle=S?M.accent:"rgba(0,0,0,0.25)",t.lineWidth=S?2:.75,t.strokeRect(u,Mt,H,H),t.fillStyle=S?M.accent:M.text,t.font=S?`bold ${J()}`:J(),t.textAlign="left",t.textBaseline="middle",t.fillText(wt(m.label,Vt),ct,rt),t.fillStyle=M.textMuted,t.font=J(),t.textAlign="right",t.fillText(String(m.count),x+E[n]-2,rt),ce.push({bin:m.key,x,y:F+_*D,w:E[n],h:D,label:m.tooltipLabel}),x+=E[n]+ot}}}else{const C=E[0],_=k!=="compact",x=C-H-gt-(_?Jt:0),n=L+C-2;let m=i;V>0&&(m=i.slice(0,T-1));let S=F;for(const p of m){const u=p.key===N,ct=!u&&p.key===q,rt=L,Mt=L+H+gt,Vt=S+D/2,Ce=S+Math.round((D-H)/2),to=p.label;ct&&(t.fillStyle=M.hoverRow,t.fillRect(L-3,S,C+3,D)),t.fillStyle=p.color,t.fillRect(rt,Ce,H,H),t.strokeStyle=u?M.accent:"rgba(0,0,0,0.25)",t.lineWidth=u?2:.75,t.strokeRect(rt,Ce,H,H),t.fillStyle=u?M.accent:M.text,t.font=u?`bold ${J()}`:J(),t.textAlign="left",t.textBaseline="middle",t.fillText(wt(to,x),Mt,Vt),(_||At)&&(t.fillStyle=M.textMuted,t.font=J(),t.textAlign="right",t.fillText(String(p.count),n,Vt)),ce.push({bin:p.key,x:L,y:S,w:C,h:D,label:p.tooltipLabel}),S+=D}V>0&&(t.fillStyle=M.textMuted,t.font=J(),t.textAlign="left",t.textBaseline="middle",t.fillText(`+ ${V} more`,L+H+gt,S+D/2))}}i.length>0&&t.restore()}const je={originX:tt,originY:et,ppm:A,snapDist:Yt},Le=e.rectangles[0]?.width??1,ke=e.rectangles[0]?.height??1,it=e.dieBounds,Xt=it?it.minX:$.length?De($.map(o=>o.x)):0,$t=it?it.minY:$.length?De($.map(o=>o.y)):0,Qe=it?it.maxX:$.length?Ut($.map(o=>o.x)):1,ve=it?it.maxY:$.length?Ut($.map(o=>o.y)):1,{cellW:re,cellH:fe,nCols:Rt,nRows:he}=ro(Qe-Xt,ve-$t,Le*1.5,ke*1.5,$.length),Ae=Array.from({length:Rt*he},()=>[]);for(let o=0;o<$.length;o++){const f=Math.floor(($[o].x-Xt)/re),s=Math.floor(($[o].y-$t)/fe),a=Number.isFinite(f)?Math.max(0,Math.min(Rt-1,f)):0,c=Number.isFinite(s)?Math.max(0,Math.min(he-1,s)):0;Ae[c*Rt+a].push(o)}function Re(o,f,s){const a=Math.max(0,Math.floor((o-s-Xt)/re)),c=Math.min(Rt-1,Math.floor((o+s-Xt)/re)),h=Math.max(0,Math.floor((f-s-$t)/fe)),d=Math.min(he-1,Math.floor((f+s-$t)/fe)),i=[];for(let b=h;b<=d;b++)for(let l=a;l<=c;l++)for(const T of Ae[b*Rt+l])i.push(T);return i}return{hitTarget:{getDieAtPoint(o,f){const s=(o-tt)/A,a=(et-f)/A,c=Math.max(Le,ke);for(const i of Re(s,a,c)){const b=e.rectangles[i];if(b&&Math.abs(s-b.x)<=b.width/2&&Math.abs(a-b.y)<=b.height/2)return e.dies[i]??null}let h=null,d=Yt*Yt;for(const i of Re(s,a,Yt)){const b=$[i].x-s,l=$[i].y-a,T=b*b+l*l;T<d&&(d=T,h=e.dies[i]??null)}return h}},viewport:je,binLegendRows:ce,legendBox:z}}function yo(g,e,P,M,r,G,X,B,mt,Z,ut,pt,w){g.save(),g.font=xo(),g.fillStyle=w.text,g.strokeStyle=w.axisLine,g.lineWidth=.5;const K=P-B+4,N=X+mt-4,q=oo(ut),Tt=Pe(q,1,0),_t=Pe(q,0,1),nt=1e-9;function xt(y){return Z?Math.abs(y.y)<nt&&Math.abs(y.x)>nt?{pitch:Z.x,sign:Math.sign(y.x)}:Math.abs(y.x)<nt&&Math.abs(y.y)>nt?{pitch:Z.y,sign:Math.sign(y.y)}:null:null}const yt=xt(Tt),U=xt(_t);function W(y){return yt?Math.round(y*yt.sign/yt.pitch):y}function Y(y){return U?Math.round(y*U.sign/U.pitch):y}function bt(y){if(!y)return Zt(50/G);const k=50/(y*G);return Math.max(1,Math.round(k))*y}const St=yt?.pitch,Lt=U?.pitch,j=bt(St),Q=bt(Lt);g.textAlign="center",g.textBaseline="top";const Bt=Math.ceil((X-M)/G/j)*j,Pt=(e-X-M)/G;for(let y=Bt;y<=Pt;y+=j){const k=M+y*G;if(k<X||k>e-X)continue;g.beginPath(),g.moveTo(k,K-He),g.lineTo(k,K),g.stroke();const ht=Z?String(W(y)):Fe(y);g.fillText(ht,k,K+2)}g.textAlign="right",g.textBaseline="middle";const v=Math.ceil((r-(P-X))/G/Q)*Q,Dt=(r-X)/G;for(let y=v;y<=Dt;y+=Q){const k=r-y*G;if(k<X||k>P-X)continue;g.beginPath(),g.moveTo(N,k),g.lineTo(N+He,k),g.stroke();const ht=Z?String(Y(y)):Fe(y);g.fillText(ht,N-2,k)}g.restore()}function Zt(g){const e=Math.pow(10,Math.floor(Math.log10(g))),P=g/e;return(P<1.5?1:P<3.5?2:P<7.5?5:10)*e}function bo(g,e,P,M,r,G,X){const B=[],mt=X;if(mt<1){const w=Zt((e-g)*M/P);if(w>0){const K=Math.ceil(g/w)*w;for(let N=K;N<=e+w*1e-6;N+=w){if(N<=g||N>=e)continue;const q=(1-(Math.log10(N)-G)/X)*P;q>r&&q<P-r&&B.push(N)}}return B}const Z=mt<3?[1,2,5]:[1],ut=Math.floor(Math.log10(g)),pt=Math.ceil(Math.log10(e));for(let w=ut;w<=pt;w++)for(const K of Z){const N=K*Math.pow(10,w);if(N<=g||N>=e)continue;const q=(1-(Math.log10(N)-G)/X)*P;q>r&&q<P-r&&B.push(N)}return B}export{fmt,fmtColorbarAxis}from"../renderer/fmt.js";
|