io_mesh_uv_layout: speed up png export with OIIO (x7)
[blender-addons.git] / mesh_snap_utilities_line / common_utilities.py
blobc66825722778ac84843cf98e56b3151a560a7bfd
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # python tip: from-imports don't save memory.
4 # They execute and cache the entire module just like a regular import.
6 import bpy
7 import bmesh
9 from mathutils import Vector
10 from mathutils.geometry import (
11 intersect_point_line,
12 intersect_line_line,
13 intersect_ray_tri,
16 from .snap_context_l import SnapContext
19 def get_units_info(scale, unit_system, separate_units):
20 if unit_system == 'METRIC':
21 scale_steps = ((1000, 'km'), (1, 'm'), (1 / 100, 'cm'),
22 (1 / 1000, 'mm'), (1 / 1000000, '\u00b5m'))
23 elif unit_system == 'IMPERIAL':
24 scale_steps = ((5280, 'mi'), (1, '\''),
25 (1 / 12, '"'), (1 / 12000, 'thou'))
26 scale /= 0.3048 # BU to feet
27 else:
28 scale_steps = ((1, ' BU'),)
29 separate_units = False
31 return (scale, scale_steps, separate_units)
34 def convert_distance(val, units_info, precision=5):
35 scale, scale_steps, separate_units = units_info
36 sval = val * scale
37 idx = 0
38 while idx < len(scale_steps) - 1:
39 if sval >= scale_steps[idx][0]:
40 break
41 idx += 1
42 factor, suffix = scale_steps[idx]
43 sval /= factor
44 if not separate_units or idx == len(scale_steps) - 1:
45 dval = str(round(sval, precision)) + suffix
46 else:
47 ival = int(sval)
48 dval = str(round(ival, precision)) + suffix
49 fval = sval - ival
50 idx += 1
51 while idx < len(scale_steps):
52 fval *= scale_steps[idx - 1][0] / scale_steps[idx][0]
53 if fval >= 1:
54 dval += ' ' \
55 + ("%.1f" % fval) \
56 + scale_steps[idx][1]
57 break
58 idx += 1
60 return dval
63 def location_3d_to_region_2d(region, rv3d, coord):
64 prj = rv3d.perspective_matrix @ Vector((coord[0], coord[1], coord[2], 1.0))
65 width_half = region.width / 2.0
66 height_half = region.height / 2.0
67 return Vector((width_half + width_half * (prj.x / prj.w),
68 height_half + height_half * (prj.y / prj.w)
72 def out_Location(rv3d, orig, vector):
73 view_matrix = rv3d.view_matrix
74 v1 = (int(view_matrix[0][0] * 1.5), int(view_matrix[0][1] * 1.5), int(view_matrix[0][2] * 1.5))
75 v2 = (int(view_matrix[1][0] * 1.5), int(view_matrix[1][1] * 1.5), int(view_matrix[1][2] * 1.5))
77 hit = intersect_ray_tri((1, 0, 0), (0, 1, 0),
78 (0, 0, 0), (vector), (orig), False)
79 if hit is None:
80 hit = intersect_ray_tri(v1, v2, (0, 0, 0), (vector), (orig), False)
81 if hit is None:
82 hit = intersect_ray_tri(v1, v2, (0, 0, 0), (-vector), (orig), False)
83 if hit is None:
84 hit = Vector()
85 return hit
88 def get_snap_bm_geom(sctx, main_snap_obj, mcursor):
90 r_snp_obj, r_loc, r_elem, r_elem_co = sctx.snap_get(mcursor, main_snap_obj)
91 r_view_vector, r_orig = sctx.last_ray
92 r_bm = None
93 r_bm_geom = None
95 if r_snp_obj is not None:
96 obj = r_snp_obj.data[0]
98 if obj.type == 'MESH' and obj.data.is_editmode:
99 r_bm = bmesh.from_edit_mesh(obj.data)
100 if len(r_elem) == 1:
101 r_bm_geom = r_bm.verts[r_elem[0]]
103 elif len(r_elem) == 2:
104 try:
105 v1 = r_bm.verts[r_elem[0]]
106 v2 = r_bm.verts[r_elem[1]]
107 r_bm_geom = r_bm.edges.get([v1, v2])
108 except IndexError:
109 r_bm.verts.ensure_lookup_table()
111 elif len(r_elem) == 3:
112 tri = [
113 r_bm.verts[r_elem[0]],
114 r_bm.verts[r_elem[1]],
115 r_bm.verts[r_elem[2]],
118 faces = set(tri[0].link_faces).intersection(
119 tri[1].link_faces, tri[2].link_faces)
120 if len(faces) == 1:
121 r_bm_geom = faces.pop()
122 else:
123 i = -2
124 edge = None
125 while not edge and i != 1:
126 edge = r_bm.edges.get([tri[i], tri[i + 1]])
127 i += 1
128 if edge:
129 for l in edge.link_loops:
130 if l.link_loop_next.vert == tri[i] or l.link_loop_prev.vert == tri[i - 2]:
131 r_bm_geom = l.face
132 break
133 if r_loc is None:
134 r_loc = r_elem_co[0]
136 return r_snp_obj, r_loc, r_elem, r_elem_co, r_view_vector, r_orig, r_bm, r_bm_geom
139 class SnapCache(object):
140 __slots__ = 'edge', 'face'
142 class Edge:
143 __slots__ = 'snp_obj', 'elem', 'vmid', 'vperp', 'v2dmid', 'v2dperp', 'is_increment'
145 def __init__(self):
146 self.snp_obj = None
147 self.elem = None
148 self.vmid = None
149 self.vperp = None
150 self.v2dmid = None
151 self.v2dperp = None
152 self.is_increment = False
154 class Face:
155 __slots__ = 'bm_face', 'vmid', 'v2dmid'
157 def __init__(self):
158 self.bm_face = None
160 def __init__(self):
161 self.edge = self.Edge()
162 self.face = self.Face()
164 def clear(self):
165 self.edge.snp_obj = self.face.bm_face = None
168 _snap_cache = SnapCache()
171 def snap_utilities(
172 sctx, main_snap_obj,
173 mcursor,
174 constrain=None,
175 previous_vert=None,
176 increment=0.0):
178 snp_obj, loc, elem, elem_co, view_vector, orig, bm, bm_geom = get_snap_bm_geom(
179 sctx, main_snap_obj, mcursor)
181 is_increment = False
182 r_loc = None
183 r_type = 'OUT'
184 r_len = 0.0
186 if not snp_obj:
187 is_increment = True
188 if constrain:
189 end = orig + view_vector
190 t_loc = intersect_line_line(constrain[0], constrain[1], orig, end)
191 if t_loc is None:
192 t_loc = constrain
193 r_loc = t_loc[0]
194 else:
195 r_loc = out_Location(sctx.rv3d, orig, view_vector)
197 elif len(elem) == 1:
198 r_type = 'VERT'
200 if constrain:
201 r_loc = intersect_point_line(loc, constrain[0], constrain[1])[0]
202 else:
203 r_loc = loc
205 elif len(elem) == 2:
206 r_type = 'EDGE'
208 if _snap_cache.edge.snp_obj is not snp_obj or not (elem == _snap_cache.edge.elem).all():
209 _snap_cache.edge.snp_obj = snp_obj
210 _snap_cache.edge.elem = elem
212 v0 = elem_co[0]
213 v1 = elem_co[1]
214 _snap_cache.edge.vmid = 0.5 * (v0 + v1)
215 _snap_cache.edge.v2dmid = location_3d_to_region_2d(
216 sctx.region, sctx.rv3d, _snap_cache.edge.vmid)
218 if previous_vert and (not bm_geom or previous_vert not in bm_geom.verts):
219 pvert_co = main_snap_obj.mat @ previous_vert.co
220 perp_point = intersect_point_line(pvert_co, v0, v1)
221 _snap_cache.edge.vperp = perp_point[0]
222 # factor = point_perpendicular[1]
223 _snap_cache.edge.v2dperp = location_3d_to_region_2d(
224 sctx.region, sctx.rv3d, perp_point[0])
225 _snap_cache.edge.is_increment = False
226 else:
227 _snap_cache.edge.is_increment = True
229 # else: _snap_cache.edge.v2dperp = None
231 if constrain:
232 t_loc = intersect_line_line(
233 constrain[0], constrain[1], elem_co[0], elem_co[1])
234 if t_loc is None:
235 is_increment = True
236 end = orig + view_vector
237 t_loc = intersect_line_line(
238 constrain[0], constrain[1], orig, end)
239 r_loc = t_loc[0]
241 elif _snap_cache.edge.v2dperp and\
242 abs(_snap_cache.edge.v2dperp[0] - mcursor[0]) < sctx._dist_px and abs(_snap_cache.edge.v2dperp[1] - mcursor[1]) < sctx._dist_px:
243 r_type = 'PERPENDICULAR'
244 r_loc = _snap_cache.edge.vperp
246 elif abs(_snap_cache.edge.v2dmid[0] - mcursor[0]) < sctx._dist_px and abs(_snap_cache.edge.v2dmid[1] - mcursor[1]) < sctx._dist_px:
247 r_type = 'CENTER'
248 r_loc = _snap_cache.edge.vmid
250 else:
251 r_loc = loc
252 is_increment = _snap_cache.edge.is_increment
254 elif len(elem) == 3:
255 r_type = 'FACE'
257 # vmid = v2dmid = None
258 # if bm_geom and _snap_cache.face is not bm_geom:
259 # _snap_cache.face.bm_face = bm_geom
260 # vmid = _snap_cache.face.vmid = bm_geom.calc_center_median()
261 # v2dmid = _snap_cache.face.v2dmid = location_3d_to_region_2d(
262 # sctx.region, sctx.rv3d, _snap_cache.face.vmid)
264 if constrain:
265 is_increment = False
266 # elem_world_co = [snp_obj.mat @ co for co in elem_co]
267 # ray_dir = constrain[1] - constrain[0]
268 # r_loc = intersect_ray_tri(*elem_world_co, ray_dir, constrain[0], False)
269 # if r_loc is None:
270 # r_loc = intersect_ray_tri(*elem_world_co, -ray_dir, constrain[0], False)
271 # if r_loc is None:
272 r_loc = intersect_point_line(loc, constrain[0], constrain[1])[0]
274 # elif v2dmid and abs(v2dmid[0] - mcursor[0]) < 10 and abs(v2dmid[1] - mcursor[1]) < 10:
275 # r_type = 'CENTER'
276 # r_loc = vmid
278 else:
279 r_loc = loc
280 is_increment = True
282 if previous_vert:
283 pv_co = main_snap_obj.mat @ previous_vert.co
284 vec = r_loc - pv_co
285 if is_increment and increment:
286 r_len = round((1 / increment) * vec.length) * increment
287 r_loc = r_len * vec.normalized() + pv_co
288 else:
289 r_len = vec.length
291 return snp_obj, loc, r_loc, r_type, bm, bm_geom, r_len
294 snap_utilities.cache = _snap_cache