added file location messeges & disabled handler that was crashing on reload or newfile
[blender-addons-contrib.git] / add_mesh_walls / Blocks.py
1 # ***** BEGIN GPL LICENSE BLOCK *****
2 #
3 # This program is free software; you may redistribute it, and/or
4 # modify it, under the terms of the GNU General Public License
5 # as published by the Free Software Foundation - either version 2
6 # of the License, or (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, write to:
15 #
16 #   the Free Software Foundation Inc.
17 #   51 Franklin Street, Fifth Floor
18 #   Boston, MA 02110-1301, USA
19 #
20 # or go online at: http://www.gnu.org/licenses/ to view license options.
21 #
22 # ***** END GPL LICENCE BLOCK *****
23
24
25 ##
26 #
27 # Module notes:
28 #
29 # Grout needs to be implemented.
30 # consider removing wedge crit for small "c" and "cl" values
31 # wrap around for openings on radial stonework?
32 # auto-clip wall edge to SMALL for radial and domes.
33 # unregister doesn't release all references.
34 # repeat for opening doesn't distribute evenly when radialized - see wrap around
35 #   note above.
36 # if opening width == indent*2 the edge blocks fail (row of blocks cross opening).
37 # if openings overlap fills inverse with blocks - see h/v slots.
38 # Negative grout width creates a pair of phantom blocks, seperated by grout
39 #   width, inside the edges.
40 # if block width variance is 0, and edging is on, right edge blocks create a "vertical seam".
41 #
42 ##
43
44 # <pep8-80 compliant>
45
46 import bpy, time, math
47 from random import random
48 from math import fmod, sqrt, sin, cos, atan
49
50 #A few constants
51 SMALL = 0.000000000001
52 NOTZERO = 0.01 # for values that must be != 0; see UI options/variables -
53 # sort of a bug to be fixed.
54 PI = math.pi
55
56 #global variables
57
58 #General masonry Settings
59 settings = {'w': 1.2, 'wv': 0.3, 'h': .6, 'hv': 0.3, 'd': 0.3, 'dv': 0.1,
60             'g': 0.1, 'gv': 0.07, 'gd': 0.01, 'gdv': 0.0, 'b': 0, 'bv': 0,
61             'f': 0.0, 'fv': 0.0, 't': 0.0, 'sdv': 0.1, 'hwt': 0.5, 'aln':0,
62             'wm': 0.8, 'hm': 0.3, 'dm':0.1,
63             'woff':0.0, 'woffv':0.0, 'eoff':0.3, 'eoffv':0.0, 'rwhl':1,
64             'hb':0, 'ht':0, 'ge':0, 'physics':0}
65 # 'w':width 'wv':widthVariation
66 # 'h':height 'hv':heightVariation
67 # 'd':depth 'dv':depthVariation
68 # 'g':grout 'gv':groutVariation 'gd':groutDepth 'gdv':groutDepthVariation
69 # 'b':bevel 'bv':bevelVariation
70 # 'f':flawSize 'fv':flawSizeVariation 'ff':flawFraction
71 # 't':taper
72 # 'sdv':subdivision(distance or angle)
73 # 'hwt':row height effect on block widths in the row (0=no effect,
74 #     1=1:1 relationship, negative values allowed, 0.5 works well)
75 # 'aln':alignment(0=none, 1=rows w/features, 2=features w/rows)
76 #     (currently un-used)
77 # 'wm':width minimum 'hm':height minimum 'dm':depth minimum
78 # 'woff':row start offset(fraction of width)
79 # 'woffv':width offset variation(fraction of width)
80 # 'eoff':edge offset 'eoffv':edge offset variation
81 # 'rwhl':row height lock(1 is all blocks in row have same height)
82 # 'hb':bottom row height 'ht': top row height 'ge': grout the edges
83 # 'physics': set up for physics
84
85 # dims = area of wall (face)
86 dims = {'s':0, 'e':PI*3/2, 'b':0.1, 't':12.3} # radial
87 # 's':start x or theta 'e':end x or theta 'b':bottom z or r 't':top z or r
88 # 'w' = e-s and h = t-b; calculated to optimize for various operations/usages
89 #dims = {'s':-12, 'e':15, 'w':27, 'b':-15., 't':15., 'h':30}
90 #dims = {'s':-bayDim/2, 'e':bayDim/2, 'b':-5., 't':10.} # bay settings?
91
92 radialized = 0 # Radiating from one point - round/disc; instead of square
93 slope = 0 # Warp/slope; curved over like a vaulted tunnel
94 # 'bigblock': merge adjacent blocks into single large blocks
95 bigBlock = 0 # Merge blocks
96
97 # Gaps in blocks for various apertures.
98 #openingSpecs = []
99 openingSpecs = [{'w':0.5, 'h':0.5, 'x':0.8, 'z':2.7, 'rp':1, 'b':0.0,
100                  'v':0, 'vl':0, 't':0, 'tl':0}]
101 # 'w': opening width, 'h': opening height,
102 # 'x': horizontal position, 'z': vertical position,
103 # 'rp': make multiple openings, with a spacing of x,
104 # 'b': bevel the opening, inside only, like an arrow slit.
105 # 'v': height of the top arch, 'vl':height of the bottom arch,
106 # 't': thickness of the top arch, 'tl': thickness of the bottom arch
107
108 # Add blocks to make platforms.
109 shelfExt = 0
110 #shelfSpecs = []
111 shelfSpecs = {'w':0.5, 'h':0.5, 'd': 0.3, 'x':0.8, 'z':2.7}
112 # 'w': block width, 'h': block height, 'd': block depth (shelf size; offset from wall)
113 # 'x': horizontal start position, 'z': vertical start position
114
115 # Add blocks to make steps.
116 stepMod = 0
117 stepSpecs = {'x':0.0, 'z':-10, 'w':10.0, 'h':10.0,
118     'v':0.7, 't':1.0, 'd':1.0 }
119 # 'x': horizontal start position, 'z': vertical start position,
120 # 'w': step area width, 'h': step area height,
121 # 'v': riser height, 't': tread width, 'd': block depth (step size; offset from wall)
122
123
124     #easier way to get to the random function
125 def rnd(): return random()
126
127     #random number from -0.5 to 0.5
128 def rndc(): return (random() - 0.5)
129
130     #random number from -1.0 to 1.0
131 def rndd(): return (random() - 0.5)*2.0
132
133
134 #Opening Test suite
135 #opening test function
136
137 def test(TestN = 13):
138     dims = {'s':-29., 'e':29., 'b':-6., 't':TestN*7.5}
139     openingSpecs = []
140     for i in range(TestN):
141         x = (random() - 0.5) * 6
142         z = i*7.5
143         v = .2 + i*(3./TestN)
144         vl = 3.2 - i*(3./TestN)
145         t = 0.3 + random()
146         tl = 0.3 + random()
147         rn = random()*2
148         openingSpecs += [{'w':3.1 + rn, 'h':0.3 + rn, 'x':float(x),
149                           'z':float(z), 'rp':0, 'b':0.,
150                           'v':float(v), 'vl':float(vl),
151                           't':float(t), 'tl':float(tl)}]
152     return dims, openingSpecs
153
154 #dims, openingSpecs = test(15)
155
156
157 #For filling a linear space with divisions
158 def fill(left, right, avedst, mindst=0.0, dev=0.0, pad=(0.0,0.0), num=0,
159          center=0):
160     __doc__ = """\
161     Fills a linear range with points and returns an ordered list of those points
162     including the end points.
163
164     left: the lower boundary
165     right: the upper boundary
166     avedst: the average distance between points
167     mindst: the minimum distance between points
168     dev: the maximum random deviation from avedst
169     pad: tends to move the points near the bounds right (positive) or
170         left (negative).
171         element 0 pads the lower bounds, element 1 pads the upper bounds
172     num: substitutes a numerical limit for the right limit.  fill will then make
173         a num+1 element list
174     center: flag to center the elements in the range, 0 == disabled
175         """
176
177     poslist = [left]
178     curpos = left+pad[0]
179
180     # Set offset by average spacing, then add blocks (fall through);
181     # if not at right edge.
182     if center:
183         curpos += ((right-left-mindst*2)%avedst)/2+mindst
184         if curpos-poslist[-1]<mindst: curpos = poslist[-1]+mindst+rnd()*dev/2
185
186         # clip to right edge.
187         if (right-curpos<mindst) or (right-curpos< mindst-pad[1]):
188             poslist.append(right)
189             return poslist
190
191         else: poslist.append(curpos)
192
193     #unused... for now.
194     if num:
195         idx = len(poslist)
196
197         while idx<num+1:
198             curpos += avedst+rndd()*dev
199             if curpos-poslist[-1]<mindst:
200                 curpos = poslist[-1]+mindst+rnd()*dev/2
201             poslist.append(curpos)
202             idx += 1
203
204         return poslist
205
206     # make block edges
207     else:
208         while True: # loop for blocks
209             curpos += avedst+rndd()*dev
210             if curpos-poslist[-1]<mindst:
211                 curpos = poslist[-1]+mindst+rnd()*dev/2
212             # close off edges at limit
213             if (right-curpos<mindst) or (right-curpos< mindst-pad[1]):
214                 poslist.append(right)
215                 return poslist
216
217             else: poslist.append(curpos)
218
219
220 #For generating block geometry
221 def MakeABlock(bounds, segsize, vll=0, Offsets=None, FaceExclude=[],
222                bevel=0, xBevScl=1):
223     __doc__ = """\
224     MakeABlock returns lists of points and faces to be made into a square
225             cornered block, subdivided along the length, with optional bevels.
226     bounds: a list of boundary positions:
227         0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
228     segsize: the maximum size before lengthwise subdivision occurs
229     vll: the number of vertexes already in the mesh. len(mesh.verts) should
230             give this number.
231     Offsets: list of coordinate delta values.
232         Offsets are lists, [x,y,z] in
233             [
234             0:left_bottom_back,
235             1:left_bottom_front,
236             2:left_top_back,
237             3:left_top_front,
238             4:right_bottom_back,
239             5:right_bottom_front,
240             6:right_top_back,
241             7:right_top_front,
242             ]
243     FaceExclude: list of faces to exclude from the faces list.  see bounds above for indices
244     xBevScl: how much to divide the end (+- x axis) bevel dimensions.  Set to current average radius to compensate for angular distortion on curved blocks
245     """
246
247     slices = fill(bounds[0], bounds[1], segsize, segsize, center=1)
248     points = []
249     faces = []
250
251     if Offsets == None:
252         points.append([slices[0],bounds[4],bounds[2]])
253         points.append([slices[0],bounds[5],bounds[2]])
254         points.append([slices[0],bounds[5],bounds[3]])
255         points.append([slices[0],bounds[4],bounds[3]])
256
257         for x in slices[1:-1]:
258             points.append([x,bounds[4],bounds[2]])
259             points.append([x,bounds[5],bounds[2]])
260             points.append([x,bounds[5],bounds[3]])
261             points.append([x,bounds[4],bounds[3]])
262
263         points.append([slices[-1],bounds[4],bounds[2]])
264         points.append([slices[-1],bounds[5],bounds[2]])
265         points.append([slices[-1],bounds[5],bounds[3]])
266         points.append([slices[-1],bounds[4],bounds[3]])
267
268     else:
269         points.append([slices[0]+Offsets[0][0],bounds[4]+Offsets[0][1],bounds[2]+Offsets[0][2]])
270         points.append([slices[0]+Offsets[1][0],bounds[5]+Offsets[1][1],bounds[2]+Offsets[1][2]])
271         points.append([slices[0]+Offsets[3][0],bounds[5]+Offsets[3][1],bounds[3]+Offsets[3][2]])
272         points.append([slices[0]+Offsets[2][0],bounds[4]+Offsets[2][1],bounds[3]+Offsets[2][2]])
273
274         for x in slices[1:-1]:
275             xwt = (x-bounds[0])/(bounds[1]-bounds[0])
276             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])
277             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])
278             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])
279             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])
280
281         points.append([slices[-1]+Offsets[4][0],bounds[4]+Offsets[4][1],bounds[2]+Offsets[4][2]])
282         points.append([slices[-1]+Offsets[5][0],bounds[5]+Offsets[5][1],bounds[2]+Offsets[5][2]])
283         points.append([slices[-1]+Offsets[7][0],bounds[5]+Offsets[7][1],bounds[3]+Offsets[7][2]])
284         points.append([slices[-1]+Offsets[6][0],bounds[4]+Offsets[6][1],bounds[3]+Offsets[6][2]])
285
286     faces.append([vll,vll+3,vll+2,vll+1])
287
288     for x in range(len(slices)-1):
289         faces.append([vll,vll+1,vll+5,vll+4])
290         vll+=1
291         faces.append([vll,vll+1,vll+5,vll+4])
292         vll+=1
293         faces.append([vll,vll+1,vll+5,vll+4])
294         vll+=1
295         faces.append([vll,vll-3,vll+1,vll+4])
296         vll+=1
297
298     faces.append([vll,vll+1,vll+2,vll+3])
299
300     return points, faces
301 #
302 #
303 #
304
305 #For generating Keystone Geometry
306 def MakeAKeystone(xpos, width, zpos, ztop, zbtm, thick, bevel, vll=0, FaceExclude=[], xBevScl=1):
307     __doc__ = """\
308     MakeAKeystone returns lists of points and faces to be made into a square cornered keystone, with optional bevels.
309     xpos: x position of the centerline
310     width: x width of the keystone at the widest point (discounting bevels)
311     zpos: z position of the widest point
312     ztop: distance from zpos to the top
313     zbtm: distance from zpos to the bottom
314     thick: thickness
315     bevel: the amount to raise the back vertex to account for arch beveling
316     vll: the number of vertexes already in the mesh. len(mesh.verts) should give this number
317     faceExclude: list of faces to exclude from the faces list.  0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
318     xBevScl: how much to divide the end (+- x axis) bevel dimensions.  Set to current average radius to compensate for angular distortion on curved blocks
319     """
320
321     points = []
322     faces = []
323     faceinclude = [1 for x in range(6)]
324     for x in FaceExclude: faceinclude[x]=0
325     Top = zpos + ztop
326     Btm = zpos - zbtm
327     Wid = width/2.
328     Thk = thick/2.
329
330     # The front top point
331     points.append([xpos, Thk, Top])
332     # The front left point
333     points.append([xpos-Wid, Thk, zpos])
334     # The front bottom point
335     points.append([xpos, Thk, Btm])
336     # The front right point
337     points.append([xpos+Wid, Thk, zpos])
338
339     MirrorPoints = []
340     for i in points:
341         MirrorPoints.append([i[0],-i[1],i[2]])
342     points += MirrorPoints
343     points[6][2] += bevel
344
345     faces.append([3,2,1,0])
346     faces.append([4,5,6,7])
347     faces.append([4,7,3,0])
348     faces.append([5,4,0,1])
349     faces.append([6,5,1,2])
350     faces.append([7,6,2,3])
351     # Offset the vertex numbers by the number of verticies already in the list
352     for i in range(len(faces)):
353         for j in range(len(faces[i])): faces[i][j] += vll
354
355     return points, faces
356
357
358 #for finding line/circle intercepts
359 def circ(offs=0.,r=1.):
360     __doc__ = """\
361     offs is the distance perpendicular to the line to the center of the circle
362     r is the radius of the circle
363     circ returns the distance paralell to the line to the center of the circle at the intercept.
364     """
365     offs = abs(offs)
366     if offs > r: return None
367     elif offs == r: return 0.
368     else: return sqrt(r**2 - offs**2)
369
370
371 #class openings in the wall
372 class opening:
373     __doc__ = """\
374     This is the class for holding the data for the openings in the wall.
375     It has methods for returning the edges of the opening for any given position value,
376     as well as bevel settings and top and bottom positions.
377     It stores the 'style' of the opening, and all other pertinent information.
378     """
379     # x = 0. # x position of the opening
380     # z = 0. # x position of the opening
381     # w = 0. # width of the opening
382     # h = 0. # height of the opening
383     r = 0  # top radius of the arch (derived from 'v')
384     rl = 0 # lower radius of the arch (derived from 'vl')
385     rt = 0 # top arch thickness
386     rtl = 0# lower arch thickness
387     ts = 0 # Opening side thickness, if greater than average width, replaces it.
388     c = 0  # top arch corner position (for low arches), distance from the top of the straight sides
389     cl = 0 # lower arch corner position (for low arches), distance from the top of the straight sides
390     # form = 0 # arch type (unused for now)
391     # b = 0. # back face bevel distance, like an arrow slit
392     v = 0. # top arch height
393     vl = 0.# lower arch height
394     # variable "s" is used for "side" in the "edge" function.
395     # it is a signed int, multiplied by the width to get + or - of the center
396
397     def btm(self):
398         if self.vl <= self.w/2 : return self.z-self.h/2-self.vl-self.rtl
399         else: return self.z - sqrt((self.rl+self.rtl)**2 - (self.rl - self.w/2 )**2)  - self.h/2
400
401
402     def top(self):
403         if self.v <= self.w/2 : return self.z+self.h/2+self.v+self.rt
404         else: return sqrt((self.r+self.rt)**2 - (self.r - self.w/2 )**2) + self.z + self.h/2
405
406
407     #crits returns the critical split points, or discontinuities, used for making rows
408     def crits(self):
409         critlist = []
410         if self.vl>0: # for lower arch
411             # add the top point if it is pointed
412             #if self.vl >= self.w/2.: critlist.append(self.btm())
413             if self.vl < self.w/2.:#else: for low arches, with wedge blocks under them
414                 #critlist.append(self.btm())
415                 critlist.append(self.z-self.h/2 - self.cl)
416
417         if self.h>0: # if it has a height, append points at the top and bottom of the main square section
418             critlist += [self.z-self.h/2,self.z+self.h/2]
419         else:  # otherwise, append just one in the center
420             critlist.append(self.z)
421
422         if self.v>0:  # for the upper arch
423             if self.v < self.w/2.: # add the splits for the upper wedge blocks, if needed
424                 critlist.append(self.z+self.h/2 + self.c)
425                 #critlist.append(self.top())
426             #otherwise just add the top point, if it is pointed
427             #else: critlist.append(self.top())
428
429         return critlist
430
431
432     # get the side position of the opening.
433     # ht is the z position; s is the side: 1 for right, -1 for left
434     # if the height passed is above or below the opening, return None
435     #
436     def edgeS(self, ht, s):
437         # set the row radius: 1 for standard wall (flat)
438         if radialized:
439             if slope: r1 = abs(dims['t']*sin(ht*PI/(dims['t']*2)))
440             else: r1 = abs(ht)
441         else: r1 = 1
442
443         #Go through all the options, and return the correct value
444         if ht < self.btm(): #too low
445             return None
446         elif ht > self.top(): #too high
447             return None
448
449         # Check for circ returning None - prevent TypeError (script failure) with float.
450
451         # in this range, pass the lower arch info
452         elif ht <= self.z-self.h/2-self.cl:
453             if self.vl > self.w/2:
454                 circVal = circ(ht-self.z+self.h/2,self.rl+self.rtl)
455                 if circVal == None:
456                     return None
457                 else: return self.x + s*(self.w/2.-self.rl+circVal)/r1
458             else:
459                 circVal = circ(ht-self.z+self.h/2+self.vl-self.rl,self.rl+self.rtl)
460                 if circVal == None:
461                     return None
462                 else: return self.x + s*circVal/r1
463
464         #in this range, pass the top arch info
465         elif ht >= self.z+self.h/2+self.c:
466             if self.v > self.w/2:
467                 circVal = circ(ht-self.z-self.h/2,self.r+self.rt)
468                 if circVal == None:
469                     return None
470                 else: return self.x + s*(self.w/2.-self.r+circVal)/r1
471             else:
472                 circVal = circ(ht-(self.z+self.h/2+self.v-self.r),self.r+self.rt)
473                 if circVal == None:
474                     return None
475                 else: return self.x + s*circVal/r1
476
477         #in this range pass the lower corner edge info
478         elif ht <= self.z-self.h/2:
479             d = sqrt(self.rtl**2 - self.cl**2)
480             if self.cl > self.rtl/sqrt(2.): return self.x + s*(self.w/2 + (self.z - self.h/2 - ht)*d/self.cl)/r1
481             else: return self.x + s*( self.w/2 + d )/r1
482
483         #in this range pass the upper corner edge info
484         elif ht >= self.z+self.h/2:
485             d = sqrt(self.rt**2 - self.c**2)
486             if self.c > self.rt/sqrt(2.): return self.x + s*(self.w/2 + (ht - self.z - self.h/2 )*d/self.c)/r1
487             else: return self.x + s*( self.w/2 + d )/r1
488
489         #in this range, pass the middle info (straight sides)
490         else: return self.x + s*self.w/2/r1
491
492
493     # get the top or bottom of the opening
494     # ht is the x position; s is the side: 1 for top, -1 for bottom
495     #
496     def edgeV(self, ht, s):
497         dist = abs(self.x-ht)
498         def radialAdjust(dist, sideVal):
499             """take the distance and adjust for radial geometry, return dist.
500             """
501             if radialized:
502                 if slope:
503                     dist = dist * abs(dims['t']*sin(sideVal*PI/(dims['t']*2)))
504                 else:
505                     dist = dist * sideVal
506             return dist
507
508         if s > 0 :#and (dist <= self.edgeS(self.z+self.h/2+self.c,1)-self.x): #check top down
509             #hack for radialized masonry, import approx Z instead of self.top()
510             dist = radialAdjust(dist, self.top())
511
512             #no arch on top, flat
513             if not self.r: return self.z+self.h/2
514
515             #pointed arch on top
516             elif self.v > self.w/2:
517                 circVal = circ(dist-self.w/2+self.r,self.r+self.rt)
518                 if circVal == None:
519                     return None
520                 else: return self.z+self.h/2+circVal
521
522             #domed arch on top
523             else:
524                 circVal = circ(dist,self.r+self.rt)
525                 if circVal == None:
526                     return None
527                 else: return self.z+self.h/2+self.v-self.r+circVal
528
529         else:#and (dist <= self.edgeS(self.z-self.h/2-self.cl,1)-self.x): #check bottom up
530             #hack for radialized masonry, import approx Z instead of self.top()
531             dist = radialAdjust(dist, self.btm())
532
533             #no arch on bottom
534             if not self.rl: return self.z-self.h/2
535
536             #pointed arch on bottom
537             elif self.vl > self.w/2:
538                 circVal = circ(dist-self.w/2+self.rl,self.rl+self.rtl)
539                 if circVal == None:
540                     return None
541                 else: return self.z-self.h/2-circVal
542
543             #old conditional? if (dist-self.w/2+self.rl)<=(self.rl+self.rtl):
544             #domed arch on bottom
545             else:
546                 circVal = circ(dist,self.rl+self.rtl) # dist-self.w/2+self.rl
547                 if circVal == None:
548                     return None
549                 else: return self.z-self.h/2-self.vl+self.rl-circVal
550
551     # and this never happens - but, leave it as failsafe :)
552         print("got all the way out of the edgeV!  Not good!")
553         print("opening x = ", self.x, ", opening z = ", self.z)
554         return 0.0
555     #
556     def edgeBev(self, ht):
557         if ht > (self.z + self.h/2): return 0.0
558         if ht < (self.z - self.h/2): return 0.0
559         if radialized:
560             if slope: r1 = abs(dims['t']*sin(ht*PI/(dims['t']*2)))
561             else: r1 = abs(ht)
562         else: r1 = 1
563         bevel = self.b / r1
564         return bevel
565 #
566 ##
567 #
568
569     def __init__(self, xpos, zpos, width, height, archHeight=0, archThk=0,
570                  archHeightLower=0, archThkLower=0, bevel=0, edgeThk=0):
571         self.x = float(xpos)
572         self.z = float(zpos)
573         self.w = float(width)
574         self.h = float(height)
575         self.rt = archThk
576         self.rtl = archThkLower
577         self.v = archHeight
578         self.vl = archHeightLower
579         if self.w <= 0: self.w = SMALL
580
581         #find the upper arch radius
582         if archHeight >= width/2:
583             # just one arch, low long
584             self.r = (self.v**2)/self.w + self.w/4
585         elif archHeight <= 0:
586             # No arches
587             self.r = 0
588             self.v = 0
589         else:
590             # Two arches
591             self.r = (self.w**2)/(8*self.v) + self.v/2.
592             self.c = self.rt*cos(atan(self.w/(2*(self.r-self.v))))
593
594         #find the lower arch radius
595         if archHeightLower >= width/2:
596             self.rl = (self.vl**2)/self.w + self.w/4
597         elif archHeightLower <= 0:
598             self.rl = 0
599             self.vl = 0
600         else:
601             self.rl = (self.w**2)/(8*self.vl) + self.vl/2.
602             self.cl = self.rtl*cos(atan(self.w/(2*(self.rl-self.vl))))
603
604         #self.form = something?
605         self.b = float(bevel)
606         self.ts = edgeThk
607 #
608 #
609
610 #class for the whole wall boundaries; a sub-class of "opening"
611 class OpeningInv(opening):
612     #this is supposed to switch the sides of the opening
613     #so the wall will properly enclose the whole wall.
614     #We'll see if it works.
615
616     def edgeS(self, ht, s):
617         return opening.edgeS(self, ht, -s)
618
619     def edgeV(self, ht, s):
620         return opening.edgeV(self, ht, -s)
621
622 #class rows in the wall
623 class rowOb:
624     __doc__ = """\
625     This is the class for holding the data for individual rows of blocks.
626     each row is required to have some edge blocks, and can also have
627     intermediate sections of "normal" blocks.
628     """
629
630     #z = 0.
631     #h = 0.
632     radius = 1
633     EdgeOffset = 0.
634 #    BlocksEdge = []
635 #    RowSegments = []
636 #    BlocksNorm = []
637
638     def FillBlocks(self):
639         # Set the radius variable, in the case of radial geometry
640         if radialized:
641             if slope: self.radius = dims['t']*(sin(self.z*PI/(dims['t']*2)))
642             else: self.radius = self.z
643
644         #initialize internal variables from global settings
645
646         SetH = settings['h']
647         SetHwt = settings['hwt']
648         SetWid = settings['w']
649         SetWidMin = settings['wm']
650         SetWidVar = settings['wv']
651         SetGrt = settings['g']
652         SetGrtVar = settings['gv']
653         SetRowHeightLink = settings['rwhl']
654         SetDepth = settings['d']
655         SetDepthVar = settings['dv']
656
657         #height weight, used for making shorter rows have narrower blocks, and vice-versa
658         hwt = ((self.h/SetH-1)*SetHwt+1)
659
660         # set variables for persistent values: loop optimization, readability, single ref for changes.
661
662         avgDist = hwt*SetWid/self.radius
663         minDist = SetWidMin/self.radius
664         deviation = hwt*SetWidVar/self.radius
665         grtOffset = SetGrt/(2*self.radius)
666
667         # init loop variables that may change...
668
669         grt = (SetGrt + rndc()*SetGrtVar)/(self.radius)
670         ThisBlockHeight = self.h+rndc()*(1-SetRowHeightLink)*SetGrtVar
671         ThisBlockDepth = rndd()*SetDepthVar+SetDepth
672
673         for segment in self.RowSegments:
674             divs = fill(segment[0]+grtOffset, segment[1]-grtOffset, avgDist, minDist, deviation)
675
676             #loop through the divisions, adding blocks for each one
677             for i in range(len(divs)-1):
678                 ThisBlockx = (divs[i]+divs[i+1])/2
679                 ThisBlockw = divs[i+1]-divs[i]-grt
680
681                 self.BlocksNorm.append([ThisBlockx, self.z, ThisBlockw, ThisBlockHeight, ThisBlockDepth, None])
682
683                 if SetDepthVar: # vary depth
684                     ThisBlockDepth = rndd()*SetDepthVar+SetDepth
685
686                 if SetGrtVar: # vary grout
687                     grt = (SetGrt + rndc()*SetGrtVar)/(self.radius)
688                     ThisBlockHeight = self.h+rndc()*(1-SetRowHeightLink)*SetGrtVar
689
690
691     def __init__(self,centerheight,rowheight,edgeoffset = 0.):
692         self.z = float(centerheight)
693         self.h = float(rowheight)
694         self.EdgeOffset = float(edgeoffset)
695
696 #THIS INITILIZATION IS IMPORTANT!  OTHERWISE ALL OBJECTS WILL HAVE THE SAME LISTS!
697         self.BlocksEdge = []
698         self.RowSegments = []
699         self.BlocksNorm = []
700
701 #
702 def arch(ra,rt,x,z, archStart, archEnd, bevel, bevAngle, vll):
703     __doc__ = """\
704     Makes a list of faces and vertexes for arches.
705     ra: the radius of the arch, to the center of the bricks
706     rt: the thickness of the arch
707     x: x center location of the circular arc, as if the arch opening were centered on x = 0
708     z: z center location of the arch
709     anglebeg: start angle of the arch, in radians, from vertical?
710     angleend: end angle of the arch, in radians, from vertical?
711     bevel: how much to bevel the inside of the arch.
712     vll: how long is the vertex list already?
713     """
714     avlist = []
715     aflist = []
716
717     #initialize internal variables for global settings
718 #overkill?
719     SetH = settings['h']
720     SetHwt = settings['hwt']
721     SetWid = settings['w']
722     SetWidMin = settings['wm']
723     SetWidVar = settings['wv']
724     SetGrt = settings['g']
725     SetGrtVar = settings['gv']
726     SetRowHeightLink = settings['rwhl']
727     SetDepth = settings['d']
728     SetDepthVar = settings['dv']
729
730     # Init loop variables
731
732     def bevelEdgeOffset(offsets, bevel, side):
733         """
734         Take the block offsets and modify it for the correct bevel.
735
736         offsets = the offset list. See MakeABlock
737         bevel = how much to offset the edge
738         side = -1 for left (right side), 1 for right (left side)
739         """
740         left = (0,2,3)
741         right = (4,6,7)
742         if side == 1: pointsToAffect = right
743         else: pointsToAffect = left
744         for num in pointsToAffect:
745             offsets[num] = offsets[num][:]
746             offsets[num][0] += -bevel * side
747
748     ArchInner = ra-rt/2
749     ArchOuter = ra+rt/2-SetGrt + rndc()*SetGrtVar
750
751     DepthBack = -SetDepth/2-rndc()*SetDepthVar
752     DepthFront = SetDepth/2+rndc()*SetDepthVar
753
754     if radialized: subdivision = settings['sdv']
755     else: subdivision = 0.12
756
757     grt = (SetGrt + rndc()*SetGrtVar)/(2*ra) # init grout offset for loop
758     # set up the offsets, it will be the same for every block
759     offsets = ([[0]*2 + [bevel]] + [[0]*3]*3)*2
760
761     #make the divisions in the "length" of the arch
762     divs = fill(archStart, archEnd, settings['w']/ra, settings['wm']/ra, settings['wv']/ra)
763
764     for i in range(len(divs)-1):
765         if i == 0:
766             ThisOffset = offsets[:]
767             bevelEdgeOffset(ThisOffset, bevAngle, -1)
768         elif i == len(divs)-2:
769             ThisOffset = offsets[:]
770             bevelEdgeOffset(ThisOffset, bevAngle, 1)
771         else:
772             ThisOffset = offsets
773
774         geom = MakeABlock([divs[i]+grt, divs[i+1]-grt, ArchInner, ArchOuter, DepthBack, DepthFront],
775                           subdivision, len(avlist) + vll, ThisOffset, [], None, ra)
776
777         avlist += geom[0]
778         aflist += geom[1]
779
780         if SetDepthVar: # vary depth
781             DepthBack = -SetDepth/2-rndc()*SetDepthVar
782             DepthFront = SetDepth/2+rndc()*SetDepthVar
783
784         if SetGrtVar: # vary grout
785             grt = (settings['g'] + rndc()*SetGrtVar)/(2*ra)
786             ArchOuter = ra+rt/2-SetGrt + rndc()*SetGrtVar
787
788     for i,vert in enumerate(avlist):
789         v0 = vert[2]*sin(vert[0]) + x
790         v1 = vert[1]
791         v2 = vert[2]*cos(vert[0]) + z
792
793         if radialized==1:
794             if slope==1: r1 = dims['t']*(sin(v2*PI/(dims['t']*2)))
795             else: r1 = v2
796             v0 = v0/r1
797
798         avlist[i] = [v0,v1,v2]
799
800     return (avlist,aflist)
801
802 #
803 def sketch():
804     __doc__ = """\
805     The 'sketch' function creates a list of openings from the general specifications passed to it.
806     It takes curved and domed walls into account, placing the openings at the appropriate angular locations
807     """
808     boundlist = []
809     for x in openingSpecs:
810         if x['rp']:
811             if radialized: r1 = x['z']
812             else: r1 = 1
813
814             if x['x'] > (x['w'] + settings['wm']):spacing = x['x']/r1
815             else: spacing = (x['w'] + settings['wm'])/r1
816
817             minspacing = (x['w'] + settings['wm'])/r1
818
819             divs = fill(dims['s'],dims['e'],spacing,minspacing,center=1)
820
821             for posidx in range(len(divs)-2):
822                 boundlist.append(opening(divs[posidx+1],x['z'],x['w'],x['h'],x['v'],x['t'],x['vl'],x['tl'],x['b']))
823
824         else: boundlist.append(opening(x['x'],x['z'],x['w'],x['h'],x['v'],x['t'],x['vl'],x['tl'],x['b']))
825         #check for overlaping edges?
826
827     return boundlist
828
829
830 def wedgeBlocks(row, opening, leftPos, rightPos, edgeBinary, r1):
831     __doc__ = """\
832     Makes wedge blocks for the left and right sides, depending
833     example:
834     wedgeBlocks(row, LeftWedgeEdge, LNerEdge, LEB, r1)
835     wedgeBlocks(row, RNerEdge, RightWedgeEdge, REB, r1)
836     """
837     wedgeEdges = fill(leftPos, rightPos, settings['w']/r1, settings['wm']/r1,
838                       settings['wv']/r1)
839
840     for i in range(len(wedgeEdges)-1):
841         x = (wedgeEdges[i+1] + wedgeEdges[i])/2
842         grt = (settings['g'] + rndd()*settings['gv'])/r1
843         w = wedgeEdges[i+1] - wedgeEdges[i] - grt
844
845         ThisBlockDepth = rndd()*settings['dv']+settings['d']
846
847 #edgeV may return "None" - causing TypeError for math op.
848 #use 0 until wedgeBlocks operation worked out
849         edgeVal = opening.edgeV(x-w/2,edgeBinary)
850         if edgeVal == None: edgeVal = 0.0
851
852         LeftVertOffset =  -( row.z - (row.h/2)*edgeBinary - edgeVal )
853
854 #edgeV may return "None" - causing TypeError for math op.
855 #use 0 until wedgeBlocks operation worked out
856         edgeVal = opening.edgeV(x+w/2,edgeBinary)
857         if edgeVal == None: edgeVal = 0.0
858
859         RightVertOffset = -( row.z - (row.h/2)*edgeBinary - edgeVal )
860
861         #Wedges are on top = off, blank, off, blank
862         #Wedges are on btm = blank, off, blank, off
863         ThisBlockOffsets = [[0,0,LeftVertOffset]]*2 + [[0]*3]*2 + [[0,0,RightVertOffset]]*2
864
865         # Instert or append "blank" for top or bottom wedges.
866         if edgeBinary == 1: ThisBlockOffsets = ThisBlockOffsets + [[0]*3]*2
867         else: ThisBlockOffsets = [[0]*3]*2 + ThisBlockOffsets
868
869         row.BlocksEdge.append([x,row.z,w,row.h,ThisBlockDepth,ThisBlockOffsets])
870
871     return None
872
873 def bevelBlockOffsets(offsets, bevel, side):
874     """
875     Take the block offsets and modify it for the correct bevel.
876
877     offsets = the offset list. See MakeABlock
878     bevel = how much to offset the edge
879     side = -1 for left (right side), 1 for right (left side)
880     """
881 #    left = (4,6)
882 #    right = (0,2)
883     if side == 1: pointsToAffect = (0,2) # right
884     else: pointsToAffect = (4,6) # left
885     for num in pointsToAffect:
886         offsets[num] = offsets[num][:]
887         offsets[num][0] += bevel * side
888
889 def rowProcessing(row, Thesketch, WallBoundaries):
890     __doc__ = """\
891     Take row and opening data and process a single row, adding edge and fill blocks to the row data.
892     """
893     #set end blocks
894     #check for openings, record top and bottom of row for right and left of each
895     #if both top and bottom intersect create blocks on each edge, appropriate to the size of the overlap
896     #if only one side intersects, run fill to get edge positions, but this should never happen
897     #
898
899     if radialized:#this checks for radial stonework, and sets the row radius if required
900         if slope: r1 = abs(dims['t']*sin(row.z*PI/(dims['t']*2)))
901         else: r1 = abs(row.z)
902     else: r1 = 1
903
904     # set the edge grout thickness, especially with radial stonework in mind
905     edgrt = settings['ge']*(settings['g']/2 + rndc()*settings['gv'])/(2*r1)
906
907     # Sets up a list of  intersections of top of row with openings,
908     #from left to right [left edge of opening, right edge of opening, etc...]
909     #initially just the left and right edge of the wall
910     edgetop = [[dims['s']+row.EdgeOffset/r1+edgrt,WallBoundaries], [dims['e']+row.EdgeOffset/r1-edgrt,WallBoundaries]]
911     # Same as edgetop, but for the bottms of the rows
912     edgebtm = [[dims['s']+row.EdgeOffset/r1+edgrt,WallBoundaries], [dims['e']+row.EdgeOffset/r1-edgrt,WallBoundaries]]
913
914     # set up some useful values for the top and bottom of the rows.
915     rowTop = row.z+row.h/2
916     rowBtm = row.z-row.h/2
917
918     for hole in Thesketch:
919         #check the top and bottom of the row, looking at the opening from the right
920         e = [hole.edgeS(rowTop, -1), hole.edgeS(rowBtm, -1)]
921
922         # If either one hit the opening, make split points for the left side of the opening.
923         if e[0] or e[1]:
924             e += [hole.edgeS(rowTop, 1), hole.edgeS(rowBtm, 1)]
925
926             # If one of them missed for some reason, set that value to
927             # the middle of the opening.
928             for i,pos in enumerate(e):
929                 if pos == None: e[i] = hole.x
930
931             # add the intersects to the list of edge points
932             edgetop.append([e[0],hole])
933             edgetop.append([e[2],hole])
934             edgebtm.append([e[1],hole])
935             edgebtm.append([e[3],hole])
936
937     # We want to make the walls in order, so sort the intersects.
938     # This is where you would want to remove edge points that are out of order
939     # so that you don't get the "oddity where overlapping openings create blocks inversely" problem
940     edgetop.sort()
941     edgebtm.sort()
942
943     #these two loops trim the edges to the limits of the wall.  This way openings extending outside the wall don't enlarge the wall.
944     while True:
945         try:
946             if (edgetop[-1][0] > dims['e']+row.EdgeOffset/r1) or (edgebtm[-1][0] > dims['e']+row.EdgeOffset/r1):
947                 edgetop[-2:] = []
948                 edgebtm[-2:] = []
949             else: break
950         except IndexError: break
951     #still trimming the edges...
952     while True:
953         try:
954             if (edgetop[0][0] < dims['s']+row.EdgeOffset/r1) or (edgebtm[0][0] < dims['s']+row.EdgeOffset/r1):
955                 edgetop[:2] = []
956                 edgebtm[:2] = []
957             else: break
958         except IndexError: break
959
960     #make those edge blocks and rows!  Wooo!
961     #This loop goes through each section, (a pair of points in edgetop)
962     #and places the edge blocks and inbetween normal block zones into the row object
963     for OpnSplitNo in range(int(len(edgetop)/2)):
964         #left edge is edge<x>[2*OpnSplitNo], right edge edgex[2*OpnSplitNo+1]
965         leftEdgeIndex = 2*OpnSplitNo
966         rightEdgeIndex = 2*OpnSplitNo + 1
967         # get the openings, to save time and confusion
968         leftOpening = edgetop[leftEdgeIndex][1]
969         rightOpening = edgetop[rightEdgeIndex][1]
970         #find the difference between the edge top and bottom on both sides
971         LTop = edgetop[leftEdgeIndex][0]
972         LBtm = edgebtm[leftEdgeIndex][0]
973         RTop = edgetop[rightEdgeIndex][0]
974         RBtm = edgebtm[rightEdgeIndex][0]
975         LDiff = LBtm-LTop
976         RDiff = RTop-RBtm
977
978         #which is furthur out on each side, top or bottom?
979         if LDiff > 0:
980             LFarEdge = LTop #The furthest edge left
981             LNerEdge = LBtm #the nearer edge left
982             LEB = 1 #Left Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
983         else:
984             LFarEdge = LBtm
985             LNerEdge = LTop
986             LEB = -1
987
988         if RDiff > 0:
989             RFarEdge = RTop #The furthest edge right
990             RNerEdge = RBtm #the nearer edge right
991             REB = 1 #Right Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
992
993         else:
994             RFarEdge = RBtm #The furthest edge right
995             RNerEdge = RTop
996             REB = -1 #Right Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
997
998         #The space between the closest edges of the openings in this section of the row
999         InnerDiff = RNerEdge - LNerEdge
1000         #The mid point between the nearest edges
1001         InnerMid = (RNerEdge + LNerEdge)/2
1002
1003         #maximum distance to span with one block
1004         MaxWid = (settings['w']+settings['wv'])/r1
1005         AveWid = settings['w']
1006         MinWid = settings['wm']
1007
1008         #check the left and right sides for wedge blocks
1009         #Check and run the left edge first
1010         #find the edge of the correct side, offset for minimum block height.  The LEB decides top or bottom
1011         ZPositionCheck = row.z + (row.h/2-settings['hm'])*LEB
1012 #edgeS may return "None"
1013         LeftWedgeEdge = leftOpening.edgeS(ZPositionCheck,1)
1014
1015         if (abs(LDiff) > AveWid) or (not LeftWedgeEdge):
1016             #make wedge blocks
1017             if not LeftWedgeEdge: LeftWedgeEdge = leftOpening.x
1018             wedgeBlocks(row, leftOpening, LeftWedgeEdge, LNerEdge, LEB, r1)
1019             #set the near and far edge settings to vertical, so the other edge blocks don't interfere
1020             LFarEdge , LTop , LBtm = LNerEdge, LNerEdge, LNerEdge
1021             LDiff = 0
1022
1023         #Now do the wedge blocks for the right, same drill... repeated code?
1024         #find the edge of the correct side, offset for minimum block height.  The REB decides top or bottom
1025         ZPositionCheck = row.z + (row.h/2-settings['hm'])*REB
1026 #edgeS may return "None"
1027         RightWedgeEdge = rightOpening.edgeS(ZPositionCheck,-1)
1028         if (abs(RDiff) > AveWid) or (not RightWedgeEdge):
1029             #make wedge blocks
1030             if not RightWedgeEdge: RightWedgeEdge = rightOpening.x
1031             wedgeBlocks(row, rightOpening, RNerEdge, RightWedgeEdge, REB, r1)
1032             #set the near and far edge settings to vertical, so the other edge blocks don't interfere
1033             RFarEdge , RTop , RBtm = RNerEdge, RNerEdge, RNerEdge
1034             RDiff = 0
1035
1036         #Check to see if the edges are close enough toegther to warrant a single block filling it
1037         if (InnerDiff < MaxWid):
1038             #if this is true, then this row is just one block!
1039             x = (LNerEdge + RNerEdge)/2.
1040             w = InnerDiff
1041             ThisBlockDepth = rndd()*settings['dv']+settings['d']
1042             BtmOff = LBtm - LNerEdge
1043             TopOff = LTop - LNerEdge
1044             ThisBlockOffsets = [[BtmOff,0,0]]*2 + [[TopOff,0,0]]*2
1045             BtmOff = RBtm - RNerEdge
1046             TopOff = RTop - RNerEdge
1047             ThisBlockOffsets += [[BtmOff,0,0]]*2 + [[TopOff,0,0]]*2
1048             bevel = leftOpening.edgeBev(rowTop)
1049             bevelBlockOffsets(ThisBlockOffsets, bevel, 1)
1050             bevel = rightOpening.edgeBev(rowTop)
1051             bevelBlockOffsets(ThisBlockOffsets, bevel, -1)
1052             row.BlocksEdge.append([x,row.z,w,row.h,ThisBlockDepth,ThisBlockOffsets])
1053             continue
1054
1055         # it's not one block, must be two or more
1056         # set up the offsets for the left
1057         BtmOff = LBtm - LNerEdge
1058         TopOff = LTop - LNerEdge
1059         leftOffsets = [[BtmOff,0,0]]*2 + [[TopOff,0,0]]*2 + [[0]*3]*4
1060         bevelL = leftOpening.edgeBev(rowTop)
1061         bevelBlockOffsets(leftOffsets, bevelL, 1)
1062         # and now for the right
1063         BtmOff = RBtm - RNerEdge
1064         TopOff = RTop - RNerEdge
1065         rightOffsets = [[0]*3]*4 + [[BtmOff,0,0]]*2 + [[TopOff,0,0]]*2
1066         bevelR = rightOpening.edgeBev(rowTop)
1067         bevelBlockOffsets(rightOffsets, bevelR, -1)
1068         # check to see if it is only two blocks
1069         if (InnerDiff < MaxWid*2):
1070         #this row is just two blocks! Left block, then right block
1071             #div is the x position of the dividing point between the two bricks
1072             div = InnerMid + (rndd()*settings['wv'])/r1
1073             #set the grout distance, since we need grout seperation between the blocks
1074             grt = (settings['g'] + rndc()*settings['gv'])/r1
1075             #set the x position and width for the left block
1076             x = (div + LNerEdge)/2 - grt/4
1077             w = (div - LNerEdge) - grt/2
1078             ThisBlockDepth = rndd()*settings['dv']+settings['d']
1079             #For reference: EdgeBlocks = [[x,z,w,h,d,[corner offset matrix]],[etc.]]
1080             row.BlocksEdge.append([x,row.z,w,row.h,ThisBlockDepth,leftOffsets])
1081             #Initialize for the block on the right side
1082             x = (div + RNerEdge)/2 + grt/4
1083             w = (RNerEdge - div) - grt/2
1084             ThisBlockDepth = rndd()*settings['dv']+settings['d']
1085             row.BlocksEdge.append([x,row.z,w,row.h,ThisBlockDepth,rightOffsets])
1086             continue
1087
1088         #program should only get here if there are more than two blocks in the row, and no wedge blocks
1089
1090         #make Left edge block
1091         #set the grout
1092         grt = (settings['g'] + rndc()*settings['gv'])/r1
1093         #set the x position and width for the left block
1094         widOptions = [settings['w'], bevelL + settings['wm'], leftOpening.ts]
1095         baseWid = max(widOptions)
1096         w = (rndd()*settings['wv']+baseWid+row.EdgeOffset)
1097         widOptions[0] = settings['wm']
1098         widOptions[2] = w
1099         w = max(widOptions) / r1 - grt
1100         x = w/2 + LNerEdge + grt/2
1101         BlockRowL = x + w/2
1102         ThisBlockDepth = rndd()*settings['dv']+settings['d']
1103         row.BlocksEdge.append([x,row.z,w,row.h,ThisBlockDepth,leftOffsets])
1104
1105         #make Right edge block
1106         #set the grout
1107         grt = (settings['g'] + rndc()*settings['gv'])/r1
1108         #set the x position and width for the left block
1109         widOptions = [settings['w'], bevelR + settings['wm'], rightOpening.ts]
1110         baseWid = max(widOptions)
1111         w = (rndd()*settings['wv']+baseWid+row.EdgeOffset)
1112         widOptions[0] = settings['wm']
1113         widOptions[2] = w
1114         w = max(widOptions) / r1 - grt
1115         x = RNerEdge - w/2 - grt/2
1116         BlockRowR = x - w/2
1117         ThisBlockDepth = rndd()*settings['dv']+settings['d']
1118         row.BlocksEdge.append([x,row.z,w,row.h,ThisBlockDepth,rightOffsets])
1119
1120         row.RowSegments.append([BlockRowL,BlockRowR])
1121     return None
1122
1123
1124 def plan(Thesketch, oldrows = 0):
1125     __doc__ = """\
1126     The 'plan' function takes the data generated by the sketch function and the global settings
1127     and creates a list of blocks.
1128     It passes out a list of row heights, edge positions, edge blocks, and rows of blocks.
1129     """
1130     # if we were passed a list of rows already, use those; else make a list.
1131     if oldrows: rows = oldrows
1132     else:
1133         #rows holds the important information common to all rows
1134         #rows = [list of row objects]
1135         rows = []
1136
1137         #splits are places where we NEED a row division, to accomidate openings
1138         #add a split for the bottom row
1139         splits = [dims['b']+settings['hb']]
1140
1141         #add a split for each critical point on each opening
1142         for hole in Thesketch: splits += hole.crits()
1143
1144         #and, a split for the top row
1145         splits.append(dims['t']-settings['ht'])
1146         splits.sort()
1147
1148         #divs are the normal old row divisions, add them between the top and bottom split
1149         divs = fill(splits[0],splits[-1],settings['h'],settings['hm']+settings['g'],settings['hv'])[1:-1]
1150
1151         #remove the divisions that are too close to the splits, so we don't get tiny thin rows
1152         for i in range(len(divs)-1,-1,-1):
1153             for j in range(len(splits)):
1154                 diff = abs(divs[i] - splits[j])
1155                 #(settings['hm']+settings['g']) is the old minimum value
1156                 if diff < (settings['h']-settings['hv']+settings['g']):
1157                     del(divs[i])
1158                     break
1159
1160         #now merge the divs and splits lists
1161         divs += splits
1162
1163         #add bottom and/or top points, if bottom and/or top row heights are more than zero
1164         if settings['hb']>0: divs.insert(0,dims['b'])
1165         if settings['ht']>0: divs.append(dims['t'])
1166
1167         divs.sort()
1168
1169         #trim the rows to the bottom and top of the wall
1170         if divs[0] < dims['b']: divs[:1] = []
1171         if divs[-1] > dims['t']: divs[-1:] = []
1172
1173         #now, make the data for each row
1174         #rows = [[center height,row height,edge offset],[etc.]]
1175
1176         divCount = len(divs)-1 # number of divs to check
1177         divCheck = 0 # current div entry
1178
1179         while divCheck < divCount:
1180             RowZ = (divs[divCheck]+divs[divCheck+1])/2
1181             RowHeight = divs[divCheck+1]-divs[divCheck]-settings['g']+rndc()*settings['rwhl']*settings['gv']
1182             EdgeOffset = settings['eoff']*(fmod(divCheck,2)-0.5)+settings['eoffv']*rndd()
1183
1184             # if row height is too shallow: delete next div entry, decrement total, and recheck current entry.
1185             if RowHeight < settings['hm']:
1186                 del(divs[divCheck+1])
1187                 divCount -= 1 # Adjust count for removed div entry.
1188                 continue
1189
1190             rows.append(rowOb(RowZ, RowHeight, EdgeOffset))
1191
1192             divCheck += 1 # on to next div entry
1193
1194     #set up a special opening object to handle the edges of the wall
1195     x = (dims['s'] + dims['e'])/2
1196     z = (dims['t'] + dims['b'])/2
1197     w = (dims['e'] - dims['s'])
1198     h = (dims['t'] - dims['b'])
1199     WallBoundaries = OpeningInv(x,z,w,h)
1200
1201     #Go over each row in the list, set up edge blocks and block sections
1202     for rownum in range(len(rows)):
1203         rowProcessing(rows[rownum], Thesketch, WallBoundaries)
1204
1205     #now return the things everyone needs
1206     #return [rows,edgeBlocks,blockRows,Asketch]
1207     return [rows,Thesketch]
1208
1209
1210 def archGeneration(hole, vlist, flist, sideSign):
1211     __doc__ = """\
1212     Makes arches for the top and bottom, depending on sideSign
1213     example, Lower arch:
1214     archGeneration(hole, vlist, flist, -1)
1215     example, Upper arch:
1216     archGeneration(hole, vlist, flist, 1)
1217     hole is the opening object that the arch is for
1218     add the verticies to vlist
1219     add the faces to flist
1220     sideSign is + or - 1, for the top or bottom arch. Other values may cause errors.
1221     """
1222
1223     # working arrays for vectors and faces
1224     avlist = []
1225     aflist = []
1226
1227     # Top (1) or bottom (-1)
1228     if sideSign == -1:
1229         r = hole.rl #radius of the arch
1230         rt = hole.rtl #thickness of the arch (stone height)
1231         v = hole.vl #height of the arch
1232         c = hole.cl
1233     else:
1234         r = hole.r #radius of the arch
1235         rt = hole.rt #thickness of the arch (stone height)
1236         v = hole.v #height of the arch
1237         c = hole.c
1238
1239     ra = r + rt/2 #average radius of the arch
1240     x = hole.x
1241     w = hole.w
1242     h = hole.h
1243     z = hole.z
1244     bev = hole.b
1245     sideSignInv = -sideSign
1246
1247     if v > w/2: #two arcs, to make a pointed arch
1248         # positioning
1249         zpos = z + (h/2)*sideSign
1250         xoffset = r - w/2
1251         #left side top, right side bottom
1252         #angles reference straight up, and are in radians
1253         bevRad = r + bev
1254         bevHt = sqrt(bevRad**2 - (bevRad - (w/2 + bev))**2)
1255         midHalfAngle = atan(v/(r-w/2))
1256         midHalfAngleBevel = atan(bevHt/(r-w/2))
1257         bevelAngle = midHalfAngle - midHalfAngleBevel
1258         anglebeg = (PI/2)*(sideSignInv)
1259         angleend = (PI/2)*(sideSignInv) + midHalfAngle
1260
1261         avlist,aflist = arch(ra,rt,(xoffset)*(sideSign),zpos,anglebeg,angleend,bev,bevelAngle,len(vlist))
1262
1263         for i,vert in enumerate(avlist): avlist[i] = [vert[0]+hole.x,vert[1],vert[2]]
1264         vlist += avlist
1265         flist += aflist
1266
1267         #right side top, left side bottom
1268
1269         #angles reference straight up, and are in radians
1270         anglebeg = (PI/2)*(sideSign) - midHalfAngle
1271         angleend = (PI/2)*(sideSign)
1272
1273         avlist,aflist = arch(ra,rt,(xoffset)*(sideSignInv),zpos,anglebeg,angleend,bev,bevelAngle,len(vlist))
1274
1275         for i,vert in enumerate(avlist): avlist[i] = [vert[0]+hole.x,vert[1],vert[2]]
1276
1277         vlist += avlist
1278         flist += aflist
1279
1280         #keystone
1281         Dpth = settings['d']+rndc()*settings['dv']
1282         Grout = settings['g'] + rndc()*settings['gv']
1283         angleBevel = (PI/2)*(sideSign) - midHalfAngle
1284         Wdth = (rt - Grout - bev) * 2 * sin(angleBevel) * sideSign #note, sin may be negative
1285         MidZ = ((sideSign)*(bevHt + h/2.0) + z) + (rt - Grout - bev) * cos(angleBevel) #note, cos may come out negative too
1286         nearCorner = sideSign*(MidZ - z) - v - h/2
1287
1288         if sideSign == 1:
1289             TopHt = hole.top() - MidZ - Grout
1290             BtmHt = nearCorner
1291         else:
1292             BtmHt =  - (hole.btm() - MidZ) - Grout
1293             TopHt = nearCorner
1294
1295         # set the amout to bevel the keystone
1296         keystoneBevel = (bevHt - v)*sideSign
1297         if Wdth >= settings['hm']:
1298             avlist,aflist = MakeAKeystone(x, Wdth, MidZ, TopHt, BtmHt, Dpth, keystoneBevel, len(vlist))
1299
1300             if radialized:
1301                 for i,vert in enumerate(avlist):
1302                     if slope: r1 = dims['t']*sin(vert[2]*PI/(dims['t']*2))
1303                     else: r1 = vert[2]
1304                     avlist[i] = [((vert[0]-hole.x)/r1)+hole.x,vert[1],vert[2]]
1305
1306             vlist += avlist
1307             flist += aflist
1308 # remove "debug note" once bevel is finalized.
1309         else: print("keystone was too narrow - " + str(Wdth))
1310
1311     else: # only one arc - curve not peak.
1312 #bottom (sideSign -1) arch has poorly sized blocks...
1313
1314         zpos = z + (sideSign * (h/2 + v - r)) # single arc positioning
1315
1316         #angles reference straight up, and are in radians
1317         if sideSign == -1: angleOffset = PI
1318         else: angleOffset = 0.0
1319
1320         if v < w/2:
1321             halfangle = atan(w/(2*(r-v)))
1322
1323             anglebeg = angleOffset - halfangle
1324             angleend = angleOffset + halfangle
1325         else:
1326             anglebeg = angleOffset - PI/2
1327             angleend = angleOffset + PI/2
1328
1329         avlist,aflist = arch(ra,rt,0,zpos,anglebeg,angleend,bev,0.0,len(vlist))
1330
1331         for i,vert in enumerate(avlist): avlist[i] = [vert[0]+x,vert[1],vert[2]]
1332
1333         vlist += avlist
1334         flist += aflist
1335
1336         #Make the Side Stones
1337         grt = (settings['g'] + rndc()*settings['gv'])
1338         width = sqrt(rt**2 - c**2) - grt
1339
1340         if c > settings['hm'] + grt and c < width + grt:
1341             if radialized: subdivision = settings['sdv'] * (zpos + (h/2)*sideSign)
1342             else: subdivision = settings['sdv']
1343
1344             #set the height of the block, it should be as high as the max corner position, minus grout
1345             height = c - grt*(0.5 + c/(width + grt))
1346
1347             #the vertical offset for the short side of the block
1348             voff = sideSign * (settings['hm'] - height)
1349             xstart = w/2
1350             zstart = z + sideSign * (h/2 + grt/2)
1351             woffset = width*(settings['hm'] + grt/2)/(c - grt/2)
1352             depth = rndd()*settings['dv']+settings['d']
1353
1354             if sideSign == 1:
1355                 offsets = [[0]*3]*6 + [[0]*2 + [voff]]*2
1356                 topSide = zstart+height
1357                 btmSide = zstart
1358             else:
1359                 offsets = [[0]*3]*4 + [[0]*2 + [voff]]*2 + [[0]*3]*2
1360                 topSide = zstart
1361                 btmSide = zstart-height
1362             # Do some stuff to incorporate bev here
1363             bevelBlockOffsets(offsets, bev, -1)
1364
1365             avlist,aflist = MakeABlock([x-xstart-width, x-xstart- woffset, btmSide, topSide, -depth/2, depth/2], subdivision, len(vlist), Offsets=offsets, xBevScl=1)
1366
1367 # top didn't use radialized in prev version; just noting for clarity - may need to revise for "sideSign == 1"
1368             if radialized:
1369                 for i,vert in enumerate(avlist): avlist[i] = [((vert[0]-x)/vert[2])+x,vert[1],vert[2]]
1370
1371             vlist += avlist
1372             flist += aflist
1373
1374 # keep sizing same - neat arches = master masons :)
1375 #           grt = (settings['g'] + rndc()*settings['gv'])
1376 #           height = c - grt*(0.5 + c/(width + grt))
1377 # if grout varies may as well change width too... width = sqrt(rt**2 - c**2) - grt
1378 #           voff = sideSign * (settings['hm'] - height)
1379 #           woffset = width*(settings['hm'] + grt/2)/(c - grt/2)
1380
1381             if sideSign == 1:
1382                 offsets = [[0]*3]*2 + [[0]*2 + [voff]]*2 + [[0]*3]*4
1383                 topSide = zstart+height
1384                 btmSide = zstart
1385             else:
1386                 offsets = [[0]*2 + [voff]]*2 + [[0]*3]*6
1387                 topSide = zstart
1388                 btmSide = zstart-height
1389             # Do some stuff to incorporate bev here
1390             bevelBlockOffsets(offsets, bev, 1)
1391
1392             avlist,aflist = MakeABlock([x+xstart+woffset, x+xstart+width, btmSide, topSide, -depth/2, depth/2], subdivision, len(vlist), Offsets=offsets, xBevScl=1)
1393
1394 # top didn't use radialized in prev version; just noting for clarity - may need to revise for "sideSign == 1"
1395             if radialized:
1396                 for i,vert in enumerate(avlist): avlist[i] = [((vert[0]-x)/vert[2])+x,vert[1],vert[2]]
1397
1398             vlist += avlist
1399             flist += aflist
1400     return None
1401
1402
1403 def build(Aplan):
1404     __doc__ = """\
1405     Build creates the geometry for the wall, based on the
1406     "Aplan" object from the "plan" function.  If physics is
1407     enabled, then it make a number of individual blocks with
1408     physics interaction enabled.  Otherwise it creates
1409     geometry for the blocks, arches, etc. of the wall.
1410     """
1411
1412     vlist = []
1413     flist = []
1414     rows = Aplan[0]
1415
1416 #dead code...
1417     #Physics setup is horribly broken.  Revisit when new API is in place.
1418     '''if False: #settings['physics']:
1419         geom = MakeABlock([-0.5,0.5,-0.5,0.5,-0.5,0.5], 3, 0, None,[], 3*settings['b']/(settings['w'] + settings['h'] + settings['d']))
1420         blockmesh = Blender.Mesh.New('block')
1421         vlist += geom[0]
1422         flist += geom[1]
1423         blockmesh.verts.extend(vlist)
1424         blockmesh.faces.extend(flist)
1425
1426         for block in Aplan[1]:
1427             x,z,w,h,d = block[:5]
1428             block = scn.objects.new(blockmesh, 'block')
1429             block.loc = [x, 0, z]
1430             block.size = [w,d,h]
1431             block.rbFlags = Blender.Object.RBFlags['BOUNDS'] | Blender.Object.RBFlags['ACTOR'] | Blender.Object.RBFlags['DYNAMIC'] | Blender.Object.RBFlags['RIGIDBODY']
1432             block.rbShapeBoundType = Blender.Object.RBShapes['BOX']
1433
1434
1435         for row in Aplan[2]:#row=[xstart,xend,z,h]
1436             #currently, radial geometry is disabled for physics blocks setup
1437             if radialized:
1438                 if slope: r1 = dims['t']*sin(row[2]*PI/(dims['t']*2))
1439                 else: r1 = row[2]
1440
1441             else: r1 = 1
1442
1443             divs = fill(row[0], row[1], settings['w'], settings['wm'], settings['wv'])
1444             for i in range(len(divs)-1):
1445                 block = scn.objects.new(blockmesh, 'block')
1446                 block.loc = [(divs[i]+divs[i+1])/2, 0, row[2]]
1447                 block.size = [(divs[i + 1] - divs[i]) - settings['g'], (settings['d'] + rndd()*settings['dv'])*(1-settings['t']*((row[3]-dims['b'])/(dims['t'] - dims['b']))), row[3]]
1448                 block.rbFlags = Blender.Object.RBFlags['BOUNDS'] | Blender.Object.RBFlags['ACTOR'] | Blender.Object.RBFlags['DYNAMIC'] | Blender.Object.RBFlags['RIGIDBODY']
1449                 block.rbShapeBoundType = Blender.Object.RBShapes['BOX']
1450
1451         return None'''
1452 #end dead code...
1453
1454     # all the edge blocks, redacted
1455     #AllBlocks = [[x,z,w,h,d,[corner offset matrix]],[etc.]]
1456
1457     #loop through each row, adding the normal old blocks
1458     for rowidx in range(len(rows)):#row = row object
1459         rows[rowidx].FillBlocks()
1460
1461     AllBlocks = []
1462
1463     #  If the wall is set to merge blocks, check all the blocks to see if you can merge any
1464 #seems to only merge vertical, should do horizontal too
1465     if bigBlock:
1466         for rowidx in range(len(rows)-1):
1467             if radialized:
1468                 if slope: r1 = dims['t']*sin(abs(rows[rowidx].z)*PI/(dims['t']*2))
1469                 else: r1 = abs(rows[rowidx].z)
1470             else: r1 = 1
1471
1472             Tollerance = settings['g']/r1
1473             idxThis = len(rows[rowidx].BlocksNorm[:]) - 1
1474             idxThat = len(rows[rowidx+1].BlocksNorm[:]) - 1
1475
1476             while True:
1477                 # end loop when either array idx wraps
1478                 if idxThis < 0 or idxThat < 0: break
1479
1480                 blockThis = rows[rowidx].BlocksNorm[idxThis]
1481                 blockThat = rows[rowidx+1].BlocksNorm[idxThat]
1482
1483 #seems to only merge vertical, should do horizontal too...
1484                 cx, cz, cw, ch, cd = blockThis[:5]
1485                 ox, oz, ow, oh, od = blockThat[:5]
1486
1487                 if (abs(cw - ow) < Tollerance) and (abs(cx - ox) < Tollerance) :
1488                     if cw > ow: BlockW = ow
1489                     else: BlockW = cw
1490
1491                     AllBlocks.append([(cx+ox)/2,(cz+oz+(oh-ch)/2)/2,BlockW,abs(cz-oz)+(ch+oh)/2,(cd+od)/2,None])
1492
1493                     rows[rowidx].BlocksNorm.pop(idxThis)
1494                     rows[rowidx+1].BlocksNorm.pop(idxThat)
1495                     idxThis -= 1
1496                     idxThat -= 1
1497
1498                 elif cx > ox: idxThis -= 1
1499                 else: idxThat -= 1
1500
1501     #
1502     #
1503     # Add blocks to create a "shelf/platform".
1504     # Does not account for openings (crosses gaps - which is a good thing)
1505     if shelfExt:
1506         SetGrtOff = settings['g']/2 # half grout for block size modifier
1507
1508         # Use wall block settings for shelf
1509         SetBW = settings['w']
1510         SetBWVar = settings['wv']
1511         SetBWMin = settings['wm']
1512         SetBH = settings['h']
1513
1514         # Shelf area settings
1515         ShelfLft = shelfSpecs['x']
1516         ShelfBtm = shelfSpecs['z']
1517         ShelfEnd = ShelfLft + shelfSpecs['w']
1518         ShelfTop = ShelfBtm + shelfSpecs['h']
1519         ShelfThk = shelfSpecs['d'] * 2 # use double-depth due to offsets to position at cursor.
1520
1521         # Use "corners" to adjust position so not centered on depth.
1522         # Facing shelf, at cursor (middle of wall blocks) - this way no gaps between platform and wall face due to wall block depth.
1523         wallDepth = settings['d']/2 # offset by wall depth so step depth matches UI setting :)
1524         if shelfBack: # place blocks on backside of wall
1525             ShelfOffsets = [[0,ShelfThk/2,0],[0,wallDepth,0],[0,ShelfThk/2,0],[0,wallDepth,0],[0,ShelfThk/2,0],[0,wallDepth,0],[0,ShelfThk/2,0],[0,wallDepth,0]]
1526         else:
1527             ShelfOffsets = [[0,-wallDepth,0],[0,-ShelfThk/2,0],[0,-wallDepth,0],[0,-ShelfThk/2,0],[0,-wallDepth,0],[0,-ShelfThk/2,0],[0,-wallDepth,0],[0,-ShelfThk/2,0]]
1528
1529     # Add blocks for each "shelf row" in area
1530         while ShelfBtm < ShelfTop:
1531
1532             # Make blocks for each row - based on rowOb::fillblocks
1533             # Does not vary grout.
1534             divs = fill(ShelfLft, ShelfEnd, SetBW, SetBWMin, SetBWVar)
1535
1536             #loop through the row divisions, adding blocks for each one
1537             for i in range(len(divs)-1):
1538                 ThisBlockx = (divs[i]+divs[i+1])/2
1539                 ThisBlockw = divs[i+1]-divs[i]-SetGrtOff
1540
1541                 AllBlocks.append([ThisBlockx, ShelfBtm, ThisBlockw, SetBH, ShelfThk, ShelfOffsets])
1542
1543             ShelfBtm += SetBH + SetGrtOff # moving up to next row...
1544     #
1545     #
1546     # Add blocks to create "steps".
1547     # Does not account for openings (crosses gaps - which is a good thing)
1548     if stepMod:
1549         SetGrtOff = settings['g']/2 # half grout for block size modifier
1550
1551         # Vary block width by wall block variations.
1552         SetWidVar = settings['wv']
1553         SetWidMin = settings['wm']
1554
1555         StepXMod = stepSpecs['t'] # width of step/tread, also sets basic block size.
1556         StepZMod = stepSpecs['v']
1557
1558         StepLft = stepSpecs['x']
1559         StepRt = stepSpecs['x'] + stepSpecs['w']
1560         StepBtm = stepSpecs['z'] + StepZMod/2 # Start offset for centered blocks
1561         StepWide = stepSpecs['w']
1562         StepTop = StepBtm + stepSpecs['h']
1563         StepThk = stepSpecs['d'] * 2 # use double-depth due to offsets to position at cursor.
1564
1565         # Use "corners" to adjust steps so not centered on depth.
1566         # Facing steps, at cursor (middle of wall blocks) - this way no gaps between steps and wall face due to wall block depth.
1567         # Also, will work fine as stand-alone if not used with wall (try block depth 0 and see what happens).
1568         wallDepth = settings['d']/2 # offset by wall depth so step depth matches UI setting :)
1569         if stepBack: # place blocks on backside of wall
1570             StepOffsets = [[0,StepThk/2,0],[0,wallDepth,0],[0,StepThk/2,0],[0,wallDepth,0],[0,StepThk/2,0],[0,wallDepth,0],[0,StepThk/2,0],[0,wallDepth,0]]
1571         else:
1572             StepOffsets = [[0,-wallDepth,0],[0,-StepThk/2,0],[0,-wallDepth,0],[0,-StepThk/2,0],[0,-wallDepth,0],[0,-StepThk/2,0],[0,-wallDepth,0],[0,-StepThk/2,0]]
1573
1574     # Add steps for each "step row" in area (neg width is interesting but prevented)
1575         while StepBtm < StepTop and StepWide > 0:
1576
1577             # Make blocks for each step row - based on rowOb::fillblocks
1578             # Does not vary grout.
1579
1580             if stepOnly: # "cantilevered steps"
1581                 if stepLeft:
1582                     stepStart = StepRt - StepXMod
1583                 else:
1584                     stepStart = StepLft
1585
1586                 AllBlocks.append([stepStart, StepBtm, StepXMod, StepZMod, StepThk, StepOffsets])
1587             else:
1588                 divs = fill(StepLft, StepRt, StepXMod, SetWidMin, SetWidVar)
1589
1590                 #loop through the row divisions, adding blocks for each one
1591                 for i in range(len(divs)-1):
1592                     ThisBlockx = (divs[i]+divs[i+1])/2
1593                     ThisBlockw = divs[i+1]-divs[i]-SetGrtOff
1594
1595                     AllBlocks.append([ThisBlockx, StepBtm, ThisBlockw, StepZMod, StepThk, StepOffsets])
1596
1597             StepBtm += StepZMod + SetGrtOff # moving up to next row...
1598             StepWide -= StepXMod # reduce step width
1599
1600             # adjust side limit depending on direction of steps
1601             if stepLeft:
1602                 StepRt -= StepXMod # move in from right
1603             else:
1604                 StepLft += StepXMod # move in from left
1605
1606
1607     #Copy all the blocks out of the rows
1608     for row in rows:
1609         AllBlocks += row.BlocksEdge
1610         AllBlocks += row.BlocksNorm
1611
1612     #This loop makes individual blocks for each block specified in the plan
1613     for block in AllBlocks:
1614         x,z,w,h,d,corners = block
1615         if radialized:
1616             if slope: r1 = dims['t']*sin(z*PI/(dims['t']*2))
1617             else: r1 = z
1618         else: r1 = 1
1619
1620         geom = MakeABlock([x-w/2, x+w/2, z-h/2, z+h/2, -d/2, d/2], settings['sdv'], len(vlist), corners, None, settings['b']+rndd()*settings['bv'], r1)
1621         vlist += geom[0]
1622         flist += geom[1]
1623
1624
1625     # This loop makes Arches for every opening specified in the plan.
1626     for hole in Aplan[1]:
1627         # lower arch stones
1628         if hole.vl > 0 and hole.rtl > (settings['g'] + settings['hm']):#make lower arch blocks
1629             archGeneration(hole, vlist, flist, -1)
1630
1631         #top arch stones
1632         if hole.v > 0 and hole.rt > (settings['g'] + settings['hm']):#make upper arch blocks
1633             archGeneration(hole, vlist, flist, 1)
1634     #
1635
1636     #Warp all the points for domed stonework
1637     if slope:
1638         for i,vert in enumerate(vlist):
1639             vlist[i] = [vert[0],(dims['t']+vert[1])*cos(vert[2]*PI/(2*dims['t'])),(dims['t']+vert[1])*sin(vert[2]*PI/(2*dims['t']))]
1640
1641     #Warp all the points for radial stonework
1642     if radialized:
1643         for i,vert in enumerate(vlist):
1644             vlist[i] = [vert[2]*cos(vert[0]),vert[2]*sin(vert[0]),vert[1]]
1645
1646     return vlist, flist
1647
1648
1649 #The main function
1650 def createWall(radial, curve, openings, mergeBlox, shelf, shelfSide,
1651         steps, stepDir, stepBare, stepSide):
1652     __doc__ = """\
1653     Call all the funcitons you need to make a wall, return the verts and faces.
1654     """
1655     global radialized
1656     global slope
1657     global openingSpecs
1658     global bigBlock
1659     global shelfExt
1660     global stepMod
1661     global stepLeft
1662     global shelfBack
1663     global stepOnly
1664     global stepBack
1665
1666     # set all the working variables from passed parameters
1667
1668     radialized = radial
1669     slope = curve
1670     openingSpecs = openings
1671     bigBlock = mergeBlox
1672     shelfExt = shelf
1673     stepMod = steps
1674     stepLeft = stepDir
1675     shelfBack = shelfSide
1676     stepOnly = stepBare
1677     stepBack = stepSide
1678
1679     asketch = sketch()
1680     aplan = plan(asketch, 0)
1681
1682     return build(aplan)
1683