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$ Commented 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$ Commented 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$ Commented 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$ Commented 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$ Commented 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$ Commented Jul 21, 2023 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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.