diff --git a/src/ifcgeom/Converter.cpp b/src/ifcgeom/Converter.cpp index 15a4584088..f091f1ceda 100644 --- a/src/ifcgeom/Converter.cpp +++ b/src/ifcgeom/Converter.cpp @@ -244,7 +244,7 @@ IfcGeom::BRepElement* ifcopenshell::geometry::Converter::create_brep_for_represe } } - shape = new IfcGeom::Representation::BRep(settings_, product_type, representation_id_builder.str(), shapes); + shape = new IfcGeom::Representation::BRep(settings_, product_type, representation_id_builder.str(), shapes, &product->declaration()); std::string context_string = ""; diff --git a/src/ifcgeom/IfcGeomRenderStyles.h b/src/ifcgeom/IfcGeomRenderStyles.h index 2486f2c9dc..ecf71f0e71 100644 --- a/src/ifcgeom/IfcGeomRenderStyles.h +++ b/src/ifcgeom/IfcGeomRenderStyles.h @@ -30,8 +30,18 @@ #include #include +namespace IfcParse { + class declaration; +} + namespace IfcGeom { - IFC_GEOM_API const ifcopenshell::geometry::taxonomy::style::ptr& get_default_style(const std::string& ifc_type); + // Looks up the default (fallback) style registered for `ifc_type`. When no style is + // registered for that exact type and `decl` is provided, the type's supertype chain + // (as defined by the IFC schema) is walked until a registered ancestor is found, so + // e.g. IfcSlabStandardCase inherits the style registered for IfcSlab. Falls back to + // the generic default style ("*" in a --default-material-file, or a fixed grey) when + // neither the type nor any of its supertypes have a registered style. + IFC_GEOM_API const ifcopenshell::geometry::taxonomy::style::ptr& get_default_style(const std::string& ifc_type, const IfcParse::declaration* decl = nullptr); IFC_GEOM_API ifcopenshell::geometry::taxonomy::style::ptr& update_default_style(const std::string& ifc_type); diff --git a/src/ifcgeom/IfcGeomRepresentation.cpp b/src/ifcgeom/IfcGeomRepresentation.cpp index d533adf0df..f1995a2e41 100644 --- a/src/ifcgeom/IfcGeomRepresentation.cpp +++ b/src/ifcgeom/IfcGeomRepresentation.cpp @@ -137,7 +137,7 @@ IfcGeom::Representation::Triangulation::Triangulation(const BRep& shape_model) } if (settings().get().get() && surface_style_id == -1) { - const auto& material = IfcGeom::get_default_style(shape_model.entity()); + const auto& material = IfcGeom::get_default_style(shape_model.entity(), shape_model.declaration()); auto mit = std::find(materials_.begin(), materials_.end(), material); if (mit == materials_.end()) { surface_style_id = (int)materials_.size(); diff --git a/src/ifcgeom/IfcGeomRepresentation.h b/src/ifcgeom/IfcGeomRepresentation.h index 2bafa9ab88..8f78687791 100644 --- a/src/ifcgeom/IfcGeomRepresentation.h +++ b/src/ifcgeom/IfcGeomRepresentation.h @@ -25,6 +25,10 @@ #include +namespace IfcParse { + class declaration; +} + namespace IfcGeom { namespace Representation { @@ -36,16 +40,26 @@ namespace IfcGeom { const ifcopenshell::geometry::Settings settings_; const std::string entity_; std::string id_; + // Most-derived IFC type declaration of the product this representation was + // built for, used to walk the supertype chain when resolving a default + // (fallback) style so subtypes such as IfcSlabStandardCase inherit the + // style registered for IfcSlab. May be null (e.g. for reconstructed / + // deserialized representations), in which case no inheritance is applied. + const IfcParse::declaration* declaration_ = nullptr; public: - explicit Representation(const ifcopenshell::geometry::Settings& settings, const std::string& entity, const std::string& id) + explicit Representation(const ifcopenshell::geometry::Settings& settings, const std::string& entity, const std::string& id, const IfcParse::declaration* decl = nullptr) : settings_(settings) , entity_(entity) , id_(id) + , declaration_(decl) {} const ifcopenshell::geometry::Settings& settings() const { return settings_; } const std::string& entity() const { return entity_; } + const IfcParse::declaration* declaration() const { + return declaration_; + } // id starts with representation id and then it may have the following dash separated elements: // - layerset-layerset_id // - material-material_id @@ -60,8 +74,8 @@ namespace IfcGeom { BRep(const BRep& other); BRep& operator=(const BRep& other); public: - BRep(const ifcopenshell::geometry::Settings& settings, const std::string& entity, const std::string& id, const IfcGeom::ConversionResults& shapes) - : Representation(settings, entity, id) + BRep(const ifcopenshell::geometry::Settings& settings, const std::string& entity, const std::string& id, const IfcGeom::ConversionResults& shapes, const IfcParse::declaration* decl = nullptr) + : Representation(settings, entity, id, decl) , shapes_(shapes) {} virtual ~BRep() {} diff --git a/src/ifcgeom/SurfaceStyle.cpp b/src/ifcgeom/SurfaceStyle.cpp index 69310f3929..29b6f0f476 100644 --- a/src/ifcgeom/SurfaceStyle.cpp +++ b/src/ifcgeom/SurfaceStyle.cpp @@ -1,4 +1,5 @@ #include "../ifcgeom/IfcGeomRenderStyles.h" +#include "../ifcparse/IfcSchema.h" #include #include @@ -112,15 +113,35 @@ void IfcGeom::set_default_style_file(const std::string& json_file) { } } -const ifcopenshell::geometry::taxonomy::style::ptr& IfcGeom::get_default_style(const std::string& s) { +const ifcopenshell::geometry::taxonomy::style::ptr& IfcGeom::get_default_style(const std::string& s, const IfcParse::declaration* decl) { static std::mutex m; std::lock_guard lk(m); if (!default_materials_initialized) InitDefaultMaterials(); auto it = default_materials.find(s); + if (it == default_materials.end() && decl != nullptr) { + // No style registered for this exact type. Walk up the schema's supertype + // chain (e.g. IfcSlabStandardCase -> IfcSlab) so a style registered for a + // supertype is inherited by its subtypes, rather than always falling back + // to the generic default style. + const IfcParse::entity* entity = decl->as_entity(); + const IfcParse::entity* super = entity ? entity->supertype() : nullptr; + while (super != nullptr) { + auto super_it = default_materials.find(super->name()); + if (super_it != default_materials.end()) { + it = super_it; + break; + } + super = super->supertype(); + } + } if (it == default_materials.end()) { default_materials.insert(std::make_pair(s, default_material)); it = default_materials.find(s); + } else if (it->first != s) { + // Cache the resolved (inherited) style under the leaf type name too, so + // repeated lookups for this exact type are O(1) afterwards. + default_materials.insert(std::make_pair(s, it->second)); } return it->second; }