@deck.gl/core
Version:
deck.gl core library
137 lines (123 loc) • 3.86 kB
text/typescript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import log from '../../utils/log';
import type Layer from '../layer';
import type Viewport from '../../viewports/viewport';
import type {PickingColorDecoder} from '../../passes/pick-layers-pass';
export type PickedPixel = {
pickedColor: Uint8Array | null;
pickedLayer?: Layer;
pickedViewports?: Viewport[];
pickedX?: number;
pickedY?: number;
pickedObjectIndex: number;
};
const NO_PICKED_OBJECT = {
pickedColor: null,
pickedObjectIndex: -1
};
/* eslint-disable max-depth, max-statements */
/**
* Pick at a specified pixel with a tolerance radius
* Returns the closest object to the pixel in shape `{pickedColor, pickedLayer, pickedObjectIndex}`
*/
export function getClosestObject({
pickedColors,
decodePickingColor,
deviceX,
deviceY,
deviceRadius,
deviceRect
}: {
pickedColors: Uint8Array;
decodePickingColor: PickingColorDecoder;
deviceX: number;
deviceY: number;
deviceRadius: number;
deviceRect: {x: number; y: number; width: number; height: number};
}): PickedPixel {
// Traverse all pixels in picking results and find the one closest to the supplied
// [deviceX, deviceY]
const {x, y, width, height} = deviceRect;
let minSquareDistanceToCenter = deviceRadius * deviceRadius;
let closestPixelIndex = -1;
let i = 0;
for (let row = 0; row < height; row++) {
const dy = row + y - deviceY;
const dy2 = dy * dy;
if (dy2 > minSquareDistanceToCenter) {
// skip this row
i += 4 * width;
} else {
for (let col = 0; col < width; col++) {
// Decode picked layer from color
const pickedLayerIndex = pickedColors[i + 3] - 1;
if (pickedLayerIndex >= 0) {
const dx = col + x - deviceX;
const d2 = dx * dx + dy2;
if (d2 <= minSquareDistanceToCenter) {
minSquareDistanceToCenter = d2;
closestPixelIndex = i;
}
}
i += 4;
}
}
}
if (closestPixelIndex >= 0) {
// Decode picked object index from color
const pickedColor = pickedColors.slice(closestPixelIndex, closestPixelIndex + 4);
const pickedObject = decodePickingColor(pickedColor);
if (pickedObject) {
const dy = Math.floor(closestPixelIndex / 4 / width);
const dx = closestPixelIndex / 4 - dy * width;
return {
...pickedObject,
pickedColor,
pickedX: x + dx,
pickedY: y + dy
};
}
log.error('Picked non-existent layer. Is picking buffer corrupt?')();
}
return NO_PICKED_OBJECT;
}
/**
* Examines a picking buffer for unique colors
* Returns array of unique objects in shape `{x, y, pickedColor, pickedLayer, pickedObjectIndex}`
*/
export function getUniqueObjects({
pickedColors,
decodePickingColor
}: {
pickedColors: Uint8Array;
decodePickingColor: PickingColorDecoder;
}): PickedPixel[] {
const uniqueColors = new Map();
// Traverse all pixels in picking results and get unique colors
if (pickedColors) {
for (let i = 0; i < pickedColors.length; i += 4) {
// Decode picked layer from color
const pickedLayerIndex = pickedColors[i + 3] - 1;
if (pickedLayerIndex >= 0) {
const pickedColor = pickedColors.slice(i, i + 4);
const colorKey = pickedColor.join(',');
// eslint-disable-next-line
if (!uniqueColors.has(colorKey)) {
const pickedObject = decodePickingColor(pickedColor);
// eslint-disable-next-line
if (pickedObject) {
uniqueColors.set(colorKey, {
...pickedObject,
color: pickedColor
});
} else {
log.error('Picked non-existent layer. Is picking buffer corrupt?')();
}
}
}
}
}
return Array.from(uniqueColors.values());
}