diff --git a/op_island_align_world.py b/op_island_align_world.py index a552d4d..463af23 100644 --- a/op_island_align_world.py +++ b/op_island_align_world.py @@ -57,7 +57,6 @@ class op(bpy.types.Operator): return {'FINISHED'} - def main(context): print("\n________________________\nis_global") @@ -76,9 +75,7 @@ def main(context): uv_layers = bm.loops.layers.uv.verify(); islands = utilities_uv.getSelectionIslands() - - for faces in islands: # Get average viewport normal of UV island avg_normal = Vector((0,0,0)) @@ -94,29 +91,23 @@ def main(context): z = 2 max_size = max(abs(avg_normal.x), abs(avg_normal.y), abs(avg_normal.z)) - # Use multiple steps - for i in range(3): - if(abs(avg_normal.x) == max_size): - print("x normal") - align_island(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False) - - elif(abs(avg_normal.y) == max_size): - print("y normal") - align_island(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False) - - elif(abs(avg_normal.z) == max_size): - print("z normal") - align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0) + #for i in range(3): # Use multiple steps #REMOVED: maybe should be reimplemented as a custom property + if(abs(avg_normal.x) == max_size): + print("x normal") + align_island(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False) + elif(abs(avg_normal.y) == max_size): + print("y normal") + align_island(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False) + elif(abs(avg_normal.z) == max_size): + print("z normal") + align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0) print("align island: faces {}x n:{}, max:{}".format(len(faces), avg_normal, max_size)) - - #Restore selection utilities_uv.selection_restore() - def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False): # Find lowest and highest verts @@ -126,67 +117,69 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False axis_names = ['x', 'y', 'z'] print("Align shell {}x at {},{} flip {},{}".format(len(faces), axis_names[x], axis_names[y], flip_x, flip_y)) - # print(" Min #{} , Max #{} along '{}'".format(minmax_vert[0].index, minmax_vert[1].index, axis_names[y] )) # print(" A1 {:.1f} , A2 {:.1f} along ".format(minmax_val[0], minmax_val[1] )) - - - + # Collect UV to Vert - vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers) - uv_to_vert = utilities_uv.get_uv_to_vert(bm, uv_layers) - + vert_to_uv = {} + for face in faces: + for loop in face.loops: + vert = loop.vert + uv = loop[uv_layers] + if vert not in vert_to_uv: + vert_to_uv[vert] = [uv]; + else: + vert_to_uv[vert].append(uv) + #uv_to_vert = utilities_uv.get_uv_to_vert(bm, uv_layers) processed_edges = [] - edges = [] + n_edges = 0 + avg_angle = 0 for face in faces: for edge in face.edges: if edge not in processed_edges: processed_edges.append(edge) delta = edge.verts[0].co -edge.verts[1].co max_side = max(abs(delta.x), abs(delta.y), abs(delta.z)) - # Check edges dominant in active axis if( abs(delta[x]) == max_side or abs(delta[y]) == max_side): - # if( abs(delta[y]) == max_side): - edges.append(edge) - - print("Edges {}x".format(len(edges))) - - avg_angle = 0 - for edge in edges: - uv0 = vert_to_uv[ edge.verts[0] ][0] - uv1 = vert_to_uv[ edge.verts[1] ][0] - delta_verts = Vector(( - edge.verts[1].co[x] - edge.verts[0].co[x], - edge.verts[1].co[y] - edge.verts[0].co[y] - )) - - - if flip_x: - delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x] - if flip_y: - delta_verts.y = -edge.verts[1].co[y] + edge.verts[0].co[y] - - # delta_verts.y = edge.verts[0].co[y] - edge.verts[1].co[y] - - - delta_uvs = Vector(( - uv1.uv.x - uv0.uv.x, - uv1.uv.y - uv0.uv.y - )) - a0 = math.atan2(delta_verts.y, delta_verts.x) - math.pi/2 - a1 = math.atan2(delta_uvs.y, delta_uvs.x) - math.pi/2 - - - - - a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1)) - # edge.verts[0].index, edge.verts[1].index - # print(" turn {:.1f} .. {:.1f} , {:.1f}".format(a_delta*180/math.pi, a0*180/math.pi,a1*180/math.pi)) - avg_angle+=a_delta - + n_edges += 1 + uv0 = vert_to_uv[ edge.verts[0] ][0] + uv1 = vert_to_uv[ edge.verts[1] ][0] + + delta_verts = Vector(( + edge.verts[1].co[x] - edge.verts[0].co[x], + edge.verts[1].co[y] - edge.verts[0].co[y] + )) + if flip_x: + delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x] + if flip_y: + delta_verts.y = -edge.verts[1].co[y] + edge.verts[0].co[y] + + delta_uvs = Vector(( + uv1.uv.x - uv0.uv.x, + uv1.uv.y - uv0.uv.y + )) - avg_angle/=len(edges) # - math.pi/2 + a0 = math.atan2(delta_verts.y, delta_verts.x) #- math.pi/2 + a1 = math.atan2(delta_uvs.y, delta_uvs.x) #- math.pi/2 + + a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1)) + + # Consolidation (math.atan2 gives the lower angle between -Pi and Pi, this triggers errors when using the average avg_angle /= n_edges for rotation angles close to Pi) + if n_edges > 1: + if abs((avg_angle / (n_edges-1)) - a_delta) > 2.8: + if a_delta > 0: + avg_angle+=(a_delta-math.pi*2) + else: + avg_angle+=(a_delta+math.pi*2) + else: + avg_angle+=a_delta + else: + avg_angle+=a_delta + + avg_angle /= n_edges + + print("Edges {}x".format(n_edges)) print("Turn {:.1f}".format(avg_angle * 180/math.pi)) bpy.ops.uv.select_all(action='DESELECT') @@ -194,98 +187,8 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False for loop in face.loops: loop[uv_layers].select = True - bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT' - bpy.ops.transform.rotate(value=avg_angle, orient_axis='Z') - # bpy.ops.transform.rotate(value=0.58191, axis=(-0, -0, -1), constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SPHERE', proportional_size=0.0267348) - - - # processed = [] - - - ''' - bpy.ops.uv.select_all(action='DESELECT') - for face in faces: - - # Collect UV to Vert - for loop in face.loops: - loop[uv_layers].select = True - vert = loop.vert - uv = loop[uv_layers] - # vert_to_uv - if vert not in vert_to_uv: - vert_to_uv[vert] = [uv]; - else: - vert_to_uv[vert].append(uv) - # uv_to_vert - if uv not in uv_to_vert: - uv_to_vert[ uv ] = vert; - - - for vert in face.verts: - if vert not in processed: - processed.append(vert) - - vert_y = (vert.co)[y] #obj.matrix_world * - - print("idx {} = {}".format(vert.index, vert_y)) - - if not minmax_vert[0] or not minmax_vert[1]: - minmax_vert[0] = vert - minmax_vert[1] = vert - minmax_val[0] = vert_y - minmax_val[1] = vert_y - continue - - if vert_y < minmax_val[0]: - # Not yet defined or smaller - minmax_vert[0] = vert - minmax_val[0] = vert_y - - elif vert_y > minmax_val[1]: - minmax_vert[1] = vert - minmax_val[1] = vert_y - - - if minmax_vert[0] and minmax_vert[1]: - axis_names = ['x', 'y', 'z'] - print(" Min #{} , Max #{} along '{}'".format(minmax_vert[0].index, minmax_vert[1].index, axis_names[y] )) - # print(" A1 {:.1f} , A2 {:.1f} along ".format(minmax_val[0], minmax_val[1] )) - - vert_A = minmax_vert[0] - vert_B = minmax_vert[1] - uv_A = vert_to_uv[vert_A][0] - uv_B = vert_to_uv[vert_B][0] - - delta_verts = Vector(( - vert_B.co[x] - vert_A.co[x], - vert_B.co[y] - vert_A.co[y] - )) - - delta_uvs = Vector(( - uv_B.uv.x - uv_A.uv.x, - uv_B.uv.y - uv_A.uv.y, - - )) - # Get angles - angle_vert = math.atan2(delta_verts.y, delta_verts.x) - math.pi/2 - angle_uv = math.atan2(delta_uvs.y, delta_uvs.x) - math.pi/2 - - angle_delta = math.atan2(math.sin(angle_vert-angle_uv), math.cos(angle_vert-angle_uv)) - - print(" Angles {:.2f} | {:.2f}".format(angle_vert*180/math.pi, angle_uv*180/math.pi)) - print(" Angle Diff {:.2f}".format(angle_delta*180/math.pi)) - - bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT' - bpy.ops.transform.rotate(value=angle_delta, axis='Z') - # bpy.ops.transform.rotate(value=0.58191, axis=(-0, -0, -1), constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SPHERE', proportional_size=0.0267348) - - - # bpy.ops.mesh.select_all(action='DESELECT') - # vert_A.select = True - # vert_B.select = True + bpy.ops.transform.rotate(value=-avg_angle, orient_axis='Z') # minus angle; Blender uses unconventional rotation notation (positive for clockwise) - # return - ''' bpy.utils.register_class(op)