Code Analysis: International City, Part 6 – Randomizing Building Shapes

I have spent the last few weeks working to thoroughly understand this bit of code I derived from the internationalCity.cga model (Lines 134 to 258) and how to best explain it. This exercise demonstrates how the rules for Residential lots create maximum boundaries for buildings, create building footprints within those boundaries and random building shapes within those footprints. To get my head around this code, I deleted random number ranges (rand(#,#)) ranges from the rules, which add even more variety to the model.

In CityEngine, create a new rule file, copy the stochasticLots.cga code below into the file and save. Then click through the slides above and follow the steps. Thanks to WordPress and how line breaks are copied with CTL + C, the  line numbers I've cited may or may not coincide with what you see. So, I have delineated "code blocks" to at least get you in the neighborhood.

Let the good times rrrr-roll.

/**

 * File:    stochasticLots.cga

 * Created: 21 July 2017

 * Author:  Jeff Herzer | jeffherzer.com/ce

 */



version "2016.0"



# ###################

# This code is derived from the internationalCity.cga rule file

# within CityEngine’s International City example.

#

# Some lines of code have been commented (#) so you can disable

# code segments and analyze how this rule file works.

# Remove the # at the beginning of the code line to activate and

# replace it to deactivate.

# # ###################





# Import facade and roof texture rule files from the ESRI library

@Hidden

import Facade_Textures          : "/ESRI.lib/rules/Facades/Facade_Textures.cga" ()

import Roof_Textures       : "/ESRI.lib/rules/Roofs/Roof_Textures.cga" ()



# ########## BEGIN BLOCK 0 ########## #

# Set parameters for building characteristics

@Hidden

attr HeightFactor = 2

@Hidden

attr buildingHeight = 18

@Hidden

attr roofAngle             = rand(40,65)

@Hidden

attr roofOverhang          = rand(0.2,0.5)

# ########## BEGIN BLOCK 0 ########## #



# Set colors for displaying building segments

@Hidden

attr red = ("#FF0000")

@Hidden

attr green = ("#00FF00")

@Hidden

attr blue = ("#0000FF")

@Hidden

attr cyan = ("#00FFFF")

@Hidden

attr magenta = ("#FF00FF")

@Hidden

attr yellow = ("#FFFF00")





# BEGIN RULES  #



@Start

Lot -->

# Randomly sets x- and z-dimensions for maximum shape footprints

# to relative percentages of lot sizes





# ########## BEGIN BLOCK 1 ########## #

# Replacement Code: s('0.8,0,'0.6)



# Set maximum boundaries for building footprints

     s('0.8,0,'0.6) 

    

# Centers the shape on the lot's x- and z- coordinates

 #   center(xz)

# Points the shape to ResidentialShape(buildingHeight)

     ResidentialShape(buildingHeight)



# ########## END BLOCK 1 ########## #



    

ResidentialShape(h) -->



# ########## BEGIN BLOCK 2 ########## #

# The primary shape patterned after the shape footprint is created

# between the [square brackets]; scope s() sets the building dimensions

# proportional to the footprint



# Set building dimensions

# Replacement Code: [ s('0.8,0,'0.6) Mass(h,blue)]

          [ s('0.8,0,'0.6)]

         

# translate t() moves the secondary shape created below on the z-axis 

#         t(0,0,8)



#scope s() randomly adjusts the z-width of the secondary shape.

#    s('1,0,'0.4)



# ########## END BLOCK 2 ########## #





# ########## BEGIN BLOCK 3 ########## #

# These code lines create secondary shapes to modify building geometry

#  REPLACEMENT CODE: split(x){ '0.33: Mass(h,red)| '0.33: Mass(h,yellow) | '0.33: Mass(h,green)  }

# U-Shape 

#     split(x){ '0.33: Mass(h,red)| ~1: NIL | '0.33: Mass(h,green)  }



# L-Shape, left

#          split(x){ ~1: NIL | '0.33: Mass(h,red)}



# L-Shape, right

#          split(x){ '0.33 : Mass(h,red) | ~1 : NIL } 



# T-Shape 

#    split(x){ ~1: NIL | '0.33 : Mass(h,red) | ~1: NIL }



# ########## END BLOCK 3 ########## #



# ########## BEGIN BLOCK 4 ########## #    

# This color shows the primary mass shape  

Mass(h,c) -->

     extrude(h*HeightFactor)

     color(c)

#   Stories



Stories -->

     comp(f){ side: Facade | top: Roof("rooffloor") }



Facade -->

     Facade_Textures.Generate

    

# ########## END BLOCK 4 ########## #



# Roof Generation

#



Roof(rooftype) -->

     case rooftype == "rooffloor":

           RoofPlane alignScopeToGeometry(yUp,auto) RoofFloor

     case rooftype == "gable1":

           roofGable(roofAngle,roofOverhang,roofOverhang,false,1) comp(f){ bottom: NIL | vertical: Facade | all: RoofPlane }

     case rooftype == "gable2":

           roofGable(roofAngle,0,roofOverhang,false,0) comp(f){ bottom: NIL | vertical: Facade | all: RoofPlane }

     case rooftype == "gable":

           roofGable(roofAngle,roofOverhang) comp(f){ bottom: NIL | vertical: Facade | all: RoofPlane }

     case rooftype == "hip":

           roofHip(roofAngle,roofOverhang) comp(f){ bottom: NIL | all: RoofPlane }

     else:

           RoofPlane

RoofFloor -->

     21% : offset(-scope.sz*rand(0.0,0.35),inside)

             shapeL(scope.sz*rand(0.2,0.7),scope.sx*rand(0.2,0.8)){ shape: rotateScope(0,90,0) Mass(rand(1,3)/HeightFactor,"flat") }

     21% : offset(-scope.sz*rand(0.0,0.35),inside)

             shapeL(scope.sz*rand(0.2,0.7),scope.sx*rand(0.2,0.8)){ shape: rotateScope(0,90,0) Mass(rand(1,3)/HeightFactor,"flat") }

     26% : innerRect [ s('(rand(0.6,0.9)),'(rand(0.6,0.9)),'1) center(xy) RoofFloorShape(rand(3,4)/HeightFactor) ]

     21% : innerRect [ s('(rand(0.6,0.9)),'(rand(0.6,0.9)),'1) center(xy) RoofFloorShape(rand(1,2)/HeightFactor) ]

     10% : set(Roof_Textures.SlopedRoofTexture, Roof_Textures.getFlatRoofTexture)

             innerRect

             [ s('(rand(0.2,0.4)),'1,'(rand(0.2,0.4))) center(xz) t('rand(-0.5,0.5),0,'rand(-0.5,0.5)) ResidentialShape(rand(2,3)/HeightFactor) ]

             [ s('(rand(0.6,0.9)),'1,'1) center(xz) RoofFloorShape(rand(1,2)/HeightFactor) ]

     else: NIL

    

RoofFloorShape(h) -->

     split(z){ ~1: RoofFloorBack(h) | 'rand(0.5,0.7): Mass(h,"flat") }

          

RoofFloorBack(h) -->

     20% : split(x){ 'rand(0.2,0.8): Mass(h,"flat") } # L-shape left

     20% : split(x){ ~1: NIL | 'rand(0.2,0.8): Mass(h,"flat") } # L-shape right

     40% : split(x){ ~(rand): NIL | (rand(0.8,1.5)*scope.sz):  Mass(h,"flat") | ~(rand): NIL } # T-shape

     10% : split(x){ 'rand(0.2,0.45): Mass(h,"flat") | ~1: NIL | 'rand(0.2,0.45): Mass(h,"flat") }  # U-shape

     else: NIL



RoofPlane -->

     Roof_Textures.Generate

Leave a Reply

Your email address will not be published. Required fields are marked *