diff options
Diffstat (limited to 'uv_magic_uv/op/pack_uv.py')
-rw-r--r-- | uv_magic_uv/op/pack_uv.py | 183 |
1 files changed, 168 insertions, 15 deletions
diff --git a/uv_magic_uv/op/pack_uv.py b/uv_magic_uv/op/pack_uv.py index 84f195c5..35685221 100644 --- a/uv_magic_uv/op/pack_uv.py +++ b/uv_magic_uv/op/pack_uv.py @@ -23,26 +23,126 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" +from math import fabs + import bpy from bpy.props import ( FloatProperty, FloatVectorProperty, BoolProperty, ) +import bmesh +import mathutils +from mathutils import Vector from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import pack_uv_impl as impl +from ..utils import compatibility as compat +from .. import common + + +def _is_valid_context(context): + obj = context.object + + # only edit mode is allowed to execute + if obj is None: + return False + if obj.type != 'MESH': + return False + if context.object.mode != 'EDIT': + return False + + # 'IMAGE_EDITOR' and 'VIEW_3D' space is allowed to execute. + # If 'View_3D' space is not allowed, you can't find option in Tool-Shelf + # after the execution + for space in context.area.spaces: + if (space.type == 'IMAGE_EDITOR') or (space.type == 'VIEW_3D'): + break + else: + return False + + return True + + +def _sort_island_faces(kd, uvs, isl1, isl2): + """ + Sort faces in island + """ + + sorted_faces = [] + for f in isl1['sorted']: + _, idx, _ = kd.find( + Vector((f['ave_uv'].x, f['ave_uv'].y, 0.0))) + sorted_faces.append(isl2['faces'][uvs[idx]['face_idx']]) + return sorted_faces + +def _group_island(island_info, allowable_center_deviation, + allowable_size_deviation): + """ + Group island + """ + + num_group = 0 + while True: + # search islands which is not parsed yet + isl_1 = None + for isl_1 in island_info: + if isl_1['group'] == -1: + break + else: + break # all faces are parsed + if isl_1 is None: + break + isl_1['group'] = num_group + isl_1['sorted'] = isl_1['faces'] -__all__ = [ - 'Properties', - 'MUV_OT_PackUV', -] + # search same island + for isl_2 in island_info: + if isl_2['group'] == -1: + dcx = isl_2['center'].x - isl_1['center'].x + dcy = isl_2['center'].y - isl_1['center'].y + dsx = isl_2['size'].x - isl_1['size'].x + dsy = isl_2['size'].y - isl_1['size'].y + center_x_matched = ( + fabs(dcx) < allowable_center_deviation[0] + ) + center_y_matched = ( + fabs(dcy) < allowable_center_deviation[1] + ) + size_x_matched = ( + fabs(dsx) < allowable_size_deviation[0] + ) + size_y_matched = ( + fabs(dsy) < allowable_size_deviation[1] + ) + center_matched = center_x_matched and center_y_matched + size_matched = size_x_matched and size_y_matched + num_uv_matched = (isl_2['num_uv'] == isl_1['num_uv']) + # are islands have same? + if center_matched and size_matched and num_uv_matched: + isl_2['group'] = num_group + kd = mathutils.kdtree.KDTree(len(isl_2['faces'])) + uvs = [ + { + 'uv': Vector( + (f['ave_uv'].x, f['ave_uv'].y, 0.0) + ), + 'face_idx': fidx + } for fidx, f in enumerate(isl_2['faces']) + ] + for i, uv in enumerate(uvs): + kd.insert(uv['uv'], i) + kd.balance() + # sort faces for copy/paste UV + isl_2['sorted'] = _sort_island_faces(kd, uvs, isl_1, isl_2) + num_group = num_group + 1 + + return num_group @PropertyClassRegistry() -class Properties: +class _Properties: idname = "pack_uv" @classmethod @@ -77,6 +177,7 @@ class Properties: @BlClassRegistry() +@compat.make_annotations class MUV_OT_PackUV(bpy.types.Operator): """ Operation class: Pack UV with same UV islands are integrated @@ -91,17 +192,17 @@ class MUV_OT_PackUV(bpy.types.Operator): bl_description = "Pack UV (Same UV Islands are integrated)" bl_options = {'REGISTER', 'UNDO'} - rotate: BoolProperty( + rotate = BoolProperty( name="Rotate", description="Rotate option used by default pack UV function", default=False) - margin: FloatProperty( + margin = FloatProperty( name="Margin", description="Margin used by default pack UV function", min=0, max=1, default=0.001) - allowable_center_deviation: FloatVectorProperty( + allowable_center_deviation = FloatVectorProperty( name="Allowable Center Deviation", description="Allowable center deviation to judge same UV island", min=0.000001, @@ -109,7 +210,7 @@ class MUV_OT_PackUV(bpy.types.Operator): default=(0.001, 0.001), size=2 ) - allowable_size_deviation: FloatVectorProperty( + allowable_size_deviation = FloatVectorProperty( name="Allowable Size Deviation", description="Allowable sizse deviation to judge same UV island", min=0.000001, @@ -118,12 +219,64 @@ class MUV_OT_PackUV(bpy.types.Operator): size=2 ) - def __init__(self): - self.__impl = impl.PackUVImpl() - @classmethod def poll(cls, context): - return impl.PackUVImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) def execute(self, context): - return self.__impl.execute(self, context) + obj = context.active_object + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.faces.ensure_lookup_table() + if not bm.loops.layers.uv: + self.report({'WARNING'}, + "Object must have more than one UV map") + return {'CANCELLED'} + uv_layer = bm.loops.layers.uv.verify() + + selected_faces = [f for f in bm.faces if f.select] + island_info = common.get_island_info(obj) + num_group = _group_island(island_info, + self.allowable_center_deviation, + self.allowable_size_deviation) + + loop_lists = [l for f in bm.faces for l in f.loops] + bpy.ops.mesh.select_all(action='DESELECT') + + # pack UV + for gidx in range(num_group): + group = list(filter( + lambda i, idx=gidx: i['group'] == idx, island_info)) + for f in group[0]['faces']: + f['face'].select = True + bmesh.update_edit_mesh(obj.data) + bpy.ops.uv.select_all(action='SELECT') + bpy.ops.uv.pack_islands(rotate=self.rotate, margin=self.margin) + + # copy/paste UV among same islands + for gidx in range(num_group): + group = list(filter( + lambda i, idx=gidx: i['group'] == idx, island_info)) + if len(group) <= 1: + continue + for g in group[1:]: + for (src_face, dest_face) in zip( + group[0]['sorted'], g['sorted']): + for (src_loop, dest_loop) in zip( + src_face['face'].loops, dest_face['face'].loops): + loop_lists[dest_loop.index][uv_layer].uv = loop_lists[ + src_loop.index][uv_layer].uv + + # restore face/UV selection + bpy.ops.uv.select_all(action='DESELECT') + bpy.ops.mesh.select_all(action='DESELECT') + for f in selected_faces: + f.select = True + bpy.ops.uv.select_all(action='SELECT') + + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} |