Newer
Older
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you may redistribute it, and/or
# modify it, under the terms of the GNU General Public License
# as published by the Free Software Foundation - either version 2
# 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, write to:
#
# the Free Software Foundation Inc.
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA
#
# or go online at: http://www.gnu.org/licenses/ to view license options.
#
# ##### END GPL LICENCE BLOCK #####
# authors: dudecon, jambay
# This module contains the UI definition, display,
# and processing (create mesh) functions.
# The routines to generate the vertices for the wall
# are found in the "Blocks" module.
from bpy.types import Operator
from bpy.props import (
BoolProperty,
FloatProperty,
Spivak Vladimir (cwolf3d)
committed
StringProperty,
from .Blocks import (
NOTZERO, PI,
dims,
settings,
shelfSpecs,
stepSpecs,
createWall,
radialized,
slope,
openingSpecs,
bigBlock,
shelfExt,
stepMod,
stepLeft,
shelfBack,
stepOnly,
stepBack,
)
Spivak Vladimir (cwolf3d)
committed
from bpy_extras import object_utils
class add_mesh_wallb(Operator, object_utils.AddObjectHelper):
bl_label = "Add a Masonry Wall"
bl_description = "Create a block (masonry) wall mesh"
bl_options = {'REGISTER', 'UNDO'}
# UI items - API for properties - User accessible variables...
# not all options are via UI, and some operations just don't work yet
Spivak Vladimir (cwolf3d)
committed
Wall : BoolProperty(name = "Wall",
default = True,
description = "Wall")
#### change properties
name : StringProperty(name = "Name",
description = "Name")
change : BoolProperty(name = "Change",
default = False,
description = "change Wall")
# only create object when True
# False allows modifying several parameters without creating object
name="Construct",
description="Generate the object",
default=True
)
# need to modify so radial makes a tower (normal);
# want "flat" setting to make disk (alternate)
# make the wall circular - if not sloped it's a flat disc
name="Radial",
description="Make masonry radial",
default=False
)
# curve the wall - if radial creates dome.
name="Curved",
description="Make masonry sloped, or curved",
default=False
)
# need to review defaults and limits for all of these UI objects
name="Start",
description="Left side, or start angle",
default=-10.0,
min=-100, max=100.0
)
name="End",
description="Right side, or end angle",
default=10.0,
min=0.0, max=100.0
)
name="Bottom",
description="Lower height or radius",
default=0.0,
min=-100, max=100
)
name="Top",
description="Upper height or radius",
default=15.0,
min=0.0, max=100.0
)
name="Edging",
description="Block staggering on wall sides",
default=0.6, min=0.0, max=100.0
)
name="Width",
description="Average width of each block",
default=1.5,
min=0.01, max=100.0
)
name="Variance",
description="Random variance of block width",
default=0.5,
min=0.0, max=100.0
)
name="Minimum",
description="Absolute minimum block width",
default=0.5,
min=0.01, max=100.0
)
name="Height",
description="Average Height of each block",
default=0.7,
min=0.01, max=100.0
)
name="Variance",
description="Random variance of block Height",
default=0.3,
min=0.0, max=100.0
)
name="Minimum",
description="Absolute minimum block Height",
default=0.25,
min=0.01, max=100.0
)
name="Depth",
description="Average Depth of each block",
default=2.0,
min=0.01, max=100.0
)
name="Variance",
description="Random variance of block Depth",
default=0.1,
min=0.0, max=100.0
)
name="Minimum",
description="Absolute minimum block Depth",
default=1.0,
min=0.01, max=100.0
)
name="Merge Blocks",
description="Make big blocks (merge closely adjoining blocks)",
default=False
)
name="Thickness",
description="Distance between blocks",
default=0.1,
min=-10.0, max=10.0
)
name="Variance",
description="Random variance of block Grout",
default=0.03,
min=0.0, max=100.0
)
name="Depth",
description="Grout Depth from the face of the blocks",
default=0.1,
min=0.0001, max=10.0
)
GroutDepthVariance: FloatProperty(
name="Variance",
description="Random variance of block Grout Depth",
default=0.03,
min=0.0, max=100.0
)
name="Edging",
description="Grout perimiter",
default=False
)
name="Opening(s)",
description="Make windows or doors",
default=True
)
name="Width",
description="The Width of the first opening",
default=2.5,
min=0.01, max=100.0
)
name="Height",
description="The Height of the first opening",
default=3.5,
min=0.01, max=100.0
)
name="Indent",
description="The x position or spacing of the first opening",
default=5.0,
min=-100, max=100.0
)
name="Bottom",
description="The z position of the First opening",
default=5.0,
min=-100, max=100.0
)
name="Repeat",
description="make multiple openings, with spacing X1",
default=False
)
Opening1TopArchTog: BoolProperty(
name="Top Arch",
description="Add an arch to the top of the first opening",
default=True
)
name="Curve",
description="Height of the arch on the top of the opening",
default=2.5,
min=0.001, max=100.0
)
Opening1TopArchThickness: FloatProperty(
name="Thickness",
description="Thickness of the arch on the top of the opening",
default=0.75,
min=0.001, max=100.0
)
Opening1BtmArchTog: BoolProperty(
name="Bottom Arch",
description="Add an arch to the bottom of opening 1",
default=False
)
name="Curve",
description="Height of the arch on the bottom of the opening",
default=1.0,
min=0.01, max=100.0
)
Opening1BtmArchThickness: FloatProperty(
name="Thickness",
description="Thickness of the arch on the bottom of the opening",
default=0.5,
min=0.01, max=100.0
)
name="Bevel",
description="Angle block face",
default=0.25,
min=-10.0, max=10.0
)
name="Crenels",
description="Make openings along top of wall",
default=False
)
name="Width",
description="Gap width in wall based the percentage of wall width",
default=0.25,
min=0.10, max=1.0,
subtype="PERCENTAGE"
)
name="Height",
description="Crenel Height as the percentage of wall height",
default=0.10,
min=0.10, max=1.0,
subtype="PERCENTAGE"
)
# need to prevent overlap with arch openings - though inversion is an interesting effect.
name="Slots",
description="Make narrow openings in wall",
default=False
)
name="Repeat",
description="Repeat slots along wall",
default=False
)
name="Wedged (n/a)",
description="Bevel edges of slots",
default=False
)
name="Indent",
description="The x position or spacing of slots",
default=0.0, min=-100, max=100.0
)
name="Opening",
description="The opening size of slots",
default=0.5, min=0.10, max=100.0
)
name="Vertical",
description="Vertical slots",
default=True
)
name="Height",
description="Height of vertical slot",
default=3.5,
min=0.10, max=100.0
)
name="Bottom",
description="Z position for slot",
default=5.00,
min=-100.0, max=100.0
)
name="Horizontal",
description="Horizontal slots",
default=False
)
name="Width",
description="Width of horizontal slot",
default=2.5,
min=0.10, max=100.0
)
# this should offset from VBtm... maybe make a % like crenels?
name="Bottom",
description="Z position for horizontal slot",
default=5.50,
min=-100.0, max=100.0
)
# properties for shelf (extend blocks in area)
name="Shelf",
description="Add blocks in area by depth to make shelf/platform",
default=False
)
name="Left",
description="The x position of Shelf",
default=-5.00,
min=-100, max=100.0
)
name="Bottom",
description="The z position of Shelf",
default=10.0,
min=-100, max=100.0
)
name="Height",
description="The Height of Shelf area",
default=1.0,
min=0.01, max=100.0
)
name="Width",
description="The Width of shelf area",
default=5.0,
min=0.01, max=100.0
)
name="Depth",
description="Depth of each block for shelf (from cursor + 1/2 wall depth)",
default=2.0,
min=0.01, max=100.0
)
name="Backside",
description="Shelf on backside of wall",
default=False
)
# properties for steps (extend blocks in area, progressive width)
name="Steps",
description="Add blocks in area by depth with progressive width to make steps",
default=False
)
name="Left",
description="The x position of steps",
default=-9.00,
min=-100, max=100.0
)
name="Bottom",
description="The z position of steps",
default=0.0,
min=-100, max=100.0
)
name="Height",
description="The Height of step area",
default=10.0,
min=0.01, max=100.0
)
name="Width",
description="The Width of step area",
default=8.0,
min=0.01, max=100.0
)
name="Depth",
description="Depth of each block for steps (from cursor + 1/2 wall depth)",
default=1.0,
min=0.01, max=100.0
)
name="Riser",
description="Height of each step",
default=0.70,
min=0.01, max=100.0
)
name="Tread",
description="Width of each step",
default=1.0,
min=0.01, max=100.0
)
name="Direction",
description="If checked, flip steps direction towards the -X axis",
default=False
)
name="Steps Only",
description="Steps only, no supporting blocks",
default=False
)
name="Backside",
description="Steps on backside of wall",
default=False
)
# Display the toolbox options
def draw(self, context):
layout = self.layout
box = layout.box()
box.prop(self, 'ConstructTog')
# Wall area (size/position)
box.label(text="Wall Size (area)")
col = box.column(align=True)
col.prop(self, "WallStart")
col.prop(self, "WallEnd")
col = box.column(align=True)
col.prop(self, "WallBottom")
col.prop(self, "WallTop")
box.prop(self, "EdgeOffset")
# Wall block sizing
box.label(text="Block Sizing")
box.prop(self, "MergeBlock")
# add checkbox for "fixed" sizing (ignore variance) a.k.a. bricks
col = box.column(align=True)
col.prop(self, "Width")
col.prop(self, "WidthVariance")
col.prop(self, "WidthMinimum")
col = box.column(align=True)
col.prop(self, "Height")
col.prop(self, "HeightVariance")
col.prop(self, "HeightMinimum")
col = box.column(align=True)
col.prop(self, "Depth")
col.prop(self, "DepthVariance")
col.prop(self, "DepthMinimum")
col = box.column(align=True)
col.prop(self, "Grout")
col.prop(self, "GroutVariance")
col = box.column(align=True)
col.prop(self, "GroutDepth")
col.prop(self, "GroutDepthVariance")
box.label(text="Wall Shape")
row = box.row(align=True)
row.prop(self, "RadialTog", toggle=True)
row.prop(self, "SlopeTog", toggle=True)
# Openings (doors, windows; arched)
box = layout.box()
box.prop(self, 'Opening1Tog')
if self.Opening1Tog:
col = box.column(align=True)
col.prop(self, "Opening1Width")
col.prop(self, "Opening1Height")
col.prop(self, "Opening1X")
col.prop(self, "Opening1Z")
col.prop(self, "Opening1Bevel")
box.prop(self, "Opening1Repeat", toggle=True)
sub_box = box.box()
sub_box.prop(self, "Opening1TopArchTog")
if self.Opening1TopArchTog:
col = sub_box.column(align=True)
col.prop(self, "Opening1TopArch")
col.prop(self, "Opening1TopArchThickness")
sub_box = box.box()
sub_box.prop(self, "Opening1BtmArchTog")
if self.Opening1BtmArchTog:
col = sub_box.column(align=True)
col.prop(self, "Opening1BtmArch")
col.prop(self, "Opening1BtmArchThickness")
# Slots (narrow openings)
box.prop(self, "SlotTog")
if self.SlotTog:
col = box.column(align=True)
col.prop(self, "SlotX")
col.prop(self, "SlotGap")
box.prop(self, "SlotRpt", toggle=True)
sub_box = box.box()
sub_box.prop(self, "SlotV")
if self.SlotV:
col = sub_box.column(align=True)
col.prop(self, "SlotVH")
col.prop(self, "SlotVBtm")
sub_box = box.box()
sub_box.prop(self, "SlotH")
if self.SlotH:
col = sub_box.column(align=True)
col.prop(self, "SlotHW")
col.prop(self, "SlotHBtm")
# Crenels, gaps in top of wall
box.prop(self, "CrenelTog")
if self.CrenelTog:
col = box.column(align=True)
col.prop(self, "CrenelXP")
col.prop(self, "CrenelZP")
box = layout.box()
box.prop(self, 'ShelfTog')
if self.ShelfTog:
col = box.column(align=True)
col.prop(self, "ShelfX")
col.prop(self, "ShelfZ")
col = box.column(align=True)
col.prop(self, "ShelfW")
col.prop(self, "ShelfH")
col.prop(self, "ShelfD")
box.prop(self, "ShelfBack")
# Steps
box = layout.box()
box.prop(self, 'StepTog')
if self.StepTog:
col = box.column(align=True)
col.prop(self, "StepX")
col.prop(self, "StepZ")
col = box.column(align=True)
col.prop(self, "StepH")
col.prop(self, "StepW")
col.prop(self, "StepD")
col = box.column(align=True)
col.prop(self, "StepV")
col.prop(self, "StepT")
col = box.column(align=True)
row = col.row(align=True)
row.prop(self, "StepLeft", toggle=True)
row.prop(self, "StepOnly", toggle=True)
col.prop(self, "StepBack", toggle=True)
if self.change == False:
# generic transform props
box = layout.box()
Spivak Vladimir (cwolf3d)
committed
box.prop(self, 'align', expand=True)
box.prop(self, 'location', expand=True)
box.prop(self, 'rotation', expand=True)
# Respond to UI - get the properties set by user.
# Check and process UI settings to generate masonry
def execute(self, context):
global radialized
global slope
global openingSpecs
global bigBlock
global shelfExt
global stepMod
global stepLeft
global shelfBack
global stepOnly
global stepBack
# Create the wall when enabled (skip regen iterations when off)
if not self.ConstructTog:
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
# enter the settings for the wall dimensions (area)
# start can't be zero - min/max don't matter [if max less than end] but zero don't workie.
# start can't exceed end.
if not self.WallStart or self.WallStart >= self.WallEnd:
self.WallStart = NOTZERO # Reset UI if input out of bounds...
dims['s'] = self.WallStart
dims['e'] = self.WallEnd
dims['b'] = self.WallBottom
dims['t'] = self.WallTop
settings['eoff'] = self.EdgeOffset
# retrieve the settings for the wall block properties
settings['w'] = self.Width
settings['wv'] = self.WidthVariance
settings['wm'] = self.WidthMinimum
if not radialized:
settings['sdv'] = settings['w']
else:
settings['sdv'] = 0.12
settings['h'] = self.Height
settings['hv'] = self.HeightVariance
settings['hm'] = self.HeightMinimum
settings['d'] = self.Depth
settings['dv'] = self.DepthVariance
settings['dm'] = self.DepthMinimum
if self.MergeBlock:
settings['g'] = self.Grout
settings['gv'] = self.GroutVariance
settings['gd'] = self.GroutDepth
settings['gdv'] = self.GroutDepthVariance
if self.GroutEdge:
settings['ge'] = 1
else:
settings['ge'] = 0
if self.RadialTog:
# eliminate to allow user control for start/completion?
dims['s'] = 0.0 # complete radial
if dims['e'] > PI * 2:
dims['e'] = PI * 2 # max end for circle
if dims['b'] < settings['g']:
dims['b'] = settings['g'] # min bottom for grout extension
else:
radialized = 0
if self.SlopeTog:
slope = 1
else:
slope = 0
shelfExt = 0
shelfBack = 0
# Add shelf if enabled
if self.ShelfTog:
shelfSpecs['h'] = self.ShelfH
shelfSpecs['w'] = self.ShelfW
shelfSpecs['d'] = self.ShelfD
shelfSpecs['x'] = self.ShelfX
shelfSpecs['z'] = self.ShelfZ
if self.ShelfBack:
shelfBack = 1
stepMod = 0
stepLeft = 0
stepOnly = 0
stepBack = 0
# Make steps if enabled
if self.StepTog:
stepSpecs['x'] = self.StepX
stepSpecs['z'] = self.StepZ
stepSpecs['h'] = self.StepH
stepSpecs['w'] = self.StepW
stepSpecs['d'] = self.StepD
stepSpecs['v'] = self.StepV
stepSpecs['t'] = self.StepT
if self.StepLeft:
if self.StepOnly:
if self.StepBack:
# enter the settings for the openings
# when openings overlap they create inverse stonework - interesting but not the desired effect :)
# if opening width == indent * 2 the edge blocks fail (row of blocks cross opening) - bug.
openingIdx = 0 # track opening array references for multiple uses
# general openings with arch options - can be windows or doors.
if self.Opening1Tog:
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 2.7, 'rp': 1,
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
openingSpecs[openingIdx]['w'] = self.Opening1Width
openingSpecs[openingIdx]['h'] = self.Opening1Height
openingSpecs[openingIdx]['x'] = self.Opening1X
openingSpecs[openingIdx]['z'] = self.Opening1Z
openingSpecs[openingIdx]['rp'] = self.Opening1Repeat
if self.Opening1TopArchTog:
openingSpecs[openingIdx]['v'] = self.Opening1TopArch
openingSpecs[openingIdx]['t'] = self.Opening1TopArchThickness
if self.Opening1BtmArchTog:
openingSpecs[openingIdx]['vl'] = self.Opening1BtmArch
openingSpecs[openingIdx]['tl'] = self.Opening1BtmArchThickness
openingSpecs[openingIdx]['b'] = self.Opening1Bevel
openingIdx += 1 # count window/door/arch openings
if self.SlotTog:
if self.SlotV: # vertical slots
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 0,
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
openingSpecs[openingIdx]['w'] = self.SlotGap
openingSpecs[openingIdx]['h'] = self.SlotVH
openingSpecs[openingIdx]['x'] = self.SlotX
openingSpecs[openingIdx]['z'] = self.SlotVBtm
openingSpecs[openingIdx]['rp'] = self.SlotRpt
openingSpecs[openingIdx]['v'] = self.SlotGap
openingSpecs[openingIdx]['t'] = self.SlotGap / 2
openingSpecs[openingIdx]['vl'] = self.SlotGap
openingSpecs[openingIdx]['tl'] = self.SlotGap / 2
openingIdx += 1 # count vertical slot openings
# need to handle overlap of H and V slots...
if self.SlotH: # Horizontal slots
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 0,
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
openingSpecs[openingIdx]['w'] = self.SlotHW
openingSpecs[openingIdx]['h'] = self.SlotGap
openingSpecs[openingIdx]['x'] = self.SlotX
openingSpecs[openingIdx]['z'] = self.SlotHBtm
# horizontal repeat isn't same spacing as vertical...
openingSpecs[openingIdx]['rp'] = self.SlotRpt
openingIdx += 1 # count horizontal slot openings
# Crenellations (top row openings)
if self.CrenelTog:
# add bottom arch option?
# perhaps a repeat toggle...
# if crenel opening overlaps with arch opening it fills with blocks...
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 1,
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
wallW = self.WallEnd - self.WallStart
crenelW = wallW * self.CrenelXP # Width % opening.
wallH = self.WallTop - self.WallBottom
crenelH = wallH * self.CrenelZP # % proportional height.
openingSpecs[openingIdx]['w'] = crenelW
openingSpecs[openingIdx]['h'] = crenelH
# calculate the spacing between openings.
# this isn't the absolute start (left),
# it's opening center offset relative to cursor (space between openings)...
openingSpecs[openingIdx]['x'] = crenelW * 2 - 1 # assume standard spacing
if not radialized: # normal wall?
# set indent 0 (center) if opening is 50% or more of wall width, no repeat.
openingSpecs[openingIdx]['x'] = 0
openingSpecs[openingIdx]['rp'] = 0
# set bottom of opening (center of hole)
openingSpecs[openingIdx]['z'] = self.WallTop - (crenelH / 2)
openingIdx += 1 # count crenel openings
# Process the user settings to generate a wall
# generate the list of vertices for the wall...
verts_array, faces_array = createWall(
radialized, slope, openingSpecs, bigBlock,
shelfExt, shelfBack, stepMod, stepLeft, stepOnly,
stepBack
)
Spivak Vladimir (cwolf3d)
committed
if bpy.context.mode == "OBJECT":
Spivak Vladimir (cwolf3d)
committed
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Wall' in context.active_object.data.keys()) and \
(self.change == True):
Spivak Vladimir (cwolf3d)
committed
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
mesh = bpy.data.meshes.new("Wall")
mesh.from_pydata(verts_array, [], faces_array)
obj.data = mesh
Spivak Vladimir (cwolf3d)
committed
for material in oldmesh.materials:
obj.data.materials.append(material)
Spivak Vladimir (cwolf3d)
committed
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
mesh = bpy.data.meshes.new("Wall")
mesh.from_pydata(verts_array, [], faces_array)
Spivak Vladimir (cwolf3d)
committed
obj = object_utils.object_data_add(context, mesh, operator=self)
Spivak Vladimir (cwolf3d)
committed
mesh.update()
Spivak Vladimir (cwolf3d)
committed
obj.data["Wall"] = True
obj.data["change"] = False
for prm in WallParameters():
obj.data[prm] = getattr(self, prm)
Spivak Vladimir (cwolf3d)
committed
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
mesh = bpy.data.meshes.new("TMP")
Spivak Vladimir (cwolf3d)
committed
mesh.from_pydata(verts_array, [], faces_array)
Spivak Vladimir (cwolf3d)
committed
obj = object_utils.object_data_add(context, mesh, operator=self)
Spivak Vladimir (cwolf3d)
committed
obj.select_set(True)
active_object.select_set(True)
Vladimir Spivak(cwolf3d)
committed
bpy.context.view_layer.objects.active = active_object
Spivak Vladimir (cwolf3d)
committed
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
Spivak Vladimir (cwolf3d)
committed
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
def WallParameters():
WallParameters = [
"ConstructTog",
"RadialTog",
"SlopeTog",
"WallStart",
"WallEnd",
"WallBottom",
"WallTop",
"EdgeOffset",
"Width",
"WidthVariance",
"WidthMinimum",
"Height",
"HeightVariance",
"HeightMinimum",
"Depth",
"DepthVariance",
"DepthMinimum",
"MergeBlock",
"Grout",
"GroutVariance",
"GroutDepth",
"GroutDepthVariance",
"GroutEdge",
"Opening1Tog",
"Opening1Width",
"Opening1Height",
"Opening1X",
"Opening1Z",
"Opening1Repeat",
"Opening1TopArchTog",
"Opening1TopArch",
"Opening1TopArchThickness",
"Opening1BtmArchTog",
"Opening1BtmArch",
"Opening1BtmArchThickness",
"CrenelTog",
"CrenelXP",
"CrenelZP",
"SlotTog",
"SlotRpt",
"SlotWdg",
"SlotX",
"SlotGap",
"SlotV",
"SlotVH",
"SlotVBtm",
"SlotH",
"SlotHW",
"SlotHBtm",
"ShelfTog",
"ShelfX",
"ShelfZ",
"ShelfH",
"ShelfW",
"ShelfD",
"ShelfBack",
"StepTog",
"StepX",
"StepZ",
"StepH",
"StepW",
"StepD",
"StepV",
"StepT",
"StepLeft",