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"
24 __date__
= "24 Feb 2018"
26 from collections
import defaultdict
27 from pprint
import pprint
28 from math
import fabs
, sqrt
31 from mathutils
import Vector
40 Print message to console in debugging mode
47 def check_version(major
, minor
, _
):
52 if bpy
.app
.version
[0] == major
and bpy
.app
.version
[1] == minor
:
54 if bpy
.app
.version
[0] > major
:
56 if bpy
.app
.version
[1] > minor
:
61 def redraw_all_areas():
66 for area
in bpy
.context
.screen
.areas
:
70 def get_space(area_type
, region_type
, space_type
):
72 Get current area/region/space
79 for area
in bpy
.context
.screen
.areas
:
80 if area
.type == area_type
:
83 return (None, None, None)
84 for region
in area
.regions
:
85 if region
.type == region_type
:
87 for space
in area
.spaces
:
88 if space
.type == space_type
:
91 return (area
, region
, space
)
94 def __get_island_info(uv_layer
, islands
):
96 get information about each island
102 max_uv
= Vector((-10000000.0, -10000000.0))
103 min_uv
= Vector((10000000.0, 10000000.0))
104 ave_uv
= Vector((0.0, 0.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
:
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
)
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
)
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
139 island_info
.append(info
)
144 def __parse_island(bm
, face_idx
, faces_left
, island
,
145 face_to_verts
, vert_to_faces
):
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
]
156 for cf
in connected_faces
:
157 __parse_island(bm
, cf
, faces_left
, island
, face_to_verts
,
161 def __get_island(bm
, face_to_verts
, vert_to_faces
):
167 faces_left
= set(face_to_verts
.keys())
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)
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
:
202 uv_layer
= bm
.loops
.layers
.uv
.verify()
206 selected_faces
= [f
for f
in bm
.faces
if f
.select
]
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
)
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
):
232 for i
, p1
in enumerate(points
):
233 p2
= points
[(i
+ 1) % len(points
)]
236 a
= v1
.x
* v2
.y
- v1
.y
* v2
.x
239 return fabs(0.5 * area
)
242 def calc_polygon_3d_area(points
):
244 for i
, p1
in enumerate(points
):
245 p2
= points
[(i
+ 1) % len(points
)]
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
)
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
]
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
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
:
285 uv_layer
= bm
.loops
.layers
.uv
.verify()
287 if not bm
.faces
.layers
.tex
:
289 tex_layer
= bm
.faces
.layers
.tex
.verify()
291 sel_faces
= [f
for f
in bm
.faces
if f
.select
]
296 uvs
= [l
[uv_layer
].uv
for l
in f
.loops
]
297 f_uv_area
= calc_polygon_2d_area(uvs
)
301 img
= f
[tex_layer
].image
302 # not found, try to search from node
304 for mat
in obj
.material_slots
:
305 for node
in mat
.material
.node_tree
.nodes
:
310 if (node
.type in tex_node_types
) and node
.image
:
314 uv_area
= uv_area
+ f_uv_area
* img
.size
[0] * img
.size
[1]
319 def diff_point_to_segment(a
, b
, p
):
321 normal_ab
= ab
.normalized()
324 dist_ax
= normal_ab
.dot(ap
)
327 x
= a
+ normal_ab
* dist_ax
329 # difference between cross point and point
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_
):
340 for ll
in l_
.vert
.link_loops
:
342 lln
= ll
.link_loop_next
343 # if there is same pair, skip it
346 if (ll
in p
) and (lln
in p
):
349 # two loops must be selected
350 if ll
[uv_layer_
].select
and lln
[uv_layer_
].select
:
352 pairs_
.append([ll
, lln
])
353 if lln
not in parsed_
:
354 __get_loop_pairs_internal(lln
, pairs_
, uv_layer_
, parsed_
)
357 llp
= ll
.link_loop_prev
358 # if there is same pair, skip it
361 if (ll
in p
) and (llp
in p
):
364 # two loops must be selected
365 if ll
[uv_layer_
].select
and llp
[uv_layer_
].select
:
367 pairs_
.append([ll
, llp
])
368 if llp
not in parsed_
:
369 __get_loop_pairs_internal(llp
, pairs_
, uv_layer_
, parsed_
)
373 __get_loop_pairs_internal(l
, pairs
, uv_layer
, parsed
)
378 # sort pair by vertex
379 # (v0, v1) - (v1, v2) - (v2, v3) ....
380 def __sort_loop_pairs(uv_layer
, pairs
, closed
):
382 sorted_pairs
= [rest
[0]]
389 if p1
[0].vert
== p2
[0].vert
:
390 sorted_pairs
.insert(0, [p2
[1], p2
[0]])
393 elif p1
[0].vert
== p2
[1].vert
:
394 sorted_pairs
.insert(0, [p2
[0], p2
[1]])
402 p1
= sorted_pairs
[-1]
404 if p1
[1].vert
== p2
[0].vert
:
405 sorted_pairs
.append([p2
[0], p2
[1]])
408 elif p1
[1].vert
== p2
[1].vert
:
409 sorted_pairs
.append([p2
[1], p2
[0]])
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:
430 sorted_pairs
= tmp_pairs
[i
+ 1:]
431 sorted_pairs
.extend(tmp_pairs
[:i
+ 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
:
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
)
460 return -1 # not found
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
470 # x ---- x <- next_loop_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
:
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
:
497 # % ---- % <- next_poly_loop_pair
498 # x ---- x <- next_loop_pair
501 def __get_next_poly_loop_pair(pair
):
504 for l1
in v1
.link_loops
:
507 for l2
in v2
.link_loops
:
510 if l1
.link_loop_next
== l2
:
512 elif l1
.link_loop_prev
== l2
:
515 # no next poly loop is found
519 # get loop sequence in the same island
520 def __get_loop_sequence_internal(uv_layer
, pairs
, island_info
, closed
):
525 isl_grp
= __get_island_group_include_pair(pair
, island_info
)
527 return None, "Can not find the island or invalid island"
530 nlp
= __get_next_loop_pair(p
)
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
537 if nlpl
[uv_layer
].select
:
538 return None, "Do not select UV which does not belong to " \
543 # when face is triangle, it indicates CLOSED
544 if (len(nlp
) == 1) and closed
:
547 nplp
= __get_next_poly_loop_pair(nlp
)
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
560 if ((p1
[0] == p2
[0]) and (p1
[1] == p2
[1])) or \
561 ((p1
[0] == p2
[1]) and (p1
[1] == p2
[0])):
564 debug_print("This is a circular sequence")
568 if nlpl
[uv_layer
].select
:
569 return None, "Do not select UV which does not belong to " \
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
587 if l
[uv_layer
].select
:
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
)
599 loop_seqs
, err
= __get_loop_sequence_internal(uv_layer
, loop_pairs
,