Advanced geometry processing capabilities including rectangular clipping, geofence-based masking, and collision detection for controlling object visibility and preventing overlapping elements.
Adds support for clipping rendered layers by rectangular bounds, allowing layers to show only objects within specified screen or world coordinates.
/**
* Adds support for clipping rendered layers by rectangular bounds
* Objects outside the clip bounds are not rendered
*/
class ClipExtension extends LayerExtension {
static extensionName: 'ClipExtension';
static defaultProps: ClipExtensionDefaultProps;
constructor();
getShaders(): any;
draw(): void;
}
interface ClipExtensionProps {
/**
* The clipping bounds [minX, minY, maxX, maxY].
* Values are in the coordinate system determined by clipByInstance.
* @default [0, 0, 1, 1]
*/
clipBounds?: [number, number, number, number];
/**
* If true, clip bounds are applied per instance (object-level clipping).
* If false, clip bounds are applied in screen/viewport coordinates.
* If undefined, automatically detected based on layer type.
*/
clipByInstance?: boolean;
}
interface ClipExtensionDefaultProps {
clipBounds: [number, number, number, number];
clipByInstance: undefined;
}
interface ClipModuleProps {
bounds: [number, number, number, number];
}Allows layers to show/hide objects by a geofence, providing geographic boundary-based visibility control.
/**
* Allows layers to show/hide objects by a geofence
* Objects outside the mask geometry are hidden
*/
class MaskExtension extends LayerExtension {
static extensionName: 'MaskExtension';
static defaultProps: MaskExtensionDefaultProps;
constructor();
initializeState(): void;
getShaders(): any;
draw(params: any): void;
}
interface MaskExtensionProps {
/**
* The ID of the mask. References a mask defined elsewhere in the deck instance.
* @default ''
*/
maskId?: string;
/**
* If true, mask is applied per instance (object-level masking).
* If false, mask is applied in screen/viewport coordinates.
* If undefined, automatically detected based on layer type.
*/
maskByInstance?: boolean;
/**
* If true, inverts the mask (shows objects outside the mask).
* @default false
*/
maskInverted?: boolean;
}
interface MaskExtensionDefaultProps {
maskId: string;
maskByInstance: undefined;
maskInverted: boolean;
}Allows layers to hide overlapping objects based on priority and collision detection, useful for label placement and avoiding visual clutter.
/**
* Allows layers to hide overlapping objects
* Objects with lower priority are hidden when they collide with higher priority objects
*/
class CollisionFilterExtension extends LayerExtension {
static extensionName: 'CollisionFilterExtension';
static defaultProps: CollisionFilterExtensionDefaultProps;
constructor();
getShaders(): any;
draw(params: any): void;
initializeState(context: LayerContext, extension: this): void;
getNeedsPickingBuffer(): boolean;
}
interface CollisionFilterExtensionProps<DataT = any> {
/**
* Accessor for collision priority of each object.
* Higher priority objects will be shown when collision occurs.
* @default {type: 'accessor', value: 0}
*/
getCollisionPriority?: Accessor<DataT, number>;
/**
* Enable/disable collision filtering.
* @default true
*/
collisionEnabled?: boolean;
/**
* Collision group identifier. Objects only collide within the same group.
* @default 'default'
*/
collisionGroup?: string;
/**
* Additional properties passed to collision test.
* @default {}
*/
collisionTestProps?: {};
}
interface CollisionFilterExtensionDefaultProps {
getCollisionPriority: {type: 'accessor', value: 0};
collisionEnabled: true;
collisionGroup: {type: 'string', value: 'default'};
collisionTestProps: {};
}Usage Examples:
import { ScatterplotLayer, TextLayer, PolygonLayer } from "@deck.gl/layers";
import { ClipExtension, MaskExtension, CollisionFilterExtension } from "@deck.gl/extensions";
// Rectangular clipping by screen coordinates
const clippedLayer = new ScatterplotLayer({
id: "clipped-points",
data: pointData,
extensions: [new ClipExtension()],
getPosition: d => d.coordinates,
clipBounds: [100, 100, 500, 400], // Screen coordinates [minX, minY, maxX, maxY]
clipByInstance: false, // Clip by screen coordinates
getRadius: 10
});
// Clipping by world coordinates per instance
const instanceClippedLayer = new ScatterplotLayer({
id: "instance-clipped",
data: pointData,
extensions: [new ClipExtension()],
getPosition: d => d.coordinates,
clipBounds: [-122.5, 37.7, -122.3, 37.8], // World coordinates (longitude, latitude)
clipByInstance: true, // Clip by world coordinates
getRadius: 50
});
// Geofence masking
const maskedLayer = new ScatterplotLayer({
id: "masked-points",
data: pointData,
extensions: [new MaskExtension()],
getPosition: d => d.coordinates,
maskId: "city-boundary", // References a mask defined in deck instance
maskByInstance: true,
maskInverted: false, // Show objects inside the mask
getRadius: 20
});
// Inverted masking (show objects outside the mask)
const invertedMaskLayer = new ScatterplotLayer({
id: "outside-mask",
data: pointData,
extensions: [new MaskExtension()],
getPosition: d => d.coordinates,
maskId: "restricted-area",
maskInverted: true, // Show objects outside the restricted area
getRadius: 15
});
// Collision detection for labels
const labelLayer = new TextLayer({
id: "collision-labels",
data: labelData,
extensions: [new CollisionFilterExtension()],
getPosition: d => d.coordinates,
getText: d => d.label,
getCollisionPriority: d => d.importance, // Higher importance labels win
collisionEnabled: true,
collisionGroup: "labels", // Only collide with other labels
getSize: 16
});
// Collision groups for different object types
const primaryLabels = new TextLayer({
id: "primary-labels",
data: primaryLabelData,
extensions: [new CollisionFilterExtension()],
getPosition: d => d.coordinates,
getText: d => d.text,
getCollisionPriority: d => 100, // High priority
collisionGroup: "primary",
getSize: 18
});
const secondaryLabels = new TextLayer({
id: "secondary-labels",
data: secondaryLabelData,
extensions: [new CollisionFilterExtension()],
getPosition: d => d.coordinates,
getText: d => d.text,
getCollisionPriority: d => 50, // Lower priority
collisionGroup: "primary", // Same group - will collide with primary
getSize: 14
});// Update clipping bounds based on viewport or interaction
const dynamicClipLayer = new ScatterplotLayer({
extensions: [new ClipExtension()],
clipBounds: getDynamicBounds(), // Function returning current bounds
clipByInstance: false
});
function getDynamicBounds() {
const viewport = deck.viewManager.views[0];
return [
viewport.x,
viewport.y,
viewport.x + viewport.width,
viewport.y + viewport.height
];
}// Define complex mask in deck instance
const deckInstance = new Deck({
// ... other config
layers: [maskedLayer],
masks: [
{
id: 'complex-boundary',
geometry: {
type: 'Polygon',
coordinates: [/* complex polygon coordinates */]
}
}
]
});
const maskedLayer = new PolygonLayer({
extensions: [new MaskExtension()],
maskId: 'complex-boundary',
maskByInstance: true
});// Different collision priorities for different data types
const mixedCollisionLayer = new TextLayer({
extensions: [new CollisionFilterExtension()],
getCollisionPriority: d => {
switch(d.type) {
case 'city': return 100; // Highest priority
case 'landmark': return 75; // High priority
case 'street': return 50; // Medium priority
case 'poi': return 25; // Low priority
default: return 0; // Lowest priority
}
},
collisionEnabled: true
});All geometry operations extensions inject custom shader code: