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:
authorNutti <nutti.metro@gmail.com>2018-12-13 16:57:08 +0300
committerNutti <nutti.metro@gmail.com>2018-12-13 16:57:08 +0300
commitfe5e51ddcd0bcd1f87428cb3c6098fc66f6962f1 (patch)
treee255fc2e2a9500ab94580678d1f5e0251cb85208
parent3a264229d2d37aee3c673545df122e41dc481723 (diff)
Magic UV: Phase 1 for porting to Blender 2.8
Below features are available for now. * Copy/Paste UV * Transfer UV * Flip/Rotate UV * Mirror UV * Move UV * UVW Also, Magic UV remains to work at Blender 2.7x by the legacy code.
-rw-r--r--uv_magic_uv/__init__.py89
-rw-r--r--uv_magic_uv/common.py10
-rw-r--r--uv_magic_uv/impl/__init__.py0
-rw-r--r--uv_magic_uv/impl/copy_paste_uv_impl.py271
-rw-r--r--uv_magic_uv/impl/copy_paste_uv_uvedit_impl.py166
-rw-r--r--uv_magic_uv/impl/flip_rotate_impl.py133
-rw-r--r--uv_magic_uv/impl/mirror_uv_impl.py158
-rw-r--r--uv_magic_uv/impl/move_uv_impl.py166
-rw-r--r--uv_magic_uv/impl/transfer_uv_impl.py330
-rw-r--r--uv_magic_uv/impl/uvw_impl.py154
-rw-r--r--uv_magic_uv/legacy/__init__.py38
-rw-r--r--uv_magic_uv/legacy/op/__init__.py74
-rw-r--r--uv_magic_uv/legacy/op/align_uv.py (renamed from uv_magic_uv/op/align_uv.py)126
-rw-r--r--uv_magic_uv/legacy/op/align_uv_cursor.py (renamed from uv_magic_uv/op/align_uv_cursor.py)12
-rw-r--r--uv_magic_uv/legacy/op/copy_paste_uv.py531
-rw-r--r--uv_magic_uv/legacy/op/copy_paste_uv_object.py298
-rw-r--r--uv_magic_uv/legacy/op/copy_paste_uv_uvedit.py97
-rw-r--r--uv_magic_uv/legacy/op/flip_rotate_uv.py132
-rw-r--r--uv_magic_uv/legacy/op/mirror_uv.py110
-rw-r--r--uv_magic_uv/legacy/op/move_uv.py82
-rw-r--r--uv_magic_uv/legacy/op/pack_uv.py (renamed from uv_magic_uv/op/pack_uv.py)12
-rw-r--r--uv_magic_uv/legacy/op/preserve_uv_aspect.py (renamed from uv_magic_uv/op/preserve_uv_aspect.py)12
-rw-r--r--uv_magic_uv/legacy/op/select_uv.py (renamed from uv_magic_uv/op/select_uv.py)17
-rw-r--r--uv_magic_uv/legacy/op/smooth_uv.py (renamed from uv_magic_uv/op/smooth_uv.py)12
-rw-r--r--uv_magic_uv/legacy/op/texture_lock.py (renamed from uv_magic_uv/op/texture_lock.py)40
-rw-r--r--uv_magic_uv/legacy/op/texture_projection.py (renamed from uv_magic_uv/op/texture_projection.py)29
-rw-r--r--uv_magic_uv/legacy/op/texture_wrap.py (renamed from uv_magic_uv/op/texture_wrap.py)17
-rw-r--r--uv_magic_uv/legacy/op/transfer_uv.py172
-rw-r--r--uv_magic_uv/legacy/op/unwrap_constraint.py (renamed from uv_magic_uv/op/unwrap_constraint.py)12
-rw-r--r--uv_magic_uv/legacy/op/uv_bounding_box.py (renamed from uv_magic_uv/op/uv_bounding_box.py)26
-rw-r--r--uv_magic_uv/legacy/op/uv_inspection.py (renamed from uv_magic_uv/op/uv_inspection.py)32
-rw-r--r--uv_magic_uv/legacy/op/uv_sculpt.py (renamed from uv_magic_uv/op/uv_sculpt.py)24
-rw-r--r--uv_magic_uv/legacy/op/uvw.py181
-rw-r--r--uv_magic_uv/legacy/op/world_scale_uv.py (renamed from uv_magic_uv/op/world_scale_uv.py)27
-rw-r--r--uv_magic_uv/legacy/preferences.py468
-rw-r--r--uv_magic_uv/legacy/properites.py61
-rw-r--r--uv_magic_uv/legacy/ui/IMAGE_MT_uvs.py197
-rw-r--r--uv_magic_uv/legacy/ui/VIEW3D_MT_object.py54
-rw-r--r--uv_magic_uv/legacy/ui/VIEW3D_MT_uv_map.py257
-rw-r--r--uv_magic_uv/legacy/ui/__init__.py50
-rw-r--r--uv_magic_uv/legacy/ui/uvedit_copy_paste_uv.py62
-rw-r--r--uv_magic_uv/legacy/ui/uvedit_editor_enhancement.py (renamed from uv_magic_uv/ui/uvedit_editor_enhancement.py)65
-rw-r--r--uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py (renamed from uv_magic_uv/ui/uvedit_uv_manipulation.py)32
-rw-r--r--uv_magic_uv/legacy/ui/view3d_copy_paste_uv_editmode.py93
-rw-r--r--uv_magic_uv/legacy/ui/view3d_copy_paste_uv_objectmode.py65
-rw-r--r--uv_magic_uv/legacy/ui/view3d_uv_manipulation.py289
-rw-r--r--uv_magic_uv/legacy/ui/view3d_uv_mapping.py116
-rw-r--r--uv_magic_uv/op/__init__.py28
-rw-r--r--uv_magic_uv/op/copy_paste_uv.py393
-rw-r--r--uv_magic_uv/op/copy_paste_uv_object.py140
-rw-r--r--uv_magic_uv/op/copy_paste_uv_uvedit.py151
-rw-r--r--uv_magic_uv/op/flip_rotate_uv.py100
-rw-r--r--uv_magic_uv/op/mirror_uv.py130
-rw-r--r--uv_magic_uv/op/move_uv.py135
-rw-r--r--uv_magic_uv/op/transfer_uv.py332
-rw-r--r--uv_magic_uv/op/uvw.py176
-rw-r--r--uv_magic_uv/preferences.py85
-rw-r--r--uv_magic_uv/properites.py73
-rw-r--r--uv_magic_uv/ui/IMAGE_MT_uvs.py156
-rw-r--r--uv_magic_uv/ui/VIEW3D_MT_object.py18
-rw-r--r--uv_magic_uv/ui/VIEW3D_MT_uv_map.py183
-rw-r--r--uv_magic_uv/ui/__init__.py12
-rw-r--r--uv_magic_uv/ui/uvedit_copy_paste_uv.py21
-rw-r--r--uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py36
-rw-r--r--uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py21
-rw-r--r--uv_magic_uv/ui/view3d_uv_manipulation.py217
-rw-r--r--uv_magic_uv/ui/view3d_uv_mapping.py62
-rw-r--r--uv_magic_uv/utils/__init__.py34
-rw-r--r--uv_magic_uv/utils/bl_class_registry.py84
-rw-r--r--uv_magic_uv/utils/property_class_registry.py72
70 files changed, 5840 insertions, 2218 deletions
diff --git a/uv_magic_uv/__init__.py b/uv_magic_uv/__init__.py
index 20709e79..63591526 100644
--- a/uv_magic_uv/__init__.py
+++ b/uv_magic_uv/__init__.py
@@ -28,8 +28,8 @@ bl_info = {
"name": "Magic UV",
"author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, imdjs"
"Keith (Wahooney) Boshoff, McBuff, MaxRobinot, Alexander Milovsky",
- "version": (5, 2, 0),
- "blender": (2, 79, 0),
+ "version": (5, 3, 0),
+ "blender": (2, 80, 0),
"location": "See Add-ons Preferences",
"description": "UV Toolset. See Add-ons Preferences for details",
"warning": "",
@@ -40,43 +40,80 @@ bl_info = {
"category": "UV"
}
+def check_version(major, minor, _):
+ """
+ Check blender version
+ """
+
+ if bpy.app.version[0] == major and bpy.app.version[1] == minor:
+ return 0
+ if bpy.app.version[0] > major:
+ return 1
+ if bpy.app.version[1] > minor:
+ return 1
+ return -1
+
+
if "bpy" in locals():
import importlib
- importlib.reload(op)
- importlib.reload(ui)
importlib.reload(common)
- importlib.reload(preferences)
- importlib.reload(properites)
- importlib.reload(addon_updater_ops)
- importlib.reload(addon_updater)
+ importlib.reload(utils)
+ utils.bl_class_registry.BlClassRegistry.cleanup()
+ if check_version(2, 80, 0) >= 0:
+ importlib.reload(op)
+ importlib.reload(ui)
+ importlib.reload(properites)
+ importlib.reload(preferences)
+ importlib.reload(addon_updater_ops)
+ importlib.reload(addon_updater)
+ else:
+ importlib.reload(legacy)
else:
- from . import op
- from . import ui
+ import bpy
from . import common
- from . import preferences
- from . import properites
- from . import addon_updater_ops
- from . import addon_updater
+ from . import utils
+ if check_version(2, 80, 0) >= 0:
+ from . import op
+ from . import ui
+ from . import properites
+ from . import preferences
+ from . import addon_updater_ops
+ from . import addon_updater
+ else:
+ from . import legacy
+
import bpy
def register():
- if not common.is_console_mode():
- addon_updater_ops.register(bl_info)
- properites.init_props(bpy.types.Scene)
- bpy.utils.register_module(__name__)
- if preferences.Preferences.enable_builtin_menu:
- preferences.add_builtin_menu()
+ if common.check_version(2, 80, 0) >= 0:
+ utils.bl_class_registry.BlClassRegistry.register()
+ properites.init_props(bpy.types.Scene)
+ if preferences.Preferences.enable_builtin_menu:
+ preferences.add_builtin_menu()
+ else:
+ utils.bl_class_registry.BlClassRegistry.register()
+ legacy.properites.init_props(bpy.types.Scene)
+ if legacy.preferences.Preferences.enable_builtin_menu:
+ legacy.preferences.add_builtin_menu()
+ if not common.is_console_mode():
+ addon_updater_ops.register(bl_info)
def unregister():
- if preferences.Preferences.enable_builtin_menu:
- preferences.remove_builtin_menu()
- bpy.utils.unregister_module(__name__)
- properites.clear_props(bpy.types.Scene)
- if not common.is_console_mode():
- addon_updater_ops.unregister()
+ if common.check_version(2, 80, 0) >= 0:
+ if preferences.Preferences.enable_builtin_menu:
+ preferences.remove_builtin_menu()
+ properites.clear_props(bpy.types.Scene)
+ utils.bl_class_registry.BlClassRegistry.unregister()
+ else:
+ if not common.is_console_mode():
+ addon_updater_ops.unregister()
+ if legacy.preferences.Preferences.enable_builtin_menu:
+ legacy.preferences.remove_builtin_menu()
+ legacy.properites.clear_props(bpy.types.Scene)
+ utils.bl_class_registry.BlClassRegistry.unregister()
if __name__ == "__main__":
diff --git a/uv_magic_uv/common.py b/uv_magic_uv/common.py
index b0c4306e..bad88167 100644
--- a/uv_magic_uv/common.py
+++ b/uv_magic_uv/common.py
@@ -35,10 +35,16 @@ import bmesh
__all__ = [
'is_console_mode',
+ 'is_debug_mode',
+ 'enable_debugg_mode',
+ 'disable_debug_mode',
'debug_print',
'check_version',
'redraw_all_areas',
'get_space',
+ 'mouse_on_region',
+ 'mouse_on_area',
+ 'mouse_on_regions',
'create_bmesh',
'create_new_uv_map',
'get_island_info',
@@ -51,10 +57,12 @@ __all__ = [
'measure_uv_area',
'diff_point_to_segment',
'get_loop_sequences',
+ 'get_overlapped_uv_info',
+ 'get_flipped_uv_info',
]
-__DEBUG_MODE = False
+__DEBUG_MODE = True
def is_console_mode():
diff --git a/uv_magic_uv/impl/__init__.py b/uv_magic_uv/impl/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/uv_magic_uv/impl/__init__.py
diff --git a/uv_magic_uv/impl/copy_paste_uv_impl.py b/uv_magic_uv/impl/copy_paste_uv_impl.py
new file mode 100644
index 00000000..ed44637b
--- /dev/null
+++ b/uv_magic_uv/impl/copy_paste_uv_impl.py
@@ -0,0 +1,271 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+import bmesh
+
+from .. import common
+
+
+__all__ = [
+ 'is_valid_context',
+ 'get_copy_uv_layers',
+ 'get_paste_uv_layers',
+ 'get_src_face_info',
+ 'get_dest_face_info',
+ 'get_select_history_src_face_info',
+ 'get_select_history_dest_face_info',
+ 'paste_uv',
+]
+
+
+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_map):
+ uv_layers = []
+ if 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 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[uv_map])
+ ops_obj.report(
+ {'INFO'}, "Copy UV coordinate (UV map:{})".format(uv_map))
+
+ return uv_layers
+
+
+def get_paste_uv_layers(ops_obj, obj, bm, src_info, uv_map):
+ uv_layers = []
+ if 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 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 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[uv_map])
+ ops_obj.report(
+ {'INFO'}, "Paste UV coordinate (UV map:{})".format(uv_map))
+
+ return uv_layers
+
+
+def get_src_face_info(ops_obj, bm, uv_layers, only_select=False):
+ src_info = {}
+ for layer in uv_layers:
+ face_info = []
+ for face in bm.faces:
+ if not only_select or face.select:
+ info = {
+ "index": face.index,
+ "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:
+ ops_obj.report({'WARNING'}, "No faces are selected")
+ return None
+ src_info[layer.name] = face_info
+
+ return src_info
+
+
+def get_dest_face_info(ops_obj, bm, uv_layers, src_info, strategy,
+ only_select=False):
+ dest_info = {}
+ for layer in uv_layers:
+ face_info = []
+ for face in bm.faces:
+ if not only_select or face.select:
+ info = {
+ "index": face.index,
+ "uvs": [l[layer].uv.copy() for l in face.loops],
+ }
+ face_info.append(info)
+ if not face_info:
+ ops_obj.report({'WARNING'}, "No faces are selected")
+ return None
+ key = list(src_info.keys())[0]
+ src_face_count = len(src_info[key])
+ dest_face_count = len(face_info)
+ if strategy == 'N_N' and src_face_count != dest_face_count:
+ ops_obj.report(
+ {'WARNING'},
+ "Number of selected faces is different from copied" +
+ "(src:{}, dest:{})"
+ .format(src_face_count, dest_face_count))
+ return None
+ dest_info[layer.name] = face_info
+
+ return dest_info
+
+
+def get_select_history_src_face_info(ops_obj, bm, uv_layers):
+ 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 = {
+ "index": hist.index,
+ "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:
+ ops_obj.report({'WARNING'}, "No faces are selected")
+ return None
+ src_info[layer.name] = face_info
+
+ return src_info
+
+
+def get_select_history_dest_face_info(ops_obj, bm, uv_layers, src_info,
+ strategy):
+ 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 = {
+ "index": hist.index,
+ "uvs": [l[layer].uv.copy() for l in hist.loops],
+ }
+ face_info.append(info)
+ if not face_info:
+ ops_obj.report({'WARNING'}, "No faces are selected")
+ return None
+ key = list(src_info.keys())[0]
+ src_face_count = len(src_info[key])
+ dest_face_count = len(face_info)
+ if strategy == 'N_N' and src_face_count != dest_face_count:
+ ops_obj.report(
+ {'WARNING'},
+ "Number of selected faces is different from copied" +
+ "(src:{}, dest:{})"
+ .format(src_face_count, dest_face_count))
+ return None
+ dest_info[layer.name] = face_info
+
+ return dest_info
+
+
+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[dinfo["index"]].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
diff --git a/uv_magic_uv/impl/copy_paste_uv_uvedit_impl.py b/uv_magic_uv/impl/copy_paste_uv_uvedit_impl.py
new file mode 100644
index 00000000..f14a70d6
--- /dev/null
+++ b/uv_magic_uv/impl/copy_paste_uv_uvedit_impl.py
@@ -0,0 +1,166 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import math
+from math import atan2, sin, cos
+
+import bmesh
+from mathutils import Vector
+
+from .. import common
+
+
+__all__ = [
+ 'is_valid_context',
+ 'CopyUVImpl',
+ 'PasteUVImpl',
+]
+
+
+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
+
+ # '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
+ for space in context.area.spaces:
+ if (space.type == 'IMAGE_EDITOR') or (space.type == 'VIEW_3D'):
+ break
+ else:
+ return False
+
+ return True
+
+
+class CopyUVImpl:
+ @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
+ 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()
+
+ 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'}
+
+
+class PasteUVImpl:
+ @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
+ 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'}
diff --git a/uv_magic_uv/impl/flip_rotate_impl.py b/uv_magic_uv/impl/flip_rotate_impl.py
new file mode 100644
index 00000000..f74bc256
--- /dev/null
+++ b/uv_magic_uv/impl/flip_rotate_impl.py
@@ -0,0 +1,133 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+__all__ = [
+ 'is_valid_context',
+ 'get_uv_layer',
+ 'get_src_face_info',
+]
+
+
+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_uv_layer(ops_obj, bm):
+ # get UV layer
+ if not bm.loops.layers.uv:
+ ops_obj.report({'WARNING'}, "Object must have more than one UV map")
+ return None
+ uv_layer = bm.loops.layers.uv.verify()
+
+ return uv_layer
+
+
+def get_src_face_info(ops_obj, bm, uv_layers, only_select=False):
+ src_info = {}
+ for layer in uv_layers:
+ face_info = []
+ for face in bm.faces:
+ if not only_select or face.select:
+ info = {
+ "index": face.index,
+ "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:
+ ops_obj.report({'WARNING'}, "No faces are selected")
+ return None
+ src_info[layer.name] = face_info
+
+ return src_info
+
+
+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[dinfo["index"]].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
diff --git a/uv_magic_uv/impl/mirror_uv_impl.py b/uv_magic_uv/impl/mirror_uv_impl.py
new file mode 100644
index 00000000..e79fbc2c
--- /dev/null
+++ b/uv_magic_uv/impl/mirror_uv_impl.py
@@ -0,0 +1,158 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bmesh
+from mathutils import Vector
+
+from .. import common
+
+
+__all__ = [
+ 'is_valid_context',
+ 'is_vector_similar',
+ 'mirror_uvs',
+ 'get_face_center',
+ 'MirrorUVImpl',
+]
+
+
+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 is_vector_similar(v1, v2, error):
+ """
+ Check if two vectors are similar, within an error threshold
+ """
+ within_err_x = abs(v2.x - v1.x) < error
+ within_err_y = abs(v2.y - v1.y) < error
+ within_err_z = abs(v2.z - v1.z) < error
+
+ return within_err_x and within_err_y and within_err_z
+
+
+def mirror_uvs(uv_layer, src, dst, axis, error):
+ """
+ Copy UV coordinates from one UV face to another
+ """
+ for sl in src.loops:
+ suv = sl[uv_layer].uv.copy()
+ svco = sl.vert.co.copy()
+ for dl in dst.loops:
+ dvco = dl.vert.co.copy()
+ if axis == 'X':
+ dvco.x = -dvco.x
+ elif axis == 'Y':
+ dvco.y = -dvco.y
+ elif axis == 'Z':
+ dvco.z = -dvco.z
+
+ if is_vector_similar(svco, dvco, error):
+ dl[uv_layer].uv = suv.copy()
+
+
+def get_face_center(face):
+ """
+ Get center coordinate of the face
+ """
+ center = Vector((0.0, 0.0, 0.0))
+ for v in face.verts:
+ center = center + v.co
+
+ return center / len(face.verts)
+
+
+class MirrorUVImpl:
+ @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, ops_obj, context):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+
+ error = ops_obj.error
+ axis = ops_obj.axis
+
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+ if not bm.loops.layers.uv:
+ ops_obj.report({'WARNING'},
+ "Object must have more than one UV map")
+ return {'CANCELLED'}
+ uv_layer = bm.loops.layers.uv.verify()
+
+ faces = [f for f in bm.faces if f.select]
+ for f_dst in faces:
+ count = len(f_dst.verts)
+ for f_src in bm.faces:
+ # check if this is a candidate to do mirror UV
+ if f_src.index == f_dst.index:
+ continue
+ if count != len(f_src.verts):
+ continue
+
+ # test if the vertices x values are the same sign
+ dst = get_face_center(f_dst)
+ src = get_face_center(f_src)
+ if (dst.x > 0 and src.x > 0) or (dst.x < 0 and src.x < 0):
+ continue
+
+ # invert source axis
+ if axis == 'X':
+ src.x = -src.x
+ elif axis == 'Y':
+ src.y = -src.z
+ elif axis == 'Z':
+ src.z = -src.z
+
+ # do mirror UV
+ if is_vector_similar(dst, src, error):
+ mirror_uvs(
+ uv_layer, f_src, f_dst, ops_obj.axis, ops_obj.error)
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/impl/move_uv_impl.py b/uv_magic_uv/impl/move_uv_impl.py
new file mode 100644
index 00000000..ce507fba
--- /dev/null
+++ b/uv_magic_uv/impl/move_uv_impl.py
@@ -0,0 +1,166 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bmesh
+from mathutils import Vector
+
+from .. import common
+
+
+__all__ = [
+ 'is_valid_context',
+ 'find_uv',
+ 'MoveUVImpl',
+]
+
+
+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 find_uv(context):
+ bm = bmesh.from_edit_mesh(context.object.data)
+ topology_dict = []
+ uvs = []
+ active_uv = bm.loops.layers.uv.active
+ for fidx, f in enumerate(bm.faces):
+ for vidx, v in enumerate(f.verts):
+ if v.select:
+ uvs.append(f.loops[vidx][active_uv].uv.copy())
+ topology_dict.append([fidx, vidx])
+
+ return topology_dict, uvs
+
+
+class MoveUVImpl():
+ __running = False
+
+ def __init__(self):
+ self.__topology_dict = []
+ self.__prev_mouse = Vector((0.0, 0.0))
+ self.__offset_uv = Vector((0.0, 0.0))
+ self.__prev_offset_uv = Vector((0.0, 0.0))
+ self.__first_time = True
+ self.__ini_uvs = []
+ self.__operating = False
+
+ @classmethod
+ def poll(cls, context):
+ # we can not get area/space/region from console
+ if common.is_console_mode():
+ return False
+ if cls.is_running(context):
+ return False
+ return is_valid_context(context)
+
+ @classmethod
+ def is_running(cls, _):
+ return cls.__running
+
+ def modal(self, _, context, event):
+ if self.__first_time is True:
+ self.__prev_mouse = Vector((
+ event.mouse_region_x, event.mouse_region_y))
+ self.__first_time = False
+ return {'RUNNING_MODAL'}
+
+ # move UV
+ div = 10000
+ self.__offset_uv += Vector((
+ (event.mouse_region_x - self.__prev_mouse.x) / div,
+ (event.mouse_region_y - self.__prev_mouse.y) / div))
+ ouv = self.__offset_uv
+ pouv = self.__prev_offset_uv
+ vec = Vector((ouv.x - ouv.y, ouv.x + ouv.y))
+ dv = vec - pouv
+ self.__prev_offset_uv = vec
+ self.__prev_mouse = Vector((
+ event.mouse_region_x, event.mouse_region_y))
+
+ # check if operation is started
+ if not self.__operating:
+ if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
+ self.__operating = True
+ return {'RUNNING_MODAL'}
+
+ # update UV
+ obj = context.object
+ bm = bmesh.from_edit_mesh(obj.data)
+ active_uv = bm.loops.layers.uv.active
+ for fidx, vidx in self.__topology_dict:
+ l = bm.faces[fidx].loops[vidx]
+ l[active_uv].uv = l[active_uv].uv + dv
+ bmesh.update_edit_mesh(obj.data)
+
+ # check mouse preference
+ if context.user_preferences.inputs.select_mouse == 'RIGHT':
+ confirm_btn = 'LEFTMOUSE'
+ cancel_btn = 'RIGHTMOUSE'
+ else:
+ confirm_btn = 'RIGHTMOUSE'
+ cancel_btn = 'LEFTMOUSE'
+
+ # cancelled
+ if event.type == cancel_btn and event.value == 'PRESS':
+ for (fidx, vidx), uv in zip(self.__topology_dict, self.__ini_uvs):
+ bm.faces[fidx].loops[vidx][active_uv].uv = uv
+ MoveUVImpl.__running = False
+ return {'FINISHED'}
+ # confirmed
+ if event.type == confirm_btn and event.value == 'PRESS':
+ MoveUVImpl.__running = False
+ return {'FINISHED'}
+
+ return {'RUNNING_MODAL'}
+
+ def execute(self, ops_obj, context):
+ MoveUVImpl.__running = True
+ self.__operating = False
+ self.__first_time = True
+
+ context.window_manager.modal_handler_add(ops_obj)
+ self.__topology_dict, self.__ini_uvs = find_uv(context)
+
+ if context.area:
+ context.area.tag_redraw()
+
+ return {'RUNNING_MODAL'}
diff --git a/uv_magic_uv/impl/transfer_uv_impl.py b/uv_magic_uv/impl/transfer_uv_impl.py
new file mode 100644
index 00000000..adc97352
--- /dev/null
+++ b/uv_magic_uv/impl/transfer_uv_impl.py
@@ -0,0 +1,330 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+from collections import OrderedDict
+
+import bpy
+import bmesh
+
+from .. import common
+
+
+__all__ = [
+ 'is_valid_context',
+ 'get_uv_layer',
+ 'main_parse',
+ 'parse_faces',
+ 'get_new_shared_faces',
+ 'get_other_verts_edges',
+ 'get_selected_src_faces',
+ 'paste_uv',
+]
+
+
+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_uv_layer(ops_obj, bm):
+ # get UV layer
+ if not bm.loops.layers.uv:
+ ops_obj.report({'WARNING'}, "Object must have more than one UV map")
+ return None
+ uv_layer = bm.loops.layers.uv.verify()
+
+ return uv_layer
+
+
+def main_parse(ops_obj, uv_layer, sel_faces, active_face, active_face_nor):
+ all_sorted_faces = OrderedDict() # This is the main stuff
+
+ used_verts = set()
+ used_edges = set()
+
+ faces_to_parse = []
+
+ # get shared edge of two faces
+ cross_edges = []
+ for edge in active_face.edges:
+ if edge in sel_faces[0].edges and edge in sel_faces[1].edges:
+ cross_edges.append(edge)
+
+ # parse two selected faces
+ if cross_edges and len(cross_edges) == 1:
+ shared_edge = cross_edges[0]
+ vert1 = None
+ vert2 = None
+
+ dot_n = active_face_nor.normalized()
+ edge_vec_1 = (shared_edge.verts[1].co - shared_edge.verts[0].co)
+ edge_vec_len = edge_vec_1.length
+ edge_vec_1 = edge_vec_1.normalized()
+
+ af_center = active_face.calc_center_median()
+ af_vec = shared_edge.verts[0].co + (edge_vec_1 * (edge_vec_len * 0.5))
+ af_vec = (af_vec - af_center).normalized()
+
+ if af_vec.cross(edge_vec_1).dot(dot_n) > 0:
+ vert1 = shared_edge.verts[0]
+ vert2 = shared_edge.verts[1]
+ else:
+ vert1 = shared_edge.verts[1]
+ vert2 = shared_edge.verts[0]
+
+ # get active face stuff and uvs
+ face_stuff = get_other_verts_edges(
+ active_face, vert1, vert2, shared_edge, uv_layer)
+ all_sorted_faces[active_face] = face_stuff
+ used_verts.update(active_face.verts)
+ used_edges.update(active_face.edges)
+
+ # get first selected face stuff and uvs as they share shared_edge
+ second_face = sel_faces[0]
+ if second_face is active_face:
+ second_face = sel_faces[1]
+ face_stuff = get_other_verts_edges(
+ second_face, vert1, vert2, shared_edge, uv_layer)
+ all_sorted_faces[second_face] = face_stuff
+ used_verts.update(second_face.verts)
+ used_edges.update(second_face.edges)
+
+ # first Grow
+ faces_to_parse.append(active_face)
+ faces_to_parse.append(second_face)
+
+ else:
+ ops_obj.report({'WARNING'}, "Two faces should share one edge")
+ return None
+
+ # parse all faces
+ while True:
+ new_parsed_faces = []
+ if not faces_to_parse:
+ break
+ for face in faces_to_parse:
+ face_stuff = all_sorted_faces.get(face)
+ new_faces = parse_faces(face, face_stuff, used_verts, used_edges,
+ all_sorted_faces, uv_layer)
+ if new_faces is None:
+ ops_obj.report({'WARNING'}, "More than 2 faces share edge")
+ return None
+
+ new_parsed_faces += new_faces
+ faces_to_parse = new_parsed_faces
+
+ return all_sorted_faces
+
+
+def parse_faces(check_face, face_stuff, used_verts, used_edges,
+ all_sorted_faces, uv_layer):
+ """recurse faces around the new_grow only"""
+
+ new_shared_faces = []
+ for sorted_edge in face_stuff[1]:
+ shared_faces = sorted_edge.link_faces
+ if shared_faces:
+ if len(shared_faces) > 2:
+ bpy.ops.mesh.select_all(action='DESELECT')
+ for face_sel in shared_faces:
+ face_sel.select = True
+ shared_faces = []
+ return None
+
+ clear_shared_faces = get_new_shared_faces(
+ check_face, sorted_edge, shared_faces, all_sorted_faces.keys())
+ if clear_shared_faces:
+ shared_face = clear_shared_faces[0]
+ # get vertices of the edge
+ vert1 = sorted_edge.verts[0]
+ vert2 = sorted_edge.verts[1]
+
+ common.debug_print(face_stuff[0], vert1, vert2)
+ if face_stuff[0].index(vert1) > face_stuff[0].index(vert2):
+ vert1 = sorted_edge.verts[1]
+ vert2 = sorted_edge.verts[0]
+
+ common.debug_print(shared_face.verts, vert1, vert2)
+ new_face_stuff = get_other_verts_edges(
+ shared_face, vert1, vert2, sorted_edge, uv_layer)
+ all_sorted_faces[shared_face] = new_face_stuff
+ used_verts.update(shared_face.verts)
+ used_edges.update(shared_face.edges)
+
+ if common.is_debug_mode():
+ shared_face.select = True # test which faces are parsed
+
+ new_shared_faces.append(shared_face)
+
+ return new_shared_faces
+
+
+def get_new_shared_faces(orig_face, shared_edge, check_faces, used_faces):
+ shared_faces = []
+
+ for face in check_faces:
+ is_shared_edge = shared_edge in face.edges
+ not_used = face not in used_faces
+ not_orig = face is not orig_face
+ not_hide = face.hide is False
+ if is_shared_edge and not_used and not_orig and not_hide:
+ shared_faces.append(face)
+
+ return shared_faces
+
+
+def get_other_verts_edges(face, vert1, vert2, first_edge, uv_layer):
+ face_edges = [first_edge]
+ face_verts = [vert1, vert2]
+ face_loops = []
+
+ other_edges = [edge for edge in face.edges if edge not in face_edges]
+
+ for _ in range(len(other_edges)):
+ found_edge = None
+ # get sorted verts and edges
+ for edge in other_edges:
+ if face_verts[-1] in edge.verts:
+ other_vert = edge.other_vert(face_verts[-1])
+
+ if other_vert not in face_verts:
+ face_verts.append(other_vert)
+
+ found_edge = edge
+ if found_edge not in face_edges:
+ face_edges.append(edge)
+ break
+
+ other_edges.remove(found_edge)
+
+ # get sorted uvs
+ for vert in face_verts:
+ for loop in face.loops:
+ if loop.vert is vert:
+ face_loops.append(loop[uv_layer])
+ break
+
+ return [face_verts, face_edges, face_loops]
+
+
+def get_selected_src_faces(ops_obj, bm, uv_layer):
+ topology_copied = []
+
+ # get selected faces
+ active_face = bm.faces.active
+ sel_faces = [face for face in bm.faces if face.select]
+ if len(sel_faces) != 2:
+ ops_obj.report({'WARNING'}, "Two faces must be selected")
+ return None
+ if not active_face or active_face not in sel_faces:
+ ops_obj.report({'WARNING'}, "Two faces must be active")
+ return None
+
+ # parse all faces according to selection
+ active_face_nor = active_face.normal.copy()
+ all_sorted_faces = main_parse(ops_obj, uv_layer, sel_faces, active_face,
+ active_face_nor)
+
+ if all_sorted_faces:
+ for face_data in all_sorted_faces.values():
+ edges = face_data[1]
+ uv_loops = face_data[2]
+ uvs = [l.uv.copy() for l in uv_loops]
+ pin_uvs = [l.pin_uv for l in uv_loops]
+ seams = [e.seam for e in edges]
+ topology_copied.append([uvs, pin_uvs, seams])
+ else:
+ return None
+
+ return topology_copied
+
+
+def paste_uv(ops_obj, bm, uv_layer, src_faces, invert_normals, copy_seams):
+ # get selection history
+ all_sel_faces = [e for e in bm.select_history
+ if isinstance(e, bmesh.types.BMFace) and e.select]
+ if len(all_sel_faces) % 2 != 0:
+ ops_obj.report({'WARNING'}, "Two faces must be selected")
+ return -1
+
+ # parse selection history
+ for i, _ in enumerate(all_sel_faces):
+ if (i == 0) or (i % 2 == 0):
+ continue
+ sel_faces = [all_sel_faces[i - 1], all_sel_faces[i]]
+ active_face = all_sel_faces[i]
+
+ # parse all faces according to selection history
+ active_face_nor = active_face.normal.copy()
+ if invert_normals:
+ active_face_nor.negate()
+ all_sorted_faces = main_parse(ops_obj, uv_layer, sel_faces,
+ active_face, active_face_nor)
+
+ if all_sorted_faces:
+ # check amount of copied/pasted faces
+ if len(all_sorted_faces) != len(src_faces):
+ ops_obj.report({'WARNING'},
+ "Mesh has different amount of faces")
+ return -1
+
+ for j, face_data in enumerate(all_sorted_faces.values()):
+ copied_data = src_faces[j]
+
+ # check amount of copied/pasted verts
+ if len(copied_data[0]) != len(face_data[2]):
+ bpy.ops.mesh.select_all(action='DESELECT')
+ # select problematic face
+ list(all_sorted_faces.keys())[j].select = True
+ ops_obj.report({'WARNING'},
+ "Face have different amount of vertices")
+ return 0
+
+ for k, (edge, uvloop) in enumerate(zip(face_data[1],
+ face_data[2])):
+ uvloop.uv = copied_data[0][k]
+ uvloop.pin_uv = copied_data[1][k]
+ if copy_seams:
+ edge.seam = copied_data[2][k]
+ else:
+ return -1
+
+ return 0
diff --git a/uv_magic_uv/impl/uvw_impl.py b/uv_magic_uv/impl/uvw_impl.py
new file mode 100644
index 00000000..e815f54f
--- /dev/null
+++ b/uv_magic_uv/impl/uvw_impl.py
@@ -0,0 +1,154 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+from math import sin, cos, pi
+
+from mathutils import Vector
+
+
+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_uv_layer(ops_obj, bm, assign_uvmap):
+ # get UV layer
+ if not bm.loops.layers.uv:
+ if assign_uvmap:
+ bm.loops.layers.uv.new()
+ else:
+ ops_obj.report({'WARNING'},
+ "Object must have more than one UV map")
+ return None
+ uv_layer = bm.loops.layers.uv.verify()
+
+ return uv_layer
+
+
+def apply_box_map(bm, uv_layer, size, offset, rotation, tex_aspect):
+ scale = 1.0 / size
+
+ sx = 1.0 * scale
+ sy = 1.0 * scale
+ sz = 1.0 * scale
+ ofx = offset[0]
+ ofy = offset[1]
+ ofz = offset[2]
+ rx = rotation[0] * pi / 180.0
+ ry = rotation[1] * pi / 180.0
+ rz = rotation[2] * pi / 180.0
+ aspect = tex_aspect
+
+ sel_faces = [f for f in bm.faces if f.select]
+
+ # update UV coordinate
+ for f in sel_faces:
+ n = f.normal
+ for l in f.loops:
+ co = l.vert.co
+ x = co.x * sx
+ y = co.y * sy
+ z = co.z * sz
+
+ # X-plane
+ if abs(n[0]) >= abs(n[1]) and abs(n[0]) >= abs(n[2]):
+ if n[0] >= 0.0:
+ u = (y - ofy) * cos(rx) + (z - ofz) * sin(rx)
+ v = -(y * aspect - ofy) * sin(rx) + \
+ (z * aspect - ofz) * cos(rx)
+ else:
+ u = -(y - ofy) * cos(rx) + (z - ofz) * sin(rx)
+ v = (y * aspect - ofy) * sin(rx) + \
+ (z * aspect - ofz) * cos(rx)
+ # Y-plane
+ elif abs(n[1]) >= abs(n[0]) and abs(n[1]) >= abs(n[2]):
+ if n[1] >= 0.0:
+ u = -(x - ofx) * cos(ry) + (z - ofz) * sin(ry)
+ v = (x * aspect - ofx) * sin(ry) + \
+ (z * aspect - ofz) * cos(ry)
+ else:
+ u = (x - ofx) * cos(ry) + (z - ofz) * sin(ry)
+ v = -(x * aspect - ofx) * sin(ry) + \
+ (z * aspect - ofz) * cos(ry)
+ # Z-plane
+ else:
+ if n[2] >= 0.0:
+ u = (x - ofx) * cos(rz) + (y - ofy) * sin(rz)
+ v = -(x * aspect - ofx) * sin(rz) + \
+ (y * aspect - ofy) * cos(rz)
+ else:
+ u = -(x - ofx) * cos(rz) - (y + ofy) * sin(rz)
+ v = -(x * aspect + ofx) * sin(rz) + \
+ (y * aspect - ofy) * cos(rz)
+
+ l[uv_layer].uv = Vector((u, v))
+
+
+def apply_planer_map(bm, uv_layer, size, offset, rotation, tex_aspect):
+ scale = 1.0 / size
+
+ sx = 1.0 * scale
+ sy = 1.0 * scale
+ ofx = offset[0]
+ ofy = offset[1]
+ rz = rotation * pi / 180.0
+ aspect = tex_aspect
+
+ sel_faces = [f for f in bm.faces if f.select]
+
+ # calculate average of normal
+ n_ave = Vector((0.0, 0.0, 0.0))
+ for f in sel_faces:
+ n_ave = n_ave + f.normal
+ q = n_ave.rotation_difference(Vector((0.0, 0.0, 1.0)))
+
+ # update UV coordinate
+ for f in sel_faces:
+ for l in f.loops:
+ co = q @ l.vert.co
+ x = co.x * sx
+ y = co.y * sy
+
+ u = x * cos(rz) - y * sin(rz) + ofx
+ v = -x * aspect * sin(rz) - y * aspect * cos(rz) + ofy
+
+ l[uv_layer].uv = Vector((u, v))
diff --git a/uv_magic_uv/legacy/__init__.py b/uv_magic_uv/legacy/__init__.py
new file mode 100644
index 00000000..794d02bc
--- /dev/null
+++ b/uv_magic_uv/legacy/__init__.py
@@ -0,0 +1,38 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+if "bpy" in locals():
+ import importlib
+ importlib.reload(op)
+ importlib.reload(ui)
+ importlib.reload(properites)
+ importlib.reload(preferences)
+else:
+ from . import op
+ from . import ui
+ from . import properites
+ from . import preferences
+
+import bpy
diff --git a/uv_magic_uv/legacy/op/__init__.py b/uv_magic_uv/legacy/op/__init__.py
new file mode 100644
index 00000000..9535b76d
--- /dev/null
+++ b/uv_magic_uv/legacy/op/__init__.py
@@ -0,0 +1,74 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+if "bpy" in locals():
+ import importlib
+ importlib.reload(align_uv)
+ importlib.reload(align_uv_cursor)
+ importlib.reload(copy_paste_uv)
+ importlib.reload(copy_paste_uv_object)
+ importlib.reload(copy_paste_uv_uvedit)
+ importlib.reload(flip_rotate_uv)
+ importlib.reload(mirror_uv)
+ importlib.reload(move_uv)
+ importlib.reload(pack_uv)
+ importlib.reload(preserve_uv_aspect)
+ importlib.reload(select_uv)
+ importlib.reload(smooth_uv)
+ importlib.reload(texture_lock)
+ importlib.reload(texture_projection)
+ importlib.reload(texture_wrap)
+ importlib.reload(transfer_uv)
+ importlib.reload(unwrap_constraint)
+ importlib.reload(uv_bounding_box)
+ importlib.reload(uv_inspection)
+ importlib.reload(uv_sculpt)
+ importlib.reload(uvw)
+ importlib.reload(world_scale_uv)
+else:
+ from . import align_uv
+ from . import align_uv_cursor
+ from . import copy_paste_uv
+ from . import copy_paste_uv_object
+ from . import copy_paste_uv_uvedit
+ from . import flip_rotate_uv
+ from . import mirror_uv
+ from . import move_uv
+ from . import pack_uv
+ from . import preserve_uv_aspect
+ from . import select_uv
+ from . import smooth_uv
+ from . import texture_lock
+ from . import texture_projection
+ from . import texture_wrap
+ from . import transfer_uv
+ from . import unwrap_constraint
+ from . import uv_bounding_box
+ from . import uv_inspection
+ from . import uv_sculpt
+ from . import uvw
+ from . import world_scale_uv
+
+import bpy
diff --git a/uv_magic_uv/op/align_uv.py b/uv_magic_uv/legacy/op/align_uv.py
index 90168a56..9d0ff5f4 100644
--- a/uv_magic_uv/op/align_uv.py
+++ b/uv_magic_uv/legacy/op/align_uv.py
@@ -31,14 +31,16 @@ import bmesh
from mathutils import Vector
from bpy.props import EnumProperty, BoolProperty, FloatProperty
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'OperatorCircle',
- 'OperatorStraighten',
- 'OperatorAxis',
+ 'MUV_OT_AlignUV_Circle',
+ 'MUV_OT_AlignUV_Straighten',
+ 'MUV_OT_AlignUV_Axis',
]
@@ -65,59 +67,10 @@ def is_valid_context(context):
return True
-# get sum vertex length of loop sequences
-def get_loop_vert_len(loops):
- length = 0
- for l1, l2 in zip(loops[:-1], loops[1:]):
- diff = l2.vert.co - l1.vert.co
- length = length + abs(diff.length)
-
- return length
-
-
-# get sum uv length of loop sequences
-def get_loop_uv_len(loops, uv_layer):
- length = 0
- for l1, l2 in zip(loops[:-1], loops[1:]):
- diff = l2[uv_layer].uv - l1[uv_layer].uv
- length = length + abs(diff.length)
-
- return length
-
-
-# get center/radius of circle by 3 vertices
-def get_circle(v):
- alpha = atan2((v[0].y - v[1].y), (v[0].x - v[1].x)) + math.pi / 2
- beta = atan2((v[1].y - v[2].y), (v[1].x - v[2].x)) + math.pi / 2
- ex = (v[0].x + v[1].x) / 2.0
- ey = (v[0].y + v[1].y) / 2.0
- fx = (v[1].x + v[2].x) / 2.0
- fy = (v[1].y + v[2].y) / 2.0
- cx = (ey - fy - ex * tan(alpha) + fx * tan(beta)) / \
- (tan(beta) - tan(alpha))
- cy = ey - (ex - cx) * tan(alpha)
- center = Vector((cx, cy))
-
- r = v[0] - center
- radian = r.length
-
- return center, radian
-
-
-# get position on circle with same arc length
-def calc_v_on_circle(v, center, radius):
- base = v[0]
- theta = atan2(base.y - center.y, base.x - center.x)
- new_v = []
- for i in range(len(v)):
- angle = theta + i * 2 * math.pi / len(v)
- new_v.append(Vector((center.x + radius * sin(angle),
- center.y + radius * cos(angle))))
-
- return new_v
-
-
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "align_uv"
+
@classmethod
def init_props(cls, scene):
scene.muv_align_uv_enabled = BoolProperty(
@@ -176,7 +129,60 @@ class Properties:
del scene.muv_align_uv_location
-class OperatorCircle(bpy.types.Operator):
+# get sum vertex length of loop sequences
+def get_loop_vert_len(loops):
+ length = 0
+ for l1, l2 in zip(loops[:-1], loops[1:]):
+ diff = l2.vert.co - l1.vert.co
+ length = length + abs(diff.length)
+
+ return length
+
+
+# get sum uv length of loop sequences
+def get_loop_uv_len(loops, uv_layer):
+ length = 0
+ for l1, l2 in zip(loops[:-1], loops[1:]):
+ diff = l2[uv_layer].uv - l1[uv_layer].uv
+ length = length + abs(diff.length)
+
+ return length
+
+
+# get center/radius of circle by 3 vertices
+def get_circle(v):
+ alpha = atan2((v[0].y - v[1].y), (v[0].x - v[1].x)) + math.pi / 2
+ beta = atan2((v[1].y - v[2].y), (v[1].x - v[2].x)) + math.pi / 2
+ ex = (v[0].x + v[1].x) / 2.0
+ ey = (v[0].y + v[1].y) / 2.0
+ fx = (v[1].x + v[2].x) / 2.0
+ fy = (v[1].y + v[2].y) / 2.0
+ cx = (ey - fy - ex * tan(alpha) + fx * tan(beta)) / \
+ (tan(beta) - tan(alpha))
+ cy = ey - (ex - cx) * tan(alpha)
+ center = Vector((cx, cy))
+
+ r = v[0] - center
+ radian = r.length
+
+ return center, radian
+
+
+# get position on circle with same arc length
+def calc_v_on_circle(v, center, radius):
+ base = v[0]
+ theta = atan2(base.y - center.y, base.x - center.x)
+ new_v = []
+ for i in range(len(v)):
+ angle = theta + i * 2 * math.pi / len(v)
+ new_v.append(Vector((center.x + radius * sin(angle),
+ center.y + radius * cos(angle))))
+
+ return new_v
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_AlignUV_Circle(bpy.types.Operator):
bl_idname = "uv.muv_align_uv_operator_circle"
bl_label = "Align UV (Circle)"
@@ -437,7 +443,8 @@ def get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx):
return int((vidx + 1) / 2) * v_uv / (len(hseq) / 2)
-class OperatorStraighten(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_AlignUV_Straighten(bpy.types.Operator):
bl_idname = "uv.muv_align_uv_operator_straighten"
bl_label = "Align UV (Straighten)"
@@ -587,7 +594,8 @@ class OperatorStraighten(bpy.types.Operator):
return {'FINISHED'}
-class OperatorAxis(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_AlignUV_Axis(bpy.types.Operator):
bl_idname = "uv.muv_align_uv_operator_axis"
bl_label = "Align UV (XY-Axis)"
diff --git a/uv_magic_uv/op/align_uv_cursor.py b/uv_magic_uv/legacy/op/align_uv_cursor.py
index d787bde9..ec3e7036 100644
--- a/uv_magic_uv/op/align_uv_cursor.py
+++ b/uv_magic_uv/legacy/op/align_uv_cursor.py
@@ -28,12 +28,14 @@ from mathutils import Vector
from bpy.props import EnumProperty, BoolProperty, FloatVectorProperty
import bmesh
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_AlignUVCursor',
]
@@ -50,7 +52,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "align_uv_cursor"
+
@classmethod
def init_props(cls, scene):
def auvc_get_cursor_loc(self):
@@ -121,7 +126,8 @@ class Properties:
del scene.muv_uv_cursor_location_enabled
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_AlignUVCursor(bpy.types.Operator):
bl_idname = "uv.muv_align_uv_cursor_operator"
bl_label = "Align UV Cursor"
diff --git a/uv_magic_uv/legacy/op/copy_paste_uv.py b/uv_magic_uv/legacy/op/copy_paste_uv.py
new file mode 100644
index 00000000..a8aef017
--- /dev/null
+++ b/uv_magic_uv/legacy/op/copy_paste_uv.py
@@ -0,0 +1,531 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+import bmesh
+import bpy.utils
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+ IntProperty,
+ EnumProperty,
+)
+
+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(legacy=True)
+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
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUV_CopyUV(bpy.types.Operator):
+ """
+ Operation class: Copy UV coordinate
+ """
+
+ 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(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.copy_paste_uv
+ obj = context.active_object
+ bm = common.create_bmesh(obj)
+
+ # get UV layer
+ uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
+ if not uv_layers:
+ return {'CANCELLED'}
+
+ # get selected face
+ src_info = impl.get_src_face_info(self, bm, uv_layers, True)
+ if src_info is None:
+ return {'CANCELLED'}
+ 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'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_CopyUV(bpy.types.Menu):
+ """
+ Menu class: 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 = common.create_bmesh(obj)
+ uv_maps = bm.loops.layers.uv.keys()
+
+ 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:
+ ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname, text=m)
+ ops.uv_map = m
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator):
+ """
+ Operation class: Paste UV coordinate
+ """
+
+ 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(default="__default", options={'HIDDEN'})
+ 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"
+ )
+ flip_copied_uv = BoolProperty(
+ name="Flip Copied UV",
+ description="Flip Copied UV...",
+ default=False
+ )
+ rotate_copied_uv = IntProperty(
+ default=0,
+ name="Rotate Copied UV",
+ min=0,
+ max=30
+ )
+ 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.copy_paste_uv
+ if not props.src_info:
+ self.report({'WARNING'}, "Need copy UV at first")
+ return {'CANCELLED'}
+ obj = context.active_object
+ bm = common.create_bmesh(obj)
+
+ # get UV layer
+ 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_info = impl.get_dest_face_info(self, bm, uv_layers,
+ props.src_info, self.strategy,
+ True)
+ if dest_info is None:
+ return {'CANCELLED'}
+
+ # paste
+ 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'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_PasteUV(bpy.types.Menu):
+ """
+ Menu class: 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 = common.create_bmesh(obj)
+ uv_maps = bm.loops.layers.uv.keys()
+
+ 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_OT_CopyPasteUV_PasteUV.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
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUV_SelSeqCopyUV(bpy.types.Operator):
+ """
+ Operation class: Copy UV coordinate by selection sequence
+ """
+
+ 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):
+ # 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.copy_paste_uv_selseq
+ obj = context.active_object
+ bm = common.create_bmesh(obj)
+
+ # get UV layer
+ uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
+ if not uv_layers:
+ return {'CANCELLED'}
+
+ # get selected face
+ src_info = impl.get_select_history_src_face_info(self, bm, uv_layers)
+ if src_info is None:
+ return {'CANCELLED'}
+ 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'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_SelSeqCopyUV(bpy.types.Menu):
+ """
+ Menu class: 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 = common.create_bmesh(obj)
+ uv_maps = bm.loops.layers.uv.keys()
+
+ 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:
+ ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+ text=m)
+ ops.uv_map = m
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator):
+ """
+ Operation class: Paste UV coordinate by selection sequence
+ """
+
+ 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(default="__default", options={'HIDDEN'})
+ 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"
+ )
+ flip_copied_uv = BoolProperty(
+ name="Flip Copied UV",
+ description="Flip Copied UV...",
+ default=False
+ )
+ rotate_copied_uv = IntProperty(
+ default=0,
+ name="Rotate Copied UV",
+ min=0,
+ max=30
+ )
+ 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.copy_paste_uv_selseq
+ if not props.src_info:
+ self.report({'WARNING'}, "Need copy UV at first")
+ return {'CANCELLED'}
+ obj = context.active_object
+ bm = common.create_bmesh(obj)
+
+ # get UV layer
+ 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_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
+ 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'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_SelSeqPasteUV(bpy.types.Menu):
+ """
+ Menu class: 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 = common.create_bmesh(obj)
+ uv_maps = bm.loops.layers.uv.keys()
+
+ ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.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_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_OT_CopyPasteUV_SelSeqPasteUV.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
diff --git a/uv_magic_uv/legacy/op/copy_paste_uv_object.py b/uv_magic_uv/legacy/op/copy_paste_uv_object.py
new file mode 100644
index 00000000..e09b003b
--- /dev/null
+++ b/uv_magic_uv/legacy/op/copy_paste_uv_object.py
@@ -0,0 +1,298 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bmesh
+import bpy
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+)
+
+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_CopyPasteUVObject_CopyUV',
+ 'MUV_MT_CopyPasteUVObject_CopyUV',
+ 'MUV_OT_CopyPasteUVObject_PasteUV',
+ 'MUV_MT_CopyPasteUVObject_PasteUV',
+]
+
+
+def is_valid_context(context):
+ obj = context.object
+
+ # only object mode is allowed to execute
+ if obj is None:
+ return False
+ if obj.type != 'MESH':
+ return False
+ if context.object.mode != 'OBJECT':
+ 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
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+ idname = "copy_paste_uv_object"
+
+ @classmethod
+ def init_props(cls, scene):
+ class Props():
+ src_info = None
+
+ scene.muv_props.copy_paste_uv_object = Props()
+
+ scene.muv_copy_paste_uv_object_copy_seams = BoolProperty(
+ name="Seams",
+ description="Copy Seams",
+ default=True
+ )
+
+ @classmethod
+ def del_props(cls, scene):
+ del scene.muv_props.copy_paste_uv_object
+ del scene.muv_copy_paste_uv_object_copy_seams
+
+
+def memorize_view_3d_mode(fn):
+ def __memorize_view_3d_mode(self, context):
+ mode_orig = bpy.context.object.mode
+ result = fn(self, context)
+ bpy.ops.object.mode_set(mode=mode_orig)
+ return result
+ return __memorize_view_3d_mode
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUVObject_CopyUV(bpy.types.Operator):
+ """
+ Operation class: Copy UV coordinate among objects
+ """
+
+ bl_idname = "object.muv_copy_paste_uv_object_operator_copy_uv"
+ bl_label = "Copy UV (Among Objects)"
+ bl_description = "Copy UV coordinate (Among Objects)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ 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)
+
+ @memorize_view_3d_mode
+ def execute(self, context):
+ props = context.scene.muv_props.copy_paste_uv_object
+ bpy.ops.object.mode_set(mode='EDIT')
+ obj = context.active_object
+ bm = common.create_bmesh(obj)
+
+ # get UV layer
+ uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
+ if not uv_layers:
+ return {'CANCELLED'}
+
+ # get selected face
+ src_info = impl.get_src_face_info(self, bm, uv_layers)
+ if src_info is None:
+ return {'CANCELLED'}
+ props.src_info = src_info
+
+ self.report({'INFO'},
+ "{}'s UV coordinates are copied".format(obj.name))
+
+ return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUVObject_CopyUV(bpy.types.Menu):
+ """
+ Menu class: Copy UV coordinate among objects
+ """
+
+ bl_idname = "object.muv_copy_paste_uv_object_menu_copy_uv"
+ bl_label = "Copy UV (Among Objects) (Menu)"
+ bl_description = "Menu of Copy UV coordinate (Among Objects)"
+
+ @classmethod
+ def poll(cls, context):
+ return is_valid_context(context)
+
+ def draw(self, _):
+ layout = self.layout
+ # create sub menu
+ uv_maps = bpy.context.active_object.data.uv_textures.keys()
+
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+ text="[Default]")
+ ops.uv_map = "__default"
+
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+ text="[All]")
+ ops.uv_map = "__all"
+
+ for m in uv_maps:
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+ text=m)
+ ops.uv_map = m
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUVObject_PasteUV(bpy.types.Operator):
+ """
+ Operation class: Paste UV coordinate among objects
+ """
+
+ bl_idname = "object.muv_copy_paste_uv_object_operator_paste_uv"
+ bl_label = "Paste UV (Among Objects)"
+ bl_description = "Paste UV coordinate (Among Objects)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ uv_map = StringProperty(default="__default", options={'HIDDEN'})
+ 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_object
+ if not props.src_info:
+ return False
+ return is_valid_context(context)
+
+ @memorize_view_3d_mode
+ def execute(self, context):
+ props = context.scene.muv_props.copy_paste_uv_object
+ if not props.src_info:
+ self.report({'WARNING'}, "Need copy UV at first")
+ return {'CANCELLED'}
+
+ for o in bpy.data.objects:
+ if not hasattr(o.data, "uv_textures") or not o.select:
+ continue
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ bpy.context.scene.objects.active = o
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ obj = context.active_object
+ bm = common.create_bmesh(obj)
+
+ # get UV layer
+ 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_info = impl.get_dest_face_info(self, bm, uv_layers,
+ props.src_info, 'N_N')
+ if dest_info is None:
+ return {'CANCELLED'}
+
+ # paste
+ ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers,
+ 'N_N', 0, 0, self.copy_seams)
+ if ret:
+ return {'CANCELLED'}
+
+ bmesh.update_edit_mesh(obj.data)
+ if self.copy_seams is True:
+ obj.data.show_edge_seams = True
+
+ self.report(
+ {'INFO'}, "{}'s UV coordinates are pasted".format(obj.name))
+
+ return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUVObject_PasteUV(bpy.types.Menu):
+ """
+ Menu class: Paste UV coordinate among objects
+ """
+
+ bl_idname = "object.muv_copy_paste_uv_object_menu_paste_uv"
+ bl_label = "Paste UV (Among Objects) (Menu)"
+ bl_description = "Menu of Paste UV coordinate (Among Objects)"
+
+ @classmethod
+ def poll(cls, context):
+ sc = context.scene
+ props = sc.muv_props.copy_paste_uv_object
+ 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
+ uv_maps = []
+ for obj in bpy.data.objects:
+ if hasattr(obj.data, "uv_textures") and obj.select:
+ uv_maps.extend(obj.data.uv_textures.keys())
+
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="[Default]")
+ ops.uv_map = "__default"
+ ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
+
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="[New]")
+ ops.uv_map = "__new"
+ ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
+
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="[All]")
+ ops.uv_map = "__all"
+ ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
+
+ for m in uv_maps:
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+ text=m)
+ ops.uv_map = m
+ ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
diff --git a/uv_magic_uv/legacy/op/copy_paste_uv_uvedit.py b/uv_magic_uv/legacy/op/copy_paste_uv_uvedit.py
new file mode 100644
index 00000000..bb72d42a
--- /dev/null
+++ b/uv_magic_uv/legacy/op/copy_paste_uv_uvedit.py
@@ -0,0 +1,97 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+from ...impl import copy_paste_uv_uvedit_impl as impl
+
+
+__all__ = [
+ 'Properties',
+ 'MUV_OT_CopyPasteUVUVEdit_CopyUV',
+ 'MUV_OT_CopyPasteUVUVEdit_PasteUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+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(legacy=True)
+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_operator_copy_uv"
+ bl_label = "Copy UV (UV/Image Editor)"
+ bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def __init__(self):
+ self.__impl = impl.CopyUVImpl()
+
+ @classmethod
+ def poll(cls, context):
+ return impl.CopyUVImpl.poll(context)
+
+ def execute(self, context):
+ return self.__impl.execute(self, context)
+
+
+@BlClassRegistry(legacy=True)
+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_operator_paste_uv"
+ bl_label = "Paste UV (UV/Image Editor)"
+ bl_description = "Paste UV coordinate (only selected in UV/Image Editor)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def __init__(self):
+ self.__impl = impl.PasteUVImpl()
+
+ @classmethod
+ def poll(cls, context):
+ return impl.PasteUVImpl.poll(context)
+
+ def execute(self, context):
+ return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/legacy/op/flip_rotate_uv.py b/uv_magic_uv/legacy/op/flip_rotate_uv.py
new file mode 100644
index 00000000..d94e4808
--- /dev/null
+++ b/uv_magic_uv/legacy/op/flip_rotate_uv.py
@@ -0,0 +1,132 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+import bmesh
+from bpy.props import (
+ BoolProperty,
+ IntProperty,
+)
+
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+from ...impl import flip_rotate_impl as impl
+
+__all__ = [
+ 'Properties',
+ 'MUV_OT_FlipRotate',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+ idname = "flip_rotate_uv"
+
+ @classmethod
+ def init_props(cls, scene):
+ scene.muv_flip_rotate_uv_enabled = BoolProperty(
+ name="Flip/Rotate UV Enabled",
+ description="Flip/Rotate UV is enabled",
+ default=False
+ )
+ scene.muv_flip_rotate_uv_seams = BoolProperty(
+ name="Seams",
+ description="Seams",
+ default=True
+ )
+
+ @classmethod
+ def del_props(cls, scene):
+ del scene.muv_flip_rotate_uv_enabled
+ del scene.muv_flip_rotate_uv_seams
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_FlipRotate(bpy.types.Operator):
+ """
+ Operation class: Flip and Rotate UV coordinate
+ """
+
+ bl_idname = "uv.muv_flip_rotate_uv_operator"
+ bl_label = "Flip/Rotate UV"
+ bl_description = "Flip/Rotate UV coordinate"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ flip = BoolProperty(
+ name="Flip UV",
+ description="Flip UV...",
+ default=False
+ )
+ rotate = IntProperty(
+ default=0,
+ name="Rotate UV",
+ min=0,
+ max=30
+ )
+ seams = BoolProperty(
+ name="Seams",
+ description="Seams",
+ default=True
+ )
+
+ @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):
+ self.report({'INFO'}, "Flip/Rotate 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()
+
+ # get UV layer
+ uv_layer = impl.get_uv_layer(self, bm)
+ if not uv_layer:
+ return {'CANCELLED'}
+
+ # get selected face
+ src_info = impl.get_src_face_info(self, bm, [uv_layer], True)
+ if not src_info:
+ return {'CANCELLED'}
+
+ face_count = len(src_info[list(src_info.keys())[0]])
+ self.report({'INFO'}, "{} face(s) are selected".format(face_count))
+
+ # paste
+ ret = impl.paste_uv(self, bm, src_info, src_info, [uv_layer], 'N_N',
+ self.flip, self.rotate, self.seams)
+ if ret:
+ return {'CANCELLED'}
+
+ bmesh.update_edit_mesh(obj.data)
+ if self.seams is True:
+ obj.data.show_edge_seams = True
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/legacy/op/mirror_uv.py b/uv_magic_uv/legacy/op/mirror_uv.py
new file mode 100644
index 00000000..e869e5e8
--- /dev/null
+++ b/uv_magic_uv/legacy/op/mirror_uv.py
@@ -0,0 +1,110 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+from bpy.props import (
+ EnumProperty,
+ FloatProperty,
+ BoolProperty,
+)
+
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+from ...impl import mirror_uv_impl as impl
+
+
+__all__ = [
+ 'Properties',
+ 'MUV_OT_MirrorUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+ idname = "mirror_uv"
+
+ @classmethod
+ def init_props(cls, scene):
+ scene.muv_mirror_uv_enabled = BoolProperty(
+ name="Mirror UV Enabled",
+ description="Mirror UV is enabled",
+ default=False
+ )
+ scene.muv_mirror_uv_axis = EnumProperty(
+ items=[
+ ('X', "X", "Mirror Along X axis"),
+ ('Y', "Y", "Mirror Along Y axis"),
+ ('Z', "Z", "Mirror Along Z axis")
+ ],
+ name="Axis",
+ description="Mirror Axis",
+ default='X'
+ )
+
+ @classmethod
+ def del_props(cls, scene):
+ del scene.muv_mirror_uv_enabled
+ del scene.muv_mirror_uv_axis
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_MirrorUV(bpy.types.Operator):
+ """
+ Operation class: Mirror UV
+ """
+
+ bl_idname = "uv.muv_mirror_uv_operator"
+ bl_label = "Mirror UV"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ axis = EnumProperty(
+ items=(
+ ('X', "X", "Mirror Along X axis"),
+ ('Y', "Y", "Mirror Along Y axis"),
+ ('Z', "Z", "Mirror Along Z axis")
+ ),
+ name="Axis",
+ description="Mirror Axis",
+ default='X'
+ )
+ error = FloatProperty(
+ name="Error",
+ description="Error threshold",
+ default=0.001,
+ min=0.0,
+ max=100.0,
+ soft_min=0.0,
+ soft_max=1.0
+ )
+
+ def __init__(self):
+ self.__impl = impl.MirrorUVImpl()
+
+ @classmethod
+ def poll(cls, context):
+ return impl.MirrorUVImpl.poll(context)
+
+ def execute(self, context):
+ return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/legacy/op/move_uv.py b/uv_magic_uv/legacy/op/move_uv.py
new file mode 100644
index 00000000..2988c2ce
--- /dev/null
+++ b/uv_magic_uv/legacy/op/move_uv.py
@@ -0,0 +1,82 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "kgeogeo, mem, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+from bpy.props import BoolProperty
+
+from ...impl import move_uv_impl as impl
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+
+
+__all__ = [
+ 'Properties',
+ 'MUV_OT_MoveUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+ idname = "move_uv"
+
+ @classmethod
+ def init_props(cls, scene):
+ scene.muv_move_uv_enabled = BoolProperty(
+ name="Move UV Enabled",
+ description="Move UV is enabled",
+ default=False
+ )
+
+ @classmethod
+ def del_props(cls, scene):
+ del scene.muv_move_uv_enabled
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_MoveUV(bpy.types.Operator):
+ """
+ Operator class: Move UV
+ """
+
+ bl_idname = "uv.muv_move_uv_operator"
+ bl_label = "Move UV"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ def __init__(self):
+ self.__impl = impl.MoveUVImpl()
+
+ @classmethod
+ def poll(cls, context):
+ return impl.MoveUVImpl.poll(context)
+
+ @classmethod
+ def is_running(cls, _):
+ return impl.MoveUVImpl.is_running(_)
+
+ def modal(self, context, event):
+ return self.__impl.modal(self, context, event)
+
+ def execute(self, context):
+ return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/op/pack_uv.py b/uv_magic_uv/legacy/op/pack_uv.py
index 39340fda..f8d58843 100644
--- a/uv_magic_uv/op/pack_uv.py
+++ b/uv_magic_uv/legacy/op/pack_uv.py
@@ -35,12 +35,14 @@ from bpy.props import (
)
from mathutils import Vector
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_PackUV',
]
@@ -67,7 +69,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "pack_uv"
+
@classmethod
def init_props(cls, scene):
scene.muv_pack_uv_enabled = BoolProperty(
@@ -99,7 +104,8 @@ class Properties:
del scene.muv_pack_uv_allowable_size_deviation
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_PackUV(bpy.types.Operator):
"""
Operation class: Pack UV with same UV islands are integrated
Island matching algorithm
diff --git a/uv_magic_uv/op/preserve_uv_aspect.py b/uv_magic_uv/legacy/op/preserve_uv_aspect.py
index cb11bd45..cf9349bc 100644
--- a/uv_magic_uv/op/preserve_uv_aspect.py
+++ b/uv_magic_uv/legacy/op/preserve_uv_aspect.py
@@ -28,12 +28,14 @@ import bmesh
from bpy.props import StringProperty, EnumProperty, BoolProperty
from mathutils import Vector
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_PreserveUVAspect',
]
@@ -58,7 +60,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "preserve_uv_aspect"
+
@classmethod
def init_props(cls, scene):
def get_loaded_texture_name(_, __):
@@ -101,7 +106,8 @@ class Properties:
del scene.muv_preserve_uv_aspect_origin
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_PreserveUVAspect(bpy.types.Operator):
"""
Operation class: Preserve UV Aspect
"""
diff --git a/uv_magic_uv/op/select_uv.py b/uv_magic_uv/legacy/op/select_uv.py
index 3a7bcbc3..bdc182d5 100644
--- a/uv_magic_uv/op/select_uv.py
+++ b/uv_magic_uv/legacy/op/select_uv.py
@@ -27,13 +27,15 @@ import bpy
import bmesh
from bpy.props import BoolProperty
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'OperatorSelectFlipped',
- 'OperatorSelectOverlapped',
+ 'MUV_OT_SelectUV_SelectFlipped',
+ 'MUV_OT_SelectUV_SelectOverlapped',
]
@@ -60,7 +62,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "select_uv"
+
@classmethod
def init_props(cls, scene):
scene.muv_select_uv_enabled = BoolProperty(
@@ -74,7 +79,8 @@ class Properties:
del scene.muv_select_uv_enabled
-class OperatorSelectOverlapped(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator):
"""
Operation class: Select faces which have overlapped UVs
"""
@@ -118,7 +124,8 @@ class OperatorSelectOverlapped(bpy.types.Operator):
return {'FINISHED'}
-class OperatorSelectFlipped(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator):
"""
Operation class: Select faces which have flipped UVs
"""
diff --git a/uv_magic_uv/op/smooth_uv.py b/uv_magic_uv/legacy/op/smooth_uv.py
index 31bef155..63062554 100644
--- a/uv_magic_uv/op/smooth_uv.py
+++ b/uv_magic_uv/legacy/op/smooth_uv.py
@@ -27,12 +27,14 @@ import bpy
import bmesh
from bpy.props import BoolProperty, FloatProperty
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_SmoothUV',
]
@@ -59,7 +61,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "smooth_uv"
+
@classmethod
def init_props(cls, scene):
scene.muv_smooth_uv_enabled = BoolProperty(
@@ -93,7 +98,8 @@ class Properties:
del scene.muv_smooth_uv_select
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_SmoothUV(bpy.types.Operator):
bl_idname = "uv.muv_smooth_uv_operator"
bl_label = "Smooth"
diff --git a/uv_magic_uv/op/texture_lock.py b/uv_magic_uv/legacy/op/texture_lock.py
index 4be97c62..65873106 100644
--- a/uv_magic_uv/op/texture_lock.py
+++ b/uv_magic_uv/legacy/op/texture_lock.py
@@ -31,14 +31,16 @@ import bmesh
from mathutils import Vector
from bpy.props import BoolProperty
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'OperatorLock',
- 'OperatorUnlock',
- 'OperatorIntr',
+ 'MUV_OT_TextureLock_Lock',
+ 'MUV_OT_TextureLock_Unlock',
+ 'MUV_OT_TextureLock_Intr',
]
@@ -213,7 +215,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "texture_lock"
+
@classmethod
def init_props(cls, scene):
class Props():
@@ -222,7 +227,7 @@ class Properties:
scene.muv_props.texture_lock = Props()
def get_func(_):
- return OperatorIntr.is_running(bpy.context)
+ return MUV_OT_TextureLock_Intr.is_running(bpy.context)
def set_func(_, __):
pass
@@ -256,7 +261,8 @@ class Properties:
del scene.muv_texture_lock_connect
-class OperatorLock(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureLock_Lock(bpy.types.Operator):
"""
Operation class: Lock Texture
"""
@@ -302,7 +308,8 @@ class OperatorLock(bpy.types.Operator):
return {'FINISHED'}
-class OperatorUnlock(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureLock_Unlock(bpy.types.Operator):
"""
Operation class: Unlock Texture
"""
@@ -326,7 +333,11 @@ class OperatorUnlock(bpy.types.Operator):
props = sc.muv_props.texture_lock
if not props.verts_orig:
return False
- return OperatorLock.is_ready(context) and is_valid_context(context)
+ if not MUV_OT_TextureLock_Lock.is_ready(context):
+ return False
+ if not is_valid_context(context):
+ return False
+ return True
def execute(self, context):
sc = context.scene
@@ -383,7 +394,8 @@ class OperatorUnlock(bpy.types.Operator):
return {'FINISHED'}
-class OperatorIntr(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureLock_Intr(bpy.types.Operator):
"""
Operation class: Texture Lock (Interactive mode)
"""
@@ -500,10 +512,10 @@ class OperatorIntr(bpy.types.Operator):
def modal(self, context, event):
if not is_valid_context(context):
- OperatorIntr.handle_remove(context)
+ MUV_OT_TextureLock_Intr.handle_remove(context)
return {'FINISHED'}
- if not OperatorIntr.is_running(context):
+ if not MUV_OT_TextureLock_Intr.is_running(context):
return {'FINISHED'}
if context.area:
@@ -521,11 +533,11 @@ class OperatorIntr(bpy.types.Operator):
if not is_valid_context(context):
return {'CANCELLED'}
- if not OperatorIntr.is_running(context):
- OperatorIntr.handle_add(self, context)
+ if not MUV_OT_TextureLock_Intr.is_running(context):
+ MUV_OT_TextureLock_Intr.handle_add(self, context)
return {'RUNNING_MODAL'}
else:
- OperatorIntr.handle_remove(context)
+ MUV_OT_TextureLock_Intr.handle_remove(context)
if context.area:
context.area.tag_redraw()
diff --git a/uv_magic_uv/op/texture_projection.py b/uv_magic_uv/legacy/op/texture_projection.py
index bdf0ad67..58f69c9d 100644
--- a/uv_magic_uv/op/texture_projection.py
+++ b/uv_magic_uv/legacy/op/texture_projection.py
@@ -36,13 +36,15 @@ from bpy.props import (
FloatProperty,
)
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'Operator',
- 'OperatorProject',
+ 'MUV_OT_TextureProjection',
+ 'MUV_OT_TextureProjection_Project',
]
@@ -143,11 +145,14 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "texture_projection"
+
@classmethod
def init_props(cls, scene):
def get_func(_):
- return Operator.is_running(bpy.context)
+ return MUV_OT_TextureProjection.is_running(bpy.context)
def set_func(_, __):
pass
@@ -214,7 +219,8 @@ class Properties:
del scene.muv_texture_projection_assign_uvmap
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureProjection(bpy.types.Operator):
"""
Operation class: Texture Projection
Render texture
@@ -240,7 +246,7 @@ class Operator(bpy.types.Operator):
@classmethod
def handle_add(cls, obj, context):
cls.__handle = bpy.types.SpaceView3D.draw_handler_add(
- Operator.draw_texture,
+ MUV_OT_TextureProjection.draw_texture,
(obj, context), 'WINDOW', 'POST_PIXEL')
@classmethod
@@ -301,10 +307,10 @@ class Operator(bpy.types.Operator):
bgl.glEnd()
def invoke(self, context, _):
- if not Operator.is_running(context):
- Operator.handle_add(self, context)
+ if not MUV_OT_TextureProjection.is_running(context):
+ MUV_OT_TextureProjection.handle_add(self, context)
else:
- Operator.handle_remove()
+ MUV_OT_TextureProjection.handle_remove()
if context.area:
context.area.tag_redraw()
@@ -312,7 +318,8 @@ class Operator(bpy.types.Operator):
return {'FINISHED'}
-class OperatorProject(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureProjection_Project(bpy.types.Operator):
"""
Operation class: Project texture
"""
@@ -327,7 +334,7 @@ class OperatorProject(bpy.types.Operator):
# we can not get area/space/region from console
if common.is_console_mode():
return True
- if not Operator.is_running(context):
+ if not MUV_OT_TextureProjection.is_running(context):
return False
return is_valid_context(context)
diff --git a/uv_magic_uv/op/texture_wrap.py b/uv_magic_uv/legacy/op/texture_wrap.py
index a7c58847..cb4cc78c 100644
--- a/uv_magic_uv/op/texture_wrap.py
+++ b/uv_magic_uv/legacy/op/texture_wrap.py
@@ -29,13 +29,15 @@ from bpy.props import (
BoolProperty,
)
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'OperatorRefer',
- 'OperatorSet',
+ 'MUV_OT_TextureWrap_Refer',
+ 'MUV_OT_TextureWrap_Set',
]
@@ -60,7 +62,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "texture_wrap"
+
@classmethod
def init_props(cls, scene):
class Props():
@@ -93,7 +98,8 @@ class Properties:
del scene.muv_texture_wrap_selseq
-class OperatorRefer(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureWrap_Refer(bpy.types.Operator):
"""
Operation class: Refer UV
"""
@@ -132,7 +138,8 @@ class OperatorRefer(bpy.types.Operator):
return {'FINISHED'}
-class OperatorSet(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureWrap_Set(bpy.types.Operator):
"""
Operation class: Set UV
"""
diff --git a/uv_magic_uv/legacy/op/transfer_uv.py b/uv_magic_uv/legacy/op/transfer_uv.py
new file mode 100644
index 00000000..cd0e4dd9
--- /dev/null
+++ b/uv_magic_uv/legacy/op/transfer_uv.py
@@ -0,0 +1,172 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>, Mifth, MaxRobinot"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+import bmesh
+from bpy.props import BoolProperty
+
+from ... import common
+from ...impl import transfer_uv_impl as impl
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+
+
+__all__ = [
+ 'Properties',
+ 'MUV_OT_TransferUV_CopyUV',
+ 'MUV_OT_TransferUV_PasteUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+ idname = "transfer_uv"
+
+ @classmethod
+ def init_props(cls, scene):
+ class Props():
+ topology_copied = None
+
+ scene.muv_props.transfer_uv = Props()
+
+ scene.muv_transfer_uv_enabled = BoolProperty(
+ name="Transfer UV Enabled",
+ description="Transfer UV is enabled",
+ default=False
+ )
+ scene.muv_transfer_uv_invert_normals = BoolProperty(
+ name="Invert Normals",
+ description="Invert Normals",
+ default=False
+ )
+ scene.muv_transfer_uv_copy_seams = BoolProperty(
+ name="Copy Seams",
+ description="Copy Seams",
+ default=True
+ )
+
+ @classmethod
+ def del_props(cls, scene):
+ del scene.muv_transfer_uv_enabled
+ del scene.muv_transfer_uv_invert_normals
+ del scene.muv_transfer_uv_copy_seams
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_TransferUV_CopyUV(bpy.types.Operator):
+ """
+ Operation class: Transfer UV copy
+ Topological based copy
+ """
+
+ bl_idname = "uv.muv_transfer_uv_operator_copy_uv"
+ bl_label = "Transfer UV Copy UV"
+ bl_description = "Transfer UV Copy UV (Topological based copy)"
+ 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 impl.is_valid_context(context)
+
+ def execute(self, context):
+ props = context.scene.muv_props.transfer_uv
+ active_obj = context.scene.objects.active
+ bm = bmesh.from_edit_mesh(active_obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ uv_layer = impl.get_uv_layer(self, bm)
+ if uv_layer is None:
+ return {'CANCELLED'}
+
+ faces = impl.get_selected_src_faces(self, bm, uv_layer)
+ if faces is None:
+ return {'CANCELLED'}
+ props.topology_copied = faces
+
+ bmesh.update_edit_mesh(active_obj.data)
+
+ return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_TransferUV_PasteUV(bpy.types.Operator):
+ """
+ Operation class: Transfer UV paste
+ Topological based paste
+ """
+
+ bl_idname = "uv.muv_transfer_uv_operator_paste_uv"
+ bl_label = "Transfer UV Paste UV"
+ bl_description = "Transfer UV Paste UV (Topological based paste)"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ invert_normals = BoolProperty(
+ name="Invert Normals",
+ description="Invert Normals",
+ default=False
+ )
+ copy_seams = BoolProperty(
+ name="Copy 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.transfer_uv
+ if not props.topology_copied:
+ return False
+ return impl.is_valid_context(context)
+
+ def execute(self, context):
+ props = context.scene.muv_props.transfer_uv
+ active_obj = context.scene.objects.active
+ bm = bmesh.from_edit_mesh(active_obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ # get UV layer
+ uv_layer = impl.get_uv_layer(self, bm)
+ if uv_layer is None:
+ return {'CANCELLED'}
+
+ ret = impl.paste_uv(self, bm, uv_layer, props.topology_copied,
+ self.invert_normals, self.copy_seams)
+ if ret:
+ return {'CANCELLED'}
+
+ bmesh.update_edit_mesh(active_obj.data)
+ if self.copy_seams:
+ active_obj.data.show_edge_seams = True
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/op/unwrap_constraint.py b/uv_magic_uv/legacy/op/unwrap_constraint.py
index b2368fc4..f06efce1 100644
--- a/uv_magic_uv/op/unwrap_constraint.py
+++ b/uv_magic_uv/legacy/op/unwrap_constraint.py
@@ -29,12 +29,14 @@ from bpy.props import (
FloatProperty,
)
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_UnwrapConstraint',
]
@@ -59,7 +61,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "unwrap_constraint"
+
@classmethod
def init_props(cls, scene):
scene.muv_unwrap_constraint_enabled = BoolProperty(
@@ -85,7 +90,8 @@ class Properties:
del scene.muv_unwrap_constraint_v_const
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UnwrapConstraint(bpy.types.Operator):
"""
Operation class: Unwrap with constrain UV coordinate
"""
diff --git a/uv_magic_uv/op/uv_bounding_box.py b/uv_magic_uv/legacy/op/uv_bounding_box.py
index 4aa8874b..47c27e41 100644
--- a/uv_magic_uv/op/uv_bounding_box.py
+++ b/uv_magic_uv/legacy/op/uv_bounding_box.py
@@ -32,12 +32,14 @@ import mathutils
import bmesh
from bpy.props import BoolProperty, EnumProperty
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_UVBoundingBox',
]
@@ -67,7 +69,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "uv_bounding_box"
+
@classmethod
def init_props(cls, scene):
class Props():
@@ -78,7 +83,7 @@ class Properties:
scene.muv_props.uv_bounding_box = Props()
def get_func(_):
- return Operator.is_running(bpy.context)
+ return MUV_OT_UVBoundingBox.is_running(bpy.context)
def set_func(_, __):
pass
@@ -599,7 +604,8 @@ class StateManager():
return self.__state
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVBoundingBox(bpy.types.Operator):
"""
Operation class: UV Bounding Box
"""
@@ -677,7 +683,7 @@ class Operator(bpy.types.Operator):
"""
props = context.scene.muv_props.uv_bounding_box
- if not Operator.is_running(context):
+ if not MUV_OT_UVBoundingBox.is_running(context):
return
if not is_valid_context(context):
@@ -781,11 +787,11 @@ class Operator(bpy.types.Operator):
props = context.scene.muv_props.uv_bounding_box
common.redraw_all_areas()
- if not Operator.is_running(context):
+ if not MUV_OT_UVBoundingBox.is_running(context):
return {'FINISHED'}
if not is_valid_context(context):
- Operator.handle_remove(context)
+ MUV_OT_UVBoundingBox.handle_remove(context)
return {'FINISHED'}
region_types = [
@@ -812,15 +818,15 @@ class Operator(bpy.types.Operator):
def invoke(self, context, _):
props = context.scene.muv_props.uv_bounding_box
- if Operator.is_running(context):
- Operator.handle_remove(context)
+ if MUV_OT_UVBoundingBox.is_running(context):
+ MUV_OT_UVBoundingBox.handle_remove(context)
return {'FINISHED'}
props.uv_info_ini = self.__get_uv_info(context)
if props.uv_info_ini is None:
return {'CANCELLED'}
- Operator.handle_add(self, context)
+ MUV_OT_UVBoundingBox.handle_add(self, context)
props.ctrl_points_ini = self.__get_ctrl_point(props.uv_info_ini)
trans_mat = self.__cmd_exec.execute()
diff --git a/uv_magic_uv/op/uv_inspection.py b/uv_magic_uv/legacy/op/uv_inspection.py
index 0c05e03d..57d42468 100644
--- a/uv_magic_uv/op/uv_inspection.py
+++ b/uv_magic_uv/legacy/op/uv_inspection.py
@@ -28,13 +28,15 @@ import bmesh
import bgl
from bpy.props import BoolProperty, EnumProperty
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'OperatorRender',
- 'OperatorUpdate',
+ 'MUV_OT_UVInspection_Render',
+ 'MUV_OT_UVInspection_Update',
]
@@ -61,7 +63,10 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "uv_inspection"
+
@classmethod
def init_props(cls, scene):
class Props():
@@ -71,7 +76,7 @@ class Properties:
scene.muv_props.uv_inspection = Props()
def get_func(_):
- return OperatorRender.is_running(bpy.context)
+ return MUV_OT_UVInspection_Render.is_running(bpy.context)
def set_func(_, __):
pass
@@ -122,7 +127,8 @@ class Properties:
del scene.muv_uv_inspection_show_mode
-class OperatorRender(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVInspection_Render(bpy.types.Operator):
"""
Operation class: Render UV Inspection
No operation (only rendering)
@@ -149,7 +155,8 @@ class OperatorRender(bpy.types.Operator):
def handle_add(cls, obj, context):
sie = bpy.types.SpaceImageEditor
cls.__handle = sie.draw_handler_add(
- OperatorRender.draw, (obj, context), 'WINDOW', 'POST_PIXEL')
+ MUV_OT_UVInspection_Render.draw, (obj, context),
+ 'WINDOW', 'POST_PIXEL')
@classmethod
def handle_remove(cls):
@@ -164,7 +171,7 @@ class OperatorRender(bpy.types.Operator):
props = sc.muv_props.uv_inspection
prefs = context.user_preferences.addons["uv_magic_uv"].preferences
- if not OperatorRender.is_running(context):
+ if not MUV_OT_UVInspection_Render.is_running(context):
return
# OpenGL configuration
@@ -213,11 +220,11 @@ class OperatorRender(bpy.types.Operator):
bgl.glEnd()
def invoke(self, context, _):
- if not OperatorRender.is_running(context):
+ if not MUV_OT_UVInspection_Render.is_running(context):
update_uvinsp_info(context)
- OperatorRender.handle_add(self, context)
+ MUV_OT_UVInspection_Render.handle_add(self, context)
else:
- OperatorRender.handle_remove()
+ MUV_OT_UVInspection_Render.handle_remove()
if context.area:
context.area.tag_redraw()
@@ -244,7 +251,8 @@ def update_uvinsp_info(context):
props.flipped_info = common.get_flipped_uv_info(sel_faces, uv_layer)
-class OperatorUpdate(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVInspection_Update(bpy.types.Operator):
"""
Operation class: Update
"""
@@ -259,7 +267,7 @@ class OperatorUpdate(bpy.types.Operator):
# we can not get area/space/region from console
if common.is_console_mode():
return True
- if not OperatorRender.is_running(context):
+ if not MUV_OT_UVInspection_Render.is_running(context):
return False
return is_valid_context(context)
diff --git a/uv_magic_uv/op/uv_sculpt.py b/uv_magic_uv/legacy/op/uv_sculpt.py
index 63c1adfe..3754a759 100644
--- a/uv_magic_uv/op/uv_sculpt.py
+++ b/uv_magic_uv/legacy/op/uv_sculpt.py
@@ -39,12 +39,14 @@ from bpy.props import (
FloatProperty,
)
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_UVSculpt',
]
@@ -69,11 +71,14 @@ def is_valid_context(context):
return True
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "uv_sculpt"
+
@classmethod
def init_props(cls, scene):
def get_func(_):
- return Operator.is_running(bpy.context)
+ return MUV_OT_UVSculpt.is_running(bpy.context)
def set_func(_, __):
pass
@@ -151,7 +156,8 @@ class Properties:
del scene.muv_uv_sculpt_relax_method
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVSculpt(bpy.types.Operator):
"""
Operation class: UV Sculpt in View3D
"""
@@ -427,8 +433,8 @@ class Operator(bpy.types.Operator):
if context.area:
context.area.tag_redraw()
- if not Operator.is_running(context):
- Operator.handle_remove(context)
+ if not MUV_OT_UVSculpt.is_running(context):
+ MUV_OT_UVSculpt.handle_remove(context)
return {'FINISHED'}
@@ -469,9 +475,9 @@ class Operator(bpy.types.Operator):
if context.area:
context.area.tag_redraw()
- if Operator.is_running(context):
- Operator.handle_remove(context)
+ if MUV_OT_UVSculpt.is_running(context):
+ MUV_OT_UVSculpt.handle_remove(context)
else:
- Operator.handle_add(self, context)
+ MUV_OT_UVSculpt.handle_add(self, context)
return {'RUNNING_MODAL'}
diff --git a/uv_magic_uv/legacy/op/uvw.py b/uv_magic_uv/legacy/op/uvw.py
new file mode 100644
index 00000000..56777b18
--- /dev/null
+++ b/uv_magic_uv/legacy/op/uvw.py
@@ -0,0 +1,181 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Alexander Milovsky, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+import bmesh
+from bpy.props import (
+ FloatProperty,
+ FloatVectorProperty,
+ BoolProperty
+)
+
+from ... import common
+from ...impl import uvw_impl as impl
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+
+
+__all__ = [
+ 'Properties',
+ 'MUV_OT_UVW_BoxMap',
+ 'MUV_OT_UVW_BestPlanerMap',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+ idname = "uvw"
+
+ @classmethod
+ def init_props(cls, scene):
+ scene.muv_uvw_enabled = BoolProperty(
+ name="UVW Enabled",
+ description="UVW is enabled",
+ default=False
+ )
+ scene.muv_uvw_assign_uvmap = BoolProperty(
+ name="Assign UVMap",
+ description="Assign UVMap when no UVmaps are available",
+ default=True
+ )
+
+ @classmethod
+ def del_props(cls, scene):
+ del scene.muv_uvw_enabled
+ del scene.muv_uvw_assign_uvmap
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVW_BoxMap(bpy.types.Operator):
+ bl_idname = "uv.muv_uvw_operator_box_map"
+ bl_label = "Box Map"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ size = FloatProperty(
+ name="Size",
+ default=1.0,
+ precision=4
+ )
+ rotation = FloatVectorProperty(
+ name="XYZ Rotation",
+ size=3,
+ default=(0.0, 0.0, 0.0)
+ )
+ offset = FloatVectorProperty(
+ name="XYZ Offset",
+ size=3,
+ default=(0.0, 0.0, 0.0)
+ )
+ tex_aspect = FloatProperty(
+ name="Texture Aspect",
+ default=1.0,
+ precision=4
+ )
+ assign_uvmap = BoolProperty(
+ name="Assign UVMap",
+ description="Assign UVMap when no UVmaps are available",
+ default=True
+ )
+
+ @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):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ # get UV layer
+ uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap)
+ if not uv_layer:
+ return {'CANCELLED'}
+
+ impl.apply_box_map(bm, uv_layer, self.size, self.offset,
+ self.rotation, self.tex_aspect)
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVW_BestPlanerMap(bpy.types.Operator):
+ bl_idname = "uv.muv_uvw_operator_best_planer_map"
+ bl_label = "Best Planer Map"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ size = FloatProperty(
+ name="Size",
+ default=1.0,
+ precision=4
+ )
+ rotation = FloatProperty(
+ name="XY Rotation",
+ default=0.0
+ )
+ offset = FloatVectorProperty(
+ name="XY Offset",
+ size=2,
+ default=(0.0, 0.0)
+ )
+ tex_aspect = FloatProperty(
+ name="Texture Aspect",
+ default=1.0,
+ precision=4
+ )
+ assign_uvmap = BoolProperty(
+ name="Assign UVMap",
+ description="Assign UVMap when no UVmaps are available",
+ default=True
+ )
+
+ @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):
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
+
+ # get UV layer
+ uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap)
+ if not uv_layer:
+ return {'CANCELLED'}
+
+ impl.apply_planer_map(bm, uv_layer, self.size, self.offset,
+ self.rotation, self.tex_aspect)
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
diff --git a/uv_magic_uv/op/world_scale_uv.py b/uv_magic_uv/legacy/op/world_scale_uv.py
index e1a44954..e56b6bfa 100644
--- a/uv_magic_uv/op/world_scale_uv.py
+++ b/uv_magic_uv/legacy/op/world_scale_uv.py
@@ -35,15 +35,17 @@ from bpy.props import (
BoolProperty,
)
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'OperatorMeasure',
- 'OperatorApplyManual',
- 'OperatorApplyScalingDensity',
- 'OperatorApplyProportionalToMesh',
+ 'MUV_OT_WorldScaleUV_Measure',
+ 'MUV_OT_WorldScaleUV_ApplyManual',
+ 'MUV_OT_WorldScaleUV_ApplyScalingDensity',
+ 'MUV_OT_WorldScaleUV_ApplyProportionalToMesh',
]
@@ -83,7 +85,10 @@ def measure_wsuv_info(obj, tex_size=None):
return uv_area, mesh_area, density
+@PropertyClassRegistry(legacy=True)
class Properties:
+ idname = "world_scale_uv"
+
@classmethod
def init_props(cls, scene):
scene.muv_world_scale_uv_enabled = BoolProperty(
@@ -172,7 +177,8 @@ class Properties:
del scene.muv_world_scale_uv_origin
-class OperatorMeasure(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator):
"""
Operation class: Measure face size
"""
@@ -310,7 +316,8 @@ def apply(obj, origin, factor):
bmesh.update_edit_mesh(obj.data)
-class OperatorApplyManual(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator):
"""
Operation class: Apply scaled UV (Manual)
"""
@@ -407,7 +414,8 @@ class OperatorApplyManual(bpy.types.Operator):
return self.__apply_manual(context)
-class OperatorApplyScalingDensity(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator):
"""
Operation class: Apply scaled UV (Scaling Density)
"""
@@ -529,7 +537,8 @@ class OperatorApplyScalingDensity(bpy.types.Operator):
return self.__apply_scaling_density(context)
-class OperatorApplyProportionalToMesh(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator):
"""
Operation class: Apply scaled UV (Proportional to mesh)
"""
diff --git a/uv_magic_uv/legacy/preferences.py b/uv_magic_uv/legacy/preferences.py
new file mode 100644
index 00000000..931cc1d4
--- /dev/null
+++ b/uv_magic_uv/legacy/preferences.py
@@ -0,0 +1,468 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+from bpy.props import (
+ FloatProperty,
+ FloatVectorProperty,
+ BoolProperty,
+ EnumProperty,
+ IntProperty,
+)
+from bpy.types import AddonPreferences
+
+from . import op
+from . import ui
+from .. import addon_updater_ops
+from ..utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'add_builtin_menu',
+ 'remove_builtin_menu',
+ 'Preferences'
+]
+
+
+def view3d_uvmap_menu_fn(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ layout.separator()
+ layout.label(text="Copy/Paste UV", icon='IMAGE_COL')
+ # Copy/Paste UV
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_CopyPasteUV.bl_idname,
+ text="Copy/Paste UV")
+ # Transfer UV
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TransferUV.bl_idname,
+ text="Transfer UV")
+
+ layout.separator()
+ layout.label("UV Manipulation", icon='IMAGE_COL')
+ # Flip/Rotate UV
+ ops = layout.operator(op.flip_rotate_uv.MUV_OT_FlipRotate.bl_idname,
+ text="Flip/Rotate UV")
+ ops.seams = sc.muv_flip_rotate_uv_seams
+ # Mirror UV
+ ops = layout.operator(op.mirror_uv.MUV_OT_MirrorUV.bl_idname,
+ text="Mirror UV")
+ ops.axis = sc.muv_mirror_uv_axis
+ # Move UV
+ layout.operator(op.move_uv.MUV_OT_MoveUV.bl_idname, text="Move UV")
+ # World Scale UV
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_WorldScaleUV.bl_idname,
+ text="World Scale UV")
+ # Preserve UV
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_PreserveUVAspect.bl_idname,
+ text="Preserve UV")
+ # Texture Lock
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TextureLock.bl_idname,
+ text="Texture Lock")
+ # Texture Wrap
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TextureWrap.bl_idname,
+ text="Texture Wrap")
+ # UV Sculpt
+ layout.prop(sc, "muv_uv_sculpt_enable", text="UV Sculpt")
+
+ layout.separator()
+ layout.label("UV Mapping", icon='IMAGE_COL')
+ # Unwrap Constraint
+ ops = layout.operator(
+ op.unwrap_constraint.MUV_OT_UnwrapConstraint.bl_idname,
+ text="Unwrap Constraint")
+ ops.u_const = sc.muv_unwrap_constraint_u_const
+ ops.v_const = sc.muv_unwrap_constraint_v_const
+ # Texture Projection
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TextureProjection.bl_idname,
+ text="Texture Projection")
+ # UVW
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_UVW.bl_idname, text="UVW")
+
+
+def view3d_object_menu_fn(self, _):
+ layout = self.layout
+
+ layout.separator()
+ # Copy/Paste UV (Among Objecct)
+ layout.menu(ui.VIEW3D_MT_object.MUV_MT_CopyPasteUV_Object.bl_idname,
+ text="Copy/Paste UV")
+ layout.label("Copy/Paste UV", icon='IMAGE_COL')
+
+
+def image_uvs_menu_fn(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ layout.separator()
+ # Align UV Cursor
+ layout.menu(ui.IMAGE_MT_uvs.MUV_MT_AlignUVCursor.bl_idname,
+ text="Align UV Cursor")
+ # UV Bounding Box
+ layout.prop(sc, "muv_uv_bounding_box_show", text="UV Bounding Box")
+ # UV Inspection
+ layout.menu(ui.IMAGE_MT_uvs.MUV_MT_UVInspection.bl_idname,
+ text="UV Inspection")
+ layout.label("Editor Enhancement", icon='IMAGE_COL')
+
+ layout.separator()
+ # Align UV
+ layout.menu(ui.IMAGE_MT_uvs.MUV_MT_AlignUV.bl_idname, text="Align UV")
+ # Smooth UV
+ ops = layout.operator(op.smooth_uv.MUV_OT_SmoothUV.bl_idname,
+ text="Smooth")
+ ops.transmission = sc.muv_smooth_uv_transmission
+ ops.select = sc.muv_smooth_uv_select
+ ops.mesh_infl = sc.muv_smooth_uv_mesh_infl
+ # Select UV
+ layout.menu(ui.IMAGE_MT_uvs.MUV_MT_SelectUV.bl_idname, text="Select UV")
+ # Pack UV
+ ops = layout.operator(op.pack_uv.MUV_OT_PackUV.bl_idname, text="Pack UV")
+ ops.allowable_center_deviation = sc.muv_pack_uv_allowable_center_deviation
+ ops.allowable_size_deviation = sc.muv_pack_uv_allowable_size_deviation
+ layout.label("UV Manipulation", icon='IMAGE_COL')
+
+ layout.separator()
+ # Copy/Paste UV (on UV/Image Editor)
+ layout.menu(ui.IMAGE_MT_uvs.MUV_MT_CopyPasteUV_UVEdit.bl_idname,
+ text="Copy/Paste UV")
+ layout.label("Copy/Paste UV", icon='IMAGE_COL')
+
+
+def add_builtin_menu():
+ bpy.types.VIEW3D_MT_uv_map.append(view3d_uvmap_menu_fn)
+ bpy.types.VIEW3D_MT_object.append(view3d_object_menu_fn)
+ bpy.types.IMAGE_MT_uvs.append(image_uvs_menu_fn)
+
+
+def remove_builtin_menu():
+ bpy.types.IMAGE_MT_uvs.remove(image_uvs_menu_fn)
+ bpy.types.VIEW3D_MT_uv_map.remove(view3d_uvmap_menu_fn)
+ bpy.types.VIEW3D_MT_object.remove(view3d_object_menu_fn)
+
+
+@BlClassRegistry(legacy=True)
+class Preferences(AddonPreferences):
+ """Preferences class: Preferences for this add-on"""
+
+ bl_idname = "uv_magic_uv"
+
+ def update_enable_builtin_menu(self, _):
+ if self['enable_builtin_menu']:
+ add_builtin_menu()
+ else:
+ remove_builtin_menu()
+
+ # enable to add features to built-in menu
+ enable_builtin_menu = BoolProperty(
+ name="Built-in Menu",
+ description="Enable built-in menu",
+ default=True,
+ update=update_enable_builtin_menu
+ )
+
+ # for UV Sculpt
+ uv_sculpt_brush_color = FloatVectorProperty(
+ name="Color",
+ description="Color",
+ default=(1.0, 0.4, 0.4, 1.0),
+ min=0.0,
+ max=1.0,
+ size=4,
+ subtype='COLOR'
+ )
+
+ # for Overlapped UV
+ uv_inspection_overlapped_color = FloatVectorProperty(
+ name="Color",
+ description="Color",
+ default=(0.0, 0.0, 1.0, 0.3),
+ min=0.0,
+ max=1.0,
+ size=4,
+ subtype='COLOR'
+ )
+
+ # for Flipped UV
+ uv_inspection_flipped_color = FloatVectorProperty(
+ name="Color",
+ description="Color",
+ default=(1.0, 0.0, 0.0, 0.3),
+ min=0.0,
+ max=1.0,
+ size=4,
+ subtype='COLOR'
+ )
+
+ # for Texture Projection
+ texture_projection_canvas_padding = FloatVectorProperty(
+ name="Canvas Padding",
+ description="Canvas Padding",
+ size=2,
+ max=50.0,
+ min=0.0,
+ default=(20.0, 20.0))
+
+ # for UV Bounding Box
+ uv_bounding_box_cp_size = FloatProperty(
+ name="Size",
+ description="Control Point Size",
+ default=6.0,
+ min=3.0,
+ max=100.0)
+ uv_bounding_box_cp_react_size = FloatProperty(
+ name="React Size",
+ description="Size event fired",
+ default=10.0,
+ min=3.0,
+ max=100.0)
+
+ # for UI
+ category = EnumProperty(
+ name="Category",
+ description="Preferences Category",
+ items=[
+ ('INFO', "Information", "Information about this add-on"),
+ ('CONFIG', "Configuration", "Configuration about this add-on"),
+ ('UPDATE', "Update", "Update this add-on"),
+ ],
+ default='INFO'
+ )
+ info_desc_expanded = BoolProperty(
+ name="Description",
+ description="Description",
+ default=False
+ )
+ info_loc_expanded = BoolProperty(
+ name="Location",
+ description="Location",
+ default=False
+ )
+ conf_uv_sculpt_expanded = BoolProperty(
+ name="UV Sculpt",
+ description="UV Sculpt",
+ default=False
+ )
+ conf_uv_inspection_expanded = BoolProperty(
+ name="UV Inspection",
+ description="UV Inspection",
+ default=False
+ )
+ conf_texture_projection_expanded = BoolProperty(
+ name="Texture Projection",
+ description="Texture Projection",
+ default=False
+ )
+ conf_uv_bounding_box_expanded = BoolProperty(
+ name="UV Bounding Box",
+ description="UV Bounding Box",
+ default=False
+ )
+
+ # for add-on updater
+ auto_check_update = BoolProperty(
+ name="Auto-check for Update",
+ description="If enabled, auto-check for updates using an interval",
+ default=False
+ )
+ updater_intrval_months = IntProperty(
+ name='Months',
+ description="Number of months between checking for updates",
+ default=0,
+ min=0
+ )
+ updater_intrval_days = IntProperty(
+ name='Days',
+ description="Number of days between checking for updates",
+ default=7,
+ min=0
+ )
+ updater_intrval_hours = IntProperty(
+ name='Hours',
+ description="Number of hours between checking for updates",
+ default=0,
+ min=0,
+ max=23
+ )
+ updater_intrval_minutes = IntProperty(
+ name='Minutes',
+ description="Number of minutes between checking for updates",
+ default=0,
+ min=0,
+ max=59
+ )
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.row().prop(self, "category", expand=True)
+
+ if self.category == 'INFO':
+ layout.prop(
+ self, "info_desc_expanded", text="Description",
+ icon='DISCLOSURE_TRI_DOWN' if self.info_desc_expanded
+ else 'DISCLOSURE_TRI_RIGHT')
+ if self.info_desc_expanded:
+ column = layout.column(align=True)
+ column.label("Magic UV is composed of many UV editing" +
+ " features.")
+ column.label("See tutorial page if you are new to this" +
+ " add-on.")
+ column.label("https://github.com/nutti/Magic-UV/wiki/Tutorial")
+
+ layout.prop(
+ self, "info_loc_expanded", text="Location",
+ icon='DISCLOSURE_TRI_DOWN' if self.info_loc_expanded
+ else 'DISCLOSURE_TRI_RIGHT')
+ if self.info_loc_expanded:
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("3D View > Tool shelf > Copy/Paste UV (Object mode)")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Copy/Paste UV (Among objects)")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("3D View > Tool shelf > Copy/Paste UV (Edit mode)")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Copy/Paste UV (Among faces in 3D View)")
+ col.label("Transfer UV")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("3D View > Tool shelf > UV Manipulation (Edit mode)")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Flip/Rotate UV")
+ col.label("Mirror UV")
+ col.label("Move UV")
+ col.label("World Scale UV")
+ col.label("Preserve UV Aspect")
+ col.label("Texture Lock")
+ col.label("Texture Wrap")
+ col.label("UV Sculpt")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("3D View > Tool shelf > UV Manipulation (Edit mode)")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Unwrap Constraint")
+ col.label("Texture Projection")
+ col.label("UVW")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("UV/Image Editor > Tool shelf > Copy/Paste UV")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Copy/Paste UV (Among faces in UV/Image Editor)")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("UV/Image Editor > Tool shelf > UV Manipulation")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Align UV")
+ col.label("Smooth UV")
+ col.label("Select UV")
+ col.label("Pack UV (Extension)")
+
+ row = layout.row(align=True)
+ sp = row.split(percentage=0.5)
+ sp.label("UV/Image Editor > Tool shelf > Editor Enhancement")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("Align UV Cursor")
+ col.label("UV Cursor Location")
+ col.label("UV Bounding Box")
+ col.label("UV Inspection")
+
+ elif self.category == 'CONFIG':
+ layout.prop(self, "enable_builtin_menu", text="Built-in Menu")
+
+ layout.separator()
+
+ layout.prop(
+ self, "conf_uv_sculpt_expanded", text="UV Sculpt",
+ icon='DISCLOSURE_TRI_DOWN' if self.conf_uv_sculpt_expanded
+ else 'DISCLOSURE_TRI_RIGHT')
+ if self.conf_uv_sculpt_expanded:
+ sp = layout.split(percentage=0.05)
+ col = sp.column() # spacer
+ sp = sp.split(percentage=0.3)
+ col = sp.column()
+ col.label("Brush Color:")
+ col.prop(self, "uv_sculpt_brush_color", text="")
+ layout.separator()
+
+ layout.prop(
+ self, "conf_uv_inspection_expanded", text="UV Inspection",
+ icon='DISCLOSURE_TRI_DOWN' if self.conf_uv_inspection_expanded
+ else 'DISCLOSURE_TRI_RIGHT')
+ if self.conf_uv_inspection_expanded:
+ sp = layout.split(percentage=0.05)
+ col = sp.column() # spacer
+ sp = sp.split(percentage=0.3)
+ col = sp.column()
+ col.label("Overlapped UV Color:")
+ col.prop(self, "uv_inspection_overlapped_color", text="")
+ sp = sp.split(percentage=0.45)
+ col = sp.column()
+ col.label("Flipped UV Color:")
+ col.prop(self, "uv_inspection_flipped_color", text="")
+ layout.separator()
+
+ layout.prop(
+ self, "conf_texture_projection_expanded",
+ text="Texture Projection",
+ icon='DISCLOSURE_TRI_DOWN'
+ if self.conf_texture_projection_expanded
+ else 'DISCLOSURE_TRI_RIGHT')
+ if self.conf_texture_projection_expanded:
+ sp = layout.split(percentage=0.05)
+ col = sp.column() # spacer
+ sp = sp.split(percentage=0.3)
+ col = sp.column()
+ col.prop(self, "texture_projection_canvas_padding")
+ layout.separator()
+
+ layout.prop(
+ self, "conf_uv_bounding_box_expanded", text="UV Bounding Box",
+ icon='DISCLOSURE_TRI_DOWN'
+ if self.conf_uv_bounding_box_expanded
+ else 'DISCLOSURE_TRI_RIGHT')
+ if self.conf_uv_bounding_box_expanded:
+ sp = layout.split(percentage=0.05)
+ col = sp.column() # spacer
+ sp = sp.split(percentage=0.3)
+ col = sp.column()
+ col.label("Control Point:")
+ col.prop(self, "uv_bounding_box_cp_size")
+ col.prop(self, "uv_bounding_box_cp_react_size")
+ layout.separator()
+
+ elif self.category == 'UPDATE':
+ addon_updater_ops.update_settings_ui(self, context)
diff --git a/uv_magic_uv/legacy/properites.py b/uv_magic_uv/legacy/properites.py
new file mode 100644
index 00000000..b64a48f3
--- /dev/null
+++ b/uv_magic_uv/legacy/properites.py
@@ -0,0 +1,61 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+from ..utils.property_class_registry import PropertyClassRegistry
+
+__all__ = [
+ 'MUV_Properties',
+ 'init_props',
+ 'clear_props',
+]
+
+
+# Properties used in this add-on.
+# pylint: disable=W0612
+class MUV_Properties():
+ def __init__(self):
+ self.prefs = MUV_Prefs()
+
+
+class MUV_Prefs():
+ expanded = {
+ "info_desc": False,
+ "info_loc": False,
+ "conf_uvsculpt": False,
+ "conf_uvinsp": False,
+ "conf_texproj": False,
+ "conf_uvbb": False
+ }
+
+
+def init_props(scene):
+ scene.muv_props = MUV_Properties()
+ PropertyClassRegistry.init_props(scene)
+
+
+def clear_props(scene):
+ PropertyClassRegistry.del_props(scene)
+ del scene.muv_props
diff --git a/uv_magic_uv/legacy/ui/IMAGE_MT_uvs.py b/uv_magic_uv/legacy/ui/IMAGE_MT_uvs.py
new file mode 100644
index 00000000..bf071bf5
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/IMAGE_MT_uvs.py
@@ -0,0 +1,197 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import (
+ align_uv_cursor,
+ copy_paste_uv_uvedit,
+ align_uv,
+ select_uv,
+ uv_inspection
+)
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'MUV_MT_CopyPasteUV_UVEdit',
+ 'MUV_MT_AlignUV',
+ 'MUV_MT_SelectUV',
+ 'MUV_MT_AlignUVCursor',
+ 'MUV_MT_UVInspection',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_UVEdit(bpy.types.Menu):
+ """
+ Menu class: Master menu of Copy/Paste UV coordinate on UV/ImageEditor
+ """
+
+ bl_idname = "uv.muv_copy_paste_uv_uvedit_menu"
+ bl_label = "Copy/Paste UV"
+ bl_description = "Copy and Paste UV coordinate among object"
+
+ def draw(self, _):
+ layout = self.layout
+
+ layout.operator(
+ copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_CopyUV.bl_idname,
+ text="Copy")
+ layout.operator(
+ copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_PasteUV.bl_idname,
+ text="Paste")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_AlignUV(bpy.types.Menu):
+ """
+ Menu class: Master menu of Align UV
+ """
+
+ bl_idname = "uv.muv_align_uv_menu"
+ bl_label = "Align UV"
+ bl_description = "Align UV"
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ ops = layout.operator(align_uv.MUV_OT_AlignUV_Circle.bl_idname,
+ text="Circle")
+ ops.transmission = sc.muv_align_uv_transmission
+ ops.select = sc.muv_align_uv_select
+
+ ops = layout.operator(align_uv.MUV_OT_AlignUV_Straighten.bl_idname,
+ text="Straighten")
+ ops.transmission = sc.muv_align_uv_transmission
+ ops.select = sc.muv_align_uv_select
+ ops.vertical = sc.muv_align_uv_vertical
+ ops.horizontal = sc.muv_align_uv_horizontal
+
+ ops = layout.operator(align_uv.MUV_OT_AlignUV_Axis.bl_idname,
+ text="XY-axis")
+ ops.transmission = sc.muv_align_uv_transmission
+ ops.select = sc.muv_align_uv_select
+ ops.vertical = sc.muv_align_uv_vertical
+ ops.horizontal = sc.muv_align_uv_horizontal
+ ops.location = sc.muv_align_uv_location
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_SelectUV(bpy.types.Menu):
+ """
+ Menu class: Master menu of Select UV
+ """
+
+ bl_idname = "uv.muv_select_uv_menu"
+ bl_label = "Select UV"
+ bl_description = "Select UV"
+
+ def draw(self, _):
+ layout = self.layout
+
+ layout.operator(select_uv.MUV_OT_SelectUV_SelectOverlapped.bl_idname,
+ text="Overlapped")
+ layout.operator(select_uv.MUV_OT_SelectUV_SelectFlipped.bl_idname,
+ text="Flipped")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_AlignUVCursor(bpy.types.Menu):
+ """
+ Menu class: Master menu of Align UV Cursor
+ """
+
+ bl_idname = "uv.muv_align_uv_cursor_menu"
+ bl_label = "Align UV Cursor"
+ bl_description = "Align UV cursor"
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Left Top")
+ ops.position = 'LEFT_TOP'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Middle Top")
+ ops.position = 'MIDDLE_TOP'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Right Top")
+ ops.position = 'RIGHT_TOP'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Left Middle")
+ ops.position = 'LEFT_MIDDLE'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Center")
+ ops.position = 'CENTER'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Right Middle")
+ ops.position = 'RIGHT_MIDDLE'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Left Bottom")
+ ops.position = 'LEFT_BOTTOM'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Middle Bottom")
+ ops.position = 'MIDDLE_BOTTOM'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+ ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+ text="Right Bottom")
+ ops.position = 'RIGHT_BOTTOM'
+ ops.base = sc.muv_align_uv_cursor_align_method
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_UVInspection(bpy.types.Menu):
+ """
+ Menu class: Master menu of UV Inspection
+ """
+
+ bl_idname = "uv.muv_uv_inspection_menu"
+ bl_label = "UV Inspection"
+ bl_description = "UV Inspection"
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ layout.prop(sc, "muv_uv_inspection_show", text="UV Inspection")
+ layout.operator(uv_inspection.MUV_OT_UVInspection_Update.bl_idname,
+ text="Update")
diff --git a/uv_magic_uv/legacy/ui/VIEW3D_MT_object.py b/uv_magic_uv/legacy/ui/VIEW3D_MT_object.py
new file mode 100644
index 00000000..e1c751ae
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/VIEW3D_MT_object.py
@@ -0,0 +1,54 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_object
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'MUV_MT_CopyPasteUV_Object',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_Object(bpy.types.Menu):
+ """
+ Menu class: Master menu of Copy/Paste UV coordinate among object
+ """
+
+ bl_idname = "uv.muv_copy_paste_uv_object_menu"
+ bl_label = "Copy/Paste UV"
+ bl_description = "Copy and Paste UV coordinate among object"
+
+ def draw(self, _):
+ layout = self.layout
+
+ layout.menu(
+ copy_paste_uv_object.MUV_MT_CopyPasteUVObject_CopyUV.bl_idname,
+ text="Copy")
+ layout.menu(
+ copy_paste_uv_object.MUV_MT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="Paste")
diff --git a/uv_magic_uv/legacy/ui/VIEW3D_MT_uv_map.py b/uv_magic_uv/legacy/ui/VIEW3D_MT_uv_map.py
new file mode 100644
index 00000000..d229322a
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/VIEW3D_MT_uv_map.py
@@ -0,0 +1,257 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+import bpy.utils
+
+from ..op import (
+ texture_lock,
+ copy_paste_uv,
+ preserve_uv_aspect,
+ texture_projection,
+ texture_wrap,
+ transfer_uv,
+ uvw,
+ world_scale_uv
+)
+from ..op.world_scale_uv import MUV_OT_WorldScaleUV_ApplyProportionalToMesh
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'MUV_MT_CopyPasteUV',
+ 'MUV_MT_TransferUV',
+ 'MUV_MT_TextureLock',
+ 'MUV_MT_WorldScaleUV',
+ 'MUV_MT_TextureWrap',
+ 'MUV_MT_UVW',
+ 'MUV_MT_TextureProjection',
+ 'MUV_MT_PreserveUVAspect',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV(bpy.types.Menu):
+ """
+ Menu class: Master menu of Copy/Paste UV coordinate
+ """
+
+ bl_idname = "uv.muv_copy_paste_uv_menu"
+ bl_label = "Copy/Paste UV"
+ bl_description = "Copy and Paste UV coordinate"
+
+ def draw(self, _):
+ layout = self.layout
+
+ layout.label(text="Default")
+ layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_CopyUV.bl_idname,
+ text="Copy")
+ layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_PasteUV.bl_idname,
+ text="Paste")
+
+ layout.separator()
+
+ layout.label(text="Selection Sequence")
+ layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+ text="Copy")
+ layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+ text="Paste")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_TransferUV(bpy.types.Menu):
+ """
+ Menu class: Master menu of Transfer UV coordinate
+ """
+
+ bl_idname = "uv.muv_transfer_uv_menu"
+ bl_label = "Transfer UV"
+ bl_description = "Transfer UV coordinate"
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ layout.operator(transfer_uv.MUV_OT_TransferUV_CopyUV.bl_idname,
+ text="Copy")
+ ops = layout.operator(transfer_uv.MUV_OT_TransferUV_PasteUV.bl_idname,
+ text="Paste")
+ ops.invert_normals = sc.muv_transfer_uv_invert_normals
+ ops.copy_seams = sc.muv_transfer_uv_copy_seams
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_TextureLock(bpy.types.Menu):
+ """
+ Menu class: Master menu of Texture Lock
+ """
+
+ bl_idname = "uv.muv_texture_lock_menu"
+ bl_label = "Texture Lock"
+ bl_description = "Lock texture when vertices of mesh (Preserve UV)"
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ layout.label("Normal Mode")
+ layout.operator(
+ texture_lock.MUV_OT_TextureLock_Lock.bl_idname,
+ text="Lock"
+ if not texture_lock.MUV_OT_TextureLock_Lock.is_ready(context)
+ else "ReLock")
+ ops = layout.operator(texture_lock.MUV_OT_TextureLock_Unlock.bl_idname,
+ text="Unlock")
+ ops.connect = sc.muv_texture_lock_connect
+
+ layout.separator()
+
+ layout.label("Interactive Mode")
+ layout.prop(sc, "muv_texture_lock_lock", text="Lock")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_WorldScaleUV(bpy.types.Menu):
+ """
+ Menu class: Master menu of world scale UV
+ """
+
+ bl_idname = "uv.muv_world_scale_uv_menu"
+ bl_label = "World Scale UV"
+ bl_description = ""
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ layout.operator(world_scale_uv.MUV_OT_WorldScaleUV_Measure.bl_idname,
+ text="Measure")
+
+ layout.operator(
+ world_scale_uv.MUV_OT_WorldScaleUV_ApplyManual.bl_idname,
+ text="Apply (Manual)")
+
+ ops = layout.operator(
+ world_scale_uv.MUV_OT_WorldScaleUV_ApplyScalingDensity.bl_idname,
+ text="Apply (Same Desity)")
+ ops.src_density = sc.muv_world_scale_uv_src_density
+ ops.same_density = True
+
+ ops = layout.operator(
+ world_scale_uv.MUV_OT_WorldScaleUV_ApplyScalingDensity.bl_idname,
+ text="Apply (Scaling Desity)")
+ ops.src_density = sc.muv_world_scale_uv_src_density
+ ops.same_density = False
+ ops.tgt_scaling_factor = sc.muv_world_scale_uv_tgt_scaling_factor
+
+ ops = layout.operator(
+ MUV_OT_WorldScaleUV_ApplyProportionalToMesh.bl_idname,
+ text="Apply (Proportional to Mesh)")
+ ops.src_density = sc.muv_world_scale_uv_src_density
+ ops.src_uv_area = sc.muv_world_scale_uv_src_uv_area
+ ops.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area
+ ops.origin = sc.muv_world_scale_uv_origin
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_TextureWrap(bpy.types.Menu):
+ """
+ Menu class: Master menu of Texture Wrap
+ """
+
+ bl_idname = "uv.muv_texture_wrap_menu"
+ bl_label = "Texture Wrap"
+ bl_description = ""
+
+ def draw(self, _):
+ layout = self.layout
+
+ layout.operator(texture_wrap.MUV_OT_TextureWrap_Refer.bl_idname,
+ text="Refer")
+ layout.operator(texture_wrap.MUV_OT_TextureWrap_Set.bl_idname,
+ text="Set")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_UVW(bpy.types.Menu):
+ """
+ Menu class: Master menu of UVW
+ """
+
+ bl_idname = "uv.muv_uvw_menu"
+ bl_label = "UVW"
+ bl_description = ""
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ ops = layout.operator(uvw.MUV_OT_UVW_BoxMap.bl_idname, text="Box")
+ ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+
+ ops = layout.operator(uvw.MUV_OT_UVW_BestPlanerMap.bl_idname,
+ text="Best Planner")
+ ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_TextureProjection(bpy.types.Menu):
+ """
+ Menu class: Master menu of Texture Projection
+ """
+
+ bl_idname = "uv.muv_texture_projection_menu"
+ bl_label = "Texture Projection"
+ bl_description = ""
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ layout.prop(sc, "muv_texture_projection_enable",
+ text="Texture Projection")
+ layout.operator(
+ texture_projection.MUV_OT_TextureProjection_Project.bl_idname,
+ text="Project")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_PreserveUVAspect(bpy.types.Menu):
+ """
+ Menu class: Master menu of Preserve UV Aspect
+ """
+
+ bl_idname = "uv.muv_preserve_uv_aspect_menu"
+ bl_label = "Preserve UV Aspect"
+ bl_description = ""
+
+ def draw(self, context):
+ layout = self.layout
+ sc = context.scene
+
+ for key in bpy.data.images.keys():
+ ops = layout.operator(
+ preserve_uv_aspect.MUV_OT_PreserveUVAspect.bl_idname, text=key)
+ ops.dest_img_name = key
+ ops.origin = sc.muv_preserve_uv_aspect_origin
diff --git a/uv_magic_uv/legacy/ui/__init__.py b/uv_magic_uv/legacy/ui/__init__.py
new file mode 100644
index 00000000..bf790a8e
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/__init__.py
@@ -0,0 +1,50 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+if "bpy" in locals():
+ import importlib
+ importlib.reload(view3d_copy_paste_uv_objectmode)
+ importlib.reload(view3d_copy_paste_uv_editmode)
+ importlib.reload(view3d_uv_manipulation)
+ importlib.reload(view3d_uv_mapping)
+ importlib.reload(uvedit_copy_paste_uv)
+ importlib.reload(uvedit_uv_manipulation)
+ importlib.reload(uvedit_editor_enhancement)
+ importlib.reload(VIEW3D_MT_uv_map)
+ importlib.reload(VIEW3D_MT_object)
+ importlib.reload(IMAGE_MT_uvs)
+else:
+ from . import view3d_copy_paste_uv_objectmode
+ from . import view3d_copy_paste_uv_editmode
+ from . import view3d_uv_manipulation
+ from . import view3d_uv_mapping
+ from . import uvedit_copy_paste_uv
+ from . import uvedit_uv_manipulation
+ from . import uvedit_editor_enhancement
+ from . import VIEW3D_MT_uv_map
+ from . import VIEW3D_MT_object
+ from . import IMAGE_MT_uvs
+
+import bpy \ No newline at end of file
diff --git a/uv_magic_uv/legacy/ui/uvedit_copy_paste_uv.py b/uv_magic_uv/legacy/ui/uvedit_copy_paste_uv.py
new file mode 100644
index 00000000..9848f03b
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/uvedit_copy_paste_uv.py
@@ -0,0 +1,62 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_uvedit
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'MUV_PT_UVEdit_CopyPasteUV',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_UVEdit_CopyPasteUV(bpy.types.Panel):
+ """
+ Panel class: Copy/Paste UV on Property Panel on UV/ImageEditor
+ """
+
+ bl_space_type = 'IMAGE_EDITOR'
+ bl_region_type = 'TOOLS'
+ bl_label = "Copy/Paste UV"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, _):
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.operator(
+ copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_CopyUV.bl_idname,
+ text="Copy")
+ row.operator(
+ copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_PasteUV.bl_idname,
+ text="Paste")
diff --git a/uv_magic_uv/ui/uvedit_editor_enhancement.py b/uv_magic_uv/legacy/ui/uvedit_editor_enhancement.py
index 9cde0cad..3f750feb 100644
--- a/uv_magic_uv/ui/uvedit_editor_enhancement.py
+++ b/uv_magic_uv/legacy/ui/uvedit_editor_enhancement.py
@@ -25,17 +25,20 @@ __date__ = "17 Nov 2018"
import bpy
-from ..op import align_uv_cursor
-from ..op import uv_bounding_box
-from ..op import uv_inspection
-
+from ..op import (
+ align_uv_cursor,
+ uv_bounding_box,
+ uv_inspection,
+)
+from ...utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'PanelEditorEnhancement',
+ 'MUV_PT_UVEdit_EditorEnhancement',
]
-class PanelEditorEnhancement(bpy.types.Panel):
+@BlClassRegistry(legacy=True)
+class MUV_PT_UVEdit_EditorEnhancement(bpy.types.Panel):
"""
Panel class: UV/Image Editor Enhancement
"""
@@ -63,43 +66,43 @@ class PanelEditorEnhancement(bpy.types.Panel):
col = box.column(align=True)
row = col.row(align=True)
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Left Top")
ops.position = 'LEFT_TOP'
ops.base = sc.muv_align_uv_cursor_align_method
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Middle Top")
ops.position = 'MIDDLE_TOP'
ops.base = sc.muv_align_uv_cursor_align_method
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Right Top")
ops.position = 'RIGHT_TOP'
ops.base = sc.muv_align_uv_cursor_align_method
row = col.row(align=True)
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Left Middle")
ops.position = 'LEFT_MIDDLE'
ops.base = sc.muv_align_uv_cursor_align_method
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Center")
ops.position = 'CENTER'
ops.base = sc.muv_align_uv_cursor_align_method
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Right Middle")
ops.position = 'RIGHT_MIDDLE'
ops.base = sc.muv_align_uv_cursor_align_method
row = col.row(align=True)
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Left Bottom")
ops.position = 'LEFT_BOTTOM'
ops.base = sc.muv_align_uv_cursor_align_method
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Middle Bottom")
ops.position = 'MIDDLE_BOTTOM'
ops.base = sc.muv_align_uv_cursor_align_method
- ops = row.operator(align_uv_cursor.Operator.bl_idname,
+ ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
text="Right Bottom")
ops.position = 'RIGHT_BOTTOM'
ops.base = sc.muv_align_uv_cursor_align_method
@@ -113,13 +116,14 @@ class PanelEditorEnhancement(bpy.types.Panel):
box = layout.box()
box.prop(sc, "muv_uv_bounding_box_enabled", text="UV Bounding Box")
if sc.muv_uv_bounding_box_enabled:
- box.prop(sc, "muv_uv_bounding_box_show",
- text="Hide"
- if uv_bounding_box.Operator.is_running(context)
- else "Show",
- icon='RESTRICT_VIEW_OFF'
- if uv_bounding_box.Operator.is_running(context)
- else 'RESTRICT_VIEW_ON')
+ box.prop(
+ sc, "muv_uv_bounding_box_show",
+ text="Hide"
+ if uv_bounding_box.MUV_OT_UVBoundingBox.is_running(context)
+ else "Show",
+ icon='RESTRICT_VIEW_OFF'
+ if uv_bounding_box.MUV_OT_UVBoundingBox.is_running(context)
+ else 'RESTRICT_VIEW_ON')
box.prop(sc, "muv_uv_bounding_box_uniform_scaling",
text="Uniform Scaling")
box.prop(sc, "muv_uv_bounding_box_boundary", text="Boundary")
@@ -128,14 +132,15 @@ class PanelEditorEnhancement(bpy.types.Panel):
box.prop(sc, "muv_uv_inspection_enabled", text="UV Inspection")
if sc.muv_uv_inspection_enabled:
row = box.row()
- row.prop(sc, "muv_uv_inspection_show",
- text="Hide"
- if uv_inspection.OperatorRender.is_running(context)
- else "Show",
- icon='RESTRICT_VIEW_OFF'
- if uv_inspection.OperatorRender.is_running(context)
- else 'RESTRICT_VIEW_ON')
- row.operator(uv_inspection.OperatorUpdate.bl_idname,
+ row.prop(
+ sc, "muv_uv_inspection_show",
+ text="Hide"
+ if uv_inspection.MUV_OT_UVInspection_Render.is_running(context)
+ else "Show",
+ icon='RESTRICT_VIEW_OFF'
+ if uv_inspection.MUV_OT_UVInspection_Render.is_running(context)
+ else 'RESTRICT_VIEW_ON')
+ row.operator(uv_inspection.MUV_OT_UVInspection_Update.bl_idname,
text="Update")
row = box.row()
row.prop(sc, "muv_uv_inspection_show_overlapped")
diff --git a/uv_magic_uv/ui/uvedit_uv_manipulation.py b/uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py
index 0f6a105e..96cf17d3 100644
--- a/uv_magic_uv/ui/uvedit_uv_manipulation.py
+++ b/uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py
@@ -25,18 +25,21 @@ __date__ = "17 Nov 2018"
import bpy
-from ..op import align_uv
-from ..op import smooth_uv
-from ..op import pack_uv
-from ..op import select_uv
-
+from ..op import (
+ align_uv,
+ smooth_uv,
+ pack_uv,
+ select_uv,
+)
+from ...utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'MenuUVManipulation',
+ 'MUV_PT_UVEdit_UVManipulation',
]
-class MenuUVManipulation(bpy.types.Panel):
+@BlClassRegistry(legacy=True)
+class MUV_PT_UVEdit_UVManipulation(bpy.types.Panel):
"""
Panel class: UV Manipulation on Property Panel on UV/ImageEditor
"""
@@ -61,11 +64,11 @@ class MenuUVManipulation(bpy.types.Panel):
if sc.muv_align_uv_enabled:
col = box.column()
row = col.row(align=True)
- ops = row.operator(align_uv.OperatorCircle.bl_idname,
+ ops = row.operator(align_uv.MUV_OT_AlignUV_Circle.bl_idname,
text="Circle")
ops.transmission = sc.muv_align_uv_transmission
ops.select = sc.muv_align_uv_select
- ops = row.operator(align_uv.OperatorStraighten.bl_idname,
+ ops = row.operator(align_uv.MUV_OT_AlignUV_Straighten.bl_idname,
text="Straighten")
ops.transmission = sc.muv_align_uv_transmission
ops.select = sc.muv_align_uv_select
@@ -73,7 +76,8 @@ class MenuUVManipulation(bpy.types.Panel):
ops.horizontal = sc.muv_align_uv_horizontal
ops.mesh_infl = sc.muv_align_uv_mesh_infl
row = col.row()
- ops = row.operator(align_uv.OperatorAxis.bl_idname, text="XY-axis")
+ ops = row.operator(align_uv.MUV_OT_AlignUV_Axis.bl_idname,
+ text="XY-axis")
ops.transmission = sc.muv_align_uv_transmission
ops.select = sc.muv_align_uv_select
ops.vertical = sc.muv_align_uv_vertical
@@ -94,7 +98,7 @@ class MenuUVManipulation(bpy.types.Panel):
box = layout.box()
box.prop(sc, "muv_smooth_uv_enabled", text="Smooth UV")
if sc.muv_smooth_uv_enabled:
- ops = box.operator(smooth_uv.Operator.bl_idname,
+ ops = box.operator(smooth_uv.MUV_OT_SmoothUV.bl_idname,
text="Smooth")
ops.transmission = sc.muv_smooth_uv_transmission
ops.select = sc.muv_smooth_uv_select
@@ -109,13 +113,13 @@ class MenuUVManipulation(bpy.types.Panel):
box.prop(sc, "muv_select_uv_enabled", text="Select UV")
if sc.muv_select_uv_enabled:
row = box.row(align=True)
- row.operator(select_uv.OperatorSelectOverlapped.bl_idname)
- row.operator(select_uv.OperatorSelectFlipped.bl_idname)
+ row.operator(select_uv.MUV_OT_SelectUV_SelectOverlapped.bl_idname)
+ row.operator(select_uv.MUV_OT_SelectUV_SelectFlipped.bl_idname)
box = layout.box()
box.prop(sc, "muv_pack_uv_enabled", text="Pack UV (Extension)")
if sc.muv_pack_uv_enabled:
- ops = box.operator(pack_uv.Operator.bl_idname, text="Pack UV")
+ ops = box.operator(pack_uv.MUV_OT_PackUV.bl_idname, text="Pack UV")
ops.allowable_center_deviation = \
sc.muv_pack_uv_allowable_center_deviation
ops.allowable_size_deviation = \
diff --git a/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_editmode.py b/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_editmode.py
new file mode 100644
index 00000000..ee2713b2
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_editmode.py
@@ -0,0 +1,93 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import (
+ transfer_uv,
+ copy_paste_uv,
+)
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'MUV_PT_View3D_Edit_CopyPasteUV',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_View3D_Edit_CopyPasteUV(bpy.types.Panel):
+ """
+ Panel class: Copy/Paste UV on Property Panel on View3D
+ """
+
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'TOOLS'
+ bl_label = "Copy/Paste UV"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ layout = self.layout
+
+ box = layout.box()
+ box.prop(sc, "muv_copy_paste_uv_enabled", text="Copy/Paste UV")
+ if sc.muv_copy_paste_uv_enabled:
+ row = box.row(align=True)
+ if sc.muv_copy_paste_uv_mode == 'DEFAULT':
+ row.menu(copy_paste_uv.MUV_MT_CopyPasteUV_CopyUV.bl_idname,
+ text="Copy")
+ row.menu(copy_paste_uv.MUV_MT_CopyPasteUV_PasteUV.bl_idname,
+ text="Paste")
+ elif sc.muv_copy_paste_uv_mode == 'SEL_SEQ':
+ row.menu(
+ copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+ text="Copy")
+ row.menu(
+ copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+ text="Paste")
+ box.prop(sc, "muv_copy_paste_uv_mode", expand=True)
+ box.prop(sc, "muv_copy_paste_uv_copy_seams", text="Seams")
+ box.prop(sc, "muv_copy_paste_uv_strategy", text="Strategy")
+
+ box = layout.box()
+ box.prop(sc, "muv_transfer_uv_enabled", text="Transfer UV")
+ if sc.muv_transfer_uv_enabled:
+ row = box.row(align=True)
+ row.operator(transfer_uv.MUV_OT_TransferUV_CopyUV.bl_idname,
+ text="Copy")
+ ops = row.operator(transfer_uv.MUV_OT_TransferUV_PasteUV.bl_idname,
+ text="Paste")
+ ops.invert_normals = sc.muv_transfer_uv_invert_normals
+ ops.copy_seams = sc.muv_transfer_uv_copy_seams
+ row = box.row()
+ row.prop(sc, "muv_transfer_uv_invert_normals",
+ text="Invert Normals")
+ row.prop(sc, "muv_transfer_uv_copy_seams", text="Seams")
diff --git a/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_objectmode.py b/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_objectmode.py
new file mode 100644
index 00000000..58031b0f
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_objectmode.py
@@ -0,0 +1,65 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_object
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'MUV_PT_View3D_Object_CopyPasteUV',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_View3D_Object_CopyPasteUV(bpy.types.Panel):
+ """
+ Panel class: Copy/Paste UV on Property Panel on View3D
+ """
+
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'TOOLS'
+ bl_label = "Copy/Paste UV"
+ bl_category = "Magic UV"
+ bl_context = 'objectmode'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.menu(
+ copy_paste_uv_object.MUV_MT_CopyPasteUVObject_CopyUV.bl_idname,
+ text="Copy")
+ row.menu(
+ copy_paste_uv_object.MUV_MT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="Paste")
+ layout.prop(sc, "muv_copy_paste_uv_object_copy_seams",
+ text="Seams")
diff --git a/uv_magic_uv/legacy/ui/view3d_uv_manipulation.py b/uv_magic_uv/legacy/ui/view3d_uv_manipulation.py
new file mode 100644
index 00000000..7d828a38
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/view3d_uv_manipulation.py
@@ -0,0 +1,289 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import (
+ move_uv,
+ flip_rotate_uv,
+ mirror_uv,
+ preserve_uv_aspect,
+ texture_lock,
+ texture_wrap,
+ uv_sculpt,
+ world_scale_uv,
+)
+from ..op.world_scale_uv import (
+ MUV_OT_WorldScaleUV_ApplyProportionalToMesh,
+ MUV_OT_WorldScaleUV_ApplyScalingDensity
+)
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'MUV_PT_View3D_UVManipulation',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_View3D_UVManipulation(bpy.types.Panel):
+ """
+ Panel class: UV Manipulation on Property Panel on View3D
+ """
+
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'TOOLS'
+ bl_label = "UV Manipulation"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ layout = self.layout
+
+ box = layout.box()
+ box.prop(sc, "muv_flip_rotate_uv_enabled", text="Flip/Rotate UV")
+ if sc.muv_flip_rotate_uv_enabled:
+ row = box.row()
+ ops = row.operator(flip_rotate_uv.MUV_OT_FlipRotate.bl_idname,
+ text="Flip/Rotate")
+ ops.seams = sc.muv_flip_rotate_uv_seams
+ row.prop(sc, "muv_flip_rotate_uv_seams", text="Seams")
+
+ box = layout.box()
+ box.prop(sc, "muv_mirror_uv_enabled", text="Mirror UV")
+ if sc.muv_mirror_uv_enabled:
+ row = box.row()
+ ops = row.operator(mirror_uv.MUV_OT_MirrorUV.bl_idname,
+ text="Mirror")
+ ops.axis = sc.muv_mirror_uv_axis
+ row.prop(sc, "muv_mirror_uv_axis", text="")
+
+ box = layout.box()
+ box.prop(sc, "muv_move_uv_enabled", text="Move UV")
+ if sc.muv_move_uv_enabled:
+ col = box.column()
+ if not move_uv.MUV_OT_MoveUV.is_running(context):
+ col.operator(move_uv.MUV_OT_MoveUV.bl_idname, icon='PLAY',
+ text="Start")
+ else:
+ col.operator(move_uv.MUV_OT_MoveUV.bl_idname, icon='PAUSE',
+ text="Stop")
+
+ box = layout.box()
+ box.prop(sc, "muv_world_scale_uv_enabled", text="World Scale UV")
+ if sc.muv_world_scale_uv_enabled:
+ box.prop(sc, "muv_world_scale_uv_mode", text="")
+
+ if sc.muv_world_scale_uv_mode == 'MANUAL':
+ sp = box.split(percentage=0.5)
+ col = sp.column()
+ col.prop(sc, "muv_world_scale_uv_tgt_texture_size",
+ text="Texture Size")
+ sp = sp.split(percentage=1.0)
+ col = sp.column()
+ col.label("Density:")
+ col.prop(sc, "muv_world_scale_uv_tgt_density", text="")
+ box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
+ ops = box.operator(
+ world_scale_uv.MUV_OT_WorldScaleUV_ApplyManual.bl_idname,
+ text="Apply")
+ ops.tgt_density = sc.muv_world_scale_uv_tgt_density
+ ops.tgt_texture_size = sc.muv_world_scale_uv_tgt_texture_size
+ ops.origin = sc.muv_world_scale_uv_origin
+ ops.show_dialog = False
+
+ elif sc.muv_world_scale_uv_mode == 'SAME_DENSITY':
+ sp = box.split(percentage=0.4)
+ col = sp.column(align=True)
+ col.label("Source:")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.operator(
+ world_scale_uv.MUV_OT_WorldScaleUV_Measure.bl_idname,
+ text="Measure")
+
+ sp = box.split(percentage=0.7)
+ col = sp.column(align=True)
+ col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
+ col.enabled = False
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("px2/cm2")
+
+ box.separator()
+ box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
+ ops = box.operator(
+ MUV_OT_WorldScaleUV_ApplyScalingDensity.bl_idname,
+ text="Apply")
+ ops.src_density = sc.muv_world_scale_uv_src_density
+ ops.origin = sc.muv_world_scale_uv_origin
+ ops.same_density = True
+ ops.show_dialog = False
+
+ elif sc.muv_world_scale_uv_mode == 'SCALING_DENSITY':
+ sp = box.split(percentage=0.4)
+ col = sp.column(align=True)
+ col.label("Source:")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.operator(
+ world_scale_uv.MUV_OT_WorldScaleUV_Measure.bl_idname,
+ text="Measure")
+
+ sp = box.split(percentage=0.7)
+ col = sp.column(align=True)
+ col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
+ col.enabled = False
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("px2/cm2")
+
+ box.separator()
+ box.prop(sc, "muv_world_scale_uv_tgt_scaling_factor",
+ text="Scaling Factor")
+ box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
+ ops = box.operator(
+ MUV_OT_WorldScaleUV_ApplyScalingDensity.bl_idname,
+ text="Apply")
+ ops.src_density = sc.muv_world_scale_uv_src_density
+ ops.origin = sc.muv_world_scale_uv_origin
+ ops.same_density = False
+ ops.show_dialog = False
+ ops.tgt_scaling_factor = \
+ sc.muv_world_scale_uv_tgt_scaling_factor
+
+ elif sc.muv_world_scale_uv_mode == 'PROPORTIONAL_TO_MESH':
+ sp = box.split(percentage=0.4)
+ col = sp.column(align=True)
+ col.label("Source:")
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.operator(
+ world_scale_uv.MUV_OT_WorldScaleUV_Measure.bl_idname,
+ text="Measure")
+
+ sp = box.split(percentage=0.7)
+ col = sp.column(align=True)
+ col.prop(sc, "muv_world_scale_uv_src_mesh_area",
+ text="Mesh Area")
+ col.prop(sc, "muv_world_scale_uv_src_uv_area", text="UV Area")
+ col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
+ col.enabled = False
+ sp = sp.split(percentage=1.0)
+ col = sp.column(align=True)
+ col.label("cm2")
+ col.label("px2")
+ col.label("px2/cm2")
+ col.enabled = False
+
+ box.separator()
+ box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
+ ops = box.operator(
+ MUV_OT_WorldScaleUV_ApplyProportionalToMesh.bl_idname,
+ text="Apply")
+ ops.src_density = sc.muv_world_scale_uv_src_density
+ ops.src_uv_area = sc.muv_world_scale_uv_src_uv_area
+ ops.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area
+ ops.origin = sc.muv_world_scale_uv_origin
+ ops.show_dialog = False
+
+ box = layout.box()
+ box.prop(sc, "muv_preserve_uv_aspect_enabled",
+ text="Preserve UV Aspect")
+ if sc.muv_preserve_uv_aspect_enabled:
+ row = box.row()
+ ops = row.operator(
+ preserve_uv_aspect.MUV_OT_PreserveUVAspect.bl_idname,
+ text="Change Image")
+ ops.dest_img_name = sc.muv_preserve_uv_aspect_tex_image
+ ops.origin = sc.muv_preserve_uv_aspect_origin
+ row.prop(sc, "muv_preserve_uv_aspect_tex_image", text="")
+ box.prop(sc, "muv_preserve_uv_aspect_origin", text="Origin")
+
+ box = layout.box()
+ box.prop(sc, "muv_texture_lock_enabled", text="Texture Lock")
+ if sc.muv_texture_lock_enabled:
+ row = box.row(align=True)
+ col = row.column(align=True)
+ col.label("Normal Mode:")
+ col = row.column(align=True)
+ col.operator(
+ texture_lock.MUV_OT_TextureLock_Lock.bl_idname,
+ text="Lock"
+ if not texture_lock.MUV_OT_TextureLock_Lock.is_ready(context)
+ else "ReLock")
+ ops = col.operator(
+ texture_lock.MUV_OT_TextureLock_Unlock.bl_idname,
+ text="Unlock")
+ ops.connect = sc.muv_texture_lock_connect
+ col.prop(sc, "muv_texture_lock_connect", text="Connect")
+
+ row = box.row(align=True)
+ row.label("Interactive Mode:")
+ box.prop(
+ sc, "muv_texture_lock_lock",
+ text="Unlock"
+ if texture_lock.MUV_OT_TextureLock_Intr.is_running(context)
+ else "Lock",
+ icon='RESTRICT_VIEW_OFF'
+ if texture_lock.MUV_OT_TextureLock_Intr.is_running(context)
+ else 'RESTRICT_VIEW_ON')
+
+ box = layout.box()
+ box.prop(sc, "muv_texture_wrap_enabled", text="Texture Wrap")
+ if sc.muv_texture_wrap_enabled:
+ row = box.row(align=True)
+ row.operator(texture_wrap.MUV_OT_TextureWrap_Refer.bl_idname,
+ text="Refer")
+ row.operator(texture_wrap.MUV_OT_TextureWrap_Set.bl_idname,
+ text="Set")
+ box.prop(sc, "muv_texture_wrap_set_and_refer")
+ box.prop(sc, "muv_texture_wrap_selseq")
+
+ box = layout.box()
+ box.prop(sc, "muv_uv_sculpt_enabled", text="UV Sculpt")
+ if sc.muv_uv_sculpt_enabled:
+ box.prop(
+ sc, "muv_uv_sculpt_enable",
+ text="Disable"if uv_sculpt.MUV_OT_UVSculpt.is_running(context)
+ else "Enable",
+ icon='RESTRICT_VIEW_OFF'
+ if uv_sculpt.MUV_OT_UVSculpt.is_running(context)
+ else 'RESTRICT_VIEW_ON')
+ col = box.column()
+ col.label("Brush:")
+ col.prop(sc, "muv_uv_sculpt_radius")
+ col.prop(sc, "muv_uv_sculpt_strength")
+ box.prop(sc, "muv_uv_sculpt_tools")
+ if sc.muv_uv_sculpt_tools == 'PINCH':
+ box.prop(sc, "muv_uv_sculpt_pinch_invert")
+ elif sc.muv_uv_sculpt_tools == 'RELAX':
+ box.prop(sc, "muv_uv_sculpt_relax_method")
+ box.prop(sc, "muv_uv_sculpt_show_brush")
diff --git a/uv_magic_uv/legacy/ui/view3d_uv_mapping.py b/uv_magic_uv/legacy/ui/view3d_uv_mapping.py
new file mode 100644
index 00000000..3de86d0d
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/view3d_uv_mapping.py
@@ -0,0 +1,116 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import (
+ uvw,
+ texture_projection,
+ unwrap_constraint,
+)
+from ..op.texture_projection import (
+ MUV_OT_TextureProjection
+)
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+ 'MUV_PT_View3D_UVMapping',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_View3D_UVMapping(bpy.types.Panel):
+ """
+ Panel class: UV Mapping on Property Panel on View3D
+ """
+
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'TOOLS'
+ bl_label = "UV Mapping"
+ bl_category = "Magic UV"
+ bl_context = 'mesh_edit'
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, _):
+ layout = self.layout
+ layout.label(text="", icon='IMAGE_COL')
+
+ def draw(self, context):
+ sc = context.scene
+ layout = self.layout
+
+ box = layout.box()
+ box.prop(sc, "muv_unwrap_constraint_enabled", text="Unwrap Constraint")
+ if sc.muv_unwrap_constraint_enabled:
+ ops = box.operator(
+ unwrap_constraint.MUV_OT_UnwrapConstraint.bl_idname,
+ text="Unwrap")
+ ops.u_const = sc.muv_unwrap_constraint_u_const
+ ops.v_const = sc.muv_unwrap_constraint_v_const
+ row = box.row(align=True)
+ row.prop(sc, "muv_unwrap_constraint_u_const", text="U-Constraint")
+ row.prop(sc, "muv_unwrap_constraint_v_const", text="V-Constraint")
+
+ box = layout.box()
+ box.prop(sc, "muv_texture_projection_enabled",
+ text="Texture Projection")
+ if sc.muv_texture_projection_enabled:
+ row = box.row()
+ row.prop(
+ sc, "muv_texture_projection_enable",
+ text="Disable"
+ if MUV_OT_TextureProjection.is_running(context)
+ else "Enable",
+ icon='RESTRICT_VIEW_OFF'
+ if MUV_OT_TextureProjection.is_running(context)
+ else 'RESTRICT_VIEW_ON')
+ row.prop(sc, "muv_texture_projection_tex_image", text="")
+ box.prop(sc, "muv_texture_projection_tex_transparency",
+ text="Transparency")
+ col = box.column(align=True)
+ row = col.row()
+ row.prop(sc, "muv_texture_projection_adjust_window",
+ text="Adjust Window")
+ if not sc.muv_texture_projection_adjust_window:
+ row.prop(sc, "muv_texture_projection_tex_magnitude",
+ text="Magnitude")
+ col.prop(sc, "muv_texture_projection_apply_tex_aspect",
+ text="Texture Aspect Ratio")
+ col.prop(sc, "muv_texture_projection_assign_uvmap",
+ text="Assign UVMap")
+ box.operator(
+ texture_projection.MUV_OT_TextureProjection_Project.bl_idname,
+ text="Project")
+
+ box = layout.box()
+ box.prop(sc, "muv_uvw_enabled", text="UVW")
+ if sc.muv_uvw_enabled:
+ row = box.row(align=True)
+ ops = row.operator(uvw.MUV_OT_UVW_BoxMap.bl_idname, text="Box")
+ ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+ ops = row.operator(uvw.MUV_OT_UVW_BestPlanerMap.bl_idname,
+ text="Best Planner")
+ ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+ box.prop(sc, "muv_uvw_assign_uvmap", text="Assign UVMap")
diff --git a/uv_magic_uv/op/__init__.py b/uv_magic_uv/op/__init__.py
index 9535b76d..2142c157 100644
--- a/uv_magic_uv/op/__init__.py
+++ b/uv_magic_uv/op/__init__.py
@@ -25,50 +25,22 @@ __date__ = "17 Nov 2018"
if "bpy" in locals():
import importlib
- importlib.reload(align_uv)
- importlib.reload(align_uv_cursor)
importlib.reload(copy_paste_uv)
importlib.reload(copy_paste_uv_object)
importlib.reload(copy_paste_uv_uvedit)
importlib.reload(flip_rotate_uv)
importlib.reload(mirror_uv)
importlib.reload(move_uv)
- importlib.reload(pack_uv)
- importlib.reload(preserve_uv_aspect)
- importlib.reload(select_uv)
- importlib.reload(smooth_uv)
- importlib.reload(texture_lock)
- importlib.reload(texture_projection)
- importlib.reload(texture_wrap)
importlib.reload(transfer_uv)
- importlib.reload(unwrap_constraint)
- importlib.reload(uv_bounding_box)
- importlib.reload(uv_inspection)
- importlib.reload(uv_sculpt)
importlib.reload(uvw)
- importlib.reload(world_scale_uv)
else:
- from . import align_uv
- from . import align_uv_cursor
from . import copy_paste_uv
from . import copy_paste_uv_object
from . import copy_paste_uv_uvedit
from . import flip_rotate_uv
from . import mirror_uv
from . import move_uv
- from . import pack_uv
- from . import preserve_uv_aspect
- from . import select_uv
- from . import smooth_uv
- from . import texture_lock
- from . import texture_projection
- from . import texture_wrap
from . import transfer_uv
- from . import unwrap_constraint
- from . import uv_bounding_box
- from . import uv_inspection
- from . import uv_sculpt
from . import uvw
- from . import world_scale_uv
import bpy
diff --git a/uv_magic_uv/op/copy_paste_uv.py b/uv_magic_uv/op/copy_paste_uv.py
index cc1baa30..23bc8343 100644
--- a/uv_magic_uv/op/copy_paste_uv.py
+++ b/uv_magic_uv/op/copy_paste_uv.py
@@ -18,14 +18,14 @@
#
# ##### END GPL LICENSE BLOCK #####
-__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
__status__ = "production"
__version__ = "5.2"
__date__ = "17 Nov 2018"
-import bpy
import bmesh
+import bpy.utils
from bpy.props import (
StringProperty,
BoolProperty,
@@ -33,151 +33,28 @@ from bpy.props import (
EnumProperty,
)
+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',
- 'OpeartorCopyUV',
- 'MenuCopyUV',
- 'OperatorPasteUV',
- 'MenuPasteUV',
- 'OperatorSelSeqCopyUV',
- 'MenuSelSeqCopyUV',
- 'OperatorSelSeqPasteUV',
- 'MenuSelSeqPasteUV',
+ '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',
]
-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
-
-
+@PropertyClassRegistry()
class Properties:
+ idname = "copy_paste_uv"
+
@classmethod
def init_props(cls, scene):
class Props():
@@ -225,7 +102,8 @@ class Properties:
del scene.muv_copy_paste_uv_strategy
-class OpeartorCopyUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUV_CopyUV(bpy.types.Operator):
"""
Operation class: Copy UV coordinate
"""
@@ -235,14 +113,14 @@ class OpeartorCopyUV(bpy.types.Operator):
bl_description = "Copy UV coordinate"
bl_options = {'REGISTER', 'UNDO'}
- uv_map = StringProperty(default="__default", 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)
+ return impl.is_valid_context(context)
def execute(self, context):
props = context.scene.muv_props.copy_paste_uv
@@ -250,34 +128,24 @@ class OpeartorCopyUV(bpy.types.Operator):
bm = common.create_bmesh(obj)
# get UV layer
- uv_layers = get_copy_uv_layers(self, bm)
+ uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
if not uv_layers:
return {'CANCELLED'}
# get selected face
- 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])
+ src_info = impl.get_src_face_info(self, bm, uv_layers)
+ if src_info is None:
+ return {'CANCELLED'}
+ 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 MenuCopyUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_CopyUV(bpy.types.Menu):
"""
Menu class: Copy UV coordinate
"""
@@ -288,7 +156,7 @@ class MenuCopyUV(bpy.types.Menu):
@classmethod
def poll(cls, context):
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def draw(self, context):
layout = self.layout
@@ -297,18 +165,21 @@ class MenuCopyUV(bpy.types.Menu):
bm = common.create_bmesh(obj)
uv_maps = bm.loops.layers.uv.keys()
- ops = layout.operator(OpeartorCopyUV.bl_idname, text="[Default]")
+ ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname,
+ text="[Default]")
ops.uv_map = "__default"
- ops = layout.operator(OpeartorCopyUV.bl_idname, text="[All]")
+ ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname,
+ text="[All]")
ops.uv_map = "__all"
for m in uv_maps:
- ops = layout.operator(OpeartorCopyUV.bl_idname, text=m)
+ ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname, text=m)
ops.uv_map = m
-class OperatorPasteUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator):
"""
Operation class: Paste UV coordinate
"""
@@ -318,8 +189,8 @@ class OperatorPasteUV(bpy.types.Operator):
bl_description = "Paste UV coordinate"
bl_options = {'REGISTER', 'UNDO'}
- uv_map = StringProperty(default="__default", options={'HIDDEN'})
- strategy = EnumProperty(
+ uv_map: StringProperty(default="__default", options={'HIDDEN'})
+ strategy: EnumProperty(
name="Strategy",
description="Paste Strategy",
items=[
@@ -328,18 +199,18 @@ class OperatorPasteUV(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(
+ copy_seams: BoolProperty(
name="Seams",
description="Copy Seams",
default=True
@@ -354,7 +225,7 @@ class OperatorPasteUV(bpy.types.Operator):
props = sc.muv_props.copy_paste_uv
if not props.src_info:
return False
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def execute(self, context):
props = context.scene.muv_props.copy_paste_uv
@@ -365,53 +236,34 @@ class OperatorPasteUV(bpy.types.Operator):
bm = common.create_bmesh(obj)
# get UV layer
- uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info)
+ 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_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'},
- "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
+ dest_info = impl.get_dest_face_info(self, bm, uv_layers,
+ props.src_info, self.strategy)
+ if dest_info is None:
+ return {'CANCELLED'}
# 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)
+ 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'}
- self.report({'INFO'}, "{} face(s) are pasted".format(dest_face_count))
+ 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 MenuPasteUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_PasteUV(bpy.types.Menu):
"""
Menu class: Paste UV coordinate
"""
@@ -426,7 +278,7 @@ class MenuPasteUV(bpy.types.Menu):
props = sc.muv_props.copy_paste_uv
if not props.src_info:
return False
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def draw(self, context):
sc = context.scene
@@ -436,29 +288,33 @@ class MenuPasteUV(bpy.types.Menu):
bm = common.create_bmesh(obj)
uv_maps = bm.loops.layers.uv.keys()
- ops = layout.operator(OperatorPasteUV.bl_idname, text="[Default]")
+ 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(OperatorPasteUV.bl_idname, text="[New]")
+ 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(OperatorPasteUV.bl_idname, text="[All]")
+ 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(OperatorPasteUV.bl_idname, text=m)
+ ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.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 OperatorSelSeqCopyUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUV_SelSeqCopyUV(bpy.types.Operator):
"""
Operation class: Copy UV coordinate by selection sequence
"""
@@ -468,14 +324,14 @@ class OperatorSelSeqCopyUV(bpy.types.Operator):
bl_description = "Copy UV data by selection sequence"
bl_options = {'REGISTER', 'UNDO'}
- uv_map = StringProperty(default="__default", 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)
+ return impl.is_valid_context(context)
def execute(self, context):
props = context.scene.muv_props.copy_paste_uv_selseq
@@ -483,34 +339,24 @@ class OperatorSelSeqCopyUV(bpy.types.Operator):
bm = common.create_bmesh(obj)
# get UV layer
- uv_layers = get_copy_uv_layers(self, bm)
+ uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
if not uv_layers:
return {'CANCELLED'}
# get selected face
- 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])
+ src_info = impl.get_select_history_src_face_info(self, bm, uv_layers)
+ if src_info is None:
+ return {'CANCELLED'}
+ 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 MenuSelSeqCopyUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_SelSeqCopyUV(bpy.types.Menu):
"""
Menu class: Copy UV coordinate by selection sequence
"""
@@ -521,7 +367,7 @@ class MenuSelSeqCopyUV(bpy.types.Menu):
@classmethod
def poll(cls, context):
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def draw(self, context):
layout = self.layout
@@ -529,18 +375,22 @@ class MenuSelSeqCopyUV(bpy.types.Menu):
bm = common.create_bmesh(obj)
uv_maps = bm.loops.layers.uv.keys()
- ops = layout.operator(OperatorSelSeqCopyUV.bl_idname, text="[Default]")
+ ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+ text="[Default]")
ops.uv_map = "__default"
- ops = layout.operator(OperatorSelSeqCopyUV.bl_idname, text="[All]")
+ ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+ text="[All]")
ops.uv_map = "__all"
for m in uv_maps:
- ops = layout.operator(OperatorSelSeqCopyUV.bl_idname, text=m)
+ ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+ text=m)
ops.uv_map = m
-class OperatorSelSeqPasteUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator):
"""
Operation class: Paste UV coordinate by selection sequence
"""
@@ -550,8 +400,8 @@ class OperatorSelSeqPasteUV(bpy.types.Operator):
bl_description = "Paste UV coordinate by selection sequence"
bl_options = {'REGISTER', 'UNDO'}
- uv_map = StringProperty(default="__default", options={'HIDDEN'})
- strategy = EnumProperty(
+ uv_map: StringProperty(default="__default", options={'HIDDEN'})
+ strategy: EnumProperty(
name="Strategy",
description="Paste Strategy",
items=[
@@ -560,18 +410,18 @@ class OperatorSelSeqPasteUV(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(
+ copy_seams: BoolProperty(
name="Seams",
description="Copy Seams",
default=True
@@ -586,7 +436,7 @@ class OperatorSelSeqPasteUV(bpy.types.Operator):
props = sc.muv_props.copy_paste_uv_selseq
if not props.src_info:
return False
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def execute(self, context):
props = context.scene.muv_props.copy_paste_uv_selseq
@@ -597,53 +447,35 @@ class OperatorSelSeqPasteUV(bpy.types.Operator):
bm = common.create_bmesh(obj)
# get UV layer
- uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info)
+ 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_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
+ 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
- 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)
+ 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'}
- self.report({'INFO'}, "{} face(s) are pasted".format(dest_face_count))
+ 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 MenuSelSeqPasteUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_SelSeqPasteUV(bpy.types.Menu):
"""
Menu class: Paste UV coordinate by selection sequence
"""
@@ -658,7 +490,7 @@ class MenuSelSeqPasteUV(bpy.types.Menu):
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)
+ return impl.is_valid_context(context)
def draw(self, context):
sc = context.scene
@@ -668,24 +500,27 @@ class MenuSelSeqPasteUV(bpy.types.Menu):
bm = common.create_bmesh(obj)
uv_maps = bm.loops.layers.uv.keys()
- ops = layout.operator(OperatorSelSeqPasteUV.bl_idname,
+ ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.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(OperatorSelSeqPasteUV.bl_idname, text="[New]")
+ 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(OperatorSelSeqPasteUV.bl_idname, text="[All]")
+ 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(OperatorSelSeqPasteUV.bl_idname, text=m)
+ ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.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
diff --git a/uv_magic_uv/op/copy_paste_uv_object.py b/uv_magic_uv/op/copy_paste_uv_object.py
index eb6d87c9..d9f42447 100644
--- a/uv_magic_uv/op/copy_paste_uv_object.py
+++ b/uv_magic_uv/op/copy_paste_uv_object.py
@@ -23,27 +23,24 @@ __status__ = "production"
__version__ = "5.2"
__date__ = "17 Nov 2018"
-import bpy
import bmesh
+import bpy
from bpy.props import (
StringProperty,
BoolProperty,
)
+from ..impl import copy_paste_uv_impl as impl
from .. import common
-from .copy_paste_uv import (
- get_copy_uv_layers,
- get_paste_uv_layers,
- paste_uv
-)
-
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'OperatorCopyUV',
- 'MenuCopyUV',
- 'OperatorPasteUV',
- 'MenuPasteUV',
+ 'MUV_OT_CopyPasteUVObject_CopyUV',
+ 'MUV_MT_CopyPasteUVObject_CopyUV',
+ 'MUV_OT_CopyPasteUVObject_PasteUV',
+ 'MUV_MT_CopyPasteUVObject_PasteUV',
]
@@ -68,16 +65,10 @@ def is_valid_context(context):
return True
-def memorize_view_3d_mode(fn):
- def __memorize_view_3d_mode(self, context):
- mode_orig = bpy.context.object.mode
- result = fn(self, context)
- bpy.ops.object.mode_set(mode=mode_orig)
- return result
- return __memorize_view_3d_mode
-
-
+@PropertyClassRegistry()
class Properties:
+ idname = "copy_paste_uv_object"
+
@classmethod
def init_props(cls, scene):
class Props():
@@ -97,7 +88,17 @@ class Properties:
del scene.muv_copy_paste_uv_object_copy_seams
-class OperatorCopyUV(bpy.types.Operator):
+def memorize_view_3d_mode(fn):
+ def __memorize_view_3d_mode(self, context):
+ mode_orig = bpy.context.object.mode
+ result = fn(self, context)
+ bpy.ops.object.mode_set(mode=mode_orig)
+ return result
+ return __memorize_view_3d_mode
+
+
+@BlClassRegistry()
+class MUV_OT_CopyPasteUVObject_CopyUV(bpy.types.Operator):
"""
Operation class: Copy UV coordinate among objects
"""
@@ -107,7 +108,7 @@ class OperatorCopyUV(bpy.types.Operator):
bl_description = "Copy UV coordinate (Among Objects)"
bl_options = {'REGISTER', 'UNDO'}
- uv_map = StringProperty(default="__default", options={'HIDDEN'})
+ uv_map: StringProperty(default="__default", options={'HIDDEN'})
@classmethod
def poll(cls, context):
@@ -124,23 +125,15 @@ class OperatorCopyUV(bpy.types.Operator):
bm = common.create_bmesh(obj)
# get UV layer
- uv_layers = get_copy_uv_layers(self, bm)
+ uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
if not uv_layers:
return {'CANCELLED'}
# get selected face
- 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)
- props.src_info[layer.name] = face_info
+ src_info = impl.get_src_face_info(self, bm, uv_layers)
+ if src_info is None:
+ return {'CANCELLED'}
+ props.src_info = src_info
self.report({'INFO'},
"{}'s UV coordinates are copied".format(obj.name))
@@ -148,7 +141,8 @@ class OperatorCopyUV(bpy.types.Operator):
return {'FINISHED'}
-class MenuCopyUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUVObject_CopyUV(bpy.types.Menu):
"""
Menu class: Copy UV coordinate among objects
"""
@@ -164,20 +158,24 @@ class MenuCopyUV(bpy.types.Menu):
def draw(self, _):
layout = self.layout
# create sub menu
- uv_maps = bpy.context.active_object.data.uv_textures.keys()
+ uv_maps = bpy.context.active_object.data.uv_layers.keys()
- ops = layout.operator(OperatorCopyUV.bl_idname, text="[Default]")
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+ text="[Default]")
ops.uv_map = "__default"
- ops = layout.operator(OperatorCopyUV.bl_idname, text="[All]")
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+ text="[All]")
ops.uv_map = "__all"
for m in uv_maps:
- ops = layout.operator(OperatorCopyUV.bl_idname, text=m)
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+ text=m)
ops.uv_map = m
-class OperatorPasteUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUVObject_PasteUV(bpy.types.Operator):
"""
Operation class: Paste UV coordinate among objects
"""
@@ -187,8 +185,8 @@ class OperatorPasteUV(bpy.types.Operator):
bl_description = "Paste UV coordinate (Among Objects)"
bl_options = {'REGISTER', 'UNDO'}
- uv_map = StringProperty(default="__default", options={'HIDDEN'})
- copy_seams = BoolProperty(
+ uv_map: StringProperty(default="__default", options={'HIDDEN'})
+ copy_seams: BoolProperty(
name="Seams",
description="Copy Seams",
default=True
@@ -213,52 +211,35 @@ class OperatorPasteUV(bpy.types.Operator):
return {'CANCELLED'}
for o in bpy.data.objects:
- if not hasattr(o.data, "uv_textures") or not o.select:
+ if not hasattr(o.data, "uv_layers") or not o.select_get():
continue
bpy.ops.object.mode_set(mode='OBJECT')
- bpy.context.scene.objects.active = o
+ bpy.context.view_layer.objects.active = o
bpy.ops.object.mode_set(mode='EDIT')
obj = context.active_object
bm = common.create_bmesh(obj)
# get UV layer
- uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info)
+ 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_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)
- key = list(props.src_info.keys())[0]
- src_face_count = len(props.src_info[key])
- dest_face_count = len(face_info)
- if 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
+ dest_info = impl.get_dest_face_info(self, bm, uv_layers,
+ props.src_info, 'N_N')
+ if dest_info is None:
+ return {'CANCELLED'}
# paste
- ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers,
- 'N_N', 0, 0, self.copy_seams)
+ ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers,
+ 'N_N', 0, 0, self.copy_seams)
if ret:
return {'CANCELLED'}
bmesh.update_edit_mesh(obj.data)
- if self.copy_seams is True:
- obj.data.show_edge_seams = True
self.report(
{'INFO'}, "{}'s UV coordinates are pasted".format(obj.name))
@@ -266,7 +247,8 @@ class OperatorPasteUV(bpy.types.Operator):
return {'FINISHED'}
-class MenuPasteUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUVObject_PasteUV(bpy.types.Menu):
"""
Menu class: Paste UV coordinate among objects
"""
@@ -289,22 +271,26 @@ class MenuPasteUV(bpy.types.Menu):
# create sub menu
uv_maps = []
for obj in bpy.data.objects:
- if hasattr(obj.data, "uv_textures") and obj.select:
- uv_maps.extend(obj.data.uv_textures.keys())
+ if hasattr(obj.data, "uv_layers") and obj.select_get():
+ uv_maps.extend(obj.data.uv_layers.keys())
- ops = layout.operator(OperatorPasteUV.bl_idname, text="[Default]")
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="[Default]")
ops.uv_map = "__default"
ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
- ops = layout.operator(OperatorPasteUV.bl_idname, text="[New]")
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="[New]")
ops.uv_map = "__new"
ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
- ops = layout.operator(OperatorPasteUV.bl_idname, text="[All]")
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="[All]")
ops.uv_map = "__all"
ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
for m in uv_maps:
- ops = layout.operator(OperatorPasteUV.bl_idname, text=m)
+ ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+ text=m)
ops.uv_map = m
ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
diff --git a/uv_magic_uv/op/copy_paste_uv_uvedit.py b/uv_magic_uv/op/copy_paste_uv_uvedit.py
index e591b5f1..719687a6 100644
--- a/uv_magic_uv/op/copy_paste_uv_uvedit.py
+++ b/uv_magic_uv/op/copy_paste_uv_uvedit.py
@@ -18,52 +18,29 @@
#
# ##### END GPL LICENSE BLOCK #####
-__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "5.2"
__date__ = "17 Nov 2018"
-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
+from ..impl import copy_paste_uv_uvedit_impl as impl
__all__ = [
'Properties',
- 'OperatorCopyUV',
- 'OperatorPasteUV',
+ 'MUV_OT_CopyPasteUVUVEdit_CopyUV',
+ 'MUV_OT_CopyPasteUVUVEdit_PasteUV',
]
-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
-
- # '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
- for space in context.area.spaces:
- if (space.type == 'IMAGE_EDITOR') or (space.type == 'VIEW_3D'):
- break
- else:
- return False
-
- return True
-
-
+@PropertyClassRegistry()
class Properties:
+ idname = "copy_paste_uv_uvedit"
+
@classmethod
def init_props(cls, scene):
class Props():
@@ -76,7 +53,8 @@ class Properties:
del scene.muv_props.copy_paste_uv_uvedit
-class OperatorCopyUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUVUVEdit_CopyUV(bpy.types.Operator):
"""
Operation class: Copy UV coordinate on UV/Image Editor
"""
@@ -86,38 +64,19 @@ class OperatorCopyUV(bpy.types.Operator):
bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
bl_options = {'REGISTER', 'UNDO'}
+ def __init__(self):
+ self.__impl = impl.CopyUVImpl()
+
@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)
+ return impl.CopyUVImpl.poll(context)
def execute(self, context):
- props = context.scene.muv_props.copy_paste_uv_uvedit
- 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()
-
- 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'}
-
-
-class OperatorPasteUV(bpy.types.Operator):
+ return self.__impl.execute(self, context)
+
+
+@BlClassRegistry()
+class MUV_OT_CopyPasteUVUVEdit_PasteUV(bpy.types.Operator):
"""
Operation class: Paste UV coordinate on UV/Image Editor
"""
@@ -127,72 +86,12 @@ class OperatorPasteUV(bpy.types.Operator):
bl_description = "Paste UV coordinate (only selected in UV/Image Editor)"
bl_options = {'REGISTER', 'UNDO'}
+ def __init__(self):
+ self.__impl = impl.PasteUVImpl()
+
@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)
+ return impl.PasteUVImpl.poll(context)
def execute(self, context):
- props = context.scene.muv_props.copy_paste_uv_uvedit
- 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'}
+ return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/op/flip_rotate_uv.py b/uv_magic_uv/op/flip_rotate_uv.py
index 751bb8fb..d1637052 100644
--- a/uv_magic_uv/op/flip_rotate_uv.py
+++ b/uv_magic_uv/op/flip_rotate_uv.py
@@ -31,36 +31,20 @@ from bpy.props import (
)
from .. import common
-
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
+from ..impl import flip_rotate_impl as impl
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_FlipRotate',
]
-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
-
-
+@PropertyClassRegistry()
class Properties:
+ idname = "flip_rotate_uv"
+
@classmethod
def init_props(cls, scene):
scene.muv_flip_rotate_uv_enabled = BoolProperty(
@@ -80,7 +64,8 @@ class Properties:
del scene.muv_flip_rotate_uv_seams
-class Operator(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_FlipRotate(bpy.types.Operator):
"""
Operation class: Flip and Rotate UV coordinate
"""
@@ -90,18 +75,18 @@ class Operator(bpy.types.Operator):
bl_description = "Flip/Rotate UV coordinate"
bl_options = {'REGISTER', 'UNDO'}
- flip = BoolProperty(
+ flip: BoolProperty(
name="Flip UV",
description="Flip UV...",
default=False
)
- rotate = IntProperty(
+ rotate: IntProperty(
default=0,
name="Rotate UV",
min=0,
max=30
)
- seams = BoolProperty(
+ seams: BoolProperty(
name="Seams",
description="Seams",
default=True
@@ -112,7 +97,7 @@ class Operator(bpy.types.Operator):
# we can not get area/space/region from console
if common.is_console_mode():
return True
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def execute(self, context):
self.report({'INFO'}, "Flip/Rotate UV")
@@ -122,61 +107,24 @@ class Operator(bpy.types.Operator):
bm.faces.ensure_lookup_table()
# get UV layer
- if not bm.loops.layers.uv:
- self.report({'WARNING'}, "Object must have more than one UV map")
+ uv_layer = impl.get_uv_layer(self, bm)
+ if not uv_layer:
return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
# 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")
+ src_info = impl.get_src_face_info(self, bm, [uv_layer], True)
+ if not src_info:
return {'CANCELLED'}
- self.report({'INFO'}, "%d face(s) are selected" % len(dest_uvs))
+
+ face_count = len(src_info[list(src_info.keys())[0]])
+ self.report({'INFO'}, "{} face(s) are selected".format(face_count))
# paste
- for idx, duvs, dpuvs, dss in zip(dest_face_indices, dest_uvs,
- dest_pin_uvs, dest_seams):
- duvs_fr = [uv for uv in duvs]
- dpuvs_fr = [pin_uv for pin_uv in dpuvs]
- dss_fr = [s for s in dss]
- # flip UVs
- if self.flip is True:
- duvs_fr.reverse()
- dpuvs_fr.reverse()
- dss_fr.reverse()
- # rotate UVs
- for _ in range(self.rotate):
- uv = duvs_fr.pop()
- pin_uv = dpuvs_fr.pop()
- s = dss_fr.pop()
- duvs_fr.insert(0, uv)
- dpuvs_fr.insert(0, pin_uv)
- dss_fr.insert(0, s)
- # paste UVs
- for l, duv, dpuv, ds in zip(
- bm.faces[idx].loops, duvs_fr, dpuvs_fr, dss_fr):
- l[uv_layer].uv = duv
- l[uv_layer].pin_uv = dpuv
- if self.seams is True:
- l.edge.seam = ds
-
- self.report({'INFO'}, "%d face(s) are flipped/rotated" % len(dest_uvs))
+ ret = impl.paste_uv(self, bm, src_info, src_info, [uv_layer], 'N_N',
+ self.flip, self.rotate, self.seams)
+ if ret:
+ return {'CANCELLED'}
bmesh.update_edit_mesh(obj.data)
- if self.seams is True:
- obj.data.show_edge_seams = True
return {'FINISHED'}
diff --git a/uv_magic_uv/op/mirror_uv.py b/uv_magic_uv/op/mirror_uv.py
index 11ad2bca..6793ca23 100644
--- a/uv_magic_uv/op/mirror_uv.py
+++ b/uv_magic_uv/op/mirror_uv.py
@@ -29,40 +29,22 @@ from bpy.props import (
FloatProperty,
BoolProperty,
)
-import bmesh
-from mathutils import Vector
-from .. import common
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
+from ..impl import mirror_uv_impl as impl
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_MirrorUV',
]
-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
-
-
+@PropertyClassRegistry()
class Properties:
+ idname = "mirror_uv"
+
@classmethod
def init_props(cls, scene):
scene.muv_mirror_uv_enabled = BoolProperty(
@@ -87,7 +69,8 @@ class Properties:
del scene.muv_mirror_uv_axis
-class Operator(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_MirrorUV(bpy.types.Operator):
"""
Operation class: Mirror UV
"""
@@ -96,7 +79,7 @@ class Operator(bpy.types.Operator):
bl_label = "Mirror UV"
bl_options = {'REGISTER', 'UNDO'}
- axis = EnumProperty(
+ axis: EnumProperty(
items=(
('X', "X", "Mirror Along X axis"),
('Y', "Y", "Mirror Along Y axis"),
@@ -106,7 +89,7 @@ class Operator(bpy.types.Operator):
description="Mirror Axis",
default='X'
)
- error = FloatProperty(
+ error: FloatProperty(
name="Error",
description="Error threshold",
default=0.001,
@@ -116,95 +99,12 @@ class Operator(bpy.types.Operator):
soft_max=1.0
)
- def __is_vector_similar(self, v1, v2, error):
- """
- Check if two vectors are similar, within an error threshold
- """
- within_err_x = abs(v2.x - v1.x) < error
- within_err_y = abs(v2.y - v1.y) < error
- within_err_z = abs(v2.z - v1.z) < error
-
- return within_err_x and within_err_y and within_err_z
-
- def __mirror_uvs(self, uv_layer, src, dst, axis, error):
- """
- Copy UV coordinates from one UV face to another
- """
- for sl in src.loops:
- suv = sl[uv_layer].uv.copy()
- svco = sl.vert.co.copy()
- for dl in dst.loops:
- dvco = dl.vert.co.copy()
- if axis == 'X':
- dvco.x = -dvco.x
- elif axis == 'Y':
- dvco.y = -dvco.y
- elif axis == 'Z':
- dvco.z = -dvco.z
-
- if self.__is_vector_similar(svco, dvco, error):
- dl[uv_layer].uv = suv.copy()
-
- def __get_face_center(self, face):
- """
- Get center coordinate of the face
- """
- center = Vector((0.0, 0.0, 0.0))
- for v in face.verts:
- center = center + v.co
-
- return center / len(face.verts)
+ def __init__(self):
+ self.__impl = impl.MirrorUVImpl()
@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)
+ return impl.MirrorUVImpl.poll(context)
def execute(self, context):
- obj = context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
-
- error = self.error
- axis = self.axis
-
- if common.check_version(2, 73, 0) >= 0:
- bm.faces.ensure_lookup_table()
- 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()
-
- faces = [f for f in bm.faces if f.select]
- for f_dst in faces:
- count = len(f_dst.verts)
- for f_src in bm.faces:
- # check if this is a candidate to do mirror UV
- if f_src.index == f_dst.index:
- continue
- if count != len(f_src.verts):
- continue
-
- # test if the vertices x values are the same sign
- dst = self.__get_face_center(f_dst)
- src = self.__get_face_center(f_src)
- if (dst.x > 0 and src.x > 0) or (dst.x < 0 and src.x < 0):
- continue
-
- # invert source axis
- if axis == 'X':
- src.x = -src.x
- elif axis == 'Y':
- src.y = -src.z
- elif axis == 'Z':
- src.z = -src.z
-
- # do mirror UV
- if self.__is_vector_similar(dst, src, error):
- self.__mirror_uvs(
- uv_layer, f_src, f_dst, self.axis, self.error)
-
- bmesh.update_edit_mesh(obj.data)
-
- return {'FINISHED'}
+ return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/op/move_uv.py b/uv_magic_uv/op/move_uv.py
index a229ae34..653918d3 100644
--- a/uv_magic_uv/op/move_uv.py
+++ b/uv_magic_uv/op/move_uv.py
@@ -24,40 +24,23 @@ __version__ = "5.2"
__date__ = "17 Nov 2018"
import bpy
-import bmesh
-from mathutils import Vector
from bpy.props import BoolProperty
-from .. import common
+from ..impl import move_uv_impl as impl
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
+
__all__ = [
'Properties',
- 'Operator',
+ 'MUV_OT_MoveUV',
]
-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
-
-
+@PropertyClassRegistry()
class Properties:
+ idname = "move_uv"
+
@classmethod
def init_props(cls, scene):
scene.muv_move_uv_enabled = BoolProperty(
@@ -71,7 +54,8 @@ class Properties:
del scene.muv_move_uv_enabled
-class Operator(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_MoveUV(bpy.types.Operator):
"""
Operator class: Move UV
"""
@@ -80,108 +64,19 @@ class Operator(bpy.types.Operator):
bl_label = "Move UV"
bl_options = {'REGISTER', 'UNDO'}
- __running = False
-
def __init__(self):
- self.__topology_dict = []
- self.__prev_mouse = Vector((0.0, 0.0))
- self.__offset_uv = Vector((0.0, 0.0))
- self.__prev_offset_uv = Vector((0.0, 0.0))
- self.__first_time = True
- self.__ini_uvs = []
- self.__operating = False
+ self.__impl = impl.MoveUVImpl()
@classmethod
def poll(cls, context):
- # we can not get area/space/region from console
- if common.is_console_mode():
- return False
- if cls.is_running(context):
- return False
- return is_valid_context(context)
+ return impl.MoveUVImpl.poll(context)
@classmethod
def is_running(cls, _):
- return cls.__running
-
- def __find_uv(self, context):
- bm = bmesh.from_edit_mesh(context.object.data)
- topology_dict = []
- uvs = []
- active_uv = bm.loops.layers.uv.active
- for fidx, f in enumerate(bm.faces):
- for vidx, v in enumerate(f.verts):
- if v.select:
- uvs.append(f.loops[vidx][active_uv].uv.copy())
- topology_dict.append([fidx, vidx])
-
- return topology_dict, uvs
+ return impl.MoveUVImpl.is_running(_)
def modal(self, context, event):
- if self.__first_time is True:
- self.__prev_mouse = Vector((
- event.mouse_region_x, event.mouse_region_y))
- self.__first_time = False
- return {'RUNNING_MODAL'}
-
- # move UV
- div = 10000
- self.__offset_uv += Vector((
- (event.mouse_region_x - self.__prev_mouse.x) / div,
- (event.mouse_region_y - self.__prev_mouse.y) / div))
- ouv = self.__offset_uv
- pouv = self.__prev_offset_uv
- vec = Vector((ouv.x - ouv.y, ouv.x + ouv.y))
- dv = vec - pouv
- self.__prev_offset_uv = vec
- self.__prev_mouse = Vector((
- event.mouse_region_x, event.mouse_region_y))
-
- # check if operation is started
- if not self.__operating:
- if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
- self.__operating = True
- return {'RUNNING_MODAL'}
-
- # update UV
- obj = context.object
- bm = bmesh.from_edit_mesh(obj.data)
- active_uv = bm.loops.layers.uv.active
- for fidx, vidx in self.__topology_dict:
- l = bm.faces[fidx].loops[vidx]
- l[active_uv].uv = l[active_uv].uv + dv
- bmesh.update_edit_mesh(obj.data)
-
- # check mouse preference
- if context.user_preferences.inputs.select_mouse == 'RIGHT':
- confirm_btn = 'LEFTMOUSE'
- cancel_btn = 'RIGHTMOUSE'
- else:
- confirm_btn = 'RIGHTMOUSE'
- cancel_btn = 'LEFTMOUSE'
-
- # cancelled
- if event.type == cancel_btn and event.value == 'PRESS':
- for (fidx, vidx), uv in zip(self.__topology_dict, self.__ini_uvs):
- bm.faces[fidx].loops[vidx][active_uv].uv = uv
- Operator.__running = False
- return {'FINISHED'}
- # confirmed
- if event.type == confirm_btn and event.value == 'PRESS':
- Operator.__running = False
- return {'FINISHED'}
-
- return {'RUNNING_MODAL'}
+ return self.__impl.modal(self, context, event)
def execute(self, context):
- Operator.__running = True
- self.__operating = False
- self.__first_time = True
-
- context.window_manager.modal_handler_add(self)
- self.__topology_dict, self.__ini_uvs = self.__find_uv(context)
-
- if context.area:
- context.area.tag_redraw()
-
- return {'RUNNING_MODAL'}
+ return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/op/transfer_uv.py b/uv_magic_uv/op/transfer_uv.py
index ef6fc3be..db05b343 100644
--- a/uv_magic_uv/op/transfer_uv.py
+++ b/uv_magic_uv/op/transfer_uv.py
@@ -23,43 +23,27 @@ __status__ = "production"
__version__ = "5.2"
__date__ = "17 Nov 2018"
-from collections import OrderedDict
-
import bpy
import bmesh
from bpy.props import BoolProperty
from .. import common
+from ..impl import transfer_uv_impl as impl
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
__all__ = [
- 'OperatorCopyUV',
- 'OperatorPasteUV',
+ 'Properties',
+ 'MUV_OT_TransferUV_CopyUV',
+ 'MUV_OT_TransferUV_PasteUV',
]
-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
-
-
+@PropertyClassRegistry()
class Properties:
+ idname = "transfer_uv"
+
@classmethod
def init_props(cls, scene):
class Props():
@@ -90,7 +74,8 @@ class Properties:
del scene.muv_transfer_uv_copy_seams
-class OperatorCopyUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_TransferUV_CopyUV(bpy.types.Operator):
"""
Operation class: Transfer UV copy
Topological based copy
@@ -106,56 +91,30 @@ class OperatorCopyUV(bpy.types.Operator):
# we can not get area/space/region from console
if common.is_console_mode():
return True
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def execute(self, context):
props = context.scene.muv_props.transfer_uv
- active_obj = context.scene.objects.active
+ active_obj = context.active_object
bm = bmesh.from_edit_mesh(active_obj.data)
- if common.check_version(2, 73, 0) >= 0:
- bm.faces.ensure_lookup_table()
+ bm.faces.ensure_lookup_table()
- # get UV layer
- if not bm.loops.layers.uv:
- self.report({'WARNING'}, "Object must have more than one UV map")
+ uv_layer = impl.get_uv_layer(self, bm)
+ if uv_layer is None:
return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
- props.topology_copied = []
-
- # get selected faces
- active_face = bm.faces.active
- sel_faces = [face for face in bm.faces if face.select]
- if len(sel_faces) != 2:
- self.report({'WARNING'}, "Two faces must be selected")
- return {'CANCELLED'}
- if not active_face or active_face not in sel_faces:
- self.report({'WARNING'}, "Two faces must be active")
- return {'CANCELLED'}
-
- # parse all faces according to selection
- active_face_nor = active_face.normal.copy()
- all_sorted_faces = main_parse(
- self, uv_layer, sel_faces, active_face,
- active_face_nor)
-
- if all_sorted_faces:
- for face_data in all_sorted_faces.values():
- edges = face_data[1]
- uv_loops = face_data[2]
- uvs = [l.uv.copy() for l in uv_loops]
- pin_uvs = [l.pin_uv for l in uv_loops]
- seams = [e.seam for e in edges]
- props.topology_copied.append([uvs, pin_uvs, seams])
- else:
+ faces = impl.get_selected_src_faces(self, bm, uv_layer)
+ if faces is None:
return {'CANCELLED'}
+ props.topology_copied = faces
bmesh.update_edit_mesh(active_obj.data)
return {'FINISHED'}
-class OperatorPasteUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_TransferUV_PasteUV(bpy.types.Operator):
"""
Operation class: Transfer UV paste
Topological based paste
@@ -166,12 +125,12 @@ class OperatorPasteUV(bpy.types.Operator):
bl_description = "Transfer UV Paste UV (Topological based paste)"
bl_options = {'REGISTER', 'UNDO'}
- invert_normals = BoolProperty(
+ invert_normals: BoolProperty(
name="Invert Normals",
description="Invert Normals",
default=False
)
- copy_seams = BoolProperty(
+ copy_seams: BoolProperty(
name="Copy Seams",
description="Copy Seams",
default=True
@@ -186,253 +145,24 @@ class OperatorPasteUV(bpy.types.Operator):
props = sc.muv_props.transfer_uv
if not props.topology_copied:
return False
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def execute(self, context):
props = context.scene.muv_props.transfer_uv
- active_obj = context.scene.objects.active
+ active_obj = context.active_object
bm = bmesh.from_edit_mesh(active_obj.data)
- if common.check_version(2, 73, 0) >= 0:
- bm.faces.ensure_lookup_table()
+ bm.faces.ensure_lookup_table()
# get UV layer
- if not bm.loops.layers.uv:
- self.report({'WARNING'}, "Object must have more than one UV map")
+ uv_layer = impl.get_uv_layer(self, bm)
+ if uv_layer is None:
return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
- # get selection history
- all_sel_faces = [
- e for e in bm.select_history
- if isinstance(e, bmesh.types.BMFace) and e.select]
- if len(all_sel_faces) % 2 != 0:
- self.report({'WARNING'}, "Two faces must be selected")
+ ret = impl.paste_uv(self, bm, uv_layer, props.topology_copied,
+ self.invert_normals, self.copy_seams)
+ if ret:
return {'CANCELLED'}
- # parse selection history
- for i, _ in enumerate(all_sel_faces):
- if (i == 0) or (i % 2 == 0):
- continue
- sel_faces = [all_sel_faces[i - 1], all_sel_faces[i]]
- active_face = all_sel_faces[i]
-
- # parse all faces according to selection history
- active_face_nor = active_face.normal.copy()
- if self.invert_normals:
- active_face_nor.negate()
- all_sorted_faces = main_parse(
- self, uv_layer, sel_faces, active_face,
- active_face_nor)
-
- if all_sorted_faces:
- # check amount of copied/pasted faces
- if len(all_sorted_faces) != len(props.topology_copied):
- self.report(
- {'WARNING'},
- "Mesh has different amount of faces"
- )
- return {'CANCELLED'}
-
- for j, face_data in enumerate(all_sorted_faces.values()):
- copied_data = props.topology_copied[j]
-
- # check amount of copied/pasted verts
- if len(copied_data[0]) != len(face_data[2]):
- bpy.ops.mesh.select_all(action='DESELECT')
- # select problematic face
- list(all_sorted_faces.keys())[j].select = True
- self.report(
- {'WARNING'},
- "Face have different amount of vertices"
- )
- return {'FINISHED'}
-
- for k, (edge, uvloop) in enumerate(zip(face_data[1],
- face_data[2])):
- uvloop.uv = copied_data[0][k]
- uvloop.pin_uv = copied_data[1][k]
- if self.copy_seams:
- edge.seam = copied_data[2][k]
- else:
- return {'CANCELLED'}
-
bmesh.update_edit_mesh(active_obj.data)
- if self.copy_seams:
- active_obj.data.show_edge_seams = True
return {'FINISHED'}
-
-
-def main_parse(
- self, uv_layer, sel_faces,
- active_face, active_face_nor):
- all_sorted_faces = OrderedDict() # This is the main stuff
-
- used_verts = set()
- used_edges = set()
-
- faces_to_parse = []
-
- # get shared edge of two faces
- cross_edges = []
- for edge in active_face.edges:
- if edge in sel_faces[0].edges and edge in sel_faces[1].edges:
- cross_edges.append(edge)
-
- # parse two selected faces
- if cross_edges and len(cross_edges) == 1:
- shared_edge = cross_edges[0]
- vert1 = None
- vert2 = None
-
- dot_n = active_face_nor.normalized()
- edge_vec_1 = (shared_edge.verts[1].co - shared_edge.verts[0].co)
- edge_vec_len = edge_vec_1.length
- edge_vec_1 = edge_vec_1.normalized()
-
- af_center = active_face.calc_center_median()
- af_vec = shared_edge.verts[0].co + (edge_vec_1 * (edge_vec_len * 0.5))
- af_vec = (af_vec - af_center).normalized()
-
- if af_vec.cross(edge_vec_1).dot(dot_n) > 0:
- vert1 = shared_edge.verts[0]
- vert2 = shared_edge.verts[1]
- else:
- vert1 = shared_edge.verts[1]
- vert2 = shared_edge.verts[0]
-
- # get active face stuff and uvs
- face_stuff = get_other_verts_edges(
- active_face, vert1, vert2, shared_edge, uv_layer)
- all_sorted_faces[active_face] = face_stuff
- used_verts.update(active_face.verts)
- used_edges.update(active_face.edges)
-
- # get first selected face stuff and uvs as they share shared_edge
- second_face = sel_faces[0]
- if second_face is active_face:
- second_face = sel_faces[1]
- face_stuff = get_other_verts_edges(
- second_face, vert1, vert2, shared_edge, uv_layer)
- all_sorted_faces[second_face] = face_stuff
- used_verts.update(second_face.verts)
- used_edges.update(second_face.edges)
-
- # first Grow
- faces_to_parse.append(active_face)
- faces_to_parse.append(second_face)
-
- else:
- self.report({'WARNING'}, "Two faces should share one edge")
- return None
-
- # parse all faces
- while True:
- new_parsed_faces = []
- if not faces_to_parse:
- break
- for face in faces_to_parse:
- face_stuff = all_sorted_faces.get(face)
- new_faces = parse_faces(
- face, face_stuff, used_verts, used_edges, all_sorted_faces,
- uv_layer)
- if new_faces == 'CANCELLED':
- self.report({'WARNING'}, "More than 2 faces share edge")
- return None
-
- new_parsed_faces += new_faces
- faces_to_parse = new_parsed_faces
-
- return all_sorted_faces
-
-
-def parse_faces(
- check_face, face_stuff, used_verts, used_edges, all_sorted_faces,
- uv_layer):
- """recurse faces around the new_grow only"""
-
- new_shared_faces = []
- for sorted_edge in face_stuff[1]:
- shared_faces = sorted_edge.link_faces
- if shared_faces:
- if len(shared_faces) > 2:
- bpy.ops.mesh.select_all(action='DESELECT')
- for face_sel in shared_faces:
- face_sel.select = True
- shared_faces = []
- return 'CANCELLED'
-
- clear_shared_faces = get_new_shared_faces(
- check_face, sorted_edge, shared_faces, all_sorted_faces.keys())
- if clear_shared_faces:
- shared_face = clear_shared_faces[0]
- # get vertices of the edge
- vert1 = sorted_edge.verts[0]
- vert2 = sorted_edge.verts[1]
-
- common.debug_print(face_stuff[0], vert1, vert2)
- if face_stuff[0].index(vert1) > face_stuff[0].index(vert2):
- vert1 = sorted_edge.verts[1]
- vert2 = sorted_edge.verts[0]
-
- common.debug_print(shared_face.verts, vert1, vert2)
- new_face_stuff = get_other_verts_edges(
- shared_face, vert1, vert2, sorted_edge, uv_layer)
- all_sorted_faces[shared_face] = new_face_stuff
- used_verts.update(shared_face.verts)
- used_edges.update(shared_face.edges)
-
- if common.is_debug_mode():
- shared_face.select = True # test which faces are parsed
-
- new_shared_faces.append(shared_face)
-
- return new_shared_faces
-
-
-def get_new_shared_faces(orig_face, shared_edge, check_faces, used_faces):
- shared_faces = []
-
- for face in check_faces:
- is_shared_edge = shared_edge in face.edges
- not_used = face not in used_faces
- not_orig = face is not orig_face
- not_hide = face.hide is False
- if is_shared_edge and not_used and not_orig and not_hide:
- shared_faces.append(face)
-
- return shared_faces
-
-
-def get_other_verts_edges(face, vert1, vert2, first_edge, uv_layer):
- face_edges = [first_edge]
- face_verts = [vert1, vert2]
- face_loops = []
-
- other_edges = [edge for edge in face.edges if edge not in face_edges]
-
- for _ in range(len(other_edges)):
- found_edge = None
- # get sorted verts and edges
- for edge in other_edges:
- if face_verts[-1] in edge.verts:
- other_vert = edge.other_vert(face_verts[-1])
-
- if other_vert not in face_verts:
- face_verts.append(other_vert)
-
- found_edge = edge
- if found_edge not in face_edges:
- face_edges.append(edge)
- break
-
- other_edges.remove(found_edge)
-
- # get sorted uvs
- for vert in face_verts:
- for loop in face.loops:
- if loop.vert is vert:
- face_loops.append(loop[uv_layer])
- break
-
- return [face_verts, face_edges, face_loops]
diff --git a/uv_magic_uv/op/uvw.py b/uv_magic_uv/op/uvw.py
index 44858187..c97e0e04 100644
--- a/uv_magic_uv/op/uvw.py
+++ b/uv_magic_uv/op/uvw.py
@@ -23,8 +23,6 @@ __status__ = "production"
__version__ = "5.2"
__date__ = "17 Nov 2018"
-from math import sin, cos, pi
-
import bpy
import bmesh
from bpy.props import (
@@ -32,40 +30,24 @@ from bpy.props import (
FloatVectorProperty,
BoolProperty
)
-from mathutils import Vector
from .. import common
+from ..impl import uvw_impl as impl
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
__all__ = [
'Properties',
- 'OperatorBoxMap',
- 'OperatorBestPlanerMap',
+ 'MUV_OT_UVW_BoxMap',
+ 'MUV_OT_UVW_BestPlanerMap',
]
-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
-
-
+@PropertyClassRegistry()
class Properties:
+ idname = "uvw"
+
@classmethod
def init_props(cls, scene):
scene.muv_uvw_enabled = BoolProperty(
@@ -85,32 +67,33 @@ class Properties:
del scene.muv_uvw_assign_uvmap
-class OperatorBoxMap(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_UVW_BoxMap(bpy.types.Operator):
bl_idname = "uv.muv_uvw_operator_box_map"
bl_label = "Box Map"
bl_options = {'REGISTER', 'UNDO'}
- size = FloatProperty(
+ size: FloatProperty(
name="Size",
default=1.0,
precision=4
)
- rotation = FloatVectorProperty(
+ rotation: FloatVectorProperty(
name="XYZ Rotation",
size=3,
default=(0.0, 0.0, 0.0)
)
- offset = FloatVectorProperty(
+ offset: FloatVectorProperty(
name="XYZ Offset",
size=3,
default=(0.0, 0.0, 0.0)
)
- tex_aspect = FloatProperty(
+ tex_aspect: FloatProperty(
name="Texture Aspect",
default=1.0,
precision=4
)
- assign_uvmap = BoolProperty(
+ assign_uvmap: BoolProperty(
name="Assign UVMap",
description="Assign UVMap when no UVmaps are available",
default=True
@@ -121,7 +104,7 @@ class OperatorBoxMap(bpy.types.Operator):
# we can not get area/space/region from console
if common.is_console_mode():
return True
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def execute(self, context):
obj = context.active_object
@@ -130,102 +113,43 @@ class OperatorBoxMap(bpy.types.Operator):
bm.faces.ensure_lookup_table()
# get UV layer
- if not bm.loops.layers.uv:
- if self.assign_uvmap:
- bm.loops.layers.uv.new()
- else:
- self.report(
- {'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
- uv_layer = bm.loops.layers.uv.verify()
-
- scale = 1.0 / self.size
-
- sx = 1.0 * scale
- sy = 1.0 * scale
- sz = 1.0 * scale
- ofx = self.offset[0]
- ofy = self.offset[1]
- ofz = self.offset[2]
- rx = self.rotation[0] * pi / 180.0
- ry = self.rotation[1] * pi / 180.0
- rz = self.rotation[2] * pi / 180.0
- aspect = self.tex_aspect
-
- sel_faces = [f for f in bm.faces if f.select]
-
- # update UV coordinate
- for f in sel_faces:
- n = f.normal
- for l in f.loops:
- co = l.vert.co
- x = co.x * sx
- y = co.y * sy
- z = co.z * sz
-
- # X-plane
- if abs(n[0]) >= abs(n[1]) and abs(n[0]) >= abs(n[2]):
- if n[0] >= 0.0:
- u = (y - ofy) * cos(rx) + (z - ofz) * sin(rx)
- v = -(y * aspect - ofy) * sin(rx) +\
- (z * aspect - ofz) * cos(rx)
- else:
- u = -(y - ofy) * cos(rx) + (z - ofz) * sin(rx)
- v = (y * aspect - ofy) * sin(rx) +\
- (z * aspect - ofz) * cos(rx)
- # Y-plane
- elif abs(n[1]) >= abs(n[0]) and abs(n[1]) >= abs(n[2]):
- if n[1] >= 0.0:
- u = -(x - ofx) * cos(ry) + (z - ofz) * sin(ry)
- v = (x * aspect - ofx) * sin(ry) +\
- (z * aspect - ofz) * cos(ry)
- else:
- u = (x - ofx) * cos(ry) + (z - ofz) * sin(ry)
- v = -(x * aspect - ofx) * sin(ry) +\
- (z * aspect - ofz) * cos(ry)
- # Z-plane
- else:
- if n[2] >= 0.0:
- u = (x - ofx) * cos(rz) + (y - ofy) * sin(rz)
- v = -(x * aspect - ofx) * sin(rz) +\
- (y * aspect - ofy) * cos(rz)
- else:
- u = -(x - ofx) * cos(rz) - (y + ofy) * sin(rz)
- v = -(x * aspect + ofx) * sin(rz) +\
- (y * aspect - ofy) * cos(rz)
-
- l[uv_layer].uv = Vector((u, v))
+ uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap)
+ if not uv_layer:
+ return {'CANCELLED'}
+ impl.apply_box_map(bm, uv_layer, self.size, self.offset,
+ self.rotation, self.tex_aspect)
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}
-class OperatorBestPlanerMap(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_UVW_BestPlanerMap(bpy.types.Operator):
bl_idname = "uv.muv_uvw_operator_best_planer_map"
bl_label = "Best Planer Map"
bl_options = {'REGISTER', 'UNDO'}
- size = FloatProperty(
+ size: FloatProperty(
name="Size",
default=1.0,
precision=4
)
- rotation = FloatProperty(
+ rotation: FloatProperty(
name="XY Rotation",
default=0.0
)
- offset = FloatVectorProperty(
+ offset: FloatVectorProperty(
name="XY Offset",
size=2,
default=(0.0, 0.0)
)
- tex_aspect = FloatProperty(
+ tex_aspect: FloatProperty(
name="Texture Aspect",
default=1.0,
precision=4
)
- assign_uvmap = BoolProperty(
+ assign_uvmap: BoolProperty(
name="Assign UVMap",
description="Assign UVMap when no UVmaps are available",
default=True
@@ -236,7 +160,7 @@ class OperatorBestPlanerMap(bpy.types.Operator):
# we can not get area/space/region from console
if common.is_console_mode():
return True
- return is_valid_context(context)
+ return impl.is_valid_context(context)
def execute(self, context):
obj = context.active_object
@@ -245,44 +169,12 @@ class OperatorBestPlanerMap(bpy.types.Operator):
bm.faces.ensure_lookup_table()
# get UV layer
- if not bm.loops.layers.uv:
- if self.assign_uvmap:
- bm.loops.layers.uv.new()
- else:
- self.report(
- {'WARNING'}, "Object must have more than one UV map")
- return {'CANCELLED'}
-
- uv_layer = bm.loops.layers.uv.verify()
-
- scale = 1.0 / self.size
-
- sx = 1.0 * scale
- sy = 1.0 * scale
- ofx = self.offset[0]
- ofy = self.offset[1]
- rz = self.rotation * pi / 180.0
- aspect = self.tex_aspect
-
- sel_faces = [f for f in bm.faces if f.select]
-
- # calculate average of normal
- n_ave = Vector((0.0, 0.0, 0.0))
- for f in sel_faces:
- n_ave = n_ave + f.normal
- q = n_ave.rotation_difference(Vector((0.0, 0.0, 1.0)))
-
- # update UV coordinate
- for f in sel_faces:
- for l in f.loops:
- co = q * l.vert.co
- x = co.x * sx
- y = co.y * sy
-
- u = x * cos(rz) - y * sin(rz) + ofx
- v = -x * aspect * sin(rz) - y * aspect * cos(rz) + ofy
+ uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap)
+ if not uv_layer:
+ return {'CANCELLED'}
- l[uv_layer].uv = Vector((u, v))
+ impl.apply_planer_map(bm, uv_layer, self.size, self.offset,
+ self.rotation, self.tex_aspect)
bmesh.update_edit_mesh(obj.data)
diff --git a/uv_magic_uv/preferences.py b/uv_magic_uv/preferences.py
index 376258d0..3ba94376 100644
--- a/uv_magic_uv/preferences.py
+++ b/uv_magic_uv/preferences.py
@@ -33,11 +33,10 @@ from bpy.props import (
)
from bpy.types import AddonPreferences
-from . import ui
from . import op
+from . import ui
from . import addon_updater_ops
-
__all__ = [
'add_builtin_menu',
'remove_builtin_menu',
@@ -50,100 +49,50 @@ def view3d_uvmap_menu_fn(self, context):
sc = context.scene
layout.separator()
- layout.label("Copy/Paste UV", icon='IMAGE_COL')
+ layout.label(text="Copy/Paste UV", icon='IMAGE')
# Copy/Paste UV
- layout.menu(ui.VIEW3D_MT_uv_map.MenuCopyPasteUV.bl_idname,
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_CopyPasteUV.bl_idname,
text="Copy/Paste UV")
# Transfer UV
- layout.menu(ui.VIEW3D_MT_uv_map.MenuTransferUV.bl_idname,
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TransferUV.bl_idname,
text="Transfer UV")
layout.separator()
- layout.label("UV Manipulation", icon='IMAGE_COL')
+ layout.label(text="UV Manipulation", icon='IMAGE')
# Flip/Rotate UV
- ops = layout.operator(op.flip_rotate_uv.Operator.bl_idname,
+ ops = layout.operator(op.flip_rotate_uv.MUV_OT_FlipRotate.bl_idname,
text="Flip/Rotate UV")
ops.seams = sc.muv_flip_rotate_uv_seams
# Mirror UV
- ops = layout.operator(op.mirror_uv.Operator.bl_idname, text="Mirror UV")
+ ops = layout.operator(op.mirror_uv.MUV_OT_MirrorUV.bl_idname,
+ text="Mirror UV")
ops.axis = sc.muv_mirror_uv_axis
# Move UV
- layout.operator(op.move_uv.Operator.bl_idname, text="Move UV")
- # World Scale UV
- layout.menu(ui.VIEW3D_MT_uv_map.MenuWorldScaleUV.bl_idname,
- text="World Scale UV")
- # Preserve UV
- layout.menu(ui.VIEW3D_MT_uv_map.MenuPreserveUVAspect.bl_idname,
- text="Preserve UV")
- # Texture Lock
- layout.menu(ui.VIEW3D_MT_uv_map.MenuTextureLock.bl_idname,
- text="Texture Lock")
- # Texture Wrap
- layout.menu(ui.VIEW3D_MT_uv_map.MenuTextureWrap.bl_idname,
- text="Texture Wrap")
- # UV Sculpt
- layout.prop(sc, "muv_uv_sculpt_enable", text="UV Sculpt")
+ layout.operator(op.move_uv.MUV_OT_MoveUV.bl_idname, text="Move UV")
layout.separator()
- layout.label("UV Mapping", icon='IMAGE_COL')
- # Unwrap Constraint
- ops = layout.operator(op.unwrap_constraint.Operator.bl_idname,
- text="Unwrap Constraint")
- ops.u_const = sc.muv_unwrap_constraint_u_const
- ops.v_const = sc.muv_unwrap_constraint_v_const
- # Texture Projection
- layout.menu(ui.VIEW3D_MT_uv_map.MenuTextureProjection.bl_idname,
- text="Texture Projection")
# UVW
- layout.menu(ui.VIEW3D_MT_uv_map.MenuUVW.bl_idname, text="UVW")
+ layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_UVW.bl_idname, text="UVW")
def view3d_object_menu_fn(self, _):
layout = self.layout
layout.separator()
+ layout.label(text="Copy/Paste UV", icon='IMAGE')
# Copy/Paste UV (Among Objecct)
- layout.menu(ui.VIEW3D_MT_object.MenuCopyPasteUVObject.bl_idname,
+ layout.menu(ui.VIEW3D_MT_object.MUV_MT_CopyPasteUV_Object.bl_idname,
text="Copy/Paste UV")
- layout.label("Copy/Paste UV", icon='IMAGE_COL')
-def image_uvs_menu_fn(self, context):
+def image_uvs_menu_fn(self, _):
layout = self.layout
- sc = context.scene
-
- layout.separator()
- # Align UV Cursor
- layout.menu(ui.IMAGE_MT_uvs.MenuAlignUVCursor.bl_idname,
- text="Align UV Cursor")
- # UV Bounding Box
- layout.prop(sc, "muv_uv_bounding_box_show", text="UV Bounding Box")
- # UV Inspection
- layout.menu(ui.IMAGE_MT_uvs.MenuUVInspection.bl_idname,
- text="UV Inspection")
- layout.label("Editor Enhancement", icon='IMAGE_COL')
-
- layout.separator()
- # Align UV
- layout.menu(ui.IMAGE_MT_uvs.MenuAlignUV.bl_idname, text="Align UV")
- # Smooth UV
- ops = layout.operator(op.smooth_uv.Operator.bl_idname, text="Smooth")
- ops.transmission = sc.muv_smooth_uv_transmission
- ops.select = sc.muv_smooth_uv_select
- ops.mesh_infl = sc.muv_smooth_uv_mesh_infl
- # Select UV
- layout.menu(ui.IMAGE_MT_uvs.MenuSelectUV.bl_idname, text="Select UV")
- # Pack UV
- ops = layout.operator(op.pack_uv.Operator.bl_idname, text="Pack UV")
- ops.allowable_center_deviation = sc.muv_pack_uv_allowable_center_deviation
- ops.allowable_size_deviation = sc.muv_pack_uv_allowable_size_deviation
- layout.label("UV Manipulation", icon='IMAGE_COL')
layout.separator()
# Copy/Paste UV (on UV/Image Editor)
- layout.menu(ui.IMAGE_MT_uvs.MenuCopyPasteUVUVEdit.bl_idname,
+ layout.label(text="Copy/Paste UV", icon='IMAGE')
+ layout.menu(ui.IMAGE_MT_uvs.MUV_MT_CopyPasteUV_UVEdit.bl_idname,
text="Copy/Paste UV")
- layout.label("Copy/Paste UV", icon='IMAGE_COL')
def add_builtin_menu():
@@ -154,14 +103,14 @@ def add_builtin_menu():
def remove_builtin_menu():
bpy.types.IMAGE_MT_uvs.remove(image_uvs_menu_fn)
- bpy.types.VIEW3D_MT_object.remove(view3d_object_menu_fn)
+ bpy.types.VIEW3D_MT_object.append(view3d_object_menu_fn)
bpy.types.VIEW3D_MT_uv_map.remove(view3d_uvmap_menu_fn)
class Preferences(AddonPreferences):
"""Preferences class: Preferences for this add-on"""
- bl_idname = __package__
+ bl_idname = "uv_magic_uv"
def update_enable_builtin_menu(self, _):
if self['enable_builtin_menu']:
diff --git a/uv_magic_uv/properites.py b/uv_magic_uv/properites.py
index e4634e51..60ce26eb 100644
--- a/uv_magic_uv/properites.py
+++ b/uv_magic_uv/properites.py
@@ -24,30 +24,7 @@ __version__ = "5.2"
__date__ = "17 Nov 2018"
-from .op import (
- align_uv,
- align_uv_cursor,
- copy_paste_uv,
- copy_paste_uv_object,
- copy_paste_uv_uvedit,
- flip_rotate_uv,
- mirror_uv,
- move_uv,
- pack_uv,
- preserve_uv_aspect,
- select_uv,
- smooth_uv,
- texture_lock,
- texture_projection,
- texture_wrap,
- transfer_uv,
- unwrap_constraint,
- uv_bounding_box,
- uv_inspection,
- uv_sculpt,
- uvw,
- world_scale_uv,
-)
+from .utils.property_class_registry import PropertyClassRegistry
__all__ = [
@@ -77,53 +54,9 @@ class MUV_Prefs():
def init_props(scene):
scene.muv_props = MUV_Properties()
-
- align_uv.Properties.init_props(scene)
- align_uv_cursor.Properties.init_props(scene)
- copy_paste_uv.Properties.init_props(scene)
- copy_paste_uv_object.Properties.init_props(scene)
- copy_paste_uv_uvedit.Properties.init_props(scene)
- flip_rotate_uv.Properties.init_props(scene)
- mirror_uv.Properties.init_props(scene)
- move_uv.Properties.init_props(scene)
- pack_uv.Properties.init_props(scene)
- preserve_uv_aspect.Properties.init_props(scene)
- select_uv.Properties.init_props(scene)
- smooth_uv.Properties.init_props(scene)
- texture_lock.Properties.init_props(scene)
- texture_projection.Properties.init_props(scene)
- texture_wrap.Properties.init_props(scene)
- transfer_uv.Properties.init_props(scene)
- unwrap_constraint.Properties.init_props(scene)
- uv_bounding_box.Properties.init_props(scene)
- uv_inspection.Properties.init_props(scene)
- uv_sculpt.Properties.init_props(scene)
- uvw.Properties.init_props(scene)
- world_scale_uv.Properties.init_props(scene)
+ PropertyClassRegistry.init_props(scene)
def clear_props(scene):
- align_uv.Properties.del_props(scene)
- align_uv_cursor.Properties.del_props(scene)
- copy_paste_uv.Properties.del_props(scene)
- copy_paste_uv_object.Properties.del_props(scene)
- copy_paste_uv_uvedit.Properties.del_props(scene)
- flip_rotate_uv.Properties.del_props(scene)
- mirror_uv.Properties.del_props(scene)
- move_uv.Properties.del_props(scene)
- pack_uv.Properties.del_props(scene)
- preserve_uv_aspect.Properties.del_props(scene)
- select_uv.Properties.del_props(scene)
- smooth_uv.Properties.del_props(scene)
- texture_lock.Properties.del_props(scene)
- texture_projection.Properties.del_props(scene)
- texture_wrap.Properties.del_props(scene)
- transfer_uv.Properties.del_props(scene)
- unwrap_constraint.Properties.del_props(scene)
- uv_bounding_box.Properties.del_props(scene)
- uv_inspection.Properties.del_props(scene)
- uv_sculpt.Properties.del_props(scene)
- uvw.Properties.del_props(scene)
- world_scale_uv.Properties.del_props(scene)
-
+ PropertyClassRegistry.del_props(scene)
del scene.muv_props
diff --git a/uv_magic_uv/ui/IMAGE_MT_uvs.py b/uv_magic_uv/ui/IMAGE_MT_uvs.py
index 9beb7e2f..e7dda379 100644
--- a/uv_magic_uv/ui/IMAGE_MT_uvs.py
+++ b/uv_magic_uv/ui/IMAGE_MT_uvs.py
@@ -24,23 +24,19 @@ __version__ = "5.2"
__date__ = "17 Nov 2018"
import bpy
-from ..op import copy_paste_uv_uvedit
-from ..op import align_uv
-from ..op import uv_inspection
-from ..op import align_uv_cursor
-from ..op import select_uv
+from ..op import (
+ copy_paste_uv_uvedit,
+)
+from ..utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'MenuCopyPasteUVUVEdit',
- 'MenuAlignUV',
- 'MenuSelectUV',
- 'MenuAlignUVCursor',
- 'MenuUVInspection',
+ 'MUV_MT_CopyPasteUV_UVEdit',
]
-class MenuCopyPasteUVUVEdit(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_UVEdit(bpy.types.Menu):
"""
Menu class: Master menu of Copy/Paste UV coordinate on UV/ImageEditor
"""
@@ -52,135 +48,9 @@ class MenuCopyPasteUVUVEdit(bpy.types.Menu):
def draw(self, _):
layout = self.layout
- layout.operator(copy_paste_uv_uvedit.OperatorCopyUV.bl_idname,
- text="Copy")
- layout.operator(copy_paste_uv_uvedit.OperatorPasteUV.bl_idname,
- text="Paste")
-
-
-class MenuAlignUV(bpy.types.Menu):
- """
- Menu class: Master menu of Align UV
- """
-
- bl_idname = "uv.muv_align_uv_menu"
- bl_label = "Align UV"
- bl_description = "Align UV"
-
- def draw(self, context):
- layout = self.layout
- sc = context.scene
-
- ops = layout.operator(align_uv.OperatorCircle.bl_idname, text="Circle")
- ops.transmission = sc.muv_align_uv_transmission
- ops.select = sc.muv_align_uv_select
-
- ops = layout.operator(align_uv.OperatorStraighten.bl_idname,
- text="Straighten")
- ops.transmission = sc.muv_align_uv_transmission
- ops.select = sc.muv_align_uv_select
- ops.vertical = sc.muv_align_uv_vertical
- ops.horizontal = sc.muv_align_uv_horizontal
-
- ops = layout.operator(align_uv.OperatorAxis.bl_idname,
- text="XY-axis")
- ops.transmission = sc.muv_align_uv_transmission
- ops.select = sc.muv_align_uv_select
- ops.vertical = sc.muv_align_uv_vertical
- ops.horizontal = sc.muv_align_uv_horizontal
- ops.location = sc.muv_align_uv_location
-
-
-class MenuSelectUV(bpy.types.Menu):
- """
- Menu class: Master menu of Select UV
- """
-
- bl_idname = "uv.muv_select_uv_menu"
- bl_label = "Select UV"
- bl_description = "Select UV"
-
- def draw(self, _):
- layout = self.layout
-
- layout.operator(select_uv.OperatorSelectOverlapped.bl_idname,
- text="Overlapped")
- layout.operator(select_uv.OperatorSelectFlipped.bl_idname,
- text="Flipped")
-
-
-class MenuAlignUVCursor(bpy.types.Menu):
- """
- Menu class: Master menu of Align UV Cursor
- """
-
- bl_idname = "uv.muv_align_uv_cursor_menu"
- bl_label = "Align UV Cursor"
- bl_description = "Align UV cursor"
-
- def draw(self, context):
- layout = self.layout
- sc = context.scene
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Left Top")
- ops.position = 'LEFT_TOP'
- ops.base = sc.muv_align_uv_cursor_align_method
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Middle Top")
- ops.position = 'MIDDLE_TOP'
- ops.base = sc.muv_align_uv_cursor_align_method
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Right Top")
- ops.position = 'RIGHT_TOP'
- ops.base = sc.muv_align_uv_cursor_align_method
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Left Middle")
- ops.position = 'LEFT_MIDDLE'
- ops.base = sc.muv_align_uv_cursor_align_method
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Center")
- ops.position = 'CENTER'
- ops.base = sc.muv_align_uv_cursor_align_method
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Right Middle")
- ops.position = 'RIGHT_MIDDLE'
- ops.base = sc.muv_align_uv_cursor_align_method
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Left Bottom")
- ops.position = 'LEFT_BOTTOM'
- ops.base = sc.muv_align_uv_cursor_align_method
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Middle Bottom")
- ops.position = 'MIDDLE_BOTTOM'
- ops.base = sc.muv_align_uv_cursor_align_method
-
- ops = layout.operator(align_uv_cursor.Operator.bl_idname,
- text="Right Bottom")
- ops.position = 'RIGHT_BOTTOM'
- ops.base = sc.muv_align_uv_cursor_align_method
-
-
-class MenuUVInspection(bpy.types.Menu):
- """
- Menu class: Master menu of UV Inspection
- """
-
- bl_idname = "uv.muv_uv_inspection_menu"
- bl_label = "UV Inspection"
- bl_description = "UV Inspection"
-
- def draw(self, context):
- layout = self.layout
- sc = context.scene
-
- layout.prop(sc, "muv_uv_inspection_show", text="UV Inspection")
- layout.operator(uv_inspection.OperatorUpdate.bl_idname,
- text="Update")
+ layout.operator(
+ copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_CopyUV.bl_idname,
+ text="Copy")
+ layout.operator(
+ copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_PasteUV.bl_idname,
+ text="Paste")
diff --git a/uv_magic_uv/ui/VIEW3D_MT_object.py b/uv_magic_uv/ui/VIEW3D_MT_object.py
index c73157cc..318cd82c 100644
--- a/uv_magic_uv/ui/VIEW3D_MT_object.py
+++ b/uv_magic_uv/ui/VIEW3D_MT_object.py
@@ -24,15 +24,17 @@ __version__ = "5.2"
__date__ = "17 Nov 2018"
import bpy
-from ..op import copy_paste_uv_object
+from ..op import copy_paste_uv_object
+from ..utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'MenuCopyPasteUVObject',
+ 'MUV_MT_CopyPasteUV_Object',
]
-class MenuCopyPasteUVObject(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_Object(bpy.types.Menu):
"""
Menu class: Master menu of Copy/Paste UV coordinate among object
"""
@@ -44,7 +46,9 @@ class MenuCopyPasteUVObject(bpy.types.Menu):
def draw(self, _):
layout = self.layout
- layout.menu(copy_paste_uv_object.MenuCopyUV.bl_idname,
- text="Copy")
- layout.menu(copy_paste_uv_object.MenuPasteUV.bl_idname,
- text="Paste")
+ layout.menu(
+ copy_paste_uv_object.MUV_MT_CopyPasteUVObject_CopyUV.bl_idname,
+ text="Copy")
+ layout.menu(
+ copy_paste_uv_object.MUV_MT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="Paste")
diff --git a/uv_magic_uv/ui/VIEW3D_MT_uv_map.py b/uv_magic_uv/ui/VIEW3D_MT_uv_map.py
index bb59c12c..c5698504 100644
--- a/uv_magic_uv/ui/VIEW3D_MT_uv_map.py
+++ b/uv_magic_uv/ui/VIEW3D_MT_uv_map.py
@@ -23,30 +23,24 @@ __status__ = "production"
__version__ = "5.2"
__date__ = "17 Nov 2018"
-import bpy
-from ..op import copy_paste_uv
-from ..op import transfer_uv
-from ..op import texture_lock
-from ..op import world_scale_uv
-from ..op import uvw
-from ..op import texture_projection
-from ..op import texture_wrap
-from ..op import preserve_uv_aspect
+import bpy.utils
+from ..op import (
+ copy_paste_uv,
+ transfer_uv,
+ uvw,
+)
+from ..utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'MenuCopyPasteUV',
- 'MenuTransferUV',
- 'MenuTextureLock',
- 'MenuWorldScaleUV',
- 'MenuTextureWrap',
- 'MenuUVW',
- 'MenuTextureProjection',
- 'MenuPreserveUVAspect',
+ 'MUV_MT_CopyPasteUV',
+ 'MUV_MT_TransferUV',
+ 'MUV_MT_UVW',
]
-class MenuCopyPasteUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV(bpy.types.Menu):
"""
Menu class: Master menu of Copy/Paste UV coordinate
"""
@@ -58,20 +52,23 @@ class MenuCopyPasteUV(bpy.types.Menu):
def draw(self, _):
layout = self.layout
- layout.label("Default")
- layout.menu(copy_paste_uv.MenuCopyUV.bl_idname, text="Copy")
- layout.menu(copy_paste_uv.MenuPasteUV.bl_idname, text="Paste")
+ layout.label(text="Default")
+ layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_CopyUV.bl_idname,
+ text="Copy")
+ layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_PasteUV.bl_idname,
+ text="Paste")
layout.separator()
- layout.label("Selection Sequence")
- layout.menu(copy_paste_uv.MenuSelSeqCopyUV.bl_idname,
+ layout.label(text="Selection Sequence")
+ layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqCopyUV.bl_idname,
text="Copy")
- layout.menu(copy_paste_uv.MenuSelSeqPasteUV.bl_idname,
+ layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqPasteUV.bl_idname,
text="Paste")
-class MenuTransferUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_TransferUV(bpy.types.Menu):
"""
Menu class: Master menu of Transfer UV coordinate
"""
@@ -84,99 +81,16 @@ class MenuTransferUV(bpy.types.Menu):
layout = self.layout
sc = context.scene
- layout.operator(transfer_uv.OperatorCopyUV.bl_idname, text="Copy")
- ops = layout.operator(transfer_uv.OperatorPasteUV.bl_idname,
+ layout.operator(transfer_uv.MUV_OT_TransferUV_CopyUV.bl_idname,
+ text="Copy")
+ ops = layout.operator(transfer_uv.MUV_OT_TransferUV_PasteUV.bl_idname,
text="Paste")
ops.invert_normals = sc.muv_transfer_uv_invert_normals
ops.copy_seams = sc.muv_transfer_uv_copy_seams
-class MenuTextureLock(bpy.types.Menu):
- """
- Menu class: Master menu of Texture Lock
- """
-
- bl_idname = "uv.muv_texture_lock_menu"
- bl_label = "Texture Lock"
- bl_description = "Lock texture when vertices of mesh (Preserve UV)"
-
- def draw(self, context):
- layout = self.layout
- sc = context.scene
-
- layout.label("Normal Mode")
- layout.operator(texture_lock.OperatorLock.bl_idname,
- text="Lock"
- if not texture_lock.OperatorLock.is_ready(context)
- else "ReLock")
- ops = layout.operator(texture_lock.OperatorUnlock.bl_idname,
- text="Unlock")
- ops.connect = sc.muv_texture_lock_connect
-
- layout.separator()
-
- layout.label("Interactive Mode")
- layout.prop(sc, "muv_texture_lock_lock", text="Lock")
-
-
-class MenuWorldScaleUV(bpy.types.Menu):
- """
- Menu class: Master menu of world scale UV
- """
-
- bl_idname = "uv.muv_world_scale_uv_menu"
- bl_label = "World Scale UV"
- bl_description = ""
-
- def draw(self, context):
- layout = self.layout
- sc = context.scene
-
- layout.operator(world_scale_uv.OperatorMeasure.bl_idname,
- text="Measure")
-
- layout.operator(world_scale_uv.OperatorApplyManual.bl_idname,
- text="Apply (Manual)")
-
- ops = layout.operator(
- world_scale_uv.OperatorApplyScalingDensity.bl_idname,
- text="Apply (Same Desity)")
- ops.src_density = sc.muv_world_scale_uv_src_density
- ops.same_density = True
-
- ops = layout.operator(
- world_scale_uv.OperatorApplyScalingDensity.bl_idname,
- text="Apply (Scaling Desity)")
- ops.src_density = sc.muv_world_scale_uv_src_density
- ops.same_density = False
- ops.tgt_scaling_factor = sc.muv_world_scale_uv_tgt_scaling_factor
-
- ops = layout.operator(
- world_scale_uv.OperatorApplyProportionalToMesh.bl_idname,
- text="Apply (Proportional to Mesh)")
- ops.src_density = sc.muv_world_scale_uv_src_density
- ops.src_uv_area = sc.muv_world_scale_uv_src_uv_area
- ops.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area
- ops.origin = sc.muv_world_scale_uv_origin
-
-
-class MenuTextureWrap(bpy.types.Menu):
- """
- Menu class: Master menu of Texture Wrap
- """
-
- bl_idname = "uv.muv_texture_wrap_menu"
- bl_label = "Texture Wrap"
- bl_description = ""
-
- def draw(self, _):
- layout = self.layout
-
- layout.operator(texture_wrap.OperatorRefer.bl_idname, text="Refer")
- layout.operator(texture_wrap.OperatorSet.bl_idname, text="Set")
-
-
-class MenuUVW(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_UVW(bpy.types.Menu):
"""
Menu class: Master menu of UVW
"""
@@ -189,48 +103,9 @@ class MenuUVW(bpy.types.Menu):
layout = self.layout
sc = context.scene
- ops = layout.operator(uvw.OperatorBoxMap.bl_idname, text="Box")
+ ops = layout.operator(uvw.MUV_OT_UVW_BoxMap.bl_idname, text="Box")
ops.assign_uvmap = sc.muv_uvw_assign_uvmap
- ops = layout.operator(uvw.OperatorBestPlanerMap.bl_idname,
+ ops = layout.operator(uvw.MUV_OT_UVW_BestPlanerMap.bl_idname,
text="Best Planner")
ops.assign_uvmap = sc.muv_uvw_assign_uvmap
-
-
-class MenuTextureProjection(bpy.types.Menu):
- """
- Menu class: Master menu of Texture Projection
- """
-
- bl_idname = "uv.muv_texture_projection_menu"
- bl_label = "Texture Projection"
- bl_description = ""
-
- def draw(self, context):
- layout = self.layout
- sc = context.scene
-
- layout.prop(sc, "muv_texture_projection_enable",
- text="Texture Projection")
- layout.operator(texture_projection.OperatorProject.bl_idname,
- text="Project")
-
-
-class MenuPreserveUVAspect(bpy.types.Menu):
- """
- Menu class: Master menu of Preserve UV Aspect
- """
-
- bl_idname = "uv.muv_preserve_uv_aspect_menu"
- bl_label = "Preserve UV Aspect"
- bl_description = ""
-
- def draw(self, context):
- layout = self.layout
- sc = context.scene
-
- for key in bpy.data.images.keys():
- ops = layout.operator(
- preserve_uv_aspect.Operator.bl_idname, text=key)
- ops.dest_img_name = key
- ops.origin = sc.muv_preserve_uv_aspect_origin
diff --git a/uv_magic_uv/ui/__init__.py b/uv_magic_uv/ui/__init__.py
index b377ed23..5f7e0c5e 100644
--- a/uv_magic_uv/ui/__init__.py
+++ b/uv_magic_uv/ui/__init__.py
@@ -25,26 +25,22 @@ __date__ = "17 Nov 2018"
if "bpy" in locals():
import importlib
- importlib.reload(view3d_copy_paste_uv_objectmode)
importlib.reload(view3d_copy_paste_uv_editmode)
+ importlib.reload(view3d_copy_paste_uv_objectmode)
importlib.reload(view3d_uv_manipulation)
importlib.reload(view3d_uv_mapping)
importlib.reload(uvedit_copy_paste_uv)
- importlib.reload(uvedit_uv_manipulation)
- importlib.reload(uvedit_editor_enhancement)
- importlib.reload(VIEW3D_MT_uv_map)
importlib.reload(VIEW3D_MT_object)
+ importlib.reload(VIEW3D_MT_uv_map)
importlib.reload(IMAGE_MT_uvs)
else:
- from . import view3d_copy_paste_uv_objectmode
from . import view3d_copy_paste_uv_editmode
+ from . import view3d_copy_paste_uv_objectmode
from . import view3d_uv_manipulation
from . import view3d_uv_mapping
from . import uvedit_copy_paste_uv
- from . import uvedit_uv_manipulation
- from . import uvedit_editor_enhancement
- from . import VIEW3D_MT_uv_map
from . import VIEW3D_MT_object
+ from . import VIEW3D_MT_uv_map
from . import IMAGE_MT_uvs
import bpy
diff --git a/uv_magic_uv/ui/uvedit_copy_paste_uv.py b/uv_magic_uv/ui/uvedit_copy_paste_uv.py
index 271277a0..e21a5abd 100644
--- a/uv_magic_uv/ui/uvedit_copy_paste_uv.py
+++ b/uv_magic_uv/ui/uvedit_copy_paste_uv.py
@@ -26,20 +26,21 @@ __date__ = "17 Nov 2018"
import bpy
from ..op import copy_paste_uv_uvedit
-
+from ..utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'PanelCopyPasteUV',
+ 'MUV_PT_UVEdit_CopyPasteUV',
]
-class PanelCopyPasteUV(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_UVEdit_CopyPasteUV(bpy.types.Panel):
"""
Panel class: Copy/Paste UV on Property Panel on UV/ImageEditor
"""
bl_space_type = 'IMAGE_EDITOR'
- bl_region_type = 'TOOLS'
+ bl_region_type = 'UI'
bl_label = "Copy/Paste UV"
bl_category = "Magic UV"
bl_context = 'mesh_edit'
@@ -47,13 +48,15 @@ class PanelCopyPasteUV(bpy.types.Panel):
def draw_header(self, _):
layout = self.layout
- layout.label(text="", icon='IMAGE_COL')
+ layout.label(text="", icon='IMAGE')
def draw(self, _):
layout = self.layout
row = layout.row(align=True)
- row.operator(copy_paste_uv_uvedit.OperatorCopyUV.bl_idname,
- text="Copy")
- row.operator(copy_paste_uv_uvedit.OperatorPasteUV.bl_idname,
- text="Paste")
+ row.operator(
+ copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_CopyUV.bl_idname,
+ text="Copy")
+ row.operator(
+ copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_PasteUV.bl_idname,
+ text="Paste")
diff --git a/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
index c5906ca0..14fba24a 100644
--- a/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
+++ b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
@@ -25,22 +25,25 @@ __date__ = "17 Nov 2018"
import bpy
-from ..op import copy_paste_uv
-from ..op import transfer_uv
-
+from ..op import (
+ copy_paste_uv,
+ transfer_uv,
+)
+from ..utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'PanelCopyPasteUVEditMode',
+ 'MUV_PT_CopyPasteUVEditMode',
]
-class PanelCopyPasteUVEditMode(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_CopyPasteUVEditMode(bpy.types.Panel):
"""
Panel class: Copy/Paste UV on Property Panel on View3D
"""
bl_space_type = 'VIEW_3D'
- bl_region_type = 'TOOLS'
+ bl_region_type = 'UI'
bl_label = "Copy/Paste UV"
bl_category = "Magic UV"
bl_context = 'mesh_edit'
@@ -48,7 +51,7 @@ class PanelCopyPasteUVEditMode(bpy.types.Panel):
def draw_header(self, _):
layout = self.layout
- layout.label(text="", icon='IMAGE_COL')
+ layout.label(text="", icon='IMAGE')
def draw(self, context):
sc = context.scene
@@ -59,15 +62,17 @@ class PanelCopyPasteUVEditMode(bpy.types.Panel):
if sc.muv_copy_paste_uv_enabled:
row = box.row(align=True)
if sc.muv_copy_paste_uv_mode == 'DEFAULT':
- row.menu(copy_paste_uv.MenuCopyUV.bl_idname,
+ row.menu(copy_paste_uv.MUV_MT_CopyPasteUV_CopyUV.bl_idname,
text="Copy")
- row.menu(copy_paste_uv.MenuPasteUV.bl_idname,
+ row.menu(copy_paste_uv.MUV_MT_CopyPasteUV_PasteUV.bl_idname,
text="Paste")
elif sc.muv_copy_paste_uv_mode == 'SEL_SEQ':
- row.menu(copy_paste_uv.MenuSelSeqCopyUV.bl_idname,
- text="Copy")
- row.menu(copy_paste_uv.MenuSelSeqPasteUV.bl_idname,
- text="Paste")
+ row.menu(
+ copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+ text="Copy")
+ row.menu(
+ copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+ text="Paste")
box.prop(sc, "muv_copy_paste_uv_mode", expand=True)
box.prop(sc, "muv_copy_paste_uv_copy_seams", text="Seams")
box.prop(sc, "muv_copy_paste_uv_strategy", text="Strategy")
@@ -76,8 +81,9 @@ class PanelCopyPasteUVEditMode(bpy.types.Panel):
box.prop(sc, "muv_transfer_uv_enabled", text="Transfer UV")
if sc.muv_transfer_uv_enabled:
row = box.row(align=True)
- row.operator(transfer_uv.OperatorCopyUV.bl_idname, text="Copy")
- ops = row.operator(transfer_uv.OperatorPasteUV.bl_idname,
+ row.operator(transfer_uv.MUV_OT_TransferUV_CopyUV.bl_idname,
+ text="Copy")
+ ops = row.operator(transfer_uv.MUV_OT_TransferUV_PasteUV.bl_idname,
text="Paste")
ops.invert_normals = sc.muv_transfer_uv_invert_normals
ops.copy_seams = sc.muv_transfer_uv_copy_seams
diff --git a/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
index a9203d87..6dd0d3b4 100644
--- a/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
+++ b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
@@ -26,20 +26,21 @@ __date__ = "17 Nov 2018"
import bpy
from ..op import copy_paste_uv_object
-
+from ..utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'PanelCopyPasteUVObjectMode',
+ 'MUV_PT_View3D_Object_CopyPasteUV',
]
-class PanelCopyPasteUVObjectMode(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_View3D_Object_CopyPasteUV(bpy.types.Panel):
"""
Panel class: Copy/Paste UV on Property Panel on View3D
"""
bl_space_type = 'VIEW_3D'
- bl_region_type = 'TOOLS'
+ bl_region_type = 'UI'
bl_label = "Copy/Paste UV"
bl_category = "Magic UV"
bl_context = 'objectmode'
@@ -47,16 +48,18 @@ class PanelCopyPasteUVObjectMode(bpy.types.Panel):
def draw_header(self, _):
layout = self.layout
- layout.label(text="", icon='IMAGE_COL')
+ layout.label(text="", icon='IMAGE')
def draw(self, context):
sc = context.scene
layout = self.layout
row = layout.row(align=True)
- row.menu(copy_paste_uv_object.MenuCopyUV.bl_idname,
- text="Copy")
- row.menu(copy_paste_uv_object.MenuPasteUV.bl_idname,
- text="Paste")
+ row.menu(
+ copy_paste_uv_object.MUV_MT_CopyPasteUVObject_CopyUV.bl_idname,
+ text="Copy")
+ row.menu(
+ copy_paste_uv_object.MUV_MT_CopyPasteUVObject_PasteUV.bl_idname,
+ text="Paste")
layout.prop(sc, "muv_copy_paste_uv_object_copy_seams",
text="Seams")
diff --git a/uv_magic_uv/ui/view3d_uv_manipulation.py b/uv_magic_uv/ui/view3d_uv_manipulation.py
index be0bcf57..365a0dc8 100644
--- a/uv_magic_uv/ui/view3d_uv_manipulation.py
+++ b/uv_magic_uv/ui/view3d_uv_manipulation.py
@@ -25,28 +25,26 @@ __date__ = "17 Nov 2018"
import bpy
-from ..op import flip_rotate_uv
-from ..op import mirror_uv
-from ..op import move_uv
-from ..op import preserve_uv_aspect
-from ..op import texture_lock
-from ..op import texture_wrap
-from ..op import uv_sculpt
-from ..op import world_scale_uv
-
+from ..op import (
+ flip_rotate_uv,
+ mirror_uv,
+ move_uv,
+)
+from ..utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'PanelUVManipulation',
+ 'MUV_PT_View3D_UVManipulation',
]
-class PanelUVManipulation(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_View3D_UVManipulation(bpy.types.Panel):
"""
Panel class: UV Manipulation on Property Panel on View3D
"""
bl_space_type = 'VIEW_3D'
- bl_region_type = 'TOOLS'
+ bl_region_type = 'UI'
bl_label = "UV Manipulation"
bl_category = "Magic UV"
bl_context = 'mesh_edit'
@@ -54,7 +52,7 @@ class PanelUVManipulation(bpy.types.Panel):
def draw_header(self, _):
layout = self.layout
- layout.label(text="", icon='IMAGE_COL')
+ layout.label(text="", icon='IMAGE')
def draw(self, context):
sc = context.scene
@@ -64,7 +62,7 @@ class PanelUVManipulation(bpy.types.Panel):
box.prop(sc, "muv_flip_rotate_uv_enabled", text="Flip/Rotate UV")
if sc.muv_flip_rotate_uv_enabled:
row = box.row()
- ops = row.operator(flip_rotate_uv.Operator.bl_idname,
+ ops = row.operator(flip_rotate_uv.MUV_OT_FlipRotate.bl_idname,
text="Flip/Rotate")
ops.seams = sc.muv_flip_rotate_uv_seams
row.prop(sc, "muv_flip_rotate_uv_seams", text="Seams")
@@ -73,7 +71,8 @@ class PanelUVManipulation(bpy.types.Panel):
box.prop(sc, "muv_mirror_uv_enabled", text="Mirror UV")
if sc.muv_mirror_uv_enabled:
row = box.row()
- ops = row.operator(mirror_uv.Operator.bl_idname, text="Mirror")
+ ops = row.operator(mirror_uv.MUV_OT_MirrorUV.bl_idname,
+ text="Mirror")
ops.axis = sc.muv_mirror_uv_axis
row.prop(sc, "muv_mirror_uv_axis", text="")
@@ -81,191 +80,9 @@ class PanelUVManipulation(bpy.types.Panel):
box.prop(sc, "muv_move_uv_enabled", text="Move UV")
if sc.muv_move_uv_enabled:
col = box.column()
- if not move_uv.Operator.is_running(context):
- col.operator(move_uv.Operator.bl_idname, icon='PLAY',
+ if not move_uv.MUV_OT_MoveUV.is_running(context):
+ col.operator(move_uv.MUV_OT_MoveUV.bl_idname, icon='PLAY',
text="Start")
else:
- col.operator(move_uv.Operator.bl_idname, icon='PAUSE',
+ col.operator(move_uv.MUV_OT_MoveUV.bl_idname, icon='PAUSE',
text="Stop")
-
- box = layout.box()
- box.prop(sc, "muv_world_scale_uv_enabled", text="World Scale UV")
- if sc.muv_world_scale_uv_enabled:
- box.prop(sc, "muv_world_scale_uv_mode", text="")
-
- if sc.muv_world_scale_uv_mode == 'MANUAL':
- sp = box.split(percentage=0.5)
- col = sp.column()
- col.prop(sc, "muv_world_scale_uv_tgt_texture_size",
- text="Texture Size")
- sp = sp.split(percentage=1.0)
- col = sp.column()
- col.label("Density:")
- col.prop(sc, "muv_world_scale_uv_tgt_density", text="")
- box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
- ops = box.operator(
- world_scale_uv.OperatorApplyManual.bl_idname, text="Apply")
- ops.tgt_density = sc.muv_world_scale_uv_tgt_density
- ops.tgt_texture_size = sc.muv_world_scale_uv_tgt_texture_size
- ops.origin = sc.muv_world_scale_uv_origin
- ops.show_dialog = False
-
- elif sc.muv_world_scale_uv_mode == 'SAME_DENSITY':
- sp = box.split(percentage=0.4)
- col = sp.column(align=True)
- col.label("Source:")
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.operator(world_scale_uv.OperatorMeasure.bl_idname,
- text="Measure")
-
- sp = box.split(percentage=0.7)
- col = sp.column(align=True)
- col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
- col.enabled = False
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.label("px2/cm2")
-
- box.separator()
- box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
- ops = box.operator(
- world_scale_uv.OperatorApplyScalingDensity.bl_idname,
- text="Apply")
- ops.src_density = sc.muv_world_scale_uv_src_density
- ops.origin = sc.muv_world_scale_uv_origin
- ops.same_density = True
- ops.show_dialog = False
-
- elif sc.muv_world_scale_uv_mode == 'SCALING_DENSITY':
- sp = box.split(percentage=0.4)
- col = sp.column(align=True)
- col.label("Source:")
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.operator(world_scale_uv.OperatorMeasure.bl_idname,
- text="Measure")
-
- sp = box.split(percentage=0.7)
- col = sp.column(align=True)
- col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
- col.enabled = False
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.label("px2/cm2")
-
- box.separator()
- box.prop(sc, "muv_world_scale_uv_tgt_scaling_factor",
- text="Scaling Factor")
- box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
- ops = box.operator(
- world_scale_uv.OperatorApplyScalingDensity.bl_idname,
- text="Apply")
- ops.src_density = sc.muv_world_scale_uv_src_density
- ops.origin = sc.muv_world_scale_uv_origin
- ops.same_density = False
- ops.show_dialog = False
- ops.tgt_scaling_factor = \
- sc.muv_world_scale_uv_tgt_scaling_factor
-
- elif sc.muv_world_scale_uv_mode == 'PROPORTIONAL_TO_MESH':
- sp = box.split(percentage=0.4)
- col = sp.column(align=True)
- col.label("Source:")
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.operator(world_scale_uv.OperatorMeasure.bl_idname,
- text="Measure")
-
- sp = box.split(percentage=0.7)
- col = sp.column(align=True)
- col.prop(sc, "muv_world_scale_uv_src_mesh_area",
- text="Mesh Area")
- col.prop(sc, "muv_world_scale_uv_src_uv_area", text="UV Area")
- col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
- col.enabled = False
- sp = sp.split(percentage=1.0)
- col = sp.column(align=True)
- col.label("cm2")
- col.label("px2")
- col.label("px2/cm2")
- col.enabled = False
-
- box.separator()
- box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
- ops = box.operator(
- world_scale_uv.OperatorApplyProportionalToMesh.bl_idname,
- text="Apply")
- ops.src_density = sc.muv_world_scale_uv_src_density
- ops.src_uv_area = sc.muv_world_scale_uv_src_uv_area
- ops.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area
- ops.origin = sc.muv_world_scale_uv_origin
- ops.show_dialog = False
-
- box = layout.box()
- box.prop(sc, "muv_preserve_uv_aspect_enabled",
- text="Preserve UV Aspect")
- if sc.muv_preserve_uv_aspect_enabled:
- row = box.row()
- ops = row.operator(
- preserve_uv_aspect.Operator.bl_idname,
- text="Change Image")
- ops.dest_img_name = sc.muv_preserve_uv_aspect_tex_image
- ops.origin = sc.muv_preserve_uv_aspect_origin
- row.prop(sc, "muv_preserve_uv_aspect_tex_image", text="")
- box.prop(sc, "muv_preserve_uv_aspect_origin", text="Origin")
-
- box = layout.box()
- box.prop(sc, "muv_texture_lock_enabled", text="Texture Lock")
- if sc.muv_texture_lock_enabled:
- row = box.row(align=True)
- col = row.column(align=True)
- col.label("Normal Mode:")
- col = row.column(align=True)
- col.operator(texture_lock.OperatorLock.bl_idname,
- text="Lock"
- if not texture_lock.OperatorLock.is_ready(context)
- else "ReLock")
- ops = col.operator(texture_lock.OperatorUnlock.bl_idname,
- text="Unlock")
- ops.connect = sc.muv_texture_lock_connect
- col.prop(sc, "muv_texture_lock_connect", text="Connect")
-
- row = box.row(align=True)
- row.label("Interactive Mode:")
- box.prop(sc, "muv_texture_lock_lock",
- text="Unlock"
- if texture_lock.OperatorIntr.is_running(context)
- else "Lock",
- icon='RESTRICT_VIEW_OFF'
- if texture_lock.OperatorIntr.is_running(context)
- else 'RESTRICT_VIEW_ON')
-
- box = layout.box()
- box.prop(sc, "muv_texture_wrap_enabled", text="Texture Wrap")
- if sc.muv_texture_wrap_enabled:
- row = box.row(align=True)
- row.operator(texture_wrap.OperatorRefer.bl_idname, text="Refer")
- row.operator(texture_wrap.OperatorSet.bl_idname, text="Set")
- box.prop(sc, "muv_texture_wrap_set_and_refer")
- box.prop(sc, "muv_texture_wrap_selseq")
-
- box = layout.box()
- box.prop(sc, "muv_uv_sculpt_enabled", text="UV Sculpt")
- if sc.muv_uv_sculpt_enabled:
- box.prop(sc, "muv_uv_sculpt_enable",
- text="Disable"if uv_sculpt.Operator.is_running(context)
- else "Enable",
- icon='RESTRICT_VIEW_OFF'
- if uv_sculpt.Operator.is_running(context)
- else 'RESTRICT_VIEW_ON')
- col = box.column()
- col.label("Brush:")
- col.prop(sc, "muv_uv_sculpt_radius")
- col.prop(sc, "muv_uv_sculpt_strength")
- box.prop(sc, "muv_uv_sculpt_tools")
- if sc.muv_uv_sculpt_tools == 'PINCH':
- box.prop(sc, "muv_uv_sculpt_pinch_invert")
- elif sc.muv_uv_sculpt_tools == 'RELAX':
- box.prop(sc, "muv_uv_sculpt_relax_method")
- box.prop(sc, "muv_uv_sculpt_show_brush")
diff --git a/uv_magic_uv/ui/view3d_uv_mapping.py b/uv_magic_uv/ui/view3d_uv_mapping.py
index 2aa62c26..c596008e 100644
--- a/uv_magic_uv/ui/view3d_uv_mapping.py
+++ b/uv_magic_uv/ui/view3d_uv_mapping.py
@@ -25,23 +25,24 @@ __date__ = "17 Nov 2018"
import bpy
-from ..op import texture_projection
-from ..op import unwrap_constraint
-from ..op import uvw
-
+from ..op import (
+ uvw,
+)
+from ..utils.bl_class_registry import BlClassRegistry
__all__ = [
- 'UVMapping',
+ 'MUV_PT_View3D_UVMapping',
]
-class UVMapping(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_View3D_UVMapping(bpy.types.Panel):
"""
Panel class: UV Mapping on Property Panel on View3D
"""
bl_space_type = 'VIEW_3D'
- bl_region_type = 'TOOLS'
+ bl_region_type = 'UI'
bl_label = "UV Mapping"
bl_category = "Magic UV"
bl_context = 'mesh_edit'
@@ -49,60 +50,19 @@ class UVMapping(bpy.types.Panel):
def draw_header(self, _):
layout = self.layout
- layout.label(text="", icon='IMAGE_COL')
+ layout.label(text="", icon='IMAGE')
def draw(self, context):
sc = context.scene
layout = self.layout
box = layout.box()
- box.prop(sc, "muv_unwrap_constraint_enabled", text="Unwrap Constraint")
- if sc.muv_unwrap_constraint_enabled:
- ops = box.operator(
- unwrap_constraint.Operator.bl_idname,
- text="Unwrap")
- ops.u_const = sc.muv_unwrap_constraint_u_const
- ops.v_const = sc.muv_unwrap_constraint_v_const
- row = box.row(align=True)
- row.prop(sc, "muv_unwrap_constraint_u_const", text="U-Constraint")
- row.prop(sc, "muv_unwrap_constraint_v_const", text="V-Constraint")
-
- box = layout.box()
- box.prop(sc, "muv_texture_projection_enabled",
- text="Texture Projection")
- if sc.muv_texture_projection_enabled:
- row = box.row()
- row.prop(sc, "muv_texture_projection_enable",
- text="Disable"
- if texture_projection.Operator.is_running(context)
- else "Enable",
- icon='RESTRICT_VIEW_OFF'
- if texture_projection.Operator.is_running(context)
- else 'RESTRICT_VIEW_ON')
- row.prop(sc, "muv_texture_projection_tex_image", text="")
- box.prop(sc, "muv_texture_projection_tex_transparency",
- text="Transparency")
- col = box.column(align=True)
- row = col.row()
- row.prop(sc, "muv_texture_projection_adjust_window",
- text="Adjust Window")
- if not sc.muv_texture_projection_adjust_window:
- row.prop(sc, "muv_texture_projection_tex_magnitude",
- text="Magnitude")
- col.prop(sc, "muv_texture_projection_apply_tex_aspect",
- text="Texture Aspect Ratio")
- col.prop(sc, "muv_texture_projection_assign_uvmap",
- text="Assign UVMap")
- box.operator(texture_projection.OperatorProject.bl_idname,
- text="Project")
-
- box = layout.box()
box.prop(sc, "muv_uvw_enabled", text="UVW")
if sc.muv_uvw_enabled:
row = box.row(align=True)
- ops = row.operator(uvw.OperatorBoxMap.bl_idname, text="Box")
+ ops = row.operator(uvw.MUV_OT_UVW_BoxMap.bl_idname, text="Box")
ops.assign_uvmap = sc.muv_uvw_assign_uvmap
- ops = row.operator(uvw.OperatorBestPlanerMap.bl_idname,
+ ops = row.operator(uvw.MUV_OT_UVW_BestPlanerMap.bl_idname,
text="Best Planner")
ops.assign_uvmap = sc.muv_uvw_assign_uvmap
box.prop(sc, "muv_uvw_assign_uvmap", text="Assign UVMap")
diff --git a/uv_magic_uv/utils/__init__.py b/uv_magic_uv/utils/__init__.py
new file mode 100644
index 00000000..4ce9d907
--- /dev/null
+++ b/uv_magic_uv/utils/__init__.py
@@ -0,0 +1,34 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+if "bpy" in locals():
+ import importlib
+ importlib.reload(bl_class_registry)
+ importlib.reload(property_class_registry)
+else:
+ from . import bl_class_registry
+ from . import property_class_registry
+
+import bpy
diff --git a/uv_magic_uv/utils/bl_class_registry.py b/uv_magic_uv/utils/bl_class_registry.py
new file mode 100644
index 00000000..d1730615
--- /dev/null
+++ b/uv_magic_uv/utils/bl_class_registry.py
@@ -0,0 +1,84 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from .. import common
+
+__all__ = [
+ 'BlClassRegistry',
+]
+
+
+class BlClassRegistry:
+ class_list = []
+
+ def __init__(self, *_, **kwargs):
+ self.legacy = kwargs.get('legacy', False)
+
+ def __call__(self, cls):
+ if hasattr(cls, "bl_idname"):
+ BlClassRegistry.add_class(cls.bl_idname, cls, self.legacy)
+ else:
+ bl_idname = "{}{}{}{}".format(cls.bl_space_type,
+ cls.bl_region_type,
+ cls.bl_context, cls.bl_label)
+ BlClassRegistry.add_class(bl_idname, cls, self.legacy)
+ return cls
+
+ @classmethod
+ def add_class(cls, bl_idname, op_class, legacy):
+ for class_ in cls.class_list:
+ if (class_["bl_idname"] == bl_idname) and \
+ (class_["legacy"] == legacy):
+ raise RuntimeError("{} is already registered"
+ .format(bl_idname))
+
+ new_op = {
+ "bl_idname": bl_idname,
+ "class": op_class,
+ "legacy": legacy,
+ }
+ cls.class_list.append(new_op)
+ common.debug_print("{} is registered.".format(bl_idname))
+
+ @classmethod
+ def register(cls):
+ for class_ in cls.class_list:
+ bpy.utils.register_class(class_["class"])
+ common.debug_print("{} is registered to Blender."
+ .format(class_["bl_idname"]))
+
+ @classmethod
+ def unregister(cls):
+ for class_ in cls.class_list:
+ bpy.utils.unregister_class(class_["class"])
+ common.debug_print("{} is unregistered from Blender."
+ .format(class_["bl_idname"]))
+
+ @classmethod
+ def cleanup(cls):
+ cls.class_list = []
+ common.debug_print("Cleanup registry.")
diff --git a/uv_magic_uv/utils/property_class_registry.py b/uv_magic_uv/utils/property_class_registry.py
new file mode 100644
index 00000000..20df0342
--- /dev/null
+++ b/uv_magic_uv/utils/property_class_registry.py
@@ -0,0 +1,72 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+from .. import common
+
+__all__ = [
+ 'PropertyClassRegistry',
+]
+
+
+class PropertyClassRegistry:
+ class_list = []
+
+ def __init__(self, *_, **kwargs):
+ self.legacy = kwargs.get('legacy', False)
+
+ def __call__(self, cls):
+ PropertyClassRegistry.add_class(cls.idname, cls, self.legacy)
+ return cls
+
+ @classmethod
+ def add_class(cls, idname, prop_class, legacy):
+ for class_ in cls.class_list:
+ if (class_["idname"] == idname) and (class_["legacy"] == legacy):
+ raise RuntimeError("{} is already registered".format(idname))
+
+ new_op = {
+ "idname": idname,
+ "class": prop_class,
+ "legacy": legacy,
+ }
+ cls.class_list.append(new_op)
+ common.debug_print("{} is registered.".format(idname))
+
+ @classmethod
+ def init_props(cls, scene):
+ for class_ in cls.class_list:
+ class_["class"].init_props(scene)
+ common.debug_print("{} is initialized.".format(class_["idname"]))
+
+ @classmethod
+ def del_props(cls, scene):
+ for class_ in cls.class_list:
+ class_["class"].del_props(scene)
+ common.debug_print("{} is cleared.".format(class_["idname"]))
+
+ @classmethod
+ def cleanup(cls):
+ cls.class_list = []
+ common.debug_print("Cleanup registry.")