[mesh_snap_utilities] use new `snap_context` module

This commit is contained in:
Germano Cavalcante 2017-09-09 20:24:12 -03:00
parent 136a8f839a
commit c5de2c5249
1 changed files with 160 additions and 183 deletions

View File

@ -19,10 +19,11 @@
# Email: germano.costa@ig.com.br
# Twitter: wii_mano @mano_wii
bl_info = {
"name": "Snap Utilities Line",
"author": "Germano Cavalcante",
"version": (5, 7, 2),
"version": (5, 7, 5),
"blender": (2, 75, 0),
"location": "View3D > TOOLS > Snap Utilities > snap utilities",
"description": "Extends Blender Snap controls",
@ -51,6 +52,13 @@ from bpy.props import (
StringProperty,
)
from snap_context import SnapContext
##DEBUG = False
##if DEBUG:
## from .snap_framebuffer_debug import screenTexture
## from .snap_context import mesh_drawing
def get_units_info(scale, unit_system, separate_units):
if unit_system == 'METRIC':
@ -106,51 +114,6 @@ def location_3d_to_region_2d(region, rv3d, coord):
))
def fac_nearest_to_segment_2d(co2v, v2d0, v2d1):
u = v2d1.xy - v2d0.xy
h = co2v.xy - v2d0.xy
return u.dot(h) / u.length_squared
def region_2d_to_orig_and_view_vector(region, rv3d, coord, clamp=None):
viewinv = rv3d.view_matrix.inverted()
persinv = rv3d.perspective_matrix.inverted()
dx = (2.0 * coord[0] / region.width) - 1.0
dy = (2.0 * coord[1] / region.height) - 1.0
if rv3d.is_perspective:
origin_start = viewinv.translation.copy()
out = Vector((dx, dy, -0.5))
w = out.dot(persinv[3].xyz) + persinv[3][3]
view_vector = ((persinv * out) / w) - origin_start
else:
view_vector = -viewinv.col[2].xyz
origin_start = ((persinv.col[0].xyz * dx) +
(persinv.col[1].xyz * dy) +
viewinv.translation)
if clamp != 0.0:
if rv3d.view_perspective != 'CAMERA':
# this value is scaled to the far clip already
origin_offset = persinv.col[2].xyz
if clamp is not None:
if clamp < 0.0:
origin_offset.negate()
clamp = -clamp
if origin_offset.length > clamp:
origin_offset.length = clamp
origin_start -= origin_offset
view_vector.normalize()
return origin_start, view_vector
def out_Location(rv3d, region, orig, vector):
view_matrix = rv3d.view_matrix
v1 = Vector((int(view_matrix[0][0] * 1.5), int(view_matrix[0][1] * 1.5), int(view_matrix[0][2] * 1.5)))
@ -166,7 +129,7 @@ def out_Location(rv3d, region, orig, vector):
return hit
def get_closest_edge(bm, point, dist):
def get_closest_edge(bm, point, dist_sq):
r_edge = None
for edge in bm.edges:
v1 = edge.verts[0].co
@ -190,9 +153,9 @@ def get_closest_edge(bm, point, dist):
else:
tmp = ret[0]
new_dist = (point - tmp).length
if new_dist <= dist:
dist = new_dist
new_dist_sq = (point - tmp).length_squared
if new_dist_sq <= dist_sq:
dist_sq = new_dist_sq
r_edge = edge
return r_edge
@ -212,23 +175,16 @@ class SnapCache():
v2dmid = None
v2dperp = None
bface = None
fmid = None
fnor = None
out_obj = None
out_obmat = None
out_obimat = None
bm_geom_selected = None
def snap_utilities(
cache, context, obj_matrix_world,
bm, mcursor,
outer_verts=False,
constrain=None,
previous_vert=None,
ignore_obj=None,
increment=0.0):
sctx, obj,
cache, context, obj_matrix_world,
bm, mcursor,
constrain = None,
previous_vert = None,
increment = 0.0):
rv3d = context.region_data
region = context.region
@ -239,62 +195,89 @@ def snap_utilities(
r_len = 0.0
bm_geom = None
if bm.select_history:
bm.select_history[0].select = False
bm.select_history.clear()
if cache.bm_geom_selected:
try:
cache.bm_geom_selected.select = False
except ReferenceError as e:
print(e)
bpy.ops.view3d.select(location=(int(mcursor.x), int(mcursor.y)))
snp_obj, loc, elem = sctx.snap_get(mcursor)
view_vector, orig = sctx.last_ray
if bm.select_history:
bm_geom = bm.select_history[0]
if not snp_obj:
is_increment = True
r_type = 'OUT'
r_loc = out_Location(rv3d, region, orig, view_vector)
if isinstance(bm_geom, bmesh.types.BMVert):
elif snp_obj.data[0] != obj: #OUT
r_loc = loc
if constrain:
is_increment = False
r_loc = intersect_point_line(r_loc, constrain[0], constrain[1])[0]
if not r_loc:
r_loc = out_Location(rv3d, region, orig, view_vector)
elif len(elem) == 1:
is_increment = False
r_type = 'VERT'
elif len(elem) == 2:
is_increment = True
r_type = 'EDGE'
else:
is_increment = True
r_type = 'FACE'
elif len(elem) == 1:
r_type = 'VERT'
bm_geom = bm.verts[elem[0]]
if cache.bvert != bm_geom:
cache.bvert = bm_geom
cache.vco = obj_matrix_world * cache.bvert.co
# cache.v2d = location_3d_to_region_2d(region, rv3d, cache.vco)
cache.vco = loc
#cache.v2d = location_3d_to_region_2d(region, rv3d, cache.vco)
if constrain:
location = intersect_point_line(cache.vco, constrain[0], constrain[1])
# factor = location[1]
#factor = location[1]
r_loc = location[0]
else:
r_loc = cache.vco
elif isinstance(bm_geom, bmesh.types.BMEdge):
elif len(elem) == 2:
v1 = bm.verts[elem[0]]
v2 = bm.verts[elem[1]]
bm_geom = bm.edges.get([v1, v2])
if cache.bedge != bm_geom:
cache.bedge = bm_geom
cache.v0 = obj_matrix_world * bm_geom.verts[0].co
cache.v1 = obj_matrix_world * bm_geom.verts[1].co
cache.v0 = obj_matrix_world * v1.co
cache.v1 = obj_matrix_world * v2.co
cache.vmid = 0.5 * (cache.v0 + cache.v1)
cache.v2d0 = location_3d_to_region_2d(region, rv3d, cache.v0)
cache.v2d1 = location_3d_to_region_2d(region, rv3d, cache.v1)
cache.v2dmid = location_3d_to_region_2d(region, rv3d, cache.vmid)
if previous_vert and previous_vert not in bm_geom.verts:
if previous_vert and previous_vert not in {v1, v2}:
pvert_co = obj_matrix_world * previous_vert.co
perp_point = intersect_point_line(pvert_co, cache.v0, cache.v1)
cache.vperp = perp_point[0]
# factor = point_perpendicular[1]
#factor = point_perpendicular[1]
cache.v2dperp = location_3d_to_region_2d(region, rv3d, perp_point[0])
# else: cache.v2dperp = None
#else: cache.v2dperp = None
if constrain:
location = intersect_line_line(constrain[0], constrain[1], cache.v0, cache.v1)
if location is None:
if location == None:
is_increment = True
orig, view_vector = region_2d_to_orig_and_view_vector(region, rv3d, mcursor)
end = orig + view_vector
location = intersect_line_line(constrain[0], constrain[1], orig, end)
r_loc = location[0]
elif cache.v2dperp and \
abs(cache.v2dperp[0] - mcursor[0]) < 10 and abs(cache.v2dperp[1] - mcursor[1]) < 10:
r_type = 'PERPENDICULAR'
r_loc = cache.vperp
elif cache.v2dperp and\
abs(cache.v2dperp[0] - mcursor[0]) < 10 and abs(cache.v2dperp[1] - mcursor[1]) < 10:
r_type = 'PERPENDICULAR'
r_loc = cache.vperp
elif abs(cache.v2dmid[0] - mcursor[0]) < 10 and abs(cache.v2dmid[1] - mcursor[1]) < 10:
r_type = 'CENTER'
@ -305,94 +288,41 @@ def snap_utilities(
is_increment = True
r_type = 'EDGE'
orig, view_vector = region_2d_to_orig_and_view_vector(region, rv3d, mcursor)
r_loc = intersect_line_line(cache.v0, cache.v1, orig, orig + view_vector)[0]
r_loc = loc
elif isinstance(bm_geom, bmesh.types.BMFace):
elif len(elem) == 3:
is_increment = True
r_type = 'FACE'
tri = [
bm.verts[elem[0]],
bm.verts[elem[1]],
bm.verts[elem[2]],
]
if cache.bface != bm_geom:
cache.bface = bm_geom
cache.fmid = obj_matrix_world * bm_geom.calc_center_median()
cache.fnor = bm_geom.normal * obj_matrix_world
faces = set(tri[0].link_faces).intersection(tri[1].link_faces, tri[2].link_faces)
if len(faces) == 1:
bm_geom = faces.pop()
else:
i = -2
edge = None
while not edge and i != 1:
edge = bm.edges.get([tri[i], tri[i + 1]])
i += 1
if edge:
for l in edge.link_loops:
if l.link_loop_next.vert == tri[i] or l.link_loop_prev.vert == tri[i - 2]:
bm_geom = l.face
break
else: # This should never happen!!!!
raise
bm_geom = faces.pop()
orig, view_vector = region_2d_to_orig_and_view_vector(region, rv3d, mcursor)
end = orig + view_vector
r_loc = intersect_line_plane(orig, end, cache.fmid, cache.fnor, False)
r_loc = loc
if constrain:
is_increment = False
r_loc = intersect_point_line(r_loc, constrain[0], constrain[1])[0]
else: # OUT
is_increment = True
r_type = 'OUT'
orig, view_vector = region_2d_to_orig_and_view_vector(region, rv3d, mcursor)
face_index = -1
if cache.out_obj is None:
## TODO: The Scene.raycast also tests the edited object.
# This is highly inefficient because the edited object
# does not keep DerivedMesh which forces rebuilding the bvhtree.
result, r_loc, normal, face_index, cache.out_obj, cache.out_obmat = scene.ray_cast(orig, view_vector)
if result:
r_type = 'FACE'
cache.out_obimat = cache.out_obmat.inverted()
else:
face_index = -1
r_loc = None
if cache.out_obj and cache.out_obj != ignore_obj:
if not r_loc or outer_verts:
location = None
if face_index == -1:
# get the ray relative to the cache.out_obj
ray_origin_obj = cache.out_obimat * orig
end = orig + view_vector * 1000
ray_target_obj = cache.out_obimat * end
result, location, normal, face_index = cache.out_obj.ray_cast(ray_origin_obj, ray_target_obj)
if face_index == -1:
cache.out_obj = None
elif outer_verts:
vloc = None
try:
me = cache.out_obj.data
verts = me.polygons[face_index].vertices
v_dist = 100
for i in verts:
v_co = cache.out_obmat * me.vertices[i].co
v_2d = location_3d_to_region_2d(region, rv3d, v_co)
dist = (Vector(mcursor) - v_2d.xy).length_squared
if dist < v_dist:
v_dist = dist
vloc = v_co
except Exception as e:
print('Fail', e)
if vloc:
is_increment = False
r_type = 'VERT'
r_loc = vloc
if not r_loc:
r_type = 'FACE'
r_loc = cache.out_obmat * location
if constrain:
if r_loc:
is_increment = False
r_loc = intersect_point_line(r_loc, constrain[0], constrain[1])[0]
else:
r_loc = intersect_line_line(constrain[0], constrain[1], orig, orig + view_vector)[0]
elif not r_loc:
r_loc = out_Location(rv3d, region, orig, view_vector)
if previous_vert:
pv_co = obj_matrix_world * previous_vert.co
vec = r_loc - pv_co
@ -402,6 +332,11 @@ def snap_utilities(
else:
r_len = vec.length
if bm_geom:
bm_geom.select = True
cache.bm_geom_selected = bm_geom
return r_loc, r_type, bm_geom, r_len
@ -421,6 +356,7 @@ def draw_line(self, obj, bm, bm_geom, location):
split_faces = set()
drawing_is_dirt = False
update_edit_mesh = False
tessface = False
@ -435,6 +371,7 @@ def draw_line(self, obj, bm, bm_geom, location):
else:
vert = bm.verts.new(location)
self.list_verts.append(vert)
drawing_is_dirt = True
elif isinstance(bm_geom, bmesh.types.BMEdge):
self.list_edges.append(bm_geom)
@ -447,40 +384,46 @@ def draw_line(self, obj, bm, 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
self.list_verts.append(vert)
# self.list_edges.append(edge)
else: # constrain point is near
vert = bm.verts.new(location)
self.list_verts.append(vert)
drawing_is_dirt = 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
# 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]
for edge in v2.link_edges:
if v1 in edge.verts:
edge = bm.edges.get([v1, v2])
if edge:
self.list_edges.append(edge)
break
else: # if v1 not in v2_link_verts:
if not v2.link_edges:
edge = bm.edges.new([v1, v2])
self.list_edges.append(edge)
drawing_is_dirt = True
else: # split face
if v1.link_faces and v2.link_faces:
split_faces.update({f for f in v2.link_faces if f in v1.link_faces})
v1_link_faces = v1.link_faces
v2_link_faces = v2.link_faces
if v1_link_faces and v2_link_faces:
split_faces.update(set(v1_link_faces).intersection(v2_link_faces))
else:
if v1.link_faces:
faces = v1.link_faces
if v1_link_faces:
faces = v1_link_faces
co2 = v2.co.copy()
else:
faces = v2.link_faces
faces = v2_link_faces
co2 = v1.co.copy()
for face in faces:
@ -505,9 +448,12 @@ def draw_line(self, obj, bm, 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
tessface = True
# create face
if self.create_face:
@ -519,7 +465,7 @@ def draw_line(self, obj, bm, bm_geom, location):
break
else:
continue
# Inner loop was broken, break the outer
# Inner loop had a break, break the outer
break
ed_list.update(get_loose_linked_edges(v2))
@ -529,7 +475,13 @@ def draw_line(self, obj, bm, bm_geom, location):
tessface = True
# print('face created')
if update_edit_mesh:
bmesh.update_edit_mesh(obj.data, tessface = tessface, destructive=True)
bmesh.update_edit_mesh(obj.data, tessface = tessface)
self.sctx.update_drawn_snap_object(self.snap_obj)
#bm.verts.index_update()
elif drawing_is_dirt:
self.obj.update_from_editmode()
self.sctx.update_drawn_snap_object(self.snap_obj)
return [obj.matrix_world * v.co for v in self.list_verts]
@ -621,6 +573,11 @@ class SnapUtilitiesLine(Operator):
# bgl.glPushMatrix()
# bgl.glMultMatrixf(self.obj_glmatrix)
## if DEBUG:
## mesh_drawing._store_current_shader_state(mesh_drawing.PreviousGLState)
## self.screen.Draw(self.sctx._texture)
## mesh_drawing._restore_shader_state(mesh_drawing.PreviousGLState)
if self.vector_constrain:
vc = self.vector_constrain
if hasattr(self, 'preloc') and self.type in {'VERT', 'FACE'}:
@ -727,6 +684,7 @@ class SnapUtilitiesLine(Operator):
self.obj = bpy.context.active_object
self.obj_matrix = self.obj.matrix_world.copy()
self.bm = bmesh.from_edit_mesh(self.obj.data)
self.sctx.update_drawn_snap_object(self.snap_obj)
return {'RUNNING_MODAL'}
if event.type == 'MOUSEMOVE' or self.bool_update:
@ -740,13 +698,11 @@ class SnapUtilitiesLine(Operator):
mval = Vector((event.mouse_region_x, event.mouse_region_y))
self.location, self.type, self.geom, self.len = snap_utilities(
self.cache, context, self.obj_matrix,
self.sctx, self.obj, self.cache, context, self.obj_matrix,
self.bm, mval,
outer_verts=(self.outer_verts and not self.keytab),
constrain=self.vector_constrain,
previous_vert=(self.list_verts[-1] if self.list_verts else None),
ignore_obj=self.obj,
increment=self.incremental
constrain = self.vector_constrain,
previous_vert = (self.list_verts[-1] if self.list_verts else None),
increment = self.incremental
)
if self.snap_to_grid and self.type == 'OUT':
loc = self.location / self.rd
@ -756,7 +712,7 @@ class SnapUtilitiesLine(Operator):
if self.keyf8 and self.list_verts_co:
lloc = self.list_verts_co[-1]
orig, view_vec = region_2d_to_orig_and_view_vector(self.region, self.rv3d, mval)
view_vec, orig = self.sctx.last_ray
location = intersect_point_line(lloc, orig, (orig + view_vec))
vec = (location[0] - lloc)
ax, ay, az = abs(vec.x), abs(vec.y), abs(vec.z)
@ -805,7 +761,7 @@ class SnapUtilitiesLine(Operator):
point = self.obj_matinv * self.location
# with constraint the intersection can be in a different element of the selected one
if self.vector_constrain and self.geom:
geom2 = get_closest_edge(self.bm, point, .001)
geom2 = get_closest_edge(self.bm, point, 1e-06)
else:
geom2 = self.geom
@ -816,8 +772,10 @@ class SnapUtilitiesLine(Operator):
elif event.type == 'TAB':
self.keytab = self.keytab is False
if self.keytab:
self.sctx.set_snap_mode(False, False, True)
context.tool_settings.mesh_select_mode = (False, False, True)
else:
self.sctx.set_snap_mode(True, True, True)
context.tool_settings.mesh_select_mode = (True, True, True)
elif event.type == 'F8':
@ -845,6 +803,7 @@ class SnapUtilitiesLine(Operator):
context.tool_settings.mesh_select_mode = self.select_mode
context.area.header_text_set()
context.user_preferences.view.use_rotate_around_active = self.use_rotate_around_active
self.sctx.free()
if not self.is_editmode:
bpy.ops.object.editmode_toggle()
return {'FINISHED'}
@ -863,6 +822,7 @@ class SnapUtilitiesLine(Operator):
length = self.len
length = convert_distance(length, self.uinfo)
a = 'length: ' + length
context.area.header_text_set(
"hit: %.3f %.3f %.3f %s" % (self.location[0],
self.location[1], self.location[2], a)
@ -947,6 +907,23 @@ class SnapUtilitiesLine(Operator):
self.outer_verts = preferences.outer_verts
self.snap_to_grid = preferences.increments_grid
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.snap_obj = self.sctx.add_obj(act_base.object, act_base.object.matrix_world)
if self.outer_verts:
for base in context.visible_bases:
if base != act_base:
self.sctx.add_obj(base.object, base.object.matrix_world)
## if DEBUG:
## self.screen = screenTexture()
bpy.ops.mesh.select_all(action='DESELECT')
self._handle = bpy.types.SpaceView3D.draw_handler_add(
self.draw_callback_px,
(context,),
@ -1067,7 +1044,7 @@ class SnapAddonPreferences(AddonPreferences):
category = StringProperty(
name="Category",
description="Choose a name for the category of the panel",
default="Tools",
default="Snap Utilities",
update=update_panel
)
incremental = FloatProperty(