Introducing Randomness to CityEngine Models

What's Covered:

  • Stochastic probability statements

  • The rand() or random function

  • The p() or probability function

  • The Seedian function

Ten turns on CityEngine's Random Number Generator

Stochastic probability statements

This is a good place to talk about how we can inject randomness into our CityEngine creations. If you’ve read the explanation of conditional logic and “case/else” statements, you know we can use STOCHASTIC rules to assign percent chances to outcomes. What does "stochastic" mean? Google the definition and you get this:

Randomly determined; having a random probability distribution or pattern that may be analyzed statistically but may not be predicted precisely.

You can see this applied in these code examples:

case Type == "Apartment Building"   :
     5%: ApartmentBlock   
     10%: ParkingGround  
else: GreenGround Trees

Of all the Lots assigned to Apartment Building land use, there is a 5 percent chance an individual lot will be assigned to apartment buildings, a 10 percent chance it will be used for parking and a remaining (85) percent chance of having GreenGround Trees. BUT you may not end up with precisely that distribution in the final model.  Here's another example:

       case distanceToCenter < radiusApartmentArea  : 
               20%: "Office Building"  
               76%: "Apartment Building" 
               3%: "Residential"       
        else: "Open Space"

Each lot of a size determined by a formula has a 20 percent chance of being allocated to office buildings, a 76 percent chance of being assigned apartments, a 3 percent of being residential and a remaining (1 percent) chance of being to open space. The actual outcome may not match these percentages; what we have done is stack the deck for each individual assignment. Kind of random but kind of not.

To add real randomness, there are three functions you should know:

The rand() or random function

case scope.sx > 2 : setback(rand(20,35)){ front: ResidentialBlockStripe | remainder: ResidentialBlockBack }
       else                        : ResidentialBlockBack

The rand(20,35) function will randomly assign setback widths of between 20 and 35. There is no stacking the deck with any assignment.

The p() or probability function

case p(0.3) && geometry.isConcave && scope.sz > 25:

              split(z){ ~rand(14,23): ResidentialBlockStripeSubdivide }*

This one is a little more involved. Each instance of p() is a coin flip that will return a “true” or “false” value, except it is possible to weight the sides of the coin. p(0.3) means you have a 30 percent chance of getting a “true” response. You can combine this boolean (True-False) value with others to create probability.

In the example, each shape has a 30 percent coin-flip chance of being tried by the rest of this statement. If that chance is successful AND the geometry has a concave shape AND its z-axis scope is greater than 25, it will be split on the z-axis; the split width will be a random value between 14 and 23 and; the split will be pointed to the ResidentialBlockStripeSubdivide rule (* = the split will be repeated as many times as possible).  Combine this statement with other case statements having various percentages, and you have some control over probability.

The Random Seed value generates two very different highrise buildings on this lot.

The Seedian function

OK, so in rand(20,35), CityEngine will randomly assign a value between 20 and 35 with each iteration. Where do those numbers come from? CityEngine has a random number generator called a “seedian” that generates “seed” values between -999999 and +999999 -- that's 1,999,999 possibilities including "0". Just like planting a garden, every entity will grow in a way based on it’s seed.

CityEngine documentation says “the seedian shape attribute controls the seed of the random number generator. It can both be read and set.” As in the illustration above, we can read a shape’s seed value (say THAT three times, fast) by clicking a shape and looking at the Random Seed value in the property inspector: the first value is -687048; when we update (reset) the seed as in the inset, the random value is 81727 and we get a very different building. Every shape has a seed value. We update a seed for individual or selected shapes, or for the whole model by entering CTL + SHIFT + G or by clicking the Update Seed button on the tool bar.

As much as I would love to be miserable and dissect this component to see how it works, I won’t. If we needed to jack with the seed value, there would be a code function or a way to manually enter a value; there isn’t. No point.

Whatever control we have on probability lies in the rand() and p() functions and the stochastic rules. From there, we are subject to the whims of the bell-shaped curve and maybe some leptokurtic kurtosis, a malady for which they have yet to find the antidote. A bit of Master’s degree humor there.

Leave a Reply

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