From 2f1fe59d88db13d2a51d2d23617f9e19f54cf9f1 Mon Sep 17 00:00:00 2001 From: mano-wii Date: Thu, 1 Nov 2018 00:33:29 -0300 Subject: Snap Utilities Line: Adapt to the use of Gizmos in order to start drawing the Tool without waiting for input --- mesh_snap_utilities_line/__init__.py | 13 +- mesh_snap_utilities_line/common_classes.py | 130 ++++++++++++++-- mesh_snap_utilities_line/ops_line.py | 231 +++++++++++++++++------------ 3 files changed, 260 insertions(+), 114 deletions(-) (limited to 'mesh_snap_utilities_line') diff --git a/mesh_snap_utilities_line/__init__.py b/mesh_snap_utilities_line/__init__.py index 9811bfb9..0aedab1f 100644 --- a/mesh_snap_utilities_line/__init__.py +++ b/mesh_snap_utilities_line/__init__.py @@ -33,6 +33,7 @@ if "bpy" in locals(): import importlib importlib.reload(preferences) importlib.reload(ops_line) + importlib.reload(common_classes) else: from . import preferences from . import ops_line @@ -64,11 +65,11 @@ def tool_make_line(): "Connect them to split faces" ), icon=os.path.join(icons_dir, "ops.mesh.make_line"), - widget=None, + widget="MESH_GGT_mouse_point", operator="mesh.make_line", - keymap=( - ("mesh.make_line", None, dict(type='ACTIONMOUSE', value='PRESS')), - ), +# keymap=( +# ("mesh.make_line", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')), +# ), draw_settings=draw_settings, ) @@ -81,6 +82,8 @@ def register(): bpy.utils.register_class(preferences.SnapUtilitiesLinePreferences) bpy.utils.register_class(ops_line.SnapUtilitiesLine) + bpy.utils.register_class(common_classes.MousePointWidget) + bpy.utils.register_class(common_classes.MousePointWidgetGroup) bpy.utils.register_tool('VIEW_3D', 'EDIT_MESH', tool_make_line) @@ -94,6 +97,8 @@ def register(): def unregister(): bpy.utils.unregister_tool('VIEW_3D', 'EDIT_MESH', tool_make_line) + bpy.utils.unregister_class(common_classes.MousePointWidgetGroup) + bpy.utils.unregister_class(common_classes.MousePointWidget) bpy.utils.unregister_class(ops_line.SnapUtilitiesLine) bpy.utils.unregister_class(preferences.SnapUtilitiesLinePreferences) diff --git a/mesh_snap_utilities_line/common_classes.py b/mesh_snap_utilities_line/common_classes.py index 09fc3a02..9a968862 100644 --- a/mesh_snap_utilities_line/common_classes.py +++ b/mesh_snap_utilities_line/common_classes.py @@ -20,6 +20,8 @@ import bgl import gpu import numpy as np +from .common_utilities import snap_utilities + class SnapDrawn(): def __init__(self, out_color, face_color, @@ -161,19 +163,20 @@ class SnapDrawn(): gpu.matrix.multiply_matrix(snap_obj.mat) if isinstance(elem, BMVert): - color = self.vert_color - edges = np.empty((len(elem.link_edges), 2), [("pos", "f4", 3), ("color", "f4", 4)]) - edges["pos"][:, 0] = elem.co - edges["pos"][:, 1] = [e.other_vert(elem).co for e in elem.link_edges] - edges["color"][:, 0] = color - edges["color"][:, 1] = (color[0], color[1], color[2], 0.0) - edges.shape = -1 - - self._program_smooth_col.bind() - bgl.glLineWidth(3.0) - batch = self.batch_lines_smooth_color_create(edges["pos"], edges["color"]) - batch.draw(self._program_smooth_col) - bgl.glLineWidth(1.0) + if elem.link_edges: + color = self.vert_color + edges = np.empty((len(elem.link_edges), 2), [("pos", "f4", 3), ("color", "f4", 4)]) + edges["pos"][:, 0] = elem.co + edges["pos"][:, 1] = [e.other_vert(elem).co for e in elem.link_edges] + edges["color"][:, 0] = color + edges["color"][:, 1] = (color[0], color[1], color[2], 0.0) + edges.shape = -1 + + self._program_smooth_col.bind() + bgl.glLineWidth(3.0) + batch = self.batch_lines_smooth_color_create(edges["pos"], edges["color"]) + batch.draw(self._program_smooth_col) + bgl.glLineWidth(1.0) else: self._program_unif_col.bind() @@ -332,3 +335,104 @@ class CharMap: elif event.type == 'RIGHT_ARROW': self.line_pos = (self.line_pos + 1) % (len(self.length_entered) + 1) + +class MousePointWidget(bpy.types.Gizmo): + bl_idname = "VIEW3D_GT_mouse_point" + + __slots__ = ( + "sctx", + "bm", + "draw_cache", + "geom", + "incremental", + "preferences", + "loc", + "snap_obj", + "snap_to_grid", + "type", + ) + + def test_select(self, context, mval): + #print('test_select', mval) + self.snap_obj, prev_loc, self.loc, self.type, self.bm, self.geom, len = snap_utilities( + self.sctx, + None, + mval, + increment=self.incremental + ) + context.area.tag_redraw() + return False + + def draw(self, context): + if self.bm: + self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom) + self.draw_cache.draw(self.type, self.loc, None, None, None) + + def setup(self): + if not hasattr(self, "sctx"): + context = bpy.context + + self.preferences = preferences = context.user_preferences.addons[__package__].preferences + + #Configure the unit of measure + self.snap_to_grid = preferences.increments_grid + self.incremental = bpy.utils.units.to_value( + context.scene.unit_settings.system, 'LENGTH', str(preferences.incremental)) + + self.draw_cache = SnapDrawn( + preferences.out_color, + preferences.face_color, + preferences.edge_color, + preferences.vert_color, + preferences.center_color, + preferences.perpendicular_color, + preferences.constrain_shift_color, + (*context.user_preferences.themes[0].user_interface.axis_x, 1.0), + (*context.user_preferences.themes[0].user_interface.axis_y, 1.0), + (*context.user_preferences.themes[0].user_interface.axis_z, 1.0) + ) + + #Init Snap Context + from .snap_context_l import SnapContext + from mathutils import Vector + + self.sctx = SnapContext(context.region, context.space_data) + self.sctx.set_pixel_dist(12) + self.sctx.use_clip_planes(True) + + if preferences.outer_verts: + for base in context.visible_bases: + self.sctx.add_obj(base.object, base.object.matrix_world) + + self.sctx.set_snap_mode(True, True, True) + self.bm = None + self.type = 'OUT' + self.loc = Vector() + + def __del__(self): + self.sctx.free() + del self.draw_cache + + +class MousePointWidgetGroup(bpy.types.GizmoGroup): + bl_idname = "MESH_GGT_mouse_point" + bl_label = "Draw Mouse Point" + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_options = {'3D'} + + __slots__ = ( + "snap_widget", + ) + + def setup(self, context): + if not hasattr(self, "snap_widget"): + self.snap_widget = self.gizmos.new(MousePointWidget.bl_idname) + props = self.snap_widget.target_set_operator("mesh.make_line") + props.wait_for_input = False + + b_sctx_ptr = id(self.snap_widget).to_bytes(8, 'big') + props.snap_widget_ptr[0] = int.from_bytes(b_sctx_ptr[0:2], 'big') + props.snap_widget_ptr[1] = int.from_bytes(b_sctx_ptr[2:4], 'big') + props.snap_widget_ptr[2] = int.from_bytes(b_sctx_ptr[4:6], 'big') + props.snap_widget_ptr[3] = int.from_bytes(b_sctx_ptr[6:8], 'big') diff --git a/mesh_snap_utilities_line/ops_line.py b/mesh_snap_utilities_line/ops_line.py index 2fe01ef9..ecef5a86 100644 --- a/mesh_snap_utilities_line/ops_line.py +++ b/mesh_snap_utilities_line/ops_line.py @@ -83,12 +83,12 @@ def draw_line(self, bm_geom, location): bm = self.main_bm split_faces = set() - drawing_is_dirt = False update_edit_mesh = False if bm_geom is None: vert = bm.verts.new(location) self.list_verts.append(vert) + update_edit_mesh = True elif isinstance(bm_geom, bmesh.types.BMVert): if (bm_geom.co - location).length_squared < .001: @@ -97,7 +97,7 @@ def draw_line(self, bm_geom, location): else: vert = bm.verts.new(location) self.list_verts.append(vert) - drawing_is_dirt = True + update_edit_mesh = True elif isinstance(bm_geom, bmesh.types.BMEdge): self.list_edges.append(bm_geom) @@ -110,7 +110,8 @@ def draw_line(self, bm_geom, location): vert = bm_geom.verts[1] else: edge, vert = bmesh.utils.edge_split(bm_geom, bm_geom.verts[0], ret[1]) - drawing_is_dirt = True + update_edit_mesh = True + self.list_verts.append(vert) self.geom = vert # hack to highlight in the drawing # self.list_edges.append(edge) @@ -118,27 +119,25 @@ def draw_line(self, bm_geom, location): else: # constrain point is near vert = bm.verts.new(location) self.list_verts.append(vert) - drawing_is_dirt = True + update_edit_mesh = True elif isinstance(bm_geom, bmesh.types.BMFace): split_faces.add(bm_geom) vert = bm.verts.new(location) self.list_verts.append(vert) - drawing_is_dirt = True + update_edit_mesh = True # draw, split and create face if len(self.list_verts) >= 2: v1, v2 = self.list_verts[-2:] - # v2_link_verts = [x for y in [a.verts for a in v2.link_edges] for x in y if x != v2] edge = bm.edges.get([v1, v2]) if edge: self.list_edges.append(edge) - else: # if v1 not in v2_link_verts: + else: if not v2.link_edges: edge = bm.edges.new([v1, v2]) self.list_edges.append(edge) - drawing_is_dirt = True else: # split face v1_link_faces = v1.link_faces v2_link_faces = v2.link_faces @@ -166,7 +165,6 @@ def draw_line(self, bm_geom, location): for face in split_faces: facesp = bmesh.utils.face_split_edgenet(face, ed_list) del split_faces - update_edit_mesh = True else: if self.intersect: facesp = bmesh.ops.connect_vert_pair(bm, verts=[v1, v2], verts_exclude=bm.verts) @@ -174,11 +172,10 @@ def draw_line(self, bm_geom, location): if not self.intersect or not facesp['edges']: edge = bm.edges.new([v1, v2]) self.list_edges.append(edge) - drawing_is_dirt = True else: for edge in facesp['edges']: self.list_edges.append(edge) - update_edit_mesh = True + update_edit_mesh = True # create face if self.create_face: @@ -198,7 +195,8 @@ def draw_line(self, bm_geom, location): bmesh.ops.edgenet_fill(bm, edges=list(ed_list)) update_edit_mesh = True # print('face created') - if update_edit_mesh or drawing_is_dirt: + + if update_edit_mesh: obj.data.update_gpu_tag() obj.data.update_tag() obj.update_from_editmode() @@ -207,6 +205,9 @@ def draw_line(self, bm_geom, location): self.sctx.tag_update_drawn_snap_object(self.main_snap_obj) #bm.verts.index_update() + if not self.wait_for_input: + bpy.ops.ed.undo_push(message="Undo draw line*") + return [obj.matrix_world @ v.co for v in self.list_verts] @@ -216,6 +217,13 @@ class SnapUtilitiesLine(bpy.types.Operator): bl_label = "Line Tool" bl_options = {'REGISTER'} + wait_for_input: bpy.props.BoolProperty(name="Wait for Input", default=True) + snap_widget_ptr: bpy.props.IntVectorProperty( + name="Adress of a SnapContext object", + size=4, + default=(0, 0, 0, 0), + ) + constrain_keys = { 'X': Vector((1,0,0)), 'Y': Vector((0,1,0)), @@ -224,6 +232,28 @@ class SnapUtilitiesLine(bpy.types.Operator): 'LEFT_SHIFT': 'shift', } + def _exit(self, context): + del self.main_bm #avoids unpredictable crashs + del self.bm + del self.list_edges + del self.list_verts + del self.list_verts_co + + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + context.area.header_text_set(None) + + if not self.snap_widget: + self.sctx.free() + del self.draw_cache + else: + self.sctx = None + self.draw_cache = None + + #Restore initial state + context.tool_settings.mesh_select_mode = self.select_mode + context.user_preferences.view.use_rotate_around_active = self.use_rotate_around_active + context.space_data.overlay.show_face_center = self.show_face_center + def modal(self, context, event): if self.navigation_ops.run(context, event, self.prevloc if self.vector_constrain else self.location): return {'RUNNING_MODAL'} @@ -336,7 +366,6 @@ class SnapUtilitiesLine(bpy.types.Operator): self.vector_constrain = None self.list_verts_co = draw_line(self, geom2, point) - bpy.ops.ed.undo_push(message="Undo draw line*") elif event.type == 'TAB': self.keytab = self.keytab is False @@ -367,26 +396,13 @@ class SnapUtilitiesLine(bpy.types.Operator): except: # ValueError: self.report({'INFO'}, "Operation not supported yet") - elif event.type in {'RIGHTMOUSE', 'ESC'}: - if not is_making_lines or event.type == 'ESC': - del self.main_bm #avoids unpredictable crashs - del self.bm - del self.list_edges - del self.list_verts - del self.list_verts_co - - bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') - context.area.header_text_set(None) - self.sctx.free() - del self.draw_cache - - #Restore initial state - context.tool_settings.mesh_select_mode = self.select_mode - context.user_preferences.view.use_rotate_around_active = self.use_rotate_around_active - context.space_data.overlay.show_face_center = self.show_face_center - if not self.is_editmode: - bpy.ops.object.editmode_toggle() + if not self.wait_for_input: + self._exit(context) + return {'FINISHED'} + elif event.type in {'RIGHTMOUSE', 'ESC'}: + if not self.wait_for_input or not is_making_lines or event.type == 'ESC': + self._exit(context) return {'FINISHED'} else: snap_utilities.cache.snp_obj = None # hack for snap edge elemens update @@ -420,108 +436,128 @@ class SnapUtilitiesLine(bpy.types.Operator): def invoke(self, context, event): if context.space_data.type == 'VIEW_3D': # print('name', __name__, __package__) - preferences = context.user_preferences.addons[__package__].preferences - - #Store the preferences that will be used in modal - self.intersect = preferences.intersect - self.create_face = preferences.create_face - self.outer_verts = preferences.outer_verts - self.snap_to_grid = preferences.increments_grid - - self.draw_cache = SnapDrawn( - preferences.out_color, - preferences.face_color, - preferences.edge_color, - preferences.vert_color, - preferences.center_color, - preferences.perpendicular_color, - preferences.constrain_shift_color, - tuple(context.user_preferences.themes[0].user_interface.axis_x) + (1.0,), - tuple(context.user_preferences.themes[0].user_interface.axis_y) + (1.0,), - tuple(context.user_preferences.themes[0].user_interface.axis_z) + (1.0,) - ) - - obj = context.active_object - - #Create a new object - if obj is None or obj.type != 'MESH': - mesh = bpy.data.meshes.new("") - obj = bpy.data.objects.new("", mesh) - context.scene.objects.link(obj) - context.scene.objects.active = obj - else: - mesh = obj.data #Store current state - self.is_editmode = mesh.is_editmode self.use_rotate_around_active = context.user_preferences.view.use_rotate_around_active self.select_mode = context.tool_settings.mesh_select_mode[:] self.show_face_center = context.space_data.overlay.show_face_center #Modify the current state - bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='DESELECT') context.user_preferences.view.use_rotate_around_active = True context.tool_settings.mesh_select_mode = (True, True, True) context.space_data.overlay.show_face_center = True - context.scene.update_tag() + + #Store values from 3d view context + self.rv3d = context.region_data + self.rotMat = self.rv3d.view_matrix.copy() + # self.obj_glmatrix = bgl.Buffer(bgl.GL_FLOAT, [4, 4], self.obj_matrix.transposed()) + + #Init event variables + self.keytab = False + self.keyf8 = False + self.snap_face = True + + snap_widget_ptr = (self.snap_widget_ptr[0] << 48) |\ + (self.snap_widget_ptr[1] << 32) |\ + (self.snap_widget_ptr[2] << 16) |\ + (self.snap_widget_ptr[3] << 00) + + if snap_widget_ptr is not 0: + import ctypes + self.snap_widget = ctypes.cast(snap_widget_ptr, ctypes.py_object).value + self.draw_cache = self.snap_widget.draw_cache + self.sctx = self.snap_widget.sctx + + preferences = self.snap_widget.preferences + else: + self.snap_widget = None + + preferences = context.user_preferences.addons[__package__].preferences + + #Init DrawCache + self.draw_cache = SnapDrawn( + preferences.out_color, + preferences.face_color, + preferences.edge_color, + preferences.vert_color, + preferences.center_color, + preferences.perpendicular_color, + preferences.constrain_shift_color, + tuple(context.user_preferences.themes[0].user_interface.axis_x) + (1.0,), + tuple(context.user_preferences.themes[0].user_interface.axis_y) + (1.0,), + tuple(context.user_preferences.themes[0].user_interface.axis_z) + (1.0,) + ) + + #Init Snap Context + from .snap_context_l import SnapContext + + self.sctx = SnapContext(context.region, context.space_data) + self.sctx.set_pixel_dist(12) + self.sctx.use_clip_planes(True) + + if self.outer_verts: + for base in context.visible_bases: + self.sctx.add_obj(base.object, base.object.matrix_world) + + self.sctx.set_snap_mode(True, True, self.snap_face) #Configure the unit of measure - scale = context.scene.unit_settings.scale_length self.unit_system = context.scene.unit_settings.system + scale = context.scene.unit_settings.scale_length separate_units = context.scene.unit_settings.use_separate self.uinfo = get_units_info(scale, self.unit_system, separate_units) - scale /= context.space_data.overlay.grid_scale * preferences.relative_scale self.rd = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(1 / scale)) + self.intersect = preferences.intersect + self.create_face = preferences.create_face + self.outer_verts = preferences.outer_verts + self.snap_to_grid = preferences.increments_grid self.incremental = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(preferences.incremental)) - #Store values from 3d view context - self.rv3d = context.region_data - self.rotMat = self.rv3d.view_matrix.copy() - # self.obj_glmatrix = bgl.Buffer(bgl.GL_FLOAT, [4, 4], self.obj_matrix.transposed()) - self.main_bm = self.bm = bmesh.from_edit_mesh(mesh) #remove at end + if self.snap_widget: + self.geom = self.snap_widget.geom + self.type = self.snap_widget.type + self.location = self.snap_widget.loc + if self.snap_widget.snap_obj: + context.view_layer.objects.active = self.snap_widget.snap_obj.data[0] + else: + #init these variables to avoid errors + self.geom = None + self.type = 'OUT' + self.location = Vector() - #init these variables to avoid errors self.prevloc = Vector() - self.location = Vector() self.list_verts = [] self.list_edges = [] self.list_verts_co = [] self.bool_update = False self.vector_constrain = () - self.navigation_ops = SnapNavigation(context, True) - self.type = 'OUT' self.len = 0 self.length_entered = "" self.line_pos = 0 - self.geom = None - #Init event variables - self.keytab = False - self.keyf8 = False - - #Init Snap Context - from .snap_context_l import SnapContext - - self.sctx = SnapContext(context.region, context.space_data) - self.sctx.set_pixel_dist(12) - self.sctx.use_clip_planes(True) - - act_base = context.active_base + self.navigation_ops = SnapNavigation(context, True) - if self.outer_verts: - for base in context.visible_bases: - if base != act_base: - self.sctx.add_obj(base.object, base.object.matrix_world) + active_object = context.active_object - self.main_snap_obj = self.snap_obj = self.sctx.add_obj(act_base.object, act_base.object.matrix_world) + #Create a new object + if active_object is None or active_object.type != 'MESH': + mesh = bpy.data.meshes.new("") + active_object = bpy.data.objects.new("", mesh) + context.scene.objects.link(obj) + context.scene.objects.active = active_object + else: + mesh = active_object.data - self.snap_face = True - self.sctx.set_snap_mode(True, True, self.snap_face) + self.main_snap_obj = self.snap_obj = self.sctx._get_snap_obj_by_obj(active_object) + self.main_bm = self.bm = bmesh.from_edit_mesh(mesh) #remove at end #modals + if not self.wait_for_input: + self.modal(context, event) + self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, (), 'WINDOW', 'POST_VIEW') context.window_manager.modal_handler_add(self) @@ -530,6 +566,7 @@ class SnapUtilitiesLine(bpy.types.Operator): self.report({'WARNING'}, "Active space must be a View3d") return {'CANCELLED'} + def register(): bpy.utils.register_class(SnapUtilitiesLine) -- cgit v1.2.3