3
$\begingroup$

Is there any API or method to get lists of UV island from Python script?
It seems there is no API to get lists of UV island in bpy module.

Thanks.

$\endgroup$

3 Answers 3

6
$\begingroup$

The API doesn't provide an operator or a data structure that specifies the list of UV islands as far as I know, but you can use the bpy.ops.uv.select_linked operator as a basis for a script that finds all the islands.

enter image description here

import bpy
C = bpy.context
o = bpy.data.objects[ C.object.name ]

# Go to object mode to read UV data
bpy.ops.object.mode_set( mode = 'OBJECT' )

uvLayer = o.data.uv_layers.active
uvs     = [ uvLayer.data[i] for i in range( len( uvLayer.data ) ) ]

others = uvs.copy()

islands = []

def select_island( uv ):
    uv.select = True

    bpy.ops.object.mode_set( mode = 'EDIT' )
    bpy.ops.uv.select_linked()
    bpy.ops.object.mode_set( mode = 'OBJECT' )

    return [ i for i, uv in enumerate( uvs ) if uv.select ]

for i in range( len( uvs ) ):
    bpy.ops.object.mode_set( mode = 'EDIT' )
    bpy.ops.uv.select_all( action = 'DESELECT' )
    bpy.ops.object.mode_set( mode = 'OBJECT' )

    flat = [ idx for isle in islands for idx in isle ]
    if i in flat: continue

    island = select_island( uvs[i] )

    exists = len( set( flat ).intersection( set( island ) ) )
    if island and not exists: islands.append( island )

print( islands )
print( " number of islands: ", len( islands ) )
$\endgroup$
7
  • $\begingroup$ Thanks. It looks good. However, number of islands calculated by this method is not same as number of selectable islands by Island Selection Mode. $\endgroup$
    – nutti
    Mar 15, 2016 at 0:23
  • $\begingroup$ It worked on two models I tried it with (this Icosphere and Suzanne), however sometimes I had to run it twice and enter and exit Object/Edit mode and then it would get the right number. Might be worth adding such a mode transition in the script, it might become more reliable. $\endgroup$
    – TLousky
    Mar 15, 2016 at 7:43
  • $\begingroup$ I didn't understand that it will be different results when execute many times in same polygon. $\endgroup$
    – nutti
    Mar 17, 2016 at 10:29
  • $\begingroup$ I think the issue is data updates when entering and exiting from edit mode. The UV data is not available in edit mode, but bpy.ops.uv operators are not available (out of context) in object mode. This creates a weird situation can elicit an update gap that requires mode switching to overcome. $\endgroup$
    – TLousky
    Mar 17, 2016 at 10:33
  • $\begingroup$ Is there any solution using bmesh module? I think that BMesh does not need switch edit/object. $\endgroup$
    – nutti
    Mar 19, 2016 at 11:12
3
$\begingroup$

In Blender 2.92 API I have found such a function:

import bpy_extras
islands = bpy_extras.mesh_utils.mesh_linked_uv_islands(bpy.context.active_object.data)

Current API

2.92 API

But it works only in ObjectMode at the moment. In EditMode it creates an Exception. I hope it will be fixed in future versions.

But I found one issue with this method and here is my modification to fix it: https://devtalk.blender.org/t/python-improve-uv-shells-determination-mesh-linked-uv-islands/18063 https://pastebin.com/bpChLcSC

$\endgroup$
1
  • $\begingroup$ I wonder how are the islands organized. For a tree mesh with millions of leaves where each leaf has a UV island, the amount of UV islands should equal to the amount of leaves, but bpy_extras.mesh_utils.mesh_linked_uv_islands can only pick up one, even when they're of different shape. $\endgroup$
    – June Wang
    Jul 21 at 15:30
1
$\begingroup$

I solved this problem in reference to add-on UV Align\Distribute.

import bpy
import bmesh

__face_to_verts = defaultdict(set)
__vert_to_faces = defaultdict(set)

obj = bpy.context.active_object
bm = bmesh.from_edit_mesh(obj.data)
uv_layer = bm.loops.layers.uv.verify()

selected_faces = [f for f in bm.faces if f.select]

for f in selected_faces:
    for l in f.loops:
        id = l[uv_layer].uv.to_tuple(5), l.vert.index
        self.__face_to_verts[f.index].add(id)
        self.__vert_to_faces[id].add(f.index)

uv_island_lists = self.__get_island(bm)

def __parse_island(self, bm, face_idx, faces_left, island):
    if face_idx in faces_left:
        faces_left.remove(face_idx)
        island.append({'face': bm.faces[face_idx]})
        for v in self.__face_to_verts[face_idx]:
            connected_faces = self.__vert_to_faces[v]
            if connected_faces:
                for cf in connected_faces:
                    self.__parse_island(bm, cf, faces_left, island)

def __get_island(self, bm):
    uv_island_lists = []
    faces_left = set(self.__face_to_verts.keys())
    while len(faces_left) > 0:
        current_island = []
        face_idx = list(faces_left)[0]
        self.__parse_island(bm, face_idx, faces_left, current_island)
        uv_island_lists.append(current_island)
    return uv_island_lists

It's very fast algorithm.
So, I fulfilled my requirement by this code.

https://github.com/nutti/Magic-UV/blob/develop/uv_magic_uv/muv_packuv_ops.py

Thanks to original author.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .