File headers: use SPDX license identifiers
[blender-addons.git] / magic_uv / op / unwrap_constraint.py
blobda94c495d82f93e17b122d9f59e84f547640cbc6
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 __author__ = "Nutti <nutti.metro@gmail.com>"
4 __status__ = "production"
5 __version__ = "6.5"
6 __date__ = "6 Mar 2021"
8 import bpy
9 from bpy.props import (
10 BoolProperty,
11 EnumProperty,
12 FloatProperty,
14 import bmesh
16 from .. import common
17 from ..utils.bl_class_registry import BlClassRegistry
18 from ..utils.property_class_registry import PropertyClassRegistry
19 from ..utils import compatibility as compat
22 def _is_valid_context(context):
23 objs = common.get_uv_editable_objects(context)
24 if not objs:
25 return False
27 # only edit mode is allowed to execute
28 if context.object.mode != 'EDIT':
29 return False
31 # only 'VIEW_3D' space is allowed to execute
32 if not common.is_valid_space(context, ['VIEW_3D']):
33 return False
35 return True
38 @PropertyClassRegistry()
39 class _Properties:
40 idname = "unwrap_constraint"
42 @classmethod
43 def init_props(cls, scene):
44 scene.muv_unwrap_constraint_enabled = BoolProperty(
45 name="Unwrap Constraint Enabled",
46 description="Unwrap Constraint is enabled",
47 default=False
49 scene.muv_unwrap_constraint_u_const = BoolProperty(
50 name="U-Constraint",
51 description="Keep UV U-axis coordinate",
52 default=False
54 scene.muv_unwrap_constraint_v_const = BoolProperty(
55 name="V-Constraint",
56 description="Keep UV V-axis coordinate",
57 default=False
60 @classmethod
61 def del_props(cls, scene):
62 del scene.muv_unwrap_constraint_enabled
63 del scene.muv_unwrap_constraint_u_const
64 del scene.muv_unwrap_constraint_v_const
67 @BlClassRegistry(legacy=True)
68 @compat.make_annotations
69 class MUV_OT_UnwrapConstraint(bpy.types.Operator):
70 """
71 Operation class: Unwrap with constrain UV coordinate
72 """
74 bl_idname = "uv.muv_unwrap_constraint"
75 bl_label = "Unwrap Constraint"
76 bl_description = "Unwrap while keeping uv coordinate"
77 bl_options = {'REGISTER', 'UNDO'}
79 # property for original unwrap
80 method = EnumProperty(
81 name="Method",
82 description="Unwrapping method",
83 items=[
84 ('ANGLE_BASED', 'Angle Based', 'Angle Based'),
85 ('CONFORMAL', 'Conformal', 'Conformal')
87 default='ANGLE_BASED')
88 fill_holes = BoolProperty(
89 name="Fill Holes",
90 description="Virtual fill holes in meshes before unwrapping",
91 default=True)
92 correct_aspect = BoolProperty(
93 name="Correct Aspect",
94 description="Map UVs taking image aspect ratio into account",
95 default=True)
96 use_subsurf_data = BoolProperty(
97 name="Use Subsurf Modifier",
98 description="""Map UVs taking vertex position after subsurf
99 into account""",
100 default=False)
101 margin = FloatProperty(
102 name="Margin",
103 description="Space between islands",
104 max=1.0,
105 min=0.0,
106 default=0.001)
108 # property for this operation
109 u_const = BoolProperty(
110 name="U-Constraint",
111 description="Keep UV U-axis coordinate",
112 default=False
114 v_const = BoolProperty(
115 name="V-Constraint",
116 description="Keep UV V-axis coordinate",
117 default=False
120 @classmethod
121 def poll(cls, context):
122 # we can not get area/space/region from console
123 if common.is_console_mode():
124 return True
125 return _is_valid_context(context)
127 def execute(self, context):
128 objs = common.get_uv_editable_objects(context)
130 uv_list = {} # { Object: uv_list }
131 for obj in objs:
132 bm = bmesh.from_edit_mesh(obj.data)
133 if common.check_version(2, 73, 0) >= 0:
134 bm.faces.ensure_lookup_table()
136 # bpy.ops.uv.unwrap() makes one UV map at least
137 if not bm.loops.layers.uv:
138 self.report({'WARNING'},
139 "Object {} must have more than one UV map"
140 .format(obj.name))
141 return {'CANCELLED'}
142 uv_layer = bm.loops.layers.uv.verify()
144 # get original UV coordinate
145 faces = [f for f in bm.faces if f.select]
146 uv_list[obj] = []
147 for f in faces:
148 uvs = [l[uv_layer].uv.copy() for l in f.loops]
149 uv_list[obj].append(uvs)
151 # unwrap
152 bpy.ops.uv.unwrap(
153 method=self.method,
154 fill_holes=self.fill_holes,
155 correct_aspect=self.correct_aspect,
156 use_subsurf_data=self.use_subsurf_data,
157 margin=self.margin)
159 # when U/V-Constraint is checked, revert original coordinate
160 for obj in objs:
161 bm = bmesh.from_edit_mesh(obj.data)
162 if common.check_version(2, 73, 0) >= 0:
163 bm.faces.ensure_lookup_table()
164 uv_layer = bm.loops.layers.uv.verify()
165 faces = [f for f in bm.faces if f.select]
167 for f, uvs in zip(faces, uv_list[obj]):
168 for l, uv in zip(f.loops, uvs):
169 if self.u_const:
170 l[uv_layer].uv.x = uv.x
171 if self.v_const:
172 l[uv_layer].uv.y = uv.y
174 # update mesh
175 bmesh.update_edit_mesh(obj.data)
177 return {'FINISHED'}