Реконструкция оси горшка с одним осколком через нормали вершин
Планирую сделать реконструкцию целого горшка из одного его осколка.
Я уже придумал, как это сделать, но я действительно не знаю, как это сделать в коде.
Теория: у каждого осколка есть определенный изгиб. Этот изгиб содержит две части информации:
- ориентация осколка внутри горшка
- диаметр горшка. В этом легко убедиться, включив нормали вершин в режиме редактирования. Технически, в процессе изготовления керамики в середине каждого горшка есть центральная ось. Нормали вершин пересекают эту ось, поэтому вы можете четко видеть ось при включении нормалей вершин.
Проблема: как мне восстановить эту ось с помощью скрипта? математически это пересечение нормалей вершин с тонким цилиндром.
Первым шагом было бы выбрать нормали вершин только из определенного выделения, так как мне не нужна обращенная наружу ...
Тестовый осколок
Ответы
Доказательство концепции
В дополнение к комментариям, добавили это как способ доказательства концепции,
Во-первых, вот скрипт, который копирует ваш объект и меш в другой, переходит в режим редактирования и преобразует его в выпуклую оболочку.
Выберите осколок и запустите
import bpy
bpy.ops.object.mode_set()
bpy.ops.object.duplicate(linked=False)
dupe = bpy.context.object
dupe.display_type = 'WIRE'
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.convex_hull()
после чего новый каркас выпуклой оболочки оригинала в режиме редактирования со всей выбранной геометрией.
Следующий скрипт проходит через края корпуса, находит ближайшую точку на сетке к его средней точке, использует их для создания круга из хорды, как описано здесь. Как я могу создать математически правильную дугу / круговой сегмент?
Для визуализации добавили вершину в центре круга и два соединяющихся края. Поскольку данные сохраняются как радиус, координата центра и нормаль (ось вращения - нормализованное векторное произведение двух векторов ребер)
Тестовый скрипт создает предсказанные круговые «клинья» для каждой выбранной кромки. Запускаем с выпуклой сеткой корпуса в режиме редактирования, выбирая интересующие края.
import bpy
import bmesh
from math import asin, degrees
context = bpy.context
scene = context.scene
ob = context.object
me = ob.data
bm = bmesh.from_edit_mesh(me)
shard = scene.objects.get("3D_Scherbe_Model_50K")
#edges = bm.edges[:] # all edges
edges = [e for e in bm.edges if e.select]
#edges = [e for e in bm.select_history if isinstance(e, bmesh.types.BMEdge)]
for edge in edges:
o = (edge.verts[1].co + edge.verts[0].co) / 2
hit, loc, _, _ = shard.closest_point_on_mesh(o)
if hit:
h = (loc - o).length
if h < 0.1:
print("On surface")
continue
a = edge.calc_length() / 2
r = (a * a + h * h) / (2 * h)
if abs(a / r) > 1:
# math domain error on arcsin
print("N/A")
else:
angle = 2 * asin(a / r)
print(f"{r} {degrees(angle)}")
vc = bm.verts.new(o + r * (o - loc).normalized())
for v in edge.verts:
bm.edges.new((v, vc))
bmesh.update_edit_mesh(me)
me.update()
Заметки.
Вместо того, чтобы прогнозировать круг от ближайшей точки к центру края, можно было бы пройти по краю и сделать точки выборки, чтобы https://meshlogic.github.io/posts/jupyter/curve-fitting/fitting-a-circle-to-cluster-of-3d-points/ и https://github.com/ndvanforeest/fit_ellipse как предлагает @RobinBetts.
Аналогичным образом можно использовать сгенерированную оценку круга для проверки фактической поверхности сетки.
Посмотрите на нормаль, возвращенную из ближайшей точки меша.
Сузьте выбор, есть ли исторические данные, которые предлагают радиусы или угол клина в определенном диапазоне.
Спроецируйте (ближайшую точку на сетке) «подхорды» края на сетку равной длины, если бы каждый имел одинаковый радиус и угол, было бы идеально. Сверните для наилучшего соответствия.
Посмотрите на размеры ограничивающей рамки. Если край короче некоторой доли минимального размера bbox, вероятно, это не главная ось горшка. Подумайте о том, чтобы немного сбрить его.
Уничтожение очисткой или сглаживание сетки осколков каким-либо образом.