2

I have a problem that I can't solve on my own. I want to select (or in my case deselect) everything that's inside the UV 0-1 space. I have a specific workflow that I follow and all my objects are placed in a number of UV quadrants. I want to select everything that's inside the first quadrant without selecting anything else.

The bpy.ops.uv.select lets you only select a single vertex located at the given coordinates, so that's the only workaround that I have at the moment, but this could be slow and, considering the following operations, is not really an option.

The other thing I looked at is the bpy.ops.uv.select_box. Unfortunately, this one works with mouse coordinates instead of 0-1 space coordinates which makes it totally useless.

I'm still looking but kind of running out of ideas.

Thank you in advance!

Aleks K.
  • 159
  • 11

2 Answers2

7

Have a look into the python templates that come along with Blender. There is an Operator Mesh UV template you can modify to your needs. Based on that, all you would have to figure out is how to set the select state of each UV vertex you're iterating on, by searching in the docs.

import bpy
import bmesh

def main(context): obj = context.active_object me = obj.data bm = bmesh.from_edit_mesh(me)

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

# adjust uv coordinates
for face in bm.faces:
    for loop in face.loops:
        loop_uv = loop[uv_layer]
        # Select UV vertex if in certain range
        if 0 <= loop_uv.uv.x <= 1 and 0 <= loop_uv.uv.y <= 1:
            loop_uv.select = True

bmesh.update_edit_mesh(me)


class UvOperator(bpy.types.Operator): """UV Operator description""" bl_idname = "uv.simple_operator" bl_label = "Simple UV Operator"

@classmethod
def poll(cls, context):
    obj = context.active_object
    return obj and obj.type == 'MESH' and obj.mode == 'EDIT'

def execute(self, context):
    main(context)
    return {'FINISHED'}


def register(): bpy.utils.register_class(UvOperator)

def unregister(): bpy.utils.unregister_class(UvOperator)

if name == "main": register()

# test call
bpy.ops.uv.simple_operator()

enter image description here

Performance: Using the bmesh method above takes 0.433614 seconds for a vertex count of 175,979 on my machine while iterating through the object data takes 1.514 seconds.


If you want to (de-)select the vertices of the actual mesh as well (keeping both in sync), you can set the selection state per vertex of the loop you are iterating on using BMVert.select_set() call:

enter image description here Mesh vertices in Edit Mode (left), UV Vertices in the UV Editor (right)

class CUSTOM_OT_uvSelectBmesh(bpy.types.Operator):
    """Select UV and Mesh Vertices if in given range"""
    bl_idname = "uv.select_verts_bmesh"
    bl_label = "Select UV vertices (Bmesh)"
@classmethod
def poll(cls, context):
    obj = context.active_object
    return obj and obj.type == 'MESH' and obj.mode == 'EDIT'

def execute(self, context):
    min, max = (0.0, 1.0)

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

    uv_layer = bm.loops.layers.uv.verify()
    for face in bm.faces:
        for loop in face.loops:
            loop_uv = loop[uv_layer]
            # Select UV vertex if in certain range
            if min <= loop_uv.uv.x <= max and min <= loop_uv.uv.y <= max:
                loop_uv.select = True
                loop.vert.select_set(True)
            else:
                loop.vert.select_set(False)

    bmesh.update_edit_mesh(me)
    return {'FINISHED'}

Performance: Selecting the UV vertices and the actual mesh vertices using the operator above takes 0.517211 seconds for a vertex count of 175,979 on my machine.

brockmann
  • 12,613
  • 4
  • 50
  • 93
  • Yes, that's my backup plan, but this can be quite slow with higher-poly meshes. What I want to do is instead of iterating through all vertices to check if they're located inside the 0-1 space, I want to get the bounds of the space and select everything inside it. This should work really fast no matter the polygon number because it doesn't have to check the location of each one individualy. I think that if Blender has a 0-1 space, then it know what it is and has data on it and its location and bounds. If this is true, accessing this data would be the perfect thing. – Aleks K. Feb 18 '21 at 19:38
  • Go for it! Measured the CPU time of the operator and as you can see, it's pretty fast. In general, I suggest mention any concerns directly in your q, makes it easier for all of us. @AleksK. – brockmann Feb 19 '21 at 11:18
  • ohh, wow, you did a great job proving me wrong! I guess this will do the trick then and thank you a lot for your time and effort! One more thing though, I tried to use this method in a slightly different way since I need to check a deselect vertices and not faces but couldn't get it to work. It sais that verts don't have uv I I must've messed up somewhere. Do you know how to do it? If so, can you write it as a sepparate answer so that people have both options when they open this thread? Thank you again! – Aleks K. Feb 19 '21 at 12:30
  • 1
    Glad it helps. Not sure what you mean by "to check a deselect vertices and not faces" and it's hard to guess... @AleksK I'm not going to add another answer because this site is no regular forum rather a Q&A site meaning no threads. It's always one question and different approaches/answers to solve one specific problem, take the [tour] to learn about how this site works. I'd suggest ask a new question (only one per post), add some images, a blend to test with and if your question is about bpy share your code, see: https://blender.stackexchange.com/help/how-to-ask Cheers! – brockmann Feb 19 '21 at 15:22
  • It was a typo. "to check AND deselect vertices, not faces". I agree that in the question it isn't specified, but I need to deselect vertices. The answer works with faces and loops. Anyway, thank you again! I guess I really have to take the tour.. – Aleks K. Feb 19 '21 at 16:18
  • What exactly you'd like to de-select? Just the outer verts bacause they are selected for some reason? The script is selecting verts, no faces @AleksK. – brockmann Feb 19 '21 at 17:18
  • All of my meshes have an applied Solidify modifier with Rims. What I'm trying to do now is to select all the Rim edges (located inside the 3rd UV quadrant and delete them in order to be able to do some operations to the object itself (more specifically trying to use the Rip function, which for some reason breaks if there are Rim polygons). Maybe there's a better (and a lot faster) way to do this, but my main goal is to print a clean UV layout without any edges inside. 3ds Max has that option and it's part of my workflow that's been missing since I switched to Blender. – Aleks K. Feb 19 '21 at 23:25
  • 1
    Better, get it @AleksK. I'm sure there are ways to handle that case. However, it needs to be tested and discussed in a seperate Q/A. I think as far as the current question goes (selecting uv verts in 0-1 range), I have answered it so I'd suggest just ask a new question explaining in detail how you achive that in Max, add an image of your uv layout and ideally share a file ideally using https://blend-exchange.giantcowfilms.com/. – brockmann Feb 20 '21 at 08:04
  • In Max, this is something that's built-in and I don't know how it works under the hood. I'll continue searching for other, more convenient and faster, ways to achieve this result. – Aleks K. Feb 20 '21 at 09:39
  • Does the code still work as of today (blender 3.6/4.0) ? It doesn't seem to produce any error on my output log, but it doesn't seem to register any tool in the uv editor. – softyoda yoann Nov 29 '23 at 22:07
6

Via Select Box Operator

enter image description here Example in blender version 2.91,2, after running script, adding Box Select operator with range options (arbitrarily labelled Select UV Image Bounds) to UV editors select menu.

AFAICT the select box operator uses normalized image 0-1 coordinates in the UV editor, as does the cursor, totally debunking this assertion in question.

Unfortunately, this one works with mouse coordinates instead of 0-1 space coordinates which makes it totally useless.

To which case can add the select box operator to a menu in the UV editor space, set the selection to our range and exec the operator so it does not wait for input via the UI.

Appended as choice Select UV Image Bounds in the select menu of UV / Image Editor.

import bpy

def draw(self, context): layout = self.layout layout.operator_context = 'EXEC_DEFAULT' op = layout.operator('uv.select_box', text="Select UV Image Bounds") op.mode = 'SET' op.xmin = 0 op.xmax = 1

op.ymin = 0
op.ymax = 1

op.wait_for_input = False
op.pinned = False

bpy.types.IMAGE_MT_select.append(draw)

PS May notice that in GIF some UV verts are shown selected outside the 0-1 range when in sync selection mode. This is because a vert can have many uvs (1 per loop) and need only one assoc. UV in range for all to be selected.

Via numpy

Run this script in object mode.

Can do this pretty quickly using the foreach_get and foreach_set methods to quickly get and set data.

import bpy
import numpy as np

ob = bpy.context.object me = ob.data uv_layer = me.uv_layers.active

get uv values

uvs = np.empty((2 * len(me.loops), 1)) uv_layer.data.foreach_get("uv", uvs)

select

u, v = uvs.reshape((-1, 2)).T uv_layer.data.foreach_set( "select", np.logical_and( (u >= 0) & (u <= 1), (v >= 0) & (v <= 1) ) )

PS. Relatively new to numpy. Feel there could be a quicker way still to create the True / False mask directly from uvs above. Please advise re any tips in this direction.

batFINGER
  • 84,216
  • 10
  • 108
  • 233
  • Thank you a lot for suggesting this, I'll definitely try it since the other method actually didn't work for me. It just doesn't select the faces or the vertices, even when I copy-paste the code (I don't know why, either I'm doing something wrong or it has something to do with the versions (I'm using 2.91)). After trying this I'll let you know if it works! – Aleks K. Feb 23 '21 at 15:16
  • 1
    Yeah, that's faster. takes 0.362339 seconds on the same model... but I feel takes longer to select the verts as well. – brockmann Feb 23 '21 at 15:21
  • Can anyone tell me what I'm doing wrong? It doesn't work.. when executing the line "uv_layer.data.foreach_get("uv", uvs)" it gives a RuntimeError: internal error setting the array. After that I get "Error: Array length mismatch (expected 0, got 110784) – Aleks K. Feb 23 '21 at 15:30
  • Ohh, I see. But now it doesn't give any error but it also doesn't select anything. It's just like it didn't do anything – Aleks K. Feb 23 '21 at 15:34
  • I do. I tried this both in version 2.91 and in 2.83. I set it to a button in the 3d view. I import an object, go to edit mode -> vert selection -> select all then go to object mode and run the script. Tried both this way and without selecting anything. I could share a .py file with everything written if that would help – Aleks K. Feb 23 '21 at 15:43
  • Also, as mentioned above, using the other method everything works up until selecting the faces/vertices. It just doesn't select anything. I tried it line by line and everything works fine but it doesn't select anything. – Aleks K. Feb 23 '21 at 15:45
  • I did register it and actually just found where the problem lies.. It's the UV sync mode. This selection method only works with the UV sync mode DISABLED and I did all my tests with it ENABLED. – Aleks K. Feb 23 '21 at 16:06
  • Great, thank you! – Aleks K. Feb 23 '21 at 16:31
  • I'll keep that in mind for the next time! Thank you a lot for helping me out with this thing! I think a dedicated Discord group would be best for tough scenarios or cases where it's hard to guess why something doesn't work. It's not over yet, though.. I still haven't completed my script, this is just one step that tripped me. I also know that this script won't work for all scenarios so after I'm done with it I'll immediately start looking for other ways to achieve the same result (hopefuly faster ways with less woraround-type of solutions) . – Aleks K. Feb 23 '21 at 18:05
  • So I got it to work with the numpy method. I ran hundreds of tests and all was fine. Suddenly it started giving me a Runtime error: internal error setting the array.. Any ideas? – Aleks K. Feb 24 '21 at 14:25