diff options
author | Campbell Barton <ideasman42@gmail.com> | 2018-12-19 03:56:05 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2018-12-19 03:56:05 +0300 |
commit | 6a1ce20043860e4f836294d46a86b5fcf7fbf1a8 (patch) | |
tree | d99f11f29954067c147cde7b99e97ddb19ef5e00 /uv_magic_uv/op/copy_paste_uv.py | |
parent | 156c5ea6a45d0549d8422e3ea295972dec7766c3 (diff) | |
parent | 9cc2ad1eaf941d8ed3b5542a3d5cdfccec7ba60b (diff) |
Merge branch 'master' into blender2.8
Diffstat (limited to 'uv_magic_uv/op/copy_paste_uv.py')
-rw-r--r-- | uv_magic_uv/op/copy_paste_uv.py | 744 |
1 files changed, 312 insertions, 432 deletions
diff --git a/uv_magic_uv/op/copy_paste_uv.py b/uv_magic_uv/op/copy_paste_uv.py index ee89b5e9..23bc8343 100644 --- a/uv_magic_uv/op/copy_paste_uv.py +++ b/uv_magic_uv/op/copy_paste_uv.py @@ -18,121 +18,179 @@ # # ##### END GPL LICENSE BLOCK ##### -__author__ = "imdjs, Nutti <nutti.metro@gmail.com>" +__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester" __status__ = "production" -__version__ = "5.1" -__date__ = "24 Feb 2018" +__version__ = "5.2" +__date__ = "17 Nov 2018" -import math -from math import atan2, sin, cos -import bpy import bmesh +import bpy.utils from bpy.props import ( StringProperty, BoolProperty, IntProperty, EnumProperty, ) -from mathutils import Vector +from ..impl import copy_paste_uv_impl as impl from .. import common +from ..utils.bl_class_registry import BlClassRegistry +from ..utils.property_class_registry import PropertyClassRegistry + +__all__ = [ + 'Properties', + 'MUV_OT_CopyPasteUV_CopyUV', + 'MUV_MT_CopyPasteUV_CopyUV', + 'MUV_OT_CopyPasteUV_PasteUV', + 'MUV_MT_CopyPasteUV_PasteUV', + 'MUV_OT_CopyPasteUV_SelSeqCopyUV', + 'MUV_MT_CopyPasteUV_SelSeqCopyUV', + 'MUV_OT_CopyPasteUV_SelSeqPasteUV', + 'MUV_MT_CopyPasteUV_SelSeqPasteUV', +] + + +@PropertyClassRegistry() +class Properties: + idname = "copy_paste_uv" + + @classmethod + def init_props(cls, scene): + class Props(): + src_info = None + + scene.muv_props.copy_paste_uv = Props() + scene.muv_props.copy_paste_uv_selseq = Props() + + scene.muv_copy_paste_uv_enabled = BoolProperty( + name="Copy/Paste UV Enabled", + description="Copy/Paste UV is enabled", + default=False + ) + scene.muv_copy_paste_uv_copy_seams = BoolProperty( + name="Seams", + description="Copy Seams", + default=True + ) + scene.muv_copy_paste_uv_mode = EnumProperty( + items=[ + ('DEFAULT', "Default", "Default Mode"), + ('SEL_SEQ', "Selection Sequence", "Selection Sequence Mode") + ], + name="Copy/Paste UV Mode", + description="Copy/Paste UV Mode", + default='DEFAULT' + ) + scene.muv_copy_paste_uv_strategy = EnumProperty( + name="Strategy", + description="Paste Strategy", + items=[ + ('N_N', 'N:N', 'Number of faces must be equal to source'), + ('N_M', 'N:M', 'Number of faces must not be equal to source') + ], + default='N_M' + ) + + @classmethod + def del_props(cls, scene): + del scene.muv_props.copy_paste_uv + del scene.muv_props.copy_paste_uv_selseq + del scene.muv_copy_paste_uv_enabled + del scene.muv_copy_paste_uv_copy_seams + del scene.muv_copy_paste_uv_mode + del scene.muv_copy_paste_uv_strategy -class MUV_CPUVCopyUV(bpy.types.Operator): +@BlClassRegistry() +class MUV_OT_CopyPasteUV_CopyUV(bpy.types.Operator): """ Operation class: Copy UV coordinate """ - bl_idname = "uv.muv_cpuv_copy_uv" - bl_label = "Copy UV (Operation)" - bl_description = "Copy UV coordinate (Operation)" + bl_idname = "uv.muv_copy_paste_uv_operator_copy_uv" + bl_label = "Copy UV" + bl_description = "Copy UV coordinate" bl_options = {'REGISTER', 'UNDO'} - uv_map = StringProperty(options={'HIDDEN'}) + uv_map: StringProperty(default="__default", options={'HIDDEN'}) + + @classmethod + def poll(cls, context): + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return impl.is_valid_context(context) def execute(self, context): - props = context.scene.muv_props.cpuv - if self.uv_map == "": - self.report({'INFO'}, "Copy UV coordinate") - else: - self.report( - {'INFO'}, "Copy UV coordinate (UV map:%s)" % (self.uv_map)) + props = context.scene.muv_props.copy_paste_uv obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() + bm = common.create_bmesh(obj) # get UV layer - if self.uv_map == "": - if not bm.loops.layers.uv: - self.report( - {'WARNING'}, "Object must have more than one UV map") - return {'CANCELLED'} - uv_layer = bm.loops.layers.uv.verify() - else: - uv_layer = bm.loops.layers.uv[self.uv_map] + uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map) + if not uv_layers: + return {'CANCELLED'} # get selected face - props.src_uvs = [] - props.src_pin_uvs = [] - props.src_seams = [] - for face in bm.faces: - if face.select: - uvs = [l[uv_layer].uv.copy() for l in face.loops] - pin_uvs = [l[uv_layer].pin_uv for l in face.loops] - seams = [l.edge.seam for l in face.loops] - props.src_uvs.append(uvs) - props.src_pin_uvs.append(pin_uvs) - props.src_seams.append(seams) - if not props.src_uvs or not props.src_pin_uvs: - self.report({'WARNING'}, "No faces are selected") + src_info = impl.get_src_face_info(self, bm, uv_layers) + if src_info is None: return {'CANCELLED'} - self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs)) + props.src_info = src_info + + face_count = len(props.src_info[list(props.src_info.keys())[0]]) + self.report({'INFO'}, "{} face(s) are copied".format(face_count)) return {'FINISHED'} -class MUV_CPUVCopyUVMenu(bpy.types.Menu): +@BlClassRegistry() +class MUV_MT_CopyPasteUV_CopyUV(bpy.types.Menu): """ Menu class: Copy UV coordinate """ - bl_idname = "uv.muv_cpuv_copy_uv_menu" - bl_label = "Copy UV" - bl_description = "Copy UV coordinate" + bl_idname = "uv.muv_copy_paste_uv_menu_copy_uv" + bl_label = "Copy UV (Menu)" + bl_description = "Menu of Copy UV coordinate" + + @classmethod + def poll(cls, context): + return impl.is_valid_context(context) def draw(self, context): layout = self.layout # create sub menu obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) + bm = common.create_bmesh(obj) uv_maps = bm.loops.layers.uv.keys() - layout.operator( - MUV_CPUVCopyUV.bl_idname, - text="[Default]", - icon="IMAGE_COL" - ).uv_map = "" + + ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname, + text="[Default]") + ops.uv_map = "__default" + + ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname, + text="[All]") + ops.uv_map = "__all" + for m in uv_maps: - layout.operator( - MUV_CPUVCopyUV.bl_idname, - text=m, - icon="IMAGE_COL" - ).uv_map = m + ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname, text=m) + ops.uv_map = m -class MUV_CPUVPasteUV(bpy.types.Operator): +@BlClassRegistry() +class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator): """ Operation class: Paste UV coordinate """ - bl_idname = "uv.muv_cpuv_paste_uv" - bl_label = "Paste UV (Operation)" - bl_description = "Paste UV coordinate (Operation)" + bl_idname = "uv.muv_copy_paste_uv_operator_paste_uv" + bl_label = "Paste UV" + bl_description = "Paste UV coordinate" bl_options = {'REGISTER', 'UNDO'} - uv_map = StringProperty(options={'HIDDEN'}) - strategy = EnumProperty( + uv_map: StringProperty(default="__default", options={'HIDDEN'}) + strategy: EnumProperty( name="Strategy", description="Paste Strategy", items=[ @@ -141,353 +199,209 @@ class MUV_CPUVPasteUV(bpy.types.Operator): ], default="N_M" ) - flip_copied_uv = BoolProperty( + flip_copied_uv: BoolProperty( name="Flip Copied UV", description="Flip Copied UV...", default=False ) - rotate_copied_uv = IntProperty( + rotate_copied_uv: IntProperty( default=0, name="Rotate Copied UV", min=0, max=30 ) - copy_seams = BoolProperty( - name="Copy Seams", + copy_seams: BoolProperty( + name="Seams", description="Copy Seams", default=True ) + @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 + if not props.src_info: + return False + return impl.is_valid_context(context) + def execute(self, context): - props = context.scene.muv_props.cpuv - if not props.src_uvs or not props.src_pin_uvs: + props = context.scene.muv_props.copy_paste_uv + if not props.src_info: self.report({'WARNING'}, "Need copy UV at first") return {'CANCELLED'} - if self.uv_map == "": - self.report({'INFO'}, "Paste UV coordinate") - else: - self.report( - {'INFO'}, "Paste UV coordinate (UV map:%s)" % (self.uv_map)) obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() + bm = common.create_bmesh(obj) # get UV layer - if self.uv_map == "": - if not bm.loops.layers.uv: - self.report( - {'WARNING'}, "Object must have more than one UV map") - return {'CANCELLED'} - uv_layer = bm.loops.layers.uv.verify() - else: - uv_layer = bm.loops.layers.uv[self.uv_map] + uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info, + self.uv_map) + if not uv_layers: + return {'CANCELLED'} # get selected face - dest_uvs = [] - dest_pin_uvs = [] - dest_seams = [] - dest_face_indices = [] - for face in bm.faces: - if face.select: - dest_face_indices.append(face.index) - uvs = [l[uv_layer].uv.copy() for l in face.loops] - pin_uvs = [l[uv_layer].pin_uv for l in face.loops] - seams = [l.edge.seam for l in face.loops] - dest_uvs.append(uvs) - dest_pin_uvs.append(pin_uvs) - dest_seams.append(seams) - if not dest_uvs or not dest_pin_uvs: - self.report({'WARNING'}, "No faces are selected") - return {'CANCELLED'} - if self.strategy == 'N_N' and len(props.src_uvs) != len(dest_uvs): - self.report( - {'WARNING'}, - "Number of selected faces is different from copied" + - "(src:%d, dest:%d)" % - (len(props.src_uvs), len(dest_uvs))) + dest_info = impl.get_dest_face_info(self, bm, uv_layers, + props.src_info, self.strategy) + if dest_info is None: return {'CANCELLED'} # paste - for i, idx in enumerate(dest_face_indices): - suv = None - spuv = None - ss = None - duv = None - if self.strategy == 'N_N': - suv = props.src_uvs[i] - spuv = props.src_pin_uvs[i] - ss = props.src_seams[i] - duv = dest_uvs[i] - elif self.strategy == 'N_M': - suv = props.src_uvs[i % len(props.src_uvs)] - spuv = props.src_pin_uvs[i % len(props.src_pin_uvs)] - ss = props.src_seams[i % len(props.src_seams)] - duv = dest_uvs[i] - if len(suv) != len(duv): - self.report({'WARNING'}, "Some faces are different size") - return {'CANCELLED'} - suvs_fr = [uv for uv in suv] - spuvs_fr = [pin_uv for pin_uv in spuv] - ss_fr = [s for s in ss] - # flip UVs - if self.flip_copied_uv is True: - suvs_fr.reverse() - spuvs_fr.reverse() - ss_fr.reverse() - # rotate UVs - for _ in range(self.rotate_copied_uv): - uv = suvs_fr.pop() - pin_uv = spuvs_fr.pop() - s = ss_fr.pop() - suvs_fr.insert(0, uv) - spuvs_fr.insert(0, pin_uv) - ss_fr.insert(0, s) - # paste UVs - for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr, - spuvs_fr, ss_fr): - l[uv_layer].uv = suv - l[uv_layer].pin_uv = spuv - if self.copy_seams is True: - l.edge.seam = ss - self.report({'INFO'}, "%d face(s) are copied" % len(dest_uvs)) + ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers, + self.strategy, self.flip_copied_uv, + self.rotate_copied_uv, self.copy_seams) + if ret: + return {'CANCELLED'} + + face_count = len(props.src_info[list(dest_info.keys())[0]]) + self.report({'INFO'}, "{} face(s) are pasted".format(face_count)) bmesh.update_edit_mesh(obj.data) - if self.copy_seams is True: - obj.data.show_edge_seams = True return {'FINISHED'} -class MUV_CPUVPasteUVMenu(bpy.types.Menu): +@BlClassRegistry() +class MUV_MT_CopyPasteUV_PasteUV(bpy.types.Menu): """ Menu class: Paste UV coordinate """ - bl_idname = "uv.muv_cpuv_paste_uv_menu" - bl_label = "Paste UV" - bl_description = "Paste UV coordinate" + bl_idname = "uv.muv_copy_paste_uv_menu_paste_uv" + bl_label = "Paste UV (Menu)" + bl_description = "Menu of Paste UV coordinate" + + @classmethod + def poll(cls, context): + sc = context.scene + props = sc.muv_props.copy_paste_uv + if not props.src_info: + return False + return impl.is_valid_context(context) def draw(self, context): sc = context.scene layout = self.layout # create sub menu obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) + bm = common.create_bmesh(obj) uv_maps = bm.loops.layers.uv.keys() - ops = layout.operator(MUV_CPUVPasteUV.bl_idname, text="[Default]") - ops.uv_map = "" - ops.copy_seams = sc.muv_cpuv_copy_seams - ops.strategy = sc.muv_cpuv_strategy + + ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname, + text="[Default]") + ops.uv_map = "__default" + ops.copy_seams = sc.muv_copy_paste_uv_copy_seams + ops.strategy = sc.muv_copy_paste_uv_strategy + + ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname, + text="[New]") + ops.uv_map = "__new" + ops.copy_seams = sc.muv_copy_paste_uv_copy_seams + ops.strategy = sc.muv_copy_paste_uv_strategy + + ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname, + text="[All]") + ops.uv_map = "__all" + ops.copy_seams = sc.muv_copy_paste_uv_copy_seams + ops.strategy = sc.muv_copy_paste_uv_strategy + for m in uv_maps: - ops = layout.operator(MUV_CPUVPasteUV.bl_idname, text=m) + ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname, text=m) ops.uv_map = m - ops.copy_seams = sc.muv_cpuv_copy_seams - ops.strategy = sc.muv_cpuv_strategy + ops.copy_seams = sc.muv_copy_paste_uv_copy_seams + ops.strategy = sc.muv_copy_paste_uv_strategy -class MUV_CPUVIECopyUV(bpy.types.Operator): +@BlClassRegistry() +class MUV_OT_CopyPasteUV_SelSeqCopyUV(bpy.types.Operator): """ - Operation class: Copy UV coordinate on UV/Image Editor + Operation class: Copy UV coordinate by selection sequence """ - bl_idname = "uv.muv_cpuv_ie_copy_uv" - bl_label = "Copy UV" - bl_description = "Copy UV coordinate (only selected in UV/Image Editor)" + bl_idname = "uv.muv_copy_paste_uv_operator_selseq_copy_uv" + bl_label = "Copy UV (Selection Sequence)" + bl_description = "Copy UV data by selection sequence" bl_options = {'REGISTER', 'UNDO'} - @classmethod - def poll(cls, context): - return context.mode == 'EDIT_MESH' - - def execute(self, context): - props = context.scene.muv_props.cpuv - obj = context.active_object - 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() - - 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'} - - -class MUV_CPUVIEPasteUV(bpy.types.Operator): - """ - Operation class: Paste UV coordinate on UV/Image Editor - """ - - bl_idname = "uv.muv_cpuv_ie_paste_uv" - bl_label = "Paste UV" - bl_description = "Paste UV coordinate (only selected in UV/Image Editor)" - bl_options = {'REGISTER', 'UNDO'} + uv_map: StringProperty(default="__default", options={'HIDDEN'}) @classmethod def poll(cls, context): - return context.mode == 'EDIT_MESH' + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return impl.is_valid_context(context) def execute(self, context): - props = context.scene.muv_props.cpuv + props = context.scene.muv_props.copy_paste_uv_selseq obj = context.active_object - 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'} - - -class MUV_CPUVSelSeqCopyUV(bpy.types.Operator): - """ - Operation class: Copy UV coordinate by selection sequence - """ - - bl_idname = "uv.muv_cpuv_selseq_copy_uv" - bl_label = "Copy UV (Selection Sequence) (Operation)" - bl_description = "Copy UV data by selection sequence (Operation)" - bl_options = {'REGISTER', 'UNDO'} - - uv_map = StringProperty(options={'HIDDEN'}) - - def execute(self, context): - props = context.scene.muv_props.cpuv_selseq - if self.uv_map == "": - self.report({'INFO'}, "Copy UV coordinate (selection sequence)") - else: - self.report( - {'INFO'}, - "Copy UV coordinate (selection sequence) (UV map:%s)" - % (self.uv_map)) - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() + bm = common.create_bmesh(obj) # get UV layer - if self.uv_map == "": - if not bm.loops.layers.uv: - self.report( - {'WARNING'}, "Object must have more than one UV map") - return {'CANCELLED'} - uv_layer = bm.loops.layers.uv.verify() - else: - uv_layer = bm.loops.layers.uv[self.uv_map] + uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map) + if not uv_layers: + return {'CANCELLED'} # get selected face - props.src_uvs = [] - props.src_pin_uvs = [] - props.src_seams = [] - for hist in bm.select_history: - if isinstance(hist, bmesh.types.BMFace) and hist.select: - uvs = [l[uv_layer].uv.copy() for l in hist.loops] - pin_uvs = [l[uv_layer].pin_uv for l in hist.loops] - seams = [l.edge.seam for l in hist.loops] - props.src_uvs.append(uvs) - props.src_pin_uvs.append(pin_uvs) - props.src_seams.append(seams) - if not props.src_uvs or not props.src_pin_uvs: - self.report({'WARNING'}, "No faces are selected") + src_info = impl.get_select_history_src_face_info(self, bm, uv_layers) + if src_info is None: return {'CANCELLED'} - self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs)) + props.src_info = src_info + + face_count = len(props.src_info[list(props.src_info.keys())[0]]) + self.report({'INFO'}, "{} face(s) are selected".format(face_count)) return {'FINISHED'} -class MUV_CPUVSelSeqCopyUVMenu(bpy.types.Menu): +@BlClassRegistry() +class MUV_MT_CopyPasteUV_SelSeqCopyUV(bpy.types.Menu): """ Menu class: Copy UV coordinate by selection sequence """ - bl_idname = "uv.muv_cpuv_selseq_copy_uv_menu" - bl_label = "Copy UV (Selection Sequence)" - bl_description = "Copy UV coordinate by selection sequence" + bl_idname = "uv.muv_copy_paste_uv_menu_selseq_copy_uv" + bl_label = "Copy UV (Selection Sequence) (Menu)" + bl_description = "Menu of Copy UV coordinate by selection sequence" + + @classmethod + def poll(cls, context): + return impl.is_valid_context(context) def draw(self, context): layout = self.layout obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) + bm = common.create_bmesh(obj) uv_maps = bm.loops.layers.uv.keys() - layout.operator( - MUV_CPUVSelSeqCopyUV.bl_idname, - text="[Default]", icon="IMAGE_COL").uv_map = "" + + ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname, + text="[Default]") + ops.uv_map = "__default" + + ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname, + text="[All]") + ops.uv_map = "__all" + for m in uv_maps: - layout.operator( - MUV_CPUVSelSeqCopyUV.bl_idname, - text=m, icon="IMAGE_COL").uv_map = m + ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname, + text=m) + ops.uv_map = m -class MUV_CPUVSelSeqPasteUV(bpy.types.Operator): +@BlClassRegistry() +class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator): """ Operation class: Paste UV coordinate by selection sequence """ - bl_idname = "uv.muv_cpuv_selseq_paste_uv" - bl_label = "Paste UV (Selection Sequence) (Operation)" - bl_description = "Paste UV coordinate by selection sequence (Operation)" + bl_idname = "uv.muv_copy_paste_uv_operator_selseq_paste_uv" + bl_label = "Paste UV (Selection Sequence)" + bl_description = "Paste UV coordinate by selection sequence" bl_options = {'REGISTER', 'UNDO'} - uv_map = StringProperty(options={'HIDDEN'}) - strategy = EnumProperty( + uv_map: StringProperty(default="__default", options={'HIDDEN'}) + strategy: EnumProperty( name="Strategy", description="Paste Strategy", items=[ @@ -496,151 +410,117 @@ class MUV_CPUVSelSeqPasteUV(bpy.types.Operator): ], default="N_M" ) - flip_copied_uv = BoolProperty( + flip_copied_uv: BoolProperty( name="Flip Copied UV", description="Flip Copied UV...", default=False ) - rotate_copied_uv = IntProperty( + rotate_copied_uv: IntProperty( default=0, name="Rotate Copied UV", min=0, max=30 ) - copy_seams = BoolProperty( - name="Copy Seams", + copy_seams: BoolProperty( + name="Seams", description="Copy Seams", default=True ) + @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_selseq + if not props.src_info: + return False + return impl.is_valid_context(context) + def execute(self, context): - props = context.scene.muv_props.cpuv_selseq - if not props.src_uvs or not props.src_pin_uvs: + props = context.scene.muv_props.copy_paste_uv_selseq + if not props.src_info: self.report({'WARNING'}, "Need copy UV at first") return {'CANCELLED'} - if self.uv_map == "": - self.report({'INFO'}, "Paste UV coordinate (selection sequence)") - else: - self.report( - {'INFO'}, - "Paste UV coordinate (selection sequence) (UV map:%s)" - % (self.uv_map)) - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() + bm = common.create_bmesh(obj) # get UV layer - if self.uv_map == "": - if not bm.loops.layers.uv: - self.report( - {'WARNING'}, "Object must have more than one UV map") - return {'CANCELLED'} - uv_layer = bm.loops.layers.uv.verify() - else: - uv_layer = bm.loops.layers.uv[self.uv_map] + uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info, + self.uv_map) + if not uv_layers: + return {'CANCELLED'} # get selected face - dest_uvs = [] - dest_pin_uvs = [] - dest_seams = [] - dest_face_indices = [] - for hist in bm.select_history: - if isinstance(hist, bmesh.types.BMFace) and hist.select: - dest_face_indices.append(hist.index) - uvs = [l[uv_layer].uv.copy() for l in hist.loops] - pin_uvs = [l[uv_layer].pin_uv for l in hist.loops] - seams = [l.edge.seam for l in hist.loops] - dest_uvs.append(uvs) - dest_pin_uvs.append(pin_uvs) - dest_seams.append(seams) - if not dest_uvs or not dest_pin_uvs: - self.report({'WARNING'}, "No faces are selected") - return {'CANCELLED'} - if self.strategy == 'N_N' and len(props.src_uvs) != len(dest_uvs): - self.report( - {'WARNING'}, - "Number of selected faces is different from copied faces " + - "(src:%d, dest:%d)" - % (len(props.src_uvs), len(dest_uvs))) + dest_info = impl.get_select_history_dest_face_info(self, bm, uv_layers, + props.src_info, + self.strategy) + if dest_info is None: return {'CANCELLED'} # paste - for i, idx in enumerate(dest_face_indices): - suv = None - spuv = None - ss = None - duv = None - if self.strategy == 'N_N': - suv = props.src_uvs[i] - spuv = props.src_pin_uvs[i] - ss = props.src_seams[i] - duv = dest_uvs[i] - elif self.strategy == 'N_M': - suv = props.src_uvs[i % len(props.src_uvs)] - spuv = props.src_pin_uvs[i % len(props.src_pin_uvs)] - ss = props.src_seams[i % len(props.src_seams)] - duv = dest_uvs[i] - if len(suv) != len(duv): - self.report({'WARNING'}, "Some faces are different size") - return {'CANCELLED'} - suvs_fr = [uv for uv in suv] - spuvs_fr = [pin_uv for pin_uv in spuv] - ss_fr = [s for s in ss] - # flip UVs - if self.flip_copied_uv is True: - suvs_fr.reverse() - spuvs_fr.reverse() - ss_fr.reverse() - # rotate UVs - for _ in range(self.rotate_copied_uv): - uv = suvs_fr.pop() - pin_uv = spuvs_fr.pop() - s = ss_fr.pop() - suvs_fr.insert(0, uv) - spuvs_fr.insert(0, pin_uv) - ss_fr.insert(0, s) - # paste UVs - for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr, - spuvs_fr, ss_fr): - l[uv_layer].uv = suv - l[uv_layer].pin_uv = spuv - if self.copy_seams is True: - l.edge.seam = ss - - self.report({'INFO'}, "%d face(s) are copied" % len(dest_uvs)) + ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers, + self.strategy, self.flip_copied_uv, + self.rotate_copied_uv, self.copy_seams) + if ret: + return {'CANCELLED'} + + face_count = len(props.src_info[list(dest_info.keys())[0]]) + self.report({'INFO'}, "{} face(s) are pasted".format(face_count)) bmesh.update_edit_mesh(obj.data) - if self.copy_seams is True: - obj.data.show_edge_seams = True return {'FINISHED'} -class MUV_CPUVSelSeqPasteUVMenu(bpy.types.Menu): +@BlClassRegistry() +class MUV_MT_CopyPasteUV_SelSeqPasteUV(bpy.types.Menu): """ Menu class: Paste UV coordinate by selection sequence """ - bl_idname = "uv.muv_cpuv_selseq_paste_uv_menu" - bl_label = "Paste UV (Selection Sequence)" - bl_description = "Paste UV coordinate by selection sequence" + bl_idname = "uv.muv_copy_paste_uv_menu_selseq_paste_uv" + bl_label = "Paste UV (Selection Sequence) (Menu)" + bl_description = "Menu of Paste UV coordinate by selection sequence" + + @classmethod + def poll(cls, context): + sc = context.scene + props = sc.muv_props.copy_paste_uv_selseq + if not props.src_uvs or not props.src_pin_uvs: + return False + return impl.is_valid_context(context) def draw(self, context): sc = context.scene layout = self.layout # create sub menu obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) + bm = common.create_bmesh(obj) uv_maps = bm.loops.layers.uv.keys() - ops = layout.operator(MUV_CPUVSelSeqPasteUV.bl_idname, + + ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname, text="[Default]") - ops.uv_map = "" - ops.copy_seams = sc.muv_cpuv_copy_seams - ops.strategy = sc.muv_cpuv_strategy + ops.uv_map = "__default" + ops.copy_seams = sc.muv_copy_paste_uv_copy_seams + ops.strategy = sc.muv_copy_paste_uv_strategy + + ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname, + text="[New]") + ops.uv_map = "__new" + ops.copy_seams = sc.muv_copy_paste_uv_copy_seams + ops.strategy = sc.muv_copy_paste_uv_strategy + + ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname, + text="[All]") + ops.uv_map = "__all" + ops.copy_seams = sc.muv_copy_paste_uv_copy_seams + ops.strategy = sc.muv_copy_paste_uv_strategy + for m in uv_maps: - ops = layout.operator(MUV_CPUVSelSeqPasteUV.bl_idname, text=m) + ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname, + text=m) ops.uv_map = m - ops.copy_seams = sc.muv_cpuv_copy_seams - ops.strategy = sc.muv_cpuv_strategy + ops.copy_seams = sc.muv_copy_paste_uv_copy_seams + ops.strategy = sc.muv_copy_paste_uv_strategy |