mirror of
https://github.com/blender/blender-addons.git
synced 2025-07-25 16:05:20 +00:00

Move copyright text to SPDX-FileCopyrightText or set to the Blender Foundation so "make check_licenses" now runs without warnings.
160 lines
4.6 KiB
Python
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'}
|