-
Notifications
You must be signed in to change notification settings - Fork 142
/
Copy pathop_align.py
199 lines (173 loc) · 6.63 KB
/
op_align.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import bpy
import bmesh
import collections
from . import utilities_uv
from .utilities_bbox import BBox
from mathutils import Vector
class op(bpy.types.Operator):
bl_idname = "uv.textools_align"
bl_label = "Align"
bl_description = "Align vertices, edges or shells"
bl_options = {'REGISTER', 'UNDO'}
direction: bpy.props.StringProperty(name="Direction", default="top", options={'HIDDEN'})
@classmethod
def poll(cls, context):
if not bpy.context.active_object:
return False
if bpy.context.active_object.mode != 'EDIT':
return False
return True
def execute(self, context):
sync = bpy.context.scene.tool_settings.use_uv_select_sync
all_groups = [] # islands, bboxes, uv_layer or corners, uv_layer
update_obj = []
general_bbox = BBox()
bmeshes_refcount_safe = []
selected_objs = utilities_uv.selected_unique_objects_in_mode_with_uv()
align_mode = bpy.context.scene.texToolsSettings.align_mode
_is_island_mode = is_island_mode()
for obj in selected_objs:
bm = bmesh.from_edit_mesh(obj.data)
uv_layer = bm.loops.layers.uv.verify()
if _is_island_mode:
islands = utilities_uv.get_selected_islands(bm, uv_layer, selected=True)
if not islands:
continue
for island in islands:
bbox = BBox.calc_bbox_uv(island, uv_layer)
general_bbox.union(bbox)
all_groups.append((island, bbox, uv_layer))
bmeshes_refcount_safe.append(bm)
update_obj.append(obj)
else:
if sync:
corners = [luv for f in bm.faces if f.select for luv in f.loops]
else:
corners = [luv for f in bm.faces if f.select for luv in f.loops if luv[uv_layer].select]
if not corners:
continue
if align_mode == 'SELECTION':
bbox = BBox.calc_bbox_uv(corners, uv_layer, are_loops=True)
general_bbox.union(bbox)
all_groups.append((corners, uv_layer))
bmeshes_refcount_safe.append(bm)
update_obj.append(obj)
if not update_obj:
self.report({'ERROR'}, "No object for manipulate")
return {'CANCELLED'}
if align_mode == 'SELECTION':
if general_bbox.width == 0 and self.direction in {'vertical', 'left', 'right'}:
self.report({'INFO'}, "Zero width")
return {'CANCELLED'}
elif general_bbox.height == 0 and self.direction in {'horizontal', 'top', 'bottom'}:
self.report({'INFO'}, "Zero height")
return {'CANCELLED'}
elif general_bbox.max_lenght == 0:
self.report({'INFO'}, "Zero width and height")
return {'CANCELLED'}
general_bbox = recalc_general_bbox_from_align_mode(align_mode, self.direction, general_bbox)
if is_island_mode():
align_islands(all_groups, self.direction, general_bbox)
else: # Vertices or Edges UV selection mode
align_corners(all_groups, self.direction, general_bbox)
for obj in update_obj:
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}
def align_islands(groups, direction, general_bbox):
for island, bounds, uv_layer in groups:
center = bounds.center
if direction == 'bottom':
delta = Vector((0, (general_bbox.min - bounds.min).y))
elif direction == 'top':
delta = Vector((0, (general_bbox.max - bounds.max).y))
elif direction == 'left':
delta = Vector(((general_bbox.min - bounds.min).x, 0))
elif direction == 'right':
delta = Vector(((general_bbox.max - bounds.max).x, 0))
elif direction == 'center':
delta = Vector((general_bbox.center - center))
elif direction == 'horizontal':
delta = Vector((0, (general_bbox.center - center).y))
elif direction == 'vertical':
delta = Vector(((general_bbox.center - center).x, 0))
elif direction == 'bottomleft':
delta = general_bbox.min - bounds.min
elif direction == 'topright':
delta = general_bbox.max - bounds.max
elif direction == 'topleft':
delta_x = general_bbox.min - bounds.min
delta_y = general_bbox.max - bounds.max
delta = Vector((delta_x.x, delta_y.y))
elif direction == 'bottomright':
delta_x = general_bbox.max - bounds.max
delta_y = general_bbox.min - bounds.min
delta = Vector((delta_x.x, delta_y.y))
else:
raise NotImplemented
if delta != Vector((0, 0)):
utilities_uv.translate_island(island, uv_layer, delta)
def align_corners(groups, direction, general_bbox):
for luvs, uv_layer in groups:
if direction in {'left', 'right', 'vertical'}:
if direction == 'left':
destination = general_bbox.min.x
elif direction == 'right':
destination = general_bbox.max.x
else:
destination = general_bbox.center.x
for luv in luvs:
luv[uv_layer].uv[0] = destination
elif direction in {'top', 'bottom', 'horizontal'}:
if direction == 'top':
destination = general_bbox.max.y
elif direction == 'bottom':
destination = general_bbox.min.y
else:
destination = general_bbox.center.y
for luv in luvs:
luv[uv_layer].uv[1] = destination
else:
if direction == 'center':
destination = general_bbox.center
elif direction == 'bottomleft':
destination = general_bbox.min
elif direction == 'topright':
destination = general_bbox.max
elif direction == 'topleft':
destination = Vector((general_bbox.min.x, general_bbox.max.y))
elif direction == 'bottomright':
destination = Vector((general_bbox.max.x, general_bbox.min.y))
else:
raise NotImplemented
for luv in luvs:
luv[uv_layer].uv = destination
def is_island_mode():
scene = bpy.context.scene
if scene.tool_settings.use_uv_select_sync:
selection_mode = 'FACE' if scene.tool_settings.mesh_select_mode[2] else 'VERTEX'
else:
selection_mode = scene.tool_settings.uv_select_mode
return selection_mode in ('FACE', 'ISLAND')
def recalc_general_bbox_from_align_mode(align_mode, direction, general_bbox):
bb = collections.namedtuple('BBox', ['min', 'max', 'center'])
if align_mode == 'SELECTION':
general_bbox = bb(general_bbox.min, general_bbox.max, general_bbox.center)
elif align_mode == 'CURSOR':
cursor = Vector(bpy.context.space_data.cursor_location.copy())
general_bbox = bb(cursor, cursor, cursor)
else: # CANVAS
_, column, row = utilities_uv.get_UDIM_tile_coords(bpy.context.active_object)
if direction in {'bottom', 'left', 'bottomleft'}:
canvas = Vector((column, row))
elif direction in {'top', 'topleft'}:
canvas = Vector((column, row + 1))
elif direction in {'right', 'topright'}:
canvas = Vector((column + 1, row + 1))
elif direction == 'bottomright':
canvas = Vector((column + 1, row))
elif direction in {'horizontal', 'vertical', 'center'}:
canvas = Vector((column + 0.5, row + 0.5))
else:
raise NotImplemented
general_bbox = bb(canvas, canvas, canvas)
return general_bbox