import bpy, bmesh
from .... ui_framework.master import Master
from .... utility.base_modal_controls import Base_Modal_Controls
from .... utils.toggle_view3d_panels import collapse_3D_view_panels
from .... utils.modal_frame_drawing import draw_modal_frame
from .... utility import method_handler
from . import Mode
from . dice_3d import Edit_3D, get_boxelize_ref
from . dice_2d import Edit_2D
from . dice_line import Edit_Line
from . shader import SD
from . interface import draw_FAS, setup_form, alter_form_layout
from . struct import get_boxelize_ref
DESC = """Dice Cut
LMB - Dice on last used axes
Shift + LMB - Dice active from selection
Ctrl + LMB - Dice on all axes
Alt + LMB - Smart Apply Dice (applies select modifiers)
Press H for help"""
class HOPS_OT_BoolDice_V2(bpy.types.Operator):
bl_idname = "hops.bool_dice_v2"
bl_label = "Dice V2"
bl_options = {'REGISTER', 'UNDO', 'BLOCKING'}
bl_description = DESC
dice_axis: bpy.props.EnumProperty(
name="Dice Axis Memory",
description="Axis for dice to start on",
items=[
("X", "X", ""),
("Y", "Y", ""),
("Z", "Z", "")],
default='X')
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == 'MESH' and obj.mode in {'OBJECT', 'EDIT'}
def invoke(self, context, event):
# Bad Selection
obj = context.active_object
if context.active_object.mode == 'EDIT':
bm = bmesh.from_edit_mesh(obj.data)
if len([v for v in bm.verts if v.select]) < 2:
bpy.ops.hops.display_notification(info="Select More Geo")
return {'CANCELLED'}
if context.active_object.mode == 'EDIT':
obj.update_from_editmode()
# States
self.edit_mode = Mode.DICE_3D
self.show_wire = obj.show_wire
# Editors
self.dice_3d = Edit_3D(self, context, event)
self.dice_2d = Edit_2D(self, context, event)
self.dice_line = Edit_Line(self, context, event)
# Setup
if self.edit_mode == Mode.DICE_3D:
if not self.dice_3d.setup(context, event):
bpy.ops.hops.display_notification(info="Invalid Selection")
return {'CANCELLED'}
# Ctrl entry
if event.ctrl:
get_boxelize_ref().active = True
# Form
self.form_exit = False
self.form = None
setup_form(self, context, event)
# Base Systems
self.master = Master(context)
self.master.only_use_fast_ui = True
self.base_controls = Base_Modal_Controls(context, event)
self.original_tool_shelf, self.original_n_panel = collapse_3D_view_panels()
self.draw_handle_2d = bpy.types.SpaceView3D.draw_handler_add(self.safe_draw_2d, (context,), 'WINDOW', 'POST_PIXEL')
# Graphics Modal
if self.edit_mode == Mode.DICE_3D:
SD.pause_drawing = False
elif self.edit_mode == Mode.DICE_2D:
SD.pause_drawing = True
if SD.draw_modal_running == False:
bpy.ops.hops.draw_dice_v2('INVOKE_DEFAULT')
else:
SD.reset_modal = True
SD.dice_modal_running = True
SD.reset_data()
SD.see_though = False
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
# Base System
self.master.receive_event(event)
self.base_controls.update(context, event)
self.form.update(context, event)
# Form Exit
if self.form_exit:
return self.confirm_exit(context, event)
# Navigation
if self.base_controls.pass_through:
if not self.form.active():
return {'PASS_THROUGH'}
# Allow wire / wire shaded
elif event.type == 'Z' and (event.shift or event.alt):
if not self.form.active():
return {'PASS_THROUGH'}
# Confirm
elif self.base_controls.confirm:
if not self.form.active():
# Line dice uses Left Mouse
if self.edit_mode == Mode.DICE_LINE:
if event.type != 'LEFTMOUSE':
return self.confirm_exit(context, event)
# Regular Cases
else:
return self.confirm_exit(context, event)
# Cancel
elif self.base_controls.cancel:
return self.cancel_exit(context)
# Form Toggle
elif event.type == 'TAB' and event.value == 'PRESS':
if self.form.is_dot_open():
self.form.close_dot()
else:
self.form.open_dot()
# Mode Switch
if not self.form.active():
if event.type == 'V' and event.value == 'PRESS':
if self.edit_mode == Mode.DICE_3D:
self.edit_mode = Mode.DICE_2D
elif self.edit_mode == Mode.DICE_2D:
self.edit_mode = Mode.DICE_LINE
elif self.edit_mode == Mode.DICE_LINE:
self.edit_mode = Mode.DICE_3D
self.mode_manager()
# Zoom
if self.edit_mode in {Mode.DICE_3D, Mode.DICE_LINE}:
if self.form.is_dot_open():
if not self.form.active():
if event.type in {"WHEELUPMOUSE", "WHEELDOWNMOUSE"} and event.value == 'PRESS':
return {'PASS_THROUGH'}
# Views
if self.edit_mode in {Mode.DICE_2D, Mode.DICE_LINE}:
if not self.form.active():
if event.type in {'NUMPAD_2', 'NUMPAD_4', 'NUMPAD_6', 'NUMPAD_8', 'NUMPAD_1', 'NUMPAD_3', 'NUMPAD_5', 'NUMPAD_7', 'NUMPAD_9'} and event.value == 'PRESS':
return {'PASS_THROUGH'}
if event.type == 'W' and event.value == 'PRESS':
self.set_wire_frame(context, toggle=True)
# Updates
if self.edit_mode == Mode.DICE_3D:
self.dice_3d.update(self, context, event)
elif self.edit_mode == Mode.DICE_2D:
if event.type != 'TIMER':
self.dice_2d.update(self, context, event)
elif self.edit_mode == Mode.DICE_LINE:
if event.type != 'TIMER':
self.dice_line.update(self, context, event)
draw_FAS(self, context)
context.area.tag_redraw()
return {'RUNNING_MODAL'}
# --- EXITS --- #
def common_exit(self, context):
self.set_wire_frame(context, value=self.show_wire)
SD.dice_modal_running = False
self.remove_shader()
self.form.shut_down(context)
self.master.run_fade()
collapse_3D_view_panels(self.original_tool_shelf, self.original_n_panel)
def confirm_exit(self, context, event):
self.common_exit(context)
if self.edit_mode == Mode.DICE_3D:
if event.shift:
boxelize = get_boxelize_ref()
objects = []
if self.dice_3d.x_dice.active or boxelize.active:
x_obj = self.dice_3d.x_dice.create_mesh(context)
objects.append(x_obj)
if self.dice_3d.y_dice.active or boxelize.active:
y_obj = self.dice_3d.y_dice.create_mesh(context)
objects.append(y_obj)
if self.dice_3d.z_dice.active or boxelize.active:
z_obj = self.dice_3d.z_dice.create_mesh(context)
objects.append(z_obj)
if context.mode == 'OBJECT' and objects:
bpy.ops.object.select_all(action='DESELECT')
for o in objects:
o.select_set(True)
o.display_type = 'WIRE'
context.view_layer.objects.active = o
else:
self.dice_3d.confirm_exit(context, event)
elif self.edit_mode == Mode.DICE_2D:
self.dice_2d.confirm_exit(context, event)
self.dice_line.confirm_exit(context)
self.report({'INFO'}, "Finished")
return {'FINISHED'}
def cancel_exit(self, context):
self.common_exit(context)
if self.edit_mode == Mode.DICE_3D:
self.dice_3d.cancel_exit(context)
elif self.edit_mode == Mode.DICE_2D:
self.dice_2d.cancel_exit(context)
self.dice_line.cancel_exit(context)
self.report({'INFO'}, "Cancelled")
return {'CANCELLED'}
# --- UTILS --- #
def mode_manager(self):
if self.edit_mode == Mode.DICE_2D:
SD.pause_drawing = True
alter_form_layout(self, preset_label='2D_DICE')
elif self.edit_mode == Mode.DICE_3D:
SD.pause_drawing = False
alter_form_layout(self, preset_label='3D_DICE')
elif self.edit_mode == Mode.DICE_LINE:
SD.pause_drawing = True
alter_form_layout(self, preset_label='LINE_DICE')
def set_wire_frame(self, context, value=True, toggle=False):
if context.active_object:
obj = context.active_object
if toggle:
obj.show_wire = not obj.show_wire
else:
obj.show_wire = value
# --- FORM --- #
def exit_button(self, use_twist=False):
self.form_exit = True
self.dice_3d.exit_to_twist = use_twist
def switch_edit_modes(self, opt=''):
if opt == '2D':
self.edit_mode = Mode.DICE_2D
elif opt == '3D':
self.edit_mode = Mode.DICE_3D
elif opt == 'LINE':
self.edit_mode = Mode.DICE_LINE
self.mode_manager()
def edit_modes_hook(self):
if self.edit_mode == Mode.DICE_2D:
return '2D'
elif self.edit_mode == Mode.DICE_3D:
return '3D'
elif self.edit_mode == Mode.DICE_LINE:
return 'LINE'
return '3D'
# --- SHADER --- #
def remove_shader(self):
if self.draw_handle_2d:
self.draw_handle_2d = bpy.types.SpaceView3D.draw_handler_remove(self.draw_handle_2d, "WINDOW")
def safe_draw_2d(self, context):
method_handler(self.draw_shader_2d,
arguments = (context,),
identifier = 'Dice 2D Shader',
exit_method = self.remove_shader)
def draw_shader_2d(self, context):
self.form.draw()
if self.edit_mode == Mode.DICE_3D:
if not self.form.is_dot_open():
draw_modal_frame(context)
elif self.edit_mode == Mode.DICE_2D:
self.dice_2d.draw_2d(context)
elif self.edit_mode == Mode.DICE_LINE:
self.dice_line.draw_2d(context)