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.
9 from mathutils
import Vector
10 from mathutils
.geometry
import (
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
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
38 while idx
< len(scale_steps
) - 1:
39 if sval
>= scale_steps
[idx
][0]:
42 factor
, suffix
= scale_steps
[idx
]
44 if not separate_units
or idx
== len(scale_steps
) - 1:
45 dval
= str(round(sval
, precision
)) + suffix
48 dval
= str(round(ival
, precision
)) + suffix
51 while idx
< len(scale_steps
):
52 fval
*= scale_steps
[idx
- 1][0] / scale_steps
[idx
][0]
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)
80 hit
= intersect_ray_tri(v1
, v2
, (0, 0, 0), (vector
), (orig
), False)
82 hit
= intersect_ray_tri(v1
, v2
, (0, 0, 0), (-vector
), (orig
), False)
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
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
)
101 r_bm_geom
= r_bm
.verts
[r_elem
[0]]
103 elif len(r_elem
) == 2:
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
])
109 r_bm
.verts
.ensure_lookup_table()
111 elif len(r_elem
) == 3:
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
)
121 r_bm_geom
= faces
.pop()
125 while not edge
and i
!= 1:
126 edge
= r_bm
.edges
.get([tri
[i
], tri
[i
+ 1]])
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]:
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'
143 __slots__
= 'snp_obj', 'elem', 'vmid', 'vperp', 'v2dmid', 'v2dperp', 'is_increment'
152 self
.is_increment
= False
155 __slots__
= 'bm_face', 'vmid', 'v2dmid'
161 self
.edge
= self
.Edge()
162 self
.face
= self
.Face()
165 self
.edge
.snp_obj
= self
.face
.bm_face
= None
168 _snap_cache
= SnapCache()
178 snp_obj
, loc
, elem
, elem_co
, view_vector
, orig
, bm
, bm_geom
= get_snap_bm_geom(
179 sctx
, main_snap_obj
, mcursor
)
189 end
= orig
+ view_vector
190 t_loc
= intersect_line_line(constrain
[0], constrain
[1], orig
, end
)
195 r_loc
= out_Location(sctx
.rv3d
, orig
, view_vector
)
201 r_loc
= intersect_point_line(loc
, constrain
[0], constrain
[1])[0]
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
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
227 _snap_cache
.edge
.is_increment
= True
229 # else: _snap_cache.edge.v2dperp = None
232 t_loc
= intersect_line_line(
233 constrain
[0], constrain
[1], elem_co
[0], elem_co
[1])
236 end
= orig
+ view_vector
237 t_loc
= intersect_line_line(
238 constrain
[0], constrain
[1], orig
, end
)
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
:
248 r_loc
= _snap_cache
.edge
.vmid
252 is_increment
= _snap_cache
.edge
.is_increment
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)
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)
270 # r_loc = intersect_ray_tri(*elem_world_co, -ray_dir, constrain[0], False)
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:
283 pv_co
= main_snap_obj
.mat
@ previous_vert
.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
291 return snp_obj
, loc
, r_loc
, r_type
, bm
, bm_geom
, r_len
294 snap_utilities
.cache
= _snap_cache