- Introduction
- Import
- Export
Introduction
This guide is for developers who are interested in customizing the CET Abstract IFC interfaces for importing and exporting IFC 2x3 files. You should have a basic understanding of the IFC standard, and some entity types required. A good reference is IFC documentation.
Overview
The API v2 for the IFC Interface revolves around several use cases:
- Import: Replace IFC Objects with native snappers– such as replacing a generic IFC Furniture into actual custom worksurface snapper.
- Export: Append additional properties – such as Parts and Dimensions using Property Sets and Quantity Sets.
- Export: Customize exported object type – such as if you want a HVAC to be properly classified as a Duct, instead of the generic FurnishingElement.
- Export: Logical grouping of objects – such as objects that contains a set of objects.
- Export: Customizing the shape representation – such as using Solids instead of generic meshes.
The IFC Base codes are stored in cm.abstract.ifc, with 2 sub-packages, cm.abstract.ifc.import and cm.abstract.ifc.export, that handles import and export respectively. There is also a pset directory that contains a list of property and quantity set data schema XMLs.
IFC Class Structure
The CET IFC Object structure starts with the base class IfcExpressObject
, which has 3 sub-classes, namely IfcExpressDefinedType
, IfcExpressEntity
and IfcExpressSelectType
.
IfcExpressObject -- cm.abstract.ifc |---IfcExpressDefinedType | |---+ |---IfcExpressEntity | |---+ |---IfcExpressSelectType | |---+
IfcExpressDefinedType
serves as the base class for different IFC Types like IfcBoolean, IfcLengthMeasure, etc., while IfcExpressEntity
serves as the base class for IFC Entities such as IfcProductRepresentation, IfcProperty, etc.
On the other hand, IfcExpressSelectType
serves as the base class for IFC selector classes that accept values from a predefined domain of classes. For example, a user could assign IfcAxis2Placement2D, or IfcAxis2Placement3D to IfcAxis2Placement, depending on whether the context is 2D or 3D.
Please note that IFC-related classes that are only defined in CET/CM start with the prefix 'Ifc', while IFC classes that originate from the IFC schema start with the prefix 'Exp'. For example, the CET class for IfcProductRepresentation is ExpIfcProductRepresentation
.
Import
Customizing Imported Products
By default, IfcObject in an IFC drawing is imported as an IfcSymbol
in CET, which is a generic snapper that stores IFC graphics and metadata.
However, it is possible to parse an IfcObject and retrieve metadata from it, and replace the IfcObject with a snapper from your extension/customization using a product replacement hook. The example below registers a hook that import IfcFlowSegment as MechaStraightSnapper
in the extension.cm file of the HVAC extension. A simplified version of the function that replaces an IfcFlowSegment with a MechaStraightSnapper
is also given in the example below.
// PipeExtension class public void start(ExtensionEnv env) { ifcRegisterProductReplacementHook("CetIfcHvacImport", function hvacProductReplacementHook); super(env); } // PipeExtension class public void stop(ExtensionEnv env) { ifcUnregisterProductReplacementHook("CetIfcHvacImport"); super(env); } public Snapper[] hvacProductReplacementHook(IfcProjectTreeNode productNode) { if (?ExpIfcFlowSegment flowSegment = productNode.owner) { if (getFlowSegmentType(flowSegment) in ExpIfcDuctSegmentType) { Snapper[] snappers(); MechaStraightSnapper mechaStraightSnapper(); // TODO: Update the dimensions and other properties of the Snapper. snapper s << mechaStraightSnapper; return snappers; } } return null; }
Customizing Imported Metadata
In IFC, each IfcObject has the ‘Name’ and ‘Description’ attributes, which could be assigned to the relevant fields of a Snapper. In addition, an IfcObject can have properties data defined using IfcPropertySet, which are associated to the IFCObject using IfcRelDefinedByProperties. Hence, it is possible to retrieve the relevant properties, and assign the values to the corresponding fields of a Snapper.
Property Set
IFC provides a list of Property Set Definition (PSD) schema, which describes properties that are specific for certain IfcObject classes. For example, the definition ‘Pset_DuctFittingTypeCommon’ is applicable to the IFC entity IfcDuctFittingType, and contains property fields like ‘NominalHeight’, ‘NominalDiameterOrWidth’, ‘Length, etc, to better describe a duct.
The property set definitions are defined in the form of .xml files, and the .xml files are stored in the folder ‘cm/abstract/ifc/pset/ifc2x3/’.
We could retrieve the relevant properties from the property sets, and assign the values to the corresponding fields of the relevant Snapper. In the example below, we retrieve the ‘Length’ from the ‘Pset_DuctSegmentTypeCommon’ property set of an IfcflowSegment and assign the value to the ‘length’ field of a MechaStraightSnapper.
public Snapper[] hvacProductReplacementHook(IfcProjectTreeNode productNode) { if (?ExpIfcFlowSegment flowSegment = productNode.owner) { if (getFlowSegmentType(flowSegment) in ExpIfcDuctSegmentType) { Snapper[] snappers(); MechaStraightSnapper mechaStraightSnapper(); // Get the length from the property set. if (IfcPropertiesObject propObject = productNode.getData("Pset_DuctSegmentTypeCommon")) { if (?Double length = propObject.get("Length")) { mechaStraightSnapper.length = length.v; } } snappers << mechaStraightSnapper; return snappers; } } return null; }
Custom Property Set Definitions
In addition to the property sets that are defined in the Property Set Definition (PSD) schema, it is also possible to import custom property sets that are not defined in the PSD schema. In order to do so, we would first need to define the custom property sets in CET, as shown below.
public const str ifcKeyPset_CETData = "Pset_CETData"; public const str ifcKeyPset_CETHvacData = "Pset_CETHvacData"; private const str{} cetDefinedSchemas = { ifcKeyPset_CETData, ifcKeyPset_CETHvacData }; public class Pset_CETDataDefs extends IfcPropertiesDefs { public constructor() { this.name = ifcKeyPset_CETData; this.schemaVersion = ifcFileSchema.ifc2x3; this.put(IfcPsetPropertySingleValueDef(k="PartList", pType=Str, expIfcType=ExpIfcLabel)); } } public class Pset_CETHvacDataDefs extends Pset_CETDataDefs { public constructor() { super(); this.name = ifcKeyPset_CETHvacData; this.put(IfcPsetPropertySingleValueDef(k="IsOpenEnded", pType=Bool, expIfcType=ExpIfcBoolean)); } } private IfcPropertiesDefs createCetDefinedSchemas(str key) { if (key == ifcKeyPset_CETData) { return Pset_CETDataDefs(); } else if (key == ifcKeyPset_CETHvacData) { return Pset_CETHvacDataDefs(); } return null; }
Then, we could retrieve the relevant properties from the custom property sets and assign the values to the corresponding fields of the relevant Snapper. In the following example, we retrieve the property ‘IsOpenEnded’ from the custom property set ‘Pset_CETHvacData’ and assign the value to the field ‘openEnded’ of MechaStraightSnapper.
public Snapper[] hvacProductReplacementHook(IfcProjectTreeNode productNode) { if (?ExpIfcFlowSegment flowSegment = productNode.owner) { if (getFlowSegmentType(flowSegment) in ExpIfcDuctSegmentType) { Snapper[] snappers(); MechaStraightSnapper mechaStraightSnapper(); // Get the property from the property set. if (IfcPropertiesObject propObject = productNode.getData(ifcKeyPset_CETHvacData)) { if (?Bool isOpenEnded = propObject.get("IsOpenEnded")) { mechaStraightSnapper.openEnded = isOpenEnded.v; } } snappers << mechaStraightSnapper; return snappers; } } return null; }
Customizing Object Aggregations
In IFC, it is possible to define a composition or decomposition relationship between IfcObjectDefinition using IfcRelAggregates, where the main object is defined as an aggregation of the parts. For example, a roof can be defined as an aggregation of roof elements like roof slabs, rafters, etc.
While processing certain IfcObject, we might want to get some properties or data from the disaggregated child IfcObject. For this purpose, we could use the following syntax to get the relevant child IfcOject.
IfcProjectTreeNode[] childNodes = ifcProjectTreeNode.get(isDecomposedBy); for (childNode in childNodes) { if (?ExpIfcProduct ifcProduct = childNode.owner) { //TODO: process child nodes. } }
Export
Customizing Export
When CET exports a drawing as IFC, we invoke a factory class for each snapper in space to generate the required IFC entities.
The basic structure of IfcExportFactory is shown below:
IfcExportFactory -- cm.abstract.ifc.export |---IfcExportCoreObjectFactory | |---IfcExportContainedVesselFactory -- cm.abstract.ifc | |---IfcExportSnapperFactory -- cm.abstract.ifc.export | | |---+
IFC Base comes with a generic factory (IfcExportSnapperFactory) out of the box, that handles graphics and basic parts data export. However, if you wish to customize the export, you can subclass the IfcExportSnapperFactory and implement your own logic.
Customizing Factory Hook Registration
After sub-classing from IfcExportSnapperFactory, we will need to register the new factory into the export factory hook in order to associate your Snapper to the corresponding export factory. Use the function 'registerIfcExportFactoryHook' to register a new hook for a new export factory sub-class. We recommend registering the hook in the 'start' method of an extension, and unregistering when the extension is turned off.
// PipeExtension class public void start(ExtensionEnv env) { registerIfcExportFactoryHook("CetIfcHvacExp", function cetHvacExportHook); super(env); }
public void registerIfcExportFactoryHook(str key, function(Snapper, IfcExportEnvG2):IfcExportFactory hook) { if (developMode) pln("[IFC] Registered IFC Export Factory Hook: ", #key); _hooks.put(key, hook); } public IfcExportFactory cetHvacExportHook(Snapper s, IfcExportEnvG2 env) { if (s as MechaStraightSnapper) return HvacSegmentIfcExportFactory(s, env); else if (s in MechaJunction) return HvacFittingIfcExportFactory(s, env); return null; }
Customizing Property Exports
In IFC, an IfcObject can have properties data defined using IfcPropertySet, and they are associated using IfcRelDefinedByProperties. As such, one possible use case with our API is to export the metadata of a Snapper (such as dimensions, name, parts, manufacturer data, etc) as an IfcProperty.
Property Set
IFC also provides a list of Property Set Definition (PSD) schema, which describes properties that are specific for certain IfcObject classes. For example, the definition 'Pset_DuctFittingTypeCommon' is applicable to the IFC entity IfcDuctFittingType, and contains property fields like 'NominalHeight', 'NominalDiameterOrWidth', 'Material', etc, to better describe a duct.
The property set definitions are defined in the form of .xml files, and the .xml files are stored in the folder 'cm/abstract/ifc/pset/ifc2x3/'.
IfcPropertiesObject works in a similar manner as PropObj, where IfcPropertiesDefs provides the definition of the data to be exported, while IfcPropertiesData stores the actual data.
In this example, we create an IfcPropertiesData (a sub-class of IfcPropertiesObject) that employs the IFC PSD schema ‘Pset_FlowSegmentDuctSegment’ and assign a value to the ‘Length’ property field. Then, we would override the method IfcExportCoreObjectFactory.getPropertySetDatas().
// HvacSegmentIfcExportFactory class public IfcPropertiesObject[] getPropertySetDatas() { IfcPropertiesObject[] ps = super(); IfcPropertiesDefs propDefs = getPsetDefinition("Pset_FlowSegmentDuctSegment"); if (IfcPropertiesData pset = IfcPropertiesData(propDefs)) { pset."Length" = length; ps << pset; } return ps; }
Quantity Set
In addition to IfcPropertySet, properties could also be exported using IfcElementQuantity, which describes the physical quantities of an element, by overriding the corresponding method IfcExportCoreObjectFactory.getQuantitySetDatas().
// IfcExportWallFactory class public IfcPropertiesObject[] getQuantitySetDatas() { IfcPropertiesObject[] props = super(); box b = snapper.?localBound(); IfcPropertiesDefs propDefs = getPsetDefinition(ifcKeyQset_WallBaseQuantities); IfcPropertiesData qs(propDefs); qs."Width" = b.w; qs."Height" = b.h; props << qs; return props; }
Custom Property Set Definitions
Besides, it is also possible to define our own IfcPropertiesDefs in CET, as shown below. In this case, let us define a new set of property definitions for HVAC.
Public class Pset_CETDataDefs extends IfcPropertiesDefs { public constructor() { this.name = ifcKeyPset_CETData; this.schemaVersion = ifcFileSchema.ifc2x3; this.put(IfcPsetPropertySingleValueDef(k="PartList", pType=Str, expIfcType=ExpIfcLabel)); } } public class Pset_CETHvacDataDefs extends Pset_CETDataDefs { public constructor() { super(); this.put(IfcPsetPropertySingleValueDef(k="IsOpenEnded", pType=Bool, expIfcType=ExpIfcBoolean)); } }
The IfcPropertiesDefs can then be used to construct IfcPropertiesData, as shown below. For properties that are only defined in CET, we should override the method IfcExportSnapperFactory.getCetDatas() in order to customize the CET data that should be exported.
// Hvacsegmentifcexportfactory class public IfcPropertiesObject getCetDatas() { IfcPropertiesDefs cetDataDef = getPsetDefinition(ifcKeyPset_CETHvacData); IfcPropertiesData cetData(cetDataDef); cetData."PartList" = (getPartListValue()); if (snapper as MechaStraightSnapper) cetData."IsOpenEnded" = snapper.openEnded; return cetData; }
Customizing Exported IFC Object Type
By default, Snapper are exported as IfcFurnishingElement. However, it is possible to export Snapper as other IfcProduct by overriding the method IfcExportCoreObjectFactory.makeProduct(). For example, the codes below customize the exporting of a straight HVAC as an IfcFlowSegment.
// HvacSegmentIfcExportFactory class public ExpIfcProduct makeProduct() { return ExpIfcFlowSegment(); }
By default, Snapper are exported as IfcFurnishingElementType. However, it is possible to export Snapper as other IfcTypeObject by overriding the method IfcExportFactory.getObjectTyping(). For example, the codes below customize the exporting of a straight HVAC as IfcDuctSegmentType.
// HvacSegmentIfcExportFactory class public ExpIfcTypeObject getObjectTyping() { if (!objectType) { ExpIfcDuctSegmentType typeObject(ifcGuid(), env.ownerHistory, expIfcDuctSegmentTypeEnum.notdefined); typeObject.name = ExpIfcLabel(getName()); typeObject.description = ExpIfcText(getDescription()); typeObject.predefinedType = ductSegmentTypeEnum(); objectType = typeObject; } return objectType; }
Customizing Object Aggregations
In IFC, it is possible to define a composition or decomposition relationship between IfcObjectDefinition using IfcRelAggregates, where the main object is defined as an aggregation of the parts. For example, a roof can be defined as an aggregation of roof elements like roof slabs, rafters, etc.
In CET, the aggregation relationship is defined in IfcExportAggregateInfo, where IfcExportAggregateInfo.parent stores the parent IfcObject, while IfcExportAggregateInfo.children stores the decomposed children IfcObject. The codes below show an example of constructing IfcExportAggregateInfo for stairs, and how to register the IFC export aggregate hook.
package IfcExportAggregateInfo[] generateStairGroups(IfcExportEnvG2 env) { IfcExportAggregateInfo[] aggInfos(); StairLandingSnapperFilter filter(); for (z in env.space.snappers) { if (filter.accepts(z)) { Snapper{} connected = z.allConnected(includeSelf=true); ExpIfcStair stair(ifcGuid, env.ownerHistory, expIfcStairTypeEnum.notdefined); aggInfos << IfcExportAggregateInfo(stair, connected); } } return aggInfos; } registerIfcExportAggregateHook(function generateStairGroups);
Customizing Exported Graphics (Shape Representations)
The generic export factory is also responsible for converting the 3D graphics for Snapper in CET into IfcRepresentation. By default, Primitive3D is converted into meshes and exported as IfcFaceBasedSurfaceModel; CmSym is exported by converting SymComponent to accurate IFC graphical representation.
Customizing the 3D graphics that should be exported for a particular Snapper could be done by overriding the method IfcExportCoreObjectFactory.getRepresentations(), as shown below. In this example, we are customizing the 3D graphics that should be exported for a straight rectangular HVAC, where the graphics are built using extrusions.
// HvacSegmentIfcExportFactory class public ExpIfcRepresentation[] getRepresentations() { if (snapper as MechaStraightSnapper) { ExpIfcAxis2Placement2D profilePos(ifcCartesianPoint(point2D(0, 0)), ifcDirection(point2D(1, 0))); ExpIfcAxis2Placement3D repPlacement(ifcCartesianPoint(point(0, 0, 0)), ifcDirection(point(1, 0, 0)), ifcDirection(point(0, -1, 0))); ExpIfcRectangleProfileDef profileDef(expIfcProfileTypeEnum.area, profilePos, ExpIfcPositiveLengthMeasure(snapper.diameter), ExpIfcPositiveLengthMeasure(snapper.height)); ExpIfcExtrudedAreaSolid repItem(sweptArea=profileDef, position=repPlacement, ifcDirection(point(0, 0, 1)), depth=ExpIfcPositiveLengthMeasure(snapper.length)); ExpIfcShapeRepresentation shapeRep(null, ExpIfcLabel("Body"), ExpIfcLabel("SweptSolid"), [ExpIfcRepresentationItem: repItem]); return [ExpIfcRepresentation : shapeRep]; } return super(); }
Customizing CmSym Exported Graphics
By default, CET supported a list of standard CmSym to IfcRepresentation conversions:
- SymExtrusion exported as IfcSweptAreaSolid
- SymBrep and SymBSplineSurface exported as IfcFacetedBrep
- SymBox and SymCylinder exported as IfcExtrudedAreaSolid
- Otherwise, CmSym will be exported as meshes to IfcFaceBasedSurfaceModel
To customize CmSym exported graphics as other IfcShapeRepresentation could be done by sub-class SymComponentToIfcRepConverter and implement your own logic in getIfcRepresentationItems. For example, SymBox is exported to IfcExtrudedAreaSolid:
/** * Get the IFC representation item. */ public void getIfcRepresentationItems(ExpIfcRepresentationItem[] expIfcRepItems) { if (symComponent as SymBox) { box b = symComponent.b; ExpIfcAxis2Placement2D pos2D(ifcCartesianPoint((b.center.point2D, b.p0.z)), ifcDirection((1, 0))); ExpIfcRectangleProfileDef profileDef(expIfcProfileTypeEnum.area, pos2D, ExpIfcPositiveLengthMeasure(b.w), ExpIfcPositiveLengthMeasure(b.d)); orientation o(); Transform t(point(), o); if (Transform symT = symNodeItEnv.transform) t = t + symT; ExpIfcAxis2Placement3D pos = ifcAxis2Placement3D(t.pos, t.rot); ExpIfcDirection extrudedDirection = ifcDirection((0, 0, 1)); ExpIfcPositiveLengthMeasure depth = ExpIfcPositiveLengthMeasure(b.h); expIfcRepItems << ExpIfcExtrudedAreaSolid(profileDef, pos, extrudedDirection, depth); } }
Comments
0 comments
Please sign in to leave a comment.