GPencil Tools: Canvas rotate improvement
[blender-addons.git] / archimesh / achm_shelves_maker.py
blob59a2bfc78aa4f6e0fa0dad50fa0a45b77dde550a
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 # ----------------------------------------------------------
22 # Automatic generation of shelves
23 # Author: Antonio Vazquez (antonioya)
25 # ----------------------------------------------------------
26 import bpy
27 from copy import copy
28 from bpy.types import Operator, PropertyGroup
29 from bpy.props import FloatProperty, BoolProperty, IntProperty, CollectionProperty, EnumProperty
30 from .achm_tools import *
33 # ------------------------------------------------------------------
34 # Define property group class for shelves properties
35 # ------------------------------------------------------------------
36 class ShelvesProperties(PropertyGroup):
37 sX: FloatProperty(name='width', min=0.001, max=10, default=1,
38 precision=3, description='Furniture width')
39 wY: FloatProperty(name='', min=-10, max=10, default=0, precision=3, description='Modify y size')
40 wZ: FloatProperty(name='', min=-10, max=10, default=0, precision=3, description='Modify z size')
41 # Cabinet position shift
42 pX: FloatProperty(name='', min=0, max=10, default=0, precision=3, description='Position x shift')
43 pY: FloatProperty(name='', min=-10, max=10, default=0, precision=3, description='Position y shift')
44 pZ: FloatProperty(name='', min=-10, max=10, default=0, precision=3, description='Position z shift')
46 # Shelves
47 sNum: IntProperty(name='Shelves', min=0, max=12, default=6, description='Number total of shelves')
49 # 12 shelves (shelf)
50 Z01: FloatProperty(name='zS1', min=-10, max=10, default=0, precision=3, description='Position z shift')
51 Z02: FloatProperty(name='zS2', min=-10, max=10, default=0, precision=3, description='Position z shift')
52 Z03: FloatProperty(name='zS3', min=-10, max=10, default=0, precision=3, description='Position z shift')
53 Z04: FloatProperty(name='zS4', min=-10, max=10, default=0, precision=3, description='Position z shift')
54 Z05: FloatProperty(name='zS5', min=-10, max=10, default=0, precision=3, description='Position z shift')
55 Z06: FloatProperty(name='zS6', min=-10, max=10, default=0, precision=3, description='Position z shift')
56 Z07: FloatProperty(name='zS7', min=-10, max=10, default=0, precision=3, description='Position z shift')
57 Z08: FloatProperty(name='zS8', min=-10, max=10, default=0, precision=3, description='Position z shift')
58 Z09: FloatProperty(name='zS9', min=-10, max=10, default=0, precision=3, description='Position z shift')
59 Z10: FloatProperty(name='zS10', min=-10, max=10, default=0, precision=3, description='Position z shift')
60 Z11: FloatProperty(name='zS11', min=-10, max=10, default=0, precision=3, description='Position z shift')
61 Z12: FloatProperty(name='zS12', min=-10, max=10, default=0, precision=3, description='Position z shift')
63 right: BoolProperty(name="Right", description="Create right side", default=True)
64 left: BoolProperty(name="Left", description="Create left side", default=True)
66 bpy.utils.register_class(ShelvesProperties)
69 # ------------------------------------------------------------------
70 # Define UI class
71 # Shelves
72 # ------------------------------------------------------------------
73 class ARCHIMESH_OT_Shelves(Operator):
74 bl_idname = "mesh.archimesh_shelves"
75 bl_label = "Shelves"
76 bl_description = "Shelves Generator"
77 bl_category = 'View'
78 bl_options = {'REGISTER', 'UNDO'}
80 thickness: FloatProperty(
81 name='Side Thickness', min=0.001, max=5,
82 default=0.03, precision=3,
83 description='Board thickness',
85 sthickness: FloatProperty(
86 name='Shelves Thickness', min=0.001, max=5,
87 default=0.03, precision=3,
88 description='Board thickness',
90 depth: FloatProperty(
91 name='Depth', min=0.001, max=50,
92 default=0.28, precision=3,
93 description='Default unit depth',
95 height: FloatProperty(
96 name='Height', min=0.001, max=50,
97 default=2, precision=3,
98 description='Default unit height',
100 top: FloatProperty(
101 name='Top', min=0, max=50,
102 default=0.03, precision=3,
103 description='Default top shelf position',
105 bottom: FloatProperty(
106 name='Bottom', min=0, max=50,
107 default=0.07, precision=3,
108 description='Default bottom self position',
110 stype: EnumProperty(
111 items=(
112 ('1', "Full side", ""),
113 ('4', "4 Legs", ""),
114 ('99', "None", "")),
115 name="Sides",
116 description="Type of side construction",
119 fitZ: BoolProperty(
120 name="Floor origin in Z=0",
121 description="Use Z=0 axis as vertical origin floor position",
122 default=True,
125 shelves_num: IntProperty(
126 name='Number of Units',
127 min=1, max=10,
128 default=1,
129 description='Number total of shelves units',
131 shelves: CollectionProperty(type=ShelvesProperties)
133 # Materials
134 crt_mat: BoolProperty(
135 name="Create default Cycles materials",
136 description="Create default materials for Cycles render",
137 default=True,
140 # -----------------------------------------------------
141 # Draw (create UI interface)
142 # -----------------------------------------------------
143 # noinspection PyUnusedLocal
144 def draw(self, context):
145 layout = self.layout
146 space = bpy.context.space_data
147 if not space.local_view:
148 # Imperial units warning
149 if bpy.context.scene.unit_settings.system == "IMPERIAL":
150 row = layout.row()
151 row.label(text="Warning: Imperial units not supported", icon='COLOR_RED')
153 box = layout.box()
154 row = box.row()
155 row.prop(self, 'thickness')
156 row.prop(self, 'sthickness')
157 row = box.row()
158 row.prop(self, 'depth')
159 row.prop(self, 'height')
160 row = box.row()
161 row.prop(self, 'top')
162 row.prop(self, 'bottom')
163 row = box.row()
164 row.prop(self, 'stype')
165 row.prop(self, 'fitZ')
167 # Furniture number
168 row = layout.row()
169 row.prop(self, 'shelves_num')
170 # Add menu for shelves
171 if self.shelves_num > 0:
172 for idx in range(0, self.shelves_num):
173 box = layout.box()
174 add_shelves(self, box, idx + 1, self.shelves[idx])
176 box = layout.box()
177 if not context.scene.render.engine in {'CYCLES', 'BLENDER_EEVEE'}:
178 box.enabled = False
179 box.prop(self, 'crt_mat')
180 else:
181 row = layout.row()
182 row.label(text="Warning: Operator does not work in local view mode", icon='ERROR')
184 # -----------------------------------------------------
185 # Execute
186 # -----------------------------------------------------
187 # noinspection PyUnusedLocal
188 def execute(self, context):
189 if bpy.context.mode == "OBJECT":
190 # Create all elements
191 for i in range(len(self.shelves) - 1, self.shelves_num):
192 self.shelves.add()
194 # Create shelves
195 create_shelves_mesh(self)
196 return {'FINISHED'}
197 else:
198 self.report({'WARNING'}, "Archimesh: Option only valid in Object mode")
199 return {'CANCELLED'}
202 # -----------------------------------------------------
203 # Add shelves parameters
204 # -----------------------------------------------------
205 def add_shelves(self, box, num, sh):
206 row = box.row()
207 row.label(text="Unit " + str(num))
208 row.prop(sh, 'sX')
210 row = box.row()
211 row.prop(sh, 'wY')
212 row.prop(sh, 'wZ')
213 if self.stype != "99":
214 row.prop(sh, 'left')
215 row.prop(sh, 'right')
217 row = box.row()
218 row.prop(sh, 'pX')
219 row.prop(sh, 'pY')
220 row.prop(sh, 'pZ')
222 row = box.row()
223 row.prop(sh, 'sNum', slider=True)
225 if sh.sNum >= 1:
226 row = box.row()
227 row.prop(sh, 'Z01')
228 if sh.sNum >= 2:
229 row.prop(sh, 'Z02')
230 if sh.sNum >= 3:
231 row.prop(sh, 'Z03')
233 if sh.sNum >= 4:
234 row = box.row()
235 row.prop(sh, 'Z04')
236 if sh.sNum >= 5:
237 row.prop(sh, 'Z05')
238 if sh.sNum >= 6:
239 row.prop(sh, 'Z06')
241 if sh.sNum >= 7:
242 row = box.row()
243 row.prop(sh, 'Z07')
244 if sh.sNum >= 8:
245 row.prop(sh, 'Z08')
246 if sh.sNum >= 9:
247 row.prop(sh, 'Z09')
249 if sh.sNum >= 10:
250 row = box.row()
251 row.prop(sh, 'Z10')
252 if sh.sNum >= 11:
253 row.prop(sh, 'Z11')
254 if sh.sNum >= 12:
255 row.prop(sh, 'Z12')
258 # ------------------------------------------------------------------------------
259 # Generate mesh data
260 # All custom values are passed using self container (self.myvariable)
261 # ------------------------------------------------------------------------------
262 def create_shelves_mesh(self):
263 # deactivate others
264 for o in bpy.data.objects:
265 if o.select_get() is True:
266 o.select_set(False)
267 bpy.ops.object.select_all(action='DESELECT')
268 # Create units
269 generate_shelves(self)
271 return
274 # ------------------------------------------------------------------------------
275 # Generate Units
276 # All custom values are passed using self container (self.myvariable)
277 # ------------------------------------------------------------------------------
278 def generate_shelves(self):
280 boxes = []
281 location = bpy.context.scene.cursor.location
282 myloc = copy(location) # copy location to keep 3D cursor position
283 # Fit to floor
284 if self.fitZ:
285 myloc[2] = 0
287 # Create units
288 lastx = myloc[0]
289 # ------------------------------------------------------------------------------
290 # Shelves
291 # ------------------------------------------------------------------------------
292 for i in range(0, self.shelves_num):
293 mydata = create_unit(self.stype, "Shelves" + str(i + 1),
294 self.thickness, self.sthickness,
295 self.shelves[i].sX, self.depth + self.shelves[i].wY, self.height + self.shelves[i].wZ,
296 self.shelves[i].pX + lastx, myloc[1] + self.shelves[i].pY, myloc[2] + self.shelves[i].pZ,
297 self.shelves[i].left, self.shelves[i].right,
298 self.shelves[i].sNum,
299 (self.shelves[i].Z01, self.shelves[i].Z02, self.shelves[i].Z03,
300 self.shelves[i].Z04, self.shelves[i].Z05, self.shelves[i].Z06,
301 self.shelves[i].Z07, self.shelves[i].Z08, self.shelves[i].Z09,
302 self.shelves[i].Z10, self.shelves[i].Z11, self.shelves[i].Z12),
303 self.top, self.bottom)
304 boxes.extend([mydata[0]])
305 lastx = mydata[1]
307 # refine units
308 for box in boxes:
309 remove_doubles(box)
310 set_normals(box)
312 # deactivate others
313 for o in bpy.data.objects:
314 if o.select_get() is True:
315 o.select_set(False)
317 boxes[0].select_set(True)
318 bpy.context.view_layer.objects.active = boxes[0]
320 # Create materials
321 if self.crt_mat and bpy.context.scene.render.engine in {'CYCLES', 'BLENDER_EEVEE'}:
322 mat = create_diffuse_material("Shelves_material", False, 0.8, 0.8, 0.8)
323 for box in boxes:
324 set_material(box, mat)
326 return
329 # ------------------------------------------------------------------------------
330 # Create shelves unit
332 # stype: type of sides
333 # objName: Name for the new object
334 # thickness: wood thickness (sides)
335 # sthickness: wood thickness (shelves)
336 # sX: Size in X axis
337 # sY: Size in Y axis
338 # sZ: Size in Z axis
339 # pX: position X axis
340 # pY: position Y axis
341 # pZ: position Z axis
342 # right: True-> create right side
343 # left: True-> create left side
344 # shelves: Number of shelves
345 # zPos: List with z shift for each self
346 # top: position of top shelf
347 # bottom: position of bottom shelf
348 # ------------------------------------------------------------------------------
349 def create_unit(stype, objname, thickness, sthickness, sx, sy, sz, px, py, pz, left, right, shelves, zpos,
350 top, bottom):
352 myvertex = []
353 myfaces = []
354 v = 0
356 # no Sides, then no thickness
357 if stype == "99":
358 thickness = 0
360 # ------------------------------
361 # Left side
362 # ------------------------------
363 if left and stype != "99":
364 # Full side
365 if stype == "1":
366 myvertex.extend([(0, 0, 0), (0, -sy, 0), (0, -sy, sz), (0, 0, sz),
367 (thickness, 0, 0), (thickness, -sy, 0), (thickness, -sy, sz), (thickness, 0, sz)])
368 myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
369 (v, v + 1, v + 5, v + 4),
370 (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
371 v += 8
372 # Four legs
373 if stype == "4":
374 # back
375 myvertex.extend([(0, 0, 0), (0, -thickness, 0), (0, -thickness, sz), (0, 0, sz),
376 (thickness, 0, 0), (thickness, -thickness, 0), (thickness, -thickness, sz),
377 (thickness, 0, sz)])
378 myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
379 (v, v + 1, v + 5, v + 4),
380 (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
381 v += 8
382 # Front
383 myvertex.extend([(0, -sy + thickness, 0), (0, -sy, 0), (0, -sy, sz), (0, -sy + thickness, sz),
384 (thickness, -sy + thickness, 0), (thickness, -sy, 0), (thickness, -sy, sz),
385 (thickness, -sy + thickness, sz)])
386 myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
387 (v, v + 1, v + 5, v + 4),
388 (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
389 v += 8
391 # -----------------
392 # Right side
393 # -----------------
394 if right and stype != "99":
395 width = sx - thickness
396 # Full side
397 if stype == "1":
398 myvertex.extend([(width, 0, 0), (width, -sy, 0), (width, -sy, sz), (width, 0, sz),
399 (width + thickness, 0, 0), (width + thickness, -sy, 0), (width + thickness, -sy, sz),
400 (width + thickness, 0, sz)])
401 myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
402 (v, v + 1, v + 5, v + 4), (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
403 v += 8
404 # Four legs
405 if stype == "4":
406 # back
407 myvertex.extend([(width, 0, 0), (width, -thickness, 0), (width, -thickness, sz), (width, 0, sz),
408 (width + thickness, 0, 0), (width + thickness, -thickness, 0),
409 (width + thickness, -thickness, sz), (width + thickness, 0, sz)])
410 myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
411 (v, v + 1, v + 5, v + 4), (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
412 v += 8
413 # Front
414 myvertex.extend(
415 [(width, -sy + thickness, 0), (width, -sy, 0), (width, -sy, sz), (width, -sy + thickness, sz),
416 (width + thickness, -sy + thickness, 0), (width + thickness, -sy, 0), (width + thickness, -sy, sz),
417 (width + thickness, -sy + thickness, sz)])
418 myfaces.extend([(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3),
419 (v, v + 1, v + 5, v + 4), (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
420 v += 8
421 # -----------------
422 # shelves
423 # -----------------
424 posx = 0
425 # calculate width
426 width = sx - thickness
427 posx = posx + thickness
429 # calculate vertical spaces
430 dist = sz - top - bottom - sthickness
431 # if only top/bottom the space is not necessary
432 if shelves > 2:
433 space = dist / (shelves - 1)
434 else:
435 space = 0
437 posz1 = bottom
439 for x in range(shelves):
440 # bottom
441 if x == 0:
442 posz1 = bottom
443 # top
444 if x == shelves - 1:
445 posz1 = sz - top - sthickness
447 posz2 = posz1 - sthickness
448 myvertex.extend([(posx, 0, posz1 + zpos[x]), (posx, -sy, posz1 + zpos[x]),
449 (posx, -sy, posz2 + zpos[x]), (posx, 0, posz2 + zpos[x]),
450 (width, 0, posz1 + zpos[x]), (width, -sy, posz1 + zpos[x]),
451 (width, -sy, posz2 + zpos[x]), (width, 0, posz2 + zpos[x])])
453 myfaces.extend(
454 [(v, v + 1, v + 2, v + 3), (v + 4, v + 5, v + 6, v + 7), (v, v + 4, v + 7, v + 3), (v, v + 1, v + 5, v + 4),
455 (v + 3, v + 2, v + 6, v + 7), (v + 1, v + 2, v + 6, v + 5)])
456 v += 8
457 posz1 += space
459 mymesh = bpy.data.meshes.new(objname)
460 myobject = bpy.data.objects.new(objname, mymesh)
462 myobject.location[0] = px
463 myobject.location[1] = py
464 myobject.location[2] = pz
465 bpy.context.collection.objects.link(myobject)
467 mymesh.from_pydata(myvertex, [], myfaces)
468 mymesh.update(calc_edges=True)
470 return myobject, px + sx - thickness