[033] How to Use the Comp Command in Esri CityEngine for Component-Based Modeling

Learn how to decompose 3D volumes into editable sub-elements using the component split.

This tutorial introduces the 'comp' command, explaining the fundamental differences between it and the 'split' command. You will learn how to isolate vertices, edges, and faces from a 3D model, understand the difference between shared and independent edges, and use operators to control model hierarchy and indexing for targeted rule applications.

When I first started this blog, I thought it would be enough to simply explain a few commands, organize the important details, and finish after writing about 50 posts. Even though I have covered less than 10% of what I originally planned, this series has already reached its 33rd post.

Not long ago, I saw the music video for "Standing Next to You" and started playing the drums again, which I had forgotten about for a while. I had taken a break because my right knee would ache every time I kicked. I started by reducing the BPM to 50%, but it looks like I'll have to work hard to catch up to my original speed. Hopefully my knee can keep up.

What Is the comp Command in CityEngine?

Starting from this post, we will explore the comp command.

Why comp() Is Different from split()

The biggest difference between split and comp is that split divides faces, and its output is always another face. In other words, split operations always produce faces as their output. However, the results of comp are more diverse, including points (vertices), lines (edges), faces, groups, materials, and holes.

Understanding the Structure of comp()

The usage is as follows.

Comp Syntax Diagram
img 1 : Comp Syntax Structure (from ArcGIS Official Documentation)

'Component' is the element to be split (point, line, face), 'selector' defines the condition used to identify matching components. 'operator' determines whether to split those elements individually or group them, and 'operation' is the part where the rule is applied.

It looks simple, but to extract components that meet specific conditions, you must construct the conditions (selector) reasonably.

Comp Parameters
img 2 : Parameters used in the comp command

In the process of using CGA to turn a simple box into a detailed 3D object by subdividing it, there are many things to consider. Since all of this must be implemented through coding, you must use appropriate selectors to select the elements you want. More selectors will likely be added in the future, allowing for even more precise selection of desired elements.

Since there is so much to cover, I'm worried about where to start. Shall we start with the component types first?

Component Types in comp()

There are seven types of components: v (vertex), e (edge or edge where faces meet), fe (face edge), f (face), g (group), m (material), and h (hole). Among these, g, m, and h are used when importing 3D objects created externally, so they aren't topics for basic comp lessons. We will focus on v, e, fe, and f.

We start with the easiest and simplest. First, create a shape and model like the following.

Initial Setup
img 3 : Lot1 and Lot2 setup

Using comp(v) to Extract Vertices

First, I created the code below to extract only the vertices.


Lot1 --> A

Lot2 --> extrude(world.up, 20) A

A --> comp(v) { all : color(1, 0, 0) X. }

Lot1 proceeds directly to rule A, and Lot2 proceeds to A after being extruded by 20m. 

In line 3, each model selects only 'v' (vertices) and colors them red. The result is as follows.

Vertex Extraction
img 4 : Applying comp(v)

Currently, the vertices on the ground are not visible because 'Shapes' are active in the Visibility settings of the 3D View panel.

Visibility Settings
img 5 : Adjusting visibility to see vertices

If you uncheck 'Shapes' as shown in the figure above (F11), you will be able to see the generated vertices. The reason they look black even though they were colored red is...

View Settings
img 6 : Adjusting view settings

Currently, you just need to disable 'Wireframe on shaded/textured' in the View settings of the 3D View panel. However, since they are literally just points, they are not easily visible unless you zoom in. 

Let's change the settings a bit in the Preferences of the Edit menu.

Preferences
img 7 : Changing vertex and edge size preferences

Run Preferences and increase the Edge size and Vertices size from their default values in 'Procedural Runtime'. The setting above means to express edges at 0.1m width and vertices at 1m size.

Then it will look like the following.

Enlarged Vertices
img 8 : Clearly visible enlarged vertices

The vertices have become larger. Now they are clearly recognizable.

Understanding comp(e) and Shared Edges

Next, let's try separating by 'e'. You just need to change the code from comp(v) to comp(e).


Lot1 --> A

Lot2 --> extrude(world.up, 20) A

A --> comp(e) { all : color(1, 0, 0) X. }

The result is:

Edge Extraction
img 9 : Result of comp(e)

While comp(e) extracts the boundaries of faces, it treats an edge where two faces meet as a single component. Shall we check the number of edges generated on the left and right? Let's check the Model Hierarchy panel.

Hierarchy for Edge
img 10 : Checking edge count in Hierarchy

4 edges were generated for Lot1, and 12 edges for Lot2. Keep in mind that comp(e) treats the part where faces meet as one edge.

Difference Between comp(e) and comp(fe)

Now let's look at comp(fe).


Lot1 --> A

Lot2 --> extrude(world.up, 20) A

A --> comp(fe) { all : color(1, 0, 0) X. }

Let's look at the result.

Face Edge Extraction
img 11 : Applying comp(fe)

It doesn't look different from comp(e), but let's check with the Model Hierarchy panel.

Hierarchy for Face Edge
img 12 : Independent face-edges in Hierarchy

How Face-Edges Work in comp(fe)

Lot1 generated 4 edges as before, but Lot2 generated 24 edges. comp(fe) treats each face boundary independently, even when edges are shared between adjacent faces. This is the difference between comp(e) and comp(fe). Keep this in mind.

Using comp(f) to Isolate Faces

Next is the most commonly used comp(f).


Lot1 --> A

Lot2 --> extrude(world.up, 20) A

A --> comp(f) { all : color(1, 0, 0) X. }

The result is:

Face Extraction
img 13 : Visualizing individual faces

Yes. It seems that only the initial shape and color have changed, but if you look at the Model Hierarchy:

Hierarchy for Face
img 14 : Faces isolated in Hierarchy

You can see that each face is separated as a 'face'. In the case of Lot2, we can now access each of these faces and apply separate rules. Right now, we are only covering the basics of comp(). Applying rules to separated elements is something that will be done later. For now, understanding the fundamentals is more important than rushing into advanced modeling. Strong fundamentals make advanced modeling much easier later.

Difference Between ':' and '=' Operators in comp()

Next, we will compare the difference between the operators ':' and '='.

':' means that the separated elements will be processed individually, and '=' means that the separated elements will be grouped and processed as one. You can use these after the selector. The usage is as follows.


Lot1 --> extrude(world.up, 20) Equal

Lot2 --> extrude(world.up, 20) Colon

Equal --> comp(f) { all = color(0.5, 0.5, 0) X. }

Colon --> comp(f) { all : color(0, 0.5, 0.5) X. }

Lot1 separates the faces forming the shape with '=', and Lot2 separates them with ':'. The results are shown below, and if you check the Model Hierarchy:

Operator Comparison
img 15 : Difference between grouped and individual processing

Lot1 groups all separated faces and sends them out as one unit, while Lot2 sends out the separated faces individually. This principle applies to v, e, fe, and f in the same way. The difference lies in whether you want to receive the desired elements as a bundle to apply the next rule collectively, or separate them individually. Remember the 'index' we covered before? It allows us to find out the order in which splits or comps were performed.

How to Use comp.index in CityEngine

In the case of Lot1, comp is performed once, and in Lot2, comp is performed 6 times. This means that for Lot2, you can select a specific face and apply a rule simply by using the index.

Indexing
img 16 : Selecting face via Hierarchy index

If you click the third 'X' of Lot2 in the Hierarchy panel on the right, that specific face will be highlighted in the 3D View window. This means that by accessing index = 2, you can proceed with a separate rule for that face.

Selecting Individual Faces with comp.index

Shall we try it? I've coded it like this.


Lot2 --> extrude(world.up, 20) Colon

Colon --> comp(f) { all : A }

A --> 
    case (comp.index == 2) : 
        color(1, 0, 0)
    else : X.

Line 7: Passes the faces separated by ':' to rule A. 

Line 9: Colors the face red where comp.index is 2. The result?

Comp Index Result
img 17 : Targeted face coloring via index

If you select that face in the model hierarchy:

Hierarchy Result
img 18 : Confirmation of face index targeting

Yes. The face with index = 2 has turned red. There is still more to cover, but this post is already getting long.

I will cover the remaining content in the next post.

FAQ About comp() in CityEngine

What is the difference between split() and comp() in CityEngine?

split() divides geometry into smaller faces, while comp() extracts existing components such as vertices, edges, and faces for separate processing.

What is the difference between comp(e) and comp(fe)?

comp(e) treats shared edges between faces as a single edge, while comp(fe) generates separate edges for each face independently.

How does comp.index work in CityEngine?

comp.index stores the order of generated components, allowing you to target specific faces, edges, or vertices with conditional rules.

When should I use ':' instead of '=' in comp()?

Use ':' when you want components processed individually. Use '=' when you want all selected components grouped into a single shape.

"The details are not the details. They make the design."
— Charles Eames
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