[025] How Relative Split Ratios (~) Work in CityEngine CGA

Distribute remaining architectural spaces proportionally using relative ratios (~) in CGA split and learn approximate repeating splits.

This tutorial continues our exploration of the split command by focusing on the powerful tilde (~) operator. You will learn how to divide geometries based on proportions rather than fixed meters, how to combine fixed dimensions with relative ones, and how to use approximate repeating splits to ensure your models perfectly fill a given space without leaving small leftover fragments.

Since tomorrow will be a busy day making kimchi, I wanted to write this lesson in advance.

Following the previous lecture, we continue with the split command. There is something I forgot to mention briefly when explaining the tilde (~).

Imagine a situation like this.

When splitting a rectangular lot with a total length of 40m, do you always have to input the exact numerical values for the lengths you want? Calculating the total length and entering exact values every time can become tedious. Since every building has different dimensions, manually recalculating values each time is inefficient.

"I want to cut out 10m at the beginning, 10m at the end, and just handle the middle part as one piece."

Fortunately, this is exactly what the tilde (~) operator is designed for. 

As a reminder, since we haven't learned about 'scope' yet, it may still be unclear how the split direction works. For now, ignore the direction and just check if it splits according to the set ratios.

Split with Fixed and Relative Values

Look at the code below.

Lot -->
    split(x){ 10: A | ~1: B | 10: C }

A --> color(1,0,0) X. // Red
B --> color(0,1,0) X. // Green
C --> color(0,0,1) X. // Blue

In line 2, the beginning and end parts are split with a fixed 10m, and the middle part is handled with ~1: B. I mentioned that ~ represents a ratio, right? This results in taking the entire middle area as one piece and passing it to rule B.

The result would be the same even if you wrote ~2. Since there are no other relative values to compare against, it simply takes the entire remaining space.

Let's look at the result.

Result of split with fixed ends and relative middle
img 1 : Result of split(x){ 10: A | ~1: B | 10: C }

If you're curious whether this changes anything, let's change the middle split to ~10: B and see the result.


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

A --> color(1,0,0) X.
B --> color(0,1,0) X.
C --> color(0,0,1) X.

The result?

Comparison with different ratio value
img 2 : It is the same.

It's exactly the same. Next, here is code that splits only the first part as 10m and groups everything else as one.

Lot -->
    split(x){ 10: A | ~1: B }

A --> color(1,0,0) X.
B --> color(0,1,0) X.
C --> color(0,0,1) X.

This means splitting the first part as 10m for A, and passing the rest to B.

Simple split with remainder
img 3 : Result of split(x){ 10: A | ~1: B }

Let's say there is a tall building. Let's code a building where the first floor height is 5m and red, and from the second floor to the roof (we don't know how many floors it will be), it is split in a 1:2 ratio into green and blue sections.

Try to imagine how you would build this before checking the solution below.


Lot -->
    split(y){ 5: A | ~1: B | ~2: C }

A --> color(1,0,0) X.
B --> color(0,1,0) X.
C --> color(0,0,1) X.

First, it splits 5m. Then, it splits the remaining area to have a ratio of 1:2.

Shall we see the result?

Proportional split of remaining space
img 4 : The ground floor is fixed, and the rest is split 1:2.

The tilde (~) does not interact with fixed-value segments inside the same split block. Relative values only compare against other relative values within the same split. If there is only one tilde, it takes up the entire remaining area excluding the fixed values. If there are two or more tildes, it means they share the remaining area according to their ratios. Do not forget this; it is important.

Combining Multiple Relative Ratios in One Split

Look at the code below and try to visualize the result in your head.

Lot -->
    split(x){ 5: A | ~1: B | 5: C | ~2: D | 5: E }

A --> color(1,0,0) X.
B --> color(0,1,0) X.
C --> color(0,0,1) X.
D --> color(1,1,0) X. // Yellow
E --> color(1,0,1) X. // Magenta

This means there are three areas split by fixed values at the beginning, middle, and end, and in between them, it splits in a 1:2 ratio using tildes (~).

Let's look at the result.

Multiple fixed and relative segments
img 5 : Red, Blue, and Magenta are each 5m, while Green and Yellow take the rest at a 1:2 ratio.

If the result came out as you expected, then you're starting to build a good intuition for CGA. If the result matched what you expected, you're starting to build a good intuition for CGA. In language learning, listening usually comes before speaking. Coding works in a similar way — you first become familiar with grammar and structure before creating your own logic. Coding and conversation are the same in that they are processes of learning new grammar and words.

There’s no need to feel pressured if you’re still learning the syntax and terminology. Focus first on understanding how different examples work by reading a variety of code patterns.

Before wrapping up, let’s revisit one concept mentioned at the end of the previous lesson: when the tilde (~) and asterisk (*) are used together, the tilde behaves as an approximate value : when tilde (~) and asterisk (*) are used together, the tilde (~) is treated as an approximation.

Approximate Splitting with (*) Operator

While you might not need this function when drawing exactly according to a blueprint, it is very useful when you need to generate buildings with flexible proportions. It's best to compare by making four 40m wide rectangles.

Four test lots for split comparison
img 6 : Four identical lots labeled Lot1 to Lot4.

I copied the rectangle where the previous coding was applied and placed them side by side. From the top, I set the StartRule to Lot1, Lot2, Lot3, and Lot4 in order. You can just select each shape and type it directly into the StartRule in the Inspector panel.

Now, let's look at the following code and compare the results.


Lot1 --> split(x){ 3: A | 4: B }
// Splitting by fixed values without '*' after the brackets. 
// Cuts 3m for A, 4m for B, and discards the rest.

Lot2 --> split(x){ 3: A | 4: B }*
// '*' is added after the brackets. 
// This means repeat the split. It repeats fixed splits of 3m and 4m.

Lot3 --> split(x){ ~3: A | ~4: B }
// Split with '~', so the entire area is split in a ratio of 3:4 (1:1.333).

Lot4 --> split(x){ ~3: A | ~4: B }*
// When '~' is inside '...*', it is said that '~' becomes an approximation.

The results are as follows:

Comparison of four split types
img 7 : Visual comparison of the four split methods.

The ones to keep an eye on are Lot2 and Lot4.

Because Lot2 repeats splits of 3 and 4 across the total 40m, a 2m leftover fragment is created at the left (or end). Because Lot4 uses both ~ and * to split by approximation, no leftover space remains.

If we measure the width of the A and B split areas in Lot4:

Measurement of approximate split dimensions
img 8 : Actual dimensions are roughly 2.86m and 3.81m instead of 3m and 4m.

Yes. Instead of exactly 3 or 4, they come out as 2.86 and 3.81. Adding the two equals 6.67. Since it repeated 6 times, 6.67 multiplied by 6 is close to 40.The dimensions are slightly reduced so the split pattern can repeat without leaving unused space.

In this situation, a question arises. What is the standard for splitting larger or smaller than the designated size? Naturally, there must be a rule that determines this behavior. We will cover those criteria in the next lesson.

"Patterns emerge when simple rules are repeated."
— Christopher Alexander
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