Выбрать главу

center = barchart(sys.argv[-1])

addcamera(center)

addlamp()

Рендеринг самый простой (мы столкнемся с более сложными примерами в Главе 8, Рендеринг и Обработка Изображения). Мы извлекаем контекст рендеринга, который хранит всю информацию о рендеринге, например, номер кадра, какой выходной формат, размер изображения, и так далее. И, поскольку большинство атрибутов по умолчанию разумны, мы установим только выходной формат на PNG и запустим рендер.

context=sc.getRenderingContext()

context.setImageType(Scene.Render.PNG)

context.render()

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

context.setRenderPath('')

context.saveRenderedImage(csv[:-4]+'.png')

Добавление лампы не значительно отличается от добавления любого другого объекта и очень подобно примеру "hello world". Мы создаём новый объект Lamp, добавляем его к текущей сцене и устанавливаем его позицию. Объект Lamp имеет, конечно, много настраиваемых параметров, но мы в этом примере довольствуемся не-направленной лампой по-умолчанию. Выделенный код показывает типичную идиому Питона: loc - кортеж из трех величин, но setLocation() принимает три отдельных аргумента, так что мы указываем, что хотим распаковать кортеж на отдельные значения с помощью * нотации:

def addlamp(loc=(0.0,0.0,10.0)):

    sc = Scene.GetCurrent()

    la = Lamp.New('Lamp')

    ob = sc.objects.new(la)

    ob.setLocation(*loc)

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

def addcamera(center):

    sc = Scene.GetCurrent()

    ca = Camera.New('persp','Camera')

    ca.angle=75.0

    ob = sc.objects.new(ca)

    ob.setLocation(center[0],center[1],center[2]+12.0)

    sc.objects.camera=ob

Сама функция barchart не такая уж большая неожиданность. Мы открываем файл с полученным именем и используем стандартный модуль csv из Питона, чтобы читать данные из файла. Мы загружаем все заголовки столбцов в xlabel, а остальные данные в rows (строки).

from csv import DictReader

def barchart(filename):

    csv = open(filename)

    data = DictReader(csv)

    xlabel = data.fieldnames[0]

    rows = [d for d in data]

Для того, чтобы масштабировать нашу гистограмму до разумных величин, мы должны определить пределы данных. Первый столбец каждой записи содержит значение по x (или метку), так что мы исключаем его из нашего вычисления. Так как каждая величина загружена в виде строки, мы должны преобразовать её в величину с плавающей точкой для сравнений.

maximum = max([float(r[n]) for n in data.fieldnames[1:] 

               for r in rows])

minimum = min([float(r[n]) for n in data.fieldnames[1:] 

               for r in rows])

Чтобы фактически создать столбики, мы проходим по всем строкам. Поскольку значение по x может быть текстовой меткой (как название месяца, например), мы сохраняем отдельно цифровое значение x для того, чтобы позиционировать столбики. Само значение x добавляется к сцене в виде объекта Text3d функцией label(), поскольку значения y визуализируются соответственно масштабированными объектами  Cube (Куб), добавляемыми функцией bar(). Функции label() и bar() не показаны здесь.

for x,row in enumerate(rows):

    lastx=x

    label(row[xlabel],(x,10,0))

    for y,ylabel in enumerate(data.fieldnames[1:]):

        bar(10.0*(float(row[ylabel])-minimum)/maximum,

(x,0,y+1))

x = lastx+1

Наконец, мы подписываем каждый столбец (то есть, каждый набор данных) своим собственным заголовком столбца как label. Мы сохранили число значений по x, так что мы можем вернуть центр нашей гистограммы деля его на два (y-компонент установлен на 5.0, так как мы масштабировали все значения по y, чтобы они лежали в пределах диапазона от 0 до 10).