new addon simple_deform_helper #104464

Open
EMM wants to merge 29 commits from Guai_Wo_Ge_EMM/blender-addons:simple_deform_helper into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
10 changed files with 492 additions and 376 deletions
Showing only changes of commit af50b2f258 - Show all commits

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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):

View File

@ -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'}

View File

@ -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

View File

@ -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')

View File

@ -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()