or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

brushing.mddata-filtering.mdgeometry-operations.mdindex.mdlegacy-experimental.mdstyling.md
tile.json

geometry-operations.mddocs/

Geometry Operations

Advanced geometry processing capabilities including rectangular clipping, geofence-based masking, and collision detection for controlling object visibility and preventing overlapping elements.

Capabilities

ClipExtension

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];
}

MaskExtension

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;
}

CollisionFilterExtension

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
});

Advanced Geometry Operations

Dynamic Clipping Bounds

// 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
  ];
}

Complex Mask Geometries

// 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
});

Priority-Based Collision Resolution

// 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
});

Performance Considerations

ClipExtension

  • Clipping is performed in shaders for optimal GPU performance
  • Screen-space clipping is faster than world-space clipping
  • Minimal performance impact for most layer types

MaskExtension

  • Requires a picking buffer for mask geometry rasterization
  • Performance scales with mask geometry complexity
  • Best suited for relatively simple mask shapes

CollisionFilterExtension

  • Requires picking buffer for collision detection
  • Performance scales with object density and overlap
  • Uses spatial indexing for efficient collision queries
  • Can significantly improve visual clarity at the cost of some performance

Shader Integration

All geometry operations extensions inject custom shader code:

  • ClipExtension: Adds clipping tests in vertex or fragment shaders
  • MaskExtension: Samples mask textures for visibility testing
  • CollisionFilterExtension: Uses collision priority buffers for visibility decisions

Compatibility

  • All geometry extensions work with layers that support the extension system
  • Can be combined with other extensions (filtering, styling, etc.)
  • Support both WebGL and WebGPU rendering backends
  • ClipExtension: Works with all layer types
  • MaskExtension: Requires mask definitions in deck instance
  • CollisionFilterExtension: Most effective with point-based layers (TextLayer, IconLayer, ScatterplotLayer)