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 554 additions and 598 deletions
Showing only changes of commit 5beacd168a - Show all commits

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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