comp operation

CityEngine 2024.1    |    |  

Syntax

  • comp(component) { selector operator operations | ... }

Parameters

  1. componentselector
    The component into which to split
    • f — faces.
    • e — edges.
    • fe — face edges.
    • v — vertices.
    • g — groups.
    • m — materials.
    • h — holes.
  2. selector—(selector, bool, float)
    • { front | back | left | right | top | bottom }—The y-normals of the components are analyzed by classifying their directions into the corresponding quadrants (in relation to the scope coordinate system of the current shape).
    • { object.front | object.back | object.left | object.right | object.top | object.bottom | object.side}—The y-normals of the components are analyzed by classifying their directions into the corresponding quadrants (in relation to the object coordinate system of the current initial shape).
    • { world.south | world.north | world.west | world.east | world.up | world.down | world.side }—The y-normals of the components are analyzed by classifying their directions into the corresponding quadrants (in relation to the world coordinate system).
    • { vertical | horizontal | aslant | nutant }—The y-normals are analyzed in relation to the xz-plane of the current shape's local coordinate system. The angle between normals and xz-plane is used to classify the components with the ranges in degrees:
      • horizontal: ]78.75, 90]
      • aslant: ]11.25, 78.75]
      • vertical: ]-11.25, 11.25]
      • nutant: ]-78.55, -11.25]
      • horizontal: ]-90, -78.75]
    • side—Selects all but the horizontal components.
    • { border | inside }—Components at the border of or fully inside the geometry respectively. Border edges are connected to only one face; border faces contain one or more border edges; border vertices are start or end point of one or more border edges. This selector does not work on holes.
    • { eave | hip | valley | ridge }—These selectors work on edges only and are designed to be used in conjunction with roofs. See section below for more details.
    • { street.front | street.back | street.left | street.right | street.side }—If the streetWidth attribute is available on the initial shape, these selectors can be used to identify street-facing components. See section below for more details.
    • all—Selects all components.
    • { isTagged(tagQuery) | isTagged(tagQuery, subcomponent) }—Selects tagged components: See the section below for details.
      • tagQuerystring

        The tag query.

      • subcomponentselector

        { e | v }—If specified, components are selected if tagQuery matches any of their subcomponents (edges or vertices) instead. See the section below for details.

        Note:

        Tags stored on a face also identify its edges and vertices, while tags stored on an edge also identify its two vertex endpoints.

    • logical expression (bool)—Selects all components for which the expression evaluates to true. User-defined functions as well as the above selectors can be used in the expression.
    • index (float)—Selects the index-th component (0-based).
  3. operator
    The operator defines how the selected components are used to generate successor shapes. Valid operators are:
    • : Each selected component is put into a new shape.
    • = All selected components are combined into one new shape.
  4. operations
    A sequence of shape operations to execute on the newly created shape.

Description

The comp operation (component split) allows to divide a shape into its topological components, which are either faces, edges, vertices, groups, materials, or holes. The components can be selected using either their index or a set of selectors. Group and material components satisfy a specific selection keyword if at least one geometric component (usually a face) satifies it. The selected components are transformed to a new shape and processed by a sequence of shape operations. Depending on the operator, either one shape is created for each individual selected component (":") ore one shape for the whole set of selected components ("=").

The selection parameters of a component split work in a excluding manner: if a parameter has selected a specific component, this component cannot be part of another selection (from left to right).

The local coordinate systems (pivot and scope) of the newly generated shapes are aligned according to the geometry's topology; the component split is one of the few shape operations which manipulate the pivot of a shape.

  • f

    In the case of a face component split, the x-axis will be parallel to the first edge of the face and the z-axis wil point along the face's normal. The pivot will be positioned at the first vertex of the first edge of the face; the scope will be the bounding box of the face, i.e.the z-dimension of the emerging shape's scope is set to zero. See an example.

  • e

    In the case of an edge component split, the x-axes of the pivot an the scope are along the edge and the z-axes point along the average of the normals of the neighboring faces. The y- and z-dimension of the scope are set to zero and the x-dimension is the length of the edge. The pivot will be positioned at one of the endpoints of the edge. The indexing of edges is as follows: index 0 is the first edge of the first face, index 1 the second edge of the first face etc. Shared edges are skipped on second encounter. See an example.

  • fe

    In contrast to edges, face edges are considered separate for each face. Similarly, indexing is done firstly per face and secondly per edge, but shared edges are not skipped. Furthermore, contrary to edges, face edges have a unique direction determined by the orientation of the face they belong to. The x-axes of the pivot and the scope are directed in face orientation and the z-axes point out of the face orthogonally to the edge and the face normal. The pivot is positioned at the first vertex of the face edge. See an example.

  • v

    In case of a vertex component split, the pivot is positioned at the vertex, the z-axes will point along the average of the normals of the neighboring faces and all scope dimensions are set to zero.

Comp shape attributes

Each generated shape has a number of comp shape attributes set, see comp attribute.

Trim planes

Additionally, for faces, the component split generates trim planes. Trim planes are placed along the shared edges of the new faces in a bisecting angle. The purpose of trim planes is twofold. On one side, trimming handles geometry intersections on the boundary of two neighboring faces, and on the other side, trimming is used to handle non-rectangular faces. The trim planes are applied using the insert operation, primitive operations, or the trim operation.

According to the direction of the shared edge, trim planes are classified into horizontal and vertical planes. They can be switched on or off by setting the trim attribute to true or false. By default, trimming is activated for vertical trim planes and deactivated for horizontal trim planes. Check the trim planes examples below.

Note:

Occluder shape

An occluder shape is automatically generated before the component split is applied. The occluder shape is made available for unlabeled occlusion queries.

Roof edges

There is a number of selectors which are designed to classify typical roof edges:

  • eave—Horizontal border edges on the bottom of the roof. The edges are always oriented anti-clockwise around the original face.
  • hip—Inside edges connected to at least one eave edge. Hip edges are always oriented upwards, i.e. the ending point has larger y-coordinate than the starting point.
  • valley—Inside edges where the two connected faces form a concavity. Valley edges are always oriented upwards, i.e. the ending point has larger y-coordinate than the starting point.
  • ridge—Inside edges which are not hip or valley. Ridge edges are always oriented upwards, i.e. the ending point has larger y-coordinate than the starting point.

These selectors can only be applied on the edge component splits.

The figure below shows examples.

Roof edges

Street selectors

Components adjacent to a street can be selected with the street.front selector, rear components can be selected with the street.back selector, and components in between front and back can be selected with the street.left and street.right selectors. street.side combines left and right components. Below are examples of the street.xxx selectors.

Street selector 1
Street selector 2

These selectors depend on the availability of the streetWidth attribute map; see Auto-generated street width attributes. If the attribute is not available, component selection falls back to theobject.xxx selectors.

Related

Examples

Facade selection / face split details

Let us split the mass model of a building into the main facade and a number of side facades. Note the orientation of the pivot (the annotated axes).

Mass model before split
Building-->	
   comp(f) { front : color("#ff0000") Main.
           | side  : color("#0000ff") Side.
           }
Applying split

Each face is now the geometry of a new shape; the new shapes' scopes and pivots depend on the faces' orientation. The x-axis points along the first edge and the z-axis points along the face normal. The scope's z-dimension is zero.

Each face is geometry of new shape

Selectors 1: Quadrant-based

Selectors are demonstrated by using them to color the faces of a spherical geometry. The selection is relative to the local coordinate system (the shown scope).

Sphere-->
   comp(f) { top    : color("#0000ff") X 
           | bottom : color("#ffff00") X 
           | front  : color("#ff0000") X 
           | back   : color("#ff00ff") X 
           | left   : color("#00ffff") X 
           | right  : color("#00ff00") X }
Selector quadrant-based

Selectors 2: Angle to y-axis based

Note the horizontal areas (blue) on the sphere's poles.

Sphere -->
    comp(f){ horizontal: color("#0000ff") X 
           | aslant    : color("#ff0000") X 
           | vertical  : color("#ffff00") X 
           | nutant    : color("#ff00ff") X }
Selector angle to y-axis based

Logical selection expressions

Static selectors can be used in a logical selector expression. Here both the top and front faces are combined into a single shape.

Sphere --> comp(f) { top || front = X. }
Top and front faces combined into a single shape

Here, only faces with a surface area greater than 1 and that are not inside are selected.

Triangle -->
   comp(f) { geometry.area() > 1 && !inside : X. }
Faces with a surface area greater than 1 selected

Here, a custom function is used to select the top faces and all faces with exactly 3 vertices.

mySelectorFunc = 
    geometry.isOriented(top) || geometry.nVertices==3

Sphere -->
   comp(f) { mySelectorFunc : X. }
Select top faces and all faces with exactly 3 vertices

Index-based selection

A mesh can also be disassembled into its components by addressing them directly by their index. The indexing scheme is inherently encoded in the model itself.

Mesh before disassembly

Only faces 0, 2 and 4 of the cylinder are selected.

Tube-->
   comp(f) { 0 : X 
           | 2 : X 
           | 4 : X  }
Mesh with selected faces

Trim planes

At shared edges, trim planes (green) are inserted.

Start-->
   s(10,10,10)
   primitiveCube()
   comp(f) {5 : X}
Trim planes with shared edges

Inserted geometry is cut with the trim planes.

X-->
   s(15,'1, 2)
   center(xyz)
   primitiveCube()
Trim planes cut geometry

By default, horizontal trim planes are off. Enabling them before inserting the geometry gives a different result.

Start-->
   s(10,10,10)
   primitiveCube()
   set(trim.horizontal, true)
   comp(f) {5 : X}
Trim planes with horizontals enabled

The operator

Using the ":" operator results in a new shape for each component selected by the selector. In the example below, a shape is created for each side of the extruded geometry. Therefore, the Lot Shape has five successors (one for each side). Each successor shape has its pivot and scope set up differently.

Lot-->
   extrude(20)
   comp(f) { side : Sides }
Operator with five new shapes

In contrast, using the "=" operator results in exactly one new shape for all component selected by the selector. In the example below, one shape is created for all five sides of the extruded geometry. Thje new shape's geometry contains all five faces, and the pivot and scope are set up relative to the first selected face.

Lot-->
   extrude(20)
   comp(f) { side = Sides }
Operator with combined shapes

Border and inside selectors

The picture shows the initial shape. It is a subdivided plane, consisting of a number of faces.

Initials shape with subdivided plane

The example selects the border and inside faces and colors them.

Init-->
   comp(f) { border : FBorder 
           | inside : FInside  }
   
FBorder-->
   color("#ff0000")
	
FInside-->
   color("#00ff00")
Shape with border and faces colored

Here, there border and inside edges are selected and colored cubes are inserted.

 Init-->
   comp(e) { border : EBorder 
           | inside : EInside  }

EBorder-->
   s('1, 0.05, 0.05) 
   t(0, '-0.5, 0)
   color("#ff0000") 
   primitiveCube()
	
EInside-->
   s('1, 0.01, 0.01) 
   t(0, '-0.5, 0)
   color("#00ff00") 
   primitiveCube()
Shape with colored cubes inserted

Finally, the border and inside vertices are used to insert colored cubes.

Init-->
   comp(v) { border : VBorder 
           | inside : VInside  }
   
VBorder--> 
   s(0.05, 0.05, 0.05) 
   t(-0.025, -0.025, -0.025)
   color("#ff0000") 
   primitiveCube()
   
VInside--> 
    s(0.05, 0.05, 0.05) 
    t(-0.025, -0.025, -0.025)
    color("#00ff00")
    primitiveCube()
Shape with colored cubes inserted by vertices

isTagged selector

This example colors the face components based on the face tags applied by the envelope operation.

Init --> envelope(normal,4, 0,45, 3,45, 2.5,50, 2,50)
         ShowEnvelopeAutoTags
         
ShowEnvelopeAutoTags -->
    comp(f) { isTagged("envelope.bottom")     : Blue 
            | isTagged("envelope.side.base")  : Yellow
            | isTagged("envelope.side.slope") : Orange
            | isTagged("envelope.side.inner") : Red
            | isTagged("envelope.top")        : Green }

isTagged with face tags

Here, the tag query "envelope.side" is used to select and color all base, slope and inner side faces at once.

Init --> envelope(normal,4, 0,45, 3,45, 2.5,50, 2,50)
         comp(f) { isTagged("envelope.side") : Yellow }
isTagged with tag query

Here, the edges of the face tagged "envelope.top" are selected and colored cubes are inserted.

Init --> envelope(normal,4, 0,45, 3,45, 2.5,50, 2,50)
         comp(e) { isTagged("envelope.top") : Edge }
         
Edge --> s('1, 0.5, 0.5) center(yz)
         color("#09de1f") primitiveCube()
isTagged edges

Here, the vertices of the face tagged "envelope.top" are selected and colored spheres are inserted.

Init --> envelope(normal,4, 0,45, 3,45, 2.5,50, 2,50)
         comp(v) { isTagged("envelope.top") : Edge }
         
Edge --> s(0.5, 0.5, 0.5) center(xyz)
         color("#09de1f") primitiveSphere(8,6)
isTagged vertices

Note:

By default, edges can be selected based on the tags of adjacent faces, while vertices can be selected based on the tags of adjacent faces and edges. However, to select faces based on the tags of their edges or vertices, the subcomponent parameter must be provided, as demonstrated in the section below.

Advanced Tag selection

By using the e subcomponent selector, all faces that share an edge with a face tagged "envelope.bottom" or have an edge tagged as such, are selected.

Init --> envelope(normal,4, 0,45, 3,45, 2.5,50, 2,50)
         comp(f) { isTagged("envelope.bottom", e) 
            : ShowEnvelopeAutoTags }

Note that the face tagged "envelope.bottom" (blue) is selected as well.

subcomponent selector

This example selects all slope faces that are edge-adjacent to the bottom face.

Init --> envelope(normal,4, 0,45, 3,45, 2.5,50, 2,50)
         comp(f) { isTagged("envelope.bottom", e) &&
                   isTagged("envelope.side.slope") 
            : ShowEnvelopeAutoTags }
Slope faces selected

This example selects all edges that share a vertex with the bottom face and inserts colored cubes.

 Init --> envelope(normal,4, 0,45, 3,45, 2.5,50, 2,50)
         comp(e) { isTagged("envelope.bottom", v)
            : Edge }
            
Edge --> s('1, 0.5, 0.5) center(yz)
         color("#09de1f") primitiveCube()
Selects all edges with shared vertices colored cubes

Edge split details

A building mass model is split into its edges, and the built-in cube model is inserted into each edge shape. The pivot of the new shape is set to the edge's start vertex, and the alignment is as follows: the x-axis points along the edge, the z-axis is the average of the neighbouring face normals and the y-axis is normal to the two former ones. The scope has zero translation and rotation, and the sizes are (edge-length, 0, 0).

Lot --> extrude(3) 
        comp(e) { all : primitiveCube()
        s('1, 0.25, 1) X. }
Mass model edge split

A split into face edges creates more components, for each face the edges are considered separately. Edges are oriented wrt. face orientations. The pivot is now set to the edge's start vertex and the x-axis points in face orientation, the z-axis is pointing out of the face orthogonally to the edge and the face normal.

Lot --> extrude(3) 
        comp(fe) { all : primitiveCube() 
        s('1, 0.25, 1) X. }
Mass model face edge split

Vertex split details

A building mass model is split into its vertices. The pivot of the new shape (the VShapes in the rule above) is set to the vertex position, and the alignment is as follows: the z-axis is the average of the neighbouring face normals and the x- and y-axes are chosen such that they are all normal to each other. The scope has zero translation, rotation and size.

Lot-->
   extrude(10) 
   MassModel
   comp(v) { all : VShapes }
Mass model with vertex split

Group split details

A tree model is split into its groups.

Lot-->
   i("Orange_Tree_Model_0.obj") 
   comp(g) { findFirst(geometry.materials,"Bark") : Trunk.
           | all = set(material.opacity, 0.2) Foliage. }
Tree model group split

Material split details

A tree model is split into its materials. In this case groups and materials are the same.

Lot-->
   i("Orange_Tree_Model_0.obj") 
   comp(m) { findFirst(geometry.materials,"Foliage") : Oranges. 
           | findFirst(geometry.materials,"Bark")    : Trunk. }
Tree model material split