# SPDX-License-Identifier: GPL-2.0-or-later # __author__ = "imdjs, Nutti " __status__ = "production" __version__ = "6.5" __date__ = "6 Mar 2021" import math from math import atan2, sin, cos import bpy import bmesh from mathutils import Vector from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry def _is_valid_context(context): # Multiple objects editing mode is not supported in this feature. objs = common.get_uv_editable_objects(context) if len(objs) != 1: return False # only edit mode is allowed to execute if context.object.mode != 'EDIT': return False # 'IMAGE_EDITOR' and 'VIEW_3D' space is allowed to execute. # If 'View_3D' space is not allowed, you can't find option in Tool-Shelf # after the execution if not common.is_valid_space(context, ['IMAGE_EDITOR', 'VIEW_3D']): return False return True @PropertyClassRegistry() class _Properties: idname = "copy_paste_uv_uvedit" @classmethod def init_props(cls, scene): class Props(): src_uvs = None scene.muv_props.copy_paste_uv_uvedit = Props() @classmethod def del_props(cls, scene): del scene.muv_props.copy_paste_uv_uvedit @BlClassRegistry() class MUV_OT_CopyPasteUVUVEdit_CopyUV(bpy.types.Operator): """ Operation class: Copy UV coordinate on UV/Image Editor """ bl_idname = "uv.muv_copy_paste_uv_uvedit_copy_uv" bl_label = "Copy UV (UV/Image Editor)" bl_description = "Copy UV coordinate (only selected in UV/Image Editor)" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): # we can not get area/space/region from console if common.is_console_mode(): return True return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.copy_paste_uv_uvedit objs = common.get_uv_editable_objects(context) # poll() method ensures that only one object is selected. obj = objs[0] bm = bmesh.from_edit_mesh(obj.data) uv_layer = bm.loops.layers.uv.verify() if common.check_version(2, 73, 0) >= 0: bm.faces.ensure_lookup_table() props.src_uvs = [] for face in bm.faces: if not face.select: continue skip = False for l in face.loops: if not l[uv_layer].select: skip = True break if skip: continue props.src_uvs.append([l[uv_layer].uv.copy() for l in face.loops]) return {'FINISHED'} @BlClassRegistry() class MUV_OT_CopyPasteUVUVEdit_PasteUV(bpy.types.Operator): """ Operation class: Paste UV coordinate on UV/Image Editor """ bl_idname = "uv.muv_copy_paste_uv_uvedit_paste_uv" bl_label = "Paste UV (UV/Image Editor)" bl_description = "Paste UV coordinate (only selected in UV/Image Editor)" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): # we can not get area/space/region from console if common.is_console_mode(): return True sc = context.scene props = sc.muv_props.copy_paste_uv_uvedit if not props.src_uvs: return False return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.copy_paste_uv_uvedit objs = common.get_uv_editable_objects(context) # poll() method ensures that only one object is selected. obj = objs[0] bm = bmesh.from_edit_mesh(obj.data) uv_layer = bm.loops.layers.uv.verify() if common.check_version(2, 73, 0) >= 0: bm.faces.ensure_lookup_table() dest_uvs = [] dest_face_indices = [] for face in bm.faces: if not face.select: continue skip = False for l in face.loops: if not l[uv_layer].select: skip = True break if skip: continue dest_face_indices.append(face.index) uvs = [l[uv_layer].uv.copy() for l in face.loops] dest_uvs.append(uvs) for suvs, duvs in zip(props.src_uvs, dest_uvs): src_diff = suvs[1] - suvs[0] dest_diff = duvs[1] - duvs[0] src_base = suvs[0] dest_base = duvs[0] src_rad = atan2(src_diff.y, src_diff.x) dest_rad = atan2(dest_diff.y, dest_diff.x) if src_rad < dest_rad: radian = dest_rad - src_rad elif src_rad > dest_rad: radian = math.pi * 2 - (src_rad - dest_rad) else: # src_rad == dest_rad radian = 0.0 ratio = dest_diff.length / src_diff.length break for suvs, fidx in zip(props.src_uvs, dest_face_indices): for l, suv in zip(bm.faces[fidx].loops, suvs): base = suv - src_base radian_ref = atan2(base.y, base.x) radian_fin = (radian + radian_ref) length = base.length turn = Vector((length * cos(radian_fin), length * sin(radian_fin))) target_uv = Vector((turn.x * ratio, turn.y * ratio)) + \ dest_base l[uv_layer].uv = target_uv bmesh.update_edit_mesh(obj.data) return {'FINISHED'}