Документация Engee

Конвейер конвертации, преобразования и проекции

Страница в процессе перевода.

В этом разделе описываются все этапы обработки данных графика перед его отображением.

Обзор

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

  1. Conversions which mainly normalize types

    1. expand_dimensions() adds defaulted/generated data (e.g. x, y in image())

    2. dim_convert processes special types like Units

    3. convert_arguments() normalizes numeric types & data formats

  2. Transformations which transform data on a per-plot basis

    1. transform_func is a function applied to data

    2. model matrix applies linear transformations

  3. Projections which project data from one coordinate system to another

    1. view matrix moves data from "world" space to a camera "view/eye" space

    2. projection matrix moves from the camera space to "clip" space

    3. viewport moves "clip" space to "pixel/screen" space

Пользователь может напрямую контролировать матрицу model (1.2) с помощью функций scale!(), translate!() и rotate!(). Проекции (3) можно контролировать косвенно с помощью атрибута space. Он задает систему координат, которая используется в качестве исходного пространства, и корректирует проекции соответствующим образом. Вы также можете косвенно контролировать функцию transform_func, которую можно задать, передав Transformation() непосредственно в график. Однако обычно она наследуется и контролируется объектом Axis.

Разработчик, расширяющий функционал Makie, может взаимодействовать с большинством этих шагов. Скорее всего, расширение будет производиться с помощью convert_arguments() для построения графиков особых типов. Но вы также можете реализовать больше dim_converts, добавить методы для expand_dimensions(), реализовать дополнительные функции преобразования или добавить камеру, которая создает собственную матрицуviewиprojection. Задаются только обработкаmodelиviewport, а также интерпретацияspace.

Конвертации аргументов

При вызове функции построения графика, например scatter!(axis_or_scene, args...), создается объект графика. Он отслеживает исходные входные аргументы как наблюдаемые объекты в plot.args. Затем эти входные аргументы конвертируются конвейером конвертации и сохраняются в plot.converted. Как уже упоминалось выше, конвейер состоит из трех этапов.

Генерирование данных

Первый шаг — генерирование недостающих данных. Например, для создания графика-изображения достаточно вызвать image(rand(10, 10)). Однако наиболее общая форма также включает ClosedInterval для измерений x и y с объявлением размера изображения. Эти данные генерируются вызовом expand_dimensions(::Trait, args...), где Trait = conversion_trait(::PlotType, args...).

Обработка особых типов

На втором этапе обрабатываются особые типы, такие как Unitful, Dates или категориальные значения, которые необходимо синхронизировать в пределах сцены. Например, если на одном графике в качестве единицы измерения для значений x используются часы, на других графиках также необходимо использовать для x единицы измерения времени. Если масштаб единиц измерения на графиках разный, например на одном используются часы, а на другом — минуты, то необходимо найти общую единицу измерения и соответствующим образом масштабировать значения. Именно этим занимается dim_converts. Дополнительные сведения можно найти в разделе документации Преобразования размеров.

Конвертация аргументов

Последний и главный этап конвейера конвертации — функция convert_arguments(). Ее цель — конвертировать различные типы данных и макеты в один выбранный формат или несколько. Например, любые данные, передаваемые в scatter(), конвертируются в Vector{Point{D, T}}, где D = 2 или 3, а T = Float32 или Float64. Эти конвертации могут происходить на основе типа графика или его типажа конвертации. Для scatter() используется типаж конвертации PointBased.

convert_arguments() также может принимать именованные аргументы, полученные из атрибутов графика. Для этого атрибут необходимо пометить с помощью used_attribute(::PlotType) = (names...). Любое имя в этом списке будет удалено из атрибутов итогового графика и передано вместо этого в convert_arguments().

Чтобы построить график собственного пользовательского типа, может потребоваться расширить convert_arguments(). Допустим, имеется некий пользовательский тип MySimulation и некая функция positions(::MySimulation), возвращающая позиции, которые следует построить при вызове scatter(::MySimulation). В этом случае можно определить

function Makie.convert_arguments(PT::PointBased, sim::MySimulation)
    return Makie.convert_arguments(PT, positions(sim))
end

чтобы это было возможно. В качестве первого аргумента можно также использовать тип графика (например, Scatter).

Преобразования

После нормализации типа и макета данных графика путем конвертации их можно скорректировать посредством преобразований. Они обрабатываются объектом Transformation, который существует как на уровне графика, так и на уровне сцены в поле transformation. Этот объект содержит похожий на функцию объект transform_func, применяемый к данным, и матрицу model, которая выполняет линейные преобразования данных.

Объект Transformation графика может наследоваться от родительской сцены или графика, если он действует в том же пространстве space. Для transform_func это означает использование той же функции, что и у родителя. Для model это означает слияние родительской матрицы model с локальной следующим образом: plot.model = parent.model * local_model.

Функция преобразования

Функция преобразования или transform_func является частью объекта Transformation. Она выполняет нелинейные преобразования, такие как логарифмическое преобразование оси.

Каждая функция transform_func реализует по крайней мере

Makie.apply_transform(transform_func, arg::VecTypes{N, T}) where {N, T}

где функция преобразования может быть представлена любым типом, а не только Function. Это позволяет ей нести вспомогательную информацию, которая может быть важна для преобразования. Кроме того, для более эффективного применения transform_func можно реализовать методы с другими типами arg, такими как числовые Vector.

Обычно transform_func также реализует

Makie.inverse_transform(transform_func)
Makie.apply_transform(transform_func, arg::Rect3)

Обратная функция позволяет преобразовывать данные обратно, что используется, например, в пределах Axis. Если существует разумное обратное преобразование (даже если оно неполное или неоднозначное), оно должно быть задано в inverse_transform. Другой метод apply_transform используется в boundingbox() и по умолчанию преобразует углы ограничивающего прямоугольника. Он должен быть реализован, если метод по умолчанию возвращает неверные результаты, например при полярном преобразовании.

Преобразования моделей

Модельная матрица отвечает за линейные преобразования данных графика. Сюда входят масштабирование с помощью scale!(), перемещение с помощью translate!() и поворот с помощью rotate!(). Дополнительные сведения см. в разделе справки Преобразование.

Проекции

Проекции — это матричные преобразования, которые переносят данные из одной системы координат (пространства) в другую. Матрицы являются частью camera(scene) и управляются либо cameracontrols(scene), либо родительским объектом Block. График не может изменять эти матрицы, но может контролировать то, какие из них будут использоваться, с помощью атрибута space.

Контроллер камеры

Контроллер камеры, будь то объект в сцене или родительский блок, генерирует матрицы view и projection. Эти матрицы хранятся в объекте camera(scene), который также объединяет их в projectionview = projection * view. Матрица view переносит данные графика из «мировой» системы координат в центрированную и ориентированную относительно наблюдателя систему. Эту систему координат обычно называют пространством камеры, наблюдателя или просмотра. Из него матрица projection может применять перспективную проекцию и масштабирует данные для перемещения в пространство отсечения. Это нормализованное пространство, где все, что находится за пределами прямоугольника --1.. 1, обрезается. Окончательная проекция в пиксельное или экранное пространство выполняется неявно с помощью API Graphics или явно в CairoMakie на основе viewport(scene). Объект camera(scene) также содержит матрицу pixel_space, которая преобразует пиксельное пространство в пространство отсечения, разрешение сцены, а также некоторую вспомогательную информацию об ориентации камеры. Обратите внимание, что контроллер камеры часто называют камерой, поскольку camera(scene) — это просто неактивное хранилище.

Атрибут пространства

Атрибут space определяет то, какие матрицы проекций использует график. Варианты относятся к входному пространству, которое обычно преобразуется в пространство отсечения. Возможные варианты:

  • space = :data: применяет матрицы view и projection камеры. (В API Graphics это обычно называется мировым пространством.)

  • space = :pixel: применяет матрицу pixel_space камеры.

  • space = :clip: применяет матрицу тождественности.

  • space = :relative: применяет константную матрицу перемещения и масштабирования.

Обратите внимание, что все это действует после преобразований, то есть после применения матрицы model.

Атрибут пространства маркеров

У некоторых графиков есть атрибут markerspace. Для них приведенные выше проекции делятся на два этапа: от space к markerspace, а затем к пространству отсечения. Применимы те же варианты, что и выше. При необходимости некоторые из этих матриц также можно инвертировать (например, для перехода между пространствами :pixel -> :data -> :clip).

Float32Convert

Float32Convert — это необязательный шаг, который не так четко вписывается в конвейер. Его задача — гарантировать, что данные, матрицы проекций и модельную матрицу можно безопасно конвертировать в типы Float32 и таким образом передать в графический API без проблем с точностью.

В настоящее время это преобразование фактически определено только для Axis. При вызове plot!(axis, ...) data_limits() графика регистрируются осью и объединяются с существующими пределами. Пределы в конечном итоге инициируют обновление камеры, в ходе которого применяется transform_func. Результат передается во Float32Convert, который обновляет линейное преобразование так, чтобы преобразованные пределы были безопасными для типа Float32. Затем из безопасных пределов выводятся проекционные матрицы. В этот момент линейное преобразование Float32Convert существует непосредственно перед view. Если возможно, оно переставляется с model, чтобы модельную матрицу можно было обработать в графическом API, то есть на GPU.