OO/◯ᴥᗱᗴᗝИNᗱᗴᙁ⚭ⵙ⚭ᙁᗱᗴИNᗝᗱᗴᴥ◯/2.90/SCRIPTS/ADDONS/ARRAY_TOOLS_1-2-1/AT_INTERFACE.PY

1224 lines
44 KiB
Plaintext
Raw Normal View History

2020-11-20 15:33:46 +00:00
# -*- coding: utf-8 -*-
import bpy
import math
from bpy.types import PropertyGroup
from mathutils import Vector
from . import cfg
from . import at_panel
from . import at_operators
from . at_calc_func import(
x_axis,
y_axis,
z_axis,
xyz_axis,
at_all_in_one,
at_random,
sum_serie,
tsr
)
"""not used yet
if check on update, may really slow the addon """
def check_list(alist):
"""check all the objects"""
for elem in alist:
if elem in bpy.data.objects:
pass
else:
cfg.display_error(str(elem)+" isn't valid.")
print("Check_list : a name isn't valid ", elem)
return False
return True
def elem_in_row(column, row, indice):
"""Number of elements in a row"""
elements = column + (row - 1) * indice
# print("row elements =", elements)
return elements
# ---------------------------- Properties ---------------------------
class ArrayTools_props(PropertyGroup):
"""Properties for array tools"""
def add_in_column(self, row, nb_column=-1):
"""Add nb_column element(s) in each row"""
column = cfg.at_count_values[0]
if nb_column == -1:
nb_column = cfg.at_count_values[1] - column
ref_name = cfg.atools_objs[0][0]
if ref_name in bpy.data.objects:
ref_obj = bpy.data.objects[ref_name]
# update the ref_mtx if object's transforms have changed
cfg.ref_mtx = ref_obj.matrix_world.copy()
# with offset no need to replace all elements, only the last
if self.is_tr_off_last:
for i in range(row):
col = column + i*self.alter
for j in range(col, col + nb_column):
objcp = ref_obj.copy()
array_col = bpy.data.collections.get(cfg.col_name)
array_col.objects.link(objcp)
if self.is_copy:
objcp.data = ref_obj.data.copy()
cfg.atools_objs[i].append(objcp.name)
self.transforms_lsr(j, i, cfg.ref_mtx, objcp.name)
# update the global ui
tr, sc, rot = self.calc_global()
self.up_ui_tr_global(tr)
self.up_ui_sc_global(sc)
self.up_ui_rot_global(rot)
else: # replace all elements
for i in range(row):
col = column + i*self.alter
for j in range(col, col + nb_column):
objcp = ref_obj.copy()
array_col = bpy.data.collections.get(cfg.col_name)
array_col.objects.link(objcp)
if self.is_copy:
objcp.data = ref_obj.data.copy()
cfg.atools_objs[i].append(objcp.name)
self.update_global(bpy.context)
del objcp
del ref_obj
else:
message = "Problem with reference object's name."
cfg.display_error(message)
print("Error in 'add_in_column' : ", message)
def del_in_column(self, row, nb_column=-1):
"""Remove nb_column element(s) in each row"""
if nb_column == -1:
nb_column = cfg.at_count_values[0] - cfg.at_count_values[1]
array_col = bpy.data.collections.get(cfg.col_name)
for i in range(row-1, -1, -1):
for j in range(nb_column):
del_name = cfg.atools_objs[i].pop()
if del_name in bpy.data.objects:
obj = bpy.data.objects[del_name]
array_col.objects.unlink(obj)
bpy.data.objects.remove(obj, do_unlink=True)
else:
cfg.display_error(del_name + " doesn't exist anymore.")
print("Error in 'del_in_column' : ", del_name)
# if no more element in list, remove the row
if not cfg.atools_objs[i]:
cfg.atools_objs.pop()
self.up_ui_updateRow(row - 1)
continue
if not self.is_tr_off_last:
# if global is used last
self.update_global(bpy.context)
else:
tr, sc, rot = self.calc_global()
self.up_ui_tr_global(tr)
self.up_ui_sc_global(sc)
self.up_ui_rot_global(rot)
def add_in_col_alter(self, row, nb_column):
"""Add elements in all rows except the first for variation"""
array_col = bpy.data.collections.get(cfg.col_name)
ref_name = cfg.atools_objs[0][0]
column = self.count
if ref_name in bpy.data.objects:
ref_obj = bpy.data.objects[ref_name]
cfg.ref_mtx = ref_obj.matrix_world.copy()
if self.is_tr_off_last:
for i in range(1, row):
for j in range(column, column + i * nb_column):
objcp = ref_obj.copy()
array_col = bpy.data.collections.get(cfg.col_name)
array_col.objects.link(objcp)
if self.is_copy:
objcp.data = ref_obj.data.copy()
cfg.atools_objs[i].append(objcp.name)
# print("objs=", cfg.atools_objs)
self.update_offset(bpy.context)
else: # replace all elements
for i in range(1, row):
for j in range(column, column + i * nb_column):
objcp = ref_obj.copy()
array_col = bpy.data.collections.get(cfg.col_name)
array_col.objects.link(objcp)
if self.is_copy:
objcp.data = ref_obj.data.copy()
cfg.atools_objs[i].append(objcp.name)
self.update_global(bpy.context)
del objcp
del ref_obj
else:
message = "Problem with reference object's name."
cfg.display_error(message)
print("Error in 'add_in_column' : ", message)
def del_in_col_alter(self, row, nb_column):
"""Remove elements in all rows except the first"""
array_col = bpy.data.collections.get(cfg.col_name)
for i in range(row -1 , 0, -1):
for j in range(nb_column * i):
del_name = cfg.atools_objs[i].pop()
# print("del name=", del_name)
if del_name in bpy.data.objects:
obj = bpy.data.objects[del_name]
array_col.objects.unlink(obj)
bpy.data.objects.remove(obj, do_unlink=True)
else:
cfg.display_error(del_name + " doesn't exist anymore.")
print("Error in 'del_in_column' : ", del_name)
if self.is_tr_off_last:
self.update_offset(bpy.context)
else:
self.update_global(bpy.context)
def add_in_row(self, column, nb_row=-1):
"""Add column elements in nb_row new row(s)"""
row = cfg.at_row_values[0]
if nb_row == -1:
nb_row = cfg.at_row_values[1] - row
ref_name = cfg.atools_objs[0][0]
if ref_name in bpy.data.objects:
ref_obj = bpy.data.objects[ref_name]
cfg.ref_mtx = ref_obj.matrix_world.copy()
if self.is_tr_off_last:
for i in range(row, row + nb_row):
cfg.atools_objs.append([])
for j in range(column + i*self.alter):
objcp = ref_obj.copy()
array_col = bpy.data.collections.get(cfg.col_name)
array_col.objects.link(objcp)
if self.is_copy:
objcp.data = ref_obj.data.copy()
cfg.atools_objs[i].append(objcp.name)
self.transforms_lsr(j, i, cfg.ref_mtx, objcp.name)
else:
for i in range(row, row + nb_row):
cfg.atools_objs.append([])
for j in range(column):
objcp = ref_obj.copy()
array_col = bpy.data.collections.get(cfg.col_name)
array_col.objects.link(objcp)
if self.is_copy:
objcp.data = ref_obj.data.copy()
cfg.atools_objs[i].append(objcp.name)
self.update_global(bpy.context)
else:
message = "Problem with reference object's name."
cfg.display_error(message)
print("Error in 'add in row' : ", message)
def del_in_row(self, nb_row=-1):
"""Remove nb_row row(s) : (column * nb_row) elements"""
if nb_row == -1:
nb_row = cfg.at_row_values[0] - cfg.at_row_values[1]
array_col = bpy.data.collections.get(cfg.col_name)
for i in range(nb_row):
names = cfg.atools_objs.pop()
for del_name in names:
if del_name in bpy.data.objects:
obj = bpy.data.objects[del_name]
array_col.objects.unlink(obj)
bpy.data.objects.remove(obj, do_unlink=True)
else:
cfg.display_error(del_name + " doesn't exist anymore.")
print("Error in 'del_in_column' : ", del_name)
def at_del_all(self, del_rall):
"""Delete all copies and remove objects from lists
del_rall : boolean, True to del reference object from list
"""
array_col = bpy.data.collections.get(cfg.col_name)
ref_name = cfg.atools_objs[0][0]
for i in range(self.row):
names = cfg.atools_objs.pop()
for obj_name in reversed(names):
if obj_name == ref_name:
continue
# test if object exist
if obj_name in bpy.data.objects:
obj = bpy.data.objects[obj_name]
array_col.objects.unlink(obj)
bpy.data.objects.remove(obj, do_unlink=True)
else:
cfg.display_error(obj_name + " not exist!")
print("Error in 'del_all' : ", obj_name)
if del_rall:
cfg.atools_objs.clear()
# removing the collection if empty
if not array_col.objects:
bpy.data.collections.remove(array_col)
else:
cfg.atools_objs.append([ref_name])
# print("Del_all done!")
# ----------------------- UI update -----------------------------
# ---------------------------------------------------------------
# ----------------------- count update --------------------------
def updateCount(self, context):
"""update the number of element(s) in column"""
if self.is_prog_change:
self.is_prog_change = False
else:
cfg.add_count(int(self.count))
cfg.del_count()
# cfg.count_values[0] always store old count value
difference = self.count - cfg.at_count_values[0]
self.update_infos()
if difference > 0:
self.add_in_column(self.row, difference)
elif difference < 0:
self.del_in_column(self.row, -difference)
# print("objs =", cfg.atools_objs)
def up_ui_updateCount(self, val):
"""Update the value of the property count in UI"""
self.is_prog_change = True
self.count = val
# ----------------------- row update ----------------------------
def update_row(self, context):
"""Update row property"""
cfg.add_row(self.row)
cfg.del_row()
if self.is_prog_change:
self.is_prog_change = False
else:
if self.alter < 0 and cfg.maxrow < self.row:
cfg.display_error("Maximun rows for these setting is : " + str(cfg.maxrow))
self.up_ui_updateRow(cfg.maxrow)
return
# cfg.at_row_values[0] always store old row value
difference = self.row - cfg.at_row_values[0]
if difference > 0:
self.add_in_row(self.count, difference)
elif difference < 0:
self.del_in_row(-difference)
line = elem_in_row(self.count, self.row, self.alter)
self.update_infos()
def up_ui_updateRow(self, val):
"""Update the value of the property row in UI"""
self.is_prog_change = True
self.row = val
def update_alter(self, context):
"""Update alter property"""
if self.is_prog_change:
self.is_prog_change = False
else:
# alter must have at least 2 rows
if self.row == 1 and self.alter != 0:
cfg.display_error("Add more rows first.")
self.up_ui_updateAlter(0)
return
if self.alter < 0:
# (column + (row-1)* variation) is the number of elements
# of the last row and must be at least >= 1
alter = int((1 - self.count) / (self.row - 1))
if self.alter < alter:
cfg.display_error("Min variation is '"+str(alter)+"' for these settings.")
self.up_ui_updateAlter(alter)
return
cfg.add_alter(self.alter)
cfg.del_alter()
self.update_ralign()
difference = self.alter - cfg.at_alter[0]
if difference > 0:
self.add_in_col_alter(self.row, difference)
elif difference < 0:
self.del_in_col_alter(self.row, -difference)
# print(f"count={self.count}, row={self.row}, alter={self.alter}")
line = elem_in_row(self.count, self.row, self.alter)
# print("elems in row =", line)
self.update_infos()
def up_ui_updateAlter(self, val):
"""Update the value of the property alter in UI"""
self.is_prog_change = True
self.alter = val
def update_ralign(self):
"""Update the value of ralign"""
decal = -self.alter * self.tr_offset
if self.align == 'LEFT':
self.ralign = Vector((0.0, 0.0, 0.0))
elif self.align == 'CENTER':
self.ralign = decal / 2
elif self.align == 'RIGHT':
self.ralign = decal
def update_align(self, context):
"""According to the value of align, calculate ralign"""
self.update_ralign()
if self.is_tr_off_last:
self.update_offset(bpy.context)
else:
self.update_global(bpy.context)
def update_infos(self):
"""Update properties total and erow"""
sum = sum_serie(self.row, self.alter)
square = self.count * self.row
if self.alter >= 0:
cfg.maxrow = self.row
else:
ca = self.count // -self.alter
cfg.maxrow = ca if self.count % self.alter == 0 else ca + 1
self.total = str(int(square + sum))
self.erow = str(elem_in_row(self.count, self.row, self.alter))
# ----------------------- translation update --------------------
def up_ui_tr_offset(self, val):
"""Update the value of the property tr_offset in UI"""
self.is_prog_change = True
self.tr_offset = val
def up_ui_tr_global(self, val):
"""Update the value of the property tr_global in UI"""
self.is_prog_change = True
self.tr_global = val
# ----------------------- scale update --------------------------
def up_ui_sc_offset(self, val):
"""Update the value of the property sc_offset in UI"""
self.is_prog_change = True
self.sc_offset = val
def up_ui_sc_global(self, val):
"""Update the value of the property sc_global in UI"""
self.is_prog_change = True
self.sc_global = val
# ----------------------- rotation update -----------------------
def up_ui_rot_offset(self, val):
"""Update the value of the property rot_offset in UI"""
self.is_prog_change = True
self.rot_offset = val
def up_ui_rot_global(self, val):
"""Update the value of the property rot_global in UI"""
self.is_prog_change = True
self.rot_global = val
# ---------------------------------------------------------------
def calc_global(self):
"""Calculate global for column"""
tg = (self.count-1) * self.tr_offset
sg = (xyz_axis() - (self.count-1) *
(cfg.ref_mtx.to_scale() - (self.sc_offset/100))) * 100
rg = self.count * Vector(self.rot_offset)
return tg,sg,rg
def transforms_lsr(self, column, row, mat, ename):
"""Calculate transforms according to the position of the element
column : indice of the element's column
row : indice of the element's row
mat : matrix of the reference object
ename : element's name to put in place
"""
localxyz = (x_axis(), y_axis(), z_axis())
translate, scaling, rotate = tsr(mat, column, row, self.tr_offset, self.tr_second,
self.sc_offset, self.sc_second, self.rot_offset, self.rot_second, self.ralign)
if ename in bpy.data.objects:
obj = bpy.data.objects[ename]
if self.at_pivot is not None:
obj.matrix_world = at_all_in_one(mat, rotate, localxyz, translate,
scaling, self.at_pivot.location)
else:
obj.matrix_world = at_all_in_one(mat, rotate, localxyz, translate,
scaling, mat.translation)
def apply_transforms(self, matx, nb_column, nb_row, tr, sc, rot):
"""Move, scale and rotate the selected elements
tr : translation offset of the first row
sc : scale offset of the first row
rot : rotation offset of the first row
return global transforms
"""
# local axis always (1,0,0) (0,1,0) (0,0,1)
localxyz = (x_axis(), y_axis(), z_axis())
ref_scale = matx.to_scale()
# duplicate code but avoid looping the test
if self.at_pivot is not None:
for i in range(nb_row):
for j in range(nb_column + i*self.alter):
elem = cfg.atools_objs[i][j]
if elem in bpy.data.objects:
obj = bpy.data.objects[elem]
else:
cfg.display_error(elem + " no more exist !")
print("Error in 'apply_transforms', name no more exist : ", elem)
continue
t_off, s_off, r_off = tsr(matx, j, i, tr, self.tr_second, sc,
self.sc_second, rot, self.rot_second, self.ralign)
obj.matrix_world = at_all_in_one(matx, r_off,
localxyz, t_off, s_off, self.at_pivot.location)
else:
for i in range(nb_row):
for j in range(nb_column + i*self.alter):
ref_loc = cfg.ref_mtx.translation
elem = cfg.atools_objs[i][j]
if elem in bpy.data.objects:
obj = bpy.data.objects[elem]
else:
cfg.display_error(elem + " no more exist !")
print("Error in 'apply_transforms', name no more exist : ", elem)
continue
t_off, s_off, r_off = tsr(matx, j, i, tr, self.tr_second, sc,
self.sc_second, rot, self.rot_second, self.ralign)
obj.matrix_world = at_all_in_one(matx, r_off,
localxyz, t_off, s_off, ref_loc)
tr_col,sc_col,rot_col = self.calc_global()
return(tr_col, sc_col, rot_col)
def update_offset(self, context):
"""Update for all offsets"""
if self.is_prog_change:
self.is_prog_change = False
else: # user change offset
self.is_tr_off_last = True
ref_name = cfg.atools_objs[0][0]
if bpy.data.objects[ref_name]:
cfg.ref_mtx = bpy.data.objects[ref_name].matrix_world.copy()
aloc, asc, arot = self.apply_transforms(cfg.ref_mtx, self.count, self.row,
self.tr_offset, self.sc_offset, Vector(self.rot_offset))
# since offset changes, global too
self.up_ui_tr_global(aloc)
self.up_ui_sc_global(asc)
self.up_ui_rot_global(arot)
def update_global(self, context):
"""Update for all globals"""
if self.is_prog_change:
self.is_prog_change = False
else: # user change global
self.is_tr_off_last = False
ref_name = cfg.atools_objs[0][0]
if bpy.data.objects[ref_name]:
cfg.ref_mtx = bpy.data.objects[ref_name].matrix_world.copy()
ref_scale = cfg.ref_mtx.to_scale()
translation_offset = Vector(self.tr_global) / (self.count - 1)
scale_offset = ref_scale - ((ref_scale-(self.sc_global/100)) / (self.count - 1))
rotation_offset = Vector(self.rot_global) / self.count
self.apply_transforms(cfg.ref_mtx, self.count, self.row, translation_offset,
Vector(scale_offset)*100, rotation_offset)
# since global changes, offset too
self.up_ui_tr_offset(translation_offset)
self.up_ui_sc_offset(Vector(scale_offset*100))
self.up_ui_rot_offset(rotation_offset)
def update_second(self, context):
"""Update the secondary transforms"""
ref_name = cfg.atools_objs[0][0]
if bpy.data.objects[ref_name]:
cfg.ref_mtx = bpy.data.objects[ref_name].matrix_world.copy()
self.apply_transforms(cfg.ref_mtx, self.count, self.row, self.tr_offset,
self.sc_offset, self.rot_offset)
# ----------------------- is_copy update ------------------------
def up_ui_is_copy(self):
"""Update the value of the property is_copy in UI"""
self.is_prog_change = True
self.is_copy = False
def update_is_copy(self, context):
"""Allow a copy or duplicate(copy link by default)"""
if self.is_prog_change:
self.is_prog_change = False
else:
if self.is_copy: # no need to rebuild all
for i in range(self.row):
for j in range(self.count):
if i == 0 and j == 0:
continue
ref_name = cfg.atools_objs[0][0]
elem_name = cfg.atools_objs[i][j]
bpy.data.objects[elem_name].data = bpy.data.objects[ref_name].data.copy()
else: # since the value change (now duplicate), need to rebuild
count = self.count
row = self.row
ref_name = cfg.atools_objs[0][0]
array_col = bpy.data.collections.get(cfg.col_name)
# DO NOT USE BLENDER CRASH WITH IT
# self.at_del_all(False)
bpy.ops.object.delete({"selected_objects": array_col.objects})
cfg.atools_objs.clear()
cfg.atools_objs.append([ref_name])
ref_obj = bpy.data.objects[ref_name]
for i in range(row):
if i != 0:
cfg.atools_objs.append([])
for j in range(count + i*self.alter):
objcp = ref_obj.copy()
array_col.objects.link(objcp)
cfg.atools_objs[i].append(objcp.name)
del objcp
del ref_obj
if self.is_tr_off_last:
self.update_offset(bpy.context)
else:
self.update_global(bpy.context)
print("Rebuild done!")
# ----------------------- random part ---------------------------
# ---------------------------------------------------------------
def update_seed(self, context):
if self.at_mode == 'ADV':
sc_min = (self.sc_min_x, self.sc_min_y, self.sc_min_z)
sc_max = (self.sc_max_x, self.sc_max_y, self.sc_max_z)
at_random(self.at_seed, self.count, self.row, self.tr_min, self.tr_max, sc_min,
sc_max, self.rot_min, self.rot_max, self.at_is_tr, self.at_is_sc, self.at_is_rot,
self.sc_all, self.tr_offset, self.tr_second, self.sc_offset, self.sc_second,
self.rot_offset, self.rot_second, self.at_pivot, self.alter, self.ralign)
else: # simple mode
vec = xyz_axis()
tr = self.tr_rand * vec
sc = self.sc_rand * vec
rot = self.rot_rand * vec
at_random(self.at_seed, self.count, self.row, -tr, tr, sc, 100*vec, -rot, rot,
self.at_is_tr, self.at_is_sc, self.at_is_rot, False, self.tr_offset,
self.tr_second, self.sc_offset, self.sc_second, self.rot_offset,
self.rot_second, self.at_pivot, self.alter, self.ralign)
def update_rtr(self, context):
"""rtr in simple mode update adv mode"""
self.tr_max = self.tr_rand * Vector((1.0, 1.0, 1.0))
self.tr_min = self.tr_rand * Vector((-1.0, -1.0, -1.0))
def update_rsc(self, context):
"""rsc in simple mode update adv mode"""
self.sc_max_x, self.sc_max_y, self.sc_max_z = (100.0, 100.0, 100.0)
rand = self.sc_rand
self.sc_min_x = rand
self.sc_min_y = rand
self.sc_min_z = rand
def update_rrot(self, context):
"""rrot in simple mode update adv mode"""
self.rot_max = self.rot_rand * Vector((1.0, 1.0, 1.0))
self.rot_min = self.rot_rand * Vector((-1.0, -1.0, -1.0))
def up_ui_sc_min_x(self, val):
"""Update the value of the property sc_min_x in UI"""
self.is_prog_change = True
self.sc_min_x = val
def up_ui_sc_min_y(self, val):
"""Update the value of the property sc_min_y in UI"""
self.is_prog_change = True
self.sc_min_y = val
def up_ui_sc_min_z(self, val):
"""Update the value of the property sc_min_z in UI"""
self.is_prog_change = True
self.sc_min_z = val
def up_ui_sc_max_x(self, val):
"""Update the value of the property sc_max_x in UI"""
self.is_prog_change = True
self.sc_max_x = val
def up_ui_sc_max_y(self, val):
"""Update the value of the property sc_max_y in UI"""
self.is_prog_change = True
self.sc_max_y = val
def up_ui_sc_max_z(self, val):
"""Update the value of the property sc_max_z in UI"""
self.is_prog_change = True
self.sc_max_z = val
# -------------- update min and max -----------------------------
# if user enter a max value < min, change min and vice versa
def up_tr_min(self, context):
"""Update tr_max if tr_min is higher"""
if self.is_prog_change:
self.is_prog_change = False
else:
for i in range(3):
if self.tr_min[i] > self.tr_max[i]:
self.is_prog_change = True
self.tr_max[i] = self.tr_min[i]
def up_tr_max(self, context):
"""Update tr_min if tr_max is lower"""
if self.is_prog_change:
self.is_prog_change = False
else:
for i in range(3):
if self.tr_min[i] > self.tr_max[i]:
self.is_prog_change = True
self.tr_min[i] = self.tr_max[i]
def up_sc_min_x(self, context):
"""Update sc_max_x if sc_min_x is higher"""
if self.is_prog_change:
self.is_prog_change = False
else:
test = self.sc_min_x > self.sc_max_x
if test and self.sc_all:
# case : min > max and uniform = True
self.up_ui_sc_max_x(self.sc_min_x)
# with uniform : min_x = min_y = min_z same for max_
self.up_ui_sc_min_y(self.sc_min_x)
self.up_ui_sc_min_z(self.sc_min_x)
self.up_ui_sc_max_y(self.sc_min_x)
self.up_ui_sc_max_z(self.sc_min_x)
elif self.sc_all:
# case : min < max and uniform = True
self.up_ui_sc_min_y(self.sc_min_x)
self.up_ui_sc_min_z(self.sc_min_x)
self.up_ui_sc_max_y(self.sc_max_x)
self.up_ui_sc_max_z(self.sc_max_x)
elif test:
# case : min > max and uniform = False
self.up_ui_sc_max_x(self.sc_min_x)
def up_sc_min_y(self, context):
"""Update sc_max_y if sc_min_y is higher"""
if self.is_prog_change:
self.is_prog_change = False
else:
test = self.sc_min_y > self.sc_max_y
if test and self.sc_all:
# case : min > max and uniform = True
self.up_ui_sc_max_y(self.sc_min_y)
# with uniform : min_x = min_y = min_z same for max_
self.up_ui_sc_min_x(self.sc_min_y)
self.up_ui_sc_min_z(self.sc_min_y)
self.up_ui_sc_max_x(self.sc_min_y)
self.up_ui_sc_max_y(self.sc_min_y)
elif self.sc_all:
# case : min < max and uniform = True
self.up_ui_sc_min_x(self.sc_min_y)
self.up_ui_sc_min_z(self.sc_min_y)
self.up_ui_sc_max_x(self.sc_max_y)
self.up_ui_sc_max_z(self.sc_max_y)
elif test:
# case : min > max and uniform = False
self.up_ui_sc_max_y(self.sc_min_y)
def up_sc_min_z(self, context):
"""Update sc_max_z if sc_min_z is higher"""
if self.is_prog_change:
self.is_prog_change = False
else:
test = self.sc_min_z > self.sc_max_z
if test and self.sc_all:
# case : min > max and uniform = True
self.up_ui_sc_max_z(self.sc_min_z)
# with uniform : min_x = min_y = min_z same for max_
self.up_ui_sc_min_x(self.sc_min_z)
self.up_ui_sc_min_y(self.sc_min_z)
self.up_ui_sc_max_x(self.sc_min_z)
self.up_ui_sc_max_y(self.sc_min_z)
elif self.sc_all:
# case : min < max and uniform = True
self.up_ui_sc_min_x(self.sc_min_z)
self.up_ui_sc_min_y(self.sc_min_z)
self.up_ui_sc_max_x(self.sc_max_z)
self.up_ui_sc_max_y(self.sc_max_z)
elif test:
# case : min > max and uniform = False
self.up_ui_sc_max_y(self.sc_min_z)
def up_sc_max_x(self, context):
"""Update sc_min_x if sc_max_x is lower"""
if self.is_prog_change:
self.is_prog_change = False
else:
test = self.sc_min_x > self.sc_max_x
if test and self.sc_all:
# case : min > max and uniform = True
self.up_ui_sc_min_x(self.sc_max_x)
# with uniform : min_x = min_y = min_z same for max_
self.up_ui_sc_max_y(self.sc_max_x)
self.up_ui_sc_max_z(self.sc_max_x)
self.up_ui_sc_min_y(self.sc_max_x)
self.up_ui_sc_min_z(self.sc_max_x)
elif self.sc_all:
# case : min < max and uniform = True
self.up_ui_sc_max_y(self.sc_max_x)
self.up_ui_sc_max_z(self.sc_max_x)
self.up_ui_sc_min_y(self.sc_min_x)
self.up_ui_sc_min_z(self.sc_min_x)
elif test:
# case : min > max and uniform = False
self.up_ui_sc_min_x(self.sc_max_x)
def up_sc_max_y(self, context):
"""Update sc_min_y if sc_max_y is lower"""
if self.is_prog_change:
self.is_prog_change = False
else:
test = self.sc_min_y > self.sc_max_y
if test and self.sc_all:
# case : min > max and uniform = True
self.up_ui_sc_min_y(self.sc_max_y)
# with uniform : min_x = min_y = min_z same for max_
self.up_ui_sc_max_x(self.sc_max_y)
self.up_ui_sc_max_z(self.sc_max_y)
self.up_ui_sc_min_x(self.sc_max_y)
self.up_ui_sc_min_z(self.sc_max_y)
elif self.sc_all:
# case : min < max and uniform = True
self.up_ui_sc_max_x(self.sc_max_y)
self.up_ui_sc_max_z(self.sc_max_y)
self.up_ui_sc_min_x(self.sc_min_y)
self.up_ui_sc_min_z(self.sc_min_y)
elif test:
# case : min > max and uniform = False
self.up_ui_sc_min_y(self.sc_max_y)
def up_sc_max_z(self, context):
"""Update sc_min_z if sc_max_z is lower"""
if self.is_prog_change:
self.is_prog_change = False
else:
test = self.sc_min_z > self.sc_max_z
if test and self.sc_all:
# case : min > max and uniform = True
self.up_ui_sc_min_z(self.sc_max_z)
# with uniform : min_x = min_y = min_z same for max_
self.up_ui_sc_max_x(self.sc_max_z)
self.up_ui_sc_max_y(self.sc_max_z)
self.up_ui_sc_min_x(self.sc_max_z)
self.up_ui_sc_min_y(self.sc_max_z)
elif self.sc_all:
# case : min < max and uniform = True
self.up_ui_sc_max_x(self.sc_max_z)
self.up_ui_sc_max_y(self.sc_max_z)
self.up_ui_sc_min_x(self.sc_min_z)
self.up_ui_sc_min_y(self.sc_min_z)
elif test:
# case : min > max and uniform = False
self.up_ui_sc_min_z(self.sc_max_z)
def up_rot_min(self, context):
"""Update rot_max if rot_min is higher"""
if self.is_prog_change:
self.is_prog_change = False
else:
for i in range(3):
if self.rot_min[i] > self.rot_max[i]:
self.is_prog_change = True
self.rot_max[i] = self.rot_min[i]
def up_rot_max(self, context):
"""Update rot_min if rot_max is lower"""
if self.is_prog_change:
self.is_prog_change = False
else:
for i in range(3):
if self.rot_min[i] > self.rot_max[i]:
self.is_prog_change = True
self.rot_min[i] = self.rot_max[i]
# ----------------------- reset all properties ------------------
def up_ui_reset(self):
"""Reset all UI properties"""
self.up_ui_updateCount(2)
self.up_ui_updateRow(1)
self.up_ui_is_copy()
self.up_ui_tr_offset(Vector((2.0, 0.0, 0.0)))
self.up_ui_tr_global(Vector((2.0, 0.0, 0.0)))
self.up_ui_sc_offset((100, 100, 100))
self.up_ui_sc_global((100, 100, 100))
self.up_ui_rot_offset(Vector((0.0, 0.0, 0.0)))
self.up_ui_rot_global(Vector((0.0, 0.0, 0.0)))
self.up_ui_updateAlter(0)
self.total = "2"
self.erow = "2"
count: bpy.props.IntProperty(
name='Count',
description="Number of elements, original count as one",
default=2,
soft_min=2,
update=updateCount
)
row: bpy.props.IntProperty(
name="Row",
description="Number of row(s)",
default=1,
soft_min=1,
soft_max=100,
update=update_row
)
"""Allow a variation in the row :
if row gets n elements, row +1 will get (n + variation) elements
only if n + variation > 0
"""
alter: bpy.props.IntProperty(
name=" Row variation",
description="""Variation in the number of elements in a row. (between -5 and 5).
\n Be careful with it""",
default=0,
soft_min=-5,
soft_max=5,
update=update_alter
)
total: bpy.props.StringProperty(
name="Total",
description="Total of elements in array",
default="2"
)
erow: bpy.props.StringProperty(
description="Number of elements in the current row.",
default="2"
)
# if alter <> 0, how align the rows
align: bpy.props.EnumProperty(
name='Align',
description="Align of rows when variation is not zero",
items=[
('LEFT', 'Left', "Align to the left", 'ALIGN_LEFT', 0),
('CENTER', 'Center', "Align to the center", 'ALIGN_CENTER', 1),
('RIGHT', 'Right', "Align to the right", 'ALIGN_RIGHT', 2)
],
default='LEFT',
update=update_align
)
# Vector alignment depends on align
ralign: bpy.props.FloatVectorProperty(
subtype='TRANSLATION',
unit='LENGTH',
default=(0.0, 0.0, 0.0)
)
# booleans use to know if user or prog change the value to avoid continuous loop
is_prog_change: bpy.props.BoolProperty(default=False) # True if prog change value
# which one between offset and global user calls last, True is offset, False global
is_tr_off_last: bpy.props.BoolProperty(default=True)
# True if addon is initialised
already_start: bpy.props.BoolProperty(default=False)
# if the user need a single copy or a duplicate (link object)
is_copy: bpy.props.BoolProperty(
name="Copy only",
description="Duplicate or copy, default is duplicate",
default=False,
update=update_is_copy
)
# translation vector offset
tr_offset: bpy.props.FloatVectorProperty(
name='Offset',
description="Distance between elements",
default=(2.0, 0.0, 0.0),
subtype='TRANSLATION',
unit='LENGTH',
precision=2,
step=50,
options={'ANIMATABLE'},
update=update_offset
)
# global translation distance
tr_global: bpy.props.FloatVectorProperty(
name='Global',
description="Distance between the original and the last element",
default=(2.0, 0.0, 0.0),
subtype='TRANSLATION',
unit='LENGTH',
precision=2,
step=50,
options={'ANIMATABLE'},
update=update_global
)
tr_second: bpy.props.FloatVectorProperty(
name="Translation",
description="Additional offset distance for rows",
default=(0.0, 0.0, 0.0),
subtype='TRANSLATION',
unit='LENGTH',
precision=2,
step=50,
update=update_second
)
at_pivot: bpy.props.PointerProperty(
name='Pivot',
description="Object you want as pivot point. If none, pivot point is the object's origine",
type=bpy.types.Object
)
# scaling vector offset
sc_offset: bpy.props.FloatVectorProperty(
name='Offset',
description="Incremental scale of the next elements",
default=(100.0, 100.0, 100.0),
subtype='XYZ',
precision=1,
step=100,
options={'ANIMATABLE'},
update=update_offset
)
# global scaling
sc_global: bpy.props.FloatVectorProperty(
name='Global',
description="Scale of the last element",
default=(100.0, 100.0, 100.0),
subtype='XYZ',
precision=1,
step=100,
options={'ANIMATABLE'},
update=update_global
)
sc_second: bpy.props.FloatVectorProperty(
name='Scale',
description="Additionnal scale for rows",
default=(100.0, 100.0, 100.0),
subtype='XYZ',
precision=1,
step=100,
options={'ANIMATABLE'},
update=update_second
)
# rotation vector offset
rot_offset: bpy.props.FloatVectorProperty(
name='Offset',
description="Angle between each element",
default=(0.0, 0.0, 0.0),
subtype='XYZ',
unit='ROTATION',
step=500, # = 5
options={'ANIMATABLE'},
update=update_offset
)
# global rotation
rot_global: bpy.props.FloatVectorProperty(
name='Global',
description="Maximum angle from the reference to the last element",
default=(0.0, 0.0, 0.0),
subtype='XYZ',
unit='ROTATION',
step=500, # = 5
options={'ANIMATABLE'},
update=update_global
)
rot_second: bpy.props.FloatVectorProperty(
name='Rotation',
description="Additionnal rotation for rows",
default=(0.0, 0.0, 0.0),
subtype='XYZ',
unit='ROTATION',
step=500,
options={'ANIMATABLE'},
update=update_second
)
# ----------------------- random part ---------------------------
at_seed: bpy.props.IntProperty(
name='Seed',
description="Seed value for random",
soft_min=0,
default=0,
update=update_seed
)
at_mode: bpy.props.EnumProperty(
name="Mode",
description="Choose between simple mode or advanced",
items=(('SIM', 'Simple', "Simple mode"),
('ADV', 'Advanced', "Advanced mode")),
default='SIM'
)
at_is_tr: bpy.props.BoolProperty(
name="Add translation",
description="Add translation in random?",
default=False
)
at_is_sc: bpy.props.BoolProperty(
name="Add scale",
description="Add scale in random?",
default=False
)
at_is_rot: bpy.props.BoolProperty(
name="Add rotation",
description="Add rotation in random?",
default=False
)
tr_min: bpy.props.FloatVectorProperty(
name="min",
description="Minimum random value for translation",
unit='LENGTH',
default=(0.0, 0.0, 0.0),
update=up_tr_min
)
tr_max: bpy.props.FloatVectorProperty(
name="max",
description="Maximum random value for translation",
unit='LENGTH',
default=(0.0, 0.0, 0.0),
update=up_tr_max
)
tr_rand: bpy.props.FloatProperty(
name="Translation",
description="Random values for all axis",
unit='LENGTH',
default=0.0,
update=update_rtr
)
sc_all: bpy.props.BoolProperty(
name="uniform scale",
description="Uniform or non uniform scale, default is non uniform.",
default=False
)
sc_min_x: bpy.props.IntProperty(
name="min",
description="Minimum random value for x scale",
default=100,
update=up_sc_min_x
)
sc_min_y: bpy.props.IntProperty(
name="min",
description="Minimum random value for y scale",
default=100,
update=up_sc_min_y
)
sc_min_z: bpy.props.IntProperty(
name="min",
description="Minimum random value for z scale",
default=100,
update=up_sc_min_z
)
sc_max_x: bpy.props.IntProperty(
name="max",
description="Maximum random value for x scale",
default=100,
update=up_sc_max_x
)
sc_max_y: bpy.props.IntProperty(
name="max",
description="Maximum random value for y scale",
default=100,
update=up_sc_max_y
)
sc_max_z: bpy.props.IntProperty(
name="max",
description="Maximum random value for z scale",
default=100,
update=up_sc_max_z
)
sc_rand: bpy.props.IntProperty(
name="Scale",
description="Random scale value for all axis",
default=100,
update=update_rsc
)
rot_min: bpy.props.FloatVectorProperty(
name="min",
description="Minimum random value for rotation",
unit='ROTATION',
default=(0.0, 0.0, 0.0),
update=up_rot_min
)
rot_max: bpy.props.FloatVectorProperty(
name="max",
description="Maximum random value for rotation",
unit='ROTATION',
default=(0.0, 0.0, 0.0),
update=up_rot_max
)
rot_rand: bpy.props.FloatProperty(
name="Rotation",
description="Random rotation for all axis",
unit='ROTATION',
default=0.0,
update=update_rrot
)