π§© The schema
πΈ Before you startβ
Before reading this page, keep in mind that we've built a whole JavaScript / TypeScript tech stack on top of Fragments to make the heavy lifting for you. If you just want to build BIM software with our libraries, you can happily treat Fragments as a black box without worrying about its internal structure. π¦
If you want to build your custom importers / exporters, want to build Fragments custom tools in another programming languages or are just curious about it, go ahead! π«‘
πΎ Introductionβ
Fragments is a format built on top of Flatbuffers, an open source libraries to easily create binary formats that are compatible with any programming language. π
The way flatbuffers work is simple:
-
βπ» We create a schema. It's just a text file containing description of the data structures inside your file. The syntax is very simple and similar to C. The extension is .fbs (stands for flatbuffers schema). You can out the fragments schema below.
-
π₯ You use that schema file and the flatbuffers library to automatically create an importer / exporter of that file in any programming language. This is covered in the flatbuffers docs. We already did it for TypeScript/JavaScript and included it in our libraries, so you don't have to worry about that one! π
Flatbuffers is extensible, so even if we make changes (evolutions) to the schema in the future, it will be backwards compatible! πβΆοΈπ¦
This is the schema file we created for fragments. Don't worry, we will cover it piece by piece in this page!
π Check it outβ
Before going in detail into each piece of the Fragments schema, let's check out a minimal Fragments file containing just a simple wall. In this example, you'll be able to see its data following the schema above, which might be useful for understanding how the schema works. You can even load your own IFC STEP files too to see their Fragments schema! π
Keep in this example we are serializing ALL the data of the file to a JSON just to display it in the screen, which is very, very inneficient, beating Fragments performance benefits. Don't expect this demo to have the same performance as a production Fragmetns app. Avoid loading huge IFCs here if you don't want to see your browser freeze. ππ»
You might want to revisit this example as a reference when trying to generate your own fragment files from IFC STEP or other data sources to have a reference of how it should look like: ππ»
βπ» General notesβ
There are some decisions we've taken when definining the Fragments data schema whose motivation might not be obvious at first sight. We will cover them here.
π± Data structuresβ
In some parts of the schema you might find data structured in a specific way. They are not arbitrary: it's the way to be able to open gigabytes of BIM data in seconds. Keep in mind that we designed Fragments for performance, so every indirection decision that you see follows that purpose. π
πππ String arraysβ
You will see that in various places we are using string arrays (categories, attributes, relations, etc). This might seem inefficient memory-wise at first sight, as when we have an array with multiple strings, each string occupies size, regardless of whether it's duplicated or not. π€
However, flatbuffers allow to define unique strings, automatically deduplicating its size. So this array is as efficient as an array with unique strings and an index to relate them. π±
π§© Modelβ
The Model is the main object and the entry point of all Fragment files. Each Fragments file has just one model, and it contains all the information of the file. It has the following structure (we'll cover it step by step): ππ»
table Model {
metadata: string; // JSON string for generic data about the file
guids: [string] (required); // An array of Global Unique Identifiers of items. Not all items may have a guid.
guids_items: [uint] (required); // An array that works as an indexation matching localIds indices with guids.
max_local_id: uint; // The smallest localID available when serializing. Used to know the next localID when adding a new item.
local_ids: [uint] (required); // File specific identification for each item.
categories: [string] (required); // An array of all item categories found in the file, stored as strings.
meshes: Meshes (required); // The object containing all explicit geometries of the model.
unique_attributes: [string]; // An array of unique item attributes in this model.
attributes: [Attribute]; // An array of items data stored as an array of arrays.
relation_names: [string]; // An array of unique relation names in this model.
relations: [Relation]; // An array of relations between different items stored as arrays of arrays.
relations_items: [int]; // An array that works as an indexation matching localIds indices with relations.
guid: string (required); // An global ID that identifies this model uniquely.
spatial_structure: SpatialStructure; // A tree representing the spatial relation between elements.
alignments: [Alignment]; // A set of civil alignments for this model
geometries: Geometries; // The object containing all implicit geometries of the model.
}
π Metadataβ
JSON string for generic data about the file itself. For example: {"schema":"IFC4"}". πππ
π¦ Guidsβ
An array of Global Unique Identifiers of items. Not all items may have a guid. They should be consistent across exports from authoring applications. πͺ¨
π¦β‘οΈβοΈ Guids Itemsβ
An array that works as an indexation matching localIds indices with guids. For instance, a Fragments file with the following data: ππ»
{
"localIds": [34, 35, 36],
"guids": ["guid-abc", "guid-xyz"],
"guidsItems": [2, 1],
...
}
π§ Means that there are 3 items:
- The first one has localId
34and no guid. - The second one has localId
35and guidguid-xyz. - The third one has localId
36and guidguid-abc.
π₯π Max Local Idβ
The biggest localID found when serializing plus one. Used to know the next local id available when adding a new item. For example, if when loading an IFC STEP file the item with the highest express id (local id) was 34928, then the max local id will be 34929. ποΈ
π Local Idsβ
File specific identification for each item. They might vary with each export from authoring applications. All items must have a local id. If you are exporting Fragments from a data source that doesn't have local ids, you can just use an incremental uint starting at 0. π’
You may have noticed that fragments support both global ids (guids) and local ids. The main reason is that global ids are heavy, so if each item had its own global id, the file size would be huge. This also happens in IFC: each item has a local id (express id), and only some items have a global id. π
𧬠Categoriesβ
An array of all item categories (arbitrary strings used to classify items) found in the file. Categories are arbitrary, but all items must have a category. For instance, a Fragments file with the following data: ππ»
{
"localIds": [34, 35, 36],
"categories": ["WALL", "SLAB", "WALL"],
...
}
π§ Means that there are 3 items:
- The first one has localId
34and categoryWALL. - The second one has localId
35and categorySLAB. - The third one has localId
36and categoryWALL.