Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Vazquez <blendergit@gmail.com>2020-08-14 19:02:03 +0300
committerAntonio Vazquez <blendergit@gmail.com>2020-08-14 19:02:03 +0300
commit39f51712fb57631bccb69883c39a8807a34319d7 (patch)
treeba93044850be2bae955e8c0512f454aca25ad77e /magic_uv/op
parentd7e2005d22435e104507ac129cd15d2eb6a10258 (diff)
parentabeef11a77ab5b05f4ce2c71b65c341bdcb7303d (diff)
Merge branch 'master' into greasepencil-addon
Diffstat (limited to 'magic_uv/op')
-rw-r--r--magic_uv/op/__init__.py6
-rw-r--r--magic_uv/op/align_uv.py10
-rw-r--r--magic_uv/op/align_uv_cursor.py4
-rw-r--r--magic_uv/op/clip_uv.py227
-rw-r--r--magic_uv/op/copy_paste_uv.py11
-rw-r--r--magic_uv/op/copy_paste_uv_object.py4
-rw-r--r--magic_uv/op/copy_paste_uv_uvedit.py4
-rw-r--r--magic_uv/op/flip_rotate_uv.py4
-rw-r--r--magic_uv/op/mirror_uv.py4
-rw-r--r--magic_uv/op/move_uv.py63
-rw-r--r--magic_uv/op/pack_uv.py4
-rw-r--r--magic_uv/op/preserve_uv_aspect.py4
-rw-r--r--magic_uv/op/select_uv.py83
-rw-r--r--magic_uv/op/smooth_uv.py10
-rw-r--r--magic_uv/op/texture_lock.py6
-rw-r--r--magic_uv/op/texture_projection.py4
-rw-r--r--magic_uv/op/texture_wrap.py4
-rw-r--r--magic_uv/op/transfer_uv.py4
-rw-r--r--magic_uv/op/unwrap_constraint.py4
-rw-r--r--magic_uv/op/uv_bounding_box.py10
-rw-r--r--magic_uv/op/uv_inspection.py249
-rw-r--r--magic_uv/op/uv_sculpt.py24
-rw-r--r--magic_uv/op/uvw.py64
-rw-r--r--magic_uv/op/world_scale_uv.py394
24 files changed, 958 insertions, 243 deletions
diff --git a/magic_uv/op/__init__.py b/magic_uv/op/__init__.py
index cd743b48..b7316192 100644
--- a/magic_uv/op/__init__.py
+++ b/magic_uv/op/__init__.py
@@ -20,13 +20,14 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
if "bpy" in locals():
import importlib
importlib.reload(align_uv)
importlib.reload(align_uv_cursor)
+ importlib.reload(clip_uv)
importlib.reload(copy_paste_uv)
importlib.reload(copy_paste_uv_object)
importlib.reload(copy_paste_uv_uvedit)
@@ -50,6 +51,7 @@ if "bpy" in locals():
else:
from . import align_uv
from . import align_uv_cursor
+ from . import clip_uv
from . import copy_paste_uv
from . import copy_paste_uv_object
from . import copy_paste_uv_uvedit
diff --git a/magic_uv/op/align_uv.py b/magic_uv/op/align_uv.py
index 31f7cbe8..77afcc25 100644
--- a/magic_uv/op/align_uv.py
+++ b/magic_uv/op/align_uv.py
@@ -20,8 +20,8 @@
__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import math
from math import atan2, tan, sin, cos
@@ -164,8 +164,7 @@ def _get_hdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pidx, infl):
# calculate target UV
for i in range(len(accum_uvlens[:-1])):
# get line segment which UV will be placed
- if ((accum_uvlens[i] <= target_length) and
- (accum_uvlens[i + 1] > target_length)):
+ if accum_uvlens[i] <= target_length < accum_uvlens[i + 1]:
tgt_seg_len = target_length - accum_uvlens[i]
seg_len = accum_uvlens[i + 1] - accum_uvlens[i]
uv1 = orig_uvs[i]
@@ -245,8 +244,7 @@ def _get_vdiff_uv_vinfl(uv_layer, loop_seqs, vidx, hidx, pidx, infl):
# calculate target UV
for i in range(len(accum_uvlens[:-1])):
# get line segment which UV will be placed
- if ((accum_uvlens[i] <= target_length) and
- (accum_uvlens[i + 1] > target_length)):
+ if accum_uvlens[i] <= target_length < accum_uvlens[i + 1]:
tgt_seg_len = target_length - accum_uvlens[i]
seg_len = accum_uvlens[i + 1] - accum_uvlens[i]
uv1 = orig_uvs[i]
diff --git a/magic_uv/op/align_uv_cursor.py b/magic_uv/op/align_uv_cursor.py
index b103de31..884f645a 100644
--- a/magic_uv/op/align_uv_cursor.py
+++ b/magic_uv/op/align_uv_cursor.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
from mathutils import Vector
diff --git a/magic_uv/op/clip_uv.py b/magic_uv/op/clip_uv.py
new file mode 100644
index 00000000..c6f006e2
--- /dev/null
+++ b/magic_uv/op/clip_uv.py
@@ -0,0 +1,227 @@
+# <pep8-80 compliant>
+
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+__author__ = "Dusan Stevanovic, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
+
+
+import math
+
+import bpy
+import bmesh
+from mathutils import Vector
+from bpy.props import BoolProperty, FloatVectorProperty
+
+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
+ for space in context.area.spaces:
+ if (space.type == 'IMAGE_EDITOR') or (space.type == 'VIEW_3D'):
+ break
+ else:
+ return False
+
+ return True
+
+
+def round_clip_uv_range(v):
+ sign = 1 if v >= 0.0 else -1
+ return int((math.fabs(v) + 0.25) / 0.5) * 0.5 * sign
+
+
+def get_clip_uv_range_max(self):
+ return self.get('muv_clip_uv_range_max', (0.5, 0.5))
+
+
+def set_clip_uv_range_max(self, value):
+ u = round_clip_uv_range(value[0])
+ u = 0.5 if u <= 0.5 else u
+ v = round_clip_uv_range(value[1])
+ v = 0.5 if v <= 0.5 else v
+ self['muv_clip_uv_range_max'] = (u, v)
+
+
+def get_clip_uv_range_min(self):
+ return self.get('muv_clip_uv_range_min', (-0.5, -0.5))
+
+
+def set_clip_uv_range_min(self, value):
+ u = round_clip_uv_range(value[0])
+ u = -0.5 if u >= -0.5 else u
+ v = round_clip_uv_range(value[1])
+ v = -0.5 if v >= -0.5 else v
+ self['muv_clip_uv_range_min'] = (u, v)
+
+
+@PropertyClassRegistry()
+class _Properties:
+ idname = "clip_uv"
+
+ @classmethod
+ def init_props(cls, scene):
+ scene.muv_clip_uv_enabled = BoolProperty(
+ name="Clip UV Enabled",
+ description="Clip UV is enabled",
+ default=False
+ )
+
+ scene.muv_clip_uv_range_max = FloatVectorProperty(
+ name="Range Max",
+ description="Max UV coordinates of the range to be clipped",
+ size=2,
+ default=(0.5, 0.5),
+ min=0.5,
+ step=50,
+ get=get_clip_uv_range_max,
+ set=set_clip_uv_range_max,
+ )
+
+ scene.muv_clip_uv_range_min = FloatVectorProperty(
+ name="Range Min",
+ description="Min UV coordinates of the range to be clipped",
+ size=2,
+ default=(-0.5, -0.5),
+ max=-0.5,
+ step=50,
+ get=get_clip_uv_range_min,
+ set=set_clip_uv_range_min,
+ )
+
+ # TODO: add option to preserve UV island
+
+ @classmethod
+ def del_props(cls, scene):
+ del scene.muv_clip_uv_range_max
+ del scene.muv_clip_uv_range_min
+
+
+@BlClassRegistry()
+@compat.make_annotations
+class MUV_OT_ClipUV(bpy.types.Operator):
+
+ bl_idname = "uv.muv_clip_uv"
+ bl_label = "Clip UV"
+ bl_description = "Clip selected UV in the specified range"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ clip_uv_range_max = FloatVectorProperty(
+ name="Range Max",
+ description="Max UV coordinates of the range to be clipped",
+ size=2,
+ default=(0.5, 0.5),
+ min=0.5,
+ step=50,
+ )
+
+ clip_uv_range_min = FloatVectorProperty(
+ name="Range Min",
+ description="Min UV coordinates of the range to be clipped",
+ size=2,
+ default=(-0.5, -0.5),
+ max=-0.5,
+ step=50,
+ )
+
+ @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 execute(self, context):
+ obj = context.active_object
+ bm = common.create_bmesh(obj)
+
+ if not bm.loops.layers.uv:
+ self.report({'WARNING'}, "Object must have more than one UV map")
+ return {'CANCELLED'}
+
+ uv_layer = bm.loops.layers.uv.verify()
+
+ for face in bm.faces:
+ if not face.select:
+ continue
+
+ selected_loops = [l for l in face.loops
+ if l[uv_layer].select or
+ context.scene.tool_settings.use_uv_select_sync]
+ if not selected_loops:
+ continue
+
+ # average of UV coordinates on the face
+ max_uv = Vector((-10000000.0, -10000000.0))
+ min_uv = Vector((10000000.0, 10000000.0))
+ for l in selected_loops:
+ uv = l[uv_layer].uv
+ max_uv.x = max(max_uv.x, uv.x)
+ max_uv.y = max(max_uv.y, uv.y)
+ min_uv.x = min(min_uv.x, uv.x)
+ min_uv.y = min(min_uv.y, uv.y)
+
+ # clip
+ move_uv = Vector((0.0, 0.0))
+ clip_size = Vector(self.clip_uv_range_max) - \
+ Vector(self.clip_uv_range_min)
+ if max_uv.x > self.clip_uv_range_max[0]:
+ target_x = math.fmod(max_uv.x - self.clip_uv_range_min[0],
+ clip_size.x)
+ if target_x < 0.0:
+ target_x += clip_size.x
+ target_x += self.clip_uv_range_min[0]
+ move_uv.x = target_x - max_uv.x
+ if min_uv.x < self.clip_uv_range_min[0]:
+ target_x = math.fmod(min_uv.x - self.clip_uv_range_min[0],
+ clip_size.x)
+ if target_x < 0.0:
+ target_x += clip_size.x
+ target_x += self.clip_uv_range_min[0]
+ move_uv.x = target_x - min_uv.x
+ if max_uv.y > self.clip_uv_range_max[1]:
+ target_y = math.fmod(max_uv.y - self.clip_uv_range_min[1],
+ clip_size.y)
+ if target_y < 0.0:
+ target_y += clip_size.y
+ target_y += self.clip_uv_range_min[1]
+ move_uv.y = target_y - max_uv.y
+ if min_uv.y < self.clip_uv_range_min[1]:
+ target_y = math.fmod(min_uv.y - self.clip_uv_range_min[1],
+ clip_size.y)
+ if target_y < 0.0:
+ target_y += clip_size.y
+ target_y += self.clip_uv_range_min[1]
+ move_uv.y = target_y - min_uv.y
+
+ # update UV
+ for l in selected_loops:
+ l[uv_layer].uv = l[uv_layer].uv + move_uv
+
+ bmesh.update_edit_mesh(obj.data)
+
+ return {'FINISHED'}
diff --git a/magic_uv/op/copy_paste_uv.py b/magic_uv/op/copy_paste_uv.py
index 5126e241..ba754425 100644
--- a/magic_uv/op/copy_paste_uv.py
+++ b/magic_uv/op/copy_paste_uv.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bmesh
import bpy.utils
@@ -75,7 +75,7 @@ def get_copy_uv_layers(ops_obj, bm, uv_map):
else:
uv_layers.append(bm.loops.layers.uv[uv_map])
ops_obj.report(
- {'INFO'}, "Copy UV coordinate (UV map:{})".format(uv_map))
+ {'INFO'}, "Copy UV coordinate (UV map: {})".format(uv_map))
return uv_layers
@@ -97,7 +97,8 @@ def get_paste_uv_layers(ops_obj, obj, bm, src_info, uv_map):
return None
uv_layers.append(bm.loops.layers.uv[new_uv_map.name])
ops_obj.report(
- {'INFO'}, "Paste UV coordinate (UV map:{})".format(new_uv_map))
+ {'INFO'},
+ "Paste UV coordinate (UV map: {})".format(new_uv_map.name))
elif uv_map == "__all":
for src_layer in src_info.keys():
if src_layer not in bm.loops.layers.uv.keys():
@@ -111,7 +112,7 @@ def get_paste_uv_layers(ops_obj, obj, bm, src_info, uv_map):
else:
uv_layers.append(bm.loops.layers.uv[uv_map])
ops_obj.report(
- {'INFO'}, "Paste UV coordinate (UV map:{})".format(uv_map))
+ {'INFO'}, "Paste UV coordinate (UV map: {})".format(uv_map))
return uv_layers
diff --git a/magic_uv/op/copy_paste_uv_object.py b/magic_uv/op/copy_paste_uv_object.py
index 3297f2b8..1b812b82 100644
--- a/magic_uv/op/copy_paste_uv_object.py
+++ b/magic_uv/op/copy_paste_uv_object.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bmesh
import bpy
diff --git a/magic_uv/op/copy_paste_uv_uvedit.py b/magic_uv/op/copy_paste_uv_uvedit.py
index 7704d1c9..f12851dd 100644
--- a/magic_uv/op/copy_paste_uv_uvedit.py
+++ b/magic_uv/op/copy_paste_uv_uvedit.py
@@ -20,8 +20,8 @@
__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import math
from math import atan2, sin, cos
diff --git a/magic_uv/op/flip_rotate_uv.py b/magic_uv/op/flip_rotate_uv.py
index da8af4c3..d0ac6a83 100644
--- a/magic_uv/op/flip_rotate_uv.py
+++ b/magic_uv/op/flip_rotate_uv.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
import bmesh
diff --git a/magic_uv/op/mirror_uv.py b/magic_uv/op/mirror_uv.py
index d28cf826..dcbaad5e 100644
--- a/magic_uv/op/mirror_uv.py
+++ b/magic_uv/op/mirror_uv.py
@@ -20,8 +20,8 @@
__author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
from bpy.props import (
diff --git a/magic_uv/op/move_uv.py b/magic_uv/op/move_uv.py
index 881ab378..19160a46 100644
--- a/magic_uv/op/move_uv.py
+++ b/magic_uv/op/move_uv.py
@@ -20,8 +20,8 @@
__author__ = "kgeogeo, mem, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
from bpy.props import BoolProperty
@@ -54,20 +54,6 @@ def _is_valid_context(context):
return True
-def _find_uv(context):
- bm = bmesh.from_edit_mesh(context.object.data)
- topology_dict = []
- uvs = []
- active_uv = bm.loops.layers.uv.active
- for fidx, f in enumerate(bm.faces):
- for vidx, v in enumerate(f.verts):
- if v.select:
- uvs.append(f.loops[vidx][active_uv].uv.copy())
- topology_dict.append([fidx, vidx])
-
- return topology_dict, uvs
-
-
@PropertyClassRegistry()
class _Properties:
idname = "move_uv"
@@ -106,6 +92,9 @@ class MUV_OT_MoveUV(bpy.types.Operator):
self.__ini_uvs = []
self.__operating = False
+ # Creation of BMesh is high cost, so cache related objects.
+ self.__cache = {}
+
@classmethod
def poll(cls, context):
# we can not get area/space/region from console
@@ -119,7 +108,18 @@ class MUV_OT_MoveUV(bpy.types.Operator):
def is_running(cls, _):
return cls.__running
- def modal(self, context, event):
+ def _find_uv(self, bm, active_uv):
+ topology_dict = []
+ uvs = []
+ for fidx, f in enumerate(bm.faces):
+ for vidx, v in enumerate(f.verts):
+ if v.select:
+ uvs.append(f.loops[vidx][active_uv].uv.copy())
+ topology_dict.append([fidx, vidx])
+
+ return topology_dict, uvs
+
+ def modal(self, _, event):
if self.__first_time is True:
self.__prev_mouse = Vector((
event.mouse_region_x, event.mouse_region_y))
@@ -146,12 +146,11 @@ class MUV_OT_MoveUV(bpy.types.Operator):
return {'RUNNING_MODAL'}
# update UV
- obj = context.object
- bm = bmesh.from_edit_mesh(obj.data)
- active_uv = bm.loops.layers.uv.active
- for fidx, vidx in self.__topology_dict:
- l = bm.faces[fidx].loops[vidx]
- l[active_uv].uv = l[active_uv].uv + dv
+ obj = self.__cache["active_object"]
+ bm = self.__cache["bmesh"]
+ active_uv = self.__cache["active_uv"]
+ for uv in self.__cache["target_uv"]:
+ uv += dv
bmesh.update_edit_mesh(obj.data)
# check mouse preference
@@ -163,10 +162,12 @@ class MUV_OT_MoveUV(bpy.types.Operator):
for (fidx, vidx), uv in zip(self.__topology_dict, self.__ini_uvs):
bm.faces[fidx].loops[vidx][active_uv].uv = uv
MUV_OT_MoveUV.__running = False
+ self.__cache = {}
return {'FINISHED'}
# confirmed
if event.type == confirm_btn and event.value == 'PRESS':
MUV_OT_MoveUV.__running = False
+ self.__cache = {}
return {'FINISHED'}
return {'RUNNING_MODAL'}
@@ -177,7 +178,21 @@ class MUV_OT_MoveUV(bpy.types.Operator):
self.__first_time = True
context.window_manager.modal_handler_add(self)
- self.__topology_dict, self.__ini_uvs = _find_uv(context)
+
+ obj = context.active_object
+ bm = bmesh.from_edit_mesh(obj.data)
+ active_uv = bm.loops.layers.uv.active
+ self.__topology_dict, self.__ini_uvs = self._find_uv(bm, active_uv)
+
+ # Optimization: Store temporary variables which cause heavy
+ # calculation.
+ self.__cache["active_object"] = obj
+ self.__cache["bmesh"] = bm
+ self.__cache["active_uv"] = active_uv
+ self.__cache["target_uv"] = []
+ for fidx, vidx in self.__topology_dict:
+ l = bm.faces[fidx].loops[vidx]
+ self.__cache["target_uv"].append(l[active_uv].uv)
if context.area:
context.area.tag_redraw()
diff --git a/magic_uv/op/pack_uv.py b/magic_uv/op/pack_uv.py
index 3589231a..0d7ed966 100644
--- a/magic_uv/op/pack_uv.py
+++ b/magic_uv/op/pack_uv.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
from math import fabs
diff --git a/magic_uv/op/preserve_uv_aspect.py b/magic_uv/op/preserve_uv_aspect.py
index c9ba7204..5b3e50cf 100644
--- a/magic_uv/op/preserve_uv_aspect.py
+++ b/magic_uv/op/preserve_uv_aspect.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
from bpy.props import StringProperty, EnumProperty, BoolProperty
diff --git a/magic_uv/op/select_uv.py b/magic_uv/op/select_uv.py
index 223f9e2f..d80b43a8 100644
--- a/magic_uv/op/select_uv.py
+++ b/magic_uv/op/select_uv.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
from bpy.props import BoolProperty
@@ -30,6 +30,7 @@ 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):
@@ -91,28 +92,42 @@ class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator):
return _is_valid_context(context)
def execute(self, context):
- obj = context.active_object
- 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()
+ objs = [o for o in bpy.data.objects if compat.get_object_select(o)]
+
+ bm_list = []
+ uv_layer_list = []
+ faces_list = []
+ for o in bpy.data.objects:
+ if not compat.get_object_select(o):
+ continue
+ if o.type != 'MESH':
+ continue
+
+ bm = bmesh.from_edit_mesh(o.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]
+ 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, sel_faces,
- uv_layer, 'FACE')
+ overlapped_info = common.get_overlapped_uv_info(bm_list, faces_list,
+ uv_layer_list, 'FACE')
for info in overlapped_info:
if context.tool_settings.use_uv_select_sync:
info["subject_face"].select = True
else:
for l in info["subject_face"].loops:
- l[uv_layer].select = True
+ l[info["subject_uv_layer"]].select = True
- bmesh.update_edit_mesh(obj.data)
+ for o in objs:
+ bmesh.update_edit_mesh(o.data)
return {'FINISHED'}
@@ -136,26 +151,40 @@ class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator):
return _is_valid_context(context)
def execute(self, context):
- obj = context.active_object
- 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()
+ objs = [o for o in bpy.data.objects if compat.get_object_select(o)]
+
+ bm_list = []
+ uv_layer_list = []
+ faces_list = []
+ for o in bpy.data.objects:
+ if not compat.get_object_select(o):
+ continue
+ if o.type != 'MESH':
+ continue
+
+ bm = bmesh.from_edit_mesh(o.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]
+ 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(sel_faces, uv_layer)
+ flipped_info = common.get_flipped_uv_info(faces_list, uv_layer_list)
for info in flipped_info:
if context.tool_settings.use_uv_select_sync:
info["face"].select = True
else:
for l in info["face"].loops:
- l[uv_layer].select = True
+ l[info["uv_layer"]].select = True
- bmesh.update_edit_mesh(obj.data)
+ for o in objs:
+ bmesh.update_edit_mesh(o.data)
return {'FINISHED'}
diff --git a/magic_uv/op/smooth_uv.py b/magic_uv/op/smooth_uv.py
index 17068308..94e41367 100644
--- a/magic_uv/op/smooth_uv.py
+++ b/magic_uv/op/smooth_uv.py
@@ -20,8 +20,8 @@
__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
from bpy.props import BoolProperty, FloatProperty
@@ -167,8 +167,7 @@ class MUV_OT_SmoothUV(bpy.types.Operator):
# get target UV
for i in range(len(accm_uvlens[:-1])):
# get line segment which UV will be placed
- if ((accm_uvlens[i] <= target_length) and
- (accm_uvlens[i + 1] > target_length)):
+ if accm_uvlens[i] <= target_length < accm_uvlens[i + 1]:
tgt_seg_len = target_length - accm_uvlens[i]
seg_len = accm_uvlens[i + 1] - accm_uvlens[i]
uv1 = orig_uvs[i]
@@ -240,8 +239,7 @@ class MUV_OT_SmoothUV(bpy.types.Operator):
# get target UV
for i in range(len(accm_uv[:-1])):
# get line segment to be placed
- if ((accm_uv[i] <= target_length) and
- (accm_uv[i + 1] > target_length)):
+ if accm_uv[i] <= target_length < accm_uv[i + 1]:
tgt_seg_len = target_length - accm_uv[i]
seg_len = accm_uv[i + 1] - accm_uv[i]
uv1 = uvs[i]
diff --git a/magic_uv/op/texture_lock.py b/magic_uv/op/texture_lock.py
index 43d78549..ddcaf315 100644
--- a/magic_uv/op/texture_lock.py
+++ b/magic_uv/op/texture_lock.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import math
from math import atan2, cos, sqrt, sin, fabs
@@ -435,7 +435,7 @@ class MUV_OT_TextureLock_Intr(bpy.types.Operator):
bm.faces.ensure_lookup_table()
prev = set(self.__intr_verts)
- now = set([v.index for v in bm.verts if v.select])
+ now = {v.index for v in bm.verts if v.select}
return prev != now
diff --git a/magic_uv/op/texture_projection.py b/magic_uv/op/texture_projection.py
index 6ef6b1ce..b754dd88 100644
--- a/magic_uv/op/texture_projection.py
+++ b/magic_uv/op/texture_projection.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
from collections import namedtuple
diff --git a/magic_uv/op/texture_wrap.py b/magic_uv/op/texture_wrap.py
index 9936a510..92512438 100644
--- a/magic_uv/op/texture_wrap.py
+++ b/magic_uv/op/texture_wrap.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
from bpy.props import (
diff --git a/magic_uv/op/transfer_uv.py b/magic_uv/op/transfer_uv.py
index b63376c9..ce9639a7 100644
--- a/magic_uv/op/transfer_uv.py
+++ b/magic_uv/op/transfer_uv.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>, Mifth, MaxRobinot"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
from collections import OrderedDict
diff --git a/magic_uv/op/unwrap_constraint.py b/magic_uv/op/unwrap_constraint.py
index bd78dafc..3c23575a 100644
--- a/magic_uv/op/unwrap_constraint.py
+++ b/magic_uv/op/unwrap_constraint.py
@@ -18,8 +18,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
import bpy
from bpy.props import (
diff --git a/magic_uv/op/uv_bounding_box.py b/magic_uv/op/uv_bounding_box.py
index 589abcc4..d4edac9c 100644
--- a/magic_uv/op/uv_bounding_box.py
+++ b/magic_uv/op/uv_bounding_box.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
from enum import IntEnum
import math
@@ -438,10 +438,8 @@ class StateNone(StateBase):
mouse_view.x, mouse_view.y)
for i, p in enumerate(ctrl_points):
px, py = context.region.view2d.view_to_region(p.x, p.y)
- in_cp_x = (px + cp_react_size > x and
- px - cp_react_size < x)
- in_cp_y = (py + cp_react_size > y and
- py - cp_react_size < y)
+ in_cp_x = px - cp_react_size < x < px + cp_react_size
+ in_cp_y = py - cp_react_size < y < py + cp_react_size
if in_cp_x and in_cp_y:
if is_uscaling:
arr = [1, 3, 6, 8]
diff --git a/magic_uv/op/uv_inspection.py b/magic_uv/op/uv_inspection.py
index c5f92004..8aae181e 100644
--- a/magic_uv/op/uv_inspection.py
+++ b/magic_uv/op/uv_inspection.py
@@ -20,8 +20,11 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
+
+import random
+from math import fabs
import bpy
from bpy.props import BoolProperty, EnumProperty
@@ -65,19 +68,31 @@ def _update_uvinsp_info(context):
sc = context.scene
props = sc.muv_props.uv_inspection
- obj = context.active_object
- 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()
+ bm_list = []
+ uv_layer_list = []
+ faces_list = []
+ for o in bpy.data.objects:
+ if not compat.get_object_select(o):
+ continue
+ if o.type != 'MESH':
+ continue
+
+ bm = bmesh.from_edit_mesh(o.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)
- 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]
props.overlapped_info = common.get_overlapped_uv_info(
- bm, sel_faces, uv_layer, sc.muv_uv_inspection_show_mode)
- props.flipped_info = common.get_flipped_uv_info(sel_faces, uv_layer)
+ bm_list, faces_list, uv_layer_list, sc.muv_uv_inspection_show_mode)
+ props.flipped_info = common.get_flipped_uv_info(faces_list, uv_layer_list)
@PropertyClassRegistry()
@@ -205,14 +220,15 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator):
bgl.glColor4f(color[0], color[1], color[2], color[3])
for uv in poly:
x, y = context.region.view2d.view_to_region(
- uv.x, uv.y)
+ uv.x, uv.y, clip=False)
bgl.glVertex2f(x, y)
bgl.glEnd()
elif sc.muv_uv_inspection_show_mode == 'FACE':
bgl.glBegin(bgl.GL_TRIANGLE_FAN)
bgl.glColor4f(color[0], color[1], color[2], color[3])
for uv in info["subject_uvs"]:
- x, y = context.region.view2d.view_to_region(uv.x, uv.y)
+ x, y = context.region.view2d.view_to_region(
+ uv.x, uv.y, clip=False)
bgl.glVertex2f(x, y)
bgl.glEnd()
@@ -226,14 +242,15 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator):
bgl.glColor4f(color[0], color[1], color[2], color[3])
for uv in poly:
x, y = context.region.view2d.view_to_region(
- uv.x, uv.y)
+ uv.x, uv.y, clip=False)
bgl.glVertex2f(x, y)
bgl.glEnd()
elif sc.muv_uv_inspection_show_mode == 'FACE':
bgl.glBegin(bgl.GL_TRIANGLE_FAN)
bgl.glColor4f(color[0], color[1], color[2], color[3])
for uv in info["uvs"]:
- x, y = context.region.view2d.view_to_region(uv.x, uv.y)
+ x, y = context.region.view2d.view_to_region(
+ uv.x, uv.y, clip=False)
bgl.glVertex2f(x, y)
bgl.glEnd()
@@ -279,3 +296,201 @@ class MUV_OT_UVInspection_Update(bpy.types.Operator):
context.area.tag_redraw()
return {'FINISHED'}
+
+
+@BlClassRegistry()
+class MUV_OT_UVInspection_PaintUVIsland(bpy.types.Operator):
+ """
+ Operation class: Paint UV island with random color.
+ """
+
+ bl_idname = "uv.muv_uv_inspection_paint_uv_island"
+ bl_label = "Paint UV Island"
+ bl_description = "Paint UV island with random color"
+ 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_or_new_image(self, name, width, height):
+ if name in bpy.data.images.keys():
+ return bpy.data.images[name]
+ return bpy.data.images.new(name, width, height)
+
+ def _get_or_new_material(self, name):
+ if name in bpy.data.materials.keys():
+ return bpy.data.materials[name]
+ return bpy.data.materials.new(name)
+
+ def _get_or_new_texture(self, name):
+ if name in bpy.data.textures.keys():
+ return bpy.data.textures[name]
+ return bpy.data.textures.new(name, 'IMAGE')
+
+ 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 _create_unique_color(self, exist_colors, allowable=0.1):
+ retry = 0
+ while retry < 20:
+ r = random.random()
+ g = random.random()
+ b = random.random()
+ new_color = [r, g, b]
+ for color in exist_colors:
+ if ((fabs(new_color[0] - color[0]) < allowable) and
+ (fabs(new_color[1] - color[1]) < allowable) and
+ (fabs(new_color[2] - color[2]) < allowable)):
+ break
+ else:
+ return new_color
+ return None
+
+ def execute(self, context):
+ obj = context.active_object
+ mode_orig = context.object.mode
+ 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'}
+
+ # Setup material of drawing target.
+ target_image = self._get_or_new_image(
+ "MagicUV_PaintUVIsland", 4096, 4096)
+ target_mtrl = self._get_or_new_material("MagicUV_PaintUVMaterial")
+ if compat.check_version(2, 80, 0) >= 0:
+ target_mtrl.use_nodes = True
+ output_node = target_mtrl.node_tree.nodes["Material Output"]
+ nodes_to_remove = [n for n in target_mtrl.node_tree.nodes
+ if n != output_node]
+ for n in nodes_to_remove:
+ target_mtrl.node_tree.nodes.remove(n)
+ texture_node = \
+ target_mtrl.node_tree.nodes.new("ShaderNodeTexImage")
+ texture_node.image = target_image
+ target_mtrl.node_tree.links.new(output_node.inputs["Surface"],
+ texture_node.outputs["Color"])
+ obj.data.use_paint_mask = True
+
+ # Apply material to object (all faces).
+ found = False
+ for mtrl_idx, mtrl_slot in enumerate(obj.material_slots):
+ if mtrl_slot.material == target_mtrl:
+ found = True
+ break
+ if not found:
+ bpy.ops.object.material_slot_add()
+ mtrl_idx = len(obj.material_slots) - 1
+ obj.material_slots[mtrl_idx].material = target_mtrl
+ bpy.ops.object.mode_set(mode='EDIT')
+ bm = bmesh.from_edit_mesh(obj.data)
+ bm.faces.ensure_lookup_table()
+ for f in bm.faces:
+ f.select = True
+ bmesh.update_edit_mesh(obj.data)
+ obj.active_material_index = mtrl_idx
+ obj.active_material = target_mtrl
+ bpy.ops.object.material_slot_assign()
+ else:
+ target_tex_slot = target_mtrl.texture_slots.add()
+ target_tex = self._get_or_new_texture("MagicUV_PaintUVTexture")
+ target_tex_slot.texture = target_tex
+ obj.data.use_paint_mask = True
+
+ # Apply material to object (all faces).
+ found = False
+ for mtrl_idx, mtrl_slot in enumerate(obj.material_slots):
+ if mtrl_slot.material == target_mtrl:
+ found = True
+ break
+ if not found:
+ bpy.ops.object.material_slot_add()
+ mtrl_idx = len(obj.material_slots) - 1
+ obj.material_slots[mtrl_idx].material = target_mtrl
+ bpy.ops.object.mode_set(mode='EDIT')
+ bm = bmesh.from_edit_mesh(obj.data)
+ bm.faces.ensure_lookup_table()
+ for f in bm.faces:
+ f.select = True
+ bmesh.update_edit_mesh(obj.data)
+ obj.active_material_index = mtrl_idx
+ obj.active_material = target_mtrl
+ bpy.ops.object.material_slot_assign()
+
+ # Update active image in Image Editor.
+ _, _, space = common.get_space(
+ 'IMAGE_EDITOR', 'WINDOW', 'IMAGE_EDITOR')
+ if space is None:
+ return {'CANCELLED'}
+ space.image = target_image
+
+ # Analyze island to make map between face and paint color.
+ islands = common.get_island_info_from_bmesh(bm)
+ color_to_faces = []
+ for isl in islands:
+ color = self._create_unique_color([c[0] for c in color_to_faces])
+ if color is None:
+ self.report({'WARNING'},
+ "Failed to create color. Please try again")
+ return {'CANCELLED'}
+ indices = [f["face"].index for f in isl["faces"]]
+ color_to_faces.append((color, indices))
+
+ for cf in color_to_faces:
+ # Update selection information.
+ bpy.ops.object.mode_set(mode='EDIT')
+ bm = bmesh.from_edit_mesh(obj.data)
+ bm.faces.ensure_lookup_table()
+ for f in bm.faces:
+ f.select = False
+ for fidx in cf[1]:
+ bm.faces[fidx].select = True
+ bmesh.update_edit_mesh(obj.data)
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # Update brush color.
+ bpy.data.brushes["Fill"].color = cf[0]
+
+ # Paint.
+ bpy.ops.object.mode_set(mode='TEXTURE_PAINT')
+ if compat.check_version(2, 80, 0) >= 0:
+ bpy.ops.paint.brush_select(override_context, image_tool='FILL')
+ else:
+ paint_settings = \
+ bpy.data.scenes['Scene'].tool_settings.image_paint
+ paint_mode_orig = paint_settings.mode
+ paint_canvas_orig = paint_settings.canvas
+ paint_settings.mode = 'IMAGE'
+ paint_settings.canvas = target_image
+ bpy.ops.paint.brush_select(override_context,
+ texture_paint_tool='FILL')
+ bpy.ops.paint.image_paint(override_context, stroke=[{
+ "name": "",
+ "location": (0, 0, 0),
+ "mouse": (0, 0),
+ "size": 0,
+ "pressure": 0,
+ "pen_flip": False,
+ "time": 0,
+ "is_start": False
+ }])
+
+ if compat.check_version(2, 80, 0) < 0:
+ paint_settings.mode = paint_mode_orig
+ paint_settings.canvas = paint_canvas_orig
+
+ bpy.ops.object.mode_set(mode=mode_orig)
+
+ return {'FINISHED'}
diff --git a/magic_uv/op/uv_sculpt.py b/magic_uv/op/uv_sculpt.py
index ff3a9db3..f40ab253 100644
--- a/magic_uv/op/uv_sculpt.py
+++ b/magic_uv/op/uv_sculpt.py
@@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
from math import pi, cos, tan, sin
@@ -168,6 +168,20 @@ class _Properties:
del scene.muv_uv_sculpt_relax_method
+def location_3d_to_region_2d_extra(region, rv3d, coord):
+ coord_2d = view3d_utils.location_3d_to_region_2d(region, rv3d, coord)
+ if coord_2d is None:
+ prj = rv3d.perspective_matrix @ Vector(
+ (coord[0], coord[1], coord[2], 1.0))
+ width_half = region.width / 2.0
+ height_half = region.height / 2.0
+ coord_2d = Vector((
+ width_half + width_half * (prj.x / prj.w),
+ height_half + height_half * (prj.y / prj.w)
+ ))
+ return coord_2d
+
+
@BlClassRegistry()
class MUV_OT_UVSculpt(bpy.types.Operator):
"""
@@ -263,7 +277,7 @@ class MUV_OT_UVSculpt(bpy.types.Operator):
if not f.select:
continue
for i, l in enumerate(f.loops):
- loc_2d = view3d_utils.location_3d_to_region_2d(
+ loc_2d = location_3d_to_region_2d_extra(
region, space.region_3d,
compat.matmul(world_mat, l.vert.co))
diff = loc_2d - self.__initial_mco
@@ -301,7 +315,7 @@ class MUV_OT_UVSculpt(bpy.types.Operator):
if not f.select:
continue
for i, l in enumerate(f.loops):
- loc_2d = view3d_utils.location_3d_to_region_2d(
+ loc_2d = location_3d_to_region_2d_extra(
region, space.region_3d,
compat.matmul(world_mat, l.vert.co))
diff = loc_2d - self.__initial_mco
@@ -393,7 +407,7 @@ class MUV_OT_UVSculpt(bpy.types.Operator):
if not f.select:
continue
for i, l in enumerate(f.loops):
- loc_2d = view3d_utils.location_3d_to_region_2d(
+ loc_2d = location_3d_to_region_2d_extra(
region, space.region_3d,
compat.matmul(world_mat, l.vert.co))
diff = loc_2d - self.__initial_mco
diff --git a/magic_uv/op/uvw.py b/magic_uv/op/uvw.py
index 4b4a4f04..fca72d2c 100644
--- a/magic_uv/op/uvw.py
+++ b/magic_uv/op/uvw.py
@@ -20,8 +20,8 @@
__author__ = "Alexander Milovsky, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
from math import sin, cos, pi
@@ -228,20 +228,26 @@ class MUV_OT_UVW_BoxMap(bpy.types.Operator):
return True
return _is_valid_context(context)
- def execute(self, context):
- obj = context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
- if common.check_version(2, 73, 0) >= 0:
- bm.faces.ensure_lookup_table()
+ def execute(self, _):
+ if compat.check_version(2, 80, 0) < 0:
+ objs = [bpy.context.active_object]
+ else:
+ objs = [o for o in bpy.data.objects
+ if compat.get_object_select(o) and o.type == 'MESH']
+
+ for o in objs:
+ bm = bmesh.from_edit_mesh(o.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
- # get UV layer
- uv_layer = _get_uv_layer(self, bm, self.assign_uvmap)
- if not uv_layer:
- return {'CANCELLED'}
+ # get UV layer
+ uv_layer = _get_uv_layer(self, bm, self.assign_uvmap)
+ if not uv_layer:
+ return {'CANCELLED'}
- _apply_box_map(bm, uv_layer, self.size, self.offset, self.rotation,
- self.tex_aspect)
- bmesh.update_edit_mesh(obj.data)
+ _apply_box_map(bm, uv_layer, self.size, self.offset, self.rotation,
+ self.tex_aspect)
+ bmesh.update_edit_mesh(o.data)
return {'FINISHED'}
@@ -285,20 +291,26 @@ class MUV_OT_UVW_BestPlanerMap(bpy.types.Operator):
return True
return _is_valid_context(context)
- def execute(self, context):
- obj = context.active_object
- bm = bmesh.from_edit_mesh(obj.data)
- if common.check_version(2, 73, 0) >= 0:
- bm.faces.ensure_lookup_table()
+ def execute(self, _):
+ if compat.check_version(2, 80, 0) < 0:
+ objs = [bpy.context.active_object]
+ else:
+ objs = [o for o in bpy.data.objects
+ if compat.get_object_select(o) and o.type == 'MESH']
+
+ for o in objs:
+ bm = bmesh.from_edit_mesh(o.data)
+ if common.check_version(2, 73, 0) >= 0:
+ bm.faces.ensure_lookup_table()
- # get UV layer
- uv_layer = _get_uv_layer(self, bm, self.assign_uvmap)
- if not uv_layer:
- return {'CANCELLED'}
+ # get UV layer
+ uv_layer = _get_uv_layer(self, bm, self.assign_uvmap)
+ if not uv_layer:
+ return {'CANCELLED'}
- _apply_planer_map(bm, uv_layer, self.size, self.offset, self.rotation,
- self.tex_aspect)
+ _apply_planer_map(bm, uv_layer, self.size, self.offset,
+ self.rotation, self.tex_aspect)
- bmesh.update_edit_mesh(obj.data)
+ bmesh.update_edit_mesh(o.data)
return {'FINISHED'}
diff --git a/magic_uv/op/world_scale_uv.py b/magic_uv/op/world_scale_uv.py
index 0107fc6f..9ed86eb0 100644
--- a/magic_uv/op/world_scale_uv.py
+++ b/magic_uv/op/world_scale_uv.py
@@ -20,8 +20,8 @@
__author__ = "McBuff, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
-__version__ = "6.2"
-__date__ = "31 Jul 2019"
+__version__ = "6.3"
+__date__ = "10 Aug 2020"
from math import sqrt
@@ -31,7 +31,6 @@ from bpy.props import (
FloatProperty,
IntVectorProperty,
BoolProperty,
- StringProperty,
)
import bmesh
from mathutils import Vector
@@ -63,9 +62,34 @@ def _is_valid_context(context):
return True
-def _measure_wsuv_info(obj, method='FIRST', tex_size=None):
- mesh_area = common.measure_mesh_area(obj)
- uv_area = common.measure_uv_area(obj, method, tex_size)
+def _measure_wsuv_info(obj, calc_method='MESH',
+ tex_selection_method='FIRST', tex_size=None,
+ only_selected=True):
+ mesh_areas = common.measure_mesh_area(obj, calc_method, only_selected)
+ uv_areas = common.measure_uv_area(obj, calc_method, tex_selection_method,
+ tex_size, only_selected)
+
+ if not uv_areas:
+ return None, mesh_areas, None
+
+ if len(mesh_areas) != len(uv_areas):
+ raise ValueError("mesh_area and uv_area must be same length")
+
+ densities = []
+ for mesh_area, uv_area in zip(mesh_areas, uv_areas):
+ if mesh_area == 0.0:
+ densities.append(0.0)
+ else:
+ densities.append(sqrt(uv_area) / sqrt(mesh_area))
+
+ return uv_areas, mesh_areas, densities
+
+
+def _measure_wsuv_info_from_faces(obj, faces, uv_layer, tex_layer,
+ tex_selection_method='FIRST', tex_size=None):
+ mesh_area = common.measure_mesh_area_from_faces(faces)
+ uv_area = common.measure_uv_area_from_faces(
+ obj, faces, uv_layer, tex_layer, tex_selection_method, tex_size)
if not uv_area:
return None, mesh_area, None
@@ -78,22 +102,12 @@ def _measure_wsuv_info(obj, method='FIRST', tex_size=None):
return uv_area, mesh_area, density
-def _apply(obj, origin, factor):
- 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()
-
- sel_faces = [f for f in bm.faces if f.select]
-
- uv_layer = bm.loops.layers.uv.verify()
-
+def _apply(faces, uv_layer, origin, factor):
# calculate origin
if origin == 'CENTER':
origin = Vector((0.0, 0.0))
num = 0
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin = origin + uv
@@ -101,7 +115,7 @@ def _apply(obj, origin, factor):
origin = origin / num
elif origin == 'LEFT_TOP':
origin = Vector((100000.0, -100000.0))
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
@@ -109,7 +123,7 @@ def _apply(obj, origin, factor):
elif origin == 'LEFT_CENTER':
origin = Vector((100000.0, 0.0))
num = 0
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
@@ -118,7 +132,7 @@ def _apply(obj, origin, factor):
origin.y = origin.y / num
elif origin == 'LEFT_BOTTOM':
origin = Vector((100000.0, 100000.0))
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
@@ -126,7 +140,7 @@ def _apply(obj, origin, factor):
elif origin == 'CENTER_TOP':
origin = Vector((0.0, -100000.0))
num = 0
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = origin.x + uv.x
@@ -136,7 +150,7 @@ def _apply(obj, origin, factor):
elif origin == 'CENTER_BOTTOM':
origin = Vector((0.0, 100000.0))
num = 0
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = origin.x + uv.x
@@ -145,7 +159,7 @@ def _apply(obj, origin, factor):
origin.x = origin.x / num
elif origin == 'RIGHT_TOP':
origin = Vector((-100000.0, -100000.0))
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
@@ -153,7 +167,7 @@ def _apply(obj, origin, factor):
elif origin == 'RIGHT_CENTER':
origin = Vector((-100000.0, 0.0))
num = 0
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
@@ -162,21 +176,19 @@ def _apply(obj, origin, factor):
origin.y = origin.y / num
elif origin == 'RIGHT_BOTTOM':
origin = Vector((-100000.0, 100000.0))
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
origin.y = min(origin.y, uv.y)
# update UV coordinate
- for f in sel_faces:
+ for f in faces:
for l in f.loops:
uv = l[uv_layer].uv
diff = uv - origin
l[uv_layer].uv = origin + diff * factor
- bmesh.update_edit_mesh(obj.data)
-
def _get_target_textures(_, __):
images = common.find_images(bpy.context.active_object)
@@ -207,7 +219,8 @@ class _Properties:
)
scene.muv_world_scale_uv_src_uv_area = FloatProperty(
name="UV Area",
- description="Source UV Area",
+ description="Source UV Area (Average if calculation method is UV "
+ "Island or Face)",
default=0.0,
min=0.0
)
@@ -277,6 +290,26 @@ class _Properties:
description="Texture to be applied",
items=_get_target_textures
)
+ scene.muv_world_scale_uv_tgt_area_calc_method = EnumProperty(
+ name="Area Calculation Method",
+ description="How to calculate target area",
+ items=[
+ ('MESH', "Mesh", "Calculate area by whole faces in mesh"),
+ ('UV ISLAND', "UV Island", "Calculate area each UV islands"),
+ ('FACE', "Face", "Calculate area each face")
+ ],
+ default='MESH'
+ )
+ scene.muv_world_scale_uv_measure_only_selected = BoolProperty(
+ name="Only Selected",
+ description="Measure with only selected faces",
+ default=True,
+ )
+ scene.muv_world_scale_uv_apply_only_selected = BoolProperty(
+ name="Only Selected",
+ description="Apply to only selected faces",
+ default=True,
+ )
@classmethod
def del_props(cls, scene):
@@ -290,6 +323,9 @@ class _Properties:
del scene.muv_world_scale_uv_origin
del scene.muv_world_scale_uv_measure_tgt_texture
del scene.muv_world_scale_uv_apply_tgt_texture
+ del scene.muv_world_scale_uv_tgt_area_calc_method
+ del scene.muv_world_scale_uv_measure_only_selected
+ del scene.muv_world_scale_uv_apply_only_selected
@BlClassRegistry()
@@ -304,10 +340,15 @@ class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator):
bl_description = "Measure face size for scale calculation"
bl_options = {'REGISTER', 'UNDO'}
- tgt_texture = StringProperty(
+ tgt_texture = EnumProperty(
name="Texture",
- description="Texture to be measured",
- default="[Average]"
+ description="Texture to be applied",
+ items=_get_target_textures
+ )
+ only_selected = BoolProperty(
+ name="Only Selected",
+ description="Measure with only selected faces",
+ default=True,
)
@classmethod
@@ -317,32 +358,44 @@ class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator):
return True
return _is_valid_context(context)
+ @staticmethod
+ def setup_argument(ops, scene):
+ ops.tgt_texture = scene.muv_world_scale_uv_measure_tgt_texture
+ ops.only_selected = scene.muv_world_scale_uv_measure_only_selected
+
def execute(self, context):
sc = context.scene
obj = context.active_object
if self.tgt_texture == "[Average]":
- uv_area, mesh_area, density = _measure_wsuv_info(obj, 'AVERAGE')
+ uv_areas, mesh_areas, densities = _measure_wsuv_info(
+ obj, calc_method='MESH', tex_selection_method='AVERAGE',
+ only_selected=self.only_selected)
elif self.tgt_texture == "[Max]":
- uv_area, mesh_area, density = _measure_wsuv_info(obj, 'MAX')
+ uv_areas, mesh_areas, densities = _measure_wsuv_info(
+ obj, calc_method='MESH', tex_selection_method='MAX',
+ only_selected=self.only_selected)
elif self.tgt_texture == "[Min]":
- uv_area, mesh_area, density = _measure_wsuv_info(obj, 'MIN')
+ uv_areas, mesh_areas, densities = _measure_wsuv_info(
+ obj, calc_method='MESH', tex_selection_method='MIN',
+ only_selected=self.only_selected)
else:
texture = bpy.data.images[self.tgt_texture]
- uv_area, mesh_area, density = _measure_wsuv_info(
- obj, 'USER_SPECIFIED', texture.size)
- if not uv_area:
+ uv_areas, mesh_areas, densities = _measure_wsuv_info(
+ obj, calc_method='MESH', tex_selection_method='USER_SPECIFIED',
+ only_selected=self.only_selected, tex_size=texture.size)
+ if not uv_areas:
self.report({'WARNING'},
"Object must have more than one UV map and texture")
return {'CANCELLED'}
- sc.muv_world_scale_uv_src_uv_area = uv_area
- sc.muv_world_scale_uv_src_mesh_area = mesh_area
- sc.muv_world_scale_uv_src_density = density
+ sc.muv_world_scale_uv_src_uv_area = uv_areas[0]
+ sc.muv_world_scale_uv_src_mesh_area = mesh_areas[0]
+ sc.muv_world_scale_uv_src_density = densities[0]
self.report({'INFO'},
"UV Area: {0}, Mesh Area: {1}, Texel Density: {2}"
- .format(uv_area, mesh_area, density))
+ .format(uv_areas[0], mesh_areas[0], densities[0]))
return {'FINISHED'}
@@ -395,6 +448,21 @@ class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator):
default=True,
options={'HIDDEN', 'SKIP_SAVE'}
)
+ tgt_area_calc_method = EnumProperty(
+ name="Area Calculation Method",
+ description="How to calculate target area",
+ items=[
+ ('MESH', "Mesh", "Calculate area by whole faces in mesh"),
+ ('UV ISLAND', "UV Island", "Calculate area each UV islands"),
+ ('FACE', "Face", "Calculate area each face")
+ ],
+ default='MESH'
+ )
+ only_selected = BoolProperty(
+ name="Only Selected",
+ description="Apply to only selected faces",
+ default=True,
+ )
@classmethod
def poll(cls, context):
@@ -403,6 +471,16 @@ class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator):
return True
return _is_valid_context(context)
+ @staticmethod
+ def setup_argument(ops, scene):
+ ops.tgt_density = scene.muv_world_scale_uv_tgt_density
+ ops.tgt_texture_size = scene.muv_world_scale_uv_tgt_texture_size
+ ops.origin = scene.muv_world_scale_uv_origin
+ ops.show_dialog = False
+ ops.tgt_area_calc_method = \
+ scene.muv_world_scale_uv_tgt_area_calc_method
+ ops.only_selected = scene.muv_world_scale_uv_apply_only_selected
+
def __apply_manual(self, context):
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
@@ -411,27 +489,47 @@ class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator):
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
- tex_size = self.tgt_texture_size
- uv_area, _, density = _measure_wsuv_info(obj, 'USER_SPECIFIED',
- tex_size)
- if not uv_area:
+ if not bm.loops.layers.uv:
self.report({'WARNING'}, "Object must have more than one UV map")
return {'CANCELLED'}
+ uv_layer = bm.loops.layers.uv.verify()
+ tex_layer = common.find_texture_layer(bm)
+ faces_list = common.get_faces_list(
+ bm, self.tgt_area_calc_method, self.only_selected)
+
+ tex_size = self.tgt_texture_size
+
+ factors = []
+ for faces in faces_list:
+ uv_area, _, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='USER_SPECIFIED', tex_size=tex_size)
- tgt_density = self.tgt_density
- factor = tgt_density / density
+ if not uv_area:
+ self.report({'WARNING'},
+ "Object must have more than one UV map")
+ return {'CANCELLED'}
- _apply(context.active_object, self.origin, factor)
- self.report({'INFO'}, "Scaling factor: {0}".format(factor))
+ tgt_density = self.tgt_density
+ factor = tgt_density / density
+
+ _apply(faces, uv_layer, self.origin, factor)
+ factors.append(factor)
+
+ bmesh.update_edit_mesh(obj.data)
+ self.report({'INFO'}, "Scaling factor: {0}".format(factors))
return {'FINISHED'}
def draw(self, _):
layout = self.layout
- layout.prop(self, "tgt_density")
+ layout.label(text="Target:")
+ layout.prop(self, "only_selected")
layout.prop(self, "tgt_texture_size")
+ layout.prop(self, "tgt_density")
layout.prop(self, "origin")
+ layout.prop(self, "tgt_area_calc_method")
layout.separator()
@@ -500,10 +598,25 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator):
default=True,
options={'HIDDEN', 'SKIP_SAVE'}
)
- tgt_texture = StringProperty(
+ tgt_texture = EnumProperty(
name="Texture",
description="Texture to be applied",
- default="[Average]"
+ items=_get_target_textures
+ )
+ tgt_area_calc_method = EnumProperty(
+ name="Area Calculation Method",
+ description="How to calculate target area",
+ items=[
+ ('MESH', "Mesh", "Calculate area by whole faces in mesh"),
+ ('UV ISLAND', "UV Island", "Calculate area each UV islands"),
+ ('FACE', "Face", "Calculate area each face")
+ ],
+ default='MESH'
+ )
+ only_selected = BoolProperty(
+ name="Only Selected",
+ description="Apply to only selected faces",
+ default=True,
)
@classmethod
@@ -513,6 +626,19 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator):
return True
return _is_valid_context(context)
+ @staticmethod
+ def setup_argument(ops, scene):
+ ops.tgt_scaling_factor = \
+ scene.muv_world_scale_uv_tgt_scaling_factor
+ ops.origin = scene.muv_world_scale_uv_origin
+ ops.src_density = scene.muv_world_scale_uv_src_density
+ ops.same_density = False
+ ops.show_dialog = False
+ ops.tgt_texture = scene.muv_world_scale_uv_apply_tgt_texture
+ ops.tgt_area_calc_method = \
+ scene.muv_world_scale_uv_tgt_area_calc_method
+ ops.only_selected = scene.muv_world_scale_uv_apply_only_selected
+
def __apply_scaling_density(self, context):
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
@@ -521,26 +647,49 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator):
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
- if self.tgt_texture == "[Average]":
- uv_area, _, density = _measure_wsuv_info(obj, 'AVERAGE')
- elif self.tgt_texture == "[Max]":
- uv_area, _, density = _measure_wsuv_info(obj, 'MAX')
- elif self.tgt_texture == "[Min]":
- uv_area, _, density = _measure_wsuv_info(obj, 'MIN')
- else:
- tgt_texture = bpy.data.images[self.tgt_texture]
- uv_area, _, density = _measure_wsuv_info(obj, 'USER_SPECIFIED',
- tgt_texture.size)
- if not uv_area:
- self.report({'WARNING'},
- "Object must have more than one UV map and texture")
+ if not bm.loops.layers.uv:
+ self.report({'WARNING'}, "Object must have more than one UV map")
return {'CANCELLED'}
+ uv_layer = bm.loops.layers.uv.verify()
+ tex_layer = common.find_texture_layer(bm)
+ faces_list = common.get_faces_list(
+ bm, self.tgt_area_calc_method, self.only_selected)
+
+ factors = []
+ for faces in faces_list:
+ if self.tgt_texture == "[Average]":
+ uv_area, _, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='AVERAGE')
+ elif self.tgt_texture == "[Max]":
+ uv_area, _, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='MAX')
+ elif self.tgt_texture == "[Min]":
+ uv_area, _, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='MIN')
+ else:
+ tgt_texture = bpy.data.images[self.tgt_texture]
+ uv_area, _, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='USER_SPECIFIED',
+ tex_size=tgt_texture.size)
+
+ if not uv_area:
+ self.report({'WARNING'},
+ "Object must have more than one UV map and "
+ "texture")
+ return {'CANCELLED'}
- tgt_density = self.src_density * self.tgt_scaling_factor
- factor = tgt_density / density
+ tgt_density = self.src_density * self.tgt_scaling_factor
+ factor = tgt_density / density
- _apply(context.active_object, self.origin, factor)
- self.report({'INFO'}, "Scaling factor: {0}".format(factor))
+ _apply(faces, uv_layer, self.origin, factor)
+ factors.append(factor)
+
+ bmesh.update_edit_mesh(obj.data)
+ self.report({'INFO'}, "Scaling factor: {0}".format(factors))
return {'FINISHED'}
@@ -554,9 +703,13 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator):
layout.separator()
+ layout.label(text="Target:")
if not self.same_density:
layout.prop(self, "tgt_scaling_factor")
+ layout.prop(self, "only_selected")
+ layout.prop(self, "tgt_texture")
layout.prop(self, "origin")
+ layout.prop(self, "tgt_area_calc_method")
layout.separator()
@@ -640,10 +793,25 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator):
default=True,
options={'HIDDEN', 'SKIP_SAVE'}
)
- tgt_texture = StringProperty(
+ tgt_texture = EnumProperty(
name="Texture",
description="Texture to be applied",
- default="[Average]"
+ items=_get_target_textures
+ )
+ tgt_area_calc_method = EnumProperty(
+ name="Area Calculation Method",
+ description="How to calculate target area",
+ items=[
+ ('MESH', "Mesh", "Calculate area by whole faces in mesh"),
+ ('UV ISLAND', "UV Island", "Calculate area each UV islands"),
+ ('FACE', "Face", "Calculate area each face")
+ ],
+ default='MESH'
+ )
+ only_selected = BoolProperty(
+ name="Only Selected",
+ description="Apply to only selected faces",
+ default=True,
)
@classmethod
@@ -653,6 +821,18 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator):
return True
return _is_valid_context(context)
+ @staticmethod
+ def setup_argument(ops, scene):
+ ops.origin = scene.muv_world_scale_uv_origin
+ ops.src_density = scene.muv_world_scale_uv_src_density
+ ops.src_uv_area = scene.muv_world_scale_uv_src_uv_area
+ ops.src_mesh_area = scene.muv_world_scale_uv_src_mesh_area
+ ops.show_dialog = False
+ ops.tgt_texture = scene.muv_world_scale_uv_apply_tgt_texture
+ ops.tgt_area_calc_method = \
+ scene.muv_world_scale_uv_tgt_area_calc_method
+ ops.only_selected = scene.muv_world_scale_uv_apply_only_selected
+
def __apply_proportional_to_mesh(self, context):
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
@@ -661,28 +841,49 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator):
bm.edges.ensure_lookup_table()
bm.faces.ensure_lookup_table()
- if self.tgt_texture == "[Average]":
- uv_area, mesh_area, density = _measure_wsuv_info(obj, 'AVERAGE')
- elif self.tgt_texture == "[Max]":
- uv_area, mesh_area, density = _measure_wsuv_info(obj, 'MAX')
- elif self.tgt_texture == "[Min]":
- uv_area, mesh_area, density = _measure_wsuv_info(obj, 'MIN')
- else:
- tgt_texture = bpy.data.images[self.tgt_texture]
- uv_area, mesh_area, density = _measure_wsuv_info(
- obj, 'USER_SPECIFIED', tgt_texture.size)
- if not uv_area:
- self.report({'WARNING'},
- "Object must have more than one UV map and texture")
+ if not bm.loops.layers.uv:
+ self.report({'WARNING'}, "Object must have more than one UV map")
return {'CANCELLED'}
-
- tgt_density = self.src_density * sqrt(mesh_area) / sqrt(
- self.src_mesh_area)
-
- factor = tgt_density / density
-
- _apply(context.active_object, self.origin, factor)
- self.report({'INFO'}, "Scaling factor: {0}".format(factor))
+ uv_layer = bm.loops.layers.uv.verify()
+ tex_layer = common.find_texture_layer(bm)
+ faces_list = common.get_faces_list(
+ bm, self.tgt_area_calc_method, self.only_selected)
+
+ factors = []
+ for faces in faces_list:
+ if self.tgt_texture == "[Average]":
+ uv_area, mesh_area, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='AVERAGE')
+ elif self.tgt_texture == "[Max]":
+ uv_area, mesh_area, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='MAX')
+ elif self.tgt_texture == "[Min]":
+ uv_area, mesh_area, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='MIN')
+ else:
+ tgt_texture = bpy.data.images[self.tgt_texture]
+ uv_area, mesh_area, density = _measure_wsuv_info_from_faces(
+ obj, faces, uv_layer, tex_layer,
+ tex_selection_method='USER_SPECIFIED',
+ tex_size=tgt_texture.size)
+ if not uv_area:
+ self.report({'WARNING'},
+ "Object must have more than one UV map and "
+ "texture")
+ return {'CANCELLED'}
+
+ tgt_density = self.src_density * sqrt(mesh_area) / sqrt(
+ self.src_mesh_area)
+ factor = tgt_density / density
+
+ _apply(faces, uv_layer, self.origin, factor)
+ factors.append(factor)
+
+ bmesh.update_edit_mesh(obj.data)
+ self.report({'INFO'}, "Scaling factor: {0}".format(factors))
return {'FINISHED'}
@@ -697,7 +898,12 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator):
col.enabled = False
layout.separator()
+
+ layout.label(text="Target:")
+ layout.prop(self, "only_selected")
layout.prop(self, "origin")
+ layout.prop(self, "tgt_area_calc_method")
+ layout.prop(self, "tgt_texture")
layout.separator()