diff options
author | Nutti <nutti.metro@gmail.com> | 2018-11-17 15:11:55 +0300 |
---|---|---|
committer | Nutti <nutti.metro@gmail.com> | 2018-11-17 15:11:55 +0300 |
commit | 2532b96844c121b710e1a1973d2a5ff824ab3be4 (patch) | |
tree | c1f7682a77310f53ed4c2682b0788f73aa677d69 /uv_magic_uv/op/uv_sculpt.py | |
parent | 92ebf635ee2a9ddbc7fbf4a0c06b9732ee555b27 (diff) |
Magic UV: Release v5.2
* Bulit-in menu preferences
* Add-on updater
* Copy/Paste UV
* Add option "[New]" for pasting to newly allocated UV map
* Add option "[All]" for pasting all UV maps at once
* Align UV
* Add option "Mesh Influence"
* World Scale UV
* Add mode option "Manual" to allow the user specify the density manually
* Improve UI
* Cleanup documents
* Fix bugs
Diffstat (limited to 'uv_magic_uv/op/uv_sculpt.py')
-rw-r--r-- | uv_magic_uv/op/uv_sculpt.py | 271 |
1 files changed, 194 insertions, 77 deletions
diff --git a/uv_magic_uv/op/uv_sculpt.py b/uv_magic_uv/op/uv_sculpt.py index 2bf76abd..63c1adfe 100644 --- a/uv_magic_uv/op/uv_sculpt.py +++ b/uv_magic_uv/op/uv_sculpt.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "5.1" -__date__ = "24 Feb 2018" +__version__ = "5.2" +__date__ = "17 Nov 2018" from math import pi, cos, tan, sin @@ -32,39 +32,172 @@ from mathutils import Vector from bpy_extras import view3d_utils from mathutils.bvhtree import BVHTree from mathutils.geometry import barycentric_transform +from bpy.props import ( + BoolProperty, + IntProperty, + EnumProperty, + FloatProperty, +) from .. import common -class MUV_UVSculptRenderer(bpy.types.Operator): +__all__ = [ + 'Properties', + 'Operator', +] + + +def is_valid_context(context): + obj = context.object + + # only edit mode is allowed to execute + if obj is None: + return False + if obj.type != 'MESH': + return False + if context.object.mode != 'EDIT': + return False + + # only 'VIEW_3D' space is allowed to execute + for space in context.area.spaces: + if space.type == 'VIEW_3D': + break + else: + return False + + return True + + +class Properties: + @classmethod + def init_props(cls, scene): + def get_func(_): + return Operator.is_running(bpy.context) + + def set_func(_, __): + pass + + def update_func(_, __): + bpy.ops.uv.muv_uv_sculpt_operator('INVOKE_REGION_WIN') + + scene.muv_uv_sculpt_enabled = BoolProperty( + name="UV Sculpt", + description="UV Sculpt is enabled", + default=False + ) + scene.muv_uv_sculpt_enable = BoolProperty( + name="UV Sculpt Showed", + description="UV Sculpt is enabled", + default=False, + get=get_func, + set=set_func, + update=update_func + ) + scene.muv_uv_sculpt_radius = IntProperty( + name="Radius", + description="Radius of the brush", + min=1, + max=500, + default=30 + ) + scene.muv_uv_sculpt_strength = FloatProperty( + name="Strength", + description="How powerful the effect of the brush when applied", + min=0.0, + max=1.0, + default=0.03, + ) + scene.muv_uv_sculpt_tools = EnumProperty( + name="Tools", + description="Select Tools for the UV sculpt brushes", + items=[ + ('GRAB', "Grab", "Grab UVs"), + ('RELAX', "Relax", "Relax UVs"), + ('PINCH', "Pinch", "Pinch UVs") + ], + default='GRAB' + ) + scene.muv_uv_sculpt_show_brush = BoolProperty( + name="Show Brush", + description="Show Brush", + default=True + ) + scene.muv_uv_sculpt_pinch_invert = BoolProperty( + name="Invert", + description="Pinch UV to invert direction", + default=False + ) + scene.muv_uv_sculpt_relax_method = EnumProperty( + name="Method", + description="Algorithm used for relaxation", + items=[ + ('HC', "HC", "Use HC method for relaxation"), + ('LAPLACIAN', "Laplacian", + "Use laplacian method for relaxation") + ], + default='HC' + ) + + @classmethod + def del_props(cls, scene): + del scene.muv_uv_sculpt_enabled + del scene.muv_uv_sculpt_enable + del scene.muv_uv_sculpt_radius + del scene.muv_uv_sculpt_strength + del scene.muv_uv_sculpt_tools + del scene.muv_uv_sculpt_show_brush + del scene.muv_uv_sculpt_pinch_invert + del scene.muv_uv_sculpt_relax_method + + +class Operator(bpy.types.Operator): """ - Operation class: Render Brush + Operation class: UV Sculpt in View3D """ - bl_idname = "uv.muv_uvsculpt_renderer" - bl_label = "Brush Renderer" - bl_description = "Brush Renderer in View3D" + bl_idname = "uv.muv_uv_sculpt_operator" + bl_label = "UV Sculpt" + bl_description = "UV Sculpt in View3D" + bl_options = {'REGISTER'} __handle = None - - @staticmethod - def handle_add(obj, context): - if MUV_UVSculptRenderer.__handle is None: + __timer = None + + @classmethod + def poll(cls, context): + # we can not get area/space/region from console + if common.is_console_mode(): + return False + return is_valid_context(context) + + @classmethod + def is_running(cls, _): + return 1 if cls.__handle else 0 + + @classmethod + def handle_add(cls, obj, context): + if not cls.__handle: sv = bpy.types.SpaceView3D - MUV_UVSculptRenderer.__handle = sv.draw_handler_add( - MUV_UVSculptRenderer.draw_brush, - (obj, context), "WINDOW", "POST_PIXEL") + cls.__handle = sv.draw_handler_add(cls.draw_brush, (obj, context), + "WINDOW", "POST_PIXEL") + if not cls.__timer: + cls.__timer = context.window_manager.event_timer_add( + 0.1, context.window) + context.window_manager.modal_handler_add(obj) - @staticmethod - def handle_remove(): - if MUV_UVSculptRenderer.__handle is not None: + @classmethod + def handle_remove(cls, context): + if cls.__handle: sv = bpy.types.SpaceView3D - sv.draw_handler_remove( - MUV_UVSculptRenderer.__handle, "WINDOW") - MUV_UVSculptRenderer.__handle = None - - @staticmethod - def draw_brush(obj, context): + sv.draw_handler_remove(cls.__handle, "WINDOW") + cls.__handle = None + if cls.__timer: + context.window_manager.event_timer_remove(cls.__timer) + cls.__timer = None + + @classmethod + def draw_brush(cls, obj, context): sc = context.scene prefs = context.user_preferences.addons["uv_magic_uv"].preferences @@ -72,12 +205,12 @@ class MUV_UVSculptRenderer(bpy.types.Operator): theta = 2 * pi / num_segment fact_t = tan(theta) fact_r = cos(theta) - color = prefs.uvsculpt_brush_color + color = prefs.uv_sculpt_brush_color bgl.glBegin(bgl.GL_LINE_STRIP) bgl.glColor4f(color[0], color[1], color[2], color[3]) - x = sc.muv_uvsculpt_radius * cos(0.0) - y = sc.muv_uvsculpt_radius * sin(0.0) + x = sc.muv_uv_sculpt_radius * cos(0.0) + y = sc.muv_uv_sculpt_radius * sin(0.0) for _ in range(num_segment): bgl.glVertex2f(x + obj.current_mco.x, y + obj.current_mco.y) tx = -y @@ -88,19 +221,7 @@ class MUV_UVSculptRenderer(bpy.types.Operator): 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)) @@ -137,7 +258,7 @@ class MUV_UVSculptOps(bpy.types.Operator): 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: + if diff.length < sc.muv_uv_sculpt_radius: info = { "face_idx": f.index, "loop_idx": i, @@ -145,8 +266,8 @@ class MUV_UVSculptOps(bpy.types.Operator): "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) + diff.length, sc.muv_uv_sculpt_radius, + sc.muv_uv_sculpt_strength) } self.__loop_info.append(info) @@ -158,13 +279,13 @@ class MUV_UVSculptOps(bpy.types.Operator): uv_layer = bm.loops.layers.uv.verify() mco = self.current_mco - if sc.muv_uvsculpt_tools == 'GRAB': + if sc.muv_uv_sculpt_tools == 'GRAB': for info in self.__loop_info: diff_uv = (mco - self.__initial_mco) * info["strength"] l = bm.faces[info["face_idx"]].loops[info["loop_idx"]] l[uv_layer].uv = info["initial_uv"] + diff_uv / 100.0 - elif sc.muv_uvsculpt_tools == 'PINCH': + elif sc.muv_uv_sculpt_tools == 'PINCH': _, region, space = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D') loop_info = [] for f in bm.faces: @@ -174,7 +295,7 @@ class MUV_UVSculptOps(bpy.types.Operator): 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: + if diff.length < sc.muv_uv_sculpt_radius: info = { "face_idx": f.index, "loop_idx": i, @@ -182,8 +303,8 @@ class MUV_UVSculptOps(bpy.types.Operator): "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) + diff.length, sc.muv_uv_sculpt_radius, + sc.muv_uv_sculpt_strength) } loop_info.append(info) @@ -215,13 +336,13 @@ class MUV_UVSculptOps(bpy.types.Operator): # 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: + if sc.muv_uv_sculpt_pinch_invert: diff_uv = (l[uv_layer].uv - target_uv) * info["strength"] else: diff_uv = (target_uv - l[uv_layer].uv) * info["strength"] l[uv_layer].uv = l[uv_layer].uv + diff_uv / 10.0 - elif sc.muv_uvsculpt_tools == 'RELAX': + elif sc.muv_uv_sculpt_tools == 'RELAX': _, region, space = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D') # get vertex and loop relation @@ -265,19 +386,19 @@ class MUV_UVSculptOps(bpy.types.Operator): 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: + if diff.length >= sc.muv_uv_sculpt_radius: continue db = vert_db[l.vert] strength = self.__get_strength(diff.length, - sc.muv_uvsculpt_radius, - sc.muv_uvsculpt_strength) + sc.muv_uv_sculpt_radius, + sc.muv_uv_sculpt_strength) base = (1.0 - strength) * l[uv_layer].uv - if sc.muv_uvsculpt_relax_method == 'HC': + if sc.muv_uv_sculpt_relax_method == 'HC': t = 0.5 * (db["uv_b"] + db["uv_sum_b"] / d["uv_count"]) diff = strength * (db["uv_p"] - t) target_uv = base + diff - elif sc.muv_uvsculpt_relax_method == 'LAPLACIAN': + elif sc.muv_uv_sculpt_relax_method == 'LAPLACIAN': diff = strength * db["uv_p"] target_uv = base + diff else: @@ -294,7 +415,7 @@ class MUV_UVSculptOps(bpy.types.Operator): uv_layer = bm.loops.layers.uv.verify() mco = self.current_mco - if sc.muv_uvsculpt_tools == 'GRAB': + if sc.muv_uv_sculpt_tools == 'GRAB': for info in self.__loop_info: diff_uv = (mco - self.__initial_mco) * info["strength"] l = bm.faces[info["face_idx"]].loops[info["loop_idx"]] @@ -303,23 +424,24 @@ class MUV_UVSculptOps(bpy.types.Operator): 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 + if not Operator.is_running(context): + Operator.handle_remove(context) + return {'FINISHED'} self.current_mco = Vector((event.mouse_region_x, event.mouse_region_y)) - area, _, _ = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D') - if self.current_mco.x < 0 or self.current_mco.x > area.width or \ - self.current_mco.y < 0 or self.current_mco.y > area.height: + region_types = [ + 'HEADER', + 'UI', + 'TOOLS', + 'TOOL_PROPS', + ] + if not common.mouse_on_area(event, 'VIEW_3D') or \ + common.mouse_on_regions(event, 'VIEW_3D', region_types): return {'PASS_THROUGH'} if event.type == 'LEFTMOUSE': @@ -331,30 +453,25 @@ class MUV_UVSculptOps(bpy.types.Operator): if self.__stroking: self.__stroke_exit(context, event) self.__stroking = False + return {'RUNNING_MODAL'} elif event.type == 'MOUSEMOVE': if self.__stroking: self.__stroke_apply(context, event) + return {'RUNNING_MODAL'} elif event.type == 'TIMER': if self.__stroking: self.__stroke_apply(context, event) + return {'RUNNING_MODAL'} - return {'RUNNING_MODAL'} + 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) + if Operator.is_running(context): + Operator.handle_remove(context) + else: + Operator.handle_add(self, context) return {'RUNNING_MODAL'} |