0ab252cc1b96afe1f51be237caf26a347278a92f
[blender-addons-contrib.git] / add_mesh_stairs / stairbuilder.py
1 # Stairs and railing creator script for blender 2.49
2 # Author: Nick van Adium
3 # Date: 2010 08 09
4
5 # Creates a straight-run staircase with railings and stringer
6 # All components are optional and can be turned on and off by setting e.g. makeTreads=True or makeTreads=False
7 # No GUI for the script, all parameters must be defined below
8 # Current values assume 1 blender unit = 1 metre
9
10 # Stringer will rest on lower landing and hang from upper landing
11 # Railings start on the lowest step and end on the upper landing
12
13 # NOTE: You must have numpy installed for this script to work!
14 #       numpy is used to easily perform the necessary algebra
15 #       numpy can be found at http://www.scipy.org/Download
16
17 # Note: I'm not sure how to use recalcNormals so not all normals points ouwards.
18 #       Perhaps someone else can contribute this.
19 #
20 #-----------------------------------------------------------
21 #
22 # Converted to Blender 2.5:
23 #   - Still uses NumPy.
24 #   - Classes are basically copy-paste from the original code
25 #   - New Make_mesh copied from BrikBot's rock generator
26 #   - Implemented standard add mesh GUI.
27 #   @todo:
28 #   - global vs. local needs cleaned up.
29 #   - Join separate stringer objects and then clean up the mesh.
30 #   - Put all objects into a group.
31 #   - Generate left/right posts/railings/retainers separatly with
32 #       option to disable just the left/right.
33 #   - Add wall railing type as an option for left/right
34 #   - Add different rail styles (profiles).  Select with enum.
35 #   - Should have a non-NumPy code path for cross-compatability.
36 #       - Could be another file with equivalent classes/functions?
37 #           Then we would just import from there instead of from
38 #           NumPy without having to change the actual code.  It
39 #           would instead be a "try-except" block that trys to use
40 #           NumPy.
41 #   - Would like to add additional staircase types.
42 #       - Spiral staircase
43 #       - "L" staircase
44 #       - "T" staircase
45 #
46 # Last Modified By: Paul "brikbot" Marshall
47 # Last Modification: January 29, 2011
48 #
49 # ##### BEGIN GPL LICENSE BLOCK #####
50 #
51 #  Stairbuilder is for quick stair generation.
52 #  Copyright (C) 2011  Paul Marshall
53 #
54 #  This program is free software: you can redistribute it and/or modify
55 #  it under the terms of the GNU General Public License as published by
56 #  the Free Software Foundation, either version 3 of the License, or
57 #  (at your option) any later version.
58 #
59 #  This program is distributed in the hope that it will be useful,
60 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
61 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
62 #  GNU General Public License for more details.
63 #
64 #  You should have received a copy of the GNU General Public License
65 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
66 #
67 # ##### END GPL LICENSE BLOCK #####
68
69 #-----------------------------------------------------------
70 # BEGIN NEW B2.5/Py3.2 CODE
71 import bpy
72 from add_mesh_stairs.general import General
73 from add_mesh_stairs.post import Posts
74 from add_mesh_stairs.rail import Rails
75 from add_mesh_stairs.retainer import Retainers
76 from add_mesh_stairs.stringer import Stringer
77 from add_mesh_stairs.tread import Treads
78 from bpy.props import (BoolProperty,
79                        EnumProperty,
80                        IntProperty,
81                        FloatProperty)
82 from mathutils import Vector
83
84 global G
85 global typ
86 global typ_s
87 global typ_t
88 global rise
89 global run               
90             
91 class stairs(bpy.types.Operator):
92     '''Add stair objects'''
93     bl_idname = "mesh.stairs"
94     bl_label = "Add Stairs"
95     bl_options = {'REGISTER', 'UNDO'}
96     bl_description = "Add stairs"
97
98     # Stair types for enum:
99     id1 = ("id1", "Freestanding", "Generate a freestanding staircase.")
100     id2 = ("id2", "Housed-Open", "Generate a housed-open staircase.")
101     id3 = ("id3", "Box", "Generate a box staircase.")
102     id4 = ("id4", "Circular", "Generate a circular or spiral staircase.")
103
104     # Tread types for enum:
105     tId1 = ("tId1", "Classic", "Generate wooden style treads")
106     tId2 = ("tId2", "Basic Steel", "Generate common steel style treads")
107     tId3 = ("tId3", "Bar 1", "Generate bar/slat steel treads")
108     tId4 = ("tId4", "Bar 2", "Generate bar-grating steel treads")
109     tId5 = ("tId5", "Bar 3", "Generate bar-support steel treads")
110
111     # Stringer types for enum:
112     sId1 = ("sId1", "Classic", "Generate a classic style stringer")
113     sId2 = ("sId2", "I-Beam", "Generate a steel I-beam stringer")
114     sId3 = ("sId3", "C-Beam", "Generate a C-channel style stringer")
115     
116     typ = EnumProperty(name = "Type",
117                        description = "Type of staircase to generate",
118                        items = [id1, id2, id3, id4])
119
120     rise = FloatProperty(name = "Rise",
121                          description = "Single tread rise",
122                          min = 0.0, max = 1024.0,
123                          default = 0.20)
124     run = FloatProperty(name = "Run",
125                         description = "Single tread run",
126                         min = 0.0, max = 1024.0,
127                         default = 0.30)
128
129     #for circular
130     rad1 = FloatProperty(name = "Inner Radius",
131                          description = "Inner radius for circular staircase",
132                          min = 0.0, max = 1024.0,
133                          soft_max = 10.0,
134                          default = 0.25)
135     rad2 = FloatProperty(name = "Outer Radius",
136                          description = "Outer radius for circular staircase",
137                          min = 0.0, max = 1024.0,
138                          soft_min = 0.015625, soft_max = 32.0,
139                          default = 1.0)
140     deg = FloatProperty(name = "Degrees",
141                         description = "Number of degrees the stairway rotates",
142                         min = 0.0, max = 92160.0, step = 5.0,
143                         default = 450.0)
144     center = BoolProperty(name = "Center Pillar",
145                           description = "Generate a central pillar",
146                           default = False)
147
148     #for treads
149     make_treads = BoolProperty(name = "Make Treads",
150                               description = "Enable tread generation",
151                               default = True)
152     tread_w = FloatProperty(name = "Tread Width",
153                             description = "Width of each generated tread",
154                             min = 0.0001, max = 1024.0,
155                             default = 1.2)
156     tread_h = FloatProperty(name = "Tread Height",
157                             description = "Height of each generated tread",
158                             min = 0.0001, max = 1024.0,
159                             default = 0.04)
160     tread_t = FloatProperty(name = "Tread Toe",
161                             description = "Toe (aka \"nosing\") of each generated tread",
162                             min = 0.0, max = 10.0,
163                             default = 0.03)
164     tread_o = FloatProperty(name = "Tread Overhang",
165                             description = "How much tread \"overhangs\" the sides",
166                             min = 0.0, max = 1024.0,
167                             default = 0.025)
168     tread_n = IntProperty(name = "Number of Treads",
169                           description = "How many treads to generate",
170                           min = 1, max = 1024,
171                           default = 10)
172     typ_t = EnumProperty(name = "Tread Type",
173                          description = "Type/style of treads to generate",
174                          items = [tId1, tId2, tId3, tId4, tId5])
175     tread_tk = FloatProperty(name = "Thickness",
176                              description = "Thickness of the treads",
177                              min = 0.0001, max = 10.0,
178                              default = 0.02)
179     tread_sec = IntProperty(name = "Sections",
180                             description = "Number of sections to use for tread",
181                             min = 1, max = 1024,
182                             default = 5)
183     tread_sp = IntProperty(name = "Spacing",
184                            description = "Total spacing between tread sections as a percentage of total tread width",
185                            min = 0, max = 80,
186                            default = 5)
187     tread_sn = IntProperty(name = "Crosses",
188                            description = "Number of cross section supports",
189                            min = 2, max = 1024,
190                            default = 4)
191     #special circular tread properties:
192     tread_slc = IntProperty(name = "Slices",
193                             description = "Number of slices each tread is composed of",
194                             min = 1, max = 1024,
195                             soft_max = 16,
196                             default = 4)
197
198     #for posts
199     make_posts = BoolProperty(name = "Make Posts",
200                               description = "Enable post generation",
201                               default = True)
202     post_d = FloatProperty(name = "Post Depth",
203                            description = "Depth of generated posts",
204                            min = 0.0001, max = 10.0,
205                            default = 0.04)
206     post_w = FloatProperty(name = "Post Width",
207                            description = "Width of generated posts",
208                            min = 0.0001, max = 10.0,
209                            default = 0.04)
210     post_n = IntProperty(name = "Number of Posts",
211                          description = "Number of posts to generated",
212                          min = 1, max = 1024,
213                          default = 5)
214
215     #for railings
216     make_railings = BoolProperty(name = "Make Railings",
217                                  description = "Generate railings",
218                                  default = True)
219     rail_w = FloatProperty(name = "Railings Width",
220                            description = "Width of railings to generate",
221                            min = 0.0001, max = 10.0,
222                            default = 0.12)
223     rail_t = FloatProperty(name = "Railings Thickness",
224                            description = "Thickness of railings to generate",
225                            min = 0.0001, max = 10.0,
226                            default = 0.03)
227     rail_h = FloatProperty(name = "Railings Height",
228                            description = "Height of railings to generate",
229                            min = 0.0001, max = 10.0,
230                            default = 0.90)
231
232     #for retainers
233     make_retainers = BoolProperty(name = "Make Retainers",
234                                   description = "Generate retainers",
235                                   default = True)
236     ret_w = FloatProperty(name = "Retainer Width",
237                           description = "Width of generated retainers",
238                           min = 0.0001, max = 10.0,
239                           default = 0.01)
240     ret_h = FloatProperty(name = "Retainer Height",
241                           description = "Height of generated retainers",
242                           min = 0.0001, max = 10.0,
243                           default = 0.01)
244     ret_n = IntProperty(name = "Number of Retainers",
245                         description = "Number of retainers to generated",
246                         min = 1, max = 1024,
247                         default = 3)
248
249     #for stringer
250     make_stringer = BoolProperty(name = "Make Stringer",
251                                  description = "Generate stair stringer",
252                                  default = True)
253     typ_s = EnumProperty(name = "Stringer Type",
254                          description = "Type/style of stringer to generate",
255                          items = [sId1, sId2, sId3])
256     string_n = IntProperty(name = "Number of Stringers",
257                            description = "Number of stringers to generate",
258                            min = 1, max = 10,
259                            default = 1)
260     string_dis = BoolProperty(name = "Distributed",
261                               description = "Use distributed stringers",
262                               default = False)
263     string_w = FloatProperty(name = "Stringer width",
264                              description = "Width of stringer as a percentage of tread width",
265                              min = 0.0001, max = 100.0,
266                              default = 15.0)
267     string_h = FloatProperty(name = "Stringer Height",
268                              description = "Height of the stringer",
269                              min = 0.0001, max = 100.0,
270                              default = 0.3)
271     string_tw = FloatProperty(name = "Web Thickness",
272                               description = "Thickness of the beam's web as a percentage of width",
273                               min = 0.0001, max = 100.0,
274                               default = 25.0)
275     string_tf = FloatProperty(name = "Flange Thickness",
276                               description = "Thickness of the flange",
277                               min = 0.0001, max = 100.0,
278                               default = 0.05)
279     string_tp = FloatProperty(name = "Flange Taper",
280                               description = "Flange thickness taper as a percentage",
281                               min = 0.0, max = 100.0,
282                               default = 0.0)
283     string_g = BoolProperty(name = "Floating",
284                             description = "Cut bottom of strigner to be a \"floating\" section",
285                             default = False)
286
287     use_original = BoolProperty(name = "Use legacy method",
288                                 description = "Use the Blender 2.49 legacy method for stair generation",
289                                 default = True)
290     rEnable = BoolProperty(name = "Right Details",
291                            description = "Generate right side details (posts/rails/retainers)",
292                            default = True)
293     lEnable = BoolProperty(name = "Left Details",
294                            description = "Generate left side details (posts/rails/retainers)",
295                            default = True)
296
297     # Draw the GUI:
298     def draw(self, context):
299         layout = self.layout
300         box = layout.box()
301         box.prop(self, 'typ')
302         box = layout.box()
303         box.prop(self, 'rise')
304         if self.typ != "id4":
305             box.prop(self, 'run')
306         else:
307             box.prop(self, 'deg')
308             box.prop(self, 'rad1')
309             box.prop(self, 'rad2')
310             box.prop(self, 'center')
311         if self.typ == "id1":
312             box.prop(self, 'use_original')
313             if not self.use_original:
314                 box.prop(self, 'rEnable')
315                 box.prop(self, 'lEnable')
316         else:
317             self.use_original = False
318             box.prop(self, 'rEnable')
319             box.prop(self, 'lEnable')
320             
321         # Treads
322         box = layout.box()
323         box.prop(self, 'make_treads')
324         if self.make_treads:
325             if not self.use_original and self.typ != "id4":
326                 box.prop(self, 'typ_t')
327             else:
328                 self.typ_t = "tId1"
329             if self.typ != "id4":
330                 box.prop(self, 'tread_w')
331             box.prop(self, 'tread_h')
332             box.prop(self, 'tread_t')
333             if self.typ not in ["id2", "id4"]:
334                 box.prop(self, 'tread_o')
335             else:
336                 self.tread_o = 0.0
337             box.prop(self, 'tread_n')
338             if self.typ_t != "tId1":
339                 box.prop(self, 'tread_tk')
340                 box.prop(self, 'tread_sec')
341                 if self.tread_sec > 1 and self.typ_t not in ["tId3", "tId4"]:
342                     box.prop(self, 'tread_sp')
343                 if self.typ_t in ["tId3", "tId4", "tId5"]:
344                     box.prop(self, 'tread_sn')
345             elif self.typ == "id4":
346                 box.prop(self, "tread_slc")
347                     
348         # Posts
349         box = layout.box()
350         box.prop(self, 'make_posts')
351         if self.make_posts:
352             box.prop(self, 'post_d')
353             box.prop(self, 'post_w')
354             box.prop(self, 'post_n')
355             
356         # Railings
357         box = layout.box()
358         box.prop(self, 'make_railings')
359         if self.make_railings:
360             box.prop(self, 'rail_w')
361             box.prop(self, 'rail_t')
362             box.prop(self, 'rail_h')
363             
364         # Retainers
365         box = layout.box()
366         box.prop(self, 'make_retainers')
367         if self.make_retainers:
368             box.prop(self, 'ret_w')
369             box.prop(self, 'ret_h')
370             box.prop(self, 'ret_n')
371             
372         # Stringers
373         box = layout.box()
374         if self.typ != "id2":
375             box.prop(self, 'make_stringer')
376         else:
377             self.make_stringer = True
378         if self.make_stringer:
379             if not self.use_original:
380                 box.prop(self, 'typ_s')
381             else:
382                 self.typ_s = "sId1"
383             box.prop(self, 'string_w')
384             if self.typ == "id1":
385                 if self.typ_s == "sId1" and not self.use_original:
386                     box.prop(self, 'string_n')
387                     box.prop(self, 'string_dis')
388                 elif self.typ_s in ["sId2", "sId3"]:
389                     box.prop(self, 'string_n')
390                     box.prop(self, 'string_dis')
391                     box.prop(self, 'string_h')
392                     box.prop(self, 'string_tw')
393                     box.prop(self, 'string_tf')
394                     box.prop(self, 'string_tp')
395                     box.prop(self, 'string_g')
396             elif self.typ == "id2":
397                 if self.typ_s in ["sId2", "sId3"]:
398                     box.prop(self, 'string_tw')
399                     box.prop(self, 'string_tf')
400
401         # Tread support:
402 ##        if self.make_stringer and typ_s in ["sId2", "sId3"]:
403
404     def execute(self, context):
405         global G
406         global typ
407         global typ_s
408         global typ_t
409         global rise
410         global run
411         typ = self.typ
412         typ_s = self.typ_s
413         typ_t = self.typ_t
414         rise = self.rise
415         run = self.run
416         G=General(rise,run,self.tread_n)
417         if self.make_treads:
418             if typ != "id4":
419                 Treads(G,
420                        typ,
421                        typ_t,
422                        run,
423                        self.tread_w,
424                        self.tread_h,
425                        self.run,
426                        self.rise,
427                        self.tread_t,
428                        self.tread_o,
429                        self.tread_n,
430                        self.tread_tk,
431                        self.tread_sec,
432                        self.tread_sp,
433                        self.tread_sn)
434             else:
435                 Treads(G,
436                        typ,
437                        typ_t,
438                        self.deg,
439                        self.rad2,
440                        self.tread_h,
441                        self.run,
442                        self.rise,
443                        self.tread_t,
444                        self.rad1,
445                        self.tread_n,
446                        self.tread_tk,
447                        self.tread_sec,
448                        self.tread_sp,
449                        self.tread_sn,
450                        self.tread_slc)
451         if self.make_posts and (self.rEnable or self.lEnable):
452             Posts(G,
453                   rise,
454                   run,
455                   self.post_d,
456                   self.post_w,
457                   self.tread_w,
458                   self.post_n,
459                   self.rail_h,
460                   self.rail_t,
461                   self.rEnable,
462                   self.lEnable)
463         if self.make_railings and (self.rEnable or self.lEnable):
464             Rails(G,
465                   self.rail_w,
466                   self.rail_t,
467                   self.rail_h,
468                   self.tread_t,
469                   self.post_w,
470                   self.post_d,
471                   self.tread_w,
472                   self.rEnable,
473                   self.lEnable)
474         if self.make_retainers and (self.rEnable or self.lEnable):
475             Retainers(G,
476                       self.ret_w,
477                       self.ret_h,
478                       self.post_w,
479                       self.tread_w,
480                       self.rail_h,
481                       self.ret_n,
482                       self.rEnable,
483                       self.lEnable)
484         if self.make_stringer:
485             if typ == "id1" and self.use_original:
486                 Stringer(G,
487                          typ,
488                          typ_s,
489                          rise,
490                          run,
491                          self.string_w,
492                          self.string_h,
493                          self.tread_n,
494                          self.tread_h,
495                          self.tread_w,
496                          self.tread_t,
497                          self.tread_o,
498                          self.string_tw,
499                          self.string_tf,
500                          self.string_tp,
501                          not self.string_g)
502             elif typ == "id3":
503                 Stringer(G,
504                          typ,
505                          typ_s,
506                          rise,
507                          run,
508                          100,
509                          self.string_h,
510                          self.tread_n,
511                          self.tread_h,
512                          self.tread_w,
513                          self.tread_t,
514                          self.tread_o,
515                          self.string_tw,
516                          self.string_tf,
517                          self.string_tp,
518                          not self.string_g,
519                          1, False, False)
520             elif typ == "id4":
521                 Stringer(G,
522                          typ,
523                          typ_s,
524                          rise,
525                          self.deg,
526                          self.string_w,
527                          self.string_h,
528                          self.tread_n,
529                          self.tread_h,
530                          self.rad2 - self.rad1,
531                          self.tread_t,
532                          self.rad1,
533                          self.string_tw,
534                          self.string_tf,
535                          self.string_tp,
536                          not self.string_g,
537                          self.string_n,
538                          self.string_dis,
539                          self.use_original,
540                          self.tread_slc)
541             else:
542                 Stringer(G,
543                          typ,
544                          typ_s,
545                          rise,
546                          run,
547                          self.string_w,
548                          self.string_h,
549                          self.tread_n,
550                          self.tread_h,
551                          self.tread_w,
552                          self.tread_t,
553                          self.tread_o,
554                          self.string_tw,
555                          self.string_tf,
556                          self.string_tp,
557                          not self.string_g,
558                          self.string_n,
559                          self.string_dis,
560                          self.use_original)
561         return {'FINISHED'}