tomvandig / eccg

Entity Component Composition Graph
2 stars 3 forks source link

Explicit geometry tree, with semantic overlay graph #2

Open daviddekoning opened 7 months ago

daviddekoning commented 7 months ago

Hi @tomvandig and @aothms,

(cc @berlotti, @gschleusner1972)

Following discussions at the buildingSMART meeting in Chicago last week, and some research I have been doing into other similar formats, I would like to name 5 types or combinations of graphs that can be used to organize a model and then discuss the tradeoffs between explicit geometry vs semantic definitions.

Each of the components I describe below should be understood as something that can only be applied to an entity once. For example, it would be an error if two placement components are attached to a single entity.

In the below, I am not addressing how a model is composed from multiple authors, nor how library entities (Revit-style types) are inserted into a model. This is just a discussion of how a composed model is structured.

My apologies for dropping a wall of text here, it seems to be the right place for this discussion!

Classes of model graphs

Placement Tree / Scenegraph

A placement tree is one where all entities in a model have:

  1. A single parent
  2. An optional transformation / offset
  3. An optional geometry

The geometry must be explicitly defined (no information needed from anywhere else in the graph).

The geometry is placed as per the product of all it's parents' (parent, grandparent, great-grandparent, ...) transforms

This means:

  1. The geometry of the model can be determined by simply traversing the tree while keeping track of the cumulative transforms.
  2. The geometry of the model is extremely unambiguous, and can be efficiently rendered.

In an Entity-component world, this can be achieved by defining two kinds of components:

  1. Placement: a component that has a reference to the entity's parent, and an optional transform
  2. Geometry: a component that contains the geometry of the object.

Placement Tree:

flowchart LR
  Wall --> w1["Window1(WindowType)"]
  Wall --> w2["Window2(WindowType)"]
  WindowType --> Frame
  WindowType --> Glazing

Composed Placement Tree:

flowchart LR
    Wall --> Window1
    Wall --> Window2
    Window1 -->F1[Frame]
    Window1 --> G1[Glazing]
    Window2 --> F2[Frame]
    Window2 --> G2[Glazing]

The placement graph can be determined by only looking at the placement components in the model.

You will have no doubt noticed that there is no semantic information in this type of graph...

Semantic Overlay on a Placement Graph

One approach include semantic information on a model is to maintain all the requirements of a Placement Tree and simply tag certain entities as assemblies or classes.

For instance, a new component called AssemblyInfo can be defined, with two parameters:

  1. kind: "assembly", "component" or "sub-component"
  2. name: a unique or user-facing name for the assembly

This maintains all the benefits of the Placement Tree, but allows us to give the important entities names and hide non-assembly entities in a simplified tree view.

This is the approach USD takes: they call their entities Prims, each Prim is transformed as per the product of its parent transforms. A subset of the Prims are tagged and organized into a Model Hierarchy. They also require that all Prims tagged as assemblies must be children of assemblies - the semantic overlay covers a contiguous set of entities that includes the root node.

Placement Tree with Semantic Overlay:

flowchart LR
  Wall["Wall\n{Assembly}"] --> w1["Window1(WindowType)"]
  Wall --> w2["Window2(WindowType)"]
  WindowType["WindowType\n{Assembly}"] --> Frame
  WindowType --> Glazing

Composed Placement Tree with Semantic Overlay:

flowchart LR
  Wall["Wall\n{Assembly}"] --> w1["Window1\n{Assembly}"]
  Wall --> w2["Window2\n{Assembly}"]
  w1 --> f1[Frame]
  w1 --> g1[Glazing]
  w2 --> f2[Frame]
  w2 --> g2[Glazing]

Semantic Tree

Another option is to have two trees over the same entities: a placement tree and a semantic tree. The placement tree is as above, and the semantic tree is a completely separate organization. In this case, we define a component called "AssemblyInfo" a little bit differently. It will have three properties:

  1. parent: the parent assembly of the entity
  2. kind: "assembly", "component" or "sub-component"
  3. name: a unique or user-facing name for the assembly
flowchart LR
  Wall["Wall\n{Assembly}"] --> w1["Window1\n{Assembly}"]
  Wall --> w2["Window2\n{Assembly}"]
  w1 --> f1[Frame]
  w1 --> g1[Glazing]
  w2 --> f2[Frame]
  w2 --> g2[Glazing]

Placement tree with all entities in global coordinates, which has no effect on the assembly hierarchy

flowchart LR
  g["Global Axis"] --> Wall
  g --> Window1
  g --> Window2
  g --> Glazing1
  g --> Frame1
  g --> Glazing2
  g --> Frame2

A few observations:

  1. This seems unweildy. On the other hand, this is how Blender organizes it's scenes. Every object can be in a collection and parented to another object. Blender parenting creates a placement tree, and the Blender collection hierarchy creates a semantic tree. The blender outliner creates 'ghost' objects when the two hierarchies do not align.
  2. There can be more than 2 hierarchies. Any hierarchy can be created by defining new kinds of components. Each tree can be assembled by simply reading the relevant components.
  3. The semantic tree is still a tree, not a graph!!

Parametric / Procedural Graph

In all three options above, the geometry is defined explicitly, and there is a hierarchy of transforms to place that geometry. But this is not how BIM software typically works. Instead of geometry being drawing, the user is presented with a number of parameters to select, many of which are other entities in the model. The BIM software then computes the geometry.

For example, a wall in Revit has 4 basic parameters: thickness (from the type), and plan layout, lower level and upper level (from the instance). The lower level and upper level are references to other entities in the model.

There are two major differences in this approach versus the first three:

  1. The geometry is calculated from the semantics. This is the parametric / procedural part.
  2. There are multiple parents. In this case, instead of there being one parent, there two levels are parents. We no longer have a tree, but a directed acyclic (I hope!) graph.
flowchart LR
  l1["Level 1"] --> Wall
  l2["Level 3"] --> Wall
  Wall --> w1["Window1(WindowType)"]
  Wall --> w2["Window2(WindowType)"]
  WindowType --> Frame
  WindowType --> Glazing

In these cases, the semantic graph is used to the compute the geometry, and the geometry dependencies are often hidden from the user (in authoring applications).

There are two big points that come from the approach of computing geometry from semantics:

  1. We must have a graph, not a tree. Many entities are defined as 'between entity a and entity b'.
  2. It is much harder to agree on semantics than it is to agree on a geometry format.

Placement Tree with Semantic Graph

Another option is to require that placement and geometric information be placed in a tree structure, but allow the semantic overlay to form a graph. This allows the model snapshot to be efficiently loaded and rendered, while the semantic properties of specific entities can be lazy loaded as required.

We can call this option explicit geometry tree, with semantic overlay graph .

Implications for interoperability

Should IFC5 define geometry explicitly, or be a semantic standard from which geometry is computed? Another way to ask the question is, should we require IFC5 exporters to render out geometry, or should we require IFC5 importers to be able to compute geometry from all the semantics that IFC5 defines?

image

There is a lot to be said for an explicit geometry tree, with semantic overlay graph approach. This would require that all IFC readers be able to read a specific set of geometric types. Semantic information could be overlaid, and IFC readers that are able to understand those semantics could make use of them. The semantic overlay graph becomes an enrichment of the base data, not a requirement to be able to ingest the file in the first place.

For example, a model with a road alignment would be exported as a series of curves in cartesian space (from OpenRoad or Civil3D). The alignment information would also be written. It could then be imported into Revit or Tekla Structures, without those tools needing to perform any math on the alignment, but if it was imported into a tool that did include support for alignments, it could grab the alignment info (and check against the rendered geometry).

aothms commented 7 months ago

Very interesting. Thanks for writing this down!

Should IFC5 define geometry explicitly, or be a semantic standard from which geometry is computed

Probably both. In previous meeting we had settled on a layered approach. 1 being explicit triangulations. 2 being the 2x3 coordination view equivalent with extrusions and some voids. 3 being full parametric constraint based geometry. We also came to the conclusion that when exchanging 2 geometry, 1* should also be included for interoperability and for assessing correctness of import. I think this kind of thinking is still valid in the ECS approach, but is has not been explicitly validated recently.

The other question is whether we should work towards equation- or schema- based parametrics. In previous investigations I did a sketch of the latter [0]. I.e by reducing the dimensionality of the representation, from solid to infinite line, but annotating the connection points to other walls using semantic relationships the viewers knows where to trim the line (as you said, thickness comes from the material) and the vertical extent of the wall can as you said be defined using the same kind of trimming relationship with storeys or other planes. The downside of this approach is that it is rather hard to make it explicit and computer interpretable it remains mostly logical connections. I also feel that this kind of "fragmented" definitions are a little bit harder to realize in the ECS approach.

[0] https://speakerdeck.com/aothms/ifc5-adequate-complexity-maximum-reliability?slide=11

Other than that I don't fully comprehend the differences and implications behind the options you have sketched. I hope we can find the time to talk this true in a dedicated meeting in the near future. One question seems to be how much special types we want to accomodate for in the datamodel, the models with special behaviour we tend to gravitate around are: hierarchy/assembly and references/archetyping, should these be ordinary components in the end or are they special enough to warrant a dedicated type in the exchange?