Skip to content
Snippets Groups Projects
stairbuilder.py 22.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • # Stairs and railing creator script for blender 2.49
    # Author: Nick van Adium
    # Date: 2010 08 09
    # 
    # Creates a straight-run staircase with railings and stringer
    # All components are optional and can be turned on and off by setting e.g. makeTreads=True or makeTreads=False
    # No GUI for the script, all parameters must be defined below
    # Current values assume 1 blender unit = 1 metre
    # 
    # Stringer will rest on lower landing and hang from upper landing
    # Railings start on the lowest step and end on the upper landing
    # 
    # NOTE: You must have numpy installed for this script to work!
    #       numpy is used to easily perform the necessary algebra
    #       numpy can be found at http://www.scipy.org/Download
    # 
    # Note: I'm not sure how to use recalcNormals so not all normals points ouwards.
    #       Perhaps someone else can contribute this.
    #
    #-----------------------------------------------------------
    #
    # Converted to Blender 2.5:
    #   - Still uses NumPy.
    #   - Classes are basically copy-paste from the original code
    #   - New Make_mesh copied from BrikBot's rock generator
    #   - Implemented standard add mesh GUI.
    #   @todo:
    #   - global vs. local needs cleaned up.
    #   - Join separate stringer objects and then clean up the mesh.
    #   - Put all objects into a group.
    #   - Generate left/right posts/railings/retainers separatly with
    #       option to disable just the left/right.
    #   - Add wall railing type as an option for left/right
    #   - Add different rail styles (profiles).  Select with enum.
    #   - Should have a non-NumPy code path for cross-compatability.
    #       - Could be another file with equivalent classes/functions?
    #           Then we would just import from there instead of from
    #           NumPy without having to change the actual code.  It
    #           would instead be a "try-except" block that trys to use
    #           NumPy.
    #   - Would like to add additional staircase types.
    #       - Spiral staircase
    #       - "L" staircase
    #       - "T" staircase
    #
    # Last Modified By: Paul "brikbot" Marshall
    
    Paul Marshall's avatar
    Paul Marshall committed
    # Last Modification: January 29, 2011
    #
    # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  Stairbuilder is for quick stair generation.
    #  Copyright (C) 2011  Paul Marshall
    #
    #  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
    #  MERCHANTABILITY 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/>.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    
    #-----------------------------------------------------------
    # BEGIN NEW B2.5/Py3.2 CODE
    import bpy
    
    from add_mesh_building_objects.general import General
    from add_mesh_building_objects.post import Posts
    from add_mesh_building_objects.rail import Rails
    from add_mesh_building_objects.retainer import Retainers
    from add_mesh_building_objects.stringer import Stringer
    from add_mesh_building_objects.tread import Treads
    
    from bpy.props import (BoolProperty,
                           EnumProperty,
                           IntProperty,
                           FloatProperty)
    from mathutils import Vector
    
    global G
    global typ
    global typ_s
    global typ_t
    global rise
    global run               
                
    class stairs(bpy.types.Operator):
    
        bl_idname = "mesh.stairs"
        bl_label = "Add Stairs"
        bl_options = {'REGISTER', 'UNDO'}
        bl_description = "Add stairs"
    
        # Stair types for enum:
        id1 = ("id1", "Freestanding", "Generate a freestanding staircase.")
        id2 = ("id2", "Housed-Open", "Generate a housed-open staircase.")
        id3 = ("id3", "Box", "Generate a box staircase.")
        id4 = ("id4", "Circular", "Generate a circular or spiral staircase.")
    
        # Tread types for enum:
        tId1 = ("tId1", "Classic", "Generate wooden style treads")
        tId2 = ("tId2", "Basic Steel", "Generate common steel style treads")
        tId3 = ("tId3", "Bar 1", "Generate bar/slat steel treads")
        tId4 = ("tId4", "Bar 2", "Generate bar-grating steel treads")
        tId5 = ("tId5", "Bar 3", "Generate bar-support steel treads")
    
        # Stringer types for enum:
        sId1 = ("sId1", "Classic", "Generate a classic style stringer")
        sId2 = ("sId2", "I-Beam", "Generate a steel I-beam stringer")
        sId3 = ("sId3", "C-Beam", "Generate a C-channel style stringer")
        
        typ = EnumProperty(name = "Type",
                           description = "Type of staircase to generate",
                           items = [id1, id2, id3, id4])
    
        rise = FloatProperty(name = "Rise",
                             description = "Single tread rise",
                             min = 0.0, max = 1024.0,
                             default = 0.20)
        run = FloatProperty(name = "Run",
                            description = "Single tread run",
                            min = 0.0, max = 1024.0,
                            default = 0.30)
    
        #for circular
        rad1 = FloatProperty(name = "Inner Radius",
                             description = "Inner radius for circular staircase",
                             min = 0.0, max = 1024.0,
    
        rad2 = FloatProperty(name = "Outer Radius",
                             description = "Outer radius for circular staircase",
    
                             min = 0.0, max = 1024.0,
                             soft_min = 0.015625, soft_max = 32.0,
                             default = 1.0)
        deg = FloatProperty(name = "Degrees",
                            description = "Number of degrees the stairway rotates",
                            min = 0.0, max = 92160.0, step = 5.0,
                            default = 450.0)
    
        center = BoolProperty(name = "Center Pillar",
                              description = "Generate a central pillar",
                              default = False)
    
        #for treads
        make_treads = BoolProperty(name = "Make Treads",
                                  description = "Enable tread generation",
                                  default = True)
        tread_w = FloatProperty(name = "Tread Width",
                                description = "Width of each generated tread",
                                min = 0.0001, max = 1024.0,
                                default = 1.2)
        tread_h = FloatProperty(name = "Tread Height",
                                description = "Height of each generated tread",
                                min = 0.0001, max = 1024.0,
                                default = 0.04)
        tread_t = FloatProperty(name = "Tread Toe",
                                description = "Toe (aka \"nosing\") of each generated tread",
                                min = 0.0, max = 10.0,
                                default = 0.03)
        tread_o = FloatProperty(name = "Tread Overhang",
                                description = "How much tread \"overhangs\" the sides",
                                min = 0.0, max = 1024.0,
                                default = 0.025)
        tread_n = IntProperty(name = "Number of Treads",
                              description = "How many treads to generate",
                              min = 1, max = 1024,
                              default = 10)
        typ_t = EnumProperty(name = "Tread Type",
                             description = "Type/style of treads to generate",
                             items = [tId1, tId2, tId3, tId4, tId5])
        tread_tk = FloatProperty(name = "Thickness",
                                 description = "Thickness of the treads",
                                 min = 0.0001, max = 10.0,
                                 default = 0.02)
        tread_sec = IntProperty(name = "Sections",
                                description = "Number of sections to use for tread",
                                min = 1, max = 1024,
                                default = 5)
        tread_sp = IntProperty(name = "Spacing",
                               description = "Total spacing between tread sections as a percentage of total tread width",
                               min = 0, max = 80,
                               default = 5)
        tread_sn = IntProperty(name = "Crosses",
                               description = "Number of cross section supports",
                               min = 2, max = 1024,
                               default = 4)
    
        #special circular tread properties:
        tread_slc = IntProperty(name = "Slices",
                                description = "Number of slices each tread is composed of",
                                min = 1, max = 1024,
                                soft_max = 16,
                                default = 4)
    
    
        #for posts
        make_posts = BoolProperty(name = "Make Posts",
                                  description = "Enable post generation",
                                  default = True)
        post_d = FloatProperty(name = "Post Depth",
                               description = "Depth of generated posts",
                               min = 0.0001, max = 10.0,
                               default = 0.04)
        post_w = FloatProperty(name = "Post Width",
                               description = "Width of generated posts",
                               min = 0.0001, max = 10.0,
                               default = 0.04)
        post_n = IntProperty(name = "Number of Posts",
                             description = "Number of posts to generated",
                             min = 1, max = 1024,
                             default = 5)
    
        #for railings
        make_railings = BoolProperty(name = "Make Railings",
                                     description = "Generate railings",
                                     default = True)
        rail_w = FloatProperty(name = "Railings Width",
                               description = "Width of railings to generate",
                               min = 0.0001, max = 10.0,
                               default = 0.12)
        rail_t = FloatProperty(name = "Railings Thickness",
                               description = "Thickness of railings to generate",
                               min = 0.0001, max = 10.0,
                               default = 0.03)
        rail_h = FloatProperty(name = "Railings Height",
                               description = "Height of railings to generate",
                               min = 0.0001, max = 10.0,
                               default = 0.90)
    
        #for retainers
        make_retainers = BoolProperty(name = "Make Retainers",
                                      description = "Generate retainers",
                                      default = True)
        ret_w = FloatProperty(name = "Retainer Width",
                              description = "Width of generated retainers",
                              min = 0.0001, max = 10.0,
                              default = 0.01)
        ret_h = FloatProperty(name = "Retainer Height",
                              description = "Height of generated retainers",
                              min = 0.0001, max = 10.0,
                              default = 0.01)
        ret_n = IntProperty(name = "Number of Retainers",
                            description = "Number of retainers to generated",
                            min = 1, max = 1024,
                            default = 3)
    
        #for stringer
        make_stringer = BoolProperty(name = "Make Stringer",
                                     description = "Generate stair stringer",
                                     default = True)
        typ_s = EnumProperty(name = "Stringer Type",
                             description = "Type/style of stringer to generate",
                             items = [sId1, sId2, sId3])
        string_n = IntProperty(name = "Number of Stringers",
                               description = "Number of stringers to generate",
                               min = 1, max = 10,
                               default = 1)
    
        string_dis = BoolProperty(name = "Distributed",
                                  description = "Use distributed stringers",
                                  default = False)
    
        string_w = FloatProperty(name = "Stringer width",
                                 description = "Width of stringer as a percentage of tread width",
                                 min = 0.0001, max = 100.0,
                                 default = 15.0)
        string_h = FloatProperty(name = "Stringer Height",
                                 description = "Height of the stringer",
                                 min = 0.0001, max = 100.0,
                                 default = 0.3)
        string_tw = FloatProperty(name = "Web Thickness",
                                  description = "Thickness of the beam's web as a percentage of width",
                                  min = 0.0001, max = 100.0,
                                  default = 25.0)
        string_tf = FloatProperty(name = "Flange Thickness",
                                  description = "Thickness of the flange",
                                  min = 0.0001, max = 100.0,
                                  default = 0.05)
        string_tp = FloatProperty(name = "Flange Taper",
                                  description = "Flange thickness taper as a percentage",
                                  min = 0.0, max = 100.0,
                                  default = 0.0)
        string_g = BoolProperty(name = "Floating",
                                description = "Cut bottom of strigner to be a \"floating\" section",
                                default = False)
    
        use_original = BoolProperty(name = "Use legacy method",
                                    description = "Use the Blender 2.49 legacy method for stair generation",
                                    default = True)
        rEnable = BoolProperty(name = "Right Details",
                               description = "Generate right side details (posts/rails/retainers)",
                               default = True)
        lEnable = BoolProperty(name = "Left Details",
                               description = "Generate left side details (posts/rails/retainers)",
                               default = True)
    
        # Draw the GUI:
        def draw(self, context):
            layout = self.layout
            box = layout.box()
            box.prop(self, 'typ')
            box = layout.box()
            box.prop(self, 'rise')
            if self.typ != "id4":
                box.prop(self, 'run')
            else:
                box.prop(self, 'deg')
                box.prop(self, 'rad1')
                box.prop(self, 'rad2')
                box.prop(self, 'center')
            if self.typ == "id1":
                box.prop(self, 'use_original')
                if not self.use_original:
                    box.prop(self, 'rEnable')
                    box.prop(self, 'lEnable')
            else:
    
                self.use_original = False
    
                box.prop(self, 'rEnable')
                box.prop(self, 'lEnable')
                
            # Treads
            box = layout.box()
            box.prop(self, 'make_treads')
            if self.make_treads:
    
                if not self.use_original and self.typ != "id4":
    
                    box.prop(self, 'typ_t')
                else:
                    self.typ_t = "tId1"
    
                if self.typ != "id4":
                    box.prop(self, 'tread_w')
    
                box.prop(self, 'tread_h')
                box.prop(self, 'tread_t')
    
                    box.prop(self, 'tread_o')
                else:
                    self.tread_o = 0.0
                box.prop(self, 'tread_n')
                if self.typ_t != "tId1":
                    box.prop(self, 'tread_tk')
                    box.prop(self, 'tread_sec')
                    if self.tread_sec > 1 and self.typ_t not in ["tId3", "tId4"]:
                        box.prop(self, 'tread_sp')
                    if self.typ_t in ["tId3", "tId4", "tId5"]:
                        box.prop(self, 'tread_sn')
    
                elif self.typ == "id4":
                    box.prop(self, "tread_slc")
    
                        
            # Posts
            box = layout.box()
            box.prop(self, 'make_posts')
            if self.make_posts:
                box.prop(self, 'post_d')
                box.prop(self, 'post_w')
                box.prop(self, 'post_n')
                
            # Railings
            box = layout.box()
            box.prop(self, 'make_railings')
            if self.make_railings:
                box.prop(self, 'rail_w')
                box.prop(self, 'rail_t')
                box.prop(self, 'rail_h')
                
            # Retainers
            box = layout.box()
            box.prop(self, 'make_retainers')
            if self.make_retainers:
                box.prop(self, 'ret_w')
                box.prop(self, 'ret_h')
                box.prop(self, 'ret_n')
                
            # Stringers
            box = layout.box()
            if self.typ != "id2":
                box.prop(self, 'make_stringer')
            else:
                self.make_stringer = True
            if self.make_stringer:
                if not self.use_original:
                    box.prop(self, 'typ_s')
                else:
                    self.typ_s = "sId1"
                box.prop(self, 'string_w')
                if self.typ == "id1":
                    if self.typ_s == "sId1" and not self.use_original:
                        box.prop(self, 'string_n')
    
                        box.prop(self, 'string_dis')
    
                    elif self.typ_s in ["sId2", "sId3"]:
                        box.prop(self, 'string_n')
    
                        box.prop(self, 'string_dis')
    
                        box.prop(self, 'string_h')
                        box.prop(self, 'string_tw')
                        box.prop(self, 'string_tf')
                        box.prop(self, 'string_tp')
                        box.prop(self, 'string_g')
                elif self.typ == "id2":
    
                    if self.typ_s in ["sId2", "sId3"]:
    
                        box.prop(self, 'string_tw')
                        box.prop(self, 'string_tf')
    
            # Tread support:
    ##        if self.make_stringer and typ_s in ["sId2", "sId3"]:
    
        def execute(self, context):
            global G
            global typ
            global typ_s
            global typ_t
            global rise
            global run
            typ = self.typ
            typ_s = self.typ_s
            typ_t = self.typ_t
            rise = self.rise
            run = self.run
            G=General(rise,run,self.tread_n)
            if self.make_treads:
    
                if typ != "id4":
                    Treads(G,
                           typ,
                           typ_t,
                           run,
                           self.tread_w,
                           self.tread_h,
                           self.run,
                           self.rise,
                           self.tread_t,
                           self.tread_o,
                           self.tread_n,
                           self.tread_tk,
                           self.tread_sec,
                           self.tread_sp,
                           self.tread_sn)
                else:
                    Treads(G,
                           typ,
                           typ_t,
                           self.deg,
                           self.rad2,
                           self.tread_h,
                           self.run,
                           self.rise,
                           self.tread_t,
                           self.rad1,
                           self.tread_n,
                           self.tread_tk,
                           self.tread_sec,
                           self.tread_sp,
                           self.tread_sn,
                           self.tread_slc)
    
            if self.make_posts and (self.rEnable or self.lEnable):
                Posts(G,
                      rise,
                      run,
                      self.post_d,
                      self.post_w,
                      self.tread_w,
                      self.post_n,
                      self.rail_h,
                      self.rail_t,
                      self.rEnable,
                      self.lEnable)
            if self.make_railings and (self.rEnable or self.lEnable):
                Rails(G,
                      self.rail_w,
                      self.rail_t,
                      self.rail_h,
                      self.tread_t,
                      self.post_w,
                      self.post_d,
                      self.tread_w,
                      self.rEnable,
                      self.lEnable)
            if self.make_retainers and (self.rEnable or self.lEnable):
                Retainers(G,
                          self.ret_w,
                          self.ret_h,
                          self.post_w,
                          self.tread_w,
                          self.rail_h,
                          self.ret_n,
                          self.rEnable,
                          self.lEnable)
            if self.make_stringer:
                if typ == "id1" and self.use_original:
                    Stringer(G,
                             typ,
                             typ_s,
                             rise,
                             run,
                             self.string_w,
                             self.string_h,
                             self.tread_n,
                             self.tread_h,
                             self.tread_w,
                             self.tread_t,
                             self.tread_o,
                             self.string_tw,
                             self.string_tf,
    
                             self.string_tp,
                             not self.string_g)
    
                elif typ == "id3":
                    Stringer(G,
                             typ,
                             typ_s,
                             rise,
                             run,
                             100,
                             self.string_h,
                             self.tread_n,
                             self.tread_h,
                             self.tread_w,
                             self.tread_t,
                             self.tread_o,
                             self.string_tw,
                             self.string_tf,
                             self.string_tp,
                             not self.string_g,
    
                elif typ == "id4":
                    Stringer(G,
                             typ,
                             typ_s,
                             rise,
                             self.deg,
                             self.string_w,
                             self.string_h,
                             self.tread_n,
                             self.tread_h,
                             self.rad2 - self.rad1,
                             self.tread_t,
                             self.rad1,
                             self.string_tw,
                             self.string_tf,
                             self.string_tp,
                             not self.string_g,
                             self.string_n,
                             self.string_dis,
                             self.use_original,
                             self.tread_slc)
    
                else:
                    Stringer(G,
                             typ,
                             typ_s,
                             rise,
                             run,
                             self.string_w,
                             self.string_h,
                             self.tread_n,
                             self.tread_h,
                             self.tread_w,
                             self.tread_t,
                             self.tread_o,
                             self.string_tw,
                             self.string_tf,
                             self.string_tp,
                             not self.string_g,
                             self.string_n,