Cleanup: Node Wrangler: preview_node operator
[blender-addons.git] / mesh_tiny_cad / VTX.py
blobe9088b61f1d99c11135b28690be8a67d69ebe012
1 # SPDX-FileCopyrightText: 2016-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 import bpy
7 import bmesh
8 import sys
10 from . import cad_module as cm
12 messages = {
13 'SHARED_VERTEX': 'Shared Vertex, no intersection possible',
14 'PARALLEL_EDGES': 'Edges Parallel, no intersection possible',
15 'NON_PLANAR_EDGES': 'Non Planar Edges, no clean intersection point'
19 def add_edges(bm, pt, idxs, fdp):
20 '''
21 this function is a disaster --
22 index updates and ensure_lookup_table() are called before this function
23 and after, and i've tried doing this less verbose but results tend to be
24 less predictable. I'm obviously a terrible coder, but can only spend so
25 much time figuring out this stuff.
26 '''
28 v1 = bm.verts.new(pt)
30 bm.verts.ensure_lookup_table()
31 bm.edges.ensure_lookup_table()
32 bm.verts.index_update()
34 try:
35 for e in idxs:
36 bm.edges.index_update()
37 v2 = bm.verts[e]
38 bm.edges.new((v1, v2))
40 bm.edges.index_update()
41 bm.verts.ensure_lookup_table()
42 bm.edges.ensure_lookup_table()
44 except Exception as err:
45 print('some failure: details')
46 for l in fdp:
47 print(l)
49 sys.stderr.write('ERROR: %s\n' % str(err))
50 print(sys.exc_info()[-1].tb_frame.f_code)
51 print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno))
54 def remove_earmarked_edges(bm, earmarked):
55 edges_select = [e for e in bm.edges if e.index in earmarked]
56 bmesh.ops.delete(bm, geom=edges_select, context='EDGES')
59 def perform_vtx(bm, pt, edges, pts, vertex_indices):
60 idx1, idx2 = edges[0].index, edges[1].index
61 fdp = pt, edges, pts, vertex_indices
63 # this list will hold those edges that pt lies on
64 edges_indices = cm.find_intersecting_edges(bm, pt, idx1, idx2)
65 mode = 'VTX'[len(edges_indices)]
67 if mode == 'V':
68 cl_vert1 = cm.closest_idx(pt, edges[0])
69 cl_vert2 = cm.closest_idx(pt, edges[1])
70 add_edges(bm, pt, [cl_vert1, cl_vert2], fdp)
72 elif mode == 'T':
73 to_edge_idx = edges_indices[0]
74 from_edge_idx = idx1 if to_edge_idx == idx2 else idx2
76 cl_vert = cm.closest_idx(pt, bm.edges[from_edge_idx])
77 to_vert1, to_vert2 = cm.vert_idxs_from_edge_idx(bm, to_edge_idx)
78 add_edges(bm, pt, [cl_vert, to_vert1, to_vert2], fdp)
80 elif mode == 'X':
81 add_edges(bm, pt, vertex_indices, fdp)
83 # final refresh before returning to user.
84 if edges_indices:
85 remove_earmarked_edges(bm, edges_indices)
87 bm.edges.index_update()
88 return bm
91 def do_vtx_if_appropriate(bm, edges):
92 vertex_indices = cm.get_vert_indices_from_bmedges(edges)
94 # test 1, are there shared vers? if so return non-viable
95 if not len(set(vertex_indices)) == 4:
96 return {'SHARED_VERTEX'}
98 # test 2, is parallel?
99 p1, p2, p3, p4 = [bm.verts[i].co for i in vertex_indices]
100 point = cm.get_intersection([p1, p2], [p3, p4])
101 if not point:
102 return {'PARALLEL_EDGES'}
104 # test 3, coplanar edges?
105 coplanar = cm.test_coplanar([p1, p2], [p3, p4])
106 if not coplanar:
107 return {'NON_PLANAR_EDGES'}
109 # point must lie on an edge or the virtual extension of an edge
110 bm = perform_vtx(bm, point, edges, (p1, p2, p3, p4), vertex_indices)
111 return bm
114 class TCAutoVTX(bpy.types.Operator):
115 '''Weld intersecting edges, project converging edges towards their intersection'''
116 bl_idname = 'tinycad.autovtx'
117 bl_label = 'VTX autoVTX'
119 @classmethod
120 def poll(cls, context):
121 obj = context.active_object
122 return bool(obj) and obj.type == 'MESH'
124 def cancel_message(self, msg):
125 print(msg)
126 self.report({"WARNING"}, msg)
127 return {'CANCELLED'}
129 def execute(self, context):
131 # final attempt to enter unfragmented bm/mesh
132 # ghastly, but what can I do? it works with these
133 # fails without.
134 bpy.ops.object.mode_set(mode='OBJECT')
135 bpy.ops.object.mode_set(mode='EDIT')
137 obj = context.active_object
138 me = obj.data
140 bm = bmesh.from_edit_mesh(me)
141 bm.verts.ensure_lookup_table()
142 bm.edges.ensure_lookup_table()
144 edges = [e for e in bm.edges if e.select and not e.hide]
146 if len(edges) == 2:
147 message = do_vtx_if_appropriate(bm, edges)
148 if isinstance(message, set):
149 msg = messages.get(message.pop())
150 return self.cancel_message(msg)
151 bm = message
152 else:
153 return self.cancel_message('select two edges!')
155 bm.verts.index_update()
156 bm.edges.index_update()
157 bmesh.update_edit_mesh(me, loop_triangles=True)
159 return {'FINISHED'}