# -*- 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 )