CityEngine Experiments, Part 4

 

Recursion in CGA / Fractal Massing

Here are some examples of recursive programming in CGA. These are all simple, abstract rules, strictly for the point of demonstrating recursion in CityEngine. I learned this from an example in this Esri video, which begins at 40:10 into the video. Watch the entire video if you'd like a great introduction to CityEngine.

These rules are basically doing the same thing - using CGA's split operation - and then sending the resultant masses back to the same function for recursive processing. The shapes created are examples of fractal geometries, in which each part of the whole is self-similar to the whole itself. Recursive algorithms are commonly used to create these shapes, because the design pattern which defines the first geometric operation is repeatedly applied to the shapes resulting from the original operation. These fractal shapes can be defined as infinitely recursive, but in practice we must implement a termination condition within the recursion, where we specify the point at which recursion ends. The CGA code for each is below - copy and paste into CityEngine and try altering the code and see what interesting results you get.

 

 

 

An Organic Fractal Shape

version "2012.1"

attr direction = -1 
attr fraction = 0.66
attr terminationSize = 0.5

Lot-->
	convexify()	innerRect() 
	s('0.9,'1,'0.9) center(xz) 
	alignScopeToAxes(y) s('1,0,'1)
	extrude(1) 
	set(direction,0)
	Recursion
	
Recursion-->
	case scope.sx > terminationSize
	&& scope.sz > terminationSize:
		case direction == 0:
			set(direction,1)
			split(x){'fraction : Recursion | 
			~1 : s('1, '1, '0.5) center(z) Recursion}
		else :
			set(direction,0)
			split(z){'fraction : Recursion | 
			~1 : s('0.5, '1, '1) center(x) Recursion}
	else : Mass

Fractals are often associated with shapes found in nature, because many organisms employ self-similar growth over a range of scales. In writing this rule, I had NO intention of creating something that looked like a leaf. I was actually going to try and model recursive splitting seen in the golden rectangle, which is where the alternating between x and z splits arose. But upon happening onto the leaf shape, I halted that investigation, as I was consumed by watching the variation in shape as I altered the variables and regenerated the shapes.

To vary the shape, adjust the "fraction" and "terminationSize" attributes.

Beware - when adjusting the "fraction", the closer you get to 1, the more shapes will be generated, so watch your memory usage.

The direction attribute will flip the direction of the split between each recursion - it is reversed with each call to the recursive rule. This is a good example of how you can store variables during CGA execution, and they seem bound to the shape stack somehow. It is still not completely obvious to me how this works, but it does, and is a good thing to know about.

 

 

 

Fractal-Shaped Buildings

version "2012.1"

attr fraction = 0.5

Lot -->	convexify()	innerRect() comp(f) {all:SubLots}
SubLots --> # Cull small lots.
	case geometry.area <= 100:	NIL
	else: Footprint

Footprint-->
	s('0.9,'1,'0.9) center(xz) 
	alignScopeToAxes(y) s('1,0,'1)
	MakeItSquare
	
MakeItSquare-->
	case scope.sx < scope.sz :
		s('1,'1,scope.sx)
	else :
		s(scope.sz, '1,'1)
	ScaleAndRotateCopies
	Recursion

ScaleAndRotateCopies-->
	s('0.5,'1,'0.5) t('1,0,'1) extrude(60)
	R0 R90 R180 R270

R0--> Recursion
R90--> r(0,90,0) Recursion
R180--> r(0,180,0) Recursion
R270--> r(0,270,0) Recursion

Recursion-->
	# Uncomment below line for a new twist.
	#r(scopeCenter,0,90,0) 
	Recursion2

Recursion2-->
	case scope.sy > 1:
		split(y) {'0.5 : Mass | 
		~1 : Recursion3}	
	else : Mass

Recursion3-->
	split(x) {'fraction : split(z){'fraction: NIL | 
	'(1-fraction): Recursion} |
	'(1-fraction):split(z){'fraction: Recursion | 
	'(1-fraction): NIL}}

True fractal forms in architecture are rare, because we construct things differently than nature does. However, it is intellectually stimulating to try and define building mass in this manner, so this example represents an attempt to create a fractal shape that would work as a real building mass.

This example worked best as a square, so the first bit of code finds a square footprint within the lot shape. Next, the square is divided into four rotated corners which all recieve the same rules. This gives a more realistic symmetry to the building.

Here the recursive rule is actually divided into three rules. The first rule (Recursion) simply offers a chance to introduce a 90 degree twist, which creates a nice variation (see image below). The second rule (Recursion2) splits the mass into a top and bottom half, and sends the top half to the third rule (Recursion3), which splits the upper mass into four quarters, and then sends two of the quarters back to the first rule (Recursion). The other two quarters are banished via NIL.

 

 

 

Branching with Inserted Models

version "2012.1"

Lot -->	convexify()	innerRect() comp(f) {all:SubLots}

SubLots --> # Cull small lots.
	case geometry.area <= 20:	NIL
	else: Footprint

Footprint-->
	s('0.9,'1,'0.9) center(xz) alignScopeToAxes(y) s('1,0,'1)
	extrude(rand(10,100)) 
	MakeItSquare
	
MakeItSquare-->
	case scope.sx < scope.sz :
		s('1,'1,scope.sx)
	else :
		s(scope.sz, '1,'1)
	Recursion

Recursion-->
	r(scopeCenter, 0,0,0) Recursion2

Recursion2-->
    case scope.sx > 0.1:
        split(y) {'rand(0.2,0.7) : Mass | ~1 : Recursion3}	
    else : Mass

Recursion3-->
    split(x) {'0.5 : split(z){'0.5: NIL | '0.5: Recursion} |
        '0.5:split(z){'0.5: Recursion | '0.5: NIL}}
	
Mass-->
	i("models/b9.obj)
	r(scopeCenter,0,90,0)

This is basically the same algorithm as in the previous example, except in this case, I've modeled a branch shape as a Wavefront OBJ model, and inserted that object. To run the example code, you'll need to download the model and place it in your "models" folder (or place it elsewhere and change the path).

For this to work, I had to pay attention to the placement of the branch connections on the top and bottom of the model - they are sized based on the recursive splitting pattern. I did have a bit of a problem getting CityEngine to accept my OBJ files. It could be that I'm using Rhino to create the OBJ files, and maybe the Rhino OBJ export is not 100% right.