1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Authors: dudecon, jambay
7 # Grout needs to be implemented.
8 # consider removing wedge crit for small "c" and "cl" values
9 # wrap around for openings on radial stonework?
10 # auto-clip wall edge to SMALL for radial and domes.
11 # unregister doesn't release all references.
12 # repeat for opening doesn't distribute evenly when radialized - see wrap around
14 # if opening width == indent*2 the edge blocks fail (row of blocks cross opening).
15 # if openings overlap fills inverse with blocks - see h/v slots.
16 # Negative grout width creates a pair of phantom blocks, separated by grout
17 # width, inside the edges.
18 # if block width variance is 0, and edging is on, right edge blocks create a "vertical seam"
22 from random
import random
29 # Set to True to enable debug_prints
33 SMALL
= 0.000000000001
34 # for values that must be != 0; see UI options/variables - sort of a bug to be fixed
39 # General masonry Settings
40 # ------------------------
42 'w': 1.2, 'wv': 0.3, 'h': .6, 'hv': 0.3, 'd': 0.3, 'dv': 0.1,
43 'g': 0.1, 'gv': 0.07, 'gd': 0.01, 'gdv': 0.0, 'b': 0, 'bv': 0,
44 'f': 0.0, 'fv': 0.0, 't': 0.0, 'sdv': 0.1, 'hwt': 0.5, 'aln': 0,
45 'wm': 0.8, 'hm': 0.3, 'dm': 0.1,
46 'woff': 0.0, 'woffv': 0.0, 'eoff': 0.3, 'eoffv': 0.0, 'rwhl': 1,
47 'hb': 0, 'ht': 0, 'ge': 0, 'physics': 0
50 settings DOCUMENTATION:
51 'w':width 'wv':widthVariation
52 'h':height 'hv':heightVariation
53 'd':depth 'dv':depthVariation
54 'g':grout 'gv':groutVariation 'gd':groutDepth 'gdv':groutDepthVariation
55 'b':bevel 'bv':bevelVariation
56 'f':flawSize 'fv':flawSizeVariation 'ff':flawFraction
58 'sdv':subdivision(distance or angle)
59 'hwt':row height effect on block widths in the row (0=no effect,
60 1=1:1 relationship, negative values allowed, 0.5 works well)
61 'aln':alignment(0=none, 1=rows w/features, 2=features w/rows)
63 'wm':width minimum 'hm':height minimum 'dm':depth minimum
64 'woff':row start offset(fraction of width)
65 'woffv':width offset variation(fraction of width)
66 'eoff':edge offset 'eoffv':edge offset variation
67 'rwhl':row height lock(1 is all blocks in row have same height)
68 'hb':bottom row height 'ht': top row height 'ge': grout the edges
69 'physics': set up for physics
72 # dims = area of wall (face)
73 # ------------------------
75 's': 0, 'e': PI
* 3 / 2, 'b': 0.1, 't': 12.3
79 's':start x or theta 'e':end x or theta 'b':bottom z or r 't':top z or r
80 'w' = e-s and h = t-b; calculated to optimize for various operations/usages
81 dims = {'s':-12, 'e':15, 'w':27, 'b':-15., 't':15., 'h':30}
82 dims = {'s':-bayDim/2, 'e':bayDim/2, 'b':-5., 't':10.} # bay settings?
85 # ------------------------
86 radialized
= 0 # Radiating from one point - round/disc; instead of square
87 slope
= 0 # Warp/slope; curved over like a vaulted tunnel
89 # 'bigblock': merge adjacent blocks into single large blocks
90 bigBlock
= 0 # Merge blocks
93 # Gaps in blocks for various apertures
94 # ------------------------
97 {'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 2.7, 'rp': 1, 'b': 0.0,
98 'v': 0, 'vl': 0, 't': 0, 'tl': 0}
101 openingSpecs DOCUMENTATION:
102 'w': opening width, 'h': opening height,
103 'x': horizontal position, 'z': vertical position,
104 'rp': make multiple openings, with a spacing of x,
105 'b': bevel the opening, inside only, like an arrow slit.
106 'v': height of the top arch, 'vl':height of the bottom arch,
107 't': thickness of the top arch, 'tl': thickness of the bottom arch
110 # Add blocks to make platforms
111 # ------------------------
115 'w': 0.5, 'h': 0.5, 'd': 0.3, 'x': 0.8, 'z': 2.7
118 shelfSpecs DOCUMENTATION:
119 'w': block width, 'h': block height, 'd': block depth (shelf size; offset from wall)
120 'x': horizontal start position, 'z': vertical start position
123 # Add blocks to make steps
124 # ------------------------
128 'x': 0.0, 'z': -10, 'w': 10.0, 'h': 10.0,
129 'v': 0.7, 't': 1.0, 'd': 1.0
132 stepSpecs DOCUMENTATION:
133 'x': horizontal start position, 'z': vertical start position,
134 'w': step area width, 'h': step area height,
135 'v': riser height, 't': tread width, 'd': block depth (step size; offset from wall)
144 def debug_prints(func
="", text
="Message", var
=None):
147 print("\n[{}]\nmessage: {}".format(func
, text
))
149 print("Error: ", var
)
152 # pass variables just like for the regular prints
153 def debug_print_vars(*args
, **kwargs
):
156 print(*args
, **kwargs
)
159 # easier way to get to the random function
164 # random number from -0.5 to 0.5
166 return (random() - 0.5)
169 # random number from -1.0 to 1.0
171 return (random() - 0.5) * 2.0
175 # opening test function
178 dims
= {'s': -29., 'e': 29., 'b': -6., 't': TestN
* 7.5}
180 for i
in range(TestN
):
181 x
= (random() - 0.5) * 6
183 v
= .2 + i
* (3. / TestN
)
184 vl
= 3.2 - i
* (3. / TestN
)
188 openingSpecs
+= [{'w': 3.1 + rn
, 'h': 0.3 + rn
, 'x': float(x
),
189 'z': float(z
), 'rp': 0, 'b': 0.,
190 'v': float(v
), 'vl': float(vl
),
191 't': float(t
), 'tl': float(tl
)}]
192 return dims
, openingSpecs
195 # dims, openingSpecs = test(15)
198 # For filling a linear space with divisions
199 def fill(left
, right
, avedst
, mindst
=0.0, dev
=0.0, pad
=(0.0, 0.0), num
=0,
202 Fills a linear range with points and returns an ordered list of those points
203 including the end points.
205 left: the lower boundary
206 right: the upper boundary
207 avedst: the average distance between points
208 mindst: the minimum distance between points
209 dev: the maximum random deviation from avedst
210 pad: tends to move the points near the bounds right (positive) or
212 element 0 pads the lower bounds, element 1 pads the upper bounds
213 num: substitutes a numerical limit for the right limit. fill will then make
215 center: flag to center the elements in the range, 0 == disabled
219 curpos
= left
+ pad
[0]
221 # Set offset by average spacing, then add blocks (fall through);
222 # if not at right edge.
224 curpos
+= ((right
- left
- mindst
* 2) % avedst
) / 2 + mindst
225 if curpos
- poslist
[-1] < mindst
:
226 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
228 # clip to right edge.
229 if (right
- curpos
< mindst
) or (right
- curpos
< mindst
- pad
[1]):
230 poslist
.append(right
)
234 poslist
.append(curpos
)
241 curpos
+= avedst
+ rndd() * dev
242 if curpos
- poslist
[-1] < mindst
:
243 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
244 poslist
.append(curpos
)
251 while True: # loop for blocks
252 curpos
+= avedst
+ rndd() * dev
253 if curpos
- poslist
[-1] < mindst
:
254 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
255 # close off edges at limit
256 if (right
- curpos
< mindst
) or (right
- curpos
< mindst
- pad
[1]):
257 poslist
.append(right
)
260 poslist
.append(curpos
)
263 # For generating block geometry
264 def MakeABlock(bounds
, segsize
, vll
=0, Offsets
=None, FaceExclude
=[],
267 MakeABlock returns lists of points and faces to be made into a square
268 cornered block, subdivided along the length, with optional bevels.
269 bounds: a list of boundary positions:
270 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
271 segsize: the maximum size before lengthwise subdivision occurs
272 vll: the number of vertexes already in the mesh. len(mesh.verts) should
274 Offsets: list of coordinate delta values.
275 Offsets are lists, [x,y,z] in
282 5:right_bottom_front,
286 FaceExclude: list of faces to exclude from the faces list. see bounds above for indices
287 xBevScl: how much to divide the end (+- x axis) bevel dimensions. Set to current average
288 radius to compensate for angular distortion on curved blocks
291 slices
= fill(bounds
[0], bounds
[1], segsize
, segsize
, center
=1)
296 points
.append([slices
[0], bounds
[4], bounds
[2]])
297 points
.append([slices
[0], bounds
[5], bounds
[2]])
298 points
.append([slices
[0], bounds
[5], bounds
[3]])
299 points
.append([slices
[0], bounds
[4], bounds
[3]])
301 for x
in slices
[1:-1]:
302 points
.append([x
, bounds
[4], bounds
[2]])
303 points
.append([x
, bounds
[5], bounds
[2]])
304 points
.append([x
, bounds
[5], bounds
[3]])
305 points
.append([x
, bounds
[4], bounds
[3]])
307 points
.append([slices
[-1], bounds
[4], bounds
[2]])
308 points
.append([slices
[-1], bounds
[5], bounds
[2]])
309 points
.append([slices
[-1], bounds
[5], bounds
[3]])
310 points
.append([slices
[-1], bounds
[4], bounds
[3]])
313 points
.append([slices
[0] + Offsets
[0][0], bounds
[4] + Offsets
[0][1], bounds
[2] + Offsets
[0][2]])
314 points
.append([slices
[0] + Offsets
[1][0], bounds
[5] + Offsets
[1][1], bounds
[2] + Offsets
[1][2]])
315 points
.append([slices
[0] + Offsets
[3][0], bounds
[5] + Offsets
[3][1], bounds
[3] + Offsets
[3][2]])
316 points
.append([slices
[0] + Offsets
[2][0], bounds
[4] + Offsets
[2][1], bounds
[3] + Offsets
[2][2]])
318 for x
in slices
[1: -1]:
319 xwt
= (x
- bounds
[0]) / (bounds
[1] - bounds
[0])
320 points
.append([x
+ Offsets
[0][0] * (1 - xwt
) + Offsets
[4][0] * xwt
,
321 bounds
[4] + Offsets
[0][1] * (1 - xwt
) + Offsets
[4][1] * xwt
,
322 bounds
[2] + Offsets
[0][2] * (1 - xwt
) + Offsets
[4][2] * xwt
])
323 points
.append([x
+ Offsets
[1][0] * (1 - xwt
) + Offsets
[5][0] * xwt
,
324 bounds
[5] + Offsets
[1][1] * (1 - xwt
) + Offsets
[5][1] * xwt
,
325 bounds
[2] + Offsets
[1][2] * (1 - xwt
) + Offsets
[5][2] * xwt
])
326 points
.append([x
+ Offsets
[3][0] * (1 - xwt
) + Offsets
[7][0] * xwt
,
327 bounds
[5] + Offsets
[3][1] * (1 - xwt
) + Offsets
[7][1] * xwt
,
328 bounds
[3] + Offsets
[3][2] * (1 - xwt
) + Offsets
[7][2] * xwt
])
329 points
.append([x
+ Offsets
[2][0] * (1 - xwt
) + Offsets
[6][0] * xwt
,
330 bounds
[4] + Offsets
[2][1] * (1 - xwt
) + Offsets
[6][1] * xwt
,
331 bounds
[3] + Offsets
[2][2] * (1 - xwt
) + Offsets
[6][2] * xwt
])
333 points
.append([slices
[-1] + Offsets
[4][0], bounds
[4] + Offsets
[4][1], bounds
[2] + Offsets
[4][2]])
334 points
.append([slices
[-1] + Offsets
[5][0], bounds
[5] + Offsets
[5][1], bounds
[2] + Offsets
[5][2]])
335 points
.append([slices
[-1] + Offsets
[7][0], bounds
[5] + Offsets
[7][1], bounds
[3] + Offsets
[7][2]])
336 points
.append([slices
[-1] + Offsets
[6][0], bounds
[4] + Offsets
[6][1], bounds
[3] + Offsets
[6][2]])
338 faces
.append([vll
, vll
+ 3, vll
+ 2, vll
+ 1])
340 for x
in range(len(slices
) - 1):
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
+ 1, vll
+ 5, vll
+ 4])
347 faces
.append([vll
, vll
- 3, vll
+ 1, vll
+ 4])
350 faces
.append([vll
, vll
+ 1, vll
+ 2, vll
+ 3])
355 # For generating Keystone Geometry
357 def MakeAKeystone(xpos
, width
, zpos
, ztop
, zbtm
, thick
, bevel
, vll
=0, FaceExclude
=[], xBevScl
=1):
359 MakeAKeystone returns lists of points and faces to be made into a
360 square cornered keystone, with optional bevels.
361 xpos: x position of the centerline
362 width: x width of the keystone at the widest point (discounting bevels)
363 zpos: z position of the widest point
364 ztop: distance from zpos to the top
365 zbtm: distance from zpos to the bottom
367 bevel: the amount to raise the back vertex to account for arch beveling
368 vll: the number of vertexes already in the mesh. len(mesh.verts) should give this number
369 faceExclude: list of faces to exclude from the faces list.
370 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
371 xBevScl: how much to divide the end (+- x axis) bevel dimensions.
372 Set to current average radius to compensate for angular distortion on curved blocks
377 faceinclude
= [1 for x
in range(6)]
378 for x
in FaceExclude
:
385 # The front top point
386 points
.append([xpos
, Thk
, Top
])
387 # The front left point
388 points
.append([xpos
- Wid
, Thk
, zpos
])
389 # The front bottom point
390 points
.append([xpos
, Thk
, Btm
])
391 # The front right point
392 points
.append([xpos
+ Wid
, Thk
, zpos
])
396 MirrorPoints
.append([i
[0], -i
[1], i
[2]])
397 points
+= MirrorPoints
398 points
[6][2] += bevel
400 faces
.append([3, 2, 1, 0])
401 faces
.append([4, 5, 6, 7])
402 faces
.append([4, 7, 3, 0])
403 faces
.append([5, 4, 0, 1])
404 faces
.append([6, 5, 1, 2])
405 faces
.append([7, 6, 2, 3])
406 # Offset the vertex numbers by the number of vertices already in the list
407 for i
in range(len(faces
)):
408 for j
in range(len(faces
[i
])):
414 # for finding line/circle intercepts
416 def circ(offs
=0., r
=1.):
418 offs is the distance perpendicular to the line to the center of the circle
419 r is the radius of the circle
420 circ returns the distance parallel to the line to the center of the circle at the intercept.
428 return sqrt(r
** 2 - offs
** 2)
431 # class openings in the wall
435 This is the class for holding the data for the openings in the wall.
436 It has methods for returning the edges of the opening for any given position value,
437 as well as bevel settings and top and bottom positions.
438 It stores the 'style' of the opening, and all other pertinent information.
440 # x = 0. # x position of the opening
441 # z = 0. # x position of the opening
442 # w = 0. # width of the opening
443 # h = 0. # height of the opening
444 r
= 0 # top radius of the arch (derived from 'v')
445 rl
= 0 # lower radius of the arch (derived from 'vl')
446 rt
= 0 # top arch thickness
447 rtl
= 0 # lower arch thickness
448 ts
= 0 # Opening side thickness, if greater than average width, replaces it.
449 c
= 0 # top arch corner position (for low arches), distance from the top of the straight sides
450 cl
= 0 # lower arch corner position (for low arches), distance from the top of the straight sides
451 # form = 0 # arch type (unused for now)
452 # b = 0. # back face bevel distance, like an arrow slit
453 v
= 0. # top arch height
454 vl
= 0. # lower arch height
455 # variable "s" is used for "side" in the "edge" function.
456 # it is a signed int, multiplied by the width to get + or - of the center
459 if self
.vl
<= self
.w
/ 2:
460 return self
.z
- self
.h
/ 2 - self
.vl
- self
.rtl
462 return self
.z
- sqrt((self
.rl
+ self
.rtl
) ** 2 - (self
.rl
- self
.w
/ 2) ** 2) - self
.h
/ 2
465 if self
.v
<= self
.w
/ 2:
466 return self
.z
+ self
.h
/ 2 + self
.v
+ self
.rt
468 return sqrt((self
.r
+ self
.rt
) ** 2 - (self
.r
- self
.w
/ 2) ** 2) + self
.z
+ self
.h
/ 2
470 # crits returns the critical split points, or discontinuities, used for making rows
473 if self
.vl
> 0: # for lower arch
474 # add the top point if it is pointed
475 # if self.vl >= self.w/2.: critlist.append(self.btm())
476 if self
.vl
< self
.w
/ 2.: # else: for low arches, with wedge blocks under them
477 # critlist.append(self.btm())
478 critlist
.append(self
.z
- self
.h
/ 2 - self
.cl
)
480 if self
.h
> 0: # if it has a height, append points at the top and bottom of the main square section
481 critlist
+= [self
.z
- self
.h
/ 2, self
.z
+ self
.h
/ 2]
482 else: # otherwise, append just one in the center
483 critlist
.append(self
.z
)
485 if self
.v
> 0: # for the upper arch
486 if self
.v
< self
.w
/ 2: # add the splits for the upper wedge blocks, if needed
487 critlist
.append(self
.z
+ self
.h
/ 2 + self
.c
)
488 # critlist.append(self.top())
489 # otherwise just add the top point, if it is pointed
490 # else: critlist.append(self.top())
494 # get the side position of the opening.
495 # ht is the z position; s is the side: 1 for right, -1 for left
496 # if the height passed is above or below the opening, return None
497 def edgeS(self
, ht
, s
):
499 # set the row radius: 1 for standard wall (flat)
502 r1
= abs(dims
['t'] * sin(ht
* PI
/ (dims
['t'] * 2)))
508 # Go through all the options, and return the correct value
509 if ht
< self
.btm(): # too low
511 elif ht
> self
.top(): # too high
514 # Check for circ returning None - prevent TypeError (script failure) with float.
515 # in this range, pass the lower arch info
516 elif ht
<= self
.z
- self
.h
/ 2 - self
.cl
:
517 if self
.vl
> self
.w
/ 2:
518 circVal
= circ(ht
- self
.z
+ self
.h
/ 2, self
.rl
+ self
.rtl
)
522 return self
.x
+ s
* (self
.w
/ 2. - self
.rl
+ circVal
) / r1
524 circVal
= circ(ht
- self
.z
+ self
.h
/ 2 + self
.vl
- self
.rl
, self
.rl
+ self
.rtl
)
528 return self
.x
+ s
* circVal
/ r1
530 # in this range, pass the top arch info
531 elif ht
>= self
.z
+ self
.h
/ 2 + self
.c
:
532 if self
.v
> self
.w
/ 2:
533 circVal
= circ(ht
- self
.z
- self
.h
/ 2, self
.r
+ self
.rt
)
537 return self
.x
+ s
* (self
.w
/ 2. - self
.r
+ circVal
) / r1
539 circVal
= circ(ht
- (self
.z
+ self
.h
/ 2 + self
.v
- self
.r
), self
.r
+ self
.rt
)
543 return self
.x
+ s
* circVal
/ r1
545 # in this range pass the lower corner edge info
546 elif ht
<= self
.z
- self
.h
/ 2:
547 d
= sqrt(self
.rtl
** 2 - self
.cl
** 2)
548 if self
.cl
> self
.rtl
/ sqrt(2.):
549 return self
.x
+ s
* (self
.w
/ 2 + (self
.z
- self
.h
/ 2 - ht
) * d
/ self
.cl
) / r1
551 return self
.x
+ s
* (self
.w
/ 2 + d
) / r1
553 # in this range pass the upper corner edge info
554 elif ht
>= self
.z
+ self
.h
/ 2:
555 d
= sqrt(self
.rt
** 2 - self
.c
** 2)
556 if self
.c
> self
.rt
/ sqrt(2.):
557 return self
.x
+ s
* (self
.w
/ 2 + (ht
- self
.z
- self
.h
/ 2) * d
/ self
.c
) / r1
559 return self
.x
+ s
* (self
.w
/ 2 + d
) / r1
561 # in this range, pass the middle info (straight sides)
563 return self
.x
+ s
* self
.w
/ 2 / r1
565 # get the top or bottom of the opening
566 # ht is the x position; s is the side: 1 for top, -1 for bottom
567 def edgeV(self
, ht
, s
):
569 dist
= abs(self
.x
- ht
)
571 def radialAdjust(dist
, sideVal
):
572 # take the distance and adjust for radial geometry, return dist
575 dist
= dist
* abs(dims
['t'] * sin(sideVal
* PI
/ (dims
['t'] * 2)))
577 dist
= dist
* sideVal
580 if s
> 0: # and (dist <= self.edgeS(self.z + self.h / 2 + self.c, 1) - self.x): # check top down
581 # hack for radialized masonry, import approx Z instead of self.top()
582 dist
= radialAdjust(dist
, self
.top())
584 # no arch on top, flat
586 return self
.z
+ self
.h
/ 2
588 # pointed arch on top
589 elif self
.v
> self
.w
/ 2:
590 circVal
= circ(dist
- self
.w
/ 2 + self
.r
, self
.r
+ self
.rt
)
594 return self
.z
+ self
.h
/ 2 + circVal
598 circVal
= circ(dist
, self
.r
+ self
.rt
)
602 return self
.z
+ self
.h
/ 2 + self
.v
- self
.r
+ circVal
604 else: # and (dist <= self.edgeS(self.z - self.h / 2 - self.cl, 1) - self.x): # check bottom up
605 # hack for radialized masonry, import approx Z instead of self.top()
606 dist
= radialAdjust(dist
, self
.btm())
610 return self
.z
- self
.h
/ 2
612 # pointed arch on bottom
613 elif self
.vl
> self
.w
/ 2:
614 circVal
= circ(dist
- self
.w
/ 2 + self
.rl
, self
.rl
+ self
.rtl
)
618 return self
.z
- self
.h
/ 2 - circVal
620 # old conditional? if (dist-self.w / 2 + self.rl) <= (self.rl + self.rtl):
621 # domed arch on bottom
623 circVal
= circ(dist
, self
.rl
+ self
.rtl
) # dist-self.w / 2 + self.rl
627 return self
.z
- self
.h
/ 2 - self
.vl
+ self
.rl
- circVal
629 # and this never happens - but, leave it as failsafe :)
630 debug_prints(func
="opening.EdgeV",
631 text
="Got all the way out of the edgeV! Not good!")
632 debug_print_vars("opening x = ", self
.x
, ", opening z = ", self
.z
)
636 def edgeBev(self
, ht
):
637 if ht
> (self
.z
+ self
.h
/ 2):
639 if ht
< (self
.z
- self
.h
/ 2):
643 r1
= abs(dims
['t'] * sin(ht
* PI
/ (dims
['t'] * 2)))
651 def __init__(self
, xpos
, zpos
, width
, height
, archHeight
=0, archThk
=0,
652 archHeightLower
=0, archThkLower
=0, bevel
=0, edgeThk
=0):
655 self
.w
= float(width
)
656 self
.h
= float(height
)
658 self
.rtl
= archThkLower
660 self
.vl
= archHeightLower
664 # find the upper arch radius
665 if archHeight
>= width
/ 2:
666 # just one arch, low long
667 self
.r
= (self
.v
** 2) / self
.w
+ self
.w
/ 4
668 elif archHeight
<= 0:
674 self
.r
= (self
.w
** 2) / (8 * self
.v
) + self
.v
/ 2.
675 self
.c
= self
.rt
* cos(atan(self
.w
/ (2 * (self
.r
- self
.v
))))
677 # find the lower arch radius
678 if archHeightLower
>= width
/ 2:
679 self
.rl
= (self
.vl
** 2) / self
.w
+ self
.w
/ 4
680 elif archHeightLower
<= 0:
684 self
.rl
= (self
.w
** 2) / (8 * self
.vl
) + self
.vl
/ 2.
685 self
.cl
= self
.rtl
* cos(atan(self
.w
/ (2 * (self
.rl
- self
.vl
))))
687 # self.form = something?
688 self
.b
= float(bevel
)
692 # class for the whole wall boundaries; a sub-class of "opening"
693 class openingInvert(opening
):
694 # this is supposed to switch the sides of the opening
695 # so the wall will properly enclose the whole wall.
697 def edgeS(self
, ht
, s
):
698 return opening
.edgeS(self
, ht
, -s
)
700 def edgeV(self
, ht
, s
):
701 return opening
.edgeV(self
, ht
, -s
)
704 # class rows in the wall
708 This is the class for holding the data for individual rows of blocks.
709 each row is required to have some edge blocks, and can also have
710 intermediate sections of "normal" blocks.
715 def FillBlocks(self
):
716 # Set the radius variable, in the case of radial geometry
719 self
.radius
= dims
['t'] * (sin(self
.z
* PI
/ (dims
['t'] * 2)))
723 # initialize internal variables from global settings
726 SetHwt
= settings
['hwt']
727 SetWid
= settings
['w']
728 SetWidMin
= settings
['wm']
729 SetWidVar
= settings
['wv']
730 SetGrt
= settings
['g']
731 SetGrtVar
= settings
['gv']
732 SetRowHeightLink
= settings
['rwhl']
733 SetDepth
= settings
['d']
734 SetDepthVar
= settings
['dv']
736 # height weight, used for making shorter rows have narrower blocks, and vice-versa
737 hwt
= ((self
.h
/ SetH
- 1) * SetHwt
+ 1)
739 # set variables for persistent values: loop optimization, readability, single ref for changes.
741 avgDist
= hwt
* SetWid
/ self
.radius
742 minDist
= SetWidMin
/ self
.radius
743 deviation
= hwt
* SetWidVar
/ self
.radius
744 grtOffset
= SetGrt
/ (2 * self
.radius
)
746 # init loop variables that may change...
748 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (self
.radius
)
749 ThisBlockHeight
= self
.h
+ rndc() * (1 - SetRowHeightLink
) * SetGrtVar
750 ThisBlockDepth
= rndd() * SetDepthVar
+ SetDepth
752 for segment
in self
.RowSegments
:
753 divs
= fill(segment
[0] + grtOffset
, segment
[1] - grtOffset
, avgDist
, minDist
, deviation
)
755 # loop through the divisions, adding blocks for each one
756 for i
in range(len(divs
) - 1):
757 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
758 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - grt
760 self
.BlocksNorm
.append([ThisBlockx
, self
.z
, ThisBlockw
, ThisBlockHeight
, ThisBlockDepth
, None])
762 if SetDepthVar
: # vary depth
763 ThisBlockDepth
= rndd() * SetDepthVar
+ SetDepth
765 if SetGrtVar
: # vary grout
766 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (self
.radius
)
767 ThisBlockHeight
= self
.h
+ rndc() * (1 - SetRowHeightLink
) * SetGrtVar
769 def __init__(self
, centerheight
, rowheight
, edgeoffset
=0.):
770 self
.z
= float(centerheight
)
771 self
.h
= float(rowheight
)
772 self
.EdgeOffset
= float(edgeoffset
)
774 # THIS INITIALIZATION IS IMPORTANT! OTHERWISE ALL OBJECTS WILL HAVE THE SAME LISTS!
776 self
.RowSegments
= []
780 def arch(ra
, rt
, x
, z
, archStart
, archEnd
, bevel
, bevAngle
, vll
):
782 Makes a list of faces and vertexes for arches.
783 ra: the radius of the arch, to the center of the bricks
784 rt: the thickness of the arch
785 x: x center location of the circular arc, as if the arch opening were centered on x = 0
786 z: z center location of the arch
787 anglebeg: start angle of the arch, in radians, from vertical?
788 angleend: end angle of the arch, in radians, from vertical?
789 bevel: how much to bevel the inside of the arch.
790 vll: how long is the vertex list already?
795 # initialize internal variables for global settings
796 SetGrt
= settings
['g']
797 SetGrtVar
= settings
['gv']
798 SetDepth
= settings
['d']
799 SetDepthVar
= settings
['dv']
801 # Init loop variables
803 def bevelEdgeOffset(offsets
, bevel
, side
):
805 Take the block offsets and modify it for the correct bevel.
807 offsets = the offset list. See MakeABlock
808 bevel = how much to offset the edge
809 side = -1 for left (right side), 1 for right (left side)
814 pointsToAffect
= right
816 pointsToAffect
= left
817 for num
in pointsToAffect
:
818 offsets
[num
] = offsets
[num
][:]
819 offsets
[num
][0] += -bevel
* side
821 ArchInner
= ra
- rt
/ 2
822 ArchOuter
= ra
+ rt
/ 2 - SetGrt
+ rndc() * SetGrtVar
824 DepthBack
= - SetDepth
/ 2 - rndc() * SetDepthVar
825 DepthFront
= SetDepth
/ 2 + rndc() * SetDepthVar
828 subdivision
= settings
['sdv']
832 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (2 * ra
) # init grout offset for loop
833 # set up the offsets, it will be the same for every block
834 offsets
= ([[0] * 2 + [bevel
]] + [[0] * 3] * 3) * 2
836 # make the divisions in the "length" of the arch
837 divs
= fill(archStart
, archEnd
, settings
['w'] / ra
, settings
['wm'] / ra
, settings
['wv'] / ra
)
839 for i
in range(len(divs
) - 1):
841 ThisOffset
= offsets
[:]
842 bevelEdgeOffset(ThisOffset
, bevAngle
, - 1)
843 elif i
== len(divs
) - 2:
844 ThisOffset
= offsets
[:]
845 bevelEdgeOffset(ThisOffset
, bevAngle
, 1)
850 [divs
[i
] + grt
, divs
[i
+ 1] - grt
, ArchInner
, ArchOuter
, DepthBack
, DepthFront
],
851 subdivision
, len(avlist
) + vll
, ThisOffset
, [], None, ra
857 if SetDepthVar
: # vary depth
858 DepthBack
= -SetDepth
/ 2 - rndc() * SetDepthVar
859 DepthFront
= SetDepth
/ 2 + rndc() * SetDepthVar
861 if SetGrtVar
: # vary grout
862 grt
= (settings
['g'] + rndc() * SetGrtVar
) / (2 * ra
)
863 ArchOuter
= ra
+ rt
/ 2 - SetGrt
+ rndc() * SetGrtVar
865 for i
, vert
in enumerate(avlist
):
866 v0
= vert
[2] * sin(vert
[0]) + x
868 v2
= vert
[2] * cos(vert
[0]) + z
872 r1
= dims
['t'] * (sin(v2
* PI
/ (dims
['t'] * 2)))
877 avlist
[i
] = [v0
, v1
, v2
]
879 return (avlist
, aflist
)
884 The 'sketch' function creates a list of openings from the general specifications passed to it.
885 It takes curved and domed walls into account, placing the openings at the appropriate angular locations
888 for x
in openingSpecs
:
895 if x
['x'] > (x
['w'] + settings
['wm']):
896 spacing
= x
['x'] / r1
898 spacing
= (x
['w'] + settings
['wm']) / r1
900 minspacing
= (x
['w'] + settings
['wm']) / r1
902 divs
= fill(dims
['s'], dims
['e'], spacing
, minspacing
, center
=1)
904 for posidx
in range(len(divs
) - 2):
905 boundlist
.append(opening(divs
[posidx
+ 1], x
['z'], x
['w'], x
['h'],
906 x
['v'], x
['t'], x
['vl'], x
['tl'], x
['b']))
908 boundlist
.append(opening(x
['x'], x
['z'], x
['w'], x
['h'], x
['v'], x
['t'], x
['vl'], x
['tl'], x
['b']))
909 # check for overlapping edges?
914 def wedgeBlocks(row
, opening
, leftPos
, rightPos
, edgeBinary
, r1
):
916 Makes wedge blocks for the left and right sides, depending
918 wedgeBlocks(row, LeftWedgeEdge, LNerEdge, LEB, r1)
919 wedgeBlocks(row, RNerEdge, RightWedgeEdge, REB, r1)
921 wedgeEdges
= fill(leftPos
, rightPos
, settings
['w'] / r1
, settings
['wm'] / r1
,
924 for i
in range(len(wedgeEdges
) - 1):
925 x
= (wedgeEdges
[i
+ 1] + wedgeEdges
[i
]) / 2
926 grt
= (settings
['g'] + rndd() * settings
['gv']) / r1
927 w
= wedgeEdges
[i
+ 1] - wedgeEdges
[i
] - grt
929 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
931 # edgeV may return "None" - causing TypeError for math op.
932 # use 0 until wedgeBlocks operation worked out
933 edgeVal
= opening
.edgeV(x
- w
/ 2, edgeBinary
)
937 LeftVertOffset
= -(row
.z
- (row
.h
/ 2) * edgeBinary
- edgeVal
)
939 # edgeV may return "None" - causing TypeError for math op.
940 # use 0 until wedgeBlocks operation worked out
941 edgeVal
= opening
.edgeV(x
+ w
/ 2, edgeBinary
)
945 RightVertOffset
= -(row
.z
- (row
.h
/ 2) * edgeBinary
- edgeVal
)
947 # Wedges are on top = off, blank, off, blank
948 # Wedges are on btm = blank, off, blank, off
949 ThisBlockOffsets
= [[0, 0, LeftVertOffset
]] * 2 + [[0] * 3] * 2 + [[0, 0, RightVertOffset
]] * 2
951 # Insert or append "blank" for top or bottom wedges.
953 ThisBlockOffsets
= ThisBlockOffsets
+ [[0] * 3] * 2
955 ThisBlockOffsets
= [[0] * 3] * 2 + ThisBlockOffsets
957 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, ThisBlockOffsets
])
962 def bevelBlockOffsets(offsets
, bevel
, side
):
964 Take the block offsets and modify it for the correct bevel.
966 offsets = the offset list. See MakeABlock
967 bevel = how much to offset the edge
968 side = -1 for left (right side), 1 for right (left side)
971 pointsToAffect
= (0, 2) # right
973 pointsToAffect
= (4, 6) # left
974 for num
in pointsToAffect
:
975 offsets
[num
] = offsets
[num
][:]
976 offsets
[num
][0] += bevel
* side
979 def rowProcessing(row
, Thesketch
, WallBoundaries
):
981 Take row and opening data and process a single row, adding edge and fill blocks to the row data.
984 # check for openings, record top and bottom of row for right and left of each
985 # if both top and bottom intersect create blocks on each edge, appropriate to the size of the overlap
986 # if only one side intersects, run fill to get edge positions, but this should never happen
988 if radialized
: # this checks for radial stonework, and sets the row radius if required
990 r1
= abs(dims
['t'] * sin(row
.z
* PI
/ (dims
['t'] * 2)))
996 # set the edge grout thickness, especially with radial stonework in mind
997 edgrt
= settings
['ge'] * (settings
['g'] / 2 + rndc() * settings
['gv']) / (2 * r1
)
999 # Sets up a list of intersections of top of row with openings,
1000 # from left to right [left edge of opening, right edge of opening, etc...]
1001 # initially just the left and right edge of the wall
1002 edgetop
= [[dims
['s'] + row
.EdgeOffset
/ r1
+ edgrt
, WallBoundaries
],
1003 [dims
['e'] + row
.EdgeOffset
/ r1
- edgrt
, WallBoundaries
]]
1005 # Same as edgetop, but for the bottms of the rows
1006 edgebtm
= [[dims
['s'] + row
.EdgeOffset
/ r1
+ edgrt
, WallBoundaries
],
1007 [dims
['e'] + row
.EdgeOffset
/ r1
- edgrt
, WallBoundaries
]]
1009 # set up some useful values for the top and bottom of the rows.
1010 rowTop
= row
.z
+ row
.h
/ 2
1011 rowBtm
= row
.z
- row
.h
/ 2
1013 for hole
in Thesketch
:
1014 # check the top and bottom of the row, looking at the opening from the right
1015 e
= [hole
.edgeS(rowTop
, -1), hole
.edgeS(rowBtm
, -1)]
1017 # If either one hit the opening, make split points for the left side of the opening.
1019 e
+= [hole
.edgeS(rowTop
, 1), hole
.edgeS(rowBtm
, 1)]
1021 # If one of them missed for some reason, set that value to
1022 # the middle of the opening.
1023 for i
, pos
in enumerate(e
):
1027 # add the intersects to the list of edge points
1028 edgetop
.append([e
[0], hole
])
1029 edgetop
.append([e
[2], hole
])
1030 edgebtm
.append([e
[1], hole
])
1031 edgebtm
.append([e
[3], hole
])
1033 # We want to make the walls in order, so sort the intersects.
1034 # This is where you would want to remove edge points that are out of order
1035 # so that you don't get the "oddity where overlapping openings
1036 # create blocks inversely" problem
1038 # Note: sort ended up comparing function pointers
1039 # if both Openings and Slots were enabled with Repeats in one of them
1041 edgetop
.sort(key
=lambda x
: x
[0])
1042 edgebtm
.sort(key
=lambda x
: x
[0])
1043 except Exception as ex
:
1044 debug_prints(func
="rowProcessing",
1045 text
="Sorting has failed", var
=ex
)
1047 # these two loops trim the edges to the limits of the wall.
1048 # This way openings extending outside the wall don't enlarge the wall.
1051 if ((edgetop
[-1][0] > dims
['e'] + row
.EdgeOffset
/ r1
) or
1052 (edgebtm
[-1][0] > dims
['e'] + row
.EdgeOffset
/ r1
)):
1059 # still trimming the edges...
1062 if ((edgetop
[0][0] < dims
['s'] + row
.EdgeOffset
/ r1
) or
1063 (edgebtm
[0][0] < dims
['s'] + row
.EdgeOffset
/ r1
)):
1071 # make those edge blocks and rows! Wooo!
1072 # This loop goes through each section, (a pair of points in edgetop)
1073 # and places the edge blocks and inbetween normal block zones into the row object
1074 for OpnSplitNo
in range(int(len(edgetop
) / 2)):
1075 # left edge is edge<x>[2*OpnSplitNo], right edge edgex[2*OpnSplitNo+1]
1076 leftEdgeIndex
= 2 * OpnSplitNo
1077 rightEdgeIndex
= 2 * OpnSplitNo
+ 1
1079 # get the openings, to save time and confusion
1080 leftOpening
= edgetop
[leftEdgeIndex
][1]
1081 rightOpening
= edgetop
[rightEdgeIndex
][1]
1083 # find the difference between the edge top and bottom on both sides
1084 LTop
= edgetop
[leftEdgeIndex
][0]
1085 LBtm
= edgebtm
[leftEdgeIndex
][0]
1086 RTop
= edgetop
[rightEdgeIndex
][0]
1087 RBtm
= edgebtm
[rightEdgeIndex
][0]
1091 # which is further out on each side, top or bottom?
1093 LNerEdge
= LBtm
# the nearer edge left
1094 LEB
= 1 # Left Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1100 RNerEdge
= RBtm
# the nearer edge right
1101 REB
= 1 # Right Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1105 REB
= -1 # Right Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1107 # The space between the closest edges of the openings in this section of the row
1108 InnerDiff
= RNerEdge
- LNerEdge
1109 # The mid point between the nearest edges
1110 InnerMid
= (RNerEdge
+ LNerEdge
) / 2
1112 # maximum distance to span with one block
1113 MaxWid
= (settings
['w'] + settings
['wv']) / r1
1114 AveWid
= settings
['w']
1116 # check the left and right sides for wedge blocks
1117 # Check and run the left edge first
1118 # find the edge of the correct side, offset for minimum block height. The LEB decides top or bottom
1119 ZPositionCheck
= row
.z
+ (row
.h
/ 2 - settings
['hm']) * LEB
1121 # edgeS may return "None"
1122 LeftWedgeEdge
= leftOpening
.edgeS(ZPositionCheck
, 1)
1124 if (abs(LDiff
) > AveWid
) or (not LeftWedgeEdge
):
1126 if not LeftWedgeEdge
:
1127 LeftWedgeEdge
= leftOpening
.x
1128 wedgeBlocks(row
, leftOpening
, LeftWedgeEdge
, LNerEdge
, LEB
, r1
)
1129 # set the near and far edge settings to vertical, so the other edge blocks don't interfere
1130 LTop
, LBtm
= LNerEdge
, LNerEdge
1133 # Now do the wedge blocks for the right, same drill... repeated code?
1134 # find the edge of the correct side, offset for minimum block height. The REB decides top or bottom
1135 ZPositionCheck
= row
.z
+ (row
.h
/ 2 - settings
['hm']) * REB
1137 # edgeS may return "None"
1138 RightWedgeEdge
= rightOpening
.edgeS(ZPositionCheck
, -1)
1139 if (abs(RDiff
) > AveWid
) or (not RightWedgeEdge
):
1141 if not RightWedgeEdge
:
1142 RightWedgeEdge
= rightOpening
.x
1143 wedgeBlocks(row
, rightOpening
, RNerEdge
, RightWedgeEdge
, REB
, r1
)
1144 # set the near and far edge settings to vertical, so the other edge blocks don't interfere
1145 RTop
, RBtm
= RNerEdge
, RNerEdge
1148 # Check to see if the edges are close enough toegther to warrant a single block filling it
1149 if (InnerDiff
< MaxWid
):
1150 # if this is true, then this row is just one block!
1151 x
= (LNerEdge
+ RNerEdge
) / 2.
1153 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1154 BtmOff
= LBtm
- LNerEdge
1155 TopOff
= LTop
- LNerEdge
1156 ThisBlockOffsets
= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1157 BtmOff
= RBtm
- RNerEdge
1158 TopOff
= RTop
- RNerEdge
1159 ThisBlockOffsets
+= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1160 bevel
= leftOpening
.edgeBev(rowTop
)
1161 bevelBlockOffsets(ThisBlockOffsets
, bevel
, 1)
1162 bevel
= rightOpening
.edgeBev(rowTop
)
1163 bevelBlockOffsets(ThisBlockOffsets
, bevel
, -1)
1164 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, ThisBlockOffsets
])
1167 # it's not one block, must be two or more
1168 # set up the offsets for the left
1169 BtmOff
= LBtm
- LNerEdge
1170 TopOff
= LTop
- LNerEdge
1171 leftOffsets
= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2 + [[0] * 3] * 4
1172 bevelL
= leftOpening
.edgeBev(rowTop
)
1173 bevelBlockOffsets(leftOffsets
, bevelL
, 1)
1174 # and now for the right
1175 BtmOff
= RBtm
- RNerEdge
1176 TopOff
= RTop
- RNerEdge
1177 rightOffsets
= [[0] * 3] * 4 + [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1178 bevelR
= rightOpening
.edgeBev(rowTop
)
1179 bevelBlockOffsets(rightOffsets
, bevelR
, -1)
1180 # check to see if it is only two blocks
1181 if (InnerDiff
< MaxWid
* 2):
1182 # this row is just two blocks! Left block, then right block
1183 # div is the x position of the dividing point between the two bricks
1184 div
= InnerMid
+ (rndd() * settings
['wv']) / r1
1185 # set the grout distance, since we need grout separation between the blocks
1186 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1187 # set the x position and width for the left block
1188 x
= (div
+ LNerEdge
) / 2 - grt
/ 4
1189 w
= (div
- LNerEdge
) - grt
/ 2
1190 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1191 # For reference: EdgeBlocks = [[x, z, w, h, d, [corner offset matrix]], [etc.]]
1192 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, leftOffsets
])
1193 # Initialize for the block on the right side
1194 x
= (div
+ RNerEdge
) / 2 + grt
/ 4
1195 w
= (RNerEdge
- div
) - grt
/ 2
1196 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1197 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, rightOffsets
])
1200 # program should only get here if there are more than two blocks in the row, and no wedge blocks
1201 # make Left edge block
1203 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1204 # set the x position and width for the left block
1205 widOptions
= [settings
['w'], bevelL
+ settings
['wm'], leftOpening
.ts
]
1206 baseWid
= max(widOptions
)
1207 w
= (rndd() * settings
['wv'] + baseWid
+ row
. EdgeOffset
)
1208 widOptions
[0] = settings
['wm']
1210 w
= max(widOptions
) / r1
- grt
1211 x
= w
/ 2 + LNerEdge
+ grt
/ 2
1212 BlockRowL
= x
+ w
/ 2
1213 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1214 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, leftOffsets
])
1216 # make Right edge block
1218 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1219 # set the x position and width for the left block
1220 widOptions
= [settings
['w'], bevelR
+ settings
['wm'], rightOpening
.ts
]
1221 baseWid
= max(widOptions
)
1222 w
= (rndd() * settings
['wv'] + baseWid
+ row
.EdgeOffset
)
1223 widOptions
[0] = settings
['wm']
1225 w
= max(widOptions
) / r1
- grt
1226 x
= RNerEdge
- w
/ 2 - grt
/ 2
1227 BlockRowR
= x
- w
/ 2
1228 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1229 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, rightOffsets
])
1231 row
.RowSegments
.append([BlockRowL
, BlockRowR
])
1235 def plan(Thesketch
, oldrows
=0):
1237 The 'plan' function takes the data generated by the sketch function and the global settings
1238 and creates a list of blocks.
1239 It passes out a list of row heights, edge positions, edge blocks, and rows of blocks.
1241 # if we were passed a list of rows already, use those; else make a list.
1245 # rows holds the important information common to all rows
1246 # rows = [list of row objects]
1249 # splits are places where we NEED a row division, to accomidate openings
1250 # add a split for the bottom row
1251 splits
= [dims
['b'] + settings
['hb']]
1253 # add a split for each critical point on each opening
1254 for hole
in Thesketch
:
1255 splits
+= hole
.crits()
1257 # and, a split for the top row
1258 splits
.append(dims
['t'] - settings
['ht'])
1261 # divs are the normal old row divisions, add them between the top and bottom split
1262 divs
= fill(splits
[0], splits
[-1], settings
['h'], settings
['hm'] + settings
['g'], settings
['hv'])[1: -1]
1264 # remove the divisions that are too close to the splits, so we don't get tiny thin rows
1265 for i
in range(len(divs
) - 1, -1, -1):
1266 for j
in range(len(splits
)):
1267 diff
= abs(divs
[i
] - splits
[j
])
1268 if diff
< (settings
['h'] - settings
['hv'] + settings
['g']):
1272 # now merge the divs and splits lists
1275 # add bottom and/or top points, if bottom and/or top row heights are more than zero
1276 if settings
['hb'] > 0:
1277 divs
.insert(0, dims
['b'])
1278 if settings
['ht'] > 0:
1279 divs
.append(dims
['t'])
1283 # trim the rows to the bottom and top of the wall
1284 if divs
[0] < dims
['b']:
1286 if divs
[-1] > dims
['t']:
1289 # now, make the data for each row
1290 # rows = [[center height,row height,edge offset],[etc.]]
1292 divCount
= len(divs
) - 1 # number of divs to check
1293 divCheck
= 0 # current div entry
1295 while divCheck
< divCount
:
1296 RowZ
= (divs
[divCheck
] + divs
[divCheck
+ 1]) / 2
1297 RowHeight
= divs
[divCheck
+ 1] - divs
[divCheck
] - settings
['g'] + rndc() * \
1298 settings
['rwhl'] * settings
['gv']
1299 EdgeOffset
= settings
['eoff'] * (fmod(divCheck
, 2) - 0.5) + settings
['eoffv'] * rndd()
1301 # if row height is too shallow: delete next div entry, decrement total, and recheck current entry.
1302 if RowHeight
< settings
['hm']:
1303 del(divs
[divCheck
+ 1])
1304 divCount
-= 1 # Adjust count for removed div entry.
1307 rows
.append(rowOb(RowZ
, RowHeight
, EdgeOffset
))
1309 divCheck
+= 1 # on to next div entry
1311 # set up a special opening object to handle the edges of the wall
1312 x
= (dims
['s'] + dims
['e']) / 2
1313 z
= (dims
['t'] + dims
['b']) / 2
1314 w
= (dims
['e'] - dims
['s'])
1315 h
= (dims
['t'] - dims
['b'])
1316 WallBoundaries
= openingInvert(x
, z
, w
, h
)
1318 # Go over each row in the list, set up edge blocks and block sections
1319 for rownum
in range(len(rows
)):
1320 rowProcessing(rows
[rownum
], Thesketch
, WallBoundaries
)
1322 # now return the things everyone needs
1323 # return [rows,edgeBlocks,blockRows,Asketch]
1324 return [rows
, Thesketch
]
1327 def archGeneration(hole
, vlist
, flist
, sideSign
):
1329 Makes arches for the top and bottom, depending on sideSign
1330 example, Lower arch:
1331 archGeneration(hole, vlist, flist, -1)
1332 example, Upper arch:
1333 archGeneration(hole, vlist, flist, 1)
1334 hole is the opening object that the arch is for
1335 add the vertices to vlist
1336 add the faces to flist
1337 sideSign is + or - 1, for the top or bottom arch. Other values may cause errors.
1340 # working arrays for vectors and faces
1344 # Top (1) or bottom (-1)
1346 r
= hole
.rl
# radius of the arch
1347 rt
= hole
.rtl
# thickness of the arch (stone height)
1348 v
= hole
.vl
# height of the arch
1351 r
= hole
.r
# radius of the arch
1352 rt
= hole
.rt
# thickness of the arch (stone height)
1353 v
= hole
.v
# height of the arch
1356 ra
= r
+ rt
/ 2 # average radius of the arch
1362 sideSignInv
= -sideSign
1364 if v
> w
/ 2: # two arcs, to make a pointed arch
1366 zpos
= z
+ (h
/ 2) * sideSign
1368 # left side top, right side bottom
1369 # angles reference straight up, and are in radians
1371 bevHt
= sqrt(bevRad
** 2 - (bevRad
- (w
/ 2 + bev
)) ** 2)
1372 midHalfAngle
= atan(v
/ (r
- w
/ 2))
1373 midHalfAngleBevel
= atan(bevHt
/ (r
- w
/ 2))
1374 bevelAngle
= midHalfAngle
- midHalfAngleBevel
1375 anglebeg
= (PI
/ 2) * (sideSignInv
)
1376 angleend
= (PI
/ 2) * (sideSignInv
) + midHalfAngle
1378 avlist
, aflist
= arch(ra
, rt
, (xoffset
) * (sideSign
), zpos
, anglebeg
, angleend
, bev
, bevelAngle
, len(vlist
))
1380 for i
, vert
in enumerate(avlist
):
1381 avlist
[i
] = [vert
[0] + hole
.x
, vert
[1], vert
[2]]
1385 # right side top, left side bottom
1387 # angles reference straight up, and are in radians
1388 anglebeg
= (PI
/ 2) * (sideSign
) - midHalfAngle
1389 angleend
= (PI
/ 2) * (sideSign
)
1391 avlist
, aflist
= arch(ra
, rt
, (xoffset
) * (sideSignInv
), zpos
, anglebeg
, angleend
, bev
, bevelAngle
, len(vlist
))
1393 for i
, vert
in enumerate(avlist
):
1394 avlist
[i
] = [vert
[0] + hole
.x
, vert
[1], vert
[2]]
1400 Dpth
= settings
['d'] + rndc() * settings
['dv']
1401 Grout
= settings
['g'] + rndc() * settings
['gv']
1402 angleBevel
= (PI
/ 2) * (sideSign
) - midHalfAngle
1403 Wdth
= (rt
- Grout
- bev
) * 2 * sin(angleBevel
) * sideSign
# note, sin may be negative
1404 MidZ
= ((sideSign
) * (bevHt
+ h
/ 2.0) + z
) + (rt
- Grout
- bev
) \
1405 * cos(angleBevel
) # note, cos may come out negative
1406 nearCorner
= sideSign
* (MidZ
- z
) - v
- h
/ 2
1409 TopHt
= hole
.top() - MidZ
- Grout
1412 BtmHt
= - (hole
.btm() - MidZ
) - Grout
1415 # set the amount to bevel the keystone
1416 keystoneBevel
= (bevHt
- v
) * sideSign
1417 if Wdth
>= settings
['hm']:
1418 avlist
, aflist
= MakeAKeystone(x
, Wdth
, MidZ
, TopHt
, BtmHt
, Dpth
, keystoneBevel
, len(vlist
))
1421 for i
, vert
in enumerate(avlist
):
1423 r1
= dims
['t'] * sin(vert
[2] * PI
/ (dims
['t'] * 2))
1426 avlist
[i
] = [((vert
[0] - hole
.x
) / r1
) + hole
.x
, vert
[1], vert
[2]]
1430 # remove "debug note" once bevel is finalized.
1432 debug_prints(func
="archGeneration",
1433 text
="Keystone was too narrow - " + str(Wdth
))
1435 else: # only one arc - curve not peak.
1436 # bottom (sideSign -1) arch has poorly sized blocks...
1438 zpos
= z
+ (sideSign
* (h
/ 2 + v
- r
)) # single arc positioning
1440 # angles reference straight up, and are in radians
1447 halfangle
= atan(w
/ (2 * (r
- v
)))
1449 anglebeg
= angleOffset
- halfangle
1450 angleend
= angleOffset
+ halfangle
1452 anglebeg
= angleOffset
- PI
/ 2
1453 angleend
= angleOffset
+ PI
/ 2
1455 avlist
, aflist
= arch(ra
, rt
, 0, zpos
, anglebeg
, angleend
, bev
, 0.0, len(vlist
))
1457 for i
, vert
in enumerate(avlist
):
1458 avlist
[i
] = [vert
[0] + x
, vert
[1], vert
[2]]
1463 # Make the Side Stones
1464 grt
= (settings
['g'] + rndc() * settings
['gv'])
1465 width
= sqrt(rt
** 2 - c
** 2) - grt
1467 if c
> settings
['hm'] + grt
and c
< width
+ grt
:
1469 subdivision
= settings
['sdv'] * (zpos
+ (h
/ 2) * sideSign
)
1471 subdivision
= settings
['sdv']
1473 # set the height of the block, it should be as high as the max corner position, minus grout
1474 height
= c
- grt
* (0.5 + c
/ (width
+ grt
))
1476 # the vertical offset for the short side of the block
1477 voff
= sideSign
* (settings
['hm'] - height
)
1479 zstart
= z
+ sideSign
* (h
/ 2 + grt
/ 2)
1480 woffset
= width
* (settings
['hm'] + grt
/ 2) / (c
- grt
/ 2)
1481 depth
= rndd() * settings
['dv'] + settings
['d']
1484 offsets
= [[0] * 3] * 6 + [[0] * 2 + [voff
]] * 2
1485 topSide
= zstart
+ height
1488 offsets
= [[0] * 3] * 4 + [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 2
1490 btmSide
= zstart
- height
1491 # Do some stuff to incorporate bev here
1492 bevelBlockOffsets(offsets
, bev
, -1)
1494 avlist
, aflist
= MakeABlock(
1495 [x
- xstart
- width
, x
- xstart
- woffset
, btmSide
, topSide
,
1496 -depth
/ 2, depth
/ 2], subdivision
, len(vlist
),
1497 Offsets
=offsets
, xBevScl
=1
1500 # top didn't use radialized in prev version;
1501 # just noting for clarity - may need to revise for "sideSign == 1"
1503 for i
, vert
in enumerate(avlist
):
1504 avlist
[i
] = [((vert
[0] - x
) / vert
[2]) + x
, vert
[1], vert
[2]]
1509 # keep sizing same - neat arches = master masons :)
1510 # grt = (settings['g'] + rndc()*settings['gv'])
1511 # height = c - grt*(0.5 + c/(width + grt))
1512 # if grout varies may as well change width too... width = sqrt(rt**2 - c**2) - grt
1513 # voff = sideSign * (settings['hm'] - height)
1514 # woffset = width*(settings['hm'] + grt/2)/(c - grt/2)
1517 offsets
= [[0] * 3] * 2 + [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 4
1518 topSide
= zstart
+ height
1521 offsets
= [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 6
1523 btmSide
= zstart
- height
1524 # Do some stuff to incorporate bev here
1525 bevelBlockOffsets(offsets
, bev
, 1)
1527 avlist
, aflist
= MakeABlock(
1528 [x
+ xstart
+ woffset
, x
+ xstart
+ width
, btmSide
, topSide
,
1529 -depth
/ 2, depth
/ 2], subdivision
, len(vlist
),
1530 Offsets
=offsets
, xBevScl
=1
1533 # top didn't use radialized in prev version;
1534 # just noting for clarity - may need to revise for "sideSign == 1"
1536 for i
, vert
in enumerate(avlist
):
1537 avlist
[i
] = [((vert
[0] - x
) / vert
[2]) + x
, vert
[1], vert
[2]]
1546 Build creates the geometry for the wall, based on the
1547 "Aplan" object from the "plan" function. If physics is
1548 enabled, then it make a number of individual blocks with
1549 physics interaction enabled. Otherwise it creates
1550 geometry for the blocks, arches, etc. of the wall.
1556 # all the edge blocks, redacted
1557 # AllBlocks = [[x, z, w, h, d, [corner offset matrix]], [etc.]]
1559 # loop through each row, adding the normal old blocks
1560 for rowidx
in range(len(rows
)):
1561 rows
[rowidx
].FillBlocks()
1565 # If the wall is set to merge blocks, check all the blocks to see if you can merge any
1566 # seems to only merge vertical, should do horizontal too
1568 for rowidx
in range(len(rows
) - 1):
1571 r1
= dims
['t'] * sin(abs(rows
[rowidx
].z
) * PI
/ (dims
['t'] * 2))
1573 r1
= abs(rows
[rowidx
].z
)
1577 Tolerance
= settings
['g'] / r1
1578 idxThis
= len(rows
[rowidx
].BlocksNorm
[:]) - 1
1579 idxThat
= len(rows
[rowidx
+ 1].BlocksNorm
[:]) - 1
1582 # end loop when either array idx wraps
1583 if idxThis
< 0 or idxThat
< 0:
1586 blockThis
= rows
[rowidx
].BlocksNorm
[idxThis
]
1587 blockThat
= rows
[rowidx
+ 1].BlocksNorm
[idxThat
]
1589 # seems to only merge vertical, should do horizontal too...
1590 cx
, cz
, cw
, ch
, cd
= blockThis
[:5]
1591 ox
, oz
, ow
, oh
, od
= blockThat
[:5]
1593 if (abs(cw
- ow
) < Tolerance
) and (abs(cx
- ox
) < Tolerance
):
1599 AllBlocks
.append([(cx
+ ox
) / 2, (cz
+ oz
+ (oh
- ch
) / 2) / 2,
1600 BlockW
, abs(cz
- oz
) + (ch
+ oh
) / 2, (cd
+ od
) / 2, None])
1602 rows
[rowidx
].BlocksNorm
.pop(idxThis
)
1603 rows
[rowidx
+ 1].BlocksNorm
.pop(idxThat
)
1612 # Add blocks to create a "shelf/platform".
1613 # Does not account for openings (crosses gaps - which is a good thing)
1615 SetGrtOff
= settings
['g'] / 2 # half grout for block size modifier
1617 # Use wall block settings for shelf
1618 SetBW
= settings
['w']
1619 SetBWVar
= settings
['wv']
1620 SetBWMin
= settings
['wm']
1621 SetBH
= settings
['h']
1623 # Shelf area settings
1624 ShelfLft
= shelfSpecs
['x']
1625 ShelfBtm
= shelfSpecs
['z']
1626 ShelfEnd
= ShelfLft
+ shelfSpecs
['w']
1627 ShelfTop
= ShelfBtm
+ shelfSpecs
['h']
1628 ShelfThk
= shelfSpecs
['d'] * 2 # use double-depth due to offsets to position at cursor.
1630 # Use "corners" to adjust position so not centered on depth.
1631 # Facing shelf, at cursor (middle of wall blocks)
1632 # - this way no gaps between platform and wall face due to wall block depth.
1633 wallDepth
= settings
['d'] / 2 # offset by wall depth so step depth matches UI setting :)
1634 if shelfBack
: # place blocks on backside of wall
1636 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1637 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1638 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1639 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0]
1643 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1644 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1645 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1646 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0]
1649 # Add blocks for each "shelf row" in area
1650 while ShelfBtm
< ShelfTop
:
1652 # Make blocks for each row - based on rowOb::fillblocks
1653 # Does not vary grout.
1654 divs
= fill(ShelfLft
, ShelfEnd
, SetBW
, SetBWMin
, SetBWVar
)
1656 # loop through the row divisions, adding blocks for each one
1657 for i
in range(len(divs
) - 1):
1658 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
1659 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - SetGrtOff
1661 AllBlocks
.append([ThisBlockx
, ShelfBtm
, ThisBlockw
, SetBH
, ShelfThk
, ShelfOffsets
])
1663 ShelfBtm
+= SetBH
+ SetGrtOff
# moving up to next row...
1665 # Add blocks to create "steps".
1666 # Does not account for openings (crosses gaps - which is a good thing)
1668 SetGrtOff
= settings
['g'] / 2 # half grout for block size modifier
1670 # Vary block width by wall block variations.
1671 SetWidVar
= settings
['wv']
1672 SetWidMin
= settings
['wm']
1674 StepXMod
= stepSpecs
['t'] # width of step/tread, also sets basic block size.
1675 StepZMod
= stepSpecs
['v']
1677 StepLft
= stepSpecs
['x']
1678 StepRt
= stepSpecs
['x'] + stepSpecs
['w']
1679 StepBtm
= stepSpecs
['z'] + StepZMod
/ 2 # Start offset for centered blocks
1680 StepWide
= stepSpecs
['w']
1681 StepTop
= StepBtm
+ stepSpecs
['h']
1682 StepThk
= stepSpecs
['d'] * 2 # use double-depth due to offsets to position at cursor.
1684 # Use "corners" to adjust steps so not centered on depth.
1685 # Facing steps, at cursor (middle of wall blocks)
1686 # - this way no gaps between steps and wall face due to wall block depth.
1687 # Also, will work fine as stand-alone if not used with wall (try block depth 0 and see what happens).
1688 wallDepth
= settings
['d'] / 2
1689 if stepBack
: # place blocks on backside of wall
1691 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1692 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1693 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1694 [0, StepThk
/ 2, 0], [0, wallDepth
, 0]
1698 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1699 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1700 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1701 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0]
1704 # Add steps for each "step row" in area (neg width is interesting but prevented)
1705 while StepBtm
< StepTop
and StepWide
> 0:
1707 # Make blocks for each step row - based on rowOb::fillblocks
1708 # Does not vary grout.
1710 if stepOnly
: # "cantilevered steps"
1712 stepStart
= StepRt
- StepXMod
1716 AllBlocks
.append([stepStart
, StepBtm
, StepXMod
, StepZMod
, StepThk
, StepOffsets
])
1718 divs
= fill(StepLft
, StepRt
, StepXMod
, SetWidMin
, SetWidVar
)
1720 # loop through the row divisions, adding blocks for each one
1721 for i
in range(len(divs
) - 1):
1722 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
1723 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - SetGrtOff
1725 AllBlocks
.append([ThisBlockx
, StepBtm
, ThisBlockw
, StepZMod
, StepThk
, StepOffsets
])
1727 StepBtm
+= StepZMod
+ SetGrtOff
# moving up to next row...
1728 StepWide
-= StepXMod
# reduce step width
1730 # adjust side limit depending on direction of steps
1732 StepRt
-= StepXMod
# move in from right
1734 StepLft
+= StepXMod
# move in from left
1736 # Copy all the blocks out of the rows
1738 AllBlocks
+= row
.BlocksEdge
1739 AllBlocks
+= row
.BlocksNorm
1741 # This loop makes individual blocks for each block specified in the plan
1742 for block
in AllBlocks
:
1743 x
, z
, w
, h
, d
, corners
= block
1746 r1
= dims
['t'] * sin(z
* PI
/ (dims
['t'] * 2))
1752 geom
= MakeABlock([x
- w
/ 2, x
+ w
/ 2, z
- h
/ 2, z
+ h
/ 2, -d
/ 2, d
/ 2],
1753 settings
['sdv'], len(vlist
),
1754 corners
, None, settings
['b'] + rndd() * settings
['bv'], r1
)
1758 # This loop makes Arches for every opening specified in the plan.
1759 for hole
in Aplan
[1]:
1761 if hole
.vl
> 0 and hole
.rtl
> (settings
['g'] + settings
['hm']): # make lower arch blocks
1762 archGeneration(hole
, vlist
, flist
, -1)
1765 if hole
.v
> 0 and hole
.rt
> (settings
['g'] + settings
['hm']): # make upper arch blocks
1766 archGeneration(hole
, vlist
, flist
, 1)
1768 # Warp all the points for domed stonework
1770 for i
, vert
in enumerate(vlist
):
1771 vlist
[i
] = [vert
[0], (dims
['t'] + vert
[1]) * cos(vert
[2] * PI
/ (2 * dims
['t'])),
1772 (dims
['t'] + vert
[1]) * sin(vert
[2] * PI
/ (2 * dims
['t']))]
1774 # Warp all the points for radial stonework
1776 for i
, vert
in enumerate(vlist
):
1777 vlist
[i
] = [vert
[2] * cos(vert
[0]), vert
[2] * sin(vert
[0]), vert
[1]]
1783 def createWall(radial
, curve
, openings
, mergeBlox
, shelf
, shelfSide
,
1784 steps
, stepDir
, stepBare
, stepSide
):
1786 Call all the functions you need to make a wall, return the verts and faces.
1799 # set all the working variables from passed parameters
1803 openingSpecs
= openings
1804 bigBlock
= mergeBlox
1808 shelfBack
= shelfSide
1813 aplan
= plan(asketch
, 0)