Node Wrangler: do not add reroutes to unavailable outputs
[blender-addons.git] / magic_uv / op / align_uv_cursor.py
blob185a9780236b31fdb83c405777d9a7fce7174a2e
1 # SPDX-FileCopyrightText: 2018-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.6"
8 __date__ = "22 Apr 2022"
10 import bpy
11 from mathutils import Vector
12 from bpy.props import EnumProperty, BoolProperty, FloatVectorProperty
13 import bmesh
15 from .. import common
16 from ..utils.bl_class_registry import BlClassRegistry
17 from ..utils.property_class_registry import PropertyClassRegistry
18 from ..utils import compatibility as compat
21 def _is_valid_context(context):
22 # 'IMAGE_EDITOR' and 'VIEW_3D' space is allowed to execute.
23 # If 'View_3D' space is not allowed, you can't find option in Tool-Shelf
24 # after the execution
25 if not common.is_valid_space(context, ['IMAGE_EDITOR', 'VIEW_3D']):
26 return False
28 return True
31 @PropertyClassRegistry()
32 class _Properties:
33 idname = "align_uv_cursor"
35 @classmethod
36 def init_props(cls, scene):
37 def auvc_get_cursor_loc(self):
38 area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW',
39 'IMAGE_EDITOR')
40 if compat.check_version(2, 80, 0) < 0:
41 bd_size = common.get_uvimg_editor_board_size(area)
42 else:
43 bd_size = [1.0, 1.0]
44 loc = space.cursor_location
46 if bd_size[0] < 0.000001:
47 cx = 0.0
48 else:
49 cx = loc[0] / bd_size[0]
50 if bd_size[1] < 0.000001:
51 cy = 0.0
52 else:
53 cy = loc[1] / bd_size[1]
55 self['muv_align_uv_cursor_cursor_loc'] = Vector((cx, cy))
56 return self.get('muv_align_uv_cursor_cursor_loc', (0.0, 0.0))
58 def auvc_set_cursor_loc(self, value):
59 self['muv_align_uv_cursor_cursor_loc'] = value
60 area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW',
61 'IMAGE_EDITOR')
62 if compat.check_version(2, 80, 0) < 0:
63 bd_size = common.get_uvimg_editor_board_size(area)
64 else:
65 bd_size = [1.0, 1.0]
66 cx = bd_size[0] * value[0]
67 cy = bd_size[1] * value[1]
68 space.cursor_location = Vector((cx, cy))
70 scene.muv_align_uv_cursor_enabled = BoolProperty(
71 name="Align UV Cursor Enabled",
72 description="Align UV Cursor is enabled",
73 default=False
76 scene.muv_align_uv_cursor_cursor_loc = FloatVectorProperty(
77 name="UV Cursor Location",
78 size=2,
79 precision=4,
80 soft_min=-1.0,
81 soft_max=1.0,
82 step=1,
83 default=(0.000, 0.000),
84 get=auvc_get_cursor_loc,
85 set=auvc_set_cursor_loc
87 scene.muv_align_uv_cursor_align_method = EnumProperty(
88 name="Align Method",
89 description="Align Method",
90 default='TEXTURE',
91 items=[
92 ('TEXTURE', "Texture", "Align to texture"),
93 ('UV', "UV", "Align to UV"),
94 ('UV_SEL', "UV (Selected)", "Align to Selected UV")
98 scene.muv_uv_cursor_location_enabled = BoolProperty(
99 name="UV Cursor Location Enabled",
100 description="UV Cursor Location is enabled",
101 default=False
104 @classmethod
105 def del_props(cls, scene):
106 del scene.muv_align_uv_cursor_enabled
107 del scene.muv_align_uv_cursor_cursor_loc
108 del scene.muv_align_uv_cursor_align_method
110 del scene.muv_uv_cursor_location_enabled
113 @BlClassRegistry()
114 @compat.make_annotations
115 class MUV_OT_AlignUVCursor(bpy.types.Operator):
117 bl_idname = "uv.muv_align_uv_cursor"
118 bl_label = "Align UV Cursor"
119 bl_description = "Align cursor to the center of UV island"
120 bl_options = {'REGISTER', 'UNDO'}
122 position = EnumProperty(
123 items=(
124 ('CENTER', "Center", "Align to Center"),
125 ('LEFT_TOP', "Left Top", "Align to Left Top"),
126 ('LEFT_MIDDLE', "Left Middle", "Align to Left Middle"),
127 ('LEFT_BOTTOM', "Left Bottom", "Align to Left Bottom"),
128 ('MIDDLE_TOP', "Middle Top", "Align to Middle Top"),
129 ('MIDDLE_BOTTOM', "Middle Bottom", "Align to Middle Bottom"),
130 ('RIGHT_TOP', "Right Top", "Align to Right Top"),
131 ('RIGHT_MIDDLE', "Right Middle", "Align to Right Middle"),
132 ('RIGHT_BOTTOM', "Right Bottom", "Align to Right Bottom")
134 name="Position",
135 description="Align position",
136 default='CENTER'
138 base = EnumProperty(
139 items=(
140 ('TEXTURE', "Texture", "Align based on Texture"),
141 ('UV', "UV", "Align to UV"),
142 ('UV_SEL', "UV (Selected)", "Align to Selected UV")
144 name="Base",
145 description="Align base",
146 default='TEXTURE'
149 @classmethod
150 def poll(cls, context):
151 # we can not get area/space/region from console
152 if common.is_console_mode():
153 return True
154 return _is_valid_context(context)
156 def execute(self, context):
157 area, _, space = common.get_space('IMAGE_EDITOR', 'WINDOW',
158 'IMAGE_EDITOR')
159 if compat.check_version(2, 80, 0) < 0:
160 bd_size = common.get_uvimg_editor_board_size(area)
161 else:
162 bd_size = [1.0, 1.0]
164 large_value = 1e7
165 if self.base == 'UV':
166 objs = common.get_uv_editable_objects(context)
167 no_selected_face = True
168 if objs:
169 max_ = Vector((-large_value, -large_value))
170 min_ = Vector((large_value, large_value))
171 for obj in objs:
172 bm = bmesh.from_edit_mesh(obj.data)
173 if not bm.loops.layers.uv:
174 return None
175 uv_layer = bm.loops.layers.uv.verify()
177 for f in bm.faces:
178 if (not context.tool_settings.use_uv_select_sync and
179 not f.select):
180 continue
181 for l in f.loops:
182 uv = l[uv_layer].uv
183 max_.x = max(max_.x, uv.x)
184 max_.y = max(max_.y, uv.y)
185 min_.x = min(min_.x, uv.x)
186 min_.y = min(min_.y, uv.y)
187 no_selected_face = False
188 if no_selected_face:
189 max_ = Vector((1.0, 1.0))
190 min_ = Vector((0.0, 0.0))
191 center = Vector((
192 (max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0
195 # pylint: disable=R1702
196 elif self.base == 'UV_SEL':
197 objs = common.get_uv_editable_objects(context)
198 no_selected_face = True
199 if objs:
200 max_ = Vector((-large_value, -large_value))
201 min_ = Vector((large_value, large_value))
202 for obj in objs:
203 bm = bmesh.from_edit_mesh(obj.data)
204 if not bm.loops.layers.uv:
205 return None
206 uv_layer = bm.loops.layers.uv.verify()
208 if context.tool_settings.use_uv_select_sync:
209 for v in bm.verts:
210 if not v.select:
211 continue
212 for l in v.link_loops:
213 uv = l[uv_layer].uv
214 max_.x = max(max_.x, uv.x)
215 max_.y = max(max_.y, uv.y)
216 min_.x = min(min_.x, uv.x)
217 min_.y = min(min_.y, uv.y)
218 no_selected_face = False
219 else:
220 for f in bm.faces:
221 if not f.select:
222 continue
223 for l in f.loops:
224 if not l[uv_layer].select:
225 continue
226 uv = l[uv_layer].uv
227 max_.x = max(max_.x, uv.x)
228 max_.y = max(max_.y, uv.y)
229 min_.x = min(min_.x, uv.x)
230 min_.y = min(min_.y, uv.y)
231 no_selected_face = False
232 if no_selected_face:
233 max_ = Vector((1.0, 1.0))
234 min_ = Vector((0.0, 0.0))
235 center = Vector((
236 (max_.x + min_.x) / 2.0, (max_.y + min_.y) / 2.0
239 elif self.base == 'TEXTURE':
240 min_ = Vector((0.0, 0.0))
241 max_ = Vector((1.0, 1.0))
242 center = Vector((0.5, 0.5))
243 else:
244 self.report({'ERROR'}, "Unknown Operation")
245 return {'CANCELLED'}
247 if self.position == 'CENTER':
248 cx = center.x
249 cy = center.y
250 elif self.position == 'LEFT_TOP':
251 cx = min_.x
252 cy = max_.y
253 elif self.position == 'LEFT_MIDDLE':
254 cx = min_.x
255 cy = center.y
256 elif self.position == 'LEFT_BOTTOM':
257 cx = min_.x
258 cy = min_.y
259 elif self.position == 'MIDDLE_TOP':
260 cx = center.x
261 cy = max_.y
262 elif self.position == 'MIDDLE_BOTTOM':
263 cx = center.x
264 cy = min_.y
265 elif self.position == 'RIGHT_TOP':
266 cx = max_.x
267 cy = max_.y
268 elif self.position == 'RIGHT_MIDDLE':
269 cx = max_.x
270 cy = center.y
271 elif self.position == 'RIGHT_BOTTOM':
272 cx = max_.x
273 cy = min_.y
274 else:
275 self.report({'ERROR'}, "Unknown Operation")
276 return {'CANCELLED'}
278 cx = cx * bd_size[0]
279 cy = cy * bd_size[1]
281 space.cursor_location = Vector((cx, cy))
283 return {'FINISHED'}