Мы также определяем вход Coords. Этот входной сокет может перехватывать любой выход сокета геометрии. Таким образом у нас есть множество возможностей отобразить нашу цветную текстуру на объект, который мы текстурируем. Сокет Scale определяется также, чтобы управлять размером нашей текстуры.
Наконец, мы определяем сокет Type, чтобы выбирать узор, который мы хотим генерировать. Так как API для Pynode не обеспечивает выпадающих меню или любого другого простого управляющего элемента для выбора, мы делаем сокет с одиночным значением и произвольно выбираем величины, представляющие наш выбор: 1.0 для треугольников, 2.0 для шахматного поля, и 3.0 для шестиугольников.
Мы заканчиваем нашу функцию __init__() определением множества констант и словаря распределений цвета, который мы будем использовать при генерации шестиугольной текстуры.
self.cos45 = cos(pi/4)
self.sin45 = sin(pi/4)
self.stretch = 1/sqrt(3.0)
self.cmap = { (0,0):None,(0,1):2, (0,2):0,
(1,0):0, (1,1):1, (1,2):None,
(2,0):2, (2,1):None,(2,2):1 }
Следующим шагом будет определение функции __call__():
def __call__(self):
tex_coord = self.input.Coords
# мы игнорируем любую z-координату
x = tex_coord[0]*self.input.scale
y = tex_coord[1]*self.input.scale
c1 = self.input.color1
c2 = self.input.color2
c3 = self.input.color3
col= c1
Функция __call__() начинается с определения нескольких сокращений для входных величин и умножения координатного входа на выбранный масштаб, чтобы растянуть или уменьшить сгенерированный узор. Следующий шаг должен установить тип желательного узора и вызвать подходящую функцию для вычисления выходного цвета для данных координат. Результирующий цвет назначается в наш единственный выходной сокет:
if self.input.type<= 1.0:
col = self.triangle(x,y,c1,c2)
elif self.input.type <= 2.0:
col = self.checker(x,y,c1,c2)
else:
col = self.hexagon(x,y,c1,c2,c3)
self.output.Color = col
Все различные функции генерации узоров очень похожи; они берут координаты x и y и два или три цвета в качестве аргументов и возвращают единственный цвет. Так как это функции-члены класса, они также принимают дополнительный первый аргумент self.
def checker(self,x,y,c1,c2):
if int(floor(x%2)) înt(floor(y%2)):
return c1
return c2
Функция checker проверяет, в какой строке и колонке мы находимся, и если номер строки и номер колонки - оба нечетные или четные (что устанавливает оператор исключающее или), она возвращает один цвет, если нет, то возвращает другой цвет.
def triangle(self,x,y,c1,c2):
y *= self.stretch
x,y = self.cos45*x - self.sin45*y,
self.sin45*x + self.cos45*y
if int(floor(x%2)) înt(floor(y%2)) ^ \
int(y%2>x%2) : return c1
return c2
Функция triangle сначала одновременно вращает как x, так и y координаты на угол 45 градусов (превращение квадратов в вертикальные ромбы). Затем она определяет цвет, основываясь на номерах строки и колонки в точности подобно функции checker, но с уловкой: третье условие (выделено) проверяет, слева ли мы от диагонали, пересекающей квадрат, и поскольку мы вращали нашу сетку, на самом деле мы проверяем действительно ли координаты выше горизонтальной линии, делящей наш ромб. Это может звучать немного сложным, но Вы можете посмотреть на следующую иллюстрацию, чтобы понять идею:
def hexagon(self,x,y,c1,c2,c3):
y *= self.stretch
x,y = self.cos45*x - self.sin45*y,
self.sin45*x + self.cos45*y
xf = int(floor(x%3))
yf = int(floor(y%3))
top = int((y%1)>(x%1))
c = self.cmap[(xf,yf)]
if c == None:
if top :
c = self.cmap[(xf,(yf+1)%3)]
else :
c = self.cmap[(xf,(yf+2)%3)]