Создадим свой Painter. Часть 2.

Начало здесь

Продолжаем свое путешествие в мир программируемой графики. Давайте вместо кругов используем квадраты. Посмотрим насколько сильно это изменит нашу графику. Кстати, кому интересно узнать поскорее какие еще готовые 2D фигуры доступны «из коробки», то загляните на эту страницу. В разделе 2D Primitives вы найдете описание всех фигур и примеры кода. Уберем все лишнее из кода. Оставим только суть. А чтобы квадрат рисовался из центра курсора, мы добавили строку rectMode(center). 

def setup():    
    size(1000,200)
    background(0)
    stroke(0)
    
def draw():
    if mousePressed:                                
        strokeWeight(1)
        fill(255)
        rectMode(CENTER)             
        rect(mouseX, mouseY,100,100)

Линия

До сих пор мы рисовали последовательность простых фигур, не связанных друг с другом. Давайте теперь попробуем рисовать так, чтобы новая фигура начиналась там, где закончилась предыдущая. Так мы сможем нарисовать непрерывную линию. Правда, для этого нам придется помнить предыдущее положение курсора. Для этого создадим две переменных oldX и oldY. Эти переменные нам понадобятся внутри функций draw и внутри функции mousePressed. Но изначально, внутри любых функций внешние переменные не видны. Поэтому, нам нужно явно заставить их видеть эти переменные. Для этого просто напишем слово global (глобальный), а после, через запятую, перечислим все, что хотим видеть.

oldX = 0;
oldY = 0;

def setup():    
    size(1000,200)
    background(0)
    stroke(255)
    strokeWeight(2)
    
def draw():
    global oldX, oldY
    if mousePressed:                                
        line(mouseX, mouseY, oldX, oldY)
    oldX = mouseX
    oldY = mouseY
            
def mousePressed():
    global oldX, oldY
    oldX = mouseX
    oldY = mouseY

Цветная линия

Давайте посмотрим какие возможности нам открывает этот код. Попробуем что-нибудь изменить в нем. Например толщину линии и цвет.

oldX = 0;
oldY = 0;

def setup():    
    size(1000,200)
    background(0)
    
def draw():
    global oldX,oldY
    if mousePressed:
        stroke(random(255),random(255),random(255))
        strokeWeight(30)                                
        line(mouseX, mouseY,oldX,oldY)
    oldX = mouseX
    oldY = mouseY
            
def mousePressed():
    global oldX,oldY
    oldX = mouseX
    oldY = mouseY

Не перестаю удивляться результату, осознавая, что это вектор. Т.е. графика, которая не теряет качества при любом масштабировании. И не могу вспомнить ни одну графическую программу, которая дает такую свободу в работе с кистью. 

 

Шарф Гарри Поттера

У линии в процессинге есть несколько свойств. Давайте что-нибудь поменяем. Например свойство strokeCap(). Это свойство может принимать три параметра (SQUARE, ROUND и PROJECT). Я поставил SQUARE. Возмем код из примера «Цветная линия» и поменяем только одну строчку (строка 7). Но как все изменилось! 

oldX = 0;
oldY = 0;

def setup():    
    size(1000,200)
    background(0)
    strokeCap(SQUARE)
    
def draw():
    global oldX,oldY
    if mousePressed:
        stroke(random(255),random(255),random(255))
        strokeWeight(30)                                
        line(mouseX, mouseY,oldX,oldY)
    oldX = mouseX
    oldY = mouseY
            
def mousePressed():
    global oldX,oldY
    oldX = mouseX
    oldY = mouseY

Можно попробовать менять один канал (например red) каждый раз, когда мы двигаем мышкой (внутри функции draw, строчка 15), а другие два (green, blue) только один раз, при нажатии мышкой на холст (внутри функции mousePressed).

oldX = 0;
oldY = 0;
r=0
g=0
b=0

def setup():    
    size(1000,200)
    background(0)
    strokeCap(SQUARE)
    
def draw():
    global oldX,oldY,r,g,b
    if mousePressed:
        stroke(random(255),g,b)
        strokeWeight(30)                                
        line(mouseX, mouseY,oldX,oldY)
    oldX = mouseX
    oldY = mouseY
            
def mousePressed():
    global oldX,oldY,r,g,b
    oldX = mouseX
    oldY = mouseY
    g=random(200)+150
    b=random(200)+150

 Или давайте сразу зададим два цвета. Только будем менять их в случайном порядке. Для этого у нас есть функция random(1). Она выдает на числа от 0 до 1. И если это число больше 0.5, то берем один цвет, а если меньше, то другой.

oldX = 0;
oldY = 0;

def setup():    
    size(1000,200)
    background(0)
    strokeCap(SQUARE)
    
def draw():
    global oldX,oldY
    garry = color(247, 218, 58)
    potter = color(77, 28, 42)
    if mousePressed:
        
        if random(1)>.5:
            stroke(garry)
        else:
            stroke(potter)
                            
        strokeWeight(50)                                
        line(mouseX, mouseY,oldX,oldY)
    oldX = mouseX
    oldY = mouseY
            
def mousePressed():
    global oldX,oldY
    oldX = mouseX
    oldY = mouseY

Настроение

И еще один пример того, что можно сделать с линией. Для этого снова, вместо random() воспользуемся функцией noise(). И не забудем, что для ее работы нам нужна постоянно увеличивающаяся переменная (строчки 2 и 10).

oldY = 0;
xoff = 0.0

def setup():    
    size(1000,200)
    background(0)
    
def draw():
    global oldX,oldY,xoff
    xoff = xoff + .01
    if mousePressed:
        stroke(noise(xoff+1)*255,noise(xoff)*255,200)
        strokeWeight(noise(xoff+1)*30)                                
        line(mouseX, mouseY,oldX,oldY)
    oldX = mouseX
    oldY = mouseY
            
def mousePressed():
    global oldX,oldY
    oldX = mouseX
    oldY = mouseY

Получилась вполне себе такая настроенческая кисточка. Вы рисуете, а программа сама плавно меняет оттенок. Интересная идея для графического редактора.  

Творческое задание

Возьмите код из раздела «Шарф Гарри Поттера» и измените его по своему вкусу. Наша задача с помощью этой кисточки нарисовать какую-нибудь абстрактную композицию размером (1000x1200px, вертикальную). Я подобрал для вас немного примеров для вдохновения. Подробнее их можно посмотреть здесь на доске Pinterest. Ниже приведу несколько вариантов готовых композиций.

Размер кисточки

Вам здесь понадобится менять по ходу ширину кисточки. Этого мы еще не обсуждали. Давайте это исправим. Пусть, при нажатии на цифр от 1-9, у нас будет меняться размер кисточки. Возьмем, например, код из «Шарф Гарри Поттера». Добавим в него глобальную переменную brushSize (строка 3). И начнем «слушать» клавиатуру (строка 29). Здесь мы проверяем, что нажата именно цифра, а затем умножаем ее на 10px и присваеваем переменной brushSize. Ну и, конечно, поставим эту переменную в качестве параметра strokeWieght(brushSize).  

oldX = 0;
oldY = 0;
brushSize=10

def setup():    
    size(1000,200)
    background(0)
    strokeCap(SQUARE)
    
def draw():
    global oldX,oldY,brushSize
    garry = color(247, 218, 58)
    potter = color(77, 28, 42)
    if mousePressed:        
        if random(1)>.5:
            stroke(garry)
        else:
            stroke(potter)                        
        strokeWeight(brushSize)                                
        line(mouseX, mouseY,oldX,oldY)
    oldX = mouseX
    oldY = mouseY
            
def mousePressed():
    global oldX,oldY
    oldX = mouseX
    oldY = mouseY
    
def keyPressed():
    global brushSize
    if(key.isnumeric()):
        brushSize = int(key)*10  

Теперь, в процессе рисования, мы можем нажимать на клавиатуре цифры и наша кисточка будет менять размер.

Сохранение в pdf

Для уменьшения размера кода, я не добавляю в него строчки для сохранения в pdf. Но вам необходимо эти строки добавить, чтобы сохранить готовую работу. Привожу код полностью, вместе с возможностью сохранения. За сохранение отвечают строки 1, 8 и 36. 

add_library('pdf')

oldX = 0;
oldY = 0;
brushSize=10

def setup():    
    beginRecord(PDF, "everything-##.pdf");
    size(1000,200)
    background(0)
    strokeCap(SQUARE)
    
def draw():
    global oldX,oldY,brushSize
    garry = color(247, 218, 58)
    potter = color(77, 28, 42)
    if mousePressed:        
        if random(1)>.5:
            stroke(garry)
        else:
            stroke(potter)                        
        strokeWeight(brushSize)                                
        line(mouseX, mouseY,oldX,oldY)
    oldX = mouseX
    oldY = mouseY
            
def mousePressed():
    global oldX,oldY
    oldX = mouseX
    oldY = mouseY
    
def keyPressed():
    global brushSize
    if(key.isnumeric()):
        brushSize = int(key)*10
    if(key == 's'):
        endRecord()
        exit() 

Продолжение. Создадим свой Painter. Часть 3.