Skip to content

Commit

Permalink
Created Computations and Data Manager Experimental (#1946)
Browse files Browse the repository at this point in the history
# Pull Request

## 🤨 Rationale

Created new computations experimental component with changed scales and
diesTable iteration for finding the bounding box until arquero fixes.

The new scales will be able to be inverted without the need to create
separate scales for the hover handler.

## 👩‍💻 Implementation

changed the scales and iteration over the diesTable
integrated the changes and the strategy swap with the wafermap and the
experimental hover

## 🧪 Testing

copied spec classes for the new experimental modules.
updated hover handler spec.
adapted the data manager to the previous renderer for visual tests in
storybook.

## ✅ 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.
  • Loading branch information
munteannatan authored Mar 27, 2024
1 parent e7f9887 commit c6d99d8
Show file tree
Hide file tree
Showing 14 changed files with 796 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Created new computations experimental module and tested it, created new data manager experimental and integrated with hover and old renderer",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
13 changes: 12 additions & 1 deletion packages/nimble-components/src/wafer-map/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { Table } from 'apache-arrow';
import { template } from './template';
import { styles } from './styles';
import { DataManager } from './modules/data-manager';
import { DataManager as ExperimentalDataManager } from './modules/experimental/data-manager';
import { RenderingModule } from './modules/rendering';
import {
HoverDie,
Expand Down Expand Up @@ -94,10 +95,17 @@ export class WaferMap<
/**
* @internal
*/
public readonly dataManager: DataManager = new DataManager(
public readonly stableDataManager: DataManager = new DataManager(
this.asRequiredFieldsWaferMap
);

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

public dataManager: DataManager | ExperimentalDataManager = this.stableDataManager;

/**
* @internal
*/
Expand Down Expand Up @@ -225,6 +233,9 @@ export class WaferMap<
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import { scaleLinear, ScaleLinear } from 'd3-scale';
import type { WaferMap } from '../..';
import { Dimensions, Margin, WaferMapOriginLocation } from '../../types';

interface GridDimensions {
origin: {
x: number,
y: number
};
rows: number;
cols: number;
}

/**
* Computations calculates and stores different measures which are used in the Wafermap
*/
export class Computations {
public get containerDimensions(): Dimensions {
return this._containerDimensions;
}

public get dieDimensions(): Dimensions {
return this._dieDimensions;
}

public get margin(): Margin {
return this._margin;
}

public get horizontalScale(): ScaleLinear<number, number> {
return this._horizontalScale;
}

public get verticalScale(): ScaleLinear<number, number> {
return this._verticalScale;
}

private _containerDimensions!: Dimensions;
private _dieDimensions!: Dimensions;
private _margin!: Margin;
private _horizontalScale!: ScaleLinear<number, number>;
private _verticalScale!: ScaleLinear<number, number>;
private readonly defaultPadding = 0;
private readonly baseMarginPercentage = 0.04;

public constructor(private readonly wafermap: WaferMap) {}

public updateContainerDimensions(): void {
const canvasDimensions = {
width: this.wafermap.canvasWidth,
height: this.wafermap.canvasHeight
};
const canvasDiameter = Math.min(
canvasDimensions.width,
canvasDimensions.height
);
const canvasMargin = {
top: (canvasDimensions.height - canvasDiameter) / 2,
right: (canvasDimensions.width - canvasDiameter) / 2,
bottom: (canvasDimensions.height - canvasDiameter) / 2,
left: (canvasDimensions.width - canvasDiameter) / 2
};
const baseMargin = {
top: canvasDiameter * this.baseMarginPercentage,
right: canvasDiameter * this.baseMarginPercentage,
bottom: canvasDiameter * this.baseMarginPercentage,
left: canvasDiameter * this.baseMarginPercentage
};
this._margin = this.calculateMarginAddition(baseMargin, canvasMargin);
this._containerDimensions = this.calculateContainerDimensions(
canvasDimensions,
this._margin
);
this.updateScales();
}

public updateScales(): void {
const containerDiameter = Math.min(
this._containerDimensions.width,
this._containerDimensions.height
);
const gridDimensions = this.gridDimensionsValidAndDefined()
? this.calculateGridDimensionsFromBoundingBox()
: this.calculateGridDimensionsFromDies();
// this scale is used for positioning the dies on the canvas
const originLocation = this.wafermap.originLocation;
this._horizontalScale = this.createHorizontalScale(
originLocation,
gridDimensions,
containerDiameter
);
// this scale is used for positioning the dies on the canvas
this._verticalScale = this.createVerticalScale(
originLocation,
gridDimensions,
containerDiameter
);
this._dieDimensions = {
width: Math.abs(
this._horizontalScale(0) - this._horizontalScale(1)
),
height: Math.abs(this._verticalScale(0) - this._verticalScale(1))
};
}

private gridDimensionsValidAndDefined(): boolean {
return (
!this.wafermap.validity.invalidGridDimensions
&& typeof this.wafermap.gridMinX === 'number'
&& typeof this.wafermap.gridMinY === 'number'
&& typeof this.wafermap.gridMaxX === 'number'
&& typeof this.wafermap.gridMinX === 'number'
);
}

private calculateGridDimensionsFromBoundingBox(): GridDimensions {
const gridDimensions = { origin: { x: 0, y: 0 }, rows: 0, cols: 0 };
if (
typeof this.wafermap.gridMaxY === 'number'
&& typeof this.wafermap.gridMinY === 'number'
&& typeof this.wafermap.gridMaxX === 'number'
&& typeof this.wafermap.gridMinX === 'number'
) {
gridDimensions.origin.x = this.wafermap.gridMinX;
gridDimensions.origin.y = this.wafermap.gridMinY;
gridDimensions.rows = this.wafermap.gridMaxY - this.wafermap.gridMinY + 1;
gridDimensions.cols = this.wafermap.gridMaxX - this.wafermap.gridMinX + 1;
}
return gridDimensions;
}

private calculateGridDimensionsFromDies(): GridDimensions {
if (this.wafermap.diesTable === undefined) {
return { origin: { x: 0, y: 0 }, rows: 0, cols: 0 };
}

const colIndex = this.wafermap.diesTable
.getChild('colIndex')!
.toArray();
const rowIndex = this.wafermap.diesTable
.getChild('rowIndex')!
.toArray();

const minPoint = { x: colIndex[0]!, y: rowIndex[0]! };
const maxPoint = { x: colIndex[0]!, y: rowIndex[0]! };

// will replace iterating with arquero after fixing issues: https://github.com/uwdata/arquero/pull/346
for (let i = 0; i < colIndex.length; i++) {
if (colIndex[i]! < minPoint.x) {
minPoint.x = colIndex[i]!;
}
if (colIndex[i]! > maxPoint.x) {
maxPoint.x = colIndex[i]!;
}
if (rowIndex[i]! < minPoint.y) {
minPoint.y = rowIndex[i]!;
}
if (rowIndex[i]! > maxPoint.y) {
maxPoint.y = rowIndex[i]!;
}
}

return {
origin: minPoint,
rows: maxPoint.y - minPoint.y + 1,
cols: maxPoint.x - minPoint.x + 1
};
}

private calculateContainerDimensions(
canvasDimensions: Dimensions,
margin: Margin
): Dimensions {
return {
width: canvasDimensions.width - margin.left - margin.right,
height: canvasDimensions.height - margin.top - margin.bottom
};
}

private createHorizontalScale(
originLocation: WaferMapOriginLocation,
grid: GridDimensions,
containerWidth: number
): ScaleLinear<number, number> {
const scale = scaleLinear<number, number>();
if (
originLocation === WaferMapOriginLocation.bottomLeft
|| originLocation === WaferMapOriginLocation.topLeft
) {
return scale
.domain([grid.origin.x, grid.origin.x + grid.cols])
.range([0, containerWidth]);
}
return scale
.domain([grid.origin.x - 1, grid.origin.x + grid.cols - 1])
.range([containerWidth, 0]);
}

private createVerticalScale(
originLocation: WaferMapOriginLocation,
grid: GridDimensions,
containerHeight: number
): ScaleLinear<number, number> {
const scale = scaleLinear<number, number>();
// html canvas has top-left origin https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes#the_grid
// we need to flip the vertical scale
if (
originLocation === WaferMapOriginLocation.bottomLeft
|| originLocation === WaferMapOriginLocation.bottomRight
) {
return scale
.domain([grid.origin.y - 1, grid.origin.y + grid.rows - 1])
.range([containerHeight, 0]);
}
return scale
.domain([grid.origin.y, grid.origin.y + grid.rows])
.range([0, containerHeight]);
}

private calculateMarginAddition(
baseMargin: Margin,
addedMargin: Margin
): Margin {
return {
top: baseMargin.top + addedMargin.top,
right: baseMargin.right + addedMargin.right,
bottom: baseMargin.bottom + addedMargin.bottom,
left: baseMargin.left + addedMargin.left
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type { ScaleLinear } from 'd3-scale';
import { Computations } from './computations';
import { Prerendering } from '../prerendering';
import type { WaferMap } from '../..';
import type {
Dimensions,
Margin,
DieRenderInfo,
WaferMapDie,
PointCoordinates
} from '../../types';

/**
* Data Manager uses Computations and Prerendering modules in order and exposes the results
*/
export class DataManager {
public get containerDimensions(): Dimensions {
return this.computations.containerDimensions;
}

public get dieDimensions(): Dimensions {
return this.computations.dieDimensions;
}

public get margin(): Margin {
return this.computations.margin;
}

public get horizontalScale(): ScaleLinear<number, number> {
return this.computations.horizontalScale;
}

public get verticalScale(): ScaleLinear<number, number> {
return this.computations.verticalScale;
}

public get labelsFontSize(): number {
return this.prerendering.labelsFontSize;
}

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

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

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}`);
}

private updateDataMap(): void {
this.dataMap = new Map(
this.wafermap.dies.map(die => [`${die.x}_${die.y}`, die])
);
}
}
Loading

0 comments on commit c6d99d8

Please sign in to comment.