@wafertools/wafermap 0.30.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +13 -3
- package/CHANGELOG.md +175 -0
- package/README.md +3 -3
- package/dist/packages/canvas-adapter/charts/barPanel.js +1 -1
- package/dist/packages/canvas-adapter/charts/binCluster.js +1 -1
- package/dist/packages/canvas-adapter/charts/boxplot.js +1 -1
- package/dist/packages/canvas-adapter/charts/capability.js +1 -1
- package/dist/packages/canvas-adapter/charts/correlation.js +2 -2
- package/dist/packages/canvas-adapter/charts/groupedBarPlot.d.ts +1 -0
- package/dist/packages/canvas-adapter/charts/histogram.js +1 -1
- package/dist/packages/canvas-adapter/charts/testPassRate.js +1 -1
- package/dist/packages/canvas-adapter/charts/trend.js +1 -1
- package/dist/packages/canvas-adapter/deprecated.d.ts +0 -3
- package/dist/packages/canvas-adapter/deprecated.js +1 -1
- package/dist/packages/canvas-adapter/dieList.js +1 -1
- package/dist/packages/canvas-adapter/exportName.d.ts +68 -0
- package/dist/packages/canvas-adapter/exportName.js +1 -0
- package/dist/packages/canvas-adapter/icons.js +2 -2
- package/dist/packages/canvas-adapter/index.d.ts +2 -1
- package/dist/packages/canvas-adapter/index.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferGallery.d.ts +25 -3
- package/dist/packages/canvas-adapter/renderWaferGallery.js +1 -1
- package/dist/packages/canvas-adapter/renderWaferMap.d.ts +24 -2
- package/dist/packages/canvas-adapter/renderWaferMap.js +1 -1
- package/dist/packages/canvas-adapter/summaryPanel.d.ts +21 -6
- package/dist/packages/canvas-adapter/summaryPanel.js +3 -3
- package/dist/packages/canvas-adapter/toCanvas.js +1 -1
- package/dist/packages/canvas-adapter/toolbar.d.ts +6 -0
- package/dist/packages/canvas-adapter/toolbar.js +2 -2
- package/dist/packages/canvas-adapter/userGuideHtml.d.ts +1 -1
- package/dist/packages/canvas-adapter/userGuideHtml.js +15 -8
- package/dist/packages/canvas-adapter/version.d.ts +2 -2
- package/dist/packages/canvas-adapter/version.js +1 -1
- package/dist/packages/canvas-adapter/warnings.js +1 -1
- package/dist/packages/core/deprecated.d.ts +8 -12
- package/dist/packages/core/deprecated.js +1 -1
- package/dist/packages/core/index.d.ts +1 -1
- package/dist/packages/core/index.js +1 -1
- package/dist/packages/core/utils.d.ts +12 -0
- package/dist/packages/core/utils.js +1 -1
- package/dist/packages/core/waferLabel.d.ts +12 -2
- package/dist/packages/core/waferLabel.js +1 -1
- package/dist/packages/renderer/binColors.d.ts +38 -0
- package/dist/packages/renderer/binColors.js +1 -1
- package/dist/packages/renderer/buildView.js +1 -1
- package/dist/packages/renderer/buildWaferMap.d.ts +30 -5
- package/dist/packages/renderer/buildWaferMap.js +1 -1
- package/dist/packages/renderer/deprecate.d.ts +7 -0
- package/dist/packages/renderer/deprecate.js +1 -1
- package/dist/packages/renderer/deprecated.d.ts +2 -2
- package/dist/packages/renderer/deprecated.js +1 -1
- package/dist/packages/renderer/index.d.ts +2 -1
- package/dist/packages/renderer/index.js +1 -1
- package/dist/packages/stats/analyzeWaferLot.js +1 -1
- package/dist/packages/stats/analyzeWaferMap.js +1 -1
- package/dist/packages/stats/capability.d.ts +16 -1
- package/dist/packages/stats/capability.js +1 -1
- package/dist/packages/stats/deprecated.d.ts +30 -35
- package/dist/packages/stats/deprecated.js +1 -1
- package/dist/packages/stats/index.d.ts +4 -2
- package/dist/packages/stats/index.js +1 -1
- package/dist/packages/stats/normalizeInput.d.ts +10 -0
- package/dist/packages/stats/normalizeInput.js +1 -0
- package/dist/packages/stats/regions.d.ts +2 -0
- package/dist/packages/stats/regions.js +1 -1
- package/dist/packages/stats/renderFindingsReport.js +1 -1
- package/dist/packages/stats/renderSummaryReport.d.ts +39 -1
- package/dist/packages/stats/renderSummaryReport.js +26 -26
- package/dist/packages/stats/reportHtml.d.ts +0 -1
- package/dist/packages/stats/reportHtml.js +10 -10
- package/dist/packages/stats/summaryFigures.d.ts +47 -0
- package/dist/packages/stats/summaryFigures.js +1 -0
- package/dist/packages/stats/types.d.ts +118 -0
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const WMAP_VERSION = "0.30.
|
|
2
|
-
export declare const WMAP_BUILD_TIME = "2026-09-
|
|
1
|
+
export declare const WMAP_VERSION = "0.30.1";
|
|
2
|
+
export declare const WMAP_BUILD_TIME = "2026-09-17T07:12:45.499Z";
|
|
3
3
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const WMAP_VERSION="0.30.
|
|
1
|
+
export const WMAP_VERSION="0.30.1",WMAP_BUILD_TIME="2026-09-17T07:12:45.499Z";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SHADOW as S,TRACKING as O,SPACE as s,RADIUS as
|
|
1
|
+
import{SHADOW as S,TRACKING as O,SPACE as s,RADIUS as v,FONT as y,CLR as d,Z_BASE as B}from"./toolbar.js";const E={error:0,warning:1,info:2};export function severityOf(t){return t.severity??"warning"}export function collectWarnings(t){const o=[],i=new Set,n=e=>{if(!e?.message)return;const r=`${e.code}\0${e.message}`;i.has(r)||(i.add(r),o.push(e))};for(const e of t.result?.warnings??[])n(e);for(const e of t.statsSummary?.stats.warnings??[])n(e);for(const e of t.lotStatsSummary?.perWafer??[])for(const r of e.summary.stats.warnings??[])n(r);for(const e of t.extra??[])n(e);return o.sort((e,r)=>E[severityOf(e)]-E[severityOf(r)])}function $(t){return t==="error"?{bg:d.errBg,border:d.errBorder,text:d.errText}:{bg:d.warnBg,border:d.warnBorder,text:d.warnText}}function w(t){return t==="error"?"\u26D4":t==="info"?"\u2139":"\u26A0"}export const SHORT_LABEL={"partial-coverage":"Partial wafer coverage","geometry-conflict":"Geometry conflict","non-standard-diameter":"Non-standard wafer diameter","diameter-exceeds-die-extent":"Diameter exceeds die extent","edge-exclusion-exceeds-radius":"Edge exclusion exceeds radius","test-count-capped":"Test analysis skipped","test-def-collision":"Test definitions disagree","analysis-option-corrected":"Analysis option corrected","bin-colors-shared":"Bins share a colour","pass-bins-mixed":"Pass bins differ by wafer","ring-count-mixed":"Ring counts differ by wafer","input-field-removed":"Removed input name ignored","input-values-not-numbers":"Bins or test values are text"};function C(t){const o=t.code?SHORT_LABEL[t.code]:void 0;if(o)return o;const i=t.message.split(/(?<=\.)\s/)[0]??t.message;return i.length>60?`${i.slice(0,57)}\u2026`:i}export function buildWarningsBanner(t,o=document){const i=o.createElement("div");Object.assign(i.style,{marginBottom:s.lg});for(const n of t){const e=severityOf(n),r=$(e),m=o.createElement("div");Object.assign(m.style,{background:r.bg,border:`1px solid ${r.border}`,borderRadius:v.control,marginBottom:s.sm,fontSize:y.body,color:r.text,lineHeight:"1.5"}),m.setAttribute("role","status");const a=o.createElement("button");a.type="button",Object.assign(a.style,{display:"flex",alignItems:"center",gap:s.sm,width:"100%",background:"none",border:"none",font:"inherit",color:"inherit",textAlign:"left",padding:`${s.sm} ${s.lg}`,cursor:"pointer"});const g=o.createElement("span");Object.assign(g.style,{flex:"1",minWidth:"0"}),g.textContent=`${w(e)} ${C(n)}`;const l=o.createElement("span");Object.assign(l.style,{flexShrink:"0"}),a.append(g,l);const p=o.createElement("div");Object.assign(p.style,{padding:`0 ${s.lg} ${s.sm}`}),p.textContent=n.message;let c=!1;const f=()=>{l.textContent=c?"\u25B4":"\u25BE",p.style.display=c?"block":"none",a.setAttribute("aria-expanded",String(c)),a.setAttribute("aria-label",`${C(n)}. ${c?"Click to collapse.":"Click to expand for detail."}`)};f(),a.addEventListener("click",()=>{c=!c,f()}),a.addEventListener("mouseenter",()=>{l.style.opacity="0.65"}),a.addEventListener("mouseleave",()=>{l.style.opacity="1"}),m.append(a,p),i.appendChild(m)}return i}export function buildWarningsMenuEl(t,o,i=window){const n=i.document,e=n.createElement("div"),r=280,a=t.left+r<=(i.innerWidth??1/0)?t.left:Math.max(4,t.right-r);Object.assign(e.style,{position:"fixed",top:`${t.bottom+4}px`,left:`${a}px`,background:d.menuBg,border:`1px solid ${d.menuBorder}`,borderRadius:v.control,boxShadow:S.menu,zIndex:B,width:`${r}px`,maxHeight:"320px",overflowY:"auto",padding:"4px 0",pointerEvents:"auto"}),e.setAttribute("role","dialog"),e.setAttribute("aria-label","Data warnings");const g=n.createElement("div");Object.assign(g.style,{padding:`${s.sm} ${s.xl} ${s.xs}`,fontSize:y.body,fontWeight:"700",letterSpacing:O,textTransform:"uppercase",color:d.label}),g.textContent=o.length===1?"1 warning":`${o.length} warnings`,e.appendChild(g);for(const l of o){const p=severityOf(l),c=$(p),f=n.createElement("div");Object.assign(f.style,{display:"flex",gap:s.md,padding:`${s.md} ${s.xl}`,fontSize:y.body,lineHeight:"1.5",color:d.text,borderTop:`1px solid ${d.menuBorder}`,alignItems:"flex-start"});const x=n.createElement("span");Object.assign(x.style,{color:c.text,flexShrink:"0"}),x.textContent=w(p),f.appendChild(x);const u=n.createElement("div"),b=n.createElement("div");Object.assign(b.style,{fontWeight:"700",color:c.text,marginBottom:s.xxs}),b.textContent=l.code,u.appendChild(b);const h=n.createElement("div");h.textContent=l.message,u.appendChild(h),f.appendChild(u),e.appendChild(f)}return e}
|
|
@@ -2,18 +2,18 @@ import { createWafer as createWaferImpl } from './wafer.js';
|
|
|
2
2
|
import { generateDies as generateDiesImpl, isPositionedDie as isPositionedDieImpl } from './dies.js';
|
|
3
3
|
import { clipDiesToWafer as clipDiesToWaferImpl, applyOrientation as applyOrientationImpl, transformDies as transformDiesImpl, mapDataToDies as mapDataToDiesImpl, isInsideWafer as isInsideWaferImpl, affineIdentity as affineIdentityImpl, affineRotation as affineRotationImpl, affineMirror as affineMirrorImpl, affineCompose as affineComposeImpl, affineInvert as affineInvertImpl, affinePoint as affinePointImpl, affineVector as affineVectorImpl, affineSwapsAxes as affineSwapsAxesImpl } from './transforms.js';
|
|
4
4
|
import { applyProbeSequence as applyProbeSequenceImpl } from './probe.js';
|
|
5
|
-
import { generateReticleGrid as generateReticleGridImpl
|
|
5
|
+
import { generateReticleGrid as generateReticleGridImpl } from './reticle.js';
|
|
6
6
|
import { classifyDie as classifyDieImpl, getRingLabel as getRingLabelImpl } from './classify.js';
|
|
7
7
|
import { aggregateValues as aggregateValuesImpl, aggregateBinCounts as aggregateBinCountsImpl, getUniqueBins as getUniqueBinsImpl } from './aggregates.js';
|
|
8
|
-
import {
|
|
8
|
+
import { metadataCategoricalValue as metadataCategoricalValueImpl } from './metadata.js';
|
|
9
9
|
import { resolveGridPitch as resolveGridPitchImpl } from './inference/pitch.js';
|
|
10
|
-
/** @deprecated Removed in 0.31.0.
|
|
10
|
+
/** @deprecated Removed in 0.31.0. For a die layout with no test data use `buildWaferMap({ layout: true, waferConfig, dieConfig })`; otherwise build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
|
|
11
11
|
export declare const createWafer: typeof createWaferImpl;
|
|
12
|
-
/** @deprecated Removed in 0.31.0.
|
|
12
|
+
/** @deprecated Removed in 0.31.0. For a die layout with no test data use `buildWaferMap({ layout: true, waferConfig, dieConfig })`, which keeps every site fully on the wafer. */
|
|
13
13
|
export declare const generateDies: typeof generateDiesImpl;
|
|
14
14
|
/** @deprecated Removed in 0.31.0. Use `hasPosition` to test whether a die has a position. */
|
|
15
15
|
export declare const isPositionedDie: typeof isPositionedDieImpl;
|
|
16
|
-
/** @deprecated Removed in 0.31.0.
|
|
16
|
+
/** @deprecated Removed in 0.31.0. For a die layout with no test data use `buildWaferMap({ layout: true, waferConfig, dieConfig })`, which keeps every site fully on the wafer. */
|
|
17
17
|
export declare const clipDiesToWafer: typeof clipDiesToWaferImpl;
|
|
18
18
|
/** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
|
|
19
19
|
export declare const applyOrientation: typeof applyOrientationImpl;
|
|
@@ -43,11 +43,9 @@ export declare const affineSwapsAxes: typeof affineSwapsAxesImpl;
|
|
|
43
43
|
export declare const applyProbeSequence: typeof applyProbeSequenceImpl;
|
|
44
44
|
/** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
|
|
45
45
|
export declare const generateReticleGrid: typeof generateReticleGridImpl;
|
|
46
|
-
/** @deprecated Removed in 0.31.0.
|
|
47
|
-
export declare const getReticleCell: typeof getReticleCellImpl;
|
|
48
|
-
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
46
|
+
/** @deprecated Removed in 0.31.0. A die's ring and quadrant are in the die list's CSV export (Ring and Quadrant columns); ring and quadrant yield is `stats.regionYield.` */
|
|
49
47
|
export declare const classifyDie: typeof classifyDieImpl;
|
|
50
|
-
/** @deprecated Removed in 0.31.0.
|
|
48
|
+
/** @deprecated Removed in 0.31.0. Ring labels come with ring yield (`stats.regionYield.ring[].label`) and ring findings (`comparison.left`). */
|
|
51
49
|
export declare const getRingLabel: typeof getRingLabelImpl;
|
|
52
50
|
/** @deprecated Removed in 0.31.0. Stack wafers with `buildWaferMap`'s `lotStack` option. */
|
|
53
51
|
export declare const aggregateValues: typeof aggregateValuesImpl;
|
|
@@ -55,9 +53,7 @@ export declare const aggregateValues: typeof aggregateValuesImpl;
|
|
|
55
53
|
export declare const aggregateBinCounts: typeof aggregateBinCountsImpl;
|
|
56
54
|
/** @deprecated Removed in 0.31.0. Stack wafers with `buildWaferMap`'s `lotStack` option. */
|
|
57
55
|
export declare const getUniqueBins: typeof getUniqueBinsImpl;
|
|
58
|
-
/** @deprecated Removed in 0.31.0.
|
|
59
|
-
export declare const metadataDisplayValue: typeof metadataDisplayValueImpl;
|
|
60
|
-
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
56
|
+
/** @deprecated Removed in 0.31.0. Use `metadataDisplayValue` for a metadata value's text. */
|
|
61
57
|
export declare const metadataCategoricalValue: typeof metadataCategoricalValueImpl;
|
|
62
58
|
/** @deprecated Removed in 0.31.0. `buildWaferMap` resolves the die pitch itself; each built die's width and height carry it. */
|
|
63
59
|
export declare const resolveGridPitch: typeof resolveGridPitchImpl;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{deprecated as e}from"../renderer/deprecate.js";import{createWafer as
|
|
1
|
+
import{deprecated as e}from"../renderer/deprecate.js";import{createWafer as o}from"./wafer.js";import{generateDies as r,isPositionedDie as n}from"./dies.js";import{clipDiesToWafer as s,applyOrientation as f,transformDies as p,mapDataToDies as l,isInsideWafer as d,affineIdentity as c,affineRotation as g,affineMirror as m,affineCompose as u,affineInvert as I,affinePoint as $,affineVector as h,affineSwapsAxes as D}from"./transforms.js";import{applyProbeSequence as y}from"./probe.js";import{generateReticleGrid as w}from"./reticle.js";import{classifyDie as x,getRingLabel as C}from"./classify.js";import{aggregateValues as b,aggregateBinCounts as W,getUniqueBins as P}from"./aggregates.js";import{metadataCategoricalValue as V}from"./metadata.js";import{resolveGridPitch as S}from"./inference/pitch.js";const a="If you depend on it, say so at https://github.com/wafertools/wafermap/issues.",t="It belongs to the low-level drawing pipeline, which is being withdrawn: build with buildWaferMap and draw with renderWaferMap or renderWaferGallery.",A="Use hasPosition to test whether a die has a position.",M="buildWaferMap maps results onto dies.",i="Stack wafers with buildWaferMap's lotStack option.",R="buildWaferMap resolves the die pitch itself; each built die's width and height carry it.";export const createWafer=e(o,"createWafer",`For a die layout with no test data use buildWaferMap({ layout: true, waferConfig, dieConfig }); otherwise build with buildWaferMap and draw with renderWaferMap or renderWaferGallery. ${a}`),generateDies=e(r,"generateDies",`For a die layout with no test data use buildWaferMap({ layout: true, waferConfig, dieConfig }), which keeps every site fully on the wafer. ${a}`),isPositionedDie=e(n,"isPositionedDie",`${A} ${a}`),clipDiesToWafer=e(s,"clipDiesToWafer",`For a die layout with no test data use buildWaferMap({ layout: true, waferConfig, dieConfig }), which keeps every site fully on the wafer. ${a}`),applyOrientation=e(f,"applyOrientation",`${t} ${a}`),transformDies=e(p,"transformDies",`${t} ${a}`),mapDataToDies=e(l,"mapDataToDies",`${M} ${a}`),isInsideWafer=e(d,"isInsideWafer",`${t} ${a}`),affineIdentity=e(c,"affineIdentity",`${t} ${a}`),affineRotation=e(g,"affineRotation",`${t} ${a}`),affineMirror=e(m,"affineMirror",`${t} ${a}`),affineCompose=e(u,"affineCompose",`${t} ${a}`),affineInvert=e(I,"affineInvert",`${t} ${a}`),affinePoint=e($,"affinePoint",`${t} ${a}`),affineVector=e(h,"affineVector",`${t} ${a}`),affineSwapsAxes=e(D,"affineSwapsAxes",`${t} ${a}`),applyProbeSequence=e(y,"applyProbeSequence",`${t} ${a}`),generateReticleGrid=e(w,"generateReticleGrid",`${t} ${a}`),classifyDie=e(x,"classifyDie",`A die's ring and quadrant are in the die list's CSV export (Ring and Quadrant columns); ring and quadrant yield is stats.regionYield. ${a}`),getRingLabel=e(C,"getRingLabel",`Ring labels come with ring yield (stats.regionYield.ring[].label) and ring findings (comparison.left). ${a}`),aggregateValues=e(b,"aggregateValues",`${i} ${a}`),aggregateBinCounts=e(W,"aggregateBinCounts",`${i} ${a}`),getUniqueBins=e(P,"getUniqueBins",`${i} ${a}`),metadataCategoricalValue=e(V,"metadataCategoricalValue",`Use metadataDisplayValue for a metadata value's text. ${a}`),resolveGridPitch=e(S,"resolveGridPitch",`${R} ${a}`);
|
|
@@ -7,5 +7,5 @@ export * from './reticle.js';
|
|
|
7
7
|
export type { DataRow, MapOptions, TransformOptions, Affine, CoordFrame } from './transforms.js';
|
|
8
8
|
export * from './wafer.js';
|
|
9
9
|
export type { PitchResult } from './inference/pitch.js';
|
|
10
|
-
export { createWafer, generateDies, isPositionedDie, clipDiesToWafer, applyOrientation, transformDies, mapDataToDies, isInsideWafer, affineIdentity, affineRotation, affineMirror, affineCompose, affineInvert, affinePoint, affineVector, affineSwapsAxes, applyProbeSequence, generateReticleGrid,
|
|
10
|
+
export { createWafer, generateDies, isPositionedDie, clipDiesToWafer, applyOrientation, transformDies, mapDataToDies, isInsideWafer, affineIdentity, affineRotation, affineMirror, affineCompose, affineInvert, affinePoint, affineVector, affineSwapsAxes, applyProbeSequence, generateReticleGrid, classifyDie, getRingLabel, aggregateValues, aggregateBinCounts, getUniqueBins, metadataCategoricalValue, resolveGridPitch, } from './deprecated.js';
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./aggregates.js";export*from"./classify.js";export*from"./dies.js";export*from"./metadata.js";export*from"./probe.js";export*from"./reticle.js";export*from"./wafer.js";export{createWafer,generateDies,isPositionedDie,clipDiesToWafer,applyOrientation,transformDies,mapDataToDies,isInsideWafer,affineIdentity,affineRotation,affineMirror,affineCompose,affineInvert,affinePoint,affineVector,affineSwapsAxes,applyProbeSequence,generateReticleGrid,
|
|
1
|
+
export*from"./aggregates.js";export*from"./classify.js";export*from"./dies.js";export*from"./metadata.js";export*from"./probe.js";export*from"./reticle.js";export*from"./wafer.js";export{createWafer,generateDies,isPositionedDie,clipDiesToWafer,applyOrientation,transformDies,mapDataToDies,isInsideWafer,affineIdentity,affineRotation,affineMirror,affineCompose,affineInvert,affinePoint,affineVector,affineSwapsAxes,applyProbeSequence,generateReticleGrid,classifyDie,getRingLabel,aggregateValues,aggregateBinCounts,getUniqueBins,metadataCategoricalValue,resolveGridPitch}from"./deprecated.js";
|
|
@@ -68,4 +68,16 @@ export declare function modeOf(values: number[]): number | null;
|
|
|
68
68
|
* obvious. Every CSV this library writes must go through it.
|
|
69
69
|
*/
|
|
70
70
|
export declare function csvField(value: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Escape text for insertion into HTML — the one copy for the whole library.
|
|
73
|
+
*
|
|
74
|
+
* Every string the library puts into `innerHTML` that did not come from its own
|
|
75
|
+
* literals must go through this: test names, units, bin names, wafer labels and
|
|
76
|
+
* metadata values all arrive from input files (STDF/ATDF/CSV headers, MES
|
|
77
|
+
* fields, operator free text), and a name like `<img src=x onerror=…>` would
|
|
78
|
+
* otherwise run as script when a tooltip showed it — in a Tauri or Electron
|
|
79
|
+
* host, inside the app's own webview. It used to live in stats/reportHtml.ts,
|
|
80
|
+
* where only the reports used it, while every tooltip interpolated raw names.
|
|
81
|
+
*/
|
|
82
|
+
export declare function escHtml(value: string): string;
|
|
71
83
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function compareNatural(t,
|
|
1
|
+
export function compareNatural(t,e){return t.localeCompare(e,"en",{numeric:!0})}export function prettyKey(t){return t.replace(/([A-Z])/g," $1").replace(/_/g," ").trim().replace(/^./,e=>e.toUpperCase())}export function clamp01(t){return Math.max(0,Math.min(1,t))}export function mean(t){if(t.length===0)return NaN;let e=0;for(const n of t)e+=n;return e/t.length}export function medianOfSorted(t){const e=t.length;if(e===0)return 0;const n=Math.floor(e/2);return e%2===1?t[n]:(t[n-1]+t[n])/2}export function median(t){return medianOfSorted([...t].sort((e,n)=>e-n))}export function percentile98(t){if(t.length===0)return 0;const e=[...t].sort((o,r)=>o-r),n=Math.floor(e.length*.98);return e[Math.min(n,e.length-1)]}export function modeOf(t){if(!t.length)return null;const e=new Map;for(const r of t)e.set(r,(e.get(r)??0)+1);let n=0,o=t[0];for(const[r,c]of e)c>n&&(n=c,o=r);return o}const p=/^[=+\-@]/;export function csvField(t){const e=p.test(t)&&Number.isNaN(Number(t))?`'${t}`:t;return/[",\n\r]/.test(e)?`"${e.replace(/"/g,'""')}"`:e}export function escHtml(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}
|
|
@@ -16,10 +16,20 @@ import { type WaferMetadata } from './metadata.js';
|
|
|
16
16
|
* Order: the caller's `label` (a blank one counts as absent), then the wafer's
|
|
17
17
|
* own `metadata.waferId`, then a position that says plainly it is not an ID.
|
|
18
18
|
*/
|
|
19
|
-
export declare function waferDisplayLabel(item:
|
|
19
|
+
export declare function waferDisplayLabel(item: WaferLabelSource, index: number): string;
|
|
20
|
+
type WaferLabelSource = {
|
|
20
21
|
label?: string;
|
|
21
22
|
wafer?: {
|
|
22
23
|
metadata?: WaferMetadata | null;
|
|
23
24
|
} | null;
|
|
24
|
-
} | null | undefined
|
|
25
|
+
} | null | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The wafer's real identity — the caller's `label`, else `metadata.waferId` —
|
|
28
|
+
* or `undefined` when it has none. `waferDisplayLabel` without its positional
|
|
29
|
+
* fallback, for places where a position must not stand in for an identity: a
|
|
30
|
+
* saved file's name (see canvas-adapter/exportName.ts), where "Wafer 3 (no ID)"
|
|
31
|
+
* would be written into every export of a lot and read back later as an ID.
|
|
32
|
+
*/
|
|
33
|
+
export declare function waferIdentityLabel(item: WaferLabelSource): string | undefined;
|
|
34
|
+
export {};
|
|
25
35
|
//# sourceMappingURL=waferLabel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{metadataDisplayValue as r}from"./metadata.js";export function waferDisplayLabel(a,e){return(a?.label?.trim()||void 0)??r(a?.wafer?.metadata?.waferId)
|
|
1
|
+
import{metadataDisplayValue as r}from"./metadata.js";export function waferDisplayLabel(a,e){return waferIdentityLabel(a)??`Wafer ${e+1} (no ID)`}export function waferIdentityLabel(a){return(a?.label?.trim()||void 0)??r(a?.wafer?.metadata?.waferId)}
|
|
@@ -83,6 +83,44 @@ export interface BinColorOptions {
|
|
|
83
83
|
* A lone map resolves over its own dies.
|
|
84
84
|
*/
|
|
85
85
|
export declare function resolveBinColors(dies: Iterable<Die>, options?: BinColorOptions): BinColors;
|
|
86
|
+
/** The inputs a built map carries for its bin colours. A `WaferMapResult` is one. */
|
|
87
|
+
export interface BinColorSource {
|
|
88
|
+
dies: Iterable<Die>;
|
|
89
|
+
/** The pass bins the map was built with (`WaferMapResult.passBins`). */
|
|
90
|
+
passBins?: readonly number[];
|
|
91
|
+
hbinDefs?: readonly BinDef[];
|
|
92
|
+
sbinDefs?: readonly BinDef[];
|
|
93
|
+
}
|
|
94
|
+
/** Display choices for `binColorsForMaps` — the palette and whether defined colours win. */
|
|
95
|
+
export interface MapBinColorOptions {
|
|
96
|
+
/** Registered bin palette name (`registerBinColorScheme`). Default `'default'`. */
|
|
97
|
+
binColorScheme?: string;
|
|
98
|
+
/** Honour `BinDef.color`. Default true. */
|
|
99
|
+
useDefinedBinColors?: boolean;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The bin colours for one or more built maps — exactly the colours
|
|
103
|
+
* `renderWaferMap` and `renderWaferGallery` draw them in, for a host surface of
|
|
104
|
+
* its own: a table swatch, a PDF, a chart in another library.
|
|
105
|
+
*
|
|
106
|
+
* Takes the maps rather than dies and a `passBins` list, so pass/fail cannot be
|
|
107
|
+
* judged by the wrong bins: each map's dies are judged by that map's own
|
|
108
|
+
* `passBins`, and bin definitions (with any `BinDef.color`) are merged across the
|
|
109
|
+
* maps, first definition of a bin winning — the gallery's own rule. For a live
|
|
110
|
+
* map with the user's palette choice, read `WaferMapController.getBinColors()` or
|
|
111
|
+
* `GalleryController.getBinColors()` instead.
|
|
112
|
+
*/
|
|
113
|
+
export declare function binColorsForMaps(maps: BinColorSource | readonly BinColorSource[], options?: MapBinColorOptions): BinColors;
|
|
114
|
+
/** @internal `binColorsForMaps` plus the hard bins that pass on some maps and fail on others. */
|
|
115
|
+
export declare function resolveBinColorsForMaps(maps: readonly BinColorSource[], options?: MapBinColorOptions): {
|
|
116
|
+
colors: BinColors;
|
|
117
|
+
mixedHardBins: number[];
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* @internal Bin definitions merged across maps: one per bin, the first map's
|
|
121
|
+
* definition winning. The one rule for a multi-map legend, report or colouring.
|
|
122
|
+
*/
|
|
123
|
+
export declare function mergeBinDefs(lists: Iterable<readonly BinDef[] | undefined>): BinDef[];
|
|
86
124
|
/** One wafer's dies and the pass bins that wafer was built with. */
|
|
87
125
|
export interface BinPassGroup {
|
|
88
126
|
dies: Iterable<Die>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{diePassStatus as g}from"../core/dies.js";import{INPUT_DEFAULT_PASS_BINS as m}from"../core/passBins.js";import{getBinColorScheme as
|
|
1
|
+
import{diePassStatus as g}from"../core/dies.js";import{INPUT_DEFAULT_PASS_BINS as B,itemPassBins as m}from"../core/passBins.js";import{getBinColorScheme as w}from"./colorSchemes.js";export function resolveBinColors(s,n={}){return resolveBinColorsByWafer([{dies:s,passBins:n.passBins}],n).colors}export function binColorsForMaps(s,n={}){return resolveBinColorsForMaps(Array.isArray(s)?s:[s],n).colors}export function resolveBinColorsForMaps(s,n={}){return resolveBinColorsByWafer(s.map(e=>({dies:e.dies,passBins:m(e)})),{...n,hbinDefs:mergeBinDefs(s.map(e=>e.hbinDefs)),sbinDefs:mergeBinDefs(s.map(e=>e.sbinDefs))})}export function mergeBinDefs(s){const n=new Set,e=[];for(const a of s)for(const o of a??[])n.has(o.bin)||(n.add(o.bin),e.push(o));return e}export function resolveBinColorsByWafer(s,n={}){const e=w(n.binColorScheme),a=n.useDefinedBinColors??!0,o=h(s),f=b(o.hardBins,i=>o.pass.hard.has(i),e,a?n.hbinDefs:void 0,!1),t=b(o.softBins,i=>o.pass.soft.has(i),e,a?n.sbinDefs:void 0,!0);return{colors:{hard:f.colors,soft:t.colors,shared:{hard:f.shared,soft:t.shared},pass:o.pass},mixedHardBins:o.mixedHardBins}}export function binPassSets(s,n){return h([{dies:s,passBins:n}]).pass}export function binPassSetsByWafer(s){return h(s).pass}function h(s){const n=new Map,e=new Map,a=new Set;for(const t of s){const i=new Set(t.passBins??B);for(const l of t.dies){const r=g(l,i)===!0;l.hbin!=null&&(n.set(l.hbin,(n.get(l.hbin)??!0)&&r),r&&a.add(l.hbin)),l.sbin!=null&&e.set(l.sbin,(e.get(l.sbin)??!0)&&r)}}const o=t=>new Set([...t].filter(([,i])=>i).map(([i])=>i)),f={hard:o(n),soft:o(e)};return{hardBins:[...n.keys()],softBins:[...e.keys()],pass:f,mixedHardBins:[...a].filter(t=>!f.hard.has(t)).sort((t,i)=>t-i)}}function u(s){return s.length>8?`${s.slice(0,8).join(", ")} and ${s.length-8} more`:s.join(", ")}export function mixedPassBinsWarning(s){if(!s.length)return null;const n=s.length===1;return{code:"pass-bins-mixed",severity:"warning",message:`Hard ${n?"bin":"bins"} ${u(s)} ${n?"passes":"pass"} on some wafers and ${n?"fails":"fail"} on others: these wafers were built with different pass bins. Each wafer's own yield and die verdicts are correct, but a bin has one colour and one legend row, so ${n?"it is":"they are"} shown as failing there.`}}export function binColorsCover(s,n){if(!s.pass)return!1;for(const e of n)if(e.hbin!=null&&!s.hard.has(e.hbin)||e.sbin!=null&&!s.soft.has(e.sbin))return!1;return!0}export function binColorWarning(s,n){const e=n==="softBin"?"soft":n==="hardBin"?"hard":null;if(!e)return null;const a=s.shared[e];if(!a.length)return null;const o=u(a);return{code:"bin-colors-shared",severity:"warning",message:`${e==="hard"?"Hard":"Soft"} bins ${o} are drawn in a colour another bin also has, so colour alone cannot tell them apart. There are more bins than the colour scheme has distinct colours (or a bin definition repeats one). Use the legend, the tooltip, or highlight one bin at a time.`}}const S=1,x=2;function p(s,n,e,a){const o=s.length,f=a?Math.floor(o/2):0;return s[((n-e+f)%o+o)%o]}function b(s,n,e,a,o){const f=new Map;for(const r of a??[])r.color&&f.set(r.bin,r.color);const t=new Map;for(const r of s)t.set(r,f.get(r)??(n(r)?p(e.pass,r,S,o):p(e.fail,r,x,o)));const i=new Map;for(const[r,c]of t){const d=c.toLowerCase();i.set(d,[...i.get(d)??[],r])}const l=[...i.values()].filter(r=>r.length>1).flat().sort((r,c)=>r-c);return{colors:t,shared:l}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{hasPosition as se}from"../core/dies.js";import{getReticleCell as oe}from"../core/reticle.js";import{metadataCategoricalValue as re,metadataDisplayValue as le}from"../core/metadata.js";import{affineRotation as mt,affineMirror as Lt,affineCompose as it,affinePoint as Y,affineVector as pt,affineSwapsAxes as ce}from"../core/transforms.js";import{contrastTextColor as G,SPEC_PASS_FILL as yt,SPEC_FAIL_LOW as ue,SPEC_FAIL_HIGH as gt}from"./colorMap.js";import{resolveValueColorFn as fe}from"./colorSchemes.js";import{resolveBinColors as de,binColorsCover as he}from"./binColors.js";import{NO_DATA_FILL as j}from"./colorMap.js";import{diePassStatus as me}from"../core/dies.js";import{getDieTestValue as P,getTestPassStatus as Q,isParametricTest as bt}from"./buildWaferMap.js";import{fmt as xt,fmtColorbarAxis as Mt,fmtAggregationMethod as Rt}from"./fmt.js";import{metadataValueColor as pe}from"./colorMap.js";import{compareNatural as ye,clamp01 as Nt}from"../core/utils.js";import{prettyKey as Ot}from"../core/utils.js";export function resolveTestNumber(t,e){return e?.length?{testNumber:(findTestDef(e,t)??e[0]).testNumber}:{testNumber:t}}export function collectMetadataValues(t,e){const a=new Set;for(const n of t){if(n.partial||n.edgeExcluded)continue;const s=st(n,e);s!==void 0&&a.add(s)}return[...a].sort(ye)}export function findTestDef(t,e){return t?.find(a=>a.testNumber===e)}export function getUniqueTestNumbers(t){return[...new Set(t.flatMap(e=>e.testValues?Object.keys(e.testValues).map(Number):[]))].sort((e,a)=>e-a)}const ge="#d3d6db",Ht="#e8e9ea",be="#aab0ba";function st(t,e){return e?re(t.metadata?.[e]):void 0}export function classifySpec(t,e){return t===void 0?null:e?.limitLow!==void 0&&t<e.limitLow?"failLow":e?.limitHigh!==void 0&&t>e.limitHigh?"failHigh":"pass"}function xe(t,e,a){const{x:n,y:s}=t.center,i=mt(t.orientation,n,s),o=it(Lt(e?.x??!1,e?.y??!1,n,s),mt(t.orientation,n,s)),l=it(Lt(a?.flipX??!1,a?.flipY??!1,n,s),mt(a?.rotation??0,n,s));return{bakedToScreen:l,physicalToScreen:it(l,i),gridToScreen:it(l,o)}}function z(t,e=!1){return{points:[t],closed:e}}function Me(t,e){const{center:a,radius:n,notch:s}=t;let i=a.x+n*Math.cos(e),o=a.y+n*Math.sin(e);if(!s)return{x:i,y:o};const l=Math.sqrt(n**2-(s.length/2)**2),u=s.length/2,g=i-a.x,p=o-a.y;return s.type==="bottom"&&p<-l?(o=a.y-l,i=a.x+Math.max(-u,Math.min(u,g))):s.type==="top"&&p>l?(o=a.y+l,i=a.x+Math.max(-u,Math.min(u,g))):s.type==="left"&&g<-l?(i=a.x-l,o=a.y+Math.max(-u,Math.min(u,p))):s.type==="right"&&g>l&&(i=a.x+l,o=a.y+Math.max(-u,Math.min(u,p))),{x:i,y:o}}function At(t,e){return t.map(e).join(" / ")}function Se(t,e=256){const a=Array.from({length:e},(n,s)=>t(s/(e-1)));return n=>a[Math.min(e-1,Math.max(0,Math.round(n*(e-1))))]}function ke(t,e){const a=Math.max(1,Math.min(t.width,t.height)),n=t.width/Math.max(e.length,1);return Math.max(8,Math.min(16,Math.round(Math.min(a*.55,n*1.8))))}function ve(t,e,a){if(e?.length){const n=e.flatMap(s=>{const i=s.testNumber;if(!bt(s)){const u=Q(t,i,s);return u===void 0?[]:[{key:i,label:s.name,value:u?"Pass":"Fail"}]}const o=P(t,i);if(o===void 0)return[];const l=t.testPass?.[i]===!1?" <i>(recorded fail)</i>":"";return[{key:i,label:s.name,value:`${xt(o,s.unit,a)}${l}`}]});if(n.length)return n}return t.testValues&&Object.keys(t.testValues).length>0?Object.entries(t.testValues).map(([n,s])=>({key:Number(n),label:`Test ${n}`,value:xt(s,void 0,a)})):t.testPass&&Object.keys(t.testPass).length>0?Object.entries(t.testPass).map(([n,s])=>({key:Number(n),label:`Test ${n}`,value:s?"Pass":"Fail"})):[]}export function buildHoverText(t,e,a={}){const{testDefs:n,hbinDefs:s,sbinDefs:i,fallbackFormat:o,aggrMethod:l,lotSize:u,activeTest:g,reticleConfig:p,metadataFields:B}=a,D=s?new Map(s.map(f=>[f.bin,f])):null,T=i?new Map(i.map(f=>[f.bin,f])):null,b=[`Die (${t.x}, ${t.y})`];if(p&&se(t)){const f=oe(t,p);b.push(`Reticle (${f.column}, ${f.row})`)}if(e==="stackedValues"){const f=P(t,0);if(f!==void 0){const d=n?.[0],h=d?.testNumber,k=d?.name??(h!=null?`Test ${h}`:"Value"),m=l?` (${l})`:"";b.push(`${k}${m}: ${xt(f,d?.unit,o)}`)}}else if(e==="stackedBins"||e==="stackedSoftBins"){const f=P(t,0),d=e==="stackedSoftBins"?t.sbin:t.hbin;if(f!==void 0){const h=e==="stackedSoftBins"?T:D,k=d!==void 0?h?.get(d)?.name?`${d} \xB7 ${h.get(d).name}`:`Bin ${d}`:"Bin";let m;if(l==="percent")m=`${f.toFixed(0)}%`;else{const C=u?` (${(f/u*100).toFixed(0)}% of stacked wafers)`:"";m=`${f}${C}`}const x=l?` [${Rt(l)}]`:"";b.push(`${k}: ${m}${x}`)}}else{const f=ve(t,n,o);if(e==="value"&&f.length){const{testNumber:d}=resolveTestNumber(g??0,n),h=findTestDef(n,d),k=P(t,d);let m=f.findIndex(y=>y.key===d);m<0&&(m=0);const x=f[m];let C=`<b>${x.label}: ${x.value}</b>`;if(x.key===d&&h){const y=classifySpec(k,h);(y==="failLow"||y==="failHigh")&&(C+=" <i>(out of spec)</i>")}b.push(C);const R=f.length-1;R>0&&b.push(`<i>+${R} more test${R===1?"":"s"}</i>`)}else if(f.length){const d=f.length;b.push(`<i>${d} test result${d===1?"":"s"} recorded</i>`)}if(t.hbin!==void 0||t.sbin!==void 0){const d=[];if(t.hbin!==void 0){const h=D?.get(t.hbin)?.name,k=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);d.push(`HBin: ${k}`)}if(t.sbin!==void 0){const h=T?.get(t.sbin)?.name,k=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);d.push(`SBin: ${k}`)}b.push(d.join(" \xB7 "))}}t.retestCount!==void 0&&b.push(`Retests: ${t.retestCount}`),t.siteNum!==void 0&&b.push(`Site: ${t.siteNum}`),t.partId!==void 0&&b.push(`Part ID: ${t.partId}`),t.partial&&b.push("<i>partial die</i>"),t.probeIndex!==void 0&&b.push(`Probe: #${t.probeIndex}`);const $={...t.metadata??{}};for(const[f,d]of Object.entries($)){const h=le(d);if(h===void 0)continue;const k=B?.find(m=>m.key===f)?.label??Ot(f);b.push(`${k}: ${h}`)}return b.join("<br>")}export function buildMapTitle(t,e="engineering",a){const n=t.isLotStack&&t.lotSize!==void 0?`stacked (${t.lotSize} ${t.lotSize===1?"wafer":"wafers"})`:"stacked",s=i=>{const o=a?.length===1?a[0]:void 0;return o===void 0?`${i} Bin`:o.name?`${i} Bin ${o.bin} \xB7 ${o.name}`:`${i} Bin ${o.bin}`};switch(t.plotMode){case"hardBin":return{primary:"Hard Bin",secondary:""};case"softBin":return{primary:"Soft Bin",secondary:""};case"stackedBins":return{primary:s("Hard"),secondary:n};case"stackedSoftBins":return{primary:s("Soft"),secondary:n};case"metadata":{const i=t.metadataFields?.find(l=>l.key===t.activeMetadataKey),o=t.activeMetadataKey?Ot(t.activeMetadataKey):"Metadata";return{primary:i?.label??o,secondary:""}}case"stackedValues":{const i=t.testDefs?.[0],o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=Mt(o,i?.name,i?.unit,e),u=l||"Value",g=t.aggrMethod?` \xB7 ${Rt(t.aggrMethod)}`:"";return{primary:`${u}${g}`,secondary:n}}default:{const i=findTestDef(t.testDefs,t.activeTest),o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=Mt(o,i?.name,i?.unit,e),u=l||i?.name;if(t.passFailDisplay!=="off"){const g=i?.testNumber??t.activeTest,p=u?`${u} \xB7 #${g}`:`Test ${g}`,B=t.passFailDisplay==="spec"?"Spec pass/fail":i!==void 0&&!bt(i)?"Functional pass/fail":"Tester pass/fail";return{primary:p,secondary:B}}return{primary:u??`Test ${t.activeTest}`,secondary:""}}}}export function generateTextOverlay(t,e,a){const{plotMode:n,colorFns:s,normalize:i,activeTest:o,valueRange:l,testDefs:u,fallbackFormat:g,passFailDisplay:p="off",activeMetadataKey:B,metadataColorMap:D,allIntegerValues:T}=a,b=findTestDef(u,o),{tickFmt:$}=Mt(l[1],b?.name,b?.unit,g),f=T?h=>String(Math.round(h)):$,{testNumber:d}=resolveTestNumber(o,u);return t.flatMap((h,k)=>{let m="",x="#111111";if(n==="value"&&p==="test"){const y=Q(h,d,b);if(y===void 0)return[];m=y?"P":"F",x=G(y?yt:gt)}else if(n==="value"){const y=P(h,d);if(y===void 0)return[];m=At([y],f),x=G(s.forValue(i(y)))}else if(n==="hardBin"||n==="softBin"){const y=n==="softBin"?h.sbin:h.hbin;if(y===void 0)return[];m=String(y),x=G(s.forBin(y))}else if(n==="metadata"){const y=st(h,B);if(y===void 0)return[];m=y,x=G(D?.get(m)??j)}else{const y=P(h,0);if(y===void 0)return[];m=At([y],f),x=G(s.forValue(i(y)))}const C=e?e[k*2]:h.physX,R=e?e[k*2+1]:h.physY;return[{x:C,y:R,text:m,fontSize:ke(h,m),color:x,align:"center"}]})}function Et(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function $e(t,e,a=720){const{center:n,radius:s,notch:i}=t,o=$=>Y(e,$.x,$.y);if(!(i!==void 0&&t.diameter>150)){const $=[];for(let f=0;f<=a;f++){const d=2*Math.PI*f/a;$.push(o(Me(t,d)))}return[{kind:"wafer-boundary",...z($,!0),lineColor:"#888888",lineWidth:1}]}const u=1.25,g=i.length,p=Et(i.type),B=Math.atan2(g,s),D=s-u,T=[];let b=!1;for(let $=0;$<=a;$++){const f=2*Math.PI*$/a;let d=f-p;for(;d>Math.PI;)d-=2*Math.PI;for(;d<-Math.PI;)d+=2*Math.PI;if(Math.abs(d)<=B){if(!b){b=!0;const h={x:n.x+s*Math.cos(p-B),y:n.y+s*Math.sin(p-B)},k={x:n.x+D*Math.cos(p),y:n.y+D*Math.sin(p)},m={x:n.x+s*Math.cos(p+B),y:n.y+s*Math.sin(p+B)};T.push(o(h),o(k),o(m))}continue}T.push(o({x:n.x+s*Math.cos(f),y:n.y+s*Math.sin(f)}))}return[{kind:"wafer-boundary",...z(T,!0),lineColor:"#888888",lineWidth:1}]}function Be(t,e,a,n=360){const s=[],i=Math.max(1,Math.floor(a));for(let o=1;o<i;o++){const l=t.radius*o/i,u=[];for(let g=0;g<=n;g++){const p=2*Math.PI*g/n,B={x:t.center.x+l*Math.cos(p),y:t.center.y+l*Math.sin(p)};u.push(Y(e,B.x,B.y))}s.push({kind:"ring-boundary",...z(u,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function Te(t,e,a,n){const{x:s,y:i}=t.center,o=t.radius,l=(f,d)=>Y(e,f,d),u=a-s,g=Math.sqrt(Math.max(0,o*o-u*u)),p=l(a,i-g),B=l(a,i+g),D=n-i,T=Math.sqrt(Math.max(0,o*o-D*D)),b=l(s-T,n),$=l(s+T,n);return[{kind:"quadrant-boundary",...z([p,B]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...z([b,$]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function Ie(t,e){return t.map(a=>{const n=a.width/2,s=a.height/2;return{kind:"reticle",points:[[[a.x-n,a.y-s],[a.x+n,a.y-s],[a.x+n,a.y+s],[a.x-n,a.y+s]].map(([o,l])=>Y(e,o,l))],closed:!0,lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5,fill:"rgba(0,0,0,0)"}})}function Ve(t){const e=t.filter(a=>a.probeIndex!==void 0).sort((a,n)=>(a.probeIndex??0)-(n.probeIndex??0));return e.length?[{kind:"probe-path",...z(e.map(a=>({x:a.physX,y:a.physY}))),lineColor:"rgba(220,80,0,0.55)",lineWidth:1}]:[]}function Ce(t,e,a,n,s,i,o,l,u,g,p,B,D,T,b="off",$,f,d){const h=e.width-o,k=e.height-o,m=i?k:h,x=i?h:k,C=S=>s==="softBin"?S.sbin:S.hbin,R=S=>st(S,$);if(e.partial){t.push({x:a,y:n,width:m,height:x,fill:ge,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:n,width:m,height:x,fill:be,type:"stacked",metadata:e.metadata});return}if(u!==void 0&&(s==="hardBin"||s==="softBin")&&C(e)!==u){t.push({x:a,y:n,width:m,height:x,fill:Ht,type:"hardBin",metadata:e.metadata});return}if(d!==void 0&&s==="metadata"&&R(e)!==d){t.push({x:a,y:n,width:m,height:x,fill:Ht,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(b==="test"){const w=Q(e,p,T),ot=w===void 0?j:w?yt:gt;t.push({x:a,y:n,width:m,height:x,fill:ot,type:"value",metadata:e.metadata});return}const S=P(e,p),N=classifySpec(S,T);let _,U;S===void 0?_=j:b==="spec"?_=N==="failLow"?ue:N==="failHigh"?gt:yt:(_=l.forValue(g(S)),(N==="failLow"||N==="failHigh")&&(U=N)),t.push({x:a,y:n,width:m,height:x,fill:_,type:"value",specMark:U,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const S=C(e),N=S!=null?l.forBin(S):j,_=S!=null&&me(e,D)===!1?!0:void 0;t.push({x:a,y:n,width:m,height:x,fill:N,type:s,binFail:_,metadata:e.metadata});return}if(s==="metadata"){const S=R(e),N=S!==void 0?f?.get(S)??j:j;t.push({x:a,y:n,width:m,height:x,fill:N,type:"metadata",metadata:e.metadata});return}const y=P(e,0),X=y!==void 0?l.forValue(g(y)):j;t.push({x:a,y:n,width:m,height:x,fill:X,type:"value",metadata:e.metadata})}function Fe(t,e,a){const n=t.radius*.15,s=pt(e,n,0),i=pt(e,0,n),o=s.x+i.x>=0?-1:1,l=s.y+i.y>=0?-1:1,u={x:t.center.x+o*t.radius*.9,y:t.center.y+l*t.radius*.9},g={x:u.x+s.x,y:u.y+s.y},p={x:u.x+i.x,y:u.y+i.y};return a.push({x:g.x+s.x*.35,y:g.y+s.y*.35,text:"+X",fontSize:10,color:"#cc3300",align:"center",role:"indicator"},{x:p.x+i.x*.35,y:p.y+i.y*.35,text:"+Y",fontSize:10,color:"#0044cc",align:"center",role:"indicator"}),[{kind:"xy-indicator",...z([u,g]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...z([u,p]),lineColor:"#0044cc",lineWidth:2}]}export function buildView(t,e,a={},n){const s=a.reticles??[],{plotMode:i="value",showDieLabels:o=!1,showReticle:l=!1,showProbePath:u=!1,ringCount:g=4,showRingBoundaries:p=!1,showQuadrantBoundaries:B=!1,showXYIndicator:D=!1,binColorScheme:T="default",valueColorScheme:b="default",reverseValueScheme:$=!1,useDefinedBinColors:f=!0,binColors:d,passBins:h=[1],highlightBin:k,highlightMetadataValue:m,interactiveTransform:x,valueRange:C,metadataValueOrder:R,testDefs:y,activeTest:X=0,activeMetadataKey:S,fallbackFormat:N="engineering",aggregationMethod:_,lotSize:U,dataAxisFlip:w,isLotStack:ot=!1,logScale:_t,colorbarRangeMode:wt="spec",passFailDisplay:Wt,showPartialDies:jt=!0}=a,St=Wt??"off",rt=n?.hbinDefs,lt=n?.sbinDefs,kt=n?.metadataFields,zt=kt?.find(c=>c.key===S),Kt={x:(w?.x??!1)!==(x?.flipX??!1),y:(w?.y??!1)!==(x?.flipY??!1)},qt=rt?new Map(rt.map(c=>[c.bin,c])):null,Xt=lt?new Map(lt.map(c=>[c.bin,c])):null,Yt=i==="softBin"?Xt:qt,Gt=new Set(h),ct=d&&he(d,e)?d:de(e,{passBins:h,binColorScheme:T,hbinDefs:rt,sbinDefs:lt,useDefinedBinColors:f}),Qt=i==="softBin"?ct.soft:ct.hard,vt={forValue:Se(fe(b,$)),forBin:c=>Qt.get(c)??j};let{testNumber:H}=resolveTestNumber(X,y);if(!y?.length&&i==="value"&&!e.some(r=>r.testValues&&H in r.testValues)){const r=e.reduce((M,V)=>{if(!V.testValues)return M;const v=Object.keys(V.testValues).map(Number),L=v.length?Math.min(...v):void 0;return L!==void 0&&(M===void 0||L<M)?L:M},void 0);r!==void 0&&(H=r)}let J;if(Array.isArray(C))J=C;else if(C){const{testNumber:c}=resolveTestNumber(C.test,y);J=c===H?C.range:void 0}const I=findTestDef(y,H),$t=i==="value"&&I!==void 0&&!bt(I),Ut=I!==void 0&&(I.limitLow!==void 0||I.limitHigh!==void 0);let E="off";i==="value"&&($t?E="test":St==="spec"&&Ut?E="spec":St==="test"&&e.some(c=>Q(c,H,I)!==void 0)&&(E="test"));const Bt=E==="spec"?"spec":wt,ut=i==="stackedValues"||i==="stackedBins"||i==="stackedSoftBins";let O,A,Z=!1;if(J){[O,A]=J;let c=!0,r=!1;for(const M of e){const V=ut?P(M,0):P(M,H);if(V!==void 0&&(r=!0,!Number.isInteger(V))){c=!1;break}}Z=r&&c}else{const c=!ut&&i==="value"&&Bt==="spec"&&I&&(I.limitLow!==void 0||I.limitHigh!==void 0);let r=1/0,M=-1/0,V=!0;for(const v of e){const L=ut?P(v,0):P(v,H);L!==void 0&&(L<r&&(r=L),L>M&&(M=L),Number.isInteger(L)||(V=!1))}isFinite(r)||(V=!1),Z=V,c?(O=I.limitLow!==void 0?I.limitLow:isFinite(r)?r:0,A=I.limitHigh!==void 0?I.limitHigh:isFinite(M)?M:1):(O=isFinite(r)?r:0,A=isFinite(M)?M:1)}O===A&&(A=O+1);const Tt=$t?!1:_t??I?.logScale??!1,It=Tt&&O>0&&A>0,Jt=It;let tt;if(It){const c=Math.log10(O),r=Math.log10(A)-c;tt=M=>M<=0?0:Nt((Math.log10(M)-c)/r)}else tt=c=>Nt((c-O)/(A-O));const F=xe(t,w,x);let Vt=null;if(t.notch){const c=Et(t.notch.type),r=pt(F.physicalToScreen,Math.cos(c)*t.radius,Math.sin(c)*t.radius),M=Math.hypot(r.x,r.y);M>0&&(Vt={x:r.x/M,y:r.y/M})}let K=1/0;for(let c=0;c<e.length;c++){const r=e[c];r.width<K&&(K=r.width),r.height<K&&(K=r.height)}const Ct=1,Zt=K!==1/0?Math.min(Ct,K*.12):Ct,Ft=[],ft=[],te=F.bakedToScreen.a===1&&F.bakedToScreen.b===0&&F.bakedToScreen.c===0&&F.bakedToScreen.d===1&&F.bakedToScreen.e===0&&F.bakedToScreen.f===0;let W=null;if(!te){W=new Float64Array(e.length*2);for(let c=0;c<e.length;c++){const r=Y(F.bakedToScreen,e[c].physX,e[c].physY);W[c*2]=r.x,W[c*2+1]=r.y}}const ee=ce(F.gridToScreen),ne=i==="hardBin"||i==="softBin"||i==="stackedBins"||i==="stackedSoftBins",ae=i==="softBin"||i==="stackedSoftBins";let et=ne?new Map:void 0;const dt=i==="value"&&E==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,ht=i==="value"&&E==="test"?{pass:0,fail:0}:void 0,Dt=i==="metadata";let nt=Dt?new Map:void 0,at=null;if(Dt&&S){const c=collectMetadataValues(e,S),r=R?.key===S?R.values:void 0,M=r?[...r,...c.filter(V=>!r.includes(V))]:c;at=new Map(M.map((V,v)=>[V,zt?.values?.find(L=>L.value===V)?.color??pe(v)]))}for(let c=0;c<e.length;c++){const r=e[c],M=W?W[c*2]:r.physX,V=W?W[c*2+1]:r.physY;if(ft.push({x:M,y:V}),et&&!r.partial&&!r.edgeExcluded){const v=ae?r.sbin:r.hbin;v!=null&&et.set(v,(et.get(v)??0)+1)}if(dt&&!r.partial&&!r.edgeExcluded){const v=classifySpec(P(r,H),I);v&&dt[v]++}if(ht&&!r.partial&&!r.edgeExcluded){const v=Q(r,H,I);v!==void 0&&ht[v?"pass":"fail"]++}if(nt&&!r.partial&&!r.edgeExcluded){const v=st(r,S);v!==void 0&&nt.set(v,(nt.get(v)??0)+1)}r.partial&&!jt||Ce(Ft,r,M,V,i,ee,Zt,vt,k,tt,H,Yt,Gt,I,E,S,at,m)}const Pt=o?generateTextOverlay(e,W,{plotMode:i,colorFns:vt,normalize:tt,activeTest:X,valueRange:[O,A],testDefs:y,fallbackFormat:N,passFailDisplay:E,activeMetadataKey:S,metadataColorMap:at,allIntegerValues:Z}):[],q=$e(t,F.physicalToScreen);p&&q.push(...Be(t,F.physicalToScreen,g)),B&&q.push(...Te(t,F.physicalToScreen,t.center.x,t.center.y)),l&&q.push(...Ie(s,F.gridToScreen)),u&&q.push(...Ve(e)),D&&q.push(...Fe(t,F.gridToScreen,Pt));const ie=ft.length>0?{minX:t.center.x-t.radius,maxX:t.center.x+t.radius,minY:t.center.y-t.radius,maxY:t.center.y+t.radius}:null;return{rectangles:Ft,hoverPoints:ft,texts:Pt,overlays:q,plotMode:i,binColorScheme:T,valueColorScheme:b,reverseValueScheme:$,binColors:ct,metadata:t.metadata??null,dies:e,valueRange:[O,A],allIntegerValues:Z,testDefs:y,activeTest:X,logScale:Jt,logScaleRequested:Tt,aggrMethod:_,lotSize:U,axisFlip:Kt,dataAxisFlip:{x:w?.x??!1,y:w?.y??!1},gridToScreen:F.gridToScreen,rotation:((t.orientation+(x?.rotation??0))%360+360)%360,hasReticle:s.length>0,isLotStack:ot,passFailDisplay:E,colorbarRangeMode:Bt,waferCenter:t.center,waferRadius:t.radius,notchDir:Vt,binCounts:et,metadataCounts:nt,metadataColorMap:at,activeMetadataKey:S,metadataFields:kt,specCounts:dt,passFailCounts:ht,dieBounds:ie}}export{getDieKey}from"../core/dies.js";
|
|
1
|
+
import{hasPosition as oe}from"../core/dies.js";import{getReticleCell as re}from"../core/reticle.js";import{metadataCategoricalValue as le,metadataDisplayValue as ce}from"../core/metadata.js";import{affineRotation as pt,affineMirror as Rt,affineCompose as st,affinePoint as G,affineVector as yt,affineSwapsAxes as ue}from"../core/transforms.js";import{contrastTextColor as Q,SPEC_PASS_FILL as gt,SPEC_FAIL_LOW as fe,SPEC_FAIL_HIGH as bt}from"./colorMap.js";import{resolveValueColorFn as de}from"./colorSchemes.js";import{resolveBinColors as he,binColorsCover as me}from"./binColors.js";import{NO_DATA_FILL as z}from"./colorMap.js";import{diePassStatus as pe}from"../core/dies.js";import{getDieTestValue as P,getTestPassStatus as U,isParametricTest as xt}from"./buildWaferMap.js";import{fmt as Mt,fmtColorbarAxis as St,fmtAggregationMethod as Nt}from"./fmt.js";import{metadataValueColor as ye}from"./colorMap.js";import{compareNatural as ge,clamp01 as Ht,escHtml as O}from"../core/utils.js";import{prettyKey as Ot}from"../core/utils.js";export function resolveTestNumber(t,e){return e?.length?{testNumber:(findTestDef(e,t)??e[0]).testNumber}:{testNumber:t}}export function collectMetadataValues(t,e){const a=new Set;for(const n of t){if(n.partial||n.edgeExcluded)continue;const s=ot(n,e);s!==void 0&&a.add(s)}return[...a].sort(ge)}export function findTestDef(t,e){return t?.find(a=>a.testNumber===e)}export function getUniqueTestNumbers(t){return[...new Set(t.flatMap(e=>e.testValues?Object.keys(e.testValues).map(Number):[]))].sort((e,a)=>e-a)}const be="#d3d6db",At="#e8e9ea",xe="#aab0ba";function ot(t,e){return e?le(t.metadata?.[e]):void 0}export function classifySpec(t,e){return t===void 0?null:e?.limitLow!==void 0&&t<e.limitLow?"failLow":e?.limitHigh!==void 0&&t>e.limitHigh?"failHigh":"pass"}function Me(t,e,a){const{x:n,y:s}=t.center,i=pt(t.orientation,n,s),o=st(Rt(e?.x??!1,e?.y??!1,n,s),pt(t.orientation,n,s)),l=st(Rt(a?.flipX??!1,a?.flipY??!1,n,s),pt(a?.rotation??0,n,s));return{bakedToScreen:l,physicalToScreen:st(l,i),gridToScreen:st(l,o)}}function K(t,e=!1){return{points:[t],closed:e}}function Se(t,e){const{center:a,radius:n,notch:s}=t;let i=a.x+n*Math.cos(e),o=a.y+n*Math.sin(e);if(!s)return{x:i,y:o};const l=Math.sqrt(n**2-(s.length/2)**2),d=s.length/2,g=i-a.x,p=o-a.y;return s.type==="bottom"&&p<-l?(o=a.y-l,i=a.x+Math.max(-d,Math.min(d,g))):s.type==="top"&&p>l?(o=a.y+l,i=a.x+Math.max(-d,Math.min(d,g))):s.type==="left"&&g<-l?(i=a.x-l,o=a.y+Math.max(-d,Math.min(d,p))):s.type==="right"&&g>l&&(i=a.x+l,o=a.y+Math.max(-d,Math.min(d,p))),{x:i,y:o}}function Et(t,e){return t.map(e).join(" / ")}function ke(t,e=256){const a=Array.from({length:e},(n,s)=>t(s/(e-1)));return n=>a[Math.min(e-1,Math.max(0,Math.round(n*(e-1))))]}function ve(t,e){const a=Math.max(1,Math.min(t.width,t.height)),n=t.width/Math.max(e.length,1);return Math.max(8,Math.min(16,Math.round(Math.min(a*.55,n*1.8))))}function $e(t,e,a){if(e?.length){const n=e.flatMap(s=>{const i=s.testNumber;if(!xt(s)){const l=U(t,i,s);return l===void 0?[]:[{key:i,label:s.name,value:l?"Pass":"Fail"}]}const o=P(t,i);return o===void 0?[]:[{key:i,label:s.name,value:Mt(o,s.unit,a),recordedFail:t.testPass?.[i]===!1}]});if(n.length)return n}return t.testValues&&Object.keys(t.testValues).length>0?Object.entries(t.testValues).map(([n,s])=>({key:Number(n),label:`Test ${n}`,value:Mt(s,void 0,a)})):t.testPass&&Object.keys(t.testPass).length>0?Object.entries(t.testPass).map(([n,s])=>({key:Number(n),label:`Test ${n}`,value:s?"Pass":"Fail"})):[]}export function buildHoverText(t,e,a={}){const{testDefs:n,hbinDefs:s,sbinDefs:i,fallbackFormat:o,aggrMethod:l,lotSize:d,activeTest:g,reticleConfig:p,metadataFields:B}=a,D=s?new Map(s.map(u=>[u.bin,u])):null,T=i?new Map(i.map(u=>[u.bin,u])):null,b=[O(`Die (${t.x}, ${t.y})`)];if(p&&oe(t)){const u=re(t,p);b.push(O(`Reticle (${u.column}, ${u.row})`))}if(e==="stackedValues"){const u=P(t,0);if(u!==void 0){const f=n?.[0],h=f?.testNumber,k=f?.name??(h!=null?`Test ${h}`:"Value"),m=l?` (${l})`:"";b.push(O(`${k}${m}: ${Mt(u,f?.unit,o)}`))}}else if(e==="stackedBins"||e==="stackedSoftBins"){const u=P(t,0),f=e==="stackedSoftBins"?t.sbin:t.hbin;if(u!==void 0){const h=e==="stackedSoftBins"?T:D,k=f!==void 0?h?.get(f)?.name?`${f} \xB7 ${h.get(f).name}`:`Bin ${f}`:"Bin";let m;if(l==="percent")m=`${u.toFixed(0)}%`;else{const C=d?` (${(u/d*100).toFixed(0)}% of stacked wafers)`:"";m=`${u}${C}`}const x=l?` [${Nt(l)}]`:"";b.push(O(`${k}: ${m}${x}`))}}else{const u=$e(t,n,o);if(e==="value"&&u.length){const{testNumber:f}=resolveTestNumber(g??0,n),h=findTestDef(n,f),k=P(t,f);let m=u.findIndex(y=>y.key===f);m<0&&(m=0);const x=u[m];let C=`<b>${O(`${x.label}: ${x.value}`)}${x.recordedFail?" <i>(recorded fail)</i>":""}</b>`;if(x.key===f&&h){const y=classifySpec(k,h);(y==="failLow"||y==="failHigh")&&(C+=" <i>(out of spec)</i>")}b.push(C);const R=u.length-1;R>0&&b.push(`<i>+${R} more test${R===1?"":"s"}</i>`)}else if(u.length){const f=u.length;b.push(`<i>${f} test result${f===1?"":"s"} recorded</i>`)}if(t.hbin!==void 0||t.sbin!==void 0){const f=[];if(t.hbin!==void 0){const h=D?.get(t.hbin)?.name,k=h?`${t.hbin} \xB7 ${h}`:String(t.hbin);f.push(`HBin: ${k}`)}if(t.sbin!==void 0){const h=T?.get(t.sbin)?.name,k=h?`${t.sbin} \xB7 ${h}`:String(t.sbin);f.push(`SBin: ${k}`)}b.push(f.map(O).join(" \xB7 "))}}t.retestCount!==void 0&&b.push(O(`Retests: ${t.retestCount}`)),t.siteNum!==void 0&&b.push(O(`Site: ${t.siteNum}`)),t.partId!==void 0&&b.push(O(`Part ID: ${t.partId}`)),t.partial&&b.push("<i>partial die</i>"),t.probeIndex!==void 0&&b.push(O(`Probe: #${t.probeIndex}`));const $={...t.metadata??{}};for(const[u,f]of Object.entries($)){const h=ce(f);if(h===void 0)continue;const k=B?.find(m=>m.key===u)?.label??Ot(u);b.push(O(`${k}: ${h}`))}return b.join("<br>")}export function buildMapTitle(t,e="engineering",a){const n=t.isLotStack&&t.lotSize!==void 0?`stacked (${t.lotSize} ${t.lotSize===1?"wafer":"wafers"})`:"stacked",s=i=>{const o=a?.length===1?a[0]:void 0;return o===void 0?`${i} Bin`:o.name?`${i} Bin ${o.bin} \xB7 ${o.name}`:`${i} Bin ${o.bin}`};switch(t.plotMode){case"hardBin":return{primary:"Hard Bin",secondary:""};case"softBin":return{primary:"Soft Bin",secondary:""};case"stackedBins":return{primary:s("Hard"),secondary:n};case"stackedSoftBins":return{primary:s("Soft"),secondary:n};case"metadata":{const i=t.metadataFields?.find(l=>l.key===t.activeMetadataKey),o=t.activeMetadataKey?Ot(t.activeMetadataKey):"Metadata";return{primary:i?.label??o,secondary:""}}case"stackedValues":{const i=t.testDefs?.[0],o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=St(o,i?.name,i?.unit,e),d=l||"Value",g=t.aggrMethod?` \xB7 ${Nt(t.aggrMethod)}`:"";return{primary:`${d}${g}`,secondary:n}}default:{const i=findTestDef(t.testDefs,t.activeTest),o=t.valueRange[1]||t.valueRange[0]||0,{axisLabel:l}=St(o,i?.name,i?.unit,e),d=l||i?.name;if(t.passFailDisplay!=="off"){const g=i?.testNumber??t.activeTest,p=d?`${d} \xB7 #${g}`:`Test ${g}`,B=t.passFailDisplay==="spec"?"Spec pass/fail":i!==void 0&&!xt(i)?"Functional pass/fail":"Tester pass/fail";return{primary:p,secondary:B}}return{primary:d??`Test ${t.activeTest}`,secondary:""}}}}export function generateTextOverlay(t,e,a){const{plotMode:n,colorFns:s,normalize:i,activeTest:o,valueRange:l,testDefs:d,fallbackFormat:g,passFailDisplay:p="off",activeMetadataKey:B,metadataColorMap:D,allIntegerValues:T}=a,b=findTestDef(d,o),{tickFmt:$}=St(l[1],b?.name,b?.unit,g),u=T?h=>String(Math.round(h)):$,{testNumber:f}=resolveTestNumber(o,d);return t.flatMap((h,k)=>{let m="",x="#111111";if(n==="value"&&p==="test"){const y=U(h,f,b);if(y===void 0)return[];m=y?"P":"F",x=Q(y?gt:bt)}else if(n==="value"){const y=P(h,f);if(y===void 0)return[];m=Et([y],u),x=Q(s.forValue(i(y)))}else if(n==="hardBin"||n==="softBin"){const y=n==="softBin"?h.sbin:h.hbin;if(y===void 0)return[];m=String(y),x=Q(s.forBin(y))}else if(n==="metadata"){const y=ot(h,B);if(y===void 0)return[];m=y,x=Q(D?.get(m)??z)}else{const y=P(h,0);if(y===void 0)return[];m=Et([y],u),x=Q(s.forValue(i(y)))}const C=e?e[k*2]:h.physX,R=e?e[k*2+1]:h.physY;return[{x:C,y:R,text:m,fontSize:ve(h,m),color:x,align:"center"}]})}function _t(t){return t==="top"?Math.PI/2:t==="bottom"?-Math.PI/2:t==="left"?Math.PI:0}function Be(t,e,a=720){const{center:n,radius:s,notch:i}=t,o=$=>G(e,$.x,$.y);if(!(i!==void 0&&t.diameter>150)){const $=[];for(let u=0;u<=a;u++){const f=2*Math.PI*u/a;$.push(o(Se(t,f)))}return[{kind:"wafer-boundary",...K($,!0),lineColor:"#888888",lineWidth:1}]}const d=1.25,g=i.length,p=_t(i.type),B=Math.atan2(g,s),D=s-d,T=[];let b=!1;for(let $=0;$<=a;$++){const u=2*Math.PI*$/a;let f=u-p;for(;f>Math.PI;)f-=2*Math.PI;for(;f<-Math.PI;)f+=2*Math.PI;if(Math.abs(f)<=B){if(!b){b=!0;const h={x:n.x+s*Math.cos(p-B),y:n.y+s*Math.sin(p-B)},k={x:n.x+D*Math.cos(p),y:n.y+D*Math.sin(p)},m={x:n.x+s*Math.cos(p+B),y:n.y+s*Math.sin(p+B)};T.push(o(h),o(k),o(m))}continue}T.push(o({x:n.x+s*Math.cos(u),y:n.y+s*Math.sin(u)}))}return[{kind:"wafer-boundary",...K(T,!0),lineColor:"#888888",lineWidth:1}]}function Te(t,e,a,n=360){const s=[],i=Math.max(1,Math.floor(a));for(let o=1;o<i;o++){const l=t.radius*o/i,d=[];for(let g=0;g<=n;g++){const p=2*Math.PI*g/n,B={x:t.center.x+l*Math.cos(p),y:t.center.y+l*Math.sin(p)};d.push(G(e,B.x,B.y))}s.push({kind:"ring-boundary",...K(d,!0),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5})}return s}function Ie(t,e,a,n){const{x:s,y:i}=t.center,o=t.radius,l=(u,f)=>G(e,u,f),d=a-s,g=Math.sqrt(Math.max(0,o*o-d*d)),p=l(a,i-g),B=l(a,i+g),D=n-i,T=Math.sqrt(Math.max(0,o*o-D*D)),b=l(s-T,n),$=l(s+T,n);return[{kind:"quadrant-boundary",...K([p,B]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5},{kind:"quadrant-boundary",...K([b,$]),lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5}]}function Ve(t,e){return t.map(a=>{const n=a.width/2,s=a.height/2;return{kind:"reticle",points:[[[a.x-n,a.y-s],[a.x+n,a.y-s],[a.x+n,a.y+s],[a.x-n,a.y+s]].map(([o,l])=>G(e,o,l))],closed:!0,lineColor:"rgba(255,255,255,0.7)",lineWidth:1.5,fill:"rgba(0,0,0,0)"}})}function Ce(t){const e=t.filter(a=>a.probeIndex!==void 0).sort((a,n)=>(a.probeIndex??0)-(n.probeIndex??0));return e.length?[{kind:"probe-path",...K(e.map(a=>({x:a.physX,y:a.physY}))),lineColor:"rgba(220,80,0,0.55)",lineWidth:1}]:[]}function Fe(t,e,a,n,s,i,o,l,d,g,p,B,D,T,b="off",$,u,f){const h=e.width-o,k=e.height-o,m=i?k:h,x=i?h:k,C=S=>s==="softBin"?S.sbin:S.hbin,R=S=>ot(S,$);if(e.partial){t.push({x:a,y:n,width:m,height:x,fill:be,type:"stacked",metadata:e.metadata});return}if(e.edgeExcluded){t.push({x:a,y:n,width:m,height:x,fill:xe,type:"stacked",metadata:e.metadata});return}if(d!==void 0&&(s==="hardBin"||s==="softBin")&&C(e)!==d){t.push({x:a,y:n,width:m,height:x,fill:At,type:"hardBin",metadata:e.metadata});return}if(f!==void 0&&s==="metadata"&&R(e)!==f){t.push({x:a,y:n,width:m,height:x,fill:At,type:"metadata",metadata:e.metadata});return}if(s==="value"){if(b==="test"){const W=U(e,p,T),rt=W===void 0?z:W?gt:bt;t.push({x:a,y:n,width:m,height:x,fill:rt,type:"value",metadata:e.metadata});return}const S=P(e,p),N=classifySpec(S,T);let w,J;S===void 0?w=z:b==="spec"?w=N==="failLow"?fe:N==="failHigh"?bt:gt:(w=l.forValue(g(S)),(N==="failLow"||N==="failHigh")&&(J=N)),t.push({x:a,y:n,width:m,height:x,fill:w,type:"value",specMark:J,metadata:e.metadata});return}if(s==="hardBin"||s==="softBin"){const S=C(e),N=S!=null?l.forBin(S):z,w=S!=null&&pe(e,D)===!1?!0:void 0;t.push({x:a,y:n,width:m,height:x,fill:N,type:s,binFail:w,metadata:e.metadata});return}if(s==="metadata"){const S=R(e),N=S!==void 0?u?.get(S)??z:z;t.push({x:a,y:n,width:m,height:x,fill:N,type:"metadata",metadata:e.metadata});return}const y=P(e,0),Y=y!==void 0?l.forValue(g(y)):z;t.push({x:a,y:n,width:m,height:x,fill:Y,type:"value",metadata:e.metadata})}function De(t,e,a){const n=t.radius*.15,s=yt(e,n,0),i=yt(e,0,n),o=s.x+i.x>=0?-1:1,l=s.y+i.y>=0?-1:1,d={x:t.center.x+o*t.radius*.9,y:t.center.y+l*t.radius*.9},g={x:d.x+s.x,y:d.y+s.y},p={x:d.x+i.x,y:d.y+i.y};return a.push({x:g.x+s.x*.35,y:g.y+s.y*.35,text:"+X",fontSize:10,color:"#cc3300",align:"center",role:"indicator"},{x:p.x+i.x*.35,y:p.y+i.y*.35,text:"+Y",fontSize:10,color:"#0044cc",align:"center",role:"indicator"}),[{kind:"xy-indicator",...K([d,g]),lineColor:"#cc3300",lineWidth:2},{kind:"xy-indicator",...K([d,p]),lineColor:"#0044cc",lineWidth:2}]}export function buildView(t,e,a={},n){const s=a.reticles??[],{plotMode:i="value",showDieLabels:o=!1,showReticle:l=!1,showProbePath:d=!1,ringCount:g=4,showRingBoundaries:p=!1,showQuadrantBoundaries:B=!1,showXYIndicator:D=!1,binColorScheme:T="default",valueColorScheme:b="default",reverseValueScheme:$=!1,useDefinedBinColors:u=!0,binColors:f,passBins:h=[1],highlightBin:k,highlightMetadataValue:m,interactiveTransform:x,valueRange:C,metadataValueOrder:R,testDefs:y,activeTest:Y=0,activeMetadataKey:S,fallbackFormat:N="engineering",aggregationMethod:w,lotSize:J,dataAxisFlip:W,isLotStack:rt=!1,logScale:wt,colorbarRangeMode:Wt="spec",passFailDisplay:jt,showPartialDies:zt=!0}=a,kt=jt??"off",lt=n?.hbinDefs,ct=n?.sbinDefs,vt=n?.metadataFields,Kt=vt?.find(c=>c.key===S),qt={x:(W?.x??!1)!==(x?.flipX??!1),y:(W?.y??!1)!==(x?.flipY??!1)},Xt=lt?new Map(lt.map(c=>[c.bin,c])):null,Yt=ct?new Map(ct.map(c=>[c.bin,c])):null,Gt=i==="softBin"?Yt:Xt,Qt=new Set(h),ut=f&&me(f,e)?f:he(e,{passBins:h,binColorScheme:T,hbinDefs:lt,sbinDefs:ct,useDefinedBinColors:u}),Ut=i==="softBin"?ut.soft:ut.hard,$t={forValue:ke(de(b,$)),forBin:c=>Ut.get(c)??z};let{testNumber:A}=resolveTestNumber(Y,y);if(!y?.length&&i==="value"&&!e.some(r=>r.testValues&&A in r.testValues)){const r=e.reduce((M,V)=>{if(!V.testValues)return M;const v=Object.keys(V.testValues).map(Number),L=v.length?Math.min(...v):void 0;return L!==void 0&&(M===void 0||L<M)?L:M},void 0);r!==void 0&&(A=r)}let Z;if(Array.isArray(C))Z=C;else if(C){const{testNumber:c}=resolveTestNumber(C.test,y);Z=c===A?C.range:void 0}const I=findTestDef(y,A),Bt=i==="value"&&I!==void 0&&!xt(I),Jt=I!==void 0&&(I.limitLow!==void 0||I.limitHigh!==void 0);let _="off";i==="value"&&(Bt?_="test":kt==="spec"&&Jt?_="spec":kt==="test"&&e.some(c=>U(c,A,I)!==void 0)&&(_="test"));const Tt=_==="spec"?"spec":Wt,ft=i==="stackedValues"||i==="stackedBins"||i==="stackedSoftBins";let H,E,tt=!1;if(Z){[H,E]=Z;let c=!0,r=!1;for(const M of e){const V=ft?P(M,0):P(M,A);if(V!==void 0&&(r=!0,!Number.isInteger(V))){c=!1;break}}tt=r&&c}else{const c=!ft&&i==="value"&&Tt==="spec"&&I&&(I.limitLow!==void 0||I.limitHigh!==void 0);let r=1/0,M=-1/0,V=!0;for(const v of e){const L=ft?P(v,0):P(v,A);L!==void 0&&(L<r&&(r=L),L>M&&(M=L),Number.isInteger(L)||(V=!1))}isFinite(r)||(V=!1),tt=V,c?(H=I.limitLow!==void 0?I.limitLow:isFinite(r)?r:0,E=I.limitHigh!==void 0?I.limitHigh:isFinite(M)?M:1):(H=isFinite(r)?r:0,E=isFinite(M)?M:1)}H===E&&(E=H+1);const It=Bt?!1:wt??I?.logScale??!1,Vt=It&&H>0&&E>0,Zt=Vt;let et;if(Vt){const c=Math.log10(H),r=Math.log10(E)-c;et=M=>M<=0?0:Ht((Math.log10(M)-c)/r)}else et=c=>Ht((c-H)/(E-H));const F=Me(t,W,x);let Ct=null;if(t.notch){const c=_t(t.notch.type),r=yt(F.physicalToScreen,Math.cos(c)*t.radius,Math.sin(c)*t.radius),M=Math.hypot(r.x,r.y);M>0&&(Ct={x:r.x/M,y:r.y/M})}let q=1/0;for(let c=0;c<e.length;c++){const r=e[c];r.width<q&&(q=r.width),r.height<q&&(q=r.height)}const Ft=1,te=q!==1/0?Math.min(Ft,q*.12):Ft,Dt=[],dt=[],ee=F.bakedToScreen.a===1&&F.bakedToScreen.b===0&&F.bakedToScreen.c===0&&F.bakedToScreen.d===1&&F.bakedToScreen.e===0&&F.bakedToScreen.f===0;let j=null;if(!ee){j=new Float64Array(e.length*2);for(let c=0;c<e.length;c++){const r=G(F.bakedToScreen,e[c].physX,e[c].physY);j[c*2]=r.x,j[c*2+1]=r.y}}const ne=ue(F.gridToScreen),ae=i==="hardBin"||i==="softBin"||i==="stackedBins"||i==="stackedSoftBins",ie=i==="softBin"||i==="stackedSoftBins";let nt=ae?new Map:void 0;const ht=i==="value"&&_==="spec"?{pass:0,failHigh:0,failLow:0}:void 0,mt=i==="value"&&_==="test"?{pass:0,fail:0}:void 0,Pt=i==="metadata";let at=Pt?new Map:void 0,it=null;if(Pt&&S){const c=collectMetadataValues(e,S),r=R?.key===S?R.values:void 0,M=r?[...r,...c.filter(V=>!r.includes(V))]:c;it=new Map(M.map((V,v)=>[V,Kt?.values?.find(L=>L.value===V)?.color??ye(v)]))}for(let c=0;c<e.length;c++){const r=e[c],M=j?j[c*2]:r.physX,V=j?j[c*2+1]:r.physY;if(dt.push({x:M,y:V}),nt&&!r.partial&&!r.edgeExcluded){const v=ie?r.sbin:r.hbin;v!=null&&nt.set(v,(nt.get(v)??0)+1)}if(ht&&!r.partial&&!r.edgeExcluded){const v=classifySpec(P(r,A),I);v&&ht[v]++}if(mt&&!r.partial&&!r.edgeExcluded){const v=U(r,A,I);v!==void 0&&mt[v?"pass":"fail"]++}if(at&&!r.partial&&!r.edgeExcluded){const v=ot(r,S);v!==void 0&&at.set(v,(at.get(v)??0)+1)}r.partial&&!zt||Fe(Dt,r,M,V,i,ne,te,$t,k,et,A,Gt,Qt,I,_,S,it,m)}const Lt=o?generateTextOverlay(e,j,{plotMode:i,colorFns:$t,normalize:et,activeTest:Y,valueRange:[H,E],testDefs:y,fallbackFormat:N,passFailDisplay:_,activeMetadataKey:S,metadataColorMap:it,allIntegerValues:tt}):[],X=Be(t,F.physicalToScreen);p&&X.push(...Te(t,F.physicalToScreen,g)),B&&X.push(...Ie(t,F.physicalToScreen,t.center.x,t.center.y)),l&&X.push(...Ve(s,F.gridToScreen)),d&&X.push(...Ce(e)),D&&X.push(...De(t,F.gridToScreen,Lt));const se=dt.length>0?{minX:t.center.x-t.radius,maxX:t.center.x+t.radius,minY:t.center.y-t.radius,maxY:t.center.y+t.radius}:null;return{rectangles:Dt,hoverPoints:dt,texts:Lt,overlays:X,plotMode:i,binColorScheme:T,valueColorScheme:b,reverseValueScheme:$,binColors:ut,metadata:t.metadata??null,dies:e,valueRange:[H,E],allIntegerValues:tt,testDefs:y,activeTest:Y,logScale:Zt,logScaleRequested:It,aggrMethod:w,lotSize:J,axisFlip:qt,dataAxisFlip:{x:W?.x??!1,y:W?.y??!1},gridToScreen:F.gridToScreen,rotation:((t.orientation+(x?.rotation??0))%360+360)%360,hasReticle:s.length>0,isLotStack:rt,passFailDisplay:_,colorbarRangeMode:Tt,waferCenter:t.center,waferRadius:t.radius,notchDir:Ct,binCounts:nt,metadataCounts:at,metadataColorMap:it,activeMetadataKey:S,metadataFields:vt,specCounts:ht,passFailCounts:mt,dieBounds:se}}export{getDieKey}from"../core/dies.js";
|
|
@@ -393,19 +393,40 @@ export interface WaferMapInputSingle extends WaferMapInputBase {
|
|
|
393
393
|
/** Per-die test results from the prober. */
|
|
394
394
|
results?: DieResult[];
|
|
395
395
|
lotStack?: never;
|
|
396
|
+
layout?: never;
|
|
396
397
|
}
|
|
397
398
|
/** Lot-stack input — collapse results from multiple wafers into a single aggregated map. */
|
|
398
399
|
export interface WaferMapInputLotStack extends WaferMapInputBase {
|
|
399
400
|
/** Lot-level stacking — collapse results from several wafers into a single map. */
|
|
400
401
|
lotStack: LotStackConfig;
|
|
401
402
|
results?: never;
|
|
403
|
+
layout?: never;
|
|
402
404
|
}
|
|
403
405
|
/**
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
406
|
+
* Layout input — a wafer's die sites with no test data: gross die per wafer,
|
|
407
|
+
* reticle and step planning, the expected die map before data exists.
|
|
408
|
+
*
|
|
409
|
+
* Requires `waferConfig.diameter` and `dieConfig.width`/`height`. The map holds
|
|
410
|
+
* every die site lying **fully** on the wafer (notch and flat included), which is
|
|
411
|
+
* the gross die count and the set of sites a prober can step to — so, like a map
|
|
412
|
+
* built from results, it never contains edge-straddling dies. Die `(0, 0)` is the
|
|
413
|
+
* site centred on the wafer, and `x`/`y` count sites in the directions
|
|
414
|
+
* `dieConfig.xAxisDirection`/`yAxisDirection` give. `waferConfig.center` is
|
|
415
|
+
* ignored: the layout defines the centre. `edgeExclusion`, `reticleConfig` and
|
|
416
|
+
* orientation apply as for any map.
|
|
417
|
+
*/
|
|
418
|
+
export interface WaferMapInputLayout extends WaferMapInputBase {
|
|
419
|
+
layout: true;
|
|
420
|
+
results?: never;
|
|
421
|
+
lotStack?: never;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Input accepted by {@link buildWaferMap}: one wafer's results
|
|
425
|
+
* ({@link WaferMapInputSingle}), an aggregated lot stack
|
|
426
|
+
* ({@link WaferMapInputLotStack}), or a die layout with no data
|
|
427
|
+
* ({@link WaferMapInputLayout}). Combining them is a type error.
|
|
407
428
|
*/
|
|
408
|
-
export type WaferMapInput = WaferMapInputSingle | WaferMapInputLotStack;
|
|
429
|
+
export type WaferMapInput = WaferMapInputSingle | WaferMapInputLotStack | WaferMapInputLayout;
|
|
409
430
|
/** Options forwarded to {@link buildView}. */
|
|
410
431
|
export interface WaferMapOptions extends ViewOptions {
|
|
411
432
|
debug?: boolean;
|
|
@@ -494,6 +515,10 @@ export interface WaferWarning {
|
|
|
494
515
|
* lot-level ring figures (Summary panel, report, Insights) use the count named in
|
|
495
516
|
* the message.
|
|
496
517
|
*
|
|
518
|
+
* - `'input-values-not-numbers'` — raised by `buildWaferMap`: bins or test values
|
|
519
|
+
* were given as text (or verdicts as something other than true/false), as a
|
|
520
|
+
* CSV parser produces. They are not converted, so those dies are judged and
|
|
521
|
+
* plotted wrongly — a bin of "1" is not pass bin 1. The message counts them.
|
|
497
522
|
* - `'input-field-removed'` — raised by `buildWaferMap`: the input used a name
|
|
498
523
|
* removed in an earlier release (`data`, `die`, `stack`, `values`,
|
|
499
524
|
* `TestDef.index`, `dieConfig.origin`, `waferConfig.flat`,
|
|
@@ -504,7 +529,7 @@ export interface WaferWarning {
|
|
|
504
529
|
* The union is intentionally open to string so future advisory codes can be
|
|
505
530
|
* added without a breaking change; switch with a `default` branch.
|
|
506
531
|
*/
|
|
507
|
-
code: 'partial-coverage' | 'geometry-conflict' | 'non-standard-diameter' | 'diameter-exceeds-die-extent' | 'test-count-capped' | 'edge-exclusion-exceeds-radius' | 'analysis-option-corrected' | 'bin-colors-shared' | 'pass-bins-mixed' | 'ring-count-mixed' | 'input-field-removed' | (string & {});
|
|
532
|
+
code: 'partial-coverage' | 'geometry-conflict' | 'non-standard-diameter' | 'diameter-exceeds-die-extent' | 'test-count-capped' | 'edge-exclusion-exceeds-radius' | 'analysis-option-corrected' | 'bin-colors-shared' | 'pass-bins-mixed' | 'ring-count-mixed' | 'input-field-removed' | 'input-values-not-numbers' | (string & {});
|
|
508
533
|
/** Human-readable explanation, suitable for direct display. */
|
|
509
534
|
message: string;
|
|
510
535
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createWafer as q}from"../core/wafer.js";import{isYieldEligibleDie as fe,getDieKey as B,hasPosition as v}from"../core/dies.js";import{applyOrientation as H,transformDies as ce}from"../core/transforms.js";import{affineRotation as le,affineMirror as ue,affineCompose as he,affinePoint as me}from"../core/transforms.js";import{inferWaferFromXY as ge}from"../core/inference/wafer.js";import{resolveGridPitch as pe}from"../core/inference/pitch.js";import{assignGridIndices as ye}from"../core/inference/grid.js";import{generateReticleGrid as xe}from"../core/reticle.js";import{buildView as U}from"./buildView.js";import{modeOf as we}from"../core/utils.js";import{aggregateValues as J,aggregateBinCounts as be}from"../core/aggregates.js";export function isParametricTest(t){return t?.testType!=="F"}function De(t){const n=(l,o)=>({ringCount:l,ringCountWarning:{code:"analysis-option-corrected",severity:"warning",message:`ringCount=${String(t)} ${o} (using ${l}).`}});if(t===void 0)return{ringCount:4,ringCountWarning:void 0};if(typeof t!="number"||!Number.isFinite(t))return n(4,"is not a finite number");const e=Math.max(1,Math.round(t));return e===t?{ringCount:t,ringCountWarning:void 0}:n(e,"is not a whole number of at least 1")}function Q(t){const n=(u,i)=>u!==null&&typeof u=="object"&&u[i]!==void 0,e=Array.isArray(t)?{}:t,l=[],o=(u,i,f)=>{u&&l.push(`\`${i}\` (now \`${f}\`)`)};o(n(e,"data"),"data","results"),o(n(e,"die"),"die","dieConfig"),o(n(e,"stack"),"stack","lotStack"),o(n(e.dieConfig,"origin"),"dieConfig.origin","dieConfig.coordinateOrigin"),o(n(e.waferConfig,"flat"),"waferConfig.flat","waferConfig.notch"),o(n(e.reticleConfig,"anchor"),"reticleConfig.anchor","reticleConfig.anchorDie"),o(n(e.lotStack,"aggr"),"lotStack.aggr","lotStack.method"),o(Array.isArray(e.testDefs)&&e.testDefs.some(u=>n(u,"index")&&!n(u,"testNumber")),"TestDef.index","TestDef.testNumber");const r=n(e.lotStack,"results")?e.lotStack.results:[],h=(Array.isArray(t)?[t]:[e.results,...Array.isArray(r)?r:[]]).filter(u=>Array.isArray(u));if(o(h.some(u=>u.slice(0,100).some(i=>n(i,"values"))),"values","testValues"),!l.length)return;const s=l.length>1,m=`buildWaferMap ignored ${l.join(", ")}: ${s?"these names were":"that name was"} removed in an earlier release, so what ${s?"they":"it"} described is not on this map. Rename and rebuild.`;return console.warn(`[wafermap] ${m}`),{code:"input-field-removed",message:m,severity:"error"}}function Me(t){if(!Array.isArray(t)&&"results"in t&&t.results!==void 0&&"lotStack"in t&&t.lotStack!==void 0)throw new Error("buildWaferMap: pass either `results` or `lotStack`, not both.");return Array.isArray(t)?{results:t,waferOpts:void 0,dieOpts:void 0,explicitDies:void 0,reticleOpts:void 0,lotStackOpts:void 0,passBins:[1],ringCount:4,ringCountWarning:void 0,removedFieldWarning:Q(t),standardDiameters:void 0,testDefs:void 0,hbinDefs:void 0,sbinDefs:void 0,metadataFields:void 0,retestPolicy:"last",edgeDieYieldMode:"exclude"}:{results:t.results??[],waferOpts:t.waferConfig,dieOpts:t.dieConfig,explicitDies:t.dies,reticleOpts:t.reticleConfig,lotStackOpts:t.lotStack,passBins:t.passBins??[1],...De(t.ringCount),removedFieldWarning:Q(t),standardDiameters:t.standardDiameters,testDefs:t.testDefs?.length?t.testDefs:void 0,hbinDefs:t.hbinDefs,sbinDefs:t.sbinDefs,metadataFields:t.metadataFields,retestPolicy:t.retestPolicy??"last",edgeDieYieldMode:t.edgeDieYieldMode??"exclude"}}function Ce(t,n){return n?.coordinateOrigin?n.coordinateOrigin:{type:"center"}}function Oe(t,n,e){if(n.type==="custom"&&n.offset)return{offsetX:n.offset.x,offsetY:n.offset.y};if(n.type!=="center"){let l=1/0,o=-1/0,r=1/0,h=-1/0;for(const s of t)s.x<l&&(l=s.x),s.x>o&&(o=s.x),s.y<r&&(r=s.y),s.y>h&&(h=s.y);return{offsetX:Math.round((o+l)/2),offsetY:Math.round((h+r)/2)}}return{offsetX:e.offsetX,offsetY:e.offsetY}}function Se(t,n,e,l,o,r){if(r)return{colMidX:(Math.round(r.x)-n)*l,colMidY:(Math.round(r.y)-e)*o,anchored:!0};if(t.length===0)return{colMidX:0,colMidY:0,anchored:!1};let h=1/0,s=-1/0,m=1/0,u=-1/0;for(const i of t){const f=Math.round(i.x)-n,c=Math.round(i.y)-e;f<h&&(h=f),f>s&&(s=f),c<m&&(m=c),c>u&&(u=c)}return{colMidX:(h+s)/2*l,colMidY:(m+u)/2*o,anchored:!1}}function ve(t,n,e){let l=1/0,o=-1/0,r=1/0,h=-1/0,s=0,m=0;for(const f of t){const c=Math.round(f.x)-n,d=Math.round(f.y)-e;s+=c,m+=d,c<l&&(l=c),c>o&&(o=c),d<r&&(r=d),d>h&&(h=d)}const u=t.length,i=(f,c,d)=>{const g=(d-c)/2;return g<=0?0:Math.abs(f/u-(c+d)/2)/g};return i(s,l,o)>.11||i(m,r,h)>.11}function Re(t,n){let e=t?.xAxisDirection==="left",l=t?.yAxisDirection==="down";return(n.type==="UL"||n.type==="UR")&&(l=!0),(n.type==="LR"||n.type==="UR")&&(e=!0),{flipX:e,flipY:l}}function ke(t,n){const{method:e,targetBin:l}=t,o=t.results.map(r=>r.filter(v));if(e==="mean"||e==="median"||e==="stddev"||e==="min"||e==="max"||e==="count"){const r=new Set((n??[]).filter(i=>!isParametricTest(i)).map(i=>i.testNumber)),h=new Set;for(const i of o)for(const f of i)if(f.testValues)for(const c of Object.keys(f.testValues)){const d=Number(c);r.has(d)||h.add(d)}if(h.size===0)return J(o,e);const s=[...h],m=s.map(i=>J(o,e,i)),u=new Map;for(let i=0;i<s.length;i++){const f=s[i];for(const c of m[i]){const d=B(c);u.has(d)||u.set(d,{template:c,testValues:{}});const g=u.get(d),p=c.testValues?.[0];p!==void 0&&(g.testValues[f]=p)}}return[...u.values()].map(({template:i,testValues:f})=>({...i,testValues:Object.keys(f).length>0?f:void 0}))}if(e==="countBin"||e==="percent"){if(l===void 0)return[];const r=be(o,l,"hard");if(e==="countBin")return r;const h=o.length;return r.map(s=>({...s,testValues:{0:h>0?(s.testValues?.[0]??0)/h*100:0}}))}if(e==="mode"){const r=new Map;for(const s of o)for(const m of s){const u=B(m);r.has(u)||r.set(u,[]),r.get(u).push(m)}const h=[];for(const[s,m]of r){const u=s.split(","),i=Number(u[0]),f=Number(u[1]),c=m.map(g=>g.hbin).filter(g=>g!==void 0),d=we(c);d!==null&&h.push({x:i,y:f,hbin:d})}return h}return[]}function Z(t){const n=t.filter(v),e=n.length,l=n.filter(r=>r.edgeExcluded).length,o=n.filter(r=>dieHasTestData(r)||r.hbin!==void 0||r.sbin!==void 0).length;return{filledDies:o,totalDies:e,edgeExcludedDies:l,unpositionedDies:t.length-n.length,ratio:e>0?o/e:0}}function ee(t,n,e="exclude"){const l=new Set(n),r=t.filter(d=>!d.partial).filter(d=>d.edgeExcluded).length,h=t.filter(d=>d.partial).length;let s=0,m=0,u=!1;for(const d of t){if(!fe(d))continue;const g=d.hbin??d.sbin;g!==void 0&&(u=!0,l.has(g)?s++:m++)}const i=s+m,f=u&&i>0?s/i*100:null;let c=null;if(e==="denominator-only"){const d=i+r;c=u&&d>0?s/d*100:null}return{passDies:s,failDies:m,edgeExcludedDies:r,partialDies:h,totalDies:i,yieldPercent:f,yieldPercentGross:c}}function te(t,n,e,l,o,r=0,h=0,s=0,m=0,u=0,i=!1,f=!1){if(!t)return[];const c=t.anchorDie??{x:0,y:0},d=xe(n,{width:t.width,height:t.height,diePitchX:l,diePitchY:o,anchorDie:{x:c.x-r,y:c.y-h},gridOrigin:{x:-s,y:-m}}),g=he(ue(i,f,n.center.x,n.center.y),le(u,n.center.x,n.center.y));return d.filter(p=>{const R=p.width/2,k=p.height/2,w=[[p.x-R,p.y-k],[p.x+R,p.y-k],[p.x+R,p.y+k],[p.x-R,p.y+k]].map(([y,N])=>me(g,y,N)),M=Math.min(...w.map(y=>y.x)),A=Math.max(...w.map(y=>y.x)),E=Math.min(...w.map(y=>y.y)),$=Math.max(...w.map(y=>y.y));return e.some(y=>y.physX>=M&&y.physX<A&&y.physY>=E&&y.physY<$)})}function Ae(t,n,e){const o=Math.max(0,n.radius-e)**2;return t.map(r=>{const h=r.physX-n.center.x,s=r.physY-n.center.y;return h*h+s*s>o?{...r,edgeExcluded:!0}:r})}function Ee(t,n,e){const l=t.filter(v),o=t.filter(i=>!v(i)),r=new Map;for(const i of l){let f=r.get(i.x);f||(f=new Map,r.set(i.x,f)),f.set(i.y,(f.get(i.y)??0)+1)}const h=new Set(e);function s(i,f){const c=i.hbin,d=f.hbin;if(c===void 0||d===void 0)return!1;const g=h.has(c),p=h.has(d);return n==="best"?p!==g?p:d<c:p!==g?g:d>c}const m=new Map;for(const i of l){const f=B(i),c=m.get(f);n==="first"&&c||(n==="best"||n==="worst")&&c&&!s(c,i)||m.set(f,i)}return[...Array.from(m.values()).map(i=>{const c=r.get(i.x)?.get(i.y)??1;return c>1?{...i,retestCount:c}:i}),...o]}export function getDieTestValue(t,n){return t.testValues?.[n]}export function getTestPassStatus(t,n,e){const l=t.testPass?.[n];if(l!==void 0)return l;if(e!==void 0&&!isParametricTest(e)){const o=t.testValues?.[n];if(o===0||o===1)return o===1}}export function dieHasTestData(t){return t.testValues!==void 0&&Object.keys(t.testValues).length>0||t.testPass!==void 0&&Object.keys(t.testPass).length>0}function Y(t,n){const e={};return n.hbin!==void 0&&(e.hbin=n.hbin),n.sbin!==void 0&&(e.sbin=n.sbin),n.retestCount!==void 0&&(e.retestCount=n.retestCount),n.siteNum!==void 0&&(e.siteNum=n.siteNum),n.partId!==void 0&&(e.partId=n.partId),n.metadata!==void 0&&(e.metadata=n.metadata),n.testPass!==void 0&&(e.testPass=n.testPass),n.testValues!==void 0&&(e.testValues=n.testValues),{...t,...e}}function ne(t,n){return n.plotMode?n.plotMode:t.some(dieHasTestData)?"value":"hardBin"}function ie(t,n){return t.map(({code:e,message:l})=>({code:e,message:l,severity:e==="non-standard-diameter"||e==="diameter-exceeds-die-extent"?"warning":"error",confidence:n.wafer.confidence}))}const Fe="do not fit inside the supplied",We="Inferred wafer diameter",$e="The probed dies reach only",Ie=.75,Pe=20;export const STANDARD_WAFER_DIAMETERS_MM=[100,125,150,200,300];const Te=.02;function se(t,n){return n.reduce((e,l)=>Math.abs(l-t)<Math.abs(e-t)?l:e)}function _e(t,n){if(!n.length)return!0;const e=se(t,n);return Math.abs(e-t)/e<=Te}function Be(t){return t===void 0?STANDARD_WAFER_DIAMETERS_MM:t.filter(n=>Number.isFinite(n)&&n>0)}export function buildWaferMap(t,n){const e=Me(t),{debug:l,...o}=n??{},r=e.lotStackOpts?ke(e.lotStackOpts,e.testDefs):e.results;for(let a=0;a<Math.min(5,r.length);a++){const x=r[a];if(typeof x.x=="string"||typeof x.y=="string")throw new TypeError("buildWaferMap: x and y must be numbers, received strings. Did you forget to cast CSV values? e.g. { x: +row.x, y: +row.y }")}for(const a of r)if(a.x==null!=(a.y==null))throw new TypeError(`buildWaferMap: die has only one of x/y set (x=${String(a.x)}, y=${String(a.y)}). A die must be either fully positioned (both x and y) or fully unpositioned (neither) \u2014 omit both to mark a die as having no reported position.`);const h=Ee(r,e.retestPolicy,e.passBins),s={wafer:{confidence:1,method:"provided"},diePitch:{confidence:1,units:"mm"},grid:{confidence:1}},m=[];if(e.explicitDies){let a=e.explicitDies;const x=h.filter(v),O=h.filter(_=>!v(_));if(x.length>0){const _=new Map(x.map(S=>[B(S),S]));a=a.map(S=>{const G=_.get(B(S));return G?Y(S,G):S})}const T=e.waferOpts?.diameter??300,W=q({diameter:T,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});W.orientation!==0&&(a=H(a,W));const z=te(e.reticleOpts,W,a,1,1,0,0,0,0,W.orientation),ae=o.showReticle??e.reticleOpts!==void 0,L=U(W,a,{...o,reticles:z,showReticle:ae,plotMode:ne(h,o),testDefs:e.testDefs,passBins:e.passBins,ringCount:e.ringCount,isLotStack:!1},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),de=O.map((_,S)=>Y({id:`unpositioned_${S}`,width:a[0]?.width??1,height:a[0]?.height??1},_)),X=[...a,...de];return{wafer:W,dies:X,view:L,reticleConfig:e.reticleOpts,units:"mm",inference:s,warnings:[...e.removedFieldWarning?[e.removedFieldWarning]:[],...ie(m,s),...e.ringCountWarning?[e.ringCountWarning]:[]],plotMode:L.plotMode,metadata:L.metadata,isLotStack:!1,dataCoverage:Z(X),passBins:e.passBins,ringCount:e.ringCount,yield:ee(X,e.passBins,e.edgeDieYieldMode),reticles:z,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields}}const u=h.filter(v),i=h.filter(a=>!v(a)),f=u.map(a=>({x:a.x,y:a.y})),c=pe(f,e.dieOpts,e.waferOpts?.diameter);s.diePitch={confidence:c.confidence,units:c.units};const{pitchX:d,pitchY:g}=c,p=c.units,R=Ce(u,e.dieOpts),k=ye(f);s.grid={confidence:k.confidence};const{offsetX:w,offsetY:M}=Oe(f,R,k),{flipX:A,flipY:E}=Re(e.dieOpts,R),{colMidX:$,colMidY:y,anchored:N}=Se(f,w,M,d,g,e.waferOpts?.center);let b=e.waferOpts?.diameter;const I=f.map(a=>({x:(Math.round(a.x)-w)*d-$,y:(Math.round(a.y)-M)*g-y})),F=I.length>0?Math.max(...I.map(({x:a,y:x})=>Math.hypot(Math.abs(a)+d/2,Math.abs(x)+g/2))):0;if(b===void 0)if(f.length>0)if(p==="mm"){const a=ge(I,{minRadius:F});b=a.diameter,s.wafer={confidence:a.confidence,method:a.method};const x=Be(e.standardDiameters);if(!_e(b,x)){const O=se(b,x);m.push({code:"non-standard-diameter",message:`${We} ${b.toFixed(1)} mm is not a standard wafer size (nearest is ${O} mm). The diameter was derived from the die extent, which assumes the probed grid reaches the wafer edge \u2014 a non-standard result usually means it does not, because the outer dies were never probed. Every die is then placed against a wafer that is too small, which moves dies between rings and changes ring and edge findings. Supply waferConfig.diameter.`})}}else b=F*2,s.wafer={confidence:c.confidence*.8,method:"extent"};else b=p==="mm"?300:30,s.wafer={confidence:0,method:"default"};if(!N&&f.length>0&&ve(f,w,M)&&(s.wafer.method="inferred-partial",m.push({code:"partial-coverage",message:"Wafer geometry was inferred from die positions alone. The data does not span a full symmetric wafer, so the inferred diameter and centre may be wrong and dies may be mis-positioned relative to the true wafer boundary. Supply waferConfig.diameter and waferConfig.center (the prober coordinate of the wafer centre) to position partial data correctly."})),e.waferOpts?.diameter!==void 0&&e.waferOpts.center===void 0&&F>0&&f.length>=Pe){const a=F/(b/2);a<Ie&&m.push({code:"diameter-exceeds-die-extent",message:`${$e} ${(a*100).toFixed(0)}% of the ${b} mm wafer's radius. Either the diameter is larger than the real one, or this is a partial map covering only part of the wafer. Ring bands are equal-radius, so the outer rings are mostly or entirely empty and ring, quadrant and edge findings describe the assumed wafer rather than the probed area. Check waferConfig.diameter, or drop it and let the diameter be inferred from the die extent.`})}if(e.waferOpts?.diameter!==void 0){const a=e.dieOpts?.width!==void 0&&e.dieOpts?.height!==void 0;if(a&&F>b/2+1e-9){const x=I.filter(({x:O,y:T})=>Math.hypot(Math.abs(O)+d/2,Math.abs(T)+g/2)>b/2+1e-9).length;m.push({code:"geometry-conflict",message:`${x} of ${I.length} probed die positions ${Fe} ${b} mm wafer at the supplied die pitch of ${d} \xD7 ${g} mm. A die with test results is a real prober position and is always fully on the wafer, so one of the two supplied values must be wrong: containing these dies at this pitch would need a diameter of at least ${(F*2).toFixed(1)} mm. Check waferConfig.diameter and dieConfig.width/height against the real device.`})}}const C=q({diameter:b,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});let D=u.map(a=>{const x=Math.round(a.x)-w,O=Math.round(a.y)-M,T={id:`${x}_${O}`,x,y:O,physX:x*d-$,physY:O*g-y,width:d,height:g,insideWafer:!0,partial:!1};return Y(T,a)});(w!==0||M!==0)&&(D=D.map(a=>({...a,x:a.x+w,y:a.y+M,id:`${a.x+w}_${a.y+M}`}))),D=H(D,C),(A||E)&&(D=ce(D,{flipX:A,flipY:E},C.center));const K=[];e.waferOpts?.edgeExclusion&&e.waferOpts.edgeExclusion>0&&(D=Ae(D,C,e.waferOpts.edgeExclusion),e.waferOpts.edgeExclusion>C.radius&&K.push({code:"edge-exclusion-exceeds-radius",message:`edgeExclusion (${e.waferOpts.edgeExclusion}mm) exceeds the resolved wafer radius (${C.radius}mm) \u2014 the entire wafer is excluded.`,severity:"warning"}));const j=te(e.reticleOpts,C,D,d,g,w,M,$,y,C.orientation,A,E),re=o.showReticle??e.reticleOpts!==void 0,P=U(C,D,{...o,reticles:j,showReticle:re,plotMode:ne(h,o),testDefs:e.testDefs,passBins:e.passBins,ringCount:e.ringCount,dataAxisFlip:{x:A,y:E},isLotStack:e.lotStackOpts!==void 0,aggregationMethod:e.lotStackOpts?.method,lotSize:e.lotStackOpts?.results.length},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),oe=i.map((a,x)=>Y({id:`unpositioned_${x}`,width:d,height:g},a)),V=[...D,...oe];return{wafer:C,dies:V,view:P,reticleConfig:e.reticleOpts,units:p,inference:s,warnings:[...e.removedFieldWarning?[e.removedFieldWarning]:[],...ie(m,s),...K,...e.ringCountWarning?[e.ringCountWarning]:[]],plotMode:P.plotMode,metadata:P.metadata,isLotStack:e.lotStackOpts!==void 0,dataCoverage:Z(V),passBins:e.passBins,ringCount:e.ringCount,yield:ee(V,e.passBins,e.edgeDieYieldMode),reticles:j,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields,aggrMethod:P.aggrMethod,lotSize:P.lotSize}}
|
|
1
|
+
import{createWafer as j}from"../core/wafer.js";import{isYieldEligibleDie as le,getDieKey as T,hasPosition as A}from"../core/dies.js";import{applyOrientation as H,transformDies as ue,clipDiesToWafer as he}from"../core/transforms.js";import{generateDies as me}from"../core/dies.js";import{affineRotation as ge,affineMirror as pe,affineCompose as ye,affinePoint as be}from"../core/transforms.js";import{inferWaferFromXY as we}from"../core/inference/wafer.js";import{resolveGridPitch as xe}from"../core/inference/pitch.js";import{assignGridIndices as De}from"../core/inference/grid.js";import{generateReticleGrid as Me}from"../core/reticle.js";import{buildView as U}from"./buildView.js";import{modeOf as Ce}from"../core/utils.js";import{aggregateValues as J,aggregateBinCounts as ve}from"../core/aggregates.js";export function isParametricTest(t){return t?.testType!=="F"}function Oe(t){const n=(l,s)=>({ringCount:l,ringCountWarning:{code:"analysis-option-corrected",severity:"warning",message:`ringCount=${String(t)} ${s} (using ${l}).`}});if(t===void 0)return{ringCount:4,ringCountWarning:void 0};if(typeof t!="number"||!Number.isFinite(t))return n(4,"is not a finite number");const e=Math.max(1,Math.round(t));return e===t?{ringCount:t,ringCountWarning:void 0}:n(e,"is not a whole number of at least 1")}function Q(t){const n=Array.isArray(t)?{}:t,e=Array.isArray(n.lotStack?.results)?n.lotStack.results:[],l=(Array.isArray(t)?[t]:[n.results,...e]).filter(a=>Array.isArray(a)),s={hbin:0,sbin:0,testValues:0,testPass:0};let r;const h=(a,i)=>{s[a]++,r??=`${a} ${JSON.stringify(i)}`};for(const a of l){const i=new Set,f=new Set;for(let p=0;p<a.length;p+=p<50?1:100){const m=a[p];if(!(m===null||typeof m!="object")){if(m.testValues&&typeof m.testValues=="object")for(const D of Object.keys(m.testValues))i.add(D);if(m.testPass&&typeof m.testPass=="object")for(const D of Object.keys(m.testPass))f.add(D)}}const u=[...i],d=[...f];for(const p of a){if(p===null||typeof p!="object")continue;const m=p;m.hbin!=null&&typeof m.hbin!="number"&&h("hbin",m.hbin),m.sbin!=null&&typeof m.sbin!="number"&&h("sbin",m.sbin);const D=m.testValues;if(D&&typeof D=="object")for(let y=0;y<u.length;y++){const b=D[u[y]];b!=null&&typeof b!="number"&&h("testValues",b)}const C=m.testPass;if(C&&typeof C=="object")for(let y=0;y<d.length;y++){const b=C[d[y]];b!=null&&typeof b!="boolean"&&h("testPass",b)}}}const o=[];if(s.hbin&&o.push(`${s.hbin} hard bin${s.hbin===1?"":"s"}`),s.sbin&&o.push(`${s.sbin} soft bin${s.sbin===1?"":"s"}`),s.testValues&&o.push(`${s.testValues} test value${s.testValues===1?"":"s"}`),s.testPass&&o.push(`${s.testPass} pass/fail verdict${s.testPass===1?"":"s"}`),!o.length)return;const g=`buildWaferMap received ${o.join(", ")} of the wrong type (for example ${r}). Bins and test values must be numbers and verdicts true/false \u2014 a bin of "1" is not pass bin 1, so those dies count as fails and yield is wrong, and a test value given as text is not plotted or analysed correctly. Convert them with Number() (and verdicts to booleans) and rebuild.`;return console.warn(`[wafermap] ${g}`),{code:"input-values-not-numbers",message:g,severity:"error"}}function Z(t){const n=(a,i)=>a!==null&&typeof a=="object"&&a[i]!==void 0,e=Array.isArray(t)?{}:t,l=[],s=(a,i,f)=>{a&&l.push(`\`${i}\` (now \`${f}\`)`)};s(n(e,"data"),"data","results"),s(n(e,"die"),"die","dieConfig"),s(n(e,"stack"),"stack","lotStack"),s(n(e.dieConfig,"origin"),"dieConfig.origin","dieConfig.coordinateOrigin"),s(n(e.waferConfig,"flat"),"waferConfig.flat","waferConfig.notch"),s(n(e.reticleConfig,"anchor"),"reticleConfig.anchor","reticleConfig.anchorDie"),s(n(e.lotStack,"aggr"),"lotStack.aggr","lotStack.method"),s(Array.isArray(e.testDefs)&&e.testDefs.some(a=>n(a,"index")&&!n(a,"testNumber")),"TestDef.index","TestDef.testNumber");const r=n(e.lotStack,"results")?e.lotStack.results:[],h=(Array.isArray(t)?[t]:[e.results,...Array.isArray(r)?r:[]]).filter(a=>Array.isArray(a));if(s(h.some(a=>a.slice(0,100).some(i=>n(i,"values"))),"values","testValues"),!l.length)return;const o=l.length>1,g=`buildWaferMap ignored ${l.join(", ")}: ${o?"these names were":"that name was"} removed in an earlier release, so what ${o?"they":"it"} described is not on this map. Rename and rebuild.`;return console.warn(`[wafermap] ${g}`),{code:"input-field-removed",message:g,severity:"error"}}function Se(t){if(t.results!==void 0||t.lotStack!==void 0)throw new Error("buildWaferMap: pass `layout`, `results` or `lotStack` \u2014 only one.");const n=t.waferConfig?.diameter,e=t.dieConfig?.width,l=t.dieConfig?.height;if(!(n>0&&e>0&&l>0))throw new Error("buildWaferMap: `layout` needs waferConfig.diameter and dieConfig.width and height, all positive.");const s=j({diameter:n,notch:t.waferConfig?.notch,orientation:0}),r=he(me(s,{width:e,height:l}),s,{width:e,height:l}).filter(i=>!i.partial),{flipX:h,flipY:o}=ee(t.dieConfig,{type:"center"}),{layout:g,...a}=t;return{...a,results:r.map(i=>({x:h?-i.x:i.x,y:o?-i.y:i.y})),waferConfig:{...t.waferConfig,center:{x:0,y:0}},dieConfig:{...t.dieConfig,coordinateOrigin:{type:"center"}}}}function ke(t){if(!Array.isArray(t)&&"results"in t&&t.results!==void 0&&"lotStack"in t&&t.lotStack!==void 0)throw new Error("buildWaferMap: pass either `results` or `lotStack`, not both.");return Array.isArray(t)?{results:t,waferOpts:void 0,dieOpts:void 0,explicitDies:void 0,reticleOpts:void 0,lotStackOpts:void 0,passBins:[1],ringCount:4,ringCountWarning:void 0,removedFieldWarning:Z(t),nonNumericWarning:Q(t),standardDiameters:void 0,testDefs:void 0,hbinDefs:void 0,sbinDefs:void 0,metadataFields:void 0,retestPolicy:"last",edgeDieYieldMode:"exclude"}:{results:t.results??[],waferOpts:t.waferConfig,dieOpts:t.dieConfig,explicitDies:t.dies,reticleOpts:t.reticleConfig,lotStackOpts:t.lotStack,passBins:t.passBins??[1],...Oe(t.ringCount),removedFieldWarning:Z(t),nonNumericWarning:Q(t),standardDiameters:t.standardDiameters,testDefs:t.testDefs?.length?t.testDefs:void 0,hbinDefs:t.hbinDefs,sbinDefs:t.sbinDefs,metadataFields:t.metadataFields,retestPolicy:t.retestPolicy??"last",edgeDieYieldMode:t.edgeDieYieldMode??"exclude"}}function Ae(t,n){return n?.coordinateOrigin?n.coordinateOrigin:{type:"center"}}function Re(t,n,e){if(n.type==="custom"&&n.offset)return{offsetX:n.offset.x,offsetY:n.offset.y};if(n.type!=="center"){let l=1/0,s=-1/0,r=1/0,h=-1/0;for(const o of t)o.x<l&&(l=o.x),o.x>s&&(s=o.x),o.y<r&&(r=o.y),o.y>h&&(h=o.y);return{offsetX:Math.round((s+l)/2),offsetY:Math.round((h+r)/2)}}return{offsetX:e.offsetX,offsetY:e.offsetY}}function $e(t,n,e,l,s,r){if(r)return{colMidX:(Math.round(r.x)-n)*l,colMidY:(Math.round(r.y)-e)*s,anchored:!0};if(t.length===0)return{colMidX:0,colMidY:0,anchored:!1};let h=1/0,o=-1/0,g=1/0,a=-1/0;for(const i of t){const f=Math.round(i.x)-n,u=Math.round(i.y)-e;f<h&&(h=f),f>o&&(o=f),u<g&&(g=u),u>a&&(a=u)}return{colMidX:(h+o)/2*l,colMidY:(g+a)/2*s,anchored:!1}}function We(t,n,e){let l=1/0,s=-1/0,r=1/0,h=-1/0,o=0,g=0;for(const f of t){const u=Math.round(f.x)-n,d=Math.round(f.y)-e;o+=u,g+=d,u<l&&(l=u),u>s&&(s=u),d<r&&(r=d),d>h&&(h=d)}const a=t.length,i=(f,u,d)=>{const p=(d-u)/2;return p<=0?0:Math.abs(f/a-(u+d)/2)/p};return i(o,l,s)>.11||i(g,r,h)>.11}function ee(t,n){let e=t?.xAxisDirection==="left",l=t?.yAxisDirection==="down";return(n.type==="UL"||n.type==="UR")&&(l=!0),(n.type==="LR"||n.type==="UR")&&(e=!0),{flipX:e,flipY:l}}function Ee(t,n){const{method:e,targetBin:l}=t,s=t.results.map(r=>r.filter(A));if(e==="mean"||e==="median"||e==="stddev"||e==="min"||e==="max"||e==="count"){const r=new Set((n??[]).filter(i=>!isParametricTest(i)).map(i=>i.testNumber)),h=new Set;for(const i of s)for(const f of i)if(f.testValues)for(const u of Object.keys(f.testValues)){const d=Number(u);r.has(d)||h.add(d)}if(h.size===0)return J(s,e);const o=[...h],g=o.map(i=>J(s,e,i)),a=new Map;for(let i=0;i<o.length;i++){const f=o[i];for(const u of g[i]){const d=T(u);a.has(d)||a.set(d,{template:u,testValues:{}});const p=a.get(d),m=u.testValues?.[0];m!==void 0&&(p.testValues[f]=m)}}return[...a.values()].map(({template:i,testValues:f})=>({...i,testValues:Object.keys(f).length>0?f:void 0}))}if(e==="countBin"||e==="percent"){if(l===void 0)return[];const r=ve(s,l,"hard");if(e==="countBin")return r;const h=s.length;return r.map(o=>({...o,testValues:{0:h>0?(o.testValues?.[0]??0)/h*100:0}}))}if(e==="mode"){const r=new Map;for(const o of s)for(const g of o){const a=T(g);r.has(a)||r.set(a,[]),r.get(a).push(g)}const h=[];for(const[o,g]of r){const a=o.split(","),i=Number(a[0]),f=Number(a[1]),u=g.map(p=>p.hbin).filter(p=>p!==void 0),d=Ce(u);d!==null&&h.push({x:i,y:f,hbin:d})}return h}return[]}function te(t){const n=t.filter(A),e=n.length,l=n.filter(r=>r.edgeExcluded).length,s=n.filter(r=>dieHasTestData(r)||r.hbin!==void 0||r.sbin!==void 0).length;return{filledDies:s,totalDies:e,edgeExcludedDies:l,unpositionedDies:t.length-n.length,ratio:e>0?s/e:0}}function ne(t,n,e="exclude"){const l=new Set(n),r=t.filter(d=>!d.partial).filter(d=>d.edgeExcluded).length,h=t.filter(d=>d.partial).length;let o=0,g=0,a=!1;for(const d of t){if(!le(d))continue;const p=d.hbin??d.sbin;p!==void 0&&(a=!0,l.has(p)?o++:g++)}const i=o+g,f=a&&i>0?o/i*100:null;let u=null;if(e==="denominator-only"){const d=i+r;u=a&&d>0?o/d*100:null}return{passDies:o,failDies:g,edgeExcludedDies:r,partialDies:h,totalDies:i,yieldPercent:f,yieldPercentGross:u}}function se(t,n,e,l,s,r=0,h=0,o=0,g=0,a=0,i=!1,f=!1){if(!t)return[];const u=t.anchorDie??{x:0,y:0},d=Me(n,{width:t.width,height:t.height,diePitchX:l,diePitchY:s,anchorDie:{x:u.x-r,y:u.y-h},gridOrigin:{x:-o,y:-g}}),p=ye(pe(i,f,n.center.x,n.center.y),ge(a,n.center.x,n.center.y));return d.filter(m=>{const D=m.width/2,C=m.height/2,y=[[m.x-D,m.y-C],[m.x+D,m.y-C],[m.x+D,m.y+C],[m.x-D,m.y+C]].map(([w,B])=>be(p,w,B)),b=Math.min(...y.map(w=>w.x)),R=Math.max(...y.map(w=>w.x)),$=Math.min(...y.map(w=>w.y)),F=Math.max(...y.map(w=>w.y));return e.some(w=>w.physX>=b&&w.physX<R&&w.physY>=$&&w.physY<F)})}function Fe(t,n,e){const s=Math.max(0,n.radius-e)**2;return t.map(r=>{const h=r.physX-n.center.x,o=r.physY-n.center.y;return h*h+o*o>s?{...r,edgeExcluded:!0}:r})}function Pe(t,n,e){const l=t.filter(A),s=t.filter(i=>!A(i)),r=new Map;for(const i of l){let f=r.get(i.x);f||(f=new Map,r.set(i.x,f)),f.set(i.y,(f.get(i.y)??0)+1)}const h=new Set(e);function o(i,f){const u=i.hbin,d=f.hbin;if(u===void 0||d===void 0)return!1;const p=h.has(u),m=h.has(d);return n==="best"?m!==p?m:d<u:m!==p?p:d>u}const g=new Map;for(const i of l){const f=T(i),u=g.get(f);n==="first"&&u||(n==="best"||n==="worst")&&u&&!o(u,i)||g.set(f,i)}return[...Array.from(g.values()).map(i=>{const u=r.get(i.x)?.get(i.y)??1;return u>1?{...i,retestCount:u}:i}),...s]}export function getDieTestValue(t,n){return t.testValues?.[n]}export function getTestPassStatus(t,n,e){const l=t.testPass?.[n];if(l!==void 0)return l;if(e!==void 0&&!isParametricTest(e)){const s=t.testValues?.[n];if(s===0||s===1)return s===1}}export function dieHasTestData(t){return t.testValues!==void 0&&Object.keys(t.testValues).length>0||t.testPass!==void 0&&Object.keys(t.testPass).length>0}function _(t,n){const e={};return n.hbin!==void 0&&(e.hbin=n.hbin),n.sbin!==void 0&&(e.sbin=n.sbin),n.retestCount!==void 0&&(e.retestCount=n.retestCount),n.siteNum!==void 0&&(e.siteNum=n.siteNum),n.partId!==void 0&&(e.partId=n.partId),n.metadata!==void 0&&(e.metadata=n.metadata),n.testPass!==void 0&&(e.testPass=n.testPass),n.testValues!==void 0&&(e.testValues=n.testValues),{...t,...e}}function ie(t,n){return n.plotMode?n.plotMode:t.some(dieHasTestData)?"value":"hardBin"}function oe(t,n){return t.map(({code:e,message:l})=>({code:e,message:l,severity:e==="non-standard-diameter"||e==="diameter-exceeds-die-extent"?"warning":"error",confidence:n.wafer.confidence}))}const Ie="do not fit inside the supplied",Ne="Inferred wafer diameter",Ve="The probed dies reach only",Te=.75,_e=20;export const STANDARD_WAFER_DIAMETERS_MM=[100,125,150,200,300];const Be=.02;function re(t,n){return n.reduce((e,l)=>Math.abs(l-t)<Math.abs(e-t)?l:e)}function Ye(t,n){if(!n.length)return!0;const e=re(t,n);return Math.abs(e-t)/e<=Be}function Le(t){return t===void 0?STANDARD_WAFER_DIAMETERS_MM:t.filter(n=>Number.isFinite(n)&&n>0)}export function buildWaferMap(t,n){const e=ke(!Array.isArray(t)&&t.layout?Se(t):t),{debug:l,...s}=n??{},r=e.lotStackOpts?Ee(e.lotStackOpts,e.testDefs):e.results;for(let c=0;c<Math.min(5,r.length);c++){const x=r[c];if(typeof x.x=="string"||typeof x.y=="string")throw new TypeError("buildWaferMap: x and y must be numbers, received strings. Did you forget to cast CSV values? e.g. { x: +row.x, y: +row.y }")}for(const c of r)if(c.x==null!=(c.y==null))throw new TypeError(`buildWaferMap: die has only one of x/y set (x=${String(c.x)}, y=${String(c.y)}). A die must be either fully positioned (both x and y) or fully unpositioned (neither) \u2014 omit both to mark a die as having no reported position.`);const h=Pe(r,e.retestPolicy,e.passBins),o={wafer:{confidence:1,method:"provided"},diePitch:{confidence:1,units:"mm"},grid:{confidence:1}},g=[];if(e.explicitDies){let c=e.explicitDies;const x=h.filter(A),S=h.filter(V=>!A(V));if(x.length>0){const V=new Map(x.map(k=>[T(k),k]));c=c.map(k=>{const q=V.get(T(k));return q?_(k,q):k})}const N=e.waferOpts?.diameter??300,E=j({diameter:N,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});E.orientation!==0&&(c=H(c,E));const G=se(e.reticleOpts,E,c,1,1,0,0,0,0,E.orientation),fe=s.showReticle??e.reticleOpts!==void 0,L=U(E,c,{...s,reticles:G,showReticle:fe,plotMode:ie(h,s),testDefs:e.testDefs,passBins:e.passBins,ringCount:e.ringCount,isLotStack:!1},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),ce=S.map((V,k)=>_({id:`unpositioned_${k}`,width:c[0]?.width??1,height:c[0]?.height??1},V)),X=[...c,...ce];return{wafer:E,dies:X,view:L,reticleConfig:e.reticleOpts,units:"mm",inference:o,warnings:[...e.removedFieldWarning?[e.removedFieldWarning]:[],...e.nonNumericWarning?[e.nonNumericWarning]:[],...oe(g,o),...e.ringCountWarning?[e.ringCountWarning]:[]],plotMode:L.plotMode,metadata:L.metadata,isLotStack:!1,dataCoverage:te(X),passBins:e.passBins,ringCount:e.ringCount,yield:ne(X,e.passBins,e.edgeDieYieldMode),reticles:G,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields}}const a=h.filter(A),i=h.filter(c=>!A(c)),f=a.map(c=>({x:c.x,y:c.y})),u=xe(f,e.dieOpts,e.waferOpts?.diameter);o.diePitch={confidence:u.confidence,units:u.units};const{pitchX:d,pitchY:p}=u,m=u.units,D=Ae(a,e.dieOpts),C=De(f);o.grid={confidence:C.confidence};const{offsetX:y,offsetY:b}=Re(f,D,C),{flipX:R,flipY:$}=ee(e.dieOpts,D),{colMidX:F,colMidY:w,anchored:B}=$e(f,y,b,d,p,e.waferOpts?.center);let M=e.waferOpts?.diameter;const P=f.map(c=>({x:(Math.round(c.x)-y)*d-F,y:(Math.round(c.y)-b)*p-w})),W=P.length>0?Math.max(...P.map(({x:c,y:x})=>Math.hypot(Math.abs(c)+d/2,Math.abs(x)+p/2))):0;if(M===void 0)if(f.length>0)if(m==="mm"){const c=we(P,{minRadius:W});M=c.diameter,o.wafer={confidence:c.confidence,method:c.method};const x=Le(e.standardDiameters);if(!Ye(M,x)){const S=re(M,x);g.push({code:"non-standard-diameter",message:`${Ne} ${M.toFixed(1)} mm is not a standard wafer size (nearest is ${S} mm). The diameter was derived from the die extent, which assumes the probed grid reaches the wafer edge \u2014 a non-standard result usually means it does not, because the outer dies were never probed. Every die is then placed against a wafer that is too small, which moves dies between rings and changes ring and edge findings. Supply waferConfig.diameter.`})}}else M=W*2,o.wafer={confidence:u.confidence*.8,method:"extent"};else M=m==="mm"?300:30,o.wafer={confidence:0,method:"default"};if(!B&&f.length>0&&We(f,y,b)&&(o.wafer.method="inferred-partial",g.push({code:"partial-coverage",message:"Wafer geometry was inferred from die positions alone. The data does not span a full symmetric wafer, so the inferred diameter and centre may be wrong and dies may be mis-positioned relative to the true wafer boundary. Supply waferConfig.diameter and waferConfig.center (the prober coordinate of the wafer centre) to position partial data correctly."})),e.waferOpts?.diameter!==void 0&&e.waferOpts.center===void 0&&W>0&&f.length>=_e){const c=W/(M/2);c<Te&&g.push({code:"diameter-exceeds-die-extent",message:`${Ve} ${(c*100).toFixed(0)}% of the ${M} mm wafer's radius. Either the diameter is larger than the real one, or this is a partial map covering only part of the wafer. Ring bands are equal-radius, so the outer rings are mostly or entirely empty and ring, quadrant and edge findings describe the assumed wafer rather than the probed area. Check waferConfig.diameter, or drop it and let the diameter be inferred from the die extent.`})}if(e.waferOpts?.diameter!==void 0){const c=e.dieOpts?.width!==void 0&&e.dieOpts?.height!==void 0;if(c&&W>M/2+1e-9){const x=P.filter(({x:S,y:N})=>Math.hypot(Math.abs(S)+d/2,Math.abs(N)+p/2)>M/2+1e-9).length;g.push({code:"geometry-conflict",message:`${x} of ${P.length} probed die positions ${Ie} ${M} mm wafer at the supplied die pitch of ${d} \xD7 ${p} mm. A die with test results is a real prober position and is always fully on the wafer, so one of the two supplied values must be wrong: containing these dies at this pitch would need a diameter of at least ${(W*2).toFixed(1)} mm. Check waferConfig.diameter and dieConfig.width/height against the real device.`})}}const O=j({diameter:M,notch:e.waferOpts?.notch,orientation:e.waferOpts?.orientation??0,metadata:e.waferOpts?.metadata});let v=a.map(c=>{const x=Math.round(c.x)-y,S=Math.round(c.y)-b,N={id:`${x}_${S}`,x,y:S,physX:x*d-F,physY:S*p-w,width:d,height:p,insideWafer:!0,partial:!1};return _(N,c)});(y!==0||b!==0)&&(v=v.map(c=>({...c,x:c.x+y,y:c.y+b,id:`${c.x+y}_${c.y+b}`}))),v=H(v,O),(R||$)&&(v=ue(v,{flipX:R,flipY:$},O.center));const K=[];e.waferOpts?.edgeExclusion&&e.waferOpts.edgeExclusion>0&&(v=Fe(v,O,e.waferOpts.edgeExclusion),e.waferOpts.edgeExclusion>O.radius&&K.push({code:"edge-exclusion-exceeds-radius",message:`edgeExclusion (${e.waferOpts.edgeExclusion}mm) exceeds the resolved wafer radius (${O.radius}mm) \u2014 the entire wafer is excluded.`,severity:"warning"}));const z=se(e.reticleOpts,O,v,d,p,y,b,F,w,O.orientation,R,$),ae=s.showReticle??e.reticleOpts!==void 0,I=U(O,v,{...s,reticles:z,showReticle:ae,plotMode:ie(h,s),testDefs:e.testDefs,passBins:e.passBins,ringCount:e.ringCount,dataAxisFlip:{x:R,y:$},isLotStack:e.lotStackOpts!==void 0,aggregationMethod:e.lotStackOpts?.method,lotSize:e.lotStackOpts?.results.length},{hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,metadataFields:e.metadataFields}),de=i.map((c,x)=>_({id:`unpositioned_${x}`,width:d,height:p},c)),Y=[...v,...de];return{wafer:O,dies:Y,view:I,reticleConfig:e.reticleOpts,units:m,inference:o,warnings:[...e.removedFieldWarning?[e.removedFieldWarning]:[],...e.nonNumericWarning?[e.nonNumericWarning]:[],...oe(g,o),...K,...e.ringCountWarning?[e.ringCountWarning]:[]],plotMode:I.plotMode,metadata:I.metadata,isLotStack:e.lotStackOpts!==void 0,dataCoverage:te(Y),passBins:e.passBins,ringCount:e.ringCount,yield:ne(Y,e.passBins,e.edgeDieYieldMode),reticles:z,hbinDefs:e.hbinDefs,sbinDefs:e.sbinDefs,testDefs:e.testDefs,metadataFields:e.metadataFields,aggrMethod:I.aggrMethod,lotSize:I.lotSize}}
|
|
@@ -10,6 +10,13 @@ export declare const DEPRECATED_REMOVAL_VERSION = "0.31.0";
|
|
|
10
10
|
* it to the announced list, so a deprecation cannot be added or dropped silently.
|
|
11
11
|
*/
|
|
12
12
|
export declare const DEPRECATED_EXPORTS: Map<string, "function" | "value">;
|
|
13
|
+
/**
|
|
14
|
+
* @internal Log `message` once per `key` for the life of the page — the one
|
|
15
|
+
* copy of the once-only rule, for export deprecations (`deprecated`) and for
|
|
16
|
+
* notices about an option whose behaviour is changing, which is not an export
|
|
17
|
+
* and so is not registered in DEPRECATED_EXPORTS or held to its removal.
|
|
18
|
+
*/
|
|
19
|
+
export declare function noticeOnce(key: string, message: string): void;
|
|
13
20
|
/**
|
|
14
21
|
* @internal Wrap `fn` so its first call logs one notice naming `name` and `advice`.
|
|
15
22
|
* The wrapper has exactly `fn`'s type — generics and overloads included — so a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DEPRECATED_REMOVAL_VERSION="0.31.0",DEPRECATED_EXPORTS=new Map;const n=new Set;export function deprecated(t,e,r){DEPRECATED_EXPORTS.set(e,"function");const o=t;return((...c)=>(
|
|
1
|
+
export const DEPRECATED_REMOVAL_VERSION="0.31.0",DEPRECATED_EXPORTS=new Map;const n=new Set;export function noticeOnce(t,e){n.has(t)||(n.add(t),console.warn(`[wafermap] ${e}`))}export function deprecated(t,e,r){DEPRECATED_EXPORTS.set(e,"function");const o=t;return((...c)=>(noticeOnce(e,`${e} is deprecated and will be removed in ${DEPRECATED_REMOVAL_VERSION}. ${r}`),o(...c)))}export function deprecatedValue(t,e){return DEPRECATED_EXPORTS.set(e,"value"),t}
|
|
@@ -28,9 +28,9 @@ export declare const buildView: typeof buildViewImpl;
|
|
|
28
28
|
export declare const buildHoverText: typeof buildHoverTextImpl;
|
|
29
29
|
/** @deprecated Removed in 0.31.0. It belongs to the low-level drawing pipeline, which is being withdrawn: build with `buildWaferMap` and draw with `renderWaferMap` or `renderWaferGallery`. */
|
|
30
30
|
export declare const buildMapTitle: typeof buildMapTitleImpl;
|
|
31
|
-
/** @deprecated Removed in 0.31.0.
|
|
31
|
+
/** @deprecated Removed in 0.31.0. Read the colours a map draws with `binColorsForMaps(results)`, which takes each map's own pass bins, or with `getBinColors()` on a WaferMapController or GalleryController. */
|
|
32
32
|
export declare const resolveBinColors: typeof resolveBinColorsImpl;
|
|
33
|
-
/** @deprecated Removed in 0.31.0.
|
|
33
|
+
/** @deprecated Removed in 0.31.0. A raw palette lookup bypasses the bin colour rule. Read the colours a map draws with `binColorsForMaps(results)`, or `getBinColors()` on a map or gallery controller. */
|
|
34
34
|
export declare const getBinColorScheme: typeof getBinColorSchemeImpl;
|
|
35
35
|
/** @deprecated Removed in 0.31.0. It is an internal helper that was exported by accident; `buildWaferMap`, `analyzeWaferMap` and the renderers already apply it. */
|
|
36
36
|
export declare const contrastTextColor: typeof contrastTextColorImpl;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{valueToViridis as a,valueToGreyscale as
|
|
1
|
+
import{valueToViridis as a,valueToGreyscale as l}from"./colorMap.js";import{getValueColorScheme as s}from"./colorSchemes.js";import{deprecated as e}from"./deprecate.js";export const valueToViridis=e(a,"valueToViridis","Use resolveValueColorFn('default'), which returns the same colours."),valueToGreyscale=e(l,"valueToGreyscale","Use resolveValueColorFn('greyscale'), which returns the same colours."),getValueColorScheme=e(s,"getValueColorScheme","Colour by value with resolveValueColorFn(name, reversed) \u2014 forValue ignores reverseValueScheme \u2014 and list names and labels with listValueColorSchemes().");import{deprecatedValue as i}from"./deprecate.js";import{buildView as n,buildHoverText as p,buildMapTitle as u}from"./buildView.js";import{resolveBinColors as c}from"./binColors.js";import{getBinColorScheme as m}from"./colorSchemes.js";import{contrastTextColor as d}from"./colorMap.js";import{dieHasTestData as T,isParametricTest as h,getDieTestValue as C,STANDARD_WAFER_DIAMETERS_MM as I}from"./buildWaferMap.js";const o="If you depend on it, say so at https://github.com/wafertools/wafermap/issues.",r="It belongs to the low-level drawing pipeline, which is being withdrawn: build with buildWaferMap and draw with renderWaferMap or renderWaferGallery.",t="It is an internal helper that was exported by accident; buildWaferMap, analyzeWaferMap and the renderers already apply it.",V="Read die.testValues[testNumber].";export const buildView=e(n,"buildView",`${r} ${o}`),buildHoverText=e(p,"buildHoverText",`${r} ${o}`),buildMapTitle=e(u,"buildMapTitle",`${r} ${o}`),resolveBinColors=e(c,"resolveBinColors",`Read the colours a map draws with binColorsForMaps(results), which takes each map's own pass bins, or with getBinColors() on a WaferMapController or GalleryController. ${o}`),getBinColorScheme=e(m,"getBinColorScheme",`A raw palette lookup bypasses the bin colour rule. Read the colours a map draws with binColorsForMaps(results), or getBinColors() on a map or gallery controller. ${o}`),contrastTextColor=e(d,"contrastTextColor",`${t} ${o}`),dieHasTestData=e(T,"dieHasTestData",`${t} ${o}`),isParametricTest=e(h,"isParametricTest",`${t} ${o}`),getDieTestValue=e(C,"getDieTestValue",`${V} ${o}`),STANDARD_WAFER_DIAMETERS_MM=i(I,"STANDARD_WAFER_DIAMETERS_MM");
|
|
@@ -3,6 +3,7 @@ export type { View, MapTitleParts, PlotMode, ViewRect, ViewText, ViewHoverPoint,
|
|
|
3
3
|
export * from './buildWaferMap.js';
|
|
4
4
|
export type { WaferMetadata, DieMetadata } from '../core/metadata.js';
|
|
5
5
|
export * from './colorSchemes.js';
|
|
6
|
-
export type { BinColors, BinColorOptions } from './binColors.js';
|
|
6
|
+
export type { BinColors, BinColorOptions, BinColorSource, MapBinColorOptions } from './binColors.js';
|
|
7
|
+
export { binColorsForMaps } from './binColors.js';
|
|
7
8
|
export { valueToViridis, valueToGreyscale, getValueColorScheme, buildView, buildHoverText, buildMapTitle, resolveBinColors, getBinColorScheme, contrastTextColor, dieHasTestData, isParametricTest, getDieTestValue, STANDARD_WAFER_DIAMETERS_MM, } from './deprecated.js';
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{getDieKey}from"./buildView.js";export*from"./buildWaferMap.js";export*from"./colorSchemes.js";export{valueToViridis,valueToGreyscale,getValueColorScheme,buildView,buildHoverText,buildMapTitle,resolveBinColors,getBinColorScheme,contrastTextColor,dieHasTestData,isParametricTest,getDieTestValue,STANDARD_WAFER_DIAMETERS_MM}from"./deprecated.js";
|
|
1
|
+
export{getDieKey}from"./buildView.js";export*from"./buildWaferMap.js";export*from"./colorSchemes.js";export{binColorsForMaps}from"./binColors.js";export{valueToViridis,valueToGreyscale,getValueColorScheme,buildView,buildHoverText,buildMapTitle,resolveBinColors,getBinColorScheme,contrastTextColor,dieHasTestData,isParametricTest,getDieTestValue,STANDARD_WAFER_DIAMETERS_MM}from"./deprecated.js";
|