From 008ac8a354b310f49441a59f06fde6eace36ffbc Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 31 Jul 2026 03:58:50 +0200 Subject: [PATCH] Discard old examples - modernize others --- .github/workflows/ci.yml | 24 +- cmake/utilities.cmake | 3 + src/examples/CMakeLists.txt | 7 +- src/examples/IfcOpenHouse.cpp | 412 ++++++++++++++++-------------- src/examples/IfcParseExamples.cpp | 57 +++-- src/ifcparse/alignment_helper.cpp | 2 +- src/plugin/plugin.cpp | 9 +- 7 files changed, 283 insertions(+), 231 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 806b853b28..a6188148e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -221,29 +221,11 @@ jobs: -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache cmake --build . - ./arbitrary_open_profile_def && test -f arbitrary_open_profile_def.ifc - ./composite_profile_def && test -f composite_profile_def.ifc - ./csg_primitive && test -f csg_primitive.ifc - ./ellipse_pies && test -f ellipse_pies.ifc - ./faces && test -f faces.ifc - ./ifc_curve_rebar && test -f ifc_curve_rebar.ifc - ./profiles - test -f IfcUShapeProfileDef.ifc - test -f IfcTShapeProfileDef.ifc - test -f IfcZShapeProfileDef.ifc - test -f IfcEllipseProfileDef.ifc - test -f IfcIShapeProfileDef.ifc - test -f IfcLShapeProfileDef.ifc - test -f IfcCShapeProfileDef.ifc - test -f IfcCircleProfileDef.ifc - test -f IfcRectangleProfileDef.ifc - test -f IfcTrapeziumProfileDef.ifc - ./IfcParseExamples "../IfcParseExamples_test.ifc" ./IfcOpenHouse && test -f IfcOpenHouse.ifc + ./IfcParseExamples IfcOpenHouse.ifc ./IfcAdvancedHouse && test -f IfcAdvancedHouse.ifc - ./IfcAlignment && test -f IfcAlignment.ifc - ./IfcSimplifiedAlignment && test -f IfcSimplifiedAlignment.ifc - ./triangulated_faceset && test -f triangulated_faceset.ifc + ./IfcAlignment && test -f FHWA_Bridge_Geometry_Alignment_Example.ifc + ./IfcSimplifiedAlignment && test -f FHWA_Bridge_Geometry_Alignment_Example_Simplified.ifc - name: Test ifcopenshell-python run: | diff --git a/cmake/utilities.cmake b/cmake/utilities.cmake index 20e1f2686a..030c831e00 100644 --- a/cmake/utilities.cmake +++ b/cmake/utilities.cmake @@ -53,6 +53,9 @@ endmacro() function(ifcopenshell_plugin_target TARGET) set_target_properties(${TARGET} PROPERTIES PREFIX "") + if((NOT WIN32) AND BUILD_SHARED_LIBS AND NOT WASM_BUILD AND NOT CREATE_BUNDLE AND NOT CMAKE_INSTALL_RPATH AND COMMAND SET_INSTALL_SELF_RPATH) + SET_INSTALL_SELF_RPATH(${TARGET}) + endif() endfunction() function(ifcopenshell_wasm_plugin_link_options TARGET REGISTRATION_SYMBOL) diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index 96de111ddb..a60ea0cb50 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -87,9 +87,12 @@ macro(build_schema_example exe_name schema) endif() endmacro() -if("2x3" IN_LIST SCHEMA_VERSIONS) +if(SCHEMA_VERSIONS) + list(GET SCHEMA_VERSIONS -1 schema) + build_schema_example(IfcParseExamples ${schema}) if(WITH_OPENCASCADE) - build_schema_example(IfcAdvancedHouse 2x3 geometry_serializer) + build_schema_example(IfcOpenHouse ${schema} geometry_serializer) + build_schema_example(IfcAdvancedHouse ${schema} geometry_serializer) endif() endif() diff --git a/src/examples/IfcOpenHouse.cpp b/src/examples/IfcOpenHouse.cpp index 9a8c5e44bb..99e8d56e1c 100644 --- a/src/examples/IfcOpenHouse.cpp +++ b/src/examples/IfcOpenHouse.cpp @@ -45,6 +45,7 @@ #include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema) #include "ifcparse/hierarchy_helper.h" +#include "plugin/plugin.h" #include "../ifcgeom/Serialization/Serialization.h" @@ -69,23 +70,21 @@ void createGroundShape(TopoDS_Shape& shape); int main() { +#ifdef IFCOPENSHELL_EXAMPLE_PLUGIN_PATH + ifcopenshell::plugin::set_search_paths({IFCOPENSHELL_EXAMPLE_PLUGIN_PATH}); +#endif + // The hierarchy_helper is a subclass of the regular file that provides several // convenience functions for working with geometry in IFC files. hierarchy_helper file; file.header().file_name().setname("IfcOpenHouse.ifc"); - // Start by adding a wall to the file, initially leaving most attributes blank. - IfcSchema::IfcWallStandardCase* south_wall = new IfcSchema::IfcWallStandardCase( - guid(), // GlobalId - 0, // OwnerHistory - "South wall"s, // Name - null, // Description - null, // ObjectType - 0, // ObjectPlacement - 0, // Representation - null // Tag + // Start by adding a wall to the file, initially leaving most attributes blank. + auto south_wall = file.create(); + south_wall.setGlobalId(guid()); + south_wall.setName("South wall"); #ifdef SCHEMA_IfcWall_HAS_PredefinedType - , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD + south_wall.setPredefinedType(IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD); #endif file.addBuildingProduct(south_wall); @@ -113,8 +112,8 @@ int main() { south_wall.setRepresentation(south_wall_shape); south_wall.setObjectPlacement(file.addLocalPlacement(storey_placement)); - // A pale white colour is assigned to the wall. - surface_style_t* wall_colour = setSurfaceColour(file, south_wall_shape, 0.75, 0.73, 0.68); + // A pale white colour is assigned to the wall. + auto wall_colour = setSurfaceColour(file, south_wall_shape, 0.75, 0.73, 0.68); // Now create a footing for the wall to rest on. auto footing = file.create(); @@ -125,24 +124,26 @@ int main() { file.addBuildingProduct(footing); - // The footing will span the entire floor plan of our building. The IfcRepresentationContext is - // something that has been created automatically as well, but representations could have been - // assigned to a specific context, for example to add a two dimensional plan representation as well. - footing->setRepresentation(file.addBox(10100, 5460, 2000)); - footing->setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 2500, -2000)); - // The footing will have a dark gray colour - surface_style_t* footing_colour = setSurfaceColour(file,footing->Representation(), 0.26, 0.22, 0.18); + // The footing will span the entire floor plan of our building. The IfcRepresentationContext is + // something that has been created automatically as well, but representations could have been + // assigned to a specific context, for example to add a two dimensional plan representation as well. + footing.setRepresentation(file.addBox(10100, 5460, 2000)); + footing.setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 2500, -2000)); + // The footing will have a dark gray colour + auto footing_colour = setSurfaceColour(file, footing.Representation(), 0.26, 0.22, 0.18); // IFC has two ways to apply boolean operations to geometry. IfcBooleanResults are commonly used // to clip geometry to a surface, for example to a slanted roof. For openings that are filled // with another element, for example a door or a window, an IfcOpeningElement is used instead. - // An opening element is created with rectangular geometry: - IfcSchema::IfcOpeningElement* west_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(), - null, null, null, file.addLocalPlacement(south_wall->ObjectPlacement(), -2500, 0, 400), - file.addBox(6000, 3630, 1600), null + // An opening element is created with rectangular geometry: + auto west_opening = file.create(); + west_opening.setGlobalId(guid()); + west_opening.setOwnerHistory(file.getSingle()); + west_opening.setObjectPlacement(file.addLocalPlacement(south_wall.ObjectPlacement(), -2500, 0, 400)); + west_opening.setRepresentation(file.addBox(6000, 3630, 1600)); #ifdef SCHEMA_IfcOpeningElement_HAS_PredefinedType - , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING + west_opening.setPredefinedType(IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING); #endif // Relate the opening element to the wall. @@ -152,12 +153,14 @@ int main() { void_element.setRelatingBuildingElement(south_wall); void_element.setRelatedOpeningElement(west_opening); - // Now create an additional opening - IfcSchema::IfcOpeningElement* south_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(), - null, null, null, file.addLocalPlacement(storey_placement, 3000, 0, 400), - file.addBox(1860, 3000, 1600), null + // Now create an additional opening + auto south_opening = file.create(); + south_opening.setGlobalId(guid()); + south_opening.setOwnerHistory(file.getSingle()); + south_opening.setObjectPlacement(file.addLocalPlacement(storey_placement, 3000, 0, 400)); + south_opening.setRepresentation(file.addBox(1860, 3000, 1600)); #ifdef SCHEMA_IfcOpeningElement_HAS_PredefinedType - , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING + south_opening.setPredefinedType(IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING); #endif // Relate the opening element to the wall. @@ -167,46 +170,17 @@ int main() { void_element2.setRelatingBuildingElement(south_wall); void_element2.setRelatedOpeningElement(south_opening); - // CV-2x3-144: Roofs are aggregates and shall have at least one contained element and no own geometry - IfcSchema::IfcSlab* south_roof_part = new IfcSchema::IfcSlab(guid(), file.getSingle(), "South roof"s, - null, null, 0, 0, null, IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF); - - // The geometry is instantiated by using IfcMappedItems. This way geometry definitions can - // be reused while maintaining the cardinality constraint that the ShapeOfProduct relation - // imposes on the IfcProductDefinitionShape. Note that this constrained is lifted in IFC4. - south_roof_part->setRepresentation(file.addMappedItem(roof_rep)); - south_roof_part->setObjectPlacement(file.addLocalPlacement(roof->ObjectPlacement(), 0, -400, 2700)); - - // The same roof geometry is re-used on the north side of the roof, by inverting the X-axis of - // the local placement the roof is rotated 180 degrees around the Z-axis - IfcSchema::IfcSlab* north_roof_part = new IfcSchema::IfcSlab(guid(), file.getSingle(), "North roof"s, - null, null, 0, 0, null, IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF); - north_roof_part->setOwnerHistory(file.getSingle()); - north_roof_part->setRepresentation(file.addMappedItem(roof_rep)); - north_roof_part->setObjectPlacement(file.addLocalPlacement(roof->ObjectPlacement(), 0, 5400, 2700, 0, 0, 1, -1, 0, 0)); - - IfcSchema::IfcObjectDefinition::list::ptr roof_parts(new IfcSchema::IfcObjectDefinition::list); - roof_parts->push(south_roof_part); - roof_parts->push(north_roof_part); - IfcSchema::IfcRelDecomposes* roof_decomposition = new IfcSchema::IfcRelAggregates(guid(), file.getSingle(), - null, null, roof, roof_parts); - file.addEntity(roof_decomposition); - - file.addBuildingProduct(south_roof_part); - file.addBuildingProduct(north_roof_part); - file.addBuildingProduct(roof); - - setSurfaceColour(file, roof_rep, 0.24, 0.08, 0.04); - - // Copy the south wall to the north - IfcSchema::IfcWallStandardCase* north_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(), "North wall"s, - null, null, file.addLocalPlacement(storey_placement, 0, 5000, 0), file.addAxisBox(10000, 360, 3000), null -#ifdef SCHEMA_IfcWall_HAS_PredefinedType - , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD -#endif - ); - file.addBuildingProduct(north_wall); - setSurfaceColour(file,north_wall->Representation(), wall_colour); + // Create a roof element that will consist of two slabs: + auto roof = file.create(); + roof.setGlobalId(guid()); + roof.setOwnerHistory(file.getSingle()); + roof.setName("Roof"); + roof.setObjectPlacement(file.addLocalPlacement(storey_placement)); +#ifdef SCHEMA_IfcRoof_HAS_PredefinedType + roof.setPredefinedType(IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF); +#else + roof.setShapeType(IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF); +#endif // The roof geometry is slanted 45 degrees by specifying a direction for the box extrusion auto roof_rep = file.addEmptyRepresentation(); @@ -225,37 +199,102 @@ int main() { south_roof_part.setRepresentation(file.addMappedItem(roof_rep)); south_roof_part.setObjectPlacement(file.addLocalPlacement(roof.ObjectPlacement(), 0, -400, 2700)); - // Now create a wall on the east of the building, again starting with just a box shape - IfcSchema::IfcWallStandardCase* east_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(), - "East wall"s, null, null, file.addLocalPlacement(storey_placement, 4820, 2500, 0, 0, 0, 1, 0, 1, 0), clipped_wall_body_reps[0], null -#ifdef SCHEMA_IfcWall_HAS_PredefinedType - , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD -#endif - ); - file.addBuildingProduct(east_wall); + // The same roof geometry is re-used on the north side of the roof, by inverting the X-axis of + // the local placement the roof is rotated 180 degrees around the Z-axis + auto north_roof_part = file.create(); + north_roof_part.setGlobalId(guid()); + north_roof_part.setOwnerHistory(file.getSingle()); + north_roof_part.setName("North roof"); + north_roof_part.setPredefinedType(IfcSchema::IfcSlabTypeEnum::IfcSlabType_ROOF); + north_roof_part.setRepresentation(file.addMappedItem(roof_rep)); + north_roof_part.setObjectPlacement(file.addLocalPlacement(roof.ObjectPlacement(), 0, 5400, 2700, 0, 0, 1, -1, 0, 0)); - // The east wall is copied to the west location of the house - IfcSchema::IfcWallStandardCase* west_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(), - "West wall"s, null, null, file.addLocalPlacement(storey_placement, -4820, 2500, 0, 0, 0, 1, 0, -1, 0), clipped_wall_body_reps[1], null + auto roof_decomposition = file.create(); + roof_decomposition.setGlobalId(guid()); + roof_decomposition.setOwnerHistory(file.getSingle()); + roof_decomposition.setRelatingObject(roof); + roof_decomposition.setRelatedObjects({south_roof_part, north_roof_part}); + + file.addBuildingProduct(south_roof_part); + file.addBuildingProduct(north_roof_part); + file.addBuildingProduct(roof); + + setSurfaceColour(file, roof_rep, 0.24, 0.08, 0.04); + + // Copy the south wall to the north + auto north_wall = file.create(); + north_wall.setGlobalId(guid()); + north_wall.setOwnerHistory(file.getSingle()); + north_wall.setName("North wall"); + north_wall.setObjectPlacement(file.addLocalPlacement(storey_placement, 0, 5000, 0)); + north_wall.setRepresentation(file.addAxisBox(10000, 360, 3000)); #ifdef SCHEMA_IfcWall_HAS_PredefinedType - , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD -#endif - ); - file.addBuildingProduct(west_wall); + north_wall.setPredefinedType(IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD); +#endif + + file.addBuildingProduct(north_wall); + setSurfaceColour(file, north_wall.Representation(), wall_colour); + + // Two identical representations are created for the two remaining walls. Mapped items + // are not used, because it is not allowed by the standard for wall body representations. + // MappedItems are not allowed for Axis representations as per CV-2x3-161 + IfcSchema::IfcProductDefinitionShape clipped_wall_body_reps[2]; + for (int i = 0; i < 2; ++i) { + auto body = file.addEmptyRepresentation(); + file.addBox(body, 5000, 360, 6000); + file.clipRepresentation(body, file.addPlacement3d(-2500, 0, 3000, -1, 0, 1), false); + file.clipRepresentation(body, file.addPlacement3d(2500, 0, 3000, 1, 0, 1), false); + setSurfaceColour(file, body, wall_colour); + + auto axis = file.addEmptyRepresentation("Axis", "Curve2D"); + file.addAxis(axis, 5000); + + clipped_wall_body_reps[i] = file.create(); + clipped_wall_body_reps[i].setRepresentations({body, axis}); + } + + // Now create a wall on the east of the building, again starting with just a box shape + auto east_wall = file.create(); + east_wall.setGlobalId(guid()); + east_wall.setOwnerHistory(file.getSingle()); + east_wall.setName("East wall"); + east_wall.setObjectPlacement(file.addLocalPlacement(storey_placement, 4820, 2500, 0, 0, 0, 1, 0, 1, 0)); + east_wall.setRepresentation(clipped_wall_body_reps[0]); +#ifdef SCHEMA_IfcWall_HAS_PredefinedType + east_wall.setPredefinedType(IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD); +#endif file.addBuildingProduct(east_wall); - // Not all viewers support opening elements with mapped representations, hence an exact copy of the - // same subtraction box is instantiated for the otherwise identical opening element. - IfcSchema::IfcOpeningElement* west_opening_copy = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(), - null, null, null, file.addLocalPlacement(west_wall->ObjectPlacement(), 2500, -2500+4820, 400, 0, 0, 1, 0, 1, 0), - file.addBox(6000, 3630, 1600), null + // The east wall is copied to the west location of the house + auto west_wall = file.create(); + west_wall.setGlobalId(guid()); + west_wall.setOwnerHistory(file.getSingle()); + west_wall.setName("West wall"); + west_wall.setObjectPlacement(file.addLocalPlacement(storey_placement, -4820, 2500, 0, 0, 0, 1, 0, -1, 0)); + west_wall.setRepresentation(clipped_wall_body_reps[1]); +#ifdef SCHEMA_IfcWall_HAS_PredefinedType + west_wall.setPredefinedType(IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD); +#endif + + file.addBuildingProduct(west_wall); + + // Not all viewers support opening elements with mapped representations, hence an exact copy of the + // same subtraction box is instantiated for the otherwise identical opening element. + auto west_opening_copy = file.create(); + west_opening_copy.setGlobalId(guid()); + west_opening_copy.setOwnerHistory(file.getSingle()); + west_opening_copy.setObjectPlacement(file.addLocalPlacement(west_wall.ObjectPlacement(), 2500, -2500 + 4820, 400, 0, 0, 1, 0, 1, 0)); + west_opening_copy.setRepresentation(file.addBox(6000, 3630, 1600)); #ifdef SCHEMA_IfcOpeningElement_HAS_PredefinedType - , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING -#endif - ); - file.addEntity(west_opening_copy); - file.addEntity(new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle(), null, null, west_wall, west_opening_copy)); + west_opening_copy.setPredefinedType(IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING); +#endif + + auto west_void = file.create(); + west_void.setGlobalId(guid()); + west_void.setOwnerHistory(file.getSingle()); + west_void.setRelatingBuildingElement(west_wall); + west_void.setRelatedOpeningElement(west_opening_copy); // Up until now we have only used simple extrusions for the creation of the geometry. For the // ground mesh of the IfcSite we will use a Nurbs surface created in Open Cascade. The surface @@ -269,17 +308,11 @@ int main() { BRepGProp::SurfaceProperties(shape, prop); const double site_area = prop.Mass() / 1000 / 1000; - IfcSchema::IfcProperty::list::ptr properties(new IfcSchema::IfcProperty::list); - properties->push(new IfcSchema::IfcPropertySingleValue("TotalArea", null, new IfcSchema::IfcAreaMeasure(site_area), 0)); - IfcSchema::IfcPropertySet* pset = new IfcSchema::IfcPropertySet(guid(), file.getSingle(), "Pset_SiteCommon"s, null, properties); -#ifdef SCHEMA_HAS_IfcDefinitionSelect - IfcSchema::IfcObjectDefinition::list::ptr related_objs(new IfcSchema::IfcObjectDefinition::list); -#else - IfcSchema::IfcObject::list::ptr related_objs(new IfcSchema::IfcObject::list); -#endif - related_objs->push(file.getSingle()); - IfcSchema::IfcRelDefinesByProperties* site_prop = new IfcSchema::IfcRelDefinesByProperties(guid(), file.getSingle(), null, null, related_objs, pset); - file.addEntity(site_prop); + auto total_area = file.create(); + total_area.setName("TotalArea"); + auto area = file.create(); + area.set_attribute_value(0, site_area); + total_area.setNominalValue(area); auto pset = file.create(); pset.setGlobalId(guid()); @@ -307,52 +340,12 @@ int main() { // Some BIM authoring applications, such as Autodesk Revit, ignore the geometrical representation // by and large and construct native walls using the layer thickness and reference line offset // provided here. -#ifdef SCHEMA_IfcMaterial_HAS_Description - IfcSchema::IfcMaterial* material = new IfcSchema::IfcMaterial("Brick", null, null); -#else - IfcSchema::IfcMaterial* material = new IfcSchema::IfcMaterial("Brick"); -#endif - IfcSchema::IfcMaterialLayer* layer = new IfcSchema::IfcMaterialLayer( - material, - 360, - null -#ifdef SCHEMA_IfcMaterialLayer_HAS_Name - , null - , null - , null - , null -#endif - ); - IfcSchema::IfcMaterialLayer::list::ptr layers (new aggregate_of()); - layers->push(layer); - IfcSchema::IfcMaterialLayerSet* layer_set = new IfcSchema::IfcMaterialLayerSet( - layers, - "Wall"s -#ifdef SCHEMA_IfcMaterialLayerSet_HAS_Description - , null -#endif - ); - IfcSchema::IfcMaterialLayerSetUsage* layer_usage = new IfcSchema::IfcMaterialLayerSetUsage( - layer_set, - IfcSchema::IfcLayerSetDirectionEnum::IfcLayerSetDirection_AXIS2, - IfcSchema::IfcDirectionSenseEnum::IfcDirectionSense_POSITIVE, - -180 -#ifdef SCHEMA_IfcMaterialLayerSetUsage_HAS_ReferenceExtent - , null -#endif - ); + auto material = file.create(); + material.setName("Brick"); - IfcSchema::IfcRelAssociatesMaterial* associates_material = new IfcSchema::IfcRelAssociatesMaterial( - guid(), - file.getSingle(), - null, - null, -#ifdef SCHEMA_HAS_IfcDefinitionSelect - file.instances_by_type()->as(), -#else - file.instances_by_type()->as(), -#endif - layer_usage); + auto layer = file.create(); + layer.setMaterial(material); + layer.setLayerThickness(360); auto layer_set = file.create(); layer_set.setMaterialLayers({layer}); @@ -379,34 +372,53 @@ int main() { stair_points.push_back(XY(500, 200)); stair_points.push_back(XY(500, 400)); stair_points.push_back(XY( 0, 400)); - IfcSchema::IfcStairFlight* stair = new IfcSchema::IfcStairFlight(guid(), file.getSingle(), - null, null, null, file.addLocalPlacement(storey_placement, 5050, 1000, 0, 0, 1, 0, 1, 0, 0), - file.addExtrudedPolyline(stair_points, 1200), null, 2, 2, 0.2, 0.25 + auto stair = file.create(); + stair.setGlobalId(guid()); + stair.setOwnerHistory(file.getSingle()); + stair.setObjectPlacement(file.addLocalPlacement(storey_placement, 5050, 1000, 0, 0, 1, 0, 1, 0, 0)); + stair.setRepresentation(file.addExtrudedPolyline(stair_points, 1200)); +#ifdef SCHEMA_IfcStairFlight_HAS_NumberOfRisers + stair.setNumberOfRisers(2); +#else + stair.setNumberOfRiser(2); +#endif + stair.setNumberOfTreads(2); + stair.setRiserHeight(0.2); + stair.setTreadLength(0.25); #ifdef SCHEMA_IfcStairFlight_HAS_PredefinedType - , IfcSchema::IfcStairFlightTypeEnum::IfcStairFlightType_STRAIGHT + stair.setPredefinedType(IfcSchema::IfcStairFlightTypeEnum::IfcStairFlightType_STRAIGHT); #endif file.addBuildingProduct(stair); setSurfaceColour(file, stair.Representation(), footing_colour); - IfcSchema::IfcOpeningElement* door_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(), - null, null, null, file.addLocalPlacement(storey_placement, 5000-180, 2500-900, 0), file.addBox(1000, 1000, 2200), null + auto door_opening = file.create(); + door_opening.setGlobalId(guid()); + door_opening.setOwnerHistory(file.getSingle()); + door_opening.setObjectPlacement(file.addLocalPlacement(storey_placement, 5000 - 180, 2500 - 900, 0)); + door_opening.setRepresentation(file.addBox(1000, 1000, 2200)); #ifdef SCHEMA_IfcOpeningElement_HAS_PredefinedType - , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING -#endif - ); - file.addEntity(door_opening); - file.addEntity(new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle(), null, null, east_wall, door_opening)); + door_opening.setPredefinedType(IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING); +#endif + + auto door_void = file.create(); + door_void.setGlobalId(guid()); + door_void.setOwnerHistory(file.getSingle()); + door_void.setRelatingBuildingElement(east_wall); + door_void.setRelatedOpeningElement(door_opening); // A single shape representation can contain multiple representiation items. This way a product // can be a composition of multiple solids. The following door will be composed of four boxes // which constitute the door and its frame. - IfcSchema::IfcDoor* door = new IfcSchema::IfcDoor(guid(), file.getSingle(), null, null, null, - file.addLocalPlacement(storey_placement, 4800, 1600, 0, 0, 0, 1, 0, 1, 0), 0, null, 2200, 1000 + auto door = file.create(); + door.setGlobalId(guid()); + door.setOwnerHistory(file.getSingle()); + door.setObjectPlacement(file.addLocalPlacement(storey_placement, 4800, 1600, 0, 0, 0, 1, 0, 1, 0)); + door.setOverallWidth(1000); + door.setOverallHeight(2200); #ifdef SCHEMA_IfcDoor_HAS_PredefinedType - , IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR - , IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT - , null + door.setPredefinedType(IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR); + door.setOperationType(IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT); #endif door.setRepresentation(file.addBox(80, 80, 2120, IfcSchema::IfcAxis2Placement2D{}, file.addPlacement3d(460, 0, 0))); @@ -424,13 +436,31 @@ int main() { file.addBox(door_body, 860, 30, 2120); file.addBuildingProduct(door); + setSurfaceColour(file, door.Representation(), 0.9, 0.9, 0.9); + auto fills = file.create(); + fills.setGlobalId(guid()); + fills.setOwnerHistory(file.getSingle()); + fills.setRelatingOpeningElement(door_opening); + fills.setRelatedBuildingElement(door); + #ifdef SCHEMA_HAS_IfcDoorType - IfcSchema::IfcDoorType* door_type = new IfcSchema::IfcDoorType(guid(), file.getSingle(), "Door type"s, null, null, null, null, null, null, - IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR, IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT, false, null); + auto door_type = file.create(); + door_type.setGlobalId(guid()); + door_type.setOwnerHistory(file.getSingle()); + door_type.setName("Door type"); + door_type.setPredefinedType(IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR); + door_type.setOperationType(IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT); + door_type.setParameterTakesPrecedence(false); file.addRelatedObject(door_type, door); #elif defined(SCHEMA_HAS_IfcDoorStyle) - IfcSchema::IfcDoorStyle* door_style = new IfcSchema::IfcDoorStyle(guid(), file.getSingle(), "Door type"s, null, null, null, null, null, - IfcSchema::IfcDoorStyleOperationEnum::IfcDoorStyleOperation_SINGLE_SWING_LEFT, IfcSchema::IfcDoorStyleConstructionEnum::IfcDoorStyleConstruction_WOOD, false, false); + auto door_style = file.create(); + door_style.setGlobalId(guid()); + door_style.setOwnerHistory(file.getSingle()); + door_style.setName("Door type"); + door_style.setOperationType(IfcSchema::IfcDoorStyleOperationEnum::IfcDoorStyleOperation_SINGLE_SWING_LEFT); + door_style.setConstructionType(IfcSchema::IfcDoorStyleConstructionEnum::IfcDoorStyleConstruction_WOOD); + door_style.setParameterTakesPrecedence(false); + door_style.setSizeable(false); file.addRelatedObject(door_style, door); #endif @@ -458,8 +488,8 @@ int main() { frame_representations.push_back(vertical_bar); // Add another reference to the vertical bar created above // The beams all have the same surface style assigned - surface_style_t* frame_style = 0; - for (IfcSchema::IfcShapeRepresentation::list::it i = frame_representations->begin(); i != frame_representations->end(); i += 2) { + surface_style_t frame_style; + for (auto i = frame_representations.begin(); i != frame_representations.end(); i += 2) { if (frame_style) { setSurfaceColour(file, *i, frame_style); } else { @@ -477,16 +507,18 @@ int main() { window_placements.push_back(file.addLocalPlacement(storey_placement, 3000-930, -45, 400)); window_placements.push_back(file.addLocalPlacement(storey_placement, -4855+45, 885-930, 400, 0, 0, 1, 0, 1, 0)); - for (IfcSchema::IfcLocalPlacement::list::it it = window_placements->begin(); it != window_placements->end(); ++it) { - + for (auto& place : window_placements) { + // Create the window at the current location - IfcSchema::IfcLocalPlacement* place = *it; - IfcSchema::IfcWindow* window = new IfcSchema::IfcWindow(guid(), file.getSingle(), - null, null, null, place, 0, null, 1600, 1860 + auto window = file.create(); + window.setGlobalId(guid()); + window.setOwnerHistory(file.getSingle()); + window.setObjectPlacement(place); + window.setOverallWidth(1860); + window.setOverallHeight(1600); #ifdef SCHEMA_IfcWindow_HAS_PredefinedType - , IfcSchema::IfcWindowTypeEnum::IfcWindowType_WINDOW - , IfcSchema::IfcWindowTypePartitioningEnum::IfcWindowTypePartitioning_SINGLE_PANEL - , null + window.setPredefinedType(IfcSchema::IfcWindowTypeEnum::IfcWindowType_WINDOW); + window.setPartitioningType(IfcSchema::IfcWindowTypePartitioningEnum::IfcWindowTypePartitioning_SINGLE_PANEL); #endif file.addBuildingProduct(window); @@ -508,20 +540,26 @@ int main() { frame_placement != frame_placements.end() && frame_representation != frame_representations.end(); ++frame_placement, ++frame_representation) { - IfcSchema::IfcMember* frame_part = new IfcSchema::IfcMember(guid(), file.getSingle(), - null, null, null, *frame_placement, file.addMappedItem(*frame_representation), null + auto frame_part = file.create(); + frame_part.setGlobalId(guid()); + frame_part.setOwnerHistory(file.getSingle()); + frame_part.setObjectPlacement(*frame_placement); + frame_part.setRepresentation(file.addMappedItem(*frame_representation)); #ifdef SCHEMA_IfcMember_HAS_PredefinedType - , IfcSchema::IfcMemberTypeEnum::IfcMemberType_MULLION + frame_part.setPredefinedType(IfcSchema::IfcMemberTypeEnum::IfcMemberType_MULLION); #endif window_parts.push_back(frame_part); file.relatePlacements(window, frame_part); } // Add the glass plate to the list of parts - IfcSchema::IfcPlate* glass_part = new IfcSchema::IfcPlate(guid(), file.getSingle(), null, - null, null, file.addLocalPlacement(storey_placement, 930, 45, 90), file.addBox(1680, 10, 1420), null + auto glass_part = file.create(); + glass_part.setGlobalId(guid()); + glass_part.setOwnerHistory(file.getSingle()); + glass_part.setObjectPlacement(file.addLocalPlacement(storey_placement, 930, 45, 90)); + glass_part.setRepresentation(file.addBox(1680, 10, 1420)); #ifdef SCHEMA_IfcPlate_HAS_PredefinedType - , IfcSchema::IfcPlateTypeEnum::IfcPlateType_SHEET + glass_part.setPredefinedType(IfcSchema::IfcPlateTypeEnum::IfcPlateType_SHEET); #endif window_parts.push_back(glass_part); diff --git a/src/examples/IfcParseExamples.cpp b/src/examples/IfcParseExamples.cpp index 0fb1f02c8e..6b9f07102d 100644 --- a/src/examples/IfcParseExamples.cpp +++ b/src/examples/IfcParseExamples.cpp @@ -25,6 +25,7 @@ #include "ifcparse/file.h" #include "ifcparse/logger.h" +#include "plugin/plugin.h" #include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema) #include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema) @@ -47,13 +48,29 @@ struct is_ifc4_or_higher> : s typedef std::map> element_properties; +std::string string_value(const std::string& value) { + return value; +} + +std::string string_value(const std::optional& value) { + return value.value_or(""); +} + +bool has_string(const std::string& value) { + return !value.empty(); +} + +bool has_string(const std::optional& value) { + return value.has_value(); +} + #ifdef SCHEMA_HAS_IfcBuildingElement typedef IfcSchema::IfcBuildingElement element_t; #else typedef IfcSchema::IfcBuiltElement element_t; #endif -std::string format_string(const AttributeValue& argument) { +std::string format_string(const attribute_value& argument) { // Argument is a runtime tagged variant for the various data types in a IFC model, // in this particular case we only care about flattening it to a string. // @todo mostly duplicated from XmlSerializer.cpp @@ -62,21 +79,21 @@ std::string format_string(const AttributeValue& argument) { } auto argument_type = argument.type(); switch (argument_type) { - case IfcUtil::Argument_BOOL: { + case ifcopenshell::Argument_BOOL: { const bool b = argument; return b ? "true" : "false"; } - case IfcUtil::Argument_DOUBLE: { + case ifcopenshell::Argument_DOUBLE: { const double d = argument; std::stringstream stream; stream << std::setprecision(std::numeric_limits< double >::max_digits10) << d; return stream.str(); break; } - case IfcUtil::Argument_STRING: - case IfcUtil::Argument_ENUMERATION: { + case ifcopenshell::Argument_STRING: + case ifcopenshell::Argument_ENUMERATION: { return static_cast(argument); break; } - case IfcUtil::Argument_INT: { + case ifcopenshell::Argument_INT: { const int v = argument; std::stringstream stream; stream << v; @@ -90,40 +107,38 @@ template void process_pset(element_properties& props, const T& inst) { // Process an individual Property or Quantity set. if (auto pset = inst.template as()) { - if (!pset.Name()) { + auto pset_name = pset.Name(); + if (!has_string(pset_name)) { return; } auto ps = pset.HasProperties(); for (const auto& p : ps) { if (auto singleval = p.template as()) { std::string propname, propvalue; - if constexpr (is_ifc4_or_higher::value) { - if (!singleval.Name()) { - continue; - } - propname = *singleval.Name(); - } - if constexpr (!is_ifc4_or_higher::value) { - propname = *singleval.Name(); + auto property_name = singleval.Name(); + if (!has_string(property_name)) { + continue; } + propname = string_value(property_name); auto nominal_value = singleval.NominalValue(); if (!nominal_value) { propvalue = "-"; } else { - props[*pset.Name()][propname] = format_string(nominal_value.get_attribute_value(0)); + props[string_value(pset_name)][propname] = format_string(nominal_value.get_attribute_value(0)); } } } } if (auto qset = inst.template as()) { - if (!qset.Name()) { + auto qset_name = qset.Name(); + if (!has_string(qset_name)) { return; } auto qs = qset.Quantities(); for (const auto& q : qs) { - if (q.template as() && q.get_attribute_value(3).type() == IfcUtil::Argument_DOUBLE) { + if (q.template as() && q.get_attribute_value(3).type() == ifcopenshell::Argument_DOUBLE) { double v = q.get_attribute_value(3); - props[*qset.Name()][q.Name()] = std::to_string(v); + props[string_value(qset_name)][string_value(q.Name())] = std::to_string(v); } } } @@ -193,6 +208,10 @@ int main(int argc, char** argv) { return 1; } +#ifdef IFCOPENSHELL_EXAMPLE_PLUGIN_PATH + ifcopenshell::plugin::set_search_paths({IFCOPENSHELL_EXAMPLE_PLUGIN_PATH}); +#endif + // Redirect the output (both progress and log) to stdout ::logger::root().set_output(&std::cout, &std::cout); diff --git a/src/ifcparse/alignment_helper.cpp b/src/ifcparse/alignment_helper.cpp index 2cd773d5d1..50565c7cb3 100644 --- a/src/ifcparse/alignment_helper.cpp +++ b/src/ifcparse/alignment_helper.cpp @@ -268,7 +268,7 @@ Ifc4x3_add2::IfcAlignment addHorizontalAlignment(hierarchy_helper& return alignment; } -std::tuple&, typename std::vector&, Ifc4x3_add2::IfcGradientCurve> _createVerticalAlignment(hierarchy_helper& file, Ifc4x3_add2::IfcCompositeCurve composite_curve,const std::vector>& vpoints, const std::vector& vclengths, bool include_geometry) { +std::tuple, std::vector, Ifc4x3_add2::IfcGradientCurve> _createVerticalAlignment(hierarchy_helper& file, Ifc4x3_add2::IfcCompositeCurve composite_curve,const std::vector>& vpoints, const std::vector& vclengths, bool include_geometry) { std::vector vertical_segments; // business logic std::vector vertical_curve_segments; // geometry diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp index 215d2d93d4..78754e8e2b 100644 --- a/src/plugin/plugin.cpp +++ b/src/plugin/plugin.cpp @@ -26,6 +26,7 @@ #endif #include +#include #include #include @@ -449,7 +450,13 @@ std::filesystem::path ifcopenshell::plugin::module_directory(const void* symbol) throw std::runtime_error("Unable to resolve module path"); } - const auto directory = std::filesystem::path(info.dli_fname).parent_path(); + auto directory = std::filesystem::path(info.dli_fname).parent_path(); + if (directory.empty()) { + // dladdr() returns the invocation string for symbols in the main + // executable. When it was found through PATH this can be just the + // executable name, so resolve the actual program location instead. + directory = boost::dll::program_location().parent_path(); + } plugin_debug("module_directory resolved " + path_string(directory)); return directory; #endif