Нелинейная интерполяция

Привет, меня зовут Павел. Разберу, что такое нелинейная интерполяция в шрифтах, и как она работает.

Вариативность и кривые

Шрифтовые дизайнеры называют мастером набор символов одного стиля. Например, нормальный и жирный мастер.

Нормальный и жирный мастер

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

Интерполяция

Дизайнеры описывают оси интерполяции — задают мастерам координаты на вариативной оси. Например, оси насыщенности. Так, чтобы получить инстанс, будет достаточно координаты на оси. Например, Weight 600.

Ось интерполяции

По умолчанию интерполяция в вариативных шрифтах линейна. Это значит, что промежуточные точки между мастерами лежат на отрезках:

Линейная интерполяция

Иногда линейной интерполяции недостаточно. Например, для оси апертуры — оси «открытости» знаков. Если в знаке c терминалы двигаются по окружности, то это выглядит естественно.

Апертура в C

Интерполяцию называют нелинейной, когда промежуточные точки лежат на кривых, а не отрезках:

Нелинейная интерполяция

Формат OpenType не поддерживает нелинейную интерполяцию по умолчанию. Чтобы добиться эффекта в шрифтовом файле, нужно использовать трюк.

Рецепт нелинейной интерполяции

Рецепт. Добьемся нелинейной интерполяции в шрифтовом редакторе «из коробки» — без специальных инструментов. Я воспользуюсь Glyphs.

Поместим контур на позиции A, B и С — в разные мастеры. Мастер B скопируем. Зададим оси вариативности как на картинке:

Оси интерполяции

Экспортируем шрифт в .ttf и будем перемещаться вдоль осей одновременно:

Нелинейное движение контура

Контур движется по кривой — интерполяция нелинейна! Почему?

Регионы, дельты и кривые Безье

Прием эксплуатирует особенности OpenType – погрузимся в стандарт.

В OpenType данные о вариативности хранятся в виде контура по умолчанию и дельта-векторов. Например, так выглядят дельта-векторы между жирной и нормальной a:

Дельты в a

Дельта-векторы применяются в «регионах» — подпространствах вариативного пространства. Регионы определяет положение мастеров на вариативных осях.

Так, в рецепте определили три региона:

Регионы в рецепте

Построим дельта-векторы для каждого:

1=BA∂_1 = B − A

2=B.copyA=BA∂_2 = B.copy − A = B - A

3=C(A+1+2)=C(2BA)∂_3 = C - (A + ∂_1 + ∂_2) = C - (2B - A)

Наглядно увидеть как строятся дельта-векторы получится в инструменте Samsa:

Samsa

Чтобы построить инстанс, дельта-векторы масштабируют и суммируют. Это обратный процесс к построению дельт.

Например, чтобы построить инстанс для Weight = 50, Width = 0, к контуру по умолчанию нужно добавить дельта-векторы из региона 1, уменьшив их на 50%:

Как строится интстанс в Samsa

Коэфицент масштабирования дельта-векторов называется скаляром. Скаляры зависит от координат инстанса в вариативном пространстве.

Скаляры рассчитываются по алгоритму стандарта OpenType. Если на вариативной оси нет промежуточных значений — задан только максимум, а минимальное значение в нуле, то скаляр оси равен:

saxiss_{axis} = instanseCoord / axisMax

Полный скаляр дельта-вектора — это произведение скаляров всех осей:

stotal=saxis1×saxis2×...×saxisns_{total} = s_{axis_1} × s_{axis_2} × ... × s_{axis_n}

Построим инстанс. Добавим к значению по умолчанию масштабированные дельта-векторы:

Iwe,wi=A+1×s1+2×s2+3×s3I_{we,wi} = A + ∂_1 × s_1 + ∂_2 × s_2 + ∂_3 × s_3

В пространстве рецепта s1wi=s2we=1s_{1_{wi}} = s_{2_{we}}=1:

Iwe,wi=A+1×s1we+2×s2wi+3×s3we×s3wiI_{we,wi} = A + ∂_1 × s_{1_{we}} + ∂_2 × s_{2_{wi}} + ∂_3 × s_{3_{we}} × s_{3_{wi}}

Для weight = width = tt:

It=A+1t+2t+3t2I_t =A + ∂_1 t + ∂_2 t + ∂_3 t^2

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

Раскроем дельты:

It=t2(C2B+A)+2t(BA)+AI_t= t^2(C - 2B + A) + 2t(B - A) + A

Это форма кривой безье второго порядка c контрольными точками в A, B и C. Поэтому контур двигался по кривой.

Этот трюк можно проворачивать для любого порядка.

Синхронизация осей

Трюк использует несколько осей, которые нужно синхронизировать, чтобы достичь нелинейного эффекта. Мне известны следующие способы синхронизации осей:

  1. Поручить синхронизацию пользователю шрифта. Тогда дизайнеру придётся вручную выставлять одинаковые значения служебных осей.

  2. Поручить синхронизацию внешей программе. Например, в вебе получится использовать JavaScript. Этот способ не подойдет для графических редакторов.

  3. Использовать хак с переопредением осей. Если в шрифтовом файле назначить разным осям одно и то же название таблицах fvar и stat, то значения осей будут меняться синхронно. Способ грубый, но сработает в большинстве программ.

  4. Использовать формат AVAR2. Это новый способ описывать вариативное пространство в шрифтвовом файле, который позволяет синхронзировать оси. AVAR2 ещё не стал стандартом, пока его поддерживают устройства на новых версиях Linux и macOS.

Источники

Студия Underware популяризовала нелинейную интерполяцю статьей Higher Order Interpolation for Variable Fonts.

Фредрик Бреннан продемонстрировал HOI в плагине для FontForge.

Питер Констебл подробно рассказывает как работает HOI в статье Understanding Non-Linear Interpolation in OpenType Variable Fonts

Команда Microsoft поддерживает подробную документацию OpenType Font Variations Overview


Павел Колчанов, cентябрь 2023.

Подписаться на новые статьи