Инверсия масштаба: .invert()

Инверсия масштаба в D3.js реализуется методом .invert(), который выполняет обратное преобразование по отношению к прямому отображению значений через масштаб. Если обычная функция масштаба переводит значения из области определения (domain) в диапазон отображения (range), то инверсия возвращает исходное значение домена по заданному значению диапазона.

Масштабы в D3 строятся вокруг двух ключевых понятий:

  • domain — исходный набор значений данных
  • range — визуальное пространство (например, пиксели на SVG)

Прямое преобразование:

scale(x) → y

Инверсия:

scale.invert(y) → x

Это свойство делает возможным двустороннюю связь между данными и визуальным представлением.

Поддержка invert в различных типах шкал

Метод .invert() доступен не во всех масштабах D3.

Поддерживаемые непрерывные шкалы:

  • d3.scaleLinear
  • d3.scaleTime
  • d3.scaleLog
  • d3.scalePow
  • d3.scaleSqrt
  • d3.scaleIdentity

Неподдерживаемые:

  • d3.scaleOrdinal
  • d3.scaleBand
  • d3.scalePoint

Причина заключается в природе отображения: дискретные шкалы не имеют однозначного обратного соответствия.

Математическая основа линейной инверсии

Для линейного масштаба преобразование задаётся формулой:

genui{“math_block_widget_always_prefetch_v2”:{“content”:“y = a x + b”}}

Инверсия вычисляется как:

x =

Где коэффициенты a и b определяются на основе domain и range.

Если задано:

  • domain = [d0, d1]
  • range = [r0, r1]

то:

a =

и:

b = r0 - a d0

Метод .invert() использует эти зависимости в обратном направлении для вычисления исходного значения.

Базовое использование invert

Типичный пример линейного масштаба:

const scale = d3.scaleLinear()
  .domain([0, 100])
  .range([0, 500]);

scale(50); 
// 250

scale.invert(250);
// 50

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

Инверсия в обработке событий

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

Координаты события находятся в пикселях, а данные — в domain масштаба:

const xScale = d3.scaleLinear()
  .domain([0, 100])
  .range([0, 600]);

svg.on("click", function(event) {
  const [x] = d3.pointer(event);
  const value = xScale.invert(x);

  console.log(value);
});

Здесь происходит преобразование:

  1. Получение пиксельной координаты
  2. Обратное отображение в значение данных

Инверсия временных шкал

Для временных данных используется scaleTime, где domain состоит из объектов Date.

const timeScale = d3.scaleTime()
  .domain([new Date(2020, 0, 1), new Date(2020, 11, 31)])
  .range([0, 1000]);

const date = timeScale.invert(500);

Результат — конкретная дата, соответствующая середине диапазона визуализации.

Ограничения точности

Инверсия не всегда даёт идеально точные значения из-за особенностей чисел с плавающей точкой и округления пикселей.

Типичные источники погрешности:

  • преобразование координат SVG
  • дробные значения масштаба
  • интерполяция между целыми пикселями

Пример:

const scale = d3.scaleLinear()
  .domain([0, 3])
  .range([0, 10]);

scale.invert(3.3333333);

Результат может слегка отличаться от ожидаемого математического значения.

Инверсия и логарифмические шкалы

В логарифмических шкалах используется обратная экспоненциальная функция.

Прямое отображение:

genui{“math_block_widget_always_prefetch_v2”:{“content”:“y = _a(x)“}}

Инверсия:

x = a^y

Пример D3:

const logScale = d3.scaleLog()
  .domain([1, 1000])
  .range([0, 300]);

logScale.invert(150);

Значение возвращается в исходную экспоненциальную область.

Поведение при clamping

Если у шкалы включено ограничение значений:

scale.clamp(true)

инверсия всё равно выполняется по математической модели, но поведение прямого преобразования ограничивается диапазоном.

Это означает:

  • scale.invert() не учитывает clamp напрямую
  • результат может выходить за пределы domain, если вход был за range

Использование с осью координат

При построении графиков инверсия используется для синхронизации визуальных элементов и данных:

const x = d3.scaleLinear()
  .domain([0, 100])
  .range([0, width]);

const y = d3.scaleLinear()
  .domain([0, 100])
  .range([height, 0]);

svg.on("mousemove", (event) => {
  const [px, py] = d3.pointer(event);

  const dataX = x.invert(px);
  const dataY = y.invert(py);
});

Инверсия позволяет получать координаты в системе данных, а не в системе пикселей.

Инверсия и непрерывность шкалы

Работа .invert() основана на предположении непрерывности:

  • любое значение range соответствует одному значению domain
  • функция монотонна

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

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

Инверсия масштаба активно используется в задачах:

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

Пример brush:

brush.on("brush", (event) => {
  const selection = event.selection;
  const start = xScale.invert(selection[0]);
  const end = xScale.invert(selection[1]);
});

Здесь инверсия превращает пиксельный диапазон обратно в диапазон данных.

Особенности поведения при изменении domain и range

Изменение параметров масштаба автоматически изменяет результат .invert().

xScale.domain([0, 200]);
xScale.invert(250); // изменится логика отображения

Инверсия всегда строго зависит от текущего состояния масштаба, а не от исходной конфигурации.

Связь invert с внутренней реализацией D3

Внутри D3 шкалы представляют собой функции с дополнительными методами:

  • scale(value) — прямое отображение
  • scale.invert(value) — обратное отображение
  • scale.domain() / scale.range() — конфигурация

Инверсия реализуется как математически обратное преобразование интерполятора, используемого для range.


Метод .invert() является ключевым механизмом двунаправленного взаимодействия между данными и визуализацией, обеспечивая возможность восстановления исходных значений из экранных координат без дополнительного хранения состояния преобразования.