Режим Скриптування на Python – Python Scripting Mode¶
Режим Скриптування на Python пропонує повністю програмовані стилізування ліній. У цьому режимі керування усі операції стилізування пишуться як скрипти на Python, що відомі як модулі стилів – style module у термінології Freestyle. Увід у модуль стилю – це розкладка огляду (тобто набір виявлених вирізнених країв), а вивід – це набір стилізованих штрихів.
Модуль стилю складається з послідовних викликів п’яти базових операторів: вибрання – selection, зчіплювання – chaining, розділювання – splitting, сортування – sorting та створення штриха – stroke creation. Оператор вибрання ідентифікує піднабір уводу вирізнених країв на основі однієї чи кількох визначених користувачем умов вибрання (предикатів). Вибрані краї обробляються операторами зчіплювання, розділювання та сортування для побудови ланцюгів вирізнених країв. Ці оператори також керуються заданими користувачем предикатами та функціями, щоб визначати, як трансформувати вирізнені краї у ланцюги. Нарешті, ланцюги трансформуються у стилізовані штрихи оператором створення штриха, який приймає список визначених користувачем шейдерів штриха.
Модулі стилю Python зберігаються у межах blend-файлів як блоки даних тексту. Зовнішні файли модулів стилю спершу необхідно завантажити у Редактор Тексту – Text Editor. Далі меню вибору у межах запису стеку модулів стилю дозволяє вам обрати модуль зі списку завантажених модулів стилю.
Freestyle для Blender поставляється з низкою модулів стилю Python, що можуть слугувати як відправна точка для написання вашого власного модуля стилю. Дивіться підрозділ Freestyle Python API у посібнику Blender Python API про деталі конструювання модуля стилю.
Написання Модулів Стилів – Writing Style Modules¶
Модуль стилю – це шматок коду, відповідальний за стилізацію прорису ліній 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)
У цьому прикладі, ланцюг розділюється кожні 2 одиниці. Більш розроблена версія використовує два предикати замість одного: один для визначення стартової точки нового ланцюга та другий для визначення його кінцевої точки. Ця друга версія може призвести до набору Ланцюгів, що є роз’єднаними або що перекриваються, якщо два ці предикати є різними (дивіться детальніше 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 одиниці та темно-сірого постійного кольору.
Керування Користувачем Визначенням Конвеєра – User Control on the Pipeline Definition¶
Написання модуля стилю пропонує різні типи керування користувачем, навіть хоча окремі модулі стилю мають фіксовану структуру конвеєра. Одним з них є секвенування різних керувальних структур конвеєра, а іншим – через визначення об’єктів функторів, що передаються як аргумент уздовж усього конвеєра.
Різні структури керування конвеєром можуть бути визначені заданням операцій вибрання, зчіплювання, розділювання та сортування. Створення штриха є завжди останньою операцією, що завершує модуль стилю.
Предикати, функції, повторювачі зчіплювання та шейдери штрихів можуть визначатися успадковуванням базових класів та перевизначенням відповідних методів. Для отримання додаткової інформації про визначувані користувачем конструкції дивіться відповідні записи про наступні базові класи.
Дивись також
Предикати, функції, повторювачі зчіплювання та шейдери штрихів можуть визначатися успадковуванням базових класів та перевизначенням відповідних методів. Дивіться детальніше Freestyle python module
про скриптовані користувачем конструкти.