Update for 2.8
[blender-addons.git] / uv_magic_uv / common.py
blob6d3d9df7e3874530cb50786ced1144c698b8f347
1 # <pep8-80 compliant>
3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # ##### END GPL LICENSE BLOCK #####
21 __author__ = "Nutti <nutti.metro@gmail.com>"
22 __status__ = "production"
23 __version__ = "5.1"
24 __date__ = "24 Feb 2018"
26 from collections import defaultdict
27 from pprint import pprint
28 from math import fabs, sqrt
30 import bpy
31 from mathutils import Vector
32 import bmesh
35 DEBUG = False
38 def debug_print(*s):
39 """
40 Print message to console in debugging mode
41 """
43 if DEBUG:
44 pprint(s)
47 def check_version(major, minor, _):
48 """
49 Check blender version
50 """
52 if bpy.app.version[0] == major and bpy.app.version[1] == minor:
53 return 0
54 if bpy.app.version[0] > major:
55 return 1
56 if bpy.app.version[1] > minor:
57 return 1
58 return -1
61 def redraw_all_areas():
62 """
63 Redraw all areas
64 """
66 for area in bpy.context.screen.areas:
67 area.tag_redraw()
70 def get_space(area_type, region_type, space_type):
71 """
72 Get current area/region/space
73 """
75 area = None
76 region = None
77 space = None
79 for area in bpy.context.screen.areas:
80 if area.type == area_type:
81 break
82 else:
83 return (None, None, None)
84 for region in area.regions:
85 if region.type == region_type:
86 break
87 for space in area.spaces:
88 if space.type == space_type:
89 break
91 return (area, region, space)
94 def __get_island_info(uv_layer, islands):
95 """
96 get information about each island
97 """
99 island_info = []
100 for isl in islands:
101 info = {}
102 max_uv = Vector((-10000000.0, -10000000.0))
103 min_uv = Vector((10000000.0, 10000000.0))
104 ave_uv = Vector((0.0, 0.0))
105 num_uv = 0
106 for face in isl:
107 n = 0
108 a = Vector((0.0, 0.0))
109 ma = Vector((-10000000.0, -10000000.0))
110 mi = Vector((10000000.0, 10000000.0))
111 for l in face['face'].loops:
112 uv = l[uv_layer].uv
113 ma.x = max(uv.x, ma.x)
114 ma.y = max(uv.y, ma.y)
115 mi.x = min(uv.x, mi.x)
116 mi.y = min(uv.y, mi.y)
117 a = a + uv
118 n = n + 1
119 ave_uv = ave_uv + a
120 num_uv = num_uv + n
121 a = a / n
122 max_uv.x = max(ma.x, max_uv.x)
123 max_uv.y = max(ma.y, max_uv.y)
124 min_uv.x = min(mi.x, min_uv.x)
125 min_uv.y = min(mi.y, min_uv.y)
126 face['max_uv'] = ma
127 face['min_uv'] = mi
128 face['ave_uv'] = a
129 ave_uv = ave_uv / num_uv
131 info['center'] = ave_uv
132 info['size'] = max_uv - min_uv
133 info['num_uv'] = num_uv
134 info['group'] = -1
135 info['faces'] = isl
136 info['max'] = max_uv
137 info['min'] = min_uv
139 island_info.append(info)
141 return island_info
144 def __parse_island(bm, face_idx, faces_left, island,
145 face_to_verts, vert_to_faces):
147 Parse island
150 if face_idx in faces_left:
151 faces_left.remove(face_idx)
152 island.append({'face': bm.faces[face_idx]})
153 for v in face_to_verts[face_idx]:
154 connected_faces = vert_to_faces[v]
155 if connected_faces:
156 for cf in connected_faces:
157 __parse_island(bm, cf, faces_left, island, face_to_verts,
158 vert_to_faces)
161 def __get_island(bm, face_to_verts, vert_to_faces):
163 Get island list
166 uv_island_lists = []
167 faces_left = set(face_to_verts.keys())
168 while faces_left:
169 current_island = []
170 face_idx = list(faces_left)[0]
171 __parse_island(bm, face_idx, faces_left, current_island,
172 face_to_verts, vert_to_faces)
173 uv_island_lists.append(current_island)
175 return uv_island_lists
178 def __create_vert_face_db(faces, uv_layer):
179 # create mesh database for all faces
180 face_to_verts = defaultdict(set)
181 vert_to_faces = defaultdict(set)
182 for f in faces:
183 for l in f.loops:
184 id_ = l[uv_layer].uv.to_tuple(5), l.vert.index
185 face_to_verts[f.index].add(id_)
186 vert_to_faces[id_].add(f.index)
188 return (face_to_verts, vert_to_faces)
191 def get_island_info(obj, only_selected=True):
192 bm = bmesh.from_edit_mesh(obj.data)
193 if check_version(2, 73, 0) >= 0:
194 bm.faces.ensure_lookup_table()
196 return get_island_info_from_bmesh(bm, only_selected)
199 def get_island_info_from_bmesh(bm, only_selected=True):
200 if not bm.loops.layers.uv:
201 return None
202 uv_layer = bm.loops.layers.uv.verify()
204 # create database
205 if only_selected:
206 selected_faces = [f for f in bm.faces if f.select]
207 else:
208 selected_faces = [f for f in bm.faces]
210 return get_island_info_from_faces(bm, selected_faces, uv_layer)
213 def get_island_info_from_faces(bm, faces, uv_layer):
214 ftv, vtf = __create_vert_face_db(faces, uv_layer)
216 # Get island information
217 uv_island_lists = __get_island(bm, ftv, vtf)
218 island_info = __get_island_info(uv_layer, uv_island_lists)
220 return island_info
223 def get_uvimg_editor_board_size(area):
224 if area.spaces.active.image:
225 return area.spaces.active.image.size
227 return (255.0, 255.0)
230 def calc_polygon_2d_area(points):
231 area = 0.0
232 for i, p1 in enumerate(points):
233 p2 = points[(i + 1) % len(points)]
234 v1 = p1 - points[0]
235 v2 = p2 - points[0]
236 a = v1.x * v2.y - v1.y * v2.x
237 area = area + a
239 return fabs(0.5 * area)
242 def calc_polygon_3d_area(points):
243 area = 0.0
244 for i, p1 in enumerate(points):
245 p2 = points[(i + 1) % len(points)]
246 v1 = p1 - points[0]
247 v2 = p2 - points[0]
248 cx = v1.y * v2.z - v1.z * v2.y
249 cy = v1.z * v2.x - v1.x * v2.z
250 cz = v1.x * v2.y - v1.y * v2.x
251 a = sqrt(cx * cx + cy * cy + cz * cz)
252 area = area + a
254 return 0.5 * area
257 def measure_mesh_area(obj):
258 bm = bmesh.from_edit_mesh(obj.data)
259 if check_version(2, 73, 0) >= 0:
260 bm.verts.ensure_lookup_table()
261 bm.edges.ensure_lookup_table()
262 bm.faces.ensure_lookup_table()
264 sel_faces = [f for f in bm.faces if f.select]
266 # measure
267 mesh_area = 0.0
268 for f in sel_faces:
269 verts = [l.vert.co for l in f.loops]
270 f_mesh_area = calc_polygon_3d_area(verts)
271 mesh_area = mesh_area + f_mesh_area
273 return mesh_area
276 def measure_uv_area(obj):
277 bm = bmesh.from_edit_mesh(obj.data)
278 if check_version(2, 73, 0) >= 0:
279 bm.verts.ensure_lookup_table()
280 bm.edges.ensure_lookup_table()
281 bm.faces.ensure_lookup_table()
283 if not bm.loops.layers.uv:
284 return None
285 uv_layer = bm.loops.layers.uv.verify()
287 if not bm.faces.layers.tex:
288 return None
289 tex_layer = bm.faces.layers.tex.verify()
291 sel_faces = [f for f in bm.faces if f.select]
293 # measure
294 uv_area = 0.0
295 for f in sel_faces:
296 uvs = [l[uv_layer].uv for l in f.loops]
297 f_uv_area = calc_polygon_2d_area(uvs)
299 if not tex_layer:
300 return None
301 img = f[tex_layer].image
302 # not found, try to search from node
303 if not img:
304 for mat in obj.material_slots:
305 for node in mat.material.node_tree.nodes:
306 tex_node_types = [
307 'TEX_ENVIRONMENT',
308 'TEX_IMAGE',
310 if (node.type in tex_node_types) and node.image:
311 img = node.image
312 if not img:
313 return None
314 uv_area = uv_area + f_uv_area * img.size[0] * img.size[1]
316 return uv_area
319 def diff_point_to_segment(a, b, p):
320 ab = b - a
321 normal_ab = ab.normalized()
323 ap = p - a
324 dist_ax = normal_ab.dot(ap)
326 # cross point
327 x = a + normal_ab * dist_ax
329 # difference between cross point and point
330 xp = p - x
332 return xp, x
335 # get selected loop pair whose loops are connected each other
336 def __get_loop_pairs(l, uv_layer):
338 def __get_loop_pairs_internal(l_, pairs_, uv_layer_, parsed_):
339 parsed_.append(l_)
340 for ll in l_.vert.link_loops:
341 # forward direction
342 lln = ll.link_loop_next
343 # if there is same pair, skip it
344 found = False
345 for p in pairs_:
346 if (ll in p) and (lln in p):
347 found = True
348 break
349 # two loops must be selected
350 if ll[uv_layer_].select and lln[uv_layer_].select:
351 if not found:
352 pairs_.append([ll, lln])
353 if lln not in parsed_:
354 __get_loop_pairs_internal(lln, pairs_, uv_layer_, parsed_)
356 # backward direction
357 llp = ll.link_loop_prev
358 # if there is same pair, skip it
359 found = False
360 for p in pairs_:
361 if (ll in p) and (llp in p):
362 found = True
363 break
364 # two loops must be selected
365 if ll[uv_layer_].select and llp[uv_layer_].select:
366 if not found:
367 pairs_.append([ll, llp])
368 if llp not in parsed_:
369 __get_loop_pairs_internal(llp, pairs_, uv_layer_, parsed_)
371 pairs = []
372 parsed = []
373 __get_loop_pairs_internal(l, pairs, uv_layer, parsed)
375 return pairs
378 # sort pair by vertex
379 # (v0, v1) - (v1, v2) - (v2, v3) ....
380 def __sort_loop_pairs(uv_layer, pairs, closed):
381 rest = pairs
382 sorted_pairs = [rest[0]]
383 rest.remove(rest[0])
385 # prepend
386 while True:
387 p1 = sorted_pairs[0]
388 for p2 in rest:
389 if p1[0].vert == p2[0].vert:
390 sorted_pairs.insert(0, [p2[1], p2[0]])
391 rest.remove(p2)
392 break
393 elif p1[0].vert == p2[1].vert:
394 sorted_pairs.insert(0, [p2[0], p2[1]])
395 rest.remove(p2)
396 break
397 else:
398 break
400 # append
401 while True:
402 p1 = sorted_pairs[-1]
403 for p2 in rest:
404 if p1[1].vert == p2[0].vert:
405 sorted_pairs.append([p2[0], p2[1]])
406 rest.remove(p2)
407 break
408 elif p1[1].vert == p2[1].vert:
409 sorted_pairs.append([p2[1], p2[0]])
410 rest.remove(p2)
411 break
412 else:
413 break
415 begin_vert = sorted_pairs[0][0].vert
416 end_vert = sorted_pairs[-1][-1].vert
417 if begin_vert != end_vert:
418 return sorted_pairs, ""
419 if closed and (begin_vert == end_vert):
420 # if the sequence of UV is circular, it is ok
421 return sorted_pairs, ""
423 # if the begin vertex and the end vertex are same, search the UVs which
424 # are separated each other
425 tmp_pairs = sorted_pairs
426 for i, (p1, p2) in enumerate(zip(tmp_pairs[:-1], tmp_pairs[1:])):
427 diff = p2[0][uv_layer].uv - p1[-1][uv_layer].uv
428 if diff.length > 0.000000001:
429 # UVs are separated
430 sorted_pairs = tmp_pairs[i + 1:]
431 sorted_pairs.extend(tmp_pairs[:i + 1])
432 break
433 else:
434 p1 = tmp_pairs[0]
435 p2 = tmp_pairs[-1]
436 diff = p2[-1][uv_layer].uv - p1[0][uv_layer].uv
437 if diff.length < 0.000000001:
438 # all UVs are not separated
439 return None, "All UVs are not separted"
441 return sorted_pairs, ""
444 # get index of the island group which includes loop
445 def __get_island_group_include_loop(loop, island_info):
446 for i, isl in enumerate(island_info):
447 for f in isl['faces']:
448 for l in f['face'].loops:
449 if l == loop:
450 return i # found
452 return -1 # not found
455 # get index of the island group which includes pair.
456 # if island group is not same between loops, it will be invalid
457 def __get_island_group_include_pair(pair, island_info):
458 l1_grp = __get_island_group_include_loop(pair[0], island_info)
459 if l1_grp == -1:
460 return -1 # not found
462 for p in pair[1:]:
463 l2_grp = __get_island_group_include_loop(p, island_info)
464 if (l2_grp == -1) or (l1_grp != l2_grp):
465 return -1 # not found or invalid
467 return l1_grp
470 # x ---- x <- next_loop_pair
471 # | |
472 # o ---- o <- pair
473 def __get_next_loop_pair(pair):
474 lp = pair[0].link_loop_prev
475 if lp.vert == pair[1].vert:
476 lp = pair[0].link_loop_next
477 if lp.vert == pair[1].vert:
478 # no loop is found
479 return None
481 ln = pair[1].link_loop_next
482 if ln.vert == pair[0].vert:
483 ln = pair[1].link_loop_prev
484 if ln.vert == pair[0].vert:
485 # no loop is found
486 return None
488 # tri-face
489 if lp == ln:
490 return [lp]
492 # quad-face
493 return [lp, ln]
496 # | ---- |
497 # % ---- % <- next_poly_loop_pair
498 # x ---- x <- next_loop_pair
499 # | |
500 # o ---- o <- pair
501 def __get_next_poly_loop_pair(pair):
502 v1 = pair[0].vert
503 v2 = pair[1].vert
504 for l1 in v1.link_loops:
505 if l1 == pair[0]:
506 continue
507 for l2 in v2.link_loops:
508 if l2 == pair[1]:
509 continue
510 if l1.link_loop_next == l2:
511 return [l1, l2]
512 elif l1.link_loop_prev == l2:
513 return [l1, l2]
515 # no next poly loop is found
516 return None
519 # get loop sequence in the same island
520 def __get_loop_sequence_internal(uv_layer, pairs, island_info, closed):
521 loop_sequences = []
522 for pair in pairs:
523 seqs = [pair]
524 p = pair
525 isl_grp = __get_island_group_include_pair(pair, island_info)
526 if isl_grp == -1:
527 return None, "Can not find the island or invalid island"
529 while True:
530 nlp = __get_next_loop_pair(p)
531 if not nlp:
532 break # no more loop pair
533 nlp_isl_grp = __get_island_group_include_pair(nlp, island_info)
534 if nlp_isl_grp != isl_grp:
535 break # another island
536 for nlpl in nlp:
537 if nlpl[uv_layer].select:
538 return None, "Do not select UV which does not belong to " \
539 "the end edge"
541 seqs.append(nlp)
543 # when face is triangle, it indicates CLOSED
544 if (len(nlp) == 1) and closed:
545 break
547 nplp = __get_next_poly_loop_pair(nlp)
548 if not nplp:
549 break # no more loop pair
550 nplp_isl_grp = __get_island_group_include_pair(nplp, island_info)
551 if nplp_isl_grp != isl_grp:
552 break # another island
554 # check if the UVs are already parsed.
555 # this check is needed for the mesh which has the circular
556 # sequence of the verticies
557 matched = False
558 for p1 in seqs:
559 p2 = nplp
560 if ((p1[0] == p2[0]) and (p1[1] == p2[1])) or \
561 ((p1[0] == p2[1]) and (p1[1] == p2[0])):
562 matched = True
563 if matched:
564 debug_print("This is a circular sequence")
565 break
567 for nlpl in nplp:
568 if nlpl[uv_layer].select:
569 return None, "Do not select UV which does not belong to " \
570 "the end edge"
572 seqs.append(nplp)
574 p = nplp
576 loop_sequences.append(seqs)
577 return loop_sequences, ""
580 def get_loop_sequences(bm, uv_layer, closed=False):
581 sel_faces = [f for f in bm.faces if f.select]
583 # get candidate loops
584 cand_loops = []
585 for f in sel_faces:
586 for l in f.loops:
587 if l[uv_layer].select:
588 cand_loops.append(l)
590 if len(cand_loops) < 2:
591 return None, "More than 2 UVs must be selected"
593 first_loop = cand_loops[0]
594 isl_info = get_island_info_from_bmesh(bm, False)
595 loop_pairs = __get_loop_pairs(first_loop, uv_layer)
596 loop_pairs, err = __sort_loop_pairs(uv_layer, loop_pairs, closed)
597 if not loop_pairs:
598 return None, err
599 loop_seqs, err = __get_loop_sequence_internal(uv_layer, loop_pairs,
600 isl_info, closed)
601 if not loop_seqs:
602 return None, err
604 return loop_seqs, ""