En

Quil Middleware

В Quil 2.1.0 была добавлена поддержка middleware (я не знаю русского аналога этого термина, так что буду писать его по-английски). Middleware позволяет расширять скетч без раздувания собственно самого кода. Идея схожа с идеей middleware в ring: middleware - это обычная кложурная функция, которая принимает мапу с параметрами скетча и должна вернуть обновлённую мапу. Что мы можем делать в middleware? В принципе, всё, что душе угодно: можно изменять функции заданные пользователем, такие как draw, setup, mouse-pressed и другие. В качестве примера мы создадим middleware, которое будет вращать скетч относительно центра.

Вращательное middleware

Работа у нашего middleware довольно проста: нужно заменить определённую пользователем draw специальной вращательной draw, которая будет делать 2 вещи: поворачивать скетч на какой-то угол, а потом вызывать оригинальную draw. Как известно, код стоит больше тысячи слов, так что код (и тысяча слов):

; rotated-draw - вышеупомянутая вращательная функция draw
; она вращает скетч на определённый угол в зависимости от текущей итерации
; и потом вызывает исходную пользовательскую draw
; period - число итераций, за которую скетч повернётся на 360 градусов
(defn rotating-draw [period orig-draw]
  (let [; вычислим угол для поворота используя удобную map-range функцию
        angle (q/map-range (mod (q/frame-count) period)
                           0 period
                           0 q/TWO-PI)
        center-x (/ (q/width) 2)
        center-h (/ (q/height) 2)]
    ; нам нужно повернуть скетч относительно центра
    ; так что сначала нужно перенести начало координат в центр
    ; и только потом вращать
    (q/with-translation [center-x center-y]
      ; повернуть на заданный угол
      (q/with-rotation [angle]
        ; вернём начало координат обратно в левый верхний угол
        ; наше middleware дожно быть незаметно для пользователя,
        (q/with-translation [(- center-x) (- center-y)]
          ; вызываем пользовательскую draw
          (orig-draw))))))

; функция, которая и определяет middelware
(defn rotate-me [options]
  (let [; получаем пользовательскую draw или используем пустую ф-цию,
        ; если пользователь не указал draw
        draw (:draw options (fn []))
        period 200]
    ; заменяем пользовательскую draw
    ; нашей кастомной вращательной draw
    (assoc options
      :draw (partial rotating-draw period draw))))

; и используем!
(q/defsketch my-sketch
  :draw draw
  :size [500 500]
  :middleware [rotate-me])

Теперь нужно проверить, работает ли. Вот скучное статичное изображение вложенных квадратов:

Static squares

И оно начинает вращаться, после того, как мы применили rotate-me middleware:

Static squares

Предположим, что теперь нам понадобилось изменить период вращения. Мы конечно можем изменить rotate-me функцию напрямую, но это не очень красиво, что если пользователь захочет изменить период? Не очень хочется, чтобы ему пришлось лезть внутрь middleware и что-то там изменять. К тому же, middleware могла быть подключена как сторонняя библиотека, и изменение внутренностей библиотек вообще задача не из простых. К счастью, нашу проблему можно решить проще: можно задать период в опциях и middleware его прочитает:

(defn rotate-me [options]
  (let [draw (:draw options (fn []))
        ; берём период как значение :rotate-period
        ; если такого нет, то используем 200 по умолчанию
        period (:rotate-period options 200)]
    (assoc options
      :draw (partial rotated-draw period draw))))

; и задаём период в скетче
(q/defsketch my-sketch
  :draw draw
  :size [200 200]
  :middleware [rotate-me]
  :rotate-period 100)

Я не буду ещё раз показывать вращающиеся квадраты, т.к. всё, что изменилось, - это скорость вращения. Вместо этого давайте повращаем не статическую картинку, а анимацию - шарик, отталкивающийся от стен. До вращения:

Static ball

После:

Static rotating

Заключение

Middleware сильно упрощают написание расширений для Quil. Достаточно легко и шарить middleware с другими пользователями - всё, что нужно - это предоставить функцию, и другие могут её добавить в :middleware и она будет работать!

Сам Quil использует middleware в своей реализации: например safe-fn middleware, которое оборачивает все функции, полученные от пользователя так, что если они кидают исключения, то они не пробрасываются дальше, а просто выводятся в консоль, приостанавливая скетч на секунду. Это позволяет пользователю пофиксить свою функции на лету, без необходимости перезапускать скетч. Ещё один пример - deprecated-options, которое проверяeт опции на наличие устаревших опций и выводит их в консоль, сообщая, что можно использовать вместо них. Вместе с middleware в Quil 2.1.0 был добавлен функциональный режим, который по сути является middleware.

Немного ссылок:

  • Quil middleware вики статья.
  • Код из данного поста доступен на GitHub.

Если вы хотите поиграться с middleware, но не хватает идей, что бы такого реализовать - посмотрите статью на вики, указанную выше. Там есть несколько идей, можно взять их за основу.

Опубликовано 22 Jun 2014

comments powered by Disqus