io_mesh_uv_layout: lazy import exporter modules
[blender-addons.git] / mesh_tiny_cad / VTX.py
blob5cdb2fcfe0c728dcf6c15d257bd99f92667fd47a
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
22 import bpy
23 import bmesh
24 import sys
26 from . import cad_module as cm
28 messages = {
29 'SHARED_VERTEX': 'Shared Vertex, no intersection possible',
30 'PARALLEL_EDGES': 'Edges Parallel, no intersection possible',
31 'NON_PLANAR_EDGES': 'Non Planar Edges, no clean intersection point'
35 def add_edges(bm, pt, idxs, fdp):
36 '''
37 this function is a disaster --
38 index updates and ensure_lookup_table() are called before this function
39 and after, and i've tried doing this less verbose but results tend to be
40 less predictable. I'm obviously a terrible coder, but can only spend so
41 much time figuring out this stuff.
42 '''
44 v1 = bm.verts.new(pt)
46 bm.verts.ensure_lookup_table()
47 bm.edges.ensure_lookup_table()
48 bm.verts.index_update()
50 try:
51 for e in idxs:
52 bm.edges.index_update()
53 v2 = bm.verts[e]
54 bm.edges.new((v1, v2))
56 bm.edges.index_update()
57 bm.verts.ensure_lookup_table()
58 bm.edges.ensure_lookup_table()
60 except Exception as err:
61 print('some failure: details')
62 for l in fdp:
63 print(l)
65 sys.stderr.write('ERROR: %s\n' % str(err))
66 print(sys.exc_info()[-1].tb_frame.f_code)
67 print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno))
70 def remove_earmarked_edges(bm, earmarked):
71 edges_select = [e for e in bm.edges if e.index in earmarked]
72 bmesh.ops.delete(bm, geom=edges_select, context=2)
75 def perform_vtx(bm, pt, edges, pts, vertex_indices):
76 idx1, idx2 = edges[0].index, edges[1].index
77 fdp = pt, edges, pts, vertex_indices
79 # this list will hold those edges that pt lies on
80 edges_indices = cm.find_intersecting_edges(bm, pt, idx1, idx2)
81 mode = 'VTX'[len(edges_indices)]
83 if mode == 'V':
84 cl_vert1 = cm.closest_idx(pt, edges[0])
85 cl_vert2 = cm.closest_idx(pt, edges[1])
86 add_edges(bm, pt, [cl_vert1, cl_vert2], fdp)
88 elif mode == 'T':
89 to_edge_idx = edges_indices[0]
90 from_edge_idx = idx1 if to_edge_idx == idx2 else idx2
92 cl_vert = cm.closest_idx(pt, bm.edges[from_edge_idx])
93 to_vert1, to_vert2 = cm.vert_idxs_from_edge_idx(bm, to_edge_idx)
94 add_edges(bm, pt, [cl_vert, to_vert1, to_vert2], fdp)
96 elif mode == 'X':
97 add_edges(bm, pt, vertex_indices, fdp)
99 # final refresh before returning to user.
100 if edges_indices:
101 remove_earmarked_edges(bm, edges_indices)
103 bm.edges.index_update()
104 return bm
107 def do_vtx_if_appropriate(bm, edges):
108 vertex_indices = cm.get_vert_indices_from_bmedges(edges)
110 # test 1, are there shared vers? if so return non-viable
111 if not len(set(vertex_indices)) == 4:
112 return {'SHARED_VERTEX'}
114 # test 2, is parallel?
115 p1, p2, p3, p4 = [bm.verts[i].co for i in vertex_indices]
116 point = cm.get_intersection([p1, p2], [p3, p4])
117 if not point:
118 return {'PARALLEL_EDGES'}
120 # test 3, coplanar edges?
121 coplanar = cm.test_coplanar([p1, p2], [p3, p4])
122 if not coplanar:
123 return {'NON_PLANAR_EDGES'}
125 # point must lie on an edge or the virtual extention of an edge
126 bm = perform_vtx(bm, point, edges, (p1, p2, p3, p4), vertex_indices)
127 return bm
130 class TCAutoVTX(bpy.types.Operator):
131 '''Weld intersecting edges, project converging edges towards their intersection'''
132 bl_idname = 'tinycad.autovtx'
133 bl_label = 'VTX autoVTX'
135 @classmethod
136 def poll(cls, context):
137 obj = context.active_object
138 return bool(obj) and obj.type == 'MESH'
140 def cancel_message(self, msg):
141 print(msg)
142 self.report({"WARNING"}, msg)
143 return {'CANCELLED'}
145 def execute(self, context):
147 # final attempt to enter unfragmented bm/mesh
148 # ghastly, but what can I do? it works with these
149 # fails without.
150 bpy.ops.object.mode_set(mode='OBJECT')
151 bpy.ops.object.mode_set(mode='EDIT')
153 obj = context.active_object
154 me = obj.data
156 bm = bmesh.from_edit_mesh(me)
157 bm.verts.ensure_lookup_table()
158 bm.edges.ensure_lookup_table()
160 edges = [e for e in bm.edges if e.select and not e.hide]
162 if len(edges) == 2:
163 message = do_vtx_if_appropriate(bm, edges)
164 if isinstance(message, set):
165 msg = messages.get(message.pop())
166 return self.cancel_message(msg)
167 bm = message
168 else:
169 return self.cancel_message('select two edges!')
171 bm.verts.index_update()
172 bm.edges.index_update()
173 bmesh.update_edit_mesh(me, True)
175 return {'FINISHED'}