diff --git a/uv_magic_uv/__init__.py b/uv_magic_uv/__init__.py index 61fcc8049b692f240755d5436f7168193e2e4410..ae0317ed8d15ec21e46e21d2c0a33bfa2a3e9a55 100644 --- a/uv_magic_uv/__init__.py +++ b/uv_magic_uv/__init__.py @@ -41,47 +41,24 @@ 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(common) importlib.reload(utils) utils.bl_class_registry.BlClassRegistry.cleanup() - if check_version(2, 80, 0) >= 0: - importlib.reload(lib) - importlib.reload(op) - importlib.reload(ui) - importlib.reload(properites) - importlib.reload(preferences) - else: - importlib.reload(legacy) - importlib.reload(impl) + importlib.reload(op) + importlib.reload(ui) + importlib.reload(properites) + importlib.reload(preferences) else: import bpy from . import common from . import utils - if check_version(2, 80, 0) >= 0: - from . import lib - from . import op - from . import ui - from . import properites - from . import preferences - else: - from . import legacy - from . import impl + from . import op + from . import ui + from . import properites + from . import preferences import os @@ -95,9 +72,8 @@ def register_updater(bl_info): config.current_addon_path = os.path.dirname(os.path.realpath(__file__)) config.branches = ["master", "develop"] config.addon_directory = config.current_addon_path[:config.current_addon_path.rfind("/")] - #config.min_release_version = bl_info["version"] - config.min_release_version = (5, 1) - config.target_addon_path = "uv_magic_uv" + config.min_release_version = bl_info["version"] + config.target_addon_path = "src/uv_magic_uv" updater = utils.addon_updator.AddonUpdatorManager.get_instance() updater.init(bl_info, config) @@ -105,29 +81,17 @@ def register_updater(bl_info): def register(): register_updater(bl_info) - if common.check_version(2, 80, 0) >= 0: - utils.bl_class_registry.BlClassRegistry.register() - properites.init_props(bpy.types.Scene) - if bpy.context.user_preferences.addons['uv_magic_uv'].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() + utils.bl_class_registry.BlClassRegistry.register() + properites.init_props(bpy.types.Scene) + if utils.compatibility.get_user_preferences(bpy.context).addons['uv_magic_uv'].preferences.enable_builtin_menu: + preferences.add_builtin_menu() def unregister(): - if common.check_version(2, 80, 0) >= 0: - if bpy.context.user_preferences.addons['uv_magic_uv'].preferences.enable_builtin_menu: - preferences.remove_builtin_menu() - properites.clear_props(bpy.types.Scene) - utils.bl_class_registry.BlClassRegistry.unregister() - else: - 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 utils.compatibility.get_user_preferences(bpy.context).addons['uv_magic_uv'].preferences.enable_builtin_menu: + preferences.remove_builtin_menu() + 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 83c6ae741e56e38003765f0eb9690edc312f68f6..961ce447898b354bef34eef5eb105e7793d438e3 100644 --- a/uv_magic_uv/common.py +++ b/uv_magic_uv/common.py @@ -32,37 +32,10 @@ import bpy from mathutils import Vector import bmesh +from .utils import compatibility as compat -__all__ = [ - 'is_console_mode', - 'is_debug_mode', - 'enable_debugg_mode', - 'disable_debug_mode', - 'debug_print', - 'check_version', - 'redraw_all_areas', - 'get_space_legacy', - 'mouse_on_region_legacy', - 'mouse_on_area_legacy', - 'mouse_on_regions_legacy', - 'create_bmesh', - 'create_new_uv_map', - 'get_island_info', - 'get_island_info_from_bmesh', - 'get_island_info_from_faces', - 'get_uvimg_editor_board_size', - 'calc_polygon_2d_area', - 'calc_polygon_3d_area', - 'measure_mesh_area', - 'measure_uv_area_legacy', - 'diff_point_to_segment', - 'get_loop_sequences', - 'get_overlapped_uv_info', - 'get_flipped_uv_info', -] - - -__DEBUG_MODE = True + +__DEBUG_MODE = False def is_console_mode(): @@ -119,70 +92,6 @@ def redraw_all_areas(): area.tag_redraw() -def get_space_legacy(area_type, region_type, space_type): - """ - Get current area/region/space - """ - - area = None - region = None - space = None - - for area in bpy.context.screen.areas: - if area.type == area_type: - break - else: - return (None, None, None) - for region in area.regions: - if region.type == region_type: - break - for space in area.spaces: - if space.type == space_type: - break - - return (area, region, space) - - -def mouse_on_region_legacy(event, area_type, region_type): - pos = Vector((event.mouse_x, event.mouse_y)) - - _, region, _ = get_space_legacy(area_type, region_type, "") - if region is None: - return False - - if (pos.x > region.x) and (pos.x < region.x + region.width) and \ - (pos.y > region.y) and (pos.y < region.y + region.height): - return True - - return False - - -def mouse_on_area_legacy(event, area_type): - pos = Vector((event.mouse_x, event.mouse_y)) - - area, _, _ = get_space_legacy(area_type, "", "") - if area is None: - return False - - if (pos.x > area.x) and (pos.x < area.x + area.width) and \ - (pos.y > area.y) and (pos.y < area.y + area.height): - return True - - return False - - -def mouse_on_regions_legacy(event, area_type, regions): - if not mouse_on_area_legacy(event, area_type): - return False - - for region in regions: - result = mouse_on_region_legacy(event, area_type, region) - if result: - return True - - return False - - def get_space(area_type, region_type, space_type): """ Get current area/region/space @@ -199,8 +108,9 @@ def get_space(area_type, region_type, space_type): return (None, None, None) for region in area.regions: if region.type == region_type: - if region.width <= 1 or region.height <= 1: - continue + if compat.check_version(2, 80, 0) >= 0: + if region.width <= 1 or region.height <= 1: + continue break else: return (area, None, None) @@ -460,65 +370,6 @@ def measure_mesh_area(obj): return mesh_area -def measure_uv_area_legacy(obj, tex_size=None): - bm = bmesh.from_edit_mesh(obj.data) - if check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - if not bm.loops.layers.uv: - return None - uv_layer = bm.loops.layers.uv.verify() - - if not bm.faces.layers.tex: - return None - tex_layer = bm.faces.layers.tex.verify() - - sel_faces = [f for f in bm.faces if f.select] - - # measure - uv_area = 0.0 - for f in sel_faces: - uvs = [l[uv_layer].uv for l in f.loops] - f_uv_area = calc_polygon_2d_area(uvs) - - # user specified - if tex_size: - uv_area = uv_area + f_uv_area * tex_size[0] * tex_size[1] - continue - - # try to find from texture_layer - img = None - if tex_layer: - img = f[tex_layer].image - - # not found, then try to search from node - if not img: - for mat in obj.material_slots: - if not mat.material.node_tree: - continue - for node in mat.material.node_tree.nodes: - tex_node_types = [ - 'TEX_ENVIRONMENT', - 'TEX_IMAGE', - ] - if node.type not in tex_node_types: - continue - if not node.image: - continue - img = node.image - - # can not find from node, so we can not get texture size - if not img: - return None - - img_size = img.size - uv_area = uv_area + f_uv_area * img_size[0] * img_size[1] - - return uv_area - - def find_texture_layer(bm): if check_version(2, 80, 0) >= 0: return None diff --git a/uv_magic_uv/impl/__init__.py b/uv_magic_uv/impl/__init__.py deleted file mode 100644 index d22125af4321896496a08c18f692a1327cef8f8d..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -# <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_cursor_impl) - importlib.reload(align_uv_impl) - importlib.reload(copy_paste_uv_impl) - importlib.reload(copy_paste_uv_uvedit_impl) - importlib.reload(flip_rotate_impl) - importlib.reload(mirror_uv_impl) - importlib.reload(move_uv_impl) - importlib.reload(pack_uv_impl) - importlib.reload(preserve_uv_aspect_impl) - importlib.reload(select_uv_impl) - importlib.reload(smooth_uv_impl) - importlib.reload(texture_lock_impl) - importlib.reload(texture_wrap_impl) - importlib.reload(transfer_uv_impl) - importlib.reload(unwrap_constraint_impl) - importlib.reload(uv_bounding_box_impl) - importlib.reload(uv_inspection_impl) - importlib.reload(uv_sculpt_impl) - importlib.reload(uvw_impl) - importlib.reload(world_scale_uv_impl) -else: - from . import align_uv_cursor_impl - from . import align_uv_impl - from . import copy_paste_uv_impl - from . import copy_paste_uv_uvedit_impl - from . import flip_rotate_impl - from . import mirror_uv_impl - from . import move_uv_impl - from . import pack_uv_impl - from . import preserve_uv_aspect_impl - from . import select_uv_impl - from . import smooth_uv_impl - from . import texture_lock_impl - from . import texture_wrap_impl - from . import transfer_uv_impl - from . import unwrap_constraint_impl - from . import uv_bounding_box_impl - from . import uv_inspection_impl - from . import uv_sculpt_impl - from . import uvw_impl - from . import world_scale_uv_impl - -import bpy diff --git a/uv_magic_uv/impl/align_uv_cursor_impl.py b/uv_magic_uv/impl/align_uv_cursor_impl.py deleted file mode 100644 index 3056e87b0082a81391d07c9945ecf42d6ea2b2d2..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/align_uv_cursor_impl.py +++ /dev/null @@ -1,239 +0,0 @@ -# <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 mathutils import Vector -import bmesh - -from .. import common - - -def _is_valid_context(context): - # '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 AlignUVCursorLegacyImpl: - @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): - area, _, space = common.get_space_legacy('IMAGE_EDITOR', 'WINDOW', - 'IMAGE_EDITOR') - bd_size = common.get_uvimg_editor_board_size(area) - - if ops_obj.base == 'UV': - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if not bm.loops.layers.uv: - return None - uv_layer = bm.loops.layers.uv.verify() - - max_ = Vector((-10000000.0, -10000000.0)) - min_ = Vector((10000000.0, 10000000.0)) - for f in bm.faces: - if not f.select: - continue - for l in f.loops: - uv = l[uv_layer].uv - max_.x = max(max_.x, uv.x) - max_.y = max(max_.y, uv.y) - min_.x = min(min_.x, uv.x) - min_.y = min(min_.y, uv.y) - center = Vector(((max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0)) - - elif ops_obj.base == 'UV_SEL': - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if not bm.loops.layers.uv: - return None - uv_layer = bm.loops.layers.uv.verify() - - max_ = Vector((-10000000.0, -10000000.0)) - min_ = Vector((10000000.0, 10000000.0)) - for f in bm.faces: - if not f.select: - continue - for l in f.loops: - if not l[uv_layer].select: - continue - uv = l[uv_layer].uv - max_.x = max(max_.x, uv.x) - max_.y = max(max_.y, uv.y) - min_.x = min(min_.x, uv.x) - min_.y = min(min_.y, uv.y) - center = Vector(((max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0)) - - elif ops_obj.base == 'TEXTURE': - min_ = Vector((0.0, 0.0)) - max_ = Vector((1.0, 1.0)) - center = Vector((0.5, 0.5)) - else: - ops_obj.report({'ERROR'}, "Unknown Operation") - return {'CANCELLED'} - - if ops_obj.position == 'CENTER': - cx = center.x * bd_size[0] - cy = center.y * bd_size[1] - elif ops_obj.position == 'LEFT_TOP': - cx = min_.x * bd_size[0] - cy = max_.y * bd_size[1] - elif ops_obj.position == 'LEFT_MIDDLE': - cx = min_.x * bd_size[0] - cy = center.y * bd_size[1] - elif ops_obj.position == 'LEFT_BOTTOM': - cx = min_.x * bd_size[0] - cy = min_.y * bd_size[1] - elif ops_obj.position == 'MIDDLE_TOP': - cx = center.x * bd_size[0] - cy = max_.y * bd_size[1] - elif ops_obj.position == 'MIDDLE_BOTTOM': - cx = center.x * bd_size[0] - cy = min_.y * bd_size[1] - elif ops_obj.position == 'RIGHT_TOP': - cx = max_.x * bd_size[0] - cy = max_.y * bd_size[1] - elif ops_obj.position == 'RIGHT_MIDDLE': - cx = max_.x * bd_size[0] - cy = center.y * bd_size[1] - elif ops_obj.position == 'RIGHT_BOTTOM': - cx = max_.x * bd_size[0] - cy = min_.y * bd_size[1] - else: - ops_obj.report({'ERROR'}, "Unknown Operation") - return {'CANCELLED'} - - space.cursor_location = Vector((cx, cy)) - - return {'FINISHED'} - - -class AlignUVCursorImpl: - @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): - _, _, space = common.get_space_legacy('IMAGE_EDITOR', 'WINDOW', - 'IMAGE_EDITOR') - - if ops_obj.base == 'UV': - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if not bm.loops.layers.uv: - return None - uv_layer = bm.loops.layers.uv.verify() - - max_ = Vector((-10000000.0, -10000000.0)) - min_ = Vector((10000000.0, 10000000.0)) - for f in bm.faces: - if not f.select: - continue - for l in f.loops: - uv = l[uv_layer].uv - max_.x = max(max_.x, uv.x) - max_.y = max(max_.y, uv.y) - min_.x = min(min_.x, uv.x) - min_.y = min(min_.y, uv.y) - center = Vector(((max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0)) - - elif ops_obj.base == 'UV_SEL': - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if not bm.loops.layers.uv: - return None - uv_layer = bm.loops.layers.uv.verify() - - max_ = Vector((-10000000.0, -10000000.0)) - min_ = Vector((10000000.0, 10000000.0)) - for f in bm.faces: - if not f.select: - continue - for l in f.loops: - if not l[uv_layer].select: - continue - uv = l[uv_layer].uv - max_.x = max(max_.x, uv.x) - max_.y = max(max_.y, uv.y) - min_.x = min(min_.x, uv.x) - min_.y = min(min_.y, uv.y) - center = Vector(((max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0)) - - elif ops_obj.base == 'TEXTURE': - min_ = Vector((0.0, 0.0)) - max_ = Vector((1.0, 1.0)) - center = Vector((0.5, 0.5)) - else: - ops_obj.report({'ERROR'}, "Unknown Operation") - return {'CANCELLED'} - - if ops_obj.position == 'CENTER': - cx = center.x - cy = center.y - elif ops_obj.position == 'LEFT_TOP': - cx = min_.x - cy = max_.y - elif ops_obj.position == 'LEFT_MIDDLE': - cx = min_.x - cy = center.y - elif ops_obj.position == 'LEFT_BOTTOM': - cx = min_.x - cy = min_.y - elif ops_obj.position == 'MIDDLE_TOP': - cx = center.x - cy = max_.y - elif ops_obj.position == 'MIDDLE_BOTTOM': - cx = center.x - cy = min_.y - elif ops_obj.position == 'RIGHT_TOP': - cx = max_.x - cy = max_.y - elif ops_obj.position == 'RIGHT_MIDDLE': - cx = max_.x - cy = center.y - elif ops_obj.position == 'RIGHT_BOTTOM': - cx = max_.x - cy = min_.y - else: - ops_obj.report({'ERROR'}, "Unknown Operation") - return {'CANCELLED'} - - space.cursor_location = Vector((cx, cy)) - - return {'FINISHED'} diff --git a/uv_magic_uv/impl/align_uv_impl.py b/uv_magic_uv/impl/align_uv_impl.py deleted file mode 100644 index b8d7d33dd8d74b290703a23a273be0f9e52e2260..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/align_uv_impl.py +++ /dev/null @@ -1,820 +0,0 @@ -# <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, tan, sin, cos - -import bmesh -from mathutils import Vector - -from .. import common - - -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 - - -# 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 - - -class CircleImpl: - @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) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - uv_layer = bm.loops.layers.uv.verify() - - # loop_seqs[horizontal][vertical][loop] - loop_seqs, error = common.get_loop_sequences(bm, uv_layer, True) - if not loop_seqs: - ops_obj.report({'WARNING'}, error) - return {'CANCELLED'} - - # get circle and new UVs - uvs = [hseq[0][0][uv_layer].uv.copy() for hseq in loop_seqs] - c, r = _get_circle(uvs[0:3]) - new_uvs = _calc_v_on_circle(uvs, c, r) - - # check center UV of circle - center = loop_seqs[0][-1][0].vert - for hseq in loop_seqs[1:]: - if len(hseq[-1]) != 1: - ops_obj.report({'WARNING'}, "Last face must be triangle") - return {'CANCELLED'} - if hseq[-1][0].vert != center: - ops_obj.report({'WARNING'}, "Center must be identical") - return {'CANCELLED'} - - # align to circle - if ops_obj.transmission: - for hidx, hseq in enumerate(loop_seqs): - for vidx, pair in enumerate(hseq): - all_ = int((len(hseq) + 1) / 2) - r = (all_ - int((vidx + 1) / 2)) / all_ - pair[0][uv_layer].uv = c + (new_uvs[hidx] - c) * r - if ops_obj.select: - pair[0][uv_layer].select = True - - if len(pair) < 2: - continue - # for quad polygon - next_hidx = (hidx + 1) % len(loop_seqs) - pair[1][uv_layer].uv = c + ((new_uvs[next_hidx]) - c) * r - if ops_obj.select: - pair[1][uv_layer].select = True - else: - for hidx, hseq in enumerate(loop_seqs): - pair = hseq[0] - pair[0][uv_layer].uv = new_uvs[hidx] - pair[1][uv_layer].uv = new_uvs[(hidx + 1) % len(loop_seqs)] - if ops_obj.select: - pair[0][uv_layer].select = True - pair[1][uv_layer].select = True - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} - - -# get accumulate vertex lengths of loop sequences -def _get_loop_vert_accum_len(loops): - accum_lengths = [0.0] - length = 0 - for l1, l2 in zip(loops[:-1], loops[1:]): - diff = l2.vert.co - l1.vert.co - length = length + abs(diff.length) - accum_lengths.extend([length]) - - return accum_lengths - - -# get sum uv length of loop sequences -def _get_loop_uv_accum_len(loops, uv_layer): - accum_lengths = [0.0] - 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) - accum_lengths.extend([length]) - - return accum_lengths - - -# get horizontal differential of UV influenced by mesh vertex -def _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pidx, infl): - common.debug_print( - "loop_seqs[hidx={0}][vidx={1}][pidx={2}]".format(hidx, vidx, pidx)) - - base_uv = loop_seqs[0][vidx][0][uv_layer].uv.copy() - - # calculate original length - hloops = [] - for s in loop_seqs: - hloops.extend([s[vidx][0], s[vidx][1]]) - total_vlen = _get_loop_vert_len(hloops) - accum_vlens = _get_loop_vert_accum_len(hloops) - total_uvlen = _get_loop_uv_len(hloops, uv_layer) - accum_uvlens = _get_loop_uv_accum_len(hloops, uv_layer) - orig_uvs = [l[uv_layer].uv.copy() for l in hloops] - - # calculate target length - tgt_noinfl = total_uvlen * (hidx + pidx) / len(loop_seqs) - tgt_infl = total_uvlen * accum_vlens[hidx * 2 + pidx] / total_vlen - target_length = tgt_noinfl * (1 - infl) + tgt_infl * infl - common.debug_print(target_length) - common.debug_print(accum_uvlens) - - # calculate target UV - for i in range(len(accum_uvlens[:-1])): - # get line segment which UV will be placed - if ((accum_uvlens[i] <= target_length) and - (accum_uvlens[i + 1] > target_length)): - tgt_seg_len = target_length - accum_uvlens[i] - seg_len = accum_uvlens[i + 1] - accum_uvlens[i] - uv1 = orig_uvs[i] - uv2 = orig_uvs[i + 1] - target_uv = (uv1 - base_uv) + (uv2 - uv1) * tgt_seg_len / seg_len - break - elif i == (len(accum_uvlens[:-1]) - 1): - if abs(accum_uvlens[i + 1] - target_length) > 0.000001: - raise Exception( - "Internal Error: horizontal_target_length={}" - " is not equal to {}" - .format(target_length, accum_uvlens[-1])) - tgt_seg_len = target_length - accum_uvlens[i] - seg_len = accum_uvlens[i + 1] - accum_uvlens[i] - uv1 = orig_uvs[i] - uv2 = orig_uvs[i + 1] - target_uv = (uv1 - base_uv) + (uv2 - uv1) * tgt_seg_len / seg_len - break - else: - raise Exception("Internal Error: horizontal_target_length={}" - " is not in range {} to {}" - .format(target_length, accum_uvlens[0], - accum_uvlens[-1])) - - return target_uv - - -# --------------------- LOOP STRUCTURE ---------------------- -# -# loops[hidx][vidx][pidx] -# hidx: horizontal index -# vidx: vertical index -# pidx: pair index -# -# <----- horizontal -----> -# -# (hidx, vidx, pidx) = (0, 3, 0) -# | (hidx, vidx, pidx) = (1, 3, 0) -# v v -# ^ o --- oo --- o -# | | || | -# vertical | o --- oo --- o <- (hidx, vidx, pidx) -# | o --- oo --- o = (1, 2, 1) -# | | || | -# v o --- oo --- o -# ^ ^ -# | (hidx, vidx, pidx) = (1, 0, 1) -# (hidx, vidx, pidx) = (0, 0, 0) -# -# ----------------------------------------------------------- - - -# get vertical differential of UV influenced by mesh vertex -def _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pidx, infl): - common.debug_print( - "loop_seqs[hidx={0}][vidx={1}][pidx={2}]".format(hidx, vidx, pidx)) - - base_uv = loop_seqs[hidx][0][pidx][uv_layer].uv.copy() - - # calculate original length - vloops = [] - for s in loop_seqs[hidx]: - vloops.append(s[pidx]) - total_vlen = _get_loop_vert_len(vloops) - accum_vlens = _get_loop_vert_accum_len(vloops) - total_uvlen = _get_loop_uv_len(vloops, uv_layer) - accum_uvlens = _get_loop_uv_accum_len(vloops, uv_layer) - orig_uvs = [l[uv_layer].uv.copy() for l in vloops] - - # calculate target length - tgt_noinfl = total_uvlen * int((vidx + 1) / 2) * 2 / len(loop_seqs[hidx]) - tgt_infl = total_uvlen * accum_vlens[vidx] / total_vlen - target_length = tgt_noinfl * (1 - infl) + tgt_infl * infl - common.debug_print(target_length) - common.debug_print(accum_uvlens) - print("#### {}".format(tgt_noinfl)) - print("#### {}".format(tgt_infl)) - - # calculate target UV - for i in range(len(accum_uvlens[:-1])): - # get line segment which UV will be placed - if ((accum_uvlens[i] <= target_length) and - (accum_uvlens[i + 1] > target_length)): - tgt_seg_len = target_length - accum_uvlens[i] - seg_len = accum_uvlens[i + 1] - accum_uvlens[i] - uv1 = orig_uvs[i] - uv2 = orig_uvs[i + 1] - target_uv = (uv1 - base_uv) + (uv2 - uv1) * tgt_seg_len / seg_len - break - elif i == (len(accum_uvlens[:-1]) - 1): - if abs(accum_uvlens[i + 1] - target_length) > 0.000001: - raise Exception("Internal Error: horizontal_target_length={}" - " is not equal to {}" - .format(target_length, accum_uvlens[-1])) - tgt_seg_len = target_length - accum_uvlens[i] - seg_len = accum_uvlens[i + 1] - accum_uvlens[i] - uv1 = orig_uvs[i] - uv2 = orig_uvs[i + 1] - target_uv = (uv1 - base_uv) + (uv2 - uv1) * tgt_seg_len / seg_len - break - else: - raise Exception("Internal Error: horizontal_target_length={}" - " is not in range {} to {}" - .format(target_length, accum_uvlens[0], - accum_uvlens[-1])) - - return target_uv - - -# get horizontal differential of UV no influenced -def _get_hdiff_uv(uv_layer, loop_seqs, hidx): - base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() - h_uv = loop_seqs[-1][0][1][uv_layer].uv.copy() - base_uv - - return hidx * h_uv / len(loop_seqs) - - -# get vertical differential of UV no influenced -def _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx): - base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() - v_uv = loop_seqs[0][-1][0][uv_layer].uv.copy() - base_uv - - hseq = loop_seqs[hidx] - return int((vidx + 1) / 2) * v_uv / (len(hseq) / 2) - - -class StraightenImpl: - @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) - - # selected and paralleled UV loop sequence will be aligned - def __align_w_transmission(self, ops_obj, loop_seqs, uv_layer): - base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() - - # calculate diff UVs - diff_uvs = [] - # hseq[vertical][loop] - for hidx, hseq in enumerate(loop_seqs): - # pair[loop] - diffs = [] - for vidx in range(0, len(hseq), 2): - if ops_obj.horizontal: - hdiff_uvs = [ - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, - ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, - ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 0, ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 1, ops_obj.mesh_infl), - ] - else: - hdiff_uvs = [ - _get_hdiff_uv(uv_layer, loop_seqs, hidx), - _get_hdiff_uv(uv_layer, loop_seqs, hidx + 1), - _get_hdiff_uv(uv_layer, loop_seqs, hidx), - _get_hdiff_uv(uv_layer, loop_seqs, hidx + 1) - ] - if ops_obj.vertical: - vdiff_uvs = [ - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, - ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, - ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 0, ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 1, ops_obj.mesh_infl), - ] - else: - vdiff_uvs = [ - _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx) - ] - diffs.append([hdiff_uvs, vdiff_uvs]) - diff_uvs.append(diffs) - - # update UV - for hseq, diffs in zip(loop_seqs, diff_uvs): - for vidx in range(0, len(hseq), 2): - loops = [ - hseq[vidx][0], hseq[vidx][1], - hseq[vidx + 1][0], hseq[vidx + 1][1] - ] - for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0], - diffs[int(vidx / 2)][1]): - l[uv_layer].uv = base_uv + hdiff + vdiff - if ops_obj.select: - l[uv_layer].select = True - - # only selected UV loop sequence will be aligned - def __align_wo_transmission(self, ops_obj, loop_seqs, uv_layer): - base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() - - h_uv = loop_seqs[-1][0][1][uv_layer].uv.copy() - base_uv - for hidx, hseq in enumerate(loop_seqs): - # only selected loop pair is targeted - pair = hseq[0] - hdiff_uv_0 = hidx * h_uv / len(loop_seqs) - hdiff_uv_1 = (hidx + 1) * h_uv / len(loop_seqs) - pair[0][uv_layer].uv = base_uv + hdiff_uv_0 - pair[1][uv_layer].uv = base_uv + hdiff_uv_1 - if ops_obj.select: - pair[0][uv_layer].select = True - pair[1][uv_layer].select = True - - def __align(self, ops_obj, loop_seqs, uv_layer): - if ops_obj.transmission: - self.__align_w_transmission(ops_obj, loop_seqs, uv_layer) - else: - self.__align_wo_transmission(ops_obj, loop_seqs, uv_layer) - - def execute(self, ops_obj, 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() - uv_layer = bm.loops.layers.uv.verify() - - # loop_seqs[horizontal][vertical][loop] - loop_seqs, error = common.get_loop_sequences(bm, uv_layer) - if not loop_seqs: - ops_obj.report({'WARNING'}, error) - return {'CANCELLED'} - - # align - self.__align(ops_obj, loop_seqs, uv_layer) - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} - - -class AxisImpl: - @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) - - # get min/max of UV - def __get_uv_max_min(self, _, loop_seqs, uv_layer): - uv_max = Vector((-1000000.0, -1000000.0)) - uv_min = Vector((1000000.0, 1000000.0)) - for hseq in loop_seqs: - for l in hseq[0]: - uv = l[uv_layer].uv - uv_max.x = max(uv.x, uv_max.x) - uv_max.y = max(uv.y, uv_max.y) - uv_min.x = min(uv.x, uv_min.x) - uv_min.y = min(uv.y, uv_min.y) - - return uv_max, uv_min - - # get UV differentiation when UVs are aligned to X-axis - def __get_x_axis_align_diff_uvs(self, ops_obj, loop_seqs, uv_layer, uv_min, - width, height): - diff_uvs = [] - for hidx, hseq in enumerate(loop_seqs): - pair = hseq[0] - luv0 = pair[0][uv_layer] - luv1 = pair[1][uv_layer] - target_uv0 = Vector((0.0, 0.0)) - target_uv1 = Vector((0.0, 0.0)) - if ops_obj.location == 'RIGHT_BOTTOM': - target_uv0.y = target_uv1.y = uv_min.y - elif ops_obj.location == 'MIDDLE': - target_uv0.y = target_uv1.y = uv_min.y + height * 0.5 - elif ops_obj.location == 'LEFT_TOP': - target_uv0.y = target_uv1.y = uv_min.y + height - if luv0.uv.x < luv1.uv.x: - target_uv0.x = uv_min.x + hidx * width / len(loop_seqs) - target_uv1.x = uv_min.x + (hidx + 1) * width / len(loop_seqs) - else: - target_uv0.x = uv_min.x + (hidx + 1) * width / len(loop_seqs) - target_uv1.x = uv_min.x + hidx * width / len(loop_seqs) - diff_uvs.append([target_uv0 - luv0.uv, target_uv1 - luv1.uv]) - - return diff_uvs - - # get UV differentiation when UVs are aligned to Y-axis - def __get_y_axis_align_diff_uvs(self, ops_obj, loop_seqs, uv_layer, uv_min, - width, height): - diff_uvs = [] - for hidx, hseq in enumerate(loop_seqs): - pair = hseq[0] - luv0 = pair[0][uv_layer] - luv1 = pair[1][uv_layer] - target_uv0 = Vector((0.0, 0.0)) - target_uv1 = Vector((0.0, 0.0)) - if ops_obj.location == 'RIGHT_BOTTOM': - target_uv0.x = target_uv1.x = uv_min.x + width - elif ops_obj.location == 'MIDDLE': - target_uv0.x = target_uv1.x = uv_min.x + width * 0.5 - elif ops_obj.location == 'LEFT_TOP': - target_uv0.x = target_uv1.x = uv_min.x - if luv0.uv.y < luv1.uv.y: - target_uv0.y = uv_min.y + hidx * height / len(loop_seqs) - target_uv1.y = uv_min.y + (hidx + 1) * height / len(loop_seqs) - else: - target_uv0.y = uv_min.y + (hidx + 1) * height / len(loop_seqs) - target_uv1.y = uv_min.y + hidx * height / len(loop_seqs) - diff_uvs.append([target_uv0 - luv0.uv, target_uv1 - luv1.uv]) - - return diff_uvs - - # only selected UV loop sequence will be aligned along to X-axis - def __align_to_x_axis_wo_transmission(self, ops_obj, loop_seqs, uv_layer, - uv_min, width, height): - # reverse if the UV coordinate is not sorted by position - need_revese = loop_seqs[0][0][0][uv_layer].uv.x > \ - loop_seqs[-1][0][0][uv_layer].uv.x - if need_revese: - loop_seqs.reverse() - for hidx, hseq in enumerate(loop_seqs): - for vidx, pair in enumerate(hseq): - tmp = loop_seqs[hidx][vidx][0] - loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1] - loop_seqs[hidx][vidx][1] = tmp - - # get UV differential - diff_uvs = self.__get_x_axis_align_diff_uvs(ops_obj, loop_seqs, - uv_layer, uv_min, - width, height) - - # update UV - for hseq, duv in zip(loop_seqs, diff_uvs): - pair = hseq[0] - luv0 = pair[0][uv_layer] - luv1 = pair[1][uv_layer] - luv0.uv = luv0.uv + duv[0] - luv1.uv = luv1.uv + duv[1] - - # only selected UV loop sequence will be aligned along to Y-axis - def __align_to_y_axis_wo_transmission(self, ops_obj, loop_seqs, uv_layer, - uv_min, width, height): - # reverse if the UV coordinate is not sorted by position - need_revese = loop_seqs[0][0][0][uv_layer].uv.y > \ - loop_seqs[-1][0][0][uv_layer].uv.y - if need_revese: - loop_seqs.reverse() - for hidx, hseq in enumerate(loop_seqs): - for vidx, pair in enumerate(hseq): - tmp = loop_seqs[hidx][vidx][0] - loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1] - loop_seqs[hidx][vidx][1] = tmp - - # get UV differential - diff_uvs = self.__get_y_axis_align_diff_uvs(ops_obj, loop_seqs, - uv_layer, uv_min, - width, height) - - # update UV - for hseq, duv in zip(loop_seqs, diff_uvs): - pair = hseq[0] - luv0 = pair[0][uv_layer] - luv1 = pair[1][uv_layer] - luv0.uv = luv0.uv + duv[0] - luv1.uv = luv1.uv + duv[1] - - # selected and paralleled UV loop sequence will be aligned along to X-axis - def __align_to_x_axis_w_transmission(self, ops_obj, loop_seqs, uv_layer, - uv_min, width, height): - # reverse if the UV coordinate is not sorted by position - need_revese = loop_seqs[0][0][0][uv_layer].uv.x > \ - loop_seqs[-1][0][0][uv_layer].uv.x - if need_revese: - loop_seqs.reverse() - for hidx, hseq in enumerate(loop_seqs): - for vidx in range(len(hseq)): - tmp = loop_seqs[hidx][vidx][0] - loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1] - loop_seqs[hidx][vidx][1] = tmp - - # get offset UVs when the UVs are aligned to X-axis - align_diff_uvs = self.__get_x_axis_align_diff_uvs(ops_obj, loop_seqs, - uv_layer, uv_min, - width, height) - base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() - offset_uvs = [] - for hseq, aduv in zip(loop_seqs, align_diff_uvs): - luv0 = hseq[0][0][uv_layer] - luv1 = hseq[0][1][uv_layer] - offset_uvs.append([luv0.uv + aduv[0] - base_uv, - luv1.uv + aduv[1] - base_uv]) - - # get UV differential - diff_uvs = [] - # hseq[vertical][loop] - for hidx, hseq in enumerate(loop_seqs): - # pair[loop] - diffs = [] - for vidx in range(0, len(hseq), 2): - if ops_obj.horizontal: - hdiff_uvs = [ - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, - ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, - ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 0, ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 1, ops_obj.mesh_infl), - ] - hdiff_uvs[0].y = hdiff_uvs[0].y + offset_uvs[hidx][0].y - hdiff_uvs[1].y = hdiff_uvs[1].y + offset_uvs[hidx][1].y - hdiff_uvs[2].y = hdiff_uvs[2].y + offset_uvs[hidx][0].y - hdiff_uvs[3].y = hdiff_uvs[3].y + offset_uvs[hidx][1].y - else: - hdiff_uvs = [ - offset_uvs[hidx][0], - offset_uvs[hidx][1], - offset_uvs[hidx][0], - offset_uvs[hidx][1], - ] - if ops_obj.vertical: - vdiff_uvs = [ - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, - ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, - ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 0, ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 1, ops_obj.mesh_infl), - ] - else: - vdiff_uvs = [ - _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx) - ] - diffs.append([hdiff_uvs, vdiff_uvs]) - diff_uvs.append(diffs) - - # update UV - for hseq, diffs in zip(loop_seqs, diff_uvs): - for vidx in range(0, len(hseq), 2): - loops = [ - hseq[vidx][0], hseq[vidx][1], - hseq[vidx + 1][0], hseq[vidx + 1][1] - ] - for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0], - diffs[int(vidx / 2)][1]): - l[uv_layer].uv = base_uv + hdiff + vdiff - if ops_obj.select: - l[uv_layer].select = True - - # selected and paralleled UV loop sequence will be aligned along to Y-axis - def __align_to_y_axis_w_transmission(self, ops_obj, loop_seqs, uv_layer, - uv_min, width, height): - # reverse if the UV coordinate is not sorted by position - need_revese = loop_seqs[0][0][0][uv_layer].uv.y > \ - loop_seqs[-1][0][-1][uv_layer].uv.y - if need_revese: - loop_seqs.reverse() - for hidx, hseq in enumerate(loop_seqs): - for vidx in range(len(hseq)): - tmp = loop_seqs[hidx][vidx][0] - loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1] - loop_seqs[hidx][vidx][1] = tmp - - # get offset UVs when the UVs are aligned to Y-axis - align_diff_uvs = self.__get_y_axis_align_diff_uvs(ops_obj, loop_seqs, - uv_layer, uv_min, - width, height) - base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() - offset_uvs = [] - for hseq, aduv in zip(loop_seqs, align_diff_uvs): - luv0 = hseq[0][0][uv_layer] - luv1 = hseq[0][1][uv_layer] - offset_uvs.append([luv0.uv + aduv[0] - base_uv, - luv1.uv + aduv[1] - base_uv]) - - # get UV differential - diff_uvs = [] - # hseq[vertical][loop] - for hidx, hseq in enumerate(loop_seqs): - # pair[loop] - diffs = [] - for vidx in range(0, len(hseq), 2): - if ops_obj.horizontal: - hdiff_uvs = [ - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, - ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, - ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 0, ops_obj.mesh_infl), - _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 1, ops_obj.mesh_infl), - ] - hdiff_uvs[0].x = hdiff_uvs[0].x + offset_uvs[hidx][0].x - hdiff_uvs[1].x = hdiff_uvs[1].x + offset_uvs[hidx][1].x - hdiff_uvs[2].x = hdiff_uvs[2].x + offset_uvs[hidx][0].x - hdiff_uvs[3].x = hdiff_uvs[3].x + offset_uvs[hidx][1].x - else: - hdiff_uvs = [ - offset_uvs[hidx][0], - offset_uvs[hidx][1], - offset_uvs[hidx][0], - offset_uvs[hidx][1], - ] - if ops_obj.vertical: - vdiff_uvs = [ - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, - ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, - ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 0, ops_obj.mesh_infl), - _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, - hidx, 1, ops_obj.mesh_infl), - ] - else: - vdiff_uvs = [ - _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx), - _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx) - ] - diffs.append([hdiff_uvs, vdiff_uvs]) - diff_uvs.append(diffs) - - # update UV - for hseq, diffs in zip(loop_seqs, diff_uvs): - for vidx in range(0, len(hseq), 2): - loops = [ - hseq[vidx][0], hseq[vidx][1], - hseq[vidx + 1][0], hseq[vidx + 1][1] - ] - for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0], - diffs[int(vidx / 2)][1]): - l[uv_layer].uv = base_uv + hdiff + vdiff - if ops_obj.select: - l[uv_layer].select = True - - def __align(self, ops_obj, loop_seqs, uv_layer, uv_min, width, height): - # align along to x-axis - if width > height: - if ops_obj.transmission: - self.__align_to_x_axis_w_transmission(ops_obj, loop_seqs, - uv_layer, uv_min, - width, height) - else: - self.__align_to_x_axis_wo_transmission(ops_obj, loop_seqs, - uv_layer, uv_min, - width, height) - # align along to y-axis - else: - if ops_obj.transmission: - self.__align_to_y_axis_w_transmission(ops_obj, loop_seqs, - uv_layer, uv_min, - width, height) - else: - self.__align_to_y_axis_wo_transmission(ops_obj, loop_seqs, - uv_layer, uv_min, - width, height) - - def execute(self, ops_obj, 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() - uv_layer = bm.loops.layers.uv.verify() - - # loop_seqs[horizontal][vertical][loop] - loop_seqs, error = common.get_loop_sequences(bm, uv_layer) - if not loop_seqs: - ops_obj.report({'WARNING'}, error) - return {'CANCELLED'} - - # get height and width - uv_max, uv_min = self.__get_uv_max_min(ops_obj, loop_seqs, uv_layer) - width = uv_max.x - uv_min.x - height = uv_max.y - uv_min.y - - self.__align(ops_obj, loop_seqs, uv_layer, uv_min, width, height) - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} diff --git a/uv_magic_uv/impl/copy_paste_uv_impl.py b/uv_magic_uv/impl/copy_paste_uv_impl.py deleted file mode 100644 index ed44637b31b1b054010754a477e0f98bf5416662..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/copy_paste_uv_impl.py +++ /dev/null @@ -1,271 +0,0 @@ -# <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 deleted file mode 100644 index f14a70d63c9c81fedc297ee0d107c24b9d708548..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/copy_paste_uv_uvedit_impl.py +++ /dev/null @@ -1,166 +0,0 @@ -# <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 deleted file mode 100644 index f74bc256a6ac39c2958fc3ff50df3ae3299ce9ec..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/flip_rotate_impl.py +++ /dev/null @@ -1,133 +0,0 @@ -# <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 deleted file mode 100644 index e79fbc2ce2d7c97ab5c65532e96133e19536bee6..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/mirror_uv_impl.py +++ /dev/null @@ -1,158 +0,0 @@ -# <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 deleted file mode 100644 index ce507fbab165c8ef75fc80e5a554a7344e62650c..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/move_uv_impl.py +++ /dev/null @@ -1,166 +0,0 @@ -# <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/pack_uv_impl.py b/uv_magic_uv/impl/pack_uv_impl.py deleted file mode 100644 index 49f954f3ca01f73f468a7668fdf5c70b4e938c64..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/pack_uv_impl.py +++ /dev/null @@ -1,202 +0,0 @@ -# <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 math import fabs - -import bpy -import bmesh -import mathutils -from mathutils import Vector - -from .. import common - - -__all__ = [ - 'PackUVImpl', -] - - -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 - - -def sort_island_faces(kd, uvs, isl1, isl2): - """ - Sort faces in island - """ - - sorted_faces = [] - for f in isl1['sorted']: - _, idx, _ = kd.find( - Vector((f['ave_uv'].x, f['ave_uv'].y, 0.0))) - sorted_faces.append(isl2['faces'][uvs[idx]['face_idx']]) - return sorted_faces - - -def group_island(island_info, allowable_center_deviation, - allowable_size_deviation): - """ - Group island - """ - - num_group = 0 - while True: - # search islands which is not parsed yet - isl_1 = None - for isl_1 in island_info: - if isl_1['group'] == -1: - break - else: - break # all faces are parsed - if isl_1 is None: - break - isl_1['group'] = num_group - isl_1['sorted'] = isl_1['faces'] - - # search same island - for isl_2 in island_info: - if isl_2['group'] == -1: - dcx = isl_2['center'].x - isl_1['center'].x - dcy = isl_2['center'].y - isl_1['center'].y - dsx = isl_2['size'].x - isl_1['size'].x - dsy = isl_2['size'].y - isl_1['size'].y - center_x_matched = ( - fabs(dcx) < allowable_center_deviation[0] - ) - center_y_matched = ( - fabs(dcy) < allowable_center_deviation[1] - ) - size_x_matched = ( - fabs(dsx) < allowable_size_deviation[0] - ) - size_y_matched = ( - fabs(dsy) < allowable_size_deviation[1] - ) - center_matched = center_x_matched and center_y_matched - size_matched = size_x_matched and size_y_matched - num_uv_matched = (isl_2['num_uv'] == isl_1['num_uv']) - # are islands have same? - if center_matched and size_matched and num_uv_matched: - isl_2['group'] = num_group - kd = mathutils.kdtree.KDTree(len(isl_2['faces'])) - uvs = [ - { - 'uv': Vector( - (f['ave_uv'].x, f['ave_uv'].y, 0.0) - ), - 'face_idx': fidx - } for fidx, f in enumerate(isl_2['faces']) - ] - for i, uv in enumerate(uvs): - kd.insert(uv['uv'], i) - kd.balance() - # sort faces for copy/paste UV - isl_2['sorted'] = sort_island_faces(kd, uvs, isl_1, isl_2) - num_group = num_group + 1 - - return num_group - - -class PackUVImpl: - @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) - 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() - - selected_faces = [f for f in bm.faces if f.select] - island_info = common.get_island_info(obj) - num_group = group_island(island_info, - ops_obj.allowable_center_deviation, - ops_obj.allowable_size_deviation) - - loop_lists = [l for f in bm.faces for l in f.loops] - bpy.ops.mesh.select_all(action='DESELECT') - - # pack UV - for gidx in range(num_group): - group = list(filter( - lambda i, idx=gidx: i['group'] == idx, island_info)) - for f in group[0]['faces']: - f['face'].select = True - bmesh.update_edit_mesh(obj.data) - bpy.ops.uv.select_all(action='SELECT') - bpy.ops.uv.pack_islands(rotate=ops_obj.rotate, margin=ops_obj.margin) - - # copy/paste UV among same islands - for gidx in range(num_group): - group = list(filter( - lambda i, idx=gidx: i['group'] == idx, island_info)) - if len(group) <= 1: - continue - for g in group[1:]: - for (src_face, dest_face) in zip( - group[0]['sorted'], g['sorted']): - for (src_loop, dest_loop) in zip( - src_face['face'].loops, dest_face['face'].loops): - loop_lists[dest_loop.index][uv_layer].uv = loop_lists[ - src_loop.index][uv_layer].uv - - # restore face/UV selection - bpy.ops.uv.select_all(action='DESELECT') - bpy.ops.mesh.select_all(action='DESELECT') - for f in selected_faces: - f.select = True - bpy.ops.uv.select_all(action='SELECT') - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} diff --git a/uv_magic_uv/impl/preserve_uv_aspect_impl.py b/uv_magic_uv/impl/preserve_uv_aspect_impl.py deleted file mode 100644 index 622ee1d365cf8124f01585912c61f8344922fac1..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/preserve_uv_aspect_impl.py +++ /dev/null @@ -1,359 +0,0 @@ -# <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 mathutils import Vector - -from .. import common - - -__all__ = [ - 'PreserveUVAspectLegacyImpl', -] - - -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 - - -class PreserveUVAspectLegacyImpl: - @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): - # Note: the current system only works if the - # f[tex_layer].image doesn't return None - # which will happen in certain cases - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - - 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() - tex_layer = bm.faces.layers.tex.verify() - - sel_faces = [f for f in bm.faces if f.select] - dest_img = bpy.data.images[ops_obj.dest_img_name] - - info = {} - - for f in sel_faces: - if not f[tex_layer].image in info.keys(): - info[f[tex_layer].image] = {} - info[f[tex_layer].image]['faces'] = [] - info[f[tex_layer].image]['faces'].append(f) - - for img in info: - if img is None: - continue - - src_img = img - ratio = Vector(( - dest_img.size[0] / src_img.size[0], - dest_img.size[1] / src_img.size[1])) - - if ops_obj.origin == 'CENTER': - origin = Vector((0.0, 0.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin = origin + uv - num = num + 1 - origin = origin / num - elif ops_obj.origin == 'LEFT_TOP': - origin = Vector((100000.0, -100000.0)) - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = max(origin.y, uv.y) - elif ops_obj.origin == 'LEFT_CENTER': - origin = Vector((100000.0, 0.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = origin.y + uv.y - num = num + 1 - origin.y = origin.y / num - elif ops_obj.origin == 'LEFT_BOTTOM': - origin = Vector((100000.0, 100000.0)) - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = min(origin.y, uv.y) - elif ops_obj.origin == 'CENTER_TOP': - origin = Vector((0.0, -100000.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = origin.x + uv.x - origin.y = max(origin.y, uv.y) - num = num + 1 - origin.x = origin.x / num - elif ops_obj.origin == 'CENTER_BOTTOM': - origin = Vector((0.0, 100000.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = origin.x + uv.x - origin.y = min(origin.y, uv.y) - num = num + 1 - origin.x = origin.x / num - elif ops_obj.origin == 'RIGHT_TOP': - origin = Vector((-100000.0, -100000.0)) - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = max(origin.y, uv.y) - elif ops_obj.origin == 'RIGHT_CENTER': - origin = Vector((-100000.0, 0.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = origin.y + uv.y - num = num + 1 - origin.y = origin.y / num - elif ops_obj.origin == 'RIGHT_BOTTOM': - origin = Vector((-100000.0, 100000.0)) - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = min(origin.y, uv.y) - - info[img]['ratio'] = ratio - info[img]['origin'] = origin - - for img in info: - if img is None: - continue - - for f in info[img]['faces']: - f[tex_layer].image = dest_img - for l in f.loops: - uv = l[uv_layer].uv - origin = info[img]['origin'] - ratio = info[img]['ratio'] - diff = uv - origin - diff.x = diff.x / ratio.x - diff.y = diff.y / ratio.y - uv.x = origin.x + diff.x - uv.y = origin.y + diff.y - l[uv_layer].uv = uv - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} - - -class PreserveUVAspectImpl: - @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): - # Note: the current system only works if the - # f[tex_layer].image doesn't return None - # which will happen in certain cases - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - - 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() - tex_image = common.find_image(obj) - - sel_faces = [f for f in bm.faces if f.select] - dest_img = bpy.data.images[ops_obj.dest_img_name] - - info = {} - - for f in sel_faces: - if not tex_image in info.keys(): - info[tex_image] = {} - info[tex_image]['faces'] = [] - info[tex_image]['faces'].append(f) - - for img in info: - if img is None: - continue - - src_img = img - ratio = Vector(( - dest_img.size[0] / src_img.size[0], - dest_img.size[1] / src_img.size[1])) - - if ops_obj.origin == 'CENTER': - origin = Vector((0.0, 0.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin = origin + uv - num = num + 1 - origin = origin / num - elif ops_obj.origin == 'LEFT_TOP': - origin = Vector((100000.0, -100000.0)) - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = max(origin.y, uv.y) - elif ops_obj.origin == 'LEFT_CENTER': - origin = Vector((100000.0, 0.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = origin.y + uv.y - num = num + 1 - origin.y = origin.y / num - elif ops_obj.origin == 'LEFT_BOTTOM': - origin = Vector((100000.0, 100000.0)) - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = min(origin.y, uv.y) - elif ops_obj.origin == 'CENTER_TOP': - origin = Vector((0.0, -100000.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = origin.x + uv.x - origin.y = max(origin.y, uv.y) - num = num + 1 - origin.x = origin.x / num - elif ops_obj.origin == 'CENTER_BOTTOM': - origin = Vector((0.0, 100000.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = origin.x + uv.x - origin.y = min(origin.y, uv.y) - num = num + 1 - origin.x = origin.x / num - elif ops_obj.origin == 'RIGHT_TOP': - origin = Vector((-100000.0, -100000.0)) - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = max(origin.y, uv.y) - elif ops_obj.origin == 'RIGHT_CENTER': - origin = Vector((-100000.0, 0.0)) - num = 0 - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = origin.y + uv.y - num = num + 1 - origin.y = origin.y / num - elif ops_obj.origin == 'RIGHT_BOTTOM': - origin = Vector((-100000.0, 100000.0)) - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = min(origin.y, uv.y) - else: - ops_obj.report({'ERROR'}, "Unknown Operation") - return {'CANCELLED'} - - info[img]['ratio'] = ratio - info[img]['origin'] = origin - - for img in info: - if img is None: - continue - - nodes = common.find_texture_nodes(obj) - nodes[0].image = dest_img - - for f in info[img]['faces']: - for l in f.loops: - uv = l[uv_layer].uv - origin = info[img]['origin'] - ratio = info[img]['ratio'] - diff = uv - origin - diff.x = diff.x / ratio.x - diff.y = diff.y / ratio.y - uv.x = origin.x + diff.x - uv.y = origin.y + diff.y - l[uv_layer].uv = uv - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} \ No newline at end of file diff --git a/uv_magic_uv/impl/select_uv_impl.py b/uv_magic_uv/impl/select_uv_impl.py deleted file mode 100644 index dbcaee7e99230add0e041c1e3e07f4aae5b388d3..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/select_uv_impl.py +++ /dev/null @@ -1,120 +0,0 @@ -# <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 - -from .. import common - - -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 SelectOverlappedImpl: - @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): - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - uv_layer = bm.loops.layers.uv.verify() - - if context.tool_settings.use_uv_select_sync: - sel_faces = [f for f in bm.faces] - else: - sel_faces = [f for f in bm.faces if f.select] - - overlapped_info = common.get_overlapped_uv_info(bm, sel_faces, - uv_layer, 'FACE') - - for info in overlapped_info: - if context.tool_settings.use_uv_select_sync: - info["subject_face"].select = True - else: - for l in info["subject_face"].loops: - l[uv_layer].select = True - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} - - -class SelectFlippedImpl: - @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): - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - uv_layer = bm.loops.layers.uv.verify() - - if context.tool_settings.use_uv_select_sync: - sel_faces = [f for f in bm.faces] - else: - sel_faces = [f for f in bm.faces if f.select] - - flipped_info = common.get_flipped_uv_info(sel_faces, uv_layer) - - for info in flipped_info: - if context.tool_settings.use_uv_select_sync: - info["face"].select = True - else: - for l in info["face"].loops: - l[uv_layer].select = True - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} diff --git a/uv_magic_uv/impl/smooth_uv_impl.py b/uv_magic_uv/impl/smooth_uv_impl.py deleted file mode 100644 index dbc8afad38556df59b8c166b006c2fd5d13bd794..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/smooth_uv_impl.py +++ /dev/null @@ -1,215 +0,0 @@ -# <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 - - -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 SmoothUVImpl: - @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 __smooth_wo_transmission(self, ops_obj, loop_seqs, uv_layer): - # calculate path length - loops = [] - for hseq in loop_seqs: - loops.extend([hseq[0][0], hseq[0][1]]) - full_vlen = 0 - accm_vlens = [0.0] - full_uvlen = 0 - accm_uvlens = [0.0] - orig_uvs = [loop_seqs[0][0][0][uv_layer].uv.copy()] - for l1, l2 in zip(loops[:-1], loops[1:]): - diff_v = l2.vert.co - l1.vert.co - full_vlen = full_vlen + diff_v.length - accm_vlens.append(full_vlen) - diff_uv = l2[uv_layer].uv - l1[uv_layer].uv - full_uvlen = full_uvlen + diff_uv.length - accm_uvlens.append(full_uvlen) - orig_uvs.append(l2[uv_layer].uv.copy()) - - for hidx, hseq in enumerate(loop_seqs): - pair = hseq[0] - for pidx, l in enumerate(pair): - if ops_obj.select: - l[uv_layer].select = True - - # ignore start/end loop - if (hidx == 0 and pidx == 0) or\ - ((hidx == len(loop_seqs) - 1) and (pidx == len(pair) - 1)): - continue - - # calculate target path length - # target = no influenced * (1 - infl) + influenced * infl - tgt_noinfl = full_uvlen * (hidx + pidx) / (len(loop_seqs)) - tgt_infl = full_uvlen * accm_vlens[hidx * 2 + pidx] / full_vlen - target_length = tgt_noinfl * (1 - ops_obj.mesh_infl) + \ - tgt_infl * ops_obj.mesh_infl - - # get target UV - for i in range(len(accm_uvlens[:-1])): - # get line segment which UV will be placed - if ((accm_uvlens[i] <= target_length) and - (accm_uvlens[i + 1] > target_length)): - tgt_seg_len = target_length - accm_uvlens[i] - seg_len = accm_uvlens[i + 1] - accm_uvlens[i] - uv1 = orig_uvs[i] - uv2 = orig_uvs[i + 1] - target_uv = uv1 + (uv2 - uv1) * tgt_seg_len / seg_len - break - else: - ops_obj.report({'ERROR'}, "Failed to get target UV") - return {'CANCELLED'} - - # update UV - l[uv_layer].uv = target_uv - - def __smooth_w_transmission(self, ops_obj, loop_seqs, uv_layer): - # calculate path length - loops = [] - for vidx in range(len(loop_seqs[0])): - ls = [] - for hseq in loop_seqs: - ls.extend(hseq[vidx]) - loops.append(ls) - - orig_uvs = [] - accm_vlens = [] - full_vlens = [] - accm_uvlens = [] - full_uvlens = [] - for ls in loops: - full_v = 0.0 - accm_v = [0.0] - full_uv = 0.0 - accm_uv = [0.0] - uvs = [ls[0][uv_layer].uv.copy()] - for l1, l2 in zip(ls[:-1], ls[1:]): - diff_v = l2.vert.co - l1.vert.co - full_v = full_v + diff_v.length - accm_v.append(full_v) - diff_uv = l2[uv_layer].uv - l1[uv_layer].uv - full_uv = full_uv + diff_uv.length - accm_uv.append(full_uv) - uvs.append(l2[uv_layer].uv.copy()) - accm_vlens.append(accm_v) - full_vlens.append(full_v) - accm_uvlens.append(accm_uv) - full_uvlens.append(full_uv) - orig_uvs.append(uvs) - - for hidx, hseq in enumerate(loop_seqs): - for vidx, (pair, uvs, accm_v, full_v, accm_uv, full_uv)\ - in enumerate(zip(hseq, orig_uvs, accm_vlens, full_vlens, - accm_uvlens, full_uvlens)): - for pidx, l in enumerate(pair): - if ops_obj.select: - l[uv_layer].select = True - - # ignore start/end loop - if hidx == 0 and pidx == 0: - continue - if hidx == len(loop_seqs) - 1 and pidx == len(pair) - 1: - continue - - # calculate target path length - # target = no influenced * (1 - infl) + influenced * infl - tgt_noinfl = full_uv * (hidx + pidx) / (len(loop_seqs)) - tgt_infl = full_uv * accm_v[hidx * 2 + pidx] / full_v - target_length = tgt_noinfl * (1 - ops_obj.mesh_infl) + \ - tgt_infl * ops_obj.mesh_infl - - # get target UV - for i in range(len(accm_uv[:-1])): - # get line segment to be placed - if ((accm_uv[i] <= target_length) and - (accm_uv[i + 1] > target_length)): - tgt_seg_len = target_length - accm_uv[i] - seg_len = accm_uv[i + 1] - accm_uv[i] - uv1 = uvs[i] - uv2 = uvs[i + 1] - target_uv = uv1 +\ - (uv2 - uv1) * tgt_seg_len / seg_len - break - else: - ops_obj.report({'ERROR'}, "Failed to get target UV") - return {'CANCELLED'} - - # update UV - l[uv_layer].uv = target_uv - - def __smooth(self, ops_obj, loop_seqs, uv_layer): - if ops_obj.transmission: - self.__smooth_w_transmission(ops_obj, loop_seqs, uv_layer) - else: - self.__smooth_wo_transmission(ops_obj, loop_seqs, uv_layer) - - def execute(self, ops_obj, 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() - uv_layer = bm.loops.layers.uv.verify() - - # loop_seqs[horizontal][vertical][loop] - loop_seqs, error = common.get_loop_sequences(bm, uv_layer) - if not loop_seqs: - ops_obj.report({'WARNING'}, error) - return {'CANCELLED'} - - # smooth - self.__smooth(ops_obj, loop_seqs, uv_layer) - - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} diff --git a/uv_magic_uv/impl/texture_lock_impl.py b/uv_magic_uv/impl/texture_lock_impl.py deleted file mode 100644 index c14eddb0f0877bf6cb1ecbd3a6571a023817edb4..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/texture_lock_impl.py +++ /dev/null @@ -1,455 +0,0 @@ -# <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 math -from math import atan2, cos, sqrt, sin, fabs - -import bpy -import bmesh -from mathutils import Vector - -from .. import common - - -def _get_vco(verts_orig, loop): - """ - Get vertex original coordinate from loop - """ - for vo in verts_orig: - if vo["vidx"] == loop.vert.index and vo["moved"] is False: - return vo["vco"] - return loop.vert.co - - -def _get_link_loops(vert): - """ - Get loop linked to vertex - """ - link_loops = [] - for f in vert.link_faces: - adj_loops = [] - for loop in f.loops: - # self loop - if loop.vert == vert: - l = loop - # linked loop - else: - for e in loop.vert.link_edges: - if e.other_vert(loop.vert) == vert: - adj_loops.append(loop) - if len(adj_loops) < 2: - return None - - link_loops.append({"l": l, "l0": adj_loops[0], "l1": adj_loops[1]}) - return link_loops - - -def _get_ini_geom(link_loop, uv_layer, verts_orig, v_orig): - """ - Get initial geometory - (Get interior angle of face in vertex/UV space) - """ - u = link_loop["l"][uv_layer].uv - v0 = _get_vco(verts_orig, link_loop["l0"]) - u0 = link_loop["l0"][uv_layer].uv - v1 = _get_vco(verts_orig, link_loop["l1"]) - u1 = link_loop["l1"][uv_layer].uv - - # get interior angle of face in vertex space - v0v1 = v1 - v0 - v0v = v_orig["vco"] - v0 - v1v = v_orig["vco"] - v1 - theta0 = v0v1.angle(v0v) - theta1 = v0v1.angle(-v1v) - if (theta0 + theta1) > math.pi: - theta0 = v0v1.angle(-v0v) - theta1 = v0v1.angle(v1v) - - # get interior angle of face in UV space - u0u1 = u1 - u0 - u0u = u - u0 - u1u = u - u1 - phi0 = u0u1.angle(u0u) - phi1 = u0u1.angle(-u1u) - if (phi0 + phi1) > math.pi: - phi0 = u0u1.angle(-u0u) - phi1 = u0u1.angle(u1u) - - # get direction of linked UV coordinate - # this will be used to judge whether angle is more or less than 180 degree - dir0 = u0u1.cross(u0u) > 0 - dir1 = u0u1.cross(u1u) > 0 - - return { - "theta0": theta0, - "theta1": theta1, - "phi0": phi0, - "phi1": phi1, - "dir0": dir0, - "dir1": dir1} - - -def _get_target_uv(link_loop, uv_layer, verts_orig, v, ini_geom): - """ - Get target UV coordinate - """ - v0 = _get_vco(verts_orig, link_loop["l0"]) - lo0 = link_loop["l0"] - v1 = _get_vco(verts_orig, link_loop["l1"]) - lo1 = link_loop["l1"] - - # get interior angle of face in vertex space - v0v1 = v1 - v0 - v0v = v.co - v0 - v1v = v.co - v1 - theta0 = v0v1.angle(v0v) - theta1 = v0v1.angle(-v1v) - if (theta0 + theta1) > math.pi: - theta0 = v0v1.angle(-v0v) - theta1 = v0v1.angle(v1v) - - # calculate target interior angle in UV space - phi0 = theta0 * ini_geom["phi0"] / ini_geom["theta0"] - phi1 = theta1 * ini_geom["phi1"] / ini_geom["theta1"] - - uv0 = lo0[uv_layer].uv - uv1 = lo1[uv_layer].uv - - # calculate target vertex coordinate from target interior angle - tuv0, tuv1 = _calc_tri_vert(uv0, uv1, phi0, phi1) - - # target UV coordinate depends on direction, so judge using direction of - # linked UV coordinate - u0u1 = uv1 - uv0 - u0u = tuv0 - uv0 - u1u = tuv0 - uv1 - dir0 = u0u1.cross(u0u) > 0 - dir1 = u0u1.cross(u1u) > 0 - if (ini_geom["dir0"] != dir0) or (ini_geom["dir1"] != dir1): - return tuv1 - - return tuv0 - - -def _calc_tri_vert(v0, v1, angle0, angle1): - """ - Calculate rest coordinate from other coordinates and angle of end - """ - angle = math.pi - angle0 - angle1 - - alpha = atan2(v1.y - v0.y, v1.x - v0.x) - d = (v1.x - v0.x) / cos(alpha) - a = d * sin(angle0) / sin(angle) - b = d * sin(angle1) / sin(angle) - s = (a + b + d) / 2.0 - if fabs(d) < 0.0000001: - xd = 0 - yd = 0 - else: - r = s * (s - a) * (s - b) * (s - d) - if r < 0: - xd = 0 - yd = 0 - else: - xd = (b * b - a * a + d * d) / (2 * d) - yd = 2 * sqrt(r) / d - x1 = xd * cos(alpha) - yd * sin(alpha) + v0.x - y1 = xd * sin(alpha) + yd * cos(alpha) + v0.y - x2 = xd * cos(alpha) + yd * sin(alpha) + v0.x - y2 = xd * sin(alpha) - yd * cos(alpha) + v0.y - - return Vector((x1, y1)), Vector((x2, y2)) - - -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 - - -class LockImpl: - @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) - - @classmethod - def is_ready(cls, context): - sc = context.scene - props = sc.muv_props.texture_lock - if props.verts_orig: - return True - return False - - def execute(self, ops_obj, context): - props = context.scene.muv_props.texture_lock - obj = bpy.context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - 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'} - - props.verts_orig = [ - {"vidx": v.index, "vco": v.co.copy(), "moved": False} - for v in bm.verts if v.select] - - return {'FINISHED'} - - -class UnlockImpl: - @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.texture_lock - if not props.verts_orig: - return False - if not LockImpl.is_ready(context): - return False - if not _is_valid_context(context): - return False - return True - - def execute(self, ops_obj, context): - sc = context.scene - props = sc.muv_props.texture_lock - obj = bpy.context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - 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() - - verts = [v.index for v in bm.verts if v.select] - verts_orig = props.verts_orig - - # move UV followed by vertex coordinate - for vidx, v_orig in zip(verts, verts_orig): - if vidx != v_orig["vidx"]: - ops_obj.report({'ERROR'}, "Internal Error") - return {"CANCELLED"} - - v = bm.verts[vidx] - link_loops = _get_link_loops(v) - - result = [] - - for ll in link_loops: - ini_geom = _get_ini_geom(ll, uv_layer, verts_orig, v_orig) - target_uv = _get_target_uv( - ll, uv_layer, verts_orig, v, ini_geom) - result.append({"l": ll["l"], "uv": target_uv}) - - # connect other face's UV - if ops_obj.connect: - ave = Vector((0.0, 0.0)) - for r in result: - ave = ave + r["uv"] - ave = ave / len(result) - for r in result: - r["l"][uv_layer].uv = ave - else: - for r in result: - r["l"][uv_layer].uv = r["uv"] - v_orig["moved"] = True - bmesh.update_edit_mesh(obj.data) - - props.verts_orig = None - - return {'FINISHED'} - - -class IntrImpl: - __timer = None - - @classmethod - def poll(cls, context): - # we can not get area/space/region from console - if common.is_console_mode(): - return False - return _is_valid_context(context) - - @classmethod - def is_running(cls, _): - return 1 if cls.__timer else 0 - - @classmethod - def handle_add(cls, ops_obj, context): - if cls.__timer is None: - cls.__timer = context.window_manager.event_timer_add( - 0.10, window=context.window) - context.window_manager.modal_handler_add(ops_obj) - - @classmethod - def handle_remove(cls, context): - if cls.__timer is not None: - context.window_manager.event_timer_remove(cls.__timer) - cls.__timer = None - - def __init__(self): - self.__intr_verts_orig = [] - self.__intr_verts = [] - - def __sel_verts_changed(self, context): - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - prev = set(self.__intr_verts) - now = set([v.index for v in bm.verts if v.select]) - - return prev != now - - def __reinit_verts(self, context): - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - self.__intr_verts_orig = [ - {"vidx": v.index, "vco": v.co.copy(), "moved": False} - for v in bm.verts if v.select] - self.__intr_verts = [v.index for v in bm.verts if v.select] - - def __update_uv(self, ops_obj, context): - """ - Update UV when vertex coordinates are changed - """ - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - if not bm.loops.layers.uv: - ops_obj.report({'WARNING'}, - "Object must have more than one UV map") - return - uv_layer = bm.loops.layers.uv.verify() - - verts = [v.index for v in bm.verts if v.select] - verts_orig = self.__intr_verts_orig - - for vidx, v_orig in zip(verts, verts_orig): - if vidx != v_orig["vidx"]: - ops_obj.report({'ERROR'}, "Internal Error") - return - - v = bm.verts[vidx] - link_loops = _get_link_loops(v) - - result = [] - for ll in link_loops: - ini_geom = _get_ini_geom(ll, uv_layer, verts_orig, v_orig) - target_uv = _get_target_uv( - ll, uv_layer, verts_orig, v, ini_geom) - result.append({"l": ll["l"], "uv": target_uv}) - - # UV connect option is always true, because it raises - # unexpected behavior - ave = Vector((0.0, 0.0)) - for r in result: - ave = ave + r["uv"] - ave = ave / len(result) - for r in result: - r["l"][uv_layer].uv = ave - v_orig["moved"] = True - bmesh.update_edit_mesh(obj.data) - - common.redraw_all_areas() - self.__intr_verts_orig = [ - {"vidx": v.index, "vco": v.co.copy(), "moved": False} - for v in bm.verts if v.select] - - def modal(self, ops_obj, context, event): - if not _is_valid_context(context): - IntrImpl.handle_remove(context) - return {'FINISHED'} - - if not IntrImpl.is_running(context): - return {'FINISHED'} - - if context.area: - context.area.tag_redraw() - - if event.type == 'TIMER': - if self.__sel_verts_changed(context): - self.__reinit_verts(context) - else: - self.__update_uv(ops_obj, context) - - return {'PASS_THROUGH'} - - def invoke(self, ops_obj, context, _): - if not _is_valid_context(context): - return {'CANCELLED'} - - if not IntrImpl.is_running(context): - IntrImpl.handle_add(ops_obj, context) - return {'RUNNING_MODAL'} - else: - IntrImpl.handle_remove(context) - - if context.area: - context.area.tag_redraw() - - return {'FINISHED'} diff --git a/uv_magic_uv/impl/texture_projection_impl.py b/uv_magic_uv/impl/texture_projection_impl.py deleted file mode 100644 index b64fa374825d251527325398efe593d5df466f83..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/texture_projection_impl.py +++ /dev/null @@ -1,126 +0,0 @@ -# <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 collections import namedtuple - -import bpy -import mathutils - - -_Rect = namedtuple('Rect', 'x0 y0 x1 y1') -_Rect2 = namedtuple('Rect2', 'x y width height') - - -def get_loaded_texture_name(_, __): - items = [(key, key, "") for key in bpy.data.images.keys()] - items.append(("None", "None", "")) - return items - - -def get_canvas(context, magnitude): - """ - Get canvas to be renderred texture - """ - sc = context.scene - prefs = context.user_preferences.addons["uv_magic_uv"].preferences - - region_w = context.region.width - region_h = context.region.height - canvas_w = region_w - prefs.texture_projection_canvas_padding[0] * 2.0 - canvas_h = region_h - prefs.texture_projection_canvas_padding[1] * 2.0 - - img = bpy.data.images[sc.muv_texture_projection_tex_image] - tex_w = img.size[0] - tex_h = img.size[1] - - center_x = region_w * 0.5 - center_y = region_h * 0.5 - - if sc.muv_texture_projection_adjust_window: - ratio_x = canvas_w / tex_w - ratio_y = canvas_h / tex_h - if sc.muv_texture_projection_apply_tex_aspect: - ratio = ratio_y if ratio_x > ratio_y else ratio_x - len_x = ratio * tex_w - len_y = ratio * tex_h - else: - len_x = canvas_w - len_y = canvas_h - else: - if sc.muv_texture_projection_apply_tex_aspect: - len_x = tex_w * magnitude - len_y = tex_h * magnitude - else: - len_x = region_w * magnitude - len_y = region_h * magnitude - - x0 = int(center_x - len_x * 0.5) - y0 = int(center_y - len_y * 0.5) - x1 = int(center_x + len_x * 0.5) - y1 = int(center_y + len_y * 0.5) - - return _Rect(x0, y0, x1, y1) - - -def rect_to_rect2(rect): - """ - Convert Rect1 to Rect2 - """ - - return _Rect2(rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0) - - -def region_to_canvas(rg_vec, canvas): - """ - Convert screen region to canvas - """ - - cv_rect = rect_to_rect2(canvas) - cv_vec = mathutils.Vector() - cv_vec.x = (rg_vec.x - cv_rect.x) / cv_rect.width - cv_vec.y = (rg_vec.y - cv_rect.y) / cv_rect.height - - return cv_vec - - -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 diff --git a/uv_magic_uv/impl/texture_wrap_impl.py b/uv_magic_uv/impl/texture_wrap_impl.py deleted file mode 100644 index 336b17608c4127c6ecf1baebfef4fc14c122959b..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/texture_wrap_impl.py +++ /dev/null @@ -1,236 +0,0 @@ -# <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 - -from .. import common - - -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 - - -class ReferImpl: - @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): - props = context.scene.muv_props.texture_wrap - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - 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'} - - sel_faces = [f for f in bm.faces if f.select] - if len(sel_faces) != 1: - ops_obj.report({'WARNING'}, "Must select only one face") - return {'CANCELLED'} - - props.ref_face_index = sel_faces[0].index - props.ref_obj = obj - - return {'FINISHED'} - - -class SetImpl: - @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.texture_wrap - if not props.ref_obj: - return False - return _is_valid_context(context) - - def execute(self, ops_obj, context): - sc = context.scene - props = sc.muv_props.texture_wrap - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - 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() - - if sc.muv_texture_wrap_selseq: - sel_faces = [] - for hist in bm.select_history: - if isinstance(hist, bmesh.types.BMFace) and hist.select: - sel_faces.append(hist) - if not sel_faces: - ops_obj.report({'WARNING'}, "Must select more than one face") - return {'CANCELLED'} - else: - sel_faces = [f for f in bm.faces if f.select] - if len(sel_faces) != 1: - ops_obj.report({'WARNING'}, "Must select only one face") - return {'CANCELLED'} - - ref_face_index = props.ref_face_index - for face in sel_faces: - tgt_face_index = face.index - if ref_face_index == tgt_face_index: - ops_obj.report({'WARNING'}, "Must select different face") - return {'CANCELLED'} - - if props.ref_obj != obj: - ops_obj.report({'WARNING'}, "Object must be same") - return {'CANCELLED'} - - ref_face = bm.faces[ref_face_index] - tgt_face = bm.faces[tgt_face_index] - - # get common vertices info - common_verts = [] - for sl in ref_face.loops: - for dl in tgt_face.loops: - if sl.vert == dl.vert: - info = {"vert": sl.vert, "ref_loop": sl, - "tgt_loop": dl} - common_verts.append(info) - break - - if len(common_verts) != 2: - ops_obj.report({'WARNING'}, - "2 vertices must be shared among faces") - return {'CANCELLED'} - - # get reference other vertices info - ref_other_verts = [] - for sl in ref_face.loops: - for ci in common_verts: - if sl.vert == ci["vert"]: - break - else: - info = {"vert": sl.vert, "loop": sl} - ref_other_verts.append(info) - - if not ref_other_verts: - ops_obj.report({'WARNING'}, - "More than 1 vertex must be unshared") - return {'CANCELLED'} - - # get reference info - ref_info = {} - cv0 = common_verts[0]["vert"].co - cv1 = common_verts[1]["vert"].co - cuv0 = common_verts[0]["ref_loop"][uv_layer].uv - cuv1 = common_verts[1]["ref_loop"][uv_layer].uv - ov0 = ref_other_verts[0]["vert"].co - ouv0 = ref_other_verts[0]["loop"][uv_layer].uv - ref_info["vert_vdiff"] = cv1 - cv0 - ref_info["uv_vdiff"] = cuv1 - cuv0 - ref_info["vert_hdiff"], _ = common.diff_point_to_segment( - cv0, cv1, ov0) - ref_info["uv_hdiff"], _ = common.diff_point_to_segment( - cuv0, cuv1, ouv0) - - # get target other vertices info - tgt_other_verts = [] - for dl in tgt_face.loops: - for ci in common_verts: - if dl.vert == ci["vert"]: - break - else: - info = {"vert": dl.vert, "loop": dl} - tgt_other_verts.append(info) - - if not tgt_other_verts: - ops_obj.report({'WARNING'}, - "More than 1 vertex must be unshared") - return {'CANCELLED'} - - # get target info - for info in tgt_other_verts: - cv0 = common_verts[0]["vert"].co - cv1 = common_verts[1]["vert"].co - cuv0 = common_verts[0]["ref_loop"][uv_layer].uv - ov = info["vert"].co - info["vert_hdiff"], x = common.diff_point_to_segment( - cv0, cv1, ov) - info["vert_vdiff"] = x - common_verts[0]["vert"].co - - # calclulate factor - fact_h = -info["vert_hdiff"].length / \ - ref_info["vert_hdiff"].length - fact_v = info["vert_vdiff"].length / \ - ref_info["vert_vdiff"].length - duv_h = ref_info["uv_hdiff"] * fact_h - duv_v = ref_info["uv_vdiff"] * fact_v - - # get target UV - info["target_uv"] = cuv0 + duv_h + duv_v - - # apply to common UVs - for info in common_verts: - info["tgt_loop"][uv_layer].uv = \ - info["ref_loop"][uv_layer].uv.copy() - # apply to other UVs - for info in tgt_other_verts: - info["loop"][uv_layer].uv = info["target_uv"] - - common.debug_print("===== Target Other Vertices =====") - common.debug_print(tgt_other_verts) - - bmesh.update_edit_mesh(obj.data) - - ref_face_index = tgt_face_index - - if sc.muv_texture_wrap_set_and_refer: - props.ref_face_index = tgt_face_index - - return {'FINISHED'} diff --git a/uv_magic_uv/impl/transfer_uv_impl.py b/uv_magic_uv/impl/transfer_uv_impl.py deleted file mode 100644 index adc973527f9d6dcd1a49c8a81150e8206917cc18..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/transfer_uv_impl.py +++ /dev/null @@ -1,330 +0,0 @@ -# <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/unwrap_constraint_impl.py b/uv_magic_uv/impl/unwrap_constraint_impl.py deleted file mode 100644 index 257197986a47f46e36428f735089239644cb8717..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/unwrap_constraint_impl.py +++ /dev/null @@ -1,98 +0,0 @@ -# ##### 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 .. import common - - -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 - - -class UnwrapConstraintImpl: - @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) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - - # bpy.ops.uv.unwrap() makes one UV map at least - 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() - - # get original UV coordinate - faces = [f for f in bm.faces if f.select] - uv_list = [] - for f in faces: - uvs = [l[uv_layer].uv.copy() for l in f.loops] - uv_list.append(uvs) - - # unwrap - bpy.ops.uv.unwrap( - method=ops_obj.method, - fill_holes=ops_obj.fill_holes, - correct_aspect=ops_obj.correct_aspect, - use_subsurf_data=ops_obj.use_subsurf_data, - margin=ops_obj.margin) - - # when U/V-Constraint is checked, revert original coordinate - for f, uvs in zip(faces, uv_list): - for l, uv in zip(f.loops, uvs): - if ops_obj.u_const: - l[uv_layer].uv.x = uv.x - if ops_obj.v_const: - l[uv_layer].uv.y = uv.y - - # update mesh - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} diff --git a/uv_magic_uv/impl/uv_bounding_box_impl.py b/uv_magic_uv/impl/uv_bounding_box_impl.py deleted file mode 100644 index bce51f3e863c4d298ccdac2977d862946e09549b..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/uv_bounding_box_impl.py +++ /dev/null @@ -1,55 +0,0 @@ -# <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 enum import IntEnum -import math - -import mathutils - - -MAX_VALUE = 100000.0 - - -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 diff --git a/uv_magic_uv/impl/uv_inspection_impl.py b/uv_magic_uv/impl/uv_inspection_impl.py deleted file mode 100644 index caa3aa79ada5d749bc456ad6299209f5c19f1d29..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/uv_inspection_impl.py +++ /dev/null @@ -1,70 +0,0 @@ -# <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 - -from .. import common - - -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 - - -def update_uvinsp_info(context): - sc = context.scene - props = sc.muv_props.uv_inspection - - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - uv_layer = bm.loops.layers.uv.verify() - - if context.tool_settings.use_uv_select_sync: - sel_faces = [f for f in bm.faces] - else: - sel_faces = [f for f in bm.faces if f.select] - props.overlapped_info = common.get_overlapped_uv_info( - bm, sel_faces, uv_layer, sc.muv_uv_inspection_show_mode) - props.flipped_info = common.get_flipped_uv_info(sel_faces, uv_layer) diff --git a/uv_magic_uv/impl/uv_sculpt_impl.py b/uv_magic_uv/impl/uv_sculpt_impl.py deleted file mode 100644 index 284787d8cc2df504a26f0e20aa19b7203aee23a2..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/uv_sculpt_impl.py +++ /dev/null @@ -1,57 +0,0 @@ -# <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" - - -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_strength(p, len_, factor): - f = factor - - if p > len_: - return 0.0 - - if p < 0.0: - return f - - return (len_ - p) * f / len_ diff --git a/uv_magic_uv/impl/uvw_impl.py b/uv_magic_uv/impl/uvw_impl.py deleted file mode 100644 index 98da0dc9bb222730ed53f73f719709ec1025053f..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/uvw_impl.py +++ /dev/null @@ -1,160 +0,0 @@ -# <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 - -from .. import common - - -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: - if common.check_version(2, 80, 0) >= 0: - # pylint: disable=E0001 - co = q @ l.vert.co - else: - 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/impl/world_scale_uv_impl.py b/uv_magic_uv/impl/world_scale_uv_impl.py deleted file mode 100644 index 3f376f0d6904d6a193d5b38ba049876be086423d..0000000000000000000000000000000000000000 --- a/uv_magic_uv/impl/world_scale_uv_impl.py +++ /dev/null @@ -1,383 +0,0 @@ -# <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__ = "McBuff, Nutti <nutti.metro@gmail.com>" -__status__ = "production" -__version__ = "5.2" -__date__ = "17 Nov 2018" - -from math import sqrt - -import bmesh -from mathutils import Vector - -from .. import common - - -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 _measure_wsuv_info(obj, tex_size=None): - mesh_area = common.measure_mesh_area(obj) - if common.check_version(2, 80, 0) >= 0: - uv_area = common.measure_uv_area(obj, tex_size) - else: - uv_area = common.measure_uv_area_legacy(obj, tex_size) - - if not uv_area: - return None, mesh_area, None - - if mesh_area == 0.0: - density = 0.0 - else: - density = sqrt(uv_area) / sqrt(mesh_area) - - return uv_area, mesh_area, density - - -def _apply(obj, origin, factor): - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - sel_faces = [f for f in bm.faces if f.select] - - uv_layer = bm.loops.layers.uv.verify() - - # calculate origin - if origin == 'CENTER': - origin = Vector((0.0, 0.0)) - num = 0 - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin = origin + uv - num = num + 1 - origin = origin / num - elif origin == 'LEFT_TOP': - origin = Vector((100000.0, -100000.0)) - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = max(origin.y, uv.y) - elif origin == 'LEFT_CENTER': - origin = Vector((100000.0, 0.0)) - num = 0 - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = origin.y + uv.y - num = num + 1 - origin.y = origin.y / num - elif origin == 'LEFT_BOTTOM': - origin = Vector((100000.0, 100000.0)) - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = min(origin.x, uv.x) - origin.y = min(origin.y, uv.y) - elif origin == 'CENTER_TOP': - origin = Vector((0.0, -100000.0)) - num = 0 - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = origin.x + uv.x - origin.y = max(origin.y, uv.y) - num = num + 1 - origin.x = origin.x / num - elif origin == 'CENTER_BOTTOM': - origin = Vector((0.0, 100000.0)) - num = 0 - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = origin.x + uv.x - origin.y = min(origin.y, uv.y) - num = num + 1 - origin.x = origin.x / num - elif origin == 'RIGHT_TOP': - origin = Vector((-100000.0, -100000.0)) - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = max(origin.y, uv.y) - elif origin == 'RIGHT_CENTER': - origin = Vector((-100000.0, 0.0)) - num = 0 - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = origin.y + uv.y - num = num + 1 - origin.y = origin.y / num - elif origin == 'RIGHT_BOTTOM': - origin = Vector((-100000.0, 100000.0)) - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - origin.x = max(origin.x, uv.x) - origin.y = min(origin.y, uv.y) - - # update UV coordinate - for f in sel_faces: - for l in f.loops: - uv = l[uv_layer].uv - diff = uv - origin - l[uv_layer].uv = origin + diff * factor - - bmesh.update_edit_mesh(obj.data) - - -class MeasureImpl: - @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): - sc = context.scene - obj = context.active_object - - uv_area, mesh_area, density = _measure_wsuv_info(obj) - if not uv_area: - ops_obj.report({'WARNING'}, - "Object must have more than one UV map and texture") - return {'CANCELLED'} - - sc.muv_world_scale_uv_src_uv_area = uv_area - sc.muv_world_scale_uv_src_mesh_area = mesh_area - sc.muv_world_scale_uv_src_density = density - - ops_obj.report({'INFO'}, - "UV Area: {0}, Mesh Area: {1}, Texel Density: {2}" - .format(uv_area, mesh_area, density)) - - return {'FINISHED'} - - -class ApplyManualImpl: - @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 __apply_manual(self, ops_obj, context): - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - tex_size = ops_obj.tgt_texture_size - uv_area, _, density = _measure_wsuv_info(obj, tex_size) - if not uv_area: - ops_obj.report({'WARNING'}, - "Object must have more than one UV map") - return {'CANCELLED'} - - tgt_density = ops_obj.tgt_density - factor = tgt_density / density - - _apply(context.active_object, ops_obj.origin, factor) - ops_obj.report({'INFO'}, "Scaling factor: {0}".format(factor)) - - return {'FINISHED'} - - def draw(self, ops_obj, _): - layout = ops_obj.layout - - layout.prop(ops_obj, "tgt_density") - layout.prop(ops_obj, "tgt_texture_size") - layout.prop(ops_obj, "origin") - - layout.separator() - - def invoke(self, ops_obj, context, _): - if ops_obj.show_dialog: - wm = context.window_manager - return wm.invoke_props_dialog(ops_obj) - - return ops_obj.execute(context) - - def execute(self, ops_obj, context): - return self.__apply_manual(ops_obj, context) - - -class ApplyScalingDensityImpl: - @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 __apply_scaling_density(self, ops_obj, context): - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - uv_area, _, density = _measure_wsuv_info(obj) - if not uv_area: - ops_obj.report({'WARNING'}, - "Object must have more than one UV map and texture") - return {'CANCELLED'} - - tgt_density = ops_obj.src_density * ops_obj.tgt_scaling_factor - factor = tgt_density / density - - _apply(context.active_object, ops_obj.origin, factor) - ops_obj.report({'INFO'}, "Scaling factor: {0}".format(factor)) - - return {'FINISHED'} - - def draw(self, ops_obj, _): - layout = ops_obj.layout - - layout.label(text="Source:") - col = layout.column() - col.prop(ops_obj, "src_density") - col.enabled = False - - layout.separator() - - if not ops_obj.same_density: - layout.prop(ops_obj, "tgt_scaling_factor") - layout.prop(ops_obj, "origin") - - layout.separator() - - def invoke(self, ops_obj, context, _): - sc = context.scene - - if ops_obj.show_dialog: - wm = context.window_manager - - if ops_obj.same_density: - ops_obj.tgt_scaling_factor = 1.0 - else: - ops_obj.tgt_scaling_factor = \ - sc.muv_world_scale_uv_tgt_scaling_factor - ops_obj.src_density = sc.muv_world_scale_uv_src_density - - return wm.invoke_props_dialog(ops_obj) - - return ops_obj.execute(context) - - def execute(self, ops_obj, context): - if ops_obj.same_density: - ops_obj.tgt_scaling_factor = 1.0 - - return self.__apply_scaling_density(ops_obj, context) - - -class ApplyProportionalToMeshImpl: - @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 __apply_proportional_to_mesh(self, ops_obj, context): - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.verts.ensure_lookup_table() - bm.edges.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - uv_area, mesh_area, density = _measure_wsuv_info(obj) - if not uv_area: - ops_obj.report({'WARNING'}, - "Object must have more than one UV map and texture") - return {'CANCELLED'} - - tgt_density = ops_obj.src_density * sqrt(mesh_area) / sqrt( - ops_obj.src_mesh_area) - - factor = tgt_density / density - - _apply(context.active_object, ops_obj.origin, factor) - ops_obj.report({'INFO'}, "Scaling factor: {0}".format(factor)) - - return {'FINISHED'} - - def draw(self, ops_obj, _): - layout = ops_obj.layout - - layout.label(text="Source:") - col = layout.column(align=True) - col.prop(ops_obj, "src_density") - col.prop(ops_obj, "src_uv_area") - col.prop(ops_obj, "src_mesh_area") - col.enabled = False - - layout.separator() - layout.prop(ops_obj, "origin") - - layout.separator() - - def invoke(self, ops_obj, context, _): - if ops_obj.show_dialog: - wm = context.window_manager - sc = context.scene - - ops_obj.src_density = sc.muv_world_scale_uv_src_density - ops_obj.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area - - return wm.invoke_props_dialog(ops_obj) - - return ops_obj.execute(context) - - def execute(self, ops_obj, context): - return self.__apply_proportional_to_mesh(ops_obj, context) diff --git a/uv_magic_uv/legacy/__init__.py b/uv_magic_uv/legacy/__init__.py deleted file mode 100644 index 794d02bc0c0c9f380584bc1f290d13395ed65e1b..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# <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 deleted file mode 100644 index 9535b76d094b148741f5563ad34f356f810cad5a..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/__init__.py +++ /dev/null @@ -1,74 +0,0 @@ -# <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/legacy/op/align_uv.py b/uv_magic_uv/legacy/op/align_uv.py deleted file mode 100644 index a274d583ce7a04cc7b840fb14a27f764047c1ef6..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/align_uv.py +++ /dev/null @@ -1,231 +0,0 @@ -# <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 bpy.props import EnumProperty, BoolProperty, FloatProperty - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import align_uv_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "align_uv" - - @classmethod - def init_props(cls, scene): - scene.muv_align_uv_enabled = BoolProperty( - name="Align UV Enabled", - description="Align UV is enabled", - default=False - ) - scene.muv_align_uv_transmission = BoolProperty( - name="Transmission", - description="Align linked UVs", - default=False - ) - scene.muv_align_uv_select = BoolProperty( - name="Select", - description="Select UVs which are aligned", - default=False - ) - scene.muv_align_uv_vertical = BoolProperty( - name="Vert-Infl (Vertical)", - description="Align vertical direction influenced " - "by mesh vertex proportion", - default=False - ) - scene.muv_align_uv_horizontal = BoolProperty( - name="Vert-Infl (Horizontal)", - description="Align horizontal direction influenced " - "by mesh vertex proportion", - default=False - ) - scene.muv_align_uv_mesh_infl = FloatProperty( - name="Mesh Influence", - description="Influence rate of mesh vertex", - min=0.0, - max=1.0, - default=0.0 - ) - scene.muv_align_uv_location = EnumProperty( - name="Location", - description="Align location", - items=[ - ('LEFT_TOP', "Left/Top", "Align to Left or Top"), - ('MIDDLE', "Middle", "Align to middle"), - ('RIGHT_BOTTOM', "Right/Bottom", "Align to Right or Bottom") - ], - default='MIDDLE' - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_align_uv_enabled - del scene.muv_align_uv_transmission - del scene.muv_align_uv_select - del scene.muv_align_uv_vertical - del scene.muv_align_uv_horizontal - del scene.muv_align_uv_mesh_infl - del scene.muv_align_uv_location - - -@BlClassRegistry(legacy=True) -class MUV_OT_AlignUV_Circle(bpy.types.Operator): - - bl_idname = "uv.muv_align_uv_operator_circle" - bl_label = "Align UV (Circle)" - bl_description = "Align UV coordinates to Circle" - bl_options = {'REGISTER', 'UNDO'} - - transmission = BoolProperty( - name="Transmission", - description="Align linked UVs", - default=False - ) - select = BoolProperty( - name="Select", - description="Select UVs which are aligned", - default=False - ) - - def __init__(self): - self.__impl = impl.CircleImpl() - - @classmethod - def poll(cls, context): - return impl.CircleImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@BlClassRegistry(legacy=True) -class MUV_OT_AlignUV_Straighten(bpy.types.Operator): - - bl_idname = "uv.muv_align_uv_operator_straighten" - bl_label = "Align UV (Straighten)" - bl_description = "Straighten UV coordinates" - bl_options = {'REGISTER', 'UNDO'} - - transmission = BoolProperty( - name="Transmission", - description="Align linked UVs", - default=False - ) - select = BoolProperty( - name="Select", - description="Select UVs which are aligned", - default=False - ) - vertical = BoolProperty( - name="Vert-Infl (Vertical)", - description="Align vertical direction influenced " - "by mesh vertex proportion", - default=False - ) - horizontal = BoolProperty( - name="Vert-Infl (Horizontal)", - description="Align horizontal direction influenced " - "by mesh vertex proportion", - default=False - ) - mesh_infl = FloatProperty( - name="Mesh Influence", - description="Influence rate of mesh vertex", - min=0.0, - max=1.0, - default=0.0 - ) - - def __init__(self): - self.__impl = impl.StraightenImpl() - - @classmethod - def poll(cls, context): - return impl.StraightenImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@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)" - bl_description = "Align UV to XY-axis" - bl_options = {'REGISTER', 'UNDO'} - - transmission = BoolProperty( - name="Transmission", - description="Align linked UVs", - default=False - ) - select = BoolProperty( - name="Select", - description="Select UVs which are aligned", - default=False - ) - vertical = BoolProperty( - name="Vert-Infl (Vertical)", - description="Align vertical direction influenced " - "by mesh vertex proportion", - default=False - ) - horizontal = BoolProperty( - name="Vert-Infl (Horizontal)", - description="Align horizontal direction influenced " - "by mesh vertex proportion", - default=False - ) - location = EnumProperty( - name="Location", - description="Align location", - items=[ - ('LEFT_TOP', "Left/Top", "Align to Left or Top"), - ('MIDDLE', "Middle", "Align to middle"), - ('RIGHT_BOTTOM', "Right/Bottom", "Align to Right or Bottom") - ], - default='MIDDLE' - ) - mesh_infl = FloatProperty( - name="Mesh Influence", - description="Influence rate of mesh vertex", - min=0.0, - max=1.0, - default=0.0 - ) - - def __init__(self): - self.__impl = impl.AxisImpl() - - @classmethod - def poll(cls, context): - return impl.AxisImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/op/align_uv_cursor.py b/uv_magic_uv/legacy/op/align_uv_cursor.py deleted file mode 100644 index 6a08de0b5cab5367c1f3ac927a8958703fca71a4..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/align_uv_cursor.py +++ /dev/null @@ -1,153 +0,0 @@ -# <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 mathutils import Vector -from bpy.props import EnumProperty, BoolProperty, FloatVectorProperty - -from ... import common -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import align_uv_cursor_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "align_uv_cursor" - - @classmethod - def init_props(cls, scene): - def auvc_get_cursor_loc(self): - area, _, space = common.get_space_legacy('IMAGE_EDITOR', 'WINDOW', - 'IMAGE_EDITOR') - bd_size = common.get_uvimg_editor_board_size(area) - loc = space.cursor_location - if bd_size[0] < 0.000001: - cx = 0.0 - else: - cx = loc[0] / bd_size[0] - if bd_size[1] < 0.000001: - cy = 0.0 - else: - cy = loc[1] / bd_size[1] - self['muv_align_uv_cursor_cursor_loc'] = Vector((cx, cy)) - return self.get('muv_align_uv_cursor_cursor_loc', (0.0, 0.0)) - - def auvc_set_cursor_loc(self, value): - self['muv_align_uv_cursor_cursor_loc'] = value - area, _, space = common.get_space_legacy('IMAGE_EDITOR', 'WINDOW', - 'IMAGE_EDITOR') - bd_size = common.get_uvimg_editor_board_size(area) - cx = bd_size[0] * value[0] - cy = bd_size[1] * value[1] - space.cursor_location = Vector((cx, cy)) - - scene.muv_align_uv_cursor_enabled = BoolProperty( - name="Align UV Cursor Enabled", - description="Align UV Cursor is enabled", - default=False - ) - - scene.muv_align_uv_cursor_cursor_loc = FloatVectorProperty( - name="UV Cursor Location", - size=2, - precision=4, - soft_min=-1.0, - soft_max=1.0, - step=1, - default=(0.000, 0.000), - get=auvc_get_cursor_loc, - set=auvc_set_cursor_loc - ) - scene.muv_align_uv_cursor_align_method = EnumProperty( - name="Align Method", - description="Align Method", - default='TEXTURE', - items=[ - ('TEXTURE', "Texture", "Align to texture"), - ('UV', "UV", "Align to UV"), - ('UV_SEL', "UV (Selected)", "Align to Selected UV") - ] - ) - - scene.muv_uv_cursor_location_enabled = BoolProperty( - name="UV Cursor Location Enabled", - description="UV Cursor Location is enabled", - default=False - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_align_uv_cursor_enabled - del scene.muv_align_uv_cursor_cursor_loc - del scene.muv_align_uv_cursor_align_method - - del scene.muv_uv_cursor_location_enabled - - -@BlClassRegistry(legacy=True) -class MUV_OT_AlignUVCursor(bpy.types.Operator): - - bl_idname = "uv.muv_align_uv_cursor_operator" - bl_label = "Align UV Cursor" - bl_description = "Align cursor to the center of UV island" - bl_options = {'REGISTER', 'UNDO'} - - position = EnumProperty( - items=( - ('CENTER', "Center", "Align to Center"), - ('LEFT_TOP', "Left Top", "Align to Left Top"), - ('LEFT_MIDDLE', "Left Middle", "Align to Left Middle"), - ('LEFT_BOTTOM', "Left Bottom", "Align to Left Bottom"), - ('MIDDLE_TOP', "Middle Top", "Align to Middle Top"), - ('MIDDLE_BOTTOM', "Middle Bottom", "Align to Middle Bottom"), - ('RIGHT_TOP', "Right Top", "Align to Right Top"), - ('RIGHT_MIDDLE', "Right Middle", "Align to Right Middle"), - ('RIGHT_BOTTOM', "Right Bottom", "Align to Right Bottom") - ), - name="Position", - description="Align position", - default='CENTER' - ) - base = EnumProperty( - items=( - ('TEXTURE', "Texture", "Align based on Texture"), - ('UV', "UV", "Align to UV"), - ('UV_SEL', "UV (Selected)", "Align to Selected UV") - ), - name="Base", - description="Align base", - default='TEXTURE' - ) - - def __init__(self): - self.__impl = impl.AlignUVCursorLegacyImpl() - - @classmethod - def poll(cls, context): - return impl.AlignUVCursorLegacyImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/op/copy_paste_uv.py b/uv_magic_uv/legacy/op/copy_paste_uv.py deleted file mode 100644 index a8aef017fb2e3c587be00cba29ea169b5775ddff..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/copy_paste_uv.py +++ /dev/null @@ -1,531 +0,0 @@ -# <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 deleted file mode 100644 index e09b003b91a96f5ed5fdf5bdcf7499f7f79d8ee2..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/copy_paste_uv_object.py +++ /dev/null @@ -1,298 +0,0 @@ -# <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 deleted file mode 100644 index bb72d42a1b83708e2608e2db810a4c89bca103c5..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/copy_paste_uv_uvedit.py +++ /dev/null @@ -1,97 +0,0 @@ -# <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 deleted file mode 100644 index d94e4808c269ce75460fa4686a93ce93099ff143..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/flip_rotate_uv.py +++ /dev/null @@ -1,132 +0,0 @@ -# <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 deleted file mode 100644 index e869e5e8ab964f5ef9c52347643398f0315713a2..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/mirror_uv.py +++ /dev/null @@ -1,110 +0,0 @@ -# <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 deleted file mode 100644 index 2988c2ce6cf699e551d05ef71f8cd35d0e67a544..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/move_uv.py +++ /dev/null @@ -1,82 +0,0 @@ -# <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/legacy/op/pack_uv.py b/uv_magic_uv/legacy/op/pack_uv.py deleted file mode 100644 index f2e1a190fce96cf8c6a6cf55266e9f7ce5c257ad..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/pack_uv.py +++ /dev/null @@ -1,129 +0,0 @@ -# <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, -) - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import pack_uv_impl as impl - - -__all__ = [ - 'Properties', - 'MUV_OT_PackUV', -] - - -@PropertyClassRegistry(legacy=True) -class Properties: - idname = "pack_uv" - - @classmethod - def init_props(cls, scene): - scene.muv_pack_uv_enabled = BoolProperty( - name="Pack UV Enabled", - description="Pack UV is enabled", - default=False - ) - scene.muv_pack_uv_allowable_center_deviation = FloatVectorProperty( - name="Allowable Center Deviation", - description="Allowable center deviation to judge same UV island", - min=0.000001, - max=0.1, - default=(0.001, 0.001), - size=2 - ) - scene.muv_pack_uv_allowable_size_deviation = FloatVectorProperty( - name="Allowable Size Deviation", - description="Allowable sizse deviation to judge same UV island", - min=0.000001, - max=0.1, - default=(0.001, 0.001), - size=2 - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_pack_uv_enabled - del scene.muv_pack_uv_allowable_center_deviation - del scene.muv_pack_uv_allowable_size_deviation - - -@BlClassRegistry(legacy=True) -class MUV_OT_PackUV(bpy.types.Operator): - """ - Operation class: Pack UV with same UV islands are integrated - Island matching algorithm - - Same center of UV island - - Same size of UV island - - Same number of UV - """ - - bl_idname = "uv.muv_pack_uv_operator" - bl_label = "Pack UV" - bl_description = "Pack UV (Same UV Islands are integrated)" - bl_options = {'REGISTER', 'UNDO'} - - rotate = BoolProperty( - name="Rotate", - description="Rotate option used by default pack UV function", - default=False) - margin = FloatProperty( - name="Margin", - description="Margin used by default pack UV function", - min=0, - max=1, - default=0.001) - allowable_center_deviation = FloatVectorProperty( - name="Allowable Center Deviation", - description="Allowable center deviation to judge same UV island", - min=0.000001, - max=0.1, - default=(0.001, 0.001), - size=2 - ) - allowable_size_deviation = FloatVectorProperty( - name="Allowable Size Deviation", - description="Allowable sizse deviation to judge same UV island", - min=0.000001, - max=0.1, - default=(0.001, 0.001), - size=2 - ) - - def __init__(self): - self.__impl = impl.PackUVImpl() - - @classmethod - def poll(cls, context): - return impl.PackUVImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/op/preserve_uv_aspect.py b/uv_magic_uv/legacy/op/preserve_uv_aspect.py deleted file mode 100644 index c6693e9ae7c9ca6274955659d06ed738b1c5e55d..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/preserve_uv_aspect.py +++ /dev/null @@ -1,124 +0,0 @@ -# <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 StringProperty, EnumProperty, BoolProperty - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import preserve_uv_aspect_impl as impl - - -__all__ = [ - 'Properties', - 'MUV_OT_PreserveUVAspect', -] - - -@PropertyClassRegistry(legacy=True) -class Properties: - idname = "preserve_uv_aspect" - - @classmethod - def init_props(cls, scene): - def get_loaded_texture_name(_, __): - items = [(key, key, "") for key in bpy.data.images.keys()] - items.append(("None", "None", "")) - return items - - scene.muv_preserve_uv_aspect_enabled = BoolProperty( - name="Preserve UV Aspect Enabled", - description="Preserve UV Aspect is enabled", - default=False - ) - scene.muv_preserve_uv_aspect_tex_image = EnumProperty( - name="Image", - description="Texture Image", - items=get_loaded_texture_name - ) - scene.muv_preserve_uv_aspect_origin = EnumProperty( - name="Origin", - description="Aspect Origin", - items=[ - ('CENTER', 'Center', 'Center'), - ('LEFT_TOP', 'Left Top', 'Left Bottom'), - ('LEFT_CENTER', 'Left Center', 'Left Center'), - ('LEFT_BOTTOM', 'Left Bottom', 'Left Bottom'), - ('CENTER_TOP', 'Center Top', 'Center Top'), - ('CENTER_BOTTOM', 'Center Bottom', 'Center Bottom'), - ('RIGHT_TOP', 'Right Top', 'Right Top'), - ('RIGHT_CENTER', 'Right Center', 'Right Center'), - ('RIGHT_BOTTOM', 'Right Bottom', 'Right Bottom') - - ], - default="CENTER" - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_preserve_uv_aspect_enabled - del scene.muv_preserve_uv_aspect_tex_image - del scene.muv_preserve_uv_aspect_origin - - -@BlClassRegistry(legacy=True) -class MUV_OT_PreserveUVAspect(bpy.types.Operator): - """ - Operation class: Preserve UV Aspect - """ - - bl_idname = "uv.muv_preserve_uv_aspect_operator" - bl_label = "Preserve UV Aspect" - bl_description = "Choose Image" - bl_options = {'REGISTER', 'UNDO'} - - dest_img_name = StringProperty(options={'HIDDEN'}) - origin = EnumProperty( - name="Origin", - description="Aspect Origin", - items=[ - ('CENTER', 'Center', 'Center'), - ('LEFT_TOP', 'Left Top', 'Left Bottom'), - ('LEFT_CENTER', 'Left Center', 'Left Center'), - ('LEFT_BOTTOM', 'Left Bottom', 'Left Bottom'), - ('CENTER_TOP', 'Center Top', 'Center Top'), - ('CENTER_BOTTOM', 'Center Bottom', 'Center Bottom'), - ('RIGHT_TOP', 'Right Top', 'Right Top'), - ('RIGHT_CENTER', 'Right Center', 'Right Center'), - ('RIGHT_BOTTOM', 'Right Bottom', 'Right Bottom') - - ], - default="CENTER" - ) - - def __init__(self): - self.__impl = impl.PreserveUVAspectLegacyImpl() - - @classmethod - def poll(cls, context): - return impl.PreserveUVAspectLegacyImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/op/select_uv.py b/uv_magic_uv/legacy/op/select_uv.py deleted file mode 100644 index c4a7639d0ad14a8890b57fb5f7e68fddbf6439c1..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/select_uv.py +++ /dev/null @@ -1,92 +0,0 @@ -# <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 BoolProperty - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import select_uv_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "select_uv" - - @classmethod - def init_props(cls, scene): - scene.muv_select_uv_enabled = BoolProperty( - name="Select UV Enabled", - description="Select UV is enabled", - default=False - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_select_uv_enabled - - -@BlClassRegistry(legacy=True) -class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator): - """ - Operation class: Select faces which have overlapped UVs - """ - - bl_idname = "uv.muv_select_uv_operator_select_overlapped" - bl_label = "Overlapped" - bl_description = "Select faces which have overlapped UVs" - bl_options = {'REGISTER', 'UNDO'} - - def __init__(self): - self.__impl = impl.SelectOverlappedImpl() - - @classmethod - def poll(cls, context): - return impl.SelectOverlappedImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@BlClassRegistry(legacy=True) -class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator): - """ - Operation class: Select faces which have flipped UVs - """ - - bl_idname = "uv.muv_select_uv_operator_select_flipped" - bl_label = "Flipped" - bl_description = "Select faces which have flipped UVs" - bl_options = {'REGISTER', 'UNDO'} - - def __init__(self): - self.__impl = impl.SelectFlippedImpl() - - @classmethod - def poll(cls, context): - return impl.SelectFlippedImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/op/smooth_uv.py b/uv_magic_uv/legacy/op/smooth_uv.py deleted file mode 100644 index 2e80e98c806c52dff2e3cbf084c51dbcd0a92dea..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/smooth_uv.py +++ /dev/null @@ -1,105 +0,0 @@ -# <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 bpy.props import BoolProperty, FloatProperty - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import smooth_uv_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "smooth_uv" - - @classmethod - def init_props(cls, scene): - scene.muv_smooth_uv_enabled = BoolProperty( - name="Smooth UV Enabled", - description="Smooth UV is enabled", - default=False - ) - scene.muv_smooth_uv_transmission = BoolProperty( - name="Transmission", - description="Smooth linked UVs", - default=False - ) - scene.muv_smooth_uv_mesh_infl = FloatProperty( - name="Mesh Influence", - description="Influence rate of mesh vertex", - min=0.0, - max=1.0, - default=0.0 - ) - scene.muv_smooth_uv_select = BoolProperty( - name="Select", - description="Select UVs which are smoothed", - default=False - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_smooth_uv_enabled - del scene.muv_smooth_uv_transmission - del scene.muv_smooth_uv_mesh_infl - del scene.muv_smooth_uv_select - - -@BlClassRegistry(legacy=True) -class MUV_OT_SmoothUV(bpy.types.Operator): - - bl_idname = "uv.muv_smooth_uv_operator" - bl_label = "Smooth" - bl_description = "Smooth UV coordinates" - bl_options = {'REGISTER', 'UNDO'} - - transmission = BoolProperty( - name="Transmission", - description="Smooth linked UVs", - default=False - ) - mesh_infl = FloatProperty( - name="Mesh Influence", - description="Influence rate of mesh vertex", - min=0.0, - max=1.0, - default=0.0 - ) - select = BoolProperty( - name="Select", - description="Select UVs which are smoothed", - default=False - ) - - def __init__(self): - self.__impl = impl.SmoothUVImpl() - - @classmethod - def poll(cls, context): - return impl.SmoothUVImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/op/texture_lock.py b/uv_magic_uv/legacy/op/texture_lock.py deleted file mode 100644 index ab06baf5a0d62ddaa6033fcad7c7596f56bc970d..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/texture_lock.py +++ /dev/null @@ -1,158 +0,0 @@ -# <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 BoolProperty - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import texture_lock_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "texture_lock" - - @classmethod - def init_props(cls, scene): - class Props(): - verts_orig = None - - scene.muv_props.texture_lock = Props() - - def get_func(_): - return MUV_OT_TextureLock_Intr.is_running(bpy.context) - - def set_func(_, __): - pass - - def update_func(_, __): - bpy.ops.uv.muv_texture_lock_operator_intr('INVOKE_REGION_WIN') - - scene.muv_texture_lock_enabled = BoolProperty( - name="Texture Lock Enabled", - description="Texture Lock is enabled", - default=False - ) - scene.muv_texture_lock_lock = BoolProperty( - name="Texture Lock Locked", - description="Texture Lock is locked", - default=False, - get=get_func, - set=set_func, - update=update_func - ) - scene.muv_texture_lock_connect = BoolProperty( - name="Connect UV", - default=True - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_props.texture_lock - del scene.muv_texture_lock_enabled - del scene.muv_texture_lock_lock - del scene.muv_texture_lock_connect - - -@BlClassRegistry(legacy=True) -class MUV_OT_TextureLock_Lock(bpy.types.Operator): - """ - Operation class: Lock Texture - """ - - bl_idname = "uv.muv_texture_lock_operator_lock" - bl_label = "Lock Texture" - bl_description = "Lock Texture" - bl_options = {'REGISTER', 'UNDO'} - - def __init__(self): - self.__impl = impl.LockImpl() - - @classmethod - def poll(cls, context): - return impl.LockImpl.poll(context) - - @classmethod - def is_ready(cls, context): - return impl.LockImpl.is_ready(context) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@BlClassRegistry(legacy=True) -class MUV_OT_TextureLock_Unlock(bpy.types.Operator): - """ - Operation class: Unlock Texture - """ - - bl_idname = "uv.muv_texture_lock_operator_unlock" - bl_label = "Unlock Texture" - bl_description = "Unlock Texture" - bl_options = {'REGISTER', 'UNDO'} - - connect = BoolProperty( - name="Connect UV", - default=True - ) - - def __init__(self): - self.__impl = impl.UnlockImpl() - - @classmethod - def poll(cls, context): - return impl.UnlockImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@BlClassRegistry(legacy=True) -class MUV_OT_TextureLock_Intr(bpy.types.Operator): - """ - Operation class: Texture Lock (Interactive mode) - """ - - bl_idname = "uv.muv_texture_lock_operator_intr" - bl_label = "Texture Lock (Interactive mode)" - bl_description = "Internal operation for Texture Lock (Interactive mode)" - - def __init__(self): - self.__impl = impl.IntrImpl() - - @classmethod - def poll(cls, context): - return impl.IntrImpl.poll(context) - - @classmethod - def is_running(cls, context): - return impl.IntrImpl.is_running(context) - - def modal(self, context, event): - return self.__impl.modal(self, context, event) - - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) diff --git a/uv_magic_uv/legacy/op/texture_projection.py b/uv_magic_uv/legacy/op/texture_projection.py deleted file mode 100644 index bb73138bb6aa16a1559befe56a75c29ebedb799c..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/texture_projection.py +++ /dev/null @@ -1,296 +0,0 @@ -# <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 bgl -import bmesh -from bpy_extras import view3d_utils -from bpy.props import ( - BoolProperty, - EnumProperty, - FloatProperty, -) - -from ... import common -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import texture_projection_impl as impl - - -@PropertyClassRegistry(legacy=True) -class Properties: - idname = "texture_projection" - - @classmethod - def init_props(cls, scene): - def get_func(_): - return MUV_OT_TextureProjection.is_running(bpy.context) - - def set_func(_, __): - pass - - def update_func(_, __): - bpy.ops.uv.muv_texture_projection_operator('INVOKE_REGION_WIN') - - scene.muv_texture_projection_enabled = BoolProperty( - name="Texture Projection Enabled", - description="Texture Projection is enabled", - default=False - ) - scene.muv_texture_projection_enable = BoolProperty( - name="Texture Projection Enabled", - description="Texture Projection is enabled", - default=False, - get=get_func, - set=set_func, - update=update_func - ) - scene.muv_texture_projection_tex_magnitude = FloatProperty( - name="Magnitude", - description="Texture Magnitude", - default=0.5, - min=0.0, - max=100.0 - ) - scene.muv_texture_projection_tex_image = EnumProperty( - name="Image", - description="Texture Image", - items=impl.get_loaded_texture_name - ) - scene.muv_texture_projection_tex_transparency = FloatProperty( - name="Transparency", - description="Texture Transparency", - default=0.2, - min=0.0, - max=1.0 - ) - scene.muv_texture_projection_adjust_window = BoolProperty( - name="Adjust Window", - description="Size of renderered texture is fitted to window", - default=True - ) - scene.muv_texture_projection_apply_tex_aspect = BoolProperty( - name="Texture Aspect Ratio", - description="Apply Texture Aspect ratio to displayed texture", - default=True - ) - scene.muv_texture_projection_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_texture_projection_enabled - del scene.muv_texture_projection_tex_magnitude - del scene.muv_texture_projection_tex_image - del scene.muv_texture_projection_tex_transparency - del scene.muv_texture_projection_adjust_window - del scene.muv_texture_projection_apply_tex_aspect - del scene.muv_texture_projection_assign_uvmap - - -@BlClassRegistry(legacy=True) -class MUV_OT_TextureProjection(bpy.types.Operator): - """ - Operation class: Texture Projection - Render texture - """ - - bl_idname = "uv.muv_texture_projection_operator" - bl_description = "Render selected texture" - bl_label = "Texture renderer" - - __handle = None - - @classmethod - def poll(cls, context): - # we can not get area/space/region from console - if common.is_console_mode(): - return False - return impl.is_valid_context(context) - - @classmethod - def is_running(cls, _): - return 1 if cls.__handle else 0 - - @classmethod - def handle_add(cls, obj, context): - cls.__handle = bpy.types.SpaceView3D.draw_handler_add( - MUV_OT_TextureProjection.draw_texture, - (obj, context), 'WINDOW', 'POST_PIXEL') - - @classmethod - def handle_remove(cls): - if cls.__handle is not None: - bpy.types.SpaceView3D.draw_handler_remove(cls.__handle, 'WINDOW') - cls.__handle = None - - @classmethod - def draw_texture(cls, _, context): - sc = context.scene - - if not cls.is_running(context): - return - - # no textures are selected - if sc.muv_texture_projection_tex_image == "None": - return - - # get texture to be renderred - img = bpy.data.images[sc.muv_texture_projection_tex_image] - - # setup rendering region - rect = impl.get_canvas(context, sc.muv_texture_projection_tex_magnitude) - positions = [ - [rect.x0, rect.y0], - [rect.x0, rect.y1], - [rect.x1, rect.y1], - [rect.x1, rect.y0] - ] - tex_coords = [ - [0.0, 0.0], - [0.0, 1.0], - [1.0, 1.0], - [1.0, 0.0] - ] - - # OpenGL configuration - bgl.glEnable(bgl.GL_BLEND) - bgl.glEnable(bgl.GL_TEXTURE_2D) - if img.bindcode: - bind = img.bindcode[0] - bgl.glBindTexture(bgl.GL_TEXTURE_2D, bind) - bgl.glTexParameteri( - bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR) - bgl.glTexParameteri( - bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR) - bgl.glTexEnvi( - bgl.GL_TEXTURE_ENV, bgl.GL_TEXTURE_ENV_MODE, bgl.GL_MODULATE) - - # render texture - bgl.glBegin(bgl.GL_QUADS) - bgl.glColor4f(1.0, 1.0, 1.0, - sc.muv_texture_projection_tex_transparency) - for (v1, v2), (u, v) in zip(positions, tex_coords): - bgl.glTexCoord2f(u, v) - bgl.glVertex2f(v1, v2) - bgl.glEnd() - - def invoke(self, context, _): - if not MUV_OT_TextureProjection.is_running(context): - MUV_OT_TextureProjection.handle_add(self, context) - else: - MUV_OT_TextureProjection.handle_remove() - - if context.area: - context.area.tag_redraw() - - return {'FINISHED'} - - -@BlClassRegistry(legacy=True) -class MUV_OT_TextureProjection_Project(bpy.types.Operator): - """ - Operation class: Project texture - """ - - bl_idname = "uv.muv_texture_projection_operator_project" - bl_label = "Project Texture" - bl_description = "Project Texture" - 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 - if not MUV_OT_TextureProjection.is_running(context): - return False - return impl.is_valid_context(context) - - def execute(self, context): - sc = context.scene - - if sc.muv_texture_projection_tex_image == "None": - self.report({'WARNING'}, "No textures are selected") - return {'CANCELLED'} - - _, region, space = common.get_space_legacy( - 'VIEW_3D', 'WINDOW', 'VIEW_3D') - - # get faces to be texture projected - obj = context.active_object - world_mat = obj.matrix_world - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - - # get UV and texture layer - if not bm.loops.layers.uv: - if sc.muv_texture_projection_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() - tex_layer = bm.faces.layers.tex.verify() - - sel_faces = [f for f in bm.faces if f.select] - - # transform 3d space to screen region - v_screen = [ - view3d_utils.location_3d_to_region_2d( - region, - space.region_3d, - world_mat * l.vert.co) - for f in sel_faces for l in f.loops - ] - - # transform screen region to canvas - v_canvas = [ - impl.region_to_canvas( - v, - impl.get_canvas(bpy.context, - sc.muv_texture_projection_tex_magnitude) - ) for v in v_screen - ] - - # project texture to object - i = 0 - for f in sel_faces: - f[tex_layer].image = \ - bpy.data.images[sc.muv_texture_projection_tex_image] - for l in f.loops: - l[uv_layer].uv = v_canvas[i].to_2d() - i = i + 1 - - common.redraw_all_areas() - bmesh.update_edit_mesh(obj.data) - - return {'FINISHED'} diff --git a/uv_magic_uv/legacy/op/texture_wrap.py b/uv_magic_uv/legacy/op/texture_wrap.py deleted file mode 100644 index 85c9d1748ffe8cc9df2d011ec4d56c5c5a9b056b..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/texture_wrap.py +++ /dev/null @@ -1,113 +0,0 @@ -# <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 ( - BoolProperty, -) - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import texture_wrap_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "texture_wrap" - - @classmethod - def init_props(cls, scene): - class Props(): - ref_face_index = -1 - ref_obj = None - - scene.muv_props.texture_wrap = Props() - - scene.muv_texture_wrap_enabled = BoolProperty( - name="Texture Wrap", - description="Texture Wrap is enabled", - default=False - ) - scene.muv_texture_wrap_set_and_refer = BoolProperty( - name="Set and Refer", - description="Refer and set UV", - default=True - ) - scene.muv_texture_wrap_selseq = BoolProperty( - name="Selection Sequence", - description="Set UV sequentially", - default=False - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_props.texture_wrap - del scene.muv_texture_wrap_enabled - del scene.muv_texture_wrap_set_and_refer - del scene.muv_texture_wrap_selseq - - -@BlClassRegistry(legacy=True) -class MUV_OT_TextureWrap_Refer(bpy.types.Operator): - """ - Operation class: Refer UV - """ - - bl_idname = "uv.muv_texture_wrap_operator_refer" - bl_label = "Refer" - bl_description = "Refer UV" - bl_options = {'REGISTER', 'UNDO'} - - def __init__(self): - self.__impl = impl.ReferImpl() - - @classmethod - def poll(cls, context): - return impl.ReferImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@BlClassRegistry(legacy=True) -class MUV_OT_TextureWrap_Set(bpy.types.Operator): - """ - Operation class: Set UV - """ - - bl_idname = "uv.muv_texture_wrap_operator_set" - bl_label = "Set" - bl_description = "Set UV" - bl_options = {'REGISTER', 'UNDO'} - - def __init__(self): - self.__impl = impl.SetImpl() - - @classmethod - def poll(cls, context): - return impl.SetImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/op/transfer_uv.py b/uv_magic_uv/legacy/op/transfer_uv.py deleted file mode 100644 index cd0e4dd9feffde6d3d95a6e8484bcdc38e24ce48..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/transfer_uv.py +++ /dev/null @@ -1,172 +0,0 @@ -# <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/legacy/op/unwrap_constraint.py b/uv_magic_uv/legacy/op/unwrap_constraint.py deleted file mode 100644 index b7faa77a39f876e6a50cb834ec574ab33cc85089..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/unwrap_constraint.py +++ /dev/null @@ -1,125 +0,0 @@ -# ##### 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 ( - BoolProperty, - EnumProperty, - FloatProperty, -) - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import unwrap_constraint_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "unwrap_constraint" - - @classmethod - def init_props(cls, scene): - scene.muv_unwrap_constraint_enabled = BoolProperty( - name="Unwrap Constraint Enabled", - description="Unwrap Constraint is enabled", - default=False - ) - scene.muv_unwrap_constraint_u_const = BoolProperty( - name="U-Constraint", - description="Keep UV U-axis coordinate", - default=False - ) - scene.muv_unwrap_constraint_v_const = BoolProperty( - name="V-Constraint", - description="Keep UV V-axis coordinate", - default=False - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_unwrap_constraint_enabled - del scene.muv_unwrap_constraint_u_const - del scene.muv_unwrap_constraint_v_const - - -@BlClassRegistry(legacy=True) -class MUV_OT_UnwrapConstraint(bpy.types.Operator): - """ - Operation class: Unwrap with constrain UV coordinate - """ - - bl_idname = "uv.muv_unwrap_constraint_operator" - bl_label = "Unwrap Constraint" - bl_description = "Unwrap while keeping uv coordinate" - bl_options = {'REGISTER', 'UNDO'} - - # property for original unwrap - method = EnumProperty( - name="Method", - description="Unwrapping method", - items=[ - ('ANGLE_BASED', 'Angle Based', 'Angle Based'), - ('CONFORMAL', 'Conformal', 'Conformal') - ], - default='ANGLE_BASED') - fill_holes = BoolProperty( - name="Fill Holes", - description="Virtual fill holes in meshes before unwrapping", - default=True) - correct_aspect = BoolProperty( - name="Correct Aspect", - description="Map UVs taking image aspect ratio into account", - default=True) - use_subsurf_data = BoolProperty( - name="Use Subsurf Modifier", - description="""Map UVs taking vertex position after subsurf - into account""", - default=False) - margin = FloatProperty( - name="Margin", - description="Space between islands", - max=1.0, - min=0.0, - default=0.001) - - # property for this operation - u_const = BoolProperty( - name="U-Constraint", - description="Keep UV U-axis coordinate", - default=False - ) - v_const = BoolProperty( - name="V-Constraint", - description="Keep UV V-axis coordinate", - default=False - ) - - def __init__(self): - self.__impl = impl.UnwrapConstraintImpl() - - @classmethod - def poll(cls, context): - return impl.UnwrapConstraintImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/op/uv_bounding_box.py b/uv_magic_uv/legacy/op/uv_bounding_box.py deleted file mode 100644 index 74c5f15c5f915f61ec02875fff481cb687c99fe6..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/uv_bounding_box.py +++ /dev/null @@ -1,810 +0,0 @@ -# <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 enum import IntEnum -import math - -import bpy -import bgl -import mathutils -import bmesh -from bpy.props import BoolProperty, EnumProperty - -from ... import common -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import uv_bounding_box_impl as impl - - -MAX_VALUE = 100000.0 - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "uv_bounding_box" - - @classmethod - def init_props(cls, scene): - class Props(): - uv_info_ini = [] - ctrl_points_ini = [] - ctrl_points = [] - - scene.muv_props.uv_bounding_box = Props() - - def get_func(_): - return MUV_OT_UVBoundingBox.is_running(bpy.context) - - def set_func(_, __): - pass - - def update_func(_, __): - bpy.ops.uv.muv_uv_bounding_box_operator('INVOKE_REGION_WIN') - - scene.muv_uv_bounding_box_enabled = BoolProperty( - name="UV Bounding Box Enabled", - description="UV Bounding Box is enabled", - default=False - ) - scene.muv_uv_bounding_box_show = BoolProperty( - name="UV Bounding Box Showed", - description="UV Bounding Box is showed", - default=False, - get=get_func, - set=set_func, - update=update_func - ) - scene.muv_uv_bounding_box_uniform_scaling = BoolProperty( - name="Uniform Scaling", - description="Enable Uniform Scaling", - default=False - ) - scene.muv_uv_bounding_box_boundary = EnumProperty( - name="Boundary", - description="Boundary", - default='UV_SEL', - items=[ - ('UV', "UV", "Boundary is decided by UV"), - ('UV_SEL', "UV (Selected)", - "Boundary is decided by Selected UV") - ] - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_props.uv_bounding_box - del scene.muv_uv_bounding_box_enabled - del scene.muv_uv_bounding_box_show - del scene.muv_uv_bounding_box_uniform_scaling - del scene.muv_uv_bounding_box_boundary - - -class CommandBase: - """ - Custom class: Base class of command - """ - - def __init__(self): - self.op = 'NONE' # operation - - def to_matrix(self): - # mat = I - mat = mathutils.Matrix() - mat.identity() - return mat - - -class TranslationCommand(CommandBase): - """ - Custom class: Translation operation - """ - - def __init__(self, ix, iy): - super().__init__() - self.op = 'TRANSLATION' - self.__x = ix # current x - self.__y = iy # current y - self.__ix = ix # initial x - self.__iy = iy # initial y - - def to_matrix(self): - # mat = Mt - dx = self.__x - self.__ix - dy = self.__y - self.__iy - return mathutils.Matrix.Translation((dx, dy, 0)) - - def set(self, x, y): - self.__x = x - self.__y = y - - -class RotationCommand(CommandBase): - """ - Custom class: Rotation operation - """ - - def __init__(self, ix, iy, cx, cy): - super().__init__() - self.op = 'ROTATION' - self.__x = ix # current x - self.__y = iy # current y - self.__cx = cx # center of rotation x - self.__cy = cy # center of rotation y - dx = self.__x - self.__cx - dy = self.__y - self.__cy - self.__iangle = math.atan2(dy, dx) # initial rotation angle - - def to_matrix(self): - # mat = Mt * Mr * Mt^-1 - dx = self.__x - self.__cx - dy = self.__y - self.__cy - angle = math.atan2(dy, dx) - self.__iangle - mti = mathutils.Matrix.Translation((-self.__cx, -self.__cy, 0.0)) - mr = mathutils.Matrix.Rotation(angle, 4, 'Z') - mt = mathutils.Matrix.Translation((self.__cx, self.__cy, 0.0)) - return mt * mr * mti - - def set(self, x, y): - self.__x = x - self.__y = y - - -class ScalingCommand(CommandBase): - """ - Custom class: Scaling operation - """ - - def __init__(self, ix, iy, ox, oy, dir_x, dir_y, mat): - super().__init__() - self.op = 'SCALING' - self.__ix = ix # initial x - self.__iy = iy # initial y - self.__x = ix # current x - self.__y = iy # current y - self.__ox = ox # origin of scaling x - self.__oy = oy # origin of scaling y - self.__dir_x = dir_x # direction of scaling x - self.__dir_y = dir_y # direction of scaling y - self.__mat = mat - # initial origin of scaling = M(to original transform) * (ox, oy) - iov = mat * mathutils.Vector((ox, oy, 0.0)) - self.__iox = iov.x # initial origin of scaling X - self.__ioy = iov.y # initial origin of scaling y - - def to_matrix(self): - """ - mat = M(to original transform)^-1 * Mt(to origin) * Ms * - Mt(to origin)^-1 * M(to original transform) - """ - m = self.__mat - mi = self.__mat.inverted() - mtoi = mathutils.Matrix.Translation((-self.__iox, -self.__ioy, 0.0)) - mto = mathutils.Matrix.Translation((self.__iox, self.__ioy, 0.0)) - # every point must be transformed to origin - t = m * mathutils.Vector((self.__ix, self.__iy, 0.0)) - tix, tiy = t.x, t.y - t = m * mathutils.Vector((self.__ox, self.__oy, 0.0)) - tox, toy = t.x, t.y - t = m * mathutils.Vector((self.__x, self.__y, 0.0)) - tx, ty = t.x, t.y - ms = mathutils.Matrix() - ms.identity() - if self.__dir_x == 1: - ms[0][0] = (tx - tox) * self.__dir_x / (tix - tox) - if self.__dir_y == 1: - ms[1][1] = (ty - toy) * self.__dir_y / (tiy - toy) - return mi * mto * ms * mtoi * m - - def set(self, x, y): - self.__x = x - self.__y = y - - -class UniformScalingCommand(CommandBase): - """ - Custom class: Uniform Scaling operation - """ - - def __init__(self, ix, iy, ox, oy, mat): - super().__init__() - self.op = 'SCALING' - self.__ix = ix # initial x - self.__iy = iy # initial y - self.__x = ix # current x - self.__y = iy # current y - self.__ox = ox # origin of scaling x - self.__oy = oy # origin of scaling y - self.__mat = mat - # initial origin of scaling = M(to original transform) * (ox, oy) - iov = mat * mathutils.Vector((ox, oy, 0.0)) - self.__iox = iov.x # initial origin of scaling x - self.__ioy = iov.y # initial origin of scaling y - self.__dir_x = 1 - self.__dir_y = 1 - - def to_matrix(self): - """ - mat = M(to original transform)^-1 * Mt(to origin) * Ms * - Mt(to origin)^-1 * M(to original transform) - """ - m = self.__mat - mi = self.__mat.inverted() - mtoi = mathutils.Matrix.Translation((-self.__iox, -self.__ioy, 0.0)) - mto = mathutils.Matrix.Translation((self.__iox, self.__ioy, 0.0)) - # every point must be transformed to origin - t = m * mathutils.Vector((self.__ix, self.__iy, 0.0)) - tix, tiy = t.x, t.y - t = m * mathutils.Vector((self.__ox, self.__oy, 0.0)) - tox, toy = t.x, t.y - t = m * mathutils.Vector((self.__x, self.__y, 0.0)) - tx, ty = t.x, t.y - ms = mathutils.Matrix() - ms.identity() - tir = math.sqrt((tix - tox) * (tix - tox) + (tiy - toy) * (tiy - toy)) - tr = math.sqrt((tx - tox) * (tx - tox) + (ty - toy) * (ty - toy)) - - sr = tr / tir - - if ((tx - tox) * (tix - tox)) > 0: - self.__dir_x = 1 - else: - self.__dir_x = -1 - if ((ty - toy) * (tiy - toy)) > 0: - self.__dir_y = 1 - else: - self.__dir_y = -1 - - ms[0][0] = sr * self.__dir_x - ms[1][1] = sr * self.__dir_y - - return mi * mto * ms * mtoi * m - - def set(self, x, y): - self.__x = x - self.__y = y - - -class CommandExecuter: - """ - Custom class: manage command history and execute command - """ - - def __init__(self): - self.__cmd_list = [] # history - self.__cmd_list_redo = [] # redo list - - def execute(self, begin=0, end=-1): - """ - create matrix from history - """ - mat = mathutils.Matrix() - mat.identity() - for i, cmd in enumerate(self.__cmd_list): - if begin <= i and (end == -1 or i <= end): - mat = cmd.to_matrix() * mat - return mat - - def undo_size(self): - """ - get history size - """ - return len(self.__cmd_list) - - def top(self): - """ - get top of history - """ - if len(self.__cmd_list) <= 0: - return None - return self.__cmd_list[-1] - - def append(self, cmd): - """ - append command - """ - self.__cmd_list.append(cmd) - self.__cmd_list_redo = [] - - def undo(self): - """ - undo command - """ - if len(self.__cmd_list) <= 0: - return - self.__cmd_list_redo.append(self.__cmd_list.pop()) - - def redo(self): - """ - redo command - """ - if len(self.__cmd_list_redo) <= 0: - return - self.__cmd_list.append(self.__cmd_list_redo.pop()) - - def pop(self): - if len(self.__cmd_list) <= 0: - return None - return self.__cmd_list.pop() - - def push(self, cmd): - self.__cmd_list.append(cmd) - - -class State(IntEnum): - """ - Enum: State definition used by MUV_UVBBStateMgr - """ - NONE = 0 - TRANSLATING = 1 - SCALING_1 = 2 - SCALING_2 = 3 - SCALING_3 = 4 - SCALING_4 = 5 - SCALING_5 = 6 - SCALING_6 = 7 - SCALING_7 = 8 - SCALING_8 = 9 - ROTATING = 10 - UNIFORM_SCALING_1 = 11 - UNIFORM_SCALING_2 = 12 - UNIFORM_SCALING_3 = 13 - UNIFORM_SCALING_4 = 14 - - -class StateBase: - """ - Custom class: Base class of state - """ - - def __init__(self): - pass - - def update(self, context, event, ctrl_points, mouse_view): - raise NotImplementedError - - -class StateNone(StateBase): - """ - Custom class: - No state - Wait for event from mouse - """ - - def __init__(self, cmd_exec): - super().__init__() - self.__cmd_exec = cmd_exec - - def update(self, context, event, ctrl_points, mouse_view): - """ - Update state - """ - prefs = context.user_preferences.addons["uv_magic_uv"].preferences - cp_react_size = prefs.uv_bounding_box_cp_react_size - is_uscaling = context.scene.muv_uv_bounding_box_uniform_scaling - if (event.type == 'LEFTMOUSE') and (event.value == 'PRESS'): - x, y = context.region.view2d.view_to_region( - mouse_view.x, mouse_view.y) - for i, p in enumerate(ctrl_points): - px, py = context.region.view2d.view_to_region(p.x, p.y) - in_cp_x = (px + cp_react_size > x and - px - cp_react_size < x) - in_cp_y = (py + cp_react_size > y and - py - cp_react_size < y) - if in_cp_x and in_cp_y: - if is_uscaling: - arr = [1, 3, 6, 8] - if i in arr: - return ( - State.UNIFORM_SCALING_1 + - arr.index(i) - ) - else: - return State.TRANSLATING + i - - return State.NONE - - -class StateTranslating(StateBase): - """ - Custom class: Translating state - """ - - def __init__(self, cmd_exec, ctrl_points): - super().__init__() - self.__cmd_exec = cmd_exec - ix, iy = ctrl_points[0].x, ctrl_points[0].y - self.__cmd_exec.append(TranslationCommand(ix, iy)) - - def update(self, context, event, ctrl_points, mouse_view): - if event.type == 'LEFTMOUSE': - if event.value == 'RELEASE': - return State.NONE - if event.type == 'MOUSEMOVE': - x, y = mouse_view.x, mouse_view.y - self.__cmd_exec.top().set(x, y) - return State.TRANSLATING - - -class StateScaling(StateBase): - """ - Custom class: Scaling state - """ - - def __init__(self, cmd_exec, state, ctrl_points): - super().__init__() - self.__state = state - self.__cmd_exec = cmd_exec - dir_x_list = [1, 1, 1, 0, 0, 1, 1, 1] - dir_y_list = [1, 0, 1, 1, 1, 1, 0, 1] - idx = state - 2 - ix, iy = ctrl_points[idx + 1].x, ctrl_points[idx + 1].y - ox, oy = ctrl_points[8 - idx].x, ctrl_points[8 - idx].y - dir_x, dir_y = dir_x_list[idx], dir_y_list[idx] - mat = self.__cmd_exec.execute(end=self.__cmd_exec.undo_size()) - self.__cmd_exec.append( - ScalingCommand(ix, iy, ox, oy, dir_x, dir_y, mat.inverted())) - - def update(self, context, event, ctrl_points, mouse_view): - if event.type == 'LEFTMOUSE': - if event.value == 'RELEASE': - return State.NONE - if event.type == 'MOUSEMOVE': - x, y = mouse_view.x, mouse_view.y - self.__cmd_exec.top().set(x, y) - return self.__state - - -class StateUniformScaling(StateBase): - """ - Custom class: Uniform Scaling state - """ - - def __init__(self, cmd_exec, state, ctrl_points): - super().__init__() - self.__state = state - self.__cmd_exec = cmd_exec - icp_idx = [1, 3, 6, 8] - ocp_idx = [8, 6, 3, 1] - idx = state - State.UNIFORM_SCALING_1 - ix, iy = ctrl_points[icp_idx[idx]].x, ctrl_points[icp_idx[idx]].y - ox, oy = ctrl_points[ocp_idx[idx]].x, ctrl_points[ocp_idx[idx]].y - mat = self.__cmd_exec.execute(end=self.__cmd_exec.undo_size()) - self.__cmd_exec.append(UniformScalingCommand( - ix, iy, ox, oy, mat.inverted())) - - def update(self, context, event, ctrl_points, mouse_view): - if event.type == 'LEFTMOUSE': - if event.value == 'RELEASE': - return State.NONE - if event.type == 'MOUSEMOVE': - x, y = mouse_view.x, mouse_view.y - self.__cmd_exec.top().set(x, y) - - return self.__state - - -class StateRotating(StateBase): - """ - Custom class: Rotating state - """ - - def __init__(self, cmd_exec, ctrl_points): - super().__init__() - self.__cmd_exec = cmd_exec - ix, iy = ctrl_points[9].x, ctrl_points[9].y - ox, oy = ctrl_points[0].x, ctrl_points[0].y - self.__cmd_exec.append(RotationCommand(ix, iy, ox, oy)) - - def update(self, context, event, ctrl_points, mouse_view): - if event.type == 'LEFTMOUSE': - if event.value == 'RELEASE': - return State.NONE - if event.type == 'MOUSEMOVE': - x, y = mouse_view.x, mouse_view.y - self.__cmd_exec.top().set(x, y) - return State.ROTATING - - -class StateManager: - """ - Custom class: Manage state about this feature - """ - - def __init__(self, cmd_exec): - self.__cmd_exec = cmd_exec # command executer - self.__state = State.NONE # current state - self.__state_obj = StateNone(self.__cmd_exec) - - def __update_state(self, next_state, ctrl_points): - """ - Update state - """ - - if next_state == self.__state: - return - obj = None - if next_state == State.TRANSLATING: - obj = StateTranslating(self.__cmd_exec, ctrl_points) - elif State.SCALING_1 <= next_state <= State.SCALING_8: - obj = StateScaling( - self.__cmd_exec, next_state, ctrl_points) - elif next_state == State.ROTATING: - obj = StateRotating(self.__cmd_exec, ctrl_points) - elif next_state == State.NONE: - obj = StateNone(self.__cmd_exec) - elif (State.UNIFORM_SCALING_1 <= next_state <= - State.UNIFORM_SCALING_4): - obj = StateUniformScaling( - self.__cmd_exec, next_state, ctrl_points) - - if obj is not None: - self.__state_obj = obj - - self.__state = next_state - - def update(self, context, ctrl_points, event): - mouse_region = mathutils.Vector(( - event.mouse_region_x, event.mouse_region_y)) - mouse_view = mathutils.Vector((context.region.view2d.region_to_view( - mouse_region.x, mouse_region.y))) - next_state = self.__state_obj.update( - context, event, ctrl_points, mouse_view) - self.__update_state(next_state, ctrl_points) - - return self.__state - - -@BlClassRegistry(legacy=True) -class MUV_OT_UVBoundingBox(bpy.types.Operator): - """ - Operation class: UV Bounding Box - """ - - bl_idname = "uv.muv_uv_bounding_box_operator" - bl_label = "UV Bounding Box" - bl_description = "Internal operation for UV Bounding Box" - bl_options = {'REGISTER', 'UNDO'} - - def __init__(self): - self.__timer = None - self.__cmd_exec = CommandExecuter() # Command executor - self.__state_mgr = StateManager(self.__cmd_exec) # State Manager - - __handle = None - __timer = None - - @classmethod - def poll(cls, context): - # we can not get area/space/region from console - if common.is_console_mode(): - return False - return impl.is_valid_context(context) - - @classmethod - def is_running(cls, _): - return 1 if cls.__handle else 0 - - @classmethod - def handle_add(cls, obj, context): - if cls.__handle is None: - sie = bpy.types.SpaceImageEditor - cls.__handle = sie.draw_handler_add( - cls.draw_bb, (obj, context), "WINDOW", "POST_PIXEL") - if cls.__timer is None: - cls.__timer = context.window_manager.event_timer_add( - 0.1, window=context.window) - context.window_manager.modal_handler_add(obj) - - @classmethod - def handle_remove(cls, context): - if cls.__handle is not None: - sie = bpy.types.SpaceImageEditor - sie.draw_handler_remove(cls.__handle, "WINDOW") - cls.__handle = None - if cls.__timer is not None: - context.window_manager.event_timer_remove(cls.__timer) - cls.__timer = None - - @classmethod - def __draw_ctrl_point(cls, context, pos): - """ - Draw control point - """ - prefs = context.user_preferences.addons["uv_magic_uv"].preferences - cp_size = prefs.uv_bounding_box_cp_size - offset = cp_size / 2 - verts = [ - [pos.x - offset, pos.y - offset], - [pos.x - offset, pos.y + offset], - [pos.x + offset, pos.y + offset], - [pos.x + offset, pos.y - offset] - ] - bgl.glEnable(bgl.GL_BLEND) - bgl.glBegin(bgl.GL_QUADS) - bgl.glColor4f(1.0, 1.0, 1.0, 1.0) - for (x, y) in verts: - bgl.glVertex2f(x, y) - bgl.glEnd() - - @classmethod - def draw_bb(cls, _, context): - """ - Draw bounding box - """ - props = context.scene.muv_props.uv_bounding_box - - if not MUV_OT_UVBoundingBox.is_running(context): - return - - if not impl.is_valid_context(context): - return - - for cp in props.ctrl_points: - cls.__draw_ctrl_point( - context, mathutils.Vector( - context.region.view2d.view_to_region(cp.x, cp.y))) - - def __get_uv_info(self, context): - """ - Get UV coordinate - """ - sc = context.scene - obj = context.active_object - uv_info = [] - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - if not bm.loops.layers.uv: - return None - uv_layer = bm.loops.layers.uv.verify() - for f in bm.faces: - if not f.select: - continue - for i, l in enumerate(f.loops): - if sc.muv_uv_bounding_box_boundary == 'UV_SEL': - if l[uv_layer].select: - uv_info.append((f.index, i, l[uv_layer].uv.copy())) - elif sc.muv_uv_bounding_box_boundary == 'UV': - uv_info.append((f.index, i, l[uv_layer].uv.copy())) - if not uv_info: - return None - return uv_info - - def __get_ctrl_point(self, uv_info_ini): - """ - Get control point - """ - left = MAX_VALUE - right = -MAX_VALUE - top = -MAX_VALUE - bottom = MAX_VALUE - - for info in uv_info_ini: - uv = info[2] - if uv.x < left: - left = uv.x - if uv.x > right: - right = uv.x - if uv.y < bottom: - bottom = uv.y - if uv.y > top: - top = uv.y - - points = [ - mathutils.Vector(( - (left + right) * 0.5, (top + bottom) * 0.5, 0.0 - )), - mathutils.Vector((left, top, 0.0)), - mathutils.Vector((left, (top + bottom) * 0.5, 0.0)), - mathutils.Vector((left, bottom, 0.0)), - mathutils.Vector(((left + right) * 0.5, top, 0.0)), - mathutils.Vector(((left + right) * 0.5, bottom, 0.0)), - mathutils.Vector((right, top, 0.0)), - mathutils.Vector((right, (top + bottom) * 0.5, 0.0)), - mathutils.Vector((right, bottom, 0.0)), - mathutils.Vector(((left + right) * 0.5, top + 0.03, 0.0)) - ] - - return points - - def __update_uvs(self, context, uv_info_ini, trans_mat): - """ - Update UV coordinate - """ - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - if not bm.loops.layers.uv: - return - uv_layer = bm.loops.layers.uv.verify() - for info in uv_info_ini: - fidx = info[0] - lidx = info[1] - uv = info[2] - v = mathutils.Vector((uv.x, uv.y, 0.0)) - av = trans_mat * v - bm.faces[fidx].loops[lidx][uv_layer].uv = mathutils.Vector( - (av.x, av.y)) - - def __update_ctrl_point(self, ctrl_points_ini, trans_mat): - """ - Update control point - """ - return [trans_mat * cp for cp in ctrl_points_ini] - - def modal(self, context, event): - props = context.scene.muv_props.uv_bounding_box - common.redraw_all_areas() - - if not MUV_OT_UVBoundingBox.is_running(context): - return {'FINISHED'} - - if not impl.is_valid_context(context): - MUV_OT_UVBoundingBox.handle_remove(context) - return {'FINISHED'} - - region_types = [ - 'HEADER', - 'UI', - 'TOOLS', - ] - if not common.mouse_on_area_legacy(event, 'IMAGE_EDITOR') or \ - common.mouse_on_regions_legacy(event, 'IMAGE_EDITOR', region_types): - return {'PASS_THROUGH'} - - if event.type == 'TIMER': - trans_mat = self.__cmd_exec.execute() - self.__update_uvs(context, props.uv_info_ini, trans_mat) - props.ctrl_points = self.__update_ctrl_point( - props.ctrl_points_ini, trans_mat) - - state = self.__state_mgr.update(context, props.ctrl_points, event) - if state == State.NONE: - return {'PASS_THROUGH'} - - return {'RUNNING_MODAL'} - - def invoke(self, context, _): - props = context.scene.muv_props.uv_bounding_box - - 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'} - - 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() - # Update is needed in order to display control point - self.__update_uvs(context, props.uv_info_ini, trans_mat) - props.ctrl_points = self.__update_ctrl_point( - props.ctrl_points_ini, trans_mat) - - return {'RUNNING_MODAL'} diff --git a/uv_magic_uv/legacy/op/uv_inspection.py b/uv_magic_uv/legacy/op/uv_inspection.py deleted file mode 100644 index df7e17e940595772978ac938166293181eba3376..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/uv_inspection.py +++ /dev/null @@ -1,231 +0,0 @@ -# <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 bgl -from bpy.props import BoolProperty, EnumProperty - -from ... import common -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import uv_inspection_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "uv_inspection" - - @classmethod - def init_props(cls, scene): - class Props(): - overlapped_info = [] - flipped_info = [] - - scene.muv_props.uv_inspection = Props() - - def get_func(_): - return MUV_OT_UVInspection_Render.is_running(bpy.context) - - def set_func(_, __): - pass - - def update_func(_, __): - bpy.ops.uv.muv_uv_inspection_operator_render('INVOKE_REGION_WIN') - - scene.muv_uv_inspection_enabled = BoolProperty( - name="UV Inspection Enabled", - description="UV Inspection is enabled", - default=False - ) - scene.muv_uv_inspection_show = BoolProperty( - name="UV Inspection Showed", - description="UV Inspection is showed", - default=False, - get=get_func, - set=set_func, - update=update_func - ) - scene.muv_uv_inspection_show_overlapped = BoolProperty( - name="Overlapped", - description="Show overlapped UVs", - default=False - ) - scene.muv_uv_inspection_show_flipped = BoolProperty( - name="Flipped", - description="Show flipped UVs", - default=False - ) - scene.muv_uv_inspection_show_mode = EnumProperty( - name="Mode", - description="Show mode", - items=[ - ('PART', "Part", "Show only overlapped/flipped part"), - ('FACE', "Face", "Show overlapped/flipped face") - ], - default='PART' - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_props.uv_inspection - del scene.muv_uv_inspection_enabled - del scene.muv_uv_inspection_show - del scene.muv_uv_inspection_show_overlapped - del scene.muv_uv_inspection_show_flipped - del scene.muv_uv_inspection_show_mode - - -@BlClassRegistry(legacy=True) -class MUV_OT_UVInspection_Render(bpy.types.Operator): - """ - Operation class: Render UV Inspection - No operation (only rendering) - """ - - bl_idname = "uv.muv_uv_inspection_operator_render" - bl_description = "Render overlapped/flipped UVs" - bl_label = "Overlapped/Flipped UV renderer" - - __handle = None - - @classmethod - def poll(cls, context): - # we can not get area/space/region from console - if common.is_console_mode(): - return False - return impl.is_valid_context(context) - - @classmethod - def is_running(cls, _): - return 1 if cls.__handle else 0 - - @classmethod - def handle_add(cls, obj, context): - sie = bpy.types.SpaceImageEditor - cls.__handle = sie.draw_handler_add( - MUV_OT_UVInspection_Render.draw, (obj, context), - 'WINDOW', 'POST_PIXEL') - - @classmethod - def handle_remove(cls): - if cls.__handle is not None: - bpy.types.SpaceImageEditor.draw_handler_remove( - cls.__handle, 'WINDOW') - cls.__handle = None - - @staticmethod - def draw(_, context): - sc = context.scene - props = sc.muv_props.uv_inspection - prefs = context.user_preferences.addons["uv_magic_uv"].preferences - - if not MUV_OT_UVInspection_Render.is_running(context): - return - - # OpenGL configuration - bgl.glEnable(bgl.GL_BLEND) - - # render overlapped UV - if sc.muv_uv_inspection_show_overlapped: - color = prefs.uv_inspection_overlapped_color - for info in props.overlapped_info: - if sc.muv_uv_inspection_show_mode == 'PART': - for poly in info["polygons"]: - bgl.glBegin(bgl.GL_TRIANGLE_FAN) - bgl.glColor4f(color[0], color[1], color[2], color[3]) - for uv in poly: - x, y = context.region.view2d.view_to_region( - uv.x, uv.y) - bgl.glVertex2f(x, y) - bgl.glEnd() - elif sc.muv_uv_inspection_show_mode == 'FACE': - bgl.glBegin(bgl.GL_TRIANGLE_FAN) - bgl.glColor4f(color[0], color[1], color[2], color[3]) - for uv in info["subject_uvs"]: - x, y = context.region.view2d.view_to_region(uv.x, uv.y) - bgl.glVertex2f(x, y) - bgl.glEnd() - - # render flipped UV - if sc.muv_uv_inspection_show_flipped: - color = prefs.uv_inspection_flipped_color - for info in props.flipped_info: - if sc.muv_uv_inspection_show_mode == 'PART': - for poly in info["polygons"]: - bgl.glBegin(bgl.GL_TRIANGLE_FAN) - bgl.glColor4f(color[0], color[1], color[2], color[3]) - for uv in poly: - x, y = context.region.view2d.view_to_region( - uv.x, uv.y) - bgl.glVertex2f(x, y) - bgl.glEnd() - elif sc.muv_uv_inspection_show_mode == 'FACE': - bgl.glBegin(bgl.GL_TRIANGLE_FAN) - bgl.glColor4f(color[0], color[1], color[2], color[3]) - for uv in info["uvs"]: - x, y = context.region.view2d.view_to_region(uv.x, uv.y) - bgl.glVertex2f(x, y) - bgl.glEnd() - - def invoke(self, context, _): - if not MUV_OT_UVInspection_Render.is_running(context): - impl.update_uvinsp_info(context) - MUV_OT_UVInspection_Render.handle_add(self, context) - else: - MUV_OT_UVInspection_Render.handle_remove() - - if context.area: - context.area.tag_redraw() - - return {'FINISHED'} - - -@BlClassRegistry(legacy=True) -class MUV_OT_UVInspection_Update(bpy.types.Operator): - """ - Operation class: Update - """ - - bl_idname = "uv.muv_uv_inspection_operator_update" - bl_label = "Update UV Inspection" - bl_description = "Update UV Inspection" - 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 - if not MUV_OT_UVInspection_Render.is_running(context): - return False - return impl.is_valid_context(context) - - def execute(self, context): - impl.update_uvinsp_info(context) - - if context.area: - context.area.tag_redraw() - - return {'FINISHED'} diff --git a/uv_magic_uv/legacy/op/uv_sculpt.py b/uv_magic_uv/legacy/op/uv_sculpt.py deleted file mode 100644 index 47a850d823a94c9b18c96d754543c45b67e26905..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/uv_sculpt.py +++ /dev/null @@ -1,450 +0,0 @@ -# <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 math import pi, cos, tan, sin - -import bpy -from bpy.props import ( - BoolProperty, - IntProperty, - EnumProperty, - FloatProperty, -) -import bmesh -import bgl -from mathutils import Vector -from bpy_extras import view3d_utils -from mathutils.bvhtree import BVHTree -from mathutils.geometry import barycentric_transform - -from ... import common -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import uv_sculpt_impl as impl - - -@PropertyClassRegistry(legacy=True) -class _Properties: - idname = "uv_sculpt" - - @classmethod - def init_props(cls, scene): - def get_func(_): - return MUV_OT_UVSculpt.is_running(bpy.context) - - def set_func(_, __): - pass - - def update_func(_, __): - bpy.ops.uv.muv_uv_sculpt_operator('INVOKE_REGION_WIN') - - scene.muv_uv_sculpt_enabled = BoolProperty( - name="UV Sculpt", - description="UV Sculpt is enabled", - default=False - ) - scene.muv_uv_sculpt_enable = BoolProperty( - name="UV Sculpt Showed", - description="UV Sculpt is enabled", - default=False, - get=get_func, - set=set_func, - update=update_func - ) - scene.muv_uv_sculpt_radius = IntProperty( - name="Radius", - description="Radius of the brush", - min=1, - max=500, - default=30 - ) - scene.muv_uv_sculpt_strength = FloatProperty( - name="Strength", - description="How powerful the effect of the brush when applied", - min=0.0, - max=1.0, - default=0.03, - ) - scene.muv_uv_sculpt_tools = EnumProperty( - name="Tools", - description="Select Tools for the UV sculpt brushes", - items=[ - ('GRAB', "Grab", "Grab UVs"), - ('RELAX', "Relax", "Relax UVs"), - ('PINCH', "Pinch", "Pinch UVs") - ], - default='GRAB' - ) - scene.muv_uv_sculpt_show_brush = BoolProperty( - name="Show Brush", - description="Show Brush", - default=True - ) - scene.muv_uv_sculpt_pinch_invert = BoolProperty( - name="Invert", - description="Pinch UV to invert direction", - default=False - ) - scene.muv_uv_sculpt_relax_method = EnumProperty( - name="Method", - description="Algorithm used for relaxation", - items=[ - ('HC', "HC", "Use HC method for relaxation"), - ('LAPLACIAN', "Laplacian", - "Use laplacian method for relaxation") - ], - default='HC' - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_uv_sculpt_enabled - del scene.muv_uv_sculpt_enable - del scene.muv_uv_sculpt_radius - del scene.muv_uv_sculpt_strength - del scene.muv_uv_sculpt_tools - del scene.muv_uv_sculpt_show_brush - del scene.muv_uv_sculpt_pinch_invert - del scene.muv_uv_sculpt_relax_method - - -@BlClassRegistry(legacy=True) -class MUV_OT_UVSculpt(bpy.types.Operator): - """ - Operation class: UV Sculpt in View3D - """ - - bl_idname = "uv.muv_uv_sculpt_operator" - bl_label = "UV Sculpt" - bl_description = "UV Sculpt in View3D" - bl_options = {'REGISTER'} - - __handle = None - __timer = None - - @classmethod - def poll(cls, context): - # we can not get area/space/region from console - if common.is_console_mode(): - return False - return impl.is_valid_context(context) - - @classmethod - def is_running(cls, _): - return 1 if cls.__handle else 0 - - @classmethod - def handle_add(cls, obj, context): - if not cls.__handle: - sv = bpy.types.SpaceView3D - cls.__handle = sv.draw_handler_add(cls.draw_brush, (obj, context), - "WINDOW", "POST_PIXEL") - if not cls.__timer: - cls.__timer = context.window_manager.event_timer_add( - 0.1, window=context.window) - context.window_manager.modal_handler_add(obj) - - @classmethod - def handle_remove(cls, context): - if cls.__handle: - sv = bpy.types.SpaceView3D - sv.draw_handler_remove(cls.__handle, "WINDOW") - cls.__handle = None - if cls.__timer: - context.window_manager.event_timer_remove(cls.__timer) - cls.__timer = None - - @classmethod - def draw_brush(cls, obj, context): - sc = context.scene - prefs = context.user_preferences.addons["uv_magic_uv"].preferences - - num_segment = 180 - theta = 2 * pi / num_segment - fact_t = tan(theta) - fact_r = cos(theta) - color = prefs.uv_sculpt_brush_color - - bgl.glBegin(bgl.GL_LINE_STRIP) - bgl.glColor4f(color[0], color[1], color[2], color[3]) - x = sc.muv_uv_sculpt_radius * cos(0.0) - y = sc.muv_uv_sculpt_radius * sin(0.0) - for _ in range(num_segment): - bgl.glVertex2f(x + obj.current_mco.x, y + obj.current_mco.y) - tx = -y - ty = x - x = x + tx * fact_t - y = y + ty * fact_t - x = x * fact_r - y = y * fact_r - bgl.glEnd() - - def __init__(self): - self.__loop_info = [] - self.__stroking = False - self.current_mco = Vector((0.0, 0.0)) - self.__initial_mco = Vector((0.0, 0.0)) - - def __stroke_init(self, context, _): - sc = context.scene - - self.__initial_mco = self.current_mco - - # get influenced UV - obj = context.active_object - world_mat = obj.matrix_world - bm = bmesh.from_edit_mesh(obj.data) - uv_layer = bm.loops.layers.uv.verify() - _, region, space = common.get_space_legacy('VIEW_3D', 'WINDOW', - 'VIEW_3D') - - self.__loop_info = [] - for f in bm.faces: - if not f.select: - continue - for i, l in enumerate(f.loops): - loc_2d = view3d_utils.location_3d_to_region_2d( - region, space.region_3d, world_mat * l.vert.co) - diff = loc_2d - self.__initial_mco - if diff.length < sc.muv_uv_sculpt_radius: - info = { - "face_idx": f.index, - "loop_idx": i, - "initial_vco": l.vert.co.copy(), - "initial_vco_2d": loc_2d, - "initial_uv": l[uv_layer].uv.copy(), - "strength": impl.get_strength( - diff.length, sc.muv_uv_sculpt_radius, - sc.muv_uv_sculpt_strength) - } - self.__loop_info.append(info) - - def __stroke_apply(self, context, _): - sc = context.scene - obj = context.active_object - world_mat = obj.matrix_world - bm = bmesh.from_edit_mesh(obj.data) - uv_layer = bm.loops.layers.uv.verify() - mco = self.current_mco - - if sc.muv_uv_sculpt_tools == 'GRAB': - for info in self.__loop_info: - diff_uv = (mco - self.__initial_mco) * info["strength"] - l = bm.faces[info["face_idx"]].loops[info["loop_idx"]] - l[uv_layer].uv = info["initial_uv"] + diff_uv / 100.0 - - elif sc.muv_uv_sculpt_tools == 'PINCH': - _, region, space = common.get_space_legacy('VIEW_3D', 'WINDOW', - 'VIEW_3D') - loop_info = [] - for f in bm.faces: - if not f.select: - continue - for i, l in enumerate(f.loops): - loc_2d = view3d_utils.location_3d_to_region_2d( - region, space.region_3d, world_mat * l.vert.co) - diff = loc_2d - self.__initial_mco - if diff.length < sc.muv_uv_sculpt_radius: - info = { - "face_idx": f.index, - "loop_idx": i, - "initial_vco": l.vert.co.copy(), - "initial_vco_2d": loc_2d, - "initial_uv": l[uv_layer].uv.copy(), - "strength": impl.get_strength( - diff.length, sc.muv_uv_sculpt_radius, - sc.muv_uv_sculpt_strength) - } - loop_info.append(info) - - # mouse coordinate to UV coordinate - ray_vec = view3d_utils.region_2d_to_vector_3d(region, - space.region_3d, mco) - ray_vec.normalize() - ray_orig = view3d_utils.region_2d_to_origin_3d(region, - space.region_3d, - mco) - ray_tgt = ray_orig + ray_vec * 1000000.0 - mwi = world_mat.inverted() - ray_orig_obj = mwi * ray_orig - ray_tgt_obj = mwi * ray_tgt - ray_dir_obj = ray_tgt_obj - ray_orig_obj - ray_dir_obj.normalize() - tree = BVHTree.FromBMesh(bm) - loc, _, fidx, _ = tree.ray_cast(ray_orig_obj, ray_dir_obj) - if not loc: - return - loops = [l for l in bm.faces[fidx].loops] - uvs = [Vector((l[uv_layer].uv.x, l[uv_layer].uv.y, 0.0)) - for l in loops] - target_uv = barycentric_transform( - loc, loops[0].vert.co, loops[1].vert.co, loops[2].vert.co, - uvs[0], uvs[1], uvs[2]) - target_uv = Vector((target_uv.x, target_uv.y)) - - # move to target UV coordinate - for info in loop_info: - l = bm.faces[info["face_idx"]].loops[info["loop_idx"]] - if sc.muv_uv_sculpt_pinch_invert: - diff_uv = (l[uv_layer].uv - target_uv) * info["strength"] - else: - diff_uv = (target_uv - l[uv_layer].uv) * info["strength"] - l[uv_layer].uv = l[uv_layer].uv + diff_uv / 10.0 - - elif sc.muv_uv_sculpt_tools == 'RELAX': - _, region, space = common.get_space_legacy('VIEW_3D', 'WINDOW', - 'VIEW_3D') - - # get vertex and loop relation - vert_db = {} - for f in bm.faces: - for l in f.loops: - if l.vert in vert_db: - vert_db[l.vert]["loops"].append(l) - else: - vert_db[l.vert] = {"loops": [l]} - - # get relaxation information - for k in vert_db.keys(): - d = vert_db[k] - d["uv_sum"] = Vector((0.0, 0.0)) - d["uv_count"] = 0 - - for l in d["loops"]: - ln = l.link_loop_next - lp = l.link_loop_prev - d["uv_sum"] = d["uv_sum"] + ln[uv_layer].uv - d["uv_sum"] = d["uv_sum"] + lp[uv_layer].uv - d["uv_count"] = d["uv_count"] + 2 - d["uv_p"] = d["uv_sum"] / d["uv_count"] - d["uv_b"] = d["uv_p"] - d["loops"][0][uv_layer].uv - for k in vert_db.keys(): - d = vert_db[k] - d["uv_sum_b"] = Vector((0.0, 0.0)) - for l in d["loops"]: - ln = l.link_loop_next - lp = l.link_loop_prev - dn = vert_db[ln.vert] - dp = vert_db[lp.vert] - d["uv_sum_b"] = d["uv_sum_b"] + dn["uv_b"] + dp["uv_b"] - - # apply - for f in bm.faces: - if not f.select: - continue - for i, l in enumerate(f.loops): - loc_2d = view3d_utils.location_3d_to_region_2d( - region, space.region_3d, world_mat * l.vert.co) - diff = loc_2d - self.__initial_mco - if diff.length >= sc.muv_uv_sculpt_radius: - continue - db = vert_db[l.vert] - strength = impl.get_strength(diff.length, - sc.muv_uv_sculpt_radius, - sc.muv_uv_sculpt_strength) - - base = (1.0 - strength) * l[uv_layer].uv - if sc.muv_uv_sculpt_relax_method == 'HC': - t = 0.5 * (db["uv_b"] + db["uv_sum_b"] / d["uv_count"]) - diff = strength * (db["uv_p"] - t) - target_uv = base + diff - elif sc.muv_uv_sculpt_relax_method == 'LAPLACIAN': - diff = strength * db["uv_p"] - target_uv = base + diff - else: - continue - - l[uv_layer].uv = target_uv - - bmesh.update_edit_mesh(obj.data) - - def __stroke_exit(self, context, _): - sc = context.scene - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - uv_layer = bm.loops.layers.uv.verify() - mco = self.current_mco - - if sc.muv_uv_sculpt_tools == 'GRAB': - for info in self.__loop_info: - diff_uv = (mco - self.__initial_mco) * info["strength"] - l = bm.faces[info["face_idx"]].loops[info["loop_idx"]] - l[uv_layer].uv = info["initial_uv"] + diff_uv / 100.0 - - bmesh.update_edit_mesh(obj.data) - - def modal(self, context, event): - if context.area: - context.area.tag_redraw() - - if not MUV_OT_UVSculpt.is_running(context): - MUV_OT_UVSculpt.handle_remove(context) - - return {'FINISHED'} - - self.current_mco = Vector((event.mouse_region_x, event.mouse_region_y)) - - region_types = [ - 'HEADER', - 'UI', - 'TOOLS', - 'TOOL_PROPS', - ] - if not common.mouse_on_area_legacy(event, 'VIEW_3D') or \ - common.mouse_on_regions_legacy(event, 'VIEW_3D', region_types): - return {'PASS_THROUGH'} - - if event.type == 'LEFTMOUSE': - if event.value == 'PRESS': - if not self.__stroking: - self.__stroke_init(context, event) - self.__stroking = True - elif event.value == 'RELEASE': - if self.__stroking: - self.__stroke_exit(context, event) - self.__stroking = False - return {'RUNNING_MODAL'} - elif event.type == 'MOUSEMOVE': - if self.__stroking: - self.__stroke_apply(context, event) - return {'RUNNING_MODAL'} - elif event.type == 'TIMER': - if self.__stroking: - self.__stroke_apply(context, event) - return {'RUNNING_MODAL'} - - return {'PASS_THROUGH'} - - def invoke(self, context, _): - if context.area: - context.area.tag_redraw() - - if MUV_OT_UVSculpt.is_running(context): - MUV_OT_UVSculpt.handle_remove(context) - else: - 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 deleted file mode 100644 index 56777b18aa18f1a451a0bcbafb42a31ead56012d..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/uvw.py +++ /dev/null @@ -1,181 +0,0 @@ -# <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/legacy/op/world_scale_uv.py b/uv_magic_uv/legacy/op/world_scale_uv.py deleted file mode 100644 index 4a6b28694528cdf95661b1e8d47f0e6763a7e0f4..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/op/world_scale_uv.py +++ /dev/null @@ -1,359 +0,0 @@ -# <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__ = "McBuff, Nutti <nutti.metro@gmail.com>" -__status__ = "production" -__version__ = "5.2" -__date__ = "17 Nov 2018" - -import bpy -from bpy.props import ( - EnumProperty, - FloatProperty, - IntVectorProperty, - BoolProperty, -) - -from ...utils.bl_class_registry import BlClassRegistry -from ...utils.property_class_registry import PropertyClassRegistry -from ...impl import world_scale_uv_impl as impl - - -@PropertyClassRegistry(legacy=True) -class Properties: - idname = "world_scale_uv" - - @classmethod - def init_props(cls, scene): - scene.muv_world_scale_uv_enabled = BoolProperty( - name="World Scale UV Enabled", - description="World Scale UV is enabled", - default=False - ) - scene.muv_world_scale_uv_src_mesh_area = FloatProperty( - name="Mesh Area", - description="Source Mesh Area", - default=0.0, - min=0.0 - ) - scene.muv_world_scale_uv_src_uv_area = FloatProperty( - name="UV Area", - description="Source UV Area", - default=0.0, - min=0.0 - ) - scene.muv_world_scale_uv_src_density = FloatProperty( - name="Density", - description="Source Texel Density", - default=0.0, - min=0.0 - ) - scene.muv_world_scale_uv_tgt_density = FloatProperty( - name="Density", - description="Target Texel Density", - default=0.0, - min=0.0 - ) - scene.muv_world_scale_uv_tgt_scaling_factor = FloatProperty( - name="Scaling Factor", - default=1.0, - max=1000.0, - min=0.00001 - ) - scene.muv_world_scale_uv_tgt_texture_size = IntVectorProperty( - name="Texture Size", - size=2, - min=1, - soft_max=10240, - default=(1024, 1024), - ) - scene.muv_world_scale_uv_mode = EnumProperty( - name="Mode", - description="Density calculation mode", - items=[ - ('PROPORTIONAL_TO_MESH', "Proportional to Mesh", - "Apply density proportionaled by mesh size"), - ('SCALING_DENSITY', "Scaling Density", - "Apply scaled density from source"), - ('SAME_DENSITY', "Same Density", - "Apply same density of source"), - ('MANUAL', "Manual", "Specify density and size by manual"), - ], - default='MANUAL' - ) - scene.muv_world_scale_uv_origin = EnumProperty( - name="Origin", - description="Aspect Origin", - items=[ - ('CENTER', "Center", "Center"), - ('LEFT_TOP', "Left Top", "Left Bottom"), - ('LEFT_CENTER', "Left Center", "Left Center"), - ('LEFT_BOTTOM', "Left Bottom", "Left Bottom"), - ('CENTER_TOP', "Center Top", "Center Top"), - ('CENTER_BOTTOM', "Center Bottom", "Center Bottom"), - ('RIGHT_TOP', "Right Top", "Right Top"), - ('RIGHT_CENTER', "Right Center", "Right Center"), - ('RIGHT_BOTTOM', "Right Bottom", "Right Bottom") - - ], - default='CENTER' - ) - - @classmethod - def del_props(cls, scene): - del scene.muv_world_scale_uv_enabled - del scene.muv_world_scale_uv_src_mesh_area - del scene.muv_world_scale_uv_src_uv_area - del scene.muv_world_scale_uv_src_density - del scene.muv_world_scale_uv_tgt_density - del scene.muv_world_scale_uv_tgt_scaling_factor - del scene.muv_world_scale_uv_mode - del scene.muv_world_scale_uv_origin - - -@BlClassRegistry(legacy=True) -class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator): - """ - Operation class: Measure face size - """ - - bl_idname = "uv.muv_world_scale_uv_operator_measure" - bl_label = "Measure World Scale UV" - bl_description = "Measure face size for scale calculation" - bl_options = {'REGISTER', 'UNDO'} - - def __init__(self): - self.__impl = impl.MeasureImpl() - - @classmethod - def poll(cls, context): - return impl.MeasureImpl.poll(context) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@BlClassRegistry(legacy=True) -class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): - """ - Operation class: Apply scaled UV (Manual) - """ - - bl_idname = "uv.muv_world_scale_uv_operator_apply_manual" - bl_label = "Apply World Scale UV (Manual)" - bl_description = "Apply scaled UV based on user specification" - bl_options = {'REGISTER', 'UNDO'} - - tgt_density = FloatProperty( - name="Density", - description="Target Texel Density", - default=1.0, - min=0.0 - ) - tgt_texture_size = IntVectorProperty( - name="Texture Size", - size=2, - min=1, - soft_max=10240, - default=(1024, 1024), - ) - origin = EnumProperty( - name="Origin", - description="Aspect Origin", - items=[ - ('CENTER', "Center", "Center"), - ('LEFT_TOP', "Left Top", "Left Bottom"), - ('LEFT_CENTER', "Left Center", "Left Center"), - ('LEFT_BOTTOM', "Left Bottom", "Left Bottom"), - ('CENTER_TOP', "Center Top", "Center Top"), - ('CENTER_BOTTOM', "Center Bottom", "Center Bottom"), - ('RIGHT_TOP', "Right Top", "Right Top"), - ('RIGHT_CENTER', "Right Center", "Right Center"), - ('RIGHT_BOTTOM', "Right Bottom", "Right Bottom") - - ], - default='CENTER' - ) - show_dialog = BoolProperty( - name="Show Diaglog Menu", - description="Show dialog menu if true", - default=True, - options={'HIDDEN', 'SKIP_SAVE'} - ) - - def __init__(self): - self.__impl = impl.ApplyManualImpl() - - @classmethod - def poll(cls, context): - return impl.ApplyManualImpl.poll(context) - - def draw(self, context): - self.__impl.draw(self, context) - - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@BlClassRegistry(legacy=True) -class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): - """ - Operation class: Apply scaled UV (Scaling Density) - """ - - bl_idname = "uv.muv_world_scale_uv_operator_apply_scaling_density" - bl_label = "Apply World Scale UV (Scaling Density)" - bl_description = "Apply scaled UV with scaling density" - bl_options = {'REGISTER', 'UNDO'} - - tgt_scaling_factor = FloatProperty( - name="Scaling Factor", - default=1.0, - max=1000.0, - min=0.00001 - ) - origin = EnumProperty( - name="Origin", - description="Aspect Origin", - items=[ - ('CENTER', "Center", "Center"), - ('LEFT_TOP', "Left Top", "Left Bottom"), - ('LEFT_CENTER', "Left Center", "Left Center"), - ('LEFT_BOTTOM', "Left Bottom", "Left Bottom"), - ('CENTER_TOP', "Center Top", "Center Top"), - ('CENTER_BOTTOM', "Center Bottom", "Center Bottom"), - ('RIGHT_TOP', "Right Top", "Right Top"), - ('RIGHT_CENTER', "Right Center", "Right Center"), - ('RIGHT_BOTTOM', "Right Bottom", "Right Bottom") - - ], - default='CENTER' - ) - src_density = FloatProperty( - name="Density", - description="Source Texel Density", - default=0.0, - min=0.0, - options={'HIDDEN'} - ) - same_density = BoolProperty( - name="Same Density", - description="Apply same density", - default=False, - options={'HIDDEN'} - ) - show_dialog = BoolProperty( - name="Show Diaglog Menu", - description="Show dialog menu if true", - default=True, - options={'HIDDEN', 'SKIP_SAVE'} - ) - - def __init__(self): - self.__impl = impl.ApplyScalingDensityImpl() - - @classmethod - def poll(cls, context): - return impl.ApplyScalingDensityImpl.poll(context) - - def draw(self, context): - self.__impl.draw(self, context) - - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) - - def execute(self, context): - return self.__impl.execute(self, context) - - -@BlClassRegistry(legacy=True) -class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): - """ - Operation class: Apply scaled UV (Proportional to mesh) - """ - - bl_idname = "uv.muv_world_scale_uv_operator_apply_proportional_to_mesh" - bl_label = "Apply World Scale UV (Proportional to mesh)" - bl_description = "Apply scaled UV proportionaled to mesh" - bl_options = {'REGISTER', 'UNDO'} - - origin = EnumProperty( - name="Origin", - description="Aspect Origin", - items=[ - ('CENTER', "Center", "Center"), - ('LEFT_TOP', "Left Top", "Left Bottom"), - ('LEFT_CENTER', "Left Center", "Left Center"), - ('LEFT_BOTTOM', "Left Bottom", "Left Bottom"), - ('CENTER_TOP', "Center Top", "Center Top"), - ('CENTER_BOTTOM', "Center Bottom", "Center Bottom"), - ('RIGHT_TOP', "Right Top", "Right Top"), - ('RIGHT_CENTER', "Right Center", "Right Center"), - ('RIGHT_BOTTOM', "Right Bottom", "Right Bottom") - - ], - default='CENTER' - ) - src_density = FloatProperty( - name="Source Density", - description="Source Texel Density", - default=0.0, - min=0.0, - options={'HIDDEN'} - ) - src_uv_area = FloatProperty( - name="Source UV Area", - description="Source UV Area", - default=0.0, - min=0.0, - options={'HIDDEN'} - ) - src_mesh_area = FloatProperty( - name="Source Mesh Area", - description="Source Mesh Area", - default=0.0, - min=0.0, - options={'HIDDEN'} - ) - show_dialog = BoolProperty( - name="Show Diaglog Menu", - description="Show dialog menu if true", - default=True, - options={'HIDDEN', 'SKIP_SAVE'} - ) - - def __init__(self): - self.__impl = impl.ApplyProportionalToMeshImpl() - - @classmethod - def poll(cls, context): - return impl.ApplyProportionalToMeshImpl.poll(context) - - def draw(self, context): - self.__impl.draw(self, context) - - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) - - def execute(self, context): - return self.__impl.execute(self, context) diff --git a/uv_magic_uv/legacy/preferences.py b/uv_magic_uv/legacy/preferences.py deleted file mode 100644 index e21f175394099191ee5d57e42d982236a4c5dc5f..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/preferences.py +++ /dev/null @@ -1,518 +0,0 @@ -# <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, - StringProperty, -) -from bpy.types import AddonPreferences - -from . import op -from . import ui -from ..utils.bl_class_registry import BlClassRegistry -from ..utils.addon_updator import AddonUpdatorManager - -__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 MUV_OT_CheckAddonUpdate(bpy.types.Operator): - bl_idname = "uv.muv_check_addon_update" - bl_label = "Check Update" - bl_description = "Check Add-on Update" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - updater = AddonUpdatorManager.get_instance() - updater.check_update_candidate() - - return {'FINISHED'} - - -@BlClassRegistry(legacy=True) -class MUV_OT_UpdateAddon(bpy.types.Operator): - bl_idname = "uv.muv_update_addon" - bl_label = "Update" - bl_description = "Update Add-on" - bl_options = {'REGISTER', 'UNDO'} - - branch_name = StringProperty( - name="Branch Name", - description="Branch name to update", - default="", - ) - - def execute(self, context): - updater = AddonUpdatorManager.get_instance() - updater.update(self.branch_name) - - return {'FINISHED'} - - -def get_update_candidate_branches(_, __): - updater = AddonUpdatorManager.get_instance() - if not updater.candidate_checked(): - return [] - - return [(name, name, "") for name in updater.get_candidate_branch_names()] - - -@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 - ) - - # for add-on updater - updater_branch_to_update = EnumProperty( - name="branch", - description="Target branch to update add-on", - items=get_update_candidate_branches - ) - - 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 deleted file mode 100644 index b64a48f39010fdfa5357146a0bcbbaac08dea18e..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/properites.py +++ /dev/null @@ -1,61 +0,0 @@ -# <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 deleted file mode 100644 index bf071bf5f07ea56d291e8fa1f6bc3640ea8d11dc..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/IMAGE_MT_uvs.py +++ /dev/null @@ -1,197 +0,0 @@ -# <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 deleted file mode 100644 index e1c751ae798dfc1793bae3dfc08b362dd62e2a17..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/VIEW3D_MT_object.py +++ /dev/null @@ -1,54 +0,0 @@ -# <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 deleted file mode 100644 index d229322a034aa382920c3b0ab782bb1d1f891d20..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/VIEW3D_MT_uv_map.py +++ /dev/null @@ -1,257 +0,0 @@ -# <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 deleted file mode 100644 index bf790a8e4203c5d0e6593d695283e020c3368d49..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/__init__.py +++ /dev/null @@ -1,50 +0,0 @@ -# <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 deleted file mode 100644 index 9848f03b78951ca083828a90d32912459a8ecaeb..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/uvedit_copy_paste_uv.py +++ /dev/null @@ -1,62 +0,0 @@ -# <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/legacy/ui/uvedit_editor_enhancement.py b/uv_magic_uv/legacy/ui/uvedit_editor_enhancement.py deleted file mode 100644 index 3f750febfbefbc51fd5f062b8916ad4572ea0b72..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/uvedit_editor_enhancement.py +++ /dev/null @@ -1,149 +0,0 @@ -# <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, - uv_bounding_box, - uv_inspection, -) -from ...utils.bl_class_registry import BlClassRegistry - -__all__ = [ - 'MUV_PT_UVEdit_EditorEnhancement', -] - - -@BlClassRegistry(legacy=True) -class MUV_PT_UVEdit_EditorEnhancement(bpy.types.Panel): - """ - Panel class: UV/Image Editor Enhancement - """ - - bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'TOOLS' - bl_label = "Editor Enhancement" - 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): - layout = self.layout - sc = context.scene - - box = layout.box() - box.prop(sc, "muv_align_uv_cursor_enabled", text="Align UV Cursor") - if sc.muv_align_uv_cursor_enabled: - box.prop(sc, "muv_align_uv_cursor_align_method", expand=True) - - col = box.column(align=True) - - row = col.row(align=True) - 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.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.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.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.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.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.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.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.MUV_OT_AlignUVCursor.bl_idname, - text="Right Bottom") - ops.position = 'RIGHT_BOTTOM' - ops.base = sc.muv_align_uv_cursor_align_method - - box = layout.box() - box.prop(sc, "muv_uv_cursor_location_enabled", - text="UV Cursor Location") - if sc.muv_uv_cursor_location_enabled: - box.prop(sc, "muv_align_uv_cursor_cursor_loc", text="") - - 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.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") - - box = layout.box() - 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.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") - row.prop(sc, "muv_uv_inspection_show_flipped") - row = box.row() - row.prop(sc, "muv_uv_inspection_show_mode") diff --git a/uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py b/uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py deleted file mode 100644 index 96cf17d324c17eeba2b45cf478559702a237ff90..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py +++ /dev/null @@ -1,130 +0,0 @@ -# <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, - smooth_uv, - pack_uv, - select_uv, -) -from ...utils.bl_class_registry import BlClassRegistry - -__all__ = [ - 'MUV_PT_UVEdit_UVManipulation', -] - - -@BlClassRegistry(legacy=True) -class MUV_PT_UVEdit_UVManipulation(bpy.types.Panel): - """ - Panel class: UV Manipulation on Property Panel on UV/ImageEditor - """ - - bl_space_type = 'IMAGE_EDITOR' - 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_align_uv_enabled", text="Align UV") - if sc.muv_align_uv_enabled: - col = box.column() - row = col.row(align=True) - 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.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.mesh_infl = sc.muv_align_uv_mesh_infl - row = col.row() - 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 - ops.horizontal = sc.muv_align_uv_horizontal - ops.location = sc.muv_align_uv_location - ops.mesh_infl = sc.muv_align_uv_mesh_infl - row.prop(sc, "muv_align_uv_location", text="") - - col = box.column(align=True) - row = col.row(align=True) - row.prop(sc, "muv_align_uv_transmission", text="Transmission") - row.prop(sc, "muv_align_uv_select", text="Select") - row = col.row(align=True) - row.prop(sc, "muv_align_uv_vertical", text="Vertical") - row.prop(sc, "muv_align_uv_horizontal", text="Horizontal") - col.prop(sc, "muv_align_uv_mesh_infl", text="Mesh Influence") - - box = layout.box() - box.prop(sc, "muv_smooth_uv_enabled", text="Smooth UV") - if sc.muv_smooth_uv_enabled: - 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 - ops.mesh_infl = sc.muv_smooth_uv_mesh_infl - col = box.column(align=True) - row = col.row(align=True) - row.prop(sc, "muv_smooth_uv_transmission", text="Transmission") - row.prop(sc, "muv_smooth_uv_select", text="Select") - col.prop(sc, "muv_smooth_uv_mesh_infl", text="Mesh Influence") - - box = layout.box() - 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.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.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 - box.label("Allowable Center Deviation:") - box.prop(sc, "muv_pack_uv_allowable_center_deviation", text="") - box.label("Allowable Size Deviation:") - box.prop(sc, "muv_pack_uv_allowable_size_deviation", text="") 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 deleted file mode 100644 index ee2713b22d19cfd193dd1ddd430e679c41aa0c1a..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_editmode.py +++ /dev/null @@ -1,93 +0,0 @@ -# <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 deleted file mode 100644 index 58031b0f3158a2f78689741713bd55ac3d899bb7..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_objectmode.py +++ /dev/null @@ -1,65 +0,0 @@ -# <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 deleted file mode 100644 index 7d828a38fb1bf5aef2356f0a955badb4b915c17e..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/view3d_uv_manipulation.py +++ /dev/null @@ -1,289 +0,0 @@ -# <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 deleted file mode 100644 index 3de86d0de00a0f698dd86a002e39cca95ba8c2a0..0000000000000000000000000000000000000000 --- a/uv_magic_uv/legacy/ui/view3d_uv_mapping.py +++ /dev/null @@ -1,116 +0,0 @@ -# <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/lib/bglx.py b/uv_magic_uv/lib/bglx.py index c4dadd697a7d9538b35d3af187faee467167cbab..72e030fa9aa11acba90ec29edfd540f4b503c5f7 100644 --- a/uv_magic_uv/lib/bglx.py +++ b/uv_magic_uv/lib/bglx.py @@ -1,10 +1,13 @@ from threading import Lock +import bgl +from bgl import Buffer as Buffer import gpu from gpu_extras.batch import batch_for_shader GL_LINES = 0 GL_LINE_STRIP = 1 +GL_LINE_LOOP = 2 GL_TRIANGLES = 5 GL_TRIANGLE_FAN = 6 GL_QUADS = 4 @@ -18,7 +21,11 @@ class InternalData: @classmethod def __internal_new(cls): - return super().__new__(cls) + inst = super().__new__(cls) + inst.color = [1.0, 1.0, 1.0, 1.0] + inst.line_width = 1.0 + + return inst @classmethod def get_instance(cls): @@ -47,6 +54,9 @@ class InternalData: def set_color(self, c): self.color = c + def set_line_width(self, width): + self.line_width = width + def clear(self): self.prim_mode = None self.verts = [] @@ -65,14 +75,21 @@ class InternalData: def get_color(self): return self.color + def get_line_width(self): + return self.line_width + def get_tex_coords(self): return self.tex_coords -def glBegin(mode): +def glLineWidth(width): inst = InternalData.get_instance() - inst.init() - inst.set_prim_mode(mode) + inst.set_line_width(width) + + +def glColor3f(r, g, b): + inst = InternalData.get_instance() + inst.set_color([r, g, b, 1.0]) def glColor4f(r, g, b, a): @@ -80,6 +97,21 @@ def glColor4f(r, g, b, a): inst.set_color([r, g, b, a]) +def glRecti(x0, y0, x1, y1): + glBegin(GL_QUADS) + glVertex2f(x0, y0) + glVertex2f(x0, y1) + glVertex2f(x1, y1) + glVertex2f(x1, y0) + glEnd() + + +def glBegin(mode): + inst = InternalData.get_instance() + inst.init() + inst.set_prim_mode(mode) + + def _get_transparency_shader(): vertex_shader = ''' uniform mat4 modelViewMatrix; @@ -149,6 +181,11 @@ def glEnd(): elif inst.get_prim_mode() == GL_LINE_STRIP: batch = batch_for_shader(shader, 'LINE_STRIP', data) + + elif inst.get_prim_mode() == GL_LINE_LOOP: + data["pos"].append(data["pos"][0]) + batch = batch_for_shader(shader, 'LINE_STRIP', data) + elif inst.get_prim_mode() == GL_TRIANGLES: indices = [] for i in range(0, len(coords), 3): @@ -189,3 +226,35 @@ def glVertex2f(x, y): def glTexCoord2f(u, v): inst = InternalData.get_instance() inst.add_tex_coord([u, v]) + + +GL_BLEND = bgl.GL_BLEND +GL_LINE_SMOOTH = bgl.GL_LINE_SMOOTH +GL_INT = bgl.GL_INT +GL_SCISSOR_BOX = bgl.GL_SCISSOR_BOX +GL_TEXTURE_2D = bgl.GL_TEXTURE_2D +GL_TEXTURE0 = bgl.GL_TEXTURE0 + + +def glEnable(cap): + bgl.glEnable(cap) + + +def glDisable(cap): + bgl.glDisable(cap) + + +def glScissor(x, y, width, height): + bgl.glScissor(x, y, width, height) + + +def glGetIntegerv(pname, params): + bgl.glGetIntegerv(pname, params) + + +def glActiveTexture(texture): + bgl.glActiveTexture(texture) + + +def glBindTexture(target, texture): + bgl.glBindTexture(target, texture) diff --git a/uv_magic_uv/op/align_uv.py b/uv_magic_uv/op/align_uv.py index fbd119d298fc2ef9df63a37ac771830e8e83ea2a..8225858e4e5029eb09be2ff8b2ee17d1523d5588 100644 --- a/uv_magic_uv/op/align_uv.py +++ b/uv_magic_uv/op/align_uv.py @@ -23,12 +23,271 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" +import math +from math import atan2, tan, sin, cos + import bpy from bpy.props import EnumProperty, BoolProperty, FloatProperty +import bmesh +from mathutils import Vector from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import align_uv_impl as impl +from ..utils import compatibility as compat + +from .. import common + + +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 + + +# 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 + + +# get accumulate vertex lengths of loop sequences +def _get_loop_vert_accum_len(loops): + accum_lengths = [0.0] + length = 0 + for l1, l2 in zip(loops[:-1], loops[1:]): + diff = l2.vert.co - l1.vert.co + length = length + abs(diff.length) + accum_lengths.extend([length]) + + return accum_lengths + + +# get sum uv length of loop sequences +def _get_loop_uv_accum_len(loops, uv_layer): + accum_lengths = [0.0] + 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) + accum_lengths.extend([length]) + + return accum_lengths + + +# get horizontal differential of UV influenced by mesh vertex +def _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pidx, infl): + common.debug_print( + "loop_seqs[hidx={0}][vidx={1}][pidx={2}]".format(hidx, vidx, pidx)) + + base_uv = loop_seqs[0][vidx][0][uv_layer].uv.copy() + + # calculate original length + hloops = [] + for s in loop_seqs: + hloops.extend([s[vidx][0], s[vidx][1]]) + total_vlen = _get_loop_vert_len(hloops) + accum_vlens = _get_loop_vert_accum_len(hloops) + total_uvlen = _get_loop_uv_len(hloops, uv_layer) + accum_uvlens = _get_loop_uv_accum_len(hloops, uv_layer) + orig_uvs = [l[uv_layer].uv.copy() for l in hloops] + + # calculate target length + tgt_noinfl = total_uvlen * (hidx + pidx) / len(loop_seqs) + tgt_infl = total_uvlen * accum_vlens[hidx * 2 + pidx] / total_vlen + target_length = tgt_noinfl * (1 - infl) + tgt_infl * infl + common.debug_print(target_length) + common.debug_print(accum_uvlens) + + # calculate target UV + for i in range(len(accum_uvlens[:-1])): + # get line segment which UV will be placed + if ((accum_uvlens[i] <= target_length) and + (accum_uvlens[i + 1] > target_length)): + tgt_seg_len = target_length - accum_uvlens[i] + seg_len = accum_uvlens[i + 1] - accum_uvlens[i] + uv1 = orig_uvs[i] + uv2 = orig_uvs[i + 1] + target_uv = (uv1 - base_uv) + (uv2 - uv1) * tgt_seg_len / seg_len + break + elif i == (len(accum_uvlens[:-1]) - 1): + if abs(accum_uvlens[i + 1] - target_length) > 0.000001: + raise Exception( + "Internal Error: horizontal_target_length={}" + " is not equal to {}" + .format(target_length, accum_uvlens[-1])) + tgt_seg_len = target_length - accum_uvlens[i] + seg_len = accum_uvlens[i + 1] - accum_uvlens[i] + uv1 = orig_uvs[i] + uv2 = orig_uvs[i + 1] + target_uv = (uv1 - base_uv) + (uv2 - uv1) * tgt_seg_len / seg_len + break + else: + raise Exception("Internal Error: horizontal_target_length={}" + " is not in range {} to {}" + .format(target_length, accum_uvlens[0], + accum_uvlens[-1])) + + return target_uv + + +# --------------------- LOOP STRUCTURE ---------------------- +# +# loops[hidx][vidx][pidx] +# hidx: horizontal index +# vidx: vertical index +# pidx: pair index +# +# <----- horizontal -----> +# +# (hidx, vidx, pidx) = (0, 3, 0) +# | (hidx, vidx, pidx) = (1, 3, 0) +# v v +# ^ o --- oo --- o +# | | || | +# vertical | o --- oo --- o <- (hidx, vidx, pidx) +# | o --- oo --- o = (1, 2, 1) +# | | || | +# v o --- oo --- o +# ^ ^ +# | (hidx, vidx, pidx) = (1, 0, 1) +# (hidx, vidx, pidx) = (0, 0, 0) +# +# ----------------------------------------------------------- + + +# get vertical differential of UV influenced by mesh vertex +def _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pidx, infl): + common.debug_print( + "loop_seqs[hidx={0}][vidx={1}][pidx={2}]".format(hidx, vidx, pidx)) + + base_uv = loop_seqs[hidx][0][pidx][uv_layer].uv.copy() + + # calculate original length + vloops = [] + for s in loop_seqs[hidx]: + vloops.append(s[pidx]) + total_vlen = _get_loop_vert_len(vloops) + accum_vlens = _get_loop_vert_accum_len(vloops) + total_uvlen = _get_loop_uv_len(vloops, uv_layer) + accum_uvlens = _get_loop_uv_accum_len(vloops, uv_layer) + orig_uvs = [l[uv_layer].uv.copy() for l in vloops] + + # calculate target length + tgt_noinfl = total_uvlen * int((vidx + 1) / 2) * 2 / len(loop_seqs[hidx]) + tgt_infl = total_uvlen * accum_vlens[vidx] / total_vlen + target_length = tgt_noinfl * (1 - infl) + tgt_infl * infl + common.debug_print(target_length) + common.debug_print(accum_uvlens) + + # calculate target UV + for i in range(len(accum_uvlens[:-1])): + # get line segment which UV will be placed + if ((accum_uvlens[i] <= target_length) and + (accum_uvlens[i + 1] > target_length)): + tgt_seg_len = target_length - accum_uvlens[i] + seg_len = accum_uvlens[i + 1] - accum_uvlens[i] + uv1 = orig_uvs[i] + uv2 = orig_uvs[i + 1] + target_uv = (uv1 - base_uv) + (uv2 - uv1) * tgt_seg_len / seg_len + break + elif i == (len(accum_uvlens[:-1]) - 1): + if abs(accum_uvlens[i + 1] - target_length) > 0.000001: + raise Exception("Internal Error: horizontal_target_length={}" + " is not equal to {}" + .format(target_length, accum_uvlens[-1])) + tgt_seg_len = target_length - accum_uvlens[i] + seg_len = accum_uvlens[i + 1] - accum_uvlens[i] + uv1 = orig_uvs[i] + uv2 = orig_uvs[i + 1] + target_uv = (uv1 - base_uv) + (uv2 - uv1) * tgt_seg_len / seg_len + break + else: + raise Exception("Internal Error: horizontal_target_length={}" + " is not in range {} to {}" + .format(target_length, accum_uvlens[0], + accum_uvlens[-1])) + + return target_uv + + +# get horizontal differential of UV no influenced +def _get_hdiff_uv(uv_layer, loop_seqs, hidx): + base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() + h_uv = loop_seqs[-1][0][1][uv_layer].uv.copy() - base_uv + + return hidx * h_uv / len(loop_seqs) + + +# get vertical differential of UV no influenced +def _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx): + base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() + v_uv = loop_seqs[0][-1][0][uv_layer].uv.copy() - base_uv + + hseq = loop_seqs[hidx] + return int((vidx + 1) / 2) * v_uv / (len(hseq) / 2) @PropertyClassRegistry() @@ -94,6 +353,7 @@ class _Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_AlignUV_Circle(bpy.types.Operator): bl_idname = "uv.muv_align_uv_operator_circle" @@ -101,29 +361,85 @@ class MUV_OT_AlignUV_Circle(bpy.types.Operator): bl_description = "Align UV coordinates to Circle" bl_options = {'REGISTER', 'UNDO'} - transmission: BoolProperty( + transmission = BoolProperty( name="Transmission", description="Align linked UVs", default=False ) - select: BoolProperty( + select = BoolProperty( name="Select", description="Select UVs which are aligned", default=False ) - def __init__(self): - self.__impl = impl.CircleImpl() - @classmethod def poll(cls, context): - return impl.CircleImpl.poll(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): - return self.__impl.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() + uv_layer = bm.loops.layers.uv.verify() + + # loop_seqs[horizontal][vertical][loop] + loop_seqs, error = common.get_loop_sequences(bm, uv_layer, True) + if not loop_seqs: + self.report({'WARNING'}, error) + return {'CANCELLED'} + + # get circle and new UVs + uvs = [hseq[0][0][uv_layer].uv.copy() for hseq in loop_seqs] + c, r = _get_circle(uvs[0:3]) + new_uvs = _calc_v_on_circle(uvs, c, r) + + # check center UV of circle + center = loop_seqs[0][-1][0].vert + for hseq in loop_seqs[1:]: + if len(hseq[-1]) != 1: + self.report({'WARNING'}, "Last face must be triangle") + return {'CANCELLED'} + if hseq[-1][0].vert != center: + self.report({'WARNING'}, "Center must be identical") + return {'CANCELLED'} + + # align to circle + if self.transmission: + for hidx, hseq in enumerate(loop_seqs): + for vidx, pair in enumerate(hseq): + all_ = int((len(hseq) + 1) / 2) + r = (all_ - int((vidx + 1) / 2)) / all_ + pair[0][uv_layer].uv = c + (new_uvs[hidx] - c) * r + if self.select: + pair[0][uv_layer].select = True + + if len(pair) < 2: + continue + # for quad polygon + next_hidx = (hidx + 1) % len(loop_seqs) + pair[1][uv_layer].uv = c + ((new_uvs[next_hidx]) - c) * r + if self.select: + pair[1][uv_layer].select = True + else: + for hidx, hseq in enumerate(loop_seqs): + pair = hseq[0] + pair[0][uv_layer].uv = new_uvs[hidx] + pair[1][uv_layer].uv = new_uvs[(hidx + 1) % len(loop_seqs)] + if self.select: + pair[0][uv_layer].select = True + pair[1][uv_layer].select = True + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} @BlClassRegistry() +@compat.make_annotations class MUV_OT_AlignUV_Straighten(bpy.types.Operator): bl_idname = "uv.muv_align_uv_operator_straighten" @@ -131,29 +447,29 @@ class MUV_OT_AlignUV_Straighten(bpy.types.Operator): bl_description = "Straighten UV coordinates" bl_options = {'REGISTER', 'UNDO'} - transmission: BoolProperty( + transmission = BoolProperty( name="Transmission", description="Align linked UVs", default=False ) - select: BoolProperty( + select = BoolProperty( name="Select", description="Select UVs which are aligned", default=False ) - vertical: BoolProperty( + vertical = BoolProperty( name="Vert-Infl (Vertical)", description="Align vertical direction influenced " "by mesh vertex proportion", default=False ) - horizontal: BoolProperty( + horizontal = BoolProperty( name="Vert-Infl (Horizontal)", description="Align horizontal direction influenced " "by mesh vertex proportion", default=False ) - mesh_infl: FloatProperty( + mesh_infl = FloatProperty( name="Mesh Influence", description="Influence rate of mesh vertex", min=0.0, @@ -161,18 +477,121 @@ class MUV_OT_AlignUV_Straighten(bpy.types.Operator): default=0.0 ) - def __init__(self): - self.__impl = impl.StraightenImpl() - @classmethod def poll(cls, context): - return impl.StraightenImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + # selected and paralleled UV loop sequence will be aligned + def __align_w_transmission(self, loop_seqs, uv_layer): + base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() + + # calculate diff UVs + diff_uvs = [] + # hseq[vertical][loop] + for hidx, hseq in enumerate(loop_seqs): + # pair[loop] + diffs = [] + for vidx in range(0, len(hseq), 2): + if self.horizontal: + hdiff_uvs = [ + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, + self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, + self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 0, self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 1, self.mesh_infl), + ] + else: + hdiff_uvs = [ + _get_hdiff_uv(uv_layer, loop_seqs, hidx), + _get_hdiff_uv(uv_layer, loop_seqs, hidx + 1), + _get_hdiff_uv(uv_layer, loop_seqs, hidx), + _get_hdiff_uv(uv_layer, loop_seqs, hidx + 1) + ] + if self.vertical: + vdiff_uvs = [ + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, + self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, + self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 0, self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 1, self.mesh_infl), + ] + else: + vdiff_uvs = [ + _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx) + ] + diffs.append([hdiff_uvs, vdiff_uvs]) + diff_uvs.append(diffs) + + # update UV + for hseq, diffs in zip(loop_seqs, diff_uvs): + for vidx in range(0, len(hseq), 2): + loops = [ + hseq[vidx][0], hseq[vidx][1], + hseq[vidx + 1][0], hseq[vidx + 1][1] + ] + for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0], + diffs[int(vidx / 2)][1]): + l[uv_layer].uv = base_uv + hdiff + vdiff + if self.select: + l[uv_layer].select = True + + # only selected UV loop sequence will be aligned + def __align_wo_transmission(self, loop_seqs, uv_layer): + base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() + + h_uv = loop_seqs[-1][0][1][uv_layer].uv.copy() - base_uv + for hidx, hseq in enumerate(loop_seqs): + # only selected loop pair is targeted + pair = hseq[0] + hdiff_uv_0 = hidx * h_uv / len(loop_seqs) + hdiff_uv_1 = (hidx + 1) * h_uv / len(loop_seqs) + pair[0][uv_layer].uv = base_uv + hdiff_uv_0 + pair[1][uv_layer].uv = base_uv + hdiff_uv_1 + if self.select: + pair[0][uv_layer].select = True + pair[1][uv_layer].select = True + + def __align(self, loop_seqs, uv_layer): + if self.transmission: + self.__align_w_transmission(loop_seqs, uv_layer) + else: + self.__align_wo_transmission(loop_seqs, uv_layer) def execute(self, context): - return self.__impl.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() + uv_layer = bm.loops.layers.uv.verify() + + # loop_seqs[horizontal][vertical][loop] + loop_seqs, error = common.get_loop_sequences(bm, uv_layer) + if not loop_seqs: + self.report({'WARNING'}, error) + return {'CANCELLED'} + + # align + self.__align(loop_seqs, uv_layer) + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} @BlClassRegistry() +@compat.make_annotations class MUV_OT_AlignUV_Axis(bpy.types.Operator): bl_idname = "uv.muv_align_uv_operator_axis" @@ -180,29 +599,29 @@ class MUV_OT_AlignUV_Axis(bpy.types.Operator): bl_description = "Align UV to XY-axis" bl_options = {'REGISTER', 'UNDO'} - transmission: BoolProperty( + transmission = BoolProperty( name="Transmission", description="Align linked UVs", default=False ) - select: BoolProperty( + select = BoolProperty( name="Select", description="Select UVs which are aligned", default=False ) - vertical: BoolProperty( + vertical = BoolProperty( name="Vert-Infl (Vertical)", description="Align vertical direction influenced " "by mesh vertex proportion", default=False ) - horizontal: BoolProperty( + horizontal = BoolProperty( name="Vert-Infl (Horizontal)", description="Align horizontal direction influenced " "by mesh vertex proportion", default=False ) - location: EnumProperty( + location = EnumProperty( name="Location", description="Align location", items=[ @@ -212,7 +631,7 @@ class MUV_OT_AlignUV_Axis(bpy.types.Operator): ], default='MIDDLE' ) - mesh_infl: FloatProperty( + mesh_infl = FloatProperty( name="Mesh Influence", description="Influence rate of mesh vertex", min=0.0, @@ -220,12 +639,353 @@ class MUV_OT_AlignUV_Axis(bpy.types.Operator): default=0.0 ) - def __init__(self): - self.__impl = impl.AxisImpl() - @classmethod def poll(cls, context): - return impl.AxisImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + # get min/max of UV + def __get_uv_max_min(self, loop_seqs, uv_layer): + uv_max = Vector((-1000000.0, -1000000.0)) + uv_min = Vector((1000000.0, 1000000.0)) + for hseq in loop_seqs: + for l in hseq[0]: + uv = l[uv_layer].uv + uv_max.x = max(uv.x, uv_max.x) + uv_max.y = max(uv.y, uv_max.y) + uv_min.x = min(uv.x, uv_min.x) + uv_min.y = min(uv.y, uv_min.y) + + return uv_max, uv_min + + # get UV differentiation when UVs are aligned to X-axis + def __get_x_axis_align_diff_uvs(self, loop_seqs, uv_layer, uv_min, + width, height): + diff_uvs = [] + for hidx, hseq in enumerate(loop_seqs): + pair = hseq[0] + luv0 = pair[0][uv_layer] + luv1 = pair[1][uv_layer] + target_uv0 = Vector((0.0, 0.0)) + target_uv1 = Vector((0.0, 0.0)) + if self.location == 'RIGHT_BOTTOM': + target_uv0.y = target_uv1.y = uv_min.y + elif self.location == 'MIDDLE': + target_uv0.y = target_uv1.y = uv_min.y + height * 0.5 + elif self.location == 'LEFT_TOP': + target_uv0.y = target_uv1.y = uv_min.y + height + if luv0.uv.x < luv1.uv.x: + target_uv0.x = uv_min.x + hidx * width / len(loop_seqs) + target_uv1.x = uv_min.x + (hidx + 1) * width / len(loop_seqs) + else: + target_uv0.x = uv_min.x + (hidx + 1) * width / len(loop_seqs) + target_uv1.x = uv_min.x + hidx * width / len(loop_seqs) + diff_uvs.append([target_uv0 - luv0.uv, target_uv1 - luv1.uv]) + + return diff_uvs + + # get UV differentiation when UVs are aligned to Y-axis + def __get_y_axis_align_diff_uvs(self, loop_seqs, uv_layer, uv_min, + width, height): + diff_uvs = [] + for hidx, hseq in enumerate(loop_seqs): + pair = hseq[0] + luv0 = pair[0][uv_layer] + luv1 = pair[1][uv_layer] + target_uv0 = Vector((0.0, 0.0)) + target_uv1 = Vector((0.0, 0.0)) + if self.location == 'RIGHT_BOTTOM': + target_uv0.x = target_uv1.x = uv_min.x + width + elif self.location == 'MIDDLE': + target_uv0.x = target_uv1.x = uv_min.x + width * 0.5 + elif self.location == 'LEFT_TOP': + target_uv0.x = target_uv1.x = uv_min.x + if luv0.uv.y < luv1.uv.y: + target_uv0.y = uv_min.y + hidx * height / len(loop_seqs) + target_uv1.y = uv_min.y + (hidx + 1) * height / len(loop_seqs) + else: + target_uv0.y = uv_min.y + (hidx + 1) * height / len(loop_seqs) + target_uv1.y = uv_min.y + hidx * height / len(loop_seqs) + diff_uvs.append([target_uv0 - luv0.uv, target_uv1 - luv1.uv]) + + return diff_uvs + + # only selected UV loop sequence will be aligned along to X-axis + def __align_to_x_axis_wo_transmission(self, loop_seqs, uv_layer, + uv_min, width, height): + # reverse if the UV coordinate is not sorted by position + need_revese = loop_seqs[0][0][0][uv_layer].uv.x > \ + loop_seqs[-1][0][0][uv_layer].uv.x + if need_revese: + loop_seqs.reverse() + for hidx, hseq in enumerate(loop_seqs): + for vidx, pair in enumerate(hseq): + tmp = loop_seqs[hidx][vidx][0] + loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1] + loop_seqs[hidx][vidx][1] = tmp + + # get UV differential + diff_uvs = self.__get_x_axis_align_diff_uvs(loop_seqs, + uv_layer, uv_min, + width, height) + + # update UV + for hseq, duv in zip(loop_seqs, diff_uvs): + pair = hseq[0] + luv0 = pair[0][uv_layer] + luv1 = pair[1][uv_layer] + luv0.uv = luv0.uv + duv[0] + luv1.uv = luv1.uv + duv[1] + + # only selected UV loop sequence will be aligned along to Y-axis + def __align_to_y_axis_wo_transmission(self, loop_seqs, uv_layer, + uv_min, width, height): + # reverse if the UV coordinate is not sorted by position + need_revese = loop_seqs[0][0][0][uv_layer].uv.y > \ + loop_seqs[-1][0][0][uv_layer].uv.y + if need_revese: + loop_seqs.reverse() + for hidx, hseq in enumerate(loop_seqs): + for vidx, pair in enumerate(hseq): + tmp = loop_seqs[hidx][vidx][0] + loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1] + loop_seqs[hidx][vidx][1] = tmp + + # get UV differential + diff_uvs = self.__get_y_axis_align_diff_uvs(loop_seqs, + uv_layer, uv_min, + width, height) + + # update UV + for hseq, duv in zip(loop_seqs, diff_uvs): + pair = hseq[0] + luv0 = pair[0][uv_layer] + luv1 = pair[1][uv_layer] + luv0.uv = luv0.uv + duv[0] + luv1.uv = luv1.uv + duv[1] + + # selected and paralleled UV loop sequence will be aligned along to X-axis + def __align_to_x_axis_w_transmission(self, loop_seqs, uv_layer, + uv_min, width, height): + # reverse if the UV coordinate is not sorted by position + need_revese = loop_seqs[0][0][0][uv_layer].uv.x > \ + loop_seqs[-1][0][0][uv_layer].uv.x + if need_revese: + loop_seqs.reverse() + for hidx, hseq in enumerate(loop_seqs): + for vidx in range(len(hseq)): + tmp = loop_seqs[hidx][vidx][0] + loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1] + loop_seqs[hidx][vidx][1] = tmp + + # get offset UVs when the UVs are aligned to X-axis + align_diff_uvs = self.__get_x_axis_align_diff_uvs(loop_seqs, + uv_layer, uv_min, + width, height) + base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() + offset_uvs = [] + for hseq, aduv in zip(loop_seqs, align_diff_uvs): + luv0 = hseq[0][0][uv_layer] + luv1 = hseq[0][1][uv_layer] + offset_uvs.append([luv0.uv + aduv[0] - base_uv, + luv1.uv + aduv[1] - base_uv]) + + # get UV differential + diff_uvs = [] + # hseq[vertical][loop] + for hidx, hseq in enumerate(loop_seqs): + # pair[loop] + diffs = [] + for vidx in range(0, len(hseq), 2): + if self.horizontal: + hdiff_uvs = [ + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, + self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, + self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 0, self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 1, self.mesh_infl), + ] + hdiff_uvs[0].y = hdiff_uvs[0].y + offset_uvs[hidx][0].y + hdiff_uvs[1].y = hdiff_uvs[1].y + offset_uvs[hidx][1].y + hdiff_uvs[2].y = hdiff_uvs[2].y + offset_uvs[hidx][0].y + hdiff_uvs[3].y = hdiff_uvs[3].y + offset_uvs[hidx][1].y + else: + hdiff_uvs = [ + offset_uvs[hidx][0], + offset_uvs[hidx][1], + offset_uvs[hidx][0], + offset_uvs[hidx][1], + ] + if self.vertical: + vdiff_uvs = [ + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, + self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, + self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 0, self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 1, self.mesh_infl), + ] + else: + vdiff_uvs = [ + _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx) + ] + diffs.append([hdiff_uvs, vdiff_uvs]) + diff_uvs.append(diffs) + + # update UV + for hseq, diffs in zip(loop_seqs, diff_uvs): + for vidx in range(0, len(hseq), 2): + loops = [ + hseq[vidx][0], hseq[vidx][1], + hseq[vidx + 1][0], hseq[vidx + 1][1] + ] + for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0], + diffs[int(vidx / 2)][1]): + l[uv_layer].uv = base_uv + hdiff + vdiff + if self.select: + l[uv_layer].select = True + + # selected and paralleled UV loop sequence will be aligned along to Y-axis + def __align_to_y_axis_w_transmission(self, loop_seqs, uv_layer, + uv_min, width, height): + # reverse if the UV coordinate is not sorted by position + need_revese = loop_seqs[0][0][0][uv_layer].uv.y > \ + loop_seqs[-1][0][-1][uv_layer].uv.y + if need_revese: + loop_seqs.reverse() + for hidx, hseq in enumerate(loop_seqs): + for vidx in range(len(hseq)): + tmp = loop_seqs[hidx][vidx][0] + loop_seqs[hidx][vidx][0] = loop_seqs[hidx][vidx][1] + loop_seqs[hidx][vidx][1] = tmp + + # get offset UVs when the UVs are aligned to Y-axis + align_diff_uvs = self.__get_y_axis_align_diff_uvs(loop_seqs, + uv_layer, uv_min, + width, height) + base_uv = loop_seqs[0][0][0][uv_layer].uv.copy() + offset_uvs = [] + for hseq, aduv in zip(loop_seqs, align_diff_uvs): + luv0 = hseq[0][0][uv_layer] + luv1 = hseq[0][1][uv_layer] + offset_uvs.append([luv0.uv + aduv[0] - base_uv, + luv1.uv + aduv[1] - base_uv]) + + # get UV differential + diff_uvs = [] + # hseq[vertical][loop] + for hidx, hseq in enumerate(loop_seqs): + # pair[loop] + diffs = [] + for vidx in range(0, len(hseq), 2): + if self.horizontal: + hdiff_uvs = [ + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, + self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, + self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 0, self.mesh_infl), + _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 1, self.mesh_infl), + ] + hdiff_uvs[0].x = hdiff_uvs[0].x + offset_uvs[hidx][0].x + hdiff_uvs[1].x = hdiff_uvs[1].x + offset_uvs[hidx][1].x + hdiff_uvs[2].x = hdiff_uvs[2].x + offset_uvs[hidx][0].x + hdiff_uvs[3].x = hdiff_uvs[3].x + offset_uvs[hidx][1].x + else: + hdiff_uvs = [ + offset_uvs[hidx][0], + offset_uvs[hidx][1], + offset_uvs[hidx][0], + offset_uvs[hidx][1], + ] + if self.vertical: + vdiff_uvs = [ + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 0, + self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1, + self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 0, self.mesh_infl), + _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1, + hidx, 1, self.mesh_infl), + ] + else: + vdiff_uvs = [ + _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx), + _get_vdiff_uv(uv_layer, loop_seqs, vidx + 1, hidx) + ] + diffs.append([hdiff_uvs, vdiff_uvs]) + diff_uvs.append(diffs) + + # update UV + for hseq, diffs in zip(loop_seqs, diff_uvs): + for vidx in range(0, len(hseq), 2): + loops = [ + hseq[vidx][0], hseq[vidx][1], + hseq[vidx + 1][0], hseq[vidx + 1][1] + ] + for l, hdiff, vdiff in zip(loops, diffs[int(vidx / 2)][0], + diffs[int(vidx / 2)][1]): + l[uv_layer].uv = base_uv + hdiff + vdiff + if self.select: + l[uv_layer].select = True + + def __align(self, loop_seqs, uv_layer, uv_min, width, height): + # align along to x-axis + if width > height: + if self.transmission: + self.__align_to_x_axis_w_transmission(loop_seqs, + uv_layer, uv_min, + width, height) + else: + self.__align_to_x_axis_wo_transmission(loop_seqs, + uv_layer, uv_min, + width, height) + # align along to y-axis + else: + if self.transmission: + self.__align_to_y_axis_w_transmission(loop_seqs, + uv_layer, uv_min, + width, height) + else: + self.__align_to_y_axis_wo_transmission(loop_seqs, + uv_layer, uv_min, + width, height) def execute(self, context): - return self.__impl.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() + uv_layer = bm.loops.layers.uv.verify() + + # loop_seqs[horizontal][vertical][loop] + loop_seqs, error = common.get_loop_sequences(bm, uv_layer) + if not loop_seqs: + self.report({'WARNING'}, error) + return {'CANCELLED'} + + # get height and width + uv_max, uv_min = self.__get_uv_max_min(loop_seqs, uv_layer) + width = uv_max.x - uv_min.x + height = uv_max.y - uv_min.y + + self.__align(loop_seqs, uv_layer, uv_min, width, height) + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} diff --git a/uv_magic_uv/op/align_uv_cursor.py b/uv_magic_uv/op/align_uv_cursor.py index 6de4bbcfd50740ca374cf1215e05a0fdc8eda25b..ab4e93b422eb57919749a856145fa292fd1050c9 100644 --- a/uv_magic_uv/op/align_uv_cursor.py +++ b/uv_magic_uv/op/align_uv_cursor.py @@ -26,11 +26,25 @@ __date__ = "17 Nov 2018" import bpy from mathutils import Vector from bpy.props import EnumProperty, BoolProperty, FloatVectorProperty +import bmesh from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import align_uv_cursor_impl as impl +from ..utils import compatibility as compat + + +def _is_valid_context(context): + # '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() @@ -40,17 +54,37 @@ class _Properties: @classmethod def init_props(cls, scene): def auvc_get_cursor_loc(self): - _, _, space = common.get_space_legacy('IMAGE_EDITOR', 'WINDOW', - 'IMAGE_EDITOR') + area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW', + 'IMAGE_EDITOR') + if compat.check_version(2, 80, 0) < 0: + bd_size = common.get_uvimg_editor_board_size(area) + else: + bd_size = [1.0, 1.0] loc = space.cursor_location - self['muv_align_uv_cursor_cursor_loc'] = Vector((loc[0], loc[1])) + + if bd_size[0] < 0.000001: + cx = 0.0 + else: + cx = loc[0] / bd_size[0] + if bd_size[1] < 0.000001: + cy = 0.0 + else: + cy = loc[1] / bd_size[1] + + self['muv_align_uv_cursor_cursor_loc'] = Vector((cx, cy)) return self.get('muv_align_uv_cursor_cursor_loc', (0.0, 0.0)) def auvc_set_cursor_loc(self, value): self['muv_align_uv_cursor_cursor_loc'] = value - _, _, space = common.get_space_legacy('IMAGE_EDITOR', 'WINDOW', - 'IMAGE_EDITOR') - space.cursor_location = Vector((value[0], value[1])) + area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW', + 'IMAGE_EDITOR') + if compat.check_version(2, 80, 0) < 0: + bd_size = common.get_uvimg_editor_board_size(area) + else: + bd_size = [1.0, 1.0] + cx = bd_size[0] * value[0] + cy = bd_size[1] * value[1] + space.cursor_location = Vector((cx, cy)) scene.muv_align_uv_cursor_enabled = BoolProperty( name="Align UV Cursor Enabled", @@ -96,6 +130,7 @@ class _Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_AlignUVCursor(bpy.types.Operator): bl_idname = "uv.muv_align_uv_cursor_operator" @@ -103,7 +138,7 @@ class MUV_OT_AlignUVCursor(bpy.types.Operator): bl_description = "Align cursor to the center of UV island" bl_options = {'REGISTER', 'UNDO'} - position: EnumProperty( + position = EnumProperty( items=( ('CENTER', "Center", "Align to Center"), ('LEFT_TOP', "Left Top", "Align to Left Top"), @@ -119,7 +154,7 @@ class MUV_OT_AlignUVCursor(bpy.types.Operator): description="Align position", default='CENTER' ) - base: EnumProperty( + base = EnumProperty( items=( ('TEXTURE', "Texture", "Align based on Texture"), ('UV', "UV", "Align to UV"), @@ -130,12 +165,105 @@ class MUV_OT_AlignUVCursor(bpy.types.Operator): default='TEXTURE' ) - def __init__(self): - self.__impl = impl.AlignUVCursorImpl() - @classmethod def poll(cls, context): - return impl.AlignUVCursorImpl.poll(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): - return self.__impl.execute(self, context) + area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW', + 'IMAGE_EDITOR') + if compat.check_version(2, 80, 0) < 0: + bd_size = common.get_uvimg_editor_board_size(area) + else: + bd_size = [1.0, 1.0] + + if self.base == 'UV': + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if not bm.loops.layers.uv: + return None + uv_layer = bm.loops.layers.uv.verify() + + max_ = Vector((-10000000.0, -10000000.0)) + min_ = Vector((10000000.0, 10000000.0)) + for f in bm.faces: + if not f.select: + continue + for l in f.loops: + uv = l[uv_layer].uv + max_.x = max(max_.x, uv.x) + max_.y = max(max_.y, uv.y) + min_.x = min(min_.x, uv.x) + min_.y = min(min_.y, uv.y) + center = Vector(((max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0)) + + elif self.base == 'UV_SEL': + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if not bm.loops.layers.uv: + return None + uv_layer = bm.loops.layers.uv.verify() + + max_ = Vector((-10000000.0, -10000000.0)) + min_ = Vector((10000000.0, 10000000.0)) + for f in bm.faces: + if not f.select: + continue + for l in f.loops: + if not l[uv_layer].select: + continue + uv = l[uv_layer].uv + max_.x = max(max_.x, uv.x) + max_.y = max(max_.y, uv.y) + min_.x = min(min_.x, uv.x) + min_.y = min(min_.y, uv.y) + center = Vector(((max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0)) + + elif self.base == 'TEXTURE': + min_ = Vector((0.0, 0.0)) + max_ = Vector((1.0, 1.0)) + center = Vector((0.5, 0.5)) + else: + self.report({'ERROR'}, "Unknown Operation") + return {'CANCELLED'} + + if self.position == 'CENTER': + cx = center.x + cy = center.y + elif self.position == 'LEFT_TOP': + cx = min_.x + cy = max_.y + elif self.position == 'LEFT_MIDDLE': + cx = min_.x + cy = center.y + elif self.position == 'LEFT_BOTTOM': + cx = min_.x + cy = min_.y + elif self.position == 'MIDDLE_TOP': + cx = center.x + cy = max_.y + elif self.position == 'MIDDLE_BOTTOM': + cx = center.x + cy = min_.y + elif self.position == 'RIGHT_TOP': + cx = max_.x + cy = max_.y + elif self.position == 'RIGHT_MIDDLE': + cx = max_.x + cy = center.y + elif self.position == 'RIGHT_BOTTOM': + cx = max_.x + cy = min_.y + else: + self.report({'ERROR'}, "Unknown Operation") + return {'CANCELLED'} + + cx = cx * bd_size[0] + cy = cy * bd_size[1] + + space.cursor_location = Vector((cx, cy)) + + return {'FINISHED'} diff --git a/uv_magic_uv/op/copy_paste_uv.py b/uv_magic_uv/op/copy_paste_uv.py index 23bc8343853fa275a4d414e19a7ab68ea628de82..f5ff883e235fe319b10420e0b8537a23e3d9e498 100644 --- a/uv_magic_uv/op/copy_paste_uv.py +++ b/uv_magic_uv/op/copy_paste_uv.py @@ -23,7 +23,6 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" - import bmesh import bpy.utils from bpy.props import ( @@ -33,26 +32,244 @@ 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', - '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', -] +from ..utils import compatibility as compat + + +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 @PropertyClassRegistry() -class Properties: +class _Properties: idname = "copy_paste_uv" @classmethod @@ -103,6 +320,7 @@ class Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_CopyPasteUV_CopyUV(bpy.types.Operator): """ Operation class: Copy UV coordinate @@ -113,14 +331,14 @@ class MUV_OT_CopyPasteUV_CopyUV(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 impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.copy_paste_uv @@ -128,12 +346,12 @@ class MUV_OT_CopyPasteUV_CopyUV(bpy.types.Operator): bm = common.create_bmesh(obj) # get UV layer - uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map) + uv_layers = 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) + src_info = get_src_face_info(self, bm, uv_layers, True) if src_info is None: return {'CANCELLED'} props.src_info = src_info @@ -156,7 +374,7 @@ class MUV_MT_CopyPasteUV_CopyUV(bpy.types.Menu): @classmethod def poll(cls, context): - return impl.is_valid_context(context) + return _is_valid_context(context) def draw(self, context): layout = self.layout @@ -179,6 +397,7 @@ class MUV_MT_CopyPasteUV_CopyUV(bpy.types.Menu): @BlClassRegistry() +@compat.make_annotations class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator): """ Operation class: Paste UV coordinate @@ -189,8 +408,8 @@ class MUV_OT_CopyPasteUV_PasteUV(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=[ @@ -199,18 +418,18 @@ class MUV_OT_CopyPasteUV_PasteUV(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 @@ -225,7 +444,7 @@ class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator): props = sc.muv_props.copy_paste_uv if not props.src_info: return False - return impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.copy_paste_uv @@ -236,21 +455,21 @@ class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator): bm = common.create_bmesh(obj) # get UV layer - uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info, - self.uv_map) + uv_layers = 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) + dest_info = 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) + ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers, + self.strategy, self.flip_copied_uv, + self.rotate_copied_uv, self.copy_seams) if ret: return {'CANCELLED'} @@ -259,6 +478,10 @@ class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator): bmesh.update_edit_mesh(obj.data) + if compat.check_version(2, 80, 0) < 0: + if self.copy_seams is True: + obj.data.show_edge_seams = True + return {'FINISHED'} @@ -278,7 +501,7 @@ class MUV_MT_CopyPasteUV_PasteUV(bpy.types.Menu): props = sc.muv_props.copy_paste_uv if not props.src_info: return False - return impl.is_valid_context(context) + return _is_valid_context(context) def draw(self, context): sc = context.scene @@ -314,6 +537,7 @@ class MUV_MT_CopyPasteUV_PasteUV(bpy.types.Menu): @BlClassRegistry() +@compat.make_annotations class MUV_OT_CopyPasteUV_SelSeqCopyUV(bpy.types.Operator): """ Operation class: Copy UV coordinate by selection sequence @@ -324,14 +548,14 @@ class MUV_OT_CopyPasteUV_SelSeqCopyUV(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 impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.copy_paste_uv_selseq @@ -339,12 +563,12 @@ class MUV_OT_CopyPasteUV_SelSeqCopyUV(bpy.types.Operator): bm = common.create_bmesh(obj) # get UV layer - uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map) + uv_layers = 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) + src_info = _get_select_history_src_face_info(self, bm, uv_layers) if src_info is None: return {'CANCELLED'} props.src_info = src_info @@ -367,7 +591,7 @@ class MUV_MT_CopyPasteUV_SelSeqCopyUV(bpy.types.Menu): @classmethod def poll(cls, context): - return impl.is_valid_context(context) + return _is_valid_context(context) def draw(self, context): layout = self.layout @@ -390,6 +614,7 @@ class MUV_MT_CopyPasteUV_SelSeqCopyUV(bpy.types.Menu): @BlClassRegistry() +@compat.make_annotations class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator): """ Operation class: Paste UV coordinate by selection sequence @@ -400,8 +625,8 @@ class MUV_OT_CopyPasteUV_SelSeqPasteUV(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=[ @@ -410,18 +635,18 @@ class MUV_OT_CopyPasteUV_SelSeqPasteUV(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 @@ -436,7 +661,7 @@ class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator): props = sc.muv_props.copy_paste_uv_selseq if not props.src_info: return False - return impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.copy_paste_uv_selseq @@ -447,22 +672,22 @@ class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator): bm = common.create_bmesh(obj) # get UV layer - uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info, - self.uv_map) + uv_layers = 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) + dest_info = _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) + ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers, + self.strategy, self.flip_copied_uv, + self.rotate_copied_uv, self.copy_seams) if ret: return {'CANCELLED'} @@ -471,6 +696,10 @@ class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator): bmesh.update_edit_mesh(obj.data) + if compat.check_version(2, 80, 0) < 0: + if self.copy_seams is True: + obj.data.show_edge_seams = True + return {'FINISHED'} @@ -490,7 +719,7 @@ class MUV_MT_CopyPasteUV_SelSeqPasteUV(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 impl.is_valid_context(context) + return _is_valid_context(context) def draw(self, context): sc = context.scene diff --git a/uv_magic_uv/op/copy_paste_uv_object.py b/uv_magic_uv/op/copy_paste_uv_object.py index d9f42447a538735b5d8c0515cb729e8f249ea14a..dc7073b81572d77781f750c5a2cd6639b451e396 100644 --- a/uv_magic_uv/op/copy_paste_uv_object.py +++ b/uv_magic_uv/op/copy_paste_uv_object.py @@ -30,21 +30,20 @@ from bpy.props import ( BoolProperty, ) -from ..impl import copy_paste_uv_impl as impl +from .copy_paste_uv import ( + get_copy_uv_layers, + get_src_face_info, + get_paste_uv_layers, + get_dest_face_info, + paste_uv, +) 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', -] +from ..utils import compatibility as compat -def is_valid_context(context): +def _is_valid_context(context): obj = context.object # only object mode is allowed to execute @@ -66,7 +65,7 @@ def is_valid_context(context): @PropertyClassRegistry() -class Properties: +class _Properties: idname = "copy_paste_uv_object" @classmethod @@ -98,6 +97,7 @@ def memorize_view_3d_mode(fn): @BlClassRegistry() +@compat.make_annotations class MUV_OT_CopyPasteUVObject_CopyUV(bpy.types.Operator): """ Operation class: Copy UV coordinate among objects @@ -108,14 +108,14 @@ class MUV_OT_CopyPasteUVObject_CopyUV(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): # we can not get area/space/region from console if common.is_console_mode(): return True - return is_valid_context(context) + return _is_valid_context(context) @memorize_view_3d_mode def execute(self, context): @@ -125,12 +125,12 @@ class MUV_OT_CopyPasteUVObject_CopyUV(bpy.types.Operator): bm = common.create_bmesh(obj) # get UV layer - uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map) + uv_layers = 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) + src_info = get_src_face_info(self, bm, uv_layers) if src_info is None: return {'CANCELLED'} props.src_info = src_info @@ -153,12 +153,12 @@ class MUV_MT_CopyPasteUVObject_CopyUV(bpy.types.Menu): @classmethod def poll(cls, context): - return is_valid_context(context) + return _is_valid_context(context) def draw(self, _): layout = self.layout # create sub menu - uv_maps = bpy.context.active_object.data.uv_layers.keys() + uv_maps = compat.get_object_uv_layers(bpy.context.active_object).keys() ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname, text="[Default]") @@ -175,6 +175,7 @@ class MUV_MT_CopyPasteUVObject_CopyUV(bpy.types.Menu): @BlClassRegistry() +@compat.make_annotations class MUV_OT_CopyPasteUVObject_PasteUV(bpy.types.Operator): """ Operation class: Paste UV coordinate among objects @@ -185,8 +186,8 @@ class MUV_OT_CopyPasteUVObject_PasteUV(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 @@ -201,7 +202,7 @@ class MUV_OT_CopyPasteUVObject_PasteUV(bpy.types.Operator): props = sc.muv_props.copy_paste_uv_object if not props.src_info: return False - return is_valid_context(context) + return _is_valid_context(context) @memorize_view_3d_mode def execute(self, context): @@ -211,36 +212,40 @@ class MUV_OT_CopyPasteUVObject_PasteUV(bpy.types.Operator): return {'CANCELLED'} for o in bpy.data.objects: - if not hasattr(o.data, "uv_layers") or not o.select_get(): + if not compat.object_has_uv_layers(o) or not compat.get_object_select(o): continue bpy.ops.object.mode_set(mode='OBJECT') - bpy.context.view_layer.objects.active = o + compat.set_active_object(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) + uv_layers = 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') + dest_info = 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) + ret = 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 compat.check_version(2, 80, 0) < 0: + if self.copy_seams is True: + obj.data.show_edge_seams = True + self.report( {'INFO'}, "{}'s UV coordinates are pasted".format(obj.name)) @@ -263,7 +268,7 @@ class MUV_MT_CopyPasteUVObject_PasteUV(bpy.types.Menu): props = sc.muv_props.copy_paste_uv_object if not props.src_info: return False - return is_valid_context(context) + return _is_valid_context(context) def draw(self, context): sc = context.scene @@ -271,8 +276,8 @@ class MUV_MT_CopyPasteUVObject_PasteUV(bpy.types.Menu): # create sub menu uv_maps = [] for obj in bpy.data.objects: - if hasattr(obj.data, "uv_layers") and obj.select_get(): - uv_maps.extend(obj.data.uv_layers.keys()) + if compat.object_has_uv_layers(obj) and compat.get_object_select(obj): + uv_maps.extend(compat.get_object_uv_layers(obj).keys()) ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname, text="[Default]") diff --git a/uv_magic_uv/op/copy_paste_uv_uvedit.py b/uv_magic_uv/op/copy_paste_uv_uvedit.py index 719687a6016e510212c10ac05f2c9fa8994bda7d..16c0dfa5da36e38768a69d6d78c119a4811facdd 100644 --- a/uv_magic_uv/op/copy_paste_uv_uvedit.py +++ b/uv_magic_uv/op/copy_paste_uv_uvedit.py @@ -24,21 +24,43 @@ __version__ = "5.2" __date__ = "17 Nov 2018" import bpy +import math +from math import atan2, sin, cos + +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', - '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: +class _Properties: idname = "copy_paste_uv_uvedit" @classmethod @@ -64,15 +86,35 @@ class MUV_OT_CopyPasteUVUVEdit_CopyUV(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): - return impl.CopyUVImpl.poll(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): - return self.__impl.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'} @BlClassRegistry() @@ -86,12 +128,72 @@ class MUV_OT_CopyPasteUVUVEdit_PasteUV(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): - return impl.PasteUVImpl.poll(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): - return self.__impl.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/op/flip_rotate_uv.py b/uv_magic_uv/op/flip_rotate_uv.py index d16370528b1927d9df1fadf5bd713253036dd1a4..2ecab25415ec8e85f4bfc53e57b324574e10421a 100644 --- a/uv_magic_uv/op/flip_rotate_uv.py +++ b/uv_magic_uv/op/flip_rotate_uv.py @@ -33,16 +33,113 @@ 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', - 'MUV_OT_FlipRotate', -] +from ..utils import compatibility as compat + + +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 @PropertyClassRegistry() -class Properties: +class _Properties: idname = "flip_rotate_uv" @classmethod @@ -65,6 +162,7 @@ class Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_FlipRotate(bpy.types.Operator): """ Operation class: Flip and Rotate UV coordinate @@ -75,18 +173,18 @@ class MUV_OT_FlipRotate(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 @@ -97,7 +195,7 @@ class MUV_OT_FlipRotate(bpy.types.Operator): # we can not get area/space/region from console if common.is_console_mode(): return True - return impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): self.report({'INFO'}, "Flip/Rotate UV") @@ -107,12 +205,12 @@ class MUV_OT_FlipRotate(bpy.types.Operator): bm.faces.ensure_lookup_table() # get UV layer - uv_layer = impl.get_uv_layer(self, bm) + uv_layer = _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) + src_info = _get_src_face_info(self, bm, [uv_layer], True) if not src_info: return {'CANCELLED'} @@ -120,11 +218,15 @@ class MUV_OT_FlipRotate(bpy.types.Operator): 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) + ret = _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 compat.check_version(2, 80, 0) < 0: + 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 6793ca232e2bdcafbb4a95c02dbc1edc0cd3bbbb..b806daea5daec2a49d52cb500fe869f827a51eef 100644 --- a/uv_magic_uv/op/mirror_uv.py +++ b/uv_magic_uv/op/mirror_uv.py @@ -29,20 +29,80 @@ from bpy.props import ( FloatProperty, BoolProperty, ) +import bmesh +from mathutils import Vector from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import mirror_uv_impl as impl +from ..utils import compatibility as compat +from .. import common -__all__ = [ - 'Properties', - '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 + + +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) @PropertyClassRegistry() -class Properties: +class _Properties: idname = "mirror_uv" @classmethod @@ -70,6 +130,7 @@ class Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_MirrorUV(bpy.types.Operator): """ Operation class: Mirror UV @@ -79,7 +140,7 @@ class MUV_OT_MirrorUV(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"), @@ -89,7 +150,7 @@ class MUV_OT_MirrorUV(bpy.types.Operator): description="Mirror Axis", default='X' ) - error: FloatProperty( + error = FloatProperty( name="Error", description="Error threshold", default=0.001, @@ -99,12 +160,56 @@ class MUV_OT_MirrorUV(bpy.types.Operator): soft_max=1.0 ) - def __init__(self): - self.__impl = impl.MirrorUVImpl() - @classmethod def poll(cls, context): - return impl.MirrorUVImpl.poll(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): - return self.__impl.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 = 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, self.axis, self.error) + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} diff --git a/uv_magic_uv/op/move_uv.py b/uv_magic_uv/op/move_uv.py index 653918d350e20c1f82e3f7b110efe73e7900dd10..b747892ae4300d81e2dfc2b21030bd35fb79f9bf 100644 --- a/uv_magic_uv/op/move_uv.py +++ b/uv_magic_uv/op/move_uv.py @@ -25,20 +25,51 @@ __date__ = "17 Nov 2018" import bpy from bpy.props import BoolProperty +import bmesh +from mathutils import Vector -from ..impl import move_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_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 + + +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 @PropertyClassRegistry() -class Properties: +class _Properties: idname = "move_uv" @classmethod @@ -64,19 +95,91 @@ class MUV_OT_MoveUV(bpy.types.Operator): bl_label = "Move UV" bl_options = {'REGISTER', 'UNDO'} + __running = False + def __init__(self): - self.__impl = impl.MoveUVImpl() + 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): - return impl.MoveUVImpl.poll(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 impl.MoveUVImpl.is_running(_) + return cls.__running def modal(self, context, event): - return self.__impl.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 + confirm_btn = 'LEFTMOUSE' + cancel_btn = 'RIGHTMOUSE' + + # 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 + MUV_OT_MoveUV.__running = False + return {'FINISHED'} + # confirmed + if event.type == confirm_btn and event.value == 'PRESS': + MUV_OT_MoveUV.__running = False + return {'FINISHED'} + + return {'RUNNING_MODAL'} def execute(self, context): - return self.__impl.execute(self, context) + MUV_OT_MoveUV.__running = True + self.__operating = False + self.__first_time = True + + context.window_manager.modal_handler_add(self) + 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/op/pack_uv.py b/uv_magic_uv/op/pack_uv.py index 84f195c5d64f853313aef3f31e4f1a978b22cdc5..35685221c772f656e97a78bc453ed4dd6c8726da 100644 --- a/uv_magic_uv/op/pack_uv.py +++ b/uv_magic_uv/op/pack_uv.py @@ -23,26 +23,126 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" +from math import fabs + import bpy from bpy.props import ( FloatProperty, FloatVectorProperty, BoolProperty, ) +import bmesh +import mathutils +from mathutils import Vector from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import pack_uv_impl as impl +from ..utils import compatibility as compat +from .. import common + + +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 + + +def _sort_island_faces(kd, uvs, isl1, isl2): + """ + Sort faces in island + """ + + sorted_faces = [] + for f in isl1['sorted']: + _, idx, _ = kd.find( + Vector((f['ave_uv'].x, f['ave_uv'].y, 0.0))) + sorted_faces.append(isl2['faces'][uvs[idx]['face_idx']]) + return sorted_faces + +def _group_island(island_info, allowable_center_deviation, + allowable_size_deviation): + """ + Group island + """ + + num_group = 0 + while True: + # search islands which is not parsed yet + isl_1 = None + for isl_1 in island_info: + if isl_1['group'] == -1: + break + else: + break # all faces are parsed + if isl_1 is None: + break + isl_1['group'] = num_group + isl_1['sorted'] = isl_1['faces'] -__all__ = [ - 'Properties', - 'MUV_OT_PackUV', -] + # search same island + for isl_2 in island_info: + if isl_2['group'] == -1: + dcx = isl_2['center'].x - isl_1['center'].x + dcy = isl_2['center'].y - isl_1['center'].y + dsx = isl_2['size'].x - isl_1['size'].x + dsy = isl_2['size'].y - isl_1['size'].y + center_x_matched = ( + fabs(dcx) < allowable_center_deviation[0] + ) + center_y_matched = ( + fabs(dcy) < allowable_center_deviation[1] + ) + size_x_matched = ( + fabs(dsx) < allowable_size_deviation[0] + ) + size_y_matched = ( + fabs(dsy) < allowable_size_deviation[1] + ) + center_matched = center_x_matched and center_y_matched + size_matched = size_x_matched and size_y_matched + num_uv_matched = (isl_2['num_uv'] == isl_1['num_uv']) + # are islands have same? + if center_matched and size_matched and num_uv_matched: + isl_2['group'] = num_group + kd = mathutils.kdtree.KDTree(len(isl_2['faces'])) + uvs = [ + { + 'uv': Vector( + (f['ave_uv'].x, f['ave_uv'].y, 0.0) + ), + 'face_idx': fidx + } for fidx, f in enumerate(isl_2['faces']) + ] + for i, uv in enumerate(uvs): + kd.insert(uv['uv'], i) + kd.balance() + # sort faces for copy/paste UV + isl_2['sorted'] = _sort_island_faces(kd, uvs, isl_1, isl_2) + num_group = num_group + 1 + + return num_group @PropertyClassRegistry() -class Properties: +class _Properties: idname = "pack_uv" @classmethod @@ -77,6 +177,7 @@ class Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_PackUV(bpy.types.Operator): """ Operation class: Pack UV with same UV islands are integrated @@ -91,17 +192,17 @@ class MUV_OT_PackUV(bpy.types.Operator): bl_description = "Pack UV (Same UV Islands are integrated)" bl_options = {'REGISTER', 'UNDO'} - rotate: BoolProperty( + rotate = BoolProperty( name="Rotate", description="Rotate option used by default pack UV function", default=False) - margin: FloatProperty( + margin = FloatProperty( name="Margin", description="Margin used by default pack UV function", min=0, max=1, default=0.001) - allowable_center_deviation: FloatVectorProperty( + allowable_center_deviation = FloatVectorProperty( name="Allowable Center Deviation", description="Allowable center deviation to judge same UV island", min=0.000001, @@ -109,7 +210,7 @@ class MUV_OT_PackUV(bpy.types.Operator): default=(0.001, 0.001), size=2 ) - allowable_size_deviation: FloatVectorProperty( + allowable_size_deviation = FloatVectorProperty( name="Allowable Size Deviation", description="Allowable sizse deviation to judge same UV island", min=0.000001, @@ -118,12 +219,64 @@ class MUV_OT_PackUV(bpy.types.Operator): size=2 ) - def __init__(self): - self.__impl = impl.PackUVImpl() - @classmethod def poll(cls, context): - return impl.PackUVImpl.poll(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): - return self.__impl.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() + 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() + + selected_faces = [f for f in bm.faces if f.select] + island_info = common.get_island_info(obj) + num_group = _group_island(island_info, + self.allowable_center_deviation, + self.allowable_size_deviation) + + loop_lists = [l for f in bm.faces for l in f.loops] + bpy.ops.mesh.select_all(action='DESELECT') + + # pack UV + for gidx in range(num_group): + group = list(filter( + lambda i, idx=gidx: i['group'] == idx, island_info)) + for f in group[0]['faces']: + f['face'].select = True + bmesh.update_edit_mesh(obj.data) + bpy.ops.uv.select_all(action='SELECT') + bpy.ops.uv.pack_islands(rotate=self.rotate, margin=self.margin) + + # copy/paste UV among same islands + for gidx in range(num_group): + group = list(filter( + lambda i, idx=gidx: i['group'] == idx, island_info)) + if len(group) <= 1: + continue + for g in group[1:]: + for (src_face, dest_face) in zip( + group[0]['sorted'], g['sorted']): + for (src_loop, dest_loop) in zip( + src_face['face'].loops, dest_face['face'].loops): + loop_lists[dest_loop.index][uv_layer].uv = loop_lists[ + src_loop.index][uv_layer].uv + + # restore face/UV selection + bpy.ops.uv.select_all(action='DESELECT') + bpy.ops.mesh.select_all(action='DESELECT') + for f in selected_faces: + f.select = True + bpy.ops.uv.select_all(action='SELECT') + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} diff --git a/uv_magic_uv/op/preserve_uv_aspect.py b/uv_magic_uv/op/preserve_uv_aspect.py index ca4969fde12536f649dfc85cb2c725d06027fead..a200edaccd802b1ae16438047286a45a8c127caa 100644 --- a/uv_magic_uv/op/preserve_uv_aspect.py +++ b/uv_magic_uv/op/preserve_uv_aspect.py @@ -25,16 +25,34 @@ __date__ = "17 Nov 2018" import bpy from bpy.props import StringProperty, EnumProperty, 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 preserve_uv_aspect_impl as impl +from ..utils import compatibility as compat -__all__ = [ - 'Properties', - 'MUV_OT_PreserveUVAspect', -] +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() @@ -84,6 +102,7 @@ class _Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_PreserveUVAspect(bpy.types.Operator): """ Operation class: Preserve UV Aspect @@ -94,8 +113,8 @@ class MUV_OT_PreserveUVAspect(bpy.types.Operator): bl_description = "Choose Image" bl_options = {'REGISTER', 'UNDO'} - dest_img_name: StringProperty(options={'HIDDEN'}) - origin: EnumProperty( + dest_img_name = StringProperty(options={'HIDDEN'}) + origin = EnumProperty( name="Origin", description="Aspect Origin", items=[ @@ -113,12 +132,166 @@ class MUV_OT_PreserveUVAspect(bpy.types.Operator): default="CENTER" ) - def __init__(self): - self.__impl = impl.PreserveUVAspectImpl() - @classmethod def poll(cls, context): - return impl.PreserveUVAspectImpl.poll(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): - return self.__impl.execute(self, context) + # Note: the current system only works if the + # f[tex_layer].image doesn't return None + # which will happen in certain cases + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + + 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() + + sel_faces = [f for f in bm.faces if f.select] + dest_img = bpy.data.images[self.dest_img_name] + + info = {} + + if compat.check_version(2, 80, 0) >= 0: + tex_image = common.find_image(obj) + for f in sel_faces: + if tex_image not in info.keys(): + info[tex_image] = {} + info[tex_image]['faces'] = [] + info[tex_image]['faces'].append(f) + else: + tex_layer = bm.faces.layers.tex.verify() + for f in sel_faces: + if not f[tex_layer].image in info.keys(): + info[f[tex_layer].image] = {} + info[f[tex_layer].image]['faces'] = [] + info[f[tex_layer].image]['faces'].append(f) + + for img in info: + if img is None: + continue + + src_img = img + ratio = Vector(( + dest_img.size[0] / src_img.size[0], + dest_img.size[1] / src_img.size[1])) + + if self.origin == 'CENTER': + origin = Vector((0.0, 0.0)) + num = 0 + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin = origin + uv + num = num + 1 + origin = origin / num + elif self.origin == 'LEFT_TOP': + origin = Vector((100000.0, -100000.0)) + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = max(origin.y, uv.y) + elif self.origin == 'LEFT_CENTER': + origin = Vector((100000.0, 0.0)) + num = 0 + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = origin.y + uv.y + num = num + 1 + origin.y = origin.y / num + elif self.origin == 'LEFT_BOTTOM': + origin = Vector((100000.0, 100000.0)) + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = min(origin.y, uv.y) + elif self.origin == 'CENTER_TOP': + origin = Vector((0.0, -100000.0)) + num = 0 + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = origin.x + uv.x + origin.y = max(origin.y, uv.y) + num = num + 1 + origin.x = origin.x / num + elif self.origin == 'CENTER_BOTTOM': + origin = Vector((0.0, 100000.0)) + num = 0 + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = origin.x + uv.x + origin.y = min(origin.y, uv.y) + num = num + 1 + origin.x = origin.x / num + elif self.origin == 'RIGHT_TOP': + origin = Vector((-100000.0, -100000.0)) + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = max(origin.x, uv.x) + origin.y = max(origin.y, uv.y) + elif self.origin == 'RIGHT_CENTER': + origin = Vector((-100000.0, 0.0)) + num = 0 + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = max(origin.x, uv.x) + origin.y = origin.y + uv.y + num = num + 1 + origin.y = origin.y / num + elif self.origin == 'RIGHT_BOTTOM': + origin = Vector((-100000.0, 100000.0)) + for f in info[img]['faces']: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = max(origin.x, uv.x) + origin.y = min(origin.y, uv.y) + else: + self.report({'ERROR'}, "Unknown Operation") + return {'CANCELLED'} + + info[img]['ratio'] = ratio + info[img]['origin'] = origin + + for img in info: + if img is None: + continue + + if compat.check_version(2, 80, 0) >= 0: + nodes = common.find_texture_nodes(obj) + nodes[0].image = dest_img + + for f in info[img]['faces']: + if compat.check_version(2, 80, 0) < 0: + tex_layer = bm.faces.layers.tex.verify() + f[tex_layer].image = dest_img + for l in f.loops: + uv = l[uv_layer].uv + origin = info[img]['origin'] + ratio = info[img]['ratio'] + diff = uv - origin + diff.x = diff.x / ratio.x + diff.y = diff.y / ratio.y + uv.x = origin.x + diff.x + uv.y = origin.y + diff.y + l[uv_layer].uv = uv + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} diff --git a/uv_magic_uv/op/select_uv.py b/uv_magic_uv/op/select_uv.py index 789af9ce8bd7b4e76c982c4290eff16519526e88..4664551e44b052d9bdc467ed6ee7defc22d79a41 100644 --- a/uv_magic_uv/op/select_uv.py +++ b/uv_magic_uv/op/select_uv.py @@ -25,10 +25,34 @@ __date__ = "17 Nov 2018" import bpy from bpy.props import BoolProperty +import bmesh +from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import select_uv_impl as impl + + +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() @@ -59,15 +83,38 @@ class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator): bl_description = "Select faces which have overlapped UVs" bl_options = {'REGISTER', 'UNDO'} - def __init__(self): - self.__impl = impl.SelectOverlappedImpl() - @classmethod def poll(cls, context): - return impl.SelectOverlappedImpl.poll(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): - return self.__impl.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() + uv_layer = bm.loops.layers.uv.verify() + + if context.tool_settings.use_uv_select_sync: + sel_faces = [f for f in bm.faces] + else: + sel_faces = [f for f in bm.faces if f.select] + + overlapped_info = common.get_overlapped_uv_info(bm, sel_faces, + uv_layer, 'FACE') + + for info in overlapped_info: + if context.tool_settings.use_uv_select_sync: + info["subject_face"].select = True + else: + for l in info["subject_face"].loops: + l[uv_layer].select = True + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} @BlClassRegistry() @@ -81,12 +128,34 @@ class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator): bl_description = "Select faces which have flipped UVs" bl_options = {'REGISTER', 'UNDO'} - def __init__(self): - self.__impl = impl.SelectFlippedImpl() - @classmethod def poll(cls, context): - return impl.SelectFlippedImpl.poll(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): - return self.__impl.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() + uv_layer = bm.loops.layers.uv.verify() + + if context.tool_settings.use_uv_select_sync: + sel_faces = [f for f in bm.faces] + else: + sel_faces = [f for f in bm.faces if f.select] + + flipped_info = common.get_flipped_uv_info(sel_faces, uv_layer) + + for info in flipped_info: + if context.tool_settings.use_uv_select_sync: + info["face"].select = True + else: + for l in info["face"].loops: + l[uv_layer].select = True + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} diff --git a/uv_magic_uv/op/smooth_uv.py b/uv_magic_uv/op/smooth_uv.py index d448d10834ea4dc9bce9d8b0c80ec719cadb435f..83a4a1a5a2b94b61395b2415ed7373d602856d92 100644 --- a/uv_magic_uv/op/smooth_uv.py +++ b/uv_magic_uv/op/smooth_uv.py @@ -25,10 +25,35 @@ __date__ = "17 Nov 2018" import bpy from bpy.props import BoolProperty, FloatProperty +import bmesh +from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import smooth_uv_impl as impl +from ..utils import compatibility as compat + + +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() @@ -69,6 +94,7 @@ class _Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_SmoothUV(bpy.types.Operator): bl_idname = "uv.muv_smooth_uv_operator" @@ -76,30 +102,182 @@ class MUV_OT_SmoothUV(bpy.types.Operator): bl_description = "Smooth UV coordinates" bl_options = {'REGISTER', 'UNDO'} - transmission: BoolProperty( + transmission = BoolProperty( name="Transmission", description="Smooth linked UVs", default=False ) - mesh_infl: FloatProperty( + mesh_infl = FloatProperty( name="Mesh Influence", description="Influence rate of mesh vertex", min=0.0, max=1.0, default=0.0 ) - select: BoolProperty( + select = BoolProperty( name="Select", description="Select UVs which are smoothed", default=False ) - def __init__(self): - self.__impl = impl.SmoothUVImpl() - @classmethod def poll(cls, context): - return impl.SmoothUVImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + def __smooth_wo_transmission(self, loop_seqs, uv_layer): + # calculate path length + loops = [] + for hseq in loop_seqs: + loops.extend([hseq[0][0], hseq[0][1]]) + full_vlen = 0 + accm_vlens = [0.0] + full_uvlen = 0 + accm_uvlens = [0.0] + orig_uvs = [loop_seqs[0][0][0][uv_layer].uv.copy()] + for l1, l2 in zip(loops[:-1], loops[1:]): + diff_v = l2.vert.co - l1.vert.co + full_vlen = full_vlen + diff_v.length + accm_vlens.append(full_vlen) + diff_uv = l2[uv_layer].uv - l1[uv_layer].uv + full_uvlen = full_uvlen + diff_uv.length + accm_uvlens.append(full_uvlen) + orig_uvs.append(l2[uv_layer].uv.copy()) + + for hidx, hseq in enumerate(loop_seqs): + pair = hseq[0] + for pidx, l in enumerate(pair): + if self.select: + l[uv_layer].select = True + + # ignore start/end loop + if (hidx == 0 and pidx == 0) or\ + ((hidx == len(loop_seqs) - 1) and (pidx == len(pair) - 1)): + continue + + # calculate target path length + # target = no influenced * (1 - infl) + influenced * infl + tgt_noinfl = full_uvlen * (hidx + pidx) / (len(loop_seqs)) + tgt_infl = full_uvlen * accm_vlens[hidx * 2 + pidx] / full_vlen + target_length = tgt_noinfl * (1 - self.mesh_infl) + \ + tgt_infl * self.mesh_infl + + # get target UV + for i in range(len(accm_uvlens[:-1])): + # get line segment which UV will be placed + if ((accm_uvlens[i] <= target_length) and + (accm_uvlens[i + 1] > target_length)): + tgt_seg_len = target_length - accm_uvlens[i] + seg_len = accm_uvlens[i + 1] - accm_uvlens[i] + uv1 = orig_uvs[i] + uv2 = orig_uvs[i + 1] + target_uv = uv1 + (uv2 - uv1) * tgt_seg_len / seg_len + break + else: + self.report({'ERROR'}, "Failed to get target UV") + return {'CANCELLED'} + + # update UV + l[uv_layer].uv = target_uv + + def __smooth_w_transmission(self, loop_seqs, uv_layer): + # calculate path length + loops = [] + for vidx in range(len(loop_seqs[0])): + ls = [] + for hseq in loop_seqs: + ls.extend(hseq[vidx]) + loops.append(ls) + + orig_uvs = [] + accm_vlens = [] + full_vlens = [] + accm_uvlens = [] + full_uvlens = [] + for ls in loops: + full_v = 0.0 + accm_v = [0.0] + full_uv = 0.0 + accm_uv = [0.0] + uvs = [ls[0][uv_layer].uv.copy()] + for l1, l2 in zip(ls[:-1], ls[1:]): + diff_v = l2.vert.co - l1.vert.co + full_v = full_v + diff_v.length + accm_v.append(full_v) + diff_uv = l2[uv_layer].uv - l1[uv_layer].uv + full_uv = full_uv + diff_uv.length + accm_uv.append(full_uv) + uvs.append(l2[uv_layer].uv.copy()) + accm_vlens.append(accm_v) + full_vlens.append(full_v) + accm_uvlens.append(accm_uv) + full_uvlens.append(full_uv) + orig_uvs.append(uvs) + + for hidx, hseq in enumerate(loop_seqs): + for vidx, (pair, uvs, accm_v, full_v, accm_uv, full_uv)\ + in enumerate(zip(hseq, orig_uvs, accm_vlens, full_vlens, + accm_uvlens, full_uvlens)): + for pidx, l in enumerate(pair): + if self.select: + l[uv_layer].select = True + + # ignore start/end loop + if hidx == 0 and pidx == 0: + continue + if hidx == len(loop_seqs) - 1 and pidx == len(pair) - 1: + continue + + # calculate target path length + # target = no influenced * (1 - infl) + influenced * infl + tgt_noinfl = full_uv * (hidx + pidx) / (len(loop_seqs)) + tgt_infl = full_uv * accm_v[hidx * 2 + pidx] / full_v + target_length = tgt_noinfl * (1 - self.mesh_infl) + \ + tgt_infl * self.mesh_infl + + # get target UV + for i in range(len(accm_uv[:-1])): + # get line segment to be placed + if ((accm_uv[i] <= target_length) and + (accm_uv[i + 1] > target_length)): + tgt_seg_len = target_length - accm_uv[i] + seg_len = accm_uv[i + 1] - accm_uv[i] + uv1 = uvs[i] + uv2 = uvs[i + 1] + target_uv = uv1 +\ + (uv2 - uv1) * tgt_seg_len / seg_len + break + else: + self.report({'ERROR'}, "Failed to get target UV") + return {'CANCELLED'} + + # update UV + l[uv_layer].uv = target_uv + + def __smooth(self, loop_seqs, uv_layer): + if self.transmission: + self.__smooth_w_transmission(loop_seqs, uv_layer) + else: + self.__smooth_wo_transmission(loop_seqs, uv_layer) def execute(self, context): - return self.__impl.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() + uv_layer = bm.loops.layers.uv.verify() + + # loop_seqs[horizontal][vertical][loop] + loop_seqs, error = common.get_loop_sequences(bm, uv_layer) + if not loop_seqs: + self.report({'WARNING'}, error) + return {'CANCELLED'} + + # smooth + self.__smooth(loop_seqs, uv_layer) + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} diff --git a/uv_magic_uv/op/texture_lock.py b/uv_magic_uv/op/texture_lock.py index b1b437531258ad43d169332bd3f058e10c6e38c3..f43a6fa9c15b16348d8c3472f34b369b625333e5 100644 --- a/uv_magic_uv/op/texture_lock.py +++ b/uv_magic_uv/op/texture_lock.py @@ -23,12 +23,189 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" +import math +from math import atan2, cos, sqrt, sin, fabs + import bpy from bpy.props import 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 texture_lock_impl as impl +from ..utils import compatibility as compat + + +def _get_vco(verts_orig, loop): + """ + Get vertex original coordinate from loop + """ + for vo in verts_orig: + if vo["vidx"] == loop.vert.index and vo["moved"] is False: + return vo["vco"] + return loop.vert.co + + +def _get_link_loops(vert): + """ + Get loop linked to vertex + """ + link_loops = [] + for f in vert.link_faces: + adj_loops = [] + for loop in f.loops: + # self loop + if loop.vert == vert: + l = loop + # linked loop + else: + for e in loop.vert.link_edges: + if e.other_vert(loop.vert) == vert: + adj_loops.append(loop) + if len(adj_loops) < 2: + return None + + link_loops.append({"l": l, "l0": adj_loops[0], "l1": adj_loops[1]}) + return link_loops + + +def _get_ini_geom(link_loop, uv_layer, verts_orig, v_orig): + """ + Get initial geometory + (Get interior angle of face in vertex/UV space) + """ + u = link_loop["l"][uv_layer].uv + v0 = _get_vco(verts_orig, link_loop["l0"]) + u0 = link_loop["l0"][uv_layer].uv + v1 = _get_vco(verts_orig, link_loop["l1"]) + u1 = link_loop["l1"][uv_layer].uv + + # get interior angle of face in vertex space + v0v1 = v1 - v0 + v0v = v_orig["vco"] - v0 + v1v = v_orig["vco"] - v1 + theta0 = v0v1.angle(v0v) + theta1 = v0v1.angle(-v1v) + if (theta0 + theta1) > math.pi: + theta0 = v0v1.angle(-v0v) + theta1 = v0v1.angle(v1v) + + # get interior angle of face in UV space + u0u1 = u1 - u0 + u0u = u - u0 + u1u = u - u1 + phi0 = u0u1.angle(u0u) + phi1 = u0u1.angle(-u1u) + if (phi0 + phi1) > math.pi: + phi0 = u0u1.angle(-u0u) + phi1 = u0u1.angle(u1u) + + # get direction of linked UV coordinate + # this will be used to judge whether angle is more or less than 180 degree + dir0 = u0u1.cross(u0u) > 0 + dir1 = u0u1.cross(u1u) > 0 + + return { + "theta0": theta0, + "theta1": theta1, + "phi0": phi0, + "phi1": phi1, + "dir0": dir0, + "dir1": dir1} + + +def _get_target_uv(link_loop, uv_layer, verts_orig, v, ini_geom): + """ + Get target UV coordinate + """ + v0 = _get_vco(verts_orig, link_loop["l0"]) + lo0 = link_loop["l0"] + v1 = _get_vco(verts_orig, link_loop["l1"]) + lo1 = link_loop["l1"] + + # get interior angle of face in vertex space + v0v1 = v1 - v0 + v0v = v.co - v0 + v1v = v.co - v1 + theta0 = v0v1.angle(v0v) + theta1 = v0v1.angle(-v1v) + if (theta0 + theta1) > math.pi: + theta0 = v0v1.angle(-v0v) + theta1 = v0v1.angle(v1v) + + # calculate target interior angle in UV space + phi0 = theta0 * ini_geom["phi0"] / ini_geom["theta0"] + phi1 = theta1 * ini_geom["phi1"] / ini_geom["theta1"] + + uv0 = lo0[uv_layer].uv + uv1 = lo1[uv_layer].uv + + # calculate target vertex coordinate from target interior angle + tuv0, tuv1 = _calc_tri_vert(uv0, uv1, phi0, phi1) + + # target UV coordinate depends on direction, so judge using direction of + # linked UV coordinate + u0u1 = uv1 - uv0 + u0u = tuv0 - uv0 + u1u = tuv0 - uv1 + dir0 = u0u1.cross(u0u) > 0 + dir1 = u0u1.cross(u1u) > 0 + if (ini_geom["dir0"] != dir0) or (ini_geom["dir1"] != dir1): + return tuv1 + + return tuv0 + + +def _calc_tri_vert(v0, v1, angle0, angle1): + """ + Calculate rest coordinate from other coordinates and angle of end + """ + angle = math.pi - angle0 - angle1 + + alpha = atan2(v1.y - v0.y, v1.x - v0.x) + d = (v1.x - v0.x) / cos(alpha) + a = d * sin(angle0) / sin(angle) + b = d * sin(angle1) / sin(angle) + s = (a + b + d) / 2.0 + if fabs(d) < 0.0000001: + xd = 0 + yd = 0 + else: + r = s * (s - a) * (s - b) * (s - d) + if r < 0: + xd = 0 + yd = 0 + else: + xd = (b * b - a * a + d * d) / (2 * d) + yd = 2 * sqrt(r) / d + x1 = xd * cos(alpha) - yd * sin(alpha) + v0.x + y1 = xd * sin(alpha) + yd * cos(alpha) + v0.y + x2 = xd * cos(alpha) + yd * sin(alpha) + v0.x + y2 = xd * sin(alpha) - yd * cos(alpha) + v0.y + + return Vector((x1, y1)), Vector((x2, y2)) + + +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() @@ -88,22 +265,43 @@ class MUV_OT_TextureLock_Lock(bpy.types.Operator): bl_description = "Lock Texture" bl_options = {'REGISTER', 'UNDO'} - def __init__(self): - self.__impl = impl.LockImpl() - @classmethod def poll(cls, context): - return impl.LockImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) @classmethod def is_ready(cls, context): - return impl.LockImpl.is_ready(context) + sc = context.scene + props = sc.muv_props.texture_lock + if props.verts_orig: + return True + return False def execute(self, context): - return self.__impl.execute(self, context) + props = context.scene.muv_props.texture_lock + obj = bpy.context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + if not bm.loops.layers.uv: + self.report({'WARNING'}, "Object must have more than one UV map") + return {'CANCELLED'} + + props.verts_orig = [ + {"vidx": v.index, "vco": v.co.copy(), "moved": False} + for v in bm.verts if v.select] + + return {'FINISHED'} @BlClassRegistry() +@compat.make_annotations class MUV_OT_TextureLock_Unlock(bpy.types.Operator): """ Operation class: Unlock Texture @@ -114,20 +312,78 @@ class MUV_OT_TextureLock_Unlock(bpy.types.Operator): bl_description = "Unlock Texture" bl_options = {'REGISTER', 'UNDO'} - connect: BoolProperty( + connect = BoolProperty( name="Connect UV", default=True ) - def __init__(self): - self.__impl = impl.UnlockImpl() - @classmethod def poll(cls, context): - return impl.UnlockImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + sc = context.scene + props = sc.muv_props.texture_lock + if not props.verts_orig: + return False + 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): - return self.__impl.execute(self, context) + sc = context.scene + props = sc.muv_props.texture_lock + obj = bpy.context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + 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() + + verts = [v.index for v in bm.verts if v.select] + verts_orig = props.verts_orig + + # move UV followed by vertex coordinate + for vidx, v_orig in zip(verts, verts_orig): + if vidx != v_orig["vidx"]: + self.report({'ERROR'}, "Internal Error") + return {"CANCELLED"} + + v = bm.verts[vidx] + link_loops = _get_link_loops(v) + + result = [] + + for ll in link_loops: + ini_geom = _get_ini_geom(ll, uv_layer, verts_orig, v_orig) + target_uv = _get_target_uv( + ll, uv_layer, verts_orig, v, ini_geom) + result.append({"l": ll["l"], "uv": target_uv}) + + # connect other face's UV + if self.connect: + ave = Vector((0.0, 0.0)) + for r in result: + ave = ave + r["uv"] + ave = ave / len(result) + for r in result: + r["l"][uv_layer].uv = ave + else: + for r in result: + r["l"][uv_layer].uv = r["uv"] + v_orig["moved"] = True + bmesh.update_edit_mesh(obj.data) + + props.verts_orig = None + + return {'FINISHED'} @BlClassRegistry() @@ -140,19 +396,142 @@ class MUV_OT_TextureLock_Intr(bpy.types.Operator): bl_label = "Texture Lock (Interactive mode)" bl_description = "Internal operation for Texture Lock (Interactive mode)" - def __init__(self): - self.__impl = impl.IntrImpl() + __timer = None @classmethod def poll(cls, context): - return impl.IntrImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return False + return _is_valid_context(context) + + @classmethod + def is_running(cls, _): + return 1 if cls.__timer else 0 + + @classmethod + def handle_add(cls, ops_obj, context): + if cls.__timer is None: + cls.__timer = context.window_manager.event_timer_add( + 0.10, window=context.window) + context.window_manager.modal_handler_add(ops_obj) @classmethod - def is_running(cls, context): - return impl.IntrImpl.is_running(context) + def handle_remove(cls, context): + if cls.__timer is not None: + context.window_manager.event_timer_remove(cls.__timer) + cls.__timer = None + + def __init__(self): + self.__intr_verts_orig = [] + self.__intr_verts = [] + + def __sel_verts_changed(self, context): + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + prev = set(self.__intr_verts) + now = set([v.index for v in bm.verts if v.select]) + + return prev != now + + def __reinit_verts(self, context): + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + self.__intr_verts_orig = [ + {"vidx": v.index, "vco": v.co.copy(), "moved": False} + for v in bm.verts if v.select] + self.__intr_verts = [v.index for v in bm.verts if v.select] + + def __update_uv(self, context): + """ + Update UV when vertex coordinates are changed + """ + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + if not bm.loops.layers.uv: + self.report({'WARNING'}, "Object must have more than one UV map") + return + uv_layer = bm.loops.layers.uv.verify() + + verts = [v.index for v in bm.verts if v.select] + verts_orig = self.__intr_verts_orig + + for vidx, v_orig in zip(verts, verts_orig): + if vidx != v_orig["vidx"]: + self.report({'ERROR'}, "Internal Error") + return + + v = bm.verts[vidx] + link_loops = _get_link_loops(v) + + result = [] + for ll in link_loops: + ini_geom = _get_ini_geom(ll, uv_layer, verts_orig, v_orig) + target_uv = _get_target_uv( + ll, uv_layer, verts_orig, v, ini_geom) + result.append({"l": ll["l"], "uv": target_uv}) + + # UV connect option is always true, because it raises + # unexpected behavior + ave = Vector((0.0, 0.0)) + for r in result: + ave = ave + r["uv"] + ave = ave / len(result) + for r in result: + r["l"][uv_layer].uv = ave + v_orig["moved"] = True + bmesh.update_edit_mesh(obj.data) + + common.redraw_all_areas() + self.__intr_verts_orig = [ + {"vidx": v.index, "vco": v.co.copy(), "moved": False} + for v in bm.verts if v.select] def modal(self, context, event): - return self.__impl.modal(self, context, event) + if not _is_valid_context(context): + MUV_OT_TextureLock_Intr.handle_remove(context) + return {'FINISHED'} + + if not MUV_OT_TextureLock_Intr.is_running(context): + return {'FINISHED'} + + if context.area: + context.area.tag_redraw() + + if event.type == 'TIMER': + if self.__sel_verts_changed(context): + self.__reinit_verts(context) + else: + self.__update_uv(context) + + return {'PASS_THROUGH'} + + def invoke(self, context, _): + if not _is_valid_context(context): + return {'CANCELLED'} + + if not MUV_OT_TextureLock_Intr.is_running(context): + MUV_OT_TextureLock_Intr.handle_add(self, context) + return {'RUNNING_MODAL'} + else: + MUV_OT_TextureLock_Intr.handle_remove(context) + + if context.area: + context.area.tag_redraw() - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) + return {'FINISHED'} diff --git a/uv_magic_uv/op/texture_projection.py b/uv_magic_uv/op/texture_projection.py index f6a3a89f0679d5badce13953ab4cbb0db5c0594c..cb7c25bb125ca2d32d00f06b780d1c72fd3caeda 100644 --- a/uv_magic_uv/op/texture_projection.py +++ b/uv_magic_uv/op/texture_projection.py @@ -23,8 +23,9 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" +from collections import namedtuple + import bpy -import bgl import bmesh from bpy_extras import view3d_utils from bpy.props import ( @@ -32,17 +33,118 @@ from bpy.props import ( EnumProperty, FloatProperty, ) +import mathutils from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import texture_projection_impl as impl +from ..utils import compatibility as compat + +if compat.check_version(2, 80, 0) >= 0: + from ..lib import bglx as bgl +else: + import bgl + + +_Rect = namedtuple('Rect', 'x0 y0 x1 y1') +_Rect2 = namedtuple('Rect2', 'x y width height') + + +def get_loaded_texture_name(_, __): + items = [(key, key, "") for key in bpy.data.images.keys()] + items.append(("None", "None", "")) + return items + + +def get_canvas(context, magnitude): + """ + Get canvas to be renderred texture + """ + sc = context.scene + prefs = compat.get_user_preferences(context).addons["uv_magic_uv"].preferences + + region_w = context.region.width + region_h = context.region.height + canvas_w = region_w - prefs.texture_projection_canvas_padding[0] * 2.0 + canvas_h = region_h - prefs.texture_projection_canvas_padding[1] * 2.0 + + img = bpy.data.images[sc.muv_texture_projection_tex_image] + tex_w = img.size[0] + tex_h = img.size[1] + + center_x = region_w * 0.5 + center_y = region_h * 0.5 + + if sc.muv_texture_projection_adjust_window: + ratio_x = canvas_w / tex_w + ratio_y = canvas_h / tex_h + if sc.muv_texture_projection_apply_tex_aspect: + ratio = ratio_y if ratio_x > ratio_y else ratio_x + len_x = ratio * tex_w + len_y = ratio * tex_h + else: + len_x = canvas_w + len_y = canvas_h + else: + if sc.muv_texture_projection_apply_tex_aspect: + len_x = tex_w * magnitude + len_y = tex_h * magnitude + else: + len_x = region_w * magnitude + len_y = region_h * magnitude -from ..lib import bglx + x0 = int(center_x - len_x * 0.5) + y0 = int(center_y - len_y * 0.5) + x1 = int(center_x + len_x * 0.5) + y1 = int(center_y + len_y * 0.5) + + return _Rect(x0, y0, x1, y1) + + +def rect_to_rect2(rect): + """ + Convert Rect1 to Rect2 + """ + + return _Rect2(rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0) + + +def region_to_canvas(rg_vec, canvas): + """ + Convert screen region to canvas + """ + + cv_rect = rect_to_rect2(canvas) + cv_vec = mathutils.Vector() + cv_vec.x = (rg_vec.x - cv_rect.x) / cv_rect.width + cv_vec.y = (rg_vec.y - cv_rect.y) / cv_rect.height + + return cv_vec + + +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: +class _Properties: idname = "texture_projection" @classmethod @@ -79,7 +181,7 @@ class Properties: scene.muv_texture_projection_tex_image = EnumProperty( name="Image", description="Texture Image", - items=impl.get_loaded_texture_name + items=get_loaded_texture_name ) scene.muv_texture_projection_tex_transparency = FloatProperty( name="Transparency", @@ -133,7 +235,7 @@ class MUV_OT_TextureProjection(bpy.types.Operator): # we can not get area/space/region from console if common.is_console_mode(): return False - return impl.is_valid_context(context) + return is_valid_context(context) @classmethod def is_running(cls, _): @@ -166,7 +268,7 @@ class MUV_OT_TextureProjection(bpy.types.Operator): img = bpy.data.images[sc.muv_texture_projection_tex_image] # setup rendering region - rect = impl.get_canvas(context, sc.muv_texture_projection_tex_magnitude) + rect = get_canvas(context, sc.muv_texture_projection_tex_magnitude) positions = [ [rect.x0, rect.y0], [rect.x0, rect.y1], @@ -181,21 +283,35 @@ class MUV_OT_TextureProjection(bpy.types.Operator): ] # OpenGL configuration - bgl.glEnable(bgl.GL_BLEND) - bgl.glEnable(bgl.GL_TEXTURE_2D) - bgl.glActiveTexture(bgl.GL_TEXTURE0) - if img.bindcode: - bind = img.bindcode - bgl.glBindTexture(bgl.GL_TEXTURE_2D, bind) + if compat.check_version(2, 80, 0) >= 0: + bgl.glEnable(bgl.GL_BLEND) + bgl.glEnable(bgl.GL_TEXTURE_2D) + bgl.glActiveTexture(bgl.GL_TEXTURE0) + if img.bindcode: + bind = img.bindcode + bgl.glBindTexture(bgl.GL_TEXTURE_2D, bind) + else: + bgl.glEnable(bgl.GL_BLEND) + bgl.glEnable(bgl.GL_TEXTURE_2D) + if img.bindcode: + bind = img.bindcode[0] + bgl.glBindTexture(bgl.GL_TEXTURE_2D, bind) + bgl.glTexParameteri( + bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR) + bgl.glTexParameteri( + bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR) + bgl.glTexEnvi( + bgl.GL_TEXTURE_ENV, bgl.GL_TEXTURE_ENV_MODE, + bgl.GL_MODULATE) # render texture - bglx.glBegin(bglx.GL_QUADS) - bglx.glColor4f(1.0, 1.0, 1.0, + bgl.glBegin(bgl.GL_QUADS) + bgl.glColor4f(1.0, 1.0, 1.0, sc.muv_texture_projection_tex_transparency) for (v1, v2), (u, v) in zip(positions, tex_coords): - bglx.glTexCoord2f(u, v) - bglx.glVertex2f(v1, v2) - bglx.glEnd() + bgl.glTexCoord2f(u, v) + bgl.glVertex2f(v1, v2) + bgl.glEnd() def invoke(self, context, _): if not MUV_OT_TextureProjection.is_running(context): @@ -227,7 +343,7 @@ class MUV_OT_TextureProjection_Project(bpy.types.Operator): return True if not MUV_OT_TextureProjection.is_running(context): return False - return impl.is_valid_context(context) + return is_valid_context(context) def execute(self, context): sc = context.scene @@ -255,6 +371,9 @@ class MUV_OT_TextureProjection_Project(bpy.types.Operator): return {'CANCELLED'} uv_layer = bm.loops.layers.uv.verify() + if compat.check_version(2, 80, 0) < 0: + tex_layer = bm.faces.layers.tex.verify() + sel_faces = [f for f in bm.faces if f.select] # transform 3d space to screen region @@ -262,26 +381,30 @@ class MUV_OT_TextureProjection_Project(bpy.types.Operator): view3d_utils.location_3d_to_region_2d( region, space.region_3d, - world_mat @ l.vert.co) + compat.matmul(world_mat, l.vert.co)) for f in sel_faces for l in f.loops ] # transform screen region to canvas v_canvas = [ - impl.region_to_canvas( + region_to_canvas( v, - impl.get_canvas(bpy.context, - sc.muv_texture_projection_tex_magnitude) + get_canvas(bpy.context, + sc.muv_texture_projection_tex_magnitude) ) for v in v_screen ] - # set texture - nodes = common.find_texture_nodes(obj) - nodes[0].image = bpy.data.images[sc.muv_texture_projection_tex_image] + if compat.check_version(2, 80, 0) >= 0: + # set texture + nodes = common.find_texture_nodes(obj) + nodes[0].image = bpy.data.images[sc.muv_texture_projection_tex_image] # project texture to object i = 0 for f in sel_faces: + if compat.check_version(2, 80, 0) < 0: + f[tex_layer].image = \ + bpy.data.images[sc.muv_texture_projection_tex_image] for l in f.loops: l[uv_layer].uv = v_canvas[i].to_2d() i = i + 1 diff --git a/uv_magic_uv/op/texture_wrap.py b/uv_magic_uv/op/texture_wrap.py index 70fb66049867fb5fde8946bf0467e0a3266c3017..c78ffed9ced4644ed52d2d5f798e7444fe6fe64b 100644 --- a/uv_magic_uv/op/texture_wrap.py +++ b/uv_magic_uv/op/texture_wrap.py @@ -27,10 +27,32 @@ import bpy from bpy.props import ( BoolProperty, ) +import bmesh +from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import texture_wrap_impl as impl + + +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() @@ -80,15 +102,33 @@ class MUV_OT_TextureWrap_Refer(bpy.types.Operator): bl_description = "Refer UV" bl_options = {'REGISTER', 'UNDO'} - def __init__(self): - self.__impl = impl.ReferImpl() - @classmethod def poll(cls, context): - return impl.ReferImpl.poll(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): - return self.__impl.execute(self, context) + props = context.scene.muv_props.texture_wrap + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + 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'} + + sel_faces = [f for f in bm.faces if f.select] + if len(sel_faces) != 1: + self.report({'WARNING'}, "Must select only one face") + return {'CANCELLED'} + + props.ref_face_index = sel_faces[0].index + props.ref_obj = obj + + return {'FINISHED'} @BlClassRegistry() @@ -102,12 +142,153 @@ class MUV_OT_TextureWrap_Set(bpy.types.Operator): bl_description = "Set UV" bl_options = {'REGISTER', 'UNDO'} - def __init__(self): - self.__impl = impl.SetImpl() - @classmethod def poll(cls, context): - return impl.SetImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + sc = context.scene + props = sc.muv_props.texture_wrap + if not props.ref_obj: + return False + return _is_valid_context(context) def execute(self, context): - return self.__impl.execute(self, context) + sc = context.scene + props = sc.muv_props.texture_wrap + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + 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() + + if sc.muv_texture_wrap_selseq: + sel_faces = [] + for hist in bm.select_history: + if isinstance(hist, bmesh.types.BMFace) and hist.select: + sel_faces.append(hist) + if not sel_faces: + self.report({'WARNING'}, "Must select more than one face") + return {'CANCELLED'} + else: + sel_faces = [f for f in bm.faces if f.select] + if len(sel_faces) != 1: + self.report({'WARNING'}, "Must select only one face") + return {'CANCELLED'} + + ref_face_index = props.ref_face_index + for face in sel_faces: + tgt_face_index = face.index + if ref_face_index == tgt_face_index: + self.report({'WARNING'}, "Must select different face") + return {'CANCELLED'} + + if props.ref_obj != obj: + self.report({'WARNING'}, "Object must be same") + return {'CANCELLED'} + + ref_face = bm.faces[ref_face_index] + tgt_face = bm.faces[tgt_face_index] + + # get common vertices info + common_verts = [] + for sl in ref_face.loops: + for dl in tgt_face.loops: + if sl.vert == dl.vert: + info = {"vert": sl.vert, "ref_loop": sl, + "tgt_loop": dl} + common_verts.append(info) + break + + if len(common_verts) != 2: + self.report({'WARNING'}, + "2 vertices must be shared among faces") + return {'CANCELLED'} + + # get reference other vertices info + ref_other_verts = [] + for sl in ref_face.loops: + for ci in common_verts: + if sl.vert == ci["vert"]: + break + else: + info = {"vert": sl.vert, "loop": sl} + ref_other_verts.append(info) + + if not ref_other_verts: + self.report({'WARNING'}, "More than 1 vertex must be unshared") + return {'CANCELLED'} + + # get reference info + ref_info = {} + cv0 = common_verts[0]["vert"].co + cv1 = common_verts[1]["vert"].co + cuv0 = common_verts[0]["ref_loop"][uv_layer].uv + cuv1 = common_verts[1]["ref_loop"][uv_layer].uv + ov0 = ref_other_verts[0]["vert"].co + ouv0 = ref_other_verts[0]["loop"][uv_layer].uv + ref_info["vert_vdiff"] = cv1 - cv0 + ref_info["uv_vdiff"] = cuv1 - cuv0 + ref_info["vert_hdiff"], _ = common.diff_point_to_segment( + cv0, cv1, ov0) + ref_info["uv_hdiff"], _ = common.diff_point_to_segment( + cuv0, cuv1, ouv0) + + # get target other vertices info + tgt_other_verts = [] + for dl in tgt_face.loops: + for ci in common_verts: + if dl.vert == ci["vert"]: + break + else: + info = {"vert": dl.vert, "loop": dl} + tgt_other_verts.append(info) + + if not tgt_other_verts: + self.report({'WARNING'}, "More than 1 vertex must be unshared") + return {'CANCELLED'} + + # get target info + for info in tgt_other_verts: + cv0 = common_verts[0]["vert"].co + cv1 = common_verts[1]["vert"].co + cuv0 = common_verts[0]["ref_loop"][uv_layer].uv + ov = info["vert"].co + info["vert_hdiff"], x = common.diff_point_to_segment( + cv0, cv1, ov) + info["vert_vdiff"] = x - common_verts[0]["vert"].co + + # calclulate factor + fact_h = -info["vert_hdiff"].length / \ + ref_info["vert_hdiff"].length + fact_v = info["vert_vdiff"].length / \ + ref_info["vert_vdiff"].length + duv_h = ref_info["uv_hdiff"] * fact_h + duv_v = ref_info["uv_vdiff"] * fact_v + + # get target UV + info["target_uv"] = cuv0 + duv_h + duv_v + + # apply to common UVs + for info in common_verts: + info["tgt_loop"][uv_layer].uv = \ + info["ref_loop"][uv_layer].uv.copy() + # apply to other UVs + for info in tgt_other_verts: + info["loop"][uv_layer].uv = info["target_uv"] + + common.debug_print("===== Target Other Vertices =====") + common.debug_print(tgt_other_verts) + + bmesh.update_edit_mesh(obj.data) + + ref_face_index = tgt_face_index + + if sc.muv_texture_wrap_set_and_refer: + props.ref_face_index = tgt_face_index + + return {'FINISHED'} diff --git a/uv_magic_uv/op/transfer_uv.py b/uv_magic_uv/op/transfer_uv.py index db05b34379ef8c6da680abc5292b4515802c23e5..25d430b9bb24863ff033e732eb2e60addfa6d15a 100644 --- a/uv_magic_uv/op/transfer_uv.py +++ b/uv_magic_uv/op/transfer_uv.py @@ -23,25 +23,307 @@ __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 +from ..utils import compatibility as compat + + +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 -__all__ = [ - 'Properties', - 'MUV_OT_TransferUV_CopyUV', - 'MUV_OT_TransferUV_PasteUV', -] + 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 @PropertyClassRegistry() -class Properties: +class _Properties: idname = "transfer_uv" @classmethod @@ -91,19 +373,20 @@ class MUV_OT_TransferUV_CopyUV(bpy.types.Operator): # we can not get area/space/region from console if common.is_console_mode(): return True - return impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.transfer_uv - active_obj = context.active_object + active_obj = compat.get_active_object(context) bm = bmesh.from_edit_mesh(active_obj.data) - bm.faces.ensure_lookup_table() + if compat.check_version(2, 73, 0) >= 0: + bm.faces.ensure_lookup_table() - uv_layer = impl.get_uv_layer(self, bm) + uv_layer = _get_uv_layer(self, bm) if uv_layer is None: return {'CANCELLED'} - faces = impl.get_selected_src_faces(self, bm, uv_layer) + faces = _get_selected_src_faces(self, bm, uv_layer) if faces is None: return {'CANCELLED'} props.topology_copied = faces @@ -114,6 +397,7 @@ class MUV_OT_TransferUV_CopyUV(bpy.types.Operator): @BlClassRegistry() +@compat.make_annotations class MUV_OT_TransferUV_PasteUV(bpy.types.Operator): """ Operation class: Transfer UV paste @@ -125,12 +409,12 @@ class MUV_OT_TransferUV_PasteUV(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 @@ -145,24 +429,29 @@ class MUV_OT_TransferUV_PasteUV(bpy.types.Operator): props = sc.muv_props.transfer_uv if not props.topology_copied: return False - return impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): props = context.scene.muv_props.transfer_uv - active_obj = context.active_object + active_obj = compat.get_active_object(context) bm = bmesh.from_edit_mesh(active_obj.data) - bm.faces.ensure_lookup_table() + if compat.check_version(2, 73, 0) >= 0: + bm.faces.ensure_lookup_table() # get UV layer - uv_layer = impl.get_uv_layer(self, bm) + uv_layer = _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) + ret = _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 compat.check_version(2, 80, 0) < 0: + 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/op/unwrap_constraint.py index df16f7834920c72306b107253f7baa0167049f36..897271601d63a3f09c7fcb6649aa2f1ac633dbcd 100644 --- a/uv_magic_uv/op/unwrap_constraint.py +++ b/uv_magic_uv/op/unwrap_constraint.py @@ -27,10 +27,33 @@ from bpy.props import ( EnumProperty, FloatProperty, ) +import bmesh +from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import unwrap_constraint_impl as impl +from ..utils import compatibility as compat + + +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() @@ -63,6 +86,7 @@ class _Properties: @BlClassRegistry(legacy=True) +@compat.make_annotations class MUV_OT_UnwrapConstraint(bpy.types.Operator): """ Operation class: Unwrap with constrain UV coordinate @@ -74,7 +98,7 @@ class MUV_OT_UnwrapConstraint(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO'} # property for original unwrap - method: EnumProperty( + method = EnumProperty( name="Method", description="Unwrapping method", items=[ @@ -82,20 +106,20 @@ class MUV_OT_UnwrapConstraint(bpy.types.Operator): ('CONFORMAL', 'Conformal', 'Conformal') ], default='ANGLE_BASED') - fill_holes: BoolProperty( + fill_holes = BoolProperty( name="Fill Holes", description="Virtual fill holes in meshes before unwrapping", default=True) - correct_aspect: BoolProperty( + correct_aspect = BoolProperty( name="Correct Aspect", description="Map UVs taking image aspect ratio into account", default=True) - use_subsurf_data: BoolProperty( + use_subsurf_data = BoolProperty( name="Use Subsurf Modifier", description="""Map UVs taking vertex position after subsurf into account""", default=False) - margin: FloatProperty( + margin = FloatProperty( name="Margin", description="Space between islands", max=1.0, @@ -103,23 +127,60 @@ class MUV_OT_UnwrapConstraint(bpy.types.Operator): default=0.001) # property for this operation - u_const: BoolProperty( + u_const = BoolProperty( name="U-Constraint", description="Keep UV U-axis coordinate", default=False ) - v_const: BoolProperty( + v_const = BoolProperty( name="V-Constraint", description="Keep UV V-axis coordinate", default=False ) - def __init__(self): - self.__impl = impl.UnwrapConstraintImpl() - @classmethod def poll(cls, context): - return impl.UnwrapConstraintImpl.poll(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): - return self.__impl.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() + + # bpy.ops.uv.unwrap() makes one UV map at least + 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() + + # get original UV coordinate + faces = [f for f in bm.faces if f.select] + uv_list = [] + for f in faces: + uvs = [l[uv_layer].uv.copy() for l in f.loops] + uv_list.append(uvs) + + # unwrap + bpy.ops.uv.unwrap( + method=self.method, + fill_holes=self.fill_holes, + correct_aspect=self.correct_aspect, + use_subsurf_data=self.use_subsurf_data, + margin=self.margin) + + # when U/V-Constraint is checked, revert original coordinate + for f, uvs in zip(faces, uv_list): + for l, uv in zip(f.loops, uvs): + if self.u_const: + l[uv_layer].uv.x = uv.x + if self.v_const: + l[uv_layer].uv.y = uv.y + + # update mesh + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} diff --git a/uv_magic_uv/op/uv_bounding_box.py b/uv_magic_uv/op/uv_bounding_box.py index 82cdea45a8361e97ce3dc2499f4251a218af161b..4839934bb783fbe9b6bc20941ba11d8cf39b07cb 100644 --- a/uv_magic_uv/op/uv_bounding_box.py +++ b/uv_magic_uv/op/uv_bounding_box.py @@ -27,7 +27,6 @@ from enum import IntEnum import math import bpy -import bgl import mathutils import bmesh from bpy.props import BoolProperty, EnumProperty @@ -35,14 +34,40 @@ from bpy.props import BoolProperty, EnumProperty from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import uv_bounding_box_impl as impl +from ..utils import compatibility as compat -from ..lib import bglx +if compat.check_version(2, 80, 0) >= 0: + from ..lib import bglx as bgl +else: + import bgl MAX_VALUE = 100000.0 +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 = "uv_bounding_box" @@ -166,7 +191,7 @@ class RotationCommand(CommandBase): mti = mathutils.Matrix.Translation((-self.__cx, -self.__cy, 0.0)) mr = mathutils.Matrix.Rotation(angle, 4, 'Z') mt = mathutils.Matrix.Translation((self.__cx, self.__cy, 0.0)) - return mt @ mr @ mti + return compat.matmul(compat.matmul(mt, mr), mti) def set(self, x, y): self.__x = x @@ -191,7 +216,7 @@ class ScalingCommand(CommandBase): self.__dir_y = dir_y # direction of scaling y self.__mat = mat # initial origin of scaling = M(to original transform) * (ox, oy) - iov = mat @ mathutils.Vector((ox, oy, 0.0)) + iov = compat.matmul(mat, mathutils.Vector((ox, oy, 0.0))) self.__iox = iov.x # initial origin of scaling X self.__ioy = iov.y # initial origin of scaling y @@ -205,11 +230,11 @@ class ScalingCommand(CommandBase): mtoi = mathutils.Matrix.Translation((-self.__iox, -self.__ioy, 0.0)) mto = mathutils.Matrix.Translation((self.__iox, self.__ioy, 0.0)) # every point must be transformed to origin - t = m @ mathutils.Vector((self.__ix, self.__iy, 0.0)) + t = compat.matmul(m, mathutils.Vector((self.__ix, self.__iy, 0.0))) tix, tiy = t.x, t.y - t = m @ mathutils.Vector((self.__ox, self.__oy, 0.0)) + t = compat.matmul(m, mathutils.Vector((self.__ox, self.__oy, 0.0))) tox, toy = t.x, t.y - t = m @ mathutils.Vector((self.__x, self.__y, 0.0)) + t = compat.matmul(m, mathutils.Vector((self.__x, self.__y, 0.0))) tx, ty = t.x, t.y ms = mathutils.Matrix() ms.identity() @@ -217,7 +242,8 @@ class ScalingCommand(CommandBase): ms[0][0] = (tx - tox) * self.__dir_x / (tix - tox) if self.__dir_y == 1: ms[1][1] = (ty - toy) * self.__dir_y / (tiy - toy) - return mi @ mto @ ms @ mtoi @ m + return compat.matmul(compat.matmul(compat.matmul( + compat.matmul(mi, mto), ms), mtoi), m) def set(self, x, y): self.__x = x @@ -240,7 +266,7 @@ class UniformScalingCommand(CommandBase): self.__oy = oy # origin of scaling y self.__mat = mat # initial origin of scaling = M(to original transform) * (ox, oy) - iov = mat @ mathutils.Vector((ox, oy, 0.0)) + iov = compat.matmul(mat, mathutils.Vector((ox, oy, 0.0))) self.__iox = iov.x # initial origin of scaling x self.__ioy = iov.y # initial origin of scaling y self.__dir_x = 1 @@ -256,11 +282,11 @@ class UniformScalingCommand(CommandBase): mtoi = mathutils.Matrix.Translation((-self.__iox, -self.__ioy, 0.0)) mto = mathutils.Matrix.Translation((self.__iox, self.__ioy, 0.0)) # every point must be transformed to origin - t = m @ mathutils.Vector((self.__ix, self.__iy, 0.0)) + t = compat.matmul(m, mathutils.Vector((self.__ix, self.__iy, 0.0))) tix, tiy = t.x, t.y - t = m @ mathutils.Vector((self.__ox, self.__oy, 0.0)) + t = compat.matmul(m, mathutils.Vector((self.__ox, self.__oy, 0.0))) tox, toy = t.x, t.y - t = m @ mathutils.Vector((self.__x, self.__y, 0.0)) + t = compat.matmul(m, mathutils.Vector((self.__x, self.__y, 0.0))) tx, ty = t.x, t.y ms = mathutils.Matrix() ms.identity() @@ -281,7 +307,8 @@ class UniformScalingCommand(CommandBase): ms[0][0] = sr * self.__dir_x ms[1][1] = sr * self.__dir_y - return mi @ mto @ ms @ mtoi @ m + return compat.matmul(compat.matmul(compat.matmul( + compat.matmul(mi, mto), ms), mtoi), m) def set(self, x, y): self.__x = x @@ -305,7 +332,7 @@ class CommandExecuter: mat.identity() for i, cmd in enumerate(self.__cmd_list): if begin <= i and (end == -1 or i <= end): - mat = cmd.to_matrix() @ mat + mat = compat.matmul(cmd.to_matrix(), mat) return mat def undo_size(self): @@ -402,7 +429,7 @@ class StateNone(StateBase): """ Update state """ - prefs = context.user_preferences.addons["uv_magic_uv"].preferences + prefs = compat.get_user_preferences(context).addons["uv_magic_uv"].preferences cp_react_size = prefs.uv_bounding_box_cp_react_size is_uscaling = context.scene.muv_uv_bounding_box_uniform_scaling if (event.type == 'LEFTMOUSE') and (event.value == 'PRESS'): @@ -602,7 +629,7 @@ class MUV_OT_UVBoundingBox(bpy.types.Operator): # we can not get area/space/region from console if common.is_console_mode(): return False - return impl.is_valid_context(context) + return _is_valid_context(context) @classmethod def is_running(cls, _): @@ -634,7 +661,7 @@ class MUV_OT_UVBoundingBox(bpy.types.Operator): """ Draw control point """ - prefs = context.user_preferences.addons["uv_magic_uv"].preferences + prefs = compat.get_user_preferences(context).addons["uv_magic_uv"].preferences cp_size = prefs.uv_bounding_box_cp_size offset = cp_size / 2 verts = [ @@ -644,11 +671,11 @@ class MUV_OT_UVBoundingBox(bpy.types.Operator): [pos.x + offset, pos.y - offset] ] bgl.glEnable(bgl.GL_BLEND) - bglx.glBegin(bglx.GL_QUADS) - bglx.glColor4f(1.0, 1.0, 1.0, 1.0) + bgl.glBegin(bgl.GL_QUADS) + bgl.glColor4f(1.0, 1.0, 1.0, 1.0) for (x, y) in verts: - bglx.glVertex2f(x, y) - bglx.glEnd() + bgl.glVertex2f(x, y) + bgl.glEnd() @classmethod def draw_bb(cls, _, context): @@ -660,7 +687,7 @@ class MUV_OT_UVBoundingBox(bpy.types.Operator): if not MUV_OT_UVBoundingBox.is_running(context): return - if not impl.is_valid_context(context): + if not _is_valid_context(context): return for cp in props.ctrl_points: @@ -747,7 +774,7 @@ class MUV_OT_UVBoundingBox(bpy.types.Operator): lidx = info[1] uv = info[2] v = mathutils.Vector((uv.x, uv.y, 0.0)) - av = trans_mat @ v + av = compat.matmul(trans_mat, v) bm.faces[fidx].loops[lidx][uv_layer].uv = mathutils.Vector( (av.x, av.y)) bmesh.update_edit_mesh(obj.data) @@ -756,7 +783,7 @@ class MUV_OT_UVBoundingBox(bpy.types.Operator): """ Update control point """ - return [trans_mat @ cp for cp in ctrl_points_ini] + return [compat.matmul(trans_mat, cp) for cp in ctrl_points_ini] def modal(self, context, event): props = context.scene.muv_props.uv_bounding_box @@ -765,7 +792,7 @@ class MUV_OT_UVBoundingBox(bpy.types.Operator): if not MUV_OT_UVBoundingBox.is_running(context): return {'FINISHED'} - if not impl.is_valid_context(context): + if not _is_valid_context(context): MUV_OT_UVBoundingBox.handle_remove(context) return {'FINISHED'} @@ -774,8 +801,8 @@ class MUV_OT_UVBoundingBox(bpy.types.Operator): 'UI', 'TOOLS', ] - if not common.mouse_on_area_legacy(event, 'IMAGE_EDITOR') or \ - common.mouse_on_regions_legacy(event, 'IMAGE_EDITOR', region_types): + if not common.mouse_on_area(event, 'IMAGE_EDITOR') or \ + common.mouse_on_regions(event, 'IMAGE_EDITOR', region_types): return {'PASS_THROUGH'} if event.type == 'TIMER': diff --git a/uv_magic_uv/op/uv_inspection.py b/uv_magic_uv/op/uv_inspection.py index 63d73fdfe2c736c42a4f7df1093ead12d2b23fdb..0978158d9cc23e63639b8e9e2247313c5be0aadb 100644 --- a/uv_magic_uv/op/uv_inspection.py +++ b/uv_magic_uv/op/uv_inspection.py @@ -24,15 +24,60 @@ __version__ = "5.2" __date__ = "17 Nov 2018" import bpy -import bgl from bpy.props import BoolProperty, EnumProperty +import bmesh from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import uv_inspection_impl as impl +from ..utils import compatibility as compat -from ..lib import bglx +if compat.check_version(2, 80, 0) >= 0: + from ..lib import bglx as bgl +else: + import bgl + + +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 + + +def _update_uvinsp_info(context): + sc = context.scene + props = sc.muv_props.uv_inspection + + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.faces.ensure_lookup_table() + uv_layer = bm.loops.layers.uv.verify() + + if context.tool_settings.use_uv_select_sync: + sel_faces = [f for f in bm.faces] + else: + sel_faces = [f for f in bm.faces if f.select] + props.overlapped_info = common.get_overlapped_uv_info( + bm, sel_faces, uv_layer, sc.muv_uv_inspection_show_mode) + props.flipped_info = common.get_flipped_uv_info(sel_faces, uv_layer) @PropertyClassRegistry() @@ -117,7 +162,7 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator): # we can not get area/space/region from console if common.is_console_mode(): return False - return impl.is_valid_context(context) + return _is_valid_context(context) @classmethod def is_running(cls, _): @@ -141,7 +186,7 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator): def draw(_, context): sc = context.scene props = sc.muv_props.uv_inspection - prefs = context.user_preferences.addons["uv_magic_uv"].preferences + prefs = compat.get_user_preferences(context).addons["uv_magic_uv"].preferences if not MUV_OT_UVInspection_Render.is_running(context): return @@ -155,20 +200,20 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator): for info in props.overlapped_info: if sc.muv_uv_inspection_show_mode == 'PART': for poly in info["polygons"]: - bglx.glBegin(bglx.GL_TRIANGLE_FAN) - bglx.glColor4f(color[0], color[1], color[2], color[3]) + bgl.glBegin(bgl.GL_TRIANGLE_FAN) + bgl.glColor4f(color[0], color[1], color[2], color[3]) for uv in poly: x, y = context.region.view2d.view_to_region( uv.x, uv.y) - bglx.glVertex2f(x, y) - bglx.glEnd() + bgl.glVertex2f(x, y) + bgl.glEnd() elif sc.muv_uv_inspection_show_mode == 'FACE': - bglx.glBegin(bglx.GL_TRIANGLE_FAN) - bglx.glColor4f(color[0], color[1], color[2], color[3]) + bgl.glBegin(bgl.GL_TRIANGLE_FAN) + bgl.glColor4f(color[0], color[1], color[2], color[3]) for uv in info["subject_uvs"]: x, y = context.region.view2d.view_to_region(uv.x, uv.y) - bglx.glVertex2f(x, y) - bglx.glEnd() + bgl.glVertex2f(x, y) + bgl.glEnd() # render flipped UV if sc.muv_uv_inspection_show_flipped: @@ -176,26 +221,26 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator): for info in props.flipped_info: if sc.muv_uv_inspection_show_mode == 'PART': for poly in info["polygons"]: - bglx.glBegin(bglx.GL_TRIANGLE_FAN) - bglx.glColor4f(color[0], color[1], color[2], color[3]) + bgl.glBegin(bgl.GL_TRIANGLE_FAN) + bgl.glColor4f(color[0], color[1], color[2], color[3]) for uv in poly: x, y = context.region.view2d.view_to_region( uv.x, uv.y) - bglx.glVertex2f(x, y) - bglx.glEnd() + bgl.glVertex2f(x, y) + bgl.glEnd() elif sc.muv_uv_inspection_show_mode == 'FACE': - bglx.glBegin(bglx.GL_TRIANGLE_FAN) - bglx.glColor4f(color[0], color[1], color[2], color[3]) + bgl.glBegin(bgl.GL_TRIANGLE_FAN) + bgl.glColor4f(color[0], color[1], color[2], color[3]) for uv in info["uvs"]: x, y = context.region.view2d.view_to_region(uv.x, uv.y) - bglx.glVertex2f(x, y) - bglx.glEnd() + bgl.glVertex2f(x, y) + bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) def invoke(self, context, _): if not MUV_OT_UVInspection_Render.is_running(context): - impl.update_uvinsp_info(context) + _update_uvinsp_info(context) MUV_OT_UVInspection_Render.handle_add(self, context) else: MUV_OT_UVInspection_Render.handle_remove() @@ -224,10 +269,10 @@ class MUV_OT_UVInspection_Update(bpy.types.Operator): return True if not MUV_OT_UVInspection_Render.is_running(context): return False - return impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): - impl.update_uvinsp_info(context) + _update_uvinsp_info(context) if context.area: context.area.tag_redraw() diff --git a/uv_magic_uv/op/uv_sculpt.py b/uv_magic_uv/op/uv_sculpt.py index cc1c057578e6759f7cb3889e252450991d2511db..7dc1b8436325ade14396c53a4680633a83592242 100644 --- a/uv_magic_uv/op/uv_sculpt.py +++ b/uv_magic_uv/op/uv_sculpt.py @@ -41,9 +41,46 @@ from bpy.props import ( from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import uv_sculpt_impl as impl +from ..utils import compatibility as compat -from ..lib import bglx + +if compat.check_version(2, 80, 0) >= 0: + from ..lib import bglx as bgl +else: + import bgl + + +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_strength(p, len_, factor): + f = factor + + if p > len_: + return 0.0 + + if p < 0.0: + return f + + return (len_ - p) * f / len_ @PropertyClassRegistry() @@ -150,7 +187,7 @@ class MUV_OT_UVSculpt(bpy.types.Operator): # we can not get area/space/region from console if common.is_console_mode(): return False - return impl.is_valid_context(context) + return _is_valid_context(context) @classmethod def is_running(cls, _): @@ -180,7 +217,7 @@ class MUV_OT_UVSculpt(bpy.types.Operator): @classmethod def draw_brush(cls, obj, context): sc = context.scene - prefs = context.user_preferences.addons["uv_magic_uv"].preferences + prefs = compat.get_user_preferences(context).addons["uv_magic_uv"].preferences num_segment = 180 theta = 2 * pi / num_segment @@ -188,19 +225,19 @@ class MUV_OT_UVSculpt(bpy.types.Operator): fact_r = cos(theta) color = prefs.uv_sculpt_brush_color - bglx.glBegin(bglx.GL_LINE_STRIP) - bglx.glColor4f(color[0], color[1], color[2], color[3]) + bgl.glBegin(bgl.GL_LINE_STRIP) + bgl.glColor4f(color[0], color[1], color[2], color[3]) x = sc.muv_uv_sculpt_radius * cos(0.0) y = sc.muv_uv_sculpt_radius * sin(0.0) for _ in range(num_segment): - bglx.glVertex2f(x + obj.current_mco.x, y + obj.current_mco.y) + bgl.glVertex2f(x + obj.current_mco.x, y + obj.current_mco.y) tx = -y ty = x x = x + tx * fact_t y = y + ty * fact_t x = x * fact_r y = y * fact_r - bglx.glEnd() + bgl.glEnd() def __init__(self): self.__loop_info = [] @@ -226,7 +263,8 @@ class MUV_OT_UVSculpt(bpy.types.Operator): continue for i, l in enumerate(f.loops): loc_2d = view3d_utils.location_3d_to_region_2d( - region, space.region_3d, world_mat @ l.vert.co) + region, space.region_3d, + compat.matmul(world_mat, l.vert.co)) diff = loc_2d - self.__initial_mco if diff.length < sc.muv_uv_sculpt_radius: info = { @@ -235,7 +273,7 @@ class MUV_OT_UVSculpt(bpy.types.Operator): "initial_vco": l.vert.co.copy(), "initial_vco_2d": loc_2d, "initial_uv": l[uv_layer].uv.copy(), - "strength": impl.get_strength( + "strength": _get_strength( diff.length, sc.muv_uv_sculpt_radius, sc.muv_uv_sculpt_strength) } @@ -263,7 +301,8 @@ class MUV_OT_UVSculpt(bpy.types.Operator): continue for i, l in enumerate(f.loops): loc_2d = view3d_utils.location_3d_to_region_2d( - region, space.region_3d, world_mat @ l.vert.co) + region, space.region_3d, + compat.matmul(world_mat, l.vert.co)) diff = loc_2d - self.__initial_mco if diff.length < sc.muv_uv_sculpt_radius: info = { @@ -272,7 +311,7 @@ class MUV_OT_UVSculpt(bpy.types.Operator): "initial_vco": l.vert.co.copy(), "initial_vco_2d": loc_2d, "initial_uv": l[uv_layer].uv.copy(), - "strength": impl.get_strength( + "strength": _get_strength( diff.length, sc.muv_uv_sculpt_radius, sc.muv_uv_sculpt_strength) } @@ -287,8 +326,8 @@ class MUV_OT_UVSculpt(bpy.types.Operator): mco) ray_tgt = ray_orig + ray_vec * 1000000.0 mwi = world_mat.inverted() - ray_orig_obj = mwi @ ray_orig - ray_tgt_obj = mwi @ ray_tgt + ray_orig_obj = compat.matmul(mwi, ray_orig) + ray_tgt_obj = compat.matmul(mwi, ray_tgt) ray_dir_obj = ray_tgt_obj - ray_orig_obj ray_dir_obj.normalize() tree = BVHTree.FromBMesh(bm) @@ -354,14 +393,15 @@ class MUV_OT_UVSculpt(bpy.types.Operator): continue for i, l in enumerate(f.loops): loc_2d = view3d_utils.location_3d_to_region_2d( - region, space.region_3d, world_mat @ l.vert.co) + region, space.region_3d, + compat.matmul(world_mat, l.vert.co)) diff = loc_2d - self.__initial_mco if diff.length >= sc.muv_uv_sculpt_radius: continue db = vert_db[l.vert] - strength = impl.get_strength(diff.length, - sc.muv_uv_sculpt_radius, - sc.muv_uv_sculpt_strength) + strength = _get_strength(diff.length, + sc.muv_uv_sculpt_radius, + sc.muv_uv_sculpt_strength) base = (1.0 - strength) * l[uv_layer].uv if sc.muv_uv_sculpt_relax_method == 'HC': diff --git a/uv_magic_uv/op/uvw.py b/uv_magic_uv/op/uvw.py index c97e0e042e2f1b3f3c47264306a381ed393d96ff..9b44100cd92943ee2129b707159317877bc6205e 100644 --- a/uv_magic_uv/op/uvw.py +++ b/uv_magic_uv/op/uvw.py @@ -23,6 +23,8 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" +from math import sin, cos, pi + import bpy import bmesh from bpy.props import ( @@ -30,22 +32,145 @@ 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 +from ..utils import compatibility as compat + + +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: + if common.check_version(2, 80, 0) >= 0: + # pylint: disable=E0001 + co = q @ l.vert.co + else: + 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 -__all__ = [ - 'Properties', - 'MUV_OT_UVW_BoxMap', - 'MUV_OT_UVW_BestPlanerMap', -] + l[uv_layer].uv = Vector((u, v)) @PropertyClassRegistry() -class Properties: +class _Properties: idname = "uvw" @classmethod @@ -68,32 +193,33 @@ class Properties: @BlClassRegistry() +@compat.make_annotations 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 @@ -104,7 +230,7 @@ class MUV_OT_UVW_BoxMap(bpy.types.Operator): # we can not get area/space/region from console if common.is_console_mode(): return True - return impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): obj = context.active_object @@ -113,43 +239,44 @@ class MUV_OT_UVW_BoxMap(bpy.types.Operator): bm.faces.ensure_lookup_table() # get UV layer - uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap) + uv_layer = _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) + _apply_box_map(bm, uv_layer, self.size, self.offset, self.rotation, + self.tex_aspect) bmesh.update_edit_mesh(obj.data) return {'FINISHED'} @BlClassRegistry() +@compat.make_annotations 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 @@ -160,7 +287,7 @@ class MUV_OT_UVW_BestPlanerMap(bpy.types.Operator): # we can not get area/space/region from console if common.is_console_mode(): return True - return impl.is_valid_context(context) + return _is_valid_context(context) def execute(self, context): obj = context.active_object @@ -169,12 +296,12 @@ class MUV_OT_UVW_BestPlanerMap(bpy.types.Operator): bm.faces.ensure_lookup_table() # get UV layer - uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap) + uv_layer = _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) + _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/op/world_scale_uv.py b/uv_magic_uv/op/world_scale_uv.py index a957d5d464ac387fdc9acfe350ba76d31ce64f52..ae46e4a93f4cabcc3a40b40ea84b215f7424a9a8 100644 --- a/uv_magic_uv/op/world_scale_uv.py +++ b/uv_magic_uv/op/world_scale_uv.py @@ -23,6 +23,7 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" +from math import sqrt import bpy from bpy.props import ( @@ -31,14 +32,153 @@ from bpy.props import ( IntVectorProperty, 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 world_scale_uv_impl as impl +from ..utils import compatibility as compat + + +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 _measure_wsuv_info(obj, tex_size=None): + mesh_area = common.measure_mesh_area(obj) + uv_area = common.measure_uv_area(obj, tex_size) + + if not uv_area: + return None, mesh_area, None + + if mesh_area == 0.0: + density = 0.0 + else: + density = sqrt(uv_area) / sqrt(mesh_area) + + return uv_area, mesh_area, density + + +def _apply(obj, origin, factor): + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + sel_faces = [f for f in bm.faces if f.select] + + uv_layer = bm.loops.layers.uv.verify() + + # calculate origin + if origin == 'CENTER': + origin = Vector((0.0, 0.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin = origin + uv + num = num + 1 + origin = origin / num + elif origin == 'LEFT_TOP': + origin = Vector((100000.0, -100000.0)) + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = max(origin.y, uv.y) + elif origin == 'LEFT_CENTER': + origin = Vector((100000.0, 0.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = origin.y + uv.y + num = num + 1 + origin.y = origin.y / num + elif origin == 'LEFT_BOTTOM': + origin = Vector((100000.0, 100000.0)) + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = min(origin.y, uv.y) + elif origin == 'CENTER_TOP': + origin = Vector((0.0, -100000.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = origin.x + uv.x + origin.y = max(origin.y, uv.y) + num = num + 1 + origin.x = origin.x / num + elif origin == 'CENTER_BOTTOM': + origin = Vector((0.0, 100000.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = origin.x + uv.x + origin.y = min(origin.y, uv.y) + num = num + 1 + origin.x = origin.x / num + elif origin == 'RIGHT_TOP': + origin = Vector((-100000.0, -100000.0)) + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = max(origin.x, uv.x) + origin.y = max(origin.y, uv.y) + elif origin == 'RIGHT_CENTER': + origin = Vector((-100000.0, 0.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = max(origin.x, uv.x) + origin.y = origin.y + uv.y + num = num + 1 + origin.y = origin.y / num + elif origin == 'RIGHT_BOTTOM': + origin = Vector((-100000.0, 100000.0)) + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = max(origin.x, uv.x) + origin.y = min(origin.y, uv.y) + + # update UV coordinate + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + diff = uv - origin + l[uv_layer].uv = origin + diff * factor + + bmesh.update_edit_mesh(obj.data) @PropertyClassRegistry() -class Properties: +class _Properties: idname = "world_scale_uv" @classmethod @@ -140,18 +280,35 @@ class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator): bl_description = "Measure face size for scale calculation" bl_options = {'REGISTER', 'UNDO'} - def __init__(self): - self.__impl = impl.MeasureImpl() - @classmethod def poll(cls, context): - return impl.MeasureImpl.poll(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): - return self.__impl.execute(self, context) + sc = context.scene + obj = context.active_object + + uv_area, mesh_area, density = _measure_wsuv_info(obj) + if not uv_area: + self.report({'WARNING'}, + "Object must have more than one UV map and texture") + return {'CANCELLED'} + + sc.muv_world_scale_uv_src_uv_area = uv_area + sc.muv_world_scale_uv_src_mesh_area = mesh_area + sc.muv_world_scale_uv_src_density = density + + self.report({'INFO'}, "UV Area: {0}, Mesh Area: {1}, Texel Density: {2}" + .format(uv_area, mesh_area, density)) + + return {'FINISHED'} @BlClassRegistry() +@compat.make_annotations class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): """ Operation class: Apply scaled UV (Manual) @@ -162,20 +319,20 @@ class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): bl_description = "Apply scaled UV based on user specification" bl_options = {'REGISTER', 'UNDO'} - tgt_density: FloatProperty( + tgt_density = FloatProperty( name="Density", description="Target Texel Density", default=1.0, min=0.0 ) - tgt_texture_size: IntVectorProperty( + tgt_texture_size = IntVectorProperty( name="Texture Size", size=2, min=1, soft_max=10240, default=(1024, 1024), ) - origin: EnumProperty( + origin = EnumProperty( name="Origin", description="Aspect Origin", items=[ @@ -192,31 +349,64 @@ class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): ], default='CENTER' ) - show_dialog: BoolProperty( + show_dialog = BoolProperty( name="Show Diaglog Menu", description="Show dialog menu if true", default=True, options={'HIDDEN', 'SKIP_SAVE'} ) - def __init__(self): - self.__impl = impl.ApplyManualImpl() - @classmethod def poll(cls, context): - return impl.ApplyManualImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + def __apply_manual(self, context): + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + tex_size = self.tgt_texture_size + uv_area, _, density = _measure_wsuv_info(obj, tex_size) + if not uv_area: + self.report({'WARNING'}, "Object must have more than one UV map") + return {'CANCELLED'} + + tgt_density = self.tgt_density + factor = tgt_density / density + + _apply(context.active_object, self.origin, factor) + self.report({'INFO'}, "Scaling factor: {0}".format(factor)) + + return {'FINISHED'} - def draw(self, context): - self.__impl.draw(self, context) + def draw(self, _): + layout = self.layout - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) + layout.prop(self, "tgt_density") + layout.prop(self, "tgt_texture_size") + layout.prop(self, "origin") + + layout.separator() + + def invoke(self, context, _): + if self.show_dialog: + wm = context.window_manager + return wm.invoke_props_dialog(self) + + return self.execute(context) def execute(self, context): - return self.__impl.execute(self, context) + return self.__apply_manual(context) @BlClassRegistry() +@compat.make_annotations class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): """ Operation class: Apply scaled UV (Scaling Density) @@ -227,13 +417,13 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): bl_description = "Apply scaled UV with scaling density" bl_options = {'REGISTER', 'UNDO'} - tgt_scaling_factor: FloatProperty( + tgt_scaling_factor = FloatProperty( name="Scaling Factor", default=1.0, max=1000.0, min=0.00001 ) - origin: EnumProperty( + origin = EnumProperty( name="Origin", description="Aspect Origin", items=[ @@ -250,44 +440,97 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): ], default='CENTER' ) - src_density: FloatProperty( + src_density = FloatProperty( name="Density", description="Source Texel Density", default=0.0, min=0.0, options={'HIDDEN'} ) - same_density: BoolProperty( + same_density = BoolProperty( name="Same Density", description="Apply same density", default=False, options={'HIDDEN'} ) - show_dialog: BoolProperty( + show_dialog = BoolProperty( name="Show Diaglog Menu", description="Show dialog menu if true", default=True, options={'HIDDEN', 'SKIP_SAVE'} ) - def __init__(self): - self.__impl = impl.ApplyScalingDensityImpl() - @classmethod def poll(cls, context): - return impl.ApplyScalingDensityImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + def __apply_scaling_density(self, context): + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + uv_area, _, density = _measure_wsuv_info(obj) + if not uv_area: + self.report({'WARNING'}, + "Object must have more than one UV map and texture") + return {'CANCELLED'} + + tgt_density = self.src_density * self.tgt_scaling_factor + factor = tgt_density / density + + _apply(context.active_object, self.origin, factor) + self.report({'INFO'}, "Scaling factor: {0}".format(factor)) + + return {'FINISHED'} + + def draw(self, _): + layout = self.layout - def draw(self, context): - self.__impl.draw(self, context) + layout.label(text="Source:") + col = layout.column() + col.prop(self, "src_density") + col.enabled = False - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) + layout.separator() + + if not self.same_density: + layout.prop(self, "tgt_scaling_factor") + layout.prop(self, "origin") + + layout.separator() + + def invoke(self, context, _): + sc = context.scene + + if self.show_dialog: + wm = context.window_manager + + if self.same_density: + self.tgt_scaling_factor = 1.0 + else: + self.tgt_scaling_factor = \ + sc.muv_world_scale_uv_tgt_scaling_factor + self.src_density = sc.muv_world_scale_uv_src_density + + return wm.invoke_props_dialog(self) + + return self.execute(context) def execute(self, context): - return self.__impl.execute(self, context) + if self.same_density: + self.tgt_scaling_factor = 1.0 + + return self.__apply_scaling_density(context) @BlClassRegistry() +@compat.make_annotations class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): """ Operation class: Apply scaled UV (Proportional to mesh) @@ -298,7 +541,7 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): bl_description = "Apply scaled UV proportionaled to mesh" bl_options = {'REGISTER', 'UNDO'} - origin: EnumProperty( + origin = EnumProperty( name="Origin", description="Aspect Origin", items=[ @@ -315,46 +558,91 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): ], default='CENTER' ) - src_density: FloatProperty( + src_density = FloatProperty( name="Source Density", description="Source Texel Density", default=0.0, min=0.0, options={'HIDDEN'} ) - src_uv_area: FloatProperty( + src_uv_area = FloatProperty( name="Source UV Area", description="Source UV Area", default=0.0, min=0.0, options={'HIDDEN'} ) - src_mesh_area: FloatProperty( + src_mesh_area = FloatProperty( name="Source Mesh Area", description="Source Mesh Area", default=0.0, min=0.0, options={'HIDDEN'} ) - show_dialog: BoolProperty( + show_dialog = BoolProperty( name="Show Diaglog Menu", description="Show dialog menu if true", default=True, options={'HIDDEN', 'SKIP_SAVE'} ) - def __init__(self): - self.__impl = impl.ApplyProportionalToMeshImpl() - @classmethod def poll(cls, context): - return impl.ApplyProportionalToMeshImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + def __apply_proportional_to_mesh(self, context): + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + bm.edges.ensure_lookup_table() + bm.faces.ensure_lookup_table() + + uv_area, mesh_area, density = _measure_wsuv_info(obj) + if not uv_area: + self.report({'WARNING'}, + "Object must have more than one UV map and texture") + return {'CANCELLED'} + + tgt_density = self.src_density * sqrt(mesh_area) / sqrt( + self.src_mesh_area) + + factor = tgt_density / density + + _apply(context.active_object, self.origin, factor) + self.report({'INFO'}, "Scaling factor: {0}".format(factor)) + + return {'FINISHED'} + + def draw(self, _): + layout = self.layout + + layout.label(text="Source:") + col = layout.column(align=True) + col.prop(self, "src_density") + col.prop(self, "src_uv_area") + col.prop(self, "src_mesh_area") + col.enabled = False + + layout.separator() + layout.prop(self, "origin") + + layout.separator() + + def invoke(self, context, _): + if self.show_dialog: + wm = context.window_manager + sc = context.scene + + self.src_density = sc.muv_world_scale_uv_src_density + self.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area - def draw(self, context): - self.__impl.draw(self, context) + return wm.invoke_props_dialog(self) - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) + return self.execute(context) def execute(self, context): - return self.__impl.execute(self, context) + return self.__apply_proportional_to_mesh(context) diff --git a/uv_magic_uv/preferences.py b/uv_magic_uv/preferences.py index a58d08d4f05178e3d43ce860806dd96f9541c89c..01d7155e66a6bc714e97566ebd1c0abf876a49ca 100644 --- a/uv_magic_uv/preferences.py +++ b/uv_magic_uv/preferences.py @@ -37,6 +37,7 @@ from . import op from . import ui from .utils.bl_class_registry import BlClassRegistry from .utils.addon_updator import AddonUpdatorManager +from .utils import compatibility as compat def view3d_uvmap_menu_fn(self, context): @@ -44,7 +45,7 @@ def view3d_uvmap_menu_fn(self, context): sc = context.scene layout.separator() - layout.label(text="Copy/Paste UV", icon='IMAGE') + layout.label(text="Copy/Paste UV", icon=compat.icon('IMAGE')) # Copy/Paste UV layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_CopyPasteUV.bl_idname, text="Copy/Paste UV") @@ -53,7 +54,7 @@ def view3d_uvmap_menu_fn(self, context): text="Transfer UV") layout.separator() - layout.label(text="UV Manipulation", icon='IMAGE') + layout.label(text="UV Manipulation", icon=compat.icon('IMAGE')) # Flip/Rotate UV ops = layout.operator(op.flip_rotate_uv.MUV_OT_FlipRotate.bl_idname, text="Flip/Rotate UV") @@ -80,7 +81,7 @@ def view3d_uvmap_menu_fn(self, context): layout.prop(sc, "muv_uv_sculpt_enable", text="UV Sculpt") layout.separator() - layout.label(text="UV Mapping", icon='IMAGE') + layout.label(text="UV Mapping", icon=compat.icon('IMAGE')) # Unwrap Constraint ops = layout.operator( op.unwrap_constraint.MUV_OT_UnwrapConstraint.bl_idname, @@ -98,7 +99,7 @@ def view3d_object_menu_fn(self, _): layout = self.layout layout.separator() - layout.label(text="Copy/Paste UV", icon='IMAGE') + layout.label(text="Copy/Paste UV", icon=compat.icon('IMAGE')) # Copy/Paste UV (Among Object) layout.menu(ui.VIEW3D_MT_object.MUV_MT_CopyPasteUV_Object.bl_idname, text="Copy/Paste UV") @@ -110,13 +111,13 @@ def image_uvs_menu_fn(self, context): layout.separator() # Copy/Paste UV (on UV/Image Editor) - layout.label(text="Copy/Paste UV", icon='IMAGE') + layout.label(text="Copy/Paste UV", icon=compat.icon('IMAGE')) layout.menu(ui.IMAGE_MT_uvs.MUV_MT_CopyPasteUV_UVEdit.bl_idname, text="Copy/Paste UV") layout.separator() # Pack UV - layout.label(text="UV Manipulation", icon='IMAGE') + layout.label(text="UV Manipulation", icon=compat.icon('IMAGE')) 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 @@ -133,7 +134,7 @@ def image_uvs_menu_fn(self, context): layout.separator() # Align UV Cursor - layout.label(text="Editor Enhancement", icon='IMAGE') + layout.label(text="Editor Enhancement", icon=compat.icon('IMAGE')) layout.menu(ui.IMAGE_MT_uvs.MUV_MT_AlignUVCursor.bl_idname, text="Align UV Cursor") # UV Bounding Box @@ -170,13 +171,14 @@ class MUV_OT_CheckAddonUpdate(bpy.types.Operator): @BlClassRegistry() +@compat.make_annotations class MUV_OT_UpdateAddon(bpy.types.Operator): bl_idname = "uv.muv_update_addon" bl_label = "Update" bl_description = "Update Add-on" bl_options = {'REGISTER', 'UNDO'} - branch_name: StringProperty( + branch_name = StringProperty( name="Branch Name", description="Branch name to update", default="", @@ -198,6 +200,7 @@ def get_update_candidate_branches(_, __): @BlClassRegistry() +@compat.make_annotations class Preferences(AddonPreferences): """Preferences class: Preferences for this add-on""" @@ -210,7 +213,7 @@ class Preferences(AddonPreferences): remove_builtin_menu() # enable to add features to built-in menu - enable_builtin_menu: BoolProperty( + enable_builtin_menu = BoolProperty( name="Built-in Menu", description="Enable built-in menu", default=True, @@ -218,7 +221,7 @@ class Preferences(AddonPreferences): ) # for UV Sculpt - uv_sculpt_brush_color: FloatVectorProperty( + uv_sculpt_brush_color = FloatVectorProperty( name="Color", description="Color", default=(1.0, 0.4, 0.4, 1.0), @@ -229,7 +232,7 @@ class Preferences(AddonPreferences): ) # for Overlapped UV - uv_inspection_overlapped_color: FloatVectorProperty( + uv_inspection_overlapped_color = FloatVectorProperty( name="Color", description="Color", default=(0.0, 0.0, 1.0, 0.3), @@ -240,7 +243,7 @@ class Preferences(AddonPreferences): ) # for Flipped UV - uv_inspection_flipped_color: FloatVectorProperty( + uv_inspection_flipped_color = FloatVectorProperty( name="Color", description="Color", default=(1.0, 0.0, 0.0, 0.3), @@ -251,7 +254,7 @@ class Preferences(AddonPreferences): ) # for Texture Projection - texture_projection_canvas_padding: FloatVectorProperty( + texture_projection_canvas_padding = FloatVectorProperty( name="Canvas Padding", description="Canvas Padding", size=2, @@ -260,13 +263,13 @@ class Preferences(AddonPreferences): default=(20.0, 20.0)) # for UV Bounding Box - uv_bounding_box_cp_size: FloatProperty( + 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( + uv_bounding_box_cp_react_size = FloatProperty( name="React Size", description="Size event fired", default=10.0, @@ -274,7 +277,7 @@ class Preferences(AddonPreferences): max=100.0) # for UI - category: EnumProperty( + category = EnumProperty( name="Category", description="Preferences Category", items=[ @@ -284,39 +287,39 @@ class Preferences(AddonPreferences): ], default='INFO' ) - info_desc_expanded: BoolProperty( + info_desc_expanded = BoolProperty( name="Description", description="Description", default=False ) - info_loc_expanded: BoolProperty( + info_loc_expanded = BoolProperty( name="Location", description="Location", default=False ) - conf_uv_sculpt_expanded: BoolProperty( + conf_uv_sculpt_expanded = BoolProperty( name="UV Sculpt", description="UV Sculpt", default=False ) - conf_uv_inspection_expanded: BoolProperty( + conf_uv_inspection_expanded = BoolProperty( name="UV Inspection", description="UV Inspection", default=False ) - conf_texture_projection_expanded: BoolProperty( + conf_texture_projection_expanded = BoolProperty( name="Texture Projection", description="Texture Projection", default=False ) - conf_uv_bounding_box_expanded: BoolProperty( + conf_uv_bounding_box_expanded = BoolProperty( name="UV Bounding Box", description="UV Bounding Box", default=False ) # for add-on updater - updater_branch_to_update: EnumProperty( + updater_branch_to_update = EnumProperty( name="branch", description="Target branch to update add-on", items=get_update_candidate_branches @@ -349,27 +352,27 @@ class Preferences(AddonPreferences): else 'DISCLOSURE_TRI_RIGHT') if self.info_loc_expanded: row = layout.row(align=True) - sp = row.split(factor=0.5) + sp = compat.layout_split(row, 0.5) sp.label(text="3D View > Tool shelf > " + "Copy/Paste UV (Object mode)") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="Copy/Paste UV (Among objects)") row = layout.row(align=True) - sp = row.split(factor=0.5) + sp = compat.layout_split(row, 0.5) sp.label(text="3D View > Tool shelf > " + "Copy/Paste UV (Edit mode)") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="Copy/Paste UV (Among faces in 3D View)") col.label(text="Transfer UV") row = layout.row(align=True) - sp = row.split(factor=0.5) + sp = compat.layout_split(row, 0.5) sp.label(text="3D View > Tool shelf > " + "UV Manipulation (Edit mode)") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="Flip/Rotate UV") col.label(text="Mirror UV") @@ -381,26 +384,26 @@ class Preferences(AddonPreferences): col.label(text="UV Sculpt") row = layout.row(align=True) - sp = row.split(factor=0.5) + sp = compat.layout_split(row, 0.5) sp.label(text="3D View > Tool shelf > " + "UV Manipulation (Edit mode)") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="Unwrap Constraint") col.label(text="Texture Projection") col.label(text="UVW") row = layout.row(align=True) - sp = row.split(factor=0.5) + sp = compat.layout_split(row, 0.5) sp.label(text="UV/Image Editor > Tool shelf > Copy/Paste UV") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="Copy/Paste UV (Among faces in UV/Image Editor)") row = layout.row(align=True) - sp = row.split(factor=0.5) + sp = compat.layout_split(row, 0.5) sp.label(text="UV/Image Editor > Tool shelf > UV Manipulation") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="Align UV") col.label(text="Smooth UV") @@ -408,10 +411,10 @@ class Preferences(AddonPreferences): col.label(text="Pack UV (Extension)") row = layout.row(align=True) - sp = row.split(factor=0.5) + sp = compat.layout_split(row, 0.5) sp.label(text="UV/Image Editor > Tool shelf > " + "Editor Enhancement") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="Align UV Cursor") col.label(text="UV Cursor Location") @@ -430,9 +433,9 @@ class Preferences(AddonPreferences): icon='DISCLOSURE_TRI_DOWN' if self.conf_uv_sculpt_expanded else 'DISCLOSURE_TRI_RIGHT') if self.conf_uv_sculpt_expanded: - sp = layout.split(factor=0.05) + sp = compat.layout_split(layout, 0.05) col = sp.column() # spacer - sp = sp.split(factor=0.3) + sp = compat.layout_split(sp, 0.3) col = sp.column() col.label(text="Brush Color:") col.prop(self, "uv_sculpt_brush_color", text="") @@ -443,13 +446,13 @@ class Preferences(AddonPreferences): icon='DISCLOSURE_TRI_DOWN' if self.conf_uv_inspection_expanded else 'DISCLOSURE_TRI_RIGHT') if self.conf_uv_inspection_expanded: - sp = layout.split(factor=0.05) + sp = compat.layout_split(layout, 0.05) col = sp.column() # spacer - sp = sp.split(factor=0.3) + sp = compat.layout_split(sp, 0.3) col = sp.column() col.label(text="Overlapped UV Color:") col.prop(self, "uv_inspection_overlapped_color", text="") - sp = sp.split(factor=0.45) + sp = compat.layout_split(sp, 0.45) col = sp.column() col.label(text="Flipped UV Color:") col.prop(self, "uv_inspection_flipped_color", text="") @@ -462,9 +465,9 @@ class Preferences(AddonPreferences): if self.conf_texture_projection_expanded else 'DISCLOSURE_TRI_RIGHT') if self.conf_texture_projection_expanded: - sp = layout.split(factor=0.05) + sp = compat.layout_split(layout, 0.05) col = sp.column() # spacer - sp = sp.split(factor=0.3) + sp = compat.layout_split(sp, 0.3) col = sp.column() col.prop(self, "texture_projection_canvas_padding") layout.separator() @@ -475,9 +478,9 @@ class Preferences(AddonPreferences): if self.conf_uv_bounding_box_expanded else 'DISCLOSURE_TRI_RIGHT') if self.conf_uv_bounding_box_expanded: - sp = layout.split(factor=0.05) + sp = compat.layout_split(layout, 0.05) col = sp.column() # spacer - sp = sp.split(factor=0.3) + sp = compat.layout_split(sp, 0.3) col = sp.column() col.label(text="Control Point:") col.prop(self, "uv_bounding_box_cp_size") diff --git a/uv_magic_uv/properites.py b/uv_magic_uv/properites.py index 60ce26eb10922b797645122d854ad52666941767..81ebbb4d6fa34806fe8795390e612a159a9eb5fc 100644 --- a/uv_magic_uv/properites.py +++ b/uv_magic_uv/properites.py @@ -24,16 +24,6 @@ __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(): @@ -41,6 +31,7 @@ class MUV_Properties(): self.prefs = MUV_Prefs() +# TODO: delete this class MUV_Prefs(): expanded = { "info_desc": False, diff --git a/uv_magic_uv/ui/IMAGE_MT_uvs.py b/uv_magic_uv/ui/IMAGE_MT_uvs.py index dfb509c7f185b1187e5d5d4d7301baf2b81bd851..3881f547c2b6d6db0ecec9dbff4a321451c68de6 100644 --- a/uv_magic_uv/ui/IMAGE_MT_uvs.py +++ b/uv_magic_uv/ui/IMAGE_MT_uvs.py @@ -41,10 +41,6 @@ from ..op.select_uv import ( from ..op.uv_inspection import MUV_OT_UVInspection_Update from ..utils.bl_class_registry import BlClassRegistry -__all__ = [ - 'MUV_MT_CopyPasteUV_UVEdit', -] - @BlClassRegistry() class MUV_MT_CopyPasteUV_UVEdit(bpy.types.Menu): diff --git a/uv_magic_uv/ui/VIEW3D_MT_object.py b/uv_magic_uv/ui/VIEW3D_MT_object.py index 318cd82c6ea6506a1176da5413359906ff4e116a..0d51a65bda5a6c08cb4c9e34dea5a7e7662bbf1b 100644 --- a/uv_magic_uv/ui/VIEW3D_MT_object.py +++ b/uv_magic_uv/ui/VIEW3D_MT_object.py @@ -28,10 +28,6 @@ import bpy from ..op import copy_paste_uv_object from ..utils.bl_class_registry import BlClassRegistry -__all__ = [ - 'MUV_MT_CopyPasteUV_Object', -] - @BlClassRegistry() class MUV_MT_CopyPasteUV_Object(bpy.types.Menu): diff --git a/uv_magic_uv/ui/VIEW3D_MT_uv_map.py b/uv_magic_uv/ui/VIEW3D_MT_uv_map.py index 012ce0472c84da1097b391ca13dc4e7af04536f0..d673ed4ded482d8f454539eaa0a1da2ee08afe44 100644 --- a/uv_magic_uv/ui/VIEW3D_MT_uv_map.py +++ b/uv_magic_uv/ui/VIEW3D_MT_uv_map.py @@ -45,18 +45,9 @@ from ..op.world_scale_uv import ( MUV_OT_WorldScaleUV_ApplyScalingDensity, MUV_OT_WorldScaleUV_ApplyProportionalToMesh, ) -from ..op.texture_projection import ( - MUV_OT_TextureProjection, - MUV_OT_TextureProjection_Project, -) +from ..op.texture_projection import MUV_OT_TextureProjection_Project from ..utils.bl_class_registry import BlClassRegistry -__all__ = [ - 'MUV_MT_CopyPasteUV', - 'MUV_MT_TransferUV', - 'MUV_MT_UVW', -] - @BlClassRegistry() class MUV_MT_CopyPasteUV(bpy.types.Menu): diff --git a/uv_magic_uv/ui/uvedit_copy_paste_uv.py b/uv_magic_uv/ui/uvedit_copy_paste_uv.py index e21a5abd336d6af9c7eb259582a29de1ad89a452..7b8a1c526e3bc73230d9ae1dc37c101a9ec5e766 100644 --- a/uv_magic_uv/ui/uvedit_copy_paste_uv.py +++ b/uv_magic_uv/ui/uvedit_copy_paste_uv.py @@ -27,13 +27,11 @@ import bpy from ..op import copy_paste_uv_uvedit from ..utils.bl_class_registry import BlClassRegistry - -__all__ = [ - 'MUV_PT_UVEdit_CopyPasteUV', -] +from ..utils import compatibility as compat @BlClassRegistry() +@compat.ChangeRegionType(region_type='TOOLS') class MUV_PT_UVEdit_CopyPasteUV(bpy.types.Panel): """ Panel class: Copy/Paste UV on Property Panel on UV/ImageEditor @@ -48,7 +46,7 @@ class MUV_PT_UVEdit_CopyPasteUV(bpy.types.Panel): def draw_header(self, _): layout = self.layout - layout.label(text="", icon='IMAGE') + layout.label(text="", icon=compat.icon('IMAGE')) def draw(self, _): layout = self.layout diff --git a/uv_magic_uv/ui/uvedit_editor_enhancement.py b/uv_magic_uv/ui/uvedit_editor_enhancement.py index cfd9ef28c260e01681a19881751a2ffa4db9324a..7c366aa7d9fd93b1c3345dbdf1f56bf425e8df3c 100644 --- a/uv_magic_uv/ui/uvedit_editor_enhancement.py +++ b/uv_magic_uv/ui/uvedit_editor_enhancement.py @@ -34,13 +34,11 @@ from ..op.uv_inspection import ( MUV_OT_UVInspection_Update, ) from ..utils.bl_class_registry import BlClassRegistry - -__all__ = [ - 'MUV_PT_UVEdit_EditorEnhancement', -] +from ..utils import compatibility as compat @BlClassRegistry() +@compat.ChangeRegionType(region_type='TOOLS') class MUV_PT_UVEdit_EditorEnhancement(bpy.types.Panel): """ Panel class: UV/Image Editor Enhancement @@ -55,7 +53,7 @@ class MUV_PT_UVEdit_EditorEnhancement(bpy.types.Panel): def draw_header(self, _): layout = self.layout - layout.label(text="", icon='IMAGE') + layout.label(text="", icon=compat.icon('IMAGE')) def draw(self, context): layout = self.layout diff --git a/uv_magic_uv/ui/uvedit_uv_manipulation.py b/uv_magic_uv/ui/uvedit_uv_manipulation.py index f5bd27e3ff818344149c18178f1a52a2e5391924..d8c7795735bff7a791741ebae87f370f54cd51d2 100644 --- a/uv_magic_uv/ui/uvedit_uv_manipulation.py +++ b/uv_magic_uv/ui/uvedit_uv_manipulation.py @@ -39,9 +39,11 @@ from ..op.select_uv import ( ) from ..op.pack_uv import MUV_OT_PackUV from ..utils.bl_class_registry import BlClassRegistry +from ..utils import compatibility as compat @BlClassRegistry() +@compat.ChangeRegionType(region_type='TOOLS') class MUV_PT_UVEdit_UVManipulation(bpy.types.Panel): """ Panel class: UV Manipulation on Property Panel on UV/ImageEditor @@ -56,7 +58,7 @@ class MUV_PT_UVEdit_UVManipulation(bpy.types.Panel): def draw_header(self, _): layout = self.layout - layout.label(text="", icon='IMAGE') + layout.label(text="", icon=compat.icon('IMAGE')) def draw(self, context): sc = context.scene 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 14fba24a2c7e80e254726308417ae4e6e30d93f6..0fc4df141a718b1873f55876d037cf70a8f7a978 100644 --- a/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py +++ b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py @@ -30,13 +30,11 @@ from ..op import ( transfer_uv, ) from ..utils.bl_class_registry import BlClassRegistry - -__all__ = [ - 'MUV_PT_CopyPasteUVEditMode', -] +from ..utils import compatibility as compat @BlClassRegistry() +@compat.ChangeRegionType(region_type='TOOLS') class MUV_PT_CopyPasteUVEditMode(bpy.types.Panel): """ Panel class: Copy/Paste UV on Property Panel on View3D @@ -51,7 +49,7 @@ class MUV_PT_CopyPasteUVEditMode(bpy.types.Panel): def draw_header(self, _): layout = self.layout - layout.label(text="", icon='IMAGE') + layout.label(text="", icon=compat.icon('IMAGE')) def draw(self, context): sc = context.scene 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 6dd0d3b43584e8bd479da565dbe9b6de34381e95..36968ffbbebe0072b237386119255e01179a66b1 100644 --- a/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py +++ b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py @@ -27,13 +27,11 @@ import bpy from ..op import copy_paste_uv_object from ..utils.bl_class_registry import BlClassRegistry - -__all__ = [ - 'MUV_PT_View3D_Object_CopyPasteUV', -] +from ..utils import compatibility as compat @BlClassRegistry() +@compat.ChangeRegionType(region_type='TOOLS') class MUV_PT_View3D_Object_CopyPasteUV(bpy.types.Panel): """ Panel class: Copy/Paste UV on Property Panel on View3D @@ -48,7 +46,7 @@ class MUV_PT_View3D_Object_CopyPasteUV(bpy.types.Panel): def draw_header(self, _): layout = self.layout - layout.label(text="", icon='IMAGE') + layout.label(text="", icon=compat.icon('IMAGE')) def draw(self, context): sc = context.scene diff --git a/uv_magic_uv/ui/view3d_uv_manipulation.py b/uv_magic_uv/ui/view3d_uv_manipulation.py index 4c09bdf298b29ddc17eab16d280541ca5ef60dd4..e385da386720122ffc3a7360be0bff5bc81734c7 100644 --- a/uv_magic_uv/ui/view3d_uv_manipulation.py +++ b/uv_magic_uv/ui/view3d_uv_manipulation.py @@ -48,13 +48,11 @@ from ..op.mirror_uv import MUV_OT_MirrorUV from ..op.move_uv import MUV_OT_MoveUV from ..op.preserve_uv_aspect import MUV_OT_PreserveUVAspect from ..utils.bl_class_registry import BlClassRegistry - -__all__ = [ - 'MUV_PT_View3D_UVManipulation', -] +from ..utils import compatibility as compat @BlClassRegistry() +@compat.ChangeRegionType(region_type='TOOLS') class MUV_PT_View3D_UVManipulation(bpy.types.Panel): """ Panel class: UV Manipulation on Property Panel on View3D @@ -69,7 +67,7 @@ class MUV_PT_View3D_UVManipulation(bpy.types.Panel): def draw_header(self, _): layout = self.layout - layout.label(text="", icon='IMAGE') + layout.label(text="", icon=compat.icon('IMAGE')) def draw(self, context): sc = context.scene @@ -108,11 +106,11 @@ class MUV_PT_View3D_UVManipulation(bpy.types.Panel): box.prop(sc, "muv_world_scale_uv_mode", text="") if sc.muv_world_scale_uv_mode == 'MANUAL': - sp = box.split(factor=0.5) + sp = compat.layout_split(box, 0.5) col = sp.column() col.prop(sc, "muv_world_scale_uv_tgt_texture_size", text="Texture Size") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column() col.label(text="Density:") col.prop(sc, "muv_world_scale_uv_tgt_density", text="") @@ -125,19 +123,19 @@ class MUV_PT_View3D_UVManipulation(bpy.types.Panel): ops.show_dialog = False elif sc.muv_world_scale_uv_mode == 'SAME_DENSITY': - sp = box.split(factor=0.4) + sp = compat.layout_split(box, 0.4) col = sp.column(align=True) col.label(text="Source:") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.operator(MUV_OT_WorldScaleUV_Measure.bl_idname, text="Measure") - sp = box.split(factor=0.7) + sp = compat.layout_split(box, 0.7) col = sp.column(align=True) col.prop(sc, "muv_world_scale_uv_src_density", text="Density") col.enabled = False - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="px2/cm2") @@ -152,19 +150,19 @@ class MUV_PT_View3D_UVManipulation(bpy.types.Panel): ops.show_dialog = False elif sc.muv_world_scale_uv_mode == 'SCALING_DENSITY': - sp = box.split(factor=0.4) + sp = compat.layout_split(box, 0.4) col = sp.column(align=True) col.label(text="Source:") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.operator(MUV_OT_WorldScaleUV_Measure.bl_idname, text="Measure") - sp = box.split(factor=0.7) + sp = compat.layout_split(box, 0.7) col = sp.column(align=True) col.prop(sc, "muv_world_scale_uv_src_density", text="Density") col.enabled = False - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="px2/cm2") @@ -183,22 +181,22 @@ class MUV_PT_View3D_UVManipulation(bpy.types.Panel): sc.muv_world_scale_uv_tgt_scaling_factor elif sc.muv_world_scale_uv_mode == 'PROPORTIONAL_TO_MESH': - sp = box.split(factor=0.4) + sp = compat.layout_split(box, 0.4) col = sp.column(align=True) col.label(text="Source:") - sp = sp.split(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.operator(MUV_OT_WorldScaleUV_Measure.bl_idname, text="Measure") - sp = box.split(factor=0.7) + sp = compat.layout_split(box, 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(factor=1.0) + sp = compat.layout_split(sp, 1.0) col = sp.column(align=True) col.label(text="cm2") col.label(text="px2") diff --git a/uv_magic_uv/ui/view3d_uv_mapping.py b/uv_magic_uv/ui/view3d_uv_mapping.py index e64a2ce183151819dacbfb4cb5edcdc6edacb239..9a75de4914fb10a709025909d574721574b9c7f4 100644 --- a/uv_magic_uv/ui/view3d_uv_mapping.py +++ b/uv_magic_uv/ui/view3d_uv_mapping.py @@ -34,13 +34,11 @@ from ..op.texture_projection import ( ) from ..op.unwrap_constraint import MUV_OT_UnwrapConstraint from ..utils.bl_class_registry import BlClassRegistry - -__all__ = [ - 'MUV_PT_View3D_UVMapping', -] +from ..utils import compatibility as compat @BlClassRegistry() +@compat.ChangeRegionType(region_type='TOOLS') class MUV_PT_View3D_UVMapping(bpy.types.Panel): """ Panel class: UV Mapping on Property Panel on View3D @@ -55,7 +53,7 @@ class MUV_PT_View3D_UVMapping(bpy.types.Panel): def draw_header(self, _): layout = self.layout - layout.label(text="", icon='IMAGE') + layout.label(text="", icon=compat.icon('IMAGE')) def draw(self, context): sc = context.scene diff --git a/uv_magic_uv/utils/__init__.py b/uv_magic_uv/utils/__init__.py index 333a38738f753e12e5148b322122110d9930281a..01b1fc0020cdcb75611e96de503b81d090d3e8f5 100644 --- a/uv_magic_uv/utils/__init__.py +++ b/uv_magic_uv/utils/__init__.py @@ -27,10 +27,12 @@ if "bpy" in locals(): import importlib importlib.reload(addon_updator) importlib.reload(bl_class_registry) + importlib.reload(compatibility) importlib.reload(property_class_registry) else: from . import addon_updator from . import bl_class_registry + from . import compatibility 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 index d173061566fd0369e68c57fec302d4f542325fe7..408e6fd6135844ec3df6b30b90a2516cdf05072f 100644 --- a/uv_magic_uv/utils/bl_class_registry.py +++ b/uv_magic_uv/utils/bl_class_registry.py @@ -27,10 +27,6 @@ import bpy from .. import common -__all__ = [ - 'BlClassRegistry', -] - class BlClassRegistry: class_list = [] diff --git a/uv_magic_uv/utils/compatibility.py b/uv_magic_uv/utils/compatibility.py new file mode 100644 index 0000000000000000000000000000000000000000..2ccd463cd7bf07283d3189cbf0a9156029240a6d --- /dev/null +++ b/uv_magic_uv/utils/compatibility.py @@ -0,0 +1,189 @@ +# <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 bgl +import blf + + +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 + + +def make_annotations(cls): + if check_version(2, 80, 0) < 0: + return cls + + # make annotation from attributes + props = {k: v for k, v in cls.__dict__.items() if isinstance(v, tuple)} + if props: + if '__annotations__' not in cls.__dict__: + setattr(cls, '__annotations__', {}) + annotations = cls.__dict__['__annotations__'] + for k, v in props.items(): + annotations[k] = v + delattr(cls, k) + + return cls + + +class ChangeRegionType: + def __init__(self, *_, **kwargs): + self.region_type = kwargs.get('region_type', False) + + def __call__(self, cls): + if check_version(2, 80, 0) >= 0: + return cls + + cls.bl_region_type = self.region_type + + return cls + + +def matmul(m1, m2): + if check_version(2, 80, 0) < 0: + return m1 * m2 + + return m1 @ m2 + + +def layout_split(layout, factor=0.0, align=False): + if check_version(2, 80, 0) < 0: + return layout.split(percentage=factor, align=align) + + return layout.split(factor=factor, align=align) + + +def get_user_preferences(context): + if hasattr(context, "user_preferences"): + return context.user_preferences + + return context.preferences + + +def get_object_select(obj): + if check_version(2, 80, 0) < 0: + return obj.select + + return obj.select_get() + + +def set_active_object(obj): + if check_version(2, 80, 0) < 0: + bpy.context.scene.objects.active = obj + else: + bpy.context.view_layer.objects.active = obj + + +def get_active_object(context): + if check_version(2, 80, 0) >= 0: + return context.scene.active_object + else: + return context.active_object + + +def object_has_uv_layers(obj): + if check_version(2, 80, 0) < 0: + return hasattr(obj.data, "uv_textures") + else: + return hasattr(obj.data, "uv_layers") + + +def get_object_uv_layers(obj): + if check_version(2, 80, 0) < 0: + return obj.data.uv_textures + else: + return obj.data.uv_layers + + +def icon(icon): + if icon == 'IMAGE': + if check_version(2, 80, 0) < 0: + return 'IMAGE_COL' + + return icon + + +def set_blf_font_color(font_id, r, g, b, a): + if check_version(2, 80, 0) >= 0: + blf.color(font_id, r, g, b, a) + else: + bgl.glColor4f(r, g, b, a) + + +def set_blf_blur(font_id, radius): + if check_version(2, 80, 0) < 0: + blf.blur(font_id, radius) + + +def get_all_space_types(): + if check_version(2, 80, 0) >= 0: + return { + 'CLIP_EDITOR': bpy.types.SpaceClipEditor, + 'CONSOLE': bpy.types.SpaceConsole, + 'DOPESHEET_EDITOR': bpy.types.SpaceDopeSheetEditor, + 'FILE_BROWSER': bpy.types.SpaceFileBrowser, + 'GRAPH_EDITOR': bpy.types.SpaceGraphEditor, + 'IMAGE_EDITOR': bpy.types.SpaceImageEditor, + 'INFO': bpy.types.SpaceInfo, + 'NLA_EDITOR': bpy.types.SpaceNLA, + 'NODE_EDITOR': bpy.types.SpaceNodeEditor, + 'OUTLINER': bpy.types.SpaceOutliner, + 'PROPERTIES': bpy.types.SpaceProperties, + 'SEQUENCE_EDITOR': bpy.types.SpaceSequenceEditor, + 'TEXT_EDITOR': bpy.types.SpaceTextEditor, + 'USER_PREFERENCES': bpy.types.SpacePreferences, + 'VIEW_3D': bpy.types.SpaceView3D, + } + else: + return { + 'VIEW_3D': bpy.types.SpaceView3D, + 'TIMELINE': bpy.types.SpaceTimeline, + 'GRAPH_EDITOR': bpy.types.SpaceGraphEditor, + 'DOPESHEET_EDITOR': bpy.types.SpaceDopeSheetEditor, + 'NLA_EDITOR': bpy.types.SpaceNLA, + 'IMAGE_EDITOR': bpy.types.SpaceImageEditor, + 'SEQUENCE_EDITOR': bpy.types.SpaceSequenceEditor, + 'CLIP_EDITOR': bpy.types.SpaceClipEditor, + 'TEXT_EDITOR': bpy.types.SpaceTextEditor, + 'NODE_EDITOR': bpy.types.SpaceNodeEditor, + 'LOGIC_EDITOR': bpy.types.SpaceLogicEditor, + 'PROPERTIES': bpy.types.SpaceProperties, + 'OUTLINER': bpy.types.SpaceOutliner, + 'USER_PREFERENCES': bpy.types.SpaceUserPreferences, + 'INFO': bpy.types.SpaceInfo, + 'FILE_BROWSER': bpy.types.SpaceFileBrowser, + 'CONSOLE': bpy.types.SpaceConsole, + } diff --git a/uv_magic_uv/utils/property_class_registry.py b/uv_magic_uv/utils/property_class_registry.py index 20df03422af86198fba12c33d3d493b0a9fabd66..4e89da5409f0e773da79ed8f5782e9ddba96e52f 100644 --- a/uv_magic_uv/utils/property_class_registry.py +++ b/uv_magic_uv/utils/property_class_registry.py @@ -25,10 +25,6 @@ __date__ = "17 Nov 2018" from .. import common -__all__ = [ - 'PropertyClassRegistry', -] - class PropertyClassRegistry: class_list = []