diff options
author | nutti <nutti.metro@gmail.com> | 2019-01-15 08:56:07 +0300 |
---|---|---|
committer | nutti <nutti.metro@gmail.com> | 2019-01-15 08:56:07 +0300 |
commit | 4fd7be071b037562c23cf4711abd54dffb667fdf (patch) | |
tree | 1126cadc9e97cbb9a2c6c437bb621e7bc6c2c00a /uv_magic_uv/op/world_scale_uv.py | |
parent | d3ab0ed74a2532faec5cc87dbd2b93d59fbbdae0 (diff) |
Magic UV: Fix error raised at UserPreferences and remove legacy codes.
Diffstat (limited to 'uv_magic_uv/op/world_scale_uv.py')
-rw-r--r-- | uv_magic_uv/op/world_scale_uv.py | 384 |
1 files changed, 336 insertions, 48 deletions
diff --git a/uv_magic_uv/op/world_scale_uv.py b/uv_magic_uv/op/world_scale_uv.py index a957d5d4..ae46e4a9 100644 --- a/uv_magic_uv/op/world_scale_uv.py +++ b/uv_magic_uv/op/world_scale_uv.py @@ -23,6 +23,7 @@ __status__ = "production" __version__ = "5.2" __date__ = "17 Nov 2018" +from math import sqrt import bpy from bpy.props import ( @@ -31,14 +32,153 @@ from bpy.props import ( IntVectorProperty, BoolProperty, ) +import bmesh +from mathutils import Vector +from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry -from ..impl import world_scale_uv_impl as impl +from ..utils import compatibility as compat + + +def _is_valid_context(context): + obj = context.object + + # only edit mode is allowed to execute + if obj is None: + return False + if obj.type != 'MESH': + return False + if context.object.mode != 'EDIT': + return False + + # only 'VIEW_3D' space is allowed to execute + for space in context.area.spaces: + if space.type == 'VIEW_3D': + break + else: + return False + + return True + + +def _measure_wsuv_info(obj, tex_size=None): + mesh_area = common.measure_mesh_area(obj) + uv_area = common.measure_uv_area(obj, tex_size) + + if not uv_area: + return None, mesh_area, None + + if mesh_area == 0.0: + density = 0.0 + else: + density = sqrt(uv_area) / sqrt(mesh_area) + + 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() + + # calculate origin + if origin == 'CENTER': + origin = Vector((0.0, 0.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin = origin + uv + num = num + 1 + origin = origin / num + elif origin == 'LEFT_TOP': + origin = Vector((100000.0, -100000.0)) + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = max(origin.y, uv.y) + elif origin == 'LEFT_CENTER': + origin = Vector((100000.0, 0.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = origin.y + uv.y + num = num + 1 + origin.y = origin.y / num + elif origin == 'LEFT_BOTTOM': + origin = Vector((100000.0, 100000.0)) + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = min(origin.x, uv.x) + origin.y = min(origin.y, uv.y) + elif origin == 'CENTER_TOP': + origin = Vector((0.0, -100000.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = origin.x + uv.x + origin.y = max(origin.y, uv.y) + num = num + 1 + origin.x = origin.x / num + elif origin == 'CENTER_BOTTOM': + origin = Vector((0.0, 100000.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = origin.x + uv.x + origin.y = min(origin.y, uv.y) + num = num + 1 + origin.x = origin.x / num + elif origin == 'RIGHT_TOP': + origin = Vector((-100000.0, -100000.0)) + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = max(origin.x, uv.x) + origin.y = max(origin.y, uv.y) + elif origin == 'RIGHT_CENTER': + origin = Vector((-100000.0, 0.0)) + num = 0 + for f in sel_faces: + for l in f.loops: + uv = l[uv_layer].uv + origin.x = max(origin.x, uv.x) + origin.y = origin.y + uv.y + num = num + 1 + origin.y = origin.y / num + elif origin == 'RIGHT_BOTTOM': + origin = Vector((-100000.0, 100000.0)) + for f in sel_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 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) @PropertyClassRegistry() -class Properties: +class _Properties: idname = "world_scale_uv" @classmethod @@ -140,18 +280,35 @@ class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator): bl_description = "Measure face size for scale calculation" bl_options = {'REGISTER', 'UNDO'} - def __init__(self): - self.__impl = impl.MeasureImpl() - @classmethod def poll(cls, context): - return impl.MeasureImpl.poll(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): - return self.__impl.execute(self, context) + sc = context.scene + obj = context.active_object + + uv_area, mesh_area, density = _measure_wsuv_info(obj) + if not uv_area: + 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 + + self.report({'INFO'}, "UV Area: {0}, Mesh Area: {1}, Texel Density: {2}" + .format(uv_area, mesh_area, density)) + + return {'FINISHED'} @BlClassRegistry() +@compat.make_annotations class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): """ Operation class: Apply scaled UV (Manual) @@ -162,20 +319,20 @@ class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): bl_description = "Apply scaled UV based on user specification" bl_options = {'REGISTER', 'UNDO'} - tgt_density: FloatProperty( + tgt_density = FloatProperty( name="Density", description="Target Texel Density", default=1.0, min=0.0 ) - tgt_texture_size: IntVectorProperty( + tgt_texture_size = IntVectorProperty( name="Texture Size", size=2, min=1, soft_max=10240, default=(1024, 1024), ) - origin: EnumProperty( + origin = EnumProperty( name="Origin", description="Aspect Origin", items=[ @@ -192,31 +349,64 @@ class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): ], default='CENTER' ) - show_dialog: BoolProperty( + show_dialog = BoolProperty( name="Show Diaglog Menu", description="Show dialog menu if true", default=True, options={'HIDDEN', 'SKIP_SAVE'} ) - def __init__(self): - self.__impl = impl.ApplyManualImpl() - @classmethod def poll(cls, context): - return impl.ApplyManualImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + def __apply_manual(self, context): + obj = context.active_object + 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() + + tex_size = self.tgt_texture_size + uv_area, _, density = _measure_wsuv_info(obj, tex_size) + if not uv_area: + self.report({'WARNING'}, "Object must have more than one UV map") + return {'CANCELLED'} + + tgt_density = self.tgt_density + factor = tgt_density / density + + _apply(context.active_object, self.origin, factor) + self.report({'INFO'}, "Scaling factor: {0}".format(factor)) + + return {'FINISHED'} - def draw(self, context): - self.__impl.draw(self, context) + def draw(self, _): + layout = self.layout - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) + layout.prop(self, "tgt_density") + layout.prop(self, "tgt_texture_size") + layout.prop(self, "origin") + + layout.separator() + + def invoke(self, context, _): + if self.show_dialog: + wm = context.window_manager + return wm.invoke_props_dialog(self) + + return self.execute(context) def execute(self, context): - return self.__impl.execute(self, context) + return self.__apply_manual(context) @BlClassRegistry() +@compat.make_annotations class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): """ Operation class: Apply scaled UV (Scaling Density) @@ -227,13 +417,13 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): bl_description = "Apply scaled UV with scaling density" bl_options = {'REGISTER', 'UNDO'} - tgt_scaling_factor: FloatProperty( + tgt_scaling_factor = FloatProperty( name="Scaling Factor", default=1.0, max=1000.0, min=0.00001 ) - origin: EnumProperty( + origin = EnumProperty( name="Origin", description="Aspect Origin", items=[ @@ -250,44 +440,97 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): ], default='CENTER' ) - src_density: FloatProperty( + src_density = FloatProperty( name="Density", description="Source Texel Density", default=0.0, min=0.0, options={'HIDDEN'} ) - same_density: BoolProperty( + same_density = BoolProperty( name="Same Density", description="Apply same density", default=False, options={'HIDDEN'} ) - show_dialog: BoolProperty( + show_dialog = BoolProperty( name="Show Diaglog Menu", description="Show dialog menu if true", default=True, options={'HIDDEN', 'SKIP_SAVE'} ) - def __init__(self): - self.__impl = impl.ApplyScalingDensityImpl() - @classmethod def poll(cls, context): - return impl.ApplyScalingDensityImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + def __apply_scaling_density(self, context): + obj = context.active_object + 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() + + uv_area, _, density = _measure_wsuv_info(obj) + 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 + + _apply(context.active_object, self.origin, factor) + self.report({'INFO'}, "Scaling factor: {0}".format(factor)) + + return {'FINISHED'} + + def draw(self, _): + layout = self.layout - def draw(self, context): - self.__impl.draw(self, context) + layout.label(text="Source:") + col = layout.column() + col.prop(self, "src_density") + col.enabled = False - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) + layout.separator() + + if not self.same_density: + layout.prop(self, "tgt_scaling_factor") + layout.prop(self, "origin") + + layout.separator() + + def invoke(self, context, _): + sc = context.scene + + if self.show_dialog: + wm = context.window_manager + + if self.same_density: + self.tgt_scaling_factor = 1.0 + else: + self.tgt_scaling_factor = \ + sc.muv_world_scale_uv_tgt_scaling_factor + self.src_density = sc.muv_world_scale_uv_src_density + + return wm.invoke_props_dialog(self) + + return self.execute(context) def execute(self, context): - return self.__impl.execute(self, context) + if self.same_density: + self.tgt_scaling_factor = 1.0 + + return self.__apply_scaling_density(context) @BlClassRegistry() +@compat.make_annotations class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): """ Operation class: Apply scaled UV (Proportional to mesh) @@ -298,7 +541,7 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): bl_description = "Apply scaled UV proportionaled to mesh" bl_options = {'REGISTER', 'UNDO'} - origin: EnumProperty( + origin = EnumProperty( name="Origin", description="Aspect Origin", items=[ @@ -315,46 +558,91 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): ], default='CENTER' ) - src_density: FloatProperty( + src_density = FloatProperty( name="Source Density", description="Source Texel Density", default=0.0, min=0.0, options={'HIDDEN'} ) - src_uv_area: FloatProperty( + src_uv_area = FloatProperty( name="Source UV Area", description="Source UV Area", default=0.0, min=0.0, options={'HIDDEN'} ) - src_mesh_area: FloatProperty( + src_mesh_area = FloatProperty( name="Source Mesh Area", description="Source Mesh Area", default=0.0, min=0.0, options={'HIDDEN'} ) - show_dialog: BoolProperty( + show_dialog = BoolProperty( name="Show Diaglog Menu", description="Show dialog menu if true", default=True, options={'HIDDEN', 'SKIP_SAVE'} ) - def __init__(self): - self.__impl = impl.ApplyProportionalToMeshImpl() - @classmethod def poll(cls, context): - return impl.ApplyProportionalToMeshImpl.poll(context) + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return _is_valid_context(context) + + def __apply_proportional_to_mesh(self, context): + obj = context.active_object + 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() + + uv_area, mesh_area, density = _measure_wsuv_info(obj) + 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(context.active_object, self.origin, factor) + self.report({'INFO'}, "Scaling factor: {0}".format(factor)) + + return {'FINISHED'} + + def draw(self, _): + layout = self.layout + + layout.label(text="Source:") + col = layout.column(align=True) + col.prop(self, "src_density") + col.prop(self, "src_uv_area") + col.prop(self, "src_mesh_area") + col.enabled = False + + layout.separator() + layout.prop(self, "origin") + + layout.separator() + + def invoke(self, context, _): + if self.show_dialog: + wm = context.window_manager + sc = context.scene + + self.src_density = sc.muv_world_scale_uv_src_density + self.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area - def draw(self, context): - self.__impl.draw(self, context) + return wm.invoke_props_dialog(self) - def invoke(self, context, event): - return self.__impl.invoke(self, context, event) + return self.execute(context) def execute(self, context): - return self.__impl.execute(self, context) + return self.__apply_proportional_to_mesh(context) |