Reconstruindo o eixo do vaso com um fragmento via vértice normais
Estou planejando fazer a reconstrução de um pote inteiro a partir de um fragmento dele.
Já pensei em uma maneira de fazer isso, mas realmente não sei como fazer isso acontecer em código.
Teoria: cada fragmento tem uma certa curvatura. Esta curva contém duas informações:
- a orientação do fragmento dentro do pote
- o diâmetro do pote. Você pode ver isso facilmente ao ativar os normais de vértice no modo de edição. Tecnicamente, desde o processo de fabricação da cerâmica, existe um eixo central no meio de cada vaso. As normais do vértice cruzam este eixo, então você pode ver o eixo claramente ao ativar as normais do vértice.
Problema: como reconstruo esse eixo via script? matematicamente, é a interseção das normais do vértice com um cilindro fino.
O primeiro passo seria selecionar as normais do vértice de uma certa seleção apenas, já que não preciso da voltada para fora ...
Fragmento de Teste
Respostas
Prova de conceito
Além de comentar, adicionamos isso como uma forma de prova de conceito,
Fistly aqui está um script que copia seu objeto e malha para outro, entra no modo de edição e o converte em um casco convexo.
Selecione o fragmento e execute
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()
após o qual nova estrutura de arame do casco convexo do original no modo de edição com toda a geometria selecionada.
O próximo script passa pelas bordas do casco, encontra o ponto mais próximo na malha ao seu ponto médio, usa isso para criar um círculo a partir da corda, conforme descrito aqui. Como posso criar um arco / segmento circular matematicamente correto?
Para visualizar foi adicionado um vert no centro do círculo e as duas bordas de união. Como os dados seriam salvos como raio, coordenada central e normal (eixo de rotação, o produto vetorial normalizado de dois vetores de borda)
O script de teste cria "cunhas" de círculo previstas para cada aresta selecionada. Execute com malha de casco convexa no modo de edição, arestas de interesse selecionadas.
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()
Notas.
Em vez de prever um círculo do ponto mais próximo ao centro da borda, poderia caminhar na borda e fazer pontos de amostra para triturar https://meshlogic.github.io/posts/jupyter/curve-fitting/fitting-a-circle-to-cluster-of-3d-points/ e https://github.com/ndvanforeest/fit_ellipse como sugerido por @RobinBetts.
Da mesma forma, poderia usar a estimativa do círculo gerada para testar contra a superfície da malha real.
Observe o valor normal retornado do ponto mais próximo da malha.
Para restringir a seleção, existem dados históricos que sugerem raios ou ângulos de cunha dentro de um determinado intervalo.
Projete (ponto mais próximo na malha) "subcordas" de comprimento igual da aresta na malha, se cada uma tiver o mesmo raio e ângulo seria uma combinação perfeita. Minimize para melhor ajuste.
Observe as dimensões da caixa delimitadora. Se uma borda for mais curta do que alguma fração da dimensão mínima do bbox, provavelmente não é o eixo principal do pote. Considere cortar alguma porcentagem.
Eliminar a limpeza ou alisar a malha de fragmentos de alguma forma.