Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'uv_magic_uv/op/copy_paste_uv.py')
-rw-r--r--uv_magic_uv/op/copy_paste_uv.py883
1 files changed, 464 insertions, 419 deletions
diff --git a/uv_magic_uv/op/copy_paste_uv.py b/uv_magic_uv/op/copy_paste_uv.py
index ee89b5e9..cc1baa30 100644
--- a/uv_magic_uv/op/copy_paste_uv.py
+++ b/uv_magic_uv/op/copy_paste_uv.py
@@ -20,11 +20,9 @@
__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
__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
@@ -34,104 +32,293 @@ from bpy.props import (
IntProperty,
EnumProperty,
)
-from mathutils import Vector
from .. import common
-class MUV_CPUVCopyUV(bpy.types.Operator):
+__all__ = [
+ 'Properties',
+ 'OpeartorCopyUV',
+ 'MenuCopyUV',
+ 'OperatorPasteUV',
+ 'MenuPasteUV',
+ 'OperatorSelSeqCopyUV',
+ 'MenuSelSeqCopyUV',
+ 'OperatorSelSeqPasteUV',
+ 'MenuSelSeqPasteUV',
+]
+
+
+def is_valid_context(context):
+ obj = context.object
+
+ # only edit mode is allowed to execute
+ if obj is None:
+ return False
+ if obj.type != 'MESH':
+ return False
+ if context.object.mode != 'EDIT':
+ return False
+
+ # only 'VIEW_3D' space is allowed to execute
+ for space in context.area.spaces:
+ if space.type == 'VIEW_3D':
+ break
+ else:
+ return False
+
+ return True
+
+
+def get_copy_uv_layers(ops_obj, bm):
+ uv_layers = []
+ if ops_obj.uv_map == "__default":
+ if not bm.loops.layers.uv:
+ ops_obj.report(
+ {'WARNING'}, "Object must have more than one UV map")
+ return None
+ uv_layers.append(bm.loops.layers.uv.verify())
+ ops_obj.report({'INFO'}, "Copy UV coordinate")
+ elif ops_obj.uv_map == "__all":
+ for uv in bm.loops.layers.uv.keys():
+ uv_layers.append(bm.loops.layers.uv[uv])
+ ops_obj.report({'INFO'}, "Copy UV coordinate (UV map: ALL)")
+ else:
+ uv_layers.append(bm.loops.layers.uv[ops_obj.uv_map])
+ ops_obj.report(
+ {'INFO'}, "Copy UV coordinate (UV map:{})".format(ops_obj.uv_map))
+
+ return uv_layers
+
+
+def get_paste_uv_layers(ops_obj, obj, bm, src_info):
+ uv_layers = []
+ if ops_obj.uv_map == "__default":
+ if not bm.loops.layers.uv:
+ ops_obj.report(
+ {'WARNING'}, "Object must have more than one UV map")
+ return None
+ uv_layers.append(bm.loops.layers.uv.verify())
+ ops_obj.report({'INFO'}, "Paste UV coordinate")
+ elif ops_obj.uv_map == "__new":
+ new_uv_map = common.create_new_uv_map(obj)
+ if not new_uv_map:
+ ops_obj.report({'WARNING'},
+ "Reached to the maximum number of UV map")
+ return None
+ uv_layers.append(bm.loops.layers.uv[new_uv_map.name])
+ ops_obj.report(
+ {'INFO'}, "Paste UV coordinate (UV map:{})".format(new_uv_map))
+ elif ops_obj.uv_map == "__all":
+ for src_layer in src_info.keys():
+ if src_layer not in bm.loops.layers.uv.keys():
+ new_uv_map = common.create_new_uv_map(obj, src_layer)
+ if not new_uv_map:
+ ops_obj.report({'WARNING'},
+ "Reached to the maximum number of UV map")
+ return None
+ uv_layers.append(bm.loops.layers.uv[src_layer])
+ ops_obj.report({'INFO'}, "Paste UV coordinate (UV map: ALL)")
+ else:
+ uv_layers.append(bm.loops.layers.uv[ops_obj.uv_map])
+ ops_obj.report(
+ {'INFO'}, "Paste UV coordinate (UV map:{})".format(ops_obj.uv_map))
+
+ return uv_layers
+
+
+def paste_uv(ops_obj, bm, src_info, dest_info, uv_layers, strategy, flip,
+ rotate, copy_seams):
+ for slayer_name, dlayer in zip(src_info.keys(), uv_layers):
+ src_faces = src_info[slayer_name]
+ dest_faces = dest_info[dlayer.name]
+
+ for idx, dinfo in enumerate(dest_faces):
+ sinfo = None
+ if strategy == 'N_N':
+ sinfo = src_faces[idx]
+ elif strategy == 'N_M':
+ sinfo = src_faces[idx % len(src_faces)]
+
+ suv = sinfo["uvs"]
+ spuv = sinfo["pin_uvs"]
+ ss = sinfo["seams"]
+ if len(sinfo["uvs"]) != len(dinfo["uvs"]):
+ ops_obj.report({'WARNING'}, "Some faces are different size")
+ return -1
+
+ 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 flip is True:
+ suvs_fr.reverse()
+ spuvs_fr.reverse()
+ ss_fr.reverse()
+
+ # rotate UVs
+ for _ in range(rotate):
+ 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[dlayer].uv = suv
+ l[dlayer].pin_uv = spuv
+ if copy_seams is True:
+ l.edge.seam = ss
+
+ return 0
+
+
+class Properties:
+ @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 OpeartorCopyUV(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 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 = get_copy_uv_layers(self, bm)
+ 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")
- return {'CANCELLED'}
- self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs))
+ props.src_info = {}
+ for layer in uv_layers:
+ face_info = []
+ for face in bm.faces:
+ if face.select:
+ info = {
+ "uvs": [l[layer].uv.copy() for l in face.loops],
+ "pin_uvs": [l[layer].pin_uv for l in face.loops],
+ "seams": [l.edge.seam for l in face.loops],
+ }
+ face_info.append(info)
+ if not face_info:
+ self.report({'WARNING'}, "No faces are selected")
+ return {'CANCELLED'}
+ props.src_info[layer.name] = face_info
+
+ face_count = len([f for f in bm.faces if f.select])
+ self.report({'INFO'}, "{} face(s) are copied".format(face_count))
return {'FINISHED'}
-class MUV_CPUVCopyUVMenu(bpy.types.Menu):
+class MenuCopyUV(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 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(OpeartorCopyUV.bl_idname, text="[Default]")
+ ops.uv_map = "__default"
+
+ ops = layout.operator(OpeartorCopyUV.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(OpeartorCopyUV.bl_idname, text=m)
+ ops.uv_map = m
-class MUV_CPUVPasteUV(bpy.types.Operator):
+class OperatorPasteUV(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'})
+ uv_map = StringProperty(default="__default", options={'HIDDEN'})
strategy = EnumProperty(
name="Strategy",
description="Paste Strategy",
@@ -153,104 +340,69 @@ class MUV_CPUVPasteUV(bpy.types.Operator):
max=30
)
copy_seams = BoolProperty(
- name="Copy Seams",
+ 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 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:
+ uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info)
+ if not uv_layers:
+ return {'CANCELLED'}
+
+ # get selected face
+ dest_face_count = 0
+ dest_info = {}
+ for layer in uv_layers:
+ face_info = []
+ for face in bm.faces:
+ if face.select:
+ info = {
+ "uvs": [l[layer].uv.copy() for l in face.loops],
+ }
+ face_info.append(info)
+ if not face_info:
+ self.report({'WARNING'}, "No faces are selected")
+ return {'CANCELLED'}
+ key = list(props.src_info.keys())[0]
+ src_face_count = len(props.src_info[key])
+ dest_face_count = len(face_info)
+ if self.strategy == 'N_N' and src_face_count != dest_face_count:
self.report(
- {'WARNING'}, "Object must have more than one UV map")
+ {'WARNING'},
+ "Number of selected faces is different from copied" +
+ "(src:{}, dest:{})"
+ .format(src_face_count, dest_face_count))
return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
- else:
- uv_layer = bm.loops.layers.uv[self.uv_map]
+ dest_info[layer.name] = face_info
- # 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)))
+ # paste
+ ret = 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'}
- # 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))
+ self.report({'INFO'}, "{} face(s) are pasted".format(dest_face_count))
bmesh.update_edit_mesh(obj.data)
if self.copy_seams is True:
@@ -259,234 +411,146 @@ class MUV_CPUVPasteUV(bpy.types.Operator):
return {'FINISHED'}
-class MUV_CPUVPasteUVMenu(bpy.types.Menu):
+class MenuPasteUV(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 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
- for m in uv_maps:
- ops = layout.operator(MUV_CPUVPasteUV.bl_idname, text=m)
- ops.uv_map = m
- ops.copy_seams = sc.muv_cpuv_copy_seams
- ops.strategy = sc.muv_cpuv_strategy
-
-class MUV_CPUVIECopyUV(bpy.types.Operator):
- """
- Operation class: Copy UV coordinate on UV/Image Editor
- """
-
- bl_idname = "uv.muv_cpuv_ie_copy_uv"
- bl_label = "Copy UV"
- bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
- bl_options = {'REGISTER', 'UNDO'}
+ ops = layout.operator(OperatorPasteUV.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
- @classmethod
- def poll(cls, context):
- return context.mode == 'EDIT_MESH'
+ ops = layout.operator(OperatorPasteUV.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
- 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])
+ ops = layout.operator(OperatorPasteUV.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
- return {'FINISHED'}
+ for m in uv_maps:
+ ops = layout.operator(OperatorPasteUV.bl_idname, text=m)
+ ops.uv_map = m
+ ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+ ops.strategy = sc.muv_copy_paste_uv_strategy
-class MUV_CPUVIEPasteUV(bpy.types.Operator):
+class OperatorSelSeqCopyUV(bpy.types.Operator):
"""
- Operation class: Paste UV coordinate on UV/Image Editor
+ Operation class: Copy UV coordinate by selection sequence
"""
- bl_idname = "uv.muv_cpuv_ie_paste_uv"
- bl_label = "Paste UV"
- bl_description = "Paste 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'}
+ uv_map = StringProperty(default="__default", options={'HIDDEN'})
+
@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()
-
- 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'})
+ # 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.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))
+ props = context.scene.muv_props.copy_paste_uv_selseq
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 = get_copy_uv_layers(self, bm)
+ 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")
- return {'CANCELLED'}
- self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs))
+ props.src_info = {}
+ for layer in uv_layers:
+ face_info = []
+ for hist in bm.select_history:
+ if isinstance(hist, bmesh.types.BMFace) and hist.select:
+ info = {
+ "uvs": [l[layer].uv.copy() for l in hist.loops],
+ "pin_uvs": [l[layer].pin_uv for l in hist.loops],
+ "seams": [l.edge.seam for l in hist.loops],
+ }
+ face_info.append(info)
+ if not face_info:
+ self.report({'WARNING'}, "No faces are selected")
+ return {'CANCELLED'}
+ props.src_info[layer.name] = face_info
+
+ face_count = len([f for f in bm.faces if f.select])
+ self.report({'INFO'}, "{} face(s) are selected".format(face_count))
return {'FINISHED'}
-class MUV_CPUVSelSeqCopyUVMenu(bpy.types.Menu):
+class MenuSelSeqCopyUV(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 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(OperatorSelSeqCopyUV.bl_idname, text="[Default]")
+ ops.uv_map = "__default"
+
+ ops = layout.operator(OperatorSelSeqCopyUV.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(OperatorSelSeqCopyUV.bl_idname, text=m)
+ ops.uv_map = m
-class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
+class OperatorSelSeqPasteUV(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'})
+ uv_map = StringProperty(default="__default", options={'HIDDEN'})
strategy = EnumProperty(
name="Strategy",
description="Paste Strategy",
@@ -508,108 +572,69 @@ class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
max=30
)
copy_seams = BoolProperty(
- name="Copy Seams",
+ 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 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 = get_paste_uv_layers(self, obj, bm, props.src_info)
+ 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)))
- return {'CANCELLED'}
+ dest_face_count = 0
+ dest_info = {}
+ for layer in uv_layers:
+ face_info = []
+ for hist in bm.select_history:
+ if isinstance(hist, bmesh.types.BMFace) and hist.select:
+ info = {
+ "uvs": [l[layer].uv.copy() for l in hist.loops],
+ }
+ face_info.append(info)
+ if not face_info:
+ self.report({'WARNING'}, "No faces are selected")
+ return {'CANCELLED'}
+ key = list(props.src_info.keys())[0]
+ src_face_count = len(props.src_info[key])
+ dest_face_count = len(face_info)
+ if self.strategy == 'N_N' and src_face_count != dest_face_count:
+ self.report(
+ {'WARNING'},
+ "Number of selected faces is different from copied" +
+ "(src:{}, dest:{})"
+ .format(src_face_count, dest_face_count))
+ return {'CANCELLED'}
+ dest_info[layer.name] = face_info
# 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
+ ret = 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'}
- self.report({'INFO'}, "%d face(s) are copied" % len(dest_uvs))
+ self.report({'INFO'}, "{} face(s) are pasted".format(dest_face_count))
bmesh.update_edit_mesh(obj.data)
if self.copy_seams is True:
@@ -618,29 +643,49 @@ class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
return {'FINISHED'}
-class MUV_CPUVSelSeqPasteUVMenu(bpy.types.Menu):
+class MenuSelSeqPasteUV(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 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(OperatorSelSeqPasteUV.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(OperatorSelSeqPasteUV.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(OperatorSelSeqPasteUV.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(OperatorSelSeqPasteUV.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