Cinema 4D: создаем плагин – объект
Часть 1. Обучение CINEMA 4D + Python: Программирование, предметная область.
Часть 2. Cinema 4D: фракталы и сплайны
Часть 3. Cinema 4D: плагины (plug-ins)
Анализ уже существующего кода.
Функция __main__ .
На данный момент у нас уже есть Eclipse с уже установленным Eclipse –плагином для PyDev. Причем, интерпретатором для этого плагина является тот, который поставляется с дистрибутивом Cinema 4D. Несколько неожиданно то, что версия Python 2.6.8. Но об этом в конце статьи, так реализована поддержка работы с кодом на Python для проектов Cinema 4D. Точнее, автоматически подгружается библиотека определений (модулей) из Cinema 4D и редактор Eclipse понимает текст и не окрашивает текст сплошным красным цветом.
Можно расширить возможности отладки добавив следующие строки :
import sys
# строка ниже уточняется для вашего варианта поиском “pydevd.py”
sys.path.append(“c:\eclipse\plugins\org.python.pydev_2.8.2.2013090511\pysrc“)
import pydevd
Код начала трассировки, ставится в нужном вам месте, таков:
pydevd.settrace()
В отладочном режиме Eclipse (‘Window’ -> ‘Open Perspective’ -> ‘Other’ -> ‘Debug’) и с запущенной отладочной версией Cinema 4D.
Непротиворечивость (особенно файлов ресурсов) проверяется единственным способом – перезагрузкой системы, в случае ошибок никаких диагностических сообщений не выдается.
Признаком отсутствия ошибок в файлах ресурсов – отсутствие сообщений в командной строке Cinema 4D. Это окно должно быть активировано, автоматом не запускается.
Ориентировкой может быть и сообщения Eclipse или факт опознавания системой кода как плагина. Очень даже не густо. Тем не менее, создадим проект (из уже готового кода), как это описывалось в предыдущей статье, для учебного примера Py-DoubleCircle.
Начнем анализировать код, естественно с функции __main__
Рис.1.
До строки 252 получаем через переменные path и file путь к файлу и имя файла, затем создается объект битовой карты и в строке 253-254 инициализируется конкретным файлом circle.tif. Если мы взглянем в Python Help, то увидим еще некоторые команды инициализации класса bitmaps
c4d.bitmaps.ShowBitmap(bmp)
,
c4d.bitmaps.InitResourceBitmap(resource_id)
,
в последней команде аргумент resource_id это константа, которая определяет одну из уже существующих иконок, такие как логотип Python, в том числе.
Для проверки понимания этих строк создадим Python Generator () и посмотрим, что получится (предварительно создан тестовый каталог, картинку создал рендерингом самого тестового примера, повторюсь, это Python Generator, не плагин):
Битовая картинка отображается в окне Picture Viewer. Перейдем к строкам 255-258. Функция plugins.RegisterObjectPlugin()
только регистрирует плагин и ничего другого. Это не очень хорошо, при отладке мы, по сути, контролируем только этот код.
Запуск на выполнение должен обеспечиваться соответствующей структурой исполнения, то есть существует раз и навсегда заданный порядок выполнения четко фиксированных функций и наша задача их переопределить.
Первым аргументом функции plugins.RegisterObjectPlugin()
является id плагина (определен в самом начале текста программы), второй – его имя, третий источник данных. Видим, что имеем дело с классом производным от
plugins.ObjectData
, поскольку создаем плагин типа объекта, для других типов плагина иначе. Далее, весь код класса DoubleCircleData
обеспечивает выполнение как реакцию на действия пользователя.
Следующий аргумент description функции RegisterObjectPlugin()
задает имена файлов ресурсов с фиксированными расширениями *.h, *.res, *.str. И, наконец, последний аргумент – логическое “or” флагов (константы) info=c4d.OBJECT_GENERATOR|c4d.OBJECT_ISSPLINE
.
Но что это за константы? Для этого нам уже понадобится уже и С++ Help к Cinema 4D (лучше всего эти 2 help-а сразу открыть в Eclipse, так как придется копировать имена констант в текст программы). Поиском по OBJECT_GENERATOR найдем (перевод с сокращениями и мы уже в С++)
1 2 3 4 5 6 |
global c4d_objectdata <span style="color: #008000;">{</span> <span style="color: #0000ff;">public</span><span style="color: #008080;">:</span> Bool RegisterObjectPlugin<span style="color: #008000;">(</span>Int32 id, <span style="color: #0000ff;">const</span> String<span style="color: #000040;">&</span> str, Int32 info, DataAllocator<span style="color: #000040;">*</span> g, <span style="color: #0000ff;">const</span> String<span style="color: #000040;">&</span> description, BaseBitmap<span style="color: #000040;">*</span> icon, Int32 disklevel<span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #008000;">}</span><span style="color: #008080;">;</span> |
Члены класса
Bool RegisterObjectPlugin(Int32 id, const String& str, Int32 info, DataAllocator* g, const String& description, BaseBitmap* icon, Int32 disklevel)
Возвращает тип Bool, если плагин прошел регистацию
Параметры
Int32 id уникальный ID плагина. Можно этот ID получить здесь http://www.plugincafe.com, но обойдемся пока отладочным
const String& str Имя плагина .
Примечание: если вы желаете повлиять на порядок, в котором отображаются ваши плагины в меню, то можете добавить “#$n“ как префикс к этому имени, где n это число. Меньшему числу соответствует более высокая строчка. Если именем будет “–” то это будет строка разделения меню.
Int32 info установки для плагина. Возможными значениями флагов могут быть:
Константы | Описания |
OBJECT_MODIFIER |
Модификатор объекта. Деформирует объект, которому подчинен. (Например, bend.) |
OBJECT_GENERATOR |
Генератор объекта. Создает полигональное или сплайновое его представление. (Например, примитив куба.) |
OBJECT_INPUT |
Используется совместно с OBJECT_GENERATOR. Определяет построение генератором полигона или сплайна, применяя подъобъект как ввод. (Например Sweep NURBS, Boolean.) |
OBJECT_PARTICLEMODIFIER |
Модификатор частиц. |
OBJECT_ISSPLINE |
Объект сплайн. |
OBJECT_CAMERADEPENDENT |
Зависимость от камеры. |
OBJECT_POINTOBJECT |
Точечный объект. |
OBJECT_POLYGONOBJECT |
Полигон. |
DataAllocator* g указатель на функцию, создающую новый экземпляр ObjectData с помощью NewObj(). (Не проверял, но думаю, что это перегруженный оператор new(). Проверить не сложно в пакете VS2010/2012)
const String& description - Имя файла ресурсов описания для применения с вашим плагином без расширения .res. Имя должно быть уникальным
BaseBitmap* icon Иконка, объект иконки копируется.
Примечание: Размер иконки 32×32, но при необходимости масштабируется. Иконка должна быть 24 битной и включать alpha.
Int32 disklevel это всего лишь версия ваших установок.
Наш класс DoubleCircleData создает двойную окружность, и при этом мы имеем возможность устанавливать параметры объекта, которые видные на рисунке слева. Некоторые параметры заблокированы. Эти параметры могут быть как стандартными, уже с определенным типом, но для других, предустановленных объектов Cinema 4D, так и пользовательскими. Пользовательские параметры определяются файлами ресурсов. Поэтому без особых хлопот эти параметры могут быть применены в задаче пользователя. А могут быть пользовательскими, связанными с вашим конкретным приложением и нужно позаботиться об их определении.
В нашем примере, DoubleCircleData , при нажатой клавише Ctrl и левой кнопки мыши происходит копирование уже существующего последнего образца и создание нового в том месте 3D пространства , где левая кнопка освобождена. То есть, код должен уметь обрабатывать события, в данном случае передвижения мыши при нажатой левой клавише, а значит должны быть и обработчики этих событий (нажатая клавиша, движение или изменение координат мыши).
Наш класс наследует классу plugins.ObjectData . Первое наблюдение показывает, что не все функции класса вызываются в пользовательском коде, как видно из текста, а это означает, что вызов этих функций реализует система. Это и есть те самые рельсы, о которых говорилось выше.
Наша задача только лишь в том, что бы прописать переопределением этих методов при создании своего собственного объекта. Причем, прописываем только то, что нужно, все остальное работает благодаря механизму наследования. Сигнатура этих функций – методов (тип возвращаемого значения, аргументы) уже предопределена родительским классом.
В свое время MFC С++ величаво и с пафосом называли парадигмой Документ-Облик. Интересно, когда трамвайчик катит по рельсам, то рельсы это парадигма? Представляю, как бы этой публикой было торжественно заявлено о новой транспортной парадигме “ни шагу в сторону!”. Увы, скорее грустно, чем смешно. Но и наш случай то же самое! И эти предопределенные методы, которые мы перегружаем, и есть рельсы. И 90% современных, так называемых, технологий это те же рельсы. Самое удивительнее, что еще и говорят о прогрессе!
Новая функция __main__ примет такой вид для разрабатываемого плагина:
Py-Fractal это имя проекта, Opyfractal это общее имя ресурсов этого проекта. Буква O вначале, по принятому соглашению, означает, что создаем объект.
Методы класса plagin.Object
Давайте поработаем только с теми методами, которые определены в нашем примере и составим табличку (сокращенную, подробности в документации, мы просто показываем методику анализа – знакомства). При этом из документации видно, что наш класс ObjectData производен от суперкласса c4d.plugins.NodeData. Поэтому, не все функции рассматриваемого класса будут в перечне, приведенном ниже для ObjectData, и в нашем примере могут функции производного класса переопределять и функции родительского класса (суперкласс суперкласса, тяжелая фраза, но ничего не сделаешь). Хотя и понятно, что при наследовании функции родительского класса являются и функциями потомка, дочернего класса.
Таблица 1
Методы класса | Описания из документацииCINEMA4DR15037PYTHONSDKHTML20130823CINEMA4DR15SDKHTML20130925 | |
GetDimension() | В примере не используется | 1 |
Draw(self,op, drawpass, bd, bh) | op (BaseObject) – конкретный объект;drawpass (int) – флаги;bd (BaseDraw) – Вид в редакторе;bh (BaseDrawHelp) – Help редактора;int - это DRAWPASS_OBJECT,DRAWPASS_BOX,DRAWPASS_HANDLES, DRAWPASS_HIGHLIGHTS – вариант действий пользователя | 2 |
DrawShadow() | В примере не используется | 3 |
DetectHandle(self, op, bd, x, y, qualifier) | op (BaseObject) – объект работы;bd (BaseDraw) – редактор;x (int), y (int) – координаты мыши;qualifier (int) int это QUALIFIER_SHIFT, QUALIFIER_SHIFT, QUALIFIER_CTRL, QUALIFIER_MOUSEHIT см.выше | 4 |
MoveHandle(self, op,undo, mouse_pos, hit_id, qualifier, bd) | op (BaseObject) – то же, что и выше;undo (BaseObject) – это копия объекта, который не должен меняться при обработке перемещения;mouse_pos (Vector) – текущая позиция мыши;hit_id (int) – значение возвращаемое из DetectHandle()qualifier (int) то же, что и выше; bd (BaseDraw) – то же, что и выше. | 5 |
AddToExecution() | В примере не используется | 6 |
Execute() | В примере не используется | 7 |
GetVirtualObjects() | Полезна для ускорения рендеринга | 8 |
ModifyObject() | В примере не используется | 9 |
CheckDirty() | Полезна для ускорения рендеринга | 10 |
GetContour(self, op, doc, lod, bt) | op (BaseObject) – см.выше;doc (BaseDocument) – документ, содержащий плагин;lod (int) – уровень детализации;bt (None:) – не используется; Отсюда ИДЕТ ОБРАЩЕНИЕ К СОЗДАНИЮ ОБЪЕКТА вызов функции GenerateCircle(self, rad) и OrientObject | 11 |
ModifyParticles() | В примере не используется | 12 |
Остались такие функции (и вновь таблица, имена параметров и их расшифровка те же):
Таблица 2
Потерянные функции | Аргументы функции | Примечания | |
Message(self, node, type, data) | Обработчик сообщений | 13 | |
Init(self, node) | Конструктор класса, заполнение полей данных пользователя по умолчанию | 14 | |
@staticmethoddef SwapPoint(p, plane) | @staticmethodЭто возможность вызова функции без создания экземпляра | Пользовательская функция – меняем направление нормали к плоскости на противоположное | 15 |
GetHandleCount(op) | Подсчет обработчиков событий | 16 | |
GetHandle(self, op, i, info) | Получение обработчика по индексу i | 17 | |
SetHandle(self, op, i, p, info) | Info – данные к обработчику (например позиция мыши) | Установка обработчика с индексом i | 18 |
GenerateCircle(self, rad) | Пользовательская функция, вызов из GetContour | 19 | |
@staticmethodOrientObject(op, plane, reverse) | Пользовательская функция, вызов из GetContour | 20 | |
GetDEnabling(self, node, id, t_data, flags, itemdesc) | node (GeListNode) – Список узлов связанных с экземпляромid (DescID) – ID параметра.t_data (any) – Значения параметраflags (int) – не применяется.itemdesc (BaseContainer) – описано в Description. | Вызывается при определении какой из параметров активировать или деактивировать (ghosted). Применяется как для параметров контейнера узла так и для пользовательских параметров.Просто читает t_data при верном id возвращает True при активации. | 21 |
Вернемся к примеру, разобранному в статье 2, и применим полученное представление о плагине, но основе проведенного анализа.
Мы, в основном, ожидаем, что уже существующий код нужно разнести по предопределенным функциям. Предполагаемая сложность – попасть в список предопределенных типов переменных. Что бы избежать конфликтов определений в файлах ресурсов сделаем их пустыми. Хорошая идея для дальнейшего, подготовить свой вариант “Hello word”, как шаблон для дальнейшего применения.
Поскольку в простейшем варианте их минимум 4 вот таблица содержания (попытаемся понять их назначение):
Таблица 3
opyfractal.h |
opyfractal.res |
opyfractal.str |
c4d_symbols.h |
PyFractal\res\description\ |
PyFractal\ res\strings_us\ description\ |
PyFractal\res\ |
|
#ifndef_Opyfractal_H_#define _Opyfractal_H_enum{};#endif | CONTAINER Opyfractal{NAME Opyfractal;INCLUDE Obase;GROUP ID_OBJECTPROPERTIES{}} | STRINGTABLE Opyfractal{Opyfractal “Fractal”;} | enum{// End of symbol definition_DUMMY_ELEMENT_}; |
То, что ниже из примера, то что выше нужно менять | |||
#ifndef _Opydoublecircle_H_#define _Opydoublecircle_H_enum{PYCIRCLEOBJECT_RAD = 10000 // values 1000.3000 already reserved from includes};#endif | CONTAINER Opydoublecircle{NAME Opydoublecircle;INCLUDE Obase;GROUP ID_OBJECTPROPERTIES{REAL PYCIRCLEOBJECT_RAD { UNIT METER; MIN 0.0; }}
INCLUDE Osplineprimitive; } |
STRINGTABLE Opydoublecircle{Opydoublecircle “Double Circle”;PYCIRCLEOBJECT_RAD “Radius”;} | enum{// End of symbol definition_DUMMY_ELEMENT_}; |
- Обычно, при создании элементов управления, каждому из них присваивается некоторое число, а это число в ресурсах имеет символическое имя. При отображении формы с этими элементами рядом должна присутствовать строка пояснения. Как правило, это статический текст и он не часто меняется, поэтому и номер ему не нужен. Из этой таблицы сравнения видно, что именно третий столбец и является перечнем надписей к элементам управления. Первая строка ( элемент) этого же столбца – это оглавление всей таблицы параметров и пользовательских и предопределенных.
- В первом столбце перечислены имена констант и их значения. По этим номерам из программы мы и будем обращаться к этим окнам редактирования или, иначе, элементам управления, которые создаются самой Cinema 4D по нашим ресурсным файлам. Причем содержимое объявлено как перечисление (enum). Это означает, что если их более одного, то они разделяются запятой.
- Второй столбец описывает структуру этих, внешним образом задаваемых значений и пользовательских значений. Указывается и специфическая информация – размерность параметра, например градусы для углов или единицы измерения длины. Строка INCLUDE Osplineprimitive обеспечивает подключение уже предопределенных идентификаторов, но уже только тех, которые предопределены в примитивах сплайнов.
- Столбец 4 определяет локализацию, по умолчанию задается именем каталога, в котором этот ресурс находится.
Если все это сделано согласно верхней части таблицы, то плагин уже может быть принят системой и будет отображен так:
Если файлы ресурсов написаны неверно, то столбец “Объект” (выделен синим цветом на рисунке), будет отсутствовать. С другой стороны, при правильной записи файлов ресуров еще не факт, что вы получите программный доступ к элементам управления (например Angle alf ). Некоторые элементы должны быть программно заблокированы, например “b transform”. Пока я не касаюсь кода, но перетаскивание каждого из параметров в окно консоли дает расшифровку каждого из параметров, их индексы в словаре объекта (следующий рисунок, окно “Консоль”). Почему словарь?
Это уже серьезный вопрос и ответ на него предполагает достаточно глубокие знания языка Python. Поэтому скажем так, по принятой системе хранения значений переменных, организации областей видимости и реализации наследования. Об этом говорилось в статье 1 при обсуждении отличий С++ и Python. Коротко это можно представлять так, что в операторе присвоения p = z, p это ключ для значения z. Доступ по ключу организуется структурой типа словарь, отсюда и квадратные скобки, содержащие значения ключа-индекса. Наследование классов реализовано в Python как объединение соответствующих словарей. Объединение реализуется с помощью специальных переменных (предположительно) имеющих символы двойного подчеркивания и как префикс и как постфикс. Значение ключа может быть и строкой.
Вот эти индексы (только часть )в порядке их отображения в этом (правом) окне:
Плоскость: [c4d.PRIM_PLANE] ;
Обратить : [c4d.PRIM_REVERSE]
Угол: [c4d.SPLINEOBJECT_ANGLE] ;
Если мы создадим пробный сплайн, то увидим, что часть этих параметров задается сплайном, индексы во встроенном словаре определяются теми же значениями, вот они ниже (напомню, перетаскивание мышки на нижнюю панель консоли Cinema 4D, в окне ввода команд в командном окне, вертикальная, короткая желтая полоска курсора)
foo[c4d.SPLINEOBJECT_TYPE]
foo[c4d.SPLINEOBJECT_ANGLE]
foo[c4d.SPLINEOBJECT_MAXIMUMLENGTH]
Именно поэтому в Табл.3 во втором столбце и стоит строка INCLUDE Osplineprimitive;
По этой же причине последняя строчка функции main содержит c4d.OBJECT_ISSPLINE info=c4d.OBJECT_GENERATOR|c4d.OBJECT_ISSPLINE (Рис.1). По содержанию нашей задачи мы можем от этого отказаться.
Файлы ресурсов
Давайте подумаем, а что нужно для нашего плагина? Наш плагин, как следует из описания Python Generator в статье 2, должен иметь:
- Аксиому (это строка)
- Правила вывода (опять строка). В простейшем случае это подстановка только для символа F, в более сложном, и подстановка для символа b. То есть, в общем случае потребуется для правил вывода 2 строки.
- Угол, на который отклоняется движущееся перо при встрече со знаками “+” или “-“ и исходный угол. В принятых обозначениях это alpha и betha.
- Число применений правил вывода к аксиоме – целое число.
- Ориентацию в пространстве – плоскости XY, XZ, YZ
В итоге наш объект полностью определен словарем:
{аксиома: str, вывод_F: str, вывод_b:str, alfa: float, betha:float, iter: enum(1,2,3,4,5), orient: enum(XY, XZ, YZ) }
Файлы ресурсов имеют вид:
Поскольку в перечислении достаточно задать только первое значение, то возможна модификация, с первой строкой как на рисунке, все остальные без числовых значений, но обязательно через запятую. Считается, что поскольку значения до 3000 уже заняты константами предопределенных объектов, то надежнее пользоваться, начиная со значений 10000, хотя во всех приложениях, учебных в том числе, применяют от 1000.
Два элемента для ввода угла. Значения угла в программе вводится в радианах, но отображаются в градусах. На количество подстановок мы ввели ограничения, не более 5, поскольку символьные выражения для фракталов очень быстро растут, и разрешающая способность экрана не позволит работать с большой глубиной замены замен (см. статью 2). Тип LONG не должен смущать, поскольку этот ресурс, по совместительству и описание данных. И это тот случай, который упоминался в статье 3, то есть вариант языка IDL. Структура GROUP ID_OBJECTPROPERTIES может быть вложенной, подробности в helpe, но уже для C++.
Не лишне напомнить, что содержимое файлов ресурсов должно быть согласовано, то есть порядок перечисления констант должен быть строго один и тот же.
В этом ресурсе перечислены тексты меток, или статический текст подписи каждого элемента управления объектом, причем, только пользовательские. Все остальные генерируются по содержанию предыдущего ресурса (последняя строка с INCLUDE Osplineprimitive
Код
В самом начале необходимо присвоить регистрационный номер нашей разработке. Возьмем из отладочного диапазона:
Метод def Init(self, op):
Этот метод реализует связь ресурсов с кодом.
До первой пустой строки я назначаю значения по умолчанию пользовательских параметров и назначаю им соответствующие индексы элементов управления. Затем (первой пустой строки) я задаю значения по умолчанию для предопределенных констант (например c4d.PRIM_PLANE). В завершении я определяю свойства класса (self.alfa) и для того, что бы не применять громоздкие записи в тексте программы.
Метод выполняется при загрузке Cinema 4D. При вызове плагина. Возвращает метод True, создание объекта класса разрешено.
Метод GetContour(…).
Этот метод отслеживает действия пользователя по изменениям в элементах управления объектом, изменения значений, отображаемых этими элементами, например глубину итераций в нашем случае. Как только внесено изменение, то автоматически по внутренне сгенерированному сообщению, вызывается этот метод. Это не всегда удобно, например, в нашем случае, вводимые углы тесно связаны с аксиомой и их часто нужно изменять одновременно, но система отреагирует вызовом этого метода при каждом изменении. В этом случае нужно научиться генерировать свои сообщения и писать их обработку, либо задействовать указанные в таблице 1 методы Execute() и AddExecute(), подробности в справочнике.
Все данные к нашему объекту (те, что задавали в методе Init) помещаются в хранилище ( по сути это и есть объект в строке 310) и, если уже эта функция работает, значит пользователь уже что то изменил. Эти данные я вытаскиваю из этого хранилища (313,314) и готовлю строку, по которой будет сгенерирован мой сплайн (316-321).
При создании нового объекта вносятся необходимые изменения в соответствующие элементы управления поведением и свойствами объекта. Здесь сохранены прежние и это строки 326 – 329.
Возвращает функция уже откорректированный действиями пользователя объект.
В строке 331 стоит обращение к методу, разработанному пользователем, это изменение ориентации нашего сплайна в пространстве. Так в статье 2 при получении нашей звездочки, изображение которой и стало иконкой для нашего плагина я получал произведение 2 –х сплайнов. Для этого они должны лежать в ортогональных друг к другу плоскостях. Там эта ориентация получалась за счет поворота, здесь в менеджере свойств я могу заказать ориентацию. Соответствующий код следующий:
Это статический метод, вызываемый без создания экземпляра класса. Вызывает некоторое удивление строка 247 (неравенство). Дело в том, что это меню типа список с перечислением возможных 3-х значений, сравнение идет по индексу значения в этом списке.
Далее в этом коде идет (закомментировано) создание касательных векторов, обязательной структуры при создании сплайнов. Поскольку фракталы не имеют касательных (установленный математический факт, то выражений для касательных нет) то при создании фрактала эта обязательная структура не создавалась. Это приведет лишь к тому, что некоторые инструменты Cinema 4D c этими фракталами работать не будут. Но наша цель в этой статье продемонстрировать основные принципы создания плагина типа Object.
Метод GetDEnabling
Этот метод вызывается при любых действиях пользователя. Это не очень удобно, так как при помещении отладочных сообщений они забьют своим выводом весь экран командной строки. Основное назначение этого метода разрешить или заблокировать элементы управления по их индексу.
В этом фрагменте кода видно, что для неразработанной, в этом примере плагина, части обработки подстановки для символа b (см. статью 2), я просто заблокировал ввод данных в это поле (элемент управления, строка 340-341) возвращая False. Как правило, здесь (возвращаемое значение) стоит некоторое логическое выражение от доступных в классе значений, фрагмент этого выражения виден на рисунке.
Остальные методы.
Что касается остальных методов, то необходимый для начала работы минимум представлен в таблицах 1 и 2.
Для обработки действий пользователя существенную роль играет структура info, которая играет роль универсального хранилища сообщений, и обработчики событий обращаются к этой структуре для выполнения своих функций.
В строке 119 – 121 мы видим, как в эту структуру происходит запись информации, а в строке 129 использование.
Принцип создания и применения специализированных хранилищ – общий принцип при работе с Cinema 4D. Этот же принцип применен и при работе с обработчиками событий, которые почти всегда обращаются к хранилищу сообщений. В функциях GetHandle и SetHandle присутствует переменная i, которая является индексом обработчика в этом хранилище.
P. S.
- В исходном тексте плагина, прилагаемого к статье, код плагина DoubleCircle, как шаблона, закомментирован и оставлен для того, что бы иметь представление об внесенных изменениях. Кроме того, для желающих получить самостоятельно рабочий плагин дальнейшей доработкой плагина, оставленные (закомментированные) строки могут служить образцом при продолжении работы самостоятельно.
- Работа в режиме отладки, все таки, достаточно некомфортна. Пользование сторонними редакторами (Notepad ++, к примеру) кода во многом предпочтительней, чем редактором Eclipse. Есть одно но, это не сохранение при разметке текста этими редакторами отступа, который в Python имеет синтаксическое значение, хотя при отображении в редакторе отступ наблюдается, но интерпретатор его не опознает. Единственный выход – редактирование в Eclipse.
- Остается добавить, что Cinema 4D очень схожа с универсальным IDE типа Eclipse, также содержит только плагины. Но вероятнее всего этот вариант IDE уже написан на Python и только иногда, при критичности к длительности выполнения того или иного задания, приходится применять библиотеки, написанные на С++ (интерпретатор работает все таки медленнее). В зависимости от набора плагинов и некоторого дополнительного оформления Eclipse превращается NetBeans (со специализацией под конкретный язык) или в Intelli IDEA (и аналогам). Возникает мысль сделать нечто подобное и для графических пакетов на базе Cinema 4D. Но для этого нужен достаточно гибкий язык. Вариант Python 3.xx вполне подходит для этого, особенно со своими возможностями метаклассов. Похоже, что вариант Python 2.6.8 выбран умышленно при реализации Cinema 4D, что бы ограничить самодеятельность.
- Внимательный читатель наверно обнаружил навязчивую привязанность к фракталам. Это не случайно и является главным лейтмотивом всей серии статей. Только фракталы понимаются здесь очень широко, так же, как компиляторы в статье 1. Например, почему бы не создать плагин, который создает другие плагины? Неясно, почему бы это было невозможным.
- Естественно, остались вопросы, основной, на мой взгляд, это принцип работы метода Draw. Возможные варианты отрисовки это DirectX, OpenGL, CUDA. Как подключить или отказаться от того или иного варианта? Но давайте подождем, в предыдущей статье я говорил о необходимости накопления опыта. Эта статья только начало.
На этом все. В следующих статьях мы займемся другими типами плагинов, но к названным вопросам будем постоянно возвращаться по мере продвижения.
P.S. Создается четкое впечатление, что пакет разработан командой люниксоидов и он, пакет Cinema 4D, перенесен на платформу Windows c потерей, даже крайне необходимых возможностей, редактирования и отладки в том числе, не говоря уже о скорости рендеринга. Скорее всего перенос реализован с целью расширения рынка сбыта продуктов Adobe. After Effects прежде всего, совершенно непонятно почему бы функциональные возможности этого пакета не включить в Cinema 4D. Но такова жизнь и политика продаж в нынешних IT.
Рейдер Эдуард Ефимович, к.ф.-м.н