1 # SPDX-FileCopyrightText: 2016-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Authors: dudecon, jambay
9 # Grout needs to be implemented.
10 # consider removing wedge crit for small "c" and "cl" values
11 # wrap around for openings on radial stonework?
12 # auto-clip wall edge to SMALL for radial and domes.
13 # unregister doesn't release all references.
14 # repeat for opening doesn't distribute evenly when radialized - see wrap around
16 # if opening width == indent*2 the edge blocks fail (row of blocks cross opening).
17 # if openings overlap fills inverse with blocks - see h/v slots.
18 # Negative grout width creates a pair of phantom blocks, separated by grout
19 # width, inside the edges.
20 # if block width variance is 0, and edging is on, right edge blocks create a "vertical seam"
24 from random
import random
31 # Set to True to enable debug_prints
35 SMALL
= 0.000000000001
36 # for values that must be != 0; see UI options/variables - sort of a bug to be fixed
41 # General masonry Settings
42 # ------------------------
44 'w': 1.2, 'wv': 0.3, 'h': .6, 'hv': 0.3, 'd': 0.3, 'dv': 0.1,
45 'g': 0.1, 'gv': 0.07, 'gd': 0.01, 'gdv': 0.0, 'b': 0, 'bv': 0,
46 'f': 0.0, 'fv': 0.0, 't': 0.0, 'sdv': 0.1, 'hwt': 0.5, 'aln': 0,
47 'wm': 0.8, 'hm': 0.3, 'dm': 0.1,
48 'woff': 0.0, 'woffv': 0.0, 'eoff': 0.3, 'eoffv': 0.0, 'rwhl': 1,
49 'hb': 0, 'ht': 0, 'ge': 0, 'physics': 0
52 settings DOCUMENTATION:
53 'w':width 'wv':widthVariation
54 'h':height 'hv':heightVariation
55 'd':depth 'dv':depthVariation
56 'g':grout 'gv':groutVariation 'gd':groutDepth 'gdv':groutDepthVariation
57 'b':bevel 'bv':bevelVariation
58 'f':flawSize 'fv':flawSizeVariation 'ff':flawFraction
60 'sdv':subdivision(distance or angle)
61 'hwt':row height effect on block widths in the row (0=no effect,
62 1=1:1 relationship, negative values allowed, 0.5 works well)
63 'aln':alignment(0=none, 1=rows w/features, 2=features w/rows)
65 'wm':width minimum 'hm':height minimum 'dm':depth minimum
66 'woff':row start offset(fraction of width)
67 'woffv':width offset variation(fraction of width)
68 'eoff':edge offset 'eoffv':edge offset variation
69 'rwhl':row height lock(1 is all blocks in row have same height)
70 'hb':bottom row height 'ht': top row height 'ge': grout the edges
71 'physics': set up for physics
74 # dims = area of wall (face)
75 # ------------------------
77 's': 0, 'e': PI
* 3 / 2, 'b': 0.1, 't': 12.3
81 's':start x or theta 'e':end x or theta 'b':bottom z or r 't':top z or r
82 'w' = e-s and h = t-b; calculated to optimize for various operations/usages
83 dims = {'s':-12, 'e':15, 'w':27, 'b':-15., 't':15., 'h':30}
84 dims = {'s':-bayDim/2, 'e':bayDim/2, 'b':-5., 't':10.} # bay settings?
87 # ------------------------
88 radialized
= 0 # Radiating from one point - round/disc; instead of square
89 slope
= 0 # Warp/slope; curved over like a vaulted tunnel
91 # 'bigblock': merge adjacent blocks into single large blocks
92 bigBlock
= 0 # Merge blocks
95 # Gaps in blocks for various apertures
96 # ------------------------
99 {'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 2.7, 'rp': 1, 'b': 0.0,
100 'v': 0, 'vl': 0, 't': 0, 'tl': 0}
103 openingSpecs DOCUMENTATION:
104 'w': opening width, 'h': opening height,
105 'x': horizontal position, 'z': vertical position,
106 'rp': make multiple openings, with a spacing of x,
107 'b': bevel the opening, inside only, like an arrow slit.
108 'v': height of the top arch, 'vl':height of the bottom arch,
109 't': thickness of the top arch, 'tl': thickness of the bottom arch
112 # Add blocks to make platforms
113 # ------------------------
117 'w': 0.5, 'h': 0.5, 'd': 0.3, 'x': 0.8, 'z': 2.7
120 shelfSpecs DOCUMENTATION:
121 'w': block width, 'h': block height, 'd': block depth (shelf size; offset from wall)
122 'x': horizontal start position, 'z': vertical start position
125 # Add blocks to make steps
126 # ------------------------
130 'x': 0.0, 'z': -10, 'w': 10.0, 'h': 10.0,
131 'v': 0.7, 't': 1.0, 'd': 1.0
134 stepSpecs DOCUMENTATION:
135 'x': horizontal start position, 'z': vertical start position,
136 'w': step area width, 'h': step area height,
137 'v': riser height, 't': tread width, 'd': block depth (step size; offset from wall)
146 def debug_prints(func
="", text
="Message", var
=None):
149 print("\n[{}]\nmessage: {}".format(func
, text
))
151 print("Error: ", var
)
154 # pass variables just like for the regular prints
155 def debug_print_vars(*args
, **kwargs
):
158 print(*args
, **kwargs
)
161 # easier way to get to the random function
166 # random number from -0.5 to 0.5
168 return (random() - 0.5)
171 # random number from -1.0 to 1.0
173 return (random() - 0.5) * 2.0
177 # opening test function
180 dims
= {'s': -29., 'e': 29., 'b': -6., 't': TestN
* 7.5}
182 for i
in range(TestN
):
183 x
= (random() - 0.5) * 6
185 v
= .2 + i
* (3. / TestN
)
186 vl
= 3.2 - i
* (3. / TestN
)
190 openingSpecs
+= [{'w': 3.1 + rn
, 'h': 0.3 + rn
, 'x': float(x
),
191 'z': float(z
), 'rp': 0, 'b': 0.,
192 'v': float(v
), 'vl': float(vl
),
193 't': float(t
), 'tl': float(tl
)}]
194 return dims
, openingSpecs
197 # dims, openingSpecs = test(15)
200 # For filling a linear space with divisions
201 def fill(left
, right
, avedst
, mindst
=0.0, dev
=0.0, pad
=(0.0, 0.0), num
=0,
204 Fills a linear range with points and returns an ordered list of those points
205 including the end points.
207 left: the lower boundary
208 right: the upper boundary
209 avedst: the average distance between points
210 mindst: the minimum distance between points
211 dev: the maximum random deviation from avedst
212 pad: tends to move the points near the bounds right (positive) or
214 element 0 pads the lower bounds, element 1 pads the upper bounds
215 num: substitutes a numerical limit for the right limit. fill will then make
217 center: flag to center the elements in the range, 0 == disabled
221 curpos
= left
+ pad
[0]
223 # Set offset by average spacing, then add blocks (fall through);
224 # if not at right edge.
226 curpos
+= ((right
- left
- mindst
* 2) % avedst
) / 2 + mindst
227 if curpos
- poslist
[-1] < mindst
:
228 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
230 # clip to right edge.
231 if (right
- curpos
< mindst
) or (right
- curpos
< mindst
- pad
[1]):
232 poslist
.append(right
)
236 poslist
.append(curpos
)
243 curpos
+= avedst
+ rndd() * dev
244 if curpos
- poslist
[-1] < mindst
:
245 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
246 poslist
.append(curpos
)
253 while True: # loop for blocks
254 curpos
+= avedst
+ rndd() * dev
255 if curpos
- poslist
[-1] < mindst
:
256 curpos
= poslist
[-1] + mindst
+ rnd() * dev
/ 2
257 # close off edges at limit
258 if (right
- curpos
< mindst
) or (right
- curpos
< mindst
- pad
[1]):
259 poslist
.append(right
)
262 poslist
.append(curpos
)
265 # For generating block geometry
266 def MakeABlock(bounds
, segsize
, vll
=0, Offsets
=None, FaceExclude
=[],
269 MakeABlock returns lists of points and faces to be made into a square
270 cornered block, subdivided along the length, with optional bevels.
271 bounds: a list of boundary positions:
272 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
273 segsize: the maximum size before lengthwise subdivision occurs
274 vll: the number of vertices already in the mesh. len(mesh.verts) should
276 Offsets: list of coordinate delta values.
277 Offsets are lists, [x,y,z] in
284 5:right_bottom_front,
288 FaceExclude: list of faces to exclude from the faces list. see bounds above for indices
289 xBevScl: how much to divide the end (+- x axis) bevel dimensions. Set to current average
290 radius to compensate for angular distortion on curved blocks
293 slices
= fill(bounds
[0], bounds
[1], segsize
, segsize
, center
=1)
298 points
.append([slices
[0], bounds
[4], bounds
[2]])
299 points
.append([slices
[0], bounds
[5], bounds
[2]])
300 points
.append([slices
[0], bounds
[5], bounds
[3]])
301 points
.append([slices
[0], bounds
[4], bounds
[3]])
303 for x
in slices
[1:-1]:
304 points
.append([x
, bounds
[4], bounds
[2]])
305 points
.append([x
, bounds
[5], bounds
[2]])
306 points
.append([x
, bounds
[5], bounds
[3]])
307 points
.append([x
, bounds
[4], bounds
[3]])
309 points
.append([slices
[-1], bounds
[4], bounds
[2]])
310 points
.append([slices
[-1], bounds
[5], bounds
[2]])
311 points
.append([slices
[-1], bounds
[5], bounds
[3]])
312 points
.append([slices
[-1], bounds
[4], bounds
[3]])
315 points
.append([slices
[0] + Offsets
[0][0], bounds
[4] + Offsets
[0][1], bounds
[2] + Offsets
[0][2]])
316 points
.append([slices
[0] + Offsets
[1][0], bounds
[5] + Offsets
[1][1], bounds
[2] + Offsets
[1][2]])
317 points
.append([slices
[0] + Offsets
[3][0], bounds
[5] + Offsets
[3][1], bounds
[3] + Offsets
[3][2]])
318 points
.append([slices
[0] + Offsets
[2][0], bounds
[4] + Offsets
[2][1], bounds
[3] + Offsets
[2][2]])
320 for x
in slices
[1: -1]:
321 xwt
= (x
- bounds
[0]) / (bounds
[1] - bounds
[0])
322 points
.append([x
+ Offsets
[0][0] * (1 - xwt
) + Offsets
[4][0] * xwt
,
323 bounds
[4] + Offsets
[0][1] * (1 - xwt
) + Offsets
[4][1] * xwt
,
324 bounds
[2] + Offsets
[0][2] * (1 - xwt
) + Offsets
[4][2] * xwt
])
325 points
.append([x
+ Offsets
[1][0] * (1 - xwt
) + Offsets
[5][0] * xwt
,
326 bounds
[5] + Offsets
[1][1] * (1 - xwt
) + Offsets
[5][1] * xwt
,
327 bounds
[2] + Offsets
[1][2] * (1 - xwt
) + Offsets
[5][2] * xwt
])
328 points
.append([x
+ Offsets
[3][0] * (1 - xwt
) + Offsets
[7][0] * xwt
,
329 bounds
[5] + Offsets
[3][1] * (1 - xwt
) + Offsets
[7][1] * xwt
,
330 bounds
[3] + Offsets
[3][2] * (1 - xwt
) + Offsets
[7][2] * xwt
])
331 points
.append([x
+ Offsets
[2][0] * (1 - xwt
) + Offsets
[6][0] * xwt
,
332 bounds
[4] + Offsets
[2][1] * (1 - xwt
) + Offsets
[6][1] * xwt
,
333 bounds
[3] + Offsets
[2][2] * (1 - xwt
) + Offsets
[6][2] * xwt
])
335 points
.append([slices
[-1] + Offsets
[4][0], bounds
[4] + Offsets
[4][1], bounds
[2] + Offsets
[4][2]])
336 points
.append([slices
[-1] + Offsets
[5][0], bounds
[5] + Offsets
[5][1], bounds
[2] + Offsets
[5][2]])
337 points
.append([slices
[-1] + Offsets
[7][0], bounds
[5] + Offsets
[7][1], bounds
[3] + Offsets
[7][2]])
338 points
.append([slices
[-1] + Offsets
[6][0], bounds
[4] + Offsets
[6][1], bounds
[3] + Offsets
[6][2]])
340 faces
.append([vll
, vll
+ 3, vll
+ 2, vll
+ 1])
342 for x
in range(len(slices
) - 1):
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
+ 1, vll
+ 5, vll
+ 4])
349 faces
.append([vll
, vll
- 3, vll
+ 1, vll
+ 4])
352 faces
.append([vll
, vll
+ 1, vll
+ 2, vll
+ 3])
357 # For generating Keystone Geometry
359 def MakeAKeystone(xpos
, width
, zpos
, ztop
, zbtm
, thick
, bevel
, vll
=0, FaceExclude
=[], xBevScl
=1):
361 MakeAKeystone returns lists of points and faces to be made into a
362 square cornered keystone, with optional bevels.
363 xpos: x position of the centerline
364 width: x width of the keystone at the widest point (discounting bevels)
365 zpos: z position of the widest point
366 ztop: distance from zpos to the top
367 zbtm: distance from zpos to the bottom
369 bevel: the amount to raise the back vertex to account for arch beveling
370 vll: the number of vertices already in the mesh. len(mesh.verts) should give this number
371 faceExclude: list of faces to exclude from the faces list.
372 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
373 xBevScl: how much to divide the end (+- x axis) bevel dimensions.
374 Set to current average radius to compensate for angular distortion on curved blocks
379 faceinclude
= [1 for x
in range(6)]
380 for x
in FaceExclude
:
387 # The front top point
388 points
.append([xpos
, Thk
, Top
])
389 # The front left point
390 points
.append([xpos
- Wid
, Thk
, zpos
])
391 # The front bottom point
392 points
.append([xpos
, Thk
, Btm
])
393 # The front right point
394 points
.append([xpos
+ Wid
, Thk
, zpos
])
398 MirrorPoints
.append([i
[0], -i
[1], i
[2]])
399 points
+= MirrorPoints
400 points
[6][2] += bevel
402 faces
.append([3, 2, 1, 0])
403 faces
.append([4, 5, 6, 7])
404 faces
.append([4, 7, 3, 0])
405 faces
.append([5, 4, 0, 1])
406 faces
.append([6, 5, 1, 2])
407 faces
.append([7, 6, 2, 3])
408 # Offset the vertex numbers by the number of vertices already in the list
409 for i
in range(len(faces
)):
410 for j
in range(len(faces
[i
])):
416 # for finding line/circle intercepts
418 def circ(offs
=0., r
=1.):
420 offs is the distance perpendicular to the line to the center of the circle
421 r is the radius of the circle
422 circ returns the distance parallel to the line to the center of the circle at the intercept.
430 return sqrt(r
** 2 - offs
** 2)
433 # class openings in the wall
437 This is the class for holding the data for the openings in the wall.
438 It has methods for returning the edges of the opening for any given position value,
439 as well as bevel settings and top and bottom positions.
440 It stores the 'style' of the opening, and all other pertinent information.
442 # x = 0. # x position of the opening
443 # z = 0. # x position of the opening
444 # w = 0. # width of the opening
445 # h = 0. # height of the opening
446 r
= 0 # top radius of the arch (derived from 'v')
447 rl
= 0 # lower radius of the arch (derived from 'vl')
448 rt
= 0 # top arch thickness
449 rtl
= 0 # lower arch thickness
450 ts
= 0 # Opening side thickness, if greater than average width, replaces it.
451 c
= 0 # top arch corner position (for low arches), distance from the top of the straight sides
452 cl
= 0 # lower arch corner position (for low arches), distance from the top of the straight sides
453 # form = 0 # arch type (unused for now)
454 # b = 0. # back face bevel distance, like an arrow slit
455 v
= 0. # top arch height
456 vl
= 0. # lower arch height
457 # variable "s" is used for "side" in the "edge" function.
458 # it is a signed int, multiplied by the width to get + or - of the center
461 if self
.vl
<= self
.w
/ 2:
462 return self
.z
- self
.h
/ 2 - self
.vl
- self
.rtl
464 return self
.z
- sqrt((self
.rl
+ self
.rtl
) ** 2 - (self
.rl
- self
.w
/ 2) ** 2) - self
.h
/ 2
467 if self
.v
<= self
.w
/ 2:
468 return self
.z
+ self
.h
/ 2 + self
.v
+ self
.rt
470 return sqrt((self
.r
+ self
.rt
) ** 2 - (self
.r
- self
.w
/ 2) ** 2) + self
.z
+ self
.h
/ 2
472 # crits returns the critical split points, or discontinuities, used for making rows
475 if self
.vl
> 0: # for lower arch
476 # add the top point if it is pointed
477 # if self.vl >= self.w/2.: critlist.append(self.btm())
478 if self
.vl
< self
.w
/ 2.: # else: for low arches, with wedge blocks under them
479 # critlist.append(self.btm())
480 critlist
.append(self
.z
- self
.h
/ 2 - self
.cl
)
482 if self
.h
> 0: # if it has a height, append points at the top and bottom of the main square section
483 critlist
+= [self
.z
- self
.h
/ 2, self
.z
+ self
.h
/ 2]
484 else: # otherwise, append just one in the center
485 critlist
.append(self
.z
)
487 if self
.v
> 0: # for the upper arch
488 if self
.v
< self
.w
/ 2: # add the splits for the upper wedge blocks, if needed
489 critlist
.append(self
.z
+ self
.h
/ 2 + self
.c
)
490 # critlist.append(self.top())
491 # otherwise just add the top point, if it is pointed
492 # else: critlist.append(self.top())
496 # get the side position of the opening.
497 # ht is the z position; s is the side: 1 for right, -1 for left
498 # if the height passed is above or below the opening, return None
499 def edgeS(self
, ht
, s
):
501 # set the row radius: 1 for standard wall (flat)
504 r1
= abs(dims
['t'] * sin(ht
* PI
/ (dims
['t'] * 2)))
510 # Go through all the options, and return the correct value
511 if ht
< self
.btm(): # too low
513 elif ht
> self
.top(): # too high
516 # Check for circ returning None - prevent TypeError (script failure) with float.
517 # in this range, pass the lower arch info
518 elif ht
<= self
.z
- self
.h
/ 2 - self
.cl
:
519 if self
.vl
> self
.w
/ 2:
520 circVal
= circ(ht
- self
.z
+ self
.h
/ 2, self
.rl
+ self
.rtl
)
524 return self
.x
+ s
* (self
.w
/ 2. - self
.rl
+ circVal
) / r1
526 circVal
= circ(ht
- self
.z
+ self
.h
/ 2 + self
.vl
- self
.rl
, self
.rl
+ self
.rtl
)
530 return self
.x
+ s
* circVal
/ r1
532 # in this range, pass the top arch info
533 elif ht
>= self
.z
+ self
.h
/ 2 + self
.c
:
534 if self
.v
> self
.w
/ 2:
535 circVal
= circ(ht
- self
.z
- self
.h
/ 2, self
.r
+ self
.rt
)
539 return self
.x
+ s
* (self
.w
/ 2. - self
.r
+ circVal
) / r1
541 circVal
= circ(ht
- (self
.z
+ self
.h
/ 2 + self
.v
- self
.r
), self
.r
+ self
.rt
)
545 return self
.x
+ s
* circVal
/ r1
547 # in this range pass the lower corner edge info
548 elif ht
<= self
.z
- self
.h
/ 2:
549 d
= sqrt(self
.rtl
** 2 - self
.cl
** 2)
550 if self
.cl
> self
.rtl
/ sqrt(2.):
551 return self
.x
+ s
* (self
.w
/ 2 + (self
.z
- self
.h
/ 2 - ht
) * d
/ self
.cl
) / r1
553 return self
.x
+ s
* (self
.w
/ 2 + d
) / r1
555 # in this range pass the upper corner edge info
556 elif ht
>= self
.z
+ self
.h
/ 2:
557 d
= sqrt(self
.rt
** 2 - self
.c
** 2)
558 if self
.c
> self
.rt
/ sqrt(2.):
559 return self
.x
+ s
* (self
.w
/ 2 + (ht
- self
.z
- self
.h
/ 2) * d
/ self
.c
) / r1
561 return self
.x
+ s
* (self
.w
/ 2 + d
) / r1
563 # in this range, pass the middle info (straight sides)
565 return self
.x
+ s
* self
.w
/ 2 / r1
567 # get the top or bottom of the opening
568 # ht is the x position; s is the side: 1 for top, -1 for bottom
569 def edgeV(self
, ht
, s
):
571 dist
= abs(self
.x
- ht
)
573 def radialAdjust(dist
, sideVal
):
574 # take the distance and adjust for radial geometry, return dist
577 dist
= dist
* abs(dims
['t'] * sin(sideVal
* PI
/ (dims
['t'] * 2)))
579 dist
= dist
* sideVal
582 if s
> 0: # and (dist <= self.edgeS(self.z + self.h / 2 + self.c, 1) - self.x): # check top down
583 # hack for radialized masonry, import approx Z instead of self.top()
584 dist
= radialAdjust(dist
, self
.top())
586 # no arch on top, flat
588 return self
.z
+ self
.h
/ 2
590 # pointed arch on top
591 elif self
.v
> self
.w
/ 2:
592 circVal
= circ(dist
- self
.w
/ 2 + self
.r
, self
.r
+ self
.rt
)
596 return self
.z
+ self
.h
/ 2 + circVal
600 circVal
= circ(dist
, self
.r
+ self
.rt
)
604 return self
.z
+ self
.h
/ 2 + self
.v
- self
.r
+ circVal
606 else: # and (dist <= self.edgeS(self.z - self.h / 2 - self.cl, 1) - self.x): # check bottom up
607 # hack for radialized masonry, import approx Z instead of self.top()
608 dist
= radialAdjust(dist
, self
.btm())
612 return self
.z
- self
.h
/ 2
614 # pointed arch on bottom
615 elif self
.vl
> self
.w
/ 2:
616 circVal
= circ(dist
- self
.w
/ 2 + self
.rl
, self
.rl
+ self
.rtl
)
620 return self
.z
- self
.h
/ 2 - circVal
622 # old conditional? if (dist-self.w / 2 + self.rl) <= (self.rl + self.rtl):
623 # domed arch on bottom
625 circVal
= circ(dist
, self
.rl
+ self
.rtl
) # dist-self.w / 2 + self.rl
629 return self
.z
- self
.h
/ 2 - self
.vl
+ self
.rl
- circVal
631 # and this never happens - but, leave it as failsafe :)
632 debug_prints(func
="opening.EdgeV",
633 text
="Got all the way out of the edgeV! Not good!")
634 debug_print_vars("opening x = ", self
.x
, ", opening z = ", self
.z
)
638 def edgeBev(self
, ht
):
639 if ht
> (self
.z
+ self
.h
/ 2):
641 if ht
< (self
.z
- self
.h
/ 2):
645 r1
= abs(dims
['t'] * sin(ht
* PI
/ (dims
['t'] * 2)))
653 def __init__(self
, xpos
, zpos
, width
, height
, archHeight
=0, archThk
=0,
654 archHeightLower
=0, archThkLower
=0, bevel
=0, edgeThk
=0):
657 self
.w
= float(width
)
658 self
.h
= float(height
)
660 self
.rtl
= archThkLower
662 self
.vl
= archHeightLower
666 # find the upper arch radius
667 if archHeight
>= width
/ 2:
668 # just one arch, low long
669 self
.r
= (self
.v
** 2) / self
.w
+ self
.w
/ 4
670 elif archHeight
<= 0:
676 self
.r
= (self
.w
** 2) / (8 * self
.v
) + self
.v
/ 2.
677 self
.c
= self
.rt
* cos(atan(self
.w
/ (2 * (self
.r
- self
.v
))))
679 # find the lower arch radius
680 if archHeightLower
>= width
/ 2:
681 self
.rl
= (self
.vl
** 2) / self
.w
+ self
.w
/ 4
682 elif archHeightLower
<= 0:
686 self
.rl
= (self
.w
** 2) / (8 * self
.vl
) + self
.vl
/ 2.
687 self
.cl
= self
.rtl
* cos(atan(self
.w
/ (2 * (self
.rl
- self
.vl
))))
689 # self.form = something?
690 self
.b
= float(bevel
)
694 # class for the whole wall boundaries; a sub-class of "opening"
695 class openingInvert(opening
):
696 # this is supposed to switch the sides of the opening
697 # so the wall will properly enclose the whole wall.
699 def edgeS(self
, ht
, s
):
700 return opening
.edgeS(self
, ht
, -s
)
702 def edgeV(self
, ht
, s
):
703 return opening
.edgeV(self
, ht
, -s
)
706 # class rows in the wall
710 This is the class for holding the data for individual rows of blocks.
711 each row is required to have some edge blocks, and can also have
712 intermediate sections of "normal" blocks.
717 def FillBlocks(self
):
718 # Set the radius variable, in the case of radial geometry
721 self
.radius
= dims
['t'] * (sin(self
.z
* PI
/ (dims
['t'] * 2)))
725 # initialize internal variables from global settings
728 SetHwt
= settings
['hwt']
729 SetWid
= settings
['w']
730 SetWidMin
= settings
['wm']
731 SetWidVar
= settings
['wv']
732 SetGrt
= settings
['g']
733 SetGrtVar
= settings
['gv']
734 SetRowHeightLink
= settings
['rwhl']
735 SetDepth
= settings
['d']
736 SetDepthVar
= settings
['dv']
738 # height weight, used for making shorter rows have narrower blocks, and vice-versa
739 hwt
= ((self
.h
/ SetH
- 1) * SetHwt
+ 1)
741 # set variables for persistent values: loop optimization, readability, single ref for changes.
743 avgDist
= hwt
* SetWid
/ self
.radius
744 minDist
= SetWidMin
/ self
.radius
745 deviation
= hwt
* SetWidVar
/ self
.radius
746 grtOffset
= SetGrt
/ (2 * self
.radius
)
748 # init loop variables that may change...
750 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (self
.radius
)
751 ThisBlockHeight
= self
.h
+ rndc() * (1 - SetRowHeightLink
) * SetGrtVar
752 ThisBlockDepth
= rndd() * SetDepthVar
+ SetDepth
754 for segment
in self
.RowSegments
:
755 divs
= fill(segment
[0] + grtOffset
, segment
[1] - grtOffset
, avgDist
, minDist
, deviation
)
757 # loop through the divisions, adding blocks for each one
758 for i
in range(len(divs
) - 1):
759 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
760 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - grt
762 self
.BlocksNorm
.append([ThisBlockx
, self
.z
, ThisBlockw
, ThisBlockHeight
, ThisBlockDepth
, None])
764 if SetDepthVar
: # vary depth
765 ThisBlockDepth
= rndd() * SetDepthVar
+ SetDepth
767 if SetGrtVar
: # vary grout
768 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (self
.radius
)
769 ThisBlockHeight
= self
.h
+ rndc() * (1 - SetRowHeightLink
) * SetGrtVar
771 def __init__(self
, centerheight
, rowheight
, edgeoffset
=0.):
772 self
.z
= float(centerheight
)
773 self
.h
= float(rowheight
)
774 self
.EdgeOffset
= float(edgeoffset
)
776 # THIS INITIALIZATION IS IMPORTANT! OTHERWISE ALL OBJECTS WILL HAVE THE SAME LISTS!
778 self
.RowSegments
= []
782 def arch(ra
, rt
, x
, z
, archStart
, archEnd
, bevel
, bevAngle
, vll
):
784 Makes a list of faces and vertices for arches.
785 ra: the radius of the arch, to the center of the bricks
786 rt: the thickness of the arch
787 x: x center location of the circular arc, as if the arch opening were centered on x = 0
788 z: z center location of the arch
789 anglebeg: start angle of the arch, in radians, from vertical?
790 angleend: end angle of the arch, in radians, from vertical?
791 bevel: how much to bevel the inside of the arch.
792 vll: how long is the vertex list already?
797 # initialize internal variables for global settings
798 SetGrt
= settings
['g']
799 SetGrtVar
= settings
['gv']
800 SetDepth
= settings
['d']
801 SetDepthVar
= settings
['dv']
803 # Init loop variables
805 def bevelEdgeOffset(offsets
, bevel
, side
):
807 Take the block offsets and modify it for the correct bevel.
809 offsets = the offset list. See MakeABlock
810 bevel = how much to offset the edge
811 side = -1 for left (right side), 1 for right (left side)
816 pointsToAffect
= right
818 pointsToAffect
= left
819 for num
in pointsToAffect
:
820 offsets
[num
] = offsets
[num
][:]
821 offsets
[num
][0] += -bevel
* side
823 ArchInner
= ra
- rt
/ 2
824 ArchOuter
= ra
+ rt
/ 2 - SetGrt
+ rndc() * SetGrtVar
826 DepthBack
= - SetDepth
/ 2 - rndc() * SetDepthVar
827 DepthFront
= SetDepth
/ 2 + rndc() * SetDepthVar
830 subdivision
= settings
['sdv']
834 grt
= (SetGrt
+ rndc() * SetGrtVar
) / (2 * ra
) # init grout offset for loop
835 # set up the offsets, it will be the same for every block
836 offsets
= ([[0] * 2 + [bevel
]] + [[0] * 3] * 3) * 2
838 # make the divisions in the "length" of the arch
839 divs
= fill(archStart
, archEnd
, settings
['w'] / ra
, settings
['wm'] / ra
, settings
['wv'] / ra
)
841 for i
in range(len(divs
) - 1):
843 ThisOffset
= offsets
[:]
844 bevelEdgeOffset(ThisOffset
, bevAngle
, - 1)
845 elif i
== len(divs
) - 2:
846 ThisOffset
= offsets
[:]
847 bevelEdgeOffset(ThisOffset
, bevAngle
, 1)
852 [divs
[i
] + grt
, divs
[i
+ 1] - grt
, ArchInner
, ArchOuter
, DepthBack
, DepthFront
],
853 subdivision
, len(avlist
) + vll
, ThisOffset
, [], None, ra
859 if SetDepthVar
: # vary depth
860 DepthBack
= -SetDepth
/ 2 - rndc() * SetDepthVar
861 DepthFront
= SetDepth
/ 2 + rndc() * SetDepthVar
863 if SetGrtVar
: # vary grout
864 grt
= (settings
['g'] + rndc() * SetGrtVar
) / (2 * ra
)
865 ArchOuter
= ra
+ rt
/ 2 - SetGrt
+ rndc() * SetGrtVar
867 for i
, vert
in enumerate(avlist
):
868 v0
= vert
[2] * sin(vert
[0]) + x
870 v2
= vert
[2] * cos(vert
[0]) + z
874 r1
= dims
['t'] * (sin(v2
* PI
/ (dims
['t'] * 2)))
879 avlist
[i
] = [v0
, v1
, v2
]
881 return (avlist
, aflist
)
886 The 'sketch' function creates a list of openings from the general specifications passed to it.
887 It takes curved and domed walls into account, placing the openings at the appropriate angular locations
890 for x
in openingSpecs
:
897 if x
['x'] > (x
['w'] + settings
['wm']):
898 spacing
= x
['x'] / r1
900 spacing
= (x
['w'] + settings
['wm']) / r1
902 minspacing
= (x
['w'] + settings
['wm']) / r1
904 divs
= fill(dims
['s'], dims
['e'], spacing
, minspacing
, center
=1)
906 for posidx
in range(len(divs
) - 2):
907 boundlist
.append(opening(divs
[posidx
+ 1], x
['z'], x
['w'], x
['h'],
908 x
['v'], x
['t'], x
['vl'], x
['tl'], x
['b']))
910 boundlist
.append(opening(x
['x'], x
['z'], x
['w'], x
['h'], x
['v'], x
['t'], x
['vl'], x
['tl'], x
['b']))
911 # check for overlapping edges?
916 def wedgeBlocks(row
, opening
, leftPos
, rightPos
, edgeBinary
, r1
):
918 Makes wedge blocks for the left and right sides, depending
920 wedgeBlocks(row, LeftWedgeEdge, LNerEdge, LEB, r1)
921 wedgeBlocks(row, RNerEdge, RightWedgeEdge, REB, r1)
923 wedgeEdges
= fill(leftPos
, rightPos
, settings
['w'] / r1
, settings
['wm'] / r1
,
926 for i
in range(len(wedgeEdges
) - 1):
927 x
= (wedgeEdges
[i
+ 1] + wedgeEdges
[i
]) / 2
928 grt
= (settings
['g'] + rndd() * settings
['gv']) / r1
929 w
= wedgeEdges
[i
+ 1] - wedgeEdges
[i
] - grt
931 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
933 # edgeV may return "None" - causing TypeError for math op.
934 # use 0 until wedgeBlocks operation worked out
935 edgeVal
= opening
.edgeV(x
- w
/ 2, edgeBinary
)
939 LeftVertOffset
= -(row
.z
- (row
.h
/ 2) * edgeBinary
- edgeVal
)
941 # edgeV may return "None" - causing TypeError for math op.
942 # use 0 until wedgeBlocks operation worked out
943 edgeVal
= opening
.edgeV(x
+ w
/ 2, edgeBinary
)
947 RightVertOffset
= -(row
.z
- (row
.h
/ 2) * edgeBinary
- edgeVal
)
949 # Wedges are on top = off, blank, off, blank
950 # Wedges are on btm = blank, off, blank, off
951 ThisBlockOffsets
= [[0, 0, LeftVertOffset
]] * 2 + [[0] * 3] * 2 + [[0, 0, RightVertOffset
]] * 2
953 # Insert or append "blank" for top or bottom wedges.
955 ThisBlockOffsets
= ThisBlockOffsets
+ [[0] * 3] * 2
957 ThisBlockOffsets
= [[0] * 3] * 2 + ThisBlockOffsets
959 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, ThisBlockOffsets
])
964 def bevelBlockOffsets(offsets
, bevel
, side
):
966 Take the block offsets and modify it for the correct bevel.
968 offsets = the offset list. See MakeABlock
969 bevel = how much to offset the edge
970 side = -1 for left (right side), 1 for right (left side)
973 pointsToAffect
= (0, 2) # right
975 pointsToAffect
= (4, 6) # left
976 for num
in pointsToAffect
:
977 offsets
[num
] = offsets
[num
][:]
978 offsets
[num
][0] += bevel
* side
981 def rowProcessing(row
, Thesketch
, WallBoundaries
):
983 Take row and opening data and process a single row, adding edge and fill blocks to the row data.
986 # check for openings, record top and bottom of row for right and left of each
987 # if both top and bottom intersect create blocks on each edge, appropriate to the size of the overlap
988 # if only one side intersects, run fill to get edge positions, but this should never happen
990 if radialized
: # this checks for radial stonework, and sets the row radius if required
992 r1
= abs(dims
['t'] * sin(row
.z
* PI
/ (dims
['t'] * 2)))
998 # set the edge grout thickness, especially with radial stonework in mind
999 edgrt
= settings
['ge'] * (settings
['g'] / 2 + rndc() * settings
['gv']) / (2 * r1
)
1001 # Sets up a list of intersections of top of row with openings,
1002 # from left to right [left edge of opening, right edge of opening, etc...]
1003 # initially just the left and right edge of the wall
1004 edgetop
= [[dims
['s'] + row
.EdgeOffset
/ r1
+ edgrt
, WallBoundaries
],
1005 [dims
['e'] + row
.EdgeOffset
/ r1
- edgrt
, WallBoundaries
]]
1007 # Same as edgetop, but for the bottms of the rows
1008 edgebtm
= [[dims
['s'] + row
.EdgeOffset
/ r1
+ edgrt
, WallBoundaries
],
1009 [dims
['e'] + row
.EdgeOffset
/ r1
- edgrt
, WallBoundaries
]]
1011 # set up some useful values for the top and bottom of the rows.
1012 rowTop
= row
.z
+ row
.h
/ 2
1013 rowBtm
= row
.z
- row
.h
/ 2
1015 for hole
in Thesketch
:
1016 # check the top and bottom of the row, looking at the opening from the right
1017 e
= [hole
.edgeS(rowTop
, -1), hole
.edgeS(rowBtm
, -1)]
1019 # If either one hit the opening, make split points for the left side of the opening.
1021 e
+= [hole
.edgeS(rowTop
, 1), hole
.edgeS(rowBtm
, 1)]
1023 # If one of them missed for some reason, set that value to
1024 # the middle of the opening.
1025 for i
, pos
in enumerate(e
):
1029 # add the intersects to the list of edge points
1030 edgetop
.append([e
[0], hole
])
1031 edgetop
.append([e
[2], hole
])
1032 edgebtm
.append([e
[1], hole
])
1033 edgebtm
.append([e
[3], hole
])
1035 # We want to make the walls in order, so sort the intersects.
1036 # This is where you would want to remove edge points that are out of order
1037 # so that you don't get the "oddity where overlapping openings
1038 # create blocks inversely" problem
1040 # Note: sort ended up comparing function pointers
1041 # if both Openings and Slots were enabled with Repeats in one of them
1043 edgetop
.sort(key
=lambda x
: x
[0])
1044 edgebtm
.sort(key
=lambda x
: x
[0])
1045 except Exception as ex
:
1046 debug_prints(func
="rowProcessing",
1047 text
="Sorting has failed", var
=ex
)
1049 # these two loops trim the edges to the limits of the wall.
1050 # This way openings extending outside the wall don't enlarge the wall.
1053 if ((edgetop
[-1][0] > dims
['e'] + row
.EdgeOffset
/ r1
) or
1054 (edgebtm
[-1][0] > dims
['e'] + row
.EdgeOffset
/ r1
)):
1061 # still trimming the edges...
1064 if ((edgetop
[0][0] < dims
['s'] + row
.EdgeOffset
/ r1
) or
1065 (edgebtm
[0][0] < dims
['s'] + row
.EdgeOffset
/ r1
)):
1073 # make those edge blocks and rows! Wooo!
1074 # This loop goes through each section, (a pair of points in edgetop)
1075 # and places the edge blocks and inbetween normal block zones into the row object
1076 for OpnSplitNo
in range(int(len(edgetop
) / 2)):
1077 # left edge is edge<x>[2*OpnSplitNo], right edge edgex[2*OpnSplitNo+1]
1078 leftEdgeIndex
= 2 * OpnSplitNo
1079 rightEdgeIndex
= 2 * OpnSplitNo
+ 1
1081 # get the openings, to save time and confusion
1082 leftOpening
= edgetop
[leftEdgeIndex
][1]
1083 rightOpening
= edgetop
[rightEdgeIndex
][1]
1085 # find the difference between the edge top and bottom on both sides
1086 LTop
= edgetop
[leftEdgeIndex
][0]
1087 LBtm
= edgebtm
[leftEdgeIndex
][0]
1088 RTop
= edgetop
[rightEdgeIndex
][0]
1089 RBtm
= edgebtm
[rightEdgeIndex
][0]
1093 # which is further out on each side, top or bottom?
1095 LNerEdge
= LBtm
# the nearer edge left
1096 LEB
= 1 # Left Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1102 RNerEdge
= RBtm
# the nearer edge right
1103 REB
= 1 # Right Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1107 REB
= -1 # Right Edge Boolean, set to 1 if furthest edge is top, -1 if it is bottom
1109 # The space between the closest edges of the openings in this section of the row
1110 InnerDiff
= RNerEdge
- LNerEdge
1111 # The mid point between the nearest edges
1112 InnerMid
= (RNerEdge
+ LNerEdge
) / 2
1114 # maximum distance to span with one block
1115 MaxWid
= (settings
['w'] + settings
['wv']) / r1
1116 AveWid
= settings
['w']
1118 # check the left and right sides for wedge blocks
1119 # Check and run the left edge first
1120 # find the edge of the correct side, offset for minimum block height. The LEB decides top or bottom
1121 ZPositionCheck
= row
.z
+ (row
.h
/ 2 - settings
['hm']) * LEB
1123 # edgeS may return "None"
1124 LeftWedgeEdge
= leftOpening
.edgeS(ZPositionCheck
, 1)
1126 if (abs(LDiff
) > AveWid
) or (not LeftWedgeEdge
):
1128 if not LeftWedgeEdge
:
1129 LeftWedgeEdge
= leftOpening
.x
1130 wedgeBlocks(row
, leftOpening
, LeftWedgeEdge
, LNerEdge
, LEB
, r1
)
1131 # set the near and far edge settings to vertical, so the other edge blocks don't interfere
1132 LTop
, LBtm
= LNerEdge
, LNerEdge
1135 # Now do the wedge blocks for the right, same drill... repeated code?
1136 # find the edge of the correct side, offset for minimum block height. The REB decides top or bottom
1137 ZPositionCheck
= row
.z
+ (row
.h
/ 2 - settings
['hm']) * REB
1139 # edgeS may return "None"
1140 RightWedgeEdge
= rightOpening
.edgeS(ZPositionCheck
, -1)
1141 if (abs(RDiff
) > AveWid
) or (not RightWedgeEdge
):
1143 if not RightWedgeEdge
:
1144 RightWedgeEdge
= rightOpening
.x
1145 wedgeBlocks(row
, rightOpening
, RNerEdge
, RightWedgeEdge
, REB
, r1
)
1146 # set the near and far edge settings to vertical, so the other edge blocks don't interfere
1147 RTop
, RBtm
= RNerEdge
, RNerEdge
1150 # Check to see if the edges are close enough toegther to warrant a single block filling it
1151 if (InnerDiff
< MaxWid
):
1152 # if this is true, then this row is just one block!
1153 x
= (LNerEdge
+ RNerEdge
) / 2.
1155 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1156 BtmOff
= LBtm
- LNerEdge
1157 TopOff
= LTop
- LNerEdge
1158 ThisBlockOffsets
= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1159 BtmOff
= RBtm
- RNerEdge
1160 TopOff
= RTop
- RNerEdge
1161 ThisBlockOffsets
+= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1162 bevel
= leftOpening
.edgeBev(rowTop
)
1163 bevelBlockOffsets(ThisBlockOffsets
, bevel
, 1)
1164 bevel
= rightOpening
.edgeBev(rowTop
)
1165 bevelBlockOffsets(ThisBlockOffsets
, bevel
, -1)
1166 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, ThisBlockOffsets
])
1169 # it's not one block, must be two or more
1170 # set up the offsets for the left
1171 BtmOff
= LBtm
- LNerEdge
1172 TopOff
= LTop
- LNerEdge
1173 leftOffsets
= [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2 + [[0] * 3] * 4
1174 bevelL
= leftOpening
.edgeBev(rowTop
)
1175 bevelBlockOffsets(leftOffsets
, bevelL
, 1)
1176 # and now for the right
1177 BtmOff
= RBtm
- RNerEdge
1178 TopOff
= RTop
- RNerEdge
1179 rightOffsets
= [[0] * 3] * 4 + [[BtmOff
, 0, 0]] * 2 + [[TopOff
, 0, 0]] * 2
1180 bevelR
= rightOpening
.edgeBev(rowTop
)
1181 bevelBlockOffsets(rightOffsets
, bevelR
, -1)
1182 # check to see if it is only two blocks
1183 if (InnerDiff
< MaxWid
* 2):
1184 # this row is just two blocks! Left block, then right block
1185 # div is the x position of the dividing point between the two bricks
1186 div
= InnerMid
+ (rndd() * settings
['wv']) / r1
1187 # set the grout distance, since we need grout separation between the blocks
1188 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1189 # set the x position and width for the left block
1190 x
= (div
+ LNerEdge
) / 2 - grt
/ 4
1191 w
= (div
- LNerEdge
) - grt
/ 2
1192 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1193 # For reference: EdgeBlocks = [[x, z, w, h, d, [corner offset matrix]], [etc.]]
1194 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, leftOffsets
])
1195 # Initialize for the block on the right side
1196 x
= (div
+ RNerEdge
) / 2 + grt
/ 4
1197 w
= (RNerEdge
- div
) - grt
/ 2
1198 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1199 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, rightOffsets
])
1202 # program should only get here if there are more than two blocks in the row, and no wedge blocks
1203 # make Left edge block
1205 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1206 # set the x position and width for the left block
1207 widOptions
= [settings
['w'], bevelL
+ settings
['wm'], leftOpening
.ts
]
1208 baseWid
= max(widOptions
)
1209 w
= (rndd() * settings
['wv'] + baseWid
+ row
. EdgeOffset
)
1210 widOptions
[0] = settings
['wm']
1212 w
= max(widOptions
) / r1
- grt
1213 x
= w
/ 2 + LNerEdge
+ grt
/ 2
1214 BlockRowL
= x
+ w
/ 2
1215 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1216 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, leftOffsets
])
1218 # make Right edge block
1220 grt
= (settings
['g'] + rndc() * settings
['gv']) / r1
1221 # set the x position and width for the left block
1222 widOptions
= [settings
['w'], bevelR
+ settings
['wm'], rightOpening
.ts
]
1223 baseWid
= max(widOptions
)
1224 w
= (rndd() * settings
['wv'] + baseWid
+ row
.EdgeOffset
)
1225 widOptions
[0] = settings
['wm']
1227 w
= max(widOptions
) / r1
- grt
1228 x
= RNerEdge
- w
/ 2 - grt
/ 2
1229 BlockRowR
= x
- w
/ 2
1230 ThisBlockDepth
= rndd() * settings
['dv'] + settings
['d']
1231 row
.BlocksEdge
.append([x
, row
.z
, w
, row
.h
, ThisBlockDepth
, rightOffsets
])
1233 row
.RowSegments
.append([BlockRowL
, BlockRowR
])
1237 def plan(Thesketch
, oldrows
=0):
1239 The 'plan' function takes the data generated by the sketch function and the global settings
1240 and creates a list of blocks.
1241 It passes out a list of row heights, edge positions, edge blocks, and rows of blocks.
1243 # if we were passed a list of rows already, use those; else make a list.
1247 # rows holds the important information common to all rows
1248 # rows = [list of row objects]
1251 # splits are places where we NEED a row division, to accommodate openings
1252 # add a split for the bottom row
1253 splits
= [dims
['b'] + settings
['hb']]
1255 # add a split for each critical point on each opening
1256 for hole
in Thesketch
:
1257 splits
+= hole
.crits()
1259 # and, a split for the top row
1260 splits
.append(dims
['t'] - settings
['ht'])
1263 # divs are the normal old row divisions, add them between the top and bottom split
1264 divs
= fill(splits
[0], splits
[-1], settings
['h'], settings
['hm'] + settings
['g'], settings
['hv'])[1: -1]
1266 # remove the divisions that are too close to the splits, so we don't get tiny thin rows
1267 for i
in range(len(divs
) - 1, -1, -1):
1268 for j
in range(len(splits
)):
1269 diff
= abs(divs
[i
] - splits
[j
])
1270 if diff
< (settings
['h'] - settings
['hv'] + settings
['g']):
1274 # now merge the divs and splits lists
1277 # add bottom and/or top points, if bottom and/or top row heights are more than zero
1278 if settings
['hb'] > 0:
1279 divs
.insert(0, dims
['b'])
1280 if settings
['ht'] > 0:
1281 divs
.append(dims
['t'])
1285 # trim the rows to the bottom and top of the wall
1286 if divs
[0] < dims
['b']:
1288 if divs
[-1] > dims
['t']:
1291 # now, make the data for each row
1292 # rows = [[center height,row height,edge offset],[etc.]]
1294 divCount
= len(divs
) - 1 # number of divs to check
1295 divCheck
= 0 # current div entry
1297 while divCheck
< divCount
:
1298 RowZ
= (divs
[divCheck
] + divs
[divCheck
+ 1]) / 2
1299 RowHeight
= divs
[divCheck
+ 1] - divs
[divCheck
] - settings
['g'] + rndc() * \
1300 settings
['rwhl'] * settings
['gv']
1301 EdgeOffset
= settings
['eoff'] * (fmod(divCheck
, 2) - 0.5) + settings
['eoffv'] * rndd()
1303 # if row height is too shallow: delete next div entry, decrement total, and recheck current entry.
1304 if RowHeight
< settings
['hm']:
1305 del(divs
[divCheck
+ 1])
1306 divCount
-= 1 # Adjust count for removed div entry.
1309 rows
.append(rowOb(RowZ
, RowHeight
, EdgeOffset
))
1311 divCheck
+= 1 # on to next div entry
1313 # set up a special opening object to handle the edges of the wall
1314 x
= (dims
['s'] + dims
['e']) / 2
1315 z
= (dims
['t'] + dims
['b']) / 2
1316 w
= (dims
['e'] - dims
['s'])
1317 h
= (dims
['t'] - dims
['b'])
1318 WallBoundaries
= openingInvert(x
, z
, w
, h
)
1320 # Go over each row in the list, set up edge blocks and block sections
1321 for rownum
in range(len(rows
)):
1322 rowProcessing(rows
[rownum
], Thesketch
, WallBoundaries
)
1324 # now return the things everyone needs
1325 # return [rows,edgeBlocks,blockRows,Asketch]
1326 return [rows
, Thesketch
]
1329 def archGeneration(hole
, vlist
, flist
, sideSign
):
1331 Makes arches for the top and bottom, depending on sideSign
1332 example, Lower arch:
1333 archGeneration(hole, vlist, flist, -1)
1334 example, Upper arch:
1335 archGeneration(hole, vlist, flist, 1)
1336 hole is the opening object that the arch is for
1337 add the vertices to vlist
1338 add the faces to flist
1339 sideSign is + or - 1, for the top or bottom arch. Other values may cause errors.
1342 # working arrays for vectors and faces
1346 # Top (1) or bottom (-1)
1348 r
= hole
.rl
# radius of the arch
1349 rt
= hole
.rtl
# thickness of the arch (stone height)
1350 v
= hole
.vl
# height of the arch
1353 r
= hole
.r
# radius of the arch
1354 rt
= hole
.rt
# thickness of the arch (stone height)
1355 v
= hole
.v
# height of the arch
1358 ra
= r
+ rt
/ 2 # average radius of the arch
1364 sideSignInv
= -sideSign
1366 if v
> w
/ 2: # two arcs, to make a pointed arch
1368 zpos
= z
+ (h
/ 2) * sideSign
1370 # left side top, right side bottom
1371 # angles reference straight up, and are in radians
1373 bevHt
= sqrt(bevRad
** 2 - (bevRad
- (w
/ 2 + bev
)) ** 2)
1374 midHalfAngle
= atan(v
/ (r
- w
/ 2))
1375 midHalfAngleBevel
= atan(bevHt
/ (r
- w
/ 2))
1376 bevelAngle
= midHalfAngle
- midHalfAngleBevel
1377 anglebeg
= (PI
/ 2) * (sideSignInv
)
1378 angleend
= (PI
/ 2) * (sideSignInv
) + midHalfAngle
1380 avlist
, aflist
= arch(ra
, rt
, (xoffset
) * (sideSign
), zpos
, anglebeg
, angleend
, bev
, bevelAngle
, len(vlist
))
1382 for i
, vert
in enumerate(avlist
):
1383 avlist
[i
] = [vert
[0] + hole
.x
, vert
[1], vert
[2]]
1387 # right side top, left side bottom
1389 # angles reference straight up, and are in radians
1390 anglebeg
= (PI
/ 2) * (sideSign
) - midHalfAngle
1391 angleend
= (PI
/ 2) * (sideSign
)
1393 avlist
, aflist
= arch(ra
, rt
, (xoffset
) * (sideSignInv
), zpos
, anglebeg
, angleend
, bev
, bevelAngle
, len(vlist
))
1395 for i
, vert
in enumerate(avlist
):
1396 avlist
[i
] = [vert
[0] + hole
.x
, vert
[1], vert
[2]]
1402 Dpth
= settings
['d'] + rndc() * settings
['dv']
1403 Grout
= settings
['g'] + rndc() * settings
['gv']
1404 angleBevel
= (PI
/ 2) * (sideSign
) - midHalfAngle
1405 Wdth
= (rt
- Grout
- bev
) * 2 * sin(angleBevel
) * sideSign
# note, sin may be negative
1406 MidZ
= ((sideSign
) * (bevHt
+ h
/ 2.0) + z
) + (rt
- Grout
- bev
) \
1407 * cos(angleBevel
) # note, cos may come out negative
1408 nearCorner
= sideSign
* (MidZ
- z
) - v
- h
/ 2
1411 TopHt
= hole
.top() - MidZ
- Grout
1414 BtmHt
= - (hole
.btm() - MidZ
) - Grout
1417 # set the amount to bevel the keystone
1418 keystoneBevel
= (bevHt
- v
) * sideSign
1419 if Wdth
>= settings
['hm']:
1420 avlist
, aflist
= MakeAKeystone(x
, Wdth
, MidZ
, TopHt
, BtmHt
, Dpth
, keystoneBevel
, len(vlist
))
1423 for i
, vert
in enumerate(avlist
):
1425 r1
= dims
['t'] * sin(vert
[2] * PI
/ (dims
['t'] * 2))
1428 avlist
[i
] = [((vert
[0] - hole
.x
) / r1
) + hole
.x
, vert
[1], vert
[2]]
1432 # remove "debug note" once bevel is finalized.
1434 debug_prints(func
="archGeneration",
1435 text
="Keystone was too narrow - " + str(Wdth
))
1437 else: # only one arc - curve not peak.
1438 # bottom (sideSign -1) arch has poorly sized blocks...
1440 zpos
= z
+ (sideSign
* (h
/ 2 + v
- r
)) # single arc positioning
1442 # angles reference straight up, and are in radians
1449 halfangle
= atan(w
/ (2 * (r
- v
)))
1451 anglebeg
= angleOffset
- halfangle
1452 angleend
= angleOffset
+ halfangle
1454 anglebeg
= angleOffset
- PI
/ 2
1455 angleend
= angleOffset
+ PI
/ 2
1457 avlist
, aflist
= arch(ra
, rt
, 0, zpos
, anglebeg
, angleend
, bev
, 0.0, len(vlist
))
1459 for i
, vert
in enumerate(avlist
):
1460 avlist
[i
] = [vert
[0] + x
, vert
[1], vert
[2]]
1465 # Make the Side Stones
1466 grt
= (settings
['g'] + rndc() * settings
['gv'])
1467 width
= sqrt(rt
** 2 - c
** 2) - grt
1469 if c
> settings
['hm'] + grt
and c
< width
+ grt
:
1471 subdivision
= settings
['sdv'] * (zpos
+ (h
/ 2) * sideSign
)
1473 subdivision
= settings
['sdv']
1475 # set the height of the block, it should be as high as the max corner position, minus grout
1476 height
= c
- grt
* (0.5 + c
/ (width
+ grt
))
1478 # the vertical offset for the short side of the block
1479 voff
= sideSign
* (settings
['hm'] - height
)
1481 zstart
= z
+ sideSign
* (h
/ 2 + grt
/ 2)
1482 woffset
= width
* (settings
['hm'] + grt
/ 2) / (c
- grt
/ 2)
1483 depth
= rndd() * settings
['dv'] + settings
['d']
1486 offsets
= [[0] * 3] * 6 + [[0] * 2 + [voff
]] * 2
1487 topSide
= zstart
+ height
1490 offsets
= [[0] * 3] * 4 + [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 2
1492 btmSide
= zstart
- height
1493 # Do some stuff to incorporate bev here
1494 bevelBlockOffsets(offsets
, bev
, -1)
1496 avlist
, aflist
= MakeABlock(
1497 [x
- xstart
- width
, x
- xstart
- woffset
, btmSide
, topSide
,
1498 -depth
/ 2, depth
/ 2], subdivision
, len(vlist
),
1499 Offsets
=offsets
, xBevScl
=1
1502 # top didn't use radialized in prev version;
1503 # just noting for clarity - may need to revise for "sideSign == 1"
1505 for i
, vert
in enumerate(avlist
):
1506 avlist
[i
] = [((vert
[0] - x
) / vert
[2]) + x
, vert
[1], vert
[2]]
1511 # keep sizing same - neat arches = master masons :)
1512 # grt = (settings['g'] + rndc()*settings['gv'])
1513 # height = c - grt*(0.5 + c/(width + grt))
1514 # if grout varies may as well change width too... width = sqrt(rt**2 - c**2) - grt
1515 # voff = sideSign * (settings['hm'] - height)
1516 # woffset = width*(settings['hm'] + grt/2)/(c - grt/2)
1519 offsets
= [[0] * 3] * 2 + [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 4
1520 topSide
= zstart
+ height
1523 offsets
= [[0] * 2 + [voff
]] * 2 + [[0] * 3] * 6
1525 btmSide
= zstart
- height
1526 # Do some stuff to incorporate bev here
1527 bevelBlockOffsets(offsets
, bev
, 1)
1529 avlist
, aflist
= MakeABlock(
1530 [x
+ xstart
+ woffset
, x
+ xstart
+ width
, btmSide
, topSide
,
1531 -depth
/ 2, depth
/ 2], subdivision
, len(vlist
),
1532 Offsets
=offsets
, xBevScl
=1
1535 # top didn't use radialized in prev version;
1536 # just noting for clarity - may need to revise for "sideSign == 1"
1538 for i
, vert
in enumerate(avlist
):
1539 avlist
[i
] = [((vert
[0] - x
) / vert
[2]) + x
, vert
[1], vert
[2]]
1548 Build creates the geometry for the wall, based on the
1549 "Aplan" object from the "plan" function. If physics is
1550 enabled, then it make a number of individual blocks with
1551 physics interaction enabled. Otherwise it creates
1552 geometry for the blocks, arches, etc. of the wall.
1558 # all the edge blocks, redacted
1559 # AllBlocks = [[x, z, w, h, d, [corner offset matrix]], [etc.]]
1561 # loop through each row, adding the normal old blocks
1562 for rowidx
in range(len(rows
)):
1563 rows
[rowidx
].FillBlocks()
1567 # If the wall is set to merge blocks, check all the blocks to see if you can merge any
1568 # seems to only merge vertical, should do horizontal too
1570 for rowidx
in range(len(rows
) - 1):
1573 r1
= dims
['t'] * sin(abs(rows
[rowidx
].z
) * PI
/ (dims
['t'] * 2))
1575 r1
= abs(rows
[rowidx
].z
)
1579 Tolerance
= settings
['g'] / r1
1580 idxThis
= len(rows
[rowidx
].BlocksNorm
[:]) - 1
1581 idxThat
= len(rows
[rowidx
+ 1].BlocksNorm
[:]) - 1
1584 # end loop when either array idx wraps
1585 if idxThis
< 0 or idxThat
< 0:
1588 blockThis
= rows
[rowidx
].BlocksNorm
[idxThis
]
1589 blockThat
= rows
[rowidx
+ 1].BlocksNorm
[idxThat
]
1591 # seems to only merge vertical, should do horizontal too...
1592 cx
, cz
, cw
, ch
, cd
= blockThis
[:5]
1593 ox
, oz
, ow
, oh
, od
= blockThat
[:5]
1595 if (abs(cw
- ow
) < Tolerance
) and (abs(cx
- ox
) < Tolerance
):
1601 AllBlocks
.append([(cx
+ ox
) / 2, (cz
+ oz
+ (oh
- ch
) / 2) / 2,
1602 BlockW
, abs(cz
- oz
) + (ch
+ oh
) / 2, (cd
+ od
) / 2, None])
1604 rows
[rowidx
].BlocksNorm
.pop(idxThis
)
1605 rows
[rowidx
+ 1].BlocksNorm
.pop(idxThat
)
1614 # Add blocks to create a "shelf/platform".
1615 # Does not account for openings (crosses gaps - which is a good thing)
1617 SetGrtOff
= settings
['g'] / 2 # half grout for block size modifier
1619 # Use wall block settings for shelf
1620 SetBW
= settings
['w']
1621 SetBWVar
= settings
['wv']
1622 SetBWMin
= settings
['wm']
1623 SetBH
= settings
['h']
1625 # Shelf area settings
1626 ShelfLft
= shelfSpecs
['x']
1627 ShelfBtm
= shelfSpecs
['z']
1628 ShelfEnd
= ShelfLft
+ shelfSpecs
['w']
1629 ShelfTop
= ShelfBtm
+ shelfSpecs
['h']
1630 ShelfThk
= shelfSpecs
['d'] * 2 # use double-depth due to offsets to position at cursor.
1632 # Use "corners" to adjust position so not centered on depth.
1633 # Facing shelf, at cursor (middle of wall blocks)
1634 # - this way no gaps between platform and wall face due to wall block depth.
1635 wallDepth
= settings
['d'] / 2 # offset by wall depth so step depth matches UI setting :)
1636 if shelfBack
: # place blocks on backside of wall
1638 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1639 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1640 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0],
1641 [0, ShelfThk
/ 2, 0], [0, wallDepth
, 0]
1645 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1646 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1647 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0],
1648 [0, -wallDepth
, 0], [0, -ShelfThk
/ 2, 0]
1651 # Add blocks for each "shelf row" in area
1652 while ShelfBtm
< ShelfTop
:
1654 # Make blocks for each row - based on rowOb::fillblocks
1655 # Does not vary grout.
1656 divs
= fill(ShelfLft
, ShelfEnd
, SetBW
, SetBWMin
, SetBWVar
)
1658 # loop through the row divisions, adding blocks for each one
1659 for i
in range(len(divs
) - 1):
1660 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
1661 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - SetGrtOff
1663 AllBlocks
.append([ThisBlockx
, ShelfBtm
, ThisBlockw
, SetBH
, ShelfThk
, ShelfOffsets
])
1665 ShelfBtm
+= SetBH
+ SetGrtOff
# moving up to next row...
1667 # Add blocks to create "steps".
1668 # Does not account for openings (crosses gaps - which is a good thing)
1670 SetGrtOff
= settings
['g'] / 2 # half grout for block size modifier
1672 # Vary block width by wall block variations.
1673 SetWidVar
= settings
['wv']
1674 SetWidMin
= settings
['wm']
1676 StepXMod
= stepSpecs
['t'] # width of step/tread, also sets basic block size.
1677 StepZMod
= stepSpecs
['v']
1679 StepLft
= stepSpecs
['x']
1680 StepRt
= stepSpecs
['x'] + stepSpecs
['w']
1681 StepBtm
= stepSpecs
['z'] + StepZMod
/ 2 # Start offset for centered blocks
1682 StepWide
= stepSpecs
['w']
1683 StepTop
= StepBtm
+ stepSpecs
['h']
1684 StepThk
= stepSpecs
['d'] * 2 # use double-depth due to offsets to position at cursor.
1686 # Use "corners" to adjust steps so not centered on depth.
1687 # Facing steps, at cursor (middle of wall blocks)
1688 # - this way no gaps between steps and wall face due to wall block depth.
1689 # Also, will work fine as stand-alone if not used with wall (try block depth 0 and see what happens).
1690 wallDepth
= settings
['d'] / 2
1691 if stepBack
: # place blocks on backside of wall
1693 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1694 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1695 [0, StepThk
/ 2, 0], [0, wallDepth
, 0],
1696 [0, StepThk
/ 2, 0], [0, wallDepth
, 0]
1700 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1701 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1702 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0],
1703 [0, -wallDepth
, 0], [0, -StepThk
/ 2, 0]
1706 # Add steps for each "step row" in area (neg width is interesting but prevented)
1707 while StepBtm
< StepTop
and StepWide
> 0:
1709 # Make blocks for each step row - based on rowOb::fillblocks
1710 # Does not vary grout.
1712 if stepOnly
: # "cantilevered steps"
1714 stepStart
= StepRt
- StepXMod
1718 AllBlocks
.append([stepStart
, StepBtm
, StepXMod
, StepZMod
, StepThk
, StepOffsets
])
1720 divs
= fill(StepLft
, StepRt
, StepXMod
, SetWidMin
, SetWidVar
)
1722 # loop through the row divisions, adding blocks for each one
1723 for i
in range(len(divs
) - 1):
1724 ThisBlockx
= (divs
[i
] + divs
[i
+ 1]) / 2
1725 ThisBlockw
= divs
[i
+ 1] - divs
[i
] - SetGrtOff
1727 AllBlocks
.append([ThisBlockx
, StepBtm
, ThisBlockw
, StepZMod
, StepThk
, StepOffsets
])
1729 StepBtm
+= StepZMod
+ SetGrtOff
# moving up to next row...
1730 StepWide
-= StepXMod
# reduce step width
1732 # adjust side limit depending on direction of steps
1734 StepRt
-= StepXMod
# move in from right
1736 StepLft
+= StepXMod
# move in from left
1738 # Copy all the blocks out of the rows
1740 AllBlocks
+= row
.BlocksEdge
1741 AllBlocks
+= row
.BlocksNorm
1743 # This loop makes individual blocks for each block specified in the plan
1744 for block
in AllBlocks
:
1745 x
, z
, w
, h
, d
, corners
= block
1748 r1
= dims
['t'] * sin(z
* PI
/ (dims
['t'] * 2))
1754 geom
= MakeABlock([x
- w
/ 2, x
+ w
/ 2, z
- h
/ 2, z
+ h
/ 2, -d
/ 2, d
/ 2],
1755 settings
['sdv'], len(vlist
),
1756 corners
, None, settings
['b'] + rndd() * settings
['bv'], r1
)
1760 # This loop makes Arches for every opening specified in the plan.
1761 for hole
in Aplan
[1]:
1763 if hole
.vl
> 0 and hole
.rtl
> (settings
['g'] + settings
['hm']): # make lower arch blocks
1764 archGeneration(hole
, vlist
, flist
, -1)
1767 if hole
.v
> 0 and hole
.rt
> (settings
['g'] + settings
['hm']): # make upper arch blocks
1768 archGeneration(hole
, vlist
, flist
, 1)
1770 # Warp all the points for domed stonework
1772 for i
, vert
in enumerate(vlist
):
1773 vlist
[i
] = [vert
[0], (dims
['t'] + vert
[1]) * cos(vert
[2] * PI
/ (2 * dims
['t'])),
1774 (dims
['t'] + vert
[1]) * sin(vert
[2] * PI
/ (2 * dims
['t']))]
1776 # Warp all the points for radial stonework
1778 for i
, vert
in enumerate(vlist
):
1779 vlist
[i
] = [vert
[2] * cos(vert
[0]), vert
[2] * sin(vert
[0]), vert
[1]]
1785 def createWall(radial
, curve
, openings
, mergeBlox
, shelf
, shelfSide
,
1786 steps
, stepDir
, stepBare
, stepSide
):
1788 Call all the functions you need to make a wall, return the verts and faces.
1801 # set all the working variables from passed parameters
1805 openingSpecs
= openings
1806 bigBlock
= mergeBlox
1810 shelfBack
= shelfSide
1815 aplan
= plan(asketch
, 0)