Add files via upload

This commit is contained in:
0000OOOO0000
2020-11-20 17:33:46 +02:00
committed by GitHub
parent 08b88e29fc
commit 774f049e12
52 changed files with 19571 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
# -*- coding: utf-8 -*-
import bpy
import math
import random
from mathutils import Matrix
from mathutils import Vector
from . import cfg
def at_random_fill(min, max):
first = random.uniform(min, max)
second = random.uniform(min, max)
if first <= second:
return(first, second)
else:
return(second, first)
def at_random(seed, totalc, totalr, mint, maxt, mins, maxs, minr, maxr, btr, bsc, brot, uniform,
tr1, tr2, sc1, sc2, r1, r2, pivot, varia, valign):
"""Random function for translation, scale and rotation,
seed : seed for random
totalc : number of elements in column
totalr : number of elements in row
mint : minimum for translation
maxt : maximum for translation
mins : minimum for scale
maxs : maximum for scale
minr : minimum for rotation
maxr : maximun for rotation
btr : (boolean) use translation or not
bsc : (boolean) use scale or not
brot : (boolean) use rotation or not
uniform : (boolean) use uniform scale or not
tr1 : translation offset of the column
tr2 : translation offset of the row
sc1 : scale offset of the column
sc2 : scale offset of the row
r1 : rotation offset of the column
r2 : rotation offset of the row
pivot : pivot
varia : variation of rows
valign : Vector of align of rows
"""
random.seed(seed)
tr, sc, rot = [0, 0, 0], [0, 0, 0], [0, 0, 0]
xyz_vec = (x_axis(), y_axis(), z_axis())
ref_name = cfg.atools_objs[0][0]
for j in range(totalr):
for k in range(totalc + j*varia):
elem_name = cfg.atools_objs[j][k]
if elem_name == ref_name:
continue
elem = bpy.data.objects[elem_name]
for i in range(3):
tr[i] = random.uniform(mint[i], maxt[i])
sc[i] = random.uniform(mins[i]/100, maxs[i]/100)
rot[i] = random.uniform(minr[i], maxr[i])
if uniform:
sc[0] = sc[1] = sc[2]
mt = Matrix.Translation(tr)
ms = Matrix.Scale(sc[0], 4, (1, 0, 0)) @ Matrix.Scale(sc[1], 4, (0, 1, 0)) @ Matrix.Scale(sc[2], 4, (0, 0, 1))
mr = Matrix.Rotation(rot[0], 4, (1, 0, 0)) @ Matrix.Rotation(rot[1], 4, (0, 1, 0)) @ Matrix.Rotation(rot[2], 4, (0, 0, 1))
# recalculate the position...
vt, vs, vr = tsr(cfg.ref_mtx, k, j, tr1, tr2, sc1, sc2, Vector(r1), Vector(r2), valign)
if pivot is not None:
emat = at_all_in_one(cfg.ref_mtx, vr, xyz_vec, vt, vs, pivot.location)
else:
emat = at_all_in_one(cfg.ref_mtx, vr, xyz_vec, vt, vs, cfg.ref_mtx.translation)
elem.matrix_world = emat
if btr:
elem.matrix_world @= mt
if bsc:
elem.matrix_world @= ms
if brot:
elem.matrix_world @= mr
def x_axis():
"""Get the x axis"""
return Vector((1.0, 0.0, 0.0))
def y_axis():
"""Get the y axis"""
return Vector((0.0, 1.0, 0.0))
def z_axis():
"""Get the z axis"""
return Vector((0.0, 0.0, 1.0))
def xyz_axis():
"""Get the xyz axis"""
return Vector((1.0, 1.0, 1.0))
def at_all_in_one(ref, angle, vecxyz, vec_tr, vec_sc, pivot):
"""Return the matrix of transformations"""
# Matrix is composed by location @ rotation @ scale
loc_ref, rot_ref, sc_ref = ref.decompose()
# ref_location = bpy.data.objects[cfg.atools_objs[0][0]].location
loc_ma = Matrix.Translation(loc_ref)
rot_ma = rot_ref.to_matrix().to_4x4()
sc_ma = Matrix.Scale(sc_ref[0], 4, (1, 0, 0)) @ Matrix.Scale(sc_ref[1], 4, (0, 1, 0)) @ Matrix.Scale(sc_ref[2], 4, (0, 0, 1))
mt = Matrix.Translation(pivot - loc_ref)
mr = Matrix.Rotation(angle[0], 4, vecxyz[0]) @ Matrix.Rotation(angle[1], 4, vecxyz[1]) @ Matrix.Rotation(angle[2], 4, vecxyz[2])
mra = mt @ mr @ mt.inverted()
trm = Matrix.Translation(vec_tr)
scm = Matrix.Scale(vec_sc[0], 4, (1, 0, 0)) @ Matrix.Scale(vec_sc[1], 4, (0, 1, 0)) @ Matrix.Scale(vec_sc[2], 4, (0, 0, 1))
if pivot == loc_ref:
mw = loc_ma @ rot_ma @ trm @ scm @ sc_ma @ mr
else:
mw = loc_ma @ mra @ rot_ma @ trm @ scm @ sc_ma
return mw
def fill_rotation(context):
prop = context.scene.arraytools_prop
offset = prop.rot_offset
for i in range(3):
if offset[i] == 0.0:
prop.rot_min[i], prop.rot_max[i] = at_random_fill(-math.pi, math.pi)
else:
prop.rot_min[i], prop.rot_max[i] = at_random_fill(-offset[i]*2, offset[i]*2)
def sum_serie(n, factor):
"""Return the sum of the serie 1+2+3+4+...+n
with a factor
"""
return ((n * (n - 1)) / 2) * factor
# (T)ranslate (S)cale (R)otation vector
def tsr(mat, col, row, tcol, trow, scol, srow, rcol, rrow, ralign):
"""Retrieve the translation, scale and rotation vector according
to the position in the array
mat : matrix of the reference object
col : position in column
row : position in row
tcol : translate offset in column
trow : translate offset in row
scol : scale offset in column
srow : scale offset in row
rcol : rotation offset in column
rrow : rotation offset in row
ralign : row align
"""
translate = col * tcol + row * trow + row * ralign
rotate = col * Vector(rcol) + row * Vector(rrow)
s1 = col * (mat.to_scale() - (scol/100))
s2 = row * (mat.to_scale() - (srow/100))
scale = xyz_axis() - s1 - s2
return translate, scale, rotate

View File

@@ -0,0 +1,219 @@
# -*- coding: utf-8 -*-
# ---------------------------- Operators ------------------------
import bpy
import math
from mathutils import Vector
from . import cfg
from . import at_interface
from . at_calc_func import at_random_fill, fill_rotation
class OBJECT_OT_at_start(bpy.types.Operator):
"""Start and init the addon"""
bl_idname = 'scene.at_op'
bl_label = "Start array"
@classmethod
def poll(cls, context):
return not context.scene.arraytools_prop.already_start
def execute(self, context):
cfg.init_array_tool(context)
return {'FINISHED'}
class OBJECT_OT_at_done(bpy.types.Operator):
"""Apply the settings"""
bl_idname = 'scene.at_done'
bl_label = "Done !"
def execute(self, context):
cfg.atools_objs.clear()
#cfg.at_mtx_list.clear()
array_col = bpy.data.collections.get(cfg.col_name)
cfg.col_name = "Array_collection"
context.scene.arraytools_prop.up_ui_reset()
context.scene.arraytools_prop.already_start = False
return {'FINISHED'}
class OBJECT_OT_at_cancel(bpy.types.Operator):
"""Cancel the settings"""
bl_idname = 'scene.at_cancel'
bl_label = "Cancel"
def execute(self, context):
scn = context.scene
scn.arraytools_prop.at_del_all(True)
scn.arraytools_prop.up_ui_reset()
scn.arraytools_prop.already_start = False
cfg.col_name = "Array_collection"
return {'FINISHED'}
class OBJECT_OT_fill_tr(bpy.types.Operator):
"""Fill the random translation fields"""
bl_idname = 'scene.fill_tr'
bl_label = "Fill"
def execute(self, context):
prop = context.scene.arraytools_prop
offset = prop.tr_offset
for i in range(3):
if offset[i] == 0.0:
prop.tr_min[i], prop.tr_max[i] = at_random_fill(-3.0, 3.0)
else:
prop.tr_min[i], prop.tr_max[i] = at_random_fill(-offset[i]/2, offset[i]/2)
return{'FINISHED'}
class OBJECT_OT_fill_sc(bpy.types.Operator):
"""Fill the random scale fields"""
bl_idname = 'scene.fill_sc'
bl_label = "Fill"
def execute(self, context):
prop = context.scene.arraytools_prop
offset = prop.sc_offset
if 100 in [offset[0], offset[1], offset[2]]:
prop.sc_min_x, prop.sc_max_x = at_random_fill(40.0, 120.0)
prop.sc_min_y, prop.sc_max_y = at_random_fill(40.0, 120.0)
prop.sc_min_z, prop.sc_max_z = at_random_fill(40.0, 120.0)
else:
rand = [(100 - offset[i]) / 2 for i in range(3)]
print(rand)
prop.sc_min_x, prop.sc_max_x = at_random_fill(offset[0]-rand[0], offset[0]+rand[0])
prop.sc_min_y, prop.sc_max_y = at_random_fill(offset[1]-rand[1], offset[1]+rand[1])
prop.sc_min_z, prop.sc_max_z = at_random_fill(offset[2]-rand[2], offset[2]+rand[2])
if prop.sc_all:
prop.sc_min_x = prop.sc_min_y = prop.sc_min_z
prop.sc_max_x = prop.sc_max_y = prop.sc_max_z
return {'FINISHED'}
class OBJECT_OT_fill_rot(bpy.types.Operator):
"""Fill the random rotation fields"""
bl_idname = 'scene.fill_rot'
bl_label = "Fill"
def execute(self, context):
fill_rotation(context)
return {'FINISHED'}
class OBJECT_OT_x360(bpy.types.Operator):
"""Quick 360 degrees on X axis"""
bl_idname = 'scene.x360'
bl_label = "360"
def execute(self, context):
prop = context.scene.arraytools_prop
prop.tr_offset = Vector((0.0, 0.0, 0.0))
prop.rot_global = Vector((math.pi/180*360, 0.0, 0.0))
return{'FINISHED'}
class OBJECT_OT_y360(bpy.types.Operator):
"""Quick 360 degrees on Y axis"""
bl_idname = 'scene.y360'
bl_label = "360"
def execute(self, context):
prop = context.scene.arraytools_prop
prop.tr_offset = Vector((0.0, 0.0, 0.0))
prop.rot_global = Vector((0.0, math.pi/180*360, 0.0))
return{'FINISHED'}
class OBJECT_OT_z360(bpy.types.Operator):
"""Quick 360 degrees on Z axis"""
bl_idname = 'scene.z360'
bl_label = "360"
def execute(self, context):
prop = context.scene.arraytools_prop
prop.tr_offset = Vector((0.0, 0.0, 0.0))
prop.rot_global = Vector((0.0, 0.0, math.pi/180*360))
return{'FINISHED'}
class OBJECT_OT_reset_tr(bpy.types.Operator):
"""Reset the settings of random translation"""
bl_idname = 'scene.at_reset_tr'
bl_label = 'Reset'
def execute(self, context):
prop = context.scene.arraytools_prop
prop.tr_min[0], prop.tr_min[1], prop.tr_min[2] = 0.0, 0.0, 0.0
prop.tr_max[0], prop.tr_max[1], prop.tr_max[2] = 0.0, 0.0, 0.0
# if operator is used many times
# get weird result != 0 with vector
# prop.tr_max = Vector((0.0, 0.0, 0.0))
return {'FINISHED'}
class OBJECT_OT_reset_sc(bpy.types.Operator):
"""Reset the settings of random scale"""
bl_idname = 'scene.at_reset_sc'
bl_label = 'Reset'
def execute(self, context):
prop = context.scene.arraytools_prop
prop.sc_min_x, prop.sc_min_y, prop.sc_min_z = 100, 100, 100
prop.sc_max_x, prop.sc_max_y, prop.sc_max_z = 100, 100, 100
return{'FINISHED'}
class OBJECT_OT_reset_rot(bpy.types.Operator):
"""Reset the settings of random rotation"""
bl_idname = 'scene.at_reset_rot'
bl_label = 'Reset'
def execute(self, context):
prop = context.scene.arraytools_prop
prop.rot_min[0], prop.rot_min[1], prop.rot_min[2] = 0.0, 0.0, 0.0
prop.rot_max[0], prop.rot_max[1], prop.rot_max[2] = 0.0, 0.0, 0.0
return{'FINISHED'}
class OBJECT_OT_reset_second(bpy.types.Operator):
"""Reset the settings of row options"""
bl_idname = 'scene.at_reset_second'
bl_label = 'Reset'
def execute(self, context):
prop = context.scene.arraytools_prop
prop.tr_second = (0,0,0)
prop.sc_second = (100,100,100)
prop.rot_second = (0,0,0)
return {'FINISHED'}
class OBJECT_OT_error(bpy.types.Operator):
"""Draw a message box to display error"""
bl_idname = "info.at_error"
bl_label = "Message info"
info: bpy.props.StringProperty(
name = "Message",
description = "Display a message",
default = ''
)
def execute(self, context):
self.report({'INFO'}, self.info)
print(self.info)
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
layout.label(text=self.info)
layout.label(text="")

View File

@@ -0,0 +1,210 @@
# -*- coding: utf-8 -*-
from bpy.types import Panel
from . import cfg
# ---------------------------- Panel --------------------------------
class UIPANEL_PT_def(Panel):
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Array Tools"
class UIPANEL_PT_trans(UIPANEL_PT_def):
"""Panel containing the settings for translation, scale and rotation array"""
bl_label = "Array Tools"
@classmethod
def poll(cls, context):
return (len(context.selected_objects) > 0 and (context.object.mode == 'OBJECT'))
def draw(self, context):
layout = self.layout
scn = context.scene
my_prop = scn.arraytools_prop
row = layout.row()
row.operator('scene.at_op')
row = layout.row()
if not my_prop.already_start:
row.alignment = 'CENTER'
row.label(text="~ Click to begin ~")
else:
row.prop(my_prop, 'is_copy')
row.prop(my_prop, 'count')
box = layout.box()
box.label(text="Translation")
col = box.column()
split = col.split()
split.prop(my_prop, 'tr_offset')
split.prop(my_prop, 'tr_global')
row = layout.row()
row.prop(my_prop, 'at_pivot')
box = layout.box()
box.label(text="Scaling (%)")
col = box.column()
split = col.split()
split.prop(my_prop, 'sc_offset')
split.prop(my_prop, 'sc_global')
box = layout.box()
if scn.unit_settings.system_rotation == 'DEGREES':
box.label(text="Rotation (degrees)")
else:
box.label(text="Rotation (radians)")
split = box.split(factor=0.08)
col = split.column(align=True)
col.label(text='')
col.operator('scene.x360', text='X')
col.operator('scene.y360', text='Y')
col.operator('scene.z360', text='Z')
col = split.column()
col.prop(my_prop, 'rot_offset')
col = split.column()
col.prop(my_prop, 'rot_global')
box = layout.box()
row = box.row()
row.scale_y = 1.5
row.operator('scene.at_done')
row.operator('scene.at_cancel')
row = box.row()
row.scale_y = 0.3
row.alignment = 'CENTER'
row.label(text="~ Tansforms are NOT applied ~")
class UIPANEL_PT_rows(UIPANEL_PT_def):
"""Panel containing the row options"""
bl_parent_id = 'UIPANEL_PT_trans'
bl_label = 'Rows options'
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
my_prop = context.scene.arraytools_prop
if my_prop.already_start:
row = layout.row()
row.prop(my_prop, 'count')
row.prop(my_prop, 'row')
row = layout.row()
row.scale_y = 0.8
row.prop(my_prop, 'align', icon_only=True, expand=True)
row.prop(my_prop, 'alter')
row = layout.row()
row.alignment = 'CENTER'
row.scale_x = 1.5
row.scale_y = 0.6
row.label(text=" - Offset settings -")
row.scale_x = 0.8
row.operator('scene.at_reset_second')
layout.use_property_split = True
col = layout.column()
row = col.row(align=True)
row.prop(my_prop, 'tr_second')
col = layout.column()
row = col.row(align=True)
row.prop(my_prop, 'sc_second')
col = layout.column()
row = col.row(align=True)
row.prop(my_prop, 'rot_second')
row = layout.row()
row.scale_y = 0.5
row.label(text="Total : " + my_prop.total + " | current row : " + my_prop.erow)
"""
box = layout.box()
box.prop(my_prop, 'tr_second')
#row = layout.row()
box.prop(my_prop, 'sc_second')
#row = layout.row()
box.prop(my_prop, 'rot_second')
"""
class UIPANEL_PT_options(UIPANEL_PT_def):
"""Panel containing the random options"""
bl_parent_id = 'UIPANEL_PT_trans'
bl_label = 'Random options'
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
my_prop = context.scene.arraytools_prop
layout.enabled = my_prop.already_start
row = layout.row()
row.alignment = 'CENTER'
row.prop(my_prop, 'at_seed')
row = layout.row()
row.prop(my_prop, 'at_mode', expand=True)
row = layout.row()
if my_prop.at_mode == 'SIM':
row.prop(my_prop, 'at_is_tr')
row = layout.row()
row.prop(my_prop, 'tr_rand')
row = layout.row()
row.prop(my_prop, 'at_is_sc')
row = layout.row()
row.prop(my_prop, 'sc_rand')
row = layout.row()
row.prop(my_prop, 'at_is_rot')
row = layout.row()
row.prop(my_prop, 'rot_rand')
else:
row.label(text=' ')
row.label(text='X')
row.label(text='Y')
row.label(text='Z')
row = layout.row()
row.prop(my_prop, 'at_is_tr')
row.scale_x = 0.5
row.scale_y = 0.7
row.operator('scene.at_reset_tr')
row.operator('scene.fill_tr')
row = layout.row()
row.prop(my_prop, 'tr_min')
row = layout.row()
row.prop(my_prop, 'tr_max')
row = layout.row()
row.prop(my_prop, 'at_is_sc')
row.scale_x = 0.5
row.scale_y = 0.7
row.operator('scene.at_reset_sc')
row.operator('scene.fill_sc')
row = layout.row()
row.alignment = "CENTER"
row.scale_y = 0.7
row.prop(my_prop, 'sc_all')
row = layout.row(align=True)
row.label(text='min:')
row.prop(my_prop, 'sc_min_x', text='')
row.prop(my_prop, 'sc_min_y', text='')
row.prop(my_prop, 'sc_min_z', text='')
row = layout.row(align=True)
row.label(text='max:')
row.prop(my_prop, 'sc_max_x', text='')
row.prop(my_prop, 'sc_max_y', text='')
row.prop(my_prop, 'sc_max_z', text='')
row = layout.row()
row.prop(my_prop, "at_is_rot")
row.scale_x = 0.5
row.scale_y = 0.7
row.operator('scene.at_reset_rot')
row.operator('scene.fill_rot')
row = layout.row()
row.prop(my_prop, 'rot_min')
row = layout.row()
row.prop(my_prop, 'rot_max')

View File

@@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
import bpy
# count values, contains only 2 values : old count and current
at_count_values = []
# row value, contains old row and current
at_row_values = []
# alter values, contains old and current
at_alter = []
# maximun row according to column and alter
maxrow = 1
# list of the copies / list of lists
atools_objs = []
ref_mtx = [] # reference matrix
# collection name
col_name = "Array_collection"
def init_array_tool(context):
"""Initialisation of the array tools"""
global at_count_values
global at_row_values
global at_alter
global atools_objs
global ref_mtx
global col_name
prop = context.scene.arraytools_prop
name = col_name
i = 1
collect = bpy.data.collections.get(col_name)
# create and link the new collection
if collect is None:
array_col = bpy.data.collections.new(col_name)
bpy.context.scene.collection.children.link(array_col)
else:
# if a collection already exist, create a new one
while bpy.data.collections.get(name) is not None:
name = col_name + str(i)
i += 1
array_col = bpy.data.collections.new(name)
bpy.context.scene.collection.children.link(array_col)
col_name = name
if not prop.already_start:
at_count_values = [1, 2]
at_row_values = [0, 1]
at_alter = [0, 0]
active = context.active_object
prop.already_start = True
prop.is_tr_off_last = True
if active is not None:
atools_objs.append([active.name])
ref_mtx = active.matrix_world.copy()
del active
prop.add_in_column(prop.row)
# no need anymore
else:
print("No object selected")
else:
print("Already started!")
def add_count(value):
"""Save the current count"""
global at_count_values
at_count_values.append(value)
def del_count():
"""Del the previous count"""
global at_count_values
del at_count_values[0]
def add_row(value):
"""Save the current row"""
global at_row_values
at_row_values.append(value)
def del_row():
""" Del the previous row value"""
global at_row_values
del at_row_values[0]
def add_alter(value):
"""save the current variation"""
global at_alter
at_alter.append(value)
def del_alter():
"""Remove previous variation"""
global at_alter
del at_alter[0]
def display_error(msg):
"""Call the operator to display an error message"""
bpy.ops.info.at_error('INVOKE_DEFAULT', info = msg)

View File

@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import bpy
from . import cfg
from . import at_interface
bl_info = {
"name": "Array_tools",
"author": "Elreenys",
"description": "Tools to create array of objects",
"blender": (2, 80, 0),
"version": (1, 2, 1),
"location": "View3D > sidebar > array tools tab",
"category": "Object"
}
classes = (
at_operators.OBJECT_OT_at_start,
at_operators.OBJECT_OT_at_cancel,
at_operators.OBJECT_OT_at_done,
at_operators.OBJECT_OT_fill_tr,
at_operators.OBJECT_OT_fill_sc,
at_operators.OBJECT_OT_fill_rot,
at_operators.OBJECT_OT_x360,
at_operators.OBJECT_OT_y360,
at_operators.OBJECT_OT_z360,
at_operators.OBJECT_OT_reset_tr,
at_operators.OBJECT_OT_reset_sc,
at_operators.OBJECT_OT_reset_rot,
at_operators.OBJECT_OT_reset_second,
at_operators.OBJECT_OT_error,
at_panel.UIPANEL_PT_trans,
at_panel.UIPANEL_PT_rows,
at_panel.UIPANEL_PT_options,
at_interface.ArrayTools_props
)
def register():
scene = bpy.types.Scene
pp = bpy.props.PointerProperty
for cls in classes:
bpy.utils.register_class(cls)
scene.arraytools_prop = pp(type=at_interface.ArrayTools_props)
def unregister():
del bpy.types.Scene.arraytools_prop
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
if __name__ == '__main__':
register()