Объект BMesh безусловно обладает большей функциональностью и возможностями для работы с данными мешей по сравнении со стандартным набором данных, хранящимся в свойстве data объекта. Однако некоторые моменты управления данными меша в объекте BMesh не всегда очевидны. Рассмотрим, на пример, достаточно тривиальная операция по получению текущего активного полигона.
Пусть текущий активный меш у нас находится в режиме редактирования. Если он в объектном режиме — переключим его.
|
1 |
bpy.ops.object.mode_set(mode='EDIT') |
Переключимся на выделение полигонов.
|
1 2 |
bpy.context.tool_settings.mesh_select_mode = (False, False, True) bpy.context.object.data.update() |
Создадим объект BMesh, перекинем в него данные из текущего активного меша и проведем балансировку индексов полигонов.
|
1 2 |
bm = bmesh.from_edit_mesh(bpy.context.object.data) bm.faces.ensure_lookup_table() |
Текущий активный полигон в BMesh мы можем получить через список bm.select_history. В нем хранится вся история выделений для текущего объекта BMesh. Последний выделенный полигон всегда будет считаться активным, но лучше получать его через указатель списка active.
|
1 2 3 4 5 6 7 |
print(bm.select_history[:]) # [<BMFace(0x00000206077B3628), index=5, totverts=4>, <BMFace(0x00000206077B35B8), index=3, totverts=4>] print(bm.select_history.active) # <BMFace(0x00000206077B35B8), index=3, totverts=4> |
Кажется, все просто, но здесь есть несколько неочевидных моментов.
Если мы выделяем еще один какой-нибудь полигон в BMesh, он не попадает автоматически в список истории и не становится, соответственном, активным.
|
1 2 3 4 5 |
bm.faces[0].select_set(True) bm.select_flush_mode() print(bm.select_history[:]) # [<BMFace(0x00000206077B3628), index=5, totverts=4>, <BMFace(0x00000206077B35B8), index=3, totverts=4>] |
Это нужно делать вручную, удалив его из списка истории, и добавив заново.
|
1 2 3 4 5 6 7 8 9 |
bm.select_history.remove(bm.select_history.active) bm.select_history.add(bm.faces[0]) print(bm.select_history[:]) # [<BMFace(0x00000206077B34A8), index=5, totverts=4>, <BMFace(0x00000206077B3390), index=0, totverts=4>] print(bm.select_history.active) # <BMFace(0x00000206077B3390), index=0, totverts=4> |
Об этом нужно помнить, манипулируя выделением в объекте BMesh.
Также нужно учитывать, что при переключении режима выделения полигоны-ребра-точки пользователем, история выделений в объекте BMesh сбрасывается.
Переключив режим выбора, например, на ребра и обратно на полигоны, мы получим пустой список выделения в BMesh.
|
1 2 3 4 5 6 7 |
print(bm.select_history[:]) # [] print(bm.select_history.active) # None |
Мы получаем пустой список, в то время, как выделения все еще присутствуют на меше. Это тоже надо учитывать.
Есть еще один вариант получения активного полигона в BMesh — через индекс активного полигона в основном блоке data меша.
|
1 2 3 4 5 6 7 |
active_face_id = bpy.context.object.data.polygons.active # 3 bm_active_face = next((_face for _face in bm.faces if _face.index == active_face_id), None) # <BMFace(0x00000206077B2EB8), index=3, totverts=4> |
Сначала мы просто берем индекс текущего активного полигона в меше, а потом получаем полигон в BMesh с этим индексом. Он, соответственно, и должен быть активным полигоном.
Однако у этого метода тоже есть особенность, если в процессе работы с BMesh мы намеренно меняем активный полигон, как это было показано выше, этим способом мы все равно получим тот полигон, который был активным в исходном блоке data меша а не установленный нами. С другой стороны — это всегда будет тот полигон, который пользователь видит активным в окне 3D вьюпорта.

.blend file on Patreon