mirror of
https://github.com/drewcassidy/TexTools-Blender
synced 2024-09-01 14:54:44 +00:00
146 lines
4.3 KiB
Python
146 lines
4.3 KiB
Python
import bpy
|
|
import bmesh
|
|
import operator
|
|
import math
|
|
from mathutils import Vector
|
|
from collections import defaultdict
|
|
from math import pi
|
|
|
|
from . import utilities_uv
|
|
|
|
|
|
class op(bpy.types.Operator):
|
|
bl_idname = "uv.textools_island_align_edge"
|
|
bl_label = "Align Island by Edge"
|
|
bl_description = "Align the island by selected edge"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
# Only in UV editor mode
|
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
|
return False
|
|
|
|
if not bpy.context.active_object:
|
|
return False
|
|
|
|
if bpy.context.active_object.type != 'MESH':
|
|
return False
|
|
|
|
# Only in Edit mode
|
|
if bpy.context.active_object.mode != 'EDIT':
|
|
return False
|
|
|
|
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
|
return False
|
|
|
|
# Requires UV map
|
|
if not bpy.context.object.data.uv_layers:
|
|
return False
|
|
|
|
# Requires UV Edge select mode
|
|
if bpy.context.scene.tool_settings.uv_select_mode != 'EDGE':
|
|
return False
|
|
|
|
return True
|
|
|
|
def execute(self, context):
|
|
# Store selection
|
|
utilities_uv.selection_store()
|
|
|
|
main(context)
|
|
|
|
# Restore selection
|
|
utilities_uv.selection_restore()
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
def main(context):
|
|
print("Executing operator_island_align_edge")
|
|
|
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
|
uv_layers = bm.loops.layers.uv.verify()
|
|
|
|
faces_selected = []
|
|
for face in bm.faces:
|
|
if face.select:
|
|
for loop in face.loops:
|
|
if loop[uv_layers].select:
|
|
faces_selected.append(face)
|
|
break
|
|
|
|
print("faces_selected: "+str(len(faces_selected)))
|
|
|
|
# Collect 2 uv verts for each island
|
|
face_uvs = {}
|
|
for face in faces_selected:
|
|
uvs = []
|
|
for loop in face.loops:
|
|
if loop[uv_layers].select:
|
|
uvs.append(loop[uv_layers])
|
|
if len(uvs) >= 2:
|
|
break
|
|
if len(uvs) >= 2:
|
|
face_uvs[face] = uvs
|
|
|
|
faces_islands = {}
|
|
faces_unparsed = faces_selected.copy()
|
|
for face in face_uvs:
|
|
if face in faces_unparsed:
|
|
|
|
bpy.ops.uv.select_all(action='DESELECT')
|
|
face_uvs[face][0].select = True
|
|
bpy.ops.uv.select_linked() # Extend selection
|
|
|
|
# Collect faces
|
|
faces_island = [face]
|
|
for f in faces_unparsed:
|
|
if f != face and f.select and f.loops[0][uv_layers].select:
|
|
print("append "+str(f.index))
|
|
faces_island.append(f)
|
|
for f in faces_island:
|
|
faces_unparsed.remove(f)
|
|
|
|
# Assign Faces to island
|
|
faces_islands[face] = faces_island
|
|
|
|
print("Sets: {}x".format(len(faces_islands)))
|
|
|
|
# Align each island to its edges
|
|
for face in faces_islands:
|
|
align_island(face_uvs[face][0].uv, face_uvs[face]
|
|
[1].uv, faces_islands[face])
|
|
|
|
|
|
def align_island(uv_vert0, uv_vert1, faces):
|
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
|
uv_layers = bm.loops.layers.uv.verify()
|
|
|
|
print("Align {}x faces".format(len(faces)))
|
|
|
|
# Select faces
|
|
bpy.ops.uv.select_all(action='DESELECT')
|
|
for face in faces:
|
|
for loop in face.loops:
|
|
loop[uv_layers].select = True
|
|
|
|
diff = uv_vert1 - uv_vert0
|
|
current_angle = math.atan2(diff.x, diff.y)
|
|
angle_to_rotate = round(current_angle / (math.pi/2)) * (math.pi/2) - current_angle
|
|
|
|
# For some reason bpy.ops.transform.rotate rotates in the opposite
|
|
# direction in Blender 2.83 than in other versions.
|
|
if float(bpy.app.version_string[0:4]) == 2.83:
|
|
angle_to_rotate = -angle_to_rotate
|
|
|
|
bpy.ops.uv.select_linked()
|
|
|
|
bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
|
|
bpy.ops.uv.cursor_set(location=uv_vert0 + diff/2)
|
|
|
|
bpy.ops.transform.rotate(value=angle_to_rotate, orient_axis='Z', constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
|
|
|
|
|
|
bpy.utils.register_class(op)
|