space_view3d_test_scenes: initial commit: move from advanced objects
[blender-addons-contrib.git] / add_advanced_objects_menu / random_box_structure.py
1 # gpl: author Dannyboy
2
3 bl_info = {
4     "name": "Add Random Box Structure",
5     "author": "Dannyboy",
6     "version": (1, 0, 1),
7     "location": "View3D > Add > Make Box Structure",
8     "description": "Fill selected box shaped meshes with randomly sized cubes",
9     "warning": "",
10     "wiki_url": "",
11     "tracker_url": "dannyboypython.blogspot.com",
12     "category": "Object"}
13
14 import bpy
15 import random
16 from bpy.types import Operator
17 from bpy.props import (
18         BoolProperty,
19         FloatProperty,
20         FloatVectorProperty,
21         IntProperty,
22         )
23
24
25 class makestructure(Operator):
26     bl_idname = "object.make_structure"
27     bl_label = "Add Random Box Structure"
28     bl_description = ("Create a randomized structure made of boxes\n"
29                       "with various control parameters\n"
30                       "Needs an existing Active Mesh Object")
31     bl_options = {'REGISTER', 'UNDO'}
32
33     dc: BoolProperty(
34             name="Delete Base Mesh(es)",
35             default=True
36             )
37     wh: BoolProperty(
38             name="Stay Within Bounds",
39             description="Keeps cubes from exceeding base mesh bounds",
40             default=True
41             )
42     uf: BoolProperty(
43             name="Uniform Cube Quantity",
44             default=False
45             )
46     qn: IntProperty(
47             name="Cube Quantity",
48             default=10,
49             min=1, max=1500
50             )
51     mn: FloatVectorProperty(
52             name="Min Scales",
53             default=(0.1, 0.1, 0.1),
54             subtype='XYZ'
55             )
56     mx: FloatVectorProperty(
57             name="Max Scales",
58             default=(2.0, 2.0, 2.0),
59             subtype='XYZ'
60             )
61     lo: FloatVectorProperty(
62             name="XYZ Offset",
63             default=(0.0, 0.0, 0.0),
64             subtype='XYZ'
65             )
66     rsd: FloatProperty(
67             name="Random Seed",
68             default=1
69             )
70
71     @classmethod
72     def poll(cls, context):
73         obj = context.active_object
74         return obj is not None and obj.type == "MESH" and obj.mode == "OBJECT"
75
76     def draw(self, context):
77         layout = self.layout
78
79         box = layout.box()
80         box.label(text="Options:")
81         box.prop(self, "dc")
82         box.prop(self, "wh")
83         box.prop(self, "uf")
84
85         box = layout.box()
86         box.label(text="Parameters:")
87         box.prop(self, "qn")
88         box.prop(self, "mn")
89         box.prop(self, "mx")
90         box.prop(self, "lo")
91         box.prop(self, "rsd")
92
93     def execute(self, context):
94         rsdchange = self.rsd
95         oblst = []
96         uvyes = 0
97         bpy.ops.collection.create(name='Cubagrouper')
98         bpy.ops.collection.objects_remove()
99
100         for ob in bpy.context.selected_objects:
101             oblst.append(ob)
102
103         for obj in oblst:
104             bpy.ops.object.select_pattern(pattern=obj.name)  # Select base mesh
105             bpy.context.view_layer.objects.active = obj
106             if obj.data.uv_layers[:] != []:
107                 uvyes = 1
108             else:
109                 uvyes = 0
110             bpy.ops.object.collection_link(group='Cubagrouper')
111             dim = obj.dimensions
112             rot = obj.rotation_euler
113             if self.uf is True:
114                 area = dim.x * dim.y * dim.z
115             else:
116                 area = 75
117
118             for cube in range(round((area / 75) * self.qn)):
119                 random.seed(rsdchange)
120                 pmn = self.mn  # Proxy values
121                 pmx = self.mx
122                 if self.wh is True:
123                     if dim.x < pmx.x:  # Keeping things from exceeding proper size
124                         pmx.x = dim.x
125                     if dim.y < pmx.y:
126                         pmx.y = dim.y
127                     if dim.z < pmx.z:
128                         pmx.z = dim.z
129                 if 0.0 > pmn.x:  # Keeping things from going under zero
130                     pmn.x = 0.0
131                 if 0.0 > pmn.y:
132                     pmn.y = 0.0
133                 if 0.0 > pmn.z:
134                     pmn.z = 0.0
135                 sx = (random.random() * (pmx.x - pmn.x)) + pmn.x  # Just changed self.mx and .mn to pmx.
136                 sy = (random.random() * (pmx.y - pmn.y)) + pmn.y
137                 sz = (random.random() * (pmx.z - pmn.z)) + pmn.z
138                 if self.wh is True:  # This keeps the cubes within the base mesh
139                     ex = (random.random() * (dim.x - sx)) - ((dim.x - sx) / 2) + obj.location.x
140                     wy = (random.random() * (dim.y - sy)) - ((dim.y - sy) / 2) + obj.location.y
141                     ze = (random.random() * (dim.z - sz)) - ((dim.z - sz) / 2) + obj.location.z
142                 elif self.wh is False:
143                     ex = (random.random() * dim.x) - (dim.x / 2) + obj.location.x
144                     wy = (random.random() * dim.y) - (dim.y / 2) + obj.location.y
145                     ze = (random.random() * dim.z) - (dim.z / 2) + obj.location.z
146                 bpy.ops.mesh.primitive_cube_add(
147                             radius=0.5, location=(ex + self.lo.x, wy + self.lo.y, ze + self.lo.z)
148                             )
149                 bpy.ops.object.mode_set(mode='EDIT')
150                 bpy.ops.mesh.select_all(action='SELECT')
151                 bpy.ops.transform.resize(
152                     value=(sx, sy, sz), constraint_axis=(True, True, True),
153                     orient_type='GLOBAL', mirror=False, proportional='DISABLED',
154                     proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
155                     )
156                 bpy.ops.object.mode_set(mode='OBJECT')
157                 select = bpy.context.object  # This is used to keep something selected for poll()
158                 bpy.ops.object.collection_link(group='Cubagrouper')
159                 rsdchange += 3
160             bpy.ops.object.select_grouped(type='GROUP')
161             bpy.ops.transform.rotate(
162                     value=rot[0], axis=(1, 0, 0), constraint_axis=(False, False, False),
163                     orient_type='GLOBAL', mirror=False, proportional='DISABLED',
164                     proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
165                     )
166             bpy.ops.transform.rotate(
167                     value=rot[1], axis=(0, 1, 0), constraint_axis=(False, False, False),
168                     orient_type='GLOBAL', mirror=False, proportional='DISABLED',
169                     proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
170                     )
171             bpy.ops.transform.rotate(
172                     value=rot[2], axis=(0, 0, 1), constraint_axis=(False, False, False),
173                     orient_type='GLOBAL', mirror=False, proportional='DISABLED',
174                     proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
175                     )
176             bpy.context.view_layer.objects.active = obj  # Again needed to avoid poll() taking me down
177             bpy.ops.object.make_links_data(type='MODIFIERS')
178             bpy.ops.object.make_links_data(type='MATERIAL')
179
180             if uvyes == 1:
181                 bpy.ops.object.join_uvs()
182
183             bpy.ops.collection.objects_remove()
184             bpy.context.view_layer.objects.active = select
185
186             if self.dc is True:
187                 bpy.context.collection.objects.unlink(obj)
188
189         return {'FINISHED'}
190
191
192 def register():
193     bpy.utils.register_class(makestructure)
194
195
196 def unregister():
197     bpy.utils.unregister_class(makestructure)
198
199
200 if __name__ == "__main__":
201     register()