Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormano-wii <germano.costa@ig.com.br>2019-02-10 16:19:32 +0300
committermano-wii <germano.costa@ig.com.br>2019-02-10 16:19:32 +0300
commit93cd3f9a9be7f97b150d7d9b58a9506bd9a0fbe3 (patch)
tree2022fe9e075e702aced33c1fbdd2dd073c8575ad
parentf999cc0908333ac0d4b2b203706f3eb640ba54c9 (diff)
mesh_snap_utilities_line: Cleanup
Rename files, and split the `common_classes.py` file into `drawing_utilities.py`, `navigation_ops.py` and `widgets.py`
-rw-r--r--mesh_snap_utilities_line/__init__.py26
-rw-r--r--mesh_snap_utilities_line/common_classes.py498
-rw-r--r--mesh_snap_utilities_line/common_utilities.py30
-rw-r--r--mesh_snap_utilities_line/drawing_utilities.py224
-rw-r--r--mesh_snap_utilities_line/navigation_ops.py102
-rw-r--r--mesh_snap_utilities_line/op_line.py (renamed from mesh_snap_utilities_line/ops_line.py)16
-rw-r--r--mesh_snap_utilities_line/preferences.py6
-rw-r--r--mesh_snap_utilities_line/snap_context_l/__init__.py8
-rw-r--r--mesh_snap_utilities_line/widgets.py244
9 files changed, 670 insertions, 484 deletions
diff --git a/mesh_snap_utilities_line/__init__.py b/mesh_snap_utilities_line/__init__.py
index dc086540..be37d8a0 100644
--- a/mesh_snap_utilities_line/__init__.py
+++ b/mesh_snap_utilities_line/__init__.py
@@ -22,22 +22,24 @@
bl_info = {
"name": "Snap_Utilities_Line",
"author": "Germano Cavalcante",
- "version": (5, 9, 00),
+ "version": (5, 9, 1),
"blender": (2, 80, 0),
"location": "View3D > TOOLS > Line Tool",
"description": "Extends Blender Snap controls",
- #"wiki_url" : "http://blenderartists.org/forum/showthread.php?363859-Addon-CAD-Snap-Utilities",
+ "wiki_url" : "http://blenderartists.org/forum/showthread.php?363859-Addon-CAD-Snap-Utilities",
"category": "Mesh"}
if "bpy" in locals():
import importlib
- importlib.reload(common_classes)
+ importlib.reload(navigation_ops)
+ importlib.reload(widgets)
importlib.reload(preferences)
- importlib.reload(ops_line)
+ importlib.reload(op_line)
else:
- from . import common_classes
+ from . import navigation_ops
+ from . import widgets
from . import preferences
- from . import ops_line
+ from . import op_line
import bpy
from bpy.utils.toolsystem import ToolDef
@@ -105,7 +107,7 @@ def register_snap_tools():
tools[:index] += None, tool_line
- del tool, tools, index
+ del tools, index
keyconfigs = bpy.context.window_manager.keyconfigs
kc_defaultconf = keyconfigs.get("blender")
@@ -144,11 +146,11 @@ def unregister_snap_tools():
classes = (
preferences.SnapUtilitiesPreferences,
- ops_line.SnapUtilitiesLine,
- common_classes.VIEW3D_OT_rotate_custom_pivot,
- common_classes.VIEW3D_OT_zoom_custom_target,
- common_classes.SnapPointWidget,
- common_classes.SnapPointWidgetGroup,
+ op_line.SnapUtilitiesLine,
+ navigation_ops.VIEW3D_OT_rotate_custom_pivot,
+ navigation_ops.VIEW3D_OT_zoom_custom_target,
+ widgets.SnapPointWidget,
+ widgets.SnapPointWidgetGroup,
)
def register():
diff --git a/mesh_snap_utilities_line/common_classes.py b/mesh_snap_utilities_line/common_classes.py
index a096415d..6ad150c4 100644
--- a/mesh_snap_utilities_line/common_classes.py
+++ b/mesh_snap_utilities_line/common_classes.py
@@ -16,222 +16,15 @@
# ##### END GPL LICENSE BLOCK #####
import bpy
-import bgl
from mathutils import Vector
-
+from .drawing_utilities import SnapDrawn
from .common_utilities import (
convert_distance,
get_units_info,
- snap_utilities,
)
-class SnapDrawn():
- def __init__(self, out_color, face_color,
- edge_color, vert_color, center_color,
- perpendicular_color, constrain_shift_color,
- axis_x_color, axis_y_color, axis_z_color):
-
- import gpu
-
- self.out_color = out_color
- self.face_color = face_color
- self.edge_color = edge_color
- self.vert_color = vert_color
- self.center_color = center_color
- self.perpendicular_color = perpendicular_color
- self.constrain_shift_color = constrain_shift_color
-
- self.axis_x_color = axis_x_color
- self.axis_y_color = axis_y_color
- self.axis_z_color = axis_z_color
-
- self._format_pos = gpu.types.GPUVertFormat()
- self._format_pos.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
-
- self._format_pos_and_color = gpu.types.GPUVertFormat()
- self._format_pos_and_color.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
- self._format_pos_and_color.attr_add(id="color", comp_type='F32', len=4, fetch_mode='FLOAT')
-
- self._program_unif_col = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
- self._program_smooth_col = gpu.shader.from_builtin("3D_SMOOTH_COLOR")
-
- self._batch_point = None
-
-
- def batch_line_strip_create(self, coords):
- from gpu.types import (
- GPUVertBuf,
- GPUBatch,
- )
-
- vbo = GPUVertBuf(self._format_pos, len = len(coords))
- vbo.attr_fill(0, data = coords)
- batch_lines = GPUBatch(type = "LINE_STRIP", buf = vbo)
- return batch_lines
-
- def batch_lines_smooth_color_create(self, coords, colors):
- from gpu.types import (
- GPUVertBuf,
- GPUBatch,
- )
-
- vbo = GPUVertBuf(self._format_pos_and_color, len = len(coords))
- vbo.attr_fill(0, data = coords)
- vbo.attr_fill(1, data = colors)
- batch_lines = GPUBatch(type = "LINES", buf = vbo)
- return batch_lines
-
- def batch_triangles_create(self, coords):
- from gpu.types import (
- GPUVertBuf,
- GPUBatch,
- )
-
- vbo = GPUVertBuf(self._format_pos, len = len(coords))
- vbo.attr_fill(0, data = coords)
- batch_tris = GPUBatch(type = "TRIS", buf = vbo)
- return batch_tris
-
- def batch_point_get(self):
- if self._batch_point is None:
- from gpu.types import (
- GPUVertBuf,
- GPUBatch,
- )
- vbo = GPUVertBuf(self._format_pos, len = 1)
- vbo.attr_fill(0, ((0.0, 0.0, 0.0),))
- self._batch_point = GPUBatch(type = "POINTS", buf = vbo)
- return self._batch_point
-
- def draw(self, type, location, list_verts_co, vector_constrain, prevloc):
- import gpu
-
- # draw 3d point OpenGL in the 3D View
- bgl.glEnable(bgl.GL_BLEND)
- gpu.matrix.push()
- self._program_unif_col.bind()
-
- if list_verts_co:
- # draw 3d line OpenGL in the 3D View
- bgl.glDepthRange(0, 0.9999)
- bgl.glLineWidth(3.0)
-
- batch = self.batch_line_strip_create([v.to_tuple() for v in list_verts_co] + [location.to_tuple()])
-
- self._program_unif_col.uniform_float("color", (1.0, 0.8, 0.0, 0.5))
- batch.draw(self._program_unif_col)
- del batch
-
- bgl.glDisable(bgl.GL_DEPTH_TEST)
-
- point_batch = self.batch_point_get()
- if vector_constrain:
- if prevloc:
- bgl.glPointSize(5)
- gpu.matrix.translate(prevloc)
- self._program_unif_col.uniform_float("color", (1.0, 1.0, 1.0, 0.5))
- point_batch.draw(self._program_unif_col)
- gpu.matrix.translate(-prevloc)
-
- if vector_constrain[2] == 'X':
- Color4f = self.axis_x_color
- elif vector_constrain[2] == 'Y':
- Color4f = self.axis_y_color
- elif vector_constrain[2] == 'Z':
- Color4f = self.axis_z_color
- else:
- Color4f = self.constrain_shift_color
- else:
- if type == 'OUT':
- Color4f = self.out_color
- elif type == 'FACE':
- Color4f = self.face_color
- elif type == 'EDGE':
- Color4f = self.edge_color
- elif type == 'VERT':
- Color4f = self.vert_color
- elif type == 'CENTER':
- Color4f = self.center_color
- elif type == 'PERPENDICULAR':
- Color4f = self.perpendicular_color
- else: # type == None
- Color4f = self.out_color
-
- bgl.glPointSize(10)
-
- gpu.matrix.translate(location)
- self._program_unif_col.uniform_float("color", Color4f)
- point_batch.draw(self._program_unif_col)
-
- # restore opengl defaults
- bgl.glDepthRange(0.0, 1.0)
- bgl.glPointSize(1.0)
- bgl.glLineWidth(1.0)
- bgl.glEnable(bgl.GL_DEPTH_TEST)
- bgl.glDisable(bgl.GL_BLEND)
-
- gpu.matrix.pop()
-
- def draw_elem(self, snap_obj, bm, elem):
- #TODO: Cache coords (because antialiasing)
- import gpu
- from bmesh.types import(
- BMVert,
- BMEdge,
- BMFace,
- )
- # draw 3d point OpenGL in the 3D View
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glDisable(bgl.GL_DEPTH_TEST)
-
- with gpu.matrix.push_pop():
- gpu.matrix.multiply_matrix(snap_obj.mat)
-
- if isinstance(elem, BMVert):
- if elem.link_edges:
- import numpy as np
-
- 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()
-
- if isinstance(elem, BMEdge):
- self._program_unif_col.uniform_float("color", self.edge_color)
-
- bgl.glLineWidth(3.0)
- batch = self.batch_line_strip_create([v.co for v in elem.verts])
- batch.draw(self._program_unif_col)
- bgl.glLineWidth(1.0)
-
- elif isinstance(elem, BMFace):
- if len(snap_obj.data) == 2:
- face_color = self.face_color[0], self.face_color[1], self.face_color[2], self.face_color[3] * 0.2
- self._program_unif_col.uniform_float("color", face_color)
-
- tris = snap_obj.data[1].get_loop_tri_co_by_bmface(bm, elem)
- tris.shape = (-1, 3)
- batch = self.batch_triangles_create(tris)
- batch.draw(self._program_unif_col)
-
- # restore opengl defaults
- bgl.glEnable(bgl.GL_DEPTH_TEST)
- bgl.glDisable(bgl.GL_BLEND)
-
-
class SnapNavigation():
@staticmethod
def debug_key(key):
@@ -400,90 +193,6 @@ class CharMap:
self.line_pos = 0
-class VIEW3D_OT_rotate_custom_pivot(bpy.types.Operator):
- bl_idname = "view3d.rotate_custom_pivot"
- bl_label = "Rotate the view"
- bl_options = {'BLOCKING', 'GRAB_CURSOR'}
-
- pivot: bpy.props.FloatVectorProperty("Pivot", subtype='XYZ')
- g_up_axis: bpy.props.FloatVectorProperty("up_axis", default=(0.0, 0.0, 1.0), subtype='XYZ')
- sensitivity: bpy.props.FloatProperty("sensitivity", default=0.007)
-
- def modal(self, context, event):
- from mathutils import Matrix
- if event.value == 'PRESS' and event.type in {'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE'}:
- dx = self.init_coord[0] - event.mouse_region_x
- dy = self.init_coord[1] - event.mouse_region_y
- rot_ver = Matrix.Rotation(-dx * self.sensitivity, 3, self.g_up_axis)
- rot_hor = Matrix.Rotation(dy * self.sensitivity, 3, self.view_rot[0])
- rot_mat = rot_hor @ rot_ver
- view_matrix = self.view_rot @ rot_mat
-
- pos = self.pos1 @ rot_mat + self.pivot
- qua = view_matrix.to_quaternion()
- qua.invert()
-
- self.rv3d.view_location = pos
- self.rv3d.view_rotation = qua
-
- context.area.tag_redraw()
- return {'RUNNING_MODAL'}
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- self.rv3d = context.region_data
- self.init_coord = event.mouse_region_x, event.mouse_region_y
- self.pos1 = self.rv3d.view_location - self.pivot
- self.view_rot = self.rv3d.view_matrix.to_3x3()
-
- context.window_manager.modal_handler_add(self)
- return {'RUNNING_MODAL'}
-
-
-class VIEW3D_OT_zoom_custom_target(bpy.types.Operator):
- bl_idname = "view3d.zoom_custom_target"
- bl_label = "Zoom the view"
- bl_options = {'BLOCKING', 'GRAB_CURSOR'}
-
- target: bpy.props.FloatVectorProperty("target", subtype='XYZ')
- delta: bpy.props.IntProperty("delta", default=0)
- step_factor = 0.333
-
- def modal(self, context, event):
- if event.value == 'PRESS' and event.type in {'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE'}:
- if not hasattr(self, "init_mouse_region_y"):
- self.init_mouse_region_y = event.mouse_region_y
- self.heigt_up = context.area.height - self.init_mouse_region_y
- self.rv3d.view_location = self.target
-
- fac = (event.mouse_region_y - self.init_mouse_region_y) / self.heigt_up
- ret = 'RUNNING_MODAL'
- else:
- fac = self.step_factor * self.delta
- ret = 'FINISHED'
-
- self.rv3d.view_location = self.init_loc + (self.target - self.init_loc) * fac
- self.rv3d.view_distance = self.init_dist - self.init_dist * fac
-
- context.area.tag_redraw()
- return {ret}
-
- def invoke(self, context, event):
- v3d = context.space_data
- dist_range = (v3d.clip_start, v3d.clip_end)
- self.rv3d = context.region_data
- self.init_dist = self.rv3d.view_distance
- if ((self.delta <= 0 and self.init_dist < dist_range[1]) or
- (self.delta > 0 and self.init_dist > dist_range[0])):
- self.init_loc = self.rv3d.view_location.copy()
-
- context.window_manager.modal_handler_add(self)
- return {'RUNNING_MODAL'}
-
- return {'FINISHED'}
-
-
class SnapUtilities:
# __slots__ = (
# "sctx",
@@ -512,11 +221,66 @@ class SnapUtilities:
widget = SnapUtilities.snapwidgets[-1] if SnapUtilities.snapwidgets else None
if SnapUtilities.constrain == key:
SnapUtilities.constrain = None
+ if hasattr(widget, "get_normal"):
+ widget.get_normal(context)
return
+ if hasattr(widget, "normal"):
+ if key == 'shift':
+ import bmesh
+ if isinstance(widget.geom, bmesh.types.BMEdge):
+ verts = widget.geom.verts
+ widget.normal = verts[1].co - verts[0].co
+ widget.normal.normalise()
+ else:
+ return
+ else:
+ widget.normal = SnapUtilities.constrain_keys[key]
+
SnapUtilities.constrain = key
+ def snap_context_update_and_return_moving_objects(self, context):
+ moving_objects = set()
+ moving_snp_objects = set()
+ children = set()
+ for obj in context.view_layer.objects.selected:
+ moving_objects.add(obj)
+
+ temp_children = set()
+ for obj in context.visible_objects:
+ temp_children.clear()
+ while obj.parent is not None:
+ temp_children.add(obj)
+ parent = obj.parent
+ if parent in moving_objects:
+ children.update(temp_children)
+ temp_children.clear()
+ obj = parent
+
+ del temp_children
+
+ moving_objects.difference_update(children)
+
+ self.sctx.clear_snap_objects()
+
+ for obj in context.visible_objects:
+ is_moving = obj in moving_objects or obj in children
+ snap_obj = self.sctx.add_obj(obj, obj.matrix_world)
+ if is_moving:
+ moving_snp_objects.add(snap_obj)
+
+ if obj.instance_type == 'COLLECTION':
+ mat = obj.matrix_world.copy()
+ for ob in obj.instance_collection.objects:
+ snap_obj = self.sctx.add_obj(obj, mat @ ob.matrix_world)
+ if is_moving:
+ moving_snp_objects.add(snap_obj)
+
+ del children
+ return moving_objects, moving_snp_objects
+
+
def snap_context_update(self, context):
def visible_objects_and_duplis():
if self.preferences.outer_verts:
@@ -554,6 +318,11 @@ class SnapUtilities:
self.location = widget.location
self.preferences = widget.preferences
self.draw_cache = widget.draw_cache
+ if hasattr(widget, "normal"):
+ self.normal = widget.normal
+ #loc = widget.location
+ #self.vector_constrain = [loc, loc + widget.normal, self.constrain]
+
else:
#init these variables to avoid errors
self.snap_obj = None
@@ -619,148 +388,3 @@ class SnapUtilities:
del self.preferences
SnapUtilities.constrain = None
-
-
-#def mesh_runtime_batchcache_isdirty(me):
-# import ctypes
-# batch_cache = ctypes.c_void_p.from_address(me.as_pointer() + 1440)
-# if batch_cache:
-# return ctypes.c_bool.from_address(batch_cache.value + 549).value
-# return False
-
-
-class SnapWidgetCommon(SnapUtilities):
- snap_to_update = False
-
- def handler(self, scene):
- cls = SnapWidgetCommon
- if cls.snap_to_update is False:
- last_operator = self.wm_operators[-1] if self.wm_operators else None
- if (not last_operator or
- last_operator.name not in {'Select', 'Loop Select', '(De)select All'}):
- cls.snap_to_update = self.depsgraph.id_type_updated('MESH') or\
- self.depsgraph.id_type_updated('OBJECT')
-
- def draw_point_and_elem(self):
- if self.bm:
- if self.bm.is_valid and self.geom.is_valid:
- self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom)
- else:
- self.bm = None
- self.geom = None
- self.sctx.update_all()
-
- self.draw_cache.draw(self.type, self.location, None, None, None)
-
- def init_snapwidget(self, context, snap_edge_and_vert = True):
- self.snap_context_init(context, snap_edge_and_vert)
- self.snap_context_update(context)
- self.mode = context.mode
- self.last_mval = None
-
- self.wm_operators = context.window_manager.operators
- self.depsgraph = context.depsgraph
- bpy.app.handlers.depsgraph_update_post.append(self.handler)
- SnapWidgetCommon.snap_to_update = False
-
- SnapUtilities.snapwidgets.append(self)
-
- def end_snapwidget(self):
- SnapUtilities.snapwidgets.remove(self)
-
- #from .snap_context_l import global_snap_context_get
- #sctx = global_snap_context_get(None, None, None)
-
- StructRNA = bpy.types.bpy_struct
- sctx = super(StructRNA, self).__getattribute__("sctx")
- if sctx and not SnapUtilities.snapwidgets:
- sctx.clear_snap_objects()
-
- handler = super(StructRNA, self).__getattribute__("handler")
- bpy.app.handlers.depsgraph_update_post.remove(handler)
-
- def update_snap(self, context, mval):
- if self.last_mval == mval:
- return -1
- else:
- self.last_mval = mval
-
- if (SnapWidgetCommon.snap_to_update):
- ## Something has changed since the last time.
- # Has the mesh been changed?
- # In the doubt lets clear the snap context.
- self.snap_context_update(context)
- SnapWidgetCommon.snap_to_update = False
-
- #print('test_select', mval)
- space = context.space_data
- self.sctx.update_viewport_context(context.depsgraph, context.region, space, True)
-
- shading = space.shading
- snap_face = not ((self.snap_vert or self.snap_edge) and
- (shading.show_xray or shading.type == 'WIREFRAME'))
-
- if snap_face != self.snap_face:
- self.snap_face = snap_face
- self.sctx.set_snap_mode(
- self.snap_vert, self.snap_edge, self.snap_face)
-
- self.snap_obj, prev_loc, self.location, self.type, self.bm, self.geom, len = snap_utilities(
- self.sctx,
- None,
- mval,
- increment=self.incremental
- )
-
-
-class SnapPointWidget(SnapWidgetCommon, bpy.types.Gizmo):
- bl_idname = "VIEW3D_GT_snap_point"
-
- def test_select(self, context, mval):
- self.update_snap(context, mval)
- self.snap_to_grid()
-
- context.area.tag_redraw()
- return -1
-
- def draw(self, context):
- self.draw_point_and_elem()
-
- def setup(self):
- self.init_snapwidget(bpy.context)
-
-
-def context_mode_check(context, widget_group):
- workspace = context.workspace
- mode = workspace.tools_mode
- for tool in workspace.tools:
- if (tool.widget == widget_group) and (tool.mode == mode):
- break
- else:
- return False
- return True
-
-
-class SnapWidgetGroupCommon:
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'WINDOW'
- bl_options = {'3D'}
-
- @classmethod
- def poll(cls, context):
- return context_mode_check(context, cls.bl_idname)
-
- def init_tool(self, context, gizmo_name):
- self.widget = self.gizmos.new(gizmo_name)
-
- def __del__(self):
- if hasattr(self, "widget"):
- super(bpy.types.bpy_struct, self.widget).__getattribute__("end_snapwidget")()
-
-
-class SnapPointWidgetGroup(SnapWidgetGroupCommon, bpy.types.GizmoGroup):
- bl_idname = "MESH_GGT_snap_point"
- bl_label = "Draw Snap Point"
-
- def setup(self, context):
- self.init_tool(context, SnapPointWidget.bl_idname)
diff --git a/mesh_snap_utilities_line/common_utilities.py b/mesh_snap_utilities_line/common_utilities.py
index 2fc0ce6d..a6ecbdc9 100644
--- a/mesh_snap_utilities_line/common_utilities.py
+++ b/mesh_snap_utilities_line/common_utilities.py
@@ -20,15 +20,16 @@
import bpy
import bmesh
-from .snap_context_l import SnapContext
+
from mathutils import Vector
from mathutils.geometry import (
intersect_point_line,
intersect_line_line,
- intersect_line_plane,
intersect_ray_tri,
)
+from .snap_context_l import SnapContext
+
def get_units_info(scale, unit_system, separate_units):
if unit_system == 'METRIC':
@@ -149,15 +150,10 @@ def get_snap_bm_geom(sctx, main_snap_obj, mcursor):
class SnapCache:
snp_obj = None
- elem = None
+ edge = None
- v0 = None
- v1 = None
vmid = None
vperp = None
-
- v2d0 = None
- v2d1 = None
v2dmid = None
v2dperp = None
@@ -198,20 +194,18 @@ def snap_utilities(
r_loc = loc
elif len(elem) == 2:
- if SnapCache.snp_obj is not snp_obj or not (elem == SnapCache.elem).all():
+ if SnapCache.snp_obj is not snp_obj or not (elem == SnapCache.edge).all():
SnapCache.snp_obj = snp_obj
- SnapCache.elem = elem
+ SnapCache.edge = elem
- SnapCache.v0 = elem_co[0]
- SnapCache.v1 = elem_co[1]
- SnapCache.vmid = 0.5 * (SnapCache.v0 + SnapCache.v1)
- SnapCache.v2d0 = location_3d_to_region_2d(sctx.region, sctx.rv3d, SnapCache.v0)
- SnapCache.v2d1 = location_3d_to_region_2d(sctx.region, sctx.rv3d, SnapCache.v1)
+ v0 = elem_co[0]
+ v1 = elem_co[1]
+ SnapCache.vmid = 0.5 * (v0 + v1)
SnapCache.v2dmid = location_3d_to_region_2d(sctx.region, sctx.rv3d, SnapCache.vmid)
if previous_vert and (not bm_geom or previous_vert not in bm_geom.verts):
pvert_co = main_snap_obj.mat @ previous_vert.co
- perp_point = intersect_point_line(pvert_co, SnapCache.v0, SnapCache.v1)
+ perp_point = intersect_point_line(pvert_co, v0, v1)
SnapCache.vperp = perp_point[0]
#factor = point_perpendicular[1]
SnapCache.v2dperp = location_3d_to_region_2d(sctx.region, sctx.rv3d, perp_point[0])
@@ -222,7 +216,7 @@ def snap_utilities(
#else: SnapCache.v2dperp = None
if constrain:
- t_loc = intersect_line_line(constrain[0], constrain[1], SnapCache.v0, SnapCache.v1)
+ t_loc = intersect_line_line(constrain[0], constrain[1], elem_co[0], elem_co[1])
if t_loc is None:
is_increment = True
@@ -266,4 +260,4 @@ def snap_utilities(
return snp_obj, loc, r_loc, r_type, bm, bm_geom, r_len
-snap_utilities.cache = SnapCache
+snap_utilities.edge_cache = SnapCache
diff --git a/mesh_snap_utilities_line/drawing_utilities.py b/mesh_snap_utilities_line/drawing_utilities.py
new file mode 100644
index 00000000..92facecc
--- /dev/null
+++ b/mesh_snap_utilities_line/drawing_utilities.py
@@ -0,0 +1,224 @@
+### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bgl
+from mathutils import Vector
+
+
+class SnapDrawn():
+ def __init__(self, out_color, face_color,
+ edge_color, vert_color, center_color,
+ perpendicular_color, constrain_shift_color,
+ axis_x_color, axis_y_color, axis_z_color):
+
+ import gpu
+
+ self.out_color = out_color
+ self.face_color = face_color
+ self.edge_color = edge_color
+ self.vert_color = vert_color
+ self.center_color = center_color
+ self.perpendicular_color = perpendicular_color
+ self.constrain_shift_color = constrain_shift_color
+
+ self.axis_x_color = axis_x_color
+ self.axis_y_color = axis_y_color
+ self.axis_z_color = axis_z_color
+
+ self._format_pos = gpu.types.GPUVertFormat()
+ self._format_pos.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
+
+ self._format_pos_and_color = gpu.types.GPUVertFormat()
+ self._format_pos_and_color.attr_add(id="pos", comp_type='F32', len=3, fetch_mode='FLOAT')
+ self._format_pos_and_color.attr_add(id="color", comp_type='F32', len=4, fetch_mode='FLOAT')
+
+ self._program_unif_col = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
+ self._program_smooth_col = gpu.shader.from_builtin("3D_SMOOTH_COLOR")
+
+ self._batch_point = None
+
+
+ def batch_line_strip_create(self, coords):
+ from gpu.types import (
+ GPUVertBuf,
+ GPUBatch,
+ )
+
+ vbo = GPUVertBuf(self._format_pos, len = len(coords))
+ vbo.attr_fill(0, data = coords)
+ batch_lines = GPUBatch(type = "LINE_STRIP", buf = vbo)
+ return batch_lines
+
+ def batch_lines_smooth_color_create(self, coords, colors):
+ from gpu.types import (
+ GPUVertBuf,
+ GPUBatch,
+ )
+
+ vbo = GPUVertBuf(self._format_pos_and_color, len = len(coords))
+ vbo.attr_fill(0, data = coords)
+ vbo.attr_fill(1, data = colors)
+ batch_lines = GPUBatch(type = "LINES", buf = vbo)
+ return batch_lines
+
+ def batch_triangles_create(self, coords):
+ from gpu.types import (
+ GPUVertBuf,
+ GPUBatch,
+ )
+
+ vbo = GPUVertBuf(self._format_pos, len = len(coords))
+ vbo.attr_fill(0, data = coords)
+ batch_tris = GPUBatch(type = "TRIS", buf = vbo)
+ return batch_tris
+
+ def batch_point_get(self):
+ if self._batch_point is None:
+ from gpu.types import (
+ GPUVertBuf,
+ GPUBatch,
+ )
+ vbo = GPUVertBuf(self._format_pos, len = 1)
+ vbo.attr_fill(0, ((0.0, 0.0, 0.0),))
+ self._batch_point = GPUBatch(type = "POINTS", buf = vbo)
+ return self._batch_point
+
+ def draw(self, type, location, list_verts_co, vector_constrain, prevloc):
+ import gpu
+
+ # draw 3d point OpenGL in the 3D View
+ bgl.glEnable(bgl.GL_BLEND)
+ gpu.matrix.push()
+ self._program_unif_col.bind()
+
+ if list_verts_co:
+ # draw 3d line OpenGL in the 3D View
+ bgl.glDepthRange(0, 0.9999)
+ bgl.glLineWidth(3.0)
+
+ batch = self.batch_line_strip_create([v.to_tuple() for v in list_verts_co] + [location.to_tuple()])
+
+ self._program_unif_col.uniform_float("color", (1.0, 0.8, 0.0, 0.5))
+ batch.draw(self._program_unif_col)
+ del batch
+
+ bgl.glDisable(bgl.GL_DEPTH_TEST)
+
+ point_batch = self.batch_point_get()
+ if vector_constrain:
+ if prevloc:
+ bgl.glPointSize(5)
+ gpu.matrix.translate(prevloc)
+ self._program_unif_col.uniform_float("color", (1.0, 1.0, 1.0, 0.5))
+ point_batch.draw(self._program_unif_col)
+ gpu.matrix.translate(-prevloc)
+
+ if vector_constrain[2] == 'X':
+ Color4f = self.axis_x_color
+ elif vector_constrain[2] == 'Y':
+ Color4f = self.axis_y_color
+ elif vector_constrain[2] == 'Z':
+ Color4f = self.axis_z_color
+ else:
+ Color4f = self.constrain_shift_color
+ else:
+ if type == 'OUT':
+ Color4f = self.out_color
+ elif type == 'FACE':
+ Color4f = self.face_color
+ elif type == 'EDGE':
+ Color4f = self.edge_color
+ elif type == 'VERT':
+ Color4f = self.vert_color
+ elif type == 'CENTER':
+ Color4f = self.center_color
+ elif type == 'PERPENDICULAR':
+ Color4f = self.perpendicular_color
+ else: # type == None
+ Color4f = self.out_color
+
+ bgl.glPointSize(10)
+
+ gpu.matrix.translate(location)
+ self._program_unif_col.uniform_float("color", Color4f)
+ point_batch.draw(self._program_unif_col)
+
+ # restore opengl defaults
+ bgl.glDepthRange(0.0, 1.0)
+ bgl.glPointSize(1.0)
+ bgl.glLineWidth(1.0)
+ bgl.glEnable(bgl.GL_DEPTH_TEST)
+ bgl.glDisable(bgl.GL_BLEND)
+
+ gpu.matrix.pop()
+
+ def draw_elem(self, snap_obj, bm, elem):
+ #TODO: Cache coords (because antialiasing)
+ import gpu
+ from bmesh.types import(
+ BMVert,
+ BMEdge,
+ BMFace,
+ )
+ # draw 3d point OpenGL in the 3D View
+ bgl.glEnable(bgl.GL_BLEND)
+ bgl.glDisable(bgl.GL_DEPTH_TEST)
+
+ with gpu.matrix.push_pop():
+ gpu.matrix.multiply_matrix(snap_obj.mat)
+
+ if isinstance(elem, BMVert):
+ if elem.link_edges:
+ import numpy as np
+
+ 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()
+
+ if isinstance(elem, BMEdge):
+ self._program_unif_col.uniform_float("color", self.edge_color)
+
+ bgl.glLineWidth(3.0)
+ batch = self.batch_line_strip_create([v.co for v in elem.verts])
+ batch.draw(self._program_unif_col)
+ bgl.glLineWidth(1.0)
+
+ elif isinstance(elem, BMFace):
+ if len(snap_obj.data) == 2:
+ face_color = self.face_color[0], self.face_color[1], self.face_color[2], self.face_color[3] * 0.2
+ self._program_unif_col.uniform_float("color", face_color)
+
+ tris = snap_obj.data[1].get_loop_tri_co_by_bmface(bm, elem)
+ tris.shape = (-1, 3)
+ batch = self.batch_triangles_create(tris)
+ batch.draw(self._program_unif_col)
+
+ # restore opengl defaults
+ bgl.glEnable(bgl.GL_DEPTH_TEST)
+ bgl.glDisable(bgl.GL_BLEND)
diff --git a/mesh_snap_utilities_line/navigation_ops.py b/mesh_snap_utilities_line/navigation_ops.py
new file mode 100644
index 00000000..21eecc19
--- /dev/null
+++ b/mesh_snap_utilities_line/navigation_ops.py
@@ -0,0 +1,102 @@
+### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+
+
+class VIEW3D_OT_rotate_custom_pivot(bpy.types.Operator):
+ bl_idname = "view3d.rotate_custom_pivot"
+ bl_label = "Rotate the view"
+ bl_options = {'BLOCKING', 'GRAB_CURSOR'}
+
+ pivot: bpy.props.FloatVectorProperty("Pivot", subtype='XYZ')
+ g_up_axis: bpy.props.FloatVectorProperty("up_axis", default=(0.0, 0.0, 1.0), subtype='XYZ')
+ sensitivity: bpy.props.FloatProperty("sensitivity", default=0.007)
+
+ def modal(self, context, event):
+ from mathutils import Matrix
+ if event.value == 'PRESS' and event.type in {'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE'}:
+ dx = self.init_coord[0] - event.mouse_region_x
+ dy = self.init_coord[1] - event.mouse_region_y
+ rot_ver = Matrix.Rotation(-dx * self.sensitivity, 3, self.g_up_axis)
+ rot_hor = Matrix.Rotation(dy * self.sensitivity, 3, self.view_rot[0])
+ rot_mat = rot_hor @ rot_ver
+ view_matrix = self.view_rot @ rot_mat
+
+ pos = self.pos1 @ rot_mat + self.pivot
+ qua = view_matrix.to_quaternion()
+ qua.invert()
+
+ self.rv3d.view_location = pos
+ self.rv3d.view_rotation = qua
+
+ context.area.tag_redraw()
+ return {'RUNNING_MODAL'}
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ self.rv3d = context.region_data
+ self.init_coord = event.mouse_region_x, event.mouse_region_y
+ self.pos1 = self.rv3d.view_location - self.pivot
+ self.view_rot = self.rv3d.view_matrix.to_3x3()
+
+ context.window_manager.modal_handler_add(self)
+ return {'RUNNING_MODAL'}
+
+
+class VIEW3D_OT_zoom_custom_target(bpy.types.Operator):
+ bl_idname = "view3d.zoom_custom_target"
+ bl_label = "Zoom the view"
+ bl_options = {'BLOCKING', 'GRAB_CURSOR'}
+
+ target: bpy.props.FloatVectorProperty("target", subtype='XYZ')
+ delta: bpy.props.IntProperty("delta", default=0)
+ step_factor = 0.333
+
+ def modal(self, context, event):
+ if event.value == 'PRESS' and event.type in {'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE'}:
+ if not hasattr(self, "init_mouse_region_y"):
+ self.init_mouse_region_y = event.mouse_region_y
+ self.heigt_up = context.area.height - self.init_mouse_region_y
+ self.rv3d.view_location = self.target
+
+ fac = (event.mouse_region_y - self.init_mouse_region_y) / self.heigt_up
+ ret = 'RUNNING_MODAL'
+ else:
+ fac = self.step_factor * self.delta
+ ret = 'FINISHED'
+
+ self.rv3d.view_location = self.init_loc + (self.target - self.init_loc) * fac
+ self.rv3d.view_distance = self.init_dist - self.init_dist * fac
+
+ context.area.tag_redraw()
+ return {ret}
+
+ def invoke(self, context, event):
+ v3d = context.space_data
+ dist_range = (v3d.clip_start, v3d.clip_end)
+ self.rv3d = context.region_data
+ self.init_dist = self.rv3d.view_distance
+ if ((self.delta <= 0 and self.init_dist < dist_range[1]) or
+ (self.delta > 0 and self.init_dist > dist_range[0])):
+ self.init_loc = self.rv3d.view_location.copy()
+
+ context.window_manager.modal_handler_add(self)
+ return {'RUNNING_MODAL'}
+
+ return {'FINISHED'}
diff --git a/mesh_snap_utilities_line/ops_line.py b/mesh_snap_utilities_line/op_line.py
index 891a6d29..585792bd 100644
--- a/mesh_snap_utilities_line/ops_line.py
+++ b/mesh_snap_utilities_line/op_line.py
@@ -15,24 +15,20 @@
#
# ##### END GPL LICENSE BLOCK #####
-import bpy, bmesh
-
-from bpy.props import FloatProperty
+import bpy
+import bmesh
from mathutils import Vector
-
from mathutils.geometry import intersect_point_line
+from .drawing_utilities import SnapDrawn
+from .common_utilities import snap_utilities
from .common_classes import (
- SnapDrawn,
CharMap,
SnapNavigation,
SnapUtilities,
)
-from .common_utilities import (
- snap_utilities,
- )
if not __package__:
__package__ = "mesh_snap_utilities_line"
@@ -276,7 +272,7 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
if self.rv3d.view_matrix != self.rotMat:
self.rotMat = self.rv3d.view_matrix.copy()
self.bool_update = True
- snap_utilities.cache.snp_obj = None # hack for snap edge elemens update
+ snap_utilities.edge_cache.snp_obj = None # hack for snap edge elemens update
else:
self.bool_update = False
@@ -381,7 +377,7 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
self._exit(context)
return {'FINISHED'}
else:
- snap_utilities.cache.snp_obj = None # hack for snap edge elemens update
+ snap_utilities.edge_cache.snp_obj = None # hack for snap edge elemens update
self.vector_constrain = None
self.list_edges = []
self.list_verts = []
diff --git a/mesh_snap_utilities_line/preferences.py b/mesh_snap_utilities_line/preferences.py
index af2ca231..7361284e 100644
--- a/mesh_snap_utilities_line/preferences.py
+++ b/mesh_snap_utilities_line/preferences.py
@@ -68,9 +68,9 @@ class SnapUtilitiesPreferences(bpy.types.AddonPreferences):
precision=3)
out_color: FloatVectorProperty(name="OUT", default=(0.0, 0.0, 0.0, 0.5), size=4, subtype="COLOR", min=0, max=1)
- face_color: FloatVectorProperty(name="FACE", default=(1.0, 0.8, 0.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
- edge_color: FloatVectorProperty(name="EDGE", default=(0.0, 0.8, 1.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
- vert_color: FloatVectorProperty(name="VERT", default=(1.0, 0.5, 0.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
+ face_color: FloatVectorProperty(name="FACE", default=(1.0, 0.8, 0.0, 0.5), size=4, subtype="COLOR", min=0, max=1)
+ edge_color: FloatVectorProperty(name="EDGE", default=(0.0, 0.8, 1.0, 0.5), size=4, subtype="COLOR", min=0, max=1)
+ vert_color: FloatVectorProperty(name="VERT", default=(1.0, 0.5, 0.0, 0.5), size=4, subtype="COLOR", min=0, max=1)
center_color: FloatVectorProperty(name="CENTER", default=(1.0, 0.0, 1.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
perpendicular_color: FloatVectorProperty(name="PERPENDICULAR", default=(0.1, 0.5, 0.5, 1.0), size=4, subtype="COLOR", min=0, max=1)
constrain_shift_color: FloatVectorProperty(name="SHIFT CONSTRAIN", default=(0.8, 0.5, 0.4, 1.0), size=4, subtype="COLOR", min=0, max=1)
diff --git a/mesh_snap_utilities_line/snap_context_l/__init__.py b/mesh_snap_utilities_line/snap_context_l/__init__.py
index 3f663434..2aab0a3e 100644
--- a/mesh_snap_utilities_line/snap_context_l/__init__.py
+++ b/mesh_snap_utilities_line/snap_context_l/__init__.py
@@ -329,7 +329,7 @@ class SnapContext():
for snap_obj in self.snap_objects:
if len(snap_obj.data) == 2:
snap_obj.data[1].free()
- snap_obj.data.pop(1)
+ del snap_obj.data[1:]
del snap_obj.data
del snap_obj.mat
@@ -368,7 +368,7 @@ class SnapContext():
for snap_obj in self.snap_objects:
if len(snap_obj.data) == 2:
snap_obj.data[1].free()
- snap_obj.data.pop(1)
+ del snap_obj.data[1:]
self.update_drawing()
@@ -380,7 +380,7 @@ class SnapContext():
def tag_update_drawn_snap_object(self, snap_obj):
if len(snap_obj.data) > 1:
snap_obj.data[1].free()
- snap_obj.data.pop(1)
+ del snap_obj.data[1:]
#self.update_drawing()
# Update on next snap_get call #
self.proj_mat = None
@@ -395,7 +395,7 @@ class SnapContext():
if len(snap_obj.data) > 1:
snap_obj.data[1].free()
- snap_obj.data.pop(1)
+ del snap_obj.data[1:]
data = GPU_Indices_Mesh(self.depsgraph, snap_obj.data[0], snap_face, snap_edge, snap_vert)
snap_obj.data.append(data)
diff --git a/mesh_snap_utilities_line/widgets.py b/mesh_snap_utilities_line/widgets.py
new file mode 100644
index 00000000..6f8639e0
--- /dev/null
+++ b/mesh_snap_utilities_line/widgets.py
@@ -0,0 +1,244 @@
+### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+
+from .common_classes import SnapUtilities
+from .common_utilities import snap_utilities
+from .drawing_utilities import SnapDrawn
+
+
+#def mesh_runtime_batchcache_isdirty(me):
+# import ctypes
+# batch_cache = ctypes.c_void_p.from_address(me.as_pointer() + 1440)
+# if batch_cache:
+# return ctypes.c_bool.from_address(batch_cache.value + 549).value
+# return False
+
+
+class SnapWidgetCommon(SnapUtilities):
+ snap_to_update = False
+
+ def handler(self, scene):
+ cls = SnapWidgetCommon
+ if cls.snap_to_update is False:
+ last_operator = self.wm_operators[-1] if self.wm_operators else None
+ if (not last_operator or
+ last_operator.name not in {'Select', 'Loop Select', '(De)select All'}):
+ cls.snap_to_update = self.depsgraph.id_type_updated('MESH') or\
+ self.depsgraph.id_type_updated('OBJECT')
+
+ def draw_point_and_elem(self):
+ if self.bm:
+ if self.bm.is_valid and self.geom.is_valid:
+ self.draw_cache.draw_elem(self.snap_obj, self.bm, self.geom)
+ else:
+ self.bm = None
+ self.geom = None
+ self.sctx.update_all()
+
+ self.draw_cache.draw(self.type, self.location, None, None, None)
+
+ def init_snapwidget(self, context, snap_edge_and_vert = True):
+ self.snap_context_init(context, snap_edge_and_vert)
+ self.snap_context_update(context)
+ self.mode = context.mode
+ self.last_mval = None
+
+ self.wm_operators = context.window_manager.operators
+ self.depsgraph = context.depsgraph
+ bpy.app.handlers.depsgraph_update_post.append(self.handler)
+ SnapWidgetCommon.snap_to_update = False
+
+ SnapUtilities.snapwidgets.append(self)
+
+ def end_snapwidget(self):
+ SnapUtilities.snapwidgets.remove(self)
+
+ #from .snap_context_l import global_snap_context_get
+ #sctx = global_snap_context_get(None, None, None)
+
+ sctx = object.__getattribute__(self, 'sctx')
+ if sctx and not SnapUtilities.snapwidgets:
+ sctx.clear_snap_objects()
+
+ handler = object.__getattribute__(self, 'handler')
+ bpy.app.handlers.depsgraph_update_post.remove(handler)
+
+ def update_snap(self, context, mval):
+ if self.last_mval == mval:
+ return -1
+ else:
+ self.last_mval = mval
+
+ if (SnapWidgetCommon.snap_to_update):
+ ## Something has changed since the last time.
+ # Has the mesh been changed?
+ # In the doubt lets clear the snap context.
+ self.snap_context_update(context)
+ SnapWidgetCommon.snap_to_update = False
+
+ #print('test_select', mval)
+ space = context.space_data
+ self.sctx.update_viewport_context(context.depsgraph, context.region, space, True)
+
+ shading = space.shading
+ snap_face = not ((self.snap_vert or self.snap_edge) and
+ (shading.show_xray or shading.type == 'WIREFRAME'))
+
+ if snap_face != self.snap_face:
+ self.snap_face = snap_face
+ self.sctx.set_snap_mode(
+ self.snap_vert, self.snap_edge, self.snap_face)
+
+ snap_utilities.edge_cache.snp_obj = None # hack for snap edge elemens update
+ self.snap_obj, prev_loc, self.location, self.type, self.bm, self.geom, len = snap_utilities(
+ self.sctx,
+ None,
+ mval,
+ increment=self.incremental
+ )
+
+
+class SnapPointWidget(SnapWidgetCommon, bpy.types.Gizmo):
+ bl_idname = "VIEW3D_GT_snap_point"
+
+ def test_select(self, context, mval):
+ self.update_snap(context, mval)
+ self.snap_to_grid()
+
+ context.area.tag_redraw()
+ return -1
+
+ def draw(self, context):
+ self.draw_point_and_elem()
+
+ def setup(self):
+ self.init_snapwidget(bpy.context)
+
+
+class SnapFaceWidget(SnapWidgetCommon, bpy.types.Gizmo):
+ bl_idname = "VIEW3D_GT_snap_face_point"
+
+ def test_select(self, context, mval):
+ self.update_snap(context, mval)
+ self.snap_to_grid()
+
+ context.area.tag_redraw()
+ return -1
+
+ def draw(self, context):
+ self.draw_point_and_elem()
+
+ def setup(self):
+ self.init_snapwidget(bpy.context, False)
+
+
+class SnapCircleWidget(SnapWidgetCommon, bpy.types.Gizmo):
+ bl_idname = "VIEW3D_GT_snap_circle"
+
+ from mathutils import Vector
+ zero_vector = Vector()
+ default_normal = Vector((0.0, 0.0, 1.0))
+
+ def get_normal(self, context):
+ if self.constrain:
+ return
+
+ view_vector, orig = self.sctx.last_ray
+ if not self.rv3d.is_perspective:
+ #temporary hack
+ orig -= view_vector * 100
+
+ normal_face = context.scene.ray_cast(context.view_layer, orig, view_vector)[2]
+ if normal_face and normal_face != self.zero_vector:
+ self.normal = normal_face
+ else:
+ self.normal = self.default_normal
+
+ def test_select(self, context, mval):
+ self.update_snap(context, mval)
+ self.snap_to_grid()
+ self.get_normal(context)
+
+ context.area.tag_redraw()
+ return -1
+
+ def draw(self, context):
+ self.draw_point_and_elem()
+ self.draw_cache.draw_rot_tool(
+ self.rv3d.view_distance / 15,
+ self.location, self.normal, None)
+
+ def setup(self):
+ context = bpy.context
+ self.init_snapwidget(context)
+ self.rv3d = context.region_data
+ self.normal = self.default_normal
+
+
+def context_mode_check(context, widget_group):
+ workspace = context.workspace
+ mode = workspace.tools_mode
+ for tool in workspace.tools:
+ if (tool.widget == widget_group) and (tool.mode == mode):
+ break
+ else:
+ context.window_manager.gizmo_group_type_unlink_delayed(widget_group)
+ return False
+ return True
+
+
+class SnapWidgetGroupCommon:
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'WINDOW'
+ bl_options = {'3D'}
+
+ @classmethod
+ def poll(cls, context):
+ return context_mode_check(context, cls.bl_idname)
+
+ def init_tool(self, context, gizmo_name):
+ self.widget = self.gizmos.new(gizmo_name)
+
+ def __del__(self):
+ if hasattr(self, "widget"):
+ object.__getattribute__(self.widget, 'end_snapwidget')()
+
+
+class SnapPointWidgetGroup(SnapWidgetGroupCommon, bpy.types.GizmoGroup):
+ bl_idname = "MESH_GGT_snap_point"
+ bl_label = "Draw Snap Point"
+
+ def setup(self, context):
+ self.init_tool(context, SnapPointWidget.bl_idname)
+
+
+class SnapCircleWidgetGroup(SnapWidgetGroupCommon, bpy.types.GizmoGroup):
+ bl_idname = "MESH_GGT_snap_circle"
+ bl_label = "Draw Snap Circle"
+
+ def setup(self, context):
+ self.init_tool(context, SnapCircleWidget.bl_idname)
+
+
+class SnapFaceWidgetGroup(SnapWidgetGroupCommon, bpy.types.GizmoGroup):
+ bl_idname = "MESH_GGT_face_snap_point"
+ bl_label = "Draw Face and Snap Point"
+
+ def setup(self, context):
+ self.init_tool(context, SnapFaceWidget.bl_idname)