2
$\begingroup$

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!

$\endgroup$
0
7
$\begingroup$

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.

$\endgroup$
9
  • $\begingroup$ 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. $\endgroup$ – Aleks K. Feb 18 at 19:38
  • $\begingroup$ 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. $\endgroup$ – brockmann Feb 19 at 11:18
  • $\begingroup$ 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! $\endgroup$ – Aleks K. Feb 19 at 12:30
  • 1
    $\begingroup$ 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: blender.stackexchange.com/help/how-to-ask Cheers! $\endgroup$ – brockmann Feb 19 at 15:22
  • 1
    $\begingroup$ 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 blend-exchange.giantcowfilms.com. $\endgroup$ – brockmann Feb 20 at 8:04
6
$\begingroup$

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.

$\endgroup$
10
  • $\begingroup$ 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! $\endgroup$ – Aleks K. Feb 23 at 15:16
  • 1
    $\begingroup$ Yeah, that's faster. takes 0.362339 seconds on the same model... but I feel takes longer to select the verts as well. $\endgroup$ – brockmann Feb 23 at 15:21
  • $\begingroup$ 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) $\endgroup$ – Aleks K. Feb 23 at 15:30
  • $\begingroup$ 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 $\endgroup$ – Aleks K. Feb 23 at 15:34
  • $\begingroup$ 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 $\endgroup$ – Aleks K. Feb 23 at 15:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.