Skip to content

Commit

Permalink
Wafer Map Experimental Event Coordinator (#1916)
Browse files Browse the repository at this point in the history
# Pull Request

## 🤨 Rationale

I tried to expand on the strategy switching mechanism from the renderer,
and reused it in the coordinator

This approach has the benefits of isolating the experimental code while
in development, and an easy way to switch to the new version by changing
just the imports and switching mechanism and removing the old versions
after they become obsolete

## 👩‍💻 Implementation

because arquero causes typescript errors I implemented an alternative
search and posted a PR with fixes in arquero repo
uwdata/arquero#346
changed the zoom handler to catch events on the component, not just the
canvas
removed zooming border of around 100px when zooming in the wafer after
PO decision, resulting in less complex code and touch controls enabling.

## 🧪 Testing

existing tests are passing
created new hover unit tests with wafer and data manager mocks

## ✅ 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: rajsite <[email protected]>
  • Loading branch information
munteannatan and rajsite authored Mar 25, 2024
1 parent 626b6c7 commit f56b73b
Show file tree
Hide file tree
Showing 22 changed files with 698 additions and 471 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Created new hover event for the new diesTable api and changed the zoom event",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
78 changes: 58 additions & 20 deletions packages/nimble-components/src/wafer-map/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@ import { template } from './template';
import { styles } from './styles';
import { DataManager } from './modules/data-manager';
import { RenderingModule } from './modules/rendering';
import { EventCoordinator } from './modules/event-coordinator';
import {
HoverDie,
HoverDieOpacity,
WaferMapColorScale,
WaferMapColorScaleMode,
WaferMapDie,
WaferMapOrientation,
WaferMapOriginLocation,
WaferMapValidity
WaferMapValidity,
type WaferRequiredFields
} from './types';
import { WaferMapUpdateTracker } from './modules/wafer-map-update-tracker';
import { WaferMapValidator } from './modules/wafer-map-validator';
import { WorkerRenderer } from './modules/worker-renderer';
import { WorkerRenderer } from './modules/experimental/worker-renderer';
import { HoverHandler } from './modules/hover-handler';
import { HoverHandler as ExperimentalHoverHandler } from './modules/experimental/hover-handler';
import { ZoomHandler } from './modules/zoom-handler';

declare global {
interface HTMLElementTagNameMap {
Expand All @@ -33,12 +37,14 @@ declare global {
/**
* A nimble-styled WaferMap
*/
export class WaferMap extends FoundationElement {
export class WaferMap<
T extends WaferRequiredFields = WaferRequiredFields
> extends FoundationElement {
/**
* @internal
* needs to be initialized before the properties trigger changes
*/
public readonly waferMapUpdateTracker = new WaferMapUpdateTracker(this);
public readonly waferMapUpdateTracker: WaferMapUpdateTracker = new WaferMapUpdateTracker(this.asRequiredFieldsWaferMap);

@attr({ attribute: 'origin-location' })
public originLocation: WaferMapOriginLocation = WaferMapOriginLocation.bottomLeft;
Expand Down Expand Up @@ -88,15 +94,23 @@ export class WaferMap extends FoundationElement {
/**
* @internal
*/
public readonly dataManager = new DataManager(this);
public readonly dataManager: DataManager = new DataManager(
this.asRequiredFieldsWaferMap
);

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

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

@observable
public renderer: RenderingModule | WorkerRenderer = this.mainRenderer;
Expand Down Expand Up @@ -144,20 +158,29 @@ export class WaferMap extends FoundationElement {
/**
* @internal
*/
@observable public hoverDie: WaferMapDie | undefined;
@observable public hoverDie: WaferMapDie | HoverDie | undefined;

@observable public highlightedTags: string[] = [];
@observable public dies: WaferMapDie[] = [];
@observable public diesTable: Table | undefined;
@observable public diesTable: Table<T> | undefined;

@observable public colorScale: WaferMapColorScale = {
colors: [],
values: []
};

private readonly eventCoordinator = new EventCoordinator(this);
private readonly hoverHandler: HoverHandler = new HoverHandler(
this.asRequiredFieldsWaferMap
);

private readonly experimentalHoverHandler: ExperimentalHoverHandler = new ExperimentalHoverHandler(this.asRequiredFieldsWaferMap);

private readonly zoomHandler: ZoomHandler = new ZoomHandler(
this.asRequiredFieldsWaferMap
);

private readonly resizeObserver = this.createResizeObserver();
private readonly waferMapValidator = new WaferMapValidator(this);
private readonly waferMapValidator: WaferMapValidator = new WaferMapValidator(this.asRequiredFieldsWaferMap);

public get validity(): WaferMapValidity {
return this.waferMapValidator.getValidity();
Expand All @@ -168,12 +191,18 @@ export class WaferMap extends FoundationElement {
this.canvasContext = this.canvas.getContext('2d', {
willReadFrequently: true
})!;
this.hoverHandler.connect();
this.experimentalHoverHandler.connect();
this.zoomHandler.connect();
this.resizeObserver.observe(this);
this.waferMapUpdateTracker.trackAll();
}

public override disconnectedCallback(): void {
super.disconnectedCallback();
this.hoverHandler.disconnect();
this.experimentalHoverHandler.disconnect();
this.zoomHandler.disconnect();
this.resizeObserver.unobserve(this);
}

Expand All @@ -190,8 +219,12 @@ export class WaferMap extends FoundationElement {
if (this.validity.invalidDiesTableSchema) {
return;
}
this.renderer = this.isExperimentalRenderer()
? this.workerRenderer
: this.mainRenderer;
if (this.waferMapUpdateTracker.requiresEventsUpdate) {
this.eventCoordinator.detachEvents();
// zoom translateExtent needs to be recalculated when canvas size changes
this.zoomHandler.disconnect();
if (this.waferMapUpdateTracker.requiresContainerDimensionsUpdate) {
this.dataManager.updateContainerDimensions();
this.renderer.updateSortedDiesAndDrawWafer();
Expand All @@ -211,12 +244,19 @@ export class WaferMap extends FoundationElement {
} else if (this.waferMapUpdateTracker.requiresDrawnWaferUpdate) {
this.renderer.drawWafer();
}
this.eventCoordinator.attachEvents();
this.zoomHandler.connect();
} else if (this.waferMapUpdateTracker.requiresRenderHoverUpdate) {
this.renderer.renderHover();
}
}

/**
* @internal
*/
public isExperimentalRenderer(): boolean {
return this.diesTable !== undefined;
}

private validate(): void {
this.waferMapValidator.validateGridDimensions();
this.waferMapValidator.validateDiesTableSchema();
Expand Down Expand Up @@ -291,17 +331,11 @@ export class WaferMap extends FoundationElement {

private diesChanged(): void {
this.waferMapUpdateTracker.track('dies');
this.renderer = this.diesTable === undefined
? this.mainRenderer
: this.workerRenderer;
this.waferMapUpdateTracker.queueUpdate();
}

private diesTableChanged(): void {
this.waferMapUpdateTracker.track('dies');
this.renderer = this.diesTable === undefined
? this.mainRenderer
: this.workerRenderer;
this.waferMapUpdateTracker.queueUpdate();
}

Expand Down Expand Up @@ -330,6 +364,10 @@ export class WaferMap extends FoundationElement {
this.waferMapUpdateTracker.track('hoverDie');
this.waferMapUpdateTracker.queueUpdate();
}

private get asRequiredFieldsWaferMap(): WaferMap {
return this as WaferMap;
}
}

const nimbleWaferMap = WaferMap.compose({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ export class DataManager {
return this.dataMap;
}

private readonly computations;
private readonly prerendering;
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, this);
this.prerendering = new Prerendering(wafermap);
}

public updateContainerDimensions(): void {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { WaferMap } from '../..';
import { PointCoordinates, WaferMapOriginLocation } from '../../types';

/**
* HoverHandler deals with user interactions and events like hovering
*/
export class HoverHandler {
public constructor(private readonly wafermap: WaferMap) {}

/**
* @internal
*/
public connect(): void {
this.wafermap.addEventListener('mousemove', this.onMouseMove);
this.wafermap.addEventListener('mouseout', this.onMouseOut);
}

/**
* @internal
*/
public disconnect(): void {
this.wafermap.removeEventListener('mousemove', this.onMouseMove);
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()) {
return;
}
// get original mouse position in case we are in zoom.
const invertedPoint = this.wafermap.transform.invert([
event.offsetX,
event.offsetY
]);

// does not work yet until data manager will parse diesTable
const dieCoordinates = this.calculateDieCoordinates({
x: invertedPoint[0],
y: invertedPoint[1]
});
const colIndex = this.wafermap
.diesTable!.getChild('colIndex')!
.toArray();
const rowIndex = this.wafermap
.diesTable!.getChild('rowIndex')!
.toArray();

// will replace iterating with arquero filtering after fixing errors
for (let i = 0; i < colIndex.length; i++) {
if (
colIndex[i] === dieCoordinates.x
&& rowIndex[i] === dieCoordinates.y
) {
this.wafermap.hoverDie = {
index: i,
x: dieCoordinates.x,
y: dieCoordinates.y
};
return;
}
}
this.wafermap.hoverDie = undefined;
};

private readonly onMouseOut = (_event: MouseEvent): void => {
this.wafermap.hoverDie = undefined;
};

private calculateDieCoordinates(
mousePosition: PointCoordinates
): PointCoordinates {
const originLocation = this.wafermap.originLocation;
const xRoundFunction = originLocation === WaferMapOriginLocation.bottomLeft
|| originLocation === WaferMapOriginLocation.topLeft
? Math.floor
: Math.ceil;
const yRoundFunction = originLocation === WaferMapOriginLocation.bottomLeft
|| originLocation === WaferMapOriginLocation.bottomRight
? Math.floor
: Math.ceil;
// go to x and y scale to get the x,y values of the die.
const x = xRoundFunction(
this.wafermap.dataManager.invertedHorizontalScale(
mousePosition.x - this.wafermap.dataManager.margin.left
)
);
const y = yRoundFunction(
this.wafermap.dataManager.invertedVerticalScale(
mousePosition.y - this.wafermap.dataManager.margin.top
)
);
return { x, y };
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { WaferMap } from '..';
import { HoverDieOpacity } from '../types';
import type { WaferMap } from '../..';
import { HoverDieOpacity } from '../../types';

/**
* Responsible for drawing the dies inside the wafer map, adding dieText and scaling the canvas
Expand Down
Loading

0 comments on commit f56b73b

Please sign in to comment.