Skip to content

Commit b48ea96

Browse files
committed
feat(core,react,docs): Add async deck picking methods
1 parent 55af702 commit b48ea96

File tree

6 files changed

+207
-12
lines changed

6 files changed

+207
-12
lines changed

docs/api-reference/core/deck.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,83 @@ Parameters:
634634
* `force` (boolean) - if `false`, only redraw if necessary (e.g. changes have been made to views or layers). If `true`, skip the check. Default `false`.
635635
636636
637+
#### `pickObjectAsync` {#pickobjectasync}
638+
639+
Get the closest pickable and visible object at the given screen coordinate.
640+
641+
```ts
642+
await deck.pickObjectAsync({x, y, radius, layerIds, unproject3D})
643+
```
644+
645+
Parameters:
646+
647+
* `x` (number) - x position in pixels
648+
* `y` (number) - y position in pixels
649+
* `radius` (number, optional) - radius of tolerance in pixels. Default `0`.
650+
* `layerIds` (string[], optional) - a list of layer ids to query from. If not specified, then all pickable and visible layers are queried.
651+
* `unproject3D` (boolean, optional) - if `true`, `info.coordinate` will be a 3D point by unprojecting the `x, y` screen coordinates onto the picked geometry. Default `false`.
652+
653+
Returns:
654+
655+
* a single [`info`](../../developer-guide/interactivity.md#the-pickinginfo-object) object, or `null` if nothing is found.
656+
657+
658+
#### `pickMultipleObjectsAsync` {#pickmultipleobjectsasync}
659+
660+
Performs deep picking. Finds all close pickable and visible object at the given screen coordinate, even if those objects are occluded by other objects.
661+
662+
```ts
663+
await deck.pickMultipleObjectsAsync({x, y, radius, layerIds, depth, unproject3D})
664+
```
665+
666+
Parameters:
667+
668+
* `x` (number) - x position in pixels
669+
* `y` (number) - y position in pixels
670+
* `radius` (number, optional) - radius of tolerance in pixels. Default `0`.
671+
* `layerIds` (string[], optional) - a list of layer ids to query from. If not specified, then all pickable and visible layers are queried.
672+
* `depth` - Specifies the max number of objects to return. Default `10`.
673+
* `unproject3D` (boolean, optional) - if `true`, `info.coordinate` will be a 3D point by unprojecting the `x, y` screen coordinates onto the picked geometry. Default `false`.
674+
675+
Returns:
676+
677+
* An array of [`info`](../../developer-guide/interactivity.md#the-pickinginfo-object) objects. The array will be empty if no object was picked.
678+
679+
Notes:
680+
681+
* Deep picking is implemented as a sequence of simpler picking operations and can have a performance impact. Should this become a concern, you can use the `depth` parameter to limit the number of matches that can be returned, and thus the maximum number of picking operations.
682+
683+
684+
#### `pickObjectsAsync` {#pickobjectsasync}
685+
686+
Get all pickable and visible objects within a bounding box.
687+
688+
```ts
689+
await deck.pickObjectsAsync({x, y, width, height, layerIds, maxObjects})
690+
```
691+
692+
Parameters:
693+
694+
* `x` (number) - left of the bounding box in pixels
695+
* `y` (number) - top of the bouding box in pixels
696+
* `width` (number, optional) - width of the bouding box in pixels. Default `1`.
697+
* `height` (number, optional) - height of the bouding box in pixels. Default `1`.
698+
* `layerIds` (string[], optional) - a list of layer ids to query from. If not specified, then all pickable and visible layers are queried.
699+
* `maxObjects` (number, optional) - if specified, limits the number of objects that can be returned.
700+
701+
Returns:
702+
703+
* an array of unique [`info`](../../developer-guide/interactivity.md#the-pickinginfo-object) objects
704+
705+
Notes:
706+
707+
* The query methods are designed to quickly find objects by utilizing the picking buffer.
708+
* The query methods offer more flexibility for developers to handle events compared to the built-in hover and click callbacks.
709+
637710
#### `pickObject` {#pickobject}
638711
712+
<img src="https://img.shields.io/badge/WebGPU-❌-brightgreen.svg?style=flat-square" />
713+
639714
Get the closest pickable and visible object at the given screen coordinate.
640715
641716
```js
@@ -657,6 +732,8 @@ Returns:
657732
658733
#### `pickMultipleObjects` {#pickmultipleobjects}
659734
735+
<img src="https://img.shields.io/badge/WebGPU-❌-brightgreen.svg?style=flat-square" />
736+
660737
Performs deep picking. Finds all close pickable and visible object at the given screen coordinate, even if those objects are occluded by other objects.
661738
662739
```js
@@ -683,6 +760,8 @@ Notes:
683760
684761
#### `pickObjects` {#pickobjects}
685762
763+
<img src="https://img.shields.io/badge/WebGPU-❌-brightgreen.svg?style=flat-square" />
764+
686765
Get all pickable and visible objects within a bounding box.
687766
688767
```js

docs/api-reference/react/deckgl.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ All [Deck](../core/deck.md#methods) methods are available on the `DeckGL` compon
192192

193193
The public methods you can call explicitly list below:
194194

195+
* `pickObjectAsync`
196+
* `pickMultipleObjectsAsync`
197+
* `pickObjectsAsync`
195198
* `pickObject`
196199
* `pickMultipleObjects`
197200
* `pickObjects`

docs/developer-guide/interactivity.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ function App() {
965965

966966
While the default events handle most of the use cases, sometimes applications need more control over when and how picking is performed.
967967

968-
The picking engine is exposed through the [`Deck.pickObject`](../api-reference/core/deck.md#pickobject) and [`Deck.pickObjects`](../api-reference/core/deck.md#pickobjects) methods. These methods allow you to query what layers and objects within those layers are under a specific point or within a specified rectangle. They return `PickingInfo` objects as described above.
968+
The picking engine is exposed through the [`Deck.pickObjectAsync`](../api-reference/core/deck.md#pickobjectasync) and [`Deck.pickObjectsAsync`](../api-reference/core/deck.md#pickobjectsasync) methods. These methods allow you to query what layers and objects within those layers are under a specific point or within a specified rectangle. They return `PickingInfo` objects as described above.
969969

970970

971971
<Tabs groupId="language">
@@ -976,9 +976,9 @@ import {Deck} from '@deck.gl/core';
976976

977977
const deckInstance = new Deck({
978978
// ...
979-
onClick: ({x, y}) => {
979+
onClick: async ({x, y}) => {
980980
// Query up to 5 overlapping objects under the pointer
981-
const pickInfos = deckInstance.pickMultipleObjects({x, y, radius: 1, depth: 5});
981+
const pickInfos = await deckInstance.pickMultipleObjectsAsync({x, y, radius: 1, depth: 5});
982982
console.log(pickInfo);
983983
}
984984
});
@@ -992,9 +992,9 @@ import {Deck, PickingInfo} from '@deck.gl/core';
992992

993993
const deckInstance = new Deck({
994994
// ...
995-
onClick: ({x, y}: PickingInfo) => {
995+
onClick: async ({x, y}: PickingInfo) => {
996996
// Query up to 5 overlapping objects under the pointer
997-
const pickInfos: PickingInfo[] = deckInstance.pickMultipleObjects({x, y, radius: 1, depth: 5});
997+
const pickInfos: PickingInfo[] = await deckInstance.pickMultipleObjectsAsync({x, y, radius: 1, depth: 5});
998998
console.log(pickInfo);
999999
}
10001000
});
@@ -1011,13 +1011,13 @@ import {PickingInfo} from '@deck.gl/core';
10111011
function App() {
10121012
const deckRef = useRef<DeckGL>();
10131013

1014-
const onClick = useCallback((evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
1014+
const onClick = useCallback(async (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
10151015
// Get mouse position relative to the containing div
10161016
const containerRect = evt.currentTarget.getBoundingClientRect();
10171017
const x = evt.clientX - containerRect.left;
10181018
const y = evt.clientY = containerRect.top;
10191019
// Query up to 5 overlapping objects under the pointer
1020-
const pickInfos: PickingInfo[] = deckRef.current?.pickMultipleObjects({x, y, radius: 1, depth: 5});
1020+
const pickInfos: PickingInfo[] = await deckRef.current?.pickMultipleObjectsAsync({x, y, radius: 1, depth: 5});
10211021
console.log(pickInfo);
10221022
}, [])
10231023

@@ -1031,7 +1031,7 @@ function App() {
10311031
</Tabs>
10321032

10331033

1034-
Also note that by directly calling `pickObject`, integrating deck.gl into an existing application often becomes easier since you don't have to change the application's existing approach to event handling.
1034+
Also note that by directly calling `pickObjectAsync`, integrating deck.gl into an existing application often becomes easier since you don't have to change the application's existing approach to event handling.
10351035

10361036
### Under The Hood
10371037

modules/core/src/lib/deck-picker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export default class DeckPicker {
113113
/**
114114
* Pick the closest info at given coordinate
115115
* @returns picking info
116-
* @deprecated WebGL only - use pickObjectAsync instead
116+
* @note WebGL only - use pickObjectAsync instead
117117
*/
118118
pickObject(opts: PickByPointOptions & PickOperationContext) {
119119
return this._pickClosestObject(opts);
@@ -122,7 +122,7 @@ export default class DeckPicker {
122122
/**
123123
* Get all unique infos within a bounding box
124124
* @returns all unique infos within a bounding box
125-
* @deprecated WebGL only - use pickObjectAsync instead
125+
* @note WebGL only - use pickObjectAsync instead
126126
*/
127127
pickObjects(opts: PickByRectOptions & PickOperationContext) {
128128
return this._pickVisibleObjects(opts);

modules/core/src/lib/deck.ts

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,66 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
611611
}
612612

613613
/** Query the object rendered on top at a given point */
614+
async pickObjectAsync(opts: {
615+
/** x position in pixels */
616+
x: number;
617+
/** y position in pixels */
618+
y: number;
619+
/** Radius of tolerance in pixels. Default `0`. */
620+
radius?: number;
621+
/** A list of layer ids to query from. If not specified, then all pickable and visible layers are queried. */
622+
layerIds?: string[];
623+
/** If `true`, `info.coordinate` will be a 3D point by unprojecting the `x, y` screen coordinates onto the picked geometry. Default `false`. */
624+
unproject3D?: boolean;
625+
}): Promise<PickingInfo | null> {
626+
const infos = (await this._pickAsync('pickObjectAsync', 'pickObject Time', opts)).result;
627+
return infos.length ? infos[0] : null;
628+
}
629+
630+
/* Query all rendered objects at a given point */
631+
async pickMultipleObjectsAsync(opts: {
632+
/** x position in pixels */
633+
x: number;
634+
/** y position in pixels */
635+
y: number;
636+
/** Radius of tolerance in pixels. Default `0`. */
637+
radius?: number;
638+
/** Specifies the max number of objects to return. Default `10`. */
639+
depth?: number;
640+
/** A list of layer ids to query from. If not specified, then all pickable and visible layers are queried. */
641+
layerIds?: string[];
642+
/** If `true`, `info.coordinate` will be a 3D point by unprojecting the `x, y` screen coordinates onto the picked geometry. Default `false`. */
643+
unproject3D?: boolean;
644+
}): Promise<PickingInfo[]> {
645+
opts.depth = opts.depth || 10;
646+
return (await this._pickAsync('pickObjectAsync', 'pickMultipleObjects Time', opts)).result;
647+
}
648+
649+
/**
650+
* Query all objects rendered on top within a bounding box
651+
* @note Caveat: this method performs multiple async GPU queries, so state could potentially change between calls.
652+
*/
653+
async pickObjectsAsync(opts: {
654+
/** Left of the bounding box in pixels */
655+
x: number;
656+
/** Top of the bounding box in pixels */
657+
y: number;
658+
/** Width of the bounding box in pixels. Default `1` */
659+
width?: number;
660+
/** Height of the bounding box in pixels. Default `1` */
661+
height?: number;
662+
/** A list of layer ids to query from. If not specified, then all pickable and visible layers are queried. */
663+
layerIds?: string[];
664+
/** If specified, limits the number of objects that can be returned. */
665+
maxObjects?: number | null;
666+
}): Promise<PickingInfo[]> {
667+
return await this._pickAsync('pickObjectsAsync', 'pickObjects Time', opts);
668+
}
669+
670+
/**
671+
* Query the object rendered on top at a given point
672+
* @deprecated WebGL only. Use `pickObjectsAsync` instead
673+
*/
614674
pickObject(opts: {
615675
/** x position in pixels */
616676
x: number;
@@ -627,7 +687,10 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
627687
return infos.length ? infos[0] : null;
628688
}
629689

630-
/* Query all rendered objects at a given point */
690+
/**
691+
* Query all rendered objects at a given point
692+
* @deprecated WebGL only. Use `pickObjectsAsync` instead
693+
*/
631694
pickMultipleObjects(opts: {
632695
/** x position in pixels */
633696
x: number;
@@ -646,7 +709,10 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
646709
return this._pick('pickObject', 'pickMultipleObjects Time', opts).result;
647710
}
648711

649-
/* Query all objects rendered on top within a bounding box */
712+
/**
713+
* Query all objects rendered on top within a bounding box
714+
* @deprecated WebGL only. Use `pickObjectsAsync` instead
715+
*/
650716
pickObjects(opts: {
651717
/** Left of the bounding box in pixels */
652718
x: number;
@@ -704,6 +770,47 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
704770

705771
// Private Methods
706772

773+
private _pickAsync(
774+
method: 'pickObjectAsync',
775+
statKey: string,
776+
opts: PickByPointOptions & {layerIds?: string[]}
777+
): Promise<{
778+
result: PickingInfo[];
779+
emptyInfo: PickingInfo;
780+
}>;
781+
private _pickAsync(
782+
method: 'pickObjectsAsync',
783+
statKey: string,
784+
opts: PickByRectOptions & {layerIds?: string[]}
785+
): Promise<PickingInfo[]>;
786+
787+
private _pickAsync(
788+
method: 'pickObjectAsync' | 'pickObjectsAsync',
789+
statKey: string,
790+
opts: (PickByPointOptions | PickByRectOptions) & {layerIds?: string[]}
791+
) {
792+
assert(this.deckPicker);
793+
794+
const {stats} = this;
795+
796+
stats.get('Pick Count').incrementCount();
797+
stats.get(statKey).timeStart();
798+
799+
const infos = this.deckPicker[method]({
800+
// layerManager, viewManager and effectManager are always defined if deckPicker is
801+
layers: this.layerManager!.getLayers(opts),
802+
views: this.viewManager!.getViews(),
803+
viewports: this.getViewports(opts),
804+
onViewportActive: this.layerManager!.activateViewport,
805+
effects: this.effectManager!.getEffects(),
806+
...opts
807+
});
808+
809+
stats.get(statKey).timeEnd();
810+
811+
return infos;
812+
}
813+
707814
private _pick(
708815
method: 'pickObject',
709816
statKey: string,

modules/react/src/deckgl.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export type DeckGLProps<ViewsT extends ViewOrViews = null> = Omit<
4444

4545
export type DeckGLRef<ViewsT extends ViewOrViews = null> = {
4646
deck?: Deck<ViewsT>;
47+
pickObjectAsync: Deck['pickObjectAsync'];
48+
pickObjectsAsync: Deck['pickObjectsAsync'];
49+
pickMultipleObjectsAsync: Deck['pickMultipleObjectsAsync'];
4750
pickObject: Deck['pickObject'];
4851
pickObjects: Deck['pickObjects'];
4952
pickMultipleObjects: Deck['pickMultipleObjects'];
@@ -57,6 +60,9 @@ function getRefHandles<ViewsT extends ViewOrViews>(
5760
return thisRef.deck;
5861
},
5962
// The following method can only be called after ref is available, by which point deck is defined in useEffect
63+
pickObjectAsync: opts => thisRef.deck!.pickObjectAsync(opts),
64+
pickMultipleObjectsAsync: opts => thisRef.deck!.pickMultipleObjectsAsync(opts),
65+
pickObjectsAsync: opts => thisRef.deck!.pickObjectsAsync(opts),
6066
pickObject: opts => thisRef.deck!.pickObject(opts),
6167
pickMultipleObjects: opts => thisRef.deck!.pickMultipleObjects(opts),
6268
pickObjects: opts => thisRef.deck!.pickObjects(opts)

0 commit comments

Comments
 (0)