Files
Campbell Barton e8da6131fd License headers: use SPDX-FileCopyrightText for all addons
Move copyright text to SPDX-FileCopyrightText or set to the
Blender Foundation so "make check_licenses" now runs without warnings.
2023-06-15 16:54:05 +10:00

160 lines
4.6 KiB
Python

# SPDX-FileCopyrightText: 2016-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
import bmesh
import sys
from . import cad_module as cm
messages = {
'SHARED_VERTEX': 'Shared Vertex, no intersection possible',
'PARALLEL_EDGES': 'Edges Parallel, no intersection possible',
'NON_PLANAR_EDGES': 'Non Planar Edges, no clean intersection point'
}
def add_edges(bm, pt, idxs, fdp):
'''
this function is a disaster --
index updates and ensure_lookup_table() are called before this function
and after, and i've tried doing this less verbose but results tend to be
less predictable. I'm obviously a terrible coder, but can only spend so
much time figuring out this stuff.
'''
v1 = bm.verts.new(pt)
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
bm.verts.index_update()
try:
for e in idxs:
bm.edges.index_update()
v2 = bm.verts[e]
bm.edges.new((v1, v2))
bm.edges.index_update()
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
except Exception as err:
print('some failure: details')
for l in fdp:
print(l)
sys.stderr.write('ERROR: %s\n' % str(err))
print(sys.exc_info()[-1].tb_frame.f_code)
print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno))
def remove_earmarked_edges(bm, earmarked):
edges_select = [e for e in bm.edges if e.index in earmarked]
bmesh.ops.delete(bm, geom=edges_select, context='EDGES')
def perform_vtx(bm, pt, edges, pts, vertex_indices):
idx1, idx2 = edges[0].index, edges[1].index
fdp = pt, edges, pts, vertex_indices
# this list will hold those edges that pt lies on
edges_indices = cm.find_intersecting_edges(bm, pt, idx1, idx2)
mode = 'VTX'[len(edges_indices)]
if mode == 'V':
cl_vert1 = cm.closest_idx(pt, edges[0])
cl_vert2 = cm.closest_idx(pt, edges[1])
add_edges(bm, pt, [cl_vert1, cl_vert2], fdp)
elif mode == 'T':
to_edge_idx = edges_indices[0]
from_edge_idx = idx1 if to_edge_idx == idx2 else idx2
cl_vert = cm.closest_idx(pt, bm.edges[from_edge_idx])
to_vert1, to_vert2 = cm.vert_idxs_from_edge_idx(bm, to_edge_idx)
add_edges(bm, pt, [cl_vert, to_vert1, to_vert2], fdp)
elif mode == 'X':
add_edges(bm, pt, vertex_indices, fdp)
# final refresh before returning to user.
if edges_indices:
remove_earmarked_edges(bm, edges_indices)
bm.edges.index_update()
return bm
def do_vtx_if_appropriate(bm, edges):
vertex_indices = cm.get_vert_indices_from_bmedges(edges)
# test 1, are there shared vers? if so return non-viable
if not len(set(vertex_indices)) == 4:
return {'SHARED_VERTEX'}
# test 2, is parallel?
p1, p2, p3, p4 = [bm.verts[i].co for i in vertex_indices]
point = cm.get_intersection([p1, p2], [p3, p4])
if not point:
return {'PARALLEL_EDGES'}
# test 3, coplanar edges?
coplanar = cm.test_coplanar([p1, p2], [p3, p4])
if not coplanar:
return {'NON_PLANAR_EDGES'}
# point must lie on an edge or the virtual extension of an edge
bm = perform_vtx(bm, point, edges, (p1, p2, p3, p4), vertex_indices)
return bm
class TCAutoVTX(bpy.types.Operator):
'''Weld intersecting edges, project converging edges towards their intersection'''
bl_idname = 'tinycad.autovtx'
bl_label = 'VTX autoVTX'
@classmethod
def poll(cls, context):
obj = context.active_object
return bool(obj) and obj.type == 'MESH'
def cancel_message(self, msg):
print(msg)
self.report({"WARNING"}, msg)
return {'CANCELLED'}
def execute(self, context):
# final attempt to enter unfragmented bm/mesh
# ghastly, but what can I do? it works with these
# fails without.
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.mode_set(mode='EDIT')
obj = context.active_object
me = obj.data
bm = bmesh.from_edit_mesh(me)
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()
edges = [e for e in bm.edges if e.select and not e.hide]
if len(edges) == 2:
message = do_vtx_if_appropriate(bm, edges)
if isinstance(message, set):
msg = messages.get(message.pop())
return self.cancel_message(msg)
bm = message
else:
return self.cancel_message('select two edges!')
bm.verts.index_update()
bm.edges.index_update()
bmesh.update_edit_mesh(me, loop_triangles=True)
return {'FINISHED'}