Skip to content

Commit 3f393d5

Browse files
committed
feat(core,react,docs): Add async deck picking methods
1 parent e305b71 commit 3f393d5

File tree

6 files changed

+206
-12
lines changed

6 files changed

+206
-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
@@ -105,12 +105,12 @@ export default class DeckPicker {
105105
}
106106

107107
/** Pick the closest info at given coordinate @deprecated WebGL only */
108-
pickObject(opts: PickByPointOptions & PickOperationContext) {
108+
_pickObject(opts: PickByPointOptions & PickOperationContext) {
109109
return this._pickClosestObject(opts);
110110
}
111111

112112
/** Get all unique infos within a bounding box */
113-
pickObjects(opts: PickByRectOptions & PickOperationContext) {
113+
_pickObjects(opts: PickByRectOptions & PickOperationContext) {
114114
return this._pickVisibleObjects(opts);
115115
}
116116

modules/core/src/lib/deck.ts

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,65 @@ 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+
*/
652+
async pickObjectsAsync(opts: {
653+
/** Left of the bounding box in pixels */
654+
x: number;
655+
/** Top of the bounding box in pixels */
656+
y: number;
657+
/** Width of the bounding box in pixels. Default `1` */
658+
width?: number;
659+
/** Height of the bounding box in pixels. Default `1` */
660+
height?: number;
661+
/** A list of layer ids to query from. If not specified, then all pickable and visible layers are queried. */
662+
layerIds?: string[];
663+
/** If specified, limits the number of objects that can be returned. */
664+
maxObjects?: number | null;
665+
}): Promise<PickingInfo[]> {
666+
return await this._pickAsync('pickObjectsAsync', 'pickObjects Time', opts);
667+
}
668+
669+
/**
670+
* Query the object rendered on top at a given point
671+
* @deprecated WebGL only. Use `pickObjectsAsync` instead
672+
*/
614673
pickObject(opts: {
615674
/** x position in pixels */
616675
x: number;
@@ -627,7 +686,10 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
627686
return infos.length ? infos[0] : null;
628687
}
629688

630-
/* Query all rendered objects at a given point */
689+
/**
690+
* Query all rendered objects at a given point
691+
* @deprecated WebGL only. Use `pickObjectsAsync` instead
692+
*/
631693
pickMultipleObjects(opts: {
632694
/** x position in pixels */
633695
x: number;
@@ -646,7 +708,10 @@ export default class Deck<ViewsT extends ViewOrViews = null> {
646708
return this._pick('pickObject', 'pickMultipleObjects Time', opts).result;
647709
}
648710

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

705770
// Private Methods
706771

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