343e218fc268a18f7bf302cc1ed77328c7bfdf9c
[blender-addons-contrib.git] / add_mesh_castle / Castle.py
1 ################################################################################
2 # ***** BEGIN GPL LICENSE BLOCK *****
3 #
4 # This is free software under the terms of the GNU General Public License
5 # you may redistribute it, and/or modify it.
6 #
7 # This code is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 # General Public License (http://www.gnu.org/licenses/) for more details.
11 #
12 # ***** END GPL LICENSE BLOCK *****
13 '''
14 Generate an object: (OBJ_N) "Castle", complex mesh collection.
15  Derived from many sources, inspired by many in the "community",
16  - see documentation for details on use and credits.
17 '''
18 # mod list, in order of complexity and necessity...
19 #  some want, some needed, some a feature not a bug just need controls.
20 #
21 # @todo: fix dome radius -  not complete when wall width or depth < 15
22 #        globe (z/2) is fun but not intended design.
23 # @todo: check width minimum as is done for height, maybe depth too.
24 # @todo: fix grout for bottom of first row (adjust wall base).
25 #
26 # @todo: eleminate result of "None" if possible from  circ() and other subroutines.
27 # @todo: review defaults and limits for all UI entries.
28 #
29 #
30 # Abstract/experimental use only:
31 # @todo: Curve (wallSlope) inverts walls: reverse slope and adjust sizing to fit floor.
32 # @todo: Tunnel, paired sloped walls, need to create separate objects and adjust sizing.
33 # @todo: Turret/tower - uses rotated wall, need to change curvature/slope orientation/sizing
34 #  allow openings and placement (corners, center wall, etc.).
35 #
36 #
37 # wish list:
38 # @todo: do not allow opening overlap.
39 # @todo: implement "Oculus" for dome (top center opening). See "Radial" floor with door.
40 # @todo: make "true" roof: top of castle walls or turret, multiple levels, style options - peaked, flat.
41 # @todo: integrate portal with doorway.
42 # @todo: add stair_builder; keep (wall) steps.
43 # @todo: integrate balcony with shelf.
44 # @todo: add block shapes: triangle, hexagon, octagon, round (disc/ball), "Rocks" (Brikbot author), etc...
45 #        - possible window/door/opening shapes?
46 #
47 ################################################################################
48
49 import bpy
50 from bpy.props import IntProperty, FloatProperty, FloatVectorProperty, BoolProperty, EnumProperty
51 from random import random
52
53 import math
54 from math import fmod, sqrt, sin, cos, atan
55
56 ################################################################################
57
58 ################################################################################
59
60 #####
61 # constants
62 #####
63
64 OBJ_N = "Castle"  # Primary object name (base/floor/foundation)
65 OBJ_CF = "CFloor"  # Multi-level "floor"
66 OBJ_CR = "CRoof"  # roofing
67 OBJ_WF = "CWallF"  # front, left, back, right wall objects
68 OBJ_WL = "CWallL"
69 OBJ_WB = "CWallB"
70 OBJ_WR = "CWallR"
71
72 # not sure if this is more efficient or not, just seems no need to import value.
73 cPie = 3.14159265359
74 cPieHlf = cPie / 2  # used to rotate walls 90 degrees (1.570796...)
75
76 BLOCK_MIN = 0.1  # Min block sizing; also used for openings.
77
78 WALL_MAX = 100
79 HALF_WALL = WALL_MAX / 2  # edging, openings, etc., limited to half wall limit.
80
81 WALL_MIN = BLOCK_MIN * 3  # min wall block size*3 for each dimension.
82 WALL_DEF = 20  # Default wall/dome size
83
84 LVL_MAX = 10  # castle levels (vertical wall repeat).
85
86 # riser BLOCK_XDEF should be 0.75 and tread BLOCK_DDEF 1.0 for stair steps.
87 BLOCK_XDEF = 0.75  # stanadard block width, including steps and shelf.
88 BLOCK_DDEF = 1.0  # standard block depth.
89 BLOCK_MAX = WALL_MAX  # Max block sizing.
90 BLOCK_VMAX = BLOCK_MAX / 2  # block variations, no negative values.
91
92 # gap 0 makes solid wall but affects other options, like openings, not in a good way.
93 # Negative gap creates "phantom blocks" (extraneous verts) inside the faces.
94 GAP_MIN = 0.01  # min space between blocks.
95 GAP_MAX = BLOCK_MAX / 2  # maybe later... -BLOCK_MIN # max space between blocks.
96
97 ROW_H_WEIGHT = 0.5  # Use 0.5, else create parameter:
98 #  0=no effect, 1=1:1 relationship, negative values allowed.
99
100 BASE_TMIN = BLOCK_MIN  # floor min thickness
101 BASE_TMAX = BLOCK_MAX / 2  # floor max thickness, limit to half (current) block height.
102
103 # Default Door settings
104 DEF_DOORW = 2.5
105 DEF_DOORH = 3.5
106 DEF_DOORX = 2.5
107
108 #####
109 # working variables, per option per level.
110 #####
111
112 # wall/block Settings
113 settings = {'w': 1.2, 'wv': 0.3, 'h': .6, 'hv': 0.1, 'd': 0.3, 'dv': 0.1,
114             'g': 0.1, 'sdv': 0.1,
115             'eoff': 0.3,
116             'Steps': False, 'StepsL': False, 'StepsB': False, 'StepsO': False,
117             'Shelf': False, 'ShelfO': False,
118             'Radial': False, 'Slope': False}
119 # 'w':width 'wv':width Variation
120 # 'h':height 'hv':height Variation
121 # 'd':depth 'dv':depth Variation
122 # 'g':grout
123 # 'sdv':subdivision(distance or angle)
124 # 'eoff':edge offset
125 # passed as params since modified per object; no sense to change UI values.
126 # 'Steps' create steps, 'StepsL'step left, 'StepsB' fill with blocks, 'StepsO' outside of wall.
127 # 'Shelf' add shelf/balcony, 'ShelfO'outside of wall.
128 # 'Radial' create "disc"; makes dome when combined with "Slope" (globe when height/2).
129 # 'Slope' curve wall - forced for Dome
130
131 # dims = area of wall (centered/splitX from 3D cursor); modified for radial/dome.
132 dims = {'s': -10, 'e': 10, 't': 15}
133 # dims = {'s':0, 'e':cPie*3/2, 't':12.3} # radial
134 # 's' start, 'e' end, 't' top
135
136 # Apertures in wall, includes all openings for door, window, slot.
137 #openingSpecs = []
138 # openingSpecs indexes, based on order of creation.
139 OP_DOOR = 0
140 OP_PORT = 1
141 OP_CREN = 2
142 OP_SLOT = 3
143
144 openingSpecs = [{'a': False, 'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 0, 'n': 0, 'bvl': 0.0,
145                  'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
146 # 'a': active (apply to object),
147 # 'w': opening width, 'h': opening height,
148 # 'x': horizontal position, 'z': vertical position,
149 # 'n': repeat opening with a spacing of x,
150 # 'bvl': bevel the inside of the opening,
151 # 'v': height of the top arch, 'vl':height of the bottom arch,
152 # 't': thickness of the top arch, 'tl': thickness of the bottom arch
153
154
155 ################################################################################
156
157 ############################
158 #
159 # Psuedo macros:
160 #
161 # random values (in specific range).
162 # random value +-0.5
163 def rndc(): return (random() - 0.5)
164
165 # random value +-1
166
167
168 def rndd(): return rndc() * 2
169
170 # line/circle intercepts
171 # COff = distance perpendicular to the line to the center
172 # cRad=radius
173 # return the distance paralell to the line to the center of the circle at the intercept.
174 # @todo: eleminate result of "None" if possible from  circ() and other subroutines.
175
176
177 def circ(COff=0, cRad=1):
178     absCOff = abs(COff)
179     if absCOff > cRad:
180         return None
181     elif absCOff == cRad:
182         return 0
183     else:
184         return sqrt(cRad**2 - absCOff**2)
185
186 # bevel blocks.
187 # pointsToAffect are: left=(4,6), right=(0,2)
188
189
190 def bevelBlockOffsets(offsets, bevel, pointsToAffect):
191     for num in pointsToAffect:
192         offsets[num] = offsets[num][:]
193         offsets[num][0] += bevel
194
195 ############################
196 #
197 # Simple material management.
198 # Return new, existing, or modified material reference.
199 #
200 # @todo: create additional materials based on diffuse options.
201 #
202
203
204 def uMatRGBSet(matName, RGBs, matMod=False, dShader='LAMBERT', dNtz=1.0):
205
206     if matName not in bpy.data.materials:
207         mtl = bpy.data.materials.new(matName)
208         matMod = True
209     else:
210         mtl = bpy.data.materials[matName]
211
212     if matMod:  # Set material values
213         mtl.diffuse_color = RGBs
214         mtl.diffuse_shader = dShader
215         mtl.diffuse_intensity = dNtz
216
217     return mtl
218
219 ################################################################################
220
221 ################################################################################
222 #
223 #  UI functions and object creation.
224 #
225
226
227 class add_castle(bpy.types.Operator):
228     bl_idname = "mesh.add_castle"
229     bl_label = OBJ_N
230     bl_description = OBJ_N
231     bl_options = {'REGISTER', 'UNDO'}
232
233     # only create object when True
234     # False allows modifying several parameters without creating object
235     ConstructTog: BoolProperty(name="Construct", description="Generate the object", default=True)
236
237     # Base area - set dimensions - Width (front/back) and Depth (sides),
238     # floor origin/offset, thickness, and, material/color.
239     cBaseW: FloatProperty(name="Width", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF, description="Base Width (X)")
240     cBaseD: FloatProperty(name="Depth", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF, description="Base Depth (Y)")
241     cBaseO: FloatProperty(name='Base', min=0, max=WALL_MAX, default=0, description="vert offset from 3D cursor.")
242     cBaseT: FloatProperty(min=BASE_TMIN, max=BASE_TMAX, default=BASE_TMIN, description="Base thickness")
243
244     cBaseRGB: FloatVectorProperty(min=0, max=1, default=(0.1, 0.1, 0.1), subtype='COLOR', size=3)
245
246     CBaseB: BoolProperty(name="BloX", default=False, description="Block flooring")
247     CBaseR: BoolProperty(default=False, description="Round flooring")
248
249     CLvls: IntProperty(name="Levels", min=1, max=LVL_MAX, default=1)
250
251     # current wall level parameter value display.
252     CLvl: IntProperty(name="Level", min=1, max=LVL_MAX, default=1)
253
254     #    curLvl=CLvl
255
256     # block sizing
257     blockX: FloatProperty(name="Width", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_XDEF)
258     blockZ: FloatProperty(name="Height", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_XDEF)
259     blockD: FloatProperty(name="Depth", min=BLOCK_MIN, max=BLOCK_MAX, default=BLOCK_DDEF)
260     # allow 0 for test cases...
261     #    blockD=FloatProperty(name="Depth",min=0,max=BLOCK_MAX,default=BLOCK_DDEF)
262
263     blockVar: BoolProperty(name="Vary", default=True, description="Randomize block sizing")
264
265     blockWVar: FloatProperty(name="Width", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Width")
266     blockHVar: FloatProperty(name="Height", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Height")
267     blockDVar: FloatProperty(name="Depth", min=0, max=BLOCK_VMAX, default=BLOCK_MIN, description="Random Depth")
268
269     blockMerge: BoolProperty(name="Merge", default=True, description="Merge closely adjoining blocks")
270
271     # @todo: fix edgeing for mis-matched row sizing (or just call it a feature).
272     EdgeOffset: FloatProperty(name="Edging", min=0, max=HALF_WALL, default=0.25, description="stagger wall ends")
273
274     # block spacing
275     Grout: FloatProperty(name="Gap", min=GAP_MIN, max=GAP_MAX, default=0.05, description="Block separation")
276
277     wallRGB: FloatVectorProperty(min=0, max=1, default=(0.5, 0.5, 0.5), subtype='COLOR', size=3)
278
279
280     # track height with each level...?
281     wallH: FloatProperty(name="Height", min=WALL_MIN, max=WALL_MAX, default=WALL_DEF)
282
283     # floor per level - LVL_MAX
284     # Base, always true (primary object/parent)
285     #    CFloor1=BoolProperty(default=True)
286     CFloor2: BoolProperty(default=False)
287     CFloor3: BoolProperty(default=False)
288     CFloor4: BoolProperty(default=False)
289     CFloor5: BoolProperty(default=False)
290     CFloor6: BoolProperty(default=False)
291     CFloor7: BoolProperty(default=False)
292     CFloor8: BoolProperty(default=False)
293     CFloor9: BoolProperty(default=False)
294     CFloor10: BoolProperty(default=False)
295
296     # Which walls to generate, per level - LVL_MAX
297     wallF1: BoolProperty(default=False, description="Front")
298     wallF2: BoolProperty(default=False)
299     wallF3: BoolProperty(default=False)
300     wallF4: BoolProperty(default=False)
301     wallF5: BoolProperty(default=False)
302     wallF6: BoolProperty(default=False)
303     wallF7: BoolProperty(default=False)
304     wallF8: BoolProperty(default=False)
305     wallF9: BoolProperty(default=False)
306     wallF10: BoolProperty(default=False)
307
308     wallL1: BoolProperty(default=False, description="Left")
309     wallL2: BoolProperty(default=False)
310     wallL3: BoolProperty(default=False)
311     wallL4: BoolProperty(default=False)
312     wallL5: BoolProperty(default=False)
313     wallL6: BoolProperty(default=False)
314     wallL7: BoolProperty(default=False)
315     wallL8: BoolProperty(default=False)
316     wallL9: BoolProperty(default=False)
317     wallL10: BoolProperty(default=False)
318
319     wallB1: BoolProperty(default=False, description="Back")
320     wallB2: BoolProperty(default=False)
321     wallB3: BoolProperty(default=False)
322     wallB4: BoolProperty(default=False)
323     wallB5: BoolProperty(default=False)
324     wallB6: BoolProperty(default=False)
325     wallB7: BoolProperty(default=False)
326     wallB8: BoolProperty(default=False)
327     wallB9: BoolProperty(default=False)
328     wallB10: BoolProperty(default=False)
329
330     wallR1: BoolProperty(default=False, description="Right")
331     wallR2: BoolProperty(default=False)
332     wallR3: BoolProperty(default=False)
333     wallR4: BoolProperty(default=False)
334     wallR5: BoolProperty(default=False)
335     wallR6: BoolProperty(default=False)
336     wallR7: BoolProperty(default=False)
337     wallR8: BoolProperty(default=False)
338     wallR9: BoolProperty(default=False)
339     wallR10: BoolProperty(default=False)
340
341     # round walls per level - LVL_MAX
342     wallFO1: BoolProperty(default=False, description="Front")
343     wallFO2: BoolProperty(default=False)
344     wallFO3: BoolProperty(default=False)
345     wallFO4: BoolProperty(default=False)
346     wallFO5: BoolProperty(default=False)
347     wallFO6: BoolProperty(default=False)
348     wallFO7: BoolProperty(default=False)
349     wallFO8: BoolProperty(default=False)
350     wallFO9: BoolProperty(default=False)
351     wallFO10: BoolProperty(default=False)
352
353     wallLO1: BoolProperty(default=False, description="Left")
354     wallLO2: BoolProperty(default=False)
355     wallLO3: BoolProperty(default=False)
356     wallLO4: BoolProperty(default=False)
357     wallLO5: BoolProperty(default=False)
358     wallLO6: BoolProperty(default=False)
359     wallLO7: BoolProperty(default=False)
360     wallLO8: BoolProperty(default=False)
361     wallLO9: BoolProperty(default=False)
362     wallLO10: BoolProperty(default=False)
363
364     wallBO1: BoolProperty(default=False, description="Back")
365     wallBO2: BoolProperty(default=False)
366     wallBO3: BoolProperty(default=False)
367     wallBO4: BoolProperty(default=False)
368     wallBO5: BoolProperty(default=False)
369     wallBO6: BoolProperty(default=False)
370     wallBO7: BoolProperty(default=False)
371     wallBO8: BoolProperty(default=False)
372     wallBO9: BoolProperty(default=False)
373     wallBO10: BoolProperty(default=False)
374
375     wallRO1: BoolProperty(default=False, description="Right")
376     wallRO2: BoolProperty(default=False)
377     wallRO3: BoolProperty(default=False)
378     wallRO4: BoolProperty(default=False)
379     wallRO5: BoolProperty(default=False)
380     wallRO6: BoolProperty(default=False)
381     wallRO7: BoolProperty(default=False)
382     wallRO8: BoolProperty(default=False)
383     wallRO9: BoolProperty(default=False)
384     wallRO10: BoolProperty(default=False)
385
386     #    CRoof=BoolProperty(name="Roof",default=False)
387
388     # Select wall modifier option to view/edit
389     wallOpView: EnumProperty(items=(
390         ("1", "Door", ""),
391         ("2", "Window", ""),
392         ("3", "Slot", ""),  # "classical arrow/rifle ports
393         ("4", "Crenel", ""),  # gaps along top of wall
394         ("5", "Steps", ""),
395         ("6", "Shelf", ""),
396     ),
397         name="", description="View/Edit wall modifiers", default="1")
398     # could add material selection for step and shelf...
399
400     # Radiating from one point - round (disc), with slope makes dome.
401     cDome: BoolProperty(name='Dome', default=False)
402     cDomeRGB: FloatVectorProperty(min=0, max=1, default=(0.3, 0.1, 0), subtype='COLOR', size=3)
403
404     cDomeH: FloatProperty(name='Height', min=WALL_MIN, max=WALL_MAX, default=WALL_DEF / 2)
405     cDomeZ: FloatProperty(name='Base', min=BASE_TMIN, max=WALL_MAX, default=WALL_DEF, description="offset from floor")
406     cDomeO: FloatProperty(name='Oculus', min=0, max=HALF_WALL, default=0, description="Dome opening.")
407
408     # holes/openings in wall - doors, windows or slots; affects block row size.
409     wallDoorW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=DEF_DOORW)
410     wallDoorH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=DEF_DOORH)
411     wallDoorX: FloatProperty(name="Indent", min=0, max=WALL_MAX, default=DEF_DOORX, description="horizontal offset from cursor")
412     doorBvl: FloatProperty(name="Bevel", min=-10, max=10, default=0.25, description="Angle block face")
413     doorRpt: BoolProperty(default=False, description="make multiple openings")
414     doorArch: BoolProperty(name="Arch", default=True)
415     doorArchC: FloatProperty(name="Curve", min=0.01, max=HALF_WALL, default=2.5, description="Arch curve height")
416     doorArchT: FloatProperty(name="Thickness", min=0.001, max=HALF_WALL, default=0.75)
417
418     wallDF1: BoolProperty(default=False, description="Front")
419     wallDF2: BoolProperty(default=False)
420     wallDF3: BoolProperty(default=False)
421     wallDF4: BoolProperty(default=False)
422     wallDF5: BoolProperty(default=False)
423     wallDF6: BoolProperty(default=False)
424     wallDF7: BoolProperty(default=False)
425     wallDF8: BoolProperty(default=False)
426     wallDF9: BoolProperty(default=False)
427     wallDF10: BoolProperty(default=False)
428
429     wallDL1: BoolProperty(default=False, description="Left")
430     wallDL2: BoolProperty(default=False)
431     wallDL3: BoolProperty(default=False)
432     wallDL4: BoolProperty(default=False)
433     wallDL5: BoolProperty(default=False)
434     wallDL6: BoolProperty(default=False)
435     wallDL7: BoolProperty(default=False)
436     wallDL8: BoolProperty(default=False)
437     wallDL9: BoolProperty(default=False)
438     wallDL10: BoolProperty(default=False)
439
440     wallDB1: BoolProperty(default=False, description="Back")
441     wallDB2: BoolProperty(default=False)
442     wallDB3: BoolProperty(default=False)
443     wallDB4: BoolProperty(default=False)
444     wallDB5: BoolProperty(default=False)
445     wallDB6: BoolProperty(default=False)
446     wallDB7: BoolProperty(default=False)
447     wallDB8: BoolProperty(default=False)
448     wallDB9: BoolProperty(default=False)
449     wallDB10: BoolProperty(default=False)
450
451     wallDR1: BoolProperty(default=False, description="Right")
452     wallDR2: BoolProperty(default=False)
453     wallDR3: BoolProperty(default=False)
454     wallDR4: BoolProperty(default=False)
455     wallDR5: BoolProperty(default=False)
456     wallDR6: BoolProperty(default=False)
457     wallDR7: BoolProperty(default=False)
458     wallDR8: BoolProperty(default=False)
459     wallDR9: BoolProperty(default=False)
460     wallDR10: BoolProperty(default=False)
461
462     # Slots, embrasure - classical slit for arrow/rifle ports.
463     # need to prevent overlap with arch openings - though inversion is an interesting effect.
464     SlotVW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=0.5)
465     SlotVH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=3.5)
466     SlotVL: FloatProperty(name="Indent", min=-WALL_MAX, max=WALL_MAX, default=0, description="The x position or spacing")
467     SlotVZ: FloatProperty(name="Bottom", min=-WALL_MAX, max=WALL_MAX, default=4.00)
468     slotVArchT: BoolProperty(name="Top", default=True)
469     slotVArchB: BoolProperty(name="Bottom", default=True)
470     SlotVRpt: BoolProperty(name="Repeat", default=False)
471
472     wallEF1: BoolProperty(default=False, description="Front")
473     wallEF2: BoolProperty(default=False)
474     wallEF3: BoolProperty(default=False)
475     wallEF4: BoolProperty(default=False)
476     wallEF5: BoolProperty(default=False)
477     wallEF6: BoolProperty(default=False)
478     wallEF7: BoolProperty(default=False)
479     wallEF8: BoolProperty(default=False)
480     wallEF9: BoolProperty(default=False)
481     wallEF10: BoolProperty(default=False)
482
483     wallEL1: BoolProperty(default=False, description="Left")
484     wallEL2: BoolProperty(default=False)
485     wallEL3: BoolProperty(default=False)
486     wallEL4: BoolProperty(default=False)
487     wallEL5: BoolProperty(default=False)
488     wallEL6: BoolProperty(default=False)
489     wallEL7: BoolProperty(default=False)
490     wallEL8: BoolProperty(default=False)
491     wallEL9: BoolProperty(default=False)
492     wallEL10: BoolProperty(default=False)
493
494     wallEB1: BoolProperty(default=False, description="Back")
495     wallEB2: BoolProperty(default=False)
496     wallEB3: BoolProperty(default=False)
497     wallEB4: BoolProperty(default=False)
498     wallEB5: BoolProperty(default=False)
499     wallEB6: BoolProperty(default=False)
500     wallEB7: BoolProperty(default=False)
501     wallEB8: BoolProperty(default=False)
502     wallEB9: BoolProperty(default=False)
503     wallEB10: BoolProperty(default=False)
504
505     wallER1: BoolProperty(default=False, description="Right")
506     wallER2: BoolProperty(default=False)
507     wallER3: BoolProperty(default=False)
508     wallER4: BoolProperty(default=False)
509     wallER5: BoolProperty(default=False)
510     wallER6: BoolProperty(default=False)
511     wallER7: BoolProperty(default=False)
512     wallER8: BoolProperty(default=False)
513     wallER9: BoolProperty(default=False)
514     wallER10: BoolProperty(default=False)
515
516     # window opening in wall.
517     wallPort: BoolProperty(default=False, description="Window opening")
518     wallPortW: FloatProperty(name="Width", min=BLOCK_MIN, max=HALF_WALL, default=2, description="Window (hole) width")
519     wallPortH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=3, description="Window (hole) height")
520     wallPortX: FloatProperty(name="Indent", min=-WALL_MAX, max=WALL_MAX, default=-2.5, description="The x position or spacing")
521     wallPortZ: FloatProperty(name="Bottom", min=-WALL_MAX, max=WALL_MAX, default=5)
522     wallPortBvl: FloatProperty(name="Bevel", min=-10, max=10, default=0.25, description="Angle block face")
523     wallPortArchT: BoolProperty(name="Top", default=False)
524     wallPortArchB: BoolProperty(name="Bottom", default=False)
525     wallPortRpt: BoolProperty(name="Repeat", default=False)
526
527     wallPF1: BoolProperty(default=False, description="Front")
528     wallPF2: BoolProperty(default=False)
529     wallPF3: BoolProperty(default=False)
530     wallPF4: BoolProperty(default=False)
531     wallPF5: BoolProperty(default=False)
532     wallPF6: BoolProperty(default=False)
533     wallPF7: BoolProperty(default=False)
534     wallPF8: BoolProperty(default=False)
535     wallPF9: BoolProperty(default=False)
536     wallPF10: BoolProperty(default=False)
537
538     wallPL1: BoolProperty(default=False, description="Left")
539     wallPL2: BoolProperty(default=False)
540     wallPL3: BoolProperty(default=False)
541     wallPL4: BoolProperty(default=False)
542     wallPL5: BoolProperty(default=False)
543     wallPL6: BoolProperty(default=False)
544     wallPL7: BoolProperty(default=False)
545     wallPL8: BoolProperty(default=False)
546     wallPL9: BoolProperty(default=False)
547     wallPL10: BoolProperty(default=False)
548
549     wallPB1: BoolProperty(default=False, description="Back")
550     wallPB2: BoolProperty(default=False)
551     wallPB3: BoolProperty(default=False)
552     wallPB4: BoolProperty(default=False)
553     wallPB5: BoolProperty(default=False)
554     wallPB6: BoolProperty(default=False)
555     wallPB7: BoolProperty(default=False)
556     wallPB8: BoolProperty(default=False)
557     wallPB9: BoolProperty(default=False)
558     wallPB10: BoolProperty(default=False)
559
560     wallPR1: BoolProperty(default=False, description="Right")
561     wallPR2: BoolProperty(default=False)
562     wallPR3: BoolProperty(default=False)
563     wallPR4: BoolProperty(default=False)
564     wallPR5: BoolProperty(default=False)
565     wallPR6: BoolProperty(default=False)
566     wallPR7: BoolProperty(default=False)
567     wallPR8: BoolProperty(default=False)
568     wallPR9: BoolProperty(default=False)
569     wallPR10: BoolProperty(default=False)
570
571     # Crenel = "gaps" on top of wall.
572     # review and determine min for % - should allow less...
573     CrenelXP: FloatProperty(name="Width %", min=0.10, max=1.0, default=0.15)
574     CrenelZP: FloatProperty(name="Height %", min=0.10, max=1.0, default=0.10)
575
576     wallCF1: BoolProperty(default=False, description="Front")
577     wallCF2: BoolProperty(default=False)
578     wallCF3: BoolProperty(default=False)
579     wallCF4: BoolProperty(default=False)
580     wallCF5: BoolProperty(default=False)
581     wallCF6: BoolProperty(default=False)
582     wallCF7: BoolProperty(default=False)
583     wallCF8: BoolProperty(default=False)
584     wallCF9: BoolProperty(default=False)
585     wallCF10: BoolProperty(default=False)
586
587     wallCL1: BoolProperty(default=False, description="Left")
588     wallCL2: BoolProperty(default=False)
589     wallCL3: BoolProperty(default=False)
590     wallCL4: BoolProperty(default=False)
591     wallCL5: BoolProperty(default=False)
592     wallCL6: BoolProperty(default=False)
593     wallCL7: BoolProperty(default=False)
594     wallCL8: BoolProperty(default=False)
595     wallCL9: BoolProperty(default=False)
596     wallCL10: BoolProperty(default=False)
597
598     wallCB1: BoolProperty(default=False, description="Back")
599     wallCB2: BoolProperty(default=False)
600     wallCB3: BoolProperty(default=False)
601     wallCB4: BoolProperty(default=False)
602     wallCB5: BoolProperty(default=False)
603     wallCB6: BoolProperty(default=False)
604     wallCB7: BoolProperty(default=False)
605     wallCB8: BoolProperty(default=False)
606     wallCB9: BoolProperty(default=False)
607     wallCB10: BoolProperty(default=False)
608
609     wallCR1: BoolProperty(default=False, description="Right")
610     wallCR2: BoolProperty(default=False)
611     wallCR3: BoolProperty(default=False)
612     wallCR4: BoolProperty(default=False)
613     wallCR5: BoolProperty(default=False)
614     wallCR6: BoolProperty(default=False)
615     wallCR7: BoolProperty(default=False)
616     wallCR8: BoolProperty(default=False)
617     wallCR9: BoolProperty(default=False)
618     wallCR10: BoolProperty(default=False)
619
620     # shelf location and size.
621     # see "balcony" options for improved capabilities.
622     # should limit x and z to wall boundaries
623     ShelfX: FloatProperty(name="XOff", min=-WALL_MAX, max=WALL_MAX, default=-(WALL_DEF / 2), description="x origin")
624     ShelfZ: FloatProperty(name="Base", min=-WALL_MAX, max=WALL_MAX, default=WALL_DEF, description="z origin")
625     ShelfW: FloatProperty(name="Width", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="The Width of shelf area")
626     # height seems to be double, check usage
627     ShelfH: FloatProperty(name="Height", min=BLOCK_MIN, max=HALF_WALL, default=0.5, description="The Height of Shelf area")
628     ShelfD: FloatProperty(name="Depth", min=BLOCK_MIN, max=WALL_MAX, default=BLOCK_DDEF, description="Depth of shelf")
629     ShelfOut: BoolProperty(name="Out", default=True, description="Position outside of wall")
630
631     wallBF1: BoolProperty(default=False, description="Front")
632     wallBF2: BoolProperty(default=False)
633     wallBF3: BoolProperty(default=False)
634     wallBF4: BoolProperty(default=False)
635     wallBF5: BoolProperty(default=False)
636     wallBF6: BoolProperty(default=False)
637     wallBF7: BoolProperty(default=False)
638     wallBF8: BoolProperty(default=False)
639     wallBF9: BoolProperty(default=False)
640     wallBF10: BoolProperty(default=False)
641
642     wallBL1: BoolProperty(default=False, description="Left")
643     wallBL2: BoolProperty(default=False)
644     wallBL3: BoolProperty(default=False)
645     wallBL4: BoolProperty(default=False)
646     wallBL5: BoolProperty(default=False)
647     wallBL6: BoolProperty(default=False)
648     wallBL7: BoolProperty(default=False)
649     wallBL8: BoolProperty(default=False)
650     wallBL9: BoolProperty(default=False)
651     wallBL10: BoolProperty(default=False)
652
653     wallBB1: BoolProperty(default=False, description="Back")
654     wallBB2: BoolProperty(default=False)
655     wallBB3: BoolProperty(default=False)
656     wallBB4: BoolProperty(default=False)
657     wallBB5: BoolProperty(default=False)
658     wallBB6: BoolProperty(default=False)
659     wallBB7: BoolProperty(default=False)
660     wallBB8: BoolProperty(default=False)
661     wallBB9: BoolProperty(default=False)
662     wallBB10: BoolProperty(default=False)
663
664     wallBR1: BoolProperty(default=False, description="Right")
665     wallBR2: BoolProperty(default=False)
666     wallBR3: BoolProperty(default=False)
667     wallBR4: BoolProperty(default=False)
668     wallBR5: BoolProperty(default=False)
669     wallBR6: BoolProperty(default=False)
670     wallBR7: BoolProperty(default=False)
671     wallBR8: BoolProperty(default=False)
672     wallBR9: BoolProperty(default=False)
673     wallBR10: BoolProperty(default=False)
674
675     # steps
676     StepX: FloatProperty(name="XOff", min=-WALL_MAX, max=WALL_MAX, default=-(WALL_DEF / 2), description="x origin")
677     StepZ: FloatProperty(name="Base", min=-WALL_MAX, max=WALL_MAX, default=0, description="z origin")
678     StepW: FloatProperty(name="Width", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="Width of step area")
679     StepH: FloatProperty(name="Height", min=BLOCK_MIN, max=WALL_MAX, default=WALL_DEF, description="Height of step area")
680     StepD: FloatProperty(name="Depth", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_XDEF, description="Step offset (distance) from wall")
681     StepV: FloatProperty(name="Riser", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_XDEF, description="Step height")
682     StepT: FloatProperty(name="Tread", min=BLOCK_MIN, max=HALF_WALL, default=BLOCK_DDEF, description="Step footing/tread")
683     StepL: BoolProperty(name="Slant", default=False, description="Step direction.")
684     StepF: BoolProperty(name="Fill", default=False, description="Fill with supporting blocks")
685     StepOut: BoolProperty(name="Out", default=False, description="Position outside of wall")
686
687     wallSF1: BoolProperty(default=False, description="Front")
688     wallSF2: BoolProperty(default=False)
689     wallSF3: BoolProperty(default=False)
690     wallSF4: BoolProperty(default=False)
691     wallSF5: BoolProperty(default=False)
692     wallSF6: BoolProperty(default=False)
693     wallSF7: BoolProperty(default=False)
694     wallSF8: BoolProperty(default=False)
695     wallSF9: BoolProperty(default=False)
696     wallSF10: BoolProperty(default=False)
697
698     wallSL1: BoolProperty(default=False, description="Left")
699     wallSL2: BoolProperty(default=False)
700     wallSL3: BoolProperty(default=False)
701     wallSL4: BoolProperty(default=False)
702     wallSL5: BoolProperty(default=False)
703     wallSL6: BoolProperty(default=False)
704     wallSL7: BoolProperty(default=False)
705     wallSL8: BoolProperty(default=False)
706     wallSL9: BoolProperty(default=False)
707     wallSL10: BoolProperty(default=False)
708
709     wallSB1: BoolProperty(default=False, description="Back")
710     wallSB2: BoolProperty(default=False)
711     wallSB3: BoolProperty(default=False)
712     wallSB4: BoolProperty(default=False)
713     wallSB5: BoolProperty(default=False)
714     wallSB6: BoolProperty(default=False)
715     wallSB7: BoolProperty(default=False)
716     wallSB8: BoolProperty(default=False)
717     wallSB9: BoolProperty(default=False)
718     wallSB10: BoolProperty(default=False)
719
720     wallSR1: BoolProperty(default=False, description="Right")
721     wallSR2: BoolProperty(default=False)
722     wallSR3: BoolProperty(default=False)
723     wallSR4: BoolProperty(default=False)
724     wallSR5: BoolProperty(default=False)
725     wallSR6: BoolProperty(default=False)
726     wallSR7: BoolProperty(default=False)
727     wallSR8: BoolProperty(default=False)
728     wallSR9: BoolProperty(default=False)
729     wallSR10: BoolProperty(default=False)
730
731     # Experimental options
732     # allow per wall and level?
733     cXTest: BoolProperty(default=False)
734     # wall shape modifiers
735     # make the wall circular - if not curved it's a flat disc
736     # Radiating from one point - round/disc; instead of square
737     #    wallDisc=BoolProperty(default=False,description="Make wall disc")
738     # Warp/slope/curve wall - abstract use only (except for dome use)
739     wallSlope: BoolProperty(default=False, description="Curve wall")  # top to bottom mid
740     CTunnel: BoolProperty(default=False, description="Tunnel wall")  # bottom to top mid
741     # rotate tunnel?...
742     CTower: BoolProperty(name='Tower', default=False)
743
744     ##
745     ####
746     ############################################################################
747     # Show the UI - present properties to user.
748     # Display the toolbox options
749     ############################################################################
750     ####
751     ##
752     def draw(self, context):
753
754         layout = self.layout
755
756         box = layout.box()
757         box.prop(self, 'ConstructTog')
758         # Castle area.
759         box = layout.box()
760         row = box.row()
761         row.prop(self, 'cBaseRGB', text='Base')
762         row = box.row()
763         row.prop(self, 'cBaseW')
764         row.prop(self, 'cBaseD')
765         # this is 0 until more complex settings allowed... like repeat or
766         # per-level (like wall) selection.
767         #        box.prop(self,'cBaseO') # vert offset from 3D cursor
768
769         box.prop(self, 'cBaseT', text="Thickness")  # height/thickness of floor.
770         row = box.row()
771         row.prop(self, 'CBaseB')  # Blocks or slab
772         if self.properties.CBaseB:
773             row.prop(self, 'CBaseR', text='', icon="MESH_CIRCLE")  # Round
774         box.prop(self, 'CLvls', text="Levels")  # number of floors.
775
776         # block sizing
777         box = layout.box()
778         row = box.row()
779         row.label(text='Blocks:')
780         box.prop(self, 'blockX')
781         box.prop(self, 'blockZ')
782         box.prop(self, 'blockD')
783         box.prop(self, 'Grout')
784         box.prop(self, 'EdgeOffset')
785
786         # "fixed" sizing unless variance enabled.
787         row = box.row()
788         row.prop(self, 'blockVar')  # randomize block sizing
789         if self.properties.blockVar:
790             row.prop(self, 'blockMerge')
791             box.prop(self, 'blockHVar')
792             box.prop(self, 'blockWVar')
793             box.prop(self, 'blockDVar')
794
795         box = layout.box()
796         box.prop(self, 'CLvl', text="Level")  # current "Level" (UI parameter values).
797
798         # Walls
799         row = box.row()
800         row.prop(self, 'wallRGB', text='Walls')
801         row = box.row()  # which walls to generate - show current level value.
802         row.prop(self, 'wallF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
803         row.prop(self, 'wallL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
804         row.prop(self, 'wallB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
805         row.prop(self, 'wallR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
806         row = box.row()  # round wall option
807         row.prop(self, 'wallFO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
808         row.prop(self, 'wallLO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
809         row.prop(self, 'wallBO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
810         row.prop(self, 'wallRO' + str(self.properties.CLvl), text='', icon="MESH_CIRCLE")
811         box.prop(self, 'wallH')
812         if self.properties.CLvl > 1:  # Base is first level (always true)
813             box.prop(self, 'CFloor' + str(self.properties.CLvl), text="Floor")  # floor for level.
814
815         box.prop(self, 'wallOpView')  # View/edit wall modifiers
816
817         # Door
818         if self.properties.wallOpView == "1":
819             #            box.prop(self,'doorRpt',text='Dupe')
820             row = box.row()  # which walls have a Door
821             row.prop(self, 'wallDF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
822             row.prop(self, 'wallDL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
823             row.prop(self, 'wallDB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
824             row.prop(self, 'wallDR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
825             box.prop(self, 'wallDoorW')
826             box.prop(self, 'wallDoorH')
827             box.prop(self, 'wallDoorX')
828             box.prop(self, 'doorBvl')
829
830             box.prop(self, 'doorArch')
831             if self.doorArch:
832                 box.prop(self, 'doorArchC')
833                 box.prop(self, 'doorArchT')
834
835         # Window
836         if self.properties.wallOpView == "2":
837             #            box.prop(self,'wallPortRpt',text='Dupe')
838             row = box.row()  # which walls have portal/window
839             row.prop(self, 'wallPF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
840             row.prop(self, 'wallPL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
841             row.prop(self, 'wallPB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
842             row.prop(self, 'wallPR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
843             row = box.row()
844             row.prop(self, 'wallPortW')
845             row.prop(self, 'wallPortH')
846             box.prop(self, 'wallPortX')
847             box.prop(self, 'wallPortZ')
848             box.prop(self, 'wallPortBvl')
849             box.label(text='Arch')
850             row = box.row()
851             row.prop(self, 'wallPortArchT')
852             row.prop(self, 'wallPortArchB')
853
854         # Slots
855         if self.properties.wallOpView == "3":
856             box.prop(self, 'SlotVRpt', text='Dupe')
857             row = box.row()  # which walls have embrasures/slits
858             row.prop(self, 'wallEF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
859             row.prop(self, 'wallEL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
860             row.prop(self, 'wallEB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
861             row.prop(self, 'wallER' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
862             box.prop(self, 'SlotVW')
863             box.prop(self, 'SlotVH')
864             box.prop(self, 'SlotVL')
865             box.prop(self, 'SlotVZ')
866             box.label(text='Arch')
867             row = box.row()
868             row.prop(self, 'slotVArchT')
869             row.prop(self, 'slotVArchB')
870
871         # Crenellation: gaps in top of wall, base of dome.
872         if self.properties.wallOpView == "4":
873             row = box.row()  # which walls have Crenellation
874             row.prop(self, 'wallCF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
875             row.prop(self, 'wallCL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
876             row.prop(self, 'wallCB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
877             row.prop(self, 'wallCR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
878             box.prop(self, 'CrenelXP')
879             box.prop(self, 'CrenelZP')
880
881         # Steps
882         if self.properties.wallOpView == "5":
883             row = box.row()
884             row.prop(self, 'StepL')
885             row.prop(self, 'StepF')
886             row.prop(self, 'StepOut')
887             row = box.row()  # which walls have steps
888             row.prop(self, 'wallSF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
889             row.prop(self, 'wallSL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
890             row.prop(self, 'wallSB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
891             row.prop(self, 'wallSR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
892             box.prop(self, 'StepX')
893             box.prop(self, 'StepZ')
894             box.prop(self, 'StepW')
895             box.prop(self, 'StepH')
896             box.prop(self, 'StepD')
897             box.prop(self, 'StepV')
898             box.prop(self, 'StepT')
899
900         # Shelf/Balcony
901         if self.properties.wallOpView == "6":
902             box.prop(self, 'ShelfOut')
903             row = box.row()  # which walls have a shelf
904             row.prop(self, 'wallBF' + str(self.properties.CLvl), text='', icon="TRIA_DOWN_BAR")
905             row.prop(self, 'wallBL' + str(self.properties.CLvl), text='', icon="TRIA_LEFT_BAR")
906             row.prop(self, 'wallBB' + str(self.properties.CLvl), text='', icon="TRIA_UP_BAR")
907             row.prop(self, 'wallBR' + str(self.properties.CLvl), text='', icon="TRIA_RIGHT_BAR")
908             box.prop(self, 'ShelfX')
909             box.prop(self, 'ShelfZ')
910             box.prop(self, 'ShelfW')
911             box.prop(self, 'ShelfH')
912             box.prop(self, 'ShelfD')
913
914         # Dome
915         box = layout.box()
916         row = box.row()
917         row.prop(self, 'cDome')
918         if self.properties.cDome:
919             row.prop(self, 'cDomeRGB', text='')
920             box.prop(self, 'cDomeH')
921             box.prop(self, 'cDomeZ')
922         # Oculus - opening in top of dome
923         #            box.prop(self,'cDomeO')
924
925         # abstract/experimental
926         # Use "icon="MESH_CONE" for roof
927         box = layout.box()
928         row = box.row()
929         row.prop(self, 'cXTest', text='Experimental')
930         if self.properties.cXTest:
931             box.prop(self, 'wallSlope', text='Curve')
932             box.prop(self, 'CTunnel', text='Tunnel', icon="CURVE_DATA")
933             box.prop(self, 'CTower', text='Tower', icon="CURVE_DATA")
934
935     ##
936     ####
937     ############################################################################
938     # Respond to UI - process the properties set by user.
939         # Check and process UI settings to generate castle.
940     ############################################################################
941     ####
942     ##
943
944     def execute(self, context):
945
946         global openingSpecs
947         global dims
948         # Limit UI settings relative to other parameters,
949         #  set globals and effeciency variables.
950
951         # current level cannot exceed level setting...
952         if self.properties.CLvl > self.properties.CLvls:
953             self.properties.CLvl = self.properties.CLvls
954
955         castleScene = bpy.context.scene
956         blockWidth = self.properties.blockX
957         blockHeight = self.properties.blockZ
958         blockDepth = self.properties.blockD
959
960         # wall "area" must be at least 3 blocks.
961         minWallW = blockWidth * 3  # min wall width/depth
962         minWallH = blockHeight * 3  # min wall height
963
964         if self.properties.cBaseW < minWallW:
965             self.properties.cBaseW = minWallW
966         CastleX = self.properties.cBaseW
967         midWallW = CastleX / 2
968
969         if self.properties.wallH < minWallH:
970             self.properties.wallH = minWallH
971         planeHL = self.properties.wallH
972         planeHL2 = planeHL * 2
973         # proportional crenel height, shared with roof positioning.
974         crenelH = planeHL * self.properties.CrenelZP
975
976         if self.properties.cBaseD < minWallW:
977             self.properties.cBaseD = minWallW
978         CastleD = self.properties.cBaseD
979         midWallD = (CastleD + blockDepth) / 2
980         #        midWallD=CastleD/2
981         #        midWallD=(CastleD-blockDepth)/2
982
983         # gap cannot reduce block below minimum.
984         if self.properties.Grout > blockHeight / 2 - BLOCK_MIN:
985             self.properties.Grout = blockHeight / 2 - BLOCK_MIN
986
987         # Variance limit for minimum block height.
988         # not quite right, but usuable.
989         blockHMin = self.properties.Grout + BLOCK_MIN
990
991         # floor "thickness" cannot exceed block height
992         if self.properties.cBaseT > blockHeight:
993             self.properties.cBaseT = blockHeight
994
995         ############
996         #
997         if self.properties.cDome:
998             # base can't be lower than floor
999             # consider limiting base to roof or wall height by levels?
1000             domeLimit = self.properties.cBaseO + self.properties.cBaseT
1001             if self.properties.cDomeZ < domeLimit:
1002                 self.properties.cDomeZ = domeLimit
1003
1004             # base can't be higher than double wall height (per level)
1005             domeLimit = planeHL2 * self.properties.CLvls
1006             if self.properties.cDomeZ > domeLimit:
1007                 self.properties.cDomeZ = domeLimit
1008
1009             # height limit to twice smallest wall width/depth.
1010             if CastleD < CastleX:  # use least dimension for dome.
1011                 domeLimit = CastleD * 2
1012             else:
1013                 domeLimit = CastleX * 2
1014
1015             if self.properties.cDomeH > domeLimit:
1016                 self.properties.cDomeH = domeLimit
1017             domeMaxO = self.properties.cDomeH / 2
1018
1019             # Dome Oculus (opening) can't be more than half dome height
1020             if self.properties.cDomeO > domeMaxO:
1021                 self.properties.cDomeO = domeMaxO
1022
1023         #
1024         ############
1025         #####
1026         #
1027         # After validating all the properties see if need to create...
1028         # @todo: fix so last object preserved... (i.e. changing levels).
1029         # No build if construct off
1030         if not self.properties.ConstructTog:
1031             # No build if construct off or just changing levels
1032             #        if not self.properties.ConstructTog or self.properties.curLvl!=self.properties.CLvl:
1033             #            self.properties.curLvl=self.properties.CLvl
1034             return {'FINISHED'}
1035
1036         #
1037         #####
1038         ############
1039         #
1040         # rowprocessing() front/back wall, modified by sides and Dome.
1041         # this centers the wall, maybe add flag to center or offset from cursor...
1042         # if centerwall:
1043         dims['s'] = -midWallW
1044         dims['e'] = midWallW
1045         # else:
1046         #        dims['s'] = 0
1047         #        dims['e'] = CastleX
1048
1049         dims['t'] = planeHL
1050
1051         # block sizing
1052         settings['h'] = blockHeight
1053         settings['w'] = blockWidth
1054         settings['d'] = blockDepth
1055
1056         if self.properties.blockVar:
1057             settings['hv'] = self.properties.blockHVar
1058             settings['wv'] = self.properties.blockWVar
1059             settings['dv'] = self.properties.blockDVar
1060         else:
1061             settings['hv'] = 0
1062             settings['wv'] = 0
1063             settings['dv'] = 0
1064
1065         settings['sdv'] = blockWidth  # divisions in area.
1066
1067         settings['g'] = self.properties.Grout
1068
1069         settings['eoff'] = self.properties.EdgeOffset
1070
1071         # when openings overlap they create inverse stonework - interesting but not the desired effect
1072         # if opening width == indent*2 the edge blocks fail (row of blocks cross opening) - bug.
1073         openingSpecs = []
1074         archTop = [0, 0]
1075         archBot = [0, 0]
1076
1077         #
1078         ############
1079         #
1080         # Openings always set info; flag per wall, per level to enable.
1081         #
1082         ############
1083         #
1084         ############
1085         # Door
1086             # set locals
1087         # @todo: fix this....
1088         openZ = self.properties.cBaseT + 1  # track with floor - else 0
1089
1090         if self.properties.doorArch:
1091             archTop[0] = self.properties.doorArchC
1092             archTop[1] = self.properties.doorArchT
1093
1094         # set opening values
1095         openingSpecs += [{'a': False,
1096                           'w': self.properties.wallDoorW, 'h': self.properties.wallDoorH,
1097                           'x': self.properties.wallDoorX, 'z': openZ,
1098                           'n': self.properties.doorRpt, 'bvl': self.properties.doorBvl,
1099                           'v': archTop[0], 'vl':archBot[0], 't':archTop[1], 'tl':archBot[1]}]
1100
1101         ############
1102         # Window
1103         archTop[0] = 0
1104         archTop[1] = 0
1105         archBot[0] = 0
1106         archBot[1] = 0
1107
1108         if self.properties.wallPortArchT:
1109             archTop[0] = self.properties.wallPortW / 2
1110             archTop[1] = self.properties.wallPortW / 4
1111
1112         if self.properties.wallPortArchB:
1113             archBot[0] = self.properties.wallPortW / 2
1114             archBot[1] = self.properties.wallPortW / 4
1115
1116         # set opening values
1117         openingSpecs += [{'a': False,
1118                           'w': self.properties.wallPortW, 'h': self.properties.wallPortH,
1119                           'x': self.properties.wallPortX, 'z': self.properties.wallPortZ,
1120                           'n': self.properties.wallPortRpt, 'bvl': self.properties.wallPortBvl,
1121                           'v': archTop[0], 'vl':archBot[0], 't':archTop[1], 'tl':archBot[1]}]
1122
1123         ############
1124         # crenellation, top wall gaps...
1125         # if crenel opening overlaps with arch opening it fills with blocks...
1126         archTop[0] = 0
1127         archTop[1] = 0
1128         # add bottom arch option?
1129         archBot[0] = 0
1130         archBot[1] = 0
1131
1132         crenelW = CastleX * self.properties.CrenelXP  # Width % opening.
1133         crenelz = planeHL - (crenelH / 2)  # set bottom of opening
1134
1135         crenelSpace = crenelW * 2  # assume standard spacing
1136         crenelRpt = True
1137         # set indent 0 (center) if opening is 50% or more of wall width, no repeat.
1138         if crenelSpace >= CastleX:
1139             crenelSpace = 0
1140             crenelRpt = False
1141
1142         # set opening values
1143         openingSpecs += [{'a': False,
1144                           'w': crenelW, 'h': crenelH,
1145                           'x': crenelSpace, 'z': crenelz,
1146                           'n': crenelRpt, 'bvl': 0,
1147                           'v': archTop[0], 'vl':archBot[0], 't':archTop[1], 'tl':archBot[1]}]
1148
1149         ############
1150         # Slots
1151         archTop[0] = 0
1152         archTop[1] = 0
1153         archBot[0] = 0
1154         archBot[1] = 0
1155
1156         if self.properties.slotVArchT:
1157             archTop[0] = self.properties.SlotVW
1158             archTop[1] = self.properties.SlotVW / 2
1159         if self.properties.slotVArchB:
1160             archBot[0] = self.properties.SlotVW
1161             archBot[1] = self.properties.SlotVW / 2
1162
1163         # set opening values
1164         openingSpecs += [{'a': False,
1165                           'w': self.properties.SlotVW, 'h': self.properties.SlotVH,
1166                           'x': self.properties.SlotVL, 'z': self.properties.SlotVZ,
1167                           'n': self.properties.SlotVRpt, 'bvl': 0,
1168                           'v': archTop[0], 'vl':archBot[0], 't':archTop[1], 'tl':archBot[1]}]
1169
1170         ##
1171         ####
1172         ########################################################################
1173         # Process the user settings to generate castle.
1174         ########################################################################
1175         ####
1176         ##
1177         # Deselect all objects.
1178         bpy.ops.object.select_all(action='DESELECT')
1179
1180         ############
1181         # Base/floor/foundation, main object for Castle, parent to all other elements.
1182         wallExtOpts = [False, False]  # no steps or shelf
1183         # @todo: offset door by floor height/thickness
1184         # @todo: replicate for "multi-floor" option-shared with roof.
1185         #  - will need openings (in plane) for stairs and such...
1186         settings['Slope'] = False  # no curve
1187         settings['eoff'] = 0  # no edgeing
1188         # need separate flag for floor/roof...
1189         settings['Radial'] = False
1190
1191         baseMtl = uMatRGBSet('cBase_mat', self.cBaseRGB, matMod=True)
1192
1193         baseDisc = False  # only when blocks and round...
1194         if self.properties.CBaseB:  # floor blocks
1195             baseDisc = self.properties.CBaseR  # rounded
1196
1197         # set block "area": height, width, depth, and spacing for floor
1198         # - when rotated or disc shape, height is depth, depth is thickness.
1199         blockArea = [self.properties.cBaseD, self.properties.cBaseW, self.properties.cBaseT, self.properties.blockZ, settings['hv'], self.properties.Grout]
1200
1201         # Block floor uses wall to generate... initialize location values.
1202         wallLoc = [castleScene.cursor_location.x, castleScene.cursor_location.y, castleScene.cursor_location.z]
1203
1204         baseRotate = False  # rotate for blocks...
1205         if self.properties.CBaseB:  # make floor with blocks.
1206             saveBD = settings['d']  # put back when done with floor...
1207             saveBDV = settings['dv']
1208             settings['d'] = self.properties.cBaseT
1209             settings['dv'] = 0
1210
1211             if baseDisc:  # make base disc shaped
1212                 settings['Radial'] = True
1213                 if self.properties.cBaseD < self.properties.cBaseW:  # narrowest extent
1214                     blockArea[0] = self.properties.cBaseD / 2
1215                 else:
1216                     blockArea[0] = self.properties.cBaseW / 2
1217
1218                 wallLoc[1] += self.properties.cBaseW / 2  # adjust location for radius
1219
1220             else:  # rotate if not disc.
1221                 baseRotate = True
1222
1223             castleObj = makeWallObj(self, castleScene, wallLoc, OBJ_N, blockArea, [], wallExtOpts, baseMtl)
1224
1225             if baseRotate:
1226                 castleObj.select_set(True)  # must select to rotate
1227                 # rotate 90 backward
1228                 bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[True, False, False])
1229                 bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
1230                 castleObj.select_set(False)  # deselect after rotate else joined with others...
1231
1232             settings['d'] = saveBD  # restore block values
1233             settings['dv'] = saveBDV
1234
1235         else:  # make solid plane
1236             # set plane dimensions and location
1237             baseZ = self.properties.cBaseO
1238             baseT = baseZ + self.properties.cBaseT
1239             baseW = self.properties.cBaseW
1240             baseXO = -baseW / 2  # center
1241             baseXE = baseW / 2
1242
1243             baseBounds = [baseXO, baseXE, baseZ, baseT, 0, self.properties.cBaseD]
1244             castleObj = makePlaneObj(OBJ_N, baseBounds, baseMtl, baseW)
1245             castleScene.objects.link(castleObj)  # must do for generation/rotation
1246
1247
1248         ####################
1249         # Make Floor and Walls
1250         wallMtl = uMatRGBSet('cWall_mat', self.wallRGB, matMod=True)
1251
1252         # Block floor uses wall to generate... reset location values.
1253         wallLoc[1] = castleScene.cursor_location.y
1254
1255         wallLvl = 0  # make per level as selected...
1256         while wallLvl < self.properties.CLvls:  # make castle levels
1257             if wallLvl == 0:
1258                 floorLvl = False  # First floor is Castle base (parent).
1259                 wallFLvl = self.properties.wallF1
1260                 wallLLvl = self.properties.wallL1
1261                 wallBLvl = self.properties.wallB1
1262                 wallRLvl = self.properties.wallR1
1263                 wallFOLvl = self.properties.wallFO1
1264                 wallLOLvl = self.properties.wallLO1
1265                 wallBOLvl = self.properties.wallBO1
1266                 wallROLvl = self.properties.wallRO1
1267                 wallDLvlF = self.properties.wallDF1
1268                 wallDLvlL = self.properties.wallDL1
1269                 wallDLvlB = self.properties.wallDB1
1270                 wallDLvlR = self.properties.wallDR1
1271                 wallPLvlF = self.properties.wallPF1
1272                 wallPLvlL = self.properties.wallPL1
1273                 wallPLvlB = self.properties.wallPB1
1274                 wallPLvlR = self.properties.wallPR1
1275                 wallELvlF = self.properties.wallEF1
1276                 wallELvlL = self.properties.wallEL1
1277                 wallELvlB = self.properties.wallEB1
1278                 wallELvlR = self.properties.wallER1
1279                 wallCLvlF = self.properties.wallCF1
1280                 wallCLvlL = self.properties.wallCL1
1281                 wallCLvlB = self.properties.wallCB1
1282                 wallCLvlR = self.properties.wallCR1
1283                 wallSLvlF = self.properties.wallSF1
1284                 wallSLvlL = self.properties.wallSL1
1285                 wallSLvlB = self.properties.wallSB1
1286                 wallSLvlR = self.properties.wallSR1
1287                 wallBLvlF = self.properties.wallBF1
1288                 wallBLvlL = self.properties.wallBL1
1289                 wallBLvlB = self.properties.wallBB1
1290                 wallBLvlR = self.properties.wallBR1
1291
1292             if wallLvl == 1:
1293                 floorLvl = self.properties.CFloor2
1294                 wallFLvl = self.properties.wallF2
1295                 wallLLvl = self.properties.wallL2
1296                 wallBLvl = self.properties.wallB2
1297                 wallRLvl = self.properties.wallR2
1298                 wallFOLvl = self.properties.wallFO2
1299                 wallLOLvl = self.properties.wallLO2
1300                 wallBOLvl = self.properties.wallBO2
1301                 wallROLvl = self.properties.wallRO2
1302                 wallDLvlF = self.properties.wallDF2
1303                 wallDLvlL = self.properties.wallDL2
1304                 wallDLvlB = self.properties.wallDB2
1305                 wallDLvlR = self.properties.wallDR2
1306                 wallPLvlF = self.properties.wallPF2
1307                 wallPLvlL = self.properties.wallPL2
1308                 wallPLvlB = self.properties.wallPB2
1309                 wallPLvlR = self.properties.wallPR2
1310                 wallELvlF = self.properties.wallEF2
1311                 wallELvlL = self.properties.wallEL2
1312                 wallELvlB = self.properties.wallEB2
1313                 wallELvlR = self.properties.wallER2
1314                 wallCLvlF = self.properties.wallCF2
1315                 wallCLvlL = self.properties.wallCL2
1316                 wallCLvlB = self.properties.wallCB2
1317                 wallCLvlR = self.properties.wallCR2
1318                 wallSLvlF = self.properties.wallSF2
1319                 wallSLvlL = self.properties.wallSL2
1320                 wallSLvlB = self.properties.wallSB2
1321                 wallSLvlR = self.properties.wallSR2
1322                 wallBLvlF = self.properties.wallBF2
1323                 wallBLvlL = self.properties.wallBL2
1324                 wallBLvlB = self.properties.wallBB2
1325                 wallBLvlR = self.properties.wallBR2
1326
1327             if wallLvl == 2:
1328                 floorLvl = self.properties.CFloor3
1329                 wallFLvl = self.properties.wallF3
1330                 wallLLvl = self.properties.wallL3
1331                 wallBLvl = self.properties.wallB3
1332                 wallRLvl = self.properties.wallR3
1333                 wallFOLvl = self.properties.wallFO3
1334                 wallLOLvl = self.properties.wallLO3
1335                 wallBOLvl = self.properties.wallBO3
1336                 wallROLvl = self.properties.wallRO3
1337                 wallDLvlF = self.properties.wallDF3
1338                 wallDLvlL = self.properties.wallDL3
1339                 wallDLvlB = self.properties.wallDB3
1340                 wallDLvlR = self.properties.wallDR3
1341                 wallPLvlF = self.properties.wallPF3
1342                 wallPLvlL = self.properties.wallPL3
1343                 wallPLvlB = self.properties.wallPB3
1344                 wallPLvlR = self.properties.wallPR3
1345                 wallELvlF = self.properties.wallEF3
1346                 wallELvlL = self.properties.wallEL3
1347                 wallELvlB = self.properties.wallEB3
1348                 wallELvlR = self.properties.wallER3
1349                 wallCLvlF = self.properties.wallCF3
1350                 wallCLvlL = self.properties.wallCL3
1351                 wallCLvlB = self.properties.wallCB3
1352                 wallCLvlR = self.properties.wallCR3
1353                 wallSLvlF = self.properties.wallSF3
1354                 wallSLvlL = self.properties.wallSL3
1355                 wallSLvlB = self.properties.wallSB3
1356                 wallSLvlR = self.properties.wallSR3
1357                 wallBLvlF = self.properties.wallBF3
1358                 wallBLvlL = self.properties.wallBL3
1359                 wallBLvlB = self.properties.wallBB3
1360                 wallBLvlR = self.properties.wallBR3
1361
1362             if wallLvl == 3:
1363                 floorLvl = self.properties.CFloor4
1364                 wallFLvl = self.properties.wallF4
1365                 wallLLvl = self.properties.wallL4
1366                 wallBLvl = self.properties.wallB4
1367                 wallRLvl = self.properties.wallR4
1368                 wallFOLvl = self.properties.wallFO4
1369                 wallLOLvl = self.properties.wallLO4
1370                 wallBOLvl = self.properties.wallBO4
1371                 wallROLvl = self.properties.wallRO4
1372                 wallDLvlF = self.properties.wallDF4
1373                 wallDLvlL = self.properties.wallDL4
1374                 wallDLvlB = self.properties.wallDB4
1375                 wallDLvlR = self.properties.wallDR4
1376                 wallPLvlF = self.properties.wallPF4
1377                 wallPLvlL = self.properties.wallPL4
1378                 wallPLvlB = self.properties.wallPB4
1379                 wallPLvlR = self.properties.wallPR4
1380                 wallELvlF = self.properties.wallEF4
1381                 wallELvlL = self.properties.wallEL4
1382                 wallELvlB = self.properties.wallEB4
1383                 wallELvlR = self.properties.wallER4
1384                 wallCLvlF = self.properties.wallCF4
1385                 wallCLvlL = self.properties.wallCL4
1386                 wallCLvlB = self.properties.wallCB4
1387                 wallCLvlR = self.properties.wallCR4
1388                 wallSLvlF = self.properties.wallSF4
1389                 wallSLvlL = self.properties.wallSL4
1390                 wallSLvlB = self.properties.wallSB4
1391                 wallSLvlR = self.properties.wallSR4
1392                 wallBLvlF = self.properties.wallBF4
1393                 wallBLvlL = self.properties.wallBL4
1394                 wallBLvlB = self.properties.wallBB4
1395                 wallBLvlR = self.properties.wallBR4
1396
1397             if wallLvl == 4:
1398                 floorLvl = self.properties.CFloor5
1399                 wallFLvl = self.properties.wallF5
1400                 wallLLvl = self.properties.wallL5
1401                 wallBLvl = self.properties.wallB5
1402                 wallRLvl = self.properties.wallR5
1403                 wallFOLvl = self.properties.wallFO5
1404                 wallLOLvl = self.properties.wallLO5
1405                 wallBOLvl = self.properties.wallBO5
1406                 wallROLvl = self.properties.wallRO5
1407                 wallDLvlF = self.properties.wallDF5
1408                 wallDLvlL = self.properties.wallDL5
1409                 wallDLvlB = self.properties.wallDB5
1410                 wallDLvlR = self.properties.wallDR5
1411                 wallPLvlF = self.properties.wallPF5
1412                 wallPLvlL = self.properties.wallPL5
1413                 wallPLvlB = self.properties.wallPB5
1414                 wallPLvlR = self.properties.wallPR5
1415                 wallELvlF = self.properties.wallEF5
1416                 wallELvlL = self.properties.wallEL5
1417                 wallELvlB = self.properties.wallEB5
1418                 wallELvlR = self.properties.wallER5
1419                 wallCLvlF = self.properties.wallCF5
1420                 wallCLvlL = self.properties.wallCL5
1421                 wallCLvlB = self.properties.wallCB5
1422                 wallCLvlR = self.properties.wallCR5
1423                 wallSLvlF = self.properties.wallSF5
1424                 wallSLvlL = self.properties.wallSL5
1425                 wallSLvlB = self.properties.wallSB5
1426                 wallSLvlR = self.properties.wallSR5
1427                 wallBLvlF = self.properties.wallBF5
1428                 wallBLvlL = self.properties.wallBL5
1429                 wallBLvlB = self.properties.wallBB5
1430                 wallBLvlR = self.properties.wallBR5
1431
1432             if wallLvl == 5:
1433                 floorLvl = self.properties.CFloor6
1434                 wallFLvl = self.properties.wallF6
1435                 wallLLvl = self.properties.wallL6
1436                 wallBLvl = self.properties.wallB6
1437                 wallRLvl = self.properties.wallR6
1438                 wallFOLvl = self.properties.wallFO6
1439                 wallLOLvl = self.properties.wallLO6
1440                 wallBOLvl = self.properties.wallBO6
1441                 wallROLvl = self.properties.wallRO6
1442                 wallDLvlF = self.properties.wallDF6
1443                 wallDLvlL = self.properties.wallDL6
1444                 wallDLvlB = self.properties.wallDB6
1445                 wallDLvlR = self.properties.wallDR6
1446                 wallPLvlF = self.properties.wallPF6
1447                 wallPLvlL = self.properties.wallPL6
1448                 wallPLvlB = self.properties.wallPB6
1449                 wallPLvlR = self.properties.wallPR6
1450                 wallELvlF = self.properties.wallEF6
1451                 wallELvlL = self.properties.wallEL6
1452                 wallELvlB = self.properties.wallEB6
1453                 wallELvlR = self.properties.wallER6
1454                 wallCLvlF = self.properties.wallCF6
1455                 wallCLvlL = self.properties.wallCL6
1456                 wallCLvlB = self.properties.wallCB6
1457                 wallCLvlR = self.properties.wallCR6
1458                 wallSLvlF = self.properties.wallSF6
1459                 wallSLvlL = self.properties.wallSL6
1460                 wallSLvlB = self.properties.wallSB6
1461                 wallSLvlR = self.properties.wallSR6
1462                 wallBLvlF = self.properties.wallBF6
1463                 wallBLvlL = self.properties.wallBL6
1464                 wallBLvlB = self.properties.wallBB6
1465                 wallBLvlR = self.properties.wallBR6
1466
1467             if wallLvl == 6:
1468                 floorLvl = self.properties.CFloor7
1469                 wallFLvl = self.properties.wallF7
1470                 wallLLvl = self.properties.wallL7
1471                 wallBLvl = self.properties.wallB7
1472                 wallRLvl = self.properties.wallR7
1473                 wallFOLvl = self.properties.wallFO7
1474                 wallLOLvl = self.properties.wallLO7
1475                 wallBOLvl = self.properties.wallBO7
1476                 wallROLvl = self.properties.wallRO7
1477                 wallDLvlF = self.properties.wallDF7
1478                 wallDLvlL = self.properties.wallDL7
1479                 wallDLvlB = self.properties.wallDB7
1480                 wallDLvlR = self.properties.wallDR7
1481                 wallPLvlF = self.properties.wallPF7
1482                 wallPLvlL = self.properties.wallPL7
1483                 wallPLvlB = self.properties.wallPB7
1484                 wallPLvlR = self.properties.wallPR7
1485                 wallELvlF = self.properties.wallEF7
1486                 wallELvlL = self.properties.wallEL7
1487                 wallELvlB = self.properties.wallEB7
1488                 wallELvlR = self.properties.wallER7
1489                 wallCLvlF = self.properties.wallCF7
1490                 wallCLvlL = self.properties.wallCL7
1491                 wallCLvlB = self.properties.wallCB7
1492                 wallCLvlR = self.properties.wallCR7
1493                 wallSLvlF = self.properties.wallSF7
1494                 wallSLvlL = self.properties.wallSL7
1495                 wallSLvlB = self.properties.wallSB7
1496                 wallSLvlR = self.properties.wallSR7
1497                 wallBLvlF = self.properties.wallBF7
1498                 wallBLvlL = self.properties.wallBL7
1499                 wallBLvlB = self.properties.wallBB7
1500                 wallBLvlR = self.properties.wallBR7
1501
1502             if wallLvl == 7:
1503                 floorLvl = self.properties.CFloor38
1504                 wallFLvl = self.properties.wallF8
1505                 wallLLvl = self.properties.wallL8
1506                 wallBLvl = self.properties.wallB8
1507                 wallRLvl = self.properties.wallR8
1508                 wallFOLvl = self.properties.wallFO8
1509                 wallLOLvl = self.properties.wallLO8
1510                 wallBOLvl = self.properties.wallBO8
1511                 wallROLvl = self.properties.wallRO8
1512                 wallDLvlF = self.properties.wallDF8
1513                 wallDLvlL = self.properties.wallDL8
1514                 wallDLvlB = self.properties.wallDB8
1515                 wallDLvlR = self.properties.wallDR8
1516                 wallPLvlF = self.properties.wallPF8
1517                 wallPLvlL = self.properties.wallPL8
1518                 wallPLvlB = self.properties.wallPB8
1519                 wallPLvlR = self.properties.wallPR8
1520                 wallELvlF = self.properties.wallEF8
1521                 wallELvlL = self.properties.wallEL8
1522                 wallELvlB = self.properties.wallEB8
1523                 wallELvlR = self.properties.wallER8
1524                 wallCLvlF = self.properties.wallCF8
1525                 wallCLvlL = self.properties.wallCL8
1526                 wallCLvlB = self.properties.wallCB8
1527                 wallCLvlR = self.properties.wallCR8
1528                 wallSLvlF = self.properties.wallSF8
1529                 wallSLvlL = self.properties.wallSL8
1530                 wallSLvlB = self.properties.wallSB8
1531                 wallSLvlR = self.properties.wallSR8
1532                 wallBLvlF = self.properties.wallBF8
1533                 wallBLvlL = self.properties.wallBL8
1534                 wallBLvlB = self.properties.wallBB8
1535                 wallBLvlR = self.properties.wallBR8
1536
1537             if wallLvl == 8:
1538                 floorLvl = self.properties.CFloor9
1539                 wallFLvl = self.properties.wallF9
1540                 wallLLvl = self.properties.wallL9
1541                 wallBLvl = self.properties.wallB9
1542                 wallRLvl = self.properties.wallR9
1543                 wallFOLvl = self.properties.wallFO9
1544                 wallLOLvl = self.properties.wallLO9
1545                 wallBOLvl = self.properties.wallBO9
1546                 wallROLvl = self.properties.wallRO9
1547                 wallDLvlF = self.properties.wallDF9
1548                 wallDLvlL = self.properties.wallDL9
1549                 wallDLvlB = self.properties.wallDB9
1550                 wallDLvlR = self.properties.wallDR9
1551                 wallPLvlF = self.properties.wallPF9
1552                 wallPLvlL = self.properties.wallPL9
1553                 wallPLvlB = self.properties.wallPB9
1554                 wallPLvlR = self.properties.wallPR9
1555                 wallELvlF = self.properties.wallEF9
1556                 wallELvlL = self.properties.wallEL9
1557                 wallELvlB = self.properties.wallEB9
1558                 wallELvlR = self.properties.wallER9
1559                 wallCLvlF = self.properties.wallCF9
1560                 wallCLvlL = self.properties.wallCL9
1561                 wallCLvlB = self.properties.wallCB9
1562                 wallCLvlR = self.properties.wallCR9
1563                 wallSLvlF = self.properties.wallSF9
1564                 wallSLvlL = self.properties.wallSL9
1565                 wallSLvlB = self.properties.wallSB9
1566                 wallSLvlR = self.properties.wallSR9
1567                 wallBLvlF = self.properties.wallBF9
1568                 wallBLvlL = self.properties.wallBL9
1569                 wallBLvlB = self.properties.wallBB9
1570                 wallBLvlR = self.properties.wallBR9
1571
1572             if wallLvl == 9:
1573                 floorLvl = self.properties.CFloor10
1574                 wallFLvl = self.properties.wallF10
1575                 wallLLvl = self.properties.wallL10
1576                 wallBLvl = self.properties.wallB10
1577                 wallRLvl = self.properties.wallR10
1578                 wallFOLvl = self.properties.wallFO10
1579                 wallLOLvl = self.properties.wallLO10
1580                 wallBOLvl = self.properties.wallBO10
1581                 wallROLvl = self.properties.wallRO10
1582                 wallDLvlF = self.properties.wallDF10
1583                 wallDLvlL = self.properties.wallDL10
1584                 wallDLvlB = self.properties.wallDB10
1585                 wallDLvlR = self.properties.wallDR10
1586                 wallPLvlF = self.properties.wallPF10
1587                 wallPLvlL = self.properties.wallPL10
1588                 wallPLvlB = self.properties.wallPB10
1589                 wallPLvlR = self.properties.wallPR10
1590                 wallELvlF = self.properties.wallEF10
1591                 wallELvlL = self.properties.wallEL10
1592                 wallELvlB = self.properties.wallEB10
1593                 wallELvlR = self.properties.wallER10
1594                 wallCLvlF = self.properties.wallCF10
1595                 wallCLvlL = self.properties.wallCL10
1596                 wallCLvlB = self.properties.wallCB10
1597                 wallCLvlR = self.properties.wallCR10
1598                 wallSLvlF = self.properties.wallSF10
1599                 wallSLvlL = self.properties.wallSL10
1600                 wallSLvlB = self.properties.wallSB10
1601                 wallSLvlR = self.properties.wallSR10
1602                 wallBLvlF = self.properties.wallBF10
1603                 wallBLvlL = self.properties.wallBL10
1604                 wallBLvlB = self.properties.wallBB10
1605                 wallBLvlR = self.properties.wallBR10
1606
1607             levelBase = planeHL * wallLvl
1608
1609             floorDisc = False  # affects floor and wall positioning.
1610             if self.properties.CBaseB:  # if blocks for floor can make disc.
1611                 floorDisc = self.properties.CBaseR
1612
1613             ############
1614             # make floor for each level
1615             if floorLvl:
1616                 objName = OBJ_CF + str(wallLvl)
1617
1618                 # set plane dimensions and location
1619                 floorBase = levelBase
1620                 floorTop = floorBase + self.properties.cBaseT
1621                 floorPW = self.properties.cBaseW
1622                 floorD = self.properties.cBaseD
1623                 floorXO = -floorPW / 2  # center
1624                 floorXE = floorPW / 2
1625                 floorBounds = [floorXO, floorXE, floorBase, floorTop, 0, floorD]
1626
1627                 floorObj = makePlaneObj(objName, floorBounds, baseMtl, floorPW)
1628
1629                 # adjust floor location for 3D cursor since parented to Base...
1630                 yMod = castleScene.cursor_location.y
1631                 if floorDisc:  # make a disc shaped floor
1632                     yMod += self.properties.cBaseD / 2
1633                 floorObj.location.x -= castleScene.cursor_location.x
1634                 floorObj.location.y -= yMod
1635                 floorObj.location.z -= castleScene.cursor_location.z
1636
1637                 castleScene.objects.link(floorObj)  # must do for generation/rotation
1638
1639                 floorObj.parent = castleObj  # Connect to parent
1640
1641             ############
1642             # make walls for each level
1643             wallSides = [wallFLvl, wallLLvl, wallBLvl, wallRLvl]
1644             # Wall modifiers, per level, per wall (FBLR not FLBR order to match wall build sequence).
1645             wallMods = [[wallDLvlF, wallPLvlF, wallELvlF, wallCLvlF, wallSLvlF, wallBLvlF, wallFOLvl],
1646                         [wallDLvlB, wallPLvlB, wallELvlB, wallCLvlB, wallSLvlB, wallBLvlB, wallBOLvl],
1647                         [wallDLvlL, wallPLvlL, wallELvlL, wallCLvlL, wallSLvlL, wallBLvlL, wallLOLvl],
1648                         [wallDLvlR, wallPLvlR, wallELvlR, wallCLvlR, wallSLvlR, wallBLvlR, wallROLvl]
1649                         ]
1650
1651             wallLoc[2] = levelBase  # each segment base...
1652             makeWalls(self, castleScene, castleObj, wallSides, wallMods, wallLoc, wallMtl, wallLvl, floorDisc)
1653             wallLvl += 1
1654
1655         ####################
1656         # Make "Tower"
1657         if self.properties.cXTest and self.properties.CTower:
1658             # no steps or shelf for tower...
1659             wallExtOpts[0] = False
1660             wallExtOpts[1] = False
1661
1662             settings['Slope'] = True  # force curvature
1663
1664             dims['s'] = 0.0  # radial origin
1665             dims['e'] = CastleX / 2  # effective tower height; affects radius.
1666
1667             # set block "area": height, width, depth, and spacing.
1668             blockArea = [dims['e'], CastleX, blockDepth, self.properties.blockZ, settings['hv'], self.properties.Grout]
1669
1670             # Make "tower" wall.
1671             wallLoc[0] = 0
1672             wallLoc[1] = 0
1673             wallLoc[2] = castleScene.cursor_location.z
1674
1675             # generate tower...
1676             cTower1Obj = makeWallObj(self, castleScene, wallLoc, "CTower1", blockArea, [], wallExtOpts, wallMtl)
1677
1678             cTower1Obj.select_set(True)  # must select to rotate
1679             # rotate 90 forward (face plant)...
1680             #            bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[True,False,False])
1681             # rotate 90 ccw along side
1682             bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[False, True, False])
1683             bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
1684             cTower1Obj.select_set(False)  # deselect after rotate else joined with others...
1685
1686             cTower1Obj.parent = castleObj  # Connect to parent
1687
1688         ####################
1689         # Make "Dome"
1690         settings['Radial'] = self.properties.cDome
1691         if self.properties.cDome:
1692             # no steps or shelf for dome...
1693             wallExtOpts[0] = False
1694             wallExtOpts[1] = False
1695
1696             settings['Slope'] = True  # force curvature
1697             settings['sdv'] = 0.12
1698
1699             wallLoc[0] = castleScene.cursor_location.x
1700             wallLoc[1] = midWallD
1701             wallLoc[2] = self.properties.cDomeZ
1702
1703             domeMtl = uMatRGBSet('cDome_mat', self.cDomeRGB, matMod=True)
1704
1705             # set block "area": height, width, depth, and spacing.
1706             blockArea = [self.properties.cDomeH, CastleX, blockDepth, self.properties.blockZ, settings['hv'], self.properties.Grout]
1707
1708             # eliminate to allow user control for start/completion by width setting.
1709             dims['s'] = 0.0  # complete radial
1710
1711             if midWallD < midWallW:  # use shortest dimension for dome.
1712                 dims['e'] = midWallD
1713             else:
1714                 dims['e'] = midWallW
1715
1716             # generate dome...
1717             cDomeObj = makeWallObj(self, castleScene, wallLoc, "cDome", blockArea, [], wallExtOpts, domeMtl)
1718
1719             yMod = 0  # shift "location" as needed...
1720             if floorDisc:  # make a disc shaped floor
1721                 yMod -= (CastleD + blockDepth) / 2
1722             cDomeObj.location.y += yMod
1723
1724             cDomeObj.parent = castleObj  # Connect to parent
1725
1726         castleObj.select_set(True)
1727         context.view_layer.objects.active = castleObj
1728
1729         return {'FINISHED'}
1730
1731 ################################################################################
1732
1733
1734 ####################
1735 #
1736     # Walls
1737 #
1738 # when variations or openings are selected different block rows are
1739 # generated for each wall; edges/ends don't match.
1740 #
1741 ####################
1742 def makeWalls(sRef, objScene, objParent, objSides, objMods, objLoc, objMat, objLvl, floorDisc):
1743
1744     wallExtOpts = [False, False]  # no steps or shelf
1745
1746     WobjH = sRef.properties.wallH
1747     WobjW = sRef.properties.cBaseW
1748     WobjY = sRef.properties.cBaseD
1749     WobjD = sRef.properties.blockD
1750
1751     segWidth = sRef.properties.blockX
1752
1753     # center wall...
1754     midWallD = (WobjY + WobjD) / 2
1755     midWallW = (WobjW + WobjD) / 2
1756
1757     settings['eoff'] = sRef.properties.EdgeOffset
1758     settings['sdv'] = sRef.properties.blockX
1759
1760     settings['Slope'] = False
1761     if sRef.properties.cXTest:
1762         if sRef.properties.CTunnel:
1763             settings['Slope'] = True  # force curve
1764         else:
1765             settings['Slope'] = sRef.properties.wallSlope  # user select curve walls...
1766
1767     # passed as params since modified by wall and dome...
1768     settings['StepsB'] = sRef.properties.StepF  # fill under with blocks
1769     settings['StepsL'] = sRef.properties.StepL  # up to left
1770     settings['StepsO'] = sRef.properties.StepOut  # outside of wall
1771
1772     # set block "area": height, width, depth, and spacing for front and back walls.
1773     blockArea = [WobjH, WobjW, WobjD, sRef.properties.blockZ, settings['hv'], sRef.properties.Grout]
1774     dims['s'] = -midWallW
1775     dims['e'] = midWallW
1776
1777     yMod = 0  # shift front/back "location" as needed...
1778     if floorDisc:  # make a disc shaped floor
1779         yMod = WobjY
1780
1781     ####################
1782     if objSides[0]:  # Make "front" wall
1783         objName = OBJ_WF + str(objLvl)
1784
1785         wallRound = objMods[0][6]  # round (disc) wall...
1786
1787         # adjust sizing for round wall else ensure full height.
1788         if wallRound:
1789             settings['Radial'] = True
1790             blockArea[0] = WobjH / 2  # half height for radius
1791         else:
1792             settings['Radial'] = False  # disable disc
1793             blockArea[0] = WobjH  # ensure full height
1794
1795         wallSteps = False
1796         wallShelf = False
1797         wallHoles = []
1798
1799         openingSpecs[OP_DOOR]['a'] = objMods[0][0]  # door
1800         openingSpecs[OP_PORT]['a'] = objMods[0][1]  # window
1801         openingSpecs[OP_SLOT]['a'] = objMods[0][2]  # slot
1802         openingSpecs[OP_CREN]['a'] = objMods[0][3]  # crenel
1803         wallExtOpts[0] = objMods[0][4]  # steps
1804         wallExtOpts[1] = objMods[0][5]  # shelf
1805
1806         wallHoles = openList(sRef)
1807
1808         objLoc[0] = 0
1809         if sRef.properties.cXTest and sRef.properties.CTunnel:
1810             objLoc[1] = WobjY / 2
1811         else:
1812             objLoc[1] = 0
1813
1814         objLoc[1] -= yMod / 2  # offset for disc
1815
1816         # generate wall...
1817         cFrontObj = makeWallObj(sRef, objScene, objLoc, objName, blockArea, wallHoles, wallExtOpts, objMat)
1818         cFrontObj.parent = objParent  # Connect to parent
1819
1820         if wallRound:  # rotate 90 forward if round/disc
1821             cFrontObj.select_set(True)  # must select to rotate
1822             bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[True, False, False])
1823             bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
1824             cFrontObj.location.z += WobjH / 2  # adjust vertical after rotate for radius
1825             cFrontObj.select_set(False)  # deselect after rotate else joined with others...
1826
1827     ####################
1828     if objSides[2]:  # Make "back" wall
1829         objName = OBJ_WB + str(objLvl)
1830
1831         wallRound = objMods[1][6]  # round (disc) wall...
1832
1833         # adjust sizing for round wall else ensure full height.
1834         if wallRound:
1835             settings['Radial'] = True
1836             blockArea[0] = WobjH / 2  # half height for radius
1837         else:
1838             settings['Radial'] = False  # disable disc
1839             blockArea[0] = WobjH  # ensure full height
1840
1841         wallSteps = False
1842         wallShelf = False
1843         wallHoles = []
1844
1845         openingSpecs[OP_DOOR]['a'] = objMods[1][0]  # door
1846         openingSpecs[OP_PORT]['a'] = objMods[1][1]  # window
1847         openingSpecs[OP_SLOT]['a'] = objMods[1][2]  # slot
1848         openingSpecs[OP_CREN]['a'] = objMods[1][3]  # crenel
1849         wallExtOpts[0] = objMods[1][4]  # steps
1850         wallExtOpts[1] = objMods[1][5]  # shelf
1851
1852         wallHoles = openList(sRef)
1853
1854         objLoc[0] = 0
1855         if sRef.properties.cXTest and sRef.properties.CTunnel:
1856             objLoc[1] = WobjY / 2
1857         else:
1858             objLoc[1] = WobjY
1859
1860         objLoc[1] -= yMod / 2  # offset for floor disc
1861
1862         # generate wall...
1863         cBackObj = makeWallObj(sRef, objScene, objLoc, objName, blockArea, wallHoles, wallExtOpts, objMat)
1864         cBackObj.parent = objParent  # Connect to parent
1865
1866         cBackObj.select_set(True)  # must select to rotate
1867
1868         # rotate to "reverse" face of wall, else just a mirror of front.
1869         bpy.ops.transform.rotate(value=cPie, constraint_axis=[False, False, True])
1870         bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
1871
1872         if wallRound:  # rotate 90 forward
1873             bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[True, False, False])
1874             cBackObj.location.z += WobjH / 2  # adjust vertical after rotate for radius
1875
1876         cBackObj.select_set(False)  # de-select after rotate else joined with others...
1877
1878     ####################
1879     # set block "area": height, width, depth, and spacing for side walls...
1880     blockArea = [WobjH, WobjY - segWidth * 2, WobjD, sRef.properties.blockZ, settings['hv'], sRef.properties.Grout]
1881     #    blockArea=[WobjH,WobjY-segWidth,WobjD,sRef.properties.blockZ,settings['hv'],sRef.properties.Grout]
1882     #    blockArea=[WobjH,WobjY,WobjD,sRef.properties.blockZ,settings['hv'],sRef.properties.Grout]
1883     # rowprocessing() side walls
1884     dims['s'] = -midWallD
1885     dims['e'] = midWallD
1886     ####################
1887
1888     ####################
1889     if objSides[1]:  # Make "Left" wall
1890         objName = OBJ_WL + str(objLvl)
1891
1892         wallRound = objMods[2][6]  # round (disc) wall...
1893
1894         # adjust sizing for round wall else ensure full height.
1895         if wallRound:
1896             settings['Radial'] = True
1897             blockArea[0] = WobjH / 2  # half height for radius
1898         else:
1899             settings['Radial'] = False  # disable disc
1900             blockArea[0] = WobjH  # ensure full height
1901
1902         wallSteps = False
1903         wallShelf = False
1904         wallHoles = []
1905
1906         openingSpecs[OP_DOOR]['a'] = objMods[2][0]  # door
1907         openingSpecs[OP_PORT]['a'] = objMods[2][1]  # window
1908         openingSpecs[OP_SLOT]['a'] = objMods[2][2]  # slot
1909         # radius/round sizing wrong when crenel selected...
1910         openingSpecs[OP_CREN]['a'] = objMods[2][3]  # crenel
1911         wallExtOpts[0] = objMods[2][4]  # steps
1912         wallExtOpts[1] = objMods[2][5]  # shelf
1913
1914         wallHoles = openList(sRef)
1915
1916         if sRef.properties.cXTest and sRef.properties.CTunnel:
1917             objLoc[0] = 0
1918         else:
1919             objLoc[0] = -midWallW
1920
1921         if floorDisc:  # make a disc shaped floor
1922             objLoc[1] = 0
1923         else:
1924             objLoc[1] = midWallD - (WobjD / 2)
1925         #        objLoc[1]=midWallD
1926
1927         # generate wall...
1928         cSideLObj = makeWallObj(sRef, objScene, objLoc, objName, blockArea, wallHoles, wallExtOpts, objMat)
1929         cSideLObj.parent = objParent  # Connect to parent
1930
1931         cSideLObj.select_set(True)  # must select to rotate
1932         bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
1933
1934         if wallRound:  # rotate 90 forward
1935             bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[True, False, False])
1936             cSideLObj.location.z += WobjH / 2  # adjust vertical after rotate for radius
1937
1938         if sRef.properties.cXTest and sRef.properties.CTunnel:
1939             # rotate 90 horizontal, ccw...
1940             bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[False, False, True])
1941         else:
1942             # rotate 90 horizontal, cw...
1943             bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[False, False, True])
1944         # rotate 90 forward (face plant)...
1945         #            bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[True,False,False])
1946         # rotate 90 cw along side
1947         #            bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[False,True,False])
1948
1949         cSideLObj.select_set(False)  # deselect after rotate else joined with others...
1950
1951
1952     ####################
1953     if objSides[3]:  # Make "Right" wall
1954         objName = OBJ_WR + str(objLvl)
1955
1956         wallRound = objMods[3][6]  # round (disc) wall...
1957
1958         # adjust sizing for round wall else ensure full height.
1959         if wallRound:
1960             settings['Radial'] = True
1961             blockArea[0] = WobjH / 2  # half height for radius
1962         else:
1963             settings['Radial'] = False  # disable disc
1964             blockArea[0] = WobjH  # ensure full height
1965
1966         wallSteps = False
1967         wallShelf = False
1968         wallHoles = []
1969
1970         openingSpecs[OP_DOOR]['a'] = objMods[3][0]  # door
1971         openingSpecs[OP_PORT]['a'] = objMods[3][1]  # window
1972         openingSpecs[OP_SLOT]['a'] = objMods[3][2]  # slot
1973         openingSpecs[OP_CREN]['a'] = objMods[3][3]  # crenel
1974         wallExtOpts[0] = objMods[3][4]  # steps
1975         wallExtOpts[1] = objMods[3][5]  # shelf
1976
1977         wallHoles = openList(sRef)
1978
1979         if sRef.properties.cXTest and sRef.properties.CTunnel:
1980             objLoc[0] = 0
1981         else:
1982             objLoc[0] = midWallW
1983
1984         if floorDisc:  # make a disc shaped floor
1985             objLoc[1] = 0
1986         else:
1987             objLoc[1] = midWallD - (WobjD / 2)
1988
1989         cSideRObj = makeWallObj(sRef, objScene, objLoc, objName, blockArea, wallHoles, wallExtOpts, objMat)
1990         cSideRObj.parent = objParent  # Connect to parent
1991         #        objScene.objects.active=cSideRObj
1992
1993         cSideRObj.select_set(True)  # must select to rotate
1994         bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
1995
1996         if wallRound:  # rotate 90 forward
1997             bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[True, False, False])
1998             cSideRObj.location.z += WobjH / 2  # adjust vertical after rotate for radius
1999
2000         if sRef.properties.cXTest and sRef.properties.CTunnel:
2001             # rotate 90 horizontal, cw...
2002             bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[False, False, True])
2003         # rotate 90 horizontal, ccw...
2004         else:
2005             bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[False, False, True])
2006         # rotate 90 forward (face plant)...
2007         #        bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[True,False,False])
2008         # rotate 90 cw along side
2009         #        bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[False,True,False])
2010
2011         cSideRObj.select_set(False)  # deselect after rotate...
2012
2013         return  # all done, is automatic, just a matter of detail/protocol.
2014
2015
2016 ################################################################################
2017
2018 def makeWallObj(sRef, objScene, objLoc, objName, blockArea, openList, wallExtOpts, objMat):
2019
2020     settings['Steps'] = wallExtOpts[0]
2021     settings['Shelf'] = wallExtOpts[1]
2022
2023     meshVs, meshFs = wallBuild(sRef, wallPlan(sRef, blockArea, openList), openList)
2024
2025     newMesh = bpy.data.meshes.new(objName)
2026     newMesh.from_pydata(meshVs, [], meshFs)
2027     # doesn't seem to matter...
2028     #    newMesh.update(calc_edges=True)
2029     newMesh.materials.append(objMat)
2030
2031     newObj = bpy.data.objects.new(objName, newMesh)
2032     newObj.location.x = objLoc[0]
2033     newObj.location.y = objLoc[1]
2034     newObj.location.z = objLoc[2]
2035
2036     objScene.objects.link(newObj)  # must do for generation/rotation
2037
2038     return newObj
2039
2040
2041 ########################
2042 # Generate simple "plane" for floor objects.
2043 #
2044 # objArea[leftX,rightX,zBase,zTop,0,yDepth]
2045 # objDiv will subdivide plane based on sizing;
2046 #  - see MakeABlock(objArea,objDiv) for details.
2047 #
2048 def makePlaneObj(objName, objArea, objMat, objDiv):
2049     objVs = []
2050     objFs = []
2051
2052     objVs, objFs = MakeABlock(objArea, objDiv)
2053
2054     objMesh = bpy.data.meshes.new(objName)
2055     objMesh.from_pydata(objVs, [], objFs)
2056     objMesh.update()
2057     #    objMesh.update(calc_edges=True) # doesn't seem to matter...
2058     objMesh.materials.append(objMat)
2059
2060     newObj = bpy.data.objects.new(objName, objMesh)
2061     newObj.location = bpy.context.scene.cursor_location
2062
2063     return newObj
2064
2065
2066 ################################################################################
2067 ################
2068 ########################
2069 ##
2070 #
2071 # Blocks module/script inclusion
2072 # - to be reduced significantly.
2073 ##
2074 # Module notes:
2075 #
2076 # consider removing wedge crit for small "c" and "cl" values
2077 # wrap around for openings on radial stonework?
2078 # repeat for opening doesn't distribute evenly when radialized
2079 #  - see wrap around note above.
2080 # if opening width == indent*2 the edge blocks fail (row of blocks cross opening).
2081 # if block width variance is 0, and edging is on, right edge blocks create a "vertical seam".
2082 #
2083 ##
2084 #######################
2085 ################
2086 ################################################################################
2087 ##
2088
2089
2090 ###################################
2091 #
2092 # create lists of blocks.
2093 #
2094 # blockArea defines the "space" (vertical, horizontal, depth) to fill,
2095 #  and provides block height, variation, and gap/grout. May affect "door" opening.
2096 #
2097 # holeList identifies "openings" in area.
2098 #
2099 # Returns: list of rows.
2100 #  rows = [[center height,row height,edge offset],[...]]
2101 #
2102 def wallPlan(sRef, blockArea, holeList):
2103
2104     rows = []
2105
2106     blockAreaZ = blockArea[0]
2107     blockAreaX = blockArea[1]
2108     blockAreaY = blockArea[2]
2109     blockHMax = blockArea[3]
2110     blockHVar = blockArea[4]
2111     blockGap = blockArea[5]
2112     # this is wrong! should be...?
2113     blockHMin = BLOCK_MIN + blockGap
2114
2115     # no variation for slopes so walls match curvature
2116     if sRef.properties.cXTest:
2117         if sRef.properties.wallSlope or sRef.properties.CTunnel:
2118             blockHVar = 0
2119
2120     rowHMin = blockHMin
2121     # alternate rowHMin=blockHMax-blockHVar+blockGap
2122
2123     # splits are a row division, for openings
2124     splits = [0]  # split bottom row
2125     # add a split for each critical point on each opening
2126     for hole in holeList:
2127         splits += hole.crits()
2128     # and, a split for the top row
2129     splits.append(blockAreaZ)
2130     splits.sort()  # needed?
2131
2132     # divs are the normal old row divisions, add them between the top and bottom split
2133     # what's with "[1:-1]" at end????
2134     divs = fill(splits[0], splits[-1], blockHMax, blockHMin, blockHVar)[1:-1]
2135
2136     # remove the divisions that are too close to the splits, so we don't get tiny thin rows
2137     for i in range(len(divs) - 1, -1, -1):
2138         for j in range(len(splits)):
2139             if abs(divs[i] - splits[j]) < rowHMin:
2140                 del(divs[i])
2141                 break
2142
2143     # now merge the divs and splits lists
2144     divs += splits
2145     divs.sort()
2146
2147     # trim the rows to the bottom and top of the wall
2148     if divs[0] < 0:
2149         divs[:1] = []
2150     if divs[-1] > blockAreaZ:
2151         divs[-1:] = []
2152
2153     # process each row
2154     divCount = len(divs) - 1  # number of divs to check
2155     divCheck = 0  # current div entry
2156
2157     while divCheck < divCount:
2158         RowZ = (divs[divCheck] + divs[divCheck + 1]) / 2
2159         RowHeight = divs[divCheck + 1] - divs[divCheck] - blockGap
2160         rowEdge = settings['eoff'] * (fmod(divCheck, 2) - 0.5)
2161
2162         if RowHeight < rowHMin:  # skip row if too shallow
2163             del(divs[divCheck + 1])  # delete next div entry
2164             divCount -= 1  # Adjust count for removed div entry.
2165             continue
2166
2167         rows.append(rowOb(RowZ, RowHeight, rowEdge))
2168
2169         divCheck += 1  # on to next div entry
2170
2171     # set up opening object to handle the edges of the wall
2172     WallBoundaries = OpeningInv((dims['s'] + dims['e']) / 2, blockAreaZ / 2, blockAreaX, blockAreaZ)
2173
2174     # Go over each row in the list, set up edge blocks and block sections
2175     for rownum in range(len(rows)):
2176         rowProcessing(rows[rownum], holeList, WallBoundaries)
2177
2178     return rows
2179
2180
2181 ################################################################################
2182 #
2183 # Build the wall, based on rows, "holeList", and parameters;
2184 #     geometry for the blocks, arches, steps, platforms...
2185 #
2186 # Return: verts and faces for wall object.
2187 #
2188 def wallBuild(sRef, rows, holeList):
2189
2190     wallVs = []
2191     wallFs = []
2192
2193     AllBlocks = []
2194
2195     # create local references for anything that's used more than once...
2196     rowCount = len(rows)
2197
2198     wallTop = rows[rowCount - 1].z
2199     #    wallTop=sRef.properties.wallH
2200     wallTop2 = wallTop * 2
2201
2202     wallDome = settings['Radial']
2203     wallSlope = settings['Slope']
2204
2205     blockWidth = sRef.properties.blockX
2206
2207     blockGap = sRef.properties.Grout
2208     halfGrout = blockGap / 2  # half grout for block size modifier
2209     blockHMin = BLOCK_MIN + blockGap
2210
2211     blockDhalf = settings['d'] / 2  # offset by half block depth to match UI setting
2212
2213     for rowidx in range(rowCount):  # add blocks for each row.
2214         rows[rowidx].FillBlocks()
2215
2216     if sRef.properties.blockVar and sRef.properties.blockMerge:  # merge (vertical) blocks in close proximity...
2217         blockSpace = blockGap
2218         for rowidx in range(rowCount - 1):
2219             if wallDome:
2220                 blockSpace = blockGap / (wallTop * sin(abs(rows[rowidx].z) * cPie / wallTop2))
2221             #            else: blockSpace=blockGap/(abs(rows[rowidx].z)) # make it flat
2222
2223             idxThis = len(rows[rowidx].BlocksNorm[:]) - 1
2224             idxThat = len(rows[rowidx + 1].BlocksNorm[:]) - 1
2225
2226             while True:
2227                 # end loop when either array idx wraps
2228                 if idxThis < 0 or idxThat < 0:
2229                     break
2230
2231                 blockThis = rows[rowidx].BlocksNorm[idxThis]
2232                 blockThat = rows[rowidx + 1].BlocksNorm[idxThat]
2233
2234                 cx, cz, cw, ch, cd = blockThis[:5]
2235                 ox, oz, ow, oh, od = blockThat[:5]
2236
2237                 if (abs(cw - ow) < blockSpace) and (abs(cx - ox) < blockSpace):
2238                     if cw > ow:
2239                         BlockW = ow
2240                     else:
2241                         BlockW = cw
2242
2243                     AllBlocks.append([(cx + ox) / 2, (cz + oz + (oh - ch) / 2) / 2, BlockW, abs(cz - oz) + (ch + oh) / 2, (cd + od) / 2, None])
2244
2245                     rows[rowidx].BlocksNorm.pop(idxThis)
2246                     rows[rowidx + 1].BlocksNorm.pop(idxThat)
2247                     idxThis -= 1
2248                     idxThat -= 1
2249
2250                 elif cx > ox:
2251                     idxThis -= 1
2252                 else:
2253                     idxThat -= 1
2254
2255     ####
2256     # Add blocks to create a "shelf/platform".
2257     # Does not account for openings (crosses gaps - which is a good thing)
2258     if settings['Shelf']:
2259
2260         # Use wall block settings for shelf
2261         shelfBW = blockWidth
2262         shelfBWVar = settings['wv']
2263         shelfBH = sRef.properties.blockZ
2264
2265         ShelfLft = sRef.properties.ShelfX
2266         ShelfBtm = sRef.properties.ShelfZ
2267         ShelfRt = ShelfLft + sRef.properties.ShelfW
2268         ShelfTop = ShelfBtm + sRef.properties.ShelfH
2269         ShelfThk = sRef.properties.ShelfD
2270         ShelfThk2 = ShelfThk * 2  # double-depth to position at cursor.
2271
2272         if sRef.properties.ShelfOut:  # place blocks on outside of wall
2273             ShelfOffsets = [[0, -blockDhalf, 0], [0, -ShelfThk, 0], [0, -blockDhalf, 0], [0, -ShelfThk, 0], [0, -blockDhalf, 0], [0, -ShelfThk, 0], [0, -blockDhalf, 0], [0, -ShelfThk, 0]]
2274         else:
2275             ShelfOffsets = [[0, ShelfThk, 0], [0, blockDhalf, 0], [0, ShelfThk, 0], [0, blockDhalf, 0], [0, ShelfThk, 0], [0, blockDhalf, 0], [0, ShelfThk, 0], [0, blockDhalf, 0]]
2276
2277         while ShelfBtm < ShelfTop:  # Add blocks for each "shelf row" in area
2278             divs = fill(ShelfLft, ShelfRt, shelfBW, shelfBW, shelfBWVar)
2279
2280             for i in range(len(divs) - 1):  # add blocks for row divisions
2281                 ThisBlockx = (divs[i] + divs[i + 1]) / 2
2282                 ThisBlockw = divs[i + 1] - divs[i] - halfGrout
2283                 AllBlocks.append([ThisBlockx, ShelfBtm, ThisBlockw, shelfBH, ShelfThk2, ShelfOffsets])
2284
2285             ShelfBtm += shelfBH + halfGrout  # moving up to next row...
2286
2287     # Set shelf material/color... on wish list.
2288
2289     ####
2290     # Add blocks to create "steps".
2291     # Does not account for openings (crosses gaps - which is a good thing)
2292     if settings['Steps']:
2293
2294         stepsFill = settings['StepsB']
2295         steps2Left = settings['StepsL']
2296
2297         # step block "filler" by wall block settings.
2298         stepFW = blockWidth
2299         StepFWVar = settings['wv']
2300
2301         StepXMod = sRef.properties.StepT  # step tread, also sets basic block size.
2302         StepZMod = sRef.properties.StepV
2303
2304         StepLft = sRef.properties.StepX
2305         StepWide = sRef.properties.StepW
2306         StepRt = StepLft + StepWide
2307         StepBtm = sRef.properties.StepZ + StepZMod / 2  # Start offset for centered blocks
2308         StepTop = StepBtm + sRef.properties.StepH
2309
2310         StepThk = sRef.properties.StepD
2311         StepThk2 = StepThk * 2  # use double-depth due to offsets to position at cursor.
2312
2313         # Use "corners" to adjust steps so not centered on depth.
2314         # steps at cursor so no gaps between steps and wall face due to wall block depth.
2315         if settings['StepsO']:  # place blocks on outside of wall
2316             StepOffsets = [[0, -blockDhalf, 0], [0, -StepThk, 0], [0, -blockDhalf, 0], [0, -StepThk, 0], [0, -blockDhalf, 0], [0, -StepThk, 0], [0, -blockDhalf, 0], [0, -StepThk, 0]]
2317         else:
2318             StepOffsets = [[0, StepThk, 0], [0, blockDhalf, 0], [0, StepThk, 0], [0, blockDhalf, 0], [0, StepThk, 0], [0, blockDhalf, 0], [0, StepThk, 0], [0, blockDhalf, 0]]
2319
2320         # Add steps for each "step row" in area (neg width is interesting but prevented)
2321         while StepBtm < StepTop and StepWide > 0:
2322
2323             # Make blocks for each step row - based on rowOb:fillblocks
2324             if stepsFill:
2325                 divs = fill(StepLft, StepRt, StepXMod, stepFW, StepFWVar)
2326
2327                 # loop through the row divisions, adding blocks for each one
2328                 for i in range(len(divs) - 1):
2329                     ThisBlockx = (divs[i] + divs[i + 1]) / 2
2330                     ThisBlockw = divs[i + 1] - divs[i] - halfGrout
2331
2332                     AllBlocks.append([ThisBlockx, StepBtm, ThisBlockw, StepZMod, StepThk2, StepOffsets])
2333             else:  # "cantilevered steps"
2334                 if steps2Left:
2335                     stepStart = StepRt - StepXMod
2336                 else:
2337                     stepStart = StepLft
2338
2339                 AllBlocks.append([stepStart, StepBtm, StepXMod, StepZMod, StepThk2, StepOffsets])
2340
2341             StepBtm += StepZMod + halfGrout  # moving up to next row...
2342             StepWide -= StepXMod  # reduce step width
2343
2344             # adjust side limit depending on direction of steps
2345             if steps2Left:
2346                 StepRt -= StepXMod  # move from right
2347             else:
2348                 StepLft += StepXMod  # move from left
2349
2350     ####
2351     # Copy all the blocks out of the rows
2352     for row in rows:
2353         AllBlocks += row.BlocksEdge
2354         AllBlocks += row.BlocksNorm
2355
2356     ####
2357     # make individual blocks for each block specified in the plan
2358     subDivision = settings['sdv']
2359
2360     for block in AllBlocks:
2361         x, z, w, h, d, corners = block
2362         holeW2 = w / 2
2363
2364         geom = MakeABlock([x - holeW2, x + holeW2, z - h / 2, z + h / 2, -d / 2, d / 2], subDivision, len(wallVs), corners)
2365         wallVs += geom[0]
2366         wallFs += geom[1]
2367
2368     # make Arches for every opening specified in the plan.
2369     for hole in holeList:
2370         # lower arch stones
2371         if hole.vl > 0 and hole.rtl > blockHMin:
2372             archGeneration(hole, wallVs, wallFs, -1)
2373
2374         # top arch stones
2375         if hole.v > 0 and hole.rt > blockHMin:
2376             archGeneration(hole, wallVs, wallFs, 1)
2377
2378     if wallSlope:  # Curve wall, dome shape if "radialized".
2379         for i, vert in enumerate(wallVs):
2380             wallVs[i] = [vert[0], (wallTop + vert[1]) * cos(vert[2] * cPie / wallTop2), (wallTop + vert[1]) * sin(vert[2] * cPie / wallTop2)]
2381
2382     if wallDome:  # Make wall circular, dome if sloped, else disc (flat round).
2383         for i, vert in enumerate(wallVs):
2384             wallVs[i] = [vert[2] * cos(vert[0]), vert[2] * sin(vert[0]), vert[1]]
2385
2386     return wallVs, wallFs
2387
2388
2389 ################################################################################
2390 #
2391 # create a list of openings from the general specifications.
2392 #
2393 def openList(sRef):
2394     boundlist = []
2395
2396     # initialize variables
2397     areaStart = dims['s']
2398     areaEnd = dims['e']
2399
2400     SetWid = sRef.properties.blockX
2401     wallDisc = settings['Radial']
2402
2403     for x in openingSpecs:
2404         if x['a']:  # apply opening to object
2405             # hope this is faster, at least for repeat.
2406             xOpenW = x['w']
2407             xOpenX = x['x']
2408             xOpenZ = x['z']
2409
2410             if x['n']:  # repeat...
2411                 if wallDisc:
2412                     r1 = xOpenZ
2413                 else:
2414                     r1 = 1
2415
2416                 if xOpenX > (xOpenW + SetWid):
2417                     spacing = xOpenX / r1
2418                 else:
2419                     spacing = (xOpenW + SetWid) / r1
2420
2421                 minspacing = (xOpenW + SetWid) / r1
2422
2423                 divs = fill(areaStart, areaEnd, spacing, minspacing, center=1)
2424
2425                 for posidx in range(len(divs) - 2):
2426                     boundlist.append(opening(divs[posidx + 1], xOpenZ, xOpenW, x['h'], x['v'], x['t'], x['vl'], x['tl'], x['bvl']))
2427
2428             else:
2429                 boundlist.append(opening(xOpenX, xOpenZ, xOpenW, x['h'], x['v'], x['t'], x['vl'], x['tl'], x['bvl']))
2430             # check for edge overlap?
2431
2432     return boundlist
2433
2434
2435 ################################################################################
2436 #
2437 # fill a linear space with divisions
2438 #
2439 #    objXO: x origin
2440 #    objXL: x limit
2441 #    avedst: the average distance between points
2442 #    mindst: the minimum distance between points
2443 #    dev: the maximum random deviation from avedst
2444 #    center: flag to center the elements in the range, 0 == disabled
2445 #
2446 # returns an ordered list of points, including the end points.
2447 #
2448 def fill(objXO, objXL, avedst, mindst=0.0, dev=0.0, center=0):
2449
2450     curpos = objXO
2451     poslist = [curpos]  # set starting point
2452
2453     # Set offset by average spacing, then add blocks (fall through);
2454     # if not at edge.
2455     if center:
2456         curpos += ((objXL - objXO - mindst * 2) % avedst) / 2 + mindst
2457         if curpos - poslist[-1] < mindst:
2458             curpos = poslist[-1] + mindst + random() * dev / 2
2459
2460         # clip to right edge.
2461         if (objXL - curpos < mindst) or (objXL - curpos < mindst):
2462             poslist.append(objXL)
2463             return poslist
2464         else:
2465             poslist.append(curpos)
2466
2467     # make block edges
2468     while True:
2469         curpos += avedst + rndd() * dev
2470         if curpos - poslist[-1] < mindst:
2471             curpos = poslist[-1] + mindst + random() * dev / 2
2472
2473         if (objXL - curpos < mindst) or (objXL - curpos < mindst):
2474             poslist.append(objXL)  # close off edges at limit
2475             return poslist
2476         else:
2477             poslist.append(curpos)
2478
2479
2480 #######################################################################
2481 #
2482 # MakeABlock: Generate block geometry
2483 #  to be made into a square cornered block, subdivided along the length.
2484 #
2485 #  bounds: a list of boundary positions:
2486 #      0:left, 1:right, 2:bottom, 3:top, 4:front, 5:back
2487 #  segsize: the maximum size before subdivision occurs
2488 #  vll: the number of vertexes already in the mesh. len(mesh.verts) should
2489 #          give this number.
2490 #  Offsets: list of coordinate delta values.
2491 #      Offsets are lists, [x,y,z] in
2492 #          [
2493 #          0:left_bottom_back,
2494 #          1:left_bottom_front,
2495 #          2:left_top_back,
2496 #          3:left_top_front,
2497 #          4:right_bottom_back,
2498 #          5:right_bottom_front,
2499 #          6:right_top_back,
2500 #          7:right_top_front,
2501 #          ]
2502 #  FaceExclude: list of faces to exclude from the faces list; see bounds above for indices
2503 #
2504 #  return lists of points and faces.
2505 #
2506 def MakeABlock(bounds, segsize, vll=0, Offsets=None, FaceExclude=[]):
2507
2508     slices = fill(bounds[0], bounds[1], segsize, segsize, center=1)
2509     points = []
2510     faces = []
2511
2512     if Offsets == None:
2513         points.append([slices[0], bounds[4], bounds[2]])
2514         points.append([slices[0], bounds[5], bounds[2]])
2515         points.append([slices[0], bounds[5], bounds[3]])
2516         points.append([slices[0], bounds[4], bounds[3]])
2517
2518         for x in slices[1:-1]:
2519             points.append([x, bounds[4], bounds[2]])
2520             points.append([x, bounds[5], bounds[2]])
2521             points.append([x, bounds[5], bounds[3]])
2522             points.append([x, bounds[4], bounds[3]])
2523
2524         points.append([slices[-1], bounds[4], bounds[2]])
2525         points.append([slices[-1], bounds[5], bounds[2]])
2526         points.append([slices[-1], bounds[5], bounds[3]])
2527         points.append([slices[-1], bounds[4], bounds[3]])
2528
2529     else:
2530         points.append([slices[0] + Offsets[0][0], bounds[4] + Offsets[0][1], bounds[2] + Offsets[0][2]])
2531         points.append([slices[0] + Offsets[1][0], bounds[5] + Offsets[1][1], bounds[2] + Offsets[1][2]])
2532         points.append([slices[0] + Offsets[3][0], bounds[5] + Offsets[3][1], bounds[3] + Offsets[3][2]])
2533         points.append([slices[0] + Offsets[2][0], bounds[4] + Offsets[2][1], bounds[3] + Offsets[2][2]])
2534
2535         for x in slices[1:-1]:
2536             xwt = (x - bounds[0]) / (bounds[1] - bounds[0])
2537             points.append([x + Offsets[0][0] * (1 - xwt) + Offsets[4][0] * xwt, bounds[4] + Offsets[0][1] * (1 - xwt) + Offsets[4][1] * xwt, bounds[2] + Offsets[0][2] * (1 - xwt) + Offsets[4][2] * xwt])
2538             points.append([x + Offsets[1][0] * (1 - xwt) + Offsets[5][0] * xwt, bounds[5] + Offsets[1][1] * (1 - xwt) + Offsets[5][1] * xwt, bounds[2] + Offsets[1][2] * (1 - xwt) + Offsets[5][2] * xwt])
2539             points.append([x + Offsets[3][0] * (1 - xwt) + Offsets[7][0] * xwt, bounds[5] + Offsets[3][1] * (1 - xwt) + Offsets[7][1] * xwt, bounds[3] + Offsets[3][2] * (1 - xwt) + Offsets[7][2] * xwt])
2540             points.append([x + Offsets[2][0] * (1 - xwt) + Offsets[6][0] * xwt, bounds[4] + Offsets[2][1] * (1 - xwt) + Offsets[6][1] * xwt, bounds[3] + Offsets[2][2] * (1 - xwt) + Offsets[6][2] * xwt])
2541
2542         points.append([slices[-1] + Offsets[4][0], bounds[4] + Offsets[4][1], bounds[2] + Offsets[4][2]])
2543         points.append([slices[-1] + Offsets[5][0], bounds[5] + Offsets[5][1], bounds[2] + Offsets[5][2]])
2544         points.append([slices[-1] + Offsets[7][0], bounds[5] + Offsets[7][1], bounds[3] + Offsets[7][2]])
2545         points.append([slices[-1] + Offsets[6][0], bounds[4] + Offsets[6][1], bounds[3] + Offsets[6][2]])
2546
2547     faces.append([vll, vll + 3, vll + 2, vll + 1])
2548
2549     for x in range(len(slices) - 1):
2550         faces.append([vll, vll + 1, vll + 5, vll + 4])
2551         vll += 1
2552         faces.append([vll, vll + 1, vll + 5, vll + 4])
2553         vll += 1
2554         faces.append([vll, vll + 1, vll + 5, vll + 4])
2555         vll += 1
2556         faces.append([vll, vll - 3, vll + 1, vll + 4])
2557         vll += 1
2558
2559     faces.append([vll, vll + 1, vll + 2, vll + 3])
2560
2561     return points, faces
2562
2563
2564 #
2565 # For generating Keystone Geometry
2566 def MakeAKeystone(xpos, width, zpos, ztop, zbtm, thick, bevel, vll=0, FaceExclude=[], xBevScl=1):
2567     __doc__ = """\
2568     MakeAKeystone returns lists of points and faces to be made into a square cornered keystone, with optional bevels.
2569     xpos: x position of the centerline
2570     width: x width of the keystone at the widest point (discounting bevels)
2571     zpos: z position of the widest point
2572     ztop: distance from zpos to the top
2573     zbtm: distance from zpos to the bottom
2574     thick: thickness
2575     bevel: the amount to raise the back vertex to account for arch beveling
2576     vll: the number of vertexes already in the mesh. len(mesh.verts) should give this number
2577     faceExclude: list of faces to exclude from the faces list.  0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
2578     xBevScl: how much to divide the end (+- x axis) bevel dimensions.  Set to current average radius to compensate for angular distortion on curved blocks
2579     """
2580
2581     points = []
2582     faces = []
2583     faceinclude = [1 for x in range(6)]
2584     for x in FaceExclude:
2585         faceinclude[x] = 0
2586     Top = zpos + ztop
2587     Btm = zpos - zbtm
2588     Wid = width / 2.
2589     Thk = thick / 2.
2590
2591     # The front top point
2592     points.append([xpos, Thk, Top])
2593     # The front left point
2594     points.append([xpos - Wid, Thk, zpos])
2595     # The front bottom point
2596     points.append([xpos, Thk, Btm])
2597     # The front right point
2598     points.append([xpos + Wid, Thk, zpos])
2599
2600     MirrorPoints = []
2601     for i in points:
2602         MirrorPoints.append([i[0], -i[1], i[2]])
2603     points += MirrorPoints
2604     points[6][2] += bevel
2605
2606     faces.append([3, 2, 1, 0])
2607     faces.append([4, 5, 6, 7])
2608     faces.append([4, 7, 3, 0])
2609     faces.append([5, 4, 0, 1])
2610     faces.append([6, 5, 1, 2])
2611     faces.append([7, 6, 2, 3])
2612     # Offset the vertex numbers by the number of verticies already in the list
2613     for i in range(len(faces)):
2614         for j in range(len(faces[i])):
2615             faces[i][j] += vll
2616
2617     return points, faces
2618
2619
2620 # class openings in the wall
2621 class opening:
2622     __doc__ = """\
2623     This is the class for holding the data for the openings in the wall.
2624     It has methods for returning the edges of the opening for any given position value,
2625     as well as bevel settings and top and bottom positions.
2626     It stores the 'style' of the opening, and all other pertinent information.
2627     """
2628     # x = 0. # x position of the opening
2629     # z = 0. # x position of the opening
2630     # w = 0. # width of the opening
2631     # h = 0. # height of the opening
2632     r = 0  # top radius of the arch (derived from 'v')
2633     rl = 0  # lower radius of the arch (derived from 'vl')
2634     rt = 0  # top arch thickness
2635     rtl = 0  # lower arch thickness
2636     ts = 0  # Opening side thickness, if greater than average width, replaces it.
2637     c = 0  # top arch corner position (for low arches), distance from the top of the straight sides
2638     cl = 0  # lower arch corner position (for low arches), distance from the top of the straight sides
2639     # form = 0 # arch type (unused for now)
2640     # b = 0. # back face bevel distance, like an arrow slit
2641     v = 0.  # top arch height
2642     vl = 0.  # lower arch height
2643     # variable "s" is used for "side" in the "edge" function.
2644     # it is a signed int, multiplied by the width to get + or - of the center
2645
2646     def btm(self):
2647         if self.vl <= self.w / 2:
2648             return self.z - self.h / 2 - self.vl - self.rtl
2649         else:
2650             return self.z - sqrt((self.rl + self.rtl)**2 - (self.rl - self.w / 2)**2) - self.h / 2
2651
2652     def top(self):
2653         if self.v <= self.w / 2:
2654             return self.z + self.h / 2 + self.v + self.rt
2655         else:
2656             return sqrt((self.r + self.rt)**2 - (self.r - self.w / 2)**2) + self.z + self.h / 2
2657
2658     # crits returns the critical split points, or discontinuities, used for making rows
2659     def crits(self):
2660         critlist = []
2661         if self.vl > 0:  # for lower arch
2662             # add the top point if it is pointed
2663             #if self.vl >= self.w/2.: critlist.append(self.btm())
2664             if self.vl < self.w / 2.:  # else: for low arches, with wedge blocks under them
2665                 # critlist.append(self.btm())
2666                 critlist.append(self.z - self.h / 2 - self.cl)
2667
2668         if self.h > 0:  # if it has a height, append points at the top and bottom of the main square section
2669             critlist += [self.z - self.h / 2, self.z + self.h / 2]
2670         else:  # otherwise, append just one in the center
2671             critlist.append(self.z)
2672
2673         if self.v > 0:  # for the upper arch
2674             if self.v < self.w / 2.:  # add the splits for the upper wedge blocks, if needed
2675                 critlist.append(self.z + self.h / 2 + self.c)
2676                 # critlist.append(self.top())
2677             # otherwise just add the top point, if it is pointed
2678             # else: critlist.append(self.top())
2679
2680         return critlist
2681
2682     #
2683     # get the side position of the opening.
2684     # ht is the z position; s is the side: 1 for right, -1 for left
2685     # if the height passed is above or below the opening, return None
2686     #
2687     def edgeS(edgeParms, ht, s):
2688
2689         wallTopZ = dims['t']
2690         wallHalfH = edgeParms.h / 2
2691         wallHalfW = edgeParms.w / 2
2692         wallBase = edgeParms.z
2693
2694         # set the row radius: 1 for standard wall (flat)
2695         if settings['Radial']:
2696             if settings['Slope']:
2697                 r1 = abs(wallTopZ * sin(ht * cPie / (wallTopZ * 2)))
2698             else:
2699                 r1 = abs(ht)
2700         else:
2701             r1 = 1
2702
2703         # Go through all the options, and return the correct value
2704         if ht < edgeParms.btm():  # too low
2705             return None
2706         elif ht > edgeParms.top():  # too high
2707             return None
2708
2709         # in this range, pass the lower arch info
2710         elif ht <= wallBase - wallHalfH - edgeParms.cl:
2711             if edgeParms.vl > wallHalfW:
2712                 circVal = circ(ht - wallBase + wallHalfH, edgeParms.rl + edgeParms.rtl)
2713                 if circVal == None:
2714                     return None
2715                 else:
2716                     return edgeParms.x + s * (wallHalfW - edgeParms.rl + circVal) / r1
2717             else:
2718                 circVal = circ(ht - wallBase + wallHalfH + edgeParms.vl - edgeParms.rl, edgeParms.rl + edgeParms.rtl)
2719                 if circVal == None:
2720                     return None
2721                 else:
2722                     return edgeParms.x + s * circVal / r1
2723
2724         # in this range, pass the top arch info
2725         elif ht >= wallBase + wallHalfH + edgeParms.c:
2726             if edgeParms.v > wallHalfW:
2727                 circVal = circ(ht - wallBase - wallHalfH, edgeParms.r + edgeParms.rt)
2728                 if circVal == None:
2729                     return None
2730                 else:
2731                     return edgeParms.x + s * (wallHalfW - edgeParms.r + circVal) / r1
2732             else:
2733                 circVal = circ(ht - (wallBase + wallHalfH + edgeParms.v - edgeParms.r), edgeParms.r + edgeParms.rt)
2734                 if circVal == None:
2735                     return None
2736                 else:
2737                     return edgeParms.x + s * circVal / r1
2738
2739         # in this range pass the lower corner edge info
2740         elif ht <= wallBase - wallHalfH:
2741             d = sqrt(edgeParms.rtl**2 - edgeParms.cl**2)
2742             if edgeParms.cl > edgeParms.rtl / sqrt(2.):
2743                 return edgeParms.x + s * (wallHalfW + (wallBase - wallHalfH - ht) * d / edgeParms.cl) / r1
2744             else:
2745                 return edgeParms.x + s * (wallHalfW + d) / r1
2746
2747         # in this range pass the upper corner edge info
2748         elif ht >= wallBase + wallHalfH:
2749             d = sqrt(edgeParms.rt**2 - edgeParms.c**2)
2750             if edgeParms.c > edgeParms.rt / sqrt(2.):
2751                 return edgeParms.x + s * (wallHalfW + (ht - wallBase - wallHalfH) * d / edgeParms.c) / r1
2752             else:
2753                 return edgeParms.x + s * (wallHalfW + d) / r1
2754
2755         # in this range, pass the middle info (straight sides)
2756         else:
2757             return edgeParms.x + s * wallHalfW / r1
2758
2759     # get the top or bottom of the opening
2760     # ht is the x position; archSide: 1 for top, -1 for bottom
2761     #
2762     def edgeV(self, ht, archSide):
2763         wallTopZ = dims['t']
2764         dist = abs(self.x - ht)
2765
2766         def radialAdjust(dist, sideVal):  # adjust distance and for radial geometry.
2767             if settings['Radial']:
2768                 if settings['Slope']:
2769                     dist = dist * abs(wallTopZ * sin(sideVal * cPie / (wallTopZ * 2)))
2770                 else:
2771                     dist = dist * sideVal
2772             return dist
2773
2774         if archSide > 0:  # check top down
2775             # hack for radialized masonry, import approx Z instead of self.top()
2776             dist = radialAdjust(dist, self.top())
2777
2778             # no arch on top, flat
2779             if not self.r:
2780                 return self.z + self.h / 2
2781
2782             # pointed arch on top
2783             elif self.v > self.w / 2:
2784                 circVal = circ(dist - self.w / 2 + self.r, self.r + self.rt)
2785                 if circVal == None:
2786                     return 0.0
2787                 else:
2788                     return self.z + self.h / 2 + circVal
2789
2790             # domed arch on top
2791             else:
2792                 circVal = circ(dist, self.r + self.rt)
2793                 if circVal == None:
2794                     return 0.0
2795                 else:
2796                     return self.z + self.h / 2 + self.v - self.r + circVal
2797
2798         else:  # check bottom up
2799             # hack for radialized masonry, import approx Z instead of self.top()
2800             dist = radialAdjust(dist, self.btm())
2801
2802             # no arch on bottom
2803             if not self.rl:
2804                 return self.z - self.h / 2
2805
2806             # pointed arch on bottom
2807             elif self.vl > self.w / 2:
2808                 circVal = circ(dist - self.w / 2 + self.rl, self.rl + self.rtl)
2809                 if circVal == None:
2810                     return 0.0
2811                 else:
2812                     return self.z - self.h / 2 - circVal
2813
2814             # old conditional? if (dist-self.w/2+self.rl)<=(self.rl+self.rtl):
2815             # domed arch on bottom
2816             else:
2817                 circVal = circ(dist, self.rl + self.rtl)  # dist-self.w/2+self.rl
2818                 if circVal == None:
2819                     return 0.0
2820                 else:
2821                     return self.z - self.h / 2 - self.vl + self.rl - circVal
2822
2823     #
2824     def edgeBev(self, ht):
2825         wallTopZ = dims['t']
2826         if ht > (self.z + self.h / 2):
2827             return 0.0
2828         if ht < (self.z - self.h / 2):
2829             return 0.0
2830         if settings['Radial']:
2831             if settings['Slope']:
2832                 r1 = abs(wallTopZ * sin(ht * cPie / (wallTopZ * 2)))
2833             else:
2834                 r1 = abs(ht)
2835         else:
2836             r1 = 1
2837         bevel = self.b / r1
2838         return bevel
2839
2840     #
2841     ##
2842     #
2843
2844     def __init__(self, xpos, zpos, width, height, archHeight=0, archThk=0,
2845                  archHeightLower=0, archThkLower=0, bevel=0, edgeThk=0):
2846         self.x = float(xpos)
2847         self.z = float(zpos)
2848         self.w = float(width)
2849         self.h = float(height)
2850         self.rt = archThk
2851         self.rtl = archThkLower
2852         self.v = archHeight
2853         self.vl = archHeightLower
2854
2855         # find the upper arch radius
2856         if archHeight >= width / 2:
2857             # just one arch, low long
2858             self.r = (self.v**2) / self.w + self.w / 4
2859         elif archHeight <= 0:
2860             # No arches
2861             self.r = 0
2862             self.v = 0
2863         else:
2864             # Two arches
2865             self.r = (self.w**2) / (8 * self.v) + self.v / 2.
2866             self.c = self.rt * cos(atan(self.w / (2 * (self.r - self.v))))
2867
2868         # find the lower arch radius
2869         if archHeightLower >= width / 2:
2870             self.rl = (self.vl**2) / self.w + self.w / 4
2871         elif archHeightLower <= 0:
2872             self.rl = 0
2873             self.vl = 0
2874         else:
2875             self.rl = (self.w**2) / (8 * self.vl) + self.vl / 2.
2876             self.cl = self.rtl * cos(atan(self.w / (2 * (self.rl - self.vl))))
2877
2878         # self.form = something?
2879         self.b = float(bevel)
2880         self.ts = edgeThk
2881
2882 #
2883 #
2884 # class for the whole wall boundaries; a sub-class of "opening"
2885
2886
2887 class OpeningInv(opening):
2888     # this is supposed to switch the sides of the opening
2889     # so the wall will properly enclose the whole wall.
2890
2891     def edgeS(self, ht, s):
2892         return opening.edgeS(self, ht, -s)
2893
2894     def edgeV(self, ht, s):
2895         return opening.edgeV(self, ht, -s)
2896
2897 # class rows in the wall
2898
2899
2900 class rowOb:
2901     __doc__ = """\
2902     This is the class for holding the data for individual rows of blocks.
2903     each row is required to have some edge blocks, and can also have
2904     intermediate sections of "normal" blocks.
2905     """
2906
2907     #z = 0.
2908     #h = 0.
2909     radius = 1
2910     rowEdge = 0
2911
2912     def FillBlocks(self):
2913         wallTopZ = dims['t']
2914
2915         # Set the radius variable, in the case of radial geometry
2916         if settings['Radial']:
2917             if settings['Slope']:
2918                 self.radius = wallTopZ * (sin(self.z * cPie / (wallTopZ * 2)))
2919             else:
2920                 self.radius = self.z
2921
2922         # initialize internal variables from global settings
2923         SetH = settings['h']
2924         # no HVar?
2925         SetWid = settings['w']
2926         SetWidVar = settings['wv']
2927         SetGrt = settings['g']
2928         SetDepth = settings['d']
2929         SetDepthVar = settings['dv']
2930
2931         # height weight, make shorter rows have narrower blocks, and vice-versa
2932         rowHWt = ((self.h / SetH - 1) * ROW_H_WEIGHT + 1)
2933
2934         # set variables for persistent values: loop optimization, readability, single ref for changes.
2935         avgDist = rowHWt * SetWid / self.radius
2936         minDist = SetWid / self.radius
2937         deviation = rowHWt * SetWidVar / self.radius
2938         grtOffset = SetGrt / (2 * self.radius)
2939
2940         # init loop variables that may change...
2941         blockGap = SetGrt / self.radius
2942         ThisBlockHeight = self.h
2943         ThisBlockDepth = SetDepth + (rndd() * SetDepthVar)
2944
2945         for segment in self.RowSegments:
2946             divs = fill(segment[0] + grtOffset, segment[1] - grtOffset, avgDist, minDist, deviation)
2947
2948             # loop through the divisions, adding blocks for each one
2949             for i in range(len(divs) - 1):
2950                 ThisBlockx = (divs[i] + divs[i + 1]) / 2
2951                 ThisBlockw = divs[i + 1] - divs[i] - blockGap
2952
2953                 self.BlocksNorm.append([ThisBlockx, self.z, ThisBlockw, ThisBlockHeight, ThisBlockDepth, None])
2954
2955                 if SetDepthVar:  # vary depth
2956                     ThisBlockDepth = SetDepth + (rndd() * SetDepthVar)
2957
2958     def __init__(self, centerheight, rowheight, rowEdge=0):
2959         self.z = float(centerheight)
2960         self.h = float(rowheight)
2961         self.rowEdge = float(rowEdge)
2962
2963         # THIS INITILIZATION IS IMPORTANT!  OTHERWISE ALL OBJECTS WILL HAVE THE SAME LISTS!
2964         self.BlocksEdge = []
2965         self.RowSegments = []
2966         self.BlocksNorm = []
2967
2968 #
2969
2970
2971 def arch(ra, rt, x, z, archStart, archEnd, bevel, bevAngle, vll):
2972     __doc__ = """\
2973     Makes a list of faces and vertexes for arches.
2974     ra: the radius of the arch, to the center of the bricks
2975     rt: the thickness of the arch
2976     x: x center location of the circular arc, as if the arch opening were centered on x = 0
2977     z: z center location of the arch
2978     anglebeg: start angle of the arch, in radians, from vertical?
2979     angleend: end angle of the arch, in radians, from vertical?
2980     bevel: how much to bevel the inside of the arch.
2981     vll: how long is the vertex list already?
2982     """
2983     avlist = []
2984     aflist = []
2985
2986     # initialize internal variables for global settings
2987     SetH = settings['h']
2988     SetWid = settings['w']
2989     SetWidVar = settings['wv']
2990     SetGrt = settings['g']
2991     SetDepth = settings['d']
2992     SetDepthVar = settings['dv']
2993     wallTopZ = dims['t']
2994
2995     wallDome = settings['Radial']
2996
2997     ArchInner = ra - rt / 2
2998     ArchOuter = ra + rt / 2 - SetGrt
2999
3000     DepthBack = -SetDepth / 2 - rndc() * SetDepthVar
3001     DepthFront = SetDepth / 2 + rndc() * SetDepthVar
3002
3003     # there's something wrong here...
3004     if wallDome:
3005         subdivision = settings['sdv']
3006     else:
3007         subdivision = 0.12
3008
3009     blockGap = SetGrt / (2 * ra)  # grout offset
3010     # set up the offsets, it will be the same for every block
3011     offsets = ([[0] * 2 + [bevel]] + [[0] * 3] * 3) * 2
3012
3013     # make the divisions in the "length" of the arch
3014     divs = fill(archStart, archEnd, settings['w'] / ra, settings['w'] / ra, settings['wv'] / ra)
3015
3016     for i in range(len(divs) - 1):
3017          # modify block offsets for bevel.
3018         if i == 0:
3019             ThisOffset = offsets[:]
3020             pointsToAffect = (0, 2, 3)
3021
3022             for num in pointsToAffect:
3023                 offsets[num] = ThisOffset[num][:]
3024                 offsets[num][0] += bevAngle
3025         elif i == len(divs) - 2:
3026             ThisOffset = offsets[:]
3027             pointsToAffect = (4, 6, 7)
3028
3029             for num in pointsToAffect:
3030                 offsets[num] = ThisOffset[num][:]
3031                 offsets[num][0] -= bevAngle
3032         else:
3033             ThisOffset = offsets
3034
3035         geom = MakeABlock([divs[i] + blockGap, divs[i + 1] - blockGap, ArchInner, ArchOuter, DepthBack, DepthFront],
3036                           subdivision, len(avlist) + vll, ThisOffset, [])
3037
3038         avlist += geom[0]
3039         aflist += geom[1]
3040
3041         if SetDepthVar:  # vary depth
3042             DepthBack = -SetDepth / 2 - rndc() * SetDepthVar
3043             DepthFront = SetDepth / 2 + rndc() * SetDepthVar
3044
3045     for i, vert in enumerate(avlist):
3046         v0 = vert[2] * sin(vert[0]) + x
3047         v1 = vert[1]
3048         v2 = vert[2] * cos(vert[0]) + z
3049
3050         if wallDome:
3051             r1 = wallTopZ * (sin(v2 * cPie / (wallTopZ * 2)))
3052         #            if settings['Slope']: r1 = wallTopZ*(sin(v2*cPie/(wallTopZ*2)))
3053         #            else: r1 = v2 # disc
3054             v0 = v0 / r1
3055
3056         avlist[i] = [v0, v1, v2]
3057
3058     return (avlist, aflist)
3059
3060
3061 #################################################################
3062 #
3063 # Make wedge blocks for openings.
3064 #
3065 #  examples:
3066 #   wedgeBlocks(row, LeftWedgeEdge, LNerEdge, LEB, r1)
3067 #   wedgeBlocks(row, RNerEdge, RightWedgeEdge, rSide, r1)
3068 #
3069 def wedgeBlocks(row, opening, leftPos, rightPos, edgeSide, r1):
3070
3071     wedgeWRad = settings['w'] / r1
3072
3073     wedgeEdges = fill(leftPos, rightPos, wedgeWRad, wedgeWRad, settings['wv'] / r1)
3074
3075     blockDepth = settings['d']
3076     blockDepthV = settings['dv']
3077     blockGap = settings['g'] / r1
3078
3079     for i in range(len(wedgeEdges) - 1):
3080         x = (wedgeEdges[i + 1] + wedgeEdges[i]) / 2
3081         w = wedgeEdges[i + 1] - wedgeEdges[i] - blockGap
3082         halfBW = w / 2
3083
3084         ThisBlockDepth = blockDepth + rndd() * blockDepthV
3085
3086         LVert = -((row.z - ((row.h / 2) * edgeSide)) - opening.edgeV(x - halfBW, edgeSide))
3087         #        LVert =  -( row.z - (row.h/2)*edgeSide - (opening.edgeV(x-halfBW,edgeSide)))
3088         RightVertOffset = -(row.z - (row.h / 2) * edgeSide - opening.edgeV(x + halfBW, edgeSide))
3089
3090         # Wedges are on top = Voff, blank, Voff, blank
3091         # Wedges are on btm = blank, Voff, blank, Voff
3092         ThisBlockOffsets = [[0, 0, LVert]] * 2 + [[0] * 3] * 2 + [[0, 0, RightVertOffset]] * 2
3093         # Instert or append "blank" for top or bottom wedges.
3094         if edgeSide == 1:
3095             ThisBlockOffsets = ThisBlockOffsets + [[0] * 3] * 2
3096         else:
3097             ThisBlockOffsets = [[0] * 3] * 2 + ThisBlockOffsets
3098
3099         row.BlocksEdge.append([x, row.z, w, row.h, ThisBlockDepth, ThisBlockOffsets])
3100
3101
3102 ############################################################
3103 #
3104 #
3105     # set end blocks
3106     # check for openings, record top and bottom of row for right and left of each
3107     # if both top and bottom intersect create blocks on each edge, appropriate to the size of the overlap
3108     # if only one side intersects, run fill to get edge positions, but this should never happen
3109     #
3110 #
3111 def rowProcessing(row, holeList, WallBoundaries):
3112
3113     if settings['Radial']:  # radial stonework sets the row radius
3114         if settings['Slope']:
3115             r1 = abs(dims['t'] * sin(row.z * cPie / (dims['t'] * 2)))
3116         else:
3117             r1 = abs(row.z)
3118     else:
3119         r1 = 1
3120
3121     # set block working values
3122     blockWidth = settings['w']
3123     blockWVar = settings['wv']
3124     blockDepth = settings['d']
3125     blockDVar = settings['dv']
3126
3127     blockGap = settings['g'] / r1
3128     blockHMin = BLOCK_MIN + blockGap
3129
3130     # set row working values
3131     rowH = row.h
3132     rowH2 = rowH / 2
3133     rowEdge = row.rowEdge / r1
3134     rowStart = dims['s'] + rowEdge
3135     # shouldn't rowEnd be minus rowEdge?
3136     rowEnd = dims['e'] + rowEdge
3137     rowTop = row.z + rowH2
3138     rowBtm = row.z - rowH2
3139
3140     # left and right wall limits for top and bottom of row.
3141     edgetop = [[rowStart, WallBoundaries], [rowEnd, WallBoundaries]]
3142     edgebtm = [[rowStart, WallBoundaries], [rowEnd, WallBoundaries]]
3143
3144     for hole in holeList:
3145         # check the top and bottom of the row, looking at the opening from the right
3146         holeEdge = [hole.edgeS(rowTop, -1), hole.edgeS(rowBtm, -1)]
3147
3148         # If either one hit the opening, make split points for the side of the opening.
3149         if holeEdge[0] or holeEdge[1]:
3150             holeEdge += [hole.edgeS(rowTop, 1), hole.edgeS(rowBtm, 1)]
3151
3152             # If one of them missed for some reason, set that value to
3153             # the middle of the opening.
3154             for i, pos in enumerate(holeEdge):
3155                 if pos == None:
3156                     holeEdge[i] = hole.x
3157
3158             # add the intersects to the list of edge points
3159             edgetop.append([holeEdge[0], hole])
3160             edgetop.append([holeEdge[2], hole])
3161             edgebtm.append([holeEdge[1], hole])
3162             edgebtm.append([holeEdge[3], hole])
3163
3164     # make the walls in order, sort the intersects.
3165     #  remove edge points that are out of order;
3166     #  else the "oddity" where opening overlap creates blocks inversely.
3167     edgetop.sort()
3168     edgebtm.sort()
3169
3170     # These two loops trim the edges to the limits of the wall.
3171     # This way openings extending outside the wall don't enlarge the wall.
3172     while True:
3173         try:
3174             if (edgetop[-1][0] > rowEnd) or (edgebtm[-1][0] > rowEnd):
3175                 edgetop[-2:] = []
3176                 edgebtm[-2:] = []
3177             else:
3178                 break
3179         except IndexError:
3180             break
3181     # still trimming the edges...
3182     while True:
3183         try:
3184             if (edgetop[0][0] < rowStart) or (edgebtm[0][0] < rowStart):
3185                 edgetop[:2] = []
3186                 edgebtm[:2] = []
3187             else:
3188                 break
3189         except IndexError:
3190             break
3191
3192     # finally, make edge blocks and rows!
3193
3194     # Process each section, a pair of points in edgetop,
3195     # and place the edge blocks and inbetween normal block zones into the row object.
3196
3197     # maximum distance to span with one block
3198     MaxWid = (blockWidth + blockWVar) / r1
3199
3200     for OpnSplitNo in range(int(len(edgetop) / 2)):
3201         lEdgeIndx = 2 * OpnSplitNo
3202         rEdgeIndx = lEdgeIndx + 1
3203
3204         leftOpening = edgetop[lEdgeIndx][1]
3205         rightOpening = edgetop[rEdgeIndx][1]
3206
3207         # find the difference between the edge top and bottom on both sides
3208         LTop = edgetop[lEdgeIndx][0]
3209         LBtm = edgebtm[lEdgeIndx][0]
3210         RTop = edgetop[rEdgeIndx][0]
3211         RBtm = edgebtm[rEdgeIndx][0]
3212         LDiff = LBtm - LTop
3213         RDiff = RTop - RBtm
3214
3215         # set side edge limits from top and bottom
3216         if LDiff > 0:  # if furthest edge is top,
3217             LEB = 1
3218             LFarEdge = LTop  # The furthest edge
3219             LNerEdge = LBtm  # the nearer edge
3220         else:  # furthest edge is bottom
3221             LEB = -1
3222             LFarEdge = LBtm
3223             LNerEdge = LTop
3224
3225         if RDiff > 0:  # if furthest edge is top,
3226             rSide = 1
3227             RFarEdge = RTop  # The furthest edge
3228             RNerEdge = RBtm  # the nearer edge
3229         else:  # furthest edge is bottom
3230             rSide = -1
3231             RFarEdge = RBtm  # The furthest edge
3232             RNerEdge = RTop  # the nearer edge
3233
3234         blockXx = RNerEdge - LNerEdge  # The space between the closest edges of the openings in this section of the row
3235         blockXm = (RNerEdge + LNerEdge) / 2  # The mid point between the nearest edges
3236
3237         # check the left and right sides for wedge blocks
3238         # find the edge of the correct side, offset for minimum block height.  The LEB decides top or bottom
3239         ZPositionCheck = row.z + (rowH2 - blockHMin) * LEB
3240         # edgeS may return "None"
3241         LeftWedgeEdge = leftOpening.edgeS(ZPositionCheck, 1)
3242
3243         if (abs(LDiff) > blockWidth) or (not LeftWedgeEdge):
3244             # make wedge blocks
3245             if not LeftWedgeEdge:
3246                 LeftWedgeEdge = leftOpening.x
3247             wedgeBlocks(row, leftOpening, LeftWedgeEdge, LNerEdge, LEB, r1)
3248             # set the near and far edge settings to vertical, so the other edge blocks don't interfere
3249             LFarEdge, LTop, LBtm = LNerEdge, LNerEdge, LNerEdge
3250             LDiff = 0
3251
3252         # Now do the wedge blocks for the right, same drill... repeated code?
3253         # find the edge of the correct side, offset for minimum block height.
3254         ZPositionCheck = row.z + (rowH2 - blockHMin) * rSide
3255         # edgeS may return "None"
3256         RightWedgeEdge = rightOpening.edgeS(ZPositionCheck, -1)
3257         if (abs(RDiff) > blockWidth) or (not RightWedgeEdge):
3258             # make wedge blocks
3259             if not RightWedgeEdge:
3260                 RightWedgeEdge = rightOpening.x
3261             wedgeBlocks(row, rightOpening, RNerEdge, RightWedgeEdge, rSide, r1)
3262
3263             # set the near and far edge settings to vertical, so the other edge blocks don't interfere
3264             RFarEdge, RTop, RBtm = RNerEdge, RNerEdge, RNerEdge
3265             RDiff = 0
3266
3267         # Single block - needed for arch "point" (keystone).
3268         if blockXx < MaxWid:
3269             x = (LNerEdge + RNerEdge) / 2.
3270             w = blockXx
3271             ThisBlockDepth = rndd() * blockDVar + blockDepth
3272             BtmOff = LBtm - LNerEdge
3273             TopOff = LTop - LNerEdge
3274             ThisBlockOffsets = [[BtmOff, 0, 0]] * 2 + [[TopOff, 0, 0]] * 2
3275             BtmOff = RBtm - RNerEdge
3276             TopOff = RTop - RNerEdge
3277             ThisBlockOffsets += [[BtmOff, 0, 0]] * 2 + [[TopOff, 0, 0]] * 2
3278
3279             pointsToAffect = (0, 2)
3280             bevelBlockOffsets(ThisBlockOffsets, leftOpening.edgeBev(rowTop), pointsToAffect)
3281
3282             pointsToAffect = (4, 6)
3283             bevelBlockOffsets(ThisBlockOffsets, -rightOpening.edgeBev(rowTop), pointsToAffect)
3284
3285             row.BlocksEdge.append([x, row.z, w, rowH, ThisBlockDepth, ThisBlockOffsets])
3286             continue
3287
3288         # must be two or more blocks
3289
3290         # Left offsets
3291         BtmOff = LBtm - LNerEdge
3292         TopOff = LTop - LNerEdge
3293         leftOffsets = [[BtmOff, 0, 0]] * 2 + [[TopOff, 0, 0]] * 2 + [[0] * 3] * 4
3294         bevelL = leftOpening.edgeBev(rowTop)
3295
3296         pointsToAffect = (0, 2)
3297         bevelBlockOffsets(leftOffsets, bevelL, pointsToAffect)
3298
3299         # Right offsets
3300         BtmOff = RBtm - RNerEdge
3301         TopOff = RTop - RNerEdge
3302         rightOffsets = [[0] * 3] * 4 + [[BtmOff, 0, 0]] * 2 + [[TopOff, 0, 0]] * 2
3303         bevelR = rightOpening.edgeBev(rowTop)
3304
3305         pointsToAffect = (4, 6)
3306         bevelBlockOffsets(rightOffsets, -bevelR, pointsToAffect)
3307
3308         if blockXx < MaxWid * 2:  # only two blocks?
3309             # div is the x position of the dividing point between the two bricks
3310             div = blockXm + (rndd() * blockWVar) / r1
3311
3312             # set the x position and width for the left block
3313             x = (div + LNerEdge) / 2 - blockGap / 4
3314             w = (div - LNerEdge) - blockGap / 2
3315             ThisBlockDepth = rndd() * blockDVar + blockDepth
3316             # For reference: EdgeBlocks = [[x,z,w,h,d,[corner offset matrix]],[etc.]]
3317             row.BlocksEdge.append([x, row.z, w, rowH, ThisBlockDepth, leftOffsets])
3318
3319             # Initialize for the block on the right side
3320             x = (div + RNerEdge) / 2 + blockGap / 4
3321             w = (RNerEdge - div) - blockGap / 2
3322             ThisBlockDepth = rndd() * blockDVar + blockDepth
3323             row.BlocksEdge.append([x, row.z, w, rowH, ThisBlockDepth, rightOffsets])
3324             continue
3325
3326         # more than two blocks in the row, and no wedge blocks
3327
3328         # make Left edge block
3329         # set the x position and width for the block
3330         widOptions = [blockWidth, bevelL + blockWidth, leftOpening.ts]
3331         baseWidMax = max(widOptions)
3332         w = baseWidMax + row.rowEdge + (rndd() * blockWVar)
3333         widOptions[0] = blockWidth
3334         widOptions[2] = w
3335         w = max(widOptions) / r1 - blockGap
3336         x = w / 2 + LNerEdge + blockGap / 2
3337         BlockRowL = x + w / 2
3338         ThisBlockDepth = rndd() * blockDVar + blockDepth
3339         row.BlocksEdge.append([x, row.z, w, rowH, ThisBlockDepth, leftOffsets])
3340
3341         # make Right edge block
3342         # set the x position and width for the block
3343         widOptions = [blockWidth, bevelR + blockWidth, rightOpening.ts]
3344         baseWidMax = max(widOptions)
3345         w = baseWidMax + row.rowEdge + (rndd() * blockWVar)
3346         widOptions[0] = blockWidth
3347         widOptions[2] = w
3348         w = max(widOptions) / r1 - blockGap
3349         x = RNerEdge - w / 2 - blockGap / 2
3350         BlockRowR = x - w / 2
3351         ThisBlockDepth = rndd() * blockDVar + blockDepth
3352         row.BlocksEdge.append([x, row.z, w, rowH, ThisBlockDepth, rightOffsets])
3353
3354         row.RowSegments.append([BlockRowL, BlockRowR])
3355
3356
3357 #####################################
3358 #
3359 # Makes arches for the top and bottom
3360 # hole is the "wall opening" that the arch is for.
3361 #
3362 def archGeneration(hole, vlist, flist, sideSign):
3363
3364     avl