Skip to content

Commit

Permalink
Created a new Experimental Prerendering module (#1955)
Browse files Browse the repository at this point in the history
# Pull Request

## 🤨 Rationale

Created a new experimental prerendering module.

The previous prerendering oversaw calculating the `fillstyle` of each
die and it's label.
We want to move these expensive operations to the web worker, and to
make them faster.

## 👩‍💻 Implementation

I removed the code which prepared the dies for rendering.
I created a domain splitter for linear color scales. this will reduce
the number of colors displayed but will increase the performance because
we are no longer finding interpolations for each individual value in the
whole domain.

Open Issues:
- given the problems with arquero, should we implement in-house
highlight or leave it unimplemented until the issue gets fixed?

## 🧪 Testing

<!---
Detail the testing done to ensure this submission meets requirements. 

Include automated/manual test additions or modifications, testing done
on a local build, private CI run results, and additional testing not
covered by automatic pull request validation. If any functionality is
not covered by automated testing, provide justification.
-->

## ✅ Checklist

<!--- Review the list and put an x in the boxes that apply or ~~strike
through~~ around items that don't (along with an explanation). -->

- [x] I have updated the project documentation to reflect my changes or
determined no changes are needed.

---------

Co-authored-by: Milan Raj <[email protected]>
  • Loading branch information
munteannatan and rajsite authored Apr 4, 2024
1 parent a56f5f5 commit b6e22fe
Show file tree
Hide file tree
Showing 15 changed files with 542 additions and 199 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Created new Experimental Prerendering Module and tests",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
67 changes: 39 additions & 28 deletions packages/nimble-components/src/wafer-map/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,36 +92,20 @@ export class WaferMap<
*/
public readonly zoomContainer!: HTMLElement;

/**
* @internal
*/
public readonly stableDataManager: DataManager = new DataManager(
this.asRequiredFieldsWaferMap
);

/**
* @internal
*/
public readonly experimentalDataManager: ExperimentalDataManager = new ExperimentalDataManager(this.asRequiredFieldsWaferMap);

public dataManager: DataManager | ExperimentalDataManager = this.stableDataManager;

/**
* @internal
*/
public readonly mainRenderer = new RenderingModule(
public dataManager: DataManager = new DataManager(
this.asRequiredFieldsWaferMap
);

/**
* @internal
*/
public readonly workerRenderer = new WorkerRenderer(
this.asRequiredFieldsWaferMap
);

@observable
public renderer: RenderingModule | WorkerRenderer = this.mainRenderer;
public renderer: RenderingModule = new RenderingModule(
this.asRequiredFieldsWaferMap
);

/**
* @internal
Expand Down Expand Up @@ -214,6 +198,38 @@ export class WaferMap<
this.resizeObserver.unobserve(this);
}

/**
* @internal
* Experimental update function called when an update is queued.
*/
public experimentalUpdate(): void {
if (this.validity.invalidDiesTableSchema) {
return;
}
if (this.waferMapUpdateTracker.requiresEventsUpdate) {
// zoom translateExtent needs to be recalculated when canvas size changes
this.zoomHandler.disconnect();
if (
this.waferMapUpdateTracker.requiresContainerDimensionsUpdate
|| this.waferMapUpdateTracker.requiresScalesUpdate
) {
this.experimentalDataManager.updateComputations();
this.workerRenderer.drawWafer();
} else if (
this.waferMapUpdateTracker.requiresLabelsFontSizeUpdate
|| this.waferMapUpdateTracker.requiresDiesRenderInfoUpdate
) {
this.experimentalDataManager.updatePrerendering();
this.workerRenderer.drawWafer();
} else if (this.waferMapUpdateTracker.requiresDrawnWaferUpdate) {
this.workerRenderer.drawWafer();
}
this.zoomHandler.connect();
} else if (this.waferMapUpdateTracker.requiresRenderHoverUpdate) {
this.workerRenderer.renderHover();
}
}

/**
* @internal
* Update function called when an update is queued.
Expand All @@ -224,18 +240,13 @@ export class WaferMap<
*/
public update(): void {
this.validate();
if (this.validity.invalidDiesTableSchema) {
if (this.isExperimentalUpdate()) {
this.experimentalUpdate();
return;
}
this.renderer = this.isExperimentalRenderer()
? this.workerRenderer
: this.mainRenderer;
if (this.waferMapUpdateTracker.requiresEventsUpdate) {
// zoom translateExtent needs to be recalculated when canvas size changes
this.zoomHandler.disconnect();
this.dataManager = this.isExperimentalRenderer()
? this.experimentalDataManager
: this.stableDataManager;
if (this.waferMapUpdateTracker.requiresContainerDimensionsUpdate) {
this.dataManager.updateContainerDimensions();
this.renderer.updateSortedDiesAndDrawWafer();
Expand Down Expand Up @@ -264,7 +275,7 @@ export class WaferMap<
/**
* @internal
*/
public isExperimentalRenderer(): boolean {
public isExperimentalUpdate(): boolean {
return this.diesTable !== undefined;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class Computations {

public constructor(private readonly wafermap: WaferMap) {}

public updateContainerDimensions(): void {
public update(): void {
const canvasDimensions = {
width: this.wafermap.canvasWidth,
height: this.wafermap.canvasHeight
Expand All @@ -71,10 +71,6 @@ export class Computations {
canvasDimensions,
this._margin
);
this.updateScales();
}

public updateScales(): void {
const containerDiameter = Math.min(
this._containerDimensions.width,
this._containerDimensions.height
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import type { ScaleLinear } from 'd3-scale';
import { Computations } from './computations';
import { Prerendering } from '../prerendering';
import { Prerendering } from './prerendering';
import type { WaferMap } from '../..';
import type {
Dimensions,
Margin,
DieRenderInfo,
WaferMapDie,
PointCoordinates
} from '../../types';
import type { ColorScale, Dimensions, Margin } from '../../types';

/**
* Data Manager uses Computations and Prerendering modules in order and exposes the results
Expand Down Expand Up @@ -38,50 +32,24 @@ export class DataManager {
return this.prerendering.labelsFontSize;
}

public get diesRenderInfo(): DieRenderInfo[] {
return this.prerendering.diesRenderInfo;
}

public get data(): Map<string, WaferMapDie> {
return this.dataMap;
public get colorScale(): ColorScale {
return this.prerendering.colorScale;
}

private readonly computations: Computations;
private readonly prerendering: Prerendering;
private dataMap!: Map<string, WaferMapDie>;

public constructor(private readonly wafermap: WaferMap) {
this.computations = new Computations(wafermap);
this.prerendering = new Prerendering(wafermap);
}

public updateContainerDimensions(): void {
this.computations.updateContainerDimensions();
this.updateDataMap();
this.updateLabelsFontSize();
}

public updateScales(): void {
this.computations.updateScales();
this.updateDataMap();
this.updateLabelsFontSize();
}

public updateLabelsFontSize(): void {
this.prerendering.updateLabelsFontSize();
}

public updateDiesRenderInfo(): void {
this.prerendering.updateDiesRenderInfo();
}

public getWaferMapDie(point: PointCoordinates): WaferMapDie | undefined {
return this.dataMap.get(`${point.x}_${point.y}`);
public updateComputations(): void {
this.computations.update();
this.prerendering.update();
}

private updateDataMap(): void {
this.dataMap = new Map(
this.wafermap.dies.map(die => [`${die.x}_${die.y}`, die])
);
public updatePrerendering(): void {
this.prerendering.update();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { WaferMap } from '../..';
import { PointCoordinates, WaferMapOriginLocation } from '../../types';
import { DataManager } from './data-manager';

/**
* HoverHandler deals with user interactions and events like hovering
Expand All @@ -24,12 +23,8 @@ export class HoverHandler {
this.wafermap.removeEventListener('mouseout', this.onMouseOut);
}

/**
* @internal
* keep public for testing until data manager refactor
*/
public readonly onMouseMove = (event: MouseEvent): void => {
if (!this.wafermap.isExperimentalRenderer()) {
private readonly onMouseMove = (event: MouseEvent): void => {
if (!this.wafermap.isExperimentalUpdate()) {
return;
}
// get original mouse position in case we are in zoom.
Expand Down Expand Up @@ -76,10 +71,7 @@ export class HoverHandler {
private calculateDieCoordinates(
mousePosition: PointCoordinates
): PointCoordinates | undefined {
if (
this.wafermap.isExperimentalRenderer()
&& this.wafermap.dataManager instanceof DataManager
) {
if (this.wafermap.isExperimentalUpdate()) {
const originLocation = this.wafermap.originLocation;
const xRoundFunction = originLocation === WaferMapOriginLocation.bottomLeft
|| originLocation === WaferMapOriginLocation.topLeft
Expand All @@ -91,13 +83,15 @@ export class HoverHandler {
: Math.floor;
// go to x and y scale to get the x,y values of the die.
const x = xRoundFunction(
this.wafermap.dataManager.horizontalScale.invert(
mousePosition.x - this.wafermap.dataManager.margin.left
this.wafermap.experimentalDataManager.horizontalScale.invert(
mousePosition.x
- this.wafermap.experimentalDataManager.margin.left
)
);
const y = yRoundFunction(
this.wafermap.dataManager.verticalScale.invert(
mousePosition.y - this.wafermap.dataManager.margin.top
this.wafermap.experimentalDataManager.verticalScale.invert(
mousePosition.y
- this.wafermap.experimentalDataManager.margin.top
)
);
return { x, y };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { scaleLinear } from 'd3-scale';
import { ticks } from 'd3-array';
import { WaferMapColorScaleMode } from '../../types';
import type { ColorScale, Dimensions } from '../../types';
import type { WaferMap } from '../..';

/**
* Prerendering prepares render-ready dies data to be used by the rendering module
*/
export class Prerendering {
public get labelsFontSize(): number {
return this._labelsFontSize;
}

public get colorScale(): ColorScale {
return this._colorScale;
}

private _colorScale!: ColorScale;

private _labelsFontSize!: number;

private readonly fontSizeFactor = 0.8;
private readonly colorScaleResolution = 10;

public constructor(private readonly wafermap: WaferMap) {}

public update(): void {
this._labelsFontSize = this.calculateLabelsFontSize(
this.wafermap.experimentalDataManager.dieDimensions,
this.wafermap.maxCharacters
);
this._colorScale = this.calculateColorScale();
}

private calculateColorScale(): ColorScale {
if (this.wafermap.colorScaleMode === WaferMapColorScaleMode.linear) {
const values = this.wafermap.colorScale.values.map(item => +item);
const d3ColorScale = scaleLinear<string, string>()
.domain(values)
.range(this.wafermap.colorScale.colors);
let min = values[0]!;
let max = values[0]!;
values.forEach(value => {
if (value < min) {
min = value;
}
if (value > max) {
max = value;
}
});
// the linear color scale will not be infinite but will be limited by the color scale resolution
const valueSamples = ticks(
min,
max,
values.length * this.colorScaleResolution
);
return valueSamples.map(value => {
return {
color: d3ColorScale(value),
value
};
});
}
// ordinal color categories have to be sorted by value
return this.wafermap.colorScale.colors
.map((color, index) => {
return {
color,
value: +this.wafermap.colorScale.values[index]!
};
})
.sort((a, b) => a.value - b.value);
}

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

0 comments on commit b6e22fe

Please sign in to comment.