Add support for new creating newer schema entities to the EntityCreator
Xbim.Ifc.EntityCreator is a cross schema factory class enabling the correctly typed entities for a model to be created without specifying the concrete schema implementation. Currently it only supports Entity types in the lowest common denominator (IFC2x3)
So it doesn't support new entities in IFC4x3 (and also IFC4). E.g you can't create and IfcSensor or IfcRoad using the EntityCreator - you have to create these concretely. Clearly constructing these in IFC2x3 would make no sense but it would be nice to create a Sensor agnostic of the IFC schema being IFC4 or IFC4x3
Options:
- We support the highest schema and throw when not supported
- We extend the contract to have
IsSchemaSupported<TInterface>()and aCreate<TInterface>(Action<T>)
Hi,
while transforming our application to be schema-independent by using the EntityCreator following Objects are missing:
- IfcMaterialProperties
- IfcIndexedPolyCurve
- IfcLightFixture
- IfcTriangulatedFaceSet
- IndexedPolygonalFace
- PolygonalFaceSet
- IfcCartesianPointList2D
- IfcCartesianPointList3D
Is there a reason why those are not supported or are they from higher schemas?
Yes these are all types introduced in IFC4 and as touched upon in #600, EntityCreator only supports IFC 2x3 entities as a baseline.
What I think may work is to have some extension to EntityCreator that introduces the same approach for IFC 4+.
Where it gets complicated is if you wanted to create an IFC2x3 equivalent to IfcLightFIxture - i.e. IfcFlowTerminal defined by a IfcLightFixtureType
FYI IFC4 changelog here: https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/link/ifc2x3-to-ifc4-changelog.htm
Some example of how this could be implemented using ExtensionMethods on IModel so you can try out yourself.
- Compile time approach:
https://gist.github.com/andyward/6d6281b0794571ef2429096ed50a4317
IModel model = new IO.Memory.MemoryModel(new EntityFactoryIfc4());
IIfcLightFixture light = model.LightFixture(); // IFC4+
IIfcFlowTerminal light2 = model.LightFixtureWithBackwardCompatibility(); // IFC2x3+
- Run-time approach
https://gist.github.com/andyward/d94b852a0fcc5dd83e13e482a13d3f16
IModel model = new IO.Memory.MemoryModel(new EntityFactoryIfc4());
if (model.SchemaSupportsEntity<IIfcLightFixture>())
{
IIfcLightFixture wall = model.Create<IIfcLightFixture>(f => f.PredefinedType = IfcLightFixtureTypeEnum.SECURITYLIGHTING);
}
else
{
// Fall back manually to Flow Terminal etc
}
This second approach might permit us to register substitutions as per LightFixtureWithBackwardCompatibility() example
Thank you for the examples!
I experimented with the "Compile Time Approach" and could cover my use-cases. To avoid down casts from the more general IIfcFlowTerminal for later IFC-schemes, we provide 2 versions. This is not ideal, but the postfix should help to distinguish it.
Here are some parts of our routines:
public static class EntityCreatorExtensions
{
public static @IIfcLightFixture LightFixtureExt(this IModel model, Action<@IIfcLightFixture> init = null)
{
return model.SchemaVersion switch
{
XbimSchemaVersion.Ifc4 => model.Instances.New<Xbim.Ifc4.ElectricalDomain.IfcLightFixture>(init),
XbimSchemaVersion.Ifc4x3 => model.Instances.New<Xbim.Ifc4x3.ElectricalDomain.IfcLightFixture>(init),
_ => throw new NotSupportedException($"Type IfcLightFixture is not supported in schema {model.SchemaVersion}") // IfcLightFixture Not supported in 2x3
};
}
public static @IIfcFlowTerminal LightFixtureExtIFC2x3(this IModel model, Xbim.Ifc2x3.ElectricalDomain.IfcLightFixtureTypeEnum? lightType = null)
{
return model.SchemaVersion switch
{
XbimSchemaVersion.Ifc2X3 =>
// NOTE: Ifc2X3 requires a LightFixtureType to type FlowTerminal as Light
// CAUTION - Instantiation from LightFixtureType: CreateAttachInstancedRepresentation links LightFixtureType to Flowterminal! (lightType == null)
// CAUTION - Direct light creation: IFC2x3 requires an IfcLightFixtureTypeEnum! (lightType != null)
model.Instances.New<Xbim.Ifc2x3.SharedBldgServiceElements.IfcFlowTerminal>(o => {
if (lightType != null)
{
o.AddDefiningType(model.Instances.New<Xbim.Ifc2x3.ElectricalDomain.IfcLightFixtureType>(lt => {
lt.PredefinedType = lightType.Value;
lt.Name = "LightType";
}));
}
}),
XbimSchemaVersion.Ifc4 => model.Instances.New<Xbim.Ifc4.ElectricalDomain.IfcLightFixture>(),
XbimSchemaVersion.Ifc4x3 => model.Instances.New<Xbim.Ifc4x3.ElectricalDomain.IfcLightFixture>(),
_ => throw new NotSupportedException($"Type IfcLightFixture is not supported in schema {model.SchemaVersion}")
};
}
}
public static IIfcLightFixture CreateLightEmpty(this IModel model, string name, IIfcObjectPlacement placement, IIfcShapeRepresentation lightShape, IfcLightFixtureTypeEnum? lightType = null)
{
// CAUTION only supports IFC4+
return model.LightFixtureExt(t =>
{
if (lightType != null) t.PredefinedType = lightType.Value;
t.Name = name;
t.ObjectPlacement = placement;
t.Representation = model.Factory().ProductDefinitionShape(r => r.Representations.Add(lightShape));
});
}
public static IIfcFlowTerminal CreateLightEmptyIFC2x3(this IModel model, string name, IIfcObjectPlacement placement, IIfcShapeRepresentation lightShape, IfcLightFixtureTypeEnum? lightType = null)
{
Xbim.Ifc2x3.ElectricalDomain.IfcLightFixtureTypeEnum? ifc2x3LightType = null;
if (model.SchemaVersion == XbimSchemaVersion.Ifc2X3)
{
if (lightType == null)
{
ifc2x3LightType = Xbim.Ifc2x3.ElectricalDomain.IfcLightFixtureTypeEnum.NOTDEFINED;
}
else
{
ifc2x3LightType = lightType.Value switch
{
IfcLightFixtureTypeEnum.POINTSOURCE => Xbim.Ifc2x3.ElectricalDomain.IfcLightFixtureTypeEnum.POINTSOURCE,
IfcLightFixtureTypeEnum.DIRECTIONSOURCE => Xbim.Ifc2x3.ElectricalDomain.IfcLightFixtureTypeEnum.DIRECTIONSOURCE,
IfcLightFixtureTypeEnum.USERDEFINED => Xbim.Ifc2x3.ElectricalDomain.IfcLightFixtureTypeEnum.USERDEFINED,
_ => Xbim.Ifc2x3.ElectricalDomain.IfcLightFixtureTypeEnum.NOTDEFINED
};
}
}
var t = model.LightFixtureExtIFC2x3(ifc2x3LightType);
t.Name = name;
t.ObjectPlacement = placement;
t.Representation = model.Factory().ProductDefinitionShape(r => r.Representations.Add(lightShape));
if (lightType != null && t is IIfcLightFixture lf) lf.PredefinedType = lightType.Value;
return t;
}