Brush Extension

Назначение расширения Brush

Brush Extension в Deck.gl предназначен для реализации интерактивного выделения географических или экранных областей с помощью прямоугольной «кисти». Это расширение используется поверх различных слоёв (обычно GeoJsonLayer, ScatterplotLayer, GridLayer, HexagonLayer) и добавляет возможность динамического выбора подмножества данных на основе пересечения с областью выделения.

Ключевая идея заключается в том, что Brush Extension не является самостоятельным слоем — он модифицирует поведение существующего слоя, добавляя:

  • обработку координат курсора
  • вычисление прямоугольной области выделения
  • фильтрацию объектов по попаданию в область
  • передачу состояния выделения в шейдер или на уровень JavaScript

Архитектура работы Brush Extension

Brush Extension работает на двух уровнях: CPU и GPU.

CPU-часть:

  • отслеживание событий мыши
  • вычисление координат прямоугольника
  • преобразование координат экрана в координаты слоя
  • фильтрация данных (при необходимости)

GPU-часть:

  • передача параметров brush-зоны в uniform-переменные
  • отбрасывание объектов, не попадающих в область
  • подсветка выбранных элементов

Основная оптимизация заключается в том, что часть вычислений переносится в шейдер, что позволяет обрабатывать большие наборы данных без деградации производительности.


Подключение Brush Extension

Brush Extension подключается как расширение слоя через свойство extensions.

import {ScatterplotLayer} from '@deck.gl/layers';
import {BrushExtension} from '@deck.gl/extensions';

const layer = new ScatterplotLayer({
  id: 'points',
  data: points,
  getPosition: d => d.coordinates,
  getRadius: 50,
  getFillColor: [255, 140, 0],

  extensions: [new BrushExtension()]
});

После подключения слой получает дополнительные параметры управления brush-выделением.


Основные параметры Brush Extension

Brush Extension принимает конфигурацию, которая управляет поведением выделения.

getBrushRadius

Определяет радиус «переходной зоны» вокруг кисти. Используется для создания мягких границ выделения.

new BrushExtension({
  getBrushRadius: 100
})

brushEnabled

Включает или отключает функциональность brush-выделения.

  • true — активное выделение
  • false — слой работает без расширения

brushTarget

Определяет, к чему применяется кисть:

  • source — используется источник данных слоя
  • target — применяется к результату трансформации слоя

Событийная модель взаимодействия

Brush Extension не имеет отдельного event API, но интегрируется через стандартные события Deck.gl и обновление props слоя.

Основные механизмы:

  • изменение координат курсора → обновление bounding box
  • обновление состояния слоя → пересчёт видимости объектов
  • rerender слоя при изменении brush-области

Типичный цикл взаимодействия:

  1. пользователь зажимает кнопку мыши
  2. фиксируется стартовая точка прямоугольника
  3. при движении мыши обновляется текущая граница
  4. слой получает обновлённые параметры
  5. происходит перерасчёт попадания объектов

Математическая модель brush-выделения

Brush-область представляет собой прямоугольник в экранных координатах:

x_{min} x x_{max},y_{min} y y_{max}

Каждый объект слоя проверяется на принадлежность:

  • если координаты объекта попадают в диапазон — объект считается выбранным
  • иначе — исключается из selection set

В случае использования географических координат добавляется проекция:

(x, y) = P_{mercator}(, )

где (P_{mercator}) — функция Web Mercator проекции.


Интеграция с shader-моделью Deck.gl

Brush Extension модифицирует uniforms, передаваемые в GPU:

  • brushBoundingBox — координаты прямоугольника
  • brushActive — флаг активности
  • brushRadius — радиус мягкой зоны

В вершинном или фрагментном шейдере выполняется проверка:

if (position.x < brushMinX || position.x > brushMaxX ||
    position.y < brushMinY || position.y > brushMaxY) {
  discard;
}

Для плавного выделения используется интерполяция расстояния до границы brush-зоны.


Использование с ScatterplotLayer

ScatterplotLayer является наиболее частым кандидатом для применения Brush Extension, поскольку работает с точечными данными.

new ScatterplotLayer({
  id: 'scatter',
  data,
  getPosition: d => d.position,
  getRadius: 20,
  getFillColor: d => d.color,

  extensions: [new BrushExtension()],
  brushEnabled: true
});

При этом каждая точка проходит проверку принадлежности brush-области.


Использование с HexagonLayer

В случае агрегированных слоёв Brush Extension применяется к уже агрегированным ячейкам.

Особенность:

  • проверяется не точка, а центр гексагона
  • возможна частичная подсветка ячеек
new HexagonLayer({
  id: 'hex',
  data,
  getPosition: d => d.position,
  radius: 1000,
  extensions: [new BrushExtension()]
});

Производительность и оптимизация

Основные факторы влияния на производительность:

  • количество объектов в слое
  • частота обновления brush-области
  • сложность shader-логики

Оптимизационные стратегии:

  • перенос фильтрации в GPU
  • кэширование bounding box
  • использование spatial indexing (R-tree)
  • минимизация rerender через shouldUpdateState

Ограничения Brush Extension

  • не поддерживает произвольные формы (только прямоугольник)
  • ограниченная точность при проекциях на больших масштабах
  • возможны артефакты при быстром движении курсора
  • зависит от качества реализации слоя-основы

Взаимодействие с другими расширениями

Brush Extension может комбинироваться с другими расширениями Deck.gl:

  • DataFilterExtension — для дополнительной фильтрации после brush-выделения
  • ElevationScaleExtension — для визуального выделения высоты выбранных объектов
  • LightingEffect — для усиления визуального контраста выделенной области

При комбинировании важно учитывать порядок применения расширений, так как он влияет на финальный результат рендеринга.


Поведение при зуме и трансформациях

Brush Extension работает в экранных координатах, поэтому при изменении:

  • масштаба карты
  • поворота
  • наклона (pitch)

координаты brush автоматически пересчитываются через матрицы преобразования Deck.gl.

Базовая формула трансформации:

p’ = M_{view} M_{proj} p

где:

  • (M_{view}) — матрица вида
  • (M_{proj}) — проекционная матрица
    1. — исходная точка

Практическая модель состояния brush

Состояние brush обычно хранится в следующей структуре:

{
  brushStart: [x0, y0],
  brushEnd: [x1, y1],
  isBrushing: true,
  bounds: {
    xMin: Math.min(x0, x1),
    xMax: Math.max(x0, x1),
    yMin: Math.min(y0, y1),
    yMax: Math.max(y0, y1)
  }
}

Это состояние используется как для визуализации рамки, так и для фильтрации данных.


Применение в аналитических интерфейсах

Brush Extension часто используется в задачах пространственной аналитики:

  • выделение кластеров точек
  • сравнение плотности распределений
  • интерактивный фильтр больших наборов геоданных
  • подготовка выборок для дальнейшей обработки

Поведение остаётся стабильным даже при работе с десятками тысяч объектов благодаря GPU-ускорению.