Скругление углов в скрипте для Иллюстратора

31.01.2021

Эта запись в блоге — просто на свою «тригонометрическую память». Ничего особо полезного тут не будет, наверно. Я даже не могу выгрузить этот скрипт, потому что он сильно встроен в экстеншен. Но попробую позже из этого куска кода сделать свой скрипт по скруглению углов и форм.

Задача

В проекте «Генератор дорожных указателей» встречается шаблон с наклонной правой стороной — это указатель направления съезда. В гайде по этому знаку указан угол наклона — 70 градусов. Определив ширину знака по его содержимому, можно без проблем рассчитать точки будущей трапеции. И после этого необходимо скруглить углы трапеции.

Казалось бы — такая простая задача: скруглить углы у многоугольника. Тем более, что производить это надо в таком мощном графическом редакторе, как Иллюстратор. Но проблема кроется в том, что скругления углов нет в скриптах для Адоба Иллюстратора.

Вернее в скриптовании Иллюстратора есть скругление углов, но только при создании прямоугольника. Но так как у нас трапеция, то придётся заниматься этим в коде уже после создания формы.

Для создания знака с наклонной стороной потребовалась именно такая задача. Вариант с созданием прямоугольника и последующим переносом точек не подходит, потому что тогда управляющие точки — «усики» — изменили бы форму углов.

Решение

Задача по шаблону состояла из 2 частей: рассчитать точки трапеции и потом уже скруглить углы.

Инструмент должен быть гибким, поэтому я принял наклон в 70 градусов за переменную. Если вдруг дизайнер поменяет гайд, система должна работать и при 70, и при 72 и даже при 89, если вдруг такое понадобится. Значение угла наклона я вынес в параметр элемента вёрстки:

layout-item

Трапеция

Рассчитать трапецию — простая геометрическая задача. Зная высоту и ширину знака, мы просто смещаем нижнюю точку на нужный угол. Единственная сложность была — это то, что правую сторону нужно считать от наклонённой стрелки. Но это тоже небольшая тригонометрическая проблема, тем более при скриптовании в Иллюстраторе объект (стрелка) после поворота можно сбросить прямоугольник трансформации — то есть можно вычислить линию правого края трапеции.

Скругление

Конечно, в интернете есть скрипты, которые отлично скругляют углы. Тем более, что это очень простая форма — трапеция. Но не хотелось запихивать в экстеншен огромный скрипт для Иллюстратора. Поэтому решил сделать свой мини-скрипт.

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

Я принципиально не хотел лезть в параметрические уравнения (которые описывают кривые Безье), потому что для этой задачи был уверен, что это не нужно. Так оно и вышло. Я взял один случай (радиус и угол из шаблона), обсчитал его и ввёл константы для моего диапазона. «Тригонометрия» получилась такая:

trigo-1
trigo-2

К сожалению не смог найти бумажку, на которой все эти отрезки подписаны: dD, dY и так далее.

Потом вывел значения отрезков. Получилась такая «кухня»:

    var
      dD = cornerRadius / Math.sin(ANGLE_RAD),
      dY = cornerRadius * Math.sin(ANGLE_RAD),
      dH = cornerRadius * Math.cos(ANGLE_RAD),
      dAY = cornerRadius + dH,
      dCX = dAY * Math.tan(ANTI_ANGLE_RAD),
      dKX = cornerRadius * Math.sin(ANGLE_RAD),
      dAX = dCX + dKX,

      dXC2 = cornerRadius * Math.tan(ANTI_ANGLE_RAD),
      dAX2 = cornerRadius / Math.sin(ANGLE_RAD),
      dDK2 = cornerRadius * Math.tan(ANTI_ANGLE_RAD),
      dYK2 = dDK2 * Math.sin(ANGLE_RAD),
      dYC2 = cornerRadius - dYK2,
      dXK2 = dDK2 * Math.cos(ANGLE_RAD),
      dXCH2 = dAX2 - dXC2

И вот у нас 8 точек будущей трапеции:

    const pathPoints = [
      // 0
      [
        this.point[0] + cornerRadius,
        this.point[1],
      ],
      // 1
      [
        this.point[0] + width - dAX,
        this.point[1],
      ],
      // 2
      [
        this.point[0] + width - dCX,
        this.point[1] - dAY,
      ],
      // 3
      [
        this.point[0] + width - dWidth + (dXC2 - dXK2),
        this.point[1] - height + dYC2,
      ],
      // 4
      [
        this.point[0] + width - dWidth - dXCH2,
        this.point[1] - height,
      ],
      // 5
      [
        this.point[0] + cornerRadius,
        this.point[1] - height,
      ],
      // 6
      [
        this.point[0],
        this.point[1] - height + cornerRadius,
      ],
      // 7
      [
        this.point[0],
        this.point[1] - cornerRadius,
      ],
    ];

Для расчёта координат управляющих точек (усиков) уже пришлось прибегать к константам. В процессе расчета выяснилось, что там всего 2 коэффициента: для острого и тупого угла. После проверки на разных радиусах и углах я убедился, что точность высокая и задача решена. Я не буду тут показывать этот код, потому что это не имеет смысла.

Результат

В результате получился инструмент, готовый к изменениям шаблонов: и по углу наклона, и по радиусу скругления. В диапазоне от 0 до 40 градусов от вертикали получается отличный результат — этого хватает с запасом на возможные изменения. При 50 градусах появляется артефакт в остром угле (зелёным отмечен целевой контур), а при 60 — уже совсем вытягивается верх и «тупится» низ:

artefact-1
artefact-2

Так как угол наклона вынесен в параметры, можно экспериментировать. И оказалось, что можно наклонять сторону знака и в другую сторону. Проверка тоже прошла удачно — скрипт отлично наклонил сторону в другую сторону. Искажения заметны стали так же на 50º от вертикали:

minus-1
minus-2

И под конец — несколько просто красивых картинок и скрины на память:

end-1
process-1
process-2