diff --git a/uv_magic_uv/__init__.py b/uv_magic_uv/__init__.py
index 171a5ac4fb66aa7debfe332f441fd14f1ddf6a3d..97f8bb79150016635333a61c1c31e41e906385b1 100644
--- a/uv_magic_uv/__init__.py
+++ b/uv_magic_uv/__init__.py
@@ -20,18 +20,18 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 
 bl_info = {
     "name": "Magic UV",
-    "author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, "
+    "author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, imdjs"
               "Keith (Wahooney) Boshoff, McBuff, MaxRobinot, Alexander Milovsky",
-    "version": (4, 5, 0),
+    "version": (5, 0, 0),
     "blender": (2, 79, 0),
     "location": "See Add-ons Preferences",
-    "description": "UV Manipulator Tools. See Add-ons Preferences for details",
+    "description": "UV Toolset. See Add-ons Preferences for details",
     "warning": "",
     "support": "COMMUNITY",
     "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
@@ -42,98 +42,29 @@ bl_info = {
 
 if "bpy" in locals():
     import importlib
-    importlib.reload(muv_preferences)
-    importlib.reload(muv_menu)
-    importlib.reload(muv_common)
-    importlib.reload(muv_props)
-    importlib.reload(muv_cpuv_ops)
-    importlib.reload(muv_cpuv_selseq_ops)
-    importlib.reload(muv_fliprot_ops)
-    importlib.reload(muv_transuv_ops)
-    importlib.reload(muv_uvbb_ops)
-    importlib.reload(muv_mvuv_ops)
-    importlib.reload(muv_texproj_ops)
-    importlib.reload(muv_packuv_ops)
-    importlib.reload(muv_texlock_ops)
-    importlib.reload(muv_mirroruv_ops)
-    importlib.reload(muv_wsuv_ops)
-    importlib.reload(muv_unwrapconst_ops)
-    importlib.reload(muv_preserve_uv_aspect)
-    importlib.reload(muv_uvw_ops)
+    importlib.reload(op)
+    importlib.reload(ui)
+    importlib.reload(common)
+    importlib.reload(preferences)
+    importlib.reload(properites)
 else:
-    from . import muv_preferences
-    from . import muv_menu
-    from . import muv_common
-    from . import muv_props
-    from . import muv_cpuv_ops
-    from . import muv_cpuv_selseq_ops
-    from . import muv_fliprot_ops
-    from . import muv_transuv_ops
-    from . import muv_uvbb_ops
-    from . import muv_mvuv_ops
-    from . import muv_texproj_ops
-    from . import muv_packuv_ops
-    from . import muv_texlock_ops
-    from . import muv_mirroruv_ops
-    from . import muv_wsuv_ops
-    from . import muv_unwrapconst_ops
-    from . import muv_preserve_uv_aspect
-    from . import muv_uvw_ops
+    from . import op
+    from . import ui
+    from . import common
+    from . import preferences
+    from . import properites
 
 import bpy
 
 
-def view3d_uvmap_menu_fn(self, context):
-    self.layout.separator()
-    self.layout.menu(muv_menu.MUV_CPUVMenu.bl_idname, icon="IMAGE_COL")
-    self.layout.operator(
-        muv_fliprot_ops.MUV_FlipRot.bl_idname, icon="IMAGE_COL")
-    self.layout.menu(muv_menu.MUV_TransUVMenu.bl_idname, icon="IMAGE_COL")
-    self.layout.operator(muv_mvuv_ops.MUV_MVUV.bl_idname, icon="IMAGE_COL")
-    self.layout.menu(muv_menu.MUV_TexLockMenu.bl_idname, icon="IMAGE_COL")
-    self.layout.operator(
-        muv_mirroruv_ops.MUV_MirrorUV.bl_idname, icon="IMAGE_COL")
-    self.layout.menu(muv_menu.MUV_WSUVMenu.bl_idname, icon="IMAGE_COL")
-    self.layout.operator(
-        muv_unwrapconst_ops.MUV_UnwrapConstraint.bl_idname, icon='IMAGE_COL')
-    self.layout.menu(
-        muv_preserve_uv_aspect.MUV_PreserveUVAspectMenu.bl_idname,
-        icon='IMAGE_COL')
-    self.layout.menu(muv_menu.MUV_UVWMenu.bl_idname, icon="IMAGE_COL")
-
-
-def image_uvs_menu_fn(self, context):
-    self.layout.separator()
-    self.layout.operator(muv_packuv_ops.MUV_PackUV.bl_idname, icon="IMAGE_COL")
-
-
-def view3d_object_menu_fn(self, context):
-    self.layout.separator()
-    self.layout.menu(muv_menu.MUV_CPUVObjMenu.bl_idname, icon="IMAGE_COL")
-
-
 def register():
     bpy.utils.register_module(__name__)
-    bpy.types.VIEW3D_MT_uv_map.append(view3d_uvmap_menu_fn)
-    bpy.types.IMAGE_MT_uvs.append(image_uvs_menu_fn)
-    bpy.types.VIEW3D_MT_object.append(view3d_object_menu_fn)
-    try:
-        bpy.types.VIEW3D_MT_Object.append(view3d_object_menu_fn)
-    except:
-        pass
-    muv_props.init_props(bpy.types.Scene)
+    properites.init_props(bpy.types.Scene)
 
 
 def unregister():
     bpy.utils.unregister_module(__name__)
-    bpy.types.VIEW3D_MT_uv_map.remove(view3d_uvmap_menu_fn)
-    bpy.types.IMAGE_MT_uvs.remove(image_uvs_menu_fn)
-    bpy.types.VIEW3D_MT_object.remove(view3d_object_menu_fn)
-    try:
-        bpy.types.VIEW3D_MT_Object.remove(view3d_object_menu_fn)
-    except:
-        pass
-    muv_props.clear_props(bpy.types.Scene)
+    properites.clear_props(bpy.types.Scene)
 
 
 if __name__ == "__main__":
diff --git a/uv_magic_uv/common.py b/uv_magic_uv/common.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc8876a05e1fc8408a940721df0e71d92214703c
--- /dev/null
+++ b/uv_magic_uv/common.py
@@ -0,0 +1,592 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+from collections import defaultdict
+from pprint import pprint
+from math import fabs, sqrt
+
+import bpy
+from mathutils import Vector
+import bmesh
+
+
+DEBUG = False
+
+
+def debug_print(*s):
+    """
+    Print message to console in debugging mode
+    """
+
+    if DEBUG:
+        pprint(s)
+
+
+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 redraw_all_areas():
+    """
+    Redraw all areas
+    """
+
+    for area in bpy.context.screen.areas:
+        area.tag_redraw()
+
+
+def get_space(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 __get_island_info(uv_layer, islands):
+    """
+    get information about each island
+    """
+
+    island_info = []
+    for isl in islands:
+        info = {}
+        max_uv = Vector((-10000000.0, -10000000.0))
+        min_uv = Vector((10000000.0, 10000000.0))
+        ave_uv = Vector((0.0, 0.0))
+        num_uv = 0
+        for face in isl:
+            n = 0
+            a = Vector((0.0, 0.0))
+            ma = Vector((-10000000.0, -10000000.0))
+            mi = Vector((10000000.0, 10000000.0))
+            for l in face['face'].loops:
+                uv = l[uv_layer].uv
+                ma.x = max(uv.x, ma.x)
+                ma.y = max(uv.y, ma.y)
+                mi.x = min(uv.x, mi.x)
+                mi.y = min(uv.y, mi.y)
+                a = a + uv
+                n = n + 1
+            ave_uv = ave_uv + a
+            num_uv = num_uv + n
+            a = a / n
+            max_uv.x = max(ma.x, max_uv.x)
+            max_uv.y = max(ma.y, max_uv.y)
+            min_uv.x = min(mi.x, min_uv.x)
+            min_uv.y = min(mi.y, min_uv.y)
+            face['max_uv'] = ma
+            face['min_uv'] = mi
+            face['ave_uv'] = a
+        ave_uv = ave_uv / num_uv
+
+        info['center'] = ave_uv
+        info['size'] = max_uv - min_uv
+        info['num_uv'] = num_uv
+        info['group'] = -1
+        info['faces'] = isl
+        info['max'] = max_uv
+        info['min'] = min_uv
+
+        island_info.append(info)
+
+    return island_info
+
+
+def __parse_island(bm, face_idx, faces_left, island,
+                   face_to_verts, vert_to_faces):
+    """
+    Parse island
+    """
+
+    if face_idx in faces_left:
+        faces_left.remove(face_idx)
+        island.append({'face': bm.faces[face_idx]})
+        for v in face_to_verts[face_idx]:
+            connected_faces = vert_to_faces[v]
+            if connected_faces:
+                for cf in connected_faces:
+                    __parse_island(bm, cf, faces_left, island, face_to_verts,
+                                   vert_to_faces)
+
+
+def __get_island(bm, face_to_verts, vert_to_faces):
+    """
+    Get island list
+    """
+
+    uv_island_lists = []
+    faces_left = set(face_to_verts.keys())
+    while faces_left:
+        current_island = []
+        face_idx = list(faces_left)[0]
+        __parse_island(bm, face_idx, faces_left, current_island,
+                       face_to_verts, vert_to_faces)
+        uv_island_lists.append(current_island)
+
+    return uv_island_lists
+
+
+def __create_vert_face_db(faces, uv_layer):
+    # create mesh database for all faces
+    face_to_verts = defaultdict(set)
+    vert_to_faces = defaultdict(set)
+    for f in faces:
+        for l in f.loops:
+            id_ = l[uv_layer].uv.to_tuple(5), l.vert.index
+            face_to_verts[f.index].add(id_)
+            vert_to_faces[id_].add(f.index)
+
+    return (face_to_verts, vert_to_faces)
+
+
+def get_island_info(obj, only_selected=True):
+    bm = bmesh.from_edit_mesh(obj.data)
+    if check_version(2, 73, 0) >= 0:
+        bm.faces.ensure_lookup_table()
+
+    return get_island_info_from_bmesh(bm, only_selected)
+
+
+def get_island_info_from_bmesh(bm, only_selected=True):
+    if not bm.loops.layers.uv:
+        return None
+    uv_layer = bm.loops.layers.uv.verify()
+
+    # create database
+    if only_selected:
+        selected_faces = [f for f in bm.faces if f.select]
+    else:
+        selected_faces = [f for f in bm.faces]
+
+    return get_island_info_from_faces(bm, selected_faces, uv_layer)
+
+
+def get_island_info_from_faces(bm, faces, uv_layer):
+    ftv, vtf = __create_vert_face_db(faces, uv_layer)
+
+    # Get island information
+    uv_island_lists = __get_island(bm, ftv, vtf)
+    island_info = __get_island_info(uv_layer, uv_island_lists)
+
+    return island_info
+
+
+def get_uvimg_editor_board_size(area):
+    if area.spaces.active.image:
+        return area.spaces.active.image.size
+
+    return (255.0, 255.0)
+
+
+def calc_polygon_2d_area(points):
+    area = 0.0
+    for i, p1 in enumerate(points):
+        p2 = points[(i + 1) % len(points)]
+        v1 = p1 - points[0]
+        v2 = p2 - points[0]
+        a = v1.x * v2.y - v1.y * v2.x
+        area = area + a
+
+    return fabs(0.5 * area)
+
+
+def calc_polygon_3d_area(points):
+    area = 0.0
+    for i, p1 in enumerate(points):
+        p2 = points[(i + 1) % len(points)]
+        v1 = p1 - points[0]
+        v2 = p2 - points[0]
+        cx = v1.y * v2.z - v1.z * v2.y
+        cy = v1.z * v2.x - v1.x * v2.z
+        cz = v1.x * v2.y - v1.y * v2.x
+        a = sqrt(cx * cx + cy * cy + cz * cz)
+        area = area + a
+
+    return 0.5 * area
+
+
+def measure_mesh_area(obj):
+    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()
+
+    sel_faces = [f for f in bm.faces if f.select]
+
+    # measure
+    mesh_area = 0.0
+    for f in sel_faces:
+        verts = [l.vert.co for l in f.loops]
+        f_mesh_area = calc_polygon_3d_area(verts)
+        mesh_area = mesh_area + f_mesh_area
+
+    return mesh_area
+
+
+def measure_uv_area(obj):
+    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)
+        if tex_layer:
+            img = f[tex_layer].image
+            if not img:
+                return None
+            uv_area = uv_area + f_uv_area * img.size[0] * img.size[1]
+
+    return uv_area
+
+
+def diff_point_to_segment(a, b, p):
+    ab = b - a
+    normal_ab = ab.normalized()
+
+    ap = p - a
+    dist_ax = normal_ab.dot(ap)
+
+    # cross point
+    x = a + normal_ab * dist_ax
+
+    # difference between cross point and point
+    xp = p - x
+
+    return xp, x
+
+
+# get selected loop pair whose loops are connected each other
+def __get_loop_pairs(l, uv_layer):
+
+    def __get_loop_pairs_internal(l_, pairs_, uv_layer_, parsed_):
+        parsed_.append(l_)
+        for ll in l_.vert.link_loops:
+            # forward direction
+            lln = ll.link_loop_next
+            # if there is same pair, skip it
+            found = False
+            for p in pairs_:
+                if (ll in p) and (lln in p):
+                    found = True
+                    break
+            # two loops must be selected
+            if ll[uv_layer_].select and lln[uv_layer_].select:
+                if not found:
+                    pairs_.append([ll, lln])
+                if lln not in parsed_:
+                    __get_loop_pairs_internal(lln, pairs_, uv_layer_, parsed_)
+
+            # backward direction
+            llp = ll.link_loop_prev
+            # if there is same pair, skip it
+            found = False
+            for p in pairs_:
+                if (ll in p) and (llp in p):
+                    found = True
+                    break
+            # two loops must be selected
+            if ll[uv_layer_].select and llp[uv_layer_].select:
+                if not found:
+                    pairs_.append([ll, llp])
+                if llp not in parsed_:
+                    __get_loop_pairs_internal(llp, pairs_, uv_layer_, parsed_)
+
+    pairs = []
+    parsed = []
+    __get_loop_pairs_internal(l, pairs, uv_layer, parsed)
+
+    return pairs
+
+
+# sort pair by vertex
+# (v0, v1) - (v1, v2) - (v2, v3) ....
+def __sort_loop_pairs(uv_layer, pairs, closed):
+    rest = pairs
+    sorted_pairs = [rest[0]]
+    rest.remove(rest[0])
+
+    # prepend
+    while True:
+        p1 = sorted_pairs[0]
+        for p2 in rest:
+            if p1[0].vert == p2[0].vert:
+                sorted_pairs.insert(0, [p2[1], p2[0]])
+                rest.remove(p2)
+                break
+            elif p1[0].vert == p2[1].vert:
+                sorted_pairs.insert(0, [p2[0], p2[1]])
+                rest.remove(p2)
+                break
+        else:
+            break
+
+    # append
+    while True:
+        p1 = sorted_pairs[-1]
+        for p2 in rest:
+            if p1[1].vert == p2[0].vert:
+                sorted_pairs.append([p2[0], p2[1]])
+                rest.remove(p2)
+                break
+            elif p1[1].vert == p2[1].vert:
+                sorted_pairs.append([p2[1], p2[0]])
+                rest.remove(p2)
+                break
+        else:
+            break
+
+    begin_vert = sorted_pairs[0][0].vert
+    end_vert = sorted_pairs[-1][-1].vert
+    if begin_vert != end_vert:
+        return sorted_pairs, ""
+    if closed and (begin_vert == end_vert):
+        # if the sequence of UV is circular, it is ok
+        return sorted_pairs, ""
+
+    # if the begin vertex and the end vertex are same, search the UVs which
+    # are separated each other
+    tmp_pairs = sorted_pairs
+    for i, (p1, p2) in enumerate(zip(tmp_pairs[:-1], tmp_pairs[1:])):
+        diff = p2[0][uv_layer].uv - p1[-1][uv_layer].uv
+        if diff.length > 0.000000001:
+            # UVs are separated
+            sorted_pairs = tmp_pairs[i + 1:]
+            sorted_pairs.extend(tmp_pairs[:i + 1])
+            break
+    else:
+        p1 = tmp_pairs[0]
+        p2 = tmp_pairs[-1]
+        diff = p2[-1][uv_layer].uv - p1[0][uv_layer].uv
+        if diff.length < 0.000000001:
+            # all UVs are not separated
+            return None, "All UVs are not separted"
+
+    return sorted_pairs, ""
+
+
+# get index of the island group which includes loop
+def __get_island_group_include_loop(loop, island_info):
+    for i, isl in enumerate(island_info):
+        for f in isl['faces']:
+            for l in f['face'].loops:
+                if l == loop:
+                    return i    # found
+
+    return -1   # not found
+
+
+# get index of the island group which includes pair.
+# if island group is not same between loops, it will be invalid
+def __get_island_group_include_pair(pair, island_info):
+    l1_grp = __get_island_group_include_loop(pair[0], island_info)
+    if l1_grp == -1:
+        return -1   # not found
+
+    for p in pair[1:]:
+        l2_grp = __get_island_group_include_loop(p, island_info)
+        if (l2_grp == -1) or (l1_grp != l2_grp):
+            return -1   # not found or invalid
+
+    return l1_grp
+
+
+# x ---- x   <- next_loop_pair
+# |      |
+# o ---- o   <- pair
+def __get_next_loop_pair(pair):
+    lp = pair[0].link_loop_prev
+    if lp.vert == pair[1].vert:
+        lp = pair[0].link_loop_next
+        if lp.vert == pair[1].vert:
+            # no loop is found
+            return None
+
+    ln = pair[1].link_loop_next
+    if ln.vert == pair[0].vert:
+        ln = pair[1].link_loop_prev
+        if ln.vert == pair[0].vert:
+            # no loop is found
+            return None
+
+    # tri-face
+    if lp == ln:
+        return [lp]
+
+    # quad-face
+    return [lp, ln]
+
+
+# | ---- |
+# % ---- %   <- next_poly_loop_pair
+# x ---- x   <- next_loop_pair
+# |      |
+# o ---- o   <- pair
+def __get_next_poly_loop_pair(pair):
+    v1 = pair[0].vert
+    v2 = pair[1].vert
+    for l1 in v1.link_loops:
+        if l1 == pair[0]:
+            continue
+        for l2 in v2.link_loops:
+            if l2 == pair[1]:
+                continue
+            if l1.link_loop_next == l2:
+                return [l1, l2]
+            elif l1.link_loop_prev == l2:
+                return [l1, l2]
+
+    # no next poly loop is found
+    return None
+
+
+# get loop sequence in the same island
+def __get_loop_sequence_internal(uv_layer, pairs, island_info, closed):
+    loop_sequences = []
+    for pair in pairs:
+        seqs = [pair]
+        p = pair
+        isl_grp = __get_island_group_include_pair(pair, island_info)
+        if isl_grp == -1:
+            return None, "Can not find the island or invalid island"
+
+        while True:
+            nlp = __get_next_loop_pair(p)
+            if not nlp:
+                break       # no more loop pair
+            nlp_isl_grp = __get_island_group_include_pair(nlp, island_info)
+            if nlp_isl_grp != isl_grp:
+                break       # another island
+            for nlpl in nlp:
+                if nlpl[uv_layer].select:
+                    return None, "Do not select UV which does not belong to " \
+                                 "the end edge"
+
+            seqs.append(nlp)
+
+            # when face is triangle, it indicates CLOSED
+            if (len(nlp) == 1) and closed:
+                break
+
+            nplp = __get_next_poly_loop_pair(nlp)
+            if not nplp:
+                break       # no more loop pair
+            nplp_isl_grp = __get_island_group_include_pair(nplp, island_info)
+            if nplp_isl_grp != isl_grp:
+                break       # another island
+
+            # check if the UVs are already parsed.
+            # this check is needed for the mesh which has the circular
+            # sequence of the verticies
+            matched = False
+            for p1 in seqs:
+                p2 = nplp
+                if ((p1[0] == p2[0]) and (p1[1] == p2[1])) or \
+                   ((p1[0] == p2[1]) and (p1[1] == p2[0])):
+                    matched = True
+            if matched:
+                debug_print("This is a circular sequence")
+                break
+
+            for nlpl in nplp:
+                if nlpl[uv_layer].select:
+                    return None, "Do not select UV which does not belong to " \
+                                 "the end edge"
+
+            seqs.append(nplp)
+
+            p = nplp
+
+        loop_sequences.append(seqs)
+    return loop_sequences, ""
+
+
+def get_loop_sequences(bm, uv_layer):
+    sel_faces = [f for f in bm.faces if f.select]
+
+    # get candidate loops
+    cand_loops = []
+    for f in sel_faces:
+        for l in f.loops:
+            if l[uv_layer].select:
+                cand_loops.append(l)
+
+    if len(cand_loops) < 2:
+        return None, "More than 2 UVs must be selected"
+
+    first_loop = cand_loops[0]
+    isl_info = get_island_info_from_bmesh(bm, False)
+    loop_pairs = __get_loop_pairs(first_loop, uv_layer)
+    loop_pairs, err = __sort_loop_pairs(uv_layer, loop_pairs, False)
+    if not loop_pairs:
+        return None, err
+    loop_seqs, err = __get_loop_sequence_internal(uv_layer, loop_pairs,
+                                                  isl_info, False)
+    if not loop_seqs:
+        return None, err
+
+    return loop_seqs, ""
diff --git a/uv_magic_uv/muv_common.py b/uv_magic_uv/muv_common.py
deleted file mode 100644
index b52971ec3080d21fe05386e15a6f08748725d1d7..0000000000000000000000000000000000000000
--- a/uv_magic_uv/muv_common.py
+++ /dev/null
@@ -1,83 +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__ = "4.5"
-__date__ = "19 Nov 2017"
-
-import bpy
-from . import muv_props
-
-
-def debug_print(*s):
-    """
-    Print message to console in debugging mode
-    """
-
-    if muv_props.DEBUG:
-        print(s)
-
-
-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 redraw_all_areas():
-    """
-    Redraw all areas
-    """
-
-    for area in bpy.context.screen.areas:
-        area.tag_redraw()
-
-
-def get_space(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)
diff --git a/uv_magic_uv/muv_cpuv_selseq_ops.py b/uv_magic_uv/muv_cpuv_selseq_ops.py
deleted file mode 100644
index 3cf69ff76de3b079f01291ef72e70868263d4927..0000000000000000000000000000000000000000
--- a/uv_magic_uv/muv_cpuv_selseq_ops.py
+++ /dev/null
@@ -1,279 +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__ = "4.5"
-__date__ = "19 Nov 2017"
-
-import bpy
-import bmesh
-from bpy.props import (
-    StringProperty,
-    BoolProperty,
-    IntProperty,
-    EnumProperty,
-)
-from . import muv_common
-
-
-class MUV_CPUVSelSeqCopyUV(bpy.types.Operator):
-    """
-    Operation class: Copy UV coordinate by selection sequence
-    """
-
-    bl_idname = "uv.muv_cpuv_selseq_copy_uv"
-    bl_label = "Copy UV (Selection Sequence) (Operation)"
-    bl_description = "Copy UV data by selection sequence (Operation)"
-    bl_options = {'REGISTER', 'UNDO'}
-
-    uv_map = StringProperty(options={'HIDDEN'})
-
-    def execute(self, context):
-        props = context.scene.muv_props.cpuv_selseq
-        if self.uv_map == "":
-            self.report({'INFO'}, "Copy UV coordinate (selection sequence)")
-        else:
-            self.report(
-                {'INFO'},
-                "Copy UV coordinate (selection sequence) (UV map:%s)"
-                % (self.uv_map))
-        obj = context.active_object
-        bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
-            bm.faces.ensure_lookup_table()
-
-        # get UV layer
-        if self.uv_map == "":
-            if not bm.loops.layers.uv:
-                self.report(
-                    {'WARNING'}, "Object must have more than one UV map")
-                return {'CANCELLED'}
-            uv_layer = bm.loops.layers.uv.verify()
-        else:
-            uv_layer = bm.loops.layers.uv[self.uv_map]
-
-        # get selected face
-        props.src_uvs = []
-        props.src_pin_uvs = []
-        props.src_seams = []
-        for hist in bm.select_history:
-            if isinstance(hist, bmesh.types.BMFace) and hist.select:
-                uvs = [l[uv_layer].uv.copy() for l in hist.loops]
-                pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
-                seams = [l.edge.seam for l in hist.loops]
-                props.src_uvs.append(uvs)
-                props.src_pin_uvs.append(pin_uvs)
-                props.src_seams.append(seams)
-        if not props.src_uvs or not props.src_pin_uvs:
-            self.report({'WARNING'}, "No faces are selected")
-            return {'CANCELLED'}
-        self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs))
-
-        return {'FINISHED'}
-
-
-class MUV_CPUVSelSeqCopyUVMenu(bpy.types.Menu):
-    """
-    Menu class: Copy UV coordinate by selection sequence
-    """
-
-    bl_idname = "uv.muv_cpuv_selseq_copy_uv_menu"
-    bl_label = "Copy UV (Selection Sequence)"
-    bl_description = "Copy UV coordinate by selection sequence"
-
-    def draw(self, context):
-        layout = self.layout
-        obj = context.active_object
-        bm = bmesh.from_edit_mesh(obj.data)
-        uv_maps = bm.loops.layers.uv.keys()
-        layout.operator(
-            MUV_CPUVSelSeqCopyUV.bl_idname,
-            text="[Default]", icon="IMAGE_COL").uv_map = ""
-        for m in uv_maps:
-            layout.operator(
-                MUV_CPUVSelSeqCopyUV.bl_idname,
-                text=m, icon="IMAGE_COL").uv_map = m
-
-
-class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
-    """
-    Operation class: Paste UV coordinate by selection sequence
-    """
-
-    bl_idname = "uv.muv_cpuv_selseq_paste_uv"
-    bl_label = "Paste UV (Selection Sequence) (Operation)"
-    bl_description = "Paste UV coordinate by selection sequence (Operation)"
-    bl_options = {'REGISTER', 'UNDO'}
-
-    uv_map = StringProperty(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="Copy Seams",
-        description="Copy Seams",
-        default=True
-    )
-
-    def execute(self, context):
-        props = context.scene.muv_props.cpuv_selseq
-        if not props.src_uvs or not props.src_pin_uvs:
-            self.report({'WARNING'}, "Need copy UV at first")
-            return {'CANCELLED'}
-        if self.uv_map == "":
-            self.report({'INFO'}, "Paste UV coordinate (selection sequence)")
-        else:
-            self.report(
-                {'INFO'},
-                "Paste UV coordinate (selection sequence) (UV map:%s)"
-                % (self.uv_map))
-
-        obj = context.active_object
-        bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
-            bm.faces.ensure_lookup_table()
-
-        # get UV layer
-        if self.uv_map == "":
-            if not bm.loops.layers.uv:
-                self.report(
-                    {'WARNING'}, "Object must have more than one UV map")
-                return {'CANCELLED'}
-            uv_layer = bm.loops.layers.uv.verify()
-        else:
-            uv_layer = bm.loops.layers.uv[self.uv_map]
-
-        # get selected face
-        dest_uvs = []
-        dest_pin_uvs = []
-        dest_seams = []
-        dest_face_indices = []
-        for hist in bm.select_history:
-            if isinstance(hist, bmesh.types.BMFace) and hist.select:
-                dest_face_indices.append(hist.index)
-                uvs = [l[uv_layer].uv.copy() for l in hist.loops]
-                pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
-                seams = [l.edge.seam for l in hist.loops]
-                dest_uvs.append(uvs)
-                dest_pin_uvs.append(pin_uvs)
-                dest_seams.append(seams)
-        if not dest_uvs or not dest_pin_uvs:
-            self.report({'WARNING'}, "No faces are selected")
-            return {'CANCELLED'}
-        if self.strategy == 'N_N' and len(props.src_uvs) != len(dest_uvs):
-            self.report(
-                {'WARNING'},
-                "Number of selected faces is different from copied faces " +
-                "(src:%d, dest:%d)"
-                % (len(props.src_uvs), len(dest_uvs)))
-            return {'CANCELLED'}
-
-        # paste
-        for i, idx in enumerate(dest_face_indices):
-            suv = None
-            spuv = None
-            ss = None
-            duv = None
-            if self.strategy == 'N_N':
-                suv = props.src_uvs[i]
-                spuv = props.src_pin_uvs[i]
-                ss = props.src_seams[i]
-                duv = dest_uvs[i]
-            elif self.strategy == 'N_M':
-                suv = props.src_uvs[i % len(props.src_uvs)]
-                spuv = props.src_pin_uvs[i % len(props.src_pin_uvs)]
-                ss = props.src_seams[i % len(props.src_seams)]
-                duv = dest_uvs[i]
-            if len(suv) != len(duv):
-                self.report({'WARNING'}, "Some faces are different size")
-                return {'CANCELLED'}
-            suvs_fr = [uv for uv in suv]
-            spuvs_fr = [pin_uv for pin_uv in spuv]
-            ss_fr = [s for s in ss]
-            # flip UVs
-            if self.flip_copied_uv is True:
-                suvs_fr.reverse()
-                spuvs_fr.reverse()
-                ss_fr.reverse()
-            # rotate UVs
-            for _ in range(self.rotate_copied_uv):
-                uv = suvs_fr.pop()
-                pin_uv = spuvs_fr.pop()
-                s = ss_fr.pop()
-                suvs_fr.insert(0, uv)
-                spuvs_fr.insert(0, pin_uv)
-                ss_fr.insert(0, s)
-            # paste UVs
-            for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr,
-                                        spuvs_fr, ss_fr):
-                l[uv_layer].uv = suv
-                l[uv_layer].pin_uv = spuv
-                if self.copy_seams is True:
-                    l.edge.seam = ss
-
-        self.report({'INFO'}, "%d face(s) are copied" % len(dest_uvs))
-
-        bmesh.update_edit_mesh(obj.data)
-        if self.copy_seams is True:
-            obj.data.show_edge_seams = True
-
-        return {'FINISHED'}
-
-
-class MUV_CPUVSelSeqPasteUVMenu(bpy.types.Menu):
-    """
-    Menu class: Paste UV coordinate by selection sequence
-    """
-
-    bl_idname = "uv.muv_cpuv_selseq_paste_uv_menu"
-    bl_label = "Paste UV (Selection Sequence)"
-    bl_description = "Paste UV coordinate by selection sequence"
-
-    def draw(self, context):
-        layout = self.layout
-        # create sub menu
-        obj = context.active_object
-        bm = bmesh.from_edit_mesh(obj.data)
-        uv_maps = bm.loops.layers.uv.keys()
-        layout.operator(
-            MUV_CPUVSelSeqPasteUV.bl_idname,
-            text="[Default]", icon="IMAGE_COL").uv_map = ""
-        for m in uv_maps:
-            layout.operator(
-                MUV_CPUVSelSeqPasteUV.bl_idname,
-                text=m, icon="IMAGE_COL").uv_map = m
diff --git a/uv_magic_uv/muv_menu.py b/uv_magic_uv/muv_menu.py
deleted file mode 100644
index 47c79bbd1b217409a44c1197aaae82a25a612a89..0000000000000000000000000000000000000000
--- a/uv_magic_uv/muv_menu.py
+++ /dev/null
@@ -1,138 +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__ = "4.5"
-__date__ = "19 Nov 2017"
-
-import bpy
-from . import muv_cpuv_ops
-from . import muv_cpuv_selseq_ops
-from . import muv_transuv_ops
-from . import muv_texlock_ops
-from . import muv_wsuv_ops
-from . import muv_uvw_ops
-
-
-class MUV_CPUVMenu(bpy.types.Menu):
-    """
-    Menu class: Master menu of Copy/Paste UV coordinate
-    """
-
-    bl_idname = "uv.muv_cpuv_menu"
-    bl_label = "Copy/Paste UV"
-    bl_description = "Copy and Paste UV coordinate"
-
-    def draw(self, _):
-        self.layout.menu(
-            muv_cpuv_ops.MUV_CPUVCopyUVMenu.bl_idname, icon="IMAGE_COL")
-        self.layout.menu(
-            muv_cpuv_ops.MUV_CPUVPasteUVMenu.bl_idname, icon="IMAGE_COL")
-        self.layout.menu(
-            muv_cpuv_selseq_ops.MUV_CPUVSelSeqCopyUVMenu.bl_idname,
-            icon="IMAGE_COL")
-        self.layout.menu(
-            muv_cpuv_selseq_ops.MUV_CPUVSelSeqPasteUVMenu.bl_idname,
-            icon="IMAGE_COL")
-
-
-class MUV_CPUVObjMenu(bpy.types.Menu):
-    """
-    Menu class: Master menu of Copy/Paste UV coordinate per object
-    """
-
-    bl_idname = "object.muv_cpuv_obj_menu"
-    bl_label = "Copy/Paste UV"
-    bl_description = "Copy and Paste UV coordinate per object"
-
-    def draw(self, _):
-        self.layout.menu(
-            muv_cpuv_ops.MUV_CPUVObjCopyUVMenu.bl_idname, icon="IMAGE_COL")
-        self.layout.menu(
-            muv_cpuv_ops.MUV_CPUVObjPasteUVMenu.bl_idname, icon="IMAGE_COL")
-
-
-class MUV_TransUVMenu(bpy.types.Menu):
-    """
-    Menu class: Master menu of Transfer UV coordinate
-    """
-
-    bl_idname = "uv.muv_transuv_menu"
-    bl_label = "Transfer UV"
-    bl_description = "Transfer UV coordinate"
-
-    def draw(self, _):
-        self.layout.operator(
-            muv_transuv_ops.MUV_TransUVCopy.bl_idname, icon="IMAGE_COL")
-        self.layout.operator(
-            muv_transuv_ops.MUV_TransUVPaste.bl_idname, icon="IMAGE_COL")
-
-
-class MUV_TexLockMenu(bpy.types.Menu):
-    """
-    Menu class: Master menu of Texture Lock
-    """
-
-    bl_idname = "uv.muv_texlock_menu"
-    bl_label = "Texture Lock"
-    bl_description = "Lock texture when vertices of mesh (Preserve UV)"
-
-    def draw(self, _):
-        self.layout.operator(
-            muv_texlock_ops.MUV_TexLockStart.bl_idname, icon="IMAGE_COL")
-        self.layout.operator(
-            muv_texlock_ops.MUV_TexLockStop.bl_idname, icon="IMAGE_COL")
-        self.layout.operator(
-            muv_texlock_ops.MUV_TexLockIntrStart.bl_idname, icon="IMAGE_COL")
-        self.layout.operator(
-            muv_texlock_ops.MUV_TexLockIntrStop.bl_idname, icon="IMAGE_COL")
-
-
-class MUV_WSUVMenu(bpy.types.Menu):
-    """
-    Menu class: Master menu of world scale UV
-    """
-
-    bl_idname = "uv.muv_wsuv_menu"
-    bl_label = "World Scale UV"
-    bl_description = ""
-
-    def draw(self, _):
-        self.layout.operator(
-            muv_wsuv_ops.MUV_WSUVMeasure.bl_idname, icon="IMAGE_COL")
-        self.layout.operator(
-            muv_wsuv_ops.MUV_WSUVApply.bl_idname, icon="IMAGE_COL")
-
-
-class MUV_UVWMenu(bpy.types.Menu):
-    """
-    Menu class: Master menu of UVW
-    """
-
-    bl_idname = "uv.muv_uvw_menu"
-    bl_label = "UVW"
-    bl_description = ""
-
-    def draw(self, _):
-        self.layout.operator(
-            muv_uvw_ops.MUV_UVWBoxMap.bl_idname, icon="IMAGE_COL")
-        self.layout.operator(
-            muv_uvw_ops.MUV_UVWBestPlanerMap.bl_idname, icon="IMAGE_COL")
diff --git a/uv_magic_uv/muv_preferences.py b/uv_magic_uv/muv_preferences.py
deleted file mode 100644
index e14ce99bd24e1caf7303537bd8d834d9ebf9dcf7..0000000000000000000000000000000000000000
--- a/uv_magic_uv/muv_preferences.py
+++ /dev/null
@@ -1,144 +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__ = "4.5"
-__date__ = "19 Nov 2017"
-
-from bpy.props import (
-    BoolProperty,
-    FloatProperty,
-    FloatVectorProperty,
-)
-from bpy.types import AddonPreferences
-
-
-class MUV_Preferences(AddonPreferences):
-    """Preferences class: Preferences for this add-on"""
-
-    bl_idname = __package__
-
-    # enable/disable switcher
-    enable_texproj = BoolProperty(
-        name="Texture Projection",
-        default=True)
-    enable_uvbb = BoolProperty(
-        name="Bounding Box",
-        default=True)
-
-    # for Texture Projection
-    texproj_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
-    uvbb_cp_size = FloatProperty(
-        name="Size",
-        description="Control Point Size",
-        default=6.0,
-        min=3.0,
-        max=100.0)
-    uvbb_cp_react_size = FloatProperty(
-        name="React Size",
-        description="Size event fired",
-        default=10.0,
-        min=3.0,
-        max=100.0)
-
-    def draw(self, _):
-        layout = self.layout
-
-        layout.label("Switch Enable/Disable and Configurate Features:")
-
-        layout.prop(self, "enable_texproj")
-        if self.enable_texproj:
-            sp = layout.split(percentage=0.05)
-            col = sp.column()       # spacer
-            sp = sp.split(percentage=0.3)
-            col = sp.column()
-            col.label("Texture Display: ")
-            col.prop(self, "texproj_canvas_padding")
-
-        layout.prop(self, "enable_uvbb")
-        if self.enable_uvbb:
-            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, "uvbb_cp_size")
-            col.prop(self, "uvbb_cp_react_size")
-
-        layout.label("Description:")
-        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.label("Location:")
-
-        row = layout.row(align=True)
-        sp = row.split(percentage=0.3)
-        sp.label("View3D > U")
-        sp = sp.split(percentage=1.0)
-        col = sp.column(align=True)
-        col.label("Copy/Paste UV Coordinates")
-        col.label("Copy/Paste UV Coordinates (by selection sequence)")
-        col.label("Flip/Rotate UVs")
-        col.label("Transfer UV")
-        col.label("Move UV from 3D View")
-        col.label("Texture Lock")
-        col.label("Mirror UV")
-        col.label("World Scale UV")
-        col.label("Unwrap Constraint")
-        col.label("Preserve UV Aspect")
-
-        row = layout.row(align=True)
-        sp = row.split(percentage=0.3)
-        sp.label("View3D > Object")
-        sp = sp.split(percentage=1.0)
-        col = sp.column(align=True)
-        col.label("Copy/Paste UV Coordinates (Among same objects)")
-
-        row = layout.row(align=True)
-        sp = row.split(percentage=0.3)
-        sp.label("ImageEditor > Property Panel")
-        sp = sp.split(percentage=1.0)
-        col = sp.column(align=True)
-        col.label("Manipulate UV with Bounding Box in UV Editor")
-
-        row = layout.row(align=True)
-        sp = row.split(percentage=0.3)
-        sp.label("View3D > Property Panel")
-        sp = sp.split(percentage=1.0)
-        col = sp.column(align=True)
-        col.label("Texture Projection")
-
-        row = layout.row(align=True)
-        sp = row.split(percentage=0.3)
-        sp.label("ImageEditor > UVs")
-        sp = sp.split(percentage=1.0)
-        col = sp.column(align=True)
-        col.label("Pack UV (with same UV island packing)")
diff --git a/uv_magic_uv/muv_props.py b/uv_magic_uv/muv_props.py
deleted file mode 100644
index c0a7d961ff9c54d2d2fcd93c8f42b0982e840652..0000000000000000000000000000000000000000
--- a/uv_magic_uv/muv_props.py
+++ /dev/null
@@ -1,148 +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__ = "4.5"
-__date__ = "19 Nov 2017"
-
-import bpy
-from bpy.props import (
-    FloatProperty,
-    EnumProperty,
-    BoolProperty,
-)
-
-
-DEBUG = False
-
-
-def get_loaded_texture_name(_, __):
-    items = [(key, key, "") for key in bpy.data.images.keys()]
-    items.append(("None", "None", ""))
-    return items
-
-
-# Properties used in this add-on.
-class MUV_Properties():
-    cpuv = None
-    cpuv_obj = None
-    cpuv_selseq = None
-    transuv = None
-    uvbb = None
-    texproj = None
-    texlock = None
-    texwrap = None
-    wsuv = None
-
-    def __init__(self):
-        self.cpuv = MUV_CPUVProps()
-        self.cpuv_obj = MUV_CPUVProps()
-        self.cpuv_selseq = MUV_CPUVSelSeqProps()
-        self.transuv = MUV_TransUVProps()
-        self.uvbb = MUV_UVBBProps()
-        self.texproj = MUV_TexProjProps()
-        self.texlock = MUV_TexLockProps()
-        self.texwrap = MUV_TexWrapProps()
-        self.wsuv = MUV_WSUVProps()
-
-
-class MUV_CPUVProps():
-    src_uvs = []
-    src_pin_uvs = []
-    src_seams = []
-
-
-class MUV_CPUVSelSeqProps():
-    src_uvs = []
-    src_pin_uvs = []
-    src_seams = []
-
-
-class MUV_TransUVProps():
-    topology_copied = []
-
-
-class MUV_UVBBProps():
-    uv_info_ini = []
-    ctrl_points_ini = []
-    ctrl_points = []
-    running = False
-
-
-class MUV_TexProjProps():
-    running = False
-
-
-class MUV_TexLockProps():
-    verts_orig = None
-    intr_verts_orig = None
-    intr_running = False
-
-
-class MUV_TexWrapProps():
-    src_face_index = -1
-
-
-class MUV_WSUVProps():
-    ref_sv = None
-    ref_suv = None
-
-
-def init_props(scene):
-    scene.muv_props = MUV_Properties()
-    scene.muv_uvbb_uniform_scaling = BoolProperty(
-        name="Uniform Scaling",
-        description="Enable Uniform Scaling",
-        default=False)
-    scene.muv_texproj_tex_magnitude = FloatProperty(
-        name="Magnitude",
-        description="Texture Magnitude",
-        default=0.5,
-        min=0.0,
-        max=100.0)
-    scene.muv_texproj_tex_image = EnumProperty(
-        name="Image",
-        description="Texture Image",
-        items=get_loaded_texture_name)
-    scene.muv_texproj_tex_transparency = FloatProperty(
-        name="Transparency",
-        description="Texture Transparency",
-        default=0.2,
-        min=0.0,
-        max=1.0)
-    scene.muv_texproj_adjust_window = BoolProperty(
-        name="Adjust Window",
-        description="Size of renderered texture is fitted to window",
-        default=True)
-    scene.muv_texproj_apply_tex_aspect = BoolProperty(
-        name="Texture Aspect Ratio",
-        description="Apply Texture Aspect ratio to displayed texture",
-        default=True)
-
-
-def clear_props(scene):
-    del scene.muv_props
-    del scene.muv_uvbb_uniform_scaling
-    del scene.muv_texproj_tex_magnitude
-    del scene.muv_texproj_tex_image
-    del scene.muv_texproj_tex_transparency
-    del scene.muv_texproj_adjust_window
-    del scene.muv_texproj_apply_tex_aspect
diff --git a/uv_magic_uv/op/__init__.py b/uv_magic_uv/op/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b93c96abddd9d0cd2359f86d468b6f18ac320e1
--- /dev/null
+++ b/uv_magic_uv/op/__init__.py
@@ -0,0 +1,72 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.0"
+__date__ = "16 Feb 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(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 smooth_uv
+    from . import texture_lock
+    from . import texture_projection
+    from . import texture_wrap
+    from . import transfer_uv
+    from . import unwrap_constraint
+    from . import uv_bounding_box
+    from . import uv_inspection
+    from . import uv_sculpt
+    from . import uvw
+    from . import world_scale_uv
+
+import bpy
diff --git a/uv_magic_uv/op/align_uv.py b/uv_magic_uv/op/align_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..f90f02ffad7144a1cae7cc81c5147baceb5550eb
--- /dev/null
+++ b/uv_magic_uv/op/align_uv.py
@@ -0,0 +1,784 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import math
+from math import atan2, tan, sin, cos
+
+import bpy
+import bmesh
+from mathutils import Vector
+from bpy.props import EnumProperty, BoolProperty
+
+from .. import common
+
+
+def get_closed_loop_sequences(bm, uv_layer):
+    sel_faces = [f for f in bm.faces if f.select]
+
+    # get candidate loops
+    cand_loops = []
+    for f in sel_faces:
+        for l in f.loops:
+            if l[uv_layer].select:
+                cand_loops.append(l)
+
+    if len(cand_loops) < 2:
+        return None, "More than 2 UVs must be selected"
+
+    first_loop = cand_loops[0]
+    isl_info = common.get_island_info_from_bmesh(bm, False)
+    loop_pairs = common.get_loop_pairs(first_loop, uv_layer)
+    loop_pairs, err = common.sort_loop_pairs(uv_layer, loop_pairs, True)
+    if not loop_pairs:
+        return None, err
+    loop_seqs, err = common.get_loop_sequence_internal(uv_layer, loop_pairs,
+                                                       isl_info, True)
+    if not loop_seqs:
+        return None, err
+
+    return loop_seqs, ""
+
+
+# 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 MUV_AUVCircle(bpy.types.Operator):
+
+    bl_idname = "uv.muv_auv_circle"
+    bl_label = "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
+    )
+
+    @classmethod
+    def poll(cls, context):
+        return context.mode == 'EDIT_MESH'
+
+    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()
+
+        # loop_seqs[horizontal][vertical][loop]
+        loop_seqs, error = get_closed_loop_sequences(bm, uv_layer)
+        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'}
+
+
+# get horizontal differential of UV influenced by mesh vertex
+def get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pair_idx):
+    common.debug_print(
+        "vidx={0}, hidx={1}, pair_idx={2}".format(vidx, hidx, pair_idx))
+
+    # get total vertex length
+    hloops = []
+    for s in loop_seqs:
+        hloops.extend([s[vidx][0], s[vidx][1]])
+    vert_total_hlen = get_loop_vert_len(hloops)
+    common.debug_print(vert_total_hlen)
+
+    # target vertex length
+    hloops = []
+    for s in loop_seqs[:hidx]:
+        hloops.extend([s[vidx][0], s[vidx][1]])
+    for pidx, l in enumerate(loop_seqs[hidx][vidx]):
+        if pidx > pair_idx:
+            break
+        hloops.append(l)
+    vert_hlen = get_loop_vert_len(hloops)
+    common.debug_print(vert_hlen)
+
+    # get total UV length
+    # uv_all_hdiff = loop_seqs[-1][0][-1][uv_layer].uv -
+    # loop_seqs[0][0][0][uv_layer].uv
+    uv_total_hlen = loop_seqs[-1][vidx][-1][uv_layer].uv -\
+        loop_seqs[0][vidx][0][uv_layer].uv
+    common.debug_print(uv_total_hlen)
+
+    return uv_total_hlen * vert_hlen / vert_total_hlen
+
+
+# get vertical differential of UV influenced by mesh vertex
+def get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pair_idx):
+    common.debug_print(
+        "vidx={0}, hidx={1}, pair_idx={2}".format(vidx, hidx, pair_idx))
+
+    # get total vertex length
+    hloops = []
+    for s in loop_seqs[hidx]:
+        hloops.append(s[pair_idx])
+    vert_total_hlen = get_loop_vert_len(hloops)
+    common.debug_print(vert_total_hlen)
+
+    # target vertex length
+    hloops = []
+    for s in loop_seqs[hidx][:vidx + 1]:
+        hloops.append(s[pair_idx])
+    vert_hlen = get_loop_vert_len(hloops)
+    common.debug_print(vert_hlen)
+
+    # get total UV length
+    # uv_all_hdiff = loop_seqs[0][-1][pair_idx][uv_layer].uv - \
+    #                loop_seqs[0][0][pair_idx][uv_layer].uv
+    uv_total_hlen = loop_seqs[hidx][-1][pair_idx][uv_layer].uv -\
+        loop_seqs[hidx][0][pair_idx][uv_layer].uv
+    common.debug_print(uv_total_hlen)
+
+    return uv_total_hlen * vert_hlen / vert_total_hlen
+
+
+# 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 MUV_AUVStraighten(bpy.types.Operator):
+
+    bl_idname = "uv.muv_auv_straighten"
+    bl_label = "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
+    )
+
+    @classmethod
+    def poll(cls, context):
+        return context.mode == 'EDIT_MESH'
+
+    # 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),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 0),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 1),
+                    ]
+                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),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 0),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 1),
+                    ]
+                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):
+        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'}
+
+
+class MUV_AUVAxis(bpy.types.Operator):
+
+    bl_idname = "uv.muv_auv_axis"
+    bl_label = "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'
+    )
+
+    @classmethod
+    def poll(cls, context):
+        return context.mode == 'EDIT_MESH'
+
+    # 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),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 0),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 1),
+                    ]
+                    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),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 0),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 1),
+                    ]
+                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),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 0),
+                        get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 1),
+                    ]
+                    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),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, 1),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 0),
+                        get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx + 1,
+                                           hidx, 1),
+                    ]
+                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):
+        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
new file mode 100644
index 0000000000000000000000000000000000000000..b33dc68ee0fe515285ca7858def4384b0f5bf022
--- /dev/null
+++ b/uv_magic_uv/op/align_uv_cursor.py
@@ -0,0 +1,154 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+from mathutils import Vector
+from bpy.props import EnumProperty
+import bmesh
+
+from .. import common
+
+
+class MUV_AUVCAlignOps(bpy.types.Operator):
+
+    bl_idname = "uv.muv_auvc_align"
+    bl_label = "Align"
+    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 execute(self, context):
+        area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW',
+                                          'IMAGE_EDITOR')
+        bd_size = common.get_uvimg_editor_board_size(area)
+
+        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")
+
+        if self.position == 'CENTER':
+            cx = center.x * bd_size[0]
+            cy = center.y * bd_size[1]
+        elif self.position == 'LEFT_TOP':
+            cx = min_.x * bd_size[0]
+            cy = max_.y * bd_size[1]
+        elif self.position == 'LEFT_MIDDLE':
+            cx = min_.x * bd_size[0]
+            cy = center.y * bd_size[1]
+        elif self.position == 'LEFT_BOTTOM':
+            cx = min_.x * bd_size[0]
+            cy = min_.y * bd_size[1]
+        elif self.position == 'MIDDLE_TOP':
+            cx = center.x * bd_size[0]
+            cy = max_.y * bd_size[1]
+        elif self.position == 'MIDDLE_BOTTOM':
+            cx = center.x * bd_size[0]
+            cy = min_.y * bd_size[1]
+        elif self.position == 'RIGHT_TOP':
+            cx = max_.x * bd_size[0]
+            cy = max_.y * bd_size[1]
+        elif self.position == 'RIGHT_MIDDLE':
+            cx = max_.x * bd_size[0]
+            cy = center.y * bd_size[1]
+        elif self.position == 'RIGHT_BOTTOM':
+            cx = max_.x * bd_size[0]
+            cy = min_.y * bd_size[1]
+        else:
+            self.report({'ERROR'}, "Unknown Operation")
+
+        space.cursor_location = Vector((cx, cy))
+
+        return {'FINISHED'}
diff --git a/uv_magic_uv/muv_cpuv_ops.py b/uv_magic_uv/op/copy_paste_uv.py
similarity index 50%
rename from uv_magic_uv/muv_cpuv_ops.py
rename to uv_magic_uv/op/copy_paste_uv.py
index 82f043c62d6cbc0b4161be42126b34cfd021954b..3aac3f6de675533da0b89658873dc80043bc676f 100644
--- a/uv_magic_uv/muv_cpuv_ops.py
+++ b/uv_magic_uv/op/copy_paste_uv.py
@@ -18,10 +18,13 @@
 #
 # ##### END GPL LICENSE BLOCK #####
 
-__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
+
+import math
+from math import atan2, sin, cos
 
 import bpy
 import bmesh
@@ -31,16 +34,9 @@ from bpy.props import (
     IntProperty,
     EnumProperty,
 )
-from . import muv_common
-
+from mathutils import Vector
 
-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
+from .. import common
 
 
 class MUV_CPUVCopyUV(bpy.types.Operator):
@@ -64,7 +60,7 @@ class MUV_CPUVCopyUV(bpy.types.Operator):
                 {'INFO'}, "Copy UV coordinate (UV map:%s)" % (self.uv_map))
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV layer
@@ -174,7 +170,7 @@ class MUV_CPUVPasteUV(bpy.types.Operator):
                 {'INFO'}, "Paste UV coordinate (UV map:%s)" % (self.uv_map))
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV layer
@@ -273,46 +269,158 @@ class MUV_CPUVPasteUVMenu(bpy.types.Menu):
     bl_description = "Paste UV coordinate"
 
     def draw(self, context):
+        sc = context.scene
         layout = self.layout
         # create sub menu
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
         uv_maps = bm.loops.layers.uv.keys()
-        layout.operator(
-            MUV_CPUVPasteUV.bl_idname,
-            text="[Default]", icon="IMAGE_COL").uv_map = ""
+        ops = layout.operator(MUV_CPUVPasteUV.bl_idname, text="[Default]")
+        ops.uv_map = ""
+        ops.copy_seams = sc.muv_cpuv_copy_seams
+        ops.strategy = sc.muv_cpuv_strategy
         for m in uv_maps:
-            layout.operator(
-                MUV_CPUVPasteUV.bl_idname,
-                text=m, icon="IMAGE_COL").uv_map = m
+            ops = layout.operator(MUV_CPUVPasteUV.bl_idname, text=m)
+            ops.uv_map = m
+            ops.copy_seams = sc.muv_cpuv_copy_seams
+            ops.strategy = sc.muv_cpuv_strategy
 
 
-class MUV_CPUVObjCopyUV(bpy.types.Operator):
+class MUV_CPUVIECopyUV(bpy.types.Operator):
     """
-    Operation class: Copy UV coordinate per object
+    Operation class: Copy UV coordinate on UV/Image Editor
     """
 
-    bl_idname = "object.muv_cpuv_obj_copy_uv"
+    bl_idname = "uv.muv_cpuv_ie_copy_uv"
     bl_label = "Copy UV"
-    bl_description = "Copy UV coordinate"
+    bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    @classmethod
+    def poll(cls, context):
+        return context.mode == 'EDIT_MESH'
+
+    def execute(self, context):
+        props = context.scene.muv_props.cpuv
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        uv_layer = bm.loops.layers.uv.verify()
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        for face in bm.faces:
+            if not face.select:
+                continue
+            skip = False
+            for l in face.loops:
+                if not l[uv_layer].select:
+                    skip = True
+                    break
+            if skip:
+                continue
+            props.src_uvs.append([l[uv_layer].uv.copy() for l in face.loops])
+
+        return {'FINISHED'}
+
+
+class MUV_CPUVIEPasteUV(bpy.types.Operator):
+    """
+    Operation class: Paste UV coordinate on UV/Image Editor
+    """
+
+    bl_idname = "uv.muv_cpuv_ie_paste_uv"
+    bl_label = "Paste UV"
+    bl_description = "Paste UV coordinate (only selected in UV/Image Editor)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    @classmethod
+    def poll(cls, context):
+        return context.mode == 'EDIT_MESH'
+
+    def execute(self, context):
+        props = context.scene.muv_props.cpuv
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        uv_layer = bm.loops.layers.uv.verify()
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        dest_uvs = []
+        dest_face_indices = []
+        for face in bm.faces:
+            if not face.select:
+                continue
+            skip = False
+            for l in face.loops:
+                if not l[uv_layer].select:
+                    skip = True
+                    break
+            if skip:
+                continue
+            dest_face_indices.append(face.index)
+            uvs = [l[uv_layer].uv.copy() for l in face.loops]
+            dest_uvs.append(uvs)
+
+        for suvs, duvs in zip(props.src_uvs, dest_uvs):
+            src_diff = suvs[1] - suvs[0]
+            dest_diff = duvs[1] - duvs[0]
+
+            src_base = suvs[0]
+            dest_base = duvs[0]
+
+            src_rad = atan2(src_diff.y, src_diff.x)
+            dest_rad = atan2(dest_diff.y, dest_diff.x)
+            if src_rad < dest_rad:
+                radian = dest_rad - src_rad
+            elif src_rad > dest_rad:
+                radian = math.pi * 2 - (src_rad - dest_rad)
+            else:       # src_rad == dest_rad
+                radian = 0.0
+
+            ratio = dest_diff.length / src_diff.length
+            break
+
+        for suvs, fidx in zip(props.src_uvs, dest_face_indices):
+            for l, suv in zip(bm.faces[fidx].loops, suvs):
+                base = suv - src_base
+                radian_ref = atan2(base.y, base.x)
+                radian_fin = (radian + radian_ref)
+                length = base.length
+                turn = Vector((length * cos(radian_fin),
+                               length * sin(radian_fin)))
+                target_uv = Vector((turn.x * ratio, turn.y * ratio)) + \
+                    dest_base
+                l[uv_layer].uv = target_uv
+
+        bmesh.update_edit_mesh(obj.data)
+
+        return {'FINISHED'}
+
+
+class MUV_CPUVSelSeqCopyUV(bpy.types.Operator):
+    """
+    Operation class: Copy UV coordinate by selection sequence
+    """
+
+    bl_idname = "uv.muv_cpuv_selseq_copy_uv"
+    bl_label = "Copy UV (Selection Sequence) (Operation)"
+    bl_description = "Copy UV data by selection sequence (Operation)"
     bl_options = {'REGISTER', 'UNDO'}
 
     uv_map = StringProperty(options={'HIDDEN'})
 
-    @memorize_view_3d_mode
     def execute(self, context):
-        props = context.scene.muv_props.cpuv_obj
+        props = context.scene.muv_props.cpuv_selseq
         if self.uv_map == "":
-            self.report({'INFO'}, "Copy UV coordinate per object")
+            self.report({'INFO'}, "Copy UV coordinate (selection sequence)")
         else:
             self.report(
                 {'INFO'},
-                "Copy UV coordinate per object (UV map:%s)" % (self.uv_map))
-        bpy.ops.object.mode_set(mode='EDIT')
-
+                "Copy UV coordinate (selection sequence) (UV map:%s)"
+                % (self.uv_map))
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV layer
@@ -329,171 +437,210 @@ class MUV_CPUVObjCopyUV(bpy.types.Operator):
         props.src_uvs = []
         props.src_pin_uvs = []
         props.src_seams = []
-        for face in bm.faces:
-            uvs = [l[uv_layer].uv.copy() for l in face.loops]
-            pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
-            seams = [l.edge.seam for l in face.loops]
-            props.src_uvs.append(uvs)
-            props.src_pin_uvs.append(pin_uvs)
-            props.src_seams.append(seams)
-
-        self.report({'INFO'}, "%s's UV coordinates are copied" % (obj.name))
+        for hist in bm.select_history:
+            if isinstance(hist, bmesh.types.BMFace) and hist.select:
+                uvs = [l[uv_layer].uv.copy() for l in hist.loops]
+                pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
+                seams = [l.edge.seam for l in hist.loops]
+                props.src_uvs.append(uvs)
+                props.src_pin_uvs.append(pin_uvs)
+                props.src_seams.append(seams)
+        if not props.src_uvs or not props.src_pin_uvs:
+            self.report({'WARNING'}, "No faces are selected")
+            return {'CANCELLED'}
+        self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs))
 
         return {'FINISHED'}
 
 
-class MUV_CPUVObjCopyUVMenu(bpy.types.Menu):
+class MUV_CPUVSelSeqCopyUVMenu(bpy.types.Menu):
     """
-    Menu class: Copy UV coordinate per object
+    Menu class: Copy UV coordinate by selection sequence
     """
 
-    bl_idname = "object.muv_cpuv_obj_copy_uv_menu"
-    bl_label = "Copy UV"
-    bl_description = "Copy UV coordinate per object"
+    bl_idname = "uv.muv_cpuv_selseq_copy_uv_menu"
+    bl_label = "Copy UV (Selection Sequence)"
+    bl_description = "Copy UV coordinate by selection sequence"
 
-    def draw(self, _):
+    def draw(self, context):
         layout = self.layout
-        # create sub menu
-        uv_maps = bpy.context.active_object.data.uv_textures.keys()
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        uv_maps = bm.loops.layers.uv.keys()
         layout.operator(
-            MUV_CPUVObjCopyUV.bl_idname,
+            MUV_CPUVSelSeqCopyUV.bl_idname,
             text="[Default]", icon="IMAGE_COL").uv_map = ""
         for m in uv_maps:
             layout.operator(
-                MUV_CPUVObjCopyUV.bl_idname,
+                MUV_CPUVSelSeqCopyUV.bl_idname,
                 text=m, icon="IMAGE_COL").uv_map = m
 
 
-class MUV_CPUVObjPasteUV(bpy.types.Operator):
+class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
     """
-    Operation class: Paste UV coordinate per object
+    Operation class: Paste UV coordinate by selection sequence
     """
 
-    bl_idname = "object.muv_cpuv_obj_paste_uv"
-    bl_label = "Paste UV"
-    bl_description = "Paste UV coordinate"
+    bl_idname = "uv.muv_cpuv_selseq_paste_uv"
+    bl_label = "Paste UV (Selection Sequence) (Operation)"
+    bl_description = "Paste UV coordinate by selection sequence (Operation)"
     bl_options = {'REGISTER', 'UNDO'}
 
     uv_map = StringProperty(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="Copy Seams",
         description="Copy Seams",
         default=True
     )
 
-    @memorize_view_3d_mode
     def execute(self, context):
-        props = context.scene.muv_props.cpuv_obj
+        props = context.scene.muv_props.cpuv_selseq
         if not props.src_uvs or not props.src_pin_uvs:
             self.report({'WARNING'}, "Need copy UV at first")
             return {'CANCELLED'}
+        if self.uv_map == "":
+            self.report({'INFO'}, "Paste UV coordinate (selection sequence)")
+        else:
+            self.report(
+                {'INFO'},
+                "Paste UV coordinate (selection sequence) (UV map:%s)"
+                % (self.uv_map))
 
-        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 = bmesh.from_edit_mesh(obj.data)
-            if muv_common.check_version(2, 73, 0) >= 0:
-                bm.faces.ensure_lookup_table()
+        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 (self.uv_map == "" or
-                    self.uv_map not in bm.loops.layers.uv.keys()):
-                self.report({'INFO'}, "Paste UV coordinate per object")
-            else:
+        # get UV layer
+        if self.uv_map == "":
+            if not bm.loops.layers.uv:
                 self.report(
-                    {'INFO'},
-                    "Paste UV coordinate per object (UV map: %s)"
-                    % (self.uv_map))
-
-            # get UV layer
-            if (self.uv_map == "" or
-                    self.uv_map not in bm.loops.layers.uv.keys()):
-                if not bm.loops.layers.uv:
-                    self.report(
-                        {'WARNING'}, "Object must have more than one UV map")
-                    return {'CANCELLED'}
-                uv_layer = bm.loops.layers.uv.verify()
-            else:
-                uv_layer = bm.loops.layers.uv[self.uv_map]
-
-            # get selected face
-            dest_uvs = []
-            dest_pin_uvs = []
-            dest_seams = []
-            dest_face_indices = []
-            for face in bm.faces:
-                dest_face_indices.append(face.index)
-                uvs = [l[uv_layer].uv.copy() for l in face.loops]
-                pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
-                seams = [l.edge.seam for l in face.loops]
+                    {'WARNING'}, "Object must have more than one UV map")
+                return {'CANCELLED'}
+            uv_layer = bm.loops.layers.uv.verify()
+        else:
+            uv_layer = bm.loops.layers.uv[self.uv_map]
+
+        # get selected face
+        dest_uvs = []
+        dest_pin_uvs = []
+        dest_seams = []
+        dest_face_indices = []
+        for hist in bm.select_history:
+            if isinstance(hist, bmesh.types.BMFace) and hist.select:
+                dest_face_indices.append(hist.index)
+                uvs = [l[uv_layer].uv.copy() for l in hist.loops]
+                pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
+                seams = [l.edge.seam for l in hist.loops]
                 dest_uvs.append(uvs)
                 dest_pin_uvs.append(pin_uvs)
                 dest_seams.append(seams)
-            if len(props.src_uvs) != len(dest_uvs):
-                self.report(
-                    {'WARNING'},
-                    "Number of faces is different from copied " +
-                    "(src:%d, dest:%d)"
-                    % (len(props.src_uvs), len(dest_uvs))
-                )
-                return {'CANCELLED'}
+        if not dest_uvs or not dest_pin_uvs:
+            self.report({'WARNING'}, "No faces are selected")
+            return {'CANCELLED'}
+        if self.strategy == 'N_N' and len(props.src_uvs) != len(dest_uvs):
+            self.report(
+                {'WARNING'},
+                "Number of selected faces is different from copied faces " +
+                "(src:%d, dest:%d)"
+                % (len(props.src_uvs), len(dest_uvs)))
+            return {'CANCELLED'}
 
-            # paste
-            for i, idx in enumerate(dest_face_indices):
+        # paste
+        for i, idx in enumerate(dest_face_indices):
+            suv = None
+            spuv = None
+            ss = None
+            duv = None
+            if self.strategy == 'N_N':
                 suv = props.src_uvs[i]
                 spuv = props.src_pin_uvs[i]
                 ss = props.src_seams[i]
                 duv = dest_uvs[i]
-                if len(suv) != len(duv):
-                    self.report({'WARNING'}, "Some faces are different size")
-                    return {'CANCELLED'}
-                suvs_fr = [uv for uv in suv]
-                spuvs_fr = [pin_uv for pin_uv in spuv]
-                ss_fr = [s for s in ss]
-                # paste UVs
-                for l, suv, spuv, ss in zip(
-                        bm.faces[idx].loops, suvs_fr, spuvs_fr, ss_fr):
-                    l[uv_layer].uv = suv
-                    l[uv_layer].pin_uv = spuv
-                    if self.copy_seams is True:
-                        l.edge.seam = ss
-
-            bmesh.update_edit_mesh(obj.data)
-            if self.copy_seams is True:
-                obj.data.show_edge_seams = True
+            elif self.strategy == 'N_M':
+                suv = props.src_uvs[i % len(props.src_uvs)]
+                spuv = props.src_pin_uvs[i % len(props.src_pin_uvs)]
+                ss = props.src_seams[i % len(props.src_seams)]
+                duv = dest_uvs[i]
+            if len(suv) != len(duv):
+                self.report({'WARNING'}, "Some faces are different size")
+                return {'CANCELLED'}
+            suvs_fr = [uv for uv in suv]
+            spuvs_fr = [pin_uv for pin_uv in spuv]
+            ss_fr = [s for s in ss]
+            # flip UVs
+            if self.flip_copied_uv is True:
+                suvs_fr.reverse()
+                spuvs_fr.reverse()
+                ss_fr.reverse()
+            # rotate UVs
+            for _ in range(self.rotate_copied_uv):
+                uv = suvs_fr.pop()
+                pin_uv = spuvs_fr.pop()
+                s = ss_fr.pop()
+                suvs_fr.insert(0, uv)
+                spuvs_fr.insert(0, pin_uv)
+                ss_fr.insert(0, s)
+            # paste UVs
+            for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr,
+                                        spuvs_fr, ss_fr):
+                l[uv_layer].uv = suv
+                l[uv_layer].pin_uv = spuv
+                if self.copy_seams is True:
+                    l.edge.seam = ss
 
-            self.report(
-                {'INFO'}, "%s's UV coordinates are pasted" % (obj.name))
+        self.report({'INFO'}, "%d face(s) are copied" % len(dest_uvs))
+
+        bmesh.update_edit_mesh(obj.data)
+        if self.copy_seams is True:
+            obj.data.show_edge_seams = True
 
         return {'FINISHED'}
 
 
-class MUV_CPUVObjPasteUVMenu(bpy.types.Menu):
+class MUV_CPUVSelSeqPasteUVMenu(bpy.types.Menu):
     """
-    Menu class: Paste UV coordinate per object
+    Menu class: Paste UV coordinate by selection sequence
     """
 
-    bl_idname = "object.muv_cpuv_obj_paste_uv_menu"
-    bl_label = "Paste UV"
-    bl_description = "Paste UV coordinate per object"
+    bl_idname = "uv.muv_cpuv_selseq_paste_uv_menu"
+    bl_label = "Paste UV (Selection Sequence)"
+    bl_description = "Paste UV coordinate by selection sequence"
 
-    def draw(self, _):
+    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())
-        uv_maps = list(set(uv_maps))
-        layout.operator(
-            MUV_CPUVObjPasteUV.bl_idname,
-            text="[Default]", icon="IMAGE_COL").uv_map = ""
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        uv_maps = bm.loops.layers.uv.keys()
+        ops = layout.operator(MUV_CPUVSelSeqPasteUV.bl_idname,
+                              text="[Default]")
+        ops.uv_map = ""
+        ops.copy_seams = sc.muv_cpuv_copy_seams
+        ops.strategy = sc.muv_cpuv_strategy
         for m in uv_maps:
-            layout.operator(
-                MUV_CPUVObjPasteUV.bl_idname,
-                text=m, icon="IMAGE_COL").uv_map = m
+            ops = layout.operator(MUV_CPUVSelSeqPasteUV.bl_idname, text=m)
+            ops.uv_map = m
+            ops.copy_seams = sc.muv_cpuv_copy_seams
+            ops.strategy = sc.muv_cpuv_strategy
diff --git a/uv_magic_uv/op/copy_paste_uv_object.py b/uv_magic_uv/op/copy_paste_uv_object.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb42d99ae9244f259812fc4ed64c21f394ef1bc3
--- /dev/null
+++ b/uv_magic_uv/op/copy_paste_uv_object.py
@@ -0,0 +1,252 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+import bmesh
+from bpy.props import (
+    StringProperty,
+    BoolProperty,
+)
+
+from .. import common
+
+
+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
+
+
+class MUV_CPUVObjCopyUV(bpy.types.Operator):
+    """
+    Operation class: Copy UV coordinate per object
+    """
+
+    bl_idname = "object.muv_cpuv_obj_copy_uv"
+    bl_label = "Copy UV"
+    bl_description = "Copy UV coordinate"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    uv_map = StringProperty(options={'HIDDEN'})
+
+    @memorize_view_3d_mode
+    def execute(self, context):
+        props = context.scene.muv_props.cpuv_obj
+        if self.uv_map == "":
+            self.report({'INFO'}, "Copy UV coordinate per object")
+        else:
+            self.report(
+                {'INFO'},
+                "Copy UV coordinate per object (UV map:%s)" % (self.uv_map))
+        bpy.ops.object.mode_set(mode='EDIT')
+
+        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
+        if self.uv_map == "":
+            if not bm.loops.layers.uv:
+                self.report(
+                    {'WARNING'}, "Object must have more than one UV map")
+                return {'CANCELLED'}
+            uv_layer = bm.loops.layers.uv.verify()
+        else:
+            uv_layer = bm.loops.layers.uv[self.uv_map]
+
+        # get selected face
+        props.src_uvs = []
+        props.src_pin_uvs = []
+        props.src_seams = []
+        for face in bm.faces:
+            uvs = [l[uv_layer].uv.copy() for l in face.loops]
+            pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
+            seams = [l.edge.seam for l in face.loops]
+            props.src_uvs.append(uvs)
+            props.src_pin_uvs.append(pin_uvs)
+            props.src_seams.append(seams)
+
+        self.report({'INFO'}, "%s's UV coordinates are copied" % (obj.name))
+
+        return {'FINISHED'}
+
+
+class MUV_CPUVObjCopyUVMenu(bpy.types.Menu):
+    """
+    Menu class: Copy UV coordinate per object
+    """
+
+    bl_idname = "object.muv_cpuv_obj_copy_uv_menu"
+    bl_label = "Copy UV"
+    bl_description = "Copy UV coordinate per object"
+
+    def draw(self, _):
+        layout = self.layout
+        # create sub menu
+        uv_maps = bpy.context.active_object.data.uv_textures.keys()
+        layout.operator(MUV_CPUVObjCopyUV.bl_idname, text="[Default]")\
+            .uv_map = ""
+        for m in uv_maps:
+            layout.operator(MUV_CPUVObjCopyUV.bl_idname, text=m).uv_map = m
+
+
+class MUV_CPUVObjPasteUV(bpy.types.Operator):
+    """
+    Operation class: Paste UV coordinate per object
+    """
+
+    bl_idname = "object.muv_cpuv_obj_paste_uv"
+    bl_label = "Paste UV"
+    bl_description = "Paste UV coordinate"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    uv_map = StringProperty(options={'HIDDEN'})
+    copy_seams = BoolProperty(
+        name="Copy Seams",
+        description="Copy Seams",
+        default=True
+    )
+
+    @memorize_view_3d_mode
+    def execute(self, context):
+        props = context.scene.muv_props.cpuv_obj
+        if not props.src_uvs or not props.src_pin_uvs:
+            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 = bmesh.from_edit_mesh(obj.data)
+            if common.check_version(2, 73, 0) >= 0:
+                bm.faces.ensure_lookup_table()
+
+            if (self.uv_map == "" or
+                    self.uv_map not in bm.loops.layers.uv.keys()):
+                self.report({'INFO'}, "Paste UV coordinate per object")
+            else:
+                self.report(
+                    {'INFO'},
+                    "Paste UV coordinate per object (UV map: %s)"
+                    % (self.uv_map))
+
+            # get UV layer
+            if (self.uv_map == "" or
+                    self.uv_map not in bm.loops.layers.uv.keys()):
+                if not bm.loops.layers.uv:
+                    self.report(
+                        {'WARNING'}, "Object must have more than one UV map")
+                    return {'CANCELLED'}
+                uv_layer = bm.loops.layers.uv.verify()
+            else:
+                uv_layer = bm.loops.layers.uv[self.uv_map]
+
+            # get selected face
+            dest_uvs = []
+            dest_pin_uvs = []
+            dest_seams = []
+            dest_face_indices = []
+            for face in bm.faces:
+                dest_face_indices.append(face.index)
+                uvs = [l[uv_layer].uv.copy() for l in face.loops]
+                pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
+                seams = [l.edge.seam for l in face.loops]
+                dest_uvs.append(uvs)
+                dest_pin_uvs.append(pin_uvs)
+                dest_seams.append(seams)
+            if len(props.src_uvs) != len(dest_uvs):
+                self.report(
+                    {'WARNING'},
+                    "Number of faces is different from copied " +
+                    "(src:%d, dest:%d)"
+                    % (len(props.src_uvs), len(dest_uvs))
+                )
+                return {'CANCELLED'}
+
+            # paste
+            for i, idx in enumerate(dest_face_indices):
+                suv = props.src_uvs[i]
+                spuv = props.src_pin_uvs[i]
+                ss = props.src_seams[i]
+                duv = dest_uvs[i]
+                if len(suv) != len(duv):
+                    self.report({'WARNING'}, "Some faces are different size")
+                    return {'CANCELLED'}
+                suvs_fr = [uv for uv in suv]
+                spuvs_fr = [pin_uv for pin_uv in spuv]
+                ss_fr = [s for s in ss]
+                # paste UVs
+                for l, suv, spuv, ss in zip(
+                        bm.faces[idx].loops, suvs_fr, spuvs_fr, ss_fr):
+                    l[uv_layer].uv = suv
+                    l[uv_layer].pin_uv = spuv
+                    if self.copy_seams is True:
+                        l.edge.seam = ss
+
+            bmesh.update_edit_mesh(obj.data)
+            if self.copy_seams is True:
+                obj.data.show_edge_seams = True
+
+            self.report(
+                {'INFO'}, "%s's UV coordinates are pasted" % (obj.name))
+
+        return {'FINISHED'}
+
+
+class MUV_CPUVObjPasteUVMenu(bpy.types.Menu):
+    """
+    Menu class: Paste UV coordinate per object
+    """
+
+    bl_idname = "object.muv_cpuv_obj_paste_uv_menu"
+    bl_label = "Paste UV"
+    bl_description = "Paste UV coordinate per object"
+
+    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())
+        uv_maps = list(set(uv_maps))
+        ops = layout.operator(MUV_CPUVObjPasteUV.bl_idname, text="[Default]")
+        ops.uv_map = ""
+        ops.copy_seams = sc.muv_cpuv_copy_seams
+        for m in uv_maps:
+            ops = layout.operator(MUV_CPUVObjPasteUV.bl_idname, text=m)
+            ops.uv_map = m
+            ops.copy_seams = sc.muv_cpuv_copy_seams
diff --git a/uv_magic_uv/op/copy_paste_uv_uvedit.py b/uv_magic_uv/op/copy_paste_uv_uvedit.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b64505eab54f2718d397553952a4a8daeb23c12
--- /dev/null
+++ b/uv_magic_uv/op/copy_paste_uv_uvedit.py
@@ -0,0 +1,144 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import math
+from math import atan2, sin, cos
+
+import bpy
+import bmesh
+from mathutils import Vector
+
+from .. import common
+
+
+class MUV_CPUVIECopyUV(bpy.types.Operator):
+    """
+    Operation class: Copy UV coordinate on UV/Image Editor
+    """
+
+    bl_idname = "uv.muv_cpuv_ie_copy_uv"
+    bl_label = "Copy UV"
+    bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    @classmethod
+    def poll(cls, context):
+        return context.mode == 'EDIT_MESH'
+
+    def execute(self, context):
+        props = context.scene.muv_props.cpuv
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        uv_layer = bm.loops.layers.uv.verify()
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        for face in bm.faces:
+            if not face.select:
+                continue
+            skip = False
+            for l in face.loops:
+                if not l[uv_layer].select:
+                    skip = True
+                    break
+            if skip:
+                continue
+            props.src_uvs.append([l[uv_layer].uv.copy() for l in face.loops])
+
+        return {'FINISHED'}
+
+
+class MUV_CPUVIEPasteUV(bpy.types.Operator):
+    """
+    Operation class: Paste UV coordinate on UV/Image Editor
+    """
+
+    bl_idname = "uv.muv_cpuv_ie_paste_uv"
+    bl_label = "Paste UV"
+    bl_description = "Paste UV coordinate (only selected in UV/Image Editor)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    @classmethod
+    def poll(cls, context):
+        return context.mode == 'EDIT_MESH'
+
+    def execute(self, context):
+        props = context.scene.muv_props.cpuv
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        uv_layer = bm.loops.layers.uv.verify()
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        dest_uvs = []
+        dest_face_indices = []
+        for face in bm.faces:
+            if not face.select:
+                continue
+            skip = False
+            for l in face.loops:
+                if not l[uv_layer].select:
+                    skip = True
+                    break
+            if skip:
+                continue
+            dest_face_indices.append(face.index)
+            uvs = [l[uv_layer].uv.copy() for l in face.loops]
+            dest_uvs.append(uvs)
+
+        for suvs, duvs in zip(props.src_uvs, dest_uvs):
+            src_diff = suvs[1] - suvs[0]
+            dest_diff = duvs[1] - duvs[0]
+
+            src_base = suvs[0]
+            dest_base = duvs[0]
+
+            src_rad = atan2(src_diff.y, src_diff.x)
+            dest_rad = atan2(dest_diff.y, dest_diff.x)
+            if src_rad < dest_rad:
+                radian = dest_rad - src_rad
+            elif src_rad > dest_rad:
+                radian = math.pi * 2 - (src_rad - dest_rad)
+            else:       # src_rad == dest_rad
+                radian = 0.0
+
+            ratio = dest_diff.length / src_diff.length
+            break
+
+        for suvs, fidx in zip(props.src_uvs, dest_face_indices):
+            for l, suv in zip(bm.faces[fidx].loops, suvs):
+                base = suv - src_base
+                radian_ref = atan2(base.y, base.x)
+                radian_fin = (radian + radian_ref)
+                length = base.length
+                turn = Vector((length * cos(radian_fin),
+                               length * sin(radian_fin)))
+                target_uv = Vector((turn.x * ratio, turn.y * ratio)) + \
+                    dest_base
+                l[uv_layer].uv = target_uv
+
+        bmesh.update_edit_mesh(obj.data)
+
+        return {'FINISHED'}
diff --git a/uv_magic_uv/muv_fliprot_ops.py b/uv_magic_uv/op/flip_rotate_uv.py
similarity index 97%
rename from uv_magic_uv/muv_fliprot_ops.py
rename to uv_magic_uv/op/flip_rotate_uv.py
index 334eb14c57d2a9767a6a6641e4860c972b74d517..907c77c48edd1c4b548308200b08159c232d04b5 100644
--- a/uv_magic_uv/muv_fliprot_ops.py
+++ b/uv_magic_uv/op/flip_rotate_uv.py
@@ -20,8 +20,8 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 import bpy
 import bmesh
@@ -29,7 +29,8 @@ from bpy.props import (
     BoolProperty,
     IntProperty,
 )
-from . import muv_common
+
+from .. import common
 
 
 class MUV_FlipRot(bpy.types.Operator):
@@ -63,7 +64,7 @@ class MUV_FlipRot(bpy.types.Operator):
         self.report({'INFO'}, "Flip/Rotate UV")
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV layer
diff --git a/uv_magic_uv/muv_mirroruv_ops.py b/uv_magic_uv/op/mirror_uv.py
similarity index 97%
rename from uv_magic_uv/muv_mirroruv_ops.py
rename to uv_magic_uv/op/mirror_uv.py
index 63eb9bd549c52c9c46396ab57da3d7d157e3f025..d1014c734591b91dca90457df9eb185ff603fe29 100644
--- a/uv_magic_uv/muv_mirroruv_ops.py
+++ b/uv_magic_uv/op/mirror_uv.py
@@ -20,8 +20,8 @@
 
 __author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 import bpy
 from bpy.props import (
@@ -30,7 +30,8 @@ from bpy.props import (
 )
 import bmesh
 from mathutils import Vector
-from . import muv_common
+
+from .. import common
 
 
 class MUV_MirrorUV(bpy.types.Operator):
@@ -113,7 +114,7 @@ class MUV_MirrorUV(bpy.types.Operator):
         error = self.error
         axis = self.axis
 
-        if muv_common.check_version(2, 73, 0) >= 0:
+        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")
diff --git a/uv_magic_uv/muv_mvuv_ops.py b/uv_magic_uv/op/move_uv.py
similarity index 94%
rename from uv_magic_uv/muv_mvuv_ops.py
rename to uv_magic_uv/op/move_uv.py
index 28346270d0a0fc3c415c7935a77f09c2c882ef84..e0ac418f7a97a9515366c25e8943a437e99ed85c 100644
--- a/uv_magic_uv/muv_mvuv_ops.py
+++ b/uv_magic_uv/op/move_uv.py
@@ -20,8 +20,8 @@
 
 __author__ = "kgeogeo, mem, Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 import bpy
 import bmesh
@@ -64,6 +64,7 @@ class MUV_MVUV(bpy.types.Operator):
         return context.edit_object
 
     def modal(self, context, event):
+        props = context.scene.muv_props.mvuv
         if self.__first_time is True:
             self.__prev_mouse = Vector((
                 event.mouse_region_x, event.mouse_region_y))
@@ -84,7 +85,7 @@ class MUV_MVUV(bpy.types.Operator):
             event.mouse_region_x, event.mouse_region_y))
 
         # check if operation is started
-        if self.__running is True:
+        if self.__running:
             if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
                 self.__running = False
             return {'RUNNING_MODAL'}
@@ -110,16 +111,20 @@ class MUV_MVUV(bpy.types.Operator):
         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
+            props.running = False
             return {'FINISHED'}
         # confirmed
         if event.type == confirm_btn and event.value == 'PRESS':
+            props.running = False
             return {'FINISHED'}
 
         return {'RUNNING_MODAL'}
 
     def execute(self, context):
-        self.__first_time = True
+        props = context.scene.muv_props.mvuv
+        props.running = True
         self.__running = True
+        self.__first_time = True
         context.window_manager.modal_handler_add(self)
         self.__topology_dict, self.__ini_uvs = self.__find_uv(context)
         return {'RUNNING_MODAL'}
diff --git a/uv_magic_uv/muv_packuv_ops.py b/uv_magic_uv/op/pack_uv.py
similarity index 69%
rename from uv_magic_uv/muv_packuv_ops.py
rename to uv_magic_uv/op/pack_uv.py
index f663e662774448cb4a07bccebac79f77522e24ca..692fa93e6f0f268ba3c46da8b2812064ea73d4af 100644
--- a/uv_magic_uv/muv_packuv_ops.py
+++ b/uv_magic_uv/op/pack_uv.py
@@ -20,11 +20,10 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 from math import fabs
-from collections import defaultdict
 
 import bpy
 import bmesh
@@ -36,7 +35,7 @@ from bpy.props import (
 )
 from mathutils import Vector
 
-from . import muv_common
+from .. import common
 
 
 class MUV_PackUV(bpy.types.Operator):
@@ -69,23 +68,21 @@ class MUV_PackUV(bpy.types.Operator):
         min=0.000001,
         max=0.1,
         default=(0.001, 0.001),
-        size=2)
+        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)
+        size=2
+    )
 
-    def __init__(self):
-        self.__face_to_verts = defaultdict(set)
-        self.__vert_to_faces = defaultdict(set)
-
-    def execute(self, _):
-        obj = bpy.context.active_object
+    def execute(self, context):
+        obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        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")
@@ -93,17 +90,7 @@ class MUV_PackUV(bpy.types.Operator):
         uv_layer = bm.loops.layers.uv.verify()
 
         selected_faces = [f for f in bm.faces if f.select]
-
-        # create mesh database
-        for f in selected_faces:
-            for l in f.loops:
-                id_ = l[uv_layer].uv.to_tuple(5), l.vert.index
-                self.__face_to_verts[f.index].add(id_)
-                self.__vert_to_faces[id_].add(f.index)
-
-        # Group island
-        uv_island_lists = self.__get_island(bm)
-        island_info = self.__get_island_info(uv_layer, uv_island_lists)
+        island_info = common.get_island_info(obj)
         num_group = self.__group_island(island_info)
 
         loop_lists = [l for f in bm.faces for l in f.loops]
@@ -183,13 +170,17 @@ class MUV_PackUV(bpy.types.Operator):
                     dsx = isl_2['size'].x - isl_1['size'].x
                     dsy = isl_2['size'].y - isl_1['size'].y
                     center_x_matched = (
-                        fabs(dcx) < self.allowable_center_deviation[0])
+                        fabs(dcx) < self.allowable_center_deviation[0]
+                    )
                     center_y_matched = (
-                        fabs(dcy) < self.allowable_center_deviation[1])
+                        fabs(dcy) < self.allowable_center_deviation[1]
+                    )
                     size_x_matched = (
-                        fabs(dsx) < self.allowable_size_deviation[0])
+                        fabs(dsx) < self.allowable_size_deviation[0]
+                    )
                     size_y_matched = (
-                        fabs(dsy) < self.allowable_size_deviation[1])
+                        fabs(dsy) < self.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'])
@@ -214,75 +205,3 @@ class MUV_PackUV(bpy.types.Operator):
             num_group = num_group + 1
 
         return num_group
-
-    def __get_island_info(self, uv_layer, islands):
-        """
-        get information about each island
-        """
-
-        island_info = []
-        for isl in islands:
-            info = {}
-            max_uv = Vector((-10000000.0, -10000000.0))
-            min_uv = Vector((10000000.0, 10000000.0))
-            ave_uv = Vector((0.0, 0.0))
-            num_uv = 0
-            for face in isl:
-                n = 0
-                a = Vector((0.0, 0.0))
-                for l in face['face'].loops:
-                    uv = l[uv_layer].uv
-                    if uv.x > max_uv.x:
-                        max_uv.x = uv.x
-                    if uv.y > max_uv.y:
-                        max_uv.y = uv.y
-                    if uv.x < min_uv.x:
-                        min_uv.x = uv.x
-                    if uv.y < min_uv.y:
-                        min_uv.y = uv.y
-                    a = a + uv
-                    n = n + 1
-                ave_uv = ave_uv + a
-                num_uv = num_uv + n
-                a = a / n
-                face['ave_uv'] = a
-            ave_uv = ave_uv / num_uv
-
-            info['center'] = ave_uv
-            info['size'] = max_uv - min_uv
-            info['num_uv'] = num_uv
-            info['group'] = -1
-            info['faces'] = isl
-
-            island_info.append(info)
-
-        return island_info
-
-    def __parse_island(self, bm, face_idx, faces_left, island):
-        """
-        Parse island
-        """
-
-        if face_idx in faces_left:
-            faces_left.remove(face_idx)
-            island.append({'face': bm.faces[face_idx]})
-            for v in self.__face_to_verts[face_idx]:
-                connected_faces = self.__vert_to_faces[v]
-                if connected_faces:
-                    for cf in connected_faces:
-                        self.__parse_island(bm, cf, faces_left, island)
-
-    def __get_island(self, bm):
-        """
-        Get island list
-        """
-
-        uv_island_lists = []
-        faces_left = set(self.__face_to_verts.keys())
-        while faces_left:
-            current_island = []
-            face_idx = list(faces_left)[0]
-            self.__parse_island(bm, face_idx, faces_left, current_island)
-            uv_island_lists.append(current_island)
-
-        return uv_island_lists
diff --git a/uv_magic_uv/muv_preserve_uv_aspect.py b/uv_magic_uv/op/preserve_uv_aspect.py
similarity index 92%
rename from uv_magic_uv/muv_preserve_uv_aspect.py
rename to uv_magic_uv/op/preserve_uv_aspect.py
index 68e75f74a15c2803609a332bf3cb0f181b1ff943..9838aec60a130ac649fc2cecb403799b747bef5e 100644
--- a/uv_magic_uv/muv_preserve_uv_aspect.py
+++ b/uv_magic_uv/op/preserve_uv_aspect.py
@@ -20,14 +20,15 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 import bpy
 import bmesh
 from bpy.props import StringProperty, EnumProperty
 from mathutils import Vector
-from . import muv_common
+
+from .. import common
 
 
 class MUV_PreserveUVAspect(bpy.types.Operator):
@@ -71,7 +72,7 @@ class MUV_PreserveUVAspect(bpy.types.Operator):
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
 
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         if not bm.loops.layers.uv:
@@ -202,22 +203,3 @@ class MUV_PreserveUVAspect(bpy.types.Operator):
         bmesh.update_edit_mesh(obj.data)
 
         return {'FINISHED'}
-
-
-class MUV_PreserveUVAspectMenu(bpy.types.Menu):
-    """
-    Menu class: Preserve UV Aspect
-    """
-
-    bl_idname = "uv.muv_preserve_uv_aspect_menu"
-    bl_label = "Preserve UV Aspect"
-    bl_description = "Preserve UV Aspect"
-
-    def draw(self, _):
-        layout = self.layout
-
-        # create sub menu
-        for key in bpy.data.images.keys():
-            layout.operator(
-                MUV_PreserveUVAspect.bl_idname,
-                text=key, icon="IMAGE_COL").dest_img_name = key
diff --git a/uv_magic_uv/op/smooth_uv.py b/uv_magic_uv/op/smooth_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a120d0870c9c09f949d9b07d7abdf643b82d094
--- /dev/null
+++ b/uv_magic_uv/op/smooth_uv.py
@@ -0,0 +1,215 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+import bmesh
+from bpy.props import BoolProperty, FloatProperty
+
+from .. import common
+
+
+class MUV_AUVSmooth(bpy.types.Operator):
+
+    bl_idname = "uv.muv_auv_smooth"
+    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
+    )
+
+    @classmethod
+    def poll(cls, context):
+        return context.mode == 'EDIT_MESH'
+
+    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):
+        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/muv_texlock_ops.py b/uv_magic_uv/op/texture_lock.py
similarity index 96%
rename from uv_magic_uv/muv_texlock_ops.py
rename to uv_magic_uv/op/texture_lock.py
index bfc951299cefeff8ab0017e3a94f176cac682c08..b0be3534626ec5dfc57a8f6e0c100bf9942f784a 100644
--- a/uv_magic_uv/muv_texlock_ops.py
+++ b/uv_magic_uv/op/texture_lock.py
@@ -20,20 +20,18 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 import math
-from math import (
-    atan2, cos,
-    sqrt, sin, fabs,
-)
+from math import atan2, cos, sqrt, sin, fabs
 
 import bpy
 import bmesh
 from mathutils import Vector
 from bpy.props import BoolProperty
-from . import muv_common
+
+from .. import common
 
 
 def get_vco(verts_orig, loop):
@@ -195,7 +193,7 @@ class MUV_TexLockStart(bpy.types.Operator):
         props = context.scene.muv_props.texlock
         obj = bpy.context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.verts.ensure_lookup_table()
             bm.edges.ensure_lookup_table()
             bm.faces.ensure_lookup_table()
@@ -224,13 +222,15 @@ class MUV_TexLockStop(bpy.types.Operator):
 
     connect = BoolProperty(
         name="Connect UV",
-        default=True)
+        default=True
+    )
 
     def execute(self, context):
-        props = context.scene.muv_props.texlock
+        sc = context.scene
+        props = sc.muv_props.texlock
         obj = bpy.context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.verts.ensure_lookup_table()
             bm.edges.ensure_lookup_table()
             bm.faces.ensure_lookup_table()
@@ -297,14 +297,14 @@ class MUV_TexLockUpdater(bpy.types.Operator):
         props = context.scene.muv_props.texlock
         obj = bpy.context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        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'}
+            return
         uv_layer = bm.loops.layers.uv.verify()
 
         verts = [v.index for v in bm.verts if v.select]
@@ -313,7 +313,7 @@ class MUV_TexLockUpdater(bpy.types.Operator):
         for vidx, v_orig in zip(verts, verts_orig):
             if vidx != v_orig["vidx"]:
                 self.report({'ERROR'}, "Internal Error")
-                return {"CANCELLED"}
+                return
 
             v = bm.verts[vidx]
             link_loops = get_link_loops(v)
@@ -336,7 +336,7 @@ class MUV_TexLockUpdater(bpy.types.Operator):
             v_orig["moved"] = True
             bmesh.update_edit_mesh(obj.data)
 
-        muv_common.redraw_all_areas()
+        common.redraw_all_areas()
         props.intr_verts_orig = [
             {"vidx": v.index, "vco": v.co.copy(), "moved": False}
             for v in bm.verts if v.select]
@@ -395,7 +395,7 @@ class MUV_TexLockIntrStart(bpy.types.Operator):
 
         obj = bpy.context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.verts.ensure_lookup_table()
             bm.edges.ensure_lookup_table()
             bm.faces.ensure_lookup_table()
diff --git a/uv_magic_uv/muv_texproj_ops.py b/uv_magic_uv/op/texture_projection.py
similarity index 80%
rename from uv_magic_uv/muv_texproj_ops.py
rename to uv_magic_uv/op/texture_projection.py
index ffa4e789e5b4cf7bbfc152f99bb6907f664bc7eb..9c2dc521ccda725cd3b4fbe80411da8c6cc1d826 100644
--- a/uv_magic_uv/muv_texproj_ops.py
+++ b/uv_magic_uv/op/texture_projection.py
@@ -20,8 +20,8 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 from collections import namedtuple
 
@@ -31,7 +31,7 @@ import bmesh
 import mathutils
 from bpy_extras import view3d_utils
 
-from . import muv_common
+from .. import common
 
 
 Rect = namedtuple('Rect', 'x0 y0 x1 y1')
@@ -237,28 +237,28 @@ class MUV_TexProjProject(bpy.types.Operator):
     def execute(self, context):
         sc = context.scene
 
-        if context.mode != "EDIT_MESH":
-            self.report({'WARNING'}, "Mesh must be in Edit mode")
-            return {'CANCELLED'}
-
         if sc.muv_texproj_tex_image == "None":
             self.report({'WARNING'}, "No textures are selected")
             return {'CANCELLED'}
 
-        _, region, space = muv_common.get_space(
+        _, region, space = common.get_space(
             '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 muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV and texture layer
         if not bm.loops.layers.uv:
-            self.report({'WARNING'}, "Object must have more than one UV map")
-            return {'CANCELLED'}
+            if sc.muv_texproj_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()
@@ -290,50 +290,7 @@ class MUV_TexProjProject(bpy.types.Operator):
                 l[uv_layer].uv = v_canvas[i].to_2d()
                 i = i + 1
 
-        muv_common.redraw_all_areas()
+        common.redraw_all_areas()
         bmesh.update_edit_mesh(obj.data)
 
         return {'FINISHED'}
-
-
-class OBJECT_PT_TP(bpy.types.Panel):
-    """
-    Panel class: Texture Projection Menu on Property Panel on View3D
-    """
-
-    bl_label = "Texture Projection"
-    bl_description = "Texture Projection Menu"
-    bl_space_type = 'VIEW_3D'
-    bl_region_type = 'UI'
-    bl_context = 'mesh_edit'
-
-    @classmethod
-    def poll(cls, context):
-        prefs = context.user_preferences.addons["uv_magic_uv"].preferences
-        return prefs.enable_texproj
-
-    def draw_header(self, _):
-        layout = self.layout
-        layout.label(text="", icon='IMAGE_COL')
-
-    def draw(self, context):
-        sc = context.scene
-        layout = self.layout
-        props = sc.muv_props.texproj
-        if props.running is False:
-            layout.operator(
-                MUV_TexProjStart.bl_idname, text="Start", icon='PLAY')
-        else:
-            layout.operator(
-                MUV_TexProjStop.bl_idname, text="Stop", icon='PAUSE')
-            layout.prop(sc, "muv_texproj_tex_image", text="Image")
-            layout.prop(
-                sc, "muv_texproj_tex_transparency", text="Transparency"
-            )
-            layout.prop(sc, "muv_texproj_adjust_window", text="Adjust Window")
-            if not sc.muv_texproj_adjust_window:
-                layout.prop(sc, "muv_texproj_tex_magnitude", text="Magnitude")
-            layout.prop(
-                sc, "muv_texproj_apply_tex_aspect", text="Texture Aspect Ratio"
-            )
-            layout.operator(MUV_TexProjProject.bl_idname, text="Project")
diff --git a/uv_magic_uv/op/texture_wrap.py b/uv_magic_uv/op/texture_wrap.py
new file mode 100644
index 0000000000000000000000000000000000000000..91b06704105e2088bc2d62e433eb12e05e5415a8
--- /dev/null
+++ b/uv_magic_uv/op/texture_wrap.py
@@ -0,0 +1,212 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+import bmesh
+
+from .. import common
+
+
+class MUV_TexWrapRefer(bpy.types.Operator):
+    """
+    Operation class: Refer UV
+    """
+
+    bl_idname = "uv.muv_texwrap_refer"
+    bl_label = "Refer"
+    bl_description = "Refer UV"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    def execute(self, context):
+        props = context.scene.muv_props.texwrap
+        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'}
+
+
+class MUV_TexWrapSet(bpy.types.Operator):
+    """
+    Operation class: Set UV
+    """
+
+    bl_idname = "uv.muv_texwrap_set"
+    bl_label = "Set"
+    bl_description = "Set UV"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    def execute(self, context):
+        sc = context.scene
+        props = sc.muv_props.texwrap
+        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_texwrap_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 verticies 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 Verticies =====")
+            common.debug_print(tgt_other_verts)
+
+            bmesh.update_edit_mesh(obj.data)
+
+            ref_face_index = tgt_face_index
+
+        if sc.muv_texwrap_set_and_refer:
+            props.ref_face_index = tgt_face_index
+
+        return {'FINISHED'}
diff --git a/uv_magic_uv/muv_transuv_ops.py b/uv_magic_uv/op/transfer_uv.py
similarity index 97%
rename from uv_magic_uv/muv_transuv_ops.py
rename to uv_magic_uv/op/transfer_uv.py
index ed0a3c46241d376472da0731d176c8d2ede18141..fd1b45e46bc67005f3d545c795e311ac7a8b7618 100644
--- a/uv_magic_uv/muv_transuv_ops.py
+++ b/uv_magic_uv/op/transfer_uv.py
@@ -20,8 +20,8 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>, Mifth, MaxRobinot"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 from collections import OrderedDict
 
@@ -29,8 +29,7 @@ import bpy
 import bmesh
 from bpy.props import BoolProperty
 
-from . import muv_props
-from . import muv_common
+from .. import common
 
 
 class MUV_TransUVCopy(bpy.types.Operator):
@@ -48,7 +47,7 @@ class MUV_TransUVCopy(bpy.types.Operator):
         props = context.scene.muv_props.transuv
         active_obj = context.scene.objects.active
         bm = bmesh.from_edit_mesh(active_obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV layer
@@ -115,7 +114,7 @@ class MUV_TransUVPaste(bpy.types.Operator):
         props = context.scene.muv_props.transuv
         active_obj = context.scene.objects.active
         bm = bmesh.from_edit_mesh(active_obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV layer
@@ -291,19 +290,19 @@ def parse_faces(
                 vert1 = sorted_edge.verts[0]
                 vert2 = sorted_edge.verts[1]
 
-                muv_common.debug_print(face_stuff[0], vert1, vert2)
+                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]
 
-                muv_common.debug_print(shared_face.verts, vert1, vert2)
+                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 muv_props.DEBUG:
+                if common.DEBUG:
                     shared_face.select = True  # test which faces are parsed
 
                 new_shared_faces.append(shared_face)
diff --git a/uv_magic_uv/muv_unwrapconst_ops.py b/uv_magic_uv/op/unwrap_constraint.py
similarity index 94%
rename from uv_magic_uv/muv_unwrapconst_ops.py
rename to uv_magic_uv/op/unwrap_constraint.py
index 1a6911199221ad3e8c8bacd4712d847ac6fcdabe..311b2c35de1222697eea7cf749cdc9a988f9424e 100644
--- a/uv_magic_uv/muv_unwrapconst_ops.py
+++ b/uv_magic_uv/op/unwrap_constraint.py
@@ -18,8 +18,8 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 import bpy
 import bmesh
@@ -28,7 +28,8 @@ from bpy.props import (
     EnumProperty,
     FloatProperty,
 )
-from . import muv_common
+
+from .. import common
 
 
 class MUV_UnwrapConstraint(bpy.types.Operator):
@@ -74,18 +75,21 @@ class MUV_UnwrapConstraint(bpy.types.Operator):
     u_const = BoolProperty(
         name="U-Constraint",
         description="Keep UV U-axis coordinate",
-        default=False)
+        default=False
+    )
     v_const = BoolProperty(
         name="V-Constraint",
         description="Keep UV V-axis coordinate",
-        default=False)
+        default=False
+    )
 
     def execute(self, _):
         obj = bpy.context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        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'}
diff --git a/uv_magic_uv/muv_uvbb_ops.py b/uv_magic_uv/op/uv_bounding_box.py
similarity index 94%
rename from uv_magic_uv/muv_uvbb_ops.py
rename to uv_magic_uv/op/uv_bounding_box.py
index 4f7b0631b597aa7c559d4636df8216d779a2c022..04aa611061d851c2ec198b95d837441c8d5a5cdd 100644
--- a/uv_magic_uv/muv_uvbb_ops.py
+++ b/uv_magic_uv/op/uv_bounding_box.py
@@ -20,8 +20,8 @@
 
 __author__ = "Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 from enum import IntEnum
 import math
@@ -31,7 +31,7 @@ import bgl
 import mathutils
 import bmesh
 
-from . import muv_common
+from .. import common
 
 
 MAX_VALUE = 100000.0
@@ -602,17 +602,23 @@ class MUV_UVBBUpdater(bpy.types.Operator):
         """
         Get UV coordinate
         """
+        sc = context.scene
         obj = context.active_object
         uv_info = []
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        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 f.select:
-                for i, l in enumerate(f.loops):
+            if not f.select:
+                continue
+            for i, l in enumerate(f.loops):
+                if sc.muv_uvbb_boundary == 'UV_SEL':
+                    if l[uv_layer].select:
+                        uv_info.append((f.index, i, l[uv_layer].uv.copy()))
+                elif sc.muv_uvbb_boundary == 'UV':
                     uv_info.append((f.index, i, l[uv_layer].uv.copy()))
         if not uv_info:
             return None
@@ -661,7 +667,7 @@ class MUV_UVBBUpdater(bpy.types.Operator):
         """
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
         if not bm.loops.layers.uv:
             return
@@ -683,7 +689,7 @@ class MUV_UVBBUpdater(bpy.types.Operator):
 
     def modal(self, context, event):
         props = context.scene.muv_props.uvbb
-        muv_common.redraw_all_areas()
+        common.redraw_all_areas()
         if props.running is False:
             self.__handle_remove(context)
             return {'FINISHED'}
@@ -717,37 +723,3 @@ class MUV_UVBBUpdater(bpy.types.Operator):
         props.running = True
 
         return {'RUNNING_MODAL'}
-
-
-class IMAGE_PT_MUV_UVBB(bpy.types.Panel):
-    """
-    Panel class: UV Bounding Box Menu on Property Panel on UV/ImageEditor
-    """
-
-    bl_space_type = 'IMAGE_EDITOR'
-    bl_region_type = 'UI'
-    bl_label = "UV Bounding Box"
-    bl_context = 'mesh_edit'
-
-    @classmethod
-    def poll(cls, context):
-        prefs = context.user_preferences.addons["uv_magic_uv"].preferences
-        return prefs.enable_uvbb
-
-    def draw_header(self, _):
-        layout = self.layout
-        layout.label(text="", icon='IMAGE_COL')
-
-    def draw(self, context):
-        sc = context.scene
-        props = sc.muv_props.uvbb
-        layout = self.layout
-        if props.running is False:
-            layout.operator(
-                MUV_UVBBUpdater.bl_idname, text="Display UV Bounding Box",
-                icon='PLAY')
-        else:
-            layout.operator(
-                MUV_UVBBUpdater.bl_idname, text="Hide UV Bounding Box",
-                icon='PAUSE')
-        layout.prop(sc, "muv_uvbb_uniform_scaling", text="Uniform Scaling")
diff --git a/uv_magic_uv/op/uv_inspection.py b/uv_magic_uv/op/uv_inspection.py
new file mode 100644
index 0000000000000000000000000000000000000000..0e8778f332d7c271aa441ce4c175f402f002cdf3
--- /dev/null
+++ b/uv_magic_uv/op/uv_inspection.py
@@ -0,0 +1,623 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+import bmesh
+import bgl
+from mathutils import Vector
+
+from .. import common
+
+
+def is_polygon_same(points1, points2):
+    if len(points1) != len(points2):
+        return False
+
+    pts1 = points1.as_list()
+    pts2 = points2.as_list()
+
+    for p1 in pts1:
+        for p2 in pts2:
+            diff = p2 - p1
+            if diff.length < 0.0000001:
+                pts2.remove(p2)
+                break
+        else:
+            return False
+
+    return True
+
+
+def is_segment_intersect(start1, end1, start2, end2):
+    seg1 = end1 - start1
+    seg2 = end2 - start2
+
+    a1 = -seg1.y
+    b1 = seg1.x
+    d1 = -(a1 * start1.x + b1 * start1.y)
+
+    a2 = -seg2.y
+    b2 = seg2.x
+    d2 = -(a2 * start2.x + b2 * start2.y)
+
+    seg1_line2_start = a2 * start1.x + b2 * start1.y + d2
+    seg1_line2_end = a2 * end1.x + b2 * end1.y + d2
+
+    seg2_line1_start = a1 * start2.x + b1 * start2.y + d1
+    seg2_line1_end = a1 * end2.x + b1 * end2.y + d1
+
+    if (seg1_line2_start * seg1_line2_end >= 0) or \
+            (seg2_line1_start * seg2_line1_end >= 0):
+        return False, None
+
+    u = seg1_line2_start / (seg1_line2_start - seg1_line2_end)
+    out = start1 + u * seg1
+
+    return True, out
+
+
+class RingBuffer:
+    def __init__(self, arr):
+        self.__buffer = arr.copy()
+        self.__pointer = 0
+
+    def __repr__(self):
+        return repr(self.__buffer)
+
+    def __len__(self):
+        return len(self.__buffer)
+
+    def insert(self, val, offset=0):
+        self.__buffer.insert(self.__pointer + offset, val)
+
+    def head(self):
+        return self.__buffer[0]
+
+    def tail(self):
+        return self.__buffer[-1]
+
+    def get(self, offset=0):
+        size = len(self.__buffer)
+        val = self.__buffer[(self.__pointer + offset) % size]
+        return val
+
+    def next(self):
+        size = len(self.__buffer)
+        self.__pointer = (self.__pointer + 1) % size
+
+    def reset(self):
+        self.__pointer = 0
+
+    def find(self, obj):
+        try:
+            idx = self.__buffer.index(obj)
+        except ValueError:
+            return None
+        return self.__buffer[idx]
+
+    def find_and_next(self, obj):
+        size = len(self.__buffer)
+        idx = self.__buffer.index(obj)
+        self.__pointer = (idx + 1) % size
+
+    def find_and_set(self, obj):
+        idx = self.__buffer.index(obj)
+        self.__pointer = idx
+
+    def as_list(self):
+        return self.__buffer.copy()
+
+    def reverse(self):
+        self.__buffer.reverse()
+        self.reset()
+
+
+# clip: reference polygon
+# subject: tested polygon
+def do_weiler_atherton_cliping(clip, subject, uv_layer, mode):
+
+    clip_uvs = RingBuffer([l[uv_layer].uv.copy() for l in clip.loops])
+    if is_polygon_flipped(clip_uvs):
+        clip_uvs.reverse()
+    subject_uvs = RingBuffer([l[uv_layer].uv.copy() for l in subject.loops])
+    if is_polygon_flipped(subject_uvs):
+        subject_uvs.reverse()
+
+    common.debug_print("===== Clip UV List =====")
+    common.debug_print(clip_uvs)
+    common.debug_print("===== Subject UV List =====")
+    common.debug_print(subject_uvs)
+
+    # check if clip and subject is overlapped completely
+    if is_polygon_same(clip_uvs, subject_uvs):
+        polygons = [subject_uvs.as_list()]
+        common.debug_print("===== Polygons Overlapped Completely =====")
+        common.debug_print(polygons)
+        return True, polygons
+
+    # check if subject is in clip
+    if is_points_in_polygon(subject_uvs, clip_uvs):
+        polygons = [subject_uvs.as_list()]
+        return True, polygons
+
+    # check if clip is in subject
+    if is_points_in_polygon(clip_uvs, subject_uvs):
+        polygons = [subject_uvs.as_list()]
+        return True, polygons
+
+    # check if clip and subject is overlapped partially
+    intersections = []
+    while True:
+        subject_uvs.reset()
+        while True:
+            uv_start1 = clip_uvs.get()
+            uv_end1 = clip_uvs.get(1)
+            uv_start2 = subject_uvs.get()
+            uv_end2 = subject_uvs.get(1)
+            intersected, point = is_segment_intersect(uv_start1, uv_end1,
+                                                      uv_start2, uv_end2)
+            if intersected:
+                clip_uvs.insert(point, 1)
+                subject_uvs.insert(point, 1)
+                intersections.append([point,
+                                      [clip_uvs.get(), clip_uvs.get(1)]])
+            subject_uvs.next()
+            if subject_uvs.get() == subject_uvs.head():
+                break
+        clip_uvs.next()
+        if clip_uvs.get() == clip_uvs.head():
+            break
+
+    common.debug_print("===== Intersection List =====")
+    common.debug_print(intersections)
+
+    # no intersection, so subject and clip is not overlapped
+    if not intersections:
+        return False, None
+
+    def get_intersection_pair(intersections, key):
+        for sect in intersections:
+            if sect[0] == key:
+                return sect[1]
+
+        return None
+
+    # make enter/exit pair
+    subject_uvs.reset()
+    subject_entering = []
+    subject_exiting = []
+    clip_entering = []
+    clip_exiting = []
+    intersect_uv_list = []
+    while True:
+        pair = get_intersection_pair(intersections, subject_uvs.get())
+        if pair:
+            sub = subject_uvs.get(1) - subject_uvs.get(-1)
+            inter = pair[1] - pair[0]
+            cross = sub.x * inter.y - inter.x * sub.y
+            if cross < 0:
+                subject_entering.append(subject_uvs.get())
+                clip_exiting.append(subject_uvs.get())
+            else:
+                subject_exiting.append(subject_uvs.get())
+                clip_entering.append(subject_uvs.get())
+            intersect_uv_list.append(subject_uvs.get())
+
+        subject_uvs.next()
+        if subject_uvs.get() == subject_uvs.head():
+            break
+
+    common.debug_print("===== Enter List =====")
+    common.debug_print(clip_entering)
+    common.debug_print(subject_entering)
+    common.debug_print("===== Exit List =====")
+    common.debug_print(clip_exiting)
+    common.debug_print(subject_exiting)
+
+    # for now, can't handle the situation when fulfill all below conditions
+    #        * two faces have common edge
+    #        * each face is intersected
+    #        * Show Mode is "Part"
+    #       so for now, ignore this situation
+    if len(subject_entering) != len(subject_exiting):
+        if mode == 'FACE':
+            polygons = [subject_uvs.as_list()]
+            return True, polygons
+        return False, None
+
+    def traverse(current_list, entering, exiting, poly, current, other_list):
+        result = current_list.find(current)
+        if not result:
+            return None
+        if result != current:
+            print("Internal Error")
+            return None
+
+        # enter
+        if entering.count(current) >= 1:
+            entering.remove(current)
+
+        current_list.find_and_next(current)
+        current = current_list.get()
+
+        while exiting.count(current) == 0:
+            poly.append(current.copy())
+            current_list.find_and_next(current)
+            current = current_list.get()
+
+        # exit
+        poly.append(current.copy())
+        exiting.remove(current)
+
+        other_list.find_and_set(current)
+        return other_list.get()
+
+    # Traverse
+    polygons = []
+    current_uv_list = subject_uvs
+    other_uv_list = clip_uvs
+    current_entering = subject_entering
+    current_exiting = subject_exiting
+
+    poly = []
+    current_uv = current_entering[0]
+
+    while True:
+        current_uv = traverse(current_uv_list, current_entering,
+                              current_exiting, poly, current_uv, other_uv_list)
+
+        if current_uv_list == subject_uvs:
+            current_uv_list = clip_uvs
+            other_uv_list = subject_uvs
+            current_entering = clip_entering
+            current_exiting = clip_exiting
+            common.debug_print("-- Next: Clip --")
+        else:
+            current_uv_list = subject_uvs
+            other_uv_list = clip_uvs
+            current_entering = subject_entering
+            current_exiting = subject_exiting
+            common.debug_print("-- Next: Subject --")
+
+        common.debug_print(clip_entering)
+        common.debug_print(clip_exiting)
+        common.debug_print(subject_entering)
+        common.debug_print(subject_exiting)
+
+        if not clip_entering and not clip_exiting \
+                and not subject_entering and not subject_exiting:
+            break
+
+    polygons.append(poly)
+
+    common.debug_print("===== Polygons Overlapped Partially =====")
+    common.debug_print(polygons)
+
+    return True, polygons
+
+
+class MUV_UVInspRenderer(bpy.types.Operator):
+    """
+    Operation class: Render UV Inspection
+    No operation (only rendering)
+    """
+
+    bl_idname = "uv.muv_uvinsp_renderer"
+    bl_description = "Render overlapped/flipped UVs"
+    bl_label = "Overlapped/Flipped UV renderer"
+
+    __handle = None
+
+    @staticmethod
+    def handle_add(obj, context):
+        sie = bpy.types.SpaceImageEditor
+        MUV_UVInspRenderer.__handle = sie.draw_handler_add(
+            MUV_UVInspRenderer.draw, (obj, context), 'WINDOW', 'POST_PIXEL')
+
+    @staticmethod
+    def handle_remove():
+        if MUV_UVInspRenderer.__handle is not None:
+            bpy.types.SpaceImageEditor.draw_handler_remove(
+                MUV_UVInspRenderer.__handle, 'WINDOW')
+            MUV_UVInspRenderer.__handle = None
+
+    @staticmethod
+    def draw(_, context):
+        sc = context.scene
+        props = sc.muv_props.uvinsp
+        prefs = context.user_preferences.addons["uv_magic_uv"].preferences
+
+        # OpenGL configuration
+        bgl.glEnable(bgl.GL_BLEND)
+
+        # render overlapped UV
+        if sc.muv_uvinsp_show_overlapped:
+            color = prefs.uvinsp_overlapped_color
+            for info in props.overlapped_info:
+                if sc.muv_uvinsp_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_uvinsp_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_uvinsp_show_flipped:
+            color = prefs.uvinsp_flipped_color
+            for info in props.flipped_info:
+                if sc.muv_uvinsp_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_uvinsp_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 is_polygon_flipped(points):
+    area = 0.0
+    for i in range(len(points)):
+        uv1 = points.get(i)
+        uv2 = points.get(i + 1)
+        a = uv1.x * uv2.y - uv1.y * uv2.x
+        area = area + a
+    if area < 0:
+        # clock-wise
+        return True
+    return False
+
+
+def is_point_in_polygon(point, subject_points):
+    count = 0
+    for i in range(len(subject_points)):
+        uv_start1 = subject_points.get(i)
+        uv_end1 = subject_points.get(i + 1)
+        uv_start2 = point
+        uv_end2 = Vector((1000000.0, point.y))
+        intersected, _ = is_segment_intersect(uv_start1, uv_end1,
+                                              uv_start2, uv_end2)
+        if intersected:
+            count = count + 1
+
+    return count % 2
+
+
+def is_points_in_polygon(points, subject_points):
+    for i in range(len(points)):
+        internal = is_point_in_polygon(points.get(i), subject_points)
+        if not internal:
+            return False
+
+    return True
+
+
+def get_overlapped_uv_info(bm, faces, uv_layer, mode):
+    # at first, check island overlapped
+    isl = common.get_island_info_from_faces(bm, faces, uv_layer)
+    overlapped_isl_pairs = []
+    for i, i1 in enumerate(isl):
+        for i2 in isl[i + 1:]:
+            if (i1["max"].x < i2["min"].x) or (i2["max"].x < i1["min"].x) or \
+               (i1["max"].y < i2["min"].y) or (i2["max"].y < i1["min"].y):
+                continue
+            overlapped_isl_pairs.append([i1, i2])
+
+    # next, check polygon overlapped
+    overlapped_uvs = []
+    for oip in overlapped_isl_pairs:
+        for clip in oip[0]["faces"]:
+            f_clip = clip["face"]
+            for subject in oip[1]["faces"]:
+                f_subject = subject["face"]
+
+                # fast operation, apply bounding box algorithm
+                if (clip["max_uv"].x < subject["min_uv"].x) or \
+                   (subject["max_uv"].x < clip["min_uv"].x) or \
+                   (clip["max_uv"].y < subject["min_uv"].y) or \
+                   (subject["max_uv"].y < clip["min_uv"].y):
+                    continue
+
+                # slow operation, apply Weiler-Atherton cliping algorithm
+                result, polygons = do_weiler_atherton_cliping(f_clip,
+                                                              f_subject,
+                                                              uv_layer, mode)
+                if result:
+                    subject_uvs = [l[uv_layer].uv.copy()
+                                   for l in f_subject.loops]
+                    overlapped_uvs.append({"clip_face": f_clip,
+                                           "subject_face": f_subject,
+                                           "subject_uvs": subject_uvs,
+                                           "polygons": polygons})
+
+    return overlapped_uvs
+
+
+def get_flipped_uv_info(faces, uv_layer):
+    flipped_uvs = []
+    for f in faces:
+        polygon = RingBuffer([l[uv_layer].uv.copy() for l in f.loops])
+        if is_polygon_flipped(polygon):
+            uvs = [l[uv_layer].uv.copy() for l in f.loops]
+            flipped_uvs.append({"face": f, "uvs": uvs,
+                                "polygons": [polygon.as_list()]})
+
+    return flipped_uvs
+
+
+def update_uvinsp_info(context):
+    sc = context.scene
+    props = sc.muv_props.uvinsp
+
+    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 = get_overlapped_uv_info(bm, sel_faces, uv_layer,
+                                                   sc.muv_uvinsp_show_mode)
+    props.flipped_info = get_flipped_uv_info(sel_faces, uv_layer)
+
+
+class MUV_UVInspUpdate(bpy.types.Operator):
+    """
+    Operation class: Update
+    """
+
+    bl_idname = "uv.muv_uvinsp_update"
+    bl_label = "Update"
+    bl_description = "Update Overlapped/Flipped UV"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    def execute(self, context):
+        update_uvinsp_info(context)
+
+        if context.area:
+            context.area.tag_redraw()
+
+        return {'FINISHED'}
+
+
+class MUV_UVInspDisplay(bpy.types.Operator):
+    """
+    Operation class: Display
+    """
+
+    bl_idname = "uv.muv_uvinsp_display"
+    bl_label = "Display"
+    bl_description = "Display Overlapped/Flipped UV"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    def execute(self, context):
+        sc = context.scene
+        props = sc.muv_props.uvinsp
+        if not props.display_running:
+            update_uvinsp_info(context)
+            MUV_UVInspRenderer.handle_add(self, context)
+            props.display_running = True
+        else:
+            MUV_UVInspRenderer.handle_remove()
+            props.display_running = False
+
+        if context.area:
+            context.area.tag_redraw()
+
+        return {'FINISHED'}
+
+
+class MUV_UVInspSelectOverlapped(bpy.types.Operator):
+    """
+    Operation class: Select faces which have overlapped UVs
+    """
+
+    bl_idname = "uv.muv_uvinsp_select_overlapped"
+    bl_label = "Overlapped"
+    bl_description = "Select faces which have overlapped UVs"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    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 = 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 MUV_UVInspSelectFlipped(bpy.types.Operator):
+    """
+    Operation class: Select faces which have flipped UVs
+    """
+
+    bl_idname = "uv.muv_uvinsp_select_flipped"
+    bl_label = "Flipped"
+    bl_description = "Select faces which have flipped UVs"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    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 = 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/uv_sculpt.py b/uv_magic_uv/op/uv_sculpt.py
new file mode 100644
index 0000000000000000000000000000000000000000..6133b2a2c131e1d129a2780536e7e5af820d82c9
--- /dev/null
+++ b/uv_magic_uv/op/uv_sculpt.py
@@ -0,0 +1,355 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+from math import pi, cos, tan, sin
+
+import bpy
+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
+
+
+class MUV_UVSculptRenderer(bpy.types.Operator):
+    """
+    Operation class: Render Brush
+    """
+
+    bl_idname = "uv.muv_uvsculpt_renderer"
+    bl_label = "Brush Renderer"
+    bl_description = "Brush Renderer in View3D"
+
+    __handle = None
+
+    @staticmethod
+    def handle_add(obj, context):
+        if MUV_UVSculptRenderer.__handle is None:
+            sv = bpy.types.SpaceView3D
+            MUV_UVSculptRenderer.__handle = sv.draw_handler_add(
+                MUV_UVSculptRenderer.draw_brush,
+                (obj, context), "WINDOW", "POST_PIXEL")
+
+    @staticmethod
+    def handle_remove():
+        if MUV_UVSculptRenderer.__handle is not None:
+            sv = bpy.types.SpaceView3D
+            sv.draw_handler_remove(
+                MUV_UVSculptRenderer.__handle, "WINDOW")
+            MUV_UVSculptRenderer.__handle = None
+
+    @staticmethod
+    def draw_brush(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.uvsculpt_brush_color
+
+        bgl.glBegin(bgl.GL_LINE_STRIP)
+        bgl.glColor4f(color[0], color[1], color[2], color[3])
+        x = sc.muv_uvsculpt_radius * cos(0.0)
+        y = sc.muv_uvsculpt_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()
+
+
+class MUV_UVSculptOps(bpy.types.Operator):
+    """
+    Operation class: UV Sculpt in View3D
+    """
+
+    bl_idname = "uv.muv_uvsculpt_ops"
+    bl_label = "UV Sculpt"
+    bl_description = "UV Sculpt in View3D"
+    bl_options = {'REGISTER'}
+
+    def __init__(self):
+        self.__timer = None
+        self.__loop_info = []
+        self.__stroking = False
+        self.current_mco = Vector((0.0, 0.0))
+        self.__initial_mco = Vector((0.0, 0.0))
+
+    def __get_strength(self, p, len_, factor):
+        f = factor
+
+        if p > len_:
+            return 0.0
+
+        if p < 0.0:
+            return f
+
+        return (len_ - p) * f / len_
+
+    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('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_uvsculpt_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": self.__get_strength(
+                            diff.length, sc.muv_uvsculpt_radius,
+                            sc.muv_uvsculpt_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_uvsculpt_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_uvsculpt_tools == 'PINCH':
+            _, region, space = common.get_space('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_uvsculpt_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": self.__get_strength(
+                                diff.length, sc.muv_uvsculpt_radius,
+                                sc.muv_uvsculpt_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_uvsculpt_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_uvsculpt_tools == 'RELAX':
+            _, region, space = common.get_space('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_uvsculpt_radius:
+                        continue
+                    db = vert_db[l.vert]
+                    strength = self.__get_strength(diff.length,
+                                                   sc.muv_uvsculpt_radius,
+                                                   sc.muv_uvsculpt_strength)
+
+                    base = (1.0 - strength) * l[uv_layer].uv
+                    if sc.muv_uvsculpt_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_uvsculpt_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_uvsculpt_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):
+        props = context.scene.muv_props.uvsculpt
+
+        if context.area:
+            context.area.tag_redraw()
+
+        if not props.running:
+            if self.__timer is not None:
+                MUV_UVSculptRenderer.handle_remove()
+                context.window_manager.event_timer_remove(self.__timer)
+                self.__timer = None
+            return {'FINISHED'}
+
+        self.current_mco = Vector((event.mouse_region_x, event.mouse_region_y))
+
+        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
+        elif event.type == 'MOUSEMOVE':
+            if self.__stroking:
+                self.__stroke_apply(context, event)
+        elif event.type == 'TIMER':
+            if self.__stroking:
+                self.__stroke_apply(context, event)
+
+        return {'PASS_THROUGH'}
+
+    def invoke(self, context, _):
+        props = context.scene.muv_props.uvsculpt
+
+        if context.area:
+            context.area.tag_redraw()
+
+        if props.running:
+            props.running = False
+            return {'FINISHED'}
+
+        props.running = True
+        if self.__timer is None:
+            self.__timer = context.window_manager.event_timer_add(
+                0.1, context.window)
+            context.window_manager.modal_handler_add(self)
+            MUV_UVSculptRenderer.handle_add(self, context)
+
+        return {'RUNNING_MODAL'}
diff --git a/uv_magic_uv/muv_uvw_ops.py b/uv_magic_uv/op/uvw.py
similarity index 86%
rename from uv_magic_uv/muv_uvw_ops.py
rename to uv_magic_uv/op/uvw.py
index eb366e97641adf15a1d623e4f6719d06f7b06270..37d88a5345177030d6f8788de8d94206183f4ca5 100644
--- a/uv_magic_uv/muv_uvw_ops.py
+++ b/uv_magic_uv/op/uvw.py
@@ -20,9 +20,8 @@
 
 __author__ = "Alexander Milovsky, Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
-
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
 from math import sin, cos, pi
 
@@ -30,11 +29,12 @@ import bpy
 import bmesh
 from bpy.props import (
     FloatProperty,
-    FloatVectorProperty
+    FloatVectorProperty,
+    BoolProperty
 )
 from mathutils import Vector
 
-from . import muv_common
+from .. import common
 
 
 class MUV_UVWBoxMap(bpy.types.Operator):
@@ -62,6 +62,11 @@ class MUV_UVWBoxMap(bpy.types.Operator):
         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):
@@ -71,15 +76,17 @@ class MUV_UVWBoxMap(bpy.types.Operator):
     def execute(self, context):
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV layer
         if not bm.loops.layers.uv:
-            self.report(
-                {'WARNING'}, "Object must have more than one UV map")
-            return {'CANCELLED'}
-
+            if self.assign_uvmap:
+                bm.loops.layers.uv.new()
+            else:
+                self.report(
+                    {'WARNING'}, "Object must have more than one UV map")
+                return {'CANCELLED'}
         uv_layer = bm.loops.layers.uv.verify()
 
         scale = 1.0 / self.size
@@ -168,6 +175,11 @@ class MUV_UVWBestPlanerMap(bpy.types.Operator):
         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):
@@ -177,14 +189,17 @@ class MUV_UVWBestPlanerMap(bpy.types.Operator):
     def execute(self, context):
         obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        if common.check_version(2, 73, 0) >= 0:
             bm.faces.ensure_lookup_table()
 
         # get UV layer
         if not bm.loops.layers.uv:
-            self.report(
-                {'WARNING'}, "Object must have more than one UV map")
-            return {'CANCELLED'}
+            if self.assign_uvmap:
+                bm.loops.layers.uv.new()
+            else:
+                self.report(
+                    {'WARNING'}, "Object must have more than one UV map")
+                return {'CANCELLED'}
 
         uv_layer = bm.loops.layers.uv.verify()
 
diff --git a/uv_magic_uv/muv_wsuv_ops.py b/uv_magic_uv/op/world_scale_uv.py
similarity index 70%
rename from uv_magic_uv/muv_wsuv_ops.py
rename to uv_magic_uv/op/world_scale_uv.py
index 4ee8b4f932ec53bea4a80ca60767a277dd2d3f55..f1539ddbbfcd145761c0e2419cf5c762ccc0efba 100644
--- a/uv_magic_uv/muv_wsuv_ops.py
+++ b/uv_magic_uv/op/world_scale_uv.py
@@ -20,43 +20,32 @@
 
 __author__ = "McBuff, Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
-__version__ = "4.5"
-__date__ = "19 Nov 2017"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
 
+from math import sqrt
 
 import bpy
 import bmesh
 from mathutils import Vector
-from bpy.props import (
-    FloatProperty,
-    BoolProperty,
-    EnumProperty
-)
-from . import muv_common
+from bpy.props import EnumProperty
 
+from .. import common
 
-def calc_edge_scale(uv_layer, loop0, loop1):
-    v0 = loop0.vert.co
-    v1 = loop1.vert.co
-    uv0 = loop0[uv_layer].uv.copy()
-    uv1 = loop1[uv_layer].uv.copy()
 
-    dv = v1 - v0
-    duv = uv1 - uv0
+def measure_wsuv_info(obj):
+    mesh_area = common.measure_mesh_area(obj)
+    uv_area = common.measure_uv_area(obj)
 
-    scale = 0.0
-    if dv.magnitude > 0.00000001:
-        scale = duv.magnitude / dv.magnitude
+    if not uv_area:
+        return None, None, None
 
-    return scale
+    if mesh_area == 0.0:
+        density = 0.0
+    else:
+        density = sqrt(uv_area) / sqrt(mesh_area)
 
-
-def calc_face_scale(uv_layer, face):
-    es = 0.0
-    for i, l in enumerate(face.loops[1:]):
-        es = es + calc_edge_scale(uv_layer, face.loops[i], l)
-
-    return es
+    return uv_area, mesh_area, density
 
 
 class MUV_WSUVMeasure(bpy.types.Operator):
@@ -70,30 +59,22 @@ class MUV_WSUVMeasure(bpy.types.Operator):
     bl_options = {'REGISTER', 'UNDO'}
 
     def execute(self, context):
-        props = context.scene.muv_props.wsuv
-        obj = bpy.context.active_object
-        bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
-            bm.verts.ensure_lookup_table()
-            bm.edges.ensure_lookup_table()
-            bm.faces.ensure_lookup_table()
+        sc = context.scene
+        obj = context.active_object
 
-        if not bm.loops.layers.uv:
-            self.report({'WARNING'}, "Object must have more than one UV map")
+        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'}
-        uv_layer = bm.loops.layers.uv.verify()
 
-        sel_faces = [f for f in bm.faces if f.select]
-
-        # measure average face size
-        scale = 0.0
-        for f in sel_faces:
-            scale = scale + calc_face_scale(uv_layer, f)
+        sc.muv_wsuv_src_uv_area = uv_area
+        sc.muv_wsuv_src_mesh_area = mesh_area
+        sc.muv_wsuv_src_density = density
 
-        props.ref_scale = scale / len(sel_faces)
-
-        self.report(
-            {'INFO'}, "Average face size: {0}".format(props.ref_scale))
+        self.report({'INFO'},
+                    "UV Area: {0}, Mesh Area: {1}, Texel Density: {2}"
+                    .format(uv_area, mesh_area, density))
 
         return {'FINISHED'}
 
@@ -108,16 +89,6 @@ class MUV_WSUVApply(bpy.types.Operator):
     bl_description = "Apply scaled UV based on scale calculation"
     bl_options = {'REGISTER', 'UNDO'}
 
-    proportional_scaling = BoolProperty(
-        name="Proportional Scaling",
-        default=True
-    )
-    scaling_factor = FloatProperty(
-        name="Scaling Factor",
-        default=1.0,
-        max=1000.0,
-        min=0.00001
-    )
     origin = EnumProperty(
         name="Origin",
         description="Aspect Origin",
@@ -139,43 +110,38 @@ class MUV_WSUVApply(bpy.types.Operator):
     def draw(self, _):
         layout = self.layout
 
-        row = layout.row()
-        row.prop(self, "proportional_scaling")
-        row = layout.row()
-        row.prop(self, "scaling_factor")
-        if self.proportional_scaling:
-            row.enabled = False
+        layout.prop(self, "origin")
 
     def execute(self, context):
-        props = context.scene.muv_props.wsuv
-        obj = bpy.context.active_object
+        sc = context.scene
+        obj = context.active_object
         bm = bmesh.from_edit_mesh(obj.data)
-        if muv_common.check_version(2, 73, 0) >= 0:
+        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()
-
         sel_faces = [f for f in bm.faces if f.select]
 
-        # measure average face size
-        scale = 0.0
-        for f in sel_faces:
-            scale = scale + calc_face_scale(uv_layer, f)
-        scale = scale / len(sel_faces)
+        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'}
 
-        self.report(
-            {'INFO'}, "Average face size: {0}".format(scale))
+        uv_layer = bm.loops.layers.uv.verify()
 
-        if self.proportional_scaling:
-            factor = props.ref_scale / scale
-        else:
-            factor = self.scaling_factor
+        if sc.muv_wsuv_mode == 'PROPORTIONAL':
+            tgt_density = sc.muv_wsuv_src_density * sqrt(mesh_area) / \
+                sqrt(sc.muv_wsuv_src_mesh_area)
+        elif sc.muv_wsuv_mode == 'SCALING':
+            tgt_density = sc.muv_wsuv_src_density * sc.muv_wsuv_scaling_factor
+        elif sc.muv_wsuv_mode == 'USER':
+            tgt_density = sc.muv_wsuv_tgt_density
+        elif sc.muv_wsuv_mode == 'CONSTANT':
+            tgt_density = sc.muv_wsuv_src_density
+
+        factor = tgt_density / density
 
         # calculate origin
         if self.origin == 'CENTER':
diff --git a/uv_magic_uv/preferences.py b/uv_magic_uv/preferences.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb86804e0d3372a60d95b12ee833b5f9c9fe6612
--- /dev/null
+++ b/uv_magic_uv/preferences.py
@@ -0,0 +1,216 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+from bpy.props import (
+    FloatProperty,
+    FloatVectorProperty,
+)
+from bpy.types import AddonPreferences
+
+
+class MUV_Preferences(AddonPreferences):
+    """Preferences class: Preferences for this add-on"""
+
+    bl_idname = __package__
+
+    # for UV Sculpt
+    uvsculpt_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
+    uvinsp_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
+    uvinsp_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
+    texproj_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
+    uvbb_cp_size = FloatProperty(
+        name="Size",
+        description="Control Point Size",
+        default=6.0,
+        min=3.0,
+        max=100.0)
+    uvbb_cp_react_size = FloatProperty(
+        name="React Size",
+        description="Size event fired",
+        default=10.0,
+        min=3.0,
+        max=100.0)
+
+    def draw(self, _):
+        layout = self.layout
+
+        layout.label("[Configuration]")
+
+        layout.label("UV Sculpt:")
+        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, "uvsculpt_brush_color", text="")
+
+        layout.separator()
+
+        layout.label("UV Inspection:")
+        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, "uvinsp_overlapped_color", text="")
+        sp = sp.split(percentage=0.45)
+        col = sp.column()
+        col.label("Flipped UV Color:")
+        col.prop(self, "uvinsp_flipped_color", text="")
+
+        layout.separator()
+
+        layout.label("Texture Projection:")
+        sp = layout.split(percentage=0.05)
+        col = sp.column()       # spacer
+        sp = sp.split(percentage=0.3)
+        col = sp.column()
+        col.prop(self, "texproj_canvas_padding")
+
+        layout.separator()
+
+        layout.label("UV Bounding Box:")
+        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, "uvbb_cp_size")
+        col.prop(self, "uvbb_cp_react_size")
+
+        layout.label("--------------------------------------")
+
+        layout.label("[Description]")
+        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.label("--------------------------------------")
+
+        layout.label("[Location]")
+
+        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")
diff --git a/uv_magic_uv/properites.py b/uv_magic_uv/properites.py
new file mode 100644
index 0000000000000000000000000000000000000000..f40e9f1fb4660da1f58d0eee97b605432e7a3ba5
--- /dev/null
+++ b/uv_magic_uv/properites.py
@@ -0,0 +1,755 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+from bpy.props import (
+    FloatProperty,
+    EnumProperty,
+    BoolProperty,
+    FloatVectorProperty,
+    IntProperty
+)
+from mathutils import Vector
+
+from . import common
+
+
+def get_loaded_texture_name(_, __):
+    items = [(key, key, "") for key in bpy.data.images.keys()]
+    items.append(("None", "None", ""))
+    return items
+
+
+# Properties used in this add-on.
+class MUV_Properties():
+    cpuv = None
+    cpuv_obj = None
+    cpuv_selseq = None
+    transuv = None
+    uvbb = None
+    texlock = None
+    texproj = None
+    texwrap = None
+    mvuv = None
+    uvinsp = None
+    uvsculpt = None
+
+    def __init__(self):
+        self.cpuv = MUV_CPUVProps()
+        self.cpuv_obj = MUV_CPUVProps()
+        self.cpuv_selseq = MUV_CPUVSelSeqProps()
+        self.transuv = MUV_TransUVProps()
+        self.uvbb = MUV_UVBBProps()
+        self.texlock = MUV_TexLockProps()
+        self.texproj = MUV_TexProjProps()
+        self.texwrap = MUV_TexWrapProps()
+        self.mvuv = MUV_MVUVProps()
+        self.uvinsp = MUV_UVInspProps()
+        self.uvsculpt = MUV_UVSculptProps()
+
+
+class MUV_CPUVProps():
+    src_uvs = []
+    src_pin_uvs = []
+    src_seams = []
+
+
+class MUV_CPUVSelSeqProps():
+    src_uvs = []
+    src_pin_uvs = []
+    src_seams = []
+
+
+class MUV_TransUVProps():
+    topology_copied = []
+
+
+class MUV_TexProjProps():
+    running = False
+
+
+class MUV_UVBBProps():
+    uv_info_ini = []
+    ctrl_points_ini = []
+    ctrl_points = []
+    running = False
+
+
+class MUV_TexLockProps():
+    verts_orig = None
+    intr_verts_orig = None
+    intr_running = False
+
+
+class MUV_TexWrapProps():
+    ref_face_index = -1
+    ref_obj = None
+
+
+class MUV_MVUVProps():
+    running = False
+
+
+class MUV_UVInspProps():
+    display_running = False
+    overlapped_info = []
+    flipped_info = []
+
+
+class MUV_UVSculptProps():
+    running = False
+
+
+def init_props(scene):
+    scene.muv_props = MUV_Properties()
+
+    # UV Sculpt
+    scene.muv_uvsculpt_enabled = BoolProperty(
+        name="UV Sculpt",
+        description="UV Sculpt is enabled",
+        default=False
+    )
+    scene.muv_uvsculpt_radius = IntProperty(
+        name="Radius",
+        description="Radius of the brush",
+        min=1,
+        max=500,
+        default=30
+    )
+    scene.muv_uvsculpt_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_uvsculpt_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_uvsculpt_show_brush = BoolProperty(
+        name="Show Brush",
+        description="Show Brush",
+        default=True
+    )
+    scene.muv_uvsculpt_pinch_invert = BoolProperty(
+        name="Invert",
+        description="Pinch UV to invert direction",
+        default=False
+    )
+    scene.muv_uvsculpt_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'
+    )
+
+    # Texture Wrap
+    scene.muv_texwrap_enabled = BoolProperty(
+        name="Texture Wrap",
+        description="Texture Wrap is enabled",
+        default=False
+    )
+    scene.muv_texwrap_set_and_refer = BoolProperty(
+        name="Set and Refer",
+        description="Refer and set UV",
+        default=True
+    )
+    scene.muv_texwrap_selseq = BoolProperty(
+        name="Selection Sequence",
+        description="Set UV sequentially",
+        default=False
+    )
+
+    # UV inspection
+    scene.muv_seluv_enabled = BoolProperty(
+        name="Select UV Enabled",
+        description="Select UV is enabled",
+        default=False
+    )
+    scene.muv_uvinsp_enabled = BoolProperty(
+        name="UV Inspection Enabled",
+        description="UV Inspection is enabled",
+        default=False
+    )
+    scene.muv_uvinsp_show_overlapped = BoolProperty(
+        name="Overlapped",
+        description="Show overlapped UVs",
+        default=False
+    )
+    scene.muv_uvinsp_show_flipped = BoolProperty(
+        name="Flipped",
+        description="Show flipped UVs",
+        default=False
+    )
+    scene.muv_uvinsp_show_mode = EnumProperty(
+        name="Mode",
+        description="Show mode",
+        items=[
+            ('PART', "Part", "Show only overlapped/flipped part"),
+            ('FACE', "Face", "Show overlapped/flipped face")
+        ],
+        default='PART'
+    )
+
+    # Align UV
+    scene.muv_auv_enabled = BoolProperty(
+        name="Aline UV Enabled",
+        description="Align UV is enabled",
+        default=False
+    )
+    scene.muv_auv_transmission = BoolProperty(
+        name="Transmission",
+        description="Align linked UVs",
+        default=False
+    )
+    scene.muv_auv_select = BoolProperty(
+        name="Select",
+        description="Select UVs which are aligned",
+        default=False
+    )
+    scene.muv_auv_vertical = BoolProperty(
+        name="Vert-Infl (Vertical)",
+        description="Align vertical direction influenced "
+                    "by mesh vertex proportion",
+        default=False
+    )
+    scene.muv_auv_horizontal = BoolProperty(
+        name="Vert-Infl (Horizontal)",
+        description="Align horizontal direction influenced "
+                    "by mesh vertex proportion",
+        default=False
+    )
+    scene.muv_auv_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'
+    )
+
+    # Smooth UV
+    scene.muv_smuv_enabled = BoolProperty(
+        name="Smooth UV Enabled",
+        description="Smooth UV is enabled",
+        default=False
+    )
+    scene.muv_smuv_transmission = BoolProperty(
+        name="Transmission",
+        description="Smooth linked UVs",
+        default=False
+    )
+    scene.muv_smuv_mesh_infl = FloatProperty(
+        name="Mesh Influence",
+        description="Influence rate of mesh vertex",
+        min=0.0,
+        max=1.0,
+        default=0.0
+    )
+    scene.muv_smuv_select = BoolProperty(
+        name="Select",
+        description="Select UVs which are smoothed",
+        default=False
+    )
+
+    # UV Bounding Box
+    scene.muv_uvbb_enabled = BoolProperty(
+        name="UV Bounding Box Enabled",
+        description="UV Bounding Box is enabled",
+        default=False
+    )
+    scene.muv_uvbb_uniform_scaling = BoolProperty(
+        name="Uniform Scaling",
+        description="Enable Uniform Scaling",
+        default=False
+    )
+    scene.muv_uvbb_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")
+        ]
+    )
+
+    # Pack UV
+    scene.muv_packuv_enabled = BoolProperty(
+        name="Pack UV Enabled",
+        description="Pack UV is enabled",
+        default=False
+    )
+    scene.muv_packuv_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_packuv_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
+    )
+
+    # Move UV
+    scene.muv_mvuv_enabled = BoolProperty(
+        name="Move UV Enabled",
+        description="Move UV is enabled",
+        default=False
+    )
+
+    # UVW
+    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
+    )
+
+    # Texture Projection
+    scene.muv_texproj_enabled = BoolProperty(
+        name="Texture Projection Enabled",
+        description="Texture Projection is enabled",
+        default=False
+    )
+    scene.muv_texproj_tex_magnitude = FloatProperty(
+        name="Magnitude",
+        description="Texture Magnitude",
+        default=0.5,
+        min=0.0,
+        max=100.0
+    )
+    scene.muv_texproj_tex_image = EnumProperty(
+        name="Image",
+        description="Texture Image",
+        items=get_loaded_texture_name
+    )
+    scene.muv_texproj_tex_transparency = FloatProperty(
+        name="Transparency",
+        description="Texture Transparency",
+        default=0.2,
+        min=0.0,
+        max=1.0
+    )
+    scene.muv_texproj_adjust_window = BoolProperty(
+        name="Adjust Window",
+        description="Size of renderered texture is fitted to window",
+        default=True
+    )
+    scene.muv_texproj_apply_tex_aspect = BoolProperty(
+        name="Texture Aspect Ratio",
+        description="Apply Texture Aspect ratio to displayed texture",
+        default=True
+    )
+    scene.muv_texproj_assign_uvmap = BoolProperty(
+        name="Assign UVMap",
+        description="Assign UVMap when no UVmaps are available",
+        default=True
+    )
+
+    # Texture Lock
+    scene.muv_texlock_enabled = BoolProperty(
+        name="Texture Lock Enabled",
+        description="Texture Lock is enabled",
+        default=False
+    )
+    scene.muv_texlock_connect = BoolProperty(
+        name="Connect UV",
+        default=True
+    )
+
+    # World Scale UV
+    scene.muv_wsuv_enabled = BoolProperty(
+        name="World Scale UV Enabled",
+        description="World Scale UV is enabled",
+        default=False
+    )
+    scene.muv_wsuv_src_mesh_area = FloatProperty(
+        name="Mesh Area",
+        description="Source Mesh Area",
+        default=0.0,
+        min=0.0
+    )
+    scene.muv_wsuv_src_uv_area = FloatProperty(
+        name="UV Area",
+        description="Source UV Area",
+        default=0.0,
+        min=0.0
+    )
+    scene.muv_wsuv_src_density = FloatProperty(
+        name="Density",
+        description="Source Texel Density",
+        default=0.0,
+        min=0.0
+    )
+    scene.muv_wsuv_tgt_density = FloatProperty(
+        name="Density",
+        description="Target Texel Density",
+        default=0.0,
+        min=0.0
+    )
+    scene.muv_wsuv_mode = EnumProperty(
+        name="Mode",
+        description="Density calculation mode",
+        items=[
+            ('PROPORTIONAL', 'Proportional', 'Scale proportionally by mesh'),
+            ('SCALING', 'Scaling', 'Specify scale factor'),
+            ('USER', 'User', 'Specify density'),
+            ('CONSTANT', 'Constant', 'Constant density')
+        ],
+        default='CONSTANT'
+    )
+    scene.muv_wsuv_scaling_factor = FloatProperty(
+        name="Scaling Factor",
+        default=1.0,
+        max=1000.0,
+        min=0.00001
+    )
+    scene.muv_wsuv_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'
+    )
+
+    # Unwrap Constraint
+    scene.muv_unwrapconst_enabled = BoolProperty(
+        name="Unwrap Constraint Enabled",
+        description="Unwrap Constraint is enabled",
+        default=False
+    )
+    scene.muv_unwrapconst_u_const = BoolProperty(
+        name="U-Constraint",
+        description="Keep UV U-axis coordinate",
+        default=False
+    )
+    scene.muv_unwrapconst_v_const = BoolProperty(
+        name="V-Constraint",
+        description="Keep UV V-axis coordinate",
+        default=False
+    )
+
+    # Preserve UV Aspect
+    scene.muv_preserve_uv_enabled = BoolProperty(
+        name="Preserve UV Aspect Enabled",
+        description="Preserve UV Aspect is enabled",
+        default=False
+    )
+    scene.muv_preserve_uv_tex_image = EnumProperty(
+        name="Image",
+        description="Texture Image",
+        items=get_loaded_texture_name
+    )
+    scene.muv_preserve_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"
+    )
+
+    # Flip/Rotate UV
+    scene.muv_fliprot_enabled = BoolProperty(
+        name="Flip/Rotate UV Enabled",
+        description="Flip/Rotate UV is enabled",
+        default=False
+    )
+    scene.muv_fliprot_seams = BoolProperty(
+        name="Seams",
+        description="Seams",
+        default=True
+    )
+
+    # Mirror UV
+    scene.muv_mirroruv_enabled = BoolProperty(
+        name="Mirror UV Enabled",
+        description="Mirror UV is enabled",
+        default=False
+    )
+    scene.muv_mirroruv_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'
+    )
+
+    # Copy/Paste UV
+    scene.muv_cpuv_enabled = BoolProperty(
+        name="Copy/Paste UV Enabled",
+        description="Copy/Paste UV is enabled",
+        default=False
+    )
+    scene.muv_cpuv_copy_seams = BoolProperty(
+        name="Copy Seams",
+        description="Copy Seams",
+        default=True
+    )
+    scene.muv_cpuv_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_cpuv_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'
+    )
+
+    # Transfer UV
+    scene.muv_transuv_enabled = BoolProperty(
+        name="Transfer UV Enabled",
+        description="Transfer UV is enabled",
+        default=False
+    )
+    scene.muv_transuv_invert_normals = BoolProperty(
+        name="Invert Normals",
+        description="Invert Normals",
+        default=False
+    )
+    scene.muv_transuv_copy_seams = BoolProperty(
+        name="Copy Seams",
+        description="Copy Seams",
+        default=True
+    )
+
+    # Align UV Cursor
+    def auvc_get_cursor_loc(self):
+        area, _, space = common.get_space('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_auvc_cursor_loc'] = Vector((cx, cy))
+        return self.get('muv_auvc_cursor_loc', (0.0, 0.0))
+
+    def auvc_set_cursor_loc(self, value):
+        self['muv_auvc_cursor_loc'] = value
+        area, _, space = common.get_space('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_auvc_enabled = BoolProperty(
+        name="Align UV Cursor Enabled",
+        description="Align UV Cursor is enabled",
+        default=False
+    )
+    scene.muv_auvc_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_auvc_align_menu = 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")
+        ]
+    )
+
+
+def clear_props(scene):
+    del scene.muv_props
+
+    # UV Sculpt
+    del scene.muv_uvsculpt_enabled
+    del scene.muv_uvsculpt_radius
+    del scene.muv_uvsculpt_strength
+    del scene.muv_uvsculpt_tools
+    del scene.muv_uvsculpt_show_brush
+    del scene.muv_uvsculpt_pinch_invert
+    del scene.muv_uvsculpt_relax_method
+
+    # Texture Wrap
+    del scene.muv_texwrap_enabled
+    del scene.muv_texwrap_set_and_refer
+    del scene.muv_texwrap_selseq
+
+    # UV Inspection
+    del scene.muv_seluv_enabled
+    del scene.muv_uvinsp_enabled
+    del scene.muv_uvinsp_show_overlapped
+    del scene.muv_uvinsp_show_flipped
+    del scene.muv_uvinsp_show_mode
+
+    # Align UV
+    del scene.muv_auv_enabled
+    del scene.muv_auv_transmission
+    del scene.muv_auv_select
+    del scene.muv_auv_vertical
+    del scene.muv_auv_horizontal
+    del scene.muv_auv_location
+
+    # Smooth UV
+    del scene.muv_smuv_enabled
+    del scene.muv_smuv_transmission
+    del scene.muv_smuv_mesh_infl
+    del scene.muv_smuv_select
+
+    # UV Bounding Box
+    del scene.muv_uvbb_enabled
+    del scene.muv_uvbb_uniform_scaling
+    del scene.muv_uvbb_boundary
+
+    # Pack UV
+    del scene.muv_packuv_enabled
+    del scene.muv_packuv_allowable_center_deviation
+    del scene.muv_packuv_allowable_size_deviation
+
+    # Move UV
+    del scene.muv_mvuv_enabled
+
+    # UVW
+    del scene.muv_uvw_enabled
+    del scene.muv_uvw_assign_uvmap
+
+    # Texture Projection
+    del scene.muv_texproj_enabled
+    del scene.muv_texproj_tex_magnitude
+    del scene.muv_texproj_tex_image
+    del scene.muv_texproj_tex_transparency
+    del scene.muv_texproj_adjust_window
+    del scene.muv_texproj_apply_tex_aspect
+    del scene.muv_texproj_assign_uvmap
+
+    # Texture Lock
+    del scene.muv_texlock_enabled
+    del scene.muv_texlock_connect
+
+    # World Scale UV
+    del scene.muv_wsuv_enabled
+    del scene.muv_wsuv_src_mesh_area
+    del scene.muv_wsuv_src_uv_area
+    del scene.muv_wsuv_src_density
+    del scene.muv_wsuv_tgt_density
+    del scene.muv_wsuv_mode
+    del scene.muv_wsuv_scaling_factor
+    del scene.muv_wsuv_origin
+
+    # Unwrap Constraint
+    del scene.muv_unwrapconst_enabled
+    del scene.muv_unwrapconst_u_const
+    del scene.muv_unwrapconst_v_const
+
+    # Preserve UV Aspect
+    del scene.muv_preserve_uv_enabled
+    del scene.muv_preserve_uv_tex_image
+    del scene.muv_preserve_uv_origin
+
+    # Flip/Rotate UV
+    del scene.muv_fliprot_enabled
+    del scene.muv_fliprot_seams
+
+    # Mirror UV
+    del scene.muv_mirroruv_enabled
+    del scene.muv_mirroruv_axis
+
+    # Copy/Paste UV
+    del scene.muv_cpuv_enabled
+    del scene.muv_cpuv_copy_seams
+    del scene.muv_cpuv_mode
+    del scene.muv_cpuv_strategy
+
+    # Transfer UV
+    del scene.muv_transuv_enabled
+    del scene.muv_transuv_invert_normals
+    del scene.muv_transuv_copy_seams
+
+    # Align UV Cursor
+    del scene.muv_auvc_enabled
+    del scene.muv_auvc_cursor_loc
+    del scene.muv_auvc_align_menu
diff --git a/uv_magic_uv/ui/__init__.py b/uv_magic_uv/ui/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..00af3e0621f283a39164c64f1e5f3158801837c0
--- /dev/null
+++ b/uv_magic_uv/ui/__init__.py
@@ -0,0 +1,44 @@
+# <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.0"
+__date__ = "16 Feb 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_enhance)
+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_enhance
+
+import bpy
diff --git a/uv_magic_uv/ui/uvedit_copy_paste_uv.py b/uv_magic_uv/ui/uvedit_copy_paste_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..87b23fed73ab0f77024d4c9e68f6442c87c207c4
--- /dev/null
+++ b/uv_magic_uv/ui/uvedit_copy_paste_uv.py
@@ -0,0 +1,54 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_uvedit
+
+
+class IMAGE_PT_MUV_CPUV(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_CPUVIECopyUV.bl_idname,
+                     text="Copy")
+        row.operator(copy_paste_uv_uvedit.MUV_CPUVIEPasteUV.bl_idname,
+                     text="Paste")
diff --git a/uv_magic_uv/ui/uvedit_editor_enhance.py b/uv_magic_uv/ui/uvedit_editor_enhance.py
new file mode 100644
index 0000000000000000000000000000000000000000..dfe3097823b4c9ad8baa605dd37cb162f781bf47
--- /dev/null
+++ b/uv_magic_uv/ui/uvedit_editor_enhance.py
@@ -0,0 +1,136 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+
+from ..op import align_uv_cursor
+from ..op import uv_bounding_box
+from ..op import uv_inspection
+
+
+class IMAGE_PT_MUV_EE(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
+        props = sc.muv_props
+
+        box = layout.box()
+        box.prop(sc, "muv_auvc_enabled", text="Align UV Cursor")
+        if sc.muv_auvc_enabled:
+            box.prop(sc, "muv_auvc_align_menu", expand=True)
+
+            col = box.column(align=True)
+
+            row = col.row(align=True)
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Left Top")
+            ops.position = 'LEFT_TOP'
+            ops.base = sc.muv_auvc_align_menu
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Middle Top")
+            ops.position = 'MIDDLE_TOP'
+            ops.base = sc.muv_auvc_align_menu
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Right Top")
+            ops.position = 'RIGHT_TOP'
+            ops.base = sc.muv_auvc_align_menu
+
+            row = col.row(align=True)
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Left Middle")
+            ops.position = 'LEFT_MIDDLE'
+            ops.base = sc.muv_auvc_align_menu
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Center")
+            ops.position = 'CENTER'
+            ops.base = sc.muv_auvc_align_menu
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Right Middle")
+            ops.position = 'RIGHT_MIDDLE'
+            ops.base = sc.muv_auvc_align_menu
+
+            row = col.row(align=True)
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Left Bottom")
+            ops.position = 'LEFT_BOTTOM'
+            ops.base = sc.muv_auvc_align_menu
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Middle Bottom")
+            ops.position = 'MIDDLE_BOTTOM'
+            ops.base = sc.muv_auvc_align_menu
+            ops = row.operator(align_uv_cursor.MUV_AUVCAlignOps.bl_idname,
+                               text="Right Bottom")
+            ops.position = 'RIGHT_BOTTOM'
+            ops.base = sc.muv_auvc_align_menu
+
+        box = layout.box()
+        box.prop(sc, "muv_uvcloc_enabled", text="UV Cursor Location")
+        if sc.muv_uvcloc_enabled:
+            box.prop(sc, "muv_auvc_cursor_loc", text="")
+
+        box = layout.box()
+        box.prop(sc, "muv_uvbb_enabled", text="UV Bounding Box")
+        if sc.muv_uvbb_enabled:
+            if props.uvbb.running is False:
+                box.operator(uv_bounding_box.MUV_UVBBUpdater.bl_idname,
+                             text="Display", icon='PLAY')
+            else:
+                box.operator(uv_bounding_box.MUV_UVBBUpdater.bl_idname,
+                             text="Hide", icon='PAUSE')
+            box.prop(sc, "muv_uvbb_uniform_scaling", text="Uniform Scaling")
+            box.prop(sc, "muv_uvbb_boundary", text="Boundary")
+
+        box = layout.box()
+        box.prop(sc, "muv_uvinsp_enabled", text="UV Inspection")
+        if sc.muv_uvinsp_enabled:
+            row = box.row()
+            if not sc.muv_props.uvinsp.display_running:
+                row.operator(uv_inspection.MUV_UVInspDisplay.bl_idname,
+                             text="Display", icon='PLAY')
+            else:
+                row.operator(uv_inspection.MUV_UVInspDisplay.bl_idname,
+                             text="Hide", icon='PAUSE')
+                row.operator(uv_inspection.MUV_UVInspUpdate.bl_idname,
+                             text="Update")
+            row = box.row()
+            row.prop(sc, "muv_uvinsp_show_overlapped")
+            row.prop(sc, "muv_uvinsp_show_flipped")
+            row = box.row()
+            row.prop(sc, "muv_uvinsp_show_mode")
diff --git a/uv_magic_uv/ui/uvedit_uv_manipulation.py b/uv_magic_uv/ui/uvedit_uv_manipulation.py
new file mode 100644
index 0000000000000000000000000000000000000000..2231cdf4b3375223b0368a1ca83b4c8e45fc79c2
--- /dev/null
+++ b/uv_magic_uv/ui/uvedit_uv_manipulation.py
@@ -0,0 +1,117 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+
+from ..op import uv_inspection
+from ..op import align_uv
+from ..op import smooth_uv
+from ..op import pack_uv
+
+
+class IMAGE_PT_MUV_UVManip(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_auv_enabled", text="Align UV")
+        if sc.muv_auv_enabled:
+            col = box.column()
+            row = col.row(align=True)
+            ops = row.operator(align_uv.MUV_AUVCircle.bl_idname, text="Circle")
+            ops.transmission = sc.muv_auv_transmission
+            ops.select = sc.muv_auv_select
+            ops = row.operator(align_uv.MUV_AUVStraighten.bl_idname,
+                               text="Straighten")
+            ops.transmission = sc.muv_auv_transmission
+            ops.select = sc.muv_auv_select
+            ops.vertical = sc.muv_auv_vertical
+            ops.horizontal = sc.muv_auv_horizontal
+            row = col.row()
+            ops = row.operator(align_uv.MUV_AUVAxis.bl_idname, text="XY-axis")
+            ops.transmission = sc.muv_auv_transmission
+            ops.select = sc.muv_auv_select
+            ops.vertical = sc.muv_auv_vertical
+            ops.horizontal = sc.muv_auv_horizontal
+            ops.location = sc.muv_auv_location
+            row.prop(sc, "muv_auv_location", text="")
+
+            col = box.column(align=True)
+            row = col.row(align=True)
+            row.prop(sc, "muv_auv_transmission", text="Transmission")
+            row.prop(sc, "muv_auv_select", text="Select")
+            row = col.row(align=True)
+            row.prop(sc, "muv_auv_vertical", text="Vertical")
+            row.prop(sc, "muv_auv_horizontal", text="Horizontal")
+
+        box = layout.box()
+        box.prop(sc, "muv_smuv_enabled", text="Smooth UV")
+        if sc.muv_smuv_enabled:
+            ops = box.operator(smooth_uv.MUV_AUVSmooth.bl_idname,
+                               text="Smooth")
+            ops.transmission = sc.muv_smuv_transmission
+            ops.select = sc.muv_smuv_select
+            ops.mesh_infl = sc.muv_smuv_mesh_infl
+            col = box.column(align=True)
+            row = col.row(align=True)
+            row.prop(sc, "muv_smuv_transmission", text="Transmission")
+            row.prop(sc, "muv_smuv_select", text="Select")
+            col.prop(sc, "muv_smuv_mesh_infl", text="Mesh Influence")
+
+        box = layout.box()
+        box.prop(sc, "muv_seluv_enabled", text="Select UV")
+        if sc.muv_seluv_enabled:
+            row = box.row(align=True)
+            row.operator(uv_inspection.MUV_UVInspSelectOverlapped.bl_idname)
+            row.operator(uv_inspection.MUV_UVInspSelectFlipped.bl_idname)
+
+        box = layout.box()
+        box.prop(sc, "muv_packuv_enabled", text="Pack UV (Extension)")
+        if sc.muv_packuv_enabled:
+            ops = box.operator(pack_uv.MUV_PackUV.bl_idname, text="Pack UV")
+            ops.allowable_center_deviation = \
+                sc.muv_packuv_allowable_center_deviation
+            ops.allowable_size_deviation = \
+                sc.muv_packuv_allowable_size_deviation
+            box.label("Allowable Center Deviation:")
+            box.prop(sc, "muv_packuv_allowable_center_deviation", text="")
+            box.label("Allowable Size Deviation:")
+            box.prop(sc, "muv_packuv_allowable_size_deviation", text="")
diff --git a/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
new file mode 100644
index 0000000000000000000000000000000000000000..530b179775d5e39cc3a9eb2ad5f1a70a68b2d2ef
--- /dev/null
+++ b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
@@ -0,0 +1,81 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+
+from ..op import copy_paste_uv
+from ..op import transfer_uv
+
+
+class OBJECT_PT_MUV_CPUV(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_cpuv_enabled", text="Copy/Paste UV")
+        if sc.muv_cpuv_enabled:
+            row = box.row(align=True)
+            if sc.muv_cpuv_mode == 'DEFAULT':
+                row.menu(copy_paste_uv.MUV_CPUVCopyUVMenu.bl_idname,
+                         text="Copy")
+                row.menu(copy_paste_uv.MUV_CPUVPasteUVMenu.bl_idname,
+                         text="Paste")
+            elif sc.muv_cpuv_mode == 'SEL_SEQ':
+                row.menu(copy_paste_uv.MUV_CPUVSelSeqCopyUVMenu.bl_idname,
+                         text="Copy")
+                row.menu(copy_paste_uv.MUV_CPUVSelSeqPasteUVMenu.bl_idname,
+                         text="Paste")
+            box.prop(sc, "muv_cpuv_mode", expand=True)
+            box.prop(sc, "muv_cpuv_copy_seams", text="Seams")
+            box.prop(sc, "muv_cpuv_strategy", text="Strategy")
+
+        box = layout.box()
+        box.prop(sc, "muv_transuv_enabled", text="Transfer UV")
+        if sc.muv_transuv_enabled:
+            row = box.row(align=True)
+            row.operator(transfer_uv.MUV_TransUVCopy.bl_idname, text="Copy")
+            ops = row.operator(transfer_uv.MUV_TransUVPaste.bl_idname,
+                               text="Paste")
+            ops.invert_normals = sc.muv_transuv_invert_normals
+            ops.copy_seams = sc.muv_transuv_copy_seams
+            row = box.row()
+            row.prop(sc, "muv_transuv_invert_normals", text="Invert Normals")
+            row.prop(sc, "muv_transuv_copy_seams", text="Seams")
diff --git a/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
new file mode 100644
index 0000000000000000000000000000000000000000..5aa968f27ed82c6475a450fffd08585e9c549b3d
--- /dev/null
+++ b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
@@ -0,0 +1,56 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_object
+
+
+class OBJECT_PT_MUV_CPUVObj(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_CPUVObjCopyUVMenu.bl_idname,
+                 text="Copy")
+        row.menu(copy_paste_uv_object.MUV_CPUVObjPasteUVMenu.bl_idname,
+                 text="Paste")
+        layout.prop(sc, "muv_cpuv_copy_seams", text="Copy Seams")
diff --git a/uv_magic_uv/ui/view3d_uv_manipulation.py b/uv_magic_uv/ui/view3d_uv_manipulation.py
new file mode 100644
index 0000000000000000000000000000000000000000..76e0d3aacc4131e920d5b5e8e1b839632385b574
--- /dev/null
+++ b/uv_magic_uv/ui/view3d_uv_manipulation.py
@@ -0,0 +1,180 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+
+from ..op import flip_rotate_uv
+from ..op import mirror_uv
+from ..op import move_uv
+from ..op import preserve_uv_aspect
+from ..op import texture_lock
+from ..op import texture_wrap
+from ..op import uv_sculpt
+from ..op import world_scale_uv
+
+
+class OBJECT_PT_MUV_UVManip(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
+        props = sc.muv_props
+        layout = self.layout
+
+        box = layout.box()
+        box.prop(sc, "muv_fliprot_enabled", text="Flip/Rotate UV")
+        if sc.muv_fliprot_enabled:
+            row = box.row()
+            ops = row.operator(flip_rotate_uv.MUV_FlipRot.bl_idname,
+                               text="Flip/Rotate")
+            ops.seams = sc.muv_fliprot_seams
+            row.prop(sc, "muv_fliprot_seams", text="Seams")
+
+        box = layout.box()
+        box.prop(sc, "muv_mirroruv_enabled", text="Mirror UV")
+        if sc.muv_mirroruv_enabled:
+            row = box.row()
+            ops = row.operator(mirror_uv.MUV_MirrorUV.bl_idname, text="Mirror")
+            ops.axis = sc.muv_mirroruv_axis
+            row.prop(sc, "muv_mirroruv_axis", text="")
+
+        box = layout.box()
+        box.prop(sc, "muv_mvuv_enabled", text="Move UV")
+        if sc.muv_mvuv_enabled:
+            col = box.column()
+            col.operator(move_uv.MUV_MVUV.bl_idname, icon='PLAY', text="Start")
+            if props.mvuv.running:
+                col.enabled = False
+            else:
+                col.enabled = True
+
+        box = layout.box()
+        box.prop(sc, "muv_wsuv_enabled", text="World Scale UV")
+        if sc.muv_wsuv_enabled:
+            row = box.row(align=True)
+            row.operator(world_scale_uv.MUV_WSUVMeasure.bl_idname,
+                         text="Measure")
+            ops = row.operator(world_scale_uv.MUV_WSUVApply.bl_idname,
+                               text="Apply")
+            ops.origin = sc.muv_wsuv_origin
+            box.label("Source:")
+            sp = box.split(percentage=0.7)
+            col = sp.column(align=True)
+            col.prop(sc, "muv_wsuv_src_mesh_area", text="Mesh Area")
+            col.prop(sc, "muv_wsuv_src_uv_area", text="UV Area")
+            col.prop(sc, "muv_wsuv_src_density", text="Density")
+            col.enabled = False
+            sp = sp.split(percentage=1.0)
+            col = sp.column(align=True)
+            col.label("cm x cm")
+            col.label("px x px")
+            col.label("px/cm")
+            col.enabled = False
+            sp = box.split(percentage=0.3)
+            sp.label("Mode:")
+            sp = sp.split(percentage=1.0)
+            col = sp.column()
+            col.prop(sc, "muv_wsuv_mode", text="")
+            if sc.muv_wsuv_mode == 'USER':
+                col.prop(sc, "muv_wsuv_tgt_density", text="Density")
+            if sc.muv_wsuv_mode == 'SCALING':
+                col.prop(sc, "muv_wsuv_scaling_factor", text="Scaling Factor")
+            box.prop(sc, "muv_wsuv_origin", text="Origin")
+
+        box = layout.box()
+        box.prop(sc, "muv_preserve_uv_enabled", text="Preserve UV Aspect")
+        if sc.muv_preserve_uv_enabled:
+            row = box.row()
+            ops = row.operator(
+                preserve_uv_aspect.MUV_PreserveUVAspect.bl_idname,
+                text="Change Image")
+            ops.dest_img_name = sc.muv_preserve_uv_tex_image
+            ops.origin = sc.muv_preserve_uv_origin
+            row.prop(sc, "muv_preserve_uv_tex_image", text="")
+            box.prop(sc, "muv_preserve_uv_origin", text="Origin")
+
+        box = layout.box()
+        box.prop(sc, "muv_texlock_enabled", text="Texture Lock")
+        if sc.muv_texlock_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_TexLockStart.bl_idname, text="Lock")
+            ops = col.operator(texture_lock.MUV_TexLockStop.bl_idname,
+                               text="Unlock")
+            ops.connect = sc.muv_texlock_connect
+            col.prop(sc, "muv_texlock_connect", text="Connect")
+
+            row = box.row(align=True)
+            row.label("Interactive Mode:")
+            if not props.texlock.intr_running:
+                row.operator(texture_lock.MUV_TexLockIntrStart.bl_idname,
+                             icon='PLAY', text="Start")
+            else:
+                row.operator(texture_lock.MUV_TexLockIntrStop.bl_idname,
+                             icon="PAUSE", text="Stop")
+
+        box = layout.box()
+        box.prop(sc, "muv_texwrap_enabled", text="Texture Wrap")
+        if sc.muv_texwrap_enabled:
+            row = box.row(align=True)
+            row.operator(texture_wrap.MUV_TexWrapRefer.bl_idname, text="Refer")
+            row.operator(texture_wrap.MUV_TexWrapSet.bl_idname, text="Set")
+            box.prop(sc, "muv_texwrap_set_and_refer")
+            box.prop(sc, "muv_texwrap_selseq")
+
+        box = layout.box()
+        box.prop(sc, "muv_uvsculpt_enabled", text="UV Sculpt")
+        if sc.muv_uvsculpt_enabled:
+            if not props.uvsculpt.running:
+                box.operator(uv_sculpt.MUV_UVSculptOps.bl_idname,
+                             icon='PLAY', text="Start")
+            else:
+                box.operator(uv_sculpt.MUV_UVSculptOps.bl_idname,
+                             icon='PAUSE', text="Stop")
+            col = box.column()
+            col.label("Brush:")
+            col.prop(sc, "muv_uvsculpt_radius")
+            col.prop(sc, "muv_uvsculpt_strength")
+            box.prop(sc, "muv_uvsculpt_tools")
+            if sc.muv_uvsculpt_tools == 'PINCH':
+                box.prop(sc, "muv_uvsculpt_pinch_invert")
+            elif sc.muv_uvsculpt_tools == 'RELAX':
+                box.prop(sc, "muv_uvsculpt_relax_method")
+            box.prop(sc, "muv_uvsculpt_show_brush")
diff --git a/uv_magic_uv/ui/view3d_uv_mapping.py b/uv_magic_uv/ui/view3d_uv_mapping.py
new file mode 100644
index 0000000000000000000000000000000000000000..77c60c9ee7dfda761e2a50f29b0b26c022e62753
--- /dev/null
+++ b/uv_magic_uv/ui/view3d_uv_mapping.py
@@ -0,0 +1,99 @@
+# <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.0"
+__date__ = "16 Feb 2018"
+
+import bpy
+
+from ..op import texture_projection
+from ..op import unwrap_constraint
+from ..op import uvw
+
+
+class OBJECT_PT_MUV_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
+        props = sc.muv_props
+        layout = self.layout
+
+        box = layout.box()
+        box.prop(sc, "muv_unwrapconst_enabled", text="Unwrap Constraint")
+        if sc.muv_unwrapconst_enabled:
+            ops = box.operator(
+                unwrap_constraint.MUV_UnwrapConstraint.bl_idname,
+                text="Unwrap")
+            ops.u_const = sc.muv_unwrapconst_u_const
+            ops.v_const = sc.muv_unwrapconst_v_const
+            row = box.row(align=True)
+            row.prop(sc, "muv_unwrapconst_u_const", text="U-Constraint")
+            row.prop(sc, "muv_unwrapconst_v_const", text="V-Constraint")
+
+        box = layout.box()
+        box.prop(sc, "muv_texproj_enabled", text="Texture Projection")
+        if sc.muv_texproj_enabled:
+            row = box.row()
+            if not props.texproj.running:
+                row.operator(texture_projection.MUV_TexProjStart.bl_idname,
+                             text="Start", icon='PLAY')
+            else:
+                row.operator(texture_projection.MUV_TexProjStop.bl_idname,
+                             text="Stop", icon='PAUSE')
+            row.prop(sc, "muv_texproj_tex_image", text="")
+            box.prop(sc, "muv_texproj_tex_transparency", text="Transparency")
+            col = box.column(align=True)
+            row = col.row()
+            row.prop(sc, "muv_texproj_adjust_window", text="Adjust Window")
+            if not sc.muv_texproj_adjust_window:
+                row.prop(sc, "muv_texproj_tex_magnitude", text="Magnitude")
+            col.prop(sc, "muv_texproj_apply_tex_aspect",
+                     text="Texture Aspect Ratio")
+            col.prop(sc, "muv_texproj_assign_uvmap", text="Assign UVMap")
+            if props.texproj.running:
+                box.operator(texture_projection.MUV_TexProjProject.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_UVWBoxMap.bl_idname, text="Box")
+            ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+            ops = row.operator(uvw.MUV_UVWBestPlanerMap.bl_idname,
+                               text="Best Planner")
+            ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+            box.prop(sc, "muv_uvw_assign_uvmap", text="Assign UVMap")