/
op_randomize.py
166 lines (136 loc) · 6.09 KB
/
op_randomize.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import bpy
import bmesh
import math
import random
import numpy as np
from mathutils import Vector
from . import utilities_uv
class op(bpy.types.Operator):
bl_idname = "uv.textools_randomize"
bl_label = "Randomize Position"
bl_description = "Randomize selected UV faces position and/or rotation"
bl_options = {'REGISTER', 'UNDO'}
bool_face : bpy.props.BoolProperty(name="Per Face", default=False)
intmode : bpy.props.BoolProperty(name="Int mode", default=False)
strengh_U : bpy.props.FloatProperty(name="U Strengh", default=1, min=-10, max=10, soft_min=0, soft_max=1)
strengh_V : bpy.props.FloatProperty(name="V Strengh", default=1, min=-10, max=10, soft_min=0, soft_max=1)
rotation : bpy.props.FloatProperty(name="Rotation Strengh", default=0, min=-10, max=10, soft_min=0, soft_max=1)
bool_precenter : bpy.props.BoolProperty(name="Pre Center Faces/Islands", default=False, description="Collect all faces/islands around the center of the UV space.")
bool_bounds : bpy.props.BoolProperty(name="Within Image Bounds", default=False, description="Keep the UV faces/islands within the 0-1 UV domain.")
rand_seed : bpy.props.IntProperty(name="Seed", default=0)
@classmethod
def poll(cls, context):
if bpy.context.area.ui_type != 'UV':
return False
if not bpy.context.active_object:
return False
if bpy.context.active_object.type != 'MESH':
return False
if bpy.context.active_object.mode != 'EDIT':
return False
if not bpy.context.object.data.uv_layers:
return False
return True
def execute(self, context):
if self.bool_bounds or self.bool_precenter:
udim_tile, column, row = utilities_uv.get_UDIM_tile_coords(bpy.context.active_object)
else:
udim_tile = 1001
column = row = 0
utilities_uv.multi_object_loop(main, self, context, udim_tile=udim_tile, column=column, row=row, ob_num=0)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def main(self, context, udim_tile=1001, column=0, row=0, ob_num=0):
selection_mode = bpy.context.scene.tool_settings.uv_select_mode
me = bpy.context.active_object.data
bm = bmesh.from_edit_mesh(me)
uv_layers = bm.loops.layers.uv.verify()
sync = bpy.context.scene.tool_settings.use_uv_select_sync
if sync:
pregroup = {f for f in bm.faces if f.select}
else:
pregroup = {f for f in bm.faces if all([l[uv_layers].select for l in f.loops]) and f.select}
if not pregroup:
return
random.seed(self.rand_seed + ob_num)
if not self.bool_face:
group = utilities_uv.get_selected_islands(bm, uv_layers)
else:
group = pregroup
for f in group:
rand_v = Vector(( 2*(random.random()-0.5), 2*(random.random()-0.5) ))
rand_3 = 2*(random.random()-0.5)
if self.bool_bounds or self.bool_precenter or self.rotation:
if self.rotation:
theta = self.rotation*rand_3*math.pi
matrix = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])
else:
matrix = np.array([[1, 0], [0, 1]])
if not self.bool_face:
vec_origin = utilities_uv.get_center(f, bm, uv_layers)
if self.bool_bounds or self.bool_precenter:
for i in f:
for loop in i.loops:
uvs0 = loop[uv_layers].uv - vec_origin
loop[uv_layers].uv = (0.5 + matrix[0][0]*uvs0.x + matrix[0][1]*uvs0.y, 0.5 + matrix[1][0]*uvs0.x + matrix[1][1]*uvs0.y)
else:
for i in f:
for loop in i.loops:
uvs0 = loop[uv_layers].uv - vec_origin
loop[uv_layers].uv = (vec_origin.x + matrix[0][0]*uvs0.x + matrix[0][1]*uvs0.y, vec_origin.y + matrix[1][0]*uvs0.x + matrix[1][1]*uvs0.y)
else:
vec_origin = utilities_uv.get_center(f.loops, bm, uv_layers, are_loops=True)
if self.bool_bounds or self.bool_precenter:
for loop in f.loops:
uvs0 = loop[uv_layers].uv - vec_origin
loop[uv_layers].uv = (0.5 + matrix[0][0]*uvs0.x + matrix[0][1]*uvs0.y, 0.5 + matrix[1][0]*uvs0.x + matrix[1][1]*uvs0.y)
else:
for loop in f.loops:
uvs0 = loop[uv_layers].uv - vec_origin
loop[uv_layers].uv = (vec_origin.x + matrix[0][0]*uvs0.x + matrix[0][1]*uvs0.y, vec_origin.y + matrix[1][0]*uvs0.x + matrix[1][1]*uvs0.y)
bmesh.update_edit_mesh(me, loop_triangles=False)
if self.bool_bounds:
boundsMin = Vector((math.inf, math.inf))
boundsMax = Vector((-math.inf, -math.inf))
if not self.bool_face:
for i in f:
for loop in i.loops:
uv = loop[uv_layers].uv
boundsMin = Vector(( max( min(boundsMin.x, uv.x), 0 ), max( min(boundsMin.y, uv.y), 0 ) ))
boundsMax = Vector(( min( max(boundsMax.x, uv.x), 1 ), min( max(boundsMax.y, uv.y), 1 ) ))
else:
for loop in f.loops:
uv = loop[uv_layers].uv
boundsMin = Vector(( max( min(boundsMin.x, uv.x), 0 ), max( min(boundsMin.y, uv.y), 0 ) ))
boundsMax = Vector(( min( max(boundsMax.x, uv.x), 1 ), min( max(boundsMax.y, uv.y), 1 ) ))
move = Vector(( min(boundsMin.x, abs(1-boundsMax.x)) * max( min(self.strengh_U, 1), -1 ), min(boundsMin.y, abs(1-boundsMax.y)) * max( min(self.strengh_V, 1), -1 ) ))
else:
move = Vector((self.strengh_U, self.strengh_V))
randmove = rand_v * move
if self.intmode:
randmove = Vector(np.around(randmove))
if (not self.bool_bounds and not self.bool_precenter) or udim_tile == 1001:
if move.x or move.y:
if not self.bool_face:
for i in f:
for loop in i.loops:
loop[uv_layers].uv += randmove
else:
for loop in f.loops:
loop[uv_layers].uv += randmove
else:
if move.x or move.y:
if not self.bool_face:
for i in f:
for loop in i.loops:
loop[uv_layers].uv += randmove + Vector((column, row))
else:
for loop in f.loops:
loop[uv_layers].uv += randmove + Vector((column, row))
bmesh.update_edit_mesh(me, loop_triangles=False)
# Workaround for selection not flushing properly from loops to EDGE Selection Mode, apparently since UV edge selection support was added to the UV space
if not sync:
bpy.ops.uv.select_mode(type='VERTEX')
bpy.context.scene.tool_settings.uv_select_mode = selection_mode