1

I have the following UV mapping:

islands

and i need to get the coordinates of the selected vertices, but only the outer ones.

So i think that the first step is to get a list of the selected vertices, then exclude the unnecessary ones, in order to have something like this:

excluded

and finally get UV coordinates for each one of them. I've tried a lot of things found on Google (the docs about these methods are quite awful), but none of what I found worked.

-I've tried:

obj.data.uv_layers.active.vertex_selection
obj.data.uv_layers.active.edge_selection

as suggested at this link, but both returns a list of 0 elements in Object mode or a list of 96 elements in Edit Mode, all False, even if the right island is fully selected (as shown in the first image).

-I've tried the code found at this link, but I got an error at the line:

bm.faces.layers.tex.verify()

the error is

AttributeError: 'BMLayerAccessFace' object has no attribute 'tex'

if I remove that line, nothing happen (the 'select' attribute is always False).

-I've also tried the code at this link, but I get a list of 0 elements (like nothing is selected), and so on (i don't remember every code or link I've tried, there are so many!)

  • I have tried two codes, vertex_selection property and your last link, and they both work correctly for me. Maybe you left the *UV Sync Selection* switch on. In this case, UV vertices cannot be selected, so there are probably no UV vertices selected. Turn it off and try those codes again. – tetii Jul 09 '23 at 01:30

2 Answers2

0

GUI settings

  • Turn of UV Sync Selection button in UV Editor
  • Select all faces of a mesh in 3D Viewport

enter image description here

Single face
This simple sample selects the UVs associated with the active face of the mesh and outputs thier coordinates. Also see Template > Python > Operator Mesh UV. enter image description here

import bpy
import bmesh

bpy.ops.uv.select_all(action='DESELECT')

obj = bpy.context.active_object me = obj.data bm = bmesh.from_edit_mesh(me)

uv_lay = bm.loops.layers.uv.verify()

print("UV coordinates of the active face:") for loop in bm.faces.active.loops: loop_uv = loop[uv_lay] loop_uv.select = True loop_uv.select_edge = True print("(%.3f, %.3f)" %loop_uv.uv[:])

bmesh.update_edit_mesh(me)

""" OUTPUT

UV coordinates of the active face: (1.000, 0.750) (1.000, 1.000) (0.800, 1.000) (0.800, 0.750) """

An island
The following example selects the UVs around the perimeter of the island containing the active face and outputs those UV coordinates. enter image description here

import bpy
import bmesh
from bpy_extras.bmesh_utils import bmesh_linked_uv_islands

bpy.ops.uv.select_all(action='DESELECT')

obj = bpy.context.object me = obj.data bm = bmesh.from_edit_mesh(me) uv_lay = bm.loops.layers.uv.active

Find a target island and it's loops

for island_faces in bmesh_linked_uv_islands(bm, uv_lay): if bm.faces.active in island_faces: break island_loops = [loop for face in island_faces for loop in face.loops]

Get border loops on the island

border_loops = [] for loop in island_loops: loops = (loop, loop.link_loop_radial_next)

if (    loops[0] == loops[1] 
        or loops[1].face not in island_faces 
        or loops[0][uv_lay].uv != loops[1].link_loop_next[uv_lay].uv 
        or loops[1][uv_lay].uv != loops[0].link_loop_next[uv_lay].uv  ):

    border_loops.append(loop)


Get border UV coordinates and select UV edges.

border_uv_coords = [0]*len(border_loops) for i, loop in enumerate(border_loops): border_uv_coords[i] = loop[uv_lay].uv loop[uv_lay].select_edge = True

Select shared UVs on the island border

for loop in border_loops: for loop in loop.vert.link_loops: if loop in island_loops: loop[uv_lay].select = True

bmesh.update_edit_mesh(me)

Output the UV coordinates on the island boundary

print(f"UV coordinates on the island boundary: {len(border_loops)}") border_uv_coords.sort(key=lambda v : v[0]) border_uv_coords.sort(key=lambda v : v[1]) for v in border_uv_coords: print("(%.3f, %.3f)" %v[:])

""" OUTPUT

UV coordinates on the island boundary: 16 (0.000, 0.500) (0.200, 0.500) (0.400, 0.500) (0.600, 0.500) (0.800, 0.500) (1.000, 0.500) (0.000, 0.750) (0.400, 0.750) (0.600, 0.750) (1.000, 0.750) (0.000, 1.000) (0.200, 1.000) (0.400, 1.000) (0.600, 1.000) (0.800, 1.000) (1.000, 1.000) """

tetii
  • 1,775
  • 1
  • 9
  • 16
0

I found a workaround, but it's not really optimized. It switches between object ad edit mode multiple times, and also context (because some function are only available in certain contextes or modes). Also, there are several loops that can surely be optimized as well. It doesn't select the outer vertices, it stores them in a list in order to use them later.

situation

The code is the following (without using BMesh):

from easybpy import *
import bpy
import bpy_extras
from bpy_extras import mesh_utils

get active object mesh data

objData = active_object().data

get current context area

area = bpy.context.area

deselect all

bpy.ops.mesh.select_all(action='DESELECT')

make sure that all the outer edges of the UV island are marked as seam

bpy.ops.uv.seams_from_islands()

switch to Object Mode

object_mode(active_object())

get UV island from active object

lstUvs = bpy_extras.mesh_utils.mesh_linked_uv_islands(objData)

switch to Edit Mode

edit_mode(active_object())

create an association list of 'edge_keys' and edges indexes

edges = objData.edges lstEdges = {ek: edges[i] for i, ek in enumerate(objData.edge_keys)}

for each UV set

for uvIsland in lstUvs:

lstCoordinates = []
lstExtVertices = []
lstLoopsIndexes = []

# analyze all the faces of the current UV island
for faceIndex in uvIsland:

    # get current face
    currentFace = objData.polygons[faceIndex]

    # loop the edge keys of this face
    for ek in currentFace.edge_keys:

        # get the edge associated to this edge key
        currentEdge = lstEdges[ek]

        # if this border is marked as seam
        if currentEdge.use_seam == True:

            # get a list of the vertices of this edge
            edgeVertices = currentEdge.vertices

            # add the vertices of this edge to the list of outer vertices
            # (only if not already in the list)
            for v in currentEdge.vertices:
                vertexAlreadyInList = False
                for vtx in lstExtVertices:
                    if v == vtx: vertexAlreadyInList = True
                if not vertexAlreadyInList:
                    lstExtVertices.append(v)

    # check the loops of this face
    for loop_index in currentFace.loop_indices:

        # check if this loop is associated with one of the external vertices
        for extVtx in lstExtVertices:
            if objData.vertices[objData.loops[loop_index].vertex_index].index == extVtx:
                lstLoopsIndexes.append(loop_index)


# get active UV map of this object
# (can be done only in object mode)                 
object_mode(active_object())
uv_layer = objData.uv_layers.active.data

# temporarly move to VIEW_3D context
old_type = area.type
area.type = 'VIEW_3D'

# for each loop
for li in lstLoopsIndexes:

    # save its coordinates, if not already in the list
    vtxUV = uv_layer[li].uv
    coordsAlreadyInList = False
    for coord in lstCoordinates:
        if coord == vtxUV:
            coordsAlreadyInList = True
    if not coordsAlreadyInList:
        # append coordinates to list
        lstCoordinates.append([vtxUV[0], vtxUV[1]])

# return to the previous context
area.type = old_type

# return to Edit mode
edit_mode(active_object())

The output: ISLAND [0.16670957207679749, 0.9996140003204346] [0.00012871802027802914, 0.9996140003204346] [0.00012876163236796856, 0.8330331444740295] [0.3332904875278473, 0.8330332040786743] [0.3332904577255249, 0.9996140599250793] [0.16670957207679749, 0.9996140003204346] [0.3332905173301697, 0.6664523482322693] [0.3332904875278473, 0.8330332040786743] [0.00012876163236796856, 0.8330331444740295] [0.0001287906925426796, 0.6664522886276245] [0.4998713731765747, 0.6664523482322693] [0.3332905173301697, 0.6664523482322693] [0.3332905173301697, 0.6664523482322693] [0.0001287906925426796, 0.6664522886276245] [0.00012883430463261902, 0.4998714029788971] [0.666452169418335, 0.49987152218818665] [0.666452169418335, 0.6664523482322693] [0.4998713731765747, 0.6664523482322693] [0.666452169418335, 0.3332907259464264] [0.666452169418335, 0.49987152218818665] [0.4998714029788971, 0.3332907259464264] [0.4998714029788971, 0.3332907259464264] [0.33329060673713684, 0.33329060673713684] [0.33329060673713684, 0.33329060673713684] [0.00012883430463261902, 0.4998714029788971] [0.0001288779021706432, 0.33329054713249207] [0.33329063653945923, 0.1667097806930542] [0.33329060673713684, 0.33329060673713684] [0.0001288779021706432, 0.33329054713249207] [0.00012892151426058263, 0.16670961678028107] [0.3332907259464264, 0.00012900872388854623] [0.33329063653945923, 0.1667097806930542] [0.16670988500118256, 0.00012889243953395635] [0.16670988500118256, 0.00012889243953395635] [0.00012892151426058263, 0.16670961678028107] [0.00012902324669994414, 0.00012871802027802914]

ISLAND [0.9998713731765747, 0.16670963168144226] [0.9998713731765747, 0.3332904875278473] [0.8332905173301697, 0.0001287761697312817] [0.9998713731765747, 0.00012871802027802914] [0.9998713731765747, 0.16670963168144226] [0.6667096614837646, 0.00012880522990599275] [0.8332905173301697, 0.0001287761697312817] [0.6667096614837646, 0.16670963168144226] [0.6667096614837646, 0.16670963168144226] [0.6667096614837646, 0.3332904577255249] [0.9998713731765747, 0.3332904875278473] [0.9998713135719299, 0.49987131357192993] [0.6667096614837646, 0.3332904577255249] [0.6667096614837646, 0.49987131357192993] [0.9998713135719299, 0.49987131357192993] [0.9998713731765747, 0.666452169418335] [0.8332905173301697, 0.666452169418335] [0.6667096614837646, 0.49987131357192993] [0.8332905173301697, 0.666452169418335] [0.6667096614837646, 0.6664522290229797]

Anyway, I just read the answer given by @tetii, I'll check it out. Thank you.