@@ -0,0 +1,101 @@
|
||||
import bpy
|
||||
import numpy as np
|
||||
from bpy.types import Operator, Context
|
||||
from typing import Set
|
||||
from ...core.translations import t
|
||||
from ...core.logging_setup import logger
|
||||
from ...core.common import get_active_armature, get_all_meshes
|
||||
from ...core.armature_validation import validate_armature
|
||||
|
||||
import bmesh
|
||||
|
||||
|
||||
class MapItem():
|
||||
length: int
|
||||
current_node: bmesh.types.BMVert
|
||||
marched_paths: list[bmesh.types.BMEdge]
|
||||
|
||||
class AvatarToolkit_OT_SelectShortestSeamPath(Operator):
|
||||
"""Find the shortest seam path between two vertices."""
|
||||
bl_idname = "avatar_toolkit.find_shortest_seam_path"
|
||||
bl_label = t("Tools.find_shortest_seam_path")
|
||||
bl_description = t("Tools.find_shortest_seam_path_desc")
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context: Context) -> bool:
|
||||
if context.mode != "EDIT_MESH":
|
||||
return False
|
||||
mesh_data: bpy.types.Mesh = context.active_object.data
|
||||
mesh = bmesh.from_edit_mesh(mesh_data)
|
||||
selected: int = 0
|
||||
for vert in mesh.verts:
|
||||
if vert.select == True:
|
||||
selected = selected+1
|
||||
if selected > 2:
|
||||
return False
|
||||
found_seam: bool = False
|
||||
for edge in vert.link_edges:
|
||||
if edge.seam:
|
||||
found_seam = True
|
||||
if not found_seam:
|
||||
return False
|
||||
if selected < 2:
|
||||
return False
|
||||
armature = get_active_armature(context)
|
||||
if not armature:
|
||||
return False
|
||||
valid, _, _ = validate_armature(armature)
|
||||
return valid
|
||||
|
||||
def execute(self, context: Context) -> Set[str]:
|
||||
mesh_data: bpy.types.Mesh = context.active_object.data
|
||||
mesh = bmesh.from_edit_mesh(mesh_data)
|
||||
vert1: bmesh.types.BMVert = None
|
||||
vert2: bmesh.types.BMVert = None
|
||||
for vert in mesh.verts:
|
||||
if vert.select == True:
|
||||
if vert1 == None:
|
||||
vert1 = vert
|
||||
else:
|
||||
vert2 = vert
|
||||
|
||||
current_verts: list[MapItem] = []
|
||||
|
||||
first_item: MapItem = MapItem()
|
||||
first_item.current_node = vert1
|
||||
first_item.length = 0
|
||||
first_item.marched_paths = []
|
||||
current_verts.append(first_item)
|
||||
|
||||
def find_next_edge() -> list[bmesh.types.BMEdge]:
|
||||
if len(current_verts) == 0: #all paths have been exausted.
|
||||
return []
|
||||
for mapeditem in current_verts:
|
||||
current_verts.remove(mapeditem)
|
||||
for edge in mapeditem.current_node.link_edges:
|
||||
if edge.seam and (edge not in mapeditem.marched_paths):
|
||||
for vert_new in edge.verts:
|
||||
if vert_new != mapeditem.current_node:
|
||||
if vert_new == vert2:
|
||||
mapeditem.marched_paths.append(edge)
|
||||
return mapeditem.marched_paths
|
||||
first_item: MapItem = MapItem()
|
||||
first_item.current_node = vert_new
|
||||
first_item.length = mapeditem.length+1
|
||||
first_item.marched_paths = []
|
||||
first_item.marched_paths.extend(mapeditem.marched_paths)
|
||||
first_item.marched_paths.append(edge)
|
||||
current_verts.append(first_item)
|
||||
return find_next_edge()
|
||||
|
||||
mesh.select_flush(False)
|
||||
path: list[bmesh.types.BMEdge] = find_next_edge()
|
||||
for edge in path:
|
||||
edge.select = True
|
||||
for vert in edge.verts:
|
||||
vert.select = True
|
||||
bpy.ops.mesh.select_mode(type='EDGE')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
Reference in New Issue
Block a user