adds lookup tests to support older versions of blender too
[blender-addons-contrib.git] / mesh_tinyCAD / VTX.py
1 import bpy
2 import sys
3 import bmesh
4 from mathutils import Vector
5 from mathutils.geometry import intersect_line_line as LineIntersect
6 from mathutils.geometry import intersect_point_line as PtLineIntersect
7
8 from mesh_tinyCAD import cad_module as cm
9
10
11 def getVTX(self):
12     self.idx1, self.idx2 = self.selected_edges
13     self.edge1 = cm.coords_tuple_from_edge_idx(self.bm, self.idx1)
14     self.edge2 = cm.coords_tuple_from_edge_idx(self.bm, self.idx2)
15     self.point = cm.get_intersection(self.edge1, self.edge2)
16     self.edges = cm.find_intersecting_edges(
17         self.bm, self.point, self.idx1, self.idx2)
18
19
20 def add_edges(self, idxs):
21
22     # precaution?
23     if hasattr(self.bm.verts, "ensure_lookup_table"):
24         self.bm.verts.ensure_lookup_table()
25         self.bm.edges.ensure_lookup_table()
26
27     for e in idxs:
28         v1 = self.bm.verts[-1]
29         v2 = self.bm.verts[e]
30         self.bm.edges.new((v1, v2))
31
32
33 def remove_earmarked_edges(self, earmarked):
34     edges_select = [e for e in self.bm.edges if e.index in earmarked]
35     bmesh.ops.delete(self.bm, geom=edges_select, context=2)
36
37
38 def checkVTX(self, context):
39     '''
40     - decides VTX automatically.
41     - remembers edges attached to current selection, for later.
42     '''
43
44     # precaution?
45     if hasattr(self.bm.verts, "ensure_lookup_table"):
46         self.bm.verts.ensure_lookup_table()
47         self.bm.edges.ensure_lookup_table()
48
49     # if either of these edges share a vertex, return early.
50     indices = cm.vertex_indices_from_edges_tuple(self.bm, self.selected_edges)
51     if cm.duplicates(indices):
52         msg = "edges share a vertex, degenerate case, returning early"
53         self.report({"WARNING"}, msg)
54         return False
55
56     # find which edges intersect
57     getVTX(self)
58
59     # check coplanar, or parallel.
60     if [] == self.edges:
61         coplanar = cm.test_coplanar(self.edge1, self.edge2)
62         if not coplanar:
63             msg = "parallel or not coplanar! returning early"
64             self.report({"WARNING"}, msg)
65             return False
66
67     return True
68
69
70 def doVTX(self):
71     '''
72     At this point we know that there is an intersection, and if it
73     is V, T or X.
74     - If both are None, then both edges are projected towards point. (V)
75     - If only one is None, then it's a projection onto a real edge (T)
76     - Else, then the intersection lies on both edges (X)
77     '''
78     print('point:', self.point)
79     print('edges selected:', self.idx1, self.idx2)
80     print('edges to use:', self.edges)
81
82     self.bm.verts.new((self.point))
83
84     earmarked = self.edges
85     pt = self.point
86
87     # V (projection of both edges)
88     if [] == earmarked:
89         cl_vert1 = cm.closest_idx(pt, self.bm.edges[self.idx1])
90         cl_vert2 = cm.closest_idx(pt, self.bm.edges[self.idx2])
91         add_edges(self, [cl_vert1, cl_vert2])
92
93     # X (weld intersection)
94     elif len(earmarked) == 2:
95         vector_indices = cm.vertex_indices_from_edges_tuple(self.bm, earmarked)
96         add_edges(self, vector_indices)
97
98     # T (extend towards)
99     else:
100         to_edge_idx = self.edges[0]
101         from_edge_idx = self.idx1 if to_edge_idx == self.idx2 else self.idx2
102
103         # make 3 new edges: 2 on the towards, 1 as extender
104         cl_vert = cm.closest_idx(pt, self.bm.edges[from_edge_idx])
105         to_vert1, to_vert2 = cm.vert_idxs_from_edge_idx(self.bm, to_edge_idx)
106         roto_indices = [cl_vert, to_vert1, to_vert2]
107         add_edges(self, roto_indices)
108
109     # final refresh before returning to user.
110     if earmarked:
111         remove_earmarked_edges(self, earmarked)
112     bmesh.update_edit_mesh(self.me, True)
113
114
115 class AutoVTX(bpy.types.Operator):
116     bl_idname = 'view3d.autovtx'
117     bl_label = 'autoVTX'
118     # bl_options = {'REGISTER', 'UNDO'}
119
120     VTX_PRECISION = 1.0e-5  # or 1.0e-6 ..if you need
121
122     @classmethod
123     def poll(self, context):
124         '''
125         - only activate if two selected edges
126         - and both are not hidden
127         '''
128         obj = context.active_object
129         self.me = obj.data
130         self.bm = bmesh.from_edit_mesh(self.me)
131
132         # self.me.update()
133         if hasattr(self.bm.verts, "ensure_lookup_table"):
134             self.bm.verts.ensure_lookup_table()
135             self.bm.edges.ensure_lookup_table()
136
137         if obj is not None and obj.type == 'MESH':
138             edges = self.bm.edges
139             ok = lambda v: v.select and not v.hide
140             idxs = [v.index for v in edges if ok(v)]
141             if len(idxs) is 2:
142                 self.selected_edges = idxs
143                 return True
144
145     def execute(self, context):
146
147         self.me.update()
148         if checkVTX(self, context):
149             doVTX(self)
150
151         return {'FINISHED'}