# -*- coding: utf-8 -*-
from __future__ import annotations
from typing import Callable, List
import bmesh
import bpy
from bmesh.types import BMEdge, BMFace, BMVert
from mathutils import Vector
from PyR3.shortcut.context import Objects
[docs]class OperationCancelled(Exception):
pass
[docs]def manual_set_edit_mode():
bpy.ops.object.mode_set(mode="EDIT")
[docs]def manual_set_object_mode():
bpy.ops.object.mode_set(mode="OBJECT")
[docs]class Edit:
"""class for automatic in-out switching Edit mode.
It is meant to be used as context manager with edited object being
passed as param to constructor.
"""
_is_edit_mode: bool = False
BMESH: bmesh.bmesh.types.BMesh = None
ob: bpy.types.Object
[docs] class MeshCompList(list):
[docs] def selected(self) -> Edit.MeshCompList:
return Edit.MeshCompList(
element for element in self if element.select
)
def __init__(
self,
ob: bpy.types.Object,
*more: bpy.types.Object,
active: bpy.types.Object = None,
) -> None:
self.ob = ob
self.active = active
self.more = more
[docs] @staticmethod
def isEditMode():
return bpy.context.object.mode == "EDIT"
def __enter__(self) -> Edit:
"""Enters edit mode, selects everything in there."""
if self.active is None:
Objects.active = self.ob
else:
Objects.active = self.active
Objects.select_only(self.ob)
Objects.select(*self.more)
manual_set_edit_mode()
Edit._is_edit_mode = True
self.BMESH = bmesh.from_edit_mesh(self.ob.data)
self.select_all()
return self
def __exit__(self, class_, instance, traceback) -> None:
"""Return to object mode."""
manual_set_object_mode()
Edit._is_edit_mode = False
[docs] def faces(self) -> MeshCompList[BMFace]:
"""Provides access to edited object bmesh attribute holding reference
to list of all faces of edited mesh.
:return: List of faces.
:rtype: MeshCompList[BMFace]
"""
self.BMESH.faces.ensure_lookup_table()
return Edit.MeshCompList(self.BMESH.faces)
[docs] def edges(self) -> MeshCompList[BMEdge]:
"""Provides access to edited object bmesh attribute holding reference
to list of all edges of edited mesh.
:return: List of edges.
:rtype: MeshCompList[BMEdge]
"""
self.BMESH.faces.ensure_lookup_table()
return Edit.MeshCompList(self.BMESH.edges)
[docs] def vertices(self) -> MeshCompList[BMVert]:
"""Access to edited object bmesh vertice table.
:return: Vertices
:rtype: MeshCompList[BMVert]
"""
self.BMESH.verts.ensure_lookup_table()
return Edit.MeshCompList(self.BMESH.verts)
[docs] def get_selected_vertices(self) -> List[BMVert]:
return [v for v in self.vertices() if v.select]
[docs] def select_vertices(
self,
condition: Callable[[Vector], bool],
):
"""Selects vertices, when condition function returns true.
:param condition: test callable. It will be given vertice coordinate as parameter.
:type condition: Callable[Vector], bool]
"""
for v in self.vertices():
if condition(v.co):
v.select = True
[docs] def select_edges(
self,
condition: Callable[[Vector, Vector], bool],
):
"""Selects edges, when condition function returns true.
:param condition: Test callable. It will be given edge vertice coordinate as parameter.
:type condition: Callable[[Vector, Vector], bool]
"""
for e in self.edges():
if condition(e.verts[0].co, e.verts[1].co):
e.select = True
[docs] def select_facing(self, direction: Vector) -> Edit:
if not isinstance(direction, Vector):
direction = Vector(direction)
for face in self.faces():
if face.normal.dot(direction) == 1:
face.select = True
return self
[docs] def select_all(self):
"""Selects whole mesh."""
bpy.ops.mesh.select_all(action="SELECT")
[docs] def deselect_all(self):
"""Deselects whole mesh."""
bpy.ops.mesh.select_all(action="DESELECT")
[docs] def invert_selection(self):
"""Invertices selection of mesh components."""
bpy.ops.mesh.select_all(action="INVERT")
[docs] def delete_vertices(self):
"""Delete selected vertices."""
bpy.ops.mesh.delete(type="VERT")
[docs] def delete_edges(self):
"""Delete selected edges."""
bpy.ops.mesh.delete(type="EDGE")
[docs] def delete_faces(self):
"""Delete selected faces."""
bpy.ops.mesh.delete(type="FACE")
[docs] def duplicate(self, mode: int = 1):
"""Duplicate selected."""
bpy.ops.mesh.duplicate(mode=mode)
# methods from blender API, moved here for easier access
bevel = bpy.ops.mesh.bevel
extrude = bpy.ops.mesh.extrude_region
extrude_repeat = bpy.ops.mesh.extrude_repeat
extrude_individual_faces = bpy.ops.mesh.extrude_edges_indiv
edge_face_add = bpy.ops.mesh.edge_face_add
collapse = bpy.ops.mesh.edge_collapse
remove_doubles = bpy.ops.mesh.remove_doubles
normals_make_consistent = bpy.ops.mesh.normals_make_consistent