expre-gen/blender/3.4/scripts/addons/magic_uv/op/select_uv.py
2025-10-17 09:47:23 +08:00

348 lines
11 KiB
Python

# SPDX-License-Identifier: GPL-2.0-or-later
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "6.6"
__date__ = "22 Apr 2022"
import bpy
from bpy.props import BoolProperty, FloatProperty, EnumProperty
import bmesh
from .. import common
from ..utils.bl_class_registry import BlClassRegistry
from ..utils.property_class_registry import PropertyClassRegistry
from ..utils import compatibility as compat
def _is_valid_context(context):
# 'IMAGE_EDITOR' and 'VIEW_3D' space is allowed to execute.
# If 'View_3D' space is not allowed, you can't find option in Tool-Shelf
# after the execution
if not common.is_valid_space(context, ['IMAGE_EDITOR', 'VIEW_3D']):
return False
objs = common.get_uv_editable_objects(context)
if not objs:
return False
# only edit mode is allowed to execute
if context.object.mode != 'EDIT':
return False
return True
@PropertyClassRegistry()
class _Properties:
idname = "select_uv"
@classmethod
def init_props(cls, scene):
scene.muv_select_uv_enabled = BoolProperty(
name="Select UV Enabled",
description="Select UV is enabled",
default=False
)
scene.muv_select_uv_same_polygon_threshold = FloatProperty(
name="Same Polygon Threshold",
description="Threshold to distinguish same polygons",
default=0.00001,
min=0.00001,
max=0.01,
step=0.00001
)
scene.muv_select_uv_selection_method = EnumProperty(
name="Selection Method",
description="How to select faces which have overlapped UVs",
items=[
('EXTEND', "Extend",
"Select faces without unselecting selected faces"),
('RESET', "Reset", "Select faces and unselect selected faces"),
],
default='RESET'
)
scene.muv_select_uv_sync_mesh_selection = BoolProperty(
name="Sync Mesh Selection",
description="Select the mesh's faces as well as UV's faces",
default=False
)
@classmethod
def del_props(cls, scene):
del scene.muv_select_uv_enabled
del scene.muv_select_uv_same_polygon_threshold
@BlClassRegistry()
@compat.make_annotations
class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator):
"""
Operation class: Select faces which have overlapped UVs
"""
bl_idname = "uv.muv_select_uv_select_overlapped"
bl_label = "Overlapped"
bl_description = "Select faces which have overlapped UVs"
bl_options = {'REGISTER', 'UNDO'}
same_polygon_threshold = FloatProperty(
name="Same Polygon Threshold",
description="Threshold to distinguish same polygons",
default=0.00001,
min=0.00001,
max=0.01,
step=0.00001
)
selection_method = EnumProperty(
name="Selection Method",
description="How to select faces which have overlapped UVs",
items=[
('EXTEND', "Extend",
"Select faces without unselecting selected faces"),
('RESET', "Reset", "Select faces and unselect selected faces"),
],
default='RESET'
)
sync_mesh_selection = BoolProperty(
name="Sync Mesh Selection",
description="Select mesh's faces as well as UV's faces",
default=False
)
@classmethod
def poll(cls, context):
# we can not get area/space/region from console
if common.is_console_mode():
return True
return _is_valid_context(context)
@staticmethod
def setup_argument(ops, scene):
ops.same_polygon_threshold = scene.muv_select_uv_same_polygon_threshold
ops.selection_method = scene.muv_select_uv_selection_method
ops.sync_mesh_selection = scene.muv_select_uv_sync_mesh_selection
def execute(self, context):
objs = common.get_uv_editable_objects(context)
bm_list = []
uv_layer_list = []
faces_list = []
for obj in objs:
bm = bmesh.from_edit_mesh(obj.data)
if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
uv_layer = bm.loops.layers.uv.verify()
if context.tool_settings.use_uv_select_sync:
sel_faces = [f for f in bm.faces]
else:
sel_faces = [f for f in bm.faces if f.select]
bm_list.append(bm)
uv_layer_list.append(uv_layer)
faces_list.append(sel_faces)
overlapped_info = common.get_overlapped_uv_info(
bm_list, faces_list, uv_layer_list, 'FACE',
self.same_polygon_threshold)
if self.selection_method == 'RESET':
if context.tool_settings.use_uv_select_sync:
for faces in faces_list:
for f in faces:
f.select = False
else:
for uv_layer, faces in zip(uv_layer_list, faces_list):
for f in faces:
if self.sync_mesh_selection:
f.select = False
for l in f.loops:
l[uv_layer].select = False
for info in overlapped_info:
if context.tool_settings.use_uv_select_sync:
info["subject_face"].select = True
else:
if self.sync_mesh_selection:
info["subject_face"].select = True
for l in info["subject_face"].loops:
l[info["subject_uv_layer"]].select = True
for obj in objs:
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}
@BlClassRegistry()
@compat.make_annotations
class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator):
"""
Operation class: Select faces which have flipped UVs
"""
bl_idname = "uv.muv_select_uv_select_flipped"
bl_label = "Flipped"
bl_description = "Select faces which have flipped UVs"
bl_options = {'REGISTER', 'UNDO'}
selection_method = EnumProperty(
name="Selection Method",
description="How to select faces which have overlapped UVs",
items=[
('EXTEND', "Extend",
"Select faces without unselecting selected faces"),
('RESET', "Reset", "Select faces and unselect selected faces"),
],
default='RESET'
)
sync_mesh_selection = BoolProperty(
name="Sync Mesh Selection",
description="Select mesh's faces as well as UV's faces",
default=False
)
@classmethod
def poll(cls, context):
# we can not get area/space/region from console
if common.is_console_mode():
return True
return _is_valid_context(context)
@staticmethod
def setup_argument(ops, scene):
ops.selection_method = scene.muv_select_uv_selection_method
ops.sync_mesh_selection = scene.muv_select_uv_sync_mesh_selection
def execute(self, context):
objs = common.get_uv_editable_objects(context)
bm_list = []
uv_layer_list = []
faces_list = []
for obj in objs:
bm = bmesh.from_edit_mesh(obj.data)
if common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
uv_layer = bm.loops.layers.uv.verify()
if context.tool_settings.use_uv_select_sync:
sel_faces = [f for f in bm.faces]
else:
sel_faces = [f for f in bm.faces if f.select]
bm_list.append(bm)
uv_layer_list.append(uv_layer)
faces_list.append(sel_faces)
flipped_info = common.get_flipped_uv_info(
bm_list, faces_list, uv_layer_list)
if self.selection_method == 'RESET':
if context.tool_settings.use_uv_select_sync:
for faces in faces_list:
for f in faces:
f.select = False
else:
for uv_layer, faces in zip(uv_layer_list, faces_list):
for f in faces:
if self.sync_mesh_selection:
f.select = False
for l in f.loops:
l[uv_layer].select = False
for info in flipped_info:
if context.tool_settings.use_uv_select_sync:
info["face"].select = True
else:
if self.sync_mesh_selection:
info["face"].select = True
for l in info["face"].loops:
l[info["uv_layer"]].select = True
for obj in objs:
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}
@BlClassRegistry()
class MUV_OT_SelectUV_ZoomSelectedUV(bpy.types.Operator):
"""
Operation class: Zoom selected UV in View3D space
"""
bl_idname = "uv.muv_select_uv_zoom_selected_uv"
bl_label = "Zoom Selected UV"
bl_description = "Zoom selected UV in View3D space"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
# we can not get area/space/region from console
if common.is_console_mode():
return True
return _is_valid_context(context)
def _get_override_context(self, context):
for window in context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
return {'window': window, 'screen': screen,
'area': area, 'region': region}
return None
def execute(self, context):
objs = common.get_uv_editable_objects(context)
bm_list = []
uv_layer_list = []
verts_list = []
for obj in objs:
bm = bmesh.from_edit_mesh(obj.data)
if common.check_version(2, 73, 0) >= 0:
bm.verts.ensure_lookup_table()
uv_layer = bm.loops.layers.uv.verify()
sel_verts = [v for v in bm.verts if v.select]
bm_list.append(bm)
uv_layer_list.append(uv_layer)
verts_list.append(sel_verts)
# Get all selected UV vertices in UV Editor.
sel_uv_verts = []
for vlist, uv_layer in zip(verts_list, uv_layer_list):
for v in vlist:
for l in v.link_loops:
if l[uv_layer].select or \
context.tool_settings.use_uv_select_sync:
sel_uv_verts.append(v)
break
# Select vertices only selected in UV Editor.
for bm in bm_list:
for v in bm.verts:
v.select = False
for v in sel_uv_verts:
v.select = True
for obj in objs:
bmesh.update_edit_mesh(obj.data)
# Zoom.
override_context = self._get_override_context(context)
if override_context is None:
self.report({'WARNING'}, "More than one 'VIEW_3D' area must exist")
return {'CANCELLED'}
bpy.ops.view3d.view_selected(override_context, use_all_regions=False)
# Revert selection of vertices.
for v in sel_verts:
v.select = True
for obj in objs:
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}