Nicolai Steinø's post above is excellent. That is a great way to make the railing.
In comparison, the code I will add is not a very nice solution, and it has limitations. However, in addition to creating a nice railing, you might also want to know how to create the corner balcony that indents inward such that you have an L shaped balcony. Here is one solution that breaks up the balcony into two pieces (which is why it is not an ideal solution).
The code splits each facade into tiles. Just to test the concept, balconies are put in each corner and on a small number of random facade tiles in the middle. Then, to test if a tile should make a corner balcony, the tiles are extruded backwards to see if they intersect other extruded tiles. (Note: The code doesn't actually do an extrude here but instead uses a cube.) The screenshot shows how the corner balconies are made in two parts. The green part is created by the facade tile which comes from the last tile in the row, and the yellow part is created by the facade tile which comes from the first tile in the row. So, the last tile in the row creates a shortened balcony, and the first tile in the row creates the full width balcony.
Limitations: This only works for 90 degree corners. I only considered rectangular shapes just to illustrate the principle, so this won't work for concave regions. Furthermore, this might cause some ugly, short railing pieces (since the railing is broken into two pieces) when using Nicolai Steinø's method above to create railings. Like I said, not ideal. Maybe someone has another solution.
/*
This rule creates corner balconies that go inwards.
First tiles in a row create a balcony which is the full width of the tile.
Last tiles in a row create a balcony which is shortened by the balcony width.
*/
const height = 12
const floor_height = 4
const tile_width = 4
const balcony_width = 1
const balcony_height = 1
const extrudedTile_label = "Extruded_Tile"
Lot -->
extrude(height)
comp(f) { side: Facade | top: Roof. }
Facade -->
split(y) { ~floor_height: split(x) { ~tile_width: Tile(split.index) }* }*
Tile(xInd) -->
case xInd==0:
TileWithBalcony(true, false)
case xInd==split.total-1:
TileWithBalcony(false, true)
case p(0.2):
TileWithBalcony(false, false)
else:
Wall.
TileWithBalcony(isFirst, isLast) -->
s('1, '1, 0.4*tile_width)
primitiveCube
t(0, 0, '-1)
label(extrudedTile_label)
TestOcclusion(isFirst, isLast)
TestOcclusion(isFirst, isLast) -->
case overlaps(intra, extrudedTile_label):
CreateBalcony(isFirst, isLast, true)
else:
CreateBalcony(isFirst, isLast, false)
CreateBalcony(isFirst, isLast, hasCornerBalcony) -->
case isFirst && hasCornerBalcony:
color(1,1,0)
t(0, 0, scope.sz-balcony_width)
s('1, '1, balcony_width)
comp(f) { front: s('1, balcony_height, '1) BalconyRailing.
| left: s('1, balcony_height, '1) BalconyRailing.
| back: s(scope.sx-balcony_width,'1,'1) Wall.
| top: NIL
| all: Wall. }
case isLast && hasCornerBalcony:
color(0,1,0)
t(0, 0, scope.sz-balcony_width)
s(scope.sx-balcony_width, '1, balcony_width)
comp(f) { front: s('1, balcony_height, '1) BalconyRailing.
| right: NIL
| top: NIL
| all: Wall. }
else:
t(0, 0, scope.sz-balcony_width)
s('1, '1, balcony_width)
comp(f) { front: s('1, balcony_height, '1) BalconyRailing.
| top: NIL
| all: Wall. }