1 # GPL # "authors": dudecon, jambay
5 # Grout needs to be implemented.
6 # consider removing wedge crit for small "c" and "cl" values
7 # wrap around for openings on radial stonework?
8 # auto-clip wall edge to SMALL for radial and domes.
9 # unregister doesn't release all references.
10 # repeat for opening doesn't distribute evenly when radialized - see wrap around
12 # if opening width == indent*2 the edge blocks fail (row of blocks cross opening).
13 # if openings overlap fills inverse with blocks - see h/v slots.
14 # Negative grout width creates a pair of phantom blocks, separated by grout
15 # width, inside the edges.
16 # if block width variance is 0, and edging is on, right edge blocks create a "vertical seam"
20 from random
import random
27 # Set to True to enable debug_prints
31 SMALL
= 0.000000000001
32 # for values that must be != 0; see UI options/variables - sort of a bug to be fixed
37 # General masonry Settings
38 # ------------------------
40 'w': 1.2, 'wv': 0.3, 'h': .6, 'hv': 0.3, 'd': 0.3, 'dv': 0.1,
41 'g': 0.1, 'gv': 0.07, 'gd': 0.01, 'gdv': 0.0, 'b': 0, 'bv': 0,
42 'f': 0.0, 'fv': 0.0, 't': 0.0, 'sdv': 0.1, 'hwt': 0.5, 'aln': 0,
43 'wm': 0.8, 'hm': 0.3, 'dm': 0.1,
44 'woff': 0.0, 'woffv': 0.0, 'eoff': 0.3, 'eoffv': 0.0, 'rwhl': 1,
45 'hb': 0, 'ht': 0, 'ge': 0, 'physics': 0
48 settings DOCUMENTATION:
49 'w':width 'wv':widthVariation
50 'h':height 'hv':heightVariation
51 'd':depth 'dv':depthVariation
52 'g':grout 'gv':groutVariation 'gd':groutDepth 'gdv':groutDepthVariation
53 'b':bevel 'bv':bevelVariation
54 'f':flawSize 'fv':flawSizeVariation 'ff':flawFraction
56 'sdv':subdivision(distance or angle)
57 'hwt':row height effect on block widths in the row (0=no effect,
58 1=1:1 relationship, negative values allowed, 0.5 works well)
59 'aln':alignment(0=none, 1=rows w/features, 2=features w/rows)
61 'wm':width minimum 'hm':height minimum 'dm':depth minimum
62 'woff':row start offset(fraction of width)
63 'woffv':width offset variation(fraction of width)
64 'eoff':edge offset 'eoffv':edge offset variation
65 'rwhl':row height lock(1 is all blocks in row have same height)
66 'hb':bottom row height 'ht': top row height 'ge': grout the edges
67 'physics': set up for physics
70 # dims = area of wall (face)
71 # ------------------------
73 's': 0, 'e': PI
* 3 / 2, 'b': 0.1, 't': 12.3
77 's':start x or theta 'e':end x or theta 'b':bottom z or r 't':top z or r
78 'w' = e-s and h = t-b; calculated to optimize for various operations/usages
79 dims = {'s':-12, 'e':15, 'w':27, 'b':-15., 't':15., 'h':30}
80 dims = {'s':-bayDim/2, 'e':bayDim/2, 'b':-5., 't':10.} # bay settings?
83 # ------------------------
84 radialized
= 0 # Radiating from one point - round/disc; instead of square
85 slope
= 0 # Warp/slope; curved over like a vaulted tunnel
87 # 'bigblock': merge adjacent blocks into single large blocks
88 bigBlock
= 0 # Merge blocks
91 # Gaps in blocks for various apertures
92 # ------------------------
95 {'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 2.7, 'rp': 1, 'b': 0.0,
96 'v': 0, 'vl': 0, 't': 0, 'tl': 0}
99 openingSpecs DOCUMENTATION:
100 'w': opening width, 'h': opening height,
101 'x': horizontal position, 'z': vertical position,
102 'rp': make multiple openings, with a spacing of x,
103 'b': bevel the opening, inside only, like an arrow slit.
104 'v': height of the top arch, 'vl':height of the bottom arch,
105 't': thickness of the top arch, 'tl': thickness of the bottom arch
108 # Add blocks to make platforms
109 # ------------------------
113 'w': 0.5, 'h': 0.5, 'd': 0.3, 'x': 0.8, 'z': 2.7
116 shelfSpecs DOCUMENTATION:
117 'w': block width, 'h': block height, 'd': block depth (shelf size; offset from wall)
118 'x': horizontal start position, 'z': vertical start position
121 # Add blocks to make steps
122 # ------------------------
126 'x': 0.0, 'z': -10, 'w': 10.0, 'h': 10.0,
127 'v': 0.7, 't': 1.0, 'd': 1.0
130 stepSpecs DOCUMENTATION:
131 'x': horizontal start position, 'z': vertical start position,
132 'w': step area width, 'h': step area height,
133 'v': riser height, 't': tread width, 'd': block depth (step size; offset from wall)
142 def debug_prints(func
="", text
="Message", var
=None):
145 print("\n[{}]\nmessage: {}".format(func
, text
))
147 print("Error: ", var
)
150 # pass variables just like for the regular prints
151 def debug_print_vars(*args
, **kwargs
):
154 print(*args
, **kwargs
)
157 # easier way to get to the random function
162 # random number from -0.5 to 0.5
164 return (random() - 0.5)
167 # random number from -1.0 to 1.0
169 return (random() - 0.5) * 2.0
173 # opening test function
176 dims
= {'s': -29., 'e': 29., 'b': -6., 't': TestN
* 7.5}
178 for i
in range(TestN
):
179 x
= (random() - 0.5) * 6
181 v
= .2 + i
* (3. / TestN
)
182 vl
= 3.2 - i
* (3. / TestN
)
186 openingSpecs
+= [{'w': 3.1 + rn
, 'h': 0.3 + rn
, 'x': float(x
),
187 'z': float(z
), 'rp': 0, 'b': 0.,
188 'v': float(v
), 'vl': float(vl
),
189 't': float(t
), 'tl': float(tl
)}]
190 return dims
, openingSpecs
193 # dims, openingSpecs = test(15)
196 # For filling a linear space with divisions
197 def fill(left
, right
, avedst
, mindst
=0.0, dev
=0.0, pad
=(0.0, 0.0), num
=0,
200 Fills a linear range with points and returns an ordered list of those points
201 including the end points.
203 left: the lower boundary
204 right: the upper boundary
205 avedst: the average distance between points
206 mindst: the minimum distance between points
207 dev: the maximum random deviation from avedst
208 pad: tends to move the points near the bounds right (positive) or
210 element 0 pads the lower bounds, element 1 pads the upper bounds
211 num: substitutes a numerical limit for the right limit. fill will then make
213 center: flag to center the elements in the range, 0 == disabled
217 curpos
= left
+ pad
[0]
219 # Set offset by average spacing, then add blocks (fall through);
220 # if not at right edge.
222 curpos
+= ((right
- left
- mindst
* 2) % avedst
) / 2 + mindst
223 if curpos
- poslist
[-1] < mindst
:
224 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
226 # clip to right edge.
227 if (right
- curpos
< mindst
) or (right
- curpos
< mindst
- pad
[1]):
228 poslist
.append(right
)
232 poslist
.append(curpos
)
239 curpos
+= avedst
+ rndd() * dev
240 if curpos
- poslist
[-1] < mindst
:
241 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
242 poslist
.append(curpos
)
249 while True: # loop for blocks
250 curpos
+= avedst
+ rndd() * dev
251 if curpos
- poslist
[-1] < mindst
:
252 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
253 # close off edges at limit
254 if (right
- curpos
< mindst
) or (right
- curpos
< mindst
- pad
[1]):
255 poslist
.append(right
)
258 poslist
.append(curpos
)
261 # For generating block geometry
262 def MakeABlock(bounds
, segsize
, vll
=0, Offsets
=None, FaceExclude
=[],
265 MakeABlock returns lists of points and faces to be made into a square
266 cornered block, subdivided along the length, with optional bevels.
267 bounds: a list of boundary positions:
268 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
269 segsize: the maximum size before lengthwise subdivision occurs
270 vll: the number of vertexes already in the mesh. len(mesh.verts) should
272 Offsets: list of coordinate delta values.
273 Offsets are lists, [x,y,z] in
280 5:right_bottom_front,
284 FaceExclude: list of faces to exclude from the faces list. see bounds above for indices
285 xBevScl: how much to divide the end (+- x axis) bevel dimensions. Set to current average
286 radius to compensate for angular distortion on curved blocks
289 slices
= fill(bounds
[0], bounds
[1], segsize
, segsize
, center
=1)
294 points
.append([slices
[0], bounds
[4], bounds
[2]])
295 points
.append([slices
[0], bounds
[5], bounds
[2]])
296 points
.append([slices
[0], bounds
[5], bounds
[3]])
297 points
.append([slices
[0], bounds
[4], bounds
[3]])
299 for x
in slices
[1:-1]:
300 points
.append([x
, bounds
[4], bounds
[2]])
301 points
.append([x
, bounds
[5], bounds
[2]])
302 points
.append([x
, bounds
[5], bounds
[3]])
303 points
.append([x
, bounds
[4], bounds
[3]])
305 points
.append([slices
[-1], bounds
[4], bounds
[2]])
306 points
.append([slices
[-1], bounds
[5], bounds
[2]])
307 points
.append([slices
[-1], bounds
[5], bounds
[3]])
308 points
.append([slices
[-1], bounds
[4], bounds
[3]])
311 points
.append([slices
[0] + Offsets
[0][0], bounds
[4] + Offsets
[0][1], bounds
[2] + Offsets
[0][2]])
312 points
.append([slices
[0] + Offsets
[1][0], bounds
[5] + Offsets
[1][1], bounds
[2] + Offsets
[1][2]])
313 points
.append([slices
[0] + Offsets
[3][0], bounds
[5] + Offsets
[3][1], bounds
[3] + Offsets
[3][2]])
314 points
.append([slices
[0] + Offsets
[2][0], bounds
[4] + Offsets
[2][1], bounds
[3] + Offsets
[2][2]])
316 for x
in slices
[1: -1]:
317 xwt
= (x
- bounds
[0]) / (bounds
[1] - bounds
[0])
318 points
.append([x
+ Offsets
[0][0] * (1 - xwt
) + Offsets
[4][0] * xwt
,
319 bounds
[4] + Offsets
[0][1] * (1 - xwt
) + Offsets
[4][1] * xwt
,
320 bounds
[2] + Offsets
[0][2] * (1 - xwt
) + Offsets
[4][2] * xwt
])
321 points
.append([x
+ Offsets
[1][0] * (1 - xwt
) + Offsets
[5][0] * xwt
,
322 bounds
[5] + Offsets
[1][1] * (1 - xwt
) + Offsets
[5][1] * xwt
,
323 bounds
[2] + Offsets
[1][2] * (1 - xwt
) + Offsets
[5][2] * xwt
])
324 points
.append([x
+ Offsets
[3][0] * (1 - xwt
) + Offsets
[7][0] * xwt
,
325 bounds
[5] + Offsets
[3][1] * (1 - xwt
) + Offsets
[7][1] * xwt
,
326 bounds
[3] + Offsets
[3][2] * (1 - xwt
) + Offsets
[7][2] * xwt
])
327 points
.append([x
+ Offsets
[2][0] * (1 - xwt
) + Offsets
[6][0] * xwt
,
328 bounds
[4] + Offsets
[2][1] * (1 - xwt
) + Offsets
[6][1] * xwt
,
329 bounds
[3] + Offsets
[2][2] * (1 - xwt
) + Offsets
[6][2] * xwt
])
331 points
.append([slices
[-1] + Offsets
[4][0], bounds
[4] + Offsets
[4][1], bounds
[2] + Offsets
[4][2]])
332 points
.append([slices
[-1] + Offsets
[5][0], bounds
[5] + Offsets
[5][1], bounds
[2] + Offsets
[5][2]])
333 points
.append([slices
[-1] + Offsets
[7][0], bounds
[5] + Offsets
[7][1], bounds
[3] + Offsets
[7][2]])
334 points
.append([slices
[-1] + Offsets
[6][0], bounds
[4] + Offsets
[6][1], bounds
[3] + Offsets
[6][2]])
336 faces
.append([vll
, vll
+ 3, vll
+ 2, vll
+ 1])
338 for x
in range(len(slices
) - 1):
339 faces
.append([vll
, vll
+ 1, vll
+ 5, vll
+ 4])
341 faces
.append([vll
, vll
+ 1, vll
+ 5, vll
+ 4])
343 faces
.append([vll
, vll
+ 1, vll
+ 5, vll
+ 4])
345 faces
.append([vll
, vll
- 3, vll
+ 1, vll
+ 4])
348 faces
.append([vll
, vll
+ 1, vll
+ 2, vll
+ 3])
353 # For generating Keystone Geometry
355 def MakeAKeystone(xpos
, width
, zpos
, ztop
, zbtm
, thick
, bevel
, vll
=0, FaceExclude
=[], xBevScl
=1):
357 MakeAKeystone returns lists of points and faces to be made into a
358 square cornered keystone, with optional bevels.
359 xpos: x position of the centerline
360 width: x width of the keystone at the widest point (discounting bevels)
361 zpos: z position of the widest point
362 ztop: distance from zpos to the top
363 zbtm: distance from zpos to the bottom
365 bevel: the amount to raise the back vertex to account for arch beveling
366 vll: the number of vertexes already in the mesh. len(mesh.verts) should give this number
367 faceExclude: list of faces to exclude from the faces list.
368 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
369 xBevScl: how much to divide the end (+- x axis) bevel dimensions.
370 Set to current average radius to compensate for angular distortion on curved blocks
375 faceinclude
= [1 for x
in range(6)]
376 for x
in FaceExclude
:
383 # The front top point
384 points
.append([xpos
, Thk
, Top
])
385 # The front left point
386 points
.append([xpos
- Wid
, Thk
, zpos
])
387 # The front bottom point
388 points
.append([xpos
, Thk
, Btm
])
389 # The front right point
390 points
.append([xpos
+ Wid
, Thk
, zpos
])
394 MirrorPoints
.append([i
[0], -i
[1], i
[2]])
395 points
+= MirrorPoints
396 points
[6][2] += bevel
398 faces
.append([3, 2, 1, 0])
399 faces
.append([4, 5, 6, 7])
400 faces
.append([4, 7, 3, 0])
401 faces
.append([5, 4, 0, 1])
402 faces
.append([6, 5, 1, 2])
403 faces
.append([7, 6, 2, 3])
404 # Offset the vertex numbers by the number of vertices already in the list
405 for i
in range(len(faces
)):
406 for j
in range(len(faces
[i
])):
412 # for finding line/circle intercepts
414 def circ(offs
=0., r
=1.):
416 offs is the distance perpendicular to the line to the center of the circle
417 r is the radius of the circle
418 circ returns the distance parallel to the line to the center of the circle at the intercept.
426 return sqrt(r
** 2 - offs
** 2)
429 # class openings in the wall
433 This is the class for holding the data for the openings in the wall.
434 It has methods for returning the edges of the opening for any given position value,
435 as well as bevel settings and top and bottom positions.
436 It stores the 'style' of the opening, and all other pertinent information.
438 # x = 0. # x position of the opening
439 # z = 0. # x position of the opening
440 # w = 0. # width of the opening
441 # h = 0. # height of the opening
442 r
= 0 # top radius of the arch (derived from 'v')
443 rl
= 0 # lower radius of the arch (derived from 'vl')
444 rt
= 0 # top arch thickness
445 rtl
= 0 # lower arch thickness
446 ts
= 0 # Opening side thickness, if greater than average width, replaces it.
447 c
= 0 # top arch corner position (for low arches), distance from the top of the straight sides
448 cl
= 0 # lower arch corner position (for low arches), distance from the top of the straight sides
449 # form = 0 # arch type (unused for now)
450 # b = 0. # back face bevel distance, like an arrow slit
451 v
= 0. # top arch height
452 vl
= 0. # lower arch height
453 # variable "s" is used for "side" in the "edge" function.
454 # it is a signed int, multiplied by the width to get + or - of the center
457 if self
.vl
<= self
.w
/ 2:
458 return self
.z
- self
.h
/ 2 - self
.vl
- self
.rtl
460 return self
.z
- sqrt((self
.rl
+ self
.rtl
) ** 2 - (self
.rl
- self
.w
/ 2) ** 2) - self
.h
/ 2
463 if self
.v
<= self
.w
/ 2:
464 return self
.z
+ self
.h
/ 2 + self
.v
+ self
.rt
466 return sqrt((self
.r
+ self
.rt
) ** 2 - (self
.r
- self
.w
/ 2) ** 2) + self
.z
+ self
.h
/ 2
468 # crits returns the critical split points, or discontinuities, used for making rows
471 if self
.vl
> 0: # for lower arch
472 # add the top point if it is pointed
473 # if self.vl >= self.w/2.: critlist.append(self.btm())
474 if self
.vl
< self
.w
/ 2.: # else: for low arches, with wedge blocks under them
475 # critlist.append(self.btm())
476 critlist
.append(self
.z
- self
.h
/ 2 - self
.cl
)
478 if self
.h
> 0: # if it has a height, append points at the top and bottom of the main square section
479 critlist
+= [self
.z
- self
.h
/ 2, self
.z
+ self
.h
/ 2]
480 else: # otherwise, append just one in the center
481 critlist
.append(self
.z
)
483 if self
.v
> 0: # for the upper arch
484 if self
.v
< self
.w
/ 2: # add the splits for the upper wedge blocks, if needed
485 critlist
.append(self
.z
+ self
.h
/ 2 + self
.c
)
486 # critlist.append(self.top())
487 # otherwise just add the top point, if it is pointed
488 # else: critlist.append(self.top())
492 # get the side position of the opening.
493 # ht is the z position; s is the side: 1 for right, -1 for left
494 # if the height passed is above or below the opening, return None
495 def edgeS(self
, ht
, s
):
497 # set the row radius: 1 for standard wall (flat)
500 r1
= abs(dims
['t'] * sin(ht
* PI
/ (dims
['t'] * 2)))
506 # Go through all the options, and return the correct value
507 if ht
< self
.btm(): # too low
509 elif ht
> self
.top(): # too high
512 # Check for circ returning None - prevent TypeError (script failure) with float.
513 # in this range, pass the lower arch info
514 elif ht
<= self
.z
- self
.h
/ 2 - self
.cl
:
515 if self
.vl
> self
.w
/ 2:
516 circVal
= circ(ht
- self
.z
+ self
.h
/ 2, self
.rl
+ self
.rtl
)
520 return self
.x
+ s
* (self
.w
/ 2. - self
.rl
+ circVal
) / r1
522 circVal
= circ(ht
- self
.z
+ self
.h
/ 2 + self
.vl
- self
.rl
, self
.rl
+ self
.rtl
)
526 return self
.x
+ s
* circVal
/ r1
528 # in this range, pass the top arch info
529 elif ht
>= self
.z
+ self
.h
/ 2 + self
.c
:
530 if self
.v
> self
.w
/ 2:
531 circVal
= circ(ht
- self
.z
- self
.h
/ 2, self
.r
+ self
.rt
)
535 return self
.x
+ s
* (self
.w
/ 2. - self
.r
+ circVal
) / r1
537 circVal
= circ(ht
- (self
.z
+ self
.h
/ 2 + self
.v
- self
.r
), self
.r
+ self
.rt
)
541 return self
.x
+ s
* circVal
/ r1
543 # in this range pass the lower corner edge info
544 elif ht
<= self
.z
- self
.h
/ 2:
545 d
= sqrt(self
.rtl
** 2 - self
.cl
** 2)
546 if self
.cl
> self
.rtl
/ sqrt(2.):
547 return self
.x
+ s
* (self
.w
/ 2 + (self
.z
- self
.h
/ 2 - ht
) * d
/ self
.cl
) / r1
549 return self
.x
+ s
* (self
.w
/ 2 + d
) / r1
551 # in this range pass the upper corner edge info
552 elif ht
>= self
.z
+ self
.h
/ 2:
553 d
= sqrt(self
.rt
** 2 - self
.c
** 2)
554 if self
.c
> self
.rt
/ sqrt(2.):
555 return self
.x
+ s
* (self
.w
/ 2 + (ht
- self
.z
- self
.h
/ 2) * d
/ self
.c
) / r1
557 return self
.x
+ s
* (self
.w
/ 2 + d
) / r1
559 # in this range, pass the middle info (straight sides)
561 return self
.x
+ s
* self
.w
/ 2 / r1
563 # get the top or bottom of the opening
564 # ht is the x position; s is the side: 1 for top, -1 for bottom
565 def edgeV(self
, ht
, s
):
567 dist
= abs(self
.x
- ht
)
569 def radialAdjust(dist
, sideVal
):
570 # take the distance and adjust for radial geometry, return dist
573 dist
= dist
* abs(dims
['t'] * sin(sideVal
* PI
/ (dims
['t'] * 2)))
575 dist
= dist
* sideVal
578 if s
> 0: # and (dist <= self.edgeS(self.z + self.h / 2 + self.c, 1) - self.x): # check top down
579 # hack for radialized masonry, import approx Z instead of self.top()
580 dist
= radialAdjust(dist
, self
.top())
582 # no arch on top, flat
584 return self
.z
+ self
.h
/ 2
586 # pointed arch on top
587 elif self
.v
> self
.w
/ 2:
588 circVal
= circ(dist
- self
.w
/ 2 + self
.r
, self
.r
+ self
.rt
)
592 return self
.z
+ self
.h
/ 2 + circVal
596 circVal
= circ(dist
, self
.r
+ self
.rt
)
600 return self
.z
+ self
.h
/ 2 + self
.v
- self
.r
+ circVal
602 else: # and (dist <= self.edgeS(self.z - self.h / 2 - self.cl, 1) - self.x): # check bottom up
603 # hack for radialized masonry, import approx Z instead of self.top()
604 dist
= radialAdjust(dist
, self
.btm())
608 return self
.z
- self
.h
/ 2
610 # pointed arch on bottom
611 elif self
.vl
> self
.w
/ 2:
612 circVal
= circ(dist
- self
.w
/ 2 + self
.rl
, self
.rl
+ self
.rtl
)
616 return self
.z
- self
.h
/ 2 - circVal
618 # old conditional? if (dist-self.w / 2 + self.rl) <= (self.rl + self.rtl):
619 # domed arch on bottom
621 circVal
= circ(dist
, self
.rl
+ self
.rtl
) # dist-self.w / 2 + self.rl
625 return self
.z
- self
.h
/ 2 - self
.vl
+ self
.rl
- circVal
627 # and this never happens - but, leave it as failsafe :)
628 debug_prints(func
="opening.EdgeV",
629 text
="Got all the way out of the edgeV! Not good!")
630 debug_print_vars("opening x = ", self
.x
, ", opening z = ", self
.z
)
634 def edgeBev(self
, ht
):
635 if ht
> (self
.z
+ self
.h
/ 2):
637 if ht
< (self
.z
- self
.h
/ 2):
641 r1
= abs(dims
['t'] * sin(ht
* PI
/ (dims
['t'] * 2)))
649 def __init__(self
, xpos
, zpos
, width
, height
, archHeight
=0, archThk
=0,
650 archHeightLower
=0, archThkLower
=0, bevel
=0, edgeThk
=0):
653 self
.w
= float(width
)
654 self
.h
= float(height
)
656 self
.rtl
= archThkLower
658 self
.vl
= archHeightLower
662 # find the upper arch radius
663 if archHeight
>= width
/ 2:
664 # just one arch, low long
665 self
.r
= (self
.v
** 2) / self
.w
+ self
.w
/ 4
666 elif archHeight
<= 0:
672 self
.r
= (self
.w
** 2) / (8 * self
.v
) + self
.v
/ 2.
673 self
.c
= self
.rt
* cos(atan(self
.w
/ (2 * (self
.r
- self
.v
))))
675 # find the lower arch radius
676 if archHeightLower
>= width
/ 2:
677 self
.rl
= (self
.vl
** 2) / self
.w
+ self
.w
/ 4
678 elif archHeightLower
<= 0:
682 self
.rl
= (self
.w
** 2) / (8 * self
.vl
) + self
.vl
/ 2.
683 self
.cl
= self
.rtl
* cos(atan(self
.w
/ (2 * (self
.rl
- self
.vl
))))
685 # self.form = something?
686 self
.b
= float(bevel
)
690 # class for the whole wall boundaries; a sub-class of "opening"
691 class openingInvert(opening
):
692 # this is supposed to switch the sides of the opening
693 # so the wall will properly enclose the whole wall.
695 def edgeS(self
, ht
, s
):
696 return opening
.edgeS(self
, ht
, -s
)
698 def edgeV(self
, ht
, s
):
699 return opening
.edgeV(self
, ht
, -s
)
702 # class rows in the wall
706 This is the class for holding the data for individual rows of blocks.
707 each row is required to have some edge blocks, and can also have
708 intermediate sections of "normal" blocks.
713 def FillBlocks(self
):
714 # Set the radius variable, in the case of radial geometry
717 self
.radius
= dims
['t'] * (sin(self
.z
* PI
/ (dims
['t'] * 2)))
721 # initialize internal variables from global settings
724 SetHwt
= settings
['hwt']
725 SetWid
= settings
['w']
726 SetWidMin
= settings
['wm']
727 SetWidVar
= settings
['wv']
728 SetGrt
= settings
['g']
729 SetGrtVar
= settings
['gv']
730 SetRowHeightLink
= settings
['rwhl']
731 SetDepth
= settings
['d']
732 SetDepthVar
= settings
['dv']
734 # height weight, used for making shorter rows have narrower blocks, and vice-versa
735 hwt
= ((self
.h
/ SetH
- 1) * SetHwt
+ 1)
737 # set variables for persistent values: loop optimization, readability, single ref for changes.
739 avgDist
= hwt
* SetWid
/ self
.radius
740 minDist
= SetWidMin
/ self
.radius
741 deviation
= hwt
* SetWidVar
/ self
.radius
742 grtOffset
= SetGrt
/ (2 * self
.radius
)
744 # init loop variables that may change...
746 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (self
.radius
)
747 ThisBlockHeight
= self
.h
+ rndc() * (1 - SetRowHeightLink
) * SetGrtVar
748 ThisBlockDepth
= rndd() * SetDepthVar
+ SetDepth
750 for segment
in self
.RowSegments
:
751 divs
= fill(segment
[0] + grtOffset
, segment
[1] - grtOffset
, avgDist
, minDist
, deviation
)
753 # loop through the divisions, adding blocks for each one
754 for i
in range(len(divs
) - 1):
755 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
756 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - grt
758 self
.BlocksNorm
.append([ThisBlockx
, self
.z
, ThisBlockw
, ThisBlockHeight
, ThisBlockDepth
, None])
760 if SetDepthVar
: # vary depth
761 ThisBlockDepth
= rndd() * SetDepthVar
+ SetDepth
763 if SetGrtVar
: # vary grout
764 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (self
.radius
)
765 ThisBlockHeight
= self
.h
+ rndc() * (1 - SetRowHeightLink
) * SetGrtVar
767 def __init__(self
, centerheight
, rowheight
, edgeoffset
=0.):
768 self
.z
= float(centerheight
)
769 self
.h
= float(rowheight
)
770 self
.EdgeOffset
= float(edgeoffset
)
772 # THIS INITIALIZATION IS IMPORTANT! OTHERWISE ALL OBJECTS WILL HAVE THE SAME LISTS!
774 self
.RowSegments
= []
778 def arch(ra
, rt
, x
, z
, archStart
, archEnd
, bevel
, bevAngle
, vll
):
780 Makes a list of faces and vertexes for arches.
781 ra: the radius of the arch, to the center of the bricks
782 rt: the thickness of the arch
783 x: x center location of the circular arc, as if the arch opening were centered on x = 0
784 z: z center location of the arch
785 anglebeg: start angle of the arch, in radians, from vertical?
786 angleend: end angle of the arch, in radians, from vertical?
787 bevel: how much to bevel the inside of the arch.
788 vll: how long is the vertex list already?
793 # initialize internal variables for global settings
794 SetGrt
= settings
['g']
795 SetGrtVar
= settings
['gv']
796 SetDepth
= settings
['d']
797 SetDepthVar
= settings
['dv']
799 # Init loop variables
801 def bevelEdgeOffset(offsets
, bevel
, side
):
803 Take the block offsets and modify it for the correct bevel.
805 offsets = the offset list. See MakeABlock
806 bevel = how much to offset the edge
807 side = -1 for left (right side), 1 for right (left side)
812 pointsToAffect
= right
814 pointsToAffect
= left
815 for num
in pointsToAffect
:
816 offsets
[num
] = offsets
[num
][:]
817 offsets
[num
][0] += -bevel
* side
819 ArchInner
= ra
- rt
/ 2
820 ArchOuter
= ra
+ rt
/ 2 - SetGrt
+ rndc() * SetGrtVar
822 DepthBack
= - SetDepth
/ 2 - rndc() * SetDepthVar
823 DepthFront
= SetDepth
/ 2 + rndc() * SetDepthVar
826 subdivision
= settings
['sdv']
830 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (2 * ra
) # init grout offset for loop
831 # set up the offsets, it will be the same for every block
832 offsets
= ([[0] * 2 + [bevel
]] + [[0] * 3] * 3) * 2
834 # make the divisions in the "length" of the arch
835 divs
= fill(archStart
, archEnd
, settings
['w'] / ra
, settings
['wm'] / ra
, settings
['wv'] / ra
)
837 for i
in range(len(divs
) - 1):
839 ThisOffset
= offsets
[:]
840 bevelEdgeOffset(ThisOffset
, bevAngle
, - 1)
841 elif i
== len(divs
) - 2:
842 ThisOffset
= offsets
[:]
843 bevelEdgeOffset(ThisOffset
, bevAngle
, 1)
848 [divs
[i
] + grt
, divs
[i
+ 1] - grt
, ArchInner
, ArchOuter
, DepthBack
, DepthFront
],
849 subdivision
, len(avlist
) + vll
, ThisOffset
, [], None, ra
855 if SetDepthVar
: # vary depth
856 DepthBack
= -SetDepth
/ 2 - rndc() * SetDepthVar
857 DepthFront
= SetDepth
/ 2 + rndc() * SetDepthVar
859 if SetGrtVar
: # vary grout
860 grt
= (settings
['g'] + rndc() * SetGrtVar
) / (2 * ra
)
861 ArchOuter
= ra
+ rt
/ 2 - SetGrt
+ rndc() * SetGrtVar
863 for i
, vert
in enumerate(avlist
):
864 v0
= vert
[2] * sin(vert
[0]) + x
866 v2
= vert
[2] * cos(vert
[0]) + z
870 r1
= dims
['t'] * (sin(v2
* PI
/ (dims
['t'] * 2)))
875 avlist
[i
] = [v0
, v1
, v2
]
877 return (avlist
, aflist
)
882 The 'sketch' function creates a list of openings from the general specifications passed to it.
883 It takes curved and domed walls into account, placing the openings at the appropriate angular locations
886 for x
in openingSpecs
:
893 if x
['x'] > (x
['w'] + settings
['wm']):
894 spacing
= x
['x'] / r1
896 spacing
= (x
['w'] + settings
['wm']) / r1
898 minspacing
= (x
['w'] + settings
['wm']) / r1
900 divs
= fill(dims
['s'], dims
['e'], spacing
, minspacing
, center
=1)
902 for posidx
in range(len(divs
) - 2):
903 boundlist
.append(opening(divs
[posidx
+ 1], x
['z'], x
['w'], x
['h'],
904 x
['v'], x
['t'], x
['vl'], x
['tl'], x
['b']))
906 boundlist
.append(opening(x
['x'], x
['z'], x
['w'], x
['h'], x
['v'], x
['t'], x
['vl'], x
['tl'], x
['b']))
907 # check for overlapping edges?
912 def wedgeBlocks(row
, opening
, leftPos
, rightPos
, edgeBinary
, r1
):
914 Makes wedge blocks for the left and right sides, depending
916 wedgeBlocks(row, LeftWedgeEdge, LNerEdge, LEB, r1)
917 wedgeBlocks(row, RNerEdge, RightWedgeEdge, REB, r1)
919 wedgeEdges
= fill(leftPos
, rightPos
, settings
['w'] / r1
, settings
['wm'] / r1
,
922 for i
in range(len(wedgeEdges
) - 1):
923 x
= (wedgeEdges
[i
+ 1] + wedgeEdges
[i
]) / 2
924 grt
= (settings
['g'] + rndd() * settings
['gv']) / r1
925 w
= wedgeEdges
[i
+ 1] - wedgeEdges
[i
] - grt
927 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
929 # edgeV may return "None" - causing TypeError for math op.
930 # use 0 until wedgeBlocks operation worked out
931 edgeVal
= opening
.edgeV(x
- w
/ 2, edgeBinary
)
935 LeftVertOffset
= -(row
.z
- (row
.h
/ 2) * edgeBinary
- edgeVal
)
937 # edgeV may return "None" - causing TypeError for math op.
938 # use 0 until wedgeBlocks operation worked out
939 edgeVal
= opening
.edgeV(x
+ w
/ 2, edgeBinary
)
943 RightVertOffset
= -(row
.z
- (row
.h
/ 2) * edgeBinary
- edgeVal
)
945 # Wedges are on top = off, blank, off, blank
946 # Wedges are on btm = blank, off, blank, off
947 ThisBlockOffsets
= [[0, 0, LeftVertOffset
]] * 2 + [[0] * 3] * 2 + [[0, 0, RightVertOffset
]] * 2
949 # Insert or append "blank" for top or bottom wedges.
951 ThisBlockOffsets
= ThisBlockOffsets
+ [[0] * 3] * 2
953 ThisBlockOffsets
= [[0] * 3] * 2 + ThisBlockOffsets
955 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, ThisBlockOffsets
])
960 def bevelBlockOffsets(offsets
, bevel
, side
):
962 Take the block offsets and modify it for the correct bevel.
964 offsets = the offset list. See MakeABlock
965 bevel = how much to offset the edge
966 side = -1 for left (right side), 1 for right (left side)
969 pointsToAffect
= (0, 2) # right
971 pointsToAffect
= (4, 6) # left
972 for num
in pointsToAffect
:
973 offsets
[num
] = offsets
[num
][:]
974 offsets
[num
][0] += bevel
* side
977 def rowProcessing(row
, Thesketch
, WallBoundaries
):
979 Take row and opening data and process a single row, adding edge and fill blocks to the row data.
982 # check for openings, record top and bottom of row for right and left of each
983 # if both top and bottom intersect create blocks on each edge, appropriate to the size of the overlap
984 # if only one side intersects, run fill to get edge positions, but this should never happen
986 if radialized
: # this checks for radial stonework, and sets the row radius if required
988 r1
= abs(dims
['t'] * sin(row
.z
* PI
/ (dims
['t'] * 2)))
994 # set the edge grout thickness, especially with radial stonework in mind
995 edgrt
= settings
['ge'] * (settings
['g'] / 2 + rndc() * settings
['gv']) / (2 * r1
)
997 # Sets up a list of intersections of top of row with openings,
998 # from left to right [left edge of opening, right edge of opening, etc...]
999 # initially just the left and right edge of the wall
1000 edgetop
= [[dims
['s'] + row
.EdgeOffset
/ r1
+ edgrt
, WallBoundaries
],
1001 [dims
['e'] + row
.EdgeOffset
/ r1
- edgrt
, WallBoundaries
]]
1003 # Same as edgetop, but for the bottms of the rows
1004 edgebtm
= [[dims
['s'] + row
.EdgeOffset
/ r1
+ edgrt
, WallBoundaries
],
1005 [dims
['e'] + row
.EdgeOffset
/ r1
- edgrt
, WallBoundaries
]]
1007 # set up some useful values for the top and bottom of the rows.
1008 rowTop
= row
.z
+ row
.h
/ 2
1009 rowBtm
= row
.z
- row
.h
/ 2
1011 for hole
in Thesketch
:
1012 # check the top and bottom of the row, looking at the opening from the right
1013 e
= [hole
.edgeS(rowTop
, -1), hole
.edgeS(rowBtm
, -1)]
1015 # If either one hit the opening, make split points for the left side of the opening.
1017 e
+= [hole
.edgeS(rowTop
, 1), hole
.edgeS(rowBtm
, 1)]
1019 # If one of them missed for some reason, set that value to
1020 # the middle of the opening.
1021 for i
, pos
in enumerate(e
):
1025 # add the intersects to the list of edge points
1026 edgetop
.append([e
[0], hole
])
1027 edgetop
.append([e
[2], hole
])
1028 edgebtm
.append([e
[1], hole
])
1029 edgebtm
.append([e
[3], hole
])
1031 # We want to make the walls in order, so sort the intersects.
1032 # This is where you would want to remove edge points that are out of order
1033 # so that you don't get the "oddity where overlapping openings
1034 # create blocks inversely" problem
1036 # Note: sort ended up comparing function pointers
1037 # if both Openings and Slots were enabled with Repeats in one of them
1039 edgetop
.sort(key
=lambda x
: x
[0])
1040 edgebtm
.sort(key
=lambda x
: x
[0])
1041 except Exception as ex
:
1042 debug_prints(func
="rowProcessing",
1043 text
="Sorting has failed", var
=ex
)
1045 # these two loops trim the edges to the limits of the wall.
1046 # This way openings extending outside the wall don't enlarge the wall.
1049 if ((edgetop
[-1][0] > dims
['e'] + row
.EdgeOffset
/ r1
) or
1050 (edgebtm
[-1][0] > dims
['e'] + row
.EdgeOffset
/ r1
)):
1057 # still trimming the edges...
1060 if ((edgetop
[0][0] < dims
['s'] + row
.EdgeOffset
/ r1
) or
1061 (edgebtm
[0][0] < dims
['s'] + row
.EdgeOffset
/ r1
)):
1069 # make those edge blocks and rows! Wooo!
1070 # This loop goes through each section, (a pair of points in edgetop)
1071 # and places the edge blocks and inbetween normal block zones into the row object
1072 for OpnSplitNo
in range(int(len(edgetop
) / 2)):
1073 # left edge is edge<x>[2*OpnSplitNo], right edge edgex[2*OpnSplitNo+1]
1074 leftEdgeIndex
= 2 * OpnSplitNo
1075 rightEdgeIndex
= 2 * OpnSplitNo
+ 1
1077 # get the openings, to save time and confusion
1078 leftOpening
= edgetop
[leftEdgeIndex
][1]
1079 rightOpening
= edgetop
[rightEdgeIndex
][1]
1081 # find the difference between the edge top and bottom on both sides
1082 LTop
= edgetop
[leftEdgeIndex
][0]
1083 LBtm
= edgebtm
[leftEdgeIndex
][0]
1084 RTop
= edgetop
[rightEdgeIndex
][0]
1085 RBtm
= edgebtm
[rightEdgeIndex
][0]
1089 # which is further out on each side, top or bottom?
1091 LNerEdge
= LBtm
# the nearer edge left
1092 LEB
= 1 # Left Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1098 RNerEdge
= RBtm
# the nearer edge right
1099 REB
= 1 # Right Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1103 REB
= -1 # Right Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1105 # The space between the closest edges of the openings in this section of the row
1106 InnerDiff
= RNerEdge
- LNerEdge
1107 # The mid point between the nearest edges
1108 InnerMid
= (RNerEdge
+ LNerEdge
) / 2
1110 # maximum distance to span with one block
1111 MaxWid
= (settings
['w'] + settings
['wv']) / r1
1112 AveWid
= settings
['w']
1114 # check the left and right sides for wedge blocks
1115 # Check and run the left edge first
1116 # find the edge of the correct side, offset for minimum block height. The LEB decides top or bottom
1117 ZPositionCheck
= row
.z
+ (row
.h
/ 2 - settings
['hm']) * LEB
1119 # edgeS may return "None"
1120 LeftWedgeEdge
= leftOpening
.edgeS(ZPositionCheck
, 1)
1122 if (abs(LDiff
) > AveWid
) or (not LeftWedgeEdge
):
1124 if not LeftWedgeEdge
:
1125 LeftWedgeEdge
= leftOpening
.x
1126 wedgeBlocks(row
, leftOpening
, LeftWedgeEdge
, LNerEdge
, LEB
, r1
)
1127 # set the near and far edge settings to vertical, so the other edge blocks don't interfere
1128 LTop
, LBtm
= LNerEdge
, LNerEdge
1131 # Now do the wedge blocks for the right, same drill... repeated code?
1132 # find the edge of the correct side, offset for minimum block height. The REB decides top or bottom
1133 ZPositionCheck
= row
.z
+ (row
.h
/ 2 - settings
['hm']) * REB
1135 # edgeS may return "None"
1136 RightWedgeEdge
= rightOpening
.edgeS(ZPositionCheck
, -1)
1137 if (abs(RDiff
) > AveWid
) or (not RightWedgeEdge
):
1139 if not RightWedgeEdge
:
1140 RightWedgeEdge
= rightOpening
.x
1141 wedgeBlocks(row
, rightOpening
, RNerEdge
, RightWedgeEdge
, REB
, r1
)
1142 # set the near and far edge settings to vertical, so the other edge blocks don't interfere
1143 RTop
, RBtm
= RNerEdge
, RNerEdge
1146 # Check to see if the edges are close enough toegther to warrant a single block filling it
1147 if (InnerDiff
< MaxWid
):
1148 # if this is true, then this row is just one block!
1149 x
= (LNerEdge
+ RNerEdge
) / 2.
1151 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1152 BtmOff
= LBtm
- LNerEdge
1153 TopOff
= LTop
- LNerEdge
1154 ThisBlockOffsets
= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1155 BtmOff
= RBtm
- RNerEdge
1156 TopOff
= RTop
- RNerEdge
1157 ThisBlockOffsets
+= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1158 bevel
= leftOpening
.edgeBev(rowTop
)
1159 bevelBlockOffsets(ThisBlockOffsets
, bevel
, 1)
1160 bevel
= rightOpening
.edgeBev(rowTop
)
1161 bevelBlockOffsets(ThisBlockOffsets
, bevel
, -1)
1162 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, ThisBlockOffsets
])
1165 # it's not one block, must be two or more
1166 # set up the offsets for the left
1167 BtmOff
= LBtm
- LNerEdge
1168 TopOff
= LTop
- LNerEdge
1169 leftOffsets
= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2 + [[0] * 3] * 4
1170 bevelL
= leftOpening
.edgeBev(rowTop
)
1171 bevelBlockOffsets(leftOffsets
, bevelL
, 1)
1172 # and now for the right
1173 BtmOff
= RBtm
- RNerEdge
1174 TopOff
= RTop
- RNerEdge
1175 rightOffsets
= [[0] * 3] * 4 + [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1176 bevelR
= rightOpening
.edgeBev(rowTop
)
1177 bevelBlockOffsets(rightOffsets
, bevelR
, -1)
1178 # check to see if it is only two blocks
1179 if (InnerDiff
< MaxWid
* 2):
1180 # this row is just two blocks! Left block, then right block
1181 # div is the x position of the dividing point between the two bricks
1182 div
= InnerMid
+ (rndd() * settings
['wv']) / r1
1183 # set the grout distance, since we need grout separation between the blocks
1184 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1185 # set the x position and width for the left block
1186 x
= (div
+ LNerEdge
) / 2 - grt
/ 4
1187 w
= (div
- LNerEdge
) - grt
/ 2
1188 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1189 # For reference: EdgeBlocks = [[x, z, w, h, d, [corner offset matrix]], [etc.]]
1190 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, leftOffsets
])
1191 # Initialize for the block on the right side
1192 x
= (div
+ RNerEdge
) / 2 + grt
/ 4
1193 w
= (RNerEdge
- div
) - grt
/ 2
1194 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1195 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, rightOffsets
])
1198 # program should only get here if there are more than two blocks in the row, and no wedge blocks
1199 # make Left edge block
1201 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1202 # set the x position and width for the left block
1203 widOptions
= [settings
['w'], bevelL
+ settings
['wm'], leftOpening
.ts
]
1204 baseWid
= max(widOptions
)
1205 w
= (rndd() * settings
['wv'] + baseWid
+ row
. EdgeOffset
)
1206 widOptions
[0] = settings
['wm']
1208 w
= max(widOptions
) / r1
- grt
1209 x
= w
/ 2 + LNerEdge
+ grt
/ 2
1210 BlockRowL
= x
+ w
/ 2
1211 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1212 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, leftOffsets
])
1214 # make Right edge block
1216 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1217 # set the x position and width for the left block
1218 widOptions
= [settings
['w'], bevelR
+ settings
['wm'], rightOpening
.ts
]
1219 baseWid
= max(widOptions
)
1220 w
= (rndd() * settings
['wv'] + baseWid
+ row
.EdgeOffset
)
1221 widOptions
[0] = settings
['wm']
1223 w
= max(widOptions
) / r1
- grt
1224 x
= RNerEdge
- w
/ 2 - grt
/ 2
1225 BlockRowR
= x
- w
/ 2
1226 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1227 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, rightOffsets
])
1229 row
.RowSegments
.append([BlockRowL
, BlockRowR
])
1233 def plan(Thesketch
, oldrows
=0):
1235 The 'plan' function takes the data generated by the sketch function and the global settings
1236 and creates a list of blocks.
1237 It passes out a list of row heights, edge positions, edge blocks, and rows of blocks.
1239 # if we were passed a list of rows already, use those; else make a list.
1243 # rows holds the important information common to all rows
1244 # rows = [list of row objects]
1247 # splits are places where we NEED a row division, to accomidate openings
1248 # add a split for the bottom row
1249 splits
= [dims
['b'] + settings
['hb']]
1251 # add a split for each critical point on each opening
1252 for hole
in Thesketch
:
1253 splits
+= hole
.crits()
1255 # and, a split for the top row
1256 splits
.append(dims
['t'] - settings
['ht'])
1259 # divs are the normal old row divisions, add them between the top and bottom split
1260 divs
= fill(splits
[0], splits
[-1], settings
['h'], settings
['hm'] + settings
['g'], settings
['hv'])[1: -1]
1262 # remove the divisions that are too close to the splits, so we don't get tiny thin rows
1263 for i
in range(len(divs
) - 1, -1, -1):
1264 for j
in range(len(splits
)):
1265 diff
= abs(divs
[i
] - splits
[j
])
1266 if diff
< (settings
['h'] - settings
['hv'] + settings
['g']):
1270 # now merge the divs and splits lists
1273 # add bottom and/or top points, if bottom and/or top row heights are more than zero
1274 if settings
['hb'] > 0:
1275 divs
.insert(0, dims
['b'])
1276 if settings
['ht'] > 0:
1277 divs
.append(dims
['t'])
1281 # trim the rows to the bottom and top of the wall
1282 if divs
[0] < dims
['b']:
1284 if divs
[-1] > dims
['t']:
1287 # now, make the data for each row
1288 # rows = [[center height,row height,edge offset],[etc.]]
1290 divCount
= len(divs
) - 1 # number of divs to check
1291 divCheck
= 0 # current div entry
1293 while divCheck
< divCount
:
1294 RowZ
= (divs
[divCheck
] + divs
[divCheck
+ 1]) / 2
1295 RowHeight
= divs
[divCheck
+ 1] - divs
[divCheck
] - settings
['g'] + rndc() * \
1296 settings
['rwhl'] * settings
['gv']
1297 EdgeOffset
= settings
['eoff'] * (fmod(divCheck
, 2) - 0.5) + settings
['eoffv'] * rndd()
1299 # if row height is too shallow: delete next div entry, decrement total, and recheck current entry.
1300 if RowHeight
< settings
['hm']:
1301 del(divs
[divCheck
+ 1])
1302 divCount
-= 1 # Adjust count for removed div entry.
1305 rows
.append(rowOb(RowZ
, RowHeight
, EdgeOffset
))
1307 divCheck
+= 1 # on to next div entry
1309 # set up a special opening object to handle the edges of the wall
1310 x
= (dims
['s'] + dims
['e']) / 2
1311 z
= (dims
['t'] + dims
['b']) / 2
1312 w
= (dims
['e'] - dims
['s'])
1313 h
= (dims
['t'] - dims
['b'])
1314 WallBoundaries
= openingInvert(x
, z
, w
, h
)
1316 # Go over each row in the list, set up edge blocks and block sections
1317 for rownum
in range(len(rows
)):
1318 rowProcessing(rows
[rownum
], Thesketch
, WallBoundaries
)
1320 # now return the things everyone needs
1321 # return [rows,edgeBlocks,blockRows,Asketch]
1322 return [rows
, Thesketch
]
1325 def archGeneration(hole
, vlist
, flist
, sideSign
):
1327 Makes arches for the top and bottom, depending on sideSign
1328 example, Lower arch:
1329 archGeneration(hole, vlist, flist, -1)
1330 example, Upper arch:
1331 archGeneration(hole, vlist, flist, 1)
1332 hole is the opening object that the arch is for
1333 add the vertices to vlist
1334 add the faces to flist
1335 sideSign is + or - 1, for the top or bottom arch. Other values may cause errors.
1338 # working arrays for vectors and faces
1342 # Top (1) or bottom (-1)
1344 r
= hole
.rl
# radius of the arch
1345 rt
= hole
.rtl
# thickness of the arch (stone height)
1346 v
= hole
.vl
# height of the arch
1349 r
= hole
.r
# radius of the arch
1350 rt
= hole
.rt
# thickness of the arch (stone height)
1351 v
= hole
.v
# height of the arch
1354 ra
= r
+ rt
/ 2 # average radius of the arch
1360 sideSignInv
= -sideSign
1362 if v
> w
/ 2: # two arcs, to make a pointed arch
1364 zpos
= z
+ (h
/ 2) * sideSign
1366 # left side top, right side bottom
1367 # angles reference straight up, and are in radians
1369 bevHt
= sqrt(bevRad
** 2 - (bevRad
- (w
/ 2 + bev
)) ** 2)
1370 midHalfAngle
= atan(v
/ (r
- w
/ 2))
1371 midHalfAngleBevel
= atan(bevHt
/ (r
- w
/ 2))
1372 bevelAngle
= midHalfAngle
- midHalfAngleBevel
1373 anglebeg
= (PI
/ 2) * (sideSignInv
)
1374 angleend
= (PI
/ 2) * (sideSignInv
) + midHalfAngle
1376 avlist
, aflist
= arch(ra
, rt
, (xoffset
) * (sideSign
), zpos
, anglebeg
, angleend
, bev
, bevelAngle
, len(vlist
))
1378 for i
, vert
in enumerate(avlist
):
1379 avlist
[i
] = [vert
[0] + hole
.x
, vert
[1], vert
[2]]
1383 # right side top, left side bottom
1385 # angles reference straight up, and are in radians
1386 anglebeg
= (PI
/ 2) * (sideSign
) - midHalfAngle
1387 angleend
= (PI
/ 2) * (sideSign
)
1389 avlist
, aflist
= arch(ra
, rt
, (xoffset
) * (sideSignInv
), zpos
, anglebeg
, angleend
, bev
, bevelAngle
, len(vlist
))
1391 for i
, vert
in enumerate(avlist
):
1392 avlist
[i
] = [vert
[0] + hole
.x
, vert
[1], vert
[2]]
1398 Dpth
= settings
['d'] + rndc() * settings
['dv']
1399 Grout
= settings
['g'] + rndc() * settings
['gv']
1400 angleBevel
= (PI
/ 2) * (sideSign
) - midHalfAngle
1401 Wdth
= (rt
- Grout
- bev
) * 2 * sin(angleBevel
) * sideSign
# note, sin may be negative
1402 MidZ
= ((sideSign
) * (bevHt
+ h
/ 2.0) + z
) + (rt
- Grout
- bev
) \
1403 * cos(angleBevel
) # note, cos may come out negative
1404 nearCorner
= sideSign
* (MidZ
- z
) - v
- h
/ 2
1407 TopHt
= hole
.top() - MidZ
- Grout
1410 BtmHt
= - (hole
.btm() - MidZ
) - Grout
1413 # set the amount to bevel the keystone
1414 keystoneBevel
= (bevHt
- v
) * sideSign
1415 if Wdth
>= settings
['hm']:
1416 avlist
, aflist
= MakeAKeystone(x
, Wdth
, MidZ
, TopHt
, BtmHt
, Dpth
, keystoneBevel
, len(vlist
))
1419 for i
, vert
in enumerate(avlist
):
1421 r1
= dims
['t'] * sin(vert
[2] * PI
/ (dims
['t'] * 2))
1424 avlist
[i
] = [((vert
[0] - hole
.x
) / r1
) + hole
.x
, vert
[1], vert
[2]]
1428 # remove "debug note" once bevel is finalized.
1430 debug_prints(func
="archGeneration",
1431 text
="Keystone was too narrow - " + str(Wdth
))
1433 else: # only one arc - curve not peak.
1434 # bottom (sideSign -1) arch has poorly sized blocks...
1436 zpos
= z
+ (sideSign
* (h
/ 2 + v
- r
)) # single arc positioning
1438 # angles reference straight up, and are in radians
1445 halfangle
= atan(w
/ (2 * (r
- v
)))
1447 anglebeg
= angleOffset
- halfangle
1448 angleend
= angleOffset
+ halfangle
1450 anglebeg
= angleOffset
- PI
/ 2
1451 angleend
= angleOffset
+ PI
/ 2
1453 avlist
, aflist
= arch(ra
, rt
, 0, zpos
, anglebeg
, angleend
, bev
, 0.0, len(vlist
))
1455 for i
, vert
in enumerate(avlist
):
1456 avlist
[i
] = [vert
[0] + x
, vert
[1], vert
[2]]
1461 # Make the Side Stones
1462 grt
= (settings
['g'] + rndc() * settings
['gv'])
1463 width
= sqrt(rt
** 2 - c
** 2) - grt
1465 if c
> settings
['hm'] + grt
and c
< width
+ grt
:
1467 subdivision
= settings
['sdv'] * (zpos
+ (h
/ 2) * sideSign
)
1469 subdivision
= settings
['sdv']
1471 # set the height of the block, it should be as high as the max corner position, minus grout
1472 height
= c
- grt
* (0.5 + c
/ (width
+ grt
))
1474 # the vertical offset for the short side of the block
1475 voff
= sideSign
* (settings
['hm'] - height
)
1477 zstart
= z
+ sideSign
* (h
/ 2 + grt
/ 2)
1478 woffset
= width
* (settings
['hm'] + grt
/ 2) / (c
- grt
/ 2)
1479 depth
= rndd() * settings
['dv'] + settings
['d']
1482 offsets
= [[0] * 3] * 6 + [[0] * 2 + [voff
]] * 2
1483 topSide
= zstart
+ height
1486 offsets
= [[0] * 3] * 4 + [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 2
1488 btmSide
= zstart
- height
1489 # Do some stuff to incorporate bev here
1490 bevelBlockOffsets(offsets
, bev
, -1)
1492 avlist
, aflist
= MakeABlock(
1493 [x
- xstart
- width
, x
- xstart
- woffset
, btmSide
, topSide
,
1494 -depth
/ 2, depth
/ 2], subdivision
, len(vlist
),
1495 Offsets
=offsets
, xBevScl
=1
1498 # top didn't use radialized in prev version;
1499 # just noting for clarity - may need to revise for "sideSign == 1"
1501 for i
, vert
in enumerate(avlist
):
1502 avlist
[i
] = [((vert
[0] - x
) / vert
[2]) + x
, vert
[1], vert
[2]]
1507 # keep sizing same - neat arches = master masons :)
1508 # grt = (settings['g'] + rndc()*settings['gv'])
1509 # height = c - grt*(0.5 + c/(width + grt))
1510 # if grout varies may as well change width too... width = sqrt(rt**2 - c**2) - grt
1511 # voff = sideSign * (settings['hm'] - height)
1512 # woffset = width*(settings['hm'] + grt/2)/(c - grt/2)
1515 offsets
= [[0] * 3] * 2 + [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 4
1516 topSide
= zstart
+ height
1519 offsets
= [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 6
1521 btmSide
= zstart
- height
1522 # Do some stuff to incorporate bev here
1523 bevelBlockOffsets(offsets
, bev
, 1)
1525 avlist
, aflist
= MakeABlock(
1526 [x
+ xstart
+ woffset
, x
+ xstart
+ width
, btmSide
, topSide
,
1527 -depth
/ 2, depth
/ 2], subdivision
, len(vlist
),
1528 Offsets
=offsets
, xBevScl
=1
1531 # top didn't use radialized in prev version;
1532 # just noting for clarity - may need to revise for "sideSign == 1"
1534 for i
, vert
in enumerate(avlist
):
1535 avlist
[i
] = [((vert
[0] - x
) / vert
[2]) + x
, vert
[1], vert
[2]]
1544 Build creates the geometry for the wall, based on the
1545 "Aplan" object from the "plan" function. If physics is
1546 enabled, then it make a number of individual blocks with
1547 physics interaction enabled. Otherwise it creates
1548 geometry for the blocks, arches, etc. of the wall.
1554 # all the edge blocks, redacted
1555 # AllBlocks = [[x, z, w, h, d, [corner offset matrix]], [etc.]]
1557 # loop through each row, adding the normal old blocks
1558 for rowidx
in range(len(rows
)):
1559 rows
[rowidx
].FillBlocks()
1563 # If the wall is set to merge blocks, check all the blocks to see if you can merge any
1564 # seems to only merge vertical, should do horizontal too
1566 for rowidx
in range(len(rows
) - 1):
1569 r1
= dims
['t'] * sin(abs(rows
[rowidx
].z
) * PI
/ (dims
['t'] * 2))
1571 r1
= abs(rows
[rowidx
].z
)
1575 Tolerance
= settings
['g'] / r1
1576 idxThis
= len(rows
[rowidx
].BlocksNorm
[:]) - 1
1577 idxThat
= len(rows
[rowidx
+ 1].BlocksNorm
[:]) - 1
1580 # end loop when either array idx wraps
1581 if idxThis
< 0 or idxThat
< 0:
1584 blockThis
= rows
[rowidx
].BlocksNorm
[idxThis
]
1585 blockThat
= rows
[rowidx
+ 1].BlocksNorm
[idxThat
]
1587 # seems to only merge vertical, should do horizontal too...
1588 cx
, cz
, cw
, ch
, cd
= blockThis
[:5]
1589 ox
, oz
, ow
, oh
, od
= blockThat
[:5]
1591 if (abs(cw
- ow
) < Tolerance
) and (abs(cx
- ox
) < Tolerance
):
1597 AllBlocks
.append([(cx
+ ox
) / 2, (cz
+ oz
+ (oh
- ch
) / 2) / 2,
1598 BlockW
, abs(cz
- oz
) + (ch
+ oh
) / 2, (cd
+ od
) / 2, None])
1600 rows
[rowidx
].BlocksNorm
.pop(idxThis
)
1601 rows
[rowidx
+ 1].BlocksNorm
.pop(idxThat
)
1610 # Add blocks to create a "shelf/platform".
1611 # Does not account for openings (crosses gaps - which is a good thing)
1613 SetGrtOff
= settings
['g'] / 2 # half grout for block size modifier
1615 # Use wall block settings for shelf
1616 SetBW
= settings
['w']
1617 SetBWVar
= settings
['wv']
1618 SetBWMin
= settings
['wm']
1619 SetBH
= settings
['h']
1621 # Shelf area settings
1622 ShelfLft
= shelfSpecs
['x']
1623 ShelfBtm
= shelfSpecs
['z']
1624 ShelfEnd
= ShelfLft
+ shelfSpecs
['w']
1625 ShelfTop
= ShelfBtm
+ shelfSpecs
['h']
1626 ShelfThk
= shelfSpecs
['d'] * 2 # use double-depth due to offsets to position at cursor.
1628 # Use "corners" to adjust position so not centered on depth.
1629 # Facing shelf, at cursor (middle of wall blocks)
1630 # - this way no gaps between platform and wall face due to wall block depth.
1631 wallDepth
= settings
['d'] / 2 # offset by wall depth so step depth matches UI setting :)
1632 if shelfBack
: # place blocks on backside of wall
1634 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1635 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1636 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1637 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0]
1641 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1642 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1643 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1644 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0]
1647 # Add blocks for each "shelf row" in area
1648 while ShelfBtm
< ShelfTop
:
1650 # Make blocks for each row - based on rowOb::fillblocks
1651 # Does not vary grout.
1652 divs
= fill(ShelfLft
, ShelfEnd
, SetBW
, SetBWMin
, SetBWVar
)
1654 # loop through the row divisions, adding blocks for each one
1655 for i
in range(len(divs
) - 1):
1656 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
1657 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - SetGrtOff
1659 AllBlocks
.append([ThisBlockx
, ShelfBtm
, ThisBlockw
, SetBH
, ShelfThk
, ShelfOffsets
])
1661 ShelfBtm
+= SetBH
+ SetGrtOff
# moving up to next row...
1663 # Add blocks to create "steps".
1664 # Does not account for openings (crosses gaps - which is a good thing)
1666 SetGrtOff
= settings
['g'] / 2 # half grout for block size modifier
1668 # Vary block width by wall block variations.
1669 SetWidVar
= settings
['wv']
1670 SetWidMin
= settings
['wm']
1672 StepXMod
= stepSpecs
['t'] # width of step/tread, also sets basic block size.
1673 StepZMod
= stepSpecs
['v']
1675 StepLft
= stepSpecs
['x']
1676 StepRt
= stepSpecs
['x'] + stepSpecs
['w']
1677 StepBtm
= stepSpecs
['z'] + StepZMod
/ 2 # Start offset for centered blocks
1678 StepWide
= stepSpecs
['w']
1679 StepTop
= StepBtm
+ stepSpecs
['h']
1680 StepThk
= stepSpecs
['d'] * 2 # use double-depth due to offsets to position at cursor.
1682 # Use "corners" to adjust steps so not centered on depth.
1683 # Facing steps, at cursor (middle of wall blocks)
1684 # - this way no gaps between steps and wall face due to wall block depth.
1685 # Also, will work fine as stand-alone if not used with wall (try block depth 0 and see what happens).
1686 wallDepth
= settings
['d'] / 2
1687 if stepBack
: # place blocks on backside of wall
1689 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1690 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1691 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1692 [0, StepThk
/ 2, 0], [0, wallDepth
, 0]
1696 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1697 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1698 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1699 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0]
1702 # Add steps for each "step row" in area (neg width is interesting but prevented)
1703 while StepBtm
< StepTop
and StepWide
> 0:
1705 # Make blocks for each step row - based on rowOb::fillblocks
1706 # Does not vary grout.
1708 if stepOnly
: # "cantilevered steps"
1710 stepStart
= StepRt
- StepXMod
1714 AllBlocks
.append([stepStart
, StepBtm
, StepXMod
, StepZMod
, StepThk
, StepOffsets
])
1716 divs
= fill(StepLft
, StepRt
, StepXMod
, SetWidMin
, SetWidVar
)
1718 # loop through the row divisions, adding blocks for each one
1719 for i
in range(len(divs
) - 1):
1720 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
1721 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - SetGrtOff
1723 AllBlocks
.append([ThisBlockx
, StepBtm
, ThisBlockw
, StepZMod
, StepThk
, StepOffsets
])
1725 StepBtm
+= StepZMod
+ SetGrtOff
# moving up to next row...
1726 StepWide
-= StepXMod
# reduce step width
1728 # adjust side limit depending on direction of steps
1730 StepRt
-= StepXMod
# move in from right
1732 StepLft
+= StepXMod
# move in from left
1734 # Copy all the blocks out of the rows
1736 AllBlocks
+= row
.BlocksEdge
1737 AllBlocks
+= row
.BlocksNorm
1739 # This loop makes individual blocks for each block specified in the plan
1740 for block
in AllBlocks
:
1741 x
, z
, w
, h
, d
, corners
= block
1744 r1
= dims
['t'] * sin(z
* PI
/ (dims
['t'] * 2))
1750 geom
= MakeABlock([x
- w
/ 2, x
+ w
/ 2, z
- h
/ 2, z
+ h
/ 2, -d
/ 2, d
/ 2],
1751 settings
['sdv'], len(vlist
),
1752 corners
, None, settings
['b'] + rndd() * settings
['bv'], r1
)
1756 # This loop makes Arches for every opening specified in the plan.
1757 for hole
in Aplan
[1]:
1759 if hole
.vl
> 0 and hole
.rtl
> (settings
['g'] + settings
['hm']): # make lower arch blocks
1760 archGeneration(hole
, vlist
, flist
, -1)
1763 if hole
.v
> 0 and hole
.rt
> (settings
['g'] + settings
['hm']): # make upper arch blocks
1764 archGeneration(hole
, vlist
, flist
, 1)
1766 # Warp all the points for domed stonework
1768 for i
, vert
in enumerate(vlist
):
1769 vlist
[i
] = [vert
[0], (dims
['t'] + vert
[1]) * cos(vert
[2] * PI
/ (2 * dims
['t'])),
1770 (dims
['t'] + vert
[1]) * sin(vert
[2] * PI
/ (2 * dims
['t']))]
1772 # Warp all the points for radial stonework
1774 for i
, vert
in enumerate(vlist
):
1775 vlist
[i
] = [vert
[2] * cos(vert
[0]), vert
[2] * sin(vert
[0]), vert
[1]]
1781 def createWall(radial
, curve
, openings
, mergeBlox
, shelf
, shelfSide
,
1782 steps
, stepDir
, stepBare
, stepSide
):
1784 Call all the functions you need to make a wall, return the verts and faces.
1797 # set all the working variables from passed parameters
1801 openingSpecs
= openings
1802 bigBlock
= mergeBlox
1806 shelfBack
= shelfSide
1811 aplan
= plan(asketch
, 0)