Плоттеры были в ходу еще до появления хороших лазерных и струйных принтеров. Но теперь они возвращаются, как музыка 80-х (смеется), на этот раз на стыке кода и искусства. По сравнению с принтерами, которые намного быстрее, плоттеры хороши тем, что дают результаты, которые выглядят нарисованными от руки. И смотреть, как они рисуют, весело! Я купил два современных плоттера для любителей, первый Makeblock XY Plotter, а совсем недавно AxiDraw v3. Каждую покупку я использовал как возможность взломать код для управления плоттером и создания интересных произведений искусства.

В этой статье я поделюсь несколькими интересными сведениями о том, как программировать плоттеры для эффективного построения графиков. Обратите внимание, что ничего из этого не требуется, если вы используете программное обеспечение, поставляемое с плоттерами, но это полезно, если вы пытаетесь самостоятельно управлять плоттерами программно.

Эти плоттеры в основном состоят из двух шаговых двигателей (один для оси X и один для оси Y) и серводвигателя для перемещения пера вверх и вниз. Вы можете вставить практически любой инструмент для рисования (ручку, маркер, карандаш, уголь и т. Д.) И перемещать его по бумаге, чтобы что-то нарисовать.

Здесь мы имеем дело с векторными координатами, и в конечном итоге все сводится к набору отрезков линии. Кривые? Серия сегментов. Заполняет? Придется сделать несколько проходов или сделать штриховку. Что касается структур данных, мы можем взглянуть на это так. Точка - это координата (x, y). Путь - это упорядоченная последовательность точек. А Рисование - это набор путей, которые мы хотим нарисовать (обычно порядок рисования не имеет значения).

Серводвигатель позволяет перемещать перо в определенное вертикальное положение, но мы обычно можем думать об этом как о двоичном: вверх (не рисование) или вниз (рисование). Вы можете представить себе попытку изменить давление пера, но это очень сложно без идеально ровной и параллельной поверхности, а разрешение мотора между отсутствием касания и полным касанием очень мало, поэтому мы не будем рассматривать это дальше.

Итак, наш язык для плоттера состоит из следующих действий: перо вверх, перо вниз и перемещение в позицию. Мы пишем код для генерации нашего чертежа, и теперь мы хотим отправить его на плоттер. Наивно, мы можем просто перебирать пути и рисовать их:

for each path in drawing:
    move to first point in path
    lower pen
    for each point in path:
        move to point
    raise pen

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

Упорядочивание путей
Во-первых, мы хотим переупорядочить пути, чтобы минимизировать перемещение без рисования. По сути, это задача коммивояжера, которая в общем случае не решается. Но жадное решение работает достаточно хорошо. Начните с произвольного пути и нарисуйте его. Далее найдите ближайший не начерченный путь и нарисуйте его. Повторить. Обратите внимание, что у пути есть две конечные точки! Итак, если мы позволим, алгоритм может нарисовать путь в обратном порядке, если эта конечная точка является ближайшей. Представьте, что мы хотим нарисовать кучу параллельных линий, начиная с X = 0 и заканчивая X = 1, при различных значениях Y. Наиболее эффективно мы бы нарисовали линию от 0 до 1, а следующую от 1 до 0, каждый раз меняя направление. Чтобы эффективно вычислить этот порядок, я предлагаю пространственный хеш-индекс.

Объединение путей
После того, как мы упорядочим пути, мы можем объединить последовательные пути, конечные точки которых находятся в пределах некоторого допуска. Таким образом, если путь A заканчивается очень близко к тому месту, где начинается путь B (в основном, в пределах диаметра кончика пера), нам не нужно беспокоиться о том, чтобы поднимать перо. Это также может сэкономить нам много времени, в зависимости от характера рисунка. Реализовать это несложно.

Упрощение контуров
После того, как мы отсортировали и соединили контуры, мы должны упростить их, опять же с определенным заданным допуском. Алгоритм Рамера – Дугласа – Пекера - простой способ реализовать упрощение ломаной линии. Это уменьшает количество данных, которые нам нужно отправить на плоттер. Но что более важно, это улучшает результаты алгоритма планирования движения. (Я реализовал свой собственный планировщик движения с постоянным ускорением, но он заслуживает отдельной статьи.)

Некоторые другие удобства включают масштабирование и / или поворот рисунка по размеру страницы, центрирование рисунка на странице и рендеринг рисунка в формате PNG, чтобы мы могли увидеть, как он будет выглядеть. Также неплохо иметь индикатор выполнения, чтобы видеть, сколько осталось времени (простые рисунки можно сделать менее чем за минуту, а сложные рисунки могут легко занять более часа).

Я также реализовал интерфейс черепаховой графики для создания рисунков. Это делает действительно элегантным создание, например, кривой дракона.

# generate a dragon curve using turtle graphics
import xy
def main(iterations):
    t = xy.Turtle()
    for i in range(1, 2 ** iterations):
        t.forward(1)
        if (((i & -i) << 1) & i) != 0:
            t.circle(-1, 90, 36)
        else:
            t.circle(1, 90, 36)
    xy.draw(t.drawing)
if __name__ == '__main__':
    main(13)

Все эти функции доступны в моих библиотеках Python xy (для Makeblock XY) и axi (для AxiDraw).

Спасибо за прочтение! Если вам это понравилось, подпишитесь на меня в Twitter @FogleBird