1 ### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 3
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 # ##### END GPL LICENSE BLOCK #####
18 #python tip: from-imports don't save memory.
19 #They execute and cache the entire module just like a regular import.
24 from mathutils
import Vector
25 from mathutils
.geometry
import (
31 from .snap_context_l
import SnapContext
34 def get_units_info(scale
, unit_system
, separate_units
):
35 if unit_system
== 'METRIC':
36 scale_steps
= ((1000, 'km'), (1, 'm'), (1 / 100, 'cm'),
37 (1 / 1000, 'mm'), (1 / 1000000, '\u00b5m'))
38 elif unit_system
== 'IMPERIAL':
39 scale_steps
= ((5280, 'mi'), (1, '\''),
40 (1 / 12, '"'), (1 / 12000, 'thou'))
41 scale
/= 0.3048 # BU to feet
43 scale_steps
= ((1, ' BU'),)
44 separate_units
= False
46 return (scale
, scale_steps
, separate_units
)
49 def convert_distance(val
, units_info
, precision
=5):
50 scale
, scale_steps
, separate_units
= units_info
53 while idx
< len(scale_steps
) - 1:
54 if sval
>= scale_steps
[idx
][0]:
57 factor
, suffix
= scale_steps
[idx
]
59 if not separate_units
or idx
== len(scale_steps
) - 1:
60 dval
= str(round(sval
, precision
)) + suffix
63 dval
= str(round(ival
, precision
)) + suffix
66 while idx
< len(scale_steps
):
67 fval
*= scale_steps
[idx
- 1][0] / scale_steps
[idx
][0]
78 def location_3d_to_region_2d(region
, rv3d
, coord
):
79 prj
= rv3d
.perspective_matrix
@ Vector((coord
[0], coord
[1], coord
[2], 1.0))
80 width_half
= region
.width
/ 2.0
81 height_half
= region
.height
/ 2.0
82 return Vector((width_half
+ width_half
* (prj
.x
/ prj
.w
),
83 height_half
+ height_half
* (prj
.y
/ prj
.w
)
87 def out_Location(rv3d
, orig
, vector
):
88 view_matrix
= rv3d
.view_matrix
89 v1
= (int(view_matrix
[0][0]*1.5), int(view_matrix
[0][1]*1.5), int(view_matrix
[0][2]*1.5))
90 v2
= (int(view_matrix
[1][0]*1.5), int(view_matrix
[1][1]*1.5), int(view_matrix
[1][2]*1.5))
92 hit
= intersect_ray_tri((1,0,0), (0,1,0), (0,0,0), (vector
), (orig
), False)
94 hit
= intersect_ray_tri(v1
, v2
, (0,0,0), (vector
), (orig
), False)
96 hit
= intersect_ray_tri(v1
, v2
, (0,0,0), (-vector
), (orig
), False)
102 def get_snap_bm_geom(sctx
, main_snap_obj
, mcursor
):
104 r_snp_obj
, r_loc
, r_elem
, r_elem_co
= sctx
.snap_get(mcursor
, main_snap_obj
)
105 r_view_vector
, r_orig
= sctx
.last_ray
109 if r_snp_obj
is not None:
110 obj
= r_snp_obj
.data
[0]
112 if obj
.type == 'MESH' and obj
.data
.is_editmode
:
113 r_bm
= bmesh
.from_edit_mesh(obj
.data
)
115 r_bm_geom
= r_bm
.verts
[r_elem
[0]]
117 elif len(r_elem
) == 2:
119 v1
= r_bm
.verts
[r_elem
[0]]
120 v2
= r_bm
.verts
[r_elem
[1]]
121 r_bm_geom
= r_bm
.edges
.get([v1
, v2
])
123 r_bm
.verts
.ensure_lookup_table()
125 elif len(r_elem
) == 3:
127 r_bm
.verts
[r_elem
[0]],
128 r_bm
.verts
[r_elem
[1]],
129 r_bm
.verts
[r_elem
[2]],
132 faces
= set(tri
[0].link_faces
).intersection(tri
[1].link_faces
, tri
[2].link_faces
)
134 r_bm_geom
= faces
.pop()
138 while not edge
and i
!= 1:
139 edge
= r_bm
.edges
.get([tri
[i
], tri
[i
+ 1]])
142 for l
in edge
.link_loops
:
143 if l
.link_loop_next
.vert
== tri
[i
] or l
.link_loop_prev
.vert
== tri
[i
- 2]:
147 return r_snp_obj
, r_loc
, r_elem
, r_elem_co
, r_view_vector
, r_orig
, r_bm
, r_bm_geom
150 class SnapCache(object):
151 __slots__
= 'edge', 'face'
154 __slots__
= 'snp_obj', 'elem', 'vmid', 'vperp', 'v2dmid', 'v2dperp', 'is_increment'
163 self
.is_increment
= False
166 __slots__
= 'bm_face', 'vmid', 'v2dmid'
172 self
.edge
= self
.Edge()
173 self
.face
= self
.Face()
176 self
.edge
.snp_obj
= self
.face
.bm_face
= None
178 _snap_cache
= SnapCache()
185 previous_vert
= None,
188 snp_obj
, loc
, elem
, elem_co
, view_vector
, orig
, bm
, bm_geom
= get_snap_bm_geom(sctx
, main_snap_obj
, mcursor
)
198 end
= orig
+ view_vector
199 t_loc
= intersect_line_line(constrain
[0], constrain
[1], orig
, end
)
204 r_loc
= out_Location(sctx
.rv3d
, orig
, view_vector
)
210 r_loc
= intersect_point_line(loc
, constrain
[0], constrain
[1])[0]
217 if _snap_cache
.edge
.snp_obj
is not snp_obj
or not (elem
== _snap_cache
.edge
.elem
).all():
218 _snap_cache
.edge
.snp_obj
= snp_obj
219 _snap_cache
.edge
.elem
= elem
223 _snap_cache
.edge
.vmid
= 0.5 * (v0
+ v1
)
224 _snap_cache
.edge
.v2dmid
= location_3d_to_region_2d(
225 sctx
.region
, sctx
.rv3d
, _snap_cache
.edge
.vmid
)
227 if previous_vert
and (not bm_geom
or previous_vert
not in bm_geom
.verts
):
228 pvert_co
= main_snap_obj
.mat
@ previous_vert
.co
229 perp_point
= intersect_point_line(pvert_co
, v0
, v1
)
230 _snap_cache
.edge
.vperp
= perp_point
[0]
231 #factor = point_perpendicular[1]
232 _snap_cache
.edge
.v2dperp
= location_3d_to_region_2d(sctx
.region
, sctx
.rv3d
, perp_point
[0])
233 _snap_cache
.edge
.is_increment
= False
235 _snap_cache
.edge
.is_increment
= True
237 #else: _snap_cache.edge.v2dperp = None
240 t_loc
= intersect_line_line(constrain
[0], constrain
[1], elem_co
[0], elem_co
[1])
243 end
= orig
+ view_vector
244 t_loc
= intersect_line_line(constrain
[0], constrain
[1], orig
, end
)
247 elif _snap_cache
.edge
.v2dperp
and\
248 abs(_snap_cache
.edge
.v2dperp
[0] - mcursor
[0]) < 10 and abs(_snap_cache
.edge
.v2dperp
[1] - mcursor
[1]) < 10:
249 r_type
= 'PERPENDICULAR'
250 r_loc
= _snap_cache
.edge
.vperp
252 elif abs(_snap_cache
.edge
.v2dmid
[0] - mcursor
[0]) < 10 and abs(_snap_cache
.edge
.v2dmid
[1] - mcursor
[1]) < 10:
254 r_loc
= _snap_cache
.edge
.vmid
258 is_increment
= _snap_cache
.edge
.is_increment
263 # vmid = v2dmid = None
264 # if bm_geom and _snap_cache.face is not bm_geom:
265 # _snap_cache.face.bm_face = bm_geom
266 # vmid = _snap_cache.face.vmid = bm_geom.calc_center_median()
267 # v2dmid = _snap_cache.face.v2dmid = location_3d_to_region_2d(
268 # sctx.region, sctx.rv3d, _snap_cache.face.vmid)
272 # elem_world_co = [snp_obj.mat @ co for co in elem_co]
273 # ray_dir = constrain[1] - constrain[0]
274 # r_loc = intersect_ray_tri(*elem_world_co, ray_dir, constrain[0], False)
276 # r_loc = intersect_ray_tri(*elem_world_co, -ray_dir, constrain[0], False)
278 r_loc
= intersect_point_line(loc
, constrain
[0], constrain
[1])[0]
280 # elif v2dmid and abs(v2dmid[0] - mcursor[0]) < 10 and abs(v2dmid[1] - mcursor[1]) < 10:
289 pv_co
= main_snap_obj
.mat
@ previous_vert
.co
291 if is_increment
and increment
:
292 r_len
= round((1 / increment
) * vec
.length
) * increment
293 r_loc
= r_len
* vec
.normalized() + pv_co
297 return snp_obj
, loc
, r_loc
, r_type
, bm
, bm_geom
, r_len
299 snap_utilities
.cache
= _snap_cache