1 # SPDX-License-Identifier: GPL-2.0-or-later
8 from . import cad_module
as cm
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
):
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.
28 bm
.verts
.ensure_lookup_table()
29 bm
.edges
.ensure_lookup_table()
30 bm
.verts
.index_update()
34 bm
.edges
.index_update()
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')
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
)]
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
)
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
)
79 add_edges(bm
, pt
, vertex_indices
, fdp
)
81 # final refresh before returning to user.
83 remove_earmarked_edges(bm
, edges_indices
)
85 bm
.edges
.index_update()
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
])
100 return {'PARALLEL_EDGES'}
102 # test 3, coplanar edges?
103 coplanar
= cm
.test_coplanar([p1
, p2
], [p3
, p4
])
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
)
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'
118 def poll(cls
, context
):
119 obj
= context
.active_object
120 return bool(obj
) and obj
.type == 'MESH'
122 def cancel_message(self
, msg
):
124 self
.report({"WARNING"}, msg
)
127 def execute(self
, context
):
129 # final attempt to enter unfragmented bm/mesh
130 # ghastly, but what can I do? it works with these
132 bpy
.ops
.object.mode_set(mode
='OBJECT')
133 bpy
.ops
.object.mode_set(mode
='EDIT')
135 obj
= context
.active_object
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
]
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
)
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)