Skip to content
Snippets Groups Projects
Castle.py 140 KiB
Newer Older
  • Learn to ignore specific revisions
  • ################################################################################
    # ***** BEGIN GPL LICENSE BLOCK *****
    #
    # This is free software under the terms of the GNU General Public License
    # you may redistribute it, and/or modify it.
    #
    # This code 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 (http://www.gnu.org/licenses/) for more details.
    #
    # ***** END GPL LICENSE BLOCK *****
    '''
    Generate an object: (OBJ_N) "Castle", complex mesh collection.
     Derived from many sources, inspired by many in the "community",
     - see documentation for details on use and credits.
    '''
    # mod list, in order of complexity and necessity...
    #  some want, some needed, some a feature not a bug just need controls.
    #
    # @todo: fix dome radius -  not complete when wall width or depth < 15
    #        globe (z/2) is fun but not intended design.
    # @todo: check width minimum as is done for height, maybe depth too.
    # @todo: fix grout for bottom of first row (adjust wall base).
    #
    # @todo: eleminate result of "None" if possible from  circ() and other subroutines.
    # @todo: review defaults and limits for all UI entries.
    #
    #
    # Abstract/experimental use only:
    # @todo: Curve (wallSlope) inverts walls: reverse slope and adjust sizing to fit floor.
    # @todo: Tunnel, paired sloped walls, need to create separate objects and adjust sizing.
    # @todo: Turret/tower - uses rotated wall, need to change curvature/slope orientation/sizing
    #  allow openings and placement (corners, center wall, etc.).
    #
    #
    # wish list:
    # @todo: do not allow opening overlap.
    # @todo: implement "Oculus" for dome (top center opening). See "Radial" floor with door.
    # @todo: make "true" roof: top of castle walls or turret, multiple levels, style options - peaked, flat.
    # @todo: integrate portal with doorway.
    # @todo: add stair_builder; keep (wall) steps.
    # @todo: integrate balcony with shelf.
    # @todo: add block shapes: triangle, hexagon, octagon, round (disc/ball), "Rocks" (Brikbot author), etc...
    #        - possible window/door/opening shapes?
    #
    ################################################################################
    
    import bpy
    from bpy.props import IntProperty, FloatProperty, FloatVectorProperty, BoolProperty, EnumProperty
    from random import random
    
    import math
    from math import fmod, sqrt, sin, cos, atan
    
    ################################################################################
    
    ################################################################################
    
    #####
    # constants
    #####
    
    OBJ_N = "Castle"  # Primary object name (base/floor/foundation)
    OBJ_CF = "CFloor"  # Multi-level "floor"
    OBJ_CR = "CRoof"  # roofing
    OBJ_WF = "CWallF"  # front, left, back, right wall objects
    OBJ_WL = "CWallL"
    OBJ_WB = "CWallB"
    OBJ_WR = "CWallR"
    
    # not sure if this is more efficient or not, just seems no need to import value.
    cPie = 3.14159265359
    cPieHlf = cPie / 2  # used to rotate walls 90 degrees (1.570796...)
    
    BLOCK_MIN = 0.1  # Min block sizing; also used for openings.
    
    WALL_MAX = 100
    HALF_WALL = WALL_MAX / 2  # edging, openings, etc., limited to half wall limit.
    
    WALL_MIN = BLOCK_MIN * 3  # min wall block size*3 for each dimension.
    WALL_DEF = 20  # Default wall/dome size
    
    LVL_MAX = 10  # castle levels (vertical wall repeat).
    
    # riser BLOCK_XDEF should be 0.75 and tread BLOCK_DDEF 1.0 for stair steps.
    BLOCK_XDEF = 0.75  # stanadard block width, including steps and shelf.
    BLOCK_DDEF = 1.0  # standard block depth.
    BLOCK_MAX = WALL_MAX  # Max block sizing.
    BLOCK_VMAX = BLOCK_MAX / 2  # block variations, no negative values.
    
    # gap 0 makes solid wall but affects other options, like openings, not in a good way.
    # Negative gap creates "phantom blocks" (extraneous verts) inside the faces.
    GAP_MIN = 0.01  # min space between blocks.
    GAP_MAX = BLOCK_MAX / 2  # maybe later... -BLOCK_MIN # max space between blocks.
    
    ROW_H_WEIGHT = 0.5  # Use 0.5, else create parameter:
    #  0=no effect, 1=1:1 relationship, negative values allowed.
    
    BASE_TMIN = BLOCK_MIN  # floor min thickness
    BASE_TMAX = BLOCK_MAX / 2  # floor max thickness, limit to half (current) block height.
    
    # Default Door settings
    DEF_DOORW = 2.5
    DEF_DOORH = 3.5
    DEF_DOORX = 2.5
    
    #####
    # working variables, per option per level.
    #####
    
    # wall/block Settings
    settings = {'w': 1.2, 'wv': 0.3, 'h': .6, 'hv': 0.1, 'd': 0.3, 'dv': 0.1,
                'g': 0.1, 'sdv': 0.1,
                'eoff': 0.3,
                'Steps': False, 'StepsL': False, 'StepsB': False, 'StepsO': False,
                'Shelf': False, 'ShelfO': False,
                'Radial': False, 'Slope': False}
    # 'w':width 'wv':width Variation
    # 'h':height 'hv':height Variation
    # 'd':depth 'dv':depth Variation
    # 'g':grout
    # 'sdv':subdivision(distance or angle)
    # 'eoff':edge offset
    # passed as params since modified per object; no sense to change UI values.
    # 'Steps' create steps, 'StepsL'step left, 'StepsB' fill with blocks, 'StepsO' outside of wall.
    # 'Shelf' add shelf/balcony, 'ShelfO'outside of wall.
    # 'Radial' create "disc"; makes dome when combined with "Slope" (globe when height/2).
    # 'Slope' curve wall - forced for Dome
    
    # dims = area of wall (centered/splitX from 3D cursor); modified for radial/dome.
    dims = {'s': -10, 'e': 10, 't': 15}
    # dims = {'s':0, 'e':cPie*3/2, 't':12.3} # radial
    # 's' start, 'e' end, 't' top
    
    # Apertures in wall, includes all openings for door, window, slot.
    #openingSpecs = []
    # openingSpecs indexes, based on order of creation.
    OP_DOOR = 0
    OP_PORT = 1
    OP_CREN = 2
    OP_SLOT = 3
    
    openingSpecs = [{'a': False, 'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 0, 'n': 0, 'bvl': 0.0,
                     'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
    # 'a': active (apply to object),
    # 'w': opening width, 'h': opening height,
    # 'x': horizontal position, 'z': vertical position,
    # 'n': repeat opening with a spacing of x,
    # 'bvl': bevel the inside of the opening,
    # 'v': height of the top arch, 'vl':height of the bottom arch,
    # 't': thickness of the top arch, 'tl': thickness of the bottom arch
    
    
    ################################################################################
    
    ############################
    #
    # Psuedo macros:
    #
    # random values (in specific range).
    # random value +-0.5
    def rndc(): return (random() - 0.5)
    
    # random value +-1
    
    
    def rndd(): return rndc() * 2
    
    # line/circle intercepts
    # COff = distance perpendicular to the line to the center
    # cRad=radius
    # return the distance paralell to the line to the center of the circle at the intercept.
    # @todo: eleminate result of "None" if possible from  circ() and other subroutines.
    
    
    def circ(COff=0, cRad=1):
        absCOff = abs(COff)
        if absCOff > cRad:
            return None
        elif absCOff == cRad:
            return 0
        else:
            return sqrt(cRad**2 - absCOff**2)
    
    # bevel blocks.
    # pointsToAffect are: left=(4,6), right=(0,2)
    
    
    def bevelBlockOffsets(offsets, bevel, pointsToAffect):
        for num in pointsToAffect:
            offsets[num] = offsets[num][:]
            offsets[num][0] += bevel
    
    ############################
    #
    # Simple material management.
    # Return new, existing, or modified material reference.
    #
    # @todo: create additional materials based on diffuse options.
    #
    
    
    def uMatRGBSet(matName, RGBs, matMod=False, dShader='LAMBERT', dNtz=1.0):
    
        if matName not in bpy.data.materials:
            mtl = bpy.data.materials.new(matName)
            matMod = True
        else:
            mtl = bpy.data.materials[matName]
    
        if matMod:  # Set material values
            mtl.diffuse_color = RGBs
            mtl.diffuse_shader = dShader
            mtl.diffuse_intensity = dNtz
    
        return mtl
    
    ################################################################################
    
    ################################################################################
    #
    #  UI functions and object creation.
    #
    
    
    class add_castle(bpy.types.Operator):
        bl_idname = "mesh.add_castle"
        bl_label = OBJ_N
        bl_description = OBJ_N
        bl_options = {'REGISTER', 'UNDO'}
    
        # only create object when True
        # False allows modifying several parameters without creating object
    
        ConstructTog: BoolProperty(name="Construct", description="Generate the object", default=True)
    
    
        # Base area - set dimensions - Width (front/back) and Depth (sides),
        # floor origin/offset, thickness, and, material/color.
    
        cBaseW: FloatProperty(name="Width", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF, description="Base Width (X)")
        cBaseD: FloatProperty(name="Depth", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF, description="Base Depth (Y)")
        cBaseO: FloatProperty(name='Base', min=0, max=WALL_MAX, default=0, description="vert offset from 3D cursor.")
        cBaseT: FloatProperty(min=BASE_TMIN, max=BASE_TMAX, default=BASE_TMIN, description="Base thickness")
    
        cBaseRGB: FloatVectorProperty(min=0, max=1, default=(0.1, 0.1, 0.1), subtype='COLOR', size=3)
    
        CBaseB: BoolProperty(name="BloX", default=False, description="Block flooring")
        CBaseR: BoolProperty(default=False, description="Round flooring")
    
        CLvls: IntProperty(name="Levels", min=1, max=LVL_MAX, default=1)
    
    
        # current wall level parameter value display.
    
        CLvl: IntProperty(name="Level", min=1, max=LVL_MAX, default=1)
    
        #    curLvl=CLvl
    
        blockX: FloatProperty(name="Width", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_XDEF)
        blockZ: FloatProperty(name="Height", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_XDEF)
        blockD: FloatProperty(name="Depth", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_DDEF)
    
        # allow 0 for test cases...
        #    blockD=FloatProperty(name="Depth",min=0,max=BLOCK_MAX,default=BLOCK_DDEF)
    
        blockVar: BoolProperty(name="Vary", default=True, description="Randomize block sizing")
    
        blockWVar: FloatProperty(name="Width", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Width")
        blockHVar: FloatProperty(name="Height", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Height")
        blockDVar: FloatProperty(name="Depth", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Depth")
    
        blockMerge: BoolProperty(name="Merge", default=True, description="Merge closely adjoining blocks")
    
        # @todo: fix edgeing for mis-matched row sizing (or just call it a feature).
    
        EdgeOffset: FloatProperty(name="Edging", min=0, max=HALF_WALL, default=0.25, description="stagger wall ends")
    
        Grout: FloatProperty(name="Gap", min=GAP_MIN, max=GAP_MAX, default=0.05, description="Block separation")
    
        wallRGB: FloatVectorProperty(min=0, max=1, default=(0.5, 0.5, 0.5), subtype='COLOR', size=3)
    
        # track height with each level...?
    
        wallH: FloatProperty(name="Height", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF)
    
        # Base, always true (primary object/parent)
        #    CFloor1=BoolProperty(default=True)
    
        CFloor2: BoolProperty(default=False)
        CFloor3: BoolProperty(default=False)
        CFloor4: BoolProperty(default=False)
        CFloor5: BoolProperty(default=False)
        CFloor6: BoolProperty(default=False)
        CFloor7: BoolProperty(default=False)
        CFloor8: BoolProperty(default=False)
        CFloor9: BoolProperty(default=False)
        CFloor10: BoolProperty(default=False)
    
    
        # Which walls to generate, per level - LVL_MAX
    
        wallF1: BoolProperty(default=False, description="Front")
        wallF2: BoolProperty(default=False)
        wallF3: BoolProperty(default=False)
        wallF4: BoolProperty(default=False)
        wallF5: BoolProperty(default=False)
        wallF6: BoolProperty(default=False)
        wallF7: BoolProperty(default=False)
        wallF8: BoolProperty(default=False)
        wallF9: BoolProperty(default=False)
        wallF10: BoolProperty(default=False)
    
        wallL1: BoolProperty(default=False, description="Left")
        wallL2: BoolProperty(default=False)
        wallL3: BoolProperty(default=False)
        wallL4: BoolProperty(default=False)
        wallL5: BoolProperty(default=False)
        wallL6: BoolProperty(default=False)
        wallL7: BoolProperty(default=False)
        wallL8: BoolProperty(default=False)
        wallL9: BoolProperty(default=False)
        wallL10: BoolProperty(default=False)
    
        wallB1: BoolProperty(default=False, description="Back")
        wallB2: BoolProperty(default=False)
        wallB3: BoolProperty(default=False)
        wallB4: BoolProperty(default=False)
        wallB5: BoolProperty(default=False)
        wallB6: BoolProperty(default=False)
        wallB7: BoolProperty(default=False)
        wallB8: BoolProperty(default=False)
        wallB9: BoolProperty(default=False)
        wallB10: BoolProperty(default=False)
    
        wallR1: BoolProperty(default=False, description="Right")
        wallR2: BoolProperty(default=False)
        wallR3: BoolProperty(default=False)
        wallR4: BoolProperty(default=False)
        wallR5: BoolProperty(default=False)
        wallR6: BoolProperty(default=False)
        wallR7: BoolProperty(default=False)
        wallR8: BoolProperty(default=False)
        wallR9: BoolProperty(default=False)
        wallR10: BoolProperty(default=False)
    
    
        # round walls per level - LVL_MAX
    
        wallFO1: BoolProperty(default=False, description="Front")
        wallFO2: BoolProperty(default=False)
        wallFO3: BoolProperty(default=False)
        wallFO4: BoolProperty(default=False)
        wallFO5: BoolProperty(default=False)
        wallFO6: BoolProperty(default=False)
        wallFO7: BoolProperty(default=False)
        wallFO8: BoolProperty(default=False)
        wallFO9: BoolProperty(default=False)
        wallFO10: BoolProperty(default=False)
    
        wallLO1: BoolProperty(default=False, description="Left")
        wallLO2: BoolProperty(default=False)
        wallLO3: BoolProperty(default=False)
        wallLO4: BoolProperty(default=False)
        wallLO5: BoolProperty(default=False)
        wallLO6: BoolProperty(default=False)
        wallLO7: BoolProperty(default=False)
        wallLO8: BoolProperty(default=False)
        wallLO9: BoolProperty(default=False)
        wallLO10: BoolProperty(default=False)
    
        wallBO1: BoolProperty(default=False, description="Back")
        wallBO2: BoolProperty(default=False)
        wallBO3: BoolProperty(default=False)
        wallBO4: BoolProperty(default=False)
        wallBO5: BoolProperty(default=False)
        wallBO6: BoolProperty(default=False)
        wallBO7: BoolProperty(default=False)
        wallBO8: BoolProperty(default=False)
        wallBO9: BoolProperty(default=False)
        wallBO10: BoolProperty(default=False)
    
        wallRO1: BoolProperty(default=False, description="Right")
        wallRO2: BoolProperty(default=False)
        wallRO3: BoolProperty(default=False)
        wallRO4: BoolProperty(default=False)
        wallRO5: BoolProperty(default=False)
        wallRO6: BoolProperty(default=False)
        wallRO7: BoolProperty(default=False)
        wallRO8: BoolProperty(default=False)
        wallRO9: BoolProperty(default=False)
        wallRO10: BoolProperty(default=False)
    
        #    CRoof=BoolProperty(name="Roof",default=False)
    
    
        # Select wall modifier option to view/edit
    
        wallOpView: EnumProperty(items=(
    
            ("1", "Door", ""),
            ("2", "Window", ""),
            ("3", "Slot", ""),  # "classical arrow/rifle ports
            ("4", "Crenel", ""),  # gaps along top of wall
            ("5", "Steps", ""),
            ("6", "Shelf", ""),
        ),
            name="", description="View/Edit wall modifiers", default="1")
    
        # could add material selection for step and shelf...
    
    
        # Radiating from one point - round (disc), with slope makes dome.
    
        cDome: BoolProperty(name='Dome', default=False)
        cDomeRGB: FloatVectorProperty(min=0, max=1, default=(0.3, 0.1, 0), subtype='COLOR', size=3)
    
        cDomeH: FloatProperty(name='Height', min=WALL_MIN, max=WALL_MAX, default=WALL_DEF / 2)
        cDomeZ: FloatProperty(name='Base', min=BASE_TMIN, max=WALL_MAX, default=WALL_DEF, description="offset from floor")
        cDomeO: FloatProperty(name='Oculus', min=0, max=HALF_WALL, default=0, description="Dome opening.")
    
    
        # holes/openings in wall - doors, windows or slots; affects block row size.
    
        wallDoorW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=DEF_DOORW)
        wallDoorH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=DEF_DOORH)
        wallDoorX: FloatProperty(name="Indent", min=0, max=WALL_MAX, default=DEF_DOORX, description="horizontal offset from cursor")
        doorBvl: FloatProperty(name="Bevel", min=-10, max=10, default=0.25, description="Angle block face")
        doorRpt: BoolProperty(default=False, description="make multiple openings")
        doorArch: BoolProperty(name="Arch", default=True)
        doorArchC: FloatProperty(name="Curve", min=0.01, max=HALF_WALL, default=2.5, description="Arch curve height")
        doorArchT: FloatProperty(name="Thickness", min=0.001, max=HALF_WALL, default=0.75)
    
        wallDF1: BoolProperty(default=False, description="Front")
        wallDF2: BoolProperty(default=False)
        wallDF3: BoolProperty(default=False)
        wallDF4: BoolProperty(default=False)
        wallDF5: BoolProperty(default=False)
        wallDF6: BoolProperty(default=False)
        wallDF7: BoolProperty(default=False)
        wallDF8: BoolProperty(default=False)
        wallDF9: BoolProperty(default=False)
        wallDF10: BoolProperty(default=False)
    
        wallDL1: BoolProperty(default=False, description="Left")
        wallDL2: BoolProperty(default=False)
        wallDL3: BoolProperty(default=False)
        wallDL4: BoolProperty(default=False)
        wallDL5: BoolProperty(default=False)
        wallDL6: BoolProperty(default=False)
        wallDL7: BoolProperty(default=False)
        wallDL8: BoolProperty(default=False)
        wallDL9: BoolProperty(default=False)
        wallDL10: BoolProperty(default=False)
    
        wallDB1: BoolProperty(default=False, description="Back")
        wallDB2: BoolProperty(default=False)
        wallDB3: BoolProperty(default=False)
        wallDB4: BoolProperty(default=False)
        wallDB5: BoolProperty(default=False)
        wallDB6: BoolProperty(default=False)
        wallDB7: BoolProperty(default=False)
        wallDB8: BoolProperty(default=False)
        wallDB9: BoolProperty(default=False)
        wallDB10: BoolProperty(default=False)
    
        wallDR1: BoolProperty(default=False, description="Right")
        wallDR2: BoolProperty(default=False)
        wallDR3: BoolProperty(default=False)
        wallDR4: BoolProperty(default=False)
        wallDR5: BoolProperty(default=False)
        wallDR6: BoolProperty(default=False)
        wallDR7: BoolProperty(default=False)
        wallDR8: BoolProperty(default=False)
        wallDR9: BoolProperty(default=False)
        wallDR10: BoolProperty(default=False)
    
    
        # Slots, embrasure - classical slit for arrow/rifle ports.
    
        # need to prevent overlap with arch openings - though inversion is an interesting effect.
    
        SlotVW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=0.5)
        SlotVH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=3.5)
        SlotVL: FloatProperty(name="Indent", min=-WALL_MAX, max=WALL_MAX, default=0, description="The x position or spacing")
        SlotVZ: FloatProperty(name="Bottom", min=-WALL_MAX, max=WALL_MAX, default=4.00)
        slotVArchT: BoolProperty(name="Top", default=True)
        slotVArchB: BoolProperty(name="Bottom", default=True)
        SlotVRpt: BoolProperty(name="Repeat", default=False)
    
        wallEF1: BoolProperty(default=False, description="Front")
        wallEF2: BoolProperty(default=False)
        wallEF3: BoolProperty(default=False)
        wallEF4: BoolProperty(default=False)
        wallEF5: BoolProperty(default=False)
        wallEF6: BoolProperty(default=False)
        wallEF7: BoolProperty(default=False)
        wallEF8: BoolProperty(default=False)
        wallEF9: BoolProperty(default=False)
        wallEF10: BoolProperty(default=False)
    
        wallEL1: BoolProperty(default=False, description="Left")
        wallEL2: BoolProperty(default=False)
        wallEL3: BoolProperty(default=False)
        wallEL4: BoolProperty(default=False)
        wallEL5: BoolProperty(default=False)
        wallEL6: BoolProperty(default=False)
        wallEL7: BoolProperty(default=False)
        wallEL8: BoolProperty(default=False)
        wallEL9: BoolProperty(default=False)
        wallEL10: BoolProperty(default=False)
    
        wallEB1: BoolProperty(default=False, description="Back")
        wallEB2: BoolProperty(default=False)
        wallEB3: BoolProperty(default=False)
        wallEB4: BoolProperty(default=False)
        wallEB5: BoolProperty(default=False)
        wallEB6: BoolProperty(default=False)
        wallEB7: BoolProperty(default=False)
        wallEB8: BoolProperty(default=False)
        wallEB9: BoolProperty(default=False)
        wallEB10: BoolProperty(default=False)
    
        wallER1: BoolProperty(default=False, description="Right")
        wallER2: BoolProperty(default=False)
        wallER3: BoolProperty(default=False)
        wallER4: BoolProperty(default=False)
        wallER5: BoolProperty(default=False)
        wallER6: BoolProperty(default=False)
        wallER7: BoolProperty(default=False)
        wallER8: BoolProperty(default=False)
        wallER9: BoolProperty(default=False)
        wallER10: BoolProperty(default=False)
    
        wallPort: BoolProperty(default=False, description="Window opening")
        wallPortW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=2, description="Window (hole) width")
        wallPortH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=3, description="Window (hole) height")
        wallPortX: FloatProperty(name="Indent", min=-WALL_MAX, max=WALL_MAX, default=-2.5, description="The x position or spacing")
        wallPortZ: FloatProperty(name="Bottom", min=-WALL_MAX, max=WALL_MAX, default=5)
        wallPortBvl: FloatProperty(name="Bevel", min=-10, max=10, default=0.25, description="Angle block face")
        wallPortArchT: BoolProperty(name="Top", default=False)
        wallPortArchB: BoolProperty(name="Bottom", default=False)
        wallPortRpt: BoolProperty(name="Repeat", default=False)
    
        wallPF1: BoolProperty(default=False, description="Front")
        wallPF2: BoolProperty(default=False)
        wallPF3: BoolProperty(default=False)
        wallPF4: BoolProperty(default=False)
        wallPF5: BoolProperty(default=False)
        wallPF6: BoolProperty(default=False)
        wallPF7: BoolProperty(default=False)
        wallPF8: BoolProperty(default=False)
        wallPF9: BoolProperty(default=False)
        wallPF10: BoolProperty(default=False)
    
        wallPL1: BoolProperty(default=False, description="Left")
        wallPL2: BoolProperty(default=False)
        wallPL3: BoolProperty(default=False)
        wallPL4: BoolProperty(default=False)
        wallPL5: BoolProperty(default=False)
        wallPL6: BoolProperty(default=False)
        wallPL7: BoolProperty(default=False)
        wallPL8: BoolProperty(default=False)
        wallPL9: BoolProperty(default=False)
        wallPL10: BoolProperty(default=False)
    
        wallPB1: BoolProperty(default=False, description="Back")
        wallPB2: BoolProperty(default=False)
        wallPB3: BoolProperty(default=False)
        wallPB4: BoolProperty(default=False)
        wallPB5: BoolProperty(default=False)
        wallPB6: BoolProperty(default=False)
        wallPB7: BoolProperty(default=False)
        wallPB8: BoolProperty(default=False)
        wallPB9: BoolProperty(default=False)
        wallPB10: BoolProperty(default=False)
    
        wallPR1: BoolProperty(default=False, description="Right")
        wallPR2: BoolProperty(default=False)
        wallPR3: BoolProperty(default=False)
        wallPR4: BoolProperty(default=False)
        wallPR5: BoolProperty(default=False)
        wallPR6: BoolProperty(default=False)
        wallPR7: BoolProperty(default=False)
        wallPR8: BoolProperty(default=False)
        wallPR9: BoolProperty(default=False)
        wallPR10: BoolProperty(default=False)
    
    
        # Crenel = "gaps" on top of wall.
    
        # review and determine min for % - should allow less...
    
        CrenelXP: FloatProperty(name="Width %", min=0.10, max=1.0, default=0.15)
        CrenelZP: FloatProperty(name="Height %", min=0.10, max=1.0, default=0.10)
    
        wallCF1: BoolProperty(default=False, description="Front")
        wallCF2: BoolProperty(default=False)
        wallCF3: BoolProperty(default=False)
        wallCF4: BoolProperty(default=False)
        wallCF5: BoolProperty(default=False)
        wallCF6: BoolProperty(default=False)
        wallCF7: BoolProperty(default=False)
        wallCF8: BoolProperty(default=False)
        wallCF9: BoolProperty(default=False)
        wallCF10: BoolProperty(default=False)
    
        wallCL1: BoolProperty(default=False, description="Left")
        wallCL2: BoolProperty(default=False)
        wallCL3: BoolProperty(default=False)
        wallCL4: BoolProperty(default=False)
        wallCL5: BoolProperty(default=False)
        wallCL6: BoolProperty(default=False)
        wallCL7: BoolProperty(default=False)
        wallCL8: BoolProperty(default=False)
        wallCL9: BoolProperty(default=False)
        wallCL10: BoolProperty(default=False)
    
        wallCB1: BoolProperty(default=False, description="Back")
        wallCB2: BoolProperty(default=False)
        wallCB3: BoolProperty(default=False)
        wallCB4: BoolProperty(default=False)
        wallCB5: BoolProperty(default=False)
        wallCB6: BoolProperty(default=False)
        wallCB7: BoolProperty(default=False)
        wallCB8: BoolProperty(default=False)
        wallCB9: BoolProperty(default=False)
        wallCB10: BoolProperty(default=False)
    
        wallCR1: BoolProperty(default=False, description="Right")
        wallCR2: BoolProperty(default=False)
        wallCR3: BoolProperty(default=False)
        wallCR4: BoolProperty(default=False)
        wallCR5: BoolProperty(default=False)
        wallCR6: BoolProperty(default=False)
        wallCR7: BoolProperty(default=False)
        wallCR8: BoolProperty(default=False)
        wallCR9: BoolProperty(default=False)
        wallCR10: BoolProperty(default=False)
    
        # see "balcony" options for improved capabilities.
        # should limit x and z to wall boundaries
    
        ShelfX: FloatProperty(name="XOff", min=-WALL_MAX, max=WALL_MAX, default=-(WALL_DEF / 2), description="x origin")
        ShelfZ: FloatProperty(name="Base", min=-WALL_MAX, max=WALL_MAX, default=WALL_DEF, description="z origin")
        ShelfW: FloatProperty(name="Width", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="The Width of shelf area")
    
        # height seems to be double, check usage
    
        ShelfH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=0.5, description="The Height of Shelf area")
        ShelfD: FloatProperty(name="Depth", min=BLOCK_MIN, max=WALL_MAX, default=BLOCK_DDEF, description="Depth of shelf")
        ShelfOut: BoolProperty(name="Out", default=True, description="Position outside of wall")
    
        wallBF1: BoolProperty(default=False, description="Front")
        wallBF2: BoolProperty(default=False)
        wallBF3: BoolProperty(default=False)
        wallBF4: BoolProperty(default=False)
        wallBF5: BoolProperty(default=False)
        wallBF6: BoolProperty(default=False)
        wallBF7: BoolProperty(default=False)
        wallBF8: BoolProperty(default=False)
        wallBF9: BoolProperty(default=False)
        wallBF10: BoolProperty(default=False)
    
        wallBL1: BoolProperty(default=False, description="Left")
        wallBL2: BoolProperty(default=False)
        wallBL3: BoolProperty(default=False)
        wallBL4: BoolProperty(default=False)
        wallBL5: BoolProperty(default=False)
        wallBL6: BoolProperty(default=False)
        wallBL7: BoolProperty(default=False)
        wallBL8: BoolProperty(default=False)
        wallBL9: BoolProperty(default=False)
        wallBL10: BoolProperty(default=False)
    
        wallBB1: BoolProperty(default=False, description="Back")
        wallBB2: BoolProperty(default=False)
        wallBB3: BoolProperty(default=False)
        wallBB4: BoolProperty(default=False)
        wallBB5: BoolProperty(default=False)
        wallBB6: BoolProperty(default=False)
        wallBB7: BoolProperty(default=False)
        wallBB8: BoolProperty(default=False)
        wallBB9: BoolProperty(default=False)
        wallBB10: BoolProperty(default=False)
    
        wallBR1: BoolProperty(default=False, description="Right")
        wallBR2: BoolProperty(default=False)
        wallBR3: BoolProperty(default=False)
        wallBR4: BoolProperty(default=False)
        wallBR5: BoolProperty(default=False)
        wallBR6: BoolProperty(default=False)
        wallBR7: BoolProperty(default=False)
        wallBR8: BoolProperty(default=False)
        wallBR9: BoolProperty(default=False)
        wallBR10: BoolProperty(default=False)
    
        StepX: FloatProperty(name="XOff", min=-WALL_MAX, max=WALL_MAX, default=-(WALL_DEF / 2), description="x origin")
        StepZ: FloatProperty(name="Base", min=-WALL_MAX, max=WALL_MAX, default=0, description="z origin")
        StepW: FloatProperty(name="Width", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="Width of step area")
        StepH: FloatProperty(name="Height", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="Height of step area")
        StepD: FloatProperty(name="Depth", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_XDEF, description="Step offset (distance) from wall")
        StepV: FloatProperty(name="Riser", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_XDEF, description="Step height")
        StepT: FloatProperty(name="Tread", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_DDEF, description="Step footing/tread")
        StepL: BoolProperty(name="Slant", default=False, description="Step direction.")
        StepF: BoolProperty(name="Fill", default=False, description="Fill with supporting blocks")
        StepOut: BoolProperty(name="Out", default=False, description="Position outside of wall")
    
        wallSF1: BoolProperty(default=False, description="Front")
        wallSF2: BoolProperty(default=False)
        wallSF3: BoolProperty(default=False)
        wallSF4: BoolProperty(default=False)
        wallSF5: BoolProperty(default=False)
        wallSF6: BoolProperty(default=False)
        wallSF7: BoolProperty(default=False)
        wallSF8: BoolProperty(default=False)
        wallSF9: BoolProperty(default=False)
        wallSF10: BoolProperty(default=False)
    
        wallSL1: BoolProperty(default=False, description="Left")
        wallSL2: BoolProperty(default=False)
        wallSL3: BoolProperty(default=False)
        wallSL4: BoolProperty(default=False)
        wallSL5: BoolProperty(default=False)
        wallSL6: BoolProperty(default=False)
        wallSL7: BoolProperty(default=False)
        wallSL8: BoolProperty(default=False)
        wallSL9: BoolProperty(default=False)
        wallSL10: BoolProperty(default=False)
    
        wallSB1: BoolProperty(default=False, description="Back")
        wallSB2: BoolProperty(default=False)
        wallSB3: BoolProperty(default=False)
        wallSB4: BoolProperty(default=False)
        wallSB5: BoolProperty(default=False)
        wallSB6: BoolProperty(default=False)
        wallSB7: BoolProperty(default=False)
        wallSB8: BoolProperty(default=False)
        wallSB9: BoolProperty(default=False)
        wallSB10: BoolProperty(default=False)
    
        wallSR1: BoolProperty(default=False, description="Right")
        wallSR2: BoolProperty(default=False)
        wallSR3: BoolProperty(default=False)
        wallSR4: BoolProperty(default=False)
        wallSR5: BoolProperty(default=False)
        wallSR6: BoolProperty(default=False)
        wallSR7: BoolProperty(default=False)
        wallSR8: BoolProperty(default=False)
        wallSR9: BoolProperty(default=False)
        wallSR10: BoolProperty(default=False)
    
        # allow per wall and level?
    
        cXTest: BoolProperty(default=False)
    
        # wall shape modifiers
        # make the wall circular - if not curved it's a flat disc
        # Radiating from one point - round/disc; instead of square
    
        #    wallDisc=BoolProperty(default=False,description="Make wall disc")
    
        # Warp/slope/curve wall - abstract use only (except for dome use)
    
        wallSlope: BoolProperty(default=False, description="Curve wall")  # top to bottom mid
        CTunnel: BoolProperty(default=False, description="Tunnel wall")  # bottom to top mid
    
        # rotate tunnel?...
    
        CTower: BoolProperty(name='Tower', default=False)
    
        ##
        ####
        ############################################################################
        # Show the UI - present properties to user.
    
        # Display the toolbox options
    
        ############################################################################
        ####
        ##
    
        def draw(self, context):
    
            layout = self.layout
    
            box = layout.box()
            box.prop(self, 'ConstructTog')
            # Castle area.
            box = layout.box()
            row = box.row()
            row.prop(self, 'cBaseRGB', text='Base')
            row = box.row()
            row.prop(self, 'cBaseW')
            row.prop(self, 'cBaseD')
    
            # this is 0 until more complex settings allowed... like repeat or
            # per-level (like wall) selection.
            #        box.prop(self,'cBaseO') # vert offset from 3D cursor
    
    
            box.prop(self, 'cBaseT', text="Thickness")  # height/thickness of floor.
            row = box.row()
            row.prop(self, 'CBaseB')  # Blocks or slab
            if self.properties.CBaseB:
                row.prop(self, 'CBaseR', text='', icon="MESH_CIRCLE")  # Round
            box.prop(self, 'CLvls', text="Levels")  # number of floors.
    
            # block sizing
            box = layout.box()
            row = box.row()
            row.label(text='Blocks:')
            box.prop(self, 'blockX')
            box.prop(self, 'blockZ')
            box.prop(self, 'blockD')
            box.prop(self, 'Grout')
            box.prop(self, 'EdgeOffset')
    
            # "fixed" sizing unless variance enabled.
            row = box.row()
            row.prop(self, 'blockVar')  # randomize block sizing
            if self.properties.blockVar:
                row.prop(self, 'blockMerge')
                box.prop(self, 'blockHVar')
                box.prop(self, 'blockWVar')
                box.prop(self, 'blockDVar')
    
            box = layout.box()
            box.prop(self, 'CLvl', text="Level")  # current "Level" (UI parameter values).
    
            # Walls
            row = box.row()
            row.prop(self, 'wallRGB', text='Walls')
            row = box.row()  # which walls to generate - show current level value.
            row.prop(self, 'wallF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
            row.prop(self, 'wallL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
            row.prop(self, 'wallB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
            row.prop(self, 'wallR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
            row = box.row()  # round wall option
            row.prop(self, 'wallFO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
            row.prop(self, 'wallLO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
            row.prop(self, 'wallBO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
            row.prop(self, 'wallRO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
            box.prop(self, 'wallH')
            if self.properties.CLvl > 1:  # Base is first level (always true)
                box.prop(self, 'CFloor' + str(self.properties.CLvl), text="Floor")  # floor for level.
    
            box.prop(self, 'wallOpView')  # View/edit wall modifiers
    
            # Door
            if self.properties.wallOpView == "1":
                #            box.prop(self,'doorRpt',text='Dupe')
                row = box.row()  # which walls have a Door
                row.prop(self, 'wallDF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
                row.prop(self, 'wallDL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
                row.prop(self, 'wallDB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
                row.prop(self, 'wallDR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
                box.prop(self, 'wallDoorW')
                box.prop(self, 'wallDoorH')
                box.prop(self, 'wallDoorX')
                box.prop(self, 'doorBvl')
    
                box.prop(self, 'doorArch')
                if self.doorArch:
                    box.prop(self, 'doorArchC')
                    box.prop(self, 'doorArchT')
    
            # Window
            if self.properties.wallOpView == "2":
                #            box.prop(self,'wallPortRpt',text='Dupe')
                row = box.row()  # which walls have portal/window
                row.prop(self, 'wallPF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
                row.prop(self, 'wallPL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
                row.prop(self, 'wallPB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
                row.prop(self, 'wallPR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
                row = box.row()
                row.prop(self, 'wallPortW')
                row.prop(self, 'wallPortH')
                box.prop(self, 'wallPortX')
                box.prop(self, 'wallPortZ')
                box.prop(self, 'wallPortBvl')
                box.label(text='Arch')
                row = box.row()
                row.prop(self, 'wallPortArchT')
                row.prop(self, 'wallPortArchB')
    
            # Slots
            if self.properties.wallOpView == "3":
                box.prop(self, 'SlotVRpt', text='Dupe')
                row = box.row()  # which walls have embrasures/slits
                row.prop(self, 'wallEF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
                row.prop(self, 'wallEL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
                row.prop(self, 'wallEB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
                row.prop(self, 'wallER' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
                box.prop(self, 'SlotVW')
                box.prop(self, 'SlotVH')
                box.prop(self, 'SlotVL')
                box.prop(self, 'SlotVZ')
                box.label(text='Arch')
                row = box.row()
                row.prop(self, 'slotVArchT')
                row.prop(self, 'slotVArchB')
    
            # Crenellation: gaps in top of wall, base of dome.
            if self.properties.wallOpView == "4":
                row = box.row()  # which walls have Crenellation
                row.prop(self, 'wallCF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
                row.prop(self, 'wallCL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
                row.prop(self, 'wallCB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
                row.prop(self, 'wallCR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
                box.prop(self, 'CrenelXP')
                box.prop(self, 'CrenelZP')
    
            # Steps
            if self.properties.wallOpView == "5":
                row = box.row()
                row.prop(self, 'StepL')
                row.prop(self, 'StepF')
                row.prop(self, 'StepOut')
                row = box.row()  # which walls have steps
                row.prop(self, 'wallSF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
                row.prop(self, 'wallSL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
                row.prop(self, 'wallSB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
                row.prop(self, 'wallSR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
                box.prop(self, 'StepX')
                box.prop(self, 'StepZ')
                box.prop(self, 'StepW')
                box.prop(self, 'StepH')
                box.prop(self, 'StepD')
                box.prop(self, 'StepV')
                box.prop(self, 'StepT')
    
            # Shelf/Balcony
            if self.properties.wallOpView == "6":
                box.prop(self, 'ShelfOut')
                row = box.row()  # which walls have a shelf
                row.prop(self, 'wallBF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
                row.prop(self, 'wallBL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
                row.prop(self, 'wallBB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
                row.prop(self, 'wallBR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
                box.prop(self, 'ShelfX')
                box.prop(self, 'ShelfZ')
                box.prop(self, 'ShelfW')
                box.prop(self, 'ShelfH')
                box.prop(self, 'ShelfD')
    
            # Dome
            box = layout.box()
            row = box.row()
            row.prop(self, 'cDome')
            if self.properties.cDome:
                row.prop(self, 'cDomeRGB', text='')
                box.prop(self, 'cDomeH')
                box.prop(self, 'cDomeZ')
    
            # Oculus - opening in top of dome
            #            box.prop(self,'cDomeO')
    
            # Use "icon="MESH_CONE" for roof
    
            box = layout.box()
            row = box.row()
            row.prop(self, 'cXTest', text='Experimental')
            if self.properties.cXTest:
                box.prop(self, 'wallSlope', text='Curve')
                box.prop(self, 'CTunnel', text='Tunnel', icon="CURVE_DATA")
                box.prop(self, 'CTower', text='Tower', icon="CURVE_DATA")
    
    
        ##
        ####
        ############################################################################
        # Respond to UI - process the properties set by user.
            # Check and process UI settings to generate castle.
        ############################################################################
        ####
        ##
    
    
        def execute(self, context):
    
            global openingSpecs
            global dims
            # Limit UI settings relative to other parameters,
            #  set globals and effeciency variables.
    
            # current level cannot exceed level setting...
            if self.properties.CLvl > self.properties.CLvls:
                self.properties.CLvl = self.properties.CLvls
    
            castleScene = bpy.context.scene
            blockWidth = self.properties.blockX
            blockHeight = self.properties.blockZ
            blockDepth = self.properties.blockD
    
            # wall "area" must be at least 3 blocks.
            minWallW = blockWidth * 3  # min wall width/depth
            minWallH = blockHeight * 3  # min wall height
    
            if self.properties.cBaseW < minWallW:
                self.properties.cBaseW = minWallW
            CastleX = self.properties.cBaseW
            midWallW = CastleX / 2
    
            if self.properties.wallH < minWallH:
                self.properties.wallH = minWallH
            planeHL = self.properties.wallH
            planeHL2 = planeHL * 2
            # proportional crenel height, shared with roof positioning.
            crenelH = planeHL * self.properties.CrenelZP
    
            if self.properties.cBaseD < minWallW:
                self.properties.cBaseD = minWallW
            CastleD = self.properties.cBaseD
            midWallD = (CastleD + blockDepth) / 2
    
            #        midWallD=CastleD/2
            #        midWallD=(CastleD-blockDepth)/2
    
    
            # gap cannot reduce block below minimum.
            if self.properties.Grout > blockHeight / 2 - BLOCK_MIN:
                self.properties.Grout = blockHeight / 2 - BLOCK_MIN
    
            # Variance limit for minimum block height.
    
            # not quite right, but usuable.
    
            blockHMin = self.properties.Grout + BLOCK_MIN
    
            # floor "thickness" cannot exceed block height
            if self.properties.cBaseT > blockHeight:
                self.properties.cBaseT = blockHeight
    
    
            ############
            #
    
            if self.properties.cDome:
                # base can't be lower than floor
                # consider limiting base to roof or wall height by levels?
                domeLimit = self.properties.cBaseO + self.properties.cBaseT