1 # SPDX-FileCopyrightText: 2018-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # python tip: from-imports don't save memory.
6 # They execute and cache the entire module just like a regular import.
11 from mathutils
import Vector
12 from mathutils
.geometry
import (
18 from .snap_context_l
import SnapContext
21 def get_units_info(scale
, unit_system
, separate_units
):
22 if unit_system
== 'METRIC':
23 scale_steps
= ((1000, 'km'), (1, 'm'), (1 / 100, 'cm'),
24 (1 / 1000, 'mm'), (1 / 1000000, '\u00b5m'))
25 elif unit_system
== 'IMPERIAL':
26 scale_steps
= ((5280, 'mi'), (1, '\''),
27 (1 / 12, '"'), (1 / 12000, 'thou'))
28 scale
/= 0.3048 # BU to feet
30 scale_steps
= ((1, ' BU'),)
31 separate_units
= False
33 return (scale
, scale_steps
, separate_units
)
36 def convert_distance(val
, units_info
, precision
=5):
37 scale
, scale_steps
, separate_units
= units_info
40 while idx
< len(scale_steps
) - 1:
41 if sval
>= scale_steps
[idx
][0]:
44 factor
, suffix
= scale_steps
[idx
]
46 if not separate_units
or idx
== len(scale_steps
) - 1:
47 dval
= str(round(sval
, precision
)) + suffix
50 dval
= str(round(ival
, precision
)) + suffix
53 while idx
< len(scale_steps
):
54 fval
*= scale_steps
[idx
- 1][0] / scale_steps
[idx
][0]
65 def location_3d_to_region_2d(region
, rv3d
, coord
):
66 prj
= rv3d
.perspective_matrix
@ Vector((coord
[0], coord
[1], coord
[2], 1.0))
67 width_half
= region
.width
/ 2.0
68 height_half
= region
.height
/ 2.0
69 return Vector((width_half
+ width_half
* (prj
.x
/ prj
.w
),
70 height_half
+ height_half
* (prj
.y
/ prj
.w
)
74 def out_Location(rv3d
, orig
, vector
):
75 view_matrix
= rv3d
.view_matrix
76 v1
= (int(view_matrix
[0][0] * 1.5), int(view_matrix
[0][1] * 1.5), int(view_matrix
[0][2] * 1.5))
77 v2
= (int(view_matrix
[1][0] * 1.5), int(view_matrix
[1][1] * 1.5), int(view_matrix
[1][2] * 1.5))
79 hit
= intersect_ray_tri((1, 0, 0), (0, 1, 0),
80 (0, 0, 0), (vector
), (orig
), False)
82 hit
= intersect_ray_tri(v1
, v2
, (0, 0, 0), (vector
), (orig
), False)
84 hit
= intersect_ray_tri(v1
, v2
, (0, 0, 0), (-vector
), (orig
), False)
90 def get_snap_bm_geom(sctx
, main_snap_obj
, mcursor
):
92 r_snp_obj
, r_loc
, r_elem
, r_elem_co
= sctx
.snap_get(mcursor
, main_snap_obj
)
93 r_view_vector
, r_orig
= sctx
.last_ray
97 if r_snp_obj
is not None:
98 obj
= r_snp_obj
.data
[0]
100 if obj
.type == 'MESH' and obj
.data
.is_editmode
:
101 r_bm
= bmesh
.from_edit_mesh(obj
.data
)
103 r_bm_geom
= r_bm
.verts
[r_elem
[0]]
105 elif len(r_elem
) == 2:
107 v1
= r_bm
.verts
[r_elem
[0]]
108 v2
= r_bm
.verts
[r_elem
[1]]
109 r_bm_geom
= r_bm
.edges
.get([v1
, v2
])
111 r_bm
.verts
.ensure_lookup_table()
113 elif len(r_elem
) == 3:
115 r_bm
.verts
[r_elem
[0]],
116 r_bm
.verts
[r_elem
[1]],
117 r_bm
.verts
[r_elem
[2]],
120 faces
= set(tri
[0].link_faces
).intersection(
121 tri
[1].link_faces
, tri
[2].link_faces
)
123 r_bm_geom
= faces
.pop()
127 while not edge
and i
!= 1:
128 edge
= r_bm
.edges
.get([tri
[i
], tri
[i
+ 1]])
131 for l
in edge
.link_loops
:
132 if l
.link_loop_next
.vert
== tri
[i
] or l
.link_loop_prev
.vert
== tri
[i
- 2]:
138 return r_snp_obj
, r_loc
, r_elem
, r_elem_co
, r_view_vector
, r_orig
, r_bm
, r_bm_geom
141 class SnapCache(object):
142 __slots__
= 'edge', 'face'
145 __slots__
= 'snp_obj', 'elem', 'vmid', 'vperp', 'v2dmid', 'v2dperp', 'is_increment'
154 self
.is_increment
= False
157 __slots__
= 'bm_face', 'vmid', 'v2dmid'
163 self
.edge
= self
.Edge()
164 self
.face
= self
.Face()
167 self
.edge
.snp_obj
= self
.face
.bm_face
= None
170 _snap_cache
= SnapCache()
180 snp_obj
, loc
, elem
, elem_co
, view_vector
, orig
, bm
, bm_geom
= get_snap_bm_geom(
181 sctx
, main_snap_obj
, mcursor
)
191 end
= orig
+ view_vector
192 t_loc
= intersect_line_line(constrain
[0], constrain
[1], orig
, end
)
197 r_loc
= out_Location(sctx
.rv3d
, orig
, view_vector
)
203 r_loc
= intersect_point_line(loc
, constrain
[0], constrain
[1])[0]
210 if _snap_cache
.edge
.snp_obj
is not snp_obj
or not (elem
== _snap_cache
.edge
.elem
).all():
211 _snap_cache
.edge
.snp_obj
= snp_obj
212 _snap_cache
.edge
.elem
= elem
216 _snap_cache
.edge
.vmid
= 0.5 * (v0
+ v1
)
217 _snap_cache
.edge
.v2dmid
= location_3d_to_region_2d(
218 sctx
.region
, sctx
.rv3d
, _snap_cache
.edge
.vmid
)
220 if previous_vert
and (not bm_geom
or previous_vert
not in bm_geom
.verts
):
221 pvert_co
= main_snap_obj
.mat
@ previous_vert
.co
222 perp_point
= intersect_point_line(pvert_co
, v0
, v1
)
223 _snap_cache
.edge
.vperp
= perp_point
[0]
224 # factor = point_perpendicular[1]
225 _snap_cache
.edge
.v2dperp
= location_3d_to_region_2d(
226 sctx
.region
, sctx
.rv3d
, perp_point
[0])
227 _snap_cache
.edge
.is_increment
= False
229 _snap_cache
.edge
.is_increment
= True
231 # else: _snap_cache.edge.v2dperp = None
234 t_loc
= intersect_line_line(
235 constrain
[0], constrain
[1], elem_co
[0], elem_co
[1])
238 end
= orig
+ view_vector
239 t_loc
= intersect_line_line(
240 constrain
[0], constrain
[1], orig
, end
)
243 elif _snap_cache
.edge
.v2dperp
and\
244 abs(_snap_cache
.edge
.v2dperp
[0] - mcursor
[0]) < sctx
._dist
_px
and abs(_snap_cache
.edge
.v2dperp
[1] - mcursor
[1]) < sctx
._dist
_px
:
245 r_type
= 'PERPENDICULAR'
246 r_loc
= _snap_cache
.edge
.vperp
248 elif abs(_snap_cache
.edge
.v2dmid
[0] - mcursor
[0]) < sctx
._dist
_px
and abs(_snap_cache
.edge
.v2dmid
[1] - mcursor
[1]) < sctx
._dist
_px
:
250 r_loc
= _snap_cache
.edge
.vmid
254 is_increment
= _snap_cache
.edge
.is_increment
259 # vmid = v2dmid = None
260 # if bm_geom and _snap_cache.face is not bm_geom:
261 # _snap_cache.face.bm_face = bm_geom
262 # vmid = _snap_cache.face.vmid = bm_geom.calc_center_median()
263 # v2dmid = _snap_cache.face.v2dmid = location_3d_to_region_2d(
264 # sctx.region, sctx.rv3d, _snap_cache.face.vmid)
268 # elem_world_co = [snp_obj.mat @ co for co in elem_co]
269 # ray_dir = constrain[1] - constrain[0]
270 # r_loc = intersect_ray_tri(*elem_world_co, ray_dir, constrain[0], False)
272 # r_loc = intersect_ray_tri(*elem_world_co, -ray_dir, constrain[0], False)
274 r_loc
= intersect_point_line(loc
, constrain
[0], constrain
[1])[0]
276 # elif v2dmid and abs(v2dmid[0] - mcursor[0]) < 10 and abs(v2dmid[1] - mcursor[1]) < 10:
285 pv_co
= main_snap_obj
.mat
@ previous_vert
.co
287 if is_increment
and increment
:
288 r_len
= round((1 / increment
) * vec
.length
) * increment
289 r_loc
= r_len
* vec
.normalized() + pv_co
293 return snp_obj
, loc
, r_loc
, r_type
, bm
, bm_geom
, r_len
296 snap_utilities
.cache
= _snap_cache