Для работы с геометрией в Blender через Python API часто бывает нужно определить центральную точку полигона. Это может понадобиться для организации различных привязок, выравнивания объектов по полигону, позиционирования и во многих других случаях.
Для примера возьмем простейший случай – плоскость (shift + a – Mesh – Plane), которая состоит из одного полигона. Пусть она будет активным объектом сцены.
1 2 3 |
active_object = bpy.context.active_object # bpy.data.objects['Plane'] |
Найдем координаты центра полигона плоскости.
Указатель на единственный полигон, составляющий нашу плоскость мы можем получить через свойство data.polygons объекта. А через полигон мы можем получить указатели на угловые вертексы, которые собственно его образовывают.
1 2 3 4 5 |
bpy.context.object.data.polygons[0] # bpy.data.meshes['Plane'].polygons[0] bpy.context.object.data.polygons[0].vertices[:] # (0, 1, 3, 2) |
Однако таким образом мы получаем не указатели на сами вертексы, а только их индексы. Чтобы перейти от индексов к самим вертексам обратимся к свойству vertices объекта. Координаты вертексов мы получаем через их свойство “co”.
1 2 3 |
vertices_co = [bpy.context.active_object.data.vertices[v_id].co for v_id in bpy.context.active_object.data.polygons[0].vertices] # [Vector((-1.0, -1.0, 0.0)), Vector((1.0, -1.0, 0.0)), Vector((1.0, 1.0, 0.0)), Vector((-1.0, 1.0, 0.0))] |
Полученные нами координаты являются локальными. Чтобы перейти к глобальным координатам, нам нужно умножить их на мировую матрицу трансформации объекта.
1 2 3 |
vertices_co_global = [active_object.matrix_world @ _co for _co in vertices_co] # [Vector((2.3434910774230957, -1.6085408926010132, 1.797777771949768)), Vector((4.28888463973999, -1.1446053981781006, 1.8122127056121826)), Vector((3.869182586669922, 0.5870418548583984, 2.720644235610962)), Vector((1.9237890243530273, 0.12310633063316345, 2.706209421157837))] |
Теперь давайте определим функцию, которая будет получать на вход список координат и возвращать высчитанную для них центральную точку.
1 2 3 4 5 6 7 8 9 |
def centroid(vertices): x_list = [vertex[0] for vertex in vertices] y_list = [vertex[1] for vertex in vertices] z_list = [vertex[2] for vertex in vertices] length = len(vertices) x = sum(x_list) / length y = sum(y_list) / length z = sum(z_list) / length return Vector((x, y, z)) |
В функции мы сначала суммируем все координаты отдельно по каждой из координатных осей, а затем делим на количество точек. Среднее значение по каждой из осей дает нам в целом центральную точку полигона, которую мы возвращаем в виде вектора.
Чтобы получить центральную точку нашего полигона, вызовем функцию и передадим в нее полученные ранее координаты вертексов.
1 2 3 |
center = centroid(vertices_co_global) # <Vector (3.1063, -0.5107, 3.1063)> |
Для проверки, поместим 3D-курсор в точку с полученными координатами.
1 |
bpy.context.scene.cursor.location = center |
Как мы видим, курсор переместился точно в центр плоскости.
Бонус: центр полигона в 2D пространстве
Если нам нужно определить центр полигона в двухмерном пространстве, например, при работе с UV-разверткой, мы можем элементарно модифицировать нашу функцию. Нужно просто убрать из расчетов координаты по оси Z.
1 2 3 4 5 6 7 |
def centroid2d(vertices): x_list = [vertex[0] for vertex in vertices] y_list = [vertex[1] for vertex in vertices] length = len(vertices) x = sum(x_list) / length y = sum(y_list) / length return Vector((x, y)) |