547 lines
16 KiB
Python
547 lines
16 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 math
|
||
|
|
from math import atan2, cos, sqrt, sin, fabs
|
||
|
|
|
||
|
|
import bpy
|
||
|
|
from bpy.props import BoolProperty
|
||
|
|
import bmesh
|
||
|
|
from mathutils import Vector
|
||
|
|
|
||
|
|
from .. import common
|
||
|
|
from ..utils.bl_class_registry import BlClassRegistry
|
||
|
|
from ..utils.property_class_registry import PropertyClassRegistry
|
||
|
|
from ..utils import compatibility as compat
|
||
|
|
|
||
|
|
|
||
|
|
def _get_vco(verts_orig, loop):
|
||
|
|
"""
|
||
|
|
Get vertex original coordinate from loop
|
||
|
|
"""
|
||
|
|
for vo in verts_orig:
|
||
|
|
if vo["vidx"] == loop.vert.index and vo["moved"] is False:
|
||
|
|
return vo["vco"]
|
||
|
|
return loop.vert.co
|
||
|
|
|
||
|
|
|
||
|
|
def _get_link_loops(vert):
|
||
|
|
"""
|
||
|
|
Get loop linked to vertex
|
||
|
|
"""
|
||
|
|
link_loops = []
|
||
|
|
for f in vert.link_faces:
|
||
|
|
adj_loops = []
|
||
|
|
for loop in f.loops:
|
||
|
|
# self loop
|
||
|
|
if loop.vert == vert:
|
||
|
|
l = loop
|
||
|
|
# linked loop
|
||
|
|
else:
|
||
|
|
for e in loop.vert.link_edges:
|
||
|
|
if e.other_vert(loop.vert) == vert:
|
||
|
|
adj_loops.append(loop)
|
||
|
|
if len(adj_loops) < 2:
|
||
|
|
return None
|
||
|
|
|
||
|
|
link_loops.append({"l": l, "l0": adj_loops[0], "l1": adj_loops[1]})
|
||
|
|
return link_loops
|
||
|
|
|
||
|
|
|
||
|
|
def _get_ini_geom(link_loop, uv_layer, verts_orig, v_orig):
|
||
|
|
"""
|
||
|
|
Get initial geometory
|
||
|
|
(Get interior angle of face in vertex/UV space)
|
||
|
|
"""
|
||
|
|
u = link_loop["l"][uv_layer].uv
|
||
|
|
v0 = _get_vco(verts_orig, link_loop["l0"])
|
||
|
|
u0 = link_loop["l0"][uv_layer].uv
|
||
|
|
v1 = _get_vco(verts_orig, link_loop["l1"])
|
||
|
|
u1 = link_loop["l1"][uv_layer].uv
|
||
|
|
|
||
|
|
# get interior angle of face in vertex space
|
||
|
|
v0v1 = v1 - v0
|
||
|
|
v0v = v_orig["vco"] - v0
|
||
|
|
v1v = v_orig["vco"] - v1
|
||
|
|
theta0 = v0v1.angle(v0v)
|
||
|
|
theta1 = v0v1.angle(-v1v)
|
||
|
|
if (theta0 + theta1) > math.pi:
|
||
|
|
theta0 = v0v1.angle(-v0v)
|
||
|
|
theta1 = v0v1.angle(v1v)
|
||
|
|
|
||
|
|
# get interior angle of face in UV space
|
||
|
|
u0u1 = u1 - u0
|
||
|
|
u0u = u - u0
|
||
|
|
u1u = u - u1
|
||
|
|
phi0 = u0u1.angle(u0u)
|
||
|
|
phi1 = u0u1.angle(-u1u)
|
||
|
|
if (phi0 + phi1) > math.pi:
|
||
|
|
phi0 = u0u1.angle(-u0u)
|
||
|
|
phi1 = u0u1.angle(u1u)
|
||
|
|
|
||
|
|
# get direction of linked UV coordinate
|
||
|
|
# this will be used to judge whether angle is more or less than 180 degree
|
||
|
|
dir0 = u0u1.cross(u0u) > 0
|
||
|
|
dir1 = u0u1.cross(u1u) > 0
|
||
|
|
|
||
|
|
return {
|
||
|
|
"theta0": theta0,
|
||
|
|
"theta1": theta1,
|
||
|
|
"phi0": phi0,
|
||
|
|
"phi1": phi1,
|
||
|
|
"dir0": dir0,
|
||
|
|
"dir1": dir1}
|
||
|
|
|
||
|
|
|
||
|
|
def _get_target_uv(link_loop, uv_layer, verts_orig, v, ini_geom):
|
||
|
|
"""
|
||
|
|
Get target UV coordinate
|
||
|
|
"""
|
||
|
|
v0 = _get_vco(verts_orig, link_loop["l0"])
|
||
|
|
lo0 = link_loop["l0"]
|
||
|
|
v1 = _get_vco(verts_orig, link_loop["l1"])
|
||
|
|
lo1 = link_loop["l1"]
|
||
|
|
|
||
|
|
# get interior angle of face in vertex space
|
||
|
|
v0v1 = v1 - v0
|
||
|
|
v0v = v.co - v0
|
||
|
|
v1v = v.co - v1
|
||
|
|
theta0 = v0v1.angle(v0v)
|
||
|
|
theta1 = v0v1.angle(-v1v)
|
||
|
|
if (theta0 + theta1) > math.pi:
|
||
|
|
theta0 = v0v1.angle(-v0v)
|
||
|
|
theta1 = v0v1.angle(v1v)
|
||
|
|
|
||
|
|
# calculate target interior angle in UV space
|
||
|
|
phi0 = theta0 * ini_geom["phi0"] / ini_geom["theta0"]
|
||
|
|
phi1 = theta1 * ini_geom["phi1"] / ini_geom["theta1"]
|
||
|
|
|
||
|
|
uv0 = lo0[uv_layer].uv
|
||
|
|
uv1 = lo1[uv_layer].uv
|
||
|
|
|
||
|
|
# calculate target vertex coordinate from target interior angle
|
||
|
|
tuv0, tuv1 = _calc_tri_vert(uv0, uv1, phi0, phi1)
|
||
|
|
|
||
|
|
# target UV coordinate depends on direction, so judge using direction of
|
||
|
|
# linked UV coordinate
|
||
|
|
u0u1 = uv1 - uv0
|
||
|
|
u0u = tuv0 - uv0
|
||
|
|
u1u = tuv0 - uv1
|
||
|
|
dir0 = u0u1.cross(u0u) > 0
|
||
|
|
dir1 = u0u1.cross(u1u) > 0
|
||
|
|
if (ini_geom["dir0"] != dir0) or (ini_geom["dir1"] != dir1):
|
||
|
|
return tuv1
|
||
|
|
|
||
|
|
return tuv0
|
||
|
|
|
||
|
|
|
||
|
|
def _calc_tri_vert(v0, v1, angle0, angle1):
|
||
|
|
"""
|
||
|
|
Calculate rest coordinate from other coordinates and angle of end
|
||
|
|
"""
|
||
|
|
angle = math.pi - angle0 - angle1
|
||
|
|
|
||
|
|
alpha = atan2(v1.y - v0.y, v1.x - v0.x)
|
||
|
|
d = (v1.x - v0.x) / cos(alpha)
|
||
|
|
a = d * sin(angle0) / sin(angle)
|
||
|
|
b = d * sin(angle1) / sin(angle)
|
||
|
|
s = (a + b + d) / 2.0
|
||
|
|
if fabs(d) < 0.0000001:
|
||
|
|
xd = 0
|
||
|
|
yd = 0
|
||
|
|
else:
|
||
|
|
r = s * (s - a) * (s - b) * (s - d)
|
||
|
|
if r < 0:
|
||
|
|
xd = 0
|
||
|
|
yd = 0
|
||
|
|
else:
|
||
|
|
xd = (b * b - a * a + d * d) / (2 * d)
|
||
|
|
yd = 2 * sqrt(r) / d
|
||
|
|
x1 = xd * cos(alpha) - yd * sin(alpha) + v0.x
|
||
|
|
y1 = xd * sin(alpha) + yd * cos(alpha) + v0.y
|
||
|
|
x2 = xd * cos(alpha) + yd * sin(alpha) + v0.x
|
||
|
|
y2 = xd * sin(alpha) - yd * cos(alpha) + v0.y
|
||
|
|
|
||
|
|
return Vector((x1, y1)), Vector((x2, y2))
|
||
|
|
|
||
|
|
|
||
|
|
def _is_valid_context(context):
|
||
|
|
# only 'VIEW_3D' space is allowed to execute
|
||
|
|
if not common.is_valid_space(context, ['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 = "texture_lock"
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def init_props(cls, scene):
|
||
|
|
class Props():
|
||
|
|
verts_orig = {} # { Object: verts_orig }
|
||
|
|
|
||
|
|
scene.muv_props.texture_lock = Props()
|
||
|
|
|
||
|
|
def get_func(_):
|
||
|
|
return MUV_OT_TextureLock_Intr.is_running(bpy.context)
|
||
|
|
|
||
|
|
def set_func(_, __):
|
||
|
|
pass
|
||
|
|
|
||
|
|
def update_func(_, __):
|
||
|
|
bpy.ops.uv.muv_texture_lock_intr('INVOKE_REGION_WIN')
|
||
|
|
|
||
|
|
scene.muv_texture_lock_enabled = BoolProperty(
|
||
|
|
name="Texture Lock Enabled",
|
||
|
|
description="Texture Lock is enabled",
|
||
|
|
default=False
|
||
|
|
)
|
||
|
|
scene.muv_texture_lock_lock = BoolProperty(
|
||
|
|
name="Texture Lock Locked",
|
||
|
|
description="Texture Lock is locked",
|
||
|
|
default=False,
|
||
|
|
get=get_func,
|
||
|
|
set=set_func,
|
||
|
|
update=update_func
|
||
|
|
)
|
||
|
|
scene.muv_texture_lock_connect = BoolProperty(
|
||
|
|
name="Connect UV",
|
||
|
|
default=True
|
||
|
|
)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def del_props(cls, scene):
|
||
|
|
del scene.muv_props.texture_lock
|
||
|
|
del scene.muv_texture_lock_enabled
|
||
|
|
del scene.muv_texture_lock_lock
|
||
|
|
del scene.muv_texture_lock_connect
|
||
|
|
|
||
|
|
|
||
|
|
@BlClassRegistry()
|
||
|
|
class MUV_OT_TextureLock_Lock(bpy.types.Operator):
|
||
|
|
"""
|
||
|
|
Operation class: Lock Texture
|
||
|
|
"""
|
||
|
|
|
||
|
|
bl_idname = "uv.muv_texture_lock_lock"
|
||
|
|
bl_label = "Lock Texture"
|
||
|
|
bl_description = "Lock Texture"
|
||
|
|
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)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def is_ready(cls, context):
|
||
|
|
sc = context.scene
|
||
|
|
props = sc.muv_props.texture_lock
|
||
|
|
if props.verts_orig:
|
||
|
|
return True
|
||
|
|
return False
|
||
|
|
|
||
|
|
def execute(self, context):
|
||
|
|
props = context.scene.muv_props.texture_lock
|
||
|
|
objs = common.get_uv_editable_objects(context)
|
||
|
|
|
||
|
|
props.verts_orig = {}
|
||
|
|
for obj in objs:
|
||
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
||
|
|
if common.check_version(2, 73, 0) >= 0:
|
||
|
|
bm.verts.ensure_lookup_table()
|
||
|
|
bm.edges.ensure_lookup_table()
|
||
|
|
bm.faces.ensure_lookup_table()
|
||
|
|
|
||
|
|
if not bm.loops.layers.uv:
|
||
|
|
self.report({'WARNING'},
|
||
|
|
"Object {} must have more than one UV map"
|
||
|
|
.format(obj.name))
|
||
|
|
return {'CANCELLED'}
|
||
|
|
|
||
|
|
props.verts_orig[obj] = [
|
||
|
|
{"vidx": v.index, "vco": v.co.copy(), "moved": False}
|
||
|
|
for v in bm.verts if v.select
|
||
|
|
]
|
||
|
|
|
||
|
|
return {'FINISHED'}
|
||
|
|
|
||
|
|
|
||
|
|
@BlClassRegistry()
|
||
|
|
@compat.make_annotations
|
||
|
|
class MUV_OT_TextureLock_Unlock(bpy.types.Operator):
|
||
|
|
"""
|
||
|
|
Operation class: Unlock Texture
|
||
|
|
"""
|
||
|
|
|
||
|
|
bl_idname = "uv.muv_texture_lock_unlock"
|
||
|
|
bl_label = "Unlock Texture"
|
||
|
|
bl_description = "Unlock Texture"
|
||
|
|
bl_options = {'REGISTER', 'UNDO'}
|
||
|
|
|
||
|
|
connect = BoolProperty(
|
||
|
|
name="Connect UV",
|
||
|
|
default=True
|
||
|
|
)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def poll(cls, context):
|
||
|
|
# we can not get area/space/region from console
|
||
|
|
if common.is_console_mode():
|
||
|
|
return True
|
||
|
|
sc = context.scene
|
||
|
|
props = sc.muv_props.texture_lock
|
||
|
|
if not props.verts_orig:
|
||
|
|
return False
|
||
|
|
if not MUV_OT_TextureLock_Lock.is_ready(context):
|
||
|
|
return False
|
||
|
|
if not _is_valid_context(context):
|
||
|
|
return False
|
||
|
|
return True
|
||
|
|
|
||
|
|
def execute(self, context):
|
||
|
|
sc = context.scene
|
||
|
|
props = sc.muv_props.texture_lock
|
||
|
|
objs = common.get_uv_editable_objects(context)
|
||
|
|
|
||
|
|
if set(objs) != set(props.verts_orig.keys()):
|
||
|
|
self.report({'WARNING'},
|
||
|
|
"Object list does not match between Lock and Unlock")
|
||
|
|
return {'CANCELLED'}
|
||
|
|
|
||
|
|
for obj in objs:
|
||
|
|
verts_orig = props.verts_orig[obj]
|
||
|
|
|
||
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
||
|
|
if common.check_version(2, 73, 0) >= 0:
|
||
|
|
bm.verts.ensure_lookup_table()
|
||
|
|
bm.edges.ensure_lookup_table()
|
||
|
|
bm.faces.ensure_lookup_table()
|
||
|
|
|
||
|
|
if not bm.loops.layers.uv:
|
||
|
|
self.report({'WARNING'},
|
||
|
|
"Object {} must have more than one UV map"
|
||
|
|
.format(obj.name))
|
||
|
|
return {'CANCELLED'}
|
||
|
|
uv_layer = bm.loops.layers.uv.verify()
|
||
|
|
|
||
|
|
verts = [v.index for v in bm.verts if v.select]
|
||
|
|
|
||
|
|
# move UV followed by vertex coordinate
|
||
|
|
for vidx, v_orig in zip(verts, verts_orig):
|
||
|
|
if vidx != v_orig["vidx"]:
|
||
|
|
self.report({'ERROR'}, "Internal Error")
|
||
|
|
return {"CANCELLED"}
|
||
|
|
|
||
|
|
v = bm.verts[vidx]
|
||
|
|
link_loops = _get_link_loops(v)
|
||
|
|
|
||
|
|
result = []
|
||
|
|
|
||
|
|
for ll in link_loops:
|
||
|
|
ini_geom = _get_ini_geom(ll, uv_layer, verts_orig, v_orig)
|
||
|
|
target_uv = _get_target_uv(
|
||
|
|
ll, uv_layer, verts_orig, v, ini_geom)
|
||
|
|
result.append({"l": ll["l"], "uv": target_uv})
|
||
|
|
|
||
|
|
# connect other face's UV
|
||
|
|
if self.connect:
|
||
|
|
ave = Vector((0.0, 0.0))
|
||
|
|
for r in result:
|
||
|
|
ave = ave + r["uv"]
|
||
|
|
ave = ave / len(result)
|
||
|
|
for r in result:
|
||
|
|
r["l"][uv_layer].uv = ave
|
||
|
|
else:
|
||
|
|
for r in result:
|
||
|
|
r["l"][uv_layer].uv = r["uv"]
|
||
|
|
v_orig["moved"] = True
|
||
|
|
bmesh.update_edit_mesh(obj.data)
|
||
|
|
|
||
|
|
props.verts_orig = {}
|
||
|
|
|
||
|
|
return {'FINISHED'}
|
||
|
|
|
||
|
|
|
||
|
|
@BlClassRegistry()
|
||
|
|
class MUV_OT_TextureLock_Intr(bpy.types.Operator):
|
||
|
|
"""
|
||
|
|
Operation class: Texture Lock (Interactive mode)
|
||
|
|
"""
|
||
|
|
|
||
|
|
bl_idname = "uv.muv_texture_lock_intr"
|
||
|
|
bl_label = "Texture Lock (Interactive mode)"
|
||
|
|
bl_description = "Internal operation for Texture Lock (Interactive mode)"
|
||
|
|
|
||
|
|
__timer = None
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def poll(cls, context):
|
||
|
|
# we can not get area/space/region from console
|
||
|
|
if common.is_console_mode():
|
||
|
|
return False
|
||
|
|
return _is_valid_context(context)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def is_running(cls, _):
|
||
|
|
return 1 if cls.__timer else 0
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def handle_add(cls, ops_obj, context):
|
||
|
|
if cls.__timer is None:
|
||
|
|
cls.__timer = context.window_manager.event_timer_add(
|
||
|
|
0.10, window=context.window)
|
||
|
|
context.window_manager.modal_handler_add(ops_obj)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def handle_remove(cls, context):
|
||
|
|
if cls.__timer is not None:
|
||
|
|
context.window_manager.event_timer_remove(cls.__timer)
|
||
|
|
cls.__timer = None
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.__intr_verts_orig = {} # { Object: verts_orig }
|
||
|
|
self.__intr_verts = {} # { Object: verts_orig }
|
||
|
|
|
||
|
|
def __sel_verts_changed(self, context):
|
||
|
|
objs = common.get_uv_editable_objects(context)
|
||
|
|
|
||
|
|
for obj in objs:
|
||
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
||
|
|
if common.check_version(2, 73, 0) >= 0:
|
||
|
|
bm.verts.ensure_lookup_table()
|
||
|
|
bm.edges.ensure_lookup_table()
|
||
|
|
bm.faces.ensure_lookup_table()
|
||
|
|
|
||
|
|
if obj not in self.__intr_verts:
|
||
|
|
return True
|
||
|
|
|
||
|
|
prev = set(self.__intr_verts[obj])
|
||
|
|
now = {v.index for v in bm.verts if v.select}
|
||
|
|
|
||
|
|
if prev != now:
|
||
|
|
return True
|
||
|
|
|
||
|
|
return False
|
||
|
|
|
||
|
|
def __reinit_verts(self, context):
|
||
|
|
objs = common.get_uv_editable_objects(context)
|
||
|
|
self.__intr_verts_orig = {}
|
||
|
|
self.__intr_verts = {}
|
||
|
|
|
||
|
|
for obj in objs:
|
||
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
||
|
|
if common.check_version(2, 73, 0) >= 0:
|
||
|
|
bm.verts.ensure_lookup_table()
|
||
|
|
bm.edges.ensure_lookup_table()
|
||
|
|
bm.faces.ensure_lookup_table()
|
||
|
|
|
||
|
|
self.__intr_verts_orig[obj] = [
|
||
|
|
{"vidx": v.index, "vco": v.co.copy(), "moved": False}
|
||
|
|
for v in bm.verts if v.select]
|
||
|
|
self.__intr_verts[obj] = [v.index for v in bm.verts if v.select]
|
||
|
|
|
||
|
|
def __update_uv(self, context):
|
||
|
|
"""
|
||
|
|
Update UV when vertex coordinates are changed
|
||
|
|
"""
|
||
|
|
objs = common.get_uv_editable_objects(context)
|
||
|
|
|
||
|
|
for obj in objs:
|
||
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
||
|
|
if common.check_version(2, 73, 0) >= 0:
|
||
|
|
bm.verts.ensure_lookup_table()
|
||
|
|
bm.edges.ensure_lookup_table()
|
||
|
|
bm.faces.ensure_lookup_table()
|
||
|
|
|
||
|
|
if not bm.loops.layers.uv:
|
||
|
|
self.report({'WARNING'},
|
||
|
|
"Object {} must have more than one UV map"
|
||
|
|
.format(obj.data))
|
||
|
|
return
|
||
|
|
uv_layer = bm.loops.layers.uv.verify()
|
||
|
|
|
||
|
|
verts = [v.index for v in bm.verts if v.select]
|
||
|
|
verts_orig = self.__intr_verts_orig[obj]
|
||
|
|
|
||
|
|
for vidx, v_orig in zip(verts, verts_orig):
|
||
|
|
if vidx != v_orig["vidx"]:
|
||
|
|
self.report({'ERROR'}, "Internal Error")
|
||
|
|
return
|
||
|
|
|
||
|
|
v = bm.verts[vidx]
|
||
|
|
link_loops = _get_link_loops(v)
|
||
|
|
|
||
|
|
result = []
|
||
|
|
for ll in link_loops:
|
||
|
|
ini_geom = _get_ini_geom(ll, uv_layer, verts_orig, v_orig)
|
||
|
|
target_uv = _get_target_uv(
|
||
|
|
ll, uv_layer, verts_orig, v, ini_geom)
|
||
|
|
result.append({"l": ll["l"], "uv": target_uv})
|
||
|
|
|
||
|
|
# UV connect option is always true, because it raises
|
||
|
|
# unexpected behavior
|
||
|
|
ave = Vector((0.0, 0.0))
|
||
|
|
for r in result:
|
||
|
|
ave = ave + r["uv"]
|
||
|
|
ave = ave / len(result)
|
||
|
|
for r in result:
|
||
|
|
r["l"][uv_layer].uv = ave
|
||
|
|
v_orig["moved"] = True
|
||
|
|
bmesh.update_edit_mesh(obj.data)
|
||
|
|
|
||
|
|
common.redraw_all_areas()
|
||
|
|
self.__intr_verts_orig[obj] = [
|
||
|
|
{"vidx": v.index, "vco": v.co.copy(), "moved": False}
|
||
|
|
for v in bm.verts if v.select]
|
||
|
|
|
||
|
|
def modal(self, context, event):
|
||
|
|
if not _is_valid_context(context):
|
||
|
|
MUV_OT_TextureLock_Intr.handle_remove(context)
|
||
|
|
return {'FINISHED'}
|
||
|
|
|
||
|
|
if not MUV_OT_TextureLock_Intr.is_running(context):
|
||
|
|
return {'FINISHED'}
|
||
|
|
|
||
|
|
if context.area:
|
||
|
|
context.area.tag_redraw()
|
||
|
|
|
||
|
|
if event.type == 'TIMER':
|
||
|
|
if self.__sel_verts_changed(context):
|
||
|
|
self.__reinit_verts(context)
|
||
|
|
else:
|
||
|
|
self.__update_uv(context)
|
||
|
|
|
||
|
|
return {'PASS_THROUGH'}
|
||
|
|
|
||
|
|
def invoke(self, context, _):
|
||
|
|
if not _is_valid_context(context):
|
||
|
|
return {'CANCELLED'}
|
||
|
|
|
||
|
|
if not MUV_OT_TextureLock_Intr.is_running(context):
|
||
|
|
MUV_OT_TextureLock_Intr.handle_add(self, context)
|
||
|
|
return {'RUNNING_MODAL'}
|
||
|
|
else:
|
||
|
|
MUV_OT_TextureLock_Intr.handle_remove(context)
|
||
|
|
|
||
|
|
if context.area:
|
||
|
|
context.area.tag_redraw()
|
||
|
|
|
||
|
|
return {'FINISHED'}
|