From d6a4326ee3afb64915c87d5dcad86793b7e689f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=90=8C=E6=96=B0?= <3209970865@qq.com> Date: Thu, 23 Mar 2023 01:11:34 +0800 Subject: [PATCH 01/12] header only show method, axis, and angle Other properties are displayed to VIEW3D_HT_tool_header and VIEW3D_PT_tools_object_options --- simple_deform_helper/__init__.py | 13 +- simple_deform_helper/data.py | 58 -- simple_deform_helper/draw.py | 55 +- simple_deform_helper/gizmo.py | 620 ------------------ simple_deform_helper/gizmo/__init__.py | 96 +++ simple_deform_helper/gizmo/bend_axis.py | 107 +++ simple_deform_helper/gizmo/ctrl_tow_point.py | 341 ++++++++++ .../gizmo/ctrl_value_and_factor.py | 132 ++++ simple_deform_helper/operators.py | 12 +- simple_deform_helper/panel.py | 6 +- simple_deform_helper/preferences.py | 13 +- simple_deform_helper/utils.py | 579 ++++++++-------- 12 files changed, 1027 insertions(+), 1005 deletions(-) delete mode 100644 simple_deform_helper/data.py delete mode 100644 simple_deform_helper/gizmo.py create mode 100644 simple_deform_helper/gizmo/__init__.py create mode 100644 simple_deform_helper/gizmo/bend_axis.py create mode 100644 simple_deform_helper/gizmo/ctrl_tow_point.py create mode 100644 simple_deform_helper/gizmo/ctrl_value_and_factor.py diff --git a/simple_deform_helper/__init__.py b/simple_deform_helper/__init__.py index 8b94dbebf..9a9052c8d 100644 --- a/simple_deform_helper/__init__.py +++ b/simple_deform_helper/__init__.py @@ -1,5 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-or-later -from . import gizmo, operators, preferences, data, update, translate, panel +from . import ( + panel, # + gizmo, + utils, + update, + translate, + operators, + preferences, +) bl_info = { "name": "SimpleDeformHelper", @@ -15,6 +23,7 @@ bl_info = { module_tuple = ( panel, gizmo, + utils, update, translate, operators, @@ -23,8 +32,6 @@ module_tuple = ( def register(): - data.Data.load_gizmo_data() - for item in module_tuple: item.register() diff --git a/simple_deform_helper/data.py b/simple_deform_helper/data.py deleted file mode 100644 index 4da383ab7..000000000 --- a/simple_deform_helper/data.py +++ /dev/null @@ -1,58 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -from os.path import dirname, basename, realpath - -G_MODIFIERS_PROPERTY = [ # copy modifier data - 'angle', - 'deform_axis', - 'deform_method', - 'factor', - 'invert_vertex_group', - 'limits', - 'lock_x', - 'lock_y', - 'lock_z', - 'origin', - 'show_expanded', - 'show_in_editmode', - 'vertex_group', -] - -G_INDICES = ( - (0, 1), (0, 2), (1, 3), (2, 3), - (4, 5), (4, 6), (5, 7), (6, 7), - (0, 4), (1, 5), (2, 6), (3, 7)) - -G_NAME = 'ViewSimpleDeformGizmo_' # Temporary use files -G_CON_LIMIT_NAME = G_NAME + 'constraints_limit_rotation' # 约束名称 -G_ADDON_NAME = basename(dirname(realpath(__file__))) # "simple_deform_helper" - - -class Data: - G_GizmoCustomShapeDict = {} - G_SimpleDeformGizmoHandlerDit = {} - - @classmethod - def load_gizmo_data(cls) -> None: - import json - import os - json_path = os.path.join(os.path.dirname(__file__), "gizmo.json") - with open(json_path, "r") as file: - cls.G_GizmoCustomShapeDict = json.load(file) - - @staticmethod - def from_bmesh_get_triangle_face_co(mesh: 'bpy.types.Mesh') -> list: - """ - :param mesh: 输入一个网格数据 - :type mesh: bpy.data.meshes - :return list: 反回顶点列表[[co1,co2,co3],[co1,co2,co3]...] - """ - import bmesh - - bm = bmesh.new() - bm.from_mesh(mesh) - bm.faces.ensure_lookup_table() - bm.verts.ensure_lookup_table() - bmesh.ops.triangulate(bm, faces=bm.faces) - co_list = [list(float(format(j, ".4f")) for j in vert.co) for face in bm.faces for vert in face.verts] - bm.free() - return co_list diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index 9fb5a674b..54d74b53e 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -5,23 +5,18 @@ import gpu from gpu_extras.batch import batch_for_shader from mathutils import Vector -from .data import G_INDICES, G_MODIFIERS_PROPERTY, G_NAME, Data -from .utils import PublicClass, Utils +from .utils import PublicUtils -class Handler(Data): +class Handler: @classmethod def add_handler(cls): - """向3d视图添加绘制handler - 并将其存储下来 - """ if 'handler' not in cls.G_SimpleDeformGizmoHandlerDit: cls.G_SimpleDeformGizmoHandlerDit['handler'] = bpy.types.SpaceView3D.draw_handler_add( Draw3D.draw_bound_box, (), 'WINDOW', 'POST_VIEW') @classmethod def del_handler_text(cls): - if 'handler_text' in cls.G_SimpleDeformGizmoHandlerDit: bpy.types.SpaceView3D.draw_handler_remove( cls.G_SimpleDeformGizmoHandlerDit['handler_text'], 'WINDOW') @@ -30,11 +25,11 @@ class Handler(Data): @classmethod def del_handler(cls): data = bpy.data - if data.meshes.get(G_NAME): - data.meshes.remove(data.meshes.get(G_NAME)) + if data.meshes.get(cls.G_NAME): + data.meshes.remove(data.meshes.get(cls.G_NAME)) - if data.objects.get(G_NAME): - data.objects.remove(data.objects.get(G_NAME)) + if data.objects.get(cls.G_NAME): + data.objects.remove(data.objects.get(cls.G_NAME)) cls.del_handler_text() @@ -44,23 +39,9 @@ class Handler(Data): cls.G_SimpleDeformGizmoHandlerDit.clear() -class Draw3D(PublicClass, Data): - +class Draw3D(PublicUtils): @classmethod def draw_3d_shader(cls, pos, indices, color=None, *, shader_name='3D_UNIFORM_COLOR', draw_type='LINES'): - """ - :param draw_type: - :param shader_name: - :param color: - :param indices: - :param pos: - :type pos:list ((0,0,0),(1,1,1)) - 2D_FLAT_COLOR - 2D_IMAGE - 2D_SMOOTH_COLOR - 2D_UNIFORM_COLOR - 3D_FLAT_COLOR - 3D_SMOOTH_COLOR - 3D_UNIFORM_COLOR - 3D_POLYLINE_FLAT_COLOR - 3D_POLYLINE_SMOOTH_COLOR - 3D_POLYLINE_UNIFORM_COLOR - ('POINTS', 'LINES', 'TRIS', 'LINE_STRIP', 'LINE_LOOP','TRI_STRIP', - 'TRI_FAN', 'LINES_ADJ', 'TRIS_ADJ', 'LINE_STRIP_ADJ') - `NONE`, `ALWAYS`, `LESS`, `LESS_EQUAL`, `EQUAL`, `GREATER` and `GREATER_EQUAL` - """ - shader = gpu.shader.from_builtin(shader_name) if draw_type == 'POINTS': batch = batch_for_shader(shader, draw_type, {'pos': pos}) @@ -105,9 +86,9 @@ class Draw3D(PublicClass, Data): @classmethod def draw_box(cls, data, mat): pref = cls.pref_() - coords = Utils.matrix_calculation(mat, - cls.data_to_calculation(data)) - cls.draw_3d_shader(coords, G_INDICES, pref.bound_box_color) + coords = PublicUtils.matrix_calculation(mat, + cls.data_to_calculation(data)) + cls.draw_3d_shader(coords, cls.G_INDICES, pref.bound_box_color) @classmethod def data_to_calculation(cls, data): @@ -130,9 +111,9 @@ class Draw3D(PublicClass, Data): if 'draw_limits_bound_box' in handler_dit: # draw limits_bound_box mat, data = handler_dit['draw_limits_bound_box'] - coords = Utils.matrix_calculation(mat, cls.data_to_calculation(data)) + coords = PublicUtils.matrix_calculation(mat, cls.data_to_calculation(data)) cls.draw_3d_shader(coords, - G_INDICES, + cls.G_INDICES, pref.limits_bound_box_color) @classmethod @@ -156,7 +137,7 @@ class Draw3D(PublicClass, Data): # draw deform mesh if 'draw' in handler_dit: pos, indices, mat, mod_data, limits = handler_dit['draw'] - if ([getattr(active, i) for i in G_MODIFIERS_PROPERTY] == mod_data) and ( + if ([getattr(active, i) for i in cls.G_MODIFIERS_PROPERTY] == mod_data) and ( ob.matrix_world == mat) and limits == active.limits[:]: cls.draw_3d_shader( pos, indices, pref.deform_wireframe_color) @@ -174,14 +155,14 @@ class Draw3D(PublicClass, Data): modifier = context.object.modifiers.active # 活动修改器 pref = cls.pref_() - simple_poll = Utils.simple_deform_poll(context) + simple_poll = PublicUtils.simple_deform_public_poll(context) bend = modifier and (modifier.deform_method == 'BEND') display_switch_axis = not pref.display_bend_axis_switch_gizmo cls.draw_scale_text(obj) - Utils.update_co_data(obj, modifier) + PublicUtils.update_co_data(obj, modifier) - co_data = Utils.generate_co_data() + co_data = PublicUtils.generate_co_data() if simple_poll and ((not bend) or display_switch_axis): # draw bound box @@ -191,7 +172,7 @@ class Draw3D(PublicClass, Data): cls.draw_limits_bound_box() elif simple_poll and (bend and not display_switch_axis): cls.draw_box(co_data, matrix) - Utils.new_empty(obj, modifier) + PublicUtils.new_empty(obj, modifier) @classmethod def draw_bound_box(cls): @@ -202,7 +183,7 @@ class Draw3D(PublicClass, Data): gpu.state.depth_test_set('ALWAYS') context = bpy.context - if Utils.simple_deform_poll(context): + if PublicUtils.simple_deform_public_poll(context): cls.is_draw_box(context) else: Handler.del_handler() diff --git a/simple_deform_helper/gizmo.py b/simple_deform_helper/gizmo.py deleted file mode 100644 index 15d03cd99..000000000 --- a/simple_deform_helper/gizmo.py +++ /dev/null @@ -1,620 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -import bpy -import math -from bpy_extras import view3d_utils -from mathutils import Vector, Euler -from bpy.types import ( - Gizmo, - GizmoGroup, -) - -from .draw import Handler -from .utils import Utils, PublicClass -from .data import Data - - -class CustomGizmo(Gizmo, Utils, Handler, Data): - """绘制自定义Gizmo""" - bl_idname = '_Custom_Gizmo' - - def setup(self): - self.draw_type = 'None_GizmoGroup_' - if not hasattr(self, 'custom_shape'): - self.custom_shape = {} - for i in self.G_GizmoCustomShapeDict: - self.custom_shape[i] = self.new_custom_shape( - 'TRIS', self.G_GizmoCustomShapeDict[i]) - self.add_handler() - - def draw(self, context): - self.draw_custom_shape(self.custom_shape[self.draw_type]) - - def draw_select(self, context, select_id): - self.draw_custom_shape( - self.custom_shape[self.draw_type], select_id=select_id) - - def invoke(self, context, event): - return {'RUNNING_MODAL'} - - def modal(self, context, event, tweak): - self.add_handler() - - self.update_bound_box(context.object) - self.update_empty_matrix() - return {'RUNNING_MODAL'} - - -class ViewSimpleDeformGizmo(Gizmo, Utils, Handler, Data, PublicClass): - """显示轴向切换拖动点Gizmo(两个点) - """ - bl_idname = 'ViewSimpleDeformGizmo' - - bl_target_properties = ( - {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, - {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, - {'id': 'angle', 'type': 'FLOAT', 'array_length': 1}, - ) - - __slots__ = ( - 'mod', - 'up', - 'down', - 'up_', - 'down_', - 'draw_type', - 'mouse_dpi', - 'ctrl_mode', - 'empty_object', - 'init_mouse_y', - 'init_mouse_x', - 'custom_shape', - 'int_value_angle', - 'value_deform_axis', - 'int_value_up_limits', - 'int_value_down_limits', - 'rotate_follow_modifier', - ) - - def update_gizmo_rotate(self, axis, mod): - if self.rotate_follow_modifier: - rot = Euler() - if axis == 'X' and (not self.is_positive(mod.angle)): - rot.z = math.pi - - elif axis == 'Y': - if self.is_positive(mod.angle): - rot.z = -(math.pi / 2) - else: - rot.z = math.pi / 2 - elif axis == 'Z': - if self.is_positive(mod.angle): - rot.x = rot.z = rot.y = math.pi / 2 - else: - rot.z = rot.y = math.pi / 2 - rot.x = -(math.pi / 2) - - rot = rot.to_matrix() - self.matrix_basis = self.matrix_basis @ rot.to_4x4() - - def update_draw_limits_bound_box(self, data, mod, axis, mat, up_, down_): - top, bottom, left, right, front, back = data - if mod.origin: - vector_axis = self.get_vector_axis(mod) - origin_mat = mod.origin.matrix_world.to_3x3() - axis_ = origin_mat @ vector_axis - point_lit = [[top, bottom], [left, right], [front, back]] - for f in range(point_lit.__len__()): - i = point_lit[f][0] - j = point_lit[f][1] - angle = self.point_to_angle(i, j, f, axis_) - if abs(angle - 180) < 0.00001: - point_lit[f][1], point_lit[f][0] = up_, down_ - elif abs(angle) < 0.00001: - point_lit[f][0], point_lit[f][1] = up_, down_ - [[top, bottom], [left, right], [front, back]] = point_lit - else: - top, bottom, left, right, front, back = self.get_up_down_return_list( - mod, axis, up_, down_, data) - data = top, bottom, left, right, front, back - (top, bottom, left, right, front, - back) = self.matrix_calculation(mat.inverted(), data) - self.G_SimpleDeformGizmoHandlerDit['draw_limits_bound_box'] = ( - mat, ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],))) - - def update_matrix_basis_translation(self, co, mat, up_, down_): - if 'angle' == self.ctrl_mode: - self.matrix_basis.translation = mat @ Vector((co[1])) - elif 'up_limits' == self.ctrl_mode: - self.matrix_basis.translation = up_ - elif 'down_limits' == self.ctrl_mode: - self.matrix_basis.translation = down_ - - def update_gizmo_matrix(self, context): - ob = context.object - mat = ob.matrix_world - mod = context.object.modifiers.active - axis = mod.deform_axis - if mod.origin: - self.matrix_basis = mod.origin.matrix_world.normalized() - else: - self.matrix_basis = ob.matrix_world.normalized() - - co = self.generate_co_data() - self.update_gizmo_rotate(axis, mod) - # calculation limits position - top, bottom, left, right, front, back = self.each_face_pos(mat) - (up, down), (up_, down_) = self.get_limits_pos( - mod, (top, bottom, left, right, front, back)) - self.update_matrix_basis_translation(co, mat, up_, down_) - - self.up = up - self.down = down - self.up_ = up_ - self.down_ = down_ - self.G_SimpleDeformGizmoHandlerDit['draw_line'] = ( - (up, down), (up_, down_)) - data = top, bottom, left, right, front, back - self.update_draw_limits_bound_box(data, mod, axis, mat, up_, down_) - - def setup(self): - self.generate_co_data() - self.draw_type = 'None_GizmoGroup_' - self.ctrl_mode = 'angle' # up_limits , down_limits - self.mouse_dpi = 10 - self.rotate_follow_modifier = True - if not hasattr(self, 'custom_shape'): - self.custom_shape = {} - for i in self.G_GizmoCustomShapeDict: - item = self.G_GizmoCustomShapeDict[i] - self.custom_shape[i] = self.new_custom_shape('TRIS', item) - self.add_handler() - - def draw(self, context): - self.add_handler() - - self.update_gizmo_matrix(context) - self.draw_custom_shape(self.custom_shape[self.draw_type]) - - def draw_select(self, context, select_id): - self.update_gizmo_matrix(context) - self.draw_custom_shape( - self.custom_shape[self.draw_type], select_id=select_id) - - def invoke(self, context, event): - self.init_mouse_y = event.mouse_y - self.init_mouse_x = event.mouse_x - mod = context.object.modifiers.active - limits = mod.limits - up_limits = limits[1] - down_limits = limits[0] - - if 'angle' == self.ctrl_mode: - self.int_value_angle = self.target_get_value('angle') - elif 'up_limits' == self.ctrl_mode: - self.int_value_up_limits = up_limits - self.target_set_value('up_limits', self.int_value_up_limits) - elif 'down_limits' == self.ctrl_mode: - self.int_value_down_limits = down_limits - self.target_set_value('down_limits', self.int_value_down_limits) - return {'RUNNING_MODAL'} - - def exit(self, context, cancel): - context.area.header_text_set(None) - - if cancel: - if 'angle' == self.ctrl_mode: - self.target_set_value('angle', self.int_value_angle) - elif 'deform_axis' == self.ctrl_mode: - self.target_set_value('deform_axis', self.value_deform_axis) - elif 'up_limits' == self.ctrl_mode: - self.target_set_value('up_limits', self.int_value_up_limits) - - elif 'down_limits' == self.ctrl_mode: - self.target_set_value( - 'down_limits', self.int_value_down_limits) - - def delta_update(self, context, event, delta): - if ('draw_line' in self.G_SimpleDeformGizmoHandlerDit) and (self.ctrl_mode in ('up_limits', 'down_limits')): - x, y = view3d_utils.location_3d_to_region_2d( - context.region, context.space_data.region_3d, self.up) - x2, y2 = view3d_utils.location_3d_to_region_2d( - context.region, context.space_data.region_3d, self.down) - - mouse_line_distance = math.sqrt(((event.mouse_region_x - x2) ** 2) + - ((event.mouse_region_y - y2) ** 2)) - straight_line_distance = math.sqrt(((x2 - x) ** 2) + - ((y2 - y) ** 2)) - delta = mouse_line_distance / \ - straight_line_distance + 0 - - v_up = Vector((x, y)) - v_down = Vector((x2, y2)) - limits_angle = v_up - v_down - - mouse_v = Vector((event.mouse_region_x, event.mouse_region_y)) - - mouse_angle = mouse_v - v_down - angle_ = mouse_angle.angle(limits_angle) - if angle_ > (math.pi / 2): - delta = 0 - return delta - - def set_down_value(self, data, mu): - up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data - value = self.value_limit(delta, max_value=mu - - limit_scope if middle else max_value) - self.target_set_value('down_limits', value) - if event.ctrl: - self.target_set_value( - 'up_limits', value + difference_value) - elif middle: - if origin_mode == 'LIMITS_MIDDLE': - self.target_set_value('up_limits', mu - (value - mu)) - elif origin_mode == 'MIDDLE': - self.target_set_value('up_limits', 1 - value) - else: - self.target_set_value('up_limits', up_limits) - else: - self.target_set_value('up_limits', up_limits) - - def set_up_value(self, data, mu): - up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data - value = self.value_limit(delta, min_value=mu + - limit_scope if middle else min_value) - self.target_set_value('up_limits', value) - if event.ctrl: - self.target_set_value( - 'down_limits', value - difference_value) - elif middle: - if origin_mode == 'LIMITS_MIDDLE': - self.target_set_value('down_limits', mu - (value - mu)) - elif origin_mode == 'MIDDLE': - self.target_set_value('down_limits', 1 - value) - else: - self.target_set_value('down_limits', down_limits) - else: - self.target_set_value('down_limits', down_limits) - - def set_prop_value(self, data): - up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data - mu = (up_limits + down_limits) / 2 - if 'angle' == self.ctrl_mode: - value = self.int_value_angle - delta - self.target_set_value('angle', value) - elif 'up_limits' == self.ctrl_mode: - self.set_up_value(data, mu) - elif 'down_limits' == self.ctrl_mode: - self.set_down_value(data, mu) - - def update_header_text(self, context, mod, origin, up_limits, down_limits): - t = lambda a: bpy.app.translations.pgettext(a) - - if (mod.deform_method in ('TWIST', 'BEND')) and (self.ctrl_mode in ('angle',)): - text = t("Angle") + ':{}'.format(math.degrees(mod.angle)) - elif 'up_limits' == self.ctrl_mode: - text = t("Upper limit") + ':{}'.format(up_limits) - elif 'down_limits' == self.ctrl_mode: - text = t("Down limit") + ':{}'.format(down_limits) - else: - text = t("Coefficient") + ':{}'.format(mod.factor) - text += ' ' - text += t(origin.bl_rna.properties[ - 'origin_mode'].enum_items[origin.origin_mode].name) - context.area.header_text_set(text) - - def event_ops(self, event, ob, origin): - """通过输入键位来更改属性""" - # event ctrl - data_path = ('object.SimpleDeformGizmo_PropertyGroup.origin_mode', - 'object.modifiers.active.origin.SimpleDeformGizmo_PropertyGroup.origin_mode') - - if event.type in ('WHEELUPMOUSE', 'WHEELDOWNMOUSE'): - reverse = (event.type == 'WHEELUPMOUSE') - for path in data_path: - bpy.ops.wm.context_cycle_enum( - data_path=path, reverse=reverse, wrap=True) - elif event.type in ('X', 'Y', 'Z'): - ob.modifiers.active.deform_axis = event.type - elif event.type == 'A': - self.pref.display_bend_axis_switch_gizmo = True - return {'FINISHED'} - self.add_handler() - - return {'RUNNING_MODAL'} - - def modal(self, context, event, tweak): - self.update_bound_box(context.object) - - delta = (self.init_mouse_x - event.mouse_x) / self.mouse_dpi - ob = context.object - mod = ob.modifiers.active - limits = mod.limits - up_limits = limits[1] - down_limits = limits[0] - origin = self.get_origin_property_group(mod, ob) - origin_mode = origin.origin_mode - middle = origin_mode in ('LIMITS_MIDDLE', 'MIDDLE') - limit_scope = self.pref.modifiers_limits_tolerance - max_value = up_limits - limit_scope - min_value = down_limits + limit_scope - difference_value = up_limits - down_limits - - if 'SNAP' in tweak: - delta = round(delta) - if 'PRECISE' in tweak: - delta /= self.mouse_dpi - delta = self.delta_update(context, event, delta) - - if origin_mode != 'NOT' and ('draw_line' in self.G_SimpleDeformGizmoHandlerDit): - self.empty_object, _ = self.new_empty(ob, mod) - self.G_SimpleDeformGizmoHandlerDit['empty_object'] = self.empty_object - data = up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode - self.set_prop_value(data) - self.update_gizmo_matrix(context) - self.update_empty_matrix() - self.update_bound_box(context.object) - - self.update_header_text(context, mod, origin, up_limits, down_limits) - self.add_handler() - - return self.event_ops(event, ob, origin) - - -class SimpleDeformGizmoGroup(GizmoGroup, Utils, Handler, PublicClass, Data): - """显示Gizmo - """ - bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup' - bl_label = 'SimpleDeformGizmoGroup' - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = {'3D', 'PERSISTENT', } - - @classmethod - def poll(cls, context): - pol = cls.simple_deform_poll(context) - pref = cls.pref_() - deform_method = ( - pol and (context.object.modifiers.active.deform_method != 'BEND')) - display_gizmo = pref.display_bend_axis_switch_gizmo - switch = (not display_gizmo) - return pol and (deform_method or switch) - - def generate_gizmo_mode(self, gizmo_data): - """生成gizmo的上限下限及角度设置 - - Args: - gizmo_data (_type_): _description_ - """ - for i, j, k in gizmo_data: - setattr(self, i, self.gizmos.new(j)) - gizmo = getattr(self, i) - for f in k: - if f == 'target_set_operator': - gizmo.target_set_operator(k[f]) - elif f == 'target_set_prop': - gizmo.target_set_prop(*k[f]) - else: - setattr(gizmo, f, k[f]) - - def setup(self, context): - sd_name = ViewSimpleDeformGizmo.bl_idname - - add_data = (('up_limits', - sd_name, - {'ctrl_mode': 'up_limits', - 'draw_type': 'Sphere_GizmoGroup_', - 'mouse_dpi': 1000, - 'color': (1.0, 0, 0), - 'alpha': 0.5, - 'color_highlight': (1.0, 1.0, 1.0), - 'alpha_highlight': 0.3, - 'use_draw_modal': True, - 'scale_basis': 0.1, - 'use_draw_value': True, }), - ('down_limits', - sd_name, - {'ctrl_mode': 'down_limits', - 'draw_type': 'Sphere_GizmoGroup_', - 'mouse_dpi': 1000, - 'color': (0, 1.0, 0), - 'alpha': 0.5, - 'color_highlight': (1.0, 1.0, 1.0), - 'alpha_highlight': 0.3, - 'use_draw_modal': True, - 'scale_basis': 0.1, - 'use_draw_value': True, }), - ('angle', - sd_name, - {'ctrl_mode': 'angle', - 'draw_type': 'SimpleDeform_GizmoGroup_', - 'color': (1.0, 0.5, 1.0), - 'alpha': 0.3, - 'color_highlight': (1.0, 1.0, 1.0), - 'alpha_highlight': 0.3, - 'use_draw_modal': True, - 'scale_basis': 0.1, - 'use_draw_value': True, - 'mouse_dpi': 100, - }), - ) - - self.generate_gizmo_mode(add_data) - - data_path = 'object.modifiers.active.deform_axis' - set_enum = 'wm.context_set_enum' - - for axis in ('X', 'Y', 'Z'): - # show toggle axis button - gizmo = self.gizmos.new('GIZMO_GT_button_2d') - gizmo.icon = f'EVENT_{axis.upper()}' - gizmo.draw_options = {'BACKDROP', 'HELPLINE'} - ops = gizmo.target_set_operator(set_enum) - ops.data_path = data_path - ops.value = axis - gizmo.color = (0, 0, 0) - gizmo.alpha = 0.3 - gizmo.color_highlight = 1.0, 1.0, 1.0 - gizmo.alpha_highlight = 0.3 - gizmo.use_draw_modal = True - gizmo.use_draw_value = True - gizmo.scale_basis = 0.1 - setattr(self, f'deform_axis_{axis.lower()}', gizmo) - self.add_handler() - - def refresh(self, context): - pro = context.object.SimpleDeformGizmo_PropertyGroup - - self.angle.target_set_prop('angle', - context.object.modifiers.active, - 'angle') - self.down_limits.target_set_prop('down_limits', - pro, - 'down_limits') - self.down_limits.target_set_prop('up_limits', - pro, - 'up_limits') - self.up_limits.target_set_prop('down_limits', - pro, - 'down_limits') - self.up_limits.target_set_prop('up_limits', - pro, - 'up_limits') - self.add_handler() - - def draw_prepare(self, context): - ob = context.object - mat = ob.matrix_world - - if 'co' in self.G_SimpleDeformGizmoHandlerDit: - def _mat(f): - co = self.G_SimpleDeformGizmoHandlerDit['co'][0] - co = (co[0] + (max(ob.dimensions) * f), co[1], - co[2] - (min(ob.dimensions) * 0.3)) - return mat @ Vector(co) - - self.deform_axis_x.matrix_basis.translation = _mat(0) - self.deform_axis_y.matrix_basis.translation = _mat(0.3) - self.deform_axis_z.matrix_basis.translation = _mat(0.6) - self.add_handler() - - def invoke_prepare(self, context, gizmo): - self.add_handler() - - -class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, Utils, Handler, PublicClass): - """绘制切换变型轴的 - 变换方向 - """ - bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo' - bl_label = 'SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo' - - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = { - '3D', - 'PERSISTENT', - } - - @classmethod - def poll(cls, context): - pref = cls.pref_() - simple = cls.simple_deform_poll(context) - bend = simple and ( - context.object.modifiers.active.deform_method == 'BEND') - switch_axis = (pref.display_bend_axis_switch_gizmo == True) - return switch_axis and bend - - def setup(self, context): - _draw_type = 'SimpleDeform_Bend_Direction_' - _color_a = 1, 0, 0 - _color_b = 0, 1, 0 - self.add_handler() - - for na, axis, rot, positive in ( - ('top_a', 'X', (math.radians(90), 0, math.radians(90)), True), - ('top_b', 'X', (math.radians(90), 0, 0), True), - - ('bottom_a', 'X', (math.radians(90), 0, math.radians(90)), False), - ('bottom_b', 'X', (math.radians(90), 0, 0), False), - - ('left_a', 'Y', (math.radians(90), 0, 0), False), - ('left_b', 'Y', (0, 0, 0), False), - - ('right_a', 'Y', (math.radians(90), 0, 0), True), - ('right_b', 'Y', (0, 0, 0), True), - - ('front_a', 'Z', (0, 0, 0), False), - ('front_b', 'X', (0, 0, 0), False), - - ('back_a', 'Z', (0, 0, 0), True), - ('back_b', 'X', (0, 0, 0), True),): - _a = (na.split('_')[1] == 'a') - setattr(self, na, self.gizmos.new(CustomGizmo.bl_idname)) - gizmo = getattr(self, na) - gizmo.mode = na - gizmo.draw_type = _draw_type - gizmo.color = _color_a if _a else _color_b - gizmo.alpha = 0.3 - gizmo.color_highlight = 1.0, 1.0, 1.0 - gizmo.alpha_highlight = 1 - gizmo.use_draw_modal = True - gizmo.scale_basis = 0.2 - gizmo.use_draw_value = True - ops = gizmo.target_set_operator( - 'simple_deform_gizmo.deform_axis') - ops.Deform_Axis = axis - ops.X_Value = rot[0] - ops.Y_Value = rot[1] - ops.Z_Value = rot[2] - ops.Is_Positive = positive - - def draw_prepare(self, context): - ob = context.object - mat = ob.matrix_world - top, bottom, left, right, front, back = self.each_face_pos(mat) - - rad = math.radians - for_list = ( - ('top_a', top, (0, 0, 0),), - ('top_b', top, (0, 0, rad(90)),), - - ('bottom_a', bottom, (0, rad(180), 0),), - ('bottom_b', bottom, (0, rad(180), rad(90)),), - - ('left_a', left, (rad(-90), 0, rad(90)),), - ('left_b', left, (0, rad(-90), 0),), - - ('right_a', right, (rad(90), 0, rad(90)),), - ('right_b', right, (0, rad(90), 0),), - - ('front_a', front, (rad(90), 0, 0),), - ('front_b', front, (rad(90), rad(90), 0),), - - ('back_a', back, (rad(-90), 0, 0),), - ('back_b', back, (rad(-90), rad(-90), 0),), - ) - for i, j, w, in for_list: - gizmo = getattr(self, i, False) - rot = Euler(w, 'XYZ').to_matrix().to_4x4() - - gizmo.matrix_basis = mat.to_euler().to_matrix().to_4x4() @ rot - gizmo.matrix_basis.translation = Vector(j) - - -class_list = ( - CustomGizmo, - ViewSimpleDeformGizmo, - SimpleDeformGizmoGroup, - SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo, -) - -register_class, unregister_class = bpy.utils.register_classes_factory(class_list) - - -def register(): - register_class() - - -def unregister(): - Handler.del_handler() - unregister_class() diff --git a/simple_deform_helper/gizmo/__init__.py b/simple_deform_helper/gizmo/__init__.py new file mode 100644 index 000000000..f73e4cb20 --- /dev/null +++ b/simple_deform_helper/gizmo/__init__.py @@ -0,0 +1,96 @@ +import bpy +from bpy_types import Gizmo + +from draw import Handler +from utils import PublicUtils + + +class GizmoProperty: + + @property + def obj(self): + return bpy.context.object + + @property + def modifier(self): + obj = self.obj + if not obj: + return + return obj.modifiers.active + + @property + def active_modifier_is_simple_deform(self): + return self.modifier and self.modifier.type == 'SIMPLE_DEFORM' + + @property + def is_use_angle_value(self): + if self.active_modifier_is_simple_deform: + return self.modifier.deform_method in ('TWIST', 'BEND') + + +class GizmoPublic(GizmoProperty, PublicUtils, Handler): + def generate_gizmo_mode(self, gizmo_data): + """生成gizmo的上限下限及角度设置 + + Args: + gizmo_data (_type_): _description_ + """ + for i, j, k in gizmo_data: + setattr(self, i, self.gizmos.new(j)) + gizmo = getattr(self, i) + for f in k: + if f == 'target_set_operator': + gizmo.target_set_operator(k[f]) + elif f == 'target_set_prop': + gizmo.target_set_prop(*k[f]) + else: + setattr(gizmo, f, k[f]) + + +class CustomGizmo(Gizmo, PublicUtils, Handler): + """绘制自定义Gizmo""" + bl_idname = '_Custom_Gizmo' + draw_type: str + custom_shape: dict + + def setup(self): + self.draw_type = 'None_GizmoGroup_' + if not hasattr(self, 'custom_shape'): + self.custom_shape = {} + for i in self.G_GizmoCustomShapeDict: + self.custom_shape[i] = self.new_custom_shape( + 'TRIS', self.G_GizmoCustomShapeDict[i]) + self.add_handler() + + def draw(self, context): + self.draw_custom_shape(self.custom_shape[self.draw_type]) + + def draw_select(self, context, select_id): + self.draw_custom_shape( + self.custom_shape[self.draw_type], select_id=select_id) + + def invoke(self, context, event): + return {'RUNNING_MODAL'} + + def modal(self, context, event, tweak): + self.add_handler() + self.update_bound_box(context.object) + self.update_empty_matrix() + return {'RUNNING_MODAL'} + + +class_list = ( + ViewSimpleDeformGizmo, + SimpleDeformGizmoGroup, +) + +register_class, unregister_class = bpy.utils.register_classes_factory(class_list) + + +def register(): + register_class() + + +def unregister(): + Handler.del_handler() + unregister_class() diff --git a/simple_deform_helper/gizmo/bend_axis.py b/simple_deform_helper/gizmo/bend_axis.py new file mode 100644 index 000000000..6fa7cf7af --- /dev/null +++ b/simple_deform_helper/gizmo/bend_axis.py @@ -0,0 +1,107 @@ +import math + +from bpy.types import GizmoGroup +from mathutils import Euler, Vector + +from ..draw import Handler +from ..utils import PublicUtils + + +class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, PublicUtils, Handler): + """绘制切换变型轴的 + 变换方向 + """ + bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo' + bl_label = 'SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo' + + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = { + '3D', + 'PERSISTENT', + } + + @classmethod + def poll(cls, context): + pref = cls.pref_() + simple = cls.simple_deform_public_poll(context) + bend = simple and ( + context.object.modifiers.active.deform_method == 'BEND') + switch_axis = pref.display_bend_axis_switch_gizmo + return switch_axis and bend + + def setup(self, context): + _draw_type = 'SimpleDeform_Bend_Direction_' + _color_a = 1, 0, 0 + _color_b = 0, 1, 0 + self.add_handler() + + for na, axis, rot, positive in ( + ('top_a', 'X', (math.radians(90), 0, math.radians(90)), True), + ('top_b', 'X', (math.radians(90), 0, 0), True), + + ('bottom_a', 'X', (math.radians(90), 0, math.radians(90)), False), + ('bottom_b', 'X', (math.radians(90), 0, 0), False), + + ('left_a', 'Y', (math.radians(90), 0, 0), False), + ('left_b', 'Y', (0, 0, 0), False), + + ('right_a', 'Y', (math.radians(90), 0, 0), True), + ('right_b', 'Y', (0, 0, 0), True), + + ('front_a', 'Z', (0, 0, 0), False), + ('front_b', 'X', (0, 0, 0), False), + + ('back_a', 'Z', (0, 0, 0), True), + ('back_b', 'X', (0, 0, 0), True),): + _a = (na.split('_')[1] == 'a') + setattr(self, na, self.gizmos.new(CustomGizmo.bl_idname)) + gizmo = getattr(self, na) + gizmo.mode = na + gizmo.draw_type = _draw_type + gizmo.color = _color_a if _a else _color_b + gizmo.alpha = 0.3 + gizmo.color_highlight = 1.0, 1.0, 1.0 + gizmo.alpha_highlight = 1 + gizmo.use_draw_modal = True + gizmo.scale_basis = 0.2 + gizmo.use_draw_value = True + ops = gizmo.target_set_operator( + 'simple_deform_gizmo.deform_axis') + ops.Deform_Axis = axis + ops.X_Value = rot[0] + ops.Y_Value = rot[1] + ops.Z_Value = rot[2] + ops.Is_Positive = positive + + def draw_prepare(self, context): + ob = context.object + mat = ob.matrix_world + top, bottom, left, right, front, back = self.each_face_pos(mat) + + rad = math.radians + for_list = ( + ('top_a', top, (0, 0, 0),), + ('top_b', top, (0, 0, rad(90)),), + + ('bottom_a', bottom, (0, rad(180), 0),), + ('bottom_b', bottom, (0, rad(180), rad(90)),), + + ('left_a', left, (rad(-90), 0, rad(90)),), + ('left_b', left, (0, rad(-90), 0),), + + ('right_a', right, (rad(90), 0, rad(90)),), + ('right_b', right, (0, rad(90), 0),), + + ('front_a', front, (rad(90), 0, 0),), + ('front_b', front, (rad(90), rad(90), 0),), + + ('back_a', back, (rad(-90), 0, 0),), + ('back_b', back, (rad(-90), rad(-90), 0),), + ) + for i, j, w, in for_list: + gizmo = getattr(self, i, False) + rot = Euler(w, 'XYZ').to_matrix().to_4x4() + + gizmo.matrix_basis = mat.to_euler().to_matrix().to_4x4() @ rot + gizmo.matrix_basis.translation = Vector(j) diff --git a/simple_deform_helper/gizmo/ctrl_tow_point.py b/simple_deform_helper/gizmo/ctrl_tow_point.py new file mode 100644 index 000000000..3a8e5583a --- /dev/null +++ b/simple_deform_helper/gizmo/ctrl_tow_point.py @@ -0,0 +1,341 @@ +import bpy + + +class GizmoProperty(Gizmo, PublicUtils, Handler): + @property + def is_angle_mode(self): + return self.ctrl_mode == 'angle' + + @property + def is_up_limits_mode(self): + return self.ctrl_mode == 'up_limits' + + @property + def is_down_limits_mode(self): + return self.ctrl_mode == 'down_limits' + + +class ViewSimpleDeformGizmo(GizmoProperty): + """显示轴向切换拖动点Gizmo(两个点) + """ + bl_idname = 'ViewSimpleDeformGizmo' + + bl_target_properties = ( + {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, + {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, + {'id': 'angle', 'type': 'FLOAT', 'array_length': 1}, + ) + + __slots__ = ( + 'mod', + 'up', + 'down', + 'up_', + 'down_', + 'draw_type', + 'mouse_dpi', + 'ctrl_mode', + 'empty_object', + 'init_mouse_y', + 'init_mouse_x', + 'custom_shape', + 'int_value_angle', + 'value_deform_axis', + 'int_value_up_limits', + 'int_value_down_limits', + 'rotate_follow_modifier', + ) + + def update_gizmo_rotate(self, axis, mod): + if self.rotate_follow_modifier: + rot = Euler() + if axis == 'X' and (not self.is_positive(mod.angle)): + rot.z = math.pi + + elif axis == 'Y': + if self.is_positive(mod.angle): + rot.z = -(math.pi / 2) + else: + rot.z = math.pi / 2 + elif axis == 'Z': + if self.is_positive(mod.angle): + rot.x = rot.z = rot.y = math.pi / 2 + else: + rot.z = rot.y = math.pi / 2 + rot.x = -(math.pi / 2) + + rot = rot.to_matrix() + self.matrix_basis = self.matrix_basis @ rot.to_4x4() + + def update_draw_limits_bound_box(self, data, mod, axis, mat, up_, down_): + top, bottom, left, right, front, back = data + if mod.origin: + vector_axis = self.get_vector_axis(mod) + origin_mat = mod.origin.matrix_world.to_3x3() + axis_ = origin_mat @ vector_axis + point_lit = [[top, bottom], [left, right], [front, back]] + for f in range(point_lit.__len__()): + i = point_lit[f][0] + j = point_lit[f][1] + angle = self.point_to_angle(i, j, f, axis_) + if abs(angle - 180) < 0.00001: + point_lit[f][1], point_lit[f][0] = up_, down_ + elif abs(angle) < 0.00001: + point_lit[f][0], point_lit[f][1] = up_, down_ + [[top, bottom], [left, right], [front, back]] = point_lit + else: + top, bottom, left, right, front, back = self.get_up_down_return_list( + mod, axis, up_, down_, data) + data = top, bottom, left, right, front, back + (top, bottom, left, right, front, + back) = self.matrix_calculation(mat.inverted(), data) + self.G_SimpleDeformGizmoHandlerDit['draw_limits_bound_box'] = ( + mat, ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],))) + + def update_matrix_basis_translation(self, co, mat, up_, down_): + if self.is_angle_mode: + self.matrix_basis.translation = mat @ Vector((co[1])) + elif self.is_up_limits_mode: + self.matrix_basis.translation = up_ + elif self.is_down_limits_mode: + self.matrix_basis.translation = down_ + + def update_gizmo_matrix(self, context): + ob = context.object + mat = ob.matrix_world + mod = context.object.modifiers.active + axis = mod.deform_axis + if mod.origin: + self.matrix_basis = mod.origin.matrix_world.normalized() + else: + self.matrix_basis = ob.matrix_world.normalized() + + co = self.generate_co_data() + self.update_gizmo_rotate(axis, mod) + # calculation limits position + top, bottom, left, right, front, back = self.each_face_pos(mat) + (up, down), (up_, down_) = self.get_limits_pos( + mod, (top, bottom, left, right, front, back)) + self.update_matrix_basis_translation(co, mat, up_, down_) + + self.up = up + self.down = down + self.up_ = up_ + self.down_ = down_ + self.G_SimpleDeformGizmoHandlerDit['draw_line'] = ( + (up, down), (up_, down_)) + data = top, bottom, left, right, front, back + self.update_draw_limits_bound_box(data, mod, axis, mat, up_, down_) + + def setup(self): + self.generate_co_data() + self.draw_type = 'None_GizmoGroup_' + self.ctrl_mode = 'angle' # up_limits , down_limits + self.mouse_dpi = 10 + self.rotate_follow_modifier = True + if not hasattr(self, 'custom_shape'): + self.custom_shape = {} + for i in self.G_GizmoCustomShapeDict: + item = self.G_GizmoCustomShapeDict[i] + self.custom_shape[i] = self.new_custom_shape('TRIS', item) + self.add_handler() + + def draw(self, context): + self.add_handler() + + self.update_gizmo_matrix(context) + self.draw_custom_shape(self.custom_shape[self.draw_type]) + + def draw_select(self, context, select_id): + self.update_gizmo_matrix(context) + self.draw_custom_shape( + self.custom_shape[self.draw_type], select_id=select_id) + + def invoke(self, context, event): + self.init_mouse_y = event.mouse_y + self.init_mouse_x = event.mouse_x + mod = context.object.modifiers.active + limits = mod.limits + up_limits = limits[1] + down_limits = limits[0] + + if 'angle' == self.ctrl_mode: + self.int_value_angle = self.target_get_value('angle') + elif 'up_limits' == self.ctrl_mode: + self.int_value_up_limits = up_limits + self.target_set_value('up_limits', self.int_value_up_limits) + elif 'down_limits' == self.ctrl_mode: + self.int_value_down_limits = down_limits + self.target_set_value('down_limits', self.int_value_down_limits) + return {'RUNNING_MODAL'} + + def exit(self, context, cancel): + context.area.header_text_set(None) + + if cancel: + if 'angle' == self.ctrl_mode: + self.target_set_value('angle', self.int_value_angle) + elif 'deform_axis' == self.ctrl_mode: + self.target_set_value('deform_axis', self.value_deform_axis) + elif 'up_limits' == self.ctrl_mode: + self.target_set_value('up_limits', self.int_value_up_limits) + + elif 'down_limits' == self.ctrl_mode: + self.target_set_value( + 'down_limits', self.int_value_down_limits) + + def delta_update(self, context, event, delta): + if ('draw_line' in self.G_SimpleDeformGizmoHandlerDit) and (self.ctrl_mode in ('up_limits', 'down_limits')): + x, y = view3d_utils.location_3d_to_region_2d( + context.region, context.space_data.region_3d, self.up) + x2, y2 = view3d_utils.location_3d_to_region_2d( + context.region, context.space_data.region_3d, self.down) + + mouse_line_distance = math.sqrt(((event.mouse_region_x - x2) ** 2) + + ((event.mouse_region_y - y2) ** 2)) + straight_line_distance = math.sqrt(((x2 - x) ** 2) + + ((y2 - y) ** 2)) + delta = mouse_line_distance / straight_line_distance + 0 + + v_up = Vector((x, y)) + v_down = Vector((x2, y2)) + limits_angle = v_up - v_down + + mouse_v = Vector((event.mouse_region_x, event.mouse_region_y)) + + mouse_angle = mouse_v - v_down + angle_ = mouse_angle.angle(limits_angle) + if angle_ > (math.pi / 2): + delta = 0 + return delta + + def set_down_value(self, data, mu): + up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data + value = self.value_limit(delta, max_value=mu - limit_scope if middle else max_value) + self.target_set_value('down_limits', value) + if event.ctrl: + self.target_set_value( + 'up_limits', value + difference_value) + elif middle: + if origin_mode == 'LIMITS_MIDDLE': + self.target_set_value('up_limits', mu - (value - mu)) + elif origin_mode == 'MIDDLE': + self.target_set_value('up_limits', 1 - value) + else: + self.target_set_value('up_limits', up_limits) + else: + self.target_set_value('up_limits', up_limits) + + def set_up_value(self, data, mu): + up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data + value = self.value_limit(delta, min_value=mu + limit_scope if middle else min_value) + self.target_set_value('up_limits', value) + if event.ctrl: + self.target_set_value( + 'down_limits', value - difference_value) + elif middle: + if origin_mode == 'LIMITS_MIDDLE': + self.target_set_value('down_limits', mu - (value - mu)) + elif origin_mode == 'MIDDLE': + self.target_set_value('down_limits', 1 - value) + else: + self.target_set_value('down_limits', down_limits) + else: + self.target_set_value('down_limits', down_limits) + + def set_prop_value(self, data): + up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data + mu = (up_limits + down_limits) / 2 + if self.is_angle_mode: + value = self.int_value_angle - delta + v = self.snap_value(value, event) + print(v) + self.target_set_value('angle', v) + elif self.is_up_limits_mode: + self.set_up_value(data, mu) + elif self.is_down_limits_mode: + self.set_down_value(data, mu) + + @staticmethod + def snap_value(value, event: 'bpy.types.Event'): + if event.ctrl: + value //= 5 + elif event.ctrl and event.shift: + value //= 1 + return value + + def update_header_text(self, context, mod, origin, up_limits, down_limits): + translate: Callable[[Any], str] = lambda t: bpy.app.translations.pgettext(t) + mode = origin.bl_rna.properties['origin_mode'].enum_items[origin.origin_mode].name + + def t_(a, b): + return translate(a) + ':{}'.format(round(b, 3)) + + text = translate(mode) + ' ' + + if self.is_use_angle_value and self.is_angle_mode: + text += t_('Angle', math.degrees(mod.angle)) + elif self.is_up_limits_mode: + text += t_('Upper limit', up_limits) + elif self.is_down_limits_mode: + text += t_('Down limit', down_limits) + else: + text += t_('Coefficient', mod.factor) + context.area.header_text_set(text) + + def event_ops(self, event, ob, origin): + """通过输入键位来更改属性""" + # event ctrl + data_path = ('object.SimpleDeformGizmo_PropertyGroup.origin_mode', + 'object.modifiers.active.origin.SimpleDeformGizmo_PropertyGroup.origin_mode') + + if event.type in ('WHEELUPMOUSE', 'WHEELDOWNMOUSE'): + reverse = (event.type == 'WHEELUPMOUSE') + for path in data_path: + bpy.ops.wm.context_cycle_enum( + data_path=path, reverse=reverse, wrap=True) + elif event.type in ('X', 'Y', 'Z'): + ob.modifiers.active.deform_axis = event.type + elif event.type == 'A': + self.pref.display_bend_axis_switch_gizmo = True + return {'FINISHED'} + self.add_handler() + + return {'RUNNING_MODAL'} + + def modal(self, context, event, tweak): + self.update_bound_box(context.object) + + delta = (self.init_mouse_x - event.mouse_x) / self.mouse_dpi + ob = context.object + mod = ob.modifiers.active + limits = mod.limits + up_limits = limits[1] + down_limits = limits[0] + origin = self.get_origin_property_group(mod, ob) + origin_mode = origin.origin_mode + middle = origin_mode in ('LIMITS_MIDDLE', 'MIDDLE') + limit_scope = self.pref.modifiers_limits_tolerance + max_value = up_limits - limit_scope + min_value = down_limits + limit_scope + difference_value = up_limits - down_limits + + if 'SNAP' in tweak: + delta = round(delta) + if 'PRECISE' in tweak: + delta /= self.mouse_dpi + delta = self.delta_update(context, event, delta) + + if origin_mode != 'NOT' and ('draw_line' in self.G_SimpleDeformGizmoHandlerDit): + self.empty_object, _ = self.new_empty(ob, mod) + self.G_SimpleDeformGizmoHandlerDit['empty_object'] = self.empty_object + data = up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode + self.set_prop_value(data) + self.update_gizmo_matrix(context) + self.update_empty_matrix() + self.update_bound_box(context.object) + + self.update_header_text(context, mod, origin, up_limits, down_limits) + self.add_handler() + + return self.event_ops(event, ob, origin) diff --git a/simple_deform_helper/gizmo/ctrl_value_and_factor.py b/simple_deform_helper/gizmo/ctrl_value_and_factor.py new file mode 100644 index 000000000..2dd4ad36b --- /dev/null +++ b/simple_deform_helper/gizmo/ctrl_value_and_factor.py @@ -0,0 +1,132 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +from bpy.types import ( + GizmoGroup, +) +from mathutils import Vector + +from utils import PublicUtils + + +class SimpleDeformGizmoGroup(GizmoGroup, PublicUtils): + """显示Gizmo + """ + bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup' + bl_label = 'SimpleDeformGizmoGroup' + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D', 'PERSISTENT', } + + @classmethod + def poll(cls, context): + pol = cls.simple_deform_public_poll(context) + pref = cls.pref_() + deform_method = ( + pol and (context.object.modifiers.active.deform_method != 'BEND')) + display_gizmo = pref.display_bend_axis_switch_gizmo + switch = (not display_gizmo) + return pol and (deform_method or switch) + + def setup(self, context): + sd_name = ViewSimpleDeformGizmo.bl_idname + + add_data = (('up_limits', + sd_name, + {'ctrl_mode': 'up_limits', + 'draw_type': 'Sphere_GizmoGroup_', + 'mouse_dpi': 1000, + 'color': (1.0, 0, 0), + 'alpha': 0.5, + 'color_highlight': (1.0, 1.0, 1.0), + 'alpha_highlight': 0.3, + 'use_draw_modal': True, + 'scale_basis': 0.1, + 'use_draw_value': True, }), + ('down_limits', + sd_name, + {'ctrl_mode': 'down_limits', + 'draw_type': 'Sphere_GizmoGroup_', + 'mouse_dpi': 1000, + 'color': (0, 1.0, 0), + 'alpha': 0.5, + 'color_highlight': (1.0, 1.0, 1.0), + 'alpha_highlight': 0.3, + 'use_draw_modal': True, + 'scale_basis': 0.1, + 'use_draw_value': True, }), + ('angle', + sd_name, + {'ctrl_mode': 'angle', + 'draw_type': 'SimpleDeform_GizmoGroup_', + 'color': (1.0, 0.5, 1.0), + 'alpha': 0.3, + 'color_highlight': (1.0, 1.0, 1.0), + 'alpha_highlight': 0.3, + 'use_draw_modal': True, + 'scale_basis': 0.1, + 'use_draw_value': True, + 'mouse_dpi': 100, + }), + ) + + self.generate_gizmo_mode(add_data) + + data_path = 'object.modifiers.active.deform_axis' + set_enum = 'wm.context_set_enum' + + for axis in ('X', 'Y', 'Z'): + # show toggle axis button + gizmo = self.gizmos.new('GIZMO_GT_button_2d') + gizmo.icon = f'EVENT_{axis.upper()}' + gizmo.draw_options = {'BACKDROP', 'HELPLINE'} + ops = gizmo.target_set_operator(set_enum) + ops.data_path = data_path + ops.value = axis + gizmo.color = (0, 0, 0) + gizmo.alpha = 0.3 + gizmo.color_highlight = 1.0, 1.0, 1.0 + gizmo.alpha_highlight = 0.3 + gizmo.use_draw_modal = True + gizmo.use_draw_value = True + gizmo.scale_basis = 0.1 + setattr(self, f'deform_axis_{axis.lower()}', gizmo) + self.add_handler() + + def refresh(self, context): + pro = context.object.SimpleDeformGizmo_PropertyGroup + + self.angle.target_set_prop('angle', + context.object.modifiers.active, + 'angle') + self.down_limits.target_set_prop('down_limits', + pro, + 'down_limits') + self.down_limits.target_set_prop('up_limits', + pro, + 'up_limits') + self.up_limits.target_set_prop('down_limits', + pro, + 'down_limits') + self.up_limits.target_set_prop('up_limits', + pro, + 'up_limits') + self.add_handler() + + def draw_prepare(self, context): + ob = context.object + mat = ob.matrix_world + + if 'co' in self.G_SimpleDeformGizmoHandlerDit: + def _mat(f): + co = self.G_SimpleDeformGizmoHandlerDit['co'][0] + co = (co[0] + (max(ob.dimensions) * f), co[1], + co[2] - (min(ob.dimensions) * 0.3)) + return mat @ Vector(co) + + self.deform_axis_x.matrix_basis.translation = _mat(0) + self.deform_axis_y.matrix_basis.translation = _mat(0.3) + self.deform_axis_z.matrix_basis.translation = _mat(0.6) + self.add_handler() + + def invoke_prepare(self, context, gizmo): + self.add_handler() diff --git a/simple_deform_helper/operators.py b/simple_deform_helper/operators.py index f933085ac..70f05c8de 100644 --- a/simple_deform_helper/operators.py +++ b/simple_deform_helper/operators.py @@ -4,10 +4,10 @@ import bpy from bpy.types import Operator from bpy.props import FloatProperty, StringProperty, BoolProperty -from .utils import PublicClass +from .utils import PublicUtils -class DeformAxisOperator(Operator, PublicClass): +class DeformAxisOperator(Operator, PublicUtils): bl_idname = 'simple_deform_gizmo.deform_axis' bl_label = 'deform_axis' bl_description = 'deform_axis operator' @@ -26,12 +26,12 @@ class DeformAxisOperator(Operator, PublicClass): return {'RUNNING_MODAL'} def modal(self, context, event): - from .gizmo import Utils + from gizmo.ctrl_value_and_factor import PublicUtils mod = context.object.modifiers.active mod.deform_axis = self.Deform_Axis - empty, con_limit_name = Utils.new_empty(context.object, mod) - is_positive = Utils.is_positive(mod.angle) + empty, con_limit_name = PublicUtils.new_empty(context.object, mod) + is_positive = PublicUtils.is_positive(mod.angle) for limit, value in (('max_x', self.X_Value), ('min_x', self.X_Value), @@ -48,7 +48,7 @@ class DeformAxisOperator(Operator, PublicClass): if not event.ctrl: self.pref.display_bend_axis_switch_gizmo = False - Utils.update_bound_box(context.object) + PublicUtils.update_bound_box(context.object) return {'FINISHED'} diff --git a/simple_deform_helper/panel.py b/simple_deform_helper/panel.py index 512017b0e..76d859ff9 100644 --- a/simple_deform_helper/panel.py +++ b/simple_deform_helper/panel.py @@ -2,10 +2,10 @@ import bpy from bpy.types import Panel, VIEW3D_HT_tool_header -from .utils import PublicClass, Utils +from .utils import PublicUtils -class SimpleDeformHelperToolPanel(Panel, PublicClass): +class SimpleDeformHelperToolPanel(Panel, PublicUtils): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Tool' @@ -16,7 +16,7 @@ class SimpleDeformHelperToolPanel(Panel, PublicClass): @classmethod def poll(cls, context): - return Utils.simple_deform_poll(context) + return PublicUtils.simple_deform_public_poll(context) def draw(self, context): cls = SimpleDeformHelperToolPanel diff --git a/simple_deform_helper/preferences.py b/simple_deform_helper/preferences.py index 90835854e..526d02f92 100644 --- a/simple_deform_helper/preferences.py +++ b/simple_deform_helper/preferences.py @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -import os import bpy from bpy.props import (FloatProperty, PointerProperty, @@ -12,12 +11,11 @@ from bpy.types import ( PropertyGroup, ) -from .data import G_ADDON_NAME -from .utils import PublicClass, Utils +from .utils import PublicUtils, GizmoUtils -class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass): - bl_idname = G_ADDON_NAME +class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicUtils): + bl_idname = PublicUtils.G_ADDON_NAME deform_wireframe_color: FloatVectorProperty( name='Deform Wireframe', @@ -63,7 +61,7 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass): col.prop(self, 'display_bend_axis_switch_gizmo') def draw_header_tool_settings(self, context): - if Utils.simple_deform_poll(context): + if GizmoUtils.simple_deform_public_poll(context): row = self.layout.row() obj = context.object mod = obj.modifiers.active @@ -81,7 +79,6 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass): class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup): - def _limits_up(self, context): mod = context.object.modifiers if mod and (mod.active.type == 'SIMPLE_DEFORM'): @@ -144,7 +141,7 @@ register_class, unregister_class = bpy.utils.register_classes_factory(class_list def register(): register_class() - PublicClass.pref_().display_bend_axis_switch_gizmo = False + PublicUtils.pref_().display_bend_axis_switch_gizmo = False bpy.types.Object.SimpleDeformGizmo_PropertyGroup = PointerProperty( type=SimpleDeformGizmoObjectPropertyGroup, name='SimpleDeformGizmo_PropertyGroup') diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index ae9df8ca8..74fb8b7c2 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -2,19 +2,88 @@ import math import uuid +from os.path import dirname, basename, realpath +from typing import Callable, Any import bpy import numpy as np from bpy.types import AddonPreferences from mathutils import Vector, Matrix -from .data import G_ADDON_NAME, G_NAME, G_INDICES, G_MODIFIERS_PROPERTY, G_CON_LIMIT_NAME, Data + +class PublicData: + """Public data class, all fixed data will be placed here + """ + G_GizmoCustomShapeDict = {} + G_SimpleDeformGizmoHandlerDit = {} + G_INDICES = ( + (0, 1), (0, 2), (1, 3), (2, 3), + (4, 5), (4, 6), (5, 7), (6, 7), + (0, 4), (1, 5), (2, 6), (3, 7)) + + G_NAME = 'ViewSimpleDeformGizmo_' # Temporary use files prefix + G_ADDON_NAME = basename(dirname(realpath(__file__))) # "simple_deform_helper" + G_CON_LIMIT_NAME = G_NAME + 'constraints_limit_rotation' # constraints name + + G_MODIFIERS_PROPERTY = [ # copy modifier data + 'angle', + 'deform_axis', + 'deform_method', + 'factor', + 'invert_vertex_group', + 'limits', + 'lock_x', + 'lock_y', + 'lock_z', + 'origin', + 'show_expanded', + 'show_in_editmode', + 'vertex_group', + ] + + @classmethod + def load_gizmo_data(cls) -> None: + import json + import os + json_path = os.path.join(os.path.dirname(__file__), "gizmo.json") + with open(json_path, "r") as file: + cls.G_GizmoCustomShapeDict = json.load(file) + + @staticmethod + def from_mesh_get_triangle_face_co(mesh: 'bpy.types.Mesh') -> list: + """ + :param mesh: input mesh read vertex + :type mesh: bpy.data.meshes + :return list: vertex coordinate list[[cox,coy,coz],[cox,coy,coz]...] + """ + import bmesh + bm = bmesh.new() + bm.from_mesh(mesh) + bm.faces.ensure_lookup_table() + bm.verts.ensure_lookup_table() + bmesh.ops.triangulate(bm, faces=bm.faces) + co_list = [list(float(format(j, ".3f")) for j in vert.co) for face in bm.faces for vert in face.verts] + bm.free() + return co_list + + @classmethod + def from_selected_obj_generate_json(cls): + """Export selected object vertex data as gizmo custom paint data + The output file should be in the blender folder + """ + import json + data = {} + for obj in bpy.context.selected_object: + data[obj.name] = cls.from_mesh_get_triangle_face_co(obj.data) + print(data) + with open('gizmo.json', 'w+') as f: + f.write(json.dumps(data)) -class PublicClass: +class PublicClass(PublicData): @staticmethod def pref_() -> "AddonPreferences": - return bpy.context.preferences.addons[G_ADDON_NAME].preferences + return bpy.context.preferences.addons[PublicData.G_ADDON_NAME].preferences @property def pref(self=None) -> 'AddonPreferences': @@ -24,37 +93,15 @@ class PublicClass: return PublicClass.pref_() -class Utils(Data): +class PublicUtils(PublicClass): @classmethod - def set_reduce(cls, list_a, list_b, operation_type='-') -> list: + def value_limit(cls, value, max_value=1, min_value=0): """ - :param list_a: 列表a - :type list_a: list or set - :param list_b: 列表b - :type list_b:list or set - :param operation_type :运算方法Enumerator in ['+','-','*','/']. - :type operation_type :str - :return list: 反回运算后的列表 - """ - if operation_type == '-': - return [list_a[i] - list_b[i] for i in range(0, len(list_a))] - elif operation_type == '+': - return [list_a[i] + list_b[i] for i in range(0, len(list_a))] - elif operation_type == '/': - return [list_a[i] / list_b[i] for i in range(0, len(list_a))] - elif operation_type == '*': - return [list_a[i] * list_b[i] for i in range(0, len(list_a))] - - @classmethod - def value_limit(cls, value, max_value=1, min_value=0) -> float: - """ - :param value: 输入值 - :type value: float - :param max_value: 允许的最大值 - :type max_value: float - :param min_value: 允许的最小值 - :type min_value: float - :return float: 反回小于最大值及大于最小值的浮点数 + @param value: limit value + @param max_value: Maximum allowed + @param min_value: Minimum allowed + @return: If the input value is greater than the maximum value or less than the minimum value + it will be limited to the maximum or minimum value """ if value > max_value: return max_value @@ -71,33 +118,29 @@ class Utils(Data): return number == abs(number) @classmethod - def get_depsgraph(cls, obj: 'bpy.context.object'): + def get_depsgraph(cls, obj: 'bpy.types.Object'): """ - :param obj: 要被评估的物体 - :type obj: bpy.types.Object - :return bpy.types.Object: 反回评估后的物体,计算应用修改器和实例化的数据 - 如果未输入物休将会评估活动物体 + @param obj: dep obj + @return: If there is no input obj, reverse the active object evaluated """ context = bpy.context if obj is None: obj = context.object - depsgraph = context.evaluated_depsgraph_get() - return obj.evaluated_get(depsgraph) + dep = context.evaluated_depsgraph_get() + return obj.evaluated_get(dep) @classmethod - def link_active_collection(cls, - obj: 'bpy.context.object') -> \ - 'bpy.context.view_layer.active_layer_collection.collection.objects': + def link_obj_to_active_collection(cls, obj: 'bpy.types.Object'): context = bpy.context - if obj.name not in context.view_layer.active_layer_collection.collection.objects: - context.view_layer.active_layer_collection.collection.objects.link( + objects = context.view_layer.active_layer_collection.collection.objects + if obj.name not in objects: + objects.link( obj) - return context.view_layer.active_layer_collection.collection.objects @classmethod def properties_is_modifier(cls) -> bool: - """ - 反回活动窗口内是否有修改器属性面板被打开,如果打开则反回True else False + """Returns whether there is a modifier property panel open in the active window. + If it is open, it returns to True else False """ for area in bpy.context.screen.areas: if area.type == 'PROPERTIES': @@ -107,54 +150,9 @@ class Utils(Data): return False @classmethod - def simple_deform_poll(cls, context: 'bpy.context') -> bool: - """Public poll""" - obj = context.object - if not obj: - return False - - mod = obj.modifiers.active - if not mod: - return False - - space = context.space_data - show_gizmo = space.show_gizmo if space.type == 'VIEW_3D' else True - - available_obj_type = obj and (obj.type in ('MESH', 'LATTICE')) - available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM') - is_available_obj = available_modifiers_type and available_obj_type - is_obj_mode = context.mode == 'OBJECT' - show_mod = mod.show_viewport - return is_available_obj and is_obj_mode and show_gizmo and show_mod - - @classmethod - def bound_box_to_list(cls, obj: 'bpy.context.object') -> tuple: - """ - :param obj:输入一个物体,反回物体的边界框列表 - :type obj:bpy.types.Object - :return tuple: - """ + def bound_box_to_list(cls, obj: 'bpy.types.Object'): return tuple(i[:] for i in obj.bound_box) - @classmethod - def get_origin_bounds(cls, obj: 'bpy.context.object') -> list: - modifiers_list = {} - for mod in obj.modifiers: - if (mod == obj.modifiers.active) or (modifiers_list != {}): - modifiers_list[mod] = (mod.show_render, mod.show_viewport) - mod.show_viewport = False - mod.show_render = False - matrix_obj = obj.matrix_world.copy() - obj.matrix_world.zero() - obj.scale = (1, 1, 1) - bound = cls.bound_box_to_list(obj) - obj.matrix_world = matrix_obj - for mod in modifiers_list: - show_render, show_viewport = modifiers_list[mod] - mod.show_render = show_render - mod.show_viewport = show_viewport - return list(bound) - @classmethod def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> tuple: if obj.type == 'MESH': @@ -173,41 +171,6 @@ class Utils(Data): def matrix_calculation(cls, mat: 'Matrix', calculation_list: 'list') -> list: return [mat @ Vector(i) for i in calculation_list] - @classmethod - def get_origin_property_group(cls, mod, ob): - if mod.origin: - return mod.origin.SimpleDeformGizmo_PropertyGroup - else: - return ob.SimpleDeformGizmo_PropertyGroup - - @classmethod - def set_empty_obj_matrix(cls, origin_mode, empty_object, up_, down_, up, down): - tow = (2, 2, 2) - if origin_mode == 'UP_LIMITS': - empty_object.matrix_world.translation = Vector(up_) - elif origin_mode == 'DOWN_LIMITS': - empty_object.matrix_world.translation = Vector( - down_) - elif origin_mode == 'LIMITS_MIDDLE': - empty_object.matrix_world.translation = cls.set_reduce( - cls.set_reduce(up_, down_, '+'), tow, '/') - elif origin_mode == 'MIDDLE': - empty_object.matrix_world.translation = cls.set_reduce( - cls.set_reduce(up, down, '+'), tow, '/') - - @classmethod - def get_vector_axis(cls, mod): - axis = mod.deform_axis - if 'BEND' == mod.deform_method: - vector_axis = Vector((0, 0, 1)) if axis in ( - 'Y', 'X') else Vector((1, 0, 0)) - else: - vector = (Vector((1, 0, 0)) if ( - axis == 'X') else Vector((0, 1, 0))) - vector_axis = Vector((0, 0, 1)) if ( - axis == 'Z') else vector - return vector_axis - @classmethod def point_to_angle(cls, i, j, f, axis_): if i == j: @@ -225,6 +188,170 @@ class Utils(Data): angle = (180 * vector_value.angle(axis_) / math.pi) return angle + @classmethod + def co_to_direction(cls, mat, data): + (min_x, min_y, min_z), (max_x, max_y, + max_z) = data + a = mat @ Vector((max_x, max_y, max_z)) + b = mat @ Vector((max_x, min_y, min_z)) + c = mat @ Vector((min_x, max_y, min_z)) + d = mat @ Vector((min_x, min_y, max_z)) + return ((aa + bb) / Vector((2, 2, 2)) for aa, bb in ((a, d) + (c, b) + (c, d) + (a, b) + (d, b) + (c, a))) + + +class GizmoUtils(PublicUtils): + + @classmethod + def each_face_pos(cls, mat: 'Matrix' = None): + if mat is None: + mat = Matrix() + return cls.co_to_direction(mat, cls.G_SimpleDeformGizmoHandlerDit['co']) + + @classmethod + def update_matrix(cls, mod, ob): + if mod.deform_method == 'BEND': + cls.new_empty(ob, mod) + if mod.origin: + empty_object = mod.origin + modifiers_co = cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'] + for index, mod_name in enumerate(modifiers_co): + co_items = list(modifiers_co.items()) + if mod.name == mod_name: + data = co_items[index - 1][1] if ( + index or (index != 1)) else modifiers_co['co'] + (up, down), (up_, down_) = cls.get_limits_pos( + mod, cls.co_to_direction(ob.matrix_world.copy(), data)) + origin_mode = cls.get_origin_property_group( + mod, ob).origin_mode + cls.set_empty_obj_matrix( + origin_mode, empty_object, up_, down_, up, down) + + @classmethod + def update_empty_matrix(cls): + ob = bpy.context.object + for mod in ob.modifiers: + if mod.type == 'SIMPLE_DEFORM': + cls.update_matrix(mod, ob) + + @classmethod + def get_origin_property_group(cls, mod, ob): + if mod.origin: + return mod.origin.SimpleDeformGizmo_PropertyGroup + else: + return ob.SimpleDeformGizmo_PropertyGroup + + @classmethod + def simple_deform_public_poll(cls, context: 'bpy.types.context') -> bool: + """Public poll + In 3D View + Active Object in ('MESH', 'LATTICE') + Active Modifier Type Is 'SIMPLE_DEFORM' and show_viewport + return True + """ + obj = context.object + if not obj: + return False + + mod = obj.modifiers.active + if not mod: + return False + + space = context.space_data + show_gizmo = space.show_gizmo if space.type == 'VIEW_3D' else True + + available_obj_type = obj and (obj.type in ('MESH', 'LATTICE')) + available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM') + is_available_obj = available_modifiers_type and available_obj_type + is_obj_mode = context.mode == 'OBJECT' + show_mod = mod.show_viewport + return is_available_obj and is_obj_mode and show_gizmo and show_mod + + @classmethod + def get_up_down_return_list(cls, mod, axis, up_, down_, data): + top, bottom, left, right, front, back = data + if 'BEND' == mod.deform_method: + if axis in ('X', 'Y'): + top = up_ + bottom = down_ + elif axis == 'Z': + right = up_ + left = down_ + else: + if axis == 'X': + right = up_ + left = down_ + elif axis == 'Y': + back = up_ + front = down_ + elif axis == 'Z': + top = up_ + bottom = down_ + return top, bottom, left, right, front, back + + @classmethod + def new_empty(cls, obj, mod): + origin = mod.origin + if origin is None: + new_name = cls.G_NAME + '_Empty_' + str(uuid.uuid4()) + origin_object = bpy.data.objects.new(new_name, None) + cls.link_obj_to_active_collection(origin_object) + origin_object.hide_set(True) + origin_object.empty_display_size = min(obj.dimensions) + mod.origin = origin_object + else: + origin_object = mod.origin + origin_object.hide_viewport = False + + if origin_object.parent != obj: + origin_object.parent = obj + + # add constraints + con_name = cls.G_CON_LIMIT_NAME + if con_name in origin_object.constraints.keys(): + limit_constraints = origin.constraints.get(con_name) + else: + limit_constraints = origin_object.constraints.new( + 'LIMIT_ROTATION') + limit_constraints.name = con_name + limit_constraints.owner_space = 'WORLD' + limit_constraints.space_object = obj + limit_constraints.use_transform_limit = True + limit_constraints.use_limit_x = True + limit_constraints.use_limit_y = True + limit_constraints.use_limit_z = True + con_copy_name = cls.G_NAME + 'constraints_copy_rotation' + if con_copy_name in origin_object.constraints.keys(): + copy_constraints = origin.constraints.get(con_copy_name) + else: + copy_constraints = origin_object.constraints.new( + 'COPY_ROTATION') + copy_constraints.name = con_copy_name + copy_constraints.target = obj + copy_constraints.mix_mode = 'BEFORE' + copy_constraints.target_space = 'WORLD' + copy_constraints.owner_space = 'WORLD' + origin_object.rotation_euler.zero() + origin_object.scale = 1, 1, 1 + return origin_object, con_name + + @classmethod + def set_empty_obj_matrix(cls, origin_mode, empty_object, up_, down_, up, down): + tow = Vector((2, 2, 2)) + if origin_mode == 'UP_LIMITS': + empty_object.matrix_world.translation = Vector(up_) + elif origin_mode == 'DOWN_LIMITS': + empty_object.matrix_world.translation = Vector( + down_) + elif origin_mode == 'LIMITS_MIDDLE': + empty_object.matrix_world.translation = (up_ + down_) / tow + elif origin_mode == 'MIDDLE': + empty_object.matrix_world.translation = (up + down) / tow + @classmethod def get_up_down(cls, mod, axis, top, bottom, left, right, front, back): if 'BEND' == mod.deform_method: @@ -265,21 +392,30 @@ class Utils(Data): up, down = cls.get_up_down(mod, axis, top, bottom, left, right, front, back) - ex = lambda a: cls.set_reduce(down, cls.set_reduce(cls.set_reduce( - up, down, '-'), (a, a, a), '*'), '+') + ex = lambda a: down + ((up - down) * Vector((a, a, a))) up_ = ex(up_limits) down_ = ex(down_limits) return (up, down), (up_, down_) @classmethod - def update_bound_box(cls, object): + def get_vector_axis(cls, mod): + axis = mod.deform_axis + if 'BEND' == mod.deform_method: + vector_axis = Vector((0, 0, 1)) if axis in ( + 'Y', 'X') else Vector((1, 0, 0)) + else: + vector = (Vector((1, 0, 0)) if ( + axis == 'X') else Vector((0, 1, 0))) + vector_axis = Vector((0, 0, 1)) if ( + axis == 'Z') else vector + return vector_axis + @classmethod + def update_bound_box(cls, obj: 'bpy.types.Object'): context = bpy.context data = bpy.data - obj = object matrix = obj.matrix_world.copy() # 物体矩阵 - # add simple_deform mesh (min_x, min_y, min_z), (max_x, max_y, max_z) = cls.get_mesh_max_min_co(object) @@ -291,18 +427,19 @@ class Utils(Data): (min_x, min_y, max_z), (max_x, max_y, max_z), (min_x, max_y, max_z)) - if data.objects.get(G_NAME): - data.objects.remove(data.objects.get(G_NAME)) + name = cls.G_NAME + if data.objects.get(name): + data.objects.remove(data.objects.get(name)) - if data.meshes.get(G_NAME): - data.meshes.remove(data.meshes.get(G_NAME)) - mesh = data.meshes.new(G_NAME) - mesh.from_pydata(vertexes, G_INDICES, []) + if data.meshes.get(name): + data.meshes.remove(data.meshes.get(name)) + mesh = data.meshes.new(name) + mesh.from_pydata(vertexes, cls.G_INDICES, []) mesh.update() - new_object = data.objects.new(G_NAME, mesh) + new_object = data.objects.new(name, mesh) - cls.link_active_collection(new_object) + cls.link_obj_to_active_collection(new_object) if new_object.parent != obj: new_object.parent = obj @@ -327,7 +464,7 @@ class Utils(Data): simple_deform.limits[0] = mo.limits[0] simple_deform.angle = mo.angle simple_deform.show_viewport = mo.show_viewport - obj = Utils.get_depsgraph(new_object) + obj = PublicUtils.get_depsgraph(new_object) cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'][mo.name] = cls.get_mesh_max_min_co( obj) new_object.hide_set(True) @@ -363,10 +500,19 @@ class Utils(Data): limits = context.object.modifiers.active.limits[:] modifiers = [getattr(context.object.modifiers.active, i) - for i in G_MODIFIERS_PROPERTY] + for i in cls.G_MODIFIERS_PROPERTY] cls.G_SimpleDeformGizmoHandlerDit['draw'] = (ver, indices, matrix, modifiers, limits) + @classmethod + def generate_co_data(cls): + handler_dit = cls.G_SimpleDeformGizmoHandlerDit + + if 'co' not in handler_dit: + handler_dit['co'] = cls.get_mesh_max_min_co( + bpy.context.object) + return handler_dit['co'] + @classmethod def update_co_data(cls, ob, mod): handler_dit = cls.G_SimpleDeformGizmoHandlerDit @@ -379,137 +525,30 @@ class Utils(Data): cls.G_SimpleDeformGizmoHandlerDit['co'] = co_items[index - 1][1] if (index or (index != 1)) else \ modifiers_co['co'] + +class tmp: @classmethod - def generate_co_data(cls): - handler_dit = cls.G_SimpleDeformGizmoHandlerDit - - if 'co' not in handler_dit: - handler_dit['co'] = cls.get_mesh_max_min_co( - bpy.context.object) - return handler_dit['co'] - - @classmethod - def new_empty(cls, obj, mod): - origin = mod.origin - if origin is None: - new_name = G_NAME + '_Empty_' + str(uuid.uuid4()) - origin_object = bpy.data.objects.new(new_name, None) - cls.link_active_collection(origin_object) - origin_object.hide_set(True) - origin_object.empty_display_size = min(obj.dimensions) - mod.origin = origin_object - else: - origin_object = mod.origin - origin_object.hide_viewport = False - - if origin_object.parent != obj: - origin_object.parent = obj - - # add constraints - if G_CON_LIMIT_NAME in origin_object.constraints.keys(): - limit_constraints = origin.constraints.get(G_CON_LIMIT_NAME) - else: - limit_constraints = origin_object.constraints.new( - 'LIMIT_ROTATION') - limit_constraints.name = G_CON_LIMIT_NAME - limit_constraints.owner_space = 'WORLD' - limit_constraints.space_object = obj - limit_constraints.use_transform_limit = True - limit_constraints.use_limit_x = True - limit_constraints.use_limit_y = True - limit_constraints.use_limit_z = True - con_copy_name = G_NAME + 'constraints_copy_rotation' - if con_copy_name in origin_object.constraints.keys(): - copy_constraints = origin.constraints.get(con_copy_name) - else: - copy_constraints = origin_object.constraints.new( - 'COPY_ROTATION') - copy_constraints.name = con_copy_name - copy_constraints.target = obj - copy_constraints.mix_mode = 'BEFORE' - copy_constraints.target_space = 'WORLD' - copy_constraints.owner_space = 'WORLD' - origin_object.rotation_euler.zero() - origin_object.scale = 1, 1, 1 - return origin_object, G_CON_LIMIT_NAME - - @classmethod - def co_to_direction(cls, mat, data): - (min_x, min_y, min_z), (max_x, max_y, - max_z) = data - a = mat @ Vector((max_x, max_y, max_z)) - b = mat @ Vector((max_x, min_y, min_z)) - c = mat @ Vector((min_x, max_y, min_z)) - d = mat @ Vector((min_x, min_y, max_z)) - - def pos_get(a, b): - return cls.set_reduce(cls.set_reduce(a, b, '+'), (2, 2, 2), '/') - - top = Vector(pos_get(a, d)) - bottom = Vector(pos_get(c, b)) - left = Vector(pos_get(c, d)) - right = Vector(pos_get(a, b)) - front = Vector(pos_get(d, b)) - back = Vector(pos_get(c, a)) - return top, bottom, left, right, front, back - - @classmethod - def each_face_pos(cls, mat: 'Matrix' = None): - if mat is None: - mat = Matrix() - return cls.co_to_direction(mat, cls.G_SimpleDeformGizmoHandlerDit['co']) - - @classmethod - def update_matrix(cls, mod, ob): - if mod.deform_method == 'BEND': - cls.new_empty(ob, mod) - if mod.origin: - empty_object = mod.origin - modifiers_co = cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'] - for index, mod_name in enumerate(modifiers_co): - co_items = list(modifiers_co.items()) - if mod.name == mod_name: - data = co_items[index - 1][1] if ( - index or (index != 1)) else modifiers_co['co'] - (up, down), (up_, down_) = cls.get_limits_pos( - mod, cls.co_to_direction(ob.matrix_world.copy(), data)) - origin_mode = cls.get_origin_property_group( - mod, ob).origin_mode - cls.set_empty_obj_matrix( - origin_mode, empty_object, up_, down_, up, down) - - @classmethod - def update_empty_matrix(cls): - ob = bpy.context.object - for mod in ob.modifiers: - if mod.type == 'SIMPLE_DEFORM': - cls.update_matrix(mod, ob) - - @classmethod - def get_up_down_return_list(cls, mod, axis, up_, down_, data): - top, bottom, left, right, front, back = data - if 'BEND' == mod.deform_method: - if axis in ('X', 'Y'): - top = up_ - bottom = down_ - elif axis == 'Z': - right = up_ - left = down_ - else: - if axis == 'X': - right = up_ - left = down_ - elif axis == 'Y': - back = up_ - front = down_ - elif axis == 'Z': - top = up_ - bottom = down_ - return top, bottom, left, right, front, back + def get_origin_bounds(cls, obj: 'bpy.types.Object') -> list: + modifiers_dict = {} + for mod in obj.modifiers: + if (mod == obj.modifiers.active) or (modifiers_dict != {}): + modifiers_dict[mod] = (mod.show_render, mod.show_viewport) + mod.show_viewport = False + mod.show_render = False + matrix_obj = obj.matrix_world.copy() + obj.matrix_world.zero() + obj.scale = (1, 1, 1) + bound = cls.bound_box_to_list(obj) + obj.matrix_world = matrix_obj + for mod in modifiers_dict: + show_render, show_viewport = modifiers_dict[mod] + mod.show_render = show_render + mod.show_viewport = show_viewport + return list(bound) def register(): - PublicClass.load_gizmo_data() + PublicData.load_gizmo_data() def unregister(): -- 2.30.2 From af50b2f258787836e823b38d15f74b757141aa01 Mon Sep 17 00:00:00 2001 From: EMM <3209970865@qq.com> Date: Wed, 29 Mar 2023 18:31:45 +0800 Subject: [PATCH 02/12] TODO add property snap value --- simple_deform_helper/draw.py | 91 +++---- simple_deform_helper/gizmo/__init__.py | 91 +------ .../gizmo/angle_and_factor.py | 163 ++++++++++++ simple_deform_helper/gizmo/bend_axis.py | 48 +++- .../gizmo/ctrl_value_and_factor.py | 132 ---------- ...l_tow_point.py => up_down_limits_point.py} | 80 ++---- simple_deform_helper/operators.py | 12 +- simple_deform_helper/panel.py | 6 +- simple_deform_helper/preferences.py | 8 +- simple_deform_helper/utils.py | 237 +++++++++++++++--- 10 files changed, 492 insertions(+), 376 deletions(-) create mode 100644 simple_deform_helper/gizmo/angle_and_factor.py delete mode 100644 simple_deform_helper/gizmo/ctrl_value_and_factor.py rename simple_deform_helper/gizmo/{ctrl_tow_point.py => up_down_limits_point.py} (82%) diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index 54d74b53e..e7970055b 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -5,7 +5,7 @@ import gpu from gpu_extras.batch import batch_for_shader from mathutils import Vector -from .utils import PublicUtils +from .utils import GizmoUtils class Handler: @@ -39,7 +39,48 @@ class Handler: cls.G_SimpleDeformGizmoHandlerDit.clear() -class Draw3D(PublicUtils): +class Draw3D(GizmoUtils): + + @classmethod + def draw_bound_box(cls): + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(1) + + gpu.state.blend_set('ALPHA') + gpu.state.depth_test_set('ALWAYS') + + context = bpy.context + if cls.simple_deform_public_poll(context): + cls.is_draw_box(context) + else: + Handler.del_handler() + + @classmethod + def is_draw_box(cls, context): + obj = context.object # 活动物体 + matrix = obj.matrix_world # 活动物体矩阵 + modifier = context.object.modifiers.active # 活动修改器 + + pref = cls.pref_() + simple_poll = cls.simple_deform_public_poll(context) + bend = modifier and (modifier.deform_method == 'BEND') + display_switch_axis = not pref.display_bend_axis_switch_gizmo + + cls.draw_scale_text(obj) + cls.update_co_data(obj, modifier) + + co_data = cls.generate_co_data() + + if simple_poll and ((not bend) or display_switch_axis): + # draw bound box + cls.draw_box(co_data, matrix) + # cls.draw_deform_mesh(obj, context) + cls.draw_limits_line() + cls.draw_limits_bound_box() + elif simple_poll and (bend and not display_switch_axis): + cls.draw_box(co_data, matrix) + cls.new_empty(obj, modifier) + @classmethod def draw_3d_shader(cls, pos, indices, color=None, *, shader_name='3D_UNIFORM_COLOR', draw_type='LINES'): shader = gpu.shader.from_builtin(shader_name) @@ -86,8 +127,8 @@ class Draw3D(PublicUtils): @classmethod def draw_box(cls, data, mat): pref = cls.pref_() - coords = PublicUtils.matrix_calculation(mat, - cls.data_to_calculation(data)) + coords = cls.matrix_calculation(mat, + cls.data_to_calculation(data)) cls.draw_3d_shader(coords, cls.G_INDICES, pref.bound_box_color) @classmethod @@ -111,7 +152,7 @@ class Draw3D(PublicUtils): if 'draw_limits_bound_box' in handler_dit: # draw limits_bound_box mat, data = handler_dit['draw_limits_bound_box'] - coords = PublicUtils.matrix_calculation(mat, cls.data_to_calculation(data)) + coords = cls.matrix_calculation(mat, cls.data_to_calculation(data)) cls.draw_3d_shader(coords, cls.G_INDICES, pref.limits_bound_box_color) @@ -147,43 +188,3 @@ class Draw3D(PublicUtils): if (ob.scale != Vector((1, 1, 1))) and ('handler_text' not in cls.G_SimpleDeformGizmoHandlerDit): cls.G_SimpleDeformGizmoHandlerDit['handler_text'] = bpy.types.SpaceView3D.draw_handler_add( cls.draw_str, (), 'WINDOW', 'POST_PIXEL') - - @classmethod - def is_draw_box(cls, context): - obj = context.object # 活动物体 - matrix = obj.matrix_world # 活动物体矩阵 - modifier = context.object.modifiers.active # 活动修改器 - - pref = cls.pref_() - simple_poll = PublicUtils.simple_deform_public_poll(context) - bend = modifier and (modifier.deform_method == 'BEND') - display_switch_axis = not pref.display_bend_axis_switch_gizmo - - cls.draw_scale_text(obj) - PublicUtils.update_co_data(obj, modifier) - - co_data = PublicUtils.generate_co_data() - - if simple_poll and ((not bend) or display_switch_axis): - # draw bound box - cls.draw_box(co_data, matrix) - cls.draw_deform_mesh(obj, context) - cls.draw_limits_line() - cls.draw_limits_bound_box() - elif simple_poll and (bend and not display_switch_axis): - cls.draw_box(co_data, matrix) - PublicUtils.new_empty(obj, modifier) - - @classmethod - def draw_bound_box(cls): - gpu.state.blend_set('ALPHA') - gpu.state.line_width_set(1) - - gpu.state.blend_set('ALPHA') - gpu.state.depth_test_set('ALWAYS') - - context = bpy.context - if PublicUtils.simple_deform_public_poll(context): - cls.is_draw_box(context) - else: - Handler.del_handler() diff --git a/simple_deform_helper/gizmo/__init__.py b/simple_deform_helper/gizmo/__init__.py index f73e4cb20..5bb9fcee9 100644 --- a/simple_deform_helper/gizmo/__init__.py +++ b/simple_deform_helper/gizmo/__init__.py @@ -1,87 +1,20 @@ import bpy from bpy_types import Gizmo -from draw import Handler -from utils import PublicUtils - - -class GizmoProperty: - - @property - def obj(self): - return bpy.context.object - - @property - def modifier(self): - obj = self.obj - if not obj: - return - return obj.modifiers.active - - @property - def active_modifier_is_simple_deform(self): - return self.modifier and self.modifier.type == 'SIMPLE_DEFORM' - - @property - def is_use_angle_value(self): - if self.active_modifier_is_simple_deform: - return self.modifier.deform_method in ('TWIST', 'BEND') - - -class GizmoPublic(GizmoProperty, PublicUtils, Handler): - def generate_gizmo_mode(self, gizmo_data): - """生成gizmo的上限下限及角度设置 - - Args: - gizmo_data (_type_): _description_ - """ - for i, j, k in gizmo_data: - setattr(self, i, self.gizmos.new(j)) - gizmo = getattr(self, i) - for f in k: - if f == 'target_set_operator': - gizmo.target_set_operator(k[f]) - elif f == 'target_set_prop': - gizmo.target_set_prop(*k[f]) - else: - setattr(gizmo, f, k[f]) - - -class CustomGizmo(Gizmo, PublicUtils, Handler): - """绘制自定义Gizmo""" - bl_idname = '_Custom_Gizmo' - draw_type: str - custom_shape: dict - - def setup(self): - self.draw_type = 'None_GizmoGroup_' - if not hasattr(self, 'custom_shape'): - self.custom_shape = {} - for i in self.G_GizmoCustomShapeDict: - self.custom_shape[i] = self.new_custom_shape( - 'TRIS', self.G_GizmoCustomShapeDict[i]) - self.add_handler() - - def draw(self, context): - self.draw_custom_shape(self.custom_shape[self.draw_type]) - - def draw_select(self, context, select_id): - self.draw_custom_shape( - self.custom_shape[self.draw_type], select_id=select_id) - - def invoke(self, context, event): - return {'RUNNING_MODAL'} - - def modal(self, context, event, tweak): - self.add_handler() - self.update_bound_box(context.object) - self.update_empty_matrix() - return {'RUNNING_MODAL'} - +from .bend_axis import SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo, CustomGizmo +from .up_down_limits_point import GizmoProperty, UpDownLimitsGizmo +from .angle_and_factor import AngleGizmoGroup, AngleGizmo +from ..draw import Handler class_list = ( - ViewSimpleDeformGizmo, - SimpleDeformGizmoGroup, + # GizmoProperty, + # UpDownLimitsGizmo, + + AngleGizmo, + AngleGizmoGroup, + + CustomGizmo, + SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo, ) register_class, unregister_class = bpy.utils.register_classes_factory(class_list) diff --git a/simple_deform_helper/gizmo/angle_and_factor.py b/simple_deform_helper/gizmo/angle_and_factor.py new file mode 100644 index 000000000..1fcd2a1b1 --- /dev/null +++ b/simple_deform_helper/gizmo/angle_and_factor.py @@ -0,0 +1,163 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +import math + +from bpy.types import ( + GizmoGroup, +) +from bpy.types import Gizmo +from mathutils import Vector, Euler, Matrix + +from ..utils import GizmoUtils +from ..draw import Handler + + +class AngleGizmo(Gizmo, GizmoUtils): + bl_idname = 'ViewSimpleAngleGizmo' + + bl_target_properties = ( + {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, + {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, + {'id': 'angle', 'type': 'FLOAT', 'array_length': 1}, + ) + + __slots__ = ( + 'draw_type', + 'mouse_dpi', + 'empty_object', + 'init_mouse_y', + 'init_mouse_x', + 'custom_shape', + 'int_value_angle', + 'rotate_follow_modifier', + ) + + int_value_angle: float + rotate_follow_modifier: bool + + def setup(self): + self.mouse_dpi = 2 + self.rotate_follow_modifier = True + self.init_setup() + + def invoke(self, context, event): + self.init_invoke(context, event) + self.int_value_angle = self.target_get_value('angle') + return {'RUNNING_MODAL'} + + def modal(self, context, event, tweak): + self.update_header_text(context) + self.update_prop_value(event, tweak) + return {'RUNNING_MODAL'} + + def exit(self, context, cancel): + context.area.header_text_set(None) + if cancel: + self.target_set_value('angle', self.int_value_angle) + + def update_prop_value(self, event, tweak): + # radians 弧度 + # degrees 角度 + delta = self.get_delta(event) + value = math.degrees(self.int_value_angle - delta) + new_value = (self.get_snap(value, tweak)) + old_value = math.degrees(self.target_get_value('angle')) + + print(new_value, old_value) + self.target_set_value('angle', math.radians(new_value)) + + def update_gizmo_matrix(self, context): + matrix = context.object.matrix_world + self.matrix_basis.translation = matrix @ Vector((self.generate_co_data()[1])) + + def update_header_text(self, context): + text = self.translate_header_text('Angle', round(math.degrees(self.modifier_angle), 3)) + context.area.header_text_set(text) + + +class AngleGizmoGroup(GizmoGroup, GizmoUtils, Handler): + """ShowGizmo + """ + bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup' + bl_label = 'AngleGizmoGroup' + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D', 'PERSISTENT', } + + @classmethod + def poll(cls, context): + return cls.simple_deform_show_gizmo_poll(context) + + def setup(self, context): + sd_name = AngleGizmo.bl_idname + + add_data = [ + ('angle', + sd_name, + {'draw_type': 'SimpleDeform_GizmoGroup_', + 'color': (1.0, 0.5, 1.0), + 'alpha': 0.3, + 'color_highlight': (1.0, 1.0, 1.0), + 'alpha_highlight': 0.3, + 'use_draw_modal': True, + 'scale_basis': 0.1, + 'use_draw_value': True, + 'mouse_dpi': 100, + }), + ] + + self.generate_gizmo_mode(add_data) + + data_path = 'object.modifiers.active.deform_axis' + set_enum = 'wm.context_set_enum' + + for axis in ('X', 'Y', 'Z'): + # show toggle axis button + gizmo = self.gizmos.new('GIZMO_GT_button_2d') + gizmo.icon = f'EVENT_{axis.upper()}' + gizmo.draw_options = {'BACKDROP', 'HELPLINE'} + ops = gizmo.target_set_operator(set_enum) + ops.data_path = data_path + ops.value = axis + gizmo.color = (0, 0, 0) + gizmo.alpha = 0.3 + gizmo.color_highlight = 1.0, 1.0, 1.0 + gizmo.alpha_highlight = 0.3 + gizmo.use_draw_modal = True + gizmo.use_draw_value = True + gizmo.scale_basis = 0.1 + setattr(self, f'deform_axis_{axis.lower()}', gizmo) + + def refresh(self, context): + + self.angle.target_set_prop('angle', + context.object.modifiers.active, + 'angle') + + # pro = context.object.SimpleDeformGizmo_PropertyGroup + # self.down_limits.target_set_prop('down_limits', + # pro, + # 'down_limits') + # self.down_limits.target_set_prop('up_limits', + # pro, + # 'up_limits') + # self.up_limits.target_set_prop('down_limits', + # pro, + # 'down_limits') + # self.up_limits.target_set_prop('up_limits', + # pro, + # 'up_limits') + + def draw_prepare(self, context): + ob = context.object + mat = ob.matrix_world + + if 'co' in self.G_SimpleDeformGizmoHandlerDit: + def _mat(f): + co = self.G_SimpleDeformGizmoHandlerDit['co'][0] + co = (co[0] + (max(ob.dimensions) * f), co[1], + co[2] - (min(ob.dimensions) * 0.3)) + return mat @ Vector(co) + + self.deform_axis_x.matrix_basis.translation = _mat(0) + self.deform_axis_y.matrix_basis.translation = _mat(0.3) + self.deform_axis_z.matrix_basis.translation = _mat(0.6) diff --git a/simple_deform_helper/gizmo/bend_axis.py b/simple_deform_helper/gizmo/bend_axis.py index 6fa7cf7af..3d102ec70 100644 --- a/simple_deform_helper/gizmo/bend_axis.py +++ b/simple_deform_helper/gizmo/bend_axis.py @@ -1,13 +1,46 @@ import math from bpy.types import GizmoGroup +from bpy_types import Gizmo from mathutils import Euler, Vector from ..draw import Handler -from ..utils import PublicUtils +from ..utils import GizmoUtils -class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, PublicUtils, Handler): +class CustomGizmo(Gizmo, GizmoUtils, Handler): + """绘制自定义Gizmo""" + bl_idname = '_Custom_Gizmo' + draw_type: str + custom_shape: dict + + def setup(self): + self.draw_type = 'None_GizmoGroup_' + if not hasattr(self, 'custom_shape'): + self.custom_shape = {} + for i in self.G_GizmoCustomShapeDict: + self.custom_shape[i] = self.new_custom_shape( + 'TRIS', self.G_GizmoCustomShapeDict[i]) + self.add_handler() + + def draw(self, context): + self.draw_custom_shape(self.custom_shape[self.draw_type]) + + def draw_select(self, context, select_id): + self.draw_custom_shape( + self.custom_shape[self.draw_type], select_id=select_id) + + def invoke(self, context, event): + return {'RUNNING_MODAL'} + + def modal(self, context, event, tweak): + self.add_handler() + self.update_bound_box(context.object) + self.update_empty_matrix() + return {'RUNNING_MODAL'} + + +class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, GizmoUtils): """绘制切换变型轴的 变换方向 """ @@ -23,21 +56,15 @@ class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, PublicUtils, H @classmethod def poll(cls, context): - pref = cls.pref_() - simple = cls.simple_deform_public_poll(context) - bend = simple and ( - context.object.modifiers.active.deform_method == 'BEND') - switch_axis = pref.display_bend_axis_switch_gizmo - return switch_axis and bend + return cls.simple_deform_show_bend_axis_witch_poll(context) def setup(self, context): _draw_type = 'SimpleDeform_Bend_Direction_' _color_a = 1, 0, 0 _color_b = 0, 1, 0 - self.add_handler() for na, axis, rot, positive in ( - ('top_a', 'X', (math.radians(90), 0, math.radians(90)), True), + ('top_a', 'X', (math.radians(90), 0, math.radians(9 - 0)), True), ('top_b', 'X', (math.radians(90), 0, 0), True), ('bottom_a', 'X', (math.radians(90), 0, math.radians(90)), False), @@ -102,6 +129,5 @@ class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, PublicUtils, H for i, j, w, in for_list: gizmo = getattr(self, i, False) rot = Euler(w, 'XYZ').to_matrix().to_4x4() - gizmo.matrix_basis = mat.to_euler().to_matrix().to_4x4() @ rot gizmo.matrix_basis.translation = Vector(j) diff --git a/simple_deform_helper/gizmo/ctrl_value_and_factor.py b/simple_deform_helper/gizmo/ctrl_value_and_factor.py deleted file mode 100644 index 2dd4ad36b..000000000 --- a/simple_deform_helper/gizmo/ctrl_value_and_factor.py +++ /dev/null @@ -1,132 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -from bpy.types import ( - GizmoGroup, -) -from mathutils import Vector - -from utils import PublicUtils - - -class SimpleDeformGizmoGroup(GizmoGroup, PublicUtils): - """显示Gizmo - """ - bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup' - bl_label = 'SimpleDeformGizmoGroup' - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = {'3D', 'PERSISTENT', } - - @classmethod - def poll(cls, context): - pol = cls.simple_deform_public_poll(context) - pref = cls.pref_() - deform_method = ( - pol and (context.object.modifiers.active.deform_method != 'BEND')) - display_gizmo = pref.display_bend_axis_switch_gizmo - switch = (not display_gizmo) - return pol and (deform_method or switch) - - def setup(self, context): - sd_name = ViewSimpleDeformGizmo.bl_idname - - add_data = (('up_limits', - sd_name, - {'ctrl_mode': 'up_limits', - 'draw_type': 'Sphere_GizmoGroup_', - 'mouse_dpi': 1000, - 'color': (1.0, 0, 0), - 'alpha': 0.5, - 'color_highlight': (1.0, 1.0, 1.0), - 'alpha_highlight': 0.3, - 'use_draw_modal': True, - 'scale_basis': 0.1, - 'use_draw_value': True, }), - ('down_limits', - sd_name, - {'ctrl_mode': 'down_limits', - 'draw_type': 'Sphere_GizmoGroup_', - 'mouse_dpi': 1000, - 'color': (0, 1.0, 0), - 'alpha': 0.5, - 'color_highlight': (1.0, 1.0, 1.0), - 'alpha_highlight': 0.3, - 'use_draw_modal': True, - 'scale_basis': 0.1, - 'use_draw_value': True, }), - ('angle', - sd_name, - {'ctrl_mode': 'angle', - 'draw_type': 'SimpleDeform_GizmoGroup_', - 'color': (1.0, 0.5, 1.0), - 'alpha': 0.3, - 'color_highlight': (1.0, 1.0, 1.0), - 'alpha_highlight': 0.3, - 'use_draw_modal': True, - 'scale_basis': 0.1, - 'use_draw_value': True, - 'mouse_dpi': 100, - }), - ) - - self.generate_gizmo_mode(add_data) - - data_path = 'object.modifiers.active.deform_axis' - set_enum = 'wm.context_set_enum' - - for axis in ('X', 'Y', 'Z'): - # show toggle axis button - gizmo = self.gizmos.new('GIZMO_GT_button_2d') - gizmo.icon = f'EVENT_{axis.upper()}' - gizmo.draw_options = {'BACKDROP', 'HELPLINE'} - ops = gizmo.target_set_operator(set_enum) - ops.data_path = data_path - ops.value = axis - gizmo.color = (0, 0, 0) - gizmo.alpha = 0.3 - gizmo.color_highlight = 1.0, 1.0, 1.0 - gizmo.alpha_highlight = 0.3 - gizmo.use_draw_modal = True - gizmo.use_draw_value = True - gizmo.scale_basis = 0.1 - setattr(self, f'deform_axis_{axis.lower()}', gizmo) - self.add_handler() - - def refresh(self, context): - pro = context.object.SimpleDeformGizmo_PropertyGroup - - self.angle.target_set_prop('angle', - context.object.modifiers.active, - 'angle') - self.down_limits.target_set_prop('down_limits', - pro, - 'down_limits') - self.down_limits.target_set_prop('up_limits', - pro, - 'up_limits') - self.up_limits.target_set_prop('down_limits', - pro, - 'down_limits') - self.up_limits.target_set_prop('up_limits', - pro, - 'up_limits') - self.add_handler() - - def draw_prepare(self, context): - ob = context.object - mat = ob.matrix_world - - if 'co' in self.G_SimpleDeformGizmoHandlerDit: - def _mat(f): - co = self.G_SimpleDeformGizmoHandlerDit['co'][0] - co = (co[0] + (max(ob.dimensions) * f), co[1], - co[2] - (min(ob.dimensions) * 0.3)) - return mat @ Vector(co) - - self.deform_axis_x.matrix_basis.translation = _mat(0) - self.deform_axis_y.matrix_basis.translation = _mat(0.3) - self.deform_axis_z.matrix_basis.translation = _mat(0.6) - self.add_handler() - - def invoke_prepare(self, context, gizmo): - self.add_handler() diff --git a/simple_deform_helper/gizmo/ctrl_tow_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py similarity index 82% rename from simple_deform_helper/gizmo/ctrl_tow_point.py rename to simple_deform_helper/gizmo/up_down_limits_point.py index 3a8e5583a..c7e1eb46e 100644 --- a/simple_deform_helper/gizmo/ctrl_tow_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -1,7 +1,16 @@ +import math +from typing import Callable, Any + import bpy +from bpy_extras import view3d_utils +from bpy.types import Gizmo +from mathutils import Euler, Vector + +from ..draw import Handler +from ..utils import GizmoUtils -class GizmoProperty(Gizmo, PublicUtils, Handler): +class GizmoProperty(GizmoUtils, Handler): @property def is_angle_mode(self): return self.ctrl_mode == 'angle' @@ -15,15 +24,14 @@ class GizmoProperty(Gizmo, PublicUtils, Handler): return self.ctrl_mode == 'down_limits' -class ViewSimpleDeformGizmo(GizmoProperty): +class UpDownLimitsGizmo(GizmoProperty, Gizmo): """显示轴向切换拖动点Gizmo(两个点) """ - bl_idname = 'ViewSimpleDeformGizmo' + bl_idname = 'UpDownLimitsGizmo' bl_target_properties = ( {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, - {'id': 'angle', 'type': 'FLOAT', 'array_length': 1}, ) __slots__ = ( @@ -46,27 +54,6 @@ class ViewSimpleDeformGizmo(GizmoProperty): 'rotate_follow_modifier', ) - def update_gizmo_rotate(self, axis, mod): - if self.rotate_follow_modifier: - rot = Euler() - if axis == 'X' and (not self.is_positive(mod.angle)): - rot.z = math.pi - - elif axis == 'Y': - if self.is_positive(mod.angle): - rot.z = -(math.pi / 2) - else: - rot.z = math.pi / 2 - elif axis == 'Z': - if self.is_positive(mod.angle): - rot.x = rot.z = rot.y = math.pi / 2 - else: - rot.z = rot.y = math.pi / 2 - rot.x = -(math.pi / 2) - - rot = rot.to_matrix() - self.matrix_basis = self.matrix_basis @ rot.to_4x4() - def update_draw_limits_bound_box(self, data, mod, axis, mat, up_, down_): top, bottom, left, right, front, back = data if mod.origin: @@ -111,7 +98,6 @@ class ViewSimpleDeformGizmo(GizmoProperty): self.matrix_basis = ob.matrix_world.normalized() co = self.generate_co_data() - self.update_gizmo_rotate(axis, mod) # calculation limits position top, bottom, left, right, front, back = self.each_face_pos(mat) (up, down), (up_, down_) = self.get_limits_pos( @@ -130,38 +116,19 @@ class ViewSimpleDeformGizmo(GizmoProperty): def setup(self): self.generate_co_data() self.draw_type = 'None_GizmoGroup_' - self.ctrl_mode = 'angle' # up_limits , down_limits + self.ctrl_mode = 'up_limits' # up_limits , down_limits self.mouse_dpi = 10 self.rotate_follow_modifier = True - if not hasattr(self, 'custom_shape'): - self.custom_shape = {} - for i in self.G_GizmoCustomShapeDict: - item = self.G_GizmoCustomShapeDict[i] - self.custom_shape[i] = self.new_custom_shape('TRIS', item) self.add_handler() - def draw(self, context): - self.add_handler() - - self.update_gizmo_matrix(context) - self.draw_custom_shape(self.custom_shape[self.draw_type]) - - def draw_select(self, context, select_id): - self.update_gizmo_matrix(context) - self.draw_custom_shape( - self.custom_shape[self.draw_type], select_id=select_id) - def invoke(self, context, event): - self.init_mouse_y = event.mouse_y - self.init_mouse_x = event.mouse_x + self.init_invoke(context, event) mod = context.object.modifiers.active limits = mod.limits up_limits = limits[1] down_limits = limits[0] - if 'angle' == self.ctrl_mode: - self.int_value_angle = self.target_get_value('angle') - elif 'up_limits' == self.ctrl_mode: + if 'up_limits' == self.ctrl_mode: self.int_value_up_limits = up_limits self.target_set_value('up_limits', self.int_value_up_limits) elif 'down_limits' == self.ctrl_mode: @@ -179,7 +146,6 @@ class ViewSimpleDeformGizmo(GizmoProperty): self.target_set_value('deform_axis', self.value_deform_axis) elif 'up_limits' == self.ctrl_mode: self.target_set_value('up_limits', self.int_value_up_limits) - elif 'down_limits' == self.ctrl_mode: self.target_set_value( 'down_limits', self.int_value_down_limits) @@ -256,14 +222,6 @@ class ViewSimpleDeformGizmo(GizmoProperty): elif self.is_down_limits_mode: self.set_down_value(data, mu) - @staticmethod - def snap_value(value, event: 'bpy.types.Event'): - if event.ctrl: - value //= 5 - elif event.ctrl and event.shift: - value //= 1 - return value - def update_header_text(self, context, mod, origin, up_limits, down_limits): translate: Callable[[Any], str] = lambda t: bpy.app.translations.pgettext(t) mode = origin.bl_rna.properties['origin_mode'].enum_items[origin.origin_mode].name @@ -274,7 +232,7 @@ class ViewSimpleDeformGizmo(GizmoProperty): text = translate(mode) + ' ' if self.is_use_angle_value and self.is_angle_mode: - text += t_('Angle', math.degrees(mod.angle)) + text += t_() elif self.is_up_limits_mode: text += t_('Upper limit', up_limits) elif self.is_down_limits_mode: @@ -306,7 +264,6 @@ class ViewSimpleDeformGizmo(GizmoProperty): def modal(self, context, event, tweak): self.update_bound_box(context.object) - delta = (self.init_mouse_x - event.mouse_x) / self.mouse_dpi ob = context.object mod = ob.modifiers.active limits = mod.limits @@ -320,10 +277,7 @@ class ViewSimpleDeformGizmo(GizmoProperty): min_value = down_limits + limit_scope difference_value = up_limits - down_limits - if 'SNAP' in tweak: - delta = round(delta) - if 'PRECISE' in tweak: - delta /= self.mouse_dpi + delta = self.get_delta(event, tweak) delta = self.delta_update(context, event, delta) if origin_mode != 'NOT' and ('draw_line' in self.G_SimpleDeformGizmoHandlerDit): diff --git a/simple_deform_helper/operators.py b/simple_deform_helper/operators.py index 70f05c8de..332962e6b 100644 --- a/simple_deform_helper/operators.py +++ b/simple_deform_helper/operators.py @@ -4,10 +4,10 @@ import bpy from bpy.types import Operator from bpy.props import FloatProperty, StringProperty, BoolProperty -from .utils import PublicUtils +from .utils import GizmoUtils -class DeformAxisOperator(Operator, PublicUtils): +class DeformAxisOperator(Operator, GizmoUtils): bl_idname = 'simple_deform_gizmo.deform_axis' bl_label = 'deform_axis' bl_description = 'deform_axis operator' @@ -26,12 +26,12 @@ class DeformAxisOperator(Operator, PublicUtils): return {'RUNNING_MODAL'} def modal(self, context, event): - from gizmo.ctrl_value_and_factor import PublicUtils + from gizmo.angle_and_factor import GizmoUtils mod = context.object.modifiers.active mod.deform_axis = self.Deform_Axis - empty, con_limit_name = PublicUtils.new_empty(context.object, mod) - is_positive = PublicUtils.is_positive(mod.angle) + empty, con_limit_name = GizmoUtils.new_empty(context.object, mod) + is_positive = GizmoUtils.is_positive(mod.angle) for limit, value in (('max_x', self.X_Value), ('min_x', self.X_Value), @@ -48,7 +48,7 @@ class DeformAxisOperator(Operator, PublicUtils): if not event.ctrl: self.pref.display_bend_axis_switch_gizmo = False - PublicUtils.update_bound_box(context.object) + GizmoUtils.update_bound_box(context.object) return {'FINISHED'} diff --git a/simple_deform_helper/panel.py b/simple_deform_helper/panel.py index 76d859ff9..3b840b0b2 100644 --- a/simple_deform_helper/panel.py +++ b/simple_deform_helper/panel.py @@ -2,10 +2,10 @@ import bpy from bpy.types import Panel, VIEW3D_HT_tool_header -from .utils import PublicUtils +from .utils import GizmoUtils -class SimpleDeformHelperToolPanel(Panel, PublicUtils): +class SimpleDeformHelperToolPanel(Panel, GizmoUtils): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Tool' @@ -16,7 +16,7 @@ class SimpleDeformHelperToolPanel(Panel, PublicUtils): @classmethod def poll(cls, context): - return PublicUtils.simple_deform_public_poll(context) + return cls.simple_deform_public_poll(context) def draw(self, context): cls = SimpleDeformHelperToolPanel diff --git a/simple_deform_helper/preferences.py b/simple_deform_helper/preferences.py index 526d02f92..d168f4813 100644 --- a/simple_deform_helper/preferences.py +++ b/simple_deform_helper/preferences.py @@ -11,11 +11,11 @@ from bpy.types import ( PropertyGroup, ) -from .utils import PublicUtils, GizmoUtils +from .utils import GizmoUtils -class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicUtils): - bl_idname = PublicUtils.G_ADDON_NAME +class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): + bl_idname = GizmoUtils.G_ADDON_NAME deform_wireframe_color: FloatVectorProperty( name='Deform Wireframe', @@ -141,7 +141,7 @@ register_class, unregister_class = bpy.utils.register_classes_factory(class_list def register(): register_class() - PublicUtils.pref_().display_bend_axis_switch_gizmo = False + GizmoUtils.pref_().display_bend_axis_switch_gizmo = False bpy.types.Object.SimpleDeformGizmo_PropertyGroup = PointerProperty( type=SimpleDeformGizmoObjectPropertyGroup, name='SimpleDeformGizmo_PropertyGroup') diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index 74fb8b7c2..10f65a7c9 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -8,7 +8,7 @@ from typing import Callable, Any import bpy import numpy as np from bpy.types import AddonPreferences -from mathutils import Vector, Matrix +from mathutils import Vector, Matrix, Euler class PublicData: @@ -93,7 +93,61 @@ class PublicClass(PublicData): return PublicClass.pref_() -class PublicUtils(PublicClass): +class PublicPoll(PublicClass): + + @classmethod + def simple_deform_public_poll(cls, context: 'bpy.types.context') -> bool: + """Public poll + In 3D View + Active Object in ('MESH', 'LATTICE') + Active Modifier Type Is 'SIMPLE_DEFORM' and show_viewport + return True + """ + obj = context.object + if not obj: + return False + + mod = obj.modifiers.active + if not mod: + return False + + space = context.space_data + show_gizmo = space.show_gizmo if space.type == 'VIEW_3D' else True + + available_obj_type = obj and (obj.type in ('MESH', 'LATTICE')) + available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM') + is_available_obj = available_modifiers_type and available_obj_type + is_obj_mode = context.mode == 'OBJECT' + show_mod = mod.show_viewport + return is_available_obj and is_obj_mode and show_gizmo and show_mod + + @classmethod + def _simple_deform_modifier_is_bend_poll(cls, context): + """ + Public poll + active modifier deform_method =='BEND' + """ + simple = cls.simple_deform_public_poll(context) + is_bend = (context.object.modifiers.active.deform_method == 'BEND') + return simple and is_bend + + @classmethod + def simple_deform_show_bend_axis_witch_poll(cls, context): + """ + Show D + """ + switch_axis = cls.pref_().display_bend_axis_switch_gizmo + bend = cls._simple_deform_modifier_is_bend_poll(context) + return switch_axis and bend + + @classmethod + def simple_deform_show_gizmo_poll(cls, context): + poll = cls.simple_deform_public_poll(context) + not_switch = (not cls.simple_deform_show_bend_axis_witch_poll(context)) + return poll and not_switch + + +class PublicUtils(PublicPoll): @classmethod def value_limit(cls, value, max_value=1, min_value=0): """ @@ -154,7 +208,7 @@ class PublicUtils(PublicClass): return tuple(i[:] for i in obj.bound_box) @classmethod - def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> tuple: + def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> '[Vector,Vector]': if obj.type == 'MESH': ver_len = obj.data.vertices.__len__() list_vertices = np.zeros(ver_len * 3, dtype=np.float32) @@ -165,7 +219,7 @@ class PublicUtils(PublicClass): list_vertices = np.zeros(ver_len * 3, dtype=np.float32) obj.data.points.foreach_get('co', list_vertices) list_vertices = list_vertices.reshape(ver_len, 3) - return tuple(list_vertices.min(axis=0)), tuple(list_vertices.max(axis=0)) + return Vector(list_vertices.min(axis=0)), Vector(list_vertices.max(axis=0)) @classmethod def matrix_calculation(cls, mat: 'Matrix', calculation_list: 'list') -> list: @@ -203,9 +257,51 @@ class PublicUtils(PublicClass): (d, b) (c, a))) + @classmethod + def translate_text(cls, text): + return bpy.app.translations.pgettext(text) -class GizmoUtils(PublicUtils): + @classmethod + def translate_header_text(cls, mode, value): + return cls.translate_text(mode) + ':{}'.format(value) + +class GizmoProperty: + + @property + def obj(self): + return bpy.context.object + + @property + def modifier(self): + obj = self.obj + if not obj: + return + return obj.modifiers.active + + @property + def modifier_deform_axis(self): + mod = self.modifier + if mod: + return mod.deform_axis + + @property + def modifier_angle(self): + mod = self.modifier + if mod: + return mod.angle + + @property + def active_modifier_is_simple_deform(self): + return self.modifier and self.modifier.type == 'SIMPLE_DEFORM' + + @property + def is_use_angle_value(self): + if self.active_modifier_is_simple_deform: + return self.modifier.deform_method in ('TWIST', 'BEND') + + +class GizmoClassMethod(GizmoProperty, PublicUtils): @classmethod def each_face_pos(cls, mat: 'Matrix' = None): if mat is None: @@ -245,32 +341,6 @@ class GizmoUtils(PublicUtils): else: return ob.SimpleDeformGizmo_PropertyGroup - @classmethod - def simple_deform_public_poll(cls, context: 'bpy.types.context') -> bool: - """Public poll - In 3D View - Active Object in ('MESH', 'LATTICE') - Active Modifier Type Is 'SIMPLE_DEFORM' and show_viewport - return True - """ - obj = context.object - if not obj: - return False - - mod = obj.modifiers.active - if not mod: - return False - - space = context.space_data - show_gizmo = space.show_gizmo if space.type == 'VIEW_3D' else True - - available_obj_type = obj and (obj.type in ('MESH', 'LATTICE')) - available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM') - is_available_obj = available_modifiers_type and available_obj_type - is_obj_mode = context.mode == 'OBJECT' - show_mod = mod.show_viewport - return is_available_obj and is_obj_mode and show_gizmo and show_mod - @classmethod def get_up_down_return_list(cls, mod, axis, up_, down_, data): top, bottom, left, right, front, back = data @@ -418,7 +488,7 @@ class GizmoUtils(PublicUtils): matrix = obj.matrix_world.copy() # 物体矩阵 # add simple_deform mesh (min_x, min_y, min_z), (max_x, max_y, - max_z) = cls.get_mesh_max_min_co(object) + max_z) = cls.get_mesh_max_min_co(obj) vertexes = ((max_x, min_y, min_z), (min_x, min_y, min_z), (max_x, max_y, min_z), @@ -526,7 +596,85 @@ class GizmoUtils(PublicUtils): modifiers_co['co'] -class tmp: +class GizmoUtils(GizmoClassMethod): + custom_shape: dict + init_mouse_y: float + init_mouse_x: float + mouse_dpi: int + matrix_basis: Matrix + draw_type: str + + def generate_gizmo_mode(self, gizmo_data): + """生成gizmo的上限下限及角度设置 + + Args: + gizmo_data (_type_): _description_ + """ + for i, j, k in gizmo_data: + setattr(self, i, self.gizmos.new(j)) + gizmo = getattr(self, i) + for f in k: + if f == 'target_set_operator': + gizmo.target_set_operator(k[f]) + elif f == 'target_set_prop': + gizmo.target_set_prop(*k[f]) + else: + setattr(gizmo, f, k[f]) + + def init_shape(self): + if not hasattr(self, 'custom_shape'): + self.custom_shape = {} + for i in self.G_GizmoCustomShapeDict: + item = self.G_GizmoCustomShapeDict[i] + self.custom_shape[i] = self.new_custom_shape('TRIS', item) + + def init_setup(self): + self.init_shape() + + def init_invoke(self, context, event): + self.init_mouse_y = event.mouse_y + self.init_mouse_x = event.mouse_x + + def _update_matrix(self, context): + func = getattr(self, 'update_gizmo_matrix', None) + if func: + func(context) + + def draw(self, context): + self.draw_custom_shape(self.custom_shape[self.draw_type]) + self._update_matrix(context) + + def draw_select(self, context, select_id): + self.draw_custom_shape( + self.custom_shape[self.draw_type], select_id=select_id) + self._update_matrix(context) + + def get_delta(self, event): + delta = (self.init_mouse_x - event.mouse_x) / self.mouse_dpi + return delta + + def get_snap(self, delta, tweak): + # ctrl SNAP + # shift PRECISE + is_snap = 'SNAP' in tweak + is_precise = 'PRECISE' in tweak + if is_snap and is_precise: + delta = round(delta) + # delta /= self.mouse_dpi + elif is_snap: + delta //= 5 + delta *= 5 + elif is_precise: + delta //= 0.01 + delta *= 0.01 + print('tweak', delta, tweak) + return delta + + def update_gizmo_matrix(self): + ... + + +class Tmp: @classmethod def get_origin_bounds(cls, obj: 'bpy.types.Object') -> list: modifiers_dict = {} @@ -546,6 +694,29 @@ class tmp: mod.show_viewport = show_viewport return list(bound) + def update_gizmo_rotate(self): + mod = self.modifier + axis = self.modifier_deform_axis + if self.rotate_follow_modifier: + rot = Euler() + if axis == 'X' and (not self.is_positive(mod.angle)): + rot.z = math.pi + + elif axis == 'Y': + if self.is_positive(mod.angle): + rot.z = -(math.pi / 2) + else: + rot.z = math.pi / 2 + elif axis == 'Z': + if self.is_positive(mod.angle): + rot.x = rot.z = rot.y = math.pi / 2 + else: + rot.z = rot.y = math.pi / 2 + rot.x = -(math.pi / 2) + + rot = rot.to_matrix() + self.matrix_basis = self.matrix_basis @ rot.to_4x4() + def register(): PublicData.load_gizmo_data() -- 2.30.2 From 9351c3c895e9372af41e7fff6f4b71f3e6a6533d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=90=8C=E6=96=B0?= <3209970865@qq.com> Date: Thu, 30 Mar 2023 01:42:48 +0800 Subject: [PATCH 03/12] move set deform axis gizmo to file --- simple_deform_helper/gizmo/__init__.py | 14 +- .../gizmo/angle_and_factor.py | 151 ++++----- simple_deform_helper/gizmo/bend_axis.py | 2 +- simple_deform_helper/gizmo/set_deform_axis.py | 53 ++++ .../gizmo/up_down_limits_point.py | 291 +++++++++--------- simple_deform_helper/utils.py | 251 ++++++++++----- 6 files changed, 440 insertions(+), 322 deletions(-) create mode 100644 simple_deform_helper/gizmo/set_deform_axis.py diff --git a/simple_deform_helper/gizmo/__init__.py b/simple_deform_helper/gizmo/__init__.py index 5bb9fcee9..8d136e80a 100644 --- a/simple_deform_helper/gizmo/__init__.py +++ b/simple_deform_helper/gizmo/__init__.py @@ -1,20 +1,22 @@ import bpy -from bpy_types import Gizmo -from .bend_axis import SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo, CustomGizmo -from .up_down_limits_point import GizmoProperty, UpDownLimitsGizmo from .angle_and_factor import AngleGizmoGroup, AngleGizmo +from .bend_axis import BendAxiSwitchGizmoGroup, CustomGizmo +from .set_deform_axis import SetDeformGizmoGroup +from .up_down_limits_point import UpDownLimitsGizmo, UpDownLimitsGizmoGroup from ..draw import Handler class_list = ( - # GizmoProperty, - # UpDownLimitsGizmo, + UpDownLimitsGizmo, + UpDownLimitsGizmoGroup, AngleGizmo, AngleGizmoGroup, CustomGizmo, - SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo, + BendAxiSwitchGizmoGroup, + + SetDeformGizmoGroup, ) register_class, unregister_class = bpy.utils.register_classes_factory(class_list) diff --git a/simple_deform_helper/gizmo/angle_and_factor.py b/simple_deform_helper/gizmo/angle_and_factor.py index 1fcd2a1b1..b8a3f9af3 100644 --- a/simple_deform_helper/gizmo/angle_and_factor.py +++ b/simple_deform_helper/gizmo/angle_and_factor.py @@ -1,58 +1,17 @@ # SPDX-License-Identifier: GPL-2.0-or-later import math +from bpy.types import Gizmo from bpy.types import ( GizmoGroup, ) -from bpy.types import Gizmo -from mathutils import Vector, Euler, Matrix +from mathutils import Vector -from ..utils import GizmoUtils from ..draw import Handler +from ..utils import GizmoUtils -class AngleGizmo(Gizmo, GizmoUtils): - bl_idname = 'ViewSimpleAngleGizmo' - - bl_target_properties = ( - {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, - {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, - {'id': 'angle', 'type': 'FLOAT', 'array_length': 1}, - ) - - __slots__ = ( - 'draw_type', - 'mouse_dpi', - 'empty_object', - 'init_mouse_y', - 'init_mouse_x', - 'custom_shape', - 'int_value_angle', - 'rotate_follow_modifier', - ) - - int_value_angle: float - rotate_follow_modifier: bool - - def setup(self): - self.mouse_dpi = 2 - self.rotate_follow_modifier = True - self.init_setup() - - def invoke(self, context, event): - self.init_invoke(context, event) - self.int_value_angle = self.target_get_value('angle') - return {'RUNNING_MODAL'} - - def modal(self, context, event, tweak): - self.update_header_text(context) - self.update_prop_value(event, tweak) - return {'RUNNING_MODAL'} - - def exit(self, context, cancel): - context.area.header_text_set(None) - if cancel: - self.target_set_value('angle', self.int_value_angle) +class AngleUpdate(GizmoUtils): def update_prop_value(self, event, tweak): # radians 弧度 @@ -74,6 +33,47 @@ class AngleGizmo(Gizmo, GizmoUtils): context.area.header_text_set(text) +class AngleGizmo(Gizmo, AngleUpdate): + bl_idname = 'ViewSimpleAngleGizmo' + + bl_target_properties = ( + {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, + {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, + {'id': 'angle', 'type': 'FLOAT', 'array_length': 1}, + ) + + __slots__ = ( + 'draw_type', + 'mouse_dpi', + 'empty_object', + 'init_mouse_y', + 'init_mouse_x', + 'custom_shape', + 'int_value_angle', + ) + + int_value_angle: float + + def setup(self): + self.mouse_dpi = 10 + self.init_setup() + + def invoke(self, context, event): + self.init_invoke(context, event) + self.int_value_angle = self.target_get_value('angle') + return {'RUNNING_MODAL'} + + def modal(self, context, event, tweak): + self.update_header_text(context) + self.update_prop_value(event, tweak) + return {'RUNNING_MODAL'} + + def exit(self, context, cancel): + context.area.header_text_set(None) + if cancel: + self.target_set_value('angle', self.int_value_angle) + + class AngleGizmoGroup(GizmoGroup, GizmoUtils, Handler): """ShowGizmo """ @@ -81,7 +81,17 @@ class AngleGizmoGroup(GizmoGroup, GizmoUtils, Handler): bl_label = 'AngleGizmoGroup' bl_space_type = 'VIEW_3D' bl_region_type = 'WINDOW' - bl_options = {'3D', 'PERSISTENT', } + bl_options = {'3D', + # 'SCALE', + # 'DEPTH_3D', + # 'SELECT', + 'PERSISTENT', + 'SHOW_MODAL_ALL', + # 'EXCLUDE_MODAL', + # 'TOOL_INIT', # not show + # 'TOOL_FALLBACK_KEYMAP', + # 'VR_REDRAWS' + } @classmethod def poll(cls, context): @@ -107,57 +117,8 @@ class AngleGizmoGroup(GizmoGroup, GizmoUtils, Handler): self.generate_gizmo_mode(add_data) - data_path = 'object.modifiers.active.deform_axis' - set_enum = 'wm.context_set_enum' - - for axis in ('X', 'Y', 'Z'): - # show toggle axis button - gizmo = self.gizmos.new('GIZMO_GT_button_2d') - gizmo.icon = f'EVENT_{axis.upper()}' - gizmo.draw_options = {'BACKDROP', 'HELPLINE'} - ops = gizmo.target_set_operator(set_enum) - ops.data_path = data_path - ops.value = axis - gizmo.color = (0, 0, 0) - gizmo.alpha = 0.3 - gizmo.color_highlight = 1.0, 1.0, 1.0 - gizmo.alpha_highlight = 0.3 - gizmo.use_draw_modal = True - gizmo.use_draw_value = True - gizmo.scale_basis = 0.1 - setattr(self, f'deform_axis_{axis.lower()}', gizmo) - def refresh(self, context): self.angle.target_set_prop('angle', context.object.modifiers.active, 'angle') - - # pro = context.object.SimpleDeformGizmo_PropertyGroup - # self.down_limits.target_set_prop('down_limits', - # pro, - # 'down_limits') - # self.down_limits.target_set_prop('up_limits', - # pro, - # 'up_limits') - # self.up_limits.target_set_prop('down_limits', - # pro, - # 'down_limits') - # self.up_limits.target_set_prop('up_limits', - # pro, - # 'up_limits') - - def draw_prepare(self, context): - ob = context.object - mat = ob.matrix_world - - if 'co' in self.G_SimpleDeformGizmoHandlerDit: - def _mat(f): - co = self.G_SimpleDeformGizmoHandlerDit['co'][0] - co = (co[0] + (max(ob.dimensions) * f), co[1], - co[2] - (min(ob.dimensions) * 0.3)) - return mat @ Vector(co) - - self.deform_axis_x.matrix_basis.translation = _mat(0) - self.deform_axis_y.matrix_basis.translation = _mat(0.3) - self.deform_axis_z.matrix_basis.translation = _mat(0.6) diff --git a/simple_deform_helper/gizmo/bend_axis.py b/simple_deform_helper/gizmo/bend_axis.py index 3d102ec70..7134e0e1b 100644 --- a/simple_deform_helper/gizmo/bend_axis.py +++ b/simple_deform_helper/gizmo/bend_axis.py @@ -40,7 +40,7 @@ class CustomGizmo(Gizmo, GizmoUtils, Handler): return {'RUNNING_MODAL'} -class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, GizmoUtils): +class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoUtils): """绘制切换变型轴的 变换方向 """ diff --git a/simple_deform_helper/gizmo/set_deform_axis.py b/simple_deform_helper/gizmo/set_deform_axis.py new file mode 100644 index 000000000..875ab6745 --- /dev/null +++ b/simple_deform_helper/gizmo/set_deform_axis.py @@ -0,0 +1,53 @@ +from bpy.types import GizmoGroup +from mathutils import Vector + +from utils import GizmoUtils + + +class SetDeformGizmoGroup(GizmoGroup, GizmoUtils): + bl_idname = 'OBJECT_GGT_SetDeformGizmoGroup' + bl_label = 'SetDeformGizmoGroup' + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D', + 'PERSISTENT', + 'SHOW_MODAL_ALL', + } + + @classmethod + def poll(cls, context): + return cls.simple_deform_show_gizmo_poll(context) + + def setup(self, context): + data_path = 'object.modifiers.active.deform_axis' + set_enum = 'wm.context_set_enum' + + for axis in ('X', 'Y', 'Z'): + # show toggle axis button + gizmo = self.gizmos.new('GIZMO_GT_button_2d') + gizmo.icon = f'EVENT_{axis.upper()}' + gizmo.draw_options = {'BACKDROP', 'HELPLINE'} + ops = gizmo.target_set_operator(set_enum) + ops.data_path = data_path + ops.value = axis + gizmo.color = (0, 0, 0) + gizmo.alpha = 0.3 + gizmo.color_highlight = 1.0, 1.0, 1.0 + gizmo.alpha_highlight = 0.3 + gizmo.use_draw_modal = True + gizmo.use_draw_value = True + gizmo.scale_basis = 0.1 + setattr(self, f'deform_axis_{axis.lower()}', gizmo) + + def draw_prepare(self, context): + + if 'co' in self.G_SimpleDeformGizmoHandlerDit: + def _mat(f): + co = self.G_SimpleDeformGizmoHandlerDit['co'][0] + co = (co[0] + (max(self.obj.dimensions) * f), co[1], + co[2] - (min(self.obj.dimensions) * 0.3)) + return self.obj_matrix_world @ Vector(co) + + self.deform_axis_x.matrix_basis.translation = _mat(0) + self.deform_axis_y.matrix_basis.translation = _mat(0.3) + self.deform_axis_z.matrix_basis.translation = _mat(0.6) diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index c7e1eb46e..2f2c11309 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -2,19 +2,15 @@ import math from typing import Callable, Any import bpy +from bpy.types import Gizmo, GizmoGroup from bpy_extras import view3d_utils -from bpy.types import Gizmo -from mathutils import Euler, Vector +from mathutils import Vector from ..draw import Handler from ..utils import GizmoUtils class GizmoProperty(GizmoUtils, Handler): - @property - def is_angle_mode(self): - return self.ctrl_mode == 'angle' - @property def is_up_limits_mode(self): return self.ctrl_mode == 'up_limits' @@ -24,85 +20,17 @@ class GizmoProperty(GizmoUtils, Handler): return self.ctrl_mode == 'down_limits' -class UpDownLimitsGizmo(GizmoProperty, Gizmo): - """显示轴向切换拖动点Gizmo(两个点) - """ - bl_idname = 'UpDownLimitsGizmo' - - bl_target_properties = ( - {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, - {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, - ) - - __slots__ = ( - 'mod', - 'up', - 'down', - 'up_', - 'down_', - 'draw_type', - 'mouse_dpi', - 'ctrl_mode', - 'empty_object', - 'init_mouse_y', - 'init_mouse_x', - 'custom_shape', - 'int_value_angle', - 'value_deform_axis', - 'int_value_up_limits', - 'int_value_down_limits', - 'rotate_follow_modifier', - ) - - def update_draw_limits_bound_box(self, data, mod, axis, mat, up_, down_): - top, bottom, left, right, front, back = data - if mod.origin: - vector_axis = self.get_vector_axis(mod) - origin_mat = mod.origin.matrix_world.to_3x3() - axis_ = origin_mat @ vector_axis - point_lit = [[top, bottom], [left, right], [front, back]] - for f in range(point_lit.__len__()): - i = point_lit[f][0] - j = point_lit[f][1] - angle = self.point_to_angle(i, j, f, axis_) - if abs(angle - 180) < 0.00001: - point_lit[f][1], point_lit[f][0] = up_, down_ - elif abs(angle) < 0.00001: - point_lit[f][0], point_lit[f][1] = up_, down_ - [[top, bottom], [left, right], [front, back]] = point_lit - else: - top, bottom, left, right, front, back = self.get_up_down_return_list( - mod, axis, up_, down_, data) - data = top, bottom, left, right, front, back - (top, bottom, left, right, front, - back) = self.matrix_calculation(mat.inverted(), data) - self.G_SimpleDeformGizmoHandlerDit['draw_limits_bound_box'] = ( - mat, ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],))) - - def update_matrix_basis_translation(self, co, mat, up_, down_): - if self.is_angle_mode: - self.matrix_basis.translation = mat @ Vector((co[1])) - elif self.is_up_limits_mode: - self.matrix_basis.translation = up_ - elif self.is_down_limits_mode: - self.matrix_basis.translation = down_ +class GizmoUpdate(GizmoProperty): def update_gizmo_matrix(self, context): - ob = context.object - mat = ob.matrix_world - mod = context.object.modifiers.active - axis = mod.deform_axis - if mod.origin: - self.matrix_basis = mod.origin.matrix_world.normalized() - else: - self.matrix_basis = ob.matrix_world.normalized() + self._update_matrix_basis_to_obj() co = self.generate_co_data() # calculation limits position top, bottom, left, right, front, back = self.each_face_pos(mat) (up, down), (up_, down_) = self.get_limits_pos( mod, (top, bottom, left, right, front, back)) - self.update_matrix_basis_translation(co, mat, up_, down_) + self._update_matrix_basis_translation(co, mat, up_, down_) self.up = up self.down = down @@ -113,42 +41,18 @@ class UpDownLimitsGizmo(GizmoProperty, Gizmo): data = top, bottom, left, right, front, back self.update_draw_limits_bound_box(data, mod, axis, mat, up_, down_) - def setup(self): - self.generate_co_data() - self.draw_type = 'None_GizmoGroup_' - self.ctrl_mode = 'up_limits' # up_limits , down_limits - self.mouse_dpi = 10 - self.rotate_follow_modifier = True - self.add_handler() + def _update_matrix_basis_to_obj(self): + origin = self.modifier.origin + if origin: + self.matrix_basis = origin.matrix_world.normalized() + else: + self.matrix_basis = self.obj_matrix_world.normalized() - def invoke(self, context, event): - self.init_invoke(context, event) - mod = context.object.modifiers.active - limits = mod.limits - up_limits = limits[1] - down_limits = limits[0] - - if 'up_limits' == self.ctrl_mode: - self.int_value_up_limits = up_limits - self.target_set_value('up_limits', self.int_value_up_limits) - elif 'down_limits' == self.ctrl_mode: - self.int_value_down_limits = down_limits - self.target_set_value('down_limits', self.int_value_down_limits) - return {'RUNNING_MODAL'} - - def exit(self, context, cancel): - context.area.header_text_set(None) - - if cancel: - if 'angle' == self.ctrl_mode: - self.target_set_value('angle', self.int_value_angle) - elif 'deform_axis' == self.ctrl_mode: - self.target_set_value('deform_axis', self.value_deform_axis) - elif 'up_limits' == self.ctrl_mode: - self.target_set_value('up_limits', self.int_value_up_limits) - elif 'down_limits' == self.ctrl_mode: - self.target_set_value( - 'down_limits', self.int_value_down_limits) + def _update_matrix_basis_translation(self, co, mat, up_, down_): + if self.is_up_limits_mode: + self.matrix_basis.translation = up_ + elif self.is_down_limits_mode: + self.matrix_basis.translation = down_ def delta_update(self, context, event, delta): if ('draw_line' in self.G_SimpleDeformGizmoHandlerDit) and (self.ctrl_mode in ('up_limits', 'down_limits')): @@ -231,7 +135,7 @@ class UpDownLimitsGizmo(GizmoProperty, Gizmo): text = translate(mode) + ' ' - if self.is_use_angle_value and self.is_angle_mode: + if self.modifier_is_use_angle_value and self.is_angle_mode: text += t_() elif self.is_up_limits_mode: text += t_('Upper limit', up_limits) @@ -261,35 +165,148 @@ class UpDownLimitsGizmo(GizmoProperty, Gizmo): return {'RUNNING_MODAL'} - def modal(self, context, event, tweak): - self.update_bound_box(context.object) - ob = context.object - mod = ob.modifiers.active +class UpDownLimitsGizmo(Gizmo, GizmoUpdate): + """显示轴向切换拖动点Gizmo(两个点) + """ + bl_idname = 'UpDownLimitsGizmo' + + bl_target_properties = ( + {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, + {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, + ) + + __slots__ = ( + 'mod', + 'up_limits', + 'down_limits', + 'draw_type', + 'mouse_dpi', + 'ctrl_mode', + 'empty_object', + 'init_mouse_y', + 'init_mouse_x', + 'custom_shape', + 'value_deform_axis', + 'int_value_up_limits', + 'int_value_down_limits', + ) + + def setup(self): + self.generate_co_data() + self.draw_type = 'None_GizmoGroup_' + self.ctrl_mode = 'up_limits' # up_limits , down_limits + self.mouse_dpi = 10 + + def invoke(self, context, event): + self.init_invoke(context, event) + mod = context.object.modifiers.active limits = mod.limits up_limits = limits[1] down_limits = limits[0] - origin = self.get_origin_property_group(mod, ob) - origin_mode = origin.origin_mode - middle = origin_mode in ('LIMITS_MIDDLE', 'MIDDLE') - limit_scope = self.pref.modifiers_limits_tolerance - max_value = up_limits - limit_scope - min_value = down_limits + limit_scope - difference_value = up_limits - down_limits - delta = self.get_delta(event, tweak) - delta = self.delta_update(context, event, delta) + if 'up_limits' == self.ctrl_mode: + self.int_value_up_limits = up_limits + self.target_set_value('up_limits', self.int_value_up_limits) + elif 'down_limits' == self.ctrl_mode: + self.int_value_down_limits = down_limits + self.target_set_value('down_limits', self.int_value_down_limits) + return {'RUNNING_MODAL'} - if origin_mode != 'NOT' and ('draw_line' in self.G_SimpleDeformGizmoHandlerDit): - self.empty_object, _ = self.new_empty(ob, mod) - self.G_SimpleDeformGizmoHandlerDit['empty_object'] = self.empty_object - data = up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode - self.set_prop_value(data) - self.update_gizmo_matrix(context) - self.update_empty_matrix() - self.update_bound_box(context.object) + def exit(self, context, cancel): + context.area.header_text_set(None) - self.update_header_text(context, mod, origin, up_limits, down_limits) - self.add_handler() + if cancel: + if 'deform_axis' == self.ctrl_mode: + self.target_set_value('deform_axis', self.value_deform_axis) + elif 'up_limits' == self.ctrl_mode: + self.target_set_value('up_limits', self.int_value_up_limits) + elif 'down_limits' == self.ctrl_mode: + self.target_set_value( + 'down_limits', self.int_value_down_limits) - return self.event_ops(event, ob, origin) + def modal(self, context, event, tweak): + # self.update_bound_box(context.object) + # + # ob = context.object + # mod = ob.modifiers.active + # limits = mod.limits + # up_limits = limits[1] + # down_limits = limits[0] + # origin = self.get_origin_property_group(mod, ob) + # origin_mode = origin.origin_mode + # middle = origin_mode in ('LIMITS_MIDDLE', 'MIDDLE') + # limit_scope = self.pref.modifiers_limits_tolerance + # max_value = up_limits - limit_scope + # min_value = down_limits + limit_scope + # difference_value = up_limits - down_limits + # + # delta = self.get_delta(event, tweak) + # delta = self.delta_update(context, event, delta) + # + # if origin_mode != 'NOT' and ('draw_line' in self.G_SimpleDeformGizmoHandlerDit): + # self.empty_object, _ = self.new_empty(ob, mod) + # self.G_SimpleDeformGizmoHandlerDit['empty_object'] = self.empty_object + # data = up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode + # self.set_prop_value(data) + # self.update_gizmo_matrix(context) + # self.update_empty_matrix() + # self.update_bound_box(context.object) + # self.update_header_text(context, mod, origin, up_limits, down_limits) + # self.add_handler() + + # return self.event_ops(event, ob, origin) + return {'RUNNING_MODAL'} + + +class UpDownLimitsGizmoGroup(GizmoGroup, GizmoUtils, Handler): + bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup' + + @classmethod + def poll(cls, context): + return cls.simple_deform_show_gizmo_poll(context) + + def setup(self, context): + sd_name = UpDownLimitsGizmo.bl_idname + + add_data = ( + ('up_limits', + sd_name, + {'ctrl_mode': 'up_limits', + 'draw_type': 'Sphere_GizmoGroup_', + 'mouse_dpi': 1000, + 'color': (1.0, 0, 0), + 'alpha': 0.5, + 'color_highlight': (1.0, 1.0, 1.0), + 'alpha_highlight': 0.3, + 'use_draw_modal': True, + 'scale_basis': 0.1, + 'use_draw_value': True, }), + ('down_limits', + sd_name, + {'ctrl_mode': 'down_limits', + 'draw_type': 'Sphere_GizmoGroup_', + 'mouse_dpi': 1000, + 'color': (0, 1.0, 0), + 'alpha': 0.5, + 'color_highlight': (1.0, 1.0, 1.0), + 'alpha_highlight': 0.3, + 'use_draw_modal': True, + 'scale_basis': 0.1, + 'use_draw_value': True, }), + ) + + def refresh(self, context): + pro = context.object.SimpleDeformGizmo_PropertyGroup + self.down_limits.target_set_prop('down_limits', + pro, + 'down_limits') + self.down_limits.target_set_prop('up_limits', + pro, + 'up_limits') + self.up_limits.target_set_prop('down_limits', + pro, + 'down_limits') + self.up_limits.target_set_prop('up_limits', + pro, + 'up_limits') diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index 10f65a7c9..aea0e8e3a 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -128,7 +128,7 @@ class PublicPoll(PublicClass): active modifier deform_method =='BEND' """ simple = cls.simple_deform_public_poll(context) - is_bend = (context.object.modifiers.active.deform_method == 'BEND') + is_bend = simple and (context.object.modifiers.active.deform_method == 'BEND') return simple and is_bend @classmethod @@ -266,74 +266,7 @@ class PublicUtils(PublicPoll): return cls.translate_text(mode) + ':{}'.format(value) -class GizmoProperty: - - @property - def obj(self): - return bpy.context.object - - @property - def modifier(self): - obj = self.obj - if not obj: - return - return obj.modifiers.active - - @property - def modifier_deform_axis(self): - mod = self.modifier - if mod: - return mod.deform_axis - - @property - def modifier_angle(self): - mod = self.modifier - if mod: - return mod.angle - - @property - def active_modifier_is_simple_deform(self): - return self.modifier and self.modifier.type == 'SIMPLE_DEFORM' - - @property - def is_use_angle_value(self): - if self.active_modifier_is_simple_deform: - return self.modifier.deform_method in ('TWIST', 'BEND') - - -class GizmoClassMethod(GizmoProperty, PublicUtils): - @classmethod - def each_face_pos(cls, mat: 'Matrix' = None): - if mat is None: - mat = Matrix() - return cls.co_to_direction(mat, cls.G_SimpleDeformGizmoHandlerDit['co']) - - @classmethod - def update_matrix(cls, mod, ob): - if mod.deform_method == 'BEND': - cls.new_empty(ob, mod) - if mod.origin: - empty_object = mod.origin - modifiers_co = cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'] - for index, mod_name in enumerate(modifiers_co): - co_items = list(modifiers_co.items()) - if mod.name == mod_name: - data = co_items[index - 1][1] if ( - index or (index != 1)) else modifiers_co['co'] - (up, down), (up_, down_) = cls.get_limits_pos( - mod, cls.co_to_direction(ob.matrix_world.copy(), data)) - origin_mode = cls.get_origin_property_group( - mod, ob).origin_mode - cls.set_empty_obj_matrix( - origin_mode, empty_object, up_, down_, up, down) - - @classmethod - def update_empty_matrix(cls): - ob = bpy.context.object - for mod in ob.modifiers: - if mod.type == 'SIMPLE_DEFORM': - cls.update_matrix(mod, ob) - +class GizmoClassMethod(PublicUtils): @classmethod def get_origin_property_group(cls, mod, ob): if mod.origin: @@ -481,6 +414,44 @@ class GizmoClassMethod(GizmoProperty, PublicUtils): axis == 'Z') else vector return vector_axis + @classmethod + def generate_co_data(cls): + handler_dit = cls.G_SimpleDeformGizmoHandlerDit + + if 'co' not in handler_dit: + handler_dit['co'] = cls.get_mesh_max_min_co( + bpy.context.object) + return handler_dit['co'] + + +class GizmoUpdate(GizmoClassMethod): + + @classmethod + def update_matrix(cls, mod, ob): + if mod.deform_method == 'BEND': + cls.new_empty(ob, mod) + if mod.origin: + empty_object = mod.origin + modifiers_co = cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'] + for index, mod_name in enumerate(modifiers_co): + co_items = list(modifiers_co.items()) + if mod.name == mod_name: + data = co_items[index - 1][1] if ( + index or (index != 1)) else modifiers_co['co'] + (up, down), (up_, down_) = cls.get_limits_pos( + mod, cls.co_to_direction(ob.matrix_world.copy(), data)) + origin_mode = cls.get_origin_property_group( + mod, ob).origin_mode + cls.set_empty_obj_matrix( + origin_mode, empty_object, up_, down_, up, down) + + @classmethod + def update_empty_matrix(cls): + ob = bpy.context.object + for mod in ob.modifiers: + if mod.type == 'SIMPLE_DEFORM': + cls.update_matrix(mod, ob) + @classmethod def update_bound_box(cls, obj: 'bpy.types.Object'): context = bpy.context @@ -574,15 +545,6 @@ class GizmoClassMethod(GizmoProperty, PublicUtils): cls.G_SimpleDeformGizmoHandlerDit['draw'] = (ver, indices, matrix, modifiers, limits) - @classmethod - def generate_co_data(cls): - handler_dit = cls.G_SimpleDeformGizmoHandlerDit - - if 'co' not in handler_dit: - handler_dit['co'] = cls.get_mesh_max_min_co( - bpy.context.object) - return handler_dit['co'] - @classmethod def update_co_data(cls, ob, mod): handler_dit = cls.G_SimpleDeformGizmoHandlerDit @@ -595,8 +557,132 @@ class GizmoClassMethod(GizmoProperty, PublicUtils): cls.G_SimpleDeformGizmoHandlerDit['co'] = co_items[index - 1][1] if (index or (index != 1)) else \ modifiers_co['co'] + def update_draw_limits_bound_box(self, data, mod, axis, mat, up_, down_): + top, bottom, left, right, front, back = data + if mod.origin: + vector_axis = self.get_vector_axis(mod) + origin_mat = mod.origin.matrix_world.to_3x3() + axis_ = origin_mat @ vector_axis + point_lit = [[top, bottom], [left, right], [front, back]] + for f in range(point_lit.__len__()): + i = point_lit[f][0] + j = point_lit[f][1] + angle = self.point_to_angle(i, j, f, axis_) + if abs(angle - 180) < 0.00001: + point_lit[f][1], point_lit[f][0] = up_, down_ + elif abs(angle) < 0.00001: + point_lit[f][0], point_lit[f][1] = up_, down_ + [[top, bottom], [left, right], [front, back]] = point_lit + else: + top, bottom, left, right, front, back = self.get_up_down_return_list( + mod, axis, up_, down_, data) + data = top, bottom, left, right, front, back + (top, bottom, left, right, front, + back) = self.matrix_calculation(mat.inverted(), data) + self.G_SimpleDeformGizmoHandlerDit['draw_limits_bound_box'] = ( + mat, ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],))) -class GizmoUtils(GizmoClassMethod): + def update_bound_box_wireframe(self): + ... + + def update_up_down_limits_wireframe(self): + ... + + def update_deform_wireframe(self): + ... + + +class PublicProperty(GizmoUpdate): + + @classmethod + def each_face_pos(cls, mat: 'Matrix' = None): + if mat is None: + mat = Matrix() + return cls.co_to_direction(mat, cls.G_SimpleDeformGizmoHandlerDit['co']) + + @property + def obj(self): + return bpy.context.object + + @property + def obj_matrix_world(self): + if self.obj: + return self.obj.matrix_world + + @property + def modifier(self): + obj = self.obj + if not obj: + return + return obj.modifiers.active + + @property + def modifier_deform_axis(self): + mod = self.modifier + if mod: + return mod.deform_axis + + @property + def modifier_angle(self): + mod = self.modifier + if mod: + return mod.angle + + @property + def active_modifier_is_simple_deform(self): + return self.modifier and self.modifier.type == 'SIMPLE_DEFORM' + + @property + def modifier_is_use_angle_value(self): + if self.active_modifier_is_simple_deform: + return self.modifier.deform_method in ('TWIST', 'BEND') + + @property + def point_up(self): + ... + + @property + def point_down(self): + ... + + @property + def point_limits_up(self): + ... + + @property + def point_limits_down(self): + ... + + @property + def point_top(self): + ... + + @property + def point_bottom(self): + ... + + @property + def point_left(self): + ... + + @property + def point_right(self): + ... + + @property + def point_front(self): + ... + + @property + def point_back(self): + ... + + @classmethod + def clear_cache(cls): + ... + + +class GizmoUtils(PublicProperty): custom_shape: dict init_mouse_y: float init_mouse_x: float @@ -654,20 +740,19 @@ class GizmoUtils(GizmoClassMethod): return delta def get_snap(self, delta, tweak): - # ctrl SNAP - # shift PRECISE + # TODO ctrl SNAP + # TODO shift PRECISE is_snap = 'SNAP' in tweak is_precise = 'PRECISE' in tweak if is_snap and is_precise: delta = round(delta) - # delta /= self.mouse_dpi elif is_snap: delta //= 5 delta *= 5 elif is_precise: + delta /= self.mouse_dpi delta //= 0.01 delta *= 0.01 - print('tweak', delta, tweak) return delta def update_gizmo_matrix(self): -- 2.30.2 From 5beacd168afc380eb5a68652c5fa263e974e8f19 Mon Sep 17 00:00:00 2001 From: EMM <3209970865@qq.com> Date: Thu, 30 Mar 2023 19:02:49 +0800 Subject: [PATCH 04/12] draw bound and line --- simple_deform_helper/draw.py | 166 ++--- simple_deform_helper/gizmo/__init__.py | 5 +- .../gizmo/angle_and_factor.py | 36 +- simple_deform_helper/gizmo/bend_axis.py | 23 +- simple_deform_helper/gizmo/set_deform_axis.py | 14 +- .../gizmo/up_down_limits_point.py | 282 ++++---- simple_deform_helper/operators.py | 2 - simple_deform_helper/preferences.py | 2 +- simple_deform_helper/update.py | 14 +- simple_deform_helper/utils.py | 608 +++++++++--------- 10 files changed, 554 insertions(+), 598 deletions(-) diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index e7970055b..72dde2f8d 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -11,16 +11,16 @@ from .utils import GizmoUtils class Handler: @classmethod def add_handler(cls): - if 'handler' not in cls.G_SimpleDeformGizmoHandlerDit: - cls.G_SimpleDeformGizmoHandlerDit['handler'] = bpy.types.SpaceView3D.draw_handler_add( - Draw3D.draw_bound_box, (), 'WINDOW', 'POST_VIEW') + if 'handler' not in cls.G_GizmoData: + cls.G_GizmoData['handler'] = bpy.types.SpaceView3D.draw_handler_add( + Draw3D().draw, (), 'WINDOW', 'POST_VIEW') @classmethod def del_handler_text(cls): - if 'handler_text' in cls.G_SimpleDeformGizmoHandlerDit: + if 'handler_text' in cls.G_GizmoData: bpy.types.SpaceView3D.draw_handler_remove( - cls.G_SimpleDeformGizmoHandlerDit['handler_text'], 'WINDOW') - cls.G_SimpleDeformGizmoHandlerDit.pop('handler_text') + cls.G_GizmoData['handler_text'], 'WINDOW') + cls.G_GizmoData.pop('handler_text') @classmethod def del_handler(cls): @@ -33,54 +33,13 @@ class Handler: cls.del_handler_text() - if 'handler' in cls.G_SimpleDeformGizmoHandlerDit: + if 'handler' in cls.G_GizmoData: bpy.types.SpaceView3D.draw_handler_remove( - cls.G_SimpleDeformGizmoHandlerDit['handler'], 'WINDOW') - cls.G_SimpleDeformGizmoHandlerDit.clear() + cls.G_GizmoData['handler'], 'WINDOW') + cls.G_GizmoData.clear() -class Draw3D(GizmoUtils): - - @classmethod - def draw_bound_box(cls): - gpu.state.blend_set('ALPHA') - gpu.state.line_width_set(1) - - gpu.state.blend_set('ALPHA') - gpu.state.depth_test_set('ALWAYS') - - context = bpy.context - if cls.simple_deform_public_poll(context): - cls.is_draw_box(context) - else: - Handler.del_handler() - - @classmethod - def is_draw_box(cls, context): - obj = context.object # 活动物体 - matrix = obj.matrix_world # 活动物体矩阵 - modifier = context.object.modifiers.active # 活动修改器 - - pref = cls.pref_() - simple_poll = cls.simple_deform_public_poll(context) - bend = modifier and (modifier.deform_method == 'BEND') - display_switch_axis = not pref.display_bend_axis_switch_gizmo - - cls.draw_scale_text(obj) - cls.update_co_data(obj, modifier) - - co_data = cls.generate_co_data() - - if simple_poll and ((not bend) or display_switch_axis): - # draw bound box - cls.draw_box(co_data, matrix) - # cls.draw_deform_mesh(obj, context) - cls.draw_limits_line() - cls.draw_limits_bound_box() - elif simple_poll and (bend and not display_switch_axis): - cls.draw_box(co_data, matrix) - cls.new_empty(obj, modifier) - +class DrawPublic: @classmethod def draw_3d_shader(cls, pos, indices, color=None, *, shader_name='3D_UNIFORM_COLOR', draw_type='LINES'): shader = gpu.shader.from_builtin(shader_name) @@ -96,6 +55,8 @@ class Draw3D(GizmoUtils): batch.draw(shader) + +class DrawText: font_info = { 'font_id': 0, 'handler': None, @@ -115,7 +76,7 @@ class Draw3D(GizmoUtils): f' which will cause the deformation of the simple deformation modifier.' f' Please apply the scaling before deformation') if obj.scale == Vector((1, 1, 1)): - Handler.del_handler_text() + cls.del_handler_text() @classmethod def draw_text(cls, x, y, text='Hello Word', font_id=0, size=10, *, color=(0.5, 0.5, 0.5, 1), dpi=72, column=0): @@ -124,56 +85,62 @@ class Draw3D(GizmoUtils): blf.draw(font_id, text) blf.color(font_id, *color) - @classmethod - def draw_box(cls, data, mat): - pref = cls.pref_() - coords = cls.matrix_calculation(mat, - cls.data_to_calculation(data)) - cls.draw_3d_shader(coords, cls.G_INDICES, pref.bound_box_color) - @classmethod - def data_to_calculation(cls, data): - ((min_x, min_y, min_z), (max_x, max_y, max_z)) = data - return ( - (max_x, min_y, min_z), - (min_x, min_y, min_z), - (max_x, max_y, min_z), - (min_x, max_y, min_z), - (max_x, min_y, max_z), - (min_x, min_y, max_z), - (max_x, max_y, max_z), - (min_x, max_y, max_z)) +class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): - @classmethod - def draw_limits_bound_box(cls): + def draw(self): + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(1) - pref = cls.pref_() - handler_dit = cls.G_SimpleDeformGizmoHandlerDit - if 'draw_limits_bound_box' in handler_dit: - # draw limits_bound_box - mat, data = handler_dit['draw_limits_bound_box'] - coords = cls.matrix_calculation(mat, cls.data_to_calculation(data)) - cls.draw_3d_shader(coords, - cls.G_INDICES, - pref.limits_bound_box_color) + gpu.state.blend_set('ALPHA') + gpu.state.depth_test_set('ALWAYS') - @classmethod - def draw_limits_line(cls): - handler_dit = cls.G_SimpleDeformGizmoHandlerDit - if 'draw_line' in handler_dit: - line_pos, limits_pos, = handler_dit['draw_line'] - # draw limits line - cls.draw_3d_shader(limits_pos, ((1, 0),), (1, 1, 0, 0.5)) - # draw line - cls.draw_3d_shader(line_pos, ((1, 0),), (1, 1, 0, 0.3)) - # draw pos - cls.draw_3d_shader([line_pos[1]], (), (0, 1, 0, 0.5), - shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') + context = bpy.context + if self.simple_deform_public_poll(context): + self.draw_3d(context) + + def draw_3d(self, context): + obj = context.object # 活动物体 + + self.draw_scale_text(obj) + if not self.modifier_origin_angle_is_available: + self.draw_bound_box() + ... + if self.simple_deform_show_gizmo_poll(context): + # draw bound box + self.draw_bound_box() + # cls.draw_deform_mesh(obj, context) + self.draw_limits_line() + self.draw_limits_bound_box() + elif self.simple_deform_show_bend_axis_witch_poll(context): + self.draw_bound_box() + # self.new_empty(obj, modifier) + + def draw_bound_box(self): + coords = self.matrix_calculation(self.obj_matrix_world, + self.tow_co_to_coordinate(self.get_bound_co_data())) + self.draw_3d_shader(coords, self.G_INDICES, self.pref.bound_box_color) + + def draw_limits_bound_box(self): + self.draw_3d_shader(self.modifier_limits_bound_box, + self.G_INDICES, + self.pref.limits_bound_box_color, + ) + + def draw_limits_line(self): + line_pos, limits_pos, = self.modifier_limits_point + # draw limits line + self.draw_3d_shader(limits_pos, ((1, 0),), (1, 1, 0, 0.5)) + # draw line + self.draw_3d_shader(line_pos, ((1, 0),), (1, 1, 0, 0.3)) + # draw pos + self.draw_3d_shader([line_pos[1]], (), (0, 1, 0, 0.5), + shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') @classmethod def draw_deform_mesh(cls, ob, context): pref = cls.pref_() - handler_dit = cls.G_SimpleDeformGizmoHandlerDit + handler_dit = cls.G_GizmoData active = context.object.modifiers.active # draw deform mesh if 'draw' in handler_dit: @@ -183,8 +150,9 @@ class Draw3D(GizmoUtils): cls.draw_3d_shader( pos, indices, pref.deform_wireframe_color) - @classmethod - def draw_scale_text(cls, ob): - if (ob.scale != Vector((1, 1, 1))) and ('handler_text' not in cls.G_SimpleDeformGizmoHandlerDit): - cls.G_SimpleDeformGizmoHandlerDit['handler_text'] = bpy.types.SpaceView3D.draw_handler_add( - cls.draw_str, (), 'WINDOW', 'POST_PIXEL') + def draw_scale_text(self, ob): + ob = self.obj + dit = self.G_GizmoData + if (ob.scale != Vector((1, 1, 1))) and ('handler_text' not in dit): + dit['handler_text'] = bpy.types.SpaceView3D.draw_handler_add( + self.draw_str, (), 'WINDOW', 'POST_PIXEL') diff --git a/simple_deform_helper/gizmo/__init__.py b/simple_deform_helper/gizmo/__init__.py index 8d136e80a..756b41aec 100644 --- a/simple_deform_helper/gizmo/__init__.py +++ b/simple_deform_helper/gizmo/__init__.py @@ -4,7 +4,7 @@ from .angle_and_factor import AngleGizmoGroup, AngleGizmo from .bend_axis import BendAxiSwitchGizmoGroup, CustomGizmo from .set_deform_axis import SetDeformGizmoGroup from .up_down_limits_point import UpDownLimitsGizmo, UpDownLimitsGizmoGroup -from ..draw import Handler +from ..draw import Draw3D class_list = ( UpDownLimitsGizmo, @@ -23,9 +23,10 @@ register_class, unregister_class = bpy.utils.register_classes_factory(class_list def register(): + Draw3D.add_handler() register_class() def unregister(): - Handler.del_handler() + Draw3D.del_handler() unregister_class() diff --git a/simple_deform_helper/gizmo/angle_and_factor.py b/simple_deform_helper/gizmo/angle_and_factor.py index b8a3f9af3..5aecb93ce 100644 --- a/simple_deform_helper/gizmo/angle_and_factor.py +++ b/simple_deform_helper/gizmo/angle_and_factor.py @@ -5,10 +5,8 @@ from bpy.types import Gizmo from bpy.types import ( GizmoGroup, ) -from mathutils import Vector -from ..draw import Handler -from ..utils import GizmoUtils +from ..utils import GizmoUtils, GizmoGroupUtils class AngleUpdate(GizmoUtils): @@ -26,10 +24,16 @@ class AngleUpdate(GizmoUtils): def update_gizmo_matrix(self, context): matrix = context.object.matrix_world - self.matrix_basis.translation = matrix @ Vector((self.generate_co_data()[1])) + point = self.get_bound_co_data()[1] + self.matrix_basis = self.obj_matrix_world + self.matrix_basis.translation = matrix @ point def update_header_text(self, context): - text = self.translate_header_text('Angle', round(math.degrees(self.modifier_angle), 3)) + if self.modifier_origin_angle_is_available: + value = round(math.degrees(self.modifier_angle), 3) + text = self.translate_header_text('Angle', value) + else: + text = self.translate_header_text('Coefficient', self.modifier.factor) context.area.header_text_set(text) @@ -46,8 +50,8 @@ class AngleGizmo(Gizmo, AngleUpdate): 'draw_type', 'mouse_dpi', 'empty_object', - 'init_mouse_y', - 'init_mouse_x', + 'init_mouse_region_y', + 'init_mouse_region_x', 'custom_shape', 'int_value_angle', ) @@ -64,6 +68,8 @@ class AngleGizmo(Gizmo, AngleUpdate): return {'RUNNING_MODAL'} def modal(self, context, event, tweak): + self.clear_cache() + self.update_header_text(context) self.update_prop_value(event, tweak) return {'RUNNING_MODAL'} @@ -74,24 +80,11 @@ class AngleGizmo(Gizmo, AngleUpdate): self.target_set_value('angle', self.int_value_angle) -class AngleGizmoGroup(GizmoGroup, GizmoUtils, Handler): +class AngleGizmoGroup(GizmoGroup, GizmoGroupUtils): """ShowGizmo """ bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup' bl_label = 'AngleGizmoGroup' - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = {'3D', - # 'SCALE', - # 'DEPTH_3D', - # 'SELECT', - 'PERSISTENT', - 'SHOW_MODAL_ALL', - # 'EXCLUDE_MODAL', - # 'TOOL_INIT', # not show - # 'TOOL_FALLBACK_KEYMAP', - # 'VR_REDRAWS' - } @classmethod def poll(cls, context): @@ -118,7 +111,6 @@ class AngleGizmoGroup(GizmoGroup, GizmoUtils, Handler): self.generate_gizmo_mode(add_data) def refresh(self, context): - self.angle.target_set_prop('angle', context.object.modifiers.active, 'angle') diff --git a/simple_deform_helper/gizmo/bend_axis.py b/simple_deform_helper/gizmo/bend_axis.py index 7134e0e1b..0c91505fd 100644 --- a/simple_deform_helper/gizmo/bend_axis.py +++ b/simple_deform_helper/gizmo/bend_axis.py @@ -4,11 +4,10 @@ from bpy.types import GizmoGroup from bpy_types import Gizmo from mathutils import Euler, Vector -from ..draw import Handler -from ..utils import GizmoUtils +from ..utils import GizmoUtils, GizmoGroupUtils -class CustomGizmo(Gizmo, GizmoUtils, Handler): +class CustomGizmo(Gizmo, GizmoUtils): """绘制自定义Gizmo""" bl_idname = '_Custom_Gizmo' draw_type: str @@ -18,10 +17,9 @@ class CustomGizmo(Gizmo, GizmoUtils, Handler): self.draw_type = 'None_GizmoGroup_' if not hasattr(self, 'custom_shape'): self.custom_shape = {} - for i in self.G_GizmoCustomShapeDict: + for i in self.G_CustomShape: self.custom_shape[i] = self.new_custom_shape( - 'TRIS', self.G_GizmoCustomShapeDict[i]) - self.add_handler() + 'TRIS', self.G_CustomShape[i]) def draw(self, context): self.draw_custom_shape(self.custom_shape[self.draw_type]) @@ -34,26 +32,17 @@ class CustomGizmo(Gizmo, GizmoUtils, Handler): return {'RUNNING_MODAL'} def modal(self, context, event, tweak): - self.add_handler() - self.update_bound_box(context.object) self.update_empty_matrix() return {'RUNNING_MODAL'} -class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoUtils): +class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils): """绘制切换变型轴的 变换方向 """ bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo' bl_label = 'SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo' - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = { - '3D', - 'PERSISTENT', - } - @classmethod def poll(cls, context): return cls.simple_deform_show_bend_axis_witch_poll(context) @@ -104,7 +93,7 @@ class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoUtils): def draw_prepare(self, context): ob = context.object mat = ob.matrix_world - top, bottom, left, right, front, back = self.each_face_pos(mat) + top, bottom, left, right, front, back = self.each_face_pos rad = math.radians for_list = ( diff --git a/simple_deform_helper/gizmo/set_deform_axis.py b/simple_deform_helper/gizmo/set_deform_axis.py index 875ab6745..fc780b8d2 100644 --- a/simple_deform_helper/gizmo/set_deform_axis.py +++ b/simple_deform_helper/gizmo/set_deform_axis.py @@ -1,18 +1,12 @@ from bpy.types import GizmoGroup from mathutils import Vector -from utils import GizmoUtils +from ..utils import GizmoGroupUtils -class SetDeformGizmoGroup(GizmoGroup, GizmoUtils): +class SetDeformGizmoGroup(GizmoGroup, GizmoGroupUtils): bl_idname = 'OBJECT_GGT_SetDeformGizmoGroup' bl_label = 'SetDeformGizmoGroup' - bl_space_type = 'VIEW_3D' - bl_region_type = 'WINDOW' - bl_options = {'3D', - 'PERSISTENT', - 'SHOW_MODAL_ALL', - } @classmethod def poll(cls, context): @@ -41,9 +35,9 @@ class SetDeformGizmoGroup(GizmoGroup, GizmoUtils): def draw_prepare(self, context): - if 'co' in self.G_SimpleDeformGizmoHandlerDit: + if 'co' in self.G_GizmoData: def _mat(f): - co = self.G_SimpleDeformGizmoHandlerDit['co'][0] + co = self.G_GizmoData['co'][0] co = (co[0] + (max(self.obj.dimensions) * f), co[1], co[2] - (min(self.obj.dimensions) * 0.3)) return self.obj_matrix_world @ Vector(co) diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index 2f2c11309..0c3b0e418 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -6,11 +6,14 @@ from bpy.types import Gizmo, GizmoGroup from bpy_extras import view3d_utils from mathutils import Vector -from ..draw import Handler -from ..utils import GizmoUtils +from ..utils import GizmoUtils, GizmoGroupUtils -class GizmoProperty(GizmoUtils, Handler): +class GizmoProperty(GizmoUtils): + ctrl_mode: str + int_value_up_limits: int + int_value_down_limits: int + @property def is_up_limits_mode(self): return self.ctrl_mode == 'up_limits' @@ -19,130 +22,145 @@ class GizmoProperty(GizmoUtils, Handler): def is_down_limits_mode(self): return self.ctrl_mode == 'down_limits' + @property + def origin_mode(self): + return self.SimpleDeformGizmo_origin_property_group.origin_mode + + @property + def is_middle_mode(self): + return self.origin_mode in ('LIMITS_MIDDLE', 'MIDDLE') + + @property + def limit_scope(self): + return self.pref.modifiers_limits_tolerance + + @property + def difference_value(self): + return self.modifier_up_limits - self.modifier_down_limits + + @property + def middle_value(self): + return (self.modifier_up_limits + self.modifier_down_limits) / 2 + + @property + def limits_min_value(self): + return self.modifier_up_limits + self.limit_scope + + @property + def limits_max_value(self): + return self.modifier_up_limits - self.limit_scope + + # ----get func + + def get_up_limits_value(self, event): + delta = self.get_delta(event) + mid = self.middle_value + self.limit_scope + min_value = mid if self.is_middle_mode else self.limits_min_value + return self.value_limit(delta, min_value=min_value) + + def get_down_limits_value(self, event): + delta = self.get_delta(event) + mid = self.middle_value - self.limit_scope + max_value = mid if self.is_middle_mode else self.limits_max_value + return self.value_limit(delta, max_value=max_value) + class GizmoUpdate(GizmoProperty): def update_gizmo_matrix(self, context): - self._update_matrix_basis_to_obj() + self.align_orientation_to_user_perspective(context) + self.align_point_to_limits_point() - co = self.generate_co_data() - # calculation limits position - top, bottom, left, right, front, back = self.each_face_pos(mat) - (up, down), (up_, down_) = self.get_limits_pos( - mod, (top, bottom, left, right, front, back)) - self._update_matrix_basis_translation(co, mat, up_, down_) + def align_orientation_to_user_perspective(self, context): + rotation = context.space_data.region_3d.view_matrix.inverted().to_quaternion() + matrix = rotation.to_matrix().to_4x4() + self.matrix_basis = matrix - self.up = up - self.down = down - self.up_ = up_ - self.down_ = down_ - self.G_SimpleDeformGizmoHandlerDit['draw_line'] = ( - (up, down), (up_, down_)) - data = top, bottom, left, right, front, back - self.update_draw_limits_bound_box(data, mod, axis, mat, up_, down_) - - def _update_matrix_basis_to_obj(self): - origin = self.modifier.origin - if origin: - self.matrix_basis = origin.matrix_world.normalized() - else: - self.matrix_basis = self.obj_matrix_world.normalized() - - def _update_matrix_basis_translation(self, co, mat, up_, down_): + def align_point_to_limits_point(self): if self.is_up_limits_mode: - self.matrix_basis.translation = up_ + self.matrix_basis.translation = self.point_limits_up elif self.is_down_limits_mode: - self.matrix_basis.translation = down_ + self.matrix_basis.translation = self.point_limits_down def delta_update(self, context, event, delta): - if ('draw_line' in self.G_SimpleDeformGizmoHandlerDit) and (self.ctrl_mode in ('up_limits', 'down_limits')): - x, y = view3d_utils.location_3d_to_region_2d( - context.region, context.space_data.region_3d, self.up) - x2, y2 = view3d_utils.location_3d_to_region_2d( - context.region, context.space_data.region_3d, self.down) + x, y = view3d_utils.location_3d_to_region_2d( + context.region, context.space_data.region_3d, self.up) + x2, y2 = view3d_utils.location_3d_to_region_2d( + context.region, context.space_data.region_3d, self.down) - mouse_line_distance = math.sqrt(((event.mouse_region_x - x2) ** 2) + - ((event.mouse_region_y - y2) ** 2)) - straight_line_distance = math.sqrt(((x2 - x) ** 2) + - ((y2 - y) ** 2)) - delta = mouse_line_distance / straight_line_distance + 0 + mouse_line_distance = math.sqrt(((event.mouse_region_x - x2) ** 2) + + ((event.mouse_region_y - y2) ** 2)) + straight_line_distance = math.sqrt(((x2 - x) ** 2) + + ((y2 - y) ** 2)) + delta = mouse_line_distance / straight_line_distance + 0 - v_up = Vector((x, y)) - v_down = Vector((x2, y2)) - limits_angle = v_up - v_down + v_up = Vector((x, y)) + v_down = Vector((x2, y2)) + limits_angle = v_up - v_down - mouse_v = Vector((event.mouse_region_x, event.mouse_region_y)) + mouse_v = Vector((event.mouse_region_x, event.mouse_region_y)) + + mouse_angle = mouse_v - v_down + angle_ = mouse_angle.angle(limits_angle) + if angle_ > (math.pi / 2): + delta = 0 - mouse_angle = mouse_v - v_down - angle_ = mouse_angle.angle(limits_angle) - if angle_ > (math.pi / 2): - delta = 0 return delta - def set_down_value(self, data, mu): - up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data - value = self.value_limit(delta, max_value=mu - limit_scope if middle else max_value) + def set_down_value(self, event): + value = self.get_down_limits_value(event) self.target_set_value('down_limits', value) if event.ctrl: self.target_set_value( - 'up_limits', value + difference_value) - elif middle: - if origin_mode == 'LIMITS_MIDDLE': + 'up_limits', value + self.difference_value) + + elif self.is_middle_mode: + if self.origin_mode == 'LIMITS_MIDDLE': + mu = self.middle_value self.target_set_value('up_limits', mu - (value - mu)) - elif origin_mode == 'MIDDLE': + elif self.origin_mode == 'MIDDLE': self.target_set_value('up_limits', 1 - value) else: - self.target_set_value('up_limits', up_limits) + self.target_set_value('up_limits', self.modifier_up_limits) else: - self.target_set_value('up_limits', up_limits) + self.target_set_value('up_limits', self.modifier_up_limits) - def set_up_value(self, data, mu): - up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data - value = self.value_limit(delta, min_value=mu + limit_scope if middle else min_value) + def set_up_value(self, event): + value = self.get_up_limits_value(event) self.target_set_value('up_limits', value) if event.ctrl: self.target_set_value( - 'down_limits', value - difference_value) - elif middle: - if origin_mode == 'LIMITS_MIDDLE': + 'down_limits', value - self.difference_value) + elif self.is_middle_mode: + if self.origin_mode == 'LIMITS_MIDDLE': + mu = self.middle_value self.target_set_value('down_limits', mu - (value - mu)) - elif origin_mode == 'MIDDLE': + elif self.origin_mode == 'MIDDLE': self.target_set_value('down_limits', 1 - value) else: - self.target_set_value('down_limits', down_limits) + self.target_set_value('down_limits', self.modifier_down_limits) else: - self.target_set_value('down_limits', down_limits) + self.target_set_value('down_limits', self.modifier_down_limits) - def set_prop_value(self, data): - up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data - mu = (up_limits + down_limits) / 2 - if self.is_angle_mode: - value = self.int_value_angle - delta - v = self.snap_value(value, event) - print(v) - self.target_set_value('angle', v) - elif self.is_up_limits_mode: - self.set_up_value(data, mu) + def set_prop_value(self, event): + if self.is_up_limits_mode: + self.set_up_value(event) elif self.is_down_limits_mode: - self.set_down_value(data, mu) + self.set_down_value(event) - def update_header_text(self, context, mod, origin, up_limits, down_limits): - translate: Callable[[Any], str] = lambda t: bpy.app.translations.pgettext(t) + def update_header_text(self, context): + origin = self.SimpleDeformGizmo_origin_property_group mode = origin.bl_rna.properties['origin_mode'].enum_items[origin.origin_mode].name - def t_(a, b): - return translate(a) + ':{}'.format(round(b, 3)) + t = self.translate_header_text + text = self.translate_text(mode) + ' ' - text = translate(mode) + ' ' - - if self.modifier_is_use_angle_value and self.is_angle_mode: - text += t_() - elif self.is_up_limits_mode: - text += t_('Upper limit', up_limits) + if self.is_up_limits_mode: + value = round(self.modifier_up_limits, 3) + text += t('Upper limit', value) elif self.is_down_limits_mode: - text += t_('Down limit', down_limits) - else: - text += t_('Coefficient', mod.factor) + value = round(self.modifier_down_limits, 3) + text += t('Down limit', value) context.area.header_text_set(text) def event_ops(self, event, ob, origin): @@ -161,16 +179,13 @@ class GizmoUpdate(GizmoProperty): elif event.type == 'A': self.pref.display_bend_axis_switch_gizmo = True return {'FINISHED'} - self.add_handler() return {'RUNNING_MODAL'} class UpDownLimitsGizmo(Gizmo, GizmoUpdate): - """显示轴向切换拖动点Gizmo(两个点) - """ bl_idname = 'UpDownLimitsGizmo' - + bl_label = 'UpDownLimitsGizmo' bl_target_properties = ( {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, @@ -183,84 +198,54 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): 'draw_type', 'mouse_dpi', 'ctrl_mode', - 'empty_object', - 'init_mouse_y', - 'init_mouse_x', + 'init_mouse_region_y', + 'init_mouse_region_x', 'custom_shape', - 'value_deform_axis', 'int_value_up_limits', 'int_value_down_limits', ) def setup(self): - self.generate_co_data() - self.draw_type = 'None_GizmoGroup_' - self.ctrl_mode = 'up_limits' # up_limits , down_limits self.mouse_dpi = 10 + self.init_setup() def invoke(self, context, event): self.init_invoke(context, event) - mod = context.object.modifiers.active - limits = mod.limits - up_limits = limits[1] - down_limits = limits[0] - if 'up_limits' == self.ctrl_mode: - self.int_value_up_limits = up_limits - self.target_set_value('up_limits', self.int_value_up_limits) - elif 'down_limits' == self.ctrl_mode: - self.int_value_down_limits = down_limits - self.target_set_value('down_limits', self.int_value_down_limits) + if self.is_up_limits_mode: + self.int_value_up_limits = up_limits = self.modifier_up_limits + self.target_set_value('up_limits', up_limits) + elif self.is_down_limits_mode: + self.int_value_down_limits = down_limits = self.modifier_down_limits + self.target_set_value('down_limits', down_limits) return {'RUNNING_MODAL'} def exit(self, context, cancel): context.area.header_text_set(None) - if cancel: - if 'deform_axis' == self.ctrl_mode: - self.target_set_value('deform_axis', self.value_deform_axis) - elif 'up_limits' == self.ctrl_mode: + if self.is_up_limits_mode: self.target_set_value('up_limits', self.int_value_up_limits) - elif 'down_limits' == self.ctrl_mode: + elif self.is_down_limits_mode: self.target_set_value( 'down_limits', self.int_value_down_limits) def modal(self, context, event, tweak): - # self.update_bound_box(context.object) - # - # ob = context.object - # mod = ob.modifiers.active - # limits = mod.limits - # up_limits = limits[1] - # down_limits = limits[0] - # origin = self.get_origin_property_group(mod, ob) - # origin_mode = origin.origin_mode - # middle = origin_mode in ('LIMITS_MIDDLE', 'MIDDLE') - # limit_scope = self.pref.modifiers_limits_tolerance - # max_value = up_limits - limit_scope - # min_value = down_limits + limit_scope - # difference_value = up_limits - down_limits - # - # delta = self.get_delta(event, tweak) - # delta = self.delta_update(context, event, delta) - # - # if origin_mode != 'NOT' and ('draw_line' in self.G_SimpleDeformGizmoHandlerDit): - # self.empty_object, _ = self.new_empty(ob, mod) - # self.G_SimpleDeformGizmoHandlerDit['empty_object'] = self.empty_object - # data = up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode - # self.set_prop_value(data) - # self.update_gizmo_matrix(context) - # self.update_empty_matrix() - # self.update_bound_box(context.object) + self.clear_cache() + + self.set_prop_value(event) + if self.SimpleDeformGizmo_is_use_empty_as_axis: + self.new_empty(self.obj, self.modifier) + # self.update_empty_matrix() + # self.update_header_text(context, mod, origin, up_limits, down_limits) - # self.add_handler() # return self.event_ops(event, ob, origin) return {'RUNNING_MODAL'} -class UpDownLimitsGizmoGroup(GizmoGroup, GizmoUtils, Handler): - bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup' +class UpDownLimitsGizmoGroup(GizmoGroup, GizmoGroupUtils): + bl_idname = 'OBJECT_GGT_UpDownLimitsGizmoGroup' + bl_label = 'UpDownLimitsGizmoGroup' @classmethod def poll(cls, context): @@ -268,8 +253,7 @@ class UpDownLimitsGizmoGroup(GizmoGroup, GizmoUtils, Handler): def setup(self, context): sd_name = UpDownLimitsGizmo.bl_idname - - add_data = ( + gizmo_data = [ ('up_limits', sd_name, {'ctrl_mode': 'up_limits', @@ -294,10 +278,14 @@ class UpDownLimitsGizmoGroup(GizmoGroup, GizmoUtils, Handler): 'use_draw_modal': True, 'scale_basis': 0.1, 'use_draw_value': True, }), - ) + ] + self.generate_gizmo_mode(gizmo_data) def refresh(self, context): pro = context.object.SimpleDeformGizmo_PropertyGroup + for i in (self.down_limits, self.up_limits): + for j in ('down_limits', 'up_limits'): + i.target_set_prop(j, pro, j) self.down_limits.target_set_prop('down_limits', pro, 'down_limits') diff --git a/simple_deform_helper/operators.py b/simple_deform_helper/operators.py index 332962e6b..e4948f2b5 100644 --- a/simple_deform_helper/operators.py +++ b/simple_deform_helper/operators.py @@ -47,8 +47,6 @@ class DeformAxisOperator(Operator, GizmoUtils): if not event.ctrl: self.pref.display_bend_axis_switch_gizmo = False - - GizmoUtils.update_bound_box(context.object) return {'FINISHED'} diff --git a/simple_deform_helper/preferences.py b/simple_deform_helper/preferences.py index d168f4813..8656ac8ec 100644 --- a/simple_deform_helper/preferences.py +++ b/simple_deform_helper/preferences.py @@ -27,7 +27,7 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): bound_box_color: FloatVectorProperty( name='Bound Box', description='Draw Bound Box Color', - default=(1, 0, 0, 0.1), + default=(1, 0, 0, 0.5), soft_max=1, soft_min=0, size=4, diff --git a/simple_deform_helper/update.py b/simple_deform_helper/update.py index fb61082be..c584669e0 100644 --- a/simple_deform_helper/update.py +++ b/simple_deform_helper/update.py @@ -3,18 +3,22 @@ import bpy from bpy.app.handlers import depsgraph_update_post, persistent +from .utils import GizmoUpdate + @persistent def remove_not_use_empty(scene, dep): """循环场景内的所有物体,找出没用的空物体并删掉 """ + GizmoUpdate.clear_cache() remove_name: str = "ViewSimpleDeformGizmo__Empty_" context = bpy.context - for obj in context.scene.objects: - is_empty = obj.type == "EMPTY" - not_parent = not obj.parent - if remove_name in obj.name and not_parent and is_empty: - bpy.data.objects.remove(obj) # remove object + if GizmoUpdate.simple_deform_modifier_is_simple(context): + for obj in context.scene.objects: + is_empty = obj.type == "EMPTY" + not_parent = not obj.parent + if remove_name in obj.name and not_parent and is_empty: + bpy.data.objects.remove(obj) # remove object def register(): diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index aea0e8e3a..ad8812017 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -2,8 +2,8 @@ import math import uuid +from functools import cache from os.path import dirname, basename, realpath -from typing import Callable, Any import bpy import numpy as np @@ -14,8 +14,8 @@ from mathutils import Vector, Matrix, Euler class PublicData: """Public data class, all fixed data will be placed here """ - G_GizmoCustomShapeDict = {} - G_SimpleDeformGizmoHandlerDit = {} + G_CustomShape = {} + G_GizmoData = {} G_INDICES = ( (0, 1), (0, 2), (1, 3), (2, 3), (4, 5), (4, 6), (5, 7), (6, 7), @@ -47,7 +47,7 @@ class PublicData: import os json_path = os.path.join(os.path.dirname(__file__), "gizmo.json") with open(json_path, "r") as file: - cls.G_GizmoCustomShapeDict = json.load(file) + cls.G_CustomShape = json.load(file) @staticmethod def from_mesh_get_triangle_face_co(mesh: 'bpy.types.Mesh') -> list: @@ -94,32 +94,41 @@ class PublicClass(PublicData): class PublicPoll(PublicClass): - @classmethod - def simple_deform_public_poll(cls, context: 'bpy.types.context') -> bool: - """Public poll - In 3D View + def simple_deform_modifier_is_simple(cls, context): + """ Active Object in ('MESH', 'LATTICE') Active Modifier Type Is 'SIMPLE_DEFORM' and show_viewport - return True + :param context:bpy.types.Object + :return: """ + obj = context.object if not obj: return False - mod = obj.modifiers.active if not mod: return False - space = context.space_data - show_gizmo = space.show_gizmo if space.type == 'VIEW_3D' else True - available_obj_type = obj and (obj.type in ('MESH', 'LATTICE')) available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM') is_available_obj = available_modifiers_type and available_obj_type is_obj_mode = context.mode == 'OBJECT' show_mod = mod.show_viewport - return is_available_obj and is_obj_mode and show_gizmo and show_mod + return is_available_obj and is_obj_mode and show_mod + + @classmethod + def simple_deform_public_poll(cls, context: 'bpy.types.context') -> bool: + """Public poll + In 3D View + return True + """ + space = context.space_data + if not space: + return False + show_gizmo = space.show_gizmo if space.type == 'VIEW_3D' else True + obj = cls.simple_deform_modifier_is_simple(context) + return obj and show_gizmo @classmethod def _simple_deform_modifier_is_bend_poll(cls, context): @@ -250,12 +259,14 @@ class PublicUtils(PublicPoll): b = mat @ Vector((max_x, min_y, min_z)) c = mat @ Vector((min_x, max_y, min_z)) d = mat @ Vector((min_x, min_y, max_z)) - return ((aa + bb) / Vector((2, 2, 2)) for aa, bb in ((a, d) - (c, b) - (c, d) - (a, b) - (d, b) - (c, a))) + point_list = ((a, d), + (c, b), + (c, d), + (a, b), + (d, b), + (c, a),) + + return list((aa + bb) / 2 for (aa, bb) in point_list) @classmethod def translate_text(cls, text): @@ -267,35 +278,6 @@ class PublicUtils(PublicPoll): class GizmoClassMethod(PublicUtils): - @classmethod - def get_origin_property_group(cls, mod, ob): - if mod.origin: - return mod.origin.SimpleDeformGizmo_PropertyGroup - else: - return ob.SimpleDeformGizmo_PropertyGroup - - @classmethod - def get_up_down_return_list(cls, mod, axis, up_, down_, data): - top, bottom, left, right, front, back = data - if 'BEND' == mod.deform_method: - if axis in ('X', 'Y'): - top = up_ - bottom = down_ - elif axis == 'Z': - right = up_ - left = down_ - else: - if axis == 'X': - right = up_ - left = down_ - elif axis == 'Y': - back = up_ - front = down_ - elif axis == 'Z': - top = up_ - bottom = down_ - return top, bottom, left, right, front, back - @classmethod def new_empty(cls, obj, mod): origin = mod.origin @@ -355,52 +337,6 @@ class GizmoClassMethod(PublicUtils): elif origin_mode == 'MIDDLE': empty_object.matrix_world.translation = (up + down) / tow - @classmethod - def get_up_down(cls, mod, axis, top, bottom, left, right, front, back): - if 'BEND' == mod.deform_method: - if axis in ('X', 'Y'): - return top, bottom - elif axis == 'Z': - return right, left - else: - if axis == 'X': - return right, left - elif axis == 'Y': - return back, front - elif axis == 'Z': - return top, bottom - - @classmethod - def get_limits_pos(cls, mod, data): - top, bottom, left, right, front, back = data - up_limits = mod.limits[1] - down_limits = mod.limits[0] - axis = mod.deform_axis - - if mod.origin: - vector_axis = cls.get_vector_axis(mod) - origin_mat = mod.origin.matrix_world.to_3x3() - axis_ = origin_mat @ vector_axis - point_lit = [[top, bottom], [left, right], [front, back]] - - for f in range(point_lit.__len__()): - i = point_lit[f][0] - j = point_lit[f][1] - angle = cls.point_to_angle(i, j, f, axis_) - if abs(angle - 180) < 0.00001: - up, down = j, i - elif abs(angle) < 0.00001: - up, down = i, j - else: - up, down = cls.get_up_down(mod, axis, top, bottom, - left, right, front, back) - - ex = lambda a: down + ((up - down) * Vector((a, a, a))) - - up_ = ex(up_limits) - down_ = ex(down_limits) - return (up, down), (up_, down_) - @classmethod def get_vector_axis(cls, mod): axis = mod.deform_axis @@ -415,151 +351,44 @@ class GizmoClassMethod(PublicUtils): return vector_axis @classmethod - def generate_co_data(cls): - handler_dit = cls.G_SimpleDeformGizmoHandlerDit - - if 'co' not in handler_dit: - handler_dit['co'] = cls.get_mesh_max_min_co( + def get_bound_co_data(cls): + if 'co' not in cls.G_GizmoData: + cls.G_GizmoData['co'] = cls.get_mesh_max_min_co( bpy.context.object) - return handler_dit['co'] - - -class GizmoUpdate(GizmoClassMethod): + return cls.G_GizmoData['co'] @classmethod - def update_matrix(cls, mod, ob): - if mod.deform_method == 'BEND': - cls.new_empty(ob, mod) - if mod.origin: - empty_object = mod.origin - modifiers_co = cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'] - for index, mod_name in enumerate(modifiers_co): - co_items = list(modifiers_co.items()) - if mod.name == mod_name: - data = co_items[index - 1][1] if ( - index or (index != 1)) else modifiers_co['co'] - (up, down), (up_, down_) = cls.get_limits_pos( - mod, cls.co_to_direction(ob.matrix_world.copy(), data)) - origin_mode = cls.get_origin_property_group( - mod, ob).origin_mode - cls.set_empty_obj_matrix( - origin_mode, empty_object, up_, down_, up, down) + def tow_co_to_coordinate(cls, data): + ((min_x, min_y, min_z), (max_x, max_y, max_z)) = data + return ( + Vector((max_x, min_y, min_z)), + Vector((min_x, min_y, min_z)), + Vector((max_x, max_y, min_z)), + Vector((min_x, max_y, min_z)), + Vector((max_x, min_y, max_z)), + Vector((min_x, min_y, max_z)), + Vector((max_x, max_y, max_z)), + Vector((min_x, max_y, max_z)) + ) - @classmethod - def update_empty_matrix(cls): - ob = bpy.context.object - for mod in ob.modifiers: - if mod.type == 'SIMPLE_DEFORM': - cls.update_matrix(mod, ob) - @classmethod - def update_bound_box(cls, obj: 'bpy.types.Object'): - context = bpy.context - data = bpy.data - matrix = obj.matrix_world.copy() # 物体矩阵 - # add simple_deform mesh - (min_x, min_y, min_z), (max_x, max_y, - max_z) = cls.get_mesh_max_min_co(obj) - vertexes = ((max_x, min_y, min_z), - (min_x, min_y, min_z), - (max_x, max_y, min_z), - (min_x, max_y, min_z), - (max_x, min_y, max_z), - (min_x, min_y, max_z), - (max_x, max_y, max_z), - (min_x, max_y, max_z)) - name = cls.G_NAME - if data.objects.get(name): - data.objects.remove(data.objects.get(name)) +class PublicProperty(GizmoClassMethod): - if data.meshes.get(name): - data.meshes.remove(data.meshes.get(name)) - mesh = data.meshes.new(name) - mesh.from_pydata(vertexes, cls.G_INDICES, []) - mesh.update() + def __from_up_down_point_get_limits_point(self, up_point, down_point): - new_object = data.objects.new(name, mesh) + def ex(a): + return down_point + ((up_point - down_point) * Vector((a, a, a))) - cls.link_obj_to_active_collection(new_object) + up_limits = ex(self.modifier_up_limits) + down_limits = ex(self.modifier_down_limits) + return up_limits, down_limits - if new_object.parent != obj: - new_object.parent = obj - - new_object.modifiers.clear() - subdivision = new_object.modifiers.new('1', 'SUBSURF') - subdivision.levels = 7 - cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'] = {} - cls.G_SimpleDeformGizmoHandlerDit['modifiers_co']['co'] = ( - min_x, min_y, min_z), (max_x, max_y, max_z) - for mo in context.object.modifiers: - if mo.type == 'SIMPLE_DEFORM': - simple_deform = new_object.modifiers.new( - mo.name, 'SIMPLE_DEFORM') - simple_deform.deform_method = mo.deform_method - simple_deform.deform_axis = mo.deform_axis - simple_deform.lock_x = mo.lock_x - simple_deform.lock_y = mo.lock_y - simple_deform.lock_z = mo.lock_z - simple_deform.origin = mo.origin - simple_deform.limits[1] = mo.limits[1] - simple_deform.limits[0] = mo.limits[0] - simple_deform.angle = mo.angle - simple_deform.show_viewport = mo.show_viewport - obj = PublicUtils.get_depsgraph(new_object) - cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'][mo.name] = cls.get_mesh_max_min_co( - obj) - new_object.hide_set(True) - new_object.hide_viewport = False - new_object.hide_select = True - new_object.hide_render = True - new_object.hide_viewport = True - new_object.hide_set(True) - ver_len = obj.data.vertices.__len__() - edge_len = obj.data.edges.__len__() - - if 'numpy_data' not in cls.G_SimpleDeformGizmoHandlerDit: - cls.G_SimpleDeformGizmoHandlerDit['numpy_data'] = {} - - numpy_data = cls.G_SimpleDeformGizmoHandlerDit['numpy_data'] - key = (ver_len, edge_len) - if key in numpy_data: - list_edges, list_vertices = numpy_data[key] - else: - list_edges = np.zeros(edge_len * 2, dtype=np.int32) - list_vertices = np.zeros(ver_len * 3, dtype=np.float32) - numpy_data[key] = (list_edges, list_vertices) - obj.data.vertices.foreach_get('co', list_vertices) - ver = list_vertices.reshape((ver_len, 3)) - ver = np.insert(ver, 3, 1, axis=1).T - ver[:] = np.dot(matrix, ver) - - ver /= ver[3, :] - ver = ver.T - ver = ver[:, :3] - obj.data.edges.foreach_get('vertices', list_edges) - indices = list_edges.reshape((edge_len, 2)) - - limits = context.object.modifiers.active.limits[:] - modifiers = [getattr(context.object.modifiers.active, i) - for i in cls.G_MODIFIERS_PROPERTY] - - cls.G_SimpleDeformGizmoHandlerDit['draw'] = (ver, indices, matrix, modifiers, limits) - - @classmethod - def update_co_data(cls, ob, mod): - handler_dit = cls.G_SimpleDeformGizmoHandlerDit - - if 'modifiers_co' in cls.G_SimpleDeformGizmoHandlerDit and ob.type in ('MESH', 'LATTICE'): - modifiers_co = cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'] - for index, mod_name in enumerate(modifiers_co): - co_items = list(modifiers_co.items()) - if mod.name == mod_name: - cls.G_SimpleDeformGizmoHandlerDit['co'] = co_items[index - 1][1] if (index or (index != 1)) else \ - modifiers_co['co'] - - def update_draw_limits_bound_box(self, data, mod, axis, mat, up_, down_): - top, bottom, left, right, front, back = data - if mod.origin: + @cache + def _get_limits_point_and_bound_box_co(self): + top, bottom, left, right, front, back = self.each_face_pos + mod = self.modifier + g_l = self.__from_up_down_point_get_limits_point + if self.modifier.origin: vector_axis = self.get_vector_axis(mod) origin_mat = mod.origin.matrix_world.to_3x3() axis_ = origin_mat @ vector_axis @@ -569,37 +398,86 @@ class GizmoUpdate(GizmoClassMethod): j = point_lit[f][1] angle = self.point_to_angle(i, j, f, axis_) if abs(angle - 180) < 0.00001: - point_lit[f][1], point_lit[f][0] = up_, down_ + up_point, down_point = j, i + up_limits, down_limits = g_l(j, i) + point_lit[f][1], point_lit[f][0] = up_limits, down_limits elif abs(angle) < 0.00001: - point_lit[f][0], point_lit[f][1] = up_, down_ + up_point, down_point = i, j + up_limits, down_limits = g_l(i, j) + point_lit[f][0], point_lit[f][1] = up_limits, down_limits + [[top, bottom], [left, right], [front, back]] = point_lit else: - top, bottom, left, right, front, back = self.get_up_down_return_list( - mod, axis, up_, down_, data) - data = top, bottom, left, right, front, back - (top, bottom, left, right, front, - back) = self.matrix_calculation(mat.inverted(), data) - self.G_SimpleDeformGizmoHandlerDit['draw_limits_bound_box'] = ( - mat, ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],))) + axis = self.modifier_deform_axis + if 'BEND' == self.modifier.deform_method: + if axis in ('X', 'Y'): + up_point, down_point = top, bottom + top, bottom = up_limits, down_limits = g_l(top, bottom) + elif axis == 'Z': + up_point, down_point = right, left + right, left = up_limits, down_limits = g_l(right, left) + else: + if axis == 'X': + up_point, down_point = right, left + right, left = up_limits, down_limits = g_l(right, left) + elif axis == 'Y': + up_point, down_point = back, front + back, front = up_limits, down_limits = g_l(back, front) - def update_bound_box_wireframe(self): - ... + elif axis == 'Z': + up_point, down_point = top, bottom + top, bottom = up_limits, down_limits = g_l(top, bottom) - def update_up_down_limits_wireframe(self): - ... + (top, bottom, left, + right, front, back) = self.matrix_calculation(self.obj_matrix_world.inverted(), + (top, bottom, left, right, front, back)) - def update_deform_wireframe(self): - ... + points = ((up_point, down_point), (up_limits, down_limits)) + each_point = ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],)) + box_bound_point = self.matrix_calculation(self.obj_matrix_world, self.tow_co_to_coordinate(each_point)) + return points, box_bound_point + # ---------------------- -class PublicProperty(GizmoUpdate): + @cache + def _each_face_pos(self, mat): + return self.co_to_direction(mat, self.get_bound_co_data()) @classmethod - def each_face_pos(cls, mat: 'Matrix' = None): - if mat is None: - mat = Matrix() - return cls.co_to_direction(mat, cls.G_SimpleDeformGizmoHandlerDit['co']) + def clear_cache(cls): + cls._each_face_pos.cache_clear() + cls._get_limits_point_and_bound_box_co.cache_clear() + cls.clear_data() + @classmethod + def clear_data(cls): + cls.G_GizmoData.clear() + + # --------------- Cache Data ---------------------- + + @property + def each_face_pos(self): + return self._each_face_pos(self.obj_matrix_world) + + @property + def modifier_limits_point(self): + points, _ = self._get_limits_point_and_bound_box_co() + return points + + @property + def modifier_limits_bound_box(self): + _, bound = self._get_limits_point_and_bound_box_co() + return bound + + @property + def modifier_origin_angle_is_available(self): + try: + self._get_limits_point_and_bound_box_co() + return True + except UnboundLocalError: + return False + + # --------------- Compute Data ---------------------- @property def obj(self): return bpy.context.object @@ -607,7 +485,12 @@ class PublicProperty(GizmoUpdate): @property def obj_matrix_world(self): if self.obj: - return self.obj.matrix_world + mat = self.obj.matrix_world.copy() + mat.freeze() + return mat + mat = Matrix() + mat.freeze() + return mat @property def modifier(self): @@ -628,64 +511,185 @@ class PublicProperty(GizmoUpdate): if mod: return mod.angle - @property - def active_modifier_is_simple_deform(self): - return self.modifier and self.modifier.type == 'SIMPLE_DEFORM' - @property def modifier_is_use_angle_value(self): if self.active_modifier_is_simple_deform: return self.modifier.deform_method in ('TWIST', 'BEND') + @property + def modifier_up_limits(self): + if self.modifier: + return self.modifier.limits[1] + + @property + def modifier_down_limits(self): + if self.modifier: + return self.modifier.limits[0] + + @property + def active_modifier_is_simple_deform(self): + return self.modifier and self.modifier.type == 'SIMPLE_DEFORM' + + # ----- point @property def point_up(self): - ... + return self.modifier_limits_point[0][0] @property def point_down(self): - ... + return self.modifier_limits_point[0][1] @property def point_limits_up(self): - ... + return self.modifier_limits_point[1][0] @property def point_limits_down(self): - ... + return self.modifier_limits_point[1][1] + + # ------ @property - def point_top(self): - ... + def SimpleDeformGizmo_origin_property_group(self): + mod = self.modifier + if mod.origin: + return mod.origin.SimpleDeformGizmo_PropertyGroup + else: + return self.obj.SimpleDeformGizmo_PropertyGroup @property - def point_bottom(self): - ... + def SimpleDeformGizmo_is_use_empty_as_axis(self): + return self.SimpleDeformGizmo_origin_property_group.origin_mode != 'NOT' - @property - def point_left(self): - ... - @property - def point_right(self): - ... - - @property - def point_front(self): - ... - - @property - def point_back(self): - ... +class GizmoUpdate(PublicProperty): @classmethod - def clear_cache(cls): + def update_matrix(cls, mod, ob): + if mod.deform_method == 'BEND': + cls.new_empty(ob, mod) + if mod.origin: + empty_object = mod.origin + modifiers_co = cls.G_GizmoData['modifiers_co'] + for index, mod_name in enumerate(modifiers_co): + co_items = list(modifiers_co.items()) + if mod.name == mod_name: + data = co_items[index - 1][1] if ( + index or (index != 1)) else modifiers_co['co'] + (up, down), (up_, down_) = cls.get_limits_pos( + mod, cls.co_to_direction(ob.matrix_world.copy(), data)) + origin_mode = cls.SimpleDeformGizmo_origin_property_group( + mod, ob).origin_mode + cls.set_empty_obj_matrix( + origin_mode, empty_object, up_, down_, up, down) + + # @classmethod + # def update_empty_matrix(cls): + # ob = bpy.context.object + # for mod in ob.modifiers: + # if mod.type == 'SIMPLE_DEFORM': + # cls.update_matrix(mod, ob) + # + # @classmethod + # context = bpy.context + # data = bpy.data + # matrix = obj.matrix_world.copy() # 物体矩阵 + # # add simple_deform mesh + # (min_x, min_y, min_z), (max_x, max_y, + # max_z) = cls.get_mesh_max_min_co(obj) + # vertexes = ((max_x, min_y, min_z), + # (min_x, min_y, min_z), + # (max_x, max_y, min_z), + # (min_x, max_y, min_z), + # (max_x, min_y, max_z), + # (min_x, min_y, max_z), + # (max_x, max_y, max_z), + # (min_x, max_y, max_z)) + # name = cls.G_NAME + # if data.objects.get(name): + # data.objects.remove(data.objects.get(name)) + # + # if data.meshes.get(name): + # data.meshes.remove(data.meshes.get(name)) + # mesh = data.meshes.new(name) + # mesh.from_pydata(vertexes, cls.G_INDICES, []) + # mesh.update() + # + # new_object = data.objects.new(name, mesh) + # + # cls.link_obj_to_active_collection(new_object) + # + # if new_object.parent != obj: + # new_object.parent = obj + # + # new_object.modifiers.clear() + # subdivision = new_object.modifiers.new('1', 'SUBSURF') + # subdivision.levels = 7 + # cls.G_GizmoData['modifiers_co'] = {} + # cls.G_GizmoData['modifiers_co']['co'] = ( + # min_x, min_y, min_z), (max_x, max_y, max_z) + # for mo in context.object.modifiers: + # if mo.type == 'SIMPLE_DEFORM': + # simple_deform = new_object.modifiers.new( + # mo.name, 'SIMPLE_DEFORM') + # simple_deform.deform_method = mo.deform_method + # simple_deform.deform_axis = mo.deform_axis + # simple_deform.lock_x = mo.lock_x + # simple_deform.lock_y = mo.lock_y + # simple_deform.lock_z = mo.lock_z + # simple_deform.origin = mo.origin + # simple_deform.limits[1] = mo.limits[1] + # simple_deform.limits[0] = mo.limits[0] + # simple_deform.angle = mo.angle + # simple_deform.show_viewport = mo.show_viewport + # obj = PublicUtils.get_depsgraph(new_object) + # cls.G_GizmoData['modifiers_co'][mo.name] = cls.get_mesh_max_min_co( + # obj) + # new_object.hide_set(True) + # new_object.hide_viewport = False + # new_object.hide_select = True + # new_object.hide_render = True + # new_object.hide_viewport = True + # new_object.hide_set(True) + # ver_len = obj.data.vertices.__len__() + # edge_len = obj.data.edges.__len__() + # + # if 'numpy_data' not in cls.G_GizmoData: + # cls.G_GizmoData['numpy_data'] = {} + # + # numpy_data = cls.G_GizmoData['numpy_data'] + # key = (ver_len, edge_len) + # if key in numpy_data: + # list_edges, list_vertices = numpy_data[key] + # else: + # list_edges = np.zeros(edge_len * 2, dtype=np.int32) + # list_vertices = np.zeros(ver_len * 3, dtype=np.float32) + # numpy_data[key] = (list_edges, list_vertices) + # obj.data.vertices.foreach_get('co', list_vertices) + # ver = list_vertices.reshape((ver_len, 3)) + # ver = np.insert(ver, 3, 1, axis=1).T + # ver[:] = np.dot(matrix, ver) + # + # ver /= ver[3, :] + # ver = ver.T + # ver = ver[:, :3] + # obj.data.edges.foreach_get('vertices', list_edges) + # indices = list_edges.reshape((edge_len, 2)) + # + # limits = context.object.modifiers.active.limits[:] + # modifiers = [getattr(context.object.modifiers.active, i) + # for i in cls.G_MODIFIERS_PROPERTY] + # + # cls.G_GizmoData['draw'] = (ver, indices, matrix, modifiers, limits) + + def update_deform_wireframe(self): ... -class GizmoUtils(PublicProperty): +class GizmoUtils(GizmoUpdate): custom_shape: dict - init_mouse_y: float - init_mouse_x: float + init_mouse_region_y: float + init_mouse_region_x: float mouse_dpi: int matrix_basis: Matrix draw_type: str @@ -710,33 +714,35 @@ class GizmoUtils(PublicProperty): def init_shape(self): if not hasattr(self, 'custom_shape'): self.custom_shape = {} - for i in self.G_GizmoCustomShapeDict: - item = self.G_GizmoCustomShapeDict[i] + for i in self.G_CustomShape: + item = self.G_CustomShape[i] self.custom_shape[i] = self.new_custom_shape('TRIS', item) def init_setup(self): self.init_shape() def init_invoke(self, context, event): - self.init_mouse_y = event.mouse_y - self.init_mouse_x = event.mouse_x + self.init_mouse_region_y = event.mouse_region_y + self.init_mouse_region_x = event.mouse_region_x def _update_matrix(self, context): func = getattr(self, 'update_gizmo_matrix', None) - if func: + if func and self.modifier_origin_angle_is_available: func(context) def draw(self, context): - self.draw_custom_shape(self.custom_shape[self.draw_type]) - self._update_matrix(context) + if self.modifier_origin_angle_is_available: + self.draw_custom_shape(self.custom_shape[self.draw_type]) + self._update_matrix(context) def draw_select(self, context, select_id): - self.draw_custom_shape( - self.custom_shape[self.draw_type], select_id=select_id) - self._update_matrix(context) + if self.modifier_origin_angle_is_available: + self.draw_custom_shape( + self.custom_shape[self.draw_type], select_id=select_id) + self._update_matrix(context) def get_delta(self, event): - delta = (self.init_mouse_x - event.mouse_x) / self.mouse_dpi + delta = (self.init_mouse_region_x - event.mouse_region_x) / self.mouse_dpi return delta def get_snap(self, delta, tweak): @@ -759,6 +765,22 @@ class GizmoUtils(PublicProperty): ... +class GizmoGroupUtils(GizmoUtils): + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D', + 'PERSISTENT', + # 'SCALE', + # 'DEPTH_3D', + # 'SELECT', + # 'SHOW_MODAL_ALL', + # 'EXCLUDE_MODAL', + # 'TOOL_INIT', # not show + # 'TOOL_FALLBACK_KEYMAP', + # 'VR_REDRAWS' + } + + class Tmp: @classmethod def get_origin_bounds(cls, obj: 'bpy.types.Object') -> list: -- 2.30.2 From 97c4bf357002d31e317a5c49cc8604d87c8a0cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=90=8C=E6=96=B0?= <3209970865@qq.com> Date: Fri, 31 Mar 2023 01:04:56 +0800 Subject: [PATCH 05/12] new_origin_empty_object func --- simple_deform_helper/draw.py | 13 +- .../gizmo/up_down_limits_point.py | 176 ++++----- simple_deform_helper/operators.py | 2 +- simple_deform_helper/utils.py | 370 +++++++++--------- 4 files changed, 277 insertions(+), 284 deletions(-) diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index 72dde2f8d..f6d5d3db8 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -105,7 +105,6 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): self.draw_scale_text(obj) if not self.modifier_origin_angle_is_available: self.draw_bound_box() - ... if self.simple_deform_show_gizmo_poll(context): # draw bound box self.draw_bound_box() @@ -136,6 +135,8 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): # draw pos self.draw_3d_shader([line_pos[1]], (), (0, 1, 0, 0.5), shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') + self.draw_3d_shader([line_pos[0]], (), (1, 0, 0, 0.5), + shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') @classmethod def draw_deform_mesh(cls, ob, context): @@ -151,8 +152,10 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): pos, indices, pref.deform_wireframe_color) def draw_scale_text(self, ob): - ob = self.obj - dit = self.G_GizmoData - if (ob.scale != Vector((1, 1, 1))) and ('handler_text' not in dit): - dit['handler_text'] = bpy.types.SpaceView3D.draw_handler_add( + scale_error = (ob.scale != Vector((1, 1, 1))) + if scale_error and ('handler_text' not in self.G_GizmoData): + self.G_GizmoData['handler_text'] = bpy.types.SpaceView3D.draw_handler_add( self.draw_str, (), 'WINDOW', 'POST_PIXEL') + + def draw_origin_error(self): + ... diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index 0c3b0e418..8ded193ef 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -1,5 +1,4 @@ import math -from typing import Callable, Any import bpy from bpy.types import Gizmo, GizmoGroup @@ -22,29 +21,13 @@ class GizmoProperty(GizmoUtils): def is_down_limits_mode(self): return self.ctrl_mode == 'down_limits' - @property - def origin_mode(self): - return self.SimpleDeformGizmo_origin_property_group.origin_mode - - @property - def is_middle_mode(self): - return self.origin_mode in ('LIMITS_MIDDLE', 'MIDDLE') - @property def limit_scope(self): return self.pref.modifiers_limits_tolerance - @property - def difference_value(self): - return self.modifier_up_limits - self.modifier_down_limits - - @property - def middle_value(self): - return (self.modifier_up_limits + self.modifier_down_limits) / 2 - @property def limits_min_value(self): - return self.modifier_up_limits + self.limit_scope + return self.modifier_down_limits + self.limit_scope @property def limits_max_value(self): @@ -54,39 +37,22 @@ class GizmoProperty(GizmoUtils): def get_up_limits_value(self, event): delta = self.get_delta(event) - mid = self.middle_value + self.limit_scope + mid = self.middle_limits_value + self.limit_scope min_value = mid if self.is_middle_mode else self.limits_min_value return self.value_limit(delta, min_value=min_value) def get_down_limits_value(self, event): delta = self.get_delta(event) - mid = self.middle_value - self.limit_scope + mid = self.middle_limits_value - self.limit_scope max_value = mid if self.is_middle_mode else self.limits_max_value return self.value_limit(delta, max_value=max_value) - -class GizmoUpdate(GizmoProperty): - - def update_gizmo_matrix(self, context): - self.align_orientation_to_user_perspective(context) - self.align_point_to_limits_point() - - def align_orientation_to_user_perspective(self, context): - rotation = context.space_data.region_3d.view_matrix.inverted().to_quaternion() - matrix = rotation.to_matrix().to_4x4() - self.matrix_basis = matrix - - def align_point_to_limits_point(self): - if self.is_up_limits_mode: - self.matrix_basis.translation = self.point_limits_up - elif self.is_down_limits_mode: - self.matrix_basis.translation = self.point_limits_down - - def delta_update(self, context, event, delta): + def get_delta(self, event): + context = bpy.context x, y = view3d_utils.location_3d_to_region_2d( - context.region, context.space_data.region_3d, self.up) + context.region, context.space_data.region_3d, self.point_up) x2, y2 = view3d_utils.location_3d_to_region_2d( - context.region, context.space_data.region_3d, self.down) + context.region, context.space_data.region_3d, self.point_down) mouse_line_distance = math.sqrt(((event.mouse_region_x - x2) ** 2) + ((event.mouse_region_y - y2) ** 2)) @@ -104,20 +70,43 @@ class GizmoUpdate(GizmoProperty): angle_ = mouse_angle.angle(limits_angle) if angle_ > (math.pi / 2): delta = 0 - return delta + +class GizmoUpdate(GizmoProperty): + # ---update gizmo matrix + def update_gizmo_matrix(self, context): + self.align_orientation_to_user_perspective(context) + self.align_point_to_limits_point() + + def align_orientation_to_user_perspective(self, context): + rotation = context.space_data.region_3d.view_matrix.inverted().to_quaternion() + matrix = rotation.to_matrix().to_4x4() + self.matrix_basis = matrix + + def align_point_to_limits_point(self): + if self.is_up_limits_mode: + self.matrix_basis.translation = self.point_limits_up + elif self.is_down_limits_mode: + self.matrix_basis.translation = self.point_limits_down + + # ---- set prop + def set_prop_value(self, event): + if self.is_up_limits_mode: + self.set_up_value(event) + elif self.is_down_limits_mode: + self.set_down_value(event) + def set_down_value(self, event): value = self.get_down_limits_value(event) self.target_set_value('down_limits', value) if event.ctrl: - self.target_set_value( - 'up_limits', value + self.difference_value) - + self.target_set_value('up_limits', value + self.difference_value) elif self.is_middle_mode: if self.origin_mode == 'LIMITS_MIDDLE': - mu = self.middle_value - self.target_set_value('up_limits', mu - (value - mu)) + mu = self.middle_limits_value + v = mu - (value - mu) + self.target_set_value('up_limits', v) elif self.origin_mode == 'MIDDLE': self.target_set_value('up_limits', 1 - value) else: @@ -129,12 +118,12 @@ class GizmoUpdate(GizmoProperty): value = self.get_up_limits_value(event) self.target_set_value('up_limits', value) if event.ctrl: - self.target_set_value( - 'down_limits', value - self.difference_value) + self.target_set_value('down_limits', value - self.difference_value) elif self.is_middle_mode: if self.origin_mode == 'LIMITS_MIDDLE': - mu = self.middle_value - self.target_set_value('down_limits', mu - (value - mu)) + mu = self.middle_limits_value + value = mu - (value - mu) + self.target_set_value('down_limits', value) elif self.origin_mode == 'MIDDLE': self.target_set_value('down_limits', 1 - value) else: @@ -142,14 +131,9 @@ class GizmoUpdate(GizmoProperty): else: self.target_set_value('down_limits', self.modifier_down_limits) - def set_prop_value(self, event): - if self.is_up_limits_mode: - self.set_up_value(event) - elif self.is_down_limits_mode: - self.set_down_value(event) - + # ------- def update_header_text(self, context): - origin = self.SimpleDeformGizmo_origin_property_group + origin = self.obj_origin_property_group mode = origin.bl_rna.properties['origin_mode'].enum_items[origin.origin_mode].name t = self.translate_header_text @@ -157,30 +141,35 @@ class GizmoUpdate(GizmoProperty): if self.is_up_limits_mode: value = round(self.modifier_up_limits, 3) - text += t('Upper limit', value) + text += t('Up limit', value) elif self.is_down_limits_mode: value = round(self.modifier_down_limits, 3) text += t('Down limit', value) context.area.header_text_set(text) - def event_ops(self, event, ob, origin): - """通过输入键位来更改属性""" - # event ctrl - data_path = ('object.SimpleDeformGizmo_PropertyGroup.origin_mode', - 'object.modifiers.active.origin.SimpleDeformGizmo_PropertyGroup.origin_mode') - - if event.type in ('WHEELUPMOUSE', 'WHEELDOWNMOUSE'): - reverse = (event.type == 'WHEELUPMOUSE') - for path in data_path: - bpy.ops.wm.context_cycle_enum( - data_path=path, reverse=reverse, wrap=True) - elif event.type in ('X', 'Y', 'Z'): - ob.modifiers.active.deform_axis = event.type - elif event.type == 'A': - self.pref.display_bend_axis_switch_gizmo = True - return {'FINISHED'} - - return {'RUNNING_MODAL'} + # def update_modifiers_origin_matrix(self): + # ob = bpy.context.object + # for mod in ob.modifiers: + # if mod.type == 'SIMPLE_DEFORM': + # self.update_matrix(mod, ob) + # + # def update_matrix(self, mod, ob): + # # if mod.deform_method == 'BEND': + # # cls.new_empty(ob, mod) + # origin_object = mod.origin + # if mod.origin: + # modifiers_co = self.G_GizmoData['modifiers_co'] + # for index, mod_name in enumerate(modifiers_co): + # co_items = list(modifiers_co.items()) + # if mod.name == mod_name: + # data = co_items[index - 1][1] if ( + # index or (index != 1)) else modifiers_co['co'] + # (up, down), (up_, down_) = self.get_limits_pos( + # mod, self.co_to_direction(ob.matrix_world.copy(), data)) + # origin_mode = self.obj_origin_property_group( + # mod, ob).origin_mode + # self.set_empty_obj_matrix( + # origin_mode, origin_object, up_, down_, up, down) class UpDownLimitsGizmo(Gizmo, GizmoUpdate): @@ -190,6 +179,7 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): {'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1}, {'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1}, ) + bl_options = {'UNDO', 'GRAB_CURSOR'} __slots__ = ( 'mod', @@ -198,12 +188,16 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): 'draw_type', 'mouse_dpi', 'ctrl_mode', + 'difference_value', + 'middle_limits_value', 'init_mouse_region_y', 'init_mouse_region_x', 'custom_shape', 'int_value_up_limits', 'int_value_down_limits', ) + difference_value: float + middle_limits_value: float def setup(self): self.mouse_dpi = 10 @@ -232,15 +226,17 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): def modal(self, context, event, tweak): self.clear_cache() + if self.modifier_is_use_origin_axis: + self.new_origin_empty_object() + + self.difference_value = self.modifier_up_limits - self.modifier_down_limits + self.middle_limits_value = (self.modifier_up_limits + self.modifier_down_limits) / 2 + self.set_prop_value(event) - if self.SimpleDeformGizmo_is_use_empty_as_axis: - self.new_empty(self.obj, self.modifier) - # self.update_empty_matrix() + self.update_object_origin_matrix() - # self.update_header_text(context, mod, origin, up_limits, down_limits) - - # return self.event_ops(event, ob, origin) - return {'RUNNING_MODAL'} + self.update_header_text(context) + return self.event_handle(event) class UpDownLimitsGizmoGroup(GizmoGroup, GizmoGroupUtils): @@ -286,15 +282,3 @@ class UpDownLimitsGizmoGroup(GizmoGroup, GizmoGroupUtils): for i in (self.down_limits, self.up_limits): for j in ('down_limits', 'up_limits'): i.target_set_prop(j, pro, j) - self.down_limits.target_set_prop('down_limits', - pro, - 'down_limits') - self.down_limits.target_set_prop('up_limits', - pro, - 'up_limits') - self.up_limits.target_set_prop('down_limits', - pro, - 'down_limits') - self.up_limits.target_set_prop('up_limits', - pro, - 'up_limits') diff --git a/simple_deform_helper/operators.py b/simple_deform_helper/operators.py index e4948f2b5..30c02b685 100644 --- a/simple_deform_helper/operators.py +++ b/simple_deform_helper/operators.py @@ -30,7 +30,7 @@ class DeformAxisOperator(Operator, GizmoUtils): mod = context.object.modifiers.active mod.deform_axis = self.Deform_Axis - empty, con_limit_name = GizmoUtils.new_empty(context.object, mod) + empty, con_limit_name = GizmoUtils.new_origin_empty_object(context.object, mod) is_positive = GizmoUtils.is_positive(mod.angle) for limit, value in (('max_x', self.X_Value), diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index ad8812017..540ce1c2e 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -278,65 +278,6 @@ class PublicUtils(PublicPoll): class GizmoClassMethod(PublicUtils): - @classmethod - def new_empty(cls, obj, mod): - origin = mod.origin - if origin is None: - new_name = cls.G_NAME + '_Empty_' + str(uuid.uuid4()) - origin_object = bpy.data.objects.new(new_name, None) - cls.link_obj_to_active_collection(origin_object) - origin_object.hide_set(True) - origin_object.empty_display_size = min(obj.dimensions) - mod.origin = origin_object - else: - origin_object = mod.origin - origin_object.hide_viewport = False - - if origin_object.parent != obj: - origin_object.parent = obj - - # add constraints - con_name = cls.G_CON_LIMIT_NAME - if con_name in origin_object.constraints.keys(): - limit_constraints = origin.constraints.get(con_name) - else: - limit_constraints = origin_object.constraints.new( - 'LIMIT_ROTATION') - limit_constraints.name = con_name - limit_constraints.owner_space = 'WORLD' - limit_constraints.space_object = obj - limit_constraints.use_transform_limit = True - limit_constraints.use_limit_x = True - limit_constraints.use_limit_y = True - limit_constraints.use_limit_z = True - con_copy_name = cls.G_NAME + 'constraints_copy_rotation' - if con_copy_name in origin_object.constraints.keys(): - copy_constraints = origin.constraints.get(con_copy_name) - else: - copy_constraints = origin_object.constraints.new( - 'COPY_ROTATION') - copy_constraints.name = con_copy_name - copy_constraints.target = obj - copy_constraints.mix_mode = 'BEFORE' - copy_constraints.target_space = 'WORLD' - copy_constraints.owner_space = 'WORLD' - origin_object.rotation_euler.zero() - origin_object.scale = 1, 1, 1 - return origin_object, con_name - - @classmethod - def set_empty_obj_matrix(cls, origin_mode, empty_object, up_, down_, up, down): - tow = Vector((2, 2, 2)) - if origin_mode == 'UP_LIMITS': - empty_object.matrix_world.translation = Vector(up_) - elif origin_mode == 'DOWN_LIMITS': - empty_object.matrix_world.translation = Vector( - down_) - elif origin_mode == 'LIMITS_MIDDLE': - empty_object.matrix_world.translation = (up_ + down_) / tow - elif origin_mode == 'MIDDLE': - empty_object.matrix_world.translation = (up + down) / tow - @classmethod def get_vector_axis(cls, mod): axis = mod.deform_axis @@ -550,7 +491,7 @@ class PublicProperty(GizmoClassMethod): # ------ @property - def SimpleDeformGizmo_origin_property_group(self): + def obj_origin_property_group(self): mod = self.modifier if mod.origin: return mod.origin.SimpleDeformGizmo_PropertyGroup @@ -558,132 +499,179 @@ class PublicProperty(GizmoClassMethod): return self.obj.SimpleDeformGizmo_PropertyGroup @property - def SimpleDeformGizmo_is_use_empty_as_axis(self): - return self.SimpleDeformGizmo_origin_property_group.origin_mode != 'NOT' + def origin_mode(self): + return self.obj_origin_property_group.origin_mode + + @property + def is_limits_middle_mode(self): + return self.origin_mode == 'LIMITS_MIDDLE' + + @property + def is_middle_mode(self): + return self.origin_mode in ('LIMITS_MIDDLE', 'MIDDLE') + + @property + def modifier_is_use_origin_axis(self): + return self.obj_origin_property_group.origin_mode != 'NOT' class GizmoUpdate(PublicProperty): - @classmethod - def update_matrix(cls, mod, ob): - if mod.deform_method == 'BEND': - cls.new_empty(ob, mod) - if mod.origin: - empty_object = mod.origin - modifiers_co = cls.G_GizmoData['modifiers_co'] - for index, mod_name in enumerate(modifiers_co): - co_items = list(modifiers_co.items()) - if mod.name == mod_name: - data = co_items[index - 1][1] if ( - index or (index != 1)) else modifiers_co['co'] - (up, down), (up_, down_) = cls.get_limits_pos( - mod, cls.co_to_direction(ob.matrix_world.copy(), data)) - origin_mode = cls.SimpleDeformGizmo_origin_property_group( - mod, ob).origin_mode - cls.set_empty_obj_matrix( - origin_mode, empty_object, up_, down_, up, down) + def new_origin_empty_object(self): + mod = self.modifier + obj = self.obj + origin = mod.origin + if not origin: + new_name = self.G_NAME + '_Empty_' + str(uuid.uuid4()) + origin_object = bpy.data.objects.new(new_name, None) + self.link_obj_to_active_collection(origin_object) + origin_object.hide_set(True) + origin_object.empty_display_size = min(obj.dimensions) + mod.origin = origin_object + else: + origin_object = mod.origin + origin_object.hide_viewport = False - # @classmethod - # def update_empty_matrix(cls): - # ob = bpy.context.object - # for mod in ob.modifiers: - # if mod.type == 'SIMPLE_DEFORM': - # cls.update_matrix(mod, ob) - # - # @classmethod - # context = bpy.context - # data = bpy.data - # matrix = obj.matrix_world.copy() # 物体矩阵 - # # add simple_deform mesh - # (min_x, min_y, min_z), (max_x, max_y, - # max_z) = cls.get_mesh_max_min_co(obj) - # vertexes = ((max_x, min_y, min_z), - # (min_x, min_y, min_z), - # (max_x, max_y, min_z), - # (min_x, max_y, min_z), - # (max_x, min_y, max_z), - # (min_x, min_y, max_z), - # (max_x, max_y, max_z), - # (min_x, max_y, max_z)) - # name = cls.G_NAME - # if data.objects.get(name): - # data.objects.remove(data.objects.get(name)) - # - # if data.meshes.get(name): - # data.meshes.remove(data.meshes.get(name)) - # mesh = data.meshes.new(name) - # mesh.from_pydata(vertexes, cls.G_INDICES, []) - # mesh.update() - # - # new_object = data.objects.new(name, mesh) - # - # cls.link_obj_to_active_collection(new_object) - # - # if new_object.parent != obj: - # new_object.parent = obj - # - # new_object.modifiers.clear() - # subdivision = new_object.modifiers.new('1', 'SUBSURF') - # subdivision.levels = 7 - # cls.G_GizmoData['modifiers_co'] = {} - # cls.G_GizmoData['modifiers_co']['co'] = ( - # min_x, min_y, min_z), (max_x, max_y, max_z) - # for mo in context.object.modifiers: - # if mo.type == 'SIMPLE_DEFORM': - # simple_deform = new_object.modifiers.new( - # mo.name, 'SIMPLE_DEFORM') - # simple_deform.deform_method = mo.deform_method - # simple_deform.deform_axis = mo.deform_axis - # simple_deform.lock_x = mo.lock_x - # simple_deform.lock_y = mo.lock_y - # simple_deform.lock_z = mo.lock_z - # simple_deform.origin = mo.origin - # simple_deform.limits[1] = mo.limits[1] - # simple_deform.limits[0] = mo.limits[0] - # simple_deform.angle = mo.angle - # simple_deform.show_viewport = mo.show_viewport - # obj = PublicUtils.get_depsgraph(new_object) - # cls.G_GizmoData['modifiers_co'][mo.name] = cls.get_mesh_max_min_co( - # obj) - # new_object.hide_set(True) - # new_object.hide_viewport = False - # new_object.hide_select = True - # new_object.hide_render = True - # new_object.hide_viewport = True - # new_object.hide_set(True) - # ver_len = obj.data.vertices.__len__() - # edge_len = obj.data.edges.__len__() - # - # if 'numpy_data' not in cls.G_GizmoData: - # cls.G_GizmoData['numpy_data'] = {} - # - # numpy_data = cls.G_GizmoData['numpy_data'] - # key = (ver_len, edge_len) - # if key in numpy_data: - # list_edges, list_vertices = numpy_data[key] - # else: - # list_edges = np.zeros(edge_len * 2, dtype=np.int32) - # list_vertices = np.zeros(ver_len * 3, dtype=np.float32) - # numpy_data[key] = (list_edges, list_vertices) - # obj.data.vertices.foreach_get('co', list_vertices) - # ver = list_vertices.reshape((ver_len, 3)) - # ver = np.insert(ver, 3, 1, axis=1).T - # ver[:] = np.dot(matrix, ver) - # - # ver /= ver[3, :] - # ver = ver.T - # ver = ver[:, :3] - # obj.data.edges.foreach_get('vertices', list_edges) - # indices = list_edges.reshape((edge_len, 2)) - # - # limits = context.object.modifiers.active.limits[:] - # modifiers = [getattr(context.object.modifiers.active, i) - # for i in cls.G_MODIFIERS_PROPERTY] - # - # cls.G_GizmoData['draw'] = (ver, indices, matrix, modifiers, limits) + if origin_object.parent != obj: + origin_object.parent = obj + + # add constraints + name = self.G_CON_LIMIT_NAME + if origin_object.constraints.keys().__len__() > 1: + origin_object.constraints.clear() + if name in origin_object.constraints.keys(): + limit_constraints = origin.constraints.get(name) + else: + limit_constraints = origin_object.constraints.new( + 'LIMIT_ROTATION') + limit_constraints.name = name + limit_constraints.owner_space = 'WORLD' + limit_constraints.space_object = obj + limit_constraints.use_transform_limit = True + limit_constraints.use_limit_x = True + limit_constraints.use_limit_y = True + limit_constraints.use_limit_z = True + con_copy_name = self.G_NAME + 'constraints_copy_rotation' + if con_copy_name in origin_object.constraints.keys(): + copy_constraints = origin.constraints.get(con_copy_name) + else: + copy_constraints = origin_object.constraints.new( + 'COPY_ROTATION') + copy_constraints.name = con_copy_name + copy_constraints.target = obj + copy_constraints.mix_mode = 'BEFORE' + copy_constraints.target_space = 'WORLD' + copy_constraints.owner_space = 'WORLD' + origin_object.rotation_euler.zero() + origin_object.scale = 1, 1, 1 + + def update_object_origin_matrix(self): + self.clear_data() + origin_mode = self.origin_mode + empty_object = self.modifier.origin + if empty_object and self.modifier_is_use_origin_axis : + if origin_mode == 'UP_LIMITS': + empty_object.matrix_world.translation = Vector(self.point_limits_up) + elif origin_mode == 'DOWN_LIMITS': + empty_object.matrix_world.translation = Vector(self.point_limits_down) + elif origin_mode == 'LIMITS_MIDDLE': + translation = (self.point_limits_up + self.point_limits_down) / 2 + empty_object.matrix_world.translation = translation + elif origin_mode == 'MIDDLE': + translation = (self.point_up + self.point_down) / 2 + empty_object.matrix_world.translation = translation def update_deform_wireframe(self): - ... + context = bpy.context + data = bpy.data + matrix = obj.matrix_world.copy() # 物体矩阵 + # add simple_deform mesh + (min_x, min_y, min_z), (max_x, max_y, + max_z) = cls.get_mesh_max_min_co(obj) + vertexes = ((max_x, min_y, min_z), + (min_x, min_y, min_z), + (max_x, max_y, min_z), + (min_x, max_y, min_z), + (max_x, min_y, max_z), + (min_x, min_y, max_z), + (max_x, max_y, max_z), + (min_x, max_y, max_z)) + name = cls.G_NAME + if data.objects.get(name): + data.objects.remove(data.objects.get(name)) + + if data.meshes.get(name): + data.meshes.remove(data.meshes.get(name)) + mesh = data.meshes.new(name) + mesh.from_pydata(vertexes, cls.G_INDICES, []) + mesh.update() + + new_object = data.objects.new(name, mesh) + + cls.link_obj_to_active_collection(new_object) + + if new_object.parent != obj: + new_object.parent = obj + + new_object.modifiers.clear() + subdivision = new_object.modifiers.new('1', 'SUBSURF') + subdivision.levels = 7 + cls.G_GizmoData['modifiers_co'] = {} + cls.G_GizmoData['modifiers_co']['co'] = ( + min_x, min_y, min_z), (max_x, max_y, max_z) + for mo in context.object.modifiers: + if mo.type == 'SIMPLE_DEFORM': + simple_deform = new_object.modifiers.new( + mo.name, 'SIMPLE_DEFORM') + simple_deform.deform_method = mo.deform_method + simple_deform.deform_axis = mo.deform_axis + simple_deform.lock_x = mo.lock_x + simple_deform.lock_y = mo.lock_y + simple_deform.lock_z = mo.lock_z + simple_deform.origin = mo.origin + simple_deform.limits[1] = mo.limits[1] + simple_deform.limits[0] = mo.limits[0] + simple_deform.angle = mo.angle + simple_deform.show_viewport = mo.show_viewport + obj = PublicUtils.get_depsgraph(new_object) + cls.G_GizmoData['modifiers_co'][mo.name] = cls.get_mesh_max_min_co( + obj) + new_object.hide_set(True) + new_object.hide_viewport = False + new_object.hide_select = True + new_object.hide_render = True + new_object.hide_viewport = True + new_object.hide_set(True) + ver_len = obj.data.vertices.__len__() + edge_len = obj.data.edges.__len__() + + if 'numpy_data' not in cls.G_GizmoData: + cls.G_GizmoData['numpy_data'] = {} + + numpy_data = cls.G_GizmoData['numpy_data'] + key = (ver_len, edge_len) + if key in numpy_data: + list_edges, list_vertices = numpy_data[key] + else: + list_edges = np.zeros(edge_len * 2, dtype=np.int32) + list_vertices = np.zeros(ver_len * 3, dtype=np.float32) + numpy_data[key] = (list_edges, list_vertices) + obj.data.vertices.foreach_get('co', list_vertices) + ver = list_vertices.reshape((ver_len, 3)) + ver = np.insert(ver, 3, 1, axis=1).T + ver[:] = np.dot(matrix, ver) + + ver /= ver[3, :] + ver = ver.T + ver = ver[:, :3] + obj.data.edges.foreach_get('vertices', list_edges) + indices = list_edges.reshape((edge_len, 2)) + + limits = context.object.modifiers.active.limits[:] + modifiers = [getattr(context.object.modifiers.active, i) + for i in cls.G_MODIFIERS_PROPERTY] + + cls.G_GizmoData['draw'] = (ver, indices, matrix, modifiers, limits) class GizmoUtils(GizmoUpdate): @@ -725,7 +713,7 @@ class GizmoUtils(GizmoUpdate): self.init_mouse_region_y = event.mouse_region_y self.init_mouse_region_x = event.mouse_region_x - def _update_matrix(self, context): + def __update_matrix_func(self, context): func = getattr(self, 'update_gizmo_matrix', None) if func and self.modifier_origin_angle_is_available: func(context) @@ -733,13 +721,13 @@ class GizmoUtils(GizmoUpdate): def draw(self, context): if self.modifier_origin_angle_is_available: self.draw_custom_shape(self.custom_shape[self.draw_type]) - self._update_matrix(context) + self.__update_matrix_func(context) def draw_select(self, context, select_id): if self.modifier_origin_angle_is_available: self.draw_custom_shape( self.custom_shape[self.draw_type], select_id=select_id) - self._update_matrix(context) + self.__update_matrix_func(context) def get_delta(self, event): delta = (self.init_mouse_region_x - event.mouse_region_x) / self.mouse_dpi @@ -764,6 +752,24 @@ class GizmoUtils(GizmoUpdate): def update_gizmo_matrix(self): ... + def event_handle(self, event): + """通过输入键位来更改属性""" + # event ctrl + data_path = ('object.SimpleDeformGizmo_PropertyGroup.origin_mode', + 'object.modifiers.active.origin.SimpleDeformGizmo_PropertyGroup.origin_mode') + + if event.type in ('WHEELUPMOUSE', 'WHEELDOWNMOUSE'): + reverse = (event.type == 'WHEELUPMOUSE') + for path in data_path: + bpy.ops.wm.context_cycle_enum( + data_path=path, reverse=reverse, wrap=True) + elif event.type in ('X', 'Y', 'Z'): + self.obj.modifiers.active.deform_axis = event.type + elif event.type == 'A': + self.pref.display_bend_axis_switch_gizmo = True + return {'FINISHED'} + return {'RUNNING_MODAL'} + class GizmoGroupUtils(GizmoUtils): bl_space_type = 'VIEW_3D' -- 2.30.2 From 4ea575bcbf3bf37379534e64d9f3923c2ba95cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=90=8C=E6=96=B0?= <3209970865@qq.com> Date: Fri, 31 Mar 2023 01:56:19 +0800 Subject: [PATCH 06/12] draw deform wireframe --- simple_deform_helper/draw.py | 17 ++-- .../gizmo/angle_and_factor.py | 3 +- .../gizmo/up_down_limits_point.py | 1 + simple_deform_helper/operators.py | 2 +- simple_deform_helper/panel.py | 6 ++ simple_deform_helper/preferences.py | 5 + simple_deform_helper/update.py | 2 +- simple_deform_helper/utils.py | 92 +++++++++---------- 8 files changed, 68 insertions(+), 60 deletions(-) diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index f6d5d3db8..3a3c710bf 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -108,7 +108,7 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): if self.simple_deform_show_gizmo_poll(context): # draw bound box self.draw_bound_box() - # cls.draw_deform_mesh(obj, context) + self.draw_deform_mesh() self.draw_limits_line() self.draw_limits_bound_box() elif self.simple_deform_show_bend_axis_witch_poll(context): @@ -138,18 +138,17 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): self.draw_3d_shader([line_pos[0]], (), (1, 0, 0, 0.5), shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') - @classmethod - def draw_deform_mesh(cls, ob, context): - pref = cls.pref_() - handler_dit = cls.G_GizmoData - active = context.object.modifiers.active + def draw_deform_mesh(self): + ob = self.obj + handler_dit = self.G_GizmoData + active = self.modifier # draw deform mesh if 'draw' in handler_dit: pos, indices, mat, mod_data, limits = handler_dit['draw'] - if ([getattr(active, i) for i in cls.G_MODIFIERS_PROPERTY] == mod_data) and ( + if ([getattr(active, i) for i in self.G_MODIFIERS_PROPERTY] == mod_data) and ( ob.matrix_world == mat) and limits == active.limits[:]: - cls.draw_3d_shader( - pos, indices, pref.deform_wireframe_color) + self.draw_3d_shader( + pos, indices, self.pref.deform_wireframe_color) def draw_scale_text(self, ob): scale_error = (ob.scale != Vector((1, 1, 1))) diff --git a/simple_deform_helper/gizmo/angle_and_factor.py b/simple_deform_helper/gizmo/angle_and_factor.py index 5aecb93ce..692dd7352 100644 --- a/simple_deform_helper/gizmo/angle_and_factor.py +++ b/simple_deform_helper/gizmo/angle_and_factor.py @@ -70,8 +70,9 @@ class AngleGizmo(Gizmo, AngleUpdate): def modal(self, context, event, tweak): self.clear_cache() - self.update_header_text(context) self.update_prop_value(event, tweak) + self.update_header_text(context) + self.update_deform_wireframe() return {'RUNNING_MODAL'} def exit(self, context, cancel): diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index 8ded193ef..6fc468bc0 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -234,6 +234,7 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): self.set_prop_value(event) self.update_object_origin_matrix() + self.update_deform_wireframe() self.update_header_text(context) return self.event_handle(event) diff --git a/simple_deform_helper/operators.py b/simple_deform_helper/operators.py index 30c02b685..35cc5e89a 100644 --- a/simple_deform_helper/operators.py +++ b/simple_deform_helper/operators.py @@ -26,7 +26,7 @@ class DeformAxisOperator(Operator, GizmoUtils): return {'RUNNING_MODAL'} def modal(self, context, event): - from gizmo.angle_and_factor import GizmoUtils + from .gizmo.angle_and_factor import GizmoUtils mod = context.object.modifiers.active mod.deform_axis = self.Deform_Axis diff --git a/simple_deform_helper/panel.py b/simple_deform_helper/panel.py index 3b840b0b2..2d453cd2a 100644 --- a/simple_deform_helper/panel.py +++ b/simple_deform_helper/panel.py @@ -33,6 +33,12 @@ class SimpleDeformHelperToolPanel(Panel, GizmoUtils): layout.prop(ctrl_obj, 'origin_mode', text='') + + layout.prop(pref, + 'update_deform_wireframe', + icon='MOD_WIREFRAME', + text='Deform Wireframe') + layout.prop(pref, 'modifiers_limits_tolerance', text='') diff --git a/simple_deform_helper/preferences.py b/simple_deform_helper/preferences.py index 8656ac8ec..4b23b5738 100644 --- a/simple_deform_helper/preferences.py +++ b/simple_deform_helper/preferences.py @@ -52,6 +52,10 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): default=False, options={'SKIP_SAVE'}) + update_deform_wireframe: BoolProperty( + name='Update Deform Wireframe', + default=True) + def draw(self, context): col = self.layout.column() col.prop(self, 'deform_wireframe_color') @@ -59,6 +63,7 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): col.prop(self, 'limits_bound_box_color') col.prop(self, 'modifiers_limits_tolerance') col.prop(self, 'display_bend_axis_switch_gizmo') + col.prop(self, 'update_deform_wireframe', icon='MOD_WIREFRAME', ) def draw_header_tool_settings(self, context): if GizmoUtils.simple_deform_public_poll(context): diff --git a/simple_deform_helper/update.py b/simple_deform_helper/update.py index c584669e0..4a101c3a7 100644 --- a/simple_deform_helper/update.py +++ b/simple_deform_helper/update.py @@ -11,7 +11,7 @@ def remove_not_use_empty(scene, dep): """循环场景内的所有物体,找出没用的空物体并删掉 """ GizmoUpdate.clear_cache() - remove_name: str = "ViewSimpleDeformGizmo__Empty_" + remove_name: str = "ViewSimpleDeformGizmo_" context = bpy.context if GizmoUpdate.simple_deform_modifier_is_simple(context): for obj in context.scene.objects: diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index 540ce1c2e..dd987285d 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -15,7 +15,7 @@ class PublicData: """Public data class, all fixed data will be placed here """ G_CustomShape = {} - G_GizmoData = {} + G_GizmoData = {'modifiers_co': {}} G_INDICES = ( (0, 1), (0, 2), (1, 3), (2, 3), (4, 5), (4, 6), (5, 7), (6, 7), @@ -393,6 +393,7 @@ class PublicProperty(GizmoClassMethod): @classmethod def clear_data(cls): cls.G_GizmoData.clear() + cls.G_GizmoData['modifiers_co'] = {} # --------------- Cache Data ---------------------- @@ -566,10 +567,10 @@ class GizmoUpdate(PublicProperty): origin_object.scale = 1, 1, 1 def update_object_origin_matrix(self): - self.clear_data() + self.clear_cache() origin_mode = self.origin_mode empty_object = self.modifier.origin - if empty_object and self.modifier_is_use_origin_axis : + if empty_object and self.modifier_is_use_origin_axis: if origin_mode == 'UP_LIMITS': empty_object.matrix_world.translation = Vector(self.point_limits_up) elif origin_mode == 'DOWN_LIMITS': @@ -582,46 +583,42 @@ class GizmoUpdate(PublicProperty): empty_object.matrix_world.translation = translation def update_deform_wireframe(self): + if not self.pref.update_deform_wireframe: + return context = bpy.context data = bpy.data + obj = self.obj + matrix = obj.matrix_world.copy() # 物体矩阵 # add simple_deform mesh - (min_x, min_y, min_z), (max_x, max_y, - max_z) = cls.get_mesh_max_min_co(obj) - vertexes = ((max_x, min_y, min_z), - (min_x, min_y, min_z), - (max_x, max_y, min_z), - (min_x, max_y, min_z), - (max_x, min_y, max_z), - (min_x, min_y, max_z), - (max_x, max_y, max_z), - (min_x, max_y, max_z)) - name = cls.G_NAME - if data.objects.get(name): - data.objects.remove(data.objects.get(name)) + name = self.G_NAME - if data.meshes.get(name): - data.meshes.remove(data.meshes.get(name)) - mesh = data.meshes.new(name) - mesh.from_pydata(vertexes, cls.G_INDICES, []) - mesh.update() + vertices = self.matrix_calculation(self.obj_matrix_world.inverted(), self.modifier_limits_bound_box) + new_mesh = data.meshes.new(name) + new_mesh.from_pydata(vertices, self.G_INDICES, []) + new_mesh.update() - new_object = data.objects.new(name, mesh) + deform_obj = data.objects.get(name, None) + if deform_obj and deform_obj.type == 'MESH': + deform_obj.data = new_mesh + else: + if deform_obj: + data.objects.remove(deform_obj) + deform_obj = data.objects.new(name, new_mesh) - cls.link_obj_to_active_collection(new_object) + self.link_obj_to_active_collection(deform_obj) + if deform_obj == obj: + return + if deform_obj.parent != obj: + deform_obj.parent = obj - if new_object.parent != obj: - new_object.parent = obj - - new_object.modifiers.clear() - subdivision = new_object.modifiers.new('1', 'SUBSURF') + deform_obj.modifiers.clear() + subdivision = deform_obj.modifiers.new('1', 'SUBSURF') subdivision.levels = 7 - cls.G_GizmoData['modifiers_co'] = {} - cls.G_GizmoData['modifiers_co']['co'] = ( - min_x, min_y, min_z), (max_x, max_y, max_z) + self.G_GizmoData['modifiers_co']['co'] = self.get_bound_co_data() for mo in context.object.modifiers: if mo.type == 'SIMPLE_DEFORM': - simple_deform = new_object.modifiers.new( + simple_deform = deform_obj.modifiers.new( mo.name, 'SIMPLE_DEFORM') simple_deform.deform_method = mo.deform_method simple_deform.deform_axis = mo.deform_axis @@ -629,26 +626,26 @@ class GizmoUpdate(PublicProperty): simple_deform.lock_y = mo.lock_y simple_deform.lock_z = mo.lock_z simple_deform.origin = mo.origin - simple_deform.limits[1] = mo.limits[1] - simple_deform.limits[0] = mo.limits[0] + # simple_deform.limits[1] = mo.limits[1] + # simple_deform.limits[0] = mo.limits[0] simple_deform.angle = mo.angle simple_deform.show_viewport = mo.show_viewport - obj = PublicUtils.get_depsgraph(new_object) - cls.G_GizmoData['modifiers_co'][mo.name] = cls.get_mesh_max_min_co( + obj = PublicUtils.get_depsgraph(deform_obj) + self.G_GizmoData['modifiers_co'][mo.name] = self.get_mesh_max_min_co( obj) - new_object.hide_set(True) - new_object.hide_viewport = False - new_object.hide_select = True - new_object.hide_render = True - new_object.hide_viewport = True - new_object.hide_set(True) + # deform_obj.hide_set(True) + # deform_obj.hide_viewport = False + # deform_obj.hide_select = True + # deform_obj.hide_render = True + # deform_obj.hide_viewport = True + # deform_obj.hide_set(True) ver_len = obj.data.vertices.__len__() edge_len = obj.data.edges.__len__() - if 'numpy_data' not in cls.G_GizmoData: - cls.G_GizmoData['numpy_data'] = {} + if 'numpy_data' not in self.G_GizmoData: + self.G_GizmoData['numpy_data'] = {} - numpy_data = cls.G_GizmoData['numpy_data'] + numpy_data = self.G_GizmoData['numpy_data'] key = (ver_len, edge_len) if key in numpy_data: list_edges, list_vertices = numpy_data[key] @@ -669,9 +666,8 @@ class GizmoUpdate(PublicProperty): limits = context.object.modifiers.active.limits[:] modifiers = [getattr(context.object.modifiers.active, i) - for i in cls.G_MODIFIERS_PROPERTY] - - cls.G_GizmoData['draw'] = (ver, indices, matrix, modifiers, limits) + for i in self.G_MODIFIERS_PROPERTY] + self.G_GizmoData['draw'] = (ver, indices, matrix, modifiers, limits) class GizmoUtils(GizmoUpdate): -- 2.30.2 From 9c5a8ebaf349fef52a66c1d149daef5b8ba8a0db Mon Sep 17 00:00:00 2001 From: EMM <3209970865@qq.com> Date: Fri, 31 Mar 2023 12:04:08 +0800 Subject: [PATCH 07/12] draw_deform_mesh update_multiple_modifiers_data --- simple_deform_helper/draw.py | 21 ++--- .../gizmo/angle_and_factor.py | 5 +- simple_deform_helper/gizmo/bend_axis.py | 4 +- simple_deform_helper/gizmo/set_deform_axis.py | 9 +- .../gizmo/up_down_limits_point.py | 26 +----- simple_deform_helper/operators.py | 10 +-- simple_deform_helper/panel.py | 13 +-- simple_deform_helper/preferences.py | 9 +- simple_deform_helper/update.py | 2 +- simple_deform_helper/utils.py | 89 +++++++++++-------- 10 files changed, 93 insertions(+), 95 deletions(-) diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index 3a3c710bf..321ef4046 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -17,10 +17,10 @@ class Handler: @classmethod def del_handler_text(cls): - if 'handler_text' in cls.G_GizmoData: + if 'scale_text' in cls.G_GizmoData: bpy.types.SpaceView3D.draw_handler_remove( - cls.G_GizmoData['handler_text'], 'WINDOW') - cls.G_GizmoData.pop('handler_text') + cls.G_GizmoData['scale_text'], 'WINDOW') + cls.G_GizmoData.pop('scale_text') @classmethod def del_handler(cls): @@ -75,8 +75,6 @@ class DrawText: f'The scaling value of the object {obj.name_full} is not 1,' f' which will cause the deformation of the simple deformation modifier.' f' Please apply the scaling before deformation') - if obj.scale == Vector((1, 1, 1)): - cls.del_handler_text() @classmethod def draw_text(cls, x, y, text='Hello Word', font_id=0, size=10, *, color=(0.5, 0.5, 0.5, 1), dpi=72, column=0): @@ -105,7 +103,7 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): self.draw_scale_text(obj) if not self.modifier_origin_angle_is_available: self.draw_bound_box() - if self.simple_deform_show_gizmo_poll(context): + elif self.simple_deform_show_gizmo_poll(context): # draw bound box self.draw_bound_box() self.draw_deform_mesh() @@ -113,11 +111,10 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): self.draw_limits_bound_box() elif self.simple_deform_show_bend_axis_witch_poll(context): self.draw_bound_box() - # self.new_empty(obj, modifier) def draw_bound_box(self): coords = self.matrix_calculation(self.obj_matrix_world, - self.tow_co_to_coordinate(self.get_bound_co_data())) + self.tow_co_to_coordinate(self.modifier_bound_co)) self.draw_3d_shader(coords, self.G_INDICES, self.pref.bound_box_color) def draw_limits_bound_box(self): @@ -143,8 +140,8 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): handler_dit = self.G_GizmoData active = self.modifier # draw deform mesh - if 'draw' in handler_dit: - pos, indices, mat, mod_data, limits = handler_dit['draw'] + if 'simple_deform_box_data' in handler_dit and self.pref.update_deform_wireframe: + pos, indices, mat, mod_data, limits = handler_dit['simple_deform_box_data'] if ([getattr(active, i) for i in self.G_MODIFIERS_PROPERTY] == mod_data) and ( ob.matrix_world == mat) and limits == active.limits[:]: self.draw_3d_shader( @@ -152,8 +149,8 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): def draw_scale_text(self, ob): scale_error = (ob.scale != Vector((1, 1, 1))) - if scale_error and ('handler_text' not in self.G_GizmoData): - self.G_GizmoData['handler_text'] = bpy.types.SpaceView3D.draw_handler_add( + if scale_error and ('scale_text' not in self.G_GizmoData): + self.G_GizmoData['scale_text'] = bpy.types.SpaceView3D.draw_handler_add( self.draw_str, (), 'WINDOW', 'POST_PIXEL') def draw_origin_error(self): diff --git a/simple_deform_helper/gizmo/angle_and_factor.py b/simple_deform_helper/gizmo/angle_and_factor.py index 692dd7352..b565a58e9 100644 --- a/simple_deform_helper/gizmo/angle_and_factor.py +++ b/simple_deform_helper/gizmo/angle_and_factor.py @@ -19,12 +19,11 @@ class AngleUpdate(GizmoUtils): new_value = (self.get_snap(value, tweak)) old_value = math.degrees(self.target_get_value('angle')) - print(new_value, old_value) self.target_set_value('angle', math.radians(new_value)) def update_gizmo_matrix(self, context): matrix = context.object.matrix_world - point = self.get_bound_co_data()[1] + point = self.modifier_bound_co[1] self.matrix_basis = self.obj_matrix_world self.matrix_basis.translation = matrix @ point @@ -72,7 +71,7 @@ class AngleGizmo(Gizmo, AngleUpdate): self.update_prop_value(event, tweak) self.update_header_text(context) - self.update_deform_wireframe() + self.update_multiple_modifiers_data() return {'RUNNING_MODAL'} def exit(self, context, cancel): diff --git a/simple_deform_helper/gizmo/bend_axis.py b/simple_deform_helper/gizmo/bend_axis.py index 0c91505fd..0ff66599b 100644 --- a/simple_deform_helper/gizmo/bend_axis.py +++ b/simple_deform_helper/gizmo/bend_axis.py @@ -53,7 +53,7 @@ class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils): _color_b = 0, 1, 0 for na, axis, rot, positive in ( - ('top_a', 'X', (math.radians(90), 0, math.radians(9 - 0)), True), + ('top_a', 'X', (math.radians(90), 0, math.radians(90)), True), ('top_b', 'X', (math.radians(90), 0, 0), True), ('bottom_a', 'X', (math.radians(90), 0, math.radians(90)), False), @@ -93,7 +93,7 @@ class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils): def draw_prepare(self, context): ob = context.object mat = ob.matrix_world - top, bottom, left, right, front, back = self.each_face_pos + top, bottom, left, right, front, back = self.modifier_bound_box_pos rad = math.radians for_list = ( diff --git a/simple_deform_helper/gizmo/set_deform_axis.py b/simple_deform_helper/gizmo/set_deform_axis.py index fc780b8d2..3865e9f75 100644 --- a/simple_deform_helper/gizmo/set_deform_axis.py +++ b/simple_deform_helper/gizmo/set_deform_axis.py @@ -10,7 +10,7 @@ class SetDeformGizmoGroup(GizmoGroup, GizmoGroupUtils): @classmethod def poll(cls, context): - return cls.simple_deform_show_gizmo_poll(context) + return cls.simple_deform_show_gizmo_poll(context) and cls.pref_().show_set_axis_button def setup(self, context): data_path = 'object.modifiers.active.deform_axis' @@ -36,10 +36,13 @@ class SetDeformGizmoGroup(GizmoGroup, GizmoGroupUtils): def draw_prepare(self, context): if 'co' in self.G_GizmoData: + obj = self.get_depsgraph(self.obj) + dimensions = obj.dimensions + def _mat(f): co = self.G_GizmoData['co'][0] - co = (co[0] + (max(self.obj.dimensions) * f), co[1], - co[2] - (min(self.obj.dimensions) * 0.3)) + co = (co[0] + (max(dimensions) * f), co[1], + co[2] - (min(dimensions) * 0.3)) return self.obj_matrix_world @ Vector(co) self.deform_axis_x.matrix_basis.translation = _mat(0) diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index 6fc468bc0..1dc332c94 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -147,30 +147,6 @@ class GizmoUpdate(GizmoProperty): text += t('Down limit', value) context.area.header_text_set(text) - # def update_modifiers_origin_matrix(self): - # ob = bpy.context.object - # for mod in ob.modifiers: - # if mod.type == 'SIMPLE_DEFORM': - # self.update_matrix(mod, ob) - # - # def update_matrix(self, mod, ob): - # # if mod.deform_method == 'BEND': - # # cls.new_empty(ob, mod) - # origin_object = mod.origin - # if mod.origin: - # modifiers_co = self.G_GizmoData['modifiers_co'] - # for index, mod_name in enumerate(modifiers_co): - # co_items = list(modifiers_co.items()) - # if mod.name == mod_name: - # data = co_items[index - 1][1] if ( - # index or (index != 1)) else modifiers_co['co'] - # (up, down), (up_, down_) = self.get_limits_pos( - # mod, self.co_to_direction(ob.matrix_world.copy(), data)) - # origin_mode = self.obj_origin_property_group( - # mod, ob).origin_mode - # self.set_empty_obj_matrix( - # origin_mode, origin_object, up_, down_, up, down) - class UpDownLimitsGizmo(Gizmo, GizmoUpdate): bl_idname = 'UpDownLimitsGizmo' @@ -234,7 +210,7 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): self.set_prop_value(event) self.update_object_origin_matrix() - self.update_deform_wireframe() + self.update_multiple_modifiers_data() self.update_header_text(context) return self.event_handle(event) diff --git a/simple_deform_helper/operators.py b/simple_deform_helper/operators.py index 35cc5e89a..3f7fe2411 100644 --- a/simple_deform_helper/operators.py +++ b/simple_deform_helper/operators.py @@ -26,12 +26,11 @@ class DeformAxisOperator(Operator, GizmoUtils): return {'RUNNING_MODAL'} def modal(self, context, event): - from .gizmo.angle_and_factor import GizmoUtils - + self.clear_cache() mod = context.object.modifiers.active mod.deform_axis = self.Deform_Axis - empty, con_limit_name = GizmoUtils.new_origin_empty_object(context.object, mod) - is_positive = GizmoUtils.is_positive(mod.angle) + empty = self.new_origin_empty_object() + is_positive = self.is_positive(mod.angle) for limit, value in (('max_x', self.X_Value), ('min_x', self.X_Value), @@ -40,13 +39,14 @@ class DeformAxisOperator(Operator, GizmoUtils): ('max_z', self.Z_Value), ('min_z', self.Z_Value), ): - setattr(empty.constraints[con_limit_name], limit, value) + setattr(empty.constraints[self.G_CON_LIMIT_NAME], limit, value) if ((not is_positive) and self.Is_Positive) or (is_positive and (not self.Is_Positive)): mod.angle = mod.angle * -1 if not event.ctrl: self.pref.display_bend_axis_switch_gizmo = False + # self.new_origin_empty_object() return {'FINISHED'} diff --git a/simple_deform_helper/panel.py b/simple_deform_helper/panel.py index 2d453cd2a..c4f8e38bb 100644 --- a/simple_deform_helper/panel.py +++ b/simple_deform_helper/panel.py @@ -33,20 +33,21 @@ class SimpleDeformHelperToolPanel(Panel, GizmoUtils): layout.prop(ctrl_obj, 'origin_mode', text='') - layout.prop(pref, 'update_deform_wireframe', icon='MOD_WIREFRAME', - text='Deform Wireframe') - - layout.prop(pref, - 'modifiers_limits_tolerance', text='') - + layout.prop(pref, + 'show_set_axis_button', + icon='EMPTY_AXIS', + text='') if mod.deform_method == 'BEND': layout.prop(pref, 'display_bend_axis_switch_gizmo', toggle=1) + layout.prop(pref, + 'modifiers_limits_tolerance', + text='') class_list = ( diff --git a/simple_deform_helper/preferences.py b/simple_deform_helper/preferences.py index 4b23b5738..02b8c9b55 100644 --- a/simple_deform_helper/preferences.py +++ b/simple_deform_helper/preferences.py @@ -53,8 +53,12 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): options={'SKIP_SAVE'}) update_deform_wireframe: BoolProperty( - name='Update Deform Wireframe', - default=True) + name='Show Deform Wireframe', + default=False) + + show_set_axis_button: BoolProperty( + name='Show Set Axis Button', + default=False) def draw(self, context): col = self.layout.column() @@ -64,6 +68,7 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): col.prop(self, 'modifiers_limits_tolerance') col.prop(self, 'display_bend_axis_switch_gizmo') col.prop(self, 'update_deform_wireframe', icon='MOD_WIREFRAME', ) + col.prop(self, 'show_set_axis_button', icon='EMPTY_AXIS', ) def draw_header_tool_settings(self, context): if GizmoUtils.simple_deform_public_poll(context): diff --git a/simple_deform_helper/update.py b/simple_deform_helper/update.py index 4a101c3a7..98d5edea0 100644 --- a/simple_deform_helper/update.py +++ b/simple_deform_helper/update.py @@ -10,10 +10,10 @@ from .utils import GizmoUpdate def remove_not_use_empty(scene, dep): """循环场景内的所有物体,找出没用的空物体并删掉 """ - GizmoUpdate.clear_cache() remove_name: str = "ViewSimpleDeformGizmo_" context = bpy.context if GizmoUpdate.simple_deform_modifier_is_simple(context): + GizmoUpdate.clear_cache() for obj in context.scene.objects: is_empty = obj.type == "EMPTY" not_parent = not obj.parent diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index dd987285d..b4eee09f5 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -115,7 +115,8 @@ class PublicPoll(PublicClass): is_available_obj = available_modifiers_type and available_obj_type is_obj_mode = context.mode == 'OBJECT' show_mod = mod.show_viewport - return is_available_obj and is_obj_mode and show_mod + not_is_self_mesh = obj.name != cls.G_NAME + return is_available_obj and is_obj_mode and show_mod and not_is_self_mesh @classmethod def simple_deform_public_poll(cls, context: 'bpy.types.context') -> bool: @@ -228,7 +229,7 @@ class PublicUtils(PublicPoll): list_vertices = np.zeros(ver_len * 3, dtype=np.float32) obj.data.points.foreach_get('co', list_vertices) list_vertices = list_vertices.reshape(ver_len, 3) - return Vector(list_vertices.min(axis=0)), Vector(list_vertices.max(axis=0)) + return Vector(list_vertices.min(axis=0)).freeze(), Vector(list_vertices.max(axis=0)).freeze() @classmethod def matrix_calculation(cls, mat: 'Matrix', calculation_list: 'list') -> list: @@ -326,7 +327,7 @@ class PublicProperty(GizmoClassMethod): @cache def _get_limits_point_and_bound_box_co(self): - top, bottom, left, right, front, back = self.each_face_pos + top, bottom, left, right, front, back = self.modifier_bound_box_pos mod = self.modifier g_l = self.__from_up_down_point_get_limits_point if self.modifier.origin: @@ -381,14 +382,13 @@ class PublicProperty(GizmoClassMethod): # ---------------------- @cache - def _each_face_pos(self, mat): - return self.co_to_direction(mat, self.get_bound_co_data()) + def _each_face_pos(self, mat, co): + return self.co_to_direction(mat, co) @classmethod def clear_cache(cls): cls._each_face_pos.cache_clear() cls._get_limits_point_and_bound_box_co.cache_clear() - cls.clear_data() @classmethod def clear_data(cls): @@ -399,7 +399,15 @@ class PublicProperty(GizmoClassMethod): @property def each_face_pos(self): - return self._each_face_pos(self.obj_matrix_world) + return self._each_face_pos(self.obj_matrix_world, self.get_bound_co_data()) + + @property + def modifier_bound_co(self): + return self.G_GizmoData['modifiers_co'].get(self.modifier.name, self.get_bound_co_data()) + + @property + def modifier_bound_box_pos(self): + return self._each_face_pos(self.obj_matrix_world, self.modifier_bound_co) @property def modifier_limits_point(self): @@ -417,6 +425,7 @@ class PublicProperty(GizmoClassMethod): self._get_limits_point_and_bound_box_co() return True except UnboundLocalError: + self.clear_cache() return False # --------------- Compute Data ---------------------- @@ -532,13 +541,14 @@ class GizmoUpdate(PublicProperty): else: origin_object = mod.origin origin_object.hide_viewport = False - + if origin_object == obj: + return if origin_object.parent != obj: origin_object.parent = obj # add constraints name = self.G_CON_LIMIT_NAME - if origin_object.constraints.keys().__len__() > 1: + if origin_object.constraints.keys().__len__() > 2: origin_object.constraints.clear() if name in origin_object.constraints.keys(): limit_constraints = origin.constraints.get(name) @@ -566,6 +576,8 @@ class GizmoUpdate(PublicProperty): origin_object.rotation_euler.zero() origin_object.scale = 1, 1, 1 + return origin_object + def update_object_origin_matrix(self): self.clear_cache() origin_mode = self.origin_mode @@ -582,29 +594,26 @@ class GizmoUpdate(PublicProperty): translation = (self.point_up + self.point_down) / 2 empty_object.matrix_world.translation = translation - def update_deform_wireframe(self): - if not self.pref.update_deform_wireframe: - return - context = bpy.context - data = bpy.data + def update_multiple_modifiers_data(self): + self.clear_data() obj = self.obj + data = bpy.data + context = bpy.context - matrix = obj.matrix_world.copy() # 物体矩阵 # add simple_deform mesh name = self.G_NAME - vertices = self.matrix_calculation(self.obj_matrix_world.inverted(), self.modifier_limits_bound_box) + if data.objects.get(name): + data.objects.remove(data.objects.get(name)) + + if data.meshes.get(name): + data.meshes.remove(data.meshes.get(name)) + + vertices = self.tow_co_to_coordinate(self.get_bound_co_data()) new_mesh = data.meshes.new(name) new_mesh.from_pydata(vertices, self.G_INDICES, []) new_mesh.update() - - deform_obj = data.objects.get(name, None) - if deform_obj and deform_obj.type == 'MESH': - deform_obj.data = new_mesh - else: - if deform_obj: - data.objects.remove(deform_obj) - deform_obj = data.objects.new(name, new_mesh) + deform_obj = data.objects.new(name, new_mesh) self.link_obj_to_active_collection(deform_obj) if deform_obj == obj: @@ -616,8 +625,12 @@ class GizmoUpdate(PublicProperty): subdivision = deform_obj.modifiers.new('1', 'SUBSURF') subdivision.levels = 7 self.G_GizmoData['modifiers_co']['co'] = self.get_bound_co_data() + for mo in context.object.modifiers: if mo.type == 'SIMPLE_DEFORM': + obj = self.get_depsgraph(deform_obj) + self.G_GizmoData['modifiers_co'][mo.name] = self.get_mesh_max_min_co( + obj) simple_deform = deform_obj.modifiers.new( mo.name, 'SIMPLE_DEFORM') simple_deform.deform_method = mo.deform_method @@ -626,19 +639,23 @@ class GizmoUpdate(PublicProperty): simple_deform.lock_y = mo.lock_y simple_deform.lock_z = mo.lock_z simple_deform.origin = mo.origin - # simple_deform.limits[1] = mo.limits[1] - # simple_deform.limits[0] = mo.limits[0] + simple_deform.limits[1] = mo.limits[1] + simple_deform.limits[0] = mo.limits[0] simple_deform.angle = mo.angle simple_deform.show_viewport = mo.show_viewport - obj = PublicUtils.get_depsgraph(deform_obj) - self.G_GizmoData['modifiers_co'][mo.name] = self.get_mesh_max_min_co( - obj) - # deform_obj.hide_set(True) - # deform_obj.hide_viewport = False - # deform_obj.hide_select = True - # deform_obj.hide_render = True - # deform_obj.hide_viewport = True - # deform_obj.hide_set(True) + deform_obj.hide_select = True + deform_obj.hide_set(True) + deform_obj.hide_viewport = False + self.update_deform_wireframe(self.get_depsgraph(deform_obj)) + deform_obj.hide_render = True + deform_obj.hide_viewport = True + deform_obj.hide_set(True) + + def update_deform_wireframe(self, obj): + if not self.pref.update_deform_wireframe: + return + context = bpy.context + matrix = self.obj_matrix_world.copy() ver_len = obj.data.vertices.__len__() edge_len = obj.data.edges.__len__() @@ -667,7 +684,7 @@ class GizmoUpdate(PublicProperty): limits = context.object.modifiers.active.limits[:] modifiers = [getattr(context.object.modifiers.active, i) for i in self.G_MODIFIERS_PROPERTY] - self.G_GizmoData['draw'] = (ver, indices, matrix, modifiers, limits) + self.G_GizmoData['simple_deform_box_data'] = (ver, indices, matrix, modifiers, limits[:]) class GizmoUtils(GizmoUpdate): -- 2.30.2 From df890375c982b6cc50f9243f99ed8f232531d658 Mon Sep 17 00:00:00 2001 From: EMM <3209970865@qq.com> Date: Fri, 31 Mar 2023 19:10:12 +0800 Subject: [PATCH 08/12] snap angle value fix assign a custom origin object update translate update gizmo.json --- simple_deform_helper/draw.py | 6 +- simple_deform_helper/gizmo.json | 2 +- .../gizmo/angle_and_factor.py | 82 ++++++++++++++----- simple_deform_helper/gizmo/bend_axis.py | 5 +- .../gizmo/up_down_limits_point.py | 6 +- simple_deform_helper/preferences.py | 2 +- simple_deform_helper/translate.py | 6 +- simple_deform_helper/update.py | 8 +- simple_deform_helper/utils.py | 73 ++++++++--------- 9 files changed, 116 insertions(+), 74 deletions(-) diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index 321ef4046..af9d7cf7e 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -98,9 +98,7 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): self.draw_3d(context) def draw_3d(self, context): - obj = context.object # 活动物体 - - self.draw_scale_text(obj) + self.draw_scale_text(self.obj) if not self.modifier_origin_angle_is_available: self.draw_bound_box() elif self.simple_deform_show_gizmo_poll(context): @@ -152,6 +150,8 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): if scale_error and ('scale_text' not in self.G_GizmoData): self.G_GizmoData['scale_text'] = bpy.types.SpaceView3D.draw_handler_add( self.draw_str, (), 'WINDOW', 'POST_PIXEL') + elif not scale_error: + self.del_handler_text() def draw_origin_error(self): ... diff --git a/simple_deform_helper/gizmo.json b/simple_deform_helper/gizmo.json index 5c5387d83..7f0694103 100644 --- a/simple_deform_helper/gizmo.json +++ b/simple_deform_helper/gizmo.json @@ -1 +1 @@ -{"SimpleDeform_GizmoGroup_": [[-0.5, 0.1, -1.0], [-0.5, -0.8, -0.2], [-0.5, 0.2, -0.6], [0.5, 0.4, -0.4], [-0.5, 1.0, -0.1], [0.5, 1.0, -0.1], [0.3, -0.3, 0.5], [0.5, -0.4, 0.4], [0.5, 1.0, 1.0], [0.5, -1.0, -1.0], [-0.5, 0.1, -1.0], [0.5, 0.1, -1.0], [0.5, 1.0, -0.1], [-0.5, 1.0, 1.0], [0.5, 1.0, 1.0], [-0.5, 0.2, -0.6], [-0.5, -0.4, 0.4], [-0.5, 0.4, -0.4], [0.5, 0.2, -0.6], [-0.5, 0.4, -0.4], [0.5, 0.4, -0.4], [0.3, -0.9, -0.7], [-0.3, -0.9, -0.7], [-0.5, -1.0, -1.0], [0.5, 0.1, -1.0], [-0.5, 0.2, -0.6], [0.5, 0.2, -0.6], [0.3, -0.9, 1.0], [0.3, -0.9, -0.7], [0.3, -0.8, -0.2], [0.3, -0.9, -0.7], [0.3, -0.9, 1.0], [-0.3, -0.9, -0.7], [-0.3, -0.3, 0.5], [0.3, -0.3, 1.0], [0.3, -0.3, 0.5], [0.5, -0.4, 0.4], [0.5, -0.8, -0.2], [0.5, 0.4, -0.4], [0.3, -0.8, -0.2], [0.5, -0.4, 0.4], [0.3, -0.3, 0.5], [-0.5, -0.4, 0.4], [-0.3, -0.8, -0.2], [-0.3, -0.3, 0.5], [-0.5, -0.4, 0.4], [-0.5, 1.0, -0.1], [-0.5, 0.4, -0.4], [0.3, -0.9, 1.0], [-0.3, -0.3, 0.5], [-0.3, -0.8, -0.2], [-0.5, 0.1, -1.0], [-0.5, -1.0, -1.0], [-0.5, -0.8, -0.2], [0.5, 0.4, -0.4], [-0.5, 0.4, -0.4], [-0.5, 1.0, -0.1], [0.5, 1.0, 1.0], [-0.5, 1.0, 1.0], [0.3, -0.3, 0.5], [-0.5, 1.0, 1.0], [-0.5, -0.4, 0.4], [-0.3, -0.3, 0.5], [-0.5, 1.0, 1.0], [-0.3, -0.3, 0.5], [0.3, -0.3, 0.5], [0.5, -1.0, -1.0], [-0.5, -1.0, -1.0], [-0.5, 0.1, -1.0], [0.5, 1.0, -0.1], [-0.5, 1.0, -0.1], [-0.5, 1.0, 1.0], [-0.5, 0.2, -0.6], [-0.5, -0.8, -0.2], [-0.5, -0.4, 0.4], [0.5, 0.2, -0.6], [-0.5, 0.2, -0.6], [-0.5, 0.4, -0.4], [0.5, -1.0, -1.0], [0.5, -0.8, -0.2], [0.3, -0.9, -0.7], [0.5, -0.8, -0.2], [0.3, -0.8, -0.2], [0.3, -0.9, -0.7], [-0.3, -0.8, -0.2], [-0.5, -0.8, -0.2], [-0.3, -0.9, -0.7], [-0.5, -0.8, -0.2], [-0.5, -1.0, -1.0], [-0.3, -0.9, -0.7], [0.5, -1.0, -1.0], [0.3, -0.9, -0.7], [-0.5, -1.0, -1.0], [0.5, 0.1, -1.0], [-0.5, 0.1, -1.0], [-0.5, 0.2, -0.6], [0.3, -0.8, -0.2], [0.3, -0.3, 0.5], [0.3, -0.9, 1.0], [0.3, -0.3, 0.5], [0.3, -0.3, 1.0], [0.3, -0.9, 1.0], [0.5, -1.0, -1.0], [0.5, 0.1, -1.0], [0.5, 0.2, -0.6], [0.5, 0.4, -0.4], [0.5, 1.0, -0.1], [0.5, -0.4, 0.4], [0.5, 1.0, -0.1], [0.5, 1.0, 1.0], [0.5, -0.4, 0.4], [0.5, -1.0, -1.0], [0.5, 0.2, -0.6], [0.5, -0.8, -0.2], [0.5, 0.2, -0.6], [0.5, 0.4, -0.4], [0.5, -0.8, -0.2], [0.3, -0.8, -0.2], [0.5, -0.8, -0.2], [0.5, -0.4, 0.4], [-0.5, -0.4, 0.4], [-0.5, -0.8, -0.2], [-0.3, -0.8, -0.2], [-0.5, -0.4, 0.4], [-0.5, 1.0, 1.0], [-0.5, 1.0, -0.1], [-0.3, -0.8, -0.2], [-0.3, -0.9, -0.7], [0.3, -0.9, 1.0], [0.3, -0.9, 1.0], [0.3, -0.3, 1.0], [-0.3, -0.3, 0.5]], "None_GizmoGroup_": [[1.0, -0.0, 0.2], [1.0, -0.0, -0.2], [1.0, -0.0, -0.0], [0.5, -0.0, 0.2], [0.2, -0.0, 0.2], [0.2, -0.0, -0.2], [-0.6, -0.0, 0.2], [-1.0, -0.0, 0.2], [-0.9, -0.0, -0.2], [-0.2, -0.0, 0.3], [-0.1, -0.0, 0.2], [-0.1, -0.0, 0.2], [-0.4, -0.0, 0.1], [-0.3, -0.0, 0.2], [-0.3, -0.0, 0.2], [0.1, -0.0, -0.0], [-0.0, -0.0, -0.1], [0.0, -0.0, -0.1], [-0.2, -0.0, -0.3], [-0.3, 0.0, -0.2], [-0.3, 0.0, -0.2], [-0.4, -0.0, 0.1], [-0.4, -0.0, -0.0], [-0.4, -0.0, 0.1], [0.1, -0.0, -0.0], [-0.0, -0.0, 0.1], [0.0, -0.0, -0.0], [-0.1, 0.0, -0.2], [-0.2, -0.0, -0.2], [-0.2, -0.0, -0.3], [-0.4, -0.0, -0.1], [-0.4, -0.0, -0.0], [-0.4, -0.0, -0.0], [-0.1, -0.0, 0.2], [-0.0, -0.0, 0.1], [0.0, -0.0, 0.1], [-0.3, -0.0, 0.2], [-0.2, -0.0, 0.2], [-0.2, -0.0, 0.3], [0.0, -0.0, -0.1], [-0.1, 0.0, -0.2], [-0.1, 0.0, -0.2], [-0.4, -0.0, -0.1], [-0.3, 0.0, -0.2], [-0.4, -0.0, -0.1], [-0.2, -0.0, 0.3], [-0.2, -0.0, 0.2], [-0.1, -0.0, 0.2], [-0.4, -0.0, 0.1], [-0.4, -0.0, 0.1], [-0.3, -0.0, 0.2], [0.1, -0.0, -0.0], [0.0, -0.0, -0.0], [-0.0, -0.0, -0.1], [-0.2, -0.0, -0.3], [-0.2, -0.0, -0.2], [-0.3, 0.0, -0.2], [-0.4, -0.0, 0.1], [-0.4, -0.0, -0.0], [-0.4, -0.0, -0.0], [0.1, -0.0, -0.0], [0.0, -0.0, 0.1], [-0.0, -0.0, 0.1], [-0.1, 0.0, -0.2], [-0.1, 0.0, -0.2], [-0.2, -0.0, -0.2], [-0.4, -0.0, -0.1], [-0.4, -0.0, -0.1], [-0.4, -0.0, -0.0], [-0.1, -0.0, 0.2], [-0.1, -0.0, 0.2], [-0.0, -0.0, 0.1], [-0.3, -0.0, 0.2], [-0.3, -0.0, 0.2], [-0.2, -0.0, 0.2], [0.0, -0.0, -0.1], [-0.0, -0.0, -0.1], [-0.1, 0.0, -0.2], [-0.4, -0.0, -0.1], [-0.3, 0.0, -0.2], [-0.3, 0.0, -0.2]], "SimpleDeform_Bend_Direction_": [[-2.5, 0.4, 1.0], [-3.0, -0.0, 1.7], [-2.5, -0.4, 1.0], [2.5, 0.4, 1.0], [2.5, -0.4, 1.0], [3.0, -0.0, 1.7], [1.3, 0.1, 0.4], [1.9, -0.1, 0.7], [1.9, 0.1, 0.7], [-2.5, -0.4, 1.0], [-1.3, 0.1, 0.4], [-2.5, 0.4, 1.0], [1.9, -0.1, 0.7], [2.5, 0.4, 1.0], [1.9, 0.1, 0.7], [0.0, 0.1, 0.3], [-1.3, -0.1, 0.4], [0.0, -0.1, 0.3], [1.3, -0.1, 0.4], [0.0, 0.1, 0.3], [0.0, -0.1, 0.3], [1.3, 0.1, 0.4], [1.3, -0.1, 0.4], [1.9, -0.1, 0.7], [-2.5, -0.4, 1.0], [-1.3, -0.1, 0.4], [-1.3, 0.1, 0.4], [1.9, -0.1, 0.7], [2.5, -0.4, 1.0], [2.5, 0.4, 1.0], [0.0, 0.1, 0.3], [-1.3, 0.1, 0.4], [-1.3, -0.1, 0.4], [1.3, -0.1, 0.4], [1.3, 0.1, 0.4], [0.0, 0.1, 0.3]], "Sphere_GizmoGroup_": [[0.7, -0.7, 0.0], [-0.7, 0.7, 0.0], [-0.7, -0.7, 0.0], [0.7, -0.7, 0.0], [0.7, 0.7, 0.0], [-0.7, 0.7, 0.0]]} \ No newline at end of file +{"SimpleDeform_GizmoGroup_": [[-0.54, 0.12, -0.96], [-0.54, -0.82, -0.23], [-0.54, 0.19, -0.64], [0.54, 0.37, -0.37], [-0.54, 0.96, -0.12], [0.54, 0.96, -0.12], [0.32, -0.29, 0.5], [0.54, -0.4, 0.4], [0.55, 0.96, 0.96], [0.54, -0.96, -0.96], [-0.54, 0.12, -0.96], [0.54, 0.12, -0.96], [0.54, 0.96, -0.12], [-0.54, 0.96, 0.96], [0.55, 0.96, 0.96], [-0.54, 0.19, -0.64], [-0.54, -0.4, 0.4], [-0.54, 0.37, -0.37], [0.54, 0.19, -0.64], [-0.54, 0.37, -0.37], [0.54, 0.37, -0.37], [0.32, -0.94, -0.69], [-0.33, -0.94, -0.69], [-0.54, -0.96, -0.96], [0.54, 0.12, -0.96], [-0.54, 0.19, -0.64], [0.54, 0.19, -0.64], [0.32, -0.94, 0.95], [0.32, -0.94, -0.69], [0.32, -0.81, -0.23], [0.32, -0.94, -0.69], [0.32, -0.94, 0.95], [-0.33, -0.94, -0.69], [-0.33, -0.29, 0.5], [0.32, -0.29, 0.95], [0.32, -0.29, 0.5], [0.54, -0.4, 0.4], [0.54, -0.81, -0.23], [0.54, 0.37, -0.37], [0.32, -0.81, -0.23], [0.54, -0.4, 0.4], [0.32, -0.29, 0.5], [-0.54, -0.4, 0.4], [-0.33, -0.81, -0.23], [-0.33, -0.29, 0.5], [-0.54, -0.4, 0.4], [-0.54, 0.96, -0.12], [-0.54, 0.37, -0.37], [0.32, -0.94, 0.95], [-0.33, -0.29, 0.5], [-0.33, -0.81, -0.23], [-0.54, 0.12, -0.96], [-0.54, -0.96, -0.96], [-0.54, -0.82, -0.23], [0.54, 0.37, -0.37], [-0.54, 0.37, -0.37], [-0.54, 0.96, -0.12], [0.55, 0.96, 0.96], [-0.54, 0.96, 0.96], [0.32, -0.29, 0.5], [-0.54, 0.96, 0.96], [-0.54, -0.4, 0.4], [-0.33, -0.29, 0.5], [-0.54, 0.96, 0.96], [-0.33, -0.29, 0.5], [0.32, -0.29, 0.5], [0.54, -0.96, -0.96], [-0.54, -0.96, -0.96], [-0.54, 0.12, -0.96], [0.54, 0.96, -0.12], [-0.54, 0.96, -0.12], [-0.54, 0.96, 0.96], [-0.54, 0.19, -0.64], [-0.54, -0.82, -0.23], [-0.54, -0.4, 0.4], [0.54, 0.19, -0.64], [-0.54, 0.19, -0.64], [-0.54, 0.37, -0.37], [0.54, -0.96, -0.96], [0.54, -0.81, -0.23], [0.32, -0.94, -0.69], [0.54, -0.81, -0.23], [0.32, -0.81, -0.23], [0.32, -0.94, -0.69], [-0.33, -0.81, -0.23], [-0.54, -0.82, -0.23], [-0.33, -0.94, -0.69], [-0.54, -0.82, -0.23], [-0.54, -0.96, -0.96], [-0.33, -0.94, -0.69], [0.54, -0.96, -0.96], [0.32, -0.94, -0.69], [-0.54, -0.96, -0.96], [0.54, 0.12, -0.96], [-0.54, 0.12, -0.96], [-0.54, 0.19, -0.64], [0.32, -0.81, -0.23], [0.32, -0.29, 0.5], [0.32, -0.94, 0.95], [0.32, -0.29, 0.5], [0.32, -0.29, 0.95], [0.32, -0.94, 0.95], [0.54, -0.96, -0.96], [0.54, 0.12, -0.96], [0.54, 0.19, -0.64], [0.54, 0.37, -0.37], [0.54, 0.96, -0.12], [0.54, -0.4, 0.4], [0.54, 0.96, -0.12], [0.55, 0.96, 0.96], [0.54, -0.4, 0.4], [0.54, -0.96, -0.96], [0.54, 0.19, -0.64], [0.54, -0.81, -0.23], [0.54, 0.19, -0.64], [0.54, 0.37, -0.37], [0.54, -0.81, -0.23], [0.32, -0.81, -0.23], [0.54, -0.81, -0.23], [0.54, -0.4, 0.4], [-0.54, -0.4, 0.4], [-0.54, -0.82, -0.23], [-0.33, -0.81, -0.23], [-0.54, -0.4, 0.4], [-0.54, 0.96, 0.96], [-0.54, 0.96, -0.12], [-0.33, -0.81, -0.23], [-0.33, -0.94, -0.69], [0.32, -0.94, 0.95], [0.32, -0.94, 0.95], [0.32, -0.29, 0.95], [-0.33, -0.29, 0.5]], "None_GizmoGroup_": [[0.97, -0.01, 0.18], [0.98, -0.01, -0.24], [0.96, -0.01, -0.03], [0.53, -0.01, 0.24], [0.15, -0.01, 0.24], [0.22, -0.01, -0.24], [-0.59, -0.01, 0.24], [-0.97, -0.01, 0.24], [-0.9, -0.01, -0.24], [-0.19, -0.0, 0.25], [-0.09, -0.0, 0.17], [-0.06, -0.0, 0.22], [-0.4, -0.0, 0.13], [-0.28, -0.0, 0.17], [-0.31, -0.0, 0.22], [0.07, -0.0, -0.0], [-0.02, -0.0, -0.1], [0.03, -0.0, -0.13], [-0.19, -0.0, -0.25], [-0.28, 0.0, -0.17], [-0.31, 0.0, -0.22], [-0.4, -0.0, 0.13], [-0.38, -0.0, -0.0], [-0.35, -0.0, 0.1], [0.07, -0.0, -0.0], [-0.02, -0.0, 0.1], [0.01, -0.0, -0.0], [-0.06, 0.0, -0.22], [-0.19, -0.0, -0.19], [-0.19, -0.0, -0.25], [-0.4, -0.0, -0.13], [-0.38, -0.0, -0.0], [-0.44, -0.0, -0.0], [-0.06, -0.0, 0.22], [-0.02, -0.0, 0.1], [0.03, -0.0, 0.13], [-0.31, -0.0, 0.22], [-0.19, -0.0, 0.19], [-0.19, -0.0, 0.25], [0.03, -0.0, -0.13], [-0.09, 0.0, -0.17], [-0.06, 0.0, -0.22], [-0.4, -0.0, -0.13], [-0.28, 0.0, -0.17], [-0.35, -0.0, -0.1], [-0.19, -0.0, 0.25], [-0.19, -0.0, 0.19], [-0.09, -0.0, 0.17], [-0.4, -0.0, 0.13], [-0.35, -0.0, 0.1], [-0.28, -0.0, 0.17], [0.07, -0.0, -0.0], [0.01, -0.0, -0.0], [-0.02, -0.0, -0.1], [-0.19, -0.0, -0.25], [-0.19, -0.0, -0.19], [-0.28, 0.0, -0.17], [-0.4, -0.0, 0.13], [-0.44, -0.0, -0.0], [-0.38, -0.0, -0.0], [0.07, -0.0, -0.0], [0.03, -0.0, 0.13], [-0.02, -0.0, 0.1], [-0.06, 0.0, -0.22], [-0.09, 0.0, -0.17], [-0.19, -0.0, -0.19], [-0.4, -0.0, -0.13], [-0.35, -0.0, -0.1], [-0.38, -0.0, -0.0], [-0.06, -0.0, 0.22], [-0.09, -0.0, 0.17], [-0.02, -0.0, 0.1], [-0.31, -0.0, 0.22], [-0.28, -0.0, 0.17], [-0.19, -0.0, 0.19], [0.03, -0.0, -0.13], [-0.02, -0.0, -0.1], [-0.09, 0.0, -0.17], [-0.4, -0.0, -0.13], [-0.31, 0.0, -0.22], [-0.28, 0.0, -0.17]], "SimpleDeform_Bend_Direction_": [[-3.04, -0.0, 1.68], [-2.79, -0.0, 1.35], [-2.79, 0.1, 1.35], [0.0, 0.0, 0.28], [-0.36, -0.03, 0.31], [0.0, -0.03, 0.28], [-2.22, 0.0, 0.87], [-1.9, 0.04, 0.71], [-2.22, 0.12, 0.87], [-2.22, 0.0, 0.87], [-1.9, -0.04, 0.71], [-1.9, 0.0, 0.71], [-3.04, -0.0, 1.68], [-2.79, -0.1, 1.35], [-2.79, -0.0, 1.35], [-2.79, 0.1, 1.35], [-2.51, 0.0, 1.06], [-2.51, 0.13, 1.06], [0.0, 0.03, 0.28], [-0.36, 0.0, 0.31], [0.0, 0.0, 0.28], [-1.9, 0.0, 0.71], [-1.43, -0.04, 0.53], [-1.43, 0.0, 0.53], [-1.43, 0.0, 0.53], [-0.86, -0.04, 0.38], [-0.86, 0.0, 0.38], [-0.86, 0.0, 0.38], [-0.36, -0.03, 0.31], [-0.36, 0.0, 0.31], [-1.9, 0.0, 0.71], [-1.43, 0.04, 0.53], [-1.9, 0.04, 0.71], [-1.43, 0.0, 0.53], [-0.86, 0.04, 0.38], [-1.43, 0.04, 0.53], [-0.86, 0.0, 0.38], [-0.36, 0.03, 0.31], [-0.86, 0.04, 0.38], [-2.22, 0.12, 0.87], [-2.51, 0.0, 1.06], [-2.22, 0.0, 0.87], [-2.79, -0.1, 1.35], [-2.51, 0.0, 1.06], [-2.79, -0.0, 1.35], [-2.22, -0.12, 0.87], [-2.51, 0.0, 1.06], [-2.51, -0.13, 1.06], [3.04, -0.0, 1.68], [2.79, 0.1, 1.35], [2.79, -0.0, 1.35], [0.36, -0.03, 0.31], [0.0, 0.0, 0.28], [0.0, -0.03, 0.28], [1.9, 0.04, 0.71], [2.22, 0.0, 0.87], [2.22, 0.12, 0.87], [2.22, 0.0, 0.87], [1.9, -0.04, 0.71], [2.22, -0.12, 0.87], [3.04, -0.0, 1.68], [2.79, -0.0, 1.35], [2.79, -0.1, 1.35], [2.79, 0.1, 1.35], [2.51, 0.0, 1.06], [2.79, -0.0, 1.35], [0.36, 0.0, 0.31], [0.0, 0.03, 0.28], [0.0, 0.0, 0.28], [1.9, 0.0, 0.71], [1.43, -0.04, 0.53], [1.9, -0.04, 0.71], [1.43, 0.0, 0.53], [0.86, -0.04, 0.38], [1.43, -0.04, 0.53], [0.86, 0.0, 0.38], [0.36, -0.03, 0.31], [0.86, -0.04, 0.38], [1.43, 0.04, 0.53], [1.9, 0.0, 0.71], [1.9, 0.04, 0.71], [0.86, 0.04, 0.38], [1.43, 0.0, 0.53], [1.43, 0.04, 0.53], [0.36, 0.03, 0.31], [0.86, 0.0, 0.38], [0.86, 0.04, 0.38], [2.51, 0.0, 1.06], [2.22, 0.12, 0.87], [2.22, 0.0, 0.87], [2.51, 0.0, 1.06], [2.79, -0.1, 1.35], [2.79, -0.0, 1.35], [2.22, -0.12, 0.87], [2.51, 0.0, 1.06], [2.22, 0.0, 0.87], [0.0, 0.0, 0.28], [-0.36, 0.0, 0.31], [-0.36, -0.03, 0.31], [-2.22, 0.0, 0.87], [-1.9, 0.0, 0.71], [-1.9, 0.04, 0.71], [-2.22, 0.0, 0.87], [-2.22, -0.12, 0.87], [-1.9, -0.04, 0.71], [-2.79, 0.1, 1.35], [-2.79, -0.0, 1.35], [-2.51, 0.0, 1.06], [0.0, 0.03, 0.28], [-0.36, 0.03, 0.31], [-0.36, 0.0, 0.31], [-1.9, 0.0, 0.71], [-1.9, -0.04, 0.71], [-1.43, -0.04, 0.53], [-1.43, 0.0, 0.53], [-1.43, -0.04, 0.53], [-0.86, -0.04, 0.38], [-0.86, 0.0, 0.38], [-0.86, -0.04, 0.38], [-0.36, -0.03, 0.31], [-1.9, 0.0, 0.71], [-1.43, 0.0, 0.53], [-1.43, 0.04, 0.53], [-1.43, 0.0, 0.53], [-0.86, 0.0, 0.38], [-0.86, 0.04, 0.38], [-0.86, 0.0, 0.38], [-0.36, 0.0, 0.31], [-0.36, 0.03, 0.31], [-2.22, 0.12, 0.87], [-2.51, 0.13, 1.06], [-2.51, 0.0, 1.06], [-2.79, -0.1, 1.35], [-2.51, -0.13, 1.06], [-2.51, 0.0, 1.06], [-2.22, -0.12, 0.87], [-2.22, 0.0, 0.87], [-2.51, 0.0, 1.06], [0.36, -0.03, 0.31], [0.36, 0.0, 0.31], [0.0, 0.0, 0.28], [1.9, 0.04, 0.71], [1.9, 0.0, 0.71], [2.22, 0.0, 0.87], [2.22, 0.0, 0.87], [1.9, 0.0, 0.71], [1.9, -0.04, 0.71], [2.79, 0.1, 1.35], [2.51, 0.13, 1.06], [2.51, 0.0, 1.06], [0.36, 0.0, 0.31], [0.36, 0.03, 0.31], [0.0, 0.03, 0.28], [1.9, 0.0, 0.71], [1.43, 0.0, 0.53], [1.43, -0.04, 0.53], [1.43, 0.0, 0.53], [0.86, 0.0, 0.38], [0.86, -0.04, 0.38], [0.86, 0.0, 0.38], [0.36, 0.0, 0.31], [0.36, -0.03, 0.31], [1.43, 0.04, 0.53], [1.43, 0.0, 0.53], [1.9, 0.0, 0.71], [0.86, 0.04, 0.38], [0.86, 0.0, 0.38], [1.43, 0.0, 0.53], [0.36, 0.03, 0.31], [0.36, 0.0, 0.31], [0.86, 0.0, 0.38], [2.51, 0.0, 1.06], [2.51, 0.13, 1.06], [2.22, 0.12, 0.87], [2.51, 0.0, 1.06], [2.51, -0.13, 1.06], [2.79, -0.1, 1.35], [2.22, -0.12, 0.87], [2.51, -0.13, 1.06], [2.51, 0.0, 1.06]], "Sphere_GizmoGroup_": [[-0.0, -0.71, 0.0], [0.61, 0.35, 0.0], [-0.61, 0.35, 0.0], [-0.61, 0.35, 0.0], [-0.71, -0.0, 0.0], [-0.0, -0.71, 0.0], [-0.71, -0.0, 0.0], [-0.61, -0.35, 0.0], [-0.0, -0.71, 0.0], [-0.61, -0.35, 0.0], [-0.35, -0.61, 0.0], [-0.0, -0.71, 0.0], [-0.0, -0.71, 0.0], [0.35, -0.61, 0.0], [0.61, -0.35, 0.0], [0.61, -0.35, 0.0], [0.71, -0.0, 0.0], [0.61, 0.35, 0.0], [0.61, 0.35, 0.0], [0.35, 0.61, 0.0], [-0.0, 0.71, 0.0], [-0.0, 0.71, 0.0], [-0.35, 0.61, 0.0], [-0.61, 0.35, 0.0], [-0.0, -0.71, 0.0], [0.61, -0.35, 0.0], [0.61, 0.35, 0.0], [0.61, 0.35, 0.0], [-0.0, 0.71, 0.0], [-0.61, 0.35, 0.0]]} \ No newline at end of file diff --git a/simple_deform_helper/gizmo/angle_and_factor.py b/simple_deform_helper/gizmo/angle_and_factor.py index b565a58e9..e5bb4810e 100644 --- a/simple_deform_helper/gizmo/angle_and_factor.py +++ b/simple_deform_helper/gizmo/angle_and_factor.py @@ -10,16 +10,54 @@ from ..utils import GizmoUtils, GizmoGroupUtils class AngleUpdate(GizmoUtils): + int_value_degrees: float + tmp_value_angle: float + + def get_snap(self, delta, tweak): + is_snap = 'SNAP' in tweak + is_precise = 'PRECISE' in tweak + if is_snap and is_precise: + delta = round(delta) + elif is_snap: + delta //= 5 + delta *= 5 + elif is_precise: + delta /= self.mouse_dpi + delta //= 0.01 + delta *= 0.01 + return delta def update_prop_value(self, event, tweak): - # radians 弧度 - # degrees 角度 - delta = self.get_delta(event) - value = math.degrees(self.int_value_angle - delta) - new_value = (self.get_snap(value, tweak)) - old_value = math.degrees(self.target_get_value('angle')) + def v(va): + self.target_set_value('angle', math.radians(va)) - self.target_set_value('angle', math.radians(new_value)) + not_c_l = not event.alt and not event.ctrl + is_only_shift = event.shift and not_c_l + + change_angle = self.get_delta(event) + if is_only_shift: + change_angle /= 50 + new_value = self.tmp_value_angle - change_angle + old_value = self.target_get_value('angle') + snap_value = self.get_snap(new_value, tweak) + + is_shift = event.type == 'LEFT_SHIFT' + if is_only_shift: + if event.value == 'PRESS': + self.init_mouse_region_x = event.mouse_region_x + self.tmp_value_angle = int(math.degrees(old_value)) + v(self.tmp_value_angle) + return + + value = (self.tmp_value_angle - change_angle) // 0.01 * 0.01 + v(value) + return + + elif not_c_l and not event.shift and is_shift and event.value == 'RELEASE': + self.init_mouse_region_x = event.mouse_region_x + new_value = self.tmp_value_angle = math.degrees(old_value) + return + v(snap_value) def update_gizmo_matrix(self, context): matrix = context.object.matrix_world @@ -28,11 +66,15 @@ class AngleUpdate(GizmoUtils): self.matrix_basis.translation = matrix @ point def update_header_text(self, context): - if self.modifier_origin_angle_is_available: + te = self.translate_text + text = te(self.modifier.deform_method.title()) + ' ' + + if self.modifier_is_use_angle_value: value = round(math.degrees(self.modifier_angle), 3) - text = self.translate_header_text('Angle', value) + text += self.translate_header_text('Angle', value) else: - text = self.translate_header_text('Coefficient', self.modifier.factor) + value = round(self.modifier.factor, 3) + text += self.translate_header_text('Coefficient', value) context.area.header_text_set(text) @@ -49,21 +91,21 @@ class AngleGizmo(Gizmo, AngleUpdate): 'draw_type', 'mouse_dpi', 'empty_object', + 'custom_shape', + 'tmp_value_angle', + 'int_value_degrees', 'init_mouse_region_y', 'init_mouse_region_x', - 'custom_shape', - 'int_value_angle', ) - int_value_angle: float - def setup(self): - self.mouse_dpi = 10 self.init_setup() def invoke(self, context, event): self.init_invoke(context, event) - self.int_value_angle = self.target_get_value('angle') + self.int_value_degrees = self.target_get_value('angle') + angle = math.degrees(self.int_value_degrees) + self.tmp_value_angle = angle return {'RUNNING_MODAL'} def modal(self, context, event, tweak): @@ -72,12 +114,12 @@ class AngleGizmo(Gizmo, AngleUpdate): self.update_prop_value(event, tweak) self.update_header_text(context) self.update_multiple_modifiers_data() - return {'RUNNING_MODAL'} + return self.event_handle(event) def exit(self, context, cancel): context.area.header_text_set(None) if cancel: - self.target_set_value('angle', self.int_value_angle) + self.target_set_value('angle', self.int_value_degrees) class AngleGizmoGroup(GizmoGroup, GizmoGroupUtils): @@ -104,11 +146,11 @@ class AngleGizmoGroup(GizmoGroup, GizmoGroupUtils): 'use_draw_modal': True, 'scale_basis': 0.1, 'use_draw_value': True, - 'mouse_dpi': 100, + 'mouse_dpi': 5, }), ] - self.generate_gizmo_mode(add_data) + self.generate_gizmo(add_data) def refresh(self, context): self.angle.target_set_prop('angle', diff --git a/simple_deform_helper/gizmo/bend_axis.py b/simple_deform_helper/gizmo/bend_axis.py index 0ff66599b..78a5ea5ff 100644 --- a/simple_deform_helper/gizmo/bend_axis.py +++ b/simple_deform_helper/gizmo/bend_axis.py @@ -8,7 +8,7 @@ from ..utils import GizmoUtils, GizmoGroupUtils class CustomGizmo(Gizmo, GizmoUtils): - """绘制自定义Gizmo""" + """Draw Custom Gizmo""" bl_idname = '_Custom_Gizmo' draw_type: str custom_shape: dict @@ -37,9 +37,6 @@ class CustomGizmo(Gizmo, GizmoUtils): class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils): - """绘制切换变型轴的 - 变换方向 - """ bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo' bl_label = 'SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo' diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index 1dc332c94..3290fd9d4 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -136,9 +136,9 @@ class GizmoUpdate(GizmoProperty): origin = self.obj_origin_property_group mode = origin.bl_rna.properties['origin_mode'].enum_items[origin.origin_mode].name + te = self.translate_text t = self.translate_header_text - text = self.translate_text(mode) + ' ' - + text = te(self.modifier.deform_method.title()) + ' ' + te(mode) + ' ' if self.is_up_limits_mode: value = round(self.modifier_up_limits, 3) text += t('Up limit', value) @@ -252,7 +252,7 @@ class UpDownLimitsGizmoGroup(GizmoGroup, GizmoGroupUtils): 'scale_basis': 0.1, 'use_draw_value': True, }), ] - self.generate_gizmo_mode(gizmo_data) + self.generate_gizmo(gizmo_data) def refresh(self, context): pro = context.object.SimpleDeformGizmo_PropertyGroup diff --git a/simple_deform_helper/preferences.py b/simple_deform_helper/preferences.py index 02b8c9b55..51fb58daa 100644 --- a/simple_deform_helper/preferences.py +++ b/simple_deform_helper/preferences.py @@ -48,7 +48,7 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): min=0.0001 ) display_bend_axis_switch_gizmo: BoolProperty( - name='Show Toggle Axis Gizmo', + name='Show Toggle Bend Axis Gizmo', default=False, options={'SKIP_SAVE'}) diff --git a/simple_deform_helper/translate.py b/simple_deform_helper/translate.py index 72e94e2a5..151d07a12 100644 --- a/simple_deform_helper/translate.py +++ b/simple_deform_helper/translate.py @@ -11,12 +11,16 @@ def origin_text(a, b): translations_dict = { "zh_CN": { ("上下文", "原文"): "翻译文字", - ("*", "Show Toggle Axis Gizmo"): "显示切换轴向Gizmo", + ("*", "Show Toggle Bend Axis Gizmo"): "显示切换弯曲轴向Gizmo", + ("*", "Show Set Axis Button"): "显示设置轴向Gizmo", ("*", "Follow Upper Limit(Red)"): "跟随上限(红色)", ("*", "Follow Lower Limit(Green)"): "跟随下限(绿色)", ("*", "Lower limit(Green)"): "下限(绿色)", ("*", "UP Limits(Red)"): "上限(红色)", + ("*", "Down limit"): "下限", + ("*", "Up limit"): "上限", + ("*", "Show Deform Wireframe"): "显示形变线框", ("*", "Minimum value between upper and lower limits"): "上限与下限之间的最小值", ("*", "Upper and lower limit tolerance"): "上下限容差", ("*", "Draw Upper and lower limit Bound Box Color"): "绘制网格上限下限边界线框的颜色", diff --git a/simple_deform_helper/update.py b/simple_deform_helper/update.py index 98d5edea0..b1ffbd729 100644 --- a/simple_deform_helper/update.py +++ b/simple_deform_helper/update.py @@ -8,12 +8,14 @@ from .utils import GizmoUpdate @persistent def remove_not_use_empty(scene, dep): - """循环场景内的所有物体,找出没用的空物体并删掉 + """Remove unused Empty Object """ remove_name: str = "ViewSimpleDeformGizmo_" context = bpy.context - if GizmoUpdate.simple_deform_modifier_is_simple(context): - GizmoUpdate.clear_cache() + gizmo = GizmoUpdate() + gizmo.clear_cache() + gizmo.fix_origin_parent_and_angle() + if gizmo.simple_deform_modifier_is_simple(context): for obj in context.scene.objects: is_empty = obj.type == "EMPTY" not_parent = not obj.parent diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index b4eee09f5..21b3ee99a 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -15,7 +15,8 @@ class PublicData: """Public data class, all fixed data will be placed here """ G_CustomShape = {} - G_GizmoData = {'modifiers_co': {}} + G_GizmoData = {} + G_Modifiers_Data = {} G_INDICES = ( (0, 1), (0, 2), (1, 3), (2, 3), (4, 5), (4, 6), (5, 7), (6, 7), @@ -393,7 +394,7 @@ class PublicProperty(GizmoClassMethod): @classmethod def clear_data(cls): cls.G_GizmoData.clear() - cls.G_GizmoData['modifiers_co'] = {} + cls.G_Modifiers_Data.clear() # --------------- Cache Data ---------------------- @@ -403,7 +404,7 @@ class PublicProperty(GizmoClassMethod): @property def modifier_bound_co(self): - return self.G_GizmoData['modifiers_co'].get(self.modifier.name, self.get_bound_co_data()) + return self.G_Modifiers_Data.get(self.modifier.name, self.get_bound_co_data()) @property def modifier_bound_box_pos(self): @@ -526,6 +527,22 @@ class PublicProperty(GizmoClassMethod): class GizmoUpdate(PublicProperty): + def fix_origin_parent_and_angle(self): + obj = self.obj + mod = self.modifier + if not obj or not mod: + return + + origin = mod.origin + if not origin: + return + + if origin.parent != obj: + origin.parent = obj + origin.rotation_euler.zero() + if not self.modifier_origin_angle_is_available: + origin.location.zero() + origin.scale = 1, 1, 1 def new_origin_empty_object(self): mod = self.modifier @@ -543,9 +560,6 @@ class GizmoUpdate(PublicProperty): origin_object.hide_viewport = False if origin_object == obj: return - if origin_object.parent != obj: - origin_object.parent = obj - # add constraints name = self.G_CON_LIMIT_NAME if origin_object.constraints.keys().__len__() > 2: @@ -573,26 +587,24 @@ class GizmoUpdate(PublicProperty): copy_constraints.mix_mode = 'BEFORE' copy_constraints.target_space = 'WORLD' copy_constraints.owner_space = 'WORLD' - origin_object.rotation_euler.zero() - origin_object.scale = 1, 1, 1 - + self.fix_origin_parent_and_angle() return origin_object def update_object_origin_matrix(self): self.clear_cache() origin_mode = self.origin_mode - empty_object = self.modifier.origin - if empty_object and self.modifier_is_use_origin_axis: + origin_object = self.modifier.origin + if origin_object and self.modifier_is_use_origin_axis: if origin_mode == 'UP_LIMITS': - empty_object.matrix_world.translation = Vector(self.point_limits_up) + origin_object.matrix_world.translation = Vector(self.point_limits_up) elif origin_mode == 'DOWN_LIMITS': - empty_object.matrix_world.translation = Vector(self.point_limits_down) + origin_object.matrix_world.translation = Vector(self.point_limits_down) elif origin_mode == 'LIMITS_MIDDLE': translation = (self.point_limits_up + self.point_limits_down) / 2 - empty_object.matrix_world.translation = translation + origin_object.matrix_world.translation = translation elif origin_mode == 'MIDDLE': translation = (self.point_up + self.point_down) / 2 - empty_object.matrix_world.translation = translation + origin_object.matrix_world.translation = translation def update_multiple_modifiers_data(self): self.clear_data() @@ -624,12 +636,12 @@ class GizmoUpdate(PublicProperty): deform_obj.modifiers.clear() subdivision = deform_obj.modifiers.new('1', 'SUBSURF') subdivision.levels = 7 - self.G_GizmoData['modifiers_co']['co'] = self.get_bound_co_data() + self.G_GizmoData['co'] = self.get_bound_co_data() for mo in context.object.modifiers: if mo.type == 'SIMPLE_DEFORM': obj = self.get_depsgraph(deform_obj) - self.G_GizmoData['modifiers_co'][mo.name] = self.get_mesh_max_min_co( + self.G_GizmoData[mo.name] = self.get_mesh_max_min_co( obj) simple_deform = deform_obj.modifiers.new( mo.name, 'SIMPLE_DEFORM') @@ -695,9 +707,8 @@ class GizmoUtils(GizmoUpdate): matrix_basis: Matrix draw_type: str - def generate_gizmo_mode(self, gizmo_data): - """生成gizmo的上限下限及角度设置 - + def generate_gizmo(self, gizmo_data): + """Generate Gizmo From Input Data Args: gizmo_data (_type_): _description_ """ @@ -746,27 +757,11 @@ class GizmoUtils(GizmoUpdate): delta = (self.init_mouse_region_x - event.mouse_region_x) / self.mouse_dpi return delta - def get_snap(self, delta, tweak): - # TODO ctrl SNAP - # TODO shift PRECISE - is_snap = 'SNAP' in tweak - is_precise = 'PRECISE' in tweak - if is_snap and is_precise: - delta = round(delta) - elif is_snap: - delta //= 5 - delta *= 5 - elif is_precise: - delta /= self.mouse_dpi - delta //= 0.01 - delta *= 0.01 - return delta - def update_gizmo_matrix(self): ... def event_handle(self, event): - """通过输入键位来更改属性""" + """General event triggeringXXX""" # event ctrl data_path = ('object.SimpleDeformGizmo_PropertyGroup.origin_mode', 'object.modifiers.active.origin.SimpleDeformGizmo_PropertyGroup.origin_mode') @@ -778,9 +773,11 @@ class GizmoUtils(GizmoUpdate): data_path=path, reverse=reverse, wrap=True) elif event.type in ('X', 'Y', 'Z'): self.obj.modifiers.active.deform_axis = event.type - elif event.type == 'A': + elif event.type == 'A' and 'BEND' == self.modifier.deform_method: self.pref.display_bend_axis_switch_gizmo = True return {'FINISHED'} + elif event.type == 'W' and event.value == 'RELEASE': + self.pref.update_deform_wireframe = self.pref.update_deform_wireframe ^ True return {'RUNNING_MODAL'} -- 2.30.2 From b9c7dcdc34309445d8faf91cad887f880e7d404d Mon Sep 17 00:00:00 2001 From: EMM <3209970865@qq.com> Date: Fri, 31 Mar 2023 19:57:01 +0800 Subject: [PATCH 09/12] snap angle value fix assign a custom origin object update translate update gizmo.json --- simple_deform_helper/__init__.py | 49 +++++++++++++++++++++++++++++ simple_deform_helper/operators.py | 1 - simple_deform_helper/preferences.py | 8 +++++ simple_deform_helper/translate.py | 12 +++++++ simple_deform_helper/update.py | 9 +++++- simple_deform_helper/utils.py | 3 +- 6 files changed, 78 insertions(+), 4 deletions(-) diff --git a/simple_deform_helper/__init__.py b/simple_deform_helper/__init__.py index 9a9052c8d..ce0ca4cf9 100644 --- a/simple_deform_helper/__init__.py +++ b/simple_deform_helper/__init__.py @@ -20,6 +20,55 @@ bl_info = { "category": "3D View" } +""" +# ------------------------- +__init__.py: + Register All Module + +gizmo/__init__.py: + Register All Gizmo + + /angle_and_factor.py: + Ctrl Modifier Angle + + /bend_axis.py: + Bend Method Switch Direction Gizmo + + /set_deform_axis.py: + Three Switch Deform Axis Operator Gizmo + + /up_down_limits_point.py: + Main control part + use utils.py PublicProperty._get_limits_point_and_bound_box_co + Obtain and calculate boundary box and limit point data + + +draw.py: + Draw 3D Bound And Line + +gizmo.json: + Draw Custom Shape Vertex Data + +operator.py: + Set Deform Axis Operator + +panel.py: + Draw Gizmo Tool Property in Options and Tool Settings Right + +preferences.py: + Addon Preferences + +translate.py: + temporary only Cn translate + +update.py: + In Change Depsgraph When Update Addon Data And Del Redundant Empty + +utils.py: + Main documents used + Most computing operations are placed in classes GizmoUtils +# ------------------------- +""" module_tuple = ( panel, gizmo, diff --git a/simple_deform_helper/operators.py b/simple_deform_helper/operators.py index 3f7fe2411..9d35d5ce5 100644 --- a/simple_deform_helper/operators.py +++ b/simple_deform_helper/operators.py @@ -46,7 +46,6 @@ class DeformAxisOperator(Operator, GizmoUtils): if not event.ctrl: self.pref.display_bend_axis_switch_gizmo = False - # self.new_origin_empty_object() return {'FINISHED'} diff --git a/simple_deform_helper/preferences.py b/simple_deform_helper/preferences.py index 51fb58daa..36b6a76f7 100644 --- a/simple_deform_helper/preferences.py +++ b/simple_deform_helper/preferences.py @@ -62,6 +62,14 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): def draw(self, context): col = self.layout.column() + box = col.box() + for text in ("You can press the following shortcut keys when dragging values", + " Wheel: Switch Origin Ctrl Mode", + " X、Y、Z: Switch Modifier Deform Axis", + " W: Switch Deform Wireframe Show", + " A: Switch To Select Bend Axis Mode(deform_method=='BEND')",): + box.label(text=self.translate_text(text)) + col.prop(self, 'deform_wireframe_color') col.prop(self, 'bound_box_color') col.prop(self, 'limits_bound_box_color') diff --git a/simple_deform_helper/translate.py b/simple_deform_helper/translate.py index 151d07a12..d2701d272 100644 --- a/simple_deform_helper/translate.py +++ b/simple_deform_helper/translate.py @@ -12,6 +12,18 @@ translations_dict = { "zh_CN": { ("上下文", "原文"): "翻译文字", ("*", "Show Toggle Bend Axis Gizmo"): "显示切换弯曲轴向Gizmo", + + ("*", "You can press the following shortcut keys when dragging values"): + "拖动值时可以按以下快捷键", + ("*", " Wheel: Switch Origin Ctrl Mode"): + " 滚轮: 切换原点控制模式", + ("*", " X、Y、Z: Switch Modifier Deform Axis"): + " X、Y、Z: 切换修改器型变轴", + ("*", " W: Switch Deform Wireframe Show"): + " W: 切换形变线框显示", + ("*", + " A: Switch To Select Bend Axis Mode(deform_method=='BEND')"): + " A: 切换到选择弯曲轴模式(形变方法='弯曲')", ("*", "Show Set Axis Button"): "显示设置轴向Gizmo", ("*", "Follow Upper Limit(Red)"): "跟随上限(红色)", diff --git a/simple_deform_helper/update.py b/simple_deform_helper/update.py index b1ffbd729..502afd34e 100644 --- a/simple_deform_helper/update.py +++ b/simple_deform_helper/update.py @@ -15,12 +15,19 @@ def remove_not_use_empty(scene, dep): gizmo = GizmoUpdate() gizmo.clear_cache() gizmo.fix_origin_parent_and_angle() + # remove redundant empty object if gizmo.simple_deform_modifier_is_simple(context): for obj in context.scene.objects: is_empty = obj.type == "EMPTY" not_parent = not obj.parent if remove_name in obj.name and not_parent and is_empty: - bpy.data.objects.remove(obj) # remove object + bpy.data.objects.remove(obj) + + # simple update data if change active object on update + update_data = gizmo.obj not in gizmo.G_GizmoData or gizmo.obj != gizmo.G_GizmoData[gizmo.obj] + if update_data: + gizmo.clear_data() + gizmo.G_GizmoData[gizmo.obj] = 'Emm' def register(): diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index 21b3ee99a..2ac23ad81 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -761,8 +761,7 @@ class GizmoUtils(GizmoUpdate): ... def event_handle(self, event): - """General event triggeringXXX""" - # event ctrl + """General event triggering""" data_path = ('object.SimpleDeformGizmo_PropertyGroup.origin_mode', 'object.modifiers.active.origin.SimpleDeformGizmo_PropertyGroup.origin_mode') -- 2.30.2 From f9ac35fdb04b26bb2d4ac57e437ccd71af157314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=90=8C=E6=96=B0?= <3209970865@qq.com> Date: Fri, 31 Mar 2023 23:38:43 +0800 Subject: [PATCH 10/12] optimize performance --- .../gizmo/up_down_limits_point.py | 8 +++-- simple_deform_helper/update.py | 22 +++++++------ simple_deform_helper/utils.py | 31 ++++++++++++++----- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index 3290fd9d4..4c41f9128 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -209,11 +209,13 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): self.middle_limits_value = (self.modifier_up_limits + self.modifier_down_limits) / 2 self.set_prop_value(event) - self.update_object_origin_matrix() + self.clear_data() + self.clear_cache() self.update_multiple_modifiers_data() - + self.update_object_origin_matrix() self.update_header_text(context) - return self.event_handle(event) + return_handle = self.event_handle(event) + return return_handle class UpDownLimitsGizmoGroup(GizmoGroup, GizmoGroupUtils): diff --git a/simple_deform_helper/update.py b/simple_deform_helper/update.py index 502afd34e..dbe32b2f5 100644 --- a/simple_deform_helper/update.py +++ b/simple_deform_helper/update.py @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later +from time import time import bpy from bpy.app.handlers import depsgraph_update_post, persistent @@ -13,21 +14,24 @@ def remove_not_use_empty(scene, dep): remove_name: str = "ViewSimpleDeformGizmo_" context = bpy.context gizmo = GizmoUpdate() - gizmo.clear_cache() gizmo.fix_origin_parent_and_angle() # remove redundant empty object - if gizmo.simple_deform_modifier_is_simple(context): - for obj in context.scene.objects: - is_empty = obj.type == "EMPTY" - not_parent = not obj.parent - if remove_name in obj.name and not_parent and is_empty: - bpy.data.objects.remove(obj) # simple update data if change active object on update - update_data = gizmo.obj not in gizmo.G_GizmoData or gizmo.obj != gizmo.G_GizmoData[gizmo.obj] + name = gizmo.obj.name + update_data = name not in gizmo.G_GizmoData or name != gizmo.G_GizmoData['active_object'] if update_data: gizmo.clear_data() - gizmo.G_GizmoData[gizmo.obj] = 'Emm' + gizmo.G_GizmoData[name] = name + gizmo.G_GizmoData['active_object'] = name + gizmo.G_Modifiers_TMP_Save_Data.clear() + if gizmo.simple_deform_modifier_is_simple(context): + for obj in context.scene.objects: + is_empty = obj.type == "EMPTY" + not_parent = not obj.parent + if remove_name in obj.name and not_parent and is_empty: + bpy.data.objects.remove(obj) + def register(): diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index 2ac23ad81..593ce0ee2 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -17,6 +17,7 @@ class PublicData: G_CustomShape = {} G_GizmoData = {} G_Modifiers_Data = {} + G_Modifiers_TMP_Save_Data = {} G_INDICES = ( (0, 1), (0, 2), (1, 3), (2, 3), (4, 5), (4, 6), (5, 7), (6, 7), @@ -314,6 +315,12 @@ class GizmoClassMethod(PublicUtils): Vector((min_x, max_y, max_z)) ) + @classmethod + def get_modifiers_data(cls, obj): + return {'obj': obj.name, + 'active_modifier': obj.modifiers.active.name, + 'modifiers': list(i.name for i in obj.modifiers)} + class PublicProperty(GizmoClassMethod): @@ -530,7 +537,7 @@ class GizmoUpdate(PublicProperty): def fix_origin_parent_and_angle(self): obj = self.obj mod = self.modifier - if not obj or not mod: + if not obj or not mod or not getattr(mod, 'origin', False): return origin = mod.origin @@ -591,10 +598,11 @@ class GizmoUpdate(PublicProperty): return origin_object def update_object_origin_matrix(self): - self.clear_cache() origin_mode = self.origin_mode origin_object = self.modifier.origin - if origin_object and self.modifier_is_use_origin_axis: + is_use = self.modifier_is_use_origin_axis + + if origin_object and is_use: if origin_mode == 'UP_LIMITS': origin_object.matrix_world.translation = Vector(self.point_limits_up) elif origin_mode == 'DOWN_LIMITS': @@ -607,16 +615,23 @@ class GizmoUpdate(PublicProperty): origin_object.matrix_world.translation = translation def update_multiple_modifiers_data(self): - self.clear_data() obj = self.obj data = bpy.data context = bpy.context + name = self.G_NAME + origin_object = data.objects.get(name) + + modifiers = self.G_Modifiers_TMP_Save_Data + mods_data = self.get_modifiers_data(obj) + if 114514 in modifiers and modifiers[114514] == mods_data: + if origin_object: + self.update_deform_wireframe(self.get_depsgraph(origin_object)) + return # add simple_deform mesh - name = self.G_NAME - if data.objects.get(name): - data.objects.remove(data.objects.get(name)) + if origin_object: + data.objects.remove(origin_object) if data.meshes.get(name): data.meshes.remove(data.meshes.get(name)) @@ -658,10 +673,10 @@ class GizmoUpdate(PublicProperty): deform_obj.hide_select = True deform_obj.hide_set(True) deform_obj.hide_viewport = False - self.update_deform_wireframe(self.get_depsgraph(deform_obj)) deform_obj.hide_render = True deform_obj.hide_viewport = True deform_obj.hide_set(True) + self.G_Modifiers_TMP_Save_Data[114514] = mods_data def update_deform_wireframe(self, obj): if not self.pref.update_deform_wireframe: -- 2.30.2 From 1a284b677bbdf0a4507205102f82a7921bcb809b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=90=8C=E6=96=B0?= <3209970865@qq.com> Date: Sun, 2 Apr 2023 11:59:23 +0800 Subject: [PATCH 11/12] optimize performance G_Modifiers_TMP_Save_Data --- simple_deform_helper/draw.py | 11 +- .../gizmo/angle_and_factor.py | 3 +- simple_deform_helper/gizmo/bend_axis.py | 2 +- .../gizmo/up_down_limits_point.py | 6 +- simple_deform_helper/update.py | 118 +++++++++++++----- simple_deform_helper/utils.py | 70 +++++------ 6 files changed, 132 insertions(+), 78 deletions(-) diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index af9d7cf7e..4a42e7cb1 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -122,15 +122,16 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): ) def draw_limits_line(self): - line_pos, limits_pos, = self.modifier_limits_point + up_point, down_point, up_limits, down_limits = self.modifier_limits_point # draw limits line - self.draw_3d_shader(limits_pos, ((1, 0),), (1, 1, 0, 0.5)) + self.draw_3d_shader((up_limits, down_limits), ((1, 0),), (1, 1, 0, 0.5)) # draw line - self.draw_3d_shader(line_pos, ((1, 0),), (1, 1, 0, 0.3)) + self.draw_3d_shader((up_point, down_point), ((1, 0),), (1, 1, 0, 0.3)) + # draw pos - self.draw_3d_shader([line_pos[1]], (), (0, 1, 0, 0.5), + self.draw_3d_shader([down_point], (), (0, 1, 0, 0.5), shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') - self.draw_3d_shader([line_pos[0]], (), (1, 0, 0, 0.5), + self.draw_3d_shader([up_point], (), (1, 0, 0, 0.5), shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') def draw_deform_mesh(self): diff --git a/simple_deform_helper/gizmo/angle_and_factor.py b/simple_deform_helper/gizmo/angle_and_factor.py index e5bb4810e..b8b8752f7 100644 --- a/simple_deform_helper/gizmo/angle_and_factor.py +++ b/simple_deform_helper/gizmo/angle_and_factor.py @@ -55,7 +55,7 @@ class AngleUpdate(GizmoUtils): elif not_c_l and not event.shift and is_shift and event.value == 'RELEASE': self.init_mouse_region_x = event.mouse_region_x - new_value = self.tmp_value_angle = math.degrees(old_value) + # new_value = self.tmp_value_angle = math.degrees(old_value) return v(snap_value) @@ -113,7 +113,6 @@ class AngleGizmo(Gizmo, AngleUpdate): self.update_prop_value(event, tweak) self.update_header_text(context) - self.update_multiple_modifiers_data() return self.event_handle(event) def exit(self, context, cancel): diff --git a/simple_deform_helper/gizmo/bend_axis.py b/simple_deform_helper/gizmo/bend_axis.py index 78a5ea5ff..94fcd85b1 100644 --- a/simple_deform_helper/gizmo/bend_axis.py +++ b/simple_deform_helper/gizmo/bend_axis.py @@ -116,4 +116,4 @@ class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils): gizmo = getattr(self, i, False) rot = Euler(w, 'XYZ').to_matrix().to_4x4() gizmo.matrix_basis = mat.to_euler().to_matrix().to_4x4() @ rot - gizmo.matrix_basis.translation = Vector(j) + gizmo.matrix_basis.translation = self.obj_matrix_world @ Vector(j) diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index 4c41f9128..eae73419c 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -1,4 +1,5 @@ import math +from time import time import bpy from bpy.types import Gizmo, GizmoGroup @@ -200,6 +201,7 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): 'down_limits', self.int_value_down_limits) def modal(self, context, event, tweak): + st = time() self.clear_cache() if self.modifier_is_use_origin_axis: @@ -209,12 +211,12 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): self.middle_limits_value = (self.modifier_up_limits + self.modifier_down_limits) / 2 self.set_prop_value(event) - self.clear_data() self.clear_cache() - self.update_multiple_modifiers_data() self.update_object_origin_matrix() + # self.update_deform_wireframe(self.get_depsgraph(origin_object)) self.update_header_text(context) return_handle = self.event_handle(event) + print('modal time sum ', time() - st) return return_handle diff --git a/simple_deform_helper/update.py b/simple_deform_helper/update.py index dbe32b2f5..e5732512a 100644 --- a/simple_deform_helper/update.py +++ b/simple_deform_helper/update.py @@ -1,42 +1,100 @@ # SPDX-License-Identifier: GPL-2.0-or-later -from time import time - -import bpy -from bpy.app.handlers import depsgraph_update_post, persistent - from .utils import GizmoUpdate -@persistent -def remove_not_use_empty(scene, dep): - """Remove unused Empty Object - """ - remove_name: str = "ViewSimpleDeformGizmo_" - context = bpy.context - gizmo = GizmoUpdate() - gizmo.fix_origin_parent_and_angle() - # remove redundant empty object +class update_public: + _event_func_list = {} + update_func: 'function' + tmp_save_data = {} + run_time = 0.2 - # simple update data if change active object on update - name = gizmo.obj.name - update_data = name not in gizmo.G_GizmoData or name != gizmo.G_GizmoData['active_object'] - if update_data: - gizmo.clear_data() - gizmo.G_GizmoData[name] = name - gizmo.G_GizmoData['active_object'] = name - gizmo.G_Modifiers_TMP_Save_Data.clear() - if gizmo.simple_deform_modifier_is_simple(context): - for obj in context.scene.objects: - is_empty = obj.type == "EMPTY" - not_parent = not obj.parent - if remove_name in obj.name and not_parent and is_empty: - bpy.data.objects.remove(obj) + @classmethod + def register(cls): + import bpy + bpy.app.timers.register(cls.update_func, persistent=True) + @classmethod + def unregister(cls): + from bpy.app import timers + func = cls.update_func + if timers.is_registered(func): + timers.unregister(func) + + @classmethod + def _update_call(cls): + for i in cls._event_func_list[cls]: + i() + + @classmethod + def append(cls, item): + if cls not in cls._event_func_list: + cls._event_func_list[cls] = [] + cls._event_func_list[cls].append(item) + + @classmethod + def remove(cls, item): + if item in cls._event_func_list[cls]: + cls._event_func_list[cls].remove(item) + + +class change_active_object(update_public): + tmp_save_data = {} + from bpy.app.handlers import depsgraph_update_post, persistent + handler_type = depsgraph_update_post + + @classmethod + def update_func(cls): + import bpy + name = bpy.context.object.name + key = 'active_object' + if key not in cls.tmp_save_data or cls.tmp_save_data[key] != name: + cls._update_call() + cls.tmp_save_data[key] = name + return cls.run_time + + +class change_active_simple_deform_modifier(update_public): + + @classmethod + def update_func(cls): + import bpy + obj = bpy.context.object + if not obj or obj.type != 'MESH': + return cls.run_time + + name = obj.name + key = 'active_object' + modifiers = cls.get_modifiers_data(obj) + change_modifiers = 'modifiers' not in cls.tmp_save_data or cls.tmp_save_data['modifiers'] != modifiers + if key not in cls.tmp_save_data or cls.tmp_save_data[key] != name: + cls.tmp_save_data['modifiers'] = modifiers + cls.tmp_save_data[key] = name + elif change_modifiers: + cls.tmp_save_data['modifiers'] = modifiers + cls._update_call() + return cls.run_time + + @classmethod + def get_modifiers_data(cls, obj): + return {'obj': obj.name, + 'active_modifier': getattr(obj.modifiers.active, 'name', None), + 'modifiers': list(i.name for i in obj.modifiers)} + + +gizmo = GizmoUpdate() def register(): - depsgraph_update_post.append(remove_not_use_empty) + change_active_object.register() + change_active_simple_deform_modifier.register() + + change_active_object.append(gizmo.update_multiple_modifiers_data) + change_active_simple_deform_modifier.append(gizmo.update_multiple_modifiers_data) def unregister(): - depsgraph_update_post.remove(remove_not_use_empty) + change_active_object.remove(gizmo.update_multiple_modifiers_data) + change_active_simple_deform_modifier.remove(gizmo.update_multiple_modifiers_data) + + change_active_object.unregister() + change_active_simple_deform_modifier.unregister() diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index 593ce0ee2..259a0b468 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -4,6 +4,7 @@ import math import uuid from functools import cache from os.path import dirname, basename, realpath +from time import time import bpy import numpy as np @@ -17,7 +18,6 @@ class PublicData: G_CustomShape = {} G_GizmoData = {} G_Modifiers_Data = {} - G_Modifiers_TMP_Save_Data = {} G_INDICES = ( (0, 1), (0, 2), (1, 3), (2, 3), (4, 5), (4, 6), (5, 7), (6, 7), @@ -315,12 +315,6 @@ class GizmoClassMethod(PublicUtils): Vector((min_x, max_y, max_z)) ) - @classmethod - def get_modifiers_data(cls, obj): - return {'obj': obj.name, - 'active_modifier': obj.modifiers.active.name, - 'modifiers': list(i.name for i in obj.modifiers)} - class PublicProperty(GizmoClassMethod): @@ -340,7 +334,7 @@ class PublicProperty(GizmoClassMethod): g_l = self.__from_up_down_point_get_limits_point if self.modifier.origin: vector_axis = self.get_vector_axis(mod) - origin_mat = mod.origin.matrix_world.to_3x3() + origin_mat = mod.origin.matrix_basis.to_3x3() axis_ = origin_mat @ vector_axis point_lit = [[top, bottom], [left, right], [front, back]] for f in range(point_lit.__len__()): @@ -378,17 +372,11 @@ class PublicProperty(GizmoClassMethod): up_point, down_point = top, bottom top, bottom = up_limits, down_limits = g_l(top, bottom) - (top, bottom, left, - right, front, back) = self.matrix_calculation(self.obj_matrix_world.inverted(), - (top, bottom, left, right, front, back)) - - points = ((up_point, down_point), (up_limits, down_limits)) + points = (up_point, down_point, up_limits, down_limits) each_point = ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],)) - box_bound_point = self.matrix_calculation(self.obj_matrix_world, self.tow_co_to_coordinate(each_point)) - return points, box_bound_point + return points, self.tow_co_to_coordinate(each_point) # ---------------------- - @cache def _each_face_pos(self, mat, co): return self.co_to_direction(mat, co) @@ -401,13 +389,18 @@ class PublicProperty(GizmoClassMethod): @classmethod def clear_data(cls): cls.G_GizmoData.clear() + + @classmethod + def clear_modifiers_data(cls): cls.G_Modifiers_Data.clear() # --------------- Cache Data ---------------------- @property def each_face_pos(self): - return self._each_face_pos(self.obj_matrix_world, self.get_bound_co_data()) + matrix = Matrix() + matrix.freeze() + return self._each_face_pos(matrix, self.get_bound_co_data()) @property def modifier_bound_co(self): @@ -415,17 +408,19 @@ class PublicProperty(GizmoClassMethod): @property def modifier_bound_box_pos(self): - return self._each_face_pos(self.obj_matrix_world, self.modifier_bound_co) + matrix = Matrix() + matrix.freeze() + return self.co_to_direction(matrix, self.modifier_bound_co) @property def modifier_limits_point(self): points, _ = self._get_limits_point_and_bound_box_co() - return points + return self.matrix_calculation(self.obj_matrix_world, points) @property def modifier_limits_bound_box(self): _, bound = self._get_limits_point_and_bound_box_co() - return bound + return self.matrix_calculation(self.obj_matrix_world, bound) @property def modifier_origin_angle_is_available(self): @@ -433,6 +428,7 @@ class PublicProperty(GizmoClassMethod): self._get_limits_point_and_bound_box_co() return True except UnboundLocalError: + print('modifier_origin_angle_is_available') self.clear_cache() return False @@ -492,19 +488,19 @@ class PublicProperty(GizmoClassMethod): # ----- point @property def point_up(self): - return self.modifier_limits_point[0][0] + return self.modifier_limits_point[0] @property def point_down(self): - return self.modifier_limits_point[0][1] + return self.modifier_limits_point[1] @property def point_limits_up(self): - return self.modifier_limits_point[1][0] + return self.modifier_limits_point[2] @property def point_limits_down(self): - return self.modifier_limits_point[1][1] + return self.modifier_limits_point[3] # ------ @@ -530,7 +526,7 @@ class PublicProperty(GizmoClassMethod): @property def modifier_is_use_origin_axis(self): - return self.obj_origin_property_group.origin_mode != 'NOT' + return self.obj_origin_property_group.origin_mode != 'NOT' and not self.modifier.origin class GizmoUpdate(PublicProperty): @@ -598,6 +594,7 @@ class GizmoUpdate(PublicProperty): return origin_object def update_object_origin_matrix(self): + st = time() origin_mode = self.origin_mode origin_object = self.modifier.origin is_use = self.modifier_is_use_origin_axis @@ -613,23 +610,21 @@ class GizmoUpdate(PublicProperty): elif origin_mode == 'MIDDLE': translation = (self.point_up + self.point_down) / 2 origin_object.matrix_world.translation = translation + print('update_object_origin_matrix', time() - st) def update_multiple_modifiers_data(self): + print('update_multiple_modifiers_data', self) + st = time() obj = self.obj - data = bpy.data context = bpy.context + if obj.type not in ('MESH', 'LATTICE') or not self.simple_deform_public_poll(context): + return + self.clear_cache() + data = bpy.data name = self.G_NAME origin_object = data.objects.get(name) - modifiers = self.G_Modifiers_TMP_Save_Data - mods_data = self.get_modifiers_data(obj) - if 114514 in modifiers and modifiers[114514] == mods_data: - if origin_object: - self.update_deform_wireframe(self.get_depsgraph(origin_object)) - return - - # add simple_deform mesh - + # update multiple simple_deform bound data if origin_object: data.objects.remove(origin_object) @@ -656,8 +651,7 @@ class GizmoUpdate(PublicProperty): for mo in context.object.modifiers: if mo.type == 'SIMPLE_DEFORM': obj = self.get_depsgraph(deform_obj) - self.G_GizmoData[mo.name] = self.get_mesh_max_min_co( - obj) + self.G_Modifiers_Data[mo.name] = self.get_mesh_max_min_co(obj) simple_deform = deform_obj.modifiers.new( mo.name, 'SIMPLE_DEFORM') simple_deform.deform_method = mo.deform_method @@ -676,7 +670,7 @@ class GizmoUpdate(PublicProperty): deform_obj.hide_render = True deform_obj.hide_viewport = True deform_obj.hide_set(True) - self.G_Modifiers_TMP_Save_Data[114514] = mods_data + print('multiple_modifiers', time() - st) def update_deform_wireframe(self, obj): if not self.pref.update_deform_wireframe: -- 2.30.2 From 7cedf3a1599bf0d993b489d924bb1e61318e82ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=90=8C=E6=96=B0?= <3209970865@qq.com> Date: Mon, 3 Apr 2023 03:35:15 +0800 Subject: [PATCH 12/12] Optimize Performance Cache Calculation results Draw Deform Mesh Del print TODO fix set_prop_value prop --- simple_deform_helper/draw.py | 44 +- .../gizmo/angle_and_factor.py | 6 +- simple_deform_helper/gizmo/bend_axis.py | 8 +- simple_deform_helper/gizmo/set_deform_axis.py | 19 +- .../gizmo/up_down_limits_point.py | 19 +- simple_deform_helper/operators.py | 4 +- simple_deform_helper/panel.py | 2 +- simple_deform_helper/preferences.py | 14 +- simple_deform_helper/update.py | 211 ++++++--- simple_deform_helper/utils.py | 420 +++++++++++------- 10 files changed, 469 insertions(+), 278 deletions(-) diff --git a/simple_deform_helper/draw.py b/simple_deform_helper/draw.py index 4a42e7cb1..d8b9c5b88 100644 --- a/simple_deform_helper/draw.py +++ b/simple_deform_helper/draw.py @@ -5,22 +5,23 @@ import gpu from gpu_extras.batch import batch_for_shader from mathutils import Vector +from .update import change_active_object, simple_update from .utils import GizmoUtils class Handler: @classmethod def add_handler(cls): - if 'handler' not in cls.G_GizmoData: - cls.G_GizmoData['handler'] = bpy.types.SpaceView3D.draw_handler_add( + if 'handler' not in cls.G_HandleData: + cls.G_HandleData['handler'] = bpy.types.SpaceView3D.draw_handler_add( Draw3D().draw, (), 'WINDOW', 'POST_VIEW') @classmethod def del_handler_text(cls): - if 'scale_text' in cls.G_GizmoData: + if 'scale_text' in cls.G_HandleData: bpy.types.SpaceView3D.draw_handler_remove( - cls.G_GizmoData['scale_text'], 'WINDOW') - cls.G_GizmoData.pop('scale_text') + cls.G_HandleData['scale_text'], 'WINDOW') + cls.G_HandleData.pop('scale_text') @classmethod def del_handler(cls): @@ -33,10 +34,10 @@ class Handler: cls.del_handler_text() - if 'handler' in cls.G_GizmoData: + if 'handler' in cls.G_HandleData: bpy.types.SpaceView3D.draw_handler_remove( - cls.G_GizmoData['handler'], 'WINDOW') - cls.G_GizmoData.clear() + cls.G_HandleData['handler'], 'WINDOW') + cls.G_HandleData.clear() class DrawPublic: @@ -94,12 +95,14 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): gpu.state.depth_test_set('ALWAYS') context = bpy.context - if self.simple_deform_public_poll(context): - self.draw_3d(context) + if simple_update.timers_update_poll(): + is_switch_obj = change_active_object.is_change_active_object(False) + if self.simple_deform_public_poll(context) and not is_switch_obj: + self.draw_3d(context) def draw_3d(self, context): self.draw_scale_text(self.obj) - if not self.modifier_origin_angle_is_available: + if not self.modifier_origin_is_available: self.draw_bound_box() elif self.simple_deform_show_gizmo_poll(context): # draw bound box @@ -136,20 +139,21 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler): def draw_deform_mesh(self): ob = self.obj - handler_dit = self.G_GizmoData + deform_data = self.G_DeformDrawData active = self.modifier # draw deform mesh - if 'simple_deform_box_data' in handler_dit and self.pref.update_deform_wireframe: - pos, indices, mat, mod_data, limits = handler_dit['simple_deform_box_data'] - if ([getattr(active, i) for i in self.G_MODIFIERS_PROPERTY] == mod_data) and ( - ob.matrix_world == mat) and limits == active.limits[:]: - self.draw_3d_shader( - pos, indices, self.pref.deform_wireframe_color) + if 'simple_deform_bound_data' in deform_data and self.pref.update_deform_wireframe: + modifiers = self.get_modifiers_parameter(self.modifier) + pos, indices, mat, mod_data, limits = deform_data['simple_deform_bound_data'] + is_limits = limits == active.limits[:] + is_mat = (ob.matrix_world == mat) + if modifiers == mod_data and is_mat and is_limits: + self.draw_3d_shader(pos, indices, self.pref.deform_wireframe_color) def draw_scale_text(self, ob): scale_error = (ob.scale != Vector((1, 1, 1))) - if scale_error and ('scale_text' not in self.G_GizmoData): - self.G_GizmoData['scale_text'] = bpy.types.SpaceView3D.draw_handler_add( + if scale_error and ('scale_text' not in self.G_HandleData): + self.G_HandleData['scale_text'] = bpy.types.SpaceView3D.draw_handler_add( self.draw_str, (), 'WINDOW', 'POST_PIXEL') elif not scale_error: self.del_handler_text() diff --git a/simple_deform_helper/gizmo/angle_and_factor.py b/simple_deform_helper/gizmo/angle_and_factor.py index b8b8752f7..56493fee0 100644 --- a/simple_deform_helper/gizmo/angle_and_factor.py +++ b/simple_deform_helper/gizmo/angle_and_factor.py @@ -6,6 +6,7 @@ from bpy.types import ( GizmoGroup, ) +from ..update import change_active_modifier_parameter from ..utils import GizmoUtils, GizmoGroupUtils @@ -109,10 +110,13 @@ class AngleGizmo(Gizmo, AngleUpdate): return {'RUNNING_MODAL'} def modal(self, context, event, tweak): - self.clear_cache() + self.clear_point_cache() self.update_prop_value(event, tweak) + self.update_deform_wireframe() self.update_header_text(context) + change_active_modifier_parameter.update_modifier_parameter() + self.tag_redraw(context) return self.event_handle(event) def exit(self, context, cancel): diff --git a/simple_deform_helper/gizmo/bend_axis.py b/simple_deform_helper/gizmo/bend_axis.py index 94fcd85b1..a6ee0bd40 100644 --- a/simple_deform_helper/gizmo/bend_axis.py +++ b/simple_deform_helper/gizmo/bend_axis.py @@ -14,12 +14,7 @@ class CustomGizmo(Gizmo, GizmoUtils): custom_shape: dict def setup(self): - self.draw_type = 'None_GizmoGroup_' - if not hasattr(self, 'custom_shape'): - self.custom_shape = {} - for i in self.G_CustomShape: - self.custom_shape[i] = self.new_custom_shape( - 'TRIS', self.G_CustomShape[i]) + self.init_setup() def draw(self, context): self.draw_custom_shape(self.custom_shape[self.draw_type]) @@ -29,6 +24,7 @@ class CustomGizmo(Gizmo, GizmoUtils): self.custom_shape[self.draw_type], select_id=select_id) def invoke(self, context, event): + self.init_invoke(context, event) return {'RUNNING_MODAL'} def modal(self, context, event, tweak): diff --git a/simple_deform_helper/gizmo/set_deform_axis.py b/simple_deform_helper/gizmo/set_deform_axis.py index 3865e9f75..34671b0b8 100644 --- a/simple_deform_helper/gizmo/set_deform_axis.py +++ b/simple_deform_helper/gizmo/set_deform_axis.py @@ -34,17 +34,18 @@ class SetDeformGizmoGroup(GizmoGroup, GizmoGroupUtils): setattr(self, f'deform_axis_{axis.lower()}', gizmo) def draw_prepare(self, context): - - if 'co' in self.G_GizmoData: + bound = self.modifier_bound_co + if bound: obj = self.get_depsgraph(self.obj) dimensions = obj.dimensions - def _mat(f): - co = self.G_GizmoData['co'][0] - co = (co[0] + (max(dimensions) * f), co[1], - co[2] - (min(dimensions) * 0.3)) + def mat(f): + b = bound[0] + co = (b[0] + (max(dimensions) * f), + b[1], + b[2] - (min(dimensions) * 0.3)) return self.obj_matrix_world @ Vector(co) - self.deform_axis_x.matrix_basis.translation = _mat(0) - self.deform_axis_y.matrix_basis.translation = _mat(0.3) - self.deform_axis_z.matrix_basis.translation = _mat(0.6) + self.deform_axis_x.matrix_basis.translation = mat(0) + self.deform_axis_y.matrix_basis.translation = mat(0.3) + self.deform_axis_z.matrix_basis.translation = mat(0.6) diff --git a/simple_deform_helper/gizmo/up_down_limits_point.py b/simple_deform_helper/gizmo/up_down_limits_point.py index eae73419c..c472bf7ef 100644 --- a/simple_deform_helper/gizmo/up_down_limits_point.py +++ b/simple_deform_helper/gizmo/up_down_limits_point.py @@ -6,6 +6,7 @@ from bpy.types import Gizmo, GizmoGroup from bpy_extras import view3d_utils from mathutils import Vector +from ..update import change_active_modifier_parameter from ..utils import GizmoUtils, GizmoGroupUtils @@ -201,22 +202,26 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate): 'down_limits', self.int_value_down_limits) def modal(self, context, event, tweak): - st = time() - self.clear_cache() + self.clear_point_cache() if self.modifier_is_use_origin_axis: self.new_origin_empty_object() + # return {'RUNNING_MODAL'} self.difference_value = self.modifier_up_limits - self.modifier_down_limits self.middle_limits_value = (self.modifier_up_limits + self.modifier_down_limits) / 2 - self.set_prop_value(event) - self.clear_cache() - self.update_object_origin_matrix() - # self.update_deform_wireframe(self.get_depsgraph(origin_object)) + try: + self.set_prop_value(event) + self.clear_point_cache() + self.update_object_origin_matrix() + except Exception: + ... + # return {'FINISHED'} self.update_header_text(context) return_handle = self.event_handle(event) - print('modal time sum ', time() - st) + change_active_modifier_parameter.update_modifier_parameter() + self.update_deform_wireframe() return return_handle diff --git a/simple_deform_helper/operators.py b/simple_deform_helper/operators.py index 9d35d5ce5..0601f9882 100644 --- a/simple_deform_helper/operators.py +++ b/simple_deform_helper/operators.py @@ -26,7 +26,7 @@ class DeformAxisOperator(Operator, GizmoUtils): return {'RUNNING_MODAL'} def modal(self, context, event): - self.clear_cache() + self.clear_point_cache() mod = context.object.modifiers.active mod.deform_axis = self.Deform_Axis empty = self.new_origin_empty_object() @@ -39,7 +39,7 @@ class DeformAxisOperator(Operator, GizmoUtils): ('max_z', self.Z_Value), ('min_z', self.Z_Value), ): - setattr(empty.constraints[self.G_CON_LIMIT_NAME], limit, value) + setattr(empty.constraints[self.G_NAME_CON_LIMIT], limit, value) if ((not is_positive) and self.Is_Positive) or (is_positive and (not self.Is_Positive)): mod.angle = mod.angle * -1 diff --git a/simple_deform_helper/panel.py b/simple_deform_helper/panel.py index c4f8e38bb..2d86f08c3 100644 --- a/simple_deform_helper/panel.py +++ b/simple_deform_helper/panel.py @@ -41,7 +41,7 @@ class SimpleDeformHelperToolPanel(Panel, GizmoUtils): 'show_set_axis_button', icon='EMPTY_AXIS', text='') - if mod.deform_method == 'BEND': + if pref.modifier_deform_method_is_bend: layout.prop(pref, 'display_bend_axis_switch_gizmo', toggle=1) diff --git a/simple_deform_helper/preferences.py b/simple_deform_helper/preferences.py index 36b6a76f7..473566b50 100644 --- a/simple_deform_helper/preferences.py +++ b/simple_deform_helper/preferences.py @@ -96,12 +96,10 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils): row.prop(mod, show_type) -class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup): +class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup, GizmoUtils): def _limits_up(self, context): - mod = context.object.modifiers - if mod and (mod.active.type == 'SIMPLE_DEFORM'): - mod = mod.active - mod.limits[1] = self.up_limits + if self.active_modifier_is_simple_deform: + self.modifier.limits[1] = self.up_limits up_limits: FloatProperty(name='up', description='UP Limits(Red)', @@ -111,10 +109,8 @@ class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup): min=0) def _limits_down(self, context): - mod = context.object.modifiers - if mod and (mod.active.type == 'SIMPLE_DEFORM'): - mod = mod.active - mod.limits[0] = self.down_limits + if self.active_modifier_is_simple_deform: + self.modifier.limits[0] = self.down_limits down_limits: FloatProperty(name='down', description='Lower limit(Green)', diff --git a/simple_deform_helper/update.py b/simple_deform_helper/update.py index e5732512a..d6fb78fca 100644 --- a/simple_deform_helper/update.py +++ b/simple_deform_helper/update.py @@ -1,78 +1,147 @@ # SPDX-License-Identifier: GPL-2.0-or-later +from functools import cache + +import bpy + from .utils import GizmoUpdate +gizmo = GizmoUpdate() + +"""depsgraph_update_post cannot listen to users modifying modifier parameters +Use timers to watch and use cache +""" + class update_public: - _event_func_list = {} - update_func: 'function' - tmp_save_data = {} + _events_func_list = {} run_time = 0.2 + @classmethod + def timers_update_poll(cls) -> bool: + return True + + @classmethod + @cache + def update_poll(cls) -> bool: + return True + + @classmethod + def _update_func_call_timer(cls): + if cls.timers_update_poll(): + for c, func_list in cls._events_func_list.items(): + if func_list and c.update_poll(): + for func in func_list: + func() + cls.clear_cache_events() + return cls.run_time + + @classmethod + def clear_cache_events(cls): + for cl in cls._events_func_list.keys(): + if getattr(cl, 'clear_cache', False): + cl.clear_cache() + + @classmethod + def clear_cache(cls): + cls.update_poll.cache_clear() + + @classmethod + def append(cls, item): + if cls not in cls._events_func_list: + cls._events_func_list[cls] = [] + cls._events_func_list[cls].append(item) + + @classmethod + def remove(cls, item): + if item in cls._events_func_list[cls]: + cls._events_func_list[cls].remove(item) + + # --------------- reg and unreg @classmethod def register(cls): - import bpy - bpy.app.timers.register(cls.update_func, persistent=True) + from bpy.app import timers + func = cls._update_func_call_timer + if not timers.is_registered(func): + timers.register(func, persistent=True) + else: + print('cls timers is registered', cls) @classmethod def unregister(cls): from bpy.app import timers - func = cls.update_func + func = cls._update_func_call_timer if timers.is_registered(func): timers.unregister(func) - - @classmethod - def _update_call(cls): - for i in cls._event_func_list[cls]: - i() - - @classmethod - def append(cls, item): - if cls not in cls._event_func_list: - cls._event_func_list[cls] = [] - cls._event_func_list[cls].append(item) - - @classmethod - def remove(cls, item): - if item in cls._event_func_list[cls]: - cls._event_func_list[cls].remove(item) + else: + print('cls timers is not registered', cls) + cls._events_func_list.clear() -class change_active_object(update_public): +class simple_update(update_public, GizmoUpdate): tmp_save_data = {} - from bpy.app.handlers import depsgraph_update_post, persistent - handler_type = depsgraph_update_post @classmethod - def update_func(cls): - import bpy - name = bpy.context.object.name - key = 'active_object' - if key not in cls.tmp_save_data or cls.tmp_save_data[key] != name: - cls._update_call() - cls.tmp_save_data[key] = name - return cls.run_time + def timers_update_poll(cls): + obj = bpy.context.object + if not cls.context_mode_is_object(): + ... + elif not obj: + ... + elif not cls.obj_type_is_mesh_or_lattice(obj): + ... + elif cls.mod_is_simple_deform_type(obj.modifiers.active): + return True + return False -class change_active_simple_deform_modifier(update_public): +class change_active_object(simple_update): + @classmethod + @cache + def update_poll(cls): + return cls.is_change_active_object() @classmethod - def update_func(cls): + def is_change_active_object(cls, change_data=True): import bpy obj = bpy.context.object - if not obj or obj.type != 'MESH': - return cls.run_time - name = obj.name key = 'active_object' + if key not in cls.tmp_save_data: + if change_data: + cls.tmp_save_data[key] = name + return True + + elif cls.tmp_save_data[key] != name: + if change_data: + cls.tmp_save_data[key] = name + return True + return False + + +class change_active_simple_deform_modifier(simple_update): + + @classmethod + @cache + def update_poll(cls): + return cls.is_change_active_simple_deform() + + @classmethod + def is_change_active_simple_deform(cls) -> bool: + import bpy + obj = bpy.context.object modifiers = cls.get_modifiers_data(obj) - change_modifiers = 'modifiers' not in cls.tmp_save_data or cls.tmp_save_data['modifiers'] != modifiers - if key not in cls.tmp_save_data or cls.tmp_save_data[key] != name: + + def update(): cls.tmp_save_data['modifiers'] = modifiers - cls.tmp_save_data[key] = name - elif change_modifiers: - cls.tmp_save_data['modifiers'] = modifiers - cls._update_call() - return cls.run_time + + if change_active_object.update_poll(): + update() + elif 'modifiers' not in cls.tmp_save_data: + update() + elif cls.tmp_save_data['modifiers'] != modifiers: + update() + return True + return False @classmethod def get_modifiers_data(cls, obj): @@ -81,20 +150,52 @@ class change_active_simple_deform_modifier(update_public): 'modifiers': list(i.name for i in obj.modifiers)} -gizmo = GizmoUpdate() +class change_active_modifier_parameter(simple_update): + key = 'active_modifier_parameter' + + @classmethod + @cache + def update_poll(cls): + return gizmo.active_modifier_is_simple_deform and cls.is_change_active_simple_parameter() + + @classmethod + def update_modifier_parameter(cls, modifier_parameter=None): + """Run this function when the gizmo is updated to avoid duplicate updates + """ + if not modifier_parameter: + modifier_parameter = cls.get_modifiers_parameter(gizmo.modifier) + cls.tmp_save_data[cls.key] = modifier_parameter + + @classmethod + def change_modifier_parameter(cls) -> bool: + mod_data = cls.get_modifiers_parameter(gizmo.modifier) + return cls.key in cls.tmp_save_data and cls.tmp_save_data[cls.key] == mod_data + + @classmethod + def is_change_active_simple_parameter(cls): + parameter = cls.get_modifiers_parameter(gizmo.modifier) + if change_active_object.update_poll(): + cls.update_modifier_parameter(parameter) + elif change_active_simple_deform_modifier.update_poll(): + cls.update_modifier_parameter(parameter) + elif cls.key not in cls.tmp_save_data: + cls.update_modifier_parameter(parameter) + elif cls.tmp_save_data[cls.key] != parameter: + cls.update_modifier_parameter(parameter) + return True + return False def register(): - change_active_object.register() - change_active_simple_deform_modifier.register() + simple_update.register() - change_active_object.append(gizmo.update_multiple_modifiers_data) - change_active_simple_deform_modifier.append(gizmo.update_multiple_modifiers_data) + def p(): + gizmo.update_multiple_modifiers_data() + + change_active_object.append(p) + change_active_modifier_parameter.append(p) + change_active_simple_deform_modifier.append(p) def unregister(): - change_active_object.remove(gizmo.update_multiple_modifiers_data) - change_active_simple_deform_modifier.remove(gizmo.update_multiple_modifiers_data) - - change_active_object.unregister() - change_active_simple_deform_modifier.unregister() + simple_update.unregister() diff --git a/simple_deform_helper/utils.py b/simple_deform_helper/utils.py index 259a0b468..f7f9092ff 100644 --- a/simple_deform_helper/utils.py +++ b/simple_deform_helper/utils.py @@ -4,7 +4,6 @@ import math import uuid from functools import cache from os.path import dirname, basename, realpath -from time import time import bpy import numpy as np @@ -13,27 +12,38 @@ from mathutils import Vector, Matrix, Euler class PublicData: - """Public data class, all fixed data will be placed here + """Public data class, where all fixed data will be placed +Classify each different type of data separately and cache it to avoid getting stuck due to excessive update frequency """ - G_CustomShape = {} - G_GizmoData = {} - G_Modifiers_Data = {} + G_CustomShape = {} # + G_HandleData = {} # Save draw Handle + + G_DeformDrawData = {} # Save Deform Vertex And Indices,Update data only when updating deformation boxes + + G_MultipleModifiersBoundData = {} + G_INDICES = ( (0, 1), (0, 2), (1, 3), (2, 3), (4, 5), (4, 6), (5, 7), (6, 7), - (0, 4), (1, 5), (2, 6), (3, 7)) - + (0, 4), (1, 5), (2, 6), (3, 7)) # The order in which the 8 points of the bounding box are drawn G_NAME = 'ViewSimpleDeformGizmo_' # Temporary use files prefix - G_ADDON_NAME = basename(dirname(realpath(__file__))) # "simple_deform_helper" - G_CON_LIMIT_NAME = G_NAME + 'constraints_limit_rotation' # constraints name - G_MODIFIERS_PROPERTY = [ # copy modifier data + G_DEFORM_MESH_NAME = G_NAME + 'DeformMesh' + G_TMP_MULTIPLE_MODIFIERS_MESH = 'TMP_' + G_NAME + 'MultipleModifiersMesh' + G_SUB_LEVELS = 7 + + G_NAME_EMPTY_AXIS = G_NAME + '_Empty_' + G_NAME_CON_LIMIT = G_NAME + 'ConstraintsLimitRotation' # constraints name + G_NAME_CON_COPY_ROTATION = G_NAME + 'ConstraintsCopyRotation' + G_ADDON_NAME = basename(dirname(realpath(__file__))) # "simple_deform_helper" + + G_MODIFIERS_PROPERTY = [ # Copy modifier data 'angle', 'deform_axis', 'deform_method', 'factor', 'invert_vertex_group', - 'limits', + 'limits', # bpy.types.bpy_prop_array 'lock_x', 'lock_y', 'lock_z', @@ -72,10 +82,11 @@ class PublicData: def from_selected_obj_generate_json(cls): """Export selected object vertex data as gizmo custom paint data The output file should be in the blender folder + gizmo.json """ import json data = {} - for obj in bpy.context.selected_object: + for obj in bpy.context.selected_objects: data[obj.name] = cls.from_mesh_get_triangle_face_co(obj.data) print(data) with open('gizmo.json', 'w+') as f: @@ -96,10 +107,14 @@ class PublicClass(PublicData): class PublicPoll(PublicClass): + @classmethod + def context_mode_is_object(cls) -> bool: + return bpy.context.mode == 'OBJECT' + @classmethod def simple_deform_modifier_is_simple(cls, context): """ - Active Object in ('MESH', 'LATTICE') + Active Object in ('MESH', 'LATTICE') Active Modifier Type Is 'SIMPLE_DEFORM' and show_viewport :param context:bpy.types.Object :return: @@ -112,10 +127,9 @@ class PublicPoll(PublicClass): if not mod: return False - available_obj_type = obj and (obj.type in ('MESH', 'LATTICE')) - available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM') - is_available_obj = available_modifiers_type and available_obj_type - is_obj_mode = context.mode == 'OBJECT' + available_obj_type = cls.obj_type_is_mesh_or_lattice(obj) + is_available_obj = cls.mod_is_simple_deform_type(mod) and available_obj_type + is_obj_mode = cls.context_mode_is_object() show_mod = mod.show_viewport not_is_self_mesh = obj.name != cls.G_NAME return is_available_obj and is_obj_mode and show_mod and not_is_self_mesh @@ -159,7 +173,51 @@ class PublicPoll(PublicClass): return poll and not_switch -class PublicUtils(PublicPoll): +class PublicTranslate(PublicPoll): + @classmethod + def translate_text(cls, text): + return bpy.app.translations.pgettext(text) + + @classmethod + def translate_header_text(cls, mode, value): + return cls.translate_text(mode) + ':{}'.format(value) + + +class GizmoClassMethod(PublicTranslate): + + @classmethod + def get_depsgraph(cls, obj: 'bpy.types.Object'): + """ + @param obj: dep obj + @return: If there is no input obj, reverse the active object evaluated + """ + context = bpy.context + if obj is None: + obj = context.object + dep = context.evaluated_depsgraph_get() + return obj.evaluated_get(dep) + + @classmethod + def get_vector_axis(cls, mod): + axis = mod.deform_axis + if 'BEND' == mod.deform_method: + vector_axis = Vector((0, 0, 1)) if axis in ( + 'Y', 'X') else Vector((1, 0, 0)) + else: + vector = (Vector((1, 0, 0)) if ( + axis == 'X') else Vector((0, 1, 0))) + vector_axis = Vector((0, 0, 1)) if ( + axis == 'Z') else vector + return vector_axis + + @classmethod + def get_modifiers_parameter(cls, modifier): + prop = bpy.types.bpy_prop_array + return list( + getattr(modifier, i)[:] if type(getattr(modifier, i)) == prop else getattr(modifier, i) + for i in cls.G_MODIFIERS_PROPERTY + ) + @classmethod def value_limit(cls, value, max_value=1, min_value=0): """ @@ -183,18 +241,6 @@ class PublicUtils(PublicPoll): """ return number == abs(number) - @classmethod - def get_depsgraph(cls, obj: 'bpy.types.Object'): - """ - @param obj: dep obj - @return: If there is no input obj, reverse the active object evaluated - """ - context = bpy.context - if obj is None: - obj = context.object - dep = context.evaluated_depsgraph_get() - return obj.evaluated_get(dep) - @classmethod def link_obj_to_active_collection(cls, obj: 'bpy.types.Object'): context = bpy.context @@ -203,22 +249,6 @@ class PublicUtils(PublicPoll): objects.link( obj) - @classmethod - def properties_is_modifier(cls) -> bool: - """Returns whether there is a modifier property panel open in the active window. - If it is open, it returns to True else False - """ - for area in bpy.context.screen.areas: - if area.type == 'PROPERTIES': - for space in area.spaces: - if space.type == 'PROPERTIES' and space.context == 'MODIFIER': - return True - return False - - @classmethod - def bound_box_to_list(cls, obj: 'bpy.types.Object'): - return tuple(i[:] for i in obj.bound_box) - @classmethod def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> '[Vector,Vector]': if obj.type == 'MESH': @@ -271,36 +301,6 @@ class PublicUtils(PublicPoll): return list((aa + bb) / 2 for (aa, bb) in point_list) - @classmethod - def translate_text(cls, text): - return bpy.app.translations.pgettext(text) - - @classmethod - def translate_header_text(cls, mode, value): - return cls.translate_text(mode) + ':{}'.format(value) - - -class GizmoClassMethod(PublicUtils): - @classmethod - def get_vector_axis(cls, mod): - axis = mod.deform_axis - if 'BEND' == mod.deform_method: - vector_axis = Vector((0, 0, 1)) if axis in ( - 'Y', 'X') else Vector((1, 0, 0)) - else: - vector = (Vector((1, 0, 0)) if ( - axis == 'X') else Vector((0, 1, 0))) - vector_axis = Vector((0, 0, 1)) if ( - axis == 'Z') else vector - return vector_axis - - @classmethod - def get_bound_co_data(cls): - if 'co' not in cls.G_GizmoData: - cls.G_GizmoData['co'] = cls.get_mesh_max_min_co( - bpy.context.object) - return cls.G_GizmoData['co'] - @classmethod def tow_co_to_coordinate(cls, data): ((min_x, min_y, min_z), (max_x, max_y, max_z)) = data @@ -315,6 +315,29 @@ class GizmoClassMethod(PublicUtils): Vector((min_x, max_y, max_z)) ) + @classmethod + def mod_is_simple_deform_type(cls, mod): + return mod and mod.type == 'SIMPLE_DEFORM' + + @classmethod + def obj_type_is_mesh_or_lattice(cls, obj: 'bpy.types.Object'): + return obj and (obj.type in ('MESH', 'LATTICE')) + + @classmethod + def from_vertices_new_mesh(cls, name, vertices): + new_mesh = bpy.data.meshes.new(name) + new_mesh.from_pydata(vertices, cls.G_INDICES, []) + new_mesh.update() + return new_mesh + + @classmethod + def copy_modifier_parameter(cls, old_mod, new_mod): + for prop_name in cls.G_MODIFIERS_PROPERTY: + origin_value = getattr(old_mod, prop_name, None) + is_array_prop = type(origin_value) == bpy.types.bpy_prop_array + value = origin_value[:] if is_array_prop else origin_value + setattr(new_mod, prop_name, value) + class PublicProperty(GizmoClassMethod): @@ -332,15 +355,17 @@ class PublicProperty(GizmoClassMethod): top, bottom, left, right, front, back = self.modifier_bound_box_pos mod = self.modifier g_l = self.__from_up_down_point_get_limits_point - if self.modifier.origin: + origin = self.modifier.origin + if origin: vector_axis = self.get_vector_axis(mod) - origin_mat = mod.origin.matrix_basis.to_3x3() - axis_ = origin_mat @ vector_axis + matrix = self.modifier.origin.matrix_local + origin_mat = matrix.to_3x3() + axis = origin_mat @ vector_axis point_lit = [[top, bottom], [left, right], [front, back]] for f in range(point_lit.__len__()): i = point_lit[f][0] j = point_lit[f][1] - angle = self.point_to_angle(i, j, f, axis_) + angle = self.point_to_angle(i, j, f, axis) if abs(angle - 180) < 0.00001: up_point, down_point = j, i up_limits, down_limits = g_l(j, i) @@ -349,7 +374,6 @@ class PublicProperty(GizmoClassMethod): up_point, down_point = i, j up_limits, down_limits = g_l(i, j) point_lit[f][0], point_lit[f][1] = up_limits, down_limits - [[top, bottom], [left, right], [front, back]] = point_lit else: axis = self.modifier_deform_axis @@ -383,28 +407,31 @@ class PublicProperty(GizmoClassMethod): @classmethod def clear_cache(cls): - cls._each_face_pos.cache_clear() + cls.clear_point_cache() + cls.clear_modifiers_data() + + @classmethod + def clear_point_cache(cls): cls._get_limits_point_and_bound_box_co.cache_clear() @classmethod - def clear_data(cls): - cls.G_GizmoData.clear() + def clear_modifiers_data(cls): + cls.G_MultipleModifiersBoundData.clear() @classmethod - def clear_modifiers_data(cls): - cls.G_Modifiers_Data.clear() + def clear_deform_data(cls): + cls.G_DeformDrawData.clear() # --------------- Cache Data ---------------------- - - @property - def each_face_pos(self): - matrix = Matrix() - matrix.freeze() - return self._each_face_pos(matrix, self.get_bound_co_data()) - @property def modifier_bound_co(self): - return self.G_Modifiers_Data.get(self.modifier.name, self.get_bound_co_data()) + def get_bound_co_data(): + key = 'self.modifier.name' + if key not in self.G_MultipleModifiersBoundData: + self.G_MultipleModifiersBoundData[key] = self.get_mesh_max_min_co(self.obj) + return self.G_MultipleModifiersBoundData[key] + + return self.G_MultipleModifiersBoundData.get(self.modifier.name, get_bound_co_data()) @property def modifier_bound_box_pos(self): @@ -423,13 +450,12 @@ class PublicProperty(GizmoClassMethod): return self.matrix_calculation(self.obj_matrix_world, bound) @property - def modifier_origin_angle_is_available(self): + def modifier_origin_is_available(self): try: self._get_limits_point_and_bound_box_co() return True except UnboundLocalError: - print('modifier_origin_angle_is_available') - self.clear_cache() + self.clear_point_cache() return False # --------------- Compute Data ---------------------- @@ -471,6 +497,11 @@ class PublicProperty(GizmoClassMethod): if self.active_modifier_is_simple_deform: return self.modifier.deform_method in ('TWIST', 'BEND') + @property + def modifier_deform_method_is_bend(self): + if self.active_modifier_is_simple_deform: + return self.modifier.deform_method == 'BEND' + @property def modifier_up_limits(self): if self.modifier: @@ -483,7 +514,7 @@ class PublicProperty(GizmoClassMethod): @property def active_modifier_is_simple_deform(self): - return self.modifier and self.modifier.type == 'SIMPLE_DEFORM' + return self.mod_is_simple_deform_type(self.modifier) # ----- point @property @@ -526,7 +557,11 @@ class PublicProperty(GizmoClassMethod): @property def modifier_is_use_origin_axis(self): - return self.obj_origin_property_group.origin_mode != 'NOT' and not self.modifier.origin + return self.obj_origin_property_group.origin_mode != 'NOT' + + @property + def modifier_is_have_origin(self): + return self.modifier_is_use_origin_axis and self.modifier.origin class GizmoUpdate(PublicProperty): @@ -543,7 +578,7 @@ class GizmoUpdate(PublicProperty): if origin.parent != obj: origin.parent = obj origin.rotation_euler.zero() - if not self.modifier_origin_angle_is_available: + if not self.modifier_origin_is_available: origin.location.zero() origin.scale = 1, 1, 1 @@ -552,7 +587,7 @@ class GizmoUpdate(PublicProperty): obj = self.obj origin = mod.origin if not origin: - new_name = self.G_NAME + '_Empty_' + str(uuid.uuid4()) + new_name = self.G_NAME_EMPTY_AXIS + str(uuid.uuid4()) origin_object = bpy.data.objects.new(new_name, None) self.link_obj_to_active_collection(origin_object) origin_object.hide_set(True) @@ -564,7 +599,7 @@ class GizmoUpdate(PublicProperty): if origin_object == obj: return # add constraints - name = self.G_CON_LIMIT_NAME + name = self.G_NAME_CON_LIMIT if origin_object.constraints.keys().__len__() > 2: origin_object.constraints.clear() if name in origin_object.constraints.keys(): @@ -579,7 +614,7 @@ class GizmoUpdate(PublicProperty): limit_constraints.use_limit_x = True limit_constraints.use_limit_y = True limit_constraints.use_limit_z = True - con_copy_name = self.G_NAME + 'constraints_copy_rotation' + con_copy_name = self.G_NAME_CON_COPY_ROTATION if con_copy_name in origin_object.constraints.keys(): copy_constraints = origin.constraints.get(con_copy_name) else: @@ -590,16 +625,15 @@ class GizmoUpdate(PublicProperty): copy_constraints.mix_mode = 'BEFORE' copy_constraints.target_space = 'WORLD' copy_constraints.owner_space = 'WORLD' + origin_mode = self.obj.SimpleDeformGizmo_PropertyGroup.origin_mode + origin_object.SimpleDeformGizmo_PropertyGroup.origin_mode = origin_mode self.fix_origin_parent_and_angle() return origin_object def update_object_origin_matrix(self): - st = time() - origin_mode = self.origin_mode - origin_object = self.modifier.origin - is_use = self.modifier_is_use_origin_axis - - if origin_object and is_use: + if self.modifier_is_have_origin: + origin_mode = self.origin_mode + origin_object = self.modifier.origin if origin_mode == 'UP_LIMITS': origin_object.matrix_world.translation = Vector(self.point_limits_up) elif origin_mode == 'DOWN_LIMITS': @@ -610,80 +644,106 @@ class GizmoUpdate(PublicProperty): elif origin_mode == 'MIDDLE': translation = (self.point_up + self.point_down) / 2 origin_object.matrix_world.translation = translation - print('update_object_origin_matrix', time() - st) def update_multiple_modifiers_data(self): - print('update_multiple_modifiers_data', self) - st = time() obj = self.obj context = bpy.context - if obj.type not in ('MESH', 'LATTICE') or not self.simple_deform_public_poll(context): + if not self.obj_type_is_mesh_or_lattice(obj) or not self.simple_deform_modifier_is_simple(context): return - self.clear_cache() + self.clear_point_cache() + self.clear_modifiers_data() data = bpy.data - name = self.G_NAME - origin_object = data.objects.get(name) + name = self.G_TMP_MULTIPLE_MODIFIERS_MESH - # update multiple simple_deform bound data - if origin_object: - data.objects.remove(origin_object) + # del old tmp object + old_object = data.objects.get(name) + if old_object: + data.objects.remove(old_object) if data.meshes.get(name): data.meshes.remove(data.meshes.get(name)) - vertices = self.tow_co_to_coordinate(self.get_bound_co_data()) - new_mesh = data.meshes.new(name) - new_mesh.from_pydata(vertices, self.G_INDICES, []) - new_mesh.update() - deform_obj = data.objects.new(name, new_mesh) + """get origin mesh bound box as multiple basic mesh + add multiple modifiers and get depsgraph obj bound box + """ + vertices = self.tow_co_to_coordinate(self.get_mesh_max_min_co(self.obj)) + new_mesh = self.from_vertices_new_mesh(name, vertices) + modifiers_obj = data.objects.new(name, new_mesh) - self.link_obj_to_active_collection(deform_obj) - if deform_obj == obj: + self.link_obj_to_active_collection(modifiers_obj) + if modifiers_obj == obj: # is cycles return - if deform_obj.parent != obj: - deform_obj.parent = obj + if modifiers_obj.parent != obj: + modifiers_obj.parent = obj - deform_obj.modifiers.clear() - subdivision = deform_obj.modifiers.new('1', 'SUBSURF') - subdivision.levels = 7 - self.G_GizmoData['co'] = self.get_bound_co_data() + modifiers_obj.modifiers.clear() + subdivision = modifiers_obj.modifiers.new('1', 'SUBSURF') + subdivision.levels = self.G_SUB_LEVELS - for mo in context.object.modifiers: - if mo.type == 'SIMPLE_DEFORM': - obj = self.get_depsgraph(deform_obj) - self.G_Modifiers_Data[mo.name] = self.get_mesh_max_min_co(obj) - simple_deform = deform_obj.modifiers.new( - mo.name, 'SIMPLE_DEFORM') - simple_deform.deform_method = mo.deform_method - simple_deform.deform_axis = mo.deform_axis - simple_deform.lock_x = mo.lock_x - simple_deform.lock_y = mo.lock_y - simple_deform.lock_z = mo.lock_z - simple_deform.origin = mo.origin - simple_deform.limits[1] = mo.limits[1] - simple_deform.limits[0] = mo.limits[0] - simple_deform.angle = mo.angle - simple_deform.show_viewport = mo.show_viewport - deform_obj.hide_select = True - deform_obj.hide_set(True) - deform_obj.hide_viewport = False - deform_obj.hide_render = True - deform_obj.hide_viewport = True - deform_obj.hide_set(True) - print('multiple_modifiers', time() - st) + for mod in context.object.modifiers: + if self.mod_is_simple_deform_type(mod): + dep_bound_tow_co = self.get_mesh_max_min_co(self.get_depsgraph(modifiers_obj)) + self.G_MultipleModifiersBoundData[mod.name] = dep_bound_tow_co + new_mod = modifiers_obj.modifiers.new(mod.name, 'SIMPLE_DEFORM') + self.copy_modifier_parameter(mod, new_mod) + data.objects.remove(modifiers_obj) - def update_deform_wireframe(self, obj): + def update_deform_wireframe(self): if not self.pref.update_deform_wireframe: return + # obj = self.obj + name = self.modifier.name + deform_name = self.G_DEFORM_MESH_NAME + + co = self.G_MultipleModifiersBoundData[name] + + deform_obj = bpy.data.objects.get(deform_name, None) + + if not deform_obj: + a, b = 0.5, -0.5 + vertices = self.tow_co_to_coordinate(((b, b, b), (a, a, a))) + new_mesh = self.from_vertices_new_mesh(name, vertices) + deform_obj = bpy.data.objects.new(deform_name, new_mesh) + deform_obj.hide_select = True + # deform_obj.hide_set(True) + deform_obj.hide_render = True + deform_obj.hide_viewport = True + + self.link_obj_to_active_collection(deform_obj) + + deform_obj.parent = self.obj + + tmv = deform_obj.hide_viewport + tmh = deform_obj.hide_get() + deform_obj.hide_viewport = False + deform_obj.hide_set(False) + + # Update Matrix + deform_obj.matrix_world = Matrix() + center = (co[0] + co[1]) / 2 + scale = co[1] - co[0] + deform_obj.matrix_world = self.obj_matrix_world @ deform_obj.matrix_world + deform_obj.location = center + deform_obj.scale = scale + + # Update Modifier data + mods = deform_obj.modifiers + mods.clear() + subdivision = mods.new('1', 'SUBSURF') + subdivision.levels = self.G_SUB_LEVELS + + new_mod = mods.new(name, 'SIMPLE_DEFORM') + self.copy_modifier_parameter(self.modifier, new_mod) + + # Get vertices data context = bpy.context - matrix = self.obj_matrix_world.copy() + obj = self.get_depsgraph(deform_obj) + matrix = deform_obj.matrix_world.copy() ver_len = obj.data.vertices.__len__() edge_len = obj.data.edges.__len__() - - if 'numpy_data' not in self.G_GizmoData: - self.G_GizmoData['numpy_data'] = {} - - numpy_data = self.G_GizmoData['numpy_data'] + if 'numpy_data' not in self.G_DeformDrawData: + self.G_DeformDrawData['numpy_data'] = {} + numpy_data = self.G_DeformDrawData['numpy_data'] key = (ver_len, edge_len) if key in numpy_data: list_edges, list_vertices = numpy_data[key] @@ -702,10 +762,13 @@ class GizmoUpdate(PublicProperty): obj.data.edges.foreach_get('vertices', list_edges) indices = list_edges.reshape((edge_len, 2)) + modifiers = self.get_modifiers_parameter(self.modifier) limits = context.object.modifiers.active.limits[:] - modifiers = [getattr(context.object.modifiers.active, i) - for i in self.G_MODIFIERS_PROPERTY] - self.G_GizmoData['simple_deform_box_data'] = (ver, indices, matrix, modifiers, limits[:]) + + deform_obj.hide_viewport = tmv + deform_obj.hide_set(tmh) + + self.G_DeformDrawData['simple_deform_bound_data'] = (ver, indices, self.obj_matrix_world, modifiers, limits[:]) class GizmoUtils(GizmoUpdate): @@ -748,16 +811,16 @@ class GizmoUtils(GizmoUpdate): def __update_matrix_func(self, context): func = getattr(self, 'update_gizmo_matrix', None) - if func and self.modifier_origin_angle_is_available: + if func and self.modifier_origin_is_available: func(context) def draw(self, context): - if self.modifier_origin_angle_is_available: + if self.modifier_origin_is_available: self.draw_custom_shape(self.custom_shape[self.draw_type]) self.__update_matrix_func(context) def draw_select(self, context, select_id): - if self.modifier_origin_angle_is_available: + if self.modifier_origin_is_available: self.draw_custom_shape( self.custom_shape[self.draw_type], select_id=select_id) self.__update_matrix_func(context) @@ -788,6 +851,11 @@ class GizmoUtils(GizmoUpdate): self.pref.update_deform_wireframe = self.pref.update_deform_wireframe ^ True return {'RUNNING_MODAL'} + @staticmethod + def tag_redraw(context): + if context.area: + context.area.tag_redraw() + class GizmoGroupUtils(GizmoUtils): bl_space_type = 'VIEW_3D' @@ -848,6 +916,22 @@ class Tmp: rot = rot.to_matrix() self.matrix_basis = self.matrix_basis @ rot.to_4x4() + @classmethod + def bound_box_to_list(cls, obj: 'bpy.types.Object'): + return tuple(i[:] for i in obj.bound_box) + + @classmethod + def properties_is_modifier(cls) -> bool: + """Returns whether there is a modifier property panel open in the active window. + If it is open, it returns to True else False + """ + for area in bpy.context.screen.areas: + if area.type == 'PROPERTIES': + for space in area.spaces: + if space.type == 'PROPERTIES' and space.context == 'MODIFIER': + return True + return False + def register(): PublicData.load_gizmo_data() -- 2.30.2