Import_3ds: Improved distance cue node setup
[blender-addons.git] / magic_uv / op / uv_bounding_box.py
blob131e6d0348648145adaf19193cc8187d3263fed1
1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 __author__ = "Nutti <nutti.metro@gmail.com>"
6 __status__ = "production"
7 __version__ = "6.7"
8 __date__ = "9 Sep 2022"
10 from enum import IntEnum
11 import math
13 import bpy
14 import mathutils
15 import bmesh
16 from bpy.props import BoolProperty, EnumProperty
18 from .. import common
19 from ..utils.bl_class_registry import BlClassRegistry
20 from ..utils.property_class_registry import PropertyClassRegistry
21 from ..utils import compatibility as compat
23 import gpu
24 from gpu_extras.batch import batch_for_shader
27 MAX_VALUE = 100000.0
30 def _is_valid_context(context):
31 # 'IMAGE_EDITOR' and 'VIEW_3D' space is allowed to execute.
32 # If 'View_3D' space is not allowed, you can't find option in Tool-Shelf
33 # after the execution
34 if not common.is_valid_space(context, ['IMAGE_EDITOR', 'VIEW_3D']):
35 return False
37 obj = context.object
39 # only edit mode is allowed to execute
40 if obj is None:
41 return False
42 if obj.type != 'MESH':
43 return False
44 if context.object.mode != 'EDIT':
45 return False
47 return True
50 @PropertyClassRegistry()
51 class _Properties:
52 idname = "uv_bounding_box"
54 @classmethod
55 def init_props(cls, scene):
56 class Props():
57 uv_info_ini = []
58 ctrl_points_ini = []
59 ctrl_points = []
61 scene.muv_props.uv_bounding_box = Props()
63 def get_func(_):
64 return MUV_OT_UVBoundingBox.is_running(bpy.context)
66 def set_func(_, __):
67 pass
69 def update_func(_, __):
70 bpy.ops.uv.muv_uv_bounding_box('INVOKE_REGION_WIN')
72 scene.muv_uv_bounding_box_enabled = BoolProperty(
73 name="UV Bounding Box Enabled",
74 description="UV Bounding Box is enabled",
75 default=False
77 scene.muv_uv_bounding_box_show = BoolProperty(
78 name="UV Bounding Box Showed",
79 description="UV Bounding Box is showed",
80 default=False,
81 get=get_func,
82 set=set_func,
83 update=update_func
85 scene.muv_uv_bounding_box_uniform_scaling = BoolProperty(
86 name="Uniform Scaling",
87 description="Enable Uniform Scaling",
88 default=False
90 scene.muv_uv_bounding_box_boundary = EnumProperty(
91 name="Boundary",
92 description="Boundary",
93 default='UV_SEL',
94 items=[
95 ('UV', "UV", "Boundary is decided by UV"),
96 ('UV_SEL', "UV (Selected)",
97 "Boundary is decided by Selected UV")
101 @classmethod
102 def del_props(cls, scene):
103 del scene.muv_props.uv_bounding_box
104 del scene.muv_uv_bounding_box_enabled
105 del scene.muv_uv_bounding_box_show
106 del scene.muv_uv_bounding_box_uniform_scaling
107 del scene.muv_uv_bounding_box_boundary
110 class CommandBase:
112 Custom class: Base class of command
115 def __init__(self):
116 self.op = 'NONE' # operation
118 def to_matrix(self):
119 # mat = I
120 mat = mathutils.Matrix()
121 mat.identity()
122 return mat
125 class TranslationCommand(CommandBase):
127 Custom class: Translation operation
130 def __init__(self, ix, iy):
131 super().__init__()
132 self.op = 'TRANSLATION'
133 self.__x = ix # current x
134 self.__y = iy # current y
135 self.__ix = ix # initial x
136 self.__iy = iy # initial y
138 def to_matrix(self):
139 # mat = Mt
140 dx = self.__x - self.__ix
141 dy = self.__y - self.__iy
142 return mathutils.Matrix.Translation((dx, dy, 0))
144 def set(self, x, y):
145 self.__x = x
146 self.__y = y
149 class RotationCommand(CommandBase):
151 Custom class: Rotation operation
154 def __init__(self, ix, iy, cx, cy):
155 super().__init__()
156 self.op = 'ROTATION'
157 self.__x = ix # current x
158 self.__y = iy # current y
159 self.__cx = cx # center of rotation x
160 self.__cy = cy # center of rotation y
161 dx = self.__x - self.__cx
162 dy = self.__y - self.__cy
163 self.__iangle = math.atan2(dy, dx) # initial rotation angle
165 def to_matrix(self):
166 # mat = Mt * Mr * Mt^-1
167 dx = self.__x - self.__cx
168 dy = self.__y - self.__cy
169 angle = math.atan2(dy, dx) - self.__iangle
170 mti = mathutils.Matrix.Translation((-self.__cx, -self.__cy, 0.0))
171 mr = mathutils.Matrix.Rotation(angle, 4, 'Z')
172 mt = mathutils.Matrix.Translation((self.__cx, self.__cy, 0.0))
173 return compat.matmul(compat.matmul(mt, mr), mti)
175 def set(self, x, y):
176 self.__x = x
177 self.__y = y
180 class ScalingCommand(CommandBase):
182 Custom class: Scaling operation
185 def __init__(self, ix, iy, ox, oy, dir_x, dir_y, mat):
186 super().__init__()
187 self.op = 'SCALING'
188 self.__ix = ix # initial x
189 self.__iy = iy # initial y
190 self.__x = ix # current x
191 self.__y = iy # current y
192 self.__ox = ox # origin of scaling x
193 self.__oy = oy # origin of scaling y
194 self.__dir_x = dir_x # direction of scaling x
195 self.__dir_y = dir_y # direction of scaling y
196 self.__mat = mat
197 # initial origin of scaling = M(to original transform) * (ox, oy)
198 iov = compat.matmul(mat, mathutils.Vector((ox, oy, 0.0)))
199 self.__iox = iov.x # initial origin of scaling X
200 self.__ioy = iov.y # initial origin of scaling y
202 def to_matrix(self):
204 mat = M(to original transform)^-1 * Mt(to origin) * Ms *
205 Mt(to origin)^-1 * M(to original transform)
207 m = self.__mat
208 mi = self.__mat.inverted()
209 mtoi = mathutils.Matrix.Translation((-self.__iox, -self.__ioy, 0.0))
210 mto = mathutils.Matrix.Translation((self.__iox, self.__ioy, 0.0))
211 # every point must be transformed to origin
212 t = compat.matmul(m, mathutils.Vector((self.__ix, self.__iy, 0.0)))
213 tix, tiy = t.x, t.y
214 t = compat.matmul(m, mathutils.Vector((self.__ox, self.__oy, 0.0)))
215 tox, toy = t.x, t.y
216 t = compat.matmul(m, mathutils.Vector((self.__x, self.__y, 0.0)))
217 tx, ty = t.x, t.y
218 ms = mathutils.Matrix()
219 ms.identity()
220 if self.__dir_x == 1:
221 ms[0][0] = (tx - tox) * self.__dir_x / (tix - tox)
222 if self.__dir_y == 1:
223 ms[1][1] = (ty - toy) * self.__dir_y / (tiy - toy)
224 return compat.matmul(compat.matmul(compat.matmul(
225 compat.matmul(mi, mto), ms), mtoi), m)
227 def set(self, x, y):
228 self.__x = x
229 self.__y = y
232 class UniformScalingCommand(CommandBase):
234 Custom class: Uniform Scaling operation
237 def __init__(self, ix, iy, ox, oy, mat):
238 super().__init__()
239 self.op = 'SCALING'
240 self.__ix = ix # initial x
241 self.__iy = iy # initial y
242 self.__x = ix # current x
243 self.__y = iy # current y
244 self.__ox = ox # origin of scaling x
245 self.__oy = oy # origin of scaling y
246 self.__mat = mat
247 # initial origin of scaling = M(to original transform) * (ox, oy)
248 iov = compat.matmul(mat, mathutils.Vector((ox, oy, 0.0)))
249 self.__iox = iov.x # initial origin of scaling x
250 self.__ioy = iov.y # initial origin of scaling y
251 self.__dir_x = 1
252 self.__dir_y = 1
254 def to_matrix(self):
256 mat = M(to original transform)^-1 * Mt(to origin) * Ms *
257 Mt(to origin)^-1 * M(to original transform)
259 m = self.__mat
260 mi = self.__mat.inverted()
261 mtoi = mathutils.Matrix.Translation((-self.__iox, -self.__ioy, 0.0))
262 mto = mathutils.Matrix.Translation((self.__iox, self.__ioy, 0.0))
263 # every point must be transformed to origin
264 t = compat.matmul(m, mathutils.Vector((self.__ix, self.__iy, 0.0)))
265 tix, tiy = t.x, t.y
266 t = compat.matmul(m, mathutils.Vector((self.__ox, self.__oy, 0.0)))
267 tox, toy = t.x, t.y
268 t = compat.matmul(m, mathutils.Vector((self.__x, self.__y, 0.0)))
269 tx, ty = t.x, t.y
270 ms = mathutils.Matrix()
271 ms.identity()
272 tir = math.sqrt((tix - tox) * (tix - tox) + (tiy - toy) * (tiy - toy))
273 tr = math.sqrt((tx - tox) * (tx - tox) + (ty - toy) * (ty - toy))
275 sr = tr / tir
277 if ((tx - tox) * (tix - tox)) > 0:
278 self.__dir_x = 1
279 else:
280 self.__dir_x = -1
281 if ((ty - toy) * (tiy - toy)) > 0:
282 self.__dir_y = 1
283 else:
284 self.__dir_y = -1
286 ms[0][0] = sr * self.__dir_x
287 ms[1][1] = sr * self.__dir_y
289 return compat.matmul(compat.matmul(compat.matmul(
290 compat.matmul(mi, mto), ms), mtoi), m)
292 def set(self, x, y):
293 self.__x = x
294 self.__y = y
297 class CommandExecuter:
299 Custom class: manage command history and execute command
302 def __init__(self):
303 self.__cmd_list = [] # history
304 self.__cmd_list_redo = [] # redo list
306 def execute(self, begin=0, end=-1):
308 create matrix from history
310 mat = mathutils.Matrix()
311 mat.identity()
312 for i, cmd in enumerate(self.__cmd_list):
313 if begin <= i and (end == -1 or i <= end):
314 mat = compat.matmul(cmd.to_matrix(), mat)
315 return mat
317 def undo_size(self):
319 get history size
321 return len(self.__cmd_list)
323 def top(self):
325 get top of history
327 if len(self.__cmd_list) <= 0:
328 return None
329 return self.__cmd_list[-1]
331 def append(self, cmd):
333 append command
335 self.__cmd_list.append(cmd)
336 self.__cmd_list_redo = []
338 def undo(self):
340 undo command
342 if len(self.__cmd_list) <= 0:
343 return
344 self.__cmd_list_redo.append(self.__cmd_list.pop())
346 def redo(self):
348 redo command
350 if len(self.__cmd_list_redo) <= 0:
351 return
352 self.__cmd_list.append(self.__cmd_list_redo.pop())
354 def pop(self):
355 if len(self.__cmd_list) <= 0:
356 return None
357 return self.__cmd_list.pop()
359 def push(self, cmd):
360 self.__cmd_list.append(cmd)
363 class State(IntEnum):
365 Enum: State definition used by MUV_UVBBStateMgr
367 NONE = 0
368 TRANSLATING = 1
369 SCALING_1 = 2
370 SCALING_2 = 3
371 SCALING_3 = 4
372 SCALING_4 = 5
373 SCALING_5 = 6
374 SCALING_6 = 7
375 SCALING_7 = 8
376 SCALING_8 = 9
377 ROTATING = 10
378 UNIFORM_SCALING_1 = 11
379 UNIFORM_SCALING_2 = 12
380 UNIFORM_SCALING_3 = 13
381 UNIFORM_SCALING_4 = 14
384 class StateBase:
386 Custom class: Base class of state
389 def __init__(self):
390 pass
392 def update(self, context, event, ctrl_points, mouse_view):
393 raise NotImplementedError
396 class StateNone(StateBase):
398 Custom class:
399 No state
400 Wait for event from mouse
403 def __init__(self, cmd_exec):
404 super().__init__()
405 self.__cmd_exec = cmd_exec
407 def update(self, context, event, ctrl_points, mouse_view):
409 Update state
411 user_prefs = compat.get_user_preferences(context)
412 prefs = user_prefs.addons["magic_uv"].preferences
413 cp_react_size = prefs.uv_bounding_box_cp_react_size
414 is_uscaling = context.scene.muv_uv_bounding_box_uniform_scaling
415 if (event.type == 'LEFTMOUSE') and (event.value == 'PRESS'):
416 x, y = context.region.view2d.view_to_region(
417 mouse_view.x, mouse_view.y)
418 for i, p in enumerate(ctrl_points):
419 px, py = context.region.view2d.view_to_region(p.x, p.y)
420 in_cp_x = px - cp_react_size < x < px + cp_react_size
421 in_cp_y = py - cp_react_size < y < py + cp_react_size
422 if in_cp_x and in_cp_y:
423 if is_uscaling:
424 arr = [1, 3, 6, 8]
425 if i in arr:
426 return (
427 State.UNIFORM_SCALING_1 +
428 arr.index(i)
430 else:
431 return State.TRANSLATING + i
433 return State.NONE
436 class StateTranslating(StateBase):
438 Custom class: Translating state
441 def __init__(self, cmd_exec, ctrl_points):
442 super().__init__()
443 self.__cmd_exec = cmd_exec
444 ix, iy = ctrl_points[0].x, ctrl_points[0].y
445 self.__cmd_exec.append(TranslationCommand(ix, iy))
447 def update(self, context, event, ctrl_points, mouse_view):
448 if event.type == 'LEFTMOUSE':
449 if event.value == 'RELEASE':
450 return State.NONE
451 if event.type == 'MOUSEMOVE':
452 x, y = mouse_view.x, mouse_view.y
453 self.__cmd_exec.top().set(x, y)
454 return State.TRANSLATING
457 class StateScaling(StateBase):
459 Custom class: Scaling state
462 def __init__(self, cmd_exec, state, ctrl_points):
463 super().__init__()
464 self.__state = state
465 self.__cmd_exec = cmd_exec
466 dir_x_list = [1, 1, 1, 0, 0, 1, 1, 1]
467 dir_y_list = [1, 0, 1, 1, 1, 1, 0, 1]
468 idx = state - 2
469 ix, iy = ctrl_points[idx + 1].x, ctrl_points[idx + 1].y
470 ox, oy = ctrl_points[8 - idx].x, ctrl_points[8 - idx].y
471 dir_x, dir_y = dir_x_list[idx], dir_y_list[idx]
472 mat = self.__cmd_exec.execute(end=self.__cmd_exec.undo_size())
473 self.__cmd_exec.append(
474 ScalingCommand(ix, iy, ox, oy, dir_x, dir_y, mat.inverted()))
476 def update(self, context, event, ctrl_points, mouse_view):
477 if event.type == 'LEFTMOUSE':
478 if event.value == 'RELEASE':
479 return State.NONE
480 if event.type == 'MOUSEMOVE':
481 x, y = mouse_view.x, mouse_view.y
482 self.__cmd_exec.top().set(x, y)
483 return self.__state
486 class StateUniformScaling(StateBase):
488 Custom class: Uniform Scaling state
491 def __init__(self, cmd_exec, state, ctrl_points):
492 super().__init__()
493 self.__state = state
494 self.__cmd_exec = cmd_exec
495 icp_idx = [1, 3, 6, 8]
496 ocp_idx = [8, 6, 3, 1]
497 idx = state - State.UNIFORM_SCALING_1
498 ix, iy = ctrl_points[icp_idx[idx]].x, ctrl_points[icp_idx[idx]].y
499 ox, oy = ctrl_points[ocp_idx[idx]].x, ctrl_points[ocp_idx[idx]].y
500 mat = self.__cmd_exec.execute(end=self.__cmd_exec.undo_size())
501 self.__cmd_exec.append(UniformScalingCommand(
502 ix, iy, ox, oy, mat.inverted()))
504 def update(self, context, event, ctrl_points, mouse_view):
505 if event.type == 'LEFTMOUSE':
506 if event.value == 'RELEASE':
507 return State.NONE
508 if event.type == 'MOUSEMOVE':
509 x, y = mouse_view.x, mouse_view.y
510 self.__cmd_exec.top().set(x, y)
512 return self.__state
515 class StateRotating(StateBase):
517 Custom class: Rotating state
520 def __init__(self, cmd_exec, ctrl_points):
521 super().__init__()
522 self.__cmd_exec = cmd_exec
523 ix, iy = ctrl_points[9].x, ctrl_points[9].y
524 ox, oy = ctrl_points[0].x, ctrl_points[0].y
525 self.__cmd_exec.append(RotationCommand(ix, iy, ox, oy))
527 def update(self, context, event, ctrl_points, mouse_view):
528 if event.type == 'LEFTMOUSE':
529 if event.value == 'RELEASE':
530 return State.NONE
531 if event.type == 'MOUSEMOVE':
532 x, y = mouse_view.x, mouse_view.y
533 self.__cmd_exec.top().set(x, y)
534 return State.ROTATING
537 class StateManager:
539 Custom class: Manage state about this feature
542 def __init__(self, cmd_exec):
543 self.__cmd_exec = cmd_exec # command executer
544 self.__state = State.NONE # current state
545 self.__state_obj = StateNone(self.__cmd_exec)
547 def __update_state(self, next_state, ctrl_points):
549 Update state
552 if next_state == self.__state:
553 return
554 obj = None
555 if next_state == State.TRANSLATING:
556 obj = StateTranslating(self.__cmd_exec, ctrl_points)
557 elif State.SCALING_1 <= next_state <= State.SCALING_8:
558 obj = StateScaling(
559 self.__cmd_exec, next_state, ctrl_points)
560 elif next_state == State.ROTATING:
561 obj = StateRotating(self.__cmd_exec, ctrl_points)
562 elif next_state == State.NONE:
563 obj = StateNone(self.__cmd_exec)
564 elif (State.UNIFORM_SCALING_1 <= next_state <=
565 State.UNIFORM_SCALING_4):
566 obj = StateUniformScaling(
567 self.__cmd_exec, next_state, ctrl_points)
569 if obj is not None:
570 self.__state_obj = obj
572 self.__state = next_state
574 def update(self, context, ctrl_points, event):
575 mouse_region = mathutils.Vector((
576 event.mouse_region_x, event.mouse_region_y))
577 mouse_view = mathutils.Vector((context.region.view2d.region_to_view(
578 mouse_region.x, mouse_region.y)))
579 next_state = self.__state_obj.update(
580 context, event, ctrl_points, mouse_view)
581 self.__update_state(next_state, ctrl_points)
583 return self.__state
586 @BlClassRegistry()
587 class MUV_OT_UVBoundingBox(bpy.types.Operator):
589 Operation class: UV Bounding Box
592 bl_idname = "uv.muv_uv_bounding_box"
593 bl_label = "UV Bounding Box"
594 bl_description = "Internal operation for UV Bounding Box"
595 bl_options = {'REGISTER', 'UNDO'}
597 def __init__(self):
598 self.__timer = None
599 self.__cmd_exec = CommandExecuter() # Command executor
600 self.__state_mgr = StateManager(self.__cmd_exec) # State Manager
602 __handle = None
603 __timer = None
605 @classmethod
606 def poll(cls, context):
607 # we can not get area/space/region from console
608 if common.is_console_mode():
609 return False
610 return _is_valid_context(context)
612 @classmethod
613 def is_running(cls, _):
614 return 1 if cls.__handle else 0
616 @classmethod
617 def handle_add(cls, obj, context):
618 if cls.__handle is None:
619 sie = bpy.types.SpaceImageEditor
620 cls.__handle = sie.draw_handler_add(
621 cls.draw_bb, (obj, context), "WINDOW", "POST_PIXEL")
622 if cls.__timer is None:
623 cls.__timer = context.window_manager.event_timer_add(
624 0.1, window=context.window)
625 context.window_manager.modal_handler_add(obj)
627 @classmethod
628 def handle_remove(cls, context):
629 if cls.__handle is not None:
630 sie = bpy.types.SpaceImageEditor
631 sie.draw_handler_remove(cls.__handle, "WINDOW")
632 cls.__handle = None
633 if cls.__timer is not None:
634 context.window_manager.event_timer_remove(cls.__timer)
635 cls.__timer = None
637 @classmethod
638 def draw_bb(cls, _, context):
640 Draw bounding box
642 props = context.scene.muv_props.uv_bounding_box
644 if not MUV_OT_UVBoundingBox.is_running(context):
645 return
647 if not _is_valid_context(context):
648 return
650 user_prefs = compat.get_user_preferences(context)
651 prefs = user_prefs.addons["magic_uv"].preferences
652 cp_size = prefs.uv_bounding_box_cp_size
654 gpu.state.program_point_size_set(False)
655 gpu.state.point_size_set(cp_size)
656 gpu.state.blend_set('ALPHA')
658 shader = gpu.shader.from_builtin("UNIFORM_COLOR")
659 shader.bind()
660 shader.uniform_float("color", (1.0, 1.0, 1.0, 1.0))
662 points = [mathutils.Vector(context.region.view2d.view_to_region(cp.x, cp.y)) for cp in props.ctrl_points]
663 batch = batch_for_shader(shader, 'POINTS', {"pos": points})
664 batch.draw(shader)
665 del batch
667 def __get_uv_info(self, context):
669 Get UV coordinate
671 sc = context.scene
672 objs = common.get_uv_editable_objects(context)
673 uv_info = []
675 for obj in objs:
676 bm = bmesh.from_edit_mesh(obj.data)
677 if common.check_version(2, 73, 0) >= 0:
678 bm.faces.ensure_lookup_table()
679 if not bm.loops.layers.uv:
680 continue
681 uv_layer = bm.loops.layers.uv.verify()
682 for f in bm.faces:
683 if not f.select:
684 continue
685 for i, l in enumerate(f.loops):
686 if sc.muv_uv_bounding_box_boundary == 'UV_SEL':
687 if l[uv_layer].select:
688 uv_info.append({
689 "bmesh": bm,
690 "fidx": f.index,
691 "lidx": i,
692 "uv": l[uv_layer].uv.copy()
694 elif sc.muv_uv_bounding_box_boundary == 'UV':
695 uv_info.append({
696 "bmesh": bm,
697 "fidx": f.index,
698 "lidx": i,
699 "uv": l[uv_layer].uv.copy()
701 if not uv_info:
702 return None
703 return uv_info
705 def __get_ctrl_point(self, uv_info_ini):
707 Get control point
709 left = MAX_VALUE
710 right = -MAX_VALUE
711 top = -MAX_VALUE
712 bottom = MAX_VALUE
714 for info in uv_info_ini:
715 uv = info["uv"]
716 if uv.x < left:
717 left = uv.x
718 if uv.x > right:
719 right = uv.x
720 if uv.y < bottom:
721 bottom = uv.y
722 if uv.y > top:
723 top = uv.y
725 points = [
726 mathutils.Vector((
727 (left + right) * 0.5, (top + bottom) * 0.5, 0.0
729 mathutils.Vector((left, top, 0.0)),
730 mathutils.Vector((left, (top + bottom) * 0.5, 0.0)),
731 mathutils.Vector((left, bottom, 0.0)),
732 mathutils.Vector(((left + right) * 0.5, top, 0.0)),
733 mathutils.Vector(((left + right) * 0.5, bottom, 0.0)),
734 mathutils.Vector((right, top, 0.0)),
735 mathutils.Vector((right, (top + bottom) * 0.5, 0.0)),
736 mathutils.Vector((right, bottom, 0.0)),
737 mathutils.Vector(((left + right) * 0.5, top + 0.03, 0.0))
740 return points
742 def __update_uvs(self, context, uv_info_ini, trans_mat):
744 Update UV coordinate
747 for info in uv_info_ini:
748 bm = info["bmesh"]
749 uv_layer = bm.loops.layers.uv.verify()
750 fidx = info["fidx"]
751 lidx = info["lidx"]
752 uv = info["uv"]
753 v = mathutils.Vector((uv.x, uv.y, 0.0))
754 av = compat.matmul(trans_mat, v)
755 bm.faces[fidx].loops[lidx][uv_layer].uv = mathutils.Vector(
756 (av.x, av.y))
758 objs = common.get_uv_editable_objects(context)
759 for obj in objs:
760 bmesh.update_edit_mesh(obj.data)
762 def __update_ctrl_point(self, ctrl_points_ini, trans_mat):
764 Update control point
766 return [compat.matmul(trans_mat, cp) for cp in ctrl_points_ini]
768 def modal(self, context, event):
769 props = context.scene.muv_props.uv_bounding_box
770 common.redraw_all_areas()
772 if not MUV_OT_UVBoundingBox.is_running(context):
773 return {'FINISHED'}
775 if not _is_valid_context(context):
776 MUV_OT_UVBoundingBox.handle_remove(context)
777 return {'FINISHED'}
779 region_types = [
780 'HEADER',
781 'UI',
782 'TOOLS',
784 if not common.mouse_on_area(event, 'IMAGE_EDITOR') or \
785 common.mouse_on_regions(event, 'IMAGE_EDITOR', region_types):
786 return {'PASS_THROUGH'}
788 if event.type == 'TIMER':
789 trans_mat = self.__cmd_exec.execute()
790 self.__update_uvs(context, props.uv_info_ini, trans_mat)
791 props.ctrl_points = self.__update_ctrl_point(
792 props.ctrl_points_ini, trans_mat)
794 state = self.__state_mgr.update(context, props.ctrl_points, event)
795 if state == State.NONE:
796 return {'PASS_THROUGH'}
798 return {'RUNNING_MODAL'}
800 def invoke(self, context, _):
801 props = context.scene.muv_props.uv_bounding_box
803 if MUV_OT_UVBoundingBox.is_running(context):
804 MUV_OT_UVBoundingBox.handle_remove(context)
805 return {'FINISHED'}
807 props.uv_info_ini = self.__get_uv_info(context)
808 if props.uv_info_ini is None:
809 return {'CANCELLED'}
811 MUV_OT_UVBoundingBox.handle_add(self, context)
813 props.ctrl_points_ini = self.__get_ctrl_point(props.uv_info_ini)
814 trans_mat = self.__cmd_exec.execute()
815 # Update is needed in order to display control point
816 self.__update_uvs(context, props.uv_info_ini, trans_mat)
817 props.ctrl_points = self.__update_ctrl_point(
818 props.ctrl_points_ini, trans_mat)
820 return {'RUNNING_MODAL'}