[Bf-blender-cvs] [20daaeffce4] master: UV: add new operator, uvcalc_align_rotation

Chris Blackbourn noreply at git.blender.org
Wed Sep 7 06:27:32 CEST 2022


Commit: 20daaeffce4cf9bfe48ab7c84cb9e2b1d71d2c91
Author: Chris Blackbourn
Date:   Wed Sep 7 13:22:45 2022 +1200
Branches: master
https://developer.blender.org/rB20daaeffce4cf9bfe48ab7c84cb9e2b1d71d2c91

UV: add new operator, uvcalc_align_rotation

Adds a new operator to automatically rotate UV Islands into alignment.

Modes:
* Auto (All edges)
* Geometry (V direction will point in geometry direction) [1]
* Edge (Rotate until selected edge is in V direction)

Also adds uv_sync_selection support to UV Randomize Transform.

Resolves: T78399
Differential Revision: https://developer.blender.org/D15820

[1] Listed as "World" in Task description.

===================================================================

M	release/scripts/startup/bl_operators/__init__.py
D	release/scripts/startup/bl_operators/uvcalc_randomize_transform.py
A	release/scripts/startup/bl_operators/uvcalc_transform.py
M	release/scripts/startup/bl_ui/space_image.py

===================================================================

diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index 6f61d7e7129..de0b7798072 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -31,7 +31,7 @@ _modules = [
     "userpref",
     "uvcalc_follow_active",
     "uvcalc_lightmap",
-    "uvcalc_randomize_transform",
+    "uvcalc_transform",
     "vertexpaint_dirt",
     "view3d",
     "wm",
diff --git a/release/scripts/startup/bl_operators/uvcalc_randomize_transform.py b/release/scripts/startup/bl_operators/uvcalc_randomize_transform.py
deleted file mode 100644
index 2867164a72e..00000000000
--- a/release/scripts/startup/bl_operators/uvcalc_randomize_transform.py
+++ /dev/null
@@ -1,212 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-from bpy.types import Operator
-from mathutils import Vector
-
-import math
-
-
-def get_random_transform(transform_params, entropy):
-    from random import uniform
-    from random import seed as random_seed
-
-    (seed, loc, rot, scale, scale_even) = transform_params
-
-    # First, seed the RNG.
-    random_seed(seed + entropy)
-
-    # Next, call uniform a known number of times.
-    offset_u = uniform(0, 1)
-    offset_v = uniform(0, 1)
-    angle = uniform(0, 1)
-    scale_u = uniform(0, 1)
-    scale_v = uniform(0, 1)
-
-    # Apply the transform_params.
-    if loc:
-        offset_u *= loc[0]
-        offset_v *= loc[1]
-    else:
-        offset_u = 0
-        offset_v = 0
-
-    if rot:
-        angle *= rot
-    else:
-        angle = 0
-
-    if scale:
-        scale_u = scale_u * (2 * scale[0] - 2.0) + 2.0 - scale[0]
-        scale_v = scale_v * (2 * scale[1] - 2.0) + 2.0 - scale[1]
-    else:
-        scale_u = 1
-        scale_v = 1
-
-    if scale_even:
-        scale_v = scale_u
-
-    # Results in homogenous co-ordinates.
-    return [[scale_u * math.cos(angle), -scale_v * math.sin(angle), offset_u],
-            [scale_u * math.sin(angle), scale_v * math.cos(angle), offset_v]]
-
-
-def randomize_uv_transform_island(bm, uv_layer, faces, transform_params):
-    # Ensure consistent random values for island, regardless of selection etc.
-    entropy = min(f.index for f in faces)
-
-    transform = get_random_transform(transform_params, entropy)
-
-    # Find bounding box.
-    minmax = [1e30, 1e30, -1e30, -1e30]
-    for face in faces:
-        for loop in face.loops:
-            u, v = loop[uv_layer].uv
-            minmax[0] = min(minmax[0], u)
-            minmax[1] = min(minmax[1], v)
-            minmax[2] = max(minmax[2], u)
-            minmax[3] = max(minmax[3], v)
-
-    mid_u = (minmax[0] + minmax[2]) / 2
-    mid_v = (minmax[1] + minmax[3]) / 2
-
-    del_u = transform[0][2] + mid_u - transform[0][0] * mid_u - transform[0][1] * mid_v
-    del_v = transform[1][2] + mid_v - transform[1][0] * mid_u - transform[1][1] * mid_v
-
-    # Apply transform.
-    for face in faces:
-        for loop in face.loops:
-            pre_uv = loop[uv_layer].uv
-            u = transform[0][0] * pre_uv[0] + transform[0][1] * pre_uv[1] + del_u
-            v = transform[1][0] * pre_uv[0] + transform[1][1] * pre_uv[1] + del_v
-            loop[uv_layer].uv = (u, v)
-
-
-def is_face_uv_selected(face, uv_layer):
-    for loop in face.loops:
-        if not loop[uv_layer].select:
-            return False
-    return True
-
-
-def is_island_uv_selected(bm, island, uv_layer):
-    for face in island:
-        if is_face_uv_selected(face, uv_layer):
-            return True
-    return False
-
-
-def randomize_uv_transform_bmesh(mesh, bm, transform_params):
-    import bpy_extras.bmesh_utils
-    uv_layer = bm.loops.layers.uv.verify()
-    islands = bpy_extras.bmesh_utils.bmesh_linked_uv_islands(bm, uv_layer)
-    for island in islands:
-        if is_island_uv_selected(bm, island, uv_layer):
-            randomize_uv_transform_island(bm, uv_layer, island, transform_params)
-
-
-def randomize_uv_transform(context, transform_params):
-    import bmesh
-    ob_list = context.objects_in_mode_unique_data
-    for ob in ob_list:
-        bm = bmesh.from_edit_mesh(ob.data)
-        if not bm.loops.layers.uv:
-            continue
-
-        # Only needed to access the minimum face index of each island.
-        bm.faces.index_update()
-        randomize_uv_transform_bmesh(ob.data, bm, transform_params)
-
-    for ob in ob_list:
-        bmesh.update_edit_mesh(ob.data)
-
-    return {'FINISHED'}
-
-
-from bpy.props import (
-    BoolProperty,
-    FloatProperty,
-    FloatVectorProperty,
-    IntProperty,
-)
-
-
-class RandomizeUVTransform(Operator):
-    """Randomize uv island's location, rotation, and scale"""
-    bl_idname = "uv.randomize_uv_transform"
-    bl_label = "Randomize"
-    bl_options = {'REGISTER', 'UNDO'}
-
-    random_seed: IntProperty(
-        name="Random Seed",
-        description="Seed value for the random generator",
-        min=0,
-        max=10000,
-        default=0,
-    )
-    use_loc: BoolProperty(
-        name="Randomize Location",
-        description="Randomize the location values",
-        default=True,
-    )
-    loc: FloatVectorProperty(
-        name="Location",
-        description=("Maximum distance the objects "
-                     "can spread over each axis"),
-        min=-100.0,
-        max=100.0,
-        size=2,
-        subtype='TRANSLATION',
-        default=(0.0, 0.0),
-    )
-    use_rot: BoolProperty(
-        name="Randomize Rotation",
-        description="Randomize the rotation value",
-        default=True,
-    )
-    rot: FloatProperty(
-        name="Rotation",
-        description="Maximum rotation",
-        min=-2 * math.pi,
-        max=2 * math.pi,
-        subtype='ANGLE',
-        default=0.0,
-    )
-    use_scale: BoolProperty(
-        name="Randomize Scale",
-        description="Randomize the scale values",
-        default=True,
-    )
-    scale_even: BoolProperty(
-        name="Scale Even",
-        description="Use the same scale value for both axes",
-        default=False,
-    )
-
-    scale: FloatVectorProperty(
-        name="Scale",
-        description="Maximum scale randomization over each axis",
-        min=-100.0,
-        max=100.0,
-        default=(1.0, 1.0),
-        size=2,
-    )
-
-    @classmethod
-    def poll(cls, context):
-        return context.mode == 'EDIT_MESH'
-
-    def execute(self, context):
-        seed = self.random_seed
-
-        loc = [0, 0] if not self.use_loc else self.loc
-        rot = 0 if not self.use_rot else self.rot
-        scale = None if not self.use_scale else self.scale
-        scale_even = self.scale_even
-
-        transformParams = [seed, loc, rot, scale, scale_even]
-        return randomize_uv_transform(context, transformParams)
-
-
-classes = (
-    RandomizeUVTransform,
-)
diff --git a/release/scripts/startup/bl_operators/uvcalc_transform.py b/release/scripts/startup/bl_operators/uvcalc_transform.py
new file mode 100644
index 00000000000..093aea4eaa8
--- /dev/null
+++ b/release/scripts/startup/bl_operators/uvcalc_transform.py
@@ -0,0 +1,436 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from bpy.types import Operator
+from mathutils import Matrix,Vector
+
+import math
+
+from bpy.props import (
+    BoolProperty,
+    EnumProperty,
+    FloatProperty,
+    FloatVectorProperty,
+    IntProperty,
+)
+
+
+def is_face_uv_selected(face, uv_layer, any_edge):
+    """
+    Returns True if the face is UV selected.
+
+    :arg face: the face to query.
+    :type bmesh: :class:`BMFace`
+    :arg uv_layer: the UV layer to source UVs from.
+    :type bmesh: :class:`BMLayerItem`
+    :arg any_edge: use edge selection instead of vertex selection.
+    :type any_edge: bool
+    :return: True if the face is UV selected.
+    :rtype: bool
+    """
+
+    if not face.select:  # Geometry selection
+        return False
+
+    import bpy
+    if bpy.context.tool_settings.use_uv_select_sync:
+        # In sync selection mode, UV selection comes solely from geometry selection.
+        return True
+
+    if any_edge:
+        for loop in face.loops:
+            if loop[uv_layer].select_edge:
+                return True
+        return False
+
+    for loop in face.loops:
+        if not loop[uv_layer].select:
+            return False
+    return True
+
+
+def is_island_uv_selected(island, uv_layer, any_edge):
+    """
+    Returns True if the island is UV selected.
+
+    :arg island: list of faces to query.
+    :arg uv_layer: the UV layer to source UVs from.
+    :type bmesh: :class:`BMLayerItem`
+    :arg any_edge: use edge selection instead of vertex selection.
+    :type any_edge: bool
+    :return: list of lists containing polygon indices.
+    :rtype: bool
+    """
+    for face in island:
+        if is_face_uv_selected(face, uv_layer, any_edge):
+            return True
+    return False
+
+
+def find_rotation_auto(bm, uv_layer, faces):
+    sum_u = 0.0
+    sum_v = 0.0
+    for face in faces:
+        prev_uv = face.loops[-1][uv_layer].uv
+        for loop in face.loops:
+            uv = loop[uv_layer].uv
+            du = uv[0] - prev_uv[0]
+            dv = uv[1] - prev_uv[1]
+            edge_angle = math.atan2(dv, du)
+            edge_angle *= 4.0  # Wrap 4 times around the circle
+            sum_u += math.cos(edge_angle)
+            sum_v += math.sin(edge_angle)
+            prev_uv = uv
+
+    # Compute angle.
+    return -math.atan2(sum_v, sum_u) / 4.0
+
+
+def find_rotation_edge(bm, uv_layer, faces):
+    sum_u = 0.0
+    sum_v = 0.0
+    for face in faces:
+        prev_uv = face.loops[-1][uv_layer].uv
+        prev_select = face.loops[-1][uv_layer].select_edge
+        for loop in face.loops:
+            uv = loop[uv_layer].uv
+            if prev_select:
+                du = uv[0] - prev_uv[0]
+                dv = uv[1] - prev_uv[1]
+                edge_angle = math.atan2(dv, du)
+                edge_angle *= 2.0  # Wrap 2 times around the circle
+                sum_u += math.cos(edge_angle)
+                sum_v += math.sin(edge_angle)
+
+            prev_uv = uv
+            prev_select = loop[uv_layer].select_edge
+
+    # Add 90 degrees to align along V co-ordinate.
+    # 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list