Skip to content

Commit

Permalink
Added Align Center/Vewrtical/Horizontal, Fixed Rotate to 90
Browse files Browse the repository at this point in the history
  • Loading branch information
TitusLVR committed Oct 22, 2020
1 parent ddb130c commit 756f525
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 44 deletions.
13 changes: 7 additions & 6 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,22 +744,22 @@ def draw(self, context):
col = row.column(align=True)
col.label(text="")
col.operator(op_align.op.bl_idname, text="←", icon_value = icon_get("op_align_left")).direction = "left"
col.operator(op_align.op.bl_idname, text="—", icon_value = icon_get("op_align_horizontal")).direction = "horizontal"

col = row.column(align=True)
col.operator(op_align.op.bl_idname, text="↑", icon_value = icon_get("op_align_top")).direction = "top"
col.operator(op_align.op.bl_idname, text="+", icon_value = icon_get("op_align_center")).direction = "center"
col.operator(op_align.op.bl_idname, text="↓", icon_value = icon_get("op_align_bottom")).direction = "bottom"

col = row.column(align=True)
col.label(text="")
col.operator(op_align.op.bl_idname, text="→", icon_value = icon_get("op_align_right")).direction = "right"
col.operator(op_align.op.bl_idname, text="|", icon_value = icon_get("op_align_vertical")).direction = "vertical"

row = col_tr.row(align=True)
row.operator(op_island_rotate_90.op.bl_idname, text="-90°", icon_value = icon_get("op_island_rotate_90_left")).angle = -math.pi / 2
row.operator(op_island_rotate_90.op.bl_idname, text="+90°", icon_value = icon_get("op_island_rotate_90_right")).angle = math.pi / 2




col = box.column(align=True)
row = col.row(align=True)
op = row.operator(op_island_align_sort.op.bl_idname, text="Sort H", icon_value = icon_get("op_island_align_sort_h"))
Expand All @@ -770,15 +770,13 @@ def draw(self, context):
op.is_vertical = True;
op.padding = utilities_ui.get_padding()


aligned = box.row(align=True)
col = aligned.column(align=True)

row = col.row(align=True)
row.operator(op_island_straighten_edge_loops.op.bl_idname, text="Straight", icon_value = icon_get("op_island_straighten_edge_loops"))
row.operator(op_rectify.op.bl_idname, text="Rectify", icon_value = icon_get("op_rectify"))


col.operator(op_unwrap_edge_peel.op.bl_idname, text="Edge Peel", icon_value = icon_get("op_unwrap_edge_peel"))

row = col.row(align=True)
Expand Down Expand Up @@ -1393,7 +1391,10 @@ def register():
"op_align_bottom.png",
"op_align_left.png",
"op_align_right.png",
"op_align_top.png",
"op_align_top.png",
"op_align_horizontal.png",
"op_align_vertical.png",
"op_align_center.png",
"op_bake.png",
"op_bake_explode.png",
"op_color_convert_texture.png",
Expand Down
Binary file added icons/op_align_center.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/op_align_horizontal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/op_align_vertical.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 30 additions & 18 deletions op_align.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import operator
from mathutils import Vector
from collections import defaultdict
from math import pi
from math import pi, sqrt
from numpy import median


from . import utilities_uv



class op(bpy.types.Operator):
bl_idname = "uv.textools_align"
bl_label = "Align"
Expand Down Expand Up @@ -46,9 +49,6 @@ def execute(self, context):
return {'FINISHED'}





def align(context, direction):
#Store selection
utilities_uv.selection_store()
Expand All @@ -67,34 +67,46 @@ def align(context, direction):

# Collect BBox sizes
boundsAll = utilities_uv.getSelectionBBox()
center_all = boundsAll['center']

mode = bpy.context.scene.tool_settings.uv_select_mode
if mode == 'FACE' or mode == 'ISLAND':
print("____ Align Islands")

#Collect UV islands
islands = utilities_uv.getSelectionIslands()

for island in islands:

bpy.ops.uv.select_all(action='DESELECT')
utilities_uv.set_selected_faces(island)
bounds = utilities_uv.getSelectionBBox()

# print("Island "+str(len(island))+"x faces, delta: "+str(delta.y))
bounds = utilities_uv.get_island_BBOX(island)
center = bounds['center']

if direction == "bottom":
delta = boundsAll['min'] - bounds['min']
bpy.ops.transform.translate(value=(0, delta.y, 0))
delta = boundsAll['min'] - bounds['min']
utilities_uv.move_island(island, 0,delta.y)

elif direction == "top":
delta = boundsAll['max'] - bounds['max']
bpy.ops.transform.translate(value=(0, delta.y, 0))
utilities_uv.move_island(island, 0,delta.y)

elif direction == "left":
delta = boundsAll['min'] - bounds['min']
bpy.ops.transform.translate(value=(delta.x, 0, 0))
utilities_uv.move_island(island, delta.x,0)

elif direction == "right":
delta = boundsAll['max'] - bounds['max']
bpy.ops.transform.translate(value=(delta.x, 0, 0))
utilities_uv.move_island(island, delta.x,0)

elif direction == "center":
delta = Vector((center_all - center))
utilities_uv.move_island(island, delta.x, delta.y)

elif direction == "horizontal":
delta = Vector((center_all - center))
utilities_uv.move_island(island, 0, delta.y)

elif direction == "vertical":
delta = Vector((center_all - center))
utilities_uv.move_island(island, delta.x, 0)


else:
print("Unkown direction: "+str(direction))

Expand All @@ -121,7 +133,7 @@ def align(context, direction):
bmesh.update_edit_mesh(obj.data)

#Restore selection
utilities_uv.selection_restore()
# utilities_uv.selection_restore()

bpy.utils.register_class(op)

Expand Down
101 changes: 81 additions & 20 deletions utilities_uv.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
from mathutils import Vector
from collections import defaultdict
from math import pi
from numpy import median

from . import settings
from . import utilities_ui

def selection_store():
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
uv_layers = bm.loops.layers.uv.verify();
def selection_store():

bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify()

# https://blender.stackexchange.com/questions/5781/how-to-list-all-selected-elements-in-python
# print("selectionStore")
Expand Down Expand Up @@ -94,6 +96,23 @@ def selection_restore(bm = None, uv_layers = None):

bpy.context.view_layer.update()

def move_island(island, dx,dy):

obj = bpy.context.active_object
me = obj.data
bm = bmesh.from_edit_mesh(me)

uv_layer = bm.loops.layers.uv.verify()

# adjust uv coordinates
for face in island:
for loop in face.loops:
loop_uv = loop[uv_layer]
loop_uv.uv[0] += dx
loop_uv.uv[1] += dy
bmesh.update_edit_mesh(me)




def get_selected_faces():
Expand Down Expand Up @@ -172,7 +191,7 @@ def get_vert_to_uv(bm, uv_layers):
vert = loop.vert
uv = loop[uv_layers]
if vert not in vert_to_uv:
vert_to_uv[vert] = [uv];
vert_to_uv[vert] = [uv]
else:
vert_to_uv[vert].append(uv)
return vert_to_uv
Expand All @@ -186,23 +205,22 @@ def get_uv_to_vert(bm, uv_layers):
vert = loop.vert
uv = loop[uv_layers]
if uv not in uv_to_vert:
uv_to_vert[ uv ] = vert;
uv_to_vert[ uv ] = vert
return uv_to_vert




def getSelectionBBox():
bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
uv_layers = bm.loops.layers.uv.verify();
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify()

bbox = {}

uvs = []
boundsMin = Vector((99999999.0,99999999.0))
boundsMax = Vector((-99999999.0,-99999999.0))
boundsCenter = Vector((0.0,0.0))
countFaces = 0;
countFaces = 0

for face in bm.faces:
if face.select:
for loop in face.loops:
Expand All @@ -213,23 +231,66 @@ def getSelectionBBox():
boundsMax.x = max(boundsMax.x, uv.x)
boundsMax.y = max(boundsMax.y, uv.y)

boundsCenter+= uv
boundsCenter += uv
countFaces+=1
uvs.append([uv.x,uv.y])

bbox['min'] = boundsMin
bbox['max'] = boundsMax
bbox['width'] = (boundsMax - boundsMin).x
bbox['height'] = (boundsMax - boundsMin).y

if countFaces == 0:
bbox['center'] = boundsMin
else:
bbox['center'] = boundsCenter / countFaces

bbox['median'] = median(uvs)
bbox['area'] = bbox['width'] * bbox['height']
bbox['minLength'] = min(bbox['width'], bbox['height'])

return bbox;
bbox['minLength'] = min(bbox['width'], bbox['height'])
return bbox


def get_island_BBOX(island):
points = []
obj = bpy.context.active_object
me = obj.data
bm = bmesh.from_edit_mesh(me)

uv_layer = bm.loops.layers.uv.verify()
boundsMin = Vector((99999999.0,99999999.0))
boundsMax = Vector((-99999999.0,-99999999.0))
boundsCenter = Vector((0.0,0.0))
countFaces = 0

for face in island:
for loop in face.loops:
loop_uv = loop[uv_layer]
points.append(loop_uv.uv)
uv = loop[uv_layer].uv
boundsMin.x = min(boundsMin.x, uv.x)
boundsMin.y = min(boundsMin.y, uv.y)
boundsMax.x = max(boundsMax.x, uv.x)
boundsMax.y = max(boundsMax.y, uv.y)

boundsCenter += uv
countFaces+=1

x_coordinates, y_coordinates = zip(*points)
island_bbox = [(min(x_coordinates), min(y_coordinates)), (max(x_coordinates), max(y_coordinates))]

bbox = {}

if countFaces == 0:
bbox['center'] = boundsMin
else:
bbox['center'] = boundsCenter / countFaces

bbox['min'] = Vector((island_bbox[0]))
bbox['max'] = Vector((island_bbox[1]))
bbox['median'] = Vector((median(x_coordinates),median(y_coordinates)))
print ("Island bbox", island_bbox)
return bbox



Expand All @@ -246,7 +307,7 @@ def getSelectionIslands(bm=None, uv_layers=None):
bpy.ops.uv.select_linked()

#Collect selected UV faces
faces_selected = [];
faces_selected = []
for face in bm.faces:
if face.select and face.loops[0][uv_layers].select:
faces_selected.append(face)
Expand Down Expand Up @@ -277,9 +338,9 @@ def getSelectionIslands(bm=None, uv_layers=None):
islands.append(islandFaces)

#Restore selection
# for face in faces_selected:
# for loop in face.loops:
# loop[uv_layers].select = True
for face in faces_selected:
for loop in face.loops:
loop[uv_layers].select = True


print("Islands: {}x".format(len(islands)))
Expand Down

0 comments on commit 756f525

Please sign in to comment.