2019-06-08 23:42:50 +00:00
import bpy
import os
import bmesh
import math
import operator
from mathutils import Vector
from collections import defaultdict
from itertools import chain # 'flattens' collection of iterables
from . import utilities_uv
class op ( bpy . types . Operator ) :
2019-12-18 20:53:16 +00:00
bl_idname = " uv.textools_island_mirror "
bl_label = " Symmetry "
bl_description = " Mirrors selected faces to other half or averages based on selected edge center "
bl_options = { ' REGISTER ' , ' UNDO ' }
is_stack : bpy . props . BoolProperty ( description = " Stack the halves on top of each other? " , default = False )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
@classmethod
def poll ( cls , context ) :
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
if not bpy . context . active_object :
return False
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
#Only in Edit mode
if bpy . context . active_object . mode != ' EDIT ' :
return False
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
#Only in UV editor mode
if bpy . context . area . type != ' IMAGE_EDITOR ' :
return False
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
#Requires UV map
if not bpy . context . object . data . uv_layers :
return False
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
if bpy . context . scene . tool_settings . use_uv_select_sync :
return False
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
if bpy . context . scene . tool_settings . uv_select_mode != ' EDGE ' and bpy . context . scene . tool_settings . uv_select_mode != ' FACE ' :
return False
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# if bpy.context.scene.tool_settings.use_uv_select_sync:
# return False
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
return True
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
def execute ( self , context ) :
main ( context )
return { ' FINISHED ' }
2019-06-08 23:42:50 +00:00
def main ( context ) :
2019-12-18 20:53:16 +00:00
print ( " --------------------------- Executing operator_mirror " )
#Store selection
utilities_uv . selection_store ( )
bm = bmesh . from_edit_mesh ( bpy . context . active_object . data )
uv_layers = bm . loops . layers . uv . verify ( )
if bpy . context . scene . tool_settings . uv_select_mode == ' EDGE ' :
# 1.) Collect left and right side verts
verts_middle = [ ] ;
for face in bm . faces :
if face . select :
for loop in face . loops :
if loop [ uv_layers ] . select and loop . vert not in verts_middle :
verts_middle . append ( loop . vert )
# 2.) Align UV shell
alignToCenterLine ( )
# Convert to Vert selection and extend edge loop in 3D space
bpy . ops . mesh . select_mode ( use_extend = False , use_expand = False , type = ' VERT ' )
bpy . ops . mesh . select_all ( action = ' DESELECT ' )
for vert in verts_middle :
vert . select = True
bpy . ops . mesh . select_mode ( use_extend = True , use_expand = False , type = ' EDGE ' )
bpy . ops . mesh . loop_multi_select ( ring = False )
for vert in bm . verts :
if vert . select and vert not in verts_middle :
print ( " Append extra vert to symmetry line from xyz edge loop " )
verts_middle . append ( vert )
# Select UV shell Again
bpy . ops . mesh . select_linked ( delimit = { ' UV ' } )
verts_island = [ ]
for vert in bm . verts :
if vert . select :
verts_island . append ( vert )
# 3.) Restore UV vert selection
x_middle = 0
bpy . ops . uv . select_all ( action = ' DESELECT ' )
for face in bm . faces :
if face . select :
for loop in face . loops :
if loop . vert in verts_middle :
loop [ uv_layers ] . select = True
x_middle = loop [ uv_layers ] . uv . x ;
print ( " Middle " + str ( len ( verts_middle ) ) + " x, x pos: " + str ( x_middle ) )
# Extend selection
bpy . ops . uv . select_more ( )
verts_A = [ ] ;
verts_B = [ ] ;
for face in bm . faces :
if face . select :
for loop in face . loops :
if loop [ uv_layers ] . select and loop . vert not in verts_middle :
if loop [ uv_layers ] . uv . x < = x_middle :
# Left
if loop . vert not in verts_A :
verts_A . append ( loop . vert )
elif loop [ uv_layers ] . uv . x > x_middle :
# Right
if loop . vert not in verts_B :
verts_B . append ( loop . vert )
def remove_doubles ( ) :
verts_double = [ vert for vert in verts_island if ( vert in verts_A and vert in verts_B ) ]
# print("Temp double: "+str(len(verts_double))+"x")
if len ( verts_double ) > 0 :
print ( " TODO: Remove doubles " + str ( len ( verts_double ) ) )
for vert in verts_double :
verts_A . remove ( vert )
verts_B . remove ( vert )
verts_middle . append ( vert )
def extend_half_selection ( verts_middle , verts_half , verts_other ) :
# Select initial half selection
bpy . ops . uv . select_all ( action = ' DESELECT ' )
for face in bm . faces :
if face . select :
for loop in face . loops :
if loop . vert in verts_half :
loop [ uv_layers ] . select = True
# Extend selection
bpy . ops . uv . select_more ( )
# count_added = 0
for face in bm . faces :
if face . select :
for loop in face . loops :
if loop . vert not in verts_half and loop . vert not in verts_middle and loop [ uv_layers ] . select :
verts_half . append ( loop . vert )
remove_doubles ( )
# Limit iteration loops
max_loops_extend = 200
for i in range ( 0 , max_loops_extend ) :
print ( " Now extend selection A / B " )
count_hash = str ( len ( verts_A ) ) + " _ " + str ( len ( verts_B ) ) ;
extend_half_selection ( verts_middle , verts_A , verts_B )
extend_half_selection ( verts_middle , verts_B , verts_A )
remove_doubles ( )
count_hash_new = str ( len ( verts_A ) ) + " _ " + str ( len ( verts_B ) ) ;
if count_hash_new == count_hash :
print ( " Break loop, same as previous loop " )
break ;
print ( " Edge, Sides: L: " + str ( len ( verts_A ) ) + " | R: " + str ( len ( verts_B ) ) )
# 4.) Mirror Verts
mirror_verts ( verts_middle , verts_A , verts_B , False )
if bpy . context . scene . tool_settings . uv_select_mode == ' FACE ' :
# 1.) Get selected UV faces to vert faces
selected_faces = [ ]
for face in bm . faces :
if face . select :
# Are all UV faces selected?
countSelected = 0
for loop in face . loops :
if loop [ uv_layers ] . select :
countSelected + = 1
# print("Vert selected "+str(face.index))
if countSelected == len ( face . loops ) :
selected_faces . append ( face )
# if bpy.context.scene.tool_settings.use_uv_select_sync == False:
bpy . ops . uv . select_linked ( )
verts_all = [ ]
for face in bm . faces :
if face . select :
for loop in face . loops :
if ( loop . vert not in verts_all ) :
verts_all . append ( loop . vert )
print ( " Verts shell: " + str ( len ( verts_all ) ) )
bpy . ops . mesh . select_all ( action = ' DESELECT ' )
for face in selected_faces :
face . select = True
# 2.) Select Vert shell's outer edges
bpy . ops . mesh . select_linked ( delimit = set ( ) )
bpy . ops . mesh . select_mode ( use_extend = False , use_expand = False , type = ' EDGE ' )
bpy . ops . mesh . region_to_loop ( )
edges_outer_shell = [ e for e in bm . edges if e . select ]
# 3.) Select Half's outer edges
bpy . ops . mesh . select_mode ( use_extend = False , use_expand = False , type = ' FACE ' )
bpy . ops . mesh . select_all ( action = ' DESELECT ' )
for face in selected_faces :
face . select = True
bpy . ops . mesh . select_mode ( use_extend = False , use_expand = False , type = ' EDGE ' )
bpy . ops . mesh . region_to_loop ( )
edges_outer_selected = [ e for e in bm . edges if e . select ]
# 4.) Mask edges exclusive to edges_outer_selected (symmetry line)
edges_middle = [ item for item in edges_outer_selected if item not in edges_outer_shell ]
# 5.) Convert to UV selection
verts_middle = [ ]
for edge in edges_middle :
if edge . verts [ 0 ] not in verts_middle :
verts_middle . append ( edge . verts [ 0 ] )
if edge . verts [ 1 ] not in verts_middle :
verts_middle . append ( edge . verts [ 1 ] )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
#Select all Vert shell faces
bpy . ops . mesh . select_linked ( delimit = set ( ) )
#Select UV matching vert array
bpy . ops . uv . select_all ( action = ' DESELECT ' )
for face in bm . faces :
if face . select :
for loop in face . loops :
if loop . vert in verts_middle :
loop [ uv_layers ] . select = True
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# 5.) Align UV shell
alignToCenterLine ( )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# 7.) Collect left and right side verts
verts_A = [ ] ;
verts_B = [ ] ;
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
bpy . ops . uv . select_all ( action = ' DESELECT ' )
for face in selected_faces :
for loop in face . loops :
if loop . vert not in verts_middle and loop . vert not in verts_A :
verts_A . append ( loop . vert )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
for vert in verts_all :
if vert not in verts_middle and vert not in verts_A and vert not in verts_B :
verts_B . append ( vert )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# 8.) Mirror Verts
mirror_verts ( verts_middle , verts_A , verts_B , True )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
#Restore selection
# utilities_uv.selection_restore()
2019-06-08 23:42:50 +00:00
def mirror_verts ( verts_middle , verts_A , verts_B , isAToB ) :
2019-12-18 20:53:16 +00:00
print ( " -------------------------------- \n Mirror: C: " + str ( len ( verts_middle ) ) + " ; verts: " + str ( len ( verts_A ) ) + " | " + str ( len ( verts_B ) ) + " x, A to B? " + str ( isAToB ) )
bm = bmesh . from_edit_mesh ( bpy . context . active_object . data )
uv_layers = bm . loops . layers . uv . verify ( )
# Get verts_island
verts_island = [ ]
for vert in verts_middle :
verts_island . append ( vert )
for vert in verts_A :
verts_island . append ( vert )
for vert in verts_B :
verts_island . append ( vert )
# Select Island as Faces
bpy . ops . mesh . select_mode ( use_extend = False , use_expand = False , type = ' VERT ' )
bpy . ops . mesh . select_all ( action = ' DESELECT ' )
for vert in verts_island :
vert . select = True
bpy . ops . mesh . select_mode ( use_extend = False , use_expand = True , type = ' FACE ' )
# Collect Librarys of verts / UV
vert_to_uv = utilities_uv . get_vert_to_uv ( bm , uv_layers )
uv_to_vert = utilities_uv . get_uv_to_vert ( bm , uv_layers )
uv_to_face = { }
# UV clusters / groups (within 0.000001 distance)
clusters = [ ]
uv_to_clusters = { }
vert_to_clusters = { }
for face in bm . faces :
if face . select :
for loop in face . loops :
vert = loop . vert
uv = loop [ uv_layers ]
if uv not in uv_to_face :
uv_to_face [ uv ] = face ;
# clusters
isMerged = False
for cluster in clusters :
d = ( uv . uv - cluster . uvs [ 0 ] . uv ) . length
if d < = 0.0000001 :
#Merge
cluster . append ( uv )
uv_to_clusters [ uv ] = cluster
if vert not in vert_to_clusters :
vert_to_clusters [ vert ] = cluster
isMerged = True ;
break ;
if not isMerged :
#New Group
clusters . append ( UVCluster ( vert , [ uv ] ) )
uv_to_clusters [ uv ] = clusters [ - 1 ]
if vert not in vert_to_clusters :
vert_to_clusters [ vert ] = clusters [ - 1 ]
# Get Center X
x_middle = vert_to_uv [ verts_middle [ 0 ] ] [ 0 ] . uv . x ;
# 3.) Grow layer by layer
bpy . ops . mesh . select_mode ( use_extend = False , use_expand = False , type = ' VERT ' )
bpy . context . scene . tool_settings . uv_select_mode = ' VERTEX '
clusters_processed = [ ]
def select_extend_filter ( clusters_border , clusters_mask ) :
# print("Extend A/B")
connected_clusters = [ ]
for cluster in clusters_border :
# Select and Extend selection
bpy . ops . uv . select_all ( action = ' DESELECT ' )
for uv in cluster . uvs :
uv . select = True
bpy . ops . uv . select_more ( )
# Collect extended
uv_extended = [ uv for clusterMask in clusters_mask for uv in clusterMask . uvs if ( uv . select and clusterMask not in clusters_processed ) ]
clusters_extended = [ ]
for uv in uv_extended :
if uv_to_clusters [ uv ] not in clusters_extended :
clusters_extended . append ( uv_to_clusters [ uv ] )
# Sort by distance
groups_distance = { }
for i in range ( 0 , len ( clusters_extended ) ) :
sub_group = clusters_extended [ i ]
groups_distance [ i ] = ( cluster . uvs [ 0 ] . uv - sub_group . uvs [ 0 ] . uv ) . length
# Append to connected clusters
array = [ ]
for item in sorted ( groups_distance . items ( ) , key = operator . itemgetter ( 1 ) ) :
key = item [ 0 ]
clust = clusters_extended [ key ]
array . append ( clust )
if clust not in clusters_processed :
clusters_processed . append ( clust )
connected_clusters . append ( array )
if cluster not in clusters_processed :
clusters_processed . append ( cluster )
bpy . ops . uv . select_all ( action = ' DESELECT ' )
for uv in uv_extended :
uv . select = True
return connected_clusters
mask_A = [ vert_to_clusters [ vert ] for vert in verts_A ]
mask_B = [ vert_to_clusters [ vert ] for vert in verts_B ]
border_A = list ( [ vert_to_clusters [ vert ] for vert in verts_middle ] )
border_B = list ( [ vert_to_clusters [ vert ] for vert in verts_middle ] )
for step in range ( 0 , 8 ) :
if len ( border_A ) == 0 :
print ( " {} .: Finished scanning with no growth iterations " . format ( step ) )
break ;
if len ( border_A ) != len ( border_B ) or len ( border_A ) == 0 :
print ( " Abort: non compatible border A/B: {} x {} x " . format ( len ( border_A ) , len ( border_B ) ) )
break ;
print ( " {} .: border {} x| {} x, processed: {} x " . format ( step , len ( border_A ) , len ( border_B ) , len ( clusters_processed ) ) )
# Collect connected pairs for each side
connected_A = select_extend_filter ( border_A , mask_A )
connected_B = select_extend_filter ( border_B , mask_B )
print ( " Connected: {} x| {} x " . format ( len ( connected_A ) , len ( connected_B ) ) )
border_A . clear ( )
border_B . clear ( )
# Traverse through pairs
for i in range ( 0 , min ( len ( connected_A ) , len ( connected_B ) ) ) :
if len ( connected_A [ i ] ) == 0 :
continue
if len ( connected_A [ i ] ) != len ( connected_B [ i ] ) :
print ( " . Error: Inconsistent grow mappings from {} {} x | {} x " . format ( i , len ( connected_A [ i ] ) , len ( connected_B [ i ] ) ) )
continue
indexA = [ cluster . vertex . index for cluster in connected_A [ i ] ]
indexB = [ cluster . vertex . index for cluster in connected_B [ i ] ]
indexA = str ( indexA ) . replace ( " [ " , " " ) . replace ( " ] " , " " ) . replace ( " " , " " )
indexB = str ( indexB ) . replace ( " [ " , " " ) . replace ( " ] " , " " ) . replace ( " " , " " )
print ( " . Map {} | {} = {} x| {} x " . format ( indexA , indexB , len ( connected_A [ i ] ) , len ( connected_B [ i ] ) ) )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
if True : #isAToB:
# Copy A side to B
for cluster in connected_B [ i ] :
for uv in cluster . uvs :
pos = connected_A [ i ] [ 0 ] . uvs [ 0 ] . uv . copy ( )
pos . x = x_middle - ( pos . x - x_middle ) # Flip cooreindate
# uv.uv = pos
# border_A[i] = uv_to_clusters[ connected_A[i][0] ]
# border_B[i] = uv_to_clusters[ connected_B[i][0] ]
for j in range ( len ( connected_A [ i ] ) ) :
border_A . append ( connected_A [ i ] [ j ] )
border_B . append ( connected_B [ i ] [ j ] )
# for uv in clusters_B[idxB]:
# pos = clusters_A[idxA][0].uv.copy()
# # Flip cooreindate
# pos.x = x_middle - (pos.x-x_middle)
# uv.uv = pos
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# for j in range(0, len(connected_A[i])):
# # Group A and B
# groupA = connected_A[i][j];
# groupB = connected_B[i][j];
# # vertexA = [vert_to_clusters[key] for key in vert_to_clusters if vert_to_clusters[key] == groupA]
# print("...map {} -> {}".format(groupA, groupB))
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# for j in range(0, count):
# if len(connected_A[j]) != len(connected_B[j]):
# # print("Error: Inconsistent grow mappings from {}:{}x | {}:{}x".format(border_A[j].index,len(connected_A[j]), border_B[j].index, len(connected_B[j]) ))
# print("Error: Inconsistent grow mappings from {} {}x | {}x".format(j, len(connected_A[j]), len(connected_B[j]) ))
# continue
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# for k in range(0, len(connected_A[j])):
# # Vertex A and B
# vA = connected_A[j][k];
# vB = connected_B[j][k];
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# uvsA = vert_to_uv[vA];
# uvsB = vert_to_uv[vB];
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# clusters_A = collect_clusters(uvsA)
# clusters_B = collect_clusters(uvsB)
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# if len(clusters_A) != len(clusters_B):
# print("Error: Inconsistent vertex UV group pairs at vertex {} : {}".format(vA.index, vB.index))
# continue
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# message= "...Map {0} -> {1} = UVs {2}|{3}x | UV-Groups {4}x|{5}x".format( vA.index, vB.index, len(uvsA), len(uvsB), len(clusters_A), len(clusters_B) )
# if len(clusters_A) > 1:
# message = ">> "+message
# print(message)
# if len(clusters_A) > 0:
# # For each group
# sortA = {}
# sortB = {}
# for g in range(0, len(clusters_A)):
# uv_A = clusters_A[g][0].uv.copy()
# uv_B = clusters_B[g][0].uv.copy()
# # localize X values (from symmetry line)
# uv_A.x = (uv_A.x - x_middle)
# uv_B.x = (uv_B.x - x_middle)
# sortA[g] = abs(uv_A.x) + uv_A.y*2.0
# sortB[g] = abs(uv_B.x) + uv_B.y*2.0
# # print(" . [{}] : {:.2f}, {:.2f} | {:.2f}, {:.2f}".format(g, uv_A.x, uv_A.y, uv_B.x, uv_B.y))
# print(" . [{}] : {:.2f} | {:.2f}".format(g, sortA[g], sortB[g]))
# # Sort sortA by value
# sortedA = sorted(sortA.items(), key=operator.itemgetter(1))
# sortedB = sorted(sortB.items(), key=operator.itemgetter(1))
# for g in range(0, len(clusters_A)):
# # sortedA[g]
# idxA = sortedA[g][0]
# idxB = sortedB[g][0]
# print("Map clusters_A {} -> ".format(idxA, idxB))
# for uv in clusters_B[idxB]:
# pos = clusters_A[idxA][0].uv.copy()
# # Flip cooreindate
# pos.x = x_middle - (pos.x-x_middle)
# uv.uv = pos
# border_A.append(vA)
# border_B.append(vB)
print ( " -------------------------------- " )
'''
def select_extend_filter ( verts_border , verts_mask ) :
# print("Extend A/B")
connected_verts = [ ]
for i in range ( 0 , len ( verts_border ) ) :
# Collect connected edge verts
verts_connected_edges = [ ]
for edge in verts_border [ i ] . link_edges :
if ( edge . verts [ 0 ] not in verts_connected_edges ) :
verts_connected_edges . append ( edge . verts [ 0 ] )
if ( edge . verts [ 1 ] not in verts_connected_edges ) :
verts_connected_edges . append ( edge . verts [ 1 ] )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# Select vert on border
bpy . ops . mesh . select_all ( action = ' DESELECT ' )
verts_border [ i ] . select = True
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# Extend selection
bpy . ops . mesh . select_more ( )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# Filter selected verts against mask, connected edges, processed and border
verts_extended = [ vert for vert in bm . verts if ( vert . select and vert in verts_connected_edges and vert in verts_mask and vert and vert not in verts_border and vert not in verts_processed ) ]
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# print(" "+str(i)+". scan: "+str(verts_border[i].index)+"; ext: "+str(len(verts_extended))+"x")
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
connected_verts . append ( [ ] )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# Sort by distance
verts_distance = { }
for vert in verts_extended :
verts_distance [ vert ] = ( verts_border [ i ] . co - vert . co ) . length
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
for item in sorted ( verts_distance . items ( ) , key = operator . itemgetter ( 1 ) ) :
connected_verts [ i ] . append ( item [ 0 ] )
if verts_border [ i ] not in verts_processed :
verts_processed . append ( verts_border [ i ] )
return connected_verts
# find UV vert blobs , see which ones are same spot
def collect_clusters ( uvs ) :
groups = [ ]
for uv in uvs :
if len ( groups ) == 0 :
groups . append ( [ uv ] )
else :
isMerged = False
for group in groups :
d = ( uv . uv - group [ 0 ] . uv ) . length
if d < = 0.0000001 :
#Merge
group . append ( uv )
isMerged = True ;
break ;
if not isMerged :
#New Group
groups . append ( [ uv ] )
return groups
border_A = [ vert for vert in verts_middle ]
border_B = [ vert for vert in verts_middle ]
for i in range ( 0 , 200 ) :
if len ( border_A ) == 0 :
print ( " Finished scanning at {} growth iterations " . format ( i ) )
break ;
if len ( border_A ) != len ( border_B ) or len ( border_A ) == 0 :
print ( " Abort: non compatible border A/B: {} x {} x " . format ( len ( border_A ) , len ( border_B ) ) )
break ;
connected_A = select_extend_filter ( border_A , verts_A )
connected_B = select_extend_filter ( border_B , verts_B )
print ( " Map pairs: {} | {} " . format ( len ( connected_A ) , len ( connected_B ) ) )
border_A . clear ( )
border_B . clear ( )
count = min ( len ( connected_A ) , len ( connected_B ) )
for j in range ( 0 , count ) :
if len ( connected_A [ j ] ) != len ( connected_B [ j ] ) :
# print("Error: Inconsistent grow mappings from {}:{}x | {}:{}x".format(border_A[j].index,len(connected_A[j]), border_B[j].index, len(connected_B[j]) ))
print ( " Error: Inconsistent grow mappings from {} {} x | {} x " . format ( j , len ( connected_A [ j ] ) , len ( connected_B [ j ] ) ) )
continue
for k in range ( 0 , len ( connected_A [ j ] ) ) :
# Vertex A and B
vA = connected_A [ j ] [ k ] ;
vB = connected_B [ j ] [ k ] ;
uvsA = vert_to_uv [ vA ] ;
uvsB = vert_to_uv [ vB ] ;
clusters_A = collect_clusters ( uvsA )
clusters_B = collect_clusters ( uvsB )
if len ( clusters_A ) != len ( clusters_B ) :
print ( " Error: Inconsistent vertex UV group pairs at vertex {} : {} " . format ( vA . index , vB . index ) )
continue
message = " ...Map {0} -> {1} = UVs {2} | {3} x | UV-Groups {4} x| {5} x " . format ( vA . index , vB . index , len ( uvsA ) , len ( uvsB ) , len ( clusters_A ) , len ( clusters_B ) )
if len ( clusters_A ) > 1 :
message = " >> " + message
print ( message )
if len ( clusters_A ) > 0 :
# For each group
sortA = { }
sortB = { }
for g in range ( 0 , len ( clusters_A ) ) :
uv_A = clusters_A [ g ] [ 0 ] . uv . copy ( )
uv_B = clusters_B [ g ] [ 0 ] . uv . copy ( )
# localize X values (from symmetry line)
uv_A . x = ( uv_A . x - x_middle )
uv_B . x = ( uv_B . x - x_middle )
sortA [ g ] = abs ( uv_A . x ) + uv_A . y * 2.0
sortB [ g ] = abs ( uv_B . x ) + uv_B . y * 2.0
# print(" . [{}] : {:.2f}, {:.2f} | {:.2f}, {:.2f}".format(g, uv_A.x, uv_A.y, uv_B.x, uv_B.y))
print ( " . [ {} ] : {:.2f} | {:.2f} " . format ( g , sortA [ g ] , sortB [ g ] ) )
# Sort sortA by value
sortedA = sorted ( sortA . items ( ) , key = operator . itemgetter ( 1 ) )
sortedB = sorted ( sortB . items ( ) , key = operator . itemgetter ( 1 ) )
for g in range ( 0 , len ( clusters_A ) ) :
# sortedA[g]
idxA = sortedA [ g ] [ 0 ]
idxB = sortedB [ g ] [ 0 ]
print ( " Map clusters_A {} -> " . format ( idxA , idxB ) )
for uv in clusters_B [ idxB ] :
pos = clusters_A [ idxA ] [ 0 ] . uv . copy ( )
# Flip cooreindate
pos . x = x_middle - ( pos . x - x_middle )
uv . uv = pos
# print("Sorted: '"+str(sortedA)+"'")
# print("Sorted: '"+str(sortedB)+"'")
# for item in sorted(verts_distance.items(), key=operator.itemgetter(1)):
# connected_verts[i].append( item[0] )
# TODO: Now map groups to each other
# uv_avg_A = Vector([0,0])
# uv_avg_B = Vector([0,0])
# for m in range(0, len(clusters_A)):
# print(" . ")
# uv_avg_A+= clusters_A[m][0].uv;
# uv_avg_B+= clusters_B[m][0].uv;
# uv_avg_A/=len(clusters_A)
# uv_avg_B/=len(clusters_B)
# print(" avg: {} : {}".format(uv_avg_A, uv_avg_B))
# Done processing, add to border arrays
border_A . append ( vA )
border_B . append ( vB )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
'''
2019-06-08 23:42:50 +00:00
def alignToCenterLine ( ) :
2019-12-18 20:53:16 +00:00
print ( " align to center line " )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
bm = bmesh . from_edit_mesh ( bpy . context . active_object . data )
uv_layers = bm . loops . layers . uv . verify ( )
bpy . ops . mesh . select_mode ( use_extend = False , use_expand = False , type = ' EDGE ' )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# 1.) Get average edges rotation + center
average_angle = 0
average_center = Vector ( ( 0 , 0 ) )
average_count = 0
for face in bm . faces :
if face . select :
verts = [ ]
for loop in face . loops :
if loop [ uv_layers ] . select :
verts . append ( loop [ uv_layers ] . uv )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
if len ( verts ) == 2 :
diff = verts [ 1 ] - verts [ 0 ]
angle = math . atan2 ( diff . y , diff . x ) % ( math . pi )
average_center + = verts [ 0 ] + diff / 2
average_angle + = angle
average_count + = 1
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
if average_count > 0 :
average_angle / = average_count
average_center / = average_count
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
average_angle - = math . pi / 2 #Rotate -90 degrees so aligned horizontally
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
# 2.) Rotate UV Shell around edge
bpy . context . tool_settings . transform_pivot_point = ' CURSOR '
bpy . ops . uv . cursor_set ( location = average_center )
2019-06-08 23:42:50 +00:00
2019-12-18 20:53:16 +00:00
bpy . ops . uv . select_linked ( )
bpy . ops . transform . rotate ( value = average_angle , orient_axis = ' Z ' , constraint_axis = ( False , False , False ) , orient_type = ' GLOBAL ' , mirror = False , use_proportional_edit = False )
2019-06-08 23:42:50 +00:00
class UVCluster :
2019-12-18 20:53:16 +00:00
uvs = [ ]
vertex = None
def __init__ ( self , vertex , uvs ) :
self . vertex = vertex
self . uvs = uvs
def append ( self , uv ) :
self . uvs . append ( uv )
2019-06-08 23:42:50 +00:00
bpy . utils . register_class ( op )