Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added text on dies and color scaling #1935

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f9a18d3
changes
Razvan1928 Mar 7, 2024
ce89ae5
offscreencanvas working
Razvan1928 Mar 7, 2024
56f5458
changes
Razvan1928 Mar 8, 2024
8924f49
Merge branch 'main' of https://github.com/ni/nimble into users/rnazar…
Razvan1928 Mar 8, 2024
99c63a4
Update package-lock.json
Razvan1928 Mar 8, 2024
b2bb758
changes
Razvan1928 Mar 8, 2024
9a90542
kinda working
Razvan1928 Mar 12, 2024
bf233ac
kinda working
Razvan1928 Mar 12, 2024
492d05e
data
Razvan1928 Mar 13, 2024
43f5810
Update index.ts
Razvan1928 Mar 13, 2024
35f2649
Merge branch 'main' of https://github.com/ni/nimble into users/rnazar…
Razvan1928 Mar 13, 2024
a6c0d34
Change files
Razvan1928 Mar 13, 2024
ac418f3
Update @ni-nimble-components-f866e449-14a0-410d-be26-c5570bf1006d.json
Razvan1928 Mar 13, 2024
e08e849
added tests, removed setters, made everything public
Razvan1928 Mar 13, 2024
e085d2f
Update matrix-renderer.ts
Razvan1928 Mar 13, 2024
a6340e1
repaired tests
Razvan1928 Mar 13, 2024
90e0444
Update index.ts
Razvan1928 Mar 13, 2024
926c724
repaired lint
Razvan1928 Mar 13, 2024
7cdfe56
changed data types
Razvan1928 Mar 14, 2024
913c269
deleted text on dies
Razvan1928 Mar 14, 2024
a26740d
changed Float32 to Float64
Razvan1928 Mar 14, 2024
724afb9
added some experimental data in storybook
Razvan1928 Mar 14, 2024
b6bfba6
deleted experimental data
Razvan1928 Mar 14, 2024
6f5b3f3
put back not needed to be deleted line
Razvan1928 Mar 14, 2024
ff364f6
added text on dies and color scaling
Razvan1928 Mar 14, 2024
8be8337
Update rendering.ts
Razvan1928 Mar 18, 2024
e15d9df
Update matrix-renderer.ts
Razvan1928 Mar 18, 2024
39edea7
Update matrix-renderer.ts
Razvan1928 Mar 18, 2024
4c3ad90
Update matrix-renderer.ts
Razvan1928 Mar 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Updated wafer map web worker class to support offscreen rendering",
"packageName": "@ni/nimble-components",
"email": "110180309+Razvan1928@users.noreply.github.com",
"dependentChangeType": "patch"
}
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expose } from 'comlink';
import type { Dimensions, Transform, WaferMapMatrix, WaferMapTypedMatrix } from './types';

/**
* MatrixRenderer class is meant to be used within a Web Worker context,
Expand All @@ -8,16 +9,168 @@ import { expose } from 'comlink';
* This setup is used in the wafer-map component to perform heavy computational duties
*/
export class MatrixRenderer {
public dieMatrix: Uint8Array = Uint8Array.from([]);
public colIndexes: Uint32Array = Uint32Array.from([]);
public rowIndexes: Uint32Array = Uint32Array.from([]);
public canvas!: OffscreenCanvas;
public context!: OffscreenCanvasRenderingContext2D;
public values = new Float64Array([14.24, 76.43, 44.63, 67.93, 72.71, 79.04, 26.49, 137.79, 59.82, 52.92,
98.53, 20.83, 462.81]);
public scaledColIndex = new Float64Array([0, 100, 100, 100, 200, 200, 200, 200, 200, 300, 300, 300, 400]);
public scaledRowIndex = new Float64Array([200, 200, 100, 300, 200, 100, 0, 300, 400, 200, 100, 300, 200]);
public dieDimensions: Dimensions = { width: 100, height: 100 };
public transform: Transform = { k: 1, x: 0, y: 0 };
public topLeftCanvasCorner: { x: number, y: number } = { x: 0, y: 0 };
public bottomRightCanvasCorner: { x: number, y: number } = { x: 500, y: 500 };
public colors: string[] = ['red', 'yellow', 'green', 'blue', 'purple'];
public colorsValues = new Float64Array([50, 0, 25, 75, 100]);
public maxCharactersOnDies: number = 6;
public isDieLabelHidden: boolean = true;

private readonly outsideRangeDieColor = 'rgba(218,223,236,1)';
private readonly fontSizeFactor = 0.8;
private fontSize = 12;

public setTransform(transform: Transform): void {
this.transform = transform;
}

public setCanvas(canvas: OffscreenCanvas): void {
this.canvas = canvas;
this.context = canvas.getContext('2d')!;
}

public getMatrix(): WaferMapTypedMatrix {
return {
colIndexes: this.colIndexes,
rowIndexes: this.rowIndexes,
values: this.values
};
}

public emptyMatrix(): void {
this.dieMatrix = Uint8Array.from([]);;
this.colIndexes = Uint32Array.from([]);
this.rowIndexes = Uint32Array.from([]);
this.values = Float64Array.from([]);
}

public scaleCanvas(): void {
this.context.translate(
this.transform.x,
this.transform.y
);
this.context.scale(
this.transform.k,
this.transform.k
);
}

public updateMatrix(
data: Iterable<number>
data: WaferMapMatrix
): void {
this.dieMatrix = Uint8Array.from(data);
this.colIndexes = Uint32Array.from(data.colIndexes);
this.rowIndexes = Uint32Array.from(data.rowIndexes);
this.values = Float64Array.from(data.values);
}

public setCanvasDimensions(data: Dimensions): void {
this.canvas.width = data.width;
this.canvas.height = data.height;
}

public clearCanvas(): void {
this.context.clearRect(
0,
0,
this.canvas.width,
this.canvas.height
);
}

public drawWafer(): void {
this.context.restore();
this.context.save();
this.clearCanvas();
this.scaleCanvas();
this.calculateLabelsFontSize(6);
this.colorsValues.sort((a, b) => a - b);
this.parseDies();
}

public parseDies(): void {
for (let i = 0; i < this.scaledColIndex.length; i++) {
const currentDieValue = this.values[i]!;
if (this.isValueInRange(currentDieValue)) {
const nearestValueIndex = this.findNearestValueIndex(currentDieValue);
this.context.fillStyle = this.colors[nearestValueIndex]!;
}
else { this.context.fillStyle = this.outsideRangeDieColor; }
const x = this.scaledColIndex[i]!;
const y = this.scaledRowIndex[i]!;
if (!this.isDieVisible(x, y)) { continue; }
this.context.fillRect(x, y, this.dieDimensions.width, this.dieDimensions.height);
}
if (this.isDieLabelHidden === true) { return; }
this.addTextOnDies();
}

public isValueInRange(value: number): boolean {
return value >= this.colorsValues[0]! && value <= this.colorsValues[this.colorsValues.length - 1]!;
}

public findNearestValueIndex(dieValue: number): number {
let start = 0;
let end = this.colorsValues.length - 1;

while (start <= end) {
let mid = Math.floor((start + end) / 2);
if (this.colorsValues[mid] === dieValue) return mid;
if (this.colorsValues[mid]! < dieValue) start = mid + 1;
else end = mid - 1;
}

return (this.colorsValues[start]! - dieValue) < (dieValue - this.colorsValues[start - 1]!) ? start : start - 1;
}

private calculateLabelsFontSize(
maxCharacters: number
): void {
this.fontSize = Math.min(
this.dieDimensions.height,
(this.dieDimensions.width / (Math.max(2, maxCharacters) * 0.5))
* this.fontSizeFactor
);
}

private dieHasData(dieData: string): boolean {
return dieData !== null && dieData !== undefined && dieData !== '';
}

public addTextOnDies() {
for (let i = 0; i < this.scaledColIndex.length; i++) {
this.context.font = `${this.fontSize}px sans-serif`;
this.context.fillStyle = 'White';

const textX = this.scaledColIndex[i]! + this.dieDimensions.width / 2;
const textY = this.scaledRowIndex[i]! + this.dieDimensions.height / 2;

let formattedValue = this.formatValue(this.values[i]);

this.context.textAlign = 'center';
this.context.textBaseline = 'middle';
this.context.fillText(formattedValue, textX, textY);
}
}

public formatValue(value: number | undefined): string {
if (value === undefined) return '';
return parseFloat(value.toFixed(1)) + '...';
}

public isDieVisible(x: number, y: number): boolean {
return x >= this.topLeftCanvasCorner.x &&
x <= this.bottomRightCanvasCorner.x &&
y >= this.topLeftCanvasCorner.y &&
y <= this.bottomRightCanvasCorner.y;
}
}
expose(MatrixRenderer);
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,55 @@ describe('MatrixRenderer with MessageChannel', () => {
});

it('updateMatrix should update the dieMatrix', async () => {
const testData = [4, 5, 6];
const testData = { colIndexes: [4, 1, 2], rowIndexes: [54, 54, 62], values: [8.12, 9.0, 0.32] };
await matrixRenderer.updateMatrix(testData);

const updatedMatrix = await matrixRenderer.dieMatrix;
expect(updatedMatrix).toEqual(Uint8Array.from(testData));
const updatedMatrix = await matrixRenderer.getMatrix();
expect(updatedMatrix).toEqual(
{
colIndexes: Uint32Array.from(testData.colIndexes),
rowIndexes: Uint32Array.from(testData.rowIndexes),
values: Float64Array.from(testData.values)
}
);
});

it('emptyMatrix should empty the dieMatrix', async () => {
await matrixRenderer.emptyMatrix();

const updatedMatrix = await matrixRenderer.dieMatrix;
expect(updatedMatrix.length).toEqual(0);
const updatedMatrix = await matrixRenderer.getMatrix();
expect(updatedMatrix.colIndexes.length
+ updatedMatrix.rowIndexes.length
+ updatedMatrix.values.length).toEqual(0);
});

it('should get the matrix', async () => {
const testData = {
colIndexes: [4, 1, 2],
rowIndexes: [54, 54, 62],
values: [8.12, 9.0, 0.32]
};

await matrixRenderer.updateMatrix(testData);

await matrixRenderer.drawWafer();

expect(matrixRenderer.clearCanvas).toHaveBeenCalled();
expect(matrixRenderer.scaleCanvas).toHaveBeenCalled();
expect(matrixRenderer.addTextOnDie).toHaveBeenCalledTimes(3);
});

it('should draw the wafer', async () => {
const offscreenCanvas = new OffscreenCanvas(500, 500);
await matrixRenderer.setCanvas(offscreenCanvas);
spyOn(matrixRenderer, 'clearCanvas');
spyOn(matrixRenderer, 'scaleCanvas');
spyOn(matrixRenderer, 'addTextOnDie');

matrixRenderer.drawWafer();

expect(matrixRenderer.clearCanvas).toHaveBeenCalled();
expect(matrixRenderer.scaleCanvas).toHaveBeenCalled();
expect(matrixRenderer.addTextOnDie).toHaveBeenCalledTimes(3);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export interface WaferMapTypedMatrix {
colIndexes: Uint32Array;
rowIndexes: Uint32Array;
values: Float64Array;
}

export interface WaferMapMatrix {
colIndexes: number[];
rowIndexes: number[];
values: number[];
}

export interface Transform{
k: number;
x: number;
y: number;
}

export interface Dimensions{
width: number;
height: number;
}
1 change: 1 addition & 0 deletions packages/nimble-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"@storybook/html-webpack5": "^7.6.13",
"@storybook/theming": "^7.6.13",
"@types/jasmine": "^5.1.4",
"@types/offscreencanvas": "^2019.7.3",
"@types/webpack-env": "^1.15.2",
"babel-loader": "^9.1.2",
"circular-dependency-plugin": "^5.2.0",
Expand Down
28 changes: 27 additions & 1 deletion packages/nimble-components/src/wafer-map/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { DesignSystem, FoundationElement } from '@microsoft/fast-foundation';
import { zoomIdentity, ZoomTransform } from 'd3-zoom';
import type { Table } from 'apache-arrow';
import { type Remote, transfer } from 'comlink';
import { template } from './template';
import { styles } from './styles';
import { DataManager } from './modules/data-manager';
Expand All @@ -23,6 +24,8 @@
import { WaferMapUpdateTracker } from './modules/wafer-map-update-tracker';
import { WaferMapValidator } from './modules/wafer-map-validator';
import { WorkerRenderer } from './modules/worker-renderer';
import type { MatrixRenderer } from '../../build/generate-workers/dist/esm/source/matrix-renderer';
import { createMatrixRenderer } from './modules/create-matrix-renderer';

declare global {
interface HTMLElementTagNameMap {
Expand Down Expand Up @@ -70,11 +73,21 @@
@attr({ attribute: 'color-scale-mode' })
public colorScaleMode: WaferMapColorScaleMode = WaferMapColorScaleMode.linear;

/**
* @internal
*/
public workerOne!: Remote<MatrixRenderer>;

/**
* @internal
*/
public readonly canvas!: HTMLCanvasElement;

/**
* @internal
*/
public readonly canvasOne!: HTMLCanvasElement;

/**
* @internal
*/
Expand Down Expand Up @@ -163,11 +176,19 @@
return this.waferMapValidator.getValidity();
}

public override connectedCallback(): void {
public override async connectedCallback(): Promise<void> {
super.connectedCallback();
this.canvasContext = this.canvas.getContext('2d', {
willReadFrequently: true
})!;
const { matrixRenderer } = await createMatrixRenderer();
this.workerOne = matrixRenderer;

const offscreenOne = this.canvasOne.transferControlToOffscreen();
await this.workerOne.setCanvas(
transfer(offscreenOne, [offscreenOne as unknown as Transferable])
);

this.resizeObserver.observe(this);
this.waferMapUpdateTracker.trackAll();
}
Expand Down Expand Up @@ -211,6 +232,7 @@
} else if (this.waferMapUpdateTracker.requiresDrawnWaferUpdate) {
this.renderer.drawWafer();
}
this.workerOne.drawWafer();

Check failure on line 235 in packages/nimble-components/src/wafer-map/index.ts

View workflow job for this annotation

GitHub Actions / build

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
this.eventCoordinator.attachEvents();
} else if (this.waferMapUpdateTracker.requiresRenderHoverUpdate) {
this.renderer.renderHover();
Expand All @@ -235,6 +257,10 @@
this.canvas.height = height;
this.canvasWidth = width;
this.canvasHeight = height;
this.workerOne.setCanvasDimensions({ width, height }).then(
() => {},
() => {}
);
});
return resizeObserver;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@

private renderText(): void {
if (this.wafermap.dieLabelsHidden) {
return;
return;

Check failure on line 101 in packages/nimble-components/src/wafer-map/modules/rendering.ts

View workflow job for this annotation

GitHub Actions / build

Trailing spaces not allowed
}
const dieWidth = this.wafermap.dataManager.dieDimensions.width;
const dieHeight = this.wafermap.dataManager.dieDimensions.height;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { HoverDieOpacity } from '../types';
* Responsible for drawing the dies inside the wafer map, adding dieText and scaling the canvas
*/
export class WorkerRenderer {
public constructor(private readonly wafermap: WaferMap) {}
public constructor(private readonly wafermap: WaferMap) { }

public updateSortedDiesAndDrawWafer(): void {
// redundant function for backwards compatibility
Expand Down
Loading
Loading