[029] How to Perform Vertical and Horizontal Splits in CityEngine

How First Edge Defines the X-Axis Direction Counter-Clockwise Polygon Construction Behavior

Transition from horizontal floor divisions to vertical facade splits by manipulating coordinate axes with alignScopeToGeometry.

In CityEngine, procedural modeling relies heavily on understanding the local coordinate system of each shape. This tutorial explains how to use the split command for vertical divisions and introduces the alignScopeToGeometry(zUp) function. By reorienting the Z-axis to the Y-axis, you can apply architectural logic using the more intuitive X and Y plane, making it much easier to code complex building facades, floors, and repeating window patterns.

Default Axis Orientation and Split Direction in CityEngine

The splits we have learned so far were mainly focused on horizontal divisions. Today, we will cover vertical splits.

Regarding the content mentioned in the previous session, I hope you haven't forgotten that "if you do not specify an axis, the first edge becomes the direction of the X-axis."

If you look at the image below, there are four squares, each with a different direction for its first edge.

When you select a shape, the edge displayed in orange is the first edge, and CityEngine connects segments in a counter-clockwise direction when constructing a polygon.

Default X-axis direction in CityEngine
img 1 : Default X-axis direction in CityEngine

Performing Vertical and Horizontal Splits Using CGA split()

Let's apply the following code to the image above.


Lot -->
    split(x){ 10 : A | 10 : B | 10 : C | 10 : D | 10 : E }

A --> color(1,0,0) X. #Red
B --> color(0,1,0) X. #Green
C --> color(0,0,1) X. #Blue
D --> color(1,1,0) X. #Yellow
E --> color(1,0,1) X. #Magenta

I split a 50m square into 10m fixed values along the X-axis and applied a color to each. Looking at the results:

Split results based on axis direction
img 2 : Split results based on axis direction

You can see that the splits were performed in the order of Red-Green-Blue-Yellow-Magenta along the first edge direction, which is the X-axis direction.

This time, let's try splitting based on the Z-axis. You simply need to change split(x) to split(z).


Lot -->
    split(z){ 10 : A | 10 : B | 10 : C | 10 : D | 10 : E }

The result is as follows.

Result of split(z)
img 3 : Result of split(z)

When viewed from the Y-axis direction, it is easier to understand if you think of the X-axis as 12 o'clock and the Z-axis as 3 o'clock.

Reorienting Axes with alignScopeToGeometry(zUp)

The problem is... we are not familiar with these axis directions. The axis direction we are usually familiar with is:

General axis direction on a 2D plane
img 4 : General axis direction on a 2D plane

As shown in the image above, the origin is at the bottom-left, X increases to the right, and Y increases upward. This is similar to how we count building floors from the bottom: 1st floor, 2nd floor, 3rd floor.

However, if you look at the four shapes earlier, none of them match the direction we are used to. While you can use them as they are if you are comfortable, let's change the axes forming the plane to a more familiar orientation. Can you understand this by looking at the image?

Axis rotation using alignScopeToGeometry(zUp)
img 5 : Axis rotation using alignScopeToGeometry(zUp)

The image on the left shows the axis directions of the plane we have seen so far.

The X and Z axes are on the plane, and the Y axis is in the height direction.

In that state, if you rotate the Z-axis toward the Y-axis direction using the X-axis as the rotation axis, the Y-axis, which was in the height direction, will now be positioned on the plane. Consequently, when splitting the plane, you can perform the splits using the X and Y axes instead of the X and Z axes.

There are two shapes with the same firstEdge as shown below. Imagine these are building facades.

Plane axes and directions in CityEngine
img 6 : Plane axes and directions in CityEngine

In both cases above, the X-axis is in a familiar direction, but the Z-axis is not. Let's see what kind of result we get when we add the following code.

Lot1 -->
    split(z){ '.5 : A | '.5 : B }

Lot2 -->
    alignScopeToGeometry(zUp, 0, 0)
    split(y){ '.5 : A | '.5 : B }
A --> color(1,0,0) X. #Blue
B --> color(0,1,0) X. #Green

Line 2: Lot1 splits the floors 1:1 without changing the axis and colors them Red-Green. Since the Z-axis points from top to bottom, it will likely be colored Red-Green from the top down.

Line 5: The Z-axis is changed to the height axis, and the axes forming the plane are changed to X and Y.

Line 6: Since the existing Z-axis has been changed to Y, we divide the floor heights using split(y) instead of split(z). It will be colored Red-Green from the bottom up. Let's look at the result.

Difference in split results due to axis rotation
img 7 : Difference in split results due to axis rotation

In the case of Lot2, the axes are set to a familiar direction, and the split is performed from the bottom to the top. Let's open the Model Hierarchy panel to check how the actual axis directions have changed.

Plane axes changed by alignScopeToGeometry(zUp)
img 8 : Plane axes changed by alignScopeToGeometry(zUp)

You can see that the colors of the axes have changed.

CityEngine displays the X-axis in red, the Y-axis in green, and the Z-axis in blue.

Axes... Scope... these are difficult to understand right now. I am just using them briefly to help you perform splits in directions familiar to you. Do you remember the Facade Wizard content where we created building facades with vertical and horizontal lines?

Recalling the massive amount of code generated by the Facade Wizard, let's practice splitting starting from simple forms.

Imagine the rectangle below is a building facade. We will divide it into two floors of equal height, leaving the 1st floor as a single block and splitting the 2nd floor in half.

Practice facade layout
img 9 : Practice facade layout

Facade Modeling Practice: Multi-Level Split Structure

Shall we look at the code?


Lot -->
    alignScopeToGeometry(zUp, 0, 0)
    split(y){ '.5 : A | '.5 : B }
    # Z-axis is rotated upward to change the workspace to the XY plane.
    # Since the Y-axis is now the upward direction, we use split(y). 
    # The 1st floor is passed to rule A and colored red.
    # The 2nd floor is passed to rule B
    
A --> Red
B --> split(x) { '0.5 : Grn | '0.5 : Blu }

Red --> color(1,0,0) X. #Red
Grn --> color(0,1,0) X. #Green
Blu --> color(0,0,1) X. #Blue

Simple, right? 

This code can also be shortened as follows:


Lot -->
    alignScopeToGeometry(zUp, 0, 0)
    split(y){ '.5 : Red | '.5 : split(x) { '0.5 : Grn | '0.5 : Blu } }

Within the 2nd-floor area, split(x) is executed to divide the 2nd floor into two sections. As such, you can separate and use rules one by one, or you can simply express the rule in a single line. 

You can use whichever method is more convenient as you get used to it. 


Next, let's create a form like the one below.

Advanced split practice
img 10 : Advanced split practice

Split Practice:

  • The 1st floor is 10m high and has no internal splits.
  • The 2nd floor occupies the remaining height after excluding the heights of the 1st and 3rd floors, and it is repeatedly split with a fixed width of 5m.
  • The 3rd floor is 10m high, and the areas split into 2m and 3m are repeating.

Rather than actually typing out the code, it is enough to just visualize in your head, "I use ~ here and * there." Shall we look at the code?

Lot -->
    alignScopeToGeometry(zUp, 0, 0)
    split(y){ 10 : A | ~1 : B | 10 : C }

A --> Red

B -->
    split(x){ 5 : BB }*

BB --> Grn

C -->
    split(x){ 2 : CC | 3 : CCC }*

CC --> Blu
CCC --> Yll

Red --> color(1,0,0) X.
Grn --> color(0,1,0) X.
Blu --> color(0,0,1) X.
Yel --> color(1,1,0) X.

for Lot1 : The Z-axis is rotated into the Y-axis position so that the Y-axis, along with X, becomes the axis forming the plane. Since the Y-axis direction is from bottom to top, it becomes the 1st, 2nd, and 3rd floors in the order of the split. We split 10m from the bottom to proceed to rule A, split the last 10m for the 3rd floor (rule C), and proceed to rule B for the 2nd floor, which is the section excluding those two areas.

for B : Performed a repeating 5m fixed-value split along the X-axis and colored it green.

for C : Performed a repeating split of 2m and 3m fixed values along the X-axis and colored them blue and yellow, respectively. Vertical and horizontal splits are essentially repetitions of such combinations, so your skills will improve if you practice by changing variables or symbols.

That's all for today. Next time, we will practice more complex splits.

"Understanding the local scope is the secret to mastering CityEngine. Once you can align the axes to your own intuition, coding facades feels like drawing on a piece of paper."
— Author, cubizen.com
Continue Learning
Explore complete tutorial collections for CityEngine and Global Mapper.

Comments

Popular posts from this blog

029] How to Split Polygon Areas with Intersecting Lines in Global Mapper

[008] How to Understand CGA Rule Structure in CityEngine (Beginner Guide)

046] How to Create Parallel Offset Lines in Global Mapper