Fix T71100: Node Wrangler creates nodes on linked node trees
[blender-addons.git] / mesh_tiny_cad / VTX.py
blob93b03c5e2f51389e742d1c5ba73d548ff6628135
1 # SPDX-License-Identifier: GPL-2.0-or-later
4 import bpy
5 import bmesh
6 import sys
8 from . import cad_module as cm
10 messages = {
11 'SHARED_VERTEX': 'Shared Vertex, no intersection possible',
12 'PARALLEL_EDGES': 'Edges Parallel, no intersection possible',
13 'NON_PLANAR_EDGES': 'Non Planar Edges, no clean intersection point'
17 def add_edges(bm, pt, idxs, fdp):
18 '''
19 this function is a disaster --
20 index updates and ensure_lookup_table() are called before this function
21 and after, and i've tried doing this less verbose but results tend to be
22 less predictable. I'm obviously a terrible coder, but can only spend so
23 much time figuring out this stuff.
24 '''
26 v1 = bm.verts.new(pt)
28 bm.verts.ensure_lookup_table()
29 bm.edges.ensure_lookup_table()
30 bm.verts.index_update()
32 try:
33 for e in idxs:
34 bm.edges.index_update()
35 v2 = bm.verts[e]
36 bm.edges.new((v1, v2))
38 bm.edges.index_update()
39 bm.verts.ensure_lookup_table()
40 bm.edges.ensure_lookup_table()
42 except Exception as err:
43 print('some failure: details')
44 for l in fdp:
45 print(l)
47 sys.stderr.write('ERROR: %s\n' % str(err))
48 print(sys.exc_info()[-1].tb_frame.f_code)
49 print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno))
52 def remove_earmarked_edges(bm, earmarked):
53 edges_select = [e for e in bm.edges if e.index in earmarked]
54 bmesh.ops.delete(bm, geom=edges_select, context='EDGES')
57 def perform_vtx(bm, pt, edges, pts, vertex_indices):
58 idx1, idx2 = edges[0].index, edges[1].index
59 fdp = pt, edges, pts, vertex_indices
61 # this list will hold those edges that pt lies on
62 edges_indices = cm.find_intersecting_edges(bm, pt, idx1, idx2)
63 mode = 'VTX'[len(edges_indices)]
65 if mode == 'V':
66 cl_vert1 = cm.closest_idx(pt, edges[0])
67 cl_vert2 = cm.closest_idx(pt, edges[1])
68 add_edges(bm, pt, [cl_vert1, cl_vert2], fdp)
70 elif mode == 'T':
71 to_edge_idx = edges_indices[0]
72 from_edge_idx = idx1 if to_edge_idx == idx2 else idx2
74 cl_vert = cm.closest_idx(pt, bm.edges[from_edge_idx])
75 to_vert1, to_vert2 = cm.vert_idxs_from_edge_idx(bm, to_edge_idx)
76 add_edges(bm, pt, [cl_vert, to_vert1, to_vert2], fdp)
78 elif mode == 'X':
79 add_edges(bm, pt, vertex_indices, fdp)
81 # final refresh before returning to user.
82 if edges_indices:
83 remove_earmarked_edges(bm, edges_indices)
85 bm.edges.index_update()
86 return bm
89 def do_vtx_if_appropriate(bm, edges):
90 vertex_indices = cm.get_vert_indices_from_bmedges(edges)
92 # test 1, are there shared vers? if so return non-viable
93 if not len(set(vertex_indices)) == 4:
94 return {'SHARED_VERTEX'}
96 # test 2, is parallel?
97 p1, p2, p3, p4 = [bm.verts[i].co for i in vertex_indices]
98 point = cm.get_intersection([p1, p2], [p3, p4])
99 if not point:
100 return {'PARALLEL_EDGES'}
102 # test 3, coplanar edges?
103 coplanar = cm.test_coplanar([p1, p2], [p3, p4])
104 if not coplanar:
105 return {'NON_PLANAR_EDGES'}
107 # point must lie on an edge or the virtual extension of an edge
108 bm = perform_vtx(bm, point, edges, (p1, p2, p3, p4), vertex_indices)
109 return bm
112 class TCAutoVTX(bpy.types.Operator):
113 '''Weld intersecting edges, project converging edges towards their intersection'''
114 bl_idname = 'tinycad.autovtx'
115 bl_label = 'VTX autoVTX'
117 @classmethod
118 def poll(cls, context):
119 obj = context.active_object
120 return bool(obj) and obj.type == 'MESH'
122 def cancel_message(self, msg):
123 print(msg)
124 self.report({"WARNING"}, msg)
125 return {'CANCELLED'}
127 def execute(self, context):
129 # final attempt to enter unfragmented bm/mesh
130 # ghastly, but what can I do? it works with these
131 # fails without.
132 bpy.ops.object.mode_set(mode='OBJECT')
133 bpy.ops.object.mode_set(mode='EDIT')
135 obj = context.active_object
136 me = obj.data
138 bm = bmesh.from_edit_mesh(me)
139 bm.verts.ensure_lookup_table()
140 bm.edges.ensure_lookup_table()
142 edges = [e for e in bm.edges if e.select and not e.hide]
144 if len(edges) == 2:
145 message = do_vtx_if_appropriate(bm, edges)
146 if isinstance(message, set):
147 msg = messages.get(message.pop())
148 return self.cancel_message(msg)
149 bm = message
150 else:
151 return self.cancel_message('select two edges!')
153 bm.verts.index_update()
154 bm.edges.index_update()
155 bmesh.update_edit_mesh(me, loop_triangles=True)
157 return {'FINISHED'}