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__
= "31 Jul 2019"
26 from collections
import defaultdict
27 from pprint
import pprint
28 from math
import fabs
, sqrt
32 from mathutils
import Vector
35 from .utils
import compatibility
as compat
41 def is_console_mode():
42 if "MUV_CONSOLE_MODE" not in os
.environ
:
44 return os
.environ
["MUV_CONSOLE_MODE"] == "true"
51 def enable_debugg_mode():
52 # pylint: disable=W0603
57 def disable_debug_mode():
58 # pylint: disable=W0603
65 Print message to console in debugging mode
72 def check_version(major
, minor
, _
):
77 if bpy
.app
.version
[0] == major
and bpy
.app
.version
[1] == minor
:
79 if bpy
.app
.version
[0] > major
:
81 if bpy
.app
.version
[1] > minor
:
86 def redraw_all_areas():
91 for area
in bpy
.context
.screen
.areas
:
95 def get_space(area_type
, region_type
, space_type
):
97 Get current area/region/space
104 for area
in bpy
.context
.screen
.areas
:
105 if area
.type == area_type
:
108 return (None, None, None)
109 for region
in area
.regions
:
110 if region
.type == region_type
:
111 if compat
.check_version(2, 80, 0) >= 0:
112 if region
.width
<= 1 or region
.height
<= 1:
116 return (area
, None, None)
117 for space
in area
.spaces
:
118 if space
.type == space_type
:
121 return (area
, region
, None)
123 return (area
, region
, space
)
126 def mouse_on_region(event
, area_type
, region_type
):
127 pos
= Vector((event
.mouse_x
, event
.mouse_y
))
129 _
, region
, _
= get_space(area_type
, region_type
, "")
133 if (pos
.x
> region
.x
) and (pos
.x
< region
.x
+ region
.width
) and \
134 (pos
.y
> region
.y
) and (pos
.y
< region
.y
+ region
.height
):
140 def mouse_on_area(event
, area_type
):
141 pos
= Vector((event
.mouse_x
, event
.mouse_y
))
143 area
, _
, _
= get_space(area_type
, "", "")
147 if (pos
.x
> area
.x
) and (pos
.x
< area
.x
+ area
.width
) and \
148 (pos
.y
> area
.y
) and (pos
.y
< area
.y
+ area
.height
):
154 def mouse_on_regions(event
, area_type
, regions
):
155 if not mouse_on_area(event
, area_type
):
158 for region
in regions
:
159 result
= mouse_on_region(event
, area_type
, region
)
166 def create_bmesh(obj
):
167 bm
= bmesh
.from_edit_mesh(obj
.data
)
168 if check_version(2, 73, 0) >= 0:
169 bm
.faces
.ensure_lookup_table()
174 def create_new_uv_map(obj
, name
=None):
175 uv_maps_old
= {l
.name
for l
in obj
.data
.uv_layers
}
176 bpy
.ops
.mesh
.uv_texture_add()
177 uv_maps_new
= {l
.name
for l
in obj
.data
.uv_layers
}
178 diff
= uv_maps_new
- uv_maps_old
181 return None # no more UV maps can not be created
184 new
= obj
.data
.uv_layers
[list(diff
)[0]]
191 def __get_island_info(uv_layer
, islands
):
193 get information about each island
199 max_uv
= Vector((-10000000.0, -10000000.0))
200 min_uv
= Vector((10000000.0, 10000000.0))
201 ave_uv
= Vector((0.0, 0.0))
205 a
= Vector((0.0, 0.0))
206 ma
= Vector((-10000000.0, -10000000.0))
207 mi
= Vector((10000000.0, 10000000.0))
208 for l
in face
['face'].loops
:
210 ma
.x
= max(uv
.x
, ma
.x
)
211 ma
.y
= max(uv
.y
, ma
.y
)
212 mi
.x
= min(uv
.x
, mi
.x
)
213 mi
.y
= min(uv
.y
, mi
.y
)
219 max_uv
.x
= max(ma
.x
, max_uv
.x
)
220 max_uv
.y
= max(ma
.y
, max_uv
.y
)
221 min_uv
.x
= min(mi
.x
, min_uv
.x
)
222 min_uv
.y
= min(mi
.y
, min_uv
.y
)
226 ave_uv
= ave_uv
/ num_uv
228 info
['center'] = ave_uv
229 info
['size'] = max_uv
- min_uv
230 info
['num_uv'] = num_uv
236 island_info
.append(info
)
241 def __parse_island(bm
, face_idx
, faces_left
, island
,
242 face_to_verts
, vert_to_faces
):
247 if face_idx
in faces_left
:
248 faces_left
.remove(face_idx
)
249 island
.append({'face': bm
.faces
[face_idx
]})
250 for v
in face_to_verts
[face_idx
]:
251 connected_faces
= vert_to_faces
[v
]
253 for cf
in connected_faces
:
254 __parse_island(bm
, cf
, faces_left
, island
, face_to_verts
,
258 def __get_island(bm
, face_to_verts
, vert_to_faces
):
264 faces_left
= set(face_to_verts
.keys())
267 face_idx
= list(faces_left
)[0]
268 __parse_island(bm
, face_idx
, faces_left
, current_island
,
269 face_to_verts
, vert_to_faces
)
270 uv_island_lists
.append(current_island
)
272 return uv_island_lists
275 def __create_vert_face_db(faces
, uv_layer
):
276 # create mesh database for all faces
277 face_to_verts
= defaultdict(set)
278 vert_to_faces
= defaultdict(set)
281 id_
= l
[uv_layer
].uv
.to_tuple(5), l
.vert
.index
282 face_to_verts
[f
.index
].add(id_
)
283 vert_to_faces
[id_
].add(f
.index
)
285 return (face_to_verts
, vert_to_faces
)
288 def get_island_info(obj
, only_selected
=True):
289 bm
= bmesh
.from_edit_mesh(obj
.data
)
290 if check_version(2, 73, 0) >= 0:
291 bm
.faces
.ensure_lookup_table()
293 return get_island_info_from_bmesh(bm
, only_selected
)
296 def get_island_info_from_bmesh(bm
, only_selected
=True):
297 if not bm
.loops
.layers
.uv
:
299 uv_layer
= bm
.loops
.layers
.uv
.verify()
303 selected_faces
= [f
for f
in bm
.faces
if f
.select
]
305 selected_faces
= [f
for f
in bm
.faces
]
307 return get_island_info_from_faces(bm
, selected_faces
, uv_layer
)
310 def get_island_info_from_faces(bm
, faces
, uv_layer
):
311 ftv
, vtf
= __create_vert_face_db(faces
, uv_layer
)
313 # Get island information
314 uv_island_lists
= __get_island(bm
, ftv
, vtf
)
315 island_info
= __get_island_info(uv_layer
, uv_island_lists
)
320 def get_uvimg_editor_board_size(area
):
321 if area
.spaces
.active
.image
:
322 return area
.spaces
.active
.image
.size
324 return (255.0, 255.0)
327 def calc_polygon_2d_area(points
):
329 for i
, p1
in enumerate(points
):
330 p2
= points
[(i
+ 1) % len(points
)]
333 a
= v1
.x
* v2
.y
- v1
.y
* v2
.x
336 return fabs(0.5 * area
)
339 def calc_polygon_3d_area(points
):
341 for i
, p1
in enumerate(points
):
342 p2
= points
[(i
+ 1) % len(points
)]
345 cx
= v1
.y
* v2
.z
- v1
.z
* v2
.y
346 cy
= v1
.z
* v2
.x
- v1
.x
* v2
.z
347 cz
= v1
.x
* v2
.y
- v1
.y
* v2
.x
348 a
= sqrt(cx
* cx
+ cy
* cy
+ cz
* cz
)
354 def measure_mesh_area(obj
):
355 bm
= bmesh
.from_edit_mesh(obj
.data
)
356 if check_version(2, 73, 0) >= 0:
357 bm
.verts
.ensure_lookup_table()
358 bm
.edges
.ensure_lookup_table()
359 bm
.faces
.ensure_lookup_table()
361 sel_faces
= [f
for f
in bm
.faces
if f
.select
]
366 verts
= [l
.vert
.co
for l
in f
.loops
]
367 f_mesh_area
= calc_polygon_3d_area(verts
)
368 mesh_area
= mesh_area
+ f_mesh_area
373 def find_texture_layer(bm
):
374 if check_version(2, 80, 0) >= 0:
376 if bm
.faces
.layers
.tex
is None:
379 return bm
.faces
.layers
.tex
.verify()
382 def find_texture_nodes(obj
):
384 for mat
in obj
.material_slots
:
387 if not mat
.material
.node_tree
:
389 for node
in mat
.material
.node_tree
.nodes
:
394 if node
.type not in tex_node_types
:
403 def find_image(obj
, face
=None, tex_layer
=None):
404 images
= find_images(obj
, face
, tex_layer
)
407 raise RuntimeError("Find more than 2 images")
414 def find_images(obj
, face
=None, tex_layer
=None):
417 # try to find from texture_layer
418 if tex_layer
and face
:
419 if face
[tex_layer
].image
is not None:
420 images
.append(face
[tex_layer
].image
)
422 # not found, then try to search from node
424 nodes
= find_texture_nodes(obj
)
426 images
.append(n
.image
)
431 def measure_uv_area(obj
, method
='FIRST', tex_size
=None):
432 bm
= bmesh
.from_edit_mesh(obj
.data
)
433 if check_version(2, 73, 0) >= 0:
434 bm
.verts
.ensure_lookup_table()
435 bm
.edges
.ensure_lookup_table()
436 bm
.faces
.ensure_lookup_table()
438 if not bm
.loops
.layers
.uv
:
440 uv_layer
= bm
.loops
.layers
.uv
.verify()
442 tex_layer
= find_texture_layer(bm
)
444 sel_faces
= [f
for f
in bm
.faces
if f
.select
]
449 uvs
= [l
[uv_layer
].uv
for l
in f
.loops
]
450 f_uv_area
= calc_polygon_2d_area(uvs
)
453 if method
== 'USER_SPECIFIED' and tex_size
is not None:
455 # first texture if there are more than 2 textures assigned
457 elif method
== 'FIRST':
458 img
= find_image(obj
, f
, tex_layer
)
459 # can not find from node, so we can not get texture size
463 # average texture size
464 elif method
== 'AVERAGE':
465 imgs
= find_images(obj
, f
, tex_layer
)
469 img_size_total
= [0.0, 0.0]
471 img_size_total
= [img_size_total
[0] + img
.size
[0],
472 img_size_total
[1] + img
.size
[1]]
473 img_size
= [img_size_total
[0] / len(imgs
),
474 img_size_total
[1] / len(imgs
)]
476 elif method
== 'MAX':
477 imgs
= find_images(obj
, f
, tex_layer
)
481 img_size_max
= [-99999999.0, -99999999.0]
483 img_size_max
= [max(img_size_max
[0], img
.size
[0]),
484 max(img_size_max
[1], img
.size
[1])]
485 img_size
= img_size_max
487 elif method
== 'MIN':
488 imgs
= find_images(obj
, f
, tex_layer
)
492 img_size_min
= [99999999.0, 99999999.0]
494 img_size_min
= [min(img_size_min
[0], img
.size
[0]),
495 min(img_size_min
[1], img
.size
[1])]
496 img_size
= img_size_min
498 raise RuntimeError("Unexpected method: {}".format(method
))
500 uv_area
= uv_area
+ f_uv_area
* img_size
[0] * img_size
[1]
505 def diff_point_to_segment(a
, b
, p
):
507 normal_ab
= ab
.normalized()
510 dist_ax
= normal_ab
.dot(ap
)
513 x
= a
+ normal_ab
* dist_ax
515 # difference between cross point and point
521 # get selected loop pair whose loops are connected each other
522 def __get_loop_pairs(l
, uv_layer
):
524 def __get_loop_pairs_internal(l_
, pairs_
, uv_layer_
, parsed_
):
526 for ll
in l_
.vert
.link_loops
:
528 lln
= ll
.link_loop_next
529 # if there is same pair, skip it
532 if (ll
in p
) and (lln
in p
):
535 # two loops must be selected
536 if ll
[uv_layer_
].select
and lln
[uv_layer_
].select
:
538 pairs_
.append([ll
, lln
])
539 if lln
not in parsed_
:
540 __get_loop_pairs_internal(lln
, pairs_
, uv_layer_
, parsed_
)
543 llp
= ll
.link_loop_prev
544 # if there is same pair, skip it
547 if (ll
in p
) and (llp
in p
):
550 # two loops must be selected
551 if ll
[uv_layer_
].select
and llp
[uv_layer_
].select
:
553 pairs_
.append([ll
, llp
])
554 if llp
not in parsed_
:
555 __get_loop_pairs_internal(llp
, pairs_
, uv_layer_
, parsed_
)
559 __get_loop_pairs_internal(l
, pairs
, uv_layer
, parsed
)
564 # sort pair by vertex
565 # (v0, v1) - (v1, v2) - (v2, v3) ....
566 def __sort_loop_pairs(uv_layer
, pairs
, closed
):
568 sorted_pairs
= [rest
[0]]
575 if p1
[0].vert
== p2
[0].vert
:
576 sorted_pairs
.insert(0, [p2
[1], p2
[0]])
579 elif p1
[0].vert
== p2
[1].vert
:
580 sorted_pairs
.insert(0, [p2
[0], p2
[1]])
588 p1
= sorted_pairs
[-1]
590 if p1
[1].vert
== p2
[0].vert
:
591 sorted_pairs
.append([p2
[0], p2
[1]])
594 elif p1
[1].vert
== p2
[1].vert
:
595 sorted_pairs
.append([p2
[1], p2
[0]])
601 begin_vert
= sorted_pairs
[0][0].vert
602 end_vert
= sorted_pairs
[-1][-1].vert
603 if begin_vert
!= end_vert
:
604 return sorted_pairs
, ""
605 if closed
and (begin_vert
== end_vert
):
606 # if the sequence of UV is circular, it is ok
607 return sorted_pairs
, ""
609 # if the begin vertex and the end vertex are same, search the UVs which
610 # are separated each other
611 tmp_pairs
= sorted_pairs
612 for i
, (p1
, p2
) in enumerate(zip(tmp_pairs
[:-1], tmp_pairs
[1:])):
613 diff
= p2
[0][uv_layer
].uv
- p1
[-1][uv_layer
].uv
614 if diff
.length
> 0.000000001:
616 sorted_pairs
= tmp_pairs
[i
+ 1:]
617 sorted_pairs
.extend(tmp_pairs
[:i
+ 1])
622 diff
= p2
[-1][uv_layer
].uv
- p1
[0][uv_layer
].uv
623 if diff
.length
< 0.000000001:
624 # all UVs are not separated
625 return None, "All UVs are not separated"
627 return sorted_pairs
, ""
630 # get index of the island group which includes loop
631 def __get_island_group_include_loop(loop
, island_info
):
632 for i
, isl
in enumerate(island_info
):
633 for f
in isl
['faces']:
634 for l
in f
['face'].loops
:
638 return -1 # not found
641 # get index of the island group which includes pair.
642 # if island group is not same between loops, it will be invalid
643 def __get_island_group_include_pair(pair
, island_info
):
644 l1_grp
= __get_island_group_include_loop(pair
[0], island_info
)
646 return -1 # not found
649 l2_grp
= __get_island_group_include_loop(p
, island_info
)
650 if (l2_grp
== -1) or (l1_grp
!= l2_grp
):
651 return -1 # not found or invalid
656 # x ---- x <- next_loop_pair
659 def __get_next_loop_pair(pair
):
660 lp
= pair
[0].link_loop_prev
661 if lp
.vert
== pair
[1].vert
:
662 lp
= pair
[0].link_loop_next
663 if lp
.vert
== pair
[1].vert
:
667 ln
= pair
[1].link_loop_next
668 if ln
.vert
== pair
[0].vert
:
669 ln
= pair
[1].link_loop_prev
670 if ln
.vert
== pair
[0].vert
:
683 # % ---- % <- next_poly_loop_pair
684 # x ---- x <- next_loop_pair
687 def __get_next_poly_loop_pair(pair
):
690 for l1
in v1
.link_loops
:
693 for l2
in v2
.link_loops
:
696 if l1
.link_loop_next
== l2
:
698 elif l1
.link_loop_prev
== l2
:
701 # no next poly loop is found
705 # get loop sequence in the same island
706 def __get_loop_sequence_internal(uv_layer
, pairs
, island_info
, closed
):
711 isl_grp
= __get_island_group_include_pair(pair
, island_info
)
713 return None, "Can not find the island or invalid island"
716 nlp
= __get_next_loop_pair(p
)
718 break # no more loop pair
719 nlp_isl_grp
= __get_island_group_include_pair(nlp
, island_info
)
720 if nlp_isl_grp
!= isl_grp
:
721 break # another island
723 if nlpl
[uv_layer
].select
:
724 return None, "Do not select UV which does not belong to " \
729 # when face is triangle, it indicates CLOSED
730 if (len(nlp
) == 1) and closed
:
733 nplp
= __get_next_poly_loop_pair(nlp
)
735 break # no more loop pair
736 nplp_isl_grp
= __get_island_group_include_pair(nplp
, island_info
)
737 if nplp_isl_grp
!= isl_grp
:
738 break # another island
740 # check if the UVs are already parsed.
741 # this check is needed for the mesh which has the circular
742 # sequence of the vertices
746 if ((p1
[0] == p2
[0]) and (p1
[1] == p2
[1])) or \
747 ((p1
[0] == p2
[1]) and (p1
[1] == p2
[0])):
750 debug_print("This is a circular sequence")
754 if nlpl
[uv_layer
].select
:
755 return None, "Do not select UV which does not belong to " \
762 loop_sequences
.append(seqs
)
763 return loop_sequences
, ""
766 def get_loop_sequences(bm
, uv_layer
, closed
=False):
767 sel_faces
= [f
for f
in bm
.faces
if f
.select
]
769 # get candidate loops
773 if l
[uv_layer
].select
:
776 if len(cand_loops
) < 2:
777 return None, "More than 2 UVs must be selected"
779 first_loop
= cand_loops
[0]
780 isl_info
= get_island_info_from_bmesh(bm
, False)
781 loop_pairs
= __get_loop_pairs(first_loop
, uv_layer
)
782 loop_pairs
, err
= __sort_loop_pairs(uv_layer
, loop_pairs
, closed
)
785 loop_seqs
, err
= __get_loop_sequence_internal(uv_layer
, loop_pairs
,
793 def __is_segment_intersect(start1
, end1
, start2
, end2
):
799 d1
= -(a1
* start1
.x
+ b1
* start1
.y
)
803 d2
= -(a2
* start2
.x
+ b2
* start2
.y
)
805 seg1_line2_start
= a2
* start1
.x
+ b2
* start1
.y
+ d2
806 seg1_line2_end
= a2
* end1
.x
+ b2
* end1
.y
+ d2
808 seg2_line1_start
= a1
* start2
.x
+ b1
* start2
.y
+ d1
809 seg2_line1_end
= a1
* end2
.x
+ b1
* end2
.y
+ d1
811 if (seg1_line2_start
* seg1_line2_end
>= 0) or \
812 (seg2_line1_start
* seg2_line1_end
>= 0):
815 u
= seg1_line2_start
/ (seg1_line2_start
- seg1_line2_end
)
816 out
= start1
+ u
* seg1
822 def __init__(self
, arr
):
823 self
.__buffer
= arr
.copy()
827 return repr(self
.__buffer
)
830 return len(self
.__buffer
)
832 def insert(self
, val
, offset
=0):
833 self
.__buffer
.insert(self
.__pointer
+ offset
, val
)
836 return self
.__buffer
[0]
839 return self
.__buffer
[-1]
841 def get(self
, offset
=0):
842 size
= len(self
.__buffer
)
843 val
= self
.__buffer
[(self
.__pointer
+ offset
) % size
]
847 size
= len(self
.__buffer
)
848 self
.__pointer
= (self
.__pointer
+ 1) % size
855 idx
= self
.__buffer
.index(obj
)
858 return self
.__buffer
[idx
]
860 def find_and_next(self
, obj
):
861 size
= len(self
.__buffer
)
862 idx
= self
.__buffer
.index(obj
)
863 self
.__pointer
= (idx
+ 1) % size
865 def find_and_set(self
, obj
):
866 idx
= self
.__buffer
.index(obj
)
870 return self
.__buffer
.copy()
873 self
.__buffer
.reverse()
877 # clip: reference polygon
878 # subject: tested polygon
879 def __do_weiler_atherton_cliping(clip
, subject
, uv_layer
, mode
):
881 clip_uvs
= RingBuffer([l
[uv_layer
].uv
.copy() for l
in clip
.loops
])
882 if __is_polygon_flipped(clip_uvs
):
884 subject_uvs
= RingBuffer([l
[uv_layer
].uv
.copy() for l
in subject
.loops
])
885 if __is_polygon_flipped(subject_uvs
):
886 subject_uvs
.reverse()
888 debug_print("===== Clip UV List =====")
889 debug_print(clip_uvs
)
890 debug_print("===== Subject UV List =====")
891 debug_print(subject_uvs
)
893 # check if clip and subject is overlapped completely
894 if __is_polygon_same(clip_uvs
, subject_uvs
):
895 polygons
= [subject_uvs
.as_list()]
896 debug_print("===== Polygons Overlapped Completely =====")
897 debug_print(polygons
)
898 return True, polygons
900 # check if subject is in clip
901 if __is_points_in_polygon(subject_uvs
, clip_uvs
):
902 polygons
= [subject_uvs
.as_list()]
903 return True, polygons
905 # check if clip is in subject
906 if __is_points_in_polygon(clip_uvs
, subject_uvs
):
907 polygons
= [subject_uvs
.as_list()]
908 return True, polygons
910 # check if clip and subject is overlapped partially
915 uv_start1
= clip_uvs
.get()
916 uv_end1
= clip_uvs
.get(1)
917 uv_start2
= subject_uvs
.get()
918 uv_end2
= subject_uvs
.get(1)
919 intersected
, point
= __is_segment_intersect(uv_start1
, uv_end1
,
922 clip_uvs
.insert(point
, 1)
923 subject_uvs
.insert(point
, 1)
924 intersections
.append([point
,
925 [clip_uvs
.get(), clip_uvs
.get(1)]])
927 if subject_uvs
.get() == subject_uvs
.head():
930 if clip_uvs
.get() == clip_uvs
.head():
933 debug_print("===== Intersection List =====")
934 debug_print(intersections
)
936 # no intersection, so subject and clip is not overlapped
937 if not intersections
:
940 def get_intersection_pair(intersects
, key
):
941 for sect
in intersects
:
947 # make enter/exit pair
949 subject_entering
= []
953 intersect_uv_list
= []
955 pair
= get_intersection_pair(intersections
, subject_uvs
.get())
957 sub
= subject_uvs
.get(1) - subject_uvs
.get(-1)
958 inter
= pair
[1] - pair
[0]
959 cross
= sub
.x
* inter
.y
- inter
.x
* sub
.y
961 subject_entering
.append(subject_uvs
.get())
962 clip_exiting
.append(subject_uvs
.get())
964 subject_exiting
.append(subject_uvs
.get())
965 clip_entering
.append(subject_uvs
.get())
966 intersect_uv_list
.append(subject_uvs
.get())
969 if subject_uvs
.get() == subject_uvs
.head():
972 debug_print("===== Enter List =====")
973 debug_print(clip_entering
)
974 debug_print(subject_entering
)
975 debug_print("===== Exit List =====")
976 debug_print(clip_exiting
)
977 debug_print(subject_exiting
)
979 # for now, can't handle the situation when fulfill all below conditions
980 # * two faces have common edge
981 # * each face is intersected
982 # * Show Mode is "Part"
983 # so for now, ignore this situation
984 if len(subject_entering
) != len(subject_exiting
):
986 polygons
= [subject_uvs
.as_list()]
987 return True, polygons
990 def traverse(current_list
, entering
, exiting
, p
, current
, other_list
):
991 result
= current_list
.find(current
)
994 if result
!= current
:
995 print("Internal Error")
998 print("Internal Error: No exiting UV")
1002 if entering
.count(current
) >= 1:
1003 entering
.remove(current
)
1005 current_list
.find_and_next(current
)
1006 current
= current_list
.get()
1010 while exiting
.count(current
) == 0:
1011 p
.append(current
.copy())
1012 current_list
.find_and_next(current
)
1013 current
= current_list
.get()
1020 print("Internal Error: Infinite loop")
1024 p
.append(current
.copy())
1025 exiting
.remove(current
)
1027 other_list
.find_and_set(current
)
1028 return other_list
.get()
1032 current_uv_list
= subject_uvs
1033 other_uv_list
= clip_uvs
1034 current_entering
= subject_entering
1035 current_exiting
= subject_exiting
1038 current_uv
= current_entering
[0]
1041 current_uv
= traverse(current_uv_list
, current_entering
,
1042 current_exiting
, poly
, current_uv
, other_uv_list
)
1044 if current_uv
is None:
1047 if current_uv_list
== subject_uvs
:
1048 current_uv_list
= clip_uvs
1049 other_uv_list
= subject_uvs
1050 current_entering
= clip_entering
1051 current_exiting
= clip_exiting
1052 debug_print("-- Next: Clip --")
1054 current_uv_list
= subject_uvs
1055 other_uv_list
= clip_uvs
1056 current_entering
= subject_entering
1057 current_exiting
= subject_exiting
1058 debug_print("-- Next: Subject --")
1060 debug_print(clip_entering
)
1061 debug_print(clip_exiting
)
1062 debug_print(subject_entering
)
1063 debug_print(subject_exiting
)
1065 if not clip_entering
and not clip_exiting \
1066 and not subject_entering
and not subject_exiting
:
1069 polygons
.append(poly
)
1071 debug_print("===== Polygons Overlapped Partially =====")
1072 debug_print(polygons
)
1074 return True, polygons
1077 def __is_polygon_flipped(points
):
1079 for i
in range(len(points
)):
1081 uv2
= points
.get(i
+ 1)
1082 a
= uv1
.x
* uv2
.y
- uv1
.y
* uv2
.x
1090 def __is_point_in_polygon(point
, subject_points
):
1092 for i
in range(len(subject_points
)):
1093 uv_start1
= subject_points
.get(i
)
1094 uv_end1
= subject_points
.get(i
+ 1)
1096 uv_end2
= Vector((1000000.0, point
.y
))
1097 intersected
, _
= __is_segment_intersect(uv_start1
, uv_end1
,
1105 def __is_points_in_polygon(points
, subject_points
):
1106 for i
in range(len(points
)):
1107 internal
= __is_point_in_polygon(points
.get(i
), subject_points
)
1114 def get_overlapped_uv_info(bm
, faces
, uv_layer
, mode
):
1115 # at first, check island overlapped
1116 isl
= get_island_info_from_faces(bm
, faces
, uv_layer
)
1117 overlapped_isl_pairs
= []
1118 for i
, i1
in enumerate(isl
):
1119 for i2
in isl
[i
+ 1:]:
1120 if (i1
["max"].x
< i2
["min"].x
) or (i2
["max"].x
< i1
["min"].x
) or \
1121 (i1
["max"].y
< i2
["min"].y
) or (i2
["max"].y
< i1
["min"].y
):
1123 overlapped_isl_pairs
.append([i1
, i2
])
1125 # next, check polygon overlapped
1127 for oip
in overlapped_isl_pairs
:
1128 for clip
in oip
[0]["faces"]:
1129 f_clip
= clip
["face"]
1130 for subject
in oip
[1]["faces"]:
1131 f_subject
= subject
["face"]
1133 # fast operation, apply bounding box algorithm
1134 if (clip
["max_uv"].x
< subject
["min_uv"].x
) or \
1135 (subject
["max_uv"].x
< clip
["min_uv"].x
) or \
1136 (clip
["max_uv"].y
< subject
["min_uv"].y
) or \
1137 (subject
["max_uv"].y
< clip
["min_uv"].y
):
1140 # slow operation, apply Weiler-Atherton cliping algorithm
1141 result
, polygons
= __do_weiler_atherton_cliping(f_clip
,
1145 subject_uvs
= [l
[uv_layer
].uv
.copy()
1146 for l
in f_subject
.loops
]
1147 overlapped_uvs
.append({"clip_face": f_clip
,
1148 "subject_face": f_subject
,
1149 "subject_uvs": subject_uvs
,
1150 "polygons": polygons
})
1152 return overlapped_uvs
1155 def get_flipped_uv_info(faces
, uv_layer
):
1158 polygon
= RingBuffer([l
[uv_layer
].uv
.copy() for l
in f
.loops
])
1159 if __is_polygon_flipped(polygon
):
1160 uvs
= [l
[uv_layer
].uv
.copy() for l
in f
.loops
]
1161 flipped_uvs
.append({"face": f
, "uvs": uvs
,
1162 "polygons": [polygon
.as_list()]})
1167 def __is_polygon_same(points1
, points2
):
1168 if len(points1
) != len(points2
):
1171 pts1
= points1
.as_list()
1172 pts2
= points2
.as_list()
1177 if diff
.length
< 0.0000001: