Режим Скриптування на Python – Python Scripting Mode¶
The Python Scripting mode offers full programmable line stylizes. In this control mode, all styling operations are written as Python scripts referred to as style modules in the Freestyle terminology. The input to a style module is a view map (i.e. a set of detected feature edges), and the output is a set of stylized strokes.
Модуль стилю складається з послідовних викликів п’яти базових операторів: виділення – selection, зчіплювання – chaining, розділення – splitting, сортування – sorting та створення штриха – stroke creation. Оператор виділення – selection ідентифікує піднабір уводу вирізнених країв на основі однієї чи кількох визначених користувачем умов виділення (предикатів, тверджень). Виділені краї обробляються операторами зчіплювання, розділення та сортування для побудови ланцюгів вирізнених країв. Ці оператори також керуються заданими користувачем предикатами та функціями, щоб визначати, як трансформувати вирізнені краї у ланцюги. Нарешті, ланцюги трансформуються у стилізовані штрихи оператором створення штриха, який приймає список визначених користувачем відтінювачів штриха.
Модулі стилю Python зберігаються у межах blend-файлів як блоки даних тексту. Зовнішні файли модулів стилю спершу необхідно завантажити у Редактор Тексту – Text Editor. Далі меню вибору у межах запису стеку модулів стилю дозволяє вам обрати модуль зі списку завантажених модулів стилю.
Freestyle для Blender поставляється з низкою модулів стилю Python, що можуть слугувати відправною точкою для написання вашого власного модуля стилю. Дивіться підрозділ Freestyle Python API у посібнику Blender Python API про деталі конструювання модуля стилю.
Написання модулів стилю¶
Модуль стилю – це шматок коду, відповідальний за стилізацію прорису ліній Freestyle. Увід модуля стилю – це набір вирізнених країв, що називається картою огляду – view map (ViewMap). Вивід – це набір стилізованих ліній, що також відомі, як штрихи. Модуль стилю структурований як конвеєр операцій, що дозволяють вибудовувати штрихи з увідних країв у межах карти огляду.
Існує п’ять видів операцій (перераховані з відповідними функціями оператора):
- Виділення – Selection
Operators.select()
- Зчіплювання – Chaining
Operators.chain(), Operators.bidirectional_chain()
- Розділення – Splitting
Operators.sequential_split(), Operators.recursive_split()
- Сортування – Sorting
Operators.sort()
- Створення штриха – Stroke creation
Operators.create()
Увідна карта огляду заповнюється набором об’єктів ViewEdge. Операція виділення використовується для вибору ViewEdges, що представляють інтерес для митців на основі визначених користувачем умов (предикатів) виділення. Операції зчіплювання приймають піднабір ViewEdges та будують Ланцюги, зчіплюючи ViewEdges відповідно до визначених користувачем предикатів та функцій. Ці Ланцюги можуть бути далі уточнені, розділенням їх на менші шматки (наприклад, у точках, де краї роблять гострий поворот) та виділенням частини їх (наприклад, для збереження тільки тих, що довші, ніж поріг довжини). Операція сортування використовується для впорядкування ланцюгів у стеку для прорису однієї лінії поверхн іншої. Ланцюги фінально трансформуються у стилізовані штрихи операцією створення штрихів шляхом застосування серії відтінювачів штрихів до окремих ланцюгів.
ViewEdges, Chains та Strokes загалом називаються одновимірними (1D) елементами. 1D елемент – це полілінія, що є серією з’єднаних прямих ліній. Вершини 1D елементів називаються 0D елементами, у загальному.
Усі оператори діють на наборі активних 1D елементів. Початковий активний набір – це набір ViewEdges в увідній карті огляду. Активний набір оновлюється операторами.
Виділення – Selection¶
Оператор виділення, вибору проходить через кожен елемент активного набору та зберігає тільки ті, які задовільняють вимоги певного предиката. Метод Operators.select()
приймає як аргумент одноаргументний предикат, що працює на будь-якому Interface1D
, що представляє 1D елемент. Наприклад:
Operators.select(QuantitativeInvisibilityUP1D(0))
Ця операція виділення використовує предикат QuantitativeInvisibilityUP1D
для вибору тільки видимих ViewEdge
(більш точно тих, яких кількісна невидимість дорівнює 0). Оператор виділення наміряється вибірково застосувати стиль до частини активних 1D елементів.
Відзначено, що QuantitativeInvisibilityUP1D
– це клас реалізації предиката, що перевіряє видимість лінії, а метод Operators.select()
приймає примірник класу предиката як аргумент. Перевірка предиката для даного 1D елемента фактично робиться викликом примірника предикати, тобто, залучаючи метод __call__
класу предиката. Іншими словами, метод Operators.select()
приймає як аргумент функтор – functor, який, у свою чергу, приймає об’єкт Interface0D
як аргумент. Freestyle Python API широко вживає функтори для реалізації предикатів, а також функцій.
Зчіплювання – Chaining¶
Оператори зчіплювання діють на наборі активних об’єктів ViewEdge
та визначають топологію майбутніх штрихів. Ідея полягає в реалізації повторювача – iterator для проходження графа ViewMap шляхом пересування уздовж ViewEdges. Повторювач визначає правило зчіплювання, яке визначає наступний ViewEdge
для слідування у задану вершину (дивіться ViewEdgeIterator
). Кілька таких повторювачів надаються як частина Freestyle Python API (дивіться ChainPredicateIterator
та ChainSilhouetteIterator
). Власні користувацькі повторювачі можуть визначатися успадковуванням класу ViewEdgeIterator
. Оператор зчіплювання також приймає як аргумент UnaryPredicate, що працює на Interface1D
, як критерій зупинки. Зчіплювання зупиняється, коли повторювач досяг задоволення ViewEdge
умов цього предикату у ході пересування по графу.
Зчіплювання може бути однонапрямним Operators.chain()
або двонапрямним Operators.bidirectional_chain()
. В останньому випадку, зчіплювання буде поширюватися у двох напрямках від стартового краю.
Наступне є прикладом коду двонапрямного зчіплювання:
Operators.bidirectional_chain(
ChainSilhouetteIterator(),
NotUP1D(QuantitativeInvisibilityUP1D(0)),
)
Оператор зчіплювання використовує ChainSilhouetteIterator
як правило зчіплювання та зупиняє зчіплювання зразу ж, як повторювач прийшов до невидимого ViewEdge
.
Оператори зчіплювання обробляють набір активних об’єктів ViewEdge
за порядком. Активні ViewEdges можуть попередньо бути сортовані за допомогою методу Operators.sort()
(дивіться нижче). Він запускає ланцюг з першого ViewEdge
активного набору. Усі ViewEdges, що вже були залучені у процес зчіплювання, позначаються (у випадку прикладу вище, штамп часу кожного ViewEdge
модифікується стандартно), щоб не обробляти двічі один і той же ViewEdge
. Після того, як зчіплювання досягає ViewEdge
, що задовільняє умовам предиката зупинки, ланцюг завершується. Далі новий ланцюг починається з першого непозначеного ViewEdge
в активному наборі. Ця операція повторюється, допоки останній непозначений ViewEdge
з активного набору не буде оброблено. У кінці операції зчіплювання, активний набір – це набір Ланцюгів, що були побудовані.
Розділення – Splitting¶
Операція розділення використовується для уточнення топології кожного Ланцюга. Розділення здійснюється послідовно або рекурсивно. Послідовне розділення Operators.sequentialSplit()
у своїй базовій формі, аналізує Ланцюг у заданій довільній роздільності та оцінює одноаргументний предикат (працюючи на 0D елементах) у кожній точці уздовж цього Ланцюга. Кожен раз, при задоволенні предиката, ланцюг розділюється на два ланцюги. У кінці операції послідовного розділення активний набір ланцюгів є набором нових ланцюгів.
Operators.sequentialSplit(TrueUP0D(), 2)
In this example, the chain is split every 2 units.
A more elaborated version uses two predicates instead of one: One to determine the starting
point of the new chain and the other to determine its ending point. This second version can
lead to a set of Chains that are disjoint or that overlap if the two predicates are different
(see Operators.sequentialSplit()
for more details).
Рекурсивне розділення Operators.recursiveSplit()
оцінює функцію на 0D елементах уздовж Ланцюга у заданій роздільності та знаходить точку, що дає максимальне значення для цієї функції. Ланцюг далі розділяється на два у цій точці. Цей процес рекурсивно повторюється на кожному з двох нових Ланцюгів, допоки увідний Ланцюг не буде задовольняти визначеній користувачем умові зупинення.
func = Curvature2DAngleF0D()
Operators.recursive_split(func, NotUP1D(HigherLengthUP1D(5)), 5)
У прикладі коду вище, Ланцюги рекурсивно розділяються у точках найвищої 2D кривини. Ця кривина оцінюється у точках уздовж Ланцюга з роздільністю 5 одиниць. Ланцюги, коротші, ніж 5 одиниць, не будуть більше розділятися.
Сортування – Sorting¶
Оператор сортування Operators.sort()
впорядковує порядок нашарування активних 1D елементів. Він приймає як аргумент двоаргументний предикат, використовуваний як оператор «менше ніж» до порядку 1D елементів.
Operators.sort(Length2DBP1D())
У цьому прикладі коду, сортування використовує двоаргументний предикат Length2DBP1D
для сортування об’єктів Interface1D
у висхідному порядку у термінах 2D довжини.
Сортування особливо корисне, коли комбінується з щільністю причин. Дійсно, щільність причин оцінює щільність результатного зображення при його модифікації. Якщо ми бажаємо використати такий інструмент для вирішення того, що слід вилучити штрихи кожен раз, коли локальна щільність є надто високою, то важливо контролювати порядок, в якому ці штрихи прорисовуються. У цьому випадку, ми скористаємося оператором сортування для гарантування, що більш «важливі» лінії прорисуються першими.
Створення Штриха – Stroke Creation¶
Фінально, оператор створення штрихів Operators.create()
приймає активний набір Ланцюгів як увід та будує Штрихи. Цей оператор приймає два аргументи. Перший – це одноаргументний предикат, що працює на Interface1D
, який призначений для зроблення останнього виділення на наборі ланцюгів. Ланцюг, що не задовільняє цій умові, не перейде далі у Штрих. Другий увід – це список відтінювачів, що будуть відповідати за відтінення кожного побудованого штриха.
shaders_list = [
SamplingShader(5.0),
ConstantThicknessShader(2),
ConstantColorShader(0.2,0.2,0.2,1),
]
Operators.create(DensityUP1D(8,0.1, IntegrationType.MEAN), shaders_list)
У цьому прикладі, предикат DensityUP1D
використовується для вилучення усіх Ланцюгів, які мають середню щільність, вище за 0.1. Кожен ланцюг трансформується у штрих шляхом повторного відбору так, щоб мати точку через кожні 5 одиниць, призначення для нього постійної товщини у 2 одиниці та темно-сірого постійного кольору.
Керування користувачем конвеєром визначення¶
Написання модуля стилю пропонує різні типи керування користувачем, навіть хоча окремі модулі стилю мають фіксовану структуру конвеєра. Одним з них є задання послідовності різних керувальних структур конвеєра, а іншим – через визначення об’єктів функторів, що передаються як аргумент уздовж усього конвеєра.
Різні структури керування конвеєром можуть бути визначені заданням операцій виділення, зчіплювання, розділення та сортування. Створення штриха є завжди останньою операцією, що завершує модуль стилю.
Предикати, функції, повторювачі зчіплювання та відтінювачі штрихів можуть визначатися успадковуванням базових класів та перевизначенням відповідних методів. Для отримання додаткової інформації про визначувані користувачем конструкції дивіться відповідні записи про наступні базові класи.
Дивись також
Predicates, functions, chaining iterators, and stroke shaders can be defined by inheriting
base classes and overriding appropriate methods. See Freestyle python module
for more information on the user-scriptable constructs.